xref: /titanic_44/usr/src/lib/json_nvlist/json_nvlist.c (revision ab05745263b5e28e134f5e7129d9098f12fe660d)
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