[BACK]Return to main.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / make

File: [local] / src / usr.bin / make / main.c (download)

Revision 1.132, Mon Sep 4 11:35:11 2023 UTC (8 months, 2 weeks ago) by espie
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, HEAD
Changes since 1.131: +2 -3 lines

GC old code that was originally implemented to facilitate adoption by
other BSDs, which never happened, so make things simpler for further
development.

Ditches config.h entirely since it gets reduced to 2 defines.

okay tb@

/*	$OpenBSD: main.c,v 1.132 2023/09/04 11:35:11 espie Exp $ */
/*	$NetBSD: main.c,v 1.34 1997/03/24 20:56:36 gwr Exp $	*/

/*
 * Copyright (c) 1988, 1989, 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 * Copyright (c) 1989 by Berkeley Softworks
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Adam de Boor.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/param.h>	/* MACHINE MACHINE_ARCH */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "defines.h"
#include "var.h"
#include "lowparse.h"
#include "parse.h"
#include "parsevar.h"
#include "dir.h"
#include "direxpand.h"
#include "error.h"
#include "pathnames.h"
#include "init.h"
#include "job.h"
#include "targ.h"
#include "suff.h"
#include "str.h"
#include "main.h"
#include "lst.h"
#include "memory.h"
#include "dump.h"
#include "enginechoice.h"

#define MAKEFLAGS	".MAKEFLAGS"

static LIST		to_create; 	/* Targets to be made */
Lst create = &to_create;
bool 		allPrecious;	/* .PRECIOUS given on line by itself */

static bool	noBuiltins;	/* -r flag */
static LIST	makefiles;	/* ordered list of makefiles to read */
static LIST	varstoprint;	/* list of variables to print */
static int	optj;		/* -j argument */
static bool 	compatMake;	/* -B argument */
static bool	forceJobs = false;
int 		debug;		/* -d flag */
bool 		noExecute;	/* -n flag */
bool 		keepgoing;	/* -k flag */
bool 		queryFlag;	/* -q flag */
bool 		touchFlag;	/* -t flag */
bool 		ignoreErrors;	/* -i flag */
bool 		beSilent;	/* -s flag */
bool		dumpData;	/* -p flag */

static LIST 	unreadable;

struct dirs {
	char *current;
	char *object;
};

static void MainParseArgs(int, char **);
static void add_dirpath(Lst, const char *);
static void usage(void);
static void posixParseOptLetter(int);
static void record_option(int, const char *);

static char *figure_out_MACHINE(void);
static char *figure_out_MACHINE_ARCH(void);
static char *figure_out_MACHINE_CPU(void);

static char *chdir_verify_path(const char *, struct dirs *);
static char *figure_out_CURDIR(void);
static void setup_CURDIR_OBJDIR(struct dirs *);

static void setup_VPATH(void);

static void read_all_make_rules(bool, bool, Lst, struct dirs *);
static void read_makefile_list(Lst, struct dirs *);
static bool ReadMakefile(void *, void *);

static void
record_option(int c, const char *arg)
{
    	char opt[3];

	opt[0] = '-';
	opt[1] = c;
	opt[2] = '\0';
	Var_Append(MAKEFLAGS, opt);
	if (arg != NULL)
		Var_Append(MAKEFLAGS, arg);
}

void
set_notparallel()
{
	compatMake = true;
}

static void
posixParseOptLetter(int c)
{
	switch(c) {
	case 'B':
		compatMake = true;
		return;	/* XXX don't pass to submakes. */
	case 'S':
		keepgoing = false;
		break;
	case 'e':
		Var_setCheckEnvFirst(true);
		break;
	case 'i':
		ignoreErrors = true;
		break;
	case 'k':
		keepgoing = true;
		break;
	case 'n':
		noExecute = true;
		break;
	case 'p':
		dumpData = true;
		break;
	case 'q':
		queryFlag = true;
		/* Kind of nonsensical, wot? */
		break;
	case 'r':
		noBuiltins = true;
		break;
	case 's':
		beSilent = true;
		break;
	case 't':
		touchFlag = true;
		break;
	default:
		usage();
	}
	record_option(c, NULL);
}

