files.c revision 1.1
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 <stdbool.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34#include <unistd.h> 35#include <fcntl.h> 36#include <errno.h> 37 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 place linestartplace, nextlinestartplace, ptmp; 176 size_t bufend, bufmax, linestart, lineend, nextlinestart, tmp; 177 ssize_t result; 178 bool ateof = false; 179 char *buf; 180 181 place_setfilestart(&linestartplace, pf); 182 nextlinestartplace = linestartplace; 183 184 bufmax = 128; 185 bufend = 0; 186 linestart = 0; 187 lineend = 0; 188 buf = domalloc(bufmax); 189 190 while (1) { 191 if (lineend >= bufend) { 192 /* do not have a whole line in the buffer; read more */ 193 assert(bufend >= linestart); 194 if (linestart > 0 && bufend > linestart) { 195 /* slide to beginning of buffer */ 196 memmove(buf, buf+linestart, bufend-linestart); 197 bufend -= linestart; 198 lineend -= linestart; 199 linestart = 0; 200 } 201 if (bufend >= bufmax) { 202 /* need bigger buffer */ 203 buf = dorealloc(buf, bufmax, bufmax*2); 204 bufmax = bufmax*2; 205 } 206 207 if (ateof) { 208 /* don't read again, in case it's a socket */ 209 result = 0; 210 } else { 211 result = read(fd, buf+bufend, bufmax - bufend); 212 } 213 214 if (result == -1) { 215 /* read error */ 216 complain(NULL, "%s: %s", 217 name, strerror(errno)); 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; 237 lineend = findeol(buf, linestart, bufend); 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; 265 lineend = findeol(buf, linestart, bufend); 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'); 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 */ 277 if (lineend > linestart) { 278 directive_gotline(&linestartplace, 279 buf+linestart, lineend-linestart); 280 } 281 282 linestart = nextlinestart; 283 lineend = findeol(buf, linestart, bufend); 284 linestartplace = nextlinestartplace; 285 } 286 287 if (toplevel) { 288 directive_goteof(&linestartplace); 289 } 290 dofree(buf, bufmax); 291} 292 293//////////////////////////////////////////////////////////// 294// path search 295 296static 297char * 298mkfilename(struct place *place, const char *dir, const char *file) 299{ 300 size_t dlen, flen, rlen; 301 char *ret; 302 bool needslash = false; 303 304 if (dir == NULL) { 305 dir = place_getparsedir(place); 306 } 307 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 324static 325int 326file_tryopen(const char *file) 327{ 328 int fd; 329 330 /* XXX check for non-regular files */ 331 332 fd = open(file, O_RDONLY); 333 if (fd < 0) { 334 if (errno != ENOENT && errno != ENOTDIR) { 335 complain(NULL, "%s: %s", file, strerror(errno)); 336 } 337 return -1; 338 } 339 340 return fd; 341} 342 343static 344void 345file_search(struct place *place, struct incdirarray *path, const char *name) 346{ 347 unsigned i, num; 348 struct incdir *id; 349 const struct placefile *pf; 350 char *file; 351 int fd; 352 353 assert(place != NULL); 354 355 if (name[0] == '/') { 356 fd = file_tryopen(name); 357 if (fd >= 0) { 358 pf = place_addfile(place, name, true); 359 file_read(pf, fd, name, false); 360 close(fd); 361 return; 362 } 363 } else { 364 num = incdirarray_num(path); 365 for (i=0; i<num; i++) { 366 id = incdirarray_get(path, i); 367 file = mkfilename(place, id->name, name); 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 } 378 } 379 complain(place, "Include file %s not found", name); 380 complain_fail(); 381} 382 383void 384file_readquote(struct place *place, const char *name) 385{ 386 file_search(place, "epath, name); 387} 388 389void 390file_readbracket(struct place *place, const char *name) 391{ 392 file_search(place, &bracketpath, name); 393} 394 395void 396file_readabsolute(struct place *place, const char *name) 397{ 398 const struct placefile *pf; 399 int fd; 400 401 assert(place != NULL); 402 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 complain(NULL, "%s: %s", name, strerror(errno)); 410 die(); 411 } 412 pf = place_addfile(place, name, false); 413 } 414 415 file_read(pf, fd, name, true); 416 417 if (name != NULL) { 418 close(fd); 419 } 420} 421