1 /* $OpenBSD: cmd-parse.y,v 1.47 2021/09/09 06:57:48 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 %{ 20 21 #include <sys/types.h> 22 23 #include <ctype.h> 24 #include <errno.h> 25 #include <pwd.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 #include <wchar.h> 30 31 #include "tmux.h" 32 33 static int yylex(void); 34 static int yyparse(void); 35 static int printflike(1,2) yyerror(const char *, ...); 36 37 static char *yylex_token(int); 38 static char *yylex_format(void); 39 40 struct cmd_parse_scope { 41 int flag; 42 TAILQ_ENTRY (cmd_parse_scope) entry; 43 }; 44 45 enum cmd_parse_argument_type { 46 CMD_PARSE_STRING, 47 CMD_PARSE_COMMANDS, 48 CMD_PARSE_PARSED_COMMANDS 49 }; 50 51 struct cmd_parse_argument { 52 enum cmd_parse_argument_type type; 53 char *string; 54 struct cmd_parse_commands *commands; 55 struct cmd_list *cmdlist; 56 57 TAILQ_ENTRY(cmd_parse_argument) entry; 58 }; 59 TAILQ_HEAD(cmd_parse_arguments, cmd_parse_argument); 60 61 struct cmd_parse_command { 62 u_int line; 63 struct cmd_parse_arguments arguments; 64 65 TAILQ_ENTRY(cmd_parse_command) entry; 66 }; 67 TAILQ_HEAD(cmd_parse_commands, cmd_parse_command); 68 69 struct cmd_parse_state { 70 FILE *f; 71 72 const char *buf; 73 size_t len; 74 size_t off; 75 76 int condition; 77 int eol; 78 int eof; 79 struct cmd_parse_input *input; 80 u_int escapes; 81 82 char *error; 83 struct cmd_parse_commands *commands; 84 85 struct cmd_parse_scope *scope; 86 TAILQ_HEAD(, cmd_parse_scope) stack; 87 }; 88 static struct cmd_parse_state parse_state; 89 90 static char *cmd_parse_get_error(const char *, u_int, const char *); 91 static void cmd_parse_free_command(struct cmd_parse_command *); 92 static struct cmd_parse_commands *cmd_parse_new_commands(void); 93 static void cmd_parse_free_commands(struct cmd_parse_commands *); 94 static void cmd_parse_build_commands(struct cmd_parse_commands *, 95 struct cmd_parse_input *, struct cmd_parse_result *); 96 static void cmd_parse_print_commands(struct cmd_parse_input *, 97 struct cmd_list *); 98 99 %} 100 101 %union 102 { 103 char *token; 104 struct cmd_parse_arguments *arguments; 105 struct cmd_parse_argument *argument; 106 int flag; 107 struct { 108 int flag; 109 struct cmd_parse_commands *commands; 110 } elif; 111 struct cmd_parse_commands *commands; 112 struct cmd_parse_command *command; 113 } 114 115 %token ERROR 116 %token HIDDEN 117 %token IF 118 %token ELSE 119 %token ELIF 120 %token ENDIF 121 %token <token> FORMAT TOKEN EQUALS 122 123 %type <token> expanded format 124 %type <arguments> arguments 125 %type <argument> argument 126 %type <flag> if_open if_elif 127 %type <elif> elif elif1 128 %type <commands> argument_statements statements statement 129 %type <commands> commands condition condition1 130 %type <command> command 131 132 %% 133 134 lines : /* empty */ 135 | statements 136 { 137 struct cmd_parse_state *ps = &parse_state; 138 139 ps->commands = $1; 140 } 141 142 statements : statement '\n' 143 { 144 $$ = $1; 145 } 146 | statements statement '\n' 147 { 148 $$ = $1; 149 TAILQ_CONCAT($$, $2, entry); 150 free($2); 151 } 152 153 statement : /* empty */ 154 { 155 $$ = xmalloc (sizeof *$$); 156 TAILQ_INIT($$); 157 } 158 | hidden_assignment 159 { 160 $$ = xmalloc (sizeof *$$); 161 TAILQ_INIT($$); 162 } 163 | condition 164 { 165 struct cmd_parse_state *ps = &parse_state; 166 167 if (ps->scope == NULL || ps->scope->flag) 168 $$ = $1; 169 else { 170 $$ = cmd_parse_new_commands(); 171 cmd_parse_free_commands($1); 172 } 173 } 174 | commands 175 { 176 struct cmd_parse_state *ps = &parse_state; 177 178 if (ps->scope == NULL || ps->scope->flag) 179 $$ = $1; 180 else { 181 $$ = cmd_parse_new_commands(); 182 cmd_parse_free_commands($1); 183 } 184 } 185 186 format : FORMAT 187 { 188 $$ = $1; 189 } 190 | TOKEN 191 { 192 $$ = $1; 193 } 194 195 expanded : format 196 { 197 struct cmd_parse_state *ps = &parse_state; 198 struct cmd_parse_input *pi = ps->input; 199 struct format_tree *ft; 200 struct client *c = pi->c; 201 struct cmd_find_state *fsp; 202 struct cmd_find_state fs; 203 int flags = FORMAT_NOJOBS; 204 205 if (cmd_find_valid_state(&pi->fs)) 206 fsp = &pi->fs; 207 else { 208 cmd_find_from_client(&fs, c, 0); 209 fsp = &fs; 210 } 211 ft = format_create(NULL, pi->item, FORMAT_NONE, flags); 212 format_defaults(ft, c, fsp->s, fsp->wl, fsp->wp); 213 214 $$ = format_expand(ft, $1); 215 format_free(ft); 216 free($1); 217 } 218 219 optional_assignment : /* empty */ 220 | assignment 221 222 assignment : EQUALS 223 { 224 struct cmd_parse_state *ps = &parse_state; 225 int flags = ps->input->flags; 226 227 if ((~flags & CMD_PARSE_PARSEONLY) && 228 (ps->scope == NULL || ps->scope->flag)) 229 environ_put(global_environ, $1, 0); 230 free($1); 231 } 232 233 hidden_assignment : HIDDEN EQUALS 234 { 235 struct cmd_parse_state *ps = &parse_state; 236 int flags = ps->input->flags; 237 238 if ((~flags & CMD_PARSE_PARSEONLY) && 239 (ps->scope == NULL || ps->scope->flag)) 240 environ_put(global_environ, $2, ENVIRON_HIDDEN); 241 free($2); 242 } 243 244 if_open : IF expanded 245 { 246 struct cmd_parse_state *ps = &parse_state; 247 struct cmd_parse_scope *scope; 248 249 scope = xmalloc(sizeof *scope); 250 $$ = scope->flag = format_true($2); 251 free($2); 252 253 if (ps->scope != NULL) 254 TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry); 255 ps->scope = scope; 256 } 257 258 if_else : ELSE 259 { 260 struct cmd_parse_state *ps = &parse_state; 261 struct cmd_parse_scope *scope; 262 263 scope = xmalloc(sizeof *scope); 264 scope->flag = !ps->scope->flag; 265 266 free(ps->scope); 267 ps->scope = scope; 268 } 269 270 if_elif : ELIF expanded 271 { 272 struct cmd_parse_state *ps = &parse_state; 273 struct cmd_parse_scope *scope; 274 275 scope = xmalloc(sizeof *scope); 276 $$ = scope->flag = format_true($2); 277 free($2); 278 279 free(ps->scope); 280 ps->scope = scope; 281 } 282 283 if_close : ENDIF 284 { 285 struct cmd_parse_state *ps = &parse_state; 286 287 free(ps->scope); 288 ps->scope = TAILQ_FIRST(&ps->stack); 289 if (ps->scope != NULL) 290 TAILQ_REMOVE(&ps->stack, ps->scope, entry); 291 } 292 293 condition : if_open '\n' statements if_close 294 { 295 if ($1) 296 $$ = $3; 297 else { 298 $$ = cmd_parse_new_commands(); 299 cmd_parse_free_commands($3); 300 } 301 } 302 | if_open '\n' statements if_else '\n' statements if_close 303 { 304 if ($1) { 305 $$ = $3; 306 cmd_parse_free_commands($6); 307 } else { 308 $$ = $6; 309 cmd_parse_free_commands($3); 310 } 311 } 312 | if_open '\n' statements elif if_close 313 { 314 if ($1) { 315 $$ = $3; 316 cmd_parse_free_commands($4.commands); 317 } else if ($4.flag) { 318 $$ = $4.commands; 319 cmd_parse_free_commands($3); 320 } else { 321 $$ = cmd_parse_new_commands(); 322 cmd_parse_free_commands($3); 323 cmd_parse_free_commands($4.commands); 324 } 325 } 326 | if_open '\n' statements elif if_else '\n' statements if_close 327 { 328 if ($1) { 329 $$ = $3; 330 cmd_parse_free_commands($4.commands); 331 cmd_parse_free_commands($7); 332 } else if ($4.flag) { 333 $$ = $4.commands; 334 cmd_parse_free_commands($3); 335 cmd_parse_free_commands($7); 336 } else { 337 $$ = $7; 338 cmd_parse_free_commands($3); 339 cmd_parse_free_commands($4.commands); 340 } 341 } 342 343 elif : if_elif '\n' statements 344 { 345 if ($1) { 346 $$.flag = 1; 347 $$.commands = $3; 348 } else { 349 $$.flag = 0; 350 $$.commands = cmd_parse_new_commands(); 351 cmd_parse_free_commands($3); 352 } 353 } 354 | if_elif '\n' statements elif 355 { 356 if ($1) { 357 $$.flag = 1; 358 $$.commands = $3; 359 cmd_parse_free_commands($4.commands); 360 } else if ($4.flag) { 361 $$.flag = 1; 362 $$.commands = $4.commands; 363 cmd_parse_free_commands($3); 364 } else { 365 $$.flag = 0; 366 $$.commands = cmd_parse_new_commands(); 367 cmd_parse_free_commands($3); 368 cmd_parse_free_commands($4.commands); 369 } 370 } 371 372 commands : command 373 { 374 struct cmd_parse_state *ps = &parse_state; 375 376 $$ = cmd_parse_new_commands(); 377 if (!TAILQ_EMPTY(&$1->arguments) && 378 (ps->scope == NULL || ps->scope->flag)) 379 TAILQ_INSERT_TAIL($$, $1, entry); 380 else 381 cmd_parse_free_command($1); 382 } 383 | commands ';' 384 { 385 $$ = $1; 386 } 387 | commands ';' condition1 388 { 389 $$ = $1; 390 TAILQ_CONCAT($$, $3, entry); 391 free($3); 392 } 393 | commands ';' command 394 { 395 struct cmd_parse_state *ps = &parse_state; 396 397 if (!TAILQ_EMPTY(&$3->arguments) && 398 (ps->scope == NULL || ps->scope->flag)) { 399 $$ = $1; 400 TAILQ_INSERT_TAIL($$, $3, entry); 401 } else { 402 $$ = cmd_parse_new_commands(); 403 cmd_parse_free_commands($1); 404 cmd_parse_free_command($3); 405 } 406 } 407 | condition1 408 { 409 $$ = $1; 410 } 411 412 command : assignment 413 { 414 struct cmd_parse_state *ps = &parse_state; 415 416 $$ = xcalloc(1, sizeof *$$); 417 $$->line = ps->input->line; 418 TAILQ_INIT(&$$->arguments); 419 } 420 | optional_assignment TOKEN 421 { 422 struct cmd_parse_state *ps = &parse_state; 423 struct cmd_parse_argument *arg; 424 425 $$ = xcalloc(1, sizeof *$$); 426 $$->line = ps->input->line; 427 TAILQ_INIT(&$$->arguments); 428 429 arg = xcalloc(1, sizeof *arg); 430 arg->type = CMD_PARSE_STRING; 431 arg->string = $2; 432 TAILQ_INSERT_HEAD(&$$->arguments, arg, entry); 433 } 434 | optional_assignment TOKEN arguments 435 { 436 struct cmd_parse_state *ps = &parse_state; 437 struct cmd_parse_argument *arg; 438 439 $$ = xcalloc(1, sizeof *$$); 440 $$->line = ps->input->line; 441 TAILQ_INIT(&$$->arguments); 442 443 TAILQ_CONCAT(&$$->arguments, $3, entry); 444 free($3); 445 446 arg = xcalloc(1, sizeof *arg); 447 arg->type = CMD_PARSE_STRING; 448 arg->string = $2; 449 TAILQ_INSERT_HEAD(&$$->arguments, arg, entry); 450 } 451 452 condition1 : if_open commands if_close 453 { 454 if ($1) 455 $$ = $2; 456 else { 457 $$ = cmd_parse_new_commands(); 458 cmd_parse_free_commands($2); 459 } 460 } 461 | if_open commands if_else commands if_close 462 { 463 if ($1) { 464 $$ = $2; 465 cmd_parse_free_commands($4); 466 } else { 467 $$ = $4; 468 cmd_parse_free_commands($2); 469 } 470 } 471 | if_open commands elif1 if_close 472 { 473 if ($1) { 474 $$ = $2; 475 cmd_parse_free_commands($3.commands); 476 } else if ($3.flag) { 477 $$ = $3.commands; 478 cmd_parse_free_commands($2); 479 } else { 480 $$ = cmd_parse_new_commands(); 481 cmd_parse_free_commands($2); 482 cmd_parse_free_commands($3.commands); 483 } 484 } 485 | if_open commands elif1 if_else commands if_close 486 { 487 if ($1) { 488 $$ = $2; 489 cmd_parse_free_commands($3.commands); 490 cmd_parse_free_commands($5); 491 } else if ($3.flag) { 492 $$ = $3.commands; 493 cmd_parse_free_commands($2); 494 cmd_parse_free_commands($5); 495 } else { 496 $$ = $5; 497 cmd_parse_free_commands($2); 498 cmd_parse_free_commands($3.commands); 499 } 500 } 501 502 elif1 : if_elif commands 503 { 504 if ($1) { 505 $$.flag = 1; 506 $$.commands = $2; 507 } else { 508 $$.flag = 0; 509 $$.commands = cmd_parse_new_commands(); 510 cmd_parse_free_commands($2); 511 } 512 } 513 | if_elif commands elif1 514 { 515 if ($1) { 516 $$.flag = 1; 517 $$.commands = $2; 518 cmd_parse_free_commands($3.commands); 519 } else if ($3.flag) { 520 $$.flag = 1; 521 $$.commands = $3.commands; 522 cmd_parse_free_commands($2); 523 } else { 524 $$.flag = 0; 525 $$.commands = cmd_parse_new_commands(); 526 cmd_parse_free_commands($2); 527 cmd_parse_free_commands($3.commands); 528 } 529 } 530 531 arguments : argument 532 { 533 $$ = xcalloc(1, sizeof *$$); 534 TAILQ_INIT($$); 535 536 TAILQ_INSERT_HEAD($$, $1, entry); 537 } 538 | argument arguments 539 { 540 TAILQ_INSERT_HEAD($2, $1, entry); 541 $$ = $2; 542 } 543 544 argument : TOKEN 545 { 546 $$ = xcalloc(1, sizeof *$$); 547 $$->type = CMD_PARSE_STRING; 548 $$->string = $1; 549 } 550 | EQUALS 551 { 552 $$ = xcalloc(1, sizeof *$$); 553 $$->type = CMD_PARSE_STRING; 554 $$->string = $1; 555 } 556 | '{' argument_statements 557 { 558 $$ = xcalloc(1, sizeof *$$); 559 $$->type = CMD_PARSE_COMMANDS; 560 $$->commands = $2; 561 } 562 563 argument_statements : statement '}' 564 { 565 $$ = $1; 566 } 567 | statements statement '}' 568 { 569 $$ = $1; 570 TAILQ_CONCAT($$, $2, entry); 571 free($2); 572 } 573 574 %% 575 576 static char * 577 cmd_parse_get_error(const char *file, u_int line, const char *error) 578 { 579 char *s; 580 581 if (file == NULL) 582 s = xstrdup(error); 583 else 584 xasprintf(&s, "%s:%u: %s", file, line, error); 585 return (s); 586 } 587 588 static void 589 cmd_parse_print_commands(struct cmd_parse_input *pi, struct cmd_list *cmdlist) 590 { 591 char *s; 592 593 if (pi->item == NULL || (~pi->flags & CMD_PARSE_VERBOSE)) 594 return; 595 s = cmd_list_print(cmdlist, 0); 596 if (pi->file != NULL) 597 cmdq_print(pi->item, "%s:%u: %s", pi->file, pi->line, s); 598 else 599 cmdq_print(pi->item, "%u: %s", pi->line, s); 600 free(s); 601 } 602 603 static void 604 cmd_parse_free_argument(struct cmd_parse_argument *arg) 605 { 606 switch (arg->type) { 607 case CMD_PARSE_STRING: 608 free(arg->string); 609 break; 610 case CMD_PARSE_COMMANDS: 611 cmd_parse_free_commands(arg->commands); 612 break; 613 case CMD_PARSE_PARSED_COMMANDS: 614 cmd_list_free(arg->cmdlist); 615 break; 616 } 617 free(arg); 618 } 619 620 static void 621 cmd_parse_free_arguments(struct cmd_parse_arguments *args) 622 { 623 struct cmd_parse_argument *arg, *arg1; 624 625 TAILQ_FOREACH_SAFE(arg, args, entry, arg1) { 626 TAILQ_REMOVE(args, arg, entry); 627 cmd_parse_free_argument(arg); 628 } 629 } 630 631 static void 632 cmd_parse_free_command(struct cmd_parse_command *cmd) 633 { 634 cmd_parse_free_arguments(&cmd->arguments); 635 free(cmd); 636 } 637 638 static struct cmd_parse_commands * 639 cmd_parse_new_commands(void) 640 { 641 struct cmd_parse_commands *cmds; 642 643 cmds = xmalloc(sizeof *cmds); 644 TAILQ_INIT(cmds); 645 return (cmds); 646 } 647 648 static void 649 cmd_parse_free_commands(struct cmd_parse_commands *cmds) 650 { 651 struct cmd_parse_command *cmd, *cmd1; 652 653 TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) { 654 TAILQ_REMOVE(cmds, cmd, entry); 655 cmd_parse_free_command(cmd); 656 } 657 free(cmds); 658 } 659 660 static struct cmd_parse_commands * 661 cmd_parse_run_parser(char **cause) 662 { 663 struct cmd_parse_state *ps = &parse_state; 664 struct cmd_parse_scope *scope, *scope1; 665 int retval; 666 667 ps->commands = NULL; 668 TAILQ_INIT(&ps->stack); 669 670 retval = yyparse(); 671 TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) { 672 TAILQ_REMOVE(&ps->stack, scope, entry); 673 free(scope); 674 } 675 if (retval != 0) { 676 *cause = ps->error; 677 return (NULL); 678 } 679 680 if (ps->commands == NULL) 681 return (cmd_parse_new_commands()); 682 return (ps->commands); 683 } 684 685 static struct cmd_parse_commands * 686 cmd_parse_do_file(FILE *f, struct cmd_parse_input *pi, char **cause) 687 { 688 struct cmd_parse_state *ps = &parse_state; 689 690 memset(ps, 0, sizeof *ps); 691 ps->input = pi; 692 ps->f = f; 693 return (cmd_parse_run_parser(cause)); 694 } 695 696 static struct cmd_parse_commands * 697 cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi, 698 char **cause) 699 { 700 struct cmd_parse_state *ps = &parse_state; 701 702 memset(ps, 0, sizeof *ps); 703 ps->input = pi; 704 ps->buf = buf; 705 ps->len = len; 706 return (cmd_parse_run_parser(cause)); 707 } 708 709 static void 710 cmd_parse_log_commands(struct cmd_parse_commands *cmds, const char *prefix) 711 { 712 struct cmd_parse_command *cmd; 713 struct cmd_parse_argument *arg; 714 u_int i, j; 715 char *s; 716 717 i = 0; 718 TAILQ_FOREACH(cmd, cmds, entry) { 719 j = 0; 720 TAILQ_FOREACH(arg, &cmd->arguments, entry) { 721 switch (arg->type) { 722 case CMD_PARSE_STRING: 723 log_debug("%s %u:%u: %s", prefix, i, j, 724 arg->string); 725 break; 726 case CMD_PARSE_COMMANDS: 727 xasprintf(&s, "%s %u:%u", prefix, i, j); 728 cmd_parse_log_commands(arg->commands, s); 729 free(s); 730 break; 731 case CMD_PARSE_PARSED_COMMANDS: 732 s = cmd_list_print(arg->cmdlist, 0); 733 log_debug("%s %u:%u: %s", prefix, i, j, s); 734 free(s); 735 break; 736 } 737 j++; 738 } 739 i++; 740 } 741 } 742 743 static int 744 cmd_parse_expand_alias(struct cmd_parse_command *cmd, 745 struct cmd_parse_input *pi, struct cmd_parse_result *pr) 746 { 747 struct cmd_parse_argument *arg, *arg1, *first; 748 struct cmd_parse_commands *cmds; 749 struct cmd_parse_command *last; 750 char *alias, *name, *cause; 751 752 memset(pr, 0, sizeof *pr); 753 754 first = TAILQ_FIRST(&cmd->arguments); 755 if (first == NULL || first->type != CMD_PARSE_STRING) { 756 pr->status = CMD_PARSE_SUCCESS; 757 pr->cmdlist = cmd_list_new(); 758 return (1); 759 } 760 name = first->string; 761 762 alias = cmd_get_alias(name); 763 if (alias == NULL) 764 return (0); 765 log_debug("%s: %u alias %s = %s", __func__, pi->line, name, alias); 766 767 cmds = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause); 768 free(alias); 769 if (cmds == NULL) { 770 pr->status = CMD_PARSE_ERROR; 771 pr->error = cause; 772 return (1); 773 } 774 775 last = TAILQ_LAST(cmds, cmd_parse_commands); 776 if (last == NULL) { 777 pr->status = CMD_PARSE_SUCCESS; 778 pr->cmdlist = cmd_list_new(); 779 return (1); 780 } 781 782 TAILQ_REMOVE(&cmd->arguments, first, entry); 783 cmd_parse_free_argument(first); 784 785 TAILQ_FOREACH_SAFE(arg, &cmd->arguments, entry, arg1) { 786 TAILQ_REMOVE(&cmd->arguments, arg, entry); 787 TAILQ_INSERT_TAIL(&last->arguments, arg, entry); 788 } 789 cmd_parse_log_commands(cmds, __func__); 790 791 cmd_parse_build_commands(cmds, pi, pr); 792 return (1); 793 } 794 795 static void 796 cmd_parse_build_command(struct cmd_parse_command *cmd, 797 struct cmd_parse_input *pi, struct cmd_parse_result *pr) 798 { 799 struct cmd_parse_argument *arg; 800 struct cmd *add; 801 char *cause; 802 struct args_value *values = NULL; 803 u_int count = 0, idx; 804 805 memset(pr, 0, sizeof *pr); 806 807 if (cmd_parse_expand_alias(cmd, pi, pr)) 808 return; 809 810 TAILQ_FOREACH(arg, &cmd->arguments, entry) { 811 values = xrecallocarray(values, count, count + 1, 812 sizeof *values); 813 switch (arg->type) { 814 case CMD_PARSE_STRING: 815 values[count].type = ARGS_STRING; 816 values[count].string = xstrdup(arg->string); 817 break; 818 case CMD_PARSE_COMMANDS: 819 cmd_parse_build_commands(arg->commands, pi, pr); 820 if (pr->status != CMD_PARSE_SUCCESS) 821 goto out; 822 values[count].type = ARGS_COMMANDS; 823 values[count].cmdlist = pr->cmdlist; 824 break; 825 case CMD_PARSE_PARSED_COMMANDS: 826 values[count].type = ARGS_COMMANDS; 827 values[count].cmdlist = arg->cmdlist; 828 values[count].cmdlist->references++; 829 break; 830 } 831 count++; 832 } 833 834 add = cmd_parse(values, count, pi->file, pi->line, &cause); 835 if (add == NULL) { 836 pr->status = CMD_PARSE_ERROR; 837 pr->error = cmd_parse_get_error(pi->file, pi->line, cause); 838 free(cause); 839 goto out; 840 } 841 pr->status = CMD_PARSE_SUCCESS; 842 pr->cmdlist = cmd_list_new(); 843 cmd_list_append(pr->cmdlist, add); 844 845 out: 846 for (idx = 0; idx < count; idx++) 847 args_free_value(&values[idx]); 848 free(values); 849 } 850 851 static void 852 cmd_parse_build_commands(struct cmd_parse_commands *cmds, 853 struct cmd_parse_input *pi, struct cmd_parse_result *pr) 854 { 855 struct cmd_parse_command *cmd; 856 u_int line = UINT_MAX; 857 struct cmd_list *current = NULL, *result; 858 char *s; 859 860 memset(pr, 0, sizeof *pr); 861 862 /* Check for an empty list. */ 863 if (TAILQ_EMPTY(cmds)) { 864 pr->status = CMD_PARSE_SUCCESS; 865 pr->cmdlist = cmd_list_new(); 866 return; 867 } 868 cmd_parse_log_commands(cmds, __func__); 869 870 /* 871 * Parse each command into a command list. Create a new command list 872 * for each line (unless the flag is set) so they get a new group (so 873 * the queue knows which ones to remove if a command fails when 874 * executed). 875 */ 876 result = cmd_list_new(); 877 TAILQ_FOREACH(cmd, cmds, entry) { 878 if (((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) { 879 if (current != NULL) { 880 cmd_parse_print_commands(pi, current); 881 cmd_list_move(result, current); 882 cmd_list_free(current); 883 } 884 current = cmd_list_new(); 885 } 886 if (current == NULL) 887 current = cmd_list_new(); 888 line = pi->line = cmd->line; 889 890 cmd_parse_build_command(cmd, pi, pr); 891 if (pr->status != CMD_PARSE_SUCCESS) { 892 cmd_list_free(result); 893 cmd_list_free(current); 894 return; 895 } 896 cmd_list_append_all(current, pr->cmdlist); 897 cmd_list_free(pr->cmdlist); 898 } 899 if (current != NULL) { 900 cmd_parse_print_commands(pi, current); 901 cmd_list_move(result, current); 902 cmd_list_free(current); 903 } 904 905 s = cmd_list_print(result, 0); 906 log_debug("%s: %s", __func__, s); 907 free(s); 908 909 pr->status = CMD_PARSE_SUCCESS; 910 pr->cmdlist = result; 911 } 912 913 struct cmd_parse_result * 914 cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi) 915 { 916 static struct cmd_parse_result pr; 917 struct cmd_parse_input input; 918 struct cmd_parse_commands *cmds; 919 char *cause; 920 921 if (pi == NULL) { 922 memset(&input, 0, sizeof input); 923 pi = &input; 924 } 925 memset(&pr, 0, sizeof pr); 926 927 cmds = cmd_parse_do_file(f, pi, &cause); 928 if (cmds == NULL) { 929 pr.status = CMD_PARSE_ERROR; 930 pr.error = cause; 931 return (&pr); 932 } 933 cmd_parse_build_commands(cmds, pi, &pr); 934 cmd_parse_free_commands(cmds); 935 return (&pr); 936 937 } 938 939 struct cmd_parse_result * 940 cmd_parse_from_string(const char *s, struct cmd_parse_input *pi) 941 { 942 struct cmd_parse_input input; 943 944 if (pi == NULL) { 945 memset(&input, 0, sizeof input); 946 pi = &input; 947 } 948 949 /* 950 * When parsing a string, put commands in one group even if there are 951 * multiple lines. This means { a \n b } is identical to "a ; b" when 952 * given as an argument to another command. 953 */ 954 pi->flags |= CMD_PARSE_ONEGROUP; 955 return (cmd_parse_from_buffer(s, strlen(s), pi)); 956 } 957 958 enum cmd_parse_status 959 cmd_parse_and_insert(const char *s, struct cmd_parse_input *pi, 960 struct cmdq_item *after, struct cmdq_state *state, char **error) 961 { 962 struct cmd_parse_result *pr; 963 struct cmdq_item *item; 964 965 pr = cmd_parse_from_string(s, pi); 966 switch (pr->status) { 967 case CMD_PARSE_ERROR: 968 if (error != NULL) 969 *error = pr->error; 970 else 971 free(pr->error); 972 break; 973 case CMD_PARSE_SUCCESS: 974 item = cmdq_get_command(pr->cmdlist, state); 975 cmdq_insert_after(after, item); 976 cmd_list_free(pr->cmdlist); 977 break; 978 } 979 return (pr->status); 980 } 981 982 enum cmd_parse_status 983 cmd_parse_and_append(const char *s, struct cmd_parse_input *pi, 984 struct client *c, struct cmdq_state *state, char **error) 985 { 986 struct cmd_parse_result *pr; 987 struct cmdq_item *item; 988 989 pr = cmd_parse_from_string(s, pi); 990 switch (pr->status) { 991 case CMD_PARSE_ERROR: 992 if (error != NULL) 993 *error = pr->error; 994 else 995 free(pr->error); 996 break; 997 case CMD_PARSE_SUCCESS: 998 item = cmdq_get_command(pr->cmdlist, state); 999 cmdq_append(c, item); 1000 cmd_list_free(pr->cmdlist); 1001 break; 1002 } 1003 return (pr->status); 1004 } 1005 1006 struct cmd_parse_result * 1007 cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi) 1008 { 1009 static struct cmd_parse_result pr; 1010 struct cmd_parse_input input; 1011 struct cmd_parse_commands *cmds; 1012 char *cause; 1013 1014 if (pi == NULL) { 1015 memset(&input, 0, sizeof input); 1016 pi = &input; 1017 } 1018 memset(&pr, 0, sizeof pr); 1019 1020 if (len == 0) { 1021 pr.status = CMD_PARSE_SUCCESS; 1022 pr.cmdlist = cmd_list_new(); 1023 return (&pr); 1024 } 1025 1026 cmds = cmd_parse_do_buffer(buf, len, pi, &cause); 1027 if (cmds == NULL) { 1028 pr.status = CMD_PARSE_ERROR; 1029 pr.error = cause; 1030 return (&pr); 1031 } 1032 cmd_parse_build_commands(cmds, pi, &pr); 1033 cmd_parse_free_commands(cmds); 1034 return (&pr); 1035 } 1036 1037 struct cmd_parse_result * 1038 cmd_parse_from_arguments(struct args_value *values, u_int count, 1039 struct cmd_parse_input *pi) 1040 { 1041 static struct cmd_parse_result pr; 1042 struct cmd_parse_input input; 1043 struct cmd_parse_commands *cmds; 1044 struct cmd_parse_command *cmd; 1045 struct cmd_parse_argument *arg; 1046 u_int i; 1047 char *copy; 1048 size_t size; 1049 int end; 1050 1051 /* 1052 * The commands are already split up into arguments, so just separate 1053 * into a set of commands by ';'. 1054 */ 1055 1056 if (pi == NULL) { 1057 memset(&input, 0, sizeof input); 1058 pi = &input; 1059 } 1060 memset(&pr, 0, sizeof pr); 1061 1062 cmds = cmd_parse_new_commands(); 1063 1064 cmd = xcalloc(1, sizeof *cmd); 1065 cmd->line = pi->line; 1066 TAILQ_INIT(&cmd->arguments); 1067 1068 for (i = 0; i < count; i++) { 1069 end = 0; 1070 if (values[i].type == ARGS_STRING) { 1071 copy = xstrdup(values[i].string); 1072 size = strlen(copy); 1073 if (size != 0 && copy[size - 1] == ';') { 1074 copy[--size] = '\0'; 1075 if (size > 0 && copy[size - 1] == '\\') 1076 copy[size - 1] = ';'; 1077 else 1078 end = 1; 1079 } 1080 if (!end || size != 0) { 1081 arg = xcalloc(1, sizeof *arg); 1082 arg->type = CMD_PARSE_STRING; 1083 arg->string = copy; 1084 TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry); 1085 } 1086 } else if (values[i].type == ARGS_COMMANDS) { 1087 arg = xcalloc(1, sizeof *arg); 1088 arg->type = CMD_PARSE_PARSED_COMMANDS; 1089 arg->cmdlist = values[i].cmdlist; 1090 arg->cmdlist->references++; 1091 TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry); 1092 } else 1093 fatalx("unknown argument type"); 1094 if (end) { 1095 TAILQ_INSERT_TAIL(cmds, cmd, entry); 1096 cmd = xcalloc(1, sizeof *cmd); 1097 cmd->line = pi->line; 1098 TAILQ_INIT(&cmd->arguments); 1099 } 1100 } 1101 if (!TAILQ_EMPTY(&cmd->arguments)) 1102 TAILQ_INSERT_TAIL(cmds, cmd, entry); 1103 else 1104 free(cmd); 1105 1106 cmd_parse_build_commands(cmds, pi, &pr); 1107 cmd_parse_free_commands(cmds); 1108 return (&pr); 1109 } 1110 1111 static int printflike(1, 2) 1112 yyerror(const char *fmt, ...) 1113 { 1114 struct cmd_parse_state *ps = &parse_state; 1115 struct cmd_parse_input *pi = ps->input; 1116 va_list ap; 1117 char *error; 1118 1119 if (ps->error != NULL) 1120 return (0); 1121 1122 va_start(ap, fmt); 1123 xvasprintf(&error, fmt, ap); 1124 va_end(ap); 1125 1126 ps->error = cmd_parse_get_error(pi->file, pi->line, error); 1127 free(error); 1128 return (0); 1129 } 1130 1131 static int 1132 yylex_is_var(char ch, int first) 1133 { 1134 if (ch == '=') 1135 return (0); 1136 if (first && isdigit((u_char)ch)) 1137 return (0); 1138 return (isalnum((u_char)ch) || ch == '_'); 1139 } 1140 1141 static void 1142 yylex_append(char **buf, size_t *len, const char *add, size_t addlen) 1143 { 1144 if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen) 1145 fatalx("buffer is too big"); 1146 *buf = xrealloc(*buf, (*len) + 1 + addlen); 1147 memcpy((*buf) + *len, add, addlen); 1148 (*len) += addlen; 1149 } 1150 1151 static void 1152 yylex_append1(char **buf, size_t *len, char add) 1153 { 1154 yylex_append(buf, len, &add, 1); 1155 } 1156 1157 static int 1158 yylex_getc1(void) 1159 { 1160 struct cmd_parse_state *ps = &parse_state; 1161 int ch; 1162 1163 if (ps->f != NULL) 1164 ch = getc(ps->f); 1165 else { 1166 if (ps->off == ps->len) 1167 ch = EOF; 1168 else 1169 ch = ps->buf[ps->off++]; 1170 } 1171 return (ch); 1172 } 1173 1174 static void 1175 yylex_ungetc(int ch) 1176 { 1177 struct cmd_parse_state *ps = &parse_state; 1178 1179 if (ps->f != NULL) 1180 ungetc(ch, ps->f); 1181 else if (ps->off > 0 && ch != EOF) 1182 ps->off--; 1183 } 1184 1185 static int 1186 yylex_getc(void) 1187 { 1188 struct cmd_parse_state *ps = &parse_state; 1189 int ch; 1190 1191 if (ps->escapes != 0) { 1192 ps->escapes--; 1193 return ('\\'); 1194 } 1195 for (;;) { 1196 ch = yylex_getc1(); 1197 if (ch == '\\') { 1198 ps->escapes++; 1199 continue; 1200 } 1201 if (ch == '\n' && (ps->escapes % 2) == 1) { 1202 ps->input->line++; 1203 ps->escapes--; 1204 continue; 1205 } 1206 1207 if (ps->escapes != 0) { 1208 yylex_ungetc(ch); 1209 ps->escapes--; 1210 return ('\\'); 1211 } 1212 return (ch); 1213 } 1214 } 1215 1216 static char * 1217 yylex_get_word(int ch) 1218 { 1219 char *buf; 1220 size_t len; 1221 1222 len = 0; 1223 buf = xmalloc(1); 1224 1225 do 1226 yylex_append1(&buf, &len, ch); 1227 while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL); 1228 yylex_ungetc(ch); 1229 1230 buf[len] = '\0'; 1231 log_debug("%s: %s", __func__, buf); 1232 return (buf); 1233 } 1234 1235 static int 1236 yylex(void) 1237 { 1238 struct cmd_parse_state *ps = &parse_state; 1239 char *token, *cp; 1240 int ch, next, condition; 1241 1242 if (ps->eol) 1243 ps->input->line++; 1244 ps->eol = 0; 1245 1246 condition = ps->condition; 1247 ps->condition = 0; 1248 1249 for (;;) { 1250 ch = yylex_getc(); 1251 1252 if (ch == EOF) { 1253 /* 1254 * Ensure every file or string is terminated by a 1255 * newline. This keeps the parser simpler and avoids 1256 * having to add a newline to each string. 1257 */ 1258 if (ps->eof) 1259 break; 1260 ps->eof = 1; 1261 return ('\n'); 1262 } 1263 1264 if (ch == ' ' || ch == '\t') { 1265 /* 1266 * Ignore whitespace. 1267 */ 1268 continue; 1269 } 1270 1271 if (ch == '\n') { 1272 /* 1273 * End of line. Update the line number. 1274 */ 1275 ps->eol = 1; 1276 return ('\n'); 1277 } 1278 1279 if (ch == ';' || ch == '{' || ch == '}') { 1280 /* 1281 * A semicolon or { or } is itself. 1282 */ 1283 return (ch); 1284 } 1285 1286 if (ch == '#') { 1287 /* 1288 * #{ after a condition opens a format; anything else 1289 * is a comment, ignore up to the end of the line. 1290 */ 1291 next = yylex_getc(); 1292 if (condition && next == '{') { 1293 yylval.token = yylex_format(); 1294 if (yylval.token == NULL) 1295 return (ERROR); 1296 return (FORMAT); 1297 } 1298 while (next != '\n' && next != EOF) 1299 next = yylex_getc(); 1300 if (next == '\n') { 1301 ps->input->line++; 1302 return ('\n'); 1303 } 1304 continue; 1305 } 1306 1307 if (ch == '%') { 1308 /* 1309 * % is a condition unless it is all % or all numbers, 1310 * then it is a token. 1311 */ 1312 yylval.token = yylex_get_word('%'); 1313 for (cp = yylval.token; *cp != '\0'; cp++) { 1314 if (*cp != '%' && !isdigit((u_char)*cp)) 1315 break; 1316 } 1317 if (*cp == '\0') 1318 return (TOKEN); 1319 ps->condition = 1; 1320 if (strcmp(yylval.token, "%hidden") == 0) { 1321 free(yylval.token); 1322 return (HIDDEN); 1323 } 1324 if (strcmp(yylval.token, "%if") == 0) { 1325 free(yylval.token); 1326 return (IF); 1327 } 1328 if (strcmp(yylval.token, "%else") == 0) { 1329 free(yylval.token); 1330 return (ELSE); 1331 } 1332 if (strcmp(yylval.token, "%elif") == 0) { 1333 free(yylval.token); 1334 return (ELIF); 1335 } 1336 if (strcmp(yylval.token, "%endif") == 0) { 1337 free(yylval.token); 1338 return (ENDIF); 1339 } 1340 free(yylval.token); 1341 return (ERROR); 1342 } 1343 1344 /* 1345 * Otherwise this is a token. 1346 */ 1347 token = yylex_token(ch); 1348 if (token == NULL) 1349 return (ERROR); 1350 yylval.token = token; 1351 1352 if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) { 1353 for (cp = token + 1; *cp != '='; cp++) { 1354 if (!yylex_is_var(*cp, 0)) 1355 break; 1356 } 1357 if (*cp == '=') 1358 return (EQUALS); 1359 } 1360 return (TOKEN); 1361 } 1362 return (0); 1363 } 1364 1365 static char * 1366 yylex_format(void) 1367 { 1368 char *buf; 1369 size_t len; 1370 int ch, brackets = 1; 1371 1372 len = 0; 1373 buf = xmalloc(1); 1374 1375 yylex_append(&buf, &len, "#{", 2); 1376 for (;;) { 1377 if ((ch = yylex_getc()) == EOF || ch == '\n') 1378 goto error; 1379 if (ch == '#') { 1380 if ((ch = yylex_getc()) == EOF || ch == '\n') 1381 goto error; 1382 if (ch == '{') 1383 brackets++; 1384 yylex_append1(&buf, &len, '#'); 1385 } else if (ch == '}') { 1386 if (brackets != 0 && --brackets == 0) { 1387 yylex_append1(&buf, &len, ch); 1388 break; 1389 } 1390 } 1391 yylex_append1(&buf, &len, ch); 1392 } 1393 if (brackets != 0) 1394 goto error; 1395 1396 buf[len] = '\0'; 1397 log_debug("%s: %s", __func__, buf); 1398 return (buf); 1399 1400 error: 1401 free(buf); 1402 return (NULL); 1403 } 1404 1405 static int 1406 yylex_token_escape(char **buf, size_t *len) 1407 { 1408 int ch, type, o2, o3, mlen; 1409 u_int size, i, tmp; 1410 char s[9], m[MB_LEN_MAX]; 1411 1412 ch = yylex_getc(); 1413 1414 if (ch >= '4' && ch <= '7') { 1415 yyerror("invalid octal escape"); 1416 return (0); 1417 } 1418 if (ch >= '0' && ch <= '3') { 1419 o2 = yylex_getc(); 1420 if (o2 >= '0' && o2 <= '7') { 1421 o3 = yylex_getc(); 1422 if (o3 >= '0' && o3 <= '7') { 1423 ch = 64 * (ch - '0') + 1424 8 * (o2 - '0') + 1425 (o3 - '0'); 1426 yylex_append1(buf, len, ch); 1427 return (1); 1428 } 1429 } 1430 yyerror("invalid octal escape"); 1431 return (0); 1432 } 1433 1434 switch (ch) { 1435 case EOF: 1436 return (0); 1437 case 'a': 1438 ch = '\a'; 1439 break; 1440 case 'b': 1441 ch = '\b'; 1442 break; 1443 case 'e': 1444 ch = '\033'; 1445 break; 1446 case 'f': 1447 ch = '\f'; 1448 break; 1449 case 's': 1450 ch = ' '; 1451 break; 1452 case 'v': 1453 ch = '\v'; 1454 break; 1455 case 'r': 1456 ch = '\r'; 1457 break; 1458 case 'n': 1459 ch = '\n'; 1460 break; 1461 case 't': 1462 ch = '\t'; 1463 break; 1464 case 'u': 1465 type = 'u'; 1466 size = 4; 1467 goto unicode; 1468 case 'U': 1469 type = 'U'; 1470 size = 8; 1471 goto unicode; 1472 } 1473 1474 yylex_append1(buf, len, ch); 1475 return (1); 1476 1477 unicode: 1478 for (i = 0; i < size; i++) { 1479 ch = yylex_getc(); 1480 if (ch == EOF || ch == '\n') 1481 return (0); 1482 if (!isxdigit((u_char)ch)) { 1483 yyerror("invalid \\%c argument", type); 1484 return (0); 1485 } 1486 s[i] = ch; 1487 } 1488 s[i] = '\0'; 1489 1490 if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) || 1491 (size == 8 && sscanf(s, "%8x", &tmp) != 1)) { 1492 yyerror("invalid \\%c argument", type); 1493 return (0); 1494 } 1495 mlen = wctomb(m, tmp); 1496 if (mlen <= 0 || mlen > (int)sizeof m) { 1497 yyerror("invalid \\%c argument", type); 1498 return (0); 1499 } 1500 yylex_append(buf, len, m, mlen); 1501 return (1); 1502 } 1503 1504 static int 1505 yylex_token_variable(char **buf, size_t *len) 1506 { 1507 struct environ_entry *envent; 1508 int ch, brackets = 0; 1509 char name[1024]; 1510 size_t namelen = 0; 1511 const char *value; 1512 1513 ch = yylex_getc(); 1514 if (ch == EOF) 1515 return (0); 1516 if (ch == '{') 1517 brackets = 1; 1518 else { 1519 if (!yylex_is_var(ch, 1)) { 1520 yylex_append1(buf, len, '$'); 1521 yylex_ungetc(ch); 1522 return (1); 1523 } 1524 name[namelen++] = ch; 1525 } 1526 1527 for (;;) { 1528 ch = yylex_getc(); 1529 if (brackets && ch == '}') 1530 break; 1531 if (ch == EOF || !yylex_is_var(ch, 0)) { 1532 if (!brackets) { 1533 yylex_ungetc(ch); 1534 break; 1535 } 1536 yyerror("invalid environment variable"); 1537 return (0); 1538 } 1539 if (namelen == (sizeof name) - 2) { 1540 yyerror("environment variable is too long"); 1541 return (0); 1542 } 1543 name[namelen++] = ch; 1544 } 1545 name[namelen] = '\0'; 1546 1547 envent = environ_find(global_environ, name); 1548 if (envent != NULL && envent->value != NULL) { 1549 value = envent->value; 1550 log_debug("%s: %s -> %s", __func__, name, value); 1551 yylex_append(buf, len, value, strlen(value)); 1552 } 1553 return (1); 1554 } 1555 1556 static int 1557 yylex_token_tilde(char **buf, size_t *len) 1558 { 1559 struct environ_entry *envent; 1560 int ch; 1561 char name[1024]; 1562 size_t namelen = 0; 1563 struct passwd *pw; 1564 const char *home = NULL; 1565 1566 for (;;) { 1567 ch = yylex_getc(); 1568 if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) { 1569 yylex_ungetc(ch); 1570 break; 1571 } 1572 if (namelen == (sizeof name) - 2) { 1573 yyerror("user name is too long"); 1574 return (0); 1575 } 1576 name[namelen++] = ch; 1577 } 1578 name[namelen] = '\0'; 1579 1580 if (*name == '\0') { 1581 envent = environ_find(global_environ, "HOME"); 1582 if (envent != NULL && *envent->value != '\0') 1583 home = envent->value; 1584 else if ((pw = getpwuid(getuid())) != NULL) 1585 home = pw->pw_dir; 1586 } else { 1587 if ((pw = getpwnam(name)) != NULL) 1588 home = pw->pw_dir; 1589 } 1590 if (home == NULL) 1591 return (0); 1592 1593 log_debug("%s: ~%s -> %s", __func__, name, home); 1594 yylex_append(buf, len, home, strlen(home)); 1595 return (1); 1596 } 1597 1598 static char * 1599 yylex_token(int ch) 1600 { 1601 char *buf; 1602 size_t len; 1603 enum { START, 1604 NONE, 1605 DOUBLE_QUOTES, 1606 SINGLE_QUOTES } state = NONE, last = START; 1607 1608 len = 0; 1609 buf = xmalloc(1); 1610 1611 for (;;) { 1612 /* EOF or \n are always the end of the token. */ 1613 if (ch == EOF || (state == NONE && ch == '\n')) 1614 break; 1615 1616 /* Whitespace or ; or } ends a token unless inside quotes. */ 1617 if ((ch == ' ' || ch == '\t' || ch == ';' || ch == '}') && 1618 state == NONE) 1619 break; 1620 1621 /* 1622 * Spaces and comments inside quotes after \n are removed but 1623 * the \n is left. 1624 */ 1625 if (ch == '\n' && state != NONE) { 1626 yylex_append1(&buf, &len, '\n'); 1627 while ((ch = yylex_getc()) == ' ' || ch == '\t') 1628 /* nothing */; 1629 if (ch != '#') 1630 continue; 1631 ch = yylex_getc(); 1632 if (strchr(",#{}:", ch) != NULL) { 1633 yylex_ungetc(ch); 1634 ch = '#'; 1635 } else { 1636 while ((ch = yylex_getc()) != '\n' && ch != EOF) 1637 /* nothing */; 1638 } 1639 continue; 1640 } 1641 1642 /* \ ~ and $ are expanded except in single quotes. */ 1643 if (ch == '\\' && state != SINGLE_QUOTES) { 1644 if (!yylex_token_escape(&buf, &len)) 1645 goto error; 1646 goto skip; 1647 } 1648 if (ch == '~' && last != state && state != SINGLE_QUOTES) { 1649 if (!yylex_token_tilde(&buf, &len)) 1650 goto error; 1651 goto skip; 1652 } 1653 if (ch == '$' && state != SINGLE_QUOTES) { 1654 if (!yylex_token_variable(&buf, &len)) 1655 goto error; 1656 goto skip; 1657 } 1658 if (ch == '}' && state == NONE) 1659 goto error; /* unmatched (matched ones were handled) */ 1660 1661 /* ' and " starts or end quotes (and is consumed). */ 1662 if (ch == '\'') { 1663 if (state == NONE) { 1664 state = SINGLE_QUOTES; 1665 goto next; 1666 } 1667 if (state == SINGLE_QUOTES) { 1668 state = NONE; 1669 goto next; 1670 } 1671 } 1672 if (ch == '"') { 1673 if (state == NONE) { 1674 state = DOUBLE_QUOTES; 1675 goto next; 1676 } 1677 if (state == DOUBLE_QUOTES) { 1678 state = NONE; 1679 goto next; 1680 } 1681 } 1682 1683 /* Otherwise add the character to the buffer. */ 1684 yylex_append1(&buf, &len, ch); 1685 1686 skip: 1687 last = state; 1688 1689 next: 1690 ch = yylex_getc(); 1691 } 1692 yylex_ungetc(ch); 1693 1694 buf[len] = '\0'; 1695 log_debug("%s: %s", __func__, buf); 1696 return (buf); 1697 1698 error: 1699 free(buf); 1700 return (NULL); 1701 } 1702