1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * esclex.c -- lexer for esc
26 *
27 * this module provides lexical analysis and error handling routine
28 * expected by the yacc-generated parser (i.e. yylex() and yyerror()).
29 * it also does lots of tracking of things like filenames, line numbers,
30 * and what tokens are seen on a line up to the point where a syntax error
31 * was found. this module also arranges for the input source files to
32 * be run through cpp.
33 */
34
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <time.h>
41 #include <errno.h>
42 #include "out.h"
43 #include "alloc.h"
44 #include "stats.h"
45 #include "stable.h"
46 #include "lut.h"
47 #include "literals.h"
48 #include "tree.h"
49 #include "esclex.h"
50 #include "eftread.h"
51 #include "check.h"
52 #include "y.tab.h"
53
54 struct lut *Dicts;
55 struct lut *Ident;
56 struct lut *Timesuffixlut;
57 int Pragma_trust_ereports;
58 int Pragma_new_errors_only;
59
60 /* ridiculously long token buffer -- disallow any token longer than this */
61 #define MAXTOK 8192
62 static char Tok[MAXTOK];
63
64 /* some misc stats we keep on the lexer & parser */
65 static struct stats *Tokcount;
66 static struct stats *Lexelapse;
67 struct stats *Filecount;
68 struct filestats {
69 struct filestats *next;
70 struct stats *stats;
71 struct stats *idstats;
72 } *Fstats;
73
74 static int Errcount;
75
76 /* input file state */
77 static char **Files;
78 static const char *Fileopened;
79 static FILE *Fp;
80 static int Line;
81 static const char *File;
82 static const char *Cpp = "/usr/lib/cpp";
83 #ifdef ESC
84 static const char *Cppargs;
85 static const char *Cppstdargs = "-undef -Y.";
86 #endif /* ESC */
87
88 /* for debugging */
89 static int Lexecho; /* echo tokens as we read them */
90
91 /* forward declarations of our internal routines */
92 static int record(int tok, const char *s);
93 static void dumpline(int flags);
94 static void doident();
95 static void dopragma(const char *tok);
96
97 /*
98 * table of reserved words. this table is only used by lex_init()
99 * to intialize the Rwords lookup table.
100 */
101 static const struct {
102 const char *word;
103 const int val;
104 } Rwords[] = {
105 { "asru", ASRU },
106 { "count", COUNT },
107 { "div", DIV },
108 { "engine", ENGINE },
109 { "event", EVENT },
110 { "fru", FRU },
111 { "if", IF },
112 { "mask", MASK },
113 { "prop", PROP },
114 { "config", CONFIG },
115 /*
116 * PATHFUNC indicates functions that operate only on paths
117 * and quotes
118 */
119 { "is_connected", PATHFUNC },
120 { "is_under", PATHFUNC },
121 { "is_on", PATHFUNC },
122 { "is_present", PATHFUNC },
123 { "is_type", PATHFUNC },
124 { "has_fault", PATHFUNC },
125 { "confprop", PATHFUNC },
126 { "confprop_defined", PATHFUNC },
127 };
128
129 /*
130 * Rwordslut is a lookup table of reserved words. lhs is the word
131 * (in the string table) and the rhs is the token value returned
132 * by the yylex() for that word.
133 */
134 static struct lut *Rwordslut;
135
136 static const struct {
137 const char *suffix;
138 const unsigned long long nsec;
139 } Timesuffix[] = {
140 { "nanosecond", 1ULL },
141 { "nanoseconds", 1ULL },
142 { "nsec", 1ULL },
143 { "nsecs", 1ULL },
144 { "ns", 1ULL },
145 { "microsecond", 1000ULL },
146 { "microseconds", 1000ULL },
147 { "usec", 1000ULL },
148 { "usecs", 1000ULL },
149 { "us", 1000ULL },
150 { "millisecond", 1000000ULL },
151 { "milliseconds", 1000000ULL },
152 { "msec", 1000000ULL },
153 { "msecs", 1000000ULL },
154 { "ms", 1000000ULL },
155 { "second", 1000000000ULL },
156 { "seconds", 1000000000ULL },
157 { "s", 1000000000ULL },
158 { "minute", 1000000000ULL * 60 },
159 { "minutes", 1000000000ULL * 60 },
160 { "min", 1000000000ULL * 60 },
161 { "mins", 1000000000ULL * 60 },
162 { "m", 1000000000ULL * 60 },
163 { "hour", 1000000000ULL * 60 * 60 },
164 { "hours", 1000000000ULL * 60 * 60 },
165 { "hr", 1000000000ULL * 60 * 60 },
166 { "hrs", 1000000000ULL * 60 * 60 },
167 { "h", 1000000000ULL * 60 * 60 },
168 { "day", 1000000000ULL * 60 * 60 * 24 },
169 { "days", 1000000000ULL * 60 * 60 * 24 },
170 { "d", 1000000000ULL * 60 * 60 * 24 },
171 { "week", 1000000000ULL * 60 * 60 * 24 * 7 },
172 { "weeks", 1000000000ULL * 60 * 60 * 24 * 7 },
173 { "wk", 1000000000ULL * 60 * 60 * 24 * 7 },
174 { "wks", 1000000000ULL * 60 * 60 * 24 * 7 },
175 { "month", 1000000000ULL * 60 * 60 * 24 * 30 },
176 { "months", 1000000000ULL * 60 * 60 * 24 * 30 },
177 { "year", 1000000000ULL * 60 * 60 * 24 * 365 },
178 { "years", 1000000000ULL * 60 * 60 * 24 * 365 },
179 { "yr", 1000000000ULL * 60 * 60 * 24 * 365 },
180 { "yrs", 1000000000ULL * 60 * 60 * 24 * 365 },
181 };
182
183 /*
184 * some wrappers around the general lut functions to provide type checking...
185 */
186
187 static struct lut *
lex_s2i_lut_add(struct lut * root,const char * s,intptr_t i)188 lex_s2i_lut_add(struct lut *root, const char *s, intptr_t i)
189 {
190 return (lut_add(root, (void *)s, (void *)i, NULL));
191 }
192
193 static int
lex_s2i_lut_lookup(struct lut * root,const char * s)194 lex_s2i_lut_lookup(struct lut *root, const char *s)
195 {
196 return ((intptr_t)lut_lookup(root, (void *)s, NULL));
197 }
198
199 static struct lut *
lex_s2ullp_lut_add(struct lut * root,const char * s,const unsigned long long * ullp)200 lex_s2ullp_lut_add(struct lut *root, const char *s,
201 const unsigned long long *ullp)
202 {
203 return (lut_add(root, (void *)s, (void *)ullp, NULL));
204 }
205
206 const unsigned long long *
lex_s2ullp_lut_lookup(struct lut * root,const char * s)207 lex_s2ullp_lut_lookup(struct lut *root, const char *s)
208 {
209 return ((unsigned long long *)lut_lookup(root, (void *)s, NULL));
210 }
211
212 /*
213 * lex_init -- initialize the lexer with appropriate filenames & debug flags
214 */
215
216 /*ARGSUSED*/
217 void
lex_init(char ** av,const char * cppargs,int lexecho)218 lex_init(char **av, const char *cppargs, int lexecho)
219 {
220 int i;
221 #ifdef ESC
222 const char *ptr;
223 #endif /* ESC */
224
225 Lexecho = lexecho;
226 Tokcount = stats_new_counter("lex.tokens", "total tokens in", 1);
227 Filecount = stats_new_counter("lex.files", "total files read", 0);
228 Lexelapse = stats_new_elapse("lex.time", "elapsed lex/parse time", 1);
229
230 #ifdef ESC
231 Cppargs = cppargs;
232
233 /* allow user to tell us where cpp is if it is some weird place */
234 if (ptr = getenv("_ESC_CPP"))
235 Cpp = ptr;
236
237 /* and in case it takes some special stdargs */
238 if (ptr = getenv("_ESC_CPP_STDARGS"))
239 Cppstdargs = ptr;
240
241 /* verify we can find cpp */
242 if (access(Cpp, X_OK) < 0) {
243 Cpp = "/usr/lib/cpp";
244 if (access(Cpp, X_OK) < 0)
245 out(O_DIE, "can't locate cpp");
246 }
247 #endif /* ESC */
248
249 Files = av;
250
251 /* verify we can find all the input files */
252 while (*av) {
253 if (strlen(*av) >= MAXTOK - strlen(Cpp) - 3)
254 out(O_DIE, "filename too long: %.100s...", *av);
255 if (access(*av, R_OK) < 0)
256 out(O_DIE|O_SYS, "%s", *av);
257 av++;
258 stats_counter_bump(Filecount);
259 }
260
261 /* put reserved words into the string table & a lookup table */
262 for (i = 0; i < sizeof (Rwords) / sizeof (*Rwords); i++)
263 Rwordslut = lex_s2i_lut_add(Rwordslut,
264 stable(Rwords[i].word), Rwords[i].val);
265
266 /* initialize table of timeval suffixes */
267 for (i = 0; i < sizeof (Timesuffix) / sizeof (*Timesuffix); i++) {
268 Timesuffixlut = lex_s2ullp_lut_add(Timesuffixlut,
269 stable(Timesuffix[i].suffix), &Timesuffix[i].nsec);
270 }
271
272 /* record start time */
273 stats_elapse_start(Lexelapse);
274 }
275
276 void
closefile(void)277 closefile(void)
278 {
279 if (Fp != NULL) {
280 #ifdef ESC
281 if (pclose(Fp) > 0)
282 out(O_DIE, "cpp errors while reading \"%s\", "
283 "bailing out.", Fileopened);
284 #else
285 (void) fclose(Fp);
286 #endif /* ESC */
287 }
288 Fp = NULL;
289 }
290
291 /*
292 * yylex -- the lexer, called yylex() because that's what yacc wants
293 */
294
295 int
yylex(void)296 yylex(void)
297 {
298 int c;
299 int nextc;
300 char *ptr = Tok;
301 char *eptr = &Tok[MAXTOK];
302 const char *cptr;
303 int startline;
304 int val;
305 static int bol = 1; /* true if we're at beginning of line */
306
307 for (;;) {
308 while (Fp == NULL) {
309 char ibuf[80];
310
311 if (*Files == NULL)
312 return (record(EOF, NULL));
313 Fileopened = stable(*Files++);
314 #ifdef ESC
315 sprintf(Tok, "%s %s %s %s",
316 Cpp, Cppstdargs, Cppargs, Fileopened);
317 if ((Fp = popen(Tok, "r")) == NULL)
318 out(O_DIE|O_SYS, "%s", Tok);
319 #else
320 Fp = eftread_fopen(Fileopened, ibuf, sizeof (ibuf));
321 #endif /* ESC */
322 Line = 1;
323 bol = 1;
324
325 /* add name to stats for visibility */
326 if (Fp != NULL) {
327 static int fnum;
328 char nbuf[100];
329 struct filestats *nfs = MALLOC(sizeof (*nfs));
330
331 (void) sprintf(nbuf, "lex.file%d", fnum);
332 nfs->stats = stats_new_string(nbuf, "", 0);
333 stats_string_set(nfs->stats, Fileopened);
334
335 if (ibuf[0] != '\0') {
336 (void) sprintf(nbuf, "lex.file%d-ident",
337 fnum);
338 nfs->idstats =
339 stats_new_string(nbuf, "", 0);
340 stats_string_set(nfs->idstats, ibuf);
341 } else {
342 nfs->idstats = NULL;
343 }
344
345 nfs->next = Fstats;
346 Fstats = nfs;
347 fnum++;
348 }
349 }
350
351 switch (c = getc(Fp)) {
352 case '#':
353 /* enforce that we're at beginning of line */
354 if (!bol)
355 return (record(c, NULL));
356
357 while ((c = getc(Fp)) != EOF &&
358 (c == ' ' || c == '\t'))
359 ;
360 if (!isdigit(c)) {
361 /*
362 * three cases here:
363 * #pragma
364 * #ident
365 * #something-we-don't-understand
366 * anything we don't expect we just ignore.
367 */
368 *ptr++ = c;
369 while ((c = getc(Fp)) != EOF && isalnum(c))
370 if (ptr < eptr - 1)
371 *ptr++ = c;
372 *ptr++ = '\0';
373 if (strcmp(Tok, "pragma") == 0) {
374 /* skip white space */
375 while ((c = getc(Fp)) != EOF &&
376 (c == ' ' || c == '\t'))
377 ;
378
379 if (c == EOF || c == '\n')
380 outfl(O_DIE, File, Line,
381 "bad #pragma");
382
383 /* pull in next token */
384 ptr = Tok;
385 *ptr++ = c;
386 while ((c = getc(Fp)) != EOF &&
387 !isspace(c))
388 if (ptr < eptr - 1)
389 *ptr++ = c;
390 *ptr++ = '\0';
391 (void) ungetc(c, Fp);
392
393 dopragma(Tok);
394 } else if (strcmp(Tok, "ident") == 0)
395 doident();
396 } else {
397 /* handle file & line info from cpp */
398 Line = 0;
399 do {
400 if (!isdigit(c))
401 break;
402 Line = Line * 10 + c - '0';
403 } while ((c = getc(Fp)) != EOF);
404 Line--; /* newline will increment it */
405 while (c != EOF && isspace(c))
406 c = getc(Fp);
407 if (c != '"')
408 outfl(O_DIE, File, Line,
409 "bad # statement (file name)");
410 while ((c = getc(Fp)) != EOF && c != '"')
411 if (ptr < eptr - 1)
412 *ptr++ = c;
413 *ptr++ = '\0';
414 if (c != '"')
415 outfl(O_DIE, File, Line,
416 "bad # statement (quotes)");
417 File = stable(Tok);
418 }
419 /* skip the rest of the cpp line */
420 while ((c = getc(Fp)) != EOF && c != '\n' && c != '\r')
421 ;
422 if (c == EOF)
423 return (record(c, NULL));
424 else
425 (void) ungetc(c, Fp);
426 ptr = Tok;
427 break;
428
429 case EOF:
430 closefile();
431 continue;
432
433 case '\n':
434 Line++;
435 bol = 1;
436 break;
437
438 case '\r':
439 case ' ':
440 case '\t':
441 bol = 0;
442 break;
443
444 case '/':
445 bol = 0;
446 /* comment handling */
447 if ((nextc = getc(Fp)) == EOF)
448 outfl(O_DIE, File, Line, "unexpected EOF");
449 else if (nextc == '*') {
450 startline = Line;
451 while ((c = getc(Fp)) != EOF) {
452 if (c == '\n')
453 Line++;
454 else if (c == '*' &&
455 (((c = getc(Fp)) == EOF) ||
456 (c == '/')))
457 break;
458 }
459 if (c == EOF) {
460 outfl(O_DIE, File, Line,
461 "end of comment not seen "
462 "(started on line %d)",
463 startline);
464 }
465 } else {
466 /* wasn't a comment, return the '/' token */
467 (void) ungetc(nextc, Fp);
468 return (record(c, NULL));
469 }
470 break;
471
472 case '"': {
473 int prevc;
474
475 bol = 0;
476 prevc = '\0';
477 /* quoted string handling */
478 startline = Line;
479 for (;;) {
480 c = getc(Fp);
481 if (c == EOF)
482 outfl(O_DIE, File, Line,
483 "end of string not seen "
484 "(started on line %d)",
485 startline);
486 else if (c == '\n')
487 Line++;
488 else if (c == '"' && prevc != '\\')
489 break;
490 else if (ptr < eptr)
491 *ptr++ = c;
492 prevc = c;
493 }
494 if (ptr >= eptr)
495 out(O_DIE, File, Line, "string too long");
496 *ptr++ = '\0';
497 return (record(QUOTE, stable(Tok)));
498 }
499 case '&':
500 bol = 0;
501 /* && */
502 if ((nextc = getc(Fp)) == '&')
503 return (record(AND, NULL));
504 else {
505 (void) ungetc(nextc, Fp);
506 return (record(c, NULL));
507 }
508 /*NOTREACHED*/
509 break;
510
511 case '|':
512 bol = 0;
513 /* || */
514 if ((nextc = getc(Fp)) == '|')
515 return (record(OR, NULL));
516 else {
517 (void) ungetc(nextc, Fp);
518 return (record(c, NULL));
519 }
520 /*NOTREACHED*/
521 break;
522
523 case '!':
524 bol = 0;
525 /* ! or != */
526 if ((nextc = getc(Fp)) == '=')
527 return (record(NE, NULL));
528 else {
529 (void) ungetc(nextc, Fp);
530 return (record(c, NULL));
531 }
532 /*NOTREACHED*/
533 break;
534
535 case '=':
536 bol = 0;
537 /* == */
538 if ((nextc = getc(Fp)) == '=')
539 return (record(EQ, NULL));
540 else {
541 (void) ungetc(nextc, Fp);
542 return (record(c, NULL));
543 }
544 /*NOTREACHED*/
545 break;
546
547 case '-':
548 bol = 0;
549 /* -> */
550 if ((nextc = getc(Fp)) == '>')
551 return (record(ARROW, stable(Tok)));
552 else {
553 (void) ungetc(nextc, Fp);
554 return (record(c, NULL));
555 }
556 /*NOTREACHED*/
557 break;
558
559 case '<':
560 bol = 0;
561 if ((nextc = getc(Fp)) == '=')
562 /* <= */
563 return (record(LE, NULL));
564 else if (nextc == '<')
565 /* << */
566 return (record(LSHIFT, NULL));
567 else {
568 (void) ungetc(nextc, Fp);
569 return (record(c, NULL));
570 }
571 /*NOTREACHED*/
572 break;
573
574 case '>':
575 bol = 0;
576 if ((nextc = getc(Fp)) == '=')
577 /* >= */
578 return (record(GE, NULL));
579 else if (nextc == '>')
580 /* >> */
581 return (record(RSHIFT, NULL));
582 else {
583 (void) ungetc(nextc, Fp);
584 return (record(c, NULL));
585 }
586 /*NOTREACHED*/
587 break;
588
589 default:
590 bol = 0;
591 if (isdigit(c)) {
592 int base;
593
594 /* collect rest of number */
595 if (c == '0') {
596 *ptr++ = c;
597 if ((c = getc(Fp)) == EOF) {
598 *ptr++ = '\0';
599 return (record(NUMBER,
600 stable(Tok)));
601 } else if (c == 'x' || c == 'X') {
602 *ptr++ = c;
603 base = 16;
604 } else {
605 (void) ungetc(c, Fp);
606 base = 8;
607 }
608 } else {
609 *ptr++ = c;
610 base = 10;
611 }
612 while ((c = getc(Fp)) != EOF) {
613 if (ptr >= eptr)
614 out(O_DIE, File, Line,
615 "number too long");
616
617 switch (base) {
618 case 16:
619 if (c >= 'a' && c <= 'f' ||
620 c >= 'A' && c <= 'F') {
621 *ptr++ = c;
622 continue;
623 }
624 /*FALLTHRU*/
625 case 10:
626 if (c >= '8' && c <= '9') {
627 *ptr++ = c;
628 continue;
629 }
630 /*FALLTHRU*/
631 case 8:
632 if (c >= '0' && c <= '7') {
633 *ptr++ = c;
634 continue;
635 }
636 /* not valid for this base */
637 *ptr++ = '\0';
638 (void) ungetc(c, Fp);
639 return (record(NUMBER,
640 stable(Tok)));
641 }
642 }
643 *ptr++ = '\0';
644 return (record(NUMBER, stable(Tok)));
645 } else if (isalpha(c)) {
646 /* collect identifier */
647 *ptr++ = c;
648 for (;;) {
649 c = getc(Fp);
650 if ((isalnum(c) || c == '_') &&
651 ptr < eptr)
652 *ptr++ = c;
653 else {
654 (void) ungetc(c, Fp);
655 break;
656 }
657 }
658 if (ptr >= eptr)
659 out(O_DIE, File, Line,
660 "identifier too long");
661 *ptr++ = '\0';
662 cptr = stable(Tok);
663 if (val = lex_s2i_lut_lookup(Rwordslut, cptr)) {
664 return (record(val, cptr));
665 }
666 return (record(ID, cptr));
667 } else
668 return (record(c, NULL));
669 }
670 /*NOTREACHED*/
671 }
672 }
673
674 /*
675 * the record()/dumpline() routines are used to track & report
676 * the list of tokens seen on a given line. this is used in two ways.
677 * first, syntax errors found by the parser are reported by us (via
678 * yyerror()) and we tack on the tokens processed so far on the current
679 * line to help indicate exactly where the error is. second, if "lexecho"
680 * debugging is turned on, these routines provide it.
681 */
682 #define MAXRECORD 1000
683 static int Recordedline;
684 static struct {
685 int tok;
686 const char *s;
687 } Recorded[MAXRECORD];
688 static int Recordnext;
689
690 static int
record(int tok,const char * s)691 record(int tok, const char *s)
692 {
693 stats_counter_bump(Tokcount);
694 if (Line != Recordedline) {
695 /* starting new line, dump out the previous line */
696 if (Lexecho && Recordedline) {
697 outfl(O_NONL, File, Recordedline, "lex: ");
698 dumpline(O_OK);
699 }
700 Recordedline = Line;
701 Recordnext = 0;
702 }
703 if (Recordnext >= MAXRECORD)
704 outfl(O_DIE, File, Line, "line too long, bailing out");
705 Recorded[Recordnext].tok = tok;
706 Recorded[Recordnext++].s = s;
707
708 yylval.tok.s = s;
709 yylval.tok.file = File;
710 yylval.tok.line = Line;
711 return (tok);
712 }
713
714 /*ARGSUSED*/
715 static void
dumpline(int flags)716 dumpline(int flags)
717 {
718 int i;
719
720 for (i = 0; i < Recordnext; i++)
721 if (Recorded[i].s && Recorded[i].tok != ARROW)
722 switch (Recorded[i].tok) {
723 case T_QUOTE:
724 out(flags|O_NONL, " \"%s\"",
725 Recorded[i].s);
726 break;
727
728 default:
729 out(flags|O_NONL, " %s",
730 Recorded[i].s);
731 break;
732 }
733 else
734 switch (Recorded[i].tok) {
735 case EOF:
736 out(flags|O_NONL, " EOF");
737 break;
738 case ARROW:
739 out(flags|O_NONL, " ->%s",
740 Recorded[i].s);
741 break;
742 case EQ:
743 out(flags|O_NONL, " ==");
744 break;
745 case NE:
746 out(flags|O_NONL, " !=");
747 break;
748 case OR:
749 out(flags|O_NONL, " ||");
750 break;
751 case AND:
752 out(flags|O_NONL, " &&");
753 break;
754 case LE:
755 out(flags|O_NONL, " <=");
756 break;
757 case GE:
758 out(flags|O_NONL, " >=");
759 break;
760 case LSHIFT:
761 out(flags|O_NONL, " <<");
762 break;
763 case RSHIFT:
764 out(flags|O_NONL, " >>");
765 break;
766 default:
767 if (isprint(Recorded[i].tok))
768 out(flags|O_NONL, " %c",
769 Recorded[i].tok);
770 else
771 out(flags|O_NONL, " '\\%03o'",
772 Recorded[i].tok);
773 break;
774 }
775 out(flags, NULL);
776 }
777
778 /*
779 * yyerror -- report a pareser error, called yyerror because yacc wants it
780 */
781
782 int
yyerror(const char * s)783 yyerror(const char *s)
784 {
785 Errcount++;
786 outfl(O_ERR|O_NONL, File, Line, "%s, tokens: ", s);
787 dumpline(O_ERR);
788 return (0);
789 }
790
791 /*
792 * doident -- handle "#pragma ident" directives
793 */
794 static void
doident()795 doident()
796 {
797 int c;
798 char *ptr = Tok;
799 char *eptr = &Tok[MAXTOK];
800
801 /* skip white space and quotes */
802 while ((c = getc(Fp)) != EOF &&
803 (c == ' ' || c == '\t' || c == '"'))
804 ;
805
806 if (c == EOF || c == '\n')
807 outfl(O_DIE, File, Line, "bad ident");
808
809 /* pull in next token */
810 ptr = Tok;
811 *ptr++ = c;
812 while ((c = getc(Fp)) != EOF && c != '"' && c != '\n')
813 if (ptr < eptr - 1)
814 *ptr++ = c;
815 *ptr++ = '\0';
816 if (c != '\n') {
817 /* skip to end of line (including close quote, if any) */
818 while ((c = getc(Fp)) != EOF && c != '\n')
819 ;
820 }
821 (void) ungetc(c, Fp);
822 Ident = lut_add(Ident, (void *)stable(Tok), (void *)0, NULL);
823
824 outfl(O_VERB, File, Line, "pragma set: ident \"%s\"", Tok);
825 }
826
827 /*
828 * dodictionary -- handle "#pragma dictionary" directives
829 */
830 static void
dodictionary()831 dodictionary()
832 {
833 int c;
834 char *ptr = Tok;
835 char *eptr = &Tok[MAXTOK];
836
837 /* skip white space and quotes */
838 while ((c = getc(Fp)) != EOF &&
839 (c == ' ' || c == '\t' || c == '"'))
840 ;
841
842 if (c == EOF || c == '\n')
843 outfl(O_DIE, File, Line, "bad dictionary");
844
845 /* pull in next token */
846 ptr = Tok;
847 *ptr++ = c;
848 while ((c = getc(Fp)) != EOF && c != '"' && c != '\n')
849 if (ptr < eptr - 1)
850 *ptr++ = c;
851 *ptr++ = '\0';
852 if (c != '\n') {
853 /* skip to end of line (including close quote, if any) */
854 while ((c = getc(Fp)) != EOF && c != '\n')
855 ;
856 }
857 (void) ungetc(c, Fp);
858 Dicts = lut_add(Dicts, (void *)stable(Tok), (void *)0, NULL);
859
860 outfl(O_VERB, File, Line, "pragma set: dictionary \"%s\"", Tok);
861 }
862
863 /*
864 * doallow_cycles -- handle "#pragma allow_cycles" directives
865 */
866 static void
doallow_cycles()867 doallow_cycles()
868 {
869 int c;
870 char *ptr = Tok;
871 char *eptr = &Tok[MAXTOK];
872 unsigned long long newlevel;
873
874 /*
875 * by default the compiler does not allow cycles or loops
876 * in propagations. when cycles are encountered, the
877 * compiler prints out an error message.
878 *
879 * "#pragma allow_cycles" and
880 * "#pragma allow_cycles 0"
881 * allow cycles, but any such cycle will produce a warning
882 * message.
883 *
884 * "#pragma allow_cycles N"
885 * with N > 0 will allow cycles and not produce any
886 * warning messages.
887 */
888
889 /* skip white space and quotes */
890 while ((c = getc(Fp)) != EOF &&
891 (c == ' ' || c == '\t' || c == '"'))
892 ;
893
894 if (c == EOF || c == '\n')
895 newlevel = 0ULL;
896 else {
897
898 /* pull in next token */
899 ptr = Tok;
900 *ptr++ = c;
901 while ((c = getc(Fp)) != EOF && c != '"' && c != '\n')
902 if (ptr < eptr - 1)
903 *ptr++ = c;
904 *ptr++ = '\0';
905 if (c != '\n') {
906 /* skip to end of line */
907 while ((c = getc(Fp)) != EOF && c != '\n')
908 ;
909 }
910 newlevel = strtoll(Tok, NULL, 0);
911 }
912 (void) ungetc(c, Fp);
913
914 (void) check_cycle_level(newlevel);
915 outfl(O_VERB, File, Line,
916 "pragma set: allow_cycles (%s)",
917 newlevel ? "no warnings" : "with warnings");
918 }
919
920 /*
921 * dopragma -- handle #pragma directives
922 */
923 static void
dopragma(const char * tok)924 dopragma(const char *tok)
925 {
926 if (strcmp(tok, "ident") == 0)
927 doident();
928 else if (strcmp(tok, "dictionary") == 0)
929 dodictionary();
930 else if (strcmp(tok, "new_errors_only") == 0) {
931 if (Pragma_new_errors_only++ == 0)
932 outfl(O_VERB, File, Line,
933 "pragma set: new_errors_only");
934 } else if (strcmp(tok, "trust_ereports") == 0) {
935 if (Pragma_trust_ereports++ == 0)
936 outfl(O_VERB, File, Line,
937 "pragma set: trust_ereports");
938 } else if (strcmp(tok, "allow_cycles") == 0)
939 doallow_cycles();
940 else
941 outfl(O_VERB, File, Line,
942 "unknown pragma ignored: \"%s\"", tok);
943 }
944
945 /*
946 * lex_fini -- finalize the lexer
947 */
948
949 int
lex_fini(void)950 lex_fini(void)
951 {
952 stats_elapse_stop(Lexelapse);
953 closefile();
954 if (Lexecho) {
955 outfl(O_OK, File, Line, "lex: ");
956 dumpline(O_OK);
957 }
958 return (Errcount);
959 }
960
961 void
lex_free(void)962 lex_free(void)
963 {
964 struct filestats *nfstats = Fstats;
965
966 /*
967 * Free up memory consumed by the lexer
968 */
969 stats_delete(Tokcount);
970 stats_delete(Filecount);
971 stats_delete(Lexelapse);
972 while (nfstats != NULL) {
973 Fstats = nfstats->next;
974 stats_delete(nfstats->stats);
975 if (nfstats->idstats != NULL)
976 stats_delete(nfstats->idstats);
977 FREE(nfstats);
978 nfstats = Fstats;
979 }
980 lut_free(Timesuffixlut, NULL, NULL);
981 lut_free(Rwordslut, NULL, NULL);
982 lut_free(Ident, NULL, NULL);
983 lut_free(Dicts, NULL, NULL);
984 }
985