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
  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 *
  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 *
  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
  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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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 }