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