1 %{
2 /*	$Id: scanner.l,v 1.50 2011/06/03 15:42:45 plunky Exp $   */
3 
4 /*
5  * Copyright (c) 2004 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  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include "config.h"
31 
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 #include <fcntl.h>
39 #include <errno.h>
40 
41 #include "compat.h"
42 #include "cpp.h"
43 #include "y.tab.h"
44 %}
45 
46 %{
47 static void cvtdig(int rad);
48 static int charcon(usch *);
49 static void elsestmt(void);
50 static void ifdefstmt(void);
51 static void ifndefstmt(void);
52 static void endifstmt(void);
53 static void ifstmt(void);
54 static void cpperror(void);
55 static void pragmastmt(void);
56 static void undefstmt(void);
57 static void cpperror(void);
58 static void elifstmt(void);
59 static void storepb(void);
60 static void badop(const char *);
61 void  include(void);
62 void  define(void);
63 
64 extern int yyget_lineno (void);
65 extern void yyset_lineno (int);
66 
67 static int inch(void);
68 
69 static int scale, gotdef, contr;
70 int inif;
71 
72 #ifdef FLEX_SCANNER /* should be set by autoconf instead */
73 static int
yyinput(char * b,int m)74 yyinput(char *b, int m)
75 {
76 	int c, i;
77 
78 	for (i = 0; i < m; i++) {
79 		if ((c = inch()) < 0)
80 			break;
81 		*b++ = c;
82 		if (c == '\n') {
83 			i++;
84 			break;
85 		}
86 	}
87 	return i;
88 }
89 #undef YY_INPUT
90 #undef YY_BUF_SIZE
91 #define	YY_BUF_SIZE (8*65536)
92 #define YY_INPUT(b,r,m) (r = yyinput(b, m))
93 #ifdef HAVE_CPP_VARARG_MACRO_GCC
94 #define fprintf(x, ...) error(__VA_ARGS__)
95 #endif
96 #define	ECHO putstr((usch *)yytext)
97 #undef fileno
98 #define fileno(x) 0
99 
100 #if YY_FLEX_SUBMINOR_VERSION >= 31
101 /* Hack to avoid unnecessary warnings */
102 FILE *yyget_in	(void);
103 FILE *yyget_out  (void);
104 int yyget_leng	(void);
105 char *yyget_text  (void);
106 void yyset_in (FILE *  in_str );
107 void yyset_out (FILE *	out_str );
108 int yyget_debug  (void);
109 void yyset_debug (int  bdebug );
110 int yylex_destroy  (void);
111 #endif
112 #else	/* Assume lex here */
113 #undef input
114 #undef unput
115 #define input() inch()
116 #define unput(ch) unch(ch)
117 #endif
118 #define PRTOUT(x) if (YYSTATE || slow) return x; if (!flslvl) putstr((usch *)yytext);
119 /* protection against recursion in #include */
120 #define MAX_INCLEVEL	100
121 static int inclevel;
122 %}
123 
124 D	[0-9]
125 L	[a-zA-Z_]
126 H	[a-fA-F0-9]
127 E	[Ee][+-]?{D}+
128 FS	(f|F|l|L)
129 IS	(u|U|l|L)*
130 WS	[\t ]
131 
132 %s IFR CONTR DEF COMMENT
133 
134 %%
135 
136 "\n"			{	int os = YYSTATE;
137 				if (os != IFR)
138 					BEGIN 0;
139 				ifiles->lineno++;
140 				if (flslvl == 0) {
141 					if (ifiles->lineno == 1)
142 						prtline();
143 					else
144 						putch('\n');
145 				}
146 				if ((os != 0 || slow) && !contr)
147 					return '\n';
148 				contr = 0;
149 			}
150 
151 "\r"			{ ; /* Ignore CR's */ }
152 
153 <IFR>"++"		{ badop("++"); }
154 <IFR>"--"		{ badop("--"); }
155 <IFR>"=="		{ return EQ; }
156 <IFR>"!="		{ return NE; }
157 <IFR>"<="		{ return LE; }
158 <IFR>"<<"		{ return LS; }
159 <IFR>">>"		{ return RS; }
160 <IFR>">="		{ return GE; }
161 <IFR>"||"		{ return OROR; }
162 <IFR>"&&"		{ return ANDAND; }
163 <IFR>"defined"		{	int p, c;
164 				gotdef = 1;
165 				if ((p = c = yylex()) == '(')
166 					c = yylex();
167 				if (c != IDENT || (p != IDENT && p != '('))
168 					error("syntax error");
169 				if (p == '(' && yylex() != ')')
170 					error("syntax error");
171 				return NUMBER;
172 			}
173 
174 <IFR>{WS}+		{ ; }
175 <IFR>{L}({L}|{D})*	{
176 				yylval.node.op = NUMBER;
177 				if (gotdef) {
178 					yylval.node.nd_val
179 					    = lookup((usch *)yytext, FIND) != 0;
180 					gotdef = 0;
181 					return IDENT;
182 				}
183 				yylval.node.nd_val = 0;
184 				return NUMBER;
185 			}
186 
187 [0-9][0-9]*		{
188 				if (slow && !YYSTATE)
189 					return IDENT;
190 				scale = yytext[0] == '0' ? 8 : 10;
191 				goto num;
192 			}
193 
194 0[xX]{H}+{IS}?		{	scale = 16;
195 			num:	if (YYSTATE == IFR)
196 					cvtdig(scale);
197 				PRTOUT(NUMBER);
198 			}
199 0{D}+{IS}?		{ scale = 8; goto num; }
200 {D}+{IS}?		{ scale = 10; goto num; }
201 '(\\.|[^\\'])+'		{
202 				if (YYSTATE || slow) {
203 					yylval.node.op = NUMBER;
204 					yylval.node.nd_val = charcon((usch *)yytext);
205 					return (NUMBER);
206 				}
207 				if (tflag)
208 					yyless(1);
209 				if (!flslvl)
210 					putstr((usch *)yytext);
211 			}
212 
213 <IFR>.			{ return yytext[0]; }
214 
215 {D}+{E}{FS}?		{ PRTOUT(FPOINT); }
216 {D}*"."{D}+({E})?{FS}?	{ PRTOUT(FPOINT); }
217 {D}+"."{D}*({E})?{FS}?	{ PRTOUT(FPOINT); }
218 
219 ^{WS}*#{WS}*		{	extern int inmac;
220 
221 				if (inmac)
222 					error("preprocessor directive found "
223 					    "while expanding macro");
224 				contr = 1;
225 				BEGIN CONTR;
226 			}
227 {WS}+			{ PRTOUT(WSPACE); }
228 
229 <CONTR>"ifndef"		{ contr = 0; ifndefstmt(); }
230 <CONTR>"ifdef"		{ contr = 0; ifdefstmt(); }
231 <CONTR>"if"		{ contr = 0; storepb(); BEGIN IFR; ifstmt(); BEGIN 0; }
232 <CONTR>"include"	{ contr = 0; BEGIN 0; include(); prtline(); }
233 <CONTR>"else"		{ contr = 0; elsestmt(); }
234 <CONTR>"endif"		{ contr = 0; endifstmt(); }
235 <CONTR>"error"		{ contr = 0; if (slow) return IDENT; cpperror(); BEGIN 0; }
236 <CONTR>"define"		{ contr = 0; BEGIN DEF; define(); BEGIN 0; }
237 <CONTR>"undef"		{ contr = 0; if (slow) return IDENT; undefstmt(); }
238 <CONTR>"line"		{ contr = 0; storepb(); BEGIN 0; line(); }
239 <CONTR>"pragma"		{ contr = 0; pragmastmt(); BEGIN 0; }
240 <CONTR>"elif"		{ contr = 0; storepb(); BEGIN IFR; elifstmt(); BEGIN 0; }
241 
242 
243 
244 "//".*$			{ /* if (tflag) yyless(..) */
245 				if (Cflag && !flslvl && !slow)
246 					putstr((usch *)yytext);
247 				else if (!flslvl)
248 					putch(' ');
249 			}
250 "/*"			{	int c, wrn;
251 				int prtcm = Cflag && !flslvl && !slow;
252 				extern int readmac;
253 
254 				if (Cflag && !flslvl && readmac)
255 					return CMNT;
256 
257 				if (prtcm)
258 					putstr((usch *)yytext);
259 				wrn = 0;
260 			more:	while ((c = input()) && c != '*') {
261 					if (c == '\n')
262 						putch(c), ifiles->lineno++;
263 					else if (c == 1) /* WARN */
264 						wrn = 1;
265 					else if (prtcm)
266 						putch(c);
267 				}
268 				if (c == 0)
269 					return 0;
270 				if (prtcm)
271 					putch(c);
272 				if ((c = input()) && c != '/') {
273 					unput(c);
274 					goto more;
275 				}
276 				if (prtcm)
277 					putch(c);
278 				if (c == 0)
279 					return 0;
280 				if (!tflag && !Cflag && !flslvl)
281 					unput(' ');
282 				if (wrn)
283 					unput(1);
284 			}
285 
286 <DEF>"##"		{ return CONCAT; }
287 <DEF>"#"		{ return MKSTR; }
288 <DEF>"..."		{ return ELLIPS; }
289 <DEF>"__VA_ARGS__"	{ return VA_ARGS; }
290 
291 L?\"(\\.|[^\\"])*\"	{ PRTOUT(STRING); }
292 [a-zA-Z_0-9]+		{ /* {L}({L}|{D})* */
293 				struct symtab *nl;
294 				if (slow)
295 					return IDENT;
296 				if (YYSTATE == CONTR) {
297 					if (flslvl == 0) {
298 						/*error("undefined control");*/
299 						while (input() != '\n')
300 							;
301 						unput('\n');
302 						BEGIN 0;
303 						goto xx;
304 					} else {
305 						BEGIN 0; /* do nothing */
306 					}
307 				}
308 				if (flslvl) {
309 					; /* do nothing */
310 				} else if (isdigit((int)yytext[0]) == 0 &&
311 				    (nl = lookup((usch *)yytext, FIND)) != 0) {
312 					usch *op = stringbuf;
313 					putstr(gotident(nl));
314 					stringbuf = op;
315 				} else
316 					putstr((usch *)yytext);
317 				xx: ;
318 			}
319 
320 .			{
321 				if (contr) {
322 					while (input() != '\n')
323 						;
324 					unput('\n');
325 					BEGIN 0;
326 					contr = 0;
327 					goto yy;
328 				}
329 				if (YYSTATE || slow)
330 					return yytext[0];
331 				if (yytext[0] == 6) { /* PRAGS */
332 					usch *obp = stringbuf;
333 					extern usch *prtprag(usch *);
334 					*stringbuf++ = yytext[0];
335 					do {
336 						*stringbuf = input();
337 					} while (*stringbuf++ != 14);
338 					prtprag(obp);
339 					stringbuf = obp;
340 				} else {
341 					PRTOUT(yytext[0]);
342 				}
343 				yy:;
344 			}
345 
346 %%
347 
348 usch *yyp, yybuf[CPPBUF];
349 
350 int yylex(void);
351 int yywrap(void);
352 
353 static int
354 inpch(void)
355 {
356 	int len;
357 
358 	if (ifiles->curptr < ifiles->maxread)
359 		return *ifiles->curptr++;
360 
361 	if ((len = read(ifiles->infil, ifiles->buffer, CPPBUF)) < 0)
362 		error("read error on file %s", ifiles->orgfn);
363 	if (len == 0)
364 		return -1;
365 	ifiles->curptr = ifiles->buffer;
366 	ifiles->maxread = ifiles->buffer + len;
367 	return inpch();
368 }
369 
370 #define unch(c) *--ifiles->curptr = c
371 
372 static int
373 inch(void)
374 {
375 	int c;
376 
377 again:	switch (c = inpch()) {
378 	case '\\': /* continued lines */
379 msdos:		if ((c = inpch()) == '\n') {
380 			ifiles->lineno++;
381 			putch('\n');
382 			goto again;
383 		} else if (c == '\r')
384 			goto msdos;
385 		unch(c);
386 		return '\\';
387 	case '?': /* trigraphs */
388 		if ((c = inpch()) != '?') {
389 			unch(c);
390 			return '?';
391 		}
392 		switch (c = inpch()) {
393 		case '=': c = '#'; break;
394 		case '(': c = '['; break;
395 		case ')': c = ']'; break;
396 		case '<': c = '{'; break;
397 		case '>': c = '}'; break;
398 		case '/': c = '\\'; break;
399 		case '\'': c = '^'; break;
400 		case '!': c = '|'; break;
401 		case '-': c = '~'; break;
402 		default:
403 			unch(c);
404 			unch('?');
405 			return '?';
406 		}
407 		unch(c);
408 		goto again;
409 	default:
410 		return c;
411 	}
412 }
413 
414 /*
415  * Let the command-line args be faked defines at beginning of file.
416  */
417 static void
418 prinit(struct initar *it, struct includ *ic)
419 {
420 	char *a, *pre, *post;
421 
422 	if (it->next)
423 		prinit(it->next, ic);
424 	pre = post = NULL; /* XXX gcc */
425 	switch (it->type) {
426 	case 'D':
427 		pre = "#define ";
428 		if ((a = strchr(it->str, '=')) != NULL) {
429 			*a = ' ';
430 			post = "\n";
431 		} else
432 			post = " 1\n";
433 		break;
434 	case 'U':
435 		pre = "#undef ";
436 		post = "\n";
437 		break;
438 	case 'i':
439 		pre = "#include \"";
440 		post = "\"\n";
441 		break;
442 	default:
443 		error("prinit");
444 	}
445 	strlcat((char *)ic->buffer, pre, CPPBUF+1);
446 	strlcat((char *)ic->buffer, it->str, CPPBUF+1);
447 	if (strlcat((char *)ic->buffer, post, CPPBUF+1) >= CPPBUF+1)
448 		error("line exceeds buffer size");
449 
450 	ic->lineno--;
451 	while (*ic->maxread)
452 		ic->maxread++;
453 }
454 
455 /*
456  * A new file included.
457  * If ifiles == NULL, this is the first file and already opened (stdin).
458  * Return 0 on success, -1 if file to be included is not found.
459  */
460 int
461 pushfile(usch *file)
462 {
463 	extern struct initar *initar;
464 	struct includ ibuf;
465 	struct includ *ic;
466 	int c, otrulvl;
467 
468 	ic = &ibuf;
469 	ic->next = ifiles;
470 
471 	slow = 0;
472 	if (file != NULL) {
473 		if ((ic->infil = open((char *)file, O_RDONLY)) < 0)
474 			return -1;
475 		ic->orgfn = ic->fname = file;
476 		if (++inclevel > MAX_INCLEVEL)
477 			error("Limit for nested includes exceeded");
478 	} else {
479 		ic->infil = 0;
480 		ic->orgfn = ic->fname = (usch *)"<stdin>";
481 	}
482 	ic->buffer = ic->bbuf+NAMEMAX;
483 	ic->curptr = ic->buffer;
484 	ifiles = ic;
485 	ic->lineno = 1;
486 	ic->maxread = ic->curptr;
487 	prtline();
488 	if (initar) {
489 		*ic->maxread = 0;
490 		prinit(initar, ic);
491 		if (dMflag)
492 			write(ofd, ic->buffer, strlen((char *)ic->buffer));
493 		initar = NULL;
494 	}
495 
496 	otrulvl = trulvl;
497 
498 	if ((c = yylex()) != 0)
499 		error("yylex returned %d", c);
500 
501 	if (otrulvl != trulvl || flslvl)
502 		error("unterminated conditional");
503 
504 	ifiles = ic->next;
505 	close(ic->infil);
506 	inclevel--;
507 	return 0;
508 }
509 
510 /*
511  * Print current position to output file.
512  */
513 void
514 prtline()
515 {
516 	usch *s, *os = stringbuf;
517 
518 	if (Mflag) {
519 		if (dMflag)
520 			return; /* no output */
521 		if (ifiles->lineno == 1) {
522 			s = sheap("%s: %s\n", Mfile, ifiles->fname);
523 			write(ofd, s, strlen((char *)s));
524 		}
525 	} else if (!Pflag)
526 		putstr(sheap("# %d \"%s\"\n", ifiles->lineno, ifiles->fname));
527 	stringbuf = os;
528 }
529 
530 void
531 cunput(int c)
532 {
533 #ifdef PCC_DEBUG
534 	extern int dflag;
535 	if (dflag)printf(": '%c'(%d)", c > 31 ? c : ' ', c);
536 #endif
537 	unput(c);
538 }
539 
540 int yywrap(void) { return 1; }
541 
542 static int
543 dig2num(int c)
544 {
545 	if (c >= 'a')
546 		c = c - 'a' + 10;
547 	else if (c >= 'A')
548 		c = c - 'A' + 10;
549 	else
550 		c = c - '0';
551 	return c;
552 }
553 
554 /*
555  * Convert string numbers to unsigned long long and check overflow.
556  */
557 static void
558 cvtdig(int rad)
559 {
560 	unsigned long long rv = 0;
561 	unsigned long long rv2 = 0;
562 	char *y = yytext;
563 	int c;
564 
565 	c = *y++;
566 	if (rad == 16)
567 		y++;
568 	while (isxdigit(c)) {
569 		rv = rv * rad + dig2num(c);
570 		/* check overflow */
571 		if (rv / rad < rv2)
572 			error("Constant \"%s\" is out of range", yytext);
573 		rv2 = rv;
574 		c = *y++;
575 	}
576 	y--;
577 	while (*y == 'l' || *y == 'L')
578 		y++;
579 	yylval.node.op = *y == 'u' || *y == 'U' ? UNUMBER : NUMBER;
580 	yylval.node.nd_uval = rv;
581 	if ((rad == 8 || rad == 16) && yylval.node.nd_val < 0)
582 		yylval.node.op = UNUMBER;
583 	if (yylval.node.op == NUMBER && yylval.node.nd_val < 0)
584 		/* too large for signed */
585 		error("Constant \"%s\" is out of range", yytext);
586 }
587 
588 static int
589 charcon(usch *p)
590 {
591 	int val, c;
592 
593 	p++; /* skip first ' */
594 	val = 0;
595 	if (*p++ == '\\') {
596 		switch (*p++) {
597 		case 'a': val = '\a'; break;
598 		case 'b': val = '\b'; break;
599 		case 'f': val = '\f'; break;
600 		case 'n': val = '\n'; break;
601 		case 'r': val = '\r'; break;
602 		case 't': val = '\t'; break;
603 		case 'v': val = '\v'; break;
604 		case '\"': val = '\"'; break;
605 		case '\'': val = '\''; break;
606 		case '\\': val = '\\'; break;
607 		case 'x':
608 			while (isxdigit(c = *p)) {
609 				val = val * 16 + dig2num(c);
610 				p++;
611 			}
612 			break;
613 		case '0': case '1': case '2': case '3': case '4':
614 		case '5': case '6': case '7':
615 			p--;
616 			while (isdigit(c = *p)) {
617 				val = val * 8 + (c - '0');
618 				p++;
619 			}
620 			break;
621 		default: val = p[-1];
622 		}
623 
624 	} else
625 		val = p[-1];
626 	return val;
627 }
628 
629 static void
630 chknl(int ignore)
631 {
632 	int t;
633 
634 	slow = 1;
635 	while ((t = yylex()) == WSPACE)
636 		;
637 	if (t != '\n') {
638 		if (ignore) {
639 			warning("newline expected, got \"%s\"", yytext);
640 			/* ignore rest of line */
641 			while ((t = yylex()) && t != '\n')
642 				;
643 		}
644 		else
645 			error("newline expected, got \"%s\"", yytext);
646 	}
647 	slow = 0;
648 }
649 
650 static void
651 elsestmt(void)
652 {
653 	if (flslvl) {
654 		if (elflvl > trulvl)
655 			;
656 		else if (--flslvl!=0) {
657 			flslvl++;
658 		} else {
659 			trulvl++;
660 			prtline();
661 		}
662 	} else if (trulvl) {
663 		flslvl++;
664 		trulvl--;
665 	} else
666 		error("If-less else");
667 	if (elslvl==trulvl+flslvl)
668 		error("Too many else");
669 	elslvl=trulvl+flslvl;
670 	chknl(1);
671 }
672 
673 static void
674 ifdefstmt(void)
675 {
676 	int t;
677 
678 	if (flslvl) {
679 		/* just ignore the rest of the line */
680 		while (input() != '\n')
681 			;
682 		unput('\n');
683 		yylex();
684 		flslvl++;
685 		return;
686 	}
687 	slow = 1;
688 	do
689 		t = yylex();
690 	while (t == WSPACE);
691 	if (t != IDENT)
692 		error("bad ifdef");
693 	slow = 0;
694 	if (flslvl == 0 && lookup((usch *)yytext, FIND) != 0)
695 		trulvl++;
696 	else
697 		flslvl++;
698 	chknl(0);
699 }
700 
701 static void
702 ifndefstmt(void)
703 {
704 	int t;
705 
706 	slow = 1;
707 	do
708 		t = yylex();
709 	while (t == WSPACE);
710 	if (t != IDENT)
711 		error("bad ifndef");
712 	slow = 0;
713 	if (flslvl == 0 && lookup((usch *)yytext, FIND) == 0)
714 		trulvl++;
715 	else
716 		flslvl++;
717 	chknl(0);
718 }
719 
720 static void
721 endifstmt(void)
722 {
723 	if (flslvl) {
724 		flslvl--;
725 		if (flslvl == 0)
726 			prtline();
727 	} else if (trulvl)
728 		trulvl--;
729 	else
730 		error("If-less endif");
731 	if (flslvl == 0)
732 		elflvl = 0;
733 	elslvl = 0;
734 	chknl(1);
735 }
736 
737 /*
738  * Note! Ugly!
739  * Walk over the string s and search for defined, and replace it with
740  * spaces and a 1 or 0.
741  */
742 static void
743 fixdefined(usch *s)
744 {
745 	usch *bc, oc;
746 
747 	for (; *s; s++) {
748 		if (*s != 'd')
749 			continue;
750 		if (memcmp(s, "defined", 7))
751 			continue;
752 		/* Ok, got defined, can scratch it now */
753 		memset(s, ' ', 7);
754 		s += 7;
755 #define	WSARG(x) (x == ' ' || x == '\t')
756 		if (*s != '(' && !WSARG(*s))
757 			continue;
758 		while (WSARG(*s))
759 			s++;
760 		if (*s == '(')
761 			s++;
762 		while (WSARG(*s))
763 			s++;
764 #define IDARG(x) ((x>= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') || (x == '_'))
765 #define	NUMARG(x) (x >= '0' && x <= '9')
766 		if (!IDARG(*s))
767 			error("bad defined arg");
768 		bc = s;
769 		while (IDARG(*s) || NUMARG(*s))
770 			s++;
771 		oc = *s;
772 		*s = 0;
773 		*bc = (lookup(bc, FIND) != 0) + '0';
774 		memset(bc+1, ' ', s-bc-1);
775 		*s = oc;
776 	}
777 }
778 
779 /*
780  * get the full line of identifiers after an #if, pushback a WARN and
781  * the line and prepare for expmac() to expand.
782  * This is done before switching state.  When expmac is finished,
783  * pushback the expanded line, change state and call yyparse.
784  */
785 static void
786 storepb(void)
787 {
788 	usch *opb = stringbuf;
789 	int c;
790 
791 	while ((c = input()) != '\n') {
792 		if (c == '/') {
793 			 if ((c = input()) == '*') {
794 				/* ignore comments here whatsoever */
795 				usch *g = stringbuf;
796 				getcmnt();
797 				stringbuf = g;
798 				continue;
799 			} else if (c == '/') {
800 				while ((c = input()) && c != '\n')
801 					;
802 				break;
803 			}
804 			unput(c);
805 			c = '/';
806 		}
807 		savch(c);
808 	}
809 	cunput('\n');
810 	savch(0);
811 	fixdefined(opb); /* XXX can fail if #line? */
812 	cunput(1); /* WARN XXX */
813 	unpstr(opb);
814 	stringbuf = opb;
815 	slow = 1;
816 	expmac(NULL);
817 	slow = 0;
818 	/* line now expanded */
819 	while (stringbuf > opb)
820 		cunput(*--stringbuf);
821 }
822 
823 static void
824 ifstmt(void)
825 {
826 	if (flslvl == 0) {
827 		slow = 1;
828 		if (yyparse())
829 			++trulvl;
830 		else
831 			++flslvl;
832 		slow = 0;
833 	} else
834 		++flslvl;
835 }
836 
837 static void
838 elifstmt(void)
839 {
840 	if (flslvl == 0)
841 		elflvl = trulvl;
842 	if (flslvl) {
843 		if (elflvl > trulvl)
844 			;
845 		else if (--flslvl!=0)
846 			++flslvl;
847 		else {
848 			slow = 1;
849 			if (yyparse()) {
850 				++trulvl;
851 				prtline();
852 			} else
853 				++flslvl;
854 			slow = 0;
855 		}
856 	} else if (trulvl) {
857 		++flslvl;
858 		--trulvl;
859 	} else
860 		error("If-less elif");
861 }
862 
863 static usch *
864 svinp(void)
865 {
866 	int c;
867 	usch *cp = stringbuf;
868 
869 	while ((c = input()) && c != '\n')
870 		savch(c);
871 	savch('\n');
872 	savch(0);
873 	BEGIN 0;
874 	return cp;
875 }
876 
877 static void
878 cpperror(void)
879 {
880 	usch *cp;
881 	int c;
882 
883 	if (flslvl)
884 		return;
885 	c = yylex();
886 	if (c != WSPACE && c != '\n')
887 		error("bad error");
888 	cp = svinp();
889 	if (flslvl)
890 		stringbuf = cp;
891 	else
892 		error("%s", cp);
893 }
894 
895 static void
896 undefstmt(void)
897 {
898 	struct symtab *np;
899 
900 	slow = 1;
901 	if (yylex() != WSPACE || yylex() != IDENT)
902 		error("bad undef");
903 	if (flslvl == 0 && (np = lookup((usch *)yytext, FIND)))
904 		np->value = 0;
905 	slow = 0;
906 	chknl(0);
907 }
908 
909 static void
910 pragmastmt(void)
911 {
912 	int c;
913 
914 	slow = 1;
915 	if (yylex() != WSPACE)
916 		error("bad pragma");
917 	if (!flslvl)
918 		putstr((usch *)"#pragma ");
919 	do {
920 		c = input();
921 		if (!flslvl)
922 			putch(c);	/* Do arg expansion instead? */
923 	} while (c && c != '\n');
924 	ifiles->lineno++;
925 	prtline();
926 	slow = 0;
927 }
928 
929 static void
930 badop(const char *op)
931 {
932 	error("invalid operator in preprocessor expression: %s", op);
933 }
934 
935 int
936 cinput()
937 {
938 	return input();
939 }
940