scc

A fork of https://git.simple-cc.org/scc/ for Qute

git clone https://git.8pit.net/scc.git

   1#include <assert.h>
   2#include <ctype.h>
   3#include <limits.h>
   4#include <stdio.h>
   5#include <stdlib.h>
   6#include <string.h>
   7#include <time.h>
   8
   9#include <scc/cstd.h>
  10#include <scc/scc.h>
  11#include "cc1.h"
  12
  13struct ifstate {
  14	unsigned char done;
  15	unsigned char enabled;
  16	unsigned char iselse;
  17};
  18
  19static unsigned ncmdlines;
  20static Symbol *symline, *symfile;
  21static struct ifstate ifstate[NR_COND];
  22static int cppoff;
  23static struct items dirinclude;
  24
  25unsigned cppctx;
  26int disexpand;
  27
  28void
  29defdefine(char *name, char *val, char *source)
  30{
  31	char buffer[INPUTSIZ+1];
  32	char *def, *fmt = "#define %s %s\n";
  33	Symbol *sym = &(Symbol) {
  34		.name = name,
  35		.flags = SDECLARED,
  36	};
  37
  38	if (!val)
  39		val = "";
  40	if (strlen(fmt) + strlen(name) + strlen(val) > INPUTSIZ) {
  41		errorp("macro definition '%s' too big", name);
  42		return;
  43	}
  44
  45	sprintf(buffer, fmt, name, val);
  46	lineno = ++ncmdlines;
  47
  48	addinput(IPARAM, buffer, FAIL);
  49	cpp();
  50	delinput();
  51}
  52
  53void
  54undefmacro(char *s)
  55{
  56	killsym(lookup(NS_CPP, s, NOALLOC));
  57}
  58
  59void
  60icpp(void)
  61{
  62	struct tm *tm;
  63	time_t t;
  64	static char sdate[14], stime[11];
  65	static struct {
  66		char *name;
  67		char *value;
  68	} *bp, list[] = {
  69		{"__STDC__", "1"},
  70		{"__STDC_HOSTED__", "1"},
  71		{"__SCC__", "1"},
  72		{"__DATE__", sdate},
  73		{"__TIME__", stime},
  74		{"__STDC_VERSION__", STDC_VERSION},
  75		{"__LINE__", NULL},
  76		{"__FILE__", NULL},
  77		{"__FLT_EVAL_METHOD__", "0"},
  78		{NULL, NULL}
  79	};
  80
  81	t = time(NULL);
  82	tm = localtime(&t);
  83	strftime(sdate, sizeof(sdate), "\"%b %d %Y\"", tm);
  84	strftime(stime, sizeof(stime), "\"%H:%M:%S\"", tm);
  85
  86	for (bp = list; bp->name; ++bp)
  87		defdefine(bp->name, bp->value, "built-in");
  88
  89	symline = lookup(NS_CPP, "__LINE__", ALLOC);
  90	symfile = lookup(NS_CPP, "__FILE__", ALLOC);
  91
  92	ncmdlines = 0;
  93}
  94
  95static char *
  96appendpar(Macro *mp, char *s, int len)
  97{
  98	char *arg;
  99	int siz = mp->argsiz;
 100
 101	if (len+1 > INT_MAX - siz) {
 102		error("too long argument invoking macro \"%s\"",
 103		      mp->sym->name);
 104	}
 105
 106	mp->arg = xrealloc(mp->arg, siz + len + 1);
 107	memcpy(mp->arg + siz, s, len);
 108	mp->argsiz = siz += len;
 109	mp->arg[siz] = '\0';
 110}
 111
 112static void
 113paren(Macro *mp)
 114{
 115	for (;;) {
 116		switch (*input->p) {
 117		case ')':
 118			appendpar(mp, input->p++, 1);
 119			return;
 120		case '(':
 121			appendpar(mp, input->p++, 1);
 122			paren(mp);
 123			break;
 124		case '"':
 125		case '\'':
 126			next();
 127			assert(yytoken == STRING || yytoken == CONSTANT);
 128			appendpar(mp, yytext, yylen);
 129			break;
 130		case '\0':
 131			if (!moreinput())
 132				goto unterminated;
 133			continue;
 134		default:
 135			appendpar(mp, input->p++, 1);
 136			break;
 137		}
 138	}
 139
 140unterminated:
 141	error("unterminated argument list invoking macro \"%s\"",
 142	      mp->sym->name);
 143}
 144
 145static int
 146cppspace(int c)
 147{
 148	return c == ' ' || c == '\t' || c == '\v' ||
 149	       c == '\n' || c == '\f' || c == '\r';
 150}
 151
 152static char *
 153parameter(Macro *mp, int n)
 154{
 155	int siz;
 156	char *s, *begin, *end;
 157
 158	mp->arg = NULL;
 159	mp->argsiz = 0;
 160
 161	for (;;) {
 162		switch (*input->p) {
 163		case '"':
 164		case '\'':
 165			next();
 166			assert(yytoken == STRING || yytoken == CONSTANT);
 167			appendpar(mp, yytext, yylen);
 168			break;
 169		case '(':
 170			appendpar(mp, input->p++, 1);
 171			paren(mp);
 172			break;
 173		case ',':
 174			if (n+1 == mp->npars)
 175				goto append;
 176		case ')':
 177			begin = mp->arg;
 178			end = begin + mp->argsiz;
 179
 180			while (begin < end && cppspace(*begin))
 181				begin++;
 182			while (end > begin && cppspace(end[-1]))
 183				end--;
 184
 185			siz = end - begin;
 186			s = memcpy(xmalloc(siz + 1), begin, siz);
 187			s[siz] = '\0';
 188
 189			return s;
 190		case '\0':
 191			if (!moreinput())
 192				goto unterminated;
 193			continue;
 194		default:
 195		append:
 196			appendpar(mp, input->p++, 1);
 197			break;
 198		}
 199	}
 200
 201unterminated:
 202	error("unterminated argument list invoking macro \"%s\"",
 203	      mp->sym->name);
 204}
 205
 206static int
 207parsepars(Macro *mp)
 208{
 209	int c, n;
 210	char *name;
 211
 212	if (mp->npars == -1)
 213		return 1;
 214	if (ahead() != '(')
 215		return 0;
 216
 217	input->p++;
 218	name = mp->sym->name;
 219
 220	if (mp->npars == 0 && ahead() == ')') {
 221		input->p++;
 222		return 1;
 223	}
 224
 225	disexpand = 1;
 226	for (n = 0; n < NR_MACROARG; ) {
 227		mp->arglist = xrealloc(mp->arglist, (n+1)*sizeof(char *));
 228		mp->arglist[n] = parameter(mp, n);
 229		DBG("MACRO fetched arg '%s'", mp->arglist[n]);
 230		n++;
 231
 232		c = *input->p++;
 233		if (c == ')')
 234			break;
 235		if (c != ',')
 236			error("incorrect macro function-alike invocation of \"%s\"", name);
 237	}
 238	disexpand = 0;
 239
 240	if (n == mp->npars)
 241		return 1;
 242
 243	if (n == mp->npars-1 && mp->sym->flags & SVARIADIC) {
 244		DBG("MACRO defaulted __VA_ARGS__ to \"\"");
 245		mp->arglist = xrealloc(mp->arglist, (n+1)*sizeof(char *));
 246		mp->arglist[n] = xstrdup("");
 247		return 1;
 248	}
 249
 250	error("macro \"%s\" received %d arguments, but it takes %d",
 251	      mp->sym->name, n, mp->npars);
 252}
 253
 254static int
 255concatoper(char *def, char *cur)
 256{
 257	char *s;
 258
 259	for (s = cur + 4; cppspace(*s); ++s)
 260		;
 261	if (*s == CONCAT)
 262		return 1;
 263
 264	for (s = cur; s > def && cppspace(s[-1]); --s)
 265		;
 266	if (s > def && s[-1] == CONCAT)
 267		return 1;
 268
 269	return 0;
 270}
 271
 272static int
 273stringoper(char **bpp, int *sizep, char *arg)
 274{
 275	int c, esc, delim, siz;
 276	char *bp;
 277
 278	bp = *bpp;
 279	siz = *sizep;
 280
 281	if (siz < 3)
 282		return 0;
 283
 284	esc = delim = 0;
 285
 286	*bp++ = '"';
 287	while ((c = *arg++) != '\0') {
 288		switch (c) {
 289		case '\'':
 290		case '"':
 291			if (!esc) {
 292				if (!delim)
 293					delim = c;
 294				else if (delim == c)
 295					delim = 0;
 296			}
 297
 298			if (c == '"') {
 299				if (siz < 4)
 300					return 0;
 301				*bp++ = '\\';
 302				siz--;
 303			} else {
 304				if (siz < 3)
 305					return 0;
 306			}
 307
 308			*bp++ = c;
 309			siz--;
 310			break;
 311		case '\\':
 312			if (!delim)
 313				goto copy;
 314			if (siz < 4)
 315				return 0;
 316			*bp++ = '\\';
 317			*bp++ = '\\';
 318			siz -= 2;
 319
 320			if (!esc) {
 321				esc = 1;
 322				continue;
 323			}
 324			break;
 325		default:
 326			if (!delim && cppspace(c)) {
 327				while (cppspace(*arg))
 328					++arg;
 329				c = ' ';
 330			}
 331		copy:
 332			if (siz < 3)
 333				return 0;
 334			*bp++ = c;
 335			siz--;
 336			break;
 337		}
 338		esc = 0;
 339	}
 340	*bp++ = '"';
 341
 342	*bpp = bp;
 343	*sizep = siz;
 344
 345	return 1;
 346}
 347
 348static int
 349expandarg(char *arg, char *def, char *curdef, char *buf, int bufsiz)
 350{
 351	int siz;
 352	char *s = buf;
 353
 354	/* gives priority to concatenation operators */
 355	if (concatoper(def, curdef)) {
 356		char *p;
 357		int c;
 358
 359		/*
 360		 * ignore NOEXPAND and EXPAND because ## has
 361		 * precedence over macro expansion, and we just
 362		 * want the argument as it is
 363		 */
 364		for (siz = 0, p = arg; (c = *p) != '\0'; p++) {
 365			if (c == NOEXPAND || c == EXPAND)
 366				continue;
 367			if (siz == bufsiz - 1)
 368				return -1;
 369			*buf++ = c;
 370			siz++;
 371		}
 372		*buf = '\0';
 373	} else {
 374		int n;
 375		char noex[2] = {0, 0}, ex[2] = {0, 0};
 376
 377		/*
 378		 * Here we have to deal with a special case,
 379		 * because the recursive input expansion deals
 380		 * correctly with the hideset manipulation,
 381		 * but once the macro expansion is flattened in
 382		 * a buffer associated to the macro expansion
 383		 * all the hideset information is lost. To solve
 384		 * this problem we have to add annotation tokens
 385		 * to maintain this information and avoid expanding
 386		 * macros hidden due to the recursive hidesets
 387		 */
 388		addinput(IPARAM, arg, FAIL);
 389		for (siz = 0; ; siz += n) {
 390			yyspace = noex[0] = ex[0] = '\0';
 391			if (next() == EOFTOK)
 392				break;
 393
 394			if (yytoken == IDEN) {
 395				Symbol *sym = yylval.cppsym;
 396				if (disexpand || sym && sym->hide) {
 397					noex[0] = NOEXPAND;
 398					ex[0] = EXPAND;
 399				}
 400			}
 401
 402			n = snprintf(buf, bufsiz, "%s%s%s%s",
 403			             yyspace ? " " : "", noex, yytext, ex);
 404
 405			if (n < 0 || n == bufsiz)
 406				return -1;
 407
 408			buf += n;
 409			bufsiz -= n;
 410		}
 411		delinput();
 412	}
 413
 414	DBG("MACRO parameter '%s' expanded to '%s'", arg, s);
 415
 416	return siz;
 417}
 418
 419static int
 420copymacro(Macro *mp)
 421{
 422	int delim, c, esc;
 423	char *s, *p, *arg, *bp;
 424	int size, bufsiz;
 425
 426	if (mp->sym == symfile)
 427		return sprintf(mp->buffer, "\"%s\" ", filenam);
 428	if (mp->sym == symline)
 429		return sprintf(mp->buffer, "%d ", lineno);
 430
 431	bp = mp->buffer;
 432	bufsiz = mp->bufsiz;
 433	for (s = mp->def; c = *s; ++s) {
 434		switch (c) {
 435		case '\'':
 436			delim = '\'';
 437			goto search_delim;
 438		case '\"':
 439			delim = '"';
 440		search_delim:
 441			esc = 0;
 442			p = s;
 443			for (++s; c = *s; ++s) {
 444				if (c == '\\' && !esc)
 445					esc = 1;
 446				else if (c == delim &&!esc)
 447					break;
 448				else
 449					esc = 0;
 450			}
 451			size = s - p + 1;
 452			if (size > bufsiz)
 453				goto expansion_too_long;
 454			memcpy(bp, p, size);
 455			bufsiz -= size;
 456			bp += size;
 457			break;
 458		case CONCAT:
 459			/* token concatenation operator */
 460			DBG("MACRO concat");
 461			while (cppspace(bp[-1]))
 462				--bp, ++bufsiz;
 463			while (cppspace(s[1]))
 464				++s;
 465			break;
 466		case STRINGIZE:
 467			/* stringfier operator */
 468			DBG("MACRO stringize");
 469			arg = mp->arglist[atoi(s += 2)];
 470			s += 2;
 471
 472			if (!stringoper(&bp, &bufsiz, arg))
 473				goto expansion_too_long;
 474			break;
 475		case MACROPAR:
 476			/* parameter substitution */
 477			arg = mp->arglist[atoi(s+1)];
 478			size = expandarg(arg, mp->def, s, bp, bufsiz);
 479			if (size < 0)
 480				goto expansion_too_long;
 481			bp += size;
 482			bufsiz -= size;
 483			s += 3;
 484			break;
 485		default:
 486			if (bufsiz-- == 0)
 487				goto expansion_too_long;
 488			*bp++ = c;
 489			break;
 490		}
 491	}
 492	*bp = '\0';
 493
 494	return bp - mp->buffer;
 495
 496expansion_too_long:
 497	error("macro expansion of \"%s\" too long", mp->sym->name);
 498}
 499
 500/*
 501 * Functions to deal with macro hidesets. Yes, this sucks, a lot,
 502 * but before blaming me read
 503 * https://www.spinellis.gr/blog/20060626/cpp.algo.pdf
 504 * It is not my fault.
 505 */
 506static void
 507addhideset(Symbol *sym)
 508{
 509	Symbol **set, **p;
 510
 511	set = input->hideset;
 512	for (p = set; p < &set[NR_MACROARG-1] && *p; ++p) {
 513		if (*p == sym)
 514			return;
 515	}
 516
 517	if (p == &set[NR_MACROARG])
 518		error("too complex macro expansion");
 519
 520	*p = sym;
 521	DBG("MACRO Adding %s to hideset of %s",
 522	    sym->name, input->macro->sym->name);
 523}
 524
 525static void
 526hide(Symbol *sym)
 527{
 528	DBG("SYM: hidding symbol %s %d", sym->name, sym->hide);
 529	sym->hide = 1;
 530}
 531
 532static void
 533unhide(Symbol *sym)
 534{
 535	DBG("SYM: unhidding symbol %s %d", sym->name, sym->hide);
 536	sym->hide = 0;
 537}
 538
 539void
 540unsethideset(Input *ip)
 541{
 542	Symbol **p;
 543
 544	for (p = ip->hideset; *p; ++p)
 545		unhide(*p);
 546}
 547
 548void
 549sethideset(Input *ip)
 550{
 551	Symbol **p;
 552
 553	for (p = ip->hideset; *p; ++p)
 554		hide(*p);
 555}
 556
 557void
 558delmacro(Macro *mp)
 559{
 560	int i;
 561	Symbol **p;
 562
 563	if (!mp)
 564		return;
 565
 566	if (mp->arglist) {
 567		for (i = 0; i < mp->npars; i++)
 568			free(mp->arglist[i]);
 569	}
 570
 571	free(mp->arglist);
 572	free(mp);
 573}
 574
 575Macro *
 576newmacro(Symbol *sym)
 577{
 578	Macro *mp;
 579
 580	mp = memset(xmalloc(sizeof(*mp)), 0, sizeof(*mp));
 581	mp->sym = sym;
 582	mp->def = sym->u.s + 3;
 583	if (sym->u.s)
 584		mp->npars = atoi(sym->u.s);
 585
 586	return mp;
 587}
 588
 589static void
 590intersect(Symbol **dst, Symbol **set)
 591{
 592	Symbol *sym, **cur, **p;
 593
 594	for (cur = dst; sym = *cur; ++cur) {
 595		for (p = set; *p && *p != sym; ++p)
 596			;
 597		if (*p)
 598			*dst++ = sym;
 599	}
 600
 601	while (dst != cur)
 602		*dst++ = NULL;
 603}
 604
 605int
 606expand(Symbol *sym)
 607{
 608	int spc, siz;
 609	Macro *mp;
 610
 611	DBG("MACRO '%s' detected disexpand=%d hide=%d",
 612	    sym->name, disexpand, sym->hide);
 613
 614	if (disexpand || sym->hide || sym->token != IDEN)
 615		return 0;
 616
 617	spc = yyspace;
 618	mp = newmacro(sym);
 619	mp->fname = filenam;
 620	memcpy(mp->hideset, input->hideset, sizeof(mp->hideset));
 621
 622	if (!parsepars(mp)) {
 623		delmacro(mp);
 624		return 0;
 625	}
 626
 627	if (mp->npars != -1)
 628		intersect(mp->hideset, input->hideset);
 629	addinput(IMACRO, mp, FAIL);
 630	addhideset(sym);
 631	hide(sym);
 632
 633	mp->buffer = input->line;
 634	mp->bufsiz = INPUTSIZ-1;
 635
 636	siz = copymacro(mp);
 637	mp->buffer[siz] = '\0';
 638	yyspace = spc;
 639
 640	DBG("MACRO '%s' expanded to :'%s'", mp->sym->name, mp->buffer);
 641
 642	return 1;
 643}
 644
 645static int
 646getpars(Symbol *args[NR_MACROARG])
 647{
 648	int n, c;
 649	Symbol *sym;
 650
 651	if (*input->p != '(')
 652		return -1;
 653
 654	/* skip the '(' */
 655	next();
 656	next();
 657	if (yytoken == ')')
 658		return 0;
 659
 660	n = 0;
 661	do {
 662		if (n == NR_MACROARG) {
 663			cpperror("too many parameters in macro");
 664			return NR_MACROARG;
 665		}
 666
 667		if (yytoken == ELLIPSIS) {
 668			sym = newsym(NS_MACROPAR, "__VA_ARGS__");
 669		} else if (yytoken == IDEN) {
 670			sym = yylval.sym;
 671			next();
 672		} else {
 673			cpperror("macro arguments must be identifiers");
 674			return NR_MACROARG;
 675		}
 676
 677		if (!install(NS_MACROPAR, sym)) {
 678			errorp("duplicated macro parameter '%s'", sym->name);
 679		} else {
 680			sym->flags |= SUSED;
 681			args[n++] = sym;
 682		}
 683
 684		if (accept(ELLIPSIS))
 685			break;
 686
 687	} while (accept(','));
 688
 689	if (yytoken != ')') {
 690		cpperror("expected ')' at the end of macro argument list");
 691		return NR_MACROARG;
 692	}
 693
 694	return n;
 695}
 696
 697static int
 698getdefs(Symbol *args[NR_MACROARG], int nargs, char *buffer, size_t bufsiz)
 699{
 700	Symbol **argp;
 701	char c, *bp, *s, *p;
 702	int len, id, token, prevc, ispar;
 703
 704	while (cppspace(*input->p))
 705		++input->p;
 706
 707	bp = buffer;
 708	for (prevc = 0; (c = *input->p) != '\n' && c != '\0'; ++input->p) {
 709		len = 1;
 710		ispar = 0;
 711		s = &c;
 712		token = c;
 713
 714		if (c == '#') {
 715			if (input->p[1] == '#') {
 716				c = token = CONCAT;
 717				++input->p;
 718			} else {
 719				c = token = STRINGIZE;
 720				while (cppspace(input->p[1]))
 721					++input->p;
 722			}
 723		} else if (c == '"' || c == '\'' || c == '_' || isalpha(c)) {
 724			next();
 725			assert(yytoken == STRING ||
 726			       yytoken == CONSTANT ||
 727			       yytoken == IDEN);
 728			token = yytoken;
 729			s = yytext;
 730			len = yylen;
 731			--input->p;
 732		}
 733
 734		if (token == IDEN && nargs > 0) {
 735			for (argp = args; argp < &args[nargs]; ++argp) {
 736				if (*argp == yylval.sym) {
 737					id = argp - args;
 738					sprintf(yytext,
 739						"%c%02d%c", MACROPAR, id, MACROPAR);
 740					ispar = 1;
 741					yylen = len = 4;
 742					break;
 743				}
 744			}
 745		}
 746
 747		if (prevc == 0 && token == CONCAT)
 748			goto wrong_concat;
 749
 750		if (prevc == STRINGIZE && !ispar) {
 751			cpperror("'#' is not followed by a macro parameter");
 752			return 0;
 753		}
 754
 755		if (len >= bufsiz) {
 756			cpperror("macro too long");
 757			return 0;
 758		}
 759
 760		memcpy(bp, s, len);
 761		bp += len;
 762		bufsiz -= len;
 763		prevc = token;
 764	}
 765
 766end_loop:
 767	if ((yytoken = c) == '\0')
 768		yytoken = EOFTOK;
 769	if (prevc == CONCAT)
 770		goto wrong_concat;
 771	for ( ; bp > buffer && cppspace(bp[-1]); --bp);
 772		;
 773	*bp = '\0';
 774	return 1;
 775
 776wrong_concat:
 777	cpperror("'##' cannot appear at either ends of a macro expansion");
 778	return 0;
 779}
 780
 781static void
 782define(void)
 783{
 784	int n;
 785	Symbol *sym,*args[NR_MACROARG];
 786	char buff[LINESIZ+1];
 787	unsigned flags = SDECLARED|SSTRING;
 788
 789	if (cppoff)
 790		return;
 791
 792	namespace = NS_CPP;
 793	next();
 794
 795	if (yytoken != IDEN) {
 796		cpperror("macro names must be identifiers");
 797		return;
 798	}
 799	sym = yylval.sym;
 800
 801	namespace = NS_MACROPAR;   /* Avoid polution in NS_CPP */
 802	if ((n = getpars(args)) == NR_MACROARG)
 803		goto delete;
 804	if (n > 0 && strcmp(args[n-1]->name, "__VA_ARGS__") == 0)
 805		flags |= SVARIADIC;
 806
 807	sprintf(buff, "%02d#", n);
 808	if (!getdefs(args, n, buff+3, LINESIZ-3))
 809		goto delete;
 810
 811	if (sym->flags & SDECLARED) {
 812		if (strcmp(sym->u.s, buff) != 0)
 813			warn("'%s' redefined", sym->name);
 814		free(sym->u.s);
 815	} else {
 816		sym = install(NS_CPP, sym);
 817		sym->flags |= flags;
 818	}
 819
 820	sym->u.s = xstrdup(buff);
 821	DBG("MACRO '%s' defined as '%s'", sym->name, buff);
 822	return;
 823
 824delete:
 825	killsym(sym);
 826}
 827
 828void
 829incdir(char *dir)
 830{
 831	if (!dir || *dir == '\0')
 832		die("cc1: incorrect -I flag");
 833	newitem(&dirinclude, dir);
 834}
 835
 836static int
 837includefile(char *dir, char *file, size_t filelen)
 838{
 839	size_t dirlen;
 840	char path[FILENAME_MAX];
 841
 842	if (!dir) {
 843		dirlen = 0;
 844		if (filelen > FILENAME_MAX-1)
 845			return 0;
 846	} else {
 847		dirlen = strlen(dir);
 848		if (dirlen + filelen > FILENAME_MAX-2)
 849			return 0;
 850		memcpy(path, dir, dirlen);
 851		if (dir[dirlen-1] != '/')
 852			path[dirlen++] = '/';
 853	}
 854	memcpy(path+dirlen, file, filelen);
 855	path[dirlen + filelen] = '\0';
 856
 857	return addinput(IFILE, path, NOFAIL);
 858}
 859
 860static char *
 861cwd(char *buf)
 862{
 863	char *p, *s = filenam;
 864	size_t len;
 865
 866	if ((p = strrchr(s, '/')) == NULL)
 867		return NULL;
 868	if ((len = p - s) >= FILENAME_MAX)
 869		die("cc1: current work directory too long");
 870	memcpy(buf, s, len);
 871	buf[len] = '\0';
 872	return buf;
 873}
 874
 875static void
 876include(void)
 877{
 878	char dir[FILENAME_MAX], file[FILENAME_MAX], *p, **bp;
 879	size_t filelen;
 880	int n;
 881
 882	if (cppoff)
 883		return;
 884
 885	disexpand = 0;
 886	namespace = NS_IDEN;
 887	next();
 888
 889	switch (*yytext) {
 890	case '<':
 891		if ((p = strchr(input->begin, '>')) == NULL || p[-1] == '<')
 892			goto bad_include;
 893		filelen = p - input->begin;
 894		if (filelen >= FILENAME_MAX)
 895			goto too_long;
 896		memcpy(file, input->begin, filelen);
 897		file[filelen] = '\0';
 898
 899		input->begin = input->p = p+1;
 900		if (next() != '\n')
 901			goto trailing_characters;
 902
 903		break;
 904	case '"':
 905		if (yylen < 3)
 906			goto bad_include;
 907		filelen = yylen-2;
 908		if (filelen >= FILENAME_MAX)
 909			goto too_long;
 910		memcpy(file, yytext+1, filelen);
 911		file[filelen] = '\0';
 912
 913		if (next() != '\n')
 914			goto trailing_characters;
 915
 916		if (includefile(cwd(dir), file, filelen))
 917			goto its_done;
 918		break;
 919	default:
 920		goto bad_include;
 921	}
 922
 923	n = dirinclude.n;
 924	for (bp = dirinclude.s; n--; ++bp) {
 925		if (includefile(*bp, file, filelen))
 926			goto its_done;
 927	}
 928	cpperror("included file '%s' not found", file);
 929
 930its_done:
 931	return;
 932
 933trailing_characters:
 934	cpperror("trailing characters after preprocessor directive");
 935	return;
 936
 937too_long:
 938	cpperror("too long file name in #include");
 939	return;
 940
 941bad_include:
 942	cpperror("#include expects \"FILENAME\" or <FILENAME>");
 943	return;
 944}
 945
 946static void
 947line(void)
 948{
 949	long n;
 950	char *endp, *fname;
 951
 952	if (cppoff)
 953		return;
 954
 955	disexpand = 0;
 956	next();
 957	n = strtol(yytext, &endp, 10);
 958	if (n <= 0 || n > USHRT_MAX || *endp != '\0') {
 959		cpperror("first parameter of #line is not a positive integer");
 960		return;
 961	}
 962
 963	next();
 964	if (yytoken == '\n') {
 965		fname = NULL;
 966	} else {
 967		if (*yytext != '\"' || yylen == 1) {
 968			cpperror("second parameter of #line is not a valid filename");
 969			return;
 970		}
 971		fname = yylval.sym->u.s;
 972	}
 973	setloc(fname, n - 1);
 974	if (yytoken != '\n')
 975		next();
 976}
 977
 978static void
 979pragma(void)
 980{
 981	if (cppoff)
 982		return;
 983	next();
 984	warn("ignoring pragma '%s'", yytext);
 985	*input->p = '\0';
 986	next();
 987}
 988
 989static void
 990usererr(void)
 991{
 992	if (cppoff)
 993		return;
 994	cpperror("#error %s", input->p);
 995	exit(EXIT_FAILURE);
 996	next();
 997}
 998
 999
