1%{
2/*	$NetBSD: scan.l,v 1.15 2010/03/08 10:19:14 pooka Exp $	*/
3
4/*
5 * Copyright (c) 1992, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * This software was developed by the Computer Systems Engineering group
9 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
10 * contributed to Berkeley.
11 *
12 * All advertising materials mentioning features or use of this software
13 * must display the following acknowledgement:
14 *	This product includes software developed by the University of
15 *	California, Lawrence Berkeley Laboratories.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 * 1. Redistributions of source code must retain the above copyright
21 *    notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 *    notice, this list of conditions and the following disclaimer in the
24 *    documentation and/or other materials provided with the distribution.
25 * 3. Neither the name of the University nor the names of its contributors
26 *    may be used to endorse or promote products derived from this software
27 *    without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 *
41 *	from: @(#)scan.l	8.1 (Berkeley) 6/6/93
42 */
43
44#include <sys/param.h>
45#include <errno.h>
46#include <libgen.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <unistd.h>
51#include <stddef.h>
52#include <ctype.h>
53#include <util.h>
54#undef ECHO
55#include "defs.h"
56#include "gram.h"
57
58int	yyline;
59const char *yyfile;
60const char *lastfile;
61char curinclpath[PATH_MAX];
62int ifdefstate = -1;
63int st;
64#define IDS_PARENT_DISABLED \
65    ((ifdefstate > 6) && ((((ifdefstate/6)-1) & 1) == 1))
66#define IDS_MAX_DEPTH		362797056 /* 6^11 */
67/* States for ifdefstate:
68
69  0  -> matched ifdef
70  1  -> unmatched ifdef
71  2  -> matched elifdef
72  3  -> unmatched elifdef
73  4  -> matched else
74  5  -> unmatched else
75
76  Upon "ifdef", add one and multiply by 6.
77  Upon "endif", divide by 6, remove 1.
78
79  ifdef -> MATCH => continue
80           MISMATCH => set to 1
81  elifdef -> if (!1) -> MISMATCH
82             MATCH => set to 2
83             MISMATCH => if (2 || 3) set to 3, else set to 1
84  else -> if (1) -> MATCH
85          MATCH => set to 4
86          MISMATCH => set to 5
87
88  in each case, if parent & 1 == 1, MISMATCH
89*/
90
91/*
92 * Data for returning to previous files from include files.
93 */
94struct incl {
95	struct	incl *in_prev;	/* previous includes in effect, if any */
96	YY_BUFFER_STATE in_buf;	/* previous lex state */
97	const char *in_fname;	/* previous file name */
98	int	in_lineno;	/* previous line number */
99	int	in_ateof;	/* token to insert at EOF */
100	int	in_interesting;	/* previous value for "interesting" */
101	int	in_ifdefstate;	/* conditional level */
102};
103static struct incl *incl;
104static int endinclude(void);
105static int getincludepath(void);
106static int getcurifdef(void);
107
108
109%}
110
111%option  noyywrap
112
113PATH	[A-Za-z_0-9]*[./][-A-Za-z_0-9./]*
114QCHARS	([^"\n]|\\\")+
115WORD	[A-Za-z_][-A-Za-z_0-9]*
116FILENAME	({PATH}|\"{QCHARS}\")
117RESTOFLINE	[ \t]*(#[^\n]*)?\n
118
119%x	IGNORED
120
121%%
122		/* Local variables for yylex() */
123		int tok;
124
125and		return AND;
126at		return AT;
127attach		return ATTACH;
128block		return BLOCK;
129build		return BUILD;
130char		return CHAR;
131compile-with	return COMPILE_WITH;
132config		return CONFIG;
133deffs		return DEFFS;
134define		return DEFINE;
135defflag		return DEFFLAG;
136defopt		return DEFOPT;
137defparam	return DEFPARAM;
138defpseudo	return DEFPSEUDO;
139defpseudodev	return DEFPSEUDODEV;
140devclass	return DEVCLASS;
141device		return DEVICE;
142device-major	return DEVICE_MAJOR;
143dumps		return DUMPS;
144file		return XFILE;
145file-system	return FILE_SYSTEM;
146flags		return FLAGS;
147ident		return IDENT;
148ioconf		return IOCONF;
149linkzero	return LINKZERO;
150machine		return XMACHINE;
151major		return MAJOR;
152makeoptions	return MAKEOPTIONS;
153maxpartitions	return MAXPARTITIONS;
154maxusers	return MAXUSERS;
155minor		return MINOR;
156needs-count	return NEEDS_COUNT;
157needs-flag	return NEEDS_FLAG;
158no		return NO;
159object		return XOBJECT;
160obsolete	return OBSOLETE;
161on		return ON;
162options		return OPTIONS;
163prefix		return PREFIX;
164pseudo-device	return PSEUDO_DEVICE;
165pseudo-root	return PSEUDO_ROOT;
166root		return ROOT;
167single		return SINGLE;
168source		return SOURCE;
169type		return TYPE;
170vector 		return VECTOR;
171version 	return VERSION;
172with		return WITH;
173
174\+=		return PLUSEQ;
175:=		return COLONEQ;
176
177<*>ifdef[ \t]+{WORD}{RESTOFLINE} {
178		ifdefstate = (ifdefstate + 1) * 6;
179		if (ifdefstate >= IDS_MAX_DEPTH) {
180			yyerror("too many levels of conditional");
181		}
182		if (!IDS_PARENT_DISABLED && getcurifdef()) {
183			BEGIN(INITIAL);
184		} else {
185			ifdefstate++;
186			BEGIN(IGNORED);
187		}
188		yyline++;
189	}
190
191<*>ifndef[ \t]+{WORD}{RESTOFLINE} {
192		ifdefstate = (ifdefstate + 1) * 6;
193		if (ifdefstate >= IDS_MAX_DEPTH) {
194			yyerror("too many levels of conditional");
195		}
196		if (!IDS_PARENT_DISABLED && !getcurifdef()) {
197			BEGIN(INITIAL);
198		} else {
199			ifdefstate++;
200			BEGIN(IGNORED);
201		}
202		yyline++;
203	}
204
205
206<*>elifdef[ \t]+{WORD}{RESTOFLINE} {
207		st = ifdefstate % 6;
208		if (ifdefstate < 0 || st > 3) {
209			yyerror("mismatched elifdef");
210		}
211		if (IDS_PARENT_DISABLED ||
212		    st != 1 || !getcurifdef()) {
213			if (st == 2 || st == 3) {
214				ifdefstate += 3 - st;
215			} else {
216				ifdefstate += 1 - st;
217			}
218			BEGIN(IGNORED);
219		} else {
220			ifdefstate++;
221			BEGIN(INITIAL);
222		}
223		yyline++;
224	}
225
226<*>elifndef[ \t]+{WORD}{RESTOFLINE} {
227		st = ifdefstate % 6;
228		if (ifdefstate < 0 || st > 3) {
229			yyerror("mismatched elifndef");
230		}
231		if (IDS_PARENT_DISABLED ||
232		    st != 1 || getcurifdef()) {
233			if (st == 2 || st == 3) {
234				ifdefstate += 3 - st;
235			} else {
236				ifdefstate += 1 - st;
237			}
238			BEGIN(IGNORED);
239		} else {
240			ifdefstate++;
241			BEGIN(INITIAL);
242		}
243		yyline++;
244	}
245
246<*>else{RESTOFLINE} {
247		st = ifdefstate % 6;
248		if (ifdefstate < 0 || st > 3) {
249			yyerror("mismatched else");
250		}
251		if (!IDS_PARENT_DISABLED && (st == 1)) {
252			ifdefstate += 3;
253			BEGIN(INITIAL);
254		} else {
255			ifdefstate += 5 - st;
256			BEGIN(IGNORED);
257		}
258		yyline++;
259	}
260
261<*>endif{RESTOFLINE} {
262		if (ifdefstate < 0) {
263			yyerror("mismatched endif");
264		}
265		if (!IDS_PARENT_DISABLED) {
266			BEGIN(INITIAL);
267		}
268		ifdefstate = (ifdefstate/6) - 1;
269		yyline++;
270	}
271
272<IGNORED>\n		{
273		yyline++;
274	}
275
276<IGNORED>.	/* ignore */
277
278include[ \t]+{FILENAME}{RESTOFLINE}	{
279		yyline++;
280		if (getincludepath()) {
281			include(curinclpath, 0, 0, 1);
282		} else {
283			yyerror("bad include path-name");
284		}
285	}
286
287cinclude[ \t]+{FILENAME}{RESTOFLINE}	{
288		yyline++;
289		if (getincludepath()) {
290			include(curinclpath, 0, 1, 1);
291		} else {
292			yyerror("bad cinclude path-name");
293		}
294	}
295
296package[ \t]+{FILENAME}{RESTOFLINE}	{
297		yyline++;
298		if (!oktopackage) {
299			yyerror("package not allowed here");
300		} else if (getincludepath()) {
301			package(curinclpath);
302		} else {
303			yyerror("bad package path-name");
304		}
305	}
306
307{PATH}	{
308		yylval.str = intern(yytext);
309		return PATHNAME;
310	}
311
312{WORD}	{
313		yylval.str = intern(yytext);
314		return WORD;
315	}
316
317\"\" {
318		yylval.str = intern("");
319		return EMPTYSTRING;
320	}
321
322\"{QCHARS}	{
323		tok = input();  /* eat closing quote */
324		if (tok != '"') {
325			cfgerror("closing quote missing\n");
326			unput(tok);
327		}
328		yylval.str = intern(yytext + 1);
329		return QSTRING;
330	}
3310[0-7]*	{
332		yylval.num.fmt = 8;
333		yylval.num.val = strtoll(yytext, NULL, 8);
334		return NUMBER;
335	}
3360[xX][0-9a-fA-F]+ {
337		yylval.num.fmt = 16;
338		yylval.num.val = strtoull(yytext + 2, NULL, 16);
339		return NUMBER;
340	}
341[1-9][0-9]* {
342		yylval.num.fmt = 10;
343		yylval.num.val = strtoll(yytext, NULL, 10);
344		return NUMBER;
345	}
346\n[ \t] {
347		/*
348		 * Note: newline followed by whitespace is always a
349		 * continuation of the previous line, so do NOT
350		 * return a token in this case.
351		 */
352		yyline++;
353	}
354\n	{
355		yyline++;
356		return '\n';
357	}
358\00	{
359		/* Detect NUL characters in the config file and
360		 * error out.
361		 */
362		cfgerror("NUL character detected at line %i\n", yyline);
363	}
364#.*	{ /* ignored (comment) */; }
365[ \t]+	{ /* ignored (white space) */; }
366.	{ return yytext[0]; }
367<*><<EOF>> {
368		if (ifdefstate > (incl == NULL ? -1 : incl->in_ifdefstate)) {
369			yyerror("reached EOF while looking for endif");
370		}
371		if (incl == NULL)
372			return YY_NULL;
373		tok = endinclude();
374		if (tok)
375			return tok;
376		/* otherwise continue scanning */
377	}
378
379%%
380
381int interesting = 1;
382
383static int
384curdir_push(const char *fname)
385{
386	struct prefix *pf;
387	char *p, *d, *f;
388
389	/* Set up the initial "current directory" for include directives. */
390	d = dirname(f = estrdup(fname));
391	if (*d == '/')
392		p = estrdup(d);
393	else {
394		char *cwd, buf[PATH_MAX];
395
396		if ((cwd = getcwd(buf, sizeof(buf))) == NULL) {
397			free(f);
398			return (-1);
399		}
400		p = emalloc(strlen(cwd) + strlen(d) + 2);
401		sprintf(p, "%s/%s", cwd, d);
402	}
403	free(f);
404	pf = ecalloc(1, sizeof(*pf));
405	pf->pf_prefix = p;
406	SLIST_INSERT_HEAD(&curdirs, pf, pf_next);
407
408	return (0);
409}
410
411static void
412curdir_pop(void)
413{
414	struct prefix *pf;
415
416	pf = SLIST_FIRST(&curdirs);
417	SLIST_REMOVE_HEAD(&curdirs, pf_next);
418	if (SLIST_EMPTY(&curdirs))
419		panic("curdirs is empty");
420	/* LINTED cast away const (pf_prefix is malloc'd for curdirs) */
421	free((void *)__UNCONST(pf->pf_prefix));
422	free(pf);
423}
424
425/*
426 * Open the "main" file (conffile).
427 */
428int
429firstfile(const char *fname)
430{
431
432#if defined(__NetBSD__)
433	if ((yyin = fopen(fname, "rf")) == NULL)
434#else
435	if ((yyin = fopen(fname, "r")) == NULL)
436#endif
437		return (-1);
438
439	if (curdir_push(fname) == -1)
440		return (-1);
441
442	yyfile = conffile = fname;
443	yyline = 1;
444	return (0);
445}
446
447/*
448 * Add a "package" to the configuration.  This is essentially
449 * syntactic sugar around the sequence:
450 *
451 *	prefix ../some/directory
452 *	include "files.package"
453 *	prefix
454 */
455void
456package(const char *fname)
457{
458	char *fname1 = estrdup(fname);
459	char *fname2 = estrdup(fname);
460	char *dir = dirname(fname1);
461	char *file = basename(fname2);
462
463	/*
464	 * Push the prefix on to the prefix stack and process the include
465	 * file.  When we reach the end of the include file, inserting
466	 * the PREFIX token into the input stream will pop the prefix off
467	 * of the prefix stack.
468	 */
469	prefix_push(dir);
470	(void) include(file, PREFIX, 0, 1);
471
472	free(fname1);
473	free(fname2);
474}
475
476/*
477 * Open the named file for inclusion at the current point.  Returns 0 on
478 * success (file opened and previous state pushed), nonzero on failure
479 * (fopen failed, complaint made).  The `ateof' parameter controls the
480 * token to be inserted at the end of the include file (i.e. ENDFILE).
481 * If ateof == 0 then nothing is inserted.
482 */
483int
484include(const char *fname, int ateof, int conditional, int direct)
485{
486	FILE *fp;
487	struct incl *in;
488	char *s;
489	static int havedirs;
490	extern int vflag;
491
492	if (havedirs == 0) {
493		havedirs = 1;
494		setupdirs();
495	}
496
497	if (fname[0] == '/')
498		s = estrdup(fname);
499	else if (fname[0] == '.' && fname[1] == '/') {
500		struct prefix *pf = SLIST_FIRST(&curdirs);
501		s = emalloc(strlen(pf->pf_prefix) + strlen(fname));
502		sprintf(s, "%s/%s", pf->pf_prefix, fname + 2);
503	} else
504		s = sourcepath(fname);
505	if ((fp = fopen(s, "r")) == NULL) {
506		if (conditional == 0)
507			cfgerror("cannot open %s for reading: %s\n", s,
508			    strerror(errno));
509		else if (vflag)
510			cfgwarn("cannot open conditional include file %s: %s",
511			     s, strerror(errno));
512		free(s);
513		return (-1);
514	}
515	if (curdir_push(s) == -1) {
516		cfgerror("cannot record current working directory for %s\n", s);
517		fclose(fp);
518		free(s);
519		return (-1);
520	}
521	in = ecalloc(1, sizeof *in);
522	in->in_prev = incl;
523	in->in_buf = YY_CURRENT_BUFFER;
524	in->in_fname = yyfile;
525	in->in_lineno = yyline;
526	in->in_ateof = ateof;
527	in->in_interesting = interesting;
528	in->in_ifdefstate = ifdefstate;
529	interesting = direct & interesting;
530	if (interesting)
531		logconfig_include(fp, fname);
532	incl = in;
533	yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
534	yyfile = intern(s);
535	yyline = 1;
536	free(s);
537	return (0);
538}
539
540/*
541 * Extract the pathname from a include/cinclude/package into curinclpath
542 */
543static int
544getincludepath()
545{
546	const char *p = yytext;
547	ptrdiff_t len;
548	const char *e;
549
550	while (*p && isascii((unsigned int)*p) && !isspace((unsigned int)*p))
551		p++;
552	while (*p && isascii((unsigned int)*p) && isspace((unsigned int)*p))
553		p++;
554	if (!*p)
555		return 0;
556	if (*p == '"') {
557		p++;
558		e = strchr(p, '"');
559		if (!e) return 0;
560	} else {
561		e = p;
562		while (*e && isascii((unsigned int)*e)
563		    && !isspace((unsigned int)*e))
564			e++;
565	}
566
567	len = e-p;
568	if (len > (ptrdiff_t)sizeof(curinclpath)-1)
569		len = sizeof(curinclpath)-1;
570	strncpy(curinclpath, p, sizeof(curinclpath));
571	curinclpath[len] = '\0';
572
573	return 1;
574}
575
576/*
577 * Terminate the most recent inclusion.
578 */
579static int
580endinclude(void)
581{
582	struct incl *in;
583	int ateof;
584
585	curdir_pop();
586	if ((in = incl) == NULL)
587		panic("endinclude");
588	incl = in->in_prev;
589	lastfile = yyfile;
590	yy_delete_buffer(YY_CURRENT_BUFFER);
591	(void)fclose(yyin);
592	yy_switch_to_buffer(in->in_buf);
593	yyfile = in->in_fname;
594	yyline = in->in_lineno;
595	ateof  = in->in_ateof;
596	interesting = in->in_interesting;
597	free(in);
598
599	return (ateof);
600}
601
602/*
603 * Return the current line number.  If yacc has looked ahead and caused
604 * us to consume a newline, we have to subtract one.  yychar is yacc's
605 * token lookahead, so we can tell.
606 */
607int
608currentline(void)
609{
610	extern int yychar;
611
612	return (yyline - (yychar == '\n'));
613}
614
615static int
616getcurifdef(void)
617{
618	char *p = yytext, *q;
619
620	while (*p && isascii((unsigned int)*p) && !isspace((unsigned int)*p))
621		p++;
622	while (*p && isascii((unsigned int)*p) && isspace((unsigned int)*p))
623		p++;
624	q = p;
625	while (*q && isascii((unsigned int)*q) && !isspace((unsigned int)*q))
626		q++;
627	*q = '\0';
628
629	return ht_lookup(attrtab, intern(p)) != NULL;
630}
631