Upgraded to Squirrel 2.2
[supertux.git] / src / squirrel / sqstdlib / sqstdstring.cpp
1 /* see copyright notice in squirrel.h */
2 #include <squirrel.h>
3 #include <sqstdstring.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <ctype.h>
8 #include <assert.h>
9
10 #ifdef SQUNICODE
11 #define scstrchr wcschr
12 #define scsnprintf wsnprintf
13 #define scatoi _wtoi
14 #define scstrtok wcstok
15 #else
16 #define scstrchr strchr
17 #define scsnprintf snprintf
18 #define scatoi atoi
19 #define scstrtok strtok
20 #endif
21 #define MAX_FORMAT_LEN  20
22 #define MAX_WFORMAT_LEN 3
23 #define ADDITIONAL_FORMAT_SPACE (100*sizeof(SQChar))
24
25 static SQInteger validate_format(HSQUIRRELVM v, SQChar *fmt, const SQChar *src, SQInteger n,SQInteger &width)
26 {
27         SQChar swidth[MAX_WFORMAT_LEN];
28         SQInteger wc = 0;
29         SQInteger start = n;
30         fmt[0] = '%';
31         while (scstrchr(_SC("-+ #0"), src[n])) n++;
32         while (scisdigit(src[n])) {
33                 swidth[wc] = src[n];
34                 n++;
35                 wc++;
36                 if(wc>=MAX_WFORMAT_LEN)
37                         return sq_throwerror(v,_SC("width format too long"));
38         }
39         swidth[wc] = '\0';
40         if(wc > 0) {
41                 width = scatoi(swidth);
42         }
43         else
44                 width = 0;
45         if (src[n] == '.') {
46             n++;
47         
48                 wc = 0;
49                 while (scisdigit(src[n])) {
50                         swidth[wc] = src[n];
51                         n++;
52                         wc++;
53                         if(wc>=MAX_WFORMAT_LEN)
54                                 return sq_throwerror(v,_SC("precision format too long"));
55                 }
56                 swidth[wc] = '\0';
57                 if(wc > 0) {
58                         width += scatoi(swidth);
59                 }
60         }
61         if (n-start > MAX_FORMAT_LEN )
62                 return sq_throwerror(v,_SC("format too long"));
63         memcpy(&fmt[1],&src[start],((n-start)+1)*sizeof(SQChar));
64         fmt[(n-start)+2] = '\0';
65         return n;
66 }
67
68 static SQInteger _string_format(HSQUIRRELVM v)
69 {
70         const SQChar *format;
71         SQChar *dest;
72         SQChar fmt[MAX_FORMAT_LEN];
73         sq_getstring(v,2,&format);
74         SQInteger allocated = (sq_getsize(v,2)+1)*sizeof(SQChar);
75         dest = sq_getscratchpad(v,allocated);
76         SQInteger n = 0,i = 0, nparam = 3, w = 0;
77         while(format[n] != '\0') {
78                 if(format[n] != '%') {
79                         assert(i < allocated);
80                         dest[i++] = format[n];
81                         n++;
82                 }
83                 else if(format[n+1] == '%') { //handles %%
84                                 dest[i++] = '%';
85                                 n += 2; 
86                 }
87                 else {
88                         n++;
89                         if( nparam > sq_gettop(v) )
90                                 return sq_throwerror(v,_SC("not enough paramters for the given format string"));
91                         n = validate_format(v,fmt,format,n,w);
92                         if(n < 0) return -1;
93                         SQInteger addlen = 0;
94                         SQInteger valtype = 0;
95                         const SQChar *ts;
96                         SQInteger ti;
97                         SQFloat tf;
98                         switch(format[n]) {
99                         case 's':
100                                 if(SQ_FAILED(sq_getstring(v,nparam,&ts))) 
101                                         return sq_throwerror(v,_SC("string expected for the specified format"));
102                                 addlen = (sq_getsize(v,nparam)*sizeof(SQChar))+((w+1)*sizeof(SQChar));
103                                 valtype = 's';
104                                 break;
105                         case 'i': case 'd': case 'c':case 'o':  case 'u':  case 'x':  case 'X':
106                                 if(SQ_FAILED(sq_getinteger(v,nparam,&ti))) 
107                                         return sq_throwerror(v,_SC("integer expected for the specified format"));
108                                 addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
109                                 valtype = 'i';
110                                 break;
111                         case 'f': case 'g': case 'G': case 'e':  case 'E':
112                                 if(SQ_FAILED(sq_getfloat(v,nparam,&tf))) 
113                                         return sq_throwerror(v,_SC("float expected for the specified format"));
114                                 addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
115                                 valtype = 'f';
116                                 break;
117                         default:
118                                 return sq_throwerror(v,_SC("invalid format"));
119                         }
120                         n++;
121                         allocated += addlen;
122                         dest = sq_getscratchpad(v,allocated);
123                         switch(valtype) {
124                         case 's': i += scsprintf(&dest[i],fmt,ts); break;
125                         case 'i': i += scsprintf(&dest[i],fmt,ti); break;
126                         case 'f': i += scsprintf(&dest[i],fmt,tf); break;
127                         };
128                         nparam ++;
129                 }
130         }
131         sq_pushstring(v,dest,i);
132         return 1;
133 }
134
135 static void __strip_l(const SQChar *str,const SQChar **start)
136 {
137         const SQChar *t = str;
138         while(((*t) != '\0') && scisspace(*t)){ t++; }
139         *start = t;
140 }
141
142 static void __strip_r(const SQChar *str,SQInteger len,const SQChar **end)
143 {
144         if(len == 0) {
145                 *end = str;
146                 return;
147         }
148         const SQChar *t = &str[len-1];
149         while(t != str && scisspace(*t)) { t--; }
150         *end = t+1;
151 }
152
153 static SQInteger _string_strip(HSQUIRRELVM v)
154 {
155         const SQChar *str,*start,*end;
156         sq_getstring(v,2,&str);
157         SQInteger len = sq_getsize(v,2);
158         __strip_l(str,&start);
159         __strip_r(str,len,&end);
160         sq_pushstring(v,start,end - start);
161         return 1;
162 }
163
164 static SQInteger _string_lstrip(HSQUIRRELVM v)
165 {
166         const SQChar *str,*start;
167         sq_getstring(v,2,&str);
168         __strip_l(str,&start);
169         sq_pushstring(v,start,-1);
170         return 1;
171 }
172
173 static SQInteger _string_rstrip(HSQUIRRELVM v)
174 {
175         const SQChar *str,*end;
176         sq_getstring(v,2,&str);
177         SQInteger len = sq_getsize(v,2);
178         __strip_r(str,len,&end);
179         sq_pushstring(v,str,end - str);
180         return 1;
181 }
182
183 static SQInteger _string_split(HSQUIRRELVM v)
184 {
185         const SQChar *str,*seps;
186         SQChar *stemp,*tok;
187         sq_getstring(v,2,&str);
188         sq_getstring(v,3,&seps);
189         if(sq_getsize(v,3) == 0) return sq_throwerror(v,_SC("empty separators string"));
190         SQInteger memsize = (sq_getsize(v,2)+1)*sizeof(SQChar);
191         stemp = sq_getscratchpad(v,memsize);
192         memcpy(stemp,str,memsize);
193         tok = scstrtok(stemp,seps);
194         sq_newarray(v,0);
195         while( tok != NULL ) {
196                 sq_pushstring(v,tok,-1);
197                 sq_arrayappend(v,-2);
198                 tok = scstrtok( NULL, seps );
199         }
200         return 1;
201 }
202
203 #define SETUP_REX(v) \
204         SQRex *self = NULL; \
205         sq_getinstanceup(v,1,(SQUserPointer *)&self,0); 
206
207 static SQInteger _rexobj_releasehook(SQUserPointer p, SQInteger size)
208 {
209         SQRex *self = ((SQRex *)p);
210         sqstd_rex_free(self);
211         return 1;
212 }
213
214 static SQInteger _regexp_match(HSQUIRRELVM v)
215 {
216         SETUP_REX(v);
217         const SQChar *str;
218         sq_getstring(v,2,&str);
219         if(sqstd_rex_match(self,str) == SQTrue)
220         {
221                 sq_pushbool(v,SQTrue);
222                 return 1;
223         }
224         sq_pushbool(v,SQFalse);
225         return 1;
226 }
227
228 static void _addrexmatch(HSQUIRRELVM v,const SQChar *str,const SQChar *begin,const SQChar *end)
229 {
230         sq_newtable(v);
231         sq_pushstring(v,_SC("begin"),-1);
232         sq_pushinteger(v,begin - str);
233         sq_rawset(v,-3);
234         sq_pushstring(v,_SC("end"),-1);
235         sq_pushinteger(v,end - str);
236         sq_rawset(v,-3);
237 }
238
239 static SQInteger _regexp_search(HSQUIRRELVM v)
240 {
241         SETUP_REX(v);
242         const SQChar *str,*begin,*end;
243         SQInteger start = 0;
244         sq_getstring(v,2,&str);
245         if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
246         if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
247                 _addrexmatch(v,str,begin,end);
248                 return 1;
249         }
250         return 0;
251 }
252
253 static SQInteger _regexp_capture(HSQUIRRELVM v)
254 {
255         SETUP_REX(v);
256         const SQChar *str,*begin,*end;
257         SQInteger start = 0;
258         sq_getstring(v,2,&str);
259         if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
260         if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
261                 SQInteger n = sqstd_rex_getsubexpcount(self);
262                 SQRexMatch match;
263                 sq_newarray(v,0);
264                 for(SQInteger i = 0;i < n; i++) {
265                         sqstd_rex_getsubexp(self,i,&match);
266                         if(match.len > 0)
267                                 _addrexmatch(v,str,match.begin,match.begin+match.len);
268                         else
269                                 _addrexmatch(v,str,str,str); //empty match
270                         sq_arrayappend(v,-2);
271                 }
272                 return 1;
273         }
274         return 0;
275 }
276
277 static SQInteger _regexp_subexpcount(HSQUIRRELVM v)
278 {
279         SETUP_REX(v);
280         sq_pushinteger(v,sqstd_rex_getsubexpcount(self));
281         return 1;
282 }
283
284 static SQInteger _regexp_constructor(HSQUIRRELVM v)
285 {
286         const SQChar *error,*pattern;
287         sq_getstring(v,2,&pattern);
288         SQRex *rex = sqstd_rex_compile(pattern,&error);
289         if(!rex) return sq_throwerror(v,error);
290         sq_setinstanceup(v,1,rex);
291         sq_setreleasehook(v,1,_rexobj_releasehook);
292         return 0;
293 }
294
295 static SQInteger _regexp__typeof(HSQUIRRELVM v)
296 {
297         sq_pushstring(v,_SC("regexp"),-1);
298         return 1;
299 }
300
301 #define _DECL_REX_FUNC(name,nparams,pmask) {_SC(#name),_regexp_##name,nparams,pmask}
302 static SQRegFunction rexobj_funcs[]={
303         _DECL_REX_FUNC(constructor,2,_SC(".s")),
304         _DECL_REX_FUNC(search,-2,_SC("xsn")),
305         _DECL_REX_FUNC(match,2,_SC("xs")),
306         _DECL_REX_FUNC(capture,-2,_SC("xsn")),
307         _DECL_REX_FUNC(subexpcount,1,_SC("x")),
308         _DECL_REX_FUNC(_typeof,1,_SC("x")),
309         {0,0}
310 };
311
312 #define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_string_##name,nparams,pmask}
313 static SQRegFunction stringlib_funcs[]={
314         _DECL_FUNC(format,-2,_SC(".s")),
315         _DECL_FUNC(strip,2,_SC(".s")),
316         _DECL_FUNC(lstrip,2,_SC(".s")),
317         _DECL_FUNC(rstrip,2,_SC(".s")),
318         _DECL_FUNC(split,3,_SC(".ss")),
319         {0,0}
320 };
321
322
323 SQInteger sqstd_register_stringlib(HSQUIRRELVM v)
324 {
325         sq_pushstring(v,_SC("regexp"),-1);
326         sq_newclass(v,SQFalse);
327         SQInteger i = 0;
328         while(rexobj_funcs[i].name != 0) {
329                 SQRegFunction &f = rexobj_funcs[i];
330                 sq_pushstring(v,f.name,-1);
331                 sq_newclosure(v,f.f,0);
332                 sq_setparamscheck(v,f.nparamscheck,f.typemask);
333                 sq_setnativeclosurename(v,-1,f.name);
334                 sq_createslot(v,-3);
335                 i++;
336         }
337         sq_createslot(v,-3);
338
339         i = 0;
340         while(stringlib_funcs[i].name!=0)
341         {
342                 sq_pushstring(v,stringlib_funcs[i].name,-1);
343                 sq_newclosure(v,stringlib_funcs[i].f,0);
344                 sq_setparamscheck(v,stringlib_funcs[i].nparamscheck,stringlib_funcs[i].typemask);
345                 sq_setnativeclosurename(v,-1,stringlib_funcs[i].name);
346                 sq_createslot(v,-3);
347                 i++;
348         }
349         return 1;
350 }