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);