1 %{
2 /*        $NetBSD: scan.l,v 1.37 2025/01/07 14:21:11 joe 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/cdefs.h>
45 __RCSID("$NetBSD: scan.l,v 1.37 2025/01/07 14:21:11 joe Exp $");
46 
47 #include <sys/param.h>
48 #include <errno.h>
49 #include <libgen.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <stddef.h>
55 #include <ctype.h>
56 #include <util.h>
57 #undef ECHO
58 #include "defs.h"
59 #include "gram.h"
60 
61 int       yyline;
62 const char *yyfile;
63 const char *lastfile;
64 char curinclpath[PATH_MAX];
65 uint64_t ifdefstate;
66 int        ifdefshift = -1;
67 
68 /*
69  * The state is represented by 3 bits.
70  */
71 #define   IDS_ENABLED         1ll
72 #define   IDS_MATCH 2ll
73 #define   IDS_ELIF  4ll
74 #define   IDS_ELSE  8ll
75 
76 #define   IDS_BITS  0xf
77 #define   IDS_SHIFT 4
78 
79 #define   IDS_ISMATCH(st) (((st) & IDS_MATCH) != 0)
80 #define   IDS_ISENABLED(st) (((st) & IDS_ENABLED) != 0)
81 #define   IDS_PARENT_DISABLED \
82           (ifdefshift > 0 && !IDS_ISENABLED(ifdefstate >> IDS_SHIFT))
83 #define IDS_MAX_DEPTH         16 /* 64 / 4 */
84 
85 #ifdef    IDS_DEBUG
86 # define IDS_PRINT(s, st, x) \
87           do { \
88                     for (int i = 0; i < ifdefshift + 1; i++) \
89                               fprintf(stderr, " "); \
90                     printf("%s%s [%d,%d,%d] %#" PRIx64 "\n", x, # s, \
91                         IDS_PARENT_DISABLED, IDS_ISMATCH(st), getcurifdef(), \
92                         ifdefstate); \
93           } while (0)
94 #else
95 # define IDS_PRINT(s, st, x) ((void)0)
96 #endif
97 
98 #define   IDS_ENTER(s, st) \
99           IDS_PRINT(s, st, ">")
100 #define   IDS_EXIT(s, st) \
101           IDS_PRINT(s, st, "<")
102 
103 /*
104  * Data for returning to previous files from include files.
105  */
106 struct incl {
107           struct    incl *in_prev;      /* previous includes in effect, if any */
108           YY_BUFFER_STATE in_buf;       /* previous lex state */
109           struct where in_where;
110           int       in_ateof; /* token to insert at EOF */
111           int       in_interesting;     /* previous value for "interesting" */
112           uint64_t  in_ifdefstate;      /* conditional level */
113           int       in_ifdefshift;      /* conditional level */
114 };
115 static struct incl *incl;
116 static int endinclude(void);
117 static int getincludepath(void);
118 static int getcurifdef(void);
119 
120 SLIST_HEAD(, prefix)          curdirs;  /* curdir stack */
121 
122 %}
123 
124 %option  noyywrap nounput noinput
125 
126 PATH      [A-Za-z_0-9]*[./][-A-Za-z_0-9./]*
127 QCHARS    \"(\\.|[^\\"])*\"
128 WORD      [A-Za-z_][-A-Za-z_0-9]*
129 FILENAME  ({PATH}|{QCHARS})
130 RESTOFLINE          [ \t]*(#[^\n]*)?\n
131 WS        ^[ \t]*
132 
133 %x        IGNORED
134 
135 %%
136                     /* Local variables for yylex() */
137                     int tok;
138 
139 and                 return AND;
140 at                  return AT;
141 attach              return ATTACH;
142 block               return BLOCK;
143 build               return BUILD;
144 char                return CHAR;
145 compile-with        return COMPILE_WITH;
146 config              return CONFIG;
147 deffs               return DEFFS;
148 define              return DEFINE;
149 defflag             return DEFFLAG;
150 defopt              return DEFOPT;
151 defparam  return DEFPARAM;
152 defpseudo return DEFPSEUDO;
153 defpseudodev        return DEFPSEUDODEV;
154 devclass  return DEVCLASS;
155 device              return DEVICE;
156 device-major        return DEVICE_MAJOR;
157 dumps               return DUMPS;
158 file                return XFILE;
159 file-system         return FILE_SYSTEM;
160 flags               return FLAGS;
161 ident               return IDENT;
162 ioconf              return IOCONF;
163 linkzero  return LINKZERO;
164 machine             return XMACHINE;
165 major               return MAJOR;
166 makeoptions         return MAKEOPTIONS;
167 mkflagvar return MKFLAGVAR;
168 maxpartitions       return MAXPARTITIONS;
169 maxusers  return MAXUSERS;
170 minor               return MINOR;
171 needs-count         return NEEDS_COUNT;
172 needs-flag          return NEEDS_FLAG;
173 no                  return NO;
174 -no                 return CNO;
175 object              return XOBJECT;
176 obsolete  return OBSOLETE;
177 on                  return ON;
178 options             return OPTIONS;
179 prefix              return PREFIX;
180 buildprefix         return BUILDPREFIX;
181 pseudo-device       return PSEUDO_DEVICE;
182 pseudo-root         return PSEUDO_ROOT;
183 root                return ROOT;
184 select              return SELECT;
185 single              return SINGLE;
186 source              return SOURCE;
187 type                return TYPE;
188 vector              return VECTOR;
189 version   return VERSION;
190 with                return WITH;
191 
192 \+=                 return PLUSEQ;
193 :=                  return COLONEQ;
194 
195 <*>{WS}ifdef[ \t]+{WORD}{RESTOFLINE} {
196                     ifdefstate <<= IDS_SHIFT;
197                     if (++ifdefshift >= IDS_MAX_DEPTH) {
198                               yyerror("too many levels of conditional");
199                     }
200                     IDS_ENTER(ifdef, 0);
201                     if (IDS_PARENT_DISABLED || !getcurifdef()) {
202                               ifdefstate &= (uint64_t)~IDS_ENABLED;
203                               BEGIN(IGNORED);
204                     } else {
205                               ifdefstate |= IDS_MATCH|IDS_ENABLED;
206                               BEGIN(INITIAL);
207                     }
208                     IDS_EXIT(ifdef, 0);
209                     yyline++;
210           }
211 
212 <*>{WS}ifndef[ \t]+{WORD}{RESTOFLINE} {
213                     ifdefstate <<= IDS_SHIFT;
214                     if (++ifdefshift >= IDS_MAX_DEPTH) {
215                               yyerror("too many levels of conditional");
216                     }
217                     IDS_ENTER(ifndef, 0);
218                     if (IDS_PARENT_DISABLED || getcurifdef()) {
219                               ifdefstate &= (uint64_t)~IDS_ENABLED;
220                               BEGIN(IGNORED);
221                     } else {
222                               ifdefstate |= IDS_MATCH|IDS_ENABLED;
223                               BEGIN(INITIAL);
224                     }
225                     IDS_EXIT(ifndef, 0);
226                     yyline++;
227           }
228 
229 
230 <*>{WS}elifdef[ \t]+{WORD}{RESTOFLINE} {
231                     int st = ifdefstate & IDS_BITS;
232                     IDS_ENTER(elifdef, st);
233                     if (ifdefshift == -1 || (st & IDS_ELSE) != 0) {
234                               yyerror("mismatched elifdef");
235                     }
236                     if (IDS_PARENT_DISABLED || IDS_ISMATCH(st) || !getcurifdef()) {
237                               ifdefstate &= (uint64_t)~IDS_ENABLED;
238                               BEGIN(IGNORED);
239                     } else {
240                               ifdefstate |= IDS_MATCH|IDS_ENABLED;
241                               BEGIN(INITIAL);
242                     }
243                     ifdefstate |= IDS_ELIF;
244                     IDS_EXIT(elifdef, st);
245                     yyline++;
246           }
247 
248 <*>{WS}elifndef[ \t]+{WORD}{RESTOFLINE} {
249                     int st = ifdefstate & IDS_BITS;
250                     IDS_ENTER(elifndef, st);
251                     if (ifdefshift == -1 || (st & IDS_ELSE) != 0) {
252                               yyerror("mismatched elifndef");
253                     }
254                     if (IDS_PARENT_DISABLED || IDS_ISMATCH(st) || getcurifdef()) {
255                               ifdefstate &= (uint64_t)~IDS_ENABLED;
256                               BEGIN(IGNORED);
257                     } else {
258                               ifdefstate |= IDS_MATCH|IDS_ENABLED;
259                               BEGIN(INITIAL);
260                     }
261                     ifdefstate |= IDS_ELIF;
262                     IDS_EXIT(elifndef, st);
263                     yyline++;
264           }
265 
266 <*>{WS}else{RESTOFLINE} {
267                     int st = ifdefstate & IDS_BITS;
268                     IDS_ENTER(else, st);
269                     if (ifdefshift == -1 || (st & IDS_ELSE) != 0) {
270                               yyerror("mismatched else");
271                     }
272                     if (IDS_PARENT_DISABLED || IDS_ISMATCH(st)) {
273                               ifdefstate &= (uint64_t)~IDS_ENABLED;
274                               BEGIN(IGNORED);
275                     } else {
276                               ifdefstate |= IDS_MATCH|IDS_ENABLED;
277                               BEGIN(INITIAL);
278                     }
279                     ifdefstate |= IDS_ELSE;
280                     IDS_ENTER(else, st);
281                     yyline++;
282           }
283 
284 <*>{WS}endif{RESTOFLINE} {
285                     IDS_ENTER(endif, 0);
286                     if (ifdefshift == -1) {
287                               yyerror("mismatched endif");
288                     }
289                     if (!IDS_PARENT_DISABLED) {
290                               BEGIN(INITIAL);
291                     }
292                     IDS_EXIT(endif, 0);
293                     ifdefshift--;
294                     ifdefstate >>= IDS_SHIFT;
295                     yyline++;
296           }
297 
298 <IGNORED>\n                   {
299                     yyline++;
300           }
301 
302 <IGNORED>.          /* ignore */
303 
304 include[ \t]+{FILENAME}{RESTOFLINE}     {
305                     yyline++;
306                     if (getincludepath()) {
307                               include(curinclpath, 0, 0, 1);
308                     } else {
309                               yyerror("bad include path-name");
310                     }
311           }
312 
313 cinclude[ \t]+{FILENAME}{RESTOFLINE}    {
314                     yyline++;
315                     if (getincludepath()) {
316                               include(curinclpath, 0, 1, 1);
317                     } else {
318                               yyerror("bad cinclude path-name");
319                     }
320           }
321 
322 package[ \t]+{FILENAME}{RESTOFLINE}     {
323                     yyline++;
324                     if (!oktopackage) {
325                               yyerror("package not allowed here");
326                     } else if (getincludepath()) {
327                               package(curinclpath);
328                     } else {
329                               yyerror("bad package path-name");
330                     }
331           }
332 
333 {PATH}    {
334                     yylval.str = intern(yytext);
335                     return PATHNAME;
336           }
337 
338 {WORD}    {
339                     yylval.str = intern(yytext);
340                     return WORD;
341           }
342 
343 \"\" {
344                     yylval.str = intern("");
345                     return EMPTYSTRING;
346           }
347 
348 {QCHARS}  {
349                     size_t l = strlen(yytext);
350                     if (l > 1 && yytext[l - 1] == '"')
351                               yytext[l - 1] = '\0';
352 
353                     yylval.str = intern(yytext + 1);
354                     return QSTRING;
355           }
356 0[0-7]*   {
357                     yylval.num.fmt = 8;
358                     yylval.num.val = strtoll(yytext, NULL, 8);
359                     return NUMBER;
360           }
361 0[xX][0-9a-fA-F]+ {
362                     yylval.num.fmt = 16;
363                     yylval.num.val = (long long)strtoull(yytext + 2, NULL, 16);
364                     return NUMBER;
365           }
366 [1-9][0-9]* {
367                     yylval.num.fmt = 10;
368                     yylval.num.val = strtoll(yytext, NULL, 10);
369                     return NUMBER;
370           }
371 \n[ \t] {
372                     /*
373                      * Note: newline followed by whitespace is always a
374                      * continuation of the previous line, so do NOT
375                      * return a token in this case.
376                      */
377                     yyline++;
378           }
379 \n        {
380                     yyline++;
381                     return '\n';
382           }
383 \00       {
384                     /* Detect NUL characters in the config file and
385                      * error out.
386                      */
387                     cfgerror("NUL character detected at line %i", yyline);
388           }
389 #.*       { /* ignored (comment) */; }
390 [ \t]+    { /* ignored (white space) */; }
391 .         { return yytext[0]; }
392 <*><<EOF>> {
393                     if (ifdefshift > (incl == NULL ? -1 : incl->in_ifdefshift)) {
394                               yyerror("reached EOF while looking for endif");
395                     }
396                     if (incl == NULL)
397                               return YY_NULL;
398                     tok = endinclude();
399                     if (tok)
400                               return tok;
401                     /* otherwise continue scanning */
402           }
403 
404 %%
405 
406 int interesting = 1;
407 
408 static int
409 curdir_push(const char *fname)
410 {
411           struct prefix *pf;
412           char *p, *d, *f;
413 
414           /* Set up the initial "current directory" for include directives. */
415           d = dirname(f = estrdup(fname));
416           if (*d == '/')
417                     p = estrdup(d);
418           else {
419                     char *cwd, buf[PATH_MAX];
420 
421                     if ((cwd = getcwd(buf, sizeof(buf))) == NULL) {
422                               free(f);
423                               return (-1);
424                     }
425                     easprintf(&p, "%s/%s", cwd, d);
426           }
427           free(f);
428           pf = ecalloc(1, sizeof(*pf));
429           pf->pf_prefix = p;
430           SLIST_INSERT_HEAD(&curdirs, pf, pf_next);
431 
432           return (0);
433 }
434 
435 static void
436 curdir_pop(void)
437 {
438           struct prefix *pf;
439 
440           pf = SLIST_FIRST(&curdirs);
441           SLIST_REMOVE_HEAD(&curdirs, pf_next);
442           if (SLIST_EMPTY(&curdirs))
443                     panic("curdirs is empty");
444           /* LINTED cast away const (pf_prefix is malloc'd for curdirs) */
445           free((void *)__UNCONST(pf->pf_prefix));
446           free(pf);
447 }
448 
449 /*
450  * Open the "main" file (conffile).
451  */
452 int
453 firstfile(const char *fname)
454 {
455 
456 #if defined(__NetBSD__)
457           if ((yyin = fopen(fname, "rf")) == NULL)
458 #else
459           if ((yyin = fopen(fname, "r")) == NULL)
460 #endif
461                     return (-1);
462 
463           if (curdir_push(fname) == -1)
464                     return (-1);
465 
466           yyfile = conffile = fname;
467           yyline = 1;
468           return (0);
469 }
470 
471 /*
472  * Add a "package" to the configuration.  This is essentially
473  * syntactic sugar around the sequence:
474  *
475  *        prefix ../some/directory
476  *        include "files.package"
477  *        prefix
478  */
479 void
480 package(const char *fname)
481 {
482           char *fname1 = estrdup(fname);
483           char *fname2 = estrdup(fname);
484           char *dir = dirname(fname1);
485           char *file = basename(fname2);
486 
487           /*
488            * Push the prefix on to the prefix stack and process the include
489            * file.  When we reach the end of the include file, inserting
490            * the PREFIX token into the input stream will pop the prefix off
491            * of the prefix stack.
492            */
493           prefix_push(dir);
494           (void) include(file, PREFIX, 0, 1);
495 
496           free(fname1);
497           free(fname2);
498 }
499 
500 int includedepth;
501 
502 /*
503  * Open the named file for inclusion at the current point.  Returns 0 on
504  * success (file opened and previous state pushed), nonzero on failure
505  * (fopen failed, complaint made).  The `ateof' parameter controls the
506  * token to be inserted at the end of the include file (i.e. ENDFILE).
507  * If ateof == 0 then nothing is inserted.
508  */
509 int
510 include(const char *fname, int ateof, int conditional, int direct)
511 {
512           FILE *fp;
513           struct incl *in;
514           char *s;
515           static int havedirs;
516           extern int vflag;
517 
518           if (havedirs == 0) {
519                     havedirs = 1;
520                     setupdirs();
521           }
522 
523           if (fname[0] == '/')
524                     s = estrdup(fname);
525           else if (fname[0] == '.' && fname[1] == '/') {
526                     struct prefix *pf = SLIST_FIRST(&curdirs);
527                     easprintf(&s, "%s/%s", pf->pf_prefix, fname + 2);
528           } else
529                     s = sourcepath(fname);
530           if ((fp = fopen(s, "r")) == NULL) {
531                     if (conditional == 0)
532                               cfgerror("cannot open %s for reading: %s", s,
533                                   strerror(errno));
534                     else if (vflag)
535                               cfgwarn("cannot open conditional include file %s: %s",
536                                    s, strerror(errno));
537                     free(s);
538                     return (-1);
539           }
540           if (curdir_push(s) == -1) {
541                     cfgerror("cannot record current working directory for %s", s);
542                     fclose(fp);
543                     free(s);
544                     return (-1);
545           }
546           in = ecalloc(1, sizeof *in);
547           in->in_prev = incl;
548           in->in_buf = YY_CURRENT_BUFFER;
549           in->in_where.w_srcfile = yyfile;
550           in->in_where.w_srcline = (u_short)yyline;
551           in->in_ateof = ateof;
552           in->in_interesting = interesting;
553           in->in_ifdefstate = ifdefstate;
554           in->in_ifdefshift = ifdefshift;
555           interesting = direct & interesting;
556           if (interesting)
557                     logconfig_include(fp, fname);
558           incl = in;
559           CFGDBG(1, "include `%s'", fname);
560           yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
561           yyfile = intern(s);
562           yyline = 1;
563           free(s);
564           includedepth++;
565           return (0);
566 }
567 
568 /*
569  * Extract the pathname from a include/cinclude/package into curinclpath
570  */
571 static int
572 getincludepath(void)
573 {
574           const char *p = yytext;
575           ptrdiff_t len;
576           const char *e;
577 
578           while (*p && isascii((unsigned char)*p) && !isspace((unsigned char)*p))
579                     p++;
580           while (*p && isascii((unsigned char)*p) && isspace((unsigned char)*p))
581                     p++;
582           if (!*p)
583                     return 0;
584           if (*p == '"') {
585                     p++;
586                     e = strchr(p, '"');
587                     if (!e) return 0;
588           } else {
589                     e = p;
590                     while (*e && isascii((unsigned char)*e)
591                         && !isspace((unsigned char)*e))
592                               e++;
593           }
594 
595           len = e-p;
596           if (len > (ptrdiff_t)sizeof(curinclpath)-1)
597                     len = sizeof(curinclpath)-1;
598           strncpy(curinclpath, p, sizeof(curinclpath));
599           curinclpath[len] = '\0';
600 
601           return 1;
602 }
603 
604 /*
605  * Terminate the most recent inclusion.
606  */
607 static int
608 endinclude(void)
609 {
610           struct incl *in;
611           int ateof;
612 
613           curdir_pop();
614           if ((in = incl) == NULL)
615                     panic("endinclude");
616           incl = in->in_prev;
617           lastfile = yyfile;
618           yy_delete_buffer(YY_CURRENT_BUFFER);
619           (void)fclose(yyin);
620           yy_switch_to_buffer(in->in_buf);
621           yyfile = in->in_where.w_srcfile;
622           yyline = in->in_where.w_srcline;
623           ateof  = in->in_ateof;
624           interesting = in->in_interesting;
625           ifdefstate = in->in_ifdefstate;
626           ifdefshift = in->in_ifdefshift;
627           free(in);
628 
629           includedepth--;
630 
631           return (ateof);
632 }
633 
634 /*
635  * Return the current line number.  If yacc has looked ahead and caused
636  * us to consume a newline, we have to subtract one.  yychar is yacc's
637  * token lookahead, so we can tell.
638  */
639 u_short
640 currentline(void)
641 {
642           extern int yychar;
643 
644           return (u_short)(yyline - (yychar == '\n'));
645 }
646 
647 static int
648 getcurifdef(void)
649 {
650           char *p = yytext, *q;
651 
652           while (*p && isascii((unsigned char)*p) && !isspace((unsigned char)*p))
653                     p++;
654           while (*p && isascii((unsigned char)*p) && isspace((unsigned char)*p))
655                     p++;
656           q = p;
657           while (*q && isascii((unsigned char)*q) && !isspace((unsigned char)*q))
658                     q++;
659           *q = '\0';
660 
661           return ht_lookup(attrtab, intern(p)) != NULL;
662 }
663