xref: /openbsd-src/libexec/tradcpp/directive.c (revision 88157d21ef4e0e7edfe65c55d80f66401a578afc)
1a9b3ff1aSjsg /*-
2a9b3ff1aSjsg  * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc.
3a9b3ff1aSjsg  * All rights reserved.
4a9b3ff1aSjsg  *
5a9b3ff1aSjsg  * This code is derived from software contributed to The NetBSD Foundation
6a9b3ff1aSjsg  * by David A. Holland.
7a9b3ff1aSjsg  *
8a9b3ff1aSjsg  * Redistribution and use in source and binary forms, with or without
9a9b3ff1aSjsg  * modification, are permitted provided that the following conditions
10a9b3ff1aSjsg  * are met:
11a9b3ff1aSjsg  * 1. Redistributions of source code must retain the above copyright
12a9b3ff1aSjsg  *    notice, this list of conditions and the following disclaimer.
13a9b3ff1aSjsg  * 2. Redistributions in binary form must reproduce the above copyright
14a9b3ff1aSjsg  *    notice, this list of conditions and the following disclaimer in the
15a9b3ff1aSjsg  *    documentation and/or other materials provided with the distribution.
16a9b3ff1aSjsg  *
17a9b3ff1aSjsg  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18a9b3ff1aSjsg  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19a9b3ff1aSjsg  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20a9b3ff1aSjsg  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21a9b3ff1aSjsg  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22a9b3ff1aSjsg  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23a9b3ff1aSjsg  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24a9b3ff1aSjsg  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25a9b3ff1aSjsg  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26a9b3ff1aSjsg  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27a9b3ff1aSjsg  * POSSIBILITY OF SUCH DAMAGE.
28a9b3ff1aSjsg  */
29a9b3ff1aSjsg 
30a9b3ff1aSjsg #include <assert.h>
31a9b3ff1aSjsg #include <stdlib.h>
32a9b3ff1aSjsg #include <string.h>
33f9343feaSjsg #include <limits.h>
34f9343feaSjsg #include <errno.h>
35a9b3ff1aSjsg 
36f9343feaSjsg #include "bool.h"
37a9b3ff1aSjsg #include "utils.h"
38a9b3ff1aSjsg #include "mode.h"
39a9b3ff1aSjsg #include "place.h"
40a9b3ff1aSjsg #include "files.h"
41a9b3ff1aSjsg #include "directive.h"
42a9b3ff1aSjsg #include "macro.h"
43a9b3ff1aSjsg #include "eval.h"
44a9b3ff1aSjsg #include "output.h"
45a9b3ff1aSjsg 
46a9b3ff1aSjsg struct ifstate {
47a9b3ff1aSjsg 	struct ifstate *prev;
48a9b3ff1aSjsg 	struct place startplace;
49a9b3ff1aSjsg 	bool curtrue;
50a9b3ff1aSjsg 	bool evertrue;
51a9b3ff1aSjsg 	bool seenelse;
52a9b3ff1aSjsg };
53a9b3ff1aSjsg 
54a9b3ff1aSjsg static struct ifstate *ifstate;
55a9b3ff1aSjsg 
56a9b3ff1aSjsg ////////////////////////////////////////////////////////////
57a9b3ff1aSjsg // common parsing bits
58a9b3ff1aSjsg 
59a9b3ff1aSjsg static
60a9b3ff1aSjsg void
uncomment(char * buf)61a9b3ff1aSjsg uncomment(char *buf)
62a9b3ff1aSjsg {
63a9b3ff1aSjsg 	char *s, *t, *u = NULL;
64a9b3ff1aSjsg 	bool incomment = false;
65a9b3ff1aSjsg 	bool inesc = false;
66a9b3ff1aSjsg 	bool inquote = false;
67a9b3ff1aSjsg 	char quote = '\0';
68a9b3ff1aSjsg 
69a9b3ff1aSjsg 	for (s = t = buf; *s; s++) {
70a9b3ff1aSjsg 		if (incomment) {
71a9b3ff1aSjsg 			if (s[0] == '*' && s[1] == '/') {
72a9b3ff1aSjsg 				s++;
73a9b3ff1aSjsg 				incomment = false;
74a9b3ff1aSjsg 			}
75a9b3ff1aSjsg 		} else {
76a9b3ff1aSjsg 			if (!inquote && s[0] == '/' && s[1] == '*') {
77a9b3ff1aSjsg 				incomment = true;
78a9b3ff1aSjsg 			} else {
79a9b3ff1aSjsg 				if (inesc) {
80a9b3ff1aSjsg 					inesc = false;
81a9b3ff1aSjsg 				} else if (s[0] == '\\') {
82a9b3ff1aSjsg 					inesc = true;
83a9b3ff1aSjsg 				} else if (!inquote &&
84a9b3ff1aSjsg 					   (s[0] == '"' || s[0] == '\'')) {
85a9b3ff1aSjsg 					inquote = true;
86a9b3ff1aSjsg 					quote = s[0];
87a9b3ff1aSjsg 				} else if (inquote && s[0] == quote) {
88a9b3ff1aSjsg 					inquote = false;
89a9b3ff1aSjsg 				}
90a9b3ff1aSjsg 
91a9b3ff1aSjsg 				if (t != s) {
92a9b3ff1aSjsg 					*t = *s;
93a9b3ff1aSjsg 				}
94a9b3ff1aSjsg 				if (!strchr(ws, *t)) {
95a9b3ff1aSjsg 					u = t;
96a9b3ff1aSjsg 				}
97a9b3ff1aSjsg 				t++;
98a9b3ff1aSjsg 			}
99a9b3ff1aSjsg 		}
100a9b3ff1aSjsg 	}
101a9b3ff1aSjsg 	if (u) {
102a9b3ff1aSjsg 		/* end string after last non-whitespace char */
103a9b3ff1aSjsg 		u[1] = '\0';
104a9b3ff1aSjsg 	} else {
105a9b3ff1aSjsg 		*t = '\0';
106a9b3ff1aSjsg 	}
107a9b3ff1aSjsg }
108a9b3ff1aSjsg 
109a9b3ff1aSjsg static
110a9b3ff1aSjsg void
oneword(const char * what,struct place * p2,char * line)111a9b3ff1aSjsg oneword(const char *what, struct place *p2, char *line)
112a9b3ff1aSjsg {
113a9b3ff1aSjsg 	size_t pos;
114a9b3ff1aSjsg 
115a9b3ff1aSjsg 	pos = strcspn(line, ws);
116a9b3ff1aSjsg 	if (line[pos] != '\0') {
117*88157d21Sjsg 		place_addcolumns(p2, pos);
118a9b3ff1aSjsg 		complain(p2, "Garbage after %s argument", what);
119a9b3ff1aSjsg 		complain_fail();
120a9b3ff1aSjsg 		line[pos] = '\0';
121a9b3ff1aSjsg 	}
122a9b3ff1aSjsg }
123a9b3ff1aSjsg 
124a9b3ff1aSjsg ////////////////////////////////////////////////////////////
125a9b3ff1aSjsg // if handling
126a9b3ff1aSjsg 
127a9b3ff1aSjsg static
128a9b3ff1aSjsg struct ifstate *
ifstate_create(struct ifstate * prev,struct place * p,bool startstate)129a9b3ff1aSjsg ifstate_create(struct ifstate *prev, struct place *p, bool startstate)
130a9b3ff1aSjsg {
131a9b3ff1aSjsg 	struct ifstate *is;
132a9b3ff1aSjsg 
133a9b3ff1aSjsg 	is = domalloc(sizeof(*is));
134a9b3ff1aSjsg 	is->prev = prev;
135a9b3ff1aSjsg 	if (p != NULL) {
136a9b3ff1aSjsg 		is->startplace = *p;
137a9b3ff1aSjsg 	} else {
138a9b3ff1aSjsg 		place_setbuiltin(&is->startplace, 1);
139a9b3ff1aSjsg 	}
140a9b3ff1aSjsg 	is->curtrue = startstate;
141a9b3ff1aSjsg 	is->evertrue = is->curtrue;
142a9b3ff1aSjsg 	is->seenelse = false;
143a9b3ff1aSjsg 	return is;
144a9b3ff1aSjsg }
145a9b3ff1aSjsg 
146a9b3ff1aSjsg static
147a9b3ff1aSjsg void
ifstate_destroy(struct ifstate * is)148a9b3ff1aSjsg ifstate_destroy(struct ifstate *is)
149a9b3ff1aSjsg {
150a9b3ff1aSjsg 	dofree(is, sizeof(*is));
151a9b3ff1aSjsg }
152a9b3ff1aSjsg 
153a9b3ff1aSjsg static
154a9b3ff1aSjsg void
ifstate_push(struct place * p,bool startstate)155a9b3ff1aSjsg ifstate_push(struct place *p, bool startstate)
156a9b3ff1aSjsg {
157a9b3ff1aSjsg 	struct ifstate *newstate;
158a9b3ff1aSjsg 
159a9b3ff1aSjsg 	newstate = ifstate_create(ifstate, p, startstate);
160a9b3ff1aSjsg 	if (!ifstate->curtrue) {
161a9b3ff1aSjsg 		newstate->curtrue = false;
162a9b3ff1aSjsg 		newstate->evertrue = true;
163a9b3ff1aSjsg 	}
164a9b3ff1aSjsg 	ifstate = newstate;
165a9b3ff1aSjsg }
166a9b3ff1aSjsg 
167a9b3ff1aSjsg static
168a9b3ff1aSjsg void
ifstate_pop(void)169a9b3ff1aSjsg ifstate_pop(void)
170a9b3ff1aSjsg {
171a9b3ff1aSjsg 	struct ifstate *is;
172a9b3ff1aSjsg 
173a9b3ff1aSjsg 	is = ifstate;
174a9b3ff1aSjsg 	ifstate = ifstate->prev;
175a9b3ff1aSjsg 	ifstate_destroy(is);
176a9b3ff1aSjsg }
177a9b3ff1aSjsg 
178a9b3ff1aSjsg static
179a9b3ff1aSjsg void
d_if(struct lineplace * lp,struct place * p2,char * line)180f9343feaSjsg d_if(struct lineplace *lp, struct place *p2, char *line)
181a9b3ff1aSjsg {
182f9343feaSjsg 	bool doprint;
183a9b3ff1aSjsg 	char *expr;
184a9b3ff1aSjsg 	bool val;
185a9b3ff1aSjsg 	struct place p3 = *p2;
186a9b3ff1aSjsg 	size_t oldlen;
187a9b3ff1aSjsg 
188f9343feaSjsg 	doprint = ifstate->curtrue;
189f9343feaSjsg 
190a9b3ff1aSjsg 	expr = macroexpand(p2, line, strlen(line), true);
191a9b3ff1aSjsg 
192a9b3ff1aSjsg 	oldlen = strlen(expr);
193a9b3ff1aSjsg 	uncomment(expr);
194a9b3ff1aSjsg 	/* trim to fit, so the malloc debugging won't complain */
195a9b3ff1aSjsg 	expr = dorealloc(expr, oldlen + 1, strlen(expr) + 1);
196a9b3ff1aSjsg 
197a9b3ff1aSjsg 	if (ifstate->curtrue) {
198a9b3ff1aSjsg 		val = eval(&p3, expr);
199a9b3ff1aSjsg 	} else {
200a9b3ff1aSjsg 		val = 0;
201a9b3ff1aSjsg 	}
202f9343feaSjsg 	ifstate_push(&lp->current, val);
203a9b3ff1aSjsg 	dostrfree(expr);
204f9343feaSjsg 
205f9343feaSjsg 	if (doprint) {
206f9343feaSjsg 		debuglog(&lp->current, "#if: %s",
207f9343feaSjsg 			  ifstate->curtrue ? "taken" : "not taken");
208f9343feaSjsg 	}
209a9b3ff1aSjsg }
210a9b3ff1aSjsg 
211a9b3ff1aSjsg static
212a9b3ff1aSjsg void
d_ifdef(struct lineplace * lp,struct place * p2,char * line)213f9343feaSjsg d_ifdef(struct lineplace *lp, struct place *p2, char *line)
214a9b3ff1aSjsg {
215f9343feaSjsg 	bool doprint;
216f9343feaSjsg 
217f9343feaSjsg 	doprint = ifstate->curtrue;
218f9343feaSjsg 
219a9b3ff1aSjsg 	uncomment(line);
220a9b3ff1aSjsg 	oneword("#ifdef", p2, line);
221f9343feaSjsg 	ifstate_push(&lp->current, macro_isdefined(line));
222f9343feaSjsg 
223f9343feaSjsg 	if (doprint) {
224f9343feaSjsg 		debuglog(&lp->current, "#ifdef %s: %s",
225f9343feaSjsg 			 line, ifstate->curtrue ? "taken" : "not taken");
226f9343feaSjsg 	}
227a9b3ff1aSjsg }
228a9b3ff1aSjsg 
229a9b3ff1aSjsg static
230a9b3ff1aSjsg void
d_ifndef(struct lineplace * lp,struct place * p2,char * line)231f9343feaSjsg d_ifndef(struct lineplace *lp, struct place *p2, char *line)
232a9b3ff1aSjsg {
233f9343feaSjsg 	bool doprint;
234f9343feaSjsg 
235f9343feaSjsg 	doprint = ifstate->curtrue;
236f9343feaSjsg 
237a9b3ff1aSjsg 	uncomment(line);
238a9b3ff1aSjsg 	oneword("#ifndef", p2, line);
239f9343feaSjsg 	ifstate_push(&lp->current, !macro_isdefined(line));
240f9343feaSjsg 
241f9343feaSjsg 	if (doprint) {
242f9343feaSjsg 		debuglog(&lp->current, "#ifndef %s: %s",
243f9343feaSjsg 			 line, ifstate->curtrue ? "taken" : "not taken");
244f9343feaSjsg 	}
245a9b3ff1aSjsg }
246a9b3ff1aSjsg 
247a9b3ff1aSjsg static
248a9b3ff1aSjsg void
d_elif(struct lineplace * lp,struct place * p2,char * line)249f9343feaSjsg d_elif(struct lineplace *lp, struct place *p2, char *line)
250a9b3ff1aSjsg {
251f9343feaSjsg 	bool doprint;
252a9b3ff1aSjsg 	char *expr;
253a9b3ff1aSjsg 	struct place p3 = *p2;
254a9b3ff1aSjsg 	size_t oldlen;
255a9b3ff1aSjsg 
256a9b3ff1aSjsg 	if (ifstate->seenelse) {
257f9343feaSjsg 		complain(&lp->current, "#elif after #else");
258a9b3ff1aSjsg 		complain_fail();
259a9b3ff1aSjsg 	}
260a9b3ff1aSjsg 
261f9343feaSjsg 	doprint = ifstate->curtrue;
262f9343feaSjsg 
263a9b3ff1aSjsg 	if (ifstate->evertrue) {
264a9b3ff1aSjsg 		ifstate->curtrue = false;
265a9b3ff1aSjsg 	} else {
266a9b3ff1aSjsg 		expr = macroexpand(p2, line, strlen(line), true);
267a9b3ff1aSjsg 
268a9b3ff1aSjsg 		oldlen = strlen(expr);
269a9b3ff1aSjsg 		uncomment(expr);
270a9b3ff1aSjsg 		/* trim to fit, so the malloc debugging won't complain */
271a9b3ff1aSjsg 		expr = dorealloc(expr, oldlen + 1, strlen(expr) + 1);
272a9b3ff1aSjsg 
273a9b3ff1aSjsg 		ifstate->curtrue = eval(&p3, expr);
274a9b3ff1aSjsg 		ifstate->evertrue = ifstate->curtrue;
275a9b3ff1aSjsg 		dostrfree(expr);
276a9b3ff1aSjsg 	}
277f9343feaSjsg 
278f9343feaSjsg 	if (doprint) {
279f9343feaSjsg 		debuglog2(&lp->current, &ifstate->startplace, "#elif: %s",
280f9343feaSjsg 			  ifstate->curtrue ? "taken" : "not taken");
281f9343feaSjsg 	}
282a9b3ff1aSjsg }
283a9b3ff1aSjsg 
284a9b3ff1aSjsg static
285a9b3ff1aSjsg void
d_else(struct lineplace * lp,struct place * p2,char * line)286f9343feaSjsg d_else(struct lineplace *lp, struct place *p2, char *line)
287a9b3ff1aSjsg {
288f9343feaSjsg 	bool doprint;
289f9343feaSjsg 
290a9b3ff1aSjsg 	(void)p2;
291a9b3ff1aSjsg 	(void)line;
292a9b3ff1aSjsg 
293a9b3ff1aSjsg 	if (ifstate->seenelse) {
294f9343feaSjsg 		complain(&lp->current,
295f9343feaSjsg 			 "Multiple #else directives in one conditional");
296a9b3ff1aSjsg 		complain_fail();
297a9b3ff1aSjsg 	}
298a9b3ff1aSjsg 
299f9343feaSjsg 	doprint = ifstate->curtrue;
300f9343feaSjsg 
301a9b3ff1aSjsg 	ifstate->curtrue = !ifstate->evertrue;
302a9b3ff1aSjsg 	ifstate->evertrue = true;
303a9b3ff1aSjsg 	ifstate->seenelse = true;
304f9343feaSjsg 
305f9343feaSjsg 	if (doprint) {
306f9343feaSjsg 		debuglog2(&lp->current, &ifstate->startplace, "#else: %s",
307f9343feaSjsg 			  ifstate->curtrue ? "taken" : "not taken");
308f9343feaSjsg 	}
309a9b3ff1aSjsg }
310a9b3ff1aSjsg 
311a9b3ff1aSjsg static
312a9b3ff1aSjsg void
d_endif(struct lineplace * lp,struct place * p2,char * line)313f9343feaSjsg d_endif(struct lineplace *lp, struct place *p2, char *line)
314a9b3ff1aSjsg {
315a9b3ff1aSjsg 	(void)p2;
316a9b3ff1aSjsg 	(void)line;
317a9b3ff1aSjsg 
318a9b3ff1aSjsg 	if (ifstate->prev == NULL) {
319f9343feaSjsg 		complain(&lp->current, "Unmatched #endif");
320a9b3ff1aSjsg 		complain_fail();
321a9b3ff1aSjsg 	} else {
322f9343feaSjsg 		debuglog2(&lp->current, &ifstate->startplace, "#endif");
323a9b3ff1aSjsg 		ifstate_pop();
324a9b3ff1aSjsg 	}
325a9b3ff1aSjsg }
326a9b3ff1aSjsg 
327a9b3ff1aSjsg ////////////////////////////////////////////////////////////
328a9b3ff1aSjsg // macros
329a9b3ff1aSjsg 
330a9b3ff1aSjsg static
331a9b3ff1aSjsg void
d_define(struct lineplace * lp,struct place * p2,char * line)332f9343feaSjsg d_define(struct lineplace *lp, struct place *p2, char *line)
333a9b3ff1aSjsg {
334a9b3ff1aSjsg 	size_t pos, argpos;
335a9b3ff1aSjsg 	struct place p3, p4;
336a9b3ff1aSjsg 
337f9343feaSjsg 	(void)lp;
338a9b3ff1aSjsg 
339a9b3ff1aSjsg 	/*
340a9b3ff1aSjsg 	 * line may be:
341a9b3ff1aSjsg 	 *    macro expansion
342a9b3ff1aSjsg 	 *    macro(arg, arg, ...) expansion
343a9b3ff1aSjsg 	 */
344a9b3ff1aSjsg 
345a9b3ff1aSjsg 	pos = strcspn(line, " \t\f\v(");
346a9b3ff1aSjsg 	if (line[pos] == '(') {
347a9b3ff1aSjsg 		line[pos++] = '\0';
348a9b3ff1aSjsg 		argpos = pos;
349a9b3ff1aSjsg 		pos = pos + strcspn(line+pos, "()");
350a9b3ff1aSjsg 		if (line[pos] == '(') {
351*88157d21Sjsg 			place_addcolumns(p2, pos);
352a9b3ff1aSjsg 			complain(p2, "Left parenthesis in macro parameters");
353a9b3ff1aSjsg 			complain_fail();
354a9b3ff1aSjsg 			return;
355a9b3ff1aSjsg 		}
356a9b3ff1aSjsg 		if (line[pos] != ')') {
357*88157d21Sjsg 			place_addcolumns(p2, pos);
358a9b3ff1aSjsg 			complain(p2, "Unclosed macro parameter list");
359a9b3ff1aSjsg 			complain_fail();
360a9b3ff1aSjsg 			return;
361a9b3ff1aSjsg 		}
362a9b3ff1aSjsg 		line[pos++] = '\0';
363a9b3ff1aSjsg #if 0
364a9b3ff1aSjsg 		if (!strchr(ws, line[pos])) {
365a9b3ff1aSjsg 			p2->column += pos;
366a9b3ff1aSjsg 			complain(p2, "Trash after macro parameter list");
367a9b3ff1aSjsg 			complain_fail();
368a9b3ff1aSjsg 			return;
369a9b3ff1aSjsg 		}
370a9b3ff1aSjsg #endif
371a9b3ff1aSjsg 	} else if (line[pos] == '\0') {
372a9b3ff1aSjsg 		argpos = 0;
373a9b3ff1aSjsg 	} else {
374a9b3ff1aSjsg 		line[pos++] = '\0';
375a9b3ff1aSjsg 		argpos = 0;
376a9b3ff1aSjsg 	}
377a9b3ff1aSjsg 
378a9b3ff1aSjsg 	pos += strspn(line+pos, ws);
379a9b3ff1aSjsg 
380a9b3ff1aSjsg 	p3 = *p2;
381*88157d21Sjsg 	place_addcolumns(&p3, argpos);
382a9b3ff1aSjsg 
383a9b3ff1aSjsg 	p4 = *p2;
384*88157d21Sjsg 	place_addcolumns(&p4, pos);
385a9b3ff1aSjsg 
386a9b3ff1aSjsg 	if (argpos) {
387f9343feaSjsg 		debuglog(&lp->current, "Defining %s()", line);
388a9b3ff1aSjsg 		macro_define_params(p2, line, &p3,
389a9b3ff1aSjsg 				    line + argpos, &p4,
390a9b3ff1aSjsg 				    line + pos);
391a9b3ff1aSjsg 	} else {
392f9343feaSjsg 		debuglog(&lp->current, "Defining %s", line);
393a9b3ff1aSjsg 		macro_define_plain(p2, line, &p4, line + pos);
394a9b3ff1aSjsg 	}
395a9b3ff1aSjsg }
396a9b3ff1aSjsg 
397a9b3ff1aSjsg static
398a9b3ff1aSjsg void
d_undef(struct lineplace * lp,struct place * p2,char * line)399f9343feaSjsg d_undef(struct lineplace *lp, struct place *p2, char *line)
400a9b3ff1aSjsg {
401f9343feaSjsg 	(void)lp;
402a9b3ff1aSjsg 
403a9b3ff1aSjsg 	uncomment(line);
404a9b3ff1aSjsg 	oneword("#undef", p2, line);
405f9343feaSjsg 	debuglog(&lp->current, "Undef %s", line);
406a9b3ff1aSjsg 	macro_undef(line);
407a9b3ff1aSjsg }
408a9b3ff1aSjsg 
409a9b3ff1aSjsg ////////////////////////////////////////////////////////////
410a9b3ff1aSjsg // includes
411a9b3ff1aSjsg 
412a9b3ff1aSjsg static
413a9b3ff1aSjsg bool
tryinclude(struct place * p,char * line)414a9b3ff1aSjsg tryinclude(struct place *p, char *line)
415a9b3ff1aSjsg {
416a9b3ff1aSjsg 	size_t len;
417a9b3ff1aSjsg 
418a9b3ff1aSjsg 	len = strlen(line);
419a9b3ff1aSjsg 	if (len > 2 && line[0] == '"' && line[len-1] == '"') {
420a9b3ff1aSjsg 		line[len-1] = '\0';
421f9343feaSjsg 		debuglog(p, "Entering include file \"%s\"", line+1);
422a9b3ff1aSjsg 		file_readquote(p, line+1);
423f9343feaSjsg 		debuglog(p, "Leaving include file \"%s\"", line+1);
424a9b3ff1aSjsg 		line[len-1] = '"';
425a9b3ff1aSjsg 		return true;
426a9b3ff1aSjsg 	}
427a9b3ff1aSjsg 	if (len > 2 && line[0] == '<' && line[len-1] == '>') {
428a9b3ff1aSjsg 		line[len-1] = '\0';
429f9343feaSjsg 		debuglog(p, "Entering include file <%s>", line+1);
430a9b3ff1aSjsg 		file_readbracket(p, line+1);
431f9343feaSjsg 		debuglog(p, "Leaving include file <%s>", line+1);
432a9b3ff1aSjsg 		line[len-1] = '>';
433a9b3ff1aSjsg 		return true;
434a9b3ff1aSjsg 	}
435a9b3ff1aSjsg 	return false;
436a9b3ff1aSjsg }
437a9b3ff1aSjsg 
438a9b3ff1aSjsg static
439a9b3ff1aSjsg void
d_include(struct lineplace * lp,struct place * p2,char * line)440f9343feaSjsg d_include(struct lineplace *lp, struct place *p2, char *line)
441a9b3ff1aSjsg {
442a9b3ff1aSjsg 	char *text;
443a9b3ff1aSjsg 	size_t oldlen;
444a9b3ff1aSjsg 
445a9b3ff1aSjsg 	uncomment(line);
446f9343feaSjsg 	if (tryinclude(&lp->current, line)) {
447a9b3ff1aSjsg 		return;
448a9b3ff1aSjsg 	}
449a9b3ff1aSjsg 	text = macroexpand(p2, line, strlen(line), false);
450a9b3ff1aSjsg 
451a9b3ff1aSjsg 	oldlen = strlen(text);
452a9b3ff1aSjsg 	uncomment(text);
453a9b3ff1aSjsg 	/* trim to fit, so the malloc debugging won't complain */
454a9b3ff1aSjsg 	text = dorealloc(text, oldlen + 1, strlen(text) + 1);
455a9b3ff1aSjsg 
456f9343feaSjsg 	if (tryinclude(&lp->current, text)) {
457a9b3ff1aSjsg 		dostrfree(text);
458a9b3ff1aSjsg 		return;
459a9b3ff1aSjsg 	}
460f9343feaSjsg 	complain(&lp->current, "Illegal #include directive");
461f9343feaSjsg 	complain(&lp->current, "Before macro expansion: #include %s", line);
462f9343feaSjsg 	complain(&lp->current, "After macro expansion: #include %s", text);
463a9b3ff1aSjsg 	dostrfree(text);
464a9b3ff1aSjsg 	complain_fail();
465a9b3ff1aSjsg }
466a9b3ff1aSjsg 
467a9b3ff1aSjsg static
468a9b3ff1aSjsg void
d_line(struct lineplace * lp,struct place * p2,char * line)469f9343feaSjsg d_line(struct lineplace *lp, struct place *p2, char *line)
470a9b3ff1aSjsg {
471f9343feaSjsg 	char *text;
472f9343feaSjsg 	size_t oldlen;
473f9343feaSjsg 	unsigned long val;
474f9343feaSjsg 	char *moretext;
475f9343feaSjsg 	size_t moretextlen;
476f9343feaSjsg 	char *filename;
477a9b3ff1aSjsg 
478f9343feaSjsg 	text = macroexpand(p2, line, strlen(line), true);
479f9343feaSjsg 
480f9343feaSjsg 	oldlen = strlen(text);
481f9343feaSjsg 	uncomment(text);
482f9343feaSjsg 	/* trim to fit, so the malloc debugging won't complain */
483f9343feaSjsg 	text = dorealloc(text, oldlen + 1, strlen(text) + 1);
484f9343feaSjsg 
485f9343feaSjsg 	/*
486f9343feaSjsg 	 * What we should have here: either 1234 "file.c",
487f9343feaSjsg 	 * or just 1234.
488f9343feaSjsg 	 */
489f9343feaSjsg 
490f9343feaSjsg 	errno = 0;
491f9343feaSjsg 	val = strtoul(text, &moretext, 10);
492f9343feaSjsg 	if (errno) {
493*88157d21Sjsg 		complain(&lp->current,
494*88157d21Sjsg 			 "Invalid line number in #line directive");
495f9343feaSjsg 		goto fail;
496f9343feaSjsg 	}
497f9343feaSjsg #if UINT_MAX < ULONG_MAX
498f9343feaSjsg 	if (val > UINT_MAX) {
499f9343feaSjsg 		complain(&lp->current,
500f9343feaSjsg 			 "Line number in #line directive too large");
501f9343feaSjsg 		goto fail;
502f9343feaSjsg 	}
503f9343feaSjsg #endif
504f9343feaSjsg 	moretext += strspn(moretext, ws);
505f9343feaSjsg 	moretextlen = strlen(moretext);
506*88157d21Sjsg 	place_addcolumns(&lp->current, moretext - text);
507f9343feaSjsg 
508f9343feaSjsg 	if (moretextlen > 2 &&
509f9343feaSjsg 	    moretext[0] == '"' && moretext[moretextlen-1] == '"') {
510f9343feaSjsg 		filename = dostrndup(moretext+1, moretextlen-2);
511f9343feaSjsg 		place_changefile(&lp->nextline, filename);
512f9343feaSjsg 		dostrfree(filename);
513f9343feaSjsg 	}
514f9343feaSjsg 	else if (moretextlen > 0) {
515f9343feaSjsg 		complain(&lp->current,
516f9343feaSjsg 			 "Invalid file name in #line directive");
517f9343feaSjsg 		goto fail;
518f9343feaSjsg 	}
519f9343feaSjsg 
520f9343feaSjsg 	lp->nextline.line = val;
521f9343feaSjsg 	dostrfree(text);
522f9343feaSjsg 	return;
523f9343feaSjsg 
524f9343feaSjsg fail:
525f9343feaSjsg 	complain(&lp->current, "Before macro expansion: #line %s", line);
526f9343feaSjsg 	complain(&lp->current, "After macro expansion: #line %s", text);
527f9343feaSjsg 	complain_fail();
528f9343feaSjsg 	dostrfree(text);
529a9b3ff1aSjsg }
530a9b3ff1aSjsg 
531a9b3ff1aSjsg ////////////////////////////////////////////////////////////
532a9b3ff1aSjsg // messages
533a9b3ff1aSjsg 
534a9b3ff1aSjsg static
535a9b3ff1aSjsg void
d_warning(struct lineplace * lp,struct place * p2,char * line)536f9343feaSjsg d_warning(struct lineplace *lp, struct place *p2, char *line)
537a9b3ff1aSjsg {
538a9b3ff1aSjsg 	char *msg;
539a9b3ff1aSjsg 
540a9b3ff1aSjsg 	msg = macroexpand(p2, line, strlen(line), false);
541f9343feaSjsg 	complain(&lp->current, "#warning: %s", msg);
542a9b3ff1aSjsg 	if (mode.werror) {
543a9b3ff1aSjsg 		complain_fail();
544a9b3ff1aSjsg 	}
545a9b3ff1aSjsg 	dostrfree(msg);
546a9b3ff1aSjsg }
547a9b3ff1aSjsg 
548a9b3ff1aSjsg static
549a9b3ff1aSjsg void
d_error(struct lineplace * lp,struct place * p2,char * line)550f9343feaSjsg d_error(struct lineplace *lp, struct place *p2, char *line)
551a9b3ff1aSjsg {
552a9b3ff1aSjsg 	char *msg;
553a9b3ff1aSjsg 
554a9b3ff1aSjsg 	msg = macroexpand(p2, line, strlen(line), false);
555f9343feaSjsg 	complain(&lp->current, "#error: %s", msg);
556a9b3ff1aSjsg 	complain_fail();
557a9b3ff1aSjsg 	dostrfree(msg);
558a9b3ff1aSjsg }
559a9b3ff1aSjsg 
560a9b3ff1aSjsg ////////////////////////////////////////////////////////////
561a9b3ff1aSjsg // other
562a9b3ff1aSjsg 
563a9b3ff1aSjsg static
564a9b3ff1aSjsg void
d_pragma(struct lineplace * lp,struct place * p2,char * line)565f9343feaSjsg d_pragma(struct lineplace *lp, struct place *p2, char *line)
566a9b3ff1aSjsg {
567a9b3ff1aSjsg 	(void)p2;
568a9b3ff1aSjsg 
569f9343feaSjsg 	complain(&lp->current, "#pragma %s", line);
570a9b3ff1aSjsg 	complain_fail();
571a9b3ff1aSjsg }
572a9b3ff1aSjsg 
573a9b3ff1aSjsg ////////////////////////////////////////////////////////////
574a9b3ff1aSjsg // directive table
575a9b3ff1aSjsg 
576a9b3ff1aSjsg static const struct {
577a9b3ff1aSjsg 	const char *name;
578a9b3ff1aSjsg 	bool ifskip;
579f9343feaSjsg 	void (*func)(struct lineplace *, struct place *, char *line);
580a9b3ff1aSjsg } directives[] = {
581a9b3ff1aSjsg 	{ "define",  true,  d_define },
582a9b3ff1aSjsg 	{ "elif",    false, d_elif },
583a9b3ff1aSjsg 	{ "else",    false, d_else },
584a9b3ff1aSjsg 	{ "endif",   false, d_endif },
585a9b3ff1aSjsg 	{ "error",   true,  d_error },
586a9b3ff1aSjsg 	{ "if",      false, d_if },
587a9b3ff1aSjsg 	{ "ifdef",   false, d_ifdef },
588a9b3ff1aSjsg 	{ "ifndef",  false, d_ifndef },
589a9b3ff1aSjsg 	{ "include", true,  d_include },
590a9b3ff1aSjsg 	{ "line",    true,  d_line },
591a9b3ff1aSjsg 	{ "pragma",  true,  d_pragma },
592a9b3ff1aSjsg 	{ "undef",   true,  d_undef },
593a9b3ff1aSjsg 	{ "warning", true,  d_warning },
594a9b3ff1aSjsg };
595a9b3ff1aSjsg static const unsigned numdirectives = HOWMANY(directives);
596a9b3ff1aSjsg 
597a9b3ff1aSjsg static
598a9b3ff1aSjsg void
directive_gotdirective(struct lineplace * lp,char * line)599f9343feaSjsg directive_gotdirective(struct lineplace *lp, char *line)
600a9b3ff1aSjsg {
601a9b3ff1aSjsg 	struct place p2;
602a9b3ff1aSjsg 	size_t len, skip;
603a9b3ff1aSjsg 	unsigned i;
604a9b3ff1aSjsg 
605f9343feaSjsg 	p2 = lp->current;
606a9b3ff1aSjsg 	for (i=0; i<numdirectives; i++) {
607a9b3ff1aSjsg 		len = strlen(directives[i].name);
608a9b3ff1aSjsg 		if (!strncmp(line, directives[i].name, len) &&
609a9b3ff1aSjsg 		    strchr(ws, line[len])) {
610a9b3ff1aSjsg 			if (directives[i].ifskip && !ifstate->curtrue) {
611a9b3ff1aSjsg 				return;
612a9b3ff1aSjsg 			}
613a9b3ff1aSjsg 			skip = len + strspn(line+len, ws);
614*88157d21Sjsg 			place_addcolumns(&p2, skip);
615a9b3ff1aSjsg 			line += skip;
616a9b3ff1aSjsg 
617a9b3ff1aSjsg 			len = strlen(line);
618a9b3ff1aSjsg 			len = notrailingws(line, len);
619a9b3ff1aSjsg 			if (len < strlen(line)) {
620a9b3ff1aSjsg 				line[len] = '\0';
621a9b3ff1aSjsg 			}
622f9343feaSjsg 			directives[i].func(lp, &p2, line);
623a9b3ff1aSjsg 			return;
624a9b3ff1aSjsg 		}
625a9b3ff1aSjsg 	}
626a9b3ff1aSjsg 	/* ugh. allow # by itself, including with a comment after it */
627a9b3ff1aSjsg 	uncomment(line);
628a9b3ff1aSjsg 	if (line[0] == '\0') {
629a9b3ff1aSjsg 		return;
630a9b3ff1aSjsg 	}
631a9b3ff1aSjsg 
632a9b3ff1aSjsg 	skip = strcspn(line, ws);
633f9343feaSjsg 	complain(&lp->current, "Unknown directive #%.*s", (int)skip, line);
634a9b3ff1aSjsg 	complain_fail();
635a9b3ff1aSjsg }
636a9b3ff1aSjsg 
637a9b3ff1aSjsg /*
638a9b3ff1aSjsg  * Check for nested comment delimiters in LINE.
639a9b3ff1aSjsg  */
640a9b3ff1aSjsg static
641a9b3ff1aSjsg size_t
directive_scancomments(const struct lineplace * lp,char * line,size_t len)642f9343feaSjsg directive_scancomments(const struct lineplace *lp, char *line, size_t len)
643a9b3ff1aSjsg {
644a9b3ff1aSjsg 	size_t pos;
645a9b3ff1aSjsg 	bool incomment;
646a9b3ff1aSjsg 	struct place p2;
647a9b3ff1aSjsg 
648f9343feaSjsg 	p2 = lp->current;
649a9b3ff1aSjsg 	incomment = 0;
650a9b3ff1aSjsg 	for (pos = 0; pos+1 < len; pos++) {
651a9b3ff1aSjsg 		if (line[pos] == '/' && line[pos+1] == '*') {
652a9b3ff1aSjsg 			if (incomment) {
653a9b3ff1aSjsg 				complain(&p2, "Warning: %c%c within comment",
654a9b3ff1aSjsg 					 '/', '*');
655a9b3ff1aSjsg 				if (mode.werror) {
656a9b3ff1aSjsg 					complain_failed();
657a9b3ff1aSjsg 				}
658a9b3ff1aSjsg 			} else {
659a9b3ff1aSjsg 				incomment = true;
660a9b3ff1aSjsg 			}
661a9b3ff1aSjsg 			pos++;
662a9b3ff1aSjsg 		} else if (line[pos] == '*' && line[pos+1] == '/') {
663a9b3ff1aSjsg 			if (incomment) {
664a9b3ff1aSjsg 				incomment = false;
665a9b3ff1aSjsg 			} else {
666a9b3ff1aSjsg 				/* stray end-comment; should we care? */
667a9b3ff1aSjsg 			}
668a9b3ff1aSjsg 			pos++;
669a9b3ff1aSjsg 		}
670a9b3ff1aSjsg 		if (line[pos] == '\n') {
671*88157d21Sjsg 			place_addlines(&p2, 1);
672a9b3ff1aSjsg 			p2.column = 0;
673a9b3ff1aSjsg 		} else {
674*88157d21Sjsg 			place_addcolumns(&p2, 1);
675a9b3ff1aSjsg 		}
676a9b3ff1aSjsg 	}
677a9b3ff1aSjsg 
678a9b3ff1aSjsg 	/* multiline comments are supposed to arrive in a single buffer */
679a9b3ff1aSjsg 	assert(!incomment);
680a9b3ff1aSjsg 	return len;
681a9b3ff1aSjsg }
682a9b3ff1aSjsg 
683a9b3ff1aSjsg void
directive_gotline(struct lineplace * lp,char * line,size_t len)684f9343feaSjsg directive_gotline(struct lineplace *lp, char *line, size_t len)
685a9b3ff1aSjsg {
686a9b3ff1aSjsg 	size_t skip;
687a9b3ff1aSjsg 
688a9b3ff1aSjsg 	if (warns.nestcomment) {
689f9343feaSjsg 		directive_scancomments(lp, line, len);
690a9b3ff1aSjsg 	}
691a9b3ff1aSjsg 
692a9b3ff1aSjsg 	/* check if we have a directive line (# exactly in column 0) */
693f9343feaSjsg 	if (len > 0 && line[0] == '#') {
694a9b3ff1aSjsg 		skip = 1 + strspn(line + 1, ws);
695a9b3ff1aSjsg 		assert(skip <= len);
696*88157d21Sjsg 		place_addcolumns(&lp->current, skip);
697a9b3ff1aSjsg 		assert(line[len] == '\0');
698f9343feaSjsg 		directive_gotdirective(lp, line+skip /*, length = len-skip */);
699*88157d21Sjsg 		place_addcolumns(&lp->current, len-skip);
700a9b3ff1aSjsg 	} else if (ifstate->curtrue) {
701f9343feaSjsg 		macro_sendline(&lp->current, line, len);
702*88157d21Sjsg 		place_addcolumns(&lp->current, len);
703a9b3ff1aSjsg 	}
704a9b3ff1aSjsg }
705a9b3ff1aSjsg 
706a9b3ff1aSjsg void
directive_goteof(struct place * p)707a9b3ff1aSjsg directive_goteof(struct place *p)
708a9b3ff1aSjsg {
709a9b3ff1aSjsg 	while (ifstate->prev != NULL) {
710a9b3ff1aSjsg 		complain(p, "Missing #endif");
711a9b3ff1aSjsg 		complain(&ifstate->startplace, "...opened at this point");
712a9b3ff1aSjsg 		complain_failed();
713a9b3ff1aSjsg 		ifstate_pop();
714a9b3ff1aSjsg 	}
715a9b3ff1aSjsg 	macro_sendeof(p);
716a9b3ff1aSjsg }
717a9b3ff1aSjsg 
718a9b3ff1aSjsg ////////////////////////////////////////////////////////////
719a9b3ff1aSjsg // module initialization
720a9b3ff1aSjsg 
721a9b3ff1aSjsg void
directive_init(void)722a9b3ff1aSjsg directive_init(void)
723a9b3ff1aSjsg {
724a9b3ff1aSjsg 	ifstate = ifstate_create(NULL, NULL, true);
725a9b3ff1aSjsg }
726a9b3ff1aSjsg 
727a9b3ff1aSjsg void
directive_cleanup(void)728a9b3ff1aSjsg directive_cleanup(void)
729a9b3ff1aSjsg {
730a9b3ff1aSjsg 	assert(ifstate->prev == NULL);
731a9b3ff1aSjsg 	ifstate_destroy(ifstate);
732a9b3ff1aSjsg 	ifstate = NULL;
733a9b3ff1aSjsg }
734