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 2014 Joyent, Inc.
14  */
15 
16 /*
17  * This program implements a small domain-specific language (DSL) for the
18  * generation of nvlists, and subsequent printing in JSON-formatted output.
19  * The test suite uses this tool to drive the JSON formatting routines in
20  * libnvpair(3LIB) for testing.
21  */
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <limits.h>
29 #include <locale.h>
30 
31 #include <libnvpair.h>
32 
33 #define	MAX_ARGS	100
34 #define	CMD_NAME_LEN	50
35 
36 /*
37  * As we are parsing a language that allows the creation of arbitrarily nested
38  * state, i.e. both nested nvlists and arrays of nested nvlists, we store that
39  * state in a stack.  The top frame in the stack represents the nested nvlist
40  * (or nvlists, for an array) that we are currently building.
41  *
42  * When creating an array, the "next" directive advances lw_pos and allocates a
43  * new nvlist.  The "end" directive commits either the nvlist, or array of
44  * nvlists, into the parent nvlist.  It then pops and frees the stack frame
45  * before returning control to the parser.
46  */
47 
48 typedef struct list_wrap {
49 	nvlist_t *lw_nvl[MAX_ARGS];
50 	char *lw_name;
51 	int lw_pos;
52 	boolean_t lw_array;
53 	struct list_wrap *lw_next;
54 } list_wrap_t;
55 
56 int
list_wrap_depth(list_wrap_t * lw)57 list_wrap_depth(list_wrap_t *lw)
58 {
59 	int d = 0;
60 
61 	while (lw != NULL) {
62 		d++;
63 		lw = lw->lw_next;
64 	}
65 
66 	return (d);
67 }
68 
69 list_wrap_t *
list_wrap_alloc(list_wrap_t * next)70 list_wrap_alloc(list_wrap_t *next)
71 {
72 	list_wrap_t *out = calloc(1, sizeof (list_wrap_t));
73 
74 	if (out == NULL)
75 		abort();
76 
77 	out->lw_next = next;
78 
79 	return (out);
80 }
81 
82 list_wrap_t *
list_wrap_pop_and_free(list_wrap_t * lw)83 list_wrap_pop_and_free(list_wrap_t *lw)
84 {
85 	list_wrap_t *next = lw->lw_next;
86 
87 	free(lw->lw_name);
88 	free(lw);
89 
90 	return (next);
91 }
92 
93 /*
94  * Generic integer and floating point parsing routines:
95  */
96 
97 int
parse_int(char * in,int64_t * val,int64_t min,int64_t max)98 parse_int(char *in, int64_t *val, int64_t min, int64_t max)
99 {
100 	int64_t t;
101 	char *end = NULL;
102 
103 	errno = 0;
104 	t = strtoll(in, &end, 10);
105 	if (errno != 0 || end == in || *end != '\0') {
106 		if (errno == ERANGE) {
107 			(void) fprintf(stderr, "ERROR: integer %s not in "
108 			    "range [%lld,%lld]\n", in, min, max);
109 			return (-1);
110 		}
111 		(void) fprintf(stderr, "ERROR: could not parse \"%s\" as "
112 		    "signed integer (%s)\n", in, strerror(errno));
113 		return (-1);
114 	}
115 
116 	if (t < min || t > max) {
117 		(void) fprintf(stderr, "ERROR: integer %lld not in range "
118 		    "[%lld,%lld]\n", t, min, max);
119 		return (-1);
120 	}
121 
122 	*val = t;
123 	return (0);
124 }
125 
126 int
parse_uint(char * in,uint64_t * val,uint64_t min,uint64_t max)127 parse_uint(char *in, uint64_t *val, uint64_t min, uint64_t max)
128 {
129 	uint64_t t;
130 	char *end = NULL;
131 
132 	errno = 0;
133 	t = strtoull(in, &end, 10);
134 	if (errno != 0 || end == in || *end != '\0') {
135 		if (errno == ERANGE) {
136 			(void) fprintf(stderr, "ERROR: integer %s not in "
137 			    "range [%llu,%llu]\n", in, min, max);
138 			return (-1);
139 		}
140 		(void) fprintf(stderr, "ERROR: could not parse \"%s\" as "
141 		    "unsigned integer (%s)\n", in, strerror(errno));
142 		return (-1);
143 	}
144 
145 	if (t < min || t > max) {
146 		(void) fprintf(stderr, "ERROR: integer %llu not in range "
147 		    "[%llu,%llu]\n", t, min, max);
148 		return (-1);
149 	}
150 
151 	*val = t;
152 	return (0);
153 }
154 
155 int
parse_double(char * in,double * val)156 parse_double(char *in, double *val)
157 {
158 	double t;
159 	char *end = NULL;
160 
161 	errno = 0;
162 	t = strtod(in, &end);
163 	if (errno != 0 || end == in || *end != '\0') {
164 		(void) fprintf(stderr, "ERROR: could not parse \"%s\" as "
165 		    "double\n", in);
166 		return (-1);
167 	}
168 
169 	*val = t;
170 	return (0);
171 }
172 
173 /*
174  * Command-specific handlers for directives specified in the DSL input:
175  */
176 
177 typedef int (*command_handler_t)(list_wrap_t **, boolean_t, int,
178     char **);
179 
180 static int
ch_add_string(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)181 ch_add_string(list_wrap_t **lw, boolean_t array, int argc, char **argv)
182 {
183 	nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
184 
185 	if (array) {
186 		if (nvlist_add_string_array(nvl, argv[0], &argv[1],
187 		    argc - 1) != 0) {
188 			(void) fprintf(stderr, "fail at "
189 			    "nvlist_add_string_array\n");
190 			return (-1);
191 		}
192 	} else {
193 		if (nvlist_add_string(nvl, argv[0], argv[1]) != 0) {
194 			(void) fprintf(stderr, "fail at nvlist_add_string\n");
195 			return (-1);
196 		}
197 	}
198 
199 	return (0);
200 }
201 
202 static int
ch_add_boolean(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)203 ch_add_boolean(list_wrap_t **lw, boolean_t array, int argc, char **argv)
204 {
205 	nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
206 
207 	if (array)
208 		abort();
209 
210 	if (nvlist_add_boolean(nvl, argv[0]) != 0) {
211 		(void) fprintf(stderr, "fail at nvlist_add_boolean\n");
212 		return (-1);
213 	}
214 	return (0);
215 }
216 
217 static int
ch_add_boolean_value(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)218 ch_add_boolean_value(list_wrap_t **lw, boolean_t array, int argc, char **argv)
219 {
220 	int i;
221 	nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
222 	boolean_t arrval[MAX_ARGS];
223 
224 	for (i = 1; i < argc; i++) {
225 		if (strcmp(argv[i], "true") == 0) {
226 			arrval[i - 1] = B_TRUE;
227 		} else if (strcmp(argv[i], "false") == 0) {
228 			arrval[i - 1] = B_FALSE;
229 		} else {
230 			(void) fprintf(stderr, "invalid boolean value: %s\n",
231 			    argv[i]);
232 			return (-1);
233 		}
234 	}
235 
236 	if (array) {
237 		if (nvlist_add_boolean_array(nvl, argv[0], arrval,
238 		    argc - 1) != 0) {
239 			(void) fprintf(stderr, "fail at "
240 			    "nvlist_add_boolean_array\n");
241 			return (-1);
242 		}
243 	} else {
244 		if (nvlist_add_boolean_value(nvl, argv[0], arrval[0]) != 0) {
245 			(void) fprintf(stderr, "fail at "
246 			    "nvlist_add_boolean_value\n");
247 			return (-1);
248 		}
249 	}
250 
251 	return (0);
252 }
253 
254 
255 /*
256  * The confluence of a strongly typed C API for libnvpair(3LIB) and the
257  * combinatorial explosion of both sizes and signedness is unfortunate.  Rather
258  * than reproduce the same code over and over, this macro parses an integer,
259  * checks applicable bounds based on size and signedness, and stores the value
260  * (or array of values).
261  */
262 #define	DO_CMD_NUMBER(typ, nam, min, max, ptyp, func)			\
263 	ptyp val;							\
264 	typ ## _t arrval[MAX_ARGS];					\
265 	int i;								\
266 	for (i = 1; i < argc; i++) {					\
267 		if (func(argv[i], &val, min, max) != 0) {		\
268 			return (-1);					\
269 		}							\
270 		arrval[i - 1] = (typ ## _t) val;			\
271 	}								\
272 	if (array) {							\
273 		if (nvlist_add_ ## nam ## _array(nvl, argv[0],		\
274 		    arrval, argc - 1) != 0) {				\
275 			(void) fprintf(stderr, "fail at "		\
276 			    "nvlist_add_" #nam "_array\n");		\
277 			return (-1);					\
278 		}							\
279 	} else {							\
280 		if (nvlist_add_ ## nam(nvl, argv[0],			\
281 		    arrval[0]) == -1) {					\
282 			(void) fprintf(stderr, "fail at "		\
283 			    "nvlist_add_" #nam "\n");			\
284 			return (-1);					\
285 		}							\
286 	}								\
287 	return (0);
288 
289 static int
ch_add_byte(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)290 ch_add_byte(list_wrap_t **lw, boolean_t array, int argc, char **argv)
291 {
292 	nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
293 
294 	DO_CMD_NUMBER(uchar, byte, 0, UCHAR_MAX, uint64_t, parse_uint)
295 }
296 
297 static int
ch_add_int8(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)298 ch_add_int8(list_wrap_t **lw, boolean_t array, int argc, char **argv)
299 {
300 	nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
301 
302 	DO_CMD_NUMBER(int8, int8, INT8_MIN, INT8_MAX, int64_t, parse_int)
303 }
304 
305 static int
ch_add_uint8(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)306 ch_add_uint8(list_wrap_t **lw, boolean_t array, int argc, char **argv)
307 {
308 	nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
309 
310 	DO_CMD_NUMBER(uint8, uint8, 0, UINT8_MAX, uint64_t, parse_uint)
311 }
312 
313 static int
ch_add_int16(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)314 ch_add_int16(list_wrap_t **lw, boolean_t array, int argc, char **argv)
315 {
316 	nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
317 
318 	DO_CMD_NUMBER(int16, int16, INT16_MIN, INT16_MAX, int64_t, parse_int)
319 }
320 
321 static int
ch_add_uint16(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)322 ch_add_uint16(list_wrap_t **lw, boolean_t array, int argc, char **argv)
323 {
324 	nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
325 
326 	DO_CMD_NUMBER(uint16, uint16, 0, UINT16_MAX, uint64_t, parse_uint)
327 }
328 
329 static int
ch_add_int32(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)330 ch_add_int32(list_wrap_t **lw, boolean_t array, int argc, char **argv)
331 {
332 	nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
333 
334 	DO_CMD_NUMBER(int32, int32, INT32_MIN, INT32_MAX, int64_t, parse_int)
335 }
336 
337 static int
ch_add_uint32(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)338 ch_add_uint32(list_wrap_t **lw, boolean_t array, int argc, char **argv)
339 {
340 	nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
341 
342 	DO_CMD_NUMBER(uint32, uint32, 0, UINT32_MAX, uint64_t, parse_uint)
343 }
344 
345 static int
ch_add_int64(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)346 ch_add_int64(list_wrap_t **lw, boolean_t array, int argc, char **argv)
347 {
348 	nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
349 
350 	DO_CMD_NUMBER(int64, int64, INT64_MIN, INT64_MAX, int64_t, parse_int)
351 }
352 
353 static int
ch_add_uint64(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)354 ch_add_uint64(list_wrap_t **lw, boolean_t array, int argc, char **argv)
355 {
356 	nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
357 
358 	DO_CMD_NUMBER(uint64, uint64, 0, UINT64_MAX, uint64_t, parse_uint)
359 }
360 
361 static int
ch_add_double(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)362 ch_add_double(list_wrap_t **lw, boolean_t array, int argc, char **argv)
363 {
364 	nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
365 	double val;
366 
367 	if (array)
368 		abort();
369 
370 	if (parse_double(argv[1], &val) != 0) {
371 		return (-1);
372 	}
373 
374 	if (nvlist_add_double(nvl, argv[0], val) != 0) {
375 		(void) fprintf(stderr, "fail at nvlist_add_double_value\n");
376 		return (-1);
377 	}
378 
379 	return (0);
380 }
381 
382 static int
ch_end(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)383 ch_end(list_wrap_t **lw, boolean_t array, int argc, char **argv)
384 {
385 	nvlist_t *parent;
386 	char *name;
387 
388 	if (list_wrap_depth(*lw) < 2) {
389 		(void) fprintf(stderr, "ERROR: not nested, cannot end.\n");
390 		return (-1);
391 	}
392 
393 	parent = (*lw)->lw_next->lw_nvl[(*lw)->lw_next->lw_pos];
394 	name = (*lw)->lw_name;
395 	if ((*lw)->lw_array) {
396 		/*
397 		 * This was an array of objects.
398 		 */
399 		nvlist_t **children = (*lw)->lw_nvl;
400 		int nelems = (*lw)->lw_pos + 1;
401 
402 		if (nvlist_add_nvlist_array(parent, name, children,
403 		    nelems) != 0) {
404 			(void) fprintf(stderr, "fail at "
405 			    "nvlist_add_nvlist_array\n");
406 			return (-1);
407 		}
408 	} else {
409 		/*
410 		 * This was a single object.
411 		 */
412 		nvlist_t *child = (*lw)->lw_nvl[0];
413 
414 		if ((*lw)->lw_pos != 0)
415 			abort();
416 
417 		if (nvlist_add_nvlist(parent, name, child) != 0) {
418 			(void) fprintf(stderr, "fail at nvlist_add_nvlist\n");
419 			return (-1);
420 		}
421 	}
422 
423 	*lw = list_wrap_pop_and_free(*lw);
424 
425 	return (0);
426 }
427 
428 static int
ch_next(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)429 ch_next(list_wrap_t **lw, boolean_t array, int argc, char **argv)
430 {
431 	if (!(*lw)->lw_array) {
432 		(void) fprintf(stderr, "ERROR: cannot use 'next' outside an "
433 		    "object array.\n");
434 		return (-1);
435 	}
436 
437 	if ((*lw)->lw_pos++ >= MAX_ARGS) {
438 		(void) fprintf(stderr, "ERROR: object array too long\n");
439 		return (-1);
440 	}
441 
442 	if (nvlist_alloc(&(*lw)->lw_nvl[(*lw)->lw_pos], NV_UNIQUE_NAME,
443 	    0) != 0) {
444 		(void) fprintf(stderr, "ERROR: failed at nvlist_alloc\n");
445 		return (-1);
446 	}
447 
448 	return (0);
449 }
450 
451 static int
ch_add_object(list_wrap_t ** lw,boolean_t array,int argc,char ** argv)452 ch_add_object(list_wrap_t **lw, boolean_t array, int argc, char **argv)
453 {
454 	*lw = list_wrap_alloc(*lw);
455 
456 	(*lw)->lw_name = strdup(argv[0]);
457 	(*lw)->lw_array = array;
458 
459 	if (nvlist_alloc(&(*lw)->lw_nvl[0], NV_UNIQUE_NAME, 0) != 0) {
460 		(void) fprintf(stderr, "fail at nvlist_alloc\n");
461 		return (-1);
462 	}
463 
464 	return (0);
465 }
466 
467 typedef struct command {
468 	char cmd_name[CMD_NAME_LEN];
469 	command_handler_t cmd_func;
470 	int cmd_min_args;
471 	int cmd_max_args;
472 	boolean_t cmd_array_mode;
473 } command_t;
474 
475 /*
476  * These are the commands we support in the testing DSL, and their
477  * handling functions:
478  */
479 command_t command_handlers[] = {
480 	{ "add_boolean", ch_add_boolean, 1, 1, B_FALSE },
481 	{ "add_boolean_value", ch_add_boolean_value, 2, 2, B_FALSE },
482 	{ "add_byte", ch_add_byte, 2, 2, B_FALSE },
483 	{ "add_int8", ch_add_int8, 2, 2, B_FALSE },
484 	{ "add_uint8", ch_add_uint8, 2, 2, B_FALSE },
485 	{ "add_int16", ch_add_int16, 2, 2, B_FALSE },
486 	{ "add_uint16", ch_add_uint16, 2, 2, B_FALSE },
487 	{ "add_int32", ch_add_int32, 2, 2, B_FALSE },
488 	{ "add_uint32", ch_add_uint32, 2, 2, B_FALSE },
489 	{ "add_int64", ch_add_int64, 2, 2, B_FALSE },
490 	{ "add_uint64", ch_add_uint64, 2, 2, B_FALSE },
491 	{ "add_double", ch_add_double, 2, 2, B_FALSE },
492 	{ "add_string", ch_add_string, 2, 2, B_FALSE },
493 	{ "add_object", ch_add_object, 1, 1, B_FALSE },
494 	{ "add_boolean_array", ch_add_boolean_value, 1, MAX_ARGS, B_TRUE },
495 	{ "add_byte_array", ch_add_byte, 1, MAX_ARGS, B_TRUE },
496 	{ "add_int8_array", ch_add_int8, 1, MAX_ARGS, B_TRUE },
497 	{ "add_uint8_array", ch_add_uint8, 1, MAX_ARGS, B_TRUE },
498 	{ "add_int16_array", ch_add_int16, 1, MAX_ARGS, B_TRUE },
499 	{ "add_uint16_array", ch_add_uint16, 1, MAX_ARGS, B_TRUE },
500 	{ "add_int32_array", ch_add_int32, 1, MAX_ARGS, B_TRUE },
501 	{ "add_uint32_array", ch_add_uint32, 1, MAX_ARGS, B_TRUE },
502 	{ "add_int64_array", ch_add_int64, 1, MAX_ARGS, B_TRUE },
503 	{ "add_uint64_array", ch_add_uint64, 1, MAX_ARGS, B_TRUE },
504 	{ "add_string_array", ch_add_string, 1, MAX_ARGS, B_TRUE },
505 	{ "add_object_array", ch_add_object, 1, 1, B_TRUE },
506 	{ "end", ch_end, 0, 0, B_FALSE },
507 	{ "next", ch_next, 0, 0, B_FALSE },
508 	{ 0 }
509 };
510 
511 /*
512  * This function determines which command we are executing, checks argument
513  * counts, and dispatches to the appropriate handler:
514  */
515 static int
command_call(list_wrap_t ** lw,char * command,int argc,char ** argv)516 command_call(list_wrap_t **lw, char *command, int argc, char **argv)
517 {
518 	int ch;
519 
520 	for (ch = 0; command_handlers[ch].cmd_name[0] != '\0'; ch++) {
521 		if (strcmp(command, command_handlers[ch].cmd_name) != 0)
522 			continue;
523 
524 		if (argc > command_handlers[ch].cmd_max_args ||
525 		    argc < command_handlers[ch].cmd_min_args) {
526 
527 			(void) fprintf(stderr, "ERROR: command \"%s\""
528 			    " expects between %d and %d arguments,"
529 			    " but %d were provided.\n", command,
530 			    command_handlers[ch].cmd_min_args,
531 			    command_handlers[ch].cmd_max_args,
532 			    argc);
533 
534 			return (-1);
535 		}
536 
537 		return (command_handlers[ch].cmd_func(lw,
538 		    command_handlers[ch].cmd_array_mode, argc, argv));
539 	}
540 
541 	(void) fprintf(stderr, "ERROR: invalid command: \"%s\"\n", command);
542 
543 	return (-1);
544 }
545 
546 /*
547  * The primary state machine for parsing the input DSL is implemented in
548  * this function:
549  */
550 
551 typedef enum state {
552 	STATE_REST = 1,
553 	STATE_COMMAND,
554 	STATE_ARG_FIND,
555 	STATE_ARG,
556 	STATE_ARG_ESCAPE,
557 	STATE_ARG_ESCAPE_HEX,
558 	STATE_C_COMMENT_0,
559 	STATE_C_COMMENT_1,
560 	STATE_C_COMMENT_2
561 } state_t;
562 
563 int
parse(FILE * in,list_wrap_t ** lw)564 parse(FILE *in, list_wrap_t **lw)
565 {
566 	char b[8192];
567 	int bp;
568 	state_t st = STATE_REST;
569 	int argc = 0;
570 	char *argv[MAX_ARGS];
571 	int line = 1;
572 	char hex[3];
573 	int nhex = 0;
574 
575 	b[0] = '\0';
576 	bp = 0;
577 
578 	for (;;) {
579 		int c = fgetc(in);
580 
581 		/*
582 		 * Signal an error if the file ends part way through a
583 		 * construct:
584 		 */
585 		if (st != STATE_REST && c == EOF) {
586 			(void) fprintf(stderr, "ERROR: unexpected end of "
587 			    "file\n");
588 			return (-1);
589 		} else if (c == EOF) {
590 			return (0);
591 		}
592 
593 		if (c == '\n')
594 			line++;
595 
596 		switch (st) {
597 		case STATE_REST:
598 			if (isalpha(c) || c == '_') {
599 				argc = 0;
600 				bp = 0;
601 				b[bp++] = c;
602 				b[bp] = '\0';
603 				st = STATE_COMMAND;
604 				continue;
605 			} else if (c == ' ' || c == '\t' || c == '\n') {
606 				/*
607 				 * Ignore whitespace.
608 				 */
609 				continue;
610 			} else if (c == '/') {
611 				st = STATE_C_COMMENT_0;
612 				continue;
613 			} else {
614 				goto unexpected;
615 			}
616 
617 		case STATE_C_COMMENT_0:
618 			if (c != '*') {
619 				goto unexpected;
620 			}
621 			st = STATE_C_COMMENT_1;
622 			continue;
623 
624 		case STATE_C_COMMENT_1:
625 			if (c == '*') {
626 				st = STATE_C_COMMENT_2;
627 			}
628 			continue;
629 
630 		case STATE_C_COMMENT_2:
631 			if (c == '/') {
632 				st = STATE_REST;
633 			} else if (c != '*') {
634 				st = STATE_C_COMMENT_1;
635 			}
636 			continue;
637 
638 		case STATE_COMMAND:
639 			if (isalnum(c) || c == '_') {
640 				b[bp++] = c;
641 				b[bp] = '\0';
642 				st = STATE_COMMAND;
643 
644 				continue;
645 
646 			} else if (isspace(c)) {
647 				/*
648 				 * Start collecting arguments into 'b'
649 				 * after the command.
650 				 */
651 				st = STATE_ARG_FIND;
652 				bp++;
653 
654 				continue;
655 			} else if (c == ';') {
656 				/*
657 				 * This line was _just_ a command,
658 				 * so break out and process now:
659 				 */
660 				goto execute;
661 			} else {
662 				goto unexpected;
663 			}
664 
665 		case STATE_ARG_FIND:
666 			if (isspace(c)) {
667 				/*
668 				 * Whitespace, ignore.
669 				 */
670 				continue;
671 
672 			} else if (c == ';') {
673 				/*
674 				 * Break out to process command.
675 				 */
676 				goto execute;
677 
678 			} else if (c == '"') {
679 				st = STATE_ARG;
680 
681 				argv[argc] = &b[++bp];
682 				b[bp] = '\0';
683 
684 				continue;
685 			} else {
686 				goto unexpected;
687 			}
688 
689 		case STATE_ARG:
690 			if (c == '"') {
691 				if (argc++ >= MAX_ARGS) {
692 					(void) fprintf(stderr, "ERROR: too "
693 					    "many args\n");
694 					return (-1);
695 				}
696 				st = STATE_ARG_FIND;
697 				continue;
698 			} else if (c == '\n') {
699 				(void) fprintf(stderr, "ERROR: line not "
700 				    "finished\n");
701 				return (-1);
702 			} else if (c == '\\') {
703 				st = STATE_ARG_ESCAPE;
704 				continue;
705 			} else {
706 				b[bp++] = c;
707 				b[bp] = '\0';
708 				continue;
709 			}
710 
711 		case STATE_ARG_ESCAPE:
712 			if (c == 'a') {
713 				c = '\a';
714 			} else if (c == 'b') {
715 				c = '\b';
716 			} else if (c == 'f') {
717 				c = '\f';
718 			} else if (c == 'n') {
719 				c = '\n';
720 			} else if (c == 'r') {
721 				c = '\r';
722 			} else if (c == 't') {
723 				c = '\t';
724 			} else if (c == 'v') {
725 				c = '\v';
726 			} else if (c == 'x') {
727 				st = STATE_ARG_ESCAPE_HEX;
728 				hex[0] = hex[1] = hex[2] = '\0';
729 				nhex = 0;
730 				continue;
731 			} else if (c != '\\' && c != '"') {
732 				goto unexpected;
733 			}
734 
735 			b[bp++] = c;
736 			b[bp] = '\0';
737 			st = STATE_ARG;
738 			continue;
739 
740 		case STATE_ARG_ESCAPE_HEX:
741 			if (!isxdigit(c)) {
742 				goto unexpected;
743 			}
744 			hex[nhex] = c;
745 			if (nhex++ >= 1) {
746 				/*
747 				 * The hex escape pair is complete, parse
748 				 * the integer and insert it as a character:
749 				 */
750 				int x;
751 				errno = 0;
752 				if ((x = strtol(hex, NULL, 16)) == 0 ||
753 				    errno != 0) {
754 					goto unexpected;
755 				}
756 				b[bp++] = (char)x;
757 				b[bp] = '\0';
758 				st = STATE_ARG;
759 			}
760 			continue;
761 		}
762 
763 		/*
764 		 * We do not ever expect to break out of the switch block
765 		 * above.  If we do, it's a programmer error.
766 		 */
767 		abort();
768 
769 execute:
770 		if (command_call(lw, b, argc, argv) == -1)
771 			return (-1);
772 
773 		st = STATE_REST;
774 		continue;
775 
776 unexpected:
777 		(void) fprintf(stderr, "ERROR: (line %d) unexpected "
778 		    "character: %c\n", line, c);
779 		return (-1);
780 	}
781 }
782 
783 /*
784  * Entry point:
785  */
786 int
main(int argc,char ** argv)787 main(int argc, char **argv)
788 {
789 	int rc = EXIT_FAILURE;
790 	list_wrap_t *lw;
791 
792 	/*
793 	 * Be locale-aware.  The JSON output functions will process multibyte
794 	 * characters in the current locale, and emit a correct JSON encoding
795 	 * for unprintable characters.
796 	 */
797 	if (setlocale(LC_ALL, "") == NULL) {
798 		(void) fprintf(stderr, "Could not set locale: %s\n",
799 		    strerror(errno));
800 		goto out;
801 	}
802 
803 	lw = list_wrap_alloc(NULL);
804 
805 	if (nvlist_alloc(&lw->lw_nvl[0], NV_UNIQUE_NAME, 0) != 0)
806 		goto out;
807 
808 	/*
809 	 * Generate the list from the commands passed to us on stdin:
810 	 */
811 	if (parse(stdin, &lw) != 0)
812 		goto out;
813 
814 	/*
815 	 * Print the resultant list, and a terminating newline:
816 	 */
817 	if (nvlist_print_json(stdout, lw->lw_nvl[0]) != 0 ||
818 	    fprintf(stdout, "\n") < 0)
819 		goto out;
820 
821 	rc = EXIT_SUCCESS;
822 
823 out:
824 	(void) list_wrap_pop_and_free(lw);
825 
826 	return (rc);
827 }
828