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