Better error reporting when applying custom layouts.
[tmux.git] / cmd-parse.y
blob1d692770834d54bb96102aff33e34d2905f8439d
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);
1090 } else if (values[i].type == ARGS_COMMANDS) {
1091 arg = xcalloc(1, sizeof *arg);
1092 arg->type = CMD_PARSE_PARSED_COMMANDS;
1093 arg->cmdlist = values[i].cmdlist;
1094 arg->cmdlist->references++;
1095 TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
1096 } else
1097 fatalx("unknown argument type");
1098 if (end) {
1099 TAILQ_INSERT_TAIL(cmds, cmd, entry);
1100 cmd = xcalloc(1, sizeof *cmd);
1101 cmd->line = pi->line;
1102 TAILQ_INIT(&cmd->arguments);
1105 if (!TAILQ_EMPTY(&cmd->arguments))
1106 TAILQ_INSERT_TAIL(cmds, cmd, entry);
1107 else
1108 free(cmd);
1110 cmd_parse_build_commands(cmds, pi, &pr);
1111 cmd_parse_free_commands(cmds);
1112 return (&pr);
1115 static int printflike(1, 2)
1116 yyerror(const char *fmt, ...)
1118 struct cmd_parse_state *ps = &parse_state;
1119 struct cmd_parse_input *pi = ps->input;
1120 va_list ap;
1121 char *error;
1123 if (ps->error != NULL)
1124 return (0);
1126 va_start(ap, fmt);
1127 xvasprintf(&error, fmt, ap);
1128 va_end(ap);
1130 ps->error = cmd_parse_get_error(pi->file, pi->line, error);
1131 free(error);
1132 return (0);
1135 static int
1136 yylex_is_var(char ch, int first)
1138 if (ch == '=')
1139 return (0);
1140 if (first && isdigit((u_char)ch))
1141 return (0);
1142 return (isalnum((u_char)ch) || ch == '_');
1145 static void
1146 yylex_append(char **buf, size_t *len, const char *add, size_t addlen)
1148 if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen)
1149 fatalx("buffer is too big");
1150 *buf = xrealloc(*buf, (*len) + 1 + addlen);
1151 memcpy((*buf) + *len, add, addlen);
1152 (*len) += addlen;
1155 static void
1156 yylex_append1(char **buf, size_t *len, char add)
1158 yylex_append(buf, len, &add, 1);
1161 static int
1162 yylex_getc1(void)
1164 struct cmd_parse_state *ps = &parse_state;
1165 int ch;
1167 if (ps->f != NULL)
1168 ch = getc(ps->f);
1169 else {
1170 if (ps->off == ps->len)
1171 ch = EOF;
1172 else
1173 ch = ps->buf[ps->off++];
1175 return (ch);
1178 static void
1179 yylex_ungetc(int ch)
1181 struct cmd_parse_state *ps = &parse_state;
1183 if (ps->f != NULL)
1184 ungetc(ch, ps->f);
1185 else if (ps->off > 0 && ch != EOF)
1186 ps->off--;
1189 static int
1190 yylex_getc(void)
1192 struct cmd_parse_state *ps = &parse_state;
1193 int ch;
1195 if (ps->escapes != 0) {
1196 ps->escapes--;
1197 return ('\\');
1199 for (;;) {
1200 ch = yylex_getc1();
1201 if (ch == '\\') {
1202 ps->escapes++;
1203 continue;
1205 if (ch == '\n' && (ps->escapes % 2) == 1) {
1206 ps->input->line++;
1207 ps->escapes--;
1208 continue;
1211 if (ps->escapes != 0) {
1212 yylex_ungetc(ch);
1213 ps->escapes--;
1214 return ('\\');
1216 return (ch);
1220 static char *
1221 yylex_get_word(int ch)
1223 char *buf;
1224 size_t len;
1226 len = 0;
1227 buf = xmalloc(1);
1230 yylex_append1(&buf, &len, ch);
1231 while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL);
1232 yylex_ungetc(ch);
1234 buf[len] = '\0';
1235 log_debug("%s: %s", __func__, buf);
1236 return (buf);
1239 static int
1240 yylex(void)
1242 struct cmd_parse_state *ps = &parse_state;
1243 char *token, *cp;
1244 int ch, next, condition;
1246 if (ps->eol)
1247 ps->input->line++;
1248 ps->eol = 0;
1250 condition = ps->condition;
1251 ps->condition = 0;
1253 for (;;) {
1254 ch = yylex_getc();
1256 if (ch == EOF) {
1258 * Ensure every file or string is terminated by a
1259 * newline. This keeps the parser simpler and avoids
1260 * having to add a newline to each string.
1262 if (ps->eof)
1263 break;
1264 ps->eof = 1;
1265 return ('\n');
1268 if (ch == ' ' || ch == '\t') {
1270 * Ignore whitespace.
1272 continue;
1275 if (ch == '\n') {
1277 * End of line. Update the line number.
1279 ps->eol = 1;
1280 return ('\n');
1283 if (ch == ';' || ch == '{' || ch == '}') {
1285 * A semicolon or { or } is itself.
1287 return (ch);
1290 if (ch == '#') {
1292 * #{ after a condition opens a format; anything else
1293 * is a comment, ignore up to the end of the line.
1295 next = yylex_getc();
1296 if (condition && next == '{') {
1297 yylval.token = yylex_format();
1298 if (yylval.token == NULL)
1299 return (ERROR);
1300 return (FORMAT);
1302 while (next != '\n' && next != EOF)
1303 next = yylex_getc();
1304 if (next == '\n') {
1305 ps->input->line++;
1306 return ('\n');
1308 continue;
1311 if (ch == '%') {
1313 * % is a condition unless it is all % or all numbers,
1314 * then it is a token.
1316 yylval.token = yylex_get_word('%');
1317 for (cp = yylval.token; *cp != '\0'; cp++) {
1318 if (*cp != '%' && !isdigit((u_char)*cp))
1319 break;
1321 if (*cp == '\0')
1322 return (TOKEN);
1323 ps->condition = 1;
1324 if (strcmp(yylval.token, "%hidden") == 0) {
1325 free(yylval.token);
1326 return (HIDDEN);
1328 if (strcmp(yylval.token, "%if") == 0) {
1329 free(yylval.token);
1330 return (IF);
1332 if (strcmp(yylval.token, "%else") == 0) {
1333 free(yylval.token);
1334 return (ELSE);
1336 if (strcmp(yylval.token, "%elif") == 0) {
1337 free(yylval.token);
1338 return (ELIF);
1340 if (strcmp(yylval.token, "%endif") == 0) {
1341 free(yylval.token);
1342 return (ENDIF);
1344 free(yylval.token);
1345 return (ERROR);
1349 * Otherwise this is a token.
1351 token = yylex_token(ch);
1352 if (token == NULL)
1353 return (ERROR);
1354 yylval.token = token;
1356 if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
1357 for (cp = token + 1; *cp != '='; cp++) {
1358 if (!yylex_is_var(*cp, 0))
1359 break;
1361 if (*cp == '=')
1362 return (EQUALS);
1364 return (TOKEN);
1366 return (0);
1369 static char *
1370 yylex_format(void)
1372 char *buf;
1373 size_t len;
1374 int ch, brackets = 1;
1376 len = 0;
1377 buf = xmalloc(1);
1379 yylex_append(&buf, &len, "#{", 2);
1380 for (;;) {
1381 if ((ch = yylex_getc()) == EOF || ch == '\n')
1382 goto error;
1383 if (ch == '#') {
1384 if ((ch = yylex_getc()) == EOF || ch == '\n')
1385 goto error;
1386 if (ch == '{')
1387 brackets++;
1388 yylex_append1(&buf, &len, '#');
1389 } else if (ch == '}') {
1390 if (brackets != 0 && --brackets == 0) {
1391 yylex_append1(&buf, &len, ch);
1392 break;
1395 yylex_append1(&buf, &len, ch);
1397 if (brackets != 0)
1398 goto error;
1400 buf[len] = '\0';
1401 log_debug("%s: %s", __func__, buf);
1402 return (buf);
1404 error:
1405 free(buf);
1406 return (NULL);
1409 static int
1410 yylex_token_escape(char **buf, size_t *len)
1412 int ch, type, o2, o3, mlen;
1413 u_int size, i, tmp;
1414 char s[9], m[MB_LEN_MAX];
1416 ch = yylex_getc();
1418 if (ch >= '4' && ch <= '7') {
1419 yyerror("invalid octal escape");
1420 return (0);
1422 if (ch >= '0' && ch <= '3') {
1423 o2 = yylex_getc();
1424 if (o2 >= '0' && o2 <= '7') {
1425 o3 = yylex_getc();
1426 if (o3 >= '0' && o3 <= '7') {
1427 ch = 64 * (ch - '0') +
1428 8 * (o2 - '0') +
1429 (o3 - '0');
1430 yylex_append1(buf, len, ch);
1431 return (1);
1434 yyerror("invalid octal escape");
1435 return (0);
1438 switch (ch) {
1439 case EOF:
1440 return (0);
1441 case 'a':
1442 ch = '\a';
1443 break;
1444 case 'b':
1445 ch = '\b';
1446 break;
1447 case 'e':
1448 ch = '\033';
1449 break;
1450 case 'f':
1451 ch = '\f';
1452 break;
1453 case 's':
1454 ch = ' ';
1455 break;
1456 case 'v':
1457 ch = '\v';
1458 break;
1459 case 'r':
1460 ch = '\r';
1461 break;
1462 case 'n':
1463 ch = '\n';
1464 break;
1465 case 't':
1466 ch = '\t';
1467 break;
1468 case 'u':
1469 type = 'u';
1470 size = 4;
1471 goto unicode;
1472 case 'U':
1473 type = 'U';
1474 size = 8;
1475 goto unicode;
1478 yylex_append1(buf, len, ch);
1479 return (1);
1481 unicode:
1482 for (i = 0; i < size; i++) {
1483 ch = yylex_getc();
1484 if (ch == EOF || ch == '\n')
1485 return (0);
1486 if (!isxdigit((u_char)ch)) {
1487 yyerror("invalid \\%c argument", type);
1488 return (0);
1490 s[i] = ch;
1492 s[i] = '\0';
1494 if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) ||
1495 (size == 8 && sscanf(s, "%8x", &tmp) != 1)) {
1496 yyerror("invalid \\%c argument", type);
1497 return (0);
1499 mlen = wctomb(m, tmp);
1500 if (mlen <= 0 || mlen > (int)sizeof m) {
1501 yyerror("invalid \\%c argument", type);
1502 return (0);
1504 yylex_append(buf, len, m, mlen);
1505 return (1);
1508 static int
1509 yylex_token_variable(char **buf, size_t *len)
1511 struct environ_entry *envent;
1512 int ch, brackets = 0;
1513 char name[1024];
1514 size_t namelen = 0;
1515 const char *value;
1517 ch = yylex_getc();
1518 if (ch == EOF)
1519 return (0);
1520 if (ch == '{')
1521 brackets = 1;
1522 else {
1523 if (!yylex_is_var(ch, 1)) {
1524 yylex_append1(buf, len, '$');
1525 yylex_ungetc(ch);
1526 return (1);
1528 name[namelen++] = ch;
1531 for (;;) {
1532 ch = yylex_getc();
1533 if (brackets && ch == '}')
1534 break;
1535 if (ch == EOF || !yylex_is_var(ch, 0)) {
1536 if (!brackets) {
1537 yylex_ungetc(ch);
1538 break;
1540 yyerror("invalid environment variable");
1541 return (0);
1543 if (namelen == (sizeof name) - 2) {
1544 yyerror("environment variable is too long");
1545 return (0);
1547 name[namelen++] = ch;
1549 name[namelen] = '\0';
1551 envent = environ_find(global_environ, name);
1552 if (envent != NULL && envent->value != NULL) {
1553 value = envent->value;
1554 log_debug("%s: %s -> %s", __func__, name, value);
1555 yylex_append(buf, len, value, strlen(value));
1557 return (1);
1560 static int
1561 yylex_token_tilde(char **buf, size_t *len)
1563 struct environ_entry *envent;
1564 int ch;
1565 char name[1024];
1566 size_t namelen = 0;
1567 struct passwd *pw;
1568 const char *home = NULL;
1570 for (;;) {
1571 ch = yylex_getc();
1572 if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) {
1573 yylex_ungetc(ch);
1574 break;
1576 if (namelen == (sizeof name) - 2) {
1577 yyerror("user name is too long");
1578 return (0);
1580 name[namelen++] = ch;
1582 name[namelen] = '\0';
1584 if (*name == '\0') {
1585 envent = environ_find(global_environ, "HOME");
1586 if (envent != NULL && *envent->value != '\0')
1587 home = envent->value;
1588 else if ((pw = getpwuid(getuid())) != NULL)
1589 home = pw->pw_dir;
1590 } else {
1591 if ((pw = getpwnam(name)) != NULL)
1592 home = pw->pw_dir;
1594 if (home == NULL)
1595 return (0);
1597 log_debug("%s: ~%s -> %s", __func__, name, home);
1598 yylex_append(buf, len, home, strlen(home));
1599 return (1);
1602 static char *
1603 yylex_token(int ch)
1605 char *buf;
1606 size_t len;
1607 enum { START,
1608 NONE,
1609 DOUBLE_QUOTES,
1610 SINGLE_QUOTES } state = NONE, last = START;
1612 len = 0;
1613 buf = xmalloc(1);
1615 for (;;) {
1616 /* EOF or \n are always the end of the token. */
1617 if (ch == EOF || (state == NONE && ch == '\n'))
1618 break;
1620 /* Whitespace or ; or } ends a token unless inside quotes. */
1621 if ((ch == ' ' || ch == '\t' || ch == ';' || ch == '}') &&
1622 state == NONE)
1623 break;
1626 * Spaces and comments inside quotes after \n are removed but
1627 * the \n is left.
1629 if (ch == '\n' && state != NONE) {
1630 yylex_append1(&buf, &len, '\n');
1631 while ((ch = yylex_getc()) == ' ' || ch == '\t')
1632 /* nothing */;
1633 if (ch != '#')
1634 continue;
1635 ch = yylex_getc();
1636 if (strchr(",#{}:", ch) != NULL) {
1637 yylex_ungetc(ch);
1638 ch = '#';
1639 } else {
1640 while ((ch = yylex_getc()) != '\n' && ch != EOF)
1641 /* nothing */;
1643 continue;
1646 /* \ ~ and $ are expanded except in single quotes. */
1647 if (ch == '\\' && state != SINGLE_QUOTES) {
1648 if (!yylex_token_escape(&buf, &len))
1649 goto error;
1650 goto skip;
1652 if (ch == '~' && last != state && state != SINGLE_QUOTES) {
1653 if (!yylex_token_tilde(&buf, &len))
1654 goto error;
1655 goto skip;
1657 if (ch == '$' && state != SINGLE_QUOTES) {
1658 if (!yylex_token_variable(&buf, &len))
1659 goto error;
1660 goto skip;
1662 if (ch == '}' && state == NONE)
1663 goto error; /* unmatched (matched ones were handled) */
1665 /* ' and " starts or end quotes (and is consumed). */
1666 if (ch == '\'') {
1667 if (state == NONE) {
1668 state = SINGLE_QUOTES;
1669 goto next;
1671 if (state == SINGLE_QUOTES) {
1672 state = NONE;
1673 goto next;
1676 if (ch == '"') {
1677 if (state == NONE) {
1678 state = DOUBLE_QUOTES;
1679 goto next;
1681 if (state == DOUBLE_QUOTES) {
1682 state = NONE;
1683 goto next;
1687 /* Otherwise add the character to the buffer. */
1688 yylex_append1(&buf, &len, ch);
1690 skip:
1691 last = state;
1693 next:
1694 ch = yylex_getc();
1696 yylex_ungetc(ch);
1698 buf[len] = '\0';
1699 log_debug("%s: %s", __func__, buf);
1700 return (buf);
1702 error:
1703 free(buf);
1704 return (NULL);