1/* $OpenBSD$ */ 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 33static int yylex(void); 34static int yyparse(void); 35static int printflike(1,2) yyerror(const char *, ...); 36 37static char *yylex_token(int); 38static char *yylex_format(void); 39 40struct cmd_parse_scope { 41 int flag; 42 TAILQ_ENTRY (cmd_parse_scope) entry; 43}; 44 45enum cmd_parse_argument_type { 46 CMD_PARSE_STRING, 47 CMD_PARSE_COMMANDS, 48 CMD_PARSE_PARSED_COMMANDS 49}; 50 51struct 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}; 59TAILQ_HEAD(cmd_parse_arguments, cmd_parse_argument); 60 61struct cmd_parse_command { 62 u_int line; 63 struct cmd_parse_arguments arguments; 64 65 TAILQ_ENTRY(cmd_parse_command) entry; 66}; 67TAILQ_HEAD(cmd_parse_commands, cmd_parse_command); 68 69struct 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}; 88static struct cmd_parse_state parse_state; 89 90static char *cmd_parse_get_error(const char *, u_int, const char *); 91static void cmd_parse_free_command(struct cmd_parse_command *); 92static struct cmd_parse_commands *cmd_parse_new_commands(void); 93static void cmd_parse_free_commands(struct cmd_parse_commands *); 94static void cmd_parse_build_commands(struct cmd_parse_commands *, 95 struct cmd_parse_input *, struct cmd_parse_result *); 96static 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 134lines : /* empty */ 135 | statements 136 { 137 struct cmd_parse_state *ps = &parse_state; 138 139 ps->commands = $1; 140 } 141 142statements : statement '\n' 143 { 144 $$ = $1; 145 } 146 | statements statement '\n' 147 { 148 $$ = $1; 149 TAILQ_CONCAT($$, $2, entry); 150 free($2); 151 } 152 153statement : /* 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 186format : FORMAT 187 { 188 $$ = $1; 189 } 190 | TOKEN 191 { 192 $$ = $1; 193 } 194 195expanded : 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 219optional_assignment : /* empty */ 220 | assignment 221 222assignment : 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 233hidden_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 244if_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 258if_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 270if_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 283if_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 293condition : 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 343elif : 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 372commands : 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 412command : 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 452condition1 : 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 502elif1 : 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 531arguments : 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 544argument : 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 563argument_statements : statement '}' 564 { 565 $$ = $1; 566 } 567 | statements statement '}' 568 { 569 $$ = $1; 570 TAILQ_CONCAT($$, $2, entry); 571 free($2); 572 } 573 574%% 575 576static char * 577cmd_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 588static void 589cmd_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 603static void 604cmd_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 620static void 621cmd_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 631static void 632cmd_parse_free_command(struct cmd_parse_command *cmd) 633{ 634 cmd_parse_free_arguments(&cmd->arguments); 635 free(cmd); 636} 637 638static struct cmd_parse_commands * 639cmd_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 648static void 649cmd_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 660static struct cmd_parse_commands * 661cmd_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 685static struct cmd_parse_commands * 686cmd_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 696static struct cmd_parse_commands * 697cmd_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 709static void 710cmd_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 743static int 744cmd_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 799static void 800cmd_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 849out: 850 for (idx = 0; idx < count; idx++) 851 args_free_value(&values[idx]); 852 free(values); 853} 854 855static void 856cmd_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 917struct cmd_parse_result * 918cmd_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 943struct cmd_parse_result * 944cmd_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 962enum cmd_parse_status 963cmd_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 986enum cmd_parse_status 987cmd_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 1010struct cmd_parse_result * 1011cmd_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 1041struct cmd_parse_result * 1042cmd_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 } else 1090 free(copy); 1091 } else if (values[i].type == ARGS_COMMANDS) { 1092 arg = xcalloc(1, sizeof *arg); 1093 arg->type = CMD_PARSE_PARSED_COMMANDS; 1094 arg->cmdlist = values[i].cmdlist; 1095 arg->cmdlist->references++; 1096 TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry); 1097 } else 1098 fatalx("unknown argument type"); 1099 if (end) { 1100 TAILQ_INSERT_TAIL(cmds, cmd, entry); 1101 cmd = xcalloc(1, sizeof *cmd); 1102 cmd->line = pi->line; 1103 TAILQ_INIT(&cmd->arguments); 1104 } 1105 } 1106 if (!TAILQ_EMPTY(&cmd->arguments)) 1107 TAILQ_INSERT_TAIL(cmds, cmd, entry); 1108 else 1109 free(cmd); 1110 1111 cmd_parse_build_commands(cmds, pi, &pr); 1112 cmd_parse_free_commands(cmds); 1113 return (&pr); 1114} 1115 1116static int printflike(1, 2) 1117yyerror(const char *fmt, ...) 1118{ 1119 struct cmd_parse_state *ps = &parse_state; 1120 struct cmd_parse_input *pi = ps->input; 1121 va_list ap; 1122 char *error; 1123 1124 if (ps->error != NULL) 1125 return (0); 1126 1127 va_start(ap, fmt); 1128 xvasprintf(&error, fmt, ap); 1129 va_end(ap); 1130 1131 ps->error = cmd_parse_get_error(pi->file, pi->line, error); 1132 free(error); 1133 return (0); 1134} 1135 1136static int 1137yylex_is_var(char ch, int first) 1138{ 1139 if (ch == '=') 1140 return (0); 1141 if (first && isdigit((u_char)ch)) 1142 return (0); 1143 return (isalnum((u_char)ch) || ch == '_'); 1144} 1145 1146static void 1147yylex_append(char **buf, size_t *len, const char *add, size_t addlen) 1148{ 1149 if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen) 1150 fatalx("buffer is too big"); 1151 *buf = xrealloc(*buf, (*len) + 1 + addlen); 1152 memcpy((*buf) + *len, add, addlen); 1153 (*len) += addlen; 1154} 1155 1156static void 1157yylex_append1(char **buf, size_t *len, char add) 1158{ 1159 yylex_append(buf, len, &add, 1); 1160} 1161 1162static int 1163yylex_getc1(void) 1164{ 1165 struct cmd_parse_state *ps = &parse_state; 1166 int ch; 1167 1168 if (ps->f != NULL) 1169 ch = getc(ps->f); 1170 else { 1171 if (ps->off == ps->len) 1172 ch = EOF; 1173 else 1174 ch = ps->buf[ps->off++]; 1175 } 1176 return (ch); 1177} 1178 1179static void 1180yylex_ungetc(int ch) 1181{ 1182 struct cmd_parse_state *ps = &parse_state; 1183 1184 if (ps->f != NULL) 1185 ungetc(ch, ps->f); 1186 else if (ps->off > 0 && ch != EOF) 1187 ps->off--; 1188} 1189 1190static int 1191yylex_getc(void) 1192{ 1193 struct cmd_parse_state *ps = &parse_state; 1194 int ch; 1195 1196 if (ps->escapes != 0) { 1197 ps->escapes--; 1198 return ('\\'); 1199 } 1200 for (;;) { 1201 ch = yylex_getc1(); 1202 if (ch == '\\') { 1203 ps->escapes++; 1204 continue; 1205 } 1206 if (ch == '\n' && (ps->escapes % 2) == 1) { 1207 ps->input->line++; 1208 ps->escapes--; 1209 continue; 1210 } 1211 1212 if (ps->escapes != 0) { 1213 yylex_ungetc(ch); 1214 ps->escapes--; 1215 return ('\\'); 1216 } 1217 return (ch); 1218 } 1219} 1220 1221static char * 1222yylex_get_word(int ch) 1223{ 1224 char *buf; 1225 size_t len; 1226 1227 len = 0; 1228 buf = xmalloc(1); 1229 1230 do 1231 yylex_append1(&buf, &len, ch); 1232 while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL); 1233 yylex_ungetc(ch); 1234 1235 buf[len] = '\0'; 1236 log_debug("%s: %s", __func__, buf); 1237 return (buf); 1238} 1239 1240static int 1241yylex(void) 1242{ 1243 struct cmd_parse_state *ps = &parse_state; 1244 char *token, *cp; 1245 int ch, next, condition; 1246 1247 if (ps->eol) 1248 ps->input->line++; 1249 ps->eol = 0; 1250 1251 condition = ps->condition; 1252 ps->condition = 0; 1253 1254 for (;;) { 1255 ch = yylex_getc(); 1256 1257 if (ch == EOF) { 1258 /* 1259 * Ensure every file or string is terminated by a 1260 * newline. This keeps the parser simpler and avoids 1261 * having to add a newline to each string. 1262 */ 1263 if (ps->eof) 1264 break; 1265 ps->eof = 1; 1266 return ('\n'); 1267 } 1268 1269 if (ch == ' ' || ch == '\t') { 1270 /* 1271 * Ignore whitespace. 1272 */ 1273 continue; 1274 } 1275 1276 if (ch == '\n') { 1277 /* 1278 * End of line. Update the line number. 1279 */ 1280 ps->eol = 1; 1281 return ('\n'); 1282 } 1283 1284 if (ch == ';' || ch == '{' || ch == '}') { 1285 /* 1286 * A semicolon or { or } is itself. 1287 */ 1288 return (ch); 1289 } 1290 1291 if (ch == '#') { 1292 /* 1293 * #{ after a condition opens a format; anything else 1294 * is a comment, ignore up to the end of the line. 1295 */ 1296 next = yylex_getc(); 1297 if (condition && next == '{') { 1298 yylval.token = yylex_format(); 1299 if (yylval.token == NULL) 1300 return (ERROR); 1301 return (FORMAT); 1302 } 1303 while (next != '\n' && next != EOF) 1304 next = yylex_getc(); 1305 if (next == '\n') { 1306 ps->input->line++; 1307 return ('\n'); 1308 } 1309 continue; 1310 } 1311 1312 if (ch == '%') { 1313 /* 1314 * % is a condition unless it is all % or all numbers, 1315 * then it is a token. 1316 */ 1317 yylval.token = yylex_get_word('%'); 1318 for (cp = yylval.token; *cp != '\0'; cp++) { 1319 if (*cp != '%' && !isdigit((u_char)*cp)) 1320 break; 1321 } 1322 if (*cp == '\0') 1323 return (TOKEN); 1324 ps->condition = 1; 1325 if (strcmp(yylval.token, "%hidden") == 0) { 1326 free(yylval.token); 1327 return (HIDDEN); 1328 } 1329 if (strcmp(yylval.token, "%if") == 0) { 1330 free(yylval.token); 1331 return (IF); 1332 } 1333 if (strcmp(yylval.token, "%else") == 0) { 1334 free(yylval.token); 1335 return (ELSE); 1336 } 1337 if (strcmp(yylval.token, "%elif") == 0) { 1338 free(yylval.token); 1339 return (ELIF); 1340 } 1341 if (strcmp(yylval.token, "%endif") == 0) { 1342 free(yylval.token); 1343 return (ENDIF); 1344 } 1345 free(yylval.token); 1346 return (ERROR); 1347 } 1348 1349 /* 1350 * Otherwise this is a token. 1351 */ 1352 token = yylex_token(ch); 1353 if (token == NULL) 1354 return (ERROR); 1355 yylval.token = token; 1356 1357 if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) { 1358 for (cp = token + 1; *cp != '='; cp++) { 1359 if (!yylex_is_var(*cp, 0)) 1360 break; 1361 } 1362 if (*cp == '=') 1363 return (EQUALS); 1364 } 1365 return (TOKEN); 1366 } 1367 return (0); 1368} 1369 1370static char * 1371yylex_format(void) 1372{ 1373 char *buf; 1374 size_t len; 1375 int ch, brackets = 1; 1376 1377 len = 0; 1378 buf = xmalloc(1); 1379 1380 yylex_append(&buf, &len, "#{", 2); 1381 for (;;) { 1382 if ((ch = yylex_getc()) == EOF || ch == '\n') 1383 goto error; 1384 if (ch == '#') { 1385 if ((ch = yylex_getc()) == EOF || ch == '\n') 1386 goto error; 1387 if (ch == '{') 1388 brackets++; 1389 yylex_append1(&buf, &len, '#'); 1390 } else if (ch == '}') { 1391 if (brackets != 0 && --brackets == 0) { 1392 yylex_append1(&buf, &len, ch); 1393 break; 1394 } 1395 } 1396 yylex_append1(&buf, &len, ch); 1397 } 1398 if (brackets != 0) 1399 goto error; 1400 1401 buf[len] = '\0'; 1402 log_debug("%s: %s", __func__, buf); 1403 return (buf); 1404 1405error: 1406 free(buf); 1407 return (NULL); 1408} 1409 1410static int 1411yylex_token_escape(char **buf, size_t *len) 1412{ 1413 int ch, type, o2, o3, mlen; 1414 u_int size, i, tmp; 1415 char s[9], m[MB_LEN_MAX]; 1416 1417 ch = yylex_getc(); 1418 1419 if (ch >= '4' && ch <= '7') { 1420 yyerror("invalid octal escape"); 1421 return (0); 1422 } 1423 if (ch >= '0' && ch <= '3') { 1424 o2 = yylex_getc(); 1425 if (o2 >= '0' && o2 <= '7') { 1426 o3 = yylex_getc(); 1427 if (o3 >= '0' && o3 <= '7') { 1428 ch = 64 * (ch - '0') + 1429 8 * (o2 - '0') + 1430 (o3 - '0'); 1431 yylex_append1(buf, len, ch); 1432 return (1); 1433 } 1434 } 1435 yyerror("invalid octal escape"); 1436 return (0); 1437 } 1438 1439 switch (ch) { 1440 case EOF: 1441 return (0); 1442 case 'a': 1443 ch = '\a'; 1444 break; 1445 case 'b': 1446 ch = '\b'; 1447 break; 1448 case 'e': 1449 ch = '\033'; 1450 break; 1451 case 'f': 1452 ch = '\f'; 1453 break; 1454 case 's': 1455 ch = ' '; 1456 break; 1457 case 'v': 1458 ch = '\v'; 1459 break; 1460 case 'r': 1461 ch = '\r'; 1462 break; 1463 case 'n': 1464 ch = '\n'; 1465 break; 1466 case 't': 1467 ch = '\t'; 1468 break; 1469 case 'u': 1470 type = 'u'; 1471 size = 4; 1472 goto unicode; 1473 case 'U': 1474 type = 'U'; 1475 size = 8; 1476 goto unicode; 1477 } 1478 1479 yylex_append1(buf, len, ch); 1480 return (1); 1481 1482unicode: 1483 for (i = 0; i < size; i++) { 1484 ch = yylex_getc(); 1485 if (ch == EOF || ch == '\n') 1486 return (0); 1487 if (!isxdigit((u_char)ch)) { 1488 yyerror("invalid \\%c argument", type); 1489 return (0); 1490 } 1491 s[i] = ch; 1492 } 1493 s[i] = '\0'; 1494 1495 if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) || 1496 (size == 8 && sscanf(s, "%8x", &tmp) != 1)) { 1497 yyerror("invalid \\%c argument", type); 1498 return (0); 1499 } 1500 mlen = wctomb(m, tmp); 1501 if (mlen <= 0 || mlen > (int)sizeof m) { 1502 yyerror("invalid \\%c argument", type); 1503 return (0); 1504 } 1505 yylex_append(buf, len, m, mlen); 1506 return (1); 1507} 1508 1509static int 1510yylex_token_variable(char **buf, size_t *len) 1511{ 1512 struct environ_entry *envent; 1513 int ch, brackets = 0; 1514 char name[1024]; 1515 size_t namelen = 0; 1516 const char *value; 1517 1518 ch = yylex_getc(); 1519 if (ch == EOF) 1520 return (0); 1521 if (ch == '{') 1522 brackets = 1; 1523 else { 1524 if (!yylex_is_var(ch, 1)) { 1525 yylex_append1(buf, len, '$'); 1526 yylex_ungetc(ch); 1527 return (1); 1528 } 1529 name[namelen++] = ch; 1530 } 1531 1532 for (;;) { 1533 ch = yylex_getc(); 1534 if (brackets && ch == '}') 1535 break; 1536 if (ch == EOF || !yylex_is_var(ch, 0)) { 1537 if (!brackets) { 1538 yylex_ungetc(ch); 1539 break; 1540 } 1541 yyerror("invalid environment variable"); 1542 return (0); 1543 } 1544 if (namelen == (sizeof name) - 2) { 1545 yyerror("environment variable is too long"); 1546 return (0); 1547 } 1548 name[namelen++] = ch; 1549 } 1550 name[namelen] = '\0'; 1551 1552 envent = environ_find(global_environ, name); 1553 if (envent != NULL && envent->value != NULL) { 1554 value = envent->value; 1555 log_debug("%s: %s -> %s", __func__, name, value); 1556 yylex_append(buf, len, value, strlen(value)); 1557 } 1558 return (1); 1559} 1560 1561static int 1562yylex_token_tilde(char **buf, size_t *len) 1563{ 1564 struct environ_entry *envent; 1565 int ch; 1566 char name[1024]; 1567 size_t namelen = 0; 1568 struct passwd *pw; 1569 const char *home = NULL; 1570 1571 for (;;) { 1572 ch = yylex_getc(); 1573 if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) { 1574 yylex_ungetc(ch); 1575 break; 1576 } 1577 if (namelen == (sizeof name) - 2) { 1578 yyerror("user name is too long"); 1579 return (0); 1580 } 1581 name[namelen++] = ch; 1582 } 1583 name[namelen] = '\0'; 1584 1585 if (*name == '\0') { 1586 envent = environ_find(global_environ, "HOME"); 1587 if (envent != NULL && *envent->value != '\0') 1588 home = envent->value; 1589 else if ((pw = getpwuid(getuid())) != NULL) 1590 home = pw->pw_dir; 1591 } else { 1592 if ((pw = getpwnam(name)) != NULL) 1593 home = pw->pw_dir; 1594 } 1595 if (home == NULL) 1596 return (0); 1597 1598 log_debug("%s: ~%s -> %s", __func__, name, home); 1599 yylex_append(buf, len, home, strlen(home)); 1600 return (1); 1601} 1602 1603static char * 1604yylex_token(int ch) 1605{ 1606 char *buf; 1607 size_t len; 1608 enum { START, 1609 NONE, 1610 DOUBLE_QUOTES, 1611 SINGLE_QUOTES } state = NONE, last = START; 1612 1613 len = 0; 1614 buf = xmalloc(1); 1615 1616 for (;;) { 1617 /* EOF or \n are always the end of the token. */ 1618 if (ch == EOF) { 1619 log_debug("%s: end at EOF", __func__); 1620 break; 1621 } 1622 if (state == NONE && ch == '\n') { 1623 log_debug("%s: end at EOL", __func__); 1624 break; 1625 } 1626 1627 /* Whitespace or ; or } ends a token unless inside quotes. */ 1628 if (state == NONE && (ch == ' ' || ch == '\t')) { 1629 log_debug("%s: end at WS", __func__); 1630 break; 1631 } 1632 if (state == NONE && (ch == ';' || ch == '}')) { 1633 log_debug("%s: end at %c", __func__, ch); 1634 break; 1635 } 1636 1637 /* 1638 * Spaces and comments inside quotes after \n are removed but 1639 * the \n is left. 1640 */ 1641 if (ch == '\n' && state != NONE) { 1642 yylex_append1(&buf, &len, '\n'); 1643 while ((ch = yylex_getc()) == ' ' || ch == '\t') 1644 /* nothing */; 1645 if (ch != '#') 1646 continue; 1647 ch = yylex_getc(); 1648 if (strchr(",#{}:", ch) != NULL) { 1649 yylex_ungetc(ch); 1650 ch = '#'; 1651 } else { 1652 while ((ch = yylex_getc()) != '\n' && ch != EOF) 1653 /* nothing */; 1654 } 1655 continue; 1656 } 1657 1658 /* \ ~ and $ are expanded except in single quotes. */ 1659 if (ch == '\\' && state != SINGLE_QUOTES) { 1660 if (!yylex_token_escape(&buf, &len)) 1661 goto error; 1662 goto skip; 1663 } 1664 if (ch == '~' && last != state && state != SINGLE_QUOTES) { 1665 if (!yylex_token_tilde(&buf, &len)) 1666 goto error; 1667 goto skip; 1668 } 1669 if (ch == '$' && state != SINGLE_QUOTES) { 1670 if (!yylex_token_variable(&buf, &len)) 1671 goto error; 1672 goto skip; 1673 } 1674 if (ch == '}' && state == NONE) 1675 goto error; /* unmatched (matched ones were handled) */ 1676 1677 /* ' and " starts or end quotes (and is consumed). */ 1678 if (ch == '\'') { 1679 if (state == NONE) { 1680 state = SINGLE_QUOTES; 1681 goto next; 1682 } 1683 if (state == SINGLE_QUOTES) { 1684 state = NONE; 1685 goto next; 1686 } 1687 } 1688 if (ch == '"') { 1689 if (state == NONE) { 1690 state = DOUBLE_QUOTES; 1691 goto next; 1692 } 1693 if (state == DOUBLE_QUOTES) { 1694 state = NONE; 1695 goto next; 1696 } 1697 } 1698 1699 /* Otherwise add the character to the buffer. */ 1700 yylex_append1(&buf, &len, ch); 1701 1702 skip: 1703 last = state; 1704 1705 next: 1706 ch = yylex_getc(); 1707 } 1708 yylex_ungetc(ch); 1709 1710 buf[len] = '\0'; 1711 log_debug("%s: %s", __func__, buf); 1712 return (buf); 1713 1714error: 1715 free(buf); 1716 return (NULL); 1717} 1718