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#include <stdarg.h>
10
11#define MAX_FORMAT_LEN 20
12#define MAX_WFORMAT_LEN 3
13#define ADDITIONAL_FORMAT_SPACE (100*sizeof(SQChar))
14
15static SQBool isfmtchr(SQChar ch)
16{
17 switch(ch) {
18 case '-': case '+': case ' ': case '#': case '0': return SQTrue;
19 }
20 return SQFalse;
21}
22
23static SQInteger validate_format(HSQUIRRELVM v, SQChar *fmt, const SQChar *src, SQInteger n,SQInteger &width)
24{
25 SQChar *dummy;
26 SQChar swidth[MAX_WFORMAT_LEN];
27 SQInteger wc = 0;
28 SQInteger start = n;
29 fmt[0] = '%';
30 while (isfmtchr(src[n])) n++;
31 while (scisdigit(src[n])) {
32 swidth[wc] = src[n];
33 n++;
34 wc++;
35 if(wc>=MAX_WFORMAT_LEN)
36 return sq_throwerror(v,_SC("width format too long"));
37 }
38 swidth[wc] = '\0';
39 if(wc > 0) {
40 width = scstrtol(swidth,&dummy,10);
41 }
42 else
43 width = 0;
44 if (src[n] == '.') {
45 n++;
46
47 wc = 0;
48 while (scisdigit(src[n])) {
49 swidth[wc] = src[n];
50 n++;
51 wc++;
52 if(wc>=MAX_WFORMAT_LEN)
53 return sq_throwerror(v,_SC("precision format too long"));
54 }
55 swidth[wc] = '\0';
56 if(wc > 0) {
57 width += scstrtol(swidth,&dummy,10);
58
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
68SQRESULT sqstd_format(HSQUIRRELVM v,SQInteger nformatstringidx,SQInteger *outlen,SQChar **output)
69{
70 const SQChar *format;
71 SQChar *dest;
72 SQChar fmt[MAX_FORMAT_LEN];
73 const SQRESULT res = sq_getstring(v,nformatstringidx,&format);
74 if (SQ_FAILED(res)) {
75 return res; // propagate the error
76 }
77 SQInteger format_size = sq_getsize(v,nformatstringidx);
78 SQInteger allocated = (format_size+2)*sizeof(SQChar);
79 dest = sq_getscratchpad(v,allocated);
80 SQInteger n = 0,i = 0, nparam = nformatstringidx+1, w = 0;
81 //while(format[n] != '\0')
82 while(n < format_size)
83 {
84 if(format[n] != '%') {
85 assert(i < allocated);
86 dest[i++] = format[n];
87 n++;
88 }
89 else if(format[n+1] == '%') { //handles %%
90 dest[i++] = '%';
91 n += 2;
92 }
93 else {
94 n++;
95 if( nparam > sq_gettop(v) )
96 return sq_throwerror(v,_SC("not enough parameters for the given format string"));
97 n = validate_format(v,fmt,format,n,w);
98 if(n < 0) return -1;
99 SQInteger addlen = 0;
100 SQInteger valtype = 0;
101 const SQChar *ts = NULL;
102 SQInteger ti = 0;
103 SQFloat tf = 0;
104 switch(format[n]) {
105 case 's':
106 if(SQ_FAILED(sq_getstring(v,nparam,&ts)))
107 return sq_throwerror(v,_SC("string expected for the specified format"));
108 addlen = (sq_getsize(v,nparam)*sizeof(SQChar))+((w+1)*sizeof(SQChar));
109 valtype = 's';
110 break;
111 case 'i': case 'd': case 'o': case 'u': case 'x': case 'X':
112#ifdef _SQ64
113 {
114 size_t flen = scstrlen(fmt);
115 SQInteger fpos = flen - 1;
116 SQChar f = fmt[fpos];
117 const SQChar *prec = (const SQChar *)_PRINT_INT_PREC;
118 while(*prec != _SC('\0')) {
119 fmt[fpos++] = *prec++;
120 }
121 fmt[fpos++] = f;
122 fmt[fpos++] = _SC('\0');
123 }
124#endif
125 case 'c':
126 if(SQ_FAILED(sq_getinteger(v,nparam,&ti)))
127 return sq_throwerror(v,_SC("integer expected for the specified format"));
128 addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
129 valtype = 'i';
130 break;
131 case 'f': case 'g': case 'G': case 'e': case 'E':
132 if(SQ_FAILED(sq_getfloat(v,nparam,&tf)))
133 return sq_throwerror(v,_SC("float expected for the specified format"));
134 addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(SQChar));
135 valtype = 'f';
136 break;
137 default:
138 return sq_throwerror(v,_SC("invalid format"));
139 }
140 n++;
141 allocated += addlen + sizeof(SQChar);
142 dest = sq_getscratchpad(v,allocated);
143 switch(valtype) {
144 case 's': i += scsprintf(&dest[i],allocated,fmt,ts); break;
145 case 'i': i += scsprintf(&dest[i],allocated,fmt,ti); break;
146 case 'f': i += scsprintf(&dest[i],allocated,fmt,tf); break;
147 };
148 nparam ++;
149 }
150 }
151 *outlen = i;
152 dest[i] = '\0';
153 *output = dest;
154 return SQ_OK;
155}
156
157void sqstd_pushstringf(HSQUIRRELVM v,const SQChar *s,...)
158{
159 SQInteger n=256;
160 va_list args;
161begin:
162 va_start(args,s);
163 SQChar *b=sq_getscratchpad(v,n);
164 SQInteger r=scvsprintf(b,n,s,args);
165 va_end(args);
166 if (r>=n) {
167 n=r+1;//required+null
168 goto begin;
169 } else if (r<0) {
170 sq_pushnull(v);
171 } else {
172 sq_pushstring(v,b,r);
173 }
174}
175
176static SQInteger _string_printf(HSQUIRRELVM v)
177{
178 SQChar *dest = NULL;
179 SQInteger length = 0;
180 if(SQ_FAILED(sqstd_format(v,2,&length,&dest)))
181 return -1;
182
183 SQPRINTFUNCTION printfunc = sq_getprintfunc(v);
184 if(printfunc) printfunc(v,dest);
185
186 return 0;
187}
188
189static SQInteger _string_format(HSQUIRRELVM v)
190{
191 SQChar *dest = NULL;
192 SQInteger length = 0;
193 if(SQ_FAILED(sqstd_format(v,2,&length,&dest)))
194 return -1;
195 sq_pushstring(v,dest,length);
196 return 1;
197}
198
199static void __strip_l(const SQChar *str,const SQChar **start)
200{
201 const SQChar *t = str;
202 while(((*t) != '\0') && scisspace(*t)){ t++; }
203 *start = t;
204}
205
206static void __strip_r(const SQChar *str,SQInteger len,const SQChar **end)
207{
208 if(len == 0) {
209 *end = str;
210 return;
211 }
212 const SQChar *t = &str[len-1];
213 while(t >= str && scisspace(*t)) { t--; }
214 *end = t + 1;
215}
216
217static SQInteger _string_strip(HSQUIRRELVM v)
218{
219 const SQChar *str,*start,*end;
220 sq_getstring(v,2,&str);
221 SQInteger len = sq_getsize(v,2);
222 __strip_l(str,&start);
223 __strip_r(str,len,&end);
224 sq_pushstring(v,start,end - start);
225 return 1;
226}
227
228static SQInteger _string_lstrip(HSQUIRRELVM v)
229{
230 const SQChar *str,*start;
231 sq_getstring(v,2,&str);
232 __strip_l(str,&start);
233 sq_pushstring(v,start,-1);
234 return 1;
235}
236
237static SQInteger _string_rstrip(HSQUIRRELVM v)
238{
239 const SQChar *str,*end;
240 sq_getstring(v,2,&str);
241 SQInteger len = sq_getsize(v,2);
242 __strip_r(str,len,&end);
243 sq_pushstring(v,str,end - str);
244 return 1;
245}
246
247static SQInteger _string_split(HSQUIRRELVM v)
248{
249 const SQChar *str,*seps;
250 SQChar *stemp;
251 sq_getstring(v,2,&str);
252 sq_getstring(v,3,&seps);
253 SQInteger sepsize = sq_getsize(v,3);
254 if(sepsize == 0) return sq_throwerror(v,_SC("empty separators string"));
255 SQInteger memsize = (sq_getsize(v,2)+1)*sizeof(SQChar);
256 stemp = sq_getscratchpad(v,memsize);
257 memcpy(stemp,str,memsize);
258 SQChar *start = stemp;
259 SQChar *end = stemp;
260 sq_newarray(v,0);
261 while(*end != '\0')
262 {
263 SQChar cur = *end;
264 for(SQInteger i = 0; i < sepsize; i++)
265 {
266 if(cur == seps[i])
267 {
268 *end = 0;
269 sq_pushstring(v,start,-1);
270 sq_arrayappend(v,-2);
271 start = end + 1;
272 break;
273 }
274 }
275 end++;
276 }
277 if(end != start)
278 {
279 sq_pushstring(v,start,-1);
280 sq_arrayappend(v,-2);
281 }
282 return 1;
283}
284
285static SQInteger _string_escape(HSQUIRRELVM v)
286{
287 const SQChar *str;
288 SQChar *dest,*resstr;
289 SQInteger size;
290 sq_getstring(v,2,&str);
291 size = sq_getsize(v,2);
292 if(size == 0) {
293 sq_push(v,2);
294 return 1;
295 }
296#ifdef SQUNICODE
297#if WCHAR_SIZE == 2
298 const SQChar *escpat = _SC("\\x%04x");
299 const SQInteger maxescsize = 6;
300#else //WCHAR_SIZE == 4
301 const SQChar *escpat = _SC("\\x%08x");
302 const SQInteger maxescsize = 10;
303#endif
304#else
305 const SQChar *escpat = _SC("\\x%02x");
306 const SQInteger maxescsize = 4;
307#endif
308 SQInteger destcharsize = (size * maxescsize); //assumes every char could be escaped
309 resstr = dest = (SQChar *)sq_getscratchpad(v,destcharsize * sizeof(SQChar));
310 SQChar c;
311 SQChar escch;
312 SQInteger escaped = 0;
313 for(int n = 0; n < size; n++){
314 c = *str++;
315 escch = 0;
316 if(scisprint(c) || c == 0) {
317 switch(c) {
318 case '\a': escch = 'a'; break;
319 case '\b': escch = 'b'; break;
320 case '\t': escch = 't'; break;
321 case '\n': escch = 'n'; break;
322 case '\v': escch = 'v'; break;
323 case '\f': escch = 'f'; break;
324 case '\r': escch = 'r'; break;
325 case '\\': escch = '\\'; break;
326 case '\"': escch = '\"'; break;
327 case '\'': escch = '\''; break;
328 case 0: escch = '0'; break;
329 }
330 if(escch) {
331 *dest++ = '\\';
332 *dest++ = escch;
333 escaped++;
334 }
335 else {
336 *dest++ = c;
337 }
338 }
339 else {
340
341 dest += scsprintf(dest, destcharsize, escpat, c);
342 escaped++;
343 }
344 }
345
346 if(escaped) {
347 sq_pushstring(v,resstr,dest - resstr);
348 }
349 else {
350 sq_push(v,2); //nothing escaped
351 }
352 return 1;
353}
354
355static SQInteger _string_startswith(HSQUIRRELVM v)
356{
357 const SQChar *str,*cmp;
358 sq_getstring(v,2,&str);
359 sq_getstring(v,3,&cmp);
360 SQInteger len = sq_getsize(v,2);
361 SQInteger cmplen = sq_getsize(v,3);
362 SQBool ret = SQFalse;
363 if(cmplen <= len) {
364 ret = memcmp(str,cmp,sq_rsl(cmplen)) == 0 ? SQTrue : SQFalse;
365 }
366 sq_pushbool(v,ret);
367 return 1;
368}
369
370static SQInteger _string_endswith(HSQUIRRELVM v)
371{
372 const SQChar *str,*cmp;
373 sq_getstring(v,2,&str);
374 sq_getstring(v,3,&cmp);
375 SQInteger len = sq_getsize(v,2);
376 SQInteger cmplen = sq_getsize(v,3);
377 SQBool ret = SQFalse;
378 if(cmplen <= len) {
379 ret = memcmp(&str[len - cmplen],cmp,sq_rsl(cmplen)) == 0 ? SQTrue : SQFalse;
380 }
381 sq_pushbool(v,ret);
382 return 1;
383}
384
385#define SETUP_REX(v) \
386 SQRex *self = NULL; \
387 sq_getinstanceup(v,1,(SQUserPointer *)&self,0);
388
389static SQInteger _rexobj_releasehook(SQUserPointer p, SQInteger SQ_UNUSED_ARG(size))
390{
391 SQRex *self = ((SQRex *)p);
392 sqstd_rex_free(self);
393 return 1;
394}
395
396static SQInteger _regexp_match(HSQUIRRELVM v)
397{
398 SETUP_REX(v);
399 const SQChar *str;
400 sq_getstring(v,2,&str);
401 if(sqstd_rex_match(self,str) == SQTrue)
402 {
403 sq_pushbool(v,SQTrue);
404 return 1;
405 }
406 sq_pushbool(v,SQFalse);
407 return 1;
408}
409
410static void _addrexmatch(HSQUIRRELVM v,const SQChar *str,const SQChar *begin,const SQChar *end)
411{
412 sq_newtable(v);
413 sq_pushstring(v,_SC("begin"),-1);
414 sq_pushinteger(v,begin - str);
415 sq_rawset(v,-3);
416 sq_pushstring(v,_SC("end"),-1);
417 sq_pushinteger(v,end - str);
418 sq_rawset(v,-3);
419}
420
421static SQInteger _regexp_search(HSQUIRRELVM v)
422{
423 SETUP_REX(v);
424 const SQChar *str,*begin,*end;
425 SQInteger start = 0;
426 sq_getstring(v,2,&str);
427 if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
428 if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
429 _addrexmatch(v,str,begin,end);
430 return 1;
431 }
432 return 0;
433}
434
435static SQInteger _regexp_capture(HSQUIRRELVM v)
436{
437 SETUP_REX(v);
438 const SQChar *str,*begin,*end;
439 SQInteger start = 0;
440 sq_getstring(v,2,&str);
441 if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
442 if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
443 SQInteger n = sqstd_rex_getsubexpcount(self);
444 SQRexMatch match;
445 sq_newarray(v,0);
446 for(SQInteger i = 0;i < n; i++) {
447 sqstd_rex_getsubexp(self,i,&match);
448 if(match.len > 0)
449 _addrexmatch(v,str,match.begin,match.begin+match.len);
450 else
451 _addrexmatch(v,str,str,str); //empty match
452 sq_arrayappend(v,-2);
453 }
454 return 1;
455 }
456 return 0;
457}
458
459static SQInteger _regexp_subexpcount(HSQUIRRELVM v)
460{
461 SETUP_REX(v);
462 sq_pushinteger(v,sqstd_rex_getsubexpcount(self));
463 return 1;
464}
465
466static SQInteger _regexp_constructor(HSQUIRRELVM v)
467{
468 const SQChar *error,*pattern;
469 sq_getstring(v,2,&pattern);
470 SQRex *rex = sqstd_rex_compile(pattern,&error);
471 if(!rex) return sq_throwerror(v,error);
472 sq_setinstanceup(v,1,rex);
473 sq_setreleasehook(v,1,_rexobj_releasehook);
474 return 0;
475}
476
477static SQInteger _regexp__typeof(HSQUIRRELVM v)
478{
479 sq_pushstring(v,_SC("regexp"),-1);
480 return 1;
481}
482
483#define _DECL_REX_FUNC(name,nparams,pmask) {_SC(#name),_regexp_##name,nparams,pmask}
484static const SQRegFunction rexobj_funcs[]={
485 _DECL_REX_FUNC(constructor,2,_SC(".s")),
486 _DECL_REX_FUNC(search,-2,_SC("xsn")),
487 _DECL_REX_FUNC(match,2,_SC("xs")),
488 _DECL_REX_FUNC(capture,-2,_SC("xsn")),
489 _DECL_REX_FUNC(subexpcount,1,_SC("x")),
490 _DECL_REX_FUNC(_typeof,1,_SC("x")),
491 {NULL,(SQFUNCTION)0,0,NULL}
492};
493#undef _DECL_REX_FUNC
494
495#define _DECL_FUNC(name,nparams,pmask) {_SC(#name),_string_##name,nparams,pmask}
496static const SQRegFunction stringlib_funcs[]={
497 _DECL_FUNC(format,-2,_SC(".s")),
498 _DECL_FUNC(printf,-2,_SC(".s")),
499 _DECL_FUNC(strip,2,_SC(".s")),
500 _DECL_FUNC(lstrip,2,_SC(".s")),
501 _DECL_FUNC(rstrip,2,_SC(".s")),
502 _DECL_FUNC(split,3,_SC(".ss")),
503 _DECL_FUNC(escape,2,_SC(".s")),
504 _DECL_FUNC(startswith,3,_SC(".ss")),
505 _DECL_FUNC(endswith,3,_SC(".ss")),
506 {NULL,(SQFUNCTION)0,0,NULL}
507};
508#undef _DECL_FUNC
509
510
511SQInteger sqstd_register_stringlib(HSQUIRRELVM v)
512{
513 sq_pushstring(v,_SC("regexp"),-1);
514 sq_newclass(v,SQFalse);
515 SQInteger i = 0;
516 while(rexobj_funcs[i].name != 0) {
517 const SQRegFunction &f = rexobj_funcs[i];
518 sq_pushstring(v,f.name,-1);
519 sq_newclosure(v,f.f,0);
520 sq_setparamscheck(v,f.nparamscheck,f.typemask);
521 sq_setnativeclosurename(v,-1,f.name);
522 sq_newslot(v,-3,SQFalse);
523 i++;
524 }
525 sq_newslot(v,-3,SQFalse);
526
527 i = 0;
528 while(stringlib_funcs[i].name!=0)
529 {
530 sq_pushstring(v,stringlib_funcs[i].name,-1);
531 sq_newclosure(v,stringlib_funcs[i].f,0);
532 sq_setparamscheck(v,stringlib_funcs[i].nparamscheck,stringlib_funcs[i].typemask);
533 sq_setnativeclosurename(v,-1,stringlib_funcs[i].name);
534 sq_newslot(v,-3,SQFalse);
535 i++;
536 }
537 return 1;
538}
539