1 /* $NetBSD: mkdep.c,v 1.43 2013/03/05 21:57:47 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Matthias Scheler. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #if HAVE_NBTOOL_CONFIG_H 33 #include "nbtool_config.h" 34 #endif 35 36 #include <sys/cdefs.h> 37 #if !defined(lint) 38 __COPYRIGHT("@(#) Copyright (c) 1999 The NetBSD Foundation, Inc.\ 39 All rights reserved."); 40 __RCSID("$NetBSD: mkdep.c,v 1.43 2013/03/05 21:57:47 christos Exp $"); 41 #endif /* not lint */ 42 43 #ifndef __minix 44 #include <sys/mman.h> 45 #endif 46 #include <sys/param.h> 47 #include <sys/wait.h> 48 #include <ctype.h> 49 #include <err.h> 50 #include <fcntl.h> 51 #include <getopt.h> 52 #include <locale.h> 53 #include <paths.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 #include "findcc.h" 60 61 typedef struct opt opt_t; 62 struct opt { 63 opt_t *left; 64 opt_t *right; 65 int len; 66 int count; 67 char name[4]; 68 }; 69 70 typedef struct suff_list { 71 size_t len; 72 char *suff; 73 struct suff_list *next; 74 } suff_list_t; 75 76 /* tree of includes for -o processing */ 77 static opt_t *opt; 78 static int width; 79 static int verbose; 80 81 #define DEFAULT_PATH _PATH_DEFPATH 82 #define DEFAULT_FILENAME ".depend" 83 84 static void save_for_optional(const char *, const char *); 85 static size_t write_optional(int, opt_t *, size_t); 86 87 static inline void * 88 deconst(const void *p) 89 { 90 return (const char *)p - (const char *)0 + (char *)0; 91 } 92 93 __dead static void 94 usage(void) 95 { 96 (void)fprintf(stderr, 97 "usage: %s [-aDdiopqv] [-f file] [-P prefix] [-s suffixes] " 98 "-- [flags] file ...\n", 99 getprogname()); 100 exit(EXIT_FAILURE); 101 } 102 103 static int 104 run_cc(int argc, char **argv, const char **fname) 105 { 106 const char *CC, *tmpdir; 107 char * volatile pathname; 108 static char tmpfilename[MAXPATHLEN]; 109 char **args; 110 int tmpfd; 111 pid_t pid, cpid; 112 int status; 113 114 if ((CC = getenv("CC")) == NULL) 115 CC = DEFAULT_CC; 116 if ((pathname = findcc(CC)) == NULL) 117 if (!setenv("PATH", DEFAULT_PATH, 1)) 118 pathname = findcc(CC); 119 if (pathname == NULL) 120 err(EXIT_FAILURE, "%s: not found", CC); 121 if ((args = malloc((argc + 3) * sizeof(char *))) == NULL) 122 err(EXIT_FAILURE, "malloc"); 123 124 args[0] = deconst(CC); 125 args[1] = deconst("-M"); 126 (void)memcpy(&args[2], argv, (argc + 1) * sizeof(char *)); 127 128 if ((tmpdir = getenv("TMPDIR")) == NULL) 129 tmpdir = _PATH_TMP; 130 (void)snprintf(tmpfilename, sizeof (tmpfilename), "%s/%s", tmpdir, 131 "mkdepXXXXXX"); 132 if ((tmpfd = mkstemp(tmpfilename)) < 0) 133 err(EXIT_FAILURE, "Unable to create temporary file %s", 134 tmpfilename); 135 (void)unlink(tmpfilename); 136 *fname = tmpfilename; 137 138 if (verbose) { 139 char **a; 140 for (a = args; *a; a++) 141 printf("%s ", *a); 142 printf("\n"); 143 } 144 145 switch (cpid = vfork()) { 146 case 0: 147 (void)dup2(tmpfd, STDOUT_FILENO); 148 (void)close(tmpfd); 149 150 (void)execv(pathname, args); 151 _exit(EXIT_FAILURE); 152 /* NOTREACHED */ 153 154 case -1: 155 err(EXIT_FAILURE, "unable to fork"); 156 } 157 158 free(pathname); 159 free(args); 160 161 while (((pid = wait(&status)) != cpid) && (pid >= 0)) 162 continue; 163 164 if (status) 165 errx(EXIT_FAILURE, "compile failed."); 166 167 return tmpfd; 168 } 169 170 static const char * 171 read_fname(void) 172 { 173 static char *fbuf; 174 static int fbuflen; 175 int len, ch; 176 177 for (len = 0; (ch = getchar()) != EOF; len++) { 178 if (isspace(ch)) { 179 if (len != 0) 180 break; 181 len--; 182 continue; 183 } 184 if (len >= fbuflen - 1) { 185 fbuf = realloc(fbuf, fbuflen += 32); 186 if (fbuf == NULL) 187 err(EXIT_FAILURE, "no memory"); 188 } 189 fbuf[len] = ch; 190 } 191 if (len == 0) 192 return NULL; 193 fbuf[len] = 0; 194 return fbuf; 195 } 196 197 static struct option longopt[] = { 198 { "sysroot", 1, NULL, 'R' }, 199 { NULL, 0, NULL, '\0' }, 200 }; 201 202 static void 203 addsuff(suff_list_t **l, const char *s, size_t len) 204 { 205 suff_list_t *p = calloc(1, sizeof(*p)); 206 if (p == NULL) 207 err(1, "calloc"); 208 p->suff = malloc(len + 1); 209 if (p->suff == NULL) 210 err(1, "malloc"); 211 memcpy(p->suff, s, len); 212 p->suff[len] = '\0'; 213 p->len = len; 214 p->next = *l; 215 *l = p; 216 } 217 218 int 219 main(int argc, char **argv) 220 { 221 int aflag, dflag, iflag, oflag, qflag; 222 const char *filename; 223 int dependfile; 224 char *buf, *lim, *ptr, *line, *suf, *colon, *eol; 225 int ok_ind, ch; 226 size_t sz; 227 int fd; 228 size_t slen; 229 const char *fname; 230 const char *prefix = NULL; 231 const char *suffixes = NULL, *s; 232 suff_list_t *suff_list = NULL, *sl; 233 #if defined(__minix) 234 size_t nr; 235 236 /* triggers a 'may be used uninitialized', when compiled with gcc, 237 * asserts off, and -Os. */ 238 slen = 0; 239 #endif /* defined(__minix) */ 240 241 suf = NULL; /* XXXGCC -Wuninitialized [sun2] */ 242 sl = NULL; /* XXXGCC -Wuninitialized [sun2] */ 243 244 setlocale(LC_ALL, ""); 245 setprogname(argv[0]); 246 247 aflag = O_WRONLY | O_APPEND | O_CREAT | O_TRUNC; 248 dflag = 0; 249 iflag = 0; 250 oflag = 0; 251 qflag = 0; 252 filename = DEFAULT_FILENAME; 253 dependfile = -1; 254 255 opterr = 0; /* stop getopt() bleating about errors. */ 256 for (;;) { 257 ok_ind = optind; 258 ch = getopt_long(argc, argv, "aDdf:ioP:pqRs:v", longopt, NULL); 259 switch (ch) { 260 case -1: 261 ok_ind = optind; 262 break; 263 case 'a': /* Append to output file */ 264 aflag &= ~O_TRUNC; 265 continue; 266 case 'D': /* Process *.d files (don't run cc -M) */ 267 dflag = 2; /* Read names from stdin */ 268 opterr = 1; 269 continue; 270 case 'd': /* Process *.d files (don't run cc -M) */ 271 dflag = 1; 272 opterr = 1; 273 continue; 274 case 'f': /* Name of output file */ 275 filename = optarg; 276 continue; 277 case 'i': 278 iflag = 1; 279 continue; 280 case 'o': /* Mark dependent files .OPTIONAL */ 281 oflag = 1; 282 continue; 283 case 'P': /* Prefix for each target filename */ 284 prefix = optarg; 285 continue; 286 case 'p': /* Program mode (x.o: -> x:) */ 287 suffixes = ""; 288 continue; 289 case 'q': /* Quiet */ 290 qflag = 1; 291 continue; 292 case 'R': 293 /* sysroot = optarg */ 294 continue; 295 case 's': /* Suffix list */ 296 suffixes = optarg; 297 continue; 298 case 'v': 299 verbose = 1; 300 continue; 301 default: 302 if (dflag) 303 usage(); 304 /* Unknown arguments are passed to "${CC} -M" */ 305 break; 306 } 307 break; 308 } 309 310 argc -= ok_ind; 311 argv += ok_ind; 312 if ((argc == 0 && !dflag) || (argc != 0 && dflag == 2)) 313 usage(); 314 315 if (suffixes != NULL) { 316 if (*suffixes) { 317 for (s = suffixes; (sz = strcspn(s, ", ")) != 0;) { 318 addsuff(&suff_list, s, sz); 319 s += sz; 320 while (*s && strchr(", ", *s)) 321 s++; 322 } 323 } else 324 addsuff(&suff_list, "", 0); 325 } 326 327 dependfile = open(filename, aflag, 0666); 328 if (dependfile == -1) 329 goto wrerror; 330 331 while (dflag == 2 || *argv != NULL) { 332 if (dflag) { 333 if (dflag == 2) { 334 fname = read_fname(); 335 if (fname == NULL) 336 break; 337 } else 338 fname = *argv++; 339 if (iflag) { 340 if (dprintf(dependfile, ".-include \"%s\"\n", 341 fname) < 0) 342 goto wrerror; 343 continue; 344 } 345 fd = open(fname, O_RDONLY, 0); 346 if (fd == -1) { 347 if (!qflag) 348 warn("ignoring %s", fname); 349 continue; 350 } 351 } else { 352 fd = run_cc(argc, argv, &fname); 353 /* consume all args... */ 354 argv += argc; 355 } 356 357 sz = lseek(fd, 0, SEEK_END); 358 if (sz == 0) { 359 close(fd); 360 continue; 361 } 362 #ifndef __minix 363 buf = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); 364 close(fd); 365 366 if (buf == MAP_FAILED) 367 err(EXIT_FAILURE, "unable to mmap file %s", fname); 368 #else 369 buf = malloc(sz); 370 if (buf == NULL) 371 err(EXIT_FAILURE, "malloc"); 372 if ((nr = pread(fd, buf, sz, 0)) != sz) 373 err(EXIT_FAILURE, "read error %s", fname); 374 close(fd); 375 #endif 376 lim = buf + sz - 1; 377 378 /* Remove leading "./" from filenames */ 379 for (ptr = buf; ptr < lim; ptr++) { 380 if (ptr[1] != '.' || ptr[2] != '/' 381 || !isspace((unsigned char)ptr[0])) 382 continue; 383 ptr[1] = ' '; 384 ptr[2] = ' '; 385 } 386 387 for (line = eol = buf; eol <= lim;) { 388 while (eol <= lim && *eol++ != '\n') 389 /* Find end of this line */ 390 continue; 391 if (line == eol - 1) { 392 /* empty line - ignore */ 393 line = eol; 394 continue; 395 } 396 if (eol[-2] == '\\') 397 /* Assemble continuation lines */ 398 continue; 399 for (colon = line; *colon != ':'; colon++) { 400 if (colon >= eol) { 401 colon = NULL; 402 break; 403 } 404 } 405 if (isspace((unsigned char)*line) || colon == NULL) { 406 /* No dependency - just transcribe line */ 407 if (write(dependfile, line, eol - line) < 0) 408 goto wrerror; 409 line = eol; 410 continue; 411 } 412 if (suff_list != NULL) { 413 /* Find the .o: */ 414 /* First allow for any whitespace */ 415 for (suf = colon; suf > buf; suf--) { 416 if (!isspace((unsigned char)suf[-1])) 417 break; 418 } 419 if (suf == buf) 420 errx(EXIT_FAILURE, 421 "Corrupted file `%s'", fname); 422 /* Then look for any valid suffix */ 423 for (sl = suff_list; sl != NULL; 424 sl = sl->next) { 425 if (sl->len && buf <= suf - sl->len && 426 !memcmp(suf - sl->len, sl->suff, 427 sl->len)) 428 break; 429 } 430 /* 431 * Not found, check for .o, since the 432 * original file will have it. 433 */ 434 if (sl == NULL) { 435 if (memcmp(suf - 2, ".o", 2) == 0) 436 slen = 2; 437 else 438 slen = 0; 439 } else 440 slen = sl->len; 441 } 442 if (suff_list != NULL && slen != 0) { 443 suf -= slen; 444 for (sl = suff_list; sl != NULL; sl = sl->next) 445 { 446 if (sl != suff_list) 447 if (write(dependfile, " ", 1) 448 < 0) 449 goto wrerror; 450 if (prefix != NULL) 451 if (write(dependfile, prefix, 452 strlen(prefix)) < 0) 453 goto wrerror; 454 if (write(dependfile, line, 455 suf - line) < 0) 456 goto wrerror; 457 if (write(dependfile, sl->suff, 458 sl->len) < 0) 459 goto wrerror; 460 } 461 if (write(dependfile, colon, eol - colon) < 0) 462 goto wrerror; 463 } else { 464 if (prefix != NULL) 465 if (write(dependfile, prefix, 466 strlen(prefix)) < 0) 467 goto wrerror; 468 if (write(dependfile, line, eol - line) < 0) 469 goto wrerror; 470 } 471 472 if (oflag) 473 save_for_optional(colon + 1, eol); 474 line = eol; 475 } 476 #ifndef __minix 477 munmap(buf, sz); 478 #else 479 free(buf); 480 #endif 481 } 482 483 if (oflag && opt != NULL) { 484 if (write(dependfile, ".OPTIONAL:", 10) < 0) 485 goto wrerror; 486 width = 9; 487 sz = write_optional(dependfile, opt, 0); 488 if (sz == (size_t)-1) 489 goto wrerror; 490 /* 'depth' is about 39 for an i386 kernel */ 491 /* fprintf(stderr, "Recursion depth %d\n", sz); */ 492 } 493 close(dependfile); 494 495 exit(EXIT_SUCCESS); 496 wrerror: 497 err(EXIT_FAILURE, "unable to %s to file %s\n", 498 aflag & O_TRUNC ? "write" : "append", filename); 499 } 500 501 502 /* 503 * Only save each file once - the kernel .depend is 3MB and there is 504 * no point doubling its size. 505 * The data seems to be 'random enough' so the simple binary tree 506 * only has a reasonable depth. 507 */ 508 static void 509 save_for_optional(const char *start, const char *limit) 510 { 511 opt_t **l, *n; 512 const char *name, *end; 513 int c; 514 515 while (start < limit && strchr(" \t\n\\", *start)) 516 start++; 517 for (name = start; ; name = end) { 518 while (name < limit && strchr(" \t\n\\", *name)) 519 name++; 520 for (end = name; end < limit && !strchr(" \t\n\\", *end);) 521 end++; 522 if (name >= limit) 523 break; 524 if (end[-1] == 'c' && end[-2] == '.' && name == start) 525 /* ignore dependency on the files own .c */ 526 continue; 527 for (l = &opt;;) { 528 n = *l; 529 if (n == NULL) { 530 n = malloc(sizeof *n + (end - name)); 531 n->left = n->right = 0; 532 n->len = end - name; 533 n->count = 1; 534 n->name[0] = ' '; 535 memcpy(n->name + 1, name, end - name); 536 *l = n; 537 break; 538 } 539 c = (end - name) - n->len; 540 if (c == 0) 541 c = memcmp(n->name + 1, name, (end - name)); 542 if (c == 0) { 543 /* Duplicate */ 544 n->count++; 545 break; 546 } 547 if (c < 0) 548 l = &n->left; 549 else 550 l = &n->right; 551 } 552 } 553 } 554 555 static size_t 556 write_optional(int fd, opt_t *node, size_t depth) 557 { 558 size_t d1 = ++depth; 559 560 if (node->left) 561 d1 = write_optional(fd, node->left, d1); 562 if (width > 76 - node->len) { 563 if (write(fd, " \\\n ", 4) < 0) 564 return (size_t)-1; 565 width = 1; 566 } 567 width += 1 + node->len; 568 if (write(fd, node->name, 1 + node->len) < 0) 569 return (size_t)-1; 570 if (node->right) 571 depth = write_optional(fd, node->right, depth); 572 return d1 > depth ? d1 : depth; 573 } 574