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