/*-
 * MainParseArgs --
 *	Parse a given argument vector. Called from main() and from
 *	Main_ParseArgLine() when the .MAKEFLAGS target is used.
 *
 *	XXX: Deal with command line overriding .MAKEFLAGS in makefile
 *
 * Side Effects:
 *	Various global and local flags will be set depending on the flags
 *	given
 */
static void
MainParseArgs(int argc, char **argv)
{
	int c, optend;

#define OPTFLAGS "BC:D:I:SV:d:ef:ij:km:npqrst"
#define OPTLETTERS "BSiknpqrst"

	if (pledge("stdio rpath wpath cpath fattr proc exec", NULL) == -1)
		err(2, "pledge");

	optind = 1;	/* since we're called more than once */
	optreset = 1;
	optend = 0;
	while (optind < argc) {
		if (!optend && argv[optind][0] == '-') {
			if (argv[optind][1] == '\0')
				optind++;	/* ignore "-" */
			else if (argv[optind][1] == '-' &&
			    argv[optind][2] == '\0') {
				optind++;	/* ignore "--" */
				optend++;	/* "--" denotes end of flags */
			}
		}
		c = optend ? -1 : getopt(argc, argv, OPTFLAGS);
		switch (c) {
		case 'C':
			break;
		case 'D':
			Var_Set(optarg, "1");
			record_option(c, optarg);
			break;
		case 'I':
			Parse_AddIncludeDir(optarg);
			record_option(c, optarg);
			break;
		case 'V':
			Lst_AtEnd(&varstoprint, optarg);
			record_option(c, optarg);
			break;
		case 'd': {
			char *modules = optarg;

			for (; *modules; ++modules)
				switch (*modules) {
				case 'A':
					debug = ~0;
					break;
				case 'a':
					debug |= DEBUG_ARCH;
					break;
				case 'c':
					debug |= DEBUG_COND;
					break;
				case 'd':
					debug |= DEBUG_DIR;
					break;
				case 'D':
					debug |= DEBUG_DOUBLE;
					break;
				case 'e':
					debug |= DEBUG_EXPENSIVE;
					break;
				case 'f':
					debug |= DEBUG_FOR;
					break;
				case 'g':
					if (modules[1] == '1') {
						debug |= DEBUG_GRAPH1;
						++modules;
					}
					else if (modules[1] == '2') {
						debug |= DEBUG_GRAPH2;
						++modules;
					}
					break;
				case 'h':
					debug |= DEBUG_HELDJOBS;
					break;
				case 'j':
					debug |= DEBUG_JOB | DEBUG_KILL;
					break;
				case 'J':
					/* ignore */
					break;
				case 'k':
					debug |= DEBUG_KILL;
					break;
				case 'l':
					debug |= DEBUG_LOUD;
					break;
				case 'm':
					debug |= DEBUG_MAKE;
					break;
				case 'n':
					debug |= DEBUG_NAME_MATCHING;
					break;
				case 'p':
					debug |= DEBUG_PARALLEL;
					break;
				case 'q':
					debug |= DEBUG_QUICKDEATH;
					break;
				case 's':
					debug |= DEBUG_SUFF;
					break;
				case 't':
					debug |= DEBUG_TARG;
					break;
				case 'T':
					debug |= DEBUG_TARGGROUP;
					break;
				case 'v':
					debug |= DEBUG_VAR;
					break;
				default:
					(void)fprintf(stderr,
				"make: illegal argument to -d option -- %c\n",
					    *modules);
					usage();
				}
			record_option(c, optarg);
			break;
		}
		case 'f':
			Lst_AtEnd(&makefiles, optarg);
			break;
		case 'j': {
			const char *errstr;

			forceJobs = true;
			optj = strtonum(optarg, 1, INT_MAX, &errstr);
			if (errstr != NULL) {
				fprintf(stderr,
				    "make: illegal argument to -j option"
				    " -- %s -- %s\n", optarg, errstr);
				usage();
			}
			record_option(c, optarg);
			break;
		}
		case 'm':
			Dir_AddDir(systemIncludePath, optarg);
			record_option(c, optarg);
			break;
		case -1:
			/* Check for variable assignments and targets. */
			if (argv[optind] != NULL &&
			    !Parse_CmdlineVar(argv[optind])) {
				if (!*argv[optind])
					Punt("illegal (null) argument.");
				Lst_AtEnd(create, estrdup(argv[optind]));
			}
			optind++;	/* skip over non-option */
			break;
		default:
			posixParseOptLetter(c);
		}
	}
}

