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