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