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