xref: /openbsd/usr.sbin/ldomctl/parse.y (revision ae0acb23)
1*ae0acb23Skn /*	$OpenBSD: parse.y,v 1.21 2020/06/29 17:58:58 kn Exp $	*/
297d8cafcSkettenis 
397d8cafcSkettenis /*
497d8cafcSkettenis  * Copyright (c) 2012 Mark Kettenis <kettenis@openbsd.org>
597d8cafcSkettenis  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
697d8cafcSkettenis  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
797d8cafcSkettenis  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
897d8cafcSkettenis  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
997d8cafcSkettenis  *
1097d8cafcSkettenis  * Permission to use, copy, modify, and distribute this software for any
1197d8cafcSkettenis  * purpose with or without fee is hereby granted, provided that the above
1297d8cafcSkettenis  * copyright notice and this permission notice appear in all copies.
1397d8cafcSkettenis  *
1497d8cafcSkettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1597d8cafcSkettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1697d8cafcSkettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1797d8cafcSkettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1897d8cafcSkettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1997d8cafcSkettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
2097d8cafcSkettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2197d8cafcSkettenis  */
2297d8cafcSkettenis 
2397d8cafcSkettenis %{
2497d8cafcSkettenis #include <sys/types.h>
2597d8cafcSkettenis #include <sys/socket.h>
2697d8cafcSkettenis #include <sys/queue.h>
2797d8cafcSkettenis 
2897d8cafcSkettenis #include <net/if.h>
2997d8cafcSkettenis #include <netinet/in.h>
3097d8cafcSkettenis #include <netinet/if_ether.h>
3197d8cafcSkettenis 
3297d8cafcSkettenis #include <ctype.h>
3397d8cafcSkettenis #include <err.h>
3497d8cafcSkettenis #include <errno.h>
3597d8cafcSkettenis #include <limits.h>
3697d8cafcSkettenis #include <stdarg.h>
3797d8cafcSkettenis #include <stdio.h>
3897d8cafcSkettenis #include <stdlib.h>
3997d8cafcSkettenis #include <string.h>
40d73a4c4fSkn #include <util.h>
4197d8cafcSkettenis 
4297d8cafcSkettenis #include "ldomctl.h"
43d73a4c4fSkn #include "ldom_util.h"
4497d8cafcSkettenis 
4597d8cafcSkettenis TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
4697d8cafcSkettenis static struct file {
4797d8cafcSkettenis 	TAILQ_ENTRY(file)	 entry;
4897d8cafcSkettenis 	FILE			*stream;
4997d8cafcSkettenis 	char			*name;
5097d8cafcSkettenis 	int			 lineno;
5197d8cafcSkettenis 	int			 errors;
5297d8cafcSkettenis } *file, *topfile;
5397d8cafcSkettenis struct file	*pushfile(const char *);
5497d8cafcSkettenis int		 popfile(void);
5597d8cafcSkettenis int		 yyparse(void);
5697d8cafcSkettenis int		 yylex(void);
570f79392cSdoug int		 yyerror(const char *, ...)
580f79392cSdoug     __attribute__((__format__ (printf, 1, 2)))
590f79392cSdoug     __attribute__((__nonnull__ (1)));
6097d8cafcSkettenis int		 kw_cmp(const void *, const void *);
6197d8cafcSkettenis int		 lookup(char *);
6297d8cafcSkettenis int		 lgetc(int);
6397d8cafcSkettenis int		 lungetc(int);
6497d8cafcSkettenis int		 findeol(void);
6597d8cafcSkettenis 
6697d8cafcSkettenis struct ldom_config		*conf;
6797d8cafcSkettenis 
6859eb6c85Skettenis struct vcpu_opts {
6959eb6c85Skettenis 	uint64_t	count;
7059eb6c85Skettenis 	uint64_t	stride;
7159eb6c85Skettenis } vcpu_opts;
7259eb6c85Skettenis 
73693255e2Skn struct vdisk_opts {
74693255e2Skn 	const char	*devalias;
75693255e2Skn } vdisk_opts;
76693255e2Skn 
7759eb6c85Skettenis struct vnet_opts {
7897d8cafcSkettenis 	uint64_t	mac_addr;
7997d8cafcSkettenis 	uint64_t	mtu;
80a3b2112fSkn 	const char	*devalias;
8159eb6c85Skettenis } vnet_opts;
8259eb6c85Skettenis 
8359eb6c85Skettenis void		vcput_opts_default(void);
84693255e2Skn void		vdisk_opts_default(void);
8559eb6c85Skettenis void		vnet_opts_default(void);
8697d8cafcSkettenis 
8797d8cafcSkettenis typedef struct {
8897d8cafcSkettenis 	union {
8997d8cafcSkettenis 		int64_t			 number;
9097d8cafcSkettenis 		char			*string;
9159eb6c85Skettenis 		struct vcpu_opts	 vcpu_opts;
92693255e2Skn 		struct vdisk_opts	 vdisk_opts;
9359eb6c85Skettenis 		struct vnet_opts	 vnet_opts;
9497d8cafcSkettenis 	} v;
9597d8cafcSkettenis 	int lineno;
9697d8cafcSkettenis } YYSTYPE;
9797d8cafcSkettenis 
9897d8cafcSkettenis %}
9997d8cafcSkettenis 
10097d8cafcSkettenis %token	DOMAIN
101693255e2Skn %token	VCPU MEMORY VDISK DEVALIAS VNET VARIABLE IODEVICE
10297d8cafcSkettenis %token	MAC_ADDR MTU
10397d8cafcSkettenis %token	ERROR
10497d8cafcSkettenis %token	<v.string>		STRING
10597d8cafcSkettenis %token	<v.number>		NUMBER
10697d8cafcSkettenis %type	<v.number>		memory
10759eb6c85Skettenis %type	<v.vcpu_opts>		vcpu
108693255e2Skn %type	<v.vdisk_opts>		vdisk_opts vdisk_opts_l vdisk_opt
109a3b2112fSkn %type	<v.vdisk_opts>		vdisk_devalias
11059eb6c85Skettenis %type	<v.vnet_opts>		vnet_opts vnet_opts_l vnet_opt
11159eb6c85Skettenis %type	<v.vnet_opts>		mac_addr
11259eb6c85Skettenis %type	<v.vnet_opts>		mtu
113a3b2112fSkn %type	<v.vnet_opts>		vnet_devalias
11497d8cafcSkettenis %%
11597d8cafcSkettenis 
11697d8cafcSkettenis grammar		: /* empty */
11797d8cafcSkettenis 		| grammar '\n'
11897d8cafcSkettenis 		| grammar domain '\n'
11997d8cafcSkettenis 		| grammar error '\n'		{ file->errors++; }
12097d8cafcSkettenis 		;
12197d8cafcSkettenis 
12297d8cafcSkettenis domain		: DOMAIN STRING optnl '{' optnl	{
1234dacfa80Skn 			struct domain *odomain;
1244dacfa80Skn 			SIMPLEQ_FOREACH(odomain, &conf->domain_list, entry)
1254dacfa80Skn 				if (strcmp(odomain->name, $2) == 0) {
1264dacfa80Skn 					yyerror("duplicate domain name: %s", $2);
1274dacfa80Skn 					YYERROR;
1284dacfa80Skn 				}
12997d8cafcSkettenis 			domain = xzalloc(sizeof(struct domain));
13097d8cafcSkettenis 			domain->name = $2;
13197d8cafcSkettenis 			SIMPLEQ_INIT(&domain->vdisk_list);
13297d8cafcSkettenis 			SIMPLEQ_INIT(&domain->vnet_list);
1338e765095Skettenis 			SIMPLEQ_INIT(&domain->var_list);
1348cfaec25Skettenis 			SIMPLEQ_INIT(&domain->iodev_list);
13597d8cafcSkettenis 		}
13697d8cafcSkettenis 		    domainopts_l '}' {
137a4193c97Skn 			if (strcmp(domain->name, "primary") != 0) {
138a4193c97Skn 				if (domain->vcpu == 0) {
139a4193c97Skn 					yyerror("vcpu is required: %s",
140a4193c97Skn 					    domain->name);
141a4193c97Skn 					YYERROR;
142a4193c97Skn 				}
143a4193c97Skn 				if ( domain->memory == 0) {
144a4193c97Skn 					yyerror("memory is required: %s",
145a4193c97Skn 					    domain->name);
146a4193c97Skn 					YYERROR;
147a4193c97Skn 				}
148a4193c97Skn 				if (SIMPLEQ_EMPTY(&domain->vdisk_list) &&
149a4193c97Skn 				    SIMPLEQ_EMPTY(&domain->vnet_list) &&
150a4193c97Skn 				    SIMPLEQ_EMPTY(&domain->iodev_list)) {
151a4193c97Skn 					yyerror("at least one bootable device"
152a4193c97Skn 					    " is required: %s", domain->name);
153a4193c97Skn 					YYERROR;
154a4193c97Skn 				}
155a4193c97Skn 			}
15697d8cafcSkettenis 			SIMPLEQ_INSERT_TAIL(&conf->domain_list, domain, entry);
15797d8cafcSkettenis 			domain = NULL;
15897d8cafcSkettenis 		}
15997d8cafcSkettenis 		;
16097d8cafcSkettenis 
16197d8cafcSkettenis domainopts_l	: domainopts_l domainoptsl
16297d8cafcSkettenis 		| domainoptsl
16397d8cafcSkettenis 		;
16497d8cafcSkettenis 
16597d8cafcSkettenis domainoptsl	: domainopts nl
16697d8cafcSkettenis 		;
16797d8cafcSkettenis 
16859eb6c85Skettenis domainopts	: VCPU vcpu {
169da59641dSkn 			if (domain->vcpu) {
170da59641dSkn 				yyerror("duplicate vcpu option");
171da59641dSkn 				YYERROR;
172da59641dSkn 			}
17359eb6c85Skettenis 			domain->vcpu = $2.count;
17459eb6c85Skettenis 			domain->vcpu_stride = $2.stride;
17597d8cafcSkettenis 		}
17697d8cafcSkettenis 		| MEMORY memory {
177da59641dSkn 			if (domain->memory) {
178da59641dSkn 				yyerror("duplicate memory option");
179da59641dSkn 				YYERROR;
180da59641dSkn 			}
18197d8cafcSkettenis 			domain->memory = $2;
18297d8cafcSkettenis 		}
183693255e2Skn 		| VDISK STRING vdisk_opts {
184*ae0acb23Skn 			if (strcmp(domain->name, "primary") == 0) {
185*ae0acb23Skn 				yyerror("vdisk option invalid for primary"
186*ae0acb23Skn 				    " domain");
187*ae0acb23Skn 				YYERROR;
188*ae0acb23Skn 			}
18997d8cafcSkettenis 			struct vdisk *vdisk = xmalloc(sizeof(struct vdisk));
19097d8cafcSkettenis 			vdisk->path = $2;
191b37e2bbcSkn 			vdisk->devalias = $3.devalias;
19297d8cafcSkettenis 			SIMPLEQ_INSERT_TAIL(&domain->vdisk_list, vdisk, entry);
19397d8cafcSkettenis 		}
19497d8cafcSkettenis 		| VNET vnet_opts {
195*ae0acb23Skn 			if (strcmp(domain->name, "primary") == 0) {
196*ae0acb23Skn 				yyerror("vnet option invalid for primary"
197*ae0acb23Skn 				    " domain");
198*ae0acb23Skn 				YYERROR;
199*ae0acb23Skn 			}
20097d8cafcSkettenis 			struct vnet *vnet = xmalloc(sizeof(struct vnet));
20197d8cafcSkettenis 			vnet->mac_addr = $2.mac_addr;
20297d8cafcSkettenis 			vnet->mtu = $2.mtu;
203a3b2112fSkn 			vnet->devalias = $2.devalias;
20497d8cafcSkettenis 			SIMPLEQ_INSERT_TAIL(&domain->vnet_list, vnet, entry);
20597d8cafcSkettenis 		}
2068e765095Skettenis 		| VARIABLE STRING '=' STRING {
2078e765095Skettenis 			struct var *var = xmalloc(sizeof(struct var));
2088e765095Skettenis 			var->name = $2;
2098e765095Skettenis 			var->str = $4;
2108e765095Skettenis 			SIMPLEQ_INSERT_TAIL(&domain->var_list, var, entry);
2118e765095Skettenis 		}
2128cfaec25Skettenis 		| IODEVICE STRING {
213*ae0acb23Skn 			if (strcmp(domain->name, "primary") == 0) {
214*ae0acb23Skn 				yyerror("iodevice option invalid for primary"
215*ae0acb23Skn 				    " domain");
216*ae0acb23Skn 				YYERROR;
217*ae0acb23Skn 			}
218da59641dSkn 			struct domain *odomain;
219da59641dSkn 			struct iodev *iodev;
220da59641dSkn 			SIMPLEQ_FOREACH(odomain, &conf->domain_list, entry)
221da59641dSkn 				SIMPLEQ_FOREACH(iodev, &odomain->iodev_list, entry)
222da59641dSkn 					if (strcmp(iodev->path, $2) == 0) {
223da59641dSkn 						yyerror("iodevice %s already"
2248efa5877Skn 						    " assigned", $2);
225da59641dSkn 						YYERROR;
226da59641dSkn 					}
227da59641dSkn 			iodev = xmalloc(sizeof(struct iodev));
2288cfaec25Skettenis 			iodev->path = $2;
2298cfaec25Skettenis 			SIMPLEQ_INSERT_TAIL(&domain->iodev_list, iodev, entry);
2308cfaec25Skettenis 		}
23197d8cafcSkettenis 		;
23297d8cafcSkettenis 
233693255e2Skn vdisk_opts	:	{ vdisk_opts_default(); }
234693255e2Skn 		  vdisk_opts_l
235693255e2Skn 			{ $$ = vdisk_opts; }
236693255e2Skn 		|	{ vdisk_opts_default(); $$ = vdisk_opts; }
237693255e2Skn 		;
238693255e2Skn vdisk_opts_l	: vdisk_opts_l vdisk_opt
239693255e2Skn 		| vdisk_opt
240693255e2Skn 		;
241a3b2112fSkn vdisk_opt	: vdisk_devalias
242693255e2Skn 		;
243693255e2Skn 
244a3b2112fSkn vdisk_devalias	: DEVALIAS '=' STRING {
245693255e2Skn 			vdisk_opts.devalias = $3;
246693255e2Skn 		}
247693255e2Skn 		;
248693255e2Skn 
24959eb6c85Skettenis vnet_opts	:	{ vnet_opts_default(); }
25097d8cafcSkettenis 		  vnet_opts_l
25159eb6c85Skettenis 			{ $$ = vnet_opts; }
25259eb6c85Skettenis 		|	{ vnet_opts_default(); $$ = vnet_opts; }
25397d8cafcSkettenis 		;
25497d8cafcSkettenis vnet_opts_l	: vnet_opts_l vnet_opt
25597d8cafcSkettenis 		| vnet_opt
25697d8cafcSkettenis 		;
25797d8cafcSkettenis vnet_opt	: mac_addr
25897d8cafcSkettenis 		| mtu
259a3b2112fSkn 		| vnet_devalias
26097d8cafcSkettenis 		;
26197d8cafcSkettenis 
26297d8cafcSkettenis mac_addr	: MAC_ADDR '=' STRING {
26397d8cafcSkettenis 			struct ether_addr *ea;
26497d8cafcSkettenis 
26597d8cafcSkettenis 			if ((ea = ether_aton($3)) == NULL) {
26632cf627cSotto 				yyerror("invalid address: %s", $3);
26797d8cafcSkettenis 				YYERROR;
26897d8cafcSkettenis 			}
26997d8cafcSkettenis 
27059eb6c85Skettenis 			vnet_opts.mac_addr =
27197d8cafcSkettenis 			    (uint64_t)ea->ether_addr_octet[0] << 40 |
27297d8cafcSkettenis 			    (uint64_t)ea->ether_addr_octet[1] << 32 |
27397d8cafcSkettenis 			    ea->ether_addr_octet[2] << 24 |
27497d8cafcSkettenis 			    ea->ether_addr_octet[3] << 16 |
27597d8cafcSkettenis 			    ea->ether_addr_octet[4] << 8 |
27697d8cafcSkettenis 			    ea->ether_addr_octet[5];
27797d8cafcSkettenis 		}
27897d8cafcSkettenis 		;
27997d8cafcSkettenis 
28097d8cafcSkettenis mtu		: MTU '=' NUMBER {
28159eb6c85Skettenis 			vnet_opts.mtu = $3;
28259eb6c85Skettenis 		}
28359eb6c85Skettenis 		;
28459eb6c85Skettenis 
285a3b2112fSkn vnet_devalias	: DEVALIAS '=' STRING {
286a3b2112fSkn 			vnet_opts.devalias = $3;
287a3b2112fSkn 		}
288a3b2112fSkn 		;
289a3b2112fSkn 
29059eb6c85Skettenis vcpu		: STRING {
29159eb6c85Skettenis 			const char *errstr;
29259eb6c85Skettenis 			char *colon;
29359eb6c85Skettenis 
29459eb6c85Skettenis 			vcpu_opts_default();
29559eb6c85Skettenis 			colon = strchr($1, ':');
29659eb6c85Skettenis 			if (colon == NULL) {
29759eb6c85Skettenis 				yyerror("bogus stride in %s", $1);
29859eb6c85Skettenis 				YYERROR;
29959eb6c85Skettenis 			}
30059eb6c85Skettenis 			*colon++ = '\0';
30159eb6c85Skettenis 			vcpu_opts.count = strtonum($1, 0, INT_MAX, &errstr);
30259eb6c85Skettenis 			if (errstr) {
30359eb6c85Skettenis 				yyerror("number %s is %s", $1, errstr);
30459eb6c85Skettenis 				YYERROR;
30559eb6c85Skettenis 			}
30659eb6c85Skettenis 			vcpu_opts.stride = strtonum(colon, 0, INT_MAX, &errstr);
30759eb6c85Skettenis 			if (errstr) {
30859eb6c85Skettenis 				yyerror("number %s is %s", colon, errstr);
30959eb6c85Skettenis 				YYERROR;
31059eb6c85Skettenis 			}
31159eb6c85Skettenis 			$$ = vcpu_opts;
31259eb6c85Skettenis 		}
31359eb6c85Skettenis 		| NUMBER {
31459eb6c85Skettenis 			vcpu_opts_default();
31559eb6c85Skettenis 			vcpu_opts.count = $1;
31659eb6c85Skettenis 			$$ = vcpu_opts;
31797d8cafcSkettenis 		}
31897d8cafcSkettenis 		;
31997d8cafcSkettenis 
32097d8cafcSkettenis memory		: NUMBER {
32197d8cafcSkettenis 			$$ = $1;
32297d8cafcSkettenis 		}
32397d8cafcSkettenis 		| STRING {
324d73a4c4fSkn 			if (scan_scaled($1, &$$) == -1) {
325d73a4c4fSkn 				yyerror("invalid size: %s", $1);
32697d8cafcSkettenis 				YYERROR;
32797d8cafcSkettenis 			}
32897d8cafcSkettenis 		}
32997d8cafcSkettenis 		;
33097d8cafcSkettenis 
33197d8cafcSkettenis optnl		: '\n' optnl
33297d8cafcSkettenis 		|
33397d8cafcSkettenis 		;
33497d8cafcSkettenis 
33597d8cafcSkettenis nl		: '\n' optnl		/* one newline or more */
33697d8cafcSkettenis 		;
33797d8cafcSkettenis 
33897d8cafcSkettenis %%
33997d8cafcSkettenis 
34097d8cafcSkettenis void
34159eb6c85Skettenis vcpu_opts_default(void)
34297d8cafcSkettenis {
34359eb6c85Skettenis 	vcpu_opts.count = -1;
34459eb6c85Skettenis 	vcpu_opts.stride = 1;
34559eb6c85Skettenis }
34659eb6c85Skettenis 
34759eb6c85Skettenis void
348693255e2Skn vdisk_opts_default(void)
349693255e2Skn {
350693255e2Skn 	vdisk_opts.devalias = NULL;
351693255e2Skn }
352693255e2Skn 
353693255e2Skn void
35459eb6c85Skettenis vnet_opts_default(void)
35559eb6c85Skettenis {
35659eb6c85Skettenis 	vnet_opts.mac_addr = -1;
35759eb6c85Skettenis 	vnet_opts.mtu = 1500;
358a3b2112fSkn 	vnet_opts.devalias = NULL;
35997d8cafcSkettenis }
36097d8cafcSkettenis 
36197d8cafcSkettenis struct keywords {
36297d8cafcSkettenis 	const char	*k_name;
36397d8cafcSkettenis 	int		 k_val;
36497d8cafcSkettenis };
36597d8cafcSkettenis 
36697d8cafcSkettenis int
36797d8cafcSkettenis yyerror(const char *fmt, ...)
36897d8cafcSkettenis {
36997d8cafcSkettenis 	va_list		 ap;
37097d8cafcSkettenis 
37197d8cafcSkettenis 	file->errors++;
37297d8cafcSkettenis 	va_start(ap, fmt);
37397d8cafcSkettenis 	fprintf(stderr, "%s:%d ", file->name, yylval.lineno);
37497d8cafcSkettenis 	vfprintf(stderr, fmt, ap);
37597d8cafcSkettenis 	fprintf(stderr, "\n");
37697d8cafcSkettenis 	va_end(ap);
37797d8cafcSkettenis 	return (0);
37897d8cafcSkettenis }
37997d8cafcSkettenis 
38097d8cafcSkettenis int
38197d8cafcSkettenis kw_cmp(const void *k, const void *e)
38297d8cafcSkettenis {
38397d8cafcSkettenis 	return (strcmp(k, ((const struct keywords *)e)->k_name));
38497d8cafcSkettenis }
38597d8cafcSkettenis 
38697d8cafcSkettenis int
38797d8cafcSkettenis lookup(char *s)
38897d8cafcSkettenis {
38997d8cafcSkettenis 	/* this has to be sorted always */
39097d8cafcSkettenis 	static const struct keywords keywords[] = {
391693255e2Skn 		{ "devalias",		DEVALIAS},
39297d8cafcSkettenis 		{ "domain",		DOMAIN},
3938cfaec25Skettenis 		{ "iodevice",		IODEVICE},
39497d8cafcSkettenis 		{ "mac-addr",		MAC_ADDR},
39597d8cafcSkettenis 		{ "memory",		MEMORY},
39697d8cafcSkettenis 		{ "mtu",		MTU},
3978e765095Skettenis 		{ "variable",		VARIABLE},
39897d8cafcSkettenis 		{ "vcpu",		VCPU},
39997d8cafcSkettenis 		{ "vdisk",		VDISK},
40097d8cafcSkettenis 		{ "vnet",		VNET}
40197d8cafcSkettenis 	};
40297d8cafcSkettenis 	const struct keywords	*p;
40397d8cafcSkettenis 
40497d8cafcSkettenis 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
40597d8cafcSkettenis 	    sizeof(keywords[0]), kw_cmp);
40697d8cafcSkettenis 
40797d8cafcSkettenis 	if (p)
40897d8cafcSkettenis 		return (p->k_val);
40997d8cafcSkettenis 	else
41097d8cafcSkettenis 		return (STRING);
41197d8cafcSkettenis }
41297d8cafcSkettenis 
41397d8cafcSkettenis #define MAXPUSHBACK	128
41497d8cafcSkettenis 
415015d7b4dSbenno u_char	*parsebuf;
41697d8cafcSkettenis int	 parseindex;
417015d7b4dSbenno u_char	 pushback_buffer[MAXPUSHBACK];
41897d8cafcSkettenis int	 pushback_index = 0;
41997d8cafcSkettenis 
42097d8cafcSkettenis int
42197d8cafcSkettenis lgetc(int quotec)
42297d8cafcSkettenis {
42397d8cafcSkettenis 	int		c, next;
42497d8cafcSkettenis 
42597d8cafcSkettenis 	if (parsebuf) {
42697d8cafcSkettenis 		/* Read character from the parsebuffer instead of input. */
42797d8cafcSkettenis 		if (parseindex >= 0) {
42897d8cafcSkettenis 			c = parsebuf[parseindex++];
42997d8cafcSkettenis 			if (c != '\0')
43097d8cafcSkettenis 				return (c);
43197d8cafcSkettenis 			parsebuf = NULL;
43297d8cafcSkettenis 		} else
43397d8cafcSkettenis 			parseindex++;
43497d8cafcSkettenis 	}
43597d8cafcSkettenis 
43697d8cafcSkettenis 	if (pushback_index)
43797d8cafcSkettenis 		return (pushback_buffer[--pushback_index]);
43897d8cafcSkettenis 
43997d8cafcSkettenis 	if (quotec) {
44097d8cafcSkettenis 		if ((c = getc(file->stream)) == EOF) {
44197d8cafcSkettenis 			yyerror("reached end of file while parsing "
44297d8cafcSkettenis 			    "quoted string");
44397d8cafcSkettenis 			if (file == topfile || popfile() == EOF)
44497d8cafcSkettenis 				return (EOF);
44597d8cafcSkettenis 			return (quotec);
44697d8cafcSkettenis 		}
44797d8cafcSkettenis 		return (c);
44897d8cafcSkettenis 	}
44997d8cafcSkettenis 
45097d8cafcSkettenis 	while ((c = getc(file->stream)) == '\\') {
45197d8cafcSkettenis 		next = getc(file->stream);
45297d8cafcSkettenis 		if (next != '\n') {
45397d8cafcSkettenis 			c = next;
45497d8cafcSkettenis 			break;
45597d8cafcSkettenis 		}
45697d8cafcSkettenis 		yylval.lineno = file->lineno;
45797d8cafcSkettenis 		file->lineno++;
45897d8cafcSkettenis 	}
45997d8cafcSkettenis 
46097d8cafcSkettenis 	while (c == EOF) {
46197d8cafcSkettenis 		if (file == topfile || popfile() == EOF)
46297d8cafcSkettenis 			return (EOF);
46397d8cafcSkettenis 		c = getc(file->stream);
46497d8cafcSkettenis 	}
46597d8cafcSkettenis 	return (c);
46697d8cafcSkettenis }
46797d8cafcSkettenis 
46897d8cafcSkettenis int
46997d8cafcSkettenis lungetc(int c)
47097d8cafcSkettenis {
47197d8cafcSkettenis 	if (c == EOF)
47297d8cafcSkettenis 		return (EOF);
47397d8cafcSkettenis 	if (parsebuf) {
47497d8cafcSkettenis 		parseindex--;
47597d8cafcSkettenis 		if (parseindex >= 0)
47697d8cafcSkettenis 			return (c);
47797d8cafcSkettenis 	}
47897d8cafcSkettenis 	if (pushback_index < MAXPUSHBACK-1)
47997d8cafcSkettenis 		return (pushback_buffer[pushback_index++] = c);
48097d8cafcSkettenis 	else
48197d8cafcSkettenis 		return (EOF);
48297d8cafcSkettenis }
48397d8cafcSkettenis 
48497d8cafcSkettenis int
48597d8cafcSkettenis findeol(void)
48697d8cafcSkettenis {
48797d8cafcSkettenis 	int	c;
48897d8cafcSkettenis 
48997d8cafcSkettenis 	parsebuf = NULL;
49097d8cafcSkettenis 
49197d8cafcSkettenis 	/* skip to either EOF or the first real EOL */
49297d8cafcSkettenis 	while (1) {
49397d8cafcSkettenis 		if (pushback_index)
49497d8cafcSkettenis 			c = pushback_buffer[--pushback_index];
49597d8cafcSkettenis 		else
49697d8cafcSkettenis 			c = lgetc(0);
49797d8cafcSkettenis 		if (c == '\n') {
49897d8cafcSkettenis 			file->lineno++;
49997d8cafcSkettenis 			break;
50097d8cafcSkettenis 		}
50197d8cafcSkettenis 		if (c == EOF)
50297d8cafcSkettenis 			break;
50397d8cafcSkettenis 	}
50497d8cafcSkettenis 	return (ERROR);
50597d8cafcSkettenis }
50697d8cafcSkettenis 
50797d8cafcSkettenis int
50897d8cafcSkettenis yylex(void)
50997d8cafcSkettenis {
510015d7b4dSbenno 	u_char	 buf[8096];
511015d7b4dSbenno 	u_char	*p;
51297d8cafcSkettenis 	int	 quotec, next, c;
51397d8cafcSkettenis 	int	 token;
51497d8cafcSkettenis 
51597d8cafcSkettenis 	p = buf;
51697d8cafcSkettenis 	while ((c = lgetc(0)) == ' ' || c == '\t')
51797d8cafcSkettenis 		; /* nothing */
51897d8cafcSkettenis 
51997d8cafcSkettenis 	yylval.lineno = file->lineno;
52097d8cafcSkettenis 	if (c == '#')
52197d8cafcSkettenis 		while ((c = lgetc(0)) != '\n' && c != EOF)
52297d8cafcSkettenis 			; /* nothing */
52397d8cafcSkettenis 
52497d8cafcSkettenis 	switch (c) {
52597d8cafcSkettenis 	case '\'':
52697d8cafcSkettenis 	case '"':
52797d8cafcSkettenis 		quotec = c;
52897d8cafcSkettenis 		while (1) {
52997d8cafcSkettenis 			if ((c = lgetc(quotec)) == EOF)
53097d8cafcSkettenis 				return (0);
53197d8cafcSkettenis 			if (c == '\n') {
53297d8cafcSkettenis 				file->lineno++;
53397d8cafcSkettenis 				continue;
53497d8cafcSkettenis 			} else if (c == '\\') {
53597d8cafcSkettenis 				if ((next = lgetc(quotec)) == EOF)
53697d8cafcSkettenis 					return (0);
537a1533359Ssashan 				if (next == quotec || next == ' ' ||
538a1533359Ssashan 				    next == '\t')
53997d8cafcSkettenis 					c = next;
54097d8cafcSkettenis 				else if (next == '\n') {
54197d8cafcSkettenis 					file->lineno++;
54297d8cafcSkettenis 					continue;
54397d8cafcSkettenis 				} else
54497d8cafcSkettenis 					lungetc(next);
54597d8cafcSkettenis 			} else if (c == quotec) {
54697d8cafcSkettenis 				*p = '\0';
54797d8cafcSkettenis 				break;
54841eef22fSjsg 			} else if (c == '\0') {
54941eef22fSjsg 				yyerror("syntax error");
55041eef22fSjsg 				return (findeol());
55197d8cafcSkettenis 			}
55297d8cafcSkettenis 			if (p + 1 >= buf + sizeof(buf) - 1) {
55397d8cafcSkettenis 				yyerror("string too long");
55497d8cafcSkettenis 				return (findeol());
55597d8cafcSkettenis 			}
556015d7b4dSbenno 			*p++ = c;
55797d8cafcSkettenis 		}
55897d8cafcSkettenis 		yylval.v.string = xstrdup(buf);
55997d8cafcSkettenis 		return (STRING);
56097d8cafcSkettenis 	}
56197d8cafcSkettenis 
56297d8cafcSkettenis #define allowed_to_end_number(x) \
56397d8cafcSkettenis 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
56497d8cafcSkettenis 
56597d8cafcSkettenis 	if (c == '-' || isdigit(c)) {
56697d8cafcSkettenis 		do {
56797d8cafcSkettenis 			*p++ = c;
568915c3f33Sderaadt 			if ((size_t)(p-buf) >= sizeof(buf)) {
56997d8cafcSkettenis 				yyerror("string too long");
57097d8cafcSkettenis 				return (findeol());
57197d8cafcSkettenis 			}
57297d8cafcSkettenis 		} while ((c = lgetc(0)) != EOF && isdigit(c));
57397d8cafcSkettenis 		lungetc(c);
57497d8cafcSkettenis 		if (p == buf + 1 && buf[0] == '-')
57597d8cafcSkettenis 			goto nodigits;
57697d8cafcSkettenis 		if (c == EOF || allowed_to_end_number(c)) {
57797d8cafcSkettenis 			const char *errstr = NULL;
57897d8cafcSkettenis 
57997d8cafcSkettenis 			*p = '\0';
58097d8cafcSkettenis 			yylval.v.number = strtonum(buf, LLONG_MIN,
58197d8cafcSkettenis 			    LLONG_MAX, &errstr);
58297d8cafcSkettenis 			if (errstr) {
58397d8cafcSkettenis 				yyerror("\"%s\" invalid number: %s",
58497d8cafcSkettenis 				    buf, errstr);
58597d8cafcSkettenis 				return (findeol());
58697d8cafcSkettenis 			}
58797d8cafcSkettenis 			return (NUMBER);
58897d8cafcSkettenis 		} else {
58997d8cafcSkettenis nodigits:
59097d8cafcSkettenis 			while (p > buf + 1)
59197d8cafcSkettenis 				lungetc(*--p);
59297d8cafcSkettenis 			c = *--p;
59397d8cafcSkettenis 			if (c == '-')
59497d8cafcSkettenis 				return (c);
59597d8cafcSkettenis 		}
59697d8cafcSkettenis 	}
59797d8cafcSkettenis 
59897d8cafcSkettenis #define allowed_in_string(x) \
59997d8cafcSkettenis 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
60097d8cafcSkettenis 	x != '{' && x != '}' && x != '<' && x != '>' && \
60197d8cafcSkettenis 	x != '!' && x != '=' && x != '/' && x != '#' && \
60297d8cafcSkettenis 	x != ','))
60397d8cafcSkettenis 
60497d8cafcSkettenis 	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
60597d8cafcSkettenis 		do {
60697d8cafcSkettenis 			*p++ = c;
607915c3f33Sderaadt 			if ((size_t)(p-buf) >= sizeof(buf)) {
60897d8cafcSkettenis 				yyerror("string too long");
60997d8cafcSkettenis 				return (findeol());
61097d8cafcSkettenis 			}
61197d8cafcSkettenis 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
61297d8cafcSkettenis 		lungetc(c);
61397d8cafcSkettenis 		*p = '\0';
61497d8cafcSkettenis 		if ((token = lookup(buf)) == STRING)
61597d8cafcSkettenis 			yylval.v.string = xstrdup(buf);
61697d8cafcSkettenis 		return (token);
61797d8cafcSkettenis 	}
61897d8cafcSkettenis 	if (c == '\n') {
61997d8cafcSkettenis 		yylval.lineno = file->lineno;
62097d8cafcSkettenis 		file->lineno++;
62197d8cafcSkettenis 	}
62297d8cafcSkettenis 	if (c == EOF)
62397d8cafcSkettenis 		return (0);
62497d8cafcSkettenis 	return (c);
62597d8cafcSkettenis }
62697d8cafcSkettenis 
62797d8cafcSkettenis struct file *
62897d8cafcSkettenis pushfile(const char *name)
62997d8cafcSkettenis {
63097d8cafcSkettenis 	struct file	*nfile;
63197d8cafcSkettenis 
63297d8cafcSkettenis 	nfile = xzalloc(sizeof(struct file));
63397d8cafcSkettenis 	nfile->name = xstrdup(name);
63497d8cafcSkettenis 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
6356a3d55f9Skrw 		warn("%s: %s", __func__, nfile->name);
63697d8cafcSkettenis 		free(nfile->name);
63797d8cafcSkettenis 		free(nfile);
63897d8cafcSkettenis 		return (NULL);
63997d8cafcSkettenis 	}
64097d8cafcSkettenis 	nfile->lineno = 1;
64197d8cafcSkettenis 	TAILQ_INSERT_TAIL(&files, nfile, entry);
64297d8cafcSkettenis 	return (nfile);
64397d8cafcSkettenis }
64497d8cafcSkettenis 
64597d8cafcSkettenis int
64697d8cafcSkettenis popfile(void)
64797d8cafcSkettenis {
64897d8cafcSkettenis 	struct file	*prev;
64997d8cafcSkettenis 
65097d8cafcSkettenis 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
65197d8cafcSkettenis 		prev->errors += file->errors;
65297d8cafcSkettenis 
65397d8cafcSkettenis 	TAILQ_REMOVE(&files, file, entry);
65497d8cafcSkettenis 	fclose(file->stream);
65597d8cafcSkettenis 	free(file->name);
65697d8cafcSkettenis 	free(file);
65797d8cafcSkettenis 	file = prev;
65897d8cafcSkettenis 	return (file ? 0 : EOF);
65997d8cafcSkettenis }
66097d8cafcSkettenis 
66197d8cafcSkettenis int
66297d8cafcSkettenis parse_config(const char *filename, struct ldom_config *xconf)
66397d8cafcSkettenis {
66497d8cafcSkettenis 	int		 errors = 0;
66597d8cafcSkettenis 
66697d8cafcSkettenis 	conf = xconf;
66797d8cafcSkettenis 
66897d8cafcSkettenis 	if ((file = pushfile(filename)) == NULL) {
66997d8cafcSkettenis 		return (-1);
67097d8cafcSkettenis 	}
67197d8cafcSkettenis 	topfile = file;
67297d8cafcSkettenis 
67397d8cafcSkettenis 	yyparse();
67497d8cafcSkettenis 	errors = file->errors;
67597d8cafcSkettenis 	popfile();
67697d8cafcSkettenis 
67797d8cafcSkettenis 	return (errors ? -1 : 0);
67897d8cafcSkettenis }
679