1000Node *
1001defined(void)
1002{
1003	Symbol *sym;
1004	int paren;
1005
1006	disexpand = 1;
1007	next();
1008	paren = accept('(');
1009	if (yytoken != IDEN && yytoken != TYPEIDEN)
1010		cpperror("operator 'defined' requires an identifier");
1011	if (yytoken == TYPEIDEN || !(yylval.sym->flags & SDECLARED))
1012		sym = zero;
1013	else
1014		sym = one;
1015	disexpand = 0;
1016	next();
1017	if (paren)
1018		expect(')');
1019	return constnode(sym);
1020}
1021
1022static void
1023ifclause(int negate, int isifdef)
1024{
1025	Symbol *sym;
1026	unsigned n;
1027	int enabled, done;
1028	Node *expr;
1029
1030	if (cppctx == NR_COND-1)
1031		error("too many nesting levels of conditional inclusion");
1032	n = cppctx++;
1033	DBG("CPP ifclause updates cppctx=%d", cppctx);
1034
1035	if (n > 0 && !ifstate[n-1].enabled) {
1036		done = 1;
1037		enabled = 0;
1038		goto disabled;
1039	}
1040
1041	namespace = NS_CPP;
1042	next();
1043
1044	if (isifdef) {
1045		if (yytoken != IDEN) {
1046			cpperror("no macro name given in #%s directive",
1047			         (negate) ? "ifndef" : "ifdef");
1048			return;
1049		}
1050		sym = yylval.sym;
1051		next();
1052		enabled = (sym->flags & SDECLARED) != 0;
1053		if (!enabled)
1054			killsym(sym);
1055	} else {
1056		/* TODO: catch recovery here */
1057		if ((expr = iconstexpr()) == NULL) {
1058			cpperror("parameter of #if is not an integer constant expression");
1059			return;
1060		}
1061		DBG("CPP if expr=%d", expr->sym->u.i);
1062		enabled = expr->sym->u.i != 0;
1063		freetree(expr);
1064	}
1065
1066	if (negate)
1067		enabled = !enabled;
1068	done = enabled;
1069
1070disabled:
1071	cppoff = !enabled;
1072	DBG("CPP if result=%d", enabled);
1073	ifstate[n].done = done;
1074	ifstate[n].enabled = enabled;
1075	ifstate[n].iselse = 0;
1076}
1077
1078static void
1079cppif(void)
1080{
1081	DBG("CPP line=%u if cppctx=%d", lineno, cppctx);
1082	disexpand = 0;
1083	ifclause(0, 0);
1084}
1085
1086static void
1087ifdef(void)
1088{
1089	DBG("CPP line=%u ifdef cppctx=%d", lineno, cppctx);
1090	ifclause(0, 1);
1091}
1092
1093static void
1094ifndef(void)
1095{
1096	DBG("CPP line=%u ifndef cppctx=%d", lineno, cppctx);
1097	ifclause(1, 1);
1098}
1099
1100static void
1101cppelse(void)
1102{
1103	DBG("CPP line=%u else cppctx=%d", lineno, cppctx);
1104
1105	if (cppctx == 0 || ifstate[cppctx-1].iselse) {
1106		cpperror("#else without #ifdef/ifndef");
1107		return;
1108	}
1109
1110	/*
1111	 * If we are disabled by a upper ifdef then ifclause() already
1112	 * marked us as disabled and done. So if we are done then we
1113	 * disable cpp because or ifclause was true, or it was disabled
1114	 * by the upper. If we are not done, then it is our turn.
1115	 */
1116	if (ifstate[cppctx-1].done) {
1117		ifstate[cppctx-1].enabled = 0;
1118		cppoff = 1;
1119	} else {
1120		ifstate[cppctx-1].done = 1;
1121		ifstate[cppctx-1].enabled = 1;
1122		cppoff = 0;
1123	}
1124	ifstate[cppctx-1].iselse = 1;
1125
1126	next();
1127}
1128
1129static void
1130elif(void)
1131{
1132	DBG("CPP line=%u elif cppctx=%d", lineno, cppctx);
1133
1134	if (cppctx == 0 || ifstate[cppctx-1].iselse) {
1135		cpperror("#elif without #ifdef/ifndef");
1136		return;
1137	}
1138
1139	/*
1140	 * If we are disabled by a upper ifdef then ifclause() already
1141	 * marked us as disabled and done. So if we are done then we
1142	 * disable cpp because or ifclause was true, or it was disabled
1143	 * by the upper. If we are not done, then we have to evaluate
1144	 * the if condition.
1145	 */
1146	if (ifstate[cppctx-1].done) {
1147		ifstate[cppctx-1].enabled = 0;
1148		cppoff = 1;
1149	} else {
1150		--cppctx;
1151		DBG("elif updates cppctx=%d", cppctx);
1152		cppif();
1153	}
1154}
1155
1156static void
1157endif(void)
1158{
1159	DBG("CPP line=%u endif cppctx=%d", lineno, cppctx);
1160
1161	if (cppctx == 0)
1162		error("#endif without #if");
1163
1164	if (cppctx > 1)
1165		cppoff = !ifstate[cppctx - 2].enabled;
1166	else
1167		cppoff = 0;
1168
1169	--cppctx;
1170	DBG("CPP endif updates cppctx=%d", cppctx);
1171	next();
1172}
1173
1174static void
1175undef(void)
1176{
1177	if (cppoff)
1178		return;
1179
1180	namespace = NS_CPP;
1181	next();
1182	if (yytoken != IDEN) {
1183		error("no macro name given in #undef directive");
1184		return;
1185	}
1186	killsym(yylval.sym);
1187	next();
1188}
1189
1190int
1191cpp(void)
1192{
1193	static struct {
1194		unsigned char token;
1195		void (*fun)(void);
1196	} *bp, clauses [] = {
1197		{DEFINE, define},
1198		{INCLUDE, include},
1199		{LINE, line},
1200		{IFDEF, ifdef},
1201		{IF, cppif},
1202		{ELIF, elif},
1203		{IFNDEF, ifndef},
1204		{ELSE, cppelse},
1205		{ENDIF, endif},
1206		{UNDEF, undef},
1207		{PRAGMA, pragma},
1208		{ERROR, usererr},
1209		{0, NULL}
1210	};
1211	int ns;
1212	char *p;
1213
1214	for (p = input->p; cppspace(*p); ++p)
1215		;
1216
1217	if (*p != '#') {
1218		if (cppoff)
1219			*input->p = '\0';
1220		return cppoff;
1221	}
1222	input->p = p+1;
1223
1224	disexpand = 1;
1225	lexmode = CPPMODE;
1226	ns = namespace;
1227	namespace = NS_CPPCLAUSES;
1228	next();
1229	namespace = NS_IDEN;
1230
1231	if (yytoken == '\n')
1232		goto ret;
1233
1234	for (bp = clauses; bp->token && bp->token != yytoken; ++bp)
1235		;
1236	if (!bp->token) {
1237		errorp("incorrect preprocessor directive '%s'", yytext);
1238		goto ret;
1239	}
1240
1241	DBG("CPP %s", yytext);
1242
1243	/*
1244	 * create a new context to avoid polish the current context,
1245	 * and to get all the symbols freed at the end
1246	 */
1247	pushctx();
1248	(*bp->fun)();
1249	popctx();
1250
1251	/*
1252	 * #include changes the content of input->line, so the correctness
1253	 * of the line must be checked in the own include(), and we have
1254	 * to skip this tests. For the same reason include() is the only
1255	 * function which does not prepare the next token
1256	 */
1257	if (bp->token == INCLUDE)
1258		goto ret;
1259
1260	if (yytoken != '\n' && yytoken != EOFTOK && !cppoff)
1261		cpperror("trailing characters after preprocessor directive");
1262
1263ret:
1264	disexpand = 0;
1265	lexmode = CCMODE;
1266	namespace = ns;
1267
1268	/*
1269	 * at this point we know that the cpp line is processed, and any error
1270	 * is generated but as next is called we cannot be sure that input is
1271	 * valid anymore, but in case of begin valid we want to discard any
1272	 * pending input in the current line
1273	 */
1274	if (input)
1275		*input->p = '\0';
1276
1277	return 1;
1278}
1279
1280void
1281ppragmaln(void)
1282{
1283	static char file[FILENAME_MAX];
1284	static unsigned nline;
1285	char *s;
1286
1287	putchar('\n');
1288	if (strcmp(file, filenam)) {
1289		strcpy(file, filenam);
1290		s = "#line %u \"%s\"\n";
1291	} else if (nline+1 != lineno) {
1292		s = "#line %u\n";
1293	} else {
1294		s = "";
1295	}
1296	nline = lineno;
1297	printf(s, nline, file);
1298
1299	for (s = input->line; isspace(*s); ++s)
1300		putchar(*s);
1301	yyspace = 0;
1302}
1303
1304void
1305outcpp(void)
1306{
1307	for (next(); yytoken != EOFTOK; next()) {
1308		if (onlyheader)
1309			continue;
1310		printf("%s%s", yyspace ? " " : "", yytext);
1311		yyspace = 0;
1312	}
1313	putchar('\n');
1314}