diff options
author | Richard Quirk <richard.quirk@gmail.com> | 2014-03-19 19:31:31 +0100 |
---|---|---|
committer | Marcin Bukat <marcin.bukat@gmail.com> | 2014-04-02 20:31:54 +0200 |
commit | 36378988ad4059982742f05f5eb50580b456840a (patch) | |
tree | ee70be810d894566c5e351f21a1ebb79be742a85 /apps/plugins/lua/lstrlib.c | |
parent | 020f16a1c7d5bc9a302671cef03f56ac735e20c5 (diff) | |
download | rockbox-36378988ad4059982742f05f5eb50580b456840a.tar.gz rockbox-36378988ad4059982742f05f5eb50580b456840a.zip |
Update lua plugin to 5.2.3
Prior to this patch the Lua plugin used version 5.1.4. This change
reduces the number of modifications in the Lua source using some new
defines and because the upstream source is now more flexible.
Unless otherwise stated, l*.[ch] files are taken unmodified from the
upstream lua-5.2.3.
fscanf.c:
file descriptors in rockbox are just ints, they are hidden behind a
void* now so liolib requires less modifications. fscanf is updated to
use void* too.
getc.c: this is a new file required for getc implementation in lauxlib.c
lauxlib.c: LoadF replaced FILE* with int, the rockbox file
descriptor int are cast to FILE* (actually void* due to typedef).
getc uses the PREFIX version. stdin is not used, as per 5.1.4.
lbaselib.c: now uses strspn in the number parsing. print uses DEBUGF now
rather than being commented out.
lbitlib.c: use the built-in version from 5.2.3 rather than Reuben
Thomas's external library. Backwards compatible and adds some new bit
operations.
ldo.c: the LUAI_THROW/TRY defines are now in the core lua code, so have
been removed from rockconf.h
liolib.c: here the implementation has changed to use the LStream from
the original source, and cast the FILE* pointers to int. This has
reduced the number of modifications from the upstream version.
llex.c: the only change from upstream is to remove the locale include.
lmathlib.c: updated from the 5.2.3 version and re-applied the changes
that were made vs 5.1.4 for random numbers and to remove unsupported
float functions.
loadlib.c: upstream version, with the 5.1.4 changes for missing
functions.
lobject.c: upstream version, with ctype.h added and sprintf changed to
snprintf.
loslib.c: upstream version with locale.h removed and 5.1.4 changes for
unsupportable functions.
lstrlib.c: sprintf changed to snprintf.
ltable.c: upstream with the hashnum function from 5.1.4 to avoid frexp
in luai_hashnum.
luaconf.h: updated to 5.2.3 version, restored relevant parts from the
original 5.1.4 configuration. The COMPAT defines that are no longer
available are not included.
lundump.c: VERSION macro conflicts with the core Rockbox equivalent.
rocklib.c: luaL_reg is no longer available, replaced by luaL_Reg
equivalent. Moved checkboolean/optboolean functions to this file and out
of core lua files. luaL_getn is no longer available, replaced by
luaL_rawlen. luaL_register is deprecated, use the newlib/setfuncs
replacements. rli_init has to be called before setting up the newlib to
avoid overwriting the rb table.
rocklib_aux.pl: use rli_checkboolean from rocklib.c.
rocklua.c: new default bits library used, update the library loading
code with idiomatic 5.2 code.
strcspn.c: no longer needed, but strspn.c is required for strspn in
lbaselib.c
Change-Id: I0c7945c755f79083afe98ec117e1e8cf13de2651
Reviewed-on: http://gerrit.rockbox.org/774
Tested: Richard Quirk <richard.quirk@gmail.com>
Reviewed-by: Marcin Bukat <marcin.bukat@gmail.com>
Diffstat (limited to 'apps/plugins/lua/lstrlib.c')
-rw-r--r-- | apps/plugins/lua/lstrlib.c | 580 |
1 files changed, 365 insertions, 215 deletions
diff --git a/apps/plugins/lua/lstrlib.c b/apps/plugins/lua/lstrlib.c index 3d6103692f..04cc2f60d6 100644 --- a/apps/plugins/lua/lstrlib.c +++ b/apps/plugins/lua/lstrlib.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lstrlib.c,v 1.132.1.4 2008/07/11 17:27:21 roberto Exp $ | 2 | ** $Id: lstrlib.c,v 1.178.1.1 2013/04/12 18:48:47 roberto Exp $ |
3 | ** Standard library for string operations and pattern-matching | 3 | ** Standard library for string operations and pattern-matching |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -20,47 +20,58 @@ | |||
20 | #include "lualib.h" | 20 | #include "lualib.h" |
21 | 21 | ||
22 | 22 | ||
23 | /* | ||
24 | ** maximum number of captures that a pattern can do during | ||
25 | ** pattern-matching. This limit is arbitrary. | ||
26 | */ | ||
27 | #if !defined(LUA_MAXCAPTURES) | ||
28 | #define LUA_MAXCAPTURES 32 | ||
29 | #endif | ||
30 | |||
31 | |||
23 | /* macro to `unsign' a character */ | 32 | /* macro to `unsign' a character */ |
24 | #define uchar(c) ((unsigned char)(c)) | 33 | #define uchar(c) ((unsigned char)(c)) |
25 | 34 | ||
26 | 35 | ||
27 | 36 | ||
28 | static int str_len (lua_State *L) { | 37 | static int str_len (lua_State *L) { |
29 | size_t l; | 38 | size_t l; |
30 | luaL_checklstring(L, 1, &l); | 39 | luaL_checklstring(L, 1, &l); |
31 | lua_pushinteger(L, l); | 40 | lua_pushinteger(L, (lua_Integer)l); |
32 | return 1; | 41 | return 1; |
33 | } | 42 | } |
34 | 43 | ||
35 | 44 | ||
36 | static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) { | 45 | /* translate a relative string position: negative means back from end */ |
37 | /* relative string position: negative means back from end */ | 46 | static size_t posrelat (ptrdiff_t pos, size_t len) { |
38 | if (pos < 0) pos += (ptrdiff_t)len + 1; | 47 | if (pos >= 0) return (size_t)pos; |
39 | return (pos >= 0) ? pos : 0; | 48 | else if (0u - (size_t)pos > len) return 0; |
49 | else return len - ((size_t)-pos) + 1; | ||
40 | } | 50 | } |
41 | 51 | ||
42 | 52 | ||
43 | static int str_sub (lua_State *L) { | 53 | static int str_sub (lua_State *L) { |
44 | size_t l; | 54 | size_t l; |
45 | const char *s = luaL_checklstring(L, 1, &l); | 55 | const char *s = luaL_checklstring(L, 1, &l); |
46 | ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l); | 56 | size_t start = posrelat(luaL_checkinteger(L, 2), l); |
47 | ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l); | 57 | size_t end = posrelat(luaL_optinteger(L, 3, -1), l); |
48 | if (start < 1) start = 1; | 58 | if (start < 1) start = 1; |
49 | if (end > (ptrdiff_t)l) end = (ptrdiff_t)l; | 59 | if (end > l) end = l; |
50 | if (start <= end) | 60 | if (start <= end) |
51 | lua_pushlstring(L, s+start-1, end-start+1); | 61 | lua_pushlstring(L, s + start - 1, end - start + 1); |
52 | else lua_pushliteral(L, ""); | 62 | else lua_pushliteral(L, ""); |
53 | return 1; | 63 | return 1; |
54 | } | 64 | } |
55 | 65 | ||
56 | 66 | ||
57 | static int str_reverse (lua_State *L) { | 67 | static int str_reverse (lua_State *L) { |
58 | size_t l; | 68 | size_t l, i; |
59 | luaL_Buffer b; | 69 | luaL_Buffer b; |
60 | const char *s = luaL_checklstring(L, 1, &l); | 70 | const char *s = luaL_checklstring(L, 1, &l); |
61 | luaL_buffinit(L, &b); | 71 | char *p = luaL_buffinitsize(L, &b, l); |
62 | while (l--) luaL_addchar(&b, s[l]); | 72 | for (i = 0; i < l; i++) |
63 | luaL_pushresult(&b); | 73 | p[i] = s[l - i - 1]; |
74 | luaL_pushresultsize(&b, l); | ||
64 | return 1; | 75 | return 1; |
65 | } | 76 | } |
66 | 77 | ||
@@ -70,10 +81,10 @@ static int str_lower (lua_State *L) { | |||
70 | size_t i; | 81 | size_t i; |
71 | luaL_Buffer b; | 82 | luaL_Buffer b; |
72 | const char *s = luaL_checklstring(L, 1, &l); | 83 | const char *s = luaL_checklstring(L, 1, &l); |
73 | luaL_buffinit(L, &b); | 84 | char *p = luaL_buffinitsize(L, &b, l); |
74 | for (i=0; i<l; i++) | 85 | for (i=0; i<l; i++) |
75 | luaL_addchar(&b, tolower(uchar(s[i]))); | 86 | p[i] = tolower(uchar(s[i])); |
76 | luaL_pushresult(&b); | 87 | luaL_pushresultsize(&b, l); |
77 | return 1; | 88 | return 1; |
78 | } | 89 | } |
79 | 90 | ||
@@ -83,22 +94,38 @@ static int str_upper (lua_State *L) { | |||
83 | size_t i; | 94 | size_t i; |
84 | luaL_Buffer b; | 95 | luaL_Buffer b; |
85 | const char *s = luaL_checklstring(L, 1, &l); | 96 | const char *s = luaL_checklstring(L, 1, &l); |
86 | luaL_buffinit(L, &b); | 97 | char *p = luaL_buffinitsize(L, &b, l); |
87 | for (i=0; i<l; i++) | 98 | for (i=0; i<l; i++) |
88 | luaL_addchar(&b, toupper(uchar(s[i]))); | 99 | p[i] = toupper(uchar(s[i])); |
89 | luaL_pushresult(&b); | 100 | luaL_pushresultsize(&b, l); |
90 | return 1; | 101 | return 1; |
91 | } | 102 | } |
92 | 103 | ||
104 | |||
105 | /* reasonable limit to avoid arithmetic overflow */ | ||
106 | #define MAXSIZE ((~(size_t)0) >> 1) | ||
107 | |||
93 | static int str_rep (lua_State *L) { | 108 | static int str_rep (lua_State *L) { |
94 | size_t l; | 109 | size_t l, lsep; |
95 | luaL_Buffer b; | ||
96 | const char *s = luaL_checklstring(L, 1, &l); | 110 | const char *s = luaL_checklstring(L, 1, &l); |
97 | int n = luaL_checkint(L, 2); | 111 | int n = luaL_checkint(L, 2); |
98 | luaL_buffinit(L, &b); | 112 | const char *sep = luaL_optlstring(L, 3, "", &lsep); |
99 | while (n-- > 0) | 113 | if (n <= 0) lua_pushliteral(L, ""); |
100 | luaL_addlstring(&b, s, l); | 114 | else if (l + lsep < l || l + lsep >= MAXSIZE / n) /* may overflow? */ |
101 | luaL_pushresult(&b); | 115 | return luaL_error(L, "resulting string too large"); |
116 | else { | ||
117 | size_t totallen = n * l + (n - 1) * lsep; | ||
118 | luaL_Buffer b; | ||
119 | char *p = luaL_buffinitsize(L, &b, totallen); | ||
120 | while (n-- > 1) { /* first n-1 copies (followed by separator) */ | ||
121 | memcpy(p, s, l * sizeof(char)); p += l; | ||
122 | if (lsep > 0) { /* avoid empty 'memcpy' (may be expensive) */ | ||
123 | memcpy(p, sep, lsep * sizeof(char)); p += lsep; | ||
124 | } | ||
125 | } | ||
126 | memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ | ||
127 | luaL_pushresultsize(&b, totallen); | ||
128 | } | ||
102 | return 1; | 129 | return 1; |
103 | } | 130 | } |
104 | 131 | ||
@@ -106,15 +133,15 @@ static int str_rep (lua_State *L) { | |||
106 | static int str_byte (lua_State *L) { | 133 | static int str_byte (lua_State *L) { |
107 | size_t l; | 134 | size_t l; |
108 | const char *s = luaL_checklstring(L, 1, &l); | 135 | const char *s = luaL_checklstring(L, 1, &l); |
109 | ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l); | 136 | size_t posi = posrelat(luaL_optinteger(L, 2, 1), l); |
110 | ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l); | 137 | size_t pose = posrelat(luaL_optinteger(L, 3, posi), l); |
111 | int n, i; | 138 | int n, i; |
112 | if (posi <= 0) posi = 1; | 139 | if (posi < 1) posi = 1; |
113 | if ((size_t)pose > l) pose = l; | 140 | if (pose > l) pose = l; |
114 | if (posi > pose) return 0; /* empty interval; return no values */ | 141 | if (posi > pose) return 0; /* empty interval; return no values */ |
115 | n = (int)(pose - posi + 1); | 142 | n = (int)(pose - posi + 1); |
116 | if (posi + n <= pose) /* overflow? */ | 143 | if (posi + n <= pose) /* (size_t -> int) overflow? */ |
117 | luaL_error(L, "string slice too long"); | 144 | return luaL_error(L, "string slice too long"); |
118 | luaL_checkstack(L, n, "string slice too long"); | 145 | luaL_checkstack(L, n, "string slice too long"); |
119 | for (i=0; i<n; i++) | 146 | for (i=0; i<n; i++) |
120 | lua_pushinteger(L, uchar(s[posi+i-1])); | 147 | lua_pushinteger(L, uchar(s[posi+i-1])); |
@@ -126,13 +153,13 @@ static int str_char (lua_State *L) { | |||
126 | int n = lua_gettop(L); /* number of arguments */ | 153 | int n = lua_gettop(L); /* number of arguments */ |
127 | int i; | 154 | int i; |
128 | luaL_Buffer b; | 155 | luaL_Buffer b; |
129 | luaL_buffinit(L, &b); | 156 | char *p = luaL_buffinitsize(L, &b, n); |
130 | for (i=1; i<=n; i++) { | 157 | for (i=1; i<=n; i++) { |
131 | int c = luaL_checkint(L, i); | 158 | int c = luaL_checkint(L, i); |
132 | luaL_argcheck(L, uchar(c) == c, i, "invalid value"); | 159 | luaL_argcheck(L, uchar(c) == c, i, "value out of range"); |
133 | luaL_addchar(&b, uchar(c)); | 160 | p[i - 1] = uchar(c); |
134 | } | 161 | } |
135 | luaL_pushresult(&b); | 162 | luaL_pushresultsize(&b, n); |
136 | return 1; | 163 | return 1; |
137 | } | 164 | } |
138 | 165 | ||
@@ -150,7 +177,7 @@ static int str_dump (lua_State *L) { | |||
150 | lua_settop(L, 1); | 177 | lua_settop(L, 1); |
151 | luaL_buffinit(L,&b); | 178 | luaL_buffinit(L,&b); |
152 | if (lua_dump(L, writer, &b) != 0) | 179 | if (lua_dump(L, writer, &b) != 0) |
153 | luaL_error(L, "unable to dump given function"); | 180 | return luaL_error(L, "unable to dump given function"); |
154 | luaL_pushresult(&b); | 181 | luaL_pushresult(&b); |
155 | return 1; | 182 | return 1; |
156 | } | 183 | } |
@@ -167,9 +194,12 @@ static int str_dump (lua_State *L) { | |||
167 | #define CAP_UNFINISHED (-1) | 194 | #define CAP_UNFINISHED (-1) |
168 | #define CAP_POSITION (-2) | 195 | #define CAP_POSITION (-2) |
169 | 196 | ||
197 | |||
170 | typedef struct MatchState { | 198 | typedef struct MatchState { |
199 | int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ | ||
171 | const char *src_init; /* init of source string */ | 200 | const char *src_init; /* init of source string */ |
172 | const char *src_end; /* end (`\0') of source string */ | 201 | const char *src_end; /* end ('\0') of source string */ |
202 | const char *p_end; /* end ('\0') of pattern */ | ||
173 | lua_State *L; | 203 | lua_State *L; |
174 | int level; /* total number of captures (finished or unfinished) */ | 204 | int level; /* total number of captures (finished or unfinished) */ |
175 | struct { | 205 | struct { |
@@ -179,6 +209,16 @@ typedef struct MatchState { | |||
179 | } MatchState; | 209 | } MatchState; |
180 | 210 | ||
181 | 211 | ||
212 | /* recursive function */ | ||
213 | static const char *match (MatchState *ms, const char *s, const char *p); | ||
214 | |||
215 | |||
216 | /* maximum recursion depth for 'match' */ | ||
217 | #if !defined(MAXCCALLS) | ||
218 | #define MAXCCALLS 200 | ||
219 | #endif | ||
220 | |||
221 | |||
182 | #define L_ESC '%' | 222 | #define L_ESC '%' |
183 | #define SPECIALS "^$*+?.([%-" | 223 | #define SPECIALS "^$*+?.([%-" |
184 | 224 | ||
@@ -186,7 +226,7 @@ typedef struct MatchState { | |||
186 | static int check_capture (MatchState *ms, int l) { | 226 | static int check_capture (MatchState *ms, int l) { |
187 | l -= '1'; | 227 | l -= '1'; |
188 | if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) | 228 | if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) |
189 | return luaL_error(ms->L, "invalid capture index"); | 229 | return luaL_error(ms->L, "invalid capture index %%%d", l + 1); |
190 | return l; | 230 | return l; |
191 | } | 231 | } |
192 | 232 | ||
@@ -202,16 +242,16 @@ static int capture_to_close (MatchState *ms) { | |||
202 | static const char *classend (MatchState *ms, const char *p) { | 242 | static const char *classend (MatchState *ms, const char *p) { |
203 | switch (*p++) { | 243 | switch (*p++) { |
204 | case L_ESC: { | 244 | case L_ESC: { |
205 | if (*p == '\0') | 245 | if (p == ms->p_end) |
206 | luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")"); | 246 | luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")"); |
207 | return p+1; | 247 | return p+1; |
208 | } | 248 | } |
209 | case '[': { | 249 | case '[': { |
210 | if (*p == '^') p++; | 250 | if (*p == '^') p++; |
211 | do { /* look for a `]' */ | 251 | do { /* look for a `]' */ |
212 | if (*p == '\0') | 252 | if (p == ms->p_end) |
213 | luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")"); | 253 | luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")"); |
214 | if (*(p++) == L_ESC && *p != '\0') | 254 | if (*(p++) == L_ESC && p < ms->p_end) |
215 | p++; /* skip escapes (e.g. `%]') */ | 255 | p++; /* skip escapes (e.g. `%]') */ |
216 | } while (*p != ']'); | 256 | } while (*p != ']'); |
217 | return p+1; | 257 | return p+1; |
@@ -229,13 +269,14 @@ static int match_class (int c, int cl) { | |||
229 | case 'a' : res = isalpha(c); break; | 269 | case 'a' : res = isalpha(c); break; |
230 | case 'c' : res = iscntrl(c); break; | 270 | case 'c' : res = iscntrl(c); break; |
231 | case 'd' : res = isdigit(c); break; | 271 | case 'd' : res = isdigit(c); break; |
272 | case 'g' : res = isgraph(c); break; | ||
232 | case 'l' : res = islower(c); break; | 273 | case 'l' : res = islower(c); break; |
233 | case 'p' : res = ispunct(c); break; | 274 | case 'p' : res = ispunct(c); break; |
234 | case 's' : res = isspace(c); break; | 275 | case 's' : res = isspace(c); break; |
235 | case 'u' : res = isupper(c); break; | 276 | case 'u' : res = isupper(c); break; |
236 | case 'w' : res = isalnum(c); break; | 277 | case 'w' : res = isalnum(c); break; |
237 | case 'x' : res = isxdigit(c); break; | 278 | case 'x' : res = isxdigit(c); break; |
238 | case 'z' : res = (c == 0); break; | 279 | case 'z' : res = (c == 0); break; /* deprecated option */ |
239 | default: return (cl == c); | 280 | default: return (cl == c); |
240 | } | 281 | } |
241 | return (islower(cl) ? res : !res); | 282 | return (islower(cl) ? res : !res); |
@@ -265,23 +306,27 @@ static int matchbracketclass (int c, const char *p, const char *ec) { | |||
265 | } | 306 | } |
266 | 307 | ||
267 | 308 | ||
268 | static int singlematch (int c, const char *p, const char *ep) { | 309 | static int singlematch (MatchState *ms, const char *s, const char *p, |
269 | switch (*p) { | 310 | const char *ep) { |
270 | case '.': return 1; /* matches any char */ | 311 | if (s >= ms->src_end) |
271 | case L_ESC: return match_class(c, uchar(*(p+1))); | 312 | return 0; |
272 | case '[': return matchbracketclass(c, p, ep-1); | 313 | else { |
273 | default: return (uchar(*p) == c); | 314 | int c = uchar(*s); |
315 | switch (*p) { | ||
316 | case '.': return 1; /* matches any char */ | ||
317 | case L_ESC: return match_class(c, uchar(*(p+1))); | ||
318 | case '[': return matchbracketclass(c, p, ep-1); | ||
319 | default: return (uchar(*p) == c); | ||
320 | } | ||
274 | } | 321 | } |
275 | } | 322 | } |
276 | 323 | ||
277 | 324 | ||
278 | static const char *match (MatchState *ms, const char *s, const char *p); | ||
279 | |||
280 | |||
281 | static const char *matchbalance (MatchState *ms, const char *s, | 325 | static const char *matchbalance (MatchState *ms, const char *s, |
282 | const char *p) { | 326 | const char *p) { |
283 | if (*p == 0 || *(p+1) == 0) | 327 | if (p >= ms->p_end - 1) |
284 | luaL_error(ms->L, "unbalanced pattern"); | 328 | luaL_error(ms->L, "malformed pattern " |
329 | "(missing arguments to " LUA_QL("%%b") ")"); | ||
285 | if (*s != *p) return NULL; | 330 | if (*s != *p) return NULL; |
286 | else { | 331 | else { |
287 | int b = *p; | 332 | int b = *p; |
@@ -301,7 +346,7 @@ static const char *matchbalance (MatchState *ms, const char *s, | |||
301 | static const char *max_expand (MatchState *ms, const char *s, | 346 | static const char *max_expand (MatchState *ms, const char *s, |
302 | const char *p, const char *ep) { | 347 | const char *p, const char *ep) { |
303 | ptrdiff_t i = 0; /* counts maximum expand for item */ | 348 | ptrdiff_t i = 0; /* counts maximum expand for item */ |
304 | while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep)) | 349 | while (singlematch(ms, s + i, p, ep)) |
305 | i++; | 350 | i++; |
306 | /* keeps trying to match with the maximum repetitions */ | 351 | /* keeps trying to match with the maximum repetitions */ |
307 | while (i>=0) { | 352 | while (i>=0) { |
@@ -319,7 +364,7 @@ static const char *min_expand (MatchState *ms, const char *s, | |||
319 | const char *res = match(ms, s, ep+1); | 364 | const char *res = match(ms, s, ep+1); |
320 | if (res != NULL) | 365 | if (res != NULL) |
321 | return res; | 366 | return res; |
322 | else if (s<ms->src_end && singlematch(uchar(*s), p, ep)) | 367 | else if (singlematch(ms, s, p, ep)) |
323 | s++; /* try with one more repetition */ | 368 | s++; /* try with one more repetition */ |
324 | else return NULL; | 369 | else return NULL; |
325 | } | 370 | } |
@@ -363,80 +408,105 @@ static const char *match_capture (MatchState *ms, const char *s, int l) { | |||
363 | 408 | ||
364 | 409 | ||
365 | static const char *match (MatchState *ms, const char *s, const char *p) { | 410 | static const char *match (MatchState *ms, const char *s, const char *p) { |
411 | if (ms->matchdepth-- == 0) | ||
412 | luaL_error(ms->L, "pattern too complex"); | ||
366 | init: /* using goto's to optimize tail recursion */ | 413 | init: /* using goto's to optimize tail recursion */ |
367 | switch (*p) { | 414 | if (p != ms->p_end) { /* end of pattern? */ |
368 | case '(': { /* start capture */ | 415 | switch (*p) { |
369 | if (*(p+1) == ')') /* position capture? */ | 416 | case '(': { /* start capture */ |
370 | return start_capture(ms, s, p+2, CAP_POSITION); | 417 | if (*(p + 1) == ')') /* position capture? */ |
371 | else | 418 | s = start_capture(ms, s, p + 2, CAP_POSITION); |
372 | return start_capture(ms, s, p+1, CAP_UNFINISHED); | 419 | else |
373 | } | 420 | s = start_capture(ms, s, p + 1, CAP_UNFINISHED); |
374 | case ')': { /* end capture */ | 421 | break; |
375 | return end_capture(ms, s, p+1); | 422 | } |
376 | } | 423 | case ')': { /* end capture */ |
377 | case L_ESC: { | 424 | s = end_capture(ms, s, p + 1); |
378 | switch (*(p+1)) { | 425 | break; |
379 | case 'b': { /* balanced string? */ | 426 | } |
380 | s = matchbalance(ms, s, p+2); | 427 | case '$': { |
381 | if (s == NULL) return NULL; | 428 | if ((p + 1) != ms->p_end) /* is the `$' the last char in pattern? */ |
382 | p+=4; goto init; /* else return match(ms, s, p+4); */ | 429 | goto dflt; /* no; go to default */ |
383 | } | 430 | s = (s == ms->src_end) ? s : NULL; /* check end of string */ |
384 | case 'f': { /* frontier? */ | 431 | break; |
385 | const char *ep; char previous; | 432 | } |
386 | p += 2; | 433 | case L_ESC: { /* escaped sequences not in the format class[*+?-]? */ |
387 | if (*p != '[') | 434 | switch (*(p + 1)) { |
388 | luaL_error(ms->L, "missing " LUA_QL("[") " after " | 435 | case 'b': { /* balanced string? */ |
389 | LUA_QL("%%f") " in pattern"); | 436 | s = matchbalance(ms, s, p + 2); |
390 | ep = classend(ms, p); /* points to what is next */ | 437 | if (s != NULL) { |
391 | previous = (s == ms->src_init) ? '\0' : *(s-1); | 438 | p += 4; goto init; /* return match(ms, s, p + 4); */ |
392 | if (matchbracketclass(uchar(previous), p, ep-1) || | 439 | } /* else fail (s == NULL) */ |
393 | !matchbracketclass(uchar(*s), p, ep-1)) return NULL; | 440 | break; |
394 | p=ep; goto init; /* else return match(ms, s, ep); */ | 441 | } |
395 | } | 442 | case 'f': { /* frontier? */ |
396 | default: { | 443 | const char *ep; char previous; |
397 | if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ | 444 | p += 2; |
398 | s = match_capture(ms, s, uchar(*(p+1))); | 445 | if (*p != '[') |
399 | if (s == NULL) return NULL; | 446 | luaL_error(ms->L, "missing " LUA_QL("[") " after " |
400 | p+=2; goto init; /* else return match(ms, s, p+2) */ | 447 | LUA_QL("%%f") " in pattern"); |
448 | ep = classend(ms, p); /* points to what is next */ | ||
449 | previous = (s == ms->src_init) ? '\0' : *(s - 1); | ||
450 | if (!matchbracketclass(uchar(previous), p, ep - 1) && | ||
451 | matchbracketclass(uchar(*s), p, ep - 1)) { | ||
452 | p = ep; goto init; /* return match(ms, s, ep); */ | ||
453 | } | ||
454 | s = NULL; /* match failed */ | ||
455 | break; | ||
401 | } | 456 | } |
402 | goto dflt; /* case default */ | 457 | case '0': case '1': case '2': case '3': |
458 | case '4': case '5': case '6': case '7': | ||
459 | case '8': case '9': { /* capture results (%0-%9)? */ | ||
460 | s = match_capture(ms, s, uchar(*(p + 1))); | ||
461 | if (s != NULL) { | ||
462 | p += 2; goto init; /* return match(ms, s, p + 2) */ | ||
463 | } | ||
464 | break; | ||
465 | } | ||
466 | default: goto dflt; | ||
403 | } | 467 | } |
468 | break; | ||
404 | } | 469 | } |
405 | } | 470 | default: dflt: { /* pattern class plus optional suffix */ |
406 | case '\0': { /* end of pattern */ | 471 | const char *ep = classend(ms, p); /* points to optional suffix */ |
407 | return s; /* match succeeded */ | 472 | /* does not match at least once? */ |
408 | } | 473 | if (!singlematch(ms, s, p, ep)) { |
409 | case '$': { | 474 | if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */ |
410 | if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ | 475 | p = ep + 1; goto init; /* return match(ms, s, ep + 1); */ |
411 | return (s == ms->src_end) ? s : NULL; /* check end of string */ | 476 | } |
412 | else goto dflt; | 477 | else /* '+' or no suffix */ |
413 | } | 478 | s = NULL; /* fail */ |
414 | default: dflt: { /* it is a pattern item */ | ||
415 | const char *ep = classend(ms, p); /* points to what is next */ | ||
416 | int m = s<ms->src_end && singlematch(uchar(*s), p, ep); | ||
417 | switch (*ep) { | ||
418 | case '?': { /* optional */ | ||
419 | const char *res; | ||
420 | if (m && ((res=match(ms, s+1, ep+1)) != NULL)) | ||
421 | return res; | ||
422 | p=ep+1; goto init; /* else return match(ms, s, ep+1); */ | ||
423 | } | ||
424 | case '*': { /* 0 or more repetitions */ | ||
425 | return max_expand(ms, s, p, ep); | ||
426 | } | ||
427 | case '+': { /* 1 or more repetitions */ | ||
428 | return (m ? max_expand(ms, s+1, p, ep) : NULL); | ||
429 | } | ||
430 | case '-': { /* 0 or more repetitions (minimum) */ | ||
431 | return min_expand(ms, s, p, ep); | ||
432 | } | 479 | } |
433 | default: { | 480 | else { /* matched once */ |
434 | if (!m) return NULL; | 481 | switch (*ep) { /* handle optional suffix */ |
435 | s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ | 482 | case '?': { /* optional */ |
483 | const char *res; | ||
484 | if ((res = match(ms, s + 1, ep + 1)) != NULL) | ||
485 | s = res; | ||
486 | else { | ||
487 | p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */ | ||
488 | } | ||
489 | break; | ||
490 | } | ||
491 | case '+': /* 1 or more repetitions */ | ||
492 | s++; /* 1 match already done */ | ||
493 | /* go through */ | ||
494 | case '*': /* 0 or more repetitions */ | ||
495 | s = max_expand(ms, s, p, ep); | ||
496 | break; | ||
497 | case '-': /* 0 or more repetitions (minimum) */ | ||
498 | s = min_expand(ms, s, p, ep); | ||
499 | break; | ||
500 | default: /* no suffix */ | ||
501 | s++; p = ep; goto init; /* return match(ms, s + 1, ep); */ | ||
502 | } | ||
436 | } | 503 | } |
504 | break; | ||
437 | } | 505 | } |
438 | } | 506 | } |
439 | } | 507 | } |
508 | ms->matchdepth++; | ||
509 | return s; | ||
440 | } | 510 | } |
441 | 511 | ||
442 | 512 | ||
@@ -492,37 +562,58 @@ static int push_captures (MatchState *ms, const char *s, const char *e) { | |||
492 | } | 562 | } |
493 | 563 | ||
494 | 564 | ||
565 | /* check whether pattern has no special characters */ | ||
566 | static int nospecials (const char *p, size_t l) { | ||
567 | size_t upto = 0; | ||
568 | do { | ||
569 | if (strpbrk(p + upto, SPECIALS)) | ||
570 | return 0; /* pattern has a special character */ | ||
571 | upto += strlen(p + upto) + 1; /* may have more after \0 */ | ||
572 | } while (upto <= l); | ||
573 | return 1; /* no special chars found */ | ||
574 | } | ||
575 | |||
576 | |||
495 | static int str_find_aux (lua_State *L, int find) { | 577 | static int str_find_aux (lua_State *L, int find) { |
496 | size_t l1, l2; | 578 | size_t ls, lp; |
497 | const char *s = luaL_checklstring(L, 1, &l1); | 579 | const char *s = luaL_checklstring(L, 1, &ls); |
498 | const char *p = luaL_checklstring(L, 2, &l2); | 580 | const char *p = luaL_checklstring(L, 2, &lp); |
499 | ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1; | 581 | size_t init = posrelat(luaL_optinteger(L, 3, 1), ls); |
500 | if (init < 0) init = 0; | 582 | if (init < 1) init = 1; |
501 | else if ((size_t)(init) > l1) init = (ptrdiff_t)l1; | 583 | else if (init > ls + 1) { /* start after string's end? */ |
502 | if (find && (lua_toboolean(L, 4) || /* explicit request? */ | 584 | lua_pushnil(L); /* cannot find anything */ |
503 | strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */ | 585 | return 1; |
586 | } | ||
587 | /* explicit request or no special characters? */ | ||
588 | if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) { | ||
504 | /* do a plain search */ | 589 | /* do a plain search */ |
505 | const char *s2 = lmemfind(s+init, l1-init, p, l2); | 590 | const char *s2 = lmemfind(s + init - 1, ls - init + 1, p, lp); |
506 | if (s2) { | 591 | if (s2) { |
507 | lua_pushinteger(L, s2-s+1); | 592 | lua_pushinteger(L, s2 - s + 1); |
508 | lua_pushinteger(L, s2-s+l2); | 593 | lua_pushinteger(L, s2 - s + lp); |
509 | return 2; | 594 | return 2; |
510 | } | 595 | } |
511 | } | 596 | } |
512 | else { | 597 | else { |
513 | MatchState ms; | 598 | MatchState ms; |
514 | int anchor = (*p == '^') ? (p++, 1) : 0; | 599 | const char *s1 = s + init - 1; |
515 | const char *s1=s+init; | 600 | int anchor = (*p == '^'); |
601 | if (anchor) { | ||
602 | p++; lp--; /* skip anchor character */ | ||
603 | } | ||
516 | ms.L = L; | 604 | ms.L = L; |
605 | ms.matchdepth = MAXCCALLS; | ||
517 | ms.src_init = s; | 606 | ms.src_init = s; |
518 | ms.src_end = s+l1; | 607 | ms.src_end = s + ls; |
608 | ms.p_end = p + lp; | ||
519 | do { | 609 | do { |
520 | const char *res; | 610 | const char *res; |
521 | ms.level = 0; | 611 | ms.level = 0; |
612 | lua_assert(ms.matchdepth == MAXCCALLS); | ||
522 | if ((res=match(&ms, s1, p)) != NULL) { | 613 | if ((res=match(&ms, s1, p)) != NULL) { |
523 | if (find) { | 614 | if (find) { |
524 | lua_pushinteger(L, s1-s+1); /* start */ | 615 | lua_pushinteger(L, s1 - s + 1); /* start */ |
525 | lua_pushinteger(L, res-s); /* end */ | 616 | lua_pushinteger(L, res - s); /* end */ |
526 | return push_captures(&ms, NULL, 0) + 2; | 617 | return push_captures(&ms, NULL, 0) + 2; |
527 | } | 618 | } |
528 | else | 619 | else |
@@ -547,18 +638,21 @@ static int str_match (lua_State *L) { | |||
547 | 638 | ||
548 | static int gmatch_aux (lua_State *L) { | 639 | static int gmatch_aux (lua_State *L) { |
549 | MatchState ms; | 640 | MatchState ms; |
550 | size_t ls; | 641 | size_t ls, lp; |
551 | const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); | 642 | const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); |
552 | const char *p = lua_tostring(L, lua_upvalueindex(2)); | 643 | const char *p = lua_tolstring(L, lua_upvalueindex(2), &lp); |
553 | const char *src; | 644 | const char *src; |
554 | ms.L = L; | 645 | ms.L = L; |
646 | ms.matchdepth = MAXCCALLS; | ||
555 | ms.src_init = s; | 647 | ms.src_init = s; |
556 | ms.src_end = s+ls; | 648 | ms.src_end = s+ls; |
649 | ms.p_end = p + lp; | ||
557 | for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); | 650 | for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); |
558 | src <= ms.src_end; | 651 | src <= ms.src_end; |
559 | src++) { | 652 | src++) { |
560 | const char *e; | 653 | const char *e; |
561 | ms.level = 0; | 654 | ms.level = 0; |
655 | lua_assert(ms.matchdepth == MAXCCALLS); | ||
562 | if ((e = match(&ms, src, p)) != NULL) { | 656 | if ((e = match(&ms, src, p)) != NULL) { |
563 | lua_Integer newstart = e-s; | 657 | lua_Integer newstart = e-s; |
564 | if (e == src) newstart++; /* empty match? go at least one position */ | 658 | if (e == src) newstart++; /* empty match? go at least one position */ |
@@ -581,12 +675,6 @@ static int gmatch (lua_State *L) { | |||
581 | } | 675 | } |
582 | 676 | ||
583 | 677 | ||
584 | static int gfind_nodef (lua_State *L) { | ||
585 | return luaL_error(L, LUA_QL("string.gfind") " was renamed to " | ||
586 | LUA_QL("string.gmatch")); | ||
587 | } | ||
588 | |||
589 | |||
590 | static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, | 678 | static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, |
591 | const char *e) { | 679 | const char *e) { |
592 | size_t l, i; | 680 | size_t l, i; |
@@ -596,8 +684,12 @@ static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, | |||
596 | luaL_addchar(b, news[i]); | 684 | luaL_addchar(b, news[i]); |
597 | else { | 685 | else { |
598 | i++; /* skip ESC */ | 686 | i++; /* skip ESC */ |
599 | if (!isdigit(uchar(news[i]))) | 687 | if (!isdigit(uchar(news[i]))) { |
688 | if (news[i] != L_ESC) | ||
689 | luaL_error(ms->L, "invalid use of " LUA_QL("%c") | ||
690 | " in replacement string", L_ESC); | ||
600 | luaL_addchar(b, news[i]); | 691 | luaL_addchar(b, news[i]); |
692 | } | ||
601 | else if (news[i] == '0') | 693 | else if (news[i] == '0') |
602 | luaL_addlstring(b, s, e - s); | 694 | luaL_addlstring(b, s, e - s); |
603 | else { | 695 | else { |
@@ -610,14 +702,9 @@ static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, | |||
610 | 702 | ||
611 | 703 | ||
612 | static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, | 704 | static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, |
613 | const char *e) { | 705 | const char *e, int tr) { |
614 | lua_State *L = ms->L; | 706 | lua_State *L = ms->L; |
615 | switch (lua_type(L, 3)) { | 707 | switch (tr) { |
616 | case LUA_TNUMBER: | ||
617 | case LUA_TSTRING: { | ||
618 | add_s(ms, b, s, e); | ||
619 | return; | ||
620 | } | ||
621 | case LUA_TFUNCTION: { | 708 | case LUA_TFUNCTION: { |
622 | int n; | 709 | int n; |
623 | lua_pushvalue(L, 3); | 710 | lua_pushvalue(L, 3); |
@@ -630,41 +717,51 @@ static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, | |||
630 | lua_gettable(L, 3); | 717 | lua_gettable(L, 3); |
631 | break; | 718 | break; |
632 | } | 719 | } |
720 | default: { /* LUA_TNUMBER or LUA_TSTRING */ | ||
721 | add_s(ms, b, s, e); | ||
722 | return; | ||
723 | } | ||
633 | } | 724 | } |
634 | if (!lua_toboolean(L, -1)) { /* nil or false? */ | 725 | if (!lua_toboolean(L, -1)) { /* nil or false? */ |
635 | lua_pop(L, 1); | 726 | lua_pop(L, 1); |
636 | lua_pushlstring(L, s, e - s); /* keep original text */ | 727 | lua_pushlstring(L, s, e - s); /* keep original text */ |
637 | } | 728 | } |
638 | else if (!lua_isstring(L, -1)) | 729 | else if (!lua_isstring(L, -1)) |
639 | luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); | 730 | luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); |
640 | luaL_addvalue(b); /* add result to accumulator */ | 731 | luaL_addvalue(b); /* add result to accumulator */ |
641 | } | 732 | } |
642 | 733 | ||
643 | 734 | ||
644 | static int str_gsub (lua_State *L) { | 735 | static int str_gsub (lua_State *L) { |
645 | size_t srcl; | 736 | size_t srcl, lp; |
646 | const char *src = luaL_checklstring(L, 1, &srcl); | 737 | const char *src = luaL_checklstring(L, 1, &srcl); |
647 | const char *p = luaL_checkstring(L, 2); | 738 | const char *p = luaL_checklstring(L, 2, &lp); |
648 | int tr = lua_type(L, 3); | 739 | int tr = lua_type(L, 3); |
649 | int max_s = luaL_optint(L, 4, srcl+1); | 740 | size_t max_s = luaL_optinteger(L, 4, srcl+1); |
650 | int anchor = (*p == '^') ? (p++, 1) : 0; | 741 | int anchor = (*p == '^'); |
651 | int n = 0; | 742 | size_t n = 0; |
652 | MatchState ms; | 743 | MatchState ms; |
653 | luaL_Buffer b; | 744 | luaL_Buffer b; |
654 | luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || | 745 | luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || |
655 | tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, | 746 | tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, |
656 | "string/function/table expected"); | 747 | "string/function/table expected"); |
657 | luaL_buffinit(L, &b); | 748 | luaL_buffinit(L, &b); |
749 | if (anchor) { | ||
750 | p++; lp--; /* skip anchor character */ | ||
751 | } | ||
658 | ms.L = L; | 752 | ms.L = L; |
753 | ms.matchdepth = MAXCCALLS; | ||
659 | ms.src_init = src; | 754 | ms.src_init = src; |
660 | ms.src_end = src+srcl; | 755 | ms.src_end = src+srcl; |
756 | ms.p_end = p + lp; | ||
661 | while (n < max_s) { | 757 | while (n < max_s) { |
662 | const char *e; | 758 | const char *e; |
663 | ms.level = 0; | 759 | ms.level = 0; |
760 | lua_assert(ms.matchdepth == MAXCCALLS); | ||
664 | e = match(&ms, src, p); | 761 | e = match(&ms, src, p); |
665 | if (e) { | 762 | if (e) { |
666 | n++; | 763 | n++; |
667 | add_value(&ms, &b, src, e); | 764 | add_value(&ms, &b, src, e, tr); |
668 | } | 765 | } |
669 | if (e && e>src) /* non empty match? */ | 766 | if (e && e>src) /* non empty match? */ |
670 | src = e; /* skip it */ | 767 | src = e; /* skip it */ |
@@ -682,6 +779,46 @@ static int str_gsub (lua_State *L) { | |||
682 | /* }====================================================== */ | 779 | /* }====================================================== */ |
683 | 780 | ||
684 | 781 | ||
782 | |||
783 | /* | ||
784 | ** {====================================================== | ||
785 | ** STRING FORMAT | ||
786 | ** ======================================================= | ||
787 | */ | ||
788 | |||
789 | /* | ||
790 | ** LUA_INTFRMLEN is the length modifier for integer conversions in | ||
791 | ** 'string.format'; LUA_INTFRM_T is the integer type corresponding to | ||
792 | ** the previous length | ||
793 | */ | ||
794 | #if !defined(LUA_INTFRMLEN) /* { */ | ||
795 | #if defined(LUA_USE_LONGLONG) | ||
796 | |||
797 | #define LUA_INTFRMLEN "ll" | ||
798 | #define LUA_INTFRM_T long long | ||
799 | |||
800 | #else | ||
801 | |||
802 | #define LUA_INTFRMLEN "l" | ||
803 | #define LUA_INTFRM_T long | ||
804 | |||
805 | #endif | ||
806 | #endif /* } */ | ||
807 | |||
808 | |||
809 | /* | ||
810 | ** LUA_FLTFRMLEN is the length modifier for float conversions in | ||
811 | ** 'string.format'; LUA_FLTFRM_T is the float type corresponding to | ||
812 | ** the previous length | ||
813 | */ | ||
814 | #if !defined(LUA_FLTFRMLEN) | ||
815 | |||
816 | #define LUA_FLTFRMLEN "" | ||
817 | #define LUA_FLTFRM_T double | ||
818 | |||
819 | #endif | ||
820 | |||
821 | |||
685 | /* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ | 822 | /* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ |
686 | #define MAX_ITEM 512 | 823 | #define MAX_ITEM 512 |
687 | /* valid flags in a format specification */ | 824 | /* valid flags in a format specification */ |
@@ -698,25 +835,20 @@ static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { | |||
698 | const char *s = luaL_checklstring(L, arg, &l); | 835 | const char *s = luaL_checklstring(L, arg, &l); |
699 | luaL_addchar(b, '"'); | 836 | luaL_addchar(b, '"'); |
700 | while (l--) { | 837 | while (l--) { |
701 | switch (*s) { | 838 | if (*s == '"' || *s == '\\' || *s == '\n') { |
702 | case '"': case '\\': case '\n': { | 839 | luaL_addchar(b, '\\'); |
703 | luaL_addchar(b, '\\'); | 840 | luaL_addchar(b, *s); |
704 | luaL_addchar(b, *s); | ||
705 | break; | ||
706 | } | ||
707 | case '\r': { | ||
708 | luaL_addlstring(b, "\\r", 2); | ||
709 | break; | ||
710 | } | ||
711 | case '\0': { | ||
712 | luaL_addlstring(b, "\\000", 4); | ||
713 | break; | ||
714 | } | ||
715 | default: { | ||
716 | luaL_addchar(b, *s); | ||
717 | break; | ||
718 | } | ||
719 | } | 841 | } |
842 | else if (*s == '\0' || iscntrl(uchar(*s))) { | ||
843 | char buff[10]; | ||
844 | if (!isdigit(uchar(*(s+1)))) | ||
845 | snprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s)); | ||
846 | else | ||
847 | snprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s)); | ||
848 | luaL_addstring(b, buff); | ||
849 | } | ||
850 | else | ||
851 | luaL_addchar(b, *s); | ||
720 | s++; | 852 | s++; |
721 | } | 853 | } |
722 | luaL_addchar(b, '"'); | 854 | luaL_addchar(b, '"'); |
@@ -725,7 +857,7 @@ static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { | |||
725 | static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { | 857 | static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { |
726 | const char *p = strfrmt; | 858 | const char *p = strfrmt; |
727 | while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ | 859 | while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ |
728 | if ((size_t)(p - strfrmt) >= sizeof(FLAGS)) | 860 | if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char)) |
729 | luaL_error(L, "invalid format (repeated flags)"); | 861 | luaL_error(L, "invalid format (repeated flags)"); |
730 | if (isdigit(uchar(*p))) p++; /* skip width */ | 862 | if (isdigit(uchar(*p))) p++; /* skip width */ |
731 | if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ | 863 | if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ |
@@ -737,23 +869,28 @@ static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { | |||
737 | if (isdigit(uchar(*p))) | 869 | if (isdigit(uchar(*p))) |
738 | luaL_error(L, "invalid format (width or precision too long)"); | 870 | luaL_error(L, "invalid format (width or precision too long)"); |
739 | *(form++) = '%'; | 871 | *(form++) = '%'; |
740 | strncpy(form, strfrmt, p - strfrmt + 1); | 872 | memcpy(form, strfrmt, (p - strfrmt + 1) * sizeof(char)); |
741 | form += p - strfrmt + 1; | 873 | form += p - strfrmt + 1; |
742 | *form = '\0'; | 874 | *form = '\0'; |
743 | return p; | 875 | return p; |
744 | } | 876 | } |
745 | 877 | ||
746 | 878 | ||
747 | static void addintlen (char *form) { | 879 | /* |
880 | ** add length modifier into formats | ||
881 | */ | ||
882 | static void addlenmod (char *form, const char *lenmod) { | ||
748 | size_t l = strlen(form); | 883 | size_t l = strlen(form); |
884 | size_t lm = strlen(lenmod); | ||
749 | char spec = form[l - 1]; | 885 | char spec = form[l - 1]; |
750 | strcpy(form + l - 1, LUA_INTFRMLEN); | 886 | strcpy(form + l - 1, lenmod); |
751 | form[l + sizeof(LUA_INTFRMLEN) - 2] = spec; | 887 | form[l + lm - 1] = spec; |
752 | form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0'; | 888 | form[l + lm] = '\0'; |
753 | } | 889 | } |
754 | 890 | ||
755 | 891 | ||
756 | static int str_format (lua_State *L) { | 892 | static int str_format (lua_State *L) { |
893 | int top = lua_gettop(L); | ||
757 | int arg = 1; | 894 | int arg = 1; |
758 | size_t sfl; | 895 | size_t sfl; |
759 | const char *strfrmt = luaL_checklstring(L, arg, &sfl); | 896 | const char *strfrmt = luaL_checklstring(L, arg, &sfl); |
@@ -767,45 +904,61 @@ static int str_format (lua_State *L) { | |||
767 | luaL_addchar(&b, *strfrmt++); /* %% */ | 904 | luaL_addchar(&b, *strfrmt++); /* %% */ |
768 | else { /* format item */ | 905 | else { /* format item */ |
769 | char form[MAX_FORMAT]; /* to store the format (`%...') */ | 906 | char form[MAX_FORMAT]; /* to store the format (`%...') */ |
770 | char buff[MAX_ITEM]; /* to store the formatted item */ | 907 | char *buff = luaL_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */ |
771 | arg++; | 908 | int nb = 0; /* number of bytes in added item */ |
909 | if (++arg > top) | ||
910 | luaL_argerror(L, arg, "no value"); | ||
772 | strfrmt = scanformat(L, strfrmt, form); | 911 | strfrmt = scanformat(L, strfrmt, form); |
773 | switch (*strfrmt++) { | 912 | switch (*strfrmt++) { |
774 | case 'c': { | 913 | case 'c': { |
775 | snprintf(buff, MAX_ITEM, form, (int)luaL_checknumber(L, arg)); | 914 | nb = snprintf(buff, MAX_ITEM, form, luaL_checkint(L, arg)); |
776 | break; | 915 | break; |
777 | } | 916 | } |
778 | case 'd': case 'i': { | 917 | case 'd': case 'i': { |
779 | addintlen(form); | 918 | lua_Number n = luaL_checknumber(L, arg); |
780 | snprintf(buff, MAX_ITEM, form, (LUA_INTFRM_T)luaL_checknumber(L, arg)); | 919 | LUA_INTFRM_T ni = (LUA_INTFRM_T)n; |
920 | lua_Number diff = n - (lua_Number)ni; | ||
921 | luaL_argcheck(L, -1 < diff && diff < 1, arg, | ||
922 | "not a number in proper range"); | ||
923 | addlenmod(form, LUA_INTFRMLEN); | ||
924 | nb = snprintf(buff, MAX_ITEM, form, ni); | ||
781 | break; | 925 | break; |
782 | } | 926 | } |
783 | case 'o': case 'u': case 'x': case 'X': { | 927 | case 'o': case 'u': case 'x': case 'X': { |
784 | addintlen(form); | 928 | lua_Number n = luaL_checknumber(L, arg); |
785 | snprintf(buff, MAX_ITEM, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg)); | 929 | unsigned LUA_INTFRM_T ni = (unsigned LUA_INTFRM_T)n; |
930 | lua_Number diff = n - (lua_Number)ni; | ||
931 | luaL_argcheck(L, -1 < diff && diff < 1, arg, | ||
932 | "not a non-negative number in proper range"); | ||
933 | addlenmod(form, LUA_INTFRMLEN); | ||
934 | nb = snprintf(buff, MAX_ITEM, form, ni); | ||
786 | break; | 935 | break; |
787 | } | 936 | } |
788 | case 'e': case 'E': case 'f': | 937 | case 'e': case 'E': case 'f': |
938 | #if defined(LUA_USE_AFORMAT) | ||
939 | case 'a': case 'A': | ||
940 | #endif | ||
789 | case 'g': case 'G': { | 941 | case 'g': case 'G': { |
790 | snprintf(buff, MAX_ITEM, form, (double)luaL_checknumber(L, arg)); | 942 | addlenmod(form, LUA_FLTFRMLEN); |
943 | nb = snprintf(buff, MAX_ITEM, form, (LUA_FLTFRM_T)luaL_checknumber(L, arg)); | ||
791 | break; | 944 | break; |
792 | } | 945 | } |
793 | case 'q': { | 946 | case 'q': { |
794 | addquoted(L, &b, arg); | 947 | addquoted(L, &b, arg); |
795 | continue; /* skip the 'addsize' at the end */ | 948 | break; |
796 | } | 949 | } |
797 | case 's': { | 950 | case 's': { |
798 | size_t l; | 951 | size_t l; |
799 | const char *s = luaL_checklstring(L, arg, &l); | 952 | const char *s = luaL_tolstring(L, arg, &l); |
800 | if (!strchr(form, '.') && l >= 100) { | 953 | if (!strchr(form, '.') && l >= 100) { |
801 | /* no precision and string is too long to be formatted; | 954 | /* no precision and string is too long to be formatted; |
802 | keep original string */ | 955 | keep original string */ |
803 | lua_pushvalue(L, arg); | ||
804 | luaL_addvalue(&b); | 956 | luaL_addvalue(&b); |
805 | continue; /* skip the `addsize' at the end */ | 957 | break; |
806 | } | 958 | } |
807 | else { | 959 | else { |
808 | snprintf(buff, MAX_ITEM, form, s); | 960 | nb = snprintf(buff, MAX_ITEM, form, s); |
961 | lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ | ||
809 | break; | 962 | break; |
810 | } | 963 | } |
811 | } | 964 | } |
@@ -814,13 +967,15 @@ static int str_format (lua_State *L) { | |||
814 | LUA_QL("format"), *(strfrmt - 1)); | 967 | LUA_QL("format"), *(strfrmt - 1)); |
815 | } | 968 | } |
816 | } | 969 | } |
817 | luaL_addlstring(&b, buff, strlen(buff)); | 970 | luaL_addsize(&b, nb); |
818 | } | 971 | } |
819 | } | 972 | } |
820 | luaL_pushresult(&b); | 973 | luaL_pushresult(&b); |
821 | return 1; | 974 | return 1; |
822 | } | 975 | } |
823 | 976 | ||
977 | /* }====================================================== */ | ||
978 | |||
824 | 979 | ||
825 | static const luaL_Reg strlib[] = { | 980 | static const luaL_Reg strlib[] = { |
826 | {"byte", str_byte}, | 981 | {"byte", str_byte}, |
@@ -828,7 +983,6 @@ static const luaL_Reg strlib[] = { | |||
828 | {"dump", str_dump}, | 983 | {"dump", str_dump}, |
829 | {"find", str_find}, | 984 | {"find", str_find}, |
830 | {"format", str_format}, | 985 | {"format", str_format}, |
831 | {"gfind", gfind_nodef}, | ||
832 | {"gmatch", gmatch}, | 986 | {"gmatch", gmatch}, |
833 | {"gsub", str_gsub}, | 987 | {"gsub", str_gsub}, |
834 | {"len", str_len}, | 988 | {"len", str_len}, |
@@ -843,13 +997,13 @@ static const luaL_Reg strlib[] = { | |||
843 | 997 | ||
844 | 998 | ||
845 | static void createmetatable (lua_State *L) { | 999 | static void createmetatable (lua_State *L) { |
846 | lua_createtable(L, 0, 1); /* create metatable for strings */ | 1000 | lua_createtable(L, 0, 1); /* table to be metatable for strings */ |
847 | lua_pushliteral(L, ""); /* dummy string */ | 1001 | lua_pushliteral(L, ""); /* dummy string */ |
848 | lua_pushvalue(L, -2); | 1002 | lua_pushvalue(L, -2); /* copy table */ |
849 | lua_setmetatable(L, -2); /* set string metatable */ | 1003 | lua_setmetatable(L, -2); /* set table as metatable for strings */ |
850 | lua_pop(L, 1); /* pop dummy string */ | 1004 | lua_pop(L, 1); /* pop dummy string */ |
851 | lua_pushvalue(L, -2); /* string library... */ | 1005 | lua_pushvalue(L, -2); /* get string library */ |
852 | lua_setfield(L, -2, "__index"); /* ...is the __index metamethod */ | 1006 | lua_setfield(L, -2, "__index"); /* metatable.__index = string */ |
853 | lua_pop(L, 1); /* pop metatable */ | 1007 | lua_pop(L, 1); /* pop metatable */ |
854 | } | 1008 | } |
855 | 1009 | ||
@@ -857,12 +1011,8 @@ static void createmetatable (lua_State *L) { | |||
857 | /* | 1011 | /* |
858 | ** Open string library | 1012 | ** Open string library |
859 | */ | 1013 | */ |
860 | LUALIB_API int luaopen_string (lua_State *L) { | 1014 | LUAMOD_API int luaopen_string (lua_State *L) { |
861 | luaL_register(L, LUA_STRLIBNAME, strlib); | 1015 | luaL_newlib(L, strlib); |
862 | #if defined(LUA_COMPAT_GFIND) | ||
863 | lua_getfield(L, -1, "gmatch"); | ||
864 | lua_setfield(L, -2, "gfind"); | ||
865 | #endif | ||
866 | createmetatable(L); | 1016 | createmetatable(L); |
867 | return 1; | 1017 | return 1; |
868 | } | 1018 | } |