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