2 * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd.
3 * Michael Clark <michael@metaparadigm.com>
4 * Copyright (c) 2006 Derrell Lipman
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the MIT license. See COPYING for details.
10 * This version is modified from the original. It has been modified to
11 * natively use EJS variables rather than the original C object interface, and
12 * to use the talloc() family of functions for memory allocation.
16 #include "scripting/ejs/smbcalls.h"
18 enum json_tokener_error
{
20 json_tokener_error_oom
, /* out of memory */
21 json_tokener_error_parse_unexpected
,
22 json_tokener_error_parse_null
,
23 json_tokener_error_parse_date
,
24 json_tokener_error_parse_boolean
,
25 json_tokener_error_parse_number
,
26 json_tokener_error_parse_array
,
27 json_tokener_error_parse_object
,
28 json_tokener_error_parse_string
,
29 json_tokener_error_parse_comment
,
30 json_tokener_error_parse_eof
33 enum json_tokener_state
{
34 json_tokener_state_eatws
,
35 json_tokener_state_start
,
36 json_tokener_state_finish
,
37 json_tokener_state_null
,
38 json_tokener_state_date
,
39 json_tokener_state_comment_start
,
40 json_tokener_state_comment
,
41 json_tokener_state_comment_eol
,
42 json_tokener_state_comment_end
,
43 json_tokener_state_string
,
44 json_tokener_state_string_escape
,
45 json_tokener_state_escape_unicode
,
46 json_tokener_state_boolean
,
47 json_tokener_state_number
,
48 json_tokener_state_array
,
49 json_tokener_state_datelist
,
50 json_tokener_state_array_sep
,
51 json_tokener_state_datelist_sep
,
52 json_tokener_state_object
,
53 json_tokener_state_object_field_start
,
54 json_tokener_state_object_field
,
55 json_tokener_state_object_field_end
,
56 json_tokener_state_object_value
,
57 json_tokener_state_object_sep
67 date_field_millisecond
78 static const char *json_number_chars
= "0123456789.+-e";
79 static const char *json_hex_chars
= "0123456789abcdef";
81 #define hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
83 extern struct MprVar
json_tokener_parse(char *s
);
84 static struct MprVar
json_tokener_do_parse(struct json_tokener
*this,
85 enum json_tokener_error
*err_p
);
88 * literal_to_var() parses a string into an ejs variable. The ejs
89 * variable is returned. Upon error, the javascript variable will be
90 * `undefined`. This was created for parsing JSON, but is generally useful
91 * for parsing the literal forms of objects and arrays, since ejs doesn't
92 * procide that functionality.
94 int literal_to_var(int eid
, int argc
, char **argv
)
96 struct json_tokener tok
;
98 enum json_tokener_error err
= json_tokener_success
;
102 "literal_to_var() requires one parameter: "
103 "the string to be parsed.");
107 tok
.source
= argv
[0];
109 tok
.ctx
= talloc_new(mprMemCtx());
110 if (tok
.ctx
== NULL
) {
111 mpr_Return(eid
, mprCreateUndefinedVar());
114 tok
.pb
= talloc_zero_size(tok
.ctx
, 1);
115 if (tok
.pb
== NULL
) {
116 mpr_Return(eid
, mprCreateUndefinedVar());
119 obj
= json_tokener_do_parse(&tok
, &err
);
121 if (err
!= json_tokener_success
) {
123 mpr_Return(eid
, mprCreateUndefinedVar());
126 mpr_Return(eid
, obj
);
130 static void *append_string(void *ctx
,
136 char *end_p
= append
+ size
;
140 * We need to null terminate the string to be copied. Save character at
141 * the size limit of the source string.
145 /* Temporarily null-terminate it */
148 /* Append the requested data */
149 ret
= talloc_append_string(ctx
, orig
, append
);
151 /* Restore the original character in place of our temporary null byte */
154 /* Give 'em what they came for */
159 static struct MprVar
json_tokener_do_parse(struct json_tokener
*this,
160 enum json_tokener_error
*err_p
)
162 enum json_tokener_state state
;
163 enum json_tokener_state saved_state
;
164 enum date_field date_field
;
165 struct MprVar current
= mprCreateUndefinedVar();
166 struct MprVar tempObj
;
168 enum json_tokener_error err
= json_tokener_success
;
169 char date_script
[] = "JSON_Date.create(0);";
170 char *obj_field_name
= NULL
;
177 state
= json_tokener_state_eatws
;
178 saved_state
= json_tokener_state_start
;
182 c
= this->source
[this->pos
];
185 case json_tokener_state_eatws
:
188 } else if(c
== '/') {
189 state
= json_tokener_state_comment_start
;
190 start_offset
= this->pos
++;
196 case json_tokener_state_start
:
199 state
= json_tokener_state_eatws
;
200 saved_state
= json_tokener_state_object
;
201 current
= mprObject(NULL
);
205 state
= json_tokener_state_eatws
;
206 saved_state
= json_tokener_state_array
;
207 current
= mprArray(NULL
);
212 start_offset
= this->pos
++;
213 if (this->source
[this->pos
] == 'e') {
214 state
= json_tokener_state_date
;
216 state
= json_tokener_state_null
;
222 talloc_free(this->pb
);
223 this->pb
= talloc_zero_size(this->ctx
, 1);
224 if (this->pb
== NULL
) {
225 *err_p
= json_tokener_error_oom
;
228 state
= json_tokener_state_string
;
229 start_offset
= ++this->pos
;
235 state
= json_tokener_state_boolean
;
236 start_offset
= this->pos
++;
238 #if defined(__GNUC__)
254 state
= json_tokener_state_number
;
255 start_offset
= this->pos
++;
258 err
= json_tokener_error_parse_unexpected
;
263 case json_tokener_state_finish
:
266 case json_tokener_state_null
:
267 if(strncasecmp("null",
268 this->source
+ start_offset
,
269 this->pos
- start_offset
)) {
270 *err_p
= json_tokener_error_parse_null
;
271 mprDestroyVar(¤t
);
272 return mprCreateUndefinedVar();
275 if(this->pos
- start_offset
== 4) {
276 mprDestroyVar(¤t
);
277 current
= mprCreateNullVar();
278 saved_state
= json_tokener_state_finish
;
279 state
= json_tokener_state_eatws
;
285 case json_tokener_state_date
:
286 if (this->pos
- start_offset
<= 18) {
287 if (strncasecmp("new Date(Date.UTC(",
288 this->source
+ start_offset
,
289 this->pos
- start_offset
)) {
290 *err_p
= json_tokener_error_parse_date
;
291 mprDestroyVar(¤t
);
292 return mprCreateUndefinedVar();
299 this->pos
--; /* we went one too far */
300 state
= json_tokener_state_eatws
;
301 saved_state
= json_tokener_state_datelist
;
303 /* Create a JsonDate object */
308 *err_p
= json_tokener_error_parse_date
;
309 mprDestroyVar(¤t
);
310 return mprCreateUndefinedVar();
312 mprDestroyVar(¤t
);
313 mprCopyVar(¤t
, &tempObj
, MPR_DEEP_COPY
);
314 date_field
= date_field_year
;
317 case json_tokener_state_comment_start
:
319 state
= json_tokener_state_comment
;
320 } else if(c
== '/') {
321 state
= json_tokener_state_comment_eol
;
323 err
= json_tokener_error_parse_comment
;
329 case json_tokener_state_comment
:
330 if(c
== '*') state
= json_tokener_state_comment_end
;
334 case json_tokener_state_comment_eol
:
336 state
= json_tokener_state_eatws
;
341 case json_tokener_state_comment_end
:
343 state
= json_tokener_state_eatws
;
345 state
= json_tokener_state_comment
;
350 case json_tokener_state_string
:
351 if(c
== quote_char
) {
352 this->pb
= append_string(
355 this->source
+ start_offset
,
356 this->pos
- start_offset
);
357 if (this->pb
== NULL
) {
358 err
= json_tokener_error_oom
;
361 current
= mprString(this->pb
);
362 saved_state
= json_tokener_state_finish
;
363 state
= json_tokener_state_eatws
;
364 } else if(c
== '\\') {
365 saved_state
= json_tokener_state_string
;
366 state
= json_tokener_state_string_escape
;
371 case json_tokener_state_string_escape
:
375 this->pb
= append_string(
378 this->source
+ start_offset
,
379 this->pos
- start_offset
- 1);
380 if (this->pb
== NULL
) {
381 err
= json_tokener_error_oom
;
384 start_offset
= this->pos
++;
391 this->pb
= append_string(
394 this->source
+ start_offset
,
395 this->pos
- start_offset
- 1);
396 if (this->pb
== NULL
) {
397 err
= json_tokener_error_oom
;
402 * second param to append_string()
403 * gets temporarily modified; can't
404 * pass string constant.
407 this->pb
= append_string(this->ctx
,
411 if (this->pb
== NULL
) {
412 err
= json_tokener_error_oom
;
415 } else if (c
== 'n') {
417 this->pb
= append_string(this->ctx
,
421 if (this->pb
== NULL
) {
422 err
= json_tokener_error_oom
;
425 } else if (c
== 'r') {
427 this->pb
= append_string(this->ctx
,
431 if (this->pb
== NULL
) {
432 err
= json_tokener_error_oom
;
435 } else if (c
== 't') {
437 this->pb
= append_string(this->ctx
,
441 if (this->pb
== NULL
) {
442 err
= json_tokener_error_oom
;
446 start_offset
= ++this->pos
;
450 this->pb
= append_string(
453 this->source
+ start_offset
,
454 this->pos
- start_offset
- 1);
455 if (this->pb
== NULL
) {
456 err
= json_tokener_error_oom
;
459 start_offset
= ++this->pos
;
460 state
= json_tokener_state_escape_unicode
;
463 err
= json_tokener_error_parse_string
;
468 case json_tokener_state_escape_unicode
:
469 if(strchr(json_hex_chars
, c
)) {
471 if(this->pos
- start_offset
== 4) {
472 unsigned char utf_out
[3];
473 unsigned int ucs_char
=
474 (hexdigit(*(this->source
+ start_offset
)) << 12) +
475 (hexdigit(*(this->source
+ start_offset
+ 1)) << 8) +
476 (hexdigit(*(this->source
+ start_offset
+ 2)) << 4) +
477 hexdigit(*(this->source
+ start_offset
+ 3));
478 if (ucs_char
< 0x80) {
479 utf_out
[0] = ucs_char
;
480 this->pb
= append_string(
485 if (this->pb
== NULL
) {
486 err
= json_tokener_error_oom
;
489 } else if (ucs_char
< 0x800) {
490 utf_out
[0] = 0xc0 | (ucs_char
>> 6);
491 utf_out
[1] = 0x80 | (ucs_char
& 0x3f);
492 this->pb
= append_string(
497 if (this->pb
== NULL
) {
498 err
= json_tokener_error_oom
;
502 utf_out
[0] = 0xe0 | (ucs_char
>> 12);
503 utf_out
[1] = 0x80 | ((ucs_char
>> 6) & 0x3f);
504 utf_out
[2] = 0x80 | (ucs_char
& 0x3f);
505 this->pb
= append_string(
510 if (this->pb
== NULL
) {
511 err
= json_tokener_error_oom
;
515 start_offset
= this->pos
;
519 err
= json_tokener_error_parse_string
;
524 case json_tokener_state_boolean
:
525 if(strncasecmp("true", this->source
+ start_offset
,
526 this->pos
- start_offset
) == 0) {
527 if(this->pos
- start_offset
== 4) {
528 current
= mprCreateBoolVar(1);
529 saved_state
= json_tokener_state_finish
;
530 state
= json_tokener_state_eatws
;
534 } else if(strncasecmp("false", this->source
+ start_offset
,
535 this->pos
- start_offset
) == 0) {
536 if(this->pos
- start_offset
== 5) {
537 current
= mprCreateBoolVar(0);
538 saved_state
= json_tokener_state_finish
;
539 state
= json_tokener_state_eatws
;
544 err
= json_tokener_error_parse_boolean
;
549 case json_tokener_state_number
:
550 if(!c
|| !strchr(json_number_chars
, c
)) {
553 char *tmp
= talloc_strndup(
555 this->source
+ start_offset
,
556 this->pos
- start_offset
);
558 err
= json_tokener_error_oom
;
561 if(!deemed_double
&& sscanf(tmp
, "%d", &numi
) == 1) {
562 current
= mprCreateIntegerVar(numi
);
563 } else if(deemed_double
&& sscanf(tmp
, "%lf", &numd
) == 1) {
564 current
= mprCreateFloatVar(numd
);
567 err
= json_tokener_error_parse_number
;
571 saved_state
= json_tokener_state_finish
;
572 state
= json_tokener_state_eatws
;
574 if(c
== '.' || c
== 'e') deemed_double
= 1;
579 case json_tokener_state_array
:
582 saved_state
= json_tokener_state_finish
;
583 state
= json_tokener_state_eatws
;
588 obj
= json_tokener_do_parse(this, &err
);
589 if (err
!= json_tokener_success
) {
592 oldlen
= mprToInt(mprGetProperty(¤t
,
595 mprItoa(oldlen
, idx
, sizeof(idx
));
596 mprSetVar(¤t
, idx
, obj
);
597 saved_state
= json_tokener_state_array_sep
;
598 state
= json_tokener_state_eatws
;
602 case json_tokener_state_datelist
:
604 if (this->source
[this->pos
+1] == ')') {
606 saved_state
= json_tokener_state_finish
;
607 state
= json_tokener_state_eatws
;
609 err
= json_tokener_error_parse_date
;
613 obj
= json_tokener_do_parse(this, &err
);
614 if (err
!= json_tokener_success
) {
618 /* date list items must be integers */
619 if (obj
.type
!= MPR_TYPE_INT
) {
620 err
= json_tokener_error_parse_date
;
625 case date_field_year
:
626 mprSetVar(¤t
, "year", obj
);
628 case date_field_month
:
629 mprSetVar(¤t
, "month", obj
);
632 mprSetVar(¤t
, "day", obj
);
634 case date_field_hour
:
635 mprSetVar(¤t
, "hour", obj
);
637 case date_field_minute
:
638 mprSetVar(¤t
, "minute", obj
);
640 case date_field_second
:
641 mprSetVar(¤t
, "second", obj
);
643 case date_field_millisecond
:
644 mprSetVar(¤t
, "millisecond", obj
);
647 err
= json_tokener_error_parse_date
;
651 /* advance to the next date field */
654 saved_state
= json_tokener_state_datelist_sep
;
655 state
= json_tokener_state_eatws
;
659 case json_tokener_state_array_sep
:
662 saved_state
= json_tokener_state_finish
;
663 state
= json_tokener_state_eatws
;
664 } else if(c
== ',') {
666 saved_state
= json_tokener_state_array
;
667 state
= json_tokener_state_eatws
;
669 *err_p
= json_tokener_error_parse_array
;
670 mprDestroyVar(¤t
);
671 return mprCreateUndefinedVar();
675 case json_tokener_state_datelist_sep
:
677 if (this->source
[this->pos
+1] == ')') {
679 saved_state
= json_tokener_state_finish
;
680 state
= json_tokener_state_eatws
;
682 err
= json_tokener_error_parse_date
;
685 } else if(c
== ',') {
687 saved_state
= json_tokener_state_datelist
;
688 state
= json_tokener_state_eatws
;
690 *err_p
= json_tokener_error_parse_date
;
691 mprDestroyVar(¤t
);
692 return mprCreateUndefinedVar();
696 case json_tokener_state_object
:
697 state
= json_tokener_state_object_field_start
;
698 start_offset
= this->pos
;
701 case json_tokener_state_object_field_start
:
704 saved_state
= json_tokener_state_finish
;
705 state
= json_tokener_state_eatws
;
706 } else if (c
== '"' || c
== '\'') {
708 talloc_free(this->pb
);
709 this->pb
= talloc_zero_size(this->ctx
, 1);
710 if (this->pb
== NULL
) {
711 *err_p
= json_tokener_error_oom
;
714 state
= json_tokener_state_object_field
;
715 start_offset
= ++this->pos
;
717 err
= json_tokener_error_parse_object
;
722 case json_tokener_state_object_field
:
723 if(c
== quote_char
) {
724 this->pb
= append_string(
727 this->source
+ start_offset
,
728 this->pos
- start_offset
);
729 if (this->pb
== NULL
) {
730 err
= json_tokener_error_oom
;
733 obj_field_name
= talloc_strdup(this->ctx
,
735 if (obj_field_name
== NULL
) {
736 err
= json_tokener_error_oom
;
739 saved_state
= json_tokener_state_object_field_end
;
740 state
= json_tokener_state_eatws
;
741 } else if(c
== '\\') {
742 saved_state
= json_tokener_state_object_field
;
743 state
= json_tokener_state_string_escape
;
748 case json_tokener_state_object_field_end
:
751 saved_state
= json_tokener_state_object_value
;
752 state
= json_tokener_state_eatws
;
754 *err_p
= json_tokener_error_parse_object
;
755 mprDestroyVar(¤t
);
756 return mprCreateUndefinedVar();
760 case json_tokener_state_object_value
:
761 obj
= json_tokener_do_parse(this, &err
);
762 if (err
!= json_tokener_success
) {
765 mprSetVar(¤t
, obj_field_name
, obj
);
766 talloc_free(obj_field_name
);
767 obj_field_name
= NULL
;
768 saved_state
= json_tokener_state_object_sep
;
769 state
= json_tokener_state_eatws
;
772 case json_tokener_state_object_sep
:
775 saved_state
= json_tokener_state_finish
;
776 state
= json_tokener_state_eatws
;
777 } else if(c
== ',') {
779 saved_state
= json_tokener_state_object
;
780 state
= json_tokener_state_eatws
;
782 err
= json_tokener_error_parse_object
;
790 if(state
!= json_tokener_state_finish
&&
791 saved_state
!= json_tokener_state_finish
)
792 err
= json_tokener_error_parse_eof
;
795 talloc_free(obj_field_name
);
796 if(err
== json_tokener_success
) {
799 mprDestroyVar(¤t
);
801 return mprCreateUndefinedVar();
806 void smb_setup_ejs_literal(void)
808 ejsDefineStringCFunction(-1,
812 MPR_VAR_SCRIPT_HANDLE
);