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