xref: /netbsd-src/external/bsd/tradcpp/dist/files.c (revision 31615c9617fab4df7f5e221552df7da87f14320d)
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(&quotepath);
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(&quotepath);
89*31615c96Sdholland 	incdirarray_cleanup(&quotepath);
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(&quotepath, 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, &quotepath, 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