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