1/*	Id: token.c,v 1.64 2011/08/30 20:12:21 plunky Exp 	*/
2/*	$NetBSD$	*/
3
4/*
5 * Copyright (c) 2004,2009 Anders Magnusson. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28/*
29 * Tokenizer for the C preprocessor.
30 * There are three main routines:
31 *	- fastscan() loops over the input stream searching for magic
32 *		characters that may require actions.
33 *	- sloscan() tokenize the input stream and returns tokens.
34 *		It may recurse into itself during expansion.
35 *	- yylex() returns something from the input stream that
36 *		is suitable for yacc.
37 *
38 *	Other functions of common use:
39 *	- inpch() returns a raw character from the current input stream.
40 *	- inch() is like inpch but \\n and trigraphs are expanded.
41 *	- unch() pushes back a character to the input stream.
42 */
43
44#include "config.h"
45
46#include <stdlib.h>
47#include <string.h>
48#include <ctype.h>
49#ifdef HAVE_UNISTD_H
50#include <unistd.h>
51#endif
52#include <fcntl.h>
53#include <errno.h>
54
55#include "compat.h"
56#include "cpp.h"
57#include "y.tab.h"
58
59static void cvtdig(int rad);
60static int charcon(usch *);
61static void elsestmt(void);
62static void ifdefstmt(void);
63static void ifndefstmt(void);
64static void endifstmt(void);
65static void ifstmt(void);
66static void cpperror(void);
67static void pragmastmt(void);
68static void undefstmt(void);
69static void cppwarning(void);
70static void elifstmt(void);
71static void badop(const char *);
72static int chktg(void);
73static int inpch(void);
74
75extern int yyget_lineno (void);
76extern void yyset_lineno (int);
77
78static int inch(void);
79
80int inif;
81
82#define	PUTCH(ch) if (!flslvl) putch(ch)
83/* protection against recursion in #include */
84#define MAX_INCLEVEL	100
85static int inclevel;
86
87/* get next character unaltered */
88#define	NXTCH() (ifiles->curptr < ifiles->maxread ? *ifiles->curptr++ : inpch())
89
90usch yytext[CPPBUF];
91
92char spechr[256] = {
93	0,	0,	0,	0,	C_SPEC,	C_SPEC,	0,	0,
94	0,	C_WSNL,	C_SPEC|C_WSNL,	0,
95	0,	C_WSNL,	0,	0,
96	0,	0,	0,	0,	0,	0,	0,	0,
97	0,	0,	0,	0,	0,	0,	0,	0,
98
99	C_WSNL,	C_2,	C_SPEC,	0,	0,	0,	C_2,	C_SPEC,
100	0,	0,	0,	C_2,	0,	C_2,	0,	C_SPEC|C_2,
101	C_I,	C_I,	C_I,	C_I,	C_I,	C_I,	C_I,	C_I,
102	C_I,	C_I,	0,	0,	C_2,	C_2,	C_2,	C_SPEC,
103
104	0,	C_I,	C_I,	C_I,	C_I,	C_I|C_EP, C_I,	C_I,
105	C_I,	C_I,	C_I,	C_I,	C_I,	C_I,	C_I,	C_I,
106	C_I|C_EP, C_I,	C_I,	C_I,	C_I,	C_I,	C_I,	C_I,
107	C_I,	C_I,	C_I,	0,	C_SPEC,	0,	0,	C_I,
108
109	0,	C_I,	C_I,	C_I,	C_I,	C_I|C_EP, C_I,	C_I,
110	C_I,	C_I,	C_I,	C_I,	C_I,	C_I,	C_I,	C_I,
111	C_I|C_EP, C_I,	C_I,	C_I,	C_I,	C_I,	C_I,	C_I,
112	C_I,	C_I,	C_I,	0,	C_2,	0,	0,	0,
113
114};
115
116/*
117 * No-replacement array.  If a macro is found and exists in this array
118 * then no replacement shall occur.  This is a stack.
119 */
120struct symtab *norep[RECMAX];	/* Symbol table index table */
121int norepptr = 1;			/* Top of index table */
122unsigned short bptr[RECMAX];	/* currently active noexpand macro stack */
123int bidx;			/* Top of bptr stack */
124
125static void
126unch(int c)
127{
128
129	--ifiles->curptr;
130	if (ifiles->curptr < ifiles->bbuf)
131		error("pushback buffer full");
132	*ifiles->curptr = (usch)c;
133}
134
135static int
136eatcmnt(void)
137{
138	int ch;
139
140	if (Cflag) { PUTCH('/'); PUTCH('*'); }
141	for (;;) {
142		ch = inch();
143		if (ch == '\n') {
144			ifiles->lineno++;
145			PUTCH('\n');
146		}
147		if (ch == -1)
148			return -1;
149		if (ch == '*') {
150			ch = inch();
151			if (ch == '/') {
152				if (Cflag) {
153					PUTCH('*');
154					PUTCH('/');
155				} else
156					PUTCH(' ');
157				break;
158			}
159			unch(ch);
160			ch = '*';
161		}
162		if (Cflag) PUTCH(ch);
163	}
164	return 0;
165}
166
167/*
168 * Scan quickly the input file searching for:
169 *	- '#' directives
170 *	- keywords (if not flslvl)
171 *	- comments
172 *
173 *	Handle strings, numbers and trigraphs with care.
174 *	Only data from pp files are scanned here, never any rescans.
175 *	TODO: Only print out strings before calling other functions.
176 */
177static void
178fastscan(void)
179{
180	struct symtab *nl;
181	int ch, i = 0;
182	int nnl = 0;
183	usch *cp;
184
185	goto run;
186	for (;;) {
187		ch = NXTCH();
188xloop:		if (ch == -1)
189			return;
190#ifdef PCC_DEBUG
191		if (dflag>1)
192			printf("fastscan ch %d (%c)\n", ch, ch > 31 ? ch : '@');
193#endif
194		if ((spechr[ch] & C_SPEC) == 0) {
195			PUTCH(ch);
196			continue;
197		}
198		switch (ch) {
199		case EBLOCK:
200		case WARN:
201		case CONC:
202			error("bad char passed");
203			break;
204
205		case '/': /* Comments */
206			if ((ch = inch()) == '/') {
207cppcmt:				if (Cflag) { PUTCH(ch); } else { PUTCH(' '); }
208				do {
209					if (Cflag) PUTCH(ch);
210					ch = inch();
211				} while (ch != -1 && ch != '\n');
212				goto xloop;
213			} else if (ch == '*') {
214				if (eatcmnt())
215					return;
216			} else {
217				PUTCH('/');
218				goto xloop;
219			}
220			break;
221
222		case '?':  /* trigraphs */
223			if ((ch = chktg()))
224				goto xloop;
225			PUTCH('?');
226			break;
227
228		case '\\':
229			if ((ch = NXTCH()) == '\n') {
230				ifiles->lineno++;
231				continue;
232			} else {
233				PUTCH('\\');
234			}
235			goto xloop;
236
237		case '\n': /* newlines, for pp directives */
238			while (nnl > 0) { PUTCH('\n'); nnl--; }
239run2:			ifiles->lineno++;
240			do {
241				PUTCH(ch);
242run:				ch = NXTCH();
243				if (ch == '/') {
244					ch = NXTCH();
245					if (ch == '/')
246						goto cppcmt;
247					if (ch == '*') {
248						if (eatcmnt())
249							return;
250						goto run;
251					}
252					unch(ch);
253					ch = '/';
254				}
255			} while (ch == ' ' || ch == '\t');
256			if (ch == '\\') {
257				ch = NXTCH();
258				if (ch == '\n')
259					goto run2;
260				unch(ch);
261				ch = '\\';
262			}
263			if (ch == '#') {
264				ppdir();
265				continue;
266			} else if (ch == '%') {
267				ch = NXTCH();
268				if (ch == ':') {
269					ppdir();
270					continue;
271				} else {
272					unch(ch);
273					ch = '%';
274				}
275			} else if (ch == '?') {
276				if ((ch = chktg()) == '#') {
277					ppdir();
278					continue;
279				} else if (ch == 0)
280					ch = '?';
281			}
282			goto xloop;
283
284		case '\"': /* strings */
285str:			PUTCH(ch);
286			while ((ch = NXTCH()) != '\"') {
287				if (ch == '\n')
288					goto xloop;
289				if (ch == '\\') {
290					if ((ch = NXTCH()) != '\n') {
291						PUTCH('\\');
292						PUTCH(ch);
293					} else
294						nnl++;
295					continue;
296                                }
297				if (ch < 0)
298					return;
299				PUTCH(ch);
300			}
301			PUTCH(ch);
302			break;
303
304		case '.':  /* for pp-number */
305			PUTCH(ch);
306			ch = NXTCH();
307			if (ch < '0' || ch > '9')
308				goto xloop;
309			/* FALLTHROUGH */
310		case '0': case '1': case '2': case '3': case '4':
311		case '5': case '6': case '7': case '8': case '9':
312			do {
313				PUTCH(ch);
314nxt:				ch = NXTCH();
315				if (ch == '\\') {
316					ch = NXTCH();
317					if (ch == '\n') {
318						goto nxt;
319					} else {
320						unch(ch);
321						ch = '\\';
322					}
323				}
324				if (spechr[ch] & C_EP) {
325					PUTCH(ch);
326					ch = NXTCH();
327					if (ch == '-' || ch == '+')
328						continue;
329				}
330			} while ((spechr[ch] & C_ID) || (ch == '.'));
331			goto xloop;
332
333		case '\'': /* character literal */
334con:			PUTCH(ch);
335			if (tflag)
336				continue; /* character constants ignored */
337			while ((ch = NXTCH()) != '\'') {
338				if (ch == '\n')
339					goto xloop;
340				if (ch == '\\') {
341					if ((ch = NXTCH()) != '\n') {
342						PUTCH('\\');
343						PUTCH(ch);
344					} else
345						nnl++;
346					continue;
347				}
348				if (ch < 0)
349					return;
350				PUTCH(ch);
351			}
352			PUTCH(ch);
353			break;
354
355		case 'L':
356			ch = NXTCH();
357			if (ch == '\"') {
358				PUTCH('L');
359				goto str;
360			}
361			if (ch == '\'') {
362				PUTCH('L');
363				goto con;
364			}
365			unch(ch);
366			ch = 'L';
367			/* FALLTHROUGH */
368		default:
369			if ((spechr[ch] & C_ID) == 0)
370				error("fastscan");
371			if (flslvl) {
372				while (spechr[ch] & C_ID)
373					ch = NXTCH();
374				goto xloop;
375			}
376			i = 0;
377			do {
378				yytext[i++] = (usch)ch;
379				ch = NXTCH();
380				if (ch == '\\') {
381					ch = NXTCH();
382					if (ch != '\n') {
383						unch(ch);
384						ch = '\\';
385					} else {
386						putch('\n');
387						ifiles->lineno++;
388						ch = NXTCH();
389					}
390				}
391				if (ch < 0)
392					return;
393			} while (spechr[ch] & C_ID);
394
395			yytext[i] = 0;
396			unch(ch);
397
398			cp = stringbuf;
399			if ((nl = lookup((usch *)yytext, FIND)) && kfind(nl)) {
400				putstr(stringbuf);
401			} else
402				putstr((usch *)yytext);
403			stringbuf = cp;
404
405			break;
406		}
407	}
408}
409
410int
411sloscan(void)
412{
413	int ch;
414	int yyp;
415
416zagain:
417	yyp = 0;
418 	ch = inch();
419	yytext[yyp++] = (usch)ch;
420	switch (ch) {
421	case -1:
422		return 0;
423	case '\n':
424		/* sloscan() never passes \n, that's up to fastscan() */
425		unch(ch);
426		goto yyret;
427
428	case '\r': /* Ignore CR's */
429		yyp = 0;
430		break;
431
432	case '0': case '1': case '2': case '3': case '4': case '5':
433	case '6': case '7': case '8': case '9':
434		/* readin a "pp-number" */
435ppnum:		for (;;) {
436			ch = inch();
437			if (spechr[ch] & C_EP) {
438				yytext[yyp++] = (usch)ch;
439				ch = inch();
440				if (ch == '-' || ch == '+') {
441					yytext[yyp++] = (usch)ch;
442				} else
443					unch(ch);
444				continue;
445			}
446			if ((spechr[ch] & C_ID) || ch == '.') {
447				yytext[yyp++] = (usch)ch;
448				continue;
449			}
450			break;
451		}
452		unch(ch);
453		yytext[yyp] = 0;
454
455		return NUMBER;
456
457	case '\'':
458chlit:
459		for (;;) {
460			if ((ch = inch()) == '\\') {
461				yytext[yyp++] = (usch)ch;
462				yytext[yyp++] = (usch)inch();
463				continue;
464			} else if (ch == '\n') {
465				/* not a constant */
466				while (yyp > 1)
467					unch(yytext[--yyp]);
468				ch = '\'';
469				goto any;
470			} else
471				yytext[yyp++] = (usch)ch;
472			if (ch == '\'')
473				break;
474		}
475		yytext[yyp] = 0;
476
477		return (NUMBER);
478
479	case ' ':
480	case '\t':
481		while ((ch = inch()) == ' ' || ch == '\t')
482			yytext[yyp++] = (usch)ch;
483		unch(ch);
484		yytext[yyp] = 0;
485		return(WSPACE);
486
487	case '/':
488		if ((ch = inch()) == '/') {
489			do {
490				yytext[yyp++] = (usch)ch;
491				ch = inch();
492			} while (ch && ch != '\n');
493			yytext[yyp] = 0;
494			unch(ch);
495			goto zagain;
496		} else if (ch == '*') {
497			int c, wrn;
498			extern int readmac;
499
500			if (Cflag && !flslvl && readmac) {
501				unch(ch);
502				yytext[yyp] = 0;
503				return CMNT;
504			}
505
506			wrn = 0;
507		more:	while ((c = inch()) && c != '*') {
508				if (c == '\n')
509					putch(c), ifiles->lineno++;
510				else if (c == EBLOCK) {
511					(void)inch();
512					(void)inch();
513				} else if (c == 1) /* WARN */
514					wrn = 1;
515			}
516			if (c == 0)
517				return 0;
518			if ((c = inch()) && c != '/') {
519				unch(c);
520				goto more;
521			}
522			if (c == 0)
523				return 0;
524			if (!tflag && !Cflag && !flslvl)
525				unch(' ');
526			if (wrn)
527				unch(1);
528			goto zagain;
529		}
530		unch(ch);
531		ch = '/';
532		goto any;
533
534	case '.':
535		ch = inch();
536		if (isdigit(ch)) {
537			yytext[yyp++] = (usch)ch;
538			goto ppnum;
539		} else {
540			unch(ch);
541			ch = '.';
542		}
543		goto any;
544
545	case '\"':
546		if (tflag && defining)
547			goto any;
548	strng:
549		for (;;) {
550			if ((ch = inch()) == '\\') {
551				yytext[yyp++] = (usch)ch;
552				yytext[yyp++] = (usch)inch();
553				continue;
554			} else
555				yytext[yyp++] = (usch)ch;
556			if (ch == '\"')
557				break;
558		}
559		yytext[yyp] = 0;
560		return(STRING);
561
562	case 'L':
563		if ((ch = inch()) == '\"' && !tflag) {
564			yytext[yyp++] = (usch)ch;
565			goto strng;
566		} else if (ch == '\'' && !tflag) {
567			yytext[yyp++] = (usch)ch;
568			goto chlit;
569		}
570		unch(ch);
571		/* FALLTHROUGH */
572
573	/* Yetch, all identifiers */
574	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
575	case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
576	case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
577	case 's': case 't': case 'u': case 'v': case 'w': case 'x':
578	case 'y': case 'z':
579	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
580	case 'G': case 'H': case 'I': case 'J': case 'K':
581	case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
582	case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
583	case 'Y': case 'Z':
584	case '_': /* {L}({L}|{D})* */
585
586		/* Special hacks */
587		for (;;) { /* get chars */
588			ch = inch();
589			if (isalpha(ch) || isdigit(ch) || ch == '_') {
590				yytext[yyp++] = (usch)ch;
591			} else {
592				if (ch != -1)
593					unch(ch);
594				break;
595			}
596		}
597		yytext[yyp] = 0; /* need already string */
598		/* end special hacks */
599
600		return IDENT;
601	default:
602	any:
603		yytext[yyp] = 0;
604		return yytext[0];
605
606	} /* endcase */
607	goto zagain;
608
609yyret:
610	yytext[yyp] = 0;
611	return ch;
612}
613
614int
615yylex(void)
616{
617	static int ifdef, noex;
618	struct symtab *nl;
619	int ch, c2;
620
621	while ((ch = sloscan()) == WSPACE)
622		;
623	if (ch < 128 && spechr[ch] & C_2)
624		c2 = inpch();
625	else
626		c2 = 0;
627
628#define	C2(a,b,c) case a: if (c2 == b) return c; break
629	switch (ch) {
630	C2('=', '=', EQ);
631	C2('!', '=', NE);
632	C2('|', '|', OROR);
633	C2('&', '&', ANDAND);
634	case '<':
635		if (c2 == '<') return LS;
636		if (c2 == '=') return LE;
637		break;
638	case '>':
639		if (c2 == '>') return RS;
640		if (c2 == '=') return GE;
641		break;
642	case '+':
643	case '-':
644		if (ch == c2)
645			badop("");
646		break;
647
648	case '/':
649		if (Cflag == 0 || c2 != '*')
650			break;
651		/* Found comment that need to be skipped */
652		for (;;) {
653			ch = inpch();
654		c1:	if (ch != '*')
655				continue;
656			if ((ch = inpch()) == '/')
657				break;
658			goto c1;
659		}
660		return yylex();
661
662	case NUMBER:
663		if (yytext[0] == '\'') {
664			yylval.node.op = NUMBER;
665			yylval.node.nd_val = charcon((usch *)yytext);
666		} else
667			cvtdig(yytext[0] != '0' ? 10 :
668			    yytext[1] == 'x' || yytext[1] == 'X' ? 16 : 8);
669		return NUMBER;
670
671	case IDENT:
672		if (strcmp((char *)yytext, "defined") == 0) {
673			ifdef = 1;
674			return DEFINED;
675		}
676		nl = lookup((usch *)yytext, FIND);
677		if (ifdef) {
678			yylval.node.nd_val = nl != NULL;
679			ifdef = 0;
680		} else if (nl && noex == 0) {
681			usch *och = stringbuf;
682			int i;
683
684			i = kfind(nl);
685			unch(WARN);
686			if (i)
687				unpstr(stringbuf);
688			else
689				unpstr(nl->namep);
690			stringbuf = och;
691			noex = 1;
692			return yylex();
693		} else {
694			yylval.node.nd_val = 0;
695		}
696		yylval.node.op = NUMBER;
697		return NUMBER;
698	case WARN:
699		noex = 0;
700		return yylex();
701	default:
702		return ch;
703	}
704	unch(c2);
705	return ch;
706}
707
708usch *yyp, yybuf[CPPBUF];
709
710int yywrap(void);
711
712static int
713inpch(void)
714{
715	int len;
716
717	if (ifiles->curptr < ifiles->maxread)
718		return *ifiles->curptr++;
719
720	if (ifiles->infil == -1)
721		return -1;
722	if ((len = read(ifiles->infil, ifiles->buffer, CPPBUF)) < 0)
723		error("read error on file %s", ifiles->orgfn);
724	if (len == 0)
725		return -1;
726	ifiles->curptr = ifiles->buffer;
727	ifiles->maxread = ifiles->buffer + len;
728	return inpch();
729}
730
731static int
732inch(void)
733{
734	int c;
735
736again:	switch (c = inpch()) {
737	case '\\': /* continued lines */
738msdos:		if ((c = inpch()) == '\n') {
739			ifiles->lineno++;
740			goto again;
741		} else if (c == '\r')
742			goto msdos;
743		unch(c);
744		return '\\';
745	case '?': /* trigraphs */
746		if ((c = chktg())) {
747			unch(c);
748			goto again;
749		}
750		return '?';
751	default:
752		return c;
753	}
754}
755
756/*
757 * Let the command-line args be faked defines at beginning of file.
758 */
759static void
760prinit(struct initar *it, struct includ *ic)
761{
762	const char *pre, *post;
763	char *a;
764
765	if (it->next)
766		prinit(it->next, ic);
767	pre = post = NULL; /* XXX gcc */
768	switch (it->type) {
769	case 'D':
770		pre = "#define ";
771		if ((a = strchr(it->str, '=')) != NULL) {
772			*a = ' ';
773			post = "\n";
774		} else
775			post = " 1\n";
776		break;
777	case 'U':
778		pre = "#undef ";
779		post = "\n";
780		break;
781	case 'i':
782		pre = "#include \"";
783		post = "\"\n";
784		break;
785	default:
786		error("prinit");
787	}
788	strlcat((char *)ic->buffer, pre, CPPBUF+1);
789	strlcat((char *)ic->buffer, it->str, CPPBUF+1);
790	if (strlcat((char *)ic->buffer, post, CPPBUF+1) >= CPPBUF+1)
791		error("line exceeds buffer size");
792
793	ic->lineno--;
794	while (*ic->maxread)
795		ic->maxread++;
796}
797
798/*
799 * A new file included.
800 * If ifiles == NULL, this is the first file and already opened (stdin).
801 * Return 0 on success, -1 if file to be included is not found.
802 */
803int
804pushfile(const usch *file, const usch *fn, int idx, void *incs)
805{
806	extern struct initar *initar;
807	struct includ ibuf;
808	struct includ *ic;
809	int otrulvl;
810
811	ic = &ibuf;
812	ic->next = ifiles;
813
814	if (file != NULL) {
815		if ((ic->infil = open((const char *)file, O_RDONLY)) < 0)
816			return -1;
817		ic->orgfn = ic->fname = file;
818		if (++inclevel > MAX_INCLEVEL)
819			error("Limit for nested includes exceeded");
820	} else {
821		ic->infil = 0;
822		ic->orgfn = ic->fname = (const usch *)"<stdin>";
823	}
824#ifndef BUF_STACK
825	ic->bbuf = malloc(BBUFSZ);
826#endif
827	ic->buffer = ic->bbuf+NAMEMAX;
828	ic->curptr = ic->buffer;
829	ifiles = ic;
830	ic->lineno = 1;
831	ic->maxread = ic->curptr;
832	ic->idx = idx;
833	ic->incs = incs;
834	ic->fn = fn;
835	prtline();
836	if (initar) {
837		int oin = ic->infil;
838		ic->infil = -1;
839		*ic->maxread = 0;
840		prinit(initar, ic);
841		initar = NULL;
842		if (dMflag)
843			write(ofd, ic->buffer, strlen((char *)ic->buffer));
844		fastscan();
845		prtline();
846		ic->infil = oin;
847	}
848
849	otrulvl = trulvl;
850
851	fastscan();
852
853	if (otrulvl != trulvl || flslvl)
854		error("unterminated conditional");
855
856#ifndef BUF_STACK
857	free(ic->bbuf);
858#endif
859	ifiles = ic->next;
860	close(ic->infil);
861	inclevel--;
862	return 0;
863}
864
865/*
866 * Print current position to output file.
867 */
868void
869prtline(void)
870{
871	usch *s, *os = stringbuf;
872
873	if (Mflag) {
874		if (dMflag)
875			return; /* no output */
876		if (ifiles->lineno == 1) {
877			s = sheap("%s: %s\n", Mfile, ifiles->fname);
878			write(ofd, s, strlen((char *)s));
879		}
880	} else if (!Pflag)
881		putstr(sheap("\n# %d \"%s\"\n", ifiles->lineno, ifiles->fname));
882	stringbuf = os;
883}
884
885void
886cunput(int c)
887{
888#ifdef PCC_DEBUG
889//	extern int dflag;
890//	if (dflag)printf(": '%c'(%d)\n", c > 31 ? c : ' ', c);
891#endif
892#if 0
893if (c == 10) {
894	printf("c == 10!!!\n");
895}
896#endif
897	unch(c);
898}
899
900int yywrap(void) { return 1; }
901
902static int
903dig2num(int c)
904{
905	if (c >= 'a')
906		c = c - 'a' + 10;
907	else if (c >= 'A')
908		c = c - 'A' + 10;
909	else
910		c = c - '0';
911	return c;
912}
913
914/*
915 * Convert string numbers to unsigned long long and check overflow.
916 */
917static void
918cvtdig(int rad)
919{
920	unsigned long long rv = 0;
921	unsigned long long rv2 = 0;
922	usch *y = yytext;
923	int c;
924
925	c = *y++;
926	if (rad == 16)
927		y++;
928	while (isxdigit(c)) {
929		rv = rv * rad + dig2num(c);
930		/* check overflow */
931		if (rv / rad < rv2)
932			error("Constant \"%s\" is out of range", yytext);
933		rv2 = rv;
934		c = *y++;
935	}
936	y--;
937	while (*y == 'l' || *y == 'L')
938		y++;
939	yylval.node.op = *y == 'u' || *y == 'U' ? UNUMBER : NUMBER;
940	yylval.node.nd_uval = rv;
941	if ((rad == 8 || rad == 16) && yylval.node.nd_val < 0)
942		yylval.node.op = UNUMBER;
943	if (yylval.node.op == NUMBER && yylval.node.nd_val < 0)
944		/* too large for signed, see 6.4.4.1 */
945		error("Constant \"%s\" is out of range", yytext);
946}
947
948static int
949charcon(usch *p)
950{
951	int val, c;
952
953	p++; /* skip first ' */
954	val = 0;
955	if (*p++ == '\\') {
956		switch (*p++) {
957		case 'a': val = '\a'; break;
958		case 'b': val = '\b'; break;
959		case 'f': val = '\f'; break;
960		case 'n': val = '\n'; break;
961		case 'r': val = '\r'; break;
962		case 't': val = '\t'; break;
963		case 'v': val = '\v'; break;
964		case '\"': val = '\"'; break;
965		case '\'': val = '\''; break;
966		case '\\': val = '\\'; break;
967		case 'x':
968			while (isxdigit(c = *p)) {
969				val = val * 16 + dig2num(c);
970				p++;
971			}
972			break;
973		case '0': case '1': case '2': case '3': case '4':
974		case '5': case '6': case '7':
975			p--;
976			while (isdigit(c = *p)) {
977				val = val * 8 + (c - '0');
978				p++;
979			}
980			break;
981		default: val = p[-1];
982		}
983
984	} else
985		val = p[-1];
986	return val;
987}
988
989static void
990chknl(int ignore)
991{
992	int t;
993
994	while ((t = sloscan()) == WSPACE)
995		;
996	if (t != '\n') {
997		if (t && t != (usch)-1) {
998			if (ignore) {
999				warning("newline expected, got \"%s\"", yytext);
1000				/* ignore rest of line */
1001				while ((t = sloscan()) && t != '\n')
1002					;
1003			}
1004			else
1005				error("newline expected, got \"%s\"", yytext);
1006		} else {
1007			if (ignore)
1008				warning("no newline at end of file");
1009			else
1010				error("no newline at end of file");
1011		}
1012	}
1013}
1014
1015static void
1016elsestmt(void)
1017{
1018	if (flslvl) {
1019		if (elflvl > trulvl)
1020			;
1021		else if (--flslvl!=0) {
1022			flslvl++;
1023		} else {
1024			trulvl++;
1025			prtline();
1026		}
1027	} else if (trulvl) {
1028		flslvl++;
1029		trulvl--;
1030	} else
1031		error("If-less else");
1032	if (elslvl==trulvl+flslvl)
1033		error("Too many else");
1034	elslvl=trulvl+flslvl;
1035	chknl(1);
1036}
1037
1038static void
1039skpln(void)
1040{
1041	/* just ignore the rest of the line */
1042	while (inch() != '\n')
1043		;
1044	unch('\n');
1045	flslvl++;
1046}
1047
1048static void
1049ifdefstmt(void)
1050{
1051	int t;
1052
1053	if (flslvl) {
1054		skpln();
1055		return;
1056	}
1057	do
1058		t = sloscan();
1059	while (t == WSPACE);
1060	if (t != IDENT)
1061		error("bad ifdef");
1062	if (lookup((usch *)yytext, FIND) == 0) {
1063		putch('\n');
1064		flslvl++;
1065	} else
1066		trulvl++;
1067	chknl(0);
1068}
1069
1070static void
1071ifndefstmt(void)
1072{
1073	int t;
1074
1075	if (flslvl) {
1076		skpln();
1077		return;
1078	}
1079	do
1080		t = sloscan();
1081	while (t == WSPACE);
1082	if (t != IDENT)
1083		error("bad ifndef");
1084	if (lookup((usch *)yytext, FIND) != 0) {
1085		putch('\n');
1086		flslvl++;
1087	} else
1088		trulvl++;
1089	chknl(0);
1090}
1091
1092static void
1093endifstmt(void)
1094{
1095	if (flslvl) {
1096		flslvl--;
1097		if (flslvl == 0) {
1098			putch('\n');
1099			prtline();
1100		}
1101	} else if (trulvl)
1102		trulvl--;
1103	else
1104		error("If-less endif");
1105	if (flslvl == 0)
1106		elflvl = 0;
1107	elslvl = 0;
1108	chknl(1);
1109}
1110
1111static void
1112ifstmt(void)
1113{
1114	if (flslvl == 0) {
1115		if (yyparse() == 0) {
1116			putch('\n');
1117			++flslvl;
1118		} else
1119			++trulvl;
1120	} else
1121		++flslvl;
1122}
1123
1124static void
1125elifstmt(void)
1126{
1127	if (flslvl == 0)
1128		elflvl = trulvl;
1129	if (flslvl) {
1130		if (elflvl > trulvl)
1131			;
1132		else if (--flslvl!=0)
1133			++flslvl;
1134		else {
1135			if (yyparse()) {
1136				++trulvl;
1137				prtline();
1138			} else {
1139				putch('\n');
1140				++flslvl;
1141			}
1142		}
1143	} else if (trulvl) {
1144		++flslvl;
1145		--trulvl;
1146	} else
1147		error("If-less elif");
1148}
1149
1150static usch *
1151svinp(void)
1152{
1153	int c;
1154	usch *cp = stringbuf;
1155
1156	while ((c = inch()) && c != '\n')
1157		savch(c);
1158	savch('\n');
1159	savch(0);
1160	return cp;
1161}
1162
1163static void
1164cpperror(void)
1165{
1166	usch *cp;
1167	int c;
1168
1169	if (flslvl)
1170		return;
1171	c = sloscan();
1172	if (c != WSPACE && c != '\n')
1173		error("bad error");
1174	cp = svinp();
1175	if (flslvl)
1176		stringbuf = cp;
1177	else
1178		error("%s", cp);
1179}
1180
1181static void
1182cppwarning(void)
1183{
1184	usch *cp;
1185	int c;
1186
1187	if (flslvl)
1188		return;
1189	c = sloscan();
1190	if (c != WSPACE && c != '\n')
1191		error("bad warning");
1192
1193	/* svinp() add an unwanted \n */
1194	cp = stringbuf;
1195	while ((c = inch()) && c != '\n')
1196		savch(c);
1197	savch(0);
1198
1199	if (flslvl)
1200		stringbuf = cp;
1201	else
1202		warning("#warning %s", cp);
1203
1204	unch('\n');
1205}
1206
1207static void
1208undefstmt(void)
1209{
1210	struct symtab *np;
1211
1212	if (flslvl)
1213		return;
1214	if (sloscan() != WSPACE || sloscan() != IDENT)
1215		error("bad undef");
1216	if (flslvl == 0 && (np = lookup((usch *)yytext, FIND)))
1217		np->value = 0;
1218	chknl(0);
1219}
1220
1221static void
1222pragmastmt(void)
1223{
1224	int c;
1225
1226	if (sloscan() != WSPACE)
1227		error("bad pragma");
1228	if (!flslvl)
1229		putstr((const usch *)"\n#pragma ");
1230	do {
1231		c = inch();
1232		if (!flslvl)
1233			putch(c);	/* Do arg expansion instead? */
1234	} while (c && c != '\n');
1235	if (c == '\n')
1236		unch(c);
1237	prtline();
1238}
1239
1240static void
1241badop(const char *op)
1242{
1243	error("invalid operator in preprocessor expression: %s", op);
1244}
1245
1246int
1247cinput(void)
1248{
1249	return inch();
1250}
1251
1252/*
1253 * Check for (and convert) trigraphs.
1254 */
1255int
1256chktg(void)
1257{
1258	int c;
1259
1260	if ((c = inpch()) != '?') {
1261		unch(c);
1262		return 0;
1263	}
1264	switch (c = inpch()) {
1265	case '=': c = '#'; break;
1266	case '(': c = '['; break;
1267	case ')': c = ']'; break;
1268	case '<': c = '{'; break;
1269	case '>': c = '}'; break;
1270	case '/': c = '\\'; break;
1271	case '\'': c = '^'; break;
1272	case '!': c = '|'; break;
1273	case '-': c = '~'; break;
1274	default:
1275		unch(c);
1276		unch('?');
1277		c = 0;
1278	}
1279	return c;
1280}
1281
1282static struct {
1283	const char *name;
1284	void (*fun)(void);
1285} ppd[] = {
1286	{ "ifndef", ifndefstmt },
1287	{ "ifdef", ifdefstmt },
1288	{ "if", ifstmt },
1289	{ "include", include },
1290	{ "else", elsestmt },
1291	{ "endif", endifstmt },
1292	{ "error", cpperror },
1293	{ "warning", cppwarning },
1294	{ "define", define },
1295	{ "undef", undefstmt },
1296	{ "line", line },
1297	{ "pragma", pragmastmt },
1298	{ "elif", elifstmt },
1299#ifdef GCC_COMPAT
1300	{ "include_next", include_next },
1301#endif
1302};
1303
1304/*
1305 * Handle a preprocessor directive.
1306 */
1307void
1308ppdir(void)
1309{
1310	char bp[20];
1311	int ch, i;
1312
1313	while ((ch = inch()) == ' ' || ch == '\t')
1314		;
1315	if (ch == '\n') { /* empty directive */
1316		unch(ch);
1317		return;
1318	}
1319	if (ch < 'a' || ch > 'z')
1320		goto out; /* something else, ignore */
1321	i = 0;
1322	do {
1323		bp[i++] = (usch)ch;
1324		if (i == sizeof(bp)-1)
1325			goto out; /* too long */
1326		ch = inch();
1327	} while ((ch >= 'a' && ch <= 'z') || (ch == '_'));
1328	unch(ch);
1329	bp[i++] = 0;
1330
1331	/* got keyword */
1332#define	SZ (int)(sizeof(ppd)/sizeof(ppd[0]))
1333	for (i = 0; i < SZ; i++)
1334		if (bp[0] == ppd[i].name[0] && strcmp(bp, ppd[i].name) == 0)
1335			break;
1336	if (i == SZ)
1337		goto out;
1338
1339	/* Found matching keyword */
1340	(*ppd[i].fun)();
1341	return;
1342
1343out:	while ((ch = inch()) != '\n' && ch != -1)
1344		;
1345	unch('\n');
1346}
1347