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 <stdio.h>
31a9b3ff1aSjsg #include <stdlib.h>
32a9b3ff1aSjsg #include <string.h>
33a9b3ff1aSjsg #include <unistd.h>
34a9b3ff1aSjsg #include <fcntl.h>
35a9b3ff1aSjsg #include <errno.h>
36a9b3ff1aSjsg
37f9343feaSjsg #include "bool.h"
38a9b3ff1aSjsg #include "array.h"
39a9b3ff1aSjsg #include "mode.h"
40a9b3ff1aSjsg #include "place.h"
41a9b3ff1aSjsg #include "files.h"
42a9b3ff1aSjsg #include "directive.h"
43a9b3ff1aSjsg
44a9b3ff1aSjsg struct incdir {
45a9b3ff1aSjsg const char *name;
46a9b3ff1aSjsg bool issystem;
47a9b3ff1aSjsg };
48a9b3ff1aSjsg
49a9b3ff1aSjsg DECLARRAY(incdir, static UNUSED);
50a9b3ff1aSjsg DEFARRAY(incdir, static);
51a9b3ff1aSjsg
52a9b3ff1aSjsg static struct incdirarray quotepath, bracketpath;
53a9b3ff1aSjsg
54a9b3ff1aSjsg ////////////////////////////////////////////////////////////
55a9b3ff1aSjsg // management
56a9b3ff1aSjsg
57a9b3ff1aSjsg static
58a9b3ff1aSjsg struct incdir *
incdir_create(const char * name,bool issystem)59a9b3ff1aSjsg incdir_create(const char *name, bool issystem)
60a9b3ff1aSjsg {
61a9b3ff1aSjsg struct incdir *id;
62a9b3ff1aSjsg
63a9b3ff1aSjsg id = domalloc(sizeof(*id));
64a9b3ff1aSjsg id->name = name;
65a9b3ff1aSjsg id->issystem = issystem;
66a9b3ff1aSjsg return id;
67a9b3ff1aSjsg }
68a9b3ff1aSjsg
69a9b3ff1aSjsg static
70a9b3ff1aSjsg void
incdir_destroy(struct incdir * id)71a9b3ff1aSjsg incdir_destroy(struct incdir *id)
72a9b3ff1aSjsg {
73a9b3ff1aSjsg dofree(id, sizeof(*id));
74a9b3ff1aSjsg }
75a9b3ff1aSjsg
76a9b3ff1aSjsg void
files_init(void)77a9b3ff1aSjsg files_init(void)
78a9b3ff1aSjsg {
79a9b3ff1aSjsg incdirarray_init("epath);
80a9b3ff1aSjsg incdirarray_init(&bracketpath);
81a9b3ff1aSjsg }
82a9b3ff1aSjsg
83a9b3ff1aSjsg DESTROYALL_ARRAY(incdir, );
84a9b3ff1aSjsg
85a9b3ff1aSjsg void
files_cleanup(void)86a9b3ff1aSjsg files_cleanup(void)
87a9b3ff1aSjsg {
88a9b3ff1aSjsg incdirarray_destroyall("epath);
89a9b3ff1aSjsg incdirarray_cleanup("epath);
90a9b3ff1aSjsg incdirarray_destroyall(&bracketpath);
91a9b3ff1aSjsg incdirarray_cleanup(&bracketpath);
92a9b3ff1aSjsg }
93a9b3ff1aSjsg
94a9b3ff1aSjsg ////////////////////////////////////////////////////////////
95a9b3ff1aSjsg // path setup
96a9b3ff1aSjsg
97a9b3ff1aSjsg void
files_addquotepath(const char * dir,bool issystem)98a9b3ff1aSjsg files_addquotepath(const char *dir, bool issystem)
99a9b3ff1aSjsg {
100a9b3ff1aSjsg struct incdir *id;
101a9b3ff1aSjsg
102a9b3ff1aSjsg id = incdir_create(dir, issystem);
103a9b3ff1aSjsg incdirarray_add("epath, id, NULL);
104a9b3ff1aSjsg }
105a9b3ff1aSjsg
106a9b3ff1aSjsg void
files_addbracketpath(const char * dir,bool issystem)107a9b3ff1aSjsg files_addbracketpath(const char *dir, bool issystem)
108a9b3ff1aSjsg {
109a9b3ff1aSjsg struct incdir *id;
110a9b3ff1aSjsg
111a9b3ff1aSjsg id = incdir_create(dir, issystem);
112a9b3ff1aSjsg incdirarray_add(&bracketpath, id, NULL);
113a9b3ff1aSjsg }
114a9b3ff1aSjsg
115a9b3ff1aSjsg ////////////////////////////////////////////////////////////
116a9b3ff1aSjsg // parsing
117a9b3ff1aSjsg
118a9b3ff1aSjsg /*
119a9b3ff1aSjsg * Find the end of the logical line. End of line characters that are
120a9b3ff1aSjsg * commented out do not count.
121a9b3ff1aSjsg */
122a9b3ff1aSjsg static
123a9b3ff1aSjsg size_t
findeol(const char * buf,size_t start,size_t limit)124a9b3ff1aSjsg findeol(const char *buf, size_t start, size_t limit)
125a9b3ff1aSjsg {
126a9b3ff1aSjsg size_t i;
127a9b3ff1aSjsg int incomment = 0;
128a9b3ff1aSjsg bool inquote = false;
129a9b3ff1aSjsg char quote = '\0';
130a9b3ff1aSjsg
131a9b3ff1aSjsg for (i=start; i<limit; i++) {
132a9b3ff1aSjsg if (incomment) {
133a9b3ff1aSjsg if (i+1 < limit && buf[i] == '*' && buf[i+1] == '/') {
134a9b3ff1aSjsg i++;
135a9b3ff1aSjsg incomment = 0;
136a9b3ff1aSjsg }
137a9b3ff1aSjsg } else if (!inquote && i+1 < limit &&
138a9b3ff1aSjsg buf[i] == '/' && buf[i+1] == '*') {
139a9b3ff1aSjsg i++;
140a9b3ff1aSjsg incomment = 1;
141a9b3ff1aSjsg } else if (i+1 < limit &&
142a9b3ff1aSjsg buf[i] == '\\' && buf[i+1] != '\n') {
143a9b3ff1aSjsg i++;
144a9b3ff1aSjsg } else if (!inquote && (buf[i] == '"' || buf[i] == '\'')) {
145a9b3ff1aSjsg inquote = true;
146a9b3ff1aSjsg quote = buf[i];
147a9b3ff1aSjsg } else if (inquote && buf[i] == quote) {
148a9b3ff1aSjsg inquote = false;
149a9b3ff1aSjsg } else if (buf[i] == '\n') {
150a9b3ff1aSjsg return i;
151a9b3ff1aSjsg }
152a9b3ff1aSjsg }
153a9b3ff1aSjsg return limit;
154a9b3ff1aSjsg }
155a9b3ff1aSjsg
156a9b3ff1aSjsg static
157a9b3ff1aSjsg unsigned
countnls(const char * buf,size_t start,size_t limit)158a9b3ff1aSjsg countnls(const char *buf, size_t start, size_t limit)
159a9b3ff1aSjsg {
160a9b3ff1aSjsg size_t i;
161a9b3ff1aSjsg unsigned count = 0;
162a9b3ff1aSjsg
163a9b3ff1aSjsg for (i=start; i<limit; i++) {
164a9b3ff1aSjsg if (buf[i] == '\n') {
165a9b3ff1aSjsg count++;
166*88157d21Sjsg if (count == 0) {
167*88157d21Sjsg /* just return the max and error downstream */
168*88157d21Sjsg return count - 1;
169*88157d21Sjsg }
170a9b3ff1aSjsg }
171a9b3ff1aSjsg }
172a9b3ff1aSjsg return count;
173a9b3ff1aSjsg }
174a9b3ff1aSjsg
175a9b3ff1aSjsg static
176a9b3ff1aSjsg void
file_read(const struct placefile * pf,int fd,const char * name,bool toplevel)177a9b3ff1aSjsg file_read(const struct placefile *pf, int fd, const char *name, bool toplevel)
178a9b3ff1aSjsg {
179f9343feaSjsg struct lineplace places;
180f9343feaSjsg struct place ptmp;
181a9b3ff1aSjsg size_t bufend, bufmax, linestart, lineend, nextlinestart, tmp;
182a9b3ff1aSjsg ssize_t result;
183a9b3ff1aSjsg bool ateof = false;
184a9b3ff1aSjsg char *buf;
185a9b3ff1aSjsg
186f9343feaSjsg place_setfilestart(&places.current, pf);
187f9343feaSjsg places.nextline = places.current;
188f9343feaSjsg
189f9343feaSjsg if (name) {
190f9343feaSjsg debuglog(&places.current, "Reading file %s", name);
191f9343feaSjsg } else {
192f9343feaSjsg debuglog(&places.current, "Reading standard input");
193f9343feaSjsg }
194a9b3ff1aSjsg
195a9b3ff1aSjsg bufmax = 128;
196a9b3ff1aSjsg bufend = 0;
197a9b3ff1aSjsg linestart = 0;
198a9b3ff1aSjsg lineend = 0;
199a9b3ff1aSjsg buf = domalloc(bufmax);
200a9b3ff1aSjsg
201a9b3ff1aSjsg while (1) {
202a9b3ff1aSjsg if (lineend >= bufend) {
203a9b3ff1aSjsg /* do not have a whole line in the buffer; read more */
204a9b3ff1aSjsg assert(bufend >= linestart);
205a9b3ff1aSjsg if (linestart > 0 && bufend > linestart) {
206a9b3ff1aSjsg /* slide to beginning of buffer */
207a9b3ff1aSjsg memmove(buf, buf+linestart, bufend-linestart);
208a9b3ff1aSjsg bufend -= linestart;
209a9b3ff1aSjsg lineend -= linestart;
210a9b3ff1aSjsg linestart = 0;
211a9b3ff1aSjsg }
212a9b3ff1aSjsg if (bufend >= bufmax) {
213a9b3ff1aSjsg /* need bigger buffer */
214a9b3ff1aSjsg buf = dorealloc(buf, bufmax, bufmax*2);
215a9b3ff1aSjsg bufmax = bufmax*2;
216*88157d21Sjsg /* just in case someone's screwing around */
217*88157d21Sjsg if (bufmax > 0xffffffff) {
218*88157d21Sjsg complain(&places.current,
219*88157d21Sjsg "Input line too long");
220*88157d21Sjsg die();
221*88157d21Sjsg }
222a9b3ff1aSjsg }
223a9b3ff1aSjsg
224a9b3ff1aSjsg if (ateof) {
225a9b3ff1aSjsg /* don't read again, in case it's a socket */
226a9b3ff1aSjsg result = 0;
227a9b3ff1aSjsg } else {
228a9b3ff1aSjsg result = read(fd, buf+bufend, bufmax - bufend);
229a9b3ff1aSjsg }
230a9b3ff1aSjsg
231a9b3ff1aSjsg if (result == -1) {
232a9b3ff1aSjsg /* read error */
233a9b3ff1aSjsg complain(NULL, "%s: %s",
234a9b3ff1aSjsg name, strerror(errno));
235a9b3ff1aSjsg complain_fail();
236a9b3ff1aSjsg } else if (result == 0 && bufend == linestart) {
237a9b3ff1aSjsg /* eof */
238a9b3ff1aSjsg ateof = true;
239a9b3ff1aSjsg break;
240a9b3ff1aSjsg } else if (result == 0) {
241a9b3ff1aSjsg /* eof in middle of line */
242a9b3ff1aSjsg ateof = true;
243f9343feaSjsg ptmp = places.current;
244*88157d21Sjsg place_addcolumns(&ptmp, bufend - linestart);
245f9343feaSjsg if (buf[bufend - 1] == '\n') {
246f9343feaSjsg complain(&ptmp, "Unclosed comment");
247f9343feaSjsg complain_fail();
248f9343feaSjsg } else {
249f9343feaSjsg complain(&ptmp,
250f9343feaSjsg "No newline at end of file");
251f9343feaSjsg }
252a9b3ff1aSjsg if (mode.werror) {
253a9b3ff1aSjsg complain_fail();
254a9b3ff1aSjsg }
255a9b3ff1aSjsg assert(bufend < bufmax);
256a9b3ff1aSjsg lineend = bufend++;
257a9b3ff1aSjsg buf[lineend] = '\n';
258a9b3ff1aSjsg } else {
259a9b3ff1aSjsg bufend += (size_t)result;
260a9b3ff1aSjsg lineend = findeol(buf, linestart, bufend);
261a9b3ff1aSjsg }
262a9b3ff1aSjsg /* loop in case we still don't have a whole line */
263a9b3ff1aSjsg continue;
264a9b3ff1aSjsg }
265a9b3ff1aSjsg
266a9b3ff1aSjsg /* have a line */
267a9b3ff1aSjsg assert(buf[lineend] == '\n');
268a9b3ff1aSjsg buf[lineend] = '\0';
269a9b3ff1aSjsg nextlinestart = lineend+1;
270*88157d21Sjsg place_addlines(&places.nextline, 1);
271a9b3ff1aSjsg
272a9b3ff1aSjsg /* check for CR/NL */
273a9b3ff1aSjsg if (lineend > 0 && buf[lineend-1] == '\r') {
274a9b3ff1aSjsg buf[lineend-1] = '\0';
275a9b3ff1aSjsg lineend--;
276a9b3ff1aSjsg }
277a9b3ff1aSjsg
278a9b3ff1aSjsg /* check for continuation line */
279a9b3ff1aSjsg if (lineend > 0 && buf[lineend-1]=='\\') {
280a9b3ff1aSjsg lineend--;
281a9b3ff1aSjsg tmp = nextlinestart - lineend;
282a9b3ff1aSjsg if (bufend > nextlinestart) {
283a9b3ff1aSjsg memmove(buf+lineend, buf+nextlinestart,
284a9b3ff1aSjsg bufend - nextlinestart);
285a9b3ff1aSjsg }
286a9b3ff1aSjsg bufend -= tmp;
287a9b3ff1aSjsg nextlinestart -= tmp;
288a9b3ff1aSjsg lineend = findeol(buf, linestart, bufend);
289a9b3ff1aSjsg /* might not have a whole line, so loop */
290a9b3ff1aSjsg continue;
291a9b3ff1aSjsg }
292a9b3ff1aSjsg
293a9b3ff1aSjsg /* line now goes from linestart to lineend */
294a9b3ff1aSjsg assert(buf[lineend] == '\0');
295a9b3ff1aSjsg
296a9b3ff1aSjsg /* count how many commented-out newlines we swallowed */
297*88157d21Sjsg place_addlines(&places.nextline,
298*88157d21Sjsg countnls(buf, linestart, lineend));
299a9b3ff1aSjsg
300f9343feaSjsg /* process the line (even if it's empty) */
301f9343feaSjsg directive_gotline(&places, buf+linestart, lineend-linestart);
302a9b3ff1aSjsg
303a9b3ff1aSjsg linestart = nextlinestart;
304a9b3ff1aSjsg lineend = findeol(buf, linestart, bufend);
305f9343feaSjsg places.current = places.nextline;
306a9b3ff1aSjsg }
307a9b3ff1aSjsg
308a9b3ff1aSjsg if (toplevel) {
309f9343feaSjsg directive_goteof(&places.current);
310a9b3ff1aSjsg }
311a9b3ff1aSjsg dofree(buf, bufmax);
312a9b3ff1aSjsg }
313a9b3ff1aSjsg
314a9b3ff1aSjsg ////////////////////////////////////////////////////////////
315a9b3ff1aSjsg // path search
316a9b3ff1aSjsg
317a9b3ff1aSjsg static
318a9b3ff1aSjsg char *
mkfilename(struct place * place,const char * dir,const char * file)319a9b3ff1aSjsg mkfilename(struct place *place, const char *dir, const char *file)
320a9b3ff1aSjsg {
321a9b3ff1aSjsg size_t dlen, flen, rlen;
322a9b3ff1aSjsg char *ret;
323a9b3ff1aSjsg bool needslash = false;
324a9b3ff1aSjsg
325a9b3ff1aSjsg if (dir == NULL) {
326a9b3ff1aSjsg dir = place_getparsedir(place);
327a9b3ff1aSjsg }
328a9b3ff1aSjsg
329a9b3ff1aSjsg dlen = strlen(dir);
330a9b3ff1aSjsg flen = strlen(file);
331a9b3ff1aSjsg if (dlen > 0 && dir[dlen-1] != '/') {
332a9b3ff1aSjsg needslash = true;
333a9b3ff1aSjsg }
334a9b3ff1aSjsg
335a9b3ff1aSjsg rlen = dlen + (needslash ? 1 : 0) + flen;
336a9b3ff1aSjsg ret = domalloc(rlen + 1);
3372fbb987bSderaadt snprintf(ret, rlen+1, "%s%s%s", dir, needslash ? "/" : "", file);
338a9b3ff1aSjsg return ret;
339a9b3ff1aSjsg }
340a9b3ff1aSjsg
341a9b3ff1aSjsg static
342a9b3ff1aSjsg int
file_tryopen(const char * file)343a9b3ff1aSjsg file_tryopen(const char *file)
344a9b3ff1aSjsg {
345a9b3ff1aSjsg int fd;
346a9b3ff1aSjsg
347a9b3ff1aSjsg /* XXX check for non-regular files */
348a9b3ff1aSjsg
349a9b3ff1aSjsg fd = open(file, O_RDONLY);
350696e08c4Sderaadt if (fd == -1) {
351a9b3ff1aSjsg if (errno != ENOENT && errno != ENOTDIR) {
352a9b3ff1aSjsg complain(NULL, "%s: %s", file, strerror(errno));
353a9b3ff1aSjsg }
354a9b3ff1aSjsg return -1;
355a9b3ff1aSjsg }
356a9b3ff1aSjsg
357a9b3ff1aSjsg return fd;
358a9b3ff1aSjsg }
359a9b3ff1aSjsg
360a9b3ff1aSjsg static
361a9b3ff1aSjsg void
file_search(struct place * place,struct incdirarray * path,const char * name)362a9b3ff1aSjsg file_search(struct place *place, struct incdirarray *path, const char *name)
363a9b3ff1aSjsg {
364a9b3ff1aSjsg unsigned i, num;
365a9b3ff1aSjsg struct incdir *id;
366a9b3ff1aSjsg const struct placefile *pf;
367a9b3ff1aSjsg char *file;
368a9b3ff1aSjsg int fd;
369a9b3ff1aSjsg
370a9b3ff1aSjsg assert(place != NULL);
371a9b3ff1aSjsg
372a9b3ff1aSjsg if (name[0] == '/') {
373a9b3ff1aSjsg fd = file_tryopen(name);
374a9b3ff1aSjsg if (fd >= 0) {
375a9b3ff1aSjsg pf = place_addfile(place, name, true);
376a9b3ff1aSjsg file_read(pf, fd, name, false);
377a9b3ff1aSjsg close(fd);
378a9b3ff1aSjsg return;
379a9b3ff1aSjsg }
380a9b3ff1aSjsg } else {
381a9b3ff1aSjsg num = incdirarray_num(path);
382a9b3ff1aSjsg for (i=0; i<num; i++) {
383a9b3ff1aSjsg id = incdirarray_get(path, i);
384a9b3ff1aSjsg file = mkfilename(place, id->name, name);
385a9b3ff1aSjsg fd = file_tryopen(file);
386a9b3ff1aSjsg if (fd >= 0) {
387a9b3ff1aSjsg pf = place_addfile(place, file, id->issystem);
388a9b3ff1aSjsg file_read(pf, fd, file, false);
389a9b3ff1aSjsg dostrfree(file);
390a9b3ff1aSjsg close(fd);
391a9b3ff1aSjsg return;
392a9b3ff1aSjsg }
393a9b3ff1aSjsg dostrfree(file);
394a9b3ff1aSjsg }
395a9b3ff1aSjsg }
396a9b3ff1aSjsg complain(place, "Include file %s not found", name);
397a9b3ff1aSjsg complain_fail();
398a9b3ff1aSjsg }
399a9b3ff1aSjsg
400a9b3ff1aSjsg void
file_readquote(struct place * place,const char * name)401a9b3ff1aSjsg file_readquote(struct place *place, const char *name)
402a9b3ff1aSjsg {
403a9b3ff1aSjsg file_search(place, "epath, name);
404a9b3ff1aSjsg }
405a9b3ff1aSjsg
406a9b3ff1aSjsg void
file_readbracket(struct place * place,const char * name)407a9b3ff1aSjsg file_readbracket(struct place *place, const char *name)
408a9b3ff1aSjsg {
409a9b3ff1aSjsg file_search(place, &bracketpath, name);
410a9b3ff1aSjsg }
411a9b3ff1aSjsg
412a9b3ff1aSjsg void
file_readabsolute(struct place * place,const char * name)413a9b3ff1aSjsg file_readabsolute(struct place *place, const char *name)
414a9b3ff1aSjsg {
415a9b3ff1aSjsg const struct placefile *pf;
416a9b3ff1aSjsg int fd;
417a9b3ff1aSjsg
418a9b3ff1aSjsg assert(place != NULL);
419a9b3ff1aSjsg
420f9343feaSjsg if (name == NULL) {
421a9b3ff1aSjsg fd = STDIN_FILENO;
422a9b3ff1aSjsg pf = place_addfile(place, "<standard-input>", false);
423a9b3ff1aSjsg } else {
424a9b3ff1aSjsg fd = file_tryopen(name);
425a9b3ff1aSjsg if (fd < 0) {
426a9b3ff1aSjsg complain(NULL, "%s: %s", name, strerror(errno));
427a9b3ff1aSjsg die();
428a9b3ff1aSjsg }
429a9b3ff1aSjsg pf = place_addfile(place, name, false);
430a9b3ff1aSjsg }
431a9b3ff1aSjsg
432a9b3ff1aSjsg file_read(pf, fd, name, true);
433a9b3ff1aSjsg
434a9b3ff1aSjsg if (name != NULL) {
435a9b3ff1aSjsg close(fd);
436a9b3ff1aSjsg }
437a9b3ff1aSjsg }
438