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