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