Home | History | Annotate | Line # | Download | only in dist
      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 *
     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
     71 incdir_destroy(struct incdir *id)
     72 {
     73 	dofree(id, sizeof(*id));
     74 }
     75 
     76 void
     77 files_init(void)
     78 {
     79 	incdirarray_init(&quotepath);
     80 	incdirarray_init(&bracketpath);
     81 }
     82 
     83 DESTROYALL_ARRAY(incdir, );
     84 
     85 void
     86 files_cleanup(void)
     87 {
     88 	incdirarray_destroyall(&quotepath);
     89 	incdirarray_cleanup(&quotepath);
     90 	incdirarray_destroyall(&bracketpath);
     91 	incdirarray_cleanup(&bracketpath);
     92 }
     93 
     94 ////////////////////////////////////////////////////////////
     95 // path setup
     96 
     97 void
     98 files_addquotepath(const char *dir, bool issystem)
     99 {
    100 	struct incdir *id;
    101 
    102 	id = incdir_create(dir, issystem);
    103 	incdirarray_add(&quotepath, id, NULL);
    104 }
    105 
    106 void
    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
    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
    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
    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 *
    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
    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
    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
    405 file_readquote(struct place *place, const char *name)
    406 {
    407 	file_search(place, &quotepath, name);
    408 }
    409 
    410 void
    411 file_readbracket(struct place *place, const char *name)
    412 {
    413 	file_search(place, &bracketpath, name);
    414 }
    415 
    416 void
    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