static void
MainParseChdir(int argc, char **argv)
{
	int c, optend, oldopterr;

	optind = 1;	/* since we're called more than once */
	optreset = 1;
	optend = 0;
	oldopterr = opterr;
	opterr = 0;
	while (optind < argc) {
		if (!optend && argv[optind][0] == '-') {
			if (argv[optind][1] == '\0')
				optind++;	/* ignore "-" */
			else if (argv[optind][1] == '-' &&
			    argv[optind][2] == '\0') {
				optind++;	/* ignore "--" */
				optend++;	/* "--" denotes end of flags */
			}
		}
		c = optend ? -1 : getopt(argc, argv, OPTFLAGS);
		switch (c) {
		case 'C':
			if (chdir(optarg) == -1)
				err(2, "chdir(%s)", optarg);
			break;
		case -1:
			optind++;	/* skip over non-option */
			break;
		default:
			break;
		}
	}
	opterr = oldopterr;
}

/*-
 * Main_ParseArgLine --
 *	Used by the parse module when a .MFLAGS or .MAKEFLAGS target
 *	is encountered and by main() when reading the .MAKEFLAGS envariable.
 *	Takes a line of arguments and breaks it into its
 *	component words and passes those words and the number of them to the
 *	MainParseArgs function.
 *	The line should have all its leading whitespace removed.
 *
 * Side Effects:
 *	Only those that come from the various arguments.
 */
void
Main_ParseArgLine(const char *line) 	/* Line to fracture */
{
	char **argv;			/* Manufactured argument vector */
	int argc;			/* Number of arguments in argv */
	char *args;			/* Space used by the args */
	char *buf;
	char *argv0;
	const char *s;
	size_t len;


	if (line == NULL)
		return;
	for (; *line == ' '; ++line)
		continue;
	if (!*line)
		return;

	/* POSIX rule: MAKEFLAGS can hold a set of option letters without
	 * any blanks or dashes. */
	for (s = line;; s++) {
		if (*s == '\0') {
			while (line != s)
				posixParseOptLetter(*line++);
			return;
		}
		if (strchr(OPTLETTERS, *s) == NULL)
			break;
	}
	argv0 = Var_Value(".MAKE");
	len = strlen(line) + strlen(argv0) + 2;
	buf = emalloc(len);
	(void)snprintf(buf, len, "%s %s", argv0, line);

	argv = brk_string(buf, &argc, &args);
	free(buf);
	MainParseArgs(argc, argv);

	free(args);
	free(argv);
}

/* Add a :-separated path to a Lst of directories.  */
static void
add_dirpath(Lst l, const char *n)
{
	const char *start;
	const char *cp;

	for (start = n;;) {
		for (cp = start; *cp != '\0' && *cp != ':';)
			cp++;
		Dir_AddDiri(l, start, cp);
		if (*cp == '\0')
			break;
		else
			start= cp+1;
	}
}

/*
 * Get the name of this type of MACHINE from utsname so we can share an
 * executable for similar machines. (i.e. m68k: amiga hp300, mac68k, sun3, ...)
 *
 * Note that MACHINE, MACHINE_ARCH and MACHINE_CPU are decided at
 * run-time.
 */
static char *
figure_out_MACHINE()
{
	char *r = getenv("MACHINE");
	if (r == NULL) {
		static struct utsname utsname;

		if (uname(&utsname) == -1)
			err(2, "uname");
		r = utsname.machine;
	}
	return r;
}

static char *
figure_out_MACHINE_ARCH()
{
	char *r = getenv("MACHINE_ARCH");
	if (r == NULL) {
#ifndef MACHINE_ARCH
		r = "unknown";	/* XXX: no uname -p yet */
#else
		r = MACHINE_ARCH;
#endif
	}
	return r;
}
static char *
figure_out_MACHINE_CPU()
{
	char *r = getenv("MACHINE_CPU");
	if (r == NULL) {
#if !defined(MACHINE_CPU) && ! defined(MACHINE_ARCH)
		r = "unknown";	/* XXX: no uname -p yet */
#else
#if defined(MACHINE_CPU)
		r = MACHINE_CPU;
#else
		r = MACHINE_ARCH;
#endif
#endif
	}
	return r;
}

