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