summaryrefslogtreecommitdiffstats
path: root/common/unqlite/jx9_builtin.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/unqlite/jx9_builtin.c')
-rw-r--r--common/unqlite/jx9_builtin.c8297
1 files changed, 8297 insertions, 0 deletions
diff --git a/common/unqlite/jx9_builtin.c b/common/unqlite/jx9_builtin.c
new file mode 100644
index 0000000..83e6c6e
--- /dev/null
+++ b/common/unqlite/jx9_builtin.c
@@ -0,0 +1,8297 @@
1/*
2 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
3 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
4 * Version 1.7.2
5 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
6 * please contact Symisc Systems via:
7 * legal@symisc.net
8 * licensing@symisc.net
9 * contact@symisc.net
10 * or visit:
11 * http://jx9.symisc.net/
12 */
13 /* $SymiscID: builtin.c v1.7 Win7 2012-12-13 00:01 stable <chm@symisc.net> $ */
14#ifndef JX9_AMALGAMATION
15#include "jx9Int.h"
16#endif
17/* This file implement built-in 'foreign' functions for the JX9 engine */
18/*
19 * Section:
20 * Variable handling Functions.
21 * Authors:
22 * Symisc Systems, devel@symisc.net.
23 * Copyright (C) Symisc Systems, http://jx9.symisc.net
24 * Status:
25 * Stable.
26 */
27/*
28 * bool is_bool($var)
29 * Finds out whether a variable is a boolean.
30 * Parameters
31 * $var: The variable being evaluated.
32 * Return
33 * TRUE if var is a boolean. False otherwise.
34 */
35static int jx9Builtin_is_bool(jx9_context *pCtx, int nArg, jx9_value **apArg)
36{
37 int res = 0; /* Assume false by default */
38 if( nArg > 0 ){
39 res = jx9_value_is_bool(apArg[0]);
40 }
41 /* Query result */
42 jx9_result_bool(pCtx, res);
43 return JX9_OK;
44}
45/*
46 * bool is_float($var)
47 * bool is_real($var)
48 * bool is_double($var)
49 * Finds out whether a variable is a float.
50 * Parameters
51 * $var: The variable being evaluated.
52 * Return
53 * TRUE if var is a float. False otherwise.
54 */
55static int jx9Builtin_is_float(jx9_context *pCtx, int nArg, jx9_value **apArg)
56{
57 int res = 0; /* Assume false by default */
58 if( nArg > 0 ){
59 res = jx9_value_is_float(apArg[0]);
60 }
61 /* Query result */
62 jx9_result_bool(pCtx, res);
63 return JX9_OK;
64}
65/*
66 * bool is_int($var)
67 * bool is_integer($var)
68 * bool is_long($var)
69 * Finds out whether a variable is an integer.
70 * Parameters
71 * $var: The variable being evaluated.
72 * Return
73 * TRUE if var is an integer. False otherwise.
74 */
75static int jx9Builtin_is_int(jx9_context *pCtx, int nArg, jx9_value **apArg)
76{
77 int res = 0; /* Assume false by default */
78 if( nArg > 0 ){
79 res = jx9_value_is_int(apArg[0]);
80 }
81 /* Query result */
82 jx9_result_bool(pCtx, res);
83 return JX9_OK;
84}
85/*
86 * bool is_string($var)
87 * Finds out whether a variable is a string.
88 * Parameters
89 * $var: The variable being evaluated.
90 * Return
91 * TRUE if var is string. False otherwise.
92 */
93static int jx9Builtin_is_string(jx9_context *pCtx, int nArg, jx9_value **apArg)
94{
95 int res = 0; /* Assume false by default */
96 if( nArg > 0 ){
97 res = jx9_value_is_string(apArg[0]);
98 }
99 /* Query result */
100 jx9_result_bool(pCtx, res);
101 return JX9_OK;
102}
103/*
104 * bool is_null($var)
105 * Finds out whether a variable is NULL.
106 * Parameters
107 * $var: The variable being evaluated.
108 * Return
109 * TRUE if var is NULL. False otherwise.
110 */
111static int jx9Builtin_is_null(jx9_context *pCtx, int nArg, jx9_value **apArg)
112{
113 int res = 0; /* Assume false by default */
114 if( nArg > 0 ){
115 res = jx9_value_is_null(apArg[0]);
116 }
117 /* Query result */
118 jx9_result_bool(pCtx, res);
119 return JX9_OK;
120}
121/*
122 * bool is_numeric($var)
123 * Find out whether a variable is NULL.
124 * Parameters
125 * $var: The variable being evaluated.
126 * Return
127 * True if var is numeric. False otherwise.
128 */
129static int jx9Builtin_is_numeric(jx9_context *pCtx, int nArg, jx9_value **apArg)
130{
131 int res = 0; /* Assume false by default */
132 if( nArg > 0 ){
133 res = jx9_value_is_numeric(apArg[0]);
134 }
135 /* Query result */
136 jx9_result_bool(pCtx, res);
137 return JX9_OK;
138}
139/*
140 * bool is_scalar($var)
141 * Find out whether a variable is a scalar.
142 * Parameters
143 * $var: The variable being evaluated.
144 * Return
145 * True if var is scalar. False otherwise.
146 */
147static int jx9Builtin_is_scalar(jx9_context *pCtx, int nArg, jx9_value **apArg)
148{
149 int res = 0; /* Assume false by default */
150 if( nArg > 0 ){
151 res = jx9_value_is_scalar(apArg[0]);
152 }
153 /* Query result */
154 jx9_result_bool(pCtx, res);
155 return JX9_OK;
156}
157/*
158 * bool is_array($var)
159 * Find out whether a variable is an array.
160 * Parameters
161 * $var: The variable being evaluated.
162 * Return
163 * True if var is an array. False otherwise.
164 */
165static int jx9Builtin_is_array(jx9_context *pCtx, int nArg, jx9_value **apArg)
166{
167 int res = 0; /* Assume false by default */
168 if( nArg > 0 ){
169 res = jx9_value_is_json_array(apArg[0]);
170 }
171 /* Query result */
172 jx9_result_bool(pCtx, res);
173 return JX9_OK;
174}
175/*
176 * bool is_object($var)
177 * Find out whether a variable is an object.
178 * Parameters
179 * $var: The variable being evaluated.
180 * Return
181 * True if var is an object. False otherwise.
182 */
183static int jx9Builtin_is_object(jx9_context *pCtx, int nArg, jx9_value **apArg)
184{
185 int res = 0; /* Assume false by default */
186 if( nArg > 0 ){
187 res = jx9_value_is_json_object(apArg[0]);
188 }
189 /* Query result */
190 jx9_result_bool(pCtx, res);
191 return JX9_OK;
192}
193/*
194 * bool is_resource($var)
195 * Find out whether a variable is a resource.
196 * Parameters
197 * $var: The variable being evaluated.
198 * Return
199 * True if a resource. False otherwise.
200 */
201static int jx9Builtin_is_resource(jx9_context *pCtx, int nArg, jx9_value **apArg)
202{
203 int res = 0; /* Assume false by default */
204 if( nArg > 0 ){
205 res = jx9_value_is_resource(apArg[0]);
206 }
207 jx9_result_bool(pCtx, res);
208 return JX9_OK;
209}
210/*
211 * float floatval($var)
212 * Get float value of a variable.
213 * Parameter
214 * $var: The variable being processed.
215 * Return
216 * the float value of a variable.
217 */
218static int jx9Builtin_floatval(jx9_context *pCtx, int nArg, jx9_value **apArg)
219{
220 if( nArg < 1 ){
221 /* return 0.0 */
222 jx9_result_double(pCtx, 0);
223 }else{
224 double dval;
225 /* Perform the cast */
226 dval = jx9_value_to_double(apArg[0]);
227 jx9_result_double(pCtx, dval);
228 }
229 return JX9_OK;
230}
231/*
232 * int intval($var)
233 * Get integer value of a variable.
234 * Parameter
235 * $var: The variable being processed.
236 * Return
237 * the int value of a variable.
238 */
239static int jx9Builtin_intval(jx9_context *pCtx, int nArg, jx9_value **apArg)
240{
241 if( nArg < 1 ){
242 /* return 0 */
243 jx9_result_int(pCtx, 0);
244 }else{
245 sxi64 iVal;
246 /* Perform the cast */
247 iVal = jx9_value_to_int64(apArg[0]);
248 jx9_result_int64(pCtx, iVal);
249 }
250 return JX9_OK;
251}
252/*
253 * string strval($var)
254 * Get the string representation of a variable.
255 * Parameter
256 * $var: The variable being processed.
257 * Return
258 * the string value of a variable.
259 */
260static int jx9Builtin_strval(jx9_context *pCtx, int nArg, jx9_value **apArg)
261{
262 if( nArg < 1 ){
263 /* return NULL */
264 jx9_result_null(pCtx);
265 }else{
266 const char *zVal;
267 int iLen = 0; /* cc -O6 warning */
268 /* Perform the cast */
269 zVal = jx9_value_to_string(apArg[0], &iLen);
270 jx9_result_string(pCtx, zVal, iLen);
271 }
272 return JX9_OK;
273}
274/*
275 * bool empty($var)
276 * Determine whether a variable is empty.
277 * Parameters
278 * $var: The variable being checked.
279 * Return
280 * 0 if var has a non-empty and non-zero value.1 otherwise.
281 */
282static int jx9Builtin_empty(jx9_context *pCtx, int nArg, jx9_value **apArg)
283{
284 int res = 1; /* Assume empty by default */
285 if( nArg > 0 ){
286 res = jx9_value_is_empty(apArg[0]);
287 }
288 jx9_result_bool(pCtx, res);
289 return JX9_OK;
290
291}
292#ifndef JX9_DISABLE_BUILTIN_FUNC
293#ifdef JX9_ENABLE_MATH_FUNC
294/*
295 * Section:
296 * Math Functions.
297 * Authors:
298 * Symisc Systems, devel@symisc.net.
299 * Copyright (C) Symisc Systems, http://jx9.symisc.net
300 * Status:
301 * Stable.
302 */
303#include <stdlib.h> /* abs */
304#include <math.h>
305/*
306 * float sqrt(float $arg )
307 * Square root of the given number.
308 * Parameter
309 * The number to process.
310 * Return
311 * The square root of arg or the special value Nan of failure.
312 */
313static int jx9Builtin_sqrt(jx9_context *pCtx, int nArg, jx9_value **apArg)
314{
315 double r, x;
316 if( nArg < 1 ){
317 /* Missing argument, return 0 */
318 jx9_result_int(pCtx, 0);
319 return JX9_OK;
320 }
321 x = jx9_value_to_double(apArg[0]);
322 /* Perform the requested operation */
323 r = sqrt(x);
324 /* store the result back */
325 jx9_result_double(pCtx, r);
326 return JX9_OK;
327}
328/*
329 * float exp(float $arg )
330 * Calculates the exponent of e.
331 * Parameter
332 * The number to process.
333 * Return
334 * 'e' raised to the power of arg.
335 */
336static int jx9Builtin_exp(jx9_context *pCtx, int nArg, jx9_value **apArg)
337{
338 double r, x;
339 if( nArg < 1 ){
340 /* Missing argument, return 0 */
341 jx9_result_int(pCtx, 0);
342 return JX9_OK;
343 }
344 x = jx9_value_to_double(apArg[0]);
345 /* Perform the requested operation */
346 r = exp(x);
347 /* store the result back */
348 jx9_result_double(pCtx, r);
349 return JX9_OK;
350}
351/*
352 * float floor(float $arg )
353 * Round fractions down.
354 * Parameter
355 * The number to process.
356 * Return
357 * Returns the next lowest integer value by rounding down value if necessary.
358 */
359static int jx9Builtin_floor(jx9_context *pCtx, int nArg, jx9_value **apArg)
360{
361 double r, x;
362 if( nArg < 1 ){
363 /* Missing argument, return 0 */
364 jx9_result_int(pCtx, 0);
365 return JX9_OK;
366 }
367 x = jx9_value_to_double(apArg[0]);
368 /* Perform the requested operation */
369 r = floor(x);
370 /* store the result back */
371 jx9_result_double(pCtx, r);
372 return JX9_OK;
373}
374/*
375 * float cos(float $arg )
376 * Cosine.
377 * Parameter
378 * The number to process.
379 * Return
380 * The cosine of arg.
381 */
382static int jx9Builtin_cos(jx9_context *pCtx, int nArg, jx9_value **apArg)
383{
384 double r, x;
385 if( nArg < 1 ){
386 /* Missing argument, return 0 */
387 jx9_result_int(pCtx, 0);
388 return JX9_OK;
389 }
390 x = jx9_value_to_double(apArg[0]);
391 /* Perform the requested operation */
392 r = cos(x);
393 /* store the result back */
394 jx9_result_double(pCtx, r);
395 return JX9_OK;
396}
397/*
398 * float acos(float $arg )
399 * Arc cosine.
400 * Parameter
401 * The number to process.
402 * Return
403 * The arc cosine of arg.
404 */
405static int jx9Builtin_acos(jx9_context *pCtx, int nArg, jx9_value **apArg)
406{
407 double r, x;
408 if( nArg < 1 ){
409 /* Missing argument, return 0 */
410 jx9_result_int(pCtx, 0);
411 return JX9_OK;
412 }
413 x = jx9_value_to_double(apArg[0]);
414 /* Perform the requested operation */
415 r = acos(x);
416 /* store the result back */
417 jx9_result_double(pCtx, r);
418 return JX9_OK;
419}
420/*
421 * float cosh(float $arg )
422 * Hyperbolic cosine.
423 * Parameter
424 * The number to process.
425 * Return
426 * The hyperbolic cosine of arg.
427 */
428static int jx9Builtin_cosh(jx9_context *pCtx, int nArg, jx9_value **apArg)
429{
430 double r, x;
431 if( nArg < 1 ){
432 /* Missing argument, return 0 */
433 jx9_result_int(pCtx, 0);
434 return JX9_OK;
435 }
436 x = jx9_value_to_double(apArg[0]);
437 /* Perform the requested operation */
438 r = cosh(x);
439 /* store the result back */
440 jx9_result_double(pCtx, r);
441 return JX9_OK;
442}
443/*
444 * float sin(float $arg )
445 * Sine.
446 * Parameter
447 * The number to process.
448 * Return
449 * The sine of arg.
450 */
451static int jx9Builtin_sin(jx9_context *pCtx, int nArg, jx9_value **apArg)
452{
453 double r, x;
454 if( nArg < 1 ){
455 /* Missing argument, return 0 */
456 jx9_result_int(pCtx, 0);
457 return JX9_OK;
458 }
459 x = jx9_value_to_double(apArg[0]);
460 /* Perform the requested operation */
461 r = sin(x);
462 /* store the result back */
463 jx9_result_double(pCtx, r);
464 return JX9_OK;
465}
466/*
467 * float asin(float $arg )
468 * Arc sine.
469 * Parameter
470 * The number to process.
471 * Return
472 * The arc sine of arg.
473 */
474static int jx9Builtin_asin(jx9_context *pCtx, int nArg, jx9_value **apArg)
475{
476 double r, x;
477 if( nArg < 1 ){
478 /* Missing argument, return 0 */
479 jx9_result_int(pCtx, 0);
480 return JX9_OK;
481 }
482 x = jx9_value_to_double(apArg[0]);
483 /* Perform the requested operation */
484 r = asin(x);
485 /* store the result back */
486 jx9_result_double(pCtx, r);
487 return JX9_OK;
488}
489/*
490 * float sinh(float $arg )
491 * Hyperbolic sine.
492 * Parameter
493 * The number to process.
494 * Return
495 * The hyperbolic sine of arg.
496 */
497static int jx9Builtin_sinh(jx9_context *pCtx, int nArg, jx9_value **apArg)
498{
499 double r, x;
500 if( nArg < 1 ){
501 /* Missing argument, return 0 */
502 jx9_result_int(pCtx, 0);
503 return JX9_OK;
504 }
505 x = jx9_value_to_double(apArg[0]);
506 /* Perform the requested operation */
507 r = sinh(x);
508 /* store the result back */
509 jx9_result_double(pCtx, r);
510 return JX9_OK;
511}
512/*
513 * float ceil(float $arg )
514 * Round fractions up.
515 * Parameter
516 * The number to process.
517 * Return
518 * The next highest integer value by rounding up value if necessary.
519 */
520static int jx9Builtin_ceil(jx9_context *pCtx, int nArg, jx9_value **apArg)
521{
522 double r, x;
523 if( nArg < 1 ){
524 /* Missing argument, return 0 */
525 jx9_result_int(pCtx, 0);
526 return JX9_OK;
527 }
528 x = jx9_value_to_double(apArg[0]);
529 /* Perform the requested operation */
530 r = ceil(x);
531 /* store the result back */
532 jx9_result_double(pCtx, r);
533 return JX9_OK;
534}
535/*
536 * float tan(float $arg )
537 * Tangent.
538 * Parameter
539 * The number to process.
540 * Return
541 * The tangent of arg.
542 */
543static int jx9Builtin_tan(jx9_context *pCtx, int nArg, jx9_value **apArg)
544{
545 double r, x;
546 if( nArg < 1 ){
547 /* Missing argument, return 0 */
548 jx9_result_int(pCtx, 0);
549 return JX9_OK;
550 }
551 x = jx9_value_to_double(apArg[0]);
552 /* Perform the requested operation */
553 r = tan(x);
554 /* store the result back */
555 jx9_result_double(pCtx, r);
556 return JX9_OK;
557}
558/*
559 * float atan(float $arg )
560 * Arc tangent.
561 * Parameter
562 * The number to process.
563 * Return
564 * The arc tangent of arg.
565 */
566static int jx9Builtin_atan(jx9_context *pCtx, int nArg, jx9_value **apArg)
567{
568 double r, x;
569 if( nArg < 1 ){
570 /* Missing argument, return 0 */
571 jx9_result_int(pCtx, 0);
572 return JX9_OK;
573 }
574 x = jx9_value_to_double(apArg[0]);
575 /* Perform the requested operation */
576 r = atan(x);
577 /* store the result back */
578 jx9_result_double(pCtx, r);
579 return JX9_OK;
580}
581/*
582 * float tanh(float $arg )
583 * Hyperbolic tangent.
584 * Parameter
585 * The number to process.
586 * Return
587 * The Hyperbolic tangent of arg.
588 */
589static int jx9Builtin_tanh(jx9_context *pCtx, int nArg, jx9_value **apArg)
590{
591 double r, x;
592 if( nArg < 1 ){
593 /* Missing argument, return 0 */
594 jx9_result_int(pCtx, 0);
595 return JX9_OK;
596 }
597 x = jx9_value_to_double(apArg[0]);
598 /* Perform the requested operation */
599 r = tanh(x);
600 /* store the result back */
601 jx9_result_double(pCtx, r);
602 return JX9_OK;
603}
604/*
605 * float atan2(float $y, float $x)
606 * Arc tangent of two variable.
607 * Parameter
608 * $y = Dividend parameter.
609 * $x = Divisor parameter.
610 * Return
611 * The arc tangent of y/x in radian.
612 */
613static int jx9Builtin_atan2(jx9_context *pCtx, int nArg, jx9_value **apArg)
614{
615 double r, x, y;
616 if( nArg < 2 ){
617 /* Missing arguments, return 0 */
618 jx9_result_int(pCtx, 0);
619 return JX9_OK;
620 }
621 y = jx9_value_to_double(apArg[0]);
622 x = jx9_value_to_double(apArg[1]);
623 /* Perform the requested operation */
624 r = atan2(y, x);
625 /* store the result back */
626 jx9_result_double(pCtx, r);
627 return JX9_OK;
628}
629/*
630 * float/int64 abs(float/int64 $arg )
631 * Absolute value.
632 * Parameter
633 * The number to process.
634 * Return
635 * The absolute value of number.
636 */
637static int jx9Builtin_abs(jx9_context *pCtx, int nArg, jx9_value **apArg)
638{
639 int is_float;
640 if( nArg < 1 ){
641 /* Missing argument, return 0 */
642 jx9_result_int(pCtx, 0);
643 return JX9_OK;
644 }
645 is_float = jx9_value_is_float(apArg[0]);
646 if( is_float ){
647 double r, x;
648 x = jx9_value_to_double(apArg[0]);
649 /* Perform the requested operation */
650 r = fabs(x);
651 jx9_result_double(pCtx, r);
652 }else{
653 int r, x;
654 x = jx9_value_to_int(apArg[0]);
655 /* Perform the requested operation */
656 r = abs(x);
657 jx9_result_int(pCtx, r);
658 }
659 return JX9_OK;
660}
661/*
662 * float log(float $arg, [int/float $base])
663 * Natural logarithm.
664 * Parameter
665 * $arg: The number to process.
666 * $base: The optional logarithmic base to use. (only base-10 is supported)
667 * Return
668 * The logarithm of arg to base, if given, or the natural logarithm.
669 * Note:
670 * only Natural log and base-10 log are supported.
671 */
672static int jx9Builtin_log(jx9_context *pCtx, int nArg, jx9_value **apArg)
673{
674 double r, x;
675 if( nArg < 1 ){
676 /* Missing argument, return 0 */
677 jx9_result_int(pCtx, 0);
678 return JX9_OK;
679 }
680 x = jx9_value_to_double(apArg[0]);
681 /* Perform the requested operation */
682 if( nArg == 2 && jx9_value_is_numeric(apArg[1]) && jx9_value_to_int(apArg[1]) == 10 ){
683 /* Base-10 log */
684 r = log10(x);
685 }else{
686 r = log(x);
687 }
688 /* store the result back */
689 jx9_result_double(pCtx, r);
690 return JX9_OK;
691}
692/*
693 * float log10(float $arg )
694 * Base-10 logarithm.
695 * Parameter
696 * The number to process.
697 * Return
698 * The Base-10 logarithm of the given number.
699 */
700static int jx9Builtin_log10(jx9_context *pCtx, int nArg, jx9_value **apArg)
701{
702 double r, x;
703 if( nArg < 1 ){
704 /* Missing argument, return 0 */
705 jx9_result_int(pCtx, 0);
706 return JX9_OK;
707 }
708 x = jx9_value_to_double(apArg[0]);
709 /* Perform the requested operation */
710 r = log10(x);
711 /* store the result back */
712 jx9_result_double(pCtx, r);
713 return JX9_OK;
714}
715/*
716 * number pow(number $base, number $exp)
717 * Exponential expression.
718 * Parameter
719 * base
720 * The base to use.
721 * exp
722 * The exponent.
723 * Return
724 * base raised to the power of exp.
725 * If the result can be represented as integer it will be returned
726 * as type integer, else it will be returned as type float.
727 */
728static int jx9Builtin_pow(jx9_context *pCtx, int nArg, jx9_value **apArg)
729{
730 double r, x, y;
731 if( nArg < 1 ){
732 /* Missing argument, return 0 */
733 jx9_result_int(pCtx, 0);
734 return JX9_OK;
735 }
736 x = jx9_value_to_double(apArg[0]);
737 y = jx9_value_to_double(apArg[1]);
738 /* Perform the requested operation */
739 r = pow(x, y);
740 jx9_result_double(pCtx, r);
741 return JX9_OK;
742}
743/*
744 * float pi(void)
745 * Returns an approximation of pi.
746 * Note
747 * you can use the M_PI constant which yields identical results to pi().
748 * Return
749 * The value of pi as float.
750 */
751static int jx9Builtin_pi(jx9_context *pCtx, int nArg, jx9_value **apArg)
752{
753 SXUNUSED(nArg); /* cc warning */
754 SXUNUSED(apArg);
755 jx9_result_double(pCtx, JX9_PI);
756 return JX9_OK;
757}
758/*
759 * float fmod(float $x, float $y)
760 * Returns the floating point remainder (modulo) of the division of the arguments.
761 * Parameters
762 * $x
763 * The dividend
764 * $y
765 * The divisor
766 * Return
767 * The floating point remainder of x/y.
768 */
769static int jx9Builtin_fmod(jx9_context *pCtx, int nArg, jx9_value **apArg)
770{
771 double x, y, r;
772 if( nArg < 2 ){
773 /* Missing arguments */
774 jx9_result_double(pCtx, 0);
775 return JX9_OK;
776 }
777 /* Extract given arguments */
778 x = jx9_value_to_double(apArg[0]);
779 y = jx9_value_to_double(apArg[1]);
780 /* Perform the requested operation */
781 r = fmod(x, y);
782 /* Processing result */
783 jx9_result_double(pCtx, r);
784 return JX9_OK;
785}
786/*
787 * float hypot(float $x, float $y)
788 * Calculate the length of the hypotenuse of a right-angle triangle .
789 * Parameters
790 * $x
791 * Length of first side
792 * $y
793 * Length of first side
794 * Return
795 * Calculated length of the hypotenuse.
796 */
797static int jx9Builtin_hypot(jx9_context *pCtx, int nArg, jx9_value **apArg)
798{
799 double x, y, r;
800 if( nArg < 2 ){
801 /* Missing arguments */
802 jx9_result_double(pCtx, 0);
803 return JX9_OK;
804 }
805 /* Extract given arguments */
806 x = jx9_value_to_double(apArg[0]);
807 y = jx9_value_to_double(apArg[1]);
808 /* Perform the requested operation */
809 r = hypot(x, y);
810 /* Processing result */
811 jx9_result_double(pCtx, r);
812 return JX9_OK;
813}
814#endif /* JX9_ENABLE_MATH_FUNC */
815/*
816 * float round ( float $val [, int $precision = 0 [, int $mode = JX9_ROUND_HALF_UP ]] )
817 * Exponential expression.
818 * Parameter
819 * $val
820 * The value to round.
821 * $precision
822 * The optional number of decimal digits to round to.
823 * $mode
824 * One of JX9_ROUND_HALF_UP, JX9_ROUND_HALF_DOWN, JX9_ROUND_HALF_EVEN, or JX9_ROUND_HALF_ODD.
825 * (not supported).
826 * Return
827 * The rounded value.
828 */
829static int jx9Builtin_round(jx9_context *pCtx, int nArg, jx9_value **apArg)
830{
831 int n = 0;
832 double r;
833 if( nArg < 1 ){
834 /* Missing argument, return 0 */
835 jx9_result_int(pCtx, 0);
836 return JX9_OK;
837 }
838 /* Extract the precision if available */
839 if( nArg > 1 ){
840 n = jx9_value_to_int(apArg[1]);
841 if( n>30 ){
842 n = 30;
843 }
844 if( n<0 ){
845 n = 0;
846 }
847 }
848 r = jx9_value_to_double(apArg[0]);
849 /* If Y==0 and X will fit in a 64-bit int,
850 * handle the rounding directly.Otherwise
851 * use our own cutsom printf [i.e:SyBufferFormat()].
852 */
853 if( n==0 && r>=0 && r<LARGEST_INT64-1 ){
854 r = (double)((jx9_int64)(r+0.5));
855 }else if( n==0 && r<0 && (-r)<LARGEST_INT64-1 ){
856 r = -(double)((jx9_int64)((-r)+0.5));
857 }else{
858 char zBuf[256];
859 sxu32 nLen;
860 nLen = SyBufferFormat(zBuf, sizeof(zBuf), "%.*f", n, r);
861 /* Convert the string to real number */
862 SyStrToReal(zBuf, nLen, (void *)&r, 0);
863 }
864 /* Return thr rounded value */
865 jx9_result_double(pCtx, r);
866 return JX9_OK;
867}
868/*
869 * string dechex(int $number)
870 * Decimal to hexadecimal.
871 * Parameters
872 * $number
873 * Decimal value to convert
874 * Return
875 * Hexadecimal string representation of number
876 */
877static int jx9Builtin_dechex(jx9_context *pCtx, int nArg, jx9_value **apArg)
878{
879 int iVal;
880 if( nArg < 1 ){
881 /* Missing arguments, return null */
882 jx9_result_null(pCtx);
883 return JX9_OK;
884 }
885 /* Extract the given number */
886 iVal = jx9_value_to_int(apArg[0]);
887 /* Format */
888 jx9_result_string_format(pCtx, "%x", iVal);
889 return JX9_OK;
890}
891/*
892 * string decoct(int $number)
893 * Decimal to Octal.
894 * Parameters
895 * $number
896 * Decimal value to convert
897 * Return
898 * Octal string representation of number
899 */
900static int jx9Builtin_decoct(jx9_context *pCtx, int nArg, jx9_value **apArg)
901{
902 int iVal;
903 if( nArg < 1 ){
904 /* Missing arguments, return null */
905 jx9_result_null(pCtx);
906 return JX9_OK;
907 }
908 /* Extract the given number */
909 iVal = jx9_value_to_int(apArg[0]);
910 /* Format */
911 jx9_result_string_format(pCtx, "%o", iVal);
912 return JX9_OK;
913}
914/*
915 * string decbin(int $number)
916 * Decimal to binary.
917 * Parameters
918 * $number
919 * Decimal value to convert
920 * Return
921 * Binary string representation of number
922 */
923static int jx9Builtin_decbin(jx9_context *pCtx, int nArg, jx9_value **apArg)
924{
925 int iVal;
926 if( nArg < 1 ){
927 /* Missing arguments, return null */
928 jx9_result_null(pCtx);
929 return JX9_OK;
930 }
931 /* Extract the given number */
932 iVal = jx9_value_to_int(apArg[0]);
933 /* Format */
934 jx9_result_string_format(pCtx, "%B", iVal);
935 return JX9_OK;
936}
937/*
938 * int64 hexdec(string $hex_string)
939 * Hexadecimal to decimal.
940 * Parameters
941 * $hex_string
942 * The hexadecimal string to convert
943 * Return
944 * The decimal representation of hex_string
945 */
946static int jx9Builtin_hexdec(jx9_context *pCtx, int nArg, jx9_value **apArg)
947{
948 const char *zString, *zEnd;
949 jx9_int64 iVal;
950 int nLen;
951 if( nArg < 1 ){
952 /* Missing arguments, return -1 */
953 jx9_result_int(pCtx, -1);
954 return JX9_OK;
955 }
956 iVal = 0;
957 if( jx9_value_is_string(apArg[0]) ){
958 /* Extract the given string */
959 zString = jx9_value_to_string(apArg[0], &nLen);
960 /* Delimit the string */
961 zEnd = &zString[nLen];
962 /* Ignore non hex-stream */
963 while( zString < zEnd ){
964 if( (unsigned char)zString[0] >= 0xc0 ){
965 /* UTF-8 stream */
966 zString++;
967 while( zString < zEnd && (((unsigned char)zString[0] & 0xc0) == 0x80) ){
968 zString++;
969 }
970 }else{
971 if( SyisHex(zString[0]) ){
972 break;
973 }
974 /* Ignore */
975 zString++;
976 }
977 }
978 if( zString < zEnd ){
979 /* Cast */
980 SyHexStrToInt64(zString, (sxu32)(zEnd-zString), (void *)&iVal, 0);
981 }
982 }else{
983 /* Extract as a 64-bit integer */
984 iVal = jx9_value_to_int64(apArg[0]);
985 }
986 /* Return the number */
987 jx9_result_int64(pCtx, iVal);
988 return JX9_OK;
989}
990/*
991 * int64 bindec(string $bin_string)
992 * Binary to decimal.
993 * Parameters
994 * $bin_string
995 * The binary string to convert
996 * Return
997 * Returns the decimal equivalent of the binary number represented by the binary_string argument.
998 */
999static int jx9Builtin_bindec(jx9_context *pCtx, int nArg, jx9_value **apArg)
1000{
1001 const char *zString;
1002 jx9_int64 iVal;
1003 int nLen;
1004 if( nArg < 1 ){
1005 /* Missing arguments, return -1 */
1006 jx9_result_int(pCtx, -1);
1007 return JX9_OK;
1008 }
1009 iVal = 0;
1010 if( jx9_value_is_string(apArg[0]) ){
1011 /* Extract the given string */
1012 zString = jx9_value_to_string(apArg[0], &nLen);
1013 if( nLen > 0 ){
1014 /* Perform a binary cast */
1015 SyBinaryStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0);
1016 }
1017 }else{
1018 /* Extract as a 64-bit integer */
1019 iVal = jx9_value_to_int64(apArg[0]);
1020 }
1021 /* Return the number */
1022 jx9_result_int64(pCtx, iVal);
1023 return JX9_OK;
1024}
1025/*
1026 * int64 octdec(string $oct_string)
1027 * Octal to decimal.
1028 * Parameters
1029 * $oct_string
1030 * The octal string to convert
1031 * Return
1032 * Returns the decimal equivalent of the octal number represented by the octal_string argument.
1033 */
1034static int jx9Builtin_octdec(jx9_context *pCtx, int nArg, jx9_value **apArg)
1035{
1036 const char *zString;
1037 jx9_int64 iVal;
1038 int nLen;
1039 if( nArg < 1 ){
1040 /* Missing arguments, return -1 */
1041 jx9_result_int(pCtx, -1);
1042 return JX9_OK;
1043 }
1044 iVal = 0;
1045 if( jx9_value_is_string(apArg[0]) ){
1046 /* Extract the given string */
1047 zString = jx9_value_to_string(apArg[0], &nLen);
1048 if( nLen > 0 ){
1049 /* Perform the cast */
1050 SyOctalStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0);
1051 }
1052 }else{
1053 /* Extract as a 64-bit integer */
1054 iVal = jx9_value_to_int64(apArg[0]);
1055 }
1056 /* Return the number */
1057 jx9_result_int64(pCtx, iVal);
1058 return JX9_OK;
1059}
1060/*
1061 * string base_convert(string $number, int $frombase, int $tobase)
1062 * Convert a number between arbitrary bases.
1063 * Parameters
1064 * $number
1065 * The number to convert
1066 * $frombase
1067 * The base number is in
1068 * $tobase
1069 * The base to convert number to
1070 * Return
1071 * Number converted to base tobase
1072 */
1073static int jx9Builtin_base_convert(jx9_context *pCtx, int nArg, jx9_value **apArg)
1074{
1075 int nLen, iFbase, iTobase;
1076 const char *zNum;
1077 jx9_int64 iNum;
1078 if( nArg < 3 ){
1079 /* Return the empty string*/
1080 jx9_result_string(pCtx, "", 0);
1081 return JX9_OK;
1082 }
1083 /* Base numbers */
1084 iFbase = jx9_value_to_int(apArg[1]);
1085 iTobase = jx9_value_to_int(apArg[2]);
1086 if( jx9_value_is_string(apArg[0]) ){
1087 /* Extract the target number */
1088 zNum = jx9_value_to_string(apArg[0], &nLen);
1089 if( nLen < 1 ){
1090 /* Return the empty string*/
1091 jx9_result_string(pCtx, "", 0);
1092 return JX9_OK;
1093 }
1094 /* Base conversion */
1095 switch(iFbase){
1096 case 16:
1097 /* Hex */
1098 SyHexStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
1099 break;
1100 case 8:
1101 /* Octal */
1102 SyOctalStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
1103 break;
1104 case 2:
1105 /* Binary */
1106 SyBinaryStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
1107 break;
1108 default:
1109 /* Decimal */
1110 SyStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
1111 break;
1112 }
1113 }else{
1114 iNum = jx9_value_to_int64(apArg[0]);
1115 }
1116 switch(iTobase){
1117 case 16:
1118 /* Hex */
1119 jx9_result_string_format(pCtx, "%qx", iNum); /* Quad hex */
1120 break;
1121 case 8:
1122 /* Octal */
1123 jx9_result_string_format(pCtx, "%qo", iNum); /* Quad octal */
1124 break;
1125 case 2:
1126 /* Binary */
1127 jx9_result_string_format(pCtx, "%qB", iNum); /* Quad binary */
1128 break;
1129 default:
1130 /* Decimal */
1131 jx9_result_string_format(pCtx, "%qd", iNum); /* Quad decimal */
1132 break;
1133 }
1134 return JX9_OK;
1135}
1136/*
1137 * Section:
1138 * String handling Functions.
1139 * Authors:
1140 * Symisc Systems, devel@symisc.net.
1141 * Copyright (C) Symisc Systems, http://jx9.symisc.net
1142 * Status:
1143 * Stable.
1144 */
1145/*
1146 * string substr(string $string, int $start[, int $length ])
1147 * Return part of a string.
1148 * Parameters
1149 * $string
1150 * The input string. Must be one character or longer.
1151 * $start
1152 * If start is non-negative, the returned string will start at the start'th position
1153 * in string, counting from zero. For instance, in the string 'abcdef', the character
1154 * at position 0 is 'a', the character at position 2 is 'c', and so forth.
1155 * If start is negative, the returned string will start at the start'th character
1156 * from the end of string.
1157 * If string is less than or equal to start characters long, FALSE will be returned.
1158 * $length
1159 * If length is given and is positive, the string returned will contain at most length
1160 * characters beginning from start (depending on the length of string).
1161 * If length is given and is negative, then that many characters will be omitted from
1162 * the end of string (after the start position has been calculated when a start is negative).
1163 * If start denotes the position of this truncation or beyond, false will be returned.
1164 * If length is given and is 0, FALSE or NULL an empty string will be returned.
1165 * If length is omitted, the substring starting from start until the end of the string
1166 * will be returned.
1167 * Return
1168 * Returns the extracted part of string, or FALSE on failure or an empty string.
1169 */
1170static int jx9Builtin_substr(jx9_context *pCtx, int nArg, jx9_value **apArg)
1171{
1172 const char *zSource, *zOfft;
1173 int nOfft, nLen, nSrcLen;
1174 if( nArg < 2 ){
1175 /* return FALSE */
1176 jx9_result_bool(pCtx, 0);
1177 return JX9_OK;
1178 }
1179 /* Extract the target string */
1180 zSource = jx9_value_to_string(apArg[0], &nSrcLen);
1181 if( nSrcLen < 1 ){
1182 /* Empty string, return FALSE */
1183 jx9_result_bool(pCtx, 0);
1184 return JX9_OK;
1185 }
1186 nLen = nSrcLen; /* cc warning */
1187 /* Extract the offset */
1188 nOfft = jx9_value_to_int(apArg[1]);
1189 if( nOfft < 0 ){
1190 zOfft = &zSource[nSrcLen+nOfft];
1191 if( zOfft < zSource ){
1192 /* Invalid offset */
1193 jx9_result_bool(pCtx, 0);
1194 return JX9_OK;
1195 }
1196 nLen = (int)(&zSource[nSrcLen]-zOfft);
1197 nOfft = (int)(zOfft-zSource);
1198 }else if( nOfft >= nSrcLen ){
1199 /* Invalid offset */
1200 jx9_result_bool(pCtx, 0);
1201 return JX9_OK;
1202 }else{
1203 zOfft = &zSource[nOfft];
1204 nLen = nSrcLen - nOfft;
1205 }
1206 if( nArg > 2 ){
1207 /* Extract the length */
1208 nLen = jx9_value_to_int(apArg[2]);
1209 if( nLen == 0 ){
1210 /* Invalid length, return an empty string */
1211 jx9_result_string(pCtx, "", 0);
1212 return JX9_OK;
1213 }else if( nLen < 0 ){
1214 nLen = nSrcLen + nLen - nOfft;
1215 if( nLen < 1 ){
1216 /* Invalid length */
1217 nLen = nSrcLen - nOfft;
1218 }
1219 }
1220 if( nLen + nOfft > nSrcLen ){
1221 /* Invalid length */
1222 nLen = nSrcLen - nOfft;
1223 }
1224 }
1225 /* Return the substring */
1226 jx9_result_string(pCtx, zOfft, nLen);
1227 return JX9_OK;
1228}
1229/*
1230 * int substr_compare(string $main_str, string $str , int $offset[, int $length[, bool $case_insensitivity = false ]])
1231 * Binary safe comparison of two strings from an offset, up to length characters.
1232 * Parameters
1233 * $main_str
1234 * The main string being compared.
1235 * $str
1236 * The secondary string being compared.
1237 * $offset
1238 * The start position for the comparison. If negative, it starts counting from
1239 * the end of the string.
1240 * $length
1241 * The length of the comparison. The default value is the largest of the length
1242 * of the str compared to the length of main_str less the offset.
1243 * $case_insensitivity
1244 * If case_insensitivity is TRUE, comparison is case insensitive.
1245 * Return
1246 * Returns < 0 if main_str from position offset is less than str, > 0 if it is greater than
1247 * str, and 0 if they are equal. If offset is equal to or greater than the length of main_str
1248 * or length is set and is less than 1, substr_compare() prints a warning and returns FALSE.
1249 */
1250static int jx9Builtin_substr_compare(jx9_context *pCtx, int nArg, jx9_value **apArg)
1251{
1252 const char *zSource, *zOfft, *zSub;
1253 int nOfft, nLen, nSrcLen, nSublen;
1254 int iCase = 0;
1255 int rc;
1256 if( nArg < 3 ){
1257 /* Missing arguments, return FALSE */
1258 jx9_result_bool(pCtx, 0);
1259 return JX9_OK;
1260 }
1261 /* Extract the target string */
1262 zSource = jx9_value_to_string(apArg[0], &nSrcLen);
1263 if( nSrcLen < 1 ){
1264 /* Empty string, return FALSE */
1265 jx9_result_bool(pCtx, 0);
1266 return JX9_OK;
1267 }
1268 nLen = nSrcLen; /* cc warning */
1269 /* Extract the substring */
1270 zSub = jx9_value_to_string(apArg[1], &nSublen);
1271 if( nSublen < 1 || nSublen > nSrcLen){
1272 /* Empty string, return FALSE */
1273 jx9_result_bool(pCtx, 0);
1274 return JX9_OK;
1275 }
1276 /* Extract the offset */
1277 nOfft = jx9_value_to_int(apArg[2]);
1278 if( nOfft < 0 ){
1279 zOfft = &zSource[nSrcLen+nOfft];
1280 if( zOfft < zSource ){
1281 /* Invalid offset */
1282 jx9_result_bool(pCtx, 0);
1283 return JX9_OK;
1284 }
1285 nLen = (int)(&zSource[nSrcLen]-zOfft);
1286 nOfft = (int)(zOfft-zSource);
1287 }else if( nOfft >= nSrcLen ){
1288 /* Invalid offset */
1289 jx9_result_bool(pCtx, 0);
1290 return JX9_OK;
1291 }else{
1292 zOfft = &zSource[nOfft];
1293 nLen = nSrcLen - nOfft;
1294 }
1295 if( nArg > 3 ){
1296 /* Extract the length */
1297 nLen = jx9_value_to_int(apArg[3]);
1298 if( nLen < 1 ){
1299 /* Invalid length */
1300 jx9_result_int(pCtx, 1);
1301 return JX9_OK;
1302 }else if( nLen + nOfft > nSrcLen ){
1303 /* Invalid length */
1304 nLen = nSrcLen - nOfft;
1305 }
1306 if( nArg > 4 ){
1307 /* Case-sensitive or not */
1308 iCase = jx9_value_to_bool(apArg[4]);
1309 }
1310 }
1311 /* Perform the comparison */
1312 if( iCase ){
1313 rc = SyStrnicmp(zOfft, zSub, (sxu32)nLen);
1314 }else{
1315 rc = SyStrncmp(zOfft, zSub, (sxu32)nLen);
1316 }
1317 /* Comparison result */
1318 jx9_result_int(pCtx, rc);
1319 return JX9_OK;
1320}
1321/*
1322 * int substr_count(string $haystack, string $needle[, int $offset = 0 [, int $length ]])
1323 * Count the number of substring occurrences.
1324 * Parameters
1325 * $haystack
1326 * The string to search in
1327 * $needle
1328 * The substring to search for
1329 * $offset
1330 * The offset where to start counting
1331 * $length (NOT USED)
1332 * The maximum length after the specified offset to search for the substring.
1333 * It outputs a warning if the offset plus the length is greater than the haystack length.
1334 * Return
1335 * Toral number of substring occurrences.
1336 */
1337static int jx9Builtin_substr_count(jx9_context *pCtx, int nArg, jx9_value **apArg)
1338{
1339 const char *zText, *zPattern, *zEnd;
1340 int nTextlen, nPatlen;
1341 int iCount = 0;
1342 sxu32 nOfft;
1343 sxi32 rc;
1344 if( nArg < 2 ){
1345 /* Missing arguments */
1346 jx9_result_int(pCtx, 0);
1347 return JX9_OK;
1348 }
1349 /* Point to the haystack */
1350 zText = jx9_value_to_string(apArg[0], &nTextlen);
1351 /* Point to the neddle */
1352 zPattern = jx9_value_to_string(apArg[1], &nPatlen);
1353 if( nTextlen < 1 || nPatlen < 1 || nPatlen > nTextlen ){
1354 /* NOOP, return zero */
1355 jx9_result_int(pCtx, 0);
1356 return JX9_OK;
1357 }
1358 if( nArg > 2 ){
1359 int nOfft;
1360 /* Extract the offset */
1361 nOfft = jx9_value_to_int(apArg[2]);
1362 if( nOfft < 0 || nOfft > nTextlen ){
1363 /* Invalid offset, return zero */
1364 jx9_result_int(pCtx, 0);
1365 return JX9_OK;
1366 }
1367 /* Point to the desired offset */
1368 zText = &zText[nOfft];
1369 /* Adjust length */
1370 nTextlen -= nOfft;
1371 }
1372 /* Point to the end of the string */
1373 zEnd = &zText[nTextlen];
1374 if( nArg > 3 ){
1375 int nLen;
1376 /* Extract the length */
1377 nLen = jx9_value_to_int(apArg[3]);
1378 if( nLen < 0 || nLen > nTextlen ){
1379 /* Invalid length, return 0 */
1380 jx9_result_int(pCtx, 0);
1381 return JX9_OK;
1382 }
1383 /* Adjust pointer */
1384 nTextlen = nLen;
1385 zEnd = &zText[nTextlen];
1386 }
1387 /* Perform the search */
1388 for(;;){
1389 rc = SyBlobSearch((const void *)zText, (sxu32)(zEnd-zText), (const void *)zPattern, nPatlen, &nOfft);
1390 if( rc != SXRET_OK ){
1391 /* Pattern not found, break immediately */
1392 break;
1393 }
1394 /* Increment counter and update the offset */
1395 iCount++;
1396 zText += nOfft + nPatlen;
1397 if( zText >= zEnd ){
1398 break;
1399 }
1400 }
1401 /* Pattern count */
1402 jx9_result_int(pCtx, iCount);
1403 return JX9_OK;
1404}
1405/*
1406 * string chunk_split(string $body[, int $chunklen = 76 [, string $end = "\r\n" ]])
1407 * Split a string into smaller chunks.
1408 * Parameters
1409 * $body
1410 * The string to be chunked.
1411 * $chunklen
1412 * The chunk length.
1413 * $end
1414 * The line ending sequence.
1415 * Return
1416 * The chunked string or NULL on failure.
1417 */
1418static int jx9Builtin_chunk_split(jx9_context *pCtx, int nArg, jx9_value **apArg)
1419{
1420 const char *zIn, *zEnd, *zSep = "\r\n";
1421 int nSepLen, nChunkLen, nLen;
1422 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1423 /* Nothing to split, return null */
1424 jx9_result_null(pCtx);
1425 return JX9_OK;
1426 }
1427 /* initialize/Extract arguments */
1428 nSepLen = (int)sizeof("\r\n") - 1;
1429 nChunkLen = 76;
1430 zIn = jx9_value_to_string(apArg[0], &nLen);
1431 zEnd = &zIn[nLen];
1432 if( nArg > 1 ){
1433 /* Chunk length */
1434 nChunkLen = jx9_value_to_int(apArg[1]);
1435 if( nChunkLen < 1 ){
1436 /* Switch back to the default length */
1437 nChunkLen = 76;
1438 }
1439 if( nArg > 2 ){
1440 /* Separator */
1441 zSep = jx9_value_to_string(apArg[2], &nSepLen);
1442 if( nSepLen < 1 ){
1443 /* Switch back to the default separator */
1444 zSep = "\r\n";
1445 nSepLen = (int)sizeof("\r\n") - 1;
1446 }
1447 }
1448 }
1449 /* Perform the requested operation */
1450 if( nChunkLen > nLen ){
1451 /* Nothing to split, return the string and the separator */
1452 jx9_result_string_format(pCtx, "%.*s%.*s", nLen, zIn, nSepLen, zSep);
1453 return JX9_OK;
1454 }
1455 while( zIn < zEnd ){
1456 if( nChunkLen > (int)(zEnd-zIn) ){
1457 nChunkLen = (int)(zEnd - zIn);
1458 }
1459 /* Append the chunk and the separator */
1460 jx9_result_string_format(pCtx, "%.*s%.*s", nChunkLen, zIn, nSepLen, zSep);
1461 /* Point beyond the chunk */
1462 zIn += nChunkLen;
1463 }
1464 return JX9_OK;
1465}
1466/*
1467 * string htmlspecialchars(string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $charset]])
1468 * HTML escaping of special characters.
1469 * The translations performed are:
1470 * '&' (ampersand) ==> '&amp;'
1471 * '"' (double quote) ==> '&quot;' when ENT_NOQUOTES is not set.
1472 * "'" (single quote) ==> '&#039;' only when ENT_QUOTES is set.
1473 * '<' (less than) ==> '&lt;'
1474 * '>' (greater than) ==> '&gt;'
1475 * Parameters
1476 * $string
1477 * The string being converted.
1478 * $flags
1479 * A bitmask of one or more of the following flags, which specify how to handle quotes.
1480 * The default is ENT_COMPAT | ENT_HTML401.
1481 * ENT_COMPAT Will convert double-quotes and leave single-quotes alone.
1482 * ENT_QUOTES Will convert both double and single quotes.
1483 * ENT_NOQUOTES Will leave both double and single quotes unconverted.
1484 * ENT_IGNORE Silently discard invalid code unit sequences instead of returning an empty string.
1485 * $charset
1486 * Defines character set used in conversion. The default character set is ISO-8859-1. (Not used)
1487 * Return
1488 * The escaped string or NULL on failure.
1489 */
1490static int jx9Builtin_htmlspecialchars(jx9_context *pCtx, int nArg, jx9_value **apArg)
1491{
1492 const char *zCur, *zIn, *zEnd;
1493 int iFlags = 0x01|0x40; /* ENT_COMPAT | ENT_HTML401 */
1494 int nLen, c;
1495 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1496 /* Missing/Invalid arguments, return NULL */
1497 jx9_result_null(pCtx);
1498 return JX9_OK;
1499 }
1500 /* Extract the target string */
1501 zIn = jx9_value_to_string(apArg[0], &nLen);
1502 zEnd = &zIn[nLen];
1503 /* Extract the flags if available */
1504 if( nArg > 1 ){
1505 iFlags = jx9_value_to_int(apArg[1]);
1506 if( iFlags < 0 ){
1507 iFlags = 0x01|0x40;
1508 }
1509 }
1510 /* Perform the requested operation */
1511 for(;;){
1512 if( zIn >= zEnd ){
1513 break;
1514 }
1515 zCur = zIn;
1516 while( zIn < zEnd && zIn[0] != '&' && zIn[0] != '\'' && zIn[0] != '"' && zIn[0] != '<' && zIn[0] != '>' ){
1517 zIn++;
1518 }
1519 if( zCur < zIn ){
1520 /* Append the raw string verbatim */
1521 jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
1522 }
1523 if( zIn >= zEnd ){
1524 break;
1525 }
1526 c = zIn[0];
1527 if( c == '&' ){
1528 /* Expand '&amp;' */
1529 jx9_result_string(pCtx, "&amp;", (int)sizeof("&amp;")-1);
1530 }else if( c == '<' ){
1531 /* Expand '&lt;' */
1532 jx9_result_string(pCtx, "&lt;", (int)sizeof("&lt;")-1);
1533 }else if( c == '>' ){
1534 /* Expand '&gt;' */
1535 jx9_result_string(pCtx, "&gt;", (int)sizeof("&gt;")-1);
1536 }else if( c == '\'' ){
1537 if( iFlags & 0x02 /*ENT_QUOTES*/ ){
1538 /* Expand '&#039;' */
1539 jx9_result_string(pCtx, "&#039;", (int)sizeof("&#039;")-1);
1540 }else{
1541 /* Leave the single quote untouched */
1542 jx9_result_string(pCtx, "'", (int)sizeof(char));
1543 }
1544 }else if( c == '"' ){
1545 if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){
1546 /* Expand '&quot;' */
1547 jx9_result_string(pCtx, "&quot;", (int)sizeof("&quot;")-1);
1548 }else{
1549 /* Leave the double quote untouched */
1550 jx9_result_string(pCtx, "\"", (int)sizeof(char));
1551 }
1552 }
1553 /* Ignore the unsafe HTML character */
1554 zIn++;
1555 }
1556 return JX9_OK;
1557}
1558/*
1559 * string htmlspecialchars_decode(string $string[, int $quote_style = ENT_COMPAT ])
1560 * Unescape HTML entities.
1561 * Parameters
1562 * $string
1563 * The string to decode
1564 * $quote_style
1565 * The quote style. One of the following constants:
1566 * ENT_COMPAT Will convert double-quotes and leave single-quotes alone (default)
1567 * ENT_QUOTES Will convert both double and single quotes
1568 * ENT_NOQUOTES Will leave both double and single quotes unconverted
1569 * Return
1570 * The unescaped string or NULL on failure.
1571 */
1572static int jx9Builtin_htmlspecialchars_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
1573{
1574 const char *zCur, *zIn, *zEnd;
1575 int iFlags = 0x01; /* ENT_COMPAT */
1576 int nLen, nJump;
1577 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1578 /* Missing/Invalid arguments, return NULL */
1579 jx9_result_null(pCtx);
1580 return JX9_OK;
1581 }
1582 /* Extract the target string */
1583 zIn = jx9_value_to_string(apArg[0], &nLen);
1584 zEnd = &zIn[nLen];
1585 /* Extract the flags if available */
1586 if( nArg > 1 ){
1587 iFlags = jx9_value_to_int(apArg[1]);
1588 if( iFlags < 0 ){
1589 iFlags = 0x01;
1590 }
1591 }
1592 /* Perform the requested operation */
1593 for(;;){
1594 if( zIn >= zEnd ){
1595 break;
1596 }
1597 zCur = zIn;
1598 while( zIn < zEnd && zIn[0] != '&' ){
1599 zIn++;
1600 }
1601 if( zCur < zIn ){
1602 /* Append the raw string verbatim */
1603 jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
1604 }
1605 nLen = (int)(zEnd-zIn);
1606 nJump = (int)sizeof(char);
1607 if( nLen >= (int)sizeof("&amp;")-1 && SyStrnicmp(zIn, "&amp;", sizeof("&amp;")-1) == 0 ){
1608 /* &amp; ==> '&' */
1609 jx9_result_string(pCtx, "&", (int)sizeof(char));
1610 nJump = (int)sizeof("&amp;")-1;
1611 }else if( nLen >= (int)sizeof("&lt;")-1 && SyStrnicmp(zIn, "&lt;", sizeof("&lt;")-1) == 0 ){
1612 /* &lt; ==> < */
1613 jx9_result_string(pCtx, "<", (int)sizeof(char));
1614 nJump = (int)sizeof("&lt;")-1;
1615 }else if( nLen >= (int)sizeof("&gt;")-1 && SyStrnicmp(zIn, "&gt;", sizeof("&gt;")-1) == 0 ){
1616 /* &gt; ==> '>' */
1617 jx9_result_string(pCtx, ">", (int)sizeof(char));
1618 nJump = (int)sizeof("&gt;")-1;
1619 }else if( nLen >= (int)sizeof("&quot;")-1 && SyStrnicmp(zIn, "&quot;", sizeof("&quot;")-1) == 0 ){
1620 /* &quot; ==> '"' */
1621 if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){
1622 jx9_result_string(pCtx, "\"", (int)sizeof(char));
1623 }else{
1624 /* Leave untouched */
1625 jx9_result_string(pCtx, "&quot;", (int)sizeof("&quot;")-1);
1626 }
1627 nJump = (int)sizeof("&quot;")-1;
1628 }else if( nLen >= (int)sizeof("&#039;")-1 && SyStrnicmp(zIn, "&#039;", sizeof("&#039;")-1) == 0 ){
1629 /* &#039; ==> ''' */
1630 if( iFlags & 0x02 /*ENT_QUOTES*/ ){
1631 /* Expand ''' */
1632 jx9_result_string(pCtx, "'", (int)sizeof(char));
1633 }else{
1634 /* Leave untouched */
1635 jx9_result_string(pCtx, "&#039;", (int)sizeof("&#039;")-1);
1636 }
1637 nJump = (int)sizeof("&#039;")-1;
1638 }else if( nLen >= (int)sizeof(char) ){
1639 /* expand '&' */
1640 jx9_result_string(pCtx, "&", (int)sizeof(char));
1641 }else{
1642 /* No more input to process */
1643 break;
1644 }
1645 zIn += nJump;
1646 }
1647 return JX9_OK;
1648}
1649/* HTML encoding/Decoding table
1650 * Source: Symisc RunTime API.[chm@symisc.net]
1651 */
1652static const char *azHtmlEscape[] = {
1653 "&lt;", "<", "&gt;", ">", "&amp;", "&", "&quot;", "\"", "&#39;", "'",
1654 "&#33;", "!", "&#36;", "$", "&#35;", "#", "&#37;", "%", "&#40;", "(",
1655 "&#41;", ")", "&#123;", "{", "&#125;", "}", "&#61;", "=", "&#43;", "+",
1656 "&#63;", "?", "&#91;", "[", "&#93;", "]", "&#64;", "@", "&#44;", ","
1657 };
1658/*
1659 * array get_html_translation_table(void)
1660 * Returns the translation table used by htmlspecialchars() and htmlentities().
1661 * Parameters
1662 * None
1663 * Return
1664 * The translation table as an array or NULL on failure.
1665 */
1666static int jx9Builtin_get_html_translation_table(jx9_context *pCtx, int nArg, jx9_value **apArg)
1667{
1668 jx9_value *pArray, *pValue;
1669 sxu32 n;
1670 /* Element value */
1671 pValue = jx9_context_new_scalar(pCtx);
1672 if( pValue == 0 ){
1673 SXUNUSED(nArg); /* cc warning */
1674 SXUNUSED(apArg);
1675 /* Return NULL */
1676 jx9_result_null(pCtx);
1677 return JX9_OK;
1678 }
1679 /* Create a new array */
1680 pArray = jx9_context_new_array(pCtx);
1681 if( pArray == 0 ){
1682 /* Return NULL */
1683 jx9_result_null(pCtx);
1684 return JX9_OK;
1685 }
1686 /* Make the table */
1687 for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
1688 /* Prepare the value */
1689 jx9_value_string(pValue, azHtmlEscape[n], -1 /* Compute length automatically */);
1690 /* Insert the value */
1691 jx9_array_add_strkey_elem(pArray, azHtmlEscape[n+1], pValue);
1692 /* Reset the string cursor */
1693 jx9_value_reset_string_cursor(pValue);
1694 }
1695 /*
1696 * Return the array.
1697 * Don't worry about freeing memory, everything will be automatically
1698 * released upon we return from this function.
1699 */
1700 jx9_result_value(pCtx, pArray);
1701 return JX9_OK;
1702}
1703/*
1704 * string htmlentities( string $string [, int $flags = ENT_COMPAT | ENT_HTML401]);
1705 * Convert all applicable characters to HTML entities
1706 * Parameters
1707 * $string
1708 * The input string.
1709 * $flags
1710 * A bitmask of one or more of the flags (see block-comment on jx9Builtin_htmlspecialchars())
1711 * Return
1712 * The encoded string.
1713 */
1714static int jx9Builtin_htmlentities(jx9_context *pCtx, int nArg, jx9_value **apArg)
1715{
1716 int iFlags = 0x01; /* ENT_COMPAT */
1717 const char *zIn, *zEnd;
1718 int nLen, c;
1719 sxu32 n;
1720 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1721 /* Missing/Invalid arguments, return NULL */
1722 jx9_result_null(pCtx);
1723 return JX9_OK;
1724 }
1725 /* Extract the target string */
1726 zIn = jx9_value_to_string(apArg[0], &nLen);
1727 zEnd = &zIn[nLen];
1728 /* Extract the flags if available */
1729 if( nArg > 1 ){
1730 iFlags = jx9_value_to_int(apArg[1]);
1731 if( iFlags < 0 ){
1732 iFlags = 0x01;
1733 }
1734 }
1735 /* Perform the requested operation */
1736 for(;;){
1737 if( zIn >= zEnd ){
1738 /* No more input to process */
1739 break;
1740 }
1741 c = zIn[0];
1742 /* Perform a linear lookup on the decoding table */
1743 for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
1744 if( azHtmlEscape[n+1][0] == c ){
1745 /* Got one */
1746 break;
1747 }
1748 }
1749 if( n < SX_ARRAYSIZE(azHtmlEscape) ){
1750 /* Output the safe sequence [i.e: '<' ==> '&lt;"] */
1751 if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){
1752 /* Expand the double quote verbatim */
1753 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
1754 }else if(c == '\'' && ((iFlags & 0x02 /*ENT_QUOTES*/) == 0 || (iFlags & 0x04) /*ENT_NOQUOTES*/) ){
1755 /* expand single quote verbatim */
1756 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
1757 }else{
1758 jx9_result_string(pCtx, azHtmlEscape[n], -1/*Compute length automatically */);
1759 }
1760 }else{
1761 /* Output character verbatim */
1762 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
1763 }
1764 zIn++;
1765 }
1766 return JX9_OK;
1767}
1768/*
1769 * string html_entity_decode(string $string [, int $quote_style = ENT_COMPAT [, string $charset = 'UTF-8' ]])
1770 * Perform the reverse operation of html_entity_decode().
1771 * Parameters
1772 * $string
1773 * The input string.
1774 * $flags
1775 * A bitmask of one or more of the flags (see comment on jx9Builtin_htmlspecialchars())
1776 * Return
1777 * The decoded string.
1778 */
1779static int jx9Builtin_html_entity_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
1780{
1781 const char *zCur, *zIn, *zEnd;
1782 int iFlags = 0x01; /* ENT_COMPAT */
1783 int nLen;
1784 sxu32 n;
1785 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1786 /* Missing/Invalid arguments, return NULL */
1787 jx9_result_null(pCtx);
1788 return JX9_OK;
1789 }
1790 /* Extract the target string */
1791 zIn = jx9_value_to_string(apArg[0], &nLen);
1792 zEnd = &zIn[nLen];
1793 /* Extract the flags if available */
1794 if( nArg > 1 ){
1795 iFlags = jx9_value_to_int(apArg[1]);
1796 if( iFlags < 0 ){
1797 iFlags = 0x01;
1798 }
1799 }
1800 /* Perform the requested operation */
1801 for(;;){
1802 if( zIn >= zEnd ){
1803 /* No more input to process */
1804 break;
1805 }
1806 zCur = zIn;
1807 while( zIn < zEnd && zIn[0] != '&' ){
1808 zIn++;
1809 }
1810 if( zCur < zIn ){
1811 /* Append raw string verbatim */
1812 jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
1813 }
1814 if( zIn >= zEnd ){
1815 break;
1816 }
1817 nLen = (int)(zEnd-zIn);
1818 /* Find an encoded sequence */
1819 for(n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
1820 int iLen = (int)SyStrlen(azHtmlEscape[n]);
1821 if( nLen >= iLen && SyStrnicmp(zIn, azHtmlEscape[n], (sxu32)iLen) == 0 ){
1822 /* Got one */
1823 zIn += iLen;
1824 break;
1825 }
1826 }
1827 if( n < SX_ARRAYSIZE(azHtmlEscape) ){
1828 int c = azHtmlEscape[n+1][0];
1829 /* Output the decoded character */
1830 if( c == '\'' && ((iFlags & 0x02) == 0 /*ENT_QUOTES*/|| (iFlags & 0x04) /*ENT_NOQUOTES*/) ){
1831 /* Do not process single quotes */
1832 jx9_result_string(pCtx, azHtmlEscape[n], -1);
1833 }else if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){
1834 /* Do not process double quotes */
1835 jx9_result_string(pCtx, azHtmlEscape[n], -1);
1836 }else{
1837 jx9_result_string(pCtx, azHtmlEscape[n+1], -1); /* Compute length automatically */
1838 }
1839 }else{
1840 /* Append '&' */
1841 jx9_result_string(pCtx, "&", (int)sizeof(char));
1842 zIn++;
1843 }
1844 }
1845 return JX9_OK;
1846}
1847/*
1848 * int strlen($string)
1849 * return the length of the given string.
1850 * Parameter
1851 * string: The string being measured for length.
1852 * Return
1853 * length of the given string.
1854 */
1855static int jx9Builtin_strlen(jx9_context *pCtx, int nArg, jx9_value **apArg)
1856{
1857 int iLen = 0;
1858 if( nArg > 0 ){
1859 jx9_value_to_string(apArg[0], &iLen);
1860 }
1861 /* String length */
1862 jx9_result_int(pCtx, iLen);
1863 return JX9_OK;
1864}
1865/*
1866 * int strcmp(string $str1, string $str2)
1867 * Perform a binary safe string comparison.
1868 * Parameter
1869 * str1: The first string
1870 * str2: The second string
1871 * Return
1872 * Returns < 0 if str1 is less than str2; > 0 if str1 is greater
1873 * than str2, and 0 if they are equal.
1874 */
1875static int jx9Builtin_strcmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
1876{
1877 const char *z1, *z2;
1878 int n1, n2;
1879 int res;
1880 if( nArg < 2 ){
1881 res = nArg == 0 ? 0 : 1;
1882 jx9_result_int(pCtx, res);
1883 return JX9_OK;
1884 }
1885 /* Perform the comparison */
1886 z1 = jx9_value_to_string(apArg[0], &n1);
1887 z2 = jx9_value_to_string(apArg[1], &n2);
1888 res = SyStrncmp(z1, z2, (sxu32)(SXMAX(n1, n2)));
1889 /* Comparison result */
1890 jx9_result_int(pCtx, res);
1891 return JX9_OK;
1892}
1893/*
1894 * int strncmp(string $str1, string $str2, int n)
1895 * Perform a binary safe string comparison of the first n characters.
1896 * Parameter
1897 * str1: The first string
1898 * str2: The second string
1899 * Return
1900 * Returns < 0 if str1 is less than str2; > 0 if str1 is greater
1901 * than str2, and 0 if they are equal.
1902 */
1903static int jx9Builtin_strncmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
1904{
1905 const char *z1, *z2;
1906 int res;
1907 int n;
1908 if( nArg < 3 ){
1909 /* Perform a standard comparison */
1910 return jx9Builtin_strcmp(pCtx, nArg, apArg);
1911 }
1912 /* Desired comparison length */
1913 n = jx9_value_to_int(apArg[2]);
1914 if( n < 0 ){
1915 /* Invalid length */
1916 jx9_result_int(pCtx, -1);
1917 return JX9_OK;
1918 }
1919 /* Perform the comparison */
1920 z1 = jx9_value_to_string(apArg[0], 0);
1921 z2 = jx9_value_to_string(apArg[1], 0);
1922 res = SyStrncmp(z1, z2, (sxu32)n);
1923 /* Comparison result */
1924 jx9_result_int(pCtx, res);
1925 return JX9_OK;
1926}
1927/*
1928 * int strcasecmp(string $str1, string $str2, int n)
1929 * Perform a binary safe case-insensitive string comparison.
1930 * Parameter
1931 * str1: The first string
1932 * str2: The second string
1933 * Return
1934 * Returns < 0 if str1 is less than str2; > 0 if str1 is greater
1935 * than str2, and 0 if they are equal.
1936 */
1937static int jx9Builtin_strcasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
1938{
1939 const char *z1, *z2;
1940 int n1, n2;
1941 int res;
1942 if( nArg < 2 ){
1943 res = nArg == 0 ? 0 : 1;
1944 jx9_result_int(pCtx, res);
1945 return JX9_OK;
1946 }
1947 /* Perform the comparison */
1948 z1 = jx9_value_to_string(apArg[0], &n1);
1949 z2 = jx9_value_to_string(apArg[1], &n2);
1950 res = SyStrnicmp(z1, z2, (sxu32)(SXMAX(n1, n2)));
1951 /* Comparison result */
1952 jx9_result_int(pCtx, res);
1953 return JX9_OK;
1954}
1955/*
1956 * int strncasecmp(string $str1, string $str2, int n)
1957 * Perform a binary safe case-insensitive string comparison of the first n characters.
1958 * Parameter
1959 * $str1: The first string
1960 * $str2: The second string
1961 * $len: The length of strings to be used in the comparison.
1962 * Return
1963 * Returns < 0 if str1 is less than str2; > 0 if str1 is greater
1964 * than str2, and 0 if they are equal.
1965 */
1966static int jx9Builtin_strncasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
1967{
1968 const char *z1, *z2;
1969 int res;
1970 int n;
1971 if( nArg < 3 ){
1972 /* Perform a standard comparison */
1973 return jx9Builtin_strcasecmp(pCtx, nArg, apArg);
1974 }
1975 /* Desired comparison length */
1976 n = jx9_value_to_int(apArg[2]);
1977 if( n < 0 ){
1978 /* Invalid length */
1979 jx9_result_int(pCtx, -1);
1980 return JX9_OK;
1981 }
1982 /* Perform the comparison */
1983 z1 = jx9_value_to_string(apArg[0], 0);
1984 z2 = jx9_value_to_string(apArg[1], 0);
1985 res = SyStrnicmp(z1, z2, (sxu32)n);
1986 /* Comparison result */
1987 jx9_result_int(pCtx, res);
1988 return JX9_OK;
1989}
1990/*
1991 * Implode context [i.e: it's private data].
1992 * A pointer to the following structure is forwarded
1993 * verbatim to the array walker callback defined below.
1994 */
1995struct implode_data {
1996 jx9_context *pCtx; /* Call context */
1997 int bRecursive; /* TRUE if recursive implode [this is a symisc eXtension] */
1998 const char *zSep; /* Arguments separator if any */
1999 int nSeplen; /* Separator length */
2000 int bFirst; /* TRUE if first call */
2001 int nRecCount; /* Recursion count to avoid infinite loop */
2002};
2003/*
2004 * Implode walker callback for the [jx9_array_walk()] interface.
2005 * The following routine is invoked for each array entry passed
2006 * to the implode() function.
2007 */
2008static int implode_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
2009{
2010 struct implode_data *pData = (struct implode_data *)pUserData;
2011 const char *zData;
2012 int nLen;
2013 if( pData->bRecursive && jx9_value_is_json_array(pValue) && pData->nRecCount < 32 ){
2014 if( pData->nSeplen > 0 ){
2015 if( !pData->bFirst ){
2016 /* append the separator first */
2017 jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen);
2018 }else{
2019 pData->bFirst = 0;
2020 }
2021 }
2022 /* Recurse */
2023 pData->bFirst = 1;
2024 pData->nRecCount++;
2025 jx9HashmapWalk((jx9_hashmap *)pValue->x.pOther, implode_callback, pData);
2026 pData->nRecCount--;
2027 return JX9_OK;
2028 }
2029 /* Extract the string representation of the entry value */
2030 zData = jx9_value_to_string(pValue, &nLen);
2031 if( nLen > 0 ){
2032 if( pData->nSeplen > 0 ){
2033 if( !pData->bFirst ){
2034 /* append the separator first */
2035 jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen);
2036 }else{
2037 pData->bFirst = 0;
2038 }
2039 }
2040 jx9_result_string(pData->pCtx, zData, nLen);
2041 }else{
2042 SXUNUSED(pKey); /* cc warning */
2043 }
2044 return JX9_OK;
2045}
2046/*
2047 * string implode(string $glue, array $pieces, ...)
2048 * string implode(array $pieces, ...)
2049 * Join array elements with a string.
2050 * $glue
2051 * Defaults to an empty string. This is not the preferred usage of implode() as glue
2052 * would be the second parameter and thus, the bad prototype would be used.
2053 * $pieces
2054 * The array of strings to implode.
2055 * Return
2056 * Returns a string containing a string representation of all the array elements in the same
2057 * order, with the glue string between each element.
2058 */
2059static int jx9Builtin_implode(jx9_context *pCtx, int nArg, jx9_value **apArg)
2060{
2061 struct implode_data imp_data;
2062 int i = 1;
2063 if( nArg < 1 ){
2064 /* Missing argument, return NULL */
2065 jx9_result_null(pCtx);
2066 return JX9_OK;
2067 }
2068 /* Prepare the implode context */
2069 imp_data.pCtx = pCtx;
2070 imp_data.bRecursive = 0;
2071 imp_data.bFirst = 1;
2072 imp_data.nRecCount = 0;
2073 if( !jx9_value_is_json_array(apArg[0]) ){
2074 imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen);
2075 }else{
2076 imp_data.zSep = 0;
2077 imp_data.nSeplen = 0;
2078 i = 0;
2079 }
2080 jx9_result_string(pCtx, "", 0); /* Set an empty stirng */
2081 /* Start the 'join' process */
2082 while( i < nArg ){
2083 if( jx9_value_is_json_array(apArg[i]) ){
2084 /* Iterate throw array entries */
2085 jx9_array_walk(apArg[i], implode_callback, &imp_data);
2086 }else{
2087 const char *zData;
2088 int nLen;
2089 /* Extract the string representation of the jx9 value */
2090 zData = jx9_value_to_string(apArg[i], &nLen);
2091 if( nLen > 0 ){
2092 if( imp_data.nSeplen > 0 ){
2093 if( !imp_data.bFirst ){
2094 /* append the separator first */
2095 jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen);
2096 }else{
2097 imp_data.bFirst = 0;
2098 }
2099 }
2100 jx9_result_string(pCtx, zData, nLen);
2101 }
2102 }
2103 i++;
2104 }
2105 return JX9_OK;
2106}
2107/*
2108 * string implode_recursive(string $glue, array $pieces, ...)
2109 * Purpose
2110 * Same as implode() but recurse on arrays.
2111 * Example:
2112 * $a = array('usr', array('home', 'dean'));
2113 * print implode_recursive("/", $a);
2114 * Will output
2115 * usr/home/dean.
2116 * While the standard implode would produce.
2117 * usr/Array.
2118 * Parameter
2119 * Refer to implode().
2120 * Return
2121 * Refer to implode().
2122 */
2123static int jx9Builtin_implode_recursive(jx9_context *pCtx, int nArg, jx9_value **apArg)
2124{
2125 struct implode_data imp_data;
2126 int i = 1;
2127 if( nArg < 1 ){
2128 /* Missing argument, return NULL */
2129 jx9_result_null(pCtx);
2130 return JX9_OK;
2131 }
2132 /* Prepare the implode context */
2133 imp_data.pCtx = pCtx;
2134 imp_data.bRecursive = 1;
2135 imp_data.bFirst = 1;
2136 imp_data.nRecCount = 0;
2137 if( !jx9_value_is_json_array(apArg[0]) ){
2138 imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen);
2139 }else{
2140 imp_data.zSep = 0;
2141 imp_data.nSeplen = 0;
2142 i = 0;
2143 }
2144 jx9_result_string(pCtx, "", 0); /* Set an empty stirng */
2145 /* Start the 'join' process */
2146 while( i < nArg ){
2147 if( jx9_value_is_json_array(apArg[i]) ){
2148 /* Iterate throw array entries */
2149 jx9_array_walk(apArg[i], implode_callback, &imp_data);
2150 }else{
2151 const char *zData;
2152 int nLen;
2153 /* Extract the string representation of the jx9 value */
2154 zData = jx9_value_to_string(apArg[i], &nLen);
2155 if( nLen > 0 ){
2156 if( imp_data.nSeplen > 0 ){
2157 if( !imp_data.bFirst ){
2158 /* append the separator first */
2159 jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen);
2160 }else{
2161 imp_data.bFirst = 0;
2162 }
2163 }
2164 jx9_result_string(pCtx, zData, nLen);
2165 }
2166 }
2167 i++;
2168 }
2169 return JX9_OK;
2170}
2171/*
2172 * array explode(string $delimiter, string $string[, int $limit ])
2173 * Returns an array of strings, each of which is a substring of string
2174 * formed by splitting it on boundaries formed by the string delimiter.
2175 * Parameters
2176 * $delimiter
2177 * The boundary string.
2178 * $string
2179 * The input string.
2180 * $limit
2181 * If limit is set and positive, the returned array will contain a maximum
2182 * of limit elements with the last element containing the rest of string.
2183 * If the limit parameter is negative, all fields except the last -limit are returned.
2184 * If the limit parameter is zero, then this is treated as 1.
2185 * Returns
2186 * Returns an array of strings created by splitting the string parameter
2187 * on boundaries formed by the delimiter.
2188 * If delimiter is an empty string (""), explode() will return FALSE.
2189 * If delimiter contains a value that is not contained in string and a negative
2190 * limit is used, then an empty array will be returned, otherwise an array containing string
2191 * will be returned.
2192 * NOTE:
2193 * Negative limit is not supported.
2194 */
2195static int jx9Builtin_explode(jx9_context *pCtx, int nArg, jx9_value **apArg)
2196{
2197 const char *zDelim, *zString, *zCur, *zEnd;
2198 int nDelim, nStrlen, iLimit;
2199 jx9_value *pArray;
2200 jx9_value *pValue;
2201 sxu32 nOfft;
2202 sxi32 rc;
2203 if( nArg < 2 ){
2204 /* Missing arguments, return FALSE */
2205 jx9_result_bool(pCtx, 0);
2206 return JX9_OK;
2207 }
2208 /* Extract the delimiter */
2209 zDelim = jx9_value_to_string(apArg[0], &nDelim);
2210 if( nDelim < 1 ){
2211 /* Empty delimiter, return FALSE */
2212 jx9_result_bool(pCtx, 0);
2213 return JX9_OK;
2214 }
2215 /* Extract the string */
2216 zString = jx9_value_to_string(apArg[1], &nStrlen);
2217 if( nStrlen < 1 ){
2218 /* Empty delimiter, return FALSE */
2219 jx9_result_bool(pCtx, 0);
2220 return JX9_OK;
2221 }
2222 /* Point to the end of the string */
2223 zEnd = &zString[nStrlen];
2224 /* Create the array */
2225 pArray = jx9_context_new_array(pCtx);
2226 pValue = jx9_context_new_scalar(pCtx);
2227 if( pArray == 0 || pValue == 0 ){
2228 /* Out of memory, return FALSE */
2229 jx9_result_bool(pCtx, 0);
2230 return JX9_OK;
2231 }
2232 /* Set a defualt limit */
2233 iLimit = SXI32_HIGH;
2234 if( nArg > 2 ){
2235 iLimit = jx9_value_to_int(apArg[2]);
2236 if( iLimit < 0 ){
2237 iLimit = -iLimit;
2238 }
2239 if( iLimit == 0 ){
2240 iLimit = 1;
2241 }
2242 iLimit--;
2243 }
2244 /* Start exploding */
2245 for(;;){
2246 if( zString >= zEnd ){
2247 /* No more entry to process */
2248 break;
2249 }
2250 rc = SyBlobSearch(zString, (sxu32)(zEnd-zString), zDelim, nDelim, &nOfft);
2251 if( rc != SXRET_OK || iLimit <= (int)jx9_array_count(pArray) ){
2252 /* Limit reached, insert the rest of the string and break */
2253 if( zEnd > zString ){
2254 jx9_value_string(pValue, zString, (int)(zEnd-zString));
2255 jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue);
2256 }
2257 break;
2258 }
2259 /* Point to the desired offset */
2260 zCur = &zString[nOfft];
2261 if( zCur > zString ){
2262 /* Perform the store operation */
2263 jx9_value_string(pValue, zString, (int)(zCur-zString));
2264 jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue);
2265 }
2266 /* Point beyond the delimiter */
2267 zString = &zCur[nDelim];
2268 /* Reset the cursor */
2269 jx9_value_reset_string_cursor(pValue);
2270 }
2271 /* Return the freshly created array */
2272 jx9_result_value(pCtx, pArray);
2273 /* NOTE that every allocated jx9_value will be automatically
2274 * released as soon we return from this foregin function.
2275 */
2276 return JX9_OK;
2277}
2278/*
2279 * string trim(string $str[, string $charlist ])
2280 * Strip whitespace (or other characters) from the beginning and end of a string.
2281 * Parameters
2282 * $str
2283 * The string that will be trimmed.
2284 * $charlist
2285 * Optionally, the stripped characters can also be specified using the charlist parameter.
2286 * Simply list all characters that you want to be stripped.
2287 * With .. you can specify a range of characters.
2288 * Returns.
2289 * Thr processed string.
2290 */
2291static int jx9Builtin_trim(jx9_context *pCtx, int nArg, jx9_value **apArg)
2292{
2293 const char *zString;
2294 int nLen;
2295 if( nArg < 1 ){
2296 /* Missing arguments, return null */
2297 jx9_result_null(pCtx);
2298 return JX9_OK;
2299 }
2300 /* Extract the target string */
2301 zString = jx9_value_to_string(apArg[0], &nLen);
2302 if( nLen < 1 ){
2303 /* Empty string, return */
2304 jx9_result_string(pCtx, "", 0);
2305 return JX9_OK;
2306 }
2307 /* Start the trim process */
2308 if( nArg < 2 ){
2309 SyString sStr;
2310 /* Remove white spaces and NUL bytes */
2311 SyStringInitFromBuf(&sStr, zString, nLen);
2312 SyStringFullTrimSafe(&sStr);
2313 jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
2314 }else{
2315 /* Char list */
2316 const char *zList;
2317 int nListlen;
2318 zList = jx9_value_to_string(apArg[1], &nListlen);
2319 if( nListlen < 1 ){
2320 /* Return the string unchanged */
2321 jx9_result_string(pCtx, zString, nLen);
2322 }else{
2323 const char *zEnd = &zString[nLen];
2324 const char *zCur = zString;
2325 const char *zPtr;
2326 int i;
2327 /* Left trim */
2328 for(;;){
2329 if( zCur >= zEnd ){
2330 break;
2331 }
2332 zPtr = zCur;
2333 for( i = 0 ; i < nListlen ; i++ ){
2334 if( zCur < zEnd && zCur[0] == zList[i] ){
2335 zCur++;
2336 }
2337 }
2338 if( zCur == zPtr ){
2339 /* No match, break immediately */
2340 break;
2341 }
2342 }
2343 /* Right trim */
2344 zEnd--;
2345 for(;;){
2346 if( zEnd <= zCur ){
2347 break;
2348 }
2349 zPtr = zEnd;
2350 for( i = 0 ; i < nListlen ; i++ ){
2351 if( zEnd > zCur && zEnd[0] == zList[i] ){
2352 zEnd--;
2353 }
2354 }
2355 if( zEnd == zPtr ){
2356 break;
2357 }
2358 }
2359 if( zCur >= zEnd ){
2360 /* Return the empty string */
2361 jx9_result_string(pCtx, "", 0);
2362 }else{
2363 zEnd++;
2364 jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
2365 }
2366 }
2367 }
2368 return JX9_OK;
2369}
2370/*
2371 * string rtrim(string $str[, string $charlist ])
2372 * Strip whitespace (or other characters) from the end of a string.
2373 * Parameters
2374 * $str
2375 * The string that will be trimmed.
2376 * $charlist
2377 * Optionally, the stripped characters can also be specified using the charlist parameter.
2378 * Simply list all characters that you want to be stripped.
2379 * With .. you can specify a range of characters.
2380 * Returns.
2381 * Thr processed string.
2382 */
2383static int jx9Builtin_rtrim(jx9_context *pCtx, int nArg, jx9_value **apArg)
2384{
2385 const char *zString;
2386 int nLen;
2387 if( nArg < 1 ){
2388 /* Missing arguments, return null */
2389 jx9_result_null(pCtx);
2390 return JX9_OK;
2391 }
2392 /* Extract the target string */
2393 zString = jx9_value_to_string(apArg[0], &nLen);
2394 if( nLen < 1 ){
2395 /* Empty string, return */
2396 jx9_result_string(pCtx, "", 0);
2397 return JX9_OK;
2398 }
2399 /* Start the trim process */
2400 if( nArg < 2 ){
2401 SyString sStr;
2402 /* Remove white spaces and NUL bytes*/
2403 SyStringInitFromBuf(&sStr, zString, nLen);
2404 SyStringRightTrimSafe(&sStr);
2405 jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
2406 }else{
2407 /* Char list */
2408 const char *zList;
2409 int nListlen;
2410 zList = jx9_value_to_string(apArg[1], &nListlen);
2411 if( nListlen < 1 ){
2412 /* Return the string unchanged */
2413 jx9_result_string(pCtx, zString, nLen);
2414 }else{
2415 const char *zEnd = &zString[nLen - 1];
2416 const char *zCur = zString;
2417 const char *zPtr;
2418 int i;
2419 /* Right trim */
2420 for(;;){
2421 if( zEnd <= zCur ){
2422 break;
2423 }
2424 zPtr = zEnd;
2425 for( i = 0 ; i < nListlen ; i++ ){
2426 if( zEnd > zCur && zEnd[0] == zList[i] ){
2427 zEnd--;
2428 }
2429 }
2430 if( zEnd == zPtr ){
2431 break;
2432 }
2433 }
2434 if( zEnd <= zCur ){
2435 /* Return the empty string */
2436 jx9_result_string(pCtx, "", 0);
2437 }else{
2438 zEnd++;
2439 jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
2440 }
2441 }
2442 }
2443 return JX9_OK;
2444}
2445/*
2446 * string ltrim(string $str[, string $charlist ])
2447 * Strip whitespace (or other characters) from the beginning and end of a string.
2448 * Parameters
2449 * $str
2450 * The string that will be trimmed.
2451 * $charlist
2452 * Optionally, the stripped characters can also be specified using the charlist parameter.
2453 * Simply list all characters that you want to be stripped.
2454 * With .. you can specify a range of characters.
2455 * Returns.
2456 * The processed string.
2457 */
2458static int jx9Builtin_ltrim(jx9_context *pCtx, int nArg, jx9_value **apArg)
2459{
2460 const char *zString;
2461 int nLen;
2462 if( nArg < 1 ){
2463 /* Missing arguments, return null */
2464 jx9_result_null(pCtx);
2465 return JX9_OK;
2466 }
2467 /* Extract the target string */
2468 zString = jx9_value_to_string(apArg[0], &nLen);
2469 if( nLen < 1 ){
2470 /* Empty string, return */
2471 jx9_result_string(pCtx, "", 0);
2472 return JX9_OK;
2473 }
2474 /* Start the trim process */
2475 if( nArg < 2 ){
2476 SyString sStr;
2477 /* Remove white spaces and NUL byte */
2478 SyStringInitFromBuf(&sStr, zString, nLen);
2479 SyStringLeftTrimSafe(&sStr);
2480 jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
2481 }else{
2482 /* Char list */
2483 const char *zList;
2484 int nListlen;
2485 zList = jx9_value_to_string(apArg[1], &nListlen);
2486 if( nListlen < 1 ){
2487 /* Return the string unchanged */
2488 jx9_result_string(pCtx, zString, nLen);
2489 }else{
2490 const char *zEnd = &zString[nLen];
2491 const char *zCur = zString;
2492 const char *zPtr;
2493 int i;
2494 /* Left trim */
2495 for(;;){
2496 if( zCur >= zEnd ){
2497 break;
2498 }
2499 zPtr = zCur;
2500 for( i = 0 ; i < nListlen ; i++ ){
2501 if( zCur < zEnd && zCur[0] == zList[i] ){
2502 zCur++;
2503 }
2504 }
2505 if( zCur == zPtr ){
2506 /* No match, break immediately */
2507 break;
2508 }
2509 }
2510 if( zCur >= zEnd ){
2511 /* Return the empty string */
2512 jx9_result_string(pCtx, "", 0);
2513 }else{
2514 jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
2515 }
2516 }
2517 }
2518 return JX9_OK;
2519}
2520/*
2521 * string strtolower(string $str)
2522 * Make a string lowercase.
2523 * Parameters
2524 * $str
2525 * The input string.
2526 * Returns.
2527 * The lowercased string.
2528 */
2529static int jx9Builtin_strtolower(jx9_context *pCtx, int nArg, jx9_value **apArg)
2530{
2531 const char *zString, *zCur, *zEnd;
2532 int nLen;
2533 if( nArg < 1 ){
2534 /* Missing arguments, return null */
2535 jx9_result_null(pCtx);
2536 return JX9_OK;
2537 }
2538 /* Extract the target string */
2539 zString = jx9_value_to_string(apArg[0], &nLen);
2540 if( nLen < 1 ){
2541 /* Empty string, return */
2542 jx9_result_string(pCtx, "", 0);
2543 return JX9_OK;
2544 }
2545 /* Perform the requested operation */
2546 zEnd = &zString[nLen];
2547 for(;;){
2548 if( zString >= zEnd ){
2549 /* No more input, break immediately */
2550 break;
2551 }
2552 if( (unsigned char)zString[0] >= 0xc0 ){
2553 /* UTF-8 stream, output verbatim */
2554 zCur = zString;
2555 zString++;
2556 while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){
2557 zString++;
2558 }
2559 /* Append UTF-8 stream */
2560 jx9_result_string(pCtx, zCur, (int)(zString-zCur));
2561 }else{
2562 int c = zString[0];
2563 if( SyisUpper(c) ){
2564 c = SyToLower(zString[0]);
2565 }
2566 /* Append character */
2567 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
2568 /* Advance the cursor */
2569 zString++;
2570 }
2571 }
2572 return JX9_OK;
2573}
2574/*
2575 * string strtolower(string $str)
2576 * Make a string uppercase.
2577 * Parameters
2578 * $str
2579 * The input string.
2580 * Returns.
2581 * The uppercased string.
2582 */
2583static int jx9Builtin_strtoupper(jx9_context *pCtx, int nArg, jx9_value **apArg)
2584{
2585 const char *zString, *zCur, *zEnd;
2586 int nLen;
2587 if( nArg < 1 ){
2588 /* Missing arguments, return null */
2589 jx9_result_null(pCtx);
2590 return JX9_OK;
2591 }
2592 /* Extract the target string */
2593 zString = jx9_value_to_string(apArg[0], &nLen);
2594 if( nLen < 1 ){
2595 /* Empty string, return */
2596 jx9_result_string(pCtx, "", 0);
2597 return JX9_OK;
2598 }
2599 /* Perform the requested operation */
2600 zEnd = &zString[nLen];
2601 for(;;){
2602 if( zString >= zEnd ){
2603 /* No more input, break immediately */
2604 break;
2605 }
2606 if( (unsigned char)zString[0] >= 0xc0 ){
2607 /* UTF-8 stream, output verbatim */
2608 zCur = zString;
2609 zString++;
2610 while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){
2611 zString++;
2612 }
2613 /* Append UTF-8 stream */
2614 jx9_result_string(pCtx, zCur, (int)(zString-zCur));
2615 }else{
2616 int c = zString[0];
2617 if( SyisLower(c) ){
2618 c = SyToUpper(zString[0]);
2619 }
2620 /* Append character */
2621 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
2622 /* Advance the cursor */
2623 zString++;
2624 }
2625 }
2626 return JX9_OK;
2627}
2628/*
2629 * int ord(string $string)
2630 * Returns the ASCII value of the first character of string.
2631 * Parameters
2632 * $str
2633 * The input string.
2634 * Returns.
2635 * The ASCII value as an integer.
2636 */
2637static int jx9Builtin_ord(jx9_context *pCtx, int nArg, jx9_value **apArg)
2638{
2639 const char *zString;
2640 int nLen, c;
2641 if( nArg < 1 ){
2642 /* Missing arguments, return -1 */
2643 jx9_result_int(pCtx, -1);
2644 return JX9_OK;
2645 }
2646 /* Extract the target string */
2647 zString = jx9_value_to_string(apArg[0], &nLen);
2648 if( nLen < 1 ){
2649 /* Empty string, return -1 */
2650 jx9_result_int(pCtx, -1);
2651 return JX9_OK;
2652 }
2653 /* Extract the ASCII value of the first character */
2654 c = zString[0];
2655 /* Return that value */
2656 jx9_result_int(pCtx, c);
2657 return JX9_OK;
2658}
2659/*
2660 * string chr(int $ascii)
2661 * Returns a one-character string containing the character specified by ascii.
2662 * Parameters
2663 * $ascii
2664 * The ascii code.
2665 * Returns.
2666 * The specified character.
2667 */
2668static int jx9Builtin_chr(jx9_context *pCtx, int nArg, jx9_value **apArg)
2669{
2670 int c;
2671 if( nArg < 1 ){
2672 /* Missing arguments, return null */
2673 jx9_result_null(pCtx);
2674 return JX9_OK;
2675 }
2676 /* Extract the ASCII value */
2677 c = jx9_value_to_int(apArg[0]);
2678 /* Return the specified character */
2679 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
2680 return JX9_OK;
2681}
2682/*
2683 * Binary to hex consumer callback.
2684 * This callback is the default consumer used by the hash functions
2685 * [i.e: bin2hex(), md5(), sha1(), md5_file() ... ] defined below.
2686 */
2687static int HashConsumer(const void *pData, unsigned int nLen, void *pUserData)
2688{
2689 /* Append hex chunk verbatim */
2690 jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
2691 return SXRET_OK;
2692}
2693/*
2694 * string bin2hex(string $str)
2695 * Convert binary data into hexadecimal representation.
2696 * Parameters
2697 * $str
2698 * The input string.
2699 * Returns.
2700 * Returns the hexadecimal representation of the given string.
2701 */
2702static int jx9Builtin_bin2hex(jx9_context *pCtx, int nArg, jx9_value **apArg)
2703{
2704 const char *zString;
2705 int nLen;
2706 if( nArg < 1 ){
2707 /* Missing arguments, return null */
2708 jx9_result_null(pCtx);
2709 return JX9_OK;
2710 }
2711 /* Extract the target string */
2712 zString = jx9_value_to_string(apArg[0], &nLen);
2713 if( nLen < 1 ){
2714 /* Empty string, return */
2715 jx9_result_string(pCtx, "", 0);
2716 return JX9_OK;
2717 }
2718 /* Perform the requested operation */
2719 SyBinToHexConsumer((const void *)zString, (sxu32)nLen, HashConsumer, pCtx);
2720 return JX9_OK;
2721}
2722/* Search callback signature */
2723typedef sxi32 (*ProcStringMatch)(const void *, sxu32, const void *, sxu32, sxu32 *);
2724/*
2725 * Case-insensitive pattern match.
2726 * Brute force is the default search method used here.
2727 * This is due to the fact that brute-forcing works quite
2728 * well for short/medium texts on modern hardware.
2729 */
2730static sxi32 iPatternMatch(const void *pText, sxu32 nLen, const void *pPattern, sxu32 iPatLen, sxu32 *pOfft)
2731{
2732 const char *zpIn = (const char *)pPattern;
2733 const char *zIn = (const char *)pText;
2734 const char *zpEnd = &zpIn[iPatLen];
2735 const char *zEnd = &zIn[nLen];
2736 const char *zPtr, *zPtr2;
2737 int c, d;
2738 if( iPatLen > nLen ){
2739 /* Don't bother processing */
2740 return SXERR_NOTFOUND;
2741 }
2742 for(;;){
2743 if( zIn >= zEnd ){
2744 break;
2745 }
2746 c = SyToLower(zIn[0]);
2747 d = SyToLower(zpIn[0]);
2748 if( c == d ){
2749 zPtr = &zIn[1];
2750 zPtr2 = &zpIn[1];
2751 for(;;){
2752 if( zPtr2 >= zpEnd ){
2753 /* Pattern found */
2754 if( pOfft ){ *pOfft = (sxu32)(zIn-(const char *)pText); }
2755 return SXRET_OK;
2756 }
2757 if( zPtr >= zEnd ){
2758 break;
2759 }
2760 c = SyToLower(zPtr[0]);
2761 d = SyToLower(zPtr2[0]);
2762 if( c != d ){
2763 break;
2764 }
2765 zPtr++; zPtr2++;
2766 }
2767 }
2768 zIn++;
2769 }
2770 /* Pattern not found */
2771 return SXERR_NOTFOUND;
2772}
2773/*
2774 * string strstr(string $haystack, string $needle[, bool $before_needle = false ])
2775 * Find the first occurrence of a string.
2776 * Parameters
2777 * $haystack
2778 * The input string.
2779 * $needle
2780 * Search pattern (must be a string).
2781 * $before_needle
2782 * If TRUE, strstr() returns the part of the haystack before the first occurrence
2783 * of the needle (excluding the needle).
2784 * Return
2785 * Returns the portion of string, or FALSE if needle is not found.
2786 */
2787static int jx9Builtin_strstr(jx9_context *pCtx, int nArg, jx9_value **apArg)
2788{
2789 ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
2790 const char *zBlob, *zPattern;
2791 int nLen, nPatLen;
2792 sxu32 nOfft;
2793 sxi32 rc;
2794 if( nArg < 2 ){
2795 /* Missing arguments, return FALSE */
2796 jx9_result_bool(pCtx, 0);
2797 return JX9_OK;
2798 }
2799 /* Extract the needle and the haystack */
2800 zBlob = jx9_value_to_string(apArg[0], &nLen);
2801 zPattern = jx9_value_to_string(apArg[1], &nPatLen);
2802 nOfft = 0; /* cc warning */
2803 if( nLen > 0 && nPatLen > 0 ){
2804 int before = 0;
2805 /* Perform the lookup */
2806 rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
2807 if( rc != SXRET_OK ){
2808 /* Pattern not found, return FALSE */
2809 jx9_result_bool(pCtx, 0);
2810 return JX9_OK;
2811 }
2812 /* Return the portion of the string */
2813 if( nArg > 2 ){
2814 before = jx9_value_to_int(apArg[2]);
2815 }
2816 if( before ){
2817 jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob));
2818 }else{
2819 jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
2820 }
2821 }else{
2822 jx9_result_bool(pCtx, 0);
2823 }
2824 return JX9_OK;
2825}
2826/*
2827 * string stristr(string $haystack, string $needle[, bool $before_needle = false ])
2828 * Case-insensitive strstr().
2829 * Parameters
2830 * $haystack
2831 * The input string.
2832 * $needle
2833 * Search pattern (must be a string).
2834 * $before_needle
2835 * If TRUE, strstr() returns the part of the haystack before the first occurrence
2836 * of the needle (excluding the needle).
2837 * Return
2838 * Returns the portion of string, or FALSE if needle is not found.
2839 */
2840static int jx9Builtin_stristr(jx9_context *pCtx, int nArg, jx9_value **apArg)
2841{
2842 ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
2843 const char *zBlob, *zPattern;
2844 int nLen, nPatLen;
2845 sxu32 nOfft;
2846 sxi32 rc;
2847 if( nArg < 2 ){
2848 /* Missing arguments, return FALSE */
2849 jx9_result_bool(pCtx, 0);
2850 return JX9_OK;
2851 }
2852 /* Extract the needle and the haystack */
2853 zBlob = jx9_value_to_string(apArg[0], &nLen);
2854 zPattern = jx9_value_to_string(apArg[1], &nPatLen);
2855 nOfft = 0; /* cc warning */
2856 if( nLen > 0 && nPatLen > 0 ){
2857 int before = 0;
2858 /* Perform the lookup */
2859 rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
2860 if( rc != SXRET_OK ){
2861 /* Pattern not found, return FALSE */
2862 jx9_result_bool(pCtx, 0);
2863 return JX9_OK;
2864 }
2865 /* Return the portion of the string */
2866 if( nArg > 2 ){
2867 before = jx9_value_to_int(apArg[2]);
2868 }
2869 if( before ){
2870 jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob));
2871 }else{
2872 jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
2873 }
2874 }else{
2875 jx9_result_bool(pCtx, 0);
2876 }
2877 return JX9_OK;
2878}
2879/*
2880 * int strpos(string $haystack, string $needle [, int $offset = 0 ] )
2881 * Returns the numeric position of the first occurrence of needle in the haystack string.
2882 * Parameters
2883 * $haystack
2884 * The input string.
2885 * $needle
2886 * Search pattern (must be a string).
2887 * $offset
2888 * This optional offset parameter allows you to specify which character in haystack
2889 * to start searching. The position returned is still relative to the beginning
2890 * of haystack.
2891 * Return
2892 * Returns the position as an integer.If needle is not found, strpos() will return FALSE.
2893 */
2894static int jx9Builtin_strpos(jx9_context *pCtx, int nArg, jx9_value **apArg)
2895{
2896 ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
2897 const char *zBlob, *zPattern;
2898 int nLen, nPatLen, nStart;
2899 sxu32 nOfft;
2900 sxi32 rc;
2901 if( nArg < 2 ){
2902 /* Missing arguments, return FALSE */
2903 jx9_result_bool(pCtx, 0);
2904 return JX9_OK;
2905 }
2906 /* Extract the needle and the haystack */
2907 zBlob = jx9_value_to_string(apArg[0], &nLen);
2908 zPattern = jx9_value_to_string(apArg[1], &nPatLen);
2909 nOfft = 0; /* cc warning */
2910 nStart = 0;
2911 /* Peek the starting offset if available */
2912 if( nArg > 2 ){
2913 nStart = jx9_value_to_int(apArg[2]);
2914 if( nStart < 0 ){
2915 nStart = -nStart;
2916 }
2917 if( nStart >= nLen ){
2918 /* Invalid offset */
2919 nStart = 0;
2920 }else{
2921 zBlob += nStart;
2922 nLen -= nStart;
2923 }
2924 }
2925 if( nLen > 0 && nPatLen > 0 ){
2926 /* Perform the lookup */
2927 rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
2928 if( rc != SXRET_OK ){
2929 /* Pattern not found, return FALSE */
2930 jx9_result_bool(pCtx, 0);
2931 return JX9_OK;
2932 }
2933 /* Return the pattern position */
2934 jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart));
2935 }else{
2936 jx9_result_bool(pCtx, 0);
2937 }
2938 return JX9_OK;
2939}
2940/*
2941 * int stripos(string $haystack, string $needle [, int $offset = 0 ] )
2942 * Case-insensitive strpos.
2943 * Parameters
2944 * $haystack
2945 * The input string.
2946 * $needle
2947 * Search pattern (must be a string).
2948 * $offset
2949 * This optional offset parameter allows you to specify which character in haystack
2950 * to start searching. The position returned is still relative to the beginning
2951 * of haystack.
2952 * Return
2953 * Returns the position as an integer.If needle is not found, strpos() will return FALSE.
2954 */
2955static int jx9Builtin_stripos(jx9_context *pCtx, int nArg, jx9_value **apArg)
2956{
2957 ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
2958 const char *zBlob, *zPattern;
2959 int nLen, nPatLen, nStart;
2960 sxu32 nOfft;
2961 sxi32 rc;
2962 if( nArg < 2 ){
2963 /* Missing arguments, return FALSE */
2964 jx9_result_bool(pCtx, 0);
2965 return JX9_OK;
2966 }
2967 /* Extract the needle and the haystack */
2968 zBlob = jx9_value_to_string(apArg[0], &nLen);
2969 zPattern = jx9_value_to_string(apArg[1], &nPatLen);
2970 nOfft = 0; /* cc warning */
2971 nStart = 0;
2972 /* Peek the starting offset if available */
2973 if( nArg > 2 ){
2974 nStart = jx9_value_to_int(apArg[2]);
2975 if( nStart < 0 ){
2976 nStart = -nStart;
2977 }
2978 if( nStart >= nLen ){
2979 /* Invalid offset */
2980 nStart = 0;
2981 }else{
2982 zBlob += nStart;
2983 nLen -= nStart;
2984 }
2985 }
2986 if( nLen > 0 && nPatLen > 0 ){
2987 /* Perform the lookup */
2988 rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
2989 if( rc != SXRET_OK ){
2990 /* Pattern not found, return FALSE */
2991 jx9_result_bool(pCtx, 0);
2992 return JX9_OK;
2993 }
2994 /* Return the pattern position */
2995 jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart));
2996 }else{
2997 jx9_result_bool(pCtx, 0);
2998 }
2999 return JX9_OK;
3000}
3001/*
3002 * int strrpos(string $haystack, string $needle [, int $offset = 0 ] )
3003 * Find the numeric position of the last occurrence of needle in the haystack string.
3004 * Parameters
3005 * $haystack
3006 * The input string.
3007 * $needle
3008 * Search pattern (must be a string).
3009 * $offset
3010 * If specified, search will start this number of characters counted from the beginning
3011 * of the string. If the value is negative, search will instead start from that many
3012 * characters from the end of the string, searching backwards.
3013 * Return
3014 * Returns the position as an integer.If needle is not found, strrpos() will return FALSE.
3015 */
3016static int jx9Builtin_strrpos(jx9_context *pCtx, int nArg, jx9_value **apArg)
3017{
3018 const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd;
3019 ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
3020 int nLen, nPatLen;
3021 sxu32 nOfft;
3022 sxi32 rc;
3023 if( nArg < 2 ){
3024 /* Missing arguments, return FALSE */
3025 jx9_result_bool(pCtx, 0);
3026 return JX9_OK;
3027 }
3028 /* Extract the needle and the haystack */
3029 zBlob = jx9_value_to_string(apArg[0], &nLen);
3030 zPattern = jx9_value_to_string(apArg[1], &nPatLen);
3031 /* Point to the end of the pattern */
3032 zPtr = &zBlob[nLen - 1];
3033 zEnd = &zBlob[nLen];
3034 /* Save the starting posistion */
3035 zStart = zBlob;
3036 nOfft = 0; /* cc warning */
3037 /* Peek the starting offset if available */
3038 if( nArg > 2 ){
3039 int nStart;
3040 nStart = jx9_value_to_int(apArg[2]);
3041 if( nStart < 0 ){
3042 nStart = -nStart;
3043 if( nStart >= nLen ){
3044 /* Invalid offset */
3045 jx9_result_bool(pCtx, 0);
3046 return JX9_OK;
3047 }else{
3048 nLen -= nStart;
3049 zPtr = &zBlob[nLen - 1];
3050 zEnd = &zBlob[nLen];
3051 }
3052 }else{
3053 if( nStart >= nLen ){
3054 /* Invalid offset */
3055 jx9_result_bool(pCtx, 0);
3056 return JX9_OK;
3057 }else{
3058 zBlob += nStart;
3059 nLen -= nStart;
3060 }
3061 }
3062 }
3063 if( nLen > 0 && nPatLen > 0 ){
3064 /* Perform the lookup */
3065 for(;;){
3066 if( zBlob >= zPtr ){
3067 break;
3068 }
3069 rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft);
3070 if( rc == SXRET_OK ){
3071 /* Pattern found, return it's position */
3072 jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart));
3073 return JX9_OK;
3074 }
3075 zPtr--;
3076 }
3077 /* Pattern not found, return FALSE */
3078 jx9_result_bool(pCtx, 0);
3079 }else{
3080 jx9_result_bool(pCtx, 0);
3081 }
3082 return JX9_OK;
3083}
3084/*
3085 * int strripos(string $haystack, string $needle [, int $offset = 0 ] )
3086 * Case-insensitive strrpos.
3087 * Parameters
3088 * $haystack
3089 * The input string.
3090 * $needle
3091 * Search pattern (must be a string).
3092 * $offset
3093 * If specified, search will start this number of characters counted from the beginning
3094 * of the string. If the value is negative, search will instead start from that many
3095 * characters from the end of the string, searching backwards.
3096 * Return
3097 * Returns the position as an integer.If needle is not found, strrpos() will return FALSE.
3098 */
3099static int jx9Builtin_strripos(jx9_context *pCtx, int nArg, jx9_value **apArg)
3100{
3101 const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd;
3102 ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
3103 int nLen, nPatLen;
3104 sxu32 nOfft;
3105 sxi32 rc;
3106 if( nArg < 2 ){
3107 /* Missing arguments, return FALSE */
3108 jx9_result_bool(pCtx, 0);
3109 return JX9_OK;
3110 }
3111 /* Extract the needle and the haystack */
3112 zBlob = jx9_value_to_string(apArg[0], &nLen);
3113 zPattern = jx9_value_to_string(apArg[1], &nPatLen);
3114 /* Point to the end of the pattern */
3115 zPtr = &zBlob[nLen - 1];
3116 zEnd = &zBlob[nLen];
3117 /* Save the starting posistion */
3118 zStart = zBlob;
3119 nOfft = 0; /* cc warning */
3120 /* Peek the starting offset if available */
3121 if( nArg > 2 ){
3122 int nStart;
3123 nStart = jx9_value_to_int(apArg[2]);
3124 if( nStart < 0 ){
3125 nStart = -nStart;
3126 if( nStart >= nLen ){
3127 /* Invalid offset */
3128 jx9_result_bool(pCtx, 0);
3129 return JX9_OK;
3130 }else{
3131 nLen -= nStart;
3132 zPtr = &zBlob[nLen - 1];
3133 zEnd = &zBlob[nLen];
3134 }
3135 }else{
3136 if( nStart >= nLen ){
3137 /* Invalid offset */
3138 jx9_result_bool(pCtx, 0);
3139 return JX9_OK;
3140 }else{
3141 zBlob += nStart;
3142 nLen -= nStart;
3143 }
3144 }
3145 }
3146 if( nLen > 0 && nPatLen > 0 ){
3147 /* Perform the lookup */
3148 for(;;){
3149 if( zBlob >= zPtr ){
3150 break;
3151 }
3152 rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft);
3153 if( rc == SXRET_OK ){
3154 /* Pattern found, return it's position */
3155 jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart));
3156 return JX9_OK;
3157 }
3158 zPtr--;
3159 }
3160 /* Pattern not found, return FALSE */
3161 jx9_result_bool(pCtx, 0);
3162 }else{
3163 jx9_result_bool(pCtx, 0);
3164 }
3165 return JX9_OK;
3166}
3167/*
3168 * int strrchr(string $haystack, mixed $needle)
3169 * Find the last occurrence of a character in a string.
3170 * Parameters
3171 * $haystack
3172 * The input string.
3173 * $needle
3174 * If needle contains more than one character, only the first is used.
3175 * This behavior is different from that of strstr().
3176 * If needle is not a string, it is converted to an integer and applied
3177 * as the ordinal value of a character.
3178 * Return
3179 * This function returns the portion of string, or FALSE if needle is not found.
3180 */
3181static int jx9Builtin_strrchr(jx9_context *pCtx, int nArg, jx9_value **apArg)
3182{
3183 const char *zBlob;
3184 int nLen, c;
3185 if( nArg < 2 ){
3186 /* Missing arguments, return FALSE */
3187 jx9_result_bool(pCtx, 0);
3188 return JX9_OK;
3189 }
3190 /* Extract the haystack */
3191 zBlob = jx9_value_to_string(apArg[0], &nLen);
3192 c = 0; /* cc warning */
3193 if( nLen > 0 ){
3194 sxu32 nOfft;
3195 sxi32 rc;
3196 if( jx9_value_is_string(apArg[1]) ){
3197 const char *zPattern;
3198 zPattern = jx9_value_to_string(apArg[1], 0); /* Never fail, so there is no need to check
3199 * for NULL pointer.
3200 */
3201 c = zPattern[0];
3202 }else{
3203 /* Int cast */
3204 c = jx9_value_to_int(apArg[1]);
3205 }
3206 /* Perform the lookup */
3207 rc = SyByteFind2(zBlob, (sxu32)nLen, c, &nOfft);
3208 if( rc != SXRET_OK ){
3209 /* No such entry, return FALSE */
3210 jx9_result_bool(pCtx, 0);
3211 return JX9_OK;
3212 }
3213 /* Return the string portion */
3214 jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
3215 }else{
3216 jx9_result_bool(pCtx, 0);
3217 }
3218 return JX9_OK;
3219}
3220/*
3221 * string strrev(string $string)
3222 * Reverse a string.
3223 * Parameters
3224 * $string
3225 * String to be reversed.
3226 * Return
3227 * The reversed string.
3228 */
3229static int jx9Builtin_strrev(jx9_context *pCtx, int nArg, jx9_value **apArg)
3230{
3231 const char *zIn, *zEnd;
3232 int nLen, c;
3233 if( nArg < 1 ){
3234 /* Missing arguments, return NULL */
3235 jx9_result_null(pCtx);
3236 return JX9_OK;
3237 }
3238 /* Extract the target string */
3239 zIn = jx9_value_to_string(apArg[0], &nLen);
3240 if( nLen < 1 ){
3241 /* Empty string Return null */
3242 jx9_result_null(pCtx);
3243 return JX9_OK;
3244 }
3245 /* Perform the requested operation */
3246 zEnd = &zIn[nLen - 1];
3247 for(;;){
3248 if( zEnd < zIn ){
3249 /* No more input to process */
3250 break;
3251 }
3252 /* Append current character */
3253 c = zEnd[0];
3254 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
3255 zEnd--;
3256 }
3257 return JX9_OK;
3258}
3259/*
3260 * string str_repeat(string $input, int $multiplier)
3261 * Returns input repeated multiplier times.
3262 * Parameters
3263 * $string
3264 * String to be repeated.
3265 * $multiplier
3266 * Number of time the input string should be repeated.
3267 * multiplier has to be greater than or equal to 0. If the multiplier is set
3268 * to 0, the function will return an empty string.
3269 * Return
3270 * The repeated string.
3271 */
3272static int jx9Builtin_str_repeat(jx9_context *pCtx, int nArg, jx9_value **apArg)
3273{
3274 const char *zIn;
3275 int nLen, nMul;
3276 int rc;
3277 if( nArg < 2 ){
3278 /* Missing arguments, return NULL */
3279 jx9_result_null(pCtx);
3280 return JX9_OK;
3281 }
3282 /* Extract the target string */
3283 zIn = jx9_value_to_string(apArg[0], &nLen);
3284 if( nLen < 1 ){
3285 /* Empty string.Return null */
3286 jx9_result_null(pCtx);
3287 return JX9_OK;
3288 }
3289 /* Extract the multiplier */
3290 nMul = jx9_value_to_int(apArg[1]);
3291 if( nMul < 1 ){
3292 /* Return the empty string */
3293 jx9_result_string(pCtx, "", 0);
3294 return JX9_OK;
3295 }
3296 /* Perform the requested operation */
3297 for(;;){
3298 if( nMul < 1 ){
3299 break;
3300 }
3301 /* Append the copy */
3302 rc = jx9_result_string(pCtx, zIn, nLen);
3303 if( rc != JX9_OK ){
3304 /* Out of memory, break immediately */
3305 break;
3306 }
3307 nMul--;
3308 }
3309 return JX9_OK;
3310}
3311/*
3312 * string nl2br(string $string[, bool $is_xhtml = true ])
3313 * Inserts HTML line breaks before all newlines in a string.
3314 * Parameters
3315 * $string
3316 * The input string.
3317 * $is_xhtml
3318 * Whenever to use XHTML compatible line breaks or not.
3319 * Return
3320 * The processed string.
3321 */
3322static int jx9Builtin_nl2br(jx9_context *pCtx, int nArg, jx9_value **apArg)
3323{
3324 const char *zIn, *zCur, *zEnd;
3325 int is_xhtml = 0;
3326 int nLen;
3327 if( nArg < 1 ){
3328 /* Missing arguments, return the empty string */
3329 jx9_result_string(pCtx, "", 0);
3330 return JX9_OK;
3331 }
3332 /* Extract the target string */
3333 zIn = jx9_value_to_string(apArg[0], &nLen);
3334 if( nLen < 1 ){
3335 /* Empty string, return null */
3336 jx9_result_null(pCtx);
3337 return JX9_OK;
3338 }
3339 if( nArg > 1 ){
3340 is_xhtml = jx9_value_to_bool(apArg[1]);
3341 }
3342 zEnd = &zIn[nLen];
3343 /* Perform the requested operation */
3344 for(;;){
3345 zCur = zIn;
3346 /* Delimit the string */
3347 while( zIn < zEnd && (zIn[0] != '\n'&& zIn[0] != '\r') ){
3348 zIn++;
3349 }
3350 if( zCur < zIn ){
3351 /* Output chunk verbatim */
3352 jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
3353 }
3354 if( zIn >= zEnd ){
3355 /* No more input to process */
3356 break;
3357 }
3358 /* Output the HTML line break */
3359 if( is_xhtml ){
3360 jx9_result_string(pCtx, "<br>", (int)sizeof("<br>")-1);
3361 }else{
3362 jx9_result_string(pCtx, "<br/>", (int)sizeof("<br/>")-1);
3363 }
3364 zCur = zIn;
3365 /* Append trailing line */
3366 while( zIn < zEnd && (zIn[0] == '\n' || zIn[0] == '\r') ){
3367 zIn++;
3368 }
3369 if( zCur < zIn ){
3370 /* Output chunk verbatim */
3371 jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
3372 }
3373 }
3374 return JX9_OK;
3375}
3376/*
3377 * Format a given string and invoke the given callback on each processed chunk.
3378 * According to the JX9 reference manual.
3379 * The format string is composed of zero or more directives: ordinary characters
3380 * (excluding %) that are copied directly to the result, and conversion
3381 * specifications, each of which results in fetching its own parameter.
3382 * This applies to both sprintf() and printf().
3383 * Each conversion specification consists of a percent sign (%), followed by one
3384 * or more of these elements, in order:
3385 * An optional sign specifier that forces a sign (- or +) to be used on a number.
3386 * By default, only the - sign is used on a number if it's negative. This specifier forces
3387 * positive numbers to have the + sign attached as well.
3388 * An optional padding specifier that says what character will be used for padding
3389 * the results to the right string size. This may be a space character or a 0 (zero character).
3390 * The default is to pad with spaces. An alternate padding character can be specified by prefixing
3391 * it with a single quote ('). See the examples below.
3392 * An optional alignment specifier that says if the result should be left-justified or right-justified.
3393 * The default is right-justified; a - character here will make it left-justified.
3394 * An optional number, a width specifier that says how many characters (minimum) this conversion
3395 * should result in.
3396 * An optional precision specifier in the form of a period (`.') followed by an optional decimal
3397 * digit string that says how many decimal digits should be displayed for floating-point numbers.
3398 * When using this specifier on a string, it acts as a cutoff point, setting a maximum character
3399 * limit to the string.
3400 * A type specifier that says what type the argument data should be treated as. Possible types:
3401 * % - a literal percent character. No argument is required.
3402 * b - the argument is treated as an integer, and presented as a binary number.
3403 * c - the argument is treated as an integer, and presented as the character with that ASCII value.
3404 * d - the argument is treated as an integer, and presented as a (signed) decimal number.
3405 * e - the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands
3406 * for the number of digits after the decimal point.
3407 * E - like %e but uses uppercase letter (e.g. 1.2E+2).
3408 * u - the argument is treated as an integer, and presented as an unsigned decimal number.
3409 * f - the argument is treated as a float, and presented as a floating-point number (locale aware).
3410 * F - the argument is treated as a float, and presented as a floating-point number (non-locale aware).
3411 * g - shorter of %e and %f.
3412 * G - shorter of %E and %f.
3413 * o - the argument is treated as an integer, and presented as an octal number.
3414 * s - the argument is treated as and presented as a string.
3415 * x - the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters).
3416 * X - the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters).
3417 */
3418/*
3419 * This implementation is based on the one found in the SQLite3 source tree.
3420 */
3421#define JX9_FMT_BUFSIZ 1024 /* Conversion buffer size */
3422/*
3423** Conversion types fall into various categories as defined by the
3424** following enumeration.
3425*/
3426#define JX9_FMT_RADIX 1 /* Integer types.%d, %x, %o, and so forth */
3427#define JX9_FMT_FLOAT 2 /* Floating point.%f */
3428#define JX9_FMT_EXP 3 /* Exponentional notation.%e and %E */
3429#define JX9_FMT_GENERIC 4 /* Floating or exponential, depending on exponent.%g */
3430#define JX9_FMT_SIZE 5 /* Total number of characters processed so far.%n */
3431#define JX9_FMT_STRING 6 /* Strings.%s */
3432#define JX9_FMT_PERCENT 7 /* Percent symbol.%% */
3433#define JX9_FMT_CHARX 8 /* Characters.%c */
3434#define JX9_FMT_ERROR 9 /* Used to indicate no such conversion type */
3435/*
3436** Allowed values for jx9_fmt_info.flags
3437*/
3438#define JX9_FMT_FLAG_SIGNED 0x01
3439#define JX9_FMT_FLAG_UNSIGNED 0x02
3440/*
3441** Each builtin conversion character (ex: the 'd' in "%d") is described
3442** by an instance of the following structure
3443*/
3444typedef struct jx9_fmt_info jx9_fmt_info;
3445struct jx9_fmt_info
3446{
3447 char fmttype; /* The format field code letter [i.e: 'd', 's', 'x'] */
3448 sxu8 base; /* The base for radix conversion */
3449 int flags; /* One or more of JX9_FMT_FLAG_ constants below */
3450 sxu8 type; /* Conversion paradigm */
3451 char *charset; /* The character set for conversion */
3452 char *prefix; /* Prefix on non-zero values in alt format */
3453};
3454#ifndef JX9_OMIT_FLOATING_POINT
3455/*
3456** "*val" is a double such that 0.1 <= *val < 10.0
3457** Return the ascii code for the leading digit of *val, then
3458** multiply "*val" by 10.0 to renormalize.
3459**
3460** Example:
3461** input: *val = 3.14159
3462** output: *val = 1.4159 function return = '3'
3463**
3464** The counter *cnt is incremented each time. After counter exceeds
3465** 16 (the number of significant digits in a 64-bit float) '0' is
3466** always returned.
3467*/
3468static int vxGetdigit(sxlongreal *val, int *cnt)
3469{
3470 sxlongreal d;
3471 int digit;
3472
3473 if( (*cnt)++ >= 16 ){
3474 return '0';
3475 }
3476 digit = (int)*val;
3477 d = digit;
3478 *val = (*val - d)*10.0;
3479 return digit + '0' ;
3480}
3481#endif /* JX9_OMIT_FLOATING_POINT */
3482/*
3483 * The following table is searched linearly, so it is good to put the most frequently
3484 * used conversion types first.
3485 */
3486static const jx9_fmt_info aFmt[] = {
3487 { 'd', 10, JX9_FMT_FLAG_SIGNED, JX9_FMT_RADIX, "0123456789", 0 },
3488 { 's', 0, 0, JX9_FMT_STRING, 0, 0 },
3489 { 'c', 0, 0, JX9_FMT_CHARX, 0, 0 },
3490 { 'x', 16, 0, JX9_FMT_RADIX, "0123456789abcdef", "x0" },
3491 { 'X', 16, 0, JX9_FMT_RADIX, "0123456789ABCDEF", "X0" },
3492 { 'b', 2, 0, JX9_FMT_RADIX, "01", "b0"},
3493 { 'o', 8, 0, JX9_FMT_RADIX, "01234567", "0" },
3494 { 'u', 10, 0, JX9_FMT_RADIX, "0123456789", 0 },
3495 { 'f', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT, 0, 0 },
3496 { 'F', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT, 0, 0 },
3497 { 'e', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP, "e", 0 },
3498 { 'E', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP, "E", 0 },
3499 { 'g', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC, "e", 0 },
3500 { 'G', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC, "E", 0 },
3501 { '%', 0, 0, JX9_FMT_PERCENT, 0, 0 }
3502};
3503/*
3504 * Format a given string.
3505 * The root program. All variations call this core.
3506 * INPUTS:
3507 * xConsumer This is a pointer to a function taking four arguments
3508 * 1. A pointer to the call context.
3509 * 2. A pointer to the list of characters to be output
3510 * (Note, this list is NOT null terminated.)
3511 * 3. An integer number of characters to be output.
3512 * (Note: This number might be zero.)
3513 * 4. Upper layer private data.
3514 * zIn This is the format string, as in the usual print.
3515 * apArg This is a pointer to a list of arguments.
3516 */
3517JX9_PRIVATE sxi32 jx9InputFormat(
3518 int (*xConsumer)(jx9_context *, const char *, int, void *), /* Format consumer */
3519 jx9_context *pCtx, /* call context */
3520 const char *zIn, /* Format string */
3521 int nByte, /* Format string length */
3522 int nArg, /* Total argument of the given arguments */
3523 jx9_value **apArg, /* User arguments */
3524 void *pUserData, /* Last argument to xConsumer() */
3525 int vf /* TRUE if called from vfprintf, vsprintf context */
3526 )
3527{
3528 char spaces[] = " ";
3529#define etSPACESIZE ((int)sizeof(spaces)-1)
3530 const char *zCur, *zEnd = &zIn[nByte];
3531 char *zBuf, zWorker[JX9_FMT_BUFSIZ]; /* Working buffer */
3532 const jx9_fmt_info *pInfo; /* Pointer to the appropriate info structure */
3533 int flag_alternateform; /* True if "#" flag is present */
3534 int flag_leftjustify; /* True if "-" flag is present */
3535 int flag_blanksign; /* True if " " flag is present */
3536 int flag_plussign; /* True if "+" flag is present */
3537 int flag_zeropad; /* True if field width constant starts with zero */
3538 jx9_value *pArg; /* Current processed argument */
3539 jx9_int64 iVal;
3540 int precision; /* Precision of the current field */
3541 char *zExtra;
3542 int c, rc, n;
3543 int length; /* Length of the field */
3544 int prefix;
3545 sxu8 xtype; /* Conversion paradigm */
3546 int width; /* Width of the current field */
3547 int idx;
3548 n = (vf == TRUE) ? 0 : 1;
3549#define NEXT_ARG ( n < nArg ? apArg[n++] : 0 )
3550 /* Start the format process */
3551 for(;;){
3552 zCur = zIn;
3553 while( zIn < zEnd && zIn[0] != '%' ){
3554 zIn++;
3555 }
3556 if( zCur < zIn ){
3557 /* Consume chunk verbatim */
3558 rc = xConsumer(pCtx, zCur, (int)(zIn-zCur), pUserData);
3559 if( rc == SXERR_ABORT ){
3560 /* Callback request an operation abort */
3561 break;
3562 }
3563 }
3564 if( zIn >= zEnd ){
3565 /* No more input to process, break immediately */
3566 break;
3567 }
3568 /* Find out what flags are present */
3569 flag_leftjustify = flag_plussign = flag_blanksign =
3570 flag_alternateform = flag_zeropad = 0;
3571 zIn++; /* Jump the precent sign */
3572 do{
3573 c = zIn[0];
3574 switch( c ){
3575 case '-': flag_leftjustify = 1; c = 0; break;
3576 case '+': flag_plussign = 1; c = 0; break;
3577 case ' ': flag_blanksign = 1; c = 0; break;
3578 case '#': flag_alternateform = 1; c = 0; break;
3579 case '0': flag_zeropad = 1; c = 0; break;
3580 case '\'':
3581 zIn++;
3582 if( zIn < zEnd ){
3583 /* An alternate padding character can be specified by prefixing it with a single quote (') */
3584 c = zIn[0];
3585 for(idx = 0 ; idx < etSPACESIZE ; ++idx ){
3586 spaces[idx] = (char)c;
3587 }
3588 c = 0;
3589 }
3590 break;
3591 default: break;
3592 }
3593 }while( c==0 && (zIn++ < zEnd) );
3594 /* Get the field width */
3595 width = 0;
3596 while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
3597 width = width*10 + (zIn[0] - '0');
3598 zIn++;
3599 }
3600 if( zIn < zEnd && zIn[0] == '$' ){
3601 /* Position specifer */
3602 if( width > 0 ){
3603 n = width;
3604 if( vf && n > 0 ){
3605 n--;
3606 }
3607 }
3608 zIn++;
3609 width = 0;
3610 if( zIn < zEnd && zIn[0] == '0' ){
3611 flag_zeropad = 1;
3612 zIn++;
3613 }
3614 while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
3615 width = width*10 + (zIn[0] - '0');
3616 zIn++;
3617 }
3618 }
3619 if( width > JX9_FMT_BUFSIZ-10 ){
3620 width = JX9_FMT_BUFSIZ-10;
3621 }
3622 /* Get the precision */
3623 precision = -1;
3624 if( zIn < zEnd && zIn[0] == '.' ){
3625 precision = 0;
3626 zIn++;
3627 while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
3628 precision = precision*10 + (zIn[0] - '0');
3629 zIn++;
3630 }
3631 }
3632 if( zIn >= zEnd ){
3633 /* No more input */
3634 break;
3635 }
3636 /* Fetch the info entry for the field */
3637 pInfo = 0;
3638 xtype = JX9_FMT_ERROR;
3639 c = zIn[0];
3640 zIn++; /* Jump the format specifer */
3641 for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){
3642 if( c==aFmt[idx].fmttype ){
3643 pInfo = &aFmt[idx];
3644 xtype = pInfo->type;
3645 break;
3646 }
3647 }
3648 zBuf = zWorker; /* Point to the working buffer */
3649 length = 0;
3650 zExtra = 0;
3651 /*
3652 ** At this point, variables are initialized as follows:
3653 **
3654 ** flag_alternateform TRUE if a '#' is present.
3655 ** flag_plussign TRUE if a '+' is present.
3656 ** flag_leftjustify TRUE if a '-' is present or if the
3657 ** field width was negative.
3658 ** flag_zeropad TRUE if the width began with 0.
3659 ** the conversion character.
3660 ** flag_blanksign TRUE if a ' ' is present.
3661 ** width The specified field width. This is
3662 ** always non-negative. Zero is the default.
3663 ** precision The specified precision. The default
3664 ** is -1.
3665 */
3666 switch(xtype){
3667 case JX9_FMT_PERCENT:
3668 /* A literal percent character */
3669 zWorker[0] = '%';
3670 length = (int)sizeof(char);
3671 break;
3672 case JX9_FMT_CHARX:
3673 /* The argument is treated as an integer, and presented as the character
3674 * with that ASCII value
3675 */
3676 pArg = NEXT_ARG;
3677 if( pArg == 0 ){
3678 c = 0;
3679 }else{
3680 c = jx9_value_to_int(pArg);
3681 }
3682 /* NUL byte is an acceptable value */
3683 zWorker[0] = (char)c;
3684 length = (int)sizeof(char);
3685 break;
3686 case JX9_FMT_STRING:
3687 /* the argument is treated as and presented as a string */
3688 pArg = NEXT_ARG;
3689 if( pArg == 0 ){
3690 length = 0;
3691 }else{
3692 zBuf = (char *)jx9_value_to_string(pArg, &length);
3693 }
3694 if( length < 1 ){
3695 zBuf = " ";
3696 length = (int)sizeof(char);
3697 }
3698 if( precision>=0 && precision<length ){
3699 length = precision;
3700 }
3701 if( flag_zeropad ){
3702 /* zero-padding works on strings too */
3703 for(idx = 0 ; idx < etSPACESIZE ; ++idx ){
3704 spaces[idx] = '0';
3705 }
3706 }
3707 break;
3708 case JX9_FMT_RADIX:
3709 pArg = NEXT_ARG;
3710 if( pArg == 0 ){
3711 iVal = 0;
3712 }else{
3713 iVal = jx9_value_to_int64(pArg);
3714 }
3715 /* Limit the precision to prevent overflowing buf[] during conversion */
3716 if( precision>JX9_FMT_BUFSIZ-40 ){
3717 precision = JX9_FMT_BUFSIZ-40;
3718 }
3719#if 1
3720 /* For the format %#x, the value zero is printed "0" not "0x0".
3721 ** I think this is stupid.*/
3722 if( iVal==0 ) flag_alternateform = 0;
3723#else
3724 /* More sensible: turn off the prefix for octal (to prevent "00"),
3725 ** but leave the prefix for hex.*/
3726 if( iVal==0 && pInfo->base==8 ) flag_alternateform = 0;
3727#endif
3728 if( pInfo->flags & JX9_FMT_FLAG_SIGNED ){
3729 if( iVal<0 ){
3730 iVal = -iVal;
3731 /* Ticket 1433-003 */
3732 if( iVal < 0 ){
3733 /* Overflow */
3734 iVal= 0x7FFFFFFFFFFFFFFF;
3735 }
3736 prefix = '-';
3737 }else if( flag_plussign ) prefix = '+';
3738 else if( flag_blanksign ) prefix = ' ';
3739 else prefix = 0;
3740 }else{
3741 if( iVal<0 ){
3742 iVal = -iVal;
3743 /* Ticket 1433-003 */
3744 if( iVal < 0 ){
3745 /* Overflow */
3746 iVal= 0x7FFFFFFFFFFFFFFF;
3747 }
3748 }
3749 prefix = 0;
3750 }
3751 if( flag_zeropad && precision<width-(prefix!=0) ){
3752 precision = width-(prefix!=0);
3753 }
3754 zBuf = &zWorker[JX9_FMT_BUFSIZ-1];
3755 {
3756 register char *cset; /* Use registers for speed */
3757 register int base;
3758 cset = pInfo->charset;
3759 base = pInfo->base;
3760 do{ /* Convert to ascii */
3761 *(--zBuf) = cset[iVal%base];
3762 iVal = iVal/base;
3763 }while( iVal>0 );
3764 }
3765 length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf;
3766 for(idx=precision-length; idx>0; idx--){
3767 *(--zBuf) = '0'; /* Zero pad */
3768 }
3769 if( prefix ) *(--zBuf) = (char)prefix; /* Add sign */
3770 if( flag_alternateform && pInfo->prefix ){ /* Add "0" or "0x" */
3771 char *pre, x;
3772 pre = pInfo->prefix;
3773 if( *zBuf!=pre[0] ){
3774 for(pre=pInfo->prefix; (x=(*pre))!=0; pre++) *(--zBuf) = x;
3775 }
3776 }
3777 length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf;
3778 break;
3779 case JX9_FMT_FLOAT:
3780 case JX9_FMT_EXP:
3781 case JX9_FMT_GENERIC:{
3782#ifndef JX9_OMIT_FLOATING_POINT
3783 long double realvalue;
3784 int exp; /* exponent of real numbers */
3785 double rounder; /* Used for rounding floating point values */
3786 int flag_dp; /* True if decimal point should be shown */
3787 int flag_rtz; /* True if trailing zeros should be removed */
3788 int flag_exp; /* True to force display of the exponent */
3789 int nsd; /* Number of significant digits returned */
3790 pArg = NEXT_ARG;
3791 if( pArg == 0 ){
3792 realvalue = 0;
3793 }else{
3794 realvalue = jx9_value_to_double(pArg);
3795 }
3796 if( precision<0 ) precision = 6; /* Set default precision */
3797 if( precision>JX9_FMT_BUFSIZ-40) precision = JX9_FMT_BUFSIZ-40;
3798 if( realvalue<0.0 ){
3799 realvalue = -realvalue;
3800 prefix = '-';
3801 }else{
3802 if( flag_plussign ) prefix = '+';
3803 else if( flag_blanksign ) prefix = ' ';
3804 else prefix = 0;
3805 }
3806 if( pInfo->type==JX9_FMT_GENERIC && precision>0 ) precision--;
3807 rounder = 0.0;
3808#if 0
3809 /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */
3810 for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
3811#else
3812 /* It makes more sense to use 0.5 */
3813 for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
3814#endif
3815 if( pInfo->type==JX9_FMT_FLOAT ) realvalue += rounder;
3816 /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
3817 exp = 0;
3818 if( realvalue>0.0 ){
3819 while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
3820 while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
3821 while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
3822 while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
3823 if( exp>350 || exp<-350 ){
3824 zBuf = "NaN";
3825 length = 3;
3826 break;
3827 }
3828 }
3829 zBuf = zWorker;
3830 /*
3831 ** If the field type is etGENERIC, then convert to either etEXP
3832 ** or etFLOAT, as appropriate.
3833 */
3834 flag_exp = xtype==JX9_FMT_EXP;
3835 if( xtype!=JX9_FMT_FLOAT ){
3836 realvalue += rounder;
3837 if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
3838 }
3839 if( xtype==JX9_FMT_GENERIC ){
3840 flag_rtz = !flag_alternateform;
3841 if( exp<-4 || exp>precision ){
3842 xtype = JX9_FMT_EXP;
3843 }else{
3844 precision = precision - exp;
3845 xtype = JX9_FMT_FLOAT;
3846 }
3847 }else{
3848 flag_rtz = 0;
3849 }
3850 /*
3851 ** The "exp+precision" test causes output to be of type etEXP if
3852 ** the precision is too large to fit in buf[].
3853 */
3854 nsd = 0;
3855 if( xtype==JX9_FMT_FLOAT && exp+precision<JX9_FMT_BUFSIZ-30 ){
3856 flag_dp = (precision>0 || flag_alternateform);
3857 if( prefix ) *(zBuf++) = (char)prefix; /* Sign */
3858 if( exp<0 ) *(zBuf++) = '0'; /* Digits before "." */
3859 else for(; exp>=0; exp--) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
3860 if( flag_dp ) *(zBuf++) = '.'; /* The decimal point */
3861 for(exp++; exp<0 && precision>0; precision--, exp++){
3862 *(zBuf++) = '0';
3863 }
3864 while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
3865 *(zBuf--) = 0; /* Null terminate */
3866 if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */
3867 while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0;
3868 if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0;
3869 }
3870 zBuf++; /* point to next free slot */
3871 }else{ /* etEXP or etGENERIC */
3872 flag_dp = (precision>0 || flag_alternateform);
3873 if( prefix ) *(zBuf++) = (char)prefix; /* Sign */
3874 *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd); /* First digit */
3875 if( flag_dp ) *(zBuf++) = '.'; /* Decimal point */
3876 while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
3877 zBuf--; /* point to last digit */
3878 if( flag_rtz && flag_dp ){ /* Remove tail zeros */
3879 while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0;
3880 if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0;
3881 }
3882 zBuf++; /* point to next free slot */
3883 if( exp || flag_exp ){
3884 *(zBuf++) = pInfo->charset[0];
3885 if( exp<0 ){ *(zBuf++) = '-'; exp = -exp; } /* sign of exp */
3886 else { *(zBuf++) = '+'; }
3887 if( exp>=100 ){
3888 *(zBuf++) = (char)((exp/100)+'0'); /* 100's digit */
3889 exp %= 100;
3890 }
3891 *(zBuf++) = (char)(exp/10+'0'); /* 10's digit */
3892 *(zBuf++) = (char)(exp%10+'0'); /* 1's digit */
3893 }
3894 }
3895 /* The converted number is in buf[] and zero terminated.Output it.
3896 ** Note that the number is in the usual order, not reversed as with
3897 ** integer conversions.*/
3898 length = (int)(zBuf-zWorker);
3899 zBuf = zWorker;
3900 /* Special case: Add leading zeros if the flag_zeropad flag is
3901 ** set and we are not left justified */
3902 if( flag_zeropad && !flag_leftjustify && length < width){
3903 int i;
3904 int nPad = width - length;
3905 for(i=width; i>=nPad; i--){
3906 zBuf[i] = zBuf[i-nPad];
3907 }
3908 i = prefix!=0;
3909 while( nPad-- ) zBuf[i++] = '0';
3910 length = width;
3911 }
3912#else
3913 zBuf = " ";
3914 length = (int)sizeof(char);
3915#endif /* JX9_OMIT_FLOATING_POINT */
3916 break;
3917 }
3918 default:
3919 /* Invalid format specifer */
3920 zWorker[0] = '?';
3921 length = (int)sizeof(char);
3922 break;
3923 }
3924 /*
3925 ** The text of the conversion is pointed to by "zBuf" and is
3926 ** "length" characters long.The field width is "width".Do
3927 ** the output.
3928 */
3929 if( !flag_leftjustify ){
3930 register int nspace;
3931 nspace = width-length;
3932 if( nspace>0 ){
3933 while( nspace>=etSPACESIZE ){
3934 rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData);
3935 if( rc != SXRET_OK ){
3936 return SXERR_ABORT; /* Consumer routine request an operation abort */
3937 }
3938 nspace -= etSPACESIZE;
3939 }
3940 if( nspace>0 ){
3941 rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData);
3942 if( rc != SXRET_OK ){
3943 return SXERR_ABORT; /* Consumer routine request an operation abort */
3944 }
3945 }
3946 }
3947 }
3948 if( length>0 ){
3949 rc = xConsumer(pCtx, zBuf, (unsigned int)length, pUserData);
3950 if( rc != SXRET_OK ){
3951 return SXERR_ABORT; /* Consumer routine request an operation abort */
3952 }
3953 }
3954 if( flag_leftjustify ){
3955 register int nspace;
3956 nspace = width-length;
3957 if( nspace>0 ){
3958 while( nspace>=etSPACESIZE ){
3959 rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData);
3960 if( rc != SXRET_OK ){
3961 return SXERR_ABORT; /* Consumer routine request an operation abort */
3962 }
3963 nspace -= etSPACESIZE;
3964 }
3965 if( nspace>0 ){
3966 rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData);
3967 if( rc != SXRET_OK ){
3968 return SXERR_ABORT; /* Consumer routine request an operation abort */
3969 }
3970 }
3971 }
3972 }
3973 }/* for(;;) */
3974 return SXRET_OK;
3975}
3976/*
3977 * Callback [i.e: Formatted input consumer] of the sprintf function.
3978 */
3979static int sprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
3980{
3981 /* Consume directly */
3982 jx9_result_string(pCtx, zInput, nLen);
3983 SXUNUSED(pUserData); /* cc warning */
3984 return JX9_OK;
3985}
3986/*
3987 * string sprintf(string $format[, mixed $args [, mixed $... ]])
3988 * Return a formatted string.
3989 * Parameters
3990 * $format
3991 * The format string (see block comment above)
3992 * Return
3993 * A string produced according to the formatting string format.
3994 */
3995static int jx9Builtin_sprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
3996{
3997 const char *zFormat;
3998 int nLen;
3999 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
4000 /* Missing/Invalid arguments, return the empty string */
4001 jx9_result_string(pCtx, "", 0);
4002 return JX9_OK;
4003 }
4004 /* Extract the string format */
4005 zFormat = jx9_value_to_string(apArg[0], &nLen);
4006 if( nLen < 1 ){
4007 /* Empty string */
4008 jx9_result_string(pCtx, "", 0);
4009 return JX9_OK;
4010 }
4011 /* Format the string */
4012 jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, nArg, apArg, 0, FALSE);
4013 return JX9_OK;
4014}
4015/*
4016 * Callback [i.e: Formatted input consumer] of the printf function.
4017 */
4018static int printfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
4019{
4020 jx9_int64 *pCounter = (jx9_int64 *)pUserData;
4021 /* Call the VM output consumer directly */
4022 jx9_context_output(pCtx, zInput, nLen);
4023 /* Increment counter */
4024 *pCounter += nLen;
4025 return JX9_OK;
4026}
4027/*
4028 * int64 printf(string $format[, mixed $args[, mixed $... ]])
4029 * Output a formatted string.
4030 * Parameters
4031 * $format
4032 * See sprintf() for a description of format.
4033 * Return
4034 * The length of the outputted string.
4035 */
4036static int jx9Builtin_printf(jx9_context *pCtx, int nArg, jx9_value **apArg)
4037{
4038 jx9_int64 nCounter = 0;
4039 const char *zFormat;
4040 int nLen;
4041 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
4042 /* Missing/Invalid arguments, return 0 */
4043 jx9_result_int(pCtx, 0);
4044 return JX9_OK;
4045 }
4046 /* Extract the string format */
4047 zFormat = jx9_value_to_string(apArg[0], &nLen);
4048 if( nLen < 1 ){
4049 /* Empty string */
4050 jx9_result_int(pCtx, 0);
4051 return JX9_OK;
4052 }
4053 /* Format the string */
4054 jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, nArg, apArg, (void *)&nCounter, FALSE);
4055 /* Return the length of the outputted string */
4056 jx9_result_int64(pCtx, nCounter);
4057 return JX9_OK;
4058}
4059/*
4060 * int vprintf(string $format, array $args)
4061 * Output a formatted string.
4062 * Parameters
4063 * $format
4064 * See sprintf() for a description of format.
4065 * Return
4066 * The length of the outputted string.
4067 */
4068static int jx9Builtin_vprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
4069{
4070 jx9_int64 nCounter = 0;
4071 const char *zFormat;
4072 jx9_hashmap *pMap;
4073 SySet sArg;
4074 int nLen, n;
4075 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
4076 /* Missing/Invalid arguments, return 0 */
4077 jx9_result_int(pCtx, 0);
4078 return JX9_OK;
4079 }
4080 /* Extract the string format */
4081 zFormat = jx9_value_to_string(apArg[0], &nLen);
4082 if( nLen < 1 ){
4083 /* Empty string */
4084 jx9_result_int(pCtx, 0);
4085 return JX9_OK;
4086 }
4087 /* Point to the hashmap */
4088 pMap = (jx9_hashmap *)apArg[1]->x.pOther;
4089 /* Extract arguments from the hashmap */
4090 n = jx9HashmapValuesToSet(pMap, &sArg);
4091 /* Format the string */
4092 jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&nCounter, TRUE);
4093 /* Return the length of the outputted string */
4094 jx9_result_int64(pCtx, nCounter);
4095 /* Release the container */
4096 SySetRelease(&sArg);
4097 return JX9_OK;
4098}
4099/*
4100 * int vsprintf(string $format, array $args)
4101 * Output a formatted string.
4102 * Parameters
4103 * $format
4104 * See sprintf() for a description of format.
4105 * Return
4106 * A string produced according to the formatting string format.
4107 */
4108static int jx9Builtin_vsprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
4109{
4110 const char *zFormat;
4111 jx9_hashmap *pMap;
4112 SySet sArg;
4113 int nLen, n;
4114 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
4115 /* Missing/Invalid arguments, return the empty string */
4116 jx9_result_string(pCtx, "", 0);
4117 return JX9_OK;
4118 }
4119 /* Extract the string format */
4120 zFormat = jx9_value_to_string(apArg[0], &nLen);
4121 if( nLen < 1 ){
4122 /* Empty string */
4123 jx9_result_string(pCtx, "", 0);
4124 return JX9_OK;
4125 }
4126 /* Point to hashmap */
4127 pMap = (jx9_hashmap *)apArg[1]->x.pOther;
4128 /* Extract arguments from the hashmap */
4129 n = jx9HashmapValuesToSet(pMap, &sArg);
4130 /* Format the string */
4131 jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), 0, TRUE);
4132 /* Release the container */
4133 SySetRelease(&sArg);
4134 return JX9_OK;
4135}
4136/*
4137 * string size_format(int64 $size)
4138 * Return a smart string represenation of the given size [i.e: 64-bit integer]
4139 * Example:
4140 * print size_format(1*1024*1024*1024);// 1GB
4141 * print size_format(512*1024*1024); // 512 MB
4142 * print size_format(file_size(/path/to/my/file_8192)); //8KB
4143 * Parameter
4144 * $size
4145 * Entity size in bytes.
4146 * Return
4147 * Formatted string representation of the given size.
4148 */
4149static int jx9Builtin_size_format(jx9_context *pCtx, int nArg, jx9_value **apArg)
4150{
4151 /*Kilo*/ /*Mega*/ /*Giga*/ /*Tera*/ /*Peta*/ /*Exa*/ /*Zeta*/
4152 static const char zUnit[] = {"KMGTPEZ"};
4153 sxi32 nRest, i_32;
4154 jx9_int64 iSize;
4155 int c = -1; /* index in zUnit[] */
4156
4157 if( nArg < 1 ){
4158 /* Missing argument, return the empty string */
4159 jx9_result_string(pCtx, "", 0);
4160 return JX9_OK;
4161 }
4162 /* Extract the given size */
4163 iSize = jx9_value_to_int64(apArg[0]);
4164 if( iSize < 100 /* Bytes */ ){
4165 /* Don't bother formatting, return immediately */
4166 jx9_result_string(pCtx, "0.1 KB", (int)sizeof("0.1 KB")-1);
4167 return JX9_OK;
4168 }
4169 for(;;){
4170 nRest = (sxi32)(iSize & 0x3FF);
4171 iSize >>= 10;
4172 c++;
4173 if( (iSize & (~0 ^ 1023)) == 0 ){
4174 break;
4175 }
4176 }
4177 nRest /= 100;
4178 if( nRest > 9 ){
4179 nRest = 9;
4180 }
4181 if( iSize > 999 ){
4182 c++;
4183 nRest = 9;
4184 iSize = 0;
4185 }
4186 i_32 = (sxi32)iSize;
4187 /* Format */
4188 jx9_result_string_format(pCtx, "%d.%d %cB", i_32, nRest, zUnit[c]);
4189 return JX9_OK;
4190}
4191#if !defined(JX9_DISABLE_HASH_FUNC)
4192/*
4193 * string md5(string $str[, bool $raw_output = false])
4194 * Calculate the md5 hash of a string.
4195 * Parameter
4196 * $str
4197 * Input string
4198 * $raw_output
4199 * If the optional raw_output is set to TRUE, then the md5 digest
4200 * is instead returned in raw binary format with a length of 16.
4201 * Return
4202 * MD5 Hash as a 32-character hexadecimal string.
4203 */
4204static int jx9Builtin_md5(jx9_context *pCtx, int nArg, jx9_value **apArg)
4205{
4206 unsigned char zDigest[16];
4207 int raw_output = FALSE;
4208 const void *pIn;
4209 int nLen;
4210 if( nArg < 1 ){
4211 /* Missing arguments, return the empty string */
4212 jx9_result_string(pCtx, "", 0);
4213 return JX9_OK;
4214 }
4215 /* Extract the input string */
4216 pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
4217 if( nLen < 1 ){
4218 /* Empty string */
4219 jx9_result_string(pCtx, "", 0);
4220 return JX9_OK;
4221 }
4222 if( nArg > 1 && jx9_value_is_bool(apArg[1])){
4223 raw_output = jx9_value_to_bool(apArg[1]);
4224 }
4225 /* Compute the MD5 digest */
4226 SyMD5Compute(pIn, (sxu32)nLen, zDigest);
4227 if( raw_output ){
4228 /* Output raw digest */
4229 jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest));
4230 }else{
4231 /* Perform a binary to hex conversion */
4232 SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx);
4233 }
4234 return JX9_OK;
4235}
4236/*
4237 * string sha1(string $str[, bool $raw_output = false])
4238 * Calculate the sha1 hash of a string.
4239 * Parameter
4240 * $str
4241 * Input string
4242 * $raw_output
4243 * If the optional raw_output is set to TRUE, then the md5 digest
4244 * is instead returned in raw binary format with a length of 16.
4245 * Return
4246 * SHA1 Hash as a 40-character hexadecimal string.
4247 */
4248static int jx9Builtin_sha1(jx9_context *pCtx, int nArg, jx9_value **apArg)
4249{
4250 unsigned char zDigest[20];
4251 int raw_output = FALSE;
4252 const void *pIn;
4253 int nLen;
4254 if( nArg < 1 ){
4255 /* Missing arguments, return the empty string */
4256 jx9_result_string(pCtx, "", 0);
4257 return JX9_OK;
4258 }
4259 /* Extract the input string */
4260 pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
4261 if( nLen < 1 ){
4262 /* Empty string */
4263 jx9_result_string(pCtx, "", 0);
4264 return JX9_OK;
4265 }
4266 if( nArg > 1 && jx9_value_is_bool(apArg[1])){
4267 raw_output = jx9_value_to_bool(apArg[1]);
4268 }
4269 /* Compute the SHA1 digest */
4270 SySha1Compute(pIn, (sxu32)nLen, zDigest);
4271 if( raw_output ){
4272 /* Output raw digest */
4273 jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest));
4274 }else{
4275 /* Perform a binary to hex conversion */
4276 SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx);
4277 }
4278 return JX9_OK;
4279}
4280/*
4281 * int64 crc32(string $str)
4282 * Calculates the crc32 polynomial of a strin.
4283 * Parameter
4284 * $str
4285 * Input string
4286 * Return
4287 * CRC32 checksum of the given input (64-bit integer).
4288 */
4289static int jx9Builtin_crc32(jx9_context *pCtx, int nArg, jx9_value **apArg)
4290{
4291 const void *pIn;
4292 sxu32 nCRC;
4293 int nLen;
4294 if( nArg < 1 ){
4295 /* Missing arguments, return 0 */
4296 jx9_result_int(pCtx, 0);
4297 return JX9_OK;
4298 }
4299 /* Extract the input string */
4300 pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
4301 if( nLen < 1 ){
4302 /* Empty string */
4303 jx9_result_int(pCtx, 0);
4304 return JX9_OK;
4305 }
4306 /* Calculate the sum */
4307 nCRC = SyCrc32(pIn, (sxu32)nLen);
4308 /* Return the CRC32 as 64-bit integer */
4309 jx9_result_int64(pCtx, (jx9_int64)nCRC^ 0xFFFFFFFF);
4310 return JX9_OK;
4311}
4312#endif /* JX9_DISABLE_HASH_FUNC */
4313/*
4314 * Parse a CSV string and invoke the supplied callback for each processed xhunk.
4315 */
4316JX9_PRIVATE sxi32 jx9ProcessCsv(
4317 const char *zInput, /* Raw input */
4318 int nByte, /* Input length */
4319 int delim, /* Delimiter */
4320 int encl, /* Enclosure */
4321 int escape, /* Escape character */
4322 sxi32 (*xConsumer)(const char *, int, void *), /* User callback */
4323 void *pUserData /* Last argument to xConsumer() */
4324 )
4325{
4326 const char *zEnd = &zInput[nByte];
4327 const char *zIn = zInput;
4328 const char *zPtr;
4329 int isEnc;
4330 /* Start processing */
4331 for(;;){
4332 if( zIn >= zEnd ){
4333 /* No more input to process */
4334 break;
4335 }
4336 isEnc = 0;
4337 zPtr = zIn;
4338 /* Find the first delimiter */
4339 while( zIn < zEnd ){
4340 if( zIn[0] == delim && !isEnc){
4341 /* Delimiter found, break imediately */
4342 break;
4343 }else if( zIn[0] == encl ){
4344 /* Inside enclosure? */
4345 isEnc = !isEnc;
4346 }else if( zIn[0] == escape ){
4347 /* Escape sequence */
4348 zIn++;
4349 }
4350 /* Advance the cursor */
4351 zIn++;
4352 }
4353 if( zIn > zPtr ){
4354 int nByte = (int)(zIn-zPtr);
4355 sxi32 rc;
4356 /* Invoke the supllied callback */
4357 if( zPtr[0] == encl ){
4358 zPtr++;
4359 nByte-=2;
4360 }
4361 if( nByte > 0 ){
4362 rc = xConsumer(zPtr, nByte, pUserData);
4363 if( rc == SXERR_ABORT ){
4364 /* User callback request an operation abort */
4365 break;
4366 }
4367 }
4368 }
4369 /* Ignore trailing delimiter */
4370 while( zIn < zEnd && zIn[0] == delim ){
4371 zIn++;
4372 }
4373 }
4374 return SXRET_OK;
4375}
4376/*
4377 * Default consumer callback for the CSV parsing routine defined above.
4378 * All the processed input is insereted into an array passed as the last
4379 * argument to this callback.
4380 */
4381JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData)
4382{
4383 jx9_value *pArray = (jx9_value *)pUserData;
4384 jx9_value sEntry;
4385 SyString sToken;
4386 /* Insert the token in the given array */
4387 SyStringInitFromBuf(&sToken, zToken, nTokenLen);
4388 /* Remove trailing and leading white spcaces and null bytes */
4389 SyStringFullTrimSafe(&sToken);
4390 if( sToken.nByte < 1){
4391 return SXRET_OK;
4392 }
4393 jx9MemObjInitFromString(pArray->pVm, &sEntry, &sToken);
4394 jx9_array_add_elem(pArray, 0, &sEntry);
4395 jx9MemObjRelease(&sEntry);
4396 return SXRET_OK;
4397}
4398/*
4399 * array str_getcsv(string $input[, string $delimiter = ', '[, string $enclosure = '"' [, string $escape='\\']]])
4400 * Parse a CSV string into an array.
4401 * Parameters
4402 * $input
4403 * The string to parse.
4404 * $delimiter
4405 * Set the field delimiter (one character only).
4406 * $enclosure
4407 * Set the field enclosure character (one character only).
4408 * $escape
4409 * Set the escape character (one character only). Defaults as a backslash (\)
4410 * Return
4411 * An indexed array containing the CSV fields or NULL on failure.
4412 */
4413static int jx9Builtin_str_getcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
4414{
4415 const char *zInput, *zPtr;
4416 jx9_value *pArray;
4417 int delim = ','; /* Delimiter */
4418 int encl = '"' ; /* Enclosure */
4419 int escape = '\\'; /* Escape character */
4420 int nLen;
4421 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
4422 /* Missing/Invalid arguments, return NULL */
4423 jx9_result_null(pCtx);
4424 return JX9_OK;
4425 }
4426 /* Extract the raw input */
4427 zInput = jx9_value_to_string(apArg[0], &nLen);
4428 if( nArg > 1 ){
4429 int i;
4430 if( jx9_value_is_string(apArg[1]) ){
4431 /* Extract the delimiter */
4432 zPtr = jx9_value_to_string(apArg[1], &i);
4433 if( i > 0 ){
4434 delim = zPtr[0];
4435 }
4436 }
4437 if( nArg > 2 ){
4438 if( jx9_value_is_string(apArg[2]) ){
4439 /* Extract the enclosure */
4440 zPtr = jx9_value_to_string(apArg[2], &i);
4441 if( i > 0 ){
4442 encl = zPtr[0];
4443 }
4444 }
4445 if( nArg > 3 ){
4446 if( jx9_value_is_string(apArg[3]) ){
4447 /* Extract the escape character */
4448 zPtr = jx9_value_to_string(apArg[3], &i);
4449 if( i > 0 ){
4450 escape = zPtr[0];
4451 }
4452 }
4453 }
4454 }
4455 }
4456 /* Create our array */
4457 pArray = jx9_context_new_array(pCtx);
4458 if( pArray == 0 ){
4459 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
4460 jx9_result_null(pCtx);
4461 return JX9_OK;
4462 }
4463 /* Parse the raw input */
4464 jx9ProcessCsv(zInput, nLen, delim, encl, escape, jx9CsvConsumer, pArray);
4465 /* Return the freshly created array */
4466 jx9_result_value(pCtx, pArray);
4467 return JX9_OK;
4468}
4469/*
4470 * Extract a tag name from a raw HTML input and insert it in the given
4471 * container.
4472 * Refer to [strip_tags()].
4473 */
4474static sxi32 AddTag(SySet *pSet, const char *zTag, int nByte)
4475{
4476 const char *zEnd = &zTag[nByte];
4477 const char *zPtr;
4478 SyString sEntry;
4479 /* Strip tags */
4480 for(;;){
4481 while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?'
4482 || zTag[0] == '!' || zTag[0] == '-' || ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){
4483 zTag++;
4484 }
4485 if( zTag >= zEnd ){
4486 break;
4487 }
4488 zPtr = zTag;
4489 /* Delimit the tag */
4490 while(zTag < zEnd ){
4491 if( (unsigned char)zTag[0] >= 0xc0 ){
4492 /* UTF-8 stream */
4493 zTag++;
4494 SX_JMP_UTF8(zTag, zEnd);
4495 }else if( !SyisAlphaNum(zTag[0]) ){
4496 break;
4497 }else{
4498 zTag++;
4499 }
4500 }
4501 if( zTag > zPtr ){
4502 /* Perform the insertion */
4503 SyStringInitFromBuf(&sEntry, zPtr, (int)(zTag-zPtr));
4504 SyStringFullTrim(&sEntry);
4505 SySetPut(pSet, (const void *)&sEntry);
4506 }
4507 /* Jump the trailing '>' */
4508 zTag++;
4509 }
4510 return SXRET_OK;
4511}
4512/*
4513 * Check if the given HTML tag name is present in the given container.
4514 * Return SXRET_OK if present.SXERR_NOTFOUND otherwise.
4515 * Refer to [strip_tags()].
4516 */
4517static sxi32 FindTag(SySet *pSet, const char *zTag, int nByte)
4518{
4519 if( SySetUsed(pSet) > 0 ){
4520 const char *zCur, *zEnd = &zTag[nByte];
4521 SyString sTag;
4522 while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?' ||
4523 ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){
4524 zTag++;
4525 }
4526 /* Delimit the tag */
4527 zCur = zTag;
4528 while(zTag < zEnd ){
4529 if( (unsigned char)zTag[0] >= 0xc0 ){
4530 /* UTF-8 stream */
4531 zTag++;
4532 SX_JMP_UTF8(zTag, zEnd);
4533 }else if( !SyisAlphaNum(zTag[0]) ){
4534 break;
4535 }else{
4536 zTag++;
4537 }
4538 }
4539 SyStringInitFromBuf(&sTag, zCur, zTag-zCur);
4540 /* Trim leading white spaces and null bytes */
4541 SyStringLeftTrimSafe(&sTag);
4542 if( sTag.nByte > 0 ){
4543 SyString *aEntry, *pEntry;
4544 sxi32 rc;
4545 sxu32 n;
4546 /* Perform the lookup */
4547 aEntry = (SyString *)SySetBasePtr(pSet);
4548 for( n = 0 ; n < SySetUsed(pSet) ; ++n ){
4549 pEntry = &aEntry[n];
4550 /* Do the comparison */
4551 rc = SyStringCmp(pEntry, &sTag, SyStrnicmp);
4552 if( !rc ){
4553 return SXRET_OK;
4554 }
4555 }
4556 }
4557 }
4558 /* No such tag */
4559 return SXERR_NOTFOUND;
4560}
4561/*
4562 * This function tries to return a string [i.e: in the call context result buffer]
4563 * with all NUL bytes, HTML and JX9 tags stripped from a given string.
4564 * Refer to [strip_tags()].
4565 */
4566JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen)
4567{
4568 const char *zEnd = &zIn[nByte];
4569 const char *zPtr, *zTag;
4570 SySet sSet;
4571 /* initialize the set of allowed tags */
4572 SySetInit(&sSet, &pCtx->pVm->sAllocator, sizeof(SyString));
4573 if( nTaglen > 0 ){
4574 /* Set of allowed tags */
4575 AddTag(&sSet, zTaglist, nTaglen);
4576 }
4577 /* Set the empty string */
4578 jx9_result_string(pCtx, "", 0);
4579 /* Start processing */
4580 for(;;){
4581 if(zIn >= zEnd){
4582 /* No more input to process */
4583 break;
4584 }
4585 zPtr = zIn;
4586 /* Find a tag */
4587 while( zIn < zEnd && zIn[0] != '<' && zIn[0] != 0 /* NUL byte */ ){
4588 zIn++;
4589 }
4590 if( zIn > zPtr ){
4591 /* Consume raw input */
4592 jx9_result_string(pCtx, zPtr, (int)(zIn-zPtr));
4593 }
4594 /* Ignore trailing null bytes */
4595 while( zIn < zEnd && zIn[0] == 0 ){
4596 zIn++;
4597 }
4598 if(zIn >= zEnd){
4599 /* No more input to process */
4600 break;
4601 }
4602 if( zIn[0] == '<' ){
4603 sxi32 rc;
4604 zTag = zIn++;
4605 /* Delimit the tag */
4606 while( zIn < zEnd && zIn[0] != '>' ){
4607 zIn++;
4608 }
4609 if( zIn < zEnd ){
4610 zIn++; /* Ignore the trailing closing tag */
4611 }
4612 /* Query the set */
4613 rc = FindTag(&sSet, zTag, (int)(zIn-zTag));
4614 if( rc == SXRET_OK ){
4615 /* Keep the tag */
4616 jx9_result_string(pCtx, zTag, (int)(zIn-zTag));
4617 }
4618 }
4619 }
4620 /* Cleanup */
4621 SySetRelease(&sSet);
4622 return SXRET_OK;
4623}
4624/*
4625 * string strip_tags(string $str[, string $allowable_tags])
4626 * Strip HTML and JX9 tags from a string.
4627 * Parameters
4628 * $str
4629 * The input string.
4630 * $allowable_tags
4631 * You can use the optional second parameter to specify tags which should not be stripped.
4632 * Return
4633 * Returns the stripped string.
4634 */
4635static int jx9Builtin_strip_tags(jx9_context *pCtx, int nArg, jx9_value **apArg)
4636{
4637 const char *zTaglist = 0;
4638 const char *zString;
4639 int nTaglen = 0;
4640 int nLen;
4641 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
4642 /* Missing/Invalid arguments, return the empty string */
4643 jx9_result_string(pCtx, "", 0);
4644 return JX9_OK;
4645 }
4646 /* Point to the raw string */
4647 zString = jx9_value_to_string(apArg[0], &nLen);
4648 if( nArg > 1 && jx9_value_is_string(apArg[1]) ){
4649 /* Allowed tag */
4650 zTaglist = jx9_value_to_string(apArg[1], &nTaglen);
4651 }
4652 /* Process input */
4653 jx9StripTagsFromString(pCtx, zString, nLen, zTaglist, nTaglen);
4654 return JX9_OK;
4655}
4656/*
4657 * array str_split(string $string[, int $split_length = 1 ])
4658 * Convert a string to an array.
4659 * Parameters
4660 * $str
4661 * The input string.
4662 * $split_length
4663 * Maximum length of the chunk.
4664 * Return
4665 * If the optional split_length parameter is specified, the returned array
4666 * will be broken down into chunks with each being split_length in length, otherwise
4667 * each chunk will be one character in length. FALSE is returned if split_length is less than 1.
4668 * If the split_length length exceeds the length of string, the entire string is returned
4669 * as the first (and only) array element.
4670 */
4671static int jx9Builtin_str_split(jx9_context *pCtx, int nArg, jx9_value **apArg)
4672{
4673 const char *zString, *zEnd;
4674 jx9_value *pArray, *pValue;
4675 int split_len;
4676 int nLen;
4677 if( nArg < 1 ){
4678 /* Missing arguments, return FALSE */
4679 jx9_result_bool(pCtx, 0);
4680 return JX9_OK;
4681 }
4682 /* Point to the target string */
4683 zString = jx9_value_to_string(apArg[0], &nLen);
4684 if( nLen < 1 ){
4685 /* Nothing to process, return FALSE */
4686 jx9_result_bool(pCtx, 0);
4687 return JX9_OK;
4688 }
4689 split_len = (int)sizeof(char);
4690 if( nArg > 1 ){
4691 /* Split length */
4692 split_len = jx9_value_to_int(apArg[1]);
4693 if( split_len < 1 ){
4694 /* Invalid length, return FALSE */
4695 jx9_result_bool(pCtx, 0);
4696 return JX9_OK;
4697 }
4698 if( split_len > nLen ){
4699 split_len = nLen;
4700 }
4701 }
4702 /* Create the array and the scalar value */
4703 pArray = jx9_context_new_array(pCtx);
4704 /*Chunk value */
4705 pValue = jx9_context_new_scalar(pCtx);
4706 if( pValue == 0 || pArray == 0 ){
4707 /* Return FALSE */
4708 jx9_result_bool(pCtx, 0);
4709 return JX9_OK;
4710 }
4711 /* Point to the end of the string */
4712 zEnd = &zString[nLen];
4713 /* Perform the requested operation */
4714 for(;;){
4715 int nMax;
4716 if( zString >= zEnd ){
4717 /* No more input to process */
4718 break;
4719 }
4720 nMax = (int)(zEnd-zString);
4721 if( nMax < split_len ){
4722 split_len = nMax;
4723 }
4724 /* Copy the current chunk */
4725 jx9_value_string(pValue, zString, split_len);
4726 /* Insert it */
4727 jx9_array_add_elem(pArray, 0, pValue); /* Will make it's own copy */
4728 /* reset the string cursor */
4729 jx9_value_reset_string_cursor(pValue);
4730 /* Update position */
4731 zString += split_len;
4732 }
4733 /*
4734 * Return the array.
4735 * Don't worry about freeing memory, everything will be automatically released
4736 * upon we return from this function.
4737 */
4738 jx9_result_value(pCtx, pArray);
4739 return JX9_OK;
4740}
4741/*
4742 * Tokenize a raw string and extract the first non-space token.
4743 * Refer to [strspn()].
4744 */
4745static sxi32 ExtractNonSpaceToken(const char **pzIn, const char *zEnd, SyString *pOut)
4746{
4747 const char *zIn = *pzIn;
4748 const char *zPtr;
4749 /* Ignore leading white spaces */
4750 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
4751 zIn++;
4752 }
4753 if( zIn >= zEnd ){
4754 /* End of input */
4755 return SXERR_EOF;
4756 }
4757 zPtr = zIn;
4758 /* Extract the token */
4759 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && !SyisSpace(zIn[0]) ){
4760 zIn++;
4761 }
4762 SyStringInitFromBuf(pOut, zPtr, zIn-zPtr);
4763 /* Synchronize pointers */
4764 *pzIn = zIn;
4765 /* Return to the caller */
4766 return SXRET_OK;
4767}
4768/*
4769 * Check if the given string contains only characters from the given mask.
4770 * return the longest match.
4771 * Refer to [strspn()].
4772 */
4773static int LongestStringMask(const char *zString, int nLen, const char *zMask, int nMaskLen)
4774{
4775 const char *zEnd = &zString[nLen];
4776 const char *zIn = zString;
4777 int i, c;
4778 for(;;){
4779 if( zString >= zEnd ){
4780 break;
4781 }
4782 /* Extract current character */
4783 c = zString[0];
4784 /* Perform the lookup */
4785 for( i = 0 ; i < nMaskLen ; i++ ){
4786 if( c == zMask[i] ){
4787 /* Character found */
4788 break;
4789 }
4790 }
4791 if( i >= nMaskLen ){
4792 /* Character not in the current mask, break immediately */
4793 break;
4794 }
4795 /* Advance cursor */
4796 zString++;
4797 }
4798 /* Longest match */
4799 return (int)(zString-zIn);
4800}
4801/*
4802 * Do the reverse operation of the previous function [i.e: LongestStringMask()].
4803 * Refer to [strcspn()].
4804 */
4805static int LongestStringMask2(const char *zString, int nLen, const char *zMask, int nMaskLen)
4806{
4807 const char *zEnd = &zString[nLen];
4808 const char *zIn = zString;
4809 int i, c;
4810 for(;;){
4811 if( zString >= zEnd ){
4812 break;
4813 }
4814 /* Extract current character */
4815 c = zString[0];
4816 /* Perform the lookup */
4817 for( i = 0 ; i < nMaskLen ; i++ ){
4818 if( c == zMask[i] ){
4819 break;
4820 }
4821 }
4822 if( i < nMaskLen ){
4823 /* Character in the current mask, break immediately */
4824 break;
4825 }
4826 /* Advance cursor */
4827 zString++;
4828 }
4829 /* Longest match */
4830 return (int)(zString-zIn);
4831}
4832/*
4833 * int strspn(string $str, string $mask[, int $start[, int $length]])
4834 * Finds the length of the initial segment of a string consisting entirely
4835 * of characters contained within a given mask.
4836 * Parameters
4837 * $str
4838 * The input string.
4839 * $mask
4840 * The list of allowable characters.
4841 * $start
4842 * The position in subject to start searching.
4843 * If start is given and is non-negative, then strspn() will begin examining
4844 * subject at the start'th position. For instance, in the string 'abcdef', the character
4845 * at position 0 is 'a', the character at position 2 is 'c', and so forth.
4846 * If start is given and is negative, then strspn() will begin examining subject at the
4847 * start'th position from the end of subject.
4848 * $length
4849 * The length of the segment from subject to examine.
4850 * If length is given and is non-negative, then subject will be examined for length
4851 * characters after the starting position.
4852 * If lengthis given and is negative, then subject will be examined from the starting
4853 * position up to length characters from the end of subject.
4854 * Return
4855 * Returns the length of the initial segment of subject which consists entirely of characters
4856 * in mask.
4857 */
4858static int jx9Builtin_strspn(jx9_context *pCtx, int nArg, jx9_value **apArg)
4859{
4860 const char *zString, *zMask, *zEnd;
4861 int iMasklen, iLen;
4862 SyString sToken;
4863 int iCount = 0;
4864 int rc;
4865 if( nArg < 2 ){
4866 /* Missing agruments, return zero */
4867 jx9_result_int(pCtx, 0);
4868 return JX9_OK;
4869 }
4870 /* Extract the target string */
4871 zString = jx9_value_to_string(apArg[0], &iLen);
4872 /* Extract the mask */
4873 zMask = jx9_value_to_string(apArg[1], &iMasklen);
4874 if( iLen < 1 || iMasklen < 1 ){
4875 /* Nothing to process, return zero */
4876 jx9_result_int(pCtx, 0);
4877 return JX9_OK;
4878 }
4879 if( nArg > 2 ){
4880 int nOfft;
4881 /* Extract the offset */
4882 nOfft = jx9_value_to_int(apArg[2]);
4883 if( nOfft < 0 ){
4884 const char *zBase = &zString[iLen + nOfft];
4885 if( zBase > zString ){
4886 iLen = (int)(&zString[iLen]-zBase);
4887 zString = zBase;
4888 }else{
4889 /* Invalid offset */
4890 jx9_result_int(pCtx, 0);
4891 return JX9_OK;
4892 }
4893 }else{
4894 if( nOfft >= iLen ){
4895 /* Invalid offset */
4896 jx9_result_int(pCtx, 0);
4897 return JX9_OK;
4898 }else{
4899 /* Update offset */
4900 zString += nOfft;
4901 iLen -= nOfft;
4902 }
4903 }
4904 if( nArg > 3 ){
4905 int iUserlen;
4906 /* Extract the desired length */
4907 iUserlen = jx9_value_to_int(apArg[3]);
4908 if( iUserlen > 0 && iUserlen < iLen ){
4909 iLen = iUserlen;
4910 }
4911 }
4912 }
4913 /* Point to the end of the string */
4914 zEnd = &zString[iLen];
4915 /* Extract the first non-space token */
4916 rc = ExtractNonSpaceToken(&zString, zEnd, &sToken);
4917 if( rc == SXRET_OK && sToken.nByte > 0 ){
4918 /* Compare against the current mask */
4919 iCount = LongestStringMask(sToken.zString, (int)sToken.nByte, zMask, iMasklen);
4920 }
4921 /* Longest match */
4922 jx9_result_int(pCtx, iCount);
4923 return JX9_OK;
4924}
4925/*
4926 * int strcspn(string $str, string $mask[, int $start[, int $length]])
4927 * Find length of initial segment not matching mask.
4928 * Parameters
4929 * $str
4930 * The input string.
4931 * $mask
4932 * The list of not allowed characters.
4933 * $start
4934 * The position in subject to start searching.
4935 * If start is given and is non-negative, then strspn() will begin examining
4936 * subject at the start'th position. For instance, in the string 'abcdef', the character
4937 * at position 0 is 'a', the character at position 2 is 'c', and so forth.
4938 * If start is given and is negative, then strspn() will begin examining subject at the
4939 * start'th position from the end of subject.
4940 * $length
4941 * The length of the segment from subject to examine.
4942 * If length is given and is non-negative, then subject will be examined for length
4943 * characters after the starting position.
4944 * If lengthis given and is negative, then subject will be examined from the starting
4945 * position up to length characters from the end of subject.
4946 * Return
4947 * Returns the length of the segment as an integer.
4948 */
4949static int jx9Builtin_strcspn(jx9_context *pCtx, int nArg, jx9_value **apArg)
4950{
4951 const char *zString, *zMask, *zEnd;
4952 int iMasklen, iLen;
4953 SyString sToken;
4954 int iCount = 0;
4955 int rc;
4956 if( nArg < 2 ){
4957 /* Missing agruments, return zero */
4958 jx9_result_int(pCtx, 0);
4959 return JX9_OK;
4960 }
4961 /* Extract the target string */
4962 zString = jx9_value_to_string(apArg[0], &iLen);
4963 /* Extract the mask */
4964 zMask = jx9_value_to_string(apArg[1], &iMasklen);
4965 if( iLen < 1 ){
4966 /* Nothing to process, return zero */
4967 jx9_result_int(pCtx, 0);
4968 return JX9_OK;
4969 }
4970 if( iMasklen < 1 ){
4971 /* No given mask, return the string length */
4972 jx9_result_int(pCtx, iLen);
4973 return JX9_OK;
4974 }
4975 if( nArg > 2 ){
4976 int nOfft;
4977 /* Extract the offset */
4978 nOfft = jx9_value_to_int(apArg[2]);
4979 if( nOfft < 0 ){
4980 const char *zBase = &zString[iLen + nOfft];
4981 if( zBase > zString ){
4982 iLen = (int)(&zString[iLen]-zBase);
4983 zString = zBase;
4984 }else{
4985 /* Invalid offset */
4986 jx9_result_int(pCtx, 0);
4987 return JX9_OK;
4988 }
4989 }else{
4990 if( nOfft >= iLen ){
4991 /* Invalid offset */
4992 jx9_result_int(pCtx, 0);
4993 return JX9_OK;
4994 }else{
4995 /* Update offset */
4996 zString += nOfft;
4997 iLen -= nOfft;
4998 }
4999 }
5000 if( nArg > 3 ){
5001 int iUserlen;
5002 /* Extract the desired length */
5003 iUserlen = jx9_value_to_int(apArg[3]);
5004 if( iUserlen > 0 && iUserlen < iLen ){
5005 iLen = iUserlen;
5006 }
5007 }
5008 }
5009 /* Point to the end of the string */
5010 zEnd = &zString[iLen];
5011 /* Extract the first non-space token */
5012 rc = ExtractNonSpaceToken(&zString, zEnd, &sToken);
5013 if( rc == SXRET_OK && sToken.nByte > 0 ){
5014 /* Compare against the current mask */
5015 iCount = LongestStringMask2(sToken.zString, (int)sToken.nByte, zMask, iMasklen);
5016 }
5017 /* Longest match */
5018 jx9_result_int(pCtx, iCount);
5019 return JX9_OK;
5020}
5021/*
5022 * string strpbrk(string $haystack, string $char_list)
5023 * Search a string for any of a set of characters.
5024 * Parameters
5025 * $haystack
5026 * The string where char_list is looked for.
5027 * $char_list
5028 * This parameter is case sensitive.
5029 * Return
5030 * Returns a string starting from the character found, or FALSE if it is not found.
5031 */
5032static int jx9Builtin_strpbrk(jx9_context *pCtx, int nArg, jx9_value **apArg)
5033{
5034 const char *zString, *zList, *zEnd;
5035 int iLen, iListLen, i, c;
5036 sxu32 nOfft, nMax;
5037 sxi32 rc;
5038 if( nArg < 2 ){
5039 /* Missing arguments, return FALSE */
5040 jx9_result_bool(pCtx, 0);
5041 return JX9_OK;
5042 }
5043 /* Extract the haystack and the char list */
5044 zString = jx9_value_to_string(apArg[0], &iLen);
5045 zList = jx9_value_to_string(apArg[1], &iListLen);
5046 if( iLen < 1 ){
5047 /* Nothing to process, return FALSE */
5048 jx9_result_bool(pCtx, 0);
5049 return JX9_OK;
5050 }
5051 /* Point to the end of the string */
5052 zEnd = &zString[iLen];
5053 nOfft = nMax = SXU32_HIGH;
5054 /* perform the requested operation */
5055 for( i = 0 ; i < iListLen ; i++ ){
5056 c = zList[i];
5057 rc = SyByteFind(zString, (sxu32)iLen, c, &nMax);
5058 if( rc == SXRET_OK ){
5059 if( nMax < nOfft ){
5060 nOfft = nMax;
5061 }
5062 }
5063 }
5064 if( nOfft == SXU32_HIGH ){
5065 /* No such substring, return FALSE */
5066 jx9_result_bool(pCtx, 0);
5067 }else{
5068 /* Return the substring */
5069 jx9_result_string(pCtx, &zString[nOfft], (int)(zEnd-&zString[nOfft]));
5070 }
5071 return JX9_OK;
5072}
5073/*
5074 * string soundex(string $str)
5075 * Calculate the soundex key of a string.
5076 * Parameters
5077 * $str
5078 * The input string.
5079 * Return
5080 * Returns the soundex key as a string.
5081 * Note:
5082 * This implementation is based on the one found in the SQLite3
5083 * source tree.
5084 */
5085static int jx9Builtin_soundex(jx9_context *pCtx, int nArg, jx9_value **apArg)
5086{
5087 const unsigned char *zIn;
5088 char zResult[8];
5089 int i, j;
5090 static const unsigned char iCode[] = {
5091 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5092 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5093 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5094 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5095 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0,
5096 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
5097 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0,
5098 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
5099 };
5100 if( nArg < 1 ){
5101 /* Missing arguments, return the empty string */
5102 jx9_result_string(pCtx, "", 0);
5103 return JX9_OK;
5104 }
5105 zIn = (unsigned char *)jx9_value_to_string(apArg[0], 0);
5106 for(i=0; zIn[i] && zIn[i] < 0xc0 && !SyisAlpha(zIn[i]); i++){}
5107 if( zIn[i] ){
5108 unsigned char prevcode = iCode[zIn[i]&0x7f];
5109 zResult[0] = (char)SyToUpper(zIn[i]);
5110 for(j=1; j<4 && zIn[i]; i++){
5111 int code = iCode[zIn[i]&0x7f];
5112 if( code>0 ){
5113 if( code!=prevcode ){
5114 prevcode = (unsigned char)code;
5115 zResult[j++] = (char)code + '0';
5116 }
5117 }else{
5118 prevcode = 0;
5119 }
5120 }
5121 while( j<4 ){
5122 zResult[j++] = '0';
5123 }
5124 jx9_result_string(pCtx, zResult, 4);
5125 }else{
5126 jx9_result_string(pCtx, "?000", 4);
5127 }
5128 return JX9_OK;
5129}
5130/*
5131 * string wordwrap(string $str[, int $width = 75[, string $break = "\n"]])
5132 * Wraps a string to a given number of characters.
5133 * Parameters
5134 * $str
5135 * The input string.
5136 * $width
5137 * The column width.
5138 * $break
5139 * The line is broken using the optional break parameter.
5140 * Return
5141 * Returns the given string wrapped at the specified column.
5142 */
5143static int jx9Builtin_wordwrap(jx9_context *pCtx, int nArg, jx9_value **apArg)
5144{
5145 const char *zIn, *zEnd, *zBreak;
5146 int iLen, iBreaklen, iChunk;
5147 if( nArg < 1 ){
5148 /* Missing arguments, return the empty string */
5149 jx9_result_string(pCtx, "", 0);
5150 return JX9_OK;
5151 }
5152 /* Extract the input string */
5153 zIn = jx9_value_to_string(apArg[0], &iLen);
5154 if( iLen < 1 ){
5155 /* Nothing to process, return the empty string */
5156 jx9_result_string(pCtx, "", 0);
5157 return JX9_OK;
5158 }
5159 /* Chunk length */
5160 iChunk = 75;
5161 iBreaklen = 0;
5162 zBreak = ""; /* cc warning */
5163 if( nArg > 1 ){
5164 iChunk = jx9_value_to_int(apArg[1]);
5165 if( iChunk < 1 ){
5166 iChunk = 75;
5167 }
5168 if( nArg > 2 ){
5169 zBreak = jx9_value_to_string(apArg[2], &iBreaklen);
5170 }
5171 }
5172 if( iBreaklen < 1 ){
5173 /* Set a default column break */
5174#ifdef __WINNT__
5175 zBreak = "\r\n";
5176 iBreaklen = (int)sizeof("\r\n")-1;
5177#else
5178 zBreak = "\n";
5179 iBreaklen = (int)sizeof(char);
5180#endif
5181 }
5182 /* Perform the requested operation */
5183 zEnd = &zIn[iLen];
5184 for(;;){
5185 int nMax;
5186 if( zIn >= zEnd ){
5187 /* No more input to process */
5188 break;
5189 }
5190 nMax = (int)(zEnd-zIn);
5191 if( iChunk > nMax ){
5192 iChunk = nMax;
5193 }
5194 /* Append the column first */
5195 jx9_result_string(pCtx, zIn, iChunk); /* Will make it's own copy */
5196 /* Advance the cursor */
5197 zIn += iChunk;
5198 if( zIn < zEnd ){
5199 /* Append the line break */
5200 jx9_result_string(pCtx, zBreak, iBreaklen);
5201 }
5202 }
5203 return JX9_OK;
5204}
5205/*
5206 * Check if the given character is a member of the given mask.
5207 * Return TRUE on success. FALSE otherwise.
5208 * Refer to [strtok()].
5209 */
5210static int CheckMask(int c, const char *zMask, int nMasklen, int *pOfft)
5211{
5212 int i;
5213 for( i = 0 ; i < nMasklen ; ++i ){
5214 if( c == zMask[i] ){
5215 if( pOfft ){
5216 *pOfft = i;
5217 }
5218 return TRUE;
5219 }
5220 }
5221 return FALSE;
5222}
5223/*
5224 * Extract a single token from the input stream.
5225 * Refer to [strtok()].
5226 */
5227static sxi32 ExtractToken(const char **pzIn, const char *zEnd, const char *zMask, int nMasklen, SyString *pOut)
5228{
5229 const char *zIn = *pzIn;
5230 const char *zPtr;
5231 /* Ignore leading delimiter */
5232 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && CheckMask(zIn[0], zMask, nMasklen, 0) ){
5233 zIn++;
5234 }
5235 if( zIn >= zEnd ){
5236 /* End of input */
5237 return SXERR_EOF;
5238 }
5239 zPtr = zIn;
5240 /* Extract the token */
5241 while( zIn < zEnd ){
5242 if( (unsigned char)zIn[0] >= 0xc0 ){
5243 /* UTF-8 stream */
5244 zIn++;
5245 SX_JMP_UTF8(zIn, zEnd);
5246 }else{
5247 if( CheckMask(zIn[0], zMask, nMasklen, 0) ){
5248 break;
5249 }
5250 zIn++;
5251 }
5252 }
5253 SyStringInitFromBuf(pOut, zPtr, zIn-zPtr);
5254 /* Update the cursor */
5255 *pzIn = zIn;
5256 /* Return to the caller */
5257 return SXRET_OK;
5258}
5259/* strtok auxiliary private data */
5260typedef struct strtok_aux_data strtok_aux_data;
5261struct strtok_aux_data
5262{
5263 const char *zDup; /* Complete duplicate of the input */
5264 const char *zIn; /* Current input stream */
5265 const char *zEnd; /* End of input */
5266};
5267/*
5268 * string strtok(string $str, string $token)
5269 * string strtok(string $token)
5270 * strtok() splits a string (str) into smaller strings (tokens), with each token
5271 * being delimited by any character from token. That is, if you have a string like
5272 * "This is an example string" you could tokenize this string into its individual
5273 * words by using the space character as the token.
5274 * Note that only the first call to strtok uses the string argument. Every subsequent
5275 * call to strtok only needs the token to use, as it keeps track of where it is in
5276 * the current string. To start over, or to tokenize a new string you simply call strtok
5277 * with the string argument again to initialize it. Note that you may put multiple tokens
5278 * in the token parameter. The string will be tokenized when any one of the characters in
5279 * the argument are found.
5280 * Parameters
5281 * $str
5282 * The string being split up into smaller strings (tokens).
5283 * $token
5284 * The delimiter used when splitting up str.
5285 * Return
5286 * Current token or FALSE on EOF.
5287 */
5288static int jx9Builtin_strtok(jx9_context *pCtx, int nArg, jx9_value **apArg)
5289{
5290 strtok_aux_data *pAux;
5291 const char *zMask;
5292 SyString sToken;
5293 int nMasklen;
5294 sxi32 rc;
5295 if( nArg < 2 ){
5296 /* Extract top aux data */
5297 pAux = (strtok_aux_data *)jx9_context_peek_aux_data(pCtx);
5298 if( pAux == 0 ){
5299 /* No aux data, return FALSE */
5300 jx9_result_bool(pCtx, 0);
5301 return JX9_OK;
5302 }
5303 nMasklen = 0;
5304 zMask = ""; /* cc warning */
5305 if( nArg > 0 ){
5306 /* Extract the mask */
5307 zMask = jx9_value_to_string(apArg[0], &nMasklen);
5308 }
5309 if( nMasklen < 1 ){
5310 /* Invalid mask, return FALSE */
5311 jx9_context_free_chunk(pCtx, (void *)pAux->zDup);
5312 jx9_context_free_chunk(pCtx, pAux);
5313 (void)jx9_context_pop_aux_data(pCtx);
5314 jx9_result_bool(pCtx, 0);
5315 return JX9_OK;
5316 }
5317 /* Extract the token */
5318 rc = ExtractToken(&pAux->zIn, pAux->zEnd, zMask, nMasklen, &sToken);
5319 if( rc != SXRET_OK ){
5320 /* EOF , discard the aux data */
5321 jx9_context_free_chunk(pCtx, (void *)pAux->zDup);
5322 jx9_context_free_chunk(pCtx, pAux);
5323 (void)jx9_context_pop_aux_data(pCtx);
5324 jx9_result_bool(pCtx, 0);
5325 }else{
5326 /* Return the extracted token */
5327 jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte);
5328 }
5329 }else{
5330 const char *zInput, *zCur;
5331 char *zDup;
5332 int nLen;
5333 /* Extract the raw input */
5334 zCur = zInput = jx9_value_to_string(apArg[0], &nLen);
5335 if( nLen < 1 ){
5336 /* Empty input, return FALSE */
5337 jx9_result_bool(pCtx, 0);
5338 return JX9_OK;
5339 }
5340 /* Extract the mask */
5341 zMask = jx9_value_to_string(apArg[1], &nMasklen);
5342 if( nMasklen < 1 ){
5343 /* Set a default mask */
5344#define TOK_MASK " \n\t\r\f"
5345 zMask = TOK_MASK;
5346 nMasklen = (int)sizeof(TOK_MASK) - 1;
5347#undef TOK_MASK
5348 }
5349 /* Extract a single token */
5350 rc = ExtractToken(&zInput, &zInput[nLen], zMask, nMasklen, &sToken);
5351 if( rc != SXRET_OK ){
5352 /* Empty input */
5353 jx9_result_bool(pCtx, 0);
5354 return JX9_OK;
5355 }else{
5356 /* Return the extracted token */
5357 jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte);
5358 }
5359 /* Create our auxilliary data and copy the input */
5360 pAux = (strtok_aux_data *)jx9_context_alloc_chunk(pCtx, sizeof(strtok_aux_data), TRUE, FALSE);
5361 if( pAux ){
5362 nLen -= (int)(zInput-zCur);
5363 if( nLen < 1 ){
5364 jx9_context_free_chunk(pCtx, pAux);
5365 return JX9_OK;
5366 }
5367 /* Duplicate input */
5368 zDup = (char *)jx9_context_alloc_chunk(pCtx, (unsigned int)(nLen+1), TRUE, FALSE);
5369 if( zDup ){
5370 SyMemcpy(zInput, zDup, (sxu32)nLen);
5371 /* Register the aux data */
5372 pAux->zDup = pAux->zIn = zDup;
5373 pAux->zEnd = &zDup[nLen];
5374 jx9_context_push_aux_data(pCtx, pAux);
5375 }
5376 }
5377 }
5378 return JX9_OK;
5379}
5380/*
5381 * string str_pad(string $input, int $pad_length[, string $pad_string = " " [, int $pad_type = STR_PAD_RIGHT]])
5382 * Pad a string to a certain length with another string
5383 * Parameters
5384 * $input
5385 * The input string.
5386 * $pad_length
5387 * If the value of pad_length is negative, less than, or equal to the length of the input
5388 * string, no padding takes place.
5389 * $pad_string
5390 * Note:
5391 * The pad_string WIIL NOT BE truncated if the required number of padding characters can't be evenly
5392 * divided by the pad_string's length.
5393 * $pad_type
5394 * Optional argument pad_type can be STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH. If pad_type
5395 * is not specified it is assumed to be STR_PAD_RIGHT.
5396 * Return
5397 * The padded string.
5398 */
5399static int jx9Builtin_str_pad(jx9_context *pCtx, int nArg, jx9_value **apArg)
5400{
5401 int iLen, iPadlen, iType, i, iDiv, iStrpad, iRealPad, jPad;
5402 const char *zIn, *zPad;
5403 if( nArg < 2 ){
5404 /* Missing arguments, return the empty string */
5405 jx9_result_string(pCtx, "", 0);
5406 return JX9_OK;
5407 }
5408 /* Extract the target string */
5409 zIn = jx9_value_to_string(apArg[0], &iLen);
5410 /* Padding length */
5411 iRealPad = iPadlen = jx9_value_to_int(apArg[1]);
5412 if( iPadlen > 0 ){
5413 iPadlen -= iLen;
5414 }
5415 if( iPadlen < 1 ){
5416 /* Return the string verbatim */
5417 jx9_result_string(pCtx, zIn, iLen);
5418 return JX9_OK;
5419 }
5420 zPad = " "; /* Whitespace padding */
5421 iStrpad = (int)sizeof(char);
5422 iType = 1 ; /* STR_PAD_RIGHT */
5423 if( nArg > 2 ){
5424 /* Padding string */
5425 zPad = jx9_value_to_string(apArg[2], &iStrpad);
5426 if( iStrpad < 1 ){
5427 /* Empty string */
5428 zPad = " "; /* Whitespace padding */
5429 iStrpad = (int)sizeof(char);
5430 }
5431 if( nArg > 3 ){
5432 /* Padd type */
5433 iType = jx9_value_to_int(apArg[3]);
5434 if( iType != 0 /* STR_PAD_LEFT */ && iType != 2 /* STR_PAD_BOTH */ ){
5435 iType = 1 ; /* STR_PAD_RIGHT */
5436 }
5437 }
5438 }
5439 iDiv = 1;
5440 if( iType == 2 ){
5441 iDiv = 2; /* STR_PAD_BOTH */
5442 }
5443 /* Perform the requested operation */
5444 if( iType == 0 /* STR_PAD_LEFT */ || iType == 2 /* STR_PAD_BOTH */ ){
5445 jPad = iStrpad;
5446 for( i = 0 ; i < iPadlen/iDiv ; i += jPad ){
5447 /* Padding */
5448 if( (int)jx9_context_result_buf_length(pCtx) + iLen + jPad >= iRealPad ){
5449 break;
5450 }
5451 jx9_result_string(pCtx, zPad, jPad);
5452 }
5453 if( iType == 0 /* STR_PAD_LEFT */ ){
5454 while( (int)jx9_context_result_buf_length(pCtx) + iLen < iRealPad ){
5455 jPad = iRealPad - (iLen + (int)jx9_context_result_buf_length(pCtx) );
5456 if( jPad > iStrpad ){
5457 jPad = iStrpad;
5458 }
5459 if( jPad < 1){
5460 break;
5461 }
5462 jx9_result_string(pCtx, zPad, jPad);
5463 }
5464 }
5465 }
5466 if( iLen > 0 ){
5467 /* Append the input string */
5468 jx9_result_string(pCtx, zIn, iLen);
5469 }
5470 if( iType == 1 /* STR_PAD_RIGHT */ || iType == 2 /* STR_PAD_BOTH */ ){
5471 for( i = 0 ; i < iPadlen/iDiv ; i += iStrpad ){
5472 /* Padding */
5473 if( (int)jx9_context_result_buf_length(pCtx) + iStrpad >= iRealPad ){
5474 break;
5475 }
5476 jx9_result_string(pCtx, zPad, iStrpad);
5477 }
5478 while( (int)jx9_context_result_buf_length(pCtx) < iRealPad ){
5479 jPad = iRealPad - (int)jx9_context_result_buf_length(pCtx);
5480 if( jPad > iStrpad ){
5481 jPad = iStrpad;
5482 }
5483 if( jPad < 1){
5484 break;
5485 }
5486 jx9_result_string(pCtx, zPad, jPad);
5487 }
5488 }
5489 return JX9_OK;
5490}
5491/*
5492 * String replacement private data.
5493 */
5494typedef struct str_replace_data str_replace_data;
5495struct str_replace_data
5496{
5497 /* The following two fields are only used by the strtr function */
5498 SyBlob *pWorker; /* Working buffer */
5499 ProcStringMatch xMatch; /* Pattern match routine */
5500 /* The following two fields are only used by the str_replace function */
5501 SySet *pCollector; /* Argument collector*/
5502 jx9_context *pCtx; /* Call context */
5503};
5504/*
5505 * Remove a substring.
5506 */
5507#define STRDEL(SRC, SLEN, OFFT, ILEN){\
5508 for(;;){\
5509 if( OFFT + ILEN >= SLEN ) break; SRC[OFFT] = SRC[OFFT+ILEN]; ++OFFT;\
5510 }\
5511}
5512/*
5513 * Shift right and insert algorithm.
5514 */
5515#define SHIFTRANDINSERT(SRC, LEN, OFFT, ENTRY, ELEN){\
5516 sxu32 INLEN = LEN - OFFT;\
5517 for(;;){\
5518 if( LEN > 0 ){ LEN--; } if(INLEN < 1 ) break; SRC[LEN + ELEN] = SRC[LEN] ; --INLEN; \
5519 }\
5520 for(;;){\
5521 if(ELEN < 1)break; SRC[OFFT] = ENTRY[0]; OFFT++; ENTRY++; --ELEN;\
5522 }\
5523}
5524/*
5525 * Replace all occurrences of the search string at offset (nOfft) with the given
5526 * replacement string [i.e: zReplace].
5527 */
5528static int StringReplace(SyBlob *pWorker, sxu32 nOfft, int nLen, const char *zReplace, int nReplen)
5529{
5530 char *zInput = (char *)SyBlobData(pWorker);
5531 sxu32 n, m;
5532 n = SyBlobLength(pWorker);
5533 m = nOfft;
5534 /* Delete the old entry */
5535 STRDEL(zInput, n, m, nLen);
5536 SyBlobLength(pWorker) -= nLen;
5537 if( nReplen > 0 ){
5538 sxi32 iRep = nReplen;
5539 sxi32 rc;
5540 /*
5541 * Make sure the working buffer is big enough to hold the replacement
5542 * string.
5543 */
5544 rc = SyBlobAppend(pWorker, 0/* Grow without an append operation*/, (sxu32)nReplen);
5545 if( rc != SXRET_OK ){
5546 /* Simply ignore any memory failure problem */
5547 return SXRET_OK;
5548 }
5549 /* Perform the insertion now */
5550 zInput = (char *)SyBlobData(pWorker);
5551 n = SyBlobLength(pWorker);
5552 SHIFTRANDINSERT(zInput, n, nOfft, zReplace, iRep);
5553 SyBlobLength(pWorker) += nReplen;
5554 }
5555 return SXRET_OK;
5556}
5557/*
5558 * String replacement walker callback.
5559 * The following callback is invoked for each array entry that hold
5560 * the replace string.
5561 * Refer to the strtr() implementation for more information.
5562 */
5563static int StringReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData)
5564{
5565 str_replace_data *pRepData = (str_replace_data *)pUserData;
5566 const char *zTarget, *zReplace;
5567 SyBlob *pWorker;
5568 int tLen, nLen;
5569 sxu32 nOfft;
5570 sxi32 rc;
5571 /* Point to the working buffer */
5572 pWorker = pRepData->pWorker;
5573 if( !jx9_value_is_string(pKey) ){
5574 /* Target and replace must be a string */
5575 return JX9_OK;
5576 }
5577 /* Extract the target and the replace */
5578 zTarget = jx9_value_to_string(pKey, &tLen);
5579 if( tLen < 1 ){
5580 /* Empty target, return immediately */
5581 return JX9_OK;
5582 }
5583 /* Perform a pattern search */
5584 rc = pRepData->xMatch(SyBlobData(pWorker), SyBlobLength(pWorker), (const void *)zTarget, (sxu32)tLen, &nOfft);
5585 if( rc != SXRET_OK ){
5586 /* Pattern not found */
5587 return JX9_OK;
5588 }
5589 /* Extract the replace string */
5590 zReplace = jx9_value_to_string(pData, &nLen);
5591 /* Perform the replace process */
5592 StringReplace(pWorker, nOfft, tLen, zReplace, nLen);
5593 /* All done */
5594 return JX9_OK;
5595}
5596/*
5597 * The following walker callback is invoked by the str_rplace() function inorder
5598 * to collect search/replace string.
5599 * This callback is invoked only if the given argument is of type array.
5600 */
5601static int StrReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData)
5602{
5603 str_replace_data *pRep = (str_replace_data *)pUserData;
5604 SyString sWorker;
5605 const char *zIn;
5606 int nByte;
5607 /* Extract a string representation of the given argument */
5608 zIn = jx9_value_to_string(pData, &nByte);
5609 SyStringInitFromBuf(&sWorker, 0, 0);
5610 if( nByte > 0 ){
5611 char *zDup;
5612 /* Duplicate the chunk */
5613 zDup = (char *)jx9_context_alloc_chunk(pRep->pCtx, (unsigned int)nByte, FALSE,
5614 TRUE /* Release the chunk automatically, upon this context is destroyd */
5615 );
5616 if( zDup == 0 ){
5617 /* Ignore any memory failure problem */
5618 jx9_context_throw_error(pRep->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
5619 return JX9_OK;
5620 }
5621 SyMemcpy(zIn, zDup, (sxu32)nByte);
5622 /* Save the chunk */
5623 SyStringInitFromBuf(&sWorker, zDup, nByte);
5624 }
5625 /* Save for later processing */
5626 SySetPut(pRep->pCollector, (const void *)&sWorker);
5627 /* All done */
5628 SXUNUSED(pKey); /* cc warning */
5629 return JX9_OK;
5630}
5631/*
5632 * mixed str_replace(mixed $search, mixed $replace, mixed $subject[, int &$count ])
5633 * mixed str_ireplace(mixed $search, mixed $replace, mixed $subject[, int &$count ])
5634 * Replace all occurrences of the search string with the replacement string.
5635 * Parameters
5636 * If search and replace are arrays, then str_replace() takes a value from each
5637 * array and uses them to search and replace on subject. If replace has fewer values
5638 * than search, then an empty string is used for the rest of replacement values.
5639 * If search is an array and replace is a string, then this replacement string is used
5640 * for every value of search. The converse would not make sense, though.
5641 * If search or replace are arrays, their elements are processed first to last.
5642 * $search
5643 * The value being searched for, otherwise known as the needle. An array may be used
5644 * to designate multiple needles.
5645 * $replace
5646 * The replacement value that replaces found search values. An array may be used
5647 * to designate multiple replacements.
5648 * $subject
5649 * The string or array being searched and replaced on, otherwise known as the haystack.
5650 * If subject is an array, then the search and replace is performed with every entry
5651 * of subject, and the return value is an array as well.
5652 * $count (Not used)
5653 * If passed, this will be set to the number of replacements performed.
5654 * Return
5655 * This function returns a string or an array with the replaced values.
5656 */
5657static int jx9Builtin_str_replace(jx9_context *pCtx, int nArg, jx9_value **apArg)
5658{
5659 SyString sTemp, *pSearch, *pReplace;
5660 ProcStringMatch xMatch;
5661 const char *zIn, *zFunc;
5662 str_replace_data sRep;
5663 SyBlob sWorker;
5664 SySet sReplace;
5665 SySet sSearch;
5666 int rep_str;
5667 int nByte;
5668 sxi32 rc;
5669 if( nArg < 3 ){
5670 /* Missing/Invalid arguments, return null */
5671 jx9_result_null(pCtx);
5672 return JX9_OK;
5673 }
5674 /* Initialize fields */
5675 SySetInit(&sSearch, &pCtx->pVm->sAllocator, sizeof(SyString));
5676 SySetInit(&sReplace, &pCtx->pVm->sAllocator, sizeof(SyString));
5677 SyBlobInit(&sWorker, &pCtx->pVm->sAllocator);
5678 SyZero(&sRep, sizeof(str_replace_data));
5679 sRep.pCtx = pCtx;
5680 sRep.pCollector = &sSearch;
5681 rep_str = 0;
5682 /* Extract the subject */
5683 zIn = jx9_value_to_string(apArg[2], &nByte);
5684 if( nByte < 1 ){
5685 /* Nothing to replace, return the empty string */
5686 jx9_result_string(pCtx, "", 0);
5687 return JX9_OK;
5688 }
5689 /* Copy the subject */
5690 SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nByte);
5691 /* Search string */
5692 if( jx9_value_is_json_array(apArg[0]) ){
5693 /* Collect search string */
5694 jx9_array_walk(apArg[0], StrReplaceWalker, &sRep);
5695 }else{
5696 /* Single pattern */
5697 zIn = jx9_value_to_string(apArg[0], &nByte);
5698 if( nByte < 1 ){
5699 /* Return the subject untouched since no search string is available */
5700 jx9_result_value(pCtx, apArg[2]/* Subject as thrird argument*/);
5701 return JX9_OK;
5702 }
5703 SyStringInitFromBuf(&sTemp, zIn, nByte);
5704 /* Save for later processing */
5705 SySetPut(&sSearch, (const void *)&sTemp);
5706 }
5707 /* Replace string */
5708 if( jx9_value_is_json_array(apArg[1]) ){
5709 /* Collect replace string */
5710 sRep.pCollector = &sReplace;
5711 jx9_array_walk(apArg[1], StrReplaceWalker, &sRep);
5712 }else{
5713 /* Single needle */
5714 zIn = jx9_value_to_string(apArg[1], &nByte);
5715 rep_str = 1;
5716 SyStringInitFromBuf(&sTemp, zIn, nByte);
5717 /* Save for later processing */
5718 SySetPut(&sReplace, (const void *)&sTemp);
5719 }
5720 /* Reset loop cursors */
5721 SySetResetCursor(&sSearch);
5722 SySetResetCursor(&sReplace);
5723 pReplace = pSearch = 0; /* cc warning */
5724 SyStringInitFromBuf(&sTemp, "", 0);
5725 /* Extract function name */
5726 zFunc = jx9_function_name(pCtx);
5727 /* Set the default pattern match routine */
5728 xMatch = SyBlobSearch;
5729 if( SyStrncmp(zFunc, "str_ireplace", sizeof("str_ireplace") - 1) == 0 ){
5730 /* Case insensitive pattern match */
5731 xMatch = iPatternMatch;
5732 }
5733 /* Start the replace process */
5734 while( SXRET_OK == SySetGetNextEntry(&sSearch, (void **)&pSearch) ){
5735 sxu32 nCount, nOfft;
5736 if( pSearch->nByte < 1 ){
5737 /* Empty string, ignore */
5738 continue;
5739 }
5740 /* Extract the replace string */
5741 if( rep_str ){
5742 pReplace = (SyString *)SySetPeek(&sReplace);
5743 }else{
5744 if( SXRET_OK != SySetGetNextEntry(&sReplace, (void **)&pReplace) ){
5745 /* Sepecial case when 'replace set' has fewer values than the search set.
5746 * An empty string is used for the rest of replacement values
5747 */
5748 pReplace = 0;
5749 }
5750 }
5751 if( pReplace == 0 ){
5752 /* Use an empty string instead */
5753 pReplace = &sTemp;
5754 }
5755 nOfft = nCount = 0;
5756 for(;;){
5757 if( nCount >= SyBlobLength(&sWorker) ){
5758 break;
5759 }
5760 /* Perform a pattern lookup */
5761 rc = xMatch(SyBlobDataAt(&sWorker, nCount), SyBlobLength(&sWorker) - nCount, (const void *)pSearch->zString,
5762 pSearch->nByte, &nOfft);
5763 if( rc != SXRET_OK ){
5764 /* Pattern not found */
5765 break;
5766 }
5767 /* Perform the replace operation */
5768 StringReplace(&sWorker, nCount+nOfft, (int)pSearch->nByte, pReplace->zString, (int)pReplace->nByte);
5769 /* Increment offset counter */
5770 nCount += nOfft + pReplace->nByte;
5771 }
5772 }
5773 /* All done, clean-up the mess left behind */
5774 jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker), (int)SyBlobLength(&sWorker));
5775 SySetRelease(&sSearch);
5776 SySetRelease(&sReplace);
5777 SyBlobRelease(&sWorker);
5778 return JX9_OK;
5779}
5780/*
5781 * string strtr(string $str, string $from, string $to)
5782 * string strtr(string $str, array $replace_pairs)
5783 * Translate characters or replace substrings.
5784 * Parameters
5785 * $str
5786 * The string being translated.
5787 * $from
5788 * The string being translated to to.
5789 * $to
5790 * The string replacing from.
5791 * $replace_pairs
5792 * The replace_pairs parameter may be used instead of to and
5793 * from, in which case it's an array in the form array('from' => 'to', ...).
5794 * Return
5795 * The translated string.
5796 * If replace_pairs contains a key which is an empty string (""), FALSE will be returned.
5797 */
5798static int jx9Builtin_strtr(jx9_context *pCtx, int nArg, jx9_value **apArg)
5799{
5800 const char *zIn;
5801 int nLen;
5802 if( nArg < 1 ){
5803 /* Nothing to replace, return FALSE */
5804 jx9_result_bool(pCtx, 0);
5805 return JX9_OK;
5806 }
5807 zIn = jx9_value_to_string(apArg[0], &nLen);
5808 if( nLen < 1 || nArg < 2 ){
5809 /* Invalid arguments */
5810 jx9_result_string(pCtx, zIn, nLen);
5811 return JX9_OK;
5812 }
5813 if( nArg == 2 && jx9_value_is_json_array(apArg[1]) ){
5814 str_replace_data sRepData;
5815 SyBlob sWorker;
5816 /* Initilaize the working buffer */
5817 SyBlobInit(&sWorker, &pCtx->pVm->sAllocator);
5818 /* Copy raw string */
5819 SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nLen);
5820 /* Init our replace data instance */
5821 sRepData.pWorker = &sWorker;
5822 sRepData.xMatch = SyBlobSearch;
5823 /* Iterate throw array entries and perform the replace operation.*/
5824 jx9_array_walk(apArg[1], StringReplaceWalker, &sRepData);
5825 /* All done, return the result string */
5826 jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker),
5827 (int)SyBlobLength(&sWorker)); /* Will make it's own copy */
5828 /* Clean-up */
5829 SyBlobRelease(&sWorker);
5830 }else{
5831 int i, flen, tlen, c, iOfft;
5832 const char *zFrom, *zTo;
5833 if( nArg < 3 ){
5834 /* Nothing to replace */
5835 jx9_result_string(pCtx, zIn, nLen);
5836 return JX9_OK;
5837 }
5838 /* Extract given arguments */
5839 zFrom = jx9_value_to_string(apArg[1], &flen);
5840 zTo = jx9_value_to_string(apArg[2], &tlen);
5841 if( flen < 1 || tlen < 1 ){
5842 /* Nothing to replace */
5843 jx9_result_string(pCtx, zIn, nLen);
5844 return JX9_OK;
5845 }
5846 /* Start the replace process */
5847 for( i = 0 ; i < nLen ; ++i ){
5848 c = zIn[i];
5849 if( CheckMask(c, zFrom, flen, &iOfft) ){
5850 if ( iOfft < tlen ){
5851 c = zTo[iOfft];
5852 }
5853 }
5854 jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
5855
5856 }
5857 }
5858 return JX9_OK;
5859}
5860/*
5861 * Parse an INI string.
5862 * According to wikipedia
5863 * The INI file format is an informal standard for configuration files for some platforms or software.
5864 * INI files are simple text files with a basic structure composed of "sections" and "properties".
5865 * Format
5866* Properties
5867* The basic element contained in an INI file is the property. Every property has a name and a value
5868* delimited by an equals sign (=). The name appears to the left of the equals sign.
5869* Example:
5870* name=value
5871* Sections
5872* Properties may be grouped into arbitrarily named sections. The section name appears on a line by itself
5873* in square brackets ([ and ]). All properties after the section declaration are associated with that section.
5874* There is no explicit "end of section" delimiter; sections end at the next section declaration
5875* or the end of the file. Sections may not be nested.
5876* Example:
5877* [section]
5878* Comments
5879* Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored.
5880* This function return an array holding parsed values on success.FALSE otherwise.
5881*/
5882JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection)
5883{
5884 jx9_value *pCur, *pArray, *pSection, *pWorker, *pValue;
5885 const char *zCur, *zEnd = &zIn[nByte];
5886 SyHashEntry *pEntry;
5887 SyString sEntry;
5888 SyHash sHash;
5889 int c;
5890 /* Create an empty array and worker variables */
5891 pArray = jx9_context_new_array(pCtx);
5892 pWorker = jx9_context_new_scalar(pCtx);
5893 pValue = jx9_context_new_scalar(pCtx);
5894 if( pArray == 0 || pWorker == 0 || pValue == 0){
5895 /* Out of memory */
5896 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
5897 /* Return FALSE */
5898 jx9_result_bool(pCtx, 0);
5899 return JX9_OK;
5900 }
5901 SyHashInit(&sHash, &pCtx->pVm->sAllocator, 0, 0);
5902 pCur = pArray;
5903 /* Start the parse process */
5904 for(;;){
5905 /* Ignore leading white spaces */
5906 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0])){
5907 zIn++;
5908 }
5909 if( zIn >= zEnd ){
5910 /* No more input to process */
5911 break;
5912 }
5913 if( zIn[0] == ';' || zIn[0] == '#' ){
5914 /* Comment til the end of line */
5915 zIn++;
5916 while(zIn < zEnd && zIn[0] != '\n' ){
5917 zIn++;
5918 }
5919 continue;
5920 }
5921 /* Reset the string cursor of the working variable */
5922 jx9_value_reset_string_cursor(pWorker);
5923 if( zIn[0] == '[' ){
5924 /* Section: Extract the section name */
5925 zIn++;
5926 zCur = zIn;
5927 while( zIn < zEnd && zIn[0] != ']' ){
5928 zIn++;
5929 }
5930 if( zIn > zCur && bProcessSection ){
5931 /* Save the section name */
5932 SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur));
5933 SyStringFullTrim(&sEntry);
5934 jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
5935 if( sEntry.nByte > 0 ){
5936 /* Associate an array with the section */
5937 pSection = jx9_context_new_array(pCtx);
5938 if( pSection ){
5939 jx9_array_add_elem(pArray, pWorker/*Section name*/, pSection);
5940 pCur = pSection;
5941 }
5942 }
5943 }
5944 zIn++; /* Trailing square brackets ']' */
5945 }else{
5946 jx9_value *pOldCur;
5947 int is_array;
5948 int iLen;
5949 /* Properties */
5950 is_array = 0;
5951 zCur = zIn;
5952 iLen = 0; /* cc warning */
5953 pOldCur = pCur;
5954 while( zIn < zEnd && zIn[0] != '=' ){
5955 if( zIn[0] == '[' && !is_array ){
5956 /* Array */
5957 iLen = (int)(zIn-zCur);
5958 is_array = 1;
5959 if( iLen > 0 ){
5960 jx9_value *pvArr = 0; /* cc warning */
5961 /* Query the hashtable */
5962 SyStringInitFromBuf(&sEntry, zCur, iLen);
5963 SyStringFullTrim(&sEntry);
5964 pEntry = SyHashGet(&sHash, (const void *)sEntry.zString, sEntry.nByte);
5965 if( pEntry ){
5966 pvArr = (jx9_value *)SyHashEntryGetUserData(pEntry);
5967 }else{
5968 /* Create an empty array */
5969 pvArr = jx9_context_new_array(pCtx);
5970 if( pvArr ){
5971 /* Save the entry */
5972 SyHashInsert(&sHash, (const void *)sEntry.zString, sEntry.nByte, pvArr);
5973 /* Insert the entry */
5974 jx9_value_reset_string_cursor(pWorker);
5975 jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
5976 jx9_array_add_elem(pCur, pWorker, pvArr);
5977 jx9_value_reset_string_cursor(pWorker);
5978 }
5979 }
5980 if( pvArr ){
5981 pCur = pvArr;
5982 }
5983 }
5984 while ( zIn < zEnd && zIn[0] != ']' ){
5985 zIn++;
5986 }
5987 }
5988 zIn++;
5989 }
5990 if( !is_array ){
5991 iLen = (int)(zIn-zCur);
5992 }
5993 /* Trim the key */
5994 SyStringInitFromBuf(&sEntry, zCur, iLen);
5995 SyStringFullTrim(&sEntry);
5996 if( sEntry.nByte > 0 ){
5997 if( !is_array ){
5998 /* Save the key name */
5999 jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
6000 }
6001 /* extract key value */
6002 jx9_value_reset_string_cursor(pValue);
6003 zIn++; /* '=' */
6004 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
6005 zIn++;
6006 }
6007 if( zIn < zEnd ){
6008 zCur = zIn;
6009 c = zIn[0];
6010 if( c == '"' || c == '\'' ){
6011 zIn++;
6012 /* Delimit the value */
6013 while( zIn < zEnd ){
6014 if ( zIn[0] == c && zIn[-1] != '\\' ){
6015 break;
6016 }
6017 zIn++;
6018 }
6019 if( zIn < zEnd ){
6020 zIn++;
6021 }
6022 }else{
6023 while( zIn < zEnd ){
6024 if( zIn[0] == '\n' ){
6025 if( zIn[-1] != '\\' ){
6026 break;
6027 }
6028 }else if( zIn[0] == ';' || zIn[0] == '#' ){
6029 /* Inline comments */
6030 break;
6031 }
6032 zIn++;
6033 }
6034 }
6035 /* Trim the value */
6036 SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur));
6037 SyStringFullTrim(&sEntry);
6038 if( c == '"' || c == '\'' ){
6039 SyStringTrimLeadingChar(&sEntry, c);
6040 SyStringTrimTrailingChar(&sEntry, c);
6041 }
6042 if( sEntry.nByte > 0 ){
6043 jx9_value_string(pValue, sEntry.zString, (int)sEntry.nByte);
6044 }
6045 /* Insert the key and it's value */
6046 jx9_array_add_elem(pCur, is_array ? 0 /*Automatic index assign */: pWorker, pValue);
6047 }
6048 }else{
6049 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && ( SyisSpace(zIn[0]) || zIn[0] == '=' ) ){
6050 zIn++;
6051 }
6052 }
6053 pCur = pOldCur;
6054 }
6055 }
6056 SyHashRelease(&sHash);
6057 /* Return the parse of the INI string */
6058 jx9_result_value(pCtx, pArray);
6059 return SXRET_OK;
6060}
6061/*
6062 * array parse_ini_string(string $ini[, bool $process_sections = false[, int $scanner_mode = INI_SCANNER_NORMAL ]])
6063 * Parse a configuration string.
6064 * Parameters
6065 * $ini
6066 * The contents of the ini file being parsed.
6067 * $process_sections
6068 * By setting the process_sections parameter to TRUE, you get a multidimensional array, with the section names
6069 * and settings included. The default for process_sections is FALSE.
6070 * $scanner_mode (Not used)
6071 * Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW. If INI_SCANNER_RAW is supplied
6072 * then option values will not be parsed.
6073 * Return
6074 * The settings are returned as an associative array on success, and FALSE on failure.
6075 */
6076static int jx9Builtin_parse_ini_string(jx9_context *pCtx, int nArg, jx9_value **apArg)
6077{
6078 const char *zIni;
6079 int nByte;
6080 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
6081 /* Missing/Invalid arguments, return FALSE*/
6082 jx9_result_bool(pCtx, 0);
6083 return JX9_OK;
6084 }
6085 /* Extract the raw INI buffer */
6086 zIni = jx9_value_to_string(apArg[0], &nByte);
6087 /* Process the INI buffer*/
6088 jx9ParseIniString(pCtx, zIni, (sxu32)nByte, (nArg > 1) ? jx9_value_to_bool(apArg[1]) : 0);
6089 return JX9_OK;
6090}
6091/*
6092 * Ctype Functions.
6093 * Authors:
6094 * Symisc Systems, devel@symisc.net.
6095 * Copyright (C) Symisc Systems, http://jx9.symisc.net
6096 * Status:
6097 * Stable.
6098 */
6099/*
6100 * bool ctype_alnum(string $text)
6101 * Checks if all of the characters in the provided string, text, are alphanumeric.
6102 * Parameters
6103 * $text
6104 * The tested string.
6105 * Return
6106 * TRUE if every character in text is either a letter or a digit, FALSE otherwise.
6107 */
6108static int jx9Builtin_ctype_alnum(jx9_context *pCtx, int nArg, jx9_value **apArg)
6109{
6110 const unsigned char *zIn, *zEnd;
6111 int nLen;
6112 if( nArg < 1 ){
6113 /* Missing arguments, return FALSE */
6114 jx9_result_bool(pCtx, 0);
6115 return JX9_OK;
6116 }
6117 /* Extract the target string */
6118 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
6119 zEnd = &zIn[nLen];
6120 if( nLen < 1 ){
6121 /* Empty string, return FALSE */
6122 jx9_result_bool(pCtx, 0);
6123 return JX9_OK;
6124 }
6125 /* Perform the requested operation */
6126 for(;;){
6127 if( zIn >= zEnd ){
6128 /* If we reach the end of the string, then the test succeeded. */
6129 jx9_result_bool(pCtx, 1);
6130 return JX9_OK;
6131 }
6132 if( !SyisAlphaNum(zIn[0]) ){
6133 break;
6134 }
6135 /* Point to the next character */
6136 zIn++;
6137 }
6138 /* The test failed, return FALSE */
6139 jx9_result_bool(pCtx, 0);
6140 return JX9_OK;
6141}
6142/*
6143 * bool ctype_alpha(string $text)
6144 * Checks if all of the characters in the provided string, text, are alphabetic.
6145 * Parameters
6146 * $text
6147 * The tested string.
6148 * Return
6149 * TRUE if every character in text is a letter from the current locale, FALSE otherwise.
6150 */
6151static int jx9Builtin_ctype_alpha(jx9_context *pCtx, int nArg, jx9_value **apArg)
6152{
6153 const unsigned char *zIn, *zEnd;
6154 int nLen;
6155 if( nArg < 1 ){
6156 /* Missing arguments, return FALSE */
6157 jx9_result_bool(pCtx, 0);
6158 return JX9_OK;
6159 }
6160 /* Extract the target string */
6161 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
6162 zEnd = &zIn[nLen];
6163 if( nLen < 1 ){
6164 /* Empty string, return FALSE */
6165 jx9_result_bool(pCtx, 0);
6166 return JX9_OK;
6167 }
6168 /* Perform the requested operation */
6169 for(;;){
6170 if( zIn >= zEnd ){
6171 /* If we reach the end of the string, then the test succeeded. */
6172 jx9_result_bool(pCtx, 1);
6173 return JX9_OK;
6174 }
6175 if( !SyisAlpha(zIn[0]) ){
6176 break;
6177 }
6178 /* Point to the next character */
6179 zIn++;
6180 }
6181 /* The test failed, return FALSE */
6182 jx9_result_bool(pCtx, 0);
6183 return JX9_OK;
6184}
6185/*
6186 * bool ctype_cntrl(string $text)
6187 * Checks if all of the characters in the provided string, text, are control characters.
6188 * Parameters
6189 * $text
6190 * The tested string.
6191 * Return
6192 * TRUE if every character in text is a control characters, FALSE otherwise.
6193 */
6194static int jx9Builtin_ctype_cntrl(jx9_context *pCtx, int nArg, jx9_value **apArg)
6195{
6196 const unsigned char *zIn, *zEnd;
6197 int nLen;
6198 if( nArg < 1 ){
6199 /* Missing arguments, return FALSE */
6200 jx9_result_bool(pCtx, 0);
6201 return JX9_OK;
6202 }
6203 /* Extract the target string */
6204 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
6205 zEnd = &zIn[nLen];
6206 if( nLen < 1 ){
6207 /* Empty string, return FALSE */
6208 jx9_result_bool(pCtx, 0);
6209 return JX9_OK;
6210 }
6211 /* Perform the requested operation */
6212 for(;;){
6213 if( zIn >= zEnd ){
6214 /* If we reach the end of the string, then the test succeeded. */
6215 jx9_result_bool(pCtx, 1);
6216 return JX9_OK;
6217 }
6218 if( zIn[0] >= 0xc0 ){
6219 /* UTF-8 stream */
6220 break;
6221 }
6222 if( !SyisCtrl(zIn[0]) ){
6223 break;
6224 }
6225 /* Point to the next character */
6226 zIn++;
6227 }
6228 /* The test failed, return FALSE */
6229 jx9_result_bool(pCtx, 0);
6230 return JX9_OK;
6231}
6232/*
6233 * bool ctype_digit(string $text)
6234 * Checks if all of the characters in the provided string, text, are numerical.
6235 * Parameters
6236 * $text
6237 * The tested string.
6238 * Return
6239 * TRUE if every character in the string text is a decimal digit, FALSE otherwise.
6240 */
6241static int jx9Builtin_ctype_digit(jx9_context *pCtx, int nArg, jx9_value **apArg)
6242{
6243 const unsigned char *zIn, *zEnd;
6244 int nLen;
6245 if( nArg < 1 ){
6246 /* Missing arguments, return FALSE */
6247 jx9_result_bool(pCtx, 0);
6248 return JX9_OK;
6249 }
6250 /* Extract the target string */
6251 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
6252 zEnd = &zIn[nLen];
6253 if( nLen < 1 ){
6254 /* Empty string, return FALSE */
6255 jx9_result_bool(pCtx, 0);
6256 return JX9_OK;
6257 }
6258 /* Perform the requested operation */
6259 for(;;){
6260 if( zIn >= zEnd ){
6261 /* If we reach the end of the string, then the test succeeded. */
6262 jx9_result_bool(pCtx, 1);
6263 return JX9_OK;
6264 }
6265 if( zIn[0] >= 0xc0 ){
6266 /* UTF-8 stream */
6267 break;
6268 }
6269 if( !SyisDigit(zIn[0]) ){
6270 break;
6271 }
6272 /* Point to the next character */
6273 zIn++;
6274 }
6275 /* The test failed, return FALSE */
6276 jx9_result_bool(pCtx, 0);
6277 return JX9_OK;
6278}
6279/*
6280 * bool ctype_xdigit(string $text)
6281 * Check for character(s) representing a hexadecimal digit.
6282 * Parameters
6283 * $text
6284 * The tested string.
6285 * Return
6286 * Returns TRUE if every character in text is a hexadecimal 'digit', that is
6287 * a decimal digit or a character from [A-Fa-f] , FALSE otherwise.
6288 */
6289static int jx9Builtin_ctype_xdigit(jx9_context *pCtx, int nArg, jx9_value **apArg)
6290{
6291 const unsigned char *zIn, *zEnd;
6292 int nLen;
6293 if( nArg < 1 ){
6294 /* Missing arguments, return FALSE */
6295 jx9_result_bool(pCtx, 0);
6296 return JX9_OK;
6297 }
6298 /* Extract the target string */
6299 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
6300 zEnd = &zIn[nLen];
6301 if( nLen < 1 ){
6302 /* Empty string, return FALSE */
6303 jx9_result_bool(pCtx, 0);
6304 return JX9_OK;
6305 }
6306 /* Perform the requested operation */
6307 for(;;){
6308 if( zIn >= zEnd ){
6309 /* If we reach the end of the string, then the test succeeded. */
6310 jx9_result_bool(pCtx, 1);
6311 return JX9_OK;
6312 }
6313 if( zIn[0] >= 0xc0 ){
6314 /* UTF-8 stream */
6315 break;
6316 }
6317 if( !SyisHex(zIn[0]) ){
6318 break;
6319 }
6320 /* Point to the next character */
6321 zIn++;
6322 }
6323 /* The test failed, return FALSE */
6324 jx9_result_bool(pCtx, 0);
6325 return JX9_OK;
6326}
6327/*
6328 * bool ctype_graph(string $text)
6329 * Checks if all of the characters in the provided string, text, creates visible output.
6330 * Parameters
6331 * $text
6332 * The tested string.
6333 * Return
6334 * Returns TRUE if every character in text is printable and actually creates visible output
6335 * (no white space), FALSE otherwise.
6336 */
6337static int jx9Builtin_ctype_graph(jx9_context *pCtx, int nArg, jx9_value **apArg)
6338{
6339 const unsigned char *zIn, *zEnd;
6340 int nLen;
6341 if( nArg < 1 ){
6342 /* Missing arguments, return FALSE */
6343 jx9_result_bool(pCtx, 0);
6344 return JX9_OK;
6345 }
6346 /* Extract the target string */
6347 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
6348 zEnd = &zIn[nLen];
6349 if( nLen < 1 ){
6350 /* Empty string, return FALSE */
6351 jx9_result_bool(pCtx, 0);
6352 return JX9_OK;
6353 }
6354 /* Perform the requested operation */
6355 for(;;){
6356 if( zIn >= zEnd ){
6357 /* If we reach the end of the string, then the test succeeded. */
6358 jx9_result_bool(pCtx, 1);
6359 return JX9_OK;
6360 }
6361 if( zIn[0] >= 0xc0 ){
6362 /* UTF-8 stream */
6363 break;
6364 }
6365 if( !SyisGraph(zIn[0]) ){
6366 break;
6367 }
6368 /* Point to the next character */
6369 zIn++;
6370 }
6371 /* The test failed, return FALSE */
6372 jx9_result_bool(pCtx, 0);
6373 return JX9_OK;
6374}
6375/*
6376 * bool ctype_print(string $text)
6377 * Checks if all of the characters in the provided string, text, are printable.
6378 * Parameters
6379 * $text
6380 * The tested string.
6381 * Return
6382 * Returns TRUE if every character in text will actually create output (including blanks).
6383 * Returns FALSE if text contains control characters or characters that do not have any output
6384 * or control function at all.
6385 */
6386static int jx9Builtin_ctype_print(jx9_context *pCtx, int nArg, jx9_value **apArg)
6387{
6388 const unsigned char *zIn, *zEnd;
6389 int nLen;
6390 if( nArg < 1 ){
6391 /* Missing arguments, return FALSE */
6392 jx9_result_bool(pCtx, 0);
6393 return JX9_OK;
6394 }
6395 /* Extract the target string */
6396 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
6397 zEnd = &zIn[nLen];
6398 if( nLen < 1 ){
6399 /* Empty string, return FALSE */
6400 jx9_result_bool(pCtx, 0);
6401 return JX9_OK;
6402 }
6403 /* Perform the requested operation */
6404 for(;;){
6405 if( zIn >= zEnd ){
6406 /* If we reach the end of the string, then the test succeeded. */
6407 jx9_result_bool(pCtx, 1);
6408 return JX9_OK;
6409 }
6410 if( zIn[0] >= 0xc0 ){
6411 /* UTF-8 stream */
6412 break;
6413 }
6414 if( !SyisPrint(zIn[0]) ){
6415 break;
6416 }
6417 /* Point to the next character */
6418 zIn++;
6419 }
6420 /* The test failed, return FALSE */
6421 jx9_result_bool(pCtx, 0);
6422 return JX9_OK;
6423}
6424/*
6425 * bool ctype_punct(string $text)
6426 * Checks if all of the characters in the provided string, text, are punctuation character.
6427 * Parameters
6428 * $text
6429 * The tested string.
6430 * Return
6431 * Returns TRUE if every character in text is printable, but neither letter
6432 * digit or blank, FALSE otherwise.
6433 */
6434static int jx9Builtin_ctype_punct(jx9_context *pCtx, int nArg, jx9_value **apArg)
6435{
6436 const unsigned char *zIn, *zEnd;
6437 int nLen;
6438 if( nArg < 1 ){
6439 /* Missing arguments, return FALSE */
6440 jx9_result_bool(pCtx, 0);
6441 return JX9_OK;
6442 }
6443 /* Extract the target string */
6444 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
6445 zEnd = &zIn[nLen];
6446 if( nLen < 1 ){
6447 /* Empty string, return FALSE */
6448 jx9_result_bool(pCtx, 0);
6449 return JX9_OK;
6450 }
6451 /* Perform the requested operation */
6452 for(;;){
6453 if( zIn >= zEnd ){
6454 /* If we reach the end of the string, then the test succeeded. */
6455 jx9_result_bool(pCtx, 1);
6456 return JX9_OK;
6457 }
6458 if( zIn[0] >= 0xc0 ){
6459 /* UTF-8 stream */
6460 break;
6461 }
6462 if( !SyisPunct(zIn[0]) ){
6463 break;
6464 }
6465 /* Point to the next character */
6466 zIn++;
6467 }
6468 /* The test failed, return FALSE */
6469 jx9_result_bool(pCtx, 0);
6470 return JX9_OK;
6471}
6472/*
6473 * bool ctype_space(string $text)
6474 * Checks if all of the characters in the provided string, text, creates whitespace.
6475 * Parameters
6476 * $text
6477 * The tested string.
6478 * Return
6479 * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise.
6480 * Besides the blank character this also includes tab, vertical tab, line feed, carriage return
6481 * and form feed characters.
6482 */
6483static int jx9Builtin_ctype_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
6484{
6485 const unsigned char *zIn, *zEnd;
6486 int nLen;
6487 if( nArg < 1 ){
6488 /* Missing arguments, return FALSE */
6489 jx9_result_bool(pCtx, 0);
6490 return JX9_OK;
6491 }
6492 /* Extract the target string */
6493 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
6494 zEnd = &zIn[nLen];
6495 if( nLen < 1 ){
6496 /* Empty string, return FALSE */
6497 jx9_result_bool(pCtx, 0);
6498 return JX9_OK;
6499 }
6500 /* Perform the requested operation */
6501 for(;;){
6502 if( zIn >= zEnd ){
6503 /* If we reach the end of the string, then the test succeeded. */
6504 jx9_result_bool(pCtx, 1);
6505 return JX9_OK;
6506 }
6507 if( zIn[0] >= 0xc0 ){
6508 /* UTF-8 stream */
6509 break;
6510 }
6511 if( !SyisSpace(zIn[0]) ){
6512 break;
6513 }
6514 /* Point to the next character */
6515 zIn++;
6516 }
6517 /* The test failed, return FALSE */
6518 jx9_result_bool(pCtx, 0);
6519 return JX9_OK;
6520}
6521/*
6522 * bool ctype_lower(string $text)
6523 * Checks if all of the characters in the provided string, text, are lowercase letters.
6524 * Parameters
6525 * $text
6526 * The tested string.
6527 * Return
6528 * Returns TRUE if every character in text is a lowercase letter in the current locale.
6529 */
6530static int jx9Builtin_ctype_lower(jx9_context *pCtx, int nArg, jx9_value **apArg)
6531{
6532 const unsigned char *zIn, *zEnd;
6533 int nLen;
6534 if( nArg < 1 ){
6535 /* Missing arguments, return FALSE */
6536 jx9_result_bool(pCtx, 0);
6537 return JX9_OK;
6538 }
6539 /* Extract the target string */
6540 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
6541 zEnd = &zIn[nLen];
6542 if( nLen < 1 ){
6543 /* Empty string, return FALSE */
6544 jx9_result_bool(pCtx, 0);
6545 return JX9_OK;
6546 }
6547 /* Perform the requested operation */
6548 for(;;){
6549 if( zIn >= zEnd ){
6550 /* If we reach the end of the string, then the test succeeded. */
6551 jx9_result_bool(pCtx, 1);
6552 return JX9_OK;
6553 }
6554 if( !SyisLower(zIn[0]) ){
6555 break;
6556 }
6557 /* Point to the next character */
6558 zIn++;
6559 }
6560 /* The test failed, return FALSE */
6561 jx9_result_bool(pCtx, 0);
6562 return JX9_OK;
6563}
6564/*
6565 * bool ctype_upper(string $text)
6566 * Checks if all of the characters in the provided string, text, are uppercase letters.
6567 * Parameters
6568 * $text
6569 * The tested string.
6570 * Return
6571 * Returns TRUE if every character in text is a uppercase letter in the current locale.
6572 */
6573static int jx9Builtin_ctype_upper(jx9_context *pCtx, int nArg, jx9_value **apArg)
6574{
6575 const unsigned char *zIn, *zEnd;
6576 int nLen;
6577 if( nArg < 1 ){
6578 /* Missing arguments, return FALSE */
6579 jx9_result_bool(pCtx, 0);
6580 return JX9_OK;
6581 }
6582 /* Extract the target string */
6583 zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
6584 zEnd = &zIn[nLen];
6585 if( nLen < 1 ){
6586 /* Empty string, return FALSE */
6587 jx9_result_bool(pCtx, 0);
6588 return JX9_OK;
6589 }
6590 /* Perform the requested operation */
6591 for(;;){
6592 if( zIn >= zEnd ){
6593 /* If we reach the end of the string, then the test succeeded. */
6594 jx9_result_bool(pCtx, 1);
6595 return JX9_OK;
6596 }
6597 if( !SyisUpper(zIn[0]) ){
6598 break;
6599 }
6600 /* Point to the next character */
6601 zIn++;
6602 }
6603 /* The test failed, return FALSE */
6604 jx9_result_bool(pCtx, 0);
6605 return JX9_OK;
6606}
6607/*
6608 * Date/Time functions
6609 * Authors:
6610 * Symisc Systems, devel@symisc.net.
6611 * Copyright (C) Symisc Systems, http://jx9.symisc.net
6612 * Status:
6613 * Devel.
6614 */
6615#include <time.h>
6616#ifdef __WINNT__
6617/* GetSystemTime() */
6618#include <Windows.h>
6619#ifdef _WIN32_WCE
6620/*
6621** WindowsCE does not have a localtime() function. So create a
6622** substitute.
6623** Taken from the SQLite3 source tree.
6624** Status: Public domain
6625*/
6626struct tm *__cdecl localtime(const time_t *t)
6627{
6628 static struct tm y;
6629 FILETIME uTm, lTm;
6630 SYSTEMTIME pTm;
6631 jx9_int64 t64;
6632 t64 = *t;
6633 t64 = (t64 + 11644473600)*10000000;
6634 uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF);
6635 uTm.dwHighDateTime= (DWORD)(t64 >> 32);
6636 FileTimeToLocalFileTime(&uTm, &lTm);
6637 FileTimeToSystemTime(&lTm, &pTm);
6638 y.tm_year = pTm.wYear - 1900;
6639 y.tm_mon = pTm.wMonth - 1;
6640 y.tm_wday = pTm.wDayOfWeek;
6641 y.tm_mday = pTm.wDay;
6642 y.tm_hour = pTm.wHour;
6643 y.tm_min = pTm.wMinute;
6644 y.tm_sec = pTm.wSecond;
6645 return &y;
6646}
6647#endif /*_WIN32_WCE */
6648#elif defined(__UNIXES__)
6649#include <sys/time.h>
6650#endif /* __WINNT__*/
6651 /*
6652 * int64 time(void)
6653 * Current Unix timestamp
6654 * Parameters
6655 * None.
6656 * Return
6657 * Returns the current time measured in the number of seconds
6658 * since the Unix Epoch (January 1 1970 00:00:00 GMT).
6659 */
6660static int jx9Builtin_time(jx9_context *pCtx, int nArg, jx9_value **apArg)
6661{
6662 time_t tt;
6663 SXUNUSED(nArg); /* cc warning */
6664 SXUNUSED(apArg);
6665 /* Extract the current time */
6666 time(&tt);
6667 /* Return as 64-bit integer */
6668 jx9_result_int64(pCtx, (jx9_int64)tt);
6669 return JX9_OK;
6670}
6671/*
6672 * string/float microtime([ bool $get_as_float = false ])
6673 * microtime() returns the current Unix timestamp with microseconds.
6674 * Parameters
6675 * $get_as_float
6676 * If used and set to TRUE, microtime() will return a float instead of a string
6677 * as described in the return values section below.
6678 * Return
6679 * By default, microtime() returns a string in the form "msec sec", where sec
6680 * is the current time measured in the number of seconds since the Unix
6681 * epoch (0:00:00 January 1, 1970 GMT), and msec is the number of microseconds
6682 * that have elapsed since sec expressed in seconds.
6683 * If get_as_float is set to TRUE, then microtime() returns a float, which represents
6684 * the current time in seconds since the Unix epoch accurate to the nearest microsecond.
6685 */
6686static int jx9Builtin_microtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
6687{
6688 int bFloat = 0;
6689 sytime sTime;
6690#if defined(__UNIXES__)
6691 struct timeval tv;
6692 gettimeofday(&tv, 0);
6693 sTime.tm_sec = (long)tv.tv_sec;
6694 sTime.tm_usec = (long)tv.tv_usec;
6695#else
6696 time_t tt;
6697 time(&tt);
6698 sTime.tm_sec = (long)tt;
6699 sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC);
6700#endif /* __UNIXES__ */
6701 if( nArg > 0 ){
6702 bFloat = jx9_value_to_bool(apArg[0]);
6703 }
6704 if( bFloat ){
6705 /* Return as float */
6706 jx9_result_double(pCtx, (double)sTime.tm_sec);
6707 }else{
6708 /* Return as string */
6709 jx9_result_string_format(pCtx, "%ld %ld", sTime.tm_usec, sTime.tm_sec);
6710 }
6711 return JX9_OK;
6712}
6713/*
6714 * array getdate ([ int $timestamp = time() ])
6715 * Get date/time information.
6716 * Parameter
6717 * $timestamp: The optional timestamp parameter is an integer Unix timestamp
6718 * that defaults to the current local time if a timestamp is not given.
6719 * In other words, it defaults to the value of time().
6720 * Returns
6721 * Returns an associative array of information related to the timestamp.
6722 * Elements from the returned associative array are as follows:
6723 * KEY VALUE
6724 * --------- -------
6725 * "seconds" Numeric representation of seconds 0 to 59
6726 * "minutes" Numeric representation of minutes 0 to 59
6727 * "hours" Numeric representation of hours 0 to 23
6728 * "mday" Numeric representation of the day of the month 1 to 31
6729 * "wday" Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
6730 * "mon" Numeric representation of a month 1 through 12
6731 * "year" A full numeric representation of a year, 4 digits Examples: 1999 or 2003
6732 * "yday" Numeric representation of the day of the year 0 through 365
6733 * "weekday" A full textual representation of the day of the week Sunday through Saturday
6734 * "month" A full textual representation of a month, such as January or March January through December
6735 * 0 Seconds since the Unix Epoch, similar to the values returned by time() and used by date().
6736 * NOTE:
6737 * NULL is returned on failure.
6738 */
6739static int jx9Builtin_getdate(jx9_context *pCtx, int nArg, jx9_value **apArg)
6740{
6741 jx9_value *pValue, *pArray;
6742 Sytm sTm;
6743 if( nArg < 1 ){
6744#ifdef __WINNT__
6745 SYSTEMTIME sOS;
6746 GetSystemTime(&sOS);
6747 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
6748#else
6749 struct tm *pTm;
6750 time_t t;
6751 time(&t);
6752 pTm = localtime(&t);
6753 STRUCT_TM_TO_SYTM(pTm, &sTm);
6754#endif
6755 }else{
6756 /* Use the given timestamp */
6757 time_t t;
6758 struct tm *pTm;
6759#ifdef __WINNT__
6760#ifdef _MSC_VER
6761#if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
6762#pragma warning(disable:4996) /* _CRT_SECURE...*/
6763#endif
6764#endif
6765#endif
6766 if( jx9_value_is_int(apArg[0]) ){
6767 t = (time_t)jx9_value_to_int64(apArg[0]);
6768 pTm = localtime(&t);
6769 if( pTm == 0 ){
6770 time(&t);
6771 }
6772 }else{
6773 time(&t);
6774 }
6775 pTm = localtime(&t);
6776 STRUCT_TM_TO_SYTM(pTm, &sTm);
6777 }
6778 /* Element value */
6779 pValue = jx9_context_new_scalar(pCtx);
6780 if( pValue == 0 ){
6781 /* Return NULL */
6782 jx9_result_null(pCtx);
6783 return JX9_OK;
6784 }
6785 /* Create a new array */
6786 pArray = jx9_context_new_array(pCtx);
6787 if( pArray == 0 ){
6788 /* Return NULL */
6789 jx9_result_null(pCtx);
6790 return JX9_OK;
6791 }
6792 /* Fill the array */
6793 /* Seconds */
6794 jx9_value_int(pValue, sTm.tm_sec);
6795 jx9_array_add_strkey_elem(pArray, "seconds", pValue);
6796 /* Minutes */
6797 jx9_value_int(pValue, sTm.tm_min);
6798 jx9_array_add_strkey_elem(pArray, "minutes", pValue);
6799 /* Hours */
6800 jx9_value_int(pValue, sTm.tm_hour);
6801 jx9_array_add_strkey_elem(pArray, "hours", pValue);
6802 /* mday */
6803 jx9_value_int(pValue, sTm.tm_mday);
6804 jx9_array_add_strkey_elem(pArray, "mday", pValue);
6805 /* wday */
6806 jx9_value_int(pValue, sTm.tm_wday);
6807 jx9_array_add_strkey_elem(pArray, "wday", pValue);
6808 /* mon */
6809 jx9_value_int(pValue, sTm.tm_mon+1);
6810 jx9_array_add_strkey_elem(pArray, "mon", pValue);
6811 /* year */
6812 jx9_value_int(pValue, sTm.tm_year);
6813 jx9_array_add_strkey_elem(pArray, "year", pValue);
6814 /* yday */
6815 jx9_value_int(pValue, sTm.tm_yday);
6816 jx9_array_add_strkey_elem(pArray, "yday", pValue);
6817 /* Weekday */
6818 jx9_value_string(pValue, SyTimeGetDay(sTm.tm_wday), -1);
6819 jx9_array_add_strkey_elem(pArray, "weekday", pValue);
6820 /* Month */
6821 jx9_value_reset_string_cursor(pValue);
6822 jx9_value_string(pValue, SyTimeGetMonth(sTm.tm_mon), -1);
6823 jx9_array_add_strkey_elem(pArray, "month", pValue);
6824 /* Seconds since the epoch */
6825 jx9_value_int64(pValue, (jx9_int64)time(0));
6826 jx9_array_add_elem(pArray, 0 /* Index zero */, pValue);
6827 /* Return the freshly created array */
6828 jx9_result_value(pCtx, pArray);
6829 return JX9_OK;
6830}
6831/*
6832 * mixed gettimeofday([ bool $return_float = false ] )
6833 * Returns an associative array containing the data returned from the system call.
6834 * Parameters
6835 * $return_float
6836 * When set to TRUE, a float instead of an array is returned.
6837 * Return
6838 * By default an array is returned. If return_float is set, then
6839 * a float is returned.
6840 */
6841static int jx9Builtin_gettimeofday(jx9_context *pCtx, int nArg, jx9_value **apArg)
6842{
6843 int bFloat = 0;
6844 sytime sTime;
6845#if defined(__UNIXES__)
6846 struct timeval tv;
6847 gettimeofday(&tv, 0);
6848 sTime.tm_sec = (long)tv.tv_sec;
6849 sTime.tm_usec = (long)tv.tv_usec;
6850#else
6851 time_t tt;
6852 time(&tt);
6853 sTime.tm_sec = (long)tt;
6854 sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC);
6855#endif /* __UNIXES__ */
6856 if( nArg > 0 ){
6857 bFloat = jx9_value_to_bool(apArg[0]);
6858 }
6859 if( bFloat ){
6860 /* Return as float */
6861 jx9_result_double(pCtx, (double)sTime.tm_sec);
6862 }else{
6863 /* Return an associative array */
6864 jx9_value *pValue, *pArray;
6865 /* Create a new array */
6866 pArray = jx9_context_new_array(pCtx);
6867 /* Element value */
6868 pValue = jx9_context_new_scalar(pCtx);
6869 if( pValue == 0 || pArray == 0 ){
6870 /* Return NULL */
6871 jx9_result_null(pCtx);
6872 return JX9_OK;
6873 }
6874 /* Fill the array */
6875 /* sec */
6876 jx9_value_int64(pValue, sTime.tm_sec);
6877 jx9_array_add_strkey_elem(pArray, "sec", pValue);
6878 /* usec */
6879 jx9_value_int64(pValue, sTime.tm_usec);
6880 jx9_array_add_strkey_elem(pArray, "usec", pValue);
6881 /* Return the array */
6882 jx9_result_value(pCtx, pArray);
6883 }
6884 return JX9_OK;
6885}
6886/* Check if the given year is leap or not */
6887#define IS_LEAP_YEAR(YEAR) (YEAR % 400 ? ( YEAR % 100 ? ( YEAR % 4 ? 0 : 1 ) : 0 ) : 1)
6888/* ISO-8601 numeric representation of the day of the week */
6889static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 };
6890/*
6891 * Format a given date string.
6892 * Supported format: (Taken from JX9 online docs)
6893 * character Description
6894 * d Day of the month
6895 * D A textual representation of a days
6896 * j Day of the month without leading zeros
6897 * l A full textual representation of the day of the week
6898 * N ISO-8601 numeric representation of the day of the week
6899 * w Numeric representation of the day of the week
6900 * z The day of the year (starting from 0)
6901 * F A full textual representation of a month, such as January or March
6902 * m Numeric representation of a month, with leading zeros 01 through 12
6903 * M A short textual representation of a month, three letters Jan through Dec
6904 * n Numeric representation of a month, without leading zeros 1 through 12
6905 * t Number of days in the given month 28 through 31
6906 * L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
6907 * o ISO-8601 year number. This has the same value as Y, except that if the ISO week number
6908 * (W) belongs to the previous or next year, that year is used instead. (added in JX9 5.1.0) Examples: 1999 or 2003
6909 * Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
6910 * y A two digit representation of a year Examples: 99 or 03
6911 * a Lowercase Ante meridiem and Post meridiem am or pm
6912 * A Uppercase Ante meridiem and Post meridiem AM or PM
6913 * g 12-hour format of an hour without leading zeros 1 through 12
6914 * G 24-hour format of an hour without leading zeros 0 through 23
6915 * h 12-hour format of an hour with leading zeros 01 through 12
6916 * H 24-hour format of an hour with leading zeros 00 through 23
6917 * i Minutes with leading zeros 00 to 59
6918 * s Seconds, with leading zeros 00 through 59
6919 * u Microseconds Example: 654321
6920 * e Timezone identifier Examples: UTC, GMT, Atlantic/Azores
6921 * I (capital i) Whether or not the date is in daylight saving time 1 if Daylight Saving Time, 0 otherwise.
6922 * r RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 +0200
6923 * U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
6924 * S English ordinal suffix for the day of the month, 2 characters
6925 * O Difference to Greenwich time (GMT) in hours
6926 * Z Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those
6927 * east of UTC is always positive.
6928 * c ISO 8601 date
6929 */
6930static sxi32 DateFormat(jx9_context *pCtx, const char *zIn, int nLen, Sytm *pTm)
6931{
6932 const char *zEnd = &zIn[nLen];
6933 const char *zCur;
6934 /* Start the format process */
6935 for(;;){
6936 if( zIn >= zEnd ){
6937 /* No more input to process */
6938 break;
6939 }
6940 switch(zIn[0]){
6941 case 'd':
6942 /* Day of the month, 2 digits with leading zeros */
6943 jx9_result_string_format(pCtx, "%02d", pTm->tm_mday);
6944 break;
6945 case 'D':
6946 /*A textual representation of a day, three letters*/
6947 zCur = SyTimeGetDay(pTm->tm_wday);
6948 jx9_result_string(pCtx, zCur, 3);
6949 break;
6950 case 'j':
6951 /* Day of the month without leading zeros */
6952 jx9_result_string_format(pCtx, "%d", pTm->tm_mday);
6953 break;
6954 case 'l':
6955 /* A full textual representation of the day of the week */
6956 zCur = SyTimeGetDay(pTm->tm_wday);
6957 jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/);
6958 break;
6959 case 'N':{
6960 /* ISO-8601 numeric representation of the day of the week */
6961 jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]);
6962 break;
6963 }
6964 case 'w':
6965 /*Numeric representation of the day of the week*/
6966 jx9_result_string_format(pCtx, "%d", pTm->tm_wday);
6967 break;
6968 case 'z':
6969 /*The day of the year*/
6970 jx9_result_string_format(pCtx, "%d", pTm->tm_yday);
6971 break;
6972 case 'F':
6973 /*A full textual representation of a month, such as January or March*/
6974 zCur = SyTimeGetMonth(pTm->tm_mon);
6975 jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/);
6976 break;
6977 case 'm':
6978 /*Numeric representation of a month, with leading zeros*/
6979 jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1);
6980 break;
6981 case 'M':
6982 /*A short textual representation of a month, three letters*/
6983 zCur = SyTimeGetMonth(pTm->tm_mon);
6984 jx9_result_string(pCtx, zCur, 3);
6985 break;
6986 case 'n':
6987 /*Numeric representation of a month, without leading zeros*/
6988 jx9_result_string_format(pCtx, "%d", pTm->tm_mon + 1);
6989 break;
6990 case 't':{
6991 static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
6992 int nDays = aMonDays[pTm->tm_mon % 12 ];
6993 if( pTm->tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(pTm->tm_year) ){
6994 nDays = 28;
6995 }
6996 /*Number of days in the given month*/
6997 jx9_result_string_format(pCtx, "%d", nDays);
6998 break;
6999 }
7000 case 'L':{
7001 int isLeap = IS_LEAP_YEAR(pTm->tm_year);
7002 /* Whether it's a leap year */
7003 jx9_result_string_format(pCtx, "%d", isLeap);
7004 break;
7005 }
7006 case 'o':
7007 /* ISO-8601 year number.*/
7008 jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
7009 break;
7010 case 'Y':
7011 /* A full numeric representation of a year, 4 digits */
7012 jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
7013 break;
7014 case 'y':
7015 /*A two digit representation of a year*/
7016 jx9_result_string_format(pCtx, "%02d", pTm->tm_year%100);
7017 break;
7018 case 'a':
7019 /* Lowercase Ante meridiem and Post meridiem */
7020 jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", 2);
7021 break;
7022 case 'A':
7023 /* Uppercase Ante meridiem and Post meridiem */
7024 jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", 2);
7025 break;
7026 case 'g':
7027 /* 12-hour format of an hour without leading zeros*/
7028 jx9_result_string_format(pCtx, "%d", 1+(pTm->tm_hour%12));
7029 break;
7030 case 'G':
7031 /* 24-hour format of an hour without leading zeros */
7032 jx9_result_string_format(pCtx, "%d", pTm->tm_hour);
7033 break;
7034 case 'h':
7035 /* 12-hour format of an hour with leading zeros */
7036 jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12));
7037 break;
7038 case 'H':
7039 /* 24-hour format of an hour with leading zeros */
7040 jx9_result_string_format(pCtx, "%02d", pTm->tm_hour);
7041 break;
7042 case 'i':
7043 /* Minutes with leading zeros */
7044 jx9_result_string_format(pCtx, "%02d", pTm->tm_min);
7045 break;
7046 case 's':
7047 /* second with leading zeros */
7048 jx9_result_string_format(pCtx, "%02d", pTm->tm_sec);
7049 break;
7050 case 'u':
7051 /* Microseconds */
7052 jx9_result_string_format(pCtx, "%u", pTm->tm_sec * SX_USEC_PER_SEC);
7053 break;
7054 case 'S':{
7055 /* English ordinal suffix for the day of the month, 2 characters */
7056 static const char zSuffix[] = "thstndrdthththththth";
7057 int v = pTm->tm_mday;
7058 jx9_result_string(pCtx, &zSuffix[2 * (int)(v / 10 % 10 != 1 ? v % 10 : 0)], (int)sizeof(char) * 2);
7059 break;
7060 }
7061 case 'e':
7062 /* Timezone identifier */
7063 zCur = pTm->tm_zone;
7064 if( zCur == 0 ){
7065 /* Assume GMT */
7066 zCur = "GMT";
7067 }
7068 jx9_result_string(pCtx, zCur, -1);
7069 break;
7070 case 'I':
7071 /* Whether or not the date is in daylight saving time */
7072#ifdef __WINNT__
7073#ifdef _MSC_VER
7074#ifndef _WIN32_WCE
7075 _get_daylight(&pTm->tm_isdst);
7076#endif
7077#endif
7078#endif
7079 jx9_result_string_format(pCtx, "%d", pTm->tm_isdst == 1);
7080 break;
7081 case 'r':
7082 /* RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 */
7083 jx9_result_string_format(pCtx, "%.3s, %02d %.3s %4d %02d:%02d:%02d",
7084 SyTimeGetDay(pTm->tm_wday),
7085 pTm->tm_mday,
7086 SyTimeGetMonth(pTm->tm_mon),
7087 pTm->tm_year,
7088 pTm->tm_hour,
7089 pTm->tm_min,
7090 pTm->tm_sec
7091 );
7092 break;
7093 case 'U':{
7094 time_t tt;
7095 /* Seconds since the Unix Epoch */
7096 time(&tt);
7097 jx9_result_string_format(pCtx, "%u", (unsigned int)tt);
7098 break;
7099 }
7100 case 'O':
7101 case 'P':
7102 /* Difference to Greenwich time (GMT) in hours */
7103 jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff);
7104 break;
7105 case 'Z':
7106 /* Timezone offset in seconds. The offset for timezones west of UTC
7107 * is always negative, and for those east of UTC is always positive.
7108 */
7109 jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff);
7110 break;
7111 case 'c':
7112 /* ISO 8601 date */
7113 jx9_result_string_format(pCtx, "%4d-%02d-%02dT%02d:%02d:%02d%+05d",
7114 pTm->tm_year,
7115 pTm->tm_mon+1,
7116 pTm->tm_mday,
7117 pTm->tm_hour,
7118 pTm->tm_min,
7119 pTm->tm_sec,
7120 pTm->tm_gmtoff
7121 );
7122 break;
7123 case '\\':
7124 zIn++;
7125 /* Expand verbatim */
7126 if( zIn < zEnd ){
7127 jx9_result_string(pCtx, zIn, (int)sizeof(char));
7128 }
7129 break;
7130 default:
7131 /* Unknown format specifer, expand verbatim */
7132 jx9_result_string(pCtx, zIn, (int)sizeof(char));
7133 break;
7134 }
7135 /* Point to the next character */
7136 zIn++;
7137 }
7138 return SXRET_OK;
7139}
7140/*
7141 * JX9 implementation of the strftime() function.
7142 * The following formats are supported:
7143 * %a An abbreviated textual representation of the day
7144 * %A A full textual representation of the day
7145 * %d Two-digit day of the month (with leading zeros)
7146 * %e Day of the month, with a space preceding single digits.
7147 * %j Day of the year, 3 digits with leading zeros
7148 * %u ISO-8601 numeric representation of the day of the week 1 (for Monday) though 7 (for Sunday)
7149 * %w Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
7150 * %U Week number of the given year, starting with the first Sunday as the first week
7151 * %V ISO-8601:1988 week number of the given year, starting with the first week of the year with at least
7152 * 4 weekdays, with Monday being the start of the week.
7153 * %W A numeric representation of the week of the year
7154 * %b Abbreviated month name, based on the locale
7155 * %B Full month name, based on the locale
7156 * %h Abbreviated month name, based on the locale (an alias of %b)
7157 * %m Two digit representation of the month
7158 * %C Two digit representation of the century (year divided by 100, truncated to an integer)
7159 * %g Two digit representation of the year going by ISO-8601:1988 standards (see %V)
7160 * %G The full four-digit version of %g
7161 * %y Two digit representation of the year
7162 * %Y Four digit representation for the year
7163 * %H Two digit representation of the hour in 24-hour format
7164 * %I Two digit representation of the hour in 12-hour format
7165 * %l (lower-case 'L') Hour in 12-hour format, with a space preceeding single digits
7166 * %M Two digit representation of the minute
7167 * %p UPPER-CASE 'AM' or 'PM' based on the given time
7168 * %P lower-case 'am' or 'pm' based on the given time
7169 * %r Same as "%I:%M:%S %p"
7170 * %R Same as "%H:%M"
7171 * %S Two digit representation of the second
7172 * %T Same as "%H:%M:%S"
7173 * %X Preferred time representation based on locale, without the date
7174 * %z Either the time zone offset from UTC or the abbreviation
7175 * %Z The time zone offset/abbreviation option NOT given by %z
7176 * %c Preferred date and time stamp based on local
7177 * %D Same as "%m/%d/%y"
7178 * %F Same as "%Y-%m-%d"
7179 * %s Unix Epoch Time timestamp (same as the time() function)
7180 * %x Preferred date representation based on locale, without the time
7181 * %n A newline character ("\n")
7182 * %t A Tab character ("\t")
7183 * %% A literal percentage character ("%")
7184 */
7185static int jx9Strftime(
7186 jx9_context *pCtx, /* Call context */
7187 const char *zIn, /* Input string */
7188 int nLen, /* Input length */
7189 Sytm *pTm /* Parse of the given time */
7190 )
7191{
7192 const char *zCur, *zEnd = &zIn[nLen];
7193 int c;
7194 /* Start the format process */
7195 for(;;){
7196 zCur = zIn;
7197 while(zIn < zEnd && zIn[0] != '%' ){
7198 zIn++;
7199 }
7200 if( zIn > zCur ){
7201 /* Consume input verbatim */
7202 jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
7203 }
7204 zIn++; /* Jump the percent sign */
7205 if( zIn >= zEnd ){
7206 /* No more input to process */
7207 break;
7208 }
7209 c = zIn[0];
7210 /* Act according to the current specifer */
7211 switch(c){
7212 case '%':
7213 /* A literal percentage character ("%") */
7214 jx9_result_string(pCtx, "%", (int)sizeof(char));
7215 break;
7216 case 't':
7217 /* A Tab character */
7218 jx9_result_string(pCtx, "\t", (int)sizeof(char));
7219 break;
7220 case 'n':
7221 /* A newline character */
7222 jx9_result_string(pCtx, "\n", (int)sizeof(char));
7223 break;
7224 case 'a':
7225 /* An abbreviated textual representation of the day */
7226 jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), (int)sizeof(char)*3);
7227 break;
7228 case 'A':
7229 /* A full textual representation of the day */
7230 jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), -1/*Compute length automatically*/);
7231 break;
7232 case 'e':
7233 /* Day of the month, 2 digits with leading space for single digit*/
7234 jx9_result_string_format(pCtx, "%2d", pTm->tm_mday);
7235 break;
7236 case 'd':
7237 /* Two-digit day of the month (with leading zeros) */
7238 jx9_result_string_format(pCtx, "%02d", pTm->tm_mon+1);
7239 break;
7240 case 'j':
7241 /*The day of the year, 3 digits with leading zeros*/
7242 jx9_result_string_format(pCtx, "%03d", pTm->tm_yday);
7243 break;
7244 case 'u':
7245 /* ISO-8601 numeric representation of the day of the week */
7246 jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]);
7247 break;
7248 case 'w':
7249 /* Numeric representation of the day of the week */
7250 jx9_result_string_format(pCtx, "%d", pTm->tm_wday);
7251 break;
7252 case 'b':
7253 case 'h':
7254 /*A short textual representation of a month, three letters (Not based on locale)*/
7255 jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), (int)sizeof(char)*3);
7256 break;
7257 case 'B':
7258 /* Full month name (Not based on locale) */
7259 jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), -1/*Compute length automatically*/);
7260 break;
7261 case 'm':
7262 /*Numeric representation of a month, with leading zeros*/
7263 jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1);
7264 break;
7265 case 'C':
7266 /* Two digit representation of the century */
7267 jx9_result_string_format(pCtx, "%2d", pTm->tm_year/100);
7268 break;
7269 case 'y':
7270 case 'g':
7271 /* Two digit representation of the year */
7272 jx9_result_string_format(pCtx, "%2d", pTm->tm_year%100);
7273 break;
7274 case 'Y':
7275 case 'G':
7276 /* Four digit representation of the year */
7277 jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
7278 break;
7279 case 'I':
7280 /* 12-hour format of an hour with leading zeros */
7281 jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12));
7282 break;
7283 case 'l':
7284 /* 12-hour format of an hour with leading space */
7285 jx9_result_string_format(pCtx, "%2d", 1+(pTm->tm_hour%12));
7286 break;
7287 case 'H':
7288 /* 24-hour format of an hour with leading zeros */
7289 jx9_result_string_format(pCtx, "%02d", pTm->tm_hour);
7290 break;
7291 case 'M':
7292 /* Minutes with leading zeros */
7293 jx9_result_string_format(pCtx, "%02d", pTm->tm_min);
7294 break;
7295 case 'S':
7296 /* Seconds with leading zeros */
7297 jx9_result_string_format(pCtx, "%02d", pTm->tm_sec);
7298 break;
7299 case 'z':
7300 case 'Z':
7301 /* Timezone identifier */
7302 zCur = pTm->tm_zone;
7303 if( zCur == 0 ){
7304 /* Assume GMT */
7305 zCur = "GMT";
7306 }
7307 jx9_result_string(pCtx, zCur, -1);
7308 break;
7309 case 'T':
7310 case 'X':
7311 /* Same as "%H:%M:%S" */
7312 jx9_result_string_format(pCtx, "%02d:%02d:%02d", pTm->tm_hour, pTm->tm_min, pTm->tm_sec);
7313 break;
7314 case 'R':
7315 /* Same as "%H:%M" */
7316 jx9_result_string_format(pCtx, "%02d:%02d", pTm->tm_hour, pTm->tm_min);
7317 break;
7318 case 'P':
7319 /* Lowercase Ante meridiem and Post meridiem */
7320 jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", (int)sizeof(char)*2);
7321 break;
7322 case 'p':
7323 /* Uppercase Ante meridiem and Post meridiem */
7324 jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", (int)sizeof(char)*2);
7325 break;
7326 case 'r':
7327 /* Same as "%I:%M:%S %p" */
7328 jx9_result_string_format(pCtx, "%02d:%02d:%02d %s",
7329 1+(pTm->tm_hour%12),
7330 pTm->tm_min,
7331 pTm->tm_sec,
7332 pTm->tm_hour > 12 ? "PM" : "AM"
7333 );
7334 break;
7335 case 'D':
7336 case 'x':
7337 /* Same as "%m/%d/%y" */
7338 jx9_result_string_format(pCtx, "%02d/%02d/%02d",
7339 pTm->tm_mon+1,
7340 pTm->tm_mday,
7341 pTm->tm_year%100
7342 );
7343 break;
7344 case 'F':
7345 /* Same as "%Y-%m-%d" */
7346 jx9_result_string_format(pCtx, "%d-%02d-%02d",
7347 pTm->tm_year,
7348 pTm->tm_mon+1,
7349 pTm->tm_mday
7350 );
7351 break;
7352 case 'c':
7353 jx9_result_string_format(pCtx, "%d-%02d-%02d %02d:%02d:%02d",
7354 pTm->tm_year,
7355 pTm->tm_mon+1,
7356 pTm->tm_mday,
7357 pTm->tm_hour,
7358 pTm->tm_min,
7359 pTm->tm_sec
7360 );
7361 break;
7362 case 's':{
7363 time_t tt;
7364 /* Seconds since the Unix Epoch */
7365 time(&tt);
7366 jx9_result_string_format(pCtx, "%u", (unsigned int)tt);
7367 break;
7368 }
7369 default:
7370 /* unknown specifer, simply ignore*/
7371 break;
7372 }
7373 /* Advance the cursor */
7374 zIn++;
7375 }
7376 return SXRET_OK;
7377}
7378/*
7379 * string date(string $format [, int $timestamp = time() ] )
7380 * Returns a string formatted according to the given format string using
7381 * the given integer timestamp or the current time if no timestamp is given.
7382 * In other words, timestamp is optional and defaults to the value of time().
7383 * Parameters
7384 * $format
7385 * The format of the outputted date string (See code above)
7386 * $timestamp
7387 * The optional timestamp parameter is an integer Unix timestamp
7388 * that defaults to the current local time if a timestamp is not given.
7389 * In other words, it defaults to the value of time().
7390 * Return
7391 * A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned.
7392 */
7393static int jx9Builtin_date(jx9_context *pCtx, int nArg, jx9_value **apArg)
7394{
7395 const char *zFormat;
7396 int nLen;
7397 Sytm sTm;
7398 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
7399 /* Missing/Invalid argument, return FALSE */
7400 jx9_result_bool(pCtx, 0);
7401 return JX9_OK;
7402 }
7403 zFormat = jx9_value_to_string(apArg[0], &nLen);
7404 if( nLen < 1 ){
7405 /* Don't bother processing return the empty string */
7406 jx9_result_string(pCtx, "", 0);
7407 }
7408 if( nArg < 2 ){
7409#ifdef __WINNT__
7410 SYSTEMTIME sOS;
7411 GetSystemTime(&sOS);
7412 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
7413#else
7414 struct tm *pTm;
7415 time_t t;
7416 time(&t);
7417 pTm = localtime(&t);
7418 STRUCT_TM_TO_SYTM(pTm, &sTm);
7419#endif
7420 }else{
7421 /* Use the given timestamp */
7422 time_t t;
7423 struct tm *pTm;
7424 if( jx9_value_is_int(apArg[1]) ){
7425 t = (time_t)jx9_value_to_int64(apArg[1]);
7426 pTm = localtime(&t);
7427 if( pTm == 0 ){
7428 time(&t);
7429 }
7430 }else{
7431 time(&t);
7432 }
7433 pTm = localtime(&t);
7434 STRUCT_TM_TO_SYTM(pTm, &sTm);
7435 }
7436 /* Format the given string */
7437 DateFormat(pCtx, zFormat, nLen, &sTm);
7438 return JX9_OK;
7439}
7440/*
7441 * string strftime(string $format [, int $timestamp = time() ] )
7442 * Format a local time/date (PLATFORM INDEPENDANT IMPLEENTATION NOT BASED ON LOCALE)
7443 * Parameters
7444 * $format
7445 * The format of the outputted date string (See code above)
7446 * $timestamp
7447 * The optional timestamp parameter is an integer Unix timestamp
7448 * that defaults to the current local time if a timestamp is not given.
7449 * In other words, it defaults to the value of time().
7450 * Return
7451 * Returns a string formatted according format using the given timestamp
7452 * or the current local time if no timestamp is given.
7453 */
7454static int jx9Builtin_strftime(jx9_context *pCtx, int nArg, jx9_value **apArg)
7455{
7456 const char *zFormat;
7457 int nLen;
7458 Sytm sTm;
7459 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
7460 /* Missing/Invalid argument, return FALSE */
7461 jx9_result_bool(pCtx, 0);
7462 return JX9_OK;
7463 }
7464 zFormat = jx9_value_to_string(apArg[0], &nLen);
7465 if( nLen < 1 ){
7466 /* Don't bother processing return FALSE */
7467 jx9_result_bool(pCtx, 0);
7468 }
7469 if( nArg < 2 ){
7470#ifdef __WINNT__
7471 SYSTEMTIME sOS;
7472 GetSystemTime(&sOS);
7473 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
7474#else
7475 struct tm *pTm;
7476 time_t t;
7477 time(&t);
7478 pTm = localtime(&t);
7479 STRUCT_TM_TO_SYTM(pTm, &sTm);
7480#endif
7481 }else{
7482 /* Use the given timestamp */
7483 time_t t;
7484 struct tm *pTm;
7485 if( jx9_value_is_int(apArg[1]) ){
7486 t = (time_t)jx9_value_to_int64(apArg[1]);
7487 pTm = localtime(&t);
7488 if( pTm == 0 ){
7489 time(&t);
7490 }
7491 }else{
7492 time(&t);
7493 }
7494 pTm = localtime(&t);
7495 STRUCT_TM_TO_SYTM(pTm, &sTm);
7496 }
7497 /* Format the given string */
7498 jx9Strftime(pCtx, zFormat, nLen, &sTm);
7499 if( jx9_context_result_buf_length(pCtx) < 1 ){
7500 /* Nothing was formatted, return FALSE */
7501 jx9_result_bool(pCtx, 0);
7502 }
7503 return JX9_OK;
7504}
7505/*
7506 * string gmdate(string $format [, int $timestamp = time() ] )
7507 * Identical to the date() function except that the time returned
7508 * is Greenwich Mean Time (GMT).
7509 * Parameters
7510 * $format
7511 * The format of the outputted date string (See code above)
7512 * $timestamp
7513 * The optional timestamp parameter is an integer Unix timestamp
7514 * that defaults to the current local time if a timestamp is not given.
7515 * In other words, it defaults to the value of time().
7516 * Return
7517 * A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned.
7518 */
7519static int jx9Builtin_gmdate(jx9_context *pCtx, int nArg, jx9_value **apArg)
7520{
7521 const char *zFormat;
7522 int nLen;
7523 Sytm sTm;
7524 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
7525 /* Missing/Invalid argument, return FALSE */
7526 jx9_result_bool(pCtx, 0);
7527 return JX9_OK;
7528 }
7529 zFormat = jx9_value_to_string(apArg[0], &nLen);
7530 if( nLen < 1 ){
7531 /* Don't bother processing return the empty string */
7532 jx9_result_string(pCtx, "", 0);
7533 }
7534 if( nArg < 2 ){
7535#ifdef __WINNT__
7536 SYSTEMTIME sOS;
7537 GetSystemTime(&sOS);
7538 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
7539#else
7540 struct tm *pTm;
7541 time_t t;
7542 time(&t);
7543 pTm = gmtime(&t);
7544 STRUCT_TM_TO_SYTM(pTm, &sTm);
7545#endif
7546 }else{
7547 /* Use the given timestamp */
7548 time_t t;
7549 struct tm *pTm;
7550 if( jx9_value_is_int(apArg[1]) ){
7551 t = (time_t)jx9_value_to_int64(apArg[1]);
7552 pTm = gmtime(&t);
7553 if( pTm == 0 ){
7554 time(&t);
7555 }
7556 }else{
7557 time(&t);
7558 }
7559 pTm = gmtime(&t);
7560 STRUCT_TM_TO_SYTM(pTm, &sTm);
7561 }
7562 /* Format the given string */
7563 DateFormat(pCtx, zFormat, nLen, &sTm);
7564 return JX9_OK;
7565}
7566/*
7567 * array localtime([ int $timestamp = time() [, bool $is_associative = false ]])
7568 * Return the local time.
7569 * Parameter
7570 * $timestamp: The optional timestamp parameter is an integer Unix timestamp
7571 * that defaults to the current local time if a timestamp is not given.
7572 * In other words, it defaults to the value of time().
7573 * $is_associative
7574 * If set to FALSE or not supplied then the array is returned as a regular, numerically
7575 * indexed array. If the argument is set to TRUE then localtime() returns an associative
7576 * array containing all the different elements of the structure returned by the C function
7577 * call to localtime. The names of the different keys of the associative array are as follows:
7578 * "tm_sec" - seconds, 0 to 59
7579 * "tm_min" - minutes, 0 to 59
7580 * "tm_hour" - hours, 0 to 23
7581 * "tm_mday" - day of the month, 1 to 31
7582 * "tm_mon" - month of the year, 0 (Jan) to 11 (Dec)
7583 * "tm_year" - years since 1900
7584 * "tm_wday" - day of the week, 0 (Sun) to 6 (Sat)
7585 * "tm_yday" - day of the year, 0 to 365
7586 * "tm_isdst" - is daylight savings time in effect? Positive if yes, 0 if not, negative if unknown.
7587 * Returns
7588 * An associative array of information related to the timestamp.
7589 */
7590static int jx9Builtin_localtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
7591{
7592 jx9_value *pValue, *pArray;
7593 int isAssoc = 0;
7594 Sytm sTm;
7595 if( nArg < 1 ){
7596#ifdef __WINNT__
7597 SYSTEMTIME sOS;
7598 GetSystemTime(&sOS); /* TODO(chems): GMT not local */
7599 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
7600#else
7601 struct tm *pTm;
7602 time_t t;
7603 time(&t);
7604 pTm = localtime(&t);
7605 STRUCT_TM_TO_SYTM(pTm, &sTm);
7606#endif
7607 }else{
7608 /* Use the given timestamp */
7609 time_t t;
7610 struct tm *pTm;
7611 if( jx9_value_is_int(apArg[0]) ){
7612 t = (time_t)jx9_value_to_int64(apArg[0]);
7613 pTm = localtime(&t);
7614 if( pTm == 0 ){
7615 time(&t);
7616 }
7617 }else{
7618 time(&t);
7619 }
7620 pTm = localtime(&t);
7621 STRUCT_TM_TO_SYTM(pTm, &sTm);
7622 }
7623 /* Element value */
7624 pValue = jx9_context_new_scalar(pCtx);
7625 if( pValue == 0 ){
7626 /* Return NULL */
7627 jx9_result_null(pCtx);
7628 return JX9_OK;
7629 }
7630 /* Create a new array */
7631 pArray = jx9_context_new_array(pCtx);
7632 if( pArray == 0 ){
7633 /* Return NULL */
7634 jx9_result_null(pCtx);
7635 return JX9_OK;
7636 }
7637 if( nArg > 1 ){
7638 isAssoc = jx9_value_to_bool(apArg[1]);
7639 }
7640 /* Fill the array */
7641 /* Seconds */
7642 jx9_value_int(pValue, sTm.tm_sec);
7643 if( isAssoc ){
7644 jx9_array_add_strkey_elem(pArray, "tm_sec", pValue);
7645 }else{
7646 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
7647 }
7648 /* Minutes */
7649 jx9_value_int(pValue, sTm.tm_min);
7650 if( isAssoc ){
7651 jx9_array_add_strkey_elem(pArray, "tm_min", pValue);
7652 }else{
7653 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
7654 }
7655 /* Hours */
7656 jx9_value_int(pValue, sTm.tm_hour);
7657 if( isAssoc ){
7658 jx9_array_add_strkey_elem(pArray, "tm_hour", pValue);
7659 }else{
7660 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
7661 }
7662 /* mday */
7663 jx9_value_int(pValue, sTm.tm_mday);
7664 if( isAssoc ){
7665 jx9_array_add_strkey_elem(pArray, "tm_mday", pValue);
7666 }else{
7667 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
7668 }
7669 /* mon */
7670 jx9_value_int(pValue, sTm.tm_mon);
7671 if( isAssoc ){
7672 jx9_array_add_strkey_elem(pArray, "tm_mon", pValue);
7673 }else{
7674 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
7675 }
7676 /* year since 1900 */
7677 jx9_value_int(pValue, sTm.tm_year-1900);
7678 if( isAssoc ){
7679 jx9_array_add_strkey_elem(pArray, "tm_year", pValue);
7680 }else{
7681 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
7682 }
7683 /* wday */
7684 jx9_value_int(pValue, sTm.tm_wday);
7685 if( isAssoc ){
7686 jx9_array_add_strkey_elem(pArray, "tm_wday", pValue);
7687 }else{
7688 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
7689 }
7690 /* yday */
7691 jx9_value_int(pValue, sTm.tm_yday);
7692 if( isAssoc ){
7693 jx9_array_add_strkey_elem(pArray, "tm_yday", pValue);
7694 }else{
7695 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
7696 }
7697 /* isdst */
7698#ifdef __WINNT__
7699#ifdef _MSC_VER
7700#ifndef _WIN32_WCE
7701 _get_daylight(&sTm.tm_isdst);
7702#endif
7703#endif
7704#endif
7705 jx9_value_int(pValue, sTm.tm_isdst);
7706 if( isAssoc ){
7707 jx9_array_add_strkey_elem(pArray, "tm_isdst", pValue);
7708 }else{
7709 jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
7710 }
7711 /* Return the array */
7712 jx9_result_value(pCtx, pArray);
7713 return JX9_OK;
7714}
7715/*
7716 * int idate(string $format [, int $timestamp = time() ])
7717 * Returns a number formatted according to the given format string
7718 * using the given integer timestamp or the current local time if
7719 * no timestamp is given. In other words, timestamp is optional and defaults
7720 * to the value of time().
7721 * Unlike the function date(), idate() accepts just one char in the format
7722 * parameter.
7723 * $Parameters
7724 * Supported format
7725 * d Day of the month
7726 * h Hour (12 hour format)
7727 * H Hour (24 hour format)
7728 * i Minutes
7729 * I (uppercase i)1 if DST is activated, 0 otherwise
7730 * L (uppercase l) returns 1 for leap year, 0 otherwise
7731 * m Month number
7732 * s Seconds
7733 * t Days in current month
7734 * U Seconds since the Unix Epoch - January 1 1970 00:00:00 UTC - this is the same as time()
7735 * w Day of the week (0 on Sunday)
7736 * W ISO-8601 week number of year, weeks starting on Monday
7737 * y Year (1 or 2 digits - check note below)
7738 * Y Year (4 digits)
7739 * z Day of the year
7740 * Z Timezone offset in seconds
7741 * $timestamp
7742 * The optional timestamp parameter is an integer Unix timestamp that defaults
7743 * to the current local time if a timestamp is not given. In other words, it defaults
7744 * to the value of time().
7745 * Return
7746 * An integer.
7747 */
7748static int jx9Builtin_idate(jx9_context *pCtx, int nArg, jx9_value **apArg)
7749{
7750 const char *zFormat;
7751 jx9_int64 iVal = 0;
7752 int nLen;
7753 Sytm sTm;
7754 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
7755 /* Missing/Invalid argument, return -1 */
7756 jx9_result_int(pCtx, -1);
7757 return JX9_OK;
7758 }
7759 zFormat = jx9_value_to_string(apArg[0], &nLen);
7760 if( nLen < 1 ){
7761 /* Don't bother processing return -1*/
7762 jx9_result_int(pCtx, -1);
7763 }
7764 if( nArg < 2 ){
7765#ifdef __WINNT__
7766 SYSTEMTIME sOS;
7767 GetSystemTime(&sOS);
7768 SYSTEMTIME_TO_SYTM(&sOS, &sTm);
7769#else
7770 struct tm *pTm;
7771 time_t t;
7772 time(&t);
7773 pTm = localtime(&t);
7774 STRUCT_TM_TO_SYTM(pTm, &sTm);
7775#endif
7776 }else{
7777 /* Use the given timestamp */
7778 time_t t;
7779 struct tm *pTm;
7780 if( jx9_value_is_int(apArg[1]) ){
7781 t = (time_t)jx9_value_to_int64(apArg[1]);
7782 pTm = localtime(&t);
7783 if( pTm == 0 ){
7784 time(&t);
7785 }
7786 }else{
7787 time(&t);
7788 }
7789 pTm = localtime(&t);
7790 STRUCT_TM_TO_SYTM(pTm, &sTm);
7791 }
7792 /* Perform the requested operation */
7793 switch(zFormat[0]){
7794 case 'd':
7795 /* Day of the month */
7796 iVal = sTm.tm_mday;
7797 break;
7798 case 'h':
7799 /* Hour (12 hour format)*/
7800 iVal = 1 + (sTm.tm_hour % 12);
7801 break;
7802 case 'H':
7803 /* Hour (24 hour format)*/
7804 iVal = sTm.tm_hour;
7805 break;
7806 case 'i':
7807 /*Minutes*/
7808 iVal = sTm.tm_min;
7809 break;
7810 case 'I':
7811 /* returns 1 if DST is activated, 0 otherwise */
7812#ifdef __WINNT__
7813#ifdef _MSC_VER
7814#ifndef _WIN32_WCE
7815 _get_daylight(&sTm.tm_isdst);
7816#endif
7817#endif
7818#endif
7819 iVal = sTm.tm_isdst;
7820 break;
7821 case 'L':
7822 /* returns 1 for leap year, 0 otherwise */
7823 iVal = IS_LEAP_YEAR(sTm.tm_year);
7824 break;
7825 case 'm':
7826 /* Month number*/
7827 iVal = sTm.tm_mon;
7828 break;
7829 case 's':
7830 /*Seconds*/
7831 iVal = sTm.tm_sec;
7832 break;
7833 case 't':{
7834 /*Days in current month*/
7835 static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
7836 int nDays = aMonDays[sTm.tm_mon % 12 ];
7837 if( sTm.tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(sTm.tm_year) ){
7838 nDays = 28;
7839 }
7840 iVal = nDays;
7841 break;
7842 }
7843 case 'U':
7844 /*Seconds since the Unix Epoch*/
7845 iVal = (jx9_int64)time(0);
7846 break;
7847 case 'w':
7848 /* Day of the week (0 on Sunday) */
7849 iVal = sTm.tm_wday;
7850 break;
7851 case 'W': {
7852 /* ISO-8601 week number of year, weeks starting on Monday */
7853 static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 };
7854 iVal = aISO8601[sTm.tm_wday % 7 ];
7855 break;
7856 }
7857 case 'y':
7858 /* Year (2 digits) */
7859 iVal = sTm.tm_year % 100;
7860 break;
7861 case 'Y':
7862 /* Year (4 digits) */
7863 iVal = sTm.tm_year;
7864 break;
7865 case 'z':
7866 /* Day of the year */
7867 iVal = sTm.tm_yday;
7868 break;
7869 case 'Z':
7870 /*Timezone offset in seconds*/
7871 iVal = sTm.tm_gmtoff;
7872 break;
7873 default:
7874 /* unknown format, throw a warning */
7875 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Unknown date format token");
7876 break;
7877 }
7878 /* Return the time value */
7879 jx9_result_int64(pCtx, iVal);
7880 return JX9_OK;
7881}
7882/*
7883 * int mktime/gmmktime([ int $hour = date("H") [, int $minute = date("i") [, int $second = date("s")
7884 * [, int $month = date("n") [, int $day = date("j") [, int $year = date("Y") [, int $is_dst = -1 ]]]]]]] )
7885 * Returns the Unix timestamp corresponding to the arguments given. This timestamp is a 64bit integer
7886 * containing the number of seconds between the Unix Epoch (January 1 1970 00:00:00 GMT) and the time
7887 * specified.
7888 * Arguments may be left out in order from right to left; any arguments thus omitted will be set to
7889 * the current value according to the local date and time.
7890 * Parameters
7891 * $hour
7892 * The number of the hour relevant to the start of the day determined by month, day and year.
7893 * Negative values reference the hour before midnight of the day in question. Values greater
7894 * than 23 reference the appropriate hour in the following day(s).
7895 * $minute
7896 * The number of the minute relevant to the start of the hour. Negative values reference
7897 * the minute in the previous hour. Values greater than 59 reference the appropriate minute
7898 * in the following hour(s).
7899 * $second
7900 * The number of seconds relevant to the start of the minute. Negative values reference
7901 * the second in the previous minute. Values greater than 59 reference the appropriate
7902 * second in the following minute(s).
7903 * $month
7904 * The number of the month relevant to the end of the previous year. Values 1 to 12 reference
7905 * the normal calendar months of the year in question. Values less than 1 (including negative values)
7906 * reference the months in the previous year in reverse order, so 0 is December, -1 is November)...
7907 * $day
7908 * The number of the day relevant to the end of the previous month. Values 1 to 28, 29, 30 or 31
7909 * (depending upon the month) reference the normal days in the relevant month. Values less than 1
7910 * (including negative values) reference the days in the previous month, so 0 is the last day
7911 * of the previous month, -1 is the day before that, etc. Values greater than the number of days
7912 * in the relevant month reference the appropriate day in the following month(s).
7913 * $year
7914 * The number of the year, may be a two or four digit value, with values between 0-69 mapping
7915 * to 2000-2069 and 70-100 to 1970-2000. On systems where time_t is a 32bit signed integer, as
7916 * most common today, the valid range for year is somewhere between 1901 and 2038.
7917 * $is_dst
7918 * This parameter can be set to 1 if the time is during daylight savings time (DST), 0 if it is not,
7919 * or -1 (the default) if it is unknown whether the time is within daylight savings time or not.
7920 * Return
7921 * mktime() returns the Unix timestamp of the arguments given.
7922 * If the arguments are invalid, the function returns FALSE
7923 */
7924static int jx9Builtin_mktime(jx9_context *pCtx, int nArg, jx9_value **apArg)
7925{
7926 const char *zFunction;
7927 jx9_int64 iVal = 0;
7928 struct tm *pTm;
7929 time_t t;
7930 /* Extract function name */
7931 zFunction = jx9_function_name(pCtx);
7932 /* Get the current time */
7933 time(&t);
7934 if( zFunction[0] == 'g' /* gmmktime */ ){
7935 pTm = gmtime(&t);
7936 }else{
7937 /* localtime */
7938 pTm = localtime(&t);
7939 }
7940 if( nArg > 0 ){
7941 int iVal;
7942 /* Hour */
7943 iVal = jx9_value_to_int(apArg[0]);
7944 pTm->tm_hour = iVal;
7945 if( nArg > 1 ){
7946 /* Minutes */
7947 iVal = jx9_value_to_int(apArg[1]);
7948 pTm->tm_min = iVal;
7949 if( nArg > 2 ){
7950 /* Seconds */
7951 iVal = jx9_value_to_int(apArg[2]);
7952 pTm->tm_sec = iVal;
7953 if( nArg > 3 ){
7954 /* Month */
7955 iVal = jx9_value_to_int(apArg[3]);
7956 pTm->tm_mon = iVal - 1;
7957 if( nArg > 4 ){
7958 /* mday */
7959 iVal = jx9_value_to_int(apArg[4]);
7960 pTm->tm_mday = iVal;
7961 if( nArg > 5 ){
7962 /* Year */
7963 iVal = jx9_value_to_int(apArg[5]);
7964 if( iVal > 1900 ){
7965 iVal -= 1900;
7966 }
7967 pTm->tm_year = iVal;
7968 if( nArg > 6 ){
7969 /* is_dst */
7970 iVal = jx9_value_to_bool(apArg[6]);
7971 pTm->tm_isdst = iVal;
7972 }
7973 }
7974 }
7975 }
7976 }
7977 }
7978 }
7979 /* Make the time */
7980 iVal = (jx9_int64)mktime(pTm);
7981 /* Return the timesatmp as a 64bit integer */
7982 jx9_result_int64(pCtx, iVal);
7983 return JX9_OK;
7984}
7985/*
7986 * Section:
7987 * URL handling Functions.
7988 * Authors:
7989 * Symisc Systems, devel@symisc.net.
7990 * Copyright (C) Symisc Systems, http://jx9.symisc.net
7991 * Status:
7992 * Stable.
7993 */
7994/*
7995 * Output consumer callback for the standard Symisc routines.
7996 * [i.e: SyBase64Encode(), SyBase64Decode(), SyUriEncode(), ...].
7997 */
7998static int Consumer(const void *pData, unsigned int nLen, void *pUserData)
7999{
8000 /* Store in the call context result buffer */
8001 jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
8002 return SXRET_OK;
8003}
8004/*
8005 * string base64_encode(string $data)
8006 * string convert_uuencode(string $data)
8007 * Encodes data with MIME base64
8008 * Parameter
8009 * $data
8010 * Data to encode
8011 * Return
8012 * Encoded data or FALSE on failure.
8013 */
8014static int jx9Builtin_base64_encode(jx9_context *pCtx, int nArg, jx9_value **apArg)
8015{
8016 const char *zIn;
8017 int nLen;
8018 if( nArg < 1 ){
8019 /* Missing arguments, return FALSE */
8020 jx9_result_bool(pCtx, 0);
8021 return JX9_OK;
8022 }
8023 /* Extract the input string */
8024 zIn = jx9_value_to_string(apArg[0], &nLen);
8025 if( nLen < 1 ){
8026 /* Nothing to process, return FALSE */
8027 jx9_result_bool(pCtx, 0);
8028 return JX9_OK;
8029 }
8030 /* Perform the BASE64 encoding */
8031 SyBase64Encode(zIn, (sxu32)nLen, Consumer, pCtx);
8032 return JX9_OK;
8033}
8034/*
8035 * string base64_decode(string $data)
8036 * string convert_uudecode(string $data)
8037 * Decodes data encoded with MIME base64
8038 * Parameter
8039 * $data
8040 * Encoded data.
8041 * Return
8042 * Returns the original data or FALSE on failure.
8043 */
8044static int jx9Builtin_base64_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
8045{
8046 const char *zIn;
8047 int nLen;
8048 if( nArg < 1 ){
8049 /* Missing arguments, return FALSE */
8050 jx9_result_bool(pCtx, 0);
8051 return JX9_OK;
8052 }
8053 /* Extract the input string */
8054 zIn = jx9_value_to_string(apArg[0], &nLen);
8055 if( nLen < 1 ){
8056 /* Nothing to process, return FALSE */
8057 jx9_result_bool(pCtx, 0);
8058 return JX9_OK;
8059 }
8060 /* Perform the BASE64 decoding */
8061 SyBase64Decode(zIn, (sxu32)nLen, Consumer, pCtx);
8062 return JX9_OK;
8063}
8064/*
8065 * string urlencode(string $str)
8066 * URL encoding
8067 * Parameter
8068 * $data
8069 * Input string.
8070 * Return
8071 * Returns a string in which all non-alphanumeric characters except -_. have
8072 * been replaced with a percent (%) sign followed by two hex digits and spaces
8073 * encoded as plus (+) signs.
8074 */
8075static int jx9Builtin_urlencode(jx9_context *pCtx, int nArg, jx9_value **apArg)
8076{
8077 const char *zIn;
8078 int nLen;
8079 if( nArg < 1 ){
8080 /* Missing arguments, return FALSE */
8081 jx9_result_bool(pCtx, 0);
8082 return JX9_OK;
8083 }
8084 /* Extract the input string */
8085 zIn = jx9_value_to_string(apArg[0], &nLen);
8086 if( nLen < 1 ){
8087 /* Nothing to process, return FALSE */
8088 jx9_result_bool(pCtx, 0);
8089 return JX9_OK;
8090 }
8091 /* Perform the URL encoding */
8092 SyUriEncode(zIn, (sxu32)nLen, Consumer, pCtx);
8093 return JX9_OK;
8094}
8095/*
8096 * string urldecode(string $str)
8097 * Decodes any %## encoding in the given string.
8098 * Plus symbols ('+') are decoded to a space character.
8099 * Parameter
8100 * $data
8101 * Input string.
8102 * Return
8103 * Decoded URL or FALSE on failure.
8104 */
8105static int jx9Builtin_urldecode(jx9_context *pCtx, int nArg, jx9_value **apArg)
8106{
8107 const char *zIn;
8108 int nLen;
8109 if( nArg < 1 ){
8110 /* Missing arguments, return FALSE */
8111 jx9_result_bool(pCtx, 0);
8112 return JX9_OK;
8113 }
8114 /* Extract the input string */
8115 zIn = jx9_value_to_string(apArg[0], &nLen);
8116 if( nLen < 1 ){
8117 /* Nothing to process, return FALSE */
8118 jx9_result_bool(pCtx, 0);
8119 return JX9_OK;
8120 }
8121 /* Perform the URL decoding */
8122 SyUriDecode(zIn, (sxu32)nLen, Consumer, pCtx, TRUE);
8123 return JX9_OK;
8124}
8125#endif /* JX9_DISABLE_BUILTIN_FUNC */
8126/* Table of the built-in functions */
8127static const jx9_builtin_func aBuiltInFunc[] = {
8128 /* Variable handling functions */
8129 { "is_bool" , jx9Builtin_is_bool },
8130 { "is_float" , jx9Builtin_is_float },
8131 { "is_real" , jx9Builtin_is_float },
8132 { "is_double" , jx9Builtin_is_float },
8133 { "is_int" , jx9Builtin_is_int },
8134 { "is_integer" , jx9Builtin_is_int },
8135 { "is_long" , jx9Builtin_is_int },
8136 { "is_string" , jx9Builtin_is_string },
8137 { "is_null" , jx9Builtin_is_null },
8138 { "is_numeric" , jx9Builtin_is_numeric },
8139 { "is_scalar" , jx9Builtin_is_scalar },
8140 { "is_array" , jx9Builtin_is_array },
8141 { "is_object" , jx9Builtin_is_object },
8142 { "is_resource", jx9Builtin_is_resource },
8143 { "douleval" , jx9Builtin_floatval },
8144 { "floatval" , jx9Builtin_floatval },
8145 { "intval" , jx9Builtin_intval },
8146 { "strval" , jx9Builtin_strval },
8147 { "empty" , jx9Builtin_empty },
8148#ifndef JX9_DISABLE_BUILTIN_FUNC
8149#ifdef JX9_ENABLE_MATH_FUNC
8150 /* Math functions */
8151 { "abs" , jx9Builtin_abs },
8152 { "sqrt" , jx9Builtin_sqrt },
8153 { "exp" , jx9Builtin_exp },
8154 { "floor", jx9Builtin_floor },
8155 { "cos" , jx9Builtin_cos },
8156 { "sin" , jx9Builtin_sin },
8157 { "acos" , jx9Builtin_acos },
8158 { "asin" , jx9Builtin_asin },
8159 { "cosh" , jx9Builtin_cosh },
8160 { "sinh" , jx9Builtin_sinh },
8161 { "ceil" , jx9Builtin_ceil },
8162 { "tan" , jx9Builtin_tan },
8163 { "tanh" , jx9Builtin_tanh },
8164 { "atan" , jx9Builtin_atan },
8165 { "atan2", jx9Builtin_atan2 },
8166 { "log" , jx9Builtin_log },
8167 { "log10" , jx9Builtin_log10 },
8168 { "pow" , jx9Builtin_pow },
8169 { "pi", jx9Builtin_pi },
8170 { "fmod", jx9Builtin_fmod },
8171 { "hypot", jx9Builtin_hypot },
8172#endif /* JX9_ENABLE_MATH_FUNC */
8173 { "round", jx9Builtin_round },
8174 { "dechex", jx9Builtin_dechex },
8175 { "decoct", jx9Builtin_decoct },
8176 { "decbin", jx9Builtin_decbin },
8177 { "hexdec", jx9Builtin_hexdec },
8178 { "bindec", jx9Builtin_bindec },
8179 { "octdec", jx9Builtin_octdec },
8180 { "base_convert", jx9Builtin_base_convert },
8181 /* String handling functions */
8182 { "substr", jx9Builtin_substr },
8183 { "substr_compare", jx9Builtin_substr_compare },
8184 { "substr_count", jx9Builtin_substr_count },
8185 { "chunk_split", jx9Builtin_chunk_split},
8186 { "htmlspecialchars", jx9Builtin_htmlspecialchars },
8187 { "htmlspecialchars_decode", jx9Builtin_htmlspecialchars_decode },
8188 { "get_html_translation_table", jx9Builtin_get_html_translation_table },
8189 { "htmlentities", jx9Builtin_htmlentities},
8190 { "html_entity_decode", jx9Builtin_html_entity_decode},
8191 { "strlen" , jx9Builtin_strlen },
8192 { "strcmp" , jx9Builtin_strcmp },
8193 { "strcoll" , jx9Builtin_strcmp },
8194 { "strncmp" , jx9Builtin_strncmp },
8195 { "strcasecmp" , jx9Builtin_strcasecmp },
8196 { "strncasecmp", jx9Builtin_strncasecmp},
8197 { "implode" , jx9Builtin_implode },
8198 { "join" , jx9Builtin_implode },
8199 { "implode_recursive" , jx9Builtin_implode_recursive },
8200 { "join_recursive" , jx9Builtin_implode_recursive },
8201 { "explode" , jx9Builtin_explode },
8202 { "trim" , jx9Builtin_trim },
8203 { "rtrim" , jx9Builtin_rtrim },
8204 { "chop" , jx9Builtin_rtrim },
8205 { "ltrim" , jx9Builtin_ltrim },
8206 { "strtolower", jx9Builtin_strtolower },
8207 { "mb_strtolower", jx9Builtin_strtolower }, /* Only UTF-8 encoding is supported */
8208 { "strtoupper", jx9Builtin_strtoupper },
8209 { "mb_strtoupper", jx9Builtin_strtoupper }, /* Only UTF-8 encoding is supported */
8210 { "ord", jx9Builtin_ord },
8211 { "chr", jx9Builtin_chr },
8212 { "bin2hex", jx9Builtin_bin2hex },
8213 { "strstr", jx9Builtin_strstr },
8214 { "stristr", jx9Builtin_stristr },
8215 { "strchr", jx9Builtin_strstr },
8216 { "strpos", jx9Builtin_strpos },
8217 { "stripos", jx9Builtin_stripos },
8218 { "strrpos", jx9Builtin_strrpos },
8219 { "strripos", jx9Builtin_strripos },
8220 { "strrchr", jx9Builtin_strrchr },
8221 { "strrev", jx9Builtin_strrev },
8222 { "str_repeat", jx9Builtin_str_repeat },
8223 { "nl2br", jx9Builtin_nl2br },
8224 { "sprintf", jx9Builtin_sprintf },
8225 { "printf", jx9Builtin_printf },
8226 { "vprintf", jx9Builtin_vprintf },
8227 { "vsprintf", jx9Builtin_vsprintf },
8228 { "size_format", jx9Builtin_size_format},
8229#if !defined(JX9_DISABLE_HASH_FUNC)
8230 { "md5", jx9Builtin_md5 },
8231 { "sha1", jx9Builtin_sha1 },
8232 { "crc32", jx9Builtin_crc32 },
8233#endif /* JX9_DISABLE_HASH_FUNC */
8234 { "str_getcsv", jx9Builtin_str_getcsv },
8235 { "strip_tags", jx9Builtin_strip_tags },
8236 { "str_split", jx9Builtin_str_split },
8237 { "strspn", jx9Builtin_strspn },
8238 { "strcspn", jx9Builtin_strcspn },
8239 { "strpbrk", jx9Builtin_strpbrk },
8240 { "soundex", jx9Builtin_soundex },
8241 { "wordwrap", jx9Builtin_wordwrap },
8242 { "strtok", jx9Builtin_strtok },
8243 { "str_pad", jx9Builtin_str_pad },
8244 { "str_replace", jx9Builtin_str_replace},
8245 { "str_ireplace", jx9Builtin_str_replace},
8246 { "strtr", jx9Builtin_strtr },
8247 { "parse_ini_string", jx9Builtin_parse_ini_string},
8248 /* Ctype functions */
8249 { "ctype_alnum", jx9Builtin_ctype_alnum },
8250 { "ctype_alpha", jx9Builtin_ctype_alpha },
8251 { "ctype_cntrl", jx9Builtin_ctype_cntrl },
8252 { "ctype_digit", jx9Builtin_ctype_digit },
8253 { "ctype_xdigit", jx9Builtin_ctype_xdigit},
8254 { "ctype_graph", jx9Builtin_ctype_graph },
8255 { "ctype_print", jx9Builtin_ctype_print },
8256 { "ctype_punct", jx9Builtin_ctype_punct },
8257 { "ctype_space", jx9Builtin_ctype_space },
8258 { "ctype_lower", jx9Builtin_ctype_lower },
8259 { "ctype_upper", jx9Builtin_ctype_upper },
8260 /* Time functions */
8261 { "time" , jx9Builtin_time },
8262 { "microtime", jx9Builtin_microtime },
8263 { "getdate" , jx9Builtin_getdate },
8264 { "gettimeofday", jx9Builtin_gettimeofday },
8265 { "date", jx9Builtin_date },
8266 { "strftime", jx9Builtin_strftime },
8267 { "idate", jx9Builtin_idate },
8268 { "gmdate", jx9Builtin_gmdate },
8269 { "localtime", jx9Builtin_localtime },
8270 { "mktime", jx9Builtin_mktime },
8271 { "gmmktime", jx9Builtin_mktime },
8272 /* URL functions */
8273 { "base64_encode", jx9Builtin_base64_encode },
8274 { "base64_decode", jx9Builtin_base64_decode },
8275 { "convert_uuencode", jx9Builtin_base64_encode },
8276 { "convert_uudecode", jx9Builtin_base64_decode },
8277 { "urlencode", jx9Builtin_urlencode },
8278 { "urldecode", jx9Builtin_urldecode },
8279 { "rawurlencode", jx9Builtin_urlencode },
8280 { "rawurldecode", jx9Builtin_urldecode },
8281#endif /* JX9_DISABLE_BUILTIN_FUNC */
8282};
8283/*
8284 * Register the built-in functions defined above, the array functions
8285 * defined in hashmap.c and the IO functions defined in vfs.c.
8286 */
8287JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm)
8288{
8289 sxu32 n;
8290 for( n = 0 ; n < SX_ARRAYSIZE(aBuiltInFunc) ; ++n ){
8291 jx9_create_function(&(*pVm), aBuiltInFunc[n].zName, aBuiltInFunc[n].xFunc, 0);
8292 }
8293 /* Register hashmap functions [i.e: sort(), count(), array_diff(), ...] */
8294 jx9RegisterHashmapFunctions(&(*pVm));
8295 /* Register IO functions [i.e: fread(), fwrite(), chdir(), mkdir(), file(), ...] */
8296 jx9RegisterIORoutine(&(*pVm));
8297}