1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2015 Joyent, Inc.
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <ctype.h>
19 #include <strings.h>
20 #include <errno.h>
21 #include <libnvpair.h>
22 #include <sys/ccompile.h>
23
24 #include "json_nvlist.h"
25
26 #ifndef __unused
27 #define __unused __sun_attr__((__unused__))
28 #endif
29
30 typedef enum json_type {
31 JSON_TYPE_NOTHING = 0,
32 JSON_TYPE_STRING = 1,
33 JSON_TYPE_INTEGER,
34 JSON_TYPE_DOUBLE,
35 JSON_TYPE_BOOLEAN,
36 JSON_TYPE_NULL,
37 JSON_TYPE_OBJECT,
38 JSON_TYPE_ARRAY
39 } json_type_t;
40
41 typedef enum parse_state {
42 PARSE_ERROR = -1,
43 PARSE_DONE = 0,
44 PARSE_REST,
45 PARSE_OBJECT,
46 PARSE_KEY_STRING,
47 PARSE_COLON,
48 PARSE_STRING,
49 PARSE_OBJECT_COMMA,
50 PARSE_ARRAY,
51 PARSE_BAREWORD,
52 PARSE_NUMBER,
53 PARSE_ARRAY_VALUE,
54 PARSE_ARRAY_COMMA
55 } parse_state_t;
56
57 #define JSON_MARKER ".__json_"
58 #define JSON_MARKER_ARRAY JSON_MARKER "array"
59
60 typedef struct parse_frame {
61 parse_state_t pf_ps;
62 nvlist_t *pf_nvl;
63
64 char *pf_key;
65 void *pf_value;
66 json_type_t pf_value_type;
67 int pf_array_index;
68
69 struct parse_frame *pf_next;
70 } parse_frame_t;
71
72 typedef struct state {
73 const char *s_in;
74 unsigned long s_pos;
75 unsigned long s_len;
76
77 parse_frame_t *s_top;
78
79 nvlist_parse_json_flags_t s_flags;
80
81 /*
82 * This string buffer is used for temporary storage by the
83 * "collect_*()" family of functions.
84 */
85 custr_t *s_collect;
86
87 int s_errno;
88 custr_t *s_errstr;
89 } state_t;
90
91 typedef void (*parse_handler_t)(state_t *);
92
93 static void
movestate(state_t * s,parse_state_t ps)94 movestate(state_t *s, parse_state_t ps)
95 {
96 if (s->s_flags & NVJSON_DEBUG) {
97 (void) fprintf(stderr, "nvjson: move state %d -> %d\n",
98 s->s_top->pf_ps, ps);
99 }
100 s->s_top->pf_ps = ps;
101 }
102
103 static void
posterror(state_t * s,int erno,const char * error)104 posterror(state_t *s, int erno, const char *error)
105 {
106 /*
107 * If the caller wants error messages printed to stderr, do that
108 * first.
109 */
110 if (s->s_flags & NVJSON_ERRORS_TO_STDERR) {
111 (void) fprintf(stderr, "nvjson error (pos %ld, errno %d): %s\n",
112 s->s_pos, erno, error);
113 }
114
115 /*
116 * Try and store the error message for the caller. This may fail if
117 * the error was related to memory pressure, and that condition still
118 * exists.
119 */
120 s->s_errno = erno;
121 if (s->s_errstr != NULL) {
122 (void) custr_append(s->s_errstr, error);
123 }
124
125 movestate(s, PARSE_ERROR);
126 }
127
128 static int
pushstate(state_t * s,parse_state_t ps,parse_state_t retps)129 pushstate(state_t *s, parse_state_t ps, parse_state_t retps)
130 {
131 parse_frame_t *n;
132
133 if (s->s_flags & NVJSON_DEBUG) {
134 (void) fprintf(stderr, "nvjson: push state %d -> %d (ret %d)\n",
135 s->s_top->pf_ps, ps, retps);
136 }
137
138 if ((n = calloc(1, sizeof (*n))) == NULL) {
139 posterror(s, errno, "pushstate calloc failure");
140 return (-1);
141 }
142
143 /*
144 * Store the state we'll return to when popping this
145 * frame:
146 */
147 s->s_top->pf_ps = retps;
148
149 /*
150 * Store the initial state for the new frame, and
151 * put it on top of the stack:
152 */
153 n->pf_ps = ps;
154 n->pf_value_type = JSON_TYPE_NOTHING;
155
156 n->pf_next = s->s_top;
157 s->s_top = n;
158
159 return (0);
160 }
161
162 static char
popchar(state_t * s)163 popchar(state_t *s)
164 {
165 if (s->s_pos > s->s_len) {
166 return (0);
167 }
168 return (s->s_in[s->s_pos++]);
169 }
170
171 static char
peekchar(state_t * s)172 peekchar(state_t *s)
173 {
174 if (s->s_pos > s->s_len) {
175 return (0);
176 }
177 return (s->s_in[s->s_pos]);
178 }
179
180 static void
discard_whitespace(state_t * s)181 discard_whitespace(state_t *s)
182 {
183 while (isspace(peekchar(s))) {
184 (void) popchar(s);
185 }
186 }
187
188 static char *escape_pairs[] = {
189 "\"\"", "\\\\", "//", "b\b", "f\f", "n\n", "r\r", "t\t", NULL
190 };
191
192 static char
collect_string_escape(state_t * s)193 collect_string_escape(state_t *s)
194 {
195 int i;
196 char c = popchar(s);
197
198 if (c == '\0') {
199 posterror(s, EPROTO, "EOF mid-escape sequence");
200 return (-1);
201 }
202
203 /*
204 * Handle four-digit Unicode escapes up to and including \u007f.
205 * Strings that cannot be represented as 7-bit clean ASCII are not
206 * currently supported.
207 */
208 if (c == 'u') {
209 int res;
210 int ndigs = 0;
211 char digs[5];
212
213 /*
214 * Deal with 4-digit unicode escape.
215 */
216 while (ndigs < 4) {
217 if ((digs[ndigs++] = popchar(s)) == '\0') {
218 posterror(s, EPROTO, "EOF mid-escape "
219 "sequence");
220 return (-1);
221 }
222 }
223 digs[4] = '\0';
224 if ((res = atoi(digs)) > 127) {
225 posterror(s, EPROTO, "unicode escape above 0x7f");
226 return (-1);
227 }
228
229 if (custr_appendc(s->s_collect, res) != 0) {
230 posterror(s, errno, "custr_appendc failure");
231 return (-1);
232 }
233 return (0);
234 }
235
236 /*
237 * See if this is a C-style escape character we recognise.
238 */
239 for (i = 0; escape_pairs[i] != NULL; i++) {
240 char *ep = escape_pairs[i];
241 if (ep[0] == c) {
242 if (custr_appendc(s->s_collect, ep[1]) != 0) {
243 posterror(s, errno, "custr_appendc failure");
244 return (-1);
245 }
246 return (0);
247 }
248 }
249
250 posterror(s, EPROTO, "unrecognised escape sequence");
251 return (-1);
252 }
253
254 static int
collect_string(state_t * s)255 collect_string(state_t *s)
256 {
257 custr_reset(s->s_collect);
258
259 for (;;) {
260 char c;
261
262 switch (c = popchar(s)) {
263 case '"':
264 /*
265 * Legal End of String.
266 */
267 return (0);
268
269 case '\0':
270 posterror(s, EPROTO, "EOF mid-string");
271 return (-1);
272
273 case '\\':
274 /*
275 * Escape Characters and Sequences.
276 */
277 if (collect_string_escape(s) != 0) {
278 return (-1);
279 }
280 break;
281
282 default:
283 if (custr_appendc(s->s_collect, c) != 0) {
284 posterror(s, errno, "custr_appendc failure");
285 return (-1);
286 }
287 break;
288 }
289 }
290 }
291
292 static int
collect_bareword(state_t * s)293 collect_bareword(state_t *s)
294 {
295 custr_reset(s->s_collect);
296
297 for (;;) {
298 if (!islower(peekchar(s))) {
299 return (0);
300 }
301
302 if (custr_appendc(s->s_collect, popchar(s)) != 0) {
303 posterror(s, errno, "custr_appendc failure");
304 return (-1);
305 }
306 }
307 }
308
309 static void
hdlr_bareword(state_t * s)310 hdlr_bareword(state_t *s)
311 {
312 const char *str;
313
314 if (collect_bareword(s) != 0) {
315 return;
316 }
317
318 str = custr_cstr(s->s_collect);
319 if (strcmp(str, "true") == 0) {
320 s->s_top->pf_value_type = JSON_TYPE_BOOLEAN;
321 s->s_top->pf_value = (void *)B_TRUE;
322 } else if (strcmp(str, "false") == 0) {
323 s->s_top->pf_value_type = JSON_TYPE_BOOLEAN;
324 s->s_top->pf_value = (void *)B_FALSE;
325 } else if (strcmp(str, "null") == 0) {
326 s->s_top->pf_value_type = JSON_TYPE_NULL;
327 } else {
328 posterror(s, EPROTO, "expected 'true', 'false' or 'null'");
329 return;
330 }
331
332 movestate(s, PARSE_DONE);
333 }
334
335 /* ARGSUSED */
336 static int
collect_number(state_t * s,boolean_t * isint,int32_t * result,double * fresult __unused)337 collect_number(state_t *s, boolean_t *isint, int32_t *result,
338 double *fresult __unused)
339 {
340 boolean_t neg = B_FALSE;
341 int t;
342
343 custr_reset(s->s_collect);
344
345 if (peekchar(s) == '-') {
346 neg = B_TRUE;
347 (void) popchar(s);
348 }
349 /*
350 * Read the 'int' portion:
351 */
352 if (!isdigit(peekchar(s))) {
353 posterror(s, EPROTO, "malformed number: expected digit (0-9)");
354 return (-1);
355 }
356 for (;;) {
357 if (!isdigit(peekchar(s))) {
358 break;
359 }
360 if (custr_appendc(s->s_collect, popchar(s)) != 0) {
361 posterror(s, errno, "custr_append failure");
362 return (-1);
363 }
364 }
365 if (peekchar(s) == '.' || peekchar(s) == 'e' || peekchar(s) == 'E') {
366 posterror(s, ENOTSUP, "do not yet support FRACs or EXPs");
367 return (-1);
368 }
369
370 t = atoi(custr_cstr(s->s_collect));
371
372 *isint = B_TRUE;
373 *result = (neg == B_TRUE) ? (-t) : t;
374 return (0);
375 }
376
377 static void
hdlr_number(state_t * s)378 hdlr_number(state_t *s)
379 {
380 boolean_t isint;
381 int32_t result;
382 double fresult;
383
384 if (collect_number(s, &isint, &result, &fresult) != 0) {
385 return;
386 }
387
388 if (isint == B_TRUE) {
389 s->s_top->pf_value = (void *)(uintptr_t)result;
390 s->s_top->pf_value_type = JSON_TYPE_INTEGER;
391 } else {
392 s->s_top->pf_value = malloc(sizeof (fresult));
393 bcopy(&fresult, s->s_top->pf_value, sizeof (fresult));
394 s->s_top->pf_value_type = JSON_TYPE_DOUBLE;
395 }
396
397 movestate(s, PARSE_DONE);
398 }
399
400 static void
hdlr_rest(state_t * s)401 hdlr_rest(state_t *s)
402 {
403 char c;
404 discard_whitespace(s);
405 c = popchar(s);
406 switch (c) {
407 case '{':
408 movestate(s, PARSE_OBJECT);
409 return;
410
411 case '[':
412 movestate(s, PARSE_ARRAY);
413 return;
414
415 default:
416 posterror(s, EPROTO, "EOF before object or array");
417 return;
418 }
419 }
420
421 static int
add_empty_child(state_t * s)422 add_empty_child(state_t *s)
423 {
424 /*
425 * Here, we create an empty nvlist to represent this object
426 * or array:
427 */
428 nvlist_t *empty;
429 if (nvlist_alloc(&empty, NV_UNIQUE_NAME, 0) != 0) {
430 posterror(s, errno, "nvlist_alloc failure");
431 return (-1);
432 }
433 if (s->s_top->pf_next != NULL) {
434 /*
435 * If we're a child of the frame above, we store ourselves in
436 * that frame's nvlist:
437 */
438 nvlist_t *nvl = s->s_top->pf_next->pf_nvl;
439 char *key = s->s_top->pf_next->pf_key;
440
441 if (nvlist_add_nvlist(nvl, key, empty) != 0) {
442 posterror(s, errno, "nvlist_add_nvlist failure");
443 nvlist_free(empty);
444 return (-1);
445 }
446 nvlist_free(empty);
447 if (nvlist_lookup_nvlist(nvl, key, &empty) != 0) {
448 posterror(s, errno, "nvlist_lookup_nvlist failure");
449 return (-1);
450 }
451 }
452 s->s_top->pf_nvl = empty;
453 return (0);
454 }
455
456 static int
decorate_array(state_t * s)457 decorate_array(state_t *s)
458 {
459 int idx = s->s_top->pf_array_index;
460 /*
461 * When we are done creating an array, we store a 'length'
462 * property on it, as well as an internal-use marker value.
463 */
464 if (nvlist_add_boolean(s->s_top->pf_nvl, JSON_MARKER_ARRAY) != 0 ||
465 nvlist_add_uint32(s->s_top->pf_nvl, "length", idx) != 0) {
466 posterror(s, errno, "nvlist_add failure");
467 return (-1);
468 }
469
470 return (0);
471 }
472
473 static void
hdlr_array(state_t * s)474 hdlr_array(state_t *s)
475 {
476 s->s_top->pf_value_type = JSON_TYPE_ARRAY;
477
478 if (add_empty_child(s) != 0) {
479 return;
480 }
481
482 discard_whitespace(s);
483
484 switch (peekchar(s)) {
485 case ']':
486 (void) popchar(s);
487
488 if (decorate_array(s) != 0) {
489 return;
490 }
491
492 movestate(s, PARSE_DONE);
493 return;
494
495 default:
496 movestate(s, PARSE_ARRAY_VALUE);
497 return;
498 }
499 }
500
501 static void
hdlr_array_comma(state_t * s)502 hdlr_array_comma(state_t *s)
503 {
504 discard_whitespace(s);
505
506 switch (popchar(s)) {
507 case ']':
508 if (decorate_array(s) != 0) {
509 return;
510 }
511
512 movestate(s, PARSE_DONE);
513 return;
514 case ',':
515 movestate(s, PARSE_ARRAY_VALUE);
516 return;
517 default:
518 posterror(s, EPROTO, "expected ',' or ']'");
519 return;
520 }
521 }
522
523 static void
hdlr_array_value(state_t * s)524 hdlr_array_value(state_t *s)
525 {
526 char c;
527
528 /*
529 * Generate keyname from the next array index:
530 */
531 if (s->s_top->pf_key != NULL) {
532 (void) fprintf(stderr, "pf_key not null! was %s\n",
533 s->s_top->pf_key);
534 abort();
535 }
536
537 if (asprintf(&s->s_top->pf_key, "%d", s->s_top->pf_array_index++) < 0) {
538 posterror(s, errno, "asprintf failure");
539 return;
540 }
541
542 discard_whitespace(s);
543
544 /*
545 * Select which type handler we need for the next value:
546 */
547 switch (c = peekchar(s)) {
548 case '"':
549 (void) popchar(s);
550 (void) pushstate(s, PARSE_STRING, PARSE_ARRAY_COMMA);
551 return;
552
553 case '{':
554 (void) popchar(s);
555 (void) pushstate(s, PARSE_OBJECT, PARSE_ARRAY_COMMA);
556 return;
557
558 case '[':
559 (void) popchar(s);
560 (void) pushstate(s, PARSE_ARRAY, PARSE_ARRAY_COMMA);
561 return;
562
563 default:
564 if (islower(c)) {
565 (void) pushstate(s, PARSE_BAREWORD,
566 PARSE_ARRAY_COMMA);
567 return;
568 } else if (c == '-' || isdigit(c)) {
569 (void) pushstate(s, PARSE_NUMBER, PARSE_ARRAY_COMMA);
570 return;
571 } else {
572 posterror(s, EPROTO, "unexpected character at start "
573 "of value");
574 return;
575 }
576 }
577 }
578
579 static void
hdlr_object(state_t * s)580 hdlr_object(state_t *s)
581 {
582 s->s_top->pf_value_type = JSON_TYPE_OBJECT;
583
584 if (add_empty_child(s) != 0) {
585 return;
586 }
587
588 discard_whitespace(s);
589
590 switch (popchar(s)) {
591 case '}':
592 movestate(s, PARSE_DONE);
593 return;
594
595 case '"':
596 movestate(s, PARSE_KEY_STRING);
597 return;
598
599 default:
600 posterror(s, EPROTO, "expected key or '}'");
601 return;
602 }
603 }
604
605 static void
hdlr_key_string(state_t * s)606 hdlr_key_string(state_t *s)
607 {
608 if (collect_string(s) != 0) {
609 return;
610 }
611
612 /*
613 * Record the key name of the next value.
614 */
615 if ((s->s_top->pf_key = strdup(custr_cstr(s->s_collect))) == NULL) {
616 posterror(s, errno, "strdup failure");
617 return;
618 }
619
620 movestate(s, PARSE_COLON);
621 }
622
623 static void
hdlr_colon(state_t * s)624 hdlr_colon(state_t *s)
625 {
626 char c;
627 discard_whitespace(s);
628
629 if ((c = popchar(s)) != ':') {
630 posterror(s, EPROTO, "expected ':'");
631 return;
632 }
633
634 discard_whitespace(s);
635
636 /*
637 * Select which type handler we need for the value after the colon:
638 */
639 switch (c = peekchar(s)) {
640 case '"':
641 (void) popchar(s);
642 (void) pushstate(s, PARSE_STRING, PARSE_OBJECT_COMMA);
643 return;
644
645 case '{':
646 (void) popchar(s);
647 (void) pushstate(s, PARSE_OBJECT, PARSE_OBJECT_COMMA);
648 return;
649
650 case '[':
651 (void) popchar(s);
652 (void) pushstate(s, PARSE_ARRAY, PARSE_OBJECT_COMMA);
653 return;
654
655 default:
656 if (islower(c)) {
657 (void) pushstate(s, PARSE_BAREWORD, PARSE_OBJECT_COMMA);
658 return;
659 } else if (c == '-' || isdigit(c)) {
660 (void) pushstate(s, PARSE_NUMBER, PARSE_OBJECT_COMMA);
661 return;
662 } else {
663 (void) posterror(s, EPROTO, "unexpected character at "
664 "start of value");
665 return;
666 }
667 }
668 }
669
670 static void
hdlr_object_comma(state_t * s)671 hdlr_object_comma(state_t *s)
672 {
673 discard_whitespace(s);
674
675 switch (popchar(s)) {
676 case '}':
677 movestate(s, PARSE_DONE);
678 return;
679
680 case ',':
681 discard_whitespace(s);
682 if (popchar(s) != '"') {
683 posterror(s, EPROTO, "expected '\"'");
684 return;
685 }
686 movestate(s, PARSE_KEY_STRING);
687 return;
688
689 default:
690 posterror(s, EPROTO, "expected ',' or '}'");
691 return;
692 }
693 }
694
695 static void
hdlr_string(state_t * s)696 hdlr_string(state_t *s)
697 {
698 if (collect_string(s) != 0) {
699 return;
700 }
701
702 s->s_top->pf_value_type = JSON_TYPE_STRING;
703 if ((s->s_top->pf_value = strdup(custr_cstr(s->s_collect))) == NULL) {
704 posterror(s, errno, "strdup failure");
705 return;
706 }
707
708 movestate(s, PARSE_DONE);
709 }
710
711 static int
store_value(state_t * s)712 store_value(state_t *s)
713 {
714 nvlist_t *targ = s->s_top->pf_next->pf_nvl;
715 char *key = s->s_top->pf_next->pf_key;
716 json_type_t type = s->s_top->pf_value_type;
717 int ret = 0;
718
719 switch (type) {
720 case JSON_TYPE_STRING:
721 if (nvlist_add_string(targ, key, s->s_top->pf_value) != 0) {
722 posterror(s, errno, "nvlist_add_string failure");
723 ret = -1;
724 }
725 free(s->s_top->pf_value);
726 break;
727
728 case JSON_TYPE_BOOLEAN:
729 if (nvlist_add_boolean_value(targ, key,
730 (boolean_t)s->s_top->pf_value) != 0) {
731 posterror(s, errno, "nvlist_add_boolean_value "
732 "failure");
733 ret = -1;
734 }
735 break;
736
737 case JSON_TYPE_NULL:
738 if (nvlist_add_boolean(targ, key) != 0) {
739 posterror(s, errno, "nvlist_add_boolean failure");
740 ret = -1;
741 }
742 break;
743
744 case JSON_TYPE_INTEGER:
745 if (nvlist_add_int32(targ, key,
746 (int32_t)(uintptr_t)s->s_top->pf_value) != 0) {
747 posterror(s, errno, "nvlist_add_int32 failure");
748 ret = -1;
749 }
750 break;
751
752 case JSON_TYPE_ARRAY:
753 case JSON_TYPE_OBJECT:
754 /*
755 * Objects and arrays are already 'stored' in their target
756 * nvlist on creation. See: hdlr_object, hdlr_array.
757 */
758 break;
759
760 default:
761 (void) fprintf(stderr, "ERROR: could not store unknown "
762 "type %d\n", type);
763 abort();
764 }
765
766 s->s_top->pf_value = NULL;
767 free(s->s_top->pf_next->pf_key);
768 s->s_top->pf_next->pf_key = NULL;
769 return (ret);
770 }
771
772 static parse_frame_t *
parse_frame_free(parse_frame_t * pf,boolean_t free_nvl)773 parse_frame_free(parse_frame_t *pf, boolean_t free_nvl)
774 {
775 parse_frame_t *next = pf->pf_next;
776 if (pf->pf_key != NULL) {
777 free(pf->pf_key);
778 }
779 if (pf->pf_value != NULL) {
780 abort();
781 }
782 if (free_nvl && pf->pf_nvl != NULL) {
783 nvlist_free(pf->pf_nvl);
784 }
785 free(pf);
786 return (next);
787 }
788
789 static parse_handler_t hdlrs[] = {
790 NULL, /* PARSE_DONE */
791 hdlr_rest, /* PARSE_REST */
792 hdlr_object, /* PARSE_OBJECT */
793 hdlr_key_string, /* PARSE_KEY_STRING */
794 hdlr_colon, /* PARSE_COLON */
795 hdlr_string, /* PARSE_STRING */
796 hdlr_object_comma, /* PARSE_OBJECT_COMMA */
797 hdlr_array, /* PARSE_ARRAY */
798 hdlr_bareword, /* PARSE_BAREWORD */
799 hdlr_number, /* PARSE_NUMBER */
800 hdlr_array_value, /* PARSE_ARRAY_VALUE */
801 hdlr_array_comma /* PARSE_ARRAY_COMMA */
802 };
803 #define NUM_PARSE_HANDLERS (int)(sizeof (hdlrs) / sizeof (hdlrs[0]))
804
805 int
nvlist_parse_json(const char * buf,size_t buflen,nvlist_t ** nvlp,nvlist_parse_json_flags_t flag,nvlist_parse_json_error_t * errout)806 nvlist_parse_json(const char *buf, size_t buflen, nvlist_t **nvlp,
807 nvlist_parse_json_flags_t flag, nvlist_parse_json_error_t *errout)
808 {
809 state_t s;
810
811 /*
812 * Check for valid flags:
813 */
814 if ((flag & NVJSON_FORCE_INTEGER) && (flag & NVJSON_FORCE_DOUBLE)) {
815 errno = EINVAL;
816 return (-1);
817 }
818 if ((flag & ~NVJSON_ALL) != 0) {
819 errno = EINVAL;
820 return (-1);
821 }
822
823 /*
824 * Initialise parsing state structure:
825 */
826 bzero(&s, sizeof (s));
827 s.s_in = buf;
828 s.s_pos = 0;
829 s.s_len = buflen;
830 s.s_flags = flag;
831
832 /*
833 * Allocate the collect buffer string.
834 */
835 if (custr_alloc(&s.s_collect) != 0) {
836 s.s_errno = errno;
837 if (errout != NULL) {
838 (void) snprintf(errout->nje_message,
839 sizeof (errout->nje_message),
840 "custr alloc failure: %s",
841 strerror(errno));
842 }
843 goto out;
844 }
845
846 /*
847 * If the caller has requested error information, allocate the error
848 * string now.
849 */
850 if (errout != NULL) {
851 if (custr_alloc_buf(&s.s_errstr, errout->nje_message,
852 sizeof (errout->nje_message)) != 0) {
853 s.s_errno = errno;
854 (void) snprintf(errout->nje_message,
855 sizeof (errout->nje_message),
856 "custr alloc failure: %s",
857 strerror(errno));
858 goto out;
859 }
860 custr_reset(s.s_errstr);
861 }
862
863 /*
864 * Allocate top-most stack frame:
865 */
866 if ((s.s_top = calloc(1, sizeof (*s.s_top))) == NULL) {
867 s.s_errno = errno;
868 goto out;
869 }
870
871 s.s_top->pf_ps = PARSE_REST;
872 for (;;) {
873 if (s.s_top->pf_ps < 0) {
874 /*
875 * The parser reported an error.
876 */
877 goto out;
878 }
879
880 if (s.s_top->pf_ps == PARSE_DONE) {
881 if (s.s_top->pf_next == NULL) {
882 /*
883 * Last frame, so we're really
884 * done.
885 */
886 *nvlp = s.s_top->pf_nvl;
887 goto out;
888 } else {
889 /*
890 * Otherwise, pop a frame and continue in
891 * previous state. Copy out the value we
892 * created in the old frame:
893 */
894 if (store_value(&s) != 0) {
895 goto out;
896 }
897
898 /*
899 * Free old frame:
900 */
901 s.s_top = parse_frame_free(s.s_top, B_FALSE);
902 }
903 }
904
905 /*
906 * Dispatch to parser handler routine for this state:
907 */
908 if (s.s_top->pf_ps >= NUM_PARSE_HANDLERS ||
909 hdlrs[s.s_top->pf_ps] == NULL) {
910 (void) fprintf(stderr, "no handler for state %d\n",
911 s.s_top->pf_ps);
912 abort();
913 }
914 hdlrs[s.s_top->pf_ps](&s);
915 }
916
917 out:
918 if (errout != NULL) {
919 /*
920 * Copy out error number and parse position. The custr_t for
921 * the error message was backed by the buffer in the error
922 * object, so no copying is required.
923 */
924 errout->nje_errno = s.s_errno;
925 errout->nje_pos = s.s_pos;
926 }
927
928 /*
929 * Free resources:
930 */
931 while (s.s_top != NULL) {
932 s.s_top = parse_frame_free(s.s_top, s.s_errno == 0 ? B_FALSE :
933 B_TRUE);
934 }
935 custr_free(s.s_collect);
936 custr_free(s.s_errstr);
937
938 errno = s.s_errno;
939 return (s.s_errno == 0 ? 0 : -1);
940 }
941