1/*-
2 * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by David A. Holland.
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 *
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <stdint.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34
35#include "union.h"
36#include "array.h"
37#include "mode.h"
38#include "place.h"
39#include "macro.h"
40#include "output.h"
41
42struct expansionitem {
43	enum { EI_STRING, EI_PARAM, EI_FILE, EI_LINE } itemtype;
44	union {
45		char *ei_string;		/* for EI_STRING */
46		unsigned ei_param;		/* for EI_PARAM */
47	} UN;
48};
49DECLARRAY(expansionitem, static UNUSED);
50DEFARRAY(expansionitem, static);
51
52#ifdef NEED_UNION_ACCESSORS
53#define ei_string un.ei_string
54#define ei_param un.ei_param
55#endif
56
57
58struct macro {
59	struct place defplace;
60	struct place expansionplace;
61	unsigned hash;
62	char *name;
63	bool hasparams;
64	struct stringarray params;
65	struct expansionitemarray expansion;
66	bool inuse;
67};
68DECLARRAY(macro, static UNUSED);
69DEFARRAY(macro, static);
70DECLARRAY(macroarray, static UNUSED);
71DEFARRAY(macroarray, static);
72
73static struct macroarrayarray macros;
74static unsigned total_macros;
75static unsigned hashmask;
76
77////////////////////////////////////////////////////////////
78// macro structure ops
79
80static
81struct expansionitem *
82expansionitem_create_string(const char *string)
83{
84	struct expansionitem *ei;
85
86	ei = domalloc(sizeof(*ei));
87	ei->itemtype = EI_STRING;
88	ei->ei_string = dostrdup(string);
89	return ei;
90}
91
92static
93struct expansionitem *
94expansionitem_create_stringlen(const char *string, size_t len)
95{
96	struct expansionitem *ei;
97
98	ei = domalloc(sizeof(*ei));
99	ei->itemtype = EI_STRING;
100	ei->ei_string = dostrndup(string, len);
101	return ei;
102}
103
104static
105struct expansionitem *
106expansionitem_create_param(unsigned param)
107{
108	struct expansionitem *ei;
109
110	ei = domalloc(sizeof(*ei));
111	ei->itemtype = EI_PARAM;
112	ei->ei_param = param;
113	return ei;
114}
115
116static
117struct expansionitem *
118expansionitem_create_file(void)
119{
120	struct expansionitem *ei;
121
122	ei = domalloc(sizeof(*ei));
123	ei->itemtype = EI_FILE;
124	return ei;
125}
126
127static
128struct expansionitem *
129expansionitem_create_line(void)
130{
131	struct expansionitem *ei;
132
133	ei = domalloc(sizeof(*ei));
134	ei->itemtype = EI_LINE;
135	return ei;
136}
137
138static
139void
140expansionitem_destroy(struct expansionitem *ei)
141{
142	switch (ei->itemtype) {
143	    case EI_STRING:
144		dostrfree(ei->ei_string);
145		break;
146	    case EI_PARAM:
147	    case EI_FILE:
148	    case EI_LINE:
149		break;
150	}
151	dofree(ei, sizeof(*ei));
152}
153
154static
155bool
156expansionitem_eq(const struct expansionitem *ei1,
157		 const struct expansionitem *ei2)
158{
159	if (ei1->itemtype != ei2->itemtype) {
160		return false;
161	}
162	switch (ei1->itemtype) {
163	    case EI_STRING:
164		if (strcmp(ei1->ei_string, ei2->ei_string) != 0) {
165			return false;
166		}
167		break;
168	    case EI_PARAM:
169		if (ei1->ei_param != ei2->ei_param) {
170			return false;
171		}
172		break;
173	    case EI_FILE:
174	    case EI_LINE:
175		break;
176	}
177	return true;
178}
179
180static
181struct macro *
182macro_create(struct place *p1, const char *name, unsigned hash,
183	     struct place *p2)
184{
185	struct macro *m;
186
187	m = domalloc(sizeof(*m));
188	m->defplace = *p1;
189	m->expansionplace = *p2;
190	m->hash = hash;
191	m->name = dostrdup(name);
192	m->hasparams = false;
193	stringarray_init(&m->params);
194	expansionitemarray_init(&m->expansion);
195	m->inuse = false;
196	return m;
197}
198
199DESTROYALL_ARRAY(expansionitem, );
200
201static
202void
203macro_destroy(struct macro *m)
204{
205	expansionitemarray_destroyall(&m->expansion);
206	expansionitemarray_cleanup(&m->expansion);
207	dostrfree(m->name);
208	dofree(m, sizeof(*m));
209}
210
211static
212bool
213macro_eq(const struct macro *m1, const struct macro *m2)
214{
215	unsigned num1, num2, i;
216	struct expansionitem *ei1, *ei2;
217	const char *p1, *p2;
218
219	if (strcmp(m1->name, m2->name) != 0) {
220		return false;
221	}
222
223	if (m1->hasparams != m2->hasparams) {
224		return false;
225	}
226
227	num1 = expansionitemarray_num(&m1->expansion);
228	num2 = expansionitemarray_num(&m2->expansion);
229	if (num1 != num2) {
230		return false;
231	}
232
233	for (i=0; i<num1; i++) {
234		ei1 = expansionitemarray_get(&m1->expansion, i);
235		ei2 = expansionitemarray_get(&m2->expansion, i);
236		if (!expansionitem_eq(ei1, ei2)) {
237			return false;
238		}
239	}
240
241	num1 = stringarray_num(&m1->params);
242	num2 = stringarray_num(&m2->params);
243	if (num1 != num2) {
244		return false;
245	}
246
247	for (i=0; i<num1; i++) {
248		p1 = stringarray_get(&m1->params, i);
249		p2 = stringarray_get(&m2->params, i);
250		if (strcmp(p1, p2) != 0) {
251			return false;
252		}
253	}
254	return true;
255}
256
257////////////////////////////////////////////////////////////
258// macro table
259
260/*
261 * Unless I've screwed up, this is something called Fletcher's Checksum
262 * that showed up in Dr. Dobbs in, according to my notes, May 1992. The
263 * implementation is new.
264 */
265static
266unsigned
267hashfunc(const char *s, size_t len)
268{
269	uint16_t x1, x2, a;
270	size_t i;
271
272	x1 = (uint16_t) (len >> 16);
273	x2 = (uint16_t) (len);
274	if (x1==0) {
275		x1++;
276	}
277	if (x2==0) {
278		x2++;
279	}
280
281	for (i=0; i<len; i+=2) {
282		if (i==len-1) {
283			a = (unsigned char)s[i];
284			/* don't run off the end of the array */
285		}
286		else {
287			a = (unsigned char)s[i] +
288				((uint16_t)(unsigned char)s[i+1] << 8);
289		}
290		x1 += a;
291		if (x1 < a) {
292			x1++;
293		}
294		x2 += x1;
295		if (x2 < x1) {
296			x2++;
297		}
298	}
299
300	x1 ^= 0xffff;
301	x2 ^= 0xffff;
302	return ((uint32_t)x2)*65535U + x1;
303}
304
305static
306void
307macrotable_init(void)
308{
309	unsigned i;
310
311	macroarrayarray_init(&macros);
312	macroarrayarray_setsize(&macros, 4);
313	for (i=0; i<4; i++) {
314		macroarrayarray_set(&macros, i, NULL);
315	}
316	total_macros = 0;
317	hashmask = 0x3;
318}
319
320DESTROYALL_ARRAY(macro, );
321
322static
323void
324macrotable_cleanup(void)
325{
326	struct macroarray *bucket;
327	unsigned numbuckets, i;
328
329	numbuckets = macroarrayarray_num(&macros);
330	for (i=0; i<numbuckets; i++) {
331		bucket = macroarrayarray_get(&macros, i);
332		if (bucket != NULL) {
333			macroarray_destroyall(bucket);
334			macroarray_destroy(bucket);
335		}
336	}
337	macroarrayarray_setsize(&macros, 0);
338	macroarrayarray_cleanup(&macros);
339}
340
341static
342struct macro *
343macrotable_findlen(const char *name, size_t len, bool remove_it)
344{
345	unsigned hash;
346	struct macroarray *bucket;
347	struct macro *m, *m2;
348	unsigned i, num;
349	size_t mlen;
350
351	hash = hashfunc(name, len);
352	bucket = macroarrayarray_get(&macros, hash & hashmask);
353	if (bucket == NULL) {
354		return NULL;
355	}
356	num = macroarray_num(bucket);
357	for (i=0; i<num; i++) {
358		m = macroarray_get(bucket, i);
359		if (hash != m->hash) {
360			continue;
361		}
362		mlen = strlen(m->name);
363		if (len == mlen && !memcmp(name, m->name, len)) {
364			if (remove_it) {
365				if (i < num-1) {
366					m2 = macroarray_get(bucket, num-1);
367					macroarray_set(bucket, i, m2);
368				}
369				macroarray_setsize(bucket, num-1);
370				total_macros--;
371			}
372			return m;
373		}
374	}
375	return NULL;
376}
377
378static
379struct macro *
380macrotable_find(const char *name, bool remove_it)
381{
382	return macrotable_findlen(name, strlen(name), remove_it);
383}
384
385static
386void
387macrotable_rehash(void)
388{
389	struct macroarray *newbucket, *oldbucket;
390	struct macro *m;
391	unsigned newmask, tossbit;
392	unsigned numbuckets, i;
393	unsigned oldnum, j, k;
394
395	numbuckets = macroarrayarray_num(&macros);
396	macroarrayarray_setsize(&macros, numbuckets*2);
397
398	assert(hashmask == numbuckets - 1);
399	newmask = (hashmask << 1) | 1U;
400	tossbit = newmask & ~hashmask;
401	hashmask = newmask;
402
403	for (i=0; i<numbuckets; i++) {
404		newbucket = NULL;
405		oldbucket = macroarrayarray_get(&macros, i);
406		if (oldbucket == NULL) {
407			macroarrayarray_set(&macros, numbuckets + i, NULL);
408			continue;
409		}
410		oldnum = macroarray_num(oldbucket);
411		for (j=0; j<oldnum; j++) {
412			m = macroarray_get(oldbucket, j);
413			if (m->hash & tossbit) {
414				if (newbucket == NULL) {
415					newbucket = macroarray_create();
416				}
417				macroarray_set(oldbucket, j, NULL);
418				macroarray_add(newbucket, m, NULL);
419			}
420		}
421		for (j=k=0; j<oldnum; j++) {
422			m = macroarray_get(oldbucket, j);
423			if (m != NULL) {
424				if (k < j) {
425					macroarray_set(oldbucket, k, m);
426				}
427				k++;
428			}
429		}
430		macroarray_setsize(oldbucket, k);
431		macroarrayarray_set(&macros, numbuckets + i, newbucket);
432	}
433}
434
435static
436void
437macrotable_add(struct macro *m)
438{
439	unsigned hash;
440	struct macroarray *bucket;
441	unsigned numbuckets;
442
443	numbuckets = macroarrayarray_num(&macros);
444	if (total_macros > 0 && total_macros / numbuckets > 9) {
445		macrotable_rehash();
446	}
447
448	hash = hashfunc(m->name, strlen(m->name));
449	bucket = macroarrayarray_get(&macros, hash & hashmask);
450	if (bucket == NULL) {
451		bucket = macroarray_create();
452		macroarrayarray_set(&macros, hash & hashmask, bucket);
453	}
454	macroarray_add(bucket, m, NULL);
455	total_macros++;
456}
457
458////////////////////////////////////////////////////////////
459// external macro definition interface
460
461static
462struct macro *
463macro_define_common_start(struct place *p1, const char *macro,
464			  struct place *p2)
465{
466	struct macro *m;
467	unsigned hash;
468
469	if (!is_identifier(macro)) {
470		complain(p1, "Invalid macro name %s", macro);
471		complain_fail();
472	}
473
474	hash = hashfunc(macro, strlen(macro));
475	m = macro_create(p1, macro, hash, p2);
476	return m;
477}
478
479static
480void
481macro_define_common_end(struct macro *m)
482{
483	struct macro *oldm;
484	bool ok;
485
486	oldm = macrotable_find(m->name, false);
487	if (oldm != NULL) {
488		ok = macro_eq(m, oldm);
489		if (ok) {
490			/* in traditional cpp this is silent */
491			//complain(&m->defplace,
492			//	 "Warning: redefinition of %s", m->name);
493			//complain(&oldm->defplace,
494			//	 "Previous definition was here");
495			//if (mode.werror) {
496			//	complain_fail();
497			//}
498		} else {
499			complain(&m->defplace,
500				 "Warning: non-identical redefinition of %s",
501				 m->name);
502			complain(&oldm->defplace,
503				 "Previous definition was here");
504			/* in traditional cpp this is not fatal */
505			if (mode.werror) {
506				complain_fail();
507			}
508		}
509		macro_destroy(m);
510		return;
511	}
512	macrotable_add(m);
513}
514
515static
516void
517macro_parse_parameters(struct macro *m, struct place *p, const char *params)
518{
519	size_t len;
520	const char *s;
521	char *param;
522
523	while (params != NULL) {
524		len = strspn(params, ws);
525		params += len;
526		place_addcolumns(p, len);
527		s = strchr(params, ',');
528		if (s) {
529			len = s-params;
530			param = dostrndup(params, len);
531			s++;
532		} else {
533			len = strlen(params);
534			param = dostrndup(params, len);
535		}
536		notrailingws(param, strlen(param));
537		if (!is_identifier(param)) {
538			complain(p, "Invalid macro parameter name %s", param);
539			complain_fail();
540		} else {
541			stringarray_add(&m->params, param, NULL);
542		}
543		params = s;
544		place_addcolumns(p, len);
545	}
546}
547
548static
549bool
550isparam(struct macro *m, const char *name, size_t len, unsigned *num_ret)
551{
552	unsigned num, i;
553	const char *param;
554
555	num = stringarray_num(&m->params);
556	for (i=0; i<num; i++) {
557		param = stringarray_get(&m->params, i);
558		if (strlen(param) == len && !memcmp(name, param, len)) {
559			*num_ret = i;
560			return true;
561		}
562	}
563	return false;
564}
565
566static
567void
568macro_parse_expansion(struct macro *m, const char *buf)
569{
570	size_t blockstart, wordstart, pos;
571	struct expansionitem *ei;
572	unsigned param;
573
574	pos = blockstart = 0;
575	while (buf[pos] != '\0') {
576		pos += strspn(buf+pos, ws);
577		if (strchr(alnum, buf[pos])) {
578			wordstart = pos;
579			pos += strspn(buf+pos, alnum);
580			if (isparam(m, buf+wordstart, pos-wordstart, &param)) {
581				if (wordstart > blockstart) {
582					ei = expansionitem_create_stringlen(
583						buf + blockstart,
584						wordstart - blockstart);
585					expansionitemarray_add(&m->expansion,
586							       ei, NULL);
587				}
588				ei = expansionitem_create_param(param);
589				expansionitemarray_add(&m->expansion, ei,NULL);
590				blockstart = pos;
591				continue;
592			}
593			continue;
594		}
595		pos++;
596	}
597	if (pos > blockstart) {
598		ei = expansionitem_create_stringlen(buf + blockstart,
599						    pos - blockstart);
600		expansionitemarray_add(&m->expansion, ei, NULL);
601	}
602}
603
604void
605macro_define_plain(struct place *p1, const char *macro,
606		   struct place *p2, const char *expansion)
607{
608	struct macro *m;
609	struct expansionitem *ei;
610
611	m = macro_define_common_start(p1, macro, p2);
612	ei = expansionitem_create_string(expansion);
613	expansionitemarray_add(&m->expansion, ei, NULL);
614	macro_define_common_end(m);
615}
616
617void
618macro_define_params(struct place *p1, const char *macro,
619		    struct place *p2, const char *params,
620		    struct place *p3, const char *expansion)
621{
622	struct macro *m;
623
624	m = macro_define_common_start(p1, macro, p3);
625	m->hasparams = true;
626	macro_parse_parameters(m, p2, params);
627	macro_parse_expansion(m, expansion);
628	macro_define_common_end(m);
629}
630
631void
632macro_define_magic(struct place *p, const char *macro)
633{
634	struct macro *m;
635	struct expansionitem *ei;
636
637	m = macro_define_common_start(p, macro, p);
638	if (!strcmp(macro, "__FILE__")) {
639		ei = expansionitem_create_file();
640	}
641	else {
642		assert(!strcmp(macro, "__LINE__"));
643		ei = expansionitem_create_line();
644	}
645	expansionitemarray_add(&m->expansion, ei, NULL);
646	macro_define_common_end(m);
647}
648
649void
650macro_undef(const char *macro)
651{
652	struct macro *m;
653
654	m = macrotable_find(macro, true);
655	if (m) {
656		macro_destroy(m);
657	}
658}
659
660bool
661macro_isdefined(const char *macro)
662{
663	struct macro *m;
664
665	m = macrotable_find(macro, false);
666	return m != NULL;
667}
668
669////////////////////////////////////////////////////////////
670// macro expansion
671
672struct expstate {
673	bool honordefined;
674	enum { ES_NORMAL, ES_WANTLPAREN, ES_NOARG, ES_HAVEARG } state;
675	struct macro *curmacro;
676	struct stringarray args;
677	unsigned argparens;
678
679	bool tobuf;
680	char *buf;
681	size_t bufpos, bufmax;
682};
683
684static struct expstate mainstate;
685
686static void doexpand(struct expstate *es, struct place *p,
687		     const char *buf, size_t len);
688
689static
690void
691expstate_init(struct expstate *es, bool tobuf, bool honordefined)
692{
693	es->honordefined = honordefined;
694	es->state = ES_NORMAL;
695	es->curmacro = NULL;
696	stringarray_init(&es->args);
697	es->argparens = 0;
698	es->tobuf = tobuf;
699	es->buf = NULL;
700	es->bufpos = 0;
701	es->bufmax = 0;
702}
703
704static
705void
706expstate_cleanup(struct expstate *es)
707{
708	assert(es->state == ES_NORMAL);
709	stringarray_cleanup(&es->args);
710	if (es->buf) {
711		dofree(es->buf, es->bufmax);
712	}
713}
714
715static
716void
717expstate_destroyargs(struct expstate *es)
718{
719	unsigned i, num;
720
721	num = stringarray_num(&es->args);
722	for (i=0; i<num; i++) {
723		dostrfree(stringarray_get(&es->args, i));
724	}
725	stringarray_setsize(&es->args, 0);
726}
727
728static
729void
730expand_send(struct expstate *es, struct place *p, const char *buf, size_t len)
731{
732	size_t oldmax;
733
734	if (es->tobuf) {
735		assert(es->bufpos <= es->bufmax);
736		if (es->bufpos + len > es->bufmax) {
737			oldmax = es->bufmax;
738			if (es->bufmax == 0) {
739				es->bufmax = 64;
740			}
741			while (es->bufpos + len > es->bufmax) {
742				es->bufmax *= 2;
743			}
744			es->buf = dorealloc(es->buf, oldmax, es->bufmax);
745		}
746		memcpy(es->buf + es->bufpos, buf, len);
747		es->bufpos += len;
748		assert(es->bufpos <= es->bufmax);
749	} else {
750		output(p, buf, len);
751	}
752}
753
754static
755void
756expand_send_eof(struct expstate *es, struct place *p)
757{
758	if (es->tobuf) {
759		expand_send(es, p, "", 1);
760		es->bufpos--;
761	} else {
762		output_eof();
763	}
764}
765
766static
767void
768expand_newarg(struct expstate *es, const char *buf, size_t len)
769{
770	char *text;
771
772	text = dostrndup(buf, len);
773	stringarray_add(&es->args, text, NULL);
774}
775
776static
777void
778expand_appendarg(struct expstate *es, const char *buf, size_t len)
779{
780	unsigned num;
781	char *text;
782	size_t oldlen;
783
784	num = stringarray_num(&es->args);
785	assert(num > 0);
786
787	text = stringarray_get(&es->args, num - 1);
788	oldlen = strlen(text);
789	text = dorealloc(text, oldlen + 1, oldlen + len + 1);
790	memcpy(text + oldlen, buf, len);
791	text[oldlen+len] = '\0';
792	stringarray_set(&es->args, num - 1, text);
793}
794
795static
796char *
797expand_substitute(struct place *p, struct expstate *es)
798{
799	struct expansionitem *ei;
800	unsigned i, num;
801	size_t len;
802	char *arg;
803	char *ret;
804	unsigned numargs, numparams;
805	char numbuf[64];
806
807	numargs = stringarray_num(&es->args);
808	numparams = stringarray_num(&es->curmacro->params);
809
810	if (numargs == 0 && numparams == 1) {
811		/* no arguments <=> one empty argument */
812		stringarray_add(&es->args, dostrdup(""), NULL);
813		numargs++;
814	}
815	if (numargs != numparams) {
816		complain(p, "Wrong number of arguments for macro %s; "
817			 "found %u, expected %u",
818			 es->curmacro->name, numargs, numparams);
819		complain_fail();
820		while (numargs < numparams) {
821			stringarray_add(&es->args, dostrdup(""), NULL);
822			numargs++;
823		}
824	}
825
826	len = 0;
827	num = expansionitemarray_num(&es->curmacro->expansion);
828	for (i=0; i<num; i++) {
829		ei = expansionitemarray_get(&es->curmacro->expansion, i);
830		switch (ei->itemtype) {
831		    case EI_STRING:
832			len += strlen(ei->ei_string);
833			break;
834		    case EI_PARAM:
835			arg = stringarray_get(&es->args, ei->ei_param);
836			len += strlen(arg);
837			break;
838		    case EI_FILE:
839			len += strlen(place_getname(p)) + 2;
840			break;
841		    case EI_LINE:
842			len += snprintf(numbuf, sizeof(numbuf), "%u", p->line);
843			break;
844		}
845	}
846
847	ret = domalloc(len+1);
848	*ret = '\0';
849	for (i=0; i<num; i++) {
850		ei = expansionitemarray_get(&es->curmacro->expansion, i);
851		switch (ei->itemtype) {
852		    case EI_STRING:
853			strlcat(ret, ei->ei_string, len + 1);
854			break;
855		    case EI_PARAM:
856			arg = stringarray_get(&es->args, ei->ei_param);
857			strlcat(ret, arg, len + 1);
858			break;
859		    case EI_FILE:
860			strlcat(ret, "\"", len + 1);
861			strlcat(ret, place_getname(p), len + 1);
862			strlcat(ret, "\"", len + 1);
863			break;
864		    case EI_LINE:
865			snprintf(numbuf, sizeof(numbuf), "%u", p->line);
866			strlcat(ret, numbuf, len + 1);
867			break;
868		}
869	}
870
871	return ret;
872}
873
874static
875void
876expand_domacro(struct expstate *es, struct place *p)
877{
878	struct macro *m;
879	const char *name, *val;
880	char *newbuf, *newbuf2;
881
882	if (es->curmacro == NULL) {
883		/* defined() */
884		if (stringarray_num(&es->args) != 1) {
885			complain(p, "Too many arguments for defined()");
886			complain_fail();
887			expand_send(es, p, "0", 1);
888			return;
889		}
890		name = stringarray_get(&es->args, 0);
891		m = macrotable_find(name, false);
892		val = (m != NULL) ? "1" : "0";
893		debuglog(p, "defined(%s): %s", name, val);
894		expand_send(es, p, val, 1);
895		expstate_destroyargs(es);
896		return;
897	}
898
899	m = es->curmacro;
900	assert(m->inuse == false);
901	m->inuse = true;
902
903	debuglog(p, "Expanding macro %s", m->name);
904	newbuf = expand_substitute(p, es);
905	debuglog(p, "Substituting for %s: %s", m->name, newbuf);
906
907	newbuf2 = macroexpand(p, newbuf, strlen(newbuf), false);
908	dostrfree(newbuf);
909	expstate_destroyargs(es);
910	debuglog(p, "Complete expansion for %s: %s", m->name, newbuf2);
911
912	doexpand(es, p, newbuf2, strlen(newbuf2));
913	dostrfree(newbuf2);
914
915	m->inuse = false;
916}
917
918/*
919 * The traditional behavior if a function-like macro appears without
920 * arguments is to pretend it isn't a macro; that is, just emit its
921 * name.
922 */
923static
924void
925expand_missingargs(struct expstate *es, struct place *p, bool needspace)
926{
927	if (es->curmacro == NULL) {
928		/* defined */
929		expand_send(es, p, "defined", 7);
930		return;
931	}
932	expand_send(es, p, es->curmacro->name, strlen(es->curmacro->name));
933	/* send a space in case we ate whitespace after the macro name */
934	if (needspace) {
935		expand_send(es, p, " ", 1);
936	}
937}
938
939static
940void
941expand_got_ws(struct expstate *es, struct place *p,
942	      const char *buf, size_t len)
943{
944	switch (es->state) {
945	    case ES_NORMAL:
946		expand_send(es, p, buf, len);
947		break;
948	    case ES_WANTLPAREN:
949		/* XXX notyet */
950		//expand_send(es, p, buf, len);
951		break;
952	    case ES_NOARG:
953		expand_newarg(es, buf, len);
954		es->state = ES_HAVEARG;
955		break;
956	    case ES_HAVEARG:
957		expand_appendarg(es, buf, len);
958		break;
959	}
960}
961
962static
963void
964expand_got_word(struct expstate *es, struct place *p,
965		const char *buf, size_t len)
966{
967	struct macro *m;
968
969	switch (es->state) {
970	    case ES_NORMAL:
971		if (es->honordefined &&
972		    len == 7 && !memcmp(buf, "defined", 7)) {
973			es->curmacro = NULL;
974			es->state = ES_WANTLPAREN;
975			break;
976		}
977		m = macrotable_findlen(buf, len, false);
978		if (m == NULL || m->inuse) {
979			expand_send(es, p, buf, len);
980		} else if (!m->hasparams) {
981			es->curmacro = m;
982			expand_domacro(es, p);
983		} else {
984			es->curmacro = m;
985			es->state = ES_WANTLPAREN;
986		}
987		break;
988	    case ES_WANTLPAREN:
989		if (es->curmacro != NULL) {
990			expand_missingargs(es, p, true);
991			es->state = ES_NORMAL;
992			/* try again */
993			expand_got_word(es, p, buf, len);
994		} else {
995			/* "defined foo" means "defined(foo)" */
996			expand_newarg(es, buf, len);
997			es->state = ES_NORMAL;
998			expand_domacro(es, p);
999		}
1000		break;
1001	    case ES_NOARG:
1002		expand_newarg(es, buf, len);
1003		es->state = ES_HAVEARG;
1004		break;
1005	    case ES_HAVEARG:
1006		expand_appendarg(es, buf, len);
1007		break;
1008	}
1009}
1010
1011static
1012void
1013expand_got_lparen(struct expstate *es, struct place *p,
1014		  const char *buf, size_t len)
1015{
1016	switch (es->state) {
1017	    case ES_NORMAL:
1018		expand_send(es, p, buf, len);
1019		break;
1020	    case ES_WANTLPAREN:
1021		es->state = ES_NOARG;
1022		break;
1023	    case ES_NOARG:
1024		expand_newarg(es, buf, len);
1025		es->state = ES_HAVEARG;
1026		es->argparens++;
1027		break;
1028	    case ES_HAVEARG:
1029		expand_appendarg(es, buf, len);
1030		es->argparens++;
1031		break;
1032	}
1033}
1034
1035static
1036void
1037expand_got_rparen(struct expstate *es, struct place *p,
1038		  const char *buf, size_t len)
1039{
1040	switch (es->state) {
1041	    case ES_NORMAL:
1042		expand_send(es, p, buf, len);
1043		break;
1044	    case ES_WANTLPAREN:
1045		expand_missingargs(es, p, false);
1046		es->state = ES_NORMAL;
1047		/* try again */
1048		expand_got_rparen(es, p, buf, len);
1049		break;
1050	    case ES_NOARG:
1051		assert(es->argparens == 0);
1052		if (stringarray_num(&es->args) > 0) {
1053			/* we are after a comma; enter an empty argument */
1054			expand_newarg(es, buf, 0);
1055		}
1056		es->state = ES_NORMAL;
1057		expand_domacro(es, p);
1058		break;
1059	    case ES_HAVEARG:
1060		if (es->argparens > 0) {
1061			es->argparens--;
1062			expand_appendarg(es, buf, len);
1063		} else {
1064			es->state = ES_NORMAL;
1065			expand_domacro(es, p);
1066		}
1067		break;
1068	}
1069}
1070
1071static
1072void
1073expand_got_comma(struct expstate *es, struct place *p,
1074		 const char *buf, size_t len)
1075{
1076	switch (es->state) {
1077	    case ES_NORMAL:
1078		expand_send(es, p, buf, len);
1079		break;
1080	    case ES_WANTLPAREN:
1081		expand_missingargs(es, p, false);
1082		es->state = ES_NORMAL;
1083		/* try again */
1084		expand_got_comma(es, p, buf, len);
1085		break;
1086	    case ES_NOARG:
1087		assert(es->argparens == 0);
1088		expand_newarg(es, buf, 0);
1089		break;
1090	    case ES_HAVEARG:
1091		if (es->argparens > 0) {
1092			expand_appendarg(es, buf, len);
1093		} else {
1094			es->state = ES_NOARG;
1095		}
1096		break;
1097	}
1098}
1099
1100static
1101void
1102expand_got_other(struct expstate *es, struct place *p,
1103		 const char *buf, size_t len)
1104{
1105	switch (es->state) {
1106	    case ES_NORMAL:
1107		expand_send(es, p, buf, len);
1108		break;
1109	    case ES_WANTLPAREN:
1110		expand_missingargs(es, p, false);
1111		es->state = ES_NORMAL;
1112		/* try again */
1113		expand_got_other(es, p, buf, len);
1114		break;
1115	    case ES_NOARG:
1116		expand_newarg(es, buf, len);
1117		es->state = ES_HAVEARG;
1118		break;
1119	    case ES_HAVEARG:
1120		expand_appendarg(es, buf, len);
1121		break;
1122	}
1123}
1124
1125static
1126void
1127expand_got_eof(struct expstate *es, struct place *p)
1128{
1129	switch (es->state) {
1130	    case ES_NORMAL:
1131		break;
1132	    case ES_WANTLPAREN:
1133		expand_missingargs(es, p, false);
1134		break;
1135	    case ES_NOARG:
1136	    case ES_HAVEARG:
1137		if (es->curmacro) {
1138			complain(p, "Unclosed argument list for macro %s",
1139				 es->curmacro->name);
1140		} else {
1141			complain(p, "Unclosed argument list for defined()");
1142		}
1143		complain_fail();
1144		expstate_destroyargs(es);
1145		break;
1146	}
1147	expand_send_eof(es, p);
1148	es->state = ES_NORMAL;
1149	es->curmacro = NULL;
1150	es->argparens = 0;
1151}
1152
1153static
1154void
1155doexpand(struct expstate *es, struct place *p, const char *buf, size_t len)
1156{
1157	char *s;
1158	size_t x;
1159	bool inquote = false;
1160	char quote = '\0';
1161
1162	while (len > 0) {
1163		x = strspn(buf, ws);
1164		if (x > len) {
1165			/* XXX gross, need strnspn */
1166			x = len;
1167		}
1168
1169		if (x > 0) {
1170			expand_got_ws(es, p, buf, x);
1171			buf += x;
1172			len -= x;
1173			continue;
1174		}
1175
1176		x = strspn(buf, alnum);
1177		if (x > len) {
1178			/* XXX gross, need strnspn */
1179			x = len;
1180		}
1181
1182		if (!inquote && x > 0) {
1183			expand_got_word(es, p, buf, x);
1184			buf += x;
1185			len -= x;
1186			continue;
1187		}
1188
1189		if (!inquote && len > 1 && buf[0] == '/' && buf[1] == '*') {
1190			s = strstr(buf, "*/");
1191			if (s) {
1192				x = s - buf;
1193			} else {
1194				x = len;
1195			}
1196			expand_got_ws(es, p, buf, x);
1197			buf += x;
1198			len -= x;
1199			continue;
1200		}
1201
1202		if (!inquote && buf[0] == '(') {
1203			expand_got_lparen(es, p, buf, 1);
1204			buf++;
1205			len--;
1206			continue;
1207		}
1208
1209		if (!inquote && buf[0] == ')') {
1210			expand_got_rparen(es, p, buf, 1);
1211			buf++;
1212			len--;
1213			continue;
1214		}
1215
1216		if (!inquote && buf[0] == ',') {
1217			expand_got_comma(es, p, buf, 1);
1218			buf++;
1219			len--;
1220			continue;
1221		}
1222
1223		if (len > 1 && buf[0] == '\\' &&
1224		    (buf[1] == '"' || buf[1] == '\'')) {
1225			expand_got_other(es, p, buf, 2);
1226			buf += 2;
1227			len -= 2;
1228			continue;
1229		}
1230		if (!inquote && (buf[0] == '"' || buf[0] == '\'')) {
1231			inquote = true;
1232			quote = buf[0];
1233		} else if (inquote && buf[0] == quote) {
1234			inquote = false;
1235		}
1236
1237		expand_got_other(es, p, buf, 1);
1238		buf++;
1239		len--;
1240	}
1241}
1242
1243char *
1244macroexpand(struct place *p, const char *buf, size_t len, bool honordefined)
1245{
1246	struct expstate es;
1247	char *ret;
1248
1249	expstate_init(&es, true, honordefined);
1250	doexpand(&es, p, buf, len);
1251	expand_got_eof(&es, p);
1252
1253	/* trim to fit, so the malloc debugging won't complain */
1254	es.buf = dorealloc(es.buf, es.bufmax, strlen(es.buf) + 1);
1255
1256	ret = es.buf;
1257	es.buf = NULL;
1258	es.bufpos = es.bufmax = 0;
1259
1260	expstate_cleanup(&es);
1261
1262	return ret;
1263}
1264
1265void
1266macro_sendline(struct place *p, const char *buf, size_t len)
1267{
1268	doexpand(&mainstate, p, buf, len);
1269	switch (mainstate.state) {
1270	    case ES_NORMAL:
1271		/*
1272		 * If we were sent a blank line, don't emit a newline
1273		 * for it. This matches the prior behavior of tradcpp.
1274		 */
1275		if (len > 0) {
1276			output(p, "\n", 1);
1277		}
1278		break;
1279	    case ES_WANTLPAREN:
1280	    case ES_NOARG:
1281	    case ES_HAVEARG:
1282		/*
1283		 * Apparently to match gcc's -traditional behavior we
1284		 * need to emit a space for each newline that appears
1285		 * while processing macro args.
1286		 */
1287		expand_got_ws(&mainstate, p, " ", 1);
1288		break;
1289	}
1290}
1291
1292void
1293macro_sendeof(struct place *p)
1294{
1295	expand_got_eof(&mainstate, p);
1296}
1297
1298////////////////////////////////////////////////////////////
1299// module initialization
1300
1301void
1302macros_init(void)
1303{
1304	macrotable_init();
1305	expstate_init(&mainstate, false, false);
1306}
1307
1308void
1309macros_cleanup(void)
1310{
1311	expstate_cleanup(&mainstate);
1312	macrotable_cleanup();
1313}
1314