xref: /illumos-gate/usr/src/lib/varpd/files/common/libvarpd_files_json.c (revision ddb365bfc9e868ad24ccdcb0dc91af18b10df082)
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 "libvarpd_files_json.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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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