Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentation/src/lua/lstrlib.c @ 2398

Last change on this file since 2398 was 1810, checked in by rgrieder, 16 years ago

merged ceguilua branch back to trunk

  • Property svn:eol-style set to native
File size: 22.9 KB
Line 
1/*
2** $Id: lstrlib.c,v 1.132.1.3 2007/12/28 15:32:23 roberto Exp $
3** Standard library for string operations and pattern-matching
4** See Copyright Notice in lua.h
5*/
6
7
8#include <ctype.h>
9#include <stddef.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#define lstrlib_c
15#define LUA_LIB
16
17#include "lua.h"
18
19#include "lauxlib.h"
20#include "lualib.h"
21
22
23/* macro to `unsign' a character */
24#define uchar(c)        ((unsigned char)(c))
25
26
27
28static int str_len (lua_State *L) {
29  size_t l;
30  luaL_checklstring(L, 1, &l);
31  lua_pushinteger(L, l);
32  return 1;
33}
34
35
36static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) {
37  /* relative string position: negative means back from end */
38  return (pos>=0) ? pos : (ptrdiff_t)len+pos+1;
39}
40
41
42static int str_sub (lua_State *L) {
43  size_t l;
44  const char *s = luaL_checklstring(L, 1, &l);
45  ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l);
46  ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l);
47  if (start < 1) start = 1;
48  if (end > (ptrdiff_t)l) end = (ptrdiff_t)l;
49  if (start <= end)
50    lua_pushlstring(L, s+start-1, end-start+1);
51  else lua_pushliteral(L, "");
52  return 1;
53}
54
55
56static int str_reverse (lua_State *L) {
57  size_t l;
58  luaL_Buffer b;
59  const char *s = luaL_checklstring(L, 1, &l);
60  luaL_buffinit(L, &b);
61  while (l--) luaL_addchar(&b, s[l]);
62  luaL_pushresult(&b);
63  return 1;
64}
65
66
67static int str_lower (lua_State *L) {
68  size_t l;
69  size_t i;
70  luaL_Buffer b;
71  const char *s = luaL_checklstring(L, 1, &l);
72  luaL_buffinit(L, &b);
73  for (i=0; i<l; i++)
74    luaL_addchar(&b, tolower(uchar(s[i])));
75  luaL_pushresult(&b);
76  return 1;
77}
78
79
80static int str_upper (lua_State *L) {
81  size_t l;
82  size_t i;
83  luaL_Buffer b;
84  const char *s = luaL_checklstring(L, 1, &l);
85  luaL_buffinit(L, &b);
86  for (i=0; i<l; i++)
87    luaL_addchar(&b, toupper(uchar(s[i])));
88  luaL_pushresult(&b);
89  return 1;
90}
91
92static int str_rep (lua_State *L) {
93  size_t l;
94  luaL_Buffer b;
95  const char *s = luaL_checklstring(L, 1, &l);
96  int n = luaL_checkint(L, 2);
97  luaL_buffinit(L, &b);
98  while (n-- > 0)
99    luaL_addlstring(&b, s, l);
100  luaL_pushresult(&b);
101  return 1;
102}
103
104
105static int str_byte (lua_State *L) {
106  size_t l;
107  const char *s = luaL_checklstring(L, 1, &l);
108  ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l);
109  ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l);
110  int n, i;
111  if (posi <= 0) posi = 1;
112  if ((size_t)pose > l) pose = l;
113  if (posi > pose) return 0;  /* empty interval; return no values */
114  n = (int)(pose -  posi + 1);
115  if (posi + n <= pose)  /* overflow? */
116    luaL_error(L, "string slice too long");
117  luaL_checkstack(L, n, "string slice too long");
118  for (i=0; i<n; i++)
119    lua_pushinteger(L, uchar(s[posi+i-1]));
120  return n;
121}
122
123
124static int str_char (lua_State *L) {
125  int n = lua_gettop(L);  /* number of arguments */
126  int i;
127  luaL_Buffer b;
128  luaL_buffinit(L, &b);
129  for (i=1; i<=n; i++) {
130    int c = luaL_checkint(L, i);
131    luaL_argcheck(L, uchar(c) == c, i, "invalid value");
132    luaL_addchar(&b, uchar(c));
133  }
134  luaL_pushresult(&b);
135  return 1;
136}
137
138
139static int writer (lua_State *L, const void* b, size_t size, void* B) {
140  (void)L;
141  luaL_addlstring((luaL_Buffer*) B, (const char *)b, size);
142  return 0;
143}
144
145
146static int str_dump (lua_State *L) {
147  luaL_Buffer b;
148  luaL_checktype(L, 1, LUA_TFUNCTION);
149  lua_settop(L, 1);
150  luaL_buffinit(L,&b);
151  if (lua_dump(L, writer, &b) != 0)
152    luaL_error(L, "unable to dump given function");
153  luaL_pushresult(&b);
154  return 1;
155}
156
157
158
159/*
160** {======================================================
161** PATTERN MATCHING
162** =======================================================
163*/
164
165
166#define CAP_UNFINISHED  (-1)
167#define CAP_POSITION    (-2)
168
169typedef struct MatchState {
170  const char *src_init;  /* init of source string */
171  const char *src_end;  /* end (`\0') of source string */
172  lua_State *L;
173  int level;  /* total number of captures (finished or unfinished) */
174  struct {
175    const char *init;
176    ptrdiff_t len;
177  } capture[LUA_MAXCAPTURES];
178} MatchState;
179
180
181#define L_ESC           '%'
182#define SPECIALS        "^$*+?.([%-"
183
184
185static int check_capture (MatchState *ms, int l) {
186  l -= '1';
187  if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
188    return luaL_error(ms->L, "invalid capture index");
189  return l;
190}
191
192
193static int capture_to_close (MatchState *ms) {
194  int level = ms->level;
195  for (level--; level>=0; level--)
196    if (ms->capture[level].len == CAP_UNFINISHED) return level;
197  return luaL_error(ms->L, "invalid pattern capture");
198}
199
200
201static const char *classend (MatchState *ms, const char *p) {
202  switch (*p++) {
203    case L_ESC: {
204      if (*p == '\0')
205        luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")");
206      return p+1;
207    }
208    case '[': {
209      if (*p == '^') p++;
210      do {  /* look for a `]' */
211        if (*p == '\0')
212          luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")");
213        if (*(p++) == L_ESC && *p != '\0')
214          p++;  /* skip escapes (e.g. `%]') */
215      } while (*p != ']');
216      return p+1;
217    }
218    default: {
219      return p;
220    }
221  }
222}
223
224
225static int match_class (int c, int cl) {
226  int res;
227  switch (tolower(cl)) {
228    case 'a' : res = isalpha(c); break;
229    case 'c' : res = iscntrl(c); break;
230    case 'd' : res = isdigit(c); break;
231    case 'l' : res = islower(c); break;
232    case 'p' : res = ispunct(c); break;
233    case 's' : res = isspace(c); break;
234    case 'u' : res = isupper(c); break;
235    case 'w' : res = isalnum(c); break;
236    case 'x' : res = isxdigit(c); break;
237    case 'z' : res = (c == 0); break;
238    default: return (cl == c);
239  }
240  return (islower(cl) ? res : !res);
241}
242
243
244static int matchbracketclass (int c, const char *p, const char *ec) {
245  int sig = 1;
246  if (*(p+1) == '^') {
247    sig = 0;
248    p++;  /* skip the `^' */
249  }
250  while (++p < ec) {
251    if (*p == L_ESC) {
252      p++;
253      if (match_class(c, uchar(*p)))
254        return sig;
255    }
256    else if ((*(p+1) == '-') && (p+2 < ec)) {
257      p+=2;
258      if (uchar(*(p-2)) <= c && c <= uchar(*p))
259        return sig;
260    }
261    else if (uchar(*p) == c) return sig;
262  }
263  return !sig;
264}
265
266
267static int singlematch (int c, const char *p, const char *ep) {
268  switch (*p) {
269    case '.': return 1;  /* matches any char */
270    case L_ESC: return match_class(c, uchar(*(p+1)));
271    case '[': return matchbracketclass(c, p, ep-1);
272    default:  return (uchar(*p) == c);
273  }
274}
275
276
277static const char *match (MatchState *ms, const char *s, const char *p);
278
279
280static const char *matchbalance (MatchState *ms, const char *s,
281                                   const char *p) {
282  if (*p == 0 || *(p+1) == 0)
283    luaL_error(ms->L, "unbalanced pattern");
284  if (*s != *p) return NULL;
285  else {
286    int b = *p;
287    int e = *(p+1);
288    int cont = 1;
289    while (++s < ms->src_end) {
290      if (*s == e) {
291        if (--cont == 0) return s+1;
292      }
293      else if (*s == b) cont++;
294    }
295  }
296  return NULL;  /* string ends out of balance */
297}
298
299
300static const char *max_expand (MatchState *ms, const char *s,
301                                 const char *p, const char *ep) {
302  ptrdiff_t i = 0;  /* counts maximum expand for item */
303  while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))
304    i++;
305  /* keeps trying to match with the maximum repetitions */
306  while (i>=0) {
307    const char *res = match(ms, (s+i), ep+1);
308    if (res) return res;
309    i--;  /* else didn't match; reduce 1 repetition to try again */
310  }
311  return NULL;
312}
313
314
315static const char *min_expand (MatchState *ms, const char *s,
316                                 const char *p, const char *ep) {
317  for (;;) {
318    const char *res = match(ms, s, ep+1);
319    if (res != NULL)
320      return res;
321    else if (s<ms->src_end && singlematch(uchar(*s), p, ep))
322      s++;  /* try with one more repetition */
323    else return NULL;
324  }
325}
326
327
328static const char *start_capture (MatchState *ms, const char *s,
329                                    const char *p, int what) {
330  const char *res;
331  int level = ms->level;
332  if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures");
333  ms->capture[level].init = s;
334  ms->capture[level].len = what;
335  ms->level = level+1;
336  if ((res=match(ms, s, p)) == NULL)  /* match failed? */
337    ms->level--;  /* undo capture */
338  return res;
339}
340
341
342static const char *end_capture (MatchState *ms, const char *s,
343                                  const char *p) {
344  int l = capture_to_close(ms);
345  const char *res;
346  ms->capture[l].len = s - ms->capture[l].init;  /* close capture */
347  if ((res = match(ms, s, p)) == NULL)  /* match failed? */
348    ms->capture[l].len = CAP_UNFINISHED;  /* undo capture */
349  return res;
350}
351
352
353static const char *match_capture (MatchState *ms, const char *s, int l) {
354  size_t len;
355  l = check_capture(ms, l);
356  len = ms->capture[l].len;
357  if ((size_t)(ms->src_end-s) >= len &&
358      memcmp(ms->capture[l].init, s, len) == 0)
359    return s+len;
360  else return NULL;
361}
362
363
364static const char *match (MatchState *ms, const char *s, const char *p) {
365  init: /* using goto's to optimize tail recursion */
366  switch (*p) {
367    case '(': {  /* start capture */
368      if (*(p+1) == ')')  /* position capture? */
369        return start_capture(ms, s, p+2, CAP_POSITION);
370      else
371        return start_capture(ms, s, p+1, CAP_UNFINISHED);
372    }
373    case ')': {  /* end capture */
374      return end_capture(ms, s, p+1);
375    }
376    case L_ESC: {
377      switch (*(p+1)) {
378        case 'b': {  /* balanced string? */
379          s = matchbalance(ms, s, p+2);
380          if (s == NULL) return NULL;
381          p+=4; goto init;  /* else return match(ms, s, p+4); */
382        }
383        case 'f': {  /* frontier? */
384          const char *ep; char previous;
385          p += 2;
386          if (*p != '[')
387            luaL_error(ms->L, "missing " LUA_QL("[") " after "
388                               LUA_QL("%%f") " in pattern");
389          ep = classend(ms, p);  /* points to what is next */
390          previous = (s == ms->src_init) ? '\0' : *(s-1);
391          if (matchbracketclass(uchar(previous), p, ep-1) ||
392             !matchbracketclass(uchar(*s), p, ep-1)) return NULL;
393          p=ep; goto init;  /* else return match(ms, s, ep); */
394        }
395        default: {
396          if (isdigit(uchar(*(p+1)))) {  /* capture results (%0-%9)? */
397            s = match_capture(ms, s, uchar(*(p+1)));
398            if (s == NULL) return NULL;
399            p+=2; goto init;  /* else return match(ms, s, p+2) */
400          }
401          goto dflt;  /* case default */
402        }
403      }
404    }
405    case '\0': {  /* end of pattern */
406      return s;  /* match succeeded */
407    }
408    case '$': {
409      if (*(p+1) == '\0')  /* is the `$' the last char in pattern? */
410        return (s == ms->src_end) ? s : NULL;  /* check end of string */
411      else goto dflt;
412    }
413    default: dflt: {  /* it is a pattern item */
414      const char *ep = classend(ms, p);  /* points to what is next */
415      int m = s<ms->src_end && singlematch(uchar(*s), p, ep);
416      switch (*ep) {
417        case '?': {  /* optional */
418          const char *res;
419          if (m && ((res=match(ms, s+1, ep+1)) != NULL))
420            return res;
421          p=ep+1; goto init;  /* else return match(ms, s, ep+1); */
422        }
423        case '*': {  /* 0 or more repetitions */
424          return max_expand(ms, s, p, ep);
425        }
426        case '+': {  /* 1 or more repetitions */
427          return (m ? max_expand(ms, s+1, p, ep) : NULL);
428        }
429        case '-': {  /* 0 or more repetitions (minimum) */
430          return min_expand(ms, s, p, ep);
431        }
432        default: {
433          if (!m) return NULL;
434          s++; p=ep; goto init;  /* else return match(ms, s+1, ep); */
435        }
436      }
437    }
438  }
439}
440
441
442
443static const char *lmemfind (const char *s1, size_t l1,
444                               const char *s2, size_t l2) {
445  if (l2 == 0) return s1;  /* empty strings are everywhere */
446  else if (l2 > l1) return NULL;  /* avoids a negative `l1' */
447  else {
448    const char *init;  /* to search for a `*s2' inside `s1' */
449    l2--;  /* 1st char will be checked by `memchr' */
450    l1 = l1-l2;  /* `s2' cannot be found after that */
451    while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
452      init++;   /* 1st char is already checked */
453      if (memcmp(init, s2+1, l2) == 0)
454        return init-1;
455      else {  /* correct `l1' and `s1' to try again */
456        l1 -= init-s1;
457        s1 = init;
458      }
459    }
460    return NULL;  /* not found */
461  }
462}
463
464
465static void push_onecapture (MatchState *ms, int i, const char *s,
466                                                    const char *e) {
467  if (i >= ms->level) {
468    if (i == 0)  /* ms->level == 0, too */
469      lua_pushlstring(ms->L, s, e - s);  /* add whole match */
470    else
471      luaL_error(ms->L, "invalid capture index");
472  }
473  else {
474    ptrdiff_t l = ms->capture[i].len;
475    if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture");
476    if (l == CAP_POSITION)
477      lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
478    else
479      lua_pushlstring(ms->L, ms->capture[i].init, l);
480  }
481}
482
483
484static int push_captures (MatchState *ms, const char *s, const char *e) {
485  int i;
486  int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
487  luaL_checkstack(ms->L, nlevels, "too many captures");
488  for (i = 0; i < nlevels; i++)
489    push_onecapture(ms, i, s, e);
490  return nlevels;  /* number of strings pushed */
491}
492
493
494static int str_find_aux (lua_State *L, int find) {
495  size_t l1, l2;
496  const char *s = luaL_checklstring(L, 1, &l1);
497  const char *p = luaL_checklstring(L, 2, &l2);
498  ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1;
499  if (init < 0) init = 0;
500  else if ((size_t)(init) > l1) init = (ptrdiff_t)l1;
501  if (find && (lua_toboolean(L, 4) ||  /* explicit request? */
502      strpbrk(p, SPECIALS) == NULL)) {  /* or no special characters? */
503    /* do a plain search */
504    const char *s2 = lmemfind(s+init, l1-init, p, l2);
505    if (s2) {
506      lua_pushinteger(L, s2-s+1);
507      lua_pushinteger(L, s2-s+l2);
508      return 2;
509    }
510  }
511  else {
512    MatchState ms;
513    int anchor = (*p == '^') ? (p++, 1) : 0;
514    const char *s1=s+init;
515    ms.L = L;
516    ms.src_init = s;
517    ms.src_end = s+l1;
518    do {
519      const char *res;
520      ms.level = 0;
521      if ((res=match(&ms, s1, p)) != NULL) {
522        if (find) {
523          lua_pushinteger(L, s1-s+1);  /* start */
524          lua_pushinteger(L, res-s);   /* end */
525          return push_captures(&ms, NULL, 0) + 2;
526        }
527        else
528          return push_captures(&ms, s1, res);
529      }
530    } while (s1++ < ms.src_end && !anchor);
531  }
532  lua_pushnil(L);  /* not found */
533  return 1;
534}
535
536
537static int str_find (lua_State *L) {
538  return str_find_aux(L, 1);
539}
540
541
542static int str_match (lua_State *L) {
543  return str_find_aux(L, 0);
544}
545
546
547static int gmatch_aux (lua_State *L) {
548  MatchState ms;
549  size_t ls;
550  const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
551  const char *p = lua_tostring(L, lua_upvalueindex(2));
552  const char *src;
553  ms.L = L;
554  ms.src_init = s;
555  ms.src_end = s+ls;
556  for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3));
557       src <= ms.src_end;
558       src++) {
559    const char *e;
560    ms.level = 0;
561    if ((e = match(&ms, src, p)) != NULL) {
562      lua_Integer newstart = e-s;
563      if (e == src) newstart++;  /* empty match? go at least one position */
564      lua_pushinteger(L, newstart);
565      lua_replace(L, lua_upvalueindex(3));
566      return push_captures(&ms, src, e);
567    }
568  }
569  return 0;  /* not found */
570}
571
572
573static int gmatch (lua_State *L) {
574  luaL_checkstring(L, 1);
575  luaL_checkstring(L, 2);
576  lua_settop(L, 2);
577  lua_pushinteger(L, 0);
578  lua_pushcclosure(L, gmatch_aux, 3);
579  return 1;
580}
581
582
583static int gfind_nodef (lua_State *L) {
584  return luaL_error(L, LUA_QL("string.gfind") " was renamed to "
585                       LUA_QL("string.gmatch"));
586}
587
588
589static void add_s (MatchState *ms, luaL_Buffer *b, const char *s,
590                                                   const char *e) {
591  size_t l, i;
592  const char *news = lua_tolstring(ms->L, 3, &l);
593  for (i = 0; i < l; i++) {
594    if (news[i] != L_ESC)
595      luaL_addchar(b, news[i]);
596    else {
597      i++;  /* skip ESC */
598      if (!isdigit(uchar(news[i])))
599        luaL_addchar(b, news[i]);
600      else if (news[i] == '0')
601          luaL_addlstring(b, s, e - s);
602      else {
603        push_onecapture(ms, news[i] - '1', s, e);
604        luaL_addvalue(b);  /* add capture to accumulated result */
605      }
606    }
607  }
608}
609
610
611static void add_value (MatchState *ms, luaL_Buffer *b, const char *s,
612                                                       const char *e) {
613  lua_State *L = ms->L;
614  switch (lua_type(L, 3)) {
615    case LUA_TNUMBER:
616    case LUA_TSTRING: {
617      add_s(ms, b, s, e);
618      return;
619    }
620    case LUA_TFUNCTION: {
621      int n;
622      lua_pushvalue(L, 3);
623      n = push_captures(ms, s, e);
624      lua_call(L, n, 1);
625      break;
626    }
627    case LUA_TTABLE: {
628      push_onecapture(ms, 0, s, e);
629      lua_gettable(L, 3);
630      break;
631    }
632  }
633  if (!lua_toboolean(L, -1)) {  /* nil or false? */
634    lua_pop(L, 1);
635    lua_pushlstring(L, s, e - s);  /* keep original text */
636  }
637  else if (!lua_isstring(L, -1))
638    luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); 
639  luaL_addvalue(b);  /* add result to accumulator */
640}
641
642
643static int str_gsub (lua_State *L) {
644  size_t srcl;
645  const char *src = luaL_checklstring(L, 1, &srcl);
646  const char *p = luaL_checkstring(L, 2);
647  int  tr = lua_type(L, 3);
648  int max_s = luaL_optint(L, 4, srcl+1);
649  int anchor = (*p == '^') ? (p++, 1) : 0;
650  int n = 0;
651  MatchState ms;
652  luaL_Buffer b;
653  luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
654                   tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,
655                      "string/function/table expected");
656  luaL_buffinit(L, &b);
657  ms.L = L;
658  ms.src_init = src;
659  ms.src_end = src+srcl;
660  while (n < max_s) {
661    const char *e;
662    ms.level = 0;
663    e = match(&ms, src, p);
664    if (e) {
665      n++;
666      add_value(&ms, &b, src, e);
667    }
668    if (e && e>src) /* non empty match? */
669      src = e;  /* skip it */
670    else if (src < ms.src_end)
671      luaL_addchar(&b, *src++);
672    else break;
673    if (anchor) break;
674  }
675  luaL_addlstring(&b, src, ms.src_end-src);
676  luaL_pushresult(&b);
677  lua_pushinteger(L, n);  /* number of substitutions */
678  return 2;
679}
680
681/* }====================================================== */
682
683
684/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
685#define MAX_ITEM        512
686/* valid flags in a format specification */
687#define FLAGS   "-+ #0"
688/*
689** maximum size of each format specification (such as '%-099.99d')
690** (+10 accounts for %99.99x plus margin of error)
691*/
692#define MAX_FORMAT      (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
693
694
695static void addquoted (lua_State *L, luaL_Buffer *b, int arg) {
696  size_t l;
697  const char *s = luaL_checklstring(L, arg, &l);
698  luaL_addchar(b, '"');
699  while (l--) {
700    switch (*s) {
701      case '"': case '\\': case '\n': {
702        luaL_addchar(b, '\\');
703        luaL_addchar(b, *s);
704        break;
705      }
706      case '\r': {
707        luaL_addlstring(b, "\\r", 2);
708        break;
709      }
710      case '\0': {
711        luaL_addlstring(b, "\\000", 4);
712        break;
713      }
714      default: {
715        luaL_addchar(b, *s);
716        break;
717      }
718    }
719    s++;
720  }
721  luaL_addchar(b, '"');
722}
723
724static const char *scanformat (lua_State *L, const char *strfrmt, char *form) {
725  const char *p = strfrmt;
726  while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++;  /* skip flags */
727  if ((size_t)(p - strfrmt) >= sizeof(FLAGS))
728    luaL_error(L, "invalid format (repeated flags)");
729  if (isdigit(uchar(*p))) p++;  /* skip width */
730  if (isdigit(uchar(*p))) p++;  /* (2 digits at most) */
731  if (*p == '.') {
732    p++;
733    if (isdigit(uchar(*p))) p++;  /* skip precision */
734    if (isdigit(uchar(*p))) p++;  /* (2 digits at most) */
735  }
736  if (isdigit(uchar(*p)))
737    luaL_error(L, "invalid format (width or precision too long)");
738  *(form++) = '%';
739  strncpy(form, strfrmt, p - strfrmt + 1);
740  form += p - strfrmt + 1;
741  *form = '\0';
742  return p;
743}
744
745
746static void addintlen (char *form) {
747  size_t l = strlen(form);
748  char spec = form[l - 1];
749  strcpy(form + l - 1, LUA_INTFRMLEN);
750  form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;
751  form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0';
752}
753
754
755static int str_format (lua_State *L) {
756  int arg = 1;
757  size_t sfl;
758  const char *strfrmt = luaL_checklstring(L, arg, &sfl);
759  const char *strfrmt_end = strfrmt+sfl;
760  luaL_Buffer b;
761  luaL_buffinit(L, &b);
762  while (strfrmt < strfrmt_end) {
763    if (*strfrmt != L_ESC)
764      luaL_addchar(&b, *strfrmt++);
765    else if (*++strfrmt == L_ESC)
766      luaL_addchar(&b, *strfrmt++);  /* %% */
767    else { /* format item */
768      char form[MAX_FORMAT];  /* to store the format (`%...') */
769      char buff[MAX_ITEM];  /* to store the formatted item */
770      arg++;
771      strfrmt = scanformat(L, strfrmt, form);
772      switch (*strfrmt++) {
773        case 'c': {
774          sprintf(buff, form, (int)luaL_checknumber(L, arg));
775          break;
776        }
777        case 'd':  case 'i': {
778          addintlen(form);
779          sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg));
780          break;
781        }
782        case 'o':  case 'u':  case 'x':  case 'X': {
783          addintlen(form);
784          sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg));
785          break;
786        }
787        case 'e':  case 'E': case 'f':
788        case 'g': case 'G': {
789          sprintf(buff, form, (double)luaL_checknumber(L, arg));
790          break;
791        }
792        case 'q': {
793          addquoted(L, &b, arg);
794          continue;  /* skip the 'addsize' at the end */
795        }
796        case 's': {
797          size_t l;
798          const char *s = luaL_checklstring(L, arg, &l);
799          if (!strchr(form, '.') && l >= 100) {
800            /* no precision and string is too long to be formatted;
801               keep original string */
802            lua_pushvalue(L, arg);
803            luaL_addvalue(&b);
804            continue;  /* skip the `addsize' at the end */
805          }
806          else {
807            sprintf(buff, form, s);
808            break;
809          }
810        }
811        default: {  /* also treat cases `pnLlh' */
812          return luaL_error(L, "invalid option " LUA_QL("%%%c") " to "
813                               LUA_QL("format"), *(strfrmt - 1));
814        }
815      }
816      luaL_addlstring(&b, buff, strlen(buff));
817    }
818  }
819  luaL_pushresult(&b);
820  return 1;
821}
822
823
824static const luaL_Reg strlib[] = {
825  {"byte", str_byte},
826  {"char", str_char},
827  {"dump", str_dump},
828  {"find", str_find},
829  {"format", str_format},
830  {"gfind", gfind_nodef},
831  {"gmatch", gmatch},
832  {"gsub", str_gsub},
833  {"len", str_len},
834  {"lower", str_lower},
835  {"match", str_match},
836  {"rep", str_rep},
837  {"reverse", str_reverse},
838  {"sub", str_sub},
839  {"upper", str_upper},
840  {NULL, NULL}
841};
842
843
844static void createmetatable (lua_State *L) {
845  lua_createtable(L, 0, 1);  /* create metatable for strings */
846  lua_pushliteral(L, "");  /* dummy string */
847  lua_pushvalue(L, -2);
848  lua_setmetatable(L, -2);  /* set string metatable */
849  lua_pop(L, 1);  /* pop dummy string */
850  lua_pushvalue(L, -2);  /* string library... */
851  lua_setfield(L, -2, "__index");  /* ...is the __index metamethod */
852  lua_pop(L, 1);  /* pop metatable */
853}
854
855
856/*
857** Open string library
858*/
859LUALIB_API int luaopen_string (lua_State *L) {
860  luaL_register(L, LUA_STRLIBNAME, strlib);
861#if defined(LUA_COMPAT_GFIND)
862  lua_getfield(L, -1, "gmatch");
863  lua_setfield(L, -2, "gfind");
864#endif
865  createmetatable(L);
866  return 1;
867}
868
Note: See TracBrowser for help on using the repository browser.