GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: libexec/tradcpp/directive.c Lines: 25 224 11.2 %
Date: 2017-11-13 Branches: 6 124 4.8 %

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 <assert.h>
31
#include <stdbool.h>
32
#include <stdlib.h>
33
#include <string.h>
34
35
#include "utils.h"
36
#include "mode.h"
37
#include "place.h"
38
#include "files.h"
39
#include "directive.h"
40
#include "macro.h"
41
#include "eval.h"
42
#include "output.h"
43
44
struct ifstate {
45
	struct ifstate *prev;
46
	struct place startplace;
47
	bool curtrue;
48
	bool evertrue;
49
	bool seenelse;
50
};
51
52
static struct ifstate *ifstate;
53
54
////////////////////////////////////////////////////////////
55
// common parsing bits
56
57
static
58
void
59
uncomment(char *buf)
60
{
61
	char *s, *t, *u = NULL;
62
	bool incomment = false;
63
	bool inesc = false;
64
	bool inquote = false;
65
	char quote = '\0';
66
67
	for (s = t = buf; *s; s++) {
68
		if (incomment) {
69
			if (s[0] == '*' && s[1] == '/') {
70
				s++;
71
				incomment = false;
72
			}
73
		} else {
74
			if (!inquote && s[0] == '/' && s[1] == '*') {
75
				incomment = true;
76
			} else {
77
				if (inesc) {
78
					inesc = false;
79
				} else if (s[0] == '\\') {
80
					inesc = true;
81
				} else if (!inquote &&
82
					   (s[0] == '"' || s[0] == '\'')) {
83
					inquote = true;
84
					quote = s[0];
85
				} else if (inquote && s[0] == quote) {
86
					inquote = false;
87
				}
88
89
				if (t != s) {
90
					*t = *s;
91
				}
92
				if (!strchr(ws, *t)) {
93
					u = t;
94
				}
95
				t++;
96
			}
97
		}
98
	}
99
	if (u) {
100
		/* end string after last non-whitespace char */
101
		u[1] = '\0';
102
	} else {
103
		*t = '\0';
104
	}
105
}
106
107
static
108
void
109
oneword(const char *what, struct place *p2, char *line)
110
{
111
	size_t pos;
112
113
	pos = strcspn(line, ws);
114
	if (line[pos] != '\0') {
115
		p2->column += pos;
116
		complain(p2, "Garbage after %s argument", what);
117
		complain_fail();
118
		line[pos] = '\0';
119
	}
120
}
121
122
////////////////////////////////////////////////////////////
123
// if handling
124
125
static
126
struct ifstate *
127
ifstate_create(struct ifstate *prev, struct place *p, bool startstate)
128
{
129
	struct ifstate *is;
130
131
1462
	is = domalloc(sizeof(*is));
132
1462
	is->prev = prev;
133
1462
	if (p != NULL) {
134
		is->startplace = *p;
135
	} else {
136
1462
		place_setbuiltin(&is->startplace, 1);
137
	}
138
1462
	is->curtrue = startstate;
139
1462
	is->evertrue = is->curtrue;
140
1462
	is->seenelse = false;
141
1462
	return is;
142
}
143
144
static
145
void
146
ifstate_destroy(struct ifstate *is)
147
{
148
2924
	dofree(is, sizeof(*is));
149
1462
}
150
151
static
152
void
153
ifstate_push(struct place *p, bool startstate)
154
{
155
	struct ifstate *newstate;
156
157
	newstate = ifstate_create(ifstate, p, startstate);
158
	if (!ifstate->curtrue) {
159
		newstate->curtrue = false;
160
		newstate->evertrue = true;
161
	}
162
	ifstate = newstate;
163
}
164
165
static
166
void
167
ifstate_pop(void)
168
{
169
	struct ifstate *is;
170
171
	is = ifstate;
172
	ifstate = ifstate->prev;
173
	ifstate_destroy(is);
174
}
175
176
static
177
void
178
d_if(struct place *p, struct place *p2, char *line)
179
{
180
	char *expr;
181
	bool val;
182
	struct place p3 = *p2;
183
	size_t oldlen;
184
185
	expr = macroexpand(p2, line, strlen(line), true);
186
187
	oldlen = strlen(expr);
188
	uncomment(expr);
189
	/* trim to fit, so the malloc debugging won't complain */
190
	expr = dorealloc(expr, oldlen + 1, strlen(expr) + 1);
191
192
	if (ifstate->curtrue) {
193
		val = eval(&p3, expr);
194
	} else {
195
		val = 0;
196
	}
197
	ifstate_push(p, val);
198
	dostrfree(expr);
199
}
200
201
static
202
void
203
d_ifdef(struct place *p, struct place *p2, char *line)
204
{
205
	uncomment(line);
206
	oneword("#ifdef", p2, line);
207
	ifstate_push(p, macro_isdefined(line));
208
}
209
210
static
211
void
212
d_ifndef(struct place *p, struct place *p2, char *line)
213
{
214
	uncomment(line);
215
	oneword("#ifndef", p2, line);
216
	ifstate_push(p, !macro_isdefined(line));
217
}
218
219
static
220
void
221
d_elif(struct place *p, struct place *p2, char *line)
222
{
223
	char *expr;
224
	struct place p3 = *p2;
225
	size_t oldlen;
226
227
	if (ifstate->seenelse) {
228
		complain(p, "#elif after #else");
229
		complain_fail();
230
	}
231
232
	if (ifstate->evertrue) {
233
		ifstate->curtrue = false;
234
	} else {
235
		expr = macroexpand(p2, line, strlen(line), true);
236
237
		oldlen = strlen(expr);
238
		uncomment(expr);
239
		/* trim to fit, so the malloc debugging won't complain */
240
		expr = dorealloc(expr, oldlen + 1, strlen(expr) + 1);
241
242
		ifstate->curtrue = eval(&p3, expr);
243
		ifstate->evertrue = ifstate->curtrue;
244
		dostrfree(expr);
245
	}
246
}
247
248
static
249
void
250
d_else(struct place *p, struct place *p2, char *line)
251
{
252
	(void)p2;
253
	(void)line;
254
255
	if (ifstate->seenelse) {
256
		complain(p, "Multiple #else directives in one conditional");
257
		complain_fail();
258
	}
259
260
	ifstate->curtrue = !ifstate->evertrue;
261
	ifstate->evertrue = true;
262
	ifstate->seenelse = true;
263
}
264
265
static
266
void
267
d_endif(struct place *p, struct place *p2, char *line)
268
{
269
	(void)p2;
270
	(void)line;
271
272
	if (ifstate->prev == NULL) {
273
		complain(p, "Unmatched #endif");
274
		complain_fail();
275
	} else {
276
		ifstate_pop();
277
	}
278
}
279
280
////////////////////////////////////////////////////////////
281
// macros
282
283
static
284
void
285
d_define(struct place *p, struct place *p2, char *line)
286
{
287
	size_t pos, argpos;
288
	struct place p3, p4;
289
290
	(void)p;
291
292
	/*
293
	 * line may be:
294
	 *    macro expansion
295
	 *    macro(arg, arg, ...) expansion
296
	 */
297
298
	pos = strcspn(line, " \t\f\v(");
299
	if (line[pos] == '(') {
300
		line[pos++] = '\0';
301
		argpos = pos;
302
		pos = pos + strcspn(line+pos, "()");
303
		if (line[pos] == '(') {
304
			p2->column += pos;
305
			complain(p2, "Left parenthesis in macro parameters");
306
			complain_fail();
307
			return;
308
		}
309
		if (line[pos] != ')') {
310
			p2->column += pos;
311
			complain(p2, "Unclosed macro parameter list");
312
			complain_fail();
313
			return;
314
		}
315
		line[pos++] = '\0';
316
#if 0
317
		if (!strchr(ws, line[pos])) {
318
			p2->column += pos;
319
			complain(p2, "Trash after macro parameter list");
320
			complain_fail();
321
			return;
322
		}
323
#endif
324
	} else if (line[pos] == '\0') {
325
		argpos = 0;
326
	} else {
327
		line[pos++] = '\0';
328
		argpos = 0;
329
	}
330
331
	pos += strspn(line+pos, ws);
332
333
	p3 = *p2;
334
	p3.column += argpos;
335
336
	p4 = *p2;
337
	p4.column += pos;
338
339
	if (argpos) {
340
		macro_define_params(p2, line, &p3,
341
				    line + argpos, &p4,
342
				    line + pos);
343
	} else {
344
		macro_define_plain(p2, line, &p4, line + pos);
345
	}
346
}
347
348
static
349
void
350
d_undef(struct place *p, struct place *p2, char *line)
351
{
352
	(void)p;
353
354
	uncomment(line);
355
	oneword("#undef", p2, line);
356
	macro_undef(line);
357
}
358
359
////////////////////////////////////////////////////////////
360
// includes
361
362
static
363
bool
364
tryinclude(struct place *p, char *line)
365
{
366
	size_t len;
367
368
	len = strlen(line);
369
	if (len > 2 && line[0] == '"' && line[len-1] == '"') {
370
		line[len-1] = '\0';
371
		file_readquote(p, line+1);
372
		line[len-1] = '"';
373
		return true;
374
	}
375
	if (len > 2 && line[0] == '<' && line[len-1] == '>') {
376
		line[len-1] = '\0';
377
		file_readbracket(p, line+1);
378
		line[len-1] = '>';
379
		return true;
380
	}
381
	return false;
382
}
383
384
static
385
void
386
d_include(struct place *p, struct place *p2, char *line)
387
{
388
	char *text;
389
	size_t oldlen;
390
391
	uncomment(line);
392
	if (tryinclude(p, line)) {
393
		return;
394
	}
395
	text = macroexpand(p2, line, strlen(line), false);
396
397
	oldlen = strlen(text);
398
	uncomment(text);
399
	/* trim to fit, so the malloc debugging won't complain */
400
	text = dorealloc(text, oldlen + 1, strlen(text) + 1);
401
402
	if (tryinclude(p, text)) {
403
		dostrfree(text);
404
		return;
405
	}
406
	complain(p, "Illegal #include directive");
407
	complain(p, "Before macro expansion: #include %s", line);
408
	complain(p, "After macro expansion: #include %s", text);
409
	dostrfree(text);
410
	complain_fail();
411
}
412
413
static
414
void
415
d_line(struct place *p, struct place *p2, char *line)
416
{
417
	(void)p2;
418
	(void)line;
419
420
	/* XXX */
421
	complain(p, "Sorry, no #line yet");
422
}
423
424
////////////////////////////////////////////////////////////
425
// messages
426
427
static
428
void
429
d_warning(struct place *p, struct place *p2, char *line)
430
{
431
	char *msg;
432
433
	msg = macroexpand(p2, line, strlen(line), false);
434
	complain(p, "#warning: %s", msg);
435
	if (mode.werror) {
436
		complain_fail();
437
	}
438
	dostrfree(msg);
439
}
440
441
static
442
void
443
d_error(struct place *p, struct place *p2, char *line)
444
{
445
	char *msg;
446
447
	msg = macroexpand(p2, line, strlen(line), false);
448
	complain(p, "#error: %s", msg);
449
	complain_fail();
450
	dostrfree(msg);
451
}
452
453
////////////////////////////////////////////////////////////
454
// other
455
456
static
457
void
458
d_pragma(struct place *p, struct place *p2, char *line)
459
{
460
	(void)p2;
461
462
	complain(p, "#pragma %s", line);
463
	complain_fail();
464
}
465
466
////////////////////////////////////////////////////////////
467
// directive table
468
469
static const struct {
470
	const char *name;
471
	bool ifskip;
472
	void (*func)(struct place *, struct place *, char *line);
473
} directives[] = {
474
	{ "define",  true,  d_define },
475
	{ "elif",    false, d_elif },
476
	{ "else",    false, d_else },
477
	{ "endif",   false, d_endif },
478
	{ "error",   true,  d_error },
479
	{ "if",      false, d_if },
480
	{ "ifdef",   false, d_ifdef },
481
	{ "ifndef",  false, d_ifndef },
482
	{ "include", true,  d_include },
483
	{ "line",    true,  d_line },
484
	{ "pragma",  true,  d_pragma },
485
	{ "undef",   true,  d_undef },
486
	{ "warning", true,  d_warning },
487
};
488
static const unsigned numdirectives = HOWMANY(directives);
489
490
static
491
void
492
directive_gotdirective(struct place *p, char *line)
493
{
494
	struct place p2;
495
	size_t len, skip;
496
	unsigned i;
497
498
	p2 = *p;
499
	for (i=0; i<numdirectives; i++) {
500
		len = strlen(directives[i].name);
501
		if (!strncmp(line, directives[i].name, len) &&
502
		    strchr(ws, line[len])) {
503
			if (directives[i].ifskip && !ifstate->curtrue) {
504
				return;
505
			}
506
			skip = len + strspn(line+len, ws);
507
			p2.column += skip;
508
			line += skip;
509
510
			len = strlen(line);
511
			len = notrailingws(line, len);
512
			if (len < strlen(line)) {
513
				line[len] = '\0';
514
			}
515
			directives[i].func(p, &p2, line);
516
			return;
517
		}
518
	}
519
	/* ugh. allow # by itself, including with a comment after it */
520
	uncomment(line);
521
	if (line[0] == '\0') {
522
		return;
523
	}
524
525
	skip = strcspn(line, ws);
526
	complain(p, "Unknown directive #%.*s", (int)skip, line);
527
	complain_fail();
528
}
529
530
/*
531
 * Check for nested comment delimiters in LINE.
532
 */
533
static
534
size_t
535
directive_scancomments(const struct place *p, char *line, size_t len)
536
{
537
	size_t pos;
538
	bool incomment;
539
	struct place p2;
540
541
	p2 = *p;
542
	incomment = 0;
543
	for (pos = 0; pos+1 < len; pos++) {
544
		if (line[pos] == '/' && line[pos+1] == '*') {
545
			if (incomment) {
546
				complain(&p2, "Warning: %c%c within comment",
547
					 '/', '*');
548
				if (mode.werror) {
549
					complain_failed();
550
				}
551
			} else {
552
				incomment = true;
553
			}
554
			pos++;
555
		} else if (line[pos] == '*' && line[pos+1] == '/') {
556
			if (incomment) {
557
				incomment = false;
558
			} else {
559
				/* stray end-comment; should we care? */
560
			}
561
			pos++;
562
		}
563
		if (line[pos] == '\n') {
564
			p2.line++;
565
			p2.column = 0;
566
		} else {
567
			p2.column++;
568
		}
569
	}
570
571
	/* multiline comments are supposed to arrive in a single buffer */
572
	assert(!incomment);
573
	return len;
574
}
575
576
void
577
directive_gotline(struct place *p, char *line, size_t len)
578
{
579
	size_t skip;
580
581
7780764
	if (warns.nestcomment) {
582
		directive_scancomments(p, line, len);
583
	}
584
585
	/* check if we have a directive line (# exactly in column 0) */
586
3890382
	if (line[0] == '#') {
587
		skip = 1 + strspn(line + 1, ws);
588
		assert(skip <= len);
589
		p->column += skip;
590
		assert(line[len] == '\0');
591
		directive_gotdirective(p, line+skip /*, length = len-skip */);
592
		p->column += len-skip;
593
3890382
	} else if (ifstate->curtrue) {
594
3890382
		macro_sendline(p, line, len);
595
		p->column += len;
596
3890382
	}
597
7780764
}
598
599
void
600
directive_goteof(struct place *p)
601
{
602
4386
	while (ifstate->prev != NULL) {
603
		complain(p, "Missing #endif");
604
		complain(&ifstate->startplace, "...opened at this point");
605
		complain_failed();
606
		ifstate_pop();
607
	}
608
1462
	macro_sendeof(p);
609
1462
}
610
611
////////////////////////////////////////////////////////////
612
// module initialization
613
614
void
615
directive_init(void)
616
{
617
2924
	ifstate = ifstate_create(NULL, NULL, true);
618
1462
}
619
620
void
621
directive_cleanup(void)
622
{
623
2924
	assert(ifstate->prev == NULL);
624
1462
	ifstate_destroy(ifstate);
625
1462
	ifstate = NULL;
626
1462
}