Index: head/UPDATING =================================================================== --- head/UPDATING +++ head/UPDATING @@ -31,6 +31,10 @@ 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 Index: head/share/mk/src.opts.mk =================================================================== --- head/share/mk/src.opts.mk +++ head/share/mk/src.opts.mk @@ -179,7 +179,6 @@ BSD_GREP \ CLANG_EXTRAS \ EISA \ - FMAKE \ HESIOD \ LLDB \ NAND \ Index: head/usr.bin/Makefile =================================================================== --- head/usr.bin/Makefile +++ head/usr.bin/Makefile @@ -253,10 +253,6 @@ SUBDIR+= finger .endif -.if ${MK_FMAKE} != "no" -SUBDIR+= make -.endif - .if ${MK_FTP} != "no" SUBDIR+= ftp .endif Index: head/usr.bin/make/GNode.h =================================================================== --- head/usr.bin/make/GNode.h +++ head/usr.bin/make/GNode.h @@ -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 */ Index: head/usr.bin/make/Makefile =================================================================== --- head/usr.bin/make/Makefile +++ head/usr.bin/make/Makefile @@ -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 - -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_ -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 ' ; \ - 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 Index: head/usr.bin/make/Makefile.depend =================================================================== --- head/usr.bin/make/Makefile.depend +++ head/usr.bin/make/Makefile.depend @@ -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 - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif Index: head/usr.bin/make/Makefile.dist =================================================================== --- head/usr.bin/make/Makefile.dist +++ head/usr.bin/make/Makefile.dist @@ -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 Index: head/usr.bin/make/arch.h =================================================================== --- head/usr.bin/make/arch.h +++ head/usr.bin/make/arch.h @@ -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 */ Index: head/usr.bin/make/arch.c =================================================================== --- head/usr.bin/make/arch.c +++ head/usr.bin/make/arch.c @@ -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 -__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 format. - * - * Arch_LibOODate Special function to decide if a library node - * is out-of-date. - * - * Arch_Init Initialize this module. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 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, ×); - } -} - -/*- - *----------------------------------------------------------------------- - * 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); -} Index: head/usr.bin/make/buf.h =================================================================== --- head/usr.bin/make/buf.h +++ head/usr.bin/make/buf.h @@ -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 - -#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 */ Index: head/usr.bin/make/buf.c =================================================================== --- head/usr.bin/make/buf.c +++ head/usr.bin/make/buf.c @@ -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 -__FBSDID("$FreeBSD$"); - -/* - * buf.c - * Functions for automatically-expanded buffers. - */ - -#include -#include - -#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'; -} Index: head/usr.bin/make/cond.h =================================================================== --- head/usr.bin/make/cond.h +++ head/usr.bin/make/cond.h @@ -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 */ Index: head/usr.bin/make/cond.c =================================================================== --- head/usr.bin/make/cond.c +++ head/usr.bin/make/cond.c @@ -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 -__FBSDID("$FreeBSD$"); - -/* - * Functions to handle conditionals in a makefile. - * - * Interface: - * Cond_Eval Evaluate the conditional in the passed line. - */ - -#include -#include -#include - -#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 */ -} 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 and .elif 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; -} Index: head/usr.bin/make/config.h =================================================================== --- head/usr.bin/make/config.h +++ head/usr.bin/make/config.h @@ -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 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 */ Index: head/usr.bin/make/dir.h =================================================================== --- head/usr.bin/make/dir.h +++ head/usr.bin/make/dir.h @@ -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 -#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 */ Index: head/usr.bin/make/dir.c =================================================================== --- head/usr.bin/make/dir.c +++ head/usr.bin/make/dir.c @@ -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 -__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 -#include -#include -#include -#include -#include -#include - -#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); -} Index: head/usr.bin/make/for.h =================================================================== --- head/usr.bin/make/for.h +++ head/usr.bin/make/for.h @@ -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 */ Index: head/usr.bin/make/for.c =================================================================== --- head/usr.bin/make/for.c +++ head/usr.bin/make/for.c @@ -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 -__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 -#include -#include - -#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 in - * ... - * .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 in - * 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); -} Index: head/usr.bin/make/globals.h =================================================================== --- head/usr.bin/make/globals.h +++ head/usr.bin/make/globals.h @@ -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 */ Index: head/usr.bin/make/hash.h =================================================================== --- head/usr.bin/make/hash.h +++ head/usr.bin/make/hash.h @@ -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 */ Index: head/usr.bin/make/hash.c =================================================================== --- head/usr.bin/make/hash.c +++ head/usr.bin/make/hash.c @@ -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 -__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 -#include -#include - -#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); -} Index: head/usr.bin/make/hash_tables.h =================================================================== --- head/usr.bin/make/hash_tables.h +++ head/usr.bin/make/hash_tables.h @@ -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 - -int directive_hash(const u_char *, size_t); -int keyword_hash(const u_char *, size_t); - -#endif Index: head/usr.bin/make/hash_tables.c =================================================================== --- head/usr.bin/make/hash_tables.c +++ head/usr.bin/make/hash_tables.c @@ -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 - -#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; -} Index: head/usr.bin/make/job.h =================================================================== --- head/usr.bin/make/job.h +++ head/usr.bin/make/job.h @@ -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 - -#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 */ Index: head/usr.bin/make/job.c =================================================================== --- head/usr.bin/make/job.c +++ head/usr.bin/make/job.c @@ -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 -__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 -#include -#include -#include -#ifdef USE_KQUEUE -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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, ×) < 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; - } - } -} Index: head/usr.bin/make/lst.h =================================================================== --- head/usr.bin/make/lst.h +++ head/usr.bin/make/lst.h @@ -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 */ Index: head/usr.bin/make/lst.c =================================================================== --- head/usr.bin/make/lst.c +++ head/usr.bin/make/lst.c @@ -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 -#include - -#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); -} Index: head/usr.bin/make/main.c =================================================================== --- head/usr.bin/make/main.c +++ head/usr.bin/make/main.c @@ -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 -__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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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. - * ::... - */ - 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); -} Index: head/usr.bin/make/make.h =================================================================== --- head/usr.bin/make/make.h +++ head/usr.bin/make/make.h @@ -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 */ Index: head/usr.bin/make/make.1 =================================================================== --- head/usr.bin/make/make.1 +++ head/usr.bin/make/make.1 @@ -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 -.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 -.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 -.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 . Index: head/usr.bin/make/make.c =================================================================== --- head/usr.bin/make/make.c +++ head/usr.bin/make/make.c @@ -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 -__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); -} Index: head/usr.bin/make/parse.h =================================================================== --- head/usr.bin/make/parse.h +++ head/usr.bin/make/parse.h @@ -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 - -#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 " -__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 -#include -#include -#include -#include -#include - -#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 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); -} Index: head/usr.bin/make/pathnames.h =================================================================== --- head/usr.bin/make/pathnames.h +++ head/usr.bin/make/pathnames.h @@ -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 */ Index: head/usr.bin/make/proc.h =================================================================== --- head/usr.bin/make/proc.h +++ head/usr.bin/make/proc.h @@ -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 */ Index: head/usr.bin/make/proc.c =================================================================== --- head/usr.bin/make/proc.c +++ head/usr.bin/make/proc.c @@ -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 -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include - -#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); -} Index: head/usr.bin/make/shell.h =================================================================== --- head/usr.bin/make/shell.h +++ head/usr.bin/make/shell.h @@ -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 - -#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 */ Index: head/usr.bin/make/shell.c =================================================================== --- head/usr.bin/make/shell.c +++ head/usr.bin/make/shell.c @@ -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 -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include - -#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); -} Index: head/usr.bin/make/str.h =================================================================== --- head/usr.bin/make/str.h +++ head/usr.bin/make/str.h @@ -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 */ Index: head/usr.bin/make/str.c =================================================================== --- head/usr.bin/make/str.c +++ head/usr.bin/make/str.c @@ -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 -__FBSDID("$FreeBSD$"); - -#include -#include - -#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); -} Index: head/usr.bin/make/suff.h =================================================================== --- head/usr.bin/make/suff.h +++ head/usr.bin/make/suff.h @@ -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 */ Index: head/usr.bin/make/suff.c =================================================================== --- head/usr.bin/make/suff.c +++ head/usr.bin/make/suff.c @@ -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 -__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: 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 -#include -#include -#include - -#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"); - } -} Index: head/usr.bin/make/targ.h =================================================================== --- head/usr.bin/make/targ.h +++ head/usr.bin/make/targ.h @@ -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 - -/* - * 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 */ Index: head/usr.bin/make/targ.c =================================================================== --- head/usr.bin/make/targ.c +++ head/usr.bin/make/targ.c @@ -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 -__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 - -#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(); -} Index: head/usr.bin/make/util.h =================================================================== --- head/usr.bin/make/util.h +++ head/usr.bin/make/util.h @@ -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 -#include - -/* - * 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 */ Index: head/usr.bin/make/util.c =================================================================== --- head/usr.bin/make/util.c +++ head/usr.bin/make/util.c @@ -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 -__FBSDID("$FreeBSD$"); - -/*- - * util.c -- - * General utilitarian routines for make(1). - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#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, ")"); -} Index: head/usr.bin/make/var.h =================================================================== --- head/usr.bin/make/var.h +++ head/usr.bin/make/var.h @@ -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 */ Index: head/usr.bin/make/var.c =================================================================== --- head/usr.bin/make/var.c +++ head/usr.bin/make/var.c @@ -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 -__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 -#include -#include -#include -#include - -#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: =) - */ - 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 - * words which match the given . - * is of the standard file - * wildcarding form. - * :N - * words which do not match the given - * is of the standard file - * wildcarding form. - * :S[g] - * Substitute for in the value - * :C[g] - * Substitute for regex 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 : ""); - } - } -} -