summaryrefslogtreecommitdiff
path: root/src/lib_string.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib_string.c')
-rw-r--r--src/lib_string.c790
1 files changed, 790 insertions, 0 deletions
diff --git a/src/lib_string.c b/src/lib_string.c
new file mode 100644
index 00000000..fdd7fbcb
--- /dev/null
+++ b/src/lib_string.c
@@ -0,0 +1,790 @@
1/*
2** String library.
3** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
4**
5** Major portions taken verbatim or adapted from the Lua interpreter.
6** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
7*/
8
9#include <stdio.h>
10
11#define lib_string_c
12#define LUA_LIB
13
14#include "lua.h"
15#include "lauxlib.h"
16#include "lualib.h"
17
18#include "lj_obj.h"
19#include "lj_err.h"
20#include "lj_str.h"
21#include "lj_tab.h"
22#include "lj_state.h"
23#include "lj_ff.h"
24#include "lj_ctype.h"
25#include "lj_lib.h"
26
27/* ------------------------------------------------------------------------ */
28
29#define LJLIB_MODULE_string
30
31LJLIB_ASM(string_len) LJLIB_REC(.)
32{
33 lj_lib_checkstr(L, 1);
34 return FFH_RETRY;
35}
36
37LJLIB_ASM(string_byte) LJLIB_REC(string_range 0)
38{
39 GCstr *s = lj_lib_checkstr(L, 1);
40 int32_t len = (int32_t)s->len;
41 int32_t start = lj_lib_optint(L, 2, 1);
42 int32_t stop = lj_lib_optint(L, 3, start);
43 int32_t n, i;
44 const unsigned char *p;
45 if (stop < 0) stop += len+1;
46 if (start < 0) start += len+1;
47 if (start <= 0) start = 1;
48 if (stop > len) stop = len;
49 if (start > stop) return FFH_RES(0); /* Empty interval: return no results. */
50 start--;
51 n = stop - start;
52 if ((uint32_t)n > LUAI_MAXCSTACK)
53 lj_err_caller(L, LJ_ERR_STRSLC);
54 lj_state_checkstack(L, (MSize)n);
55 p = (const unsigned char *)strdata(s) + start;
56 for (i = 0; i < n; i++)
57 setintV(L->base + i-1, p[i]);
58 return FFH_RES(n);
59}
60
61LJLIB_ASM(string_char)
62{
63 int i, nargs = cast_int(L->top - L->base);
64 char *buf = lj_str_needbuf(L, &G(L)->tmpbuf, (size_t)nargs);
65 for (i = 1; i <= nargs; i++) {
66 int32_t k = lj_lib_checkint(L, i);
67 if (!checku8(k))
68 lj_err_arg(L, i, LJ_ERR_BADVAL);
69 buf[i-1] = (char)k;
70 }
71 setstrV(L, L->base-1, lj_str_new(L, buf, (size_t)nargs));
72 return FFH_RES(1);
73}
74
75LJLIB_ASM(string_sub) LJLIB_REC(string_range 1)
76{
77 lj_lib_checkstr(L, 1);
78 lj_lib_checkint(L, 2);
79 setintV(L->base+2, lj_lib_optint(L, 3, -1));
80 return FFH_RETRY;
81}
82
83LJLIB_ASM(string_rep)
84{
85 GCstr *s = lj_lib_checkstr(L, 1);
86 int32_t len = (int32_t)s->len;
87 int32_t k = lj_lib_checkint(L, 2);
88 int64_t tlen = (int64_t)k * len;
89 const char *src;
90 char *buf;
91 if (k <= 0) return FFH_RETRY;
92 if (tlen > LJ_MAX_STR)
93 lj_err_caller(L, LJ_ERR_STROV);
94 buf = lj_str_needbuf(L, &G(L)->tmpbuf, (MSize)tlen);
95 if (len <= 1) return FFH_RETRY; /* ASM code only needed buffer resize. */
96 src = strdata(s);
97 do {
98 int32_t i = 0;
99 do { *buf++ = src[i++]; } while (i < len);
100 } while (--k > 0);
101 setstrV(L, L->base-1, lj_str_new(L, G(L)->tmpbuf.buf, (size_t)tlen));
102 return FFH_RES(1);
103}
104
105LJLIB_ASM(string_reverse)
106{
107 GCstr *s = lj_lib_checkstr(L, 1);
108 lj_str_needbuf(L, &G(L)->tmpbuf, s->len);
109 return FFH_RETRY;
110}
111LJLIB_ASM_(string_lower)
112LJLIB_ASM_(string_upper)
113
114/* ------------------------------------------------------------------------ */
115
116LJLIB_CF(string_dump)
117{
118 lj_err_caller(L, LJ_ERR_STRDUMP);
119 return 0; /* unreachable */
120}
121
122/* ------------------------------------------------------------------------ */
123
124/* macro to `unsign' a character */
125#define uchar(c) ((unsigned char)(c))
126
127#define CAP_UNFINISHED (-1)
128#define CAP_POSITION (-2)
129
130typedef struct MatchState {
131 const char *src_init; /* init of source string */
132 const char *src_end; /* end (`\0') of source string */
133 lua_State *L;
134 int level; /* total number of captures (finished or unfinished) */
135 struct {
136 const char *init;
137 ptrdiff_t len;
138 } capture[LUA_MAXCAPTURES];
139} MatchState;
140
141#define L_ESC '%'
142#define SPECIALS "^$*+?.([%-"
143
144static int check_capture(MatchState *ms, int l)
145{
146 l -= '1';
147 if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
148 lj_err_caller(ms->L, LJ_ERR_STRCAPI);
149 return l;
150}
151
152static int capture_to_close(MatchState *ms)
153{
154 int level = ms->level;
155 for (level--; level>=0; level--)
156 if (ms->capture[level].len == CAP_UNFINISHED) return level;
157 lj_err_caller(ms->L, LJ_ERR_STRPATC);
158 return 0; /* unreachable */
159}
160
161static const char *classend(MatchState *ms, const char *p)
162{
163 switch (*p++) {
164 case L_ESC:
165 if (*p == '\0')
166 lj_err_caller(ms->L, LJ_ERR_STRPATE);
167 return p+1;
168 case '[':
169 if (*p == '^') p++;
170 do { /* look for a `]' */
171 if (*p == '\0')
172 lj_err_caller(ms->L, LJ_ERR_STRPATM);
173 if (*(p++) == L_ESC && *p != '\0')
174 p++; /* skip escapes (e.g. `%]') */
175 } while (*p != ']');
176 return p+1;
177 default:
178 return p;
179 }
180}
181
182static const unsigned char match_class_map[32] = {
183 0, LJ_CTYPE_ALPHA, 0, LJ_CTYPE_CNTRL, LJ_CTYPE_DIGIT, 0,0,0,0,0,0,0,
184 LJ_CTYPE_LOWER, 0,0,0, LJ_CTYPE_PUNCT, 0,0, LJ_CTYPE_SPACE, 0,
185 LJ_CTYPE_UPPER, 0, LJ_CTYPE_ALNUM, LJ_CTYPE_XDIGIT, 0,0,0,0,0,0,0
186};
187
188static int match_class(int c, int cl)
189{
190 if ((cl & 0xc0) == 0x40) {
191 int t = match_class_map[(cl&0x1f)];
192 if (t) {
193 t = lj_ctype_isa(c, t);
194 return (cl & 0x20) ? t : !t;
195 }
196 if (cl == 'z') return c == 0;
197 if (cl == 'Z') return c != 0;
198 }
199 return (cl == c);
200}
201
202static int matchbracketclass(int c, const char *p, const char *ec)
203{
204 int sig = 1;
205 if (*(p+1) == '^') {
206 sig = 0;
207 p++; /* skip the `^' */
208 }
209 while (++p < ec) {
210 if (*p == L_ESC) {
211 p++;
212 if (match_class(c, uchar(*p)))
213 return sig;
214 }
215 else if ((*(p+1) == '-') && (p+2 < ec)) {
216 p+=2;
217 if (uchar(*(p-2)) <= c && c <= uchar(*p))
218 return sig;
219 }
220 else if (uchar(*p) == c) return sig;
221 }
222 return !sig;
223}
224
225static int singlematch(int c, const char *p, const char *ep)
226{
227 switch (*p) {
228 case '.': return 1; /* matches any char */
229 case L_ESC: return match_class(c, uchar(*(p+1)));
230 case '[': return matchbracketclass(c, p, ep-1);
231 default: return (uchar(*p) == c);
232 }
233}
234
235static const char *match(MatchState *ms, const char *s, const char *p);
236
237static const char *matchbalance(MatchState *ms, const char *s, const char *p)
238{
239 if (*p == 0 || *(p+1) == 0)
240 lj_err_caller(ms->L, LJ_ERR_STRPATU);
241 if (*s != *p) {
242 return NULL;
243 } else {
244 int b = *p;
245 int e = *(p+1);
246 int cont = 1;
247 while (++s < ms->src_end) {
248 if (*s == e) {
249 if (--cont == 0) return s+1;
250 } else if (*s == b) {
251 cont++;
252 }
253 }
254 }
255 return NULL; /* string ends out of balance */
256}
257
258static const char *max_expand(MatchState *ms, const char *s,
259 const char *p, const char *ep)
260{
261 ptrdiff_t i = 0; /* counts maximum expand for item */
262 while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))
263 i++;
264 /* keeps trying to match with the maximum repetitions */
265 while (i>=0) {
266 const char *res = match(ms, (s+i), ep+1);
267 if (res) return res;
268 i--; /* else didn't match; reduce 1 repetition to try again */
269 }
270 return NULL;
271}
272
273static const char *min_expand(MatchState *ms, const char *s,
274 const char *p, const char *ep)
275{
276 for (;;) {
277 const char *res = match(ms, s, ep+1);
278 if (res != NULL)
279 return res;
280 else if (s<ms->src_end && singlematch(uchar(*s), p, ep))
281 s++; /* try with one more repetition */
282 else
283 return NULL;
284 }
285}
286
287static const char *start_capture(MatchState *ms, const char *s,
288 const char *p, int what)
289{
290 const char *res;
291 int level = ms->level;
292 if (level >= LUA_MAXCAPTURES) lj_err_caller(ms->L, LJ_ERR_STRCAPN);
293 ms->capture[level].init = s;
294 ms->capture[level].len = what;
295 ms->level = level+1;
296 if ((res=match(ms, s, p)) == NULL) /* match failed? */
297 ms->level--; /* undo capture */
298 return res;
299}
300
301static const char *end_capture(MatchState *ms, const char *s,
302 const char *p)
303{
304 int l = capture_to_close(ms);
305 const char *res;
306 ms->capture[l].len = s - ms->capture[l].init; /* close capture */
307 if ((res = match(ms, s, p)) == NULL) /* match failed? */
308 ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
309 return res;
310}
311
312static const char *match_capture(MatchState *ms, const char *s, int l)
313{
314 size_t len;
315 l = check_capture(ms, l);
316 len = (size_t)ms->capture[l].len;
317 if ((size_t)(ms->src_end-s) >= len &&
318 memcmp(ms->capture[l].init, s, len) == 0)
319 return s+len;
320 else
321 return NULL;
322}
323
324static const char *match(MatchState *ms, const char *s, const char *p)
325{
326 init: /* using goto's to optimize tail recursion */
327 switch (*p) {
328 case '(': /* start capture */
329 if (*(p+1) == ')') /* position capture? */
330 return start_capture(ms, s, p+2, CAP_POSITION);
331 else
332 return start_capture(ms, s, p+1, CAP_UNFINISHED);
333 case ')': /* end capture */
334 return end_capture(ms, s, p+1);
335 case L_ESC:
336 switch (*(p+1)) {
337 case 'b': /* balanced string? */
338 s = matchbalance(ms, s, p+2);
339 if (s == NULL) return NULL;
340 p+=4;
341 goto init; /* else return match(ms, s, p+4); */
342 case 'f': { /* frontier? */
343 const char *ep; char previous;
344 p += 2;
345 if (*p != '[')
346 lj_err_caller(ms->L, LJ_ERR_STRPATB);
347 ep = classend(ms, p); /* points to what is next */
348 previous = (s == ms->src_init) ? '\0' : *(s-1);
349 if (matchbracketclass(uchar(previous), p, ep-1) ||
350 !matchbracketclass(uchar(*s), p, ep-1)) return NULL;
351 p=ep;
352 goto init; /* else return match(ms, s, ep); */
353 }
354 default:
355 if (lj_ctype_isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */
356 s = match_capture(ms, s, uchar(*(p+1)));
357 if (s == NULL) return NULL;
358 p+=2;
359 goto init; /* else return match(ms, s, p+2) */
360 }
361 goto dflt; /* case default */
362 }
363 case '\0': /* end of pattern */
364 return s; /* match succeeded */
365 case '$':
366 if (*(p+1) == '\0') /* is the `$' the last char in pattern? */
367 return (s == ms->src_end) ? s : NULL; /* check end of string */
368 else
369 goto dflt;
370 default: dflt: { /* it is a pattern item */
371 const char *ep = classend(ms, p); /* points to what is next */
372 int m = s<ms->src_end && singlematch(uchar(*s), p, ep);
373 switch (*ep) {
374 case '?': { /* optional */
375 const char *res;
376 if (m && ((res=match(ms, s+1, ep+1)) != NULL))
377 return res;
378 p=ep+1;
379 goto init; /* else return match(ms, s, ep+1); */
380 }
381 case '*': /* 0 or more repetitions */
382 return max_expand(ms, s, p, ep);
383 case '+': /* 1 or more repetitions */
384 return (m ? max_expand(ms, s+1, p, ep) : NULL);
385 case '-': /* 0 or more repetitions (minimum) */
386 return min_expand(ms, s, p, ep);
387 default:
388 if (!m) return NULL;
389 s++; p=ep;
390 goto init; /* else return match(ms, s+1, ep); */
391 }
392 }
393 }
394}
395
396static const char *lmemfind(const char *s1, size_t l1,
397 const char *s2, size_t l2)
398{
399 if (l2 == 0) {
400 return s1; /* empty strings are everywhere */
401 } else if (l2 > l1) {
402 return NULL; /* avoids a negative `l1' */
403 } else {
404 const char *init; /* to search for a `*s2' inside `s1' */
405 l2--; /* 1st char will be checked by `memchr' */
406 l1 = l1-l2; /* `s2' cannot be found after that */
407 while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
408 init++; /* 1st char is already checked */
409 if (memcmp(init, s2+1, l2) == 0) {
410 return init-1;
411 } else { /* correct `l1' and `s1' to try again */
412 l1 -= (size_t)(init-s1);
413 s1 = init;
414 }
415 }
416 return NULL; /* not found */
417 }
418}
419
420static void push_onecapture(MatchState *ms, int i, const char *s, const char *e)
421{
422 if (i >= ms->level) {
423 if (i == 0) /* ms->level == 0, too */
424 lua_pushlstring(ms->L, s, (size_t)(e - s)); /* add whole match */
425 else
426 lj_err_caller(ms->L, LJ_ERR_STRCAPI);
427 } else {
428 ptrdiff_t l = ms->capture[i].len;
429 if (l == CAP_UNFINISHED) lj_err_caller(ms->L, LJ_ERR_STRCAPU);
430 if (l == CAP_POSITION)
431 lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
432 else
433 lua_pushlstring(ms->L, ms->capture[i].init, (size_t)l);
434 }
435}
436
437static int push_captures(MatchState *ms, const char *s, const char *e)
438{
439 int i;
440 int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
441 luaL_checkstack(ms->L, nlevels, "too many captures");
442 for (i = 0; i < nlevels; i++)
443 push_onecapture(ms, i, s, e);
444 return nlevels; /* number of strings pushed */
445}
446
447static ptrdiff_t posrelat(ptrdiff_t pos, size_t len)
448{
449 /* relative string position: negative means back from end */
450 if (pos < 0) pos += (ptrdiff_t)len + 1;
451 return (pos >= 0) ? pos : 0;
452}
453
454static int str_find_aux(lua_State *L, int find)
455{
456 size_t l1, l2;
457 const char *s = luaL_checklstring(L, 1, &l1);
458 const char *p = luaL_checklstring(L, 2, &l2);
459 ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1;
460 if (init < 0)
461 init = 0;
462 else if ((size_t)(init) > l1)
463 init = (ptrdiff_t)l1;
464 if (find && (lua_toboolean(L, 4) || /* explicit request? */
465 strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */
466 /* do a plain search */
467 const char *s2 = lmemfind(s+init, l1-(size_t)init, p, l2);
468 if (s2) {
469 lua_pushinteger(L, s2-s+1);
470 lua_pushinteger(L, s2-s+(ptrdiff_t)l2);
471 return 2;
472 }
473 } else {
474 MatchState ms;
475 int anchor = (*p == '^') ? (p++, 1) : 0;
476 const char *s1=s+init;
477 ms.L = L;
478 ms.src_init = s;
479 ms.src_end = s+l1;
480 do {
481 const char *res;
482 ms.level = 0;
483 if ((res=match(&ms, s1, p)) != NULL) {
484 if (find) {
485 lua_pushinteger(L, s1-s+1); /* start */
486 lua_pushinteger(L, res-s); /* end */
487 return push_captures(&ms, NULL, 0) + 2;
488 } else {
489 return push_captures(&ms, s1, res);
490 }
491 }
492 } while (s1++ < ms.src_end && !anchor);
493 }
494 lua_pushnil(L); /* not found */
495 return 1;
496}
497
498LJLIB_CF(string_find)
499{
500 return str_find_aux(L, 1);
501}
502
503LJLIB_CF(string_match)
504{
505 return str_find_aux(L, 0);
506}
507
508LJLIB_NOREG LJLIB_CF(string_gmatch_aux)
509{
510 const char *p = strVdata(lj_lib_upvalue(L, 2));
511 GCstr *str = strV(lj_lib_upvalue(L, 1));
512 const char *s = strdata(str);
513 TValue *tvpos = lj_lib_upvalue(L, 3);
514 const char *src = s + tvpos->u32.lo;
515 MatchState ms;
516 ms.L = L;
517 ms.src_init = s;
518 ms.src_end = s + str->len;
519 for (; src <= ms.src_end; src++) {
520 const char *e;
521 ms.level = 0;
522 if ((e = match(&ms, src, p)) != NULL) {
523 int32_t pos = (int32_t)(e - s);
524 if (e == src) pos++; /* Ensure progress for empty match. */
525 tvpos->u32.lo = (uint32_t)pos;
526 return push_captures(&ms, src, e);
527 }
528 }
529 return 0; /* not found */
530}
531
532LJLIB_CF(string_gmatch)
533{
534 lj_lib_checkstr(L, 1);
535 lj_lib_checkstr(L, 2);
536 L->top = L->base+3;
537 (L->top-1)->u64 = 0;
538 lua_pushcclosure(L, lj_cf_string_gmatch_aux, 3);
539 funcV(L->top-1)->c.ffid = FF_string_gmatch_aux;
540 return 1;
541}
542
543static void add_s(MatchState *ms, luaL_Buffer *b, const char *s, const char *e)
544{
545 size_t l, i;
546 const char *news = lua_tolstring(ms->L, 3, &l);
547 for (i = 0; i < l; i++) {
548 if (news[i] != L_ESC) {
549 luaL_addchar(b, news[i]);
550 } else {
551 i++; /* skip ESC */
552 if (!lj_ctype_isdigit(uchar(news[i]))) {
553 luaL_addchar(b, news[i]);
554 } else if (news[i] == '0') {
555 luaL_addlstring(b, s, (size_t)(e - s));
556 } else {
557 push_onecapture(ms, news[i] - '1', s, e);
558 luaL_addvalue(b); /* add capture to accumulated result */
559 }
560 }
561 }
562}
563
564static void add_value(MatchState *ms, luaL_Buffer *b,
565 const char *s, const char *e)
566{
567 lua_State *L = ms->L;
568 switch (lua_type(L, 3)) {
569 case LUA_TNUMBER:
570 case LUA_TSTRING: {
571 add_s(ms, b, s, e);
572 return;
573 }
574 case LUA_TFUNCTION: {
575 int n;
576 lua_pushvalue(L, 3);
577 n = push_captures(ms, s, e);
578 lua_call(L, n, 1);
579 break;
580 }
581 case LUA_TTABLE: {
582 push_onecapture(ms, 0, s, e);
583 lua_gettable(L, 3);
584 break;
585 }
586 }
587 if (!lua_toboolean(L, -1)) { /* nil or false? */
588 lua_pop(L, 1);
589 lua_pushlstring(L, s, (size_t)(e - s)); /* keep original text */
590 } else if (!lua_isstring(L, -1)) {
591 lj_err_callerv(L, LJ_ERR_STRGSRV, luaL_typename(L, -1));
592 }
593 luaL_addvalue(b); /* add result to accumulator */
594}
595
596LJLIB_CF(string_gsub)
597{
598 size_t srcl;
599 const char *src = luaL_checklstring(L, 1, &srcl);
600 const char *p = luaL_checkstring(L, 2);
601 int tr = lua_type(L, 3);
602 int max_s = luaL_optint(L, 4, (int)(srcl+1));
603 int anchor = (*p == '^') ? (p++, 1) : 0;
604 int n = 0;
605 MatchState ms;
606 luaL_Buffer b;
607 if (!(tr == LUA_TNUMBER || tr == LUA_TSTRING ||
608 tr == LUA_TFUNCTION || tr == LUA_TTABLE))
609 lj_err_arg(L, 3, LJ_ERR_NOSFT);
610 luaL_buffinit(L, &b);
611 ms.L = L;
612 ms.src_init = src;
613 ms.src_end = src+srcl;
614 while (n < max_s) {
615 const char *e;
616 ms.level = 0;
617 e = match(&ms, src, p);
618 if (e) {
619 n++;
620 add_value(&ms, &b, src, e);
621 }
622 if (e && e>src) /* non empty match? */
623 src = e; /* skip it */
624 else if (src < ms.src_end)
625 luaL_addchar(&b, *src++);
626 else
627 break;
628 if (anchor)
629 break;
630 }
631 luaL_addlstring(&b, src, (size_t)(ms.src_end-src));
632 luaL_pushresult(&b);
633 lua_pushinteger(L, n); /* number of substitutions */
634 return 2;
635}
636
637/* ------------------------------------------------------------------------ */
638
639/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
640#define MAX_FMTITEM 512
641/* valid flags in a format specification */
642#define FMT_FLAGS "-+ #0"
643/*
644** maximum size of each format specification (such as '%-099.99d')
645** (+10 accounts for %99.99x plus margin of error)
646*/
647#define MAX_FMTSPEC (sizeof(FMT_FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
648
649static void addquoted(lua_State *L, luaL_Buffer *b, int arg)
650{
651 GCstr *str = lj_lib_checkstr(L, arg);
652 int32_t len = (int32_t)str->len;
653 const char *s = strdata(str);
654 luaL_addchar(b, '"');
655 while (len--) {
656 switch (*s) {
657 case '"': case '\\': case '\n':
658 luaL_addchar(b, '\\');
659 luaL_addchar(b, *s);
660 break;
661 case '\r':
662 luaL_addlstring(b, "\\r", 2);
663 break;
664 case '\0':
665 luaL_addlstring(b, "\\000", 4);
666 break;
667 default:
668 luaL_addchar(b, *s);
669 break;
670 }
671 s++;
672 }
673 luaL_addchar(b, '"');
674}
675
676static const char *scanformat(lua_State *L, const char *strfrmt, char *form)
677{
678 const char *p = strfrmt;
679 while (*p != '\0' && strchr(FMT_FLAGS, *p) != NULL) p++; /* skip flags */
680 if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS))
681 lj_err_caller(L, LJ_ERR_STRFMTR);
682 if (lj_ctype_isdigit(uchar(*p))) p++; /* skip width */
683 if (lj_ctype_isdigit(uchar(*p))) p++; /* (2 digits at most) */
684 if (*p == '.') {
685 p++;
686 if (lj_ctype_isdigit(uchar(*p))) p++; /* skip precision */
687 if (lj_ctype_isdigit(uchar(*p))) p++; /* (2 digits at most) */
688 }
689 if (lj_ctype_isdigit(uchar(*p)))
690 lj_err_caller(L, LJ_ERR_STRFMTW);
691 *(form++) = '%';
692 strncpy(form, strfrmt, (size_t)(p - strfrmt + 1));
693 form += p - strfrmt + 1;
694 *form = '\0';
695 return p;
696}
697
698static void addintlen(char *form)
699{
700 size_t l = strlen(form);
701 char spec = form[l - 1];
702 strcpy(form + l - 1, LUA_INTFRMLEN);
703 form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;
704 form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0';
705}
706
707LJLIB_CF(string_format)
708{
709 int arg = 1;
710 GCstr *fmt = lj_lib_checkstr(L, arg);
711 const char *strfrmt = strdata(fmt);
712 const char *strfrmt_end = strfrmt + fmt->len;
713 luaL_Buffer b;
714 luaL_buffinit(L, &b);
715 while (strfrmt < strfrmt_end) {
716 if (*strfrmt != L_ESC) {
717 luaL_addchar(&b, *strfrmt++);
718 } else if (*++strfrmt == L_ESC) {
719 luaL_addchar(&b, *strfrmt++); /* %% */
720 } else { /* format item */
721 char form[MAX_FMTSPEC]; /* to store the format (`%...') */
722 char buff[MAX_FMTITEM]; /* to store the formatted item */
723 arg++;
724 strfrmt = scanformat(L, strfrmt, form);
725 switch (*strfrmt++) {
726 case 'c':
727 sprintf(buff, form, lj_lib_checkint(L, arg));
728 break;
729 case 'd': case 'i':
730 addintlen(form);
731 sprintf(buff, form, (LUA_INTFRM_T)lj_lib_checknum(L, arg));
732 break;
733 case 'o': case 'u': case 'x': case 'X':
734 addintlen(form);
735 sprintf(buff, form, (unsigned LUA_INTFRM_T)lj_lib_checknum(L, arg));
736 break;
737 case 'e': case 'E': case 'f': case 'g': case 'G':
738 sprintf(buff, form, (double)lj_lib_checknum(L, arg));
739 break;
740 case 'q':
741 addquoted(L, &b, arg);
742 continue;
743 case 'p':
744 lj_str_pushf(L, "%p", lua_topointer(L, arg));
745 luaL_addvalue(&b);
746 continue;
747 case 's': {
748 GCstr *str = lj_lib_checkstr(L, arg);
749 if (!strchr(form, '.') && str->len >= 100) {
750 /* no precision and string is too long to be formatted;
751 keep original string */
752 setstrV(L, L->top++, str);
753 luaL_addvalue(&b);
754 continue;
755 }
756 sprintf(buff, form, strdata(str));
757 break;
758 }
759 default:
760 lj_err_callerv(L, LJ_ERR_STRFMTO, *(strfrmt -1));
761 break;
762 }
763 luaL_addlstring(&b, buff, strlen(buff));
764 }
765 }
766 luaL_pushresult(&b);
767 return 1;
768}
769
770/* ------------------------------------------------------------------------ */
771
772#include "lj_libdef.h"
773
774LUALIB_API int luaopen_string(lua_State *L)
775{
776 GCtab *mt;
777 LJ_LIB_REG(L, string);
778#if defined(LUA_COMPAT_GFIND)
779 lua_getfield(L, -1, "gmatch");
780 lua_setfield(L, -2, "gfind");
781#endif
782 mt = lj_tab_new(L, 0, 1);
783 /* NOBARRIER: G(L)->mmname[] is a GC root. */
784 setgcref(G(L)->basemt[~LJ_TSTR], obj2gco(mt));
785 settabV(L, lj_tab_setstr(L, mt, strref(G(L)->mmname[MM_index])),
786 tabV(L->top-1));
787 mt->nomm = cast_byte(~(1u<<MM_index));
788 return 1;
789}
790