static char *
figure_out_CURDIR()
{
	char *dir, *cwd;
	struct stat sa, sb;

	/* curdir is cwd... */
	cwd = getcwd(NULL, 0);
	if (cwd == NULL)
		err(2, "getcwd");

	if (stat(cwd, &sa) == -1)
		err(2, "%s", cwd);

	/* ...but we can use the alias $PWD if we can prove it is the same
	 * directory */
	if ((dir = getenv("PWD")) != NULL) {
		if (stat(dir, &sb) == 0 && sa.st_ino == sb.st_ino &&
		    sa.st_dev == sb.st_dev) {
		    	free(cwd);
			return estrdup(dir);
		}
	}

	return cwd;
}

static char *
chdir_verify_path(const char *path, struct dirs *d)
{
	if (chdir(path) == 0) {
		if (path[0] != '/')
			return Str_concat(d->current, path, '/');
		else
			return estrdup(path);
	}
	return NULL;
}

static void
setup_CURDIR_OBJDIR(struct dirs *d)
{
	char *path;

	d->current = figure_out_CURDIR();
	/*
	 * If the MAKEOBJDIR (or by default, the _PATH_OBJDIR) directory
	 * exists, change into it and build there.  
	 *
	 * Once things are initted,
	 * have to add the original directory to the search path,
	 * and modify the paths for the Makefiles appropriately.  The
	 * current directory is also placed as a variable for make scripts.
	 */
	if ((path = getenv("MAKEOBJDIR")) == NULL) {
		path = _PATH_OBJDIR;
	} 
	d->object = chdir_verify_path(path, d);
	if (d->object == NULL)
		d->object = d->current;
}

/*
 * if the VPATH variable is defined, add its contents to the search path.
 * Uses the same format as the PATH env variable, i.e.,
 * <directory>:<directory>:<directory>...
 */
static void
setup_VPATH()
{
	if (Var_Value("VPATH") != NULL) {
		char *vpath;

		vpath = Var_Subst("${VPATH}", NULL, false);
		add_dirpath(defaultPath, vpath);
		(void)free(vpath);
	}
}

static void
read_makefile_list(Lst mk, struct dirs *d)
{
	LstNode ln;
	ln = Lst_Find(mk, ReadMakefile, d);
	if (ln != NULL)
		Fatal("make: cannot open %s.", (char *)Lst_Datum(ln));
}

static void
read_all_make_rules(bool noBuiltins, bool read_depend,
    Lst makefiles, struct dirs *d)
{
	/*
	 * Read in the built-in rules first, followed by the specified
	 * makefile(s), or the default Makefile or makefile, in that order.
	 */
	if (!noBuiltins) {
		LIST sysMkPath; 		/* Path of sys.mk */

		Lst_Init(&sysMkPath);
		Dir_Expand(_PATH_DEFSYSMK, systemIncludePath, &sysMkPath);
		if (Lst_IsEmpty(&sysMkPath))
			Fatal("make: no system rules (%s).", _PATH_DEFSYSMK);

		read_makefile_list(&sysMkPath, d);
	}

	if (!Lst_IsEmpty(makefiles)) {
		read_makefile_list(makefiles, d);
	} else if (!ReadMakefile("makefile", d))
		(void)ReadMakefile("Makefile", d);

	/* read a .depend file, if it exists, and we're not building depend */

	if (read_depend)
		(void)ReadMakefile(".depend", d);
	Parse_End();
}

static void
run_node(GNode *gn, bool *has_errors, bool *out_of_date)
{
	LIST l;

	Lst_Init(&l);
	Lst_AtEnd(&l, gn);
	engine_run_list(&l, has_errors, out_of_date);
	Lst_Destroy(&l, NOFREE);
}

int main(int, char **);

int
main(int argc, char **argv)
{
	static LIST targs;	/* target nodes to create */
	bool outOfDate = false;	/* false if all targets up to date */
	bool errored = false;	/* true if errors occurred */
	char *machine = figure_out_MACHINE();
	char *machine_arch = figure_out_MACHINE_ARCH();
	char *machine_cpu = figure_out_MACHINE_CPU();
	const char *syspath = _PATH_DEFSYSPATH;
	char *p;
	static struct dirs d;
	bool read_depend = true;/* false if we don't want to read .depend */

	MainParseChdir(argc, argv);
	setup_CURDIR_OBJDIR(&d);

	esetenv("PWD", d.object);
	unsetenv("CDPATH");

	Static_Lst_Init(create);
	Static_Lst_Init(&makefiles);
	Static_Lst_Init(&varstoprint);
	Static_Lst_Init(&targs);
	Static_Lst_Init(&special);

	beSilent = false;		/* Print commands as executed */
	ignoreErrors = false;		/* Pay attention to non-zero returns */
	noExecute = false;		/* Execute all commands */
	keepgoing = false;		/* Stop on error */
	allPrecious = false;		/* Remove targets when interrupted */
	queryFlag = false;		/* This is not just a check-run */
	noBuiltins = false;		/* Read the built-in rules */
	touchFlag = false;		/* Actually update targets */
	debug = 0;			/* No debug verbosity, please. */

	optj = DEFMAXJOBS;
	compatMake = false;		/* No compat mode */


	/*
	 * Initialize all external modules.
	 */
	Init();

	if (d.object != d.current)
		Dir_AddDir(defaultPath, d.current);
	Var_Set(".CURDIR", d.current);
	Var_Set(".OBJDIR", d.object);
	Parse_setcurdir(d.current);
	Targ_setdirs(d.current, d.object);

	/*
	 * Initialize various variables.
	 *	MAKE also gets this name, for compatibility
	 *	.MAKEFLAGS gets set to the empty string just in case.
	 *	MFLAGS also gets initialized empty, for compatibility.
	 */
	Var_Set("MAKE", argv[0]);
	Var_Set(".MAKE", argv[0]);
	Var_Set(MAKEFLAGS, "");
	Var_Set("MFLAGS", "");
	Var_Set("MACHINE", machine);
	Var_Set("MACHINE_ARCH", machine_arch);
	Var_Set("MACHINE_CPU", machine_cpu);

	/*
	 * First snag any flags out of the MAKEFLAGS environment variable.
	 */
	Main_ParseArgLine(getenv("MAKEFLAGS"));
	
	basedirectory = getenv("MAKEBASEDIRECTORY");
	if (basedirectory == NULL)
		setenv("MAKEBASEDIRECTORY", d.current, 0);

	MainParseArgs(argc, argv);

	/*
	 * Be compatible if user did not specify -j
	 */
	if (!forceJobs)
		compatMake = true;

	/* And set up everything for sub-makes */
	Var_AddCmdline(MAKEFLAGS);


	/*
	 * Set up the .TARGETS variable to contain the list of targets to be
	 * created. If none specified, make the variable empty -- the parser
	 * will fill the thing in with the default or .MAIN target.
	 */
	if (!Lst_IsEmpty(create)) {
		LstNode ln;

		for (ln = Lst_First(create); ln != NULL; ln = Lst_Adv(ln)) {
			char *name = Lst_Datum(ln);

			if (strcmp(name, "depend") == 0)
				read_depend = false;

			Var_Append(".TARGETS", name);
		}
	} else
		Var_Set(".TARGETS", "");


	/*
	 * If no user-supplied system path was given (through the -m option)
	 * add the directories from the DEFSYSPATH (more than one may be given
	 * as dir1:...:dirn) to the system include path.
	 */
	if (Lst_IsEmpty(systemIncludePath))
	    add_dirpath(systemIncludePath, syspath);

	read_all_make_rules(noBuiltins, read_depend, &makefiles, &d);

	if (compatMake)
		optj = 1;

	Var_Append("MFLAGS", Var_Value(MAKEFLAGS));

	/* Install all the flags into the MAKEFLAGS env variable. */
	if (((p = Var_Value(MAKEFLAGS)) != NULL) && *p)
		esetenv("MAKEFLAGS", p);

	setup_VPATH();

	process_suffixes_after_makefile_is_read();

	if (dumpData) {
		dump_data();
		exit(0);
	}

	/* Print the initial graph, if the user requested it.  */
	if (DEBUG(GRAPH1))
		dump_data();

	/* Print the values of any variables requested by the user.  */
	if (!Lst_IsEmpty(&varstoprint)) {
		LstNode ln;

		for (ln = Lst_First(&varstoprint); ln != NULL;
		    ln = Lst_Adv(ln)) {
			char *value = Var_Value(Lst_Datum(ln));

			printf("%s\n", value ? value : "");
		}
	} else {
		/* Have now read the entire graph and need to make a list
		 * of targets to create. If none was given on the command
		 * line, we consult the parsing module to find the main
		 * target(s) to create.  */
		if (Lst_IsEmpty(create))
			Parse_MainName(&targs);
		else
			Targ_FindList(&targs, create);

		choose_engine(compatMake);
		Job_Init(optj);
		if (!queryFlag && node_is_real(begin_node))
			run_node(begin_node, &errored, &outOfDate);

		if (!errored)
			engine_run_list(&targs, &errored, &outOfDate);

		if (!queryFlag && !errored && node_is_real(end_node))
			run_node(end_node, &errored, &outOfDate);
	}

	/* print the graph now it's been processed if the user requested it */
	if (DEBUG(GRAPH2))
		post_mortem();

	/* Note that we only hit this code if -k is used, otherwise we
	 * exited early in case of errors. */
	if (errored)
		Fatal("Errors while building");

	if (queryFlag && outOfDate)
		return 1;
	else
		return 0;
}

struct unreadable {
	char *fname;
	int errcode;
};

void
dump_unreadable(void)
{
	struct unreadable *u;

	if (Lst_IsEmpty(&unreadable))
		return;

	fprintf(stderr, "Makefile(s) that couldn't be read:\n");

	while ((u = Lst_Pop(&unreadable))) {
		fprintf(stderr, "\t%s: %s\n", u->fname, strerror(u->errcode));
		free(u->fname);
		free(u);
	}
}

static FILE *
open_makefile(const char *fname)
{
	FILE *stream;
	struct unreadable *u;

	stream = fopen(fname, "r");
	if (stream != NULL)
		return stream;

	if (errno != ENOENT) {
		u = emalloc(sizeof *u);
		u->fname = estrdup(fname);
		u->errcode = errno;
		Lst_AtEnd(&unreadable, u);
	}

	return NULL;
}

/*-
 * ReadMakefile  --
 *	Open and parse the given makefile.
 *
 * Results:
 *	true if ok. false if couldn't open file.
 *
 * Side Effects:
 *	lots
 */
static bool
ReadMakefile(void *p, void *q)
{
	const char *fname = p;	/* makefile to read */
	struct dirs *d = q;
	FILE *stream;
	char *name;

	if (!strcmp(fname, "-")) {
		Var_Set("MAKEFILE", "");
		Parse_File(estrdup("(stdin)"), stdin);
	} else {
		if ((stream = open_makefile(fname)) != NULL)
			goto found;
		/* if we've chdir'd, rebuild the path name */
		if (d->current != d->object && *fname != '/') {
			char *path;

			path = Str_concat(d->current, fname, '/');
			if ((stream = open_makefile(path)) == NULL)
				free(path);
			else {
				fname = path;
				goto found;
			}
		}
		/* look in -I and system include directories. */
		name = Dir_FindFile(fname, userIncludePath);
		if (!name)
			name = Dir_FindFile(fname, systemIncludePath);
		if (!name)
			return false;
		/* do not try to open a file we already have, so that
		 * dump_unreadable() yields non-confusing results.
		 */
		if (strcmp(name, fname) == 0)
			return false;
		if ((stream = open_makefile(name)) == NULL)
			return false;
		fname = name;
		/*
		 * set the MAKEFILE variable desired by System V fans -- the
		 * placement of the setting here means it gets set to the last
		 * makefile specified, as it is set by SysV make.
		 */
found:		Var_Set("MAKEFILE", fname);
		Parse_File(fname, stream);
	}
	return true;
}


/*
 * usage --
 *	exit with usage message
 */
static void
usage()
{
	(void)fprintf(stderr,
"usage: make [-BeiknpqrSst] [-C directory] [-D variable] [-d flags] [-f mk]\n\
	    [-I directory] [-j max_jobs] [-m directory] [-V variable]\n\
	    [NAME=value ...] [target ...]\n");
	exit(2);
}