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