GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: libexec/tradcpp/macro.c Lines: 0 513 0.0 %
Date: 2017-11-07 Branches: 0 273 0.0 %

Line Branch Exec Source
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
}