lstrlib.cpp

Go to the documentation of this file.
00001 /*
00002 ** $Id: lstrlib.c,v 1.132a 2006/04/26 20:41:19 roberto Exp $
00003 ** Standard library for string operations and pattern-matching
00004 ** See Copyright Notice in lua.h
00005 */
00006 
00007 
00008 #include <ctype.h>
00009 #include <stddef.h>
00010 #include <stdio.h>
00011 #include <stdlib.h>
00012 #include <string.h>
00013 
00014 #define lstrlib_c
00015 #define LUA_LIB
00016 
00017 #include "lua.h"
00018 
00019 #include "lauxlib.h"
00020 #include "lualib.h"
00021 
00022 
00023 /* macro to `unsign' a character */
00024 #define uchar(c)        ((unsigned char)(c))
00025 
00026 
00027 
00028 static int str_len (lua_State *L) {
00029   size_t l;
00030   luaL_checklstring(L, 1, &l);
00031   lua_pushinteger(L, l);
00032   return 1;
00033 }
00034 
00035 
00036 static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) {
00037   /* relative string position: negative means back from end */
00038   return (pos>=0) ? pos : (ptrdiff_t)len+pos+1;
00039 }
00040 
00041 
00042 static int str_sub (lua_State *L) {
00043   size_t l;
00044   const char *s = luaL_checklstring(L, 1, &l);
00045   ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l);
00046   ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l);
00047   if (start < 1) start = 1;
00048   if (end > (ptrdiff_t)l) end = (ptrdiff_t)l;
00049   if (start <= end)
00050     lua_pushlstring(L, s+start-1, end-start+1);
00051   else lua_pushliteral(L, "");
00052   return 1;
00053 }
00054 
00055 
00056 static int str_reverse (lua_State *L) {
00057   size_t l;
00058   luaL_Buffer b;
00059   const char *s = luaL_checklstring(L, 1, &l);
00060   luaL_buffinit(L, &b);
00061   while (l--) luaL_addchar(&b, s[l]);
00062   luaL_pushresult(&b);
00063   return 1;
00064 }
00065 
00066 
00067 static int str_lower (lua_State *L) {
00068   size_t l;
00069   size_t i;
00070   luaL_Buffer b;
00071   const char *s = luaL_checklstring(L, 1, &l);
00072   luaL_buffinit(L, &b);
00073   for (i=0; i<l; i++)
00074     luaL_addchar(&b, tolower(uchar(s[i])));
00075   luaL_pushresult(&b);
00076   return 1;
00077 }
00078 
00079 
00080 static int str_upper (lua_State *L) {
00081   size_t l;
00082   size_t i;
00083   luaL_Buffer b;
00084   const char *s = luaL_checklstring(L, 1, &l);
00085   luaL_buffinit(L, &b);
00086   for (i=0; i<l; i++)
00087     luaL_addchar(&b, toupper(uchar(s[i])));
00088   luaL_pushresult(&b);
00089   return 1;
00090 }
00091 
00092 static int str_rep (lua_State *L) {
00093   size_t l;
00094   luaL_Buffer b;
00095   const char *s = luaL_checklstring(L, 1, &l);
00096   int n = luaL_checkint(L, 2);
00097   luaL_buffinit(L, &b);
00098   while (n-- > 0)
00099     luaL_addlstring(&b, s, l);
00100   luaL_pushresult(&b);
00101   return 1;
00102 }
00103 
00104 
00105 static int str_byte (lua_State *L) {
00106   size_t l;
00107   const char *s = luaL_checklstring(L, 1, &l);
00108   ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l);
00109   ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l);
00110   int n, i;
00111   if (posi <= 0) posi = 1;
00112   if ((size_t)pose > l) pose = l;
00113   if (posi > pose) return 0;  /* empty interval; return no values */
00114   n = (int)(pose -  posi + 1);
00115   if (posi + n <= pose)  /* overflow? */
00116     luaL_error(L, "string slice too long");
00117   luaL_checkstack(L, n, "string slice too long");
00118   for (i=0; i<n; i++)
00119     lua_pushinteger(L, uchar(s[posi+i-1]));
00120   return n;
00121 }
00122 
00123 
00124 static int str_char (lua_State *L) {
00125   int n = lua_gettop(L);  /* number of arguments */
00126   int i;
00127   luaL_Buffer b;
00128   luaL_buffinit(L, &b);
00129   for (i=1; i<=n; i++) {
00130     int c = luaL_checkint(L, i);
00131     luaL_argcheck(L, uchar(c) == c, i, "invalid value");
00132     luaL_addchar(&b, uchar(c));
00133   }
00134   luaL_pushresult(&b);
00135   return 1;
00136 }
00137 
00138 
00139 static int writer (lua_State *L, const void* b, size_t size, void* B) {
00140   (void)L;
00141   luaL_addlstring((luaL_Buffer*) B, (const char *)b, size);
00142   return 0;
00143 }
00144 
00145 
00146 static int str_dump (lua_State *L) {
00147   luaL_Buffer b;
00148   luaL_checktype(L, 1, LUA_TFUNCTION);
00149   lua_settop(L, 1);
00150   luaL_buffinit(L,&b);
00151   if (lua_dump(L, writer, &b) != 0)
00152     luaL_error(L, "unable to dump given function");
00153   luaL_pushresult(&b);
00154   return 1;
00155 }
00156 
00157 
00158 
00159 /*
00160 ** {======================================================
00161 ** PATTERN MATCHING
00162 ** =======================================================
00163 */
00164 
00165 
00166 #define CAP_UNFINISHED  (-1)
00167 #define CAP_POSITION    (-2)
00168 
00169 typedef struct MatchState {
00170   const char *src_init;  /* init of source string */
00171   const char *src_end;  /* end (`\0') of source string */
00172   lua_State *L;
00173   int level;  /* total number of captures (finished or unfinished) */
00174   struct {
00175     const char *init;
00176     ptrdiff_t len;
00177   } capture[LUA_MAXCAPTURES];
00178 } MatchState;
00179 
00180 
00181 #define L_ESC           '%'
00182 #define SPECIALS        "^$*+?.([%-"
00183 
00184 
00185 static int check_capture (MatchState *ms, int l) {
00186   l -= '1';
00187   if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
00188     return luaL_error(ms->L, "invalid capture index");
00189   return l;
00190 }
00191 
00192 
00193 static int capture_to_close (MatchState *ms) {
00194   int level = ms->level;
00195   for (level--; level>=0; level--)
00196     if (ms->capture[level].len == CAP_UNFINISHED) return level;
00197   return luaL_error(ms->L, "invalid pattern capture");
00198 }
00199 
00200 
00201 static const char *classend (MatchState *ms, const char *p) {
00202   switch (*p++) {
00203     case L_ESC: {
00204       if (*p == '\0')
00205         luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")");
00206       return p+1;
00207     }
00208     case '[': {
00209       if (*p == '^') p++;
00210       do {  /* look for a `]' */
00211         if (*p == '\0')
00212           luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")");
00213         if (*(p++) == L_ESC && *p != '\0')
00214           p++;  /* skip escapes (e.g. `%]') */
00215       } while (*p != ']');
00216       return p+1;
00217     }
00218     default: {
00219       return p;
00220     }
00221   }
00222 }
00223 
00224 
00225 static int match_class (int c, int cl) {
00226   int res;
00227   switch (tolower(cl)) {
00228     case 'a' : res = isalpha(c); break;
00229     case 'c' : res = iscntrl(c); break;
00230     case 'd' : res = isdigit(c); break;
00231     case 'l' : res = islower(c); break;
00232     case 'p' : res = ispunct(c); break;
00233     case 's' : res = isspace(c); break;
00234     case 'u' : res = isupper(c); break;
00235     case 'w' : res = isalnum(c); break;
00236     case 'x' : res = isxdigit(c); break;
00237     case 'z' : res = (c == 0); break;
00238     default: return (cl == c);
00239   }
00240   return (islower(cl) ? res : !res);
00241 }
00242 
00243 
00244 static int matchbracketclass (int c, const char *p, const char *ec) {
00245   int sig = 1;
00246   if (*(p+1) == '^') {
00247     sig = 0;
00248     p++;  /* skip the `^' */
00249   }
00250   while (++p < ec) {
00251     if (*p == L_ESC) {
00252       p++;
00253       if (match_class(c, uchar(*p)))
00254         return sig;
00255     }
00256     else if ((*(p+1) == '-') && (p+2 < ec)) {
00257       p+=2;
00258       if (uchar(*(p-2)) <= c && c <= uchar(*p))
00259         return sig;
00260     }
00261     else if (uchar(*p) == c) return sig;
00262   }
00263   return !sig;
00264 }
00265 
00266 
00267 static int singlematch (int c, const char *p, const char *ep) {
00268   switch (*p) {
00269     case '.': return 1;  /* matches any char */
00270     case L_ESC: return match_class(c, uchar(*(p+1)));
00271     case '[': return matchbracketclass(c, p, ep-1);
00272     default:  return (uchar(*p) == c);
00273   }
00274 }
00275 
00276 
00277 static const char *match (MatchState *ms, const char *s, const char *p);
00278 
00279 
00280 static const char *matchbalance (MatchState *ms, const char *s,
00281                                    const char *p) {
00282   if (*p == 0 || *(p+1) == 0)
00283     luaL_error(ms->L, "unbalanced pattern");
00284   if (*s != *p) return NULL;
00285   else {
00286     int b = *p;
00287     int e = *(p+1);
00288     int cont = 1;
00289     while (++s < ms->src_end) {
00290       if (*s == e) {
00291         if (--cont == 0) return s+1;
00292       }
00293       else if (*s == b) cont++;
00294     }
00295   }
00296   return NULL;  /* string ends out of balance */
00297 }
00298 
00299 
00300 static const char *max_expand (MatchState *ms, const char *s,
00301                                  const char *p, const char *ep) {
00302   ptrdiff_t i = 0;  /* counts maximum expand for item */
00303   while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))
00304     i++;
00305   /* keeps trying to match with the maximum repetitions */
00306   while (i>=0) {
00307     const char *res = match(ms, (s+i), ep+1);
00308     if (res) return res;
00309     i--;  /* else didn't match; reduce 1 repetition to try again */
00310   }
00311   return NULL;
00312 }
00313 
00314 
00315 static const char *min_expand (MatchState *ms, const char *s,
00316                                  const char *p, const char *ep) {
00317   for (;;) {
00318     const char *res = match(ms, s, ep+1);
00319     if (res != NULL)
00320       return res;
00321     else if (s<ms->src_end && singlematch(uchar(*s), p, ep))
00322       s++;  /* try with one more repetition */
00323     else return NULL;
00324   }
00325 }
00326 
00327 
00328 static const char *start_capture (MatchState *ms, const char *s,
00329                                     const char *p, int what) {
00330   const char *res;
00331   int level = ms->level;
00332   if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures");
00333   ms->capture[level].init = s;
00334   ms->capture[level].len = what;
00335   ms->level = level+1;
00336   if ((res=match(ms, s, p)) == NULL)  /* match failed? */
00337     ms->level--;  /* undo capture */
00338   return res;
00339 }
00340 
00341 
00342 static const char *end_capture (MatchState *ms, const char *s,
00343                                   const char *p) {
00344   int l = capture_to_close(ms);
00345   const char *res;
00346   ms->capture[l].len = s - ms->capture[l].init;  /* close capture */
00347   if ((res = match(ms, s, p)) == NULL)  /* match failed? */
00348     ms->capture[l].len = CAP_UNFINISHED;  /* undo capture */
00349   return res;
00350 }
00351 
00352 
00353 static const char *match_capture (MatchState *ms, const char *s, int l) {
00354   size_t len;
00355   l = check_capture(ms, l);
00356   len = ms->capture[l].len;
00357   if ((size_t)(ms->src_end-s) >= len &&
00358       memcmp(ms->capture[l].init, s, len) == 0)
00359     return s+len;
00360   else return NULL;
00361 }
00362 
00363 
00364 static const char *match (MatchState *ms, const char *s, const char *p) {
00365   init: /* using goto's to optimize tail recursion */
00366   switch (*p) {
00367     case '(': {  /* start capture */
00368       if (*(p+1) == ')')  /* position capture? */
00369         return start_capture(ms, s, p+2, CAP_POSITION);
00370       else
00371         return start_capture(ms, s, p+1, CAP_UNFINISHED);
00372     }
00373     case ')': {  /* end capture */
00374       return end_capture(ms, s, p+1);
00375     }
00376     case L_ESC: {
00377       switch (*(p+1)) {
00378         case 'b': {  /* balanced string? */
00379           s = matchbalance(ms, s, p+2);
00380           if (s == NULL) return NULL;
00381           p+=4; goto init;  /* else return match(ms, s, p+4); */
00382         }
00383         case 'f': {  /* frontier? */
00384           const char *ep; char previous;
00385           p += 2;
00386           if (*p != '[')
00387             luaL_error(ms->L, "missing " LUA_QL("[") " after "
00388                                LUA_QL("%%f") " in pattern");
00389           ep = classend(ms, p);  /* points to what is next */
00390           previous = (s == ms->src_init) ? '\0' : *(s-1);
00391           if (matchbracketclass(uchar(previous), p, ep-1) ||
00392              !matchbracketclass(uchar(*s), p, ep-1)) return NULL;
00393           p=ep; goto init;  /* else return match(ms, s, ep); */
00394         }
00395         default: {
00396           if (isdigit(uchar(*(p+1)))) {  /* capture results (%0-%9)? */
00397             s = match_capture(ms, s, uchar(*(p+1)));
00398             if (s == NULL) return NULL;
00399             p+=2; goto init;  /* else return match(ms, s, p+2) */
00400           }
00401           goto dflt;  /* case default */
00402         }
00403       }
00404     }
00405     case '\0': {  /* end of pattern */
00406       return s;  /* match succeeded */
00407     }
00408     case '$': {
00409       if (*(p+1) == '\0')  /* is the `$' the last char in pattern? */
00410         return (s == ms->src_end) ? s : NULL;  /* check end of string */
00411       else goto dflt;
00412     }
00413     default: dflt: {  /* it is a pattern item */
00414       const char *ep = classend(ms, p);  /* points to what is next */
00415       int m = s<ms->src_end && singlematch(uchar(*s), p, ep);
00416       switch (*ep) {
00417         case '?': {  /* optional */
00418           const char *res;
00419           if (m && ((res=match(ms, s+1, ep+1)) != NULL))
00420             return res;
00421           p=ep+1; goto init;  /* else return match(ms, s, ep+1); */
00422         }
00423         case '*': {  /* 0 or more repetitions */
00424           return max_expand(ms, s, p, ep);
00425         }
00426         case '+': {  /* 1 or more repetitions */
00427           return (m ? max_expand(ms, s+1, p, ep) : NULL);
00428         }
00429         case '-': {  /* 0 or more repetitions (minimum) */
00430           return min_expand(ms, s, p, ep);
00431         }
00432         default: {
00433           if (!m) return NULL;
00434           s++; p=ep; goto init;  /* else return match(ms, s+1, ep); */
00435         }
00436       }
00437     }
00438   }
00439 }
00440 
00441 
00442 
00443 static const char *lmemfind (const char *s1, size_t l1,
00444                                const char *s2, size_t l2) {
00445   if (l2 == 0) return s1;  /* empty strings are everywhere */
00446   else if (l2 > l1) return NULL;  /* avoids a negative `l1' */
00447   else {
00448     const char *init;  /* to search for a `*s2' inside `s1' */
00449     l2--;  /* 1st char will be checked by `memchr' */
00450     l1 = l1-l2;  /* `s2' cannot be found after that */
00451     while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
00452       init++;   /* 1st char is already checked */
00453       if (memcmp(init, s2+1, l2) == 0)
00454         return init-1;
00455       else {  /* correct `l1' and `s1' to try again */
00456         l1 -= init-s1;
00457         s1 = init;
00458       }
00459     }
00460     return NULL;  /* not found */
00461   }
00462 }
00463 
00464 
00465 static void push_onecapture (MatchState *ms, int i, const char *s,
00466                                                     const char *e) {
00467   if (i >= ms->level) {
00468     if (i == 0)  /* ms->level == 0, too */
00469       lua_pushlstring(ms->L, s, e - s);  /* add whole match */
00470     else
00471       luaL_error(ms->L, "invalid capture index");
00472   }
00473   else {
00474     ptrdiff_t l = ms->capture[i].len;
00475     if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture");
00476     if (l == CAP_POSITION)
00477       lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
00478     else
00479       lua_pushlstring(ms->L, ms->capture[i].init, l);
00480   }
00481 }
00482 
00483 
00484 static int push_captures (MatchState *ms, const char *s, const char *e) {
00485   int i;
00486   int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
00487   luaL_checkstack(ms->L, nlevels, "too many captures");
00488   for (i = 0; i < nlevels; i++)
00489     push_onecapture(ms, i, s, e);
00490   return nlevels;  /* number of strings pushed */
00491 }
00492 
00493 
00494 static int str_find_aux (lua_State *L, int find) {
00495   size_t l1, l2;
00496   const char *s = luaL_checklstring(L, 1, &l1);
00497   const char *p = luaL_checklstring(L, 2, &l2);
00498   ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1;
00499   if (init < 0) init = 0;
00500   else if ((size_t)(init) > l1) init = (ptrdiff_t)l1;
00501   if (find && (lua_toboolean(L, 4) ||  /* explicit request? */
00502       strpbrk(p, SPECIALS) == NULL)) {  /* or no special characters? */
00503     /* do a plain search */
00504     const char *s2 = lmemfind(s+init, l1-init, p, l2);
00505     if (s2) {
00506       lua_pushinteger(L, s2-s+1);
00507       lua_pushinteger(L, s2-s+l2);
00508       return 2;
00509     }
00510   }
00511   else {
00512     MatchState ms;
00513     int anchor = (*p == '^') ? (p++, 1) : 0;
00514     const char *s1=s+init;
00515     ms.L = L;
00516     ms.src_init = s;
00517     ms.src_end = s+l1;
00518     do {
00519       const char *res;
00520       ms.level = 0;
00521       if ((res=match(&ms, s1, p)) != NULL) {
00522         if (find) {
00523           lua_pushinteger(L, s1-s+1);  /* start */
00524           lua_pushinteger(L, res-s);   /* end */
00525           return push_captures(&ms, NULL, 0) + 2;
00526         }
00527         else
00528           return push_captures(&ms, s1, res);
00529       }
00530     } while (s1++ < ms.src_end && !anchor);
00531   }
00532   lua_pushnil(L);  /* not found */
00533   return 1;
00534 }
00535 
00536 
00537 static int str_find (lua_State *L) {
00538   return str_find_aux(L, 1);
00539 }
00540 
00541 
00542 static int str_match (lua_State *L) {
00543   return str_find_aux(L, 0);
00544 }
00545 
00546 
00547 static int gmatch_aux (lua_State *L) {
00548   MatchState ms;
00549   size_t ls;
00550   const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
00551   const char *p = lua_tostring(L, lua_upvalueindex(2));
00552   const char *src;
00553   ms.L = L;
00554   ms.src_init = s;
00555   ms.src_end = s+ls;
00556   for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3));
00557        src <= ms.src_end;
00558        src++) {
00559     const char *e;
00560     ms.level = 0;
00561     if ((e = match(&ms, src, p)) != NULL) {
00562       lua_Integer newstart = e-s;
00563       if (e == src) newstart++;  /* empty match? go at least one position */
00564       lua_pushinteger(L, newstart);
00565       lua_replace(L, lua_upvalueindex(3));
00566       return push_captures(&ms, src, e);
00567     }
00568   }
00569   return 0;  /* not found */
00570 }
00571 
00572 
00573 static int gmatch (lua_State *L) {
00574   luaL_checkstring(L, 1);
00575   luaL_checkstring(L, 2);
00576   lua_settop(L, 2);
00577   lua_pushinteger(L, 0);
00578   lua_pushcclosure(L, gmatch_aux, 3);
00579   return 1;
00580 }
00581 
00582 
00583 static int gfind_nodef (lua_State *L) {
00584   return luaL_error(L, LUA_QL("string.gfind") " was renamed to "
00585                        LUA_QL("string.gmatch"));
00586 }
00587 
00588 
00589 static void add_s (MatchState *ms, luaL_Buffer *b, const char *s,
00590                                                    const char *e) {
00591   size_t l, i;
00592   const char *news = lua_tolstring(ms->L, 3, &l);
00593   for (i = 0; i < l; i++) {
00594     if (news[i] != L_ESC)
00595       luaL_addchar(b, news[i]);
00596     else {
00597       i++;  /* skip ESC */
00598       if (!isdigit(uchar(news[i])))
00599         luaL_addchar(b, news[i]);
00600       else if (news[i] == '0')
00601           luaL_addlstring(b, s, e - s);
00602       else {
00603         push_onecapture(ms, news[i] - '1', s, e);
00604         luaL_addvalue(b);  /* add capture to accumulated result */
00605       }
00606     }
00607   }
00608 }
00609 
00610 
00611 static void add_value (MatchState *ms, luaL_Buffer *b, const char *s,
00612                                                        const char *e) {
00613   lua_State *L = ms->L;
00614   switch (lua_type(L, 3)) {
00615     case LUA_TNUMBER:
00616     case LUA_TSTRING: {
00617       add_s(ms, b, s, e);
00618       return;
00619     }
00620     case LUA_TFUNCTION: {
00621       int n;
00622       lua_pushvalue(L, 3);
00623       n = push_captures(ms, s, e);
00624       lua_call(L, n, 1);
00625       break;
00626     }
00627     case LUA_TTABLE: {
00628       push_onecapture(ms, 0, s, e);
00629       lua_gettable(L, 3);
00630       break;
00631     }
00632     default: {
00633       luaL_argerror(L, 3, "string/function/table expected"); 
00634       return;
00635     }
00636   }
00637   if (!lua_toboolean(L, -1)) {  /* nil or false? */
00638     lua_pop(L, 1);
00639     lua_pushlstring(L, s, e - s);  /* keep original text */
00640   }
00641   else if (!lua_isstring(L, -1))
00642     luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); 
00643   luaL_addvalue(b);  /* add result to accumulator */
00644 }
00645 
00646 
00647 static int str_gsub (lua_State *L) {
00648   size_t srcl;
00649   const char *src = luaL_checklstring(L, 1, &srcl);
00650   const char *p = luaL_checkstring(L, 2);
00651   int max_s = luaL_optint(L, 4, srcl+1);
00652   int anchor = (*p == '^') ? (p++, 1) : 0;
00653   int n = 0;
00654   MatchState ms;
00655   luaL_Buffer b;
00656   luaL_buffinit(L, &b);
00657   ms.L = L;
00658   ms.src_init = src;
00659   ms.src_end = src+srcl;
00660   while (n < max_s) {
00661     const char *e;
00662     ms.level = 0;
00663     e = match(&ms, src, p);
00664     if (e) {
00665       n++;
00666       add_value(&ms, &b, src, e);
00667     }
00668     if (e && e>src) /* non empty match? */
00669       src = e;  /* skip it */
00670     else if (src < ms.src_end)
00671       luaL_addchar(&b, *src++);
00672     else break;
00673     if (anchor) break;
00674   }
00675   luaL_addlstring(&b, src, ms.src_end-src);
00676   luaL_pushresult(&b);
00677   lua_pushinteger(L, n);  /* number of substitutions */
00678   return 2;
00679 }
00680 
00681 /* }====================================================== */
00682 
00683 
00684 /* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
00685 #define MAX_ITEM        512
00686 /* valid flags in a format specification */
00687 #define FLAGS   "-+ #0"
00688 /*
00689 ** maximum size of each format specification (such as '%-099.99d')
00690 ** (+10 accounts for %99.99x plus margin of error)
00691 */
00692 #define MAX_FORMAT      (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
00693 
00694 
00695 static void addquoted (lua_State *L, luaL_Buffer *b, int arg) {
00696   size_t l;
00697   const char *s = luaL_checklstring(L, arg, &l);
00698   luaL_addchar(b, '"');
00699   while (l--) {
00700     switch (*s) {
00701       case '"': case '\\': case '\n': {
00702         luaL_addchar(b, '\\');
00703         luaL_addchar(b, *s);
00704         break;
00705       }
00706       case '\r': {
00707         luaL_addlstring(b, "\\r", 2);
00708         break;
00709       }
00710       case '\0': {
00711         luaL_addlstring(b, "\\000", 4);
00712         break;
00713       }
00714       default: {
00715         luaL_addchar(b, *s);
00716         break;
00717       }
00718     }
00719     s++;
00720   }
00721   luaL_addchar(b, '"');
00722 }
00723 
00724 static const char *scanformat (lua_State *L, const char *strfrmt, char *form) {
00725   const char *p = strfrmt;
00726   while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++;  /* skip flags */
00727   if ((size_t)(p - strfrmt) >= sizeof(FLAGS))
00728     luaL_error(L, "invalid format (repeated flags)");
00729   if (isdigit(uchar(*p))) p++;  /* skip width */
00730   if (isdigit(uchar(*p))) p++;  /* (2 digits at most) */
00731   if (*p == '.') {
00732     p++;
00733     if (isdigit(uchar(*p))) p++;  /* skip precision */
00734     if (isdigit(uchar(*p))) p++;  /* (2 digits at most) */
00735   }
00736   if (isdigit(uchar(*p)))
00737     luaL_error(L, "invalid format (width or precision too long)");
00738   *(form++) = '%';
00739   strncpy(form, strfrmt, p - strfrmt + 1);
00740   form += p - strfrmt + 1;
00741   *form = '\0';
00742   return p;
00743 }
00744 
00745 
00746 static void addintlen (char *form) {
00747   size_t l = strlen(form);
00748   char spec = form[l - 1];
00749   strcpy(form + l - 1, LUA_INTFRMLEN);
00750   form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;
00751   form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0';
00752 }
00753 
00754 
00755 static int str_format (lua_State *L) {
00756   int arg = 1;
00757   size_t sfl;
00758   const char *strfrmt = luaL_checklstring(L, arg, &sfl);
00759   const char *strfrmt_end = strfrmt+sfl;
00760   luaL_Buffer b;
00761   luaL_buffinit(L, &b);
00762   while (strfrmt < strfrmt_end) {
00763     if (*strfrmt != L_ESC)
00764       luaL_addchar(&b, *strfrmt++);
00765     else if (*++strfrmt == L_ESC)
00766       luaL_addchar(&b, *strfrmt++);  /* %% */
00767     else { /* format item */
00768       char form[MAX_FORMAT];  /* to store the format (`%...') */
00769       char buff[MAX_ITEM];  /* to store the formatted item */
00770       arg++;
00771       strfrmt = scanformat(L, strfrmt, form);
00772       switch (*strfrmt++) {
00773         case 'c': {
00774           sprintf(buff, form, (int)luaL_checknumber(L, arg));
00775           break;
00776         }
00777         case 'd':  case 'i': {
00778           addintlen(form);
00779           sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg));
00780           break;
00781         }
00782         case 'o':  case 'u':  case 'x':  case 'X': {
00783           addintlen(form);
00784           sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg));
00785           break;
00786         }
00787         case 'e':  case 'E': case 'f':
00788         case 'g': case 'G': {
00789           sprintf(buff, form, (double)luaL_checknumber(L, arg));
00790           break;
00791         }
00792         case 'q': {
00793           addquoted(L, &b, arg);
00794           continue;  /* skip the 'addsize' at the end */
00795         }
00796         case 's': {
00797           size_t l;
00798           const char *s = luaL_checklstring(L, arg, &l);
00799           if (!strchr(form, '.') && l >= 100) {
00800             /* no precision and string is too long to be formatted;
00801                keep original string */
00802             lua_pushvalue(L, arg);
00803             luaL_addvalue(&b);
00804             continue;  /* skip the `addsize' at the end */
00805           }
00806           else {
00807             sprintf(buff, form, s);
00808             break;
00809           }
00810         }
00811         default: {  /* also treat cases `pnLlh' */
00812           return luaL_error(L, "invalid option " LUA_QL("%%%c") " to "
00813                                LUA_QL("format"), *(strfrmt - 1));
00814         }
00815       }
00816       luaL_addlstring(&b, buff, strlen(buff));
00817     }
00818   }
00819   luaL_pushresult(&b);
00820   return 1;
00821 }
00822 
00823 
00824 static const luaL_Reg strlib[] = {
00825   {"byte", str_byte},
00826   {"char", str_char},
00827   {"dump", str_dump},
00828   {"find", str_find},
00829   {"format", str_format},
00830   {"gfind", gfind_nodef},
00831   {"gmatch", gmatch},
00832   {"gsub", str_gsub},
00833   {"len", str_len},
00834   {"lower", str_lower},
00835   {"match", str_match},
00836   {"rep", str_rep},
00837   {"reverse", str_reverse},
00838   {"sub", str_sub},
00839   {"upper", str_upper},
00840   {NULL, NULL}
00841 };
00842 
00843 
00844 static void createmetatable (lua_State *L) {
00845   lua_createtable(L, 0, 1);  /* create metatable for strings */
00846   lua_pushliteral(L, "");  /* dummy string */
00847   lua_pushvalue(L, -2);
00848   lua_setmetatable(L, -2);  /* set string metatable */
00849   lua_pop(L, 1);  /* pop dummy string */
00850   lua_pushvalue(L, -2);  /* string library... */
00851   lua_setfield(L, -2, "__index");  /* ...is the __index metamethod */
00852   lua_pop(L, 1);  /* pop metatable */
00853 }
00854 
00855 
00856 /*
00857 ** Open string library
00858 */
00859 LUALIB_API int luaopen_string (lua_State *L) {
00860   luaL_register(L, LUA_STRLIBNAME, strlib);
00861 #if defined(LUA_COMPAT_GFIND)
00862   lua_getfield(L, -1, "gmatch");
00863   lua_setfield(L, -2, "gfind");
00864 #endif
00865   createmetatable(L);
00866   return 1;
00867 }
00868 

Generated on Mon Feb 16 15:14:51 2009 for Scorched3D by  doxygen 1.5.3