xref: /openbsd-src/usr.bin/tmux/cmd-parse.y (revision 79be0cc0595cc4c8ca37f0f6104150e67db7cde9)
1*79be0cc0Snicm /* $OpenBSD: cmd-parse.y,v 1.53 2025/01/13 08:58:34 nicm Exp $ */
2df6ab229Snicm 
3df6ab229Snicm /*
4df6ab229Snicm  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
5df6ab229Snicm  *
6df6ab229Snicm  * Permission to use, copy, modify, and distribute this software for any
7df6ab229Snicm  * purpose with or without fee is hereby granted, provided that the above
8df6ab229Snicm  * copyright notice and this permission notice appear in all copies.
9df6ab229Snicm  *
10df6ab229Snicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11df6ab229Snicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12df6ab229Snicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13df6ab229Snicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14df6ab229Snicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15df6ab229Snicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16df6ab229Snicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17df6ab229Snicm  */
18df6ab229Snicm 
19df6ab229Snicm %{
20df6ab229Snicm 
21df6ab229Snicm #include <sys/types.h>
22df6ab229Snicm 
23df6ab229Snicm #include <ctype.h>
24df6ab229Snicm #include <errno.h>
25df6ab229Snicm #include <pwd.h>
26fae03c8aSnicm #include <stdlib.h>
27df6ab229Snicm #include <string.h>
28df6ab229Snicm #include <unistd.h>
296852c63bSnicm #include <wchar.h>
30df6ab229Snicm 
31df6ab229Snicm #include "tmux.h"
32df6ab229Snicm 
33df6ab229Snicm static int			 yylex(void);
34df6ab229Snicm static int			 yyparse(void);
35df6ab229Snicm static int printflike(1,2)	 yyerror(const char *, ...);
36df6ab229Snicm 
37df6ab229Snicm static char			*yylex_token(int);
38df6ab229Snicm static char			*yylex_format(void);
39df6ab229Snicm 
40df6ab229Snicm struct cmd_parse_scope {
41df6ab229Snicm 	int				 flag;
42df6ab229Snicm 	TAILQ_ENTRY (cmd_parse_scope)	 entry;
43df6ab229Snicm };
44df6ab229Snicm 
45b6e38b61Snicm enum cmd_parse_argument_type {
46b6e38b61Snicm 	CMD_PARSE_STRING,
47d8b32369Snicm 	CMD_PARSE_COMMANDS,
48d8b32369Snicm 	CMD_PARSE_PARSED_COMMANDS
49b6e38b61Snicm };
50b6e38b61Snicm 
51b6e38b61Snicm struct cmd_parse_argument {
52b6e38b61Snicm 	enum cmd_parse_argument_type	 type;
53b6e38b61Snicm 	char				*string;
54b6e38b61Snicm 	struct cmd_parse_commands	*commands;
55d8b32369Snicm 	struct cmd_list			*cmdlist;
56b6e38b61Snicm 
57b6e38b61Snicm 	TAILQ_ENTRY(cmd_parse_argument)	 entry;
58b6e38b61Snicm };
59b6e38b61Snicm TAILQ_HEAD(cmd_parse_arguments, cmd_parse_argument);
60b6e38b61Snicm 
61df6ab229Snicm struct cmd_parse_command {
62df6ab229Snicm 	u_int				 line;
63b6e38b61Snicm 	struct cmd_parse_arguments	 arguments;
64df6ab229Snicm 
65df6ab229Snicm 	TAILQ_ENTRY(cmd_parse_command)	 entry;
66df6ab229Snicm };
67df6ab229Snicm TAILQ_HEAD(cmd_parse_commands, cmd_parse_command);
68df6ab229Snicm 
69df6ab229Snicm struct cmd_parse_state {
70df6ab229Snicm 	FILE				*f;
7173530c3cSnicm 
7273530c3cSnicm 	const char			*buf;
7373530c3cSnicm 	size_t				 len;
7473530c3cSnicm 	size_t				 off;
7573530c3cSnicm 
7609ba5f57Snicm 	int				 condition;
7747394861Snicm 	int				 eol;
78df6ab229Snicm 	int				 eof;
79df6ab229Snicm 	struct cmd_parse_input		*input;
80df6ab229Snicm 	u_int				 escapes;
81df6ab229Snicm 
82df6ab229Snicm 	char				*error;
835d24dd80Snicm 	struct cmd_parse_commands	*commands;
84df6ab229Snicm 
85df6ab229Snicm 	struct cmd_parse_scope		*scope;
86df6ab229Snicm 	TAILQ_HEAD(, cmd_parse_scope)	 stack;
87df6ab229Snicm };
88df6ab229Snicm static struct cmd_parse_state parse_state;
89df6ab229Snicm 
90df6ab229Snicm static char	*cmd_parse_get_error(const char *, u_int, const char *);
91df6ab229Snicm static void	 cmd_parse_free_command(struct cmd_parse_command *);
925d24dd80Snicm static struct cmd_parse_commands *cmd_parse_new_commands(void);
93df6ab229Snicm static void	 cmd_parse_free_commands(struct cmd_parse_commands *);
94287cddb6Snicm static void	 cmd_parse_build_commands(struct cmd_parse_commands *,
95287cddb6Snicm 		     struct cmd_parse_input *, struct cmd_parse_result *);
96287cddb6Snicm static void	 cmd_parse_print_commands(struct cmd_parse_input *,
975304b409Snicm 		     struct cmd_list *);
98df6ab229Snicm 
99df6ab229Snicm %}
100df6ab229Snicm 
101df6ab229Snicm %union
102df6ab229Snicm {
103df6ab229Snicm 	char					 *token;
104b6e38b61Snicm 	struct cmd_parse_arguments		 *arguments;
105b6e38b61Snicm 	struct cmd_parse_argument		 *argument;
106df6ab229Snicm 	int					  flag;
107df6ab229Snicm 	struct {
108df6ab229Snicm 		int				  flag;
1095d24dd80Snicm 		struct cmd_parse_commands	 *commands;
110df6ab229Snicm 	} elif;
1115d24dd80Snicm 	struct cmd_parse_commands		 *commands;
112df6ab229Snicm 	struct cmd_parse_command		 *command;
113df6ab229Snicm }
114df6ab229Snicm 
115df6ab229Snicm %token ERROR
116d6f6a5d2Snicm %token HIDDEN
117df6ab229Snicm %token IF
118df6ab229Snicm %token ELSE
119df6ab229Snicm %token ELIF
120df6ab229Snicm %token ENDIF
121df6ab229Snicm %token <token> FORMAT TOKEN EQUALS
122df6ab229Snicm 
123b6e38b61Snicm %type <token> expanded format
124df6ab229Snicm %type <arguments> arguments
125b6e38b61Snicm %type <argument> argument
126df6ab229Snicm %type <flag> if_open if_elif
127df6ab229Snicm %type <elif> elif elif1
1282a840c62Snicm %type <commands> argument_statements statements statement
1292a840c62Snicm %type <commands> commands condition condition1
130df6ab229Snicm %type <command> command
131df6ab229Snicm 
132df6ab229Snicm %%
133df6ab229Snicm 
134df6ab229Snicm lines		: /* empty */
135df6ab229Snicm 		| statements
136df6ab229Snicm 		{
137df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
138df6ab229Snicm 
1395d24dd80Snicm 			ps->commands = $1;
140df6ab229Snicm 		}
141df6ab229Snicm 
142df6ab229Snicm statements	: statement '\n'
143df6ab229Snicm 		{
1445d24dd80Snicm 			$$ = $1;
145df6ab229Snicm 		}
146df6ab229Snicm 		| statements statement '\n'
147df6ab229Snicm 		{
1485d24dd80Snicm 			$$ = $1;
1495d24dd80Snicm 			TAILQ_CONCAT($$, $2, entry);
1505d24dd80Snicm 			free($2);
151df6ab229Snicm 		}
152df6ab229Snicm 
153bec5bc45Snicm statement	: /* empty */
154bec5bc45Snicm 		{
155bec5bc45Snicm 			$$ = xmalloc (sizeof *$$);
156bec5bc45Snicm 			TAILQ_INIT($$);
157bec5bc45Snicm 		}
158d6f6a5d2Snicm 		| hidden_assignment
159d6f6a5d2Snicm 		{
160d6f6a5d2Snicm 			$$ = xmalloc (sizeof *$$);
161d6f6a5d2Snicm 			TAILQ_INIT($$);
162d6f6a5d2Snicm 		}
163bec5bc45Snicm 		| condition
164df6ab229Snicm 		{
165df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
166df6ab229Snicm 
167df6ab229Snicm 			if (ps->scope == NULL || ps->scope->flag)
1685d24dd80Snicm 				$$ = $1;
1695d24dd80Snicm 			else {
1705d24dd80Snicm 				$$ = cmd_parse_new_commands();
1715d24dd80Snicm 				cmd_parse_free_commands($1);
1725d24dd80Snicm 			}
173df6ab229Snicm 		}
174df6ab229Snicm 		| commands
175df6ab229Snicm 		{
176df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
177df6ab229Snicm 
178df6ab229Snicm 			if (ps->scope == NULL || ps->scope->flag)
1795d24dd80Snicm 				$$ = $1;
1805d24dd80Snicm 			else {
1815d24dd80Snicm 				$$ = cmd_parse_new_commands();
1825d24dd80Snicm 				cmd_parse_free_commands($1);
1835d24dd80Snicm 			}
184df6ab229Snicm 		}
185df6ab229Snicm 
18609ba5f57Snicm format		: FORMAT
18709ba5f57Snicm 		{
18809ba5f57Snicm 			$$ = $1;
18909ba5f57Snicm 		}
19009ba5f57Snicm 		| TOKEN
19109ba5f57Snicm 		{
19209ba5f57Snicm 			$$ = $1;
19309ba5f57Snicm 		}
19409ba5f57Snicm 
19509ba5f57Snicm expanded	: format
196df6ab229Snicm 		{
197df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
198df6ab229Snicm 			struct cmd_parse_input	*pi = ps->input;
199df6ab229Snicm 			struct format_tree	*ft;
200df6ab229Snicm 			struct client		*c = pi->c;
201a267b926Snicm 			struct cmd_find_state	*fsp;
202a267b926Snicm 			struct cmd_find_state	 fs;
203df6ab229Snicm 			int			 flags = FORMAT_NOJOBS;
204df6ab229Snicm 
205df6ab229Snicm 			if (cmd_find_valid_state(&pi->fs))
206a267b926Snicm 				fsp = &pi->fs;
207a267b926Snicm 			else {
208a267b926Snicm 				cmd_find_from_client(&fs, c, 0);
209a267b926Snicm 				fsp = &fs;
210a267b926Snicm 			}
211df6ab229Snicm 			ft = format_create(NULL, pi->item, FORMAT_NONE, flags);
212a267b926Snicm 			format_defaults(ft, c, fsp->s, fsp->wl, fsp->wp);
213df6ab229Snicm 
214df6ab229Snicm 			$$ = format_expand(ft, $1);
215df6ab229Snicm 			format_free(ft);
216df6ab229Snicm 			free($1);
217df6ab229Snicm 		}
218df6ab229Snicm 
219bec5bc45Snicm optional_assignment	: /* empty */
220bec5bc45Snicm 			| assignment
221bec5bc45Snicm 
222bec5bc45Snicm assignment	: EQUALS
223df6ab229Snicm 		{
224df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
225df6ab229Snicm 			int			 flags = ps->input->flags;
226bd2271cfSnicm 			int			 flag = 1;
227bd2271cfSnicm 			struct cmd_parse_scope	*scope;
228df6ab229Snicm 
229bd2271cfSnicm 			if (ps->scope != NULL) {
230bd2271cfSnicm 				flag = ps->scope->flag;
231bd2271cfSnicm 				TAILQ_FOREACH(scope, &ps->stack, entry)
232bd2271cfSnicm 					flag = flag && scope->flag;
233bd2271cfSnicm 			}
234bd2271cfSnicm 
235bd2271cfSnicm 			if ((~flags & CMD_PARSE_PARSEONLY) && flag)
236d6f6a5d2Snicm 				environ_put(global_environ, $1, 0);
237df6ab229Snicm 			free($1);
238df6ab229Snicm 		}
239df6ab229Snicm 
240d6f6a5d2Snicm hidden_assignment : HIDDEN EQUALS
241d6f6a5d2Snicm 		{
242d6f6a5d2Snicm 			struct cmd_parse_state	*ps = &parse_state;
243d6f6a5d2Snicm 			int			 flags = ps->input->flags;
244bd2271cfSnicm 			int			 flag = 1;
245bd2271cfSnicm 			struct cmd_parse_scope	*scope;
246d6f6a5d2Snicm 
247bd2271cfSnicm 			if (ps->scope != NULL) {
248bd2271cfSnicm 				flag = ps->scope->flag;
249bd2271cfSnicm 				TAILQ_FOREACH(scope, &ps->stack, entry)
250bd2271cfSnicm 					flag = flag && scope->flag;
251bd2271cfSnicm 			}
252bd2271cfSnicm 
253bd2271cfSnicm 			if ((~flags & CMD_PARSE_PARSEONLY) && flag)
254d6f6a5d2Snicm 				environ_put(global_environ, $2, ENVIRON_HIDDEN);
255d6f6a5d2Snicm 			free($2);
256d6f6a5d2Snicm 		}
257d6f6a5d2Snicm 
258df6ab229Snicm if_open		: IF expanded
259df6ab229Snicm 		{
260df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
261df6ab229Snicm 			struct cmd_parse_scope	*scope;
262df6ab229Snicm 
263df6ab229Snicm 			scope = xmalloc(sizeof *scope);
264df6ab229Snicm 			$$ = scope->flag = format_true($2);
265df6ab229Snicm 			free($2);
266df6ab229Snicm 
267df6ab229Snicm 			if (ps->scope != NULL)
268df6ab229Snicm 				TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry);
269df6ab229Snicm 			ps->scope = scope;
270df6ab229Snicm 		}
271df6ab229Snicm 
272df6ab229Snicm if_else		: ELSE
273df6ab229Snicm 		{
274df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
275df6ab229Snicm 			struct cmd_parse_scope	*scope;
276df6ab229Snicm 
277df6ab229Snicm 			scope = xmalloc(sizeof *scope);
278df6ab229Snicm 			scope->flag = !ps->scope->flag;
279df6ab229Snicm 
280df6ab229Snicm 			free(ps->scope);
281df6ab229Snicm 			ps->scope = scope;
282df6ab229Snicm 		}
283df6ab229Snicm 
284df6ab229Snicm if_elif		: ELIF expanded
285df6ab229Snicm 		{
286df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
287df6ab229Snicm 			struct cmd_parse_scope	*scope;
288df6ab229Snicm 
289df6ab229Snicm 			scope = xmalloc(sizeof *scope);
290df6ab229Snicm 			$$ = scope->flag = format_true($2);
291df6ab229Snicm 			free($2);
292df6ab229Snicm 
293df6ab229Snicm 			free(ps->scope);
294df6ab229Snicm 			ps->scope = scope;
295df6ab229Snicm 		}
296df6ab229Snicm 
297df6ab229Snicm if_close	: ENDIF
298df6ab229Snicm 		{
299df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
300df6ab229Snicm 
301df6ab229Snicm 			free(ps->scope);
302df6ab229Snicm 			ps->scope = TAILQ_FIRST(&ps->stack);
303df6ab229Snicm 			if (ps->scope != NULL)
304df6ab229Snicm 				TAILQ_REMOVE(&ps->stack, ps->scope, entry);
305df6ab229Snicm 		}
306df6ab229Snicm 
307df6ab229Snicm condition	: if_open '\n' statements if_close
308df6ab229Snicm 		{
309df6ab229Snicm 			if ($1)
3105d24dd80Snicm 				$$ = $3;
3115d24dd80Snicm 			else {
3125d24dd80Snicm 				$$ = cmd_parse_new_commands();
3135d24dd80Snicm 				cmd_parse_free_commands($3);
3145d24dd80Snicm 			}
315df6ab229Snicm 		}
316df6ab229Snicm 		| if_open '\n' statements if_else '\n' statements if_close
317df6ab229Snicm 		{
318df6ab229Snicm 			if ($1) {
3195d24dd80Snicm 				$$ = $3;
3205d24dd80Snicm 				cmd_parse_free_commands($6);
321df6ab229Snicm 			} else {
3225d24dd80Snicm 				$$ = $6;
3235d24dd80Snicm 				cmd_parse_free_commands($3);
324df6ab229Snicm 			}
325df6ab229Snicm 		}
326df6ab229Snicm 		| if_open '\n' statements elif if_close
327df6ab229Snicm 		{
328df6ab229Snicm 			if ($1) {
3295d24dd80Snicm 				$$ = $3;
3305d24dd80Snicm 				cmd_parse_free_commands($4.commands);
331df6ab229Snicm 			} else if ($4.flag) {
3325d24dd80Snicm 				$$ = $4.commands;
3335d24dd80Snicm 				cmd_parse_free_commands($3);
334df6ab229Snicm 			} else {
3355d24dd80Snicm 				$$ = cmd_parse_new_commands();
3365d24dd80Snicm 				cmd_parse_free_commands($3);
3375d24dd80Snicm 				cmd_parse_free_commands($4.commands);
338df6ab229Snicm 			}
339df6ab229Snicm 		}
340df6ab229Snicm 		| if_open '\n' statements elif if_else '\n' statements if_close
341df6ab229Snicm 		{
342df6ab229Snicm 			if ($1) {
3435d24dd80Snicm 				$$ = $3;
3445d24dd80Snicm 				cmd_parse_free_commands($4.commands);
3455d24dd80Snicm 				cmd_parse_free_commands($7);
346df6ab229Snicm 			} else if ($4.flag) {
3475d24dd80Snicm 				$$ = $4.commands;
3485d24dd80Snicm 				cmd_parse_free_commands($3);
3495d24dd80Snicm 				cmd_parse_free_commands($7);
350df6ab229Snicm 			} else {
3515d24dd80Snicm 				$$ = $7;
3525d24dd80Snicm 				cmd_parse_free_commands($3);
3535d24dd80Snicm 				cmd_parse_free_commands($4.commands);
354df6ab229Snicm 			}
355df6ab229Snicm 		}
356df6ab229Snicm 
357df6ab229Snicm elif		: if_elif '\n' statements
358df6ab229Snicm 		{
3595d24dd80Snicm 			if ($1) {
3605d24dd80Snicm 				$$.flag = 1;
3615d24dd80Snicm 				$$.commands = $3;
3625d24dd80Snicm 			} else {
3635d24dd80Snicm 				$$.flag = 0;
3645d24dd80Snicm 				$$.commands = cmd_parse_new_commands();
3655d24dd80Snicm 				cmd_parse_free_commands($3);
3665d24dd80Snicm 			}
367df6ab229Snicm 		}
368df6ab229Snicm 		| if_elif '\n' statements elif
369df6ab229Snicm 		{
370df6ab229Snicm 			if ($1) {
371df6ab229Snicm 				$$.flag = 1;
3725d24dd80Snicm 				$$.commands = $3;
3735d24dd80Snicm 				cmd_parse_free_commands($4.commands);
3745d24dd80Snicm 			} else if ($4.flag) {
3755d24dd80Snicm 				$$.flag = 1;
3765d24dd80Snicm 				$$.commands = $4.commands;
3775d24dd80Snicm 				cmd_parse_free_commands($3);
378df6ab229Snicm 			} else {
3795d24dd80Snicm 				$$.flag = 0;
3805d24dd80Snicm 				$$.commands = cmd_parse_new_commands();
3815d24dd80Snicm 				cmd_parse_free_commands($3);
3825d24dd80Snicm 				cmd_parse_free_commands($4.commands);
383df6ab229Snicm 			}
384df6ab229Snicm 		}
385df6ab229Snicm 
386df6ab229Snicm commands	: command
387df6ab229Snicm 		{
388df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
389df6ab229Snicm 
3905d24dd80Snicm 			$$ = cmd_parse_new_commands();
391b6e38b61Snicm 			if (!TAILQ_EMPTY(&$1->arguments) &&
39259b819b0Snicm 			    (ps->scope == NULL || ps->scope->flag))
3935d24dd80Snicm 				TAILQ_INSERT_TAIL($$, $1, entry);
394df6ab229Snicm 			else
395df6ab229Snicm 				cmd_parse_free_command($1);
396df6ab229Snicm 		}
397df6ab229Snicm 		| commands ';'
398df6ab229Snicm 		{
3995d24dd80Snicm 			$$ = $1;
400df6ab229Snicm 		}
401df6ab229Snicm 		| commands ';' condition1
402df6ab229Snicm 		{
4035d24dd80Snicm 			$$ = $1;
4045d24dd80Snicm 			TAILQ_CONCAT($$, $3, entry);
4055d24dd80Snicm 			free($3);
406df6ab229Snicm 		}
407df6ab229Snicm 		| commands ';' command
408df6ab229Snicm 		{
409df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
410df6ab229Snicm 
411b6e38b61Snicm 			if (!TAILQ_EMPTY(&$3->arguments) &&
41259b819b0Snicm 			    (ps->scope == NULL || ps->scope->flag)) {
4135d24dd80Snicm 				$$ = $1;
4145d24dd80Snicm 				TAILQ_INSERT_TAIL($$, $3, entry);
415df6ab229Snicm 			} else {
4165d24dd80Snicm 				$$ = cmd_parse_new_commands();
4175d24dd80Snicm 				cmd_parse_free_commands($1);
418df6ab229Snicm 				cmd_parse_free_command($3);
419df6ab229Snicm 			}
420df6ab229Snicm 		}
421df6ab229Snicm 		| condition1
422df6ab229Snicm 		{
4235d24dd80Snicm 			$$ = $1;
424df6ab229Snicm 		}
425df6ab229Snicm 
426bec5bc45Snicm command		: assignment
427bec5bc45Snicm 		{
428bec5bc45Snicm 			struct cmd_parse_state	*ps = &parse_state;
429bec5bc45Snicm 
430bec5bc45Snicm 			$$ = xcalloc(1, sizeof *$$);
431bec5bc45Snicm 			$$->line = ps->input->line;
432b6e38b61Snicm 			TAILQ_INIT(&$$->arguments);
433bec5bc45Snicm 		}
434bec5bc45Snicm 		| optional_assignment TOKEN
435df6ab229Snicm 		{
436df6ab229Snicm 			struct cmd_parse_state		*ps = &parse_state;
437b6e38b61Snicm 			struct cmd_parse_argument	*arg;
438df6ab229Snicm 
439df6ab229Snicm 			$$ = xcalloc(1, sizeof *$$);
4401ab16754Snicm 			$$->line = ps->input->line;
441b6e38b61Snicm 			TAILQ_INIT(&$$->arguments);
442df6ab229Snicm 
443b6e38b61Snicm 			arg = xcalloc(1, sizeof *arg);
444b6e38b61Snicm 			arg->type = CMD_PARSE_STRING;
4451d297f78Snicm 			arg->string = $2;
446b6e38b61Snicm 			TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
447df6ab229Snicm 		}
448bec5bc45Snicm 		| optional_assignment TOKEN arguments
449df6ab229Snicm 		{
450df6ab229Snicm 			struct cmd_parse_state		*ps = &parse_state;
451b6e38b61Snicm 			struct cmd_parse_argument	*arg;
452df6ab229Snicm 
453df6ab229Snicm 			$$ = xcalloc(1, sizeof *$$);
4541ab16754Snicm 			$$->line = ps->input->line;
455b6e38b61Snicm 			TAILQ_INIT(&$$->arguments);
456df6ab229Snicm 
457b6e38b61Snicm 			TAILQ_CONCAT(&$$->arguments, $3, entry);
458b6e38b61Snicm 			free($3);
459b6e38b61Snicm 
460b6e38b61Snicm 			arg = xcalloc(1, sizeof *arg);
461b6e38b61Snicm 			arg->type = CMD_PARSE_STRING;
4621d297f78Snicm 			arg->string = $2;
463b6e38b61Snicm 			TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
464df6ab229Snicm 		}
465df6ab229Snicm 
466df6ab229Snicm condition1	: if_open commands if_close
467df6ab229Snicm 		{
468df6ab229Snicm 			if ($1)
4695d24dd80Snicm 				$$ = $2;
4705d24dd80Snicm 			else {
4715d24dd80Snicm 				$$ = cmd_parse_new_commands();
4725d24dd80Snicm 				cmd_parse_free_commands($2);
4735d24dd80Snicm 			}
474df6ab229Snicm 		}
475df6ab229Snicm 		| if_open commands if_else commands if_close
476df6ab229Snicm 		{
477df6ab229Snicm 			if ($1) {
4785d24dd80Snicm 				$$ = $2;
4795d24dd80Snicm 				cmd_parse_free_commands($4);
480df6ab229Snicm 			} else {
4815d24dd80Snicm 				$$ = $4;
4825d24dd80Snicm 				cmd_parse_free_commands($2);
483df6ab229Snicm 			}
484df6ab229Snicm 		}
485df6ab229Snicm 		| if_open commands elif1 if_close
486df6ab229Snicm 		{
487df6ab229Snicm 			if ($1) {
4885d24dd80Snicm 				$$ = $2;
4895d24dd80Snicm 				cmd_parse_free_commands($3.commands);
490df6ab229Snicm 			} else if ($3.flag) {
4915d24dd80Snicm 				$$ = $3.commands;
4925d24dd80Snicm 				cmd_parse_free_commands($2);
493df6ab229Snicm 			} else {
4945d24dd80Snicm 				$$ = cmd_parse_new_commands();
4955d24dd80Snicm 				cmd_parse_free_commands($2);
4965d24dd80Snicm 				cmd_parse_free_commands($3.commands);
497df6ab229Snicm 			}
498df6ab229Snicm 		}
499df6ab229Snicm 		| if_open commands elif1 if_else commands if_close
500df6ab229Snicm 		{
501df6ab229Snicm 			if ($1) {
5025d24dd80Snicm 				$$ = $2;
5035d24dd80Snicm 				cmd_parse_free_commands($3.commands);
5045d24dd80Snicm 				cmd_parse_free_commands($5);
505df6ab229Snicm 			} else if ($3.flag) {
5065d24dd80Snicm 				$$ = $3.commands;
5075d24dd80Snicm 				cmd_parse_free_commands($2);
5085d24dd80Snicm 				cmd_parse_free_commands($5);
509df6ab229Snicm 			} else {
5105d24dd80Snicm 				$$ = $5;
5115d24dd80Snicm 				cmd_parse_free_commands($2);
5125d24dd80Snicm 				cmd_parse_free_commands($3.commands);
513df6ab229Snicm 			}
514df6ab229Snicm 		}
515df6ab229Snicm 
516df6ab229Snicm elif1		: if_elif commands
517df6ab229Snicm 		{
5185d24dd80Snicm 			if ($1) {
5195d24dd80Snicm 				$$.flag = 1;
5205d24dd80Snicm 				$$.commands = $2;
5215d24dd80Snicm 			} else {
5225d24dd80Snicm 				$$.flag = 0;
5235d24dd80Snicm 				$$.commands = cmd_parse_new_commands();
5245d24dd80Snicm 				cmd_parse_free_commands($2);
5255d24dd80Snicm 			}
526df6ab229Snicm 		}
527df6ab229Snicm 		| if_elif commands elif1
528df6ab229Snicm 		{
529df6ab229Snicm 			if ($1) {
530df6ab229Snicm 				$$.flag = 1;
5315d24dd80Snicm 				$$.commands = $2;
5325d24dd80Snicm 				cmd_parse_free_commands($3.commands);
5335d24dd80Snicm 			} else if ($3.flag) {
5345d24dd80Snicm 				$$.flag = 1;
5355d24dd80Snicm 				$$.commands = $3.commands;
5365d24dd80Snicm 				cmd_parse_free_commands($2);
537df6ab229Snicm 			} else {
5385d24dd80Snicm 				$$.flag = 0;
5395d24dd80Snicm 				$$.commands = cmd_parse_new_commands();
5405d24dd80Snicm 				cmd_parse_free_commands($2);
5415d24dd80Snicm 				cmd_parse_free_commands($3.commands);
542df6ab229Snicm 			}
543df6ab229Snicm 		}
544df6ab229Snicm 
545df6ab229Snicm arguments	: argument
546df6ab229Snicm 		{
547b6e38b61Snicm 			$$ = xcalloc(1, sizeof *$$);
548b6e38b61Snicm 			TAILQ_INIT($$);
549df6ab229Snicm 
550b6e38b61Snicm 			TAILQ_INSERT_HEAD($$, $1, entry);
551df6ab229Snicm 		}
552df6ab229Snicm 		| argument arguments
553df6ab229Snicm 		{
554b6e38b61Snicm 			TAILQ_INSERT_HEAD($2, $1, entry);
555df6ab229Snicm 			$$ = $2;
556df6ab229Snicm 		}
557df6ab229Snicm 
558df6ab229Snicm argument	: TOKEN
559df6ab229Snicm 		{
560b6e38b61Snicm 			$$ = xcalloc(1, sizeof *$$);
561b6e38b61Snicm 			$$->type = CMD_PARSE_STRING;
5621d297f78Snicm 			$$->string = $1;
563df6ab229Snicm 		}
564df6ab229Snicm 		| EQUALS
565df6ab229Snicm 		{
566b6e38b61Snicm 			$$ = xcalloc(1, sizeof *$$);
567b6e38b61Snicm 			$$->type = CMD_PARSE_STRING;
5681d297f78Snicm 			$$->string = $1;
569df6ab229Snicm 		}
5702a840c62Snicm 		| '{' argument_statements
5712a840c62Snicm 		{
572b6e38b61Snicm 			$$ = xcalloc(1, sizeof *$$);
573b6e38b61Snicm 			$$->type = CMD_PARSE_COMMANDS;
574b6e38b61Snicm 			$$->commands = $2;
5752a840c62Snicm 		}
5762a840c62Snicm 
5772a840c62Snicm argument_statements	: statement '}'
5782a840c62Snicm 			{
5792a840c62Snicm 				$$ = $1;
5802a840c62Snicm 			}
58124479cbfSnicm 			| statements statement '}'
5822a840c62Snicm 			{
5832a840c62Snicm 				$$ = $1;
58424479cbfSnicm 				TAILQ_CONCAT($$, $2, entry);
58524479cbfSnicm 				free($2);
5862a840c62Snicm 			}
587df6ab229Snicm 
588df6ab229Snicm %%
589df6ab229Snicm 
590df6ab229Snicm static char *
591df6ab229Snicm cmd_parse_get_error(const char *file, u_int line, const char *error)
592df6ab229Snicm {
593df6ab229Snicm 	char	*s;
594df6ab229Snicm 
595df6ab229Snicm 	if (file == NULL)
596df6ab229Snicm 		s = xstrdup(error);
597df6ab229Snicm 	else
598df6ab229Snicm 		xasprintf(&s, "%s:%u: %s", file, line, error);
599df6ab229Snicm 	return (s);
600df6ab229Snicm }
601df6ab229Snicm 
602df6ab229Snicm static void
603287cddb6Snicm cmd_parse_print_commands(struct cmd_parse_input *pi, struct cmd_list *cmdlist)
6045304b409Snicm {
6055304b409Snicm 	char	*s;
6065304b409Snicm 
607287cddb6Snicm 	if (pi->item == NULL || (~pi->flags & CMD_PARSE_VERBOSE))
608287cddb6Snicm 		return;
6095304b409Snicm 	s = cmd_list_print(cmdlist, 0);
610d47c8058Snicm 	if (pi->file != NULL)
611287cddb6Snicm 		cmdq_print(pi->item, "%s:%u: %s", pi->file, pi->line, s);
612d47c8058Snicm 	else
613287cddb6Snicm 		cmdq_print(pi->item, "%u: %s", pi->line, s);
6145304b409Snicm 	free(s);
6155304b409Snicm }
6165304b409Snicm 
6175304b409Snicm static void
618287cddb6Snicm cmd_parse_free_argument(struct cmd_parse_argument *arg)
619b6e38b61Snicm {
620b6e38b61Snicm 	switch (arg->type) {
621b6e38b61Snicm 	case CMD_PARSE_STRING:
622b6e38b61Snicm 		free(arg->string);
623b6e38b61Snicm 		break;
624b6e38b61Snicm 	case CMD_PARSE_COMMANDS:
625b6e38b61Snicm 		cmd_parse_free_commands(arg->commands);
626b6e38b61Snicm 		break;
627d8b32369Snicm 	case CMD_PARSE_PARSED_COMMANDS:
628d8b32369Snicm 		cmd_list_free(arg->cmdlist);
629d8b32369Snicm 		break;
630b6e38b61Snicm 	}
631b6e38b61Snicm 	free(arg);
632b6e38b61Snicm }
633287cddb6Snicm 
634287cddb6Snicm static void
635287cddb6Snicm cmd_parse_free_arguments(struct cmd_parse_arguments *args)
636287cddb6Snicm {
637287cddb6Snicm 	struct cmd_parse_argument	*arg, *arg1;
638287cddb6Snicm 
639287cddb6Snicm 	TAILQ_FOREACH_SAFE(arg, args, entry, arg1) {
640287cddb6Snicm 		TAILQ_REMOVE(args, arg, entry);
641287cddb6Snicm 		cmd_parse_free_argument(arg);
642287cddb6Snicm 	}
643b6e38b61Snicm }
644b6e38b61Snicm 
645b6e38b61Snicm static void
646df6ab229Snicm cmd_parse_free_command(struct cmd_parse_command *cmd)
647df6ab229Snicm {
648b6e38b61Snicm 	cmd_parse_free_arguments(&cmd->arguments);
649df6ab229Snicm 	free(cmd);
650df6ab229Snicm }
651df6ab229Snicm 
6525d24dd80Snicm static struct cmd_parse_commands *
6535d24dd80Snicm cmd_parse_new_commands(void)
6545d24dd80Snicm {
6555d24dd80Snicm 	struct cmd_parse_commands	*cmds;
6565d24dd80Snicm 
6575d24dd80Snicm 	cmds = xmalloc(sizeof *cmds);
6585d24dd80Snicm 	TAILQ_INIT(cmds);
6595d24dd80Snicm 	return (cmds);
6605d24dd80Snicm }
6615d24dd80Snicm 
662df6ab229Snicm static void
663df6ab229Snicm cmd_parse_free_commands(struct cmd_parse_commands *cmds)
664df6ab229Snicm {
665df6ab229Snicm 	struct cmd_parse_command	*cmd, *cmd1;
666df6ab229Snicm 
667df6ab229Snicm 	TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) {
668df6ab229Snicm 		TAILQ_REMOVE(cmds, cmd, entry);
669df6ab229Snicm 		cmd_parse_free_command(cmd);
670df6ab229Snicm 	}
6715d24dd80Snicm 	free(cmds);
672df6ab229Snicm }
673df6ab229Snicm 
674df6ab229Snicm static struct cmd_parse_commands *
67573530c3cSnicm cmd_parse_run_parser(char **cause)
676df6ab229Snicm {
677df6ab229Snicm 	struct cmd_parse_state	*ps = &parse_state;
678df6ab229Snicm 	struct cmd_parse_scope	*scope, *scope1;
679df6ab229Snicm 	int			 retval;
680df6ab229Snicm 
6815d24dd80Snicm 	ps->commands = NULL;
682df6ab229Snicm 	TAILQ_INIT(&ps->stack);
683df6ab229Snicm 
684df6ab229Snicm 	retval = yyparse();
685df6ab229Snicm 	TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) {
686df6ab229Snicm 		TAILQ_REMOVE(&ps->stack, scope, entry);
687df6ab229Snicm 		free(scope);
688df6ab229Snicm 	}
689df6ab229Snicm 	if (retval != 0) {
690df6ab229Snicm 		*cause = ps->error;
691df6ab229Snicm 		return (NULL);
692df6ab229Snicm 	}
693df6ab229Snicm 
6945d24dd80Snicm 	if (ps->commands == NULL)
6955d24dd80Snicm 		return (cmd_parse_new_commands());
6965d24dd80Snicm 	return (ps->commands);
697df6ab229Snicm }
698df6ab229Snicm 
69973530c3cSnicm static struct cmd_parse_commands *
70073530c3cSnicm cmd_parse_do_file(FILE *f, struct cmd_parse_input *pi, char **cause)
70173530c3cSnicm {
70273530c3cSnicm 	struct cmd_parse_state	*ps = &parse_state;
70373530c3cSnicm 
70473530c3cSnicm 	memset(ps, 0, sizeof *ps);
70573530c3cSnicm 	ps->input = pi;
70673530c3cSnicm 	ps->f = f;
70773530c3cSnicm 	return (cmd_parse_run_parser(cause));
70873530c3cSnicm }
70973530c3cSnicm 
71073530c3cSnicm static struct cmd_parse_commands *
71173530c3cSnicm cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi,
71273530c3cSnicm     char **cause)
71373530c3cSnicm {
71473530c3cSnicm 	struct cmd_parse_state	*ps = &parse_state;
71573530c3cSnicm 
71673530c3cSnicm 	memset(ps, 0, sizeof *ps);
71773530c3cSnicm 	ps->input = pi;
71873530c3cSnicm 	ps->buf = buf;
71973530c3cSnicm 	ps->len = len;
72073530c3cSnicm 	return (cmd_parse_run_parser(cause));
72173530c3cSnicm }
72273530c3cSnicm 
723b6e38b61Snicm static void
724b6e38b61Snicm cmd_parse_log_commands(struct cmd_parse_commands *cmds, const char *prefix)
725b6e38b61Snicm {
726b6e38b61Snicm 	struct cmd_parse_command	*cmd;
727b6e38b61Snicm 	struct cmd_parse_argument	*arg;
728b6e38b61Snicm 	u_int				 i, j;
729b6e38b61Snicm 	char				*s;
730b6e38b61Snicm 
731b6e38b61Snicm 	i = 0;
732b6e38b61Snicm 	TAILQ_FOREACH(cmd, cmds, entry) {
733b6e38b61Snicm 		j = 0;
734b6e38b61Snicm 		TAILQ_FOREACH(arg, &cmd->arguments, entry) {
735b6e38b61Snicm 			switch (arg->type) {
736b6e38b61Snicm 			case CMD_PARSE_STRING:
737b6e38b61Snicm 				log_debug("%s %u:%u: %s", prefix, i, j,
738b6e38b61Snicm 				    arg->string);
739b6e38b61Snicm 				break;
740b6e38b61Snicm 			case CMD_PARSE_COMMANDS:
741b6e38b61Snicm 				xasprintf(&s, "%s %u:%u", prefix, i, j);
742b6e38b61Snicm 				cmd_parse_log_commands(arg->commands, s);
743b6e38b61Snicm 				free(s);
744b6e38b61Snicm 				break;
745d8b32369Snicm 			case CMD_PARSE_PARSED_COMMANDS:
746d8b32369Snicm 				s = cmd_list_print(arg->cmdlist, 0);
747d8b32369Snicm 				log_debug("%s %u:%u: %s", prefix, i, j, s);
748d8b32369Snicm 				free(s);
749d8b32369Snicm 				break;
750b6e38b61Snicm 			}
751b6e38b61Snicm 			j++;
752b6e38b61Snicm 		}
753b6e38b61Snicm 		i++;
754b6e38b61Snicm 	}
755b6e38b61Snicm }
756b6e38b61Snicm 
757287cddb6Snicm static int
758287cddb6Snicm cmd_parse_expand_alias(struct cmd_parse_command *cmd,
759cb3c07dcSnicm     struct cmd_parse_input *pi, struct cmd_parse_result *pr)
760b6e38b61Snicm {
761cb3c07dcSnicm 	struct cmd_parse_argument	*arg, *arg1, *first;
762287cddb6Snicm 	struct cmd_parse_commands	*cmds;
763287cddb6Snicm 	struct cmd_parse_command	*last;
764287cddb6Snicm 	char				*alias, *name, *cause;
765b6e38b61Snicm 
7669a68e586Snicm 	if (pi->flags & CMD_PARSE_NOALIAS)
7679a68e586Snicm 		return (0);
768cb3c07dcSnicm 	memset(pr, 0, sizeof *pr);
769b6e38b61Snicm 
770287cddb6Snicm 	first = TAILQ_FIRST(&cmd->arguments);
771287cddb6Snicm 	if (first == NULL || first->type != CMD_PARSE_STRING) {
772afdf680fSnicm 		pr->status = CMD_PARSE_SUCCESS;
773afdf680fSnicm 		pr->cmdlist = cmd_list_new();
774287cddb6Snicm 		return (1);
775287cddb6Snicm 	}
776287cddb6Snicm 	name = first->string;
777287cddb6Snicm 
778287cddb6Snicm 	alias = cmd_get_alias(name);
779287cddb6Snicm 	if (alias == NULL)
780287cddb6Snicm 		return (0);
781287cddb6Snicm 	log_debug("%s: %u alias %s = %s", __func__, pi->line, name, alias);
782287cddb6Snicm 
783287cddb6Snicm 	cmds = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause);
784287cddb6Snicm 	free(alias);
785287cddb6Snicm 	if (cmds == NULL) {
786287cddb6Snicm 		pr->status = CMD_PARSE_ERROR;
787287cddb6Snicm 		pr->error = cause;
788287cddb6Snicm 		return (1);
789b6e38b61Snicm 	}
790b6e38b61Snicm 
791287cddb6Snicm 	last = TAILQ_LAST(cmds, cmd_parse_commands);
792287cddb6Snicm 	if (last == NULL) {
793cb3c07dcSnicm 		pr->status = CMD_PARSE_SUCCESS;
794cb3c07dcSnicm 		pr->cmdlist = cmd_list_new();
795287cddb6Snicm 		return (1);
796b6e38b61Snicm 	}
797b6e38b61Snicm 
798287cddb6Snicm 	TAILQ_REMOVE(&cmd->arguments, first, entry);
799287cddb6Snicm 	cmd_parse_free_argument(first);
800287cddb6Snicm 
801287cddb6Snicm 	TAILQ_FOREACH_SAFE(arg, &cmd->arguments, entry, arg1) {
802287cddb6Snicm 		TAILQ_REMOVE(&cmd->arguments, arg, entry);
803287cddb6Snicm 		TAILQ_INSERT_TAIL(&last->arguments, arg, entry);
804287cddb6Snicm 	}
805287cddb6Snicm 	cmd_parse_log_commands(cmds, __func__);
806287cddb6Snicm 
8079a68e586Snicm 	pi->flags |= CMD_PARSE_NOALIAS;
808287cddb6Snicm 	cmd_parse_build_commands(cmds, pi, pr);
8099a68e586Snicm 	pi->flags &= ~CMD_PARSE_NOALIAS;
810287cddb6Snicm 	return (1);
811287cddb6Snicm }
812287cddb6Snicm 
813cb3c07dcSnicm static void
814287cddb6Snicm cmd_parse_build_command(struct cmd_parse_command *cmd,
815287cddb6Snicm     struct cmd_parse_input *pi, struct cmd_parse_result *pr)
816b6e38b61Snicm {
817b6e38b61Snicm 	struct cmd_parse_argument	*arg;
818287cddb6Snicm 	struct cmd			*add;
819fb147d85Snicm 	char				*cause;
820fb147d85Snicm 	struct args_value		*values = NULL;
821fb147d85Snicm 	u_int				 count = 0, idx;
822b6e38b61Snicm 
823cb3c07dcSnicm 	memset(pr, 0, sizeof *pr);
824cb3c07dcSnicm 
825cb3c07dcSnicm 	if (cmd_parse_expand_alias(cmd, pi, pr))
826cb3c07dcSnicm 		return;
827b6e38b61Snicm 
828b6e38b61Snicm 	TAILQ_FOREACH(arg, &cmd->arguments, entry) {
829bee784faSnicm 		values = xrecallocarray(values, count, count + 1,
830bee784faSnicm 		    sizeof *values);
831b6e38b61Snicm 		switch (arg->type) {
832b6e38b61Snicm 		case CMD_PARSE_STRING:
833fb147d85Snicm 			values[count].type = ARGS_STRING;
834fb147d85Snicm 			values[count].string = xstrdup(arg->string);
835b6e38b61Snicm 			break;
836b6e38b61Snicm 		case CMD_PARSE_COMMANDS:
837287cddb6Snicm 			cmd_parse_build_commands(arg->commands, pi, pr);
838287cddb6Snicm 			if (pr->status != CMD_PARSE_SUCCESS)
839fb147d85Snicm 				goto out;
840fb147d85Snicm 			values[count].type = ARGS_COMMANDS;
841fb147d85Snicm 			values[count].cmdlist = pr->cmdlist;
842b6e38b61Snicm 			break;
843d8b32369Snicm 		case CMD_PARSE_PARSED_COMMANDS:
844d8b32369Snicm 			values[count].type = ARGS_COMMANDS;
845d8b32369Snicm 			values[count].cmdlist = arg->cmdlist;
846d8b32369Snicm 			values[count].cmdlist->references++;
847d8b32369Snicm 			break;
848b6e38b61Snicm 		}
849fb147d85Snicm 		count++;
850b6e38b61Snicm 	}
851b6e38b61Snicm 
852fb147d85Snicm 	add = cmd_parse(values, count, pi->file, pi->line, &cause);
853379d46e0Snicm 	if (add == NULL) {
854379d46e0Snicm 		pr->status = CMD_PARSE_ERROR;
855287cddb6Snicm 		pr->error = cmd_parse_get_error(pi->file, pi->line, cause);
856379d46e0Snicm 		free(cause);
857fb147d85Snicm 		goto out;
858379d46e0Snicm 	}
859cb3c07dcSnicm 	pr->status = CMD_PARSE_SUCCESS;
860cb3c07dcSnicm 	pr->cmdlist = cmd_list_new();
861cb3c07dcSnicm 	cmd_list_append(pr->cmdlist, add);
862fb147d85Snicm 
863fb147d85Snicm out:
864fb147d85Snicm 	for (idx = 0; idx < count; idx++)
865fb147d85Snicm 		args_free_value(&values[idx]);
866fb147d85Snicm 	free(values);
867379d46e0Snicm }
868379d46e0Snicm 
869287cddb6Snicm static void
870a6afde38Snicm cmd_parse_build_commands(struct cmd_parse_commands *cmds,
871287cddb6Snicm     struct cmd_parse_input *pi, struct cmd_parse_result *pr)
872df6ab229Snicm {
873287cddb6Snicm 	struct cmd_parse_command	*cmd;
874df6ab229Snicm 	u_int				 line = UINT_MAX;
875cb3c07dcSnicm 	struct cmd_list			*current = NULL, *result;
876287cddb6Snicm 	char				*s;
877df6ab229Snicm 
878cb3c07dcSnicm 	memset(pr, 0, sizeof *pr);
879cb3c07dcSnicm 
880a6afde38Snicm 	/* Check for an empty list. */
881df6ab229Snicm 	if (TAILQ_EMPTY(cmds)) {
882afdf680fSnicm 		pr->status = CMD_PARSE_SUCCESS;
883afdf680fSnicm 		pr->cmdlist = cmd_list_new();
884287cddb6Snicm 		return;
885df6ab229Snicm 	}
886b6e38b61Snicm 	cmd_parse_log_commands(cmds, __func__);
887df6ab229Snicm 
888df6ab229Snicm 	/*
889df6ab229Snicm 	 * Parse each command into a command list. Create a new command list
890a49ab104Snicm 	 * for each line (unless the flag is set) so they get a new group (so
891a49ab104Snicm 	 * the queue knows which ones to remove if a command fails when
892a49ab104Snicm 	 * executed).
893df6ab229Snicm 	 */
894df6ab229Snicm 	result = cmd_list_new();
895df6ab229Snicm 	TAILQ_FOREACH(cmd, cmds, entry) {
896379d46e0Snicm 		if (((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) {
897379d46e0Snicm 			if (current != NULL) {
898287cddb6Snicm 				cmd_parse_print_commands(pi, current);
899379d46e0Snicm 				cmd_list_move(result, current);
900379d46e0Snicm 				cmd_list_free(current);
901df6ab229Snicm 			}
902379d46e0Snicm 			current = cmd_list_new();
903df6ab229Snicm 		}
904379d46e0Snicm 		if (current == NULL)
905379d46e0Snicm 			current = cmd_list_new();
906287cddb6Snicm 		line = pi->line = cmd->line;
907df6ab229Snicm 
908cb3c07dcSnicm 		cmd_parse_build_command(cmd, pi, pr);
909cb3c07dcSnicm 		if (pr->status != CMD_PARSE_SUCCESS) {
910df6ab229Snicm 			cmd_list_free(result);
911379d46e0Snicm 			cmd_list_free(current);
912287cddb6Snicm 			return;
913df6ab229Snicm 		}
914cb3c07dcSnicm 		cmd_list_append_all(current, pr->cmdlist);
915cb3c07dcSnicm 		cmd_list_free(pr->cmdlist);
916df6ab229Snicm 	}
917379d46e0Snicm 	if (current != NULL) {
918287cddb6Snicm 		cmd_parse_print_commands(pi, current);
919379d46e0Snicm 		cmd_list_move(result, current);
920379d46e0Snicm 		cmd_list_free(current);
921df6ab229Snicm 	}
922df6ab229Snicm 
9235c131106Snicm 	s = cmd_list_print(result, 0);
924df6ab229Snicm 	log_debug("%s: %s", __func__, s);
925df6ab229Snicm 	free(s);
926df6ab229Snicm 
927287cddb6Snicm 	pr->status = CMD_PARSE_SUCCESS;
928287cddb6Snicm 	pr->cmdlist = result;
929df6ab229Snicm }
930df6ab229Snicm 
931df6ab229Snicm struct cmd_parse_result *
932a6afde38Snicm cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
933a6afde38Snicm {
934a6afde38Snicm 	static struct cmd_parse_result	 pr;
935a6afde38Snicm 	struct cmd_parse_input		 input;
936a6afde38Snicm 	struct cmd_parse_commands	*cmds;
937a6afde38Snicm 	char				*cause;
938a6afde38Snicm 
939a6afde38Snicm 	if (pi == NULL) {
940a6afde38Snicm 		memset(&input, 0, sizeof input);
941a6afde38Snicm 		pi = &input;
942a6afde38Snicm 	}
943a6afde38Snicm 	memset(&pr, 0, sizeof pr);
944a6afde38Snicm 
94573530c3cSnicm 	cmds = cmd_parse_do_file(f, pi, &cause);
946a6afde38Snicm 	if (cmds == NULL) {
947a6afde38Snicm 		pr.status = CMD_PARSE_ERROR;
948a6afde38Snicm 		pr.error = cause;
949a6afde38Snicm 		return (&pr);
950a6afde38Snicm 	}
951287cddb6Snicm 	cmd_parse_build_commands(cmds, pi, &pr);
952287cddb6Snicm 	cmd_parse_free_commands(cmds);
953287cddb6Snicm 	return (&pr);
954287cddb6Snicm 
955a6afde38Snicm }
956a6afde38Snicm 
957a6afde38Snicm struct cmd_parse_result *
958df6ab229Snicm cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
959df6ab229Snicm {
960a49ab104Snicm 	struct cmd_parse_input	input;
961a49ab104Snicm 
962a49ab104Snicm 	if (pi == NULL) {
963a49ab104Snicm 		memset(&input, 0, sizeof input);
964a49ab104Snicm 		pi = &input;
965a49ab104Snicm 	}
966a49ab104Snicm 
967a49ab104Snicm 	/*
968a49ab104Snicm 	 * When parsing a string, put commands in one group even if there are
969a49ab104Snicm 	 * multiple lines. This means { a \n b } is identical to "a ; b" when
970a49ab104Snicm 	 * given as an argument to another command.
971a49ab104Snicm 	 */
972a49ab104Snicm 	pi->flags |= CMD_PARSE_ONEGROUP;
973f233c35dSnicm 	return (cmd_parse_from_buffer(s, strlen(s), pi));
974f233c35dSnicm }
975f233c35dSnicm 
9761c43462cSnicm enum cmd_parse_status
9771c43462cSnicm cmd_parse_and_insert(const char *s, struct cmd_parse_input *pi,
9781c43462cSnicm     struct cmdq_item *after, struct cmdq_state *state, char **error)
9791c43462cSnicm {
9801c43462cSnicm 	struct cmd_parse_result	*pr;
9811c43462cSnicm 	struct cmdq_item	*item;
9821c43462cSnicm 
9831c43462cSnicm 	pr = cmd_parse_from_string(s, pi);
9841c43462cSnicm 	switch (pr->status) {
9851c43462cSnicm 	case CMD_PARSE_ERROR:
9861c43462cSnicm 		if (error != NULL)
9871c43462cSnicm 			*error = pr->error;
9881c43462cSnicm 		else
9891c43462cSnicm 			free(pr->error);
9901c43462cSnicm 		break;
9911c43462cSnicm 	case CMD_PARSE_SUCCESS:
9921c43462cSnicm 		item = cmdq_get_command(pr->cmdlist, state);
9931c43462cSnicm 		cmdq_insert_after(after, item);
9941c43462cSnicm 		cmd_list_free(pr->cmdlist);
9951c43462cSnicm 		break;
9961c43462cSnicm 	}
9971c43462cSnicm 	return (pr->status);
9981c43462cSnicm }
9991c43462cSnicm 
10001c43462cSnicm enum cmd_parse_status
10011c43462cSnicm cmd_parse_and_append(const char *s, struct cmd_parse_input *pi,
10021c43462cSnicm     struct client *c, struct cmdq_state *state, char **error)
10031c43462cSnicm {
10041c43462cSnicm 	struct cmd_parse_result	*pr;
10051c43462cSnicm 	struct cmdq_item	*item;
10061c43462cSnicm 
10071c43462cSnicm 	pr = cmd_parse_from_string(s, pi);
10081c43462cSnicm 	switch (pr->status) {
10091c43462cSnicm 	case CMD_PARSE_ERROR:
10101c43462cSnicm 		if (error != NULL)
10111c43462cSnicm 			*error = pr->error;
10121c43462cSnicm 		else
10131c43462cSnicm 			free(pr->error);
10141c43462cSnicm 		break;
10151c43462cSnicm 	case CMD_PARSE_SUCCESS:
10161c43462cSnicm 		item = cmdq_get_command(pr->cmdlist, state);
10171c43462cSnicm 		cmdq_append(c, item);
10181c43462cSnicm 		cmd_list_free(pr->cmdlist);
10191c43462cSnicm 		break;
10201c43462cSnicm 	}
10211c43462cSnicm 	return (pr->status);
10221c43462cSnicm }
10231c43462cSnicm 
1024f233c35dSnicm struct cmd_parse_result *
1025f233c35dSnicm cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi)
1026f233c35dSnicm {
1027df6ab229Snicm 	static struct cmd_parse_result	 pr;
1028a6afde38Snicm 	struct cmd_parse_input		 input;
102973530c3cSnicm 	struct cmd_parse_commands	*cmds;
103073530c3cSnicm 	char				*cause;
1031df6ab229Snicm 
1032a6afde38Snicm 	if (pi == NULL) {
1033a6afde38Snicm 		memset(&input, 0, sizeof input);
1034a6afde38Snicm 		pi = &input;
1035a6afde38Snicm 	}
1036a6afde38Snicm 	memset(&pr, 0, sizeof pr);
1037a6afde38Snicm 
1038f233c35dSnicm 	if (len == 0) {
1039afdf680fSnicm 		pr.status = CMD_PARSE_SUCCESS;
1040afdf680fSnicm 		pr.cmdlist = cmd_list_new();
1041df6ab229Snicm 		return (&pr);
1042df6ab229Snicm 	}
1043df6ab229Snicm 
1044f233c35dSnicm 	cmds = cmd_parse_do_buffer(buf, len, pi, &cause);
104573530c3cSnicm 	if (cmds == NULL) {
1046df6ab229Snicm 		pr.status = CMD_PARSE_ERROR;
104773530c3cSnicm 		pr.error = cause;
104873530c3cSnicm 		return (&pr);
1049df6ab229Snicm 	}
1050287cddb6Snicm 	cmd_parse_build_commands(cmds, pi, &pr);
1051287cddb6Snicm 	cmd_parse_free_commands(cmds);
1052287cddb6Snicm 	return (&pr);
1053df6ab229Snicm }
1054df6ab229Snicm 
1055a6afde38Snicm struct cmd_parse_result *
1056d8b32369Snicm cmd_parse_from_arguments(struct args_value *values, u_int count,
1057d8b32369Snicm     struct cmd_parse_input *pi)
1058a6afde38Snicm {
1059287cddb6Snicm 	static struct cmd_parse_result	 pr;
1060a6afde38Snicm 	struct cmd_parse_input		 input;
1061a6afde38Snicm 	struct cmd_parse_commands	*cmds;
1062d8b32369Snicm 	struct cmd_parse_command	*cmd;
1063d8b32369Snicm 	struct cmd_parse_argument	*arg;
1064d8b32369Snicm 	u_int				 i;
1065d8b32369Snicm 	char				*copy;
1066a6afde38Snicm 	size_t				 size;
1067d8b32369Snicm 	int				 end;
1068a6afde38Snicm 
1069a6afde38Snicm 	/*
1070a6afde38Snicm 	 * The commands are already split up into arguments, so just separate
1071a6afde38Snicm 	 * into a set of commands by ';'.
1072a6afde38Snicm 	 */
1073a6afde38Snicm 
1074a6afde38Snicm 	if (pi == NULL) {
1075a6afde38Snicm 		memset(&input, 0, sizeof input);
1076a6afde38Snicm 		pi = &input;
1077a6afde38Snicm 	}
1078cb3c07dcSnicm 	memset(&pr, 0, sizeof pr);
1079a6afde38Snicm 
10805d24dd80Snicm 	cmds = cmd_parse_new_commands();
1081a6afde38Snicm 
1082d8b32369Snicm 	cmd = xcalloc(1, sizeof *cmd);
1083d8b32369Snicm 	cmd->line = pi->line;
1084d8b32369Snicm 	TAILQ_INIT(&cmd->arguments);
1085d8b32369Snicm 
1086d8b32369Snicm 	for (i = 0; i < count; i++) {
1087d8b32369Snicm 		end = 0;
1088d8b32369Snicm 		if (values[i].type == ARGS_STRING) {
1089d8b32369Snicm 			copy = xstrdup(values[i].string);
1090d8b32369Snicm 			size = strlen(copy);
1091d8b32369Snicm 			if (size != 0 && copy[size - 1] == ';') {
1092d8b32369Snicm 				copy[--size] = '\0';
1093d8b32369Snicm 				if (size > 0 && copy[size - 1] == '\\')
1094d8b32369Snicm 					copy[size - 1] = ';';
1095d8b32369Snicm 				else
1096d8b32369Snicm 					end = 1;
1097a6afde38Snicm 			}
1098d8b32369Snicm 			if (!end || size != 0) {
1099d8b32369Snicm 				arg = xcalloc(1, sizeof *arg);
1100d8b32369Snicm 				arg->type = CMD_PARSE_STRING;
1101d8b32369Snicm 				arg->string = copy;
1102d8b32369Snicm 				TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
11038d320eeeSnicm 			} else
11048d320eeeSnicm 				free(copy);
1105d8b32369Snicm 		} else if (values[i].type == ARGS_COMMANDS) {
1106d8b32369Snicm 			arg = xcalloc(1, sizeof *arg);
1107d8b32369Snicm 			arg->type = CMD_PARSE_PARSED_COMMANDS;
1108d8b32369Snicm 			arg->cmdlist = values[i].cmdlist;
1109d8b32369Snicm 			arg->cmdlist->references++;
1110d8b32369Snicm 			TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
1111d8b32369Snicm 		} else
1112d8b32369Snicm 			fatalx("unknown argument type");
1113d8b32369Snicm 		if (end) {
1114d8b32369Snicm 			TAILQ_INSERT_TAIL(cmds, cmd, entry);
1115d8b32369Snicm 			cmd = xcalloc(1, sizeof *cmd);
1116d8b32369Snicm 			cmd->line = pi->line;
1117d8b32369Snicm 			TAILQ_INIT(&cmd->arguments);
1118a6afde38Snicm 		}
1119d8b32369Snicm 	}
1120d8b32369Snicm 	if (!TAILQ_EMPTY(&cmd->arguments))
1121d8b32369Snicm 		TAILQ_INSERT_TAIL(cmds, cmd, entry);
1122d8b32369Snicm 	else
1123d8b32369Snicm 		free(cmd);
11245d24dd80Snicm 
1125287cddb6Snicm 	cmd_parse_build_commands(cmds, pi, &pr);
1126287cddb6Snicm 	cmd_parse_free_commands(cmds);
1127287cddb6Snicm 	return (&pr);
1128a6afde38Snicm }
1129a6afde38Snicm 
1130df6ab229Snicm static int printflike(1, 2)
1131df6ab229Snicm yyerror(const char *fmt, ...)
1132df6ab229Snicm {
1133df6ab229Snicm 	struct cmd_parse_state	*ps = &parse_state;
1134df6ab229Snicm 	struct cmd_parse_input	*pi = ps->input;
1135df6ab229Snicm 	va_list			 ap;
1136df6ab229Snicm 	char			*error;
1137df6ab229Snicm 
1138df6ab229Snicm 	if (ps->error != NULL)
1139df6ab229Snicm 		return (0);
1140df6ab229Snicm 
1141df6ab229Snicm 	va_start(ap, fmt);
1142df6ab229Snicm 	xvasprintf(&error, fmt, ap);
1143df6ab229Snicm 	va_end(ap);
1144df6ab229Snicm 
1145df6ab229Snicm 	ps->error = cmd_parse_get_error(pi->file, pi->line, error);
1146df6ab229Snicm 	free(error);
1147df6ab229Snicm 	return (0);
1148df6ab229Snicm }
1149df6ab229Snicm 
1150df6ab229Snicm static int
1151df6ab229Snicm yylex_is_var(char ch, int first)
1152df6ab229Snicm {
1153df6ab229Snicm 	if (ch == '=')
1154df6ab229Snicm 		return (0);
1155df6ab229Snicm 	if (first && isdigit((u_char)ch))
1156df6ab229Snicm 		return (0);
1157df6ab229Snicm 	return (isalnum((u_char)ch) || ch == '_');
1158df6ab229Snicm }
1159df6ab229Snicm 
1160df6ab229Snicm static void
1161df6ab229Snicm yylex_append(char **buf, size_t *len, const char *add, size_t addlen)
1162df6ab229Snicm {
1163df6ab229Snicm 	if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen)
1164df6ab229Snicm 		fatalx("buffer is too big");
1165df6ab229Snicm 	*buf = xrealloc(*buf, (*len) + 1 + addlen);
1166df6ab229Snicm 	memcpy((*buf) + *len, add, addlen);
1167df6ab229Snicm 	(*len) += addlen;
1168df6ab229Snicm }
1169df6ab229Snicm 
1170df6ab229Snicm static void
1171df6ab229Snicm yylex_append1(char **buf, size_t *len, char add)
1172df6ab229Snicm {
1173df6ab229Snicm 	yylex_append(buf, len, &add, 1);
1174df6ab229Snicm }
1175df6ab229Snicm 
1176df6ab229Snicm static int
117773530c3cSnicm yylex_getc1(void)
117873530c3cSnicm {
117973530c3cSnicm 	struct cmd_parse_state	*ps = &parse_state;
118073530c3cSnicm 	int			 ch;
118173530c3cSnicm 
118273530c3cSnicm 	if (ps->f != NULL)
118373530c3cSnicm 		ch = getc(ps->f);
118473530c3cSnicm 	else {
118573530c3cSnicm 		if (ps->off == ps->len)
118673530c3cSnicm 			ch = EOF;
118773530c3cSnicm 		else
118873530c3cSnicm 			ch = ps->buf[ps->off++];
118973530c3cSnicm 	}
119073530c3cSnicm 	return (ch);
119173530c3cSnicm }
119273530c3cSnicm 
119373530c3cSnicm static void
119473530c3cSnicm yylex_ungetc(int ch)
119573530c3cSnicm {
119673530c3cSnicm 	struct cmd_parse_state	*ps = &parse_state;
119773530c3cSnicm 
119873530c3cSnicm 	if (ps->f != NULL)
119973530c3cSnicm 		ungetc(ch, ps->f);
120073530c3cSnicm 	else if (ps->off > 0 && ch != EOF)
120173530c3cSnicm 		ps->off--;
120273530c3cSnicm }
120373530c3cSnicm 
120473530c3cSnicm static int
1205df6ab229Snicm yylex_getc(void)
1206df6ab229Snicm {
1207df6ab229Snicm 	struct cmd_parse_state	*ps = &parse_state;
1208df6ab229Snicm 	int			 ch;
1209df6ab229Snicm 
1210df6ab229Snicm 	if (ps->escapes != 0) {
1211df6ab229Snicm 		ps->escapes--;
1212df6ab229Snicm 		return ('\\');
1213df6ab229Snicm 	}
1214df6ab229Snicm 	for (;;) {
121573530c3cSnicm 		ch = yylex_getc1();
1216df6ab229Snicm 		if (ch == '\\') {
1217df6ab229Snicm 			ps->escapes++;
1218df6ab229Snicm 			continue;
1219df6ab229Snicm 		}
1220df6ab229Snicm 		if (ch == '\n' && (ps->escapes % 2) == 1) {
1221df6ab229Snicm 			ps->input->line++;
1222df6ab229Snicm 			ps->escapes--;
1223df6ab229Snicm 			continue;
1224df6ab229Snicm 		}
1225df6ab229Snicm 
1226df6ab229Snicm 		if (ps->escapes != 0) {
122773530c3cSnicm 			yylex_ungetc(ch);
1228df6ab229Snicm 			ps->escapes--;
1229df6ab229Snicm 			return ('\\');
1230df6ab229Snicm 		}
1231df6ab229Snicm 		return (ch);
1232df6ab229Snicm 	}
1233df6ab229Snicm }
1234df6ab229Snicm 
1235df6ab229Snicm static char *
1236df6ab229Snicm yylex_get_word(int ch)
1237df6ab229Snicm {
1238df6ab229Snicm 	char	*buf;
1239df6ab229Snicm 	size_t	 len;
1240df6ab229Snicm 
1241df6ab229Snicm 	len = 0;
1242df6ab229Snicm 	buf = xmalloc(1);
1243df6ab229Snicm 
1244df6ab229Snicm 	do
1245df6ab229Snicm 		yylex_append1(&buf, &len, ch);
1246df6ab229Snicm 	while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL);
124773530c3cSnicm 	yylex_ungetc(ch);
1248df6ab229Snicm 
1249df6ab229Snicm 	buf[len] = '\0';
1250df6ab229Snicm 	log_debug("%s: %s", __func__, buf);
1251df6ab229Snicm 	return (buf);
1252df6ab229Snicm }
1253df6ab229Snicm 
1254df6ab229Snicm static int
1255df6ab229Snicm yylex(void)
1256df6ab229Snicm {
1257df6ab229Snicm 	struct cmd_parse_state	*ps = &parse_state;
1258df6ab229Snicm 	char			*token, *cp;
125909ba5f57Snicm 	int			 ch, next, condition;
1260df6ab229Snicm 
126147394861Snicm 	if (ps->eol)
126247394861Snicm 		ps->input->line++;
126347394861Snicm 	ps->eol = 0;
126447394861Snicm 
126509ba5f57Snicm 	condition = ps->condition;
126609ba5f57Snicm 	ps->condition = 0;
126709ba5f57Snicm 
1268df6ab229Snicm 	for (;;) {
1269df6ab229Snicm 		ch = yylex_getc();
1270df6ab229Snicm 
1271df6ab229Snicm 		if (ch == EOF) {
1272df6ab229Snicm 			/*
1273df6ab229Snicm 			 * Ensure every file or string is terminated by a
1274df6ab229Snicm 			 * newline. This keeps the parser simpler and avoids
1275df6ab229Snicm 			 * having to add a newline to each string.
1276df6ab229Snicm 			 */
1277df6ab229Snicm 			if (ps->eof)
1278df6ab229Snicm 				break;
1279df6ab229Snicm 			ps->eof = 1;
1280df6ab229Snicm 			return ('\n');
1281df6ab229Snicm 		}
1282df6ab229Snicm 
1283df6ab229Snicm 		if (ch == ' ' || ch == '\t') {
1284df6ab229Snicm 			/*
1285df6ab229Snicm 			 * Ignore whitespace.
1286df6ab229Snicm 			 */
1287df6ab229Snicm 			continue;
1288df6ab229Snicm 		}
1289df6ab229Snicm 
12902544af8fSnicm 		if (ch == '\r') {
12912544af8fSnicm 			/*
12922544af8fSnicm 			 * Treat \r\n as \n.
12932544af8fSnicm 			 */
12942544af8fSnicm 			ch = yylex_getc();
12952544af8fSnicm 			if (ch != '\n') {
12962544af8fSnicm 				yylex_ungetc(ch);
12972544af8fSnicm 				ch = '\r';
12982544af8fSnicm 			}
12992544af8fSnicm 		}
1300df6ab229Snicm 		if (ch == '\n') {
1301df6ab229Snicm 			/*
1302df6ab229Snicm 			 * End of line. Update the line number.
1303df6ab229Snicm 			 */
130447394861Snicm 			ps->eol = 1;
1305df6ab229Snicm 			return ('\n');
1306df6ab229Snicm 		}
1307df6ab229Snicm 
13082a840c62Snicm 		if (ch == ';' || ch == '{' || ch == '}') {
1309df6ab229Snicm 			/*
13102a840c62Snicm 			 * A semicolon or { or } is itself.
1311df6ab229Snicm 			 */
13122a840c62Snicm 			return (ch);
1313df6ab229Snicm 		}
1314df6ab229Snicm 
1315df6ab229Snicm 		if (ch == '#') {
1316df6ab229Snicm 			/*
131709ba5f57Snicm 			 * #{ after a condition opens a format; anything else
131809ba5f57Snicm 			 * is a comment, ignore up to the end of the line.
1319df6ab229Snicm 			 */
1320df6ab229Snicm 			next = yylex_getc();
132109ba5f57Snicm 			if (condition && next == '{') {
1322df6ab229Snicm 				yylval.token = yylex_format();
1323df6ab229Snicm 				if (yylval.token == NULL)
1324df6ab229Snicm 					return (ERROR);
1325df6ab229Snicm 				return (FORMAT);
1326df6ab229Snicm 			}
1327df6ab229Snicm 			while (next != '\n' && next != EOF)
1328df6ab229Snicm 				next = yylex_getc();
1329df6ab229Snicm 			if (next == '\n') {
1330df6ab229Snicm 				ps->input->line++;
1331df6ab229Snicm 				return ('\n');
1332df6ab229Snicm 			}
1333df6ab229Snicm 			continue;
1334df6ab229Snicm 		}
1335df6ab229Snicm 
1336df6ab229Snicm 		if (ch == '%') {
1337df6ab229Snicm 			/*
1338f48431f7Snicm 			 * % is a condition unless it is all % or all numbers,
1339f48431f7Snicm 			 * then it is a token.
1340df6ab229Snicm 			 */
1341df6ab229Snicm 			yylval.token = yylex_get_word('%');
1342f48431f7Snicm 			for (cp = yylval.token; *cp != '\0'; cp++) {
1343f48431f7Snicm 				if (*cp != '%' && !isdigit((u_char)*cp))
1344f48431f7Snicm 					break;
1345f48431f7Snicm 			}
1346f48431f7Snicm 			if (*cp == '\0')
1347df6ab229Snicm 				return (TOKEN);
134809ba5f57Snicm 			ps->condition = 1;
1349d6f6a5d2Snicm 			if (strcmp(yylval.token, "%hidden") == 0) {
1350d6f6a5d2Snicm 				free(yylval.token);
1351d6f6a5d2Snicm 				return (HIDDEN);
1352d6f6a5d2Snicm 			}
1353df6ab229Snicm 			if (strcmp(yylval.token, "%if") == 0) {
1354df6ab229Snicm 				free(yylval.token);
1355df6ab229Snicm 				return (IF);
1356df6ab229Snicm 			}
1357df6ab229Snicm 			if (strcmp(yylval.token, "%else") == 0) {
1358df6ab229Snicm 				free(yylval.token);
1359df6ab229Snicm 				return (ELSE);
1360df6ab229Snicm 			}
1361df6ab229Snicm 			if (strcmp(yylval.token, "%elif") == 0) {
1362df6ab229Snicm 				free(yylval.token);
1363df6ab229Snicm 				return (ELIF);
1364df6ab229Snicm 			}
1365df6ab229Snicm 			if (strcmp(yylval.token, "%endif") == 0) {
1366df6ab229Snicm 				free(yylval.token);
1367df6ab229Snicm 				return (ENDIF);
1368df6ab229Snicm 			}
1369df6ab229Snicm 			free(yylval.token);
1370df6ab229Snicm 			return (ERROR);
1371df6ab229Snicm 		}
1372df6ab229Snicm 
1373df6ab229Snicm 		/*
1374df6ab229Snicm 		 * Otherwise this is a token.
1375df6ab229Snicm 		 */
1376df6ab229Snicm 		token = yylex_token(ch);
1377df6ab229Snicm 		if (token == NULL)
1378df6ab229Snicm 			return (ERROR);
1379df6ab229Snicm 		yylval.token = token;
1380df6ab229Snicm 
1381df6ab229Snicm 		if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
1382df6ab229Snicm 			for (cp = token + 1; *cp != '='; cp++) {
1383df6ab229Snicm 				if (!yylex_is_var(*cp, 0))
1384df6ab229Snicm 					break;
1385df6ab229Snicm 			}
1386df6ab229Snicm 			if (*cp == '=')
1387df6ab229Snicm 				return (EQUALS);
1388df6ab229Snicm 		}
1389df6ab229Snicm 		return (TOKEN);
1390df6ab229Snicm 	}
1391df6ab229Snicm 	return (0);
1392df6ab229Snicm }
1393df6ab229Snicm 
1394df6ab229Snicm static char *
1395df6ab229Snicm yylex_format(void)
1396df6ab229Snicm {
1397df6ab229Snicm 	char	*buf;
1398df6ab229Snicm 	size_t	 len;
1399df6ab229Snicm 	int	 ch, brackets = 1;
1400df6ab229Snicm 
1401df6ab229Snicm 	len = 0;
1402df6ab229Snicm 	buf = xmalloc(1);
1403df6ab229Snicm 
1404df6ab229Snicm 	yylex_append(&buf, &len, "#{", 2);
1405df6ab229Snicm 	for (;;) {
1406df6ab229Snicm 		if ((ch = yylex_getc()) == EOF || ch == '\n')
1407df6ab229Snicm 			goto error;
1408df6ab229Snicm 		if (ch == '#') {
1409df6ab229Snicm 			if ((ch = yylex_getc()) == EOF || ch == '\n')
1410df6ab229Snicm 				goto error;
1411df6ab229Snicm 			if (ch == '{')
1412df6ab229Snicm 				brackets++;
1413df6ab229Snicm 			yylex_append1(&buf, &len, '#');
1414df6ab229Snicm 		} else if (ch == '}') {
1415df6ab229Snicm 			if (brackets != 0 && --brackets == 0) {
1416df6ab229Snicm 				yylex_append1(&buf, &len, ch);
1417df6ab229Snicm 				break;
1418df6ab229Snicm 			}
1419df6ab229Snicm 		}
1420df6ab229Snicm 		yylex_append1(&buf, &len, ch);
1421df6ab229Snicm 	}
1422df6ab229Snicm 	if (brackets != 0)
1423df6ab229Snicm 		goto error;
1424df6ab229Snicm 
1425df6ab229Snicm 	buf[len] = '\0';
1426df6ab229Snicm 	log_debug("%s: %s", __func__, buf);
1427df6ab229Snicm 	return (buf);
1428df6ab229Snicm 
1429df6ab229Snicm error:
1430df6ab229Snicm 	free(buf);
1431df6ab229Snicm 	return (NULL);
1432df6ab229Snicm }
1433df6ab229Snicm 
1434df6ab229Snicm static int
1435df6ab229Snicm yylex_token_escape(char **buf, size_t *len)
1436df6ab229Snicm {
14376852c63bSnicm 	int	 ch, type, o2, o3, mlen;
1438df6ab229Snicm 	u_int	 size, i, tmp;
14396852c63bSnicm 	char	 s[9], m[MB_LEN_MAX];
1440df6ab229Snicm 
1441dfc4c510Snicm 	ch = yylex_getc();
1442dfc4c510Snicm 
1443dfc4c510Snicm 	if (ch >= '4' && ch <= '7') {
1444dfc4c510Snicm 		yyerror("invalid octal escape");
1445dfc4c510Snicm 		return (0);
1446dfc4c510Snicm 	}
1447dfc4c510Snicm 	if (ch >= '0' && ch <= '3') {
1448dfc4c510Snicm 		o2 = yylex_getc();
1449dfc4c510Snicm 		if (o2 >= '0' && o2 <= '7') {
1450dfc4c510Snicm 			o3 = yylex_getc();
1451dfc4c510Snicm 			if (o3 >= '0' && o3 <= '7') {
1452dfc4c510Snicm 				ch = 64 * (ch - '0') +
1453dfc4c510Snicm 				      8 * (o2 - '0') +
1454dfc4c510Snicm 					  (o3 - '0');
1455dfc4c510Snicm 				yylex_append1(buf, len, ch);
1456dfc4c510Snicm 				return (1);
1457dfc4c510Snicm 			}
1458dfc4c510Snicm 		}
1459dfc4c510Snicm 		yyerror("invalid octal escape");
1460dfc4c510Snicm 		return (0);
1461dfc4c510Snicm 	}
1462dfc4c510Snicm 
1463dfc4c510Snicm 	switch (ch) {
1464df6ab229Snicm 	case EOF:
1465df6ab229Snicm 		return (0);
146606feda64Snicm 	case 'a':
146706feda64Snicm 		ch = '\a';
146806feda64Snicm 		break;
146906feda64Snicm 	case 'b':
147006feda64Snicm 		ch = '\b';
147106feda64Snicm 		break;
1472df6ab229Snicm 	case 'e':
1473df6ab229Snicm 		ch = '\033';
1474df6ab229Snicm 		break;
147506feda64Snicm 	case 'f':
147606feda64Snicm 		ch = '\f';
147706feda64Snicm 		break;
147806feda64Snicm 	case 's':
147906feda64Snicm 		ch = ' ';
148006feda64Snicm 		break;
148106feda64Snicm 	case 'v':
148206feda64Snicm 		ch = '\v';
148306feda64Snicm 		break;
1484df6ab229Snicm 	case 'r':
1485df6ab229Snicm 		ch = '\r';
1486df6ab229Snicm 		break;
1487df6ab229Snicm 	case 'n':
1488df6ab229Snicm 		ch = '\n';
1489df6ab229Snicm 		break;
1490df6ab229Snicm 	case 't':
1491df6ab229Snicm 		ch = '\t';
1492df6ab229Snicm 		break;
1493df6ab229Snicm 	case 'u':
1494df6ab229Snicm 		type = 'u';
1495df6ab229Snicm 		size = 4;
1496df6ab229Snicm 		goto unicode;
1497df6ab229Snicm 	case 'U':
1498df6ab229Snicm 		type = 'U';
1499df6ab229Snicm 		size = 8;
1500df6ab229Snicm 		goto unicode;
1501df6ab229Snicm 	}
1502df6ab229Snicm 
1503df6ab229Snicm 	yylex_append1(buf, len, ch);
1504df6ab229Snicm 	return (1);
1505df6ab229Snicm 
1506df6ab229Snicm unicode:
1507df6ab229Snicm 	for (i = 0; i < size; i++) {
1508df6ab229Snicm 		ch = yylex_getc();
1509df6ab229Snicm 		if (ch == EOF || ch == '\n')
1510df6ab229Snicm 			return (0);
1511df6ab229Snicm 		if (!isxdigit((u_char)ch)) {
1512df6ab229Snicm 			yyerror("invalid \\%c argument", type);
1513df6ab229Snicm 			return (0);
1514df6ab229Snicm 		}
1515df6ab229Snicm 		s[i] = ch;
1516df6ab229Snicm 	}
1517df6ab229Snicm 	s[i] = '\0';
1518df6ab229Snicm 
1519df6ab229Snicm 	if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) ||
1520df6ab229Snicm 	    (size == 8 && sscanf(s, "%8x", &tmp) != 1)) {
1521df6ab229Snicm 		yyerror("invalid \\%c argument", type);
1522df6ab229Snicm 		return (0);
1523df6ab229Snicm 	}
15246852c63bSnicm 	mlen = wctomb(m, tmp);
15256852c63bSnicm 	if (mlen <= 0 || mlen > (int)sizeof m) {
1526df6ab229Snicm 		yyerror("invalid \\%c argument", type);
1527df6ab229Snicm 		return (0);
1528df6ab229Snicm 	}
15296852c63bSnicm 	yylex_append(buf, len, m, mlen);
1530df6ab229Snicm 	return (1);
1531df6ab229Snicm }
1532df6ab229Snicm 
1533df6ab229Snicm static int
1534df6ab229Snicm yylex_token_variable(char **buf, size_t *len)
1535df6ab229Snicm {
1536df6ab229Snicm 	struct environ_entry	*envent;
1537df6ab229Snicm 	int			 ch, brackets = 0;
15384512d27dSnicm 	char			 name[1024];
1539df6ab229Snicm 	size_t			 namelen = 0;
1540df6ab229Snicm 	const char		*value;
1541df6ab229Snicm 
1542df6ab229Snicm 	ch = yylex_getc();
1543df6ab229Snicm 	if (ch == EOF)
1544df6ab229Snicm 		return (0);
1545df6ab229Snicm 	if (ch == '{')
1546df6ab229Snicm 		brackets = 1;
1547df6ab229Snicm 	else {
1548df6ab229Snicm 		if (!yylex_is_var(ch, 1)) {
1549df6ab229Snicm 			yylex_append1(buf, len, '$');
155073530c3cSnicm 			yylex_ungetc(ch);
1551df6ab229Snicm 			return (1);
1552df6ab229Snicm 		}
1553df6ab229Snicm 		name[namelen++] = ch;
1554df6ab229Snicm 	}
1555df6ab229Snicm 
1556df6ab229Snicm 	for (;;) {
1557df6ab229Snicm 		ch = yylex_getc();
1558df6ab229Snicm 		if (brackets && ch == '}')
1559df6ab229Snicm 			break;
1560df6ab229Snicm 		if (ch == EOF || !yylex_is_var(ch, 0)) {
1561df6ab229Snicm 			if (!brackets) {
156273530c3cSnicm 				yylex_ungetc(ch);
1563df6ab229Snicm 				break;
1564df6ab229Snicm 			}
1565df6ab229Snicm 			yyerror("invalid environment variable");
1566df6ab229Snicm 			return (0);
1567df6ab229Snicm 		}
1568df6ab229Snicm 		if (namelen == (sizeof name) - 2) {
1569df6ab229Snicm 			yyerror("environment variable is too long");
1570df6ab229Snicm 			return (0);
1571df6ab229Snicm 		}
1572df6ab229Snicm 		name[namelen++] = ch;
1573df6ab229Snicm 	}
1574df6ab229Snicm 	name[namelen] = '\0';
1575df6ab229Snicm 
1576df6ab229Snicm 	envent = environ_find(global_environ, name);
15779beba1feSnicm 	if (envent != NULL && envent->value != NULL) {
1578df6ab229Snicm 		value = envent->value;
1579df6ab229Snicm 		log_debug("%s: %s -> %s", __func__, name, value);
1580df6ab229Snicm 		yylex_append(buf, len, value, strlen(value));
1581df6ab229Snicm 	}
1582df6ab229Snicm 	return (1);
1583df6ab229Snicm }
1584df6ab229Snicm 
1585df6ab229Snicm static int
1586df6ab229Snicm yylex_token_tilde(char **buf, size_t *len)
1587df6ab229Snicm {
1588df6ab229Snicm 	struct environ_entry	*envent;
1589df6ab229Snicm 	int			 ch;
15904512d27dSnicm 	char			 name[1024];
1591df6ab229Snicm 	size_t			 namelen = 0;
1592df6ab229Snicm 	struct passwd		*pw;
1593df6ab229Snicm 	const char		*home = NULL;
1594df6ab229Snicm 
1595df6ab229Snicm 	for (;;) {
1596df6ab229Snicm 		ch = yylex_getc();
1597df6ab229Snicm 		if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) {
159873530c3cSnicm 			yylex_ungetc(ch);
1599df6ab229Snicm 			break;
1600df6ab229Snicm 		}
1601df6ab229Snicm 		if (namelen == (sizeof name) - 2) {
1602df6ab229Snicm 			yyerror("user name is too long");
1603df6ab229Snicm 			return (0);
1604df6ab229Snicm 		}
1605df6ab229Snicm 		name[namelen++] = ch;
1606df6ab229Snicm 	}
1607df6ab229Snicm 	name[namelen] = '\0';
1608df6ab229Snicm 
1609df6ab229Snicm 	if (*name == '\0') {
1610df6ab229Snicm 		envent = environ_find(global_environ, "HOME");
1611df6ab229Snicm 		if (envent != NULL && *envent->value != '\0')
1612df6ab229Snicm 			home = envent->value;
1613df6ab229Snicm 		else if ((pw = getpwuid(getuid())) != NULL)
1614df6ab229Snicm 			home = pw->pw_dir;
1615df6ab229Snicm 	} else {
1616df6ab229Snicm 		if ((pw = getpwnam(name)) != NULL)
1617df6ab229Snicm 			home = pw->pw_dir;
1618df6ab229Snicm 	}
1619df6ab229Snicm 	if (home == NULL)
1620df6ab229Snicm 		return (0);
1621df6ab229Snicm 
1622df6ab229Snicm 	log_debug("%s: ~%s -> %s", __func__, name, home);
1623df6ab229Snicm 	yylex_append(buf, len, home, strlen(home));
1624df6ab229Snicm 	return (1);
1625df6ab229Snicm }
1626df6ab229Snicm 
1627df6ab229Snicm static char *
1628df6ab229Snicm yylex_token(int ch)
1629df6ab229Snicm {
1630*79be0cc0Snicm 	struct cmd_parse_state	*ps = &parse_state;
1631df6ab229Snicm 	char			*buf;
1632df6ab229Snicm 	size_t			 len;
1633df6ab229Snicm 	enum { START,
1634df6ab229Snicm 	       NONE,
1635df6ab229Snicm 	       DOUBLE_QUOTES,
1636df6ab229Snicm 	       SINGLE_QUOTES }	 state = NONE, last = START;
1637df6ab229Snicm 
1638df6ab229Snicm 	len = 0;
1639df6ab229Snicm 	buf = xmalloc(1);
1640df6ab229Snicm 
1641df6ab229Snicm 	for (;;) {
1642af9d9f3bSnicm 		/* EOF or \n are always the end of the token. */
1643a01f743bSnicm 		if (ch == EOF) {
1644a01f743bSnicm 			log_debug("%s: end at EOF", __func__);
1645df6ab229Snicm 			break;
1646a01f743bSnicm 		}
16472544af8fSnicm 		if (state == NONE && ch == '\r') {
16482544af8fSnicm 			ch = yylex_getc();
16492544af8fSnicm 			if (ch != '\n') {
16502544af8fSnicm 				yylex_ungetc(ch);
16512544af8fSnicm 				ch = '\r';
16522544af8fSnicm 			}
16532544af8fSnicm 		}
1654*79be0cc0Snicm 		if (ch == '\n') {
1655*79be0cc0Snicm 			if (state == NONE) {
1656a01f743bSnicm 				log_debug("%s: end at EOL", __func__);
1657a01f743bSnicm 				break;
1658a01f743bSnicm 			}
1659*79be0cc0Snicm 			ps->input->line++;
1660*79be0cc0Snicm 		}
1661df6ab229Snicm 
1662af9d9f3bSnicm 		/* Whitespace or ; or } ends a token unless inside quotes. */
1663a01f743bSnicm 		if (state == NONE && (ch == ' ' || ch == '\t')) {
1664a01f743bSnicm 			log_debug("%s: end at WS", __func__);
1665df6ab229Snicm 			break;
1666a01f743bSnicm 		}
1667a01f743bSnicm 		if (state == NONE && (ch == ';' || ch == '}')) {
1668a01f743bSnicm 			log_debug("%s: end at %c", __func__, ch);
1669a01f743bSnicm 			break;
1670a01f743bSnicm 		}
1671df6ab229Snicm 
1672d156f0b4Snicm 		/*
1673d156f0b4Snicm 		 * Spaces and comments inside quotes after \n are removed but
1674d156f0b4Snicm 		 * the \n is left.
1675d156f0b4Snicm 		 */
1676af9d9f3bSnicm 		if (ch == '\n' && state != NONE) {
1677d156f0b4Snicm 			yylex_append1(&buf, &len, '\n');
1678af9d9f3bSnicm 			while ((ch = yylex_getc()) == ' ' || ch == '\t')
1679af9d9f3bSnicm 				/* nothing */;
1680af9d9f3bSnicm 			if (ch != '#')
1681af9d9f3bSnicm 				continue;
1682af9d9f3bSnicm 			ch = yylex_getc();
1683af9d9f3bSnicm 			if (strchr(",#{}:", ch) != NULL) {
1684af9d9f3bSnicm 				yylex_ungetc(ch);
1685af9d9f3bSnicm 				ch = '#';
1686af9d9f3bSnicm 			} else {
1687af9d9f3bSnicm 				while ((ch = yylex_getc()) != '\n' && ch != EOF)
1688af9d9f3bSnicm 					/* nothing */;
1689af9d9f3bSnicm 			}
1690af9d9f3bSnicm 			continue;
1691af9d9f3bSnicm 		}
1692af9d9f3bSnicm 
1693af9d9f3bSnicm 		/* \ ~ and $ are expanded except in single quotes. */
1694df6ab229Snicm 		if (ch == '\\' && state != SINGLE_QUOTES) {
1695df6ab229Snicm 			if (!yylex_token_escape(&buf, &len))
1696df6ab229Snicm 				goto error;
1697df6ab229Snicm 			goto skip;
1698df6ab229Snicm 		}
1699df6ab229Snicm 		if (ch == '~' && last != state && state != SINGLE_QUOTES) {
1700df6ab229Snicm 			if (!yylex_token_tilde(&buf, &len))
1701df6ab229Snicm 				goto error;
1702df6ab229Snicm 			goto skip;
1703df6ab229Snicm 		}
1704df6ab229Snicm 		if (ch == '$' && state != SINGLE_QUOTES) {
1705df6ab229Snicm 			if (!yylex_token_variable(&buf, &len))
1706df6ab229Snicm 				goto error;
1707df6ab229Snicm 			goto skip;
1708df6ab229Snicm 		}
17091c947278Snicm 		if (ch == '}' && state == NONE)
17101c947278Snicm 			goto error;  /* unmatched (matched ones were handled) */
1711df6ab229Snicm 
1712af9d9f3bSnicm 		/* ' and " starts or end quotes (and is consumed). */
1713df6ab229Snicm 		if (ch == '\'') {
1714df6ab229Snicm 			if (state == NONE) {
1715df6ab229Snicm 				state = SINGLE_QUOTES;
1716df6ab229Snicm 				goto next;
1717df6ab229Snicm 			}
1718df6ab229Snicm 			if (state == SINGLE_QUOTES) {
1719df6ab229Snicm 				state = NONE;
1720df6ab229Snicm 				goto next;
1721df6ab229Snicm 			}
1722df6ab229Snicm 		}
1723df6ab229Snicm 		if (ch == '"') {
1724df6ab229Snicm 			if (state == NONE) {
1725df6ab229Snicm 				state = DOUBLE_QUOTES;
1726df6ab229Snicm 				goto next;
1727df6ab229Snicm 			}
1728df6ab229Snicm 			if (state == DOUBLE_QUOTES) {
1729df6ab229Snicm 				state = NONE;
1730df6ab229Snicm 				goto next;
1731df6ab229Snicm 			}
1732df6ab229Snicm 		}
1733df6ab229Snicm 
1734af9d9f3bSnicm 		/* Otherwise add the character to the buffer. */
1735df6ab229Snicm 		yylex_append1(&buf, &len, ch);
1736df6ab229Snicm 
1737df6ab229Snicm 	skip:
1738df6ab229Snicm 		last = state;
1739df6ab229Snicm 
1740df6ab229Snicm 	next:
1741df6ab229Snicm 		ch = yylex_getc();
1742df6ab229Snicm 	}
174373530c3cSnicm 	yylex_ungetc(ch);
1744df6ab229Snicm 
1745df6ab229Snicm 	buf[len] = '\0';
1746df6ab229Snicm 	log_debug("%s: %s", __func__, buf);
1747df6ab229Snicm 	return (buf);
1748df6ab229Snicm 
1749df6ab229Snicm error:
1750df6ab229Snicm 	free(buf);
1751df6ab229Snicm 	return (NULL);
1752df6ab229Snicm }
1753