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