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("epath); 80 incdirarray_init(&bracketpath); 81 } 82 83 DESTROYALL_ARRAY(incdir, ); 84 85 void 86 files_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 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("epath, 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 snprintf(ret, rlen+1, "%s%s%s", dir, needslash ? "/" : "", file); 338 return ret; 339 } 340 341 static 342 int 343 file_tryopen(const char *file) 344 { 345 int fd; 346 347 /* XXX check for non-regular files */ 348 349 fd = open(file, O_RDONLY); 350 if (fd == -1) { 351 if (errno != ENOENT && errno != ENOTDIR) { 352 complain(NULL, "%s: %s", file, strerror(errno)); 353 } 354 return -1; 355 } 356 357 return fd; 358 } 359 360 static 361 void 362 file_search(struct place *place, struct incdirarray *path, const char *name) 363 { 364 unsigned i, num; 365 struct incdir *id; 366 const struct placefile *pf; 367 char *file; 368 int fd; 369 370 assert(place != NULL); 371 372 if (name[0] == '/') { 373 fd = file_tryopen(name); 374 if (fd >= 0) { 375 pf = place_addfile(place, name, true); 376 file_read(pf, fd, name, false); 377 close(fd); 378 return; 379 } 380 } else { 381 num = incdirarray_num(path); 382 for (i=0; i<num; i++) { 383 id = incdirarray_get(path, i); 384 file = mkfilename(place, id->name, name); 385 fd = file_tryopen(file); 386 if (fd >= 0) { 387 pf = place_addfile(place, file, id->issystem); 388 file_read(pf, fd, file, false); 389 dostrfree(file); 390 close(fd); 391 return; 392 } 393 dostrfree(file); 394 } 395 } 396 complain(place, "Include file %s not found", name); 397 complain_fail(); 398 } 399 400 void 401 file_readquote(struct place *place, const char *name) 402 { 403 file_search(place, "epath, name); 404 } 405 406 void 407 file_readbracket(struct place *place, const char *name) 408 { 409 file_search(place, &bracketpath, name); 410 } 411 412 void 413 file_readabsolute(struct place *place, const char *name) 414 { 415 const struct placefile *pf; 416 int fd; 417 418 assert(place != NULL); 419 420 if (name == NULL) { 421 fd = STDIN_FILENO; 422 pf = place_addfile(place, "<standard-input>", false); 423 } else { 424 fd = file_tryopen(name); 425 if (fd < 0) { 426 complain(NULL, "%s: %s", name, strerror(errno)); 427 die(); 428 } 429 pf = place_addfile(place, name, false); 430 } 431 432 file_read(pf, fd, name, true); 433 434 if (name != NULL) { 435 close(fd); 436 } 437 } 438