Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / usr.bin / config / scan.l
blobfcbca2215e27ce21f22b04c3aeb8521a2fa1ae90
1 %{
2 /*      $NetBSD: scan.l,v 1.12 2009/04/11 12:41:10 lukem Exp $  */
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  */
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"
58 int     yyline;
59 const char *yyfile;
60 const char *lastfile;
61 char curinclpath[PATH_MAX];
62 int ifdefstate = -1;
63 int 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:
69   0  -> matched ifdef
70   1  -> unmatched ifdef
71   2  -> matched elifdef
72   3  -> unmatched elifdef
73   4  -> matched else
74   5  -> unmatched else
76   Upon "ifdef", add one and multiply by 6.
77   Upon "endif", divide by 6, remove 1.
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
88   in each case, if parent & 1 == 1, MISMATCH
92  * Data for returning to previous files from include files.
93  */
94 struct 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 */
103 static struct incl *incl;
104 static int endinclude(void);
105 static int getincludepath(void);
106 static int getcurifdef(void);
111 %option  noyywrap
113 PATH    [A-Za-z_0-9]*[./][-A-Za-z_0-9./]*
114 QCHARS  ([^"\n]|\\\")+
115 WORD    [A-Za-z_][-A-Za-z_0-9]*
116 FILENAME        ({PATH}|\"{QCHARS}\")
117 RESTOFLINE      [ \t]*(#[^\n]*)?\n
119 %x      IGNORED
122                 /* Local variables for yylex() */
123                 int tok;
125 and             return AND;
126 at              return AT;
127 attach          return ATTACH;
128 block           return BLOCK;
129 build           return BUILD;
130 char            return CHAR;
131 compile-with    return COMPILE_WITH;
132 config          return CONFIG;
133 deffs           return DEFFS;
134 define          return DEFINE;
135 defflag         return DEFFLAG;
136 defopt          return DEFOPT;
137 defparam        return DEFPARAM;
138 defpseudo       return DEFPSEUDO;
139 defpseudodev    return DEFPSEUDODEV;
140 devclass        return DEVCLASS;
141 device          return DEVICE;
142 device-major    return DEVICE_MAJOR;
143 dumps           return DUMPS;
144 file            return XFILE;
145 file-system     return FILE_SYSTEM;
146 flags           return FLAGS;
147 ident           return IDENT;
148 machine         return XMACHINE;
149 major           return MAJOR;
150 makeoptions     return MAKEOPTIONS;
151 maxpartitions   return MAXPARTITIONS;
152 maxusers        return MAXUSERS;
153 minor           return MINOR;
154 needs-count     return NEEDS_COUNT;
155 needs-flag      return NEEDS_FLAG;
156 no              return NO;
157 object          return XOBJECT;
158 obsolete        return OBSOLETE;
159 on              return ON;
160 options         return OPTIONS;
161 prefix          return PREFIX;
162 pseudo-device   return PSEUDO_DEVICE;
163 root            return ROOT;
164 source          return SOURCE;
165 type            return TYPE;
166 version         return VERSION;
167 with            return WITH;
169 \+=             return PLUSEQ;
170 :=              return COLONEQ;
172 <*>ifdef[ \t]+{WORD}{RESTOFLINE} {
173                 ifdefstate = (ifdefstate + 1) * 6;
174                 if (ifdefstate >= IDS_MAX_DEPTH) {
175                         yyerror("too many levels of conditional");
176                 }
177                 if (!IDS_PARENT_DISABLED && getcurifdef()) {
178                         BEGIN(INITIAL);
179                 } else {
180                         ifdefstate++;
181                         BEGIN(IGNORED);
182                 }
183                 yyline++;
184         }
186 <*>ifndef[ \t]+{WORD}{RESTOFLINE} {
187                 ifdefstate = (ifdefstate + 1) * 6;
188                 if (ifdefstate >= IDS_MAX_DEPTH) {
189                         yyerror("too many levels of conditional");
190                 }
191                 if (!IDS_PARENT_DISABLED && !getcurifdef()) {
192                         BEGIN(INITIAL);
193                 } else {
194                         ifdefstate++;
195                         BEGIN(IGNORED);
196                 }
197                 yyline++;
198         }
201 <*>elifdef[ \t]+{WORD}{RESTOFLINE} {
202                 st = ifdefstate % 6;
203                 if (ifdefstate < 0 || st > 3) {
204                         yyerror("mismatched elifdef");
205                 }
206                 if (IDS_PARENT_DISABLED ||
207                     st != 1 || !getcurifdef()) {
208                         if (st == 2 || st == 3) {
209                                 ifdefstate += 3 - st;
210                         } else {
211                                 ifdefstate += 1 - st;
212                         }
213                         BEGIN(IGNORED);
214                 } else {
215                         ifdefstate++;
216                         BEGIN(INITIAL);
217                 }
218                 yyline++;
219         }
221 <*>elifndef[ \t]+{WORD}{RESTOFLINE} {
222                 st = ifdefstate % 6;
223                 if (ifdefstate < 0 || st > 3) {
224                         yyerror("mismatched elifndef");
225                 }
226                 if (IDS_PARENT_DISABLED ||
227                     st != 1 || getcurifdef()) {
228                         if (st == 2 || st == 3) {
229                                 ifdefstate += 3 - st;
230                         } else {
231                                 ifdefstate += 1 - st;
232                         }
233                         BEGIN(IGNORED);
234                 } else {
235                         ifdefstate++;
236                         BEGIN(INITIAL);
237                 }
238                 yyline++;
239         }
241 <*>else{RESTOFLINE} {
242                 st = ifdefstate % 6;
243                 if (ifdefstate < 0 || st > 3) {
244                         yyerror("mismatched else");
245                 }
246                 if (!IDS_PARENT_DISABLED && (st == 1)) {
247                         ifdefstate += 3;
248                         BEGIN(INITIAL);
249                 } else {
250                         ifdefstate += 5 - st;
251                         BEGIN(IGNORED);
252                 }
253                 yyline++;
254         }
256 <*>endif{RESTOFLINE} {
257                 if (ifdefstate < 0) {
258                         yyerror("mismatched endif");
259                 }
260                 if (!IDS_PARENT_DISABLED) {
261                         BEGIN(INITIAL);
262                 }
263                 ifdefstate = (ifdefstate/6) - 1;
264                 yyline++;
265         }
267 <IGNORED>\n             {
268                 yyline++;
269         }
271 <IGNORED>.      /* ignore */
273 include[ \t]+{FILENAME}{RESTOFLINE}     {
274                 yyline++;
275                 if (getincludepath()) {
276                         include(curinclpath, 0, 0, 1);
277                 } else {
278                         yyerror("bad include path-name");
279                 }
280         }
282 cinclude[ \t]+{FILENAME}{RESTOFLINE}    {
283                 yyline++;
284                 if (getincludepath()) {
285                         include(curinclpath, 0, 1, 1);
286                 } else {
287                         yyerror("bad cinclude path-name");
288                 }
289         }
291 package[ \t]+{FILENAME}{RESTOFLINE}     {
292                 yyline++;
293                 if (!oktopackage) {
294                         yyerror("package not allowed here");
295                 } else if (getincludepath()) {
296                         package(curinclpath);
297                 } else {
298                         yyerror("bad package path-name");
299                 }
300         }
302 {PATH}  {
303                 yylval.str = intern(yytext);
304                 return PATHNAME;
305         }
307 {WORD}  {
308                 yylval.str = intern(yytext);
309                 return WORD;
310         }
312 \"\" {
313                 yylval.str = intern("");
314                 return EMPTYSTRING;
315         }
317 \"{QCHARS}      {
318                 tok = input();  /* eat closing quote */
319                 if (tok != '"') {
320                         cfgerror("closing quote missing\n");
321                         unput(tok);
322                 }
323                 yylval.str = intern(yytext + 1);
324                 return QSTRING;
325         }
326 0[0-7]* {
327                 yylval.num.fmt = 8;
328                 yylval.num.val = strtoll(yytext, NULL, 8);
329                 return NUMBER;
330         }
331 0[xX][0-9a-fA-F]+ {
332                 yylval.num.fmt = 16;
333                 yylval.num.val = strtoull(yytext + 2, NULL, 16);
334                 return NUMBER;
335         }
336 [1-9][0-9]* {
337                 yylval.num.fmt = 10;
338                 yylval.num.val = strtoll(yytext, NULL, 10);
339                 return NUMBER;
340         }
341 \n[ \t] {
342                 /*
343                  * Note: newline followed by whitespace is always a
344                  * continuation of the previous line, so do NOT
345                  * return a token in this case.
346                  */
347                 yyline++;
348         }
349 \n      {
350                 yyline++;
351                 return '\n';
352         }
353 \00     {
354                 /* Detect NUL characters in the config file and
355                  * error out.
356                  */
357                 cfgerror("NUL character detected at line %i\n", yyline);
358         }
359 #.*     { /* ignored (comment) */; }
360 [ \t]+  { /* ignored (white space) */; }
361 .       { return yytext[0]; }
362 <*><<EOF>> {
363                 if (ifdefstate > (incl == NULL ? -1 : incl->in_ifdefstate)) {
364                         yyerror("reached EOF while looking for endif");
365                 }
366                 if (incl == NULL)
367                         return YY_NULL;
368                 tok = endinclude();
369                 if (tok)
370                         return tok;
371                 /* otherwise continue scanning */
372         }
376 int interesting = 1;
378 static int
379 curdir_push(const char *fname)
381         struct prefix *pf;
382         char *p, *d, *f;
384         /* Set up the initial "current directory" for include directives. */
385         d = dirname(f = estrdup(fname));
386         if (*d == '/')
387                 p = estrdup(d);
388         else {
389                 char *cwd, buf[PATH_MAX];
391                 if ((cwd = getcwd(buf, sizeof(buf))) == NULL) {
392                         free(f);
393                         return (-1);
394                 }
395                 p = emalloc(strlen(cwd) + strlen(d) + 2);
396                 sprintf(p, "%s/%s", cwd, d);
397         }
398         free(f);
399         pf = ecalloc(1, sizeof(*pf));
400         pf->pf_prefix = p;
401         SLIST_INSERT_HEAD(&curdirs, pf, pf_next);
403         return (0);
406 static void
407 curdir_pop(void)
409         struct prefix *pf;
411         pf = SLIST_FIRST(&curdirs);
412         SLIST_REMOVE_HEAD(&curdirs, pf_next);
413         if (SLIST_EMPTY(&curdirs))
414                 panic("curdirs is empty");
415         /* LINTED cast away const (pf_prefix is malloc'd for curdirs) */
416         free((void *)__UNCONST(pf->pf_prefix));
417         free(pf);
421  * Open the "main" file (conffile).
422  */
424 firstfile(const char *fname)
427 #if defined(__NetBSD__)
428         if ((yyin = fopen(fname, "rf")) == NULL)
429 #else
430         if ((yyin = fopen(fname, "r")) == NULL)
431 #endif
432                 return (-1);
434         if (curdir_push(fname) == -1)
435                 return (-1);
437         yyfile = conffile = fname;
438         yyline = 1;
439         return (0);
443  * Add a "package" to the configuration.  This is essentially
444  * syntactic sugar around the sequence:
446  *      prefix ../some/directory
447  *      include "files.package"
448  *      prefix
449  */
450 void
451 package(const char *fname)
453         char *fname1 = estrdup(fname);
454         char *fname2 = estrdup(fname);
455         char *dir = dirname(fname1);
456         char *file = basename(fname2);
458         /*
459          * Push the prefix on to the prefix stack and process the include
460          * file.  When we reach the end of the include file, inserting
461          * the PREFIX token into the input stream will pop the prefix off
462          * of the prefix stack.
463          */
464         prefix_push(dir);
465         (void) include(file, PREFIX, 0, 1);
467         free(fname1);
468         free(fname2);
472  * Open the named file for inclusion at the current point.  Returns 0 on
473  * success (file opened and previous state pushed), nonzero on failure
474  * (fopen failed, complaint made).  The `ateof' parameter controls the
475  * token to be inserted at the end of the include file (i.e. ENDFILE).
476  * If ateof == 0 then nothing is inserted.
477  */
479 include(const char *fname, int ateof, int conditional, int direct)
481         FILE *fp;
482         struct incl *in;
483         char *s;
484         static int havedirs;
485         extern int vflag;
487         if (havedirs == 0) {
488                 havedirs = 1;
489                 setupdirs();
490         }
492         if (fname[0] == '/')
493                 s = estrdup(fname);
494         else if (fname[0] == '.' && fname[1] == '/') {
495                 struct prefix *pf = SLIST_FIRST(&curdirs);
496                 s = emalloc(strlen(pf->pf_prefix) + strlen(fname));
497                 sprintf(s, "%s/%s", pf->pf_prefix, fname + 2);
498         } else
499                 s = sourcepath(fname);
500         if ((fp = fopen(s, "r")) == NULL) {
501                 if (conditional == 0)
502                         cfgerror("cannot open %s for reading: %s\n", s,
503                             strerror(errno));
504                 else if (vflag)
505                         cfgwarn("cannot open conditional include file %s: %s",
506                              s, strerror(errno));
507                 free(s);
508                 return (-1);
509         }
510         if (curdir_push(s) == -1) {
511                 cfgerror("cannot record current working directory for %s\n", s);
512                 fclose(fp);
513                 free(s);
514                 return (-1);
515         }
516         in = ecalloc(1, sizeof *in);
517         in->in_prev = incl;
518         in->in_buf = YY_CURRENT_BUFFER;
519         in->in_fname = yyfile;
520         in->in_lineno = yyline;
521         in->in_ateof = ateof;
522         in->in_interesting = interesting;
523         in->in_ifdefstate = ifdefstate;
524         interesting = direct & interesting;
525         if (interesting)
526                 logconfig_include(fp, fname);
527         incl = in;
528         yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
529         yyfile = intern(s);
530         yyline = 1;
531         free(s);
532         return (0);
536  * Extract the pathname from a include/cinclude/package into curinclpath
537  */
538 static int
539 getincludepath()
541         const char *p = yytext;
542         ptrdiff_t len;
543         const char *e;
545         while (*p && isascii((unsigned int)*p) && !isspace((unsigned int)*p))
546                 p++;
547         while (*p && isascii((unsigned int)*p) && isspace((unsigned int)*p))
548                 p++;
549         if (!*p)
550                 return 0;
551         if (*p == '"') {
552                 p++;
553                 e = strchr(p, '"');
554                 if (!e) return 0;
555         } else {
556                 e = p;
557                 while (*e && isascii((unsigned int)*e)
558                     && !isspace((unsigned int)*e))
559                         e++;
560         }
562         len = e-p;
563         if (len > (ptrdiff_t)sizeof(curinclpath)-1)
564                 len = sizeof(curinclpath)-1;
565         strncpy(curinclpath, p, sizeof(curinclpath));
566         curinclpath[len] = '\0';
568         return 1;
572  * Terminate the most recent inclusion.
573  */
574 static int
575 endinclude(void)
577         struct incl *in;
578         int ateof;
580         curdir_pop();
581         if ((in = incl) == NULL)
582                 panic("endinclude");
583         incl = in->in_prev;
584         lastfile = yyfile;
585         yy_delete_buffer(YY_CURRENT_BUFFER);
586         (void)fclose(yyin);
587         yy_switch_to_buffer(in->in_buf);
588         yyfile = in->in_fname;
589         yyline = in->in_lineno;
590         ateof  = in->in_ateof;
591         interesting = in->in_interesting;
592         free(in);
594         return (ateof);
598  * Return the current line number.  If yacc has looked ahead and caused
599  * us to consume a newline, we have to subtract one.  yychar is yacc's
600  * token lookahead, so we can tell.
601  */
603 currentline(void)
605         extern int yychar;
607         return (yyline - (yychar == '\n'));
610 static int
611 getcurifdef(void)
613         char *p = yytext, *q;
615         while (*p && isascii((unsigned int)*p) && !isspace((unsigned int)*p))
616                 p++;
617         while (*p && isascii((unsigned int)*p) && isspace((unsigned int)*p))
618                 p++;
619         q = p;
620         while (*q && isascii((unsigned int)*q) && !isspace((unsigned int)*q))
621                 q++;
622         *q = '\0';
624         return ht_lookup(attrtab, intern(p)) != NULL;