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