# sqstdstring.cpp

    00001 /* see copyright notice in squirrel.h */
    00002 #include <squirrel.h>
    00003 #include <sqstdstring.h>
    00004 #include <string.h>
    00005 #include <stdlib.h>
    00006 #include <stdio.h>
    00007 #include <ctype.h>
    00008 #include <assert.h>
    00009 #include <stdarg.h>
    00010
    00011 #ifdef SQUNICODE
    00012 #define scstrchr wcschr
    00013 #define scatoi _wtoi
    00014 #define scstrtok wcstok
    00015 #else
    00016 #define scstrchr strchr
    00017 #define scatoi atoi
    00018 #define scstrtok strtok
    00019 #endif
    00020 #define MAX_FORMAT_LEN  20
    00021 #define MAX_WFORMAT_LEN 3
    00022 #define ADDITIONAL_FORMAT_SPACE (100*sizeof(SQChar))
    00023
    00024 static SQInteger validate_format(HSQUIRRELVM v, SQChar *fmt, const SQChar *src, SQInteger n,SQInteger &width)
    00025 {
    00026   SQChar swidth[MAX_WFORMAT_LEN];
    00027   SQInteger wc = 0;
    00028   SQInteger start = n;
    00029   fmt[0] = '%';
    00030   while (scstrchr(_SC("-+ #0"), src[n])) n++;
    00031   while (scisdigit(src[n])) {
    00032     swidth[wc] = src[n];
    00033     n++;
    00034     wc++;
    00035     if(wc>=MAX_WFORMAT_LEN)
    00036       return sq_throwerror(v,_SC("width format too long"));
    00037   }
    00038   swidth[wc] = '\0';
    00039   if(wc > 0) {
    00040     width = scatoi(swidth);
    00041   }
    00042   else
    00043     width = 0;
    00044   if (src[n] == '.') {
    00045       n++;
    00046
    00047     wc = 0;
    00048     while (scisdigit(src[n])) {
    00049       swidth[wc] = src[n];
    00050       n++;
    00051       wc++;
    00052       if(wc>=MAX_WFORMAT_LEN)
    00053         return sq_throwerror(v,_SC("precision format too long"));
    00054     }
    00055     swidth[wc] = '\0';
    00056     if(wc > 0) {
    00057       width += scatoi(swidth);
    00058     }
    00059   }
    00060   if (n-start > MAX_FORMAT_LEN )
    00061     return sq_throwerror(v,_SC("format too long"));
    00062   memcpy(&fmt[1],&src[start],((n-start)+1)*sizeof(SQChar));
    00063   fmt[(n-start)+2] = '\0';
    00064   return n;
    00065 }
    00066
    00067 /*
    00068  * Little hack to remove the "format not a string literal, argument types not checked" warning.
    00069  * This check has been added to OpenTTD to make sure that nobody passes wrong string literals,
    00070  * but three lines in Squirrel have a little problem with those. Therefor we use this hack
    00071  * which basically uses vsnprintf instead of sprintf as vsnprintf is not testing for the right
    00072  * string literal at compile time.
    00073  */
    00074 static void _append_string(SQInteger &i, SQChar *dest, SQInteger allocated, const SQChar *fmt, ...)
    00075 {
    00076   va_list va;
    00077   va_start(va, fmt);
    00078   i += scvsnprintf(&dest[i],allocated-i,fmt,va);
    00079   va_end(va);
    00080 }
    00081
    00082 static SQInteger _string_format(HSQUIRRELVM v)
    00083 {
    00084   const SQChar *format;
    00085   SQChar *dest;
    00086   SQChar fmt[MAX_FORMAT_LEN];
    00087   sq_getstring(v,2,&format);
    00088   SQInteger allocated = (sq_getsize(v,2)+1)*sizeof(SQChar);
    00089   dest = sq_getscratchpad(v,allocated);
    00090   SQInteger n = 0,i = 0, nparam = 3, w = 0;
    00091   while(format[n] != '\0') {
    00092     if(format[n] != '%') {
    00093       assert(i < allocated);
    00094       dest[i++] = format[n];
    00095       n++;
    00096     }
    00097     else if(format[n+1] == '%') { //handles %%
    00098         dest[i++] = '%';
    00099         n += 2;
    00100     }
    00101     else {
    00102       n++;
    00103       if( nparam > sq_gettop(v) )
    00104         return sq_throwerror(v,_SC("not enough paramters for the given format string"));
    00105       n = validate_format(v,fmt,format,n,w);
    00106       if(n < 0) return -1;
    00107       SQInteger addlen = 0;
    00108       SQInteger valtype = 0;
    00109       const SQChar *ts;
    00110       SQInteger ti;
    00111       SQFloat tf;
    00112       switch(format[n]) {
    00113       case 's':
    00114         if(SQ_FAILED(sq_getstring(v,nparam,&ts)))
    00115           return sq_throwerror(v,_SC("string expected for the specified format"));
    00116         addlen = (sq_getsize(v,nparam)*sizeof(SQChar))+((w+1)*sizeof(SQChar));
    00117         valtype = 's';
    00118         break;
    00119       case 'i': case 'd': case 'c':case 'o':  case 'u':  case 'x':  case 'X':
    00120         if(SQ_FAILED(sq_getinteger(v,nparam,&ti)))
    00121           return sq_throwerror(v,_SC("integer expected for the specified format"));
    00122         addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
    00123         valtype = 'i';
    00124         break;
    00125       case 'f': case 'g': case 'G': case 'e':  case 'E':
    00126         if(SQ_FAILED(sq_getfloat(v,nparam,&tf)))
    00127           return sq_throwerror(v,_SC("float expected for the specified format"));
    00128         addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
    00129         valtype = 'f';
    00130         break;
    00131       default:
    00132         return sq_throwerror(v,_SC("invalid format"));
    00133       }
    00134       n++;
    00135       allocated += addlen;
    00136       dest = sq_getscratchpad(v,allocated);
    00137       switch(valtype) {
    00138       case 's': _append_string(i,dest,allocated,fmt,ts); break;
    00139       case 'i': _append_string(i,dest,allocated,fmt,ti); break;
    00140       case 'f': _append_string(i,dest,allocated,fmt,tf); break;
    00141       };
    00142       nparam ++;
    00143     }
    00144   }
    00145   sq_pushstring(v,dest,i);
    00146   return 1;
    00147 }
    00148
    00149 static void __strip_l(const SQChar *str,const SQChar **start)
    00150 {
    00151   const SQChar *t = str;
    00152   while(((*t) != '\0') && scisspace(*t)){ t++; }
    00153   *start = t;
    00154 }
    00155
    00156 static void __strip_r(const SQChar *str,SQInteger len,const SQChar **end)
    00157 {
    00158   if(len == 0) {
    00159     *end = str;
    00160     return;
    00161   }
    00162   const SQChar *t = &str[len-1];
    00163   while(t != str && scisspace(*t)) { t--; }
    00164   *end = t+1;
    00165 }
    00166
    00167 static SQInteger _string_strip(HSQUIRRELVM v)
    00168 {
    00169   const SQChar *str,*start,*end;
    00170   sq_getstring(v,2,&str);
    00171   SQInteger len = sq_getsize(v,2);
    00172   __strip_l(str,&start);
    00173   __strip_r(str,len,&end);
    00174   sq_pushstring(v,start,end - start);
    00175   return 1;
    00176 }
    00177
    00178 static SQInteger _string_lstrip(HSQUIRRELVM v)
    00179 {
    00180   const SQChar *str,*start;
    00181   sq_getstring(v,2,&str);
    00182   __strip_l(str,&start);
    00183   sq_pushstring(v,start,-1);
    00184   return 1;
    00185 }
    00186
    00187 static SQInteger _string_rstrip(HSQUIRRELVM v)
    00188 {
    00189   const SQChar *str,*end;
    00190   sq_getstring(v,2,&str);
    00191   SQInteger len = sq_getsize(v,2);
    00192   __strip_r(str,len,&end);
    00193   sq_pushstring(v,str,end - str);
    00194   return 1;
    00195 }
    00196
    00197 static SQInteger _string_split(HSQUIRRELVM v)
    00198 {
    00199   const SQChar *str,*seps;
    00200   SQChar *stemp,*tok;
    00201   sq_getstring(v,2,&str);
    00202   sq_getstring(v,3,&seps);
    00203   if(sq_getsize(v,3) == 0) return sq_throwerror(v,_SC("empty separators string"));
    00204   SQInteger memsize = (sq_getsize(v,2)+1)*sizeof(SQChar);
    00205   stemp = sq_getscratchpad(v,memsize);
    00206   memcpy(stemp,str,memsize);
    00207   tok = scstrtok(stemp,seps);
    00208   sq_newarray(v,0);
    00209   while( tok != NULL ) {
    00210     sq_pushstring(v,tok,-1);
    00211     sq_arrayappend(v,-2);
    00212     tok = scstrtok( NULL, seps );
    00213   }
    00214   return 1;
    00215 }
    00216
    00217 #define SETUP_REX(v) \
    00218   SQRex *self = NULL; \
    00219   sq_getinstanceup(v,1,(SQUserPointer *)&self,0);
    00220
    00221 static SQInteger _rexobj_releasehook(SQUserPointer p, SQInteger size)
    00222 {
    00223   SQRex *self = ((SQRex *)p);
    00224   sqstd_rex_free(self);
    00225   return 1;
    00226 }
    00227
    00228 static SQInteger _regexp_match(HSQUIRRELVM v)
    00229 {
    00230   SETUP_REX(v);
    00231   const SQChar *str;
    00232   sq_getstring(v,2,&str);
    00233   if(sqstd_rex_match(self,str) == SQTrue)
    00234   {
    00235     sq_pushbool(v,SQTrue);
    00236     return 1;
    00237   }
    00238   sq_pushbool(v,SQFalse);
    00239   return 1;
    00240 }
    00241
    00242 static void _addrexmatch(HSQUIRRELVM v,const SQChar *str,const SQChar *begin,const SQChar *end)
    00243 {
    00244   sq_newtable(v);
    00245   sq_pushstring(v,_SC("begin"),-1);
    00246   sq_pushinteger(v,begin - str);
    00247   sq_rawset(v,-3);
    00248   sq_pushstring(v,_SC("end"),-1);
    00249   sq_pushinteger(v,end - str);
    00250   sq_rawset(v,-3);
    00251 }
    00252
    00253 static SQInteger _regexp_search(HSQUIRRELVM v)
    00254 {
    00255   SETUP_REX(v);
    00256   const SQChar *str,*begin,*end;
    00257   SQInteger start = 0;
    00258   sq_getstring(v,2,&str);
    00259   if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
    00260   if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
    00261     _addrexmatch(v,str,begin,end);
    00262     return 1;
    00263   }
    00264   return 0;
    00265 }
    00266
    00267 static SQInteger _regexp_capture(HSQUIRRELVM v)
    00268 {
    00269   SETUP_REX(v);
    00270   const SQChar *str,*begin,*end;
    00271   SQInteger start = 0;
    00272   sq_getstring(v,2,&str);
    00273   if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
    00274   if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
    00275     SQInteger n = sqstd_rex_getsubexpcount(self);
    00276     SQRexMatch match;
    00277     sq_newarray(v,0);
    00278     for(SQInteger i = 0;i < n; i++) {
    00279       sqstd_rex_getsubexp(self,i,&match);
    00280       if(match.len > 0)
    00281         _addrexmatch(v,str,match.begin,match.begin+match.len);
    00282       else
    00283         _addrexmatch(v,str,str,str); //empty match
    00284       sq_arrayappend(v,-2);
    00285     }
    00286     return 1;
    00287   }
    00288   return 0;
    00289 }
    00290
    00291 static SQInteger _regexp_subexpcount(HSQUIRRELVM v)
    00292 {
    00293   SETUP_REX(v);
    00294   sq_pushinteger(v,sqstd_rex_getsubexpcount(self));
    00295   return 1;
    00296 }
    00297
    00298 static SQInteger _regexp_constructor(HSQUIRRELVM v)
    00299 {
    00300   const SQChar *error,*pattern;
    00301   sq_getstring(v,2,&pattern);
    00302   SQRex *rex = sqstd_rex_compile(pattern,&error);
    00303   if(!rex) return sq_throwerror(v,error);
    00304   sq_setinstanceup(v,1,rex);
    00305   sq_setreleasehook(v,1,_rexobj_releasehook);
    00306   return 0;
    00307 }
    00308
    00309 static SQInteger _regexp__typeof(HSQUIRRELVM v)
    00310 {
    00311   sq_pushstring(v,_SC("regexp"),-1);
    00312   return 1;
    00313 }
    00314
    00315 #define _DECL_REX_FUNC(name,nparams,pmask) {_SC(#name),_regexp_##name,nparams,pmask}
    00316 static SQRegFunction rexobj_funcs[]={
    00317   _DECL_REX_FUNC(constructor,2,_SC(".s")),
    00318   _DECL_REX_FUNC(search,-2,_SC("xsn")),
    00319   _DECL_REX_FUNC(match,2,_SC("xs")),
    00320   _DECL_REX_FUNC(capture,-2,_SC("xsn")),
    00321   _DECL_REX_FUNC(subexpcount,1,_SC("x")),
    00322   _DECL_REX_FUNC(_typeof,1,_SC("x")),
    00323   {0,0,0,0}
    00324 };
    00325
    00326 #define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_string_##name,nparams,pmask}
    00327 static SQRegFunction stringlib_funcs[]={
    00328   _DECL_FUNC(format,-2,_SC(".s")),
    00329   _DECL_FUNC(strip,2,_SC(".s")),
    00330   _DECL_FUNC(lstrip,2,_SC(".s")),
    00331   _DECL_FUNC(rstrip,2,_SC(".s")),
    00332   _DECL_FUNC(split,3,_SC(".ss")),
    00333   {0,0,0,0}
    00334 };
    00335
    00336
    00337 SQInteger sqstd_register_stringlib(HSQUIRRELVM v)
    00338 {
    00339   sq_pushstring(v,_SC("regexp"),-1);
    00340   sq_newclass(v,SQFalse);
    00341   SQInteger i = 0;
    00342   while(rexobj_funcs[i].name != 0) {
    00343     SQRegFunction &f = rexobj_funcs[i];
    00344     sq_pushstring(v,f.name,-1);
    00345     sq_newclosure(v,f.f,0);
    00346     sq_setparamscheck(v,f.nparamscheck,f.typemask);
    00347     sq_setnativeclosurename(v,-1,f.name);
    00348     sq_createslot(v,-3);
    00349     i++;
    00350   }
    00351   sq_createslot(v,-3);
    00352
    00353   i = 0;
    00354   while(stringlib_funcs[i].name!=0)
    00355   {
    00356     sq_pushstring(v,stringlib_funcs[i].name,-1);
    00357     sq_newclosure(v,stringlib_funcs[i].f,0);
    00358     sq_setparamscheck(v,stringlib_funcs[i].nparamscheck,stringlib_funcs[i].typemask);
    00359     sq_setnativeclosurename(v,-1,stringlib_funcs[i].name);
    00360     sq_createslot(v,-3);
    00361     i++;
    00362   }
    00363   return 1;
    00364 }