xref: /openbsd/usr.bin/tmux/cmd-parse.y (revision a01f743b)
1*a01f743bSnicm /* $OpenBSD: cmd-parse.y,v 1.50 2023/03/15 08:15:39 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;
226df6ab229Snicm 
227df6ab229Snicm 			if ((~flags & CMD_PARSE_PARSEONLY) &&
228df6ab229Snicm 			    (ps->scope == NULL || ps->scope->flag))
229d6f6a5d2Snicm 				environ_put(global_environ, $1, 0);
230df6ab229Snicm 			free($1);
231df6ab229Snicm 		}
232df6ab229Snicm 
233d6f6a5d2Snicm hidden_assignment : HIDDEN EQUALS
234d6f6a5d2Snicm 		{
235d6f6a5d2Snicm 			struct cmd_parse_state	*ps = &parse_state;
236d6f6a5d2Snicm 			int			 flags = ps->input->flags;
237d6f6a5d2Snicm 
238d6f6a5d2Snicm 			if ((~flags & CMD_PARSE_PARSEONLY) &&
239d6f6a5d2Snicm 			    (ps->scope == NULL || ps->scope->flag))
240d6f6a5d2Snicm 				environ_put(global_environ, $2, ENVIRON_HIDDEN);
241d6f6a5d2Snicm 			free($2);
242d6f6a5d2Snicm 		}
243d6f6a5d2Snicm 
244df6ab229Snicm if_open		: IF expanded
245df6ab229Snicm 		{
246df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
247df6ab229Snicm 			struct cmd_parse_scope	*scope;
248df6ab229Snicm 
249df6ab229Snicm 			scope = xmalloc(sizeof *scope);
250df6ab229Snicm 			$$ = scope->flag = format_true($2);
251df6ab229Snicm 			free($2);
252df6ab229Snicm 
253df6ab229Snicm 			if (ps->scope != NULL)
254df6ab229Snicm 				TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry);
255df6ab229Snicm 			ps->scope = scope;
256df6ab229Snicm 		}
257df6ab229Snicm 
258df6ab229Snicm if_else		: ELSE
259df6ab229Snicm 		{
260df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
261df6ab229Snicm 			struct cmd_parse_scope	*scope;
262df6ab229Snicm 
263df6ab229Snicm 			scope = xmalloc(sizeof *scope);
264df6ab229Snicm 			scope->flag = !ps->scope->flag;
265df6ab229Snicm 
266df6ab229Snicm 			free(ps->scope);
267df6ab229Snicm 			ps->scope = scope;
268df6ab229Snicm 		}
269df6ab229Snicm 
270df6ab229Snicm if_elif		: ELIF expanded
271df6ab229Snicm 		{
272df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
273df6ab229Snicm 			struct cmd_parse_scope	*scope;
274df6ab229Snicm 
275df6ab229Snicm 			scope = xmalloc(sizeof *scope);
276df6ab229Snicm 			$$ = scope->flag = format_true($2);
277df6ab229Snicm 			free($2);
278df6ab229Snicm 
279df6ab229Snicm 			free(ps->scope);
280df6ab229Snicm 			ps->scope = scope;
281df6ab229Snicm 		}
282df6ab229Snicm 
283df6ab229Snicm if_close	: ENDIF
284df6ab229Snicm 		{
285df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
286df6ab229Snicm 
287df6ab229Snicm 			free(ps->scope);
288df6ab229Snicm 			ps->scope = TAILQ_FIRST(&ps->stack);
289df6ab229Snicm 			if (ps->scope != NULL)
290df6ab229Snicm 				TAILQ_REMOVE(&ps->stack, ps->scope, entry);
291df6ab229Snicm 		}
292df6ab229Snicm 
293df6ab229Snicm condition	: if_open '\n' statements if_close
294df6ab229Snicm 		{
295df6ab229Snicm 			if ($1)
2965d24dd80Snicm 				$$ = $3;
2975d24dd80Snicm 			else {
2985d24dd80Snicm 				$$ = cmd_parse_new_commands();
2995d24dd80Snicm 				cmd_parse_free_commands($3);
3005d24dd80Snicm 			}
301df6ab229Snicm 		}
302df6ab229Snicm 		| if_open '\n' statements if_else '\n' statements if_close
303df6ab229Snicm 		{
304df6ab229Snicm 			if ($1) {
3055d24dd80Snicm 				$$ = $3;
3065d24dd80Snicm 				cmd_parse_free_commands($6);
307df6ab229Snicm 			} else {
3085d24dd80Snicm 				$$ = $6;
3095d24dd80Snicm 				cmd_parse_free_commands($3);
310df6ab229Snicm 			}
311df6ab229Snicm 		}
312df6ab229Snicm 		| if_open '\n' statements elif if_close
313df6ab229Snicm 		{
314df6ab229Snicm 			if ($1) {
3155d24dd80Snicm 				$$ = $3;
3165d24dd80Snicm 				cmd_parse_free_commands($4.commands);
317df6ab229Snicm 			} else if ($4.flag) {
3185d24dd80Snicm 				$$ = $4.commands;
3195d24dd80Snicm 				cmd_parse_free_commands($3);
320df6ab229Snicm 			} else {
3215d24dd80Snicm 				$$ = cmd_parse_new_commands();
3225d24dd80Snicm 				cmd_parse_free_commands($3);
3235d24dd80Snicm 				cmd_parse_free_commands($4.commands);
324df6ab229Snicm 			}
325df6ab229Snicm 		}
326df6ab229Snicm 		| if_open '\n' statements elif if_else '\n' statements if_close
327df6ab229Snicm 		{
328df6ab229Snicm 			if ($1) {
3295d24dd80Snicm 				$$ = $3;
3305d24dd80Snicm 				cmd_parse_free_commands($4.commands);
3315d24dd80Snicm 				cmd_parse_free_commands($7);
332df6ab229Snicm 			} else if ($4.flag) {
3335d24dd80Snicm 				$$ = $4.commands;
3345d24dd80Snicm 				cmd_parse_free_commands($3);
3355d24dd80Snicm 				cmd_parse_free_commands($7);
336df6ab229Snicm 			} else {
3375d24dd80Snicm 				$$ = $7;
3385d24dd80Snicm 				cmd_parse_free_commands($3);
3395d24dd80Snicm 				cmd_parse_free_commands($4.commands);
340df6ab229Snicm 			}
341df6ab229Snicm 		}
342df6ab229Snicm 
343df6ab229Snicm elif		: if_elif '\n' statements
344df6ab229Snicm 		{
3455d24dd80Snicm 			if ($1) {
3465d24dd80Snicm 				$$.flag = 1;
3475d24dd80Snicm 				$$.commands = $3;
3485d24dd80Snicm 			} else {
3495d24dd80Snicm 				$$.flag = 0;
3505d24dd80Snicm 				$$.commands = cmd_parse_new_commands();
3515d24dd80Snicm 				cmd_parse_free_commands($3);
3525d24dd80Snicm 			}
353df6ab229Snicm 		}
354df6ab229Snicm 		| if_elif '\n' statements elif
355df6ab229Snicm 		{
356df6ab229Snicm 			if ($1) {
357df6ab229Snicm 				$$.flag = 1;
3585d24dd80Snicm 				$$.commands = $3;
3595d24dd80Snicm 				cmd_parse_free_commands($4.commands);
3605d24dd80Snicm 			} else if ($4.flag) {
3615d24dd80Snicm 				$$.flag = 1;
3625d24dd80Snicm 				$$.commands = $4.commands;
3635d24dd80Snicm 				cmd_parse_free_commands($3);
364df6ab229Snicm 			} else {
3655d24dd80Snicm 				$$.flag = 0;
3665d24dd80Snicm 				$$.commands = cmd_parse_new_commands();
3675d24dd80Snicm 				cmd_parse_free_commands($3);
3685d24dd80Snicm 				cmd_parse_free_commands($4.commands);
369df6ab229Snicm 			}
370df6ab229Snicm 		}
371df6ab229Snicm 
372df6ab229Snicm commands	: command
373df6ab229Snicm 		{
374df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
375df6ab229Snicm 
3765d24dd80Snicm 			$$ = cmd_parse_new_commands();
377b6e38b61Snicm 			if (!TAILQ_EMPTY(&$1->arguments) &&
37859b819b0Snicm 			    (ps->scope == NULL || ps->scope->flag))
3795d24dd80Snicm 				TAILQ_INSERT_TAIL($$, $1, entry);
380df6ab229Snicm 			else
381df6ab229Snicm 				cmd_parse_free_command($1);
382df6ab229Snicm 		}
383df6ab229Snicm 		| commands ';'
384df6ab229Snicm 		{
3855d24dd80Snicm 			$$ = $1;
386df6ab229Snicm 		}
387df6ab229Snicm 		| commands ';' condition1
388df6ab229Snicm 		{
3895d24dd80Snicm 			$$ = $1;
3905d24dd80Snicm 			TAILQ_CONCAT($$, $3, entry);
3915d24dd80Snicm 			free($3);
392df6ab229Snicm 		}
393df6ab229Snicm 		| commands ';' command
394df6ab229Snicm 		{
395df6ab229Snicm 			struct cmd_parse_state	*ps = &parse_state;
396df6ab229Snicm 
397b6e38b61Snicm 			if (!TAILQ_EMPTY(&$3->arguments) &&
39859b819b0Snicm 			    (ps->scope == NULL || ps->scope->flag)) {
3995d24dd80Snicm 				$$ = $1;
4005d24dd80Snicm 				TAILQ_INSERT_TAIL($$, $3, entry);
401df6ab229Snicm 			} else {
4025d24dd80Snicm 				$$ = cmd_parse_new_commands();
4035d24dd80Snicm 				cmd_parse_free_commands($1);
404df6ab229Snicm 				cmd_parse_free_command($3);
405df6ab229Snicm 			}
406df6ab229Snicm 		}
407df6ab229Snicm 		| condition1
408df6ab229Snicm 		{
4095d24dd80Snicm 			$$ = $1;
410df6ab229Snicm 		}
411df6ab229Snicm 
412bec5bc45Snicm command		: assignment
413bec5bc45Snicm 		{
414bec5bc45Snicm 			struct cmd_parse_state	*ps = &parse_state;
415bec5bc45Snicm 
416bec5bc45Snicm 			$$ = xcalloc(1, sizeof *$$);
417bec5bc45Snicm 			$$->line = ps->input->line;
418b6e38b61Snicm 			TAILQ_INIT(&$$->arguments);
419bec5bc45Snicm 		}
420bec5bc45Snicm 		| optional_assignment TOKEN
421df6ab229Snicm 		{
422df6ab229Snicm 			struct cmd_parse_state		*ps = &parse_state;
423b6e38b61Snicm 			struct cmd_parse_argument	*arg;
424df6ab229Snicm 
425df6ab229Snicm 			$$ = xcalloc(1, sizeof *$$);
4261ab16754Snicm 			$$->line = ps->input->line;
427b6e38b61Snicm 			TAILQ_INIT(&$$->arguments);
428df6ab229Snicm 
429b6e38b61Snicm 			arg = xcalloc(1, sizeof *arg);
430b6e38b61Snicm 			arg->type = CMD_PARSE_STRING;
4311d297f78Snicm 			arg->string = $2;
432b6e38b61Snicm 			TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
433df6ab229Snicm 		}
434bec5bc45Snicm 		| optional_assignment TOKEN arguments
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 			TAILQ_CONCAT(&$$->arguments, $3, entry);
444b6e38b61Snicm 			free($3);
445b6e38b61Snicm 
446b6e38b61Snicm 			arg = xcalloc(1, sizeof *arg);
447b6e38b61Snicm 			arg->type = CMD_PARSE_STRING;
4481d297f78Snicm 			arg->string = $2;
449b6e38b61Snicm 			TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
450df6ab229Snicm 		}
451df6ab229Snicm 
452df6ab229Snicm condition1	: if_open commands if_close
453df6ab229Snicm 		{
454df6ab229Snicm 			if ($1)
4555d24dd80Snicm 				$$ = $2;
4565d24dd80Snicm 			else {
4575d24dd80Snicm 				$$ = cmd_parse_new_commands();
4585d24dd80Snicm 				cmd_parse_free_commands($2);
4595d24dd80Snicm 			}
460df6ab229Snicm 		}
461df6ab229Snicm 		| if_open commands if_else commands if_close
462df6ab229Snicm 		{
463df6ab229Snicm 			if ($1) {
4645d24dd80Snicm 				$$ = $2;
4655d24dd80Snicm 				cmd_parse_free_commands($4);
466df6ab229Snicm 			} else {
4675d24dd80Snicm 				$$ = $4;
4685d24dd80Snicm 				cmd_parse_free_commands($2);
469df6ab229Snicm 			}
470df6ab229Snicm 		}
471df6ab229Snicm 		| if_open commands elif1 if_close
472df6ab229Snicm 		{
473df6ab229Snicm 			if ($1) {
4745d24dd80Snicm 				$$ = $2;
4755d24dd80Snicm 				cmd_parse_free_commands($3.commands);
476df6ab229Snicm 			} else if ($3.flag) {
4775d24dd80Snicm 				$$ = $3.commands;
4785d24dd80Snicm 				cmd_parse_free_commands($2);
479df6ab229Snicm 			} else {
4805d24dd80Snicm 				$$ = cmd_parse_new_commands();
4815d24dd80Snicm 				cmd_parse_free_commands($2);
4825d24dd80Snicm 				cmd_parse_free_commands($3.commands);
483df6ab229Snicm 			}
484df6ab229Snicm 		}
485df6ab229Snicm 		| if_open commands elif1 if_else commands if_close
486df6ab229Snicm 		{
487df6ab229Snicm 			if ($1) {
4885d24dd80Snicm 				$$ = $2;
4895d24dd80Snicm 				cmd_parse_free_commands($3.commands);
4905d24dd80Snicm 				cmd_parse_free_commands($5);
491df6ab229Snicm 			} else if ($3.flag) {
4925d24dd80Snicm 				$$ = $3.commands;
4935d24dd80Snicm 				cmd_parse_free_commands($2);
4945d24dd80Snicm 				cmd_parse_free_commands($5);
495df6ab229Snicm 			} else {
4965d24dd80Snicm 				$$ = $5;
4975d24dd80Snicm 				cmd_parse_free_commands($2);
4985d24dd80Snicm 				cmd_parse_free_commands($3.commands);
499df6ab229Snicm 			}
500df6ab229Snicm 		}
501df6ab229Snicm 
502df6ab229Snicm elif1		: if_elif commands
503df6ab229Snicm 		{
5045d24dd80Snicm 			if ($1) {
5055d24dd80Snicm 				$$.flag = 1;
5065d24dd80Snicm 				$$.commands = $2;
5075d24dd80Snicm 			} else {
5085d24dd80Snicm 				$$.flag = 0;
5095d24dd80Snicm 				$$.commands = cmd_parse_new_commands();
5105d24dd80Snicm 				cmd_parse_free_commands($2);
5115d24dd80Snicm 			}
512df6ab229Snicm 		}
513df6ab229Snicm 		| if_elif commands elif1
514df6ab229Snicm 		{
515df6ab229Snicm 			if ($1) {
516df6ab229Snicm 				$$.flag = 1;
5175d24dd80Snicm 				$$.commands = $2;
5185d24dd80Snicm 				cmd_parse_free_commands($3.commands);
5195d24dd80Snicm 			} else if ($3.flag) {
5205d24dd80Snicm 				$$.flag = 1;
5215d24dd80Snicm 				$$.commands = $3.commands;
5225d24dd80Snicm 				cmd_parse_free_commands($2);
523df6ab229Snicm 			} else {
5245d24dd80Snicm 				$$.flag = 0;
5255d24dd80Snicm 				$$.commands = cmd_parse_new_commands();
5265d24dd80Snicm 				cmd_parse_free_commands($2);
5275d24dd80Snicm 				cmd_parse_free_commands($3.commands);
528df6ab229Snicm 			}
529df6ab229Snicm 		}
530df6ab229Snicm 
531df6ab229Snicm arguments	: argument
532df6ab229Snicm 		{
533b6e38b61Snicm 			$$ = xcalloc(1, sizeof *$$);
534b6e38b61Snicm 			TAILQ_INIT($$);
535df6ab229Snicm 
536b6e38b61Snicm 			TAILQ_INSERT_HEAD($$, $1, entry);
537df6ab229Snicm 		}
538df6ab229Snicm 		| argument arguments
539df6ab229Snicm 		{
540b6e38b61Snicm 			TAILQ_INSERT_HEAD($2, $1, entry);
541df6ab229Snicm 			$$ = $2;
542df6ab229Snicm 		}
543df6ab229Snicm 
544df6ab229Snicm argument	: TOKEN
545df6ab229Snicm 		{
546b6e38b61Snicm 			$$ = xcalloc(1, sizeof *$$);
547b6e38b61Snicm 			$$->type = CMD_PARSE_STRING;
5481d297f78Snicm 			$$->string = $1;
549df6ab229Snicm 		}
550df6ab229Snicm 		| EQUALS
551df6ab229Snicm 		{
552b6e38b61Snicm 			$$ = xcalloc(1, sizeof *$$);
553b6e38b61Snicm 			$$->type = CMD_PARSE_STRING;
5541d297f78Snicm 			$$->string = $1;
555df6ab229Snicm 		}
5562a840c62Snicm 		| '{' argument_statements
5572a840c62Snicm 		{
558b6e38b61Snicm 			$$ = xcalloc(1, sizeof *$$);
559b6e38b61Snicm 			$$->type = CMD_PARSE_COMMANDS;
560b6e38b61Snicm 			$$->commands = $2;
5612a840c62Snicm 		}
5622a840c62Snicm 
5632a840c62Snicm argument_statements	: statement '}'
5642a840c62Snicm 			{
5652a840c62Snicm 				$$ = $1;
5662a840c62Snicm 			}
56724479cbfSnicm 			| statements statement '}'
5682a840c62Snicm 			{
5692a840c62Snicm 				$$ = $1;
57024479cbfSnicm 				TAILQ_CONCAT($$, $2, entry);
57124479cbfSnicm 				free($2);
5722a840c62Snicm 			}
573df6ab229Snicm 
574df6ab229Snicm %%
575df6ab229Snicm 
576df6ab229Snicm static char *
577df6ab229Snicm cmd_parse_get_error(const char *file, u_int line, const char *error)
578df6ab229Snicm {
579df6ab229Snicm 	char	*s;
580df6ab229Snicm 
581df6ab229Snicm 	if (file == NULL)
582df6ab229Snicm 		s = xstrdup(error);
583df6ab229Snicm 	else
584df6ab229Snicm 		xasprintf(&s, "%s:%u: %s", file, line, error);
585df6ab229Snicm 	return (s);
586df6ab229Snicm }
587df6ab229Snicm 
588df6ab229Snicm static void
cmd_parse_print_commands(struct cmd_parse_input * pi,struct cmd_list * cmdlist)589287cddb6Snicm cmd_parse_print_commands(struct cmd_parse_input *pi, struct cmd_list *cmdlist)
5905304b409Snicm {
5915304b409Snicm 	char	*s;
5925304b409Snicm 
593287cddb6Snicm 	if (pi->item == NULL || (~pi->flags & CMD_PARSE_VERBOSE))
594287cddb6Snicm 		return;
5955304b409Snicm 	s = cmd_list_print(cmdlist, 0);
596d47c8058Snicm 	if (pi->file != NULL)
597287cddb6Snicm 		cmdq_print(pi->item, "%s:%u: %s", pi->file, pi->line, s);
598d47c8058Snicm 	else
599287cddb6Snicm 		cmdq_print(pi->item, "%u: %s", pi->line, s);
6005304b409Snicm 	free(s);
6015304b409Snicm }
6025304b409Snicm 
6035304b409Snicm static void
cmd_parse_free_argument(struct cmd_parse_argument * arg)604287cddb6Snicm cmd_parse_free_argument(struct cmd_parse_argument *arg)
605b6e38b61Snicm {
606b6e38b61Snicm 	switch (arg->type) {
607b6e38b61Snicm 	case CMD_PARSE_STRING:
608b6e38b61Snicm 		free(arg->string);
609b6e38b61Snicm 		break;
610b6e38b61Snicm 	case CMD_PARSE_COMMANDS:
611b6e38b61Snicm 		cmd_parse_free_commands(arg->commands);
612b6e38b61Snicm 		break;
613d8b32369Snicm 	case CMD_PARSE_PARSED_COMMANDS:
614d8b32369Snicm 		cmd_list_free(arg->cmdlist);
615d8b32369Snicm 		break;
616b6e38b61Snicm 	}
617b6e38b61Snicm 	free(arg);
618b6e38b61Snicm }
619287cddb6Snicm 
620287cddb6Snicm static void
cmd_parse_free_arguments(struct cmd_parse_arguments * args)621287cddb6Snicm cmd_parse_free_arguments(struct cmd_parse_arguments *args)
622287cddb6Snicm {
623287cddb6Snicm 	struct cmd_parse_argument	*arg, *arg1;
624287cddb6Snicm 
625287cddb6Snicm 	TAILQ_FOREACH_SAFE(arg, args, entry, arg1) {
626287cddb6Snicm 		TAILQ_REMOVE(args, arg, entry);
627287cddb6Snicm 		cmd_parse_free_argument(arg);
628287cddb6Snicm 	}
629b6e38b61Snicm }
630b6e38b61Snicm 
631b6e38b61Snicm static void
cmd_parse_free_command(struct cmd_parse_command * cmd)632df6ab229Snicm cmd_parse_free_command(struct cmd_parse_command *cmd)
633df6ab229Snicm {
634b6e38b61Snicm 	cmd_parse_free_arguments(&cmd->arguments);
635df6ab229Snicm 	free(cmd);
636df6ab229Snicm }
637df6ab229Snicm 
6385d24dd80Snicm static struct cmd_parse_commands *
cmd_parse_new_commands(void)6395d24dd80Snicm cmd_parse_new_commands(void)
6405d24dd80Snicm {
6415d24dd80Snicm 	struct cmd_parse_commands	*cmds;
6425d24dd80Snicm 
6435d24dd80Snicm 	cmds = xmalloc(sizeof *cmds);
6445d24dd80Snicm 	TAILQ_INIT(cmds);
6455d24dd80Snicm 	return (cmds);
6465d24dd80Snicm }
6475d24dd80Snicm 
648df6ab229Snicm static void
cmd_parse_free_commands(struct cmd_parse_commands * cmds)649df6ab229Snicm cmd_parse_free_commands(struct cmd_parse_commands *cmds)
650df6ab229Snicm {
651df6ab229Snicm 	struct cmd_parse_command	*cmd, *cmd1;
652df6ab229Snicm 
653df6ab229Snicm 	TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) {
654df6ab229Snicm 		TAILQ_REMOVE(cmds, cmd, entry);
655df6ab229Snicm 		cmd_parse_free_command(cmd);
656df6ab229Snicm 	}
6575d24dd80Snicm 	free(cmds);
658df6ab229Snicm }
659df6ab229Snicm 
660df6ab229Snicm static struct cmd_parse_commands *
cmd_parse_run_parser(char ** cause)66173530c3cSnicm cmd_parse_run_parser(char **cause)
662df6ab229Snicm {
663df6ab229Snicm 	struct cmd_parse_state	*ps = &parse_state;
664df6ab229Snicm 	struct cmd_parse_scope	*scope, *scope1;
665df6ab229Snicm 	int			 retval;
666df6ab229Snicm 
6675d24dd80Snicm 	ps->commands = NULL;
668df6ab229Snicm 	TAILQ_INIT(&ps->stack);
669df6ab229Snicm 
670df6ab229Snicm 	retval = yyparse();
671df6ab229Snicm 	TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) {
672df6ab229Snicm 		TAILQ_REMOVE(&ps->stack, scope, entry);
673df6ab229Snicm 		free(scope);
674df6ab229Snicm 	}
675df6ab229Snicm 	if (retval != 0) {
676df6ab229Snicm 		*cause = ps->error;
677df6ab229Snicm 		return (NULL);
678df6ab229Snicm 	}
679df6ab229Snicm 
6805d24dd80Snicm 	if (ps->commands == NULL)
6815d24dd80Snicm 		return (cmd_parse_new_commands());
6825d24dd80Snicm 	return (ps->commands);
683df6ab229Snicm }
684df6ab229Snicm 
68573530c3cSnicm static struct cmd_parse_commands *
cmd_parse_do_file(FILE * f,struct cmd_parse_input * pi,char ** cause)68673530c3cSnicm cmd_parse_do_file(FILE *f, struct cmd_parse_input *pi, char **cause)
68773530c3cSnicm {
68873530c3cSnicm 	struct cmd_parse_state	*ps = &parse_state;
68973530c3cSnicm 
69073530c3cSnicm 	memset(ps, 0, sizeof *ps);
69173530c3cSnicm 	ps->input = pi;
69273530c3cSnicm 	ps->f = f;
69373530c3cSnicm 	return (cmd_parse_run_parser(cause));
69473530c3cSnicm }
69573530c3cSnicm 
69673530c3cSnicm static struct cmd_parse_commands *
cmd_parse_do_buffer(const char * buf,size_t len,struct cmd_parse_input * pi,char ** cause)69773530c3cSnicm cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi,
69873530c3cSnicm     char **cause)
69973530c3cSnicm {
70073530c3cSnicm 	struct cmd_parse_state	*ps = &parse_state;
70173530c3cSnicm 
70273530c3cSnicm 	memset(ps, 0, sizeof *ps);
70373530c3cSnicm 	ps->input = pi;
70473530c3cSnicm 	ps->buf = buf;
70573530c3cSnicm 	ps->len = len;
70673530c3cSnicm 	return (cmd_parse_run_parser(cause));
70773530c3cSnicm }
70873530c3cSnicm 
709b6e38b61Snicm static void
cmd_parse_log_commands(struct cmd_parse_commands * cmds,const char * prefix)710b6e38b61Snicm cmd_parse_log_commands(struct cmd_parse_commands *cmds, const char *prefix)
711b6e38b61Snicm {
712b6e38b61Snicm 	struct cmd_parse_command	*cmd;
713b6e38b61Snicm 	struct cmd_parse_argument	*arg;
714b6e38b61Snicm 	u_int				 i, j;
715b6e38b61Snicm 	char				*s;
716b6e38b61Snicm 
717b6e38b61Snicm 	i = 0;
718b6e38b61Snicm 	TAILQ_FOREACH(cmd, cmds, entry) {
719b6e38b61Snicm 		j = 0;
720b6e38b61Snicm 		TAILQ_FOREACH(arg, &cmd->arguments, entry) {
721b6e38b61Snicm 			switch (arg->type) {
722b6e38b61Snicm 			case CMD_PARSE_STRING:
723b6e38b61Snicm 				log_debug("%s %u:%u: %s", prefix, i, j,
724b6e38b61Snicm 				    arg->string);
725b6e38b61Snicm 				break;
726b6e38b61Snicm 			case CMD_PARSE_COMMANDS:
727b6e38b61Snicm 				xasprintf(&s, "%s %u:%u", prefix, i, j);
728b6e38b61Snicm 				cmd_parse_log_commands(arg->commands, s);
729b6e38b61Snicm 				free(s);
730b6e38b61Snicm 				break;
731d8b32369Snicm 			case CMD_PARSE_PARSED_COMMANDS:
732d8b32369Snicm 				s = cmd_list_print(arg->cmdlist, 0);
733d8b32369Snicm 				log_debug("%s %u:%u: %s", prefix, i, j, s);
734d8b32369Snicm 				free(s);
735d8b32369Snicm 				break;
736b6e38b61Snicm 			}
737b6e38b61Snicm 			j++;
738b6e38b61Snicm 		}
739b6e38b61Snicm 		i++;
740b6e38b61Snicm 	}
741b6e38b61Snicm }
742b6e38b61Snicm 
743287cddb6Snicm static int
cmd_parse_expand_alias(struct cmd_parse_command * cmd,struct cmd_parse_input * pi,struct cmd_parse_result * pr)744287cddb6Snicm cmd_parse_expand_alias(struct cmd_parse_command *cmd,
745cb3c07dcSnicm     struct cmd_parse_input *pi, struct cmd_parse_result *pr)
746b6e38b61Snicm {
747cb3c07dcSnicm 	struct cmd_parse_argument	*arg, *arg1, *first;
748287cddb6Snicm 	struct cmd_parse_commands	*cmds;
749287cddb6Snicm 	struct cmd_parse_command	*last;
750287cddb6Snicm 	char				*alias, *name, *cause;
751b6e38b61Snicm 
7529a68e586Snicm 	if (pi->flags & CMD_PARSE_NOALIAS)
7539a68e586Snicm 		return (0);
754cb3c07dcSnicm 	memset(pr, 0, sizeof *pr);
755b6e38b61Snicm 
756287cddb6Snicm 	first = TAILQ_FIRST(&cmd->arguments);
757287cddb6Snicm 	if (first == NULL || first->type != CMD_PARSE_STRING) {
758afdf680fSnicm 		pr->status = CMD_PARSE_SUCCESS;
759afdf680fSnicm 		pr->cmdlist = cmd_list_new();
760287cddb6Snicm 		return (1);
761287cddb6Snicm 	}
762287cddb6Snicm 	name = first->string;
763287cddb6Snicm 
764287cddb6Snicm 	alias = cmd_get_alias(name);
765287cddb6Snicm 	if (alias == NULL)
766287cddb6Snicm 		return (0);
767287cddb6Snicm 	log_debug("%s: %u alias %s = %s", __func__, pi->line, name, alias);
768287cddb6Snicm 
769287cddb6Snicm 	cmds = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause);
770287cddb6Snicm 	free(alias);
771287cddb6Snicm 	if (cmds == NULL) {
772287cddb6Snicm 		pr->status = CMD_PARSE_ERROR;
773287cddb6Snicm 		pr->error = cause;
774287cddb6Snicm 		return (1);
775b6e38b61Snicm 	}
776b6e38b61Snicm 
777287cddb6Snicm 	last = TAILQ_LAST(cmds, cmd_parse_commands);
778287cddb6Snicm 	if (last == NULL) {
779cb3c07dcSnicm 		pr->status = CMD_PARSE_SUCCESS;
780cb3c07dcSnicm 		pr->cmdlist = cmd_list_new();
781287cddb6Snicm 		return (1);
782b6e38b61Snicm 	}
783b6e38b61Snicm 
784287cddb6Snicm 	TAILQ_REMOVE(&cmd->arguments, first, entry);
785287cddb6Snicm 	cmd_parse_free_argument(first);
786287cddb6Snicm 
787287cddb6Snicm 	TAILQ_FOREACH_SAFE(arg, &cmd->arguments, entry, arg1) {
788287cddb6Snicm 		TAILQ_REMOVE(&cmd->arguments, arg, entry);
789287cddb6Snicm 		TAILQ_INSERT_TAIL(&last->arguments, arg, entry);
790287cddb6Snicm 	}
791287cddb6Snicm 	cmd_parse_log_commands(cmds, __func__);
792287cddb6Snicm 
7939a68e586Snicm 	pi->flags |= CMD_PARSE_NOALIAS;
794287cddb6Snicm 	cmd_parse_build_commands(cmds, pi, pr);
7959a68e586Snicm 	pi->flags &= ~CMD_PARSE_NOALIAS;
796287cddb6Snicm 	return (1);
797287cddb6Snicm }
798287cddb6Snicm 
799cb3c07dcSnicm static void
cmd_parse_build_command(struct cmd_parse_command * cmd,struct cmd_parse_input * pi,struct cmd_parse_result * pr)800287cddb6Snicm cmd_parse_build_command(struct cmd_parse_command *cmd,
801287cddb6Snicm     struct cmd_parse_input *pi, struct cmd_parse_result *pr)
802b6e38b61Snicm {
803b6e38b61Snicm 	struct cmd_parse_argument	*arg;
804287cddb6Snicm 	struct cmd			*add;
805fb147d85Snicm 	char				*cause;
806fb147d85Snicm 	struct args_value		*values = NULL;
807fb147d85Snicm 	u_int				 count = 0, idx;
808b6e38b61Snicm 
809cb3c07dcSnicm 	memset(pr, 0, sizeof *pr);
810cb3c07dcSnicm 
811cb3c07dcSnicm 	if (cmd_parse_expand_alias(cmd, pi, pr))
812cb3c07dcSnicm 		return;
813b6e38b61Snicm 
814b6e38b61Snicm 	TAILQ_FOREACH(arg, &cmd->arguments, entry) {
815bee784faSnicm 		values = xrecallocarray(values, count, count + 1,
816bee784faSnicm 		    sizeof *values);
817b6e38b61Snicm 		switch (arg->type) {
818b6e38b61Snicm 		case CMD_PARSE_STRING:
819fb147d85Snicm 			values[count].type = ARGS_STRING;
820fb147d85Snicm 			values[count].string = xstrdup(arg->string);
821b6e38b61Snicm 			break;
822b6e38b61Snicm 		case CMD_PARSE_COMMANDS:
823287cddb6Snicm 			cmd_parse_build_commands(arg->commands, pi, pr);
824287cddb6Snicm 			if (pr->status != CMD_PARSE_SUCCESS)
825fb147d85Snicm 				goto out;
826fb147d85Snicm 			values[count].type = ARGS_COMMANDS;
827fb147d85Snicm 			values[count].cmdlist = pr->cmdlist;
828b6e38b61Snicm 			break;
829d8b32369Snicm 		case CMD_PARSE_PARSED_COMMANDS:
830d8b32369Snicm 			values[count].type = ARGS_COMMANDS;
831d8b32369Snicm 			values[count].cmdlist = arg->cmdlist;
832d8b32369Snicm 			values[count].cmdlist->references++;
833d8b32369Snicm 			break;
834b6e38b61Snicm 		}
835fb147d85Snicm 		count++;
836b6e38b61Snicm 	}
837b6e38b61Snicm 
838fb147d85Snicm 	add = cmd_parse(values, count, pi->file, pi->line, &cause);
839379d46e0Snicm 	if (add == NULL) {
840379d46e0Snicm 		pr->status = CMD_PARSE_ERROR;
841287cddb6Snicm 		pr->error = cmd_parse_get_error(pi->file, pi->line, cause);
842379d46e0Snicm 		free(cause);
843fb147d85Snicm 		goto out;
844379d46e0Snicm 	}
845cb3c07dcSnicm 	pr->status = CMD_PARSE_SUCCESS;
846cb3c07dcSnicm 	pr->cmdlist = cmd_list_new();
847cb3c07dcSnicm 	cmd_list_append(pr->cmdlist, add);
848fb147d85Snicm 
849fb147d85Snicm out:
850fb147d85Snicm 	for (idx = 0; idx < count; idx++)
851fb147d85Snicm 		args_free_value(&values[idx]);
852fb147d85Snicm 	free(values);
853379d46e0Snicm }
854379d46e0Snicm 
855287cddb6Snicm static void
cmd_parse_build_commands(struct cmd_parse_commands * cmds,struct cmd_parse_input * pi,struct cmd_parse_result * pr)856a6afde38Snicm cmd_parse_build_commands(struct cmd_parse_commands *cmds,
857287cddb6Snicm     struct cmd_parse_input *pi, struct cmd_parse_result *pr)
858df6ab229Snicm {
859287cddb6Snicm 	struct cmd_parse_command	*cmd;
860df6ab229Snicm 	u_int				 line = UINT_MAX;
861cb3c07dcSnicm 	struct cmd_list			*current = NULL, *result;
862287cddb6Snicm 	char				*s;
863df6ab229Snicm 
864cb3c07dcSnicm 	memset(pr, 0, sizeof *pr);
865cb3c07dcSnicm 
866a6afde38Snicm 	/* Check for an empty list. */
867df6ab229Snicm 	if (TAILQ_EMPTY(cmds)) {
868afdf680fSnicm 		pr->status = CMD_PARSE_SUCCESS;
869afdf680fSnicm 		pr->cmdlist = cmd_list_new();
870287cddb6Snicm 		return;
871df6ab229Snicm 	}
872b6e38b61Snicm 	cmd_parse_log_commands(cmds, __func__);
873df6ab229Snicm 
874df6ab229Snicm 	/*
875df6ab229Snicm 	 * Parse each command into a command list. Create a new command list
876a49ab104Snicm 	 * for each line (unless the flag is set) so they get a new group (so
877a49ab104Snicm 	 * the queue knows which ones to remove if a command fails when
878a49ab104Snicm 	 * executed).
879df6ab229Snicm 	 */
880df6ab229Snicm 	result = cmd_list_new();
881df6ab229Snicm 	TAILQ_FOREACH(cmd, cmds, entry) {
882379d46e0Snicm 		if (((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) {
883379d46e0Snicm 			if (current != NULL) {
884287cddb6Snicm 				cmd_parse_print_commands(pi, current);
885379d46e0Snicm 				cmd_list_move(result, current);
886379d46e0Snicm 				cmd_list_free(current);
887df6ab229Snicm 			}
888379d46e0Snicm 			current = cmd_list_new();
889df6ab229Snicm 		}
890379d46e0Snicm 		if (current == NULL)
891379d46e0Snicm 			current = cmd_list_new();
892287cddb6Snicm 		line = pi->line = cmd->line;
893df6ab229Snicm 
894cb3c07dcSnicm 		cmd_parse_build_command(cmd, pi, pr);
895cb3c07dcSnicm 		if (pr->status != CMD_PARSE_SUCCESS) {
896df6ab229Snicm 			cmd_list_free(result);
897379d46e0Snicm 			cmd_list_free(current);
898287cddb6Snicm 			return;
899df6ab229Snicm 		}
900cb3c07dcSnicm 		cmd_list_append_all(current, pr->cmdlist);
901cb3c07dcSnicm 		cmd_list_free(pr->cmdlist);
902df6ab229Snicm 	}
903379d46e0Snicm 	if (current != NULL) {
904287cddb6Snicm 		cmd_parse_print_commands(pi, current);
905379d46e0Snicm 		cmd_list_move(result, current);
906379d46e0Snicm 		cmd_list_free(current);
907df6ab229Snicm 	}
908df6ab229Snicm 
9095c131106Snicm 	s = cmd_list_print(result, 0);
910df6ab229Snicm 	log_debug("%s: %s", __func__, s);
911df6ab229Snicm 	free(s);
912df6ab229Snicm 
913287cddb6Snicm 	pr->status = CMD_PARSE_SUCCESS;
914287cddb6Snicm 	pr->cmdlist = result;
915df6ab229Snicm }
916df6ab229Snicm 
917df6ab229Snicm struct cmd_parse_result *
cmd_parse_from_file(FILE * f,struct cmd_parse_input * pi)918a6afde38Snicm cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
919a6afde38Snicm {
920a6afde38Snicm 	static struct cmd_parse_result	 pr;
921a6afde38Snicm 	struct cmd_parse_input		 input;
922a6afde38Snicm 	struct cmd_parse_commands	*cmds;
923a6afde38Snicm 	char				*cause;
924a6afde38Snicm 
925a6afde38Snicm 	if (pi == NULL) {
926a6afde38Snicm 		memset(&input, 0, sizeof input);
927a6afde38Snicm 		pi = &input;
928a6afde38Snicm 	}
929a6afde38Snicm 	memset(&pr, 0, sizeof pr);
930a6afde38Snicm 
93173530c3cSnicm 	cmds = cmd_parse_do_file(f, pi, &cause);
932a6afde38Snicm 	if (cmds == NULL) {
933a6afde38Snicm 		pr.status = CMD_PARSE_ERROR;
934a6afde38Snicm 		pr.error = cause;
935a6afde38Snicm 		return (&pr);
936a6afde38Snicm 	}
937287cddb6Snicm 	cmd_parse_build_commands(cmds, pi, &pr);
938287cddb6Snicm 	cmd_parse_free_commands(cmds);
939287cddb6Snicm 	return (&pr);
940287cddb6Snicm 
941a6afde38Snicm }
942a6afde38Snicm 
943a6afde38Snicm struct cmd_parse_result *
cmd_parse_from_string(const char * s,struct cmd_parse_input * pi)944df6ab229Snicm cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
945df6ab229Snicm {
946a49ab104Snicm 	struct cmd_parse_input	input;
947a49ab104Snicm 
948a49ab104Snicm 	if (pi == NULL) {
949a49ab104Snicm 		memset(&input, 0, sizeof input);
950a49ab104Snicm 		pi = &input;
951a49ab104Snicm 	}
952a49ab104Snicm 
953a49ab104Snicm 	/*
954a49ab104Snicm 	 * When parsing a string, put commands in one group even if there are
955a49ab104Snicm 	 * multiple lines. This means { a \n b } is identical to "a ; b" when
956a49ab104Snicm 	 * given as an argument to another command.
957a49ab104Snicm 	 */
958a49ab104Snicm 	pi->flags |= CMD_PARSE_ONEGROUP;
959f233c35dSnicm 	return (cmd_parse_from_buffer(s, strlen(s), pi));
960f233c35dSnicm }
961f233c35dSnicm 
9621c43462cSnicm enum cmd_parse_status
cmd_parse_and_insert(const char * s,struct cmd_parse_input * pi,struct cmdq_item * after,struct cmdq_state * state,char ** error)9631c43462cSnicm cmd_parse_and_insert(const char *s, struct cmd_parse_input *pi,
9641c43462cSnicm     struct cmdq_item *after, struct cmdq_state *state, char **error)
9651c43462cSnicm {
9661c43462cSnicm 	struct cmd_parse_result	*pr;
9671c43462cSnicm 	struct cmdq_item	*item;
9681c43462cSnicm 
9691c43462cSnicm 	pr = cmd_parse_from_string(s, pi);
9701c43462cSnicm 	switch (pr->status) {
9711c43462cSnicm 	case CMD_PARSE_ERROR:
9721c43462cSnicm 		if (error != NULL)
9731c43462cSnicm 			*error = pr->error;
9741c43462cSnicm 		else
9751c43462cSnicm 			free(pr->error);
9761c43462cSnicm 		break;
9771c43462cSnicm 	case CMD_PARSE_SUCCESS:
9781c43462cSnicm 		item = cmdq_get_command(pr->cmdlist, state);
9791c43462cSnicm 		cmdq_insert_after(after, item);
9801c43462cSnicm 		cmd_list_free(pr->cmdlist);
9811c43462cSnicm 		break;
9821c43462cSnicm 	}
9831c43462cSnicm 	return (pr->status);
9841c43462cSnicm }
9851c43462cSnicm 
9861c43462cSnicm enum cmd_parse_status
cmd_parse_and_append(const char * s,struct cmd_parse_input * pi,struct client * c,struct cmdq_state * state,char ** error)9871c43462cSnicm cmd_parse_and_append(const char *s, struct cmd_parse_input *pi,
9881c43462cSnicm     struct client *c, struct cmdq_state *state, char **error)
9891c43462cSnicm {
9901c43462cSnicm 	struct cmd_parse_result	*pr;
9911c43462cSnicm 	struct cmdq_item	*item;
9921c43462cSnicm 
9931c43462cSnicm 	pr = cmd_parse_from_string(s, pi);
9941c43462cSnicm 	switch (pr->status) {
9951c43462cSnicm 	case CMD_PARSE_ERROR:
9961c43462cSnicm 		if (error != NULL)
9971c43462cSnicm 			*error = pr->error;
9981c43462cSnicm 		else
9991c43462cSnicm 			free(pr->error);
10001c43462cSnicm 		break;
10011c43462cSnicm 	case CMD_PARSE_SUCCESS:
10021c43462cSnicm 		item = cmdq_get_command(pr->cmdlist, state);
10031c43462cSnicm 		cmdq_append(c, item);
10041c43462cSnicm 		cmd_list_free(pr->cmdlist);
10051c43462cSnicm 		break;
10061c43462cSnicm 	}
10071c43462cSnicm 	return (pr->status);
10081c43462cSnicm }
10091c43462cSnicm 
1010f233c35dSnicm struct cmd_parse_result *
cmd_parse_from_buffer(const void * buf,size_t len,struct cmd_parse_input * pi)1011f233c35dSnicm cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi)
1012f233c35dSnicm {
1013df6ab229Snicm 	static struct cmd_parse_result	 pr;
1014a6afde38Snicm 	struct cmd_parse_input		 input;
101573530c3cSnicm 	struct cmd_parse_commands	*cmds;
101673530c3cSnicm 	char				*cause;
1017df6ab229Snicm 
1018a6afde38Snicm 	if (pi == NULL) {
1019a6afde38Snicm 		memset(&input, 0, sizeof input);
1020a6afde38Snicm 		pi = &input;
1021a6afde38Snicm 	}
1022a6afde38Snicm 	memset(&pr, 0, sizeof pr);
1023a6afde38Snicm 
1024f233c35dSnicm 	if (len == 0) {
1025afdf680fSnicm 		pr.status = CMD_PARSE_SUCCESS;
1026afdf680fSnicm 		pr.cmdlist = cmd_list_new();
1027df6ab229Snicm 		return (&pr);
1028df6ab229Snicm 	}
1029df6ab229Snicm 
1030f233c35dSnicm 	cmds = cmd_parse_do_buffer(buf, len, pi, &cause);
103173530c3cSnicm 	if (cmds == NULL) {
1032df6ab229Snicm 		pr.status = CMD_PARSE_ERROR;
103373530c3cSnicm 		pr.error = cause;
103473530c3cSnicm 		return (&pr);
1035df6ab229Snicm 	}
1036287cddb6Snicm 	cmd_parse_build_commands(cmds, pi, &pr);
1037287cddb6Snicm 	cmd_parse_free_commands(cmds);
1038287cddb6Snicm 	return (&pr);
1039df6ab229Snicm }
1040df6ab229Snicm 
1041a6afde38Snicm struct cmd_parse_result *
cmd_parse_from_arguments(struct args_value * values,u_int count,struct cmd_parse_input * pi)1042d8b32369Snicm cmd_parse_from_arguments(struct args_value *values, u_int count,
1043d8b32369Snicm     struct cmd_parse_input *pi)
1044a6afde38Snicm {
1045287cddb6Snicm 	static struct cmd_parse_result	 pr;
1046a6afde38Snicm 	struct cmd_parse_input		 input;
1047a6afde38Snicm 	struct cmd_parse_commands	*cmds;
1048d8b32369Snicm 	struct cmd_parse_command	*cmd;
1049d8b32369Snicm 	struct cmd_parse_argument	*arg;
1050d8b32369Snicm 	u_int				 i;
1051d8b32369Snicm 	char				*copy;
1052a6afde38Snicm 	size_t				 size;
1053d8b32369Snicm 	int				 end;
1054a6afde38Snicm 
1055a6afde38Snicm 	/*
1056a6afde38Snicm 	 * The commands are already split up into arguments, so just separate
1057a6afde38Snicm 	 * into a set of commands by ';'.
1058a6afde38Snicm 	 */
1059a6afde38Snicm 
1060a6afde38Snicm 	if (pi == NULL) {
1061a6afde38Snicm 		memset(&input, 0, sizeof input);
1062a6afde38Snicm 		pi = &input;
1063a6afde38Snicm 	}
1064cb3c07dcSnicm 	memset(&pr, 0, sizeof pr);
1065a6afde38Snicm 
10665d24dd80Snicm 	cmds = cmd_parse_new_commands();
1067a6afde38Snicm 
1068d8b32369Snicm 	cmd = xcalloc(1, sizeof *cmd);
1069d8b32369Snicm 	cmd->line = pi->line;
1070d8b32369Snicm 	TAILQ_INIT(&cmd->arguments);
1071d8b32369Snicm 
1072d8b32369Snicm 	for (i = 0; i < count; i++) {
1073d8b32369Snicm 		end = 0;
1074d8b32369Snicm 		if (values[i].type == ARGS_STRING) {
1075d8b32369Snicm 			copy = xstrdup(values[i].string);
1076d8b32369Snicm 			size = strlen(copy);
1077d8b32369Snicm 			if (size != 0 && copy[size - 1] == ';') {
1078d8b32369Snicm 				copy[--size] = '\0';
1079d8b32369Snicm 				if (size > 0 && copy[size - 1] == '\\')
1080d8b32369Snicm 					copy[size - 1] = ';';
1081d8b32369Snicm 				else
1082d8b32369Snicm 					end = 1;
1083a6afde38Snicm 			}
1084d8b32369Snicm 			if (!end || size != 0) {
1085d8b32369Snicm 				arg = xcalloc(1, sizeof *arg);
1086d8b32369Snicm 				arg->type = CMD_PARSE_STRING;
1087d8b32369Snicm 				arg->string = copy;
1088d8b32369Snicm 				TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
10898d320eeeSnicm 			} else
10908d320eeeSnicm 				free(copy);
1091d8b32369Snicm 		} else if (values[i].type == ARGS_COMMANDS) {
1092d8b32369Snicm 			arg = xcalloc(1, sizeof *arg);
1093d8b32369Snicm 			arg->type = CMD_PARSE_PARSED_COMMANDS;
1094d8b32369Snicm 			arg->cmdlist = values[i].cmdlist;
1095d8b32369Snicm 			arg->cmdlist->references++;
1096d8b32369Snicm 			TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
1097d8b32369Snicm 		} else
1098d8b32369Snicm 			fatalx("unknown argument type");
1099d8b32369Snicm 		if (end) {
1100d8b32369Snicm 			TAILQ_INSERT_TAIL(cmds, cmd, entry);
1101d8b32369Snicm 			cmd = xcalloc(1, sizeof *cmd);
1102d8b32369Snicm 			cmd->line = pi->line;
1103d8b32369Snicm 			TAILQ_INIT(&cmd->arguments);
1104a6afde38Snicm 		}
1105d8b32369Snicm 	}
1106d8b32369Snicm 	if (!TAILQ_EMPTY(&cmd->arguments))
1107d8b32369Snicm 		TAILQ_INSERT_TAIL(cmds, cmd, entry);
1108d8b32369Snicm 	else
1109d8b32369Snicm 		free(cmd);
11105d24dd80Snicm 
1111287cddb6Snicm 	cmd_parse_build_commands(cmds, pi, &pr);
1112287cddb6Snicm 	cmd_parse_free_commands(cmds);
1113287cddb6Snicm 	return (&pr);
1114a6afde38Snicm }
1115a6afde38Snicm 
1116df6ab229Snicm static int printflike(1, 2)
yyerror(const char * fmt,...)1117df6ab229Snicm yyerror(const char *fmt, ...)
1118df6ab229Snicm {
1119df6ab229Snicm 	struct cmd_parse_state	*ps = &parse_state;
1120df6ab229Snicm 	struct cmd_parse_input	*pi = ps->input;
1121df6ab229Snicm 	va_list			 ap;
1122df6ab229Snicm 	char			*error;
1123df6ab229Snicm 
1124df6ab229Snicm 	if (ps->error != NULL)
1125df6ab229Snicm 		return (0);
1126df6ab229Snicm 
1127df6ab229Snicm 	va_start(ap, fmt);
1128df6ab229Snicm 	xvasprintf(&error, fmt, ap);
1129df6ab229Snicm 	va_end(ap);
1130df6ab229Snicm 
1131df6ab229Snicm 	ps->error = cmd_parse_get_error(pi->file, pi->line, error);
1132df6ab229Snicm 	free(error);
1133df6ab229Snicm 	return (0);
1134df6ab229Snicm }
1135df6ab229Snicm 
1136df6ab229Snicm static int
yylex_is_var(char ch,int first)1137df6ab229Snicm yylex_is_var(char ch, int first)
1138df6ab229Snicm {
1139df6ab229Snicm 	if (ch == '=')
1140df6ab229Snicm 		return (0);
1141df6ab229Snicm 	if (first && isdigit((u_char)ch))
1142df6ab229Snicm 		return (0);
1143df6ab229Snicm 	return (isalnum((u_char)ch) || ch == '_');
1144df6ab229Snicm }
1145df6ab229Snicm 
1146df6ab229Snicm static void
yylex_append(char ** buf,size_t * len,const char * add,size_t addlen)1147df6ab229Snicm yylex_append(char **buf, size_t *len, const char *add, size_t addlen)
1148df6ab229Snicm {
1149df6ab229Snicm 	if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen)
1150df6ab229Snicm 		fatalx("buffer is too big");
1151df6ab229Snicm 	*buf = xrealloc(*buf, (*len) + 1 + addlen);
1152df6ab229Snicm 	memcpy((*buf) + *len, add, addlen);
1153df6ab229Snicm 	(*len) += addlen;
1154df6ab229Snicm }
1155df6ab229Snicm 
1156df6ab229Snicm static void
yylex_append1(char ** buf,size_t * len,char add)1157df6ab229Snicm yylex_append1(char **buf, size_t *len, char add)
1158df6ab229Snicm {
1159df6ab229Snicm 	yylex_append(buf, len, &add, 1);
1160df6ab229Snicm }
1161df6ab229Snicm 
1162df6ab229Snicm static int
yylex_getc1(void)116373530c3cSnicm yylex_getc1(void)
116473530c3cSnicm {
116573530c3cSnicm 	struct cmd_parse_state	*ps = &parse_state;
116673530c3cSnicm 	int			 ch;
116773530c3cSnicm 
116873530c3cSnicm 	if (ps->f != NULL)
116973530c3cSnicm 		ch = getc(ps->f);
117073530c3cSnicm 	else {
117173530c3cSnicm 		if (ps->off == ps->len)
117273530c3cSnicm 			ch = EOF;
117373530c3cSnicm 		else
117473530c3cSnicm 			ch = ps->buf[ps->off++];
117573530c3cSnicm 	}
117673530c3cSnicm 	return (ch);
117773530c3cSnicm }
117873530c3cSnicm 
117973530c3cSnicm static void
yylex_ungetc(int ch)118073530c3cSnicm yylex_ungetc(int ch)
118173530c3cSnicm {
118273530c3cSnicm 	struct cmd_parse_state	*ps = &parse_state;
118373530c3cSnicm 
118473530c3cSnicm 	if (ps->f != NULL)
118573530c3cSnicm 		ungetc(ch, ps->f);
118673530c3cSnicm 	else if (ps->off > 0 && ch != EOF)
118773530c3cSnicm 		ps->off--;
118873530c3cSnicm }
118973530c3cSnicm 
119073530c3cSnicm static int
yylex_getc(void)1191df6ab229Snicm yylex_getc(void)
1192df6ab229Snicm {
1193df6ab229Snicm 	struct cmd_parse_state	*ps = &parse_state;
1194df6ab229Snicm 	int			 ch;
1195df6ab229Snicm 
1196df6ab229Snicm 	if (ps->escapes != 0) {
1197df6ab229Snicm 		ps->escapes--;
1198df6ab229Snicm 		return ('\\');
1199df6ab229Snicm 	}
1200df6ab229Snicm 	for (;;) {
120173530c3cSnicm 		ch = yylex_getc1();
1202df6ab229Snicm 		if (ch == '\\') {
1203df6ab229Snicm 			ps->escapes++;
1204df6ab229Snicm 			continue;
1205df6ab229Snicm 		}
1206df6ab229Snicm 		if (ch == '\n' && (ps->escapes % 2) == 1) {
1207df6ab229Snicm 			ps->input->line++;
1208df6ab229Snicm 			ps->escapes--;
1209df6ab229Snicm 			continue;
1210df6ab229Snicm 		}
1211df6ab229Snicm 
1212df6ab229Snicm 		if (ps->escapes != 0) {
121373530c3cSnicm 			yylex_ungetc(ch);
1214df6ab229Snicm 			ps->escapes--;
1215df6ab229Snicm 			return ('\\');
1216df6ab229Snicm 		}
1217df6ab229Snicm 		return (ch);
1218df6ab229Snicm 	}
1219df6ab229Snicm }
1220df6ab229Snicm 
1221df6ab229Snicm static char *
yylex_get_word(int ch)1222df6ab229Snicm yylex_get_word(int ch)
1223df6ab229Snicm {
1224df6ab229Snicm 	char	*buf;
1225df6ab229Snicm 	size_t	 len;
1226df6ab229Snicm 
1227df6ab229Snicm 	len = 0;
1228df6ab229Snicm 	buf = xmalloc(1);
1229df6ab229Snicm 
1230df6ab229Snicm 	do
1231df6ab229Snicm 		yylex_append1(&buf, &len, ch);
1232df6ab229Snicm 	while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL);
123373530c3cSnicm 	yylex_ungetc(ch);
1234df6ab229Snicm 
1235df6ab229Snicm 	buf[len] = '\0';
1236df6ab229Snicm 	log_debug("%s: %s", __func__, buf);
1237df6ab229Snicm 	return (buf);
1238df6ab229Snicm }
1239df6ab229Snicm 
1240df6ab229Snicm static int
yylex(void)1241df6ab229Snicm yylex(void)
1242df6ab229Snicm {
1243df6ab229Snicm 	struct cmd_parse_state	*ps = &parse_state;
1244df6ab229Snicm 	char			*token, *cp;
124509ba5f57Snicm 	int			 ch, next, condition;
1246df6ab229Snicm 
124747394861Snicm 	if (ps->eol)
124847394861Snicm 		ps->input->line++;
124947394861Snicm 	ps->eol = 0;
125047394861Snicm 
125109ba5f57Snicm 	condition = ps->condition;
125209ba5f57Snicm 	ps->condition = 0;
125309ba5f57Snicm 
1254df6ab229Snicm 	for (;;) {
1255df6ab229Snicm 		ch = yylex_getc();
1256df6ab229Snicm 
1257df6ab229Snicm 		if (ch == EOF) {
1258df6ab229Snicm 			/*
1259df6ab229Snicm 			 * Ensure every file or string is terminated by a
1260df6ab229Snicm 			 * newline. This keeps the parser simpler and avoids
1261df6ab229Snicm 			 * having to add a newline to each string.
1262df6ab229Snicm 			 */
1263df6ab229Snicm 			if (ps->eof)
1264df6ab229Snicm 				break;
1265df6ab229Snicm 			ps->eof = 1;
1266df6ab229Snicm 			return ('\n');
1267df6ab229Snicm 		}
1268df6ab229Snicm 
1269df6ab229Snicm 		if (ch == ' ' || ch == '\t') {
1270df6ab229Snicm 			/*
1271df6ab229Snicm 			 * Ignore whitespace.
1272df6ab229Snicm 			 */
1273df6ab229Snicm 			continue;
1274df6ab229Snicm 		}
1275df6ab229Snicm 
1276df6ab229Snicm 		if (ch == '\n') {
1277df6ab229Snicm 			/*
1278df6ab229Snicm 			 * End of line. Update the line number.
1279df6ab229Snicm 			 */
128047394861Snicm 			ps->eol = 1;
1281df6ab229Snicm 			return ('\n');
1282df6ab229Snicm 		}
1283df6ab229Snicm 
12842a840c62Snicm 		if (ch == ';' || ch == '{' || ch == '}') {
1285df6ab229Snicm 			/*
12862a840c62Snicm 			 * A semicolon or { or } is itself.
1287df6ab229Snicm 			 */
12882a840c62Snicm 			return (ch);
1289df6ab229Snicm 		}
1290df6ab229Snicm 
1291df6ab229Snicm 		if (ch == '#') {
1292df6ab229Snicm 			/*
129309ba5f57Snicm 			 * #{ after a condition opens a format; anything else
129409ba5f57Snicm 			 * is a comment, ignore up to the end of the line.
1295df6ab229Snicm 			 */
1296df6ab229Snicm 			next = yylex_getc();
129709ba5f57Snicm 			if (condition && next == '{') {
1298df6ab229Snicm 				yylval.token = yylex_format();
1299df6ab229Snicm 				if (yylval.token == NULL)
1300df6ab229Snicm 					return (ERROR);
1301df6ab229Snicm 				return (FORMAT);
1302df6ab229Snicm 			}
1303df6ab229Snicm 			while (next != '\n' && next != EOF)
1304df6ab229Snicm 				next = yylex_getc();
1305df6ab229Snicm 			if (next == '\n') {
1306df6ab229Snicm 				ps->input->line++;
1307df6ab229Snicm 				return ('\n');
1308df6ab229Snicm 			}
1309df6ab229Snicm 			continue;
1310df6ab229Snicm 		}
1311df6ab229Snicm 
1312df6ab229Snicm 		if (ch == '%') {
1313df6ab229Snicm 			/*
1314f48431f7Snicm 			 * % is a condition unless it is all % or all numbers,
1315f48431f7Snicm 			 * then it is a token.
1316df6ab229Snicm 			 */
1317df6ab229Snicm 			yylval.token = yylex_get_word('%');
1318f48431f7Snicm 			for (cp = yylval.token; *cp != '\0'; cp++) {
1319f48431f7Snicm 				if (*cp != '%' && !isdigit((u_char)*cp))
1320f48431f7Snicm 					break;
1321f48431f7Snicm 			}
1322f48431f7Snicm 			if (*cp == '\0')
1323df6ab229Snicm 				return (TOKEN);
132409ba5f57Snicm 			ps->condition = 1;
1325d6f6a5d2Snicm 			if (strcmp(yylval.token, "%hidden") == 0) {
1326d6f6a5d2Snicm 				free(yylval.token);
1327d6f6a5d2Snicm 				return (HIDDEN);
1328d6f6a5d2Snicm 			}
1329df6ab229Snicm 			if (strcmp(yylval.token, "%if") == 0) {
1330df6ab229Snicm 				free(yylval.token);
1331df6ab229Snicm 				return (IF);
1332df6ab229Snicm 			}
1333df6ab229Snicm 			if (strcmp(yylval.token, "%else") == 0) {
1334df6ab229Snicm 				free(yylval.token);
1335df6ab229Snicm 				return (ELSE);
1336df6ab229Snicm 			}
1337df6ab229Snicm 			if (strcmp(yylval.token, "%elif") == 0) {
1338df6ab229Snicm 				free(yylval.token);
1339df6ab229Snicm 				return (ELIF);
1340df6ab229Snicm 			}
1341df6ab229Snicm 			if (strcmp(yylval.token, "%endif") == 0) {
1342df6ab229Snicm 				free(yylval.token);
1343df6ab229Snicm 				return (ENDIF);
1344df6ab229Snicm 			}
1345df6ab229Snicm 			free(yylval.token);
1346df6ab229Snicm 			return (ERROR);
1347df6ab229Snicm 		}
1348df6ab229Snicm 
1349df6ab229Snicm 		/*
1350df6ab229Snicm 		 * Otherwise this is a token.
1351df6ab229Snicm 		 */
1352df6ab229Snicm 		token = yylex_token(ch);
1353df6ab229Snicm 		if (token == NULL)
1354df6ab229Snicm 			return (ERROR);
1355df6ab229Snicm 		yylval.token = token;
1356df6ab229Snicm 
1357df6ab229Snicm 		if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
1358df6ab229Snicm 			for (cp = token + 1; *cp != '='; cp++) {
1359df6ab229Snicm 				if (!yylex_is_var(*cp, 0))
1360df6ab229Snicm 					break;
1361df6ab229Snicm 			}
1362df6ab229Snicm 			if (*cp == '=')
1363df6ab229Snicm 				return (EQUALS);
1364df6ab229Snicm 		}
1365df6ab229Snicm 		return (TOKEN);
1366df6ab229Snicm 	}
1367df6ab229Snicm 	return (0);
1368df6ab229Snicm }
1369df6ab229Snicm 
1370df6ab229Snicm static char *
yylex_format(void)1371df6ab229Snicm yylex_format(void)
1372df6ab229Snicm {
1373df6ab229Snicm 	char	*buf;
1374df6ab229Snicm 	size_t	 len;
1375df6ab229Snicm 	int	 ch, brackets = 1;
1376df6ab229Snicm 
1377df6ab229Snicm 	len = 0;
1378df6ab229Snicm 	buf = xmalloc(1);
1379df6ab229Snicm 
1380df6ab229Snicm 	yylex_append(&buf, &len, "#{", 2);
1381df6ab229Snicm 	for (;;) {
1382df6ab229Snicm 		if ((ch = yylex_getc()) == EOF || ch == '\n')
1383df6ab229Snicm 			goto error;
1384df6ab229Snicm 		if (ch == '#') {
1385df6ab229Snicm 			if ((ch = yylex_getc()) == EOF || ch == '\n')
1386df6ab229Snicm 				goto error;
1387df6ab229Snicm 			if (ch == '{')
1388df6ab229Snicm 				brackets++;
1389df6ab229Snicm 			yylex_append1(&buf, &len, '#');
1390df6ab229Snicm 		} else if (ch == '}') {
1391df6ab229Snicm 			if (brackets != 0 && --brackets == 0) {
1392df6ab229Snicm 				yylex_append1(&buf, &len, ch);
1393df6ab229Snicm 				break;
1394df6ab229Snicm 			}
1395df6ab229Snicm 		}
1396df6ab229Snicm 		yylex_append1(&buf, &len, ch);
1397df6ab229Snicm 	}
1398df6ab229Snicm 	if (brackets != 0)
1399df6ab229Snicm 		goto error;
1400df6ab229Snicm 
1401df6ab229Snicm 	buf[len] = '\0';
1402df6ab229Snicm 	log_debug("%s: %s", __func__, buf);
1403df6ab229Snicm 	return (buf);
1404df6ab229Snicm 
1405df6ab229Snicm error:
1406df6ab229Snicm 	free(buf);
1407df6ab229Snicm 	return (NULL);
1408df6ab229Snicm }
1409df6ab229Snicm 
1410df6ab229Snicm static int
yylex_token_escape(char ** buf,size_t * len)1411df6ab229Snicm yylex_token_escape(char **buf, size_t *len)
1412df6ab229Snicm {
14136852c63bSnicm 	int	 ch, type, o2, o3, mlen;
1414df6ab229Snicm 	u_int	 size, i, tmp;
14156852c63bSnicm 	char	 s[9], m[MB_LEN_MAX];
1416df6ab229Snicm 
1417dfc4c510Snicm 	ch = yylex_getc();
1418dfc4c510Snicm 
1419dfc4c510Snicm 	if (ch >= '4' && ch <= '7') {
1420dfc4c510Snicm 		yyerror("invalid octal escape");
1421dfc4c510Snicm 		return (0);
1422dfc4c510Snicm 	}
1423dfc4c510Snicm 	if (ch >= '0' && ch <= '3') {
1424dfc4c510Snicm 		o2 = yylex_getc();
1425dfc4c510Snicm 		if (o2 >= '0' && o2 <= '7') {
1426dfc4c510Snicm 			o3 = yylex_getc();
1427dfc4c510Snicm 			if (o3 >= '0' && o3 <= '7') {
1428dfc4c510Snicm 				ch = 64 * (ch - '0') +
1429dfc4c510Snicm 				      8 * (o2 - '0') +
1430dfc4c510Snicm 					  (o3 - '0');
1431dfc4c510Snicm 				yylex_append1(buf, len, ch);
1432dfc4c510Snicm 				return (1);
1433dfc4c510Snicm 			}
1434dfc4c510Snicm 		}
1435dfc4c510Snicm 		yyerror("invalid octal escape");
1436dfc4c510Snicm 		return (0);
1437dfc4c510Snicm 	}
1438dfc4c510Snicm 
1439dfc4c510Snicm 	switch (ch) {
1440df6ab229Snicm 	case EOF:
1441df6ab229Snicm 		return (0);
144206feda64Snicm 	case 'a':
144306feda64Snicm 		ch = '\a';
144406feda64Snicm 		break;
144506feda64Snicm 	case 'b':
144606feda64Snicm 		ch = '\b';
144706feda64Snicm 		break;
1448df6ab229Snicm 	case 'e':
1449df6ab229Snicm 		ch = '\033';
1450df6ab229Snicm 		break;
145106feda64Snicm 	case 'f':
145206feda64Snicm 		ch = '\f';
145306feda64Snicm 		break;
145406feda64Snicm 	case 's':
145506feda64Snicm 		ch = ' ';
145606feda64Snicm 		break;
145706feda64Snicm 	case 'v':
145806feda64Snicm 		ch = '\v';
145906feda64Snicm 		break;
1460df6ab229Snicm 	case 'r':
1461df6ab229Snicm 		ch = '\r';
1462df6ab229Snicm 		break;
1463df6ab229Snicm 	case 'n':
1464df6ab229Snicm 		ch = '\n';
1465df6ab229Snicm 		break;
1466df6ab229Snicm 	case 't':
1467df6ab229Snicm 		ch = '\t';
1468df6ab229Snicm 		break;
1469df6ab229Snicm 	case 'u':
1470df6ab229Snicm 		type = 'u';
1471df6ab229Snicm 		size = 4;
1472df6ab229Snicm 		goto unicode;
1473df6ab229Snicm 	case 'U':
1474df6ab229Snicm 		type = 'U';
1475df6ab229Snicm 		size = 8;
1476df6ab229Snicm 		goto unicode;
1477df6ab229Snicm 	}
1478df6ab229Snicm 
1479df6ab229Snicm 	yylex_append1(buf, len, ch);
1480df6ab229Snicm 	return (1);
1481df6ab229Snicm 
1482df6ab229Snicm unicode:
1483df6ab229Snicm 	for (i = 0; i < size; i++) {
1484df6ab229Snicm 		ch = yylex_getc();
1485df6ab229Snicm 		if (ch == EOF || ch == '\n')
1486df6ab229Snicm 			return (0);
1487df6ab229Snicm 		if (!isxdigit((u_char)ch)) {
1488df6ab229Snicm 			yyerror("invalid \\%c argument", type);
1489df6ab229Snicm 			return (0);
1490df6ab229Snicm 		}
1491df6ab229Snicm 		s[i] = ch;
1492df6ab229Snicm 	}
1493df6ab229Snicm 	s[i] = '\0';
1494df6ab229Snicm 
1495df6ab229Snicm 	if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) ||
1496df6ab229Snicm 	    (size == 8 && sscanf(s, "%8x", &tmp) != 1)) {
1497df6ab229Snicm 		yyerror("invalid \\%c argument", type);
1498df6ab229Snicm 		return (0);
1499df6ab229Snicm 	}
15006852c63bSnicm 	mlen = wctomb(m, tmp);
15016852c63bSnicm 	if (mlen <= 0 || mlen > (int)sizeof m) {
1502df6ab229Snicm 		yyerror("invalid \\%c argument", type);
1503df6ab229Snicm 		return (0);
1504df6ab229Snicm 	}
15056852c63bSnicm 	yylex_append(buf, len, m, mlen);
1506df6ab229Snicm 	return (1);
1507df6ab229Snicm }
1508df6ab229Snicm 
1509df6ab229Snicm static int
yylex_token_variable(char ** buf,size_t * len)1510df6ab229Snicm yylex_token_variable(char **buf, size_t *len)
1511df6ab229Snicm {
1512df6ab229Snicm 	struct environ_entry	*envent;
1513df6ab229Snicm 	int			 ch, brackets = 0;
15144512d27dSnicm 	char			 name[1024];
1515df6ab229Snicm 	size_t			 namelen = 0;
1516df6ab229Snicm 	const char		*value;
1517df6ab229Snicm 
1518df6ab229Snicm 	ch = yylex_getc();
1519df6ab229Snicm 	if (ch == EOF)
1520df6ab229Snicm 		return (0);
1521df6ab229Snicm 	if (ch == '{')
1522df6ab229Snicm 		brackets = 1;
1523df6ab229Snicm 	else {
1524df6ab229Snicm 		if (!yylex_is_var(ch, 1)) {
1525df6ab229Snicm 			yylex_append1(buf, len, '$');
152673530c3cSnicm 			yylex_ungetc(ch);
1527df6ab229Snicm 			return (1);
1528df6ab229Snicm 		}
1529df6ab229Snicm 		name[namelen++] = ch;
1530df6ab229Snicm 	}
1531df6ab229Snicm 
1532df6ab229Snicm 	for (;;) {
1533df6ab229Snicm 		ch = yylex_getc();
1534df6ab229Snicm 		if (brackets && ch == '}')
1535df6ab229Snicm 			break;
1536df6ab229Snicm 		if (ch == EOF || !yylex_is_var(ch, 0)) {
1537df6ab229Snicm 			if (!brackets) {
153873530c3cSnicm 				yylex_ungetc(ch);
1539df6ab229Snicm 				break;
1540df6ab229Snicm 			}
1541df6ab229Snicm 			yyerror("invalid environment variable");
1542df6ab229Snicm 			return (0);
1543df6ab229Snicm 		}
1544df6ab229Snicm 		if (namelen == (sizeof name) - 2) {
1545df6ab229Snicm 			yyerror("environment variable is too long");
1546df6ab229Snicm 			return (0);
1547df6ab229Snicm 		}
1548df6ab229Snicm 		name[namelen++] = ch;
1549df6ab229Snicm 	}
1550df6ab229Snicm 	name[namelen] = '\0';
1551df6ab229Snicm 
1552df6ab229Snicm 	envent = environ_find(global_environ, name);
15539beba1feSnicm 	if (envent != NULL && envent->value != NULL) {
1554df6ab229Snicm 		value = envent->value;
1555df6ab229Snicm 		log_debug("%s: %s -> %s", __func__, name, value);
1556df6ab229Snicm 		yylex_append(buf, len, value, strlen(value));
1557df6ab229Snicm 	}
1558df6ab229Snicm 	return (1);
1559df6ab229Snicm }
1560df6ab229Snicm 
1561df6ab229Snicm static int
yylex_token_tilde(char ** buf,size_t * len)1562df6ab229Snicm yylex_token_tilde(char **buf, size_t *len)
1563df6ab229Snicm {
1564df6ab229Snicm 	struct environ_entry	*envent;
1565df6ab229Snicm 	int			 ch;
15664512d27dSnicm 	char			 name[1024];
1567df6ab229Snicm 	size_t			 namelen = 0;
1568df6ab229Snicm 	struct passwd		*pw;
1569df6ab229Snicm 	const char		*home = NULL;
1570df6ab229Snicm 
1571df6ab229Snicm 	for (;;) {
1572df6ab229Snicm 		ch = yylex_getc();
1573df6ab229Snicm 		if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) {
157473530c3cSnicm 			yylex_ungetc(ch);
1575df6ab229Snicm 			break;
1576df6ab229Snicm 		}
1577df6ab229Snicm 		if (namelen == (sizeof name) - 2) {
1578df6ab229Snicm 			yyerror("user name is too long");
1579df6ab229Snicm 			return (0);
1580df6ab229Snicm 		}
1581df6ab229Snicm 		name[namelen++] = ch;
1582df6ab229Snicm 	}
1583df6ab229Snicm 	name[namelen] = '\0';
1584df6ab229Snicm 
1585df6ab229Snicm 	if (*name == '\0') {
1586df6ab229Snicm 		envent = environ_find(global_environ, "HOME");
1587df6ab229Snicm 		if (envent != NULL && *envent->value != '\0')
1588df6ab229Snicm 			home = envent->value;
1589df6ab229Snicm 		else if ((pw = getpwuid(getuid())) != NULL)
1590df6ab229Snicm 			home = pw->pw_dir;
1591df6ab229Snicm 	} else {
1592df6ab229Snicm 		if ((pw = getpwnam(name)) != NULL)
1593df6ab229Snicm 			home = pw->pw_dir;
1594df6ab229Snicm 	}
1595df6ab229Snicm 	if (home == NULL)
1596df6ab229Snicm 		return (0);
1597df6ab229Snicm 
1598df6ab229Snicm 	log_debug("%s: ~%s -> %s", __func__, name, home);
1599df6ab229Snicm 	yylex_append(buf, len, home, strlen(home));
1600df6ab229Snicm 	return (1);
1601df6ab229Snicm }
1602df6ab229Snicm 
1603df6ab229Snicm static char *
yylex_token(int ch)1604df6ab229Snicm yylex_token(int ch)
1605df6ab229Snicm {
1606df6ab229Snicm 	char			*buf;
1607df6ab229Snicm 	size_t			 len;
1608df6ab229Snicm 	enum { START,
1609df6ab229Snicm 	       NONE,
1610df6ab229Snicm 	       DOUBLE_QUOTES,
1611df6ab229Snicm 	       SINGLE_QUOTES }	 state = NONE, last = START;
1612df6ab229Snicm 
1613df6ab229Snicm 	len = 0;
1614df6ab229Snicm 	buf = xmalloc(1);
1615df6ab229Snicm 
1616df6ab229Snicm 	for (;;) {
1617af9d9f3bSnicm 		/* EOF or \n are always the end of the token. */
1618*a01f743bSnicm 		if (ch == EOF) {
1619*a01f743bSnicm 			log_debug("%s: end at EOF", __func__);
1620df6ab229Snicm 			break;
1621*a01f743bSnicm 		}
1622*a01f743bSnicm 		if (state == NONE && ch == '\n') {
1623*a01f743bSnicm 			log_debug("%s: end at EOL", __func__);
1624*a01f743bSnicm 			break;
1625*a01f743bSnicm 		}
1626df6ab229Snicm 
1627af9d9f3bSnicm 		/* Whitespace or ; or } ends a token unless inside quotes. */
1628*a01f743bSnicm 		if (state == NONE && (ch == ' ' || ch == '\t')) {
1629*a01f743bSnicm 			log_debug("%s: end at WS", __func__);
1630df6ab229Snicm 			break;
1631*a01f743bSnicm 		}
1632*a01f743bSnicm 		if (state == NONE && (ch == ';' || ch == '}')) {
1633*a01f743bSnicm 			log_debug("%s: end at %c", __func__, ch);
1634*a01f743bSnicm 			break;
1635*a01f743bSnicm 		}
1636df6ab229Snicm 
1637d156f0b4Snicm 		/*
1638d156f0b4Snicm 		 * Spaces and comments inside quotes after \n are removed but
1639d156f0b4Snicm 		 * the \n is left.
1640d156f0b4Snicm 		 */
1641af9d9f3bSnicm 		if (ch == '\n' && state != NONE) {
1642d156f0b4Snicm 			yylex_append1(&buf, &len, '\n');
1643af9d9f3bSnicm 			while ((ch = yylex_getc()) == ' ' || ch == '\t')
1644af9d9f3bSnicm 				/* nothing */;
1645af9d9f3bSnicm 			if (ch != '#')
1646af9d9f3bSnicm 				continue;
1647af9d9f3bSnicm 			ch = yylex_getc();
1648af9d9f3bSnicm 			if (strchr(",#{}:", ch) != NULL) {
1649af9d9f3bSnicm 				yylex_ungetc(ch);
1650af9d9f3bSnicm 				ch = '#';
1651af9d9f3bSnicm 			} else {
1652af9d9f3bSnicm 				while ((ch = yylex_getc()) != '\n' && ch != EOF)
1653af9d9f3bSnicm 					/* nothing */;
1654af9d9f3bSnicm 			}
1655af9d9f3bSnicm 			continue;
1656af9d9f3bSnicm 		}
1657af9d9f3bSnicm 
1658af9d9f3bSnicm 		/* \ ~ and $ are expanded except in single quotes. */
1659df6ab229Snicm 		if (ch == '\\' && state != SINGLE_QUOTES) {
1660df6ab229Snicm 			if (!yylex_token_escape(&buf, &len))
1661df6ab229Snicm 				goto error;
1662df6ab229Snicm 			goto skip;
1663df6ab229Snicm 		}
1664df6ab229Snicm 		if (ch == '~' && last != state && state != SINGLE_QUOTES) {
1665df6ab229Snicm 			if (!yylex_token_tilde(&buf, &len))
1666df6ab229Snicm 				goto error;
1667df6ab229Snicm 			goto skip;
1668df6ab229Snicm 		}
1669df6ab229Snicm 		if (ch == '$' && state != SINGLE_QUOTES) {
1670df6ab229Snicm 			if (!yylex_token_variable(&buf, &len))
1671df6ab229Snicm 				goto error;
1672df6ab229Snicm 			goto skip;
1673df6ab229Snicm 		}
16741c947278Snicm 		if (ch == '}' && state == NONE)
16751c947278Snicm 			goto error;  /* unmatched (matched ones were handled) */
1676df6ab229Snicm 
1677af9d9f3bSnicm 		/* ' and " starts or end quotes (and is consumed). */
1678df6ab229Snicm 		if (ch == '\'') {
1679df6ab229Snicm 			if (state == NONE) {
1680df6ab229Snicm 				state = SINGLE_QUOTES;
1681df6ab229Snicm 				goto next;
1682df6ab229Snicm 			}
1683df6ab229Snicm 			if (state == SINGLE_QUOTES) {
1684df6ab229Snicm 				state = NONE;
1685df6ab229Snicm 				goto next;
1686df6ab229Snicm 			}
1687df6ab229Snicm 		}
1688df6ab229Snicm 		if (ch == '"') {
1689df6ab229Snicm 			if (state == NONE) {
1690df6ab229Snicm 				state = DOUBLE_QUOTES;
1691df6ab229Snicm 				goto next;
1692df6ab229Snicm 			}
1693df6ab229Snicm 			if (state == DOUBLE_QUOTES) {
1694df6ab229Snicm 				state = NONE;
1695df6ab229Snicm 				goto next;
1696df6ab229Snicm 			}
1697df6ab229Snicm 		}
1698df6ab229Snicm 
1699af9d9f3bSnicm 		/* Otherwise add the character to the buffer. */
1700df6ab229Snicm 		yylex_append1(&buf, &len, ch);
1701df6ab229Snicm 
1702df6ab229Snicm 	skip:
1703df6ab229Snicm 		last = state;
1704df6ab229Snicm 
1705df6ab229Snicm 	next:
1706df6ab229Snicm 		ch = yylex_getc();
1707df6ab229Snicm 	}
170873530c3cSnicm 	yylex_ungetc(ch);
1709df6ab229Snicm 
1710df6ab229Snicm 	buf[len] = '\0';
1711df6ab229Snicm 	log_debug("%s: %s", __func__, buf);
1712df6ab229Snicm 	return (buf);
1713df6ab229Snicm 
1714df6ab229Snicm error:
1715df6ab229Snicm 	free(buf);
1716df6ab229Snicm 	return (NULL);
1717df6ab229Snicm }
1718