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 }