summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--UPDATING4
-rw-r--r--share/mk/src.opts.mk1
-rw-r--r--usr.bin/Makefile4
-rw-r--r--usr.bin/make/GNode.h224
-rw-r--r--usr.bin/make/Makefile120
-rw-r--r--usr.bin/make/Makefile.depend19
-rw-r--r--usr.bin/make/Makefile.dist10
-rw-r--r--usr.bin/make/arch.c1224
-rw-r--r--usr.bin/make/arch.h61
-rw-r--r--usr.bin/make/buf.c291
-rw-r--r--usr.bin/make/buf.h92
-rw-r--r--usr.bin/make/cond.c1221
-rw-r--r--usr.bin/make/cond.h73
-rw-r--r--usr.bin/make/config.h111
-rw-r--r--usr.bin/make/dir.c1217
-rw-r--r--usr.bin/make/dir.h72
-rw-r--r--usr.bin/make/for.c267
-rw-r--r--usr.bin/make/for.h50
-rw-r--r--usr.bin/make/globals.h116
-rw-r--r--usr.bin/make/hash.c398
-rw-r--r--usr.bin/make/hash.h103
-rw-r--r--usr.bin/make/hash_tables.c130
-rw-r--r--usr.bin/make/hash_tables.h36
-rw-r--r--usr.bin/make/job.c3391
-rw-r--r--usr.bin/make/job.h80
-rw-r--r--usr.bin/make/lst.c344
-rw-r--r--usr.bin/make/lst.h175
-rw-r--r--usr.bin/make/main.c1339
-rw-r--r--usr.bin/make/make.11843
-rw-r--r--usr.bin/make/make.c819
-rw-r--r--usr.bin/make/make.h75
-rw-r--r--usr.bin/make/parse.c2545
-rw-r--r--usr.bin/make/parse.h86
-rw-r--r--usr.bin/make/pathnames.h56
-rw-r--r--usr.bin/make/proc.c134
-rw-r--r--usr.bin/make/proc.h53
-rw-r--r--usr.bin/make/shell.c472
-rw-r--r--usr.bin/make/shell.h110
-rw-r--r--usr.bin/make/str.c559
-rw-r--r--usr.bin/make/str.h81
-rw-r--r--usr.bin/make/suff.c2205
-rw-r--r--usr.bin/make/suff.h61
-rw-r--r--usr.bin/make/targ.c472
-rw-r--r--usr.bin/make/targ.h73
-rw-r--r--usr.bin/make/util.c316
-rw-r--r--usr.bin/make/util.h116
-rw-r--r--usr.bin/make/var.c2623
-rw-r--r--usr.bin/make/var.h85
48 files changed, 4 insertions, 23953 deletions
diff --git a/UPDATING b/UPDATING
index 5472d97..56cb5f0 100644
--- a/UPDATING
+++ b/UPDATING
@@ -31,6 +31,10 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 11.x IS SLOW:
disable the most expensive debugging functionality run
"ln -s 'abort:false,junk:false' /etc/malloc.conf".)
+20150616:
+ FreeBSD's old make (fmake) has been removed from the system. It is
+ available as the devel/fmake port or via pkg install fmake.
+
20150615:
The fix for the issue described in the 20150614 sendmail entry
below has been been committed in revision 284436. The work
diff --git a/share/mk/src.opts.mk b/share/mk/src.opts.mk
index 127b81c..3331f6f 100644
--- a/share/mk/src.opts.mk
+++ b/share/mk/src.opts.mk
@@ -179,7 +179,6 @@ __DEFAULT_NO_OPTIONS = \
BSD_GREP \
CLANG_EXTRAS \
EISA \
- FMAKE \
HESIOD \
LLDB \
NAND \
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
index c77f89a..1187dc2 100644
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -253,10 +253,6 @@ SUBDIR+= file
SUBDIR+= finger
.endif
-.if ${MK_FMAKE} != "no"
-SUBDIR+= make
-.endif
-
.if ${MK_FTP} != "no"
SUBDIR+= ftp
.endif
diff --git a/usr.bin/make/GNode.h b/usr.bin/make/GNode.h
deleted file mode 100644
index 05af1d0..0000000
--- a/usr.bin/make/GNode.h
+++ /dev/null
@@ -1,224 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * $FreeBSD$
- */
-
-#ifndef GNode_h_39503bf2
-#define GNode_h_39503bf2
-
-#include "lst.h"
-#include "util.h"
-
-struct Suff;
-
-/*
- * The structure for an individual graph node. Each node has several
- * pieces of data associated with it.
- */
-typedef struct GNode {
- char *name; /* The target's name */
- char *path; /* The full pathname of the target file */
-
- /*
- * The type of operator used to define the sources (qv. parse.c)
- *
- * The OP_ constants are used when parsing a dependency line as a way of
- * communicating to other parts of the program the way in which a target
- * should be made. These constants are bitwise-OR'ed together and
- * placed in the 'type' field of each node. Any node that has
- * a 'type' field which satisfies the OP_NOP function was never never on
- * the lefthand side of an operator, though it may have been on the
- * righthand side...
- */
- int type;
-#define OP_DEPENDS 0x00000001 /* Execution of commands depends on
- * kids (:) */
-#define OP_FORCE 0x00000002 /* Always execute commands (!) */
-#define OP_DOUBLEDEP 0x00000004 /* Execution of commands depends on
- * kids per line (::) */
-#define OP_OPMASK (OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP)
-
-#define OP_OPTIONAL 0x00000008 /* Don't care if the target doesn't
- * exist and can't be created */
-#define OP_USE 0x00000010 /*
- * Use associated commands for
- * parents
- */
-#define OP_EXEC 0x00000020 /* Target is never out of date, but
- * always execute commands anyway.
- * Its time doesn't matter, so it has
- * none...sort of
- */
-#define OP_IGNORE 0x00000040 /*
- * Ignore errors when creating the node
- */
-#define OP_PRECIOUS 0x00000080 /* Don't remove the target when
- * interrupted */
-#define OP_SILENT 0x00000100 /* Don't echo commands when executed */
-#define OP_MAKE 0x00000200 /*
- * Target is a recurrsive make so its
- * commands should always be executed
- * when it is out of date, regardless
- * of the state of the -n or -t flags
- */
-#define OP_JOIN 0x00000400 /* Target is out-of-date only if any of
- * its children was out-of-date */
-#define OP_INVISIBLE 0x00004000 /* The node is invisible to its parents.
- * I.e. it doesn't show up in the
- * parents's local variables. */
-#define OP_NOTMAIN 0x00008000 /* The node is exempt from normal 'main
- * target' processing in parse.c */
-#define OP_PHONY 0x00010000 /* Not a file target; run always */
-/* Attributes applied by PMake */
-#define OP_TRANSFORM 0x80000000 /* The node is a transformation rule */
-#define OP_MEMBER 0x40000000 /* Target is a member of an archive */
-#define OP_LIB 0x20000000 /* Target is a library */
-#define OP_ARCHV 0x10000000 /* Target is an archive construct */
-#define OP_HAS_COMMANDS 0x08000000 /* Target has all the commands it
- * should. Used when parsing to catch
- * multiple commands for a target */
-#define OP_SAVE_CMDS 0x04000000 /* Saving commands on .END (Compat) */
-#define OP_DEPS_FOUND 0x02000000 /* Already processed by Suff_FindDeps */
-
-/*
- * OP_NOP will return TRUE if the node with the given type was not the
- * object of a dependency operator
- */
-#define OP_NOP(t) (((t) & OP_OPMASK) == 0x00000000)
-
- int order; /* Its wait weight */
-
- Boolean make; /* TRUE if this target needs to be remade */
-
- /* Set to reflect the state of processing on this node */
- enum {
- UNMADE, /* Not examined yet */
-
- /*
- * Target is already being made. Indicates a cycle in the graph.
- * (compat mode only)
- */
- BEINGMADE,
-
- MADE, /* Was out-of-date and has been made */
- UPTODATE, /* Was already up-to-date */
-
- /*
- * An error occurred while it was being
- * made (used only in compat mode)
- */
- ERROR,
-
- /*
- * The target was aborted due to an
- * error making an inferior (compat).
- */
- ABORTED,
-
- /*
- * Marked as potentially being part of a graph cycle. If we
- * come back to a node marked this way, it is printed and
- * 'made' is changed to ENDCYCLE.
- */
- CYCLE,
-
- /*
- * The cycle has been completely printed. Go back and
- * unmark all its members.
- */
- ENDCYCLE
- } made;
-
- /* TRUE if one of this target's children was made */
- Boolean childMade;
-
- int unmade; /* The number of unmade children */
- int mtime; /* Its modification time */
- int cmtime; /* Modification time of its youngest child */
- struct GNode *cmtime_gn;/* Youngest child */
-
- /*
- * Links to parents for which this is an implied source, if any. (nodes
- * that depend on this, as gleaned from the transformation rules.
- */
- Lst iParents;
-
- /* List of nodes of the same name created by the :: operator */
- Lst cohorts;
-
- /* Lst of nodes for which this is a source (that depend on this one) */
- Lst parents;
-
- /* List of nodes on which this depends */
- Lst children;
-
- /*
- * List of nodes that must be made (if they're made) after this node is,
- * but that do not depend on this node, in the normal sense.
- */
- Lst successors;
-
- /*
- * List of nodes that must be made (if they're made) before this node
- * can be, but that do no enter into the datedness of this node.
- */
- Lst preds;
-
- /*
- * List of ``local'' variables that are specific to this target
- * and this target only (qv. var.c [$@ $< $?, etc.])
- */
- Lst context;
-
- /*
- * List of strings that are commands to be given to a shell
- * to create this target.
- */
- Lst commands;
-
- /* current command executing in compat mode */
- LstNode *compat_command;
-
- /*
- * Suffix for the node (determined by Suff_FindDeps and opaque to
- * everyone but the Suff module)
- */
- struct Suff *suffix;
-} GNode;
-
-#endif /* GNode_h_39503bf2 */
diff --git a/usr.bin/make/Makefile b/usr.bin/make/Makefile
deleted file mode 100644
index 1d366a5..0000000
--- a/usr.bin/make/Makefile
+++ /dev/null
@@ -1,120 +0,0 @@
-# @(#)Makefile 5.2 (Berkeley) 12/28/90
-# $Id: Makefile,v 1.6 1994/06/30 05:33:39 cgd Exp $
-# $FreeBSD$
-
-.include <src.opts.mk>
-
-PROG= make
-CFLAGS+=-I${.CURDIR}
-SRCS= arch.c buf.c cond.c dir.c for.c hash.c hash_tables.c job.c \
- lst.c main.c make.c parse.c proc.c shell.c str.c suff.c targ.c \
- util.c var.c
-
-.if !defined(MK_SHARED_TOOLCHAIN) || ${MK_SHARED_TOOLCHAIN} == "no"
-NO_SHARED?= YES
-.endif
-
-# Version has the RYYYYMMDDX format, where R is from RELENG_<R>
-CFLAGS+=-DMAKE_VERSION=\"10201205300\"
-
-# There is no obvious performance improvement currently.
-# CFLAGS+=-DUSE_KQUEUE
-
-# Make object files which depend on preprocessor symbols defined in
-# the Makefile which are not compilation options but rather configuration
-# options dependend on the Makefile. main.c needs MAKE_VERSION while
-# shell.c uses DEFSHELLNAME. This will cause recompilation in the case
-# the definition is changed in the makefile. It will of course not cause
-# recompilation if one does 'make MAKE_SHELL=csh'.
-main.o shell.o: ${MAKEFILE}
-
-# Directive and keyword tables. We use hash tables. These hash tables have been
-# generated with mph (ports/devel/mph)
-# If you change the directives or keywords (adding, deleting, reordering) you
-# need to create new tables and hash functions (directive_hash, keyword_hash).
-#
-# The following changes have been made to the generated code:
-#
-# o prefix the names of the g, T0 and T1 arrays with 'directive_'
-# resp. 'keyword_'.
-#
-# o make the type of the tables 'const [un]signed char' (if you change
-# anything make sure that the numbers fit into a char).
-#
-# o make the hash function use the length for termination,
-# not the trailing '\0', via the -l flag in emitc and some editing
-# (only for directive_hash).
-
-LOCALBASE ?= /usr/local
-MPH ?= ${LOCALBASE}/bin/mph
-EMITC ?= ${LOCALBASE}/bin/emitc
-
-.PRECIOUS: hash
-
-hash:
- ( echo '/*' ; \
- echo ' * DO NOT EDIT' ; \
- echo ' * $$''FreeBSD$$' ; \
- echo -n ' * auto-generated from ' ; \
- sed -nEe '/\$$FreeBSD/s/^.*\$$(.*)\$$.*$$/\1/p' \
- ${.CURDIR}/parse.c ; \
- echo ' * DO NOT EDIT' ; \
- echo ' */' ; \
- echo '#include <sys/types.h>' ; \
- echo ; \
- echo '#include "hash_tables.h"' ; \
- echo ; \
- cat ${.CURDIR}/parse.c | sed \
- -e '1,/DIRECTIVES-START-TAG/d' \
- -e '/DIRECTIVES-END-TAG/,$$d' \
- -e 's/^[^"]*"\([^"]*\)".*$$/\1/' | \
- ${MPH} -d2 -m1 | ${EMITC} -l -s | \
- sed \
- -e 's/^static int g\[\]/static const signed char directive_g[]/' \
- -e 's/^static int T0\[\]/static const u_char directive_T0[]/' \
- -e 's/^static int T1\[\]/static const u_char directive_T1[]/' \
- -e '/^#define uchar unsigned char/d' \
- -e 's/uchar/u_char/g' \
- -e 's/^hash(/directive_hash(/' \
- -e 's/; \*kp;/; kp < key + len;/' \
- -e 's/int len)/size_t len)/' \
- -e 's/= T0\[/= directive_T0\[/' \
- -e 's/= T1\[/= directive_T1\[/' \
- -e 's/g\[f/directive_g[f/g' ; \
- cat ${.CURDIR}/parse.c | sed \
- -e '1,/KEYWORD-START-TAG/d' \
- -e '/KEYWORD-END-TAG/,$$d' \
- -e 's/^[^"]*"\([^"]*\)".*$$/\1/' | \
- ${MPH} -d2 -m1 | ${EMITC} -l -s | \
- sed \
- -e 's/^static int g\[\]/static const signed char keyword_g[]/' \
- -e 's/^static int T0\[\]/static const u_char keyword_T0[]/' \
- -e 's/^static int T1\[\]/static const u_char keyword_T1[]/' \
- -e '/^#define uchar unsigned char/d' \
- -e 's/uchar/u_char/g' \
- -e 's/^hash(/keyword_hash(/' \
- -e 's/int len)/size_t len)/' \
- -e 's/= T0\[/= keyword_T0\[/' \
- -e 's/= T1\[/= keyword_T1\[/' \
- -e 's/g\[f/keyword_g[f/g' \
- ) > ${.CURDIR}/hash_tables.c
-
-# Set the shell which make(1) uses. Bourne is the default, but a decent
-# Korn shell works fine, and much faster. Using the C shell for this
-# will almost certainly break everything, but it's Unix tradition to
-# allow you to shoot yourself in the foot if you want to :-)
-
-MAKE_SHELL?= sh
-.if ${MAKE_SHELL} == "csh" || ${MAKE_SHELL} == "sh" || ${MAKE_SHELL} == "ksh"
-CFLAGS+= -DDEFSHELLNAME=\"${MAKE_SHELL}\"
-.else
-.error "MAKE_SHELL must be set to one of \"csh\", \"sh\" or \"ksh\"."
-.endif
-
-# if we are here we don't want this called 'make'
-PROG= fmake
-CLEANFILES+= fmake.1
-fmake.1: make.1
- ${CP} ${.ALLSRC} ${.TARGET}
-
-.include <bsd.prog.mk>
diff --git a/usr.bin/make/Makefile.depend b/usr.bin/make/Makefile.depend
deleted file mode 100644
index 2f3ce42..0000000
--- a/usr.bin/make/Makefile.depend
+++ /dev/null
@@ -1,19 +0,0 @@
-# $FreeBSD$
-# Autogenerated - do NOT edit!
-
-DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,}
-
-DEP_MACHINE := ${.PARSEFILE:E}
-
-DIRDEPS = \
- include \
- include/xlocale \
- lib/${CSU_DIR} \
- lib/libc \
-
-
-.include <dirdeps.mk>
-
-.if ${DEP_RELDIR} == ${_DEP_RELDIR}
-# local dependencies - needed for -jN in clean tree
-.endif
diff --git a/usr.bin/make/Makefile.dist b/usr.bin/make/Makefile.dist
deleted file mode 100644
index 8823d74..0000000
--- a/usr.bin/make/Makefile.dist
+++ /dev/null
@@ -1,10 +0,0 @@
-# $FreeBSD$
-# a simple makefile to help builds on !FreeBSD systems
-pmake:
- @echo 'make started.'
- cc -D__dead2="" -D__unused="" -Darc4random=random -D__FBSDID="static const char *id=" -DDEFSHELLNAME=\"sh\" -I. -c *.c
- cc *.o -o pmake
- @echo 'make completed.'
-
-clean:
- @rm -f *.o pmake
diff --git a/usr.bin/make/arch.c b/usr.bin/make/arch.c
deleted file mode 100644
index e4f6b0d..0000000
--- a/usr.bin/make/arch.c
+++ /dev/null
@@ -1,1224 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)arch.c 8.2 (Berkeley) 1/2/94
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*-
- * arch.c --
- * Functions to manipulate libraries, archives and their members.
- *
- * Once again, cacheing/hashing comes into play in the manipulation
- * of archives. The first time an archive is referenced, all of its members'
- * headers are read and hashed and the archive closed again. All hashed
- * archives are kept on a list which is searched each time an archive member
- * is referenced.
- *
- * The interface to this module is:
- * Arch_ParseArchive Given an archive specification, return a list
- * of GNode's, one for each member in the spec.
- * FALSE is returned if the specification is
- * invalid for some reason.
- *
- * Arch_Touch Alter the modification time of the archive
- * member described by the given node to be
- * the current time.
- *
- * Arch_TouchLib Update the modification time of the library
- * described by the given node. This is special
- * because it also updates the modification time
- * of the library's table of contents.
- *
- * Arch_MTime Find the modification time of a member of
- * an archive *in the archive*. The time is also
- * placed in the member's GNode. Returns the
- * modification time.
- *
- * Arch_MemTime Find the modification time of a member of
- * an archive. Called when the member doesn't
- * already exist. Looks in the archive for the
- * modification time. Returns the modification
- * time.
- *
- * Arch_FindLib Search for a library along a path. The
- * library name in the GNode should be in
- * -l<name> format.
- *
- * Arch_LibOODate Special function to decide if a library node
- * is out-of-date.
- *
- * Arch_Init Initialize this module.
- */
-
-#include <sys/param.h>
-#include <sys/queue.h>
-#include <sys/types.h>
-#include <ar.h>
-#include <ctype.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <regex.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <utime.h>
-
-#include "arch.h"
-#include "buf.h"
-#include "config.h"
-#include "dir.h"
-#include "globals.h"
-#include "GNode.h"
-#include "hash.h"
-#include "make.h"
-#include "parse.h"
-#include "targ.h"
-#include "util.h"
-#include "var.h"
-
-typedef struct Arch {
- char *name; /* Name of archive */
-
- /*
- * All the members of the archive described
- * by <name, struct ar_hdr *> key/value pairs
- */
- Hash_Table members;
-
- TAILQ_ENTRY(Arch) link; /* link all cached archives */
-} Arch;
-
-/* Lst of archives we've already examined */
-static TAILQ_HEAD(, Arch) archives = TAILQ_HEAD_INITIALIZER(archives);
-
-
-/* size of the name field in the archive member header */
-#define AR_NAMSIZ sizeof(((struct ar_hdr *)0)->ar_name)
-
-/*
- * This structure is used while reading/writing an archive
- */
-struct arfile {
- FILE *fp; /* archive file */
- char *fname; /* name of the file */
- struct ar_hdr hdr; /* current header */
- char sname[AR_NAMSIZ + 1]; /* short name */
- char *member; /* (long) member name */
- size_t mlen; /* size of the above */
- char *nametab; /* name table */
- size_t nametablen; /* size of the table */
- int64_t time; /* from ar_date */
- uint64_t size; /* from ar_size */
- off_t pos; /* header pos of current entry */
-};
-
-/*
- * Name of the symbol table. The original BSD used "__.SYMDEF". Rumours go
- * that this name may have a slash appended sometimes. Actually FreeBSD
- * uses "/" which probably came from SVR4.
- */
-#define SVR4_RANLIBMAG "/"
-#define BSD_RANLIBMAG "__.SYMDEF"
-
-/*
- * Name of the filename table. The 4.4BSD ar format did not use this, but
- * puts long filenames directly between the member header and the object
- * file.
- */
-#define SVR4_NAMEMAG "//"
-#define BSD_NAMEMAG "ARFILENAMES/"
-
-/*
- * 44BSD long filename key. Use a local define here instead of relying
- * on ar.h because we want this to continue working even when the
- * definition is removed from ar.h.
- */
-#define BSD_EXT1 "#1/"
-#define BSD_EXT1LEN 3
-
-/* if this is TRUE make archive errors fatal */
-Boolean arch_fatal = TRUE;
-
-/**
- * ArchError
- * An error happened while handling an archive. BSDmake traditionally
- * ignored these errors. Now this is dependent on the global arch_fatal
- * which, if true, makes these errors fatal and, if false, just emits an
- * error message.
- */
-#define ArchError(ARGS) do { \
- if (arch_fatal) \
- Fatal ARGS; \
- else \
- Error ARGS; \
- } while (0)
-
-/*-
- *-----------------------------------------------------------------------
- * Arch_ParseArchive --
- * Parse the archive specification in the given line and find/create
- * the nodes for the specified archive members, placing their nodes
- * on the given list, given the pointer to the start of the
- * specification, a Lst on which to place the nodes, and a context
- * in which to expand variables.
- *
- * Results:
- * TRUE if it was a valid specification. The linePtr is updated
- * to point to the first non-space after the archive spec. The
- * nodes for the members are placed on the given list.
- *
- * Side Effects:
- * Some nodes may be created. The given list is extended.
- *
- *-----------------------------------------------------------------------
- */
-Boolean
-Arch_ParseArchive(char **linePtr, Lst *nodeLst, GNode *ctxt)
-{
- char *cp; /* Pointer into line */
- GNode *gn; /* New node */
- char *libName; /* Library-part of specification */
- char *memName; /* Member-part of specification */
- char *nameBuf; /* temporary place for node name */
- char saveChar; /* Ending delimiter of member-name */
- Boolean subLibName; /* TRUE if libName should have/had
- * variable substitution performed on it */
-
- libName = *linePtr;
-
- subLibName = FALSE;
-
- for (cp = libName; *cp != '(' && *cp != '\0'; cp++) {
- if (*cp == '$') {
- /*
- * Variable spec, so call the Var module to parse the
- * puppy so we can safely advance beyond it...
- */
- size_t length = 0;
- Boolean freeIt;
- char *result;
-
- result = Var_Parse(cp, ctxt, TRUE, &length, &freeIt);
- if (result == var_Error) {
- return (FALSE);
- }
- subLibName = TRUE;
-
- if (freeIt) {
- free(result);
- }
- cp += length - 1;
- }
- }
-
- *cp++ = '\0';
- if (subLibName) {
- libName = Buf_Peel(Var_Subst(libName, ctxt, TRUE));
- }
-
- for (;;) {
- /*
- * First skip to the start of the member's name, mark that
- * place and skip to the end of it (either white-space or
- * a close paren).
- */
-
- /*
- * TRUE if need to substitute in memName
- */
- Boolean doSubst = FALSE;
-
- while (*cp != '\0' && *cp != ')' &&
- isspace((unsigned char)*cp)) {
- cp++;
- }
-
- memName = cp;
- while (*cp != '\0' && *cp != ')' &&
- !isspace((unsigned char)*cp)) {
- if (*cp == '$') {
- /*
- * Variable spec, so call the Var module to
- * parse the puppy so we can safely advance
- * beyond it...
- */
- size_t length = 0;
- Boolean freeIt;
- char *result;
-
- result = Var_Parse(cp, ctxt, TRUE,
- &length, &freeIt);
- if (result == var_Error) {
- return (FALSE);
- }
- doSubst = TRUE;
-
- if (freeIt) {
- free(result);
- }
- cp += length;
- } else {
- cp++;
- }
- }
-
- /*
- * If the specification ends without a closing parenthesis,
- * chances are there's something wrong (like a missing
- * backslash), so it's better to return failure than allow
- * such things to happen
- */
- if (*cp == '\0') {
- printf("No closing parenthesis in archive "
- "specification\n");
- return (FALSE);
- }
-
- /*
- * If we didn't move anywhere, we must be done
- */
- if (cp == memName) {
- break;
- }
-
- saveChar = *cp;
- *cp = '\0';
-
- /*
- * XXX: This should be taken care of intelligently by
- * SuffExpandChildren, both for the archive and the member
- * portions.
- */
- /*
- * If member contains variables, try and substitute for them.
- * This will slow down archive specs with dynamic sources, of
- * course, since we'll be (non-)substituting them three times,
- * but them's the breaks -- we need to do this since
- * SuffExpandChildren calls us, otherwise we could assume the
- * thing would be taken care of later.
- */
- if (doSubst) {
- char *buf;
- char *sacrifice;
- char *oldMemName = memName;
- size_t sz;
- Buffer *buf1;
-
- /*
- * Now form an archive spec and recurse to deal with
- * nested variables and multi-word variable values....
- * The results are just placed at the end of the
- * nodeLst we're returning.
- */
- buf1 = Var_Subst(memName, ctxt, TRUE);
- memName = Buf_Data(buf1);
-
- sz = strlen(memName) + strlen(libName) + 3;
- buf = emalloc(sz);
-
- snprintf(buf, sz, "%s(%s)", libName, memName);
-
- sacrifice = buf;
-
- if (strchr(memName, '$') &&
- strcmp(memName, oldMemName) == 0) {
- /*
- * Must contain dynamic sources, so we can't
- * deal with it now.
- * Just create an ARCHV node for the thing and
- * let SuffExpandChildren handle it...
- */
- gn = Targ_FindNode(buf, TARG_CREATE);
-
- if (gn == NULL) {
- free(buf);
- Buf_Destroy(buf1, FALSE);
- return (FALSE);
- }
- gn->type |= OP_ARCHV;
- Lst_AtEnd(nodeLst, (void *)gn);
- } else if (!Arch_ParseArchive(&sacrifice, nodeLst,
- ctxt)) {
- /*
- * Error in nested call -- free buffer and
- * return FALSE ourselves.
- */
- free(buf);
- Buf_Destroy(buf1, FALSE);
- return (FALSE);
- }
-
- /* Free buffer and continue with our work. */
- free(buf);
- Buf_Destroy(buf1, FALSE);
-
- } else if (Dir_HasWildcards(memName)) {
- Lst members = Lst_Initializer(members);
- char *member;
- size_t sz = MAXPATHLEN;
- size_t nsz;
-
- nameBuf = emalloc(sz);
-
- Path_Expand(memName, &dirSearchPath, &members);
- while (!Lst_IsEmpty(&members)) {
- member = Lst_DeQueue(&members);
- nsz = strlen(libName) + strlen(member) + 3;
- if (nsz > sz) {
- sz = nsz * 2;
- nameBuf = erealloc(nameBuf, sz);
- }
-
- snprintf(nameBuf, sz, "%s(%s)",
- libName, member);
- free(member);
- gn = Targ_FindNode(nameBuf, TARG_CREATE);
- if (gn == NULL) {
- free(nameBuf);
- /* XXXHB Lst_Destroy(&members) */
- return (FALSE);
- }
- /*
- * We've found the node, but have to make sure
- * the rest of the world knows it's an archive
- * member, without having to constantly check
- * for parentheses, so we type the thing with
- * the OP_ARCHV bit before we place it on the
- * end of the provided list.
- */
- gn->type |= OP_ARCHV;
- Lst_AtEnd(nodeLst, gn);
- }
- free(nameBuf);
- } else {
- size_t sz = strlen(libName) + strlen(memName) + 3;
-
- nameBuf = emalloc(sz);
- snprintf(nameBuf, sz, "%s(%s)", libName, memName);
- gn = Targ_FindNode(nameBuf, TARG_CREATE);
- free(nameBuf);
- if (gn == NULL) {
- return (FALSE);
- }
- /*
- * We've found the node, but have to make sure the
- * rest of the world knows it's an archive member,
- * without having to constantly check for parentheses,
- * so we type the thing with the OP_ARCHV bit before
- * we place it on the end of the provided list.
- */
- gn->type |= OP_ARCHV;
- Lst_AtEnd(nodeLst, gn);
- }
- if (doSubst) {
- free(memName);
- }
-
- *cp = saveChar;
- }
-
- /*
- * If substituted libName, free it now, since we need it no longer.
- */
- if (subLibName) {
- free(libName);
- }
-
- /*
- * We promised the pointer would be set up at the next non-space, so
- * we must advance cp there before setting *linePtr... (note that on
- * entrance to the loop, cp is guaranteed to point at a ')')
- */
- do {
- cp++;
- } while (*cp != '\0' && isspace((unsigned char)*cp));
-
- *linePtr = cp;
- return (TRUE);
-}
-
-/*
- * Close an archive file an free all resources
- */
-static void
-ArchArchiveClose(struct arfile *ar)
-{
-
- if (ar->nametab != NULL)
- free(ar->nametab);
- free(ar->member);
- if (ar->fp != NULL) {
- if (fclose(ar->fp) == EOF)
- ArchError(("%s: close error", ar->fname));
- }
- free(ar->fname);
- free(ar);
-}
-
-/*
- * Open an archive file.
- */
-static struct arfile *
-ArchArchiveOpen(const char *archive, const char *mode)
-{
- struct arfile *ar;
- char magic[SARMAG];
-
- ar = emalloc(sizeof(*ar));
- ar->fname = estrdup(archive);
- ar->mlen = 100;
- ar->member = emalloc(ar->mlen);
- ar->nametab = NULL;
- ar->nametablen = 0;
-
- if ((ar->fp = fopen(ar->fname, mode)) == NULL) {
- DEBUGM(ARCH, ("%s", ar->fname));
- ArchArchiveClose(ar);
- return (NULL);
- }
-
- /* read MAGIC */
- if (fread(magic, SARMAG, 1, ar->fp) != 1 ||
- strncmp(magic, ARMAG, SARMAG) != 0) {
- ArchError(("%s: bad archive magic\n", ar->fname));
- ArchArchiveClose(ar);
- return (NULL);
- }
-
- ar->pos = 0;
- return (ar);
-}
-
-/*
- * Read the next header from the archive. The return value will be +1 if
- * the header is read successfully, 0 on EOF and -1 if an error happened.
- * On a successful return sname contains the truncated member name and
- * member the full name. hdr contains the member header. For the symbol table
- * names of length 0 are returned. The entry for the file name table is never
- * returned.
- */
-static int
-ArchArchiveNext(struct arfile *ar)
-{
- char *end;
- int have_long_name;
- u_long offs;
- char *ptr;
- size_t ret;
- char buf[MAX(sizeof(ar->hdr.ar_size), sizeof(ar->hdr.ar_date)) + 1];
-
- next:
- /*
- * Seek to the next header.
- */
- if (ar->pos == 0) {
- ar->pos = SARMAG;
- } else {
- ar->pos += sizeof(ar->hdr) + ar->size;
- if (ar->size % 2 == 1)
- ar->pos++;
- }
-
- if (fseeko(ar->fp, ar->pos, SEEK_SET) == -1) {
- ArchError(("%s: cannot seek to %jd: %s", ar->fname,
- (intmax_t)ar->pos, strerror(errno)));
- return (-1);
- }
-
- /*
- * Read next member header
- */
- ret = fread(&ar->hdr, sizeof(ar->hdr), 1, ar->fp);
- if (ret != 1) {
- if (feof(ar->fp))
- return (0);
- ArchError(("%s: error reading member header: %s", ar->fname,
- strerror(errno)));
- return (-1);
- }
- if (strncmp(ar->hdr.ar_fmag, ARFMAG, sizeof(ar->hdr.ar_fmag)) != 0) {
- ArchError(("%s: bad entry magic", ar->fname));
- return (-1);
- }
-
- /*
- * looks like a member - get name by stripping trailing spaces
- * and NUL terminating.
- */
- strlcpy(ar->sname, ar->hdr.ar_name, AR_NAMSIZ + 1);
- for (ptr = ar->sname + AR_NAMSIZ; ptr > ar->sname; ptr--)
- if (ptr[-1] != ' ')
- break;
-
- *ptr = '\0';
-
- /*
- * Parse the size. All entries need to have a size. Be careful
- * to not allow buffer overruns.
- */
- strlcpy(buf, ar->hdr.ar_size, sizeof(ar->hdr.ar_size) + 1);
-
- errno = 0;
- ar->size = strtoumax(buf, &end, 10);
- if (errno != 0 || strspn(end, " ") != strlen(end)) {
- ArchError(("%s: bad size format in archive '%s'",
- ar->fname, buf));
- return (-1);
- }
-
- /*
- * Look for the extended name table. Do this before parsing
- * the date because this table doesn't need a date.
- */
- if (strcmp(ar->sname, BSD_NAMEMAG) == 0 ||
- strcmp(ar->sname, SVR4_NAMEMAG) == 0) {
- /* filename table - read it in */
- ar->nametablen = ar->size;
- ar->nametab = emalloc(ar->nametablen);
-
- ret = fread(ar->nametab, 1, ar->nametablen, ar->fp);
- if (ret != ar->nametablen) {
- if (ferror(ar->fp)) {
- ArchError(("%s: cannot read nametab: %s",
- ar->fname, strerror(errno)));
- } else {
- ArchError(("%s: cannot read nametab: "
- "short read", ar->fname, strerror(errno)));
- }
- return (-1);
- }
-
- /*
- * NUL terminate the entries. Entries are \n terminated
- * and may have a trailing / or \.
- */
- ptr = ar->nametab;
- while (ptr < ar->nametab + ar->nametablen) {
- if (*ptr == '\n') {
- if (ptr[-1] == '/' || ptr[-1] == '\\')
- ptr[-1] = '\0';
- *ptr = '\0';
- }
- ptr++;
- }
-
- /* get next archive entry */
- goto next;
- }
-
- /*
- * Now parse the modification date. Be careful to not overrun
- * buffers.
- */
- strlcpy(buf, ar->hdr.ar_date, sizeof(ar->hdr.ar_date) + 1);
-
- errno = 0;
- ar->time = (int64_t)strtoll(buf, &end, 10);
- if (errno != 0 || strspn(end, " ") != strlen(end)) {
- ArchError(("%s: bad date format in archive '%s'",
- ar->fname, buf));
- return (-1);
- }
-
- /*
- * Now check for the symbol table. This should really be the first
- * entry, but we don't check this.
- */
- if (strcmp(ar->sname, BSD_RANLIBMAG) == 0 ||
- strcmp(ar->sname, SVR4_RANLIBMAG) == 0) {
- /* symbol table - return a zero length name */
- ar->member[0] = '\0';
- ar->sname[0] = '\0';
- return (1);
- }
-
- have_long_name = 0;
-
- /*
- * Look whether this is a long name. There are several variants
- * of long names:
- * "#1/12 " - 12 length of following filename
- * "/17 " - index into name table
- * " 17 " - index into name table
- * Note that in the last case we must also check that there is no
- * slash in the name because of filenames with leading spaces:
- * " 777.o/ " - filename 777.o
- */
- if (ar->sname[0] == '/' || (ar->sname[0] == ' ' &&
- strchr(ar->sname, '/') == NULL)) {
- /* SVR4 extended name */
- errno = 0;
- offs = strtoul(ar->sname + 1, &end, 10);
- if (errno != 0 || *end != '\0' || offs >= ar->nametablen ||
- end == ar->sname + 1) {
- ArchError(("%s: bad extended name '%s'", ar->fname,
- ar->sname));
- return (-1);
- }
-
- /* fetch the name */
- if (ar->mlen <= strlen(ar->nametab + offs)) {
- ar->mlen = strlen(ar->nametab + offs) + 1;
- ar->member = erealloc(ar->member, ar->mlen);
- }
- strcpy(ar->member, ar->nametab + offs);
-
- have_long_name = 1;
-
- } else if (strncmp(ar->sname, BSD_EXT1, BSD_EXT1LEN) == 0 &&
- isdigit(ar->sname[BSD_EXT1LEN])) {
- /* BSD4.4 extended name */
- errno = 0;
- offs = strtoul(ar->sname + BSD_EXT1LEN, &end, 10);
- if (errno != 0 || *end != '\0' ||
- end == ar->sname + BSD_EXT1LEN) {
- ArchError(("%s: bad extended name '%s'", ar->fname,
- ar->sname));
- return (-1);
- }
-
- /* read it from the archive */
- if (ar->mlen <= offs) {
- ar->mlen = offs + 1;
- ar->member = erealloc(ar->member, ar->mlen);
- }
- ret = fread(ar->member, 1, offs, ar->fp);
- if (ret != offs) {
- if (ferror(ar->fp)) {
- ArchError(("%s: reading extended name: %s",
- ar->fname, strerror(errno)));
- } else {
- ArchError(("%s: reading extended name: "
- "short read", ar->fname));
- }
- return (-1);
- }
- ar->member[offs] = '\0';
-
- have_long_name = 1;
- }
-
- /*
- * Now remove the trailing slash that Svr4 puts at
- * the end of the member name to support trailing spaces in names.
- */
- if (ptr > ar->sname && ptr[-1] == '/')
- *--ptr = '\0';
-
- if (!have_long_name) {
- if (strlen(ar->sname) >= ar->mlen) {
- ar->mlen = strlen(ar->sname) + 1;
- ar->member = erealloc(ar->member, ar->mlen);
- }
- strcpy(ar->member, ar->sname);
- }
-
- return (1);
-}
-
-/*
- * Touch the current archive member by writing a new header with an
- * updated timestamp. The return value is 0 for success and -1 for errors.
- */
-static int
-ArchArchiveTouch(struct arfile *ar, int64_t ts)
-{
-
- /* seek to our header */
- if (fseeko(ar->fp, ar->pos, SEEK_SET) == -1) {
- ArchError(("%s: cannot seek to %jd: %s", ar->fname,
- (intmax_t)ar->pos, strerror(errno)));
- return (-1);
- }
-
- /*
- * change timestamp, be sure to not NUL-terminated it, but
- * to fill with spaces.
- */
- snprintf(ar->hdr.ar_date, sizeof(ar->hdr.ar_date), "%jd",
- (intmax_t)ts);
- memset(ar->hdr.ar_date + strlen(ar->hdr.ar_date),
- ' ', sizeof(ar->hdr.ar_date) - strlen(ar->hdr.ar_date));
-
- if (fwrite(&ar->hdr, sizeof(ar->hdr), 1, ar->fp) != 1) {
- ArchError(("%s: cannot touch: %s", ar->fname, strerror(errno)));
- return (-1);
- }
- return (0);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * ArchFindMember --
- * Locate a member of an archive, given the path of the archive and
- * the path of the desired member. If the archive is to be modified,
- * the mode should be "r+", if not, it should be "r". The archive
- * file is returned positioned at the correct header.
- *
- * Results:
- * A struct arfile *, opened for reading and, possibly writing,
- * positioned at the member's header, or NULL if the member was
- * nonexistent.
- *
- *-----------------------------------------------------------------------
- */
-static struct arfile *
-ArchFindMember(const char *archive, const char *member, const char *mode)
-{
- struct arfile *ar;
- const char *cp; /* Useful character pointer */
-
- if ((ar = ArchArchiveOpen(archive, mode)) == NULL)
- return (NULL);
-
- /*
- * Because of space constraints and similar things, files are archived
- * using their final path components, not the entire thing, so we need
- * to point 'member' to the final component, if there is one, to make
- * the comparisons easier...
- */
- if (member != NULL) {
- cp = strrchr(member, '/');
- if (cp != NULL) {
- member = cp + 1;
- }
- }
-
- while (ArchArchiveNext(ar) > 0) {
- /*
- * When comparing there are actually three cases:
- * (1) the name fits into the limit og af_name,
- * (2) the name is longer and the archive supports long names,
- * (3) the name is longer and the archive doesn't support long
- * names.
- * Because we don't know whether the archive supports long
- * names or not we need to be careful.
- */
- if (member == NULL) {
- /* special case - symbol table */
- if (ar->member[0] == '\0')
- return (ar);
- } else if (strlen(member) <= AR_NAMSIZ) {
- /* case (1) */
- if (strcmp(ar->member, member) == 0)
- return (ar);
- } else if (strcmp(ar->member, member) == 0) {
- /* case (3) */
- return (ar);
- } else {
- /* case (2) */
- if (strlen(ar->member) == AR_NAMSIZ &&
- strncmp(member, ar->member, AR_NAMSIZ) == 0)
- return (ar);
- }
- }
-
- /* not found */
- ArchArchiveClose(ar);
- return (NULL);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * ArchStatMember --
- * Locate a member of an archive, given the path of the archive and
- * the path of the desired member, and a boolean representing whether
- * or not the archive should be hashed (if not already hashed).
- *
- * Results:
- * A pointer to the current struct ar_hdr structure for the member. Note
- * That no position is returned, so this is not useful for touching
- * archive members. This is mostly because we have no assurances that
- * The archive will remain constant after we read all the headers, so
- * there's not much point in remembering the position...
- *
- * Side Effects:
- *
- *-----------------------------------------------------------------------
- */
-static int64_t
-ArchStatMember(const char *archive, const char *member, Boolean hash)
-{
- struct arfile *arf;
- int64_t ret;
- int t;
- char *cp; /* Useful character pointer */
- Arch *ar; /* Archive descriptor */
- Hash_Entry *he; /* Entry containing member's description */
- char copy[AR_NAMSIZ + 1];
-
- /*
- * Because of space constraints and similar things, files are archived
- * using their final path components, not the entire thing, so we need
- * to point 'member' to the final component, if there is one, to make
- * the comparisons easier...
- */
- if (member != NULL) {
- cp = strrchr(member, '/');
- if (cp != NULL)
- member = cp + 1;
- }
-
- TAILQ_FOREACH(ar, &archives, link) {
- if (strcmp(archive, ar->name) == 0)
- break;
- }
- if (ar == NULL) {
- /* archive not found */
- if (!hash) {
- /*
- * Caller doesn't want the thing hashed, just use
- * ArchFindMember to read the header for the member
- * out and close down the stream again.
- */
- arf = ArchFindMember(archive, member, "r");
- if (arf == NULL) {
- return (INT64_MIN);
- }
- ret = arf->time;
- ArchArchiveClose(arf);
- return (ret);
- }
-
- /*
- * We don't have this archive on the list yet, so we want to
- * find out everything that's in it and cache it so we can get
- * at it quickly.
- */
- arf = ArchArchiveOpen(archive, "r");
- if (arf == NULL) {
- return (INT64_MIN);
- }
-
- /* create archive data structure */
- ar = emalloc(sizeof(*ar));
- ar->name = estrdup(archive);
- Hash_InitTable(&ar->members, -1);
-
- while ((t = ArchArchiveNext(arf)) > 0) {
- he = Hash_CreateEntry(&ar->members, arf->member, NULL);
- Hash_SetValue(he, emalloc(sizeof(int64_t)));
- *(int64_t *)Hash_GetValue(he) = arf->time;
- }
-
- ArchArchiveClose(arf);
-
- if (t < 0) {
- /* error happened - throw away everything */
- Hash_DeleteTable(&ar->members);
- free(ar->name);
- free(ar);
- return (INT64_MIN);
- }
-
- TAILQ_INSERT_TAIL(&archives, ar, link);
- }
-
- /*
- * Now that the archive has been read and cached, we can look into
- * the hash table to find the desired member's header.
- */
- he = Hash_FindEntry(&ar->members, member);
- if (he != NULL)
- return (*(int64_t *)Hash_GetValue (he));
-
- if (member != NULL && strlen(member) > AR_NAMSIZ) {
- /* Try truncated name */
- strlcpy(copy, member, AR_NAMSIZ + 1);
-
- if ((he = Hash_FindEntry(&ar->members, copy)) != NULL)
- return (*(int64_t *)Hash_GetValue(he));
- }
-
- return (INT64_MIN);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Arch_Touch --
- * Touch a member of an archive.
- *
- * Results:
- * The 'time' field of the member's header is updated.
- *
- * Side Effects:
- * The modification time of the entire archive is also changed.
- * For a library, this could necessitate the re-ranlib'ing of the
- * whole thing.
- *
- *-----------------------------------------------------------------------
- */
-void
-Arch_Touch(GNode *gn)
-{
- struct arfile *ar;
-
- ar = ArchFindMember(Var_Value(ARCHIVE, gn),
- Var_Value(TARGET, gn), "r+");
-
- if (ar != NULL) {
- ArchArchiveTouch(ar, (int64_t)now);
- ArchArchiveClose(ar);
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Arch_TouchLib --
- * Given a node which represents a library, touch the thing, making
- * sure that the table of contents also is touched.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Both the modification time of the library and of the RANLIBMAG
- * member are set to 'now'.
- *
- *-----------------------------------------------------------------------
- */
-void
-Arch_TouchLib(GNode *gn)
-{
- struct arfile *ar; /* Open archive */
- struct utimbuf times; /* Times for utime() call */
-
- ar = ArchFindMember(gn->path, NULL, "r+");
- if (ar != NULL) {
- ArchArchiveTouch(ar, (int64_t)now);
- ArchArchiveClose(ar);
-
- times.actime = times.modtime = now;
- utime(gn->path, &times);
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Arch_MTime --
- * Return the modification time of a member of an archive, given its
- * name.
- *
- * Results:
- * The modification time(seconds).
- * XXXHB this should be a long.
- *
- * Side Effects:
- * The mtime field of the given node is filled in with the value
- * returned by the function.
- *
- *-----------------------------------------------------------------------
- */
-int
-Arch_MTime(GNode *gn)
-{
- int64_t mtime;
-
- mtime = ArchStatMember(Var_Value(ARCHIVE, gn),
- Var_Value(TARGET, gn), TRUE);
-
- if (mtime == INT_MIN) {
- mtime = 0;
- }
- gn->mtime = (int)mtime; /* XXX */
- return (gn->mtime);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Arch_MemMTime --
- * Given a non-existent archive member's node, get its modification
- * time from its archived form, if it exists.
- *
- * Results:
- * The modification time.
- *
- * Side Effects:
- * The mtime field is filled in.
- *
- *-----------------------------------------------------------------------
- */
-int
-Arch_MemMTime(GNode *gn)
-{
- LstNode *ln;
- GNode *pgn;
- char *nameStart;
- char *nameEnd;
-
- for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Succ(ln)) {
- pgn = Lst_Datum(ln);
-
- if (pgn->type & OP_ARCHV) {
- /*
- * If the parent is an archive specification and is
- * being made and its member's name matches the name of
- * the node we were given, record the modification time
- * of the parent in the child. We keep searching its
- * parents in case some other parent requires this
- * child to exist...
- */
- nameStart = strchr(pgn->name, '(') + 1;
- nameEnd = strchr(nameStart, ')');
-
- if (pgn->make && strncmp(nameStart, gn->name,
- nameEnd - nameStart) == 0) {
- gn->mtime = Arch_MTime(pgn);
- }
- } else if (pgn->make) {
- /*
- * Something which isn't a library depends on the
- * existence of this target, so it needs to exist.
- */
- gn->mtime = 0;
- break;
- }
- }
- return (gn->mtime);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Arch_FindLib --
- * Search for a named library along the given search path.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The node's 'path' field is set to the found path (including the
- * actual file name, not -l...). If the system can handle the -L
- * flag when linking (or we cannot find the library), we assume that
- * the user has placed the .LIBRARIES variable in the final linking
- * command (or the linker will know where to find it) and set the
- * TARGET variable for this node to be the node's name. Otherwise,
- * we set the TARGET variable to be the full path of the library,
- * as returned by Path_FindFile.
- *
- *-----------------------------------------------------------------------
- */
-void
-Arch_FindLib(GNode *gn, struct Path *path)
-{
- char *libName; /* file name for archive */
- size_t sz;
-
- sz = strlen(gn->name) + 4;
- libName = emalloc(sz);
- snprintf(libName, sz, "lib%s.a", &gn->name[2]);
-
- gn->path = Path_FindFile(libName, path);
-
- free(libName);
-
-#ifdef LIBRARIES
- Var_Set(TARGET, gn->name, gn);
-#else
- Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn);
-#endif /* LIBRARIES */
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Arch_LibOODate --
- * Decide if a node with the OP_LIB attribute is out-of-date. Called
- * from Make_OODate to make its life easier, with the library's
- * graph node.
- *
- * There are several ways for a library to be out-of-date that are
- * not available to ordinary files. In addition, there are ways
- * that are open to regular files that are not available to
- * libraries. A library that is only used as a source is never
- * considered out-of-date by itself. This does not preclude the
- * library's modification time from making its parent be out-of-date.
- * A library will be considered out-of-date for any of these reasons,
- * given that it is a target on a dependency line somewhere:
- * Its modification time is less than that of one of its
- * sources (gn->mtime < gn->cmtime).
- * Its modification time is greater than the time at which the
- * make began (i.e. it's been modified in the course
- * of the make, probably by archiving).
- * The modification time of one of its sources is greater than
- * the one of its RANLIBMAG member (i.e. its table of contents
- * is out-of-date). We don't compare of the archive time
- * vs. TOC time because they can be too close. In my
- * opinion we should not bother with the TOC at all since
- * this is used by 'ar' rules that affect the data contents
- * of the archive, not by ranlib rules, which affect the
- * TOC.
- *
- * Results:
- * TRUE if the library is out-of-date. FALSE otherwise.
- *
- * Side Effects:
- * The library will be hashed if it hasn't been already.
- *
- *-----------------------------------------------------------------------
- */
-Boolean
-Arch_LibOODate(GNode *gn)
-{
- int64_t mtime; /* The table-of-contents's mod time */
-
- if (OP_NOP(gn->type) && Lst_IsEmpty(&gn->children)) {
- return (FALSE);
- }
- if (gn->mtime > now || gn->mtime < gn->cmtime) {
- return (TRUE);
- }
-
- mtime = ArchStatMember(gn->path, NULL, FALSE);
- if (mtime == INT64_MIN) {
- /*
- * Not found. A library w/o a table of contents is out-of-date
- */
- if (DEBUG(ARCH) || DEBUG(MAKE)) {
- Debug("No TOC...");
- }
- return (TRUE);
- }
-
- /* XXX choose one. */
- if (DEBUG(ARCH) || DEBUG(MAKE)) {
- Debug("TOC modified %s...", Targ_FmtTime(mtime));
- }
- return (gn->cmtime > mtime);
-}
diff --git a/usr.bin/make/arch.h b/usr.bin/make/arch.h
deleted file mode 100644
index 3c28b2f..0000000
--- a/usr.bin/make/arch.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * $FreeBSD$
- */
-
-#ifndef arch_h_488adf7a
-#define arch_h_488adf7a
-
-#include "util.h"
-
-struct GNode;
-struct Lst;
-struct Path;
-
-/* archive errors are fatal */
-extern Boolean arch_fatal;
-
-Boolean Arch_ParseArchive(char **, struct Lst *, struct GNode *);
-void Arch_Touch(struct GNode *);
-void Arch_TouchLib(struct GNode *);
-int Arch_MTime(struct GNode *);
-int Arch_MemMTime(struct GNode *);
-void Arch_FindLib(struct GNode *, struct Path *);
-Boolean Arch_LibOODate(struct GNode *);
-
-#endif /* arch_h_488adf7a */
diff --git a/usr.bin/make/buf.c b/usr.bin/make/buf.c
deleted file mode 100644
index fa866d3..0000000
--- a/usr.bin/make/buf.c
+++ /dev/null
@@ -1,291 +0,0 @@
-/*-
- * Copyright (c) 2005 Max Okumoto
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1988, 1989 by Adam de Boor
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)buf.c 8.1 (Berkeley) 6/6/93
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*
- * buf.c
- * Functions for automatically-expanded buffers.
- */
-
-#include <string.h>
-#include <stdlib.h>
-
-#include "buf.h"
-#include "util.h"
-
-/**
- * Returns the number of bytes in the buffer. Doesn't include the
- * null-terminating byte.
- */
-size_t
-Buf_Size(const Buffer *buf)
-{
-
- return (buf->end - buf->buf);
-}
-
-/**
- * Returns a reference to the data contained in the buffer.
- *
- * @note Adding data to the Buffer object may invalidate the reference.
- */
-char *
-Buf_Data(const Buffer *bp)
-{
-
- return (bp->buf);
-}
-
-/**
- * Expand the buffer to hold the number of additional bytes, plus
- * space to store a terminating NULL byte.
- */
-static inline void
-BufExpand(Buffer *bp, size_t nb)
-{
- size_t len = Buf_Size(bp);
- size_t size;
-
- if (bp->size < len + nb + 1) {
- size = bp->size + MAX(nb + 1, BUF_ADD_INC);
- bp->size = size;
- bp->buf = erealloc(bp->buf, size);
- bp->end = bp->buf + len;
- }
-}
-
-/**
- * Add a single byte to the buffer.
- */
-void
-Buf_AddByte(Buffer *bp, Byte byte)
-{
-
- BufExpand(bp, 1);
-
- *bp->end = byte;
- bp->end++;
- *bp->end = '\0';
-}
-
-/**
- * Add bytes to the buffer.
- */
-void
-Buf_AddBytes(Buffer *bp, size_t len, const Byte *bytes)
-{
-
- BufExpand(bp, len);
-
- memcpy(bp->end, bytes, len);
- bp->end += len;
- *bp->end = '\0';
-}
-
-/**
- * Get a reference to the internal buffer.
- *
- * len:
- * Pointer to where we return the number of bytes in the internal buffer.
- *
- * Returns:
- * return A pointer to the data.
- */
-Byte *
-Buf_GetAll(Buffer *bp, size_t *len)
-{
-
- if (len != NULL)
- *len = Buf_Size(bp);
-
- return (bp->buf);
-}
-
-/**
- * Get the contents of a buffer and destroy the buffer. If the buffer
- * is NULL, return NULL.
- *
- * Returns:
- * the pointer to the data.
- */
-char *
-Buf_Peel(Buffer *bp)
-{
- char *ret;
-
- if (bp == NULL)
- return (NULL);
- ret = bp->buf;
- free(bp);
- return (ret);
-}
-
-/**
- * Initialize a buffer. If no initial size is given, a reasonable
- * default is used.
- *
- * Returns:
- * A buffer object to be given to other functions in this library.
- *
- * Side Effects:
- * Space is allocated for the Buffer object and a internal buffer.
- */
-Buffer *
-Buf_Init(size_t size)
-{
- Buffer *bp; /* New Buffer */
-
- if (size <= 0)
- size = BUF_DEF_SIZE;
-
- bp = emalloc(sizeof(*bp));
- bp->size = size;
- bp->buf = emalloc(size);
- bp->end = bp->buf;
- *bp->end = '\0';
-
- return (bp);
-}
-
-/**
- * Destroy a buffer, and optionally free its data, too.
- *
- * Side Effects:
- * Space for the Buffer object and possibly the internal buffer
- * is de-allocated.
- */
-void
-Buf_Destroy(Buffer *buf, Boolean freeData)
-{
-
- if (freeData)
- free(buf->buf);
- free(buf);
-}
-
-/**
- * Replace the last byte in a buffer. If the buffer was empty
- * initially, then a new byte will be added.
- */
-void
-Buf_ReplaceLastByte(Buffer *bp, Byte byte)
-{
-
- if (bp->end == bp->buf) {
- Buf_AddByte(bp, byte);
- } else {
- *(bp->end - 1) = byte;
- }
-}
-
-/**
- * Append characters in str to Buffer object
- */
-void
-Buf_Append(Buffer *bp, const char str[])
-{
-
- Buf_AddBytes(bp, strlen(str), str);
-}
-
-/**
- * Append characters in buf to Buffer object
- */
-void
-Buf_AppendBuf(Buffer *bp, const Buffer *buf)
-{
-
- Buf_AddBytes(bp, Buf_Size(buf), buf->buf);
-}
-
-/**
- * Append characters between str and end to Buffer object.
- */
-void
-Buf_AppendRange(Buffer *bp, const char str[], const char *end)
-{
-
- Buf_AddBytes(bp, end - str, str);
-}
-
-/**
- * Convert newlines in buffer to spaces. The trailing newline is
- * removed.
- */
-void
-Buf_StripNewlines(Buffer *bp)
-{
- char *ptr = bp->end;
-
- /*
- * If there is anything in the buffer, remove the last
- * newline character.
- */
- if (ptr != bp->buf) {
- if (*(ptr - 1) == '\n') {
- /* shorten buffer */
- *(ptr - 1) = '\0';
- --bp->end;
- }
- --ptr;
- }
-
- /* Convert newline characters to a space characters. */
- while (ptr != bp->buf) {
- if (*ptr == '\n') {
- *ptr = ' ';
- }
- --ptr;
- }
-}
-/**
- * Clear the contents of the buffer.
- */
-void
-Buf_Clear(Buffer *bp)
-{
-
- bp->end = bp->buf;
- *bp->end = '\0';
-}
diff --git a/usr.bin/make/buf.h b/usr.bin/make/buf.h
deleted file mode 100644
index 106f348..0000000
--- a/usr.bin/make/buf.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*-
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1988, 1989 by Adam de Boor
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)buf.h 8.2 (Berkeley) 4/28/95
- * $FreeBSD$
- */
-
-#ifndef buf_h_a61a6812
-#define buf_h_a61a6812
-
-/*-
- * buf.h --
- * Header for users of the buf library.
- */
-
-#include <sys/types.h>
-
-#include "util.h"
-
-/*
- * There are several places where expandable buffers are used (parse.c and
- * var.c). This constant is merely the starting point for those buffers. If
- * lines tend to be much shorter than this, it would be best to reduce BSIZE.
- * If longer, it should be increased. Reducing it will cause more copying to
- * be done for longer lines, but will save space for shorter ones. In any
- * case, it ought to be a power of two simply because most storage allocation
- * schemes allocate in powers of two.
- */
-#define MAKE_BSIZE 256 /* starting size for expandable buffers */
-
-#define BUF_DEF_SIZE 256 /* Default buffer size */
-#define BUF_ADD_INC 256 /* Expansion increment when Adding */
-
-typedef char Byte;
-
-typedef struct Buffer {
- size_t size; /* Current size of the buffer */
- Byte *buf; /* The buffer itself */
- Byte *end; /* Place to write to */
-} Buffer;
-
-void Buf_AddByte(Buffer *, Byte);
-void Buf_AddBytes(Buffer *, size_t, const Byte *);
-void Buf_Append(Buffer *, const char []);
-void Buf_AppendBuf(Buffer *, const Buffer *);
-void Buf_AppendRange(Buffer *, const char [], const char *);
-void Buf_Clear(Buffer *);
-char *Buf_Data(const Buffer *);
-void Buf_Destroy(Buffer *, Boolean);
-Byte *Buf_GetAll(Buffer *, size_t *);
-Buffer *Buf_Init(size_t);
-char *Buf_Peel(Buffer *);
-void Buf_ReplaceLastByte(Buffer *, Byte);
-size_t Buf_Size(const Buffer *);
-void Buf_StripNewlines(Buffer *);
-
-#endif /* buf_h_a61a6812 */
diff --git a/usr.bin/make/cond.c b/usr.bin/make/cond.c
deleted file mode 100644
index 6e7a094..0000000
--- a/usr.bin/make/cond.c
+++ /dev/null
@@ -1,1221 +0,0 @@
-/*-
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1988, 1989 by Adam de Boor
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)cond.c 8.2 (Berkeley) 1/2/94
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*
- * Functions to handle conditionals in a makefile.
- *
- * Interface:
- * Cond_Eval Evaluate the conditional in the passed line.
- */
-
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "buf.h"
-#include "cond.h"
-#include "dir.h"
-#include "globals.h"
-#include "GNode.h"
-#include "make.h"
-#include "parse.h"
-#include "str.h"
-#include "targ.h"
-#include "util.h"
-#include "var.h"
-
-/*
- * The parsing of conditional expressions is based on this grammar:
- * E -> F || E
- * E -> F
- * F -> T && F
- * F -> T
- * T -> defined(variable)
- * T -> make(target)
- * T -> exists(file)
- * T -> empty(varspec)
- * T -> target(name)
- * T -> symbol
- * T -> $(varspec) op value
- * T -> $(varspec) == "string"
- * T -> $(varspec) != "string"
- * T -> ( E )
- * T -> ! T
- * op -> == | != | > | < | >= | <=
- *
- * 'symbol' is some other symbol to which the default function (condDefProc)
- * is applied.
- *
- * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
- * will return And for '&' and '&&', Or for '|' and '||', Not for '!',
- * LParen for '(', RParen for ')' and will evaluate the other terminal
- * symbols, using either the default function or the function given in the
- * terminal, and return the result as either True or False.
- *
- * All Non-Terminal functions (CondE, CondF and CondT) return Err on error.
- */
-typedef enum {
- And,
- Or,
- Not,
- True,
- False,
- LParen,
- RParen,
- EndOfFile,
- None,
- Err
-} Token;
-
-typedef Boolean CondProc(int, char *);
-
-/*-
- * Structures to handle elegantly the different forms of #if's. The
- * last two fields are stored in condInvert and condDefProc, respectively.
- */
-static void CondPushBack(Token);
-static int CondGetArg(char **, char **, const char *, Boolean);
-static CondProc CondDoDefined;
-static CondProc CondDoMake;
-static CondProc CondDoExists;
-static CondProc CondDoTarget;
-static char *CondCvtArg(char *, double *);
-static Token CondToken(Boolean);
-static Token CondT(Boolean);
-static Token CondF(Boolean);
-static Token CondE(Boolean);
-
-static const struct If {
- Boolean doNot; /* TRUE if default function should be negated */
- CondProc *defProc; /* Default function to apply */
- Boolean isElse; /* actually el<XXX> */
-} ifs[] = {
- [COND_IF] = { FALSE, CondDoDefined, FALSE },
- [COND_IFDEF] = { FALSE, CondDoDefined, FALSE },
- [COND_IFNDEF] = { TRUE, CondDoDefined, FALSE },
- [COND_IFMAKE] = { FALSE, CondDoMake, FALSE },
- [COND_IFNMAKE] = { TRUE, CondDoMake, FALSE },
- [COND_ELIF] = { FALSE, CondDoDefined, TRUE },
- [COND_ELIFDEF] = { FALSE, CondDoDefined, TRUE },
- [COND_ELIFNDEF] = { TRUE, CondDoDefined, TRUE },
- [COND_ELIFMAKE] = { FALSE, CondDoMake, TRUE },
- [COND_ELIFNMAKE] = { TRUE, CondDoMake, TRUE },
-};
-
-static Boolean condInvert; /* Invert the default function */
-static CondProc *condDefProc; /* default function to apply */
-static char *condExpr; /* The expression to parse */
-static Token condPushBack = None; /* Single push-back token in parsing */
-
-#define MAXIF 30 /* greatest depth of #if'ing */
-
-static Boolean condStack[MAXIF]; /* Stack of conditionals's values */
-static int condLineno[MAXIF]; /* Line numbers of the opening .if */
-static int condTop = MAXIF; /* Top-most conditional */
-static int skipIfLevel = 0; /* Depth of skipped conditionals */
-static int skipIfLineno[MAXIF]; /* Line numbers of skipped .ifs */
-Boolean skipLine = FALSE; /* Whether the parse module is skipping
- * lines */
-
-/**
- * CondPushBack
- * Push back the most recent token read. We only need one level of
- * this, so the thing is just stored in 'condPushback'.
- *
- * Side Effects:
- * condPushback is overwritten.
- */
-static void
-CondPushBack(Token t)
-{
-
- condPushBack = t;
-}
-
-/**
- * CondGetArg
- * Find the argument of a built-in function. parens is set to TRUE
- * if the arguments are bounded by parens.
- *
- * Results:
- * The length of the argument and the address of the argument.
- *
- * Side Effects:
- * The pointer is set to point to the closing parenthesis of the
- * function call.
- */
-static int
-CondGetArg(char **linePtr, char **argPtr, const char *func, Boolean parens)
-{
- char *cp;
- size_t argLen;
- Buffer *buf;
-
- cp = *linePtr;
- if (parens) {
- while (*cp != '(' && *cp != '\0') {
- cp++;
- }
- if (*cp == '(') {
- cp++;
- }
- }
-
- if (*cp == '\0') {
- /*
- * No arguments whatsoever. Because 'make' and 'defined'
- * aren't really "reserved words", we don't print a message.
- * I think this is better than hitting the user with a warning
- * message every time s/he uses the word 'make' or 'defined'
- * at the beginning of a symbol...
- */
- *argPtr = cp;
- return (0);
- }
-
- while (*cp == ' ' || *cp == '\t') {
- cp++;
- }
-
- /*
- * Create a buffer for the argument and start it out at 16 characters
- * long. Why 16? Why not?
- */
- buf = Buf_Init(16);
-
- while ((strchr(" \t)&|", *cp) == NULL) && (*cp != '\0')) {
- if (*cp == '$') {
- /*
- * Parse the variable spec and install it as part of
- * the argument if it's valid. We tell Var_Parse to
- * complain on an undefined variable, so we don't do
- * it too. Nor do we return an error, though perhaps
- * we should...
- */
- char *cp2;
- size_t len = 0;
- Boolean doFree;
-
- cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree);
-
- Buf_Append(buf, cp2);
- if (doFree) {
- free(cp2);
- }
- cp += len;
- } else {
- Buf_AddByte(buf, (Byte)*cp);
- cp++;
- }
- }
-
- Buf_AddByte(buf, (Byte)'\0');
- *argPtr = (char *)Buf_GetAll(buf, &argLen);
- Buf_Destroy(buf, FALSE);
-
- while (*cp == ' ' || *cp == '\t') {
- cp++;
- }
- if (parens && *cp != ')') {
- Parse_Error(PARSE_WARNING,
- "Missing closing parenthesis for %s()", func);
- return (0);
- } else if (parens) {
- /*
- * Advance pointer past close parenthesis.
- */
- cp++;
- }
-
- *linePtr = cp;
- return (argLen);
-}
-
-/**
- * CondDoDefined
- * Handle the 'defined' function for conditionals.
- *
- * Results:
- * TRUE if the given variable is defined.
- */
-static Boolean
-CondDoDefined(int argLen, char *arg)
-{
- char savec = arg[argLen];
- Boolean result;
-
- arg[argLen] = '\0';
- if (Var_Value(arg, VAR_CMD) != NULL) {
- result = TRUE;
- } else {
- result = FALSE;
- }
- arg[argLen] = savec;
- return (result);
-}
-
-/**
- * CondDoMake
- * Handle the 'make' function for conditionals.
- *
- * Results:
- * TRUE if the given target is being made.
- */
-static Boolean
-CondDoMake(int argLen, char *arg)
-{
- char savec = arg[argLen];
- Boolean result;
- const LstNode *ln;
-
- arg[argLen] = '\0';
- result = FALSE;
- LST_FOREACH(ln, &create) {
- if (Str_Match(Lst_Datum(ln), arg)) {
- result = TRUE;
- break;
- }
- }
- arg[argLen] = savec;
- return (result);
-}
-
-/**
- * CondDoExists
- * See if the given file exists.
- *
- * Results:
- * TRUE if the file exists and FALSE if it does not.
- */
-static Boolean
-CondDoExists(int argLen, char *arg)
-{
- char savec = arg[argLen];
- Boolean result;
- char *path;
-
- arg[argLen] = '\0';
- path = Path_FindFile(arg, &dirSearchPath);
- if (path != NULL) {
- result = TRUE;
- free(path);
- } else {
- result = FALSE;
- }
- arg[argLen] = savec;
- return (result);
-}
-
-/**
- * CondDoTarget
- * See if the given node exists and is an actual target.
- *
- * Results:
- * TRUE if the node exists as a target and FALSE if it does not.
- */
-static Boolean
-CondDoTarget(int argLen, char *arg)
-{
- char savec = arg[argLen];
- Boolean result;
- GNode *gn;
-
- arg[argLen] = '\0';
- gn = Targ_FindNode(arg, TARG_NOCREATE);
- if ((gn != NULL) && !OP_NOP(gn->type)) {
- result = TRUE;
- } else {
- result = FALSE;
- }
- arg[argLen] = savec;
- return (result);
-}
-
-/**
- * CondCvtArg
- * Convert the given number into a double. If the number begins
- * with 0x, it is interpreted as a hexadecimal integer
- * and converted to a double from there. All other strings just have
- * strtod called on them.
- *
- * Results:
- * Sets 'value' to double value of string.
- * Returns address of the first character after the last valid
- * character of the converted number.
- *
- * Side Effects:
- * Can change 'value' even if string is not a valid number.
- */
-static char *
-CondCvtArg(char *str, double *value)
-{
-
- if ((*str == '0') && (str[1] == 'x')) {
- long i;
-
- for (str += 2, i = 0; ; str++) {
- int x;
-
- if (isdigit((unsigned char)*str))
- x = *str - '0';
- else if (isxdigit((unsigned char)*str))
- x = 10 + *str -
- isupper((unsigned char)*str) ? 'A' : 'a';
- else {
- *value = (double)i;
- return (str);
- }
- i = (i << 4) + x;
- }
-
- } else {
- char *eptr;
-
- *value = strtod(str, &eptr);
- return (eptr);
- }
-}
-
-/**
- * CondToken
- * Return the next token from the input.
- *
- * Results:
- * A Token for the next lexical token in the stream.
- *
- * Side Effects:
- * condPushback will be set back to None if it is used.
- */
-static Token
-CondToken(Boolean doEval)
-{
- Token t;
-
- if (condPushBack != None) {
- t = condPushBack;
- condPushBack = None;
- return (t);
- }
-
- while (*condExpr == ' ' || *condExpr == '\t') {
- condExpr++;
- }
- switch (*condExpr) {
- case '(':
- t = LParen;
- condExpr++;
- break;
- case ')':
- t = RParen;
- condExpr++;
- break;
- case '|':
- if (condExpr[1] == '|') {
- condExpr++;
- }
- condExpr++;
- t = Or;
- break;
- case '&':
- if (condExpr[1] == '&') {
- condExpr++;
- }
- condExpr++;
- t = And;
- break;
- case '!':
- t = Not;
- condExpr++;
- break;
- case '\n':
- case '\0':
- t = EndOfFile;
- break;
- case '$': {
- char *lhs;
- const char *op;
- char *rhs;
- char zero[] = "0";
- size_t varSpecLen = 0;
- Boolean doFree;
-
- /*
- * Parse the variable spec and skip over it, saving its
- * value in lhs.
- */
- t = Err;
- lhs = Var_Parse(condExpr, VAR_CMD, doEval,
- &varSpecLen, &doFree);
- if (lhs == var_Error) {
- /*
- * Even if !doEval, we still report syntax
- * errors, which is what getting var_Error
- * back with !doEval means.
- */
- return (Err);
- }
- condExpr += varSpecLen;
-
- if (!isspace((unsigned char)*condExpr) &&
- strchr("!=><", *condExpr) == NULL) {
- Buffer *buf;
-
- buf = Buf_Init(0);
-
- Buf_Append(buf, lhs);
-
- if (doFree)
- free(lhs);
-
- for (;*condExpr &&
- !isspace((unsigned char)*condExpr);
- condExpr++)
- Buf_AddByte(buf, (Byte)*condExpr);
-
- Buf_AddByte(buf, (Byte)'\0');
- lhs = (char *)Buf_GetAll(buf, &varSpecLen);
- Buf_Destroy(buf, FALSE);
-
- doFree = TRUE;
- }
-
- /*
- * Skip whitespace to get to the operator
- */
- while (isspace((unsigned char)*condExpr))
- condExpr++;
-
- /*
- * Make sure the operator is a valid one. If it isn't a
- * known relational operator, pretend we got a
- * != 0 comparison.
- */
- op = condExpr;
- switch (*condExpr) {
- case '!':
- case '=':
- case '<':
- case '>':
- if (condExpr[1] == '=') {
- condExpr += 2;
- } else {
- condExpr += 1;
- }
- while (isspace((unsigned char)*condExpr)) {
- condExpr++;
- }
- if (*condExpr == '\0') {
- Parse_Error(PARSE_WARNING,
- "Missing right-hand-side of operator");
- goto error;
- }
- rhs = condExpr;
- break;
-
- default:
- op = "!=";
- rhs = zero;
- break;
- }
- if (*rhs == '"') {
- /*
- * Doing a string comparison. Only allow == and
- * != for * operators.
- */
- char *string;
- char *cp, *cp2;
- int qt;
- Buffer *buf;
-
- do_string_compare:
- if (((*op != '!') && (*op != '=')) ||
- (op[1] != '=')) {
- Parse_Error(PARSE_WARNING,
- "String comparison operator should "
- "be either == or !=");
- goto error;
- }
-
- buf = Buf_Init(0);
- qt = *rhs == '"' ? 1 : 0;
-
- for (cp = &rhs[qt];
- ((qt && (*cp != '"')) ||
- (!qt && strchr(" \t)", *cp) == NULL)) &&
- (*cp != '\0'); cp++) {
- if ((*cp == '\\') && (cp[1] != '\0')) {
- /*
- * Backslash escapes things --
- * skip over next character, * if it exists.
- */
- cp++;
- Buf_AddByte(buf, (Byte)*cp);
-
- } else if (*cp == '$') {
- size_t len = 0;
- Boolean freeIt;
-
- cp2 = Var_Parse(cp, VAR_CMD,
- doEval, &len, &freeIt);
- if (cp2 != var_Error) {
- Buf_Append(buf, cp2);
- if (freeIt) {
- free(cp2);
- }
- cp += len - 1;
- } else {
- Buf_AddByte(buf,
- (Byte)*cp);
- }
- } else {
- Buf_AddByte(buf, (Byte)*cp);
- }
- }
-
- string = Buf_Peel(buf);
-
- DEBUGF(COND, ("lhs = \"%s\", rhs = \"%s\", "
- "op = %.2s\n", lhs, string, op));
- /*
- * Null-terminate rhs and perform the
- * comparison. t is set to the result.
- */
- if (*op == '=') {
- t = strcmp(lhs, string) ? False : True;
- } else {
- t = strcmp(lhs, string) ? True : False;
- }
- free(string);
- if (rhs == condExpr) {
- if (*cp == '\0' || (!qt && *cp == ')'))
- condExpr = cp;
- else
- condExpr = cp + 1;
- }
- } else {
- /*
- * rhs is either a float or an integer.
- * Convert both the lhs and the rhs to a
- * double and compare the two.
- */
- double left, right;
- char *string;
-
- if (*CondCvtArg(lhs, &left) != '\0')
- goto do_string_compare;
- if (*rhs == '$') {
- size_t len = 0;
- Boolean freeIt;
-
- string = Var_Parse(rhs, VAR_CMD, doEval,
- &len, &freeIt);
- if (string == var_Error) {
- right = 0.0;
- } else {
- if (*CondCvtArg(string,
- &right) != '\0') {
- if (freeIt)
- free(string);
- goto do_string_compare;
- }
- if (freeIt)
- free(string);
- if (rhs == condExpr)
- condExpr += len;
- }
- } else {
- char *c = CondCvtArg(rhs, &right);
-
- if (c == rhs)
- goto do_string_compare;
- if (rhs == condExpr) {
- /*
- * Skip over the right-hand side
- */
- condExpr = c;
- }
- }
-
- DEBUGF(COND, ("left = %f, right = %f, "
- "op = %.2s\n", left, right, op));
- switch (op[0]) {
- case '!':
- if (op[1] != '=') {
- Parse_Error(PARSE_WARNING,
- "Unknown operator");
- goto error;
- }
- t = (left != right ? True : False);
- break;
- case '=':
- if (op[1] != '=') {
- Parse_Error(PARSE_WARNING,
- "Unknown operator");
- goto error;
- }
- t = (left == right ? True : False);
- break;
- case '<':
- if (op[1] == '=') {
- t = (left <= right?True:False);
- } else {
- t = (left < right?True:False);
- }
- break;
- case '>':
- if (op[1] == '=') {
- t = (left >= right?True:False);
- } else {
- t = (left > right?True:False);
- }
- break;
- default:
- break;
- }
- }
- error:
- if (doFree)
- free(lhs);
- break;
- }
-
- default: {
- CondProc *evalProc;
- Boolean invert = FALSE;
- char *arg;
- int arglen;
-
- if (strncmp(condExpr, "defined", 7) == 0) {
- /*
- * Use CondDoDefined to evaluate the argument
- * and CondGetArg to extract the argument from
- * the 'function call'.
- */
- evalProc = CondDoDefined;
- condExpr += 7;
- arglen = CondGetArg(&condExpr, &arg,
- "defined", TRUE);
- if (arglen == 0) {
- condExpr -= 7;
- goto use_default;
- }
-
- } else if (strncmp(condExpr, "make", 4) == 0) {
- /*
- * Use CondDoMake to evaluate the argument and
- * CondGetArg to extract the argument from the
- * 'function call'.
- */
- evalProc = CondDoMake;
- condExpr += 4;
- arglen = CondGetArg(&condExpr, &arg,
- "make", TRUE);
- if (arglen == 0) {
- condExpr -= 4;
- goto use_default;
- }
-
- } else if (strncmp(condExpr, "exists", 6) == 0) {
- /*
- * Use CondDoExists to evaluate the argument and
- * CondGetArg to extract the argument from the
- * 'function call'.
- */
- evalProc = CondDoExists;
- condExpr += 6;
- arglen = CondGetArg(&condExpr, &arg,
- "exists", TRUE);
- if (arglen == 0) {
- condExpr -= 6;
- goto use_default;
- }
-
- } else if (strncmp(condExpr, "empty", 5) == 0) {
- /*
- * Use Var_Parse to parse the spec in parens and
- * return True if the resulting string is empty.
- */
- size_t length;
- Boolean doFree;
- char *val;
-
- condExpr += 5;
-
- for (arglen = 0;
- condExpr[arglen] != '(' &&
- condExpr[arglen] != '\0'; arglen += 1)
- continue;
-
- if (condExpr[arglen] != '\0') {
- length = 0;
- val = Var_Parse(&condExpr[arglen - 1],
- VAR_CMD, FALSE, &length, &doFree);
- if (val == var_Error) {
- t = Err;
- } else {
- /*
- * A variable is empty when it
- * just contains spaces...
- * 4/15/92, christos
- */
- char *p;
-
- for (p = val;
- *p &&
- isspace((unsigned char)*p);
- p++)
- continue;
- t = (*p == '\0') ? True : False;
- }
- if (doFree) {
- free(val);
- }
- /*
- * Advance condExpr to beyond the
- * closing ). Note that we subtract
- * one from arglen + length b/c length
- * is calculated from
- * condExpr[arglen - 1].
- */
- condExpr += arglen + length - 1;
- } else {
- condExpr -= 5;
- goto use_default;
- }
- break;
-
- } else if (strncmp(condExpr, "target", 6) == 0) {
- /*
- * Use CondDoTarget to evaluate the argument and
- * CondGetArg to extract the argument from the
- * 'function call'.
- */
- evalProc = CondDoTarget;
- condExpr += 6;
- arglen = CondGetArg(&condExpr, &arg,
- "target", TRUE);
- if (arglen == 0) {
- condExpr -= 6;
- goto use_default;
- }
-
- } else {
- /*
- * The symbol is itself the argument to the
- * default function. We advance condExpr to
- * the end of the symbol by hand (the next
- * whitespace, closing paren or binary operator)
- * and set to invert the evaluation
- * function if condInvert is TRUE.
- */
- use_default:
- invert = condInvert;
- evalProc = condDefProc;
- arglen = CondGetArg(&condExpr, &arg, "", FALSE);
- }
-
- /*
- * Evaluate the argument using the set function. If
- * invert is TRUE, we invert the sense of the function.
- */
- t = (!doEval || (* evalProc) (arglen, arg) ?
- (invert ? False : True) :
- (invert ? True : False));
- free(arg);
- break;
- }
- }
- return (t);
-}
-
-/**
- * CondT
- * Parse a single term in the expression. This consists of a terminal
- * symbol or Not and a terminal symbol (not including the binary
- * operators):
- * T -> defined(variable) | make(target) | exists(file) | symbol
- * T -> ! T | ( E )
- *
- * Results:
- * True, False or Err.
- *
- * Side Effects:
- * Tokens are consumed.
- */
-static Token
-CondT(Boolean doEval)
-{
- Token t;
-
- t = CondToken(doEval);
- if (t == EndOfFile) {
- /*
- * If we reached the end of the expression, the expression
- * is malformed...
- */
- t = Err;
- } else if (t == LParen) {
- /*
- * T -> ( E )
- */
- t = CondE(doEval);
- if (t != Err) {
- if (CondToken(doEval) != RParen) {
- t = Err;
- }
- }
- } else if (t == Not) {
- t = CondT(doEval);
- if (t == True) {
- t = False;
- } else if (t == False) {
- t = True;
- }
- }
- return (t);
-}
-
-/**
- * CondF --
- * Parse a conjunctive factor (nice name, wot?)
- * F -> T && F | T
- *
- * Results:
- * True, False or Err
- *
- * Side Effects:
- * Tokens are consumed.
- */
-static Token
-CondF(Boolean doEval)
-{
- Token l, o;
-
- l = CondT(doEval);
- if (l != Err) {
- o = CondToken(doEval);
-
- if (o == And) {
- /*
- * F -> T && F
- *
- * If T is False, the whole thing will be False, but
- * we have to parse the r.h.s. anyway (to throw it
- * away). If T is True, the result is the r.h.s.,
- * be it an Err or no.
- */
- if (l == True) {
- l = CondF(doEval);
- } else {
- CondF(FALSE);
- }
- } else {
- /*
- * F -> T
- */
- CondPushBack(o);
- }
- }
- return (l);
-}
-
-/**
- * CondE --
- * Main expression production.
- * E -> F || E | F
- *
- * Results:
- * True, False or Err.
- *
- * Side Effects:
- * Tokens are, of course, consumed.
- */
-static Token
-CondE(Boolean doEval)
-{
- Token l, o;
-
- l = CondF(doEval);
- if (l != Err) {
- o = CondToken(doEval);
-
- if (o == Or) {
- /*
- * E -> F || E
- *
- * A similar thing occurs for ||, except that here we
- * make sure the l.h.s. is False before we bother to
- * evaluate the r.h.s. Once again, if l is False, the
- * result is the r.h.s. and once again if l is True,
- * we parse the r.h.s. to throw it away.
- */
- if (l == False) {
- l = CondE(doEval);
- } else {
- CondE(FALSE);
- }
- } else {
- /*
- * E -> F
- */
- CondPushBack(o);
- }
- }
- return (l);
-}
-
-/**
- * Cond_If
- * Handle .if<X> and .elif<X> directives.
- * This function is called even when we're skipping.
- */
-void
-Cond_If(char *line, int code, int lineno)
-{
- const struct If *ifp;
- Boolean value;
-
- ifp = &ifs[code];
-
- if (ifp->isElse) {
- if (condTop == MAXIF) {
- Parse_Error(PARSE_FATAL, "if-less elif");
- return;
- }
- if (skipIfLevel != 0) {
- /*
- * If skipping this conditional, just ignore
- * the whole thing. If we don't, the user
- * might be employing a variable that's
- * undefined, for which there's an enclosing
- * ifdef that we're skipping...
- */
- skipIfLineno[skipIfLevel - 1] = lineno;
- return;
- }
-
- } else if (skipLine) {
- /*
- * Don't even try to evaluate a conditional that's
- * not an else if we're skipping things...
- */
- skipIfLineno[skipIfLevel] = lineno;
- skipIfLevel += 1;
- return;
- }
-
- /*
- * Initialize file-global variables for parsing
- */
- condDefProc = ifp->defProc;
- condInvert = ifp->doNot;
-
- while (*line == ' ' || *line == '\t') {
- line++;
- }
-
- condExpr = line;
- condPushBack = None;
-
- switch (CondE(TRUE)) {
- case True:
- if (CondToken(TRUE) != EndOfFile)
- goto err;
- value = TRUE;
- break;
-
- case False:
- if (CondToken(TRUE) != EndOfFile)
- goto err;
- value = FALSE;
- break;
-
- case Err:
- err: Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line);
- return;
-
- default:
- abort();
- }
-
- if (!ifp->isElse) {
- /* push this value */
- condTop -= 1;
-
- } else if (skipIfLevel != 0 || condStack[condTop]) {
- /*
- * If this is an else-type conditional, it should only take
- * effect if its corresponding if was evaluated and FALSE.
- * If its if was TRUE or skipped, we return COND_SKIP (and
- * start skipping in case we weren't already), leaving the
- * stack unmolested so later elif's don't screw up...
- */
- skipLine = TRUE;
- return;
- }
-
- if (condTop < 0) {
- /*
- * This is the one case where we can definitely proclaim a fatal
- * error. If we don't, we're hosed.
- */
- Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.",MAXIF);
- return;
- }
-
- /* push */
- condStack[condTop] = value;
- condLineno[condTop] = lineno;
- skipLine = !value;
-}
-
-/**
- * Cond_Else
- * Handle .else statement.
- */
-void
-Cond_Else(char *line __unused, int code __unused, int lineno __unused)
-{
-
- while (isspace((u_char)*line))
- line++;
-
- if (*line != '\0' && (warn_flags & WARN_DIRSYNTAX)) {
- Parse_Error(PARSE_WARNING, "junk after .else ignored '%s'",
- line);
- }
-
- if (condTop == MAXIF) {
- Parse_Error(PARSE_FATAL, "if-less else");
- return;
- }
- if (skipIfLevel != 0)
- return;
-
- if (skipIfLevel != 0 || condStack[condTop]) {
- /*
- * An else should only take effect if its corresponding if was
- * evaluated and FALSE.
- * If its if was TRUE or skipped, we return COND_SKIP (and
- * start skipping in case we weren't already), leaving the
- * stack unmolested so later elif's don't screw up...
- * XXX How does this work with two .else's?
- */
- skipLine = TRUE;
- return;
- }
-
- /* inverse value */
- condStack[condTop] = !condStack[condTop];
- skipLine = !condStack[condTop];
-}
-
-/**
- * Cond_Endif
- * Handle .endif statement.
- */
-void
-Cond_Endif(char *line __unused, int code __unused, int lineno __unused)
-{
-
- while (isspace((u_char)*line))
- line++;
-
- if (*line != '\0' && (warn_flags & WARN_DIRSYNTAX)) {
- Parse_Error(PARSE_WARNING, "junk after .endif ignored '%s'",
- line);
- }
-
- /*
- * End of a conditional section. If skipIfLevel is non-zero,
- * that conditional was skipped, so lines following it should
- * also be skipped. Hence, we return COND_SKIP. Otherwise,
- * the conditional was read so succeeding lines should be
- * parsed (think about it...) so we return COND_PARSE, unless
- * this endif isn't paired with a decent if.
- */
- if (skipIfLevel != 0) {
- skipIfLevel -= 1;
- return;
- }
-
- if (condTop == MAXIF) {
- Parse_Error(PARSE_FATAL, "if-less endif");
- return;
- }
-
- /* pop */
- skipLine = FALSE;
- condTop += 1;
-}
-
-/**
- * Cond_End
- * Make sure everything's clean at the end of a makefile.
- *
- * Side Effects:
- * Parse_Error will be called if open conditionals are around.
- */
-void
-Cond_End(void)
-{
- int level;
-
- if (condTop != MAXIF) {
- Parse_Error(PARSE_FATAL, "%d open conditional%s:",
- MAXIF - condTop + skipIfLevel,
- MAXIF - condTop + skipIfLevel== 1 ? "" : "s");
-
- for (level = skipIfLevel; level > 0; level--)
- Parse_Error(PARSE_FATAL, "\t%*sat line %d (skipped)",
- MAXIF - condTop + level + 1, "",
- skipIfLineno[level - 1]);
- for (level = condTop; level < MAXIF; level++)
- Parse_Error(PARSE_FATAL, "\t%*sat line %d "
- "(evaluated to %s)", MAXIF - level + skipIfLevel,
- "", condLineno[level],
- condStack[level] ? "true" : "false");
- }
- condTop = MAXIF;
-}
diff --git a/usr.bin/make/cond.h b/usr.bin/make/cond.h
deleted file mode 100644
index 9a8dbdc..0000000
--- a/usr.bin/make/cond.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * $FreeBSD$
- */
-
-#ifndef cond_h_6e96ad7c
-#define cond_h_6e96ad7c
-
-/*
- * Values returned by Cond_Eval.
- */
-#define COND_PARSE 0 /* Parse the next lines */
-#define COND_SKIP 1 /* Skip the next lines */
-#define COND_INVALID 2 /* Not a conditional statement */
-
-enum {
- COND_IF,
- COND_IFDEF,
- COND_IFNDEF,
- COND_IFMAKE,
- COND_IFNMAKE,
- COND_ELSE,
- COND_ELIF,
- COND_ELIFDEF,
- COND_ELIFNDEF,
- COND_ELIFMAKE,
- COND_ELIFNMAKE,
- COND_ENDIF,
-};
-
-void Cond_If(char *, int, int);
-void Cond_Else(char *, int, int);
-void Cond_Endif(char *, int, int);
-void Cond_End(void);
-
-extern Boolean skipLine;
-
-#endif /* cond_h_6e96ad7c */
diff --git a/usr.bin/make/config.h b/usr.bin/make/config.h
deleted file mode 100644
index 06b0f8e..0000000
--- a/usr.bin/make/config.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1988, 1989 by Adam de Boor
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)config.h 8.2 (Berkeley) 4/28/95
- * $FreeBSD$
- */
-
-#ifndef config_h_efe0765e
-#define config_h_efe0765e
-
-/*
- * DEFMAXJOBS
- * This control the default concurrency. On no occasion will more
- * than DEFMAXJOBS targets be created at once.
- */
-#define DEFMAXJOBS 1
-
-/*
- * INCLUDES
- * LIBRARIES
- * These control the handling of the .INCLUDES and .LIBS variables.
- * If INCLUDES is defined, the .INCLUDES variable will be filled
- * from the search paths of those suffixes which are marked by
- * .INCLUDES dependency lines. Similarly for LIBRARIES and .LIBS
- * See suff.c for more details.
- */
-#define INCLUDES
-#define LIBRARIES
-
-/*
- * LIBSUFF
- * Is the suffix used to denote libraries and is used by the Suff module
- * to find the search path on which to seek any -l<xx> targets.
- *
- * RECHECK
- * If defined, Make_Update will check a target for its current
- * modification time after it has been re-made, setting it to the
- * starting time of the make only if the target still doesn't exist.
- * Unfortunately, under NFS the modification time often doesn't
- * get updated in time, so a target will appear to not have been
- * re-made, causing later targets to appear up-to-date. On systems
- * that don't have this problem, you should defined this. Under
- * NFS you probably should not, unless you aren't exporting jobs.
- */
-#define LIBSUFF ".a"
-#define RECHECK
-
-/*
- * SYSVINCLUDE
- * Recognize system V like include directives [include "filename"]
- * SYSVVARSUB
- * Recognize system V like ${VAR:x=y} variable substitutions
- */
-#define SYSVINCLUDE
-#define SYSVVARSUB
-
-/*
- * SUNSHCMD
- * Recognize SunOS and Solaris:
- * VAR :sh= CMD # Assign VAR to the command substitution of CMD
- * ${VAR:sh} # Return the command substitution of the value
- * # of ${VAR}
- */
-#define SUNSHCMD
-
-#if !defined(__svr4__) && !defined(__SVR4) && !defined(__ELF__)
-# ifndef RANLIBMAG
-# define RANLIBMAG "__.SYMDEF"
-# endif
-#else
-# ifndef RANLIBMAG
-# define RANLIBMAG "/"
-# endif
-#endif
-
-#endif /* config_h_efe0765e */
diff --git a/usr.bin/make/dir.c b/usr.bin/make/dir.c
deleted file mode 100644
index 7cee3d4..0000000
--- a/usr.bin/make/dir.c
+++ /dev/null
@@ -1,1217 +0,0 @@
-/*-
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1988, 1989 by Adam de Boor
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)dir.c 8.2 (Berkeley) 1/2/94
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*-
- * dir.c --
- * Directory searching using wildcards and/or normal names...
- * Used both for source wildcarding in the Makefile and for finding
- * implicit sources.
- *
- * The interface for this module is:
- * Dir_Init Initialize the module.
- *
- * Dir_HasWildcards Returns TRUE if the name given it needs to
- * be wildcard-expanded.
- *
- * Path_Expand Given a pattern and a path, return a Lst of names
- * which match the pattern on the search path.
- *
- * Path_FindFile Searches for a file on a given search path.
- * If it exists, the entire path is returned.
- * Otherwise NULL is returned.
- *
- * Dir_FindHereOrAbove Search for a path in the current directory and
- * then all the directories above it in turn until
- * the path is found or we reach the root ("/").
- *
- * Dir_MTime Return the modification time of a node. The file
- * is searched for along the default search path.
- * The path and mtime fields of the node are filled in.
- *
- * Path_AddDir Add a directory to a search path.
- *
- * Dir_MakeFlags Given a search path and a command flag, create
- * a string with each of the directories in the path
- * preceded by the command flag and all of them
- * separated by a space.
- *
- * Dir_Destroy Destroy an element of a search path. Frees up all
- * things that can be freed for the element as long
- * as the element is no longer referenced by any other
- * search path.
- *
- * Dir_ClearPath Resets a search path to the empty list.
- *
- * For debugging:
- * Dir_PrintDirectories Print stats about the directory cache.
- */
-
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <err.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "arch.h"
-#include "dir.h"
-#include "globals.h"
-#include "GNode.h"
-#include "hash.h"
-#include "lst.h"
-#include "str.h"
-#include "targ.h"
-#include "util.h"
-
-/*
- * A search path consists of a list of Dir structures. A Dir structure
- * has in it the name of the directory and a hash table of all the files
- * in the directory. This is used to cut down on the number of system
- * calls necessary to find implicit dependents and their like. Since
- * these searches are made before any actions are taken, we need not
- * worry about the directory changing due to creation commands. If this
- * hampers the style of some makefiles, they must be changed.
- *
- * A list of all previously-read directories is kept in the
- * openDirectories list. This list is checked first before a directory
- * is opened.
- *
- * The need for the caching of whole directories is brought about by
- * the multi-level transformation code in suff.c, which tends to search
- * for far more files than regular make does. In the initial
- * implementation, the amount of time spent performing "stat" calls was
- * truly astronomical. The problem with hashing at the start is,
- * of course, that pmake doesn't then detect changes to these directories
- * during the course of the make. Three possibilities suggest themselves:
- *
- * 1) just use stat to test for a file's existence. As mentioned
- * above, this is very inefficient due to the number of checks
- * engendered by the multi-level transformation code.
- * 2) use readdir() and company to search the directories, keeping
- * them open between checks. I have tried this and while it
- * didn't slow down the process too much, it could severely
- * affect the amount of parallelism available as each directory
- * open would take another file descriptor out of play for
- * handling I/O for another job. Given that it is only recently
- * that UNIX OS's have taken to allowing more than 20 or 32
- * file descriptors for a process, this doesn't seem acceptable
- * to me.
- * 3) record the mtime of the directory in the Dir structure and
- * verify the directory hasn't changed since the contents were
- * hashed. This will catch the creation or deletion of files,
- * but not the updating of files. However, since it is the
- * creation and deletion that is the problem, this could be
- * a good thing to do. Unfortunately, if the directory (say ".")
- * were fairly large and changed fairly frequently, the constant
- * rehashing could seriously degrade performance. It might be
- * good in such cases to keep track of the number of rehashes
- * and if the number goes over a (small) limit, resort to using
- * stat in its place.
- *
- * An additional thing to consider is that pmake is used primarily
- * to create C programs and until recently pcc-based compilers refused
- * to allow you to specify where the resulting object file should be
- * placed. This forced all objects to be created in the current
- * directory. This isn't meant as a full excuse, just an explanation of
- * some of the reasons for the caching used here.
- *
- * One more note: the location of a target's file is only performed
- * on the downward traversal of the graph and then only for terminal
- * nodes in the graph. This could be construed as wrong in some cases,
- * but prevents inadvertent modification of files when the "installed"
- * directory for a file is provided in the search path.
- *
- * Another data structure maintained by this module is an mtime
- * cache used when the searching of cached directories fails to find
- * a file. In the past, Path_FindFile would simply perform an access()
- * call in such a case to determine if the file could be found using
- * just the name given. When this hit, however, all that was gained
- * was the knowledge that the file existed. Given that an access() is
- * essentially a stat() without the copyout() call, and that the same
- * filesystem overhead would have to be incurred in Dir_MTime, it made
- * sense to replace the access() with a stat() and record the mtime
- * in a cache for when Dir_MTime was actually called.
- */
-
-typedef struct Dir {
- char *name; /* Name of directory */
- int refCount; /* No. of paths with this directory */
- int hits; /* No. of times a file has been found here */
- Hash_Table files; /* Hash table of files in directory */
- TAILQ_ENTRY(Dir) link; /* allDirs link */
-} Dir;
-
-/*
- * A path is a list of pointers to directories. These directories are
- * reference counted so a directory can be on more than one path.
- */
-struct PathElement {
- struct Dir *dir; /* pointer to the directory */
- TAILQ_ENTRY(PathElement) link; /* path link */
-};
-
-/* main search path */
-struct Path dirSearchPath = TAILQ_HEAD_INITIALIZER(dirSearchPath);
-
-/* the list of all open directories */
-static TAILQ_HEAD(, Dir) openDirectories =
- TAILQ_HEAD_INITIALIZER(openDirectories);
-
-/*
- * Variables for gathering statistics on the efficiency of the hashing
- * mechanism.
- */
-static int hits; /* Found in directory cache */
-static int misses; /* Sad, but not evil misses */
-static int nearmisses; /* Found under search path */
-static int bigmisses; /* Sought by itself */
-
-static Dir *dot; /* contents of current directory */
-
-/* Results of doing a last-resort stat in Path_FindFile --
- * if we have to go to the system to find the file, we might as well
- * have its mtime on record.
- * XXX: If this is done way early, there's a chance other rules will
- * have already updated the file, in which case we'll update it again.
- * Generally, there won't be two rules to update a single file, so this
- * should be ok, but...
- */
-static Hash_Table mtimes;
-
-/*-
- *-----------------------------------------------------------------------
- * Dir_Init --
- * initialize things for this module
- *
- * Results:
- * none
- *
- * Side Effects:
- * none
- *-----------------------------------------------------------------------
- */
-void
-Dir_Init(void)
-{
-
- Hash_InitTable(&mtimes, 0);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Dir_InitDot --
- * initialize the "." directory
- *
- * Results:
- * none
- *
- * Side Effects:
- * some directories may be opened.
- *-----------------------------------------------------------------------
- */
-void
-Dir_InitDot(void)
-{
-
- dot = Path_AddDir(NULL, ".");
- if (dot == NULL)
- err(1, "cannot open current directory");
-
- /*
- * We always need to have dot around, so we increment its
- * reference count to make sure it's not destroyed.
- */
- dot->refCount += 1;
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Dir_HasWildcards --
- * See if the given name has any wildcard characters in it.
- *
- * Results:
- * returns TRUE if the word should be expanded, FALSE otherwise
- *
- * Side Effects:
- * none
- *-----------------------------------------------------------------------
- */
-Boolean
-Dir_HasWildcards(const char *name)
-{
- const char *cp;
- int wild = 0, brace = 0, bracket = 0;
-
- for (cp = name; *cp; cp++) {
- switch (*cp) {
- case '{':
- brace++;
- wild = 1;
- break;
- case '}':
- brace--;
- break;
- case '[':
- bracket++;
- wild = 1;
- break;
- case ']':
- bracket--;
- break;
- case '?':
- case '*':
- wild = 1;
- break;
- default:
- break;
- }
- }
- return (wild && bracket == 0 && brace == 0);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * DirMatchFiles --
- * Given a pattern and a Dir structure, see if any files
- * match the pattern and add their names to the 'expansions' list if
- * any do. This is incomplete -- it doesn't take care of patterns like
- * src / *src / *.c properly (just *.c on any of the directories), but it
- * will do for now.
- *
- * Results:
- * Always returns 0
- *
- * Side Effects:
- * File names are added to the expansions lst. The directory will be
- * fully hashed when this is done.
- *-----------------------------------------------------------------------
- */
-static int
-DirMatchFiles(const char *pattern, const Dir *p, Lst *expansions)
-{
- Hash_Search search; /* Index into the directory's table */
- Hash_Entry *entry; /* Current entry in the table */
- Boolean isDot; /* TRUE if the directory being searched is . */
-
- isDot = (*p->name == '.' && p->name[1] == '\0');
-
- for (entry = Hash_EnumFirst(&p->files, &search);
- entry != NULL;
- entry = Hash_EnumNext(&search)) {
- /*
- * See if the file matches the given pattern. Note we follow
- * the UNIX convention that dot files will only be found if
- * the pattern begins with a dot (note also that as a side
- * effect of the hashing scheme, .* won't match . or ..
- * since they aren't hashed).
- */
- if (Str_Match(entry->name, pattern) &&
- ((entry->name[0] != '.') ||
- (pattern[0] == '.'))) {
- Lst_AtEnd(expansions, (isDot ? estrdup(entry->name) :
- str_concat(p->name, entry->name, STR_ADDSLASH)));
- }
- }
- return (0);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * DirExpandCurly --
- * Expand curly braces like the C shell. Does this recursively.
- * Note the special case: if after the piece of the curly brace is
- * done there are no wildcard characters in the result, the result is
- * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE. The
- * given arguments are the entire word to expand, the first curly
- * brace in the word, the search path, and the list to store the
- * expansions in.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The given list is filled with the expansions...
- *
- *-----------------------------------------------------------------------
- */
-static void
-DirExpandCurly(const char *word, const char *brace, struct Path *path,
- Lst *expansions)
-{
- const char *end; /* Character after the closing brace */
- const char *cp; /* Current position in brace clause */
- const char *start; /* Start of current piece of brace clause */
- int bracelevel; /* Number of braces we've seen. If we see a right brace
- * when this is 0, we've hit the end of the clause. */
- char *file; /* Current expansion */
- int otherLen; /* The length of the other pieces of the expansion
- * (chars before and after the clause in 'word') */
- char *cp2; /* Pointer for checking for wildcards in
- * expansion before calling Dir_Expand */
-
- start = brace + 1;
-
- /*
- * Find the end of the brace clause first, being wary of nested brace
- * clauses.
- */
- for (end = start, bracelevel = 0; *end != '\0'; end++) {
- if (*end == '{')
- bracelevel++;
- else if ((*end == '}') && (bracelevel-- == 0))
- break;
- }
- if (*end == '\0') {
- Error("Unterminated {} clause \"%s\"", start);
- return;
- } else
- end++;
-
- otherLen = brace - word + strlen(end);
-
- for (cp = start; cp < end; cp++) {
- /*
- * Find the end of this piece of the clause.
- */
- bracelevel = 0;
- while (*cp != ',') {
- if (*cp == '{')
- bracelevel++;
- else if ((*cp == '}') && (bracelevel-- <= 0))
- break;
- cp++;
- }
- /*
- * Allocate room for the combination and install the
- * three pieces.
- */
- file = emalloc(otherLen + cp - start + 1);
- if (brace != word)
- strncpy(file, word, brace - word);
- if (cp != start)
- strncpy(&file[brace - word], start, cp - start);
- strcpy(&file[(brace - word) + (cp - start)], end);
-
- /*
- * See if the result has any wildcards in it. If we find one,
- * call Dir_Expand right away, telling it to place the result
- * on our list of expansions.
- */
- for (cp2 = file; *cp2 != '\0'; cp2++) {
- switch (*cp2) {
- case '*':
- case '?':
- case '{':
- case '[':
- Path_Expand(file, path, expansions);
- goto next;
- default:
- break;
- }
- }
- if (*cp2 == '\0') {
- /*
- * Hit the end w/o finding any wildcards, so stick
- * the expansion on the end of the list.
- */
- Lst_AtEnd(expansions, file);
- } else {
- next:
- free(file);
- }
- start = cp + 1;
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * DirExpandInt --
- * Internal expand routine. Passes through the directories in the
- * path one by one, calling DirMatchFiles for each. NOTE: This still
- * doesn't handle patterns in directories... Works given a word to
- * expand, a path to look in, and a list to store expansions in.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Things are added to the expansions list.
- *
- *-----------------------------------------------------------------------
- */
-static void
-DirExpandInt(const char *word, const struct Path *path, Lst *expansions)
-{
- struct PathElement *pe;
-
- TAILQ_FOREACH(pe, path, link)
- DirMatchFiles(word, pe->dir, expansions);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Dir_Expand --
- * Expand the given word into a list of words by globbing it looking
- * in the directories on the given search path.
- *
- * Results:
- * A list of words consisting of the files which exist along the search
- * path matching the given pattern is placed in expansions.
- *
- * Side Effects:
- * Directories may be opened. Who knows?
- *-----------------------------------------------------------------------
- */
-void
-Path_Expand(char *word, struct Path *path, Lst *expansions)
-{
- LstNode *ln;
- char *cp;
-
- DEBUGF(DIR, ("expanding \"%s\"...", word));
-
- cp = strchr(word, '{');
- if (cp != NULL)
- DirExpandCurly(word, cp, path, expansions);
- else {
- cp = strchr(word, '/');
- if (cp != NULL) {
- /*
- * The thing has a directory component -- find the
- * first wildcard in the string.
- */
- for (cp = word; *cp != '\0'; cp++) {
- if (*cp == '?' || *cp == '[' ||
- *cp == '*' || *cp == '{') {
- break;
- }
- }
- if (*cp == '{') {
- /*
- * This one will be fun.
- */
- DirExpandCurly(word, cp, path, expansions);
- return;
- } else if (*cp != '\0') {
- /*
- * Back up to the start of the component
- */
- char *dirpath;
-
- while (cp > word && *cp != '/')
- cp--;
- if (cp != word) {
- char sc;
-
- /*
- * If the glob isn't in the first
- * component, try and find all the
- * components up to the one with a
- * wildcard.
- */
- sc = cp[1];
- cp[1] = '\0';
- dirpath = Path_FindFile(word, path);
- cp[1] = sc;
- /*
- * dirpath is null if can't find the
- * leading component
- * XXX: Path_FindFile won't find internal
- * components. i.e. if the path contains
- * ../Etc/Object and we're looking for
- * Etc, * it won't be found. Ah well.
- * Probably not important.
- */
- if (dirpath != NULL) {
- char *dp =
- &dirpath[strlen(dirpath)
- - 1];
- struct Path tp =
- TAILQ_HEAD_INITIALIZER(tp);
-
- if (*dp == '/')
- *dp = '\0';
- Path_AddDir(&tp, dirpath);
- DirExpandInt(cp + 1, &tp,
- expansions);
- Path_Clear(&tp);
- }
- } else {
- /*
- * Start the search from the local
- * directory
- */
- DirExpandInt(word, path, expansions);
- }
- } else {
- /*
- * Return the file -- this should never happen.
- */
- DirExpandInt(word, path, expansions);
- }
- } else {
- /*
- * First the files in dot
- */
- DirMatchFiles(word, dot, expansions);
-
- /*
- * Then the files in every other directory on the path.
- */
- DirExpandInt(word, path, expansions);
- }
- }
- if (DEBUG(DIR)) {
- LST_FOREACH(ln, expansions)
- DEBUGF(DIR, ("%s ", (const char *)Lst_Datum(ln)));
- DEBUGF(DIR, ("\n"));
- }
-}
-
-/**
- * Path_FindFile
- * Find the file with the given name along the given search path.
- *
- * Results:
- * The path to the file or NULL. This path is guaranteed to be in a
- * different part of memory than name and so may be safely free'd.
- *
- * Side Effects:
- * If the file is found in a directory which is not on the path
- * already (either 'name' is absolute or it is a relative path
- * [ dir1/.../dirn/file ] which exists below one of the directories
- * already on the search path), its directory is added to the end
- * of the path on the assumption that there will be more files in
- * that directory later on. Sometimes this is true. Sometimes not.
- */
-char *
-Path_FindFile(char *name, struct Path *path)
-{
- char *p1; /* pointer into p->name */
- char *p2; /* pointer into name */
- char *file; /* the current filename to check */
- const struct PathElement *pe; /* current path member */
- char *cp; /* final component of the name */
- Boolean hasSlash; /* true if 'name' contains a / */
- struct stat stb; /* Buffer for stat, if necessary */
- Hash_Entry *entry; /* Entry for mtimes table */
-
- /*
- * Find the final component of the name and note whether it has a
- * slash in it (the name, I mean)
- */
- cp = strrchr(name, '/');
- if (cp != NULL) {
- hasSlash = TRUE;
- cp += 1;
- } else {
- hasSlash = FALSE;
- cp = name;
- }
-
- DEBUGF(DIR, ("Searching for %s...", name));
- /*
- * No matter what, we always look for the file in the current directory
- * before anywhere else and we *do not* add the ./ to it if it exists.
- * This is so there are no conflicts between what the user specifies
- * (fish.c) and what pmake finds (./fish.c).
- */
- if ((!hasSlash || (cp - name == 2 && *name == '.')) &&
- (Hash_FindEntry(&dot->files, cp) != NULL)) {
- DEBUGF(DIR, ("in '.'\n"));
- hits += 1;
- dot->hits += 1;
- return (estrdup(name));
- }
-
- /*
- * We look through all the directories on the path seeking one which
- * contains the final component of the given name and whose final
- * component(s) match the name's initial component(s). If such a beast
- * is found, we concatenate the directory name and the final component
- * and return the resulting string. If we don't find any such thing,
- * we go on to phase two...
- */
- TAILQ_FOREACH(pe, path, link) {
- DEBUGF(DIR, ("%s...", pe->dir->name));
- if (Hash_FindEntry(&pe->dir->files, cp) != NULL) {
- DEBUGF(DIR, ("here..."));
- if (hasSlash) {
- /*
- * If the name had a slash, its initial
- * components and p's final components must
- * match. This is false if a mismatch is
- * encountered before all of the initial
- * components have been checked (p2 > name at
- * the end of the loop), or we matched only
- * part of one of the components of p
- * along with all the rest of them (*p1 != '/').
- */
- p1 = pe->dir->name + strlen(pe->dir->name) - 1;
- p2 = cp - 2;
- while (p2 >= name && p1 >= pe->dir->name &&
- *p1 == *p2) {
- p1 -= 1; p2 -= 1;
- }
- if (p2 >= name || (p1 >= pe->dir->name &&
- *p1 != '/')) {
- DEBUGF(DIR, ("component mismatch -- "
- "continuing..."));
- continue;
- }
- }
- file = str_concat(pe->dir->name, cp, STR_ADDSLASH);
- DEBUGF(DIR, ("returning %s\n", file));
- pe->dir->hits += 1;
- hits += 1;
- return (file);
- } else if (hasSlash) {
- /*
- * If the file has a leading path component and that
- * component exactly matches the entire name of the
- * current search directory, we assume the file
- * doesn't exist and return NULL.
- */
- for (p1 = pe->dir->name, p2 = name; *p1 && *p1 == *p2;
- p1++, p2++)
- continue;
- if (*p1 == '\0' && p2 == cp - 1) {
- if (*cp == '\0' || ISDOT(cp) || ISDOTDOT(cp)) {
- DEBUGF(DIR, ("returning %s\n", name));
- return (estrdup(name));
- } else {
- DEBUGF(DIR, ("must be here but isn't --"
- " returning NULL\n"));
- return (NULL);
- }
- }
- }
- }
-
- /*
- * We didn't find the file on any existing members of the directory.
- * If the name doesn't contain a slash, that means it doesn't exist.
- * If it *does* contain a slash, however, there is still hope: it
- * could be in a subdirectory of one of the members of the search
- * path. (eg. /usr/include and sys/types.h. The above search would
- * fail to turn up types.h in /usr/include, but it *is* in
- * /usr/include/sys/types.h) If we find such a beast, we assume there
- * will be more (what else can we assume?) and add all but the last
- * component of the resulting name onto the search path (at the
- * end). This phase is only performed if the file is *not* absolute.
- */
- if (!hasSlash) {
- DEBUGF(DIR, ("failed.\n"));
- misses += 1;
- return (NULL);
- }
-
- if (*name != '/') {
- Boolean checkedDot = FALSE;
-
- DEBUGF(DIR, ("failed. Trying subdirectories..."));
- TAILQ_FOREACH(pe, path, link) {
- if (pe->dir != dot) {
- file = str_concat(pe->dir->name,
- name, STR_ADDSLASH);
- } else {
- /*
- * Checking in dot -- DON'T put a leading ./
- * on the thing.
- */
- file = estrdup(name);
- checkedDot = TRUE;
- }
- DEBUGF(DIR, ("checking %s...", file));
-
- if (stat(file, &stb) == 0) {
- DEBUGF(DIR, ("got it.\n"));
-
- /*
- * We've found another directory to search. We
- * know there's a slash in 'file' because we put
- * one there. We nuke it after finding it and
- * call Path_AddDir to add this new directory
- * onto the existing search path. Once that's
- * done, we restore the slash and triumphantly
- * return the file name, knowing that should a
- * file in this directory every be referenced
- * again in such a manner, we will find it
- * without having to do numerous numbers of
- * access calls. Hurrah!
- */
- cp = strrchr(file, '/');
- *cp = '\0';
- Path_AddDir(path, file);
- *cp = '/';
-
- /*
- * Save the modification time so if
- * it's needed, we don't have to fetch it again.
- */
- DEBUGF(DIR, ("Caching %s for %s\n",
- Targ_FmtTime(stb.st_mtime), file));
- entry = Hash_CreateEntry(&mtimes, file,
- (Boolean *)NULL);
- Hash_SetValue(entry,
- (void *)(long)stb.st_mtime);
- nearmisses += 1;
- return (file);
- } else {
- free(file);
- }
- }
-
- DEBUGF(DIR, ("failed. "));
-
- if (checkedDot) {
- /*
- * Already checked by the given name, since . was in
- * the path, so no point in proceeding...
- */
- DEBUGF(DIR, ("Checked . already, returning NULL\n"));
- return (NULL);
- }
- }
-
- /*
- * Didn't find it that way, either. Sigh. Phase 3. Add its directory
- * onto the search path in any case, just in case, then look for the
- * thing in the hash table. If we find it, grand. We return a new
- * copy of the name. Otherwise we sadly return a NULL pointer. Sigh.
- * Note that if the directory holding the file doesn't exist, this will
- * do an extra search of the final directory on the path. Unless
- * something weird happens, this search won't succeed and life will
- * be groovy.
- *
- * Sigh. We cannot add the directory onto the search path because
- * of this amusing case:
- * $(INSTALLDIR)/$(FILE): $(FILE)
- *
- * $(FILE) exists in $(INSTALLDIR) but not in the current one.
- * When searching for $(FILE), we will find it in $(INSTALLDIR)
- * b/c we added it here. This is not good...
- */
- DEBUGF(DIR, ("Looking for \"%s\"...", name));
-
- bigmisses += 1;
- entry = Hash_FindEntry(&mtimes, name);
- if (entry != NULL) {
- DEBUGF(DIR, ("got it (in mtime cache)\n"));
- return (estrdup(name));
- } else if (stat (name, &stb) == 0) {
- entry = Hash_CreateEntry(&mtimes, name, (Boolean *)NULL);
- DEBUGF(DIR, ("Caching %s for %s\n",
- Targ_FmtTime(stb.st_mtime), name));
- Hash_SetValue(entry, (void *)(long)stb.st_mtime);
- return (estrdup(name));
- } else {
- DEBUGF(DIR, ("failed. Returning NULL\n"));
- return (NULL);
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Dir_FindHereOrAbove --
- * search for a path starting at a given directory and then working
- * our way up towards the root.
- *
- * Input:
- * here starting directory
- * search_path the path we are looking for
- * result the result of a successful search is placed here
- * rlen the length of the result buffer
- * (typically MAXPATHLEN + 1)
- *
- * Results:
- * 0 on failure, 1 on success [in which case the found path is put
- * in the result buffer].
- *
- * Side Effects:
- *-----------------------------------------------------------------------
- */
-int
-Dir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen)
-{
- struct stat st;
- char dirbase[MAXPATHLEN + 1], *db_end;
- char try[MAXPATHLEN + 1], *try_end;
-
- /* copy out our starting point */
- snprintf(dirbase, sizeof(dirbase), "%s", here);
- db_end = dirbase + strlen(dirbase);
-
- /* loop until we determine a result */
- while (1) {
- /* try and stat(2) it ... */
- snprintf(try, sizeof(try), "%s/%s", dirbase, search_path);
- if (stat(try, &st) != -1) {
- /*
- * Success! If we found a file, chop off
- * the filename so we return a directory.
- */
- if ((st.st_mode & S_IFMT) != S_IFDIR) {
- try_end = try + strlen(try);
- while (try_end > try && *try_end != '/')
- try_end--;
- if (try_end > try)
- *try_end = 0; /* chop! */
- }
-
- /*
- * Done!
- */
- snprintf(result, rlen, "%s", try);
- return(1);
- }
-
- /*
- * Nope, we didn't find it. If we used up dirbase we've
- * reached the root and failed.
- */
- if (db_end == dirbase)
- break; /* Failed! */
-
- /*
- * truncate dirbase from the end to move up a dir
- */
- while (db_end > dirbase && *db_end != '/')
- db_end--;
- *db_end = 0; /* chop! */
-
- } /* while (1) */
-
- /*
- * We failed...
- */
- return(0);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Dir_MTime --
- * Find the modification time of the file described by gn along the
- * search path dirSearchPath.
- *
- * Results:
- * The modification time or 0 if it doesn't exist
- *
- * Side Effects:
- * The modification time is placed in the node's mtime slot.
- * If the node didn't have a path entry before, and Path_FindFile
- * found one for it, the full name is placed in the path slot.
- *-----------------------------------------------------------------------
- */
-int
-Dir_MTime(GNode *gn)
-{
- char *fullName; /* the full pathname of name */
- struct stat stb; /* buffer for finding the mod time */
- Hash_Entry *entry;
-
- if (gn->type & OP_ARCHV)
- return (Arch_MTime(gn));
-
- else if (gn->path == NULL)
- fullName = Path_FindFile(gn->name, &dirSearchPath);
- else
- fullName = gn->path;
-
- if (fullName == NULL)
- fullName = estrdup(gn->name);
-
- entry = Hash_FindEntry(&mtimes, fullName);
- if (entry != NULL) {
- /*
- * Only do this once -- the second time folks are checking to
- * see if the file was actually updated, so we need to
- * actually go to the filesystem.
- */
- DEBUGF(DIR, ("Using cached time %s for %s\n",
- Targ_FmtTime((time_t)(long)Hash_GetValue(entry)),
- fullName));
- stb.st_mtime = (time_t)(long)Hash_GetValue(entry);
- Hash_DeleteEntry(&mtimes, entry);
- } else if (stat(fullName, &stb) < 0) {
- if (gn->type & OP_MEMBER) {
- if (fullName != gn->path)
- free(fullName);
- return (Arch_MemMTime(gn));
- } else {
- stb.st_mtime = 0;
- }
- }
- if (fullName && gn->path == (char *)NULL)
- gn->path = fullName;
-
- gn->mtime = stb.st_mtime;
- return (gn->mtime);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Path_AddDir --
- * Add the given name to the end of the given path.
- *
- * Results:
- * none
- *
- * Side Effects:
- * A structure is added to the list and the directory is
- * read and hashed.
- *-----------------------------------------------------------------------
- */
-struct Dir *
-Path_AddDir(struct Path *path, const char *name)
-{
- Dir *d; /* pointer to new Path structure */
- DIR *dir; /* for reading directory */
- struct PathElement *pe;
- struct dirent *dp; /* entry in directory */
-
- /* check whether we know this directory */
- TAILQ_FOREACH(d, &openDirectories, link) {
- if (strcmp(d->name, name) == 0) {
- /* Found it. */
- if (path == NULL)
- return (d);
-
- /* Check whether its already on the path. */
- TAILQ_FOREACH(pe, path, link) {
- if (pe->dir == d)
- return (d);
- }
- /* Add it to the path */
- d->refCount += 1;
- pe = emalloc(sizeof(*pe));
- pe->dir = d;
- TAILQ_INSERT_TAIL(path, pe, link);
- return (d);
- }
- }
-
- DEBUGF(DIR, ("Caching %s...", name));
-
- if ((dir = opendir(name)) == NULL) {
- DEBUGF(DIR, (" cannot open\n"));
- return (NULL);
- }
-
- d = emalloc(sizeof(*d));
- d->name = estrdup(name);
- d->hits = 0;
- d->refCount = 1;
- Hash_InitTable(&d->files, -1);
-
- while ((dp = readdir(dir)) != NULL) {
-#if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */
- /*
- * The sun directory library doesn't check for
- * a 0 inode (0-inode slots just take up space),
- * so we have to do it ourselves.
- */
- if (dp->d_fileno == 0)
- continue;
-#endif /* sun && d_ino */
-
- /* Skip the '.' and '..' entries by checking
- * for them specifically instead of assuming
- * readdir() reuturns them in that order when
- * first going through a directory. This is
- * needed for XFS over NFS filesystems since
- * SGI does not guarantee that these are the
- * first two entries returned from readdir().
- */
- if (ISDOT(dp->d_name) || ISDOTDOT(dp->d_name))
- continue;
-
- Hash_CreateEntry(&d->files, dp->d_name, (Boolean *)NULL);
- }
- closedir(dir);
-
- if (path != NULL) {
- /* Add it to the path */
- d->refCount += 1;
- pe = emalloc(sizeof(*pe));
- pe->dir = d;
- TAILQ_INSERT_TAIL(path, pe, link);
- }
-
- /* Add to list of all directories */
- TAILQ_INSERT_TAIL(&openDirectories, d, link);
-
- DEBUGF(DIR, ("done\n"));
-
- return (d);
-}
-
-/**
- * Path_Duplicate
- * Duplicate a path. Ups the reference count for the directories.
- */
-void
-Path_Duplicate(struct Path *dst, const struct Path *src)
-{
- struct PathElement *ped, *pes;
-
- TAILQ_FOREACH(pes, src, link) {
- ped = emalloc(sizeof(*ped));
- ped->dir = pes->dir;
- ped->dir->refCount++;
- TAILQ_INSERT_TAIL(dst, ped, link);
- }
-}
-
-/**
- * Path_MakeFlags
- * Make a string by taking all the directories in the given search
- * path and preceding them by the given flag. Used by the suffix
- * module to create variables for compilers based on suffix search
- * paths.
- *
- * Results:
- * The string mentioned above. Note that there is no space between
- * the given flag and each directory. The empty string is returned if
- * Things don't go well.
- */
-char *
-Path_MakeFlags(const char *flag, const struct Path *path)
-{
- char *str; /* the string which will be returned */
- char *tstr; /* the current directory preceded by 'flag' */
- char *nstr;
- const struct PathElement *pe;
-
- str = estrdup("");
-
- TAILQ_FOREACH(pe, path, link) {
- tstr = str_concat(flag, pe->dir->name, 0);
- nstr = str_concat(str, tstr, STR_ADDSPACE);
- free(str);
- free(tstr);
- str = nstr;
- }
-
- return (str);
-}
-
-/**
- * Path_Clear
- *
- * Destroy a path. This decrements the reference counts of all
- * directories of this path and, if a reference count goes 0,
- * destroys the directory object.
- */
-void
-Path_Clear(struct Path *path)
-{
- struct PathElement *pe;
-
- while ((pe = TAILQ_FIRST(path)) != NULL) {
- pe->dir->refCount--;
- TAILQ_REMOVE(path, pe, link);
- if (pe->dir->refCount == 0) {
- TAILQ_REMOVE(&openDirectories, pe->dir, link);
- Hash_DeleteTable(&pe->dir->files);
- free(pe->dir->name);
- free(pe->dir);
- }
- free(pe);
- }
-}
-
-/**
- * Path_Concat
- *
- * Concatenate two paths, adding the second to the end of the first.
- * Make sure to avoid duplicates.
- *
- * Side Effects:
- * Reference counts for added dirs are upped.
- */
-void
-Path_Concat(struct Path *path1, const struct Path *path2)
-{
- struct PathElement *p1, *p2;
-
- TAILQ_FOREACH(p2, path2, link) {
- TAILQ_FOREACH(p1, path1, link) {
- if (p1->dir == p2->dir)
- break;
- }
- if (p1 == NULL) {
- p1 = emalloc(sizeof(*p1));
- p1->dir = p2->dir;
- p1->dir->refCount++;
- TAILQ_INSERT_TAIL(path1, p1, link);
- }
- }
-}
-
-/********** DEBUG INFO **********/
-void
-Dir_PrintDirectories(void)
-{
- const Dir *d;
-
- printf("#*** Directory Cache:\n");
- printf("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n",
- hits, misses, nearmisses, bigmisses,
- (hits + bigmisses + nearmisses ?
- hits * 100 / (hits + bigmisses + nearmisses) : 0));
- printf("# %-20s referenced\thits\n", "directory");
- TAILQ_FOREACH(d, &openDirectories, link)
- printf("# %-20s %10d\t%4d\n", d->name, d->refCount, d->hits);
-}
-
-void
-Path_Print(const struct Path *path)
-{
- const struct PathElement *p;
-
- TAILQ_FOREACH(p, path, link)
- printf("%s ", p->dir->name);
-}
diff --git a/usr.bin/make/dir.h b/usr.bin/make/dir.h
deleted file mode 100644
index 1ae89ae..0000000
--- a/usr.bin/make/dir.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*-
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1988, 1989 by Adam de Boor
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)dir.h 8.2 (Berkeley) 4/28/95
- * $FreeBSD$
- */
-
-#ifndef dir_h_6002e3b8
-#define dir_h_6002e3b8
-
-#include <sys/queue.h>
-#include "hash.h"
-
-struct GNode;
-struct Lst;
-struct Dir;
-
-struct PathElement;
-TAILQ_HEAD(Path, PathElement);
-
-void Dir_Init(void);
-void Dir_InitDot(void);
-Boolean Dir_HasWildcards(const char *);
-int Dir_FindHereOrAbove(char *, char *, char *, int);
-int Dir_MTime(struct GNode *);
-void Dir_PrintDirectories(void);
-
-struct Dir *Path_AddDir(struct Path *, const char *);
-void Path_Clear(struct Path *);
-void Path_Concat(struct Path *, const struct Path *);
-void Path_Duplicate(struct Path *, const struct Path *);
-void Path_Expand(char *, struct Path *, struct Lst *);
-char *Path_FindFile(char *, struct Path *);
-char *Path_MakeFlags(const char *, const struct Path *);
-void Path_Print(const struct Path *);
-
-#endif /* dir_h_6002e3b8 */
diff --git a/usr.bin/make/for.c b/usr.bin/make/for.c
deleted file mode 100644
index 1b2e578..0000000
--- a/usr.bin/make/for.c
+++ /dev/null
@@ -1,267 +0,0 @@
-/*-
- * Copyright (c) 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Christos Zoulas.
- *
- * 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.
- *
- * @(#)for.c 8.1 (Berkeley) 6/6/93
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*-
- * for.c --
- * Functions to handle loops in a makefile.
- *
- * Interface:
- * For_Eval Evaluate the loop in the passed line.
- * For_Run Run accumulated loop
- *
- */
-
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "buf.h"
-#include "for.h"
-#include "globals.h"
-#include "lst.h"
-#include "parse.h"
-#include "str.h"
-#include "util.h"
-#include "var.h"
-
-/*
- * For statements are of the form:
- *
- * .for <variable> in <varlist>
- * ...
- * .endfor
- *
- * The trick is to look for the matching end inside for for loop
- * To do that, we count the current nesting level of the for loops.
- * and the .endfor statements, accumulating all the statements between
- * the initial .for loop and the matching .endfor;
- * then we evaluate the for loop for each variable in the varlist.
- */
-
-static int forLevel = 0; /* Nesting level */
-static char *forVar; /* Iteration variable */
-static Buffer *forBuf; /* Commands in loop */
-static Lst forLst; /* List of items */
-
-/**
- * For_For
- * Evaluate the for loop in the passed line. The line
- * looks like this:
- * .for <variable> in <varlist>
- * The line pointer points just behind the for.
- *
- * Results:
- * TRUE: Syntax ok.
- * FALSE: Syntax error.
- */
-Boolean
-For_For(char *line)
-{
- char *ptr;
- char *wrd;
- char *sub;
- Buffer *buf;
- size_t varlen;
- int i;
- ArgArray words;
-
- ptr = line;
-
- /*
- * Skip space between for and the variable.
- */
- for (ptr++; *ptr && isspace((u_char)*ptr); ptr++)
- ;
-
- /*
- * Grab the variable
- */
- for (wrd = ptr; *ptr && !isspace((u_char)*ptr); ptr++)
- ;
-
- buf = Buf_Init(0);
- Buf_AppendRange(buf, wrd, ptr);
- forVar = Buf_GetAll(buf, &varlen);
-
- if (varlen == 0) {
- Buf_Destroy(buf, TRUE);
- Parse_Error(PARSE_FATAL, "missing variable in for");
- return (FALSE);
- }
- Buf_Destroy(buf, FALSE);
-
- /*
- * Skip to 'in'.
- */
- while (*ptr && isspace((u_char)*ptr))
- ptr++;
-
- /*
- * Grab the `in'
- */
- if (ptr[0] != 'i' || ptr[1] != 'n' || !isspace((u_char)ptr[2])) {
- free(forVar);
- Parse_Error(PARSE_FATAL, "missing `in' in for");
- fprintf(stderr, "%s\n", ptr);
- return (FALSE);
- }
- ptr += 3;
-
- /*
- * Skip to values
- */
- while (*ptr && isspace((u_char)*ptr))
- ptr++;
-
- /*
- * Make a list with the remaining words
- */
- sub = Buf_Peel(Var_Subst(ptr, VAR_CMD, FALSE));
- brk_string(&words, sub, FALSE);
- Lst_Init(&forLst);
- for (i = 1; i < words.argc; i++) {
- if (words.argv[i][0] != '\0')
- Lst_AtFront(&forLst, estrdup(words.argv[i]));
- }
- ArgArray_Done(&words);
- DEBUGF(FOR, ("For: Iterator %s List %s\n", forVar, sub));
- free(sub);
-
- forBuf = Buf_Init(0);
- forLevel++;
- return (TRUE);
-}
-
-/**
- * For_Eval
- * Eat a line of the .for body looking for embedded .for loops
- * and the .endfor
- */
-Boolean
-For_Eval(char *line)
-{
- char *ptr;
-
- ptr = line;
-
- if (*ptr == '.') {
- /*
- * Need to check for 'endfor' and 'for' to find the end
- * of our loop or to find embedded for loops.
- */
- for (ptr++; *ptr != '\0' && isspace((u_char)*ptr); ptr++)
- ;
-
- /* XXX the isspace is wrong */
- if (strncmp(ptr, "endfor", 6) == 0 &&
- (isspace((u_char)ptr[6]) || ptr[6] == '\0')) {
- DEBUGF(FOR, ("For: end for %d\n", forLevel));
- if (forLevel == 0) {
- /* should not be here */
- abort();
- }
- forLevel--;
-
- } else if (strncmp(ptr, "for", 3) == 0 &&
- isspace((u_char)ptr[3])) {
- forLevel++;
- DEBUGF(FOR, ("For: new loop %d\n", forLevel));
- }
- }
-
- if (forLevel != 0) {
- /*
- * Still in loop - append the line
- */
- Buf_Append(forBuf, line);
- Buf_AddByte(forBuf, (Byte)'\n');
- return (TRUE);
- }
-
- return (FALSE);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * For_Run --
- * Run the for loop, imitating the actions of an include file
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The values of the variables forLst, forVar and forBuf are freed.
- *
- *-----------------------------------------------------------------------
- */
-void
-For_Run(int lineno)
-{
- Lst values; /* list of values for the variable */
- char *var; /* the variable's name */
- Buffer *buf; /* the contents of the for loop */
- const char *val; /* current value of loop variable */
- LstNode *ln;
- char *str;
-
- if (forVar == NULL || forBuf == NULL)
- return;
-
- /* copy the global variables to have them free for embedded fors */
- var = forVar;
- buf = forBuf;
- Lst_Init(&values);
- Lst_Concat(&values, &forLst, LST_CONCLINK);
-
- forVar = NULL;
- forBuf = NULL;
-
- LST_FOREACH(ln, &values) {
- val = Lst_Datum(ln);
- Var_SetGlobal(var, val);
-
- DEBUGF(FOR, ("--- %s = %s\n", var, val));
- str = Buf_Peel(Var_SubstOnly(var, Buf_Data(buf), FALSE));
-
- Parse_FromString(str, lineno);
- Var_Delete(var, VAR_GLOBAL);
- }
-
- free(var);
- Lst_Destroy(&values, free);
- Buf_Destroy(buf, TRUE);
-}
diff --git a/usr.bin/make/for.h b/usr.bin/make/for.h
deleted file mode 100644
index 0e43c4a..0000000
--- a/usr.bin/make/for.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * $FreeBSD$
- */
-
-#ifndef for_h_9d770f33
-#define for_h_9d770f33
-
-#include "util.h"
-
-Boolean For_For(char *);
-Boolean For_Eval(char *);
-void For_Run(int);
-
-#endif /* for_h_9d770f33 */
diff --git a/usr.bin/make/globals.h b/usr.bin/make/globals.h
deleted file mode 100644
index e3ce51b..0000000
--- a/usr.bin/make/globals.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * $FreeBSD$
- */
-
-#ifndef globals_h_1c1edb96
-#define globals_h_1c1edb96
-
-/*
- * Global Variables
- */
-
-#include "lst.h"
-#include "util.h"
-
-struct GNode;
-struct Path;
-
-/*
- * The list of target names specified on the command line.
- * Used to resolve #if make(...) statements
- */
-extern Lst create;
-
-/* The list of directories to search when looking for targets */
-extern struct Path dirSearchPath;
-
-/* The list of directories to search when looking for includes */
-extern struct Path parseIncPath;
-
-/* The system include path. */
-extern struct Path sysIncPath;
-
-extern int jobLimit; /* -j argument: maximum number of jobs */
-extern int makeErrors; /* Number of targets not remade due to errors */
-extern Boolean jobsRunning; /* True if jobs are running */
-extern Boolean compatMake; /* True if we are make compatible */
-extern Boolean ignoreErrors; /* True if should ignore all errors */
-extern Boolean beSilent; /* True if should print no commands */
-extern Boolean beVerbose; /* True if should print extra cruft */
-extern Boolean beQuiet; /* True if want quiet headers with -j */
-extern Boolean noExecute; /* True if should execute nothing */
-extern Boolean printGraphOnly; /* -p flag */
-extern Boolean allPrecious; /* True if every target is precious */
-extern Boolean is_posix; /* .POSIX target seen */
-extern Boolean mfAutoDeps; /* .MAKEFILEDEPS target seen */
-extern Boolean remakingMakefiles; /* True if remaking makefiles is in progress */
-
-/* True if should continue on unaffected portions of the graph
- * when have an error in one portion */
-extern Boolean keepgoing;
-
-/* TRUE if targets should just be 'touched'if out of date. Set by the -t flag */
-extern Boolean touchFlag;
-
-/* TRUE if should capture the output of subshells by means of pipes.
- * Otherwise it is routed to temporary files from which it is retrieved
- * when the shell exits */
-extern Boolean usePipes;
-
-/* TRUE if we aren't supposed to really make anything, just see if the
- * targets are out-of-date */
-extern Boolean queryFlag;
-
-/* List of specific variables for which the environment should be
- * searched before the global context */
-extern Lst envFirstVars;
-
-extern struct GNode *DEFAULT; /* .DEFAULT rule */
-
-/* The time at the start of this whole process */
-extern time_t now;
-
-extern int debug;
-
-/* warning flags */
-extern uint32_t warn_cmd; /* positive warning flags on command line */
-extern uint32_t warn_nocmd; /* negative warning flags on command line */
-extern uint32_t warn_flags; /* current warning flags */
-
-#endif /* globals_h_1c1edb96 */
diff --git a/usr.bin/make/hash.c b/usr.bin/make/hash.c
deleted file mode 100644
index b5ea6bc..0000000
--- a/usr.bin/make/hash.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/*-
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1988, 1989 by Adam de Boor
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)hash.c 8.1 (Berkeley) 6/6/93
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/* hash.c --
- *
- * This module contains routines to manipulate a hash table.
- * See hash.h for a definition of the structure of the hash
- * table. Hash tables grow automatically as the amount of
- * information increases.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "hash.h"
-#include "util.h"
-
-/*
- * Forward references to local procedures that are used before they're
- * defined:
- */
-static void RebuildTable(Hash_Table *);
-
-/*
- * The following defines the ratio of # entries to # buckets
- * at which we rebuild the table to make it larger.
- */
-
-#define rebuildLimit 8
-
-/*
- *---------------------------------------------------------
- *
- * Hash_InitTable --
- *
- * Set up the hash table t with a given number of buckets, or a
- * reasonable default if the number requested is less than or
- * equal to zero. Hash tables will grow in size as needed.
- *
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Memory is allocated for the initial bucket area.
- *
- *---------------------------------------------------------
- */
-void
-Hash_InitTable(Hash_Table *t, int numBuckets)
-{
- int i;
- struct Hash_Entry **hp;
-
- /*
- * Round up the size to a power of two.
- */
- if (numBuckets <= 0)
- i = 16;
- else {
- for (i = 2; i < numBuckets; i <<= 1)
- continue;
- }
- t->numEntries = 0;
- t->size = i;
- t->mask = i - 1;
- t->bucketPtr = hp = emalloc(sizeof(*hp) * i);
- while (--i >= 0)
- *hp++ = NULL;
-}
-
-/*
- *---------------------------------------------------------
- *
- * Hash_DeleteTable --
- *
- * This routine removes everything from a hash table
- * and frees up the memory space it occupied (except for
- * the space in the Hash_Table structure).
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Lots of memory is freed up.
- *
- *---------------------------------------------------------
- */
-void
-Hash_DeleteTable(Hash_Table *t)
-{
- struct Hash_Entry **hp, *h, *nexth = NULL;
- int i;
-
- for (hp = t->bucketPtr, i = t->size; --i >= 0;) {
- for (h = *hp++; h != NULL; h = nexth) {
- nexth = h->next;
- free(h);
- }
- }
- free(t->bucketPtr);
-
- /*
- * Set up the hash table to cause memory faults on any future access
- * attempts until re-initialization.
- */
- t->bucketPtr = NULL;
-}
-
-/*
- *---------------------------------------------------------
- *
- * Hash_FindEntry --
- *
- * Searches a hash table for an entry corresponding to key.
- *
- * Results:
- * The return value is a pointer to the entry for key,
- * if key was present in the table. If key was not
- * present, NULL is returned.
- *
- * Side Effects:
- * None.
- *
- *---------------------------------------------------------
- */
-Hash_Entry *
-Hash_FindEntry(const Hash_Table *t, const char *key)
-{
- Hash_Entry *e;
- unsigned h;
- const char *p;
-
- for (h = 0, p = key; *p;)
- h = (h << 5) - h + *p++;
- p = key;
- for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next)
- if (e->namehash == h && strcmp(e->name, p) == 0)
- return (e);
- return (NULL);
-}
-
-/*
- *---------------------------------------------------------
- *
- * Hash_CreateEntry --
- *
- * Searches a hash table for an entry corresponding to
- * key. If no entry is found, then one is created.
- *
- * Results:
- * The return value is a pointer to the entry. If *newPtr
- * isn't NULL, then *newPtr is filled in with TRUE if a
- * new entry was created, and FALSE if an entry already existed
- * with the given key.
- *
- * Side Effects:
- * Memory may be allocated, and the hash buckets may be modified.
- *---------------------------------------------------------
- */
-Hash_Entry *
-Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr)
-{
- Hash_Entry *e;
- unsigned int h;
- const char *p;
- int keylen;
- struct Hash_Entry **hp;
-
- /*
- * Hash the key. As a side effect, save the length (strlen) of the
- * key in case we need to create the entry.
- */
- for (h = 0, p = key; *p;)
- h = (h << 5) - h + *p++;
- keylen = p - key;
- p = key;
- for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) {
- if (e->namehash == h && strcmp(e->name, p) == 0) {
- if (newPtr != NULL)
- *newPtr = FALSE;
- return (e);
- }
- }
-
- /*
- * The desired entry isn't there. Before allocating a new entry,
- * expand the table if necessary (and this changes the resulting
- * bucket chain).
- */
- if (t->numEntries >= rebuildLimit * t->size)
- RebuildTable(t);
- e = emalloc(sizeof(*e) + keylen);
- hp = &t->bucketPtr[h & t->mask];
- e->next = *hp;
- *hp = e;
- e->clientData = NULL;
- e->namehash = h;
- strcpy(e->name, p);
- t->numEntries++;
-
- if (newPtr != NULL)
- *newPtr = TRUE;
- return (e);
-}
-
-/*
- *---------------------------------------------------------
- *
- * Hash_DeleteEntry --
- *
- * Delete the given hash table entry and free memory associated with
- * it.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Hash chain that entry lives in is modified and memory is freed.
- *
- *---------------------------------------------------------
- */
-void
-Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e)
-{
- Hash_Entry **hp, *p;
-
- if (e == NULL)
- return;
- for (hp = &t->bucketPtr[e->namehash & t->mask];
- (p = *hp) != NULL; hp = &p->next) {
- if (p == e) {
- *hp = p->next;
- free(p);
- t->numEntries--;
- return;
- }
- }
- write(STDERR_FILENO, "bad call to Hash_DeleteEntry\n", 29);
- abort();
-}
-
-/*
- *---------------------------------------------------------
- *
- * Hash_EnumFirst --
- * This procedure sets things up for a complete search
- * of all entries recorded in the hash table.
- *
- * Results:
- * The return value is the address of the first entry in
- * the hash table, or NULL if the table is empty.
- *
- * Side Effects:
- * The information in searchPtr is initialized so that successive
- * calls to Hash_Next will return successive HashEntry's
- * from the table.
- *
- *---------------------------------------------------------
- */
-Hash_Entry *
-Hash_EnumFirst(const Hash_Table *t, Hash_Search *searchPtr)
-{
-
- searchPtr->tablePtr = t;
- searchPtr->nextIndex = 0;
- searchPtr->hashEntryPtr = NULL;
- return (Hash_EnumNext(searchPtr));
-}
-
-/*
- *---------------------------------------------------------
- *
- * Hash_EnumNext --
- * This procedure returns successive entries in the hash table.
- *
- * Results:
- * The return value is a pointer to the next HashEntry
- * in the table, or NULL when the end of the table is
- * reached.
- *
- * Side Effects:
- * The information in searchPtr is modified to advance to the
- * next entry.
- *
- *---------------------------------------------------------
- */
-Hash_Entry *
-Hash_EnumNext(Hash_Search *searchPtr)
-{
- Hash_Entry *e;
- const Hash_Table *t = searchPtr->tablePtr;
-
- /*
- * The hashEntryPtr field points to the most recently returned
- * entry, or is NULL if we are starting up. If not NULL, we have
- * to start at the next one in the chain.
- */
- e = searchPtr->hashEntryPtr;
- if (e != NULL)
- e = e->next;
- /*
- * If the chain ran out, or if we are starting up, we need to
- * find the next nonempty chain.
- */
- while (e == NULL) {
- if (searchPtr->nextIndex >= t->size)
- return (NULL);
- e = t->bucketPtr[searchPtr->nextIndex++];
- }
- searchPtr->hashEntryPtr = e;
- return (e);
-}
-
-/*
- *---------------------------------------------------------
- *
- * RebuildTable --
- * This local routine makes a new hash table that
- * is larger than the old one.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The entire hash table is moved, so any bucket numbers
- * from the old table are invalid.
- *
- *---------------------------------------------------------
- */
-static void
-RebuildTable(Hash_Table *t)
-{
- Hash_Entry *e, *next = NULL, **hp, **xp;
- int i, mask;
- Hash_Entry **oldhp;
- int oldsize;
-
- oldhp = t->bucketPtr;
- oldsize = i = t->size;
- i <<= 1;
- t->size = i;
- t->mask = mask = i - 1;
- t->bucketPtr = hp = emalloc(sizeof(*hp) * i);
- while (--i >= 0)
- *hp++ = NULL;
- for (hp = oldhp, i = oldsize; --i >= 0;) {
- for (e = *hp++; e != NULL; e = next) {
- next = e->next;
- xp = &t->bucketPtr[e->namehash & mask];
- e->next = *xp;
- *xp = e;
- }
- }
- free(oldhp);
-}
diff --git a/usr.bin/make/hash.h b/usr.bin/make/hash.h
deleted file mode 100644
index 80dd74d..0000000
--- a/usr.bin/make/hash.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*-
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1988, 1989 by Adam de Boor
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)hash.h 8.1 (Berkeley) 6/6/93
- * $FreeBSD$
- */
-
-#ifndef hash_h_f6312f46
-#define hash_h_f6312f46
-
-/* hash.h --
- *
- * This file contains definitions used by the hash module,
- * which maintains hash tables.
- */
-
-#include "util.h"
-
-/*
- * The following defines one entry in the hash table.
- */
-typedef struct Hash_Entry {
- struct Hash_Entry *next; /* Link entries within same bucket. */
- void *clientData; /* Data associated with key. */
- unsigned namehash; /* hash value of key */
- char name[1]; /* key string */
-} Hash_Entry;
-
-typedef struct Hash_Table {
- struct Hash_Entry **bucketPtr; /* Buckets in the table */
- int size; /* Actual size of array. */
- int numEntries; /* Number of entries in the table. */
- int mask; /* Used to select bits for hashing. */
-} Hash_Table;
-
-/*
- * The following structure is used by the searching routines
- * to record where we are in the search.
- */
-typedef struct Hash_Search {
- const Hash_Table *tablePtr; /* Table being searched. */
- int nextIndex; /* Next bucket to check */
- Hash_Entry *hashEntryPtr; /* Next entry in current bucket */
-} Hash_Search;
-
-/*
- * Macros.
- */
-
-/*
- * void *Hash_GetValue(const Hash_Entry *h)
- */
-#define Hash_GetValue(h) ((h)->clientData)
-
-/*
- * Hash_SetValue(Hash_Entry *h, void *val);
- */
-#define Hash_SetValue(h, val) ((h)->clientData = (val))
-
-void Hash_InitTable(Hash_Table *, int);
-void Hash_DeleteTable(Hash_Table *);
-Hash_Entry *Hash_FindEntry(const Hash_Table *, const char *);
-Hash_Entry *Hash_CreateEntry(Hash_Table *, const char *, Boolean *);
-void Hash_DeleteEntry(Hash_Table *, Hash_Entry *);
-Hash_Entry *Hash_EnumFirst(const Hash_Table *, Hash_Search *);
-Hash_Entry *Hash_EnumNext(Hash_Search *);
-
-#endif /* hash_h_f6312f46 */
diff --git a/usr.bin/make/hash_tables.c b/usr.bin/make/hash_tables.c
deleted file mode 100644
index 27909d0..0000000
--- a/usr.bin/make/hash_tables.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * DO NOT EDIT
- * $FreeBSD$
- * auto-generated from FreeBSD: src/usr.bin/make/parse.c,v 1.114 2008/03/12 14:50:58 obrien Exp
- * DO NOT EDIT
- */
-#include <sys/types.h>
-
-#include "hash_tables.h"
-
-/*
- * d=2
- * n=40
- * m=19
- * c=2.09
- * maxlen=1
- * minklen=2
- * maxklen=9
- * minchar=97
- * maxchar=119
- * loop=0
- * numiter=1
- * seed=
- */
-
-static const signed char directive_g[] = {
- 8, 0, 0, 5, 6, -1, 17, 15, 10, 6,
- -1, -1, 10, 0, 0, -1, 18, 2, 3, 0,
- 7, -1, -1, -1, 0, 14, -1, -1, 11, 16,
- -1, -1, 0, -1, 0, 0, 17, 0, -1, 1,
-};
-
-static const u_char directive_T0[] = {
- 26, 14, 19, 35, 10, 34, 18, 27, 1, 17,
- 22, 37, 12, 12, 36, 21, 0, 6, 1, 25,
- 9, 4, 19,
-};
-
-static const u_char directive_T1[] = {
- 25, 22, 19, 0, 2, 18, 33, 18, 30, 4,
- 30, 9, 21, 19, 16, 12, 35, 34, 4, 19,
- 9, 33, 16,
-};
-
-
-int
-directive_hash(const u_char *key, size_t len)
-{
- unsigned f0, f1;
- const u_char *kp = key;
-
- if (len < 2 || len > 9)
- return -1;
-
- for (f0=f1=0; kp < key + len; ++kp) {
- if (*kp < 97 || *kp > 119)
- return -1;
- f0 += directive_T0[-97 + *kp];
- f1 += directive_T1[-97 + *kp];
- }
-
- f0 %= 40;
- f1 %= 40;
-
- return (directive_g[f0] + directive_g[f1]) % 19;
-}
-/*
- * d=2
- * n=74
- * m=35
- * c=2.09
- * maxlen=1
- * minklen=4
- * maxklen=13
- * minchar=46
- * maxchar=95
- * loop=0
- * numiter=4
- * seed=
- */
-
-static const signed char keyword_g[] = {
- 12, 18, 7, 25, 30, 5, -1, -1, -1, 7,
- -1, 0, 33, 0, 4, -1, -1, 13, 29, 0,
- -1, 28, -1, 28, -1, 0, -1, 27, 4, 34,
- -1, -1, -1, 30, 13, 10, -1, -1, 0, 10,
- 24, -1, -1, -1, 0, 6, 0, 0, -1, 23,
- -1, -1, -1, 0, -1, 23, -1, -1, 19, 4,
- -1, 31, 12, 16, -1, 20, 22, 9, 0, -1,
- -1, 9, 4, 0,
-};
-
-static const u_char keyword_T0[] = {
- 34, 28, 50, 61, 14, 57, 48, 60, 20, 67,
- 60, 63, 0, 24, 28, 2, 49, 64, 18, 23,
- 36, 33, 40, 14, 38, 42, 71, 49, 2, 53,
- 53, 37, 7, 29, 24, 21, 12, 50, 59, 10,
- 43, 23, 0, 44, 47, 6, 46, 22, 48, 64,
-};
-
-static const u_char keyword_T1[] = {
- 18, 67, 39, 60, 7, 70, 2, 26, 31, 18,
- 73, 47, 61, 17, 38, 50, 22, 52, 13, 55,
- 56, 32, 63, 4, 64, 55, 49, 21, 47, 67,
- 33, 66, 60, 73, 30, 68, 69, 32, 72, 4,
- 28, 49, 51, 15, 66, 68, 43, 67, 46, 56,
-};
-
-
-int
-keyword_hash(const u_char *key, size_t len)
-{
- unsigned f0, f1;
- const u_char *kp = key;
-
- if (len < 4 || len > 13)
- return -1;
-
- for (f0=f1=0; *kp; ++kp) {
- if (*kp < 46 || *kp > 95)
- return -1;
- f0 += keyword_T0[-46 + *kp];
- f1 += keyword_T1[-46 + *kp];
- }
-
- f0 %= 74;
- f1 %= 74;
-
- return (keyword_g[f0] + keyword_g[f1]) % 35;
-}
diff --git a/usr.bin/make/hash_tables.h b/usr.bin/make/hash_tables.h
deleted file mode 100644
index 8fa43a5..0000000
--- a/usr.bin/make/hash_tables.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2005 Max Okumoto.
- * All rights reserved.
- *
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
- *
- * $FreeBSD$
- */
-#ifndef hash_tables_h_
-#define hash_tables_h_
-
-#include <sys/types.h>
-
-int directive_hash(const u_char *, size_t);
-int keyword_hash(const u_char *, size_t);
-
-#endif
diff --git a/usr.bin/make/job.c b/usr.bin/make/job.c
deleted file mode 100644
index 6fee0ec..0000000
--- a/usr.bin/make/job.c
+++ /dev/null
@@ -1,3391 +0,0 @@
-/*-
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1988, 1989 by Adam de Boor
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)job.c 8.2 (Berkeley) 3/19/94
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*-
- * job.c --
- * handle the creation etc. of our child processes.
- *
- * Interface:
- * Job_Make Start the creation of the given target.
- *
- * Job_CatchChildren
- * Check for and handle the termination of any children.
- * This must be called reasonably frequently to keep the
- * whole make going at a decent clip, since job table
- * entries aren't removed until their process is caught
- * this way. Its single argument is TRUE if the function
- * should block waiting for a child to terminate.
- *
- * Job_CatchOutput Print any output our children have produced. Should
- * also be called fairly frequently to keep the user
- * informed of what's going on. If no output is waiting,
- * it will block for a time given by the SEL_* constants,
- * below, or until output is ready.
- *
- * Job_Init Called to intialize this module. in addition, any
- * commands attached to the .BEGIN target are executed
- * before this function returns. Hence, the makefile must
- * have been parsed before this function is called.
- *
- * Job_Full Return TRUE if the job table is filled.
- *
- * Job_Empty Return TRUE if the job table is completely empty.
- *
- * Job_Finish Perform any final processing which needs doing. This
- * includes the execution of any commands which have
- * been/were attached to the .END target. It should only
- * be called when the job table is empty.
- *
- * Job_AbortAll Abort all currently running jobs. It doesn't handle
- * output or do anything for the jobs, just kills them.
- * It should only be called in an emergency, as it were.
- *
- * Job_CheckCommands
- * Verify that the commands for a target are ok. Provide
- * them if necessary and possible.
- *
- * Job_Touch Update a target without really updating it.
- *
- * Job_Wait Wait for all currently-running jobs to finish.
- *
- * compat.c --
- * The routines in this file implement the full-compatibility
- * mode of PMake. Most of the special functionality of PMake
- * is available in this mode. Things not supported:
- * - different shells.
- * - friendly variable substitution.
- *
- * Interface:
- * Compat_Run Initialize things for this module and recreate
- * thems as need creatin'
- */
-
-#include <sys/queue.h>
-#include <sys/types.h>
-#include <sys/select.h>
-#include <sys/stat.h>
-#ifdef USE_KQUEUE
-#include <sys/event.h>
-#endif
-#include <sys/wait.h>
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <paths.h>
-#include <string.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <utime.h>
-
-#include "arch.h"
-#include "buf.h"
-#include "config.h"
-#include "dir.h"
-#include "globals.h"
-#include "GNode.h"
-#include "job.h"
-#include "make.h"
-#include "parse.h"
-#include "proc.h"
-#include "shell.h"
-#include "str.h"
-#include "suff.h"
-#include "targ.h"
-#include "util.h"
-#include "var.h"
-
-#define TMPPAT "makeXXXXXXXXXX"
-
-#ifndef USE_KQUEUE
-/*
- * The SEL_ constants determine the maximum amount of time spent in select
- * before coming out to see if a child has finished. SEL_SEC is the number of
- * seconds and SEL_USEC is the number of micro-seconds
- */
-#define SEL_SEC 2
-#define SEL_USEC 0
-#endif /* !USE_KQUEUE */
-
-/*
- * Job Table definitions.
- *
- * The job "table" is kept as a linked Lst in 'jobs', with the number of
- * active jobs maintained in the 'nJobs' variable. At no time will this
- * exceed the value of 'maxJobs', initialized by the Job_Init function.
- *
- * When a job is finished, the Make_Update function is called on each of the
- * parents of the node which was just remade. This takes care of the upward
- * traversal of the dependency graph.
- */
-#define JOB_BUFSIZE 1024
-typedef struct Job {
- pid_t pid; /* The child's process ID */
-
- struct GNode *node; /* The target the child is making */
-
- /*
- * A LstNode for the first command to be saved after the job completes.
- * This is NULL if there was no "..." in the job's commands.
- */
- LstNode *tailCmds;
-
- /*
- * An FILE* for writing out the commands. This is only
- * used before the job is actually started.
- */
- FILE *cmdFILE;
-
- /*
- * A word of flags which determine how the module handles errors,
- * echoing, etc. for the job
- */
- short flags; /* Flags to control treatment of job */
-#define JOB_IGNERR 0x001 /* Ignore non-zero exits */
-#define JOB_SILENT 0x002 /* no output */
-#define JOB_SPECIAL 0x004 /* Target is a special one. i.e. run it locally
- * if we can't export it and maxLocal is 0 */
-#define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing
- * commands */
-#define JOB_FIRST 0x020 /* Job is first job for the node */
-#define JOB_RESTART 0x080 /* Job needs to be completely restarted */
-#define JOB_RESUME 0x100 /* Job needs to be resumed b/c it stopped,
- * for some reason */
-#define JOB_CONTINUING 0x200 /* We are in the process of resuming this job.
- * Used to avoid infinite recursion between
- * JobFinish and JobRestart */
-
- /* union for handling shell's output */
- union {
- /*
- * This part is used when usePipes is true.
- * The output is being caught via a pipe and the descriptors
- * of our pipe, an array in which output is line buffered and
- * the current position in that buffer are all maintained for
- * each job.
- */
- struct {
- /*
- * Input side of pipe associated with
- * job's output channel
- */
- int op_inPipe;
-
- /*
- * Output side of pipe associated with job's
- * output channel
- */
- int op_outPipe;
-
- /*
- * Buffer for storing the output of the
- * job, line by line
- */
- char op_outBuf[JOB_BUFSIZE + 1];
-
- /* Current position in op_outBuf */
- int op_curPos;
- } o_pipe;
-
- /*
- * If usePipes is false the output is routed to a temporary
- * file and all that is kept is the name of the file and the
- * descriptor open to the file.
- */
- struct {
- /* Name of file to which shell output was rerouted */
- char of_outFile[PATH_MAX];
-
- /*
- * Stream open to the output file. Used to funnel all
- * from a single job to one file while still allowing
- * multiple shell invocations
- */
- int of_outFd;
- } o_file;
-
- } output; /* Data for tracking a shell's output */
-
- TAILQ_ENTRY(Job) link; /* list link */
-} Job;
-
-#define outPipe output.o_pipe.op_outPipe
-#define inPipe output.o_pipe.op_inPipe
-#define outBuf output.o_pipe.op_outBuf
-#define curPos output.o_pipe.op_curPos
-#define outFile output.o_file.of_outFile
-#define outFd output.o_file.of_outFd
-
-TAILQ_HEAD(JobList, Job);
-
-/*
- * error handling variables
- */
-static int aborting = 0; /* why is the make aborting? */
-#define ABORT_ERROR 1 /* Because of an error */
-#define ABORT_INTERRUPT 2 /* Because it was interrupted */
-#define ABORT_WAIT 3 /* Waiting for jobs to finish */
-
-/*
- * XXX: Avoid SunOS bug... FILENO() is fp->_file, and file
- * is a char! So when we go above 127 we turn negative!
- */
-#define FILENO(a) ((unsigned)fileno(a))
-
-/*
- * post-make command processing. The node postCommands is really just the
- * .END target but we keep it around to avoid having to search for it
- * all the time.
- */
-static GNode *postCommands;
-
-/*
- * The number of commands actually printed for a target. Should this
- * number be 0, no shell will be executed.
- */
-static int numCommands;
-
-/*
- * Return values from JobStart.
- */
-#define JOB_RUNNING 0 /* Job is running */
-#define JOB_ERROR 1 /* Error in starting the job */
-#define JOB_FINISHED 2 /* The job is already finished */
-#define JOB_STOPPED 3 /* The job is stopped */
-
-/*
- * The maximum number of jobs that may run. This is initialize from the
- * -j argument for the leading make and from the FIFO for sub-makes.
- */
-static int maxJobs;
-
-static int nJobs; /* The number of children currently running */
-
-/* The structures that describe them */
-static struct JobList jobs = TAILQ_HEAD_INITIALIZER(jobs);
-
-static Boolean jobFull; /* Flag to tell when the job table is full. It
- * is set TRUE when (1) the total number of
- * running jobs equals the maximum allowed */
-#ifdef USE_KQUEUE
-static int kqfd; /* File descriptor obtained by kqueue() */
-#else
-static fd_set outputs; /* Set of descriptors of pipes connected to
- * the output channels of children */
-#endif
-
-static GNode *lastNode; /* The node for which output was most recently
- * produced. */
-static const char *targFmt; /* Format string to use to head output from a
- * job when it's not the most-recent job heard
- * from */
-static char *targPrefix = NULL; /* What we print at the start of targFmt */
-
-#define TARG_FMT "%s %s ---\n" /* Default format */
-#define MESSAGE(fp, gn) \
- fprintf(fp, targFmt, targPrefix, gn->name);
-
-/*
- * When JobStart attempts to run a job but isn't allowed to
- * or when Job_CatchChildren detects a job that has
- * been stopped somehow, the job is placed on the stoppedJobs queue to be run
- * when the next job finishes.
- *
- * Lst of Job structures describing jobs that were stopped due to
- * concurrency limits or externally
- */
-static struct JobList stoppedJobs = TAILQ_HEAD_INITIALIZER(stoppedJobs);
-
-static int fifoFd; /* Fd of our job fifo */
-static char fifoName[] = "/tmp/make_fifo_XXXXXXXXX";
-static int fifoMaster;
-
-static volatile sig_atomic_t interrupted;
-
-
-#if defined(USE_PGRP) && defined(SYSV)
-# define KILL(pid, sig) killpg(-(pid), (sig))
-#else
-# if defined(USE_PGRP)
-# define KILL(pid, sig) killpg((pid), (sig))
-# else
-# define KILL(pid, sig) kill((pid), (sig))
-# endif
-#endif
-
-/*
- * Grmpf... There is no way to set bits of the wait structure
- * anymore with the stupid W*() macros. I liked the union wait
- * stuff much more. So, we devise our own macros... This is
- * really ugly, use dramamine sparingly. You have been warned.
- */
-#define W_SETMASKED(st, val, fun) \
- { \
- int sh = (int)~0; \
- int mask = fun(sh); \
- \
- for (sh = 0; ((mask >> sh) & 1) == 0; sh++) \
- continue; \
- *(st) = (*(st) & ~mask) | ((val) << sh); \
- }
-
-#define W_SETTERMSIG(st, val) W_SETMASKED(st, val, WTERMSIG)
-#define W_SETEXITSTATUS(st, val) W_SETMASKED(st, val, WEXITSTATUS)
-
-static void JobRestart(Job *);
-static int JobStart(GNode *, int, Job *);
-static void JobDoOutput(Job *, Boolean);
-static void JobInterrupt(int, int);
-static void JobRestartJobs(void);
-static int Compat_RunCommand(LstNode *, struct GNode *);
-
-static GNode *curTarg = NULL;
-static GNode *ENDNode;
-
-/**
- * Create a fifo file with a uniq filename, and returns a file
- * descriptor to that fifo.
- */
-static int
-mkfifotemp(char *template)
-{
- char *start;
- char *pathend;
- char *ptr;
- const unsigned char padchar[] =
- "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-
- if (template[0] == '\0') {
- errno = EINVAL; /* bad input string */
- return (-1);
- }
-
- /* Find end of template string. */
- pathend = strchr(template, '\0');
- ptr = pathend - 1;
-
- /*
- * Starting from the end of the template replace spaces with 'X' in
- * them with random characters until there are no more 'X'.
- */
- while (ptr >= template && *ptr == 'X') {
- uint32_t rand_num =
-#if __FreeBSD_version < 800041
- arc4random() % (sizeof(padchar) - 1);
-#else
- arc4random_uniform(sizeof(padchar) - 1);
-#endif
- *ptr-- = padchar[rand_num];
- }
- start = ptr + 1;
-
- /* Check the target directory. */
- for (; ptr > template; --ptr) {
- if (*ptr == '/') {
- struct stat sbuf;
-
- *ptr = '\0';
- if (stat(template, &sbuf) != 0)
- return (-1);
-
- if (!S_ISDIR(sbuf.st_mode)) {
- errno = ENOTDIR;
- return (-1);
- }
- *ptr = '/';
- break;
- }
- }
-
- for (;;) {
- if (mkfifo(template, 0600) == 0) {
- int fd;
-
- if ((fd = open(template, O_RDWR, 0600)) < 0) {
- unlink(template);
- return (-1);
- } else {
- return (fd);
- }
- } else {
- if (errno != EEXIST) {
- return (-1);
- }
- }
-
- /*
- * If we have a collision, cycle through the space of
- * filenames.
- */
- for (ptr = start;;) {
- char *pad;
-
- if (*ptr == '\0' || ptr == pathend)
- return (-1);
-
- pad = strchr(padchar, *ptr);
- if (pad == NULL || *++pad == '\0') {
- *ptr++ = padchar[0];
- } else {
- *ptr++ = *pad;
- break;
- }
- }
- }
- /*NOTREACHED*/
-}
-
-static void
-catch_child(int sig __unused)
-{
-}
-
-/**
- */
-void
-Proc_Init(void)
-{
- /*
- * Catch SIGCHLD so that we get kicked out of select() when we
- * need to look at a child. This is only known to matter for the
- * -j case (perhaps without -P).
- *
- * XXX this is intentionally misplaced.
- */
- struct sigaction sa;
-
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
- sa.sa_handler = catch_child;
- sigaction(SIGCHLD, &sa, NULL);
-}
-
-/**
- * Wait for child process to terminate.
- */
-static int
-ProcWait(ProcStuff *ps)
-{
- pid_t pid;
- int status;
-
- /*
- * Wait for the process to exit.
- */
- for (;;) {
- pid = wait(&status);
- if (pid == -1 && errno != EINTR) {
- Fatal("error in wait: %d", pid);
- /* NOTREACHED */
- }
- if (pid == ps->child_pid) {
- break;
- }
- if (interrupted) {
- break;
- }
- }
-
- return (status);
-}
-
-/**
- * JobCatchSignal
- * Got a signal. Set global variables and hope that someone will
- * handle it.
- */
-static void
-JobCatchSig(int signo)
-{
-
- interrupted = signo;
-}
-
-/**
- * JobPassSig --
- * Pass a signal on to all local jobs if
- * USE_PGRP is defined, then die ourselves.
- *
- * Side Effects:
- * We die by the same signal.
- */
-static void
-JobPassSig(int signo)
-{
- Job *job;
- sigset_t nmask, omask;
- struct sigaction act;
-
- sigemptyset(&nmask);
- sigaddset(&nmask, signo);
- sigprocmask(SIG_SETMASK, &nmask, &omask);
-
- DEBUGF(JOB, ("JobPassSig(%d) called.\n", signo));
- TAILQ_FOREACH(job, &jobs, link) {
- DEBUGF(JOB, ("JobPassSig passing signal %d to child %jd.\n",
- signo, (intmax_t)job->pid));
- KILL(job->pid, signo);
- }
-
- /*
- * Deal with proper cleanup based on the signal received. We only run
- * the .INTERRUPT target if the signal was in fact an interrupt.
- * The other three termination signals are more of a "get out *now*"
- * command.
- */
- if (signo == SIGINT) {
- JobInterrupt(TRUE, signo);
- } else if (signo == SIGHUP || signo == SIGTERM || signo == SIGQUIT) {
- JobInterrupt(FALSE, signo);
- }
-
- /*
- * Leave gracefully if SIGQUIT, rather than core dumping.
- */
- if (signo == SIGQUIT) {
- signo = SIGINT;
- }
-
- /*
- * Send ourselves the signal now we've given the message to everyone
- * else. Note we block everything else possible while we're getting
- * the signal. This ensures that all our jobs get continued when we
- * wake up before we take any other signal.
- * XXX this comment seems wrong.
- */
- act.sa_handler = SIG_DFL;
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- sigaction(signo, &act, NULL);
-
- DEBUGF(JOB, ("JobPassSig passing signal to self, mask = %x.\n",
- ~0 & ~(1 << (signo - 1))));
- signal(signo, SIG_DFL);
-
- KILL(getpid(), signo);
-
- signo = SIGCONT;
- TAILQ_FOREACH(job, &jobs, link) {
- DEBUGF(JOB, ("JobPassSig passing signal %d to child %jd.\n",
- signo, (intmax_t)job->pid));
- KILL(job->pid, signo);
- }
-
- sigprocmask(SIG_SETMASK, &omask, NULL);
- sigprocmask(SIG_SETMASK, &omask, NULL);
- act.sa_handler = JobPassSig;
- sigaction(signo, &act, NULL);
-}
-
-/**
- * JobPrintCommand --
- * Put out another command for the given job. If the command starts
- * with an @ or a - we process it specially. In the former case,
- * so long as the -s and -n flags weren't given to make, we stick
- * a shell-specific echoOff command in the script. In the latter,
- * we ignore errors for the entire job, unless the shell has error
- * control.
- * If the command is just "..." we take all future commands for this
- * job to be commands to be executed once the entire graph has been
- * made and return non-zero to signal that the end of the commands
- * was reached. These commands are later attached to the postCommands
- * node and executed by Job_Finish when all things are done.
- * This function is called from JobStart via LST_FOREACH.
- *
- * Results:
- * Always 0, unless the command was "..."
- *
- * Side Effects:
- * If the command begins with a '-' and the shell has no error control,
- * the JOB_IGNERR flag is set in the job descriptor.
- * If the command is "..." and we're not ignoring such things,
- * tailCmds is set to the successor node of the cmd.
- * numCommands is incremented if the command is actually printed.
- */
-static int
-JobPrintCommand(LstNode *cmdNode, Job *job)
-{
- Boolean noSpecials; /* true if we shouldn't worry about
- * inserting special commands into
- * the input stream. */
- Boolean shutUp = FALSE; /* true if we put a no echo command
- * into the command file */
- Boolean errOff = FALSE; /* true if we turned error checking
- * off before printing the command
- * and need to turn it back on */
- const char *cmdTemplate;/* Template to use when printing the command */
- char *cmd; /* Expanded command */
-
- noSpecials = (noExecute && !(job->node->type & OP_MAKE));
-
-#define DBPRINTF(fmt, arg) \
- DEBUGF(JOB, (fmt, arg)); \
- fprintf(job->cmdFILE, fmt, arg); \
- fflush(job->cmdFILE);
-
- /*
- * For debugging, we replace each command with the result of expanding
- * the variables in the command.
- */
- cmd = Buf_Peel(Var_Subst(Lst_Datum(cmdNode), job->node, FALSE));
- if (strcmp(cmd, "...") == 0) {
- free(cmd);
- job->node->type |= OP_SAVE_CMDS;
- if ((job->flags & JOB_IGNDOTS) == 0) {
- job->tailCmds = Lst_Succ(cmdNode);
- return (1);
- }
- return (0);
- }
- Lst_Replace(cmdNode, cmd);
-
- /*
- * Check for leading @', -' or +'s to control echoing, error checking,
- * and execution on -n.
- */
- while (*cmd == '@' || *cmd == '-' || *cmd == '+') {
- switch (*cmd) {
-
- case '@':
- shutUp = DEBUG(LOUD) ? FALSE : TRUE;
- break;
-
- case '-':
- errOff = TRUE;
- break;
-
- case '+':
- if (noSpecials) {
- /*
- * We're not actually exececuting anything...
- * but this one needs to be - use compat mode
- * just for it.
- */
- Compat_RunCommand(cmdNode, job->node);
- return (0);
- }
- break;
- }
- cmd++;
- }
-
- while (isspace((unsigned char)*cmd))
- cmd++;
-
- /*
- * Ignore empty commands
- */
- if (*cmd == '\0') {
- return (0);
- }
-
- cmdTemplate = "%s\n";
- numCommands += 1;
-
- if (shutUp) {
- if (!(job->flags & JOB_SILENT) && !noSpecials &&
- commandShell->hasEchoCtl) {
- DBPRINTF("%s\n", commandShell->echoOff);
- } else {
- shutUp = FALSE;
- }
- }
-
- if (errOff) {
- if (!(job->flags & JOB_IGNERR) && !noSpecials) {
- if (commandShell->hasErrCtl) {
- /*
- * We don't want the error-control commands
- * showing up either, so we turn off echoing
- * while executing them. We could put another
- * field in the shell structure to tell
- * JobDoOutput to look for this string too,
- * but why make it any more complex than
- * it already is?
- */
- if (!(job->flags & JOB_SILENT) && !shutUp &&
- commandShell->hasEchoCtl) {
- DBPRINTF("%s\n", commandShell->echoOff);
- DBPRINTF("%s\n", commandShell->ignErr);
- DBPRINTF("%s\n", commandShell->echoOn);
- } else {
- DBPRINTF("%s\n", commandShell->ignErr);
- }
- } else if (commandShell->ignErr &&
- *commandShell->ignErr != '\0') {
- /*
- * The shell has no error control, so we need to
- * be weird to get it to ignore any errors from
- * the command. If echoing is turned on, we turn
- * it off and use the errCheck template to echo
- * the command. Leave echoing off so the user
- * doesn't see the weirdness we go through to
- * ignore errors. Set cmdTemplate to use the
- * weirdness instead of the simple "%s\n"
- * template.
- */
- if (!(job->flags & JOB_SILENT) && !shutUp &&
- commandShell->hasEchoCtl) {
- DBPRINTF("%s\n", commandShell->echoOff);
- DBPRINTF(commandShell->errCheck, cmd);
- shutUp = TRUE;
- }
- cmdTemplate = commandShell->ignErr;
- /*
- * The error ignoration (hee hee) is already
- * taken care of by the ignErr template, so
- * pretend error checking is still on.
- */
- errOff = FALSE;
- } else {
- errOff = FALSE;
- }
- } else {
- errOff = FALSE;
- }
- }
-
- DBPRINTF(cmdTemplate, cmd);
-
- if (errOff) {
- /*
- * If echoing is already off, there's no point in issuing the
- * echoOff command. Otherwise we issue it and pretend it was on
- * for the whole command...
- */
- if (!shutUp && !(job->flags & JOB_SILENT) &&
- commandShell->hasEchoCtl) {
- DBPRINTF("%s\n", commandShell->echoOff);
- shutUp = TRUE;
- }
- DBPRINTF("%s\n", commandShell->errCheck);
- }
- if (shutUp) {
- DBPRINTF("%s\n", commandShell->echoOn);
- }
- return (0);
-}
-
-/**
- * JobClose --
- * Called to close both input and output pipes when a job is finished.
- *
- * Side Effects:
- * The file descriptors associated with the job are closed.
- */
-static void
-JobClose(Job *job)
-{
-
- if (usePipes) {
-#if !defined(USE_KQUEUE)
- FD_CLR(job->inPipe, &outputs);
-#endif
- if (job->outPipe != job->inPipe) {
- close(job->outPipe);
- }
- JobDoOutput(job, TRUE);
- close(job->inPipe);
- } else {
- close(job->outFd);
- JobDoOutput(job, TRUE);
- }
-}
-
-/**
- * JobFinish --
- * Do final processing for the given job including updating
- * parents and starting new jobs as available/necessary. Note
- * that we pay no attention to the JOB_IGNERR flag here.
- * This is because when we're called because of a noexecute flag
- * or something, jstat.w_status is 0 and when called from
- * Job_CatchChildren, the status is zeroed if it s/b ignored.
- *
- * Side Effects:
- * Some nodes may be put on the toBeMade queue.
- * Final commands for the job are placed on postCommands.
- *
- * If we got an error and are aborting (aborting == ABORT_ERROR) and
- * the job list is now empty, we are done for the day.
- * If we recognized an error (makeErrors !=0), we set the aborting flag
- * to ABORT_ERROR so no more jobs will be started.
- */
-static void
-JobFinish(Job *job, int *status)
-{
- Boolean done;
- LstNode *ln;
-
- if (WIFEXITED(*status)) {
- int job_status = WEXITSTATUS(*status);
-
- JobClose(job);
- /*
- * Deal with ignored errors in -B mode. We need to
- * print a message telling of the ignored error as
- * well as setting status.w_status to 0 so the next
- * command gets run. To do this, we set done to be
- * TRUE if in -B mode and the job exited non-zero.
- */
- if (job_status == 0) {
- done = FALSE;
- } else {
- if (job->flags & JOB_IGNERR) {
- done = TRUE;
- } else {
- /*
- * If it exited non-zero and either we're
- * doing things our way or we're not ignoring
- * errors, the job is finished. Similarly, if
- * the shell died because of a signal the job
- * is also finished. In these cases, finish
- * out the job's output before printing the
- * exit status...
- */
- done = TRUE;
- if (job->cmdFILE != NULL &&
- job->cmdFILE != stdout) {
- fclose(job->cmdFILE);
- }
-
- }
- }
- } else if (WIFSIGNALED(*status)) {
- if (WTERMSIG(*status) == SIGCONT) {
- /*
- * No need to close things down or anything.
- */
- done = FALSE;
- } else {
- /*
- * If it exited non-zero and either we're
- * doing things our way or we're not ignoring
- * errors, the job is finished. Similarly, if
- * the shell died because of a signal the job
- * is also finished. In these cases, finish
- * out the job's output before printing the
- * exit status...
- */
- JobClose(job);
- if (job->cmdFILE != NULL &&
- job->cmdFILE != stdout) {
- fclose(job->cmdFILE);
- }
- done = TRUE;
- }
- } else {
- /*
- * No need to close things down or anything.
- */
- done = FALSE;
- }
-
- if (WIFEXITED(*status)) {
- if (done || DEBUG(JOB)) {
- FILE *out;
-
- if (compatMake &&
- !usePipes &&
- (job->flags & JOB_IGNERR)) {
- /*
- * If output is going to a file and this job
- * is ignoring errors, arrange to have the
- * exit status sent to the output file as
- * well.
- */
- out = fdopen(job->outFd, "w");
- if (out == NULL)
- Punt("Cannot fdopen");
- } else {
- out = stdout;
- }
-
- DEBUGF(JOB, ("Process %jd exited.\n",
- (intmax_t)job->pid));
-
- if (WEXITSTATUS(*status) == 0) {
- if (DEBUG(JOB)) {
- if (usePipes && job->node != lastNode) {
- MESSAGE(out, job->node);
- lastNode = job->node;
- }
- fprintf(out,
- "*** [%s] Completed successfully\n",
- job->node->name);
- }
- } else {
- if (usePipes && job->node != lastNode) {
- MESSAGE(out, job->node);
- lastNode = job->node;
- }
- fprintf(out, "*** [%s] Error code %d%s\n",
- job->node->name,
- WEXITSTATUS(*status),
- (job->flags & JOB_IGNERR) ?
- " (ignored)" : "");
-
- if (job->flags & JOB_IGNERR) {
- *status = 0;
- }
- }
-
- fflush(out);
- }
- } else if (WIFSIGNALED(*status)) {
- if (done || DEBUG(JOB) || (WTERMSIG(*status) == SIGCONT)) {
- FILE *out;
-
- if (compatMake &&
- !usePipes &&
- (job->flags & JOB_IGNERR)) {
- /*
- * If output is going to a file and this job
- * is ignoring errors, arrange to have the
- * exit status sent to the output file as
- * well.
- */
- out = fdopen(job->outFd, "w");
- if (out == NULL)
- Punt("Cannot fdopen");
- } else {
- out = stdout;
- }
-
- if (WTERMSIG(*status) == SIGCONT) {
- /*
- * If the beastie has continued, shift the
- * Job from the stopped list to the running
- * one (or re-stop it if concurrency is
- * exceeded) and go and get another child.
- */
- if (job->flags & (JOB_RESUME | JOB_RESTART)) {
- if (usePipes && job->node != lastNode) {
- MESSAGE(out, job->node);
- lastNode = job->node;
- }
- fprintf(out, "*** [%s] Continued\n",
- job->node->name);
- }
- if (!(job->flags & JOB_CONTINUING)) {
- DEBUGF(JOB, ("Warning: process %jd was not "
- "continuing.\n", (intmax_t) job->pid));
- }
- job->flags &= ~JOB_CONTINUING;
- TAILQ_INSERT_TAIL(&jobs, job, link);
- nJobs += 1;
- DEBUGF(JOB, ("Process %jd is continuing locally.\n",
- (intmax_t) job->pid));
- if (nJobs == maxJobs) {
- jobFull = TRUE;
- DEBUGF(JOB, ("Job queue is full.\n"));
- }
- fflush(out);
- return;
-
- } else {
- if (usePipes && job->node != lastNode) {
- MESSAGE(out, job->node);
- lastNode = job->node;
- }
- fprintf(out,
- "*** [%s] Signal %d\n", job->node->name,
- WTERMSIG(*status));
- fflush(out);
- }
- }
- } else {
- /* STOPPED */
- FILE *out;
-
- if (compatMake && !usePipes && (job->flags & JOB_IGNERR)) {
- /*
- * If output is going to a file and this job
- * is ignoring errors, arrange to have the
- * exit status sent to the output file as
- * well.
- */
- out = fdopen(job->outFd, "w");
- if (out == NULL)
- Punt("Cannot fdopen");
- } else {
- out = stdout;
- }
-
- DEBUGF(JOB, ("Process %jd stopped.\n", (intmax_t) job->pid));
- if (usePipes && job->node != lastNode) {
- MESSAGE(out, job->node);
- lastNode = job->node;
- }
- fprintf(out, "*** [%s] Stopped -- signal %d\n",
- job->node->name, WSTOPSIG(*status));
- job->flags |= JOB_RESUME;
- TAILQ_INSERT_TAIL(&stoppedJobs, job, link);
- fflush(out);
- return;
- }
-
- /*
- * Now handle the -B-mode stuff. If the beast still isn't finished,
- * try and restart the job on the next command. If JobStart says it's
- * ok, it's ok. If there's an error, this puppy is done.
- */
- if (compatMake && WIFEXITED(*status) &&
- Lst_Succ(job->node->compat_command) != NULL) {
- switch (JobStart(job->node, job->flags & JOB_IGNDOTS, job)) {
- case JOB_RUNNING:
- done = FALSE;
- break;
- case JOB_ERROR:
- done = TRUE;
- W_SETEXITSTATUS(status, 1);
- break;
- case JOB_FINISHED:
- /*
- * If we got back a JOB_FINISHED code, JobStart has
- * already called Make_Update and freed the job
- * descriptor. We set done to false here to avoid fake
- * cycles and double frees. JobStart needs to do the
- * update so we can proceed up the graph when given
- * the -n flag..
- */
- done = FALSE;
- break;
- default:
- break;
- }
- } else {
- done = TRUE;
- }
-
- if (done && aborting != ABORT_ERROR &&
- aborting != ABORT_INTERRUPT && *status == 0) {
- /*
- * As long as we aren't aborting and the job didn't return a
- * non-zero status that we shouldn't ignore, we call
- * Make_Update to update the parents. In addition, any saved
- * commands for the node are placed on the .END target.
- */
- for (ln = job->tailCmds; ln != NULL; ln = LST_NEXT(ln)) {
- Lst_AtEnd(&postCommands->commands,
- Buf_Peel(
- Var_Subst(Lst_Datum(ln), job->node, FALSE)));
- }
-
- job->node->made = MADE;
- Make_Update(job->node);
- free(job);
-
- } else if (*status != 0) {
- makeErrors++;
- free(job);
- }
-
- JobRestartJobs();
-
- /*
- * Set aborting if any error.
- */
- if (makeErrors && !keepgoing && aborting != ABORT_INTERRUPT) {
- /*
- * If we found any errors in this batch of children and the -k
- * flag wasn't given, we set the aborting flag so no more jobs
- * get started.
- */
- aborting = ABORT_ERROR;
- }
-
- if (aborting == ABORT_ERROR && Job_Empty()) {
- /*
- * If we are aborting and the job table is now empty, we finish.
- */
- Finish(makeErrors);
- }
-}
-
-/**
- * Job_Touch
- * Touch the given target. Called by JobStart when the -t flag was
- * given. Prints messages unless told to be silent.
- *
- * Side Effects:
- * The data modification of the file is changed. In addition, if the
- * file did not exist, it is created.
- */
-void
-Job_Touch(GNode *gn, Boolean silent)
-{
- int streamID; /* ID of stream opened to do the touch */
- struct utimbuf times; /* Times for utime() call */
-
- if (gn->type & (OP_JOIN | OP_USE | OP_EXEC | OP_OPTIONAL)) {
- /*
- * .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual"
- * targets and, as such, shouldn't really be created.
- */
- return;
- }
-
- if (!silent) {
- fprintf(stdout, "touch %s\n", gn->name);
- fflush(stdout);
- }
-
- if (noExecute) {
- return;
- }
-
- if (gn->type & OP_ARCHV) {
- Arch_Touch(gn);
- } else if (gn->type & OP_LIB) {
- Arch_TouchLib(gn);
- } else {
- char *file = gn->path ? gn->path : gn->name;
-
- times.actime = times.modtime = now;
- if (utime(file, &times) < 0) {
- streamID = open(file, O_RDWR | O_CREAT, 0666);
-
- if (streamID >= 0) {
- char c;
-
- /*
- * Read and write a byte to the file to change
- * the modification time, then close the file.
- */
- if (read(streamID, &c, 1) == 1) {
- lseek(streamID, (off_t)0, SEEK_SET);
- write(streamID, &c, 1);
- }
-
- close(streamID);
- } else {
- fprintf(stdout, "*** couldn't touch %s: %s",
- file, strerror(errno));
- fflush(stdout);
- }
- }
- }
-}
-
-/**
- * Job_CheckCommands
- * Make sure the given node has all the commands it needs.
- *
- * Results:
- * TRUE if the commands list is/was ok.
- *
- * Side Effects:
- * The node will have commands from the .DEFAULT rule added to it
- * if it needs them.
- */
-Boolean
-Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
-{
-
- if (OP_NOP(gn->type) && Lst_IsEmpty(&gn->commands) &&
- (gn->type & OP_LIB) == 0) {
- /*
- * No commands. Look for .DEFAULT rule from which we might infer
- * commands.
- */
- if (DEFAULT != NULL && !Lst_IsEmpty(&DEFAULT->commands)) {
- /*
- * Make only looks for a .DEFAULT if the node was
- * never the target of an operator, so that's what we
- * do too. If a .DEFAULT was given, we substitute its
- * commands for gn's commands and set the IMPSRC
- * variable to be the target's name The DEFAULT node
- * acts like a transformation rule, in that gn also
- * inherits any attributes or sources attached to
- * .DEFAULT itself.
- */
- Make_HandleUse(DEFAULT, gn);
- Var_Set(IMPSRC, Var_Value(TARGET, gn), gn);
-
- } else if (Dir_MTime(gn) == 0) {
- /*
- * The node wasn't the target of an operator we have
- * no .DEFAULT rule to go on and the target doesn't
- * already exist. There's nothing more we can do for
- * this branch. If the -k flag wasn't given, we stop
- * in our tracks, otherwise we just don't update
- * this node's parents so they never get examined.
- */
- static const char msg[] =
- "make: don't know how to make";
-
- if (gn->type & OP_OPTIONAL) {
- fprintf(stdout, "%s %s(ignored)\n",
- msg, gn->name);
- fflush(stdout);
- } else if (keepgoing) {
- fprintf(stdout, "%s %s(continuing)\n",
- msg, gn->name);
- fflush(stdout);
- return (FALSE);
- } else {
-#ifndef WITHOUT_OLD_JOKE
- if (strcmp(gn->name,"love") == 0)
- (*abortProc)("Not war.");
- else
-#endif
- (*abortProc)("%s %s. Stop",
- msg, gn->name);
- return (FALSE);
- }
- }
- }
- return (TRUE);
-}
-
-/**
- * JobExec
- * Execute the shell for the given job. Called from JobStart and
- * JobRestart.
- *
- * Side Effects:
- * A shell is executed, outputs is altered and the Job structure added
- * to the job table.
- */
-static void
-JobExec(Job *job, char **argv)
-{
- ProcStuff ps;
-
- if (DEBUG(JOB)) {
- int i;
-
- DEBUGF(JOB, ("Running %s\n", job->node->name));
- DEBUGF(JOB, ("\tCommand: "));
- for (i = 0; argv[i] != NULL; i++) {
- DEBUGF(JOB, ("%s ", argv[i]));
- }
- DEBUGF(JOB, ("\n"));
- }
-
- /*
- * Some jobs produce no output and it's disconcerting to have
- * no feedback of their running (since they produce no output, the
- * banner with their name in it never appears). This is an attempt to
- * provide that feedback, even if nothing follows it.
- */
- if (lastNode != job->node && (job->flags & JOB_FIRST) &&
- !(job->flags & JOB_SILENT)) {
- MESSAGE(stdout, job->node);
- lastNode = job->node;
- }
-
- ps.in = FILENO(job->cmdFILE);
- if (usePipes) {
- /*
- * Set up the child's output to be routed through the
- * pipe we've created for it.
- */
- ps.out = job->outPipe;
- } else {
- /*
- * We're capturing output in a file, so we duplicate
- * the descriptor to the temporary file into the
- * standard output.
- */
- ps.out = job->outFd;
- }
- ps.err = STDERR_FILENO;
-
- ps.merge_errors = 1;
- ps.pgroup = 1;
- ps.searchpath = 0;
-
- ps.argv = argv;
- ps.argv_free = 0;
-
- /*
- * Fork. Warning since we are doing vfork() instead of fork(),
- * do not allocate memory in the child process!
- */
- if ((ps.child_pid = vfork()) == -1) {
- Punt("Cannot fork");
-
-
- } else if (ps.child_pid == 0) {
- /*
- * Child
- */
- if (fifoFd >= 0)
- close(fifoFd);
-
- Proc_Exec(&ps);
- /* NOTREACHED */
- }
-
- /*
- * Parent
- */
- job->pid = ps.child_pid;
-
- if (usePipes && (job->flags & JOB_FIRST)) {
- /*
- * The first time a job is run for a node, we set the
- * current position in the buffer to the beginning and
- * mark another stream to watch in the outputs mask.
- */
-#ifdef USE_KQUEUE
- struct kevent kev[2];
-#endif
- job->curPos = 0;
-
-#if defined(USE_KQUEUE)
- EV_SET(&kev[0], job->inPipe, EVFILT_READ, EV_ADD, 0, 0, job);
- EV_SET(&kev[1], job->pid, EVFILT_PROC,
- EV_ADD | EV_ONESHOT, NOTE_EXIT, 0, NULL);
- if (kevent(kqfd, kev, 2, NULL, 0, NULL) != 0) {
- /*
- * kevent() will fail if the job is already
- * finished
- */
- if (errno != EINTR && errno != EBADF && errno != ESRCH)
- Punt("kevent: %s", strerror(errno));
- }
-#else
- FD_SET(job->inPipe, &outputs);
-#endif /* USE_KQUEUE */
- }
-
- if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
- fclose(job->cmdFILE);
- job->cmdFILE = NULL;
- }
-
- /*
- * Now the job is actually running, add it to the table.
- */
- nJobs += 1;
- TAILQ_INSERT_TAIL(&jobs, job, link);
- if (nJobs == maxJobs) {
- jobFull = TRUE;
- }
-}
-
-/**
- * JobMakeArgv
- * Create the argv needed to execute the shell for a given job.
- */
-static void
-JobMakeArgv(Job *job, char **argv)
-{
- int argc;
- static char args[10]; /* For merged arguments */
-
- argv[0] = commandShell->name;
- argc = 1;
-
- if ((commandShell->exit && *commandShell->exit != '-') ||
- (commandShell->echo && *commandShell->echo != '-')) {
- /*
- * At least one of the flags doesn't have a minus before it, so
- * merge them together. Have to do this because the *(&(@*#*&#$#
- * Bourne shell thinks its second argument is a file to source.
- * Grrrr. Note the ten-character limitation on the combined
- * arguments.
- */
- sprintf(args, "-%s%s", (job->flags & JOB_IGNERR) ? "" :
- commandShell->exit ? commandShell->exit : "",
- (job->flags & JOB_SILENT) ? "" :
- commandShell->echo ? commandShell->echo : "");
-
- if (args[1]) {
- argv[argc] = args;
- argc++;
- }
- } else {
- if (!(job->flags & JOB_IGNERR) && commandShell->exit) {
- argv[argc] = commandShell->exit;
- argc++;
- }
- if (!(job->flags & JOB_SILENT) && commandShell->echo) {
- argv[argc] = commandShell->echo;
- argc++;
- }
- }
- argv[argc] = NULL;
-}
-
-/**
- * JobRestart
- * Restart a job that stopped for some reason. The job must be neither
- * on the jobs nor on the stoppedJobs list.
- *
- * Side Effects:
- * jobFull will be set if the job couldn't be run.
- */
-static void
-JobRestart(Job *job)
-{
-
- if (job->flags & JOB_RESTART) {
- /*
- * Set up the control arguments to the shell. This is based on
- * the flags set earlier for this job. If the JOB_IGNERR flag
- * is clear, the 'exit' flag of the commandShell is used to
- * cause it to exit upon receiving an error. If the JOB_SILENT
- * flag is clear, the 'echo' flag of the commandShell is used
- * to get it to start echoing as soon as it starts
- * processing commands.
- */
- char *argv[4];
-
- JobMakeArgv(job, argv);
-
- DEBUGF(JOB, ("Restarting %s...", job->node->name));
- if (nJobs >= maxJobs && !(job->flags & JOB_SPECIAL)) {
- /*
- * Not allowed to run -- put it back on the hold
- * queue and mark the table full
- */
- DEBUGF(JOB, ("holding\n"));
- TAILQ_INSERT_HEAD(&stoppedJobs, job, link);
- jobFull = TRUE;
- DEBUGF(JOB, ("Job queue is full.\n"));
- return;
- } else {
- /*
- * Job may be run locally.
- */
- DEBUGF(JOB, ("running locally\n"));
- }
- JobExec(job, argv);
-
- } else {
- /*
- * The job has stopped and needs to be restarted.
- * Why it stopped, we don't know...
- */
- DEBUGF(JOB, ("Resuming %s...", job->node->name));
- if ((nJobs < maxJobs || ((job->flags & JOB_SPECIAL) &&
- maxJobs == 0)) && nJobs != maxJobs) {
- /*
- * If we haven't reached the concurrency limit already
- * (or the job must be run and maxJobs is 0), it's ok
- * to resume it.
- */
- Boolean error;
- int status;
-
- error = (KILL(job->pid, SIGCONT) != 0);
-
- if (!error) {
- /*
- * Make sure the user knows we've continued
- * the beast and actually put the thing in the
- * job table.
- */
- job->flags |= JOB_CONTINUING;
- status = 0;
- W_SETTERMSIG(&status, SIGCONT);
- JobFinish(job, &status);
-
- job->flags &= ~(JOB_RESUME|JOB_CONTINUING);
- DEBUGF(JOB, ("done\n"));
- } else {
- Error("couldn't resume %s: %s",
- job->node->name, strerror(errno));
- status = 0;
- W_SETEXITSTATUS(&status, 1);
- JobFinish(job, &status);
- }
- } else {
- /*
- * Job cannot be restarted. Mark the table as full and
- * place the job back on the list of stopped jobs.
- */
- DEBUGF(JOB, ("table full\n"));
- TAILQ_INSERT_HEAD(&stoppedJobs, job, link);
- jobFull = TRUE;
- DEBUGF(JOB, ("Job queue is full.\n"));
- }
- }
-}
-
-/**
- * JobStart
- * Start a target-creation process going for the target described
- * by the graph node gn.
- *
- * Results:
- * JOB_ERROR if there was an error in the commands, JOB_FINISHED
- * if there isn't actually anything left to do for the job and
- * JOB_RUNNING if the job has been started.
- *
- * Side Effects:
- * A new Job node is created and added to the list of running
- * jobs. PMake is forked and a child shell created.
- */
-static int
-JobStart(GNode *gn, int flags, Job *previous)
-{
- Job *job; /* new job descriptor */
- char *argv[4]; /* Argument vector to shell */
- Boolean cmdsOK; /* true if the nodes commands were all right */
- Boolean noExec; /* Set true if we decide not to run the job */
- int tfd; /* File descriptor for temp file */
- LstNode *ln;
- char tfile[PATH_MAX];
- const char *tdir;
-
- if (interrupted) {
- JobPassSig(interrupted);
- return (JOB_ERROR);
- }
- if (previous != NULL) {
- previous->flags &= ~(JOB_FIRST | JOB_IGNERR | JOB_SILENT);
- job = previous;
- } else {
- job = emalloc(sizeof(Job));
- flags |= JOB_FIRST;
- }
-
- job->node = gn;
- job->tailCmds = NULL;
-
- /*
- * Set the initial value of the flags for this job based on the global
- * ones and the node's attributes... Any flags supplied by the caller
- * are also added to the field.
- */
- job->flags = 0;
- if (Targ_Ignore(gn)) {
- job->flags |= JOB_IGNERR;
- }
- if (Targ_Silent(gn)) {
- job->flags |= JOB_SILENT;
- }
- job->flags |= flags;
-
- /*
- * Check the commands now so any attributes from .DEFAULT have a chance
- * to migrate to the node.
- */
- if (!compatMake && (job->flags & JOB_FIRST)) {
- cmdsOK = Job_CheckCommands(gn, Error);
- } else {
- cmdsOK = TRUE;
- }
-
- if ((tdir = getenv("TMPDIR")) == NULL)
- tdir = _PATH_TMP;
-
- /*
- * If the -n flag wasn't given, we open up OUR (not the child's)
- * temporary file to stuff commands in it. The thing is rd/wr so we
- * don't need to reopen it to feed it to the shell. If the -n flag
- * *was* given, we just set the file to be stdout. Cute, huh?
- */
- if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) {
- /*
- * We're serious here, but if the commands were bogus, we're
- * also dead...
- */
- if (!cmdsOK) {
- DieHorribly();
- }
-
- snprintf(tfile, sizeof(tfile), "%s/%s", tdir, TMPPAT);
- if ((tfd = mkstemp(tfile)) == -1)
- Punt("Cannot create temp file: %s", strerror(errno));
- job->cmdFILE = fdopen(tfd, "w+");
- eunlink(tfile);
- if (job->cmdFILE == NULL) {
- close(tfd);
- Punt("Could not open %s", tfile);
- }
- fcntl(FILENO(job->cmdFILE), F_SETFD, 1);
- /*
- * Send the commands to the command file, flush all its
- * buffers then rewind and remove the thing.
- */
- noExec = FALSE;
-
- /*
- * Used to be backwards; replace when start doing multiple
- * commands per shell.
- */
- if (compatMake) {
- /*
- * Be compatible: If this is the first time for this
- * node, verify its commands are ok and open the
- * commands list for sequential access by later
- * invocations of JobStart. Once that is done, we take
- * the next command off the list and print it to the
- * command file. If the command was an ellipsis, note
- * that there's nothing more to execute.
- */
- if (job->flags & JOB_FIRST)
- gn->compat_command = Lst_First(&gn->commands);
- else
- gn->compat_command =
- Lst_Succ(gn->compat_command);
-
- if (gn->compat_command == NULL ||
- JobPrintCommand(gn->compat_command, job))
- noExec = TRUE;
-
- if (noExec && !(job->flags & JOB_FIRST)) {
- /*
- * If we're not going to execute anything, the
- * job is done and we need to close down the
- * various file descriptors we've opened for
- * output, then call JobDoOutput to catch the
- * final characters or send the file to the
- * screen... Note that the i/o streams are only
- * open if this isn't the first job. Note also
- * that this could not be done in
- * Job_CatchChildren b/c it wasn't clear if
- * there were more commands to execute or not...
- */
- JobClose(job);
- }
- } else {
- /*
- * We can do all the commands at once. hooray for sanity
- */
- numCommands = 0;
- LST_FOREACH(ln, &gn->commands) {
- if (JobPrintCommand(ln, job))
- break;
- }
-
- /*
- * If we didn't print out any commands to the shell
- * script, there's not much point in executing the
- * shell, is there?
- */
- if (numCommands == 0) {
- noExec = TRUE;
- }
- }
-
- } else if (noExecute) {
- /*
- * Not executing anything -- just print all the commands to
- * stdout in one fell swoop. This will still set up
- * job->tailCmds correctly.
- */
- if (lastNode != gn) {
- MESSAGE(stdout, gn);
- lastNode = gn;
- }
- job->cmdFILE = stdout;
-
- /*
- * Only print the commands if they're ok, but don't die if
- * they're not -- just let the user know they're bad and keep
- * going. It doesn't do any harm in this case and may do
- * some good.
- */
- if (cmdsOK) {
- LST_FOREACH(ln, &gn->commands) {
- if (JobPrintCommand(ln, job))
- break;
- }
- }
- /*
- * Don't execute the shell, thank you.
- */
- noExec = TRUE;
-
- } else {
- /*
- * Just touch the target and note that no shell should be
- * executed. Set cmdFILE to stdout to make life easier. Check
- * the commands, too, but don't die if they're no good -- it
- * does no harm to keep working up the graph.
- */
- job->cmdFILE = stdout;
- Job_Touch(gn, job->flags & JOB_SILENT);
- noExec = TRUE;
- }
-
- /*
- * If we're not supposed to execute a shell, don't.
- */
- if (noExec) {
- /*
- * Unlink and close the command file if we opened one
- */
- if (job->cmdFILE != stdout) {
- if (job->cmdFILE != NULL)
- fclose(job->cmdFILE);
- } else {
- fflush(stdout);
- }
-
- /*
- * We only want to work our way up the graph if we aren't here
- * because the commands for the job were no good.
- */
- if (cmdsOK) {
- if (aborting == 0) {
- for (ln = job->tailCmds; ln != NULL;
- ln = LST_NEXT(ln)) {
- Lst_AtEnd(&postCommands->commands,
- Buf_Peel(Var_Subst(Lst_Datum(ln),
- job->node, FALSE)));
- }
- job->node->made = MADE;
- Make_Update(job->node);
- }
- free(job);
- return(JOB_FINISHED);
- } else {
- free(job);
- return(JOB_ERROR);
- }
- } else {
- fflush(job->cmdFILE);
- }
-
- /*
- * Set up the control arguments to the shell. This is based on the flags
- * set earlier for this job.
- */
- JobMakeArgv(job, argv);
-
- /*
- * If we're using pipes to catch output, create the pipe by which we'll
- * get the shell's output. If we're using files, print out that we're
- * starting a job and then set up its temporary-file name.
- */
- if (!compatMake || (job->flags & JOB_FIRST)) {
- if (usePipes) {
- int fd[2];
-
- if (pipe(fd) == -1)
- Punt("Cannot create pipe: %s", strerror(errno));
- job->inPipe = fd[0];
- job->outPipe = fd[1];
- fcntl(job->inPipe, F_SETFD, 1);
- fcntl(job->outPipe, F_SETFD, 1);
- } else {
- fprintf(stdout, "Remaking `%s'\n", gn->name);
- fflush(stdout);
- snprintf(job->outFile, sizeof(job->outFile), "%s/%s",
- tdir, TMPPAT);
- if ((job->outFd = mkstemp(job->outFile)) == -1)
- Punt("cannot create temp file: %s",
- strerror(errno));
- fcntl(job->outFd, F_SETFD, 1);
- }
- }
-
- if (nJobs >= maxJobs && !(job->flags & JOB_SPECIAL) && maxJobs != 0) {
- /*
- * We've hit the limit of concurrency, so put the job on hold
- * until some other job finishes. Note that the special jobs
- * (.BEGIN, .INTERRUPT and .END) may be run even when the
- * limit has been reached (e.g. when maxJobs == 0).
- */
- jobFull = TRUE;
-
- DEBUGF(JOB, ("Can only run job locally.\n"));
- job->flags |= JOB_RESTART;
- TAILQ_INSERT_TAIL(&stoppedJobs, job, link);
- } else {
- if (nJobs >= maxJobs) {
- /*
- * If we're running this job as a special case
- * (see above), at least say the table is full.
- */
- jobFull = TRUE;
- DEBUGF(JOB, ("Local job queue is full.\n"));
- }
- JobExec(job, argv);
- }
- return (JOB_RUNNING);
-}
-
-static char *
-JobOutput(Job *job, char *cp, char *endp, int msg)
-{
- char *ecp;
-
- if (commandShell->noPrint) {
- ecp = strstr(cp, commandShell->noPrint);
- while (ecp != NULL) {
- if (cp != ecp) {
- *ecp = '\0';
- if (msg && job->node != lastNode) {
- MESSAGE(stdout, job->node);
- lastNode = job->node;
- }
- /*
- * The only way there wouldn't be a newline
- * after this line is if it were the last in
- * the buffer. However, since the non-printable
- * comes after it, there must be a newline, so
- * we don't print one.
- */
- fprintf(stdout, "%s", cp);
- fflush(stdout);
- }
- cp = ecp + strlen(commandShell->noPrint);
- if (cp != endp) {
- /*
- * Still more to print, look again after
- * skipping the whitespace following the
- * non-printable command....
- */
- cp++;
- while (*cp == ' ' || *cp == '\t' ||
- *cp == '\n') {
- cp++;
- }
- ecp = strstr(cp, commandShell->noPrint);
- } else {
- return (cp);
- }
- }
- }
- return (cp);
-}
-
-/**
- * JobDoOutput
- * This function is called at different times depending on
- * whether the user has specified that output is to be collected
- * via pipes or temporary files. In the former case, we are called
- * whenever there is something to read on the pipe. We collect more
- * output from the given job and store it in the job's outBuf. If
- * this makes up a line, we print it tagged by the job's identifier,
- * as necessary.
- * If output has been collected in a temporary file, we open the
- * file and read it line by line, transferring it to our own
- * output channel until the file is empty. At which point we
- * remove the temporary file.
- * In both cases, however, we keep our figurative eye out for the
- * 'noPrint' line for the shell from which the output came. If
- * we recognize a line, we don't print it. If the command is not
- * alone on the line (the character after it is not \0 or \n), we
- * do print whatever follows it.
- *
- * Side Effects:
- * curPos may be shifted as may the contents of outBuf.
- */
-static void
-JobDoOutput(Job *job, Boolean finish)
-{
- Boolean gotNL = FALSE; /* true if got a newline */
- Boolean fbuf; /* true if our buffer filled up */
- int nr; /* number of bytes read */
- int i; /* auxiliary index into outBuf */
- int max; /* limit for i (end of current data) */
- int nRead; /* (Temporary) number of bytes read */
- FILE *oFILE; /* Stream pointer to shell's output file */
- char inLine[132];
-
- if (usePipes) {
- /*
- * Read as many bytes as will fit in the buffer.
- */
- end_loop:
- gotNL = FALSE;
- fbuf = FALSE;
-
- nRead = read(job->inPipe, &job->outBuf[job->curPos],
- JOB_BUFSIZE - job->curPos);
- /*
- * Check for interrupt here too, because the above read may
- * block when the child process is stopped. In this case the
- * interrupt will unblock it (we don't use SA_RESTART).
- */
- if (interrupted)
- JobPassSig(interrupted);
-
- if (nRead < 0) {
- DEBUGF(JOB, ("JobDoOutput(piperead)"));
- nr = 0;
- } else {
- nr = nRead;
- }
-
- /*
- * If we hit the end-of-file (the job is dead), we must flush
- * its remaining output, so pretend we read a newline if
- * there's any output remaining in the buffer.
- * Also clear the 'finish' flag so we stop looping.
- */
- if (nr == 0 && job->curPos != 0) {
- job->outBuf[job->curPos] = '\n';
- nr = 1;
- finish = FALSE;
- } else if (nr == 0) {
- finish = FALSE;
- }
-
- /*
- * Look for the last newline in the bytes we just got. If there
- * is one, break out of the loop with 'i' as its index and
- * gotNL set TRUE.
- */
- max = job->curPos + nr;
- for (i = job->curPos + nr - 1; i >= job->curPos; i--) {
- if (job->outBuf[i] == '\n') {
- gotNL = TRUE;
- break;
- } else if (job->outBuf[i] == '\0') {
- /*
- * Why?
- */
- job->outBuf[i] = ' ';
- }
- }
-
- if (!gotNL) {
- job->curPos += nr;
- if (job->curPos == JOB_BUFSIZE) {
- /*
- * If we've run out of buffer space, we have
- * no choice but to print the stuff. sigh.
- */
- fbuf = TRUE;
- i = job->curPos;
- }
- }
- if (gotNL || fbuf) {
- /*
- * Need to send the output to the screen. Null terminate
- * it first, overwriting the newline character if there
- * was one. So long as the line isn't one we should
- * filter (according to the shell description), we print
- * the line, preceded by a target banner if this target
- * isn't the same as the one for which we last printed
- * something. The rest of the data in the buffer are
- * then shifted down to the start of the buffer and
- * curPos is set accordingly.
- */
- job->outBuf[i] = '\0';
- if (i >= job->curPos) {
- char *cp;
-
- cp = JobOutput(job, job->outBuf,
- &job->outBuf[i], FALSE);
-
- /*
- * There's still more in that buffer. This time,
- * though, we know there's no newline at the
- * end, so we add one of our own free will.
- */
- if (*cp != '\0') {
- if (job->node != lastNode) {
- MESSAGE(stdout, job->node);
- lastNode = job->node;
- }
- fprintf(stdout, "%s%s", cp,
- gotNL ? "\n" : "");
- fflush(stdout);
- }
- }
- if (i < max - 1) {
- /* shift the remaining characters down */
- memcpy(job->outBuf, &job->outBuf[i + 1],
- max - (i + 1));
- job->curPos = max - (i + 1);
-
- } else {
- /*
- * We have written everything out, so we just
- * start over from the start of the buffer.
- * No copying. No nothing.
- */
- job->curPos = 0;
- }
- }
- if (finish) {
- /*
- * If the finish flag is true, we must loop until we hit
- * end-of-file on the pipe. This is guaranteed to happen
- * eventually since the other end of the pipe is now
- * closed (we closed it explicitly and the child has
- * exited). When we do get an EOF, finish will be set
- * FALSE and we'll fall through and out.
- */
- goto end_loop;
- }
-
- } else {
- /*
- * We've been called to retrieve the output of the job from the
- * temporary file where it's been squirreled away. This consists
- * of opening the file, reading the output line by line, being
- * sure not to print the noPrint line for the shell we used,
- * then close and remove the temporary file. Very simple.
- *
- * Change to read in blocks and do FindSubString type things
- * as for pipes? That would allow for "@echo -n..."
- */
- oFILE = fopen(job->outFile, "r");
- if (oFILE != NULL) {
- fprintf(stdout, "Results of making %s:\n",
- job->node->name);
- fflush(stdout);
-
- while (fgets(inLine, sizeof(inLine), oFILE) != NULL) {
- char *cp, *endp, *oendp;
-
- cp = inLine;
- oendp = endp = inLine + strlen(inLine);
- if (endp[-1] == '\n') {
- *--endp = '\0';
- }
- cp = JobOutput(job, inLine, endp, FALSE);
-
- /*
- * There's still more in that buffer. This time,
- * though, we know there's no newline at the
- * end, so we add one of our own free will.
- */
- fprintf(stdout, "%s", cp);
- fflush(stdout);
- if (endp != oendp) {
- fprintf(stdout, "\n");
- fflush(stdout);
- }
- }
- fclose(oFILE);
- eunlink(job->outFile);
- }
- }
-}
-
-/**
- * Job_CatchChildren
- * Handle the exit of a child. Called from Make_Make.
- *
- * Side Effects:
- * The job descriptor is removed from the list of children.
- *
- * Notes:
- * We do waits, blocking or not, according to the wisdom of our
- * caller, until there are no more children to report. For each
- * job, call JobFinish to finish things off. This will take care of
- * putting jobs on the stoppedJobs queue.
- */
-void
-Job_CatchChildren(Boolean block)
-{
- pid_t pid; /* pid of dead child */
- Job *job; /* job descriptor for dead child */
- int status; /* Exit/termination status */
-
- /*
- * Don't even bother if we know there's no one around.
- */
- if (nJobs == 0) {
- return;
- }
-
- for (;;) {
- pid = waitpid(-1, &status,
- (block ? 0 : WNOHANG) | WUNTRACED);
- if (pid <= 0)
- break;
-
- DEBUGF(JOB, ("Process %jd exited or stopped.\n",
- (intmax_t)pid));
-
- TAILQ_FOREACH(job, &jobs, link) {
- if (job->pid == pid)
- break;
- }
-
- if (job == NULL) {
- if (WIFSIGNALED(status) &&
- (WTERMSIG(status) == SIGCONT)) {
- TAILQ_FOREACH(job, &jobs, link) {
- if (job->pid == pid)
- break;
- }
- if (job == NULL) {
- Error("Resumed child (%jd) "
- "not in table", (intmax_t)pid);
- continue;
- }
- TAILQ_REMOVE(&stoppedJobs, job, link);
- } else {
- Error("Child (%jd) not in table?",
- (intmax_t)pid);
- continue;
- }
- } else {
- TAILQ_REMOVE(&jobs, job, link);
- nJobs -= 1;
- if (fifoFd >= 0 && maxJobs > 1) {
- write(fifoFd, "+", 1);
- maxJobs--;
- if (nJobs >= maxJobs)
- jobFull = TRUE;
- else
- jobFull = FALSE;
- } else {
- DEBUGF(JOB, ("Job queue is no longer full.\n"));
- jobFull = FALSE;
- }
- }
-
- JobFinish(job, &status);
- }
- if (interrupted)
- JobPassSig(interrupted);
-}
-
-/**
- * Job_CatchOutput
- * Catch the output from our children, if we're using
- * pipes do so. Otherwise just block time until we get a
- * signal(most likely a SIGCHLD) since there's no point in
- * just spinning when there's nothing to do and the reaping
- * of a child can wait for a while.
- *
- * Side Effects:
- * Output is read from pipes if we're piping.
- * -----------------------------------------------------------------------
- */
-void
-#ifdef USE_KQUEUE
-Job_CatchOutput(int flag __unused)
-#else
-Job_CatchOutput(int flag)
-#endif
-{
- int nfds;
-#ifdef USE_KQUEUE
-#define KEV_SIZE 4
- struct kevent kev[KEV_SIZE];
- int i;
-#else
- struct timeval timeout;
- fd_set readfds;
- Job *job;
-#endif
-
- fflush(stdout);
-
- if (usePipes) {
-#ifdef USE_KQUEUE
- if ((nfds = kevent(kqfd, NULL, 0, kev, KEV_SIZE, NULL)) == -1) {
- if (errno != EINTR)
- Punt("kevent: %s", strerror(errno));
- if (interrupted)
- JobPassSig(interrupted);
- } else {
- for (i = 0; i < nfds; i++) {
- if (kev[i].flags & EV_ERROR) {
- warnc(kev[i].data, "kevent");
- continue;
- }
- switch (kev[i].filter) {
- case EVFILT_READ:
- JobDoOutput(kev[i].udata, FALSE);
- break;
- case EVFILT_PROC:
- /*
- * Just wake up and let
- * Job_CatchChildren() collect the
- * terminated job.
- */
- break;
- }
- }
- }
-#else
- readfds = outputs;
- timeout.tv_sec = SEL_SEC;
- timeout.tv_usec = SEL_USEC;
- if (flag && jobFull && fifoFd >= 0)
- FD_SET(fifoFd, &readfds);
-
- nfds = select(FD_SETSIZE, &readfds, (fd_set *)NULL,
- (fd_set *)NULL, &timeout);
- if (nfds <= 0) {
- if (interrupted)
- JobPassSig(interrupted);
- return;
- }
- if (fifoFd >= 0 && FD_ISSET(fifoFd, &readfds)) {
- if (--nfds <= 0)
- return;
- }
- job = TAILQ_FIRST(&jobs);
- while (nfds != 0 && job != NULL) {
- if (FD_ISSET(job->inPipe, &readfds)) {
- JobDoOutput(job, FALSE);
- nfds--;
- }
- job = TAILQ_NEXT(job, link);
- }
-#endif /* !USE_KQUEUE */
- }
-}
-
-/**
- * Job_Make
- * Start the creation of a target. Basically a front-end for
- * JobStart used by the Make module.
- *
- * Side Effects:
- * Another job is started.
- */
-void
-Job_Make(GNode *gn)
-{
-
- JobStart(gn, 0, NULL);
-}
-
-void
-Job_SetPrefix(void)
-{
-
- if (targPrefix) {
- free(targPrefix);
- } else if (!Var_Exists(MAKE_JOB_PREFIX, VAR_GLOBAL)) {
- Var_SetGlobal(MAKE_JOB_PREFIX, "---");
- }
- targPrefix = Var_Subst("${" MAKE_JOB_PREFIX "}", VAR_GLOBAL, 0)->buf;
-}
-
-/**
- * Job_Init
- * Initialize the process module, given a maximum number of jobs.
- *
- * Side Effects:
- * lists and counters are initialized
- */
-void
-Job_Init(int maxproc)
-{
- GNode *begin; /* node for commands to do at the very start */
- const char *env;
- struct sigaction sa;
-
- fifoFd = -1;
- env = getenv("MAKE_JOBS_FIFO");
-
- if (env == NULL && maxproc > 1) {
- /*
- * We did not find the environment variable so we are the
- * leader. Create the fifo, open it, write one char per
- * allowed job into the pipe.
- */
- fifoFd = mkfifotemp(fifoName);
- if (fifoFd < 0) {
- env = NULL;
- } else {
- fifoMaster = 1;
- fcntl(fifoFd, F_SETFL, O_NONBLOCK);
- env = fifoName;
- setenv("MAKE_JOBS_FIFO", env, 1);
- while (maxproc-- > 0) {
- write(fifoFd, "+", 1);
- }
- /* The master make does not get a magic token */
- jobFull = TRUE;
- maxJobs = 0;
- }
-
- } else if (env != NULL) {
- /*
- * We had the environment variable so we are a slave.
- * Open fifo and give ourselves a magic token which represents
- * the token our parent make has grabbed to start his make
- * process. Otherwise the sub-makes would gobble up tokens and
- * the proper number of tokens to specify to -j would depend
- * on the depth of the tree and the order of execution.
- */
- fifoFd = open(env, O_RDWR, 0);
- if (fifoFd >= 0) {
- fcntl(fifoFd, F_SETFL, O_NONBLOCK);
- maxJobs = 1;
- jobFull = FALSE;
- }
- }
- if (fifoFd < 0) {
- maxJobs = maxproc;
- jobFull = FALSE;
- } else {
- }
- nJobs = 0;
-
- aborting = 0;
- makeErrors = 0;
-
- lastNode = NULL;
- if ((maxJobs == 1 && fifoFd < 0) || !beVerbose || is_posix || beQuiet) {
- /*
- * If only one job can run at a time, there's no need for a
- * banner, no is there?
- */
- targFmt = "";
- } else {
- targFmt = TARG_FMT;
- }
-
- /*
- * Catch the four signals that POSIX specifies if they aren't ignored.
- * JobCatchSignal will just set global variables and hope someone
- * else is going to handle the interrupt.
- */
- sa.sa_handler = JobCatchSig;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
-
- if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
- sigaction(SIGINT, &sa, NULL);
- }
- if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
- sigaction(SIGHUP, &sa, NULL);
- }
- if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) {
- sigaction(SIGQUIT, &sa, NULL);
- }
- if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
- sigaction(SIGTERM, &sa, NULL);
- }
- /*
- * There are additional signals that need to be caught and passed if
- * either the export system wants to be told directly of signals or if
- * we're giving each job its own process group (since then it won't get
- * signals from the terminal driver as we own the terminal)
- */
-#if defined(USE_PGRP)
- if (signal(SIGTSTP, SIG_IGN) != SIG_IGN) {
- sigaction(SIGTSTP, &sa, NULL);
- }
- if (signal(SIGTTOU, SIG_IGN) != SIG_IGN) {
- sigaction(SIGTTOU, &sa, NULL);
- }
- if (signal(SIGTTIN, SIG_IGN) != SIG_IGN) {
- sigaction(SIGTTIN, &sa, NULL);
- }
- if (signal(SIGWINCH, SIG_IGN) != SIG_IGN) {
- sigaction(SIGWINCH, &sa, NULL);
- }
-#endif
-
-#ifdef USE_KQUEUE
- if ((kqfd = kqueue()) == -1) {
- Punt("kqueue: %s", strerror(errno));
- }
-#endif
-
- begin = Targ_FindNode(".BEGIN", TARG_NOCREATE);
-
- if (begin != NULL) {
- JobStart(begin, JOB_SPECIAL, (Job *)NULL);
- while (nJobs) {
- Job_CatchOutput(0);
- Job_CatchChildren(!usePipes);
- }
- }
- postCommands = Targ_FindNode(".END", TARG_CREATE);
-}
-
-/**
- * Job_Full
- * See if the job table is full. It is considered full if it is OR
- * if we are in the process of aborting OR if we have
- * reached/exceeded our local quota. This prevents any more jobs
- * from starting up.
- *
- * Results:
- * TRUE if the job table is full, FALSE otherwise
- */
-Boolean
-Job_Full(void)
-{
- char c;
- int i;
-
- if (aborting)
- return (aborting);
- if (fifoFd >= 0 && jobFull) {
- i = read(fifoFd, &c, 1);
- if (i > 0) {
- maxJobs++;
- jobFull = FALSE;
- }
- }
- return (jobFull);
-}
-
-/**
- * Job_Empty
- * See if the job table is empty. Because the local concurrency may
- * be set to 0, it is possible for the job table to become empty,
- * while the list of stoppedJobs remains non-empty. In such a case,
- * we want to restart as many jobs as we can.
- *
- * Results:
- * TRUE if it is. FALSE if it ain't.
- */
-Boolean
-Job_Empty(void)
-{
- if (nJobs == 0) {
- if (!TAILQ_EMPTY(&stoppedJobs) && !aborting) {
- /*
- * The job table is obviously not full if it has no
- * jobs in it...Try and restart the stopped jobs.
- */
- jobFull = FALSE;
- JobRestartJobs();
- return (FALSE);
- } else {
- return (TRUE);
- }
- } else {
- return (FALSE);
- }
-}
-
-/**
- * JobInterrupt
- * Handle the receipt of an interrupt.
- *
- * Side Effects:
- * All children are killed. Another job will be started if the
- * .INTERRUPT target was given.
- */
-static void
-JobInterrupt(int runINTERRUPT, int signo)
-{
- Job *job; /* job descriptor in that element */
- GNode *interrupt; /* the node describing the .INTERRUPT target */
-
- aborting = ABORT_INTERRUPT;
-
- TAILQ_FOREACH(job, &jobs, link) {
- if (!Targ_Precious(job->node)) {
- char *file = (job->node->path == NULL ?
- job->node->name : job->node->path);
-
- if (!noExecute && eunlink(file) != -1) {
- Error("*** %s removed", file);
- }
- }
- if (job->pid) {
- DEBUGF(JOB, ("JobInterrupt passing signal to child "
- "%jd.\n", (intmax_t)job->pid));
- KILL(job->pid, signo);
- }
- }
-
- if (runINTERRUPT && !touchFlag) {
- /*
- * clear the interrupted flag because we would get an
- * infinite loop otherwise.
- */
- interrupted = 0;
-
- interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
- if (interrupt != NULL) {
- ignoreErrors = FALSE;
-
- JobStart(interrupt, JOB_IGNDOTS, (Job *)NULL);
- while (nJobs) {
- Job_CatchOutput(0);
- Job_CatchChildren(!usePipes);
- }
- }
- }
- if (fifoMaster)
- unlink(fifoName);
-}
-
-/**
- * Job_Finish
- * Do final processing such as the running of the commands
- * attached to the .END target.
- *
- * Results:
- * None.
- */
-void
-Job_Finish(void)
-{
-
- if (postCommands != NULL && !Lst_IsEmpty(&postCommands->commands)) {
- if (makeErrors) {
- Error("Errors reported so .END ignored");
- } else {
- JobStart(postCommands, JOB_SPECIAL | JOB_IGNDOTS, NULL);
-
- while (nJobs) {
- Job_CatchOutput(0);
- Job_CatchChildren(!usePipes);
- }
- }
- }
- if (fifoFd >= 0) {
- close(fifoFd);
- fifoFd = -1;
- if (fifoMaster)
- unlink(fifoName);
- }
-}
-
-/**
- * Job_Wait
- * Waits for all running jobs to finish and returns. Sets 'aborting'
- * to ABORT_WAIT to prevent other jobs from starting.
- *
- * Side Effects:
- * Currently running jobs finish.
- */
-void
-Job_Wait(void)
-{
-
- aborting = ABORT_WAIT;
- while (nJobs != 0) {
- Job_CatchOutput(0);
- Job_CatchChildren(!usePipes);
- }
- aborting = 0;
-}
-
-/**
- * Job_AbortAll
- * Abort all currently running jobs without handling output or anything.
- * This function is to be called only in the event of a major
- * error. Most definitely NOT to be called from JobInterrupt.
- *
- * Side Effects:
- * All children are killed, not just the firstborn
- */
-void
-Job_AbortAll(void)
-{
- Job *job; /* the job descriptor in that element */
- int foo;
-
- aborting = ABORT_ERROR;
-
- if (nJobs) {
- TAILQ_FOREACH(job, &jobs, link) {
- /*
- * kill the child process with increasingly drastic
- * signals to make darn sure it's dead.
- */
- KILL(job->pid, SIGINT);
- KILL(job->pid, SIGKILL);
- }
- }
-
- /*
- * Catch as many children as want to report in at first, then give up
- */
- while (waitpid(-1, &foo, WNOHANG) > 0)
- ;
-}
-
-/**
- * JobRestartJobs
- * Tries to restart stopped jobs if there are slots available.
- * Note that this tries to restart them regardless of pending errors.
- * It's not good to leave stopped jobs lying around!
- *
- * Side Effects:
- * Resumes(and possibly migrates) jobs.
- */
-static void
-JobRestartJobs(void)
-{
- Job *job;
-
- while (!jobFull && (job = TAILQ_FIRST(&stoppedJobs)) != NULL) {
- DEBUGF(JOB, ("Job queue is not full. "
- "Restarting a stopped job.\n"));
- TAILQ_REMOVE(&stoppedJobs, job, link);
- JobRestart(job);
- }
-}
-
-/**
- * Cmd_Exec
- * Execute the command in cmd, and return the output of that command
- * in a string.
- *
- * Results:
- * A string containing the output of the command, or the empty string
- * If error is not NULL, it contains the reason for the command failure
- * Any output sent to stderr in the child process is passed to stderr,
- * and not captured in the string.
- *
- * Side Effects:
- * The string must be freed by the caller.
- */
-Buffer *
-Cmd_Exec(const char *cmd, const char **error)
-{
- int fds[2]; /* Pipe streams */
- int status; /* command exit status */
- Buffer *buf; /* buffer to store the result */
- ssize_t rcnt;
- ProcStuff ps;
-
- *error = NULL;
- buf = Buf_Init(0);
-
- /*
- * Open a pipe for fetching its output
- */
- if (pipe(fds) == -1) {
- *error = "Couldn't create pipe for \"%s\"";
- return (buf);
- }
-
- /* Set close-on-exec on read side of pipe. */
- fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC);
-
- ps.in = STDIN_FILENO;
- ps.out = fds[1];
- ps.err = STDERR_FILENO;
-
- ps.merge_errors = 0;
- ps.pgroup = 0;
- ps.searchpath = 0;
-
- /* Set up arguments for shell */
- ps.argv = emalloc(4 * sizeof(char *));
- ps.argv[0] = strdup(commandShell->name);
- ps.argv[1] = strdup("-c");
- ps.argv[2] = strdup(cmd);
- ps.argv[3] = NULL;
- ps.argv_free = 1;
-
- /*
- * Fork. Warning since we are doing vfork() instead of fork(),
- * do not allocate memory in the child process!
- */
- if ((ps.child_pid = vfork()) == -1) {
- *error = "Couldn't exec \"%s\"";
- return (buf);
-
- } else if (ps.child_pid == 0) {
- /*
- * Child
- */
- Proc_Exec(&ps);
- /* NOTREACHED */
- }
-
- free(ps.argv[2]);
- free(ps.argv[1]);
- free(ps.argv[0]);
- free(ps.argv);
-
- close(fds[1]); /* No need for the writing half of the pipe. */
-
- do {
- char result[BUFSIZ];
-
- rcnt = read(fds[0], result, sizeof(result));
- if (rcnt != -1)
- Buf_AddBytes(buf, (size_t)rcnt, (Byte *)result);
- } while (rcnt > 0 || (rcnt == -1 && errno == EINTR));
-
- if (rcnt == -1)
- *error = "Error reading shell's output for \"%s\"";
-
- /*
- * Close the input side of the pipe.
- */
- close(fds[0]);
-
- status = ProcWait(&ps);
-
- if (status)
- *error = "\"%s\" returned non-zero status";
-
- Buf_StripNewlines(buf);
-
- return (buf);
-}
-
-
-/*
- * Interrupt handler - set flag and defer handling to the main code
- */
-static void
-CompatCatchSig(int signo)
-{
-
- interrupted = signo;
-}
-
-/*-
- *-----------------------------------------------------------------------
- * CompatInterrupt --
- * Interrupt the creation of the current target and remove it if
- * it ain't precious.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The target is removed and the process exits. If .INTERRUPT exists,
- * its commands are run first WITH INTERRUPTS IGNORED..
- *
- *-----------------------------------------------------------------------
- */
-static void
-CompatInterrupt(int signo)
-{
- GNode *gn;
- sigset_t nmask, omask;
- LstNode *ln;
-
- sigemptyset(&nmask);
- sigaddset(&nmask, SIGINT);
- sigaddset(&nmask, SIGTERM);
- sigaddset(&nmask, SIGHUP);
- sigaddset(&nmask, SIGQUIT);
- sigprocmask(SIG_SETMASK, &nmask, &omask);
-
- /* prevent recursion in evaluation of .INTERRUPT */
- interrupted = 0;
-
- if (curTarg != NULL && !Targ_Precious(curTarg)) {
- const char *file = Var_Value(TARGET, curTarg);
-
- if (!noExecute && eunlink(file) != -1) {
- printf("*** %s removed\n", file);
- }
- }
-
- /*
- * Run .INTERRUPT only if hit with interrupt signal
- */
- if (signo == SIGINT) {
- gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
- if (gn != NULL) {
- LST_FOREACH(ln, &gn->commands) {
- if (Compat_RunCommand(ln, gn))
- break;
- }
- }
- }
-
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- if (signo == SIGQUIT)
- exit(signo);
- signal(signo, SIG_DFL);
- kill(getpid(), signo);
-}
-
-/**
- * shellneed
- *
- * Results:
- * Returns NULL if a specified line must be executed by the shell,
- * and an argument vector if it can be run via execvp().
- *
- * Side Effects:
- * Uses brk_string so destroys the contents of argv.
- */
-static char **
-shellneed(ArgArray *aa, char *cmd)
-{
- char **p;
- int ret;
-
- if (commandShell->meta == NULL || commandShell->builtins.argc <= 1)
- /* use shell */
- return (NULL);
-
- if (strpbrk(cmd, commandShell->meta) != NULL)
- return (NULL);
-
- /*
- * Break the command into words to form an argument
- * vector we can execute.
- */
- brk_string(aa, cmd, TRUE);
- for (p = commandShell->builtins.argv + 1; *p != 0; p++) {
- if ((ret = strcmp(aa->argv[1], *p)) == 0) {
- /* found - use shell */
- ArgArray_Done(aa);
- return (NULL);
- }
- if (ret < 0) {
- /* not found */
- break;
- }
- }
- return (aa->argv + 1);
-}
-
-/**
- * Execute the next command for a target. If the command returns an
- * error, the node's made field is set to ERROR and creation stops.
- * The node from which the command came is also given. This is used
- * to execute the commands in compat mode and when executing commands
- * with the '+' flag in non-compat mode. In these modes each command
- * line should be executed by its own shell. We do some optimisation here:
- * if the shell description defines both a string of meta characters and
- * a list of builtins and the command line neither contains a meta character
- * nor starts with one of the builtins then we execute the command directly
- * without invoking a shell.
- *
- * Results:
- * 0 if the command succeeded, 1 if an error occurred.
- *
- * Side Effects:
- * The node's 'made' field may be set to ERROR.
- */
-static int
-Compat_RunCommand(LstNode *cmdNode, GNode *gn)
-{
- ArgArray aa;
- char *cmd; /* Expanded command */
- Boolean silent; /* Don't print command */
- Boolean doit; /* Execute even in -n */
- Boolean errCheck; /* Check errors */
- int reason; /* Reason for child's death */
- int status; /* Description of child's death */
- char **av; /* Argument vector for thing to exec */
- ProcStuff ps;
-
- silent = gn->type & OP_SILENT;
- errCheck = !(gn->type & OP_IGNORE);
- doit = FALSE;
-
- cmd = Buf_Peel(Var_Subst(Lst_Datum(cmdNode), gn, FALSE));
- if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) {
- Lst_AtEnd(&ENDNode->commands, cmd);
- return (0);
- } else if (strcmp(cmd, "...") == 0) {
- free(cmd);
- gn->type |= OP_SAVE_CMDS;
- return (0);
- }
- Lst_Replace(cmdNode, cmd);
-
- while (*cmd == '@' || *cmd == '-' || *cmd == '+') {
- switch (*cmd) {
-
- case '@':
- silent = DEBUG(LOUD) ? FALSE : TRUE;
- break;
-
- case '-':
- errCheck = FALSE;
- break;
-
- case '+':
- doit = TRUE;
- break;
- }
- cmd++;
- }
-
- while (isspace((unsigned char)*cmd))
- cmd++;
-
- /*
- * Ignore empty commands
- */
- if (*cmd == '\0') {
- return (0);
- }
-
- /*
- * Print the command before echoing if we're not supposed to be quiet
- * for this one. We also print the command if -n given, but not if '+'.
- */
- if (!silent || (noExecute && !doit)) {
- printf("%s\n", cmd);
- fflush(stdout);
- }
-
- /*
- * If we're not supposed to execute any commands, this is as far as
- * we go...
- */
- if (!doit && noExecute) {
- return (0);
- }
-
- ps.in = STDIN_FILENO;
- ps.out = STDOUT_FILENO;
- ps.err = STDERR_FILENO;
-
- ps.merge_errors = 0;
- ps.pgroup = 0;
- ps.searchpath = 1;
-
- if ((av = shellneed(&aa, cmd)) == NULL) {
- /*
- * Shell meta character or shell builtin found - pass
- * command to shell. We give the shell the -e flag as
- * well as -c if it is supposed to exit when it hits an error.
- */
- ps.argv = emalloc(4 * sizeof(char *));
- ps.argv[0] = strdup(commandShell->path);
- ps.argv[1] = strdup(errCheck ? "-ec" : "-c");
- ps.argv[2] = strdup(cmd);
- ps.argv[3] = NULL;
- ps.argv_free = 1;
- } else {
- ps.argv = av;
- ps.argv_free = 0;
- }
- ps.errCheck = errCheck;
-
- /*
- * Warning since we are doing vfork() instead of fork(),
- * do not allocate memory in the child process!
- */
- if ((ps.child_pid = vfork()) == -1) {
- Fatal("Could not fork");
-
- } else if (ps.child_pid == 0) {
- /*
- * Child
- */
- Proc_Exec(&ps);
- /* NOTREACHED */
-
- } else {
- if (ps.argv_free) {
- free(ps.argv[2]);
- free(ps.argv[1]);
- free(ps.argv[0]);
- free(ps.argv);
- } else {
- ArgArray_Done(&aa);
- }
-
- /*
- * we need to print out the command associated with this
- * Gnode in Targ_PrintCmd from Targ_PrintGraph when debugging
- * at level g2, in main(), Fatal() and DieHorribly(),
- * therefore do not free it when debugging.
- */
- if (!DEBUG(GRAPH2)) {
- free(Lst_Datum(cmdNode));
- Lst_Replace(cmdNode, NULL);
- }
-
- /*
- * The child is off and running. Now all we can do is wait...
- */
- reason = ProcWait(&ps);
-
- if (interrupted)
- CompatInterrupt(interrupted);
-
- /*
- * Decode and report the reason child exited, then
- * indicate how we handled it.
- */
- if (WIFEXITED(reason)) {
- status = WEXITSTATUS(reason);
- if (status == 0) {
- return (0);
- } else {
- printf("*** [%s] Error code %d",
- gn->name, status);
- }
- } else if (WIFSTOPPED(reason)) {
- status = WSTOPSIG(reason);
- } else {
- status = WTERMSIG(reason);
- printf("*** [%s] Signal %d",
- gn->name, status);
- }
-
- if (ps.errCheck) {
- gn->made = ERROR;
- if (keepgoing) {
- /*
- * Abort the current
- * target, but let
- * others continue.
- */
- printf(" (continuing)\n");
- }
- return (status);
- } else {
- /*
- * Continue executing
- * commands for this target.
- * If we return 0, this will
- * happen...
- */
- printf(" (ignored)\n");
- return (0);
- }
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Compat_Make --
- * Make a target, given the parent, to abort if necessary.
- *
- * Side Effects:
- * If an error is detected and not being ignored, the process exits.
- *
- *-----------------------------------------------------------------------
- */
-int
-Compat_Make(GNode *gn, GNode *pgn)
-{
- LstNode *ln;
-
- if (gn->type & OP_USE) {
- Make_HandleUse(gn, pgn);
-
- } else if (gn->made == UNMADE) {
- /*
- * First mark ourselves to be made, then apply whatever
- * transformations the suffix module thinks are necessary.
- * Once that's done, we can descend and make all our children.
- * If any of them has an error but the -k flag was given, our
- * 'make' field will be set FALSE again. This is our signal to
- * not attempt to do anything but abort our parent as well.
- */
- gn->make = TRUE;
- gn->made = BEINGMADE;
- Suff_FindDeps(gn);
- LST_FOREACH(ln, &gn->children)
- Compat_Make(Lst_Datum(ln), gn);
- if (!gn->make) {
- gn->made = ABORTED;
- pgn->make = FALSE;
- return (0);
- }
-
- if (Lst_Member(&gn->iParents, pgn) != NULL) {
- Var_Set(IMPSRC, Var_Value(TARGET, gn), pgn);
- }
-
- /*
- * All the children were made ok. Now cmtime contains the
- * modification time of the newest child, we need to find out
- * if we exist and when we were modified last. The criteria for
- * datedness are defined by the Make_OODate function.
- */
- DEBUGF(MAKE, ("Examining %s...", gn->name));
- if (!Make_OODate(gn)) {
- gn->made = UPTODATE;
- DEBUGF(MAKE, ("up-to-date.\n"));
- return (0);
- } else {
- DEBUGF(MAKE, ("out-of-date.\n"));
- }
-
- /*
- * If the user is just seeing if something is out-of-date,
- * exit now to tell him/her "yes".
- */
- if (queryFlag) {
- exit(1);
- }
-
- /*
- * We need to be re-made. We also have to make sure we've got
- * a $? variable. To be nice, we also define the $> variable
- * using Make_DoAllVar().
- */
- Make_DoAllVar(gn);
-
- /*
- * Alter our type to tell if errors should be ignored or things
- * should not be printed so Compat_RunCommand knows what to do.
- */
- if (Targ_Ignore(gn)) {
- gn->type |= OP_IGNORE;
- }
- if (Targ_Silent(gn)) {
- gn->type |= OP_SILENT;
- }
-
- if (Job_CheckCommands(gn, Fatal)) {
- /*
- * Our commands are ok, but we still have to worry
- * about the -t flag...
- */
- if (!touchFlag) {
- curTarg = gn;
- LST_FOREACH(ln, &gn->commands) {
- if (Compat_RunCommand(ln, gn))
- break;
- }
- curTarg = NULL;
- } else {
- Job_Touch(gn, gn->type & OP_SILENT);
- }
- } else {
- gn->made = ERROR;
- }
-
- if (gn->made != ERROR) {
- /*
- * If the node was made successfully, mark it so, update
- * its modification time and timestamp all its parents.
- * Note that for .ZEROTIME targets, the timestamping
- * isn't done. This is to keep its state from affecting
- * that of its parent.
- */
- gn->made = MADE;
-#ifndef RECHECK
- /*
- * We can't re-stat the thing, but we can at least take
- * care of rules where a target depends on a source that
- * actually creates the target, but only if it has
- * changed, e.g.
- *
- * parse.h : parse.o
- *
- * parse.o : parse.y
- * yacc -d parse.y
- * cc -c y.tab.c
- * mv y.tab.o parse.o
- * cmp -s y.tab.h parse.h || mv y.tab.h parse.h
- *
- * In this case, if the definitions produced by yacc
- * haven't changed from before, parse.h won't have been
- * updated and gn->mtime will reflect the current
- * modification time for parse.h. This is something of a
- * kludge, I admit, but it's a useful one..
- *
- * XXX: People like to use a rule like
- *
- * FRC:
- *
- * To force things that depend on FRC to be made, so we
- * have to check for gn->children being empty as well...
- */
- if (!Lst_IsEmpty(&gn->commands) ||
- Lst_IsEmpty(&gn->children)) {
- gn->mtime = now;
- }
-#else
- /*
- * This is what Make does and it's actually a good
- * thing, as it allows rules like
- *
- * cmp -s y.tab.h parse.h || cp y.tab.h parse.h
- *
- * to function as intended. Unfortunately, thanks to
- * the stateless nature of NFS (and the speed of this
- * program), there are times when the modification time
- * of a file created on a remote machine will not be
- * modified before the stat() implied by the Dir_MTime
- * occurs, thus leading us to believe that the file
- * is unchanged, wreaking havoc with files that depend
- * on this one.
- *
- * I have decided it is better to make too much than to
- * make too little, so this stuff is commented out
- * unless you're sure it's ok.
- * -- ardeb 1/12/88
- */
- if (noExecute || Dir_MTime(gn) == 0) {
- gn->mtime = now;
- }
- if (gn->cmtime > gn->mtime)
- gn->mtime = gn->cmtime;
- DEBUGF(MAKE, ("update time: %s\n",
- Targ_FmtTime(gn->mtime)));
-#endif
- if (!(gn->type & OP_EXEC)) {
- pgn->childMade = TRUE;
- Make_TimeStamp(pgn, gn);
- }
-
- } else if (keepgoing) {
- pgn->make = FALSE;
-
- } else {
- printf("\n\nStop in %s.\n", Var_Value(".CURDIR", gn));
- exit(1);
- }
- } else if (gn->made == ERROR) {
- /*
- * Already had an error when making this beastie. Tell the
- * parent to abort.
- */
- pgn->make = FALSE;
- } else {
- if (Lst_Member(&gn->iParents, pgn) != NULL) {
- Var_Set(IMPSRC, Var_Value(TARGET, gn), pgn);
- }
- switch(gn->made) {
- case BEINGMADE:
- Error("Graph cycles through %s\n", gn->name);
- gn->made = ERROR;
- pgn->make = FALSE;
- break;
- case MADE:
- if ((gn->type & OP_EXEC) == 0) {
- pgn->childMade = TRUE;
- Make_TimeStamp(pgn, gn);
- }
- break;
- case UPTODATE:
- if ((gn->type & OP_EXEC) == 0) {
- Make_TimeStamp(pgn, gn);
- }
- break;
- default:
- break;
- }
- }
-
- return (0);
-}
-
-/*-
- * Install signal handlers for Compat_Run
- */
-void
-Compat_InstallSignalHandlers(void)
-{
-
- if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
- signal(SIGINT, CompatCatchSig);
- }
- if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
- signal(SIGTERM, CompatCatchSig);
- }
- if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
- signal(SIGHUP, CompatCatchSig);
- }
- if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) {
- signal(SIGQUIT, CompatCatchSig);
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Compat_Run --
- * Start making again, given a list of target nodes.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Guess what?
- *
- *-----------------------------------------------------------------------
- */
-void
-Compat_Run(Lst *targs)
-{
- GNode *gn = NULL; /* Current root target */
- LstNode *ln;
-
- Compat_InstallSignalHandlers();
- ENDNode = Targ_FindNode(".END", TARG_CREATE);
- /*
- * If the user has defined a .BEGIN target, execute the commands
- * attached to it.
- */
- if (!queryFlag) {
- gn = Targ_FindNode(".BEGIN", TARG_NOCREATE);
- if (gn != NULL) {
- LST_FOREACH(ln, &gn->commands) {
- if (Compat_RunCommand(ln, gn))
- break;
- }
- if (gn->made == ERROR) {
- printf("\n\nStop.\n");
- exit(1);
- }
- }
- }
-
- /*
- * For each entry in the list of targets to create, call Compat_Make on
- * it to create the thing. Compat_Make will leave the 'made' field of gn
- * in one of several states:
- * UPTODATE gn was already up-to-date
- * MADE gn was recreated successfully
- * ERROR An error occurred while gn was being created
- * ABORTED gn was not remade because one of its inferiors
- * could not be made due to errors.
- */
- makeErrors = 0;
- while (!Lst_IsEmpty(targs)) {
- gn = Lst_DeQueue(targs);
- Compat_Make(gn, gn);
-
- if (gn->made == UPTODATE) {
- printf("`%s' is up to date.\n", gn->name);
- } else if (gn->made == ABORTED) {
- printf("`%s' not remade because of errors.\n",
- gn->name);
- makeErrors++;
- } else if (gn->made == ERROR) {
- makeErrors++;
- }
- }
-
- /*
- * If the user has defined a .END target, run its commands.
- */
- if (makeErrors == 0) {
- LST_FOREACH(ln, &ENDNode->commands) {
- if (Compat_RunCommand(ln, ENDNode))
- break;
- }
- }
-}
diff --git a/usr.bin/make/job.h b/usr.bin/make/job.h
deleted file mode 100644
index 31e1cb7..0000000
--- a/usr.bin/make/job.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*-
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1988, 1989 by Adam de Boor
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)job.h 8.1 (Berkeley) 6/6/93
- * $FreeBSD$
- */
-
-#ifndef job_h_4678dfd1
-#define job_h_4678dfd1
-
-/*-
- * job.h --
- * Definitions pertaining to the running of jobs in parallel mode.
- */
-
-#include <stdio.h>
-
-#include "util.h"
-
-struct Buffer;
-struct GNode;
-struct Lst;
-
-void Job_Touch(struct GNode *, Boolean);
-Boolean Job_CheckCommands(struct GNode *, void (*abortProc)(const char *, ...));
-void Job_CatchChildren(Boolean);
-void Job_CatchOutput(int flag);
-void Job_Make(struct GNode *);
-void Job_Init(int);
-Boolean Job_Full(void);
-Boolean Job_Empty(void);
-void Job_Finish(void);
-void Job_Wait(void);
-void Job_AbortAll(void);
-void Job_SetPrefix(void);
-
-void Proc_Init(void);
-
-struct Buffer *Cmd_Exec(const char *, const char **);
-
-int Compat_Make(struct GNode *gn, struct GNode *pgn);
-void Compat_InstallSignalHandlers(void);
-void Compat_Run(struct Lst *);
-
-#endif /* job_h_4678dfd1 */
diff --git a/usr.bin/make/lst.c b/usr.bin/make/lst.c
deleted file mode 100644
index d57c34b..0000000
--- a/usr.bin/make/lst.c
+++ /dev/null
@@ -1,344 +0,0 @@
-/*-
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. 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.
- *
- * $FreeBSD$
- */
-
-/*-
- * lst.c --
- * Routines to maintain a linked list of objects.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "lst.h"
-#include "util.h"
-
-/**
- * Lst_Append
- * Create a new node and add it to the given list after the given node.
- *
- * Arguments:
- * l affected list
- * ln node after which to append the datum
- * d said datum
- *
- * Side Effects:
- * A new LstNode is created and linked in to the List. The lastPtr
- * field of the List will be altered if ln is the last node in the
- * list. lastPtr and firstPtr will alter if the list was empty and
- * ln was NULL.
- */
-void
-Lst_Append(Lst *list, LstNode *ln, void *d)
-{
- LstNode *nLNode;
-
- nLNode = emalloc(sizeof(*nLNode));
- nLNode->datum = d;
-
- if (ln == NULL) {
- nLNode->nextPtr = nLNode->prevPtr = NULL;
- list->firstPtr = list->lastPtr = nLNode;
- } else {
- nLNode->prevPtr = ln;
- nLNode->nextPtr = ln->nextPtr;
-
- ln->nextPtr = nLNode;
- if (nLNode->nextPtr != NULL) {
- nLNode->nextPtr->prevPtr = nLNode;
- }
-
- if (ln == list->lastPtr) {
- list->lastPtr = nLNode;
- }
- }
-}
-
-/**
- * Lst_Concat
- * Concatenate two lists. New elements are created to hold the data
- * elements, if specified, but the elements themselves are not copied.
- * If the elements should be duplicated to avoid confusion with another
- * list, the Lst_Duplicate function should be called first.
- *
- * Arguments:
- * list1 The list to which list2 is to be appended
- * list2 The list to append to list1
- * flags LST_CONCNEW if LstNode's should be duplicated
- * LST_CONCLINK if should just be relinked
- *
- * Side Effects:
- * New elements are created and appended the first list.
- */
-void
-Lst_Concat(Lst *list1, Lst *list2, int flags)
-{
- LstNode *ln; /* original LstNode */
- LstNode *nln; /* new LstNode */
- LstNode *last; /* the last element in the list. Keeps
- * bookkeeping until the end */
-
- if (list2->firstPtr == NULL)
- return;
-
- if (flags == LST_CONCLINK) {
- /*
- * Link the first element of the second list to the last
- * element of the first list. If the first list isn't empty,
- * we then link the last element of the list to the first
- * element of the second list. The last element of the second
- * list, if it exists, then becomes the last element of the
- * first list.
- */
- list2->firstPtr->prevPtr = list1->lastPtr;
- if (list1->lastPtr != NULL)
- list1->lastPtr->nextPtr = list2->firstPtr;
- else
- list1->firstPtr = list2->firstPtr;
- list1->lastPtr = list2->lastPtr;
-
- Lst_Init(list2);
- } else {
- /*
- * The loop simply goes through the entire second list creating
- * new LstNodes and filling in the nextPtr, and prevPtr to fit
- * into list1 and its datum field from the datum field of the
- * corresponding element in list2. The 'last' node follows the
- * last of the new nodes along until the entire list2 has been
- * appended. Only then does the bookkeeping catch up with the
- * changes. During the first iteration of the loop, if 'last'
- * is NULL, the first list must have been empty so the
- * newly-created node is made the first node of the list.
- */
- for (last = list1->lastPtr, ln = list2->firstPtr;
- ln != NULL;
- ln = ln->nextPtr) {
- nln = emalloc(sizeof(*nln));
- nln->datum = ln->datum;
- if (last != NULL) {
- last->nextPtr = nln;
- } else {
- list1->firstPtr = nln;
- }
- nln->prevPtr = last;
- last = nln;
- }
-
- /*
- * Finish bookkeeping. The last new element becomes the last
- * element of list one.
- */
- list1->lastPtr = last;
- last->nextPtr = NULL;
- }
-}
-
-/**
- * Lst_DeQueue
- * Remove and return the datum at the head of the given list.
- *
- * Results:
- * The datum in the node at the head or (ick) NULL if the list
- * is empty.
- *
- * Side Effects:
- * The head node is removed from the list.
- */
-void *
-Lst_DeQueue(Lst *l)
-{
- void *rd;
- LstNode *tln;
-
- tln = Lst_First(l);
- if (tln == NULL) {
- return (NULL);
- }
-
- rd = tln->datum;
- Lst_Remove(l, tln);
- return (rd);
-}
-
-/**
- * Lst_Destroy
- * Destroy a list and free all its resources. If the freeProc is
- * given, it is called with the datum from each node in turn before
- * the node is freed.
- *
- * Side Effects:
- * The given list is freed in its entirety.
- */
-void
-Lst_Destroy(Lst *list, FreeProc *freeProc)
-{
- LstNode *ln;
-
- if (list->firstPtr == NULL)
- return;
-
- if (freeProc != NOFREE) {
- while ((ln = list->firstPtr) != NULL) {
- list->firstPtr = ln->nextPtr;
- (*freeProc)(ln->datum);
- free(ln);
- }
- } else {
- while ((ln = list->firstPtr) != NULL) {
- list->firstPtr = ln->nextPtr;
- free(ln);
- }
- }
- list->lastPtr = NULL;
-}
-
-/**
- * Lst_Duplicate
- * Duplicate an entire list. If a function to copy a void * is
- * given, the individual client elements will be duplicated as well.
- *
- * Arguments:
- * dst the destination list (initialized)
- * src the list to duplicate
- * copyProc A function to duplicate each void
- */
-void
-Lst_Duplicate(Lst *dst, Lst *src, DuplicateProc *copyProc)
-{
- LstNode *ln;
-
- ln = src->firstPtr;
- while (ln != NULL) {
- if (copyProc != NOCOPY)
- Lst_AtEnd(dst, (*copyProc)(ln->datum));
- else
- Lst_AtEnd(dst, ln->datum);
- ln = ln->nextPtr;
- }
-}
-
-/**
- * Lst_Insert
- * Insert a new node with the given piece of data before the given
- * node in the given list.
- *
- * Parameters:
- * l list to manipulate
- * ln node before which to insert d
- * d datum to be inserted
- *
- * Side Effects:
- * the firstPtr field will be changed if ln is the first node in the
- * list.
- */
-void
-Lst_Insert(Lst *list, LstNode *ln, void *d)
-{
- LstNode *nLNode; /* new lnode for d */
-
- nLNode = emalloc(sizeof(*nLNode));
- nLNode->datum = d;
-
- if (ln == NULL) {
- nLNode->prevPtr = nLNode->nextPtr = NULL;
- list->firstPtr = list->lastPtr = nLNode;
- } else {
- nLNode->prevPtr = ln->prevPtr;
- nLNode->nextPtr = ln;
-
- if (nLNode->prevPtr != NULL) {
- nLNode->prevPtr->nextPtr = nLNode;
- }
- ln->prevPtr = nLNode;
-
- if (ln == list->firstPtr) {
- list->firstPtr = nLNode;
- }
- }
-}
-
-LstNode *
-Lst_Member(Lst *list, void *d)
-{
- LstNode *lNode;
-
- lNode = list->firstPtr;
- if (lNode == NULL) {
- return (NULL);
- }
-
- do {
- if (lNode->datum == d) {
- return (lNode);
- }
- lNode = lNode->nextPtr;
- } while (lNode != NULL && lNode != list->firstPtr);
-
- return (NULL);
-}
-
-/**
- * Lst_Remove
- * Remove the given node from the given list.
- *
- * Side Effects:
- * The list's firstPtr will be set to NULL if ln is the last
- * node on the list. firsPtr and lastPtr will be altered if ln is
- * either the first or last node, respectively, on the list.
- */
-void
-Lst_Remove(Lst *list, LstNode *ln)
-{
- /*
- * unlink it from the list
- */
- if (ln->nextPtr != NULL)
- /* unlink from the backward chain */
- ln->nextPtr->prevPtr = ln->prevPtr;
- else
- /* this was the last element */
- list->lastPtr = ln->prevPtr;
-
- if (ln->prevPtr != NULL)
- /* unlink from the forward chain */
- ln->prevPtr->nextPtr = ln->nextPtr;
- else
- /* this was the first element */
- list->firstPtr = ln->nextPtr;
-
- /*
- * note that the datum is unmolested. The caller must free it as
- * necessary and as expected.
- */
- free(ln);
-}
diff --git a/usr.bin/make/lst.h b/usr.bin/make/lst.h
deleted file mode 100644
index 1199e94..0000000
--- a/usr.bin/make/lst.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/*-
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1988, 1989 by Adam de Boor
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)lst.h 8.2 (Berkeley) 4/28/95
- * $FreeBSD$
- */
-
-#ifndef lst_h_38f3ead1
-#define lst_h_38f3ead1
-
-/*-
- * lst.h --
- * Header for using the list library
- */
-
-/*
- * Structure of a list node.
- */
-struct LstNode {
- struct LstNode *prevPtr; /* previous element in list */
- struct LstNode *nextPtr; /* next in list */
- void *datum; /* datum associated with this element */
-};
-typedef struct LstNode LstNode;
-
-/*
- * The list itself
- */
-struct Lst {
- LstNode *firstPtr; /* first node in list */
- LstNode *lastPtr; /* last node in list */
-};
-typedef struct Lst Lst;
-
-typedef void *DuplicateProc(void *);
-typedef void FreeProc(void *);
-
-/*
- * NOFREE can be used as the freeProc to Lst_Destroy when the elements are
- * not to be freed.
- * NOCOPY performs similarly when given as the copyProc to Lst_Duplicate.
- */
-#define NOFREE ((FreeProc *)NULL)
-#define NOCOPY ((DuplicateProc *)NULL)
-
-#define LST_CONCNEW 0 /* create new LstNode's when using Lst_Concat */
-#define LST_CONCLINK 1 /* relink LstNode's when using Lst_Concat */
-
-/*
- * Creation/destruction functions
- */
-/* Create a new list */
-#define Lst_Init(LST) do { \
- (LST)->firstPtr = NULL; \
- (LST)->lastPtr = NULL; \
- } while (0)
-#define Lst_Initializer(NAME) { NULL, NULL }
-
-/* Duplicate an existing list */
-void Lst_Duplicate(Lst *, Lst *, DuplicateProc *);
-
-/* Destroy an old one */
-void Lst_Destroy(Lst *, FreeProc *);
-
-/*
- * Functions to modify a list
- */
-/* Insert an element before another */
-void Lst_Insert(Lst *, LstNode *, void *);
-/* Insert an element after another */
-void Lst_Append(Lst *, LstNode *, void *);
-/* Place an element at the front of a lst. */
-#define Lst_AtFront(LST, D) (Lst_Insert((LST), Lst_First(LST), (D)))
-/* Place an element at the end of a lst. */
-#define Lst_AtEnd(LST, D) (Lst_Append((LST), Lst_Last(LST), (D)))
-/* Remove an element */
-void Lst_Remove(Lst *, LstNode *);
-/* Replace a node with a new value */
-#define Lst_Replace(NODE, D) ((void)((NODE)->datum = (D)))
-/* Concatenate two lists */
-void Lst_Concat(Lst *, Lst *, int);
-
-/*
- * Node-specific functions
- */
-/* Return first element in list */
-#define Lst_First(LST) ((Lst_Valid(LST) && !Lst_IsEmpty(LST)) \
- ? (LST)->firstPtr : NULL)
-/* Return last element in list */
-#define Lst_Last(LST) ((Lst_Valid(LST) && !Lst_IsEmpty(LST)) \
- ? (LST)->lastPtr : NULL)
-/* Return successor to given element */
-#define Lst_Succ(NODE) (((NODE) == NULL) ? NULL : (NODE)->nextPtr)
-#define LST_NEXT(NODE) ((NODE)->nextPtr)
-/* Get datum from LstNode */
-#define Lst_Datum(NODE) ((NODE)->datum)
-
-/*
- * Functions for entire lists
- */
-
-/*
- * See if the given datum is on the list. Returns the LstNode containing
- * the datum
- */
-LstNode *Lst_Member(Lst *, void *);
-
-/* Loop through a list. Note, that you may not delete the list element. */
-#define LST_FOREACH(PTR, LST) \
- for ((PTR) = (LST)->firstPtr; (PTR) != NULL; (PTR) = (PTR)->nextPtr)
-
-/*
- * for using the list as a queue
- */
-/* Place an element at tail of queue */
-#define Lst_EnQueue(LST, D) (Lst_Valid(LST) \
- ? Lst_Append((LST), Lst_Last(LST), (D)) \
- : (void)0)
-/* Remove an element from head of queue */
-void *Lst_DeQueue(Lst *);
-
-/*
- * LstValid (L) --
- * Return TRUE if the list L is valid
- */
-#define Lst_Valid(L) (((L) == NULL) ? FALSE : TRUE)
-
-/*
- * LstNodeValid (LN, L) --
- * Return TRUE if the LstNode LN is valid with respect to L
- */
-#define Lst_NodeValid(LN, L) (((LN) == NULL) ? FALSE : TRUE)
-
-/*
- * Lst_IsEmpty(L) --
- * TRUE if the list L is empty.
- */
-#define Lst_IsEmpty(L) (!Lst_Valid(L) || (L)->firstPtr == NULL)
-
-#endif /* lst_h_38f3ead1 */
diff --git a/usr.bin/make/main.c b/usr.bin/make/main.c
deleted file mode 100644
index f019d52..0000000
--- a/usr.bin/make/main.c
+++ /dev/null
@@ -1,1339 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)main.c 8.3 (Berkeley) 3/19/94
- */
-
-#ifndef lint
-#if 0
-static char copyright[] =
-"@(#) Copyright (c) 1988, 1989, 1990, 1993\n\
- The Regents of the University of California. All rights reserved.\n";
-#endif
-#endif /* not lint */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*
- * main.c
- * The main file for this entire program. Exit routines etc
- * reside here.
- *
- * Utility functions defined in this file:
- * Main_ParseArgLine
- * Takes a line of arguments, breaks them and
- * treats them as if they were given when first
- * invoked. Used by the parse module to implement
- * the .MFLAGS target.
- */
-
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/queue.h>
-#include <sys/resource.h>
-#include <sys/utsname.h>
-#include <sys/wait.h>
-#include <err.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "arch.h"
-#include "buf.h"
-#include "config.h"
-#include "dir.h"
-#include "globals.h"
-#include "GNode.h"
-#include "job.h"
-#include "make.h"
-#include "parse.h"
-#include "pathnames.h"
-#include "shell.h"
-#include "str.h"
-#include "suff.h"
-#include "targ.h"
-#include "util.h"
-#include "var.h"
-
-extern char **environ; /* XXX what header declares this variable? */
-
-#define WANT_ENV_MKLVL 1
-#define MKLVL_MAXVAL 500
-#define MKLVL_ENVVAR "__MKLVL__"
-
-/* ordered list of makefiles to read */
-static Lst makefiles = Lst_Initializer(makefiles);
-
-/* ordered list of source makefiles */
-static Lst source_makefiles = Lst_Initializer(source_makefiles);
-
-/* list of variables to print */
-static Lst variables = Lst_Initializer(variables);
-
-static Boolean expandVars; /* fully expand printed variables */
-static Boolean noBuiltins; /* -r flag */
-static Boolean forceJobs; /* -j argument given */
-static char *curdir; /* startup directory */
-static char *objdir; /* where we chdir'ed to */
-static char **save_argv; /* saved argv */
-static char *save_makeflags;/* saved MAKEFLAGS */
-
-/* (-E) vars to override from env */
-Lst envFirstVars = Lst_Initializer(envFirstVars);
-
-/* Targets to be made */
-Lst create = Lst_Initializer(create);
-
-Boolean allPrecious; /* .PRECIOUS given on line by itself */
-Boolean is_posix; /* .POSIX target seen */
-Boolean mfAutoDeps; /* .MAKEFILEDEPS target seen */
-Boolean remakingMakefiles; /* True if remaking makefiles is in progress */
-Boolean beSilent; /* -s flag */
-Boolean beVerbose; /* -v flag */
-Boolean beQuiet; /* -Q flag */
-Boolean compatMake; /* -B argument */
-int debug; /* -d flag */
-Boolean ignoreErrors; /* -i flag */
-int jobLimit; /* -j argument */
-int makeErrors; /* Number of targets not remade due to errors */
-Boolean jobsRunning; /* TRUE if the jobs might be running */
-Boolean keepgoing; /* -k flag */
-Boolean noExecute; /* -n flag */
-Boolean printGraphOnly; /* -p flag */
-Boolean queryFlag; /* -q flag */
-Boolean touchFlag; /* -t flag */
-Boolean usePipes; /* !-P flag */
-uint32_t warn_cmd; /* command line warning flags */
-uint32_t warn_flags; /* actual warning flags */
-uint32_t warn_nocmd; /* command line no-warning flags */
-
-time_t now; /* Time at start of make */
-struct GNode *DEFAULT; /* .DEFAULT node */
-
-static struct {
- const char *foreign_name;
- const char *freebsd_name;
-} arch_aliases[] = {
- { "x86_64", "amd64" },
- { "mipsel", "mips" },
-};
-
-/**
- * Exit with usage message.
- */
-static void
-usage(void)
-{
- fprintf(stderr,
- "usage: make [-BPSXeiknpqrstv] [-C directory] [-D variable]\n"
- "\t[-d flags] [-E variable] [-f makefile] [-I directory]\n"
- "\t[-j max_jobs] [-m directory] [-V variable]\n"
- "\t[variable=value] [target ...]\n");
- exit(2);
-}
-
-/**
- * MFLAGS_append
- * Append a flag with an optional argument to MAKEFLAGS and MFLAGS
- */
-static void
-MFLAGS_append(const char *flag, char *arg)
-{
- char *str;
-
- Var_Append(".MAKEFLAGS", flag, VAR_GLOBAL);
- if (arg != NULL) {
- str = MAKEFLAGS_quote(arg);
- Var_Append(".MAKEFLAGS", str, VAR_GLOBAL);
- free(str);
- }
-
- Var_Append("MFLAGS", flag, VAR_GLOBAL);
- if (arg != NULL) {
- str = MAKEFLAGS_quote(arg);
- Var_Append("MFLAGS", str, VAR_GLOBAL);
- free(str);
- }
-}
-
-/**
- * Main_ParseWarn
- *
- * Handle argument to warning option.
- */
-int
-Main_ParseWarn(const char *arg, int iscmd)
-{
- int i, neg;
-
- static const struct {
- const char *option;
- uint32_t flag;
- } options[] = {
- { "dirsyntax", WARN_DIRSYNTAX },
- { NULL, 0 }
- };
-
- neg = 0;
- if (arg[0] == 'n' && arg[1] == 'o') {
- neg = 1;
- arg += 2;
- }
-
- for (i = 0; options[i].option != NULL; i++)
- if (strcmp(arg, options[i].option) == 0)
- break;
-
- if (options[i].option == NULL)
- /* unknown option */
- return (-1);
-
- if (iscmd) {
- if (!neg) {
- warn_cmd |= options[i].flag;
- warn_nocmd &= ~options[i].flag;
- warn_flags |= options[i].flag;
- } else {
- warn_nocmd |= options[i].flag;
- warn_cmd &= ~options[i].flag;
- warn_flags &= ~options[i].flag;
- }
- } else {
- if (!neg) {
- warn_flags |= (options[i].flag & ~warn_nocmd);
- } else {
- warn_flags &= ~(options[i].flag | warn_cmd);
- }
- }
- return (0);
-}
-
-/**
- * Open and parse the given makefile.
- *
- * Results:
- * TRUE if ok. FALSE if couldn't open file.
- */
-static Boolean
-ReadMakefile(const char p[])
-{
- char *fname, *fnamesave; /* makefile to read */
- FILE *stream;
- char *name, path[MAXPATHLEN];
- char *MAKEFILE;
- int setMAKEFILE;
-
- /* XXX - remove this once constification is done */
- fnamesave = fname = estrdup(p);
-
- if (!strcmp(fname, "-")) {
- Parse_File("(stdin)", stdin);
- Var_SetGlobal("MAKEFILE", "");
- } else {
- setMAKEFILE = strcmp(fname, ".depend");
-
- /* if we've chdir'd, rebuild the path name */
- if (curdir != objdir && *fname != '/') {
- snprintf(path, MAXPATHLEN, "%s/%s", curdir, fname);
- /*
- * XXX The realpath stuff breaks relative includes
- * XXX in some cases. The problem likely is in
- * XXX parse.c where it does special things in
- * XXX ParseDoInclude if the file is relative
- * XXX or absolute and not a system file. There
- * XXX it assumes that if the current file that's
- * XXX being included is absolute, that any files
- * XXX that it includes shouldn't do the -I path
- * XXX stuff, which is inconsistent with historical
- * XXX behavior. However, I can't penetrate the mists
- * XXX further, so I'm putting this workaround in
- * XXX here until such time as the underlying bug
- * XXX can be fixed.
- */
-#if THIS_BREAKS_THINGS
- if (realpath(path, path) != NULL &&
- (stream = fopen(path, "r")) != NULL) {
- MAKEFILE = fname;
- fname = path;
- goto found;
- }
- } else if (realpath(fname, path) != NULL) {
- MAKEFILE = fname;
- fname = path;
- if ((stream = fopen(fname, "r")) != NULL)
- goto found;
- }
-#else
- if ((stream = fopen(path, "r")) != NULL) {
- MAKEFILE = fname;
- fname = path;
- goto found;
- }
- } else {
- MAKEFILE = fname;
- if ((stream = fopen(fname, "r")) != NULL)
- goto found;
- }
-#endif
- /* look in -I and system include directories. */
- name = Path_FindFile(fname, &parseIncPath);
- if (!name)
- name = Path_FindFile(fname, &sysIncPath);
- if (!name || !(stream = fopen(name, "r"))) {
- free(fnamesave);
- return (FALSE);
- }
- MAKEFILE = 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:
- if (setMAKEFILE)
- Var_SetGlobal("MAKEFILE", MAKEFILE);
- Parse_File(fname, stream);
- }
- free(fnamesave);
- return (TRUE);
-}
-
-/**
- * Open and parse the given makefile.
- * If open is successful add it to the list of makefiles.
- *
- * Results:
- * TRUE if ok. FALSE if couldn't open file.
- */
-static Boolean
-TryReadMakefile(const char p[])
-{
- char *data;
- LstNode *last = Lst_Last(&source_makefiles);
-
- if (!ReadMakefile(p))
- return (FALSE);
-
- data = estrdup(p);
- if (last == NULL) {
- LstNode *first = Lst_First(&source_makefiles);
- Lst_Insert(&source_makefiles, first, data);
- } else
- Lst_Append(&source_makefiles, last, estrdup(p));
- return (TRUE);
-}
-
-/**
- * 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;
- Boolean found_dd = FALSE;
- char found_dir[MAXPATHLEN + 1]; /* for searching for sys.mk */
-
-rearg:
- optind = 1; /* since we're called more than once */
- optreset = 1;
-#define OPTFLAGS "ABC:D:d:E:ef:I:ij:km:nPpQqrSstV:vXx:"
- for (;;) {
- if ((optind < argc) && strcmp(argv[optind], "--") == 0) {
- found_dd = TRUE;
- }
- if ((c = getopt(argc, argv, OPTFLAGS)) == -1) {
- break;
- }
- switch(c) {
-
- case 'A':
- arch_fatal = FALSE;
- MFLAGS_append("-A", NULL);
- break;
- case 'B':
- compatMake = TRUE;
- MFLAGS_append("-B", NULL);
- unsetenv("MAKE_JOBS_FIFO");
- break;
- case 'C':
- if (chdir(optarg) == -1)
- err(1, "chdir %s", optarg);
- if (getcwd(curdir, MAXPATHLEN) == NULL)
- err(2, NULL);
- break;
- case 'D':
- Var_SetGlobal(optarg, "1");
- MFLAGS_append("-D", 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 '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 'j':
- debug |= DEBUG_JOB;
- break;
- case 'l':
- debug |= DEBUG_LOUD;
- break;
- case 'm':
- debug |= DEBUG_MAKE;
- break;
- case 's':
- debug |= DEBUG_SUFF;
- break;
- case 't':
- debug |= DEBUG_TARG;
- break;
- case 'v':
- debug |= DEBUG_VAR;
- break;
- default:
- warnx("illegal argument to d option "
- "-- %c", *modules);
- usage();
- }
- MFLAGS_append("-d", optarg);
- break;
- }
- case 'E':
- Lst_AtEnd(&envFirstVars, estrdup(optarg));
- MFLAGS_append("-E", optarg);
- break;
- case 'e':
- checkEnvFirst = TRUE;
- MFLAGS_append("-e", NULL);
- break;
- case 'f':
- Lst_AtEnd(&makefiles, estrdup(optarg));
- break;
- case 'I':
- Parse_AddIncludeDir(optarg);
- MFLAGS_append("-I", optarg);
- break;
- case 'i':
- ignoreErrors = TRUE;
- MFLAGS_append("-i", NULL);
- break;
- case 'j': {
- char *endptr;
-
- forceJobs = TRUE;
- jobLimit = strtol(optarg, &endptr, 10);
- if (jobLimit <= 0 || *endptr != '\0') {
- warnx("illegal number, -j argument -- %s",
- optarg);
- usage();
- }
- MFLAGS_append("-j", optarg);
- break;
- }
- case 'k':
- keepgoing = TRUE;
- MFLAGS_append("-k", NULL);
- break;
- case 'm':
- /* look for magic parent directory search string */
- if (strncmp(".../", optarg, 4) == 0) {
- if (!Dir_FindHereOrAbove(curdir, optarg + 4,
- found_dir, sizeof(found_dir)))
- break; /* nothing doing */
- Path_AddDir(&sysIncPath, found_dir);
- } else {
- Path_AddDir(&sysIncPath, optarg);
- }
- MFLAGS_append("-m", optarg);
- break;
- case 'n':
- noExecute = TRUE;
- MFLAGS_append("-n", NULL);
- break;
- case 'P':
- usePipes = FALSE;
- MFLAGS_append("-P", NULL);
- break;
- case 'p':
- printGraphOnly = TRUE;
- debug |= DEBUG_GRAPH1;
- break;
- case 'Q':
- beQuiet = TRUE;
- beVerbose = FALSE;
- MFLAGS_append("-Q", NULL);
- break;
- case 'q':
- queryFlag = TRUE;
- /* Kind of nonsensical, wot? */
- MFLAGS_append("-q", NULL);
- break;
- case 'r':
- noBuiltins = TRUE;
- MFLAGS_append("-r", NULL);
- break;
- case 'S':
- keepgoing = FALSE;
- MFLAGS_append("-S", NULL);
- break;
- case 's':
- beSilent = TRUE;
- MFLAGS_append("-s", NULL);
- break;
- case 't':
- touchFlag = TRUE;
- MFLAGS_append("-t", NULL);
- break;
- case 'V':
- Lst_AtEnd(&variables, estrdup(optarg));
- MFLAGS_append("-V", optarg);
- break;
- case 'v':
- beVerbose = TRUE;
- beQuiet = FALSE;
- MFLAGS_append("-v", NULL);
- break;
- case 'X':
- expandVars = FALSE;
- break;
- case 'x':
- if (Main_ParseWarn(optarg, 1) != -1)
- MFLAGS_append("-x", optarg);
- break;
-
- default:
- case '?':
- usage();
- }
- }
- argv += optind;
- argc -= optind;
-
- oldVars = TRUE;
-
- /*
- * Parse the rest of the arguments.
- * o Check for variable assignments and perform them if so.
- * o Check for more flags and restart getopt if so.
- * o Anything else is taken to be a target and added
- * to the end of the "create" list.
- */
- for (; *argv != NULL; ++argv, --argc) {
- if (Parse_IsVar(*argv)) {
- char *ptr = MAKEFLAGS_quote(*argv);
- char *v = estrdup(*argv);
-
- Var_Append(".MAKEFLAGS", ptr, VAR_GLOBAL);
- Parse_DoVar(v, VAR_CMD);
- free(ptr);
- free(v);
-
- } else if ((*argv)[0] == '-') {
- if ((*argv)[1] == '\0') {
- /*
- * (*argv) is a single dash, so we
- * just ignore it.
- */
- } else if (found_dd) {
- /*
- * Double dash has been found, ignore
- * any more options. But what do we do
- * with it? For now treat it like a target.
- */
- Lst_AtEnd(&create, estrdup(*argv));
- } else {
- /*
- * (*argv) is a -flag, so backup argv and
- * argc. getopt() expects options to start
- * in the 2nd position.
- */
- argc++;
- argv--;
- goto rearg;
- }
-
- } else if ((*argv)[0] == '\0') {
- Punt("illegal (null) argument.");
-
- } else {
- Lst_AtEnd(&create, estrdup(*argv));
- }
- }
-}
-
-/**
- * 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(char *line, int mflags)
-{
- ArgArray aa;
-
- if (line == NULL)
- return;
- for (; *line == ' '; ++line)
- continue;
- if (!*line)
- return;
-
- if (mflags)
- MAKEFLAGS_break(&aa, line);
- else
- brk_string(&aa, line, TRUE);
-
- MainParseArgs(aa.argc, aa.argv);
- ArgArray_Done(&aa);
-}
-
-static char *
-chdir_verify_path(const char *path, char *obpath)
-{
- struct stat sb;
-
- if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
- if (chdir(path) == -1 || getcwd(obpath, MAXPATHLEN) == NULL) {
- warn("warning: %s", path);
- return (NULL);
- }
- return (obpath);
- }
-
- return (NULL);
-}
-
-/**
- * In lieu of a good way to prevent every possible looping in make(1), stop
- * there from being more than MKLVL_MAXVAL processes forked by make(1), to
- * prevent a forkbomb from happening, in a dumb and mechanical way.
- *
- * Side Effects:
- * Creates or modifies environment variable MKLVL_ENVVAR via setenv().
- */
-static void
-check_make_level(void)
-{
-#ifdef WANT_ENV_MKLVL
- char *value = getenv(MKLVL_ENVVAR);
- int level = (value == NULL) ? 0 : atoi(value);
-
- if (level < 0) {
- errx(2, "Invalid value for recursion level (%d).", level);
- } else if (level > MKLVL_MAXVAL) {
- errx(2, "Max recursion level (%d) exceeded.", MKLVL_MAXVAL);
- } else {
- char new_value[32];
- sprintf(new_value, "%d", level + 1);
- setenv(MKLVL_ENVVAR, new_value, 1);
- }
-#endif /* WANT_ENV_MKLVL */
-}
-
-/**
- * Main_AddSourceMakefile
- * Add a file to the list of source makefiles
- */
-void
-Main_AddSourceMakefile(const char *name)
-{
-
- Lst_AtEnd(&source_makefiles, estrdup(name));
-}
-
-/**
- * Remake_Makefiles
- * Remake all the makefiles
- */
-static void
-Remake_Makefiles(void)
-{
- Lst cleanup;
- LstNode *ln;
- int error_cnt = 0;
- int remade_cnt = 0;
-
- Compat_InstallSignalHandlers();
- if (curdir != objdir) {
- if (chdir(curdir) < 0)
- Fatal("Failed to change directory to %s.", curdir);
- }
-
- Lst_Init(&cleanup);
- LST_FOREACH(ln, &source_makefiles) {
- LstNode *ln2;
- struct GNode *gn;
- const char *name = Lst_Datum(ln);
- Boolean saveTouchFlag = touchFlag;
- Boolean saveQueryFlag = queryFlag;
- Boolean saveNoExecute = noExecute;
- int mtime;
-
- /*
- * Create node
- */
- gn = Targ_FindNode(name, TARG_CREATE);
- DEBUGF(MAKE, ("Checking %s...", gn->name));
- Suff_FindDeps(gn);
-
- /*
- * -t, -q and -n has no effect unless the makefile is
- * specified as one of the targets explicitly in the
- * command line
- */
- LST_FOREACH(ln2, &create) {
- if (!strcmp(gn->name, Lst_Datum(ln2))) {
- /* found as a target */
- break;
- }
- }
- if (ln2 == NULL) {
- touchFlag = FALSE;
- queryFlag = FALSE;
- noExecute = FALSE;
- }
-
- /*
- * Check and remake the makefile
- */
- mtime = Dir_MTime(gn);
- remakingMakefiles = TRUE;
- Compat_Make(gn, gn);
- remakingMakefiles = FALSE;
-
- /*
- * Restore -t, -q and -n behaviour
- */
- touchFlag = saveTouchFlag;
- queryFlag = saveQueryFlag;
- noExecute = saveNoExecute;
-
- /*
- * Compat_Make will leave the 'made' field of gn
- * in one of the following states:
- * UPTODATE gn was already up-to-date
- * MADE gn was recreated successfully
- * ERROR An error occurred while gn was being created
- * ABORTED gn was not remade because one of its inferiors
- * could not be made due to errors.
- */
- if (gn->made == MADE) {
- if (mtime != Dir_MTime(gn)) {
- DEBUGF(MAKE,
- ("%s updated (%d -> %d).\n",
- gn->name, mtime, gn->mtime));
- remade_cnt++;
- } else {
- DEBUGF(MAKE,
- ("%s not updated: skipping restart.\n",
- gn->name));
- }
- } else if (gn->made == ERROR)
- error_cnt++;
- else if (gn->made == ABORTED) {
- printf("`%s' not remade because of errors.\n",
- gn->name);
- error_cnt++;
- } else if (gn->made == UPTODATE) {
- Lst_EnQueue(&cleanup, gn);
- }
- }
-
- if (error_cnt > 0)
- Fatal("Failed to remake Makefiles.");
- if (remade_cnt > 0) {
- DEBUGF(MAKE, ("Restarting `%s'.\n", save_argv[0]));
-
- /*
- * Some of makefiles were remade -- restart from clean state
- */
- if (save_makeflags != NULL)
- setenv("MAKEFLAGS", save_makeflags, 1);
- else
- unsetenv("MAKEFLAGS");
- if (execvp(save_argv[0], save_argv) < 0) {
- Fatal("Can't restart `%s': %s.",
- save_argv[0], strerror(errno));
- }
- }
-
- while (!Lst_IsEmpty(&cleanup)) {
- GNode *gn = Lst_DeQueue(&cleanup);
-
- gn->unmade = 0;
- gn->make = FALSE;
- gn->made = UNMADE;
- gn->childMade = FALSE;
- gn->mtime = gn->cmtime = 0;
- gn->cmtime_gn = NULL;
-
- LST_FOREACH(ln, &gn->children) {
- GNode *cgn = Lst_Datum(ln);
-
- gn->unmade++;
- Lst_EnQueue(&cleanup, cgn);
- }
- }
-
- if (curdir != objdir) {
- if (chdir(objdir) < 0)
- Fatal("Failed to change directory to %s.", objdir);
- }
-}
-
-/**
- * main
- * The main function, for obvious reasons. Initializes variables
- * and a few modules, then parses the arguments give it in the
- * environment and on the command line. Reads the system makefile
- * followed by either Makefile, makefile or the file given by the
- * -f argument. Sets the .MAKEFLAGS PMake variable based on all the
- * flags it has received by then uses either the Make or the Compat
- * module to create the initial list of targets.
- *
- * Results:
- * If -q was given, exits -1 if anything was out-of-date. Else it exits
- * 0.
- *
- * Side Effects:
- * The program exits when done. Targets are created. etc. etc. etc.
- */
-int
-main(int argc, char **argv)
-{
- const char *machine;
- const char *machine_arch;
- const char *machine_cpu;
- Boolean outOfDate = TRUE; /* FALSE if all targets up to date */
- const char *p;
- const char *pathp;
- const char *path;
- char mdpath[MAXPATHLEN];
- char obpath[MAXPATHLEN];
- char cdpath[MAXPATHLEN];
- char found_dir[MAXPATHLEN + 1]; /* for searching for sys.mk */
- char *cp = NULL, *start;
-
- save_argv = argv;
- save_makeflags = getenv("MAKEFLAGS");
- if (save_makeflags != NULL)
- save_makeflags = estrdup(save_makeflags);
-
- /*
- * Initialize file global variables.
- */
- expandVars = TRUE;
- noBuiltins = FALSE; /* Read the built-in rules */
- forceJobs = FALSE; /* No -j flag */
- curdir = cdpath;
-
- /*
- * Initialize program global variables.
- */
- beSilent = FALSE; /* Print commands as executed */
- ignoreErrors = FALSE; /* Pay attention to non-zero returns */
- noExecute = FALSE; /* Execute all commands */
- printGraphOnly = FALSE; /* Don't stop after printing graph */
- keepgoing = FALSE; /* Stop on error */
- allPrecious = FALSE; /* Remove targets when interrupted */
- queryFlag = FALSE; /* This is not just a check-run */
- touchFlag = FALSE; /* Actually update targets */
- usePipes = TRUE; /* Catch child output in pipes */
- debug = 0; /* No debug verbosity, please. */
- jobsRunning = FALSE;
-
- jobLimit = DEFMAXJOBS;
- compatMake = FALSE; /* No compat mode */
-
- check_make_level();
-
-#ifdef RLIMIT_NOFILE
- /*
- * get rid of resource limit on file descriptors
- */
- {
- struct rlimit rl;
- if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
- err(2, "getrlimit");
- }
- rl.rlim_cur = rl.rlim_max;
- if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
- err(2, "setrlimit");
- }
- }
-#endif
-
- /*
- * 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 both MACHINE and MACHINE_ARCH are decided at
- * run-time.
- */
- if ((machine = getenv("MACHINE")) == NULL) {
- static struct utsname utsname;
- unsigned int i;
-
- if (uname(&utsname) == -1)
- err(2, "uname");
- machine = utsname.machine;
-
- /* Canonicalize non-FreeBSD naming conventions */
- for (i = 0; i < sizeof(arch_aliases)
- / sizeof(arch_aliases[0]); i++)
- if (!strcmp(machine, arch_aliases[i].foreign_name)) {
- machine = arch_aliases[i].freebsd_name;
- break;
- }
- }
-
- if ((machine_arch = getenv("MACHINE_ARCH")) == NULL) {
-#ifdef MACHINE_ARCH
- machine_arch = MACHINE_ARCH;
-#else
- machine_arch = "unknown";
-#endif
- }
-
- /*
- * Set machine_cpu to the minimum supported CPU revision based
- * on the target architecture, if not already set.
- */
- if ((machine_cpu = getenv("MACHINE_CPU")) == NULL) {
- if (!strcmp(machine_arch, "i386"))
- machine_cpu = "i486";
- else
- machine_cpu = "unknown";
- }
-
- /*
- * Initialize the parsing, directory and variable modules to prepare
- * for the reading of inclusion paths and variable settings on the
- * command line
- */
- Proc_Init();
-
- Dir_Init(); /* Initialize directory structures so -I flags
- * can be processed correctly */
- Var_Init(environ); /* As well as the lists of variables for
- * parsing arguments */
-
- /*
- * Initialize the Shell so that we have a shell for != assignments
- * on the command line.
- */
- Shell_Init();
-
- /*
- * 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_SetGlobal("MAKE", argv[0]);
- Var_SetGlobal(".MAKEFLAGS", "");
- Var_SetGlobal("MFLAGS", "");
- Var_SetGlobal("MACHINE", machine);
- Var_SetGlobal("MACHINE_ARCH", machine_arch);
- Var_SetGlobal("MACHINE_CPU", machine_cpu);
-#ifdef MAKE_VERSION
- Var_SetGlobal("MAKE_VERSION", MAKE_VERSION);
-#endif
- Var_SetGlobal(".newline", "\n"); /* handy for :@ loops */
- {
- char tmp[64];
-
- snprintf(tmp, sizeof(tmp), "%u", getpid());
- Var_SetGlobal(".MAKE.PID", tmp);
- snprintf(tmp, sizeof(tmp), "%u", getppid());
- Var_SetGlobal(".MAKE.PPID", tmp);
- }
- Job_SetPrefix();
-
- /*
- * Find where we are...
- */
- if (getcwd(curdir, MAXPATHLEN) == NULL)
- err(2, NULL);
-
- /*
- * First snag things out of the MAKEFLAGS environment
- * variable. Then parse the command line arguments.
- */
- Main_ParseArgLine(getenv("MAKEFLAGS"), 1);
-
- MainParseArgs(argc, argv);
-
- /*
- * Verify that cwd is sane (after -C may have changed it).
- */
- {
- struct stat sa;
-
- if (stat(curdir, &sa) == -1)
- err(2, "%s", curdir);
- }
-
- /*
- * The object directory location is determined using the
- * following order of preference:
- *
- * 1. MAKEOBJDIRPREFIX`cwd`
- * 2. MAKEOBJDIR
- * 3. PATH_OBJDIR.${MACHINE}
- * 4. PATH_OBJDIR
- * 5. PATH_OBJDIRPREFIX`cwd`
- *
- * If one of the first two fails, use the current directory.
- * If the remaining three all fail, use the current directory.
- *
- * 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 (!(pathp = getenv("MAKEOBJDIRPREFIX"))) {
- if (!(path = getenv("MAKEOBJDIR"))) {
- path = PATH_OBJDIR;
- pathp = PATH_OBJDIRPREFIX;
- snprintf(mdpath, MAXPATHLEN, "%s.%s", path, machine);
- if (!(objdir = chdir_verify_path(mdpath, obpath)))
- if (!(objdir=chdir_verify_path(path, obpath))) {
- snprintf(mdpath, MAXPATHLEN,
- "%s%s", pathp, curdir);
- if (!(objdir=chdir_verify_path(mdpath,
- obpath)))
- objdir = curdir;
- }
- }
- else if (!(objdir = chdir_verify_path(path, obpath)))
- objdir = curdir;
- }
- else {
- snprintf(mdpath, MAXPATHLEN, "%s%s", pathp, curdir);
- if (!(objdir = chdir_verify_path(mdpath, obpath)))
- objdir = curdir;
- }
- Dir_InitDot(); /* Initialize the "." directory */
- if (objdir != curdir)
- Path_AddDir(&dirSearchPath, curdir);
- Var_SetGlobal(".ST_EXPORTVAR", "YES");
- Var_SetGlobal(".CURDIR", curdir);
- Var_SetGlobal(".OBJDIR", objdir);
-
- if (getenv("MAKE_JOBS_FIFO") != NULL)
- forceJobs = TRUE;
- /*
- * Be compatible if user did not specify -j and did not explicitly
- * turned compatibility on
- */
- if (!compatMake && !forceJobs)
- compatMake = TRUE;
-
- /*
- * Initialize target and suffix modules in preparation for
- * parsing the makefile(s)
- */
- Targ_Init();
- Suff_Init();
-
- DEFAULT = NULL;
- time(&now);
-
- /*
- * 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)) {
- Var_SetGlobal(".TARGETS", "");
- } else {
- LstNode *ln;
-
- for (ln = Lst_First(&create); ln != NULL; ln = Lst_Succ(ln)) {
- char *name = Lst_Datum(ln);
-
- Var_Append(".TARGETS", name, VAR_GLOBAL);
- }
- }
-
-
- /*
- * 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 (TAILQ_EMPTY(&sysIncPath)) {
- char defsyspath[] = PATH_DEFSYSPATH;
- char *syspath = getenv("MAKESYSPATH");
-
- /*
- * If no user-supplied system path was given (thru -m option)
- * add the directories from the DEFSYSPATH (more than one may
- * be given as dir1:...:dirn) to the system include path.
- */
- if (syspath == NULL || *syspath == '\0')
- syspath = defsyspath;
- else
- syspath = estrdup(syspath);
-
- for (start = syspath; *start != '\0'; start = cp) {
- for (cp = start; *cp != '\0' && *cp != ':'; cp++)
- continue;
- if (*cp == ':') {
- *cp++ = '\0';
- }
- /* look for magic parent directory search string */
- if (strncmp(".../", start, 4) == 0) {
- if (Dir_FindHereOrAbove(curdir, start + 4,
- found_dir, sizeof(found_dir))) {
- Path_AddDir(&sysIncPath, found_dir);
- }
- } else {
- Path_AddDir(&sysIncPath, start);
- }
- }
- if (syspath != defsyspath)
- free(syspath);
- }
-
- /*
- * Read in the built-in rules first, followed by the specified
- * makefile, if it was (makefile != (char *) NULL), or the default
- * Makefile and makefile, in that order, if it wasn't.
- */
- if (!noBuiltins) {
- /* Path of sys.mk */
- Lst sysMkPath = Lst_Initializer(sysMkPath);
- LstNode *ln;
- char defsysmk[] = PATH_DEFSYSMK;
-
- Path_Expand(defsysmk, &sysIncPath, &sysMkPath);
- if (Lst_IsEmpty(&sysMkPath))
- Fatal("make: no system rules (%s).", PATH_DEFSYSMK);
- LST_FOREACH(ln, &sysMkPath) {
- if (!ReadMakefile(Lst_Datum(ln)))
- break;
- }
- if (ln != NULL)
- Fatal("make: cannot open %s.", (char *)Lst_Datum(ln));
- Lst_Destroy(&sysMkPath, free);
- }
-
- if (!Lst_IsEmpty(&makefiles)) {
- LstNode *ln;
-
- LST_FOREACH(ln, &makefiles) {
- if (!TryReadMakefile(Lst_Datum(ln)))
- break;
- }
- if (ln != NULL)
- Fatal("make: cannot open %s.", (char *)Lst_Datum(ln));
- } else if (!TryReadMakefile("BSDmakefile"))
- if (!TryReadMakefile("makefile"))
- TryReadMakefile("Makefile");
-
- ReadMakefile(".depend");
-
- /* Install all the flags into the MAKEFLAGS envariable. */
- if (((p = Var_Value(".MAKEFLAGS", VAR_GLOBAL)) != NULL) && *p)
- setenv("MAKEFLAGS", p, 1);
- else
- setenv("MAKEFLAGS", "", 1);
-
- /*
- * For compatibility, look at the directories in the VPATH variable
- * and add them to the search path, if the variable is defined. The
- * variable's value is in the same format as the PATH envariable, i.e.
- * <directory>:<directory>:<directory>...
- */
- if (Var_Exists("VPATH", VAR_CMD)) {
- /*
- * GCC stores string constants in read-only memory, but
- * Var_Subst will want to write this thing, so store it
- * in an array
- */
- static char VPATH[] = "${VPATH}";
- Buffer *buf;
- char *vpath;
- char *ptr;
- char savec;
-
- buf = Var_Subst(VPATH, VAR_CMD, FALSE);
-
- vpath = Buf_Data(buf);
- do {
- /* skip to end of directory */
- for (ptr = vpath; *ptr != ':' && *ptr != '\0'; ptr++)
- ;
-
- /* Save terminator character so know when to stop */
- savec = *ptr;
- *ptr = '\0';
-
- /* Add directory to search path */
- Path_AddDir(&dirSearchPath, vpath);
-
- vpath = ptr + 1;
- } while (savec != '\0');
-
- Buf_Destroy(buf, TRUE);
- }
-
- /*
- * Now that all search paths have been read for suffixes et al, it's
- * time to add the default search path to their lists...
- */
- Suff_DoPaths();
-
- /* print the initial graph, if the user requested it */
- if (DEBUG(GRAPH1))
- Targ_PrintGraph(1);
-
- /* print the values of any variables requested by the user */
- if (Lst_IsEmpty(&variables) && !printGraphOnly) {
- /*
- * Since the user has not requested that any variables
- * be printed, we can build targets.
- *
- * Have 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.
- */
- Lst targs = Lst_Initializer(targs);
-
- if (!is_posix && mfAutoDeps) {
- /*
- * Check if any of the makefiles are out-of-date.
- */
- Remake_Makefiles();
- }
-
- if (Lst_IsEmpty(&create))
- Parse_MainName(&targs);
- else
- Targ_FindList(&targs, &create, TARG_CREATE);
-
- if (compatMake) {
- /*
- * Compat_Init will take care of creating
- * all the targets as well as initializing
- * the module.
- */
- Compat_Run(&targs);
- outOfDate = 0;
- } else {
- /*
- * Initialize job module before traversing
- * the graph, now that any .BEGIN and .END
- * targets have been read. This is done
- * only if the -q flag wasn't given (to
- * prevent the .BEGIN from being executed
- * should it exist).
- */
- if (!queryFlag) {
- Job_Init(jobLimit);
- jobsRunning = TRUE;
- }
-
- /* Traverse the graph, checking on all the targets */
- outOfDate = Make_Run(&targs);
- }
- Lst_Destroy(&targs, NOFREE);
-
- } else {
- Var_Print(&variables, expandVars);
- }
-
- Lst_Destroy(&variables, free);
- Lst_Destroy(&makefiles, free);
- Lst_Destroy(&source_makefiles, free);
- Lst_Destroy(&create, free);
-
- /* print the graph now it's been processed if the user requested it */
- if (DEBUG(GRAPH2))
- Targ_PrintGraph(2);
-
- if (queryFlag)
- return (outOfDate);
-
- if (makeErrors != 0)
- Finish(makeErrors);
-
- return (0);
-}
diff --git a/usr.bin/make/make.1 b/usr.bin/make/make.1
deleted file mode 100644
index 63da1aa..0000000
--- a/usr.bin/make/make.1
+++ /dev/null
@@ -1,1843 +0,0 @@
-.\" Copyright (c) 1990, 1993
-.\" The Regents of the University of California. All rights reserved.
-.\"
-.\" 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.
-.\"
-.\" @(#)make.1 8.8 (Berkeley) 6/13/95
-.\" $FreeBSD$
-.\"
-.Dd May 30, 2012
-.Dt MAKE 1
-.Os
-.Sh NAME
-.Nm make
-.Nd maintain program dependencies
-.Sh SYNOPSIS
-.Nm
-.Op Fl ABPSXeiknpqrstv
-.Op Fl C Ar directory
-.Op Fl D Ar variable
-.Op Fl d Ar flags
-.Op Fl E Ar variable
-.Op Fl f Ar makefile
-.Op Fl I Ar directory
-.Bk -words
-.Op Fl j Ar max_jobs
-.Op Fl m Ar directory
-.Ek
-.Op Fl V Ar variable
-.Op Fl x Ar warning_options
-.Op Ar variable Ns No = Ns Ar value
-.Op Ar target ...
-.Sh DESCRIPTION
-The
-.Nm
-utility is a program designed to simplify the maintenance of other programs.
-Its input is a list of specifications
-describing dependency relationships between the generation of
-files and programs.
-.Pp
-First of all, the initial list of specifications will be read
-from the system makefile,
-.Pa sys.mk ,
-unless inhibited with the
-.Fl r
-option.
-The standard
-.Pa sys.mk
-as shipped with
-.Fx
-also handles
-.Xr make.conf 5 ,
-the default path to which
-can be altered via the
-.Nm
-variable
-.Va __MAKE_CONF .
-.Pp
-Then the first of
-.Pa BSDmakefile ,
-.Pa makefile ,
-and
-.Pa Makefile
-that can be found in the current directory, object directory (see
-.Va .OBJDIR ) ,
-or search path (see the
-.Fl I
-option)
-will be read for the main list of dependency specifications.
-A different makefile or list of them can be supplied via the
-.Fl f
-option(s).
-Finally, if the file
-.Pa .depend
-can be found in any of the aforesaid locations, it will also be read (see
-.Xr mkdep 1 ) .
-.Pp
-When
-.Nm
-searches for a makefile, its name takes precedence over its location.
-For instance,
-.Pa BSDmakefile
-in the object directory will be favored over
-.Pa Makefile
-in the current directory.
-.Pp
-The options are as follows:
-.Bl -tag -width Ds
-.It Fl A
-Make archive errors non-fatal, causing
-.Nm
-to just skip the remainder
-or all of the archive and continue after printing a message.
-.It Fl B
-Try to be backwards compatible by executing a single shell per command and
-by executing the commands to make the sources of a dependency line in sequence.
-This is turned on by default unless
-.Fl j
-is used.
-.It Fl C Ar directory
-Change to
-.Ar directory
-before reading the makefiles or doing anything else.
-If multiple
-.Fl C
-options are specified, each is interpreted relative to the previous one:
-.Fl C Pa / Fl C Pa etc
-is equivalent to
-.Fl C Pa /etc .
-.It Fl D Ar variable
-Define
-.Ar variable
-to be 1, in the global context.
-.It Fl d Ar flags
-Turn on debugging, and specify which portions of
-.Nm
-are to print debugging information.
-Argument
-.Ar flags
-is one or more of the following:
-.Bl -tag -width Ds
-.It Ar A
-Print all possible debugging information;
-equivalent to specifying all of the debugging flags.
-.It Ar a
-Print debugging information about archive searching and caching.
-.It Ar c
-Print debugging information about conditional evaluation.
-.It Ar d
-Print debugging information about directory searching and caching.
-.It Ar f
-Print debugging information about the execution of for loops.
-.It Ar "g1"
-Print the input graph before making anything.
-.It Ar "g2"
-Print the input graph after making everything, or before exiting
-on error.
-.It Ar j
-Print debugging information about running multiple shells.
-.It Ar l
-Print commands in Makefiles regardless of whether or not they are prefixed
-by @ or other "quiet" flags.
-Also known as "loud" behavior.
-.It Ar m
-Print debugging information about making targets, including modification
-dates.
-.It Ar s
-Print debugging information about suffix-transformation rules.
-.It Ar t
-Print debugging information about target list maintenance.
-.It Ar v
-Print debugging information about variable assignment.
-.El
-.It Fl E Ar variable
-Specify a variable whose environment value (if any) will override
-macro assignments within makefiles.
-.It Fl e
-Specify that environment values override macro assignments within
-makefiles for all variables.
-.It Fl f Ar makefile
-Specify a makefile to read instead of the default one.
-If
-.Ar makefile
-is not an absolute pathname,
-.Nm
-will search for it as described above.
-In case
-.Ar makefile
-is
-.Sq Fl ,
-standard input is read.
-Multiple
-.Fl f
-options can be supplied,
-and the makefiles will be read in that order.
-Unlike the other command-line options,
-.Fl f
-is neither stored in
-.Va .MAKEFLAGS
-nor pushed down to sub-makes via
-.Ev MAKEFLAGS .
-See below for more details on these variables.
-.It Fl I Ar directory
-Specify a directory in which to search for makefiles and included makefiles.
-Multiple
-.Fl I
-options can be specified to form a search path.
-The system makefile directory (or directories, see the
-.Fl m
-option) is automatically appended at the tail of this path.
-.It Fl i
-Ignore non-zero exit of shell commands in the makefile.
-Equivalent to specifying
-.Sq Ic \-
-before each command line in the makefile.
-.It Fl j Ar max_jobs
-Specify the maximum number of jobs that
-.Nm
-may have running at any one time.
-Turns compatibility mode off, unless the
-.Fl B
-flag is also specified.
-.It Fl k
-Continue processing after errors are encountered, but only on those targets
-that do not depend on the target whose creation caused the error.
-.It Fl m Ar directory
-Specify a directory in which to search for
-the system makefile and makefiles included via the <...> style.
-Multiple
-.Fl m
-options can be specified to form a search path.
-This path will override the default system include path,
-.Pa /usr/share/mk .
-The system include path will always be appended to the search path used
-for "..."-style inclusions and makefile searches (see the
-.Fl I
-option).
-.Pp
-If a file or directory name in the
-.Fl m
-argument (or the
-.Ev MAKESYSPATH
-environment variable) starts with the string
-.Qq \&.../
-then
-.Nm
-will search for the specified file or directory named in the remaining part
-of the argument string.
-The search starts with the current directory of the Makefile and then works
-upward towards the root of the filesystem.
-If the search is successful,
-then the resulting directory replaces the
-.Qq \&.../
-specification in the
-.Fl m
-argument.
-If used, this feature allows
-.Nm
-to easily search in the current source tree for customized sys.mk files
-(e.g. by using
-.Qq \&.../mk/sys.mk
-as an argument).
-Note that a
-.Fl C
-that are earlier on the command line affect where
-.Fl m Qq \&.../
-searches.
-.It Fl n
-Display the commands that would have been executed, but do not actually
-execute them.
-.It Fl P
-Collate the output of a given job and display it only when the job finishes,
-instead of mixing the output of parallel jobs together.
-This option has no effect unless
-.Fl j
-is used too.
-.It Fl p
-Only print the input graph, not executing any commands.
-The output is the same as
-.Fl d Ar g1 .
-When combined with
-.Fl f Pa /dev/null ,
-only the builtin rules of
-.Nm
-are displayed.
-.It Fl Q
-Be extra quiet.
-For multi-job makes, this will cause file banners not to be generated.
-.It Fl q
-Do not execute any commands, but exit 0 if the specified targets are
-up-to-date and 1, otherwise.
-.It Fl r
-Do not process the system makefile.
-.It Fl S
-Stop processing when an error is encountered.
-Default behaviour.
-This is needed to negate the
-.Fl k
-option during recursive builds.
-.It Fl s
-Do not echo any commands as they are executed.
-Equivalent to specifying
-.Sq Ic @
-before each command line in the makefile.
-.It Fl t
-Rather than re-building a target as specified in the makefile, create it
-or update its modification time to make it appear up-to-date.
-.It Fl V Ar variable
-Print
-.Nm Ns 's
-idea of the value of
-.Ar variable ,
-in the global context.
-Do not build any targets.
-Multiple instances of this option may be specified;
-the variables will be printed one per line,
-with a blank line for each null or undefined variable.
-If
-.Ar variable
-contains a
-.Sq Ic $
-then the value will be expanded before printing.
-.It Fl v
-Be extra verbose.
-Print any extra information.
-.It Fl X
-When using the
-.Fl V
-option to print the values of variables,
-do not recursively expand the values.
-.It Ar variable Ns No = Ns Ar value
-Set the value of the variable
-.Ar variable
-to
-.Ar value .
-.It Fl x Ar warning_options
-Specify extended warning options.
-This option may be specified several times.
-A
-.Ar warning_option
-can be prefixed with
-.Dq Li no
-in which case the warning is switched off.
-The currently available options are:
-.Bl -tag -width indent
-.It Li dirsyntax
-Warn if anything except blanks and comments follows an
-.Ic .endif
-or
-.Ic .else
-directive.
-.El
-.Pp
-See also the
-.Ic .WARN
-special target.
-.El
-.Pp
-There are seven different types of lines in a makefile: file dependency
-specifications, shell commands, variable assignments, include statements,
-conditional directives, for loops, and comments.
-.Pp
-In general, lines may be continued from one line to the next by ending
-them with a backslash
-.Pq Ql \e .
-The trailing newline character and initial whitespace on the following
-line are compressed into a single space.
-.Sh FILE DEPENDENCY SPECIFICATIONS
-Dependency lines consist of one or more targets, an operator, and zero
-or more sources.
-This creates a relationship where the targets
-.Dq depend
-on the sources
-and are usually created from them.
-The exact relationship between the target and the source is determined
-by the operator that separates them.
-The three operators are as follows:
-.Bl -tag -width flag
-.It Ic \&:
-A target is considered out-of-date if its modification time is less than
-those of any of its sources.
-Sources for a target accumulate over dependency lines when this operator
-is used.
-The target is removed if
-.Nm
-is interrupted.
-.It Ic \&!
-Targets are always re-created, but not until all sources have been
-examined and re-created as necessary.
-Sources for a target accumulate over dependency lines when this operator
-is used.
-The target is removed if
-.Nm
-is interrupted.
-.It Ic ::
-If no sources are specified, the target is always re-created.
-Otherwise, a target is considered out-of-date if any of its sources has
-been modified more recently than the target.
-Sources for a target do not accumulate over dependency lines when this
-operator is used.
-The target will not be removed if
-.Nm
-is interrupted.
-.El
-.Pp
-Targets and sources may contain the shell wildcard expressions
-.Ql \&? ,
-.Ql * ,
-.Ql []
-and
-.Ql {} .
-The expressions
-.Ql \&? ,
-.Ql *
-and
-.Ql []
-may only be used as part of the final
-component of the target or source, and must be used to describe existing
-files.
-The expression
-.Ql {}
-need not necessarily be used to describe existing files.
-Expansion is in directory order, not alphabetically as done in the shell.
-.Sh SHELL COMMANDS
-Each target may have associated with it a series of shell commands, normally
-used to create the target.
-Each of the commands in this script
-.Em must
-be preceded by a tab.
-While any target may appear on a dependency line, only one of these
-dependencies may be followed by a creation script, unless the
-.Sq Ic ::
-operator is used.
-.Pp
-If the first characters of the command line are
-.Sq Ic @ ,
-.Sq Ic \- ,
-and/or
-.Sq Ic + ,
-the command is treated specially.
-A
-.Sq Ic @
-causes the command not to be echoed before it is executed.
-A
-.Sq Ic \-
-causes any non-zero exit status of the command line to be ignored.
-A
-.Sq Ic +
-causes the command to be executed even if
-.Fl n
-is specified on the command line.
-.Sh VARIABLE ASSIGNMENTS
-Variables in
-.Nm
-are much like variables in the shell, and, by tradition,
-consist of all upper-case letters.
-The five operators that can be used to assign values to variables are as
-follows:
-.Bl -tag -width Ds
-.It Ic =
-Assign the value to the variable.
-Any previous value is overridden.
-.It Ic +=
-Append the value to the current value of the variable.
-.It Ic ?=
-Assign the value to the variable if it is not already defined.
-.It Ic :=
-Assign with expansion, i.e., expand the value before assigning it
-to the variable.
-Normally, expansion is not done until the variable is referenced.
-.It Ic !=
-Expand the value and pass it to the shell for execution and assign
-the result to the variable.
-Any newlines in the result are replaced with spaces.
-.El
-.Pp
-Any whitespace before the assigned
-.Ar value
-is removed; if the value is being appended, a single space is inserted
-between the previous contents of the variable and the appended value.
-.Pp
-Variables are expanded by surrounding the variable name with either
-curly braces
-.Pq Ql {}
-or parentheses
-.Pq Ql ()
-and preceding it with
-a dollar sign
-.Pq Ql $ .
-If the variable name contains only a single letter, the surrounding
-braces or parentheses are not required.
-This shorter form is not recommended.
-.Pp
-Variable substitution occurs at two distinct times, depending on where
-the variable is being used.
-Variables in dependency lines are expanded as the line is read.
-Variables in shell commands are expanded when the shell command is
-executed.
-.Pp
-The four different classes of variables (in order of increasing precedence)
-are:
-.Bl -tag -width Ds
-.It Environment variables
-Variables defined as part of
-.Nm Ns 's
-environment.
-.It Global variables
-Variables defined in the makefile or in included makefiles.
-.It Command line variables
-Variables defined as part of the command line and variables
-obtained from the
-.Ev MAKEFLAGS
-environment variable or the
-.Ic .MAKEFLAGS
-target.
-.It Local variables
-Variables that are defined specific to a certain target.
-.El
-.Pp
-If the name of an environment variable appears in a makefile
-on the left-hand side of an assignment,
-a global variable with the same name is created, and the latter
-shadows the former as per their relative precedences.
-The environment is not changed in this case, and the change
-is not exported to programs executed by
-.Nm .
-However, a command-line variable actually replaces
-the environment variable of the same name if the latter exists,
-which is visible to child programs.
-.Pp
-There are seven local variables in
-.Nm :
-.Bl -tag -width ".ARCHIVE"
-.It Va .ALLSRC
-The list of all sources for this target; also known as
-.Sq Va > .
-.It Va .ARCHIVE
-The name of the archive file; also known as
-.Sq Va \&! .
-.It Va .IMPSRC
-The name/path of the source from which the target is to be transformed
-(the
-.Dq implied
-source); also known as
-.Sq Va < .
-.It Va .MEMBER
-The name of the archive member; also known as
-.Sq Va % .
-.It Va .OODATE
-The list of sources for this target that were deemed out-of-date; also
-known as
-.Sq Va \&? .
-.It Va .PREFIX
-The file prefix of the file, containing only the file portion, no suffix
-or preceding directory components; also known as
-.Sq Va * .
-.It Va .TARGET
-The name of the target; also known as
-.Sq Va @ .
-.El
-.Pp
-The shorter forms
-.Sq Va @ ,
-.Sq Va \&! ,
-.Sq Va < ,
-.Sq Va % ,
-.Sq Va \&? ,
-.Sq Va > ,
-and
-.Sq Va *
-are permitted for backward
-compatibility and are not recommended.
-The six variables
-.Sq Va @F ,
-.Sq Va @D ,
-.Sq Va <F ,
-.Sq Va <D ,
-.Sq Va *F ,
-and
-.Sq Va *D
-are
-permitted for compatibility with
-.At V
-makefiles and are not recommended.
-.Pp
-Four of the local variables may be used in sources on dependency lines
-because they expand to the proper value for each target on the line.
-These variables are
-.Va .TARGET ,
-.Va .PREFIX ,
-.Va .ARCHIVE ,
-and
-.Va .MEMBER .
-.Pp
-In addition,
-.Nm
-sets or knows about the following internal variables or environment
-variables:
-.Bl -tag -width ".Va .MAKEFILE_LIST"
-.It Va $
-A single dollar sign
-.Ql $ ,
-i.e.\&
-.Ql $$
-expands to a single dollar
-sign.
-.It Va MAKE
-The name that
-.Nm
-was executed with
-.Pq Va argv Ns Op 0 .
-.It Va .CURDIR
-A path to the directory where
-.Nm
-was executed.
-The
-.Nm
-utility sets
-.Va .CURDIR
-to the canonical path given by
-.Xr getcwd 3 .
-.It Va .OBJDIR
-A path to the directory where the targets are built.
-At startup,
-.Nm
-searches for an alternate directory to place target files.
-It will attempt to change into this special directory
-and will search this directory for makefiles
-not found in the current directory.
-The following directories are tried in order:
-.Pp
-.Bl -enum -compact
-.It
-${MAKEOBJDIRPREFIX}/`pwd -P`
-.It
-${MAKEOBJDIR}
-.It
-obj.${MACHINE}
-.It
-obj
-.It
-/usr/obj/`pwd -P`
-.El
-.Pp
-The first directory that
-.Nm
-successfully changes into is used.
-If either
-.Ev MAKEOBJDIRPREFIX
-or
-.Ev MAKEOBJDIR
-is set in the environment but
-.Nm
-is unable to change into the corresponding directory,
-then the current directory is used
-without checking the remainder of the list.
-If they are undefined and
-.Nm
-is unable to change into any of the remaining three directories,
-then the current directory is used.
-Note, that
-.Ev MAKEOBJDIRPREFIX
-and
-.Ev MAKEOBJDIR
-must be environment variables and should not be set on
-.Nm Ns 's
-command line.
-.Pp
-The
-.Nm
-utility sets
-.Va .OBJDIR
-to the canonical path given by
-.Xr getcwd 3 .
-.It Va .MAKEFILE_LIST
-As
-.Nm
-reads various makefiles, including the default files and any
-obtained from the command line and
-.Ic .include
-and
-.Ic .sinclude
-directives, their names will be automatically appended to the
-.Va .MAKEFILE_LIST
-variable.
-They are added right before
-.Nm
-begins to parse them, so that the name of the current makefile is the
-last word in this variable.
-.It Ev MAKEFLAGS
-The environment variable
-.Ev MAKEFLAGS
-may initially contain anything that
-may be specified on
-.Nm Ns 's
-command line,
-including
-.Fl f
-option(s).
-After processing, its contents are stored in the
-.Va .MAKEFLAGS
-global variable, although any
-.Fl f
-options are omitted.
-Then all options and variable assignments specified on
-.Nm Ns 's
-command line, except for
-.Fl f ,
-are appended to the
-.Va .MAKEFLAGS
-variable.
-.Pp
-Whenever
-.Nm
-executes a program, it sets
-.Ev MAKEFLAGS
-in the program's environment to the current value of the
-.Va .MAKEFLAGS
-global variable.
-Thus, if
-.Ev MAKEFLAGS
-in
-.Nm Ns 's
-environment contains any
-.Fl f
-options, they will not be pushed down to child programs automatically.
-The
-.Nm
-utility effectively filters out
-.Fl f
-options from the environment and command line although it
-passes the rest of its options down to sub-makes via
-.Ev MAKEFLAGS
-by default.
-.Pp
-When passing macro definitions and flag arguments in the
-.Ev MAKEFLAGS
-environment variable,
-space and tab characters are quoted by preceding them with a backslash.
-When reading the
-.Ev MAKEFLAGS
-variable from the environment,
-all sequences of a backslash and one of space or tab
-are replaced just with their second character
-without causing a word break.
-Any other occurrences of a backslash are retained.
-Groups of unquoted space, tab and newline characters cause word
-breaking.
-.It Va .MAKEFLAGS
-Initially, this global variable contains
-.Nm Ns 's
-current run-time options from the environment
-and command line as described above, under
-.Ev MAKEFLAGS .
-By modifying the contents of the
-.Va .MAKEFLAGS
-global variable, the makefile can alter the contents of the
-.Ev MAKEFLAGS
-environment variable made available for all programs which
-.Nm
-executes.
-This includes adding
-.Fl f
-option(s).
-The current value of
-.Va .MAKEFLAGS
-is just copied verbatim to
-.Ev MAKEFLAGS
-in the environment of child programs.
-.Pp
-Note that any options entered to
-.Va .MAKEFLAGS
-neither affect the current instance of
-.Nm
-nor show up in its own copy of
-.Ev MAKEFLAGS
-instantly.
-However, they do show up in the
-.Ev MAKEFLAGS
-environment variable of programs executed by
-.Nm .
-On the other hand, a direct assignment to
-.Ev MAKEFLAGS
-neither affects the current instance of
-.Nm
-nor is passed down to
-.Nm Ns 's
-children.
-Compare with the
-.Ic .MAKEFLAGS
-special target below.
-.It Va MFLAGS
-This variable is provided for backward compatibility and
-contains all the options from the
-.Ev MAKEFLAGS
-environment variable plus any options specified on
-.Nm Ns 's
-command line.
-.It Va .MAKE.PID
-The process-id of
-.Nm .
-.It Va .MAKE.PPID
-The parent process-id of
-.Nm .
-.It Va .MAKE.JOB.PREFIX
-If
-.Nm
-is run with
-.Fl j Fl v
-then output for each target is prefixed with a token
-.Ql --- target ---
-the first part of which can be controlled via
-.Va .MAKE.JOB.PREFIX .
-.br
-For example:
-.Li .MAKE.JOB.PREFIX=${.newline}---${MAKE:T}[${.MAKE.PID}]
-would produce tokens like
-.Ql ---make[1234] target ---
-or
-.Li .MAKE.JOB.PREFIX=---pid[${.MAKE.PID}],ppid[${.MAKE.PPID}]
-would produce tokens like
-.Ql ---pid[56789],ppid[1234] target ---
-making it easier to track the degree of parallelism being achieved.
-.It Va .TARGETS
-List of targets
-.Nm
-is currently building.
-.It Va .INCLUDES
-See
-.Ic .INCLUDES
-special target.
-.It Va .LIBS
-See
-.Ic .LIBS
-special target.
-.It Va MACHINE
-Name of the machine architecture
-.Nm
-is running on, obtained from the
-.Ev MACHINE
-environment variable, or through
-.Xr uname 3
-if not defined.
-.It Va MACHINE_ARCH
-Name of the machine architecture
-.Nm
-was compiled for, defined at compilation time.
-.It Va VPATH
-Makefiles may assign a colon-delimited list of directories to
-.Va VPATH .
-These directories will be searched for source files by
-.Nm
-after it has finished parsing all input makefiles.
-.El
-.Ss Variable Modifiers
-Variable expansion may be modified to select or modify each word of the
-variable (where a
-.Dq word
-is whitespace-delimited sequence of characters).
-The general format of a variable expansion is as follows:
-.Pp
-.Dl {variable[:modifier[:...]]}
-.Pp
-Each modifier begins with a colon and one of the following
-special characters.
-The colon may be escaped with a backslash
-.Pq Ql \e .
-.Bl -tag -width Cm
-.Sm off
-.It Cm :C No / Ar pattern Xo
-.No / Ar replacement
-.No / Op Cm 1g
-.Xc
-.Sm on
-Modify each word of the value,
-substituting every match of the extended regular expression
-.Ar pattern
-(see
-.Xr re_format 7 )
-with the
-.Xr ed 1 Ns \-style
-.Ar replacement
-string.
-Normally, the first occurrence of the pattern in
-each word of the value is changed.
-The
-.Ql 1
-modifier causes the substitution to apply to at most one word; the
-.Ql g
-modifier causes the substitution to apply to as many instances of the
-search pattern as occur in the word or words it is found in.
-Note that
-.Ql 1
-and
-.Ql g
-are orthogonal; the former specifies whether multiple words are
-potentially affected, the latter whether multiple substitutions can
-potentially occur within each affected word.
-.It Cm :E
-Replaces each word in the variable with its suffix.
-.It Cm :H
-Replaces each word in the variable with everything but the last component.
-.It Cm :L
-Converts variable to lower-case letters.
-(deprecated)
-.It Cm :M Ns Ar pattern
-Select only those words that match the rest of the modifier.
-The standard shell wildcard characters
-.Pf ( Ql * ,
-.Ql \&? ,
-and
-.Ql [] )
-may
-be used.
-The wildcard characters may be escaped with a backslash
-.Pq Ql \e .
-.It Cm :N Ns Ar pattern
-This is identical to
-.Cm :M ,
-but selects all words which do not match
-the rest of the modifier.
-.It Cm :O
-Order every word in the variable alphabetically.
-.It Cm :Q
-Quotes every shell meta-character in the variable, so that it can be passed
-safely through recursive invocations of
-.Nm .
-.It Cm :R
-Replaces each word in the variable with everything but its suffix.
-.Sm off
-.It Cm :S No / Ar old_string Xo
-.No / Ar new_string
-.No / Op Cm g
-.Xc
-.Sm on
-Modify the first occurrence of
-.Ar old_string
-in each word of the variable's value, replacing it with
-.Ar new_string .
-If a
-.Ql g
-is appended to the last slash of the pattern, all occurrences
-in each word are replaced.
-If
-.Ar old_string
-begins with a caret
-.Pq Ql ^ ,
-.Ar old_string
-is anchored at the beginning of each word.
-If
-.Ar old_string
-ends with a dollar sign
-.Pq Ql $ ,
-it is anchored at the end of each word.
-Inside
-.Ar new_string ,
-an ampersand
-.Pq Ql &
-is replaced by
-.Ar old_string .
-Any character may be used as a delimiter for the parts of the modifier
-string.
-The anchoring, ampersand, and delimiter characters may be escaped with a
-backslash
-.Pq Ql \e .
-.Pp
-Variable expansion occurs in the normal fashion inside both
-.Ar old_string
-and
-.Ar new_string
-with the single exception that a backslash is used to prevent the expansion
-of a dollar sign
-.Pq Ql $ ,
-not a preceding dollar sign as is usual.
-.It Ar :old_string=new_string
-This is the
-.At V
-style variable substitution.
-It must be the last modifier specified.
-If
-.Ar old_string
-or
-.Ar new_string
-do not contain the pattern matching character
-.Ar %
-then it is assumed that they are
-anchored at the end of each word, so only suffixes or entire
-words may be replaced.
-Otherwise
-.Ar %
-is the substring of
-.Ar old_string
-to be replaced in
-.Ar new_string .
-.It Cm :T
-Replaces each word in the variable with its last component.
-.It Cm :tl
-Converts variable to lower-case letters.
-.It Cm :tu
-Converts variable to upper-case letters.
-.It Cm :U
-Converts variable to upper-case letters.
-(deprecated)
-.It Cm :u
-Remove adjacent duplicate words (like
-.Xr uniq 1 ) .
-.El
-.Sh DIRECTIVES, CONDITIONALS, AND FOR LOOPS
-Directives, conditionals, and for loops reminiscent
-of the C programming language are provided in
-.Nm .
-All such structures are identified by a line beginning with a single
-dot
-.Pq Ql \&.
-character.
-The following directives are supported:
-.Bl -tag -width Ds
-.It Ic .include Ar <file>
-.It Ic .include Ar \*qfile\*q
-Include the specified makefile.
-Variables between the angle brackets
-or double quotes are expanded to form the file name.
-If angle brackets
-are used, the included makefile is expected to be in the system
-makefile directory.
-If double quotes are used, the including
-makefile's directory and any directories specified using the
-.Fl I
-option are searched before the system
-makefile directory.
-.It Ic .sinclude Ar <file>
-.It Ic .sinclude Ar \*qfile\*q
-Like
-.Ic .include ,
-but silently ignored if the file cannot be found and opened.
-.It Ic .undef Ar variable
-Un-define the specified global variable.
-Only global variables may be un-defined.
-.It Ic .error Ar message
-Terminate processing of the makefile immediately.
-The filename of the
-makefile, the line on which the error was encountered and the specified
-message are printed to the standard error output and
-.Nm
-terminates with exit code 1.
-Variables in the message are expanded.
-.It Ic .warning Ar message
-Emit a warning message.
-The filename of the makefile,
-the line on which the warning was encountered,
-and the specified message are printed to the standard error output.
-Variables in the message are expanded.
-.El
-.Pp
-Conditionals are used to determine which parts of the Makefile
-to process.
-They are used similarly to the conditionals supported
-by the C pre-processor.
-The following conditionals are supported:
-.Bl -tag -width Ds
-.It Xo
-.Ic .if
-.Oo \&! Oc Ns Ar expression
-.Op Ar operator expression ...
-.Xc
-Test the value of an expression.
-.It Xo
-.Ic .ifdef
-.Oo \&! Oc Ns Ar variable
-.Op Ar operator variable ...
-.Xc
-Test the value of a variable.
-.It Xo
-.Ic .ifndef
-.Oo \&! Oc Ns Ar variable
-.Op Ar operator variable ...
-.Xc
-Test the value of a variable.
-.It Xo
-.Ic .ifmake
-.Oo \&! Oc Ns Ar target
-.Op Ar operator target ...
-.Xc
-Test the target being built.
-.It Xo
-.Ic .ifnmake
-.Oo \&! Oc Ns Ar target
-.Op Ar operator target ...
-.Xc
-Test the target being built.
-.It Ic .else
-Reverse the sense of the last conditional.
-.It Xo
-.Ic .elif
-.Oo \&! Oc Ns Ar expression
-.Op Ar operator expression ...
-.Xc
-A combination of
-.Ic .else
-followed by
-.Ic .if .
-.It Xo
-.Ic .elifdef
-.Oo \&! Oc Ns Ar variable
-.Op Ar operator variable ...
-.Xc
-A combination of
-.Ic .else
-followed by
-.Ic .ifdef .
-.It Xo
-.Ic .elifndef
-.Oo \&! Oc Ns Ar variable
-.Op Ar operator variable ...
-.Xc
-A combination of
-.Ic .else
-followed by
-.Ic .ifndef .
-.It Xo
-.Ic .elifmake
-.Oo \&! Oc Ns Ar target
-.Op Ar operator target ...
-.Xc
-A combination of
-.Ic .else
-followed by
-.Ic .ifmake .
-.It Xo
-.Ic .elifnmake
-.Oo \&! Oc Ns Ar target
-.Op Ar operator target ...
-.Xc
-A combination of
-.Ic .else
-followed by
-.Ic .ifnmake .
-.It Ic .endif
-End the body of the conditional.
-.El
-.Pp
-The
-.Ar operator
-may be any one of the following:
-.Bl -tag -width "Cm XX"
-.It Cm ||
-Logical
-.Tn OR
-.It Cm &&
-Logical
-.Tn AND ;
-of higher precedence than
-.Sq Ic || .
-.El
-.Pp
-As in C,
-.Nm
-will only evaluate a conditional as far as is necessary to determine
-its value.
-Parentheses may be used to change the order of evaluation.
-The boolean operator
-.Sq Ic !\&
-may be used to logically negate an entire
-conditional.
-It is of higher precedence than
-.Sq Ic && .
-.Pp
-The value of
-.Ar expression
-may be any of the following:
-.Bl -tag -width Ic
-.It Ic defined
-Takes a variable name as an argument and evaluates to true if the variable
-has been defined.
-.It Ic make
-Takes a target name as an argument and evaluates to true if the target
-was specified as part of
-.Nm Ns 's
-command line or was declared the default target (either implicitly or
-explicitly, see
-.Va .MAIN )
-before the line containing the conditional.
-.It Ic empty
-Takes a variable, with possible modifiers, and evaluates to true if
-the expansion of the variable would result in an empty string.
-.It Ic exists
-Takes a file name as an argument and evaluates to true if the file exists.
-The file is searched for on the system search path (see
-.Va .PATH ) .
-.It Ic target
-Takes a target name as an argument and evaluates to true if the target
-has been defined.
-.El
-.Pp
-An
-.Ar expression
-may also be a numeric or string comparison:
-in this case, the left-hand side
-.Ar must be
-a variable expansion, whereas the right-hand side can be a
-constant or a variable expansion.
-Variable expansion is performed on both sides, after which the resulting
-values are compared.
-A value is interpreted as hexadecimal if it is
-preceded by 0x, otherwise it is decimal; octal numbers are not supported.
-.Pp
-String comparison can only use the
-.Sq Ic ==
-or
-.Sq Ic !=
-operators, whereas numeric values (both integer and floating point)
-can also be compared using the
-.Sq Ic > ,
-.Sq Ic >= ,
-.Sq Ic <
-and
-.Sq Ic <=
-operators.
-.Pp
-If no relational operator (and right-hand value) are given, an implicit
-.Sq Ic != 0
-is used.
-However be very careful in using this feature especially
-when the left-hand side variable expansion returns a string.
-.Pp
-When
-.Nm
-is evaluating one of these conditional expressions, and it encounters
-a word it does not recognize, either the
-.Dq make
-or
-.Dq defined
-expression is applied to it, depending on the form of the conditional.
-If the form is
-.Ic .if ,
-.Ic .ifdef
-or
-.Ic .ifndef ,
-the
-.Dq defined
-expression is applied.
-Similarly, if the form is
-.Ic .ifmake
-or
-.Ic .ifnmake ,
-the
-.Dq make
-expression is applied.
-.Pp
-If the conditional evaluates to true the parsing of the makefile continues
-as before.
-If it evaluates to false, the following lines are skipped.
-In both cases this continues until a
-.Ic .else
-or
-.Ic .endif
-is found.
-.Pp
-For loops are typically used to apply a set of rules to a list of files.
-The syntax of a for loop is:
-.Pp
-.Bl -tag -width indent -compact
-.It Ic .for Ar variable Ic in Ar expression
-.It <make-rules>
-.It Ic .endfor
-.El
-.Pp
-After the for
-.Ar expression
-is evaluated, it is split into words.
-The
-iteration
-.Ar variable
-is successively set to each word, and substituted in the
-.Ic make-rules
-inside the body of the for loop.
-.Sh COMMENTS
-Comments begin with a hash
-.Pq Ql #
-character, anywhere but in a shell
-command line, and continue to the end of the line.
-.Sh SPECIAL SOURCES
-.Bl -tag -width Ic
-.It Ic .IGNORE
-Ignore any errors from the commands associated with this target, exactly
-as if they all were preceded by a dash
-.Pq Ql \- .
-.It Ic .MAKE
-Execute the commands associated with this target even if the
-.Fl n
-or
-.Fl t
-options were specified.
-Normally used to mark recursive
-.Nm Ns 's .
-.It Ic .NOTMAIN
-Normally
-.Nm
-selects the first target it encounters as the default target to be built
-if no target was specified.
-This source prevents this target from being selected.
-.It Ic .OPTIONAL
-If a target is marked with this attribute and
-.Nm
-cannot figure out how to create it, it will ignore this fact and assume
-the file is not needed or already exists.
-.It Ic .PRECIOUS
-When
-.Nm
-is interrupted, it removes any partially made targets.
-This source prevents the target from being removed.
-.It Ic .SILENT
-Do not echo any of the commands associated with this target, exactly
-as if they all were preceded by an at sign
-.Pq Ql @ .
-.It Ic .USE
-Turn the target into
-.Nm Ns 's
-version of a macro.
-When the target is used as a source for another target, the other target
-acquires the commands, sources, and attributes (except for
-.Ic .USE )
-of the
-source.
-If the target already has commands, the
-.Ic .USE
-target's commands are appended
-to them.
-.It Ic .WAIT
-If special
-.Ic .WAIT
-source appears in a dependency line, the sources that precede it are
-made before the sources that succeed it in the line.
-Loops are not being
-detected and targets that form loops will be silently ignored.
-.El
-.Sh SPECIAL TARGETS
-Special targets may not be included with other targets, i.e., they must be
-the only target specified.
-.Bl -tag -width Ic
-.It Ic .BEGIN
-Any command lines attached to this target are executed before anything
-else is done.
-.It Ic .DEFAULT
-This is sort of a
-.Ic .USE
-rule for any target (that was used only as a
-source) that
-.Nm
-cannot figure out any other way to create.
-Only the shell script is used.
-The
-.Ic .IMPSRC
-variable of a target that inherits
-.Ic .DEFAULT Ns 's
-commands is set
-to the target's own name.
-.It Ic .END
-Any command lines attached to this target are executed after everything
-else is done.
-.It Ic .IGNORE
-Mark each of the sources with the
-.Ic .IGNORE
-attribute.
-If no sources are specified, this is the equivalent of specifying the
-.Fl i
-option.
-.It Ic .INCLUDES
-A list of suffixes that indicate files that can be included in a source
-file.
-The suffix must have already been declared with
-.Ic .SUFFIXES ;
-any suffix so declared will have the directories on its search path (see
-.Ic .PATH )
-placed in the
-.Va .INCLUDES
-special variable, each preceded by a
-.Fl I
-flag.
-.It Ic .INTERRUPT
-If
-.Nm
-is interrupted, the commands for this target will be executed.
-.It Ic .LIBS
-This does for libraries what
-.Ic .INCLUDES
-does for include files, except that the flag used is
-.Fl L .
-.It Ic .MAIN
-If no target is specified when
-.Nm
-is invoked, this target will be built.
-This is always set, either
-explicitly, or implicitly when
-.Nm
-selects the default target, to give the user a way to refer to the default
-target on the command line.
-.It Ic .MAKEFILEDEPS
-Enable the
-.Dq Remaking Makefiles
-functionality, as explained in the
-.Sx REMAKING MAKEFILES
-section below.
-.It Ic .MAKEFLAGS
-This target provides a way to specify flags for
-.Nm
-when the makefile is used.
-The flags are as if typed to the shell, though the
-.Fl f
-option will have
-no effect.
-Flags (except for
-.Fl f )
-and variable assignments specified as the source
-for this target are also appended to the
-.Va .MAKEFLAGS
-internal variable.
-Please note the difference between this target and the
-.Va .MAKEFLAGS
-internal variable: specifying an option or variable
-assignment as the source for this target will affect
-.Em both
-the current makefile and all processes that
-.Nm
-executes.
-.It Ic .MFLAGS
-Same as above, for backward compatibility.
-.\" XXX: NOT YET!!!!
-.\" .It Ic .NOTPARALLEL
-.\" The named targets are executed in non parallel mode. If no targets are
-.\" specified, then all targets are executed in non parallel mode.
-.It Ic .NOTPARALLEL
-Disable parallel mode.
-.It Ic .NO_PARALLEL
-Same as above, for compatibility with other
-.Nm pmake
-variants.
-.It Ic .ORDER
-The named targets are made in sequence.
-.\" XXX: NOT YET!!!!
-.\" .It Ic .PARALLEL
-.\" The named targets are executed in parallel mode. If no targets are
-.\" specified, then all targets are executed in parallel mode.
-.It Ic .PATH
-The sources are directories which are to be searched for files not
-found in the current directory.
-If no sources are specified, any previously specified directories are
-deleted.
-Where possible, use of
-.Ic .PATH
-is preferred over use of the
-.Va VPATH
-variable.
-.It Ic .PATH\fIsuffix\fR
-The sources are directories which are to be searched for suffixed files
-not found in the current directory.
-The
-.Nm
-utility
-first searches the suffixed search path, before reverting to the default
-path if the file is not found there.
-This form is required for
-.Ic .LIBS
-and
-.Ic .INCLUDES
-to work.
-.It Ic .PHONY
-Apply the
-.Ic .PHONY
-attribute to any specified sources.
-Targets with this attribute are always
-considered to be out of date.
-.It Ic .POSIX
-Adjust
-.Nm Ap s
-behavior to match the applicable
-.Tn POSIX
-specifications.
-(Note this disables the
-.Dq Remaking Makefiles
-feature.)
-.It Ic .PRECIOUS
-Apply the
-.Ic .PRECIOUS
-attribute to any specified sources.
-If no sources are specified, the
-.Ic .PRECIOUS
-attribute is applied to every
-target in the file.
-.It Ic .SHELL
-Select another shell.
-The sources of this target have the format
-.Ar key Ns = Ns Ar value .
-The
-.Ar key
-is one of:
-.Bl -tag -width ".Va hasErrCtl"
-.It Va path
-Specify the path to the new shell.
-.It Va name
-Specify the name of the new shell.
-This may be either one of the three builtin shells (see below) or any
-other name.
-.It Va quiet
-Specify the shell command to turn echoing off.
-.It Va echo
-Specify the shell command to turn echoing on.
-.It Va filter
-Usually shells print the echo off command before turning echoing off.
-This is the exact string that will be printed by the shell and is used
-to filter the shell output to remove the echo off command.
-.It Va echoFlag
-The shell option that turns echoing on.
-.It Va errFlag
-The shell option to turn on error checking.
-If error checking is on, the shell should exit if a command returns
-a non-zero status.
-.It Va hasErrCtl
-True if the shell has error control.
-.It Va check
-If
-.Va hasErrCtl
-is true then this is the shell command to turn error checking on.
-If
-.Va hasErrCtl
-is false then this is a command template to echo commands for which error
-checking is disabled.
-The template must contain a
-.Ql %s .
-.It Va ignore
-If
-.Va hasErrCtl
-is true, this is the shell command to turn error checking off.
-If
-.Va hasErrCtl
-is false, this is a command template to execute a command so that errors
-are ignored.
-The template must contain a
-.Ql %s .
-.It Va meta
-This is a string of meta characters of the shell.
-.It Va builtins
-This is a string holding all the shell's builtin commands separated by blanks.
-The
-.Va meta
-and
-.Va builtins
-strings are used in compat mode.
-When a command line contains neither a meta
-character nor starts with a shell builtin, it is executed directly without
-invoking a shell.
-When one of these strings (or both) is empty all commands are executed
-through a shell.
-.It Va unsetenv
-If true, remove the
-.Ev ENV
-environment variable before executing any command.
-This is useful for the Korn-shell
-.Pq Nm ksh .
-.El
-.Pp
-Values that are strings must be surrounded by double quotes.
-Boolean values are specified as
-.Ql T
-or
-.Ql Y
-(in either case) to mean true.
-Any other value is taken to mean false.
-.Pp
-There are several uses of the
-.Ic .SHELL
-target:
-.Bl -bullet
-.It
-Selecting one of the builtin shells.
-This is done by just specifying the name of the shell with the
-.Va name
-keyword.
-It is also possible to modify the parameters of the builtin shell by just
-specifying other keywords (except for
-.Va path ) .
-.It
-Using another executable for one of the builtin shells.
-This is done by specifying the path to the executable with the
-.Va path
-keyword.
-If the last component is the same as the name of the builtin shell, no
-name needs to be specified; if it is different, the name must be given:
-.Bd -literal -offset indent
-\&.SHELL: path="/usr/local/bin/sh"
-.Ed
-.Pp
-selects the builtin shell
-.Dq Li sh
-but will execute it from
-.Pa /usr/local/bin/sh .
-Like in the previous case, it is possible to modify parameters of the builtin
-shell by just specifying them.
-.It
-Using an entirely different shell.
-This is done by specifying all keywords.
-.El
-.Pp
-The builtin shells are
-.Dq Li sh ,
-.Dq Li csh
-and
-.Dq Li ksh .
-Because
-.Fx
-has no
-.Nm ksh
-in
-.Pa /bin ,
-it is unwise to specify
-.Va name Ns = Ns Qq Li ksh
-without also specifying a path.
-.It Ic .SILENT
-Apply the
-.Ic .SILENT
-attribute to any specified sources.
-If no sources are specified, the
-.Ic .SILENT
-attribute is applied to every
-command in the file.
-.It Ic .SUFFIXES
-Each source specifies a suffix to
-.Nm .
-If no sources are specified, any previous specified suffixes are deleted.
-.It Ic .WARN
-Each source specifies a warning flag as previously described for the
-.Fl x
-command line option.
-Warning flags specified on the command line take precedence over flags
-specified in the makefile.
-Also, command line warning flags are pushed to sub-makes through the
-.Ev MAKEFLAGS
-environment variables so that a warning flag specified on the command
-line will influence all sub-makes.
-Several flags can be specified on a single
-.Ic .WARN
-target by separating them with blanks.
-.El
-.Sh REMAKING MAKEFILES
-If the special target
-.Ic .MAKEFILEDEPS
-exists in the Makefile,
-.Nm
-enables the
-.Dq Remaking Makefiles
-feature.
-After reading Makefile and all the files that are included using
-.Ic .include
-or
-.Ic .sinclude
-directives (source Makefiles)
-.Nm
-considers each source Makefile as a target and tries to rebuild it.
-Both explicit and implicit rules are checked and all source Makefiles
-are updated if necessary. If any of the source Makefiles were rebuilt,
-.Nm
-restarts from clean state.
-.Pp
-To prevent infinite loops the following source Makefile targets are ignored:
-.Bl -bullet
-.It
-.Ic ::
-targets that have no prerequisites
-.It
-.Ic \&!
-targets
-.It
-targets that have
-.Ic .PHONY
-or
-.Ic .EXEC
-attributes
-.It
-targets without prerequisites and without commands
-.El
-.Pp
-When remaking a source Makefile options
-.Ic -t
-(touch target),
-.Ic -q
-(query mode), and
-.Ic -n
-(no exec) do not take effect, unless source Makefile is specified
-explicitly as a target in
-.Nm
-command line.
-.Pp
-Additionally, system makefiles and
-.Ic .depend
-are not considered as Makefiles that can be rebuilt.
-.Sh ENVIRONMENT
-The
-.Nm
-utility uses the following environment variables, if they exist:
-.Ev MACHINE ,
-.Ev MAKE ,
-.Ev MAKEFLAGS ,
-.Ev MAKEOBJDIR ,
-.Ev MAKEOBJDIRPREFIX ,
-and
-.Ev MAKESYSPATH .
-.Sh FILES
-.Bl -tag -width /usr/share/doc/psd/12.make -compact
-.It Pa .depend
-list of dependencies
-.It Pa Makefile
-list of dependencies
-.It Pa makefile
-list of dependencies
-.It Pa obj
-object directory
-.It Pa sys.mk
-system makefile
-.It Pa /usr/share/mk
-default system makefile directory
-.It Pa /usr/share/doc/psd/12.make
-PMake tutorial
-.It Pa /usr/obj
-default
-.Ev MAKEOBJDIRPREFIX
-directory.
-.It Pa /etc/make.conf
-default path to
-.Xr make.conf 5
-.El
-.Sh EXAMPLES
-List all included makefiles in order visited:
-.Pp
-.Dl "make -V .MAKEFILE_LIST | tr \e\ \e\en"
-.Sh COMPATIBILITY
-Older versions of
-.Nm
-used
-.Ev MAKE
-instead of
-.Ev MAKEFLAGS .
-This was removed for
-.Tn POSIX
-compatibility.
-The internal variable
-.Va MAKE
-is set to the same value as
-.Va .MAKE ;
-support for this may be removed in the future.
-.Pp
-The use of the
-.Cm :L
-and
-.Cm :U
-modifiers are deprecated
-in
-.Fx 10.0
-and the more portable (among Pmake decedents)
-.Cm :tl
-and
-.Cm :tu
-should be used instead.
-.Pp
-Most of the more esoteric features of
-.Nm
-should probably be avoided for greater compatibility.
-.Sh SEE ALSO
-.Xr mkdep 1 ,
-.Xr make.conf 5
-.Rs
-.%T "PMake - A Tutorial"
-.Re
-in
-.Pa /usr/share/doc/psd/12.make
-.Sh HISTORY
-A
-.Nm
-command appeared in PWB UNIX.
-.Sh BUGS
-The determination of
-.Va .OBJDIR
-is contorted to the point of absurdity.
-.Pp
-In the presence of several
-.Ic .MAIN
-special targets,
-.Nm
-silently ignores all but the first.
-.Pp
-.Va .TARGETS
-is not set to the default target when
-.Nm
-is invoked without a target name and no
-.Ic .MAIN
-special target exists.
-.Pp
-The evaluation of
-.Ar expression
-in a test is very simple-minded.
-Currently, the only form that works is
-.Ql .if ${VAR} op something .
-For instance, you should write tests as
-.Ql .if ${VAR} == "string"
-not the other way around, which would give you an error.
-.Pp
-For loops are expanded before tests, so a fragment such as:
-.Bd -literal -offset indent
-\&.for ARCH in ${SHARED_ARCHS}
-\&.if ${ARCH} == ${MACHINE}
- ...
-\&.endif
-\&.endfor
-.Ed
-.Pp
-will not work, and should be rewritten as:
-.Bd -literal -offset indent
-\&.for ARCH in ${SHARED_ARCHS}
-\&.if ${MACHINE} == ${ARCH}
- ...
-\&.endif
-\&.endfor
-.Ed
-.Pp
-The parsing code is broken with respect to handling a semicolon
-after a colon, so a fragment like this will fail:
-.Bd -literal -offset indent
-HDRS= foo.h bar.h
-
-all:
-\&.for h in ${HDRS:S;^;${.CURDIR}/;}
- ...
-\&.endfor
-.Ed
-.Pp
-A trailing backslash in a variable value defined on the command line causes
-the delimiting space in the
-.Ev MAKEFLAGS
-environment variable to be preceded by that backslash.
-That causes a submake to not treat that space as a word delimiter.
-Fixing this requires a larger rewrite of the code handling command line
-macros and assignments to
-.Va .MAKEFLAGS .
diff --git a/usr.bin/make/make.c b/usr.bin/make/make.c
deleted file mode 100644
index 3e6cb42..0000000
--- a/usr.bin/make/make.c
+++ /dev/null
@@ -1,819 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)make.c 8.1 (Berkeley) 6/6/93
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*
- * make.c
- * The functions which perform the examination of targets and
- * their suitability for creation
- *
- * Interface:
- * Make_Run Initialize things for the module and recreate
- * whatever needs recreating. Returns TRUE if
- * work was (or would have been) done and FALSE
- * otherwise.
- *
- * Make_Update Update all parents of a given child. Performs
- * various bookkeeping chores like the updating
- * of the cmtime field of the parent, filling
- * of the IMPSRC context variable, etc. It will
- * place the parent on the toBeMade queue if it should be.
- *
- * Make_TimeStamp Function to set the parent's cmtime field
- * based on a child's modification time.
- *
- * Make_DoAllVar Set up the various local variables for a
- * target, including the .ALLSRC variable, making
- * sure that any variable that needs to exist
- * at the very least has the empty value.
- *
- * Make_OODate Determine if a target is out-of-date.
- *
- * Make_HandleUse See if a child is a .USE node for a parent
- * and perform the .USE actions if so.
- */
-
-#include "arch.h"
-#include "config.h"
-#include "dir.h"
-#include "globals.h"
-#include "GNode.h"
-#include "job.h"
-#include "make.h"
-#include "parse.h"
-#include "suff.h"
-#include "targ.h"
-#include "util.h"
-#include "var.h"
-
-/* The current fringe of the graph. These are nodes which await examination
- * by MakeOODate. It is added to by Make_Update and subtracted from by
- * MakeStartJobs */
-static Lst toBeMade = Lst_Initializer(toBeMade);
-
-/*
- * Number of nodes to be processed. If this is non-zero when Job_Empty()
- * returns TRUE, there's a cycle in the graph.
- */
-static int numNodes;
-
-static Boolean MakeStartJobs(void);
-
-/**
- * Make_TimeStamp
- * Set the cmtime field of a parent node based on the mtime stamp in its
- * child. Called from MakeOODate via LST_FOREACH.
- *
- * Results:
- * Always returns 0.
- *
- * Side Effects:
- * The cmtime of the parent node will be changed if the mtime
- * field of the child is greater than it.
- */
-int
-Make_TimeStamp(GNode *pgn, GNode *cgn)
-{
-
- if (cgn->mtime > pgn->cmtime) {
- pgn->cmtime = cgn->mtime;
- pgn->cmtime_gn = cgn;
- }
- return (0);
-}
-
-/**
- * Make_OODate
- * See if a given node is out of date with respect to its sources.
- * Used by Make_Run when deciding which nodes to place on the
- * toBeMade queue initially and by Make_Update to screen out USE and
- * EXEC nodes. In the latter case, however, any other sort of node
- * must be considered out-of-date since at least one of its children
- * will have been recreated.
- *
- * Results:
- * TRUE if the node is out of date. FALSE otherwise.
- *
- * Side Effects:
- * The mtime field of the node and the cmtime field of its parents
- * will/may be changed.
- */
-Boolean
-Make_OODate(GNode *gn)
-{
- Boolean oodate;
- LstNode *ln;
-
- /*
- * Certain types of targets needn't even be sought as their datedness
- * doesn't depend on their modification time...
- */
- if ((gn->type & (OP_JOIN | OP_USE | OP_EXEC)) == 0) {
- Dir_MTime(gn);
- if (gn->mtime != 0) {
- DEBUGF(MAKE, ("modified %s...",
- Targ_FmtTime(gn->mtime)));
- } else {
- DEBUGF(MAKE, ("non-existent..."));
- }
- }
-
- /*
- * A target is remade in one of the following circumstances:
- * its modification time is smaller than that of its youngest child
- * and it would actually be run (has commands or type OP_NOP)
- * it's the object of a force operator
- * it has no children, was on the lhs of an operator and doesn't
- * exist already.
- *
- * Libraries are only considered out-of-date if the archive module says
- * they are.
- *
- * These weird rules are brought to you by Backward-Compatibility and
- * the strange people who wrote 'Make'.
- */
- if (gn->type & OP_USE) {
- /*
- * If the node is a USE node it is *never* out of date
- * no matter *what*.
- */
- DEBUGF(MAKE, (".USE node..."));
- oodate = FALSE;
-
- } else if (gn->type & OP_LIB) {
- DEBUGF(MAKE, ("library..."));
-
- /*
- * always out of date if no children and :: target
- */
- oodate = Arch_LibOODate(gn) ||
- ((gn->cmtime == 0) && (gn->type & OP_DOUBLEDEP));
-
- } else if (gn->type & OP_JOIN) {
- /*
- * A target with the .JOIN attribute is only considered
- * out-of-date if any of its children was out-of-date.
- */
- DEBUGF(MAKE, (".JOIN node..."));
- oodate = gn->childMade;
-
- } else if (gn->type & (OP_FORCE|OP_EXEC|OP_PHONY)) {
- /*
- * A node which is the object of the force (!) operator or
- * which has the .EXEC attribute is always considered
- * out-of-date.
- */
- if (gn->type & OP_FORCE) {
- DEBUGF(MAKE, ("! operator..."));
- } else if (gn->type & OP_PHONY) {
- DEBUGF(MAKE, (".PHONY node..."));
- } else {
- DEBUGF(MAKE, (".EXEC node..."));
- }
-
- if (remakingMakefiles) {
- DEBUGF(MAKE, ("skipping (remaking makefiles)..."));
- oodate = FALSE;
- } else {
- oodate = TRUE;
- }
- } else if (gn->mtime < gn->cmtime ||
- (gn->cmtime == 0 && (gn->mtime == 0 || (gn->type & OP_DOUBLEDEP)))) {
- /*
- * A node whose modification time is less than that of its
- * youngest child or that has no children (cmtime == 0) and
- * either doesn't exist (mtime == 0) or was the object of a
- * :: operator is out-of-date. Why? Because that's the way
- * Make does it.
- */
- if (gn->mtime < gn->cmtime) {
- DEBUGF(MAKE, ("modified before source (%s)...",
- gn->cmtime_gn ? gn->cmtime_gn->path : "???"));
- oodate = TRUE;
- } else if (gn->mtime == 0) {
- DEBUGF(MAKE, ("non-existent and no sources..."));
- if (remakingMakefiles && Lst_IsEmpty(&gn->commands)) {
- DEBUGF(MAKE, ("skipping (no commands and remaking makefiles)..."));
- oodate = FALSE;
- } else {
- oodate = TRUE;
- }
- } else {
- DEBUGF(MAKE, (":: operator and no sources..."));
- if (remakingMakefiles) {
- DEBUGF(MAKE, ("skipping (remaking makefiles)..."));
- oodate = FALSE;
- } else {
- oodate = TRUE;
- }
- }
- } else
- oodate = FALSE;
-
- /*
- * If the target isn't out-of-date, the parents need to know its
- * modification time. Note that targets that appear to be out-of-date
- * but aren't, because they have no commands and aren't of type OP_NOP,
- * have their mtime stay below their children's mtime to keep parents
- * from thinking they're out-of-date.
- */
- if (!oodate) {
- LST_FOREACH(ln, &gn->parents)
- if (Make_TimeStamp(Lst_Datum(ln), gn))
- break;
- }
-
- return (oodate);
-}
-
-/**
- * Make_HandleUse
- * Function called by Make_Run and SuffApplyTransform on the downward
- * pass to handle .USE and transformation nodes. A callback function
- * for LST_FOREACH, it implements the .USE and transformation
- * functionality by copying the node's commands, type flags
- * and children to the parent node. Should be called before the
- * children are enqueued to be looked at.
- *
- * A .USE node is much like an explicit transformation rule, except
- * its commands are always added to the target node, even if the
- * target already has commands.
- *
- * Results:
- * returns 0.
- *
- * Side Effects:
- * Children and commands may be added to the parent and the parent's
- * type may be changed.
- *
- *-----------------------------------------------------------------------
- */
-int
-Make_HandleUse(GNode *cgn, GNode *pgn)
-{
- GNode *gn; /* A child of the .USE node */
- LstNode *ln; /* An element in the children list */
-
- if (cgn->type & (OP_USE | OP_TRANSFORM)) {
- if ((cgn->type & OP_USE) || Lst_IsEmpty(&pgn->commands)) {
- /*
- * .USE or transformation and target has no commands --
- * append the child's commands to the parent.
- */
- Lst_Concat(&pgn->commands, &cgn->commands, LST_CONCNEW);
- }
-
- for (ln = Lst_First(&cgn->children); ln != NULL;
- ln = Lst_Succ(ln)) {
- gn = Lst_Datum(ln);
-
- if (Lst_Member(&pgn->children, gn) == NULL) {
- Lst_AtEnd(&pgn->children, gn);
- Lst_AtEnd(&gn->parents, pgn);
- pgn->unmade += 1;
- }
- }
-
- pgn->type |= cgn->type & ~(OP_OPMASK | OP_USE | OP_TRANSFORM);
-
- /*
- * This child node is now "made", so we decrement the count of
- * unmade children in the parent... We also remove the child
- * from the parent's list to accurately reflect the number of
- * decent children the parent has. This is used by Make_Run to
- * decide whether to queue the parent or examine its children...
- */
- if (cgn->type & OP_USE) {
- pgn->unmade--;
- }
- }
- return (0);
-}
-
-/**
- * Make_Update
- * Perform update on the parents of a node. Used by JobFinish once
- * a node has been dealt with and by MakeStartJobs if it finds an
- * up-to-date node.
- *
- * Results:
- * Always returns 0
- *
- * Side Effects:
- * The unmade field of pgn is decremented and pgn may be placed on
- * the toBeMade queue if this field becomes 0.
- *
- * If the child was made, the parent's childMade field will be set true
- * and its cmtime set to now.
- *
- * If the child wasn't made, the cmtime field of the parent will be
- * altered if the child's mtime is big enough.
- *
- * Finally, if the child is the implied source for the parent, the
- * parent's IMPSRC variable is set appropriately.
- */
-void
-Make_Update(GNode *cgn)
-{
- GNode *pgn; /* the parent node */
- const char *cname; /* the child's name */
- LstNode *ln; /* Element in parents and iParents lists */
- const char *cpref;
-
- cname = Var_Value(TARGET, cgn);
-
- /*
- * If the child was actually made, see what its modification time is
- * now -- some rules won't actually update the file. If the file still
- * doesn't exist, make its mtime now.
- */
- if (cgn->made != UPTODATE) {
-#ifndef RECHECK
- /*
- * We can't re-stat the thing, but we can at least take care
- * of rules where a target depends on a source that actually
- * creates the target, but only if it has changed, e.g.
- *
- * parse.h : parse.o
- *
- * parse.o : parse.y
- * yacc -d parse.y
- * cc -c y.tab.c
- * mv y.tab.o parse.o
- * cmp -s y.tab.h parse.h || mv y.tab.h parse.h
- *
- * In this case, if the definitions produced by yacc haven't
- * changed from before, parse.h won't have been updated and
- * cgn->mtime will reflect the current modification time for
- * parse.h. This is something of a kludge, I admit, but it's a
- * useful one..
- * XXX: People like to use a rule like
- *
- * FRC:
- *
- * To force things that depend on FRC to be made, so we have to
- * check for gn->children being empty as well...
- */
- if (!Lst_IsEmpty(&cgn->commands) ||
- Lst_IsEmpty(&cgn->children)) {
- cgn->mtime = now;
- }
- #else
- /*
- * This is what Make does and it's actually a good thing, as it
- * allows rules like
- *
- * cmp -s y.tab.h parse.h || cp y.tab.h parse.h
- *
- * to function as intended. Unfortunately, thanks to the
- * stateless nature of NFS (by which I mean the loose coupling
- * of two clients using the same file from a common server),
- * there are times when the modification time of a file created
- * on a remote machine will not be modified before the local
- * stat() implied by the Dir_MTime occurs, thus leading us to
- * believe that the file is unchanged, wreaking havoc with
- * files that depend on this one.
- *
- * I have decided it is better to make too much than to make too
- * little, so this stuff is commented out unless you're sure
- * it's ok.
- * -- ardeb 1/12/88
- */
- /*
- * Christos, 4/9/92: If we are saving commands pretend that
- * the target is made now. Otherwise archives with ... rules
- * don't work!
- */
- if (noExecute || (cgn->type & OP_SAVE_CMDS) ||
- Dir_MTime(cgn) == 0) {
- cgn->mtime = now;
- }
- DEBUGF(MAKE, ("update time: %s\n", Targ_FmtTime(cgn->mtime)));
-#endif
- }
-
- for (ln = Lst_First(&cgn->parents); ln != NULL; ln = Lst_Succ(ln)) {
- pgn = Lst_Datum(ln);
- if (pgn->make) {
- pgn->unmade -= 1;
-
- if (!(cgn->type & (OP_EXEC | OP_USE))) {
- if (cgn->made == MADE)
- pgn->childMade = TRUE;
- Make_TimeStamp(pgn, cgn);
- }
- if (pgn->unmade == 0) {
- /*
- * Queue the node up -- any unmade predecessors
- * will be dealt with in MakeStartJobs.
- */
- Lst_EnQueue(&toBeMade, pgn);
- } else if (pgn->unmade < 0) {
- Error("Graph cycles through %s", pgn->name);
- }
- }
- }
-
- /*
- * Deal with successor nodes. If any is marked for making and has an
- * unmade count of 0, has not been made and isn't in the examination
- * queue, it means we need to place it in the queue as it restrained
- * itself before.
- */
- for (ln = Lst_First(&cgn->successors); ln != NULL; ln = Lst_Succ(ln)) {
- GNode *succ = Lst_Datum(ln);
-
- if (succ->make && succ->unmade == 0 && succ->made == UNMADE &&
- Lst_Member(&toBeMade, succ) == NULL) {
- Lst_EnQueue(&toBeMade, succ);
- }
- }
-
- /*
- * Set the .PREFIX and .IMPSRC variables for all the implied parents
- * of this node.
- */
- cpref = Var_Value(PREFIX, cgn);
- for (ln = Lst_First(&cgn->iParents); ln != NULL; ln = Lst_Succ(ln)) {
- pgn = Lst_Datum(ln);
- if (pgn->make) {
- Var_Set(IMPSRC, cname, pgn);
- Var_Set(PREFIX, cpref, pgn);
- }
- }
-}
-
-/**
- * Make_DoAllVar
- * Set up the ALLSRC and OODATE variables. Sad to say, it must be
- * done separately, rather than while traversing the graph. This is
- * because Make defined OODATE to contain all sources whose modification
- * times were later than that of the target, *not* those sources that
- * were out-of-date. Since in both compatibility and native modes,
- * the modification time of the parent isn't found until the child
- * has been dealt with, we have to wait until now to fill in the
- * variable. As for ALLSRC, the ordering is important and not
- * guaranteed when in native mode, so it must be set here, too.
- *
- * Side Effects:
- * The ALLSRC and OODATE variables of the given node is filled in.
- * If the node is a .JOIN node, its TARGET variable will be set to
- * match its ALLSRC variable.
- */
-void
-Make_DoAllVar(GNode *gn)
-{
- LstNode *ln;
- GNode *cgn;
- const char *child;
-
- LST_FOREACH(ln, &gn->children) {
- /*
- * Add the child's name to the ALLSRC and OODATE variables of
- * the given node. The child is added only if it has not been
- * given the .EXEC, .USE or .INVISIBLE attributes. .EXEC and
- * .USE children are very rarely going to be files, so...
- *
- * A child is added to the OODATE variable if its modification
- * time is later than that of its parent, as defined by Make,
- * except if the parent is a .JOIN node. In that case, it is
- * only added to the OODATE variable if it was actually made
- * (since .JOIN nodes don't have modification times, the
- * comparison is rather unfair...).
- */
- cgn = Lst_Datum(ln);
-
- if ((cgn->type & (OP_EXEC | OP_USE | OP_INVISIBLE)) == 0) {
- if (OP_NOP(cgn->type)) {
- /*
- * this node is only source; use the specific
- * pathname for it
- */
- child = cgn->path ? cgn->path : cgn->name;
- } else
- child = Var_Value(TARGET, cgn);
- Var_Append(ALLSRC, child, gn);
- if (gn->type & OP_JOIN) {
- if (cgn->made == MADE) {
- Var_Append(OODATE, child, gn);
- }
- } else if (gn->mtime < cgn->mtime ||
- (cgn->mtime >= now && cgn->made == MADE)) {
- /*
- * It goes in the OODATE variable if the parent
- * is younger than the child or if the child has
- * been modified more recently than the start of
- * the make. This is to keep pmake from getting
- * confused if something else updates the parent
- * after the make starts (shouldn't happen, I
- * know, but sometimes it does). In such a case,
- * if we've updated the kid, the parent is
- * likely to have a modification time later than
- * that of the kid and anything that relies on
- * the OODATE variable will be hosed.
- *
- * XXX: This will cause all made children to
- * go in the OODATE variable, even if they're
- * not touched, if RECHECK isn't defined, since
- * cgn->mtime is set to now in Make_Update.
- * According to some people, this is good...
- */
- Var_Append(OODATE, child, gn);
- }
- }
- }
-
- if (!Var_Exists (OODATE, gn)) {
- Var_Set(OODATE, "", gn);
- }
- if (!Var_Exists (ALLSRC, gn)) {
- Var_Set(ALLSRC, "", gn);
- }
-
- if (gn->type & OP_JOIN) {
- Var_Set(TARGET, Var_Value(ALLSRC, gn), gn);
- }
-}
-
-/**
- * MakeStartJobs
- * Start as many jobs as possible.
- *
- * Results:
- * If the query flag was given to pmake, no job will be started,
- * but as soon as an out-of-date target is found, this function
- * returns TRUE. At all other times, this function returns FALSE.
- *
- * Side Effects:
- * Nodes are removed from the toBeMade queue and job table slots
- * are filled.
- */
-static Boolean
-MakeStartJobs(void)
-{
- GNode *gn;
-
- while (!Lst_IsEmpty(&toBeMade) && !Job_Full()) {
- gn = Lst_DeQueue(&toBeMade);
- DEBUGF(MAKE, ("Examining %s...", gn->name));
-
- /*
- * Make sure any and all predecessors that are going to be made,
- * have been.
- */
- if (!Lst_IsEmpty(&gn->preds)) {
- LstNode *ln;
-
- for (ln = Lst_First(&gn->preds); ln != NULL;
- ln = Lst_Succ(ln)){
- GNode *pgn = Lst_Datum(ln);
-
- if (pgn->make && pgn->made == UNMADE) {
- DEBUGF(MAKE, ("predecessor %s not made "
- "yet.\n", pgn->name));
- break;
- }
- }
- /*
- * If ln isn't NULL, there's a predecessor as yet
- * unmade, so we just drop this node on the floor.
- * When the node in question has been made, it will
- * notice this node as being ready to make but as yet
- * unmade and will place the node on the queue.
- */
- if (ln != NULL) {
- continue;
- }
- }
-
- numNodes--;
- if (Make_OODate(gn)) {
- DEBUGF(MAKE, ("out-of-date\n"));
- if (queryFlag) {
- return (TRUE);
- }
- Make_DoAllVar(gn);
- Job_Make(gn);
- } else {
- DEBUGF(MAKE, ("up-to-date\n"));
- gn->made = UPTODATE;
- if (gn->type & OP_JOIN) {
- /*
- * Even for an up-to-date .JOIN node, we need
- * it to have its context variables so
- * references to it get the correct value for
- * .TARGET when building up the context
- * variables of its parent(s)...
- */
- Make_DoAllVar(gn);
- }
-
- Make_Update(gn);
- }
- }
- return (FALSE);
-}
-
-/**
- * MakePrintStatus
- * Print the status of a top-level node, viz. it being up-to-date
- * already or not created due to an error in a lower level.
- * Callback function for Make_Run via LST_FOREACH. If gn->unmade is
- * nonzero and that is meant to imply a cycle in the graph, then
- * cycle is TRUE.
- *
- * Side Effects:
- * A message may be printed.
- */
-static void
-MakePrintStatus(GNode *gn, Boolean cycle)
-{
- LstNode *ln;
-
- if (gn->made == UPTODATE) {
- printf("`%s' is up to date.\n", gn->name);
-
- } else if (gn->unmade != 0) {
- if (cycle) {
- /*
- * If printing cycles and came to one that has unmade
- * children, print out the cycle by recursing on its
- * children. Note a cycle like:
- * a : b
- * b : c
- * c : b
- * will cause this to erroneously complain about a
- * being in the cycle, but this is a good approximation.
- */
- if (gn->made == CYCLE) {
- Error("Graph cycles through `%s'", gn->name);
- gn->made = ENDCYCLE;
- LST_FOREACH(ln, &gn->children)
- MakePrintStatus(Lst_Datum(ln), TRUE);
- gn->made = UNMADE;
- } else if (gn->made != ENDCYCLE) {
- gn->made = CYCLE;
- LST_FOREACH(ln, &gn->children)
- MakePrintStatus(Lst_Datum(ln), TRUE);
- }
- } else {
- printf("`%s' not remade because of errors.\n",
- gn->name);
- }
- }
-}
-
-/**
- * Make_Run
- * Initialize the nodes to remake and the list of nodes which are
- * ready to be made by doing a breadth-first traversal of the graph
- * starting from the nodes in the given list. Once this traversal
- * is finished, all the 'leaves' of the graph are in the toBeMade
- * queue.
- * Using this queue and the Job module, work back up the graph,
- * calling on MakeStartJobs to keep the job table as full as
- * possible.
- *
- * Results:
- * TRUE if work was done. FALSE otherwise.
- *
- * Side Effects:
- * The make field of all nodes involved in the creation of the given
- * targets is set to 1. The toBeMade list is set to contain all the
- * 'leaves' of these subgraphs.
- */
-Boolean
-Make_Run(Lst *targs)
-{
- GNode *gn; /* a temporary pointer */
- GNode *cgn;
- Lst examine; /* List of targets to examine */
- LstNode *ln;
-
- Lst_Init(&examine);
- Lst_Duplicate(&examine, targs, NOCOPY);
- numNodes = 0;
-
- /*
- * Make an initial downward pass over the graph, marking nodes to be
- * made as we go down. We call Suff_FindDeps to find where a node is and
- * to get some children for it if it has none and also has no commands.
- * If the node is a leaf, we stick it on the toBeMade queue to
- * be looked at in a minute, otherwise we add its children to our queue
- * and go on about our business.
- */
- while (!Lst_IsEmpty(&examine)) {
- gn = Lst_DeQueue(&examine);
-
- if (!gn->make) {
- gn->make = TRUE;
- numNodes++;
-
- /*
- * Apply any .USE rules before looking for implicit
- * dependencies to make sure everything has commands
- * that should...
- */
- LST_FOREACH(ln, &gn->children)
- if (Make_HandleUse(Lst_Datum(ln), gn))
- break;
-
- Suff_FindDeps(gn);
-
- if (gn->unmade != 0) {
- LST_FOREACH(ln, &gn->children) {
- cgn = Lst_Datum(ln);
- if (!cgn->make && !(cgn->type & OP_USE))
- Lst_EnQueue(&examine, cgn);
- }
- } else {
- Lst_EnQueue(&toBeMade, gn);
- }
- }
- }
-
- if (queryFlag) {
- /*
- * We wouldn't do any work unless we could start some jobs in
- * the next loop... (we won't actually start any, of course,
- * this is just to see if any of the targets was out of date)
- */
- return (MakeStartJobs());
-
- } else {
- /*
- * Initialization. At the moment, no jobs are running and
- * until some get started, nothing will happen since the
- * remaining upward traversal of the graph is performed by the
- * routines in job.c upon the finishing of a job. So we fill
- * the Job table as much as we can before going into our loop.
- */
- MakeStartJobs();
- }
-
- /*
- * Main Loop: The idea here is that the ending of jobs will take
- * care of the maintenance of data structures and the waiting for output
- * will cause us to be idle most of the time while our children run as
- * much as possible. Because the job table is kept as full as possible,
- * the only time when it will be empty is when all the jobs which need
- * running have been run, so that is the end condition of this loop.
- * Note that the Job module will exit if there were any errors unless
- * the keepgoing flag was given.
- */
- while (!Job_Empty()) {
- Job_CatchOutput(!Lst_IsEmpty(&toBeMade));
- Job_CatchChildren(!usePipes);
- MakeStartJobs();
- }
-
- Job_Finish();
-
- /*
- * Print the final status of each target. E.g. if it wasn't made
- * because some inferior reported an error.
- */
- LST_FOREACH(ln, targs)
- MakePrintStatus(Lst_Datum(ln), (makeErrors == 0) && (numNodes != 0));
-
- return (TRUE);
-}
diff --git a/usr.bin/make/make.h b/usr.bin/make/make.h
deleted file mode 100644
index 6e2813d..0000000
--- a/usr.bin/make/make.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)make.h 8.3 (Berkeley) 6/13/95
- * $FreeBSD$
- */
-
-#ifndef make_h_a91074b9
-#define make_h_a91074b9
-
-/**
- * make.h
- * The global definitions for make
- */
-
-#include "util.h"
-
-#define MAKE_JOB_PREFIX ".MAKE.JOB.PREFIX"
-
-struct GNode;
-struct Lst;
-struct Buffer;
-
-/*
- * Warning flags
- */
-enum {
- WARN_DIRSYNTAX = 0x0001, /* syntax errors in directives */
-};
-
-int Make_TimeStamp(struct GNode *, struct GNode *);
-Boolean Make_OODate(struct GNode *);
-int Make_HandleUse(struct GNode *, struct GNode *);
-void Make_Update(struct GNode *);
-void Make_DoAllVar(struct GNode *);
-Boolean Make_Run(struct Lst *);
-void Main_ParseArgLine(char *, int);
-int Main_ParseWarn(const char *, int);
-void Main_AddSourceMakefile(const char *);
-
-#endif /* make_h_a91074b9 */
diff --git a/usr.bin/make/parse.c b/usr.bin/make/parse.c
deleted file mode 100644
index 134d905..0000000
--- a/usr.bin/make/parse.c
+++ /dev/null
@@ -1,2545 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)parse.c 8.3 (Berkeley) 3/19/94
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*-
- * parse.c --
- * Functions to parse a makefile.
- *
- * Most important structures are kept in Lsts. Directories for
- * the #include "..." function are kept in the 'parseIncPath' Lst, while
- * those for the #include <...> are kept in the 'sysIncPath' Lst. The
- * targets currently being defined are kept in the 'targets' Lst.
- *
- * Interface:
- *
- * Parse_File Function used to parse a makefile. It must
- * be given the name of the file, which should
- * already have been opened, and a function
- * to call to read a character from the file.
- *
- * Parse_IsVar Returns TRUE if the given line is a
- * variable assignment. Used by MainParseArgs
- * to determine if an argument is a target
- * or a variable assignment. Used internally
- * for pretty much the same thing...
- *
- * Parse_Error Function called when an error occurs in
- * parsing. Used by the variable and
- * conditional modules.
- *
- * Parse_MainName Returns a Lst of the main target to create.
- */
-
-#include <assert.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdlib.h>
-#include <err.h>
-
-#include "arch.h"
-#include "buf.h"
-#include "cond.h"
-#include "config.h"
-#include "dir.h"
-#include "for.h"
-#include "globals.h"
-#include "GNode.h"
-#include "hash_tables.h"
-#include "job.h"
-#include "make.h"
-#include "parse.h"
-#include "pathnames.h"
-#include "shell.h"
-#include "str.h"
-#include "suff.h"
-#include "targ.h"
-#include "util.h"
-#include "var.h"
-
-/*
- * These values are returned by ParsePopInput to tell Parse_File whether to
- * CONTINUE parsing, i.e. it had only reached the end of an include file,
- * or if it's DONE.
- */
-#define CONTINUE 1
-#define DONE 0
-
-/* targets we're working on */
-static Lst targets = Lst_Initializer(targets);
-
-/* true if currently in a dependency line or its commands */
-static Boolean inLine;
-
-static int fatals = 0;
-
-/*
- * The main target to create. This is the first target on the
- * first dependency line in the first makefile.
- */
-static GNode *mainNode;
-
-/*
- * Definitions for handling #include specifications
- */
-struct IFile {
- char *fname; /* name of previous file */
- int lineno; /* saved line number */
- FILE *F; /* the open stream */
- char *str; /* the string when parsing a string */
- char *ptr; /* the current pointer when parsing a string */
- TAILQ_ENTRY(IFile) link;/* stack the files */
-};
-
-/* stack of IFiles generated by * #includes */
-static TAILQ_HEAD(, IFile) includes = TAILQ_HEAD_INITIALIZER(includes);
-
-/* access current file */
-#define CURFILE (TAILQ_FIRST(&includes))
-
-/* list of directories for "..." includes */
-struct Path parseIncPath = TAILQ_HEAD_INITIALIZER(parseIncPath);
-
-/* list of directories for <...> includes */
-struct Path sysIncPath = TAILQ_HEAD_INITIALIZER(sysIncPath);
-
-/*
- * specType contains the SPECial TYPE of the current target. It is
- * Not if the target is unspecial. If it *is* special, however, the children
- * are linked as children of the parent but not vice versa. This variable is
- * set in ParseDoDependency
- */
-typedef enum {
- Begin, /* .BEGIN */
- Default, /* .DEFAULT */
- End, /* .END */
- ExportVar, /* .EXPORTVAR */
- Ignore, /* .IGNORE */
- Includes, /* .INCLUDES */
- Interrupt, /* .INTERRUPT */
- Libs, /* .LIBS */
- MFlags, /* .MFLAGS or .MAKEFLAGS */
- Main, /* .MAIN and we don't have anyth. user-spec. to make */
- Not, /* Not special */
- NotParallel, /* .NOTPARALELL */
- Null, /* .NULL */
- Order, /* .ORDER */
- Parallel, /* .PARALLEL */
- ExPath, /* .PATH */
- Phony, /* .PHONY */
- Posix, /* .POSIX */
- MakefileDeps, /* .MAKEFILEDEPS */
- Precious, /* .PRECIOUS */
- ExShell, /* .SHELL */
- Silent, /* .SILENT */
- SingleShell, /* .SINGLESHELL */
- Suffixes, /* .SUFFIXES */
- Wait, /* .WAIT */
- Warn, /* .WARN */
- Attribute /* Generic attribute */
-} ParseSpecial;
-
-static ParseSpecial specType;
-static int waiting;
-
-/*
- * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER
- * seen, then set to each successive source on the line.
- */
-static GNode *predecessor;
-
-/*
- * The parseKeywords table is searched using binary search when deciding
- * if a target or source is special. The 'spec' field is the ParseSpecial
- * type of the keyword ("Not" if the keyword isn't special as a target) while
- * the 'op' field is the operator to apply to the list of targets if the
- * keyword is used as a source ("0" if the keyword isn't special as a source)
- */
-static const struct keyword {
- const char *name; /* Name of keyword */
- ParseSpecial spec; /* Type when used as a target */
- int op; /* Operator when used as a source */
-} parseKeywords[] = {
- /* KEYWORD-START-TAG */
- { ".BEGIN", Begin, 0 },
- { ".DEFAULT", Default, 0 },
- { ".END", End, 0 },
- { ".EXEC", Attribute, OP_EXEC },
- { ".EXPORTVAR", ExportVar, 0 },
- { ".IGNORE", Ignore, OP_IGNORE },
- { ".INCLUDES", Includes, 0 },
- { ".INTERRUPT", Interrupt, 0 },
- { ".INVISIBLE", Attribute, OP_INVISIBLE },
- { ".JOIN", Attribute, OP_JOIN },
- { ".LIBS", Libs, 0 },
- { ".MAIN", Main, 0 },
- { ".MAKE", Attribute, OP_MAKE },
- { ".MAKEFILEDEPS", MakefileDeps, 0 },
- { ".MAKEFLAGS", MFlags, 0 },
- { ".MFLAGS", MFlags, 0 },
- { ".NOTMAIN", Attribute, OP_NOTMAIN },
- { ".NOTPARALLEL", NotParallel, 0 },
- { ".NO_PARALLEL", NotParallel, 0 },
- { ".NULL", Null, 0 },
- { ".OPTIONAL", Attribute, OP_OPTIONAL },
- { ".ORDER", Order, 0 },
- { ".PARALLEL", Parallel, 0 },
- { ".PATH", ExPath, 0 },
- { ".PHONY", Phony, OP_PHONY },
- { ".POSIX", Posix, 0 },
- { ".PRECIOUS", Precious, OP_PRECIOUS },
- { ".RECURSIVE", Attribute, OP_MAKE },
- { ".SHELL", ExShell, 0 },
- { ".SILENT", Silent, OP_SILENT },
- { ".SINGLESHELL", SingleShell, 0 },
- { ".SUFFIXES", Suffixes, 0 },
- { ".USE", Attribute, OP_USE },
- { ".WAIT", Wait, 0 },
- { ".WARN", Warn, 0 },
- /* KEYWORD-END-TAG */
-};
-#define NKEYWORDS (sizeof(parseKeywords) / sizeof(parseKeywords[0]))
-
-static void parse_include(char *, int, int);
-static void parse_sinclude(char *, int, int);
-static void parse_message(char *, int, int);
-static void parse_undef(char *, int, int);
-static void parse_for(char *, int, int);
-static void parse_endfor(char *, int, int);
-
-static const struct directive {
- const char *name;
- int code;
- Boolean skip_flag; /* execute even when skipped */
- void (*func)(char *, int, int);
-} directives[] = {
- /* DIRECTIVES-START-TAG */
- { "elif", COND_ELIF, TRUE, Cond_If },
- { "elifdef", COND_ELIFDEF, TRUE, Cond_If },
- { "elifmake", COND_ELIFMAKE, TRUE, Cond_If },
- { "elifndef", COND_ELIFNDEF, TRUE, Cond_If },
- { "elifnmake", COND_ELIFNMAKE, TRUE, Cond_If },
- { "else", COND_ELSE, TRUE, Cond_Else },
- { "endfor", 0, FALSE, parse_endfor },
- { "endif", COND_ENDIF, TRUE, Cond_Endif },
- { "error", 1, FALSE, parse_message },
- { "for", 0, FALSE, parse_for },
- { "if", COND_IF, TRUE, Cond_If },
- { "ifdef", COND_IFDEF, TRUE, Cond_If },
- { "ifmake", COND_IFMAKE, TRUE, Cond_If },
- { "ifndef", COND_IFNDEF, TRUE, Cond_If },
- { "ifnmake", COND_IFNMAKE, TRUE, Cond_If },
- { "include", 0, FALSE, parse_include },
- { "sinclude", 0, FALSE, parse_sinclude },
- { "undef", 0, FALSE, parse_undef },
- { "warning", 0, FALSE, parse_message },
- /* DIRECTIVES-END-TAG */
-};
-#define NDIRECTS (sizeof(directives) / sizeof(directives[0]))
-
-/*-
- * ParseFindKeyword
- * Look in the table of keywords for one matching the given string.
- *
- * Results:
- * The pointer to keyword table entry or NULL.
- */
-static const struct keyword *
-ParseFindKeyword(const char *str)
-{
- int kw;
-
- kw = keyword_hash(str, strlen(str));
- if (kw < 0 || kw >= (int)NKEYWORDS ||
- strcmp(str, parseKeywords[kw].name) != 0)
- return (NULL);
- return (&parseKeywords[kw]);
-}
-
-/*-
- * Parse_Error --
- * Error message abort function for parsing. Prints out the context
- * of the error (line number and file) as well as the message with
- * two optional arguments.
- *
- * Results:
- * None
- *
- * Side Effects:
- * "fatals" is incremented if the level is PARSE_FATAL.
- */
-/* VARARGS */
-void
-Parse_Error(int type, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- if (CURFILE != NULL)
- fprintf(stderr, "\"%s\", line %d: ",
- CURFILE->fname, CURFILE->lineno);
- if (type == PARSE_WARNING)
- fprintf(stderr, "warning: ");
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, "\n");
- fflush(stderr);
- if (type == PARSE_FATAL)
- fatals += 1;
-}
-
-/**
- * ParsePushInput
- *
- * Push a new input source onto the input stack. If ptr is NULL
- * the fullname is used to fopen the file. If it is not NULL,
- * ptr is assumed to point to the string to be parsed. If opening the
- * file fails, the fullname is freed.
- */
-static void
-ParsePushInput(char *fullname, FILE *fp, char *ptr, int lineno)
-{
- struct IFile *nf;
-
- nf = emalloc(sizeof(*nf));
- nf->fname = fullname;
- nf->lineno = lineno;
-
- if (ptr == NULL) {
- /* the input source is a file */
- if ((nf->F = fp) == NULL) {
- nf->F = fopen(fullname, "r");
- if (nf->F == NULL) {
- Parse_Error(PARSE_FATAL, "Cannot open %s",
- fullname);
- free(fullname);
- free(nf);
- return;
- }
- }
- nf->str = nf->ptr = NULL;
- Var_Append(".MAKEFILE_LIST", fullname, VAR_GLOBAL);
- } else {
- nf->str = nf->ptr = ptr;
- nf->F = NULL;
- }
- TAILQ_INSERT_HEAD(&includes, nf, link);
-}
-
-/**
- * ParsePopInput
- * Called when EOF is reached in the current file. If we were reading
- * an include file, the includes stack is popped and things set up
- * to go back to reading the previous file at the previous location.
- *
- * Results:
- * CONTINUE if there's more to do. DONE if not.
- *
- * Side Effects:
- * The old curFile.F is closed. The includes list is shortened.
- * curFile.lineno, curFile.F, and curFile.fname are changed if
- * CONTINUE is returned.
- */
-static int
-ParsePopInput(void)
-{
- struct IFile *ifile; /* the state on the top of the includes stack */
-
- assert(!TAILQ_EMPTY(&includes));
-
- ifile = TAILQ_FIRST(&includes);
- TAILQ_REMOVE(&includes, ifile, link);
-
- free(ifile->fname);
- if (ifile->F != NULL) {
- fclose(ifile->F);
- Var_Append(".MAKEFILE_LIST", "..", VAR_GLOBAL);
- }
- if (ifile->str != NULL) {
- free(ifile->str);
- }
- free(ifile);
-
- return (TAILQ_EMPTY(&includes) ? DONE : CONTINUE);
-}
-
-/**
- * parse_warn
- * Parse the .WARN pseudo-target.
- */
-static void
-parse_warn(char *line)
-{
- ArgArray aa;
- int i;
-
- brk_string(&aa, line, TRUE);
-
- for (i = 1; i < aa.argc; i++)
- Main_ParseWarn(aa.argv[i], 0);
-}
-
-/*-
- *---------------------------------------------------------------------
- * ParseLinkSrc --
- * Link the parent nodes to their new child. Used by
- * ParseDoDependency. If the specType isn't 'Not', the parent
- * isn't linked as a parent of the child.
- *
- * Side Effects:
- * New elements are added to the parents lists of cgn and the
- * children list of cgn. the unmade field of pgn is updated
- * to reflect the additional child.
- *---------------------------------------------------------------------
- */
-static void
-ParseLinkSrc(Lst *parents, GNode *cgn)
-{
- LstNode *ln;
- GNode *pgn;
-
- LST_FOREACH(ln, parents) {
- pgn = Lst_Datum(ln);
- if (Lst_Member(&pgn->children, cgn) == NULL) {
- Lst_AtEnd(&pgn->children, cgn);
- if (specType == Not) {
- Lst_AtEnd(&cgn->parents, pgn);
- }
- pgn->unmade += 1;
- }
- }
-}
-
-/*-
- *---------------------------------------------------------------------
- * ParseDoOp --
- * Apply the parsed operator to all target nodes. Used in
- * ParseDoDependency once all targets have been found and their
- * operator parsed. If the previous and new operators are incompatible,
- * a major error is taken.
- *
- * Side Effects:
- * The type field of the node is altered to reflect any new bits in
- * the op.
- *---------------------------------------------------------------------
- */
-static void
-ParseDoOp(int op)
-{
- GNode *cohort;
- LstNode *ln;
- GNode *gn;
-
- LST_FOREACH(ln, &targets) {
- gn = Lst_Datum(ln);
-
- /*
- * If the dependency mask of the operator and the node don't
- * match and the node has actually had an operator applied to
- * it before, and the operator actually has some dependency
- * information in it, complain.
- */
- if ((op & OP_OPMASK) != (gn->type & OP_OPMASK) &&
- !OP_NOP(gn->type) && !OP_NOP(op)) {
- Parse_Error(PARSE_FATAL, "Inconsistent operator for %s",
- gn->name);
- return;
- }
-
- if (op == OP_DOUBLEDEP &&
- (gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
- /*
- * If the node was the object of a :: operator, we need
- * to create a new instance of it for the children and
- * commands on this dependency line. The new instance
- * is placed on the 'cohorts' list of the initial one
- * (note the initial one is not on its own cohorts list)
- * and the new instance is linked to all parents of the
- * initial instance.
- */
- cohort = Targ_NewGN(gn->name);
-
- /*
- * Duplicate links to parents so graph traversal is
- * simple. Perhaps some type bits should be duplicated?
- *
- * Make the cohort invisible as well to avoid
- * duplicating it into other variables. True, parents
- * of this target won't tend to do anything with their
- * local variables, but better safe than sorry.
- */
- ParseLinkSrc(&gn->parents, cohort);
- cohort->type = OP_DOUBLEDEP|OP_INVISIBLE;
- Lst_AtEnd(&gn->cohorts, cohort);
-
- /*
- * Replace the node in the targets list with the
- * new copy
- */
- Lst_Replace(ln, cohort);
- gn = cohort;
- }
- /*
- * We don't want to nuke any previous flags (whatever they were)
- * so we just OR the new operator into the old
- */
- gn->type |= op;
- }
-}
-
-/*-
- *---------------------------------------------------------------------
- * ParseDoSrc --
- * Given the name of a source, figure out if it is an attribute
- * and apply it to the targets if it is. Else decide if there is
- * some attribute which should be applied *to* the source because
- * of some special target and apply it if so. Otherwise, make the
- * source be a child of the targets in the list 'targets'
- *
- * Results:
- * None
- *
- * Side Effects:
- * Operator bits may be added to the list of targets or to the source.
- * The targets may have a new source added to their lists of children.
- *---------------------------------------------------------------------
- */
-static void
-ParseDoSrc(int tOp, char *src, Lst *allsrc)
-{
- GNode *gn = NULL;
- const struct keyword *kw;
-
- if (src[0] == '.' && isupper ((unsigned char)src[1])) {
- if ((kw = ParseFindKeyword(src)) != NULL) {
- if (kw->op != 0) {
- ParseDoOp(kw->op);
- return;
- }
- if (kw->spec == Wait) {
- waiting++;
- return;
- }
- }
- }
-
- switch (specType) {
- case Main:
- /*
- * If we have noted the existence of a .MAIN, it means we need
- * to add the sources of said target to the list of things
- * to create. The string 'src' is likely to be free, so we
- * must make a new copy of it. Note that this will only be
- * invoked if the user didn't specify a target on the command
- * line. This is to allow #ifmake's to succeed, or something...
- */
- Lst_AtEnd(&create, estrdup(src));
- /*
- * Add the name to the .TARGETS variable as well, so the user
- * can employ that, if desired.
- */
- Var_Append(".TARGETS", src, VAR_GLOBAL);
- return;
-
- case Order:
- /*
- * Create proper predecessor/successor links between the
- * previous source and the current one.
- */
- gn = Targ_FindNode(src, TARG_CREATE);
- if (predecessor != NULL) {
- Lst_AtEnd(&predecessor->successors, gn);
- Lst_AtEnd(&gn->preds, predecessor);
- }
- /*
- * The current source now becomes the predecessor for the next
- * one.
- */
- predecessor = gn;
- break;
-
- default:
- /*
- * If the source is not an attribute, we need to find/create
- * a node for it. After that we can apply any operator to it
- * from a special target or link it to its parents, as
- * appropriate.
- *
- * In the case of a source that was the object of a :: operator,
- * the attribute is applied to all of its instances (as kept in
- * the 'cohorts' list of the node) or all the cohorts are linked
- * to all the targets.
- */
- gn = Targ_FindNode(src, TARG_CREATE);
- if (tOp) {
- gn->type |= tOp;
- } else {
- ParseLinkSrc(&targets, gn);
- }
- if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
- GNode *cohort;
- LstNode *ln;
-
- for (ln = Lst_First(&gn->cohorts); ln != NULL;
- ln = Lst_Succ(ln)) {
- cohort = Lst_Datum(ln);
- if (tOp) {
- cohort->type |= tOp;
- } else {
- ParseLinkSrc(&targets, cohort);
- }
- }
- }
- break;
- }
-
- gn->order = waiting;
- Lst_AtEnd(allsrc, gn);
- if (waiting) {
- LstNode *ln;
- GNode *p;
-
- /*
- * Check if GNodes needs to be synchronized.
- * This has to be when two nodes are on different sides of a
- * .WAIT directive.
- */
- LST_FOREACH(ln, allsrc) {
- p = Lst_Datum(ln);
-
- if (p->order >= gn->order)
- break;
- /*
- * XXX: This can cause loops, and loops can cause
- * unmade targets, but checking is tedious, and the
- * debugging output can show the problem
- */
- Lst_AtEnd(&p->successors, gn);
- Lst_AtEnd(&gn->preds, p);
- }
- }
-}
-
-
-/*-
- *---------------------------------------------------------------------
- * ParseDoDependency --
- * Parse the dependency line in line.
- *
- * Results:
- * None
- *
- * Side Effects:
- * The nodes of the sources are linked as children to the nodes of the
- * targets. Some nodes may be created.
- *
- * We parse a dependency line by first extracting words from the line and
- * finding nodes in the list of all targets with that name. This is done
- * until a character is encountered which is an operator character. Currently
- * these are only ! and :. At this point the operator is parsed and the
- * pointer into the line advanced until the first source is encountered.
- * The parsed operator is applied to each node in the 'targets' list,
- * which is where the nodes found for the targets are kept, by means of
- * the ParseDoOp function.
- * The sources are read in much the same way as the targets were except
- * that now they are expanded using the wildcarding scheme of the C-Shell
- * and all instances of the resulting words in the list of all targets
- * are found. Each of the resulting nodes is then linked to each of the
- * targets as one of its children.
- * Certain targets are handled specially. These are the ones detailed
- * by the specType variable.
- * The storing of transformation rules is also taken care of here.
- * A target is recognized as a transformation rule by calling
- * Suff_IsTransform. If it is a transformation rule, its node is gotten
- * from the suffix module via Suff_AddTransform rather than the standard
- * Targ_FindNode in the target module.
- *---------------------------------------------------------------------
- */
-static void
-ParseDoDependency(char *line)
-{
- char *cp; /* our current position */
- char *lstart = line; /* original input line */
- GNode *gn; /* a general purpose temporary node */
- int op; /* the operator on the line */
- char savec; /* a place to save a character */
- Lst paths; /* Search paths to alter when parsing .PATH targets */
- int tOp; /* operator from special target */
- LstNode *ln;
- const struct keyword *kw;
-
- tOp = 0;
-
- specType = Not;
- waiting = 0;
- Lst_Init(&paths);
-
- do {
- for (cp = line;
- *cp && !isspace((unsigned char)*cp) && *cp != '(';
- cp++) {
- if (*cp == '$') {
- /*
- * Must be a dynamic source (would have been
- * expanded otherwise), so call the Var module
- * to parse the puppy so we can safely advance
- * beyond it...There should be no errors in this
- * as they would have been discovered in the
- * initial Var_Subst and we wouldn't be here.
- */
- size_t length = 0;
- Boolean freeIt;
- char *result;
-
- result = Var_Parse(cp, VAR_CMD, TRUE,
- &length, &freeIt);
-
- if (freeIt) {
- free(result);
- }
- cp += length - 1;
-
- } else if (*cp == '!' || *cp == ':') {
- /*
- * We don't want to end a word on ':' or '!' if
- * there is a better match later on in the
- * string (greedy matching).
- * This allows the user to have targets like:
- * fie::fi:fo: fum
- * foo::bar:
- * where "fie::fi:fo" and "foo::bar" are the
- * targets. In real life this is used for perl5
- * library man pages where "::" separates an
- * object from its class. Ie:
- * "File::Spec::Unix". This behaviour is also
- * consistent with other versions of make.
- */
- char *p = cp + 1;
-
- if (*cp == ':' && *p == ':')
- p++;
-
- /* Found the best match already. */
- if (*p == '\0' || isspace(*p))
- break;
-
- p += strcspn(p, "!:");
-
- /* No better match later on... */
- if (*p == '\0')
- break;
- }
- continue;
- }
- if (*cp == '(') {
- /*
- * Archives must be handled specially to make sure the
- * OP_ARCHV flag is set in their 'type' field, for one
- * thing, and because things like "archive(file1.o
- * file2.o file3.o)" are permissible. Arch_ParseArchive
- * will set 'line' to be the first non-blank after the
- * archive-spec. It creates/finds nodes for the members
- * and places them on the given list, returning TRUE
- * if all went well and FALSE if there was an error in
- * the specification. On error, line should remain
- * untouched.
- */
- if (!Arch_ParseArchive(&line, &targets, VAR_CMD)) {
- Parse_Error(PARSE_FATAL,
- "Error in archive specification: \"%s\"",
- line);
- return;
- } else {
- cp = line;
- continue;
- }
- }
- savec = *cp;
-
- if (!*cp) {
- /*
- * Ending a dependency line without an operator is a * Bozo no-no. As a heuristic, this is also often
- * triggered by undetected conflicts from cvs/rcs
- * merges.
- */
- if (strncmp(line, "<<<<<<", 6) == 0 ||
- strncmp(line, "||||||", 6) == 0 ||
- strncmp(line, "======", 6) == 0 ||
- strncmp(line, ">>>>>>", 6) == 0) {
- Parse_Error(PARSE_FATAL, "Makefile appears to "
- "contain unresolved cvs/rcs/??? merge "
- "conflicts");
- } else
- Parse_Error(PARSE_FATAL, lstart[0] == '.' ?
- "Unknown directive" : "Need an operator");
- return;
- }
- *cp = '\0';
- /*
- * Have a word in line. See if it's a special target and set
- * specType to match it.
- */
- if (*line == '.' && isupper((unsigned char)line[1])) {
- /*
- * See if the target is a special target that must have
- * it or its sources handled specially.
- */
- if ((kw = ParseFindKeyword(line)) != NULL) {
- if (specType == ExPath && kw->spec != ExPath) {
- Parse_Error(PARSE_FATAL,
- "Mismatched special targets");
- return;
- }
-
- specType = kw->spec;
- tOp = kw->op;
-
- /*
- * Certain special targets have special
- * semantics:
- * .PATH Have to set the dirSearchPath
- * variable too
- * .MAIN Its sources are only used if
- * nothing has been specified to
- * create.
- * .DEFAULT Need to create a node to hang
- * commands on, but we don't want
- * it in the graph, nor do we want
- * it to be the Main Target, so we
- * create it, set OP_NOTMAIN and
- * add it to the list, setting
- * DEFAULT to the new node for
- * later use. We claim the node is
- * A transformation rule to make
- * life easier later, when we'll
- * use Make_HandleUse to actually
- * apply the .DEFAULT commands.
- * .PHONY The list of targets
- * .BEGIN
- * .END
- * .INTERRUPT Are not to be considered the
- * main target.
- * .NOTPARALLEL Make only one target at a time.
- * .SINGLESHELL Create a shell for each
- * command.
- * .ORDER Must set initial predecessor
- * to NULL
- */
- switch (specType) {
- case ExPath:
- Lst_AtEnd(&paths, &dirSearchPath);
- break;
- case Main:
- if (!Lst_IsEmpty(&create)) {
- specType = Not;
- }
- break;
- case Begin:
- case End:
- case Interrupt:
- gn = Targ_FindNode(line, TARG_CREATE);
- gn->type |= OP_NOTMAIN;
- Lst_AtEnd(&targets, gn);
- break;
- case Default:
- gn = Targ_NewGN(".DEFAULT");
- gn->type |= (OP_NOTMAIN|OP_TRANSFORM);
- Lst_AtEnd(&targets, gn);
- DEFAULT = gn;
- break;
- case NotParallel:
- jobLimit = 1;
- break;
- case SingleShell:
- compatMake = 1;
- break;
- case Order:
- predecessor = NULL;
- break;
- default:
- break;
- }
-
- } else if (strncmp(line, ".PATH", 5) == 0) {
- /*
- * .PATH<suffix> has to be handled specially.
- * Call on the suffix module to give us a path
- * to modify.
- */
- struct Path *path;
-
- specType = ExPath;
- path = Suff_GetPath(&line[5]);
- if (path == NULL) {
- Parse_Error(PARSE_FATAL, "Suffix '%s' "
- "not defined (yet)", &line[5]);
- return;
- } else
- Lst_AtEnd(&paths, path);
- }
- }
-
- /*
- * Have word in line. Get or create its node and stick it at
- * the end of the targets list
- */
- if (specType == Not && *line != '\0') {
-
- /* target names to be found and added to targets list */
- Lst curTargs = Lst_Initializer(curTargs);
-
- if (Dir_HasWildcards(line)) {
- /*
- * Targets are to be sought only in the current
- * directory, so create an empty path for the
- * thing. Note we need to use Path_Clear in the
- * destruction of the path as the Dir module
- * could have added a directory to the path...
- */
- struct Path emptyPath =
- TAILQ_HEAD_INITIALIZER(emptyPath);
-
- Path_Expand(line, &emptyPath, &curTargs);
- Path_Clear(&emptyPath);
-
- } else {
- /*
- * No wildcards, but we want to avoid code
- * duplication, so create a list with the word
- * on it.
- */
- Lst_AtEnd(&curTargs, line);
- }
-
- while (!Lst_IsEmpty(&curTargs)) {
- char *targName = Lst_DeQueue(&curTargs);
-
- if (!Suff_IsTransform (targName)) {
- gn = Targ_FindNode(targName,
- TARG_CREATE);
- } else {
- gn = Suff_AddTransform(targName);
- }
-
- Lst_AtEnd(&targets, gn);
- }
- } else if (specType == ExPath && *line != '.' && *line != '\0'){
- Parse_Error(PARSE_WARNING, "Extra target (%s) ignored",
- line);
- }
-
- *cp = savec;
- /*
- * If it is a special type and not .PATH, it's the only
- * target we allow on this line...
- */
- if (specType != Not && specType != ExPath) {
- Boolean warnFlag = FALSE;
-
- while (*cp != '!' && *cp != ':' && *cp) {
- if (*cp != ' ' && *cp != '\t') {
- warnFlag = TRUE;
- }
- cp++;
- }
- if (warnFlag) {
- Parse_Error(PARSE_WARNING,
- "Extra target ignored");
- }
- } else {
- while (*cp && isspace((unsigned char)*cp)) {
- cp++;
- }
- }
- line = cp;
- } while (*line != '!' && *line != ':' && *line);
-
- if (!Lst_IsEmpty(&targets)) {
- switch (specType) {
- default:
- Parse_Error(PARSE_WARNING, "Special and mundane "
- "targets don't mix. Mundane ones ignored");
- break;
- case Default:
- case Begin:
- case End:
- case Interrupt:
- /*
- * These four create nodes on which to hang commands, so
- * targets shouldn't be empty...
- */
- case Not:
- /*
- * Nothing special here -- targets can be empty if it
- * wants.
- */
- break;
- }
- }
-
- /*
- * Have now parsed all the target names. Must parse the operator next.
- * The result is left in op.
- */
- if (*cp == '!') {
- op = OP_FORCE;
- } else if (*cp == ':') {
- if (cp[1] == ':') {
- op = OP_DOUBLEDEP;
- cp++;
- } else {
- op = OP_DEPENDS;
- }
- } else {
- Parse_Error(PARSE_FATAL, lstart[0] == '.' ?
- "Unknown directive" : "Missing dependency operator");
- return;
- }
-
- cp++; /* Advance beyond operator */
-
- ParseDoOp(op);
-
- /*
- * Get to the first source
- */
- while (*cp && isspace((unsigned char)*cp)) {
- cp++;
- }
- line = cp;
-
- /*
- * Several special targets take different actions if present with no
- * sources:
- * a .SUFFIXES line with no sources clears out all old suffixes
- * a .PRECIOUS line makes all targets precious
- * a .IGNORE line ignores errors for all targets
- * a .SILENT line creates silence when making all targets
- * a .PATH removes all directories from the search path(s).
- */
- if (!*line) {
- switch (specType) {
- case Suffixes:
- Suff_ClearSuffixes();
- break;
- case Precious:
- allPrecious = TRUE;
- break;
- case Ignore:
- ignoreErrors = TRUE;
- break;
- case Silent:
- beSilent = TRUE;
- break;
- case ExPath:
- LST_FOREACH(ln, &paths)
- Path_Clear(Lst_Datum(ln));
- break;
- case MakefileDeps:
- mfAutoDeps = TRUE;
- break;
- case Posix:
- is_posix = TRUE;
- Var_SetGlobal("%POSIX", "1003.2");
- break;
- default:
- break;
- }
-
- } else if (specType == MFlags) {
- /*
- * Call on functions in main.c to deal with these arguments and
- * set the initial character to a null-character so the loop to
- * get sources won't get anything
- */
- Main_ParseArgLine(line, 0);
- *line = '\0';
-
- } else if (specType == Warn) {
- parse_warn(line);
- *line = '\0';
-
- } else if (specType == ExShell) {
- if (!Shell_Parse(line)) {
- Parse_Error(PARSE_FATAL,
- "improper shell specification");
- return;
- }
- *line = '\0';
-
- } else if (specType == NotParallel || specType == SingleShell) {
- *line = '\0';
- }
-
- /*
- * NOW GO FOR THE SOURCES
- */
- if (specType == Suffixes || specType == ExPath ||
- specType == Includes || specType == Libs ||
- specType == Null) {
- while (*line) {
- /*
- * If the target was one that doesn't take files as its
- * sources but takes something like suffixes, we take
- * each space-separated word on the line as a something
- * and deal with it accordingly.
- *
- * If the target was .SUFFIXES, we take each source as
- * a suffix and add it to the list of suffixes
- * maintained by the Suff module.
- *
- * If the target was a .PATH, we add the source as a
- * directory to search on the search path.
- *
- * If it was .INCLUDES, the source is taken to be the
- * suffix of files which will be #included and whose
- * search path should be present in the .INCLUDES
- * variable.
- *
- * If it was .LIBS, the source is taken to be the
- * suffix of files which are considered libraries and
- * whose search path should be present in the .LIBS
- * variable.
- *
- * If it was .NULL, the source is the suffix to use
- * when a file has no valid suffix.
- */
- char savech;
- while (*cp && !isspace((unsigned char)*cp)) {
- cp++;
- }
- savech = *cp;
- *cp = '\0';
- switch (specType) {
- case Suffixes:
- Suff_AddSuffix(line);
- break;
- case ExPath:
- LST_FOREACH(ln, &paths)
- Path_AddDir(Lst_Datum(ln), line);
- break;
- case Includes:
- Suff_AddInclude(line);
- break;
- case Libs:
- Suff_AddLib(line);
- break;
- case Null:
- Suff_SetNull(line);
- break;
- default:
- break;
- }
- *cp = savech;
- if (savech != '\0') {
- cp++;
- }
- while (*cp && isspace((unsigned char)*cp)) {
- cp++;
- }
- line = cp;
- }
- Lst_Destroy(&paths, NOFREE);
-
- } else if (specType == ExportVar) {
- Var_SetEnv(line, VAR_GLOBAL);
-
- } else {
- /* list of sources in order */
- Lst curSrcs = Lst_Initializer(curSrc);
-
- while (*line) {
- /*
- * The targets take real sources, so we must beware of
- * archive specifications (i.e. things with left
- * parentheses in them) and handle them accordingly.
- */
- while (*cp && !isspace((unsigned char)*cp)) {
- if (*cp == '(' && cp > line && cp[-1] != '$') {
- /*
- * Only stop for a left parenthesis if
- * it isn't at the start of a word
- * (that'll be for variable changes
- * later) and isn't preceded by a dollar
- * sign (a dynamic source).
- */
- break;
- } else {
- cp++;
- }
- }
-
- if (*cp == '(') {
- GNode *gnp;
-
- /* list of archive source names after exp. */
- Lst sources = Lst_Initializer(sources);
-
- if (!Arch_ParseArchive(&line, &sources,
- VAR_CMD)) {
- Parse_Error(PARSE_FATAL, "Error in "
- "source archive spec \"%s\"", line);
- return;
- }
-
- while (!Lst_IsEmpty(&sources)) {
- gnp = Lst_DeQueue(&sources);
- ParseDoSrc(tOp, gnp->name, &curSrcs);
- }
- cp = line;
- } else {
- if (*cp) {
- *cp = '\0';
- cp += 1;
- }
-
- ParseDoSrc(tOp, line, &curSrcs);
- }
- while (*cp && isspace((unsigned char)*cp)) {
- cp++;
- }
- line = cp;
- }
- Lst_Destroy(&curSrcs, NOFREE);
- }
-
- if (mainNode == NULL) {
- /*
- * If we have yet to decide on a main target to make, in the
- * absence of any user input, we want the first target on
- * the first dependency line that is actually a real target
- * (i.e. isn't a .USE or .EXEC rule) to be made.
- */
- LST_FOREACH(ln, &targets) {
- gn = Lst_Datum(ln);
- if ((gn->type & (OP_NOTMAIN | OP_USE |
- OP_EXEC | OP_TRANSFORM)) == 0) {
- mainNode = gn;
- Targ_SetMain(gn);
- break;
- }
- }
- }
-}
-
-/*-
- *---------------------------------------------------------------------
- * Parse_IsVar --
- * Return TRUE if the passed line is a variable assignment. A variable
- * assignment consists of a single word followed by optional whitespace
- * followed by either a += or an = operator.
- * This function is used both by the Parse_File function and main when
- * parsing the command-line arguments.
- *
- * Results:
- * TRUE if it is. FALSE if it ain't
- *
- * Side Effects:
- * none
- *---------------------------------------------------------------------
- */
-Boolean
-Parse_IsVar(char *line)
-{
- Boolean wasSpace = FALSE; /* set TRUE if found a space */
- Boolean haveName = FALSE; /* Set TRUE if have a variable name */
-
- int level = 0;
-#define ISEQOPERATOR(c) \
- ((c) == '+' || (c) == ':' || (c) == '?' || (c) == '!')
-
- /*
- * Skip to variable name
- */
- for (; *line == ' ' || *line == '\t'; line++)
- continue;
-
- for (; *line != '=' || level != 0; line++) {
- switch (*line) {
- case '\0':
- /*
- * end-of-line -- can't be a variable assignment.
- */
- return (FALSE);
-
- case ' ':
- case '\t':
- /*
- * there can be as much white space as desired so long
- * as there is only one word before the operator
- */
- wasSpace = TRUE;
- break;
-
- case '(':
- case '{':
- level++;
- break;
-
- case '}':
- case ')':
- level--;
- break;
-
- default:
- if (wasSpace && haveName) {
- if (ISEQOPERATOR(*line)) {
- /*
- * We must have a finished word
- */
- if (level != 0)
- return (FALSE);
-
- /*
- * When an = operator [+?!:] is found,
- * the next character must be an = or
- * it ain't a valid assignment.
- */
- if (line[1] == '=')
- return (haveName);
-#ifdef SUNSHCMD
- /*
- * This is a shell command
- */
- if (strncmp(line, ":sh", 3) == 0)
- return (haveName);
-#endif
- }
- /*
- * This is the start of another word, so not
- * assignment.
- */
- return (FALSE);
-
- } else {
- haveName = TRUE;
- wasSpace = FALSE;
- }
- break;
- }
- }
-
- return (haveName);
-}
-
-/*-
- *---------------------------------------------------------------------
- * Parse_DoVar --
- * Take the variable assignment in the passed line and do it in the
- * global context.
- *
- * Note: There is a lexical ambiguity with assignment modifier characters
- * in variable names. This routine interprets the character before the =
- * as a modifier. Therefore, an assignment like
- * C++=/usr/bin/CC
- * is interpreted as "C+ +=" instead of "C++ =".
- *
- * Results:
- * none
- *
- * Side Effects:
- * the variable structure of the given variable name is altered in the
- * global context.
- *---------------------------------------------------------------------
- */
-void
-Parse_DoVar(char *line, GNode *ctxt)
-{
- char *cp; /* pointer into line */
- enum {
- VAR_SUBST,
- VAR_APPEND,
- VAR_SHELL,
- VAR_NORMAL
- } type; /* Type of assignment */
- char *opc; /* ptr to operator character to
- * null-terminate the variable name */
-
- /*
- * Skip to variable name
- */
- while (*line == ' ' || *line == '\t') {
- line++;
- }
-
- /*
- * Skip to operator character, nulling out whitespace as we go
- */
- for (cp = line + 1; *cp != '='; cp++) {
- if (isspace((unsigned char)*cp)) {
- *cp = '\0';
- }
- }
- opc = cp - 1; /* operator is the previous character */
- *cp++ = '\0'; /* nuke the = */
-
- /*
- * Check operator type
- */
- switch (*opc) {
- case '+':
- type = VAR_APPEND;
- *opc = '\0';
- break;
-
- case '?':
- /*
- * If the variable already has a value, we don't do anything.
- */
- *opc = '\0';
- if (Var_Exists(line, ctxt)) {
- return;
- } else {
- type = VAR_NORMAL;
- }
- break;
-
- case ':':
- type = VAR_SUBST;
- *opc = '\0';
- break;
-
- case '!':
- type = VAR_SHELL;
- *opc = '\0';
- break;
-
- default:
-#ifdef SUNSHCMD
- while (*opc != ':') {
- if (opc == line)
- break;
- else
- --opc;
- }
-
- if (strncmp(opc, ":sh", 3) == 0) {
- type = VAR_SHELL;
- *opc = '\0';
- break;
- }
-#endif
- type = VAR_NORMAL;
- break;
- }
-
- while (isspace((unsigned char)*cp)) {
- cp++;
- }
-
- if (type == VAR_APPEND) {
- Var_Append(line, cp, ctxt);
-
- } else if (type == VAR_SUBST) {
- /*
- * Allow variables in the old value to be undefined, but leave
- * their invocation alone -- this is done by forcing oldVars
- * to be false.
- * XXX: This can cause recursive variables, but that's not
- * hard to do, and this allows someone to do something like
- *
- * CFLAGS = $(.INCLUDES)
- * CFLAGS := -I.. $(CFLAGS)
- *
- * And not get an error.
- */
- Boolean oldOldVars = oldVars;
-
- oldVars = FALSE;
-
- /*
- * make sure that we set the variable the first time to nothing
- * so that it gets substituted!
- */
- if (!Var_Exists(line, ctxt))
- Var_Set(line, "", ctxt);
-
- cp = Buf_Peel(Var_Subst(cp, ctxt, FALSE));
-
- oldVars = oldOldVars;
-
- Var_Set(line, cp, ctxt);
- free(cp);
-
- } else if (type == VAR_SHELL) {
- /*
- * TRUE if the command needs to be freed, i.e.
- * if any variable expansion was performed
- */
- Boolean freeCmd = FALSE;
- Buffer *buf;
- const char *error;
-
- if (strchr(cp, '$') != NULL) {
- /*
- * There's a dollar sign in the command, so perform
- * variable expansion on the whole thing. The
- * resulting string will need freeing when we're done,
- * so set freeCmd to TRUE.
- */
- cp = Buf_Peel(Var_Subst(cp, VAR_CMD, TRUE));
- freeCmd = TRUE;
- }
-
- buf = Cmd_Exec(cp, &error);
- Var_Set(line, Buf_Data(buf), ctxt);
- Buf_Destroy(buf, TRUE);
-
- if (error)
- Parse_Error(PARSE_WARNING, error, cp);
-
- if (freeCmd)
- free(cp);
-
- } else {
- /*
- * Normal assignment -- just do it.
- */
- Var_Set(line, cp, ctxt);
- }
- if (strcmp(line, MAKE_JOB_PREFIX) == 0)
- Job_SetPrefix();
-}
-
-/*-
- *-----------------------------------------------------------------------
- * ParseHasCommands --
- * Callback procedure for Parse_File when destroying the list of
- * targets on the last dependency line. Marks a target as already
- * having commands if it does, to keep from having shell commands
- * on multiple dependency lines.
- *
- * Results:
- * None
- *
- * Side Effects:
- * OP_HAS_COMMANDS may be set for the target.
- *
- *-----------------------------------------------------------------------
- */
-static void
-ParseHasCommands(void *gnp)
-{
- GNode *gn = gnp;
-
- if (!Lst_IsEmpty(&gn->commands)) {
- gn->type |= OP_HAS_COMMANDS;
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Parse_AddIncludeDir --
- * Add a directory to the path searched for included makefiles
- * bracketed by double-quotes. Used by functions in main.c
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The directory is appended to the list.
- *
- *-----------------------------------------------------------------------
- */
-void
-Parse_AddIncludeDir(char *dir)
-{
-
- Path_AddDir(&parseIncPath, dir);
-}
-
-/*-
- *---------------------------------------------------------------------
- * Parse_FromString --
- * Start Parsing from the given string
- *
- * Results:
- * None
- *
- * Side Effects:
- * A structure is added to the includes Lst and readProc, curFile.lineno,
- * curFile.fname and curFile.F are altered for the new file
- *---------------------------------------------------------------------
- */
-void
-Parse_FromString(char *str, int lineno)
-{
-
- DEBUGF(FOR, ("%s\n---- at line %d\n", str, lineno));
-
- ParsePushInput(estrdup(CURFILE->fname), NULL, str, lineno);
-}
-
-#ifdef SYSVINCLUDE
-/*-
- *---------------------------------------------------------------------
- * ParseTraditionalInclude --
- * Push to another file.
- *
- * The input is the line minus the "include". The file name is
- * the string following the "include".
- *
- * Results:
- * None
- *
- * Side Effects:
- * A structure is added to the includes Lst and readProc, curFile.lineno,
- * curFile.fname and curFile.F are altered for the new file
- *---------------------------------------------------------------------
- */
-static void
-ParseTraditionalInclude(char *file)
-{
- char *fullname; /* full pathname of file */
- char *cp; /* current position in file spec */
-
- /*
- * Skip over whitespace
- */
- while (*file == ' ' || *file == '\t') {
- file++;
- }
-
- if (*file == '\0') {
- Parse_Error(PARSE_FATAL, "Filename missing from \"include\"");
- return;
- }
-
- /*
- * Skip to end of line or next whitespace
- */
- for (cp = file; *cp && *cp != '\n' && *cp != '\t' && *cp != ' '; cp++) {
- continue;
- }
-
- *cp = '\0';
-
- /*
- * Substitute for any variables in the file name before trying to
- * find the thing.
- */
- file = Buf_Peel(Var_Subst(file, VAR_CMD, FALSE));
-
- /*
- * Now we know the file's name, we attempt to find the durn thing.
- * Search for it first on the -I search path, then on the .PATH
- * search path, if not found in a -I directory.
- */
- fullname = Path_FindFile(file, &parseIncPath);
- if (fullname == NULL) {
- fullname = Path_FindFile(file, &dirSearchPath);
- }
-
- if (fullname == NULL) {
- /*
- * Still haven't found the makefile. Look for it on the system
- * path as a last resort.
- */
- fullname = Path_FindFile(file, &sysIncPath);
- }
-
- if (fullname == NULL) {
- Parse_Error(PARSE_FATAL, "Could not find %s", file);
- /* XXXHB free(file) */
- return;
- }
-
- /* XXXHB free(file) */
-
- /*
- * We set up the name of the file to be the absolute
- * name of the include file so error messages refer to the right
- * place.
- */
- ParsePushInput(fullname, NULL, NULL, 0);
-}
-#endif
-
-/*-
- *---------------------------------------------------------------------
- * ParseReadc --
- * Read a character from the current file
- *
- * Results:
- * The character that was read
- *
- * Side Effects:
- *---------------------------------------------------------------------
- */
-static int
-ParseReadc(void)
-{
-
- if (CURFILE->F != NULL)
- return (fgetc(CURFILE->F));
-
- if (CURFILE->str != NULL && *CURFILE->ptr != '\0')
- return (*CURFILE->ptr++);
-
- return (EOF);
-}
-
-
-/*-
- *---------------------------------------------------------------------
- * ParseUnreadc --
- * Put back a character to the current file
- *
- * Results:
- * None.
- *
- * Side Effects:
- *---------------------------------------------------------------------
- */
-static void
-ParseUnreadc(int c)
-{
-
- if (CURFILE->F != NULL) {
- ungetc(c, CURFILE->F);
- return;
- }
- if (CURFILE->str != NULL) {
- *--(CURFILE->ptr) = c;
- return;
- }
-}
-
-/* ParseSkipLine():
- * Grab the next line unless it begins with a dot (`.') and we're told to
- * ignore such lines.
- */
-static char *
-ParseSkipLine(int skip, int keep_newline)
-{
- char *line;
- int c, lastc;
- Buffer *buf;
-
- buf = Buf_Init(MAKE_BSIZE);
-
- do {
- Buf_Clear(buf);
- lastc = '\0';
-
- while (((c = ParseReadc()) != '\n' || lastc == '\\')
- && c != EOF) {
- if (skip && c == '#' && lastc != '\\') {
- /*
- * let a comment be terminated even by an
- * escaped \n. This is consistent to comment
- * handling in ParseReadLine
- */
- while ((c = ParseReadc()) != '\n' && c != EOF)
- ;
- break;
- }
- if (c == '\n') {
- if (keep_newline)
- Buf_AddByte(buf, (Byte)c);
- else
- Buf_ReplaceLastByte(buf, (Byte)' ');
- CURFILE->lineno++;
-
- while ((c = ParseReadc()) == ' ' || c == '\t')
- continue;
-
- if (c == EOF)
- break;
- }
-
- Buf_AddByte(buf, (Byte)c);
- lastc = c;
- }
-
- if (c == EOF) {
- Parse_Error(PARSE_FATAL,
- "Unclosed conditional/for loop");
- Buf_Destroy(buf, TRUE);
- return (NULL);
- }
-
- CURFILE->lineno++;
- Buf_AddByte(buf, (Byte)'\0');
- line = Buf_Data(buf);
- } while (skip == 1 && line[0] != '.');
-
- Buf_Destroy(buf, FALSE);
- return (line);
-}
-
-/*-
- *---------------------------------------------------------------------
- * ParseReadLine --
- * Read an entire line from the input file. Called only by Parse_File.
- * To facilitate escaped newlines and what have you, a character is
- * buffered in 'lastc', which is '\0' when no characters have been
- * read. When we break out of the loop, c holds the terminating
- * character and lastc holds a character that should be added to
- * the line (unless we don't read anything but a terminator).
- *
- * Results:
- * A line w/o its newline
- *
- * Side Effects:
- * Only those associated with reading a character
- *---------------------------------------------------------------------
- */
-static char *
-ParseReadLine(void)
-{
- Buffer *buf; /* Buffer for current line */
- int c; /* the current character */
- int lastc; /* The most-recent character */
- Boolean semiNL; /* treat semi-colons as newlines */
- Boolean ignDepOp; /* TRUE if should ignore dependency operators
- * for the purposes of setting semiNL */
- Boolean ignComment; /* TRUE if should ignore comments (in a
- * shell command */
- char *line; /* Result */
- char *ep; /* to strip trailing blanks */
-
- again:
- semiNL = FALSE;
- ignDepOp = FALSE;
- ignComment = FALSE;
-
- lastc = '\0';
-
- /*
- * Handle tab at the beginning of the line. A leading tab (shell
- * command) forces us to ignore comments and dependency operators and
- * treat semi-colons as semi-colons (by leaving semiNL FALSE).
- * This also discards completely blank lines.
- */
- for (;;) {
- c = ParseReadc();
- if (c == EOF) {
- if (ParsePopInput() == DONE) {
- /* End of all inputs - return NULL */
- return (NULL);
- }
- continue;
- }
-
- if (c == '\t') {
- ignComment = ignDepOp = TRUE;
- lastc = c;
- break;
- }
- if (c != '\n') {
- ParseUnreadc(c);
- break;
- }
- CURFILE->lineno++;
- }
-
- buf = Buf_Init(MAKE_BSIZE);
-
- while (((c = ParseReadc()) != '\n' || lastc == '\\') && c != EOF) {
- test_char:
- switch (c) {
- case '\n':
- /*
- * Escaped newline: read characters until a
- * non-space or an unescaped newline and
- * replace them all by a single space. This is
- * done by storing the space over the backslash
- * and dropping through with the next nonspace.
- * If it is a semi-colon and semiNL is TRUE,
- * it will be recognized as a newline in the
- * code below this...
- */
- CURFILE->lineno++;
- lastc = ' ';
- while ((c = ParseReadc()) == ' ' || c == '\t') {
- continue;
- }
- if (c == EOF || c == '\n') {
- goto line_read;
- } else {
- /*
- * Check for comments, semiNL's, etc. --
- * easier than ParseUnreadc(c);
- * continue;
- */
- goto test_char;
- }
- /*NOTREACHED*/
- break;
-
- case ';':
- /*
- * Semi-colon: Need to see if it should be
- * interpreted as a newline
- */
- if (semiNL) {
- /*
- * To make sure the command that may
- * be following this semi-colon begins
- * with a tab, we push one back into the
- * input stream. This will overwrite the
- * semi-colon in the buffer. If there is
- * no command following, this does no
- * harm, since the newline remains in
- * the buffer and the
- * whole line is ignored.
- */
- ParseUnreadc('\t');
- goto line_read;
- }
- break;
- case '=':
- if (!semiNL) {
- /*
- * Haven't seen a dependency operator
- * before this, so this must be a
- * variable assignment -- don't pay
- * attention to dependency operators
- * after this.
- */
- ignDepOp = TRUE;
- } else if (lastc == ':' || lastc == '!') {
- /*
- * Well, we've seen a dependency
- * operator already, but it was the
- * previous character, so this is really
- * just an expanded variable assignment.
- * Revert semi-colons to being just
- * semi-colons again and ignore any more
- * dependency operators.
- *
- * XXX: Note that a line like
- * "foo : a:=b" will blow up, but who'd
- * write a line like that anyway?
- */
- ignDepOp = TRUE;
- semiNL = FALSE;
- }
- break;
- case '#':
- if (!ignComment) {
- if (lastc != '\\') {
- /*
- * If the character is a hash
- * mark and it isn't escaped
- * (or we're being compatible),
- * the thing is a comment.
- * Skip to the end of the line.
- */
- do {
- c = ParseReadc();
- } while (c != '\n' && c != EOF);
- goto line_read;
- } else {
- /*
- * Don't add the backslash.
- * Just let the # get copied
- * over.
- */
- lastc = c;
- continue;
- }
- }
- break;
-
- case ':':
- case '!':
- if (!ignDepOp) {
- /*
- * A semi-colon is recognized as a
- * newline only on dependency lines.
- * Dependency lines are lines with a
- * colon or an exclamation point.
- * Ergo...
- */
- semiNL = TRUE;
- }
- break;
-
- default:
- break;
- }
- /*
- * Copy in the previous character (there may be none if this
- * was the first character) and save this one in
- * lastc.
- */
- if (lastc != '\0')
- Buf_AddByte(buf, (Byte)lastc);
- lastc = c;
- }
- line_read:
- CURFILE->lineno++;
-
- if (lastc != '\0') {
- Buf_AddByte(buf, (Byte)lastc);
- }
- Buf_AddByte(buf, (Byte)'\0');
- line = Buf_Peel(buf);
-
- /*
- * Strip trailing blanks and tabs from the line.
- * Do not strip a blank or tab that is preceded by
- * a '\'
- */
- ep = line;
- while (*ep)
- ++ep;
- while (ep > line + 1 && (ep[-1] == ' ' || ep[-1] == '\t')) {
- if (ep > line + 1 && ep[-2] == '\\')
- break;
- --ep;
- }
- *ep = 0;
-
- if (line[0] == '\0') {
- /* empty line - just ignore */
- free(line);
- goto again;
- }
-
- return (line);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * ParseFinishLine --
- * Handle the end of a dependency group.
- *
- * Results:
- * Nothing.
- *
- * Side Effects:
- * inLine set FALSE. 'targets' list destroyed.
- *
- *-----------------------------------------------------------------------
- */
-static void
-ParseFinishLine(void)
-{
- const LstNode *ln;
-
- if (inLine) {
- LST_FOREACH(ln, &targets) {
- if (((const GNode *)Lst_Datum(ln))->type & OP_TRANSFORM)
- Suff_EndTransform(Lst_Datum(ln));
- }
- Lst_Destroy(&targets, ParseHasCommands);
- inLine = FALSE;
- }
-}
-
-/**
- * xparse_include
- * Parse an .include directive and push the file onto the input stack.
- * The input is the line minus the .include. A file spec is a string
- * enclosed in <> or "". The former is looked for only in sysIncPath.
- * The latter in . and the directories specified by -I command line
- * options
- */
-static void
-xparse_include(char *file, int sinclude)
-{
- char *fullname; /* full pathname of file */
- char endc; /* the character which ends the file spec */
- char *cp; /* current position in file spec */
- Boolean isSystem; /* TRUE if makefile is a system makefile */
- char *prefEnd, *Fname;
- char *newName;
-
- /*
- * Skip to delimiter character so we know where to look
- */
- while (*file == ' ' || *file == '\t') {
- file++;
- }
-
- if (*file != '"' && *file != '<') {
- Parse_Error(PARSE_FATAL,
- ".include filename must be delimited by '\"' or '<'");
- return;
- }
-
- /*
- * Set the search path on which to find the include file based on the
- * characters which bracket its name. Angle-brackets imply it's
- * a system Makefile while double-quotes imply it's a user makefile
- */
- if (*file == '<') {
- isSystem = TRUE;
- endc = '>';
- } else {
- isSystem = FALSE;
- endc = '"';
- }
-
- /*
- * Skip to matching delimiter
- */
- for (cp = ++file; *cp != endc; cp++) {
- if (*cp == '\0') {
- Parse_Error(PARSE_FATAL,
- "Unclosed .include filename. '%c' expected", endc);
- return;
- }
- }
- *cp = '\0';
-
- /*
- * Substitute for any variables in the file name before trying to
- * find the thing.
- */
- file = Buf_Peel(Var_Subst(file, VAR_CMD, FALSE));
-
- /*
- * Now we know the file's name and its search path, we attempt to
- * find the durn thing. A return of NULL indicates the file don't
- * exist.
- */
- if (!isSystem) {
- /*
- * Include files contained in double-quotes are first searched
- * for relative to the including file's location. We don't want
- * to cd there, of course, so we just tack on the old file's
- * leading path components and call Path_FindFile to see if
- * we can locate the beast.
- */
-
- /* Make a temporary copy of this, to be safe. */
- Fname = estrdup(CURFILE->fname);
-
- prefEnd = strrchr(Fname, '/');
- if (prefEnd != NULL) {
- *prefEnd = '\0';
- if (file[0] == '/')
- newName = estrdup(file);
- else
- newName = str_concat(Fname, file, STR_ADDSLASH);
- fullname = Path_FindFile(newName, &parseIncPath);
- if (fullname == NULL) {
- fullname = Path_FindFile(newName,
- &dirSearchPath);
- }
- free(newName);
- *prefEnd = '/';
- } else {
- fullname = NULL;
- }
- free(Fname);
- if (fullname == NULL) {
- /*
- * Makefile wasn't found in same directory as included
- * makefile. Search for it first on the -I search path,
- * then on the .PATH search path, if not found in a -I
- * directory.
- * XXX: Suffix specific?
- */
- fullname = Path_FindFile(file, &parseIncPath);
- if (fullname == NULL) {
- fullname = Path_FindFile(file, &dirSearchPath);
- }
- }
- } else {
- fullname = NULL;
- }
-
- if (fullname == NULL) {
- /*
- * System makefile or still haven't found the makefile.
- * Look for it on the system path.
- */
- fullname = Path_FindFile(file, &sysIncPath);
- }
-
- if (fullname == NULL) {
- *cp = endc;
- if (!sinclude)
- Parse_Error(PARSE_FATAL, "Could not find %s", file);
- else
- Main_AddSourceMakefile(file);
- free(file);
- return;
- }
- Main_AddSourceMakefile(fullname);
- free(file);
-
- /*
- * We set up the name of the file to be the absolute
- * name of the include file so error messages refer to the right
- * place.
- */
- ParsePushInput(fullname, NULL, NULL, 0);
- DEBUGF(DIR, (".include %s\n", fullname));
-}
-
-static void
-parse_include(char *file, int code __unused, int lineno __unused)
-{
- xparse_include(file, 0);
-}
-
-static void
-parse_sinclude(char *file, int code __unused, int lineno __unused)
-{
- xparse_include(file, 1);
-}
-
-/**
- * parse_message
- * Parse a .warning or .error directive
- *
- * The input is the line minus the ".error"/".warning". We substitute
- * variables, print the message and exit(1) (for .error) or just print
- * a warning if the directive is malformed.
- */
-static void
-parse_message(char *line, int iserror, int lineno __unused)
-{
-
- if (!isspace((u_char)*line)) {
- Parse_Error(PARSE_WARNING, "invalid syntax: .%s%s",
- iserror ? "error" : "warning", line);
- return;
- }
-
- while (isspace((u_char)*line))
- line++;
-
- line = Buf_Peel(Var_Subst(line, VAR_CMD, FALSE));
- Parse_Error(iserror ? PARSE_FATAL : PARSE_WARNING, "%s", line);
- free(line);
-
- if (iserror) {
- /* Terminate immediately. */
- exit(1);
- }
-}
-
-/**
- * parse_undef
- * Parse an .undef directive.
- */
-static void
-parse_undef(char *line, int code __unused, int lineno __unused)
-{
- char *cp;
-
- while (isspace((u_char)*line))
- line++;
-
- for (cp = line; !isspace((u_char)*cp) && *cp != '\0'; cp++) {
- ;
- }
- *cp = '\0';
-
- cp = Buf_Peel(Var_Subst(line, VAR_CMD, FALSE));
- Var_Delete(cp, VAR_GLOBAL);
- free(cp);
-}
-
-/**
- * parse_for
- * Parse a .for directive.
- */
-static void
-parse_for(char *line, int code __unused, int lineno)
-{
-
- if (!For_For(line)) {
- /* syntax error */
- return;
- }
- line = NULL;
-
- /*
- * Skip after the matching endfor.
- */
- do {
- free(line);
- line = ParseSkipLine(0, 1);
- if (line == NULL) {
- Parse_Error(PARSE_FATAL,
- "Unexpected end of file in for loop.\n");
- return;
- }
- } while (For_Eval(line));
- free(line);
-
- /* execute */
- For_Run(lineno);
-}
-
-/**
- * parse_endfor
- * Parse endfor. This may only happen if there was no matching .for.
- */
-static void
-parse_endfor(char *line __unused, int code __unused, int lineno __unused)
-{
-
- Parse_Error(PARSE_FATAL, "for-less endfor");
-}
-
-/**
- * parse_directive
- * Got a line starting with a '.'. Check if this is a directive
- * and parse it.
- *
- * return:
- * TRUE if line was a directive, FALSE otherwise.
- */
-static Boolean
-parse_directive(char *line)
-{
- char *start;
- char *cp;
- int dir;
-
- /*
- * Get the keyword:
- * .[[:space:]]*\([[:alpha:]][[:alnum:]_]*\).*
- * \1 is the keyword.
- */
- for (start = line; isspace((u_char)*start); start++) {
- ;
- }
-
- if (!isalpha((u_char)*start)) {
- return (FALSE);
- }
-
- cp = start + 1;
- while (isalnum((u_char)*cp) || *cp == '_') {
- cp++;
- }
-
- dir = directive_hash(start, cp - start);
- if (dir < 0 || dir >= (int)NDIRECTS ||
- (size_t)(cp - start) != strlen(directives[dir].name) ||
- strncmp(start, directives[dir].name, cp - start) != 0) {
- /* not actually matched */
- return (FALSE);
- }
-
- if (!skipLine || directives[dir].skip_flag)
- (*directives[dir].func)(cp, directives[dir].code,
- CURFILE->lineno);
- return (TRUE);
-}
-
-/*-
- *---------------------------------------------------------------------
- * Parse_File --
- * Parse a file into its component parts, incorporating it into the
- * current dependency graph. This is the main function and controls
- * almost every other function in this module
- *
- * Results:
- * None
- *
- * Side Effects:
- * Loads. Nodes are added to the list of all targets, nodes and links
- * are added to the dependency graph. etc. etc. etc.
- *---------------------------------------------------------------------
- */
-void
-Parse_File(const char *name, FILE *stream)
-{
- char *cp; /* pointer into the line */
- char *line; /* the line we're working on */
-
- inLine = FALSE;
- fatals = 0;
-
- ParsePushInput(estrdup(name), stream, NULL, 0);
-
- while ((line = ParseReadLine()) != NULL) {
- if (*line == '.' && parse_directive(line + 1)) {
- /* directive consumed */
- goto nextLine;
- }
- if (skipLine || *line == '#') {
- /* Skipping .if block or comment. */
- goto nextLine;
- }
-
- if (*line == '\t') {
- /*
- * If a line starts with a tab, it can only
- * hope to be a creation command.
- */
- for (cp = line + 1; isspace((unsigned char)*cp); cp++) {
- continue;
- }
- if (*cp) {
- if (inLine) {
- LstNode *ln;
- GNode *gn;
-
- /*
- * So long as it's not a blank
- * line and we're actually in a
- * dependency spec, add the
- * command to the list of
- * commands of all targets in
- * the dependency spec.
- */
- LST_FOREACH(ln, &targets) {
- gn = Lst_Datum(ln);
-
- /*
- * if target already
- * supplied, ignore
- * commands
- */
- if (!(gn->type & OP_HAS_COMMANDS))
- Lst_AtEnd(&gn->commands, cp);
- else
- Parse_Error(PARSE_WARNING, "duplicate script "
- "for target \"%s\" ignored", gn->name);
- }
- continue;
- } else {
- Parse_Error(PARSE_FATAL,
- "Unassociated shell command \"%s\"",
- cp);
- }
- }
-#ifdef SYSVINCLUDE
- } else if (strncmp(line, "include", 7) == 0 &&
- isspace((unsigned char)line[7]) &&
- strchr(line, ':') == NULL) {
- /*
- * It's an S3/S5-style "include".
- */
- ParseTraditionalInclude(line + 7);
- goto nextLine;
-#endif
- } else if (Parse_IsVar(line)) {
- ParseFinishLine();
- Parse_DoVar(line, VAR_GLOBAL);
-
- } else {
- /*
- * We now know it's a dependency line so it
- * needs to have all variables expanded before
- * being parsed. Tell the variable module to
- * complain if some variable is undefined...
- * To make life easier on novices, if the line
- * is indented we first make sure the line has
- * a dependency operator in it. If it doesn't
- * have an operator and we're in a dependency
- * line's script, we assume it's actually a
- * shell command and add it to the current
- * list of targets. XXX this comment seems wrong.
- */
- cp = line;
- if (isspace((unsigned char)line[0])) {
- while (*cp != '\0' &&
- isspace((unsigned char)*cp)) {
- cp++;
- }
- if (*cp == '\0') {
- goto nextLine;
- }
- }
-
- ParseFinishLine();
-
- cp = Buf_Peel(Var_Subst(line, VAR_CMD, TRUE));
-
- free(line);
- line = cp;
-
- /*
- * Need a non-circular list for the target nodes
- */
- Lst_Destroy(&targets, NOFREE);
- inLine = TRUE;
-
- ParseDoDependency(line);
- }
-
- nextLine:
- free(line);
- }
-
- ParseFinishLine();
-
- /*
- * Make sure conditionals are clean
- */
- Cond_End();
-
- if (fatals)
- errx(1, "fatal errors encountered -- cannot continue");
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Parse_MainName --
- * Return a Lst of the main target to create for main()'s sake. If
- * no such target exists, we Punt with an obnoxious error message.
- *
- * Results:
- * A Lst of the single node to create.
- *
- * Side Effects:
- * None.
- *
- *-----------------------------------------------------------------------
- */
-void
-Parse_MainName(Lst *listmain)
-{
-
- if (mainNode == NULL) {
- Punt("no target to make.");
- /*NOTREACHED*/
- } else if (mainNode->type & OP_DOUBLEDEP) {
- Lst_AtEnd(listmain, mainNode);
- Lst_Concat(listmain, &mainNode->cohorts, LST_CONCNEW);
- } else
- Lst_AtEnd(listmain, mainNode);
-}
diff --git a/usr.bin/make/parse.h b/usr.bin/make/parse.h
deleted file mode 100644
index 6ee15f9..0000000
--- a/usr.bin/make/parse.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * $FreeBSD$
- */
-
-#ifndef parse_h_470eeb9a
-#define parse_h_470eeb9a
-
-#include <stdio.h>
-
-#include "util.h"
-
-struct GNode;
-struct Lst;
-
-/*
- * Error levels for parsing. PARSE_FATAL means the process cannot continue
- * once the makefile has been parsed. PARSE_WARNING means it can. Passed
- * as the first argument to Parse_Error.
- */
-#define PARSE_WARNING 2
-#define PARSE_FATAL 1
-
-/*
- * Definitions for the "local" variables. Used only for clarity.
- */
-#define TARGET "@" /* Target of dependency */
-#define OODATE "?" /* All out-of-date sources */
-#define ALLSRC ">" /* All sources */
-#define IMPSRC "<" /* Source implied by transformation */
-#define PREFIX "*" /* Common prefix */
-#define ARCHIVE "!" /* Archive in "archive(member)" syntax */
-#define MEMBER "%" /* Member in "archive(member)" syntax */
-
-#define FTARGET "@F" /* file part of TARGET */
-#define DTARGET "@D" /* directory part of TARGET */
-#define FIMPSRC "<F" /* file part of IMPSRC */
-#define DIMPSRC "<D" /* directory part of IMPSRC */
-#define FPREFIX "*F" /* file part of PREFIX */
-#define DPREFIX "*D" /* directory part of PREFIX */
-
-void Parse_Error(int, const char *, ...);
-Boolean Parse_AnyExport(void);
-Boolean Parse_IsVar(char *);
-void Parse_DoVar(char *, struct GNode *);
-void Parse_AddIncludeDir(char *);
-void Parse_File(const char *, FILE *);
-void Parse_FromString(char *, int);
-void Parse_MainName(struct Lst *);
-
-#endif /* parse_h_470eeb9a */
diff --git a/usr.bin/make/pathnames.h b/usr.bin/make/pathnames.h
deleted file mode 100644
index 5fb4ceb..0000000
--- a/usr.bin/make/pathnames.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*-
- * Copyright (c) 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * 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.
- *
- * @(#)pathnames.h 8.2 (Berkeley) 4/28/95
- * $FreeBSD$
- */
-
-#ifndef pathnames_h_235b888a
-#define pathnames_h_235b888a
-
-#ifndef PATH_OBJDIR
-#define PATH_OBJDIR "obj"
-#endif /* ! PATH_OBJDIR */
-
-#ifndef PATH_OBJDIRPREFIX
-#define PATH_OBJDIRPREFIX "/usr/obj"
-#endif /* ! PATH_OBJDIRPREFIX */
-
-#ifndef PATH_DEFSHELLDIR
-#define PATH_DEFSHELLDIR "/bin"
-#endif /* ! PATH_DEFSHELLDIR */
-
-#ifndef PATH_DEFSYSMK
-#define PATH_DEFSYSMK "sys.mk"
-#endif /* ! PATH_DEFSYSMK */
-
-#ifndef PATH_DEFSYSPATH
-#define PATH_DEFSYSPATH "/usr/share/mk"
-#endif /* ! PATH_DEFSYSPATH */
-
-#endif /* pathnames_h_235b888a */
diff --git a/usr.bin/make/proc.c b/usr.bin/make/proc.c
deleted file mode 100644
index 3a7e4f6..0000000
--- a/usr.bin/make/proc.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/*-
- * Copyright (C) 2005 Max Okumoto.
- * All rights reserved.
- *
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <unistd.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-
-#include "proc.h"
-#include "shell.h"
-#include "util.h"
-
-/**
- * Replace the current process.
- */
-void
-Proc_Exec(const ProcStuff *ps)
-{
-
- if (ps->in != STDIN_FILENO) {
- /*
- * Redirect the child's stdin to the input fd
- * and reset it to the beginning (again).
- */
- if (dup2(ps->in, STDIN_FILENO) == -1)
- Punt("Cannot dup2: %s", strerror(errno));
- lseek(STDIN_FILENO, (off_t)0, SEEK_SET);
- }
-
- if (ps->out != STDOUT_FILENO) {
- /*
- * Redirect the child's stdout to the output fd.
- */
- if (dup2(ps->out, STDOUT_FILENO) == -1)
- Punt("Cannot dup2: %s", strerror(errno));
- close(ps->out);
- }
-
- if (ps->err != STDERR_FILENO) {
- /*
- * Redirect the child's stderr to the err fd.
- */
- if (dup2(ps->err, STDERR_FILENO) == -1)
- Punt("Cannot dup2: %s", strerror(errno));
- close(ps->err);
- }
-
- if (ps->merge_errors) {
- /*
- * Send stderr to parent process too.
- */
- if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1)
- Punt("Cannot dup2: %s", strerror(errno));
- }
-
- if (commandShell->unsetenv) {
- /* for the benfit of ksh */
- unsetenv("ENV");
- }
-
- /*
- * The file descriptors for stdin, stdout, or stderr might
- * have been marked close-on-exec. Clear the flag on all
- * of them.
- */
- fcntl(STDIN_FILENO, F_SETFD,
- fcntl(STDIN_FILENO, F_GETFD) & (~FD_CLOEXEC));
- fcntl(STDOUT_FILENO, F_SETFD,
- fcntl(STDOUT_FILENO, F_GETFD) & (~FD_CLOEXEC));
- fcntl(STDERR_FILENO, F_SETFD,
- fcntl(STDERR_FILENO, F_GETFD) & (~FD_CLOEXEC));
-
- if (ps->pgroup) {
-#ifdef USE_PGRP
- /*
- * Become a process group leader, so we can kill it and all
- * its descendants in one fell swoop, by killing its process
- * family, but not commit suicide.
- */
-#if defined(SYSV)
- setsid();
-#else
- setpgid(0, getpid());
-#endif
-#endif /* USE_PGRP */
- }
-
- if (ps->searchpath) {
- execvp(ps->argv[0], ps->argv);
-
- write(STDERR_FILENO, ps->argv[0], strlen(ps->argv[0]));
- write(STDERR_FILENO, ": ", 2);
- write(STDERR_FILENO, strerror(errno), strlen(strerror(errno)));
- write(STDERR_FILENO, "\n", 1);
- } else {
- execv(commandShell->path, ps->argv);
-
- write(STDERR_FILENO,
- "Could not execute shell\n",
- sizeof("Could not execute shell"));
- }
-
- /*
- * Since we are the child process, exit without flushing buffers.
- */
- _exit(1);
-}
diff --git a/usr.bin/make/proc.h b/usr.bin/make/proc.h
deleted file mode 100644
index a4ce6d3..0000000
--- a/usr.bin/make/proc.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*-
- * Copyright (C) 2005 Max Okumoto.
- * All rights reserved.
- *
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
- *
- * $FreeBSD$
- */
-
-#ifndef proc_h_458845848
-#define proc_h_458845848
-
-/**
- * Information used to create a new process.
- */
-typedef struct ProcStuff {
- int in; /* stdin for new process */
- int out; /* stdout for new process */
- int err; /* stderr for new process */
-
- int merge_errors; /* true if stderr is redirected to stdin */
- int pgroup; /* true if new process a process leader */
- int searchpath; /* true if binary should be found via $PATH */
-
- char **argv;
- int argv_free; /* release argv after use */
- int errCheck;
-
- pid_t child_pid;
-} ProcStuff;
-
-void Proc_Exec(const ProcStuff *) __dead2;
-
-#endif /* proc_h_458845848 */
diff --git a/usr.bin/make/shell.c b/usr.bin/make/shell.c
deleted file mode 100644
index c1f82d1..0000000
--- a/usr.bin/make/shell.c
+++ /dev/null
@@ -1,472 +0,0 @@
-/*-
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1988, 1989 by Adam de Boor
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/queue.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "parse.h"
-#include "pathnames.h"
-#include "shell.h"
-#include "util.h"
-
-/*
- * Descriptions for various shells. What the list of builtins should contain
- * is debatable: either all builtins or only those which may specified on
- * a single line without use of meta-characters. For correct makefiles that
- * contain only correct command lines there is no difference. But if a command
- * line, for example, is: 'if -foo bar' and there is an executable named 'if'
- * in the path, the first possibility would execute that 'if' while in the
- * second case the shell would give an error. Histerically only a small
- * subset of the builtins and no reserved words where given in the list which
- * corresponds roughly to the first variant. So go with this but add missing
- * words.
- */
-#define CSH_BUILTINS \
- "alias cd eval exec exit read set ulimit unalias " \
- "umask unset wait"
-
-#define SH_BUILTINS \
- "alias cd eval exec exit read set ulimit unalias " \
- "umask unset wait"
-
-#define CSH_META "#=|^(){};&<>*?[]:$`\\@\n"
-#define SH_META "#=|^(){};&<>*?[]:$`\\\n"
-
-static const char *const shells_init[] = {
- /*
- * CSH description. The csh can do echo control by playing
- * with the setting of the 'echo' shell variable. Sadly,
- * however, it is unable to do error control nicely.
- */
- "name=csh path='" PATH_DEFSHELLDIR "/csh' "
- "quiet='unset verbose' echo='set verbose' filter='unset verbose' "
- "hasErrCtl=N check='echo \"%s\"\n' ignore='csh -c \"%s || exit 0\"' "
- "echoFlag=v errFlag=e "
- "meta='" CSH_META "' builtins='" CSH_BUILTINS "'",
-
- /*
- * SH description. Echo control is also possible and, under
- * sun UNIX anyway, one can even control error checking.
- */
- "name=sh path='" PATH_DEFSHELLDIR "/sh' "
- "quiet='set -' echo='set -v' filter='set -' "
- "hasErrCtl=Y check='set -e' ignore='set +e' "
- "echoFlag=v errFlag=e "
- "meta='" SH_META "' builtins='" SH_BUILTINS "'",
-
- /*
- * KSH description. The Korn shell has a superset of
- * the Bourne shell's functionality. There are probably builtins
- * missing here.
- */
- "name=ksh path='" PATH_DEFSHELLDIR "/ksh' "
- "quiet='set -' echo='set -v' filter='set -' "
- "hasErrCtl=Y check='set -e' ignore='set +e' "
- "echoFlag=v errFlag=e "
- "meta='" SH_META "' builtins='" SH_BUILTINS "' unsetenv=T",
-
- NULL
-};
-
-/*
- * This is the shell to which we pass all commands in the Makefile.
- * It is set by the Job_ParseShell function.
- */
-struct Shell *commandShell;
-
-/*
- * This is the list of all known shells.
- */
-static struct Shells shells = TAILQ_HEAD_INITIALIZER(shells);
-
-void ShellDump(const struct Shell *) __unused;
-
-/**
- * Helper function for sorting the builtin list alphabetically.
- */
-static int
-sort_builtins(const void *p1, const void *p2)
-{
-
- return (strcmp(*(const char* const*)p1, *(const char* const*)p2));
-}
-
-/**
- * Free a shell structure and all associated strings.
- */
-static void
-ShellFree(struct Shell *sh)
-{
-
- if (sh != NULL) {
- free(sh->name);
- free(sh->path);
- free(sh->echoOff);
- free(sh->echoOn);
- free(sh->noPrint);
- free(sh->errCheck);
- free(sh->ignErr);
- free(sh->echo);
- free(sh->exit);
- ArgArray_Done(&sh->builtins);
- free(sh->meta);
- free(sh);
- }
-}
-
-/**
- * Dump a shell specification to stderr.
- */
-void
-ShellDump(const struct Shell *sh)
-{
- int i;
-
- fprintf(stderr, "Shell %p:\n", sh);
- fprintf(stderr, " name='%s' path='%s'\n", sh->name, sh->path);
- fprintf(stderr, " hasEchoCtl=%d echoOff='%s' echoOn='%s'\n",
- sh->hasEchoCtl, sh->echoOff, sh->echoOn);
- fprintf(stderr, " noPrint='%s'\n", sh->noPrint);
- fprintf(stderr, " hasErrCtl=%d errCheck='%s' ignErr='%s'\n",
- sh->hasErrCtl, sh->errCheck, sh->ignErr);
- fprintf(stderr, " echo='%s' exit='%s'\n", sh->echo, sh->exit);
- fprintf(stderr, " builtins=%d\n", sh->builtins.argc - 1);
- for (i = 1; i < sh->builtins.argc; i++)
- fprintf(stderr, " '%s'", sh->builtins.argv[i]);
- fprintf(stderr, "\n meta='%s'\n", sh->meta);
- fprintf(stderr, " unsetenv=%d\n", sh->unsetenv);
-}
-
-/**
- * Parse a shell specification line and return the new Shell structure.
- * In case of an error a message is printed and NULL is returned.
- */
-static struct Shell *
-ShellParseSpec(const char *spec, Boolean *fullSpec)
-{
- ArgArray aa;
- struct Shell *sh;
- char *eq;
- char *keyw;
- int arg;
-
- *fullSpec = FALSE;
-
- sh = emalloc(sizeof(*sh));
- memset(sh, 0, sizeof(*sh));
- ArgArray_Init(&sh->builtins);
-
- /*
- * Parse the specification by keyword but skip the first word
- */
- brk_string(&aa, spec, TRUE);
-
- for (arg = 1; arg < aa.argc; arg++) {
- /*
- * Split keyword and value
- */
- keyw = aa.argv[arg];
- if ((eq = strchr(keyw, '=')) == NULL) {
- Parse_Error(PARSE_FATAL, "missing '=' in shell "
- "specification keyword '%s'", keyw);
- ArgArray_Done(&aa);
- ShellFree(sh);
- return (NULL);
- }
- *eq++ = '\0';
-
- if (strcmp(keyw, "path") == 0) {
- free(sh->path);
- sh->path = estrdup(eq);
- } else if (strcmp(keyw, "name") == 0) {
- free(sh->name);
- sh->name = estrdup(eq);
- } else if (strcmp(keyw, "quiet") == 0) {
- free(sh->echoOff);
- sh->echoOff = estrdup(eq);
- *fullSpec = TRUE;
- } else if (strcmp(keyw, "echo") == 0) {
- free(sh->echoOn);
- sh->echoOn = estrdup(eq);
- *fullSpec = TRUE;
- } else if (strcmp(keyw, "filter") == 0) {
- free(sh->noPrint);
- sh->noPrint = estrdup(eq);
- *fullSpec = TRUE;
- } else if (strcmp(keyw, "echoFlag") == 0) {
- free(sh->echo);
- sh->echo = estrdup(eq);
- *fullSpec = TRUE;
- } else if (strcmp(keyw, "errFlag") == 0) {
- free(sh->exit);
- sh->exit = estrdup(eq);
- *fullSpec = TRUE;
- } else if (strcmp(keyw, "hasErrCtl") == 0) {
- sh->hasErrCtl = (*eq == 'Y' || *eq == 'y' ||
- *eq == 'T' || *eq == 't');
- *fullSpec = TRUE;
- } else if (strcmp(keyw, "check") == 0) {
- free(sh->errCheck);
- sh->errCheck = estrdup(eq);
- *fullSpec = TRUE;
- } else if (strcmp(keyw, "ignore") == 0) {
- free(sh->ignErr);
- sh->ignErr = estrdup(eq);
- *fullSpec = TRUE;
- } else if (strcmp(keyw, "builtins") == 0) {
- ArgArray_Done(&sh->builtins);
- brk_string(&sh->builtins, eq, TRUE);
- qsort(sh->builtins.argv + 1, sh->builtins.argc - 1,
- sizeof(char *), sort_builtins);
- *fullSpec = TRUE;
- } else if (strcmp(keyw, "meta") == 0) {
- free(sh->meta);
- sh->meta = estrdup(eq);
- *fullSpec = TRUE;
- } else if (strcmp(keyw, "unsetenv") == 0) {
- sh->unsetenv = (*eq == 'Y' || *eq == 'y' ||
- *eq == 'T' || *eq == 't');
- *fullSpec = TRUE;
- } else {
- Parse_Error(PARSE_FATAL, "unknown keyword in shell "
- "specification '%s'", keyw);
- ArgArray_Done(&aa);
- ShellFree(sh);
- return (NULL);
- }
- }
- ArgArray_Done(&aa);
-
- /*
- * Some checks (could be more)
- */
- if (*fullSpec) {
- if ((sh->echoOn != NULL) ^ (sh->echoOff != NULL)) {
- Parse_Error(PARSE_FATAL, "Shell must have either both "
- "echoOff and echoOn or none of them");
- ShellFree(sh);
- return (NULL);
- }
-
- if (sh->echoOn != NULL && sh->echoOff != NULL)
- sh->hasEchoCtl = TRUE;
- }
-
- return (sh);
-}
-
-/**
- * Parse the builtin shell specifications and put them into the shell
- * list. Then select the default shell to be the current shell. This
- * is called from main() before any parsing (including MAKEFLAGS and
- * command line) is done.
- */
-void
-Shell_Init(void)
-{
- int i;
- struct Shell *sh;
- Boolean fullSpec;
-
- for (i = 0; shells_init[i] != NULL; i++) {
- sh = ShellParseSpec(shells_init[i], &fullSpec);
- TAILQ_INSERT_TAIL(&shells, sh, link);
- if (strcmp(sh->name, DEFSHELLNAME) == 0)
- commandShell = sh;
- }
-}
-
-/**
- * Find a matching shell in 'shells' given its final component.
- *
- * Results:
- * A pointer to a freshly allocated Shell structure with the contents
- * from static description or NULL if no shell with the given name
- * is found.
- */
-static struct Shell *
-ShellMatch(const char *name)
-{
- struct Shell *sh;
-
- TAILQ_FOREACH(sh, &shells, link)
- if (strcmp(sh->name, name) == 0)
- return (sh);
-
- return (NULL);
-}
-
-/**
- * Parse a shell specification and set up commandShell appropriately.
- *
- * Results:
- * TRUE if the specification was correct. FALSE otherwise.
- *
- * Side Effects:
- * commandShell points to a Shell structure.
- * created from the shell spec).
- *
- * Notes:
- * A shell specification consists of a .SHELL target, with dependency
- * operator, followed by a series of blank-separated words. Double
- * quotes can be used to use blanks in words. A backslash escapes
- * anything (most notably a double-quote and a space) and
- * provides the functionality it does in C. Each word consists of
- * keyword and value separated by an equal sign. There should be no
- * unnecessary spaces in the word. The keywords are as follows:
- * name Name of shell.
- * path Location of shell. Overrides "name" if given
- * quiet Command to turn off echoing.
- * echo Command to turn echoing on
- * filter Result of turning off echoing that shouldn't be
- * printed.
- * echoFlag Flag to turn echoing on at the start
- * errFlag Flag to turn error checking on at the start
- * hasErrCtl True if shell has error checking control
- * check Command to turn on error checking if hasErrCtl
- * is TRUE or template of command to echo a command
- * for which error checking is off if hasErrCtl is
- * FALSE.
- * ignore Command to turn off error checking if hasErrCtl
- * is TRUE or template of command to execute a
- * command so as to ignore any errors it returns if
- * hasErrCtl is FALSE.
- * builtins A space separated list of builtins. If one
- * of these builtins is detected when make wants
- * to execute a command line, the command line is
- * handed to the shell. Otherwise make may try to
- * execute the command directly. If this list is empty
- * it is assumed, that the command must always be
- * handed over to the shell.
- * meta The shell meta characters. If this is not specified
- * or empty, commands are alway passed to the shell.
- * Otherwise they are not passed when they contain
- * neither a meta character nor a builtin command.
- * unsetenv Unsetenv("ENV") before executing anything.
- */
-Boolean
-Shell_Parse(const char line[])
-{
- Boolean fullSpec;
- struct Shell *sh;
- struct Shell *match;
-
- /* parse the specification */
- if ((sh = ShellParseSpec(line, &fullSpec)) == NULL)
- return (FALSE);
-
- if (sh->path == NULL) {
- /*
- * If no path was given, the user wants one of the pre-defined
- * shells, yes? So we find the one s/he wants with the help of
- * JobMatchShell and set things up the right way.
- */
- if (sh->name == NULL) {
- Parse_Error(PARSE_FATAL,
- "Neither path nor name specified");
- ShellFree(sh);
- return (FALSE);
- }
- if (fullSpec) {
- /*
- * XXX May want to merge sh into match. But this
- * require ShellParseSpec to return information
- * which attributes actuall have been specified.
- */
- Parse_Error(PARSE_FATAL, "No path specified");
- ShellFree(sh);
- return (FALSE);
- }
- if ((match = ShellMatch(sh->name)) == NULL) {
- Parse_Error(PARSE_FATAL, "%s: no matching shell",
- sh->name);
- ShellFree(sh);
- return (FALSE);
- }
- ShellFree(sh);
- commandShell = match;
-
- return (TRUE);
- }
-
- /*
- * The user provided a path. If s/he gave nothing else
- * (fullSpec is FALSE), try and find a matching shell in the
- * ones we know of. Else we just take the specification at its
- * word and copy it to a new location. In either case, we need
- * to record the path the user gave for the shell.
- */
- if (sh->name == NULL) {
- /* get the base name as the name */
- if ((sh->name = strrchr(sh->path, '/')) == NULL) {
- sh->name = estrdup(sh->path);
- } else {
- sh->name = estrdup(sh->name + 1);
- }
- }
-
- if (!fullSpec) {
- if ((match = ShellMatch(sh->name)) == NULL) {
- Parse_Error(PARSE_FATAL,
- "%s: no matching shell", sh->name);
- ShellFree(sh);
- return (FALSE);
- }
-
- /* set the patch on the matching shell */
- free(match->path);
- match->path = sh->path;
- sh->path = NULL;
-
- ShellFree(sh);
- commandShell = match;
- return (TRUE);
- }
-
- TAILQ_INSERT_HEAD(&shells, sh, link);
-
- /* set the new shell */
- commandShell = sh;
- return (TRUE);
-}
diff --git a/usr.bin/make/shell.h b/usr.bin/make/shell.h
deleted file mode 100644
index a317058..0000000
--- a/usr.bin/make/shell.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*-
- * Copyright (c) 1988, 1989, 1990, 1993
- * The Regents of the University of California. All rights reserved.
- * Copyright (c) 1988, 1989 by Adam de Boor
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * $FreeBSD$
- */
-
-#ifndef shell_h_6002e3b8
-#define shell_h_6002e3b8
-
-#include <sys/queue.h>
-
-#include "str.h"
-#include "util.h"
-
-/**
- * Shell Specifications:
- *
- * Some special stuff goes on if a shell doesn't have error control. In such
- * a case, errCheck becomes a printf template for echoing the command,
- * should echoing be on and ignErr becomes another printf template for
- * executing the command while ignoring the return status. If either of these
- * strings is empty when hasErrCtl is FALSE, the command will be executed
- * anyway as is and if it causes an error, so be it.
- */
-struct Shell {
- TAILQ_ENTRY(Shell) link; /* link all shell descriptions */
-
- /*
- * the name of the shell. For Bourne and C shells, this is used
- * only to find the shell description when used as the single
- * source of a .SHELL target.
- */
- char *name;
-
- char *path; /* full path to the shell */
-
- /* True if both echoOff and echoOn defined */
- Boolean hasEchoCtl;
-
- char *echoOff; /* command to turn off echo */
- char *echoOn; /* command to turn it back on */
-
- /*
- * What the shell prints, and its length, when given the
- * echo-off command. This line will not be printed when
- * received from the shell. This is usually the command which
- * was executed to turn off echoing
- */
- char *noPrint;
-
- /* set if can control error checking for individual commands */
- Boolean hasErrCtl;
-
- /* string to turn error checking on */
- char *errCheck;
-
- /* string to turn off error checking */
- char *ignErr;
-
- char *echo; /* command line flag: echo commands */
- char *exit; /* command line flag: exit on error */
-
- ArgArray builtins; /* ordered list of shell builtins */
- char *meta; /* shell meta characters */
-
- Boolean unsetenv; /* unsetenv("ENV") before exec */
-};
-TAILQ_HEAD(Shells, Shell);
-
-extern struct Shell *commandShell;
-
-void Shell_Init(void);
-Boolean Shell_Parse(const char []);
-
-#endif /* shell_h_6002e3b8 */
diff --git a/usr.bin/make/str.c b/usr.bin/make/str.c
deleted file mode 100644
index ce4ac05..0000000
--- a/usr.bin/make/str.c
+++ /dev/null
@@ -1,559 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)str.c 5.8 (Berkeley) 6/1/90
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "buf.h"
-#include "str.h"
-#include "util.h"
-
-/**
- * Initialize the argument array object. The array is initially
- * eight positions, and will be expanded as necessary. The first
- * position is set to NULL since everything ignores it. We allocate
- * (size + 1) since we need space for the terminating NULL. The
- * buffer is set to NULL, since no common buffer is allocated yet.
- */
-void
-ArgArray_Init(ArgArray *aa)
-{
-
- aa->size = 8;
- aa->argv = emalloc((aa->size + 1) * sizeof(char *));
- aa->argc = 0;
- aa->argv[aa->argc++] = NULL;
- aa->len = 0;
- aa->buffer = NULL;
-}
-
-/**
- * Cleanup the memory allocated for in the argument array object.
- */
-void
-ArgArray_Done(ArgArray *aa)
-{
-
- if (aa->buffer == NULL) {
- int i;
- /* args are individually allocated */
- for (i = 0; i < aa->argc; ++i) {
- if (aa->argv[i]) {
- free(aa->argv[i]);
- aa->argv[i] = NULL;
- }
- }
- } else {
- /* args are part of a single allocation */
- free(aa->buffer);
- aa->buffer = NULL;
- }
- free(aa->argv);
- aa->argv = NULL;
- aa->argc = 0;
- aa->size = 0;
-}
-
-/*-
- * str_concat --
- * concatenate the two strings, inserting a space or slash between them.
- *
- * returns --
- * the resulting string in allocated space.
- */
-char *
-str_concat(const char *s1, const char *s2, int flags)
-{
- int len1, len2;
- char *result;
-
- /* get the length of both strings */
- len1 = strlen(s1);
- len2 = strlen(s2);
-
- /* allocate length plus separator plus EOS */
- result = emalloc(len1 + len2 + 2);
-
- /* copy first string into place */
- memcpy(result, s1, len1);
-
- /* add separator character */
- if (flags & STR_ADDSPACE) {
- result[len1] = ' ';
- ++len1;
- } else if (flags & STR_ADDSLASH) {
- result[len1] = '/';
- ++len1;
- }
-
- /* copy second string plus EOS into place */
- memcpy(result + len1, s2, len2 + 1);
-
- return (result);
-}
-
-/**
- * Fracture a string into an array of words (as delineated by tabs or
- * spaces) taking quotation marks into account. Leading tabs/spaces
- * are ignored.
- */
-void
-brk_string(ArgArray *aa, const char str[], Boolean expand)
-{
- char inquote;
- char *start;
- char *arg;
-
- /* skip leading space chars. */
- for (; *str == ' ' || *str == '\t'; ++str)
- continue;
-
- ArgArray_Init(aa);
-
- aa->buffer = estrdup(str);
-
- arg = aa->buffer;
- start = arg;
- inquote = '\0';
-
- /*
- * copy the string; at the same time, parse backslashes,
- * quotes and build the argument list.
- */
- for (;;) {
- switch (str[0]) {
- case '"':
- case '\'':
- if (inquote == '\0') {
- inquote = str[0];
- if (expand)
- break;
- if (start == NULL)
- start = arg;
- } else if (inquote == str[0]) {
- inquote = '\0';
- /* Don't miss "" or '' */
- if (start == NULL)
- start = arg;
- if (expand)
- break;
- } else {
- /* other type of quote found */
- if (start == NULL)
- start = arg;
- }
- *arg++ = str[0];
- break;
- case ' ':
- case '\t':
- case '\n':
- if (inquote) {
- if (start == NULL)
- start = arg;
- *arg++ = str[0];
- break;
- }
- if (start == NULL)
- break;
- /* FALLTHROUGH */
- case '\0':
- /*
- * end of a token -- make sure there's enough argv
- * space and save off a pointer.
- */
- if (aa->argc == aa->size) {
- aa->size *= 2; /* ramp up fast */
- aa->argv = erealloc(aa->argv,
- (aa->size + 1) * sizeof(char *));
- }
-
- *arg++ = '\0';
- if (start == NULL) {
- aa->argv[aa->argc] = start;
- return;
- }
- if (str[0] == '\n' || str[0] == '\0') {
- aa->argv[aa->argc++] = start;
- aa->argv[aa->argc] = NULL;
- return;
- } else {
- aa->argv[aa->argc++] = start;
- start = NULL;
- break;
- }
- case '\\':
- if (start == NULL)
- start = arg;
- if (expand) {
- switch (str[1]) {
- case '\0':
- case '\n':
- /* hmmm; fix it up as best we can */
- *arg++ = '\\';
- break;
- case 'b':
- *arg++ = '\b';
- ++str;
- break;
- case 'f':
- *arg++ = '\f';
- ++str;
- break;
- case 'n':
- *arg++ = '\n';
- ++str;
- break;
- case 'r':
- *arg++ = '\r';
- ++str;
- break;
- case 't':
- *arg++ = '\t';
- ++str;
- break;
- default:
- *arg++ = str[1];
- ++str;
- break;
- }
- } else {
- *arg++ = str[0];
- if (str[1] != '\0') {
- ++str;
- *arg++ = str[0];
- }
- }
- break;
- default:
- if (start == NULL)
- start = arg;
- *arg++ = str[0];
- break;
- }
- ++str;
- }
-}
-
-/*
- * Quote a string for appending it to MAKEFLAGS. According to Posix the
- * kind of quoting here is implementation-defined. This quoting must ensure
- * that the parsing of MAKEFLAGS's contents in a sub-shell yields the same
- * options, option arguments and macro definitions as in the calling make.
- * We simply quote all blanks, which according to Posix are space and tab
- * in the POSIX locale. Don't use isblank because in that case makes with
- * different locale settings could not communicate. We must also quote
- * backslashes obviously.
- */
-char *
-MAKEFLAGS_quote(const char *str)
-{
- char *ret, *q;
- const char *p;
-
- /* assume worst case - everything has to be quoted */
- ret = emalloc(strlen(str) * 2 + 1);
-
- p = str;
- q = ret;
- while (*p != '\0') {
- switch (*p) {
-
- case ' ':
- case '\t':
- *q++ = '\\';
- break;
-
- default:
- break;
- }
- *q++ = *p++;
- }
- *q++ = '\0';
- return (ret);
-}
-
-void
-MAKEFLAGS_break(ArgArray *aa, const char str[])
-{
- char *arg;
- char *start;
-
- ArgArray_Init(aa);
-
- aa->buffer = strdup(str);
-
- arg = aa->buffer;
- start = NULL;
-
- for (;;) {
- switch (str[0]) {
- case ' ':
- case '\t':
- /* word separator */
- if (start == NULL) {
- /* not in a word */
- str++;
- continue;
- }
- /* FALLTHRU */
- case '\0':
- if (aa->argc == aa->size) {
- aa->size *= 2;
- aa->argv = erealloc(aa->argv,
- (aa->size + 1) * sizeof(char *));
- }
-
- *arg++ = '\0';
- if (start == NULL) {
- aa->argv[aa->argc] = start;
- return;
- }
- if (str[0] == '\0') {
- aa->argv[aa->argc++] = start;
- aa->argv[aa->argc] = NULL;
- return;
- } else {
- aa->argv[aa->argc++] = start;
- start = NULL;
- str++;
- continue;
- }
-
- case '\\':
- if (str[1] == ' ' || str[1] == '\t')
- str++;
- break;
-
- default:
- break;
- }
- if (start == NULL)
- start = arg;
- *arg++ = *str++;
- }
-}
-
-/*
- * Str_Match --
- *
- * See if a particular string matches a particular pattern.
- *
- * Results: Non-zero is returned if string matches pattern, 0 otherwise. The
- * matching operation permits the following special characters in the
- * pattern: *?\[] (see the man page for details on what these mean).
- *
- * Side effects: None.
- */
-int
-Str_Match(const char *string, const char *pattern)
-{
- char c2;
-
- for (;;) {
- /*
- * See if we're at the end of both the pattern and the
- * string. If, we succeeded. If we're at the end of the
- * pattern but not at the end of the string, we failed.
- */
- if (*pattern == 0)
- return (!*string);
- if (*string == 0 && *pattern != '*')
- return (0);
- /*
- * Check for a "*" as the next pattern character. It matches
- * any substring. We handle this by calling ourselves
- * recursively for each postfix of string, until either we
- * match or we reach the end of the string.
- */
- if (*pattern == '*') {
- pattern += 1;
- if (*pattern == 0)
- return (1);
- while (*string != 0) {
- if (Str_Match(string, pattern))
- return (1);
- ++string;
- }
- return (0);
- }
- /*
- * Check for a "?" as the next pattern character. It matches
- * any single character.
- */
- if (*pattern == '?')
- goto thisCharOK;
- /*
- * Check for a "[" as the next pattern character. It is
- * followed by a list of characters that are acceptable, or
- * by a range (two characters separated by "-").
- */
- if (*pattern == '[') {
- ++pattern;
- for (;;) {
- if ((*pattern == ']') || (*pattern == 0))
- return (0);
- if (*pattern == *string)
- break;
- if (pattern[1] == '-') {
- c2 = pattern[2];
- if (c2 == 0)
- return (0);
- if ((*pattern <= *string) &&
- (c2 >= *string))
- break;
- if ((*pattern >= *string) &&
- (c2 <= *string))
- break;
- pattern += 2;
- }
- ++pattern;
- }
- while ((*pattern != ']') && (*pattern != 0))
- ++pattern;
- goto thisCharOK;
- }
- /*
- * If the next pattern character is '/', just strip off the
- * '/' so we do exact matching on the character that follows.
- */
- if (*pattern == '\\') {
- ++pattern;
- if (*pattern == 0)
- return (0);
- }
- /*
- * There's no special character. Just make sure that the
- * next characters of each string match.
- */
- if (*pattern != *string)
- return (0);
-thisCharOK: ++pattern;
- ++string;
- }
-}
-
-
-/**
- * Str_SYSVMatch
- * Check word against pattern for a match (% is wild),
- *
- * Results:
- * Returns the beginning position of a match or null. The number
- * of characters matched is returned in len.
- */
-const char *
-Str_SYSVMatch(const char *word, const char *pattern, int *len)
-{
- const char *m, *p, *w;
-
- p = pattern;
- w = word;
-
- if (*w == '\0') {
- /* Zero-length word cannot be matched against */
- *len = 0;
- return (NULL);
- }
-
- if (*p == '\0') {
- /* Null pattern is the whole string */
- *len = strlen(w);
- return (w);
- }
-
- if ((m = strchr(p, '%')) != NULL) {
- /* check that the prefix matches */
- for (; p != m && *w && *w == *p; w++, p++)
- continue;
-
- if (p != m)
- return (NULL); /* No match */
-
- if (*++p == '\0') {
- /* No more pattern, return the rest of the string */
- *len = strlen(w);
- return (w);
- }
- }
-
- m = w;
-
- /* Find a matching tail */
- do
- if (strcmp(p, w) == 0) {
- *len = w - m;
- return (m);
- }
- while (*w++ != '\0');
-
- return (NULL);
-}
-
-
-/**
- * Str_SYSVSubst
- * Substitute '%' on the pattern with len characters from src.
- * If the pattern does not contain a '%' prepend len characters
- * from src.
- *
- * Side Effects:
- * Places result on buf
- */
-void
-Str_SYSVSubst(Buffer *buf, const char *pat, const char *src, int len)
-{
- const char *m;
-
- if ((m = strchr(pat, '%')) != NULL) {
- /* Copy the prefix */
- Buf_AppendRange(buf, pat, m);
- /* skip the % */
- pat = m + 1;
- }
-
- /* Copy the pattern */
- Buf_AddBytes(buf, len, (const Byte *)src);
-
- /* append the rest */
- Buf_Append(buf, pat);
-}
diff --git a/usr.bin/make/str.h b/usr.bin/make/str.h
deleted file mode 100644
index e8cf10c..0000000
--- a/usr.bin/make/str.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * $FreeBSD$
- */
-
-#ifndef str_h_44db59e6
-#define str_h_44db59e6
-
-#include "util.h"
-
-struct Buffer;
-
-/**
- * An array of c-strings. The pointers stored in argv, point to
- * strings stored in buffer.
- */
-typedef struct ArgArray {
- int size; /* size of argv array */
- int argc; /* strings referenced in argv */
- char **argv; /* array of string pointers */
- size_t len; /* size of buffer */
- char *buffer; /* data buffer */
-} ArgArray;
-
-/*
- * These constants are all used by the Str_Concat function to decide how the
- * final string should look. If STR_ADDSPACE is given, a space will be
- * placed between the two strings. If STR_ADDSLASH is given, a '/' will
- * be used instead of a space. If neither is given, no intervening characters
- * will be placed between the two strings in the final output.
- */
-#define STR_ADDSPACE 0x01 /* add a space when Str_Concat'ing */
-#define STR_ADDSLASH 0x04 /* add a slash when Str_Concat'ing */
-
-void ArgArray_Init(ArgArray *);
-void ArgArray_Done(ArgArray *);
-
-char *str_concat(const char *, const char *, int);
-void brk_string(ArgArray *, const char [], Boolean);
-char *MAKEFLAGS_quote(const char *);
-void MAKEFLAGS_break(ArgArray *, const char []);
-int Str_Match(const char *, const char *);
-const char *Str_SYSVMatch(const char *, const char *, int *);
-void Str_SYSVSubst(struct Buffer *, const char *, const char *, int);
-
-#endif /* str_h_44db59e6 */
diff --git a/usr.bin/make/suff.c b/usr.bin/make/suff.c
deleted file mode 100644
index bed6c7a..0000000
--- a/usr.bin/make/suff.c
+++ /dev/null
@@ -1,2205 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)suff.c 8.4 (Berkeley) 3/21/94
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*-
- * suff.c --
- * Functions to maintain suffix lists and find implicit dependents
- * using suffix transformation rules
- *
- * Interface:
- * Suff_Init Initialize all things to do with suffixes.
- *
- * Suff_DoPaths This function is used to make life easier
- * when searching for a file according to its
- * suffix. It takes the global search path,
- * as defined using the .PATH: target, and appends
- * its directories to the path of each of the
- * defined suffixes, as specified using
- * .PATH<suffix>: targets. In addition, all
- * directories given for suffixes labeled as
- * include files or libraries, using the .INCLUDES
- * or .LIBS targets, are played with using
- * Dir_MakeFlags to create the .INCLUDES and
- * .LIBS global variables.
- *
- * Suff_ClearSuffixes Clear out all the suffixes and defined
- * transformations.
- *
- * Suff_IsTransform Return TRUE if the passed string is the lhs
- * of a transformation rule.
- *
- * Suff_AddSuffix Add the passed string as another known suffix.
- *
- * Suff_GetPath Return the search path for the given suffix.
- *
- * Suff_AddInclude Mark the given suffix as denoting an include
- * file.
- *
- * Suff_AddLib Mark the given suffix as denoting a library.
- *
- * Suff_AddTransform Add another transformation to the suffix
- * graph. Returns GNode suitable for framing, I
- * mean, tacking commands, attributes, etc. on.
- *
- * Suff_SetNull Define the suffix to consider the suffix of
- * any file that doesn't have a known one.
- *
- * Suff_FindDeps Find implicit sources for and the location of
- * a target based on its suffix. Returns the
- * bottom-most node added to the graph or NULL
- * if the target had no implicit sources.
- */
-
-#include <sys/queue.h>
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "arch.h"
-#include "buf.h"
-#include "config.h"
-#include "dir.h"
-#include "globals.h"
-#include "GNode.h"
-#include "lst.h"
-#include "make.h"
-#include "parse.h"
-#include "str.h"
-#include "suff.h"
-#include "targ.h"
-#include "util.h"
-#include "var.h"
-
-/* Lst of suffixes */
-static Lst sufflist = Lst_Initializer(sufflist);
-
-/* Lst of suffixes to be cleaned */
-static Lst suffClean = Lst_Initializer(suffClean);
-
-/* Lst of sources */
-static Lst srclist = Lst_Initializer(srclist);
-
-/* Lst of transformation rules */
-static Lst transforms = Lst_Initializer(transforms);
-
-/* Counter for assigning suffix numbers */
-static int sNum = 0;
-
-/*
- * Structure describing an individual suffix.
- */
-typedef struct Suff {
- char *name; /* The suffix itself */
- int nameLen; /* Length of the suffix */
- short flags; /* Type of suffix */
-#define SUFF_INCLUDE 0x01 /* One which is #include'd */
-#define SUFF_LIBRARY 0x02 /* One which contains a library */
-#define SUFF_NULL 0x04 /* The empty suffix */
- struct Path searchPath; /* Path for files with this suffix */
- int sNum; /* The suffix number */
- int refCount; /* Reference count of list membership */
- Lst parents; /* Suffixes we have a transformation to */
- Lst children; /* Suffixes we have a transformation from */
- Lst ref; /* List of lists this suffix is referenced */
-} Suff;
-
-/*
- * Structure used in the search for implied sources.
- */
-typedef struct Src {
- char *file; /* The file to look for */
- char *pref; /* Prefix from which file was formed */
- Suff *suff; /* The suffix on the file */
- struct Src *parent; /* The Src for which this is a source */
- GNode *node; /* The node describing the file */
- int children; /* Count of existing children (so we don't free
- * this thing too early or never nuke it) */
-#ifdef DEBUG_SRC
- Lst cp; /* Debug; children list */
-#endif
-} Src;
-
-/* The NULL suffix for this run */
-static Suff *suffNull;
-
-/* The empty suffix required for POSIX single-suffix transformation rules */
-static Suff *emptySuff;
-
-static void SuffFindDeps(GNode *, Lst *);
-
-
-/*-
- *-----------------------------------------------------------------------
- * SuffSuffIsSuffix --
- * See if suff is a suffix of str.
- *
- * Results:
- * NULL if it ain't, pointer to character in str before suffix if
- * it is.
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
- */
-static char *
-SuffSuffIsSuffix(const Suff *s, char *str)
-{
- const char *p1; /* Pointer into suffix name */
- char *p2; /* Pointer into string being examined */
- size_t len;
-
- len = strlen(str);
- p1 = s->name + s->nameLen;
- p2 = str + len;
-
- while (p1 >= s->name && len > 0 && *p1 == *p2) {
- p1--;
- p2--;
- len--;
- }
-
- return (p1 == s->name - 1 ? p2 : NULL);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * SuffSuffFind --
- * Find a suffix given its name.
- *
- * Results:
- * The suffix or NULL.
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
- */
-static Suff *
-SuffSuffFind(const char *s)
-{
- LstNode *ln;
-
- LST_FOREACH(ln, &sufflist) {
- if (strcmp(s, ((const Suff *)Lst_Datum(ln))->name) == 0)
- return (Lst_Datum(ln));
- }
- return (NULL);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * SuffTransFind
- * Find a transform.
- *
- * Results:
- * transform or NULL.
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
- */
-static GNode *
-SuffTransFind(const char *name)
-{
- LstNode *ln;
-
- LST_FOREACH(ln, &transforms) {
- if (strcmp(name, ((const GNode *)Lst_Datum(ln))->name) == 0)
- return (Lst_Datum(ln));
- }
- return (NULL);
-}
-
- /*********** Maintenance Functions ************/
-
-#if 0
-/*
- * Keep this function for now until it is clear why a .SUFFIXES: doesn't
- * actually delete the suffixes but just puts them on the suffClean list.
- */
-/*-
- *-----------------------------------------------------------------------
- * SuffFree --
- * Free up all memory associated with the given suffix structure.
- *
- * Results:
- * none
- *
- * Side Effects:
- * the suffix entry is detroyed
- *-----------------------------------------------------------------------
- */
-static void
-SuffFree(void *sp)
-{
- Suff *s = sp;
-
- if (s == suffNull)
- suffNull = NULL;
-
- if (s == emptySuff)
- emptySuff = NULL;
-
- Lst_Destroy(&s->ref, NOFREE);
- Lst_Destroy(&s->children, NOFREE);
- Lst_Destroy(&s->parents, NOFREE);
- Lst_Destroy(&s->searchPath, Dir_Destroy);
-
- free(s->name);
- free(s);
-}
-#endif
-
-/*-
- *-----------------------------------------------------------------------
- * SuffRemove --
- * Remove the suffix into the list
- *
- * Results:
- * None
- *
- * Side Effects:
- * The reference count for the suffix is decremented
- *-----------------------------------------------------------------------
- */
-static void
-SuffRemove(Lst *l, Suff *s)
-{
- LstNode *ln = Lst_Member(l, s);
-
- if (ln != NULL) {
- Lst_Remove(l, ln);
- s->refCount--;
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * SuffInsert --
- * Insert the suffix into the list keeping the list ordered by suffix
- * numbers.
- *
- * Results:
- * None
- *
- * Side Effects:
- * The reference count of the suffix is incremented
- *-----------------------------------------------------------------------
- */
-static void
-SuffInsert(Lst *l, Suff *s)
-{
- LstNode *ln; /* current element in l we're examining */
- Suff *s2; /* the suffix descriptor in this element */
-
- s2 = NULL;
- for (ln = Lst_First(l); ln != NULL; ln = Lst_Succ(ln)) {
- s2 = Lst_Datum(ln);
- if (s2->sNum >= s->sNum)
- break;
- }
- if (s2 == NULL) {
- DEBUGF(SUFF, ("inserting an empty list?..."));
- }
-
- DEBUGF(SUFF, ("inserting %s(%d)...", s->name, s->sNum));
- if (ln == NULL) {
- DEBUGF(SUFF, ("at end of list\n"));
- Lst_AtEnd(l, s);
- s->refCount++;
- Lst_AtEnd(&s->ref, l);
- } else if (s2->sNum != s->sNum) {
- DEBUGF(SUFF, ("before %s(%d)\n", s2->name, s2->sNum));
- Lst_Insert(l, ln, s);
- s->refCount++;
- Lst_AtEnd(&s->ref, l);
- } else {
- DEBUGF(SUFF, ("already there\n"));
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Suff_ClearSuffixes --
- * This is gross. Nuke the list of suffixes but keep all transformation
- * rules around. The transformation graph is destroyed in this process,
- * but we leave the list of rules so when a new graph is formed the rules
- * will remain.
- * This function is called from the parse module when a
- * .SUFFIXES:\n line is encountered.
- *
- * Results:
- * none
- *
- * Side Effects:
- * the sufflist and its graph nodes are destroyed
- *-----------------------------------------------------------------------
- */
-void
-Suff_ClearSuffixes(void)
-{
-
- Lst_Concat(&suffClean, &sufflist, LST_CONCLINK);
-
- sNum = 1;
- suffNull = emptySuff;
- /*
- * Clear suffNull's children list (the other suffixes are built new, but
- * suffNull is used as is).
- * NOFREE is used because all suffixes are are on the suffClean list.
- * suffNull should not have parents.
- */
- Lst_Destroy(&suffNull->children, NOFREE);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * SuffParseTransform --
- * Parse a transformation string to find its two component suffixes.
- *
- * Results:
- * TRUE if the string is a valid transformation and FALSE otherwise.
- *
- * Side Effects:
- * The passed pointers are overwritten.
- *
- *-----------------------------------------------------------------------
- */
-static Boolean
-SuffParseTransform(char *str, Suff **srcPtr, Suff **targPtr)
-{
- LstNode *srcLn; /* element in suffix list of trans source*/
- Suff *src; /* Source of transformation */
- char *str2; /* Extra pointer (maybe target suffix) */
- LstNode *singleLn; /* element in suffix list of any suffix
- * that exactly matches str */
- Suff *single = NULL; /* Source of possible transformation to
- * null suffix */
-
- singleLn = NULL;
-
- /*
- * Loop looking first for a suffix that matches the start of the
- * string and then for one that exactly matches the rest of it. If
- * we can find two that meet these criteria, we've successfully
- * parsed the string.
- */
- srcLn = Lst_First(&sufflist);
- for (;;) {
- /* advance to next possible suffix */
- while (srcLn != NULL) {
- src = Lst_Datum(srcLn);
- if (strncmp(str, src->name, strlen(src->name)) == 0)
- break;
- srcLn = LST_NEXT(srcLn);
- }
-
- if (srcLn == NULL) {
- /*
- * Ran out of source suffixes -- no such rule
- */
- if (singleLn != NULL) {
- /*
- * Not so fast Mr. Smith! There was a suffix
- * that encompassed the entire string, so we
- * assume it was a transformation to the null
- * suffix (thank you POSIX). We still prefer to
- * find a double rule over a singleton, hence we
- * leave this check until the end.
- *
- * XXX: Use emptySuff over suffNull?
- */
- *srcPtr = single;
- *targPtr = suffNull;
- return (TRUE);
- }
- return (FALSE);
- }
- str2 = str + src->nameLen;
- if (*str2 == '\0') {
- single = src;
- singleLn = srcLn;
- } else {
-
- *targPtr = SuffSuffFind(str2);
- if (*targPtr != NULL) {
- *srcPtr = src;
- return (TRUE);
- }
- }
- /* next one */
- srcLn = LST_NEXT(srcLn);
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Suff_IsTransform --
- * Return TRUE if the given string is a transformation rule
- *
- *
- * Results:
- * TRUE if the string is a concatenation of two known suffixes.
- * FALSE otherwise
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
- */
-Boolean
-Suff_IsTransform(char *str)
-{
- Suff *src, *targ;
-
- return (SuffParseTransform(str, &src, &targ));
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Suff_AddTransform --
- * Add the transformation rule described by the line to the
- * list of rules and place the transformation itself in the graph
- *
- * Results:
- * The node created for the transformation in the transforms list
- *
- * Side Effects:
- * The node is placed on the end of the transforms Lst and links are
- * made between the two suffixes mentioned in the target name
- *-----------------------------------------------------------------------
- */
-GNode *
-Suff_AddTransform(char *line)
-{
- GNode *gn; /* GNode of transformation rule */
- Suff *s; /* source suffix */
- Suff *t; /* target suffix */
-
- s = t = NULL; /* silence gcc */
- gn = SuffTransFind(line);
- if (gn == NULL) {
- /*
- * Make a new graph node for the transformation.
- * It will be filled in by the Parse module.
- */
- gn = Targ_NewGN(line);
- Lst_AtEnd(&transforms, gn);
- } else {
- /*
- * New specification for transformation rule. Just nuke the
- * old list of commands so they can be filled in again...
- * We don't actually free the commands themselves, because a
- * given command can be attached to several different
- * transformations.
- */
- Lst_Destroy(&gn->commands, NOFREE);
- Lst_Destroy(&gn->children, NOFREE);
- }
-
- gn->type = OP_TRANSFORM;
-
- SuffParseTransform(line, &s, &t);
-
- /*
- * link the two together in the proper relationship and order
- */
- DEBUGF(SUFF, ("defining transformation from `%s' to `%s'\n",
- s->name, t->name));
- SuffInsert(&t->children, s);
- SuffInsert(&s->parents, t);
-
- return (gn);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Suff_EndTransform --
- * Handle the finish of a transformation definition, removing the
- * transformation from the graph if it has neither commands nor
- * sources. This is called from the Parse module at the end of
- * a dependency block.
- *
- * Side Effects:
- * If the node has no commands or children, the children and parents
- * lists of the affected suffices are altered.
- *
- *-----------------------------------------------------------------------
- */
-void
-Suff_EndTransform(const GNode *gn)
-{
- Suff *s, *t;
-
- if (!Lst_IsEmpty(&gn->commands) || !Lst_IsEmpty(&gn->children)) {
- DEBUGF(SUFF, ("transformation %s complete\n", gn->name));
- return;
- }
-
- /*
- * SuffParseTransform() may fail for special rules which are not
- * actual transformation rules (e.g., .DEFAULT).
- */
- if (!SuffParseTransform(gn->name, &s, &t))
- return;
-
- DEBUGF(SUFF, ("deleting transformation from `%s' to `%s'\n",
- s->name, t->name));
-
- /*
- * Remove the source from the target's children list. We check
- * for a NULL return to handle a beanhead saying something like
- * .c.o .c.o:
- *
- * We'll be called twice when the next target is seen, but .c
- * and .o are only linked once...
- */
- SuffRemove(&t->children, s);
-
- /*
- * Remove the target from the source's parents list
- */
- SuffRemove(&s->parents, t);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * SuffRebuildGraph --
- * Called from Suff_AddSuffix via LST_FOREACH to search through the
- * list of existing transformation rules and rebuild the transformation
- * graph when it has been destroyed by Suff_ClearSuffixes. If the
- * given rule is a transformation involving this suffix and another,
- * existing suffix, the proper relationship is established between
- * the two.
- *
- * Side Effects:
- * The appropriate links will be made between this suffix and
- * others if transformation rules exist for it.
- *
- *-----------------------------------------------------------------------
- */
-static void
-SuffRebuildGraph(const GNode *transform, Suff *s)
-{
- char *cp;
- Suff *s2 = NULL;
-
- /*
- * First see if it is a transformation from this suffix.
- */
- if (strncmp(transform->name, s->name, strlen(s->name)) == 0) {
- cp = transform->name + strlen(s->name);
-
- if (cp[0] == '\0') /* null rule */
- s2 = suffNull;
- else
- s2 = SuffSuffFind(cp);
- if (s2 != NULL) {
- /*
- * Found target. Link in and return, since it can't be
- * anything else.
- */
- SuffInsert(&s2->children, s);
- SuffInsert(&s->parents, s2);
- return;
- }
- }
-
- /*
- * Not from, maybe to?
- */
- cp = SuffSuffIsSuffix(s, transform->name);
- if (cp != NULL) {
- /*
- * Null-terminate the source suffix in order to find it.
- */
- cp[1] = '\0';
- s2 = SuffSuffFind(transform->name);
-
- /*
- * Replace the start of the target suffix
- */
- cp[1] = s->name[0];
- if (s2 != NULL) {
- /*
- * Found it -- establish the proper relationship
- */
- SuffInsert(&s->children, s2);
- SuffInsert(&s2->parents, s);
- }
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Suff_AddSuffix --
- * Add the suffix in string to the end of the list of known suffixes.
- * Should we restructure the suffix graph? Make doesn't...
- *
- * Results:
- * None
- *
- * Side Effects:
- * A GNode is created for the suffix and a Suff structure is created and
- * added to the suffixes list unless the suffix was already known.
- *-----------------------------------------------------------------------
- */
-void
-Suff_AddSuffix(char *str)
-{
- Suff *s; /* new suffix descriptor */
- LstNode *ln;
-
- if (SuffSuffFind(str) != NULL)
- /*
- * Already known
- */
- return;
-
- s = emalloc(sizeof(Suff));
-
- s->name = estrdup(str);
- s->nameLen = strlen(s->name);
- TAILQ_INIT(&s->searchPath);
- Lst_Init(&s->children);
- Lst_Init(&s->parents);
- Lst_Init(&s->ref);
- s->sNum = sNum++;
- s->flags = 0;
- s->refCount = 0;
-
- Lst_AtEnd(&sufflist, s);
-
- /*
- * Look for any existing transformations from or to this suffix.
- * XXX: Only do this after a Suff_ClearSuffixes?
- */
- LST_FOREACH(ln, &transforms)
- SuffRebuildGraph(Lst_Datum(ln), s);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Suff_GetPath --
- * Return the search path for the given suffix, if it's defined.
- *
- * Results:
- * The searchPath for the desired suffix or NULL if the suffix isn't
- * defined.
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
- */
-struct Path *
-Suff_GetPath(char *sname)
-{
- Suff *s;
-
- s = SuffSuffFind(sname);
- if (s == NULL)
- return (NULL);
- return (&s->searchPath);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Suff_DoPaths --
- * Extend the search paths for all suffixes to include the default
- * search path.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The searchPath field of all the suffixes is extended by the
- * directories in dirSearchPath. If paths were specified for the
- * ".h" suffix, the directories are stuffed into a global variable
- * called ".INCLUDES" with each directory preceded by a -I. The same
- * is done for the ".a" suffix, except the variable is called
- * ".LIBS" and the flag is -L.
- *-----------------------------------------------------------------------
- */
-void
-Suff_DoPaths(void)
-{
- Suff *s;
- LstNode *ln;
- char *ptr;
- struct Path inIncludes; /* Cumulative .INCLUDES path */
- struct Path inLibs; /* Cumulative .LIBS path */
-
- TAILQ_INIT(&inIncludes);
- TAILQ_INIT(&inLibs);
-
- for (ln = Lst_First(&sufflist); ln != NULL; ln = Lst_Succ(ln)) {
- s = Lst_Datum(ln);
-#ifdef INCLUDES
- if (s->flags & SUFF_INCLUDE) {
- Path_Concat(&inIncludes, &s->searchPath);
- }
-#endif /* INCLUDES */
-#ifdef LIBRARIES
- if (s->flags & SUFF_LIBRARY) {
- Path_Concat(&inLibs, &s->searchPath);
- }
-#endif /* LIBRARIES */
- Path_Concat(&s->searchPath, &dirSearchPath);
- }
-
- ptr = Path_MakeFlags("-I", &inIncludes);
- Var_SetGlobal(".INCLUDES", ptr);
- free(ptr);
-
- ptr = Path_MakeFlags("-L", &inLibs);
- Var_SetGlobal(".LIBS", ptr);
- free(ptr);
-
- Path_Clear(&inIncludes);
- Path_Clear(&inLibs);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Suff_AddInclude --
- * Add the given suffix as a type of file which gets included.
- * Called from the parse module when a .INCLUDES line is parsed.
- * The suffix must have already been defined.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The SUFF_INCLUDE bit is set in the suffix's flags field
- *
- *-----------------------------------------------------------------------
- */
-void
-Suff_AddInclude(char *sname)
-{
- Suff *s;
-
- if ((s = SuffSuffFind(sname)) != NULL)
- s->flags |= SUFF_INCLUDE;
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Suff_AddLib --
- * Add the given suffix as a type of file which is a library.
- * Called from the parse module when parsing a .LIBS line. The
- * suffix must have been defined via .SUFFIXES before this is
- * called.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The SUFF_LIBRARY bit is set in the suffix's flags field
- *
- *-----------------------------------------------------------------------
- */
-void
-Suff_AddLib(char *sname)
-{
- Suff *s;
-
- if ((s = SuffSuffFind(sname)) != NULL)
- s->flags |= SUFF_LIBRARY;
-}
-
-/*
- * Create a new Src structure
- */
-static Src *
-SuffSrcCreate(char *file, char *prefix, Suff *suff, Src *parent, GNode *node)
-{
- Src *s;
-
- s = emalloc(sizeof(*s));
- s->file = file;
- s->pref = prefix;
- s->suff = suff;
- s->parent = parent;
- s->node = node;
- s->children = 0;
-
-#ifdef DEBUG_SRC
- Lst_Init(&s->cp);
-#endif
-
- return (s);
-}
-
- /********** Implicit Source Search Functions *********/
-
-/*-
- *-----------------------------------------------------------------------
- * SuffAddLevel --
- * Add all the children of targ as Src structures to the given list:
- * Add a suffix as a Src structure to the given list with its parent
- * being the given Src structure. If the suffix is the null suffix,
- * the prefix is used unaltered as the file name in the Src structure.
- *
- * Results:
- * None
- *
- * Side Effects:
- * Lots of structures are created and added to the list
- *-----------------------------------------------------------------------
- */
-static void
-SuffAddLevel(Lst *l, Src *targ)
-{
- LstNode *ln;
- Suff *suff;
- Src *s2;
-#ifdef DEBUG_SRC
- const LstNode *ln1;
-#endif
-
- LST_FOREACH(ln, &targ->suff->children) {
- suff = Lst_Datum(ln);
-
- if ((suff->flags & SUFF_NULL) && *suff->name != '\0') {
- /*
- * If the suffix has been marked as the NULL suffix,
- * also create a Src structure for a file with no suffix
- * attached. Two birds, and all that...
- */
- s2 = SuffSrcCreate(estrdup(targ->pref), targ->pref,
- suff, targ, NULL);
- suff->refCount++;
- targ->children += 1;
- Lst_AtEnd(l, s2);
-#ifdef DEBUG_SRC
- Lst_AtEnd(&targ->cp, s2);
- printf("1 add %p %p to %p:", targ, s2, l);
- LST_FOREACH(ln1, l)
- printf("%p ", (const void *)Lst_Datum(ln1));
- printf("\n");
-#endif
- }
- s2 = SuffSrcCreate(str_concat(targ->pref, suff->name, 0),
- targ->pref, suff, targ, NULL);
- suff->refCount++;
- targ->children += 1;
- Lst_AtEnd(l, s2);
-#ifdef DEBUG_SRC
- Lst_AtEnd(&targ->cp, s2);
- printf("2 add %p %p to %p:", targ, s2, l);
- LST_FOREACH(ln1, l)
- printf("%p ", (const void *)Lst_Datum(ln1));
- printf("\n");
-#endif
- }
-}
-
-/*-
- *----------------------------------------------------------------------
- * SuffRemoveSrc --
- * Free all src structures in list that don't have a reference count
- * XXX this actually frees only the first of these.
- *
- * Results:
- * True if a src was removed
- *
- * Side Effects:
- * The memory is free'd.
- *----------------------------------------------------------------------
- */
-static int
-SuffRemoveSrc(Lst *l)
-{
- LstNode *ln, *ln1;
- Src *s;
- int t = 0;
-
-#ifdef DEBUG_SRC
- printf("cleaning %lx: ", (unsigned long) l);
- LST_FOREACH(ln, l)
- printf("%p ", (const void *)Lst_Datum(ln));
- printf("\n");
-#endif
-
- for (ln = Lst_First(l); ln != NULL; ln = ln1) {
- ln1 = Lst_Succ(ln);
-
- s = (Src *)Lst_Datum(ln);
- if (s->children == 0) {
- free(s->file);
- if (!s->parent)
- free(s->pref);
- else {
-#ifdef DEBUG_SRC
- LstNode *ln = Lst_Member(&s->parent->cp, s);
- if (ln != NULL)
- Lst_Remove(&s->parent->cp, ln);
-#endif
- --s->parent->children;
- }
-#ifdef DEBUG_SRC
- printf("free: [l=%p] p=%p %d\n", l, s, s->children);
- Lst_Destroy(&s->cp, NOFREE);
-#endif
- Lst_Remove(l, ln);
- free(s);
- t |= 1;
- return (TRUE);
- }
-#ifdef DEBUG_SRC
- else {
- const LstNode *tln;
-
- printf("keep: [l=%p] p=%p %d: ", l, s, s->children);
- LST_FOREACH(tln, &s->cp)
- printf("%p ", (const void *)Lst_Datum(tln));
- printf("\n");
- }
-#endif
- }
-
- return (t);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * SuffFindThem --
- * Find the first existing file/target in the list srcs
- *
- * Results:
- * The lowest structure in the chain of transformations
- *
- * Side Effects:
- * None
- *-----------------------------------------------------------------------
- */
-static Src *
-SuffFindThem(Lst *srcs, Lst *slst)
-{
- Src *s; /* current Src */
- Src *rs; /* returned Src */
- char *ptr;
-
- rs = NULL;
-
- while (!Lst_IsEmpty (srcs)) {
- s = Lst_DeQueue(srcs);
-
- DEBUGF(SUFF, ("\ttrying %s...", s->file));
-
- /*
- * A file is considered to exist if either a node exists in the
- * graph for it or the file actually exists.
- */
- if (Targ_FindNode(s->file, TARG_NOCREATE) != NULL) {
-#ifdef DEBUG_SRC
- printf("remove %p from %p\n", s, srcs);
-#endif
- rs = s;
- break;
- }
-
- if ((ptr = Path_FindFile(s->file,
- &s->suff->searchPath)) != NULL) {
- rs = s;
-#ifdef DEBUG_SRC
- printf("remove %p from %p\n", s, srcs);
-#endif
- free(ptr);
- break;
- }
-
- DEBUGF(SUFF, ("not there\n"));
-
- SuffAddLevel(srcs, s);
- Lst_AtEnd(slst, s);
- }
-
- if (rs) {
- DEBUGF(SUFF, ("got it\n"));
- }
- return (rs);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * SuffFindCmds --
- * See if any of the children of the target in the Src structure is
- * one from which the target can be transformed. If there is one,
- * a Src structure is put together for it and returned.
- *
- * Results:
- * The Src structure of the "winning" child, or NULL if no such beast.
- *
- * Side Effects:
- * A Src structure may be allocated.
- *
- *-----------------------------------------------------------------------
- */
-static Src *
-SuffFindCmds(Src *targ, Lst *slst)
-{
- LstNode *ln; /* General-purpose list node */
- GNode *t; /* Target GNode */
- GNode *s; /* Source GNode */
- int prefLen;/* The length of the defined prefix */
- Suff *suff; /* Suffix on matching beastie */
- Src *ret; /* Return value */
- char *cp;
-
- t = targ->node;
- prefLen = strlen(targ->pref);
-
- for (ln = Lst_First(&t->children); ln != NULL; ln = Lst_Succ(ln)) {
- s = Lst_Datum(ln);
-
- cp = strrchr(s->name, '/');
- if (cp == NULL) {
- cp = s->name;
- } else {
- cp++;
- }
- if (strncmp(cp, targ->pref, prefLen) == 0) {
- /*
- * The node matches the prefix ok, see if it has
- * a known suffix.
- */
- suff = SuffSuffFind(&cp[prefLen]);
- if (suff != NULL) {
- /*
- * It even has a known suffix, see if there's
- * a transformation defined between the node's
- * suffix and the target's suffix.
- *
- * XXX: Handle multi-stage transformations
- * here, too.
- */
- if (Lst_Member(&suff->parents,
- targ->suff) != NULL) {
- /*
- * Hot Damn! Create a new Src structure
- * to describe this transformation
- * (making sure to duplicate the
- * source node's name so Suff_FindDeps
- * can free it again (ick)), and return
- * the new structure.
- */
- ret = SuffSrcCreate(estrdup(s->name),
- targ->pref, suff, targ, s);
- suff->refCount++;
- targ->children += 1;
-#ifdef DEBUG_SRC
- printf("3 add %p %p\n", &targ, ret);
- Lst_AtEnd(&targ->cp, ret);
-#endif
- Lst_AtEnd(slst, ret);
- DEBUGF(SUFF, ("\tusing existing source "
- "%s\n", s->name));
- return (ret);
- }
- }
- }
- }
- return (NULL);
-}
-
-/*-
- * The child node contains variable references. Expand them and return
- * a list of expansions.
- */
-static void
-SuffExpandVariables(GNode *parent, GNode *child, Lst *members)
-{
- Buffer *buf;
- char *cp;
- char *start;
-
- Lst_Init(members);
-
- DEBUGF(SUFF, ("Expanding \"%s\"...", child->name));
- buf = Var_Subst(child->name, parent, TRUE);
- cp = Buf_Data(buf);
-
- if (child->type & OP_ARCHV) {
- /*
- * Node was an archive(member) target, so we
- * want to call on the Arch module to find the
- * nodes for us, expanding variables in the
- * parent's context.
- */
- Arch_ParseArchive(&cp, members, parent);
- Buf_Destroy(buf, TRUE);
- return;
- }
- /*
- * Break the result into a vector of strings whose nodes we can find,
- * then add those nodes to the members list. Unfortunately, we can't use
- * brk_string b/c it doesn't understand about variable specifications
- * with spaces in them... XXX
- */
- for (start = cp; *start == ' ' || *start == '\t'; start++)
- ;
-
- for (cp = start; *cp != '\0'; cp++) {
- if (*cp == ' ' || *cp == '\t') {
- /*
- * White-space -- terminate element, find the node,
- * add it, skip any further spaces.
- */
- *cp++ = '\0';
- Lst_AtEnd(members, Targ_FindNode(start, TARG_CREATE));
-
- while (*cp == ' ' || *cp == '\t') {
- cp++;
- }
- /*
- * Adjust cp for increment at
- * start of loop, but set start
- * to first non-space.
- */
- start = cp--;
-
- } else if (*cp == '$') {
- /*
- * Start of a variable spec -- contact variable module
- * to find the end so we can skip over it.
- */
- char *junk;
- size_t len = 0;
- Boolean doFree;
-
- junk = Var_Parse(cp, parent, TRUE, &len, &doFree);
- if (junk != var_Error) {
- cp += len - 1;
- }
- if (doFree) {
- free(junk);
- }
-
- } else if (*cp == '\\' && *cp != '\0') {
- /*
- * Escaped something -- skip over it
- */
- cp++;
- }
- }
-
- if (cp != start) {
- /*
- * Stuff left over -- add it to the
- * list too
- */
- Lst_AtEnd(members, Targ_FindNode(start, TARG_CREATE));
- }
-
- Buf_Destroy(buf, TRUE);
-}
-
-/*-
- * The child node contains wildcards. Expand them and return a list of
- * expansions.
- */
-static void
-SuffExpandWildcards(GNode *child, Lst *members)
-{
- char *cp;
- Lst exp; /* List of expansions */
- LstNode *ln;
- struct Path *path; /* Search path along which to expand */
-
- Lst_Init(members);
-
- /*
- * Find a path along which to expand the word.
- *
- * If the word has a known suffix, use that path.
- * If it has no known suffix and we're allowed to use the null
- * suffix, use its path.
- * Else use the default system search path.
- */
- LST_FOREACH(ln, &sufflist) {
- if (SuffSuffIsSuffix(Lst_Datum(ln), child->name) != NULL)
- break;
- }
-
- DEBUGF(SUFF, ("Wildcard expanding \"%s\"...", child->name));
-
- if (ln != NULL) {
- Suff *s = Lst_Datum(ln);
-
- DEBUGF(SUFF, ("suffix is \"%s\"...", s->name));
- path = &s->searchPath;
- } else {
- /*
- * Use default search path
- */
- path = &dirSearchPath;
- }
-
- /*
- * Expand the word along the chosen path
- */
- Lst_Init(&exp);
- Path_Expand(child->name, path, &exp);
-
- while (!Lst_IsEmpty(&exp)) {
- /*
- * Fetch next expansion off the list and find its GNode
- */
- cp = Lst_DeQueue(&exp);
-
- DEBUGF(SUFF, ("%s...", cp));
- Lst_AtEnd(members, Targ_FindNode(cp, TARG_CREATE));
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * SuffExpandChildren --
- * Expand the names of any children of a given node that contain
- * variable invocations or file wildcards into actual targets.
- *
- * Results:
- * == 0 (continue)
- *
- * Side Effects:
- * The expanded node is removed from the parent's list of children,
- * and the parent's unmade counter is decremented, but other nodes
- * may be added.
- *
- *-----------------------------------------------------------------------
- */
-static void
-SuffExpandChildren(GNode *parent, LstNode *current)
-{
- GNode *cchild; /* current child */
- GNode *gn;
- LstNode *prev; /* node after which to append new source */
- Lst members; /* expanded nodes */
-
- if (current == NULL) {
- /* start from begin of parent's children list */
- current = Lst_First(&parent->children);
- }
-
- while (current != NULL) {
- cchild = Lst_Datum(current);
-
- /*
- * First do variable expansion -- this takes precedence over
- * wildcard expansion. If the result contains wildcards, they'll
- * be gotten to later since the resulting words are tacked
- * instead of the current child onto the children list.
- *
- * XXXHB what if cchild contains lib.a(t1.o t2.o t3.o) but
- * no $?
- */
- if (strchr(cchild->name, '$') != NULL) {
- SuffExpandVariables(parent, cchild, &members);
-
- } else if (Dir_HasWildcards(cchild->name)) {
- SuffExpandWildcards(cchild, &members);
-
- } else {
- /* nothing special just advance to next child */
- current = LST_NEXT(current);
- continue;
- }
-
- /*
- * New nodes effectively take the place of the child,
- * so place them after the child
- */
- prev = current;
-
- /*
- * Add all new elements to the parent node if they aren't
- * already children of it.
- */
- while(!Lst_IsEmpty(&members)) {
- gn = Lst_DeQueue(&members);
-
- DEBUGF(SUFF, ("%s...", gn->name));
- if (Lst_Member(&parent->children, gn) == NULL) {
- Lst_Append(&parent->children, prev, gn);
- prev = Lst_Succ(prev);
- Lst_AtEnd(&gn->parents, parent);
- parent->unmade++;
- }
- }
-
- /*
- * Now the source is expanded, remove it from the list
- * of children to keep it from being processed.
- * Advance to the next child.
- */
- prev = current;
- current = LST_NEXT(current);
-
- parent->unmade--;
- Lst_Remove(&parent->children, prev);
- DEBUGF(SUFF, ("\n"));
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * SuffApplyTransform --
- * Apply a transformation rule, given the source and target nodes
- * and suffixes.
- *
- * Results:
- * TRUE if successful, FALSE if not.
- *
- * Side Effects:
- * The source and target are linked and the commands from the
- * transformation are added to the target node's commands list.
- * All attributes but OP_DEPMASK and OP_TRANSFORM are applied
- * to the target. The target also inherits all the sources for
- * the transformation rule.
- *
- *-----------------------------------------------------------------------
- */
-static Boolean
-SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s)
-{
- LstNode *ln; /* General node */
- char *tname; /* Name of transformation rule */
- GNode *gn; /* Node for same */
-
- if (Lst_Member(&tGn->children, sGn) == NULL) {
- /*
- * Not already linked, so form the proper links between the
- * target and source.
- */
- Lst_AtEnd(&tGn->children, sGn);
- Lst_AtEnd(&sGn->parents, tGn);
- tGn->unmade += 1;
- }
-
- if ((sGn->type & OP_OPMASK) == OP_DOUBLEDEP) {
- /*
- * When a :: node is used as the implied source of a node,
- * we have to link all its cohorts in as sources as well. Only
- * the initial sGn gets the target in its iParents list, however
- * as that will be sufficient to get the .IMPSRC variable set
- * for tGn
- */
- for (ln = Lst_First(&sGn->cohorts); ln != NULL;
- ln = Lst_Succ(ln)) {
- gn = Lst_Datum(ln);
-
- if (Lst_Member(&tGn->children, gn) == NULL) {
- /*
- * Not already linked, so form the proper
- * links between the target and source.
- */
- Lst_AtEnd(&tGn->children, gn);
- Lst_AtEnd(&gn->parents, tGn);
- tGn->unmade += 1;
- }
- }
- }
- /*
- * Locate the transformation rule itself
- */
- tname = str_concat(s->name, t->name, 0);
- gn = SuffTransFind(tname);
- free(tname);
-
- if (gn == NULL) {
- /*
- * Not really such a transformation rule (can happen when we're
- * called to link an OP_MEMBER and OP_ARCHV node), so return
- * FALSE.
- */
- return (FALSE);
- }
-
- DEBUGF(SUFF, ("\tapplying %s -> %s to \"%s\"\n",
- s->name, t->name, tGn->name));
-
- /*
- * Record last child for expansion purposes
- */
- ln = Lst_Last(&tGn->children);
-
- /*
- * Pass the buck to Make_HandleUse to apply the rule
- */
- Make_HandleUse(gn, tGn);
-
- /*
- * Deal with wildcards and variables in any acquired sources
- */
- ln = Lst_Succ(ln);
- if (ln != NULL) {
- SuffExpandChildren(tGn, ln);
- }
-
- /*
- * Keep track of another parent to which this beast is transformed so
- * the .IMPSRC variable can be set correctly for the parent.
- */
- Lst_AtEnd(&sGn->iParents, tGn);
-
- return (TRUE);
-}
-
-
-/*-
- *-----------------------------------------------------------------------
- * SuffFindArchiveDeps --
- * Locate dependencies for an OP_ARCHV node.
- *
- * Results:
- * None
- *
- * Side Effects:
- * Same as Suff_FindDeps
- *
- *-----------------------------------------------------------------------
- */
-static void
-SuffFindArchiveDeps(GNode *gn, Lst *slst)
-{
- char *eoarch; /* End of archive portion */
- char *eoname; /* End of member portion */
- char *name; /* Start of member's name */
- GNode *mem; /* Node for member */
- Suff *ms; /* Suffix descriptor for member */
-
- static const char *copy[] = {
- TARGET, /* Must be first */
- PREFIX, /* Must be second */
- };
-
- /*
- * The node is an archive(member) pair. so we must find a
- * suffix for both of them.
- */
- eoarch = strchr(gn->name, '(');
- eoname = strchr(eoarch, ')');
-
- *eoname = '\0'; /* Nuke parentheses during suffix search */
- *eoarch = '\0'; /* So a suffix can be found */
-
- name = eoarch + 1;
-
- /*
- * To simplify things, call Suff_FindDeps recursively on the member now,
- * so we can simply compare the member's .PREFIX and .TARGET variables
- * to locate its suffix. This allows us to figure out the suffix to
- * use for the archive without having to do a quadratic search over the
- * suffix list, backtracking for each one...
- */
- mem = Targ_FindNode(name, TARG_CREATE);
- SuffFindDeps(mem, slst);
-
- /*
- * Create the link between the two nodes right off
- */
- if (Lst_Member(&gn->children, mem) == NULL) {
- Lst_AtEnd(&gn->children, mem);
- Lst_AtEnd(&mem->parents, gn);
- gn->unmade += 1;
- }
-
- /*
- * Copy in the variables from the member node to this one.
- */
- Var_Set(copy[1], Var_Value(copy[1], mem), gn);
- Var_Set(copy[0], Var_Value(copy[0], mem), gn);
-
- ms = mem->suffix;
- if (ms == NULL) {
- /*
- * Didn't know what it was -- use .NULL suffix if not in
- * make mode
- */
- DEBUGF(SUFF, ("using null suffix\n"));
- ms = suffNull;
- }
-
- /*
- * Set the other two local variables required for this target.
- */
- Var_Set(MEMBER, name, gn);
- Var_Set(ARCHIVE, gn->name, gn);
-
- if (ms != NULL) {
- /*
- * Member has a known suffix, so look for a transformation rule
- * from it to a possible suffix of the archive. Rather than
- * searching through the entire list, we just look at suffixes
- * to which the member's suffix may be transformed...
- */
- LstNode *ln;
-
- /*
- * Use first matching suffix...
- */
- LST_FOREACH(ln, &ms->parents) {
- if (SuffSuffIsSuffix(Lst_Datum(ln), gn->name) != NULL)
- break;
- }
-
- if (ln != NULL) {
- /*
- * Got one -- apply it
- */
- if (!SuffApplyTransform(gn, mem, Lst_Datum(ln), ms)) {
- DEBUGF(SUFF, ("\tNo transformation from "
- "%s -> %s\n", ms->name,
- ((Suff *)Lst_Datum(ln))->name));
- }
- }
- }
-
- /*
- * Replace the opening and closing parens now we've no need
- * of the separate pieces.
- */
- *eoarch = '(';
- *eoname = ')';
-
- /*
- * Pretend gn appeared to the left of a dependency operator so
- * the user needn't provide a transformation from the member to the
- * archive.
- */
- if (OP_NOP(gn->type)) {
- gn->type |= OP_DEPENDS;
- }
-
- /*
- * Flag the member as such so we remember to look in the archive for
- * its modification time.
- */
- mem->type |= OP_MEMBER;
-}
-
-/*-
- *-----------------------------------------------------------------------
- * SuffFindNormalDeps --
- * Locate implicit dependencies for regular targets.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Same as Suff_FindDeps...
- *
- *-----------------------------------------------------------------------
- */
-static void
-SuffFindNormalDeps(GNode *gn, Lst *slst)
-{
- char *eoname; /* End of name */
- char *sopref; /* Start of prefix */
- LstNode *ln; /* Next suffix node to check */
- Lst srcs; /* List of sources at which to look */
- Lst targs; /* List of targets to which things can be
- * transformed. They all have the same file,
- * but different suff and pref fields */
- Src *bottom; /* Start of found transformation path */
- Src *src; /* General Src pointer */
- char *pref; /* Prefix to use */
- Src *targ; /* General Src target pointer */
-
- eoname = gn->name + strlen(gn->name);
- sopref = gn->name;
-
- /*
- * Begin at the beginning...
- */
- ln = Lst_First(&sufflist);
- Lst_Init(&srcs);
- Lst_Init(&targs);
-
- /*
- * We're caught in a catch-22 here. On the one hand, we want to use any
- * transformation implied by the target's sources, but we can't examine
- * the sources until we've expanded any variables/wildcards they may
- * hold, and we can't do that until we've set up the target's local
- * variables and we can't do that until we know what the proper suffix
- * for the target is (in case there are two suffixes one of which is a
- * suffix of the other) and we can't know that until we've found its
- * implied source, which we may not want to use if there's an existing
- * source that implies a different transformation.
- *
- * In an attempt to get around this, which may not work all the time,
- * but should work most of the time, we look for implied sources first,
- * checking transformations to all possible suffixes of the target,
- * use what we find to set the target's local variables, expand the
- * children, then look for any overriding transformations they imply.
- * Should we find one, we discard the one we found before.
- */
-
- while (ln != NULL) {
- /*
- * Look for next possible suffix...
- */
- while (ln != NULL) {
- if (SuffSuffIsSuffix(Lst_Datum(ln), gn->name) != NULL)
- break;
- ln = LST_NEXT(ln);
- }
-
- if (ln != NULL) {
- int prefLen; /* Length of the prefix */
- Src *target;
-
- /*
- * Allocate a Src structure to which things can be
- * transformed
- */
- target = SuffSrcCreate(estrdup(gn->name), NULL,
- Lst_Datum(ln), NULL, gn);
- target->suff->refCount++;
-
- /*
- * Allocate room for the prefix, whose end is found
- * by subtracting the length of the suffix from
- * the end of the name.
- */
- prefLen = (eoname - target->suff->nameLen) - sopref;
- assert(prefLen >= 0);
- target->pref = emalloc(prefLen + 1);
- memcpy(target->pref, sopref, prefLen);
- target->pref[prefLen] = '\0';
-
- /*
- * Add nodes from which the target can be made
- */
- SuffAddLevel(&srcs, target);
-
- /*
- * Record the target so we can nuke it
- */
- Lst_AtEnd(&targs, target);
-
- /*
- * Search from this suffix's successor...
- */
- ln = Lst_Succ(ln);
- }
- }
-
- /*
- * Handle target of unknown suffix...
- */
- if (Lst_IsEmpty(&targs) && suffNull != NULL) {
- DEBUGF(SUFF, ("\tNo known suffix on %s. Using .NULL suffix\n",
- gn->name));
-
- targ = SuffSrcCreate(estrdup(gn->name), estrdup(sopref),
- suffNull, NULL, gn);
- targ->suff->refCount++;
-
- /*
- * Only use the default suffix rules if we don't have commands
- * or dependencies defined for this gnode
- */
- if (Lst_IsEmpty(&gn->commands) && Lst_IsEmpty(&gn->children))
- SuffAddLevel(&srcs, targ);
- else {
- DEBUGF(SUFF, ("not "));
- }
-
- DEBUGF(SUFF, ("adding suffix rules\n"));
-
- Lst_AtEnd(&targs, targ);
- }
-
- /*
- * Using the list of possible sources built up from the target
- * suffix(es), try and find an existing file/target that matches.
- */
- bottom = SuffFindThem(&srcs, slst);
-
- if (bottom == NULL) {
- /*
- * No known transformations -- use the first suffix found for
- * setting the local variables.
- */
- if (!Lst_IsEmpty(&targs)) {
- targ = Lst_Datum(Lst_First(&targs));
- } else {
- targ = NULL;
- }
- } else {
- /*
- * Work up the transformation path to find the suffix of the
- * target to which the transformation was made.
- */
- for (targ = bottom; targ->parent != NULL; targ = targ->parent)
- continue;
- }
-
- /*
- * The .TARGET variable we always set to be the name at this point,
- * since it's only set to the path if the thing is only a source and
- * if it's only a source, it doesn't matter what we put here as far
- * as expanding sources is concerned, since it has none...
- */
- Var_Set(TARGET, gn->name, gn);
-
- pref = (targ != NULL) ? targ->pref : gn->name;
- Var_Set(PREFIX, pref, gn);
-
- /*
- * Now we've got the important local variables set, expand any sources
- * that still contain variables or wildcards in their names.
- */
- SuffExpandChildren(gn, NULL);
-
- if (targ == NULL) {
- DEBUGF(SUFF, ("\tNo valid suffix on %s\n", gn->name));
-
- sfnd_abort:
- /*
- * Deal with finding the thing on the default search path if the
- * node is only a source (not on the lhs of a dependency
- * operator or [XXX] it has neither children or commands).
- */
- if (OP_NOP(gn->type) || (Lst_IsEmpty(&gn->children) &&
- Lst_IsEmpty(&gn->commands))) {
- gn->path = Path_FindFile(gn->name,
- (targ == NULL ? &dirSearchPath :
- &targ->suff->searchPath));
- if (gn->path != NULL) {
- char *ptr;
- Var_Set(TARGET, gn->path, gn);
-
- if (targ != NULL) {
- /*
- * Suffix known for the thing -- trim
- * the suffix off the path to form the
- * proper .PREFIX variable.
- */
- int savep = strlen(gn->path) -
- targ->suff->nameLen;
- char savec;
-
- if (gn->suffix)
- gn->suffix->refCount--;
- gn->suffix = targ->suff;
- gn->suffix->refCount++;
-
- savec = gn->path[savep];
- gn->path[savep] = '\0';
-
- if ((ptr = strrchr(gn->path, '/')) != NULL)
- ptr++;
- else
- ptr = gn->path;
-
- Var_Set(PREFIX, ptr, gn);
-
- gn->path[savep] = savec;
- } else {
- /*
- * The .PREFIX gets the full path if
- * the target has no known suffix.
- */
- if (gn->suffix)
- gn->suffix->refCount--;
- gn->suffix = NULL;
-
- if ((ptr = strrchr(gn->path, '/')) != NULL)
- ptr++;
- else
- ptr = gn->path;
-
- Var_Set(PREFIX, ptr, gn);
- }
- }
- } else {
- /*
- * Not appropriate to search for the thing -- set the
- * path to be the name so Dir_MTime won't go
- * grovelling for it.
- */
- if (gn->suffix)
- gn->suffix->refCount--;
- gn->suffix = (targ == NULL) ? NULL : targ->suff;
- if (gn->suffix)
- gn->suffix->refCount++;
- free(gn->path);
- gn->path = estrdup(gn->name);
- }
-
- goto sfnd_return;
- }
-
- /*
- * If the suffix indicates that the target is a library, mark that in
- * the node's type field.
- */
- if (targ->suff->flags & SUFF_LIBRARY) {
- gn->type |= OP_LIB;
- }
-
- /*
- * Check for overriding transformation rule implied by sources
- */
- if (!Lst_IsEmpty(&gn->children)) {
- src = SuffFindCmds(targ, slst);
-
- if (src != NULL) {
- /*
- * Free up all the Src structures in the
- * transformation path up to, but not including,
- * the parent node.
- */
- while (bottom && bottom->parent != NULL) {
- if (Lst_Member(slst, bottom) == NULL) {
- Lst_AtEnd(slst, bottom);
- }
- bottom = bottom->parent;
- }
- bottom = src;
- }
- }
-
- if (bottom == NULL) {
- /*
- * No idea from where it can come -- return now.
- */
- goto sfnd_abort;
- }
-
- /*
- * We now have a list of Src structures headed by 'bottom' and linked
- * via their 'parent' pointers. What we do next is create links between
- * source and target nodes (which may or may not have been created)
- * and set the necessary local variables in each target. The
- * commands for each target are set from the commands of the
- * transformation rule used to get from the src suffix to the targ
- * suffix. Note that this causes the commands list of the original
- * node, gn, to be replaced by the commands of the final
- * transformation rule. Also, the unmade field of gn is incremented.
- * Etc.
- */
- if (bottom->node == NULL) {
- bottom->node = Targ_FindNode(bottom->file, TARG_CREATE);
- }
-
- for (src = bottom; src->parent != NULL; src = src->parent) {
- targ = src->parent;
-
- if (src->node->suffix)
- src->node->suffix->refCount--;
- src->node->suffix = src->suff;
- src->node->suffix->refCount++;
-
- if (targ->node == NULL) {
- targ->node = Targ_FindNode(targ->file, TARG_CREATE);
- }
-
- SuffApplyTransform(targ->node, src->node,
- targ->suff, src->suff);
-
- if (targ->node != gn) {
- /*
- * Finish off the dependency-search process for any
- * nodes between bottom and gn (no point in questing
- * around the filesystem for their implicit source
- * when it's already known). Note that the node can't
- * have any sources that need expanding, since
- * SuffFindThem will stop on an existing
- * node, so all we need to do is set the standard and
- * System V variables.
- */
- targ->node->type |= OP_DEPS_FOUND;
-
- Var_Set(PREFIX, targ->pref, targ->node);
- Var_Set(TARGET, targ->node->name, targ->node);
- }
- }
-
- if (gn->suffix)
- gn->suffix->refCount--;
- gn->suffix = src->suff;
- gn->suffix->refCount++;
-
- /*
- * So Dir_MTime doesn't go questing for it...
- */
- free(gn->path);
- gn->path = estrdup(gn->name);
-
- /*
- * Nuke the transformation path and the Src structures left over in the
- * two lists.
- */
- sfnd_return:
- if (bottom)
- if (Lst_Member(slst, bottom) == NULL)
- Lst_AtEnd(slst, bottom);
-
- while (SuffRemoveSrc(&srcs) || SuffRemoveSrc(&targs))
- continue;
-
- Lst_Concat(slst, &srcs, LST_CONCLINK);
- Lst_Concat(slst, &targs, LST_CONCLINK);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Suff_FindDeps --
- * Find implicit sources for the target described by the graph node
- * gn
- *
- * Results:
- * Nothing.
- *
- * Side Effects:
- * Nodes are added to the graph below the passed-in node. The nodes
- * are marked to have their IMPSRC variable filled in. The
- * PREFIX variable is set for the given node and all its
- * implied children.
- *
- * Notes:
- * The path found by this target is the shortest path in the
- * transformation graph, which may pass through non-existent targets,
- * to an existing target. The search continues on all paths from the
- * root suffix until a file is found. I.e. if there's a path
- * .o -> .c -> .l -> .l,v from the root and the .l,v file exists but
- * the .c and .l files don't, the search will branch out in
- * all directions from .o and again from all the nodes on the
- * next level until the .l,v node is encountered.
- *
- *-----------------------------------------------------------------------
- */
-void
-Suff_FindDeps(GNode *gn)
-{
-
- SuffFindDeps(gn, &srclist);
- while (SuffRemoveSrc(&srclist))
- continue;
-}
-
-
-static void
-SuffFindDeps(GNode *gn, Lst *slst)
-{
-
- if (gn->type & OP_DEPS_FOUND) {
- /*
- * If dependencies already found, no need to do it again...
- */
- return;
- } else {
- gn->type |= OP_DEPS_FOUND;
- }
-
- DEBUGF(SUFF, ("SuffFindDeps (%s)\n", gn->name));
-
- if (gn->type & OP_ARCHV) {
- SuffFindArchiveDeps(gn, slst);
-
- } else if (gn->type & OP_LIB) {
- /*
- * If the node is a library, it is the arch module's job to find
- * it and set the TARGET variable accordingly. We merely provide
- * the search path, assuming all libraries end in ".a" (if the
- * suffix hasn't been defined, there's nothing we can do for it,
- * so we just set the TARGET variable to the node's name in order
- * to give it a value).
- */
- Suff *s;
-
- s = SuffSuffFind(LIBSUFF);
- if (gn->suffix)
- gn->suffix->refCount--;
- if (s != NULL) {
- gn->suffix = s;
- gn->suffix->refCount++;
- Arch_FindLib(gn, &s->searchPath);
- } else {
- gn->suffix = NULL;
- Var_Set(TARGET, gn->name, gn);
- }
-
- /*
- * Because a library (-lfoo) target doesn't follow the standard
- * filesystem conventions, we don't set the regular variables for
- * the thing. .PREFIX is simply made empty...
- */
- Var_Set(PREFIX, "", gn);
-
- } else {
- SuffFindNormalDeps(gn, slst);
- }
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Suff_SetNull --
- * Define which suffix is the null suffix.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * 'suffNull' is altered.
- *
- * Notes:
- * Need to handle the changing of the null suffix gracefully so the
- * old transformation rules don't just go away.
- *
- *-----------------------------------------------------------------------
- */
-void
-Suff_SetNull(char *name)
-{
- Suff *s;
-
- if ((s = SuffSuffFind(name)) == NULL) {
- Parse_Error(PARSE_WARNING, "Desired null suffix %s "
- "not defined.", name);
- return;
- }
-
- if (suffNull != NULL) {
- suffNull->flags &= ~SUFF_NULL;
- }
- s->flags |= SUFF_NULL;
-
- /*
- * XXX: Here's where the transformation mangling
- * would take place
- */
- suffNull = s;
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Suff_Init --
- * Initialize suffixes module
- *
- * Results:
- * None
- *
- * Side Effects:
- * Many
- *-----------------------------------------------------------------------
- */
-void
-Suff_Init(void)
-{
-
- sNum = 0;
- /*
- * Create null suffix for single-suffix rules (POSIX). The thing doesn't
- * actually go on the suffix list or everyone will think that's its
- * suffix.
- */
- emptySuff = suffNull = emalloc(sizeof(Suff));
-
- suffNull->name = estrdup("");
- suffNull->nameLen = 0;
- TAILQ_INIT(&suffNull->searchPath);
- Path_Concat(&suffNull->searchPath, &dirSearchPath);
- Lst_Init(&suffNull->children);
- Lst_Init(&suffNull->parents);
- Lst_Init(&suffNull->ref);
- suffNull->sNum = sNum++;
- suffNull->flags = SUFF_NULL;
- suffNull->refCount = 1;
-}
-
-/********************* DEBUGGING FUNCTIONS **********************/
-
-void
-Suff_PrintAll(void)
-{
- const LstNode *ln;
- const LstNode *tln;
- const GNode *gn;
- const Suff *s;
-
- static const struct flag2str suff_flags[] = {
- { SUFF_INCLUDE, "INCLUDE" },
- { SUFF_LIBRARY, "LIBRARY" },
- { SUFF_NULL, "NULL" },
- { 0, NULL }
- };
-
- printf("#*** Suffixes:\n");
- LST_FOREACH(ln, &sufflist) {
- s = Lst_Datum(ln);
- printf("# `%s' [%d] ", s->name, s->refCount);
-
- if (s->flags != 0) {
- printf(" ");
- print_flags(stdout, suff_flags, s->flags, 1);
- }
-
- printf("\n#\tTo: ");
- LST_FOREACH(tln, &s->parents)
- printf("`%s' ", ((const Suff *)Lst_Datum(tln))->name);
-
- printf("\n#\tFrom: ");
- LST_FOREACH(tln, &s->children)
- printf("`%s' ", ((const Suff *)Lst_Datum(tln))->name);
-
- printf("\n#\tSearch Path: ");
- Path_Print(&s->searchPath);
-
- printf("\n");
- }
-
- printf("#*** Transformations:\n");
- LST_FOREACH(ln, &transforms) {
- gn = Lst_Datum(ln);
- printf("%-16s: ", gn->name);
- Targ_PrintType(gn->type);
- printf("\n");
- LST_FOREACH(tln, &gn->commands)
- printf("\t%s\n", (const char *)Lst_Datum(tln));
- printf("\n");
- }
-}
diff --git a/usr.bin/make/suff.h b/usr.bin/make/suff.h
deleted file mode 100644
index 9982ab4..0000000
--- a/usr.bin/make/suff.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * $FreeBSD$
- */
-
-#ifndef suff_h_2d5a821c
-#define suff_h_2d5a821c
-
-struct GNode;
-struct Path;
-
-void Suff_ClearSuffixes(void);
-Boolean Suff_IsTransform(char *);
-struct GNode *Suff_AddTransform(char *);
-void Suff_EndTransform(const struct GNode *);
-void Suff_AddSuffix(char *);
-struct Path *Suff_GetPath(char *);
-void Suff_DoPaths(void);
-void Suff_AddInclude(char *);
-void Suff_AddLib(char *);
-void Suff_FindDeps(struct GNode *);
-void Suff_SetNull(char *);
-void Suff_Init(void);
-void Suff_PrintAll(void);
-
-#endif /* suff_h_2d5a821c */
diff --git a/usr.bin/make/targ.c b/usr.bin/make/targ.c
deleted file mode 100644
index 75ced89..0000000
--- a/usr.bin/make/targ.c
+++ /dev/null
@@ -1,472 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)targ.c 8.2 (Berkeley) 3/19/94
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*
- * Functions for maintaining the Lst allTargets. Target nodes are
- * kept in two structures: a Lst, maintained by the list library, and a
- * hash table, maintained by the hash library.
- *
- * Interface:
- * Targ_Init Initialization procedure.
- *
- * Targ_NewGN Create a new GNode for the passed target (string).
- * The node is *not* placed in the hash table, though all
- * its fields are initialized.
- *
- * Targ_FindNode Find the node for a given target, creating and storing
- * it if it doesn't exist and the flags are right
- * (TARG_CREATE)
- *
- * Targ_FindList Given a list of names, find nodes for all of them. If a
- * name doesn't exist and the TARG_NOCREATE flag was given,
- * an error message is printed. Else, if a name doesn't
- * exist, its node is created.
- *
- * Targ_Ignore Return TRUE if errors should be ignored when creating
- * the given target.
- *
- * Targ_Silent Return TRUE if we should be silent when creating the
- * given target.
- *
- * Targ_Precious Return TRUE if the target is precious and should not
- * be removed if we are interrupted.
- *
- * Debugging:
- * Targ_PrintGraph Print out the entire graphm all variables and statistics
- * for the directory cache. Should print something for
- * suffixes, too, but...
- */
-
-#include <stdio.h>
-
-#include "dir.h"
-#include "globals.h"
-#include "GNode.h"
-#include "hash.h"
-#include "suff.h"
-#include "targ.h"
-#include "util.h"
-#include "var.h"
-
-/* the list of all targets found so far */
-static Lst allTargets = Lst_Initializer(allTargets);
-
-static Hash_Table targets; /* a hash table of same */
-
-#define HTSIZE 191 /* initial size of hash table */
-
-/**
- * Targ_Init
- * Initialize this module
- *
- * Side Effects:
- * The allTargets list and the targets hash table are initialized
- */
-void
-Targ_Init(void)
-{
-
- Hash_InitTable(&targets, HTSIZE);
-}
-
-/**
- * Targ_NewGN
- * Create and initialize a new graph node
- *
- * Results:
- * An initialized graph node with the name field filled with a copy
- * of the passed name
- *
- * Side Effects:
- * The gnode is added to the list of all gnodes.
- */
-GNode *
-Targ_NewGN(const char *name)
-{
- GNode *gn;
-
- gn = emalloc(sizeof(GNode));
- gn->name = estrdup(name);
- gn->path = NULL;
- if (name[0] == '-' && name[1] == 'l') {
- gn->type = OP_LIB;
- } else {
- gn->type = 0;
- }
- gn->unmade = 0;
- gn->make = FALSE;
- gn->made = UNMADE;
- gn->childMade = FALSE;
- gn->order = 0;
- gn->mtime = gn->cmtime = 0;
- gn->cmtime_gn = NULL;
- Lst_Init(&gn->iParents);
- Lst_Init(&gn->cohorts);
- Lst_Init(&gn->parents);
- Lst_Init(&gn->children);
- Lst_Init(&gn->successors);
- Lst_Init(&gn->preds);
- Lst_Init(&gn->context);
- Lst_Init(&gn->commands);
- gn->suffix = NULL;
-
- return (gn);
-}
-
-/**
- * Targ_FindNode
- * Find a node in the list using the given name for matching
- *
- * Results:
- * The node in the list if it was. If it wasn't, return NULL of
- * flags was TARG_NOCREATE or the newly created and initialized node
- * if it was TARG_CREATE
- *
- * Side Effects:
- * Sometimes a node is created and added to the list
- */
-GNode *
-Targ_FindNode(const char *name, int flags)
-{
- GNode *gn; /* node in that element */
- Hash_Entry *he; /* New or used hash entry for node */
- Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */
- /* an entry for the node */
-
- if (flags & TARG_CREATE) {
- he = Hash_CreateEntry(&targets, name, &isNew);
- if (isNew) {
- gn = Targ_NewGN(name);
- Hash_SetValue(he, gn);
- Lst_AtEnd(&allTargets, gn);
- }
- } else {
- he = Hash_FindEntry(&targets, name);
- }
-
- if (he == NULL) {
- return (NULL);
- } else {
- return (Hash_GetValue(he));
- }
-}
-
-/**
- * Targ_FindList
- * Make a complete list of GNodes from the given list of names
- *
- * Results:
- * A complete list of graph nodes corresponding to all instances of all
- * the names in names.
- *
- * Side Effects:
- * If flags is TARG_CREATE, nodes will be created for all names in
- * names which do not yet have graph nodes. If flags is TARG_NOCREATE,
- * an error message will be printed for each name which can't be found.
- */
-void
-Targ_FindList(Lst *nodes, Lst *names, int flags)
-{
- LstNode *ln; /* name list element */
- GNode *gn; /* node in tLn */
- char *name;
-
- for (ln = Lst_First(names); ln != NULL; ln = Lst_Succ(ln)) {
- name = Lst_Datum(ln);
- gn = Targ_FindNode(name, flags);
- if (gn != NULL) {
- /*
- * Note: Lst_AtEnd must come before the Lst_Concat so
- * the nodes are added to the list in the order in which
- * they were encountered in the makefile.
- */
- Lst_AtEnd(nodes, gn);
- if (gn->type & OP_DOUBLEDEP) {
- Lst_Concat(nodes, &gn->cohorts, LST_CONCNEW);
- }
-
- } else if (flags == TARG_NOCREATE) {
- Error("\"%s\" -- target unknown.", name);
- }
- }
-}
-
-/**
- * Targ_Ignore
- * Return true if should ignore errors when creating gn
- *
- * Results:
- * TRUE if should ignore errors
- */
-Boolean
-Targ_Ignore(GNode *gn)
-{
-
- if (ignoreErrors || (gn->type & OP_IGNORE)) {
- return (TRUE);
- } else {
- return (FALSE);
- }
-}
-
-/**
- * Targ_Silent
- * Return true if be silent when creating gn
- *
- * Results:
- * TRUE if should be silent
- */
-Boolean
-Targ_Silent(GNode *gn)
-{
-
- if (beSilent || (gn->type & OP_SILENT)) {
- return (TRUE);
- } else {
- return (FALSE);
- }
-}
-
-/**
- * Targ_Precious
- * See if the given target is precious
- *
- * Results:
- * TRUE if it is precious. FALSE otherwise
- */
-Boolean
-Targ_Precious(GNode *gn)
-{
-
- if (allPrecious || (gn->type & (OP_PRECIOUS | OP_DOUBLEDEP))) {
- return (TRUE);
- } else {
- return (FALSE);
- }
-}
-
-static GNode *mainTarg; /* the main target, as set by Targ_SetMain */
-
-/**
- * Targ_SetMain
- * Set our idea of the main target we'll be creating. Used for
- * debugging output.
- *
- * Side Effects:
- * "mainTarg" is set to the main target's node.
- */
-void
-Targ_SetMain(GNode *gn)
-{
-
- mainTarg = gn;
-}
-
-/**
- * Targ_FmtTime
- * Format a modification time in some reasonable way and return it.
- *
- * Results:
- * The time reformatted.
- *
- * Side Effects:
- * The time is placed in a static area, so it is overwritten
- * with each call.
- */
-char *
-Targ_FmtTime(time_t modtime)
-{
- struct tm *parts;
- static char buf[128];
-
- parts = localtime(&modtime);
-
- strftime(buf, sizeof(buf), "%H:%M:%S %b %d, %Y", parts);
- buf[sizeof(buf) - 1] = '\0';
- return (buf);
-}
-
-/**
- * Targ_PrintType
- * Print out a type field giving only those attributes the user can
- * set.
- */
-void
-Targ_PrintType(int type)
-{
- static const struct flag2str type2str[] = {
- { OP_OPTIONAL, ".OPTIONAL" },
- { OP_USE, ".USE" },
- { OP_EXEC, ".EXEC" },
- { OP_IGNORE, ".IGNORE" },
- { OP_PRECIOUS, ".PRECIOUS" },
- { OP_SILENT, ".SILENT" },
- { OP_MAKE, ".MAKE" },
- { OP_JOIN, ".JOIN" },
- { OP_INVISIBLE, ".INVISIBLE" },
- { OP_NOTMAIN, ".NOTMAIN" },
- { OP_PHONY, ".PHONY" },
- { OP_LIB, ".LIB" },
- { OP_MEMBER, ".MEMBER" },
- { OP_ARCHV, ".ARCHV" },
- { 0, NULL }
- };
-
- type &= ~OP_OPMASK;
- if (!DEBUG(TARG))
- type &= ~(OP_ARCHV | OP_LIB | OP_MEMBER);
- print_flags(stdout, type2str, type, 0);
-}
-
-/**
- * TargPrintNode
- * print the contents of a node
- */
-static int
-TargPrintNode(const GNode *gn, int pass)
-{
- const LstNode *tln;
-
- if (!OP_NOP(gn->type)) {
- printf("#\n");
- if (gn == mainTarg) {
- printf("# *** MAIN TARGET ***\n");
- }
- if (pass == 2) {
- if (gn->unmade) {
- printf("# %d unmade children\n", gn->unmade);
- } else {
- printf("# No unmade children\n");
- }
- if (!(gn->type & (OP_JOIN | OP_USE | OP_EXEC))) {
- if (gn->mtime != 0) {
- printf("# last modified %s: %s\n",
- Targ_FmtTime(gn->mtime),
- gn->made == UNMADE ? "unmade" :
- gn->made == MADE ? "made" :
- gn->made == UPTODATE ? "up-to-date":
- "error when made");
- } else if (gn->made != UNMADE) {
- printf("# non-existent (maybe): %s\n",
- gn->made == MADE ? "made" :
- gn->made == UPTODATE ? "up-to-date":
- gn->made == ERROR?"error when made":
- "aborted");
- } else {
- printf("# unmade\n");
- }
- }
- if (!Lst_IsEmpty(&gn->iParents)) {
- printf("# implicit parents: ");
- LST_FOREACH(tln, &gn->iParents)
- printf("%s ", ((const GNode *)
- Lst_Datum(tln))->name);
- printf("\n");
- }
- }
- if (!Lst_IsEmpty(&gn->parents)) {
- printf("# parents: ");
- LST_FOREACH(tln, &gn->parents)
- printf("%s ", ((const GNode *)
- Lst_Datum(tln))->name);
- printf("\n");
- }
-
- printf("%-16s", gn->name);
- switch (gn->type & OP_OPMASK) {
- case OP_DEPENDS:
- printf(": ");
- break;
- case OP_FORCE:
- printf("! ");
- break;
- case OP_DOUBLEDEP:
- printf(":: ");
- break;
- default:
- break;
- }
- Targ_PrintType(gn->type);
- LST_FOREACH(tln, &gn->children)
- printf("%s ", ((const GNode *)Lst_Datum(tln))->name);
- printf("\n");
- LST_FOREACH(tln, &gn->commands)
- printf("\t%s\n", (const char *)Lst_Datum(tln));
- printf("\n\n");
- if (gn->type & OP_DOUBLEDEP) {
- LST_FOREACH(tln, &gn->cohorts)
- TargPrintNode((const GNode *)Lst_Datum(tln),
- pass);
- }
- }
- return (0);
-}
-
-/**
- * Targ_PrintGraph
- * Print the entire graph.
- */
-void
-Targ_PrintGraph(int pass)
-{
- const GNode *gn;
- const LstNode *tln;
-
- printf("#*** Input graph:\n");
- LST_FOREACH(tln, &allTargets)
- TargPrintNode((const GNode *)Lst_Datum(tln), pass);
- printf("\n\n");
-
- printf("#\n# Files that are only sources:\n");
- LST_FOREACH(tln, &allTargets) {
- gn = Lst_Datum(tln);
- if (OP_NOP(gn->type))
- printf("#\t%s [%s]\n", gn->name,
- gn->path ? gn->path : gn->name);
- }
- Var_Dump();
- printf("\n");
- Dir_PrintDirectories();
- printf("\n");
- Suff_PrintAll();
-}
diff --git a/usr.bin/make/targ.h b/usr.bin/make/targ.h
deleted file mode 100644
index 3882833..0000000
--- a/usr.bin/make/targ.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * $FreeBSD$
- */
-
-#ifndef targ_h_6ded1830
-#define targ_h_6ded1830
-
-#include <time.h>
-
-/*
- * The TARG_ constants are used when calling the Targ_FindNode and
- * Targ_FindList functions in targ.c. They simply tell the functions what to
- * do if the desired node(s) is (are) not found. If the TARG_CREATE constant
- * is given, a new, empty node will be created for the target, placed in the
- * table of all targets and its address returned. If TARG_NOCREATE is given,
- * a NULL pointer will be returned.
- */
-#define TARG_CREATE 0x01 /* create node if not found */
-#define TARG_NOCREATE 0x00 /* don't create it */
-
-struct GNode;
-struct Lst;
-
-void Targ_Init(void);
-struct GNode *Targ_NewGN(const char *);
-struct GNode *Targ_FindNode(const char *, int);
-void Targ_FindList(struct Lst *, struct Lst *, int);
-Boolean Targ_Ignore(struct GNode *);
-Boolean Targ_Silent(struct GNode *);
-Boolean Targ_Precious(struct GNode *);
-void Targ_SetMain(struct GNode *);
-int Targ_PrintCmd(void *, void *);
-char *Targ_FmtTime(time_t);
-void Targ_PrintType(int);
-void Targ_PrintGraph(int);
-
-#endif /* targ_h_6ded1830 */
diff --git a/usr.bin/make/util.c b/usr.bin/make/util.c
deleted file mode 100644
index 3a27b0a..0000000
--- a/usr.bin/make/util.c
+++ /dev/null
@@ -1,316 +0,0 @@
-/*-
- * Copyright (c) 2002 Juli Mallett. All rights reserved.
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)main.c 8.3 (Berkeley) 3/19/94
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*-
- * util.c --
- * General utilitarian routines for make(1).
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <err.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "globals.h"
-#include "job.h"
-#include "targ.h"
-#include "util.h"
-
-static void enomem(void) __dead2;
-
-/*-
- * Debug --
- * Print a debugging message given its format.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The message is printed.
- */
-/* VARARGS */
-void
-Debug(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fflush(stderr);
-}
-
-/*-
- * Print a debugging message given its format and append the current
- * errno description. Terminate with a newline.
- */
-/* VARARGS */
-void
-DebugM(const char *fmt, ...)
-{
- va_list ap;
- int e = errno;
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, ": %s\n", strerror(e));
- va_end(ap);
- fflush(stderr);
-}
-
-/*-
- * Error --
- * Print an error message given its format.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The message is printed.
- */
-/* VARARGS */
-void
-Error(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, "\n");
- fflush(stderr);
-}
-
-/*-
- * Fatal --
- * Produce a Fatal error message. If jobs are running, waits for them
- * to finish.
- *
- * Results:
- * None
- *
- * Side Effects:
- * The program exits
- */
-/* VARARGS */
-void
-Fatal(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- if (jobsRunning)
- Job_Wait();
-
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, "\n");
- fflush(stderr);
-
- if (DEBUG(GRAPH2))
- Targ_PrintGraph(2);
- exit(2); /* Not 1 so -q can distinguish error */
-}
-
-/*
- * Punt --
- * Major exception once jobs are being created. Kills all jobs, prints
- * a message and exits.
- *
- * Results:
- * None
- *
- * Side Effects:
- * All children are killed indiscriminately and the program Lib_Exits
- */
-/* VARARGS */
-void
-Punt(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- fprintf(stderr, "make: ");
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, "\n");
- fflush(stderr);
-
- DieHorribly();
-}
-
-/*-
- * DieHorribly --
- * Exit without giving a message.
- *
- * Results:
- * None
- *
- * Side Effects:
- * A big one...
- */
-void
-DieHorribly(void)
-{
- if (jobsRunning)
- Job_AbortAll();
- if (DEBUG(GRAPH2))
- Targ_PrintGraph(2);
- exit(2); /* Not 1, so -q can distinguish error */
-}
-
-/*
- * Finish --
- * Called when aborting due to errors in child shell to signal
- * abnormal exit, with the number of errors encountered in Make_Make.
- *
- * Results:
- * None
- *
- * Side Effects:
- * The program exits
- */
-void
-Finish(int errors)
-{
-
- Fatal("%d error%s", errors, errors == 1 ? "" : "s");
-}
-
-/*
- * emalloc --
- * malloc, but die on error.
- */
-void *
-emalloc(size_t len)
-{
- void *p;
-
- if ((p = malloc(len)) == NULL)
- enomem();
- return (p);
-}
-
-/*
- * estrdup --
- * strdup, but die on error.
- */
-char *
-estrdup(const char *str)
-{
- char *p;
-
- if ((p = strdup(str)) == NULL)
- enomem();
- return (p);
-}
-
-/*
- * erealloc --
- * realloc, but die on error.
- */
-void *
-erealloc(void *ptr, size_t size)
-{
-
- if ((ptr = realloc(ptr, size)) == NULL)
- enomem();
- return (ptr);
-}
-
-/*
- * enomem --
- * die when out of memory.
- */
-static void
-enomem(void)
-{
- err(2, NULL);
-}
-
-/*
- * enunlink --
- * Remove a file carefully, avoiding directories.
- */
-int
-eunlink(const char *file)
-{
- struct stat st;
-
- if (lstat(file, &st) == -1)
- return (-1);
-
- if (S_ISDIR(st.st_mode)) {
- errno = EISDIR;
- return (-1);
- }
- return (unlink(file));
-}
-
-/*
- * Convert a flag word to a printable thing and print it
- */
-void
-print_flags(FILE *fp, const struct flag2str *tab, u_int flags, int par)
-{
- int first = 1;
-
- if (par)
- fprintf(fp, "(");
- while (tab->str != NULL) {
- if (flags & tab->flag) {
- if (!first)
- fprintf(fp, par ? "|" : " ");
- first = 0;
- fprintf(fp, "%s", tab->str);
- }
- tab++;
- }
- if (par)
- fprintf(fp, ")");
-}
diff --git a/usr.bin/make/util.h b/usr.bin/make/util.h
deleted file mode 100644
index 9a1dedf..0000000
--- a/usr.bin/make/util.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/*-
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * $FreeBSD$
- */
-
-#ifndef util_h_b7020fdb
-#define util_h_b7020fdb
-
-#include <sys/types.h>
-#include <stdio.h>
-
-/*
- * A boolean type is defined as an integer, not an enum. This allows a
- * boolean argument to be an expression that isn't strictly 0 or 1 valued.
- */
-
-typedef int Boolean;
-#ifndef TRUE
-#define TRUE 1
-#define FALSE 0
-#endif /* TRUE */
-
-#define CONCAT(a,b) a##b
-
-struct flag2str {
- u_int flag;
- const char *str;
-};
-
-/*
- * debug control:
- * There is one bit per module. It is up to the module what debug
- * information to print.
- */
-#define DEBUG_ARCH 0x0001
-#define DEBUG_COND 0x0002
-#define DEBUG_DIR 0x0004
-#define DEBUG_GRAPH1 0x0008
-#define DEBUG_GRAPH2 0x0010
-#define DEBUG_JOB 0x0020
-#define DEBUG_MAKE 0x0040
-#define DEBUG_SUFF 0x0080
-#define DEBUG_TARG 0x0100
-#define DEBUG_VAR 0x0200
-#define DEBUG_FOR 0x0400
-#define DEBUG_LOUD 0x0800
-
-#define DEBUG(module) (debug & CONCAT(DEBUG_,module))
-#define DEBUGF(module,args) \
-do { \
- if (DEBUG(module)) { \
- Debug args ; \
- } \
-} while (0)
-#define DEBUGM(module, args) do { \
- if (DEBUG(module)) { \
- DebugM args; \
- } \
- } while (0)
-
-#define ISDOT(c) ((c)[0] == '.' && (((c)[1] == '\0') || ((c)[1] == '/')))
-#define ISDOTDOT(c) ((c)[0] == '.' && ISDOT(&((c)[1])))
-
-#ifndef MAX
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#endif
-
-void Debug(const char *, ...);
-void DebugM(const char *, ...);
-void Error(const char *, ...);
-void Fatal(const char *, ...) __dead2;
-void Punt(const char *, ...) __dead2;
-void DieHorribly(void) __dead2;
-void Finish(int) __dead2;
-char *estrdup(const char *);
-void *emalloc(size_t);
-void *erealloc(void *, size_t);
-int eunlink(const char *);
-void print_flags(FILE *, const struct flag2str *, u_int, int);
-
-#endif /* util_h_b7020fdb */
diff --git a/usr.bin/make/var.c b/usr.bin/make/var.c
deleted file mode 100644
index cbaea9e..0000000
--- a/usr.bin/make/var.c
+++ /dev/null
@@ -1,2623 +0,0 @@
-/*-
- * Copyright (c) 2002 Juli Mallett.
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * @(#)var.c 8.3 (Berkeley) 3/19/94
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/**
- * var.c --
- * Variable-handling functions
- *
- * Interface:
- * Var_Set Set the value of a variable in the given
- * context. The variable is created if it doesn't
- * yet exist. The value and variable name need not
- * be preserved.
- *
- * Var_Append Append more characters to an existing variable
- * in the given context. The variable needn't
- * exist already -- it will be created if it doesn't.
- * A space is placed between the old value and the
- * new one.
- *
- * Var_Exists See if a variable exists.
- *
- * Var_Value Return the value of a variable in a context or
- * NULL if the variable is undefined.
- *
- * Var_Subst Substitute named variable, or all variables if
- * NULL in a string using
- * the given context as the top-most one. If the
- * third argument is non-zero, Parse_Error is
- * called if any variables are undefined.
- *
- * Var_Parse Parse a variable expansion from a string and
- * return the result and the number of characters
- * consumed.
- *
- * Var_Delete Delete a variable in a context.
- *
- * Var_Init Initialize this module.
- *
- * Debugging:
- * Var_Dump Print out all variables defined in the given
- * context.
- *
- * XXX: There's a lot of duplication in these functions.
- */
-
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <regex.h>
-
-#include "buf.h"
-#include "config.h"
-#include "globals.h"
-#include "GNode.h"
-#include "job.h"
-#include "lst.h"
-#include "parse.h"
-#include "str.h"
-#include "targ.h"
-#include "util.h"
-#include "var.h"
-
-/**
- *
- */
-typedef struct VarParser {
- const char *const input; /* pointer to input string */
- const char *ptr; /* current parser pos in input str */
- GNode *ctxt;
- Boolean err;
- Boolean execute;
-} VarParser;
-
-typedef struct Var {
- char *name; /* the variable's name */
- struct Buffer *val; /* its value */
- int flags; /* miscellaneous status flags */
-
-#define VAR_IN_USE 1 /* Variable's value currently being used.
- * Used to avoid recursion */
-
-#define VAR_JUNK 4 /* Variable is a junk variable that
- * should be destroyed when done with
- * it. Used by Var_Parse for undefined,
- * modified variables */
-
-#define VAR_TO_ENV 8 /* Place variable in environment */
-} Var;
-
-typedef struct {
- struct Buffer *lhs; /* String to match */
- struct Buffer *rhs; /* Replacement string (w/ &'s removed) */
-
- regex_t re;
- int nsub;
- regmatch_t *matches;
-
- int flags;
-#define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */
-#define VAR_SUB_ONE 0x02 /* Apply substitution to one word */
-#define VAR_SUB_MATCHED 0x04 /* There was a match */
-#define VAR_MATCH_START 0x08 /* Match at start of word */
-#define VAR_MATCH_END 0x10 /* Match at end of word */
-} VarPattern;
-
-typedef Boolean VarModifyProc(const char *, Boolean, struct Buffer *, void *);
-
-static char *VarParse(VarParser *, Boolean *);
-
-/*
- * This is a harmless return value for Var_Parse that can be used by Var_Subst
- * to determine if there was an error in parsing -- easier than returning
- * a flag, as things outside this module don't give a hoot.
- */
-char var_Error[] = "";
-
-/*
- * Similar to var_Error, but returned when the 'err' flag for Var_Parse is
- * set false. Why not just use a constant? Well, gcc likes to condense
- * identical string instances...
- */
-static char varNoError[] = "";
-
-/*
- * Internally, variables are contained in four different contexts.
- * 1) the environment. They may not be changed. If an environment
- * variable is appended-to, the result is placed in the global
- * context.
- * 2) the global context. Variables set in the Makefile are located in
- * the global context. It is the penultimate context searched when
- * substituting.
- * 3) the command-line context. All variables set on the command line
- * are placed in this context. They are UNALTERABLE once placed here.
- * 4) the local context. Each target has associated with it a context
- * list. On this list are located the structures describing such
- * local variables as $(@) and $(*)
- * The four contexts are searched in the reverse order from which they are
- * listed.
- */
-static GNode *VAR_ENV; /* variables from the environment */
-GNode *VAR_GLOBAL; /* variables from the makefile */
-GNode *VAR_CMD; /* variables defined on the command-line */
-
-Boolean oldVars; /* variable substitution style */
-Boolean checkEnvFirst; /* -e flag */
-
-#define OPEN_PAREN '('
-#define CLOSE_PAREN ')'
-#define OPEN_BRACE '{'
-#define CLOSE_BRACE '}'
-
-/**
- * Create a Var object.
- *
- * Params:
- * name Name of variable (copied).
- * value Value of variable (copied) or NULL.
- * flags Flags set on variable.
- *
- * Returns:
- * New variable.
- */
-static Var *
-VarCreate(const char name[], const char value[], int flags)
-{
- Var *v;
-
- v = emalloc(sizeof(Var));
- v->name = estrdup(name);
- v->val = Buf_Init(0);
- v->flags = flags;
-
- if (value != NULL) {
- Buf_Append(v->val, value);
- }
- return (v);
-}
-
-/**
- * Destroy a Var object.
- *
- * Params:
- * v Object to destroy.
- * f True if internal buffer in Buffer object is to be removed.
- */
-static void
-VarDestroy(Var *v, Boolean f)
-{
-
- Buf_Destroy(v->val, f);
- free(v->name);
- free(v);
-}
-
-/**
- * Remove the tail of the given word and place the result in the given
- * buffer.
- *
- * Results:
- * TRUE if characters were added to the buffer (a space needs to be
- * added to the buffer before the next word).
- *
- * Side Effects:
- * The trimmed word is added to the buffer.
- */
-static Boolean
-VarHead(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
-{
- char *slash;
-
- slash = strrchr(word, '/');
- if (slash != NULL) {
- if (addSpace) {
- Buf_AddByte(buf, (Byte)' ');
- }
- Buf_AppendRange(buf, word, slash);
- } else {
- /*
- * If no directory part, give . (q.v. the POSIX standard)
- */
- if (addSpace) {
- Buf_Append(buf, " .");
- } else {
- Buf_AddByte(buf, (Byte)'.');
- }
- }
- return (TRUE);
-}
-
-/**
- * Remove the head of the given word and place the result in the given
- * buffer.
- *
- * Results:
- * TRUE if characters were added to the buffer (a space needs to be
- * added to the buffer before the next word).
- *
- * Side Effects:
- * The trimmed word is added to the buffer.
- */
-static Boolean
-VarTail(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
-{
- const char *slash;
-
- if (addSpace) {
- Buf_AddByte (buf, (Byte)' ');
- }
-
- slash = strrchr(word, '/');
- if (slash != NULL) {
- slash++;
- Buf_Append(buf, slash);
- } else {
- Buf_Append(buf, word);
- }
- return (TRUE);
-}
-
-/**
- * Place the suffix of the given word in the given buffer.
- *
- * Results:
- * TRUE if characters were added to the buffer (a space needs to be
- * added to the buffer before the next word).
- *
- * Side Effects:
- * The suffix from the word is placed in the buffer.
- */
-static Boolean
-VarSuffix(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
-{
- const char *dot;
-
- dot = strrchr(word, '.');
- if (dot != NULL) {
- if (addSpace) {
- Buf_AddByte(buf, (Byte)' ');
- }
- dot++;
- Buf_Append(buf, dot);
- addSpace = TRUE;
- }
- return (addSpace);
-}
-
-/**
- * Remove the suffix of the given word and place the result in the
- * buffer.
- *
- * Results:
- * TRUE if characters were added to the buffer (a space needs to be
- * added to the buffer before the next word).
- *
- * Side Effects:
- * The trimmed word is added to the buffer.
- */
-static Boolean
-VarRoot(const char *word, Boolean addSpace, Buffer *buf, void *dummy __unused)
-{
- char *dot;
-
- if (addSpace) {
- Buf_AddByte(buf, (Byte)' ');
- }
-
- dot = strrchr(word, '.');
- if (dot != NULL) {
- Buf_AppendRange(buf, word, dot);
- } else {
- Buf_Append(buf, word);
- }
- return (TRUE);
-}
-
-/**
- * Place the word in the buffer if it matches the given pattern.
- * Callback function for VarModify to implement the :M modifier.
- * A space will be added if requested. A pattern is supplied
- * which the word must match.
- *
- * Results:
- * TRUE if a space should be placed in the buffer before the next
- * word.
- *
- * Side Effects:
- * The word may be copied to the buffer.
- */
-static Boolean
-VarMatch(const char *word, Boolean addSpace, Buffer *buf, void *pattern)
-{
-
- if (Str_Match(word, pattern)) {
- if (addSpace) {
- Buf_AddByte(buf, (Byte)' ');
- }
- addSpace = TRUE;
- Buf_Append(buf, word);
- }
- return (addSpace);
-}
-
-#ifdef SYSVVARSUB
-/**
- * Place the word in the buffer if it matches the given pattern.
- * Callback function for VarModify to implement the System V %
- * modifiers. A space is added if requested.
- *
- * Results:
- * TRUE if a space should be placed in the buffer before the next
- * word.
- *
- * Side Effects:
- * The word may be copied to the buffer.
- */
-static Boolean
-VarSYSVMatch(const char *word, Boolean addSpace, Buffer *buf, void *patp)
-{
- int len;
- const char *ptr;
- VarPattern *pat = (VarPattern *)patp;
-
- if (addSpace)
- Buf_AddByte(buf, (Byte)' ');
-
- addSpace = TRUE;
-
- if ((ptr = Str_SYSVMatch(word, Buf_Data(pat->lhs), &len)) != NULL)
- Str_SYSVSubst(buf, Buf_Data(pat->rhs), ptr, len);
- else
- Buf_Append(buf, word);
-
- return (addSpace);
-}
-#endif
-
-/**
- * Place the word in the buffer if it doesn't match the given pattern.
- * Callback function for VarModify to implement the :N modifier. A
- * space is added if requested.
- *
- * Results:
- * TRUE if a space should be placed in the buffer before the next
- * word.
- *
- * Side Effects:
- * The word may be copied to the buffer.
- */
-static Boolean
-VarNoMatch(const char *word, Boolean addSpace, Buffer *buf, void *pattern)
-{
-
- if (!Str_Match(word, pattern)) {
- if (addSpace) {
- Buf_AddByte(buf, (Byte)' ');
- }
- addSpace = TRUE;
- Buf_Append(buf, word);
- }
- return (addSpace);
-}
-
-/**
- * Perform a string-substitution on the given word, placing the
- * result in the passed buffer. A space is added if requested.
- *
- * Results:
- * TRUE if a space is needed before more characters are added.
- */
-static Boolean
-VarSubstitute(const char *word, Boolean addSpace, Buffer *buf, void *patternp)
-{
- size_t wordLen; /* Length of word */
- const char *cp; /* General pointer */
- VarPattern *pattern = patternp;
-
- wordLen = strlen(word);
- if (1) { /* substitute in each word of the variable */
- /*
- * Break substitution down into simple anchored cases
- * and if none of them fits, perform the general substitution
- * case.
- */
- if ((pattern->flags & VAR_MATCH_START) &&
- (strncmp(word, Buf_Data(pattern->lhs),
- Buf_Size(pattern->lhs)) == 0)) {
- /*
- * Anchored at start and beginning of word matches
- * pattern.
- */
- if ((pattern->flags & VAR_MATCH_END) &&
- (wordLen == Buf_Size(pattern->lhs))) {
- /*
- * Also anchored at end and matches to the end
- * (word is same length as pattern) add space
- * and rhs only if rhs is non-null.
- */
- if (Buf_Size(pattern->rhs) != 0) {
- if (addSpace) {
- Buf_AddByte(buf, (Byte)' ');
- }
- addSpace = TRUE;
- Buf_AppendBuf(buf, pattern->rhs);
- }
-
- } else if (pattern->flags & VAR_MATCH_END) {
- /*
- * Doesn't match to end -- copy word wholesale
- */
- goto nosub;
-
- } else {
- /*
- * Matches at start but need to copy in
- * trailing characters.
- */
- if ((Buf_Size(pattern->rhs) + wordLen -
- Buf_Size(pattern->lhs)) != 0) {
- if (addSpace) {
- Buf_AddByte(buf, (Byte)' ');
- }
- addSpace = TRUE;
- }
- Buf_AppendBuf(buf, pattern->rhs);
- Buf_AddBytes(buf, wordLen -
- Buf_Size(pattern->lhs),
- (word + Buf_Size(pattern->lhs)));
- }
-
- } else if (pattern->flags & VAR_MATCH_START) {
- /*
- * Had to match at start of word and didn't -- copy
- * whole word.
- */
- goto nosub;
-
- } else if (pattern->flags & VAR_MATCH_END) {
- /*
- * Anchored at end, Find only place match could occur
- * (leftLen characters from the end of the word) and
- * see if it does. Note that because the $ will be
- * left at the end of the lhs, we have to use strncmp.
- */
- cp = word + (wordLen - Buf_Size(pattern->lhs));
- if ((cp >= word) && (strncmp(cp, Buf_Data(pattern->lhs),
- Buf_Size(pattern->lhs)) == 0)) {
- /*
- * Match found. If we will place characters in
- * the buffer, add a space before hand as
- * indicated by addSpace, then stuff in the
- * initial, unmatched part of the word followed
- * by the right-hand-side.
- */
- if ((cp - word) + Buf_Size(pattern->rhs) != 0) {
- if (addSpace) {
- Buf_AddByte(buf, (Byte)' ');
- }
- addSpace = TRUE;
- }
- Buf_AppendRange(buf, word, cp);
- Buf_AppendBuf(buf, pattern->rhs);
-
- } else {
- /*
- * Had to match at end and didn't. Copy entire
- * word.
- */
- goto nosub;
- }
- } else {
- /*
- * Pattern is unanchored: search for the pattern in the
- * word using strstr(3), copying unmatched portions and
- * the right-hand-side for each match found, handling
- * non-global substitutions correctly, etc. When the
- * loop is done, any remaining part of the word (word
- * and wordLen are adjusted accordingly through the
- * loop) is copied straight into the buffer.
- * addSpace is set FALSE as soon as a space is added
- * to the buffer.
- */
- Boolean done;
- size_t origSize;
-
- done = FALSE;
- origSize = Buf_Size(buf);
- while (!done) {
- cp = strstr(word, Buf_Data(pattern->lhs));
- if (cp != NULL) {
- if (addSpace && (((cp - word) +
- Buf_Size(pattern->rhs)) != 0)) {
- Buf_AddByte(buf, (Byte)' ');
- addSpace = FALSE;
- }
- Buf_AppendRange(buf, word, cp);
- Buf_AppendBuf(buf, pattern->rhs);
- wordLen -= (cp - word) +
- Buf_Size(pattern->lhs);
- word = cp + Buf_Size(pattern->lhs);
- if (wordLen == 0 || (pattern->flags &
- VAR_SUB_GLOBAL) == 0) {
- done = TRUE;
- }
- } else {
- done = TRUE;
- }
- }
- if (wordLen != 0) {
- if (addSpace) {
- Buf_AddByte(buf, (Byte)' ');
- }
- Buf_AddBytes(buf, wordLen, (const Byte *)word);
- }
-
- /*
- * If added characters to the buffer, need to add a
- * space before we add any more. If we didn't add any,
- * just return the previous value of addSpace.
- */
- return ((Buf_Size(buf) != origSize) || addSpace);
- }
- /*
- * Common code for anchored substitutions:
- * addSpace was set TRUE if characters were added to the buffer.
- */
- return (addSpace);
- }
- nosub:
- if (addSpace) {
- Buf_AddByte(buf, (Byte)' ');
- }
- Buf_AddBytes(buf, wordLen, (const Byte *)word);
- return (TRUE);
-}
-
-/**
- * Print the error caused by a regcomp or regexec call.
- *
- * Side Effects:
- * An error gets printed.
- */
-static void
-VarREError(int err, regex_t *pat, const char *str)
-{
- char *errbuf;
- int errlen;
-
- errlen = regerror(err, pat, 0, 0);
- errbuf = emalloc(errlen);
- regerror(err, pat, errbuf, errlen);
- Error("%s: %s", str, errbuf);
- free(errbuf);
-}
-
-
-/**
- * Perform a regex substitution on the given word, placing the
- * result in the passed buffer. A space is added if requested.
- *
- * Results:
- * TRUE if a space is needed before more characters are added.
- */
-static Boolean
-VarRESubstitute(const char *word, Boolean addSpace, Buffer *buf, void *patternp)
-{
- VarPattern *pat;
- int xrv;
- const char *wp;
- char *rp;
- int added;
- int flags = 0;
-
-#define MAYBE_ADD_SPACE() \
- if (addSpace && !added) \
- Buf_AddByte(buf, (Byte)' '); \
- added = 1
-
- added = 0;
- wp = word;
- pat = patternp;
-
- if ((pat->flags & (VAR_SUB_ONE | VAR_SUB_MATCHED)) ==
- (VAR_SUB_ONE | VAR_SUB_MATCHED)) {
- xrv = REG_NOMATCH;
- } else {
- tryagain:
- xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, flags);
- }
-
- switch (xrv) {
- case 0:
- pat->flags |= VAR_SUB_MATCHED;
- if (pat->matches[0].rm_so > 0) {
- MAYBE_ADD_SPACE();
- Buf_AddBytes(buf, pat->matches[0].rm_so,
- (const Byte *)wp);
- }
-
- for (rp = Buf_Data(pat->rhs); *rp; rp++) {
- if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) {
- MAYBE_ADD_SPACE();
- Buf_AddByte(buf, (Byte)rp[1]);
- rp++;
-
- } else if ((*rp == '&') ||
- ((*rp == '\\') && isdigit((unsigned char)rp[1]))) {
- int n;
- const char *subbuf;
- int sublen;
- char errstr[3];
-
- if (*rp == '&') {
- n = 0;
- errstr[0] = '&';
- errstr[1] = '\0';
- } else {
- n = rp[1] - '0';
- errstr[0] = '\\';
- errstr[1] = rp[1];
- errstr[2] = '\0';
- rp++;
- }
-
- if (n > pat->nsub) {
- Error("No subexpression %s",
- &errstr[0]);
- subbuf = "";
- sublen = 0;
-
- } else if ((pat->matches[n].rm_so == -1) &&
- (pat->matches[n].rm_eo == -1)) {
- Error("No match for subexpression %s",
- &errstr[0]);
- subbuf = "";
- sublen = 0;
-
- } else {
- subbuf = wp + pat->matches[n].rm_so;
- sublen = pat->matches[n].rm_eo -
- pat->matches[n].rm_so;
- }
-
- if (sublen > 0) {
- MAYBE_ADD_SPACE();
- Buf_AddBytes(buf, sublen,
- (const Byte *)subbuf);
- }
- } else {
- MAYBE_ADD_SPACE();
- Buf_AddByte(buf, (Byte)*rp);
- }
- }
- wp += pat->matches[0].rm_eo;
- if (pat->flags & VAR_SUB_GLOBAL) {
- flags |= REG_NOTBOL;
- if (pat->matches[0].rm_so == 0 &&
- pat->matches[0].rm_eo == 0) {
- MAYBE_ADD_SPACE();
- Buf_AddByte(buf, (Byte)*wp);
- wp++;
- }
- if (*wp)
- goto tryagain;
- }
- if (*wp) {
- MAYBE_ADD_SPACE();
- Buf_Append(buf, wp);
- }
- break;
-
- default:
- VarREError(xrv, &pat->re, "Unexpected regex error");
- /* fall through */
-
- case REG_NOMATCH:
- if (*wp) {
- MAYBE_ADD_SPACE();
- Buf_Append(buf, wp);
- }
- break;
- }
- return (addSpace || added);
-}
-
-/**
- * Find a variable in a variable list.
- */
-static Var *
-VarLookup(Lst *vlist, const char *name)
-{
- LstNode *ln;
-
- LST_FOREACH(ln, vlist)
- if (strcmp(((const Var *)Lst_Datum(ln))->name, name) == 0)
- return (Lst_Datum(ln));
- return (NULL);
-}
-
-/**
- * Expand a variable name's embedded variables in the given context.
- *
- * Results:
- * The contents of name, possibly expanded.
- */
-static char *
-VarPossiblyExpand(const char *name, GNode *ctxt)
-{
- Buffer *buf;
-
- if (strchr(name, '$') != NULL) {
- buf = Var_Subst(name, ctxt, 0);
- return (Buf_Peel(buf));
- } else {
- return estrdup(name);
- }
-}
-
-/**
- * If the variable name begins with a '.', it could very well be
- * one of the local ones. We check the name against all the local
- * variables and substitute the short version in for 'name' if it
- * matches one of them.
- */
-static const char *
-VarLocal(const char name[])
-{
- if (name[0] == '.') {
- switch (name[1]) {
- case 'A':
- if (!strcmp(name, ".ALLSRC"))
- return (ALLSRC);
- if (!strcmp(name, ".ARCHIVE"))
- return (ARCHIVE);
- break;
- case 'I':
- if (!strcmp(name, ".IMPSRC"))
- return (IMPSRC);
- break;
- case 'M':
- if (!strcmp(name, ".MEMBER"))
- return (MEMBER);
- break;
- case 'O':
- if (!strcmp(name, ".OODATE"))
- return (OODATE);
- break;
- case 'P':
- if (!strcmp(name, ".PREFIX"))
- return (PREFIX);
- break;
- case 'T':
- if (!strcmp(name, ".TARGET"))
- return (TARGET);
- break;
- default:
- break;
- }
- }
- return (name);
-}
-
-/**
- * Find the given variable in the given context and the environment.
- *
- * Results:
- * A pointer to the structure describing the desired variable or
- * NULL if the variable does not exist.
- */
-static Var *
-VarFindEnv(const char name[], GNode *ctxt)
-{
- Var *var;
-
- name = VarLocal(name);
-
- if ((var = VarLookup(&ctxt->context, name)) != NULL)
- return (var);
-
- if ((var = VarLookup(&VAR_ENV->context, name)) != NULL)
- return (var);
-
- return (NULL);
-}
-
-/**
- * Look for the variable in the given context.
- */
-static Var *
-VarFindOnly(const char name[], GNode *ctxt)
-{
- Var *var;
-
- name = VarLocal(name);
-
- if ((var = VarLookup(&ctxt->context, name)) != NULL)
- return (var);
-
- return (NULL);
-}
-
-/**
- * Look for the variable in all contexts.
- */
-static Var *
-VarFindAny(const char name[], GNode *ctxt)
-{
- Boolean localCheckEnvFirst;
- LstNode *ln;
- Var *var;
-
- name = VarLocal(name);
-
- /*
- * Note whether this is one of the specific variables we were told
- * through the -E flag to use environment-variable-override for.
- */
- localCheckEnvFirst = FALSE;
- LST_FOREACH(ln, &envFirstVars) {
- if (strcmp(Lst_Datum(ln), name) == 0) {
- localCheckEnvFirst = TRUE;
- break;
- }
- }
-
- /*
- * First look for the variable in the given context. If it's not there,
- * look for it in VAR_CMD, VAR_GLOBAL and the environment,
- * in that order, depending on the FIND_* flags in 'flags'
- */
- if ((var = VarLookup(&ctxt->context, name)) != NULL)
- return (var);
-
- /* not there - try command line context */
- if (ctxt != VAR_CMD) {
- if ((var = VarLookup(&VAR_CMD->context, name)) != NULL)
- return (var);
- }
-
- /* not there - try global context, but only if not -e/-E */
- if (ctxt != VAR_GLOBAL && (!checkEnvFirst && !localCheckEnvFirst)) {
- if ((var = VarLookup(&VAR_GLOBAL->context, name)) != NULL)
- return (var);
- }
-
- if ((var = VarLookup(&VAR_ENV->context, name)) != NULL)
- return (var);
-
- /* deferred check for the environment (in case of -e/-E) */
- if ((ctxt != VAR_GLOBAL) && (checkEnvFirst || localCheckEnvFirst)) {
- if ((var = VarLookup(&VAR_GLOBAL->context, name)) != NULL)
- return (var);
- }
-
- return (NULL);
-}
-
-/**
- * Add a new variable of name name and value val to the given context.
- *
- * Side Effects:
- * The new variable is placed at the front of the given context
- * The name and val arguments are duplicated so they may
- * safely be freed.
- */
-static Var *
-VarAdd(const char *name, const char *val, GNode *ctxt)
-{
- Var *v;
-
- Lst_AtFront(&ctxt->context, v = VarCreate(name, val, 0));
- DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, name, val));
- return (v);
-}
-
-/**
- * Remove a variable from a context.
- *
- * Side Effects:
- * The Var structure is removed and freed.
- */
-void
-Var_Delete(const char *name, GNode *ctxt)
-{
- LstNode *ln;
-
- DEBUGF(VAR, ("%s:delete %s\n", ctxt->name, name));
- LST_FOREACH(ln, &ctxt->context) {
- if (strcmp(((const Var *)Lst_Datum(ln))->name, name) == 0) {
- VarDestroy(Lst_Datum(ln), TRUE);
- Lst_Remove(&ctxt->context, ln);
- break;
- }
- }
-}
-
-/**
- * Set the variable name to the value val in the given context.
- *
- * Side Effects:
- * If the variable doesn't yet exist, a new record is created for it.
- * Else the old value is freed and the new one stuck in its place
- *
- * Notes:
- * The variable is searched for only in its context before being
- * created in that context. I.e. if the context is VAR_GLOBAL,
- * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only
- * VAR_CMD->context is searched. This is done to avoid the literally
- * thousands of unnecessary strcmp's that used to be done to
- * set, say, $(@) or $(<).
- */
-void
-Var_Set(const char *name, const char *val, GNode *ctxt)
-{
- Var *v;
- char *n;
-
- /*
- * We only look for a variable in the given context since anything
- * set here will override anything in a lower context, so there's not
- * much point in searching them all just to save a bit of memory...
- */
- n = VarPossiblyExpand(name, ctxt);
- v = VarFindOnly(n, ctxt);
- if (v == NULL) {
- v = VarAdd(n, val, ctxt);
- } else {
- Buf_Clear(v->val);
- Buf_Append(v->val, val);
- DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, n, val));
- }
-
- if (ctxt == VAR_CMD || (v->flags & VAR_TO_ENV)) {
- /*
- * Any variables given on the command line
- * are automatically exported to the
- * environment (as per POSIX standard)
- */
- setenv(n, val, 1);
- }
-
- free(n);
-}
-
-/**
- * Set the a global name variable to the value.
- */
-void
-Var_SetGlobal(const char name[], const char value[])
-{
-
- Var_Set(name, value, VAR_GLOBAL);
-}
-
-
-/**
- * Set the VAR_TO_ENV flag on a variable
- */
-void
-Var_SetEnv(const char *name, GNode *ctxt)
-{
- Var *v;
-
- v = VarFindOnly(name, VAR_CMD);
- if (v != NULL) {
- /*
- * Do not allow .EXPORT: to be set on variables
- * from the comand line or MAKEFLAGS.
- */
- Error(
- "Warning: Did not set .EXPORTVAR: on %s because it "
- "is from the comand line or MAKEFLAGS", name);
- return;
- }
-
- v = VarFindAny(name, ctxt);
- if (v == NULL) {
- Lst_AtFront(&VAR_ENV->context,
- VarCreate(name, NULL, VAR_TO_ENV));
- setenv(name, "", 1);
- Error("Warning: .EXPORTVAR: set on undefined variable %s", name);
- } else {
- if ((v->flags & VAR_TO_ENV) == 0) {
- v->flags |= VAR_TO_ENV;
- setenv(v->name, Buf_Data(v->val), 1);
- }
- }
-}
-
-/**
- * The variable of the given name has the given value appended to it in
- * the given context.
- *
- * Side Effects:
- * If the variable doesn't exist, it is created. Else the strings
- * are concatenated (with a space in between).
- *
- * Notes:
- * Only if the variable is being sought in the global context is the
- * environment searched.
- * XXX: Knows its calling circumstances in that if called with ctxt
- * an actual target, it will only search that context since only
- * a local variable could be being appended to. This is actually
- * a big win and must be tolerated.
- */
-void
-Var_Append(const char *name, const char *val, GNode *ctxt)
-{
- Var *v;
- char *n;
-
- n = VarPossiblyExpand(name, ctxt);
- if (ctxt == VAR_GLOBAL) {
- v = VarFindEnv(n, ctxt);
- } else {
- v = VarFindOnly(n, ctxt);
- }
- if (v == NULL) {
- VarAdd(n, val, ctxt);
- } else {
- Buf_AddByte(v->val, (Byte)' ');
- Buf_Append(v->val, val);
- DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, n, Buf_Data(v->val)));
- }
- free(n);
-}
-
-/**
- * See if the given variable exists.
- *
- * Results:
- * TRUE if it does, FALSE if it doesn't
- */
-Boolean
-Var_Exists(const char *name, GNode *ctxt)
-{
- Var *v;
- char *n;
-
- n = VarPossiblyExpand(name, ctxt);
- v = VarFindAny(n, ctxt);
- if (v == NULL) {
- free(n);
- return (FALSE);
- } else {
- free(n);
- return (TRUE);
- }
-}
-
-/**
- * Return the value of the named variable in the given context
- *
- * Results:
- * The value if the variable exists, NULL if it doesn't.
- */
-const char *
-Var_Value(const char name[], GNode *ctxt)
-{
- Var *v;
- char *n;
-
- n = VarPossiblyExpand(name, ctxt);
- v = VarFindAny(n, ctxt);
- free(n);
- if (v == NULL) {
- return (NULL);
- } else {
- return (Buf_Data(v->val));
- }
-}
-
-/**
- * Modify each of the words of the passed string using the given
- * function. Used to implement all modifiers.
- *
- * Results:
- * A string of all the words modified appropriately.
- *
- * Side Effects:
- * Uses brk_string() so it invalidates any previous call to
- * brk_string().
- */
-static char *
-VarModify(const char *str, VarModifyProc *modProc, void *datum)
-{
- ArgArray aa;
- Buffer *buf; /* Buffer for the new string */
- int i;
- Boolean addSpace; /*
- * TRUE if need to add a space to
- * the buffer before adding the
- * trimmed word
- */
-
- brk_string(&aa, str, FALSE);
-
- addSpace = FALSE;
- buf = Buf_Init(0);
- for (i = 1; i < aa.argc; i++)
- addSpace = (*modProc)(aa.argv[i], addSpace, buf, datum);
-
- ArgArray_Done(&aa);
- return (Buf_Peel(buf));
-}
-
-/**
- * Sort the words in the string.
- *
- * Input:
- * str String whose words should be sorted
- * cmp A comparison function to control the ordering
- *
- * Results:
- * A string containing the words sorted
- */
-static char *
-VarSortWords(const char *str, int (*cmp)(const void *, const void *))
-{
- ArgArray aa;
- Buffer *buf;
- int i;
-
- brk_string(&aa, str, FALSE);
- qsort(aa.argv + 1, aa.argc - 1, sizeof(char *), cmp);
-
- buf = Buf_Init(0);
- for (i = 1; i < aa.argc; i++) {
- Buf_Append(buf, aa.argv[i]);
- Buf_AddByte(buf, (Byte)((i < aa.argc - 1) ? ' ' : '\0'));
- }
-
- ArgArray_Done(&aa);
- return (Buf_Peel(buf));
-}
-
-static int
-SortIncreasing(const void *l, const void *r)
-{
-
- return (strcmp(*(const char* const*)l, *(const char* const*)r));
-}
-
-/**
- * Remove adjacent duplicate words.
- *
- * Results:
- * A string containing the resulting words.
- */
-static char *
-VarUniq(const char *str)
-{
- ArgArray aa;
- Buffer *buf; /* Buffer for new string */
- int i, j;
-
- buf = Buf_Init(0);
- brk_string(&aa, str, FALSE);
-
- if (aa.argc > 2) {
- for (j = 1, i = 2; i < aa.argc; i++) {
- if (strcmp(aa.argv[i], aa.argv[j]) != 0 && (++j != i))
- aa.argv[j] = aa.argv[i];
- }
- aa.argc = j + 1;
- }
-
- for (i = 1; i < aa.argc; i++) {
- Buf_AddBytes(buf, strlen(aa.argv[i]), (Byte *)aa.argv[i]);
- if (i != aa.argc - 1)
- Buf_AddByte(buf, ' ');
- }
- Buf_AddByte(buf, '\0');
-
- ArgArray_Done(&aa);
- return (Buf_Peel(buf));
-}
-
-/**
- * Pass through the tstr looking for 1) escaped delimiters,
- * '$'s and backslashes (place the escaped character in
- * uninterpreted) and 2) unescaped $'s that aren't before
- * the delimiter (expand the variable substitution).
- * Return the expanded string or NULL if the delimiter was missing
- * If pattern is specified, handle escaped ampersands, and replace
- * unescaped ampersands with the lhs of the pattern.
- *
- * Results:
- * A string of all the words modified appropriately.
- * If length is specified, return the string length of the buffer
- * If flags is specified and the last character of the pattern is a
- * $ set the VAR_MATCH_END bit of flags.
- */
-static Buffer *
-VarGetPattern(VarParser *vp, int delim, int *flags, VarPattern *patt)
-{
- Buffer *buf;
-
- buf = Buf_Init(0);
-
- /*
- * Skim through until the matching delimiter is found; pick up
- * variable substitutions on the way. Also allow backslashes to quote
- * the delimiter, $, and \, but don't touch other backslashes.
- */
- while (*vp->ptr != '\0') {
- if (*vp->ptr == delim) {
- return (buf);
-
- } else if ((vp->ptr[0] == '\\') &&
- ((vp->ptr[1] == delim) ||
- (vp->ptr[1] == '\\') ||
- (vp->ptr[1] == '$') ||
- (vp->ptr[1] == '&' && patt != NULL))) {
- vp->ptr++; /* consume backslash */
- Buf_AddByte(buf, (Byte)vp->ptr[0]);
- vp->ptr++;
-
- } else if (vp->ptr[0] == '$') {
- if (vp->ptr[1] == delim) {
- if (flags == NULL) {
- Buf_AddByte(buf, (Byte)vp->ptr[0]);
- vp->ptr++;
- } else {
- /*
- * Unescaped $ at end of patt =>
- * anchor patt at end.
- */
- *flags |= VAR_MATCH_END;
- vp->ptr++;
- }
- } else {
- VarParser subvp = {
- vp->ptr,
- vp->ptr,
- vp->ctxt,
- vp->err,
- vp->execute
- };
- char *rval;
- Boolean rfree;
-
- /*
- * If unescaped dollar sign not
- * before the delimiter, assume it's
- * a variable substitution and
- * recurse.
- */
- rval = VarParse(&subvp, &rfree);
- Buf_Append(buf, rval);
- if (rfree)
- free(rval);
- vp->ptr = subvp.ptr;
- }
- } else if (vp->ptr[0] == '&' && patt != NULL) {
- Buf_AppendBuf(buf, patt->lhs);
- vp->ptr++;
- } else {
- Buf_AddByte(buf, (Byte)vp->ptr[0]);
- vp->ptr++;
- }
- }
-
- Buf_Destroy(buf, TRUE);
- return (NULL);
-}
-
-/**
- * Make sure this variable is fully expanded.
- */
-static char *
-VarExpand(Var *v, VarParser *vp)
-{
- char *value;
- char *result;
-
- if (v->flags & VAR_IN_USE) {
- Fatal("Variable %s is recursive.", v->name);
- /* NOTREACHED */
- }
-
- v->flags |= VAR_IN_USE;
-
- /*
- * Before doing any modification, we have to make sure the
- * value has been fully expanded. If it looks like recursion
- * might be necessary (there's a dollar sign somewhere in the
- * variable's value) we just call Var_Subst to do any other
- * substitutions that are necessary. Note that the value
- * returned by Var_Subst will have been
- * dynamically-allocated, so it will need freeing when we
- * return.
- */
- value = Buf_Data(v->val);
- if (strchr(value, '$') == NULL) {
- result = strdup(value);
- } else {
- Buffer *buf;
-
- buf = Var_Subst(value, vp->ctxt, vp->err);
- result = Buf_Peel(buf);
- }
-
- v->flags &= ~VAR_IN_USE;
-
- return (result);
-}
-
-/**
- * Select only those words in value that match the modifier.
- */
-static char *
-modifier_M(VarParser *vp, const char value[], char endc)
-{
- char *patt;
- char *ptr;
- char *newValue;
- char modifier;
-
- modifier = vp->ptr[0];
- vp->ptr++; /* consume 'M' or 'N' */
-
- /*
- * Compress the \:'s out of the pattern, so allocate enough
- * room to hold the uncompressed pattern and compress the
- * pattern into that space.
- */
- patt = estrdup(vp->ptr);
- ptr = patt;
- while (vp->ptr[0] != '\0') {
- if (vp->ptr[0] == endc || vp->ptr[0] == ':') {
- break;
- }
- if (vp->ptr[0] == '\\' &&
- (vp->ptr[1] == endc || vp->ptr[1] == ':')) {
- vp->ptr++; /* consume backslash */
- }
- *ptr = vp->ptr[0];
- ptr++;
- vp->ptr++;
- }
- *ptr = '\0';
- DEBUGF(VAR, ("Pattern :%s\n", patt));
-
- if (modifier == 'M') {
- newValue = VarModify(value, VarMatch, patt);
- } else {
- newValue = VarModify(value, VarNoMatch, patt);
- }
- free(patt);
-
- return (newValue);
-}
-
-/**
- * Substitute the replacement string for the pattern. The substitution
- * is applied to each word in value.
- */
-static char *
-modifier_S(VarParser *vp, const char value[], Var *v)
-{
- VarPattern patt;
- char delim;
- char *newValue;
-
- patt.flags = 0;
-
- vp->ptr++; /* consume 'S' */
-
- delim = *vp->ptr; /* used to find end of pattern */
- vp->ptr++; /* consume 1st delim */
-
- /*
- * If pattern begins with '^', it is anchored to the start of the
- * word -- skip over it and flag pattern.
- */
- if (*vp->ptr == '^') {
- patt.flags |= VAR_MATCH_START;
- vp->ptr++;
- }
-
- patt.lhs = VarGetPattern(vp, delim, &patt.flags, NULL);
- if (patt.lhs == NULL) {
- /*
- * LHS didn't end with the delim, complain and exit.
- */
- Fatal("Unclosed substitution for %s (%c missing)",
- v->name, delim);
- }
-
- vp->ptr++; /* consume 2nd delim */
-
- patt.rhs = VarGetPattern(vp, delim, NULL, &patt);
- if (patt.rhs == NULL) {
- /*
- * RHS didn't end with the delim, complain and exit.
- */
- Fatal("Unclosed substitution for %s (%c missing)",
- v->name, delim);
- }
-
- vp->ptr++; /* consume last delim */
-
- /*
- * Check for global substitution. If 'g' after the final delimiter,
- * substitution is global and is marked that way.
- */
- if (vp->ptr[0] == 'g') {
- patt.flags |= VAR_SUB_GLOBAL;
- vp->ptr++;
- }
-
- /*
- * Global substitution of the empty string causes an infinite number
- * of matches, unless anchored by '^' (start of string) or '$' (end
- * of string). Catch the infinite substitution here. Note that flags
- * can only contain the 3 bits we're interested in so we don't have
- * to mask unrelated bits. We can test for equality.
- */
- if (Buf_Size(patt.lhs) == 0 && patt.flags == VAR_SUB_GLOBAL)
- Fatal("Global substitution of the empty string");
-
- newValue = VarModify(value, VarSubstitute, &patt);
-
- /*
- * Free the two strings.
- */
- free(patt.lhs);
- free(patt.rhs);
-
- return (newValue);
-}
-
-static char *
-modifier_C(VarParser *vp, char value[], Var *v)
-{
- VarPattern patt;
- char delim;
- int error;
- char *newValue;
-
- patt.flags = 0;
-
- vp->ptr++; /* consume 'C' */
-
- delim = *vp->ptr; /* delimiter between sections */
-
- vp->ptr++; /* consume 1st delim */
-
- patt.lhs = VarGetPattern(vp, delim, NULL, NULL);
- if (patt.lhs == NULL) {
- Fatal("Unclosed substitution for %s (%c missing)",
- v->name, delim);
- }
-
- vp->ptr++; /* consume 2st delim */
-
- patt.rhs = VarGetPattern(vp, delim, NULL, NULL);
- if (patt.rhs == NULL) {
- Fatal("Unclosed substitution for %s (%c missing)",
- v->name, delim);
- }
-
- vp->ptr++; /* consume last delim */
-
- switch (*vp->ptr) {
- case 'g':
- patt.flags |= VAR_SUB_GLOBAL;
- vp->ptr++; /* consume 'g' */
- break;
- case '1':
- patt.flags |= VAR_SUB_ONE;
- vp->ptr++; /* consume '1' */
- break;
- default:
- break;
- }
-
- error = regcomp(&patt.re, Buf_Data(patt.lhs), REG_EXTENDED);
- if (error) {
- VarREError(error, &patt.re, "RE substitution error");
- free(patt.rhs);
- free(patt.lhs);
- return (var_Error);
- }
-
- patt.nsub = patt.re.re_nsub + 1;
- if (patt.nsub < 1)
- patt.nsub = 1;
- if (patt.nsub > 10)
- patt.nsub = 10;
- patt.matches = emalloc(patt.nsub * sizeof(regmatch_t));
-
- newValue = VarModify(value, VarRESubstitute, &patt);
-
- regfree(&patt.re);
- free(patt.matches);
- free(patt.rhs);
- free(patt.lhs);
-
- return (newValue);
-}
-
-static char *
-sysVvarsub(VarParser *vp, char startc, Var *v, const char value[])
-{
-#ifdef SYSVVARSUB
- /*
- * This can either be a bogus modifier or a System-V substitution
- * command.
- */
- char endc;
- VarPattern patt;
- Boolean eqFound;
- int cnt;
- char *newStr;
- const char *cp;
-
- endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE;
-
- patt.flags = 0;
-
- /*
- * First we make a pass through the string trying to verify it is a
- * SYSV-make-style translation: it must be: <string1>=<string2>)
- */
- eqFound = FALSE;
- cp = vp->ptr;
- cnt = 1;
- while (*cp != '\0' && cnt) {
- if (*cp == '=') {
- eqFound = TRUE;
- /* continue looking for endc */
- } else if (*cp == endc)
- cnt--;
- else if (*cp == startc)
- cnt++;
- if (cnt)
- cp++;
- }
-
- if (*cp == endc && eqFound) {
- /*
- * Now we break this sucker into the lhs and rhs.
- */
- patt.lhs = VarGetPattern(vp, '=', &patt.flags, NULL);
- if (patt.lhs == NULL) {
- Fatal("Unclosed substitution for %s (%c missing)",
- v->name, '=');
- }
- vp->ptr++; /* consume '=' */
-
- patt.rhs = VarGetPattern(vp, endc, NULL, &patt);
- if (patt.rhs == NULL) {
- Fatal("Unclosed substitution for %s (%c missing)",
- v->name, endc);
- }
-
- /*
- * SYSV modifications happen through the whole string. Note
- * the pattern is anchored at the end.
- */
- newStr = VarModify(value, VarSYSVMatch, &patt);
-
- free(patt.lhs);
- free(patt.rhs);
- } else
-#endif
- {
- Error("Unknown modifier '%c'\n", *vp->ptr);
- vp->ptr++;
- while (*vp->ptr != '\0') {
- if (*vp->ptr == endc && *vp->ptr == ':') {
- break;
- }
- vp->ptr++;
- }
- newStr = var_Error;
- }
-
- return (newStr);
-}
-
-/**
- * Quote shell meta-characters in the string
- *
- * Results:
- * The quoted string
- */
-static char *
-Var_Quote(const char *str)
-{
- Buffer *buf;
- /* This should cover most shells :-( */
- static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
-
- buf = Buf_Init(MAKE_BSIZE);
- for (; *str; str++) {
- if (strchr(meta, *str) != NULL)
- Buf_AddByte(buf, (Byte)'\\');
- Buf_AddByte(buf, (Byte)*str);
- }
-
- return (Buf_Peel(buf));
-}
-
-
-/*
- * Now we need to apply any modifiers the user wants applied.
- * These are:
- * :M<pattern>
- * words which match the given <pattern>.
- * <pattern> is of the standard file
- * wildcarding form.
- * :N<pattern>
- * words which do not match the given <pattern>
- * <pattern> is of the standard file
- * wildcarding form.
- * :S<d><pat1><d><pat2><d>[g]
- * Substitute <pat2> for <pat1> in the value
- * :C<d><pat1><d><pat2><d>[g]
- * Substitute <pat2> for regex <pat1> in the value
- * :H Substitute the head of each word
- * :T Substitute the tail of each word
- * :E Substitute the extension (minus '.') of
- * each word
- * :R Substitute the root of each word
- * (pathname minus the suffix).
- * :lhs=rhs
- * Like :S, but the rhs goes to the end of
- * the invocation.
- * :U Converts variable to upper-case.
- * :L Converts variable to lower-case.
- * :O ("Order") Alphabeticaly sort words in variable.
- * :u ("uniq") Remove adjacent duplicate words.
- */
-static char *
-ParseModifier(VarParser *vp, char startc, Var *v, Boolean *freeResult)
-{
- char *value;
- char endc;
-
- value = VarExpand(v, vp);
- *freeResult = TRUE;
-
- endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE;
-
- vp->ptr++; /* consume first colon */
-
- while (*vp->ptr != '\0') {
- char *newStr; /* New value to return */
-
- if (*vp->ptr == endc) {
- return (value);
- }
-
- DEBUGF(VAR, ("Applying :%c to \"%s\"\n", *vp->ptr, value));
- switch (*vp->ptr) {
- case 'N':
- case 'M':
- newStr = modifier_M(vp, value, endc);
- break;
- case 'S':
- newStr = modifier_S(vp, value, v);
- break;
- case 'C':
- newStr = modifier_C(vp, value, v);
- break;
- case 't':
- /* :tl :tu for OSF ODE & NetBSD make compatibility */
- switch (vp->ptr[1]) {
- case 'l':
- vp->ptr++;
- goto mod_lower;
- break;
- case 'u':
- vp->ptr++;
- goto mod_upper;
- break;
- }
- /* FALLTHROUGH */
- default:
- if (vp->ptr[1] != endc && vp->ptr[1] != ':') {
-#ifdef SUNSHCMD
- if ((vp->ptr[0] == 's') &&
- (vp->ptr[1] == 'h') &&
- (vp->ptr[2] == endc || vp->ptr[2] == ':')) {
- const char *error = NULL;
-
- if (vp->execute) {
- newStr = Buf_Peel(
- Cmd_Exec(value, &error));
- } else {
- newStr = estrdup("");
- }
-
- if (error)
- Error(error, value);
- vp->ptr += 2;
- } else
-#endif
- {
- newStr = sysVvarsub(vp, startc, v, value);
- }
- break;
- }
-
- switch (vp->ptr[0]) {
- case 'L':
- mod_lower:
- {
- const char *cp;
- Buffer *buf;
- buf = Buf_Init(MAKE_BSIZE);
- for (cp = value; *cp; cp++)
- Buf_AddByte(buf, (Byte)tolower(*cp));
-
- newStr = Buf_Peel(buf);
-
- vp->ptr++;
- break;
- }
- case 'O':
- newStr = VarSortWords(value, SortIncreasing);
- vp->ptr++;
- break;
- case 'Q':
- newStr = Var_Quote(value);
- vp->ptr++;
- break;
- case 'T':
- newStr = VarModify(value, VarTail, NULL);
- vp->ptr++;
- break;
- case 'U':
- mod_upper:
- {
- const char *cp;
- Buffer *buf;
- buf = Buf_Init(MAKE_BSIZE);
- for (cp = value; *cp; cp++)
- Buf_AddByte(buf, (Byte)toupper(*cp));
-
- newStr = Buf_Peel(buf);
-
- vp->ptr++;
- break;
- }
- case 'H':
- newStr = VarModify(value, VarHead, NULL);
- vp->ptr++;
- break;
- case 'E':
- newStr = VarModify(value, VarSuffix, NULL);
- vp->ptr++;
- break;
- case 'R':
- newStr = VarModify(value, VarRoot, NULL);
- vp->ptr++;
- break;
- case 'u':
- newStr = VarUniq(value);
- vp->ptr++;
- break;
- default:
- newStr = sysVvarsub(vp, startc, v, value);
- break;
- }
- break;
- }
-
- DEBUGF(VAR, ("Result is \"%s\"\n", newStr));
- if (*freeResult) {
- free(value);
- }
-
- value = newStr;
- *freeResult = (value == var_Error) ? FALSE : TRUE;
-
- if (vp->ptr[0] == ':') {
- vp->ptr++; /* consume colon */
- }
- }
-
- return (value);
-}
-
-static char *
-ParseRestModifier(VarParser *vp, char startc, Buffer *buf, Boolean *freeResult)
-{
- const char *vname;
- size_t vlen;
- Var *v;
- char *value;
-
- vname = Buf_GetAll(buf, &vlen);
-
- v = VarFindAny(vname, vp->ctxt);
- if (v != NULL) {
- value = ParseModifier(vp, startc, v, freeResult);
- return (value);
- }
-
- if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) {
- size_t consumed;
- /*
- * Still need to get to the end of the variable
- * specification, so kludge up a Var structure for the
- * modifications
- */
- v = VarCreate(vname, NULL, VAR_JUNK);
- value = ParseModifier(vp, startc, v, freeResult);
- if (*freeResult) {
- free(value);
- }
- VarDestroy(v, TRUE);
-
- consumed = vp->ptr - vp->input + 1;
- /*
- * If substituting a local variable in a non-local context,
- * assume it's for dynamic source stuff. We have to handle
- * this specially and return the longhand for the variable
- * with the dollar sign escaped so it makes it back to the
- * caller. Only four of the local variables are treated
- * specially as they are the only four that will be set when
- * dynamic sources are expanded.
- */
- if (vlen == 1 ||
- (vlen == 2 && (vname[1] == 'F' || vname[1] == 'D'))) {
- if (strchr("!%*@", vname[0]) != NULL) {
- value = emalloc(consumed + 1);
- strncpy(value, vp->input, consumed);
- value[consumed] = '\0';
-
- *freeResult = TRUE;
- return (value);
- }
- }
- if (vlen > 2 &&
- vname[0] == '.' &&
- isupper((unsigned char)vname[1])) {
- if ((strncmp(vname, ".TARGET", vlen - 1) == 0) ||
- (strncmp(vname, ".ARCHIVE", vlen - 1) == 0) ||
- (strncmp(vname, ".PREFIX", vlen - 1) == 0) ||
- (strncmp(vname, ".MEMBER", vlen - 1) == 0)) {
- value = emalloc(consumed + 1);
- strncpy(value, vp->input, consumed);
- value[consumed] = '\0';
-
- *freeResult = TRUE;
- return (value);
- }
- }
-
- *freeResult = FALSE;
- return (vp->err ? var_Error : varNoError);
- } else {
- /*
- * Check for D and F forms of local variables since we're in
- * a local context and the name is the right length.
- */
- if (vlen == 2 &&
- (vname[1] == 'F' || vname[1] == 'D') &&
- (strchr("!%*<>@", vname[0]) != NULL)) {
- char name[2];
-
- name[0] = vname[0];
- name[1] = '\0';
-
- v = VarFindOnly(name, vp->ctxt);
- if (v != NULL) {
- value = ParseModifier(vp, startc, v, freeResult);
- return (value);
- }
- }
-
- /*
- * Still need to get to the end of the variable
- * specification, so kludge up a Var structure for the
- * modifications
- */
- v = VarCreate(vname, NULL, VAR_JUNK);
- value = ParseModifier(vp, startc, v, freeResult);
- if (*freeResult) {
- free(value);
- }
- VarDestroy(v, TRUE);
-
- *freeResult = FALSE;
- return (vp->err ? var_Error : varNoError);
- }
-}
-
-static char *
-ParseRestEnd(VarParser *vp, Buffer *buf, Boolean *freeResult)
-{
- const char *vname;
- size_t vlen;
- Var *v;
- char *value;
-
- vname = Buf_GetAll(buf, &vlen);
-
- v = VarFindAny(vname, vp->ctxt);
- if (v != NULL) {
- value = VarExpand(v, vp);
- *freeResult = TRUE;
- return (value);
- }
-
- if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) {
- size_t consumed = vp->ptr - vp->input + 1;
-
- /*
- * If substituting a local variable in a non-local context,
- * assume it's for dynamic source stuff. We have to handle
- * this specially and return the longhand for the variable
- * with the dollar sign escaped so it makes it back to the
- * caller. Only four of the local variables are treated
- * specially as they are the only four that will be set when
- * dynamic sources are expanded.
- */
- if (vlen == 1 ||
- (vlen == 2 && (vname[1] == 'F' || vname[1] == 'D'))) {
- if (strchr("!%*@", vname[0]) != NULL) {
- value = emalloc(consumed + 1);
- strncpy(value, vp->input, consumed);
- value[consumed] = '\0';
-
- *freeResult = TRUE;
- return (value);
- }
- }
- if (vlen > 2 &&
- vname[0] == '.' &&
- isupper((unsigned char)vname[1])) {
- if ((strncmp(vname, ".TARGET", vlen - 1) == 0) ||
- (strncmp(vname, ".ARCHIVE", vlen - 1) == 0) ||
- (strncmp(vname, ".PREFIX", vlen - 1) == 0) ||
- (strncmp(vname, ".MEMBER", vlen - 1) == 0)) {
- value = emalloc(consumed + 1);
- strncpy(value, vp->input, consumed);
- value[consumed] = '\0';
-
- *freeResult = TRUE;
- return (value);
- }
- }
- } else {
- /*
- * Check for D and F forms of local variables since we're in
- * a local context and the name is the right length.
- */
- if (vlen == 2 &&
- (vname[1] == 'F' || vname[1] == 'D') &&
- (strchr("!%*<>@", vname[0]) != NULL)) {
- char name[2];
-
- name[0] = vname[0];
- name[1] = '\0';
-
- v = VarFindOnly(name, vp->ctxt);
- if (v != NULL) {
- char *val;
- /*
- * No need for nested expansion or anything,
- * as we're the only one who sets these
- * things and we sure don't put nested
- * invocations in them...
- */
- val = Buf_Data(v->val);
-
- if (vname[1] == 'D') {
- val = VarModify(val, VarHead, NULL);
- } else {
- val = VarModify(val, VarTail, NULL);
- }
-
- *freeResult = TRUE;
- return (val);
- }
- }
- }
-
- *freeResult = FALSE;
- return (vp->err ? var_Error : varNoError);
-}
-
-/**
- * Parse a multi letter variable name, and return it's value.
- */
-static char *
-VarParseLong(VarParser *vp, Boolean *freeResult)
-{
- Buffer *buf;
- char startc;
- char endc;
- char *value;
-
- buf = Buf_Init(MAKE_BSIZE);
-
- startc = vp->ptr[0];
- vp->ptr++; /* consume opening paren or brace */
-
- endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE;
-
- /*
- * Process characters until we reach an end character or a colon,
- * replacing embedded variables as we go.
- */
- while (*vp->ptr != '\0') {
- if (*vp->ptr == endc) {
- value = ParseRestEnd(vp, buf, freeResult);
- vp->ptr++; /* consume closing paren or brace */
- Buf_Destroy(buf, TRUE);
- return (value);
-
- } else if (*vp->ptr == ':') {
- value = ParseRestModifier(vp, startc, buf, freeResult);
- vp->ptr++; /* consume closing paren or brace */
- Buf_Destroy(buf, TRUE);
- return (value);
-
- } else if (*vp->ptr == '$') {
- VarParser subvp = {
- vp->ptr,
- vp->ptr,
- vp->ctxt,
- vp->err,
- vp->execute
- };
- char *rval;
- Boolean rfree;
-
- rval = VarParse(&subvp, &rfree);
- if (rval == var_Error) {
- Fatal("Error expanding embedded variable.");
- }
- Buf_Append(buf, rval);
- if (rfree)
- free(rval);
- vp->ptr = subvp.ptr;
- } else {
- Buf_AddByte(buf, (Byte)*vp->ptr);
- vp->ptr++;
- }
- }
-
- /* If we did not find the end character, return var_Error */
- Buf_Destroy(buf, TRUE);
- *freeResult = FALSE;
- return (var_Error);
-}
-
-/**
- * Parse a single letter variable name, and return it's value.
- */
-static char *
-VarParseShort(VarParser *vp, Boolean *freeResult)
-{
- char vname[2];
- Var *v;
- char *value;
-
- vname[0] = vp->ptr[0];
- vname[1] = '\0';
-
- vp->ptr++; /* consume single letter */
-
- v = VarFindAny(vname, vp->ctxt);
- if (v != NULL) {
- value = VarExpand(v, vp);
- *freeResult = TRUE;
- return (value);
- }
-
- /*
- * If substituting a local variable in a non-local context, assume
- * it's for dynamic source stuff. We have to handle this specially
- * and return the longhand for the variable with the dollar sign
- * escaped so it makes it back to the caller. Only four of the local
- * variables are treated specially as they are the only four that
- * will be set when dynamic sources are expanded.
- */
- if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) {
-
- /* XXX: It looks like $% and $! are reversed here */
- switch (vname[0]) {
- case '@':
- *freeResult = TRUE;
- return (estrdup("$(.TARGET)"));
- case '%':
- *freeResult = TRUE;
- return (estrdup("$(.ARCHIVE)"));
- case '*':
- *freeResult = TRUE;
- return (estrdup("$(.PREFIX)"));
- case '!':
- *freeResult = TRUE;
- return (estrdup("$(.MEMBER)"));
- default:
- *freeResult = FALSE;
- return (vp->err ? var_Error : varNoError);
- }
- }
-
- /* Variable name was not found. */
- *freeResult = FALSE;
- return (vp->err ? var_Error : varNoError);
-}
-
-static char *
-VarParse(VarParser *vp, Boolean *freeResult)
-{
-
- vp->ptr++; /* consume '$' or last letter of conditional */
-
- if (vp->ptr[0] == '\0') {
- /* Error, there is only a dollar sign in the input string. */
- *freeResult = FALSE;
- return (vp->err ? var_Error : varNoError);
-
- } else if (vp->ptr[0] == OPEN_PAREN || vp->ptr[0] == OPEN_BRACE) {
- /* multi letter variable name */
- return (VarParseLong(vp, freeResult));
-
- } else {
- /* single letter variable name */
- return (VarParseShort(vp, freeResult));
- }
-}
-
-/**
- * Given the start of a variable invocation, extract the variable
- * name and find its value, then modify it according to the
- * specification.
- *
- * Results:
- * The value of the variable or var_Error if the specification
- * is invalid. The number of characters in the specification
- * is placed in the variable pointed to by consumed. (for
- * invalid specifications, this is just 2 to skip the '$' and
- * the following letter, or 1 if '$' was the last character
- * in the string). A Boolean in *freeResult telling whether the
- * returned string should be freed by the caller.
- */
-char *
-Var_Parse(const char input[], GNode *ctxt, Boolean err,
- size_t *consumed, Boolean *freeResult)
-{
- VarParser vp = {
- input,
- input,
- ctxt,
- err,
- TRUE
- };
- char *value;
-
- value = VarParse(&vp, freeResult);
- *consumed += vp.ptr - vp.input;
- return (value);
-}
-
-/*
- * Given the start of a variable invocation, determine the length
- * of the specification.
- *
- * Results:
- * The number of characters in the specification. For invalid
- * specifications, this is just 2 to skip the '$' and the
- * following letter, or 1 if '$' was the last character in the
- * string.
- */
-size_t
-Var_Match(const char input[], GNode *ctxt)
-{
- VarParser vp = {
- input,
- input,
- ctxt,
- FALSE,
- FALSE
- };
- char *value;
- Boolean freeResult;
-
- value = VarParse(&vp, &freeResult);
- if (freeResult) {
- free(value);
- }
- return (vp.ptr - vp.input);
-}
-
-static int
-match_var(const char str[], const char var[])
-{
- const char *start = str;
- size_t len;
-
- str++; /* consume '$' */
-
- if (str[0] == OPEN_PAREN || str[0] == OPEN_BRACE) {
- str++; /* consume opening paren or brace */
-
- while (str[0] != '\0') {
- if (str[0] == '$') {
- /*
- * A variable inside the variable. We cannot
- * expand the external variable yet.
- */
- return (str - start);
- } else if (str[0] == ':' ||
- str[0] == CLOSE_PAREN ||
- str[0] == CLOSE_BRACE) {
- len = str - (start + 2);
-
- if (strncmp(var, start + 2, len) == 0 && var[len] == '\0') {
- return (0); /* match */
- } else {
- /*
- * Not the variable we want to
- * expand.
- */
- return (str - start);
- }
- } else {
- ++str;
- }
- }
- return (str - start);
- } else {
- /* Single letter variable name */
- if (var[1] == '\0' && var[0] == str[0]) {
- return (0); /* match */
- } else {
- str++; /* consume variable name */
- return (str - start);
- }
- }
-}
-
-/**
- * Substitute for all variables in the given string in the given
- * context If err is TRUE, Parse_Error will be called when an
- * undefined variable is encountered.
- *
- * Results:
- * The resulting string.
- *
- * Side Effects:
- * None. The old string must be freed by the caller
- */
-Buffer *
-Var_Subst(const char *str, GNode *ctxt, Boolean err)
-{
- Boolean errorReported;
- Buffer *buf; /* Buffer for forming things */
-
- /*
- * Set TRUE if an error has already been reported to prevent a
- * plethora of messages when recursing. XXXHB this comment sounds
- * wrong.
- */
- errorReported = FALSE;
-
- buf = Buf_Init(0);
- while (str[0] != '\0') {
- if ((str[0] == '$') && (str[1] == '$')) {
- /*
- * A dollar sign may be escaped with another dollar
- * sign. In such a case, we skip over the escape
- * character and store the dollar sign into the
- * buffer directly.
- */
- str++;
- Buf_AddByte(buf, (Byte)str[0]);
- str++;
-
- } else if (str[0] == '$') {
- /* Variable invocation. */
- VarParser subvp = {
- str,
- str,
- ctxt,
- err,
- TRUE
- };
- char *rval;
- Boolean rfree;
-
- rval = VarParse(&subvp, &rfree);
-
- /*
- * When we come down here, val should either point to
- * the value of this variable, suitably modified, or
- * be NULL. Length should be the total length of the
- * potential variable invocation (from $ to end
- * character...)
- */
- if (rval == var_Error || rval == varNoError) {
- /*
- * If performing old-time variable
- * substitution, skip over the variable and
- * continue with the substitution. Otherwise,
- * store the dollar sign and advance str so
- * we continue with the string...
- */
- if (oldVars) {
- str = subvp.ptr;
- } else if (err) {
- /*
- * If variable is undefined, complain
- * and skip the variable. The
- * complaint will stop us from doing
- * anything when the file is parsed.
- */
- if (!errorReported) {
- Parse_Error(PARSE_FATAL,
- "Undefined variable \"%.*s\"", subvp.ptr - subvp.input, str);
- }
- errorReported = TRUE;
- str = subvp.ptr;
- } else {
- Buf_AddByte(buf, (Byte)str[0]);
- str++;
- }
- } else {
- /*
- * Copy all the characters from the variable
- * value straight into the new string.
- */
- Buf_Append(buf, rval);
- if (rfree) {
- free(rval);
- }
- str = subvp.ptr;
- }
- } else {
- Buf_AddByte(buf, (Byte)str[0]);
- str++;
- }
- }
-
- return (buf);
-}
-
-/**
- * Substitute for all variables except if it is the same as 'var',
- * in the given string in the given context. If err is TRUE,
- * Parse_Error will be called when an undefined variable is
- * encountered.
- *
- * Results:
- * The resulting string.
- *
- * Side Effects:
- * None. The old string must be freed by the caller
- */
-Buffer *
-Var_SubstOnly(const char *var, const char *str, Boolean err)
-{
- GNode *ctxt = VAR_GLOBAL;
- Boolean errorReported;
- Buffer *buf; /* Buffer for forming things */
-
- /*
- * Set TRUE if an error has already been reported to prevent a
- * plethora of messages when recursing. XXXHB this comment sounds
- * wrong.
- */
- errorReported = FALSE;
-
- buf = Buf_Init(0);
- while (str[0] != '\0') {
- if (str[0] == '$') {
- int skip;
-
- skip = match_var(str, var);
- if (skip > 0) {
- Buf_AddBytes(buf, skip, str);
- str += skip;
- } else {
- /* Variable invocation. */
- VarParser subvp = {
- str,
- str,
- ctxt,
- err,
- TRUE
- };
- char *rval;
- Boolean rfree;
-
- rval = VarParse(&subvp, &rfree);
-
- /*
- * When we get down here, rval should either
- * point to the value of this variable, or be
- * NULL.
- */
- if (rval == var_Error || rval == varNoError) {
- /*
- * If performing old-time variable
- * substitution, skip over the
- * variable and continue with the
- * substitution. Otherwise, store the
- * dollar sign and advance str so we
- * continue with the string...
- */
- if (oldVars) {
- str = subvp.ptr;
- } else if (err) {
- /*
- * If variable is undefined,
- * complain and skip the
- * variable. The complaint
- * will stop us from doing
- * anything when the file is
- * parsed.
- */
- if (!errorReported) {
- Parse_Error(PARSE_FATAL,
- "Undefined variable \"%.*s\"", subvp.ptr - subvp.input, str);
- }
- errorReported = TRUE;
- str = subvp.ptr;
- } else {
- Buf_AddByte(buf, (Byte)str[0]);
- str++;
- }
- } else {
- /*
- * Copy all the characters from the
- * variable value straight into the
- * new string.
- */
- Buf_Append(buf, rval);
- if (rfree) {
- free(rval);
- }
- str = subvp.ptr;
- }
- }
- } else {
- Buf_AddByte(buf, (Byte)str[0]);
- str++;
- }
- }
-
- return (buf);
-}
-
-/**
- * Initialize the module
- *
- * Side Effects:
- * The VAR_CMD and VAR_GLOBAL contexts are created
- */
-void
-Var_Init(char **env)
-{
- char **ptr;
-
- VAR_CMD = Targ_NewGN("Command");
- VAR_ENV = Targ_NewGN("Environment");
- VAR_GLOBAL = Targ_NewGN("Global");
-
- /*
- * Copy user environment variables into ENV context.
- */
- for (ptr = env; *ptr != NULL; ++ptr) {
- char *tmp = estrdup(*ptr);
- const char *name = tmp;
- char *sep = strchr(name, '=');
- const char *value = sep + 1;
-
- if (sep != NULL) {
- *sep = '\0';
- VarAdd(name, value, VAR_ENV);
- }
- free(tmp);
- }
-}
-
-/**
- * Print all variables in global and command line contexts.
- */
-void
-Var_Dump(void)
-{
- const LstNode *ln;
- const Var *v;
-
- printf("#*** Global Variables:\n");
- LST_FOREACH(ln, &VAR_GLOBAL->context) {
- v = Lst_Datum(ln);
- printf("%-16s = %s\n", v->name, Buf_Data(v->val));
- }
-
- printf("#*** Command-line Variables:\n");
- LST_FOREACH(ln, &VAR_CMD->context) {
- v = Lst_Datum(ln);
- printf("%-16s = %s\n", v->name, Buf_Data(v->val));
- }
-}
-
-/**
- * Print the values of any variables requested by
- * the user.
- */
-void
-Var_Print(Lst *vlist, Boolean expandVars)
-{
- LstNode *n;
- char *name;
-
- LST_FOREACH(n, vlist) {
- name = Lst_Datum(n);
- if (expandVars) {
- char *value;
- char *v;
-
- if (*name == '$') {
- v = name;
- } else {
- v = emalloc(strlen(name) + 1 + 3);
- sprintf(v, "${%s}", name);
- }
- value = Buf_Peel(Var_Subst(v, VAR_GLOBAL, FALSE));
- printf("%s\n", value);
-
- if (v != name)
- free(v);
- free(value);
- } else {
- const char *value = Var_Value(name, VAR_GLOBAL);
- printf("%s\n", value != NULL ? value : "");
- }
- }
-}
-
diff --git a/usr.bin/make/var.h b/usr.bin/make/var.h
deleted file mode 100644
index 618cf7a..0000000
--- a/usr.bin/make/var.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*-
- * Copyright (c) 2002 Juli Mallett.
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- *
- * $FreeBSD$
- */
-
-#ifndef var_h_9cccafce
-#define var_h_9cccafce
-
-struct Buffer;
-struct GNode;
-struct List;
-
-/* Variables defined in a global context, e.g in the Makefile itself */
-extern struct GNode *VAR_GLOBAL;
-
-/* Variables defined on the command line */
-extern struct GNode *VAR_CMD;
-
-/*
- * Value returned by Var_Parse when an error is encountered. It actually
- * points to an empty string, so naive callers needn't worry about it.
- */
-extern char var_Error[];
-
-/*
- * TRUE if environment should be searched for all variables before
- * the global context
- */
-extern Boolean checkEnvFirst;
-
-/* Do old-style variable substitution */
-extern Boolean oldVars;
-
-void Var_Append(const char *, const char *, struct GNode *);
-void Var_Delete(const char *, struct GNode *);
-void Var_Dump(void);
-Boolean Var_Exists(const char *, struct GNode *);
-void Var_Init(char **);
-size_t Var_Match(const char [], struct GNode *);
-char *Var_Parse(const char *, struct GNode *, Boolean, size_t *, Boolean *);
-void Var_Print(struct Lst *, Boolean);
-void Var_Set(const char *, const char *, struct GNode *);
-void Var_SetGlobal(const char *, const char *);
-void Var_SetEnv(const char *, struct GNode *);
-struct Buffer *Var_Subst(const char *, struct GNode *, Boolean);
-struct Buffer *Var_SubstOnly(const char *, const char *, Boolean);
-const char *Var_Value(const char [], struct GNode *);
-
-#endif /* var_h_9cccafce */
OpenPOWER on IntegriCloud