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