Mercurial > ~dholland > hg > tradcpp > index.cgi
annotate files.c @ 136:59680a727e9d
Improve previous.
Just in case we ever crash and reach cleanup() while processing an
-include foo option, take the array entry for it out of the array to
make sure it doesn't get freed twice. This case shouldn't be
reachable, but it's better to be safe.
author | David A. Holland |
---|---|
date | Tue, 09 Jul 2013 13:38:43 -0400 (2013-07-09) |
parents | 1cda505ddc78 |
children | ed45f2d8d3bc |
rev | line source |
---|---|
30 | 1 /*- |
99
60184aa42604
add 2013 to copyrights where it seems warranted
David A. Holland
parents:
81
diff
changeset
|
2 * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc. |
30 | 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 | |
6 | 30 #include <stdbool.h> |
31 #include <stdio.h> | |
32 #include <stdlib.h> | |
13 | 33 #include <string.h> |
6 | 34 #include <unistd.h> |
35 #include <fcntl.h> | |
113
1e7144176a42
Print a warning if we get an unexpected error trying to open a file.
David A. Holland
parents:
112
diff
changeset
|
36 #include <errno.h> |
6 | 37 #include <err.h> |
38 | |
39 #include "array.h" | |
15 | 40 #include "mode.h" |
8 | 41 #include "place.h" |
6 | 42 #include "files.h" |
15 | 43 #include "directive.h" |
6 | 44 |
45 struct incdir { | |
46 const char *name; | |
47 bool issystem; | |
48 }; | |
49 | |
107 | 50 DECLARRAY(incdir, static UNUSED); |
47
2e25e55dba6b
Fix inline usage as per the version in dholland-make2.
David A. Holland
parents:
39
diff
changeset
|
51 DEFARRAY(incdir, static); |
6 | 52 |
53 static struct incdirarray quotepath, bracketpath; | |
54 | |
55 //////////////////////////////////////////////////////////// | |
56 // management | |
57 | |
58 static | |
59 struct incdir * | |
60 incdir_create(const char *name, bool issystem) | |
61 { | |
62 struct incdir *id; | |
63 | |
64 id = domalloc(sizeof(*id)); | |
65 id->name = name; | |
66 id->issystem = issystem; | |
67 return id; | |
68 } | |
69 | |
70 static | |
71 void | |
72 incdir_destroy(struct incdir *id) | |
73 { | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
74 dofree(id, sizeof(*id)); |
6 | 75 } |
76 | |
77 void | |
78 files_init(void) | |
79 { | |
80 incdirarray_init("epath); | |
81 incdirarray_init(&bracketpath); | |
82 } | |
83 | |
9 | 84 DESTROYALL_ARRAY(incdir, ); |
6 | 85 |
86 void | |
87 files_cleanup(void) | |
88 { | |
89 incdirarray_destroyall("epath); | |
90 incdirarray_cleanup("epath); | |
91 incdirarray_destroyall(&bracketpath); | |
92 incdirarray_cleanup(&bracketpath); | |
93 } | |
94 | |
95 //////////////////////////////////////////////////////////// | |
96 // path setup | |
97 | |
98 void | |
99 files_addquotepath(const char *dir, bool issystem) | |
100 { | |
101 struct incdir *id; | |
102 | |
103 id = incdir_create(dir, issystem); | |
104 incdirarray_add("epath, id, NULL); | |
105 } | |
106 | |
107 void | |
108 files_addbracketpath(const char *dir, bool issystem) | |
109 { | |
110 struct incdir *id; | |
111 | |
112 id = incdir_create(dir, issystem); | |
113 incdirarray_add(&bracketpath, id, NULL); | |
114 } | |
115 | |
116 //////////////////////////////////////////////////////////// | |
117 // parsing | |
118 | |
75 | 119 /* |
120 * Find the end of the logical line. End of line characters that are | |
121 * commented out do not count. | |
122 */ | |
15 | 123 static |
124 size_t | |
75 | 125 findeol(const char *buf, size_t start, size_t limit) |
15 | 126 { |
127 size_t i; | |
75 | 128 int incomment = 0; |
81
27c9aafcaca1
Don't recognize comments within double-quote strings.
David A. Holland
parents:
75
diff
changeset
|
129 bool inquote = false; |
128
1cda505ddc78
Don't expand macros within character constants.
David A. Holland
parents:
113
diff
changeset
|
130 char quote = '\0'; |
75 | 131 |
132 for (i=start; i<limit; i++) { | |
133 if (incomment) { | |
134 if (i+1 < limit && buf[i] == '*' && buf[i+1] == '/') { | |
135 i++; | |
136 incomment = 0; | |
137 } | |
81
27c9aafcaca1
Don't recognize comments within double-quote strings.
David A. Holland
parents:
75
diff
changeset
|
138 } else if (!inquote && i+1 < limit && |
27c9aafcaca1
Don't recognize comments within double-quote strings.
David A. Holland
parents:
75
diff
changeset
|
139 buf[i] == '/' && buf[i+1] == '*') { |
27c9aafcaca1
Don't recognize comments within double-quote strings.
David A. Holland
parents:
75
diff
changeset
|
140 i++; |
27c9aafcaca1
Don't recognize comments within double-quote strings.
David A. Holland
parents:
75
diff
changeset
|
141 incomment = 1; |
27c9aafcaca1
Don't recognize comments within double-quote strings.
David A. Holland
parents:
75
diff
changeset
|
142 } else if (i+1 < limit && |
128
1cda505ddc78
Don't expand macros within character constants.
David A. Holland
parents:
113
diff
changeset
|
143 buf[i] == '\\' && buf[i+1] != '\n') { |
81
27c9aafcaca1
Don't recognize comments within double-quote strings.
David A. Holland
parents:
75
diff
changeset
|
144 i++; |
128
1cda505ddc78
Don't expand macros within character constants.
David A. Holland
parents:
113
diff
changeset
|
145 } else if (!inquote && (buf[i] == '"' || buf[i] == '\'')) { |
1cda505ddc78
Don't expand macros within character constants.
David A. Holland
parents:
113
diff
changeset
|
146 inquote = true; |
1cda505ddc78
Don't expand macros within character constants.
David A. Holland
parents:
113
diff
changeset
|
147 quote = buf[i]; |
1cda505ddc78
Don't expand macros within character constants.
David A. Holland
parents:
113
diff
changeset
|
148 } else if (inquote && buf[i] == quote) { |
1cda505ddc78
Don't expand macros within character constants.
David A. Holland
parents:
113
diff
changeset
|
149 inquote = false; |
81
27c9aafcaca1
Don't recognize comments within double-quote strings.
David A. Holland
parents:
75
diff
changeset
|
150 } else if (buf[i] == '\n') { |
27c9aafcaca1
Don't recognize comments within double-quote strings.
David A. Holland
parents:
75
diff
changeset
|
151 return i; |
75 | 152 } |
153 } | |
154 return limit; | |
155 } | |
156 | |
157 static | |
158 unsigned | |
159 countnls(const char *buf, size_t start, size_t limit) | |
160 { | |
161 size_t i; | |
162 unsigned count = 0; | |
15 | 163 |
164 for (i=start; i<limit; i++) { | |
165 if (buf[i] == '\n') { | |
75 | 166 count++; |
15 | 167 } |
168 } | |
75 | 169 return count; |
15 | 170 } |
171 | |
172 static | |
6 | 173 void |
28 | 174 file_read(const struct placefile *pf, int fd, const char *name, bool toplevel) |
15 | 175 { |
176 struct place linestartplace, nextlinestartplace, ptmp; | |
177 size_t bufend, bufmax, linestart, lineend, nextlinestart, tmp; | |
178 ssize_t result; | |
179 bool ateof = false; | |
180 char *buf; | |
181 | |
182 place_setfilestart(&linestartplace, pf); | |
183 nextlinestartplace = linestartplace; | |
184 | |
185 bufmax = 128; | |
186 bufend = 0; | |
187 linestart = 0; | |
188 lineend = 0; | |
189 buf = domalloc(bufmax); | |
190 | |
191 while (1) { | |
192 if (lineend >= bufend) { | |
193 /* do not have a whole line in the buffer; read more */ | |
75 | 194 assert(bufend >= linestart); |
15 | 195 if (linestart > 0 && bufend > linestart) { |
196 /* slide to beginning of buffer */ | |
197 memmove(buf, buf+linestart, bufend-linestart); | |
198 bufend -= linestart; | |
199 lineend -= linestart; | |
200 linestart = 0; | |
201 } | |
202 if (bufend >= bufmax) { | |
203 /* need bigger buffer */ | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
204 buf = dorealloc(buf, bufmax, bufmax*2); |
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
205 bufmax = bufmax*2; |
15 | 206 } |
207 | |
208 if (ateof) { | |
209 /* don't read again, in case it's a socket */ | |
210 result = 0; | |
211 } else { | |
212 result = read(fd, buf+bufend, bufmax - bufend); | |
213 } | |
214 | |
215 if (result == -1) { | |
216 /* read error */ | |
217 warn("%s", name); | |
218 complain_fail(); | |
219 } else if (result == 0 && bufend == linestart) { | |
220 /* eof */ | |
221 ateof = true; | |
222 break; | |
223 } else if (result == 0) { | |
224 /* eof in middle of line */ | |
225 ateof = true; | |
226 ptmp = linestartplace; | |
227 ptmp.column += bufend - linestart; | |
228 complain(&ptmp, "No newline at end of file"); | |
229 if (mode.werror) { | |
230 complain_fail(); | |
231 } | |
232 assert(bufend < bufmax); | |
233 lineend = bufend++; | |
234 buf[lineend] = '\n'; | |
235 } else { | |
236 bufend += (size_t)result; | |
81
27c9aafcaca1
Don't recognize comments within double-quote strings.
David A. Holland
parents:
75
diff
changeset
|
237 lineend = findeol(buf, linestart, bufend); |
15 | 238 } |
239 /* loop in case we still don't have a whole line */ | |
240 continue; | |
241 } | |
242 | |
243 /* have a line */ | |
244 assert(buf[lineend] == '\n'); | |
245 buf[lineend] = '\0'; | |
246 nextlinestart = lineend+1; | |
247 nextlinestartplace.line++; | |
248 | |
249 /* check for CR/NL */ | |
250 if (lineend > 0 && buf[lineend-1] == '\r') { | |
251 buf[lineend-1] = '\0'; | |
252 lineend--; | |
253 } | |
254 | |
255 /* check for continuation line */ | |
256 if (lineend > 0 && buf[lineend-1]=='\\') { | |
257 lineend--; | |
258 tmp = nextlinestart - lineend; | |
259 if (bufend > nextlinestart) { | |
260 memmove(buf+lineend, buf+nextlinestart, | |
261 bufend - nextlinestart); | |
262 } | |
263 bufend -= tmp; | |
264 nextlinestart -= tmp; | |
81
27c9aafcaca1
Don't recognize comments within double-quote strings.
David A. Holland
parents:
75
diff
changeset
|
265 lineend = findeol(buf, linestart, bufend); |
15 | 266 /* might not have a whole line, so loop */ |
267 continue; | |
268 } | |
269 | |
270 /* line now goes from linestart to lineend */ | |
271 assert(buf[lineend] == '\0'); | |
75 | 272 |
273 /* count how many commented-out newlines we swallowed */ | |
274 nextlinestartplace.line += countnls(buf, linestart, lineend); | |
275 | |
276 /* if the line isn't empty, process it */ | |
15 | 277 if (lineend > linestart) { |
278 directive_gotline(&linestartplace, | |
279 buf+linestart, lineend-linestart); | |
280 } | |
281 | |
282 linestart = nextlinestart; | |
75 | 283 lineend = findeol(buf, linestart, bufend); |
15 | 284 linestartplace = nextlinestartplace; |
285 } | |
286 | |
28 | 287 if (toplevel) { |
288 directive_goteof(&linestartplace); | |
289 } | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
290 dofree(buf, bufmax); |
15 | 291 } |
6 | 292 |
293 //////////////////////////////////////////////////////////// | |
294 // path search | |
295 | |
296 static | |
13 | 297 char * |
112 | 298 mkfilename(struct place *place, const char *dir, const char *file) |
13 | 299 { |
300 size_t dlen, flen, rlen; | |
301 char *ret; | |
302 bool needslash = false; | |
303 | |
112 | 304 if (dir == NULL) { |
305 dir = place_getparsedir(place); | |
306 } | |
307 | |
13 | 308 dlen = strlen(dir); |
309 flen = strlen(file); | |
310 if (dlen > 0 && dir[dlen-1] != '/') { | |
311 needslash = true; | |
312 } | |
313 | |
314 rlen = dlen + (needslash ? 1 : 0) + flen; | |
315 ret = domalloc(rlen + 1); | |
316 strcpy(ret, dir); | |
317 if (needslash) { | |
318 strcat(ret, "/"); | |
319 } | |
320 strcat(ret, file); | |
321 return ret; | |
322 } | |
323 | |
324 static | |
6 | 325 int |
326 file_tryopen(const char *file) | |
327 { | |
328 int fd; | |
329 | |
15 | 330 /* XXX check for non-regular files */ |
331 | |
6 | 332 fd = open(file, O_RDONLY); |
333 if (fd < 0) { | |
113
1e7144176a42
Print a warning if we get an unexpected error trying to open a file.
David A. Holland
parents:
112
diff
changeset
|
334 if (errno != ENOENT && errno != ENOTDIR) { |
1e7144176a42
Print a warning if we get an unexpected error trying to open a file.
David A. Holland
parents:
112
diff
changeset
|
335 warn("%s", file); |
1e7144176a42
Print a warning if we get an unexpected error trying to open a file.
David A. Holland
parents:
112
diff
changeset
|
336 } |
6 | 337 return -1; |
338 } | |
15 | 339 |
6 | 340 return fd; |
341 } | |
342 | |
343 static | |
344 void | |
345 file_search(struct place *place, struct incdirarray *path, const char *name) | |
346 { | |
347 unsigned i, num; | |
348 struct incdir *id; | |
13 | 349 const struct placefile *pf; |
6 | 350 char *file; |
351 int fd; | |
352 | |
353 assert(place != NULL); | |
354 | |
104 | 355 if (name[0] == '/') { |
356 fd = file_tryopen(name); | |
6 | 357 if (fd >= 0) { |
104 | 358 pf = place_addfile(place, name, true); |
359 file_read(pf, fd, name, false); | |
6 | 360 close(fd); |
361 return; | |
362 } | |
104 | 363 } else { |
364 num = incdirarray_num(path); | |
365 for (i=0; i<num; i++) { | |
366 id = incdirarray_get(path, i); | |
112 | 367 file = mkfilename(place, id->name, name); |
104 | 368 fd = file_tryopen(file); |
369 if (fd >= 0) { | |
370 pf = place_addfile(place, file, id->issystem); | |
371 file_read(pf, fd, file, false); | |
372 dostrfree(file); | |
373 close(fd); | |
374 return; | |
375 } | |
376 dostrfree(file); | |
377 } | |
6 | 378 } |
379 complain(place, "Include file %s not found", name); | |
380 complain_fail(); | |
381 } | |
382 | |
383 void | |
384 file_readquote(struct place *place, const char *name) | |
385 { | |
386 file_search(place, "epath, name); | |
387 } | |
388 | |
389 void | |
390 file_readbracket(struct place *place, const char *name) | |
391 { | |
392 file_search(place, &bracketpath, name); | |
393 } | |
394 | |
395 void | |
396 file_readabsolute(struct place *place, const char *name) | |
397 { | |
13 | 398 const struct placefile *pf; |
6 | 399 int fd; |
400 | |
401 assert(place != NULL); | |
402 | |
24 | 403 if (name == NULL) { |
404 fd = STDIN_FILENO; | |
405 pf = place_addfile(place, "<standard-input>", false); | |
406 } else { | |
407 fd = file_tryopen(name); | |
408 if (fd < 0) { | |
409 warn("%s", name); | |
410 die(); | |
411 } | |
412 pf = place_addfile(place, name, false); | |
6 | 413 } |
24 | 414 |
28 | 415 file_read(pf, fd, name, true); |
24 | 416 |
417 if (name != NULL) { | |
418 close(fd); | |
419 } | |
6 | 420 } |