1 /* $NetBSD: main.c,v 1.599 2023/09/10 21:52:36 rillig Exp $ */ 2 3 /* 4 * Copyright (c) 1988, 1989, 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Adam de Boor. 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 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 /* 36 * Copyright (c) 1989 by Berkeley Softworks 37 * All rights reserved. 38 * 39 * This code is derived from software contributed to Berkeley by 40 * Adam de Boor. 41 * 42 * Redistribution and use in source and binary forms, with or without 43 * modification, are permitted provided that the following conditions 44 * are met: 45 * 1. Redistributions of source code must retain the above copyright 46 * notice, this list of conditions and the following disclaimer. 47 * 2. Redistributions in binary form must reproduce the above copyright 48 * notice, this list of conditions and the following disclaimer in the 49 * documentation and/or other materials provided with the distribution. 50 * 3. All advertising materials mentioning features or use of this software 51 * must display the following acknowledgement: 52 * This product includes software developed by the University of 53 * California, Berkeley and its contributors. 54 * 4. Neither the name of the University nor the names of its contributors 55 * may be used to endorse or promote products derived from this software 56 * without specific prior written permission. 57 * 58 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 61 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 68 * SUCH DAMAGE. 69 */ 70 71 /* 72 * The main file for this entire program. Exit routines etc. reside here. 73 * 74 * Utility functions defined in this file: 75 * 76 * Main_ParseArgLine 77 * Parse and process command line arguments from a 78 * single string. Used to implement the special targets 79 * .MFLAGS and .MAKEFLAGS. 80 * 81 * Error Print a tagged error message. 82 * 83 * Fatal Print an error message and exit. 84 * 85 * Punt Abort all jobs and exit with a message. 86 * 87 * Finish Finish things up by printing the number of errors 88 * that occurred, and exit. 89 */ 90 91 #include <sys/types.h> 92 #include <sys/time.h> 93 #include <sys/param.h> 94 #include <sys/resource.h> 95 #include <sys/stat.h> 96 #if defined(MAKE_NATIVE) && defined(HAVE_SYSCTL) 97 #include <sys/sysctl.h> 98 #endif 99 #include <sys/utsname.h> 100 #include "wait.h" 101 102 #include <errno.h> 103 #include <signal.h> 104 #include <stdarg.h> 105 #include <time.h> 106 107 #include "make.h" 108 #include "dir.h" 109 #include "job.h" 110 #include "pathnames.h" 111 #include "trace.h" 112 113 /* "@(#)main.c 8.3 (Berkeley) 3/19/94" */ 114 MAKE_RCSID("$NetBSD: main.c,v 1.599 2023/09/10 21:52:36 rillig Exp $"); 115 #if defined(MAKE_NATIVE) && !defined(lint) 116 __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 " 117 "The Regents of the University of California. " 118 "All rights reserved."); 119 #endif 120 121 #ifndef __arraycount 122 # define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) 123 #endif 124 125 CmdOpts opts; 126 time_t now; /* Time at start of make */ 127 GNode *defaultNode; /* .DEFAULT node */ 128 bool allPrecious; /* .PRECIOUS given on line by itself */ 129 bool deleteOnError; /* .DELETE_ON_ERROR: set */ 130 131 static int maxJobTokens; /* -j argument */ 132 static bool enterFlagObj; /* -w and objdir != srcdir */ 133 134 static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */ 135 bool doing_depend; /* Set while reading .depend */ 136 static bool jobsRunning; /* true if the jobs might be running */ 137 static const char *tracefile; 138 static bool ReadMakefile(const char *); 139 static void purge_relative_cached_realpaths(void); 140 141 static bool ignorePWD; /* if we use -C, PWD is meaningless */ 142 static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */ 143 char curdir[MAXPATHLEN + 1]; /* Startup directory */ 144 const char *progname; 145 char *makeDependfile; 146 pid_t myPid; 147 int makelevel; 148 149 bool forceJobs = false; 150 static int main_errors = 0; 151 static HashTable cached_realpaths; 152 153 /* 154 * For compatibility with the POSIX version of MAKEFLAGS that includes 155 * all the options without '-', convert 'flags' to '-f -l -a -g -s'. 156 */ 157 static char * 158 explode(const char *flags) 159 { 160 char *exploded, *ep; 161 const char *p; 162 163 if (flags == NULL) 164 return NULL; 165 166 for (p = flags; *p != '\0'; p++) 167 if (!ch_isalpha(*p)) 168 return bmake_strdup(flags); 169 170 exploded = bmake_malloc((size_t)(p - flags) * 3 + 1); 171 for (p = flags, ep = exploded; *p != '\0'; p++) { 172 *ep++ = '-'; 173 *ep++ = *p; 174 *ep++ = ' '; 175 } 176 *ep = '\0'; 177 return exploded; 178 } 179 180 MAKE_ATTR_DEAD static void 181 usage(void) 182 { 183 size_t prognameLen = strcspn(progname, "["); 184 185 (void)fprintf(stderr, 186 "usage: %.*s [-BeikNnqrSstWwX]\n" 187 " [-C directory] [-D variable] [-d flags] [-f makefile]\n" 188 " [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n" 189 " [-V variable] [-v variable] [variable=value] [target ...]\n", 190 (int)prognameLen, progname); 191 exit(2); 192 } 193 194 static void 195 MainParseArgDebugFile(const char *arg) 196 { 197 const char *mode; 198 size_t len; 199 char *fname; 200 201 if (opts.debug_file != stdout && opts.debug_file != stderr) 202 fclose(opts.debug_file); 203 204 if (*arg == '+') { 205 arg++; 206 mode = "a"; 207 } else 208 mode = "w"; 209 210 if (strcmp(arg, "stdout") == 0) { 211 opts.debug_file = stdout; 212 return; 213 } 214 if (strcmp(arg, "stderr") == 0) { 215 opts.debug_file = stderr; 216 return; 217 } 218 219 len = strlen(arg); 220 fname = bmake_malloc(len + 20); 221 memcpy(fname, arg, len + 1); 222 223 /* Replace the trailing '%d' after '.%d' with the pid. */ 224 if (len >= 3 && memcmp(fname + len - 3, ".%d", 3) == 0) 225 snprintf(fname + len - 2, 20, "%d", getpid()); 226 227 opts.debug_file = fopen(fname, mode); 228 if (opts.debug_file == NULL) { 229 fprintf(stderr, "Cannot open debug file \"%s\"\n", 230 fname); 231 exit(2); 232 } 233 free(fname); 234 } 235 236 static void 237 MainParseArgDebug(const char *argvalue) 238 { 239 const char *modules; 240 DebugFlags debug = opts.debug; 241 242 for (modules = argvalue; *modules != '\0'; modules++) { 243 switch (*modules) { 244 case '0': /* undocumented, only intended for tests */ 245 memset(&debug, 0, sizeof(debug)); 246 break; 247 case 'A': 248 memset(&debug, ~0, sizeof(debug)); 249 break; 250 case 'a': 251 debug.DEBUG_ARCH = true; 252 break; 253 case 'C': 254 debug.DEBUG_CWD = true; 255 break; 256 case 'c': 257 debug.DEBUG_COND = true; 258 break; 259 case 'd': 260 debug.DEBUG_DIR = true; 261 break; 262 case 'e': 263 debug.DEBUG_ERROR = true; 264 break; 265 case 'f': 266 debug.DEBUG_FOR = true; 267 break; 268 case 'g': 269 if (modules[1] == '1') { 270 debug.DEBUG_GRAPH1 = true; 271 modules++; 272 } else if (modules[1] == '2') { 273 debug.DEBUG_GRAPH2 = true; 274 modules++; 275 } else if (modules[1] == '3') { 276 debug.DEBUG_GRAPH3 = true; 277 modules++; 278 } 279 break; 280 case 'h': 281 debug.DEBUG_HASH = true; 282 break; 283 case 'j': 284 debug.DEBUG_JOB = true; 285 break; 286 case 'L': 287 opts.strict = true; 288 break; 289 case 'l': 290 debug.DEBUG_LOUD = true; 291 break; 292 case 'M': 293 debug.DEBUG_META = true; 294 break; 295 case 'm': 296 debug.DEBUG_MAKE = true; 297 break; 298 case 'n': 299 debug.DEBUG_SCRIPT = true; 300 break; 301 case 'p': 302 debug.DEBUG_PARSE = true; 303 break; 304 case 's': 305 debug.DEBUG_SUFF = true; 306 break; 307 case 't': 308 debug.DEBUG_TARG = true; 309 break; 310 case 'V': 311 opts.debugVflag = true; 312 break; 313 case 'v': 314 debug.DEBUG_VAR = true; 315 break; 316 case 'x': 317 debug.DEBUG_SHELL = true; 318 break; 319 case 'F': 320 MainParseArgDebugFile(modules + 1); 321 goto finish; 322 default: 323 (void)fprintf(stderr, 324 "%s: illegal argument to d option -- %c\n", 325 progname, *modules); 326 usage(); 327 } 328 } 329 330 finish: 331 opts.debug = debug; 332 333 setvbuf(opts.debug_file, NULL, _IONBF, 0); 334 if (opts.debug_file != stdout) 335 setvbuf(stdout, NULL, _IOLBF, 0); 336 } 337 338 /* Is path relative or does it contain any relative component "." or ".."? */ 339 static bool 340 IsRelativePath(const char *path) 341 { 342 const char *p; 343 344 if (path[0] != '/') 345 return true; 346 p = path; 347 while ((p = strstr(p, "/.")) != NULL) { 348 p += 2; 349 if (*p == '.') 350 p++; 351 if (*p == '/' || *p == '\0') 352 return true; 353 } 354 return false; 355 } 356 357 static void 358 MainParseArgChdir(const char *argvalue) 359 { 360 struct stat sa, sb; 361 362 if (chdir(argvalue) == -1) { 363 (void)fprintf(stderr, "%s: chdir %s: %s\n", 364 progname, argvalue, strerror(errno)); 365 exit(2); /* Not 1 so -q can distinguish error */ 366 } 367 if (getcwd(curdir, MAXPATHLEN) == NULL) { 368 (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); 369 exit(2); 370 } 371 if (!IsRelativePath(argvalue) && 372 stat(argvalue, &sa) != -1 && 373 stat(curdir, &sb) != -1 && 374 sa.st_ino == sb.st_ino && 375 sa.st_dev == sb.st_dev) 376 strncpy(curdir, argvalue, MAXPATHLEN); 377 ignorePWD = true; 378 } 379 380 static void 381 MainParseArgJobsInternal(const char *argvalue) 382 { 383 char end; 384 if (sscanf(argvalue, "%d,%d%c", &jp_0, &jp_1, &end) != 2) { 385 (void)fprintf(stderr, 386 "%s: internal error -- J option malformed (%s)\n", 387 progname, argvalue); 388 usage(); 389 } 390 if ((fcntl(jp_0, F_GETFD, 0) < 0) || 391 (fcntl(jp_1, F_GETFD, 0) < 0)) { 392 jp_0 = -1; 393 jp_1 = -1; 394 opts.compatMake = true; 395 } else { 396 Global_Append(MAKEFLAGS, "-J"); 397 Global_Append(MAKEFLAGS, argvalue); 398 } 399 } 400 401 static void 402 MainParseArgJobs(const char *arg) 403 { 404 const char *p; 405 char *end; 406 char v[12]; 407 408 forceJobs = true; 409 opts.maxJobs = (int)strtol(arg, &end, 0); 410 p = arg + (end - arg); 411 #ifdef _SC_NPROCESSORS_ONLN 412 if (*p != '\0') { 413 double d; 414 415 if (*p == 'C') { 416 d = (opts.maxJobs > 0) ? opts.maxJobs : 1; 417 } else if (*p == '.') { 418 d = strtod(arg, &end); 419 p = arg + (end - arg); 420 } else 421 d = 0; 422 if (d > 0) { 423 p = ""; 424 opts.maxJobs = (int)sysconf(_SC_NPROCESSORS_ONLN); 425 opts.maxJobs = (int)(d * (double)opts.maxJobs); 426 } 427 } 428 #endif 429 if (*p != '\0' || opts.maxJobs < 1) { 430 (void)fprintf(stderr, 431 "%s: argument '%s' to option '-j' " 432 "must be a positive number\n", 433 progname, arg); 434 exit(2); /* Not 1 so -q can distinguish error */ 435 } 436 snprintf(v, sizeof(v), "%d", opts.maxJobs); 437 Global_Append(MAKEFLAGS, "-j"); 438 Global_Append(MAKEFLAGS, v); 439 Global_Set(".MAKE.JOBS", v); 440 maxJobTokens = opts.maxJobs; 441 } 442 443 static void 444 MainParseArgSysInc(const char *argvalue) 445 { 446 /* look for magic parent directory search string */ 447 if (strncmp(".../", argvalue, 4) == 0) { 448 char *found_path = Dir_FindHereOrAbove(curdir, argvalue + 4); 449 if (found_path == NULL) 450 return; 451 (void)SearchPath_Add(sysIncPath, found_path); 452 free(found_path); 453 } else { 454 (void)SearchPath_Add(sysIncPath, argvalue); 455 } 456 Global_Append(MAKEFLAGS, "-m"); 457 Global_Append(MAKEFLAGS, argvalue); 458 Dir_SetSYSPATH(); 459 } 460 461 static bool 462 MainParseOption(char c, const char *argvalue) 463 { 464 switch (c) { 465 case '\0': 466 break; 467 case 'B': 468 opts.compatMake = true; 469 Global_Append(MAKEFLAGS, "-B"); 470 Global_Set(".MAKE.MODE", "compat"); 471 break; 472 case 'C': 473 MainParseArgChdir(argvalue); 474 break; 475 case 'D': 476 if (argvalue[0] == '\0') 477 return false; 478 Var_SetExpand(SCOPE_GLOBAL, argvalue, "1"); 479 Global_Append(MAKEFLAGS, "-D"); 480 Global_Append(MAKEFLAGS, argvalue); 481 break; 482 case 'I': 483 Parse_AddIncludeDir(argvalue); 484 Global_Append(MAKEFLAGS, "-I"); 485 Global_Append(MAKEFLAGS, argvalue); 486 break; 487 case 'J': 488 MainParseArgJobsInternal(argvalue); 489 break; 490 case 'N': 491 opts.noExecute = true; 492 opts.noRecursiveExecute = true; 493 Global_Append(MAKEFLAGS, "-N"); 494 break; 495 case 'S': 496 opts.keepgoing = false; 497 Global_Append(MAKEFLAGS, "-S"); 498 break; 499 case 'T': 500 tracefile = bmake_strdup(argvalue); 501 Global_Append(MAKEFLAGS, "-T"); 502 Global_Append(MAKEFLAGS, argvalue); 503 break; 504 case 'V': 505 case 'v': 506 opts.printVars = c == 'v' ? PVM_EXPANDED : PVM_UNEXPANDED; 507 Lst_Append(&opts.variables, bmake_strdup(argvalue)); 508 /* XXX: Why always -V? */ 509 Global_Append(MAKEFLAGS, "-V"); 510 Global_Append(MAKEFLAGS, argvalue); 511 break; 512 case 'W': 513 opts.parseWarnFatal = true; 514 /* XXX: why no Global_Append? */ 515 break; 516 case 'X': 517 opts.varNoExportEnv = true; 518 Global_Append(MAKEFLAGS, "-X"); 519 break; 520 case 'd': 521 /* If '-d-opts' don't pass to children */ 522 if (argvalue[0] == '-') 523 argvalue++; 524 else { 525 Global_Append(MAKEFLAGS, "-d"); 526 Global_Append(MAKEFLAGS, argvalue); 527 } 528 MainParseArgDebug(argvalue); 529 break; 530 case 'e': 531 opts.checkEnvFirst = true; 532 Global_Append(MAKEFLAGS, "-e"); 533 break; 534 case 'f': 535 Lst_Append(&opts.makefiles, bmake_strdup(argvalue)); 536 break; 537 case 'i': 538 opts.ignoreErrors = true; 539 Global_Append(MAKEFLAGS, "-i"); 540 break; 541 case 'j': 542 MainParseArgJobs(argvalue); 543 break; 544 case 'k': 545 opts.keepgoing = true; 546 Global_Append(MAKEFLAGS, "-k"); 547 break; 548 case 'm': 549 MainParseArgSysInc(argvalue); 550 /* XXX: why no Var_Append? */ 551 break; 552 case 'n': 553 opts.noExecute = true; 554 Global_Append(MAKEFLAGS, "-n"); 555 break; 556 case 'q': 557 opts.query = true; 558 /* Kind of nonsensical, wot? */ 559 Global_Append(MAKEFLAGS, "-q"); 560 break; 561 case 'r': 562 opts.noBuiltins = true; 563 Global_Append(MAKEFLAGS, "-r"); 564 break; 565 case 's': 566 opts.silent = true; 567 Global_Append(MAKEFLAGS, "-s"); 568 break; 569 case 't': 570 opts.touch = true; 571 Global_Append(MAKEFLAGS, "-t"); 572 break; 573 case 'w': 574 opts.enterFlag = true; 575 Global_Append(MAKEFLAGS, "-w"); 576 break; 577 default: 578 usage(); 579 } 580 return true; 581 } 582 583 /* 584 * Parse the given arguments. Called from main() and from 585 * Main_ParseArgLine() when the .MAKEFLAGS target is used. 586 * 587 * The arguments must be treated as read-only and will be freed after the 588 * call. 589 * 590 * XXX: Deal with command line overriding .MAKEFLAGS in makefile 591 */ 592 static void 593 MainParseArgs(int argc, char **argv) 594 { 595 char c; 596 int arginc; 597 char *argvalue; 598 char *optscan; 599 bool inOption, dashDash = false; 600 601 const char *optspecs = "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstv:w"; 602 /* Can't actually use getopt(3) because rescanning is not portable */ 603 604 rearg: 605 inOption = false; 606 optscan = NULL; 607 while (argc > 1) { 608 const char *optspec; 609 if (!inOption) 610 optscan = argv[1]; 611 c = *optscan++; 612 arginc = 0; 613 if (inOption) { 614 if (c == '\0') { 615 argv++; 616 argc--; 617 inOption = false; 618 continue; 619 } 620 } else { 621 if (c != '-' || dashDash) 622 break; 623 inOption = true; 624 c = *optscan++; 625 } 626 /* '-' found at some earlier point */ 627 optspec = strchr(optspecs, c); 628 if (c != '\0' && optspec != NULL && optspec[1] == ':') { 629 /* 630 * -<something> found, and <something> should have an 631 * argument 632 */ 633 inOption = false; 634 arginc = 1; 635 argvalue = optscan; 636 if (*argvalue == '\0') { 637 if (argc < 3) 638 goto noarg; 639 argvalue = argv[2]; 640 arginc = 2; 641 } 642 } else { 643 argvalue = NULL; 644 } 645 switch (c) { 646 case '\0': 647 arginc = 1; 648 inOption = false; 649 break; 650 case '-': 651 dashDash = true; 652 break; 653 default: 654 if (!MainParseOption(c, argvalue)) 655 goto noarg; 656 } 657 argv += arginc; 658 argc -= arginc; 659 } 660 661 /* 662 * See if the rest of the arguments are variable assignments and 663 * perform them if so. Else take them to be targets and stuff them 664 * on the end of the "create" list. 665 */ 666 for (; argc > 1; argv++, argc--) { 667 if (!Parse_VarAssign(argv[1], false, SCOPE_CMDLINE)) { 668 if (argv[1][0] == '\0') 669 Punt("illegal (null) argument."); 670 if (argv[1][0] == '-' && !dashDash) 671 goto rearg; 672 Lst_Append(&opts.create, bmake_strdup(argv[1])); 673 } 674 } 675 676 return; 677 noarg: 678 (void)fprintf(stderr, "%s: option requires an argument -- %c\n", 679 progname, c); 680 usage(); 681 } 682 683 /* 684 * Break a line of arguments into words and parse them. 685 * 686 * Used when a .MFLAGS or .MAKEFLAGS target is encountered during parsing and 687 * by main() when reading the MAKEFLAGS environment variable. 688 */ 689 void 690 Main_ParseArgLine(const char *line) 691 { 692 Words words; 693 char *buf; 694 695 if (line == NULL) 696 return; 697 /* XXX: don't use line as an iterator variable */ 698 for (; *line == ' '; line++) 699 continue; 700 if (line[0] == '\0') 701 return; 702 703 #ifndef POSIX 704 { 705 /* 706 * $MAKE may simply be naming the make(1) binary 707 */ 708 char *cp; 709 710 if (!(cp = strrchr(line, '/'))) 711 cp = line; 712 if ((cp = strstr(cp, "make")) && 713 strcmp(cp, "make") == 0) 714 return; 715 } 716 #endif 717 { 718 FStr argv0 = Var_Value(SCOPE_GLOBAL, ".MAKE"); 719 buf = str_concat3(argv0.str, " ", line); 720 FStr_Done(&argv0); 721 } 722 723 words = Str_Words(buf, true); 724 if (words.words == NULL) { 725 Error("Unterminated quoted string [%s]", buf); 726 free(buf); 727 return; 728 } 729 free(buf); 730 MainParseArgs((int)words.len, words.words); 731 732 Words_Free(words); 733 } 734 735 bool 736 Main_SetObjdir(bool writable, const char *fmt, ...) 737 { 738 struct stat sb; 739 char *path; 740 char buf[MAXPATHLEN + 1]; 741 char buf2[MAXPATHLEN + 1]; 742 va_list ap; 743 744 va_start(ap, fmt); 745 vsnprintf(path = buf, MAXPATHLEN, fmt, ap); 746 va_end(ap); 747 748 if (path[0] != '/') { 749 if (snprintf(buf2, MAXPATHLEN, "%s/%s", curdir, path) <= MAXPATHLEN) 750 path = buf2; 751 else 752 return false; 753 } 754 755 /* look for the directory and try to chdir there */ 756 if (stat(path, &sb) != 0 || !S_ISDIR(sb.st_mode)) 757 return false; 758 759 if ((writable && access(path, W_OK) != 0) || chdir(path) != 0) { 760 (void)fprintf(stderr, "%s: warning: %s: %s.\n", 761 progname, path, strerror(errno)); 762 return false; 763 } 764 765 snprintf(objdir, sizeof objdir, "%s", path); 766 Global_Set(".OBJDIR", objdir); 767 setenv("PWD", objdir, 1); 768 Dir_InitDot(); 769 purge_relative_cached_realpaths(); 770 if (opts.enterFlag && strcmp(objdir, curdir) != 0) 771 enterFlagObj = true; 772 return true; 773 } 774 775 static bool 776 SetVarObjdir(bool writable, const char *var, const char *suffix) 777 { 778 FStr path = Var_Value(SCOPE_CMDLINE, var); 779 780 if (path.str == NULL || path.str[0] == '\0') { 781 FStr_Done(&path); 782 return false; 783 } 784 785 Var_Expand(&path, SCOPE_GLOBAL, VARE_WANTRES); 786 787 (void)Main_SetObjdir(writable, "%s%s", path.str, suffix); 788 789 FStr_Done(&path); 790 return true; 791 } 792 793 /* 794 * Splits str into words, adding them to the list. 795 * The string must be kept alive as long as the list. 796 */ 797 int 798 str2Lst_Append(StringList *lp, char *str) 799 { 800 char *cp; 801 int n; 802 803 const char *sep = " \t"; 804 805 for (n = 0, cp = strtok(str, sep); cp != NULL; cp = strtok(NULL, sep)) { 806 Lst_Append(lp, cp); 807 n++; 808 } 809 return n; 810 } 811 812 #ifdef SIGINFO 813 /*ARGSUSED*/ 814 static void 815 siginfo(int signo MAKE_ATTR_UNUSED) 816 { 817 char dir[MAXPATHLEN]; 818 char str[2 * MAXPATHLEN]; 819 int len; 820 if (getcwd(dir, sizeof dir) == NULL) 821 return; 822 len = snprintf(str, sizeof str, "%s: Working in: %s\n", progname, dir); 823 if (len > 0) 824 (void)write(STDERR_FILENO, str, (size_t)len); 825 } 826 #endif 827 828 /* Allow makefiles some control over the mode we run in. */ 829 static void 830 MakeMode(void) 831 { 832 char *mode = Var_Subst("${.MAKE.MODE:tl}", 833 SCOPE_GLOBAL, VARE_WANTRES); 834 /* TODO: handle errors */ 835 836 if (mode[0] != '\0') { 837 if (strstr(mode, "compat") != NULL) { 838 opts.compatMake = true; 839 forceJobs = false; 840 } 841 #if USE_META 842 if (strstr(mode, "meta") != NULL) 843 meta_mode_init(mode); 844 #endif 845 if (strstr(mode, "randomize-targets") != NULL) 846 opts.randomizeTargets = true; 847 } 848 849 free(mode); 850 } 851 852 static void 853 PrintVar(const char *varname, bool expandVars) 854 { 855 if (strchr(varname, '$') != NULL) { 856 char *evalue = Var_Subst(varname, SCOPE_GLOBAL, VARE_WANTRES); 857 /* TODO: handle errors */ 858 printf("%s\n", evalue); 859 free(evalue); 860 861 } else if (expandVars) { 862 char *expr = str_concat3("${", varname, "}"); 863 char *evalue = Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES); 864 /* TODO: handle errors */ 865 free(expr); 866 printf("%s\n", evalue); 867 free(evalue); 868 869 } else { 870 FStr value = Var_Value(SCOPE_GLOBAL, varname); 871 printf("%s\n", value.str != NULL ? value.str : ""); 872 FStr_Done(&value); 873 } 874 } 875 876 /* 877 * Return a bool based on a variable. 878 * 879 * If the knob is not set, return the fallback. 880 * If set, anything that looks or smells like "No", "False", "Off", "0", etc. 881 * is false, otherwise true. 882 */ 883 bool 884 GetBooleanExpr(const char *expr, bool fallback) 885 { 886 char *value; 887 bool res; 888 889 value = Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES); 890 /* TODO: handle errors */ 891 res = ParseBoolean(value, fallback); 892 free(value); 893 return res; 894 } 895 896 static void 897 doPrintVars(void) 898 { 899 StringListNode *ln; 900 bool expandVars; 901 902 if (opts.printVars == PVM_EXPANDED) 903 expandVars = true; 904 else if (opts.debugVflag) 905 expandVars = false; 906 else 907 expandVars = GetBooleanExpr("${.MAKE.EXPAND_VARIABLES}", 908 false); 909 910 for (ln = opts.variables.first; ln != NULL; ln = ln->next) { 911 const char *varname = ln->datum; 912 PrintVar(varname, expandVars); 913 } 914 } 915 916 static bool 917 runTargets(void) 918 { 919 GNodeList targs = LST_INIT; /* target nodes to create */ 920 bool outOfDate; /* false if all targets up to date */ 921 922 /* 923 * Have now read the entire graph and need to make a list of 924 * targets to create. If none was given on the command line, 925 * we consult the parsing module to find the main target(s) 926 * to create. 927 */ 928 if (Lst_IsEmpty(&opts.create)) 929 Parse_MainName(&targs); 930 else 931 Targ_FindList(&targs, &opts.create); 932 933 if (!opts.compatMake) { 934 /* 935 * Initialize job module before traversing the graph 936 * now that any .BEGIN and .END targets have been read. 937 * This is done only if the -q flag wasn't given 938 * (to prevent the .BEGIN from being executed should 939 * it exist). 940 */ 941 if (!opts.query) { 942 Job_Init(); 943 jobsRunning = true; 944 } 945 946 /* Traverse the graph, checking on all the targets */ 947 outOfDate = Make_Run(&targs); 948 } else { 949 Compat_MakeAll(&targs); 950 outOfDate = false; 951 } 952 Lst_Done(&targs); /* Don't free the targets themselves. */ 953 return outOfDate; 954 } 955 956 /* 957 * Set up the .TARGETS variable to contain the list of targets to be created. 958 * If none specified, make the variable empty for now, the parser will fill 959 * in the default or .MAIN target later. 960 */ 961 static void 962 InitVarTargets(void) 963 { 964 StringListNode *ln; 965 966 if (Lst_IsEmpty(&opts.create)) { 967 Global_Set(".TARGETS", ""); 968 return; 969 } 970 971 for (ln = opts.create.first; ln != NULL; ln = ln->next) { 972 const char *name = ln->datum; 973 Global_Append(".TARGETS", name); 974 } 975 } 976 977 static void 978 InitRandom(void) 979 { 980 struct timeval tv; 981 982 gettimeofday(&tv, NULL); 983 srandom((unsigned int)(tv.tv_sec + tv.tv_usec)); 984 } 985 986 static const char * 987 InitVarMachine(const struct utsname *utsname MAKE_ATTR_UNUSED) 988 { 989 #ifdef FORCE_MACHINE 990 return FORCE_MACHINE; 991 #else 992 const char *machine = getenv("MACHINE"); 993 994 if (machine != NULL) 995 return machine; 996 997 #if defined(MAKE_NATIVE) 998 return utsname->machine; 999 #elif defined(MAKE_MACHINE) 1000 return MAKE_MACHINE; 1001 #else 1002 return "unknown"; 1003 #endif 1004 #endif 1005 } 1006 1007 static const char * 1008 InitVarMachineArch(void) 1009 { 1010 #ifdef FORCE_MACHINE_ARCH 1011 return FORCE_MACHINE_ARCH; 1012 #else 1013 const char *env = getenv("MACHINE_ARCH"); 1014 if (env != NULL) 1015 return env; 1016 1017 #if defined(MAKE_NATIVE) && defined(CTL_HW) 1018 { 1019 struct utsname utsname; 1020 static char machine_arch_buf[sizeof utsname.machine]; 1021 const int mib[2] = { CTL_HW, HW_MACHINE_ARCH }; 1022 size_t len = sizeof machine_arch_buf; 1023 1024 if (sysctl(mib, (unsigned int)__arraycount(mib), 1025 machine_arch_buf, &len, NULL, 0) < 0) { 1026 (void)fprintf(stderr, "%s: sysctl failed (%s).\n", 1027 progname, strerror(errno)); 1028 exit(2); 1029 } 1030 1031 return machine_arch_buf; 1032 } 1033 #elif defined(MACHINE_ARCH) 1034 return MACHINE_ARCH; 1035 #elif defined(MAKE_MACHINE_ARCH) 1036 return MAKE_MACHINE_ARCH; 1037 #else 1038 return "unknown"; 1039 #endif 1040 #endif 1041 } 1042 1043 #ifndef NO_PWD_OVERRIDE 1044 /* 1045 * All this code is so that we know where we are when we start up 1046 * on a different machine with pmake. 1047 * 1048 * XXX: Make no longer has "local" and "remote" mode. Is this code still 1049 * necessary? 1050 * 1051 * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX 1052 * since the value of curdir can vary depending on how we got 1053 * here. Ie sitting at a shell prompt (shell that provides $PWD) 1054 * or via subdir.mk in which case its likely a shell which does 1055 * not provide it. 1056 * 1057 * So, to stop it breaking this case only, we ignore PWD if 1058 * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a variable expression. 1059 */ 1060 static void 1061 HandlePWD(const struct stat *curdir_st) 1062 { 1063 char *pwd; 1064 FStr makeobjdir; 1065 struct stat pwd_st; 1066 1067 if (ignorePWD || (pwd = getenv("PWD")) == NULL) 1068 return; 1069 1070 if (Var_Exists(SCOPE_CMDLINE, "MAKEOBJDIRPREFIX")) 1071 return; 1072 1073 makeobjdir = Var_Value(SCOPE_CMDLINE, "MAKEOBJDIR"); 1074 if (makeobjdir.str != NULL && strchr(makeobjdir.str, '$') != NULL) 1075 goto ignore_pwd; 1076 1077 if (stat(pwd, &pwd_st) == 0 && 1078 curdir_st->st_ino == pwd_st.st_ino && 1079 curdir_st->st_dev == pwd_st.st_dev) 1080 (void)strncpy(curdir, pwd, MAXPATHLEN); 1081 1082 ignore_pwd: 1083 FStr_Done(&makeobjdir); 1084 } 1085 #endif 1086 1087 /* 1088 * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that, MAKEOBJDIR is set 1089 * in the environment, try only that value and fall back to .CURDIR if it 1090 * does not exist. 1091 * 1092 * Otherwise, try _PATH_OBJDIR.MACHINE-MACHINE_ARCH, _PATH_OBJDIR.MACHINE, 1093 * and finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none of these 1094 * paths exist, just use .CURDIR. 1095 */ 1096 static void 1097 InitObjdir(const char *machine, const char *machine_arch) 1098 { 1099 bool writable; 1100 1101 Dir_InitCur(curdir); 1102 writable = GetBooleanExpr("${MAKE_OBJDIR_CHECK_WRITABLE}", true); 1103 (void)Main_SetObjdir(false, "%s", curdir); 1104 1105 if (!SetVarObjdir(writable, "MAKEOBJDIRPREFIX", curdir) && 1106 !SetVarObjdir(writable, "MAKEOBJDIR", "") && 1107 !Main_SetObjdir(writable, "%s.%s-%s", _PATH_OBJDIR, machine, machine_arch) && 1108 !Main_SetObjdir(writable, "%s.%s", _PATH_OBJDIR, machine) && 1109 !Main_SetObjdir(writable, "%s", _PATH_OBJDIR)) 1110 (void)Main_SetObjdir(writable, "%s%s", _PATH_OBJDIRPREFIX, curdir); 1111 } 1112 1113 /* get rid of resource limit on file descriptors */ 1114 static void 1115 UnlimitFiles(void) 1116 { 1117 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 1118 struct rlimit rl; 1119 if (getrlimit(RLIMIT_NOFILE, &rl) != -1 && 1120 rl.rlim_cur != rl.rlim_max) { 1121 #ifdef BMAKE_NOFILE_MAX 1122 if (BMAKE_NOFILE_MAX < rl.rlim_max) 1123 rl.rlim_cur = BMAKE_NOFILE_MAX; 1124 else 1125 #endif 1126 rl.rlim_cur = rl.rlim_max; 1127 (void)setrlimit(RLIMIT_NOFILE, &rl); 1128 } 1129 #endif 1130 } 1131 1132 static void 1133 CmdOpts_Init(void) 1134 { 1135 opts.compatMake = false; 1136 memset(&opts.debug, 0, sizeof(opts.debug)); 1137 /* opts.debug_file has already been initialized earlier */ 1138 opts.strict = false; 1139 opts.debugVflag = false; 1140 opts.checkEnvFirst = false; 1141 Lst_Init(&opts.makefiles); 1142 opts.ignoreErrors = false; /* Pay attention to non-zero returns */ 1143 opts.maxJobs = 1; 1144 opts.keepgoing = false; /* Stop on error */ 1145 opts.noRecursiveExecute = false; /* Execute all .MAKE targets */ 1146 opts.noExecute = false; /* Execute all commands */ 1147 opts.query = false; 1148 opts.noBuiltins = false; /* Read the built-in rules */ 1149 opts.silent = false; /* Print commands as executed */ 1150 opts.touch = false; 1151 opts.printVars = PVM_NONE; 1152 Lst_Init(&opts.variables); 1153 opts.parseWarnFatal = false; 1154 opts.enterFlag = false; 1155 opts.varNoExportEnv = false; 1156 Lst_Init(&opts.create); 1157 } 1158 1159 /* 1160 * Initialize MAKE and .MAKE to the path of the executable, so that it can be 1161 * found by execvp(3) and the shells, even after a chdir. 1162 * 1163 * If it's a relative path and contains a '/', resolve it to an absolute path. 1164 * Otherwise keep it as is, assuming it will be found in the PATH. 1165 */ 1166 static void 1167 InitVarMake(const char *argv0) 1168 { 1169 const char *make = argv0; 1170 1171 if (argv0[0] != '/' && strchr(argv0, '/') != NULL) { 1172 char pathbuf[MAXPATHLEN]; 1173 const char *abspath = cached_realpath(argv0, pathbuf); 1174 struct stat st; 1175 if (abspath != NULL && abspath[0] == '/' && 1176 stat(make, &st) == 0) 1177 make = abspath; 1178 } 1179 1180 Global_Set("MAKE", make); 1181 Global_Set(".MAKE", make); 1182 } 1183 1184 /* 1185 * Add the directories from the colon-separated syspath to defSysIncPath. 1186 * After returning, the contents of syspath is unspecified. 1187 */ 1188 static void 1189 InitDefSysIncPath(char *syspath) 1190 { 1191 static char defsyspath[] = _PATH_DEFSYSPATH; 1192 char *start, *cp; 1193 1194 /* 1195 * If no user-supplied system path was given (through the -m option) 1196 * add the directories from the DEFSYSPATH (more than one may be given 1197 * as dir1:...:dirn) to the system include path. 1198 */ 1199 if (syspath == NULL || syspath[0] == '\0') 1200 syspath = defsyspath; 1201 else 1202 syspath = bmake_strdup(syspath); 1203 1204 for (start = syspath; *start != '\0'; start = cp) { 1205 for (cp = start; *cp != '\0' && *cp != ':'; cp++) 1206 continue; 1207 if (*cp == ':') 1208 *cp++ = '\0'; 1209 1210 /* look for magic parent directory search string */ 1211 if (strncmp(start, ".../", 4) == 0) { 1212 char *dir = Dir_FindHereOrAbove(curdir, start + 4); 1213 if (dir != NULL) { 1214 (void)SearchPath_Add(defSysIncPath, dir); 1215 free(dir); 1216 } 1217 } else { 1218 (void)SearchPath_Add(defSysIncPath, start); 1219 } 1220 } 1221 1222 if (syspath != defsyspath) 1223 free(syspath); 1224 } 1225 1226 static void 1227 ReadBuiltinRules(void) 1228 { 1229 StringListNode *ln; 1230 StringList sysMkFiles = LST_INIT; 1231 1232 SearchPath_Expand( 1233 Lst_IsEmpty(&sysIncPath->dirs) ? defSysIncPath : sysIncPath, 1234 _PATH_DEFSYSMK, 1235 &sysMkFiles); 1236 if (Lst_IsEmpty(&sysMkFiles)) 1237 Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK); 1238 1239 for (ln = sysMkFiles.first; ln != NULL; ln = ln->next) 1240 if (ReadMakefile(ln->datum)) 1241 break; 1242 1243 if (ln == NULL) 1244 Fatal("%s: cannot open %s.", 1245 progname, (const char *)sysMkFiles.first->datum); 1246 1247 Lst_DoneCall(&sysMkFiles, free); 1248 } 1249 1250 static void 1251 InitMaxJobs(void) 1252 { 1253 char *value; 1254 int n; 1255 1256 if (forceJobs || opts.compatMake || 1257 !Var_Exists(SCOPE_GLOBAL, ".MAKE.JOBS")) 1258 return; 1259 1260 value = Var_Subst("${.MAKE.JOBS}", SCOPE_GLOBAL, VARE_WANTRES); 1261 /* TODO: handle errors */ 1262 n = (int)strtol(value, NULL, 0); 1263 if (n < 1) { 1264 (void)fprintf(stderr, 1265 "%s: illegal value for .MAKE.JOBS " 1266 "-- must be positive integer!\n", 1267 progname); 1268 exit(2); /* Not 1 so -q can distinguish error */ 1269 } 1270 1271 if (n != opts.maxJobs) { 1272 Global_Append(MAKEFLAGS, "-j"); 1273 Global_Append(MAKEFLAGS, value); 1274 } 1275 1276 opts.maxJobs = n; 1277 maxJobTokens = opts.maxJobs; 1278 forceJobs = true; 1279 free(value); 1280 } 1281 1282 /* 1283 * For compatibility, look at the directories in the VPATH variable 1284 * and add them to the search path, if the variable is defined. The 1285 * variable's value is in the same format as the PATH environment 1286 * variable, i.e. <directory>:<directory>:<directory>... 1287 */ 1288 static void 1289 InitVpath(void) 1290 { 1291 char *vpath, savec, *path; 1292 if (!Var_Exists(SCOPE_CMDLINE, "VPATH")) 1293 return; 1294 1295 vpath = Var_Subst("${VPATH}", SCOPE_CMDLINE, VARE_WANTRES); 1296 /* TODO: handle errors */ 1297 path = vpath; 1298 do { 1299 char *cp; 1300 /* skip to end of directory */ 1301 for (cp = path; *cp != ':' && *cp != '\0'; cp++) 1302 continue; 1303 /* Save terminator character so know when to stop */ 1304 savec = *cp; 1305 *cp = '\0'; 1306 /* Add directory to search path */ 1307 (void)SearchPath_Add(&dirSearchPath, path); 1308 *cp = savec; 1309 path = cp + 1; 1310 } while (savec == ':'); 1311 free(vpath); 1312 } 1313 1314 static void 1315 ReadAllMakefiles(const StringList *makefiles) 1316 { 1317 StringListNode *ln; 1318 1319 for (ln = makefiles->first; ln != NULL; ln = ln->next) { 1320 const char *fname = ln->datum; 1321 if (!ReadMakefile(fname)) 1322 Fatal("%s: cannot open %s.", progname, fname); 1323 } 1324 } 1325 1326 static void 1327 ReadFirstDefaultMakefile(void) 1328 { 1329 StringList makefiles = LST_INIT; 1330 StringListNode *ln; 1331 char *prefs = Var_Subst("${.MAKE.MAKEFILE_PREFERENCE}", 1332 SCOPE_CMDLINE, VARE_WANTRES); 1333 /* TODO: handle errors */ 1334 1335 (void)str2Lst_Append(&makefiles, prefs); 1336 1337 for (ln = makefiles.first; ln != NULL; ln = ln->next) 1338 if (ReadMakefile(ln->datum)) 1339 break; 1340 1341 Lst_Done(&makefiles); 1342 free(prefs); 1343 } 1344 1345 /* 1346 * Initialize variables such as MAKE, MACHINE, .MAKEFLAGS. 1347 * Initialize a few modules. 1348 * Parse the arguments from MAKEFLAGS and the command line. 1349 */ 1350 static void 1351 main_Init(int argc, char **argv) 1352 { 1353 struct stat sa; 1354 const char *machine; 1355 const char *machine_arch; 1356 char *syspath = getenv("MAKESYSPATH"); 1357 struct utsname utsname; 1358 1359 /* default to writing debug to stderr */ 1360 opts.debug_file = stderr; 1361 1362 Str_Intern_Init(); 1363 HashTable_Init(&cached_realpaths); 1364 1365 #ifdef SIGINFO 1366 (void)bmake_signal(SIGINFO, siginfo); 1367 #endif 1368 1369 InitRandom(); 1370 1371 progname = str_basename(argv[0]); 1372 1373 UnlimitFiles(); 1374 1375 if (uname(&utsname) == -1) { 1376 (void)fprintf(stderr, "%s: uname failed (%s).\n", progname, 1377 strerror(errno)); 1378 exit(2); 1379 } 1380 1381 /* 1382 * Get the name of this type of MACHINE from utsname 1383 * so we can share an executable for similar machines. 1384 * (i.e. m68k: amiga hp300, mac68k, sun3, ...) 1385 * 1386 * Note that both MACHINE and MACHINE_ARCH are decided at 1387 * run-time. 1388 */ 1389 machine = InitVarMachine(&utsname); 1390 machine_arch = InitVarMachineArch(); 1391 1392 myPid = getpid(); /* remember this for vFork() */ 1393 1394 /* 1395 * Just in case MAKEOBJDIR wants us to do something tricky. 1396 */ 1397 Targ_Init(); 1398 Var_Init(); 1399 Global_Set_ReadOnly(".MAKE.OS", utsname.sysname); 1400 Global_Set("MACHINE", machine); 1401 Global_Set("MACHINE_ARCH", machine_arch); 1402 #ifdef MAKE_VERSION 1403 Global_Set("MAKE_VERSION", MAKE_VERSION); 1404 #endif 1405 Global_Set_ReadOnly(".newline", "\n"); /* handy for :@ loops */ 1406 #ifndef MAKEFILE_PREFERENCE_LIST 1407 /* This is the traditional preference for makefiles. */ 1408 # define MAKEFILE_PREFERENCE_LIST "makefile Makefile" 1409 #endif 1410 Global_Set(".MAKE.MAKEFILE_PREFERENCE", MAKEFILE_PREFERENCE_LIST); 1411 Global_Set(".MAKE.DEPENDFILE", ".depend"); 1412 /* Tell makefiles like jobs.mk whether we support -jC */ 1413 #ifdef _SC_NPROCESSORS_ONLN 1414 Global_Set_ReadOnly(".MAKE.JOBS.C", "yes"); 1415 #else 1416 Global_Set_ReadOnly(".MAKE.JOBS.C", "no"); 1417 #endif 1418 1419 CmdOpts_Init(); 1420 allPrecious = false; /* Remove targets when interrupted */ 1421 deleteOnError = false; /* Historical default behavior */ 1422 jobsRunning = false; 1423 1424 maxJobTokens = opts.maxJobs; 1425 ignorePWD = false; 1426 1427 /* 1428 * Initialize the parsing, directory and variable modules to prepare 1429 * for the reading of inclusion paths and variable settings on the 1430 * command line 1431 */ 1432 1433 /* 1434 * Initialize various variables. 1435 * MAKE also gets this name, for compatibility 1436 * .MAKEFLAGS gets set to the empty string just in case. 1437 * MFLAGS also gets initialized empty, for compatibility. 1438 */ 1439 Parse_Init(); 1440 InitVarMake(argv[0]); 1441 Global_Set(MAKEFLAGS, ""); 1442 Global_Set(".MAKEOVERRIDES", ""); 1443 Global_Set("MFLAGS", ""); 1444 Global_Set(".ALLTARGETS", ""); 1445 Var_Set(SCOPE_CMDLINE, ".MAKE.LEVEL.ENV", MAKE_LEVEL_ENV); 1446 1447 /* Set some other useful variables. */ 1448 { 1449 char buf[64], *ep = getenv(MAKE_LEVEL_ENV); 1450 1451 makelevel = ep != NULL && ep[0] != '\0' ? atoi(ep) : 0; 1452 if (makelevel < 0) 1453 makelevel = 0; 1454 snprintf(buf, sizeof buf, "%d", makelevel); 1455 Global_Set(".MAKE.LEVEL", buf); 1456 snprintf(buf, sizeof buf, "%u", myPid); 1457 Global_Set_ReadOnly(".MAKE.PID", buf); 1458 snprintf(buf, sizeof buf, "%u", getppid()); 1459 Global_Set_ReadOnly(".MAKE.PPID", buf); 1460 snprintf(buf, sizeof buf, "%u", getuid()); 1461 Global_Set_ReadOnly(".MAKE.UID", buf); 1462 snprintf(buf, sizeof buf, "%u", getgid()); 1463 Global_Set_ReadOnly(".MAKE.GID", buf); 1464 } 1465 if (makelevel > 0) { 1466 char pn[1024]; 1467 snprintf(pn, sizeof pn, "%s[%d]", progname, makelevel); 1468 progname = bmake_strdup(pn); 1469 } 1470 1471 #ifdef USE_META 1472 meta_init(); 1473 #endif 1474 Dir_Init(); 1475 1476 #ifdef POSIX 1477 { 1478 char *makeflags = explode(getenv("MAKEFLAGS")); 1479 Main_ParseArgLine(makeflags); 1480 free(makeflags); 1481 } 1482 #else 1483 /* 1484 * First snag any flags out of the MAKE environment variable. 1485 * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's 1486 * in a different format). 1487 */ 1488 Main_ParseArgLine(getenv("MAKE")); 1489 #endif 1490 1491 if (getcwd(curdir, MAXPATHLEN) == NULL) { 1492 (void)fprintf(stderr, "%s: getcwd: %s.\n", 1493 progname, strerror(errno)); 1494 exit(2); 1495 } 1496 1497 MainParseArgs(argc, argv); 1498 1499 if (opts.enterFlag) 1500 printf("%s: Entering directory `%s'\n", progname, curdir); 1501 1502 if (stat(curdir, &sa) == -1) { 1503 (void)fprintf(stderr, "%s: %s: %s.\n", 1504 progname, curdir, strerror(errno)); 1505 exit(2); 1506 } 1507 1508 #ifndef NO_PWD_OVERRIDE 1509 HandlePWD(&sa); 1510 #endif 1511 Global_Set(".CURDIR", curdir); 1512 1513 InitObjdir(machine, machine_arch); 1514 1515 Arch_Init(); 1516 Suff_Init(); 1517 Trace_Init(tracefile); 1518 1519 defaultNode = NULL; 1520 (void)time(&now); 1521 1522 Trace_Log(MAKESTART, NULL); 1523 1524 InitVarTargets(); 1525 1526 InitDefSysIncPath(syspath); 1527 } 1528 1529 /* 1530 * Read the system makefile followed by either makefile, Makefile or the 1531 * files given by the -f option. Exit on parse errors. 1532 */ 1533 static void 1534 main_ReadFiles(void) 1535 { 1536 1537 if (Lst_IsEmpty(&sysIncPath->dirs)) 1538 SearchPath_AddAll(sysIncPath, defSysIncPath); 1539 1540 Dir_SetSYSPATH(); 1541 if (!opts.noBuiltins) 1542 ReadBuiltinRules(); 1543 1544 posix_state = PS_MAYBE_NEXT_LINE; 1545 if (!Lst_IsEmpty(&opts.makefiles)) 1546 ReadAllMakefiles(&opts.makefiles); 1547 else 1548 ReadFirstDefaultMakefile(); 1549 } 1550 1551 /* Compute the dependency graph. */ 1552 static void 1553 main_PrepareMaking(void) 1554 { 1555 /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ 1556 if (!opts.noBuiltins || opts.printVars == PVM_NONE) { 1557 makeDependfile = Var_Subst("${.MAKE.DEPENDFILE}", 1558 SCOPE_CMDLINE, VARE_WANTRES); 1559 if (makeDependfile[0] != '\0') { 1560 /* TODO: handle errors */ 1561 doing_depend = true; 1562 (void)ReadMakefile(makeDependfile); 1563 doing_depend = false; 1564 } 1565 } 1566 1567 if (enterFlagObj) 1568 printf("%s: Entering directory `%s'\n", progname, objdir); 1569 1570 MakeMode(); 1571 1572 { 1573 FStr makeflags = Var_Value(SCOPE_GLOBAL, MAKEFLAGS); 1574 Global_Append("MFLAGS", makeflags.str); 1575 FStr_Done(&makeflags); 1576 } 1577 1578 InitMaxJobs(); 1579 1580 if (!opts.compatMake && !forceJobs) 1581 opts.compatMake = true; 1582 1583 if (!opts.compatMake) 1584 Job_ServerStart(maxJobTokens, jp_0, jp_1); 1585 DEBUG5(JOB, "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n", 1586 jp_0, jp_1, opts.maxJobs, maxJobTokens, opts.compatMake ? 1 : 0); 1587 1588 if (opts.printVars == PVM_NONE) 1589 Main_ExportMAKEFLAGS(true); /* initial export */ 1590 1591 InitVpath(); 1592 1593 /* 1594 * Now that all search paths have been read for suffixes et al, it's 1595 * time to add the default search path to their lists... 1596 */ 1597 Suff_ExtendPaths(); 1598 1599 /* 1600 * Propagate attributes through :: dependency lists. 1601 */ 1602 Targ_Propagate(); 1603 1604 /* print the initial graph, if the user requested it */ 1605 if (DEBUG(GRAPH1)) 1606 Targ_PrintGraph(1); 1607 } 1608 1609 /* 1610 * Make the targets. 1611 * If the -v or -V options are given, print variables instead. 1612 * Return whether any of the targets is out-of-date. 1613 */ 1614 static bool 1615 main_Run(void) 1616 { 1617 if (opts.printVars != PVM_NONE) { 1618 /* print the values of any variables requested by the user */ 1619 doPrintVars(); 1620 return false; 1621 } else { 1622 return runTargets(); 1623 } 1624 } 1625 1626 /* Clean up after making the targets. */ 1627 static void 1628 main_CleanUp(void) 1629 { 1630 #ifdef CLEANUP 1631 Lst_DoneCall(&opts.variables, free); 1632 Lst_DoneCall(&opts.makefiles, free); 1633 Lst_DoneCall(&opts.create, free); 1634 #endif 1635 1636 if (DEBUG(GRAPH2)) 1637 Targ_PrintGraph(2); 1638 1639 Trace_Log(MAKEEND, NULL); 1640 1641 if (enterFlagObj) 1642 printf("%s: Leaving directory `%s'\n", progname, objdir); 1643 if (opts.enterFlag) 1644 printf("%s: Leaving directory `%s'\n", progname, curdir); 1645 1646 #ifdef USE_META 1647 meta_finish(); 1648 #endif 1649 Suff_End(); 1650 Targ_End(); 1651 Arch_End(); 1652 Var_End(); 1653 Parse_End(); 1654 Dir_End(); 1655 Job_End(); 1656 Trace_End(); 1657 Str_Intern_End(); 1658 } 1659 1660 /* Determine the exit code. */ 1661 static int 1662 main_Exit(bool outOfDate) 1663 { 1664 if (opts.strict && (main_errors > 0 || Parse_NumErrors() > 0)) 1665 return 2; /* Not 1 so -q can distinguish error */ 1666 return outOfDate ? 1 : 0; 1667 } 1668 1669 int 1670 main(int argc, char **argv) 1671 { 1672 bool outOfDate; 1673 1674 main_Init(argc, argv); 1675 main_ReadFiles(); 1676 main_PrepareMaking(); 1677 outOfDate = main_Run(); 1678 main_CleanUp(); 1679 return main_Exit(outOfDate); 1680 } 1681 1682 /* 1683 * Open and parse the given makefile, with all its side effects. 1684 * Return false if the file could not be opened. 1685 */ 1686 static bool 1687 ReadMakefile(const char *fname) 1688 { 1689 int fd; 1690 char *name, *path = NULL; 1691 1692 if (strcmp(fname, "-") == 0) { 1693 Parse_File("(stdin)", -1); 1694 Var_Set(SCOPE_INTERNAL, "MAKEFILE", ""); 1695 } else { 1696 /* if we've chdir'd, rebuild the path name */ 1697 if (strcmp(curdir, objdir) != 0 && *fname != '/') { 1698 path = str_concat3(curdir, "/", fname); 1699 fd = open(path, O_RDONLY); 1700 if (fd != -1) { 1701 fname = path; 1702 goto found; 1703 } 1704 free(path); 1705 1706 /* If curdir failed, try objdir (ala .depend) */ 1707 path = str_concat3(objdir, "/", fname); 1708 fd = open(path, O_RDONLY); 1709 if (fd != -1) { 1710 fname = path; 1711 goto found; 1712 } 1713 } else { 1714 fd = open(fname, O_RDONLY); 1715 if (fd != -1) 1716 goto found; 1717 } 1718 /* look in -I and system include directories. */ 1719 name = Dir_FindFile(fname, parseIncPath); 1720 if (name == NULL) { 1721 SearchPath *sysInc = Lst_IsEmpty(&sysIncPath->dirs) 1722 ? defSysIncPath : sysIncPath; 1723 name = Dir_FindFile(fname, sysInc); 1724 } 1725 if (name == NULL || (fd = open(name, O_RDONLY)) == -1) { 1726 free(name); 1727 free(path); 1728 return false; 1729 } 1730 fname = name; 1731 /* 1732 * set the MAKEFILE variable desired by System V fans -- the 1733 * placement of the setting here means it gets set to the last 1734 * makefile specified, as it is set by SysV make. 1735 */ 1736 found: 1737 if (!doing_depend) 1738 Var_Set(SCOPE_INTERNAL, "MAKEFILE", fname); 1739 Parse_File(fname, fd); 1740 } 1741 free(path); 1742 return true; 1743 } 1744 1745 /* 1746 * Execute the command in cmd, and return its output (only stdout, not 1747 * stderr, possibly empty). In the output, replace newlines with spaces. 1748 */ 1749 char * 1750 Cmd_Exec(const char *cmd, char **error) 1751 { 1752 const char *args[4]; /* Arguments for invoking the shell */ 1753 int pipefds[2]; 1754 int cpid; /* Child PID */ 1755 int pid; /* PID from wait() */ 1756 int status; /* command exit status */ 1757 Buffer buf; /* buffer to store the result */ 1758 ssize_t bytes_read; 1759 char *output; 1760 char *cp; 1761 int saved_errno; 1762 1763 if (shellName == NULL) 1764 Shell_Init(); 1765 1766 args[0] = shellName; 1767 args[1] = "-c"; 1768 args[2] = cmd; 1769 args[3] = NULL; 1770 DEBUG1(VAR, "Capturing the output of command \"%s\"\n", cmd); 1771 1772 if (pipe(pipefds) == -1) { 1773 *error = str_concat3( 1774 "Couldn't create pipe for \"", cmd, "\""); 1775 return bmake_strdup(""); 1776 } 1777 1778 Var_ReexportVars(); 1779 1780 switch (cpid = vfork()) { 1781 case 0: 1782 (void)close(pipefds[0]); 1783 (void)dup2(pipefds[1], STDOUT_FILENO); 1784 (void)close(pipefds[1]); 1785 1786 (void)execv(shellPath, UNCONST(args)); 1787 _exit(1); 1788 /* NOTREACHED */ 1789 1790 case -1: 1791 *error = str_concat3("Couldn't exec \"", cmd, "\""); 1792 return bmake_strdup(""); 1793 } 1794 1795 (void)close(pipefds[1]); /* No need for the writing half */ 1796 1797 saved_errno = 0; 1798 Buf_Init(&buf); 1799 1800 do { 1801 char result[BUFSIZ]; 1802 bytes_read = read(pipefds[0], result, sizeof result); 1803 if (bytes_read > 0) 1804 Buf_AddBytes(&buf, result, (size_t)bytes_read); 1805 } while (bytes_read > 0 || (bytes_read == -1 && errno == EINTR)); 1806 if (bytes_read == -1) 1807 saved_errno = errno; 1808 1809 (void)close(pipefds[0]); /* Close the input side of the pipe. */ 1810 1811 while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0) 1812 JobReapChild(pid, status, false); 1813 1814 if (Buf_EndsWith(&buf, '\n')) 1815 buf.data[buf.len - 1] = '\0'; 1816 1817 output = Buf_DoneData(&buf); 1818 for (cp = output; *cp != '\0'; cp++) 1819 if (*cp == '\n') 1820 *cp = ' '; 1821 1822 if (WIFSIGNALED(status)) 1823 *error = str_concat3("\"", cmd, "\" exited on a signal"); 1824 else if (WEXITSTATUS(status) != 0) 1825 *error = str_concat3( 1826 "\"", cmd, "\" returned non-zero status"); 1827 else if (saved_errno != 0) 1828 *error = str_concat3( 1829 "Couldn't read shell's output for \"", cmd, "\""); 1830 else 1831 *error = NULL; 1832 return output; 1833 } 1834 1835 /* 1836 * Print a printf-style error message. 1837 * 1838 * In default mode, this error message has no consequences, for compatibility 1839 * reasons, in particular it does not affect the exit status. Only in lint 1840 * mode (-dL) it does. 1841 */ 1842 void 1843 Error(const char *fmt, ...) 1844 { 1845 va_list ap; 1846 FILE *f; 1847 1848 f = opts.debug_file; 1849 if (f == stdout) 1850 f = stderr; 1851 (void)fflush(stdout); 1852 1853 for (;;) { 1854 fprintf(f, "%s: ", progname); 1855 va_start(ap, fmt); 1856 (void)vfprintf(f, fmt, ap); 1857 va_end(ap); 1858 (void)fprintf(f, "\n"); 1859 (void)fflush(f); 1860 if (f == stderr) 1861 break; 1862 f = stderr; 1863 } 1864 main_errors++; 1865 } 1866 1867 /* 1868 * Wait for any running jobs to finish, then produce an error message, 1869 * finally exit immediately. 1870 * 1871 * Exiting immediately differs from Parse_Error, which exits only after the 1872 * current top-level makefile has been parsed completely. 1873 */ 1874 void 1875 Fatal(const char *fmt, ...) 1876 { 1877 va_list ap; 1878 1879 if (jobsRunning) 1880 Job_Wait(); 1881 1882 (void)fflush(stdout); 1883 va_start(ap, fmt); 1884 (void)vfprintf(stderr, fmt, ap); 1885 va_end(ap); 1886 (void)fprintf(stderr, "\n"); 1887 (void)fflush(stderr); 1888 PrintStackTrace(true); 1889 1890 PrintOnError(NULL, "\n"); 1891 1892 if (DEBUG(GRAPH2) || DEBUG(GRAPH3)) 1893 Targ_PrintGraph(2); 1894 Trace_Log(MAKEERROR, NULL); 1895 exit(2); /* Not 1 so -q can distinguish error */ 1896 } 1897 1898 /* 1899 * Major exception once jobs are being created. 1900 * Kills all jobs, prints a message and exits. 1901 */ 1902 void 1903 Punt(const char *fmt, ...) 1904 { 1905 va_list ap; 1906 1907 (void)fflush(stdout); 1908 (void)fprintf(stderr, "%s: ", progname); 1909 va_start(ap, fmt); 1910 (void)vfprintf(stderr, fmt, ap); 1911 va_end(ap); 1912 (void)fprintf(stderr, "\n"); 1913 (void)fflush(stderr); 1914 1915 PrintOnError(NULL, "\n"); 1916 1917 DieHorribly(); 1918 } 1919 1920 /* Exit without giving a message. */ 1921 void 1922 DieHorribly(void) 1923 { 1924 if (jobsRunning) 1925 Job_AbortAll(); 1926 if (DEBUG(GRAPH2)) 1927 Targ_PrintGraph(2); 1928 Trace_Log(MAKEERROR, NULL); 1929 exit(2); /* Not 1 so -q can distinguish error */ 1930 } 1931 1932 /* 1933 * Called when aborting due to errors in child shell to signal abnormal exit. 1934 * The program exits. 1935 * Errors is the number of errors encountered in Make_Make. 1936 */ 1937 void 1938 Finish(int errs) 1939 { 1940 if (shouldDieQuietly(NULL, -1)) 1941 exit(2); 1942 Fatal("%d error%s", errs, errs == 1 ? "" : "s"); 1943 } 1944 1945 int 1946 unlink_file(const char *file) 1947 { 1948 struct stat st; 1949 1950 if (lstat(file, &st) == -1) 1951 return -1; 1952 1953 if (S_ISDIR(st.st_mode)) { 1954 /* 1955 * POSIX says for unlink: "The path argument shall not name 1956 * a directory unless [...]". 1957 */ 1958 errno = EISDIR; 1959 return -1; 1960 } 1961 return unlink(file); 1962 } 1963 1964 static void 1965 write_all(int fd, const void *data, size_t n) 1966 { 1967 const char *mem = data; 1968 1969 while (n > 0) { 1970 ssize_t written = write(fd, mem, n); 1971 /* XXX: Should this EAGAIN be EINTR? */ 1972 if (written == -1 && errno == EAGAIN) 1973 continue; 1974 if (written == -1) 1975 break; 1976 mem += written; 1977 n -= (size_t)written; 1978 } 1979 } 1980 1981 /* Print why exec failed, avoiding stdio. */ 1982 void MAKE_ATTR_DEAD 1983 execDie(const char *af, const char *av) 1984 { 1985 Buffer buf; 1986 1987 Buf_Init(&buf); 1988 Buf_AddStr(&buf, progname); 1989 Buf_AddStr(&buf, ": "); 1990 Buf_AddStr(&buf, af); 1991 Buf_AddStr(&buf, "("); 1992 Buf_AddStr(&buf, av); 1993 Buf_AddStr(&buf, ") failed ("); 1994 Buf_AddStr(&buf, strerror(errno)); 1995 Buf_AddStr(&buf, ")\n"); 1996 1997 write_all(STDERR_FILENO, buf.data, buf.len); 1998 1999 Buf_Done(&buf); 2000 _exit(1); 2001 } 2002 2003 static void 2004 purge_relative_cached_realpaths(void) 2005 { 2006 HashEntry *he, *nhe; 2007 HashIter hi; 2008 2009 HashIter_Init(&hi, &cached_realpaths); 2010 he = HashIter_Next(&hi); 2011 while (he != NULL) { 2012 nhe = HashIter_Next(&hi); 2013 if (he->key[0] != '/') { 2014 DEBUG1(DIR, "cached_realpath: purging %s\n", he->key); 2015 HashTable_DeleteEntry(&cached_realpaths, he); 2016 /* 2017 * XXX: What about the allocated he->value? Either 2018 * free them or document why they cannot be freed. 2019 */ 2020 } 2021 he = nhe; 2022 } 2023 } 2024 2025 const char * 2026 cached_realpath(const char *pathname, char *resolved) 2027 { 2028 const char *rp; 2029 2030 if (pathname == NULL || pathname[0] == '\0') 2031 return NULL; 2032 2033 rp = HashTable_FindValue(&cached_realpaths, pathname); 2034 if (rp != NULL) { 2035 /* a hit */ 2036 strncpy(resolved, rp, MAXPATHLEN); 2037 resolved[MAXPATHLEN - 1] = '\0'; 2038 return resolved; 2039 } 2040 2041 rp = realpath(pathname, resolved); 2042 if (rp != NULL) { 2043 HashTable_Set(&cached_realpaths, pathname, bmake_strdup(rp)); 2044 DEBUG2(DIR, "cached_realpath: %s -> %s\n", pathname, rp); 2045 return resolved; 2046 } 2047 2048 /* should we negative-cache? */ 2049 return NULL; 2050 } 2051 2052 /* 2053 * Return true if we should die without noise. 2054 * For example our failing child was a sub-make or failure happened elsewhere. 2055 */ 2056 bool 2057 shouldDieQuietly(GNode *gn, int bf) 2058 { 2059 static int quietly = -1; 2060 2061 if (quietly < 0) { 2062 if (DEBUG(JOB) || 2063 !GetBooleanExpr("${.MAKE.DIE_QUIETLY}", true)) 2064 quietly = 0; 2065 else if (bf >= 0) 2066 quietly = bf; 2067 else 2068 quietly = (gn != NULL && (gn->type & OP_MAKE)) ? 1 : 0; 2069 } 2070 return quietly != 0; 2071 } 2072 2073 static void 2074 SetErrorVars(GNode *gn) 2075 { 2076 StringListNode *ln; 2077 2078 /* 2079 * We can print this even if there is no .ERROR target. 2080 */ 2081 Global_Set(".ERROR_TARGET", gn->name); 2082 Global_Delete(".ERROR_CMD"); 2083 2084 for (ln = gn->commands.first; ln != NULL; ln = ln->next) { 2085 const char *cmd = ln->datum; 2086 2087 if (cmd == NULL) 2088 break; 2089 Global_Append(".ERROR_CMD", cmd); 2090 } 2091 } 2092 2093 /* 2094 * Print some helpful information in case of an error. 2095 * The caller should exit soon after calling this function. 2096 */ 2097 void 2098 PrintOnError(GNode *gn, const char *msg) 2099 { 2100 static GNode *errorNode = NULL; 2101 2102 if (DEBUG(HASH)) { 2103 Targ_Stats(); 2104 Var_Stats(); 2105 } 2106 2107 if (errorNode != NULL) 2108 return; /* we've been here! */ 2109 2110 printf("%s%s: stopped in %s\n", msg, progname, curdir); 2111 2112 /* we generally want to keep quiet if a sub-make died */ 2113 if (shouldDieQuietly(gn, -1)) 2114 return; 2115 2116 if (gn != NULL) 2117 SetErrorVars(gn); 2118 2119 { 2120 char *errorVarsValues = Var_Subst( 2121 "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}", 2122 SCOPE_GLOBAL, VARE_WANTRES); 2123 /* TODO: handle errors */ 2124 printf("%s", errorVarsValues); 2125 free(errorVarsValues); 2126 } 2127 2128 fflush(stdout); 2129 2130 /* 2131 * Finally, see if there is a .ERROR target, and run it if so. 2132 */ 2133 errorNode = Targ_FindNode(".ERROR"); 2134 if (errorNode != NULL) { 2135 errorNode->type |= OP_SPECIAL; 2136 Compat_Make(errorNode, errorNode); 2137 } 2138 } 2139 2140 void 2141 Main_ExportMAKEFLAGS(bool first) 2142 { 2143 static bool once = true; 2144 char *flags; 2145 2146 if (once != first) 2147 return; 2148 once = false; 2149 2150 flags = Var_Subst( 2151 "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}", 2152 SCOPE_CMDLINE, VARE_WANTRES); 2153 /* TODO: handle errors */ 2154 if (flags[0] != '\0') { 2155 #ifdef POSIX 2156 setenv("MAKEFLAGS", flags, 1); 2157 #else 2158 setenv("MAKE", flags, 1); 2159 #endif 2160 } 2161 } 2162 2163 char * 2164 getTmpdir(void) 2165 { 2166 static char *tmpdir = NULL; 2167 struct stat st; 2168 2169 if (tmpdir != NULL) 2170 return tmpdir; 2171 2172 /* Honor $TMPDIR if it is valid, strip a trailing '/'. */ 2173 tmpdir = Var_Subst("${TMPDIR:tA:U" _PATH_TMP ":S,/$,,W}/", 2174 SCOPE_GLOBAL, VARE_WANTRES); 2175 /* TODO: handle errors */ 2176 2177 if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) { 2178 free(tmpdir); 2179 tmpdir = bmake_strdup(_PATH_TMP); 2180 } 2181 return tmpdir; 2182 } 2183 2184 /* 2185 * Create and open a temp file using "pattern". 2186 * If out_fname is provided, set it to a copy of the filename created. 2187 * Otherwise unlink the file once open. 2188 */ 2189 int 2190 mkTempFile(const char *pattern, char *tfile, size_t tfile_sz) 2191 { 2192 static char *tmpdir = NULL; 2193 char tbuf[MAXPATHLEN]; 2194 int fd; 2195 2196 if (pattern == NULL) 2197 pattern = TMPPAT; 2198 if (tmpdir == NULL) 2199 tmpdir = getTmpdir(); 2200 if (tfile == NULL) { 2201 tfile = tbuf; 2202 tfile_sz = sizeof tbuf; 2203 } 2204 2205 if (pattern[0] == '/') 2206 snprintf(tfile, tfile_sz, "%s", pattern); 2207 else 2208 snprintf(tfile, tfile_sz, "%s%s", tmpdir, pattern); 2209 2210 if ((fd = mkstemp(tfile)) < 0) 2211 Punt("Could not create temporary file %s: %s", tfile, 2212 strerror(errno)); 2213 if (tfile == tbuf) 2214 unlink(tfile); /* we just want the descriptor */ 2215 2216 return fd; 2217 } 2218 2219 /* 2220 * Convert a string representation of a boolean into a boolean value. 2221 * Anything that looks like "No", "False", "Off", "0" etc. is false, 2222 * the empty string is the fallback, everything else is true. 2223 */ 2224 bool 2225 ParseBoolean(const char *s, bool fallback) 2226 { 2227 char ch = ch_tolower(s[0]); 2228 if (ch == '\0') 2229 return fallback; 2230 if (ch == '0' || ch == 'f' || ch == 'n') 2231 return false; 2232 if (ch == 'o') 2233 return ch_tolower(s[1]) != 'f'; 2234 return true; 2235 } 2236