1*31615c96Sdholland /*-
2*31615c96Sdholland * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc.
3*31615c96Sdholland * All rights reserved.
4*31615c96Sdholland *
5*31615c96Sdholland * This code is derived from software contributed to The NetBSD Foundation
6*31615c96Sdholland * by David A. Holland.
7*31615c96Sdholland *
8*31615c96Sdholland * Redistribution and use in source and binary forms, with or without
9*31615c96Sdholland * modification, are permitted provided that the following conditions
10*31615c96Sdholland * are met:
11*31615c96Sdholland * 1. Redistributions of source code must retain the above copyright
12*31615c96Sdholland * notice, this list of conditions and the following disclaimer.
13*31615c96Sdholland * 2. Redistributions in binary form must reproduce the above copyright
14*31615c96Sdholland * notice, this list of conditions and the following disclaimer in the
15*31615c96Sdholland * documentation and/or other materials provided with the distribution.
16*31615c96Sdholland *
17*31615c96Sdholland * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18*31615c96Sdholland * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19*31615c96Sdholland * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20*31615c96Sdholland * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21*31615c96Sdholland * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22*31615c96Sdholland * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23*31615c96Sdholland * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24*31615c96Sdholland * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25*31615c96Sdholland * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26*31615c96Sdholland * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27*31615c96Sdholland * POSSIBILITY OF SUCH DAMAGE.
28*31615c96Sdholland */
29*31615c96Sdholland
30*31615c96Sdholland #include <stdio.h>
31*31615c96Sdholland #include <stdlib.h>
32*31615c96Sdholland #include <string.h>
33*31615c96Sdholland #include <unistd.h>
34*31615c96Sdholland #include <fcntl.h>
35*31615c96Sdholland #include <errno.h>
36*31615c96Sdholland
37*31615c96Sdholland #include "bool.h"
38*31615c96Sdholland #include "array.h"
39*31615c96Sdholland #include "mode.h"
40*31615c96Sdholland #include "place.h"
41*31615c96Sdholland #include "files.h"
42*31615c96Sdholland #include "directive.h"
43*31615c96Sdholland
44*31615c96Sdholland struct incdir {
45*31615c96Sdholland const char *name;
46*31615c96Sdholland bool issystem;
47*31615c96Sdholland };
48*31615c96Sdholland
49*31615c96Sdholland DECLARRAY(incdir, static UNUSED);
50*31615c96Sdholland DEFARRAY(incdir, static);
51*31615c96Sdholland
52*31615c96Sdholland static struct incdirarray quotepath, bracketpath;
53*31615c96Sdholland
54*31615c96Sdholland ////////////////////////////////////////////////////////////
55*31615c96Sdholland // management
56*31615c96Sdholland
57*31615c96Sdholland static
58*31615c96Sdholland struct incdir *
incdir_create(const char * name,bool issystem)59*31615c96Sdholland incdir_create(const char *name, bool issystem)
60*31615c96Sdholland {
61*31615c96Sdholland struct incdir *id;
62*31615c96Sdholland
63*31615c96Sdholland id = domalloc(sizeof(*id));
64*31615c96Sdholland id->name = name;
65*31615c96Sdholland id->issystem = issystem;
66*31615c96Sdholland return id;
67*31615c96Sdholland }
68*31615c96Sdholland
69*31615c96Sdholland static
70*31615c96Sdholland void
incdir_destroy(struct incdir * id)71*31615c96Sdholland incdir_destroy(struct incdir *id)
72*31615c96Sdholland {
73*31615c96Sdholland dofree(id, sizeof(*id));
74*31615c96Sdholland }
75*31615c96Sdholland
76*31615c96Sdholland void
files_init(void)77*31615c96Sdholland files_init(void)
78*31615c96Sdholland {
79*31615c96Sdholland incdirarray_init("epath);
80*31615c96Sdholland incdirarray_init(&bracketpath);
81*31615c96Sdholland }
82*31615c96Sdholland
83*31615c96Sdholland DESTROYALL_ARRAY(incdir, );
84*31615c96Sdholland
85*31615c96Sdholland void
files_cleanup(void)86*31615c96Sdholland files_cleanup(void)
87*31615c96Sdholland {
88*31615c96Sdholland incdirarray_destroyall("epath);
89*31615c96Sdholland incdirarray_cleanup("epath);
90*31615c96Sdholland incdirarray_destroyall(&bracketpath);
91*31615c96Sdholland incdirarray_cleanup(&bracketpath);
92*31615c96Sdholland }
93*31615c96Sdholland
94*31615c96Sdholland ////////////////////////////////////////////////////////////
95*31615c96Sdholland // path setup
96*31615c96Sdholland
97*31615c96Sdholland void
files_addquotepath(const char * dir,bool issystem)98*31615c96Sdholland files_addquotepath(const char *dir, bool issystem)
99*31615c96Sdholland {
100*31615c96Sdholland struct incdir *id;
101*31615c96Sdholland
102*31615c96Sdholland id = incdir_create(dir, issystem);
103*31615c96Sdholland incdirarray_add("epath, id, NULL);
104*31615c96Sdholland }
105*31615c96Sdholland
106*31615c96Sdholland void
files_addbracketpath(const char * dir,bool issystem)107*31615c96Sdholland files_addbracketpath(const char *dir, bool issystem)
108*31615c96Sdholland {
109*31615c96Sdholland struct incdir *id;
110*31615c96Sdholland
111*31615c96Sdholland id = incdir_create(dir, issystem);
112*31615c96Sdholland incdirarray_add(&bracketpath, id, NULL);
113*31615c96Sdholland }
114*31615c96Sdholland
115*31615c96Sdholland ////////////////////////////////////////////////////////////
116*31615c96Sdholland // parsing
117*31615c96Sdholland
118*31615c96Sdholland /*
119*31615c96Sdholland * Find the end of the logical line. End of line characters that are
120*31615c96Sdholland * commented out do not count.
121*31615c96Sdholland */
122*31615c96Sdholland static
123*31615c96Sdholland size_t
findeol(const char * buf,size_t start,size_t limit)124*31615c96Sdholland findeol(const char *buf, size_t start, size_t limit)
125*31615c96Sdholland {
126*31615c96Sdholland size_t i;
127*31615c96Sdholland int incomment = 0;
128*31615c96Sdholland bool inquote = false;
129*31615c96Sdholland char quote = '\0';
130*31615c96Sdholland
131*31615c96Sdholland for (i=start; i<limit; i++) {
132*31615c96Sdholland if (incomment) {
133*31615c96Sdholland if (i+1 < limit && buf[i] == '*' && buf[i+1] == '/') {
134*31615c96Sdholland i++;
135*31615c96Sdholland incomment = 0;
136*31615c96Sdholland }
137*31615c96Sdholland } else if (!inquote && i+1 < limit &&
138*31615c96Sdholland buf[i] == '/' && buf[i+1] == '*') {
139*31615c96Sdholland i++;
140*31615c96Sdholland incomment = 1;
141*31615c96Sdholland } else if (i+1 < limit &&
142*31615c96Sdholland buf[i] == '\\' && buf[i+1] != '\n') {
143*31615c96Sdholland i++;
144*31615c96Sdholland } else if (!inquote && (buf[i] == '"' || buf[i] == '\'')) {
145*31615c96Sdholland inquote = true;
146*31615c96Sdholland quote = buf[i];
147*31615c96Sdholland } else if (inquote && buf[i] == quote) {
148*31615c96Sdholland inquote = false;
149*31615c96Sdholland } else if (buf[i] == '\n') {
150*31615c96Sdholland return i;
151*31615c96Sdholland }
152*31615c96Sdholland }
153*31615c96Sdholland return limit;
154*31615c96Sdholland }
155*31615c96Sdholland
156*31615c96Sdholland static
157*31615c96Sdholland unsigned
countnls(const char * buf,size_t start,size_t limit)158*31615c96Sdholland countnls(const char *buf, size_t start, size_t limit)
159*31615c96Sdholland {
160*31615c96Sdholland size_t i;
161*31615c96Sdholland unsigned count = 0;
162*31615c96Sdholland
163*31615c96Sdholland for (i=start; i<limit; i++) {
164*31615c96Sdholland if (buf[i] == '\n') {
165*31615c96Sdholland count++;
166*31615c96Sdholland if (count == 0) {
167*31615c96Sdholland /* just return the max and error downstream */
168*31615c96Sdholland return count - 1;
169*31615c96Sdholland }
170*31615c96Sdholland }
171*31615c96Sdholland }
172*31615c96Sdholland return count;
173*31615c96Sdholland }
174*31615c96Sdholland
175*31615c96Sdholland static
176*31615c96Sdholland void
file_read(const struct placefile * pf,int fd,const char * name,bool toplevel)177*31615c96Sdholland file_read(const struct placefile *pf, int fd, const char *name, bool toplevel)
178*31615c96Sdholland {
179*31615c96Sdholland struct lineplace places;
180*31615c96Sdholland struct place ptmp;
181*31615c96Sdholland size_t bufend, bufmax, linestart, lineend, nextlinestart, tmp;
182*31615c96Sdholland ssize_t result;
183*31615c96Sdholland bool ateof = false;
184*31615c96Sdholland char *buf;
185*31615c96Sdholland
186*31615c96Sdholland place_setfilestart(&places.current, pf);
187*31615c96Sdholland places.nextline = places.current;
188*31615c96Sdholland
189*31615c96Sdholland if (name) {
190*31615c96Sdholland debuglog(&places.current, "Reading file %s", name);
191*31615c96Sdholland } else {
192*31615c96Sdholland debuglog(&places.current, "Reading standard input");
193*31615c96Sdholland }
194*31615c96Sdholland
195*31615c96Sdholland bufmax = 128;
196*31615c96Sdholland bufend = 0;
197*31615c96Sdholland linestart = 0;
198*31615c96Sdholland lineend = 0;
199*31615c96Sdholland buf = domalloc(bufmax);
200*31615c96Sdholland
201*31615c96Sdholland while (1) {
202*31615c96Sdholland if (lineend >= bufend) {
203*31615c96Sdholland /* do not have a whole line in the buffer; read more */
204*31615c96Sdholland assert(bufend >= linestart);
205*31615c96Sdholland if (linestart > 0 && bufend > linestart) {
206*31615c96Sdholland /* slide to beginning of buffer */
207*31615c96Sdholland memmove(buf, buf+linestart, bufend-linestart);
208*31615c96Sdholland bufend -= linestart;
209*31615c96Sdholland lineend -= linestart;
210*31615c96Sdholland linestart = 0;
211*31615c96Sdholland }
212*31615c96Sdholland if (bufend >= bufmax) {
213*31615c96Sdholland /* need bigger buffer */
214*31615c96Sdholland buf = dorealloc(buf, bufmax, bufmax*2);
215*31615c96Sdholland bufmax = bufmax*2;
216*31615c96Sdholland /* just in case someone's screwing around */
217*31615c96Sdholland if (bufmax > 0xffffffff) {
218*31615c96Sdholland complain(&places.current,
219*31615c96Sdholland "Input line too long");
220*31615c96Sdholland die();
221*31615c96Sdholland }
222*31615c96Sdholland }
223*31615c96Sdholland
224*31615c96Sdholland if (ateof) {
225*31615c96Sdholland /* don't read again, in case it's a socket */
226*31615c96Sdholland result = 0;
227*31615c96Sdholland } else {
228*31615c96Sdholland result = read(fd, buf+bufend, bufmax - bufend);
229*31615c96Sdholland }
230*31615c96Sdholland
231*31615c96Sdholland if (result == -1) {
232*31615c96Sdholland /* read error */
233*31615c96Sdholland complain(NULL, "%s: %s",
234*31615c96Sdholland name, strerror(errno));
235*31615c96Sdholland complain_fail();
236*31615c96Sdholland } else if (result == 0 && bufend == linestart) {
237*31615c96Sdholland /* eof */
238*31615c96Sdholland ateof = true;
239*31615c96Sdholland break;
240*31615c96Sdholland } else if (result == 0) {
241*31615c96Sdholland /* eof in middle of line */
242*31615c96Sdholland ateof = true;
243*31615c96Sdholland ptmp = places.current;
244*31615c96Sdholland place_addcolumns(&ptmp, bufend - linestart);
245*31615c96Sdholland if (buf[bufend - 1] == '\n') {
246*31615c96Sdholland complain(&ptmp, "Unclosed comment");
247*31615c96Sdholland complain_fail();
248*31615c96Sdholland } else {
249*31615c96Sdholland complain(&ptmp,
250*31615c96Sdholland "No newline at end of file");
251*31615c96Sdholland }
252*31615c96Sdholland if (mode.werror) {
253*31615c96Sdholland complain_fail();
254*31615c96Sdholland }
255*31615c96Sdholland assert(bufend < bufmax);
256*31615c96Sdholland lineend = bufend++;
257*31615c96Sdholland buf[lineend] = '\n';
258*31615c96Sdholland } else {
259*31615c96Sdholland bufend += (size_t)result;
260*31615c96Sdholland lineend = findeol(buf, linestart, bufend);
261*31615c96Sdholland }
262*31615c96Sdholland /* loop in case we still don't have a whole line */
263*31615c96Sdholland continue;
264*31615c96Sdholland }
265*31615c96Sdholland
266*31615c96Sdholland /* have a line */
267*31615c96Sdholland assert(buf[lineend] == '\n');
268*31615c96Sdholland buf[lineend] = '\0';
269*31615c96Sdholland nextlinestart = lineend+1;
270*31615c96Sdholland place_addlines(&places.nextline, 1);
271*31615c96Sdholland
272*31615c96Sdholland /* check for CR/NL */
273*31615c96Sdholland if (lineend > 0 && buf[lineend-1] == '\r') {
274*31615c96Sdholland buf[lineend-1] = '\0';
275*31615c96Sdholland lineend--;
276*31615c96Sdholland }
277*31615c96Sdholland
278*31615c96Sdholland /* check for continuation line */
279*31615c96Sdholland if (lineend > 0 && buf[lineend-1]=='\\') {
280*31615c96Sdholland lineend--;
281*31615c96Sdholland tmp = nextlinestart - lineend;
282*31615c96Sdholland if (bufend > nextlinestart) {
283*31615c96Sdholland memmove(buf+lineend, buf+nextlinestart,
284*31615c96Sdholland bufend - nextlinestart);
285*31615c96Sdholland }
286*31615c96Sdholland bufend -= tmp;
287*31615c96Sdholland nextlinestart -= tmp;
288*31615c96Sdholland lineend = findeol(buf, linestart, bufend);
289*31615c96Sdholland /* might not have a whole line, so loop */
290*31615c96Sdholland continue;
291*31615c96Sdholland }
292*31615c96Sdholland
293*31615c96Sdholland /* line now goes from linestart to lineend */
294*31615c96Sdholland assert(buf[lineend] == '\0');
295*31615c96Sdholland
296*31615c96Sdholland /* count how many commented-out newlines we swallowed */
297*31615c96Sdholland place_addlines(&places.nextline,
298*31615c96Sdholland countnls(buf, linestart, lineend));
299*31615c96Sdholland
300*31615c96Sdholland /* process the line (even if it's empty) */
301*31615c96Sdholland directive_gotline(&places, buf+linestart, lineend-linestart);
302*31615c96Sdholland
303*31615c96Sdholland linestart = nextlinestart;
304*31615c96Sdholland lineend = findeol(buf, linestart, bufend);
305*31615c96Sdholland places.current = places.nextline;
306*31615c96Sdholland }
307*31615c96Sdholland
308*31615c96Sdholland if (toplevel) {
309*31615c96Sdholland directive_goteof(&places.current);
310*31615c96Sdholland }
311*31615c96Sdholland dofree(buf, bufmax);
312*31615c96Sdholland }
313*31615c96Sdholland
314*31615c96Sdholland ////////////////////////////////////////////////////////////
315*31615c96Sdholland // path search
316*31615c96Sdholland
317*31615c96Sdholland static
318*31615c96Sdholland char *
mkfilename(struct place * place,const char * dir,const char * file)319*31615c96Sdholland mkfilename(struct place *place, const char *dir, const char *file)
320*31615c96Sdholland {
321*31615c96Sdholland size_t dlen, flen, rlen;
322*31615c96Sdholland char *ret;
323*31615c96Sdholland bool needslash = false;
324*31615c96Sdholland
325*31615c96Sdholland if (dir == NULL) {
326*31615c96Sdholland dir = place_getparsedir(place);
327*31615c96Sdholland }
328*31615c96Sdholland
329*31615c96Sdholland dlen = strlen(dir);
330*31615c96Sdholland flen = strlen(file);
331*31615c96Sdholland if (dlen > 0 && dir[dlen-1] != '/') {
332*31615c96Sdholland needslash = true;
333*31615c96Sdholland }
334*31615c96Sdholland
335*31615c96Sdholland rlen = dlen + (needslash ? 1 : 0) + flen;
336*31615c96Sdholland ret = domalloc(rlen + 1);
337*31615c96Sdholland strcpy(ret, dir);
338*31615c96Sdholland if (needslash) {
339*31615c96Sdholland strcat(ret, "/");
340*31615c96Sdholland }
341*31615c96Sdholland strcat(ret, file);
342*31615c96Sdholland return ret;
343*31615c96Sdholland }
344*31615c96Sdholland
345*31615c96Sdholland static
346*31615c96Sdholland int
file_tryopen(const char * file)347*31615c96Sdholland file_tryopen(const char *file)
348*31615c96Sdholland {
349*31615c96Sdholland int fd;
350*31615c96Sdholland
351*31615c96Sdholland /* XXX check for non-regular files */
352*31615c96Sdholland
353*31615c96Sdholland fd = open(file, O_RDONLY);
354*31615c96Sdholland if (fd < 0) {
355*31615c96Sdholland if (errno != ENOENT && errno != ENOTDIR) {
356*31615c96Sdholland complain(NULL, "%s: %s", file, strerror(errno));
357*31615c96Sdholland }
358*31615c96Sdholland return -1;
359*31615c96Sdholland }
360*31615c96Sdholland
361*31615c96Sdholland return fd;
362*31615c96Sdholland }
363*31615c96Sdholland
364*31615c96Sdholland static
365*31615c96Sdholland void
file_search(struct place * place,struct incdirarray * path,const char * name)366*31615c96Sdholland file_search(struct place *place, struct incdirarray *path, const char *name)
367*31615c96Sdholland {
368*31615c96Sdholland unsigned i, num;
369*31615c96Sdholland struct incdir *id;
370*31615c96Sdholland const struct placefile *pf;
371*31615c96Sdholland char *file;
372*31615c96Sdholland int fd;
373*31615c96Sdholland
374*31615c96Sdholland assert(place != NULL);
375*31615c96Sdholland
376*31615c96Sdholland if (name[0] == '/') {
377*31615c96Sdholland fd = file_tryopen(name);
378*31615c96Sdholland if (fd >= 0) {
379*31615c96Sdholland pf = place_addfile(place, name, true);
380*31615c96Sdholland file_read(pf, fd, name, false);
381*31615c96Sdholland close(fd);
382*31615c96Sdholland return;
383*31615c96Sdholland }
384*31615c96Sdholland } else {
385*31615c96Sdholland num = incdirarray_num(path);
386*31615c96Sdholland for (i=0; i<num; i++) {
387*31615c96Sdholland id = incdirarray_get(path, i);
388*31615c96Sdholland file = mkfilename(place, id->name, name);
389*31615c96Sdholland fd = file_tryopen(file);
390*31615c96Sdholland if (fd >= 0) {
391*31615c96Sdholland pf = place_addfile(place, file, id->issystem);
392*31615c96Sdholland file_read(pf, fd, file, false);
393*31615c96Sdholland dostrfree(file);
394*31615c96Sdholland close(fd);
395*31615c96Sdholland return;
396*31615c96Sdholland }
397*31615c96Sdholland dostrfree(file);
398*31615c96Sdholland }
399*31615c96Sdholland }
400*31615c96Sdholland complain(place, "Include file %s not found", name);
401*31615c96Sdholland complain_fail();
402*31615c96Sdholland }
403*31615c96Sdholland
404*31615c96Sdholland void
file_readquote(struct place * place,const char * name)405*31615c96Sdholland file_readquote(struct place *place, const char *name)
406*31615c96Sdholland {
407*31615c96Sdholland file_search(place, "epath, name);
408*31615c96Sdholland }
409*31615c96Sdholland
410*31615c96Sdholland void
file_readbracket(struct place * place,const char * name)411*31615c96Sdholland file_readbracket(struct place *place, const char *name)
412*31615c96Sdholland {
413*31615c96Sdholland file_search(place, &bracketpath, name);
414*31615c96Sdholland }
415*31615c96Sdholland
416*31615c96Sdholland void
file_readabsolute(struct place * place,const char * name)417*31615c96Sdholland file_readabsolute(struct place *place, const char *name)
418*31615c96Sdholland {
419*31615c96Sdholland const struct placefile *pf;
420*31615c96Sdholland int fd;
421*31615c96Sdholland
422*31615c96Sdholland assert(place != NULL);
423*31615c96Sdholland
424*31615c96Sdholland if (name == NULL) {
425*31615c96Sdholland fd = STDIN_FILENO;
426*31615c96Sdholland pf = place_addfile(place, "<standard-input>", false);
427*31615c96Sdholland } else {
428*31615c96Sdholland fd = file_tryopen(name);
429*31615c96Sdholland if (fd < 0) {
430*31615c96Sdholland complain(NULL, "%s: %s", name, strerror(errno));
431*31615c96Sdholland die();
432*31615c96Sdholland }
433*31615c96Sdholland pf = place_addfile(place, name, false);
434*31615c96Sdholland }
435*31615c96Sdholland
436*31615c96Sdholland file_read(pf, fd, name, true);
437*31615c96Sdholland
438*31615c96Sdholland if (name != NULL) {
439*31615c96Sdholland close(fd);
440*31615c96Sdholland }
441*31615c96Sdholland }
442