Merged changes from branches/supertux-milestone2-grumbel/ to trunk/supertux/
[supertux.git] / external / 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
69 SQRESULT sqstd_format(HSQUIRRELVM v,SQInteger nformatstringidx,SQInteger *outlen,SQChar **output)
70 {
71         const SQChar *format;
72         SQChar *dest;
73         SQChar fmt[MAX_FORMAT_LEN];
74         sq_getstring(v,nformatstringidx,&format);
75         SQInteger allocated = (sq_getsize(v,nformatstringidx)+2)*sizeof(SQChar);
76         dest = sq_getscratchpad(v,allocated);
77         SQInteger n = 0,i = 0, nparam = nformatstringidx+1, w = 0;
78         while(format[n] != '\0') {
79                 if(format[n] != '%') {
80                         assert(i < allocated);
81                         dest[i++] = format[n];
82                         n++;
83                 }
84                 else if(format[n+1] == '%') { //handles %%
85                                 dest[i++] = '%';
86                                 n += 2; 
87                 }
88                 else {
89                         n++;
90                         if( nparam > sq_gettop(v) )
91                                 return sq_throwerror(v,_SC("not enough paramters for the given format string"));
92                         n = validate_format(v,fmt,format,n,w);
93                         if(n < 0) return -1;
94                         SQInteger addlen = 0;
95                         SQInteger valtype = 0;
96                         const SQChar *ts;
97                         SQInteger ti;
98                         SQFloat tf;
99                         switch(format[n]) {
100                         case 's':
101                                 if(SQ_FAILED(sq_getstring(v,nparam,&ts))) 
102                                         return sq_throwerror(v,_SC("string expected for the specified format"));
103                                 addlen = (sq_getsize(v,nparam)*sizeof(SQChar))+((w+1)*sizeof(SQChar));
104                                 valtype = 's';
105                                 break;
106                         case 'i': case 'd': case 'c':case 'o':  case 'u':  case 'x':  case 'X':
107                                 if(SQ_FAILED(sq_getinteger(v,nparam,&ti))) 
108                                         return sq_throwerror(v,_SC("integer expected for the specified format"));
109                                 addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
110                                 valtype = 'i';
111                                 break;
112                         case 'f': case 'g': case 'G': case 'e':  case 'E':
113                                 if(SQ_FAILED(sq_getfloat(v,nparam,&tf))) 
114                                         return sq_throwerror(v,_SC("float expected for the specified format"));
115                                 addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
116                                 valtype = 'f';
117                                 break;
118                         default:
119                                 return sq_throwerror(v,_SC("invalid format"));
120                         }
121                         n++;
122                         allocated += addlen + sizeof(SQChar);
123                         dest = sq_getscratchpad(v,allocated);
124                         switch(valtype) {
125                         case 's': i += scsprintf(&dest[i],fmt,ts); break;
126                         case 'i': i += scsprintf(&dest[i],fmt,ti); break;
127                         case 'f': i += scsprintf(&dest[i],fmt,tf); break;
128                         };
129                         nparam ++;
130                 }
131         }
132         *outlen = i;
133         dest[i] = '\0';
134         *output = dest;
135         return SQ_OK;
136 }
137
138 static SQInteger _string_format(HSQUIRRELVM v)
139 {
140         SQChar *dest = NULL;
141         SQInteger length = 0;
142         if(SQ_FAILED(sqstd_format(v,2,&length,&dest)))
143                 return -1;
144         sq_pushstring(v,dest,length);
145         return 1;
146 }
147
148 static void __strip_l(const SQChar *str,const SQChar **start)
149 {
150         const SQChar *t = str;
151         while(((*t) != '\0') && scisspace(*t)){ t++; }
152         *start = t;
153 }
154
155 static void __strip_r(const SQChar *str,SQInteger len,const SQChar **end)
156 {
157         if(len == 0) {
158                 *end = str;
159                 return;
160         }
161         const SQChar *t = &str[len-1];
162         while(t != str && scisspace(*t)) { t--; }
163         *end = t+1;
164 }
165
166 static SQInteger _string_strip(HSQUIRRELVM v)
167 {
168         const SQChar *str,*start,*end;
169         sq_getstring(v,2,&str);
170         SQInteger len = sq_getsize(v,2);
171         __strip_l(str,&start);
172         __strip_r(str,len,&end);
173         sq_pushstring(v,start,end - start);
174         return 1;
175 }
176
177 static SQInteger _string_lstrip(HSQUIRRELVM v)
178 {
179         const SQChar *str,*start;
180         sq_getstring(v,2,&str);
181         __strip_l(str,&start);
182         sq_pushstring(v,start,-1);
183         return 1;
184 }
185
186 static SQInteger _string_rstrip(HSQUIRRELVM v)
187 {
188         const SQChar *str,*end;
189         sq_getstring(v,2,&str);
190         SQInteger len = sq_getsize(v,2);
191         __strip_r(str,len,&end);
192         sq_pushstring(v,str,end - str);
193         return 1;
194 }
195
196 static SQInteger _string_split(HSQUIRRELVM v)
197 {
198         const SQChar *str,*seps;
199         SQChar *stemp,*tok;
200         sq_getstring(v,2,&str);
201         sq_getstring(v,3,&seps);
202         if(sq_getsize(v,3) == 0) return sq_throwerror(v,_SC("empty separators string"));
203         SQInteger memsize = (sq_getsize(v,2)+1)*sizeof(SQChar);
204         stemp = sq_getscratchpad(v,memsize);
205         memcpy(stemp,str,memsize);
206         tok = scstrtok(stemp,seps);
207         sq_newarray(v,0);
208         while( tok != NULL ) {
209                 sq_pushstring(v,tok,-1);
210                 sq_arrayappend(v,-2);
211                 tok = scstrtok( NULL, seps );
212         }
213         return 1;
214 }
215
216 #define SETUP_REX(v) \
217         SQRex *self = NULL; \
218         sq_getinstanceup(v,1,(SQUserPointer *)&self,0); 
219
220 static SQInteger _rexobj_releasehook(SQUserPointer p, SQInteger size)
221 {
222         SQRex *self = ((SQRex *)p);
223         sqstd_rex_free(self);
224         return 1;
225 }
226
227 static SQInteger _regexp_match(HSQUIRRELVM v)
228 {
229         SETUP_REX(v);
230         const SQChar *str;
231         sq_getstring(v,2,&str);
232         if(sqstd_rex_match(self,str) == SQTrue)
233         {
234                 sq_pushbool(v,SQTrue);
235                 return 1;
236         }
237         sq_pushbool(v,SQFalse);
238         return 1;
239 }
240
241 static void _addrexmatch(HSQUIRRELVM v,const SQChar *str,const SQChar *begin,const SQChar *end)
242 {
243         sq_newtable(v);
244         sq_pushstring(v,_SC("begin"),-1);
245         sq_pushinteger(v,begin - str);
246         sq_rawset(v,-3);
247         sq_pushstring(v,_SC("end"),-1);
248         sq_pushinteger(v,end - str);
249         sq_rawset(v,-3);
250 }
251
252 static SQInteger _regexp_search(HSQUIRRELVM v)
253 {
254         SETUP_REX(v);
255         const SQChar *str,*begin,*end;
256         SQInteger start = 0;
257         sq_getstring(v,2,&str);
258         if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
259         if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
260                 _addrexmatch(v,str,begin,end);
261                 return 1;
262         }
263         return 0;
264 }
265
266 static SQInteger _regexp_capture(HSQUIRRELVM v)
267 {
268         SETUP_REX(v);
269         const SQChar *str,*begin,*end;
270         SQInteger start = 0;
271         sq_getstring(v,2,&str);
272         if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
273         if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
274                 SQInteger n = sqstd_rex_getsubexpcount(self);
275                 SQRexMatch match;
276                 sq_newarray(v,0);
277                 for(SQInteger i = 0;i < n; i++) {
278                         sqstd_rex_getsubexp(self,i,&match);
279                         if(match.len > 0)
280                                 _addrexmatch(v,str,match.begin,match.begin+match.len);
281                         else
282                                 _addrexmatch(v,str,str,str); //empty match
283                         sq_arrayappend(v,-2);
284                 }
285                 return 1;
286         }
287         return 0;
288 }
289
290 static SQInteger _regexp_subexpcount(HSQUIRRELVM v)
291 {
292         SETUP_REX(v);
293         sq_pushinteger(v,sqstd_rex_getsubexpcount(self));
294         return 1;
295 }
296
297 static SQInteger _regexp_constructor(HSQUIRRELVM v)
298 {
299         const SQChar *error,*pattern;
300         sq_getstring(v,2,&pattern);
301         SQRex *rex = sqstd_rex_compile(pattern,&error);
302         if(!rex) return sq_throwerror(v,error);
303         sq_setinstanceup(v,1,rex);
304         sq_setreleasehook(v,1,_rexobj_releasehook);
305         return 0;
306 }
307
308 static SQInteger _regexp__typeof(HSQUIRRELVM v)
309 {
310         sq_pushstring(v,_SC("regexp"),-1);
311         return 1;
312 }
313
314 #define _DECL_REX_FUNC(name,nparams,pmask) {_SC(#name),_regexp_##name,nparams,pmask}
315 static SQRegFunction rexobj_funcs[]={
316         _DECL_REX_FUNC(constructor,2,_SC(".s")),
317         _DECL_REX_FUNC(search,-2,_SC("xsn")),
318         _DECL_REX_FUNC(match,2,_SC("xs")),
319         _DECL_REX_FUNC(capture,-2,_SC("xsn")),
320         _DECL_REX_FUNC(subexpcount,1,_SC("x")),
321         _DECL_REX_FUNC(_typeof,1,_SC("x")),
322         {0,0}
323 };
324
325 #define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_string_##name,nparams,pmask}
326 static SQRegFunction stringlib_funcs[]={
327         _DECL_FUNC(format,-2,_SC(".s")),
328         _DECL_FUNC(strip,2,_SC(".s")),
329         _DECL_FUNC(lstrip,2,_SC(".s")),
330         _DECL_FUNC(rstrip,2,_SC(".s")),
331         _DECL_FUNC(split,3,_SC(".ss")),
332         {0,0}
333 };
334
335
336 SQInteger sqstd_register_stringlib(HSQUIRRELVM v)
337 {
338         sq_pushstring(v,_SC("regexp"),-1);
339         sq_newclass(v,SQFalse);
340         SQInteger i = 0;
341         while(rexobj_funcs[i].name != 0) {
342                 SQRegFunction &f = rexobj_funcs[i];
343                 sq_pushstring(v,f.name,-1);
344                 sq_newclosure(v,f.f,0);
345                 sq_setparamscheck(v,f.nparamscheck,f.typemask);
346                 sq_setnativeclosurename(v,-1,f.name);
347                 sq_createslot(v,-3);
348                 i++;
349         }
350         sq_createslot(v,-3);
351
352         i = 0;
353         while(stringlib_funcs[i].name!=0)
354         {
355                 sq_pushstring(v,stringlib_funcs[i].name,-1);
356                 sq_newclosure(v,stringlib_funcs[i].f,0);
357                 sq_setparamscheck(v,stringlib_funcs[i].nparamscheck,stringlib_funcs[i].typemask);
358                 sq_setnativeclosurename(v,-1,stringlib_funcs[i].name);
359                 sq_createslot(v,-3);
360                 i++;
361         }
362         return 1;
363 }