files.c revision 1.5
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 44struct incdir { 45 const char *name; 46 bool issystem; 47}; 48 49DECLARRAY(incdir, static UNUSED); 50DEFARRAY(incdir, static); 51 52static struct incdirarray quotepath, bracketpath; 53 54//////////////////////////////////////////////////////////// 55// management 56 57static 58struct incdir * 59incdir_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 69static 70void 71incdir_destroy(struct incdir *id) 72{ 73 dofree(id, sizeof(*id)); 74} 75 76void 77files_init(void) 78{ 79 incdirarray_init("epath); 80 incdirarray_init(&bracketpath); 81} 82 83DESTROYALL_ARRAY(incdir, ); 84 85void 86files_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 97void 98files_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 106void 107files_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 */ 122static 123size_t 124findeol(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 156static 157unsigned 158countnls(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 } 167 } 168 return count; 169} 170 171static 172void 173file_read(const struct placefile *pf, int fd, const char *name, bool toplevel) 174{ 175 struct lineplace places; 176 struct place 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(&places.current, pf); 183 places.nextline = places.current; 184 185 if (name) { 186 debuglog(&places.current, "Reading file %s", name); 187 } else { 188 debuglog(&places.current, "Reading standard input"); 189 } 190 191 bufmax = 128; 192 bufend = 0; 193 linestart = 0; 194 lineend = 0; 195 buf = domalloc(bufmax); 196 197 while (1) { 198 if (lineend >= bufend) { 199 /* do not have a whole line in the buffer; read more */ 200 assert(bufend >= linestart); 201 if (linestart > 0 && bufend > linestart) { 202 /* slide to beginning of buffer */ 203 memmove(buf, buf+linestart, bufend-linestart); 204 bufend -= linestart; 205 lineend -= linestart; 206 linestart = 0; 207 } 208 if (bufend >= bufmax) { 209 /* need bigger buffer */ 210 buf = dorealloc(buf, bufmax, bufmax*2); 211 bufmax = bufmax*2; 212 } 213 214 if (ateof) { 215 /* don't read again, in case it's a socket */ 216 result = 0; 217 } else { 218 result = read(fd, buf+bufend, bufmax - bufend); 219 } 220 221 if (result == -1) { 222 /* read error */ 223 complain(NULL, "%s: %s", 224 name, strerror(errno)); 225 complain_fail(); 226 } else if (result == 0 && bufend == linestart) { 227 /* eof */ 228 ateof = true; 229 break; 230 } else if (result == 0) { 231 /* eof in middle of line */ 232 ateof = true; 233 ptmp = places.current; 234 ptmp.column += bufend - linestart; 235 if (buf[bufend - 1] == '\n') { 236 complain(&ptmp, "Unclosed comment"); 237 complain_fail(); 238 } else { 239 complain(&ptmp, 240 "No newline at end of file"); 241 } 242 if (mode.werror) { 243 complain_fail(); 244 } 245 assert(bufend < bufmax); 246 lineend = bufend++; 247 buf[lineend] = '\n'; 248 } else { 249 bufend += (size_t)result; 250 lineend = findeol(buf, linestart, bufend); 251 } 252 /* loop in case we still don't have a whole line */ 253 continue; 254 } 255 256 /* have a line */ 257 assert(buf[lineend] == '\n'); 258 buf[lineend] = '\0'; 259 nextlinestart = lineend+1; 260 places.nextline.line++; 261 262 /* check for CR/NL */ 263 if (lineend > 0 && buf[lineend-1] == '\r') { 264 buf[lineend-1] = '\0'; 265 lineend--; 266 } 267 268 /* check for continuation line */ 269 if (lineend > 0 && buf[lineend-1]=='\\') { 270 lineend--; 271 tmp = nextlinestart - lineend; 272 if (bufend > nextlinestart) { 273 memmove(buf+lineend, buf+nextlinestart, 274 bufend - nextlinestart); 275 } 276 bufend -= tmp; 277 nextlinestart -= tmp; 278 lineend = findeol(buf, linestart, bufend); 279 /* might not have a whole line, so loop */ 280 continue; 281 } 282 283 /* line now goes from linestart to lineend */ 284 assert(buf[lineend] == '\0'); 285 286 /* count how many commented-out newlines we swallowed */ 287 places.nextline.line += countnls(buf, linestart, lineend); 288 289 /* process the line (even if it's empty) */ 290 directive_gotline(&places, buf+linestart, lineend-linestart); 291 292 linestart = nextlinestart; 293 lineend = findeol(buf, linestart, bufend); 294 places.current = places.nextline; 295 } 296 297 if (toplevel) { 298 directive_goteof(&places.current); 299 } 300 dofree(buf, bufmax); 301} 302 303//////////////////////////////////////////////////////////// 304// path search 305 306static 307char * 308mkfilename(struct place *place, const char *dir, const char *file) 309{ 310 size_t dlen, flen, rlen; 311 char *ret; 312 bool needslash = false; 313 314 if (dir == NULL) { 315 dir = place_getparsedir(place); 316 } 317 318 dlen = strlen(dir); 319 flen = strlen(file); 320 if (dlen > 0 && dir[dlen-1] != '/') { 321 needslash = true; 322 } 323 324 rlen = dlen + (needslash ? 1 : 0) + flen; 325 ret = domalloc(rlen + 1); 326 snprintf(ret, rlen+1, "%s%s%s", dir, needslash ? "/" : "", file); 327 return ret; 328} 329 330static 331int 332file_tryopen(const char *file) 333{ 334 int fd; 335 336 /* XXX check for non-regular files */ 337 338 fd = open(file, O_RDONLY); 339 if (fd == -1) { 340 if (errno != ENOENT && errno != ENOTDIR) { 341 complain(NULL, "%s: %s", file, strerror(errno)); 342 } 343 return -1; 344 } 345 346 return fd; 347} 348 349static 350void 351file_search(struct place *place, struct incdirarray *path, const char *name) 352{ 353 unsigned i, num; 354 struct incdir *id; 355 const struct placefile *pf; 356 char *file; 357 int fd; 358 359 assert(place != NULL); 360 361 if (name[0] == '/') { 362 fd = file_tryopen(name); 363 if (fd >= 0) { 364 pf = place_addfile(place, name, true); 365 file_read(pf, fd, name, false); 366 close(fd); 367 return; 368 } 369 } else { 370 num = incdirarray_num(path); 371 for (i=0; i<num; i++) { 372 id = incdirarray_get(path, i); 373 file = mkfilename(place, id->name, name); 374 fd = file_tryopen(file); 375 if (fd >= 0) { 376 pf = place_addfile(place, file, id->issystem); 377 file_read(pf, fd, file, false); 378 dostrfree(file); 379 close(fd); 380 return; 381 } 382 dostrfree(file); 383 } 384 } 385 complain(place, "Include file %s not found", name); 386 complain_fail(); 387} 388 389void 390file_readquote(struct place *place, const char *name) 391{ 392 file_search(place, "epath, name); 393} 394 395void 396file_readbracket(struct place *place, const char *name) 397{ 398 file_search(place, &bracketpath, name); 399} 400 401void 402file_readabsolute(struct place *place, const char *name) 403{ 404 const struct placefile *pf; 405 int fd; 406 407 assert(place != NULL); 408 409 if (name == NULL) { 410 fd = STDIN_FILENO; 411 pf = place_addfile(place, "<standard-input>", false); 412 } else { 413 fd = file_tryopen(name); 414 if (fd < 0) { 415 complain(NULL, "%s: %s", name, strerror(errno)); 416 die(); 417 } 418 pf = place_addfile(place, name, false); 419 } 420 421 file_read(pf, fd, name, true); 422 423 if (name != NULL) { 424 close(fd); 425 } 426} 427