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