r24958: This is the final text, and the final version. I'll send the release
[jelmer/samba4-debian.git] / source / scripting / ejs / literal.c
1 /*
2  * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd.
3  * Michael Clark <michael@metaparadigm.com>
4  * Copyright (c) 2006 Derrell Lipman
5  *
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.
8  *
9  * Derrell Lipman:
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.
13  */
14
15 #include "includes.h"
16 #include "scripting/ejs/smbcalls.h"
17
18 enum json_tokener_error {
19         json_tokener_success,
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
31 };
32
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
58 };
59
60 enum date_field {
61         date_field_year,
62         date_field_month,
63         date_field_day,
64         date_field_hour,
65         date_field_minute,
66         date_field_second,
67         date_field_millisecond
68 };
69
70 struct json_tokener
71 {
72         char *source;
73         int pos;
74         void *ctx;
75         void *pb;
76 };
77
78 static const char *json_number_chars = "0123456789.+-e";
79 static const char *json_hex_chars = "0123456789abcdef";
80
81 #define hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
82
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);
86
87 /*
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.
93  */
94 int literal_to_var(int eid, int argc, char **argv)
95 {
96         struct json_tokener tok;
97         struct MprVar obj;
98         enum json_tokener_error err = json_tokener_success;
99         
100         if (argc != 1) {
101                 ejsSetErrorMsg(eid,
102                                "literal_to_var() requires one parameter: "
103                                "the string to be parsed.");
104                 return -1;
105         }
106
107         tok.source = argv[0];
108         tok.pos = 0;
109         tok.ctx = talloc_new(mprMemCtx());
110         if (tok.ctx == NULL) {
111                 mpr_Return(eid, mprCreateUndefinedVar());
112                 return 0;
113         }
114         tok.pb = talloc_zero_size(tok.ctx, 1);
115         if (tok.pb == NULL) {
116                 mpr_Return(eid, mprCreateUndefinedVar());
117                 return 0;
118         }
119         obj = json_tokener_do_parse(&tok, &err);
120         talloc_free(tok.pb);
121         if (err != json_tokener_success) {
122                 mprDestroyVar(&obj);
123                 mpr_Return(eid, mprCreateUndefinedVar());
124                 return 0;
125         }
126         mpr_Return(eid, obj);
127         return 0;
128 }
129
130 static void *append_string(void *ctx,
131                            char *orig,
132                            char *append,
133                            int size)
134 {
135     char c;
136     char *end_p = append + size;
137     void *ret;
138
139     /*
140      * We need to null terminate the string to be copied.  Save character at
141      * the size limit of the source string.
142      */
143     c = *end_p;
144
145     /* Temporarily null-terminate it */
146     *end_p = '\0';
147
148     /* Append the requested data */
149     ret = talloc_append_string(ctx, orig, append);
150     
151     /* Restore the original character in place of our temporary null byte */
152     *end_p = c;
153
154     /* Give 'em what they came for */
155     return ret;
156 }
157
158
159 static struct MprVar json_tokener_do_parse(struct json_tokener *this,
160                                            enum json_tokener_error *err_p)
161 {
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;
167         struct MprVar obj;
168         enum json_tokener_error err = json_tokener_success;
169         char date_script[] = "JSON_Date.create(0);";
170         char *obj_field_name = NULL;
171         char *emsg = NULL;
172         char quote_char;
173         int deemed_double;
174         int start_offset;
175         char c;
176         
177         state = json_tokener_state_eatws;
178         saved_state = json_tokener_state_start;
179
180         
181         do {
182                 c = this->source[this->pos];
183                 switch(state) {
184                         
185                 case json_tokener_state_eatws:
186                         if(isspace(c)) {
187                                 this->pos++;
188                         } else if(c == '/') {
189                                 state = json_tokener_state_comment_start;
190                                 start_offset = this->pos++;
191                         } else {
192                                 state = saved_state;
193                         }
194                         break;
195                         
196                 case json_tokener_state_start:
197                         switch(c) {
198                         case '{':
199                                 state = json_tokener_state_eatws;
200                                 saved_state = json_tokener_state_object;
201                                 current = mprObject(NULL);
202                                 this->pos++;
203                                 break;
204                         case '[':
205                                 state = json_tokener_state_eatws;
206                                 saved_state = json_tokener_state_array;
207                                 current = mprArray(NULL);
208                                 this->pos++;
209                                 break;
210                         case 'N':
211                         case 'n':
212                                 start_offset = this->pos++;
213                                 if (this->source[this->pos] == 'e') {
214                                     state = json_tokener_state_date;
215                                 } else {
216                                     state = json_tokener_state_null;
217                                 }
218                                 break;
219                         case '"':
220                         case '\'':
221                                 quote_char = c;
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;
226                                         goto out;
227                                 }
228                                 state = json_tokener_state_string;
229                                 start_offset = ++this->pos;
230                                 break;
231                         case 'T':
232                         case 't':
233                         case 'F':
234                         case 'f':
235                                 state = json_tokener_state_boolean;
236                                 start_offset = this->pos++;
237                                 break;
238 #if defined(__GNUC__)
239                         case '0' ... '9':
240 #else
241                         case '0':
242                         case '1':
243                         case '2':
244                         case '3':
245                         case '4':
246                         case '5':
247                         case '6':
248                         case '7':
249                         case '8':
250                         case '9':
251 #endif
252                         case '-':
253                                 deemed_double = 0;
254                                 state = json_tokener_state_number;
255                                 start_offset = this->pos++;
256                                 break;
257                         default:
258                                 err = json_tokener_error_parse_unexpected;
259                                 goto out;
260                         }
261                         break;
262                         
263                 case json_tokener_state_finish:
264                         goto out;
265                         
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(&current);
272                                 return mprCreateUndefinedVar();
273                         }
274                         
275                         if(this->pos - start_offset == 4) {
276                                 mprDestroyVar(&current);
277                                 current = mprCreateNullVar();
278                                 saved_state = json_tokener_state_finish;
279                                 state = json_tokener_state_eatws;
280                         } else {
281                                 this->pos++;
282                         }
283                         break;
284                         
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(&current);
292                                         return mprCreateUndefinedVar();
293                                 } else {
294                                         this->pos++;
295                                         break;
296                                 }
297                         }
298                         
299                         this->pos--;            /* we went one too far */
300                         state = json_tokener_state_eatws;
301                         saved_state = json_tokener_state_datelist;
302
303                         /* Create a JsonDate object */
304                         if (ejsEvalScript(0,
305                                           date_script,
306                                           &tempObj,
307                                           &emsg) != 0) {
308                                 *err_p = json_tokener_error_parse_date;
309                                 mprDestroyVar(&current);
310                                 return mprCreateUndefinedVar();
311                         }
312                         mprDestroyVar(&current);
313                         mprCopyVar(&current, &tempObj, MPR_DEEP_COPY);
314                         date_field = date_field_year;
315                         break;
316                         
317                 case json_tokener_state_comment_start:
318                         if(c == '*') {
319                                 state = json_tokener_state_comment;
320                         } else if(c == '/') {
321                                 state = json_tokener_state_comment_eol;
322                         } else {
323                                 err = json_tokener_error_parse_comment;
324                                 goto out;
325                         }
326                         this->pos++;
327                         break;
328                         
329                 case json_tokener_state_comment:
330                         if(c == '*') state = json_tokener_state_comment_end;
331                         this->pos++;
332                         break;
333                         
334                 case json_tokener_state_comment_eol:
335                         if(c == '\n') {
336                                 state = json_tokener_state_eatws;
337                         }
338                         this->pos++;
339                         break;
340                         
341                 case json_tokener_state_comment_end:
342                         if(c == '/') {
343                                 state = json_tokener_state_eatws;
344                         } else {
345                                 state = json_tokener_state_comment;
346                         }
347                         this->pos++;
348                         break;
349                         
350                 case json_tokener_state_string:
351                         if(c == quote_char) {
352                                 this->pb = append_string(
353                                         this->ctx,
354                                         this->pb,
355                                         this->source + start_offset,
356                                         this->pos - start_offset);
357                                 if (this->pb == NULL) {
358                                         err = json_tokener_error_oom;
359                                         goto out;
360                                 }
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;
367                         }
368                         this->pos++;
369                         break;
370                         
371                 case json_tokener_state_string_escape:
372                         switch(c) {
373                         case '"':
374                         case '\\':
375                                 this->pb = append_string(
376                                         this->ctx,
377                                         this->pb,
378                                         this->source + start_offset,
379                                         this->pos - start_offset - 1);
380                                 if (this->pb == NULL) {
381                                         err = json_tokener_error_oom;
382                                         goto out;
383                                 }
384                                 start_offset = this->pos++;
385                                 state = saved_state;
386                                 break;
387                         case 'b':
388                         case 'n':
389                         case 'r':
390                         case 't':
391                                 this->pb = append_string(
392                                         this->ctx,
393                                         this->pb,
394                                         this->source + start_offset,
395                                         this->pos - start_offset - 1);
396                                 if (this->pb == NULL) {
397                                         err = json_tokener_error_oom;
398                                         goto out;
399                                 }
400                                 if (c == 'b') {
401                                         /*
402                                          * second param to append_string()
403                                          * gets temporarily modified; can't
404                                          * pass string constant.
405                                          */
406                                         char buf[] = "\b";
407                                         this->pb = append_string(this->ctx,
408                                                                  this->pb,
409                                                                  buf,
410                                                                  1);
411                                         if (this->pb == NULL) {
412                                                 err = json_tokener_error_oom;
413                                                 goto out;
414                                         }
415                                 } else if (c == 'n') {
416                                         char buf[] = "\n";
417                                         this->pb = append_string(this->ctx,
418                                                                  this->pb,
419                                                                  buf,
420                                                                  1);
421                                         if (this->pb == NULL) {
422                                                 err = json_tokener_error_oom;
423                                                 goto out;
424                                         }
425                                 } else if (c == 'r') {
426                                         char buf[] = "\r";
427                                         this->pb = append_string(this->ctx,
428                                                                  this->pb,
429                                                                  buf,
430                                                                  1);
431                                         if (this->pb == NULL) {
432                                                 err = json_tokener_error_oom;
433                                                 goto out;
434                                         }
435                                 } else if (c == 't') {
436                                         char buf[] = "\t";
437                                         this->pb = append_string(this->ctx,
438                                                                  this->pb,
439                                                                  buf,
440                                                                  1);
441                                         if (this->pb == NULL) {
442                                                 err = json_tokener_error_oom;
443                                                 goto out;
444                                         }
445                                 }
446                                 start_offset = ++this->pos;
447                                 state = saved_state;
448                                 break;
449                         case 'u':
450                                 this->pb = append_string(
451                                         this->ctx,
452                                         this->pb,
453                                         this->source + start_offset,
454                                         this->pos - start_offset - 1);
455                                 if (this->pb == NULL) {
456                                         err = json_tokener_error_oom;
457                                         goto out;
458                                 }
459                                 start_offset = ++this->pos;
460                                 state = json_tokener_state_escape_unicode;
461                                 break;
462                         default:
463                                 err = json_tokener_error_parse_string;
464                                 goto out;
465                         }
466                         break;
467                         
468                 case json_tokener_state_escape_unicode:
469                         if(strchr(json_hex_chars, c)) {
470                                 this->pos++;
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(
481                                                         this->ctx,
482                                                         this->pb,
483                                                         (char *) utf_out,
484                                                         1);
485                                                 if (this->pb == NULL) {
486                                                         err = json_tokener_error_oom;
487                                                         goto out;
488                                                 }
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(
493                                                         this->ctx,
494                                                         this->pb,
495                                                         (char *) utf_out,
496                                                         2);
497                                                 if (this->pb == NULL) {
498                                                         err = json_tokener_error_oom;
499                                                         goto out;
500                                                 }
501                                         } else {
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(
506                                                         this->ctx,
507                                                         this->pb,
508                                                         (char *) utf_out,
509                                                         3);
510                                                 if (this->pb == NULL) {
511                                                         err = json_tokener_error_oom;
512                                                         goto out;
513                                                 }
514                                         }
515                                         start_offset = this->pos;
516                                         state = saved_state;
517                                 }
518                         } else {
519                                 err = json_tokener_error_parse_string;
520                                 goto out;
521                         }
522                         break;
523                         
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;
531                                 } else {
532                                         this->pos++;
533                                 }
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;
540                                 } else {
541                                         this->pos++;
542                                 }
543                         } else {
544                                 err = json_tokener_error_parse_boolean;
545                                 goto out;
546                         }
547                         break;
548                         
549                 case json_tokener_state_number:
550                         if(!c || !strchr(json_number_chars, c)) {
551                                 int numi;
552                                 double numd;
553                                 char *tmp = talloc_strndup(
554                                         this->ctx,
555                                         this->source + start_offset,
556                                         this->pos - start_offset);
557                                 if (tmp == NULL) {
558                                         err = json_tokener_error_oom;
559                                         goto out;
560                                 }
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);
565                                 } else {
566                                         talloc_free(tmp);
567                                         err = json_tokener_error_parse_number;
568                                         goto out;
569                                 }
570                                 talloc_free(tmp);
571                                 saved_state = json_tokener_state_finish;
572                                 state = json_tokener_state_eatws;
573                         } else {
574                                 if(c == '.' || c == 'e') deemed_double = 1;
575                                 this->pos++;
576                         }
577                         break;
578                         
579                 case json_tokener_state_array:
580                         if(c == ']') {
581                                 this->pos++;
582                                 saved_state = json_tokener_state_finish;
583                                 state = json_tokener_state_eatws;
584                         } else {
585                                 int oldlen;
586                                 char idx[16];
587                                 
588                                 obj = json_tokener_do_parse(this, &err);
589                                 if (err != json_tokener_success) {
590                                         goto out;
591                                 }
592                                 oldlen = mprToInt(mprGetProperty(&current,
593                                                                  "length",
594                                                                  NULL));
595                                 mprItoa(oldlen, idx, sizeof(idx));
596                                 mprSetVar(&current, idx, obj);
597                                 saved_state = json_tokener_state_array_sep;
598                                 state = json_tokener_state_eatws;
599                         }
600                         break;
601                         
602                 case json_tokener_state_datelist:
603                         if(c == ')') {
604                                 if (this->source[this->pos+1] == ')') {
605                                         this->pos += 2;
606                                         saved_state = json_tokener_state_finish;
607                                         state = json_tokener_state_eatws;
608                                 } else {
609                                         err = json_tokener_error_parse_date;
610                                         goto out;
611                                 }
612                         } else {
613                                 obj = json_tokener_do_parse(this, &err);
614                                 if (err != json_tokener_success) {
615                                         goto out;
616                                 }
617
618                                 /* date list items must be integers */
619                                 if (obj.type != MPR_TYPE_INT) {
620                                         err = json_tokener_error_parse_date;
621                                         goto out;
622                                 }
623                                 
624                                 switch(date_field) {
625                                 case date_field_year:
626                                         mprSetVar(&current, "year", obj);
627                                         break;
628                                 case date_field_month:
629                                         mprSetVar(&current, "month", obj);
630                                         break;
631                                 case date_field_day:
632                                         mprSetVar(&current, "day", obj);
633                                         break;
634                                 case date_field_hour:
635                                         mprSetVar(&current, "hour", obj);
636                                         break;
637                                 case date_field_minute:
638                                         mprSetVar(&current, "minute", obj);
639                                         break;
640                                 case date_field_second:
641                                         mprSetVar(&current, "second", obj);
642                                         break;
643                                 case date_field_millisecond:
644                                         mprSetVar(&current, "millisecond", obj);
645                                         break;
646                                 default:
647                                         err = json_tokener_error_parse_date;
648                                         goto out;
649                                 }
650
651                                 /* advance to the next date field */
652                                 date_field++;
653
654                                 saved_state = json_tokener_state_datelist_sep;
655                                 state = json_tokener_state_eatws;
656                         }
657                         break;
658                         
659                 case json_tokener_state_array_sep:
660                         if(c == ']') {
661                                 this->pos++;
662                                 saved_state = json_tokener_state_finish;
663                                 state = json_tokener_state_eatws;
664                         } else if(c == ',') {
665                                 this->pos++;
666                                 saved_state = json_tokener_state_array;
667                                 state = json_tokener_state_eatws;
668                         } else {
669                                 *err_p = json_tokener_error_parse_array;
670                                 mprDestroyVar(&current);
671                                 return mprCreateUndefinedVar();
672                         }
673                         break;
674                         
675                 case json_tokener_state_datelist_sep:
676                         if(c == ')') {
677                                 if (this->source[this->pos+1] == ')') {
678                                         this->pos += 2;
679                                         saved_state = json_tokener_state_finish;
680                                         state = json_tokener_state_eatws;
681                                 } else {
682                                         err = json_tokener_error_parse_date;
683                                         goto out;
684                                 }
685                         } else if(c == ',') {
686                                 this->pos++;
687                                 saved_state = json_tokener_state_datelist;
688                                 state = json_tokener_state_eatws;
689                         } else {
690                                 *err_p = json_tokener_error_parse_date;
691                                 mprDestroyVar(&current);
692                                 return mprCreateUndefinedVar();
693                         }
694                         break;
695                         
696                 case json_tokener_state_object:
697                         state = json_tokener_state_object_field_start;
698                         start_offset = this->pos;
699                         break;
700                         
701                 case json_tokener_state_object_field_start:
702                         if(c == '}') {
703                                 this->pos++;
704                                 saved_state = json_tokener_state_finish;
705                                 state = json_tokener_state_eatws;
706                         } else if (c == '"' || c == '\'') {
707                                 quote_char = 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;
712                                         goto out;
713                                 }
714                                 state = json_tokener_state_object_field;
715                                 start_offset = ++this->pos;
716                         } else {
717                                 err = json_tokener_error_parse_object;
718                                 goto out;
719                         }
720                         break;
721                         
722                 case json_tokener_state_object_field:
723                         if(c == quote_char) {
724                                 this->pb = append_string(
725                                         this->ctx,
726                                         this->pb,
727                                         this->source + start_offset,
728                                         this->pos - start_offset);
729                                 if (this->pb == NULL) {
730                                         err = json_tokener_error_oom;
731                                         goto out;
732                                 }
733                                 obj_field_name = talloc_strdup(this->ctx,
734                                                                this->pb);
735                                 if (obj_field_name == NULL) {
736                                         err = json_tokener_error_oom;
737                                         goto out;
738                                 }
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;
744                         }
745                         this->pos++;
746                         break;
747                         
748                 case json_tokener_state_object_field_end:
749                         if(c == ':') {
750                                 this->pos++;
751                                 saved_state = json_tokener_state_object_value;
752                                 state = json_tokener_state_eatws;
753                         } else {
754                                 *err_p = json_tokener_error_parse_object;
755                                 mprDestroyVar(&current);
756                                 return mprCreateUndefinedVar();
757                         }
758                         break;
759                         
760                 case json_tokener_state_object_value:
761                         obj = json_tokener_do_parse(this, &err);
762                         if (err != json_tokener_success) {
763                                 goto out;
764                         }
765                         mprSetVar(&current, 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;
770                         break;
771                         
772                 case json_tokener_state_object_sep:
773                         if(c == '}') {
774                                 this->pos++;
775                                 saved_state = json_tokener_state_finish;
776                                 state = json_tokener_state_eatws;
777                         } else if(c == ',') {
778                                 this->pos++;
779                                 saved_state = json_tokener_state_object;
780                                 state = json_tokener_state_eatws;
781                         } else {
782                                 err = json_tokener_error_parse_object;
783                                 goto out;
784                         }
785                         break;
786                         
787                 }
788         } while(c);
789         
790         if(state != json_tokener_state_finish &&
791            saved_state != json_tokener_state_finish)
792                 err = json_tokener_error_parse_eof;
793         
794 out:
795         talloc_free(obj_field_name);
796         if(err == json_tokener_success) {
797                 return current;
798         } else {
799                 mprDestroyVar(&current);
800                 *err_p = err;
801                 return mprCreateUndefinedVar();
802         }
803 }
804
805
806 void smb_setup_ejs_literal(void)
807 {
808         ejsDefineStringCFunction(-1,
809                                  "literal_to_var",
810                                  literal_to_var,
811                                  NULL,
812                                  MPR_VAR_SCRIPT_HANDLE);
813 }