Merge branch 'obsd-master'
[tmux.git] / cmd-parse.y
blobfbed727b36ea995a8d5eace6df195146c045edbf
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;
226 int flag = 1;
227 struct cmd_parse_scope *scope;
229 if (ps->scope != NULL) {
230 flag = ps->scope->flag;
231 TAILQ_FOREACH(scope, &ps->stack, entry)
232 flag = flag && scope->flag;
235 if ((~flags & CMD_PARSE_PARSEONLY) && flag)
236 environ_put(global_environ, $1, 0);
237 free($1);
240 hidden_assignment : HIDDEN EQUALS
242 struct cmd_parse_state *ps = &parse_state;
243 int flags = ps->input->flags;
244 int flag = 1;
245 struct cmd_parse_scope *scope;
247 if (ps->scope != NULL) {
248 flag = ps->scope->flag;
249 TAILQ_FOREACH(scope, &ps->stack, entry)
250 flag = flag && scope->flag;
253 if ((~flags & CMD_PARSE_PARSEONLY) && flag)
254 environ_put(global_environ, $2, ENVIRON_HIDDEN);
255 free($2);
258 if_open : IF expanded
260 struct cmd_parse_state *ps = &parse_state;
261 struct cmd_parse_scope *scope;
263 scope = xmalloc(sizeof *scope);
264 $$ = scope->flag = format_true($2);
265 free($2);
267 if (ps->scope != NULL)
268 TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry);
269 ps->scope = scope;
272 if_else : ELSE
274 struct cmd_parse_state *ps = &parse_state;
275 struct cmd_parse_scope *scope;
277 scope = xmalloc(sizeof *scope);
278 scope->flag = !ps->scope->flag;
280 free(ps->scope);
281 ps->scope = scope;
284 if_elif : ELIF expanded
286 struct cmd_parse_state *ps = &parse_state;
287 struct cmd_parse_scope *scope;
289 scope = xmalloc(sizeof *scope);
290 $$ = scope->flag = format_true($2);
291 free($2);
293 free(ps->scope);
294 ps->scope = scope;
297 if_close : ENDIF
299 struct cmd_parse_state *ps = &parse_state;
301 free(ps->scope);
302 ps->scope = TAILQ_FIRST(&ps->stack);
303 if (ps->scope != NULL)
304 TAILQ_REMOVE(&ps->stack, ps->scope, entry);
307 condition : if_open '\n' statements if_close
309 if ($1)
310 $$ = $3;
311 else {
312 $$ = cmd_parse_new_commands();
313 cmd_parse_free_commands($3);
316 | if_open '\n' statements if_else '\n' statements if_close
318 if ($1) {
319 $$ = $3;
320 cmd_parse_free_commands($6);
321 } else {
322 $$ = $6;
323 cmd_parse_free_commands($3);
326 | if_open '\n' statements elif if_close
328 if ($1) {
329 $$ = $3;
330 cmd_parse_free_commands($4.commands);
331 } else if ($4.flag) {
332 $$ = $4.commands;
333 cmd_parse_free_commands($3);
334 } else {
335 $$ = cmd_parse_new_commands();
336 cmd_parse_free_commands($3);
337 cmd_parse_free_commands($4.commands);
340 | if_open '\n' statements elif if_else '\n' statements if_close
342 if ($1) {
343 $$ = $3;
344 cmd_parse_free_commands($4.commands);
345 cmd_parse_free_commands($7);
346 } else if ($4.flag) {
347 $$ = $4.commands;
348 cmd_parse_free_commands($3);
349 cmd_parse_free_commands($7);
350 } else {
351 $$ = $7;
352 cmd_parse_free_commands($3);
353 cmd_parse_free_commands($4.commands);
357 elif : if_elif '\n' statements
359 if ($1) {
360 $$.flag = 1;
361 $$.commands = $3;
362 } else {
363 $$.flag = 0;
364 $$.commands = cmd_parse_new_commands();
365 cmd_parse_free_commands($3);
368 | if_elif '\n' statements elif
370 if ($1) {
371 $$.flag = 1;
372 $$.commands = $3;
373 cmd_parse_free_commands($4.commands);
374 } else if ($4.flag) {
375 $$.flag = 1;
376 $$.commands = $4.commands;
377 cmd_parse_free_commands($3);
378 } else {
379 $$.flag = 0;
380 $$.commands = cmd_parse_new_commands();
381 cmd_parse_free_commands($3);
382 cmd_parse_free_commands($4.commands);
386 commands : command
388 struct cmd_parse_state *ps = &parse_state;
390 $$ = cmd_parse_new_commands();
391 if (!TAILQ_EMPTY(&$1->arguments) &&
392 (ps->scope == NULL || ps->scope->flag))
393 TAILQ_INSERT_TAIL($$, $1, entry);
394 else
395 cmd_parse_free_command($1);
397 | commands ';'
399 $$ = $1;
401 | commands ';' condition1
403 $$ = $1;
404 TAILQ_CONCAT($$, $3, entry);
405 free($3);
407 | commands ';' command
409 struct cmd_parse_state *ps = &parse_state;
411 if (!TAILQ_EMPTY(&$3->arguments) &&
412 (ps->scope == NULL || ps->scope->flag)) {
413 $$ = $1;
414 TAILQ_INSERT_TAIL($$, $3, entry);
415 } else {
416 $$ = cmd_parse_new_commands();
417 cmd_parse_free_commands($1);
418 cmd_parse_free_command($3);
421 | condition1
423 $$ = $1;
426 command : assignment
428 struct cmd_parse_state *ps = &parse_state;
430 $$ = xcalloc(1, sizeof *$$);
431 $$->line = ps->input->line;
432 TAILQ_INIT(&$$->arguments);
434 | optional_assignment TOKEN
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 arg = xcalloc(1, sizeof *arg);
444 arg->type = CMD_PARSE_STRING;
445 arg->string = $2;
446 TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
448 | optional_assignment TOKEN arguments
450 struct cmd_parse_state *ps = &parse_state;
451 struct cmd_parse_argument *arg;
453 $$ = xcalloc(1, sizeof *$$);
454 $$->line = ps->input->line;
455 TAILQ_INIT(&$$->arguments);
457 TAILQ_CONCAT(&$$->arguments, $3, entry);
458 free($3);
460 arg = xcalloc(1, sizeof *arg);
461 arg->type = CMD_PARSE_STRING;
462 arg->string = $2;
463 TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
466 condition1 : if_open commands if_close
468 if ($1)
469 $$ = $2;
470 else {
471 $$ = cmd_parse_new_commands();
472 cmd_parse_free_commands($2);
475 | if_open commands if_else commands if_close
477 if ($1) {
478 $$ = $2;
479 cmd_parse_free_commands($4);
480 } else {
481 $$ = $4;
482 cmd_parse_free_commands($2);
485 | if_open commands elif1 if_close
487 if ($1) {
488 $$ = $2;
489 cmd_parse_free_commands($3.commands);
490 } else if ($3.flag) {
491 $$ = $3.commands;
492 cmd_parse_free_commands($2);
493 } else {
494 $$ = cmd_parse_new_commands();
495 cmd_parse_free_commands($2);
496 cmd_parse_free_commands($3.commands);
499 | if_open commands elif1 if_else commands if_close
501 if ($1) {
502 $$ = $2;
503 cmd_parse_free_commands($3.commands);
504 cmd_parse_free_commands($5);
505 } else if ($3.flag) {
506 $$ = $3.commands;
507 cmd_parse_free_commands($2);
508 cmd_parse_free_commands($5);
509 } else {
510 $$ = $5;
511 cmd_parse_free_commands($2);
512 cmd_parse_free_commands($3.commands);
516 elif1 : if_elif commands
518 if ($1) {
519 $$.flag = 1;
520 $$.commands = $2;
521 } else {
522 $$.flag = 0;
523 $$.commands = cmd_parse_new_commands();
524 cmd_parse_free_commands($2);
527 | if_elif commands elif1
529 if ($1) {
530 $$.flag = 1;
531 $$.commands = $2;
532 cmd_parse_free_commands($3.commands);
533 } else if ($3.flag) {
534 $$.flag = 1;
535 $$.commands = $3.commands;
536 cmd_parse_free_commands($2);
537 } else {
538 $$.flag = 0;
539 $$.commands = cmd_parse_new_commands();
540 cmd_parse_free_commands($2);
541 cmd_parse_free_commands($3.commands);
545 arguments : argument
547 $$ = xcalloc(1, sizeof *$$);
548 TAILQ_INIT($$);
550 TAILQ_INSERT_HEAD($$, $1, entry);
552 | argument arguments
554 TAILQ_INSERT_HEAD($2, $1, entry);
555 $$ = $2;
558 argument : TOKEN
560 $$ = xcalloc(1, sizeof *$$);
561 $$->type = CMD_PARSE_STRING;
562 $$->string = $1;
564 | EQUALS
566 $$ = xcalloc(1, sizeof *$$);
567 $$->type = CMD_PARSE_STRING;
568 $$->string = $1;
570 | '{' argument_statements
572 $$ = xcalloc(1, sizeof *$$);
573 $$->type = CMD_PARSE_COMMANDS;
574 $$->commands = $2;
577 argument_statements : statement '}'
579 $$ = $1;
581 | statements statement '}'
583 $$ = $1;
584 TAILQ_CONCAT($$, $2, entry);
585 free($2);
590 static char *
591 cmd_parse_get_error(const char *file, u_int line, const char *error)
593 char *s;
595 if (file == NULL)
596 s = xstrdup(error);
597 else
598 xasprintf(&s, "%s:%u: %s", file, line, error);
599 return (s);
602 static void
603 cmd_parse_print_commands(struct cmd_parse_input *pi, struct cmd_list *cmdlist)
605 char *s;
607 if (pi->item == NULL || (~pi->flags & CMD_PARSE_VERBOSE))
608 return;
609 s = cmd_list_print(cmdlist, 0);
610 if (pi->file != NULL)
611 cmdq_print(pi->item, "%s:%u: %s", pi->file, pi->line, s);
612 else
613 cmdq_print(pi->item, "%u: %s", pi->line, s);
614 free(s);
617 static void
618 cmd_parse_free_argument(struct cmd_parse_argument *arg)
620 switch (arg->type) {
621 case CMD_PARSE_STRING:
622 free(arg->string);
623 break;
624 case CMD_PARSE_COMMANDS:
625 cmd_parse_free_commands(arg->commands);
626 break;
627 case CMD_PARSE_PARSED_COMMANDS:
628 cmd_list_free(arg->cmdlist);
629 break;
631 free(arg);
634 static void
635 cmd_parse_free_arguments(struct cmd_parse_arguments *args)
637 struct cmd_parse_argument *arg, *arg1;
639 TAILQ_FOREACH_SAFE(arg, args, entry, arg1) {
640 TAILQ_REMOVE(args, arg, entry);
641 cmd_parse_free_argument(arg);
645 static void
646 cmd_parse_free_command(struct cmd_parse_command *cmd)
648 cmd_parse_free_arguments(&cmd->arguments);
649 free(cmd);
652 static struct cmd_parse_commands *
653 cmd_parse_new_commands(void)
655 struct cmd_parse_commands *cmds;
657 cmds = xmalloc(sizeof *cmds);
658 TAILQ_INIT(cmds);
659 return (cmds);
662 static void
663 cmd_parse_free_commands(struct cmd_parse_commands *cmds)
665 struct cmd_parse_command *cmd, *cmd1;
667 TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) {
668 TAILQ_REMOVE(cmds, cmd, entry);
669 cmd_parse_free_command(cmd);
671 free(cmds);
674 static struct cmd_parse_commands *
675 cmd_parse_run_parser(char **cause)
677 struct cmd_parse_state *ps = &parse_state;
678 struct cmd_parse_scope *scope, *scope1;
679 int retval;
681 ps->commands = NULL;
682 TAILQ_INIT(&ps->stack);
684 retval = yyparse();
685 TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) {
686 TAILQ_REMOVE(&ps->stack, scope, entry);
687 free(scope);
689 if (retval != 0) {
690 *cause = ps->error;
691 return (NULL);
694 if (ps->commands == NULL)
695 return (cmd_parse_new_commands());
696 return (ps->commands);
699 static struct cmd_parse_commands *
700 cmd_parse_do_file(FILE *f, struct cmd_parse_input *pi, char **cause)
702 struct cmd_parse_state *ps = &parse_state;
704 memset(ps, 0, sizeof *ps);
705 ps->input = pi;
706 ps->f = f;
707 return (cmd_parse_run_parser(cause));
710 static struct cmd_parse_commands *
711 cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi,
712 char **cause)
714 struct cmd_parse_state *ps = &parse_state;
716 memset(ps, 0, sizeof *ps);
717 ps->input = pi;
718 ps->buf = buf;
719 ps->len = len;
720 return (cmd_parse_run_parser(cause));
723 static void
724 cmd_parse_log_commands(struct cmd_parse_commands *cmds, const char *prefix)
726 struct cmd_parse_command *cmd;
727 struct cmd_parse_argument *arg;
728 u_int i, j;
729 char *s;
731 i = 0;
732 TAILQ_FOREACH(cmd, cmds, entry) {
733 j = 0;
734 TAILQ_FOREACH(arg, &cmd->arguments, entry) {
735 switch (arg->type) {
736 case CMD_PARSE_STRING:
737 log_debug("%s %u:%u: %s", prefix, i, j,
738 arg->string);
739 break;
740 case CMD_PARSE_COMMANDS:
741 xasprintf(&s, "%s %u:%u", prefix, i, j);
742 cmd_parse_log_commands(arg->commands, s);
743 free(s);
744 break;
745 case CMD_PARSE_PARSED_COMMANDS:
746 s = cmd_list_print(arg->cmdlist, 0);
747 log_debug("%s %u:%u: %s", prefix, i, j, s);
748 free(s);
749 break;
751 j++;
753 i++;
757 static int
758 cmd_parse_expand_alias(struct cmd_parse_command *cmd,
759 struct cmd_parse_input *pi, struct cmd_parse_result *pr)
761 struct cmd_parse_argument *arg, *arg1, *first;
762 struct cmd_parse_commands *cmds;
763 struct cmd_parse_command *last;
764 char *alias, *name, *cause;
766 if (pi->flags & CMD_PARSE_NOALIAS)
767 return (0);
768 memset(pr, 0, sizeof *pr);
770 first = TAILQ_FIRST(&cmd->arguments);
771 if (first == NULL || first->type != CMD_PARSE_STRING) {
772 pr->status = CMD_PARSE_SUCCESS;
773 pr->cmdlist = cmd_list_new();
774 return (1);
776 name = first->string;
778 alias = cmd_get_alias(name);
779 if (alias == NULL)
780 return (0);
781 log_debug("%s: %u alias %s = %s", __func__, pi->line, name, alias);
783 cmds = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause);
784 free(alias);
785 if (cmds == NULL) {
786 pr->status = CMD_PARSE_ERROR;
787 pr->error = cause;
788 return (1);
791 last = TAILQ_LAST(cmds, cmd_parse_commands);
792 if (last == NULL) {
793 pr->status = CMD_PARSE_SUCCESS;
794 pr->cmdlist = cmd_list_new();
795 return (1);
798 TAILQ_REMOVE(&cmd->arguments, first, entry);
799 cmd_parse_free_argument(first);
801 TAILQ_FOREACH_SAFE(arg, &cmd->arguments, entry, arg1) {
802 TAILQ_REMOVE(&cmd->arguments, arg, entry);
803 TAILQ_INSERT_TAIL(&last->arguments, arg, entry);
805 cmd_parse_log_commands(cmds, __func__);
807 pi->flags |= CMD_PARSE_NOALIAS;
808 cmd_parse_build_commands(cmds, pi, pr);
809 pi->flags &= ~CMD_PARSE_NOALIAS;
810 return (1);
813 static void
814 cmd_parse_build_command(struct cmd_parse_command *cmd,
815 struct cmd_parse_input *pi, struct cmd_parse_result *pr)
817 struct cmd_parse_argument *arg;
818 struct cmd *add;
819 char *cause;
820 struct args_value *values = NULL;
821 u_int count = 0, idx;
823 memset(pr, 0, sizeof *pr);
825 if (cmd_parse_expand_alias(cmd, pi, pr))
826 return;
828 TAILQ_FOREACH(arg, &cmd->arguments, entry) {
829 values = xrecallocarray(values, count, count + 1,
830 sizeof *values);
831 switch (arg->type) {
832 case CMD_PARSE_STRING:
833 values[count].type = ARGS_STRING;
834 values[count].string = xstrdup(arg->string);
835 break;
836 case CMD_PARSE_COMMANDS:
837 cmd_parse_build_commands(arg->commands, pi, pr);
838 if (pr->status != CMD_PARSE_SUCCESS)
839 goto out;
840 values[count].type = ARGS_COMMANDS;
841 values[count].cmdlist = pr->cmdlist;
842 break;
843 case CMD_PARSE_PARSED_COMMANDS:
844 values[count].type = ARGS_COMMANDS;
845 values[count].cmdlist = arg->cmdlist;
846 values[count].cmdlist->references++;
847 break;
849 count++;
852 add = cmd_parse(values, count, pi->file, pi->line, &cause);
853 if (add == NULL) {
854 pr->status = CMD_PARSE_ERROR;
855 pr->error = cmd_parse_get_error(pi->file, pi->line, cause);
856 free(cause);
857 goto out;
859 pr->status = CMD_PARSE_SUCCESS;
860 pr->cmdlist = cmd_list_new();
861 cmd_list_append(pr->cmdlist, add);
863 out:
864 for (idx = 0; idx < count; idx++)
865 args_free_value(&values[idx]);
866 free(values);
869 static void
870 cmd_parse_build_commands(struct cmd_parse_commands *cmds,
871 struct cmd_parse_input *pi, struct cmd_parse_result *pr)
873 struct cmd_parse_command *cmd;
874 u_int line = UINT_MAX;
875 struct cmd_list *current = NULL, *result;
876 char *s;
878 memset(pr, 0, sizeof *pr);
880 /* Check for an empty list. */
881 if (TAILQ_EMPTY(cmds)) {
882 pr->status = CMD_PARSE_SUCCESS;
883 pr->cmdlist = cmd_list_new();
884 return;
886 cmd_parse_log_commands(cmds, __func__);
889 * Parse each command into a command list. Create a new command list
890 * for each line (unless the flag is set) so they get a new group (so
891 * the queue knows which ones to remove if a command fails when
892 * executed).
894 result = cmd_list_new();
895 TAILQ_FOREACH(cmd, cmds, entry) {
896 if (((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) {
897 if (current != NULL) {
898 cmd_parse_print_commands(pi, current);
899 cmd_list_move(result, current);
900 cmd_list_free(current);
902 current = cmd_list_new();
904 if (current == NULL)
905 current = cmd_list_new();
906 line = pi->line = cmd->line;
908 cmd_parse_build_command(cmd, pi, pr);
909 if (pr->status != CMD_PARSE_SUCCESS) {
910 cmd_list_free(result);
911 cmd_list_free(current);
912 return;
914 cmd_list_append_all(current, pr->cmdlist);
915 cmd_list_free(pr->cmdlist);
917 if (current != NULL) {
918 cmd_parse_print_commands(pi, current);
919 cmd_list_move(result, current);
920 cmd_list_free(current);
923 s = cmd_list_print(result, 0);
924 log_debug("%s: %s", __func__, s);
925 free(s);
927 pr->status = CMD_PARSE_SUCCESS;
928 pr->cmdlist = result;
931 struct cmd_parse_result *
932 cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
934 static struct cmd_parse_result pr;
935 struct cmd_parse_input input;
936 struct cmd_parse_commands *cmds;
937 char *cause;
939 if (pi == NULL) {
940 memset(&input, 0, sizeof input);
941 pi = &input;
943 memset(&pr, 0, sizeof pr);
945 cmds = cmd_parse_do_file(f, pi, &cause);
946 if (cmds == NULL) {
947 pr.status = CMD_PARSE_ERROR;
948 pr.error = cause;
949 return (&pr);
951 cmd_parse_build_commands(cmds, pi, &pr);
952 cmd_parse_free_commands(cmds);
953 return (&pr);
957 struct cmd_parse_result *
958 cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
960 struct cmd_parse_input input;
962 if (pi == NULL) {
963 memset(&input, 0, sizeof input);
964 pi = &input;
968 * When parsing a string, put commands in one group even if there are
969 * multiple lines. This means { a \n b } is identical to "a ; b" when
970 * given as an argument to another command.
972 pi->flags |= CMD_PARSE_ONEGROUP;
973 return (cmd_parse_from_buffer(s, strlen(s), pi));
976 enum cmd_parse_status
977 cmd_parse_and_insert(const char *s, struct cmd_parse_input *pi,
978 struct cmdq_item *after, struct cmdq_state *state, char **error)
980 struct cmd_parse_result *pr;
981 struct cmdq_item *item;
983 pr = cmd_parse_from_string(s, pi);
984 switch (pr->status) {
985 case CMD_PARSE_ERROR:
986 if (error != NULL)
987 *error = pr->error;
988 else
989 free(pr->error);
990 break;
991 case CMD_PARSE_SUCCESS:
992 item = cmdq_get_command(pr->cmdlist, state);
993 cmdq_insert_after(after, item);
994 cmd_list_free(pr->cmdlist);
995 break;
997 return (pr->status);
1000 enum cmd_parse_status
1001 cmd_parse_and_append(const char *s, struct cmd_parse_input *pi,
1002 struct client *c, struct cmdq_state *state, char **error)
1004 struct cmd_parse_result *pr;
1005 struct cmdq_item *item;
1007 pr = cmd_parse_from_string(s, pi);
1008 switch (pr->status) {
1009 case CMD_PARSE_ERROR:
1010 if (error != NULL)
1011 *error = pr->error;
1012 else
1013 free(pr->error);
1014 break;
1015 case CMD_PARSE_SUCCESS:
1016 item = cmdq_get_command(pr->cmdlist, state);
1017 cmdq_append(c, item);
1018 cmd_list_free(pr->cmdlist);
1019 break;
1021 return (pr->status);
1024 struct cmd_parse_result *
1025 cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi)
1027 static struct cmd_parse_result pr;
1028 struct cmd_parse_input input;
1029 struct cmd_parse_commands *cmds;
1030 char *cause;
1032 if (pi == NULL) {
1033 memset(&input, 0, sizeof input);
1034 pi = &input;
1036 memset(&pr, 0, sizeof pr);
1038 if (len == 0) {
1039 pr.status = CMD_PARSE_SUCCESS;
1040 pr.cmdlist = cmd_list_new();
1041 return (&pr);
1044 cmds = cmd_parse_do_buffer(buf, len, pi, &cause);
1045 if (cmds == NULL) {
1046 pr.status = CMD_PARSE_ERROR;
1047 pr.error = cause;
1048 return (&pr);
1050 cmd_parse_build_commands(cmds, pi, &pr);
1051 cmd_parse_free_commands(cmds);
1052 return (&pr);
1055 struct cmd_parse_result *
1056 cmd_parse_from_arguments(struct args_value *values, u_int count,
1057 struct cmd_parse_input *pi)
1059 static struct cmd_parse_result pr;
1060 struct cmd_parse_input input;
1061 struct cmd_parse_commands *cmds;
1062 struct cmd_parse_command *cmd;
1063 struct cmd_parse_argument *arg;
1064 u_int i;
1065 char *copy;
1066 size_t size;
1067 int end;
1070 * The commands are already split up into arguments, so just separate
1071 * into a set of commands by ';'.
1074 if (pi == NULL) {
1075 memset(&input, 0, sizeof input);
1076 pi = &input;
1078 memset(&pr, 0, sizeof pr);
1080 cmds = cmd_parse_new_commands();
1082 cmd = xcalloc(1, sizeof *cmd);
1083 cmd->line = pi->line;
1084 TAILQ_INIT(&cmd->arguments);
1086 for (i = 0; i < count; i++) {
1087 end = 0;
1088 if (values[i].type == ARGS_STRING) {
1089 copy = xstrdup(values[i].string);
1090 size = strlen(copy);
1091 if (size != 0 && copy[size - 1] == ';') {
1092 copy[--size] = '\0';
1093 if (size > 0 && copy[size - 1] == '\\')
1094 copy[size - 1] = ';';
1095 else
1096 end = 1;
1098 if (!end || size != 0) {
1099 arg = xcalloc(1, sizeof *arg);
1100 arg->type = CMD_PARSE_STRING;
1101 arg->string = copy;
1102 TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
1103 } else
1104 free(copy);
1105 } else if (values[i].type == ARGS_COMMANDS) {
1106 arg = xcalloc(1, sizeof *arg);
1107 arg->type = CMD_PARSE_PARSED_COMMANDS;
1108 arg->cmdlist = values[i].cmdlist;
1109 arg->cmdlist->references++;
1110 TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
1111 } else
1112 fatalx("unknown argument type");
1113 if (end) {
1114 TAILQ_INSERT_TAIL(cmds, cmd, entry);
1115 cmd = xcalloc(1, sizeof *cmd);
1116 cmd->line = pi->line;
1117 TAILQ_INIT(&cmd->arguments);
1120 if (!TAILQ_EMPTY(&cmd->arguments))
1121 TAILQ_INSERT_TAIL(cmds, cmd, entry);
1122 else
1123 free(cmd);
1125 cmd_parse_build_commands(cmds, pi, &pr);
1126 cmd_parse_free_commands(cmds);
1127 return (&pr);
1130 static int printflike(1, 2)
1131 yyerror(const char *fmt, ...)
1133 struct cmd_parse_state *ps = &parse_state;
1134 struct cmd_parse_input *pi = ps->input;
1135 va_list ap;
1136 char *error;
1138 if (ps->error != NULL)
1139 return (0);
1141 va_start(ap, fmt);
1142 xvasprintf(&error, fmt, ap);
1143 va_end(ap);
1145 ps->error = cmd_parse_get_error(pi->file, pi->line, error);
1146 free(error);
1147 return (0);
1150 static int
1151 yylex_is_var(char ch, int first)
1153 if (ch == '=')
1154 return (0);
1155 if (first && isdigit((u_char)ch))
1156 return (0);
1157 return (isalnum((u_char)ch) || ch == '_');
1160 static void
1161 yylex_append(char **buf, size_t *len, const char *add, size_t addlen)
1163 if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen)
1164 fatalx("buffer is too big");
1165 *buf = xrealloc(*buf, (*len) + 1 + addlen);
1166 memcpy((*buf) + *len, add, addlen);
1167 (*len) += addlen;
1170 static void
1171 yylex_append1(char **buf, size_t *len, char add)
1173 yylex_append(buf, len, &add, 1);
1176 static int
1177 yylex_getc1(void)
1179 struct cmd_parse_state *ps = &parse_state;
1180 int ch;
1182 if (ps->f != NULL)
1183 ch = getc(ps->f);
1184 else {
1185 if (ps->off == ps->len)
1186 ch = EOF;
1187 else
1188 ch = ps->buf[ps->off++];
1190 return (ch);
1193 static void
1194 yylex_ungetc(int ch)
1196 struct cmd_parse_state *ps = &parse_state;
1198 if (ps->f != NULL)
1199 ungetc(ch, ps->f);
1200 else if (ps->off > 0 && ch != EOF)
1201 ps->off--;
1204 static int
1205 yylex_getc(void)
1207 struct cmd_parse_state *ps = &parse_state;
1208 int ch;
1210 if (ps->escapes != 0) {
1211 ps->escapes--;
1212 return ('\\');
1214 for (;;) {
1215 ch = yylex_getc1();
1216 if (ch == '\\') {
1217 ps->escapes++;
1218 continue;
1220 if (ch == '\n' && (ps->escapes % 2) == 1) {
1221 ps->input->line++;
1222 ps->escapes--;
1223 continue;
1226 if (ps->escapes != 0) {
1227 yylex_ungetc(ch);
1228 ps->escapes--;
1229 return ('\\');
1231 return (ch);
1235 static char *
1236 yylex_get_word(int ch)
1238 char *buf;
1239 size_t len;
1241 len = 0;
1242 buf = xmalloc(1);
1245 yylex_append1(&buf, &len, ch);
1246 while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL);
1247 yylex_ungetc(ch);
1249 buf[len] = '\0';
1250 log_debug("%s: %s", __func__, buf);
1251 return (buf);
1254 static int
1255 yylex(void)
1257 struct cmd_parse_state *ps = &parse_state;
1258 char *token, *cp;
1259 int ch, next, condition;
1261 if (ps->eol)
1262 ps->input->line++;
1263 ps->eol = 0;
1265 condition = ps->condition;
1266 ps->condition = 0;
1268 for (;;) {
1269 ch = yylex_getc();
1271 if (ch == EOF) {
1273 * Ensure every file or string is terminated by a
1274 * newline. This keeps the parser simpler and avoids
1275 * having to add a newline to each string.
1277 if (ps->eof)
1278 break;
1279 ps->eof = 1;
1280 return ('\n');
1283 if (ch == ' ' || ch == '\t') {
1285 * Ignore whitespace.
1287 continue;
1290 if (ch == '\r') {
1292 * Treat \r\n as \n.
1294 ch = yylex_getc();
1295 if (ch != '\n') {
1296 yylex_ungetc(ch);
1297 ch = '\r';
1300 if (ch == '\n') {
1302 * End of line. Update the line number.
1304 ps->eol = 1;
1305 return ('\n');
1308 if (ch == ';' || ch == '{' || ch == '}') {
1310 * A semicolon or { or } is itself.
1312 return (ch);
1315 if (ch == '#') {
1317 * #{ after a condition opens a format; anything else
1318 * is a comment, ignore up to the end of the line.
1320 next = yylex_getc();
1321 if (condition && next == '{') {
1322 yylval.token = yylex_format();
1323 if (yylval.token == NULL)
1324 return (ERROR);
1325 return (FORMAT);
1327 while (next != '\n' && next != EOF)
1328 next = yylex_getc();
1329 if (next == '\n') {
1330 ps->input->line++;
1331 return ('\n');
1333 continue;
1336 if (ch == '%') {
1338 * % is a condition unless it is all % or all numbers,
1339 * then it is a token.
1341 yylval.token = yylex_get_word('%');
1342 for (cp = yylval.token; *cp != '\0'; cp++) {
1343 if (*cp != '%' && !isdigit((u_char)*cp))
1344 break;
1346 if (*cp == '\0')
1347 return (TOKEN);
1348 ps->condition = 1;
1349 if (strcmp(yylval.token, "%hidden") == 0) {
1350 free(yylval.token);
1351 return (HIDDEN);
1353 if (strcmp(yylval.token, "%if") == 0) {
1354 free(yylval.token);
1355 return (IF);
1357 if (strcmp(yylval.token, "%else") == 0) {
1358 free(yylval.token);
1359 return (ELSE);
1361 if (strcmp(yylval.token, "%elif") == 0) {
1362 free(yylval.token);
1363 return (ELIF);
1365 if (strcmp(yylval.token, "%endif") == 0) {
1366 free(yylval.token);
1367 return (ENDIF);
1369 free(yylval.token);
1370 return (ERROR);
1374 * Otherwise this is a token.
1376 token = yylex_token(ch);
1377 if (token == NULL)
1378 return (ERROR);
1379 yylval.token = token;
1381 if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
1382 for (cp = token + 1; *cp != '='; cp++) {
1383 if (!yylex_is_var(*cp, 0))
1384 break;
1386 if (*cp == '=')
1387 return (EQUALS);
1389 return (TOKEN);
1391 return (0);
1394 static char *
1395 yylex_format(void)
1397 char *buf;
1398 size_t len;
1399 int ch, brackets = 1;
1401 len = 0;
1402 buf = xmalloc(1);
1404 yylex_append(&buf, &len, "#{", 2);
1405 for (;;) {
1406 if ((ch = yylex_getc()) == EOF || ch == '\n')
1407 goto error;
1408 if (ch == '#') {
1409 if ((ch = yylex_getc()) == EOF || ch == '\n')
1410 goto error;
1411 if (ch == '{')
1412 brackets++;
1413 yylex_append1(&buf, &len, '#');
1414 } else if (ch == '}') {
1415 if (brackets != 0 && --brackets == 0) {
1416 yylex_append1(&buf, &len, ch);
1417 break;
1420 yylex_append1(&buf, &len, ch);
1422 if (brackets != 0)
1423 goto error;
1425 buf[len] = '\0';
1426 log_debug("%s: %s", __func__, buf);
1427 return (buf);
1429 error:
1430 free(buf);
1431 return (NULL);
1434 static int
1435 yylex_token_escape(char **buf, size_t *len)
1437 int ch, type, o2, o3, mlen;
1438 u_int size, i, tmp;
1439 char s[9], m[MB_LEN_MAX];
1441 ch = yylex_getc();
1443 if (ch >= '4' && ch <= '7') {
1444 yyerror("invalid octal escape");
1445 return (0);
1447 if (ch >= '0' && ch <= '3') {
1448 o2 = yylex_getc();
1449 if (o2 >= '0' && o2 <= '7') {
1450 o3 = yylex_getc();
1451 if (o3 >= '0' && o3 <= '7') {
1452 ch = 64 * (ch - '0') +
1453 8 * (o2 - '0') +
1454 (o3 - '0');
1455 yylex_append1(buf, len, ch);
1456 return (1);
1459 yyerror("invalid octal escape");
1460 return (0);
1463 switch (ch) {
1464 case EOF:
1465 return (0);
1466 case 'a':
1467 ch = '\a';
1468 break;
1469 case 'b':
1470 ch = '\b';
1471 break;
1472 case 'e':
1473 ch = '\033';
1474 break;
1475 case 'f':
1476 ch = '\f';
1477 break;
1478 case 's':
1479 ch = ' ';
1480 break;
1481 case 'v':
1482 ch = '\v';
1483 break;
1484 case 'r':
1485 ch = '\r';
1486 break;
1487 case 'n':
1488 ch = '\n';
1489 break;
1490 case 't':
1491 ch = '\t';
1492 break;
1493 case 'u':
1494 type = 'u';
1495 size = 4;
1496 goto unicode;
1497 case 'U':
1498 type = 'U';
1499 size = 8;
1500 goto unicode;
1503 yylex_append1(buf, len, ch);
1504 return (1);
1506 unicode:
1507 for (i = 0; i < size; i++) {
1508 ch = yylex_getc();
1509 if (ch == EOF || ch == '\n')
1510 return (0);
1511 if (!isxdigit((u_char)ch)) {
1512 yyerror("invalid \\%c argument", type);
1513 return (0);
1515 s[i] = ch;
1517 s[i] = '\0';
1519 if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) ||
1520 (size == 8 && sscanf(s, "%8x", &tmp) != 1)) {
1521 yyerror("invalid \\%c argument", type);
1522 return (0);
1524 mlen = wctomb(m, tmp);
1525 if (mlen <= 0 || mlen > (int)sizeof m) {
1526 yyerror("invalid \\%c argument", type);
1527 return (0);
1529 yylex_append(buf, len, m, mlen);
1530 return (1);
1533 static int
1534 yylex_token_variable(char **buf, size_t *len)
1536 struct environ_entry *envent;
1537 int ch, brackets = 0;
1538 char name[1024];
1539 size_t namelen = 0;
1540 const char *value;
1542 ch = yylex_getc();
1543 if (ch == EOF)
1544 return (0);
1545 if (ch == '{')
1546 brackets = 1;
1547 else {
1548 if (!yylex_is_var(ch, 1)) {
1549 yylex_append1(buf, len, '$');
1550 yylex_ungetc(ch);
1551 return (1);
1553 name[namelen++] = ch;
1556 for (;;) {
1557 ch = yylex_getc();
1558 if (brackets && ch == '}')
1559 break;
1560 if (ch == EOF || !yylex_is_var(ch, 0)) {
1561 if (!brackets) {
1562 yylex_ungetc(ch);
1563 break;
1565 yyerror("invalid environment variable");
1566 return (0);
1568 if (namelen == (sizeof name) - 2) {
1569 yyerror("environment variable is too long");
1570 return (0);
1572 name[namelen++] = ch;
1574 name[namelen] = '\0';
1576 envent = environ_find(global_environ, name);
1577 if (envent != NULL && envent->value != NULL) {
1578 value = envent->value;
1579 log_debug("%s: %s -> %s", __func__, name, value);
1580 yylex_append(buf, len, value, strlen(value));
1582 return (1);
1585 static int
1586 yylex_token_tilde(char **buf, size_t *len)
1588 struct environ_entry *envent;
1589 int ch;
1590 char name[1024];
1591 size_t namelen = 0;
1592 struct passwd *pw;
1593 const char *home = NULL;
1595 for (;;) {
1596 ch = yylex_getc();
1597 if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) {
1598 yylex_ungetc(ch);
1599 break;
1601 if (namelen == (sizeof name) - 2) {
1602 yyerror("user name is too long");
1603 return (0);
1605 name[namelen++] = ch;
1607 name[namelen] = '\0';
1609 if (*name == '\0') {
1610 envent = environ_find(global_environ, "HOME");
1611 if (envent != NULL && *envent->value != '\0')
1612 home = envent->value;
1613 else if ((pw = getpwuid(getuid())) != NULL)
1614 home = pw->pw_dir;
1615 } else {
1616 if ((pw = getpwnam(name)) != NULL)
1617 home = pw->pw_dir;
1619 if (home == NULL)
1620 return (0);
1622 log_debug("%s: ~%s -> %s", __func__, name, home);
1623 yylex_append(buf, len, home, strlen(home));
1624 return (1);
1627 static char *
1628 yylex_token(int ch)
1630 char *buf;
1631 size_t len;
1632 enum { START,
1633 NONE,
1634 DOUBLE_QUOTES,
1635 SINGLE_QUOTES } state = NONE, last = START;
1637 len = 0;
1638 buf = xmalloc(1);
1640 for (;;) {
1641 /* EOF or \n are always the end of the token. */
1642 if (ch == EOF) {
1643 log_debug("%s: end at EOF", __func__);
1644 break;
1646 if (state == NONE && ch == '\r') {
1647 ch = yylex_getc();
1648 if (ch != '\n') {
1649 yylex_ungetc(ch);
1650 ch = '\r';
1653 if (state == NONE && ch == '\n') {
1654 log_debug("%s: end at EOL", __func__);
1655 break;
1658 /* Whitespace or ; or } ends a token unless inside quotes. */
1659 if (state == NONE && (ch == ' ' || ch == '\t')) {
1660 log_debug("%s: end at WS", __func__);
1661 break;
1663 if (state == NONE && (ch == ';' || ch == '}')) {
1664 log_debug("%s: end at %c", __func__, ch);
1665 break;
1669 * Spaces and comments inside quotes after \n are removed but
1670 * the \n is left.
1672 if (ch == '\n' && state != NONE) {
1673 yylex_append1(&buf, &len, '\n');
1674 while ((ch = yylex_getc()) == ' ' || ch == '\t')
1675 /* nothing */;
1676 if (ch != '#')
1677 continue;
1678 ch = yylex_getc();
1679 if (strchr(",#{}:", ch) != NULL) {
1680 yylex_ungetc(ch);
1681 ch = '#';
1682 } else {
1683 while ((ch = yylex_getc()) != '\n' && ch != EOF)
1684 /* nothing */;
1686 continue;
1689 /* \ ~ and $ are expanded except in single quotes. */
1690 if (ch == '\\' && state != SINGLE_QUOTES) {
1691 if (!yylex_token_escape(&buf, &len))
1692 goto error;
1693 goto skip;
1695 if (ch == '~' && last != state && state != SINGLE_QUOTES) {
1696 if (!yylex_token_tilde(&buf, &len))
1697 goto error;
1698 goto skip;
1700 if (ch == '$' && state != SINGLE_QUOTES) {
1701 if (!yylex_token_variable(&buf, &len))
1702 goto error;
1703 goto skip;
1705 if (ch == '}' && state == NONE)
1706 goto error; /* unmatched (matched ones were handled) */
1708 /* ' and " starts or end quotes (and is consumed). */
1709 if (ch == '\'') {
1710 if (state == NONE) {
1711 state = SINGLE_QUOTES;
1712 goto next;
1714 if (state == SINGLE_QUOTES) {
1715 state = NONE;
1716 goto next;
1719 if (ch == '"') {
1720 if (state == NONE) {
1721 state = DOUBLE_QUOTES;
1722 goto next;
1724 if (state == DOUBLE_QUOTES) {
1725 state = NONE;
1726 goto next;
1730 /* Otherwise add the character to the buffer. */
1731 yylex_append1(&buf, &len, ch);
1733 skip:
1734 last = state;
1736 next:
1737 ch = yylex_getc();
1739 yylex_ungetc(ch);
1741 buf[len] = '\0';
1742 log_debug("%s: %s", __func__, buf);
1743 return (buf);
1745 error:
1746 free(buf);
1747 return (NULL);