1*79be0cc0Snicm /* $OpenBSD: cmd-parse.y,v 1.53 2025/01/13 08:58:34 nicm Exp $ */ 2df6ab229Snicm 3df6ab229Snicm /* 4df6ab229Snicm * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> 5df6ab229Snicm * 6df6ab229Snicm * Permission to use, copy, modify, and distribute this software for any 7df6ab229Snicm * purpose with or without fee is hereby granted, provided that the above 8df6ab229Snicm * copyright notice and this permission notice appear in all copies. 9df6ab229Snicm * 10df6ab229Snicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11df6ab229Snicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12df6ab229Snicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13df6ab229Snicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14df6ab229Snicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15df6ab229Snicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16df6ab229Snicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17df6ab229Snicm */ 18df6ab229Snicm 19df6ab229Snicm %{ 20df6ab229Snicm 21df6ab229Snicm #include <sys/types.h> 22df6ab229Snicm 23df6ab229Snicm #include <ctype.h> 24df6ab229Snicm #include <errno.h> 25df6ab229Snicm #include <pwd.h> 26fae03c8aSnicm #include <stdlib.h> 27df6ab229Snicm #include <string.h> 28df6ab229Snicm #include <unistd.h> 296852c63bSnicm #include <wchar.h> 30df6ab229Snicm 31df6ab229Snicm #include "tmux.h" 32df6ab229Snicm 33df6ab229Snicm static int yylex(void); 34df6ab229Snicm static int yyparse(void); 35df6ab229Snicm static int printflike(1,2) yyerror(const char *, ...); 36df6ab229Snicm 37df6ab229Snicm static char *yylex_token(int); 38df6ab229Snicm static char *yylex_format(void); 39df6ab229Snicm 40df6ab229Snicm struct cmd_parse_scope { 41df6ab229Snicm int flag; 42df6ab229Snicm TAILQ_ENTRY (cmd_parse_scope) entry; 43df6ab229Snicm }; 44df6ab229Snicm 45b6e38b61Snicm enum cmd_parse_argument_type { 46b6e38b61Snicm CMD_PARSE_STRING, 47d8b32369Snicm CMD_PARSE_COMMANDS, 48d8b32369Snicm CMD_PARSE_PARSED_COMMANDS 49b6e38b61Snicm }; 50b6e38b61Snicm 51b6e38b61Snicm struct cmd_parse_argument { 52b6e38b61Snicm enum cmd_parse_argument_type type; 53b6e38b61Snicm char *string; 54b6e38b61Snicm struct cmd_parse_commands *commands; 55d8b32369Snicm struct cmd_list *cmdlist; 56b6e38b61Snicm 57b6e38b61Snicm TAILQ_ENTRY(cmd_parse_argument) entry; 58b6e38b61Snicm }; 59b6e38b61Snicm TAILQ_HEAD(cmd_parse_arguments, cmd_parse_argument); 60b6e38b61Snicm 61df6ab229Snicm struct cmd_parse_command { 62df6ab229Snicm u_int line; 63b6e38b61Snicm struct cmd_parse_arguments arguments; 64df6ab229Snicm 65df6ab229Snicm TAILQ_ENTRY(cmd_parse_command) entry; 66df6ab229Snicm }; 67df6ab229Snicm TAILQ_HEAD(cmd_parse_commands, cmd_parse_command); 68df6ab229Snicm 69df6ab229Snicm struct cmd_parse_state { 70df6ab229Snicm FILE *f; 7173530c3cSnicm 7273530c3cSnicm const char *buf; 7373530c3cSnicm size_t len; 7473530c3cSnicm size_t off; 7573530c3cSnicm 7609ba5f57Snicm int condition; 7747394861Snicm int eol; 78df6ab229Snicm int eof; 79df6ab229Snicm struct cmd_parse_input *input; 80df6ab229Snicm u_int escapes; 81df6ab229Snicm 82df6ab229Snicm char *error; 835d24dd80Snicm struct cmd_parse_commands *commands; 84df6ab229Snicm 85df6ab229Snicm struct cmd_parse_scope *scope; 86df6ab229Snicm TAILQ_HEAD(, cmd_parse_scope) stack; 87df6ab229Snicm }; 88df6ab229Snicm static struct cmd_parse_state parse_state; 89df6ab229Snicm 90df6ab229Snicm static char *cmd_parse_get_error(const char *, u_int, const char *); 91df6ab229Snicm static void cmd_parse_free_command(struct cmd_parse_command *); 925d24dd80Snicm static struct cmd_parse_commands *cmd_parse_new_commands(void); 93df6ab229Snicm static void cmd_parse_free_commands(struct cmd_parse_commands *); 94287cddb6Snicm static void cmd_parse_build_commands(struct cmd_parse_commands *, 95287cddb6Snicm struct cmd_parse_input *, struct cmd_parse_result *); 96287cddb6Snicm static void cmd_parse_print_commands(struct cmd_parse_input *, 975304b409Snicm struct cmd_list *); 98df6ab229Snicm 99df6ab229Snicm %} 100df6ab229Snicm 101df6ab229Snicm %union 102df6ab229Snicm { 103df6ab229Snicm char *token; 104b6e38b61Snicm struct cmd_parse_arguments *arguments; 105b6e38b61Snicm struct cmd_parse_argument *argument; 106df6ab229Snicm int flag; 107df6ab229Snicm struct { 108df6ab229Snicm int flag; 1095d24dd80Snicm struct cmd_parse_commands *commands; 110df6ab229Snicm } elif; 1115d24dd80Snicm struct cmd_parse_commands *commands; 112df6ab229Snicm struct cmd_parse_command *command; 113df6ab229Snicm } 114df6ab229Snicm 115df6ab229Snicm %token ERROR 116d6f6a5d2Snicm %token HIDDEN 117df6ab229Snicm %token IF 118df6ab229Snicm %token ELSE 119df6ab229Snicm %token ELIF 120df6ab229Snicm %token ENDIF 121df6ab229Snicm %token <token> FORMAT TOKEN EQUALS 122df6ab229Snicm 123b6e38b61Snicm %type <token> expanded format 124df6ab229Snicm %type <arguments> arguments 125b6e38b61Snicm %type <argument> argument 126df6ab229Snicm %type <flag> if_open if_elif 127df6ab229Snicm %type <elif> elif elif1 1282a840c62Snicm %type <commands> argument_statements statements statement 1292a840c62Snicm %type <commands> commands condition condition1 130df6ab229Snicm %type <command> command 131df6ab229Snicm 132df6ab229Snicm %% 133df6ab229Snicm 134df6ab229Snicm lines : /* empty */ 135df6ab229Snicm | statements 136df6ab229Snicm { 137df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 138df6ab229Snicm 1395d24dd80Snicm ps->commands = $1; 140df6ab229Snicm } 141df6ab229Snicm 142df6ab229Snicm statements : statement '\n' 143df6ab229Snicm { 1445d24dd80Snicm $$ = $1; 145df6ab229Snicm } 146df6ab229Snicm | statements statement '\n' 147df6ab229Snicm { 1485d24dd80Snicm $$ = $1; 1495d24dd80Snicm TAILQ_CONCAT($$, $2, entry); 1505d24dd80Snicm free($2); 151df6ab229Snicm } 152df6ab229Snicm 153bec5bc45Snicm statement : /* empty */ 154bec5bc45Snicm { 155bec5bc45Snicm $$ = xmalloc (sizeof *$$); 156bec5bc45Snicm TAILQ_INIT($$); 157bec5bc45Snicm } 158d6f6a5d2Snicm | hidden_assignment 159d6f6a5d2Snicm { 160d6f6a5d2Snicm $$ = xmalloc (sizeof *$$); 161d6f6a5d2Snicm TAILQ_INIT($$); 162d6f6a5d2Snicm } 163bec5bc45Snicm | condition 164df6ab229Snicm { 165df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 166df6ab229Snicm 167df6ab229Snicm if (ps->scope == NULL || ps->scope->flag) 1685d24dd80Snicm $$ = $1; 1695d24dd80Snicm else { 1705d24dd80Snicm $$ = cmd_parse_new_commands(); 1715d24dd80Snicm cmd_parse_free_commands($1); 1725d24dd80Snicm } 173df6ab229Snicm } 174df6ab229Snicm | commands 175df6ab229Snicm { 176df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 177df6ab229Snicm 178df6ab229Snicm if (ps->scope == NULL || ps->scope->flag) 1795d24dd80Snicm $$ = $1; 1805d24dd80Snicm else { 1815d24dd80Snicm $$ = cmd_parse_new_commands(); 1825d24dd80Snicm cmd_parse_free_commands($1); 1835d24dd80Snicm } 184df6ab229Snicm } 185df6ab229Snicm 18609ba5f57Snicm format : FORMAT 18709ba5f57Snicm { 18809ba5f57Snicm $$ = $1; 18909ba5f57Snicm } 19009ba5f57Snicm | TOKEN 19109ba5f57Snicm { 19209ba5f57Snicm $$ = $1; 19309ba5f57Snicm } 19409ba5f57Snicm 19509ba5f57Snicm expanded : format 196df6ab229Snicm { 197df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 198df6ab229Snicm struct cmd_parse_input *pi = ps->input; 199df6ab229Snicm struct format_tree *ft; 200df6ab229Snicm struct client *c = pi->c; 201a267b926Snicm struct cmd_find_state *fsp; 202a267b926Snicm struct cmd_find_state fs; 203df6ab229Snicm int flags = FORMAT_NOJOBS; 204df6ab229Snicm 205df6ab229Snicm if (cmd_find_valid_state(&pi->fs)) 206a267b926Snicm fsp = &pi->fs; 207a267b926Snicm else { 208a267b926Snicm cmd_find_from_client(&fs, c, 0); 209a267b926Snicm fsp = &fs; 210a267b926Snicm } 211df6ab229Snicm ft = format_create(NULL, pi->item, FORMAT_NONE, flags); 212a267b926Snicm format_defaults(ft, c, fsp->s, fsp->wl, fsp->wp); 213df6ab229Snicm 214df6ab229Snicm $$ = format_expand(ft, $1); 215df6ab229Snicm format_free(ft); 216df6ab229Snicm free($1); 217df6ab229Snicm } 218df6ab229Snicm 219bec5bc45Snicm optional_assignment : /* empty */ 220bec5bc45Snicm | assignment 221bec5bc45Snicm 222bec5bc45Snicm assignment : EQUALS 223df6ab229Snicm { 224df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 225df6ab229Snicm int flags = ps->input->flags; 226bd2271cfSnicm int flag = 1; 227bd2271cfSnicm struct cmd_parse_scope *scope; 228df6ab229Snicm 229bd2271cfSnicm if (ps->scope != NULL) { 230bd2271cfSnicm flag = ps->scope->flag; 231bd2271cfSnicm TAILQ_FOREACH(scope, &ps->stack, entry) 232bd2271cfSnicm flag = flag && scope->flag; 233bd2271cfSnicm } 234bd2271cfSnicm 235bd2271cfSnicm if ((~flags & CMD_PARSE_PARSEONLY) && flag) 236d6f6a5d2Snicm environ_put(global_environ, $1, 0); 237df6ab229Snicm free($1); 238df6ab229Snicm } 239df6ab229Snicm 240d6f6a5d2Snicm hidden_assignment : HIDDEN EQUALS 241d6f6a5d2Snicm { 242d6f6a5d2Snicm struct cmd_parse_state *ps = &parse_state; 243d6f6a5d2Snicm int flags = ps->input->flags; 244bd2271cfSnicm int flag = 1; 245bd2271cfSnicm struct cmd_parse_scope *scope; 246d6f6a5d2Snicm 247bd2271cfSnicm if (ps->scope != NULL) { 248bd2271cfSnicm flag = ps->scope->flag; 249bd2271cfSnicm TAILQ_FOREACH(scope, &ps->stack, entry) 250bd2271cfSnicm flag = flag && scope->flag; 251bd2271cfSnicm } 252bd2271cfSnicm 253bd2271cfSnicm if ((~flags & CMD_PARSE_PARSEONLY) && flag) 254d6f6a5d2Snicm environ_put(global_environ, $2, ENVIRON_HIDDEN); 255d6f6a5d2Snicm free($2); 256d6f6a5d2Snicm } 257d6f6a5d2Snicm 258df6ab229Snicm if_open : IF expanded 259df6ab229Snicm { 260df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 261df6ab229Snicm struct cmd_parse_scope *scope; 262df6ab229Snicm 263df6ab229Snicm scope = xmalloc(sizeof *scope); 264df6ab229Snicm $$ = scope->flag = format_true($2); 265df6ab229Snicm free($2); 266df6ab229Snicm 267df6ab229Snicm if (ps->scope != NULL) 268df6ab229Snicm TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry); 269df6ab229Snicm ps->scope = scope; 270df6ab229Snicm } 271df6ab229Snicm 272df6ab229Snicm if_else : ELSE 273df6ab229Snicm { 274df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 275df6ab229Snicm struct cmd_parse_scope *scope; 276df6ab229Snicm 277df6ab229Snicm scope = xmalloc(sizeof *scope); 278df6ab229Snicm scope->flag = !ps->scope->flag; 279df6ab229Snicm 280df6ab229Snicm free(ps->scope); 281df6ab229Snicm ps->scope = scope; 282df6ab229Snicm } 283df6ab229Snicm 284df6ab229Snicm if_elif : ELIF expanded 285df6ab229Snicm { 286df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 287df6ab229Snicm struct cmd_parse_scope *scope; 288df6ab229Snicm 289df6ab229Snicm scope = xmalloc(sizeof *scope); 290df6ab229Snicm $$ = scope->flag = format_true($2); 291df6ab229Snicm free($2); 292df6ab229Snicm 293df6ab229Snicm free(ps->scope); 294df6ab229Snicm ps->scope = scope; 295df6ab229Snicm } 296df6ab229Snicm 297df6ab229Snicm if_close : ENDIF 298df6ab229Snicm { 299df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 300df6ab229Snicm 301df6ab229Snicm free(ps->scope); 302df6ab229Snicm ps->scope = TAILQ_FIRST(&ps->stack); 303df6ab229Snicm if (ps->scope != NULL) 304df6ab229Snicm TAILQ_REMOVE(&ps->stack, ps->scope, entry); 305df6ab229Snicm } 306df6ab229Snicm 307df6ab229Snicm condition : if_open '\n' statements if_close 308df6ab229Snicm { 309df6ab229Snicm if ($1) 3105d24dd80Snicm $$ = $3; 3115d24dd80Snicm else { 3125d24dd80Snicm $$ = cmd_parse_new_commands(); 3135d24dd80Snicm cmd_parse_free_commands($3); 3145d24dd80Snicm } 315df6ab229Snicm } 316df6ab229Snicm | if_open '\n' statements if_else '\n' statements if_close 317df6ab229Snicm { 318df6ab229Snicm if ($1) { 3195d24dd80Snicm $$ = $3; 3205d24dd80Snicm cmd_parse_free_commands($6); 321df6ab229Snicm } else { 3225d24dd80Snicm $$ = $6; 3235d24dd80Snicm cmd_parse_free_commands($3); 324df6ab229Snicm } 325df6ab229Snicm } 326df6ab229Snicm | if_open '\n' statements elif if_close 327df6ab229Snicm { 328df6ab229Snicm if ($1) { 3295d24dd80Snicm $$ = $3; 3305d24dd80Snicm cmd_parse_free_commands($4.commands); 331df6ab229Snicm } else if ($4.flag) { 3325d24dd80Snicm $$ = $4.commands; 3335d24dd80Snicm cmd_parse_free_commands($3); 334df6ab229Snicm } else { 3355d24dd80Snicm $$ = cmd_parse_new_commands(); 3365d24dd80Snicm cmd_parse_free_commands($3); 3375d24dd80Snicm cmd_parse_free_commands($4.commands); 338df6ab229Snicm } 339df6ab229Snicm } 340df6ab229Snicm | if_open '\n' statements elif if_else '\n' statements if_close 341df6ab229Snicm { 342df6ab229Snicm if ($1) { 3435d24dd80Snicm $$ = $3; 3445d24dd80Snicm cmd_parse_free_commands($4.commands); 3455d24dd80Snicm cmd_parse_free_commands($7); 346df6ab229Snicm } else if ($4.flag) { 3475d24dd80Snicm $$ = $4.commands; 3485d24dd80Snicm cmd_parse_free_commands($3); 3495d24dd80Snicm cmd_parse_free_commands($7); 350df6ab229Snicm } else { 3515d24dd80Snicm $$ = $7; 3525d24dd80Snicm cmd_parse_free_commands($3); 3535d24dd80Snicm cmd_parse_free_commands($4.commands); 354df6ab229Snicm } 355df6ab229Snicm } 356df6ab229Snicm 357df6ab229Snicm elif : if_elif '\n' statements 358df6ab229Snicm { 3595d24dd80Snicm if ($1) { 3605d24dd80Snicm $$.flag = 1; 3615d24dd80Snicm $$.commands = $3; 3625d24dd80Snicm } else { 3635d24dd80Snicm $$.flag = 0; 3645d24dd80Snicm $$.commands = cmd_parse_new_commands(); 3655d24dd80Snicm cmd_parse_free_commands($3); 3665d24dd80Snicm } 367df6ab229Snicm } 368df6ab229Snicm | if_elif '\n' statements elif 369df6ab229Snicm { 370df6ab229Snicm if ($1) { 371df6ab229Snicm $$.flag = 1; 3725d24dd80Snicm $$.commands = $3; 3735d24dd80Snicm cmd_parse_free_commands($4.commands); 3745d24dd80Snicm } else if ($4.flag) { 3755d24dd80Snicm $$.flag = 1; 3765d24dd80Snicm $$.commands = $4.commands; 3775d24dd80Snicm cmd_parse_free_commands($3); 378df6ab229Snicm } else { 3795d24dd80Snicm $$.flag = 0; 3805d24dd80Snicm $$.commands = cmd_parse_new_commands(); 3815d24dd80Snicm cmd_parse_free_commands($3); 3825d24dd80Snicm cmd_parse_free_commands($4.commands); 383df6ab229Snicm } 384df6ab229Snicm } 385df6ab229Snicm 386df6ab229Snicm commands : command 387df6ab229Snicm { 388df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 389df6ab229Snicm 3905d24dd80Snicm $$ = cmd_parse_new_commands(); 391b6e38b61Snicm if (!TAILQ_EMPTY(&$1->arguments) && 39259b819b0Snicm (ps->scope == NULL || ps->scope->flag)) 3935d24dd80Snicm TAILQ_INSERT_TAIL($$, $1, entry); 394df6ab229Snicm else 395df6ab229Snicm cmd_parse_free_command($1); 396df6ab229Snicm } 397df6ab229Snicm | commands ';' 398df6ab229Snicm { 3995d24dd80Snicm $$ = $1; 400df6ab229Snicm } 401df6ab229Snicm | commands ';' condition1 402df6ab229Snicm { 4035d24dd80Snicm $$ = $1; 4045d24dd80Snicm TAILQ_CONCAT($$, $3, entry); 4055d24dd80Snicm free($3); 406df6ab229Snicm } 407df6ab229Snicm | commands ';' command 408df6ab229Snicm { 409df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 410df6ab229Snicm 411b6e38b61Snicm if (!TAILQ_EMPTY(&$3->arguments) && 41259b819b0Snicm (ps->scope == NULL || ps->scope->flag)) { 4135d24dd80Snicm $$ = $1; 4145d24dd80Snicm TAILQ_INSERT_TAIL($$, $3, entry); 415df6ab229Snicm } else { 4165d24dd80Snicm $$ = cmd_parse_new_commands(); 4175d24dd80Snicm cmd_parse_free_commands($1); 418df6ab229Snicm cmd_parse_free_command($3); 419df6ab229Snicm } 420df6ab229Snicm } 421df6ab229Snicm | condition1 422df6ab229Snicm { 4235d24dd80Snicm $$ = $1; 424df6ab229Snicm } 425df6ab229Snicm 426bec5bc45Snicm command : assignment 427bec5bc45Snicm { 428bec5bc45Snicm struct cmd_parse_state *ps = &parse_state; 429bec5bc45Snicm 430bec5bc45Snicm $$ = xcalloc(1, sizeof *$$); 431bec5bc45Snicm $$->line = ps->input->line; 432b6e38b61Snicm TAILQ_INIT(&$$->arguments); 433bec5bc45Snicm } 434bec5bc45Snicm | optional_assignment TOKEN 435df6ab229Snicm { 436df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 437b6e38b61Snicm struct cmd_parse_argument *arg; 438df6ab229Snicm 439df6ab229Snicm $$ = xcalloc(1, sizeof *$$); 4401ab16754Snicm $$->line = ps->input->line; 441b6e38b61Snicm TAILQ_INIT(&$$->arguments); 442df6ab229Snicm 443b6e38b61Snicm arg = xcalloc(1, sizeof *arg); 444b6e38b61Snicm arg->type = CMD_PARSE_STRING; 4451d297f78Snicm arg->string = $2; 446b6e38b61Snicm TAILQ_INSERT_HEAD(&$$->arguments, arg, entry); 447df6ab229Snicm } 448bec5bc45Snicm | optional_assignment TOKEN arguments 449df6ab229Snicm { 450df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 451b6e38b61Snicm struct cmd_parse_argument *arg; 452df6ab229Snicm 453df6ab229Snicm $$ = xcalloc(1, sizeof *$$); 4541ab16754Snicm $$->line = ps->input->line; 455b6e38b61Snicm TAILQ_INIT(&$$->arguments); 456df6ab229Snicm 457b6e38b61Snicm TAILQ_CONCAT(&$$->arguments, $3, entry); 458b6e38b61Snicm free($3); 459b6e38b61Snicm 460b6e38b61Snicm arg = xcalloc(1, sizeof *arg); 461b6e38b61Snicm arg->type = CMD_PARSE_STRING; 4621d297f78Snicm arg->string = $2; 463b6e38b61Snicm TAILQ_INSERT_HEAD(&$$->arguments, arg, entry); 464df6ab229Snicm } 465df6ab229Snicm 466df6ab229Snicm condition1 : if_open commands if_close 467df6ab229Snicm { 468df6ab229Snicm if ($1) 4695d24dd80Snicm $$ = $2; 4705d24dd80Snicm else { 4715d24dd80Snicm $$ = cmd_parse_new_commands(); 4725d24dd80Snicm cmd_parse_free_commands($2); 4735d24dd80Snicm } 474df6ab229Snicm } 475df6ab229Snicm | if_open commands if_else commands if_close 476df6ab229Snicm { 477df6ab229Snicm if ($1) { 4785d24dd80Snicm $$ = $2; 4795d24dd80Snicm cmd_parse_free_commands($4); 480df6ab229Snicm } else { 4815d24dd80Snicm $$ = $4; 4825d24dd80Snicm cmd_parse_free_commands($2); 483df6ab229Snicm } 484df6ab229Snicm } 485df6ab229Snicm | if_open commands elif1 if_close 486df6ab229Snicm { 487df6ab229Snicm if ($1) { 4885d24dd80Snicm $$ = $2; 4895d24dd80Snicm cmd_parse_free_commands($3.commands); 490df6ab229Snicm } else if ($3.flag) { 4915d24dd80Snicm $$ = $3.commands; 4925d24dd80Snicm cmd_parse_free_commands($2); 493df6ab229Snicm } else { 4945d24dd80Snicm $$ = cmd_parse_new_commands(); 4955d24dd80Snicm cmd_parse_free_commands($2); 4965d24dd80Snicm cmd_parse_free_commands($3.commands); 497df6ab229Snicm } 498df6ab229Snicm } 499df6ab229Snicm | if_open commands elif1 if_else commands if_close 500df6ab229Snicm { 501df6ab229Snicm if ($1) { 5025d24dd80Snicm $$ = $2; 5035d24dd80Snicm cmd_parse_free_commands($3.commands); 5045d24dd80Snicm cmd_parse_free_commands($5); 505df6ab229Snicm } else if ($3.flag) { 5065d24dd80Snicm $$ = $3.commands; 5075d24dd80Snicm cmd_parse_free_commands($2); 5085d24dd80Snicm cmd_parse_free_commands($5); 509df6ab229Snicm } else { 5105d24dd80Snicm $$ = $5; 5115d24dd80Snicm cmd_parse_free_commands($2); 5125d24dd80Snicm cmd_parse_free_commands($3.commands); 513df6ab229Snicm } 514df6ab229Snicm } 515df6ab229Snicm 516df6ab229Snicm elif1 : if_elif commands 517df6ab229Snicm { 5185d24dd80Snicm if ($1) { 5195d24dd80Snicm $$.flag = 1; 5205d24dd80Snicm $$.commands = $2; 5215d24dd80Snicm } else { 5225d24dd80Snicm $$.flag = 0; 5235d24dd80Snicm $$.commands = cmd_parse_new_commands(); 5245d24dd80Snicm cmd_parse_free_commands($2); 5255d24dd80Snicm } 526df6ab229Snicm } 527df6ab229Snicm | if_elif commands elif1 528df6ab229Snicm { 529df6ab229Snicm if ($1) { 530df6ab229Snicm $$.flag = 1; 5315d24dd80Snicm $$.commands = $2; 5325d24dd80Snicm cmd_parse_free_commands($3.commands); 5335d24dd80Snicm } else if ($3.flag) { 5345d24dd80Snicm $$.flag = 1; 5355d24dd80Snicm $$.commands = $3.commands; 5365d24dd80Snicm cmd_parse_free_commands($2); 537df6ab229Snicm } else { 5385d24dd80Snicm $$.flag = 0; 5395d24dd80Snicm $$.commands = cmd_parse_new_commands(); 5405d24dd80Snicm cmd_parse_free_commands($2); 5415d24dd80Snicm cmd_parse_free_commands($3.commands); 542df6ab229Snicm } 543df6ab229Snicm } 544df6ab229Snicm 545df6ab229Snicm arguments : argument 546df6ab229Snicm { 547b6e38b61Snicm $$ = xcalloc(1, sizeof *$$); 548b6e38b61Snicm TAILQ_INIT($$); 549df6ab229Snicm 550b6e38b61Snicm TAILQ_INSERT_HEAD($$, $1, entry); 551df6ab229Snicm } 552df6ab229Snicm | argument arguments 553df6ab229Snicm { 554b6e38b61Snicm TAILQ_INSERT_HEAD($2, $1, entry); 555df6ab229Snicm $$ = $2; 556df6ab229Snicm } 557df6ab229Snicm 558df6ab229Snicm argument : TOKEN 559df6ab229Snicm { 560b6e38b61Snicm $$ = xcalloc(1, sizeof *$$); 561b6e38b61Snicm $$->type = CMD_PARSE_STRING; 5621d297f78Snicm $$->string = $1; 563df6ab229Snicm } 564df6ab229Snicm | EQUALS 565df6ab229Snicm { 566b6e38b61Snicm $$ = xcalloc(1, sizeof *$$); 567b6e38b61Snicm $$->type = CMD_PARSE_STRING; 5681d297f78Snicm $$->string = $1; 569df6ab229Snicm } 5702a840c62Snicm | '{' argument_statements 5712a840c62Snicm { 572b6e38b61Snicm $$ = xcalloc(1, sizeof *$$); 573b6e38b61Snicm $$->type = CMD_PARSE_COMMANDS; 574b6e38b61Snicm $$->commands = $2; 5752a840c62Snicm } 5762a840c62Snicm 5772a840c62Snicm argument_statements : statement '}' 5782a840c62Snicm { 5792a840c62Snicm $$ = $1; 5802a840c62Snicm } 58124479cbfSnicm | statements statement '}' 5822a840c62Snicm { 5832a840c62Snicm $$ = $1; 58424479cbfSnicm TAILQ_CONCAT($$, $2, entry); 58524479cbfSnicm free($2); 5862a840c62Snicm } 587df6ab229Snicm 588df6ab229Snicm %% 589df6ab229Snicm 590df6ab229Snicm static char * 591df6ab229Snicm cmd_parse_get_error(const char *file, u_int line, const char *error) 592df6ab229Snicm { 593df6ab229Snicm char *s; 594df6ab229Snicm 595df6ab229Snicm if (file == NULL) 596df6ab229Snicm s = xstrdup(error); 597df6ab229Snicm else 598df6ab229Snicm xasprintf(&s, "%s:%u: %s", file, line, error); 599df6ab229Snicm return (s); 600df6ab229Snicm } 601df6ab229Snicm 602df6ab229Snicm static void 603287cddb6Snicm cmd_parse_print_commands(struct cmd_parse_input *pi, struct cmd_list *cmdlist) 6045304b409Snicm { 6055304b409Snicm char *s; 6065304b409Snicm 607287cddb6Snicm if (pi->item == NULL || (~pi->flags & CMD_PARSE_VERBOSE)) 608287cddb6Snicm return; 6095304b409Snicm s = cmd_list_print(cmdlist, 0); 610d47c8058Snicm if (pi->file != NULL) 611287cddb6Snicm cmdq_print(pi->item, "%s:%u: %s", pi->file, pi->line, s); 612d47c8058Snicm else 613287cddb6Snicm cmdq_print(pi->item, "%u: %s", pi->line, s); 6145304b409Snicm free(s); 6155304b409Snicm } 6165304b409Snicm 6175304b409Snicm static void 618287cddb6Snicm cmd_parse_free_argument(struct cmd_parse_argument *arg) 619b6e38b61Snicm { 620b6e38b61Snicm switch (arg->type) { 621b6e38b61Snicm case CMD_PARSE_STRING: 622b6e38b61Snicm free(arg->string); 623b6e38b61Snicm break; 624b6e38b61Snicm case CMD_PARSE_COMMANDS: 625b6e38b61Snicm cmd_parse_free_commands(arg->commands); 626b6e38b61Snicm break; 627d8b32369Snicm case CMD_PARSE_PARSED_COMMANDS: 628d8b32369Snicm cmd_list_free(arg->cmdlist); 629d8b32369Snicm break; 630b6e38b61Snicm } 631b6e38b61Snicm free(arg); 632b6e38b61Snicm } 633287cddb6Snicm 634287cddb6Snicm static void 635287cddb6Snicm cmd_parse_free_arguments(struct cmd_parse_arguments *args) 636287cddb6Snicm { 637287cddb6Snicm struct cmd_parse_argument *arg, *arg1; 638287cddb6Snicm 639287cddb6Snicm TAILQ_FOREACH_SAFE(arg, args, entry, arg1) { 640287cddb6Snicm TAILQ_REMOVE(args, arg, entry); 641287cddb6Snicm cmd_parse_free_argument(arg); 642287cddb6Snicm } 643b6e38b61Snicm } 644b6e38b61Snicm 645b6e38b61Snicm static void 646df6ab229Snicm cmd_parse_free_command(struct cmd_parse_command *cmd) 647df6ab229Snicm { 648b6e38b61Snicm cmd_parse_free_arguments(&cmd->arguments); 649df6ab229Snicm free(cmd); 650df6ab229Snicm } 651df6ab229Snicm 6525d24dd80Snicm static struct cmd_parse_commands * 6535d24dd80Snicm cmd_parse_new_commands(void) 6545d24dd80Snicm { 6555d24dd80Snicm struct cmd_parse_commands *cmds; 6565d24dd80Snicm 6575d24dd80Snicm cmds = xmalloc(sizeof *cmds); 6585d24dd80Snicm TAILQ_INIT(cmds); 6595d24dd80Snicm return (cmds); 6605d24dd80Snicm } 6615d24dd80Snicm 662df6ab229Snicm static void 663df6ab229Snicm cmd_parse_free_commands(struct cmd_parse_commands *cmds) 664df6ab229Snicm { 665df6ab229Snicm struct cmd_parse_command *cmd, *cmd1; 666df6ab229Snicm 667df6ab229Snicm TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) { 668df6ab229Snicm TAILQ_REMOVE(cmds, cmd, entry); 669df6ab229Snicm cmd_parse_free_command(cmd); 670df6ab229Snicm } 6715d24dd80Snicm free(cmds); 672df6ab229Snicm } 673df6ab229Snicm 674df6ab229Snicm static struct cmd_parse_commands * 67573530c3cSnicm cmd_parse_run_parser(char **cause) 676df6ab229Snicm { 677df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 678df6ab229Snicm struct cmd_parse_scope *scope, *scope1; 679df6ab229Snicm int retval; 680df6ab229Snicm 6815d24dd80Snicm ps->commands = NULL; 682df6ab229Snicm TAILQ_INIT(&ps->stack); 683df6ab229Snicm 684df6ab229Snicm retval = yyparse(); 685df6ab229Snicm TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) { 686df6ab229Snicm TAILQ_REMOVE(&ps->stack, scope, entry); 687df6ab229Snicm free(scope); 688df6ab229Snicm } 689df6ab229Snicm if (retval != 0) { 690df6ab229Snicm *cause = ps->error; 691df6ab229Snicm return (NULL); 692df6ab229Snicm } 693df6ab229Snicm 6945d24dd80Snicm if (ps->commands == NULL) 6955d24dd80Snicm return (cmd_parse_new_commands()); 6965d24dd80Snicm return (ps->commands); 697df6ab229Snicm } 698df6ab229Snicm 69973530c3cSnicm static struct cmd_parse_commands * 70073530c3cSnicm cmd_parse_do_file(FILE *f, struct cmd_parse_input *pi, char **cause) 70173530c3cSnicm { 70273530c3cSnicm struct cmd_parse_state *ps = &parse_state; 70373530c3cSnicm 70473530c3cSnicm memset(ps, 0, sizeof *ps); 70573530c3cSnicm ps->input = pi; 70673530c3cSnicm ps->f = f; 70773530c3cSnicm return (cmd_parse_run_parser(cause)); 70873530c3cSnicm } 70973530c3cSnicm 71073530c3cSnicm static struct cmd_parse_commands * 71173530c3cSnicm cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi, 71273530c3cSnicm char **cause) 71373530c3cSnicm { 71473530c3cSnicm struct cmd_parse_state *ps = &parse_state; 71573530c3cSnicm 71673530c3cSnicm memset(ps, 0, sizeof *ps); 71773530c3cSnicm ps->input = pi; 71873530c3cSnicm ps->buf = buf; 71973530c3cSnicm ps->len = len; 72073530c3cSnicm return (cmd_parse_run_parser(cause)); 72173530c3cSnicm } 72273530c3cSnicm 723b6e38b61Snicm static void 724b6e38b61Snicm cmd_parse_log_commands(struct cmd_parse_commands *cmds, const char *prefix) 725b6e38b61Snicm { 726b6e38b61Snicm struct cmd_parse_command *cmd; 727b6e38b61Snicm struct cmd_parse_argument *arg; 728b6e38b61Snicm u_int i, j; 729b6e38b61Snicm char *s; 730b6e38b61Snicm 731b6e38b61Snicm i = 0; 732b6e38b61Snicm TAILQ_FOREACH(cmd, cmds, entry) { 733b6e38b61Snicm j = 0; 734b6e38b61Snicm TAILQ_FOREACH(arg, &cmd->arguments, entry) { 735b6e38b61Snicm switch (arg->type) { 736b6e38b61Snicm case CMD_PARSE_STRING: 737b6e38b61Snicm log_debug("%s %u:%u: %s", prefix, i, j, 738b6e38b61Snicm arg->string); 739b6e38b61Snicm break; 740b6e38b61Snicm case CMD_PARSE_COMMANDS: 741b6e38b61Snicm xasprintf(&s, "%s %u:%u", prefix, i, j); 742b6e38b61Snicm cmd_parse_log_commands(arg->commands, s); 743b6e38b61Snicm free(s); 744b6e38b61Snicm break; 745d8b32369Snicm case CMD_PARSE_PARSED_COMMANDS: 746d8b32369Snicm s = cmd_list_print(arg->cmdlist, 0); 747d8b32369Snicm log_debug("%s %u:%u: %s", prefix, i, j, s); 748d8b32369Snicm free(s); 749d8b32369Snicm break; 750b6e38b61Snicm } 751b6e38b61Snicm j++; 752b6e38b61Snicm } 753b6e38b61Snicm i++; 754b6e38b61Snicm } 755b6e38b61Snicm } 756b6e38b61Snicm 757287cddb6Snicm static int 758287cddb6Snicm cmd_parse_expand_alias(struct cmd_parse_command *cmd, 759cb3c07dcSnicm struct cmd_parse_input *pi, struct cmd_parse_result *pr) 760b6e38b61Snicm { 761cb3c07dcSnicm struct cmd_parse_argument *arg, *arg1, *first; 762287cddb6Snicm struct cmd_parse_commands *cmds; 763287cddb6Snicm struct cmd_parse_command *last; 764287cddb6Snicm char *alias, *name, *cause; 765b6e38b61Snicm 7669a68e586Snicm if (pi->flags & CMD_PARSE_NOALIAS) 7679a68e586Snicm return (0); 768cb3c07dcSnicm memset(pr, 0, sizeof *pr); 769b6e38b61Snicm 770287cddb6Snicm first = TAILQ_FIRST(&cmd->arguments); 771287cddb6Snicm if (first == NULL || first->type != CMD_PARSE_STRING) { 772afdf680fSnicm pr->status = CMD_PARSE_SUCCESS; 773afdf680fSnicm pr->cmdlist = cmd_list_new(); 774287cddb6Snicm return (1); 775287cddb6Snicm } 776287cddb6Snicm name = first->string; 777287cddb6Snicm 778287cddb6Snicm alias = cmd_get_alias(name); 779287cddb6Snicm if (alias == NULL) 780287cddb6Snicm return (0); 781287cddb6Snicm log_debug("%s: %u alias %s = %s", __func__, pi->line, name, alias); 782287cddb6Snicm 783287cddb6Snicm cmds = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause); 784287cddb6Snicm free(alias); 785287cddb6Snicm if (cmds == NULL) { 786287cddb6Snicm pr->status = CMD_PARSE_ERROR; 787287cddb6Snicm pr->error = cause; 788287cddb6Snicm return (1); 789b6e38b61Snicm } 790b6e38b61Snicm 791287cddb6Snicm last = TAILQ_LAST(cmds, cmd_parse_commands); 792287cddb6Snicm if (last == NULL) { 793cb3c07dcSnicm pr->status = CMD_PARSE_SUCCESS; 794cb3c07dcSnicm pr->cmdlist = cmd_list_new(); 795287cddb6Snicm return (1); 796b6e38b61Snicm } 797b6e38b61Snicm 798287cddb6Snicm TAILQ_REMOVE(&cmd->arguments, first, entry); 799287cddb6Snicm cmd_parse_free_argument(first); 800287cddb6Snicm 801287cddb6Snicm TAILQ_FOREACH_SAFE(arg, &cmd->arguments, entry, arg1) { 802287cddb6Snicm TAILQ_REMOVE(&cmd->arguments, arg, entry); 803287cddb6Snicm TAILQ_INSERT_TAIL(&last->arguments, arg, entry); 804287cddb6Snicm } 805287cddb6Snicm cmd_parse_log_commands(cmds, __func__); 806287cddb6Snicm 8079a68e586Snicm pi->flags |= CMD_PARSE_NOALIAS; 808287cddb6Snicm cmd_parse_build_commands(cmds, pi, pr); 8099a68e586Snicm pi->flags &= ~CMD_PARSE_NOALIAS; 810287cddb6Snicm return (1); 811287cddb6Snicm } 812287cddb6Snicm 813cb3c07dcSnicm static void 814287cddb6Snicm cmd_parse_build_command(struct cmd_parse_command *cmd, 815287cddb6Snicm struct cmd_parse_input *pi, struct cmd_parse_result *pr) 816b6e38b61Snicm { 817b6e38b61Snicm struct cmd_parse_argument *arg; 818287cddb6Snicm struct cmd *add; 819fb147d85Snicm char *cause; 820fb147d85Snicm struct args_value *values = NULL; 821fb147d85Snicm u_int count = 0, idx; 822b6e38b61Snicm 823cb3c07dcSnicm memset(pr, 0, sizeof *pr); 824cb3c07dcSnicm 825cb3c07dcSnicm if (cmd_parse_expand_alias(cmd, pi, pr)) 826cb3c07dcSnicm return; 827b6e38b61Snicm 828b6e38b61Snicm TAILQ_FOREACH(arg, &cmd->arguments, entry) { 829bee784faSnicm values = xrecallocarray(values, count, count + 1, 830bee784faSnicm sizeof *values); 831b6e38b61Snicm switch (arg->type) { 832b6e38b61Snicm case CMD_PARSE_STRING: 833fb147d85Snicm values[count].type = ARGS_STRING; 834fb147d85Snicm values[count].string = xstrdup(arg->string); 835b6e38b61Snicm break; 836b6e38b61Snicm case CMD_PARSE_COMMANDS: 837287cddb6Snicm cmd_parse_build_commands(arg->commands, pi, pr); 838287cddb6Snicm if (pr->status != CMD_PARSE_SUCCESS) 839fb147d85Snicm goto out; 840fb147d85Snicm values[count].type = ARGS_COMMANDS; 841fb147d85Snicm values[count].cmdlist = pr->cmdlist; 842b6e38b61Snicm break; 843d8b32369Snicm case CMD_PARSE_PARSED_COMMANDS: 844d8b32369Snicm values[count].type = ARGS_COMMANDS; 845d8b32369Snicm values[count].cmdlist = arg->cmdlist; 846d8b32369Snicm values[count].cmdlist->references++; 847d8b32369Snicm break; 848b6e38b61Snicm } 849fb147d85Snicm count++; 850b6e38b61Snicm } 851b6e38b61Snicm 852fb147d85Snicm add = cmd_parse(values, count, pi->file, pi->line, &cause); 853379d46e0Snicm if (add == NULL) { 854379d46e0Snicm pr->status = CMD_PARSE_ERROR; 855287cddb6Snicm pr->error = cmd_parse_get_error(pi->file, pi->line, cause); 856379d46e0Snicm free(cause); 857fb147d85Snicm goto out; 858379d46e0Snicm } 859cb3c07dcSnicm pr->status = CMD_PARSE_SUCCESS; 860cb3c07dcSnicm pr->cmdlist = cmd_list_new(); 861cb3c07dcSnicm cmd_list_append(pr->cmdlist, add); 862fb147d85Snicm 863fb147d85Snicm out: 864fb147d85Snicm for (idx = 0; idx < count; idx++) 865fb147d85Snicm args_free_value(&values[idx]); 866fb147d85Snicm free(values); 867379d46e0Snicm } 868379d46e0Snicm 869287cddb6Snicm static void 870a6afde38Snicm cmd_parse_build_commands(struct cmd_parse_commands *cmds, 871287cddb6Snicm struct cmd_parse_input *pi, struct cmd_parse_result *pr) 872df6ab229Snicm { 873287cddb6Snicm struct cmd_parse_command *cmd; 874df6ab229Snicm u_int line = UINT_MAX; 875cb3c07dcSnicm struct cmd_list *current = NULL, *result; 876287cddb6Snicm char *s; 877df6ab229Snicm 878cb3c07dcSnicm memset(pr, 0, sizeof *pr); 879cb3c07dcSnicm 880a6afde38Snicm /* Check for an empty list. */ 881df6ab229Snicm if (TAILQ_EMPTY(cmds)) { 882afdf680fSnicm pr->status = CMD_PARSE_SUCCESS; 883afdf680fSnicm pr->cmdlist = cmd_list_new(); 884287cddb6Snicm return; 885df6ab229Snicm } 886b6e38b61Snicm cmd_parse_log_commands(cmds, __func__); 887df6ab229Snicm 888df6ab229Snicm /* 889df6ab229Snicm * Parse each command into a command list. Create a new command list 890a49ab104Snicm * for each line (unless the flag is set) so they get a new group (so 891a49ab104Snicm * the queue knows which ones to remove if a command fails when 892a49ab104Snicm * executed). 893df6ab229Snicm */ 894df6ab229Snicm result = cmd_list_new(); 895df6ab229Snicm TAILQ_FOREACH(cmd, cmds, entry) { 896379d46e0Snicm if (((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) { 897379d46e0Snicm if (current != NULL) { 898287cddb6Snicm cmd_parse_print_commands(pi, current); 899379d46e0Snicm cmd_list_move(result, current); 900379d46e0Snicm cmd_list_free(current); 901df6ab229Snicm } 902379d46e0Snicm current = cmd_list_new(); 903df6ab229Snicm } 904379d46e0Snicm if (current == NULL) 905379d46e0Snicm current = cmd_list_new(); 906287cddb6Snicm line = pi->line = cmd->line; 907df6ab229Snicm 908cb3c07dcSnicm cmd_parse_build_command(cmd, pi, pr); 909cb3c07dcSnicm if (pr->status != CMD_PARSE_SUCCESS) { 910df6ab229Snicm cmd_list_free(result); 911379d46e0Snicm cmd_list_free(current); 912287cddb6Snicm return; 913df6ab229Snicm } 914cb3c07dcSnicm cmd_list_append_all(current, pr->cmdlist); 915cb3c07dcSnicm cmd_list_free(pr->cmdlist); 916df6ab229Snicm } 917379d46e0Snicm if (current != NULL) { 918287cddb6Snicm cmd_parse_print_commands(pi, current); 919379d46e0Snicm cmd_list_move(result, current); 920379d46e0Snicm cmd_list_free(current); 921df6ab229Snicm } 922df6ab229Snicm 9235c131106Snicm s = cmd_list_print(result, 0); 924df6ab229Snicm log_debug("%s: %s", __func__, s); 925df6ab229Snicm free(s); 926df6ab229Snicm 927287cddb6Snicm pr->status = CMD_PARSE_SUCCESS; 928287cddb6Snicm pr->cmdlist = result; 929df6ab229Snicm } 930df6ab229Snicm 931df6ab229Snicm struct cmd_parse_result * 932a6afde38Snicm cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi) 933a6afde38Snicm { 934a6afde38Snicm static struct cmd_parse_result pr; 935a6afde38Snicm struct cmd_parse_input input; 936a6afde38Snicm struct cmd_parse_commands *cmds; 937a6afde38Snicm char *cause; 938a6afde38Snicm 939a6afde38Snicm if (pi == NULL) { 940a6afde38Snicm memset(&input, 0, sizeof input); 941a6afde38Snicm pi = &input; 942a6afde38Snicm } 943a6afde38Snicm memset(&pr, 0, sizeof pr); 944a6afde38Snicm 94573530c3cSnicm cmds = cmd_parse_do_file(f, pi, &cause); 946a6afde38Snicm if (cmds == NULL) { 947a6afde38Snicm pr.status = CMD_PARSE_ERROR; 948a6afde38Snicm pr.error = cause; 949a6afde38Snicm return (&pr); 950a6afde38Snicm } 951287cddb6Snicm cmd_parse_build_commands(cmds, pi, &pr); 952287cddb6Snicm cmd_parse_free_commands(cmds); 953287cddb6Snicm return (&pr); 954287cddb6Snicm 955a6afde38Snicm } 956a6afde38Snicm 957a6afde38Snicm struct cmd_parse_result * 958df6ab229Snicm cmd_parse_from_string(const char *s, struct cmd_parse_input *pi) 959df6ab229Snicm { 960a49ab104Snicm struct cmd_parse_input input; 961a49ab104Snicm 962a49ab104Snicm if (pi == NULL) { 963a49ab104Snicm memset(&input, 0, sizeof input); 964a49ab104Snicm pi = &input; 965a49ab104Snicm } 966a49ab104Snicm 967a49ab104Snicm /* 968a49ab104Snicm * When parsing a string, put commands in one group even if there are 969a49ab104Snicm * multiple lines. This means { a \n b } is identical to "a ; b" when 970a49ab104Snicm * given as an argument to another command. 971a49ab104Snicm */ 972a49ab104Snicm pi->flags |= CMD_PARSE_ONEGROUP; 973f233c35dSnicm return (cmd_parse_from_buffer(s, strlen(s), pi)); 974f233c35dSnicm } 975f233c35dSnicm 9761c43462cSnicm enum cmd_parse_status 9771c43462cSnicm cmd_parse_and_insert(const char *s, struct cmd_parse_input *pi, 9781c43462cSnicm struct cmdq_item *after, struct cmdq_state *state, char **error) 9791c43462cSnicm { 9801c43462cSnicm struct cmd_parse_result *pr; 9811c43462cSnicm struct cmdq_item *item; 9821c43462cSnicm 9831c43462cSnicm pr = cmd_parse_from_string(s, pi); 9841c43462cSnicm switch (pr->status) { 9851c43462cSnicm case CMD_PARSE_ERROR: 9861c43462cSnicm if (error != NULL) 9871c43462cSnicm *error = pr->error; 9881c43462cSnicm else 9891c43462cSnicm free(pr->error); 9901c43462cSnicm break; 9911c43462cSnicm case CMD_PARSE_SUCCESS: 9921c43462cSnicm item = cmdq_get_command(pr->cmdlist, state); 9931c43462cSnicm cmdq_insert_after(after, item); 9941c43462cSnicm cmd_list_free(pr->cmdlist); 9951c43462cSnicm break; 9961c43462cSnicm } 9971c43462cSnicm return (pr->status); 9981c43462cSnicm } 9991c43462cSnicm 10001c43462cSnicm enum cmd_parse_status 10011c43462cSnicm cmd_parse_and_append(const char *s, struct cmd_parse_input *pi, 10021c43462cSnicm struct client *c, struct cmdq_state *state, char **error) 10031c43462cSnicm { 10041c43462cSnicm struct cmd_parse_result *pr; 10051c43462cSnicm struct cmdq_item *item; 10061c43462cSnicm 10071c43462cSnicm pr = cmd_parse_from_string(s, pi); 10081c43462cSnicm switch (pr->status) { 10091c43462cSnicm case CMD_PARSE_ERROR: 10101c43462cSnicm if (error != NULL) 10111c43462cSnicm *error = pr->error; 10121c43462cSnicm else 10131c43462cSnicm free(pr->error); 10141c43462cSnicm break; 10151c43462cSnicm case CMD_PARSE_SUCCESS: 10161c43462cSnicm item = cmdq_get_command(pr->cmdlist, state); 10171c43462cSnicm cmdq_append(c, item); 10181c43462cSnicm cmd_list_free(pr->cmdlist); 10191c43462cSnicm break; 10201c43462cSnicm } 10211c43462cSnicm return (pr->status); 10221c43462cSnicm } 10231c43462cSnicm 1024f233c35dSnicm struct cmd_parse_result * 1025f233c35dSnicm cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi) 1026f233c35dSnicm { 1027df6ab229Snicm static struct cmd_parse_result pr; 1028a6afde38Snicm struct cmd_parse_input input; 102973530c3cSnicm struct cmd_parse_commands *cmds; 103073530c3cSnicm char *cause; 1031df6ab229Snicm 1032a6afde38Snicm if (pi == NULL) { 1033a6afde38Snicm memset(&input, 0, sizeof input); 1034a6afde38Snicm pi = &input; 1035a6afde38Snicm } 1036a6afde38Snicm memset(&pr, 0, sizeof pr); 1037a6afde38Snicm 1038f233c35dSnicm if (len == 0) { 1039afdf680fSnicm pr.status = CMD_PARSE_SUCCESS; 1040afdf680fSnicm pr.cmdlist = cmd_list_new(); 1041df6ab229Snicm return (&pr); 1042df6ab229Snicm } 1043df6ab229Snicm 1044f233c35dSnicm cmds = cmd_parse_do_buffer(buf, len, pi, &cause); 104573530c3cSnicm if (cmds == NULL) { 1046df6ab229Snicm pr.status = CMD_PARSE_ERROR; 104773530c3cSnicm pr.error = cause; 104873530c3cSnicm return (&pr); 1049df6ab229Snicm } 1050287cddb6Snicm cmd_parse_build_commands(cmds, pi, &pr); 1051287cddb6Snicm cmd_parse_free_commands(cmds); 1052287cddb6Snicm return (&pr); 1053df6ab229Snicm } 1054df6ab229Snicm 1055a6afde38Snicm struct cmd_parse_result * 1056d8b32369Snicm cmd_parse_from_arguments(struct args_value *values, u_int count, 1057d8b32369Snicm struct cmd_parse_input *pi) 1058a6afde38Snicm { 1059287cddb6Snicm static struct cmd_parse_result pr; 1060a6afde38Snicm struct cmd_parse_input input; 1061a6afde38Snicm struct cmd_parse_commands *cmds; 1062d8b32369Snicm struct cmd_parse_command *cmd; 1063d8b32369Snicm struct cmd_parse_argument *arg; 1064d8b32369Snicm u_int i; 1065d8b32369Snicm char *copy; 1066a6afde38Snicm size_t size; 1067d8b32369Snicm int end; 1068a6afde38Snicm 1069a6afde38Snicm /* 1070a6afde38Snicm * The commands are already split up into arguments, so just separate 1071a6afde38Snicm * into a set of commands by ';'. 1072a6afde38Snicm */ 1073a6afde38Snicm 1074a6afde38Snicm if (pi == NULL) { 1075a6afde38Snicm memset(&input, 0, sizeof input); 1076a6afde38Snicm pi = &input; 1077a6afde38Snicm } 1078cb3c07dcSnicm memset(&pr, 0, sizeof pr); 1079a6afde38Snicm 10805d24dd80Snicm cmds = cmd_parse_new_commands(); 1081a6afde38Snicm 1082d8b32369Snicm cmd = xcalloc(1, sizeof *cmd); 1083d8b32369Snicm cmd->line = pi->line; 1084d8b32369Snicm TAILQ_INIT(&cmd->arguments); 1085d8b32369Snicm 1086d8b32369Snicm for (i = 0; i < count; i++) { 1087d8b32369Snicm end = 0; 1088d8b32369Snicm if (values[i].type == ARGS_STRING) { 1089d8b32369Snicm copy = xstrdup(values[i].string); 1090d8b32369Snicm size = strlen(copy); 1091d8b32369Snicm if (size != 0 && copy[size - 1] == ';') { 1092d8b32369Snicm copy[--size] = '\0'; 1093d8b32369Snicm if (size > 0 && copy[size - 1] == '\\') 1094d8b32369Snicm copy[size - 1] = ';'; 1095d8b32369Snicm else 1096d8b32369Snicm end = 1; 1097a6afde38Snicm } 1098d8b32369Snicm if (!end || size != 0) { 1099d8b32369Snicm arg = xcalloc(1, sizeof *arg); 1100d8b32369Snicm arg->type = CMD_PARSE_STRING; 1101d8b32369Snicm arg->string = copy; 1102d8b32369Snicm TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry); 11038d320eeeSnicm } else 11048d320eeeSnicm free(copy); 1105d8b32369Snicm } else if (values[i].type == ARGS_COMMANDS) { 1106d8b32369Snicm arg = xcalloc(1, sizeof *arg); 1107d8b32369Snicm arg->type = CMD_PARSE_PARSED_COMMANDS; 1108d8b32369Snicm arg->cmdlist = values[i].cmdlist; 1109d8b32369Snicm arg->cmdlist->references++; 1110d8b32369Snicm TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry); 1111d8b32369Snicm } else 1112d8b32369Snicm fatalx("unknown argument type"); 1113d8b32369Snicm if (end) { 1114d8b32369Snicm TAILQ_INSERT_TAIL(cmds, cmd, entry); 1115d8b32369Snicm cmd = xcalloc(1, sizeof *cmd); 1116d8b32369Snicm cmd->line = pi->line; 1117d8b32369Snicm TAILQ_INIT(&cmd->arguments); 1118a6afde38Snicm } 1119d8b32369Snicm } 1120d8b32369Snicm if (!TAILQ_EMPTY(&cmd->arguments)) 1121d8b32369Snicm TAILQ_INSERT_TAIL(cmds, cmd, entry); 1122d8b32369Snicm else 1123d8b32369Snicm free(cmd); 11245d24dd80Snicm 1125287cddb6Snicm cmd_parse_build_commands(cmds, pi, &pr); 1126287cddb6Snicm cmd_parse_free_commands(cmds); 1127287cddb6Snicm return (&pr); 1128a6afde38Snicm } 1129a6afde38Snicm 1130df6ab229Snicm static int printflike(1, 2) 1131df6ab229Snicm yyerror(const char *fmt, ...) 1132df6ab229Snicm { 1133df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 1134df6ab229Snicm struct cmd_parse_input *pi = ps->input; 1135df6ab229Snicm va_list ap; 1136df6ab229Snicm char *error; 1137df6ab229Snicm 1138df6ab229Snicm if (ps->error != NULL) 1139df6ab229Snicm return (0); 1140df6ab229Snicm 1141df6ab229Snicm va_start(ap, fmt); 1142df6ab229Snicm xvasprintf(&error, fmt, ap); 1143df6ab229Snicm va_end(ap); 1144df6ab229Snicm 1145df6ab229Snicm ps->error = cmd_parse_get_error(pi->file, pi->line, error); 1146df6ab229Snicm free(error); 1147df6ab229Snicm return (0); 1148df6ab229Snicm } 1149df6ab229Snicm 1150df6ab229Snicm static int 1151df6ab229Snicm yylex_is_var(char ch, int first) 1152df6ab229Snicm { 1153df6ab229Snicm if (ch == '=') 1154df6ab229Snicm return (0); 1155df6ab229Snicm if (first && isdigit((u_char)ch)) 1156df6ab229Snicm return (0); 1157df6ab229Snicm return (isalnum((u_char)ch) || ch == '_'); 1158df6ab229Snicm } 1159df6ab229Snicm 1160df6ab229Snicm static void 1161df6ab229Snicm yylex_append(char **buf, size_t *len, const char *add, size_t addlen) 1162df6ab229Snicm { 1163df6ab229Snicm if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen) 1164df6ab229Snicm fatalx("buffer is too big"); 1165df6ab229Snicm *buf = xrealloc(*buf, (*len) + 1 + addlen); 1166df6ab229Snicm memcpy((*buf) + *len, add, addlen); 1167df6ab229Snicm (*len) += addlen; 1168df6ab229Snicm } 1169df6ab229Snicm 1170df6ab229Snicm static void 1171df6ab229Snicm yylex_append1(char **buf, size_t *len, char add) 1172df6ab229Snicm { 1173df6ab229Snicm yylex_append(buf, len, &add, 1); 1174df6ab229Snicm } 1175df6ab229Snicm 1176df6ab229Snicm static int 117773530c3cSnicm yylex_getc1(void) 117873530c3cSnicm { 117973530c3cSnicm struct cmd_parse_state *ps = &parse_state; 118073530c3cSnicm int ch; 118173530c3cSnicm 118273530c3cSnicm if (ps->f != NULL) 118373530c3cSnicm ch = getc(ps->f); 118473530c3cSnicm else { 118573530c3cSnicm if (ps->off == ps->len) 118673530c3cSnicm ch = EOF; 118773530c3cSnicm else 118873530c3cSnicm ch = ps->buf[ps->off++]; 118973530c3cSnicm } 119073530c3cSnicm return (ch); 119173530c3cSnicm } 119273530c3cSnicm 119373530c3cSnicm static void 119473530c3cSnicm yylex_ungetc(int ch) 119573530c3cSnicm { 119673530c3cSnicm struct cmd_parse_state *ps = &parse_state; 119773530c3cSnicm 119873530c3cSnicm if (ps->f != NULL) 119973530c3cSnicm ungetc(ch, ps->f); 120073530c3cSnicm else if (ps->off > 0 && ch != EOF) 120173530c3cSnicm ps->off--; 120273530c3cSnicm } 120373530c3cSnicm 120473530c3cSnicm static int 1205df6ab229Snicm yylex_getc(void) 1206df6ab229Snicm { 1207df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 1208df6ab229Snicm int ch; 1209df6ab229Snicm 1210df6ab229Snicm if (ps->escapes != 0) { 1211df6ab229Snicm ps->escapes--; 1212df6ab229Snicm return ('\\'); 1213df6ab229Snicm } 1214df6ab229Snicm for (;;) { 121573530c3cSnicm ch = yylex_getc1(); 1216df6ab229Snicm if (ch == '\\') { 1217df6ab229Snicm ps->escapes++; 1218df6ab229Snicm continue; 1219df6ab229Snicm } 1220df6ab229Snicm if (ch == '\n' && (ps->escapes % 2) == 1) { 1221df6ab229Snicm ps->input->line++; 1222df6ab229Snicm ps->escapes--; 1223df6ab229Snicm continue; 1224df6ab229Snicm } 1225df6ab229Snicm 1226df6ab229Snicm if (ps->escapes != 0) { 122773530c3cSnicm yylex_ungetc(ch); 1228df6ab229Snicm ps->escapes--; 1229df6ab229Snicm return ('\\'); 1230df6ab229Snicm } 1231df6ab229Snicm return (ch); 1232df6ab229Snicm } 1233df6ab229Snicm } 1234df6ab229Snicm 1235df6ab229Snicm static char * 1236df6ab229Snicm yylex_get_word(int ch) 1237df6ab229Snicm { 1238df6ab229Snicm char *buf; 1239df6ab229Snicm size_t len; 1240df6ab229Snicm 1241df6ab229Snicm len = 0; 1242df6ab229Snicm buf = xmalloc(1); 1243df6ab229Snicm 1244df6ab229Snicm do 1245df6ab229Snicm yylex_append1(&buf, &len, ch); 1246df6ab229Snicm while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL); 124773530c3cSnicm yylex_ungetc(ch); 1248df6ab229Snicm 1249df6ab229Snicm buf[len] = '\0'; 1250df6ab229Snicm log_debug("%s: %s", __func__, buf); 1251df6ab229Snicm return (buf); 1252df6ab229Snicm } 1253df6ab229Snicm 1254df6ab229Snicm static int 1255df6ab229Snicm yylex(void) 1256df6ab229Snicm { 1257df6ab229Snicm struct cmd_parse_state *ps = &parse_state; 1258df6ab229Snicm char *token, *cp; 125909ba5f57Snicm int ch, next, condition; 1260df6ab229Snicm 126147394861Snicm if (ps->eol) 126247394861Snicm ps->input->line++; 126347394861Snicm ps->eol = 0; 126447394861Snicm 126509ba5f57Snicm condition = ps->condition; 126609ba5f57Snicm ps->condition = 0; 126709ba5f57Snicm 1268df6ab229Snicm for (;;) { 1269df6ab229Snicm ch = yylex_getc(); 1270df6ab229Snicm 1271df6ab229Snicm if (ch == EOF) { 1272df6ab229Snicm /* 1273df6ab229Snicm * Ensure every file or string is terminated by a 1274df6ab229Snicm * newline. This keeps the parser simpler and avoids 1275df6ab229Snicm * having to add a newline to each string. 1276df6ab229Snicm */ 1277df6ab229Snicm if (ps->eof) 1278df6ab229Snicm break; 1279df6ab229Snicm ps->eof = 1; 1280df6ab229Snicm return ('\n'); 1281df6ab229Snicm } 1282df6ab229Snicm 1283df6ab229Snicm if (ch == ' ' || ch == '\t') { 1284df6ab229Snicm /* 1285df6ab229Snicm * Ignore whitespace. 1286df6ab229Snicm */ 1287df6ab229Snicm continue; 1288df6ab229Snicm } 1289df6ab229Snicm 12902544af8fSnicm if (ch == '\r') { 12912544af8fSnicm /* 12922544af8fSnicm * Treat \r\n as \n. 12932544af8fSnicm */ 12942544af8fSnicm ch = yylex_getc(); 12952544af8fSnicm if (ch != '\n') { 12962544af8fSnicm yylex_ungetc(ch); 12972544af8fSnicm ch = '\r'; 12982544af8fSnicm } 12992544af8fSnicm } 1300df6ab229Snicm if (ch == '\n') { 1301df6ab229Snicm /* 1302df6ab229Snicm * End of line. Update the line number. 1303df6ab229Snicm */ 130447394861Snicm ps->eol = 1; 1305df6ab229Snicm return ('\n'); 1306df6ab229Snicm } 1307df6ab229Snicm 13082a840c62Snicm if (ch == ';' || ch == '{' || ch == '}') { 1309df6ab229Snicm /* 13102a840c62Snicm * A semicolon or { or } is itself. 1311df6ab229Snicm */ 13122a840c62Snicm return (ch); 1313df6ab229Snicm } 1314df6ab229Snicm 1315df6ab229Snicm if (ch == '#') { 1316df6ab229Snicm /* 131709ba5f57Snicm * #{ after a condition opens a format; anything else 131809ba5f57Snicm * is a comment, ignore up to the end of the line. 1319df6ab229Snicm */ 1320df6ab229Snicm next = yylex_getc(); 132109ba5f57Snicm if (condition && next == '{') { 1322df6ab229Snicm yylval.token = yylex_format(); 1323df6ab229Snicm if (yylval.token == NULL) 1324df6ab229Snicm return (ERROR); 1325df6ab229Snicm return (FORMAT); 1326df6ab229Snicm } 1327df6ab229Snicm while (next != '\n' && next != EOF) 1328df6ab229Snicm next = yylex_getc(); 1329df6ab229Snicm if (next == '\n') { 1330df6ab229Snicm ps->input->line++; 1331df6ab229Snicm return ('\n'); 1332df6ab229Snicm } 1333df6ab229Snicm continue; 1334df6ab229Snicm } 1335df6ab229Snicm 1336df6ab229Snicm if (ch == '%') { 1337df6ab229Snicm /* 1338f48431f7Snicm * % is a condition unless it is all % or all numbers, 1339f48431f7Snicm * then it is a token. 1340df6ab229Snicm */ 1341df6ab229Snicm yylval.token = yylex_get_word('%'); 1342f48431f7Snicm for (cp = yylval.token; *cp != '\0'; cp++) { 1343f48431f7Snicm if (*cp != '%' && !isdigit((u_char)*cp)) 1344f48431f7Snicm break; 1345f48431f7Snicm } 1346f48431f7Snicm if (*cp == '\0') 1347df6ab229Snicm return (TOKEN); 134809ba5f57Snicm ps->condition = 1; 1349d6f6a5d2Snicm if (strcmp(yylval.token, "%hidden") == 0) { 1350d6f6a5d2Snicm free(yylval.token); 1351d6f6a5d2Snicm return (HIDDEN); 1352d6f6a5d2Snicm } 1353df6ab229Snicm if (strcmp(yylval.token, "%if") == 0) { 1354df6ab229Snicm free(yylval.token); 1355df6ab229Snicm return (IF); 1356df6ab229Snicm } 1357df6ab229Snicm if (strcmp(yylval.token, "%else") == 0) { 1358df6ab229Snicm free(yylval.token); 1359df6ab229Snicm return (ELSE); 1360df6ab229Snicm } 1361df6ab229Snicm if (strcmp(yylval.token, "%elif") == 0) { 1362df6ab229Snicm free(yylval.token); 1363df6ab229Snicm return (ELIF); 1364df6ab229Snicm } 1365df6ab229Snicm if (strcmp(yylval.token, "%endif") == 0) { 1366df6ab229Snicm free(yylval.token); 1367df6ab229Snicm return (ENDIF); 1368df6ab229Snicm } 1369df6ab229Snicm free(yylval.token); 1370df6ab229Snicm return (ERROR); 1371df6ab229Snicm } 1372df6ab229Snicm 1373df6ab229Snicm /* 1374df6ab229Snicm * Otherwise this is a token. 1375df6ab229Snicm */ 1376df6ab229Snicm token = yylex_token(ch); 1377df6ab229Snicm if (token == NULL) 1378df6ab229Snicm return (ERROR); 1379df6ab229Snicm yylval.token = token; 1380df6ab229Snicm 1381df6ab229Snicm if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) { 1382df6ab229Snicm for (cp = token + 1; *cp != '='; cp++) { 1383df6ab229Snicm if (!yylex_is_var(*cp, 0)) 1384df6ab229Snicm break; 1385df6ab229Snicm } 1386df6ab229Snicm if (*cp == '=') 1387df6ab229Snicm return (EQUALS); 1388df6ab229Snicm } 1389df6ab229Snicm return (TOKEN); 1390df6ab229Snicm } 1391df6ab229Snicm return (0); 1392df6ab229Snicm } 1393df6ab229Snicm 1394df6ab229Snicm static char * 1395df6ab229Snicm yylex_format(void) 1396df6ab229Snicm { 1397df6ab229Snicm char *buf; 1398df6ab229Snicm size_t len; 1399df6ab229Snicm int ch, brackets = 1; 1400df6ab229Snicm 1401df6ab229Snicm len = 0; 1402df6ab229Snicm buf = xmalloc(1); 1403df6ab229Snicm 1404df6ab229Snicm yylex_append(&buf, &len, "#{", 2); 1405df6ab229Snicm for (;;) { 1406df6ab229Snicm if ((ch = yylex_getc()) == EOF || ch == '\n') 1407df6ab229Snicm goto error; 1408df6ab229Snicm if (ch == '#') { 1409df6ab229Snicm if ((ch = yylex_getc()) == EOF || ch == '\n') 1410df6ab229Snicm goto error; 1411df6ab229Snicm if (ch == '{') 1412df6ab229Snicm brackets++; 1413df6ab229Snicm yylex_append1(&buf, &len, '#'); 1414df6ab229Snicm } else if (ch == '}') { 1415df6ab229Snicm if (brackets != 0 && --brackets == 0) { 1416df6ab229Snicm yylex_append1(&buf, &len, ch); 1417df6ab229Snicm break; 1418df6ab229Snicm } 1419df6ab229Snicm } 1420df6ab229Snicm yylex_append1(&buf, &len, ch); 1421df6ab229Snicm } 1422df6ab229Snicm if (brackets != 0) 1423df6ab229Snicm goto error; 1424df6ab229Snicm 1425df6ab229Snicm buf[len] = '\0'; 1426df6ab229Snicm log_debug("%s: %s", __func__, buf); 1427df6ab229Snicm return (buf); 1428df6ab229Snicm 1429df6ab229Snicm error: 1430df6ab229Snicm free(buf); 1431df6ab229Snicm return (NULL); 1432df6ab229Snicm } 1433df6ab229Snicm 1434df6ab229Snicm static int 1435df6ab229Snicm yylex_token_escape(char **buf, size_t *len) 1436df6ab229Snicm { 14376852c63bSnicm int ch, type, o2, o3, mlen; 1438df6ab229Snicm u_int size, i, tmp; 14396852c63bSnicm char s[9], m[MB_LEN_MAX]; 1440df6ab229Snicm 1441dfc4c510Snicm ch = yylex_getc(); 1442dfc4c510Snicm 1443dfc4c510Snicm if (ch >= '4' && ch <= '7') { 1444dfc4c510Snicm yyerror("invalid octal escape"); 1445dfc4c510Snicm return (0); 1446dfc4c510Snicm } 1447dfc4c510Snicm if (ch >= '0' && ch <= '3') { 1448dfc4c510Snicm o2 = yylex_getc(); 1449dfc4c510Snicm if (o2 >= '0' && o2 <= '7') { 1450dfc4c510Snicm o3 = yylex_getc(); 1451dfc4c510Snicm if (o3 >= '0' && o3 <= '7') { 1452dfc4c510Snicm ch = 64 * (ch - '0') + 1453dfc4c510Snicm 8 * (o2 - '0') + 1454dfc4c510Snicm (o3 - '0'); 1455dfc4c510Snicm yylex_append1(buf, len, ch); 1456dfc4c510Snicm return (1); 1457dfc4c510Snicm } 1458dfc4c510Snicm } 1459dfc4c510Snicm yyerror("invalid octal escape"); 1460dfc4c510Snicm return (0); 1461dfc4c510Snicm } 1462dfc4c510Snicm 1463dfc4c510Snicm switch (ch) { 1464df6ab229Snicm case EOF: 1465df6ab229Snicm return (0); 146606feda64Snicm case 'a': 146706feda64Snicm ch = '\a'; 146806feda64Snicm break; 146906feda64Snicm case 'b': 147006feda64Snicm ch = '\b'; 147106feda64Snicm break; 1472df6ab229Snicm case 'e': 1473df6ab229Snicm ch = '\033'; 1474df6ab229Snicm break; 147506feda64Snicm case 'f': 147606feda64Snicm ch = '\f'; 147706feda64Snicm break; 147806feda64Snicm case 's': 147906feda64Snicm ch = ' '; 148006feda64Snicm break; 148106feda64Snicm case 'v': 148206feda64Snicm ch = '\v'; 148306feda64Snicm break; 1484df6ab229Snicm case 'r': 1485df6ab229Snicm ch = '\r'; 1486df6ab229Snicm break; 1487df6ab229Snicm case 'n': 1488df6ab229Snicm ch = '\n'; 1489df6ab229Snicm break; 1490df6ab229Snicm case 't': 1491df6ab229Snicm ch = '\t'; 1492df6ab229Snicm break; 1493df6ab229Snicm case 'u': 1494df6ab229Snicm type = 'u'; 1495df6ab229Snicm size = 4; 1496df6ab229Snicm goto unicode; 1497df6ab229Snicm case 'U': 1498df6ab229Snicm type = 'U'; 1499df6ab229Snicm size = 8; 1500df6ab229Snicm goto unicode; 1501df6ab229Snicm } 1502df6ab229Snicm 1503df6ab229Snicm yylex_append1(buf, len, ch); 1504df6ab229Snicm return (1); 1505df6ab229Snicm 1506df6ab229Snicm unicode: 1507df6ab229Snicm for (i = 0; i < size; i++) { 1508df6ab229Snicm ch = yylex_getc(); 1509df6ab229Snicm if (ch == EOF || ch == '\n') 1510df6ab229Snicm return (0); 1511df6ab229Snicm if (!isxdigit((u_char)ch)) { 1512df6ab229Snicm yyerror("invalid \\%c argument", type); 1513df6ab229Snicm return (0); 1514df6ab229Snicm } 1515df6ab229Snicm s[i] = ch; 1516df6ab229Snicm } 1517df6ab229Snicm s[i] = '\0'; 1518df6ab229Snicm 1519df6ab229Snicm if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) || 1520df6ab229Snicm (size == 8 && sscanf(s, "%8x", &tmp) != 1)) { 1521df6ab229Snicm yyerror("invalid \\%c argument", type); 1522df6ab229Snicm return (0); 1523df6ab229Snicm } 15246852c63bSnicm mlen = wctomb(m, tmp); 15256852c63bSnicm if (mlen <= 0 || mlen > (int)sizeof m) { 1526df6ab229Snicm yyerror("invalid \\%c argument", type); 1527df6ab229Snicm return (0); 1528df6ab229Snicm } 15296852c63bSnicm yylex_append(buf, len, m, mlen); 1530df6ab229Snicm return (1); 1531df6ab229Snicm } 1532df6ab229Snicm 1533df6ab229Snicm static int 1534df6ab229Snicm yylex_token_variable(char **buf, size_t *len) 1535df6ab229Snicm { 1536df6ab229Snicm struct environ_entry *envent; 1537df6ab229Snicm int ch, brackets = 0; 15384512d27dSnicm char name[1024]; 1539df6ab229Snicm size_t namelen = 0; 1540df6ab229Snicm const char *value; 1541df6ab229Snicm 1542df6ab229Snicm ch = yylex_getc(); 1543df6ab229Snicm if (ch == EOF) 1544df6ab229Snicm return (0); 1545df6ab229Snicm if (ch == '{') 1546df6ab229Snicm brackets = 1; 1547df6ab229Snicm else { 1548df6ab229Snicm if (!yylex_is_var(ch, 1)) { 1549df6ab229Snicm yylex_append1(buf, len, '$'); 155073530c3cSnicm yylex_ungetc(ch); 1551df6ab229Snicm return (1); 1552df6ab229Snicm } 1553df6ab229Snicm name[namelen++] = ch; 1554df6ab229Snicm } 1555df6ab229Snicm 1556df6ab229Snicm for (;;) { 1557df6ab229Snicm ch = yylex_getc(); 1558df6ab229Snicm if (brackets && ch == '}') 1559df6ab229Snicm break; 1560df6ab229Snicm if (ch == EOF || !yylex_is_var(ch, 0)) { 1561df6ab229Snicm if (!brackets) { 156273530c3cSnicm yylex_ungetc(ch); 1563df6ab229Snicm break; 1564df6ab229Snicm } 1565df6ab229Snicm yyerror("invalid environment variable"); 1566df6ab229Snicm return (0); 1567df6ab229Snicm } 1568df6ab229Snicm if (namelen == (sizeof name) - 2) { 1569df6ab229Snicm yyerror("environment variable is too long"); 1570df6ab229Snicm return (0); 1571df6ab229Snicm } 1572df6ab229Snicm name[namelen++] = ch; 1573df6ab229Snicm } 1574df6ab229Snicm name[namelen] = '\0'; 1575df6ab229Snicm 1576df6ab229Snicm envent = environ_find(global_environ, name); 15779beba1feSnicm if (envent != NULL && envent->value != NULL) { 1578df6ab229Snicm value = envent->value; 1579df6ab229Snicm log_debug("%s: %s -> %s", __func__, name, value); 1580df6ab229Snicm yylex_append(buf, len, value, strlen(value)); 1581df6ab229Snicm } 1582df6ab229Snicm return (1); 1583df6ab229Snicm } 1584df6ab229Snicm 1585df6ab229Snicm static int 1586df6ab229Snicm yylex_token_tilde(char **buf, size_t *len) 1587df6ab229Snicm { 1588df6ab229Snicm struct environ_entry *envent; 1589df6ab229Snicm int ch; 15904512d27dSnicm char name[1024]; 1591df6ab229Snicm size_t namelen = 0; 1592df6ab229Snicm struct passwd *pw; 1593df6ab229Snicm const char *home = NULL; 1594df6ab229Snicm 1595df6ab229Snicm for (;;) { 1596df6ab229Snicm ch = yylex_getc(); 1597df6ab229Snicm if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) { 159873530c3cSnicm yylex_ungetc(ch); 1599df6ab229Snicm break; 1600df6ab229Snicm } 1601df6ab229Snicm if (namelen == (sizeof name) - 2) { 1602df6ab229Snicm yyerror("user name is too long"); 1603df6ab229Snicm return (0); 1604df6ab229Snicm } 1605df6ab229Snicm name[namelen++] = ch; 1606df6ab229Snicm } 1607df6ab229Snicm name[namelen] = '\0'; 1608df6ab229Snicm 1609df6ab229Snicm if (*name == '\0') { 1610df6ab229Snicm envent = environ_find(global_environ, "HOME"); 1611df6ab229Snicm if (envent != NULL && *envent->value != '\0') 1612df6ab229Snicm home = envent->value; 1613df6ab229Snicm else if ((pw = getpwuid(getuid())) != NULL) 1614df6ab229Snicm home = pw->pw_dir; 1615df6ab229Snicm } else { 1616df6ab229Snicm if ((pw = getpwnam(name)) != NULL) 1617df6ab229Snicm home = pw->pw_dir; 1618df6ab229Snicm } 1619df6ab229Snicm if (home == NULL) 1620df6ab229Snicm return (0); 1621df6ab229Snicm 1622df6ab229Snicm log_debug("%s: ~%s -> %s", __func__, name, home); 1623df6ab229Snicm yylex_append(buf, len, home, strlen(home)); 1624df6ab229Snicm return (1); 1625df6ab229Snicm } 1626df6ab229Snicm 1627df6ab229Snicm static char * 1628df6ab229Snicm yylex_token(int ch) 1629df6ab229Snicm { 1630*79be0cc0Snicm struct cmd_parse_state *ps = &parse_state; 1631df6ab229Snicm char *buf; 1632df6ab229Snicm size_t len; 1633df6ab229Snicm enum { START, 1634df6ab229Snicm NONE, 1635df6ab229Snicm DOUBLE_QUOTES, 1636df6ab229Snicm SINGLE_QUOTES } state = NONE, last = START; 1637df6ab229Snicm 1638df6ab229Snicm len = 0; 1639df6ab229Snicm buf = xmalloc(1); 1640df6ab229Snicm 1641df6ab229Snicm for (;;) { 1642af9d9f3bSnicm /* EOF or \n are always the end of the token. */ 1643a01f743bSnicm if (ch == EOF) { 1644a01f743bSnicm log_debug("%s: end at EOF", __func__); 1645df6ab229Snicm break; 1646a01f743bSnicm } 16472544af8fSnicm if (state == NONE && ch == '\r') { 16482544af8fSnicm ch = yylex_getc(); 16492544af8fSnicm if (ch != '\n') { 16502544af8fSnicm yylex_ungetc(ch); 16512544af8fSnicm ch = '\r'; 16522544af8fSnicm } 16532544af8fSnicm } 1654*79be0cc0Snicm if (ch == '\n') { 1655*79be0cc0Snicm if (state == NONE) { 1656a01f743bSnicm log_debug("%s: end at EOL", __func__); 1657a01f743bSnicm break; 1658a01f743bSnicm } 1659*79be0cc0Snicm ps->input->line++; 1660*79be0cc0Snicm } 1661df6ab229Snicm 1662af9d9f3bSnicm /* Whitespace or ; or } ends a token unless inside quotes. */ 1663a01f743bSnicm if (state == NONE && (ch == ' ' || ch == '\t')) { 1664a01f743bSnicm log_debug("%s: end at WS", __func__); 1665df6ab229Snicm break; 1666a01f743bSnicm } 1667a01f743bSnicm if (state == NONE && (ch == ';' || ch == '}')) { 1668a01f743bSnicm log_debug("%s: end at %c", __func__, ch); 1669a01f743bSnicm break; 1670a01f743bSnicm } 1671df6ab229Snicm 1672d156f0b4Snicm /* 1673d156f0b4Snicm * Spaces and comments inside quotes after \n are removed but 1674d156f0b4Snicm * the \n is left. 1675d156f0b4Snicm */ 1676af9d9f3bSnicm if (ch == '\n' && state != NONE) { 1677d156f0b4Snicm yylex_append1(&buf, &len, '\n'); 1678af9d9f3bSnicm while ((ch = yylex_getc()) == ' ' || ch == '\t') 1679af9d9f3bSnicm /* nothing */; 1680af9d9f3bSnicm if (ch != '#') 1681af9d9f3bSnicm continue; 1682af9d9f3bSnicm ch = yylex_getc(); 1683af9d9f3bSnicm if (strchr(",#{}:", ch) != NULL) { 1684af9d9f3bSnicm yylex_ungetc(ch); 1685af9d9f3bSnicm ch = '#'; 1686af9d9f3bSnicm } else { 1687af9d9f3bSnicm while ((ch = yylex_getc()) != '\n' && ch != EOF) 1688af9d9f3bSnicm /* nothing */; 1689af9d9f3bSnicm } 1690af9d9f3bSnicm continue; 1691af9d9f3bSnicm } 1692af9d9f3bSnicm 1693af9d9f3bSnicm /* \ ~ and $ are expanded except in single quotes. */ 1694df6ab229Snicm if (ch == '\\' && state != SINGLE_QUOTES) { 1695df6ab229Snicm if (!yylex_token_escape(&buf, &len)) 1696df6ab229Snicm goto error; 1697df6ab229Snicm goto skip; 1698df6ab229Snicm } 1699df6ab229Snicm if (ch == '~' && last != state && state != SINGLE_QUOTES) { 1700df6ab229Snicm if (!yylex_token_tilde(&buf, &len)) 1701df6ab229Snicm goto error; 1702df6ab229Snicm goto skip; 1703df6ab229Snicm } 1704df6ab229Snicm if (ch == '$' && state != SINGLE_QUOTES) { 1705df6ab229Snicm if (!yylex_token_variable(&buf, &len)) 1706df6ab229Snicm goto error; 1707df6ab229Snicm goto skip; 1708df6ab229Snicm } 17091c947278Snicm if (ch == '}' && state == NONE) 17101c947278Snicm goto error; /* unmatched (matched ones were handled) */ 1711df6ab229Snicm 1712af9d9f3bSnicm /* ' and " starts or end quotes (and is consumed). */ 1713df6ab229Snicm if (ch == '\'') { 1714df6ab229Snicm if (state == NONE) { 1715df6ab229Snicm state = SINGLE_QUOTES; 1716df6ab229Snicm goto next; 1717df6ab229Snicm } 1718df6ab229Snicm if (state == SINGLE_QUOTES) { 1719df6ab229Snicm state = NONE; 1720df6ab229Snicm goto next; 1721df6ab229Snicm } 1722df6ab229Snicm } 1723df6ab229Snicm if (ch == '"') { 1724df6ab229Snicm if (state == NONE) { 1725df6ab229Snicm state = DOUBLE_QUOTES; 1726df6ab229Snicm goto next; 1727df6ab229Snicm } 1728df6ab229Snicm if (state == DOUBLE_QUOTES) { 1729df6ab229Snicm state = NONE; 1730df6ab229Snicm goto next; 1731df6ab229Snicm } 1732df6ab229Snicm } 1733df6ab229Snicm 1734af9d9f3bSnicm /* Otherwise add the character to the buffer. */ 1735df6ab229Snicm yylex_append1(&buf, &len, ch); 1736df6ab229Snicm 1737df6ab229Snicm skip: 1738df6ab229Snicm last = state; 1739df6ab229Snicm 1740df6ab229Snicm next: 1741df6ab229Snicm ch = yylex_getc(); 1742df6ab229Snicm } 174373530c3cSnicm yylex_ungetc(ch); 1744df6ab229Snicm 1745df6ab229Snicm buf[len] = '\0'; 1746df6ab229Snicm log_debug("%s: %s", __func__, buf); 1747df6ab229Snicm return (buf); 1748df6ab229Snicm 1749df6ab229Snicm error: 1750df6ab229Snicm free(buf); 1751df6ab229Snicm return (NULL); 1752df6ab229Snicm } 1753