Add -Wno-macro-redefined for macOS.
[tmux.git] / cmd-parse.y
blob02e305503ddb97812f3efb6ef5c916eb2fec7ddf
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
21 #include <sys/types.h>
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>
31 #include "tmux.h"
33 static int yylex(void);
34 static int yyparse(void);
35 static int printflike(1,2) yyerror(const char *, ...);
37 static char *yylex_token(int);
38 static char *yylex_format(void);
40 struct cmd_parse_scope {
41 int flag;
42 TAILQ_ENTRY (cmd_parse_scope) entry;
45 enum cmd_parse_argument_type {
46 CMD_PARSE_STRING,
47 CMD_PARSE_COMMANDS,
48 CMD_PARSE_PARSED_COMMANDS
51 struct cmd_parse_argument {
52 enum cmd_parse_argument_type type;
53 char *string;
54 struct cmd_parse_commands *commands;
55 struct cmd_list *cmdlist;
57 TAILQ_ENTRY(cmd_parse_argument) entry;
59 TAILQ_HEAD(cmd_parse_arguments, cmd_parse_argument);
61 struct cmd_parse_command {
62 u_int line;
63 struct cmd_parse_arguments arguments;
65 TAILQ_ENTRY(cmd_parse_command) entry;
67 TAILQ_HEAD(cmd_parse_commands, cmd_parse_command);
69 struct cmd_parse_state {
70 FILE *f;
72 const char *buf;
73 size_t len;
74 size_t off;
76 int condition;
77 int eol;
78 int eof;
79 struct cmd_parse_input *input;
80 u_int escapes;
82 char *error;
83 struct cmd_parse_commands *commands;
85 struct cmd_parse_scope *scope;
86 TAILQ_HEAD(, cmd_parse_scope) stack;
88 static struct cmd_parse_state parse_state;
90 static char *cmd_parse_get_error(const char *, u_int, const char *);
91 static void cmd_parse_free_command(struct cmd_parse_command *);
92 static struct cmd_parse_commands *cmd_parse_new_commands(void);
93 static void cmd_parse_free_commands(struct cmd_parse_commands *);
94 static void cmd_parse_build_commands(struct cmd_parse_commands *,
95 struct cmd_parse_input *, struct cmd_parse_result *);
96 static void cmd_parse_print_commands(struct cmd_parse_input *,
97 struct cmd_list *);
101 %union
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;
115 %token ERROR
116 %token HIDDEN
117 %token IF
118 %token ELSE
119 %token ELIF
120 %token ENDIF
121 %token <token> FORMAT TOKEN EQUALS
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
134 lines : /* empty */
135 | statements
137 struct cmd_parse_state *ps = &parse_state;
139 ps->commands = $1;
142 statements : statement '\n'
144 $$ = $1;
146 | statements statement '\n'
148 $$ = $1;
149 TAILQ_CONCAT($$, $2, entry);
150 free($2);
153 statement : /* empty */
155 $$ = xmalloc (sizeof *$$);
156 TAILQ_INIT($$);
158 | hidden_assignment
160 $$ = xmalloc (sizeof *$$);
161 TAILQ_INIT($$);
163 | condition
165 struct cmd_parse_state *ps = &parse_state;
167 if (ps->scope == NULL || ps->scope->flag)
168 $$ = $1;
169 else {
170 $$ = cmd_parse_new_commands();
171 cmd_parse_free_commands($1);
174 | commands
176 struct cmd_parse_state *ps = &parse_state;
178 if (ps->scope == NULL || ps->scope->flag)
179 $$ = $1;
180 else {
181 $$ = cmd_parse_new_commands();
182 cmd_parse_free_commands($1);
186 format : FORMAT
188 $$ = $1;
190 | TOKEN
192 $$ = $1;
195 expanded : format
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;
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;
211 ft = format_create(NULL, pi->item, FORMAT_NONE, flags);
212 format_defaults(ft, c, fsp->s, fsp->wl, fsp->wp);
214 $$ = format_expand(ft, $1);
215 format_free(ft);
216 free($1);
219 optional_assignment : /* empty */
220 | assignment
222 assignment : EQUALS
224 struct cmd_parse_state *ps = &parse_state;
225 int flags = ps->input->flags;
227 if ((~flags & CMD_PARSE_PARSEONLY) &&
228 (ps->scope == NULL || ps->scope->flag))
229 environ_put(global_environ, $1, 0);
230 free($1);
233 hidden_assignment : HIDDEN EQUALS
235 struct cmd_parse_state *ps = &parse_state;
236 int flags = ps->input->flags;
238 if ((~flags & CMD_PARSE_PARSEONLY) &&
239 (ps->scope == NULL || ps->scope->flag))
240 environ_put(global_environ, $2, ENVIRON_HIDDEN);
241 free($2);
244 if_open : IF expanded
246 struct cmd_parse_state *ps = &parse_state;
247 struct cmd_parse_scope *scope;
249 scope = xmalloc(sizeof *scope);
250 $$ = scope->flag = format_true($2);
251 free($2);
253 if (ps->scope != NULL)
254 TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry);
255 ps->scope = scope;
258 if_else : ELSE
260 struct cmd_parse_state *ps = &parse_state;
261 struct cmd_parse_scope *scope;
263 scope = xmalloc(sizeof *scope);
264 scope->flag = !ps->scope->flag;
266 free(ps->scope);
267 ps->scope = scope;
270 if_elif : ELIF expanded
272 struct cmd_parse_state *ps = &parse_state;
273 struct cmd_parse_scope *scope;
275 scope = xmalloc(sizeof *scope);
276 $$ = scope->flag = format_true($2);
277 free($2);
279 free(ps->scope);
280 ps->scope = scope;
283 if_close : ENDIF
285 struct cmd_parse_state *ps = &parse_state;
287 free(ps->scope);
288 ps->scope = TAILQ_FIRST(&ps->stack);
289 if (ps->scope != NULL)
290 TAILQ_REMOVE(&ps->stack, ps->scope, entry);
293 condition : if_open '\n' statements if_close
295 if ($1)
296 $$ = $3;
297 else {
298 $$ = cmd_parse_new_commands();
299 cmd_parse_free_commands($3);
302 | if_open '\n' statements if_else '\n' statements if_close
304 if ($1) {
305 $$ = $3;
306 cmd_parse_free_commands($6);
307 } else {
308 $$ = $6;
309 cmd_parse_free_commands($3);
312 | if_open '\n' statements elif if_close
314 if ($1) {
315 $$ = $3;
316 cmd_parse_free_commands($4.commands);
317 } else if ($4.flag) {
318 $$ = $4.commands;
319 cmd_parse_free_commands($3);
320 } else {
321 $$ = cmd_parse_new_commands();
322 cmd_parse_free_commands($3);
323 cmd_parse_free_commands($4.commands);
326 | if_open '\n' statements elif if_else '\n' statements if_close
328 if ($1) {
329 $$ = $3;
330 cmd_parse_free_commands($4.commands);
331 cmd_parse_free_commands($7);
332 } else if ($4.flag) {
333 $$ = $4.commands;
334 cmd_parse_free_commands($3);
335 cmd_parse_free_commands($7);
336 } else {
337 $$ = $7;
338 cmd_parse_free_commands($3);
339 cmd_parse_free_commands($4.commands);
343 elif : if_elif '\n' statements
345 if ($1) {
346 $$.flag = 1;
347 $$.commands = $3;
348 } else {
349 $$.flag = 0;
350 $$.commands = cmd_parse_new_commands();
351 cmd_parse_free_commands($3);
354 | if_elif '\n' statements elif
356 if ($1) {
357 $$.flag = 1;
358 $$.commands = $3;
359 cmd_parse_free_commands($4.commands);
360 } else if ($4.flag) {
361 $$.flag = 1;
362 $$.commands = $4.commands;
363 cmd_parse_free_commands($3);
364 } else {
365 $$.flag = 0;
366 $$.commands = cmd_parse_new_commands();
367 cmd_parse_free_commands($3);
368 cmd_parse_free_commands($4.commands);
372 commands : command
374 struct cmd_parse_state *ps = &parse_state;
376 $$ = cmd_parse_new_commands();
377 if (!TAILQ_EMPTY(&$1->arguments) &&
378 (ps->scope == NULL || ps->scope->flag))
379 TAILQ_INSERT_TAIL($$, $1, entry);
380 else
381 cmd_parse_free_command($1);
383 | commands ';'
385 $$ = $1;
387 | commands ';' condition1
389 $$ = $1;
390 TAILQ_CONCAT($$, $3, entry);
391 free($3);
393 | commands ';' command
395 struct cmd_parse_state *ps = &parse_state;
397 if (!TAILQ_EMPTY(&$3->arguments) &&
398 (ps->scope == NULL || ps->scope->flag)) {
399 $$ = $1;
400 TAILQ_INSERT_TAIL($$, $3, entry);
401 } else {
402 $$ = cmd_parse_new_commands();
403 cmd_parse_free_commands($1);
404 cmd_parse_free_command($3);
407 | condition1
409 $$ = $1;
412 command : assignment
414 struct cmd_parse_state *ps = &parse_state;
416 $$ = xcalloc(1, sizeof *$$);
417 $$->line = ps->input->line;
418 TAILQ_INIT(&$$->arguments);
420 | optional_assignment TOKEN
422 struct cmd_parse_state *ps = &parse_state;
423 struct cmd_parse_argument *arg;
425 $$ = xcalloc(1, sizeof *$$);
426 $$->line = ps->input->line;
427 TAILQ_INIT(&$$->arguments);
429 arg = xcalloc(1, sizeof *arg);
430 arg->type = CMD_PARSE_STRING;
431 arg->string = $2;
432 TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
434 | optional_assignment TOKEN arguments
436 struct cmd_parse_state *ps = &parse_state;
437 struct cmd_parse_argument *arg;
439 $$ = xcalloc(1, sizeof *$$);
440 $$->line = ps->input->line;
441 TAILQ_INIT(&$$->arguments);
443 TAILQ_CONCAT(&$$->arguments, $3, entry);
444 free($3);
446 arg = xcalloc(1, sizeof *arg);
447 arg->type = CMD_PARSE_STRING;
448 arg->string = $2;
449 TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
452 condition1 : if_open commands if_close
454 if ($1)
455 $$ = $2;
456 else {
457 $$ = cmd_parse_new_commands();
458 cmd_parse_free_commands($2);
461 | if_open commands if_else commands if_close
463 if ($1) {
464 $$ = $2;
465 cmd_parse_free_commands($4);
466 } else {
467 $$ = $4;
468 cmd_parse_free_commands($2);
471 | if_open commands elif1 if_close
473 if ($1) {
474 $$ = $2;
475 cmd_parse_free_commands($3.commands);
476 } else if ($3.flag) {
477 $$ = $3.commands;
478 cmd_parse_free_commands($2);
479 } else {
480 $$ = cmd_parse_new_commands();
481 cmd_parse_free_commands($2);
482 cmd_parse_free_commands($3.commands);
485 | if_open commands elif1 if_else commands if_close
487 if ($1) {
488 $$ = $2;
489 cmd_parse_free_commands($3.commands);
490 cmd_parse_free_commands($5);
491 } else if ($3.flag) {
492 $$ = $3.commands;
493 cmd_parse_free_commands($2);
494 cmd_parse_free_commands($5);
495 } else {
496 $$ = $5;
497 cmd_parse_free_commands($2);
498 cmd_parse_free_commands($3.commands);
502 elif1 : if_elif commands
504 if ($1) {
505 $$.flag = 1;
506 $$.commands = $2;
507 } else {
508 $$.flag = 0;
509 $$.commands = cmd_parse_new_commands();
510 cmd_parse_free_commands($2);
513 | if_elif commands elif1
515 if ($1) {
516 $$.flag = 1;
517 $$.commands = $2;
518 cmd_parse_free_commands($3.commands);
519 } else if ($3.flag) {
520 $$.flag = 1;
521 $$.commands = $3.commands;
522 cmd_parse_free_commands($2);
523 } else {
524 $$.flag = 0;
525 $$.commands = cmd_parse_new_commands();
526 cmd_parse_free_commands($2);
527 cmd_parse_free_commands($3.commands);
531 arguments : argument
533 $$ = xcalloc(1, sizeof *$$);
534 TAILQ_INIT($$);
536 TAILQ_INSERT_HEAD($$, $1, entry);
538 | argument arguments
540 TAILQ_INSERT_HEAD($2, $1, entry);
541 $$ = $2;
544 argument : TOKEN
546 $$ = xcalloc(1, sizeof *$$);
547 $$->type = CMD_PARSE_STRING;
548 $$->string = $1;
550 | EQUALS
552 $$ = xcalloc(1, sizeof *$$);
553 $$->type = CMD_PARSE_STRING;
554 $$->string = $1;
556 | '{' argument_statements
558 $$ = xcalloc(1, sizeof *$$);
559 $$->type = CMD_PARSE_COMMANDS;
560 $$->commands = $2;
563 argument_statements : statement '}'
565 $$ = $1;
567 | statements statement '}'
569 $$ = $1;
570 TAILQ_CONCAT($$, $2, entry);
571 free($2);
576 static char *
577 cmd_parse_get_error(const char *file, u_int line, const char *error)
579 char *s;
581 if (file == NULL)
582 s = xstrdup(error);
583 else
584 xasprintf(&s, "%s:%u: %s", file, line, error);
585 return (s);
588 static void
589 cmd_parse_print_commands(struct cmd_parse_input *pi, struct cmd_list *cmdlist)
591 char *s;
593 if (pi->item == NULL || (~pi->flags & CMD_PARSE_VERBOSE))
594 return;
595 s = cmd_list_print(cmdlist, 0);
596 if (pi->file != NULL)
597 cmdq_print(pi->item, "%s:%u: %s", pi->file, pi->line, s);
598 else
599 cmdq_print(pi->item, "%u: %s", pi->line, s);
600 free(s);
603 static void
604 cmd_parse_free_argument(struct cmd_parse_argument *arg)
606 switch (arg->type) {
607 case CMD_PARSE_STRING:
608 free(arg->string);
609 break;
610 case CMD_PARSE_COMMANDS:
611 cmd_parse_free_commands(arg->commands);
612 break;
613 case CMD_PARSE_PARSED_COMMANDS:
614 cmd_list_free(arg->cmdlist);
615 break;
617 free(arg);
620 static void
621 cmd_parse_free_arguments(struct cmd_parse_arguments *args)
623 struct cmd_parse_argument *arg, *arg1;
625 TAILQ_FOREACH_SAFE(arg, args, entry, arg1) {
626 TAILQ_REMOVE(args, arg, entry);
627 cmd_parse_free_argument(arg);
631 static void
632 cmd_parse_free_command(struct cmd_parse_command *cmd)
634 cmd_parse_free_arguments(&cmd->arguments);
635 free(cmd);
638 static struct cmd_parse_commands *
639 cmd_parse_new_commands(void)
641 struct cmd_parse_commands *cmds;
643 cmds = xmalloc(sizeof *cmds);
644 TAILQ_INIT(cmds);
645 return (cmds);
648 static void
649 cmd_parse_free_commands(struct cmd_parse_commands *cmds)
651 struct cmd_parse_command *cmd, *cmd1;
653 TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) {
654 TAILQ_REMOVE(cmds, cmd, entry);
655 cmd_parse_free_command(cmd);
657 free(cmds);
660 static struct cmd_parse_commands *
661 cmd_parse_run_parser(char **cause)
663 struct cmd_parse_state *ps = &parse_state;
664 struct cmd_parse_scope *scope, *scope1;
665 int retval;
667 ps->commands = NULL;
668 TAILQ_INIT(&ps->stack);
670 retval = yyparse();
671 TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) {
672 TAILQ_REMOVE(&ps->stack, scope, entry);
673 free(scope);
675 if (retval != 0) {
676 *cause = ps->error;
677 return (NULL);
680 if (ps->commands == NULL)
681 return (cmd_parse_new_commands());
682 return (ps->commands);
685 static struct cmd_parse_commands *
686 cmd_parse_do_file(FILE *f, struct cmd_parse_input *pi, char **cause)
688 struct cmd_parse_state *ps = &parse_state;
690 memset(ps, 0, sizeof *ps);
691 ps->input = pi;
692 ps->f = f;
693 return (cmd_parse_run_parser(cause));
696 static struct cmd_parse_commands *
697 cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi,
698 char **cause)
700 struct cmd_parse_state *ps = &parse_state;
702 memset(ps, 0, sizeof *ps);
703 ps->input = pi;
704 ps->buf = buf;
705 ps->len = len;
706 return (cmd_parse_run_parser(cause));
709 static void
710 cmd_parse_log_commands(struct cmd_parse_commands *cmds, const char *prefix)
712 struct cmd_parse_command *cmd;
713 struct cmd_parse_argument *arg;
714 u_int i, j;
715 char *s;
717 i = 0;
718 TAILQ_FOREACH(cmd, cmds, entry) {
719 j = 0;
720 TAILQ_FOREACH(arg, &cmd->arguments, entry) {
721 switch (arg->type) {
722 case CMD_PARSE_STRING:
723 log_debug("%s %u:%u: %s", prefix, i, j,
724 arg->string);
725 break;
726 case CMD_PARSE_COMMANDS:
727 xasprintf(&s, "%s %u:%u", prefix, i, j);
728 cmd_parse_log_commands(arg->commands, s);
729 free(s);
730 break;
731 case CMD_PARSE_PARSED_COMMANDS:
732 s = cmd_list_print(arg->cmdlist, 0);
733 log_debug("%s %u:%u: %s", prefix, i, j, s);
734 free(s);
735 break;
737 j++;
739 i++;
743 static int
744 cmd_parse_expand_alias(struct cmd_parse_command *cmd,
745 struct cmd_parse_input *pi, struct cmd_parse_result *pr)
747 struct cmd_parse_argument *arg, *arg1, *first;
748 struct cmd_parse_commands *cmds;
749 struct cmd_parse_command *last;
750 char *alias, *name, *cause;
752 if (pi->flags & CMD_PARSE_NOALIAS)
753 return (0);
754 memset(pr, 0, sizeof *pr);
756 first = TAILQ_FIRST(&cmd->arguments);
757 if (first == NULL || first->type != CMD_PARSE_STRING) {
758 pr->status = CMD_PARSE_SUCCESS;
759 pr->cmdlist = cmd_list_new();
760 return (1);
762 name = first->string;
764 alias = cmd_get_alias(name);
765 if (alias == NULL)
766 return (0);
767 log_debug("%s: %u alias %s = %s", __func__, pi->line, name, alias);
769 cmds = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause);
770 free(alias);
771 if (cmds == NULL) {
772 pr->status = CMD_PARSE_ERROR;
773 pr->error = cause;
774 return (1);
777 last = TAILQ_LAST(cmds, cmd_parse_commands);
778 if (last == NULL) {
779 pr->status = CMD_PARSE_SUCCESS;
780 pr->cmdlist = cmd_list_new();
781 return (1);
784 TAILQ_REMOVE(&cmd->arguments, first, entry);
785 cmd_parse_free_argument(first);
787 TAILQ_FOREACH_SAFE(arg, &cmd->arguments, entry, arg1) {
788 TAILQ_REMOVE(&cmd->arguments, arg, entry);
789 TAILQ_INSERT_TAIL(&last->arguments, arg, entry);
791 cmd_parse_log_commands(cmds, __func__);
793 pi->flags |= CMD_PARSE_NOALIAS;
794 cmd_parse_build_commands(cmds, pi, pr);
795 pi->flags &= ~CMD_PARSE_NOALIAS;
796 return (1);
799 static void
800 cmd_parse_build_command(struct cmd_parse_command *cmd,
801 struct cmd_parse_input *pi, struct cmd_parse_result *pr)
803 struct cmd_parse_argument *arg;
804 struct cmd *add;
805 char *cause;
806 struct args_value *values = NULL;
807 u_int count = 0, idx;
809 memset(pr, 0, sizeof *pr);
811 if (cmd_parse_expand_alias(cmd, pi, pr))
812 return;
814 TAILQ_FOREACH(arg, &cmd->arguments, entry) {
815 values = xrecallocarray(values, count, count + 1,
816 sizeof *values);
817 switch (arg->type) {
818 case CMD_PARSE_STRING:
819 values[count].type = ARGS_STRING;
820 values[count].string = xstrdup(arg->string);
821 break;
822 case CMD_PARSE_COMMANDS:
823 cmd_parse_build_commands(arg->commands, pi, pr);
824 if (pr->status != CMD_PARSE_SUCCESS)
825 goto out;
826 values[count].type = ARGS_COMMANDS;
827 values[count].cmdlist = pr->cmdlist;
828 break;
829 case CMD_PARSE_PARSED_COMMANDS:
830 values[count].type = ARGS_COMMANDS;
831 values[count].cmdlist = arg->cmdlist;
832 values[count].cmdlist->references++;
833 break;
835 count++;
838 add = cmd_parse(values, count, pi->file, pi->line, &cause);
839 if (add == NULL) {
840 pr->status = CMD_PARSE_ERROR;
841 pr->error = cmd_parse_get_error(pi->file, pi->line, cause);
842 free(cause);
843 goto out;
845 pr->status = CMD_PARSE_SUCCESS;
846 pr->cmdlist = cmd_list_new();
847 cmd_list_append(pr->cmdlist, add);
849 out:
850 for (idx = 0; idx < count; idx++)
851 args_free_value(&values[idx]);
852 free(values);
855 static void
856 cmd_parse_build_commands(struct cmd_parse_commands *cmds,
857 struct cmd_parse_input *pi, struct cmd_parse_result *pr)
859 struct cmd_parse_command *cmd;
860 u_int line = UINT_MAX;
861 struct cmd_list *current = NULL, *result;
862 char *s;
864 memset(pr, 0, sizeof *pr);
866 /* Check for an empty list. */
867 if (TAILQ_EMPTY(cmds)) {
868 pr->status = CMD_PARSE_SUCCESS;
869 pr->cmdlist = cmd_list_new();
870 return;
872 cmd_parse_log_commands(cmds, __func__);
875 * Parse each command into a command list. Create a new command list
876 * for each line (unless the flag is set) so they get a new group (so
877 * the queue knows which ones to remove if a command fails when
878 * executed).
880 result = cmd_list_new();
881 TAILQ_FOREACH(cmd, cmds, entry) {
882 if (((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) {
883 if (current != NULL) {
884 cmd_parse_print_commands(pi, current);
885 cmd_list_move(result, current);
886 cmd_list_free(current);
888 current = cmd_list_new();
890 if (current == NULL)
891 current = cmd_list_new();
892 line = pi->line = cmd->line;
894 cmd_parse_build_command(cmd, pi, pr);
895 if (pr->status != CMD_PARSE_SUCCESS) {
896 cmd_list_free(result);
897 cmd_list_free(current);
898 return;
900 cmd_list_append_all(current, pr->cmdlist);
901 cmd_list_free(pr->cmdlist);
903 if (current != NULL) {
904 cmd_parse_print_commands(pi, current);
905 cmd_list_move(result, current);
906 cmd_list_free(current);
909 s = cmd_list_print(result, 0);
910 log_debug("%s: %s", __func__, s);
911 free(s);
913 pr->status = CMD_PARSE_SUCCESS;
914 pr->cmdlist = result;
917 struct cmd_parse_result *
918 cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
920 static struct cmd_parse_result pr;
921 struct cmd_parse_input input;
922 struct cmd_parse_commands *cmds;
923 char *cause;
925 if (pi == NULL) {
926 memset(&input, 0, sizeof input);
927 pi = &input;
929 memset(&pr, 0, sizeof pr);
931 cmds = cmd_parse_do_file(f, pi, &cause);
932 if (cmds == NULL) {
933 pr.status = CMD_PARSE_ERROR;
934 pr.error = cause;
935 return (&pr);
937 cmd_parse_build_commands(cmds, pi, &pr);
938 cmd_parse_free_commands(cmds);
939 return (&pr);
943 struct cmd_parse_result *
944 cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
946 struct cmd_parse_input input;
948 if (pi == NULL) {
949 memset(&input, 0, sizeof input);
950 pi = &input;
954 * When parsing a string, put commands in one group even if there are
955 * multiple lines. This means { a \n b } is identical to "a ; b" when
956 * given as an argument to another command.
958 pi->flags |= CMD_PARSE_ONEGROUP;
959 return (cmd_parse_from_buffer(s, strlen(s), pi));
962 enum cmd_parse_status
963 cmd_parse_and_insert(const char *s, struct cmd_parse_input *pi,
964 struct cmdq_item *after, struct cmdq_state *state, char **error)
966 struct cmd_parse_result *pr;
967 struct cmdq_item *item;
969 pr = cmd_parse_from_string(s, pi);
970 switch (pr->status) {
971 case CMD_PARSE_ERROR:
972 if (error != NULL)
973 *error = pr->error;
974 else
975 free(pr->error);
976 break;
977 case CMD_PARSE_SUCCESS:
978 item = cmdq_get_command(pr->cmdlist, state);
979 cmdq_insert_after(after, item);
980 cmd_list_free(pr->cmdlist);
981 break;
983 return (pr->status);
986 enum cmd_parse_status
987 cmd_parse_and_append(const char *s, struct cmd_parse_input *pi,
988 struct client *c, struct cmdq_state *state, char **error)
990 struct cmd_parse_result *pr;
991 struct cmdq_item *item;
993 pr = cmd_parse_from_string(s, pi);
994 switch (pr->status) {
995 case CMD_PARSE_ERROR:
996 if (error != NULL)
997 *error = pr->error;
998 else
999 free(pr->error);
1000 break;
1001 case CMD_PARSE_SUCCESS:
1002 item = cmdq_get_command(pr->cmdlist, state);
1003 cmdq_append(c, item);
1004 cmd_list_free(pr->cmdlist);
1005 break;
1007 return (pr->status);
1010 struct cmd_parse_result *
1011 cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi)
1013 static struct cmd_parse_result pr;
1014 struct cmd_parse_input input;
1015 struct cmd_parse_commands *cmds;
1016 char *cause;
1018 if (pi == NULL) {
1019 memset(&input, 0, sizeof input);
1020 pi = &input;
1022 memset(&pr, 0, sizeof pr);
1024 if (len == 0) {
1025 pr.status = CMD_PARSE_SUCCESS;
1026 pr.cmdlist = cmd_list_new();
1027 return (&pr);
1030 cmds = cmd_parse_do_buffer(buf, len, pi, &cause);
1031 if (cmds == NULL) {
1032 pr.status = CMD_PARSE_ERROR;
1033 pr.error = cause;
1034 return (&pr);
1036 cmd_parse_build_commands(cmds, pi, &pr);
1037 cmd_parse_free_commands(cmds);
1038 return (&pr);
1041 struct cmd_parse_result *
1042 cmd_parse_from_arguments(struct args_value *values, u_int count,
1043 struct cmd_parse_input *pi)
1045 static struct cmd_parse_result pr;
1046 struct cmd_parse_input input;
1047 struct cmd_parse_commands *cmds;
1048 struct cmd_parse_command *cmd;
1049 struct cmd_parse_argument *arg;
1050 u_int i;
1051 char *copy;
1052 size_t size;
1053 int end;
1056 * The commands are already split up into arguments, so just separate
1057 * into a set of commands by ';'.
1060 if (pi == NULL) {
1061 memset(&input, 0, sizeof input);
1062 pi = &input;
1064 memset(&pr, 0, sizeof pr);
1066 cmds = cmd_parse_new_commands();
1068 cmd = xcalloc(1, sizeof *cmd);
1069 cmd->line = pi->line;
1070 TAILQ_INIT(&cmd->arguments);
1072 for (i = 0; i < count; i++) {
1073 end = 0;
1074 if (values[i].type == ARGS_STRING) {
1075 copy = xstrdup(values[i].string);
1076 size = strlen(copy);
1077 if (size != 0 && copy[size - 1] == ';') {
1078 copy[--size] = '\0';
1079 if (size > 0 && copy[size - 1] == '\\')
1080 copy[size - 1] = ';';
1081 else
1082 end = 1;
1084 if (!end || size != 0) {
1085 arg = xcalloc(1, sizeof *arg);
1086 arg->type = CMD_PARSE_STRING;
1087 arg->string = copy;
1088 TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
1089 } else
1090 free(copy);
1091 } else if (values[i].type == ARGS_COMMANDS) {
1092 arg = xcalloc(1, sizeof *arg);
1093 arg->type = CMD_PARSE_PARSED_COMMANDS;
1094 arg->cmdlist = values[i].cmdlist;
1095 arg->cmdlist->references++;
1096 TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
1097 } else
1098 fatalx("unknown argument type");
1099 if (end) {
1100 TAILQ_INSERT_TAIL(cmds, cmd, entry);
1101 cmd = xcalloc(1, sizeof *cmd);
1102 cmd->line = pi->line;
1103 TAILQ_INIT(&cmd->arguments);
1106 if (!TAILQ_EMPTY(&cmd->arguments))
1107 TAILQ_INSERT_TAIL(cmds, cmd, entry);
1108 else
1109 free(cmd);
1111 cmd_parse_build_commands(cmds, pi, &pr);
1112 cmd_parse_free_commands(cmds);
1113 return (&pr);
1116 static int printflike(1, 2)
1117 yyerror(const char *fmt, ...)
1119 struct cmd_parse_state *ps = &parse_state;
1120 struct cmd_parse_input *pi = ps->input;
1121 va_list ap;
1122 char *error;
1124 if (ps->error != NULL)
1125 return (0);
1127 va_start(ap, fmt);
1128 xvasprintf(&error, fmt, ap);
1129 va_end(ap);
1131 ps->error = cmd_parse_get_error(pi->file, pi->line, error);
1132 free(error);
1133 return (0);
1136 static int
1137 yylex_is_var(char ch, int first)
1139 if (ch == '=')
1140 return (0);
1141 if (first && isdigit((u_char)ch))
1142 return (0);
1143 return (isalnum((u_char)ch) || ch == '_');
1146 static void
1147 yylex_append(char **buf, size_t *len, const char *add, size_t addlen)
1149 if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen)
1150 fatalx("buffer is too big");
1151 *buf = xrealloc(*buf, (*len) + 1 + addlen);
1152 memcpy((*buf) + *len, add, addlen);
1153 (*len) += addlen;
1156 static void
1157 yylex_append1(char **buf, size_t *len, char add)
1159 yylex_append(buf, len, &add, 1);
1162 static int
1163 yylex_getc1(void)
1165 struct cmd_parse_state *ps = &parse_state;
1166 int ch;
1168 if (ps->f != NULL)
1169 ch = getc(ps->f);
1170 else {
1171 if (ps->off == ps->len)
1172 ch = EOF;
1173 else
1174 ch = ps->buf[ps->off++];
1176 return (ch);
1179 static void
1180 yylex_ungetc(int ch)
1182 struct cmd_parse_state *ps = &parse_state;
1184 if (ps->f != NULL)
1185 ungetc(ch, ps->f);
1186 else if (ps->off > 0 && ch != EOF)
1187 ps->off--;
1190 static int
1191 yylex_getc(void)
1193 struct cmd_parse_state *ps = &parse_state;
1194 int ch;
1196 if (ps->escapes != 0) {
1197 ps->escapes--;
1198 return ('\\');
1200 for (;;) {
1201 ch = yylex_getc1();
1202 if (ch == '\\') {
1203 ps->escapes++;
1204 continue;
1206 if (ch == '\n' && (ps->escapes % 2) == 1) {
1207 ps->input->line++;
1208 ps->escapes--;
1209 continue;
1212 if (ps->escapes != 0) {
1213 yylex_ungetc(ch);
1214 ps->escapes--;
1215 return ('\\');
1217 return (ch);
1221 static char *
1222 yylex_get_word(int ch)
1224 char *buf;
1225 size_t len;
1227 len = 0;
1228 buf = xmalloc(1);
1231 yylex_append1(&buf, &len, ch);
1232 while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL);
1233 yylex_ungetc(ch);
1235 buf[len] = '\0';
1236 log_debug("%s: %s", __func__, buf);
1237 return (buf);
1240 static int
1241 yylex(void)
1243 struct cmd_parse_state *ps = &parse_state;
1244 char *token, *cp;
1245 int ch, next, condition;
1247 if (ps->eol)
1248 ps->input->line++;
1249 ps->eol = 0;
1251 condition = ps->condition;
1252 ps->condition = 0;
1254 for (;;) {
1255 ch = yylex_getc();
1257 if (ch == EOF) {
1259 * Ensure every file or string is terminated by a
1260 * newline. This keeps the parser simpler and avoids
1261 * having to add a newline to each string.
1263 if (ps->eof)
1264 break;
1265 ps->eof = 1;
1266 return ('\n');
1269 if (ch == ' ' || ch == '\t') {
1271 * Ignore whitespace.
1273 continue;
1276 if (ch == '\r') {
1278 * Treat \r\n as \n.
1280 ch = yylex_getc();
1281 if (ch != '\n') {
1282 yylex_ungetc(ch);
1283 ch = '\r';
1286 if (ch == '\n') {
1288 * End of line. Update the line number.
1290 ps->eol = 1;
1291 return ('\n');
1294 if (ch == ';' || ch == '{' || ch == '}') {
1296 * A semicolon or { or } is itself.
1298 return (ch);
1301 if (ch == '#') {
1303 * #{ after a condition opens a format; anything else
1304 * is a comment, ignore up to the end of the line.
1306 next = yylex_getc();
1307 if (condition && next == '{') {
1308 yylval.token = yylex_format();
1309 if (yylval.token == NULL)
1310 return (ERROR);
1311 return (FORMAT);
1313 while (next != '\n' && next != EOF)
1314 next = yylex_getc();
1315 if (next == '\n') {
1316 ps->input->line++;
1317 return ('\n');
1319 continue;
1322 if (ch == '%') {
1324 * % is a condition unless it is all % or all numbers,
1325 * then it is a token.
1327 yylval.token = yylex_get_word('%');
1328 for (cp = yylval.token; *cp != '\0'; cp++) {
1329 if (*cp != '%' && !isdigit((u_char)*cp))
1330 break;
1332 if (*cp == '\0')
1333 return (TOKEN);
1334 ps->condition = 1;
1335 if (strcmp(yylval.token, "%hidden") == 0) {
1336 free(yylval.token);
1337 return (HIDDEN);
1339 if (strcmp(yylval.token, "%if") == 0) {
1340 free(yylval.token);
1341 return (IF);
1343 if (strcmp(yylval.token, "%else") == 0) {
1344 free(yylval.token);
1345 return (ELSE);
1347 if (strcmp(yylval.token, "%elif") == 0) {
1348 free(yylval.token);
1349 return (ELIF);
1351 if (strcmp(yylval.token, "%endif") == 0) {
1352 free(yylval.token);
1353 return (ENDIF);
1355 free(yylval.token);
1356 return (ERROR);
1360 * Otherwise this is a token.
1362 token = yylex_token(ch);
1363 if (token == NULL)
1364 return (ERROR);
1365 yylval.token = token;
1367 if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
1368 for (cp = token + 1; *cp != '='; cp++) {
1369 if (!yylex_is_var(*cp, 0))
1370 break;
1372 if (*cp == '=')
1373 return (EQUALS);
1375 return (TOKEN);
1377 return (0);
1380 static char *
1381 yylex_format(void)
1383 char *buf;
1384 size_t len;
1385 int ch, brackets = 1;
1387 len = 0;
1388 buf = xmalloc(1);
1390 yylex_append(&buf, &len, "#{", 2);
1391 for (;;) {
1392 if ((ch = yylex_getc()) == EOF || ch == '\n')
1393 goto error;
1394 if (ch == '#') {
1395 if ((ch = yylex_getc()) == EOF || ch == '\n')
1396 goto error;
1397 if (ch == '{')
1398 brackets++;
1399 yylex_append1(&buf, &len, '#');
1400 } else if (ch == '}') {
1401 if (brackets != 0 && --brackets == 0) {
1402 yylex_append1(&buf, &len, ch);
1403 break;
1406 yylex_append1(&buf, &len, ch);
1408 if (brackets != 0)
1409 goto error;
1411 buf[len] = '\0';
1412 log_debug("%s: %s", __func__, buf);
1413 return (buf);
1415 error:
1416 free(buf);
1417 return (NULL);
1420 static int
1421 yylex_token_escape(char **buf, size_t *len)
1423 int ch, type, o2, o3, mlen;
1424 u_int size, i, tmp;
1425 char s[9], m[MB_LEN_MAX];
1427 ch = yylex_getc();
1429 if (ch >= '4' && ch <= '7') {
1430 yyerror("invalid octal escape");
1431 return (0);
1433 if (ch >= '0' && ch <= '3') {
1434 o2 = yylex_getc();
1435 if (o2 >= '0' && o2 <= '7') {
1436 o3 = yylex_getc();
1437 if (o3 >= '0' && o3 <= '7') {
1438 ch = 64 * (ch - '0') +
1439 8 * (o2 - '0') +
1440 (o3 - '0');
1441 yylex_append1(buf, len, ch);
1442 return (1);
1445 yyerror("invalid octal escape");
1446 return (0);
1449 switch (ch) {
1450 case EOF:
1451 return (0);
1452 case 'a':
1453 ch = '\a';
1454 break;
1455 case 'b':
1456 ch = '\b';
1457 break;
1458 case 'e':
1459 ch = '\033';
1460 break;
1461 case 'f':
1462 ch = '\f';
1463 break;
1464 case 's':
1465 ch = ' ';
1466 break;
1467 case 'v':
1468 ch = '\v';
1469 break;
1470 case 'r':
1471 ch = '\r';
1472 break;
1473 case 'n':
1474 ch = '\n';
1475 break;
1476 case 't':
1477 ch = '\t';
1478 break;
1479 case 'u':
1480 type = 'u';
1481 size = 4;
1482 goto unicode;
1483 case 'U':
1484 type = 'U';
1485 size = 8;
1486 goto unicode;
1489 yylex_append1(buf, len, ch);
1490 return (1);
1492 unicode:
1493 for (i = 0; i < size; i++) {
1494 ch = yylex_getc();
1495 if (ch == EOF || ch == '\n')
1496 return (0);
1497 if (!isxdigit((u_char)ch)) {
1498 yyerror("invalid \\%c argument", type);
1499 return (0);
1501 s[i] = ch;
1503 s[i] = '\0';
1505 if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) ||
1506 (size == 8 && sscanf(s, "%8x", &tmp) != 1)) {
1507 yyerror("invalid \\%c argument", type);
1508 return (0);
1510 mlen = wctomb(m, tmp);
1511 if (mlen <= 0 || mlen > (int)sizeof m) {
1512 yyerror("invalid \\%c argument", type);
1513 return (0);
1515 yylex_append(buf, len, m, mlen);
1516 return (1);
1519 static int
1520 yylex_token_variable(char **buf, size_t *len)
1522 struct environ_entry *envent;
1523 int ch, brackets = 0;
1524 char name[1024];
1525 size_t namelen = 0;
1526 const char *value;
1528 ch = yylex_getc();
1529 if (ch == EOF)
1530 return (0);
1531 if (ch == '{')
1532 brackets = 1;
1533 else {
1534 if (!yylex_is_var(ch, 1)) {
1535 yylex_append1(buf, len, '$');
1536 yylex_ungetc(ch);
1537 return (1);
1539 name[namelen++] = ch;
1542 for (;;) {
1543 ch = yylex_getc();
1544 if (brackets && ch == '}')
1545 break;
1546 if (ch == EOF || !yylex_is_var(ch, 0)) {
1547 if (!brackets) {
1548 yylex_ungetc(ch);
1549 break;
1551 yyerror("invalid environment variable");
1552 return (0);
1554 if (namelen == (sizeof name) - 2) {
1555 yyerror("environment variable is too long");
1556 return (0);
1558 name[namelen++] = ch;
1560 name[namelen] = '\0';
1562 envent = environ_find(global_environ, name);
1563 if (envent != NULL && envent->value != NULL) {
1564 value = envent->value;
1565 log_debug("%s: %s -> %s", __func__, name, value);
1566 yylex_append(buf, len, value, strlen(value));
1568 return (1);
1571 static int
1572 yylex_token_tilde(char **buf, size_t *len)
1574 struct environ_entry *envent;
1575 int ch;
1576 char name[1024];
1577 size_t namelen = 0;
1578 struct passwd *pw;
1579 const char *home = NULL;
1581 for (;;) {
1582 ch = yylex_getc();
1583 if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) {
1584 yylex_ungetc(ch);
1585 break;
1587 if (namelen == (sizeof name) - 2) {
1588 yyerror("user name is too long");
1589 return (0);
1591 name[namelen++] = ch;
1593 name[namelen] = '\0';
1595 if (*name == '\0') {
1596 envent = environ_find(global_environ, "HOME");
1597 if (envent != NULL && *envent->value != '\0')
1598 home = envent->value;
1599 else if ((pw = getpwuid(getuid())) != NULL)
1600 home = pw->pw_dir;
1601 } else {
1602 if ((pw = getpwnam(name)) != NULL)
1603 home = pw->pw_dir;
1605 if (home == NULL)
1606 return (0);
1608 log_debug("%s: ~%s -> %s", __func__, name, home);
1609 yylex_append(buf, len, home, strlen(home));
1610 return (1);
1613 static char *
1614 yylex_token(int ch)
1616 char *buf;
1617 size_t len;
1618 enum { START,
1619 NONE,
1620 DOUBLE_QUOTES,
1621 SINGLE_QUOTES } state = NONE, last = START;
1623 len = 0;
1624 buf = xmalloc(1);
1626 for (;;) {
1627 /* EOF or \n are always the end of the token. */
1628 if (ch == EOF) {
1629 log_debug("%s: end at EOF", __func__);
1630 break;
1632 if (state == NONE && ch == '\r') {
1633 ch = yylex_getc();
1634 if (ch != '\n') {
1635 yylex_ungetc(ch);
1636 ch = '\r';
1639 if (state == NONE && ch == '\n') {
1640 log_debug("%s: end at EOL", __func__);
1641 break;
1644 /* Whitespace or ; or } ends a token unless inside quotes. */
1645 if (state == NONE && (ch == ' ' || ch == '\t')) {
1646 log_debug("%s: end at WS", __func__);
1647 break;
1649 if (state == NONE && (ch == ';' || ch == '}')) {
1650 log_debug("%s: end at %c", __func__, ch);
1651 break;
1655 * Spaces and comments inside quotes after \n are removed but
1656 * the \n is left.
1658 if (ch == '\n' && state != NONE) {
1659 yylex_append1(&buf, &len, '\n');
1660 while ((ch = yylex_getc()) == ' ' || ch == '\t')
1661 /* nothing */;
1662 if (ch != '#')
1663 continue;
1664 ch = yylex_getc();
1665 if (strchr(",#{}:", ch) != NULL) {
1666 yylex_ungetc(ch);
1667 ch = '#';
1668 } else {
1669 while ((ch = yylex_getc()) != '\n' && ch != EOF)
1670 /* nothing */;
1672 continue;
1675 /* \ ~ and $ are expanded except in single quotes. */
1676 if (ch == '\\' && state != SINGLE_QUOTES) {
1677 if (!yylex_token_escape(&buf, &len))
1678 goto error;
1679 goto skip;
1681 if (ch == '~' && last != state && state != SINGLE_QUOTES) {
1682 if (!yylex_token_tilde(&buf, &len))
1683 goto error;
1684 goto skip;
1686 if (ch == '$' && state != SINGLE_QUOTES) {
1687 if (!yylex_token_variable(&buf, &len))
1688 goto error;
1689 goto skip;
1691 if (ch == '}' && state == NONE)
1692 goto error; /* unmatched (matched ones were handled) */
1694 /* ' and " starts or end quotes (and is consumed). */
1695 if (ch == '\'') {
1696 if (state == NONE) {
1697 state = SINGLE_QUOTES;
1698 goto next;
1700 if (state == SINGLE_QUOTES) {
1701 state = NONE;
1702 goto next;
1705 if (ch == '"') {
1706 if (state == NONE) {
1707 state = DOUBLE_QUOTES;
1708 goto next;
1710 if (state == DOUBLE_QUOTES) {
1711 state = NONE;
1712 goto next;
1716 /* Otherwise add the character to the buffer. */
1717 yylex_append1(&buf, &len, ch);
1719 skip:
1720 last = state;
1722 next:
1723 ch = yylex_getc();
1725 yylex_ungetc(ch);
1727 buf[len] = '\0';
1728 log_debug("%s: %s", __func__, buf);
1729 return (buf);
1731 error:
1732 free(buf);
1733 return (NULL);