xref: /openbsd-src/usr.bin/tmux/cmd-parse.y (revision 7350f337b9e3eb4461d99580e625c7ef148d107c)
1 /* $OpenBSD: cmd-parse.y,v 1.17 2019/06/18 11:17:40 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 %{
20 
21 #include <sys/types.h>
22 
23 #include <ctype.h>
24 #include <errno.h>
25 #include <pwd.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 
30 #include "tmux.h"
31 
32 static int			 yylex(void);
33 static int			 yyparse(void);
34 static int printflike(1,2)	 yyerror(const char *, ...);
35 
36 static char			*yylex_token(int);
37 static char			*yylex_format(void);
38 
39 struct cmd_parse_scope {
40 	int				 flag;
41 	TAILQ_ENTRY (cmd_parse_scope)	 entry;
42 };
43 
44 struct cmd_parse_command {
45 	char				 *name;
46 	u_int				  line;
47 
48 	int				  argc;
49 	char				**argv;
50 
51 	TAILQ_ENTRY(cmd_parse_command)	  entry;
52 };
53 TAILQ_HEAD(cmd_parse_commands, cmd_parse_command);
54 
55 struct cmd_parse_state {
56 	FILE				*f;
57 
58 	const char			*buf;
59 	size_t				 len;
60 	size_t				 off;
61 
62 	int				 condition;
63 	int				 eol;
64 	int				 eof;
65 	struct cmd_parse_input		*input;
66 	u_int				 escapes;
67 
68 	char				*error;
69 	struct cmd_parse_commands	*commands;
70 
71 	struct cmd_parse_scope		*scope;
72 	TAILQ_HEAD(, cmd_parse_scope)	 stack;
73 };
74 static struct cmd_parse_state parse_state;
75 
76 static char	*cmd_parse_get_error(const char *, u_int, const char *);
77 static void	 cmd_parse_free_command(struct cmd_parse_command *);
78 static struct cmd_parse_commands *cmd_parse_new_commands(void);
79 static void	 cmd_parse_free_commands(struct cmd_parse_commands *);
80 static void	 cmd_parse_print_commands(struct cmd_parse_input *, u_int,
81 		     struct cmd_list *);
82 
83 %}
84 
85 %union
86 {
87 	char					 *token;
88 	struct {
89 		int				  argc;
90 		char				**argv;
91 	} arguments;
92 	int					  flag;
93 	struct {
94 		int				  flag;
95 		struct cmd_parse_commands	 *commands;
96 	} elif;
97 	struct cmd_parse_commands		 *commands;
98 	struct cmd_parse_command		 *command;
99 }
100 
101 %token ERROR
102 %token IF
103 %token ELSE
104 %token ELIF
105 %token ENDIF
106 %token <token> FORMAT TOKEN EQUALS
107 
108 %type <token> argument expanded format
109 %type <arguments> arguments
110 %type <flag> if_open if_elif
111 %type <elif> elif elif1
112 %type <commands> statements statement commands condition condition1
113 %type <command> command
114 
115 %%
116 
117 lines		: /* empty */
118 		| statements
119 		{
120 			struct cmd_parse_state	*ps = &parse_state;
121 
122 			ps->commands = $1;
123 		}
124 
125 statements	: statement '\n'
126 		{
127 			$$ = $1;
128 		}
129 		| statements statement '\n'
130 		{
131 			$$ = $1;
132 			TAILQ_CONCAT($$, $2, entry);
133 			free($2);
134 		}
135 
136 statement	: condition
137 		{
138 			struct cmd_parse_state	*ps = &parse_state;
139 
140 			if (ps->scope == NULL || ps->scope->flag)
141 				$$ = $1;
142 			else {
143 				$$ = cmd_parse_new_commands();
144 				cmd_parse_free_commands($1);
145 			}
146 		}
147 		| assignment
148 		{
149 			$$ = xmalloc (sizeof *$$);
150 			TAILQ_INIT($$);
151 		}
152 		| commands
153 		{
154 			struct cmd_parse_state	*ps = &parse_state;
155 
156 			if (ps->scope == NULL || ps->scope->flag)
157 				$$ = $1;
158 			else {
159 				$$ = cmd_parse_new_commands();
160 				cmd_parse_free_commands($1);
161 			}
162 		}
163 
164 format		: FORMAT
165 		{
166 			$$ = $1;
167 		}
168 		| TOKEN
169 		{
170 			$$ = $1;
171 		}
172 
173 expanded	: format
174 		{
175 			struct cmd_parse_state	*ps = &parse_state;
176 			struct cmd_parse_input	*pi = ps->input;
177 			struct format_tree	*ft;
178 			struct client		*c = pi->c;
179 			struct cmd_find_state	*fs;
180 			int			 flags = FORMAT_NOJOBS;
181 
182 			if (cmd_find_valid_state(&pi->fs))
183 				fs = &pi->fs;
184 			else
185 				fs = NULL;
186 			ft = format_create(NULL, pi->item, FORMAT_NONE, flags);
187 			if (fs != NULL)
188 				format_defaults(ft, c, fs->s, fs->wl, fs->wp);
189 			else
190 				format_defaults(ft, c, NULL, NULL, NULL);
191 
192 			$$ = format_expand(ft, $1);
193 			format_free(ft);
194 			free($1);
195 		}
196 
197 assignment	: /* empty */
198 		| EQUALS
199 		{
200 			struct cmd_parse_state	*ps = &parse_state;
201 			int			 flags = ps->input->flags;
202 
203 			if ((~flags & CMD_PARSE_PARSEONLY) &&
204 			    (ps->scope == NULL || ps->scope->flag))
205 				environ_put(global_environ, $1);
206 			free($1);
207 		}
208 
209 if_open		: IF expanded
210 		{
211 			struct cmd_parse_state	*ps = &parse_state;
212 			struct cmd_parse_scope	*scope;
213 
214 			scope = xmalloc(sizeof *scope);
215 			$$ = scope->flag = format_true($2);
216 			free($2);
217 
218 			if (ps->scope != NULL)
219 				TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry);
220 			ps->scope = scope;
221 		}
222 
223 if_else		: ELSE
224 		{
225 			struct cmd_parse_state	*ps = &parse_state;
226 			struct cmd_parse_scope	*scope;
227 
228 			scope = xmalloc(sizeof *scope);
229 			scope->flag = !ps->scope->flag;
230 
231 			free(ps->scope);
232 			ps->scope = scope;
233 		}
234 
235 if_elif		: ELIF expanded
236 		{
237 			struct cmd_parse_state	*ps = &parse_state;
238 			struct cmd_parse_scope	*scope;
239 
240 			scope = xmalloc(sizeof *scope);
241 			$$ = scope->flag = format_true($2);
242 			free($2);
243 
244 			free(ps->scope);
245 			ps->scope = scope;
246 		}
247 
248 if_close	: ENDIF
249 		{
250 			struct cmd_parse_state	*ps = &parse_state;
251 
252 			free(ps->scope);
253 			ps->scope = TAILQ_FIRST(&ps->stack);
254 			if (ps->scope != NULL)
255 				TAILQ_REMOVE(&ps->stack, ps->scope, entry);
256 		}
257 
258 condition	: if_open '\n' statements if_close
259 		{
260 			if ($1)
261 				$$ = $3;
262 			else {
263 				$$ = cmd_parse_new_commands();
264 				cmd_parse_free_commands($3);
265 			}
266 		}
267 		| if_open '\n' statements if_else '\n' statements if_close
268 		{
269 			if ($1) {
270 				$$ = $3;
271 				cmd_parse_free_commands($6);
272 			} else {
273 				$$ = $6;
274 				cmd_parse_free_commands($3);
275 			}
276 		}
277 		| if_open '\n' statements elif if_close
278 		{
279 			if ($1) {
280 				$$ = $3;
281 				cmd_parse_free_commands($4.commands);
282 			} else if ($4.flag) {
283 				$$ = $4.commands;
284 				cmd_parse_free_commands($3);
285 			} else {
286 				$$ = cmd_parse_new_commands();
287 				cmd_parse_free_commands($3);
288 				cmd_parse_free_commands($4.commands);
289 			}
290 		}
291 		| if_open '\n' statements elif if_else '\n' statements if_close
292 		{
293 			if ($1) {
294 				$$ = $3;
295 				cmd_parse_free_commands($4.commands);
296 				cmd_parse_free_commands($7);
297 			} else if ($4.flag) {
298 				$$ = $4.commands;
299 				cmd_parse_free_commands($3);
300 				cmd_parse_free_commands($7);
301 			} else {
302 				$$ = $7;
303 				cmd_parse_free_commands($3);
304 				cmd_parse_free_commands($4.commands);
305 			}
306 		}
307 
308 elif		: if_elif '\n' statements
309 		{
310 			if ($1) {
311 				$$.flag = 1;
312 				$$.commands = $3;
313 			} else {
314 				$$.flag = 0;
315 				$$.commands = cmd_parse_new_commands();
316 				cmd_parse_free_commands($3);
317 			}
318 		}
319 		| if_elif '\n' statements elif
320 		{
321 			if ($1) {
322 				$$.flag = 1;
323 				$$.commands = $3;
324 				cmd_parse_free_commands($4.commands);
325 			} else if ($4.flag) {
326 				$$.flag = 1;
327 				$$.commands = $4.commands;
328 				cmd_parse_free_commands($3);
329 			} else {
330 				$$.flag = 0;
331 				$$.commands = cmd_parse_new_commands();
332 				cmd_parse_free_commands($3);
333 				cmd_parse_free_commands($4.commands);
334 			}
335 		}
336 
337 commands	: command
338 		{
339 			struct cmd_parse_state	*ps = &parse_state;
340 
341 			$$ = cmd_parse_new_commands();
342 			if (ps->scope == NULL || ps->scope->flag)
343 				TAILQ_INSERT_TAIL($$, $1, entry);
344 			else
345 				cmd_parse_free_command($1);
346 		}
347 		| commands ';'
348 		{
349 			$$ = $1;
350 		}
351 		| commands ';' condition1
352 		{
353 			$$ = $1;
354 			TAILQ_CONCAT($$, $3, entry);
355 			free($3);
356 		}
357 		| commands ';' command
358 		{
359 			struct cmd_parse_state	*ps = &parse_state;
360 
361 			if (ps->scope == NULL || ps->scope->flag) {
362 				$$ = $1;
363 				TAILQ_INSERT_TAIL($$, $3, entry);
364 			} else {
365 				$$ = cmd_parse_new_commands();
366 				cmd_parse_free_commands($1);
367 				cmd_parse_free_command($3);
368 			}
369 		}
370 		| condition1
371 		{
372 			$$ = $1;
373 		}
374 
375 command		: assignment TOKEN
376 		{
377 			struct cmd_parse_state	*ps = &parse_state;
378 
379 			$$ = xcalloc(1, sizeof *$$);
380 			$$->name = $2;
381 			$$->line = ps->input->line;
382 
383 		}
384 		| assignment TOKEN arguments
385 		{
386 			struct cmd_parse_state	*ps = &parse_state;
387 
388 			$$ = xcalloc(1, sizeof *$$);
389 			$$->name = $2;
390 			$$->line = ps->input->line;
391 
392 			$$->argc = $3.argc;
393 			$$->argv = $3.argv;
394 		}
395 
396 condition1	: if_open commands if_close
397 		{
398 			if ($1)
399 				$$ = $2;
400 			else {
401 				$$ = cmd_parse_new_commands();
402 				cmd_parse_free_commands($2);
403 			}
404 		}
405 		| if_open commands if_else commands if_close
406 		{
407 			if ($1) {
408 				$$ = $2;
409 				cmd_parse_free_commands($4);
410 			} else {
411 				$$ = $4;
412 				cmd_parse_free_commands($2);
413 			}
414 		}
415 		| if_open commands elif1 if_close
416 		{
417 			if ($1) {
418 				$$ = $2;
419 				cmd_parse_free_commands($3.commands);
420 			} else if ($3.flag) {
421 				$$ = $3.commands;
422 				cmd_parse_free_commands($2);
423 			} else {
424 				$$ = cmd_parse_new_commands();
425 				cmd_parse_free_commands($2);
426 				cmd_parse_free_commands($3.commands);
427 			}
428 		}
429 		| if_open commands elif1 if_else commands if_close
430 		{
431 			if ($1) {
432 				$$ = $2;
433 				cmd_parse_free_commands($3.commands);
434 				cmd_parse_free_commands($5);
435 			} else if ($3.flag) {
436 				$$ = $3.commands;
437 				cmd_parse_free_commands($2);
438 				cmd_parse_free_commands($5);
439 			} else {
440 				$$ = $5;
441 				cmd_parse_free_commands($2);
442 				cmd_parse_free_commands($3.commands);
443 			}
444 		}
445 
446 elif1		: if_elif commands
447 		{
448 			if ($1) {
449 				$$.flag = 1;
450 				$$.commands = $2;
451 			} else {
452 				$$.flag = 0;
453 				$$.commands = cmd_parse_new_commands();
454 				cmd_parse_free_commands($2);
455 			}
456 		}
457 		| if_elif commands elif1
458 		{
459 			if ($1) {
460 				$$.flag = 1;
461 				$$.commands = $2;
462 				cmd_parse_free_commands($3.commands);
463 			} else if ($3.flag) {
464 				$$.flag = 1;
465 				$$.commands = $3.commands;
466 				cmd_parse_free_commands($2);
467 			} else {
468 				$$.flag = 0;
469 				$$.commands = cmd_parse_new_commands();
470 				cmd_parse_free_commands($2);
471 				cmd_parse_free_commands($3.commands);
472 			}
473 		}
474 
475 arguments	: argument
476 		{
477 			$$.argc = 1;
478 			$$.argv = xreallocarray(NULL, 1, sizeof *$$.argv);
479 
480 			$$.argv[0] = $1;
481 		}
482 		| argument arguments
483 		{
484 			cmd_prepend_argv(&$2.argc, &$2.argv, $1);
485 			free($1);
486 			$$ = $2;
487 		}
488 
489 argument	: TOKEN
490 		{
491 			$$ = $1;
492 		}
493 		| EQUALS
494 		{
495 			$$ = $1;
496 		}
497 
498 %%
499 
500 static char *
501 cmd_parse_get_error(const char *file, u_int line, const char *error)
502 {
503 	char	*s;
504 
505 	if (file == NULL)
506 		s = xstrdup(error);
507 	else
508 		xasprintf (&s, "%s:%u: %s", file, line, error);
509 	return (s);
510 }
511 
512 static void
513 cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line,
514     struct cmd_list *cmdlist)
515 {
516 	char	*s;
517 
518 	if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) {
519 		s = cmd_list_print(cmdlist, 0);
520 		if (pi->file != NULL)
521 			cmdq_print(pi->item, "%s:%u: %s", pi->file, line, s);
522 		else
523 			cmdq_print(pi->item, "%u: %s", line, s);
524 		free(s);
525 	}
526 }
527 
528 static void
529 cmd_parse_free_command(struct cmd_parse_command *cmd)
530 {
531 	free(cmd->name);
532 	cmd_free_argv(cmd->argc, cmd->argv);
533 	free(cmd);
534 }
535 
536 static struct cmd_parse_commands *
537 cmd_parse_new_commands(void)
538 {
539 	struct cmd_parse_commands	*cmds;
540 
541 	cmds = xmalloc(sizeof *cmds);
542 	TAILQ_INIT (cmds);
543 	return (cmds);
544 }
545 
546 static void
547 cmd_parse_free_commands(struct cmd_parse_commands *cmds)
548 {
549 	struct cmd_parse_command	*cmd, *cmd1;
550 
551 	TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) {
552 		TAILQ_REMOVE(cmds, cmd, entry);
553 		cmd_parse_free_command(cmd);
554 	}
555 	free(cmds);
556 }
557 
558 static struct cmd_parse_commands *
559 cmd_parse_run_parser(char **cause)
560 {
561 	struct cmd_parse_state	*ps = &parse_state;
562 	struct cmd_parse_scope	*scope, *scope1;
563 	int			 retval;
564 
565 	ps->commands = NULL;
566 	TAILQ_INIT(&ps->stack);
567 
568 	retval = yyparse();
569 	TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) {
570 		TAILQ_REMOVE(&ps->stack, scope, entry);
571 		free(scope);
572 	}
573 	if (retval != 0) {
574 		*cause = ps->error;
575 		return (NULL);
576 	}
577 
578 	if (ps->commands == NULL)
579 		return (cmd_parse_new_commands());
580 	return (ps->commands);
581 }
582 
583 static struct cmd_parse_commands *
584 cmd_parse_do_file(FILE *f, struct cmd_parse_input *pi, char **cause)
585 {
586 	struct cmd_parse_state	*ps = &parse_state;
587 
588 	memset(ps, 0, sizeof *ps);
589 	ps->input = pi;
590 	ps->f = f;
591 	return (cmd_parse_run_parser(cause));
592 }
593 
594 static struct cmd_parse_commands *
595 cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi,
596     char **cause)
597 {
598 	struct cmd_parse_state	*ps = &parse_state;
599 
600 	memset(ps, 0, sizeof *ps);
601 	ps->input = pi;
602 	ps->buf = buf;
603 	ps->len = len;
604 	return (cmd_parse_run_parser(cause));
605 }
606 
607 static struct cmd_parse_result *
608 cmd_parse_build_commands(struct cmd_parse_commands *cmds,
609     struct cmd_parse_input *pi)
610 {
611 	static struct cmd_parse_result	 pr;
612 	struct cmd_parse_commands	*cmds2;
613 	struct cmd_parse_command	*cmd, *cmd2, *next, *next2, *after;
614 	u_int				 line = UINT_MAX;
615 	int				 i;
616 	struct cmd_list			*cmdlist = NULL, *result;
617 	struct cmd			*add;
618 	char				*alias, *cause, *s;
619 
620 	/* Check for an empty list. */
621 	if (TAILQ_EMPTY(cmds)) {
622 		cmd_parse_free_commands(cmds);
623 		pr.status = CMD_PARSE_EMPTY;
624 		return (&pr);
625 	}
626 
627 	/*
628 	 * Walk the commands and expand any aliases. Each alias is parsed
629 	 * individually to a new command list, any trailing arguments appended
630 	 * to the last command, and all commands inserted into the original
631 	 * command list.
632 	 */
633 	TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) {
634 		alias = cmd_get_alias(cmd->name);
635 		if (alias == NULL)
636 			continue;
637 
638 		line = cmd->line;
639 		log_debug("%s: %u %s = %s", __func__, line, cmd->name, alias);
640 
641 		pi->line = line;
642 		cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause);
643 		free(alias);
644 		if (cmds2 == NULL) {
645 			pr.status = CMD_PARSE_ERROR;
646 			pr.error = cause;
647 			goto out;
648 		}
649 
650 		cmd2 = TAILQ_LAST(cmds2, cmd_parse_commands);
651 		if (cmd2 == NULL) {
652 			TAILQ_REMOVE(cmds, cmd, entry);
653 			cmd_parse_free_command(cmd);
654 			continue;
655 		}
656 		for (i = 0; i < cmd->argc; i++)
657 			cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]);
658 
659 		after = cmd;
660 		TAILQ_FOREACH_SAFE(cmd2, cmds2, entry, next2) {
661 			cmd2->line = line;
662 			TAILQ_REMOVE(cmds2, cmd2, entry);
663 			TAILQ_INSERT_AFTER(cmds, after, cmd2, entry);
664 			after = cmd2;
665 		}
666 		cmd_parse_free_commands(cmds2);
667 
668 		TAILQ_REMOVE(cmds, cmd, entry);
669 		cmd_parse_free_command(cmd);
670 	}
671 
672 	/*
673 	 * Parse each command into a command list. Create a new command list
674 	 * for each line so they get a new group (so the queue knows which ones
675 	 * to remove if a command fails when executed).
676 	 */
677 	result = cmd_list_new();
678 	TAILQ_FOREACH(cmd, cmds, entry) {
679 		log_debug("%s: %u %s", __func__, cmd->line, cmd->name);
680 		cmd_log_argv(cmd->argc, cmd->argv, __func__);
681 
682 		if (cmdlist == NULL || cmd->line != line) {
683 			if (cmdlist != NULL) {
684 				cmd_parse_print_commands(pi, line, cmdlist);
685 				cmd_list_move(result, cmdlist);
686 				cmd_list_free(cmdlist);
687 			}
688 			cmdlist = cmd_list_new();
689 		}
690 		line = cmd->line;
691 
692 		cmd_prepend_argv(&cmd->argc, &cmd->argv, cmd->name);
693 		add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause);
694 		if (add == NULL) {
695 			cmd_list_free(result);
696 			pr.status = CMD_PARSE_ERROR;
697 			pr.error = cmd_parse_get_error(pi->file, line, cause);
698 			free(cause);
699 			goto out;
700 		}
701 		cmd_list_append(cmdlist, add);
702 	}
703 	if (cmdlist != NULL) {
704 		cmd_parse_print_commands(pi, line, cmdlist);
705 		cmd_list_move(result, cmdlist);
706 		cmd_list_free(cmdlist);
707 	}
708 
709 	s = cmd_list_print(result, 0);
710 	log_debug("%s: %s", __func__, s);
711 	free(s);
712 
713 	pr.status = CMD_PARSE_SUCCESS;
714 	pr.cmdlist = result;
715 
716 out:
717 	cmd_parse_free_commands(cmds);
718 
719 	return (&pr);
720 }
721 
722 struct cmd_parse_result *
723 cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
724 {
725 	static struct cmd_parse_result	 pr;
726 	struct cmd_parse_input		 input;
727 	struct cmd_parse_commands	*cmds;
728 	char				*cause;
729 
730 	if (pi == NULL) {
731 		memset(&input, 0, sizeof input);
732 		pi = &input;
733 	}
734 	memset(&pr, 0, sizeof pr);
735 
736 	cmds = cmd_parse_do_file(f, pi, &cause);
737 	if (cmds == NULL) {
738 		pr.status = CMD_PARSE_ERROR;
739 		pr.error = cause;
740 		return (&pr);
741 	}
742 	return (cmd_parse_build_commands(cmds, pi));
743 }
744 
745 struct cmd_parse_result *
746 cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
747 {
748 	static struct cmd_parse_result	 pr;
749 	struct cmd_parse_input		 input;
750 	struct cmd_parse_commands	*cmds;
751 	char				*cause;
752 
753 	if (pi == NULL) {
754 		memset(&input, 0, sizeof input);
755 		pi = &input;
756 	}
757 	memset(&pr, 0, sizeof pr);
758 
759 	if (*s == '\0') {
760 		pr.status = CMD_PARSE_EMPTY;
761 		pr.cmdlist = NULL;
762 		pr.error = NULL;
763 		return (&pr);
764 	}
765 
766 	cmds = cmd_parse_do_buffer(s, strlen(s), pi, &cause);
767 	if (cmds == NULL) {
768 		pr.status = CMD_PARSE_ERROR;
769 		pr.error = cause;
770 		return (&pr);
771 	}
772 	return (cmd_parse_build_commands(cmds, pi));
773 }
774 
775 struct cmd_parse_result *
776 cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi)
777 {
778 	struct cmd_parse_input		  input;
779 	struct cmd_parse_commands	 *cmds;
780 	struct cmd_parse_command	 *cmd;
781 	char				**copy, **new_argv;
782 	size_t				  size;
783 	int				  i, last, new_argc;
784 
785 	/*
786 	 * The commands are already split up into arguments, so just separate
787 	 * into a set of commands by ';'.
788 	 */
789 
790 	if (pi == NULL) {
791 		memset(&input, 0, sizeof input);
792 		pi = &input;
793 	}
794 	cmd_log_argv(argc, argv, "%s", __func__);
795 
796 	cmds = cmd_parse_new_commands();
797 	copy = cmd_copy_argv(argc, argv);
798 
799 	last = 0;
800 	for (i = 0; i < argc; i++) {
801 		size = strlen(copy[i]);
802 		if (size == 0 || copy[i][size - 1] != ';')
803 			continue;
804 		copy[i][--size] = '\0';
805 		if (size > 0 && copy[i][size - 1] == '\\') {
806 			copy[i][size - 1] = ';';
807 			continue;
808 		}
809 
810 		new_argc = i - last;
811 		new_argv = copy + last;
812 		if (size != 0)
813 			new_argc++;
814 
815 		if (new_argc != 0) {
816 			cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__,
817 			    i);
818 
819 			cmd = xcalloc(1, sizeof *cmd);
820 			cmd->name = xstrdup(new_argv[0]);
821 			cmd->line = pi->line;
822 
823 			cmd->argc = new_argc - 1;
824 			cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1);
825 
826 			TAILQ_INSERT_TAIL(cmds, cmd, entry);
827 		}
828 
829 		last = i + 1;
830 	}
831 	if (last != argc) {
832 		new_argv = copy + last;
833 		new_argc = argc - last;
834 
835 		if (new_argc != 0) {
836 			cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__,
837 			    last);
838 
839 			cmd = xcalloc(1, sizeof *cmd);
840 			cmd->name = xstrdup(new_argv[0]);
841 			cmd->line = pi->line;
842 
843 			cmd->argc = new_argc - 1;
844 			cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1);
845 
846 			TAILQ_INSERT_TAIL(cmds, cmd, entry);
847 		}
848 	}
849 
850 	cmd_free_argv(argc, copy);
851 	return (cmd_parse_build_commands(cmds, pi));
852 }
853 
854 static int printflike(1, 2)
855 yyerror(const char *fmt, ...)
856 {
857 	struct cmd_parse_state	*ps = &parse_state;
858 	struct cmd_parse_input	*pi = ps->input;
859 	va_list			 ap;
860 	char			*error;
861 
862 	if (ps->error != NULL)
863 		return (0);
864 
865 	va_start(ap, fmt);
866 	xvasprintf(&error, fmt, ap);
867 	va_end(ap);
868 
869 	ps->error = cmd_parse_get_error(pi->file, pi->line, error);
870 	free(error);
871 	return (0);
872 }
873 
874 static int
875 yylex_is_var(char ch, int first)
876 {
877 	if (ch == '=')
878 		return (0);
879 	if (first && isdigit((u_char)ch))
880 		return (0);
881 	return (isalnum((u_char)ch) || ch == '_');
882 }
883 
884 static void
885 yylex_append(char **buf, size_t *len, const char *add, size_t addlen)
886 {
887 	if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen)
888 		fatalx("buffer is too big");
889 	*buf = xrealloc(*buf, (*len) + 1 + addlen);
890 	memcpy((*buf) + *len, add, addlen);
891 	(*len) += addlen;
892 }
893 
894 static void
895 yylex_append1(char **buf, size_t *len, char add)
896 {
897 	yylex_append(buf, len, &add, 1);
898 }
899 
900 static int
901 yylex_getc1(void)
902 {
903 	struct cmd_parse_state	*ps = &parse_state;
904 	int			 ch;
905 
906 	if (ps->f != NULL)
907 		ch = getc(ps->f);
908 	else {
909 		if (ps->off == ps->len)
910 			ch = EOF;
911 		else
912 			ch = ps->buf[ps->off++];
913 	}
914 	return (ch);
915 }
916 
917 static void
918 yylex_ungetc(int ch)
919 {
920 	struct cmd_parse_state	*ps = &parse_state;
921 
922 	if (ps->f != NULL)
923 		ungetc(ch, ps->f);
924 	else if (ps->off > 0 && ch != EOF)
925 		ps->off--;
926 }
927 
928 static int
929 yylex_getc(void)
930 {
931 	struct cmd_parse_state	*ps = &parse_state;
932 	int			 ch;
933 
934 	if (ps->escapes != 0) {
935 		ps->escapes--;
936 		return ('\\');
937 	}
938 	for (;;) {
939 		ch = yylex_getc1();
940 		if (ch == '\\') {
941 			ps->escapes++;
942 			continue;
943 		}
944 		if (ch == '\n' && (ps->escapes % 2) == 1) {
945 			ps->input->line++;
946 			ps->escapes--;
947 			continue;
948 		}
949 
950 		if (ps->escapes != 0) {
951 			yylex_ungetc(ch);
952 			ps->escapes--;
953 			return ('\\');
954 		}
955 		return (ch);
956 	}
957 }
958 
959 static char *
960 yylex_get_word(int ch)
961 {
962 	char	*buf;
963 	size_t	 len;
964 
965 	len = 0;
966 	buf = xmalloc(1);
967 
968 	do
969 		yylex_append1(&buf, &len, ch);
970 	while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL);
971 	yylex_ungetc(ch);
972 
973 	buf[len] = '\0';
974 	log_debug("%s: %s", __func__, buf);
975 	return (buf);
976 }
977 
978 static int
979 yylex(void)
980 {
981 	struct cmd_parse_state	*ps = &parse_state;
982 	char			*token, *cp;
983 	int			 ch, next, condition;
984 
985 	if (ps->eol)
986 		ps->input->line++;
987 	ps->eol = 0;
988 
989 	condition = ps->condition;
990 	ps->condition = 0;
991 
992 	for (;;) {
993 		ch = yylex_getc();
994 
995 		if (ch == EOF) {
996 			/*
997 			 * Ensure every file or string is terminated by a
998 			 * newline. This keeps the parser simpler and avoids
999 			 * having to add a newline to each string.
1000 			 */
1001 			if (ps->eof)
1002 				break;
1003 			ps->eof = 1;
1004 			return ('\n');
1005 		}
1006 
1007 		if (ch == ' ' || ch == '\t') {
1008 			/*
1009 			 * Ignore whitespace.
1010 			 */
1011 			continue;
1012 		}
1013 
1014 		if (ch == '\n') {
1015 			/*
1016 			 * End of line. Update the line number.
1017 			 */
1018 			ps->eol = 1;
1019 			return ('\n');
1020 		}
1021 
1022 		if (ch == ';') {
1023 			/*
1024 			 * A semicolon is itself.
1025 			 */
1026 			return (';');
1027 		}
1028 
1029 		if (ch == '#') {
1030 			/*
1031 			 * #{ after a condition opens a format; anything else
1032 			 * is a comment, ignore up to the end of the line.
1033 			 */
1034 			next = yylex_getc();
1035 			if (condition && next == '{') {
1036 				yylval.token = yylex_format();
1037 				if (yylval.token == NULL)
1038 					return (ERROR);
1039 				return (FORMAT);
1040 			}
1041 			while (next != '\n' && next != EOF)
1042 				next = yylex_getc();
1043 			if (next == '\n') {
1044 				ps->input->line++;
1045 				return ('\n');
1046 			}
1047 			continue;
1048 		}
1049 
1050 		if (ch == '%') {
1051 			/*
1052 			 * % is a condition unless it is all % or all numbers,
1053 			 * then it is a token.
1054 			 */
1055 			yylval.token = yylex_get_word('%');
1056 			for (cp = yylval.token; *cp != '\0'; cp++) {
1057 				if (*cp != '%' && !isdigit((u_char)*cp))
1058 					break;
1059 			}
1060 			if (*cp == '\0')
1061 				return (TOKEN);
1062 			ps->condition = 1;
1063 			if (strcmp(yylval.token, "%if") == 0) {
1064 				free(yylval.token);
1065 				return (IF);
1066 			}
1067 			if (strcmp(yylval.token, "%else") == 0) {
1068 				free(yylval.token);
1069 				return (ELSE);
1070 			}
1071 			if (strcmp(yylval.token, "%elif") == 0) {
1072 				free(yylval.token);
1073 				return (ELIF);
1074 			}
1075 			if (strcmp(yylval.token, "%endif") == 0) {
1076 				free(yylval.token);
1077 				return (ENDIF);
1078 			}
1079 			free(yylval.token);
1080 			return (ERROR);
1081 		}
1082 
1083 		/*
1084 		 * Otherwise this is a token.
1085 		 */
1086 		token = yylex_token(ch);
1087 		if (token == NULL)
1088 			return (ERROR);
1089 		yylval.token = token;
1090 
1091 		if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
1092 			for (cp = token + 1; *cp != '='; cp++) {
1093 				if (!yylex_is_var(*cp, 0))
1094 					break;
1095 			}
1096 			if (*cp == '=')
1097 				return (EQUALS);
1098 		}
1099 		return (TOKEN);
1100 	}
1101 	return (0);
1102 }
1103 
1104 static char *
1105 yylex_format(void)
1106 {
1107 	char	*buf;
1108 	size_t	 len;
1109 	int	 ch, brackets = 1;
1110 
1111 	len = 0;
1112 	buf = xmalloc(1);
1113 
1114 	yylex_append(&buf, &len, "#{", 2);
1115 	for (;;) {
1116 		if ((ch = yylex_getc()) == EOF || ch == '\n')
1117 			goto error;
1118 		if (ch == '#') {
1119 			if ((ch = yylex_getc()) == EOF || ch == '\n')
1120 				goto error;
1121 			if (ch == '{')
1122 				brackets++;
1123 			yylex_append1(&buf, &len, '#');
1124 		} else if (ch == '}') {
1125 			if (brackets != 0 && --brackets == 0) {
1126 				yylex_append1(&buf, &len, ch);
1127 				break;
1128 			}
1129 		}
1130 		yylex_append1(&buf, &len, ch);
1131 	}
1132 	if (brackets != 0)
1133 		goto error;
1134 
1135 	buf[len] = '\0';
1136 	log_debug("%s: %s", __func__, buf);
1137 	return (buf);
1138 
1139 error:
1140 	free(buf);
1141 	return (NULL);
1142 }
1143 
1144 static int
1145 yylex_token_escape(char **buf, size_t *len)
1146 {
1147 	int			 ch, type, o2, o3;
1148 	u_int			 size, i, tmp;
1149 	char			 s[9];
1150 	struct utf8_data	 ud;
1151 
1152 	ch = yylex_getc();
1153 
1154 	if (ch >= '4' && ch <= '7') {
1155 		yyerror("invalid octal escape");
1156 		return (0);
1157 	}
1158 	if (ch >= '0' && ch <= '3') {
1159 		o2 = yylex_getc();
1160 		if (o2 >= '0' && o2 <= '7') {
1161 			o3 = yylex_getc();
1162 			if (o3 >= '0' && o3 <= '7') {
1163 				ch = 64 * (ch - '0') +
1164 				      8 * (o2 - '0') +
1165 				          (o3 - '0');
1166 				yylex_append1(buf, len, ch);
1167 				return (1);
1168 			}
1169 		}
1170 		yyerror("invalid octal escape");
1171 		return (0);
1172 	}
1173 
1174 	switch (ch) {
1175 	case EOF:
1176 		return (0);
1177 	case 'a':
1178 		ch = '\a';
1179 		break;
1180 	case 'b':
1181 		ch = '\b';
1182 		break;
1183 	case 'e':
1184 		ch = '\033';
1185 		break;
1186 	case 'f':
1187 		ch = '\f';
1188 		break;
1189 	case 's':
1190 		ch = ' ';
1191 		break;
1192 	case 'v':
1193 		ch = '\v';
1194 		break;
1195 	case 'r':
1196 		ch = '\r';
1197 		break;
1198 	case 'n':
1199 		ch = '\n';
1200 		break;
1201 	case 't':
1202 		ch = '\t';
1203 		break;
1204 	case 'u':
1205 		type = 'u';
1206 		size = 4;
1207 		goto unicode;
1208 	case 'U':
1209 		type = 'U';
1210 		size = 8;
1211 		goto unicode;
1212 	}
1213 
1214 	yylex_append1(buf, len, ch);
1215 	return (1);
1216 
1217 unicode:
1218 	for (i = 0; i < size; i++) {
1219 		ch = yylex_getc();
1220 		if (ch == EOF || ch == '\n')
1221 			return (0);
1222 		if (!isxdigit((u_char)ch)) {
1223 			yyerror("invalid \\%c argument", type);
1224 			return (0);
1225 		}
1226 		s[i] = ch;
1227 	}
1228 	s[i] = '\0';
1229 
1230 	if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) ||
1231 	    (size == 8 && sscanf(s, "%8x", &tmp) != 1)) {
1232 		yyerror("invalid \\%c argument", type);
1233 		return (0);
1234 	}
1235 	if (utf8_split(tmp, &ud) != UTF8_DONE) {
1236 		yyerror("invalid \\%c argument", type);
1237 		return (0);
1238 	}
1239 	yylex_append(buf, len, ud.data, ud.size);
1240 	return (1);
1241 }
1242 
1243 static int
1244 yylex_token_variable(char **buf, size_t *len)
1245 {
1246 	struct environ_entry	*envent;
1247 	int			 ch, brackets = 0;
1248 	char			 name[BUFSIZ];
1249 	size_t			 namelen = 0;
1250 	const char		*value;
1251 
1252 	ch = yylex_getc();
1253 	if (ch == EOF)
1254 		return (0);
1255 	if (ch == '{')
1256 		brackets = 1;
1257 	else {
1258 		if (!yylex_is_var(ch, 1)) {
1259 			yylex_append1(buf, len, '$');
1260 			yylex_ungetc(ch);
1261 			return (1);
1262 		}
1263 		name[namelen++] = ch;
1264 	}
1265 
1266 	for (;;) {
1267 		ch = yylex_getc();
1268 		if (brackets && ch == '}')
1269 			break;
1270 		if (ch == EOF || !yylex_is_var(ch, 0)) {
1271 			if (!brackets) {
1272 				yylex_ungetc(ch);
1273 				break;
1274 			}
1275 			yyerror("invalid environment variable");
1276 			return (0);
1277 		}
1278 		if (namelen == (sizeof name) - 2) {
1279 			yyerror("environment variable is too long");
1280 			return (0);
1281 		}
1282 		name[namelen++] = ch;
1283 	}
1284 	name[namelen] = '\0';
1285 
1286 	envent = environ_find(global_environ, name);
1287 	if (envent != NULL) {
1288 		value = envent->value;
1289 		log_debug("%s: %s -> %s", __func__, name, value);
1290 		yylex_append(buf, len, value, strlen(value));
1291 	}
1292 	return (1);
1293 }
1294 
1295 static int
1296 yylex_token_tilde(char **buf, size_t *len)
1297 {
1298 	struct environ_entry	*envent;
1299 	int			 ch;
1300 	char			 name[BUFSIZ];
1301 	size_t			 namelen = 0;
1302 	struct passwd		*pw;
1303 	const char		*home = NULL;
1304 
1305 	for (;;) {
1306 		ch = yylex_getc();
1307 		if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) {
1308 			yylex_ungetc(ch);
1309 			break;
1310 		}
1311 		if (namelen == (sizeof name) - 2) {
1312 			yyerror("user name is too long");
1313 			return (0);
1314 		}
1315 		name[namelen++] = ch;
1316 	}
1317 	name[namelen] = '\0';
1318 
1319 	if (*name == '\0') {
1320 		envent = environ_find(global_environ, "HOME");
1321 		if (envent != NULL && *envent->value != '\0')
1322 			home = envent->value;
1323 		else if ((pw = getpwuid(getuid())) != NULL)
1324 			home = pw->pw_dir;
1325 	} else {
1326 		if ((pw = getpwnam(name)) != NULL)
1327 			home = pw->pw_dir;
1328 	}
1329 	if (home == NULL)
1330 		return (0);
1331 
1332 	log_debug("%s: ~%s -> %s", __func__, name, home);
1333 	yylex_append(buf, len, home, strlen(home));
1334 	return (1);
1335 }
1336 
1337 static int
1338 yylex_token_brace(char **buf, size_t *len)
1339 {
1340 	struct cmd_parse_state	*ps = &parse_state;
1341 	int 			 ch, lines = 0, nesting = 1, escape = 0;
1342 	int			 quote = '\0', token = 0;
1343 
1344 	/*
1345 	 * Extract a string up to the matching unquoted '}', including newlines
1346 	 * and handling nested braces.
1347 	 *
1348 	 * To detect the final and intermediate braces which affect the nesting
1349 	 * depth, we scan the input as if it was a tmux config file, and ignore
1350 	 * braces which would be considered quoted, escaped, or in a comment.
1351 	 *
1352 	 * We update the token state after every character because '#' begins a
1353 	 * comment only when it begins a token. For simplicity, we treat an
1354 	 * unquoted directive format as comment.
1355 	 *
1356 	 * The result is verbatim copy of the input excluding the final brace.
1357 	 */
1358 
1359 	for (ch = yylex_getc1(); ch != EOF; ch = yylex_getc1()) {
1360 		yylex_append1(buf, len, ch);
1361 		if (ch == '\n')
1362 			lines++;
1363 
1364 		/*
1365 		 * If the previous character was a backslash (escape is set),
1366 		 * escape anything if unquoted or in double quotes, otherwise
1367 		 * escape only '\n' and '\\'.
1368 		 */
1369 		if (escape &&
1370 		    (quote == '\0' ||
1371 		    quote == '"' ||
1372 		    ch == '\n' ||
1373 		    ch == '\\')) {
1374 			escape = 0;
1375 			if (ch != '\n')
1376 				token = 1;
1377 			continue;
1378 		}
1379 
1380 		/*
1381 		 * The character is not escaped. If it is a backslash, set the
1382 		 * escape flag.
1383 		 */
1384 		if (ch == '\\') {
1385 			escape = 1;
1386 			continue;
1387 		}
1388 		escape = 0;
1389 
1390 		/* A newline always resets to unquoted. */
1391 		if (ch == '\n') {
1392 			quote = token = 0;
1393 			continue;
1394 		}
1395 
1396 		if (quote) {
1397 			/*
1398 			 * Inside quotes or comment. Check if this is the
1399 			 * closing quote.
1400 			 */
1401 			if (ch == quote && quote != '#')
1402 				quote = 0;
1403 			token = 1;  /* token continues regardless */
1404 		} else {
1405 			/* Not inside quotes or comment. */
1406 			switch (ch) {
1407 			case '"':
1408 			case '\'':
1409 			case '#':
1410 				/* Beginning of quote or maybe comment. */
1411 				if (ch != '#' || !token)
1412 					quote = ch;
1413 				token = 1;
1414 				break;
1415 			case ' ':
1416 			case '\t':
1417 			case ';':
1418 				/* Delimiter - token resets. */
1419 				token = 0;
1420 				break;
1421 			case '{':
1422 				nesting++;
1423 				token = 0; /* new commands set - token resets */
1424 				break;
1425 			case '}':
1426 				nesting--;
1427 				token = 1;  /* same as after quotes */
1428 				if (nesting == 0) {
1429 					(*len)--; /* remove closing } */
1430 					ps->input->line += lines;
1431 					return (1);
1432 				}
1433 				break;
1434 			default:
1435 				token = 1;
1436 				break;
1437 			}
1438 		}
1439 	}
1440 
1441 	/*
1442 	 * Update line count after error as reporting the opening line is more
1443 	 * useful than EOF.
1444 	 */
1445 	yyerror("unterminated brace string");
1446 	ps->input->line += lines;
1447 	return (0);
1448 }
1449 
1450 static char *
1451 yylex_token(int ch)
1452 {
1453 	char			*buf;
1454 	size_t			 len;
1455 	enum { START,
1456 	       NONE,
1457 	       DOUBLE_QUOTES,
1458 	       SINGLE_QUOTES }	 state = NONE, last = START;
1459 
1460 	len = 0;
1461 	buf = xmalloc(1);
1462 
1463 	for (;;) {
1464 		/*
1465 		 * EOF or \n are always the end of the token. If inside quotes
1466 		 * they are an error.
1467 		 */
1468 		if (ch == EOF || ch == '\n') {
1469 			if (state != NONE)
1470 				goto error;
1471 			break;
1472 		}
1473 
1474 		/* Whitespace or ; ends a token unless inside quotes. */
1475 		if ((ch == ' ' || ch == '\t' || ch == ';') && state == NONE)
1476 			break;
1477 
1478 		/*
1479 		 * \ ~ and $ are expanded except in single quotes.
1480 		 */
1481 		if (ch == '\\' && state != SINGLE_QUOTES) {
1482 			if (!yylex_token_escape(&buf, &len))
1483 				goto error;
1484 			goto skip;
1485 		}
1486 		if (ch == '~' && last != state && state != SINGLE_QUOTES) {
1487 			if (!yylex_token_tilde(&buf, &len))
1488 				goto error;
1489 			goto skip;
1490 		}
1491 		if (ch == '$' && state != SINGLE_QUOTES) {
1492 			if (!yylex_token_variable(&buf, &len))
1493 				goto error;
1494 			goto skip;
1495 		}
1496 		if (ch == '{' && state == NONE) {
1497 			if (!yylex_token_brace(&buf, &len))
1498 				goto error;
1499 			goto skip;
1500 		}
1501 		if (ch == '}' && state == NONE)
1502 			goto error;  /* unmatched (matched ones were handled) */
1503 
1504 		/*
1505 		 * ' and " starts or end quotes (and is consumed).
1506 		 */
1507 		if (ch == '\'') {
1508 			if (state == NONE) {
1509 				state = SINGLE_QUOTES;
1510 				goto next;
1511 			}
1512 			if (state == SINGLE_QUOTES) {
1513 				state = NONE;
1514 				goto next;
1515 			}
1516 		}
1517 		if (ch == '"') {
1518 			if (state == NONE) {
1519 				state = DOUBLE_QUOTES;
1520 				goto next;
1521 			}
1522 			if (state == DOUBLE_QUOTES) {
1523 				state = NONE;
1524 				goto next;
1525 			}
1526 		}
1527 
1528 		/*
1529 		 * Otherwise add the character to the buffer.
1530 		 */
1531 		yylex_append1(&buf, &len, ch);
1532 
1533 	skip:
1534 		last = state;
1535 
1536 	next:
1537 		ch = yylex_getc();
1538 	}
1539 	yylex_ungetc(ch);
1540 
1541 	buf[len] = '\0';
1542 	log_debug("%s: %s", __func__, buf);
1543 	return (buf);
1544 
1545 error:
1546 	free(buf);
1547 	return (NULL);
1548 }
1549