xref: /plan9/sys/src/cmd/cpp/cpp.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
33e12c5d1SDavid du Colombier #include <stdio.h>
43e12c5d1SDavid du Colombier #include <stdarg.h>
53e12c5d1SDavid du Colombier #include "cpp.h"
63e12c5d1SDavid du Colombier 
73e12c5d1SDavid du Colombier #define	OUTS	16384
83e12c5d1SDavid du Colombier char	outbuf[OUTS];
93e12c5d1SDavid du Colombier char	*outp = outbuf;
103e12c5d1SDavid du Colombier Source	*cursource;
113e12c5d1SDavid du Colombier int	nerrs;
123e12c5d1SDavid du Colombier struct	token nltoken = { NL, 0, 0, 0, 1, (uchar*)"\n" };
133e12c5d1SDavid du Colombier char	*curtime;
143e12c5d1SDavid du Colombier int	incdepth;
153e12c5d1SDavid du Colombier int	ifdepth;
163e12c5d1SDavid du Colombier int	ifsatisfied[NIF];
173e12c5d1SDavid du Colombier int	skipping;
183e12c5d1SDavid du Colombier 
19*219b2ee8SDavid du Colombier int
203e12c5d1SDavid du Colombier main(int argc, char **argv)
213e12c5d1SDavid du Colombier {
223e12c5d1SDavid du Colombier 	Tokenrow tr;
233e12c5d1SDavid du Colombier 	long t;
243e12c5d1SDavid du Colombier 	char ebuf[BUFSIZ];
253e12c5d1SDavid du Colombier 
263e12c5d1SDavid du Colombier 	setbuf(stderr, ebuf);
273e12c5d1SDavid du Colombier 	t = time(NULL);
283e12c5d1SDavid du Colombier 	curtime = ctime(t);
293e12c5d1SDavid du Colombier 	maketokenrow(3, &tr);
303e12c5d1SDavid du Colombier 	expandlex();
313e12c5d1SDavid du Colombier 	setup(argc, argv);
323e12c5d1SDavid du Colombier 	fixlex();
333e12c5d1SDavid du Colombier 	iniths();
343e12c5d1SDavid du Colombier 	genline();
353e12c5d1SDavid du Colombier 	process(&tr);
363e12c5d1SDavid du Colombier 	flushout();
373e12c5d1SDavid du Colombier 	fflush(stderr);
383e12c5d1SDavid du Colombier 	exits(nerrs? "errors" : 0);
39*219b2ee8SDavid du Colombier 	return 0;
403e12c5d1SDavid du Colombier }
413e12c5d1SDavid du Colombier 
423e12c5d1SDavid du Colombier void
433e12c5d1SDavid du Colombier process(Tokenrow *trp)
443e12c5d1SDavid du Colombier {
453e12c5d1SDavid du Colombier 	int anymacros = 0;
463e12c5d1SDavid du Colombier 
473e12c5d1SDavid du Colombier 	for (;;) {
483e12c5d1SDavid du Colombier 		if (trp->tp >= trp->lp) {
493e12c5d1SDavid du Colombier 			trp->tp = trp->lp = trp->bp;
503e12c5d1SDavid du Colombier 			outp = outbuf;
513e12c5d1SDavid du Colombier 			anymacros |= gettokens(trp, 1);
523e12c5d1SDavid du Colombier 			trp->tp = trp->bp;
533e12c5d1SDavid du Colombier 		}
543e12c5d1SDavid du Colombier 		if (trp->tp->type == END) {
553e12c5d1SDavid du Colombier 			if (--incdepth>=0) {
563e12c5d1SDavid du Colombier 				if (cursource->ifdepth)
573e12c5d1SDavid du Colombier 					error(ERROR,
583e12c5d1SDavid du Colombier 					 "Unterminated conditional in #include");
593e12c5d1SDavid du Colombier 				unsetsource();
603e12c5d1SDavid du Colombier 				cursource->line += cursource->lineinc;
613e12c5d1SDavid du Colombier 				trp->tp = trp->lp;
623e12c5d1SDavid du Colombier 				genline();
633e12c5d1SDavid du Colombier 				continue;
643e12c5d1SDavid du Colombier 			}
653e12c5d1SDavid du Colombier 			if (ifdepth)
663e12c5d1SDavid du Colombier 				error(ERROR, "Unterminated #if/#ifdef/#ifndef");
673e12c5d1SDavid du Colombier 			break;
683e12c5d1SDavid du Colombier 		}
693e12c5d1SDavid du Colombier 		if (trp->tp->type==SHARP) {
703e12c5d1SDavid du Colombier 			trp->tp += 1;
713e12c5d1SDavid du Colombier 			control(trp);
723e12c5d1SDavid du Colombier 		} else if (!skipping && anymacros)
733e12c5d1SDavid du Colombier 			expandrow(trp, NULL);
743e12c5d1SDavid du Colombier 		if (skipping)
753e12c5d1SDavid du Colombier 			setempty(trp);
763e12c5d1SDavid du Colombier 		puttokens(trp);
773e12c5d1SDavid du Colombier 		anymacros = 0;
783e12c5d1SDavid du Colombier 		cursource->line += cursource->lineinc;
793e12c5d1SDavid du Colombier 		if (cursource->lineinc>1) {
803e12c5d1SDavid du Colombier 			genline();
813e12c5d1SDavid du Colombier 		}
823e12c5d1SDavid du Colombier 	}
833e12c5d1SDavid du Colombier }
843e12c5d1SDavid du Colombier 
853e12c5d1SDavid du Colombier void
863e12c5d1SDavid du Colombier control(Tokenrow *trp)
873e12c5d1SDavid du Colombier {
883e12c5d1SDavid du Colombier 	Nlist *np;
893e12c5d1SDavid du Colombier 	Token *tp;
903e12c5d1SDavid du Colombier 
913e12c5d1SDavid du Colombier 	tp = trp->tp;
923e12c5d1SDavid du Colombier 	if (tp->type!=NAME) {
933e12c5d1SDavid du Colombier 		if (tp->type==NUMBER)
943e12c5d1SDavid du Colombier 			goto kline;
953e12c5d1SDavid du Colombier 		if (tp->type != NL)
963e12c5d1SDavid du Colombier 			error(ERROR, "Unidentifiable control line");
973e12c5d1SDavid du Colombier 		return;			/* else empty line */
983e12c5d1SDavid du Colombier 	}
993e12c5d1SDavid du Colombier 	if ((np = lookup(tp, 0))==NULL || (np->flag&ISKW)==0 && !skipping) {
1003e12c5d1SDavid du Colombier 		error(WARNING, "Unknown preprocessor control %t", tp);
1013e12c5d1SDavid du Colombier 		return;
1023e12c5d1SDavid du Colombier 	}
1033e12c5d1SDavid du Colombier 	if (skipping) {
1043e12c5d1SDavid du Colombier 		switch (np->val) {
1053e12c5d1SDavid du Colombier 		case KENDIF:
1063e12c5d1SDavid du Colombier 			if (--ifdepth<skipping)
1073e12c5d1SDavid du Colombier 				skipping = 0;
1083e12c5d1SDavid du Colombier 			--cursource->ifdepth;
1093e12c5d1SDavid du Colombier 			setempty(trp);
1103e12c5d1SDavid du Colombier 			return;
1113e12c5d1SDavid du Colombier 
1123e12c5d1SDavid du Colombier 		case KIFDEF:
1133e12c5d1SDavid du Colombier 		case KIFNDEF:
1143e12c5d1SDavid du Colombier 		case KIF:
1153e12c5d1SDavid du Colombier 			if (++ifdepth >= NIF)
1163e12c5d1SDavid du Colombier 				error(FATAL, "#if too deeply nested");
1173e12c5d1SDavid du Colombier 			++cursource->ifdepth;
1183e12c5d1SDavid du Colombier 			return;
1193e12c5d1SDavid du Colombier 
1203e12c5d1SDavid du Colombier 		case KELIF:
1213e12c5d1SDavid du Colombier 		case KELSE:
1223e12c5d1SDavid du Colombier 			if (ifdepth<=skipping)
1233e12c5d1SDavid du Colombier 				break;
1243e12c5d1SDavid du Colombier 			return;
1253e12c5d1SDavid du Colombier 
1263e12c5d1SDavid du Colombier 		default:
1273e12c5d1SDavid du Colombier 			return;
1283e12c5d1SDavid du Colombier 		}
1293e12c5d1SDavid du Colombier 	}
1303e12c5d1SDavid du Colombier 	switch (np->val) {
1313e12c5d1SDavid du Colombier 	case KDEFINE:
1323e12c5d1SDavid du Colombier 		dodefine(trp);
1333e12c5d1SDavid du Colombier 		break;
1343e12c5d1SDavid du Colombier 
1353e12c5d1SDavid du Colombier 	case KUNDEF:
1363e12c5d1SDavid du Colombier 		tp += 1;
1373e12c5d1SDavid du Colombier 		if (tp->type!=NAME || trp->lp - trp->bp != 4) {
1383e12c5d1SDavid du Colombier 			error(ERROR, "Syntax error in #undef");
1393e12c5d1SDavid du Colombier 			break;
1403e12c5d1SDavid du Colombier 		}
1413e12c5d1SDavid du Colombier 		if ((np = lookup(tp, 0)))
1423e12c5d1SDavid du Colombier 			np->flag &= ~ISDEFINED;
1433e12c5d1SDavid du Colombier 		break;
1443e12c5d1SDavid du Colombier 
1453e12c5d1SDavid du Colombier 	case KPRAGMA:
1463e12c5d1SDavid du Colombier 		return;
1473e12c5d1SDavid du Colombier 
1483e12c5d1SDavid du Colombier 	case KIFDEF:
1493e12c5d1SDavid du Colombier 	case KIFNDEF:
1503e12c5d1SDavid du Colombier 	case KIF:
151bd389b36SDavid du Colombier 		if (++ifdepth >= NIF)
1523e12c5d1SDavid du Colombier 			error(FATAL, "#if too deeply nested");
1533e12c5d1SDavid du Colombier 		++cursource->ifdepth;
1543e12c5d1SDavid du Colombier 		ifsatisfied[ifdepth] = 0;
1553e12c5d1SDavid du Colombier 		if (eval(trp, np->val))
1563e12c5d1SDavid du Colombier 			ifsatisfied[ifdepth] = 1;
1573e12c5d1SDavid du Colombier 		else
1583e12c5d1SDavid du Colombier 			skipping = ifdepth;
1593e12c5d1SDavid du Colombier 		break;
1603e12c5d1SDavid du Colombier 
1613e12c5d1SDavid du Colombier 	case KELIF:
1623e12c5d1SDavid du Colombier 		if (ifdepth==0) {
1633e12c5d1SDavid du Colombier 			error(ERROR, "#elif with no #if");
1643e12c5d1SDavid du Colombier 			return;
1653e12c5d1SDavid du Colombier 		}
1663e12c5d1SDavid du Colombier 		if (ifsatisfied[ifdepth]==2)
1673e12c5d1SDavid du Colombier 			error(ERROR, "#elif after #else");
1683e12c5d1SDavid du Colombier 		if (eval(trp, np->val)) {
1693e12c5d1SDavid du Colombier 			if (ifsatisfied[ifdepth])
1703e12c5d1SDavid du Colombier 				skipping = ifdepth;
1713e12c5d1SDavid du Colombier 			else {
1723e12c5d1SDavid du Colombier 				skipping = 0;
1733e12c5d1SDavid du Colombier 				ifsatisfied[ifdepth] = 1;
1743e12c5d1SDavid du Colombier 			}
1753e12c5d1SDavid du Colombier 		} else
1763e12c5d1SDavid du Colombier 			skipping = ifdepth;
1773e12c5d1SDavid du Colombier 		break;
1783e12c5d1SDavid du Colombier 
1793e12c5d1SDavid du Colombier 	case KELSE:
1803e12c5d1SDavid du Colombier 		if (ifdepth==0 || cursource->ifdepth==0) {
1813e12c5d1SDavid du Colombier 			error(ERROR, "#else with no #if");
1823e12c5d1SDavid du Colombier 			return;
1833e12c5d1SDavid du Colombier 		}
1843e12c5d1SDavid du Colombier 		if (ifsatisfied[ifdepth]==2)
1853e12c5d1SDavid du Colombier 			error(ERROR, "#else after #else");
1863e12c5d1SDavid du Colombier 		if (trp->lp - trp->bp != 3)
1873e12c5d1SDavid du Colombier 			error(ERROR, "Syntax error in #else");
1883e12c5d1SDavid du Colombier 		skipping = ifsatisfied[ifdepth]? ifdepth: 0;
1893e12c5d1SDavid du Colombier 		ifsatisfied[ifdepth] = 2;
1903e12c5d1SDavid du Colombier 		break;
1913e12c5d1SDavid du Colombier 
1923e12c5d1SDavid du Colombier 	case KENDIF:
1933e12c5d1SDavid du Colombier 		if (ifdepth==0 || cursource->ifdepth==0) {
1943e12c5d1SDavid du Colombier 			error(ERROR, "#endif with no #if");
1953e12c5d1SDavid du Colombier 			return;
1963e12c5d1SDavid du Colombier 		}
1973e12c5d1SDavid du Colombier 		--ifdepth;
1983e12c5d1SDavid du Colombier 		--cursource->ifdepth;
1993e12c5d1SDavid du Colombier 		if (trp->lp - trp->bp != 3)
2003e12c5d1SDavid du Colombier 			error(WARNING, "Syntax error in #endif");
2013e12c5d1SDavid du Colombier 		break;
2023e12c5d1SDavid du Colombier 
2033e12c5d1SDavid du Colombier 	case KERROR:
2043e12c5d1SDavid du Colombier 		trp->tp = tp+1;
2053e12c5d1SDavid du Colombier 		error(WARNING, "#error directive: %r", trp);
2063e12c5d1SDavid du Colombier 		break;
2073e12c5d1SDavid du Colombier 
2083e12c5d1SDavid du Colombier 	case KLINE:
2093e12c5d1SDavid du Colombier 		trp->tp = tp+1;
2103e12c5d1SDavid du Colombier 		expandrow(trp, "<line>");
2113e12c5d1SDavid du Colombier 		tp = trp->bp+2;
2123e12c5d1SDavid du Colombier 	kline:
2133e12c5d1SDavid du Colombier 		if (tp+1>=trp->lp || tp->type!=NUMBER || tp+3<trp->lp
2143e12c5d1SDavid du Colombier 		 || (tp+3==trp->lp && ((tp+1)->type!=STRING)||*(tp+1)->t=='L')){
2153e12c5d1SDavid du Colombier 			error(ERROR, "Syntax error in #line");
2163e12c5d1SDavid du Colombier 			return;
2173e12c5d1SDavid du Colombier 		}
2183e12c5d1SDavid du Colombier 		cursource->line = atol((char*)tp->t)-1;
2193e12c5d1SDavid du Colombier 		if (cursource->line<0 || cursource->line>=32768)
2203e12c5d1SDavid du Colombier 			error(WARNING, "#line specifies number out of range");
2213e12c5d1SDavid du Colombier 		tp = tp+1;
2223e12c5d1SDavid du Colombier 		if (tp+1<trp->lp)
2233e12c5d1SDavid du Colombier 			cursource->filename=(char*)newstring(tp->t+1,tp->len-2,0);
2243e12c5d1SDavid du Colombier 		return;
2253e12c5d1SDavid du Colombier 
2263e12c5d1SDavid du Colombier 	case KDEFINED:
2273e12c5d1SDavid du Colombier 		error(ERROR, "Bad syntax for control line");
2283e12c5d1SDavid du Colombier 		break;
2293e12c5d1SDavid du Colombier 
2303e12c5d1SDavid du Colombier 	case KINCLUDE:
2313e12c5d1SDavid du Colombier 		doinclude(trp);
2323e12c5d1SDavid du Colombier 		trp->lp = trp->bp;
2333e12c5d1SDavid du Colombier 		return;
2343e12c5d1SDavid du Colombier 
2353e12c5d1SDavid du Colombier 	case KEVAL:
2363e12c5d1SDavid du Colombier 		eval(trp, np->val);
2373e12c5d1SDavid du Colombier 		break;
2383e12c5d1SDavid du Colombier 
2393e12c5d1SDavid du Colombier 	default:
2403e12c5d1SDavid du Colombier 		error(ERROR, "Preprocessor control `%t' not yet implemented", tp);
2413e12c5d1SDavid du Colombier 		break;
2423e12c5d1SDavid du Colombier 	}
2433e12c5d1SDavid du Colombier 	setempty(trp);
2443e12c5d1SDavid du Colombier 	return;
2453e12c5d1SDavid du Colombier }
2463e12c5d1SDavid du Colombier 
2473e12c5d1SDavid du Colombier void *
2483e12c5d1SDavid du Colombier domalloc(int size)
2493e12c5d1SDavid du Colombier {
2503e12c5d1SDavid du Colombier 	void *p = malloc(size);
2513e12c5d1SDavid du Colombier 
2523e12c5d1SDavid du Colombier 	if (p==NULL)
2533e12c5d1SDavid du Colombier 		error(FATAL, "Out of memory from malloc");
2543e12c5d1SDavid du Colombier 	return p;
2553e12c5d1SDavid du Colombier }
2563e12c5d1SDavid du Colombier 
2573e12c5d1SDavid du Colombier void
2583e12c5d1SDavid du Colombier dofree(void *p)
2593e12c5d1SDavid du Colombier {
2603e12c5d1SDavid du Colombier 	free(p);
2613e12c5d1SDavid du Colombier }
2623e12c5d1SDavid du Colombier 
2633e12c5d1SDavid du Colombier void
2643e12c5d1SDavid du Colombier error(enum errtype type, char *string, ...)
2653e12c5d1SDavid du Colombier {
2663e12c5d1SDavid du Colombier 	va_list ap;
2673e12c5d1SDavid du Colombier 	char *cp, *ep;
2683e12c5d1SDavid du Colombier 	Token *tp;
2693e12c5d1SDavid du Colombier 	Tokenrow *trp;
270*219b2ee8SDavid du Colombier 	Source *s;
2713e12c5d1SDavid du Colombier 	int i;
2723e12c5d1SDavid du Colombier 
2733e12c5d1SDavid du Colombier 	fprintf(stderr, "cpp: ");
274*219b2ee8SDavid du Colombier 	for (s=cursource; s; s=s->next)
275*219b2ee8SDavid du Colombier 		if (*s->filename)
276*219b2ee8SDavid du Colombier 			fprintf(stderr, "%s:%d ", s->filename, s->line);
2773e12c5d1SDavid du Colombier 	va_start(ap, string);
2783e12c5d1SDavid du Colombier 	for (ep=string; *ep; ep++) {
2793e12c5d1SDavid du Colombier 		if (*ep=='%') {
2803e12c5d1SDavid du Colombier 			switch (*++ep) {
2813e12c5d1SDavid du Colombier 
2823e12c5d1SDavid du Colombier 			case 's':
2833e12c5d1SDavid du Colombier 				cp = va_arg(ap, char *);
2843e12c5d1SDavid du Colombier 				fprintf(stderr, "%s", cp);
2853e12c5d1SDavid du Colombier 				break;
2863e12c5d1SDavid du Colombier 			case 'd':
2873e12c5d1SDavid du Colombier 				i = va_arg(ap, int);
2883e12c5d1SDavid du Colombier 				fprintf(stderr, "%d", i);
2893e12c5d1SDavid du Colombier 				break;
2903e12c5d1SDavid du Colombier 			case 't':
2913e12c5d1SDavid du Colombier 				tp = va_arg(ap, Token *);
2923e12c5d1SDavid du Colombier 				fprintf(stderr, "%.*s", tp->len, tp->t);
2933e12c5d1SDavid du Colombier 				break;
2943e12c5d1SDavid du Colombier 
2953e12c5d1SDavid du Colombier 			case 'r':
2963e12c5d1SDavid du Colombier 				trp = va_arg(ap, Tokenrow *);
2973e12c5d1SDavid du Colombier 				for (tp=trp->tp; tp<trp->lp&&tp->type!=NL; tp++) {
2983e12c5d1SDavid du Colombier 					if (tp>trp->tp && tp->wslen)
2993e12c5d1SDavid du Colombier 						fputc(' ', stderr);
3003e12c5d1SDavid du Colombier 					fprintf(stderr, "%.*s", tp->len, tp->t);
3013e12c5d1SDavid du Colombier 				}
3023e12c5d1SDavid du Colombier 				break;
3033e12c5d1SDavid du Colombier 
3043e12c5d1SDavid du Colombier 			default:
3053e12c5d1SDavid du Colombier 				fputc(*ep, stderr);
3063e12c5d1SDavid du Colombier 				break;
3073e12c5d1SDavid du Colombier 			}
3083e12c5d1SDavid du Colombier 		} else
3093e12c5d1SDavid du Colombier 			fputc(*ep, stderr);
3103e12c5d1SDavid du Colombier 	}
3113e12c5d1SDavid du Colombier 	va_end(ap);
3123e12c5d1SDavid du Colombier 	fputc('\n', stderr);
3133e12c5d1SDavid du Colombier 	if (type==FATAL)
3143e12c5d1SDavid du Colombier 		exits("error");
3153e12c5d1SDavid du Colombier 	if (type!=WARNING)
3163e12c5d1SDavid du Colombier 		nerrs = 1;
3173e12c5d1SDavid du Colombier 	fflush(stderr);
3183e12c5d1SDavid du Colombier }
319