xref: /illumos-gate/usr/src/cmd/fm/eversholt/common/esclex.c (revision 1a90c98d7539778aeb0a1d20f735b66aaba17fca)
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