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