From 1dca369142f58197e6750b062c0280dd708de934 Mon Sep 17 00:00:00 2001 From: millert Date: Tue, 1 Apr 1997 07:28:02 +0000 Subject: [PATCH] Sync with NetBSD (mostly by christos initial substitution/regexp from Der Mouse) - fix the variable substitution code in make [PR/2748] 1. change s/a/b/ so that it substitutes the first occurance of the pattern on each word, not only the first word. 2. add flag '1' to the variable substitution so that the substitutions get performed only once. ***THIS IS AN INCOMPATIBLE CHANGE!*** Unfortunately there was no way to make things consistent without modifying the current behavior. Fortunately none of our Makefiles depended on this. OLD: VAR = aa1 aa2 aa3 aa4 S/a/b/ = ba1 aa2 aa3 aa4 S/a/b/g = bb1 bb2 bb3 bb4 NEW: VAR = aa1 aa2 aa3 aa4 S/a/b/ = ba1 ba2 ba3 ba4 S/a/b/1 = ba1 aa2 aa3 aa4 S/a/b/g = bb1 bb2 bb3 bb4 S/a/b/1g = bb1 aa2 aa3 aa4 - add regexp variable substitution via 'C/foo/bar/' [PR/2752] - add variable quoting via the ${VAR:Q} modifier. This is useful when running recursive invocations of make(1): make VAR=${VAR:Q} will always work... (This may prove useful in the kernel builds...) [PR/2981] - BSD did not traditionally have ; use BSD4_4 instead and include to grab it. - Don't compile the regex code if MAKE_BOOTSTRAP (from gwr) - Use explicit .c.o rule in Makefile.boot so that the bootstrap process works. - Use only integral types in procedure arguments. [buf.c buf.h] - Include to get getenv() prototype on SVR4 - if __STDC__ -> ifdef __STDC__ to appease SVR4 - Define const and volatile for non __STDC__ - Implement snprintf() and vsnprintf() for non BSD4_4 systems. - Make $MACHINE_ARCH settable from the environment. - Fix .USE directive problems: (reported by cgd) 1. ${.*} variables did not get expanded in dependencies. 2. expanded ${.*} variables in .USE dependencies can cause tree restructuring; handle it. 3. in compat mode, expand .USE before evaluating the list of targets, instead of doing .USE expansions on demand, because they can cause tree restructuring. - Add a .MADE directive to indicated that the children of a target are up-to-date, even when they are not. This is to simulate our current make install behavior with proper dependencies. - Fix problems in the RE substitution error handling. - Locate all the children of a node marked as MADE. - Do not compile-in ${MACHINE} (as per NetBSD PR#3386) - Disable globbing for targets/dependencies when POSIX is defined. - Fix globbing so that patterns that don't have a matching number of [] or {} don't get expanded. (before the [ case got expanded to nothing!) This is disabled. - Make sure that the children of nodes that are marked .MADE, are marked UPTODATE and their timestamps are consistent. - Don't disable wildcards completely; they are used by other Makefiles. --- usr.bin/make/Makefile.boot | 8 +- usr.bin/make/buf.c | 8 +- usr.bin/make/buf.h | 6 +- usr.bin/make/compat.c | 22 +- usr.bin/make/dir.c | 31 +- usr.bin/make/main.c | 47 +-- usr.bin/make/make.1 | 70 ++++- usr.bin/make/make.c | 149 +++++++--- usr.bin/make/make.h | 35 ++- usr.bin/make/parse.c | 21 +- usr.bin/make/targ.c | 9 +- usr.bin/make/util.c | 74 ++++- usr.bin/make/var.c | 570 +++++++++++++++++++++++++++---------- 13 files changed, 788 insertions(+), 262 deletions(-) diff --git a/usr.bin/make/Makefile.boot b/usr.bin/make/Makefile.boot index 02bb006d454..c51fb1e01e2 100644 --- a/usr.bin/make/Makefile.boot +++ b/usr.bin/make/Makefile.boot @@ -1,5 +1,5 @@ -# $OpenBSD: Makefile.boot,v 1.3 1996/11/30 21:08:48 millert Exp $ -# $NetBSD: Makefile.boot,v 1.7 1996/08/30 17:59:37 thorpej Exp $ +# $OpenBSD: Makefile.boot,v 1.4 1997/04/01 07:28:02 millert Exp $ +# $NetBSD: Makefile.boot,v 1.8 1996/12/31 17:52:23 christos Exp $ # # a very simple makefile... # @@ -7,6 +7,10 @@ # # modify MACHINE and MACHINE_ARCH as appropriate for your target architecture # + +.c.o: + ${CC} ${CFLAGS} -c $< -o $@ + MACHINE=sun MACHINE_ARCH=sparc CFLAGS= -I. -DMACHINE=\"${MACHINE}\" -DMACHINE_ARCH=\"${MACHINE_ARCH}\" \ diff --git a/usr.bin/make/buf.c b/usr.bin/make/buf.c index 7d95b9d86d3..c9feb117f37 100644 --- a/usr.bin/make/buf.c +++ b/usr.bin/make/buf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: buf.c,v 1.4 1996/11/30 21:08:50 millert Exp $ */ -/* $NetBSD: buf.c,v 1.8 1996/11/06 17:59:00 christos Exp $ */ +/* $OpenBSD: buf.c,v 1.5 1997/04/01 07:28:05 millert Exp $ */ +/* $NetBSD: buf.c,v 1.9 1996/12/31 17:53:21 christos Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -43,7 +43,7 @@ #if 0 static char sccsid[] = "@(#)buf.c 8.1 (Berkeley) 6/6/93"; #else -static char rcsid[] = "$OpenBSD: buf.c,v 1.4 1996/11/30 21:08:50 millert Exp $"; +static char rcsid[] = "$OpenBSD: buf.c,v 1.5 1997/04/01 07:28:05 millert Exp $"; #endif #endif /* not lint */ @@ -459,7 +459,7 @@ Buf_Destroy (buf, freeData) void Buf_ReplaceLastByte (buf, byte) Buffer buf; /* buffer to augment */ - Byte byte; /* byte to be written */ + int byte; /* byte to be written */ { if (buf->inPtr == buf->outPtr) Buf_AddByte(buf, byte); diff --git a/usr.bin/make/buf.h b/usr.bin/make/buf.h index 52899a37a2c..bc753c78549 100644 --- a/usr.bin/make/buf.h +++ b/usr.bin/make/buf.h @@ -1,5 +1,5 @@ -/* $OpenBSD: buf.h,v 1.3 1996/11/30 21:08:51 millert Exp $ */ -/* $NetBSD: buf.h,v 1.6 1996/11/06 17:59:00 christos Exp $ */ +/* $OpenBSD: buf.h,v 1.4 1997/04/01 07:28:07 millert Exp $ */ +/* $NetBSD: buf.h,v 1.7 1996/12/31 17:53:22 christos Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -79,6 +79,6 @@ void Buf_Discard __P((Buffer, int)); int Buf_Size __P((Buffer)); Buffer Buf_Init __P((int)); void Buf_Destroy __P((Buffer, Boolean)); -void Buf_ReplaceLastByte __P((Buffer, Byte)); +void Buf_ReplaceLastByte __P((Buffer, int)); #endif /* _BUF_H */ diff --git a/usr.bin/make/compat.c b/usr.bin/make/compat.c index 8cffa92288c..a7e63cc19ce 100644 --- a/usr.bin/make/compat.c +++ b/usr.bin/make/compat.c @@ -1,5 +1,5 @@ -/* $OpenBSD: compat.c,v 1.4 1996/11/30 21:08:51 millert Exp $ */ -/* $NetBSD: compat.c,v 1.14 1996/11/06 17:59:01 christos Exp $ */ +/* $OpenBSD: compat.c,v 1.5 1997/04/01 07:28:09 millert Exp $ */ +/* $NetBSD: compat.c,v 1.18 1997/03/28 22:31:22 christos Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -43,7 +43,7 @@ #if 0 static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94"; #else -static char rcsid[] = "$OpenBSD: compat.c,v 1.4 1996/11/30 21:08:51 millert Exp $"; +static char rcsid[] = "$OpenBSD: compat.c,v 1.5 1997/04/01 07:28:09 millert Exp $"; #endif #endif /* not lint */ @@ -366,9 +366,13 @@ CompatMake (gnp, pgnp) { GNode *gn = (GNode *) gnp; GNode *pgn = (GNode *) pgnp; - if (gn->type & OP_USE) { - Make_HandleUse(gn, pgn); - } else if (gn->made == UNMADE) { + + if (pgn->type & OP_MADE) { + (void) Dir_MTime(gn); + gn->made = UPTODATE; + } + + 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 @@ -627,6 +631,12 @@ Compat_Run(targs) } } + /* + * Expand .USE nodes right now, because they can modify the structure + * of the tree. + */ + Lst_Destroy(Make_ExpandUse(targs), NOFREE); + /* * For each entry in the list of targets to create, call CompatMake on * it to create the thing. CompatMake will leave the 'made' field of gn diff --git a/usr.bin/make/dir.c b/usr.bin/make/dir.c index 79622bcedf1..185c3207025 100644 --- a/usr.bin/make/dir.c +++ b/usr.bin/make/dir.c @@ -1,5 +1,5 @@ -/* $OpenBSD: dir.c,v 1.6 1996/11/30 21:08:53 millert Exp $ */ -/* $NetBSD: dir.c,v 1.12 1996/11/06 17:59:04 christos Exp $ */ +/* $OpenBSD: dir.c,v 1.7 1997/04/01 07:28:11 millert Exp $ */ +/* $NetBSD: dir.c,v 1.14 1997/03/29 16:51:26 christos Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -43,7 +43,7 @@ #if 0 static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94"; #else -static char rcsid[] = "$OpenBSD: dir.c,v 1.6 1996/11/30 21:08:53 millert Exp $"; +static char rcsid[] = "$OpenBSD: dir.c,v 1.7 1997/04/01 07:28:11 millert Exp $"; #endif #endif /* not lint */ @@ -285,6 +285,11 @@ DirFindName (p, dname) *----------------------------------------------------------------------- * Dir_HasWildcards -- * see if the given name has any wildcard characters in it + * be careful not to expand unmatching brackets or braces. + * XXX: This code is not 100% correct. ([^]] fails etc.) + * I really don't think that make(1) should be expanding + * patterns, because then you have to set a mechanism for + * escaping the expansion! * * Results: * returns TRUE if the word should be expanded, FALSE otherwise @@ -298,17 +303,33 @@ Dir_HasWildcards (name) char *name; /* name to check */ { register 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 '*': - return (TRUE); + wild = 1; + break; + default: + break; } } - return (FALSE); + return (wild && bracket == 0 && brace == 0); } /*- diff --git a/usr.bin/make/main.c b/usr.bin/make/main.c index 5adbb274c19..a57654ef4dc 100644 --- a/usr.bin/make/main.c +++ b/usr.bin/make/main.c @@ -1,5 +1,5 @@ -/* $OpenBSD: main.c,v 1.11 1997/01/27 05:24:09 briggs Exp $ */ -/* $NetBSD: main.c,v 1.31 1996/11/06 17:59:12 christos Exp $ */ +/* $OpenBSD: main.c,v 1.12 1997/04/01 07:28:13 millert Exp $ */ +/* $NetBSD: main.c,v 1.34 1997/03/24 20:56:36 gwr Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -49,7 +49,7 @@ static char copyright[] = #if 0 static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 3/19/94"; #else -static char rcsid[] = "$OpenBSD: main.c,v 1.11 1997/01/27 05:24:09 briggs Exp $"; +static char rcsid[] = "$OpenBSD: main.c,v 1.12 1997/04/01 07:28:13 millert Exp $"; #endif #endif /* not lint */ @@ -86,14 +86,15 @@ static char rcsid[] = "$OpenBSD: main.c,v 1.11 1997/01/27 05:24:09 briggs Exp $" #include #include #include -#ifndef MACHINE +#ifndef MAKE_BOOTSTRAP #include #endif #include #include #include #include -#if __STDC__ +#include +#ifdef __STDC__ #include #else #include @@ -444,6 +445,7 @@ main(argc, argv) char obpath[MAXPATHLEN + 1]; char cdpath[MAXPATHLEN + 1]; char *machine = getenv("MACHINE"); + char *machine_arch = getenv("MACHINE_ARCH"); Lst sysMkPath; /* Path of sys.mk */ char *cp = NULL, *start; /* avoid faults on read-only strings */ @@ -490,12 +492,11 @@ main(argc, argv) * so we can share an executable for similar machines. * (i.e. m68k: amiga hp300, mac68k, sun3, ...) * - * Note that both MACHINE and MACHINE_ARCH can be overridden - * by environment variables. MACHINE through the getenv() - * above and MACHINE_ARCH, below. + * Note that both MACHINE and MACHINE_ARCH are decided at + * run-time. */ - if (!machine) { -#ifndef MACHINE + if (!machine) { +#ifndef MAKE_BOOTSTRAP struct utsname utsname; if (uname(&utsname) == -1) { @@ -508,6 +509,14 @@ main(argc, argv) #endif } + if (!machine_arch) { +#ifndef MACHINE_ARCH + machine_arch = "unknown"; /* XXX: no uname -p yet */ +#else + machine_arch = MACHINE_ARCH; +#endif + } + /* * If the MAKEOBJDIR (or by default, the _PATH_OBJDIR) directory * exists, change into it and build there. (If a .${MACHINE} suffix @@ -599,11 +608,7 @@ main(argc, argv) Var_Set(MAKEFLAGS, "", VAR_GLOBAL); Var_Set("MFLAGS", "", VAR_GLOBAL); Var_Set("MACHINE", machine, VAR_GLOBAL); -#ifdef MACHINE_ARCH - if (NULL == getenv("MACHINE_ARCH")) { - Var_Set("MACHINE_ARCH", MACHINE_ARCH, VAR_GLOBAL); - } -#endif + Var_Set("MACHINE_ARCH", machine_arch, VAR_GLOBAL); /* * First snag any flags out of the MAKE environment variable. @@ -1021,7 +1026,7 @@ bad: */ /* VARARGS */ void -#if __STDC__ +#ifdef __STDC__ Error(char *fmt, ...) #else Error(va_alist) @@ -1029,7 +1034,7 @@ Error(va_alist) #endif { va_list ap; -#if __STDC__ +#ifdef __STDC__ va_start(ap, fmt); #else char *fmt; @@ -1056,7 +1061,7 @@ Error(va_alist) */ /* VARARGS */ void -#if __STDC__ +#ifdef __STDC__ Fatal(char *fmt, ...) #else Fatal(va_alist) @@ -1064,7 +1069,7 @@ Fatal(va_alist) #endif { va_list ap; -#if __STDC__ +#ifdef __STDC__ va_start(ap, fmt); #else char *fmt; @@ -1098,7 +1103,7 @@ Fatal(va_alist) */ /* VARARGS */ void -#if __STDC__ +#ifdef __STDC__ Punt(char *fmt, ...) #else Punt(va_alist) @@ -1106,7 +1111,7 @@ Punt(va_alist) #endif { va_list ap; -#if __STDC__ +#ifdef __STDC__ va_start(ap, fmt); #else char *fmt; diff --git a/usr.bin/make/make.1 b/usr.bin/make/make.1 index 45975a87564..d514dcf3b7a 100644 --- a/usr.bin/make/make.1 +++ b/usr.bin/make/make.1 @@ -1,5 +1,5 @@ -.\" $OpenBSD: make.1,v 1.9 1996/11/30 21:08:59 millert Exp $ -.\" $NetBSD: make.1,v 1.16 1996/11/06 17:59:13 christos Exp $ +.\" $OpenBSD: make.1,v 1.10 1997/04/01 07:28:15 millert Exp $ +.\" $NetBSD: make.1,v 1.18 1997/03/10 21:19:53 christos Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -506,30 +506,38 @@ This is identical to .Ql Cm M , but selects all words which do not match the rest of the modifier. +.It Cm Q +Quotes every shell meta-character in the variable, so that it can be passed +safely through recursive invocations of +.Nm . .It Cm R Replaces each word in the variable with everything but its suffix. .Sm off -.It Cm S No \&/ Ar old_pattern Xo -.No \&/ Ar new_pattern -.No \&/ Op Cm g +.It Cm S No \&/ Ar old_string Xo +.No \&/ Ar new_string +.No \&/ Op Cm 1g .Xc .Sm on Modify the first occurrence of -.Ar old_pattern -in each word to be replaced with -.Ar new_pattern . +.Ar old_string +in the variable's value, replacing it with +.Ar new_string . If a .Ql g is appended to the last slash of the pattern, all occurrences in each word are replaced. +If a +.Ql 1 +is appended to the last slash of the pattern, only the first word +is affected. If -.Ar old_pattern -begins with a carat +.Ar old_string +begins with a caret .Pq Ql ^ , -.Ar old_pattern +.Ar old_string is anchored at the beginning of each word. If -.Ar old_pattern +.Ar old_string ends with a dollar sign .Pq Ql \&$ , it is anchored at the end of each word. @@ -538,7 +546,11 @@ Inside an ampersand .Pq Ql & is replaced by -.Ar old_pattern . +.Ar old_string +(without any +.Ql ^ +or +.Ql \&$ ) . Any character may be used as a delimiter for the parts of the modifier string. The anchoring, ampersand and delimiter characters may be escaped with a @@ -551,8 +563,36 @@ and .Ar new_string with the single exception that a backslash is used to prevent the expansion of a dollar sign -.Pq Ql \&$ +.Pq Ql \&$ , not a preceding dollar sign as is usual. +.Sm off +.It Cm C No \&/ Ar pattern Xo +.No \&/ Ar replacement +.No \&/ Op Cm 1g +.Xc +.Sm on +The +.Cm C +modifier is just like the +.Cm S +modifier except that the the old and new strings, instead of being +simple strings, are a regular expression (see +.Xr regex 3 ) +and an +.Xr ed 1 Ns \-style +replacement string. Normally, the first occurrence of the pattern in +each word of the value is changed. The +.Ql 1 +modifier causes the substitution to apply to at most one word; the +.Ql g +modifier causes the substitution to apply to as many instances of the +search pattern as occur in the word or words it is found in. Note that +.Ql 1 +and +.Ql g +are orthogonal; the former specifies whether multiple words are +potentially affected, the latter whether multiple substitutions can +potentially occur within each affected word. .It Cm T Replaces each word in the variable with its last component. .It Ar old_string=new_string @@ -814,6 +854,8 @@ command line, and continue to the end of the line. Ignore any errors from the commands associated with this target, exactly as if they all were preceded by a dash .Pq Ql \- . +.It Ic .MADE +Mark all sources of this target as being up-to-date. .It Ic .MAKE Execute the commands associated with this target even if the .Fl n diff --git a/usr.bin/make/make.c b/usr.bin/make/make.c index 11887103adc..f84b75d756f 100644 --- a/usr.bin/make/make.c +++ b/usr.bin/make/make.c @@ -1,5 +1,5 @@ -/* $OpenBSD: make.c,v 1.4 1996/11/30 21:09:00 millert Exp $ */ -/* $NetBSD: make.c,v 1.10 1996/11/06 17:59:15 christos Exp $ */ +/* $OpenBSD: make.c,v 1.5 1997/04/01 07:28:17 millert Exp $ */ +/* $NetBSD: make.c,v 1.14 1997/03/28 22:31:21 christos Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -43,7 +43,7 @@ #if 0 static char sccsid[] = "@(#)make.c 8.1 (Berkeley) 6/6/93"; #else -static char rcsid[] = "$OpenBSD: make.c,v 1.4 1996/11/30 21:09:00 millert Exp $"; +static char rcsid[] = "$OpenBSD: make.c,v 1.5 1997/04/01 07:28:17 millert Exp $"; #endif #endif /* not lint */ @@ -75,8 +75,11 @@ static char rcsid[] = "$OpenBSD: make.c,v 1.4 1996/11/30 21:09:00 millert Exp $" * * Make_OODate Determine if a target is out-of-date. * - * Make_HandleUse See if a child is a .USE node for a parent + * Make_HandleUse See if a child is a .USE node for a parent * and perform the .USE actions if so. + * + * Make_ExpandUse Expand .USE nodes and return the new list of + * targets. */ #include "make.h" @@ -94,6 +97,7 @@ static int numNodes; /* Number of nodes to be processed. If this * TRUE, there's a cycle in the graph */ static int MakeAddChild __P((ClientData, ClientData)); +static int MakeFindChild __P((ClientData, ClientData)); static int MakeAddAllSrc __P((ClientData, ClientData)); static int MakeTimeStamp __P((ClientData, ClientData)); static int MakeHandleUse __P((ClientData, ClientData)); @@ -129,7 +133,7 @@ MakeTimeStamp (pgn, cgn) ClientData pgn; /* the current parent */ ClientData cgn; /* the child we've just examined */ { - return Make_TimeStamp((GNode *) pgn, (GNode *) cgn); + return (Make_TimeStamp((GNode *) pgn, (GNode *) cgn)); } /*- @@ -296,12 +300,43 @@ MakeAddChild (gnp, lp) { GNode *gn = (GNode *) gnp; Lst l = (Lst) lp; + if (!gn->make && !(gn->type & OP_USE)) { (void)Lst_EnQueue (l, (ClientData)gn); } return (0); } + +/*- + *----------------------------------------------------------------------- + * MakeFindChild -- + * Function used by Make_Run to find the pathname of a child + * that was already made. + * + * Results: + * Always returns 0 + * + * Side Effects: + * The path and mtime of the node and the cmtime of the parent are + * updated + *----------------------------------------------------------------------- + */ +static int +MakeFindChild (gnp, pgnp) + ClientData gnp; /* the node to find */ + ClientData pgnp; +{ + GNode *gn = (GNode *) gnp; + GNode *pgn = (GNode *) pgnp; + + (void) Dir_MTime(gn); + if (pgn->cmtime < gn->mtime) + pgn->cmtime = gn->mtime; + gn->made = UPTODATE; + return (0); +} + /*- *----------------------------------------------------------------------- * Make_HandleUse -- @@ -330,7 +365,6 @@ Make_HandleUse (cgn, pgn) register GNode *cgn; /* The .USE node */ register GNode *pgn; /* The target of the .USE node */ { - register GNode *gn; /* A child of the .USE node */ register LstNode ln; /* An element in the children list */ if (cgn->type & (OP_USE|OP_TRANSFORM)) { @@ -344,7 +378,27 @@ Make_HandleUse (cgn, pgn) if (Lst_Open (cgn->children) == SUCCESS) { while ((ln = Lst_Next (cgn->children)) != NILLNODE) { - gn = (GNode *)Lst_Datum (ln); + register GNode *tgn, *gn = (GNode *)Lst_Datum (ln); + + /* + * Expand variables in the .USE node's name + * and save the unexpanded form. + * We don't need to do this for commands. + * They get expanded properly when we execute. + */ + if (gn->uname == NULL) { + gn->uname = gn->name; + } else { + if (gn->name) + free(gn->name); + } + gn->name = Var_Subst(NULL, gn->uname, pgn, FALSE); + if (gn->name && gn->uname && strcmp(gn->name, gn->uname) != 0) { + /* See if we have a target for this node. */ + tgn = Targ_FindNode(gn->name, TARG_NOCREATE); + if (tgn != NILGNODE) + gn = tgn; + } if (Lst_Member (pgn->children, gn) == NILLNODE) { (void) Lst_AtEnd (pgn->children, gn); @@ -364,8 +418,10 @@ Make_HandleUse (cgn, pgn) * 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 -= 1; + if ((cgn->type & OP_USE) && + (ln = Lst_Member (pgn->children, (ClientData) cgn)) != NILLNODE) { + Lst_Remove(pgn->children, ln); + pgn->unmade--; } } return (0); @@ -375,7 +431,7 @@ MakeHandleUse (pgn, cgn) ClientData pgn; /* the current parent */ ClientData cgn; /* the child we've just examined */ { - return Make_HandleUse((GNode *) pgn, (GNode *) cgn); + return (Make_HandleUse((GNode *) pgn, (GNode *) cgn)); } /*- @@ -581,14 +637,14 @@ MakeAddAllSrc (cgnp, pgnp) char *child; char *p1 = NULL; - if (OP_NOP(cgn->type)) { + if (OP_NOP(cgn->type) || + (child = Var_Value(TARGET, cgn, &p1)) == NULL) { /* * this node is only source; use the specific pathname for it */ child = cgn->path ? cgn->path : cgn->name; } - else - child = Var_Value(TARGET, cgn, &p1); + Var_Append (ALLSRC, child, pgn); if (pgn->type & OP_JOIN) { if (cgn->made == MADE) { @@ -804,36 +860,27 @@ MakePrintStatus(gnp, cyclep) return (0); } + /*- *----------------------------------------------------------------------- - * 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. - * + * Make_ExpandUse -- + * Expand .USE nodes and create a new targets list * Results: - * TRUE if work was done. FALSE otherwise. + * The new list of targets. * * 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. + * numNodes is set to the number of elements in the list of targets. *----------------------------------------------------------------------- */ -Boolean -Make_Run (targs) +Lst +Make_ExpandUse (targs) Lst targs; /* the initial list of targets */ { register GNode *gn; /* a temporary pointer */ register Lst examine; /* List of targets to examine */ - int errors; /* Number of errors the Job module reports */ + register Lst ntargs; /* List of new targets to be made */ - toBeMade = Lst_Init (FALSE); + ntargs = Lst_Init (FALSE); examine = Lst_Duplicate(targs, NOCOPY); numNodes = 0; @@ -856,19 +903,55 @@ Make_Run (targs) /* * Apply any .USE rules before looking for implicit dependencies * to make sure everything has commands that should... + * Make sure that the TARGET is set, so that we can make + * expansions. */ + Var_Set (TARGET, gn->name, gn); Lst_ForEach (gn->children, MakeHandleUse, (ClientData)gn); Suff_FindDeps (gn); - if (gn->unmade != 0) { + if (gn->unmade != 0 && (gn->type & OP_MADE) == 0) { Lst_ForEach (gn->children, MakeAddChild, (ClientData)examine); } else { - (void)Lst_EnQueue (toBeMade, (ClientData)gn); + (void)Lst_EnQueue (ntargs, (ClientData)gn); + if (gn->type & OP_MADE) + Lst_ForEach (gn->children, MakeFindChild, (ClientData)gn); } } } Lst_Destroy (examine, NOFREE); + return (ntargs); +} + +/*- + *----------------------------------------------------------------------- + * 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 (targs) + Lst targs; /* the initial list of targets */ +{ + int errors; /* Number of errors the Job module reports */ + + toBeMade = Make_ExpandUse (targs); if (queryFlag) { /* diff --git a/usr.bin/make/make.h b/usr.bin/make/make.h index aa21d9e6c8b..ab47ff09fc4 100644 --- a/usr.bin/make/make.h +++ b/usr.bin/make/make.h @@ -1,5 +1,5 @@ -/* $OpenBSD: make.h,v 1.7 1996/11/30 21:09:00 millert Exp $ */ -/* $NetBSD: make.h,v 1.11 1996/11/06 17:59:17 christos Exp $ */ +/* $OpenBSD: make.h,v 1.8 1997/04/01 07:28:19 millert Exp $ */ +/* $NetBSD: make.h,v 1.15 1997/03/10 21:20:00 christos Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -50,20 +50,29 @@ #define _MAKE_H_ #include +#include #include #include #include -#if !defined(MAKE_BOOTSTRAP) && defined(BSD) -#include -#else -#ifndef __P -#if defined(__STDC__) || defined(__cplusplus) -#define __P(protos) protos /* full-blown ANSI C */ + +#if !defined(MAKE_BOOTSTRAP) && defined(BSD4_4) +# include #else -#define __P(protos) () /* traditional C preprocessor */ -#endif -#endif +# ifndef __P +# if defined(__STDC__) || defined(__cplusplus) +# define __P(protos) protos /* full-blown ANSI C */ +# else +# define __P(protos) () /* traditional C preprocessor */ +# endif +# endif +# ifndef const +# define const +# endif +# ifndef volatile +# define volatile +# endif #endif + #if __STDC__ #include #include @@ -103,6 +112,7 @@ */ typedef struct GNode { char *name; /* The target's name */ + char *uname; /* The unexpanded name of a .USE node */ char *path; /* The full pathname of the file */ int type; /* Its type (see the OP flags, below) */ int order; /* Its wait weight */ @@ -193,6 +203,8 @@ typedef struct GNode { * 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_MADE 0x00000800 /* Assume the node is already made; even if + * it really is 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. */ @@ -366,6 +378,7 @@ extern int debug; int Make_TimeStamp __P((GNode *, GNode *)); Boolean Make_OODate __P((GNode *)); +Lst Make_ExpandUse __P((Lst)); int Make_HandleUse __P((GNode *, GNode *)); void Make_Update __P((GNode *)); void Make_DoAllVar __P((GNode *)); diff --git a/usr.bin/make/parse.c b/usr.bin/make/parse.c index 934d27af81e..9c930d6d950 100644 --- a/usr.bin/make/parse.c +++ b/usr.bin/make/parse.c @@ -1,5 +1,5 @@ -/* $OpenBSD: parse.c,v 1.12 1997/03/26 17:46:44 deraadt Exp $ */ -/* $NetBSD: parse.c,v 1.27 1996/11/06 17:59:20 christos Exp $ */ +/* $OpenBSD: parse.c,v 1.13 1997/04/01 07:28:21 millert Exp $ */ +/* $NetBSD: parse.c,v 1.29 1997/03/10 21:20:04 christos Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -43,7 +43,7 @@ #if 0 static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94"; #else -static char rcsid[] = "$OpenBSD: parse.c,v 1.12 1997/03/26 17:46:44 deraadt Exp $"; +static char rcsid[] = "$OpenBSD: parse.c,v 1.13 1997/04/01 07:28:21 millert Exp $"; #endif #endif /* not lint */ @@ -211,6 +211,7 @@ static struct { { ".INVISIBLE", Attribute, OP_INVISIBLE }, { ".JOIN", Attribute, OP_JOIN }, { ".LIBS", Libs, 0 }, +{ ".MADE", Attribute, OP_MADE }, { ".MAIN", Main, 0 }, { ".MAKE", Attribute, OP_MAKE }, { ".MAKEFLAGS", MFlags, 0 }, @@ -466,7 +467,7 @@ ParseDoOp (gnp, opp) * *--------------------------------------------------------------------- */ -int +static int ParseAddDep(pp, sp) ClientData pp; ClientData sp; @@ -2574,18 +2575,18 @@ Parse_End() Lst Parse_MainName() { - Lst listmain; /* result list */ + Lst main; /* result list */ - listmain = Lst_Init (FALSE); + main = Lst_Init (FALSE); if (mainNode == NILGNODE) { Punt ("no target to make."); /*NOTREACHED*/ } else if (mainNode->type & OP_DOUBLEDEP) { - (void) Lst_AtEnd (listmain, (ClientData)mainNode); - Lst_Concat(listmain, mainNode->cohorts, LST_CONCNEW); + (void) Lst_AtEnd (main, (ClientData)mainNode); + Lst_Concat(main, mainNode->cohorts, LST_CONCNEW); } else - (void) Lst_AtEnd (listmain, (ClientData)mainNode); - return (listmain); + (void) Lst_AtEnd (main, (ClientData)mainNode); + return (main); } diff --git a/usr.bin/make/targ.c b/usr.bin/make/targ.c index 48b492350af..80a9293362c 100644 --- a/usr.bin/make/targ.c +++ b/usr.bin/make/targ.c @@ -1,5 +1,5 @@ -/* $OpenBSD: targ.c,v 1.5 1996/11/30 21:09:05 millert Exp $ */ -/* $NetBSD: targ.c,v 1.10 1996/11/06 17:59:27 christos Exp $ */ +/* $OpenBSD: targ.c,v 1.6 1997/04/01 07:28:24 millert Exp $ */ +/* $NetBSD: targ.c,v 1.11 1997/02/20 16:51:50 christos Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -43,7 +43,7 @@ #if 0 static char sccsid[] = "@(#)targ.c 8.2 (Berkeley) 3/19/94"; #else -static char *rcsid = "$OpenBSD: targ.c,v 1.5 1996/11/30 21:09:05 millert Exp $"; +static char *rcsid = "$OpenBSD: targ.c,v 1.6 1997/04/01 07:28:24 millert Exp $"; #endif #endif /* not lint */ @@ -166,6 +166,7 @@ Targ_NewGN (name) gn = (GNode *) emalloc (sizeof (GNode)); gn->name = estrdup (name); + gn->uname = NULL; gn->path = (char *) 0; if (name[0] == '-' && name[1] == 'l') { gn->type = OP_LIB; @@ -215,6 +216,8 @@ TargFreeGN (gnp) free(gn->name); + if (gn->uname) + free(gn->uname); if (gn->path) free(gn->path); diff --git a/usr.bin/make/util.c b/usr.bin/make/util.c index 4032472dcc6..929391b5c23 100644 --- a/usr.bin/make/util.c +++ b/usr.bin/make/util.c @@ -1,16 +1,17 @@ -/* $OpenBSD: util.c,v 1.5 1996/11/30 21:09:06 millert Exp $ */ -/* $NetBSD: util.c,v 1.9 1996/11/11 15:16:10 christos Exp $ */ +/* $OpenBSD: util.c,v 1.6 1997/04/01 07:28:26 millert Exp $ */ +/* $NetBSD: util.c,v 1.10 1996/12/31 17:56:04 christos Exp $ */ /* * Missing stuff from OS's */ #ifndef lint -static char rcsid[] = "$OpenBSD: util.c,v 1.5 1996/11/30 21:09:06 millert Exp $"; +static char rcsid[] = "$OpenBSD: util.c,v 1.6 1997/04/01 07:28:26 millert Exp $"; #endif #include #include "make.h" +#include #if !__STDC__ # ifndef const @@ -351,3 +352,70 @@ signal(s, a)) () } #endif + +#ifndef BSD4_4 +#ifdef __STDC__ +#include +#else +#include +#endif + +#ifdef _IOSTRG +#define STRFLAG (_IOSTRG|_IOWRT) /* no _IOWRT: avoid stdio bug */ +#else +#define STRFLAG (_IOREAD) /* XXX: Assume svr4 stdio */ +#endif + +int +vsnprintf(s, n, fmt, args) + char *s; + size_t n; + const char *fmt; + va_list args; +{ + FILE fakebuf; + + fakebuf._flag = STRFLAG; + /* + * Some os's are char * _ptr, others are unsigned char *_ptr... + * We cast to void * to make everyone happy. + */ + fakebuf._ptr = (void *) s; + fakebuf._cnt = n-1; + fakebuf._file = -1; + _doprnt(fmt, args, &fakebuf); + fakebuf._cnt++; + putc('\0', &fakebuf); + if (fakebuf._cnt<0) + fakebuf._cnt = 0; + return (n-fakebuf._cnt-1); +} + +int +#ifdef __STDC__ +snprintf(char *s, size_t n, const char *fmt, ...) +#else +snprintf(va_alist) + va_dcl +#endif +{ + va_list ap; + int rv; +#ifdef __STDC__ + va_start(ap, fmt); +#else + char *s; + size_t n; + const char *fmt; + + va_start(ap); + + s = va_arg(ap, char *); + n = va_arg(ap, size_t); + fmt = va_arg(ap, const char *); +#endif + rv = vsnprintf(s, n, fmt, ap); + va_end(ap); + return rv; +} +#endif diff --git a/usr.bin/make/var.c b/usr.bin/make/var.c index 642c07c0be2..fdd8051ce6c 100644 --- a/usr.bin/make/var.c +++ b/usr.bin/make/var.c @@ -1,5 +1,5 @@ -/* $OpenBSD: var.c,v 1.5 1996/11/30 21:09:07 millert Exp $ */ -/* $NetBSD: var.c,v 1.15 1996/11/06 17:59:29 christos Exp $ */ +/* $OpenBSD: var.c,v 1.6 1997/04/01 07:28:28 millert Exp $ */ +/* $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -43,7 +43,7 @@ #if 0 static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; #else -static char rcsid[] = "$OpenBSD: var.c,v 1.5 1996/11/30 21:09:07 millert Exp $"; +static char rcsid[] = "$OpenBSD: var.c,v 1.6 1997/04/01 07:28:28 millert Exp $"; #endif #endif /* not lint */ @@ -90,6 +90,10 @@ static char rcsid[] = "$OpenBSD: var.c,v 1.5 1996/11/30 21:09:07 millert Exp $"; */ #include +#ifndef MAKE_BOOTSTRAP +#include +#endif +#include #include "make.h" #include "buf.h" @@ -145,17 +149,31 @@ typedef struct Var { * modified variables */ } Var; +/* Var*Pattern 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 */ + typedef struct { char *lhs; /* String to match */ int leftLen; /* Length of string */ char *rhs; /* Replacement string (w/ &'s removed) */ int rightLen; /* Length of replacement */ int flags; -#define VAR_SUB_GLOBAL 1 /* Apply substitution globally */ -#define VAR_MATCH_START 2 /* Match at start of word */ -#define VAR_MATCH_END 4 /* Match at end of word */ } VarPattern; +#ifndef MAKE_BOOTSTRAP +typedef struct { + regex_t re; + int nsub; + regmatch_t *matches; + char *replace; + int flags; +} VarREPattern; +#endif + static int VarCmp __P((ClientData, ClientData)); static Var *VarFind __P((char *, GNode *, int)); static void VarAdd __P((char *, char *, GNode *)); @@ -169,7 +187,14 @@ static Boolean VarMatch __P((char *, Boolean, Buffer, ClientData)); static Boolean VarSYSVMatch __P((char *, Boolean, Buffer, ClientData)); #endif static Boolean VarNoMatch __P((char *, Boolean, Buffer, ClientData)); +#ifndef MAKE_BOOTSTRAP +static void VarREError __P((int, regex_t *, const char *)); +static Boolean VarRESubstitute __P((char *, Boolean, Buffer, ClientData)); +#endif static Boolean VarSubstitute __P((char *, Boolean, Buffer, ClientData)); +static char *VarGetPattern __P((GNode *, int, char **, int, int *, int *, + VarPattern *)); +static char *VarQuote __P((char *)); static char *VarModify __P((char *, Boolean (*)(char *, Boolean, Buffer, ClientData), ClientData)); @@ -891,9 +916,10 @@ VarSubstitute (word, addSpace, buf, patternp) VarPattern *pattern = (VarPattern *) patternp; wordLen = strlen(word); - if (1) { /* substitute in each word of the variable */ + if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) != + (VAR_SUB_ONE|VAR_SUB_MATCHED)) { /* - * Break substitution down into simple anchored cases + * Still substituting -- break it down into simple anchored cases * and if none of them fits, perform the general substitution case. */ if ((pattern->flags & VAR_MATCH_START) && @@ -916,6 +942,7 @@ VarSubstitute (word, addSpace, buf, patternp) Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); } + pattern->flags |= VAR_SUB_MATCHED; } else if (pattern->flags & VAR_MATCH_END) { /* * Doesn't match to end -- copy word wholesale @@ -934,6 +961,7 @@ VarSubstitute (word, addSpace, buf, patternp) Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); Buf_AddBytes(buf, wordLen - pattern->leftLen, (Byte *)(word + pattern->leftLen)); + pattern->flags |= VAR_SUB_MATCHED; } } else if (pattern->flags & VAR_MATCH_START) { /* @@ -964,6 +992,7 @@ VarSubstitute (word, addSpace, buf, patternp) } Buf_AddBytes(buf, cp - word, (Byte *)word); Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); + pattern->flags |= VAR_SUB_MATCHED; } else { /* * Had to match at end and didn't. Copy entire word. @@ -1001,6 +1030,7 @@ VarSubstitute (word, addSpace, buf, patternp) if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0){ done = TRUE; } + pattern->flags |= VAR_SUB_MATCHED; } else { done = TRUE; } @@ -1018,10 +1048,6 @@ VarSubstitute (word, addSpace, buf, patternp) */ 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: @@ -1032,6 +1058,158 @@ VarSubstitute (word, addSpace, buf, patternp) return(TRUE); } +#ifndef MAKE_BOOTSTRAP +/*- + *----------------------------------------------------------------------- + * VarREError -- + * Print the error caused by a regcomp or regexec call. + * + * Results: + * None. + * + * Side Effects: + * An error gets printed. + * + *----------------------------------------------------------------------- + */ +static void +VarREError(err, pat, str) + 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); +} + +/*- + *----------------------------------------------------------------------- + * VarRESubstitute -- + * Perform a regex substitution on the given word, placing the + * result in the passed buffer. + * + * Results: + * TRUE if a space is needed before more characters are added. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarRESubstitute(word, addSpace, buf, patternp) + char *word; + Boolean addSpace; + Buffer buf; + ClientData patternp; +{ + VarREPattern *pat; + int xrv; + char *wp; + char *rp; + int added; + +#define MAYBE_ADD_SPACE() \ + if (addSpace && !added) \ + Buf_AddByte(buf, ' '); \ + 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, 0); + } + + 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, wp); + } + + for (rp = pat->replace; *rp; rp++) { + if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) { + MAYBE_ADD_SPACE(); + Buf_AddByte(buf,rp[1]); + rp++; + } + else if ((*rp == '&') || ((*rp == '\\') && isdigit(rp[1]))) { + int n; + char *subbuf; + char zsub; + 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, subbuf); + } + } else { + MAYBE_ADD_SPACE(); + Buf_AddByte(buf, *rp); + } + } + wp += pat->matches[0].rm_eo; + if (pat->flags & VAR_SUB_GLOBAL) + goto tryagain; + if (*wp) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf, strlen(wp), wp); + } + break; + default: + VarREError(xrv, &pat->re, "Unexpected regex error"); + /* fall through */ + case REG_NOMATCH: + if (*wp) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf,strlen(wp),wp); + } + break; + } + return(addSpace||added); +} +#endif + /*- *----------------------------------------------------------------------- * VarModify -- @@ -1074,6 +1252,141 @@ VarModify (str, modProc, datum) return (str); } +/*- + *----------------------------------------------------------------------- + * VarGetPattern -- + * 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 ampersants, 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. + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +static char * +VarGetPattern(ctxt, err, tstr, delim, flags, length, pattern) + GNode *ctxt; + int err; + char **tstr; + int delim; + int *flags; + int *length; + VarPattern *pattern; +{ + char *cp; + Buffer buf = Buf_Init(0); + int junk; + if (length == NULL) + length = &junk; + +#define IS_A_MATCH(cp, delim) \ + ((cp[0] == '\\') && ((cp[1] == delim) || \ + (cp[1] == '\\') || (cp[1] == '$') || (pattern && (cp[1] == '&')))) + + /* + * 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. + */ + for (cp = *tstr; *cp && (*cp != delim); cp++) { + if (IS_A_MATCH(cp, delim)) { + Buf_AddByte(buf, (Byte) cp[1]); + cp++; + } else if (*cp == '$') { + if (cp[1] == delim) { + if (flags == NULL) + Buf_AddByte(buf, (Byte) *cp); + else + /* + * Unescaped $ at end of pattern => anchor + * pattern at end. + */ + *flags |= VAR_MATCH_END; + } + else { + char *cp2; + int len; + Boolean freeIt; + + /* + * If unescaped dollar sign not before the + * delimiter, assume it's a variable + * substitution and recurse. + */ + cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); + Buf_AddBytes(buf, strlen(cp2), (Byte *) cp2); + if (freeIt) + free(cp2); + cp += len - 1; + } + } + else if (pattern && *cp == '&') + Buf_AddBytes(buf, pattern->leftLen, (Byte *)pattern->lhs); + else + Buf_AddByte(buf, (Byte) *cp); + } + + Buf_AddByte(buf, (Byte) '\0'); + + if (*cp != delim) { + *tstr = cp; + *length = 0; + return NULL; + } + else { + *tstr = ++cp; + cp = (char *) Buf_GetAll(buf, length); + *length -= 1; /* Don't count the NULL */ + Buf_Destroy(buf, FALSE); + return cp; + } +} + +/*- + *----------------------------------------------------------------------- + * VarQuote -- + * Quote shell meta-characters in the string + * + * Results: + * The quoted string + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarQuote(str) + 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); + } + Buf_AddByte(buf, (Byte) '\0'); + str = (char *)Buf_GetAll (buf, (int *)NULL); + Buf_Destroy (buf, FALSE); + return str; +} + /*- *----------------------------------------------------------------------- * Var_Parse -- @@ -1104,7 +1417,7 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr) { register char *tstr; /* Pointer into str */ Var *v; /* Variable in invocation */ - register char *cp; /* Secondary pointer into str (place marker + char *cp; /* Secondary pointer into str (place marker * for tstr) */ Boolean haveModifier;/* TRUE if have modifiers for the variable */ register char endc; /* Ending character when variable in parens @@ -1114,6 +1427,7 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr) int cnt; /* Used to count brace pairs when variable in * in parens or braces */ char *start; + char delim; Boolean dynamic; /* TRUE if the variable is local and we're * expanding it in a non-local context. This * is done to support dynamic sources. The @@ -1348,6 +1662,8 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr) * 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 @@ -1426,12 +1742,11 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr) case 'S': { VarPattern pattern; - register char delim; - Buffer buf; /* Buffer for patterns */ pattern.flags = 0; delim = tstr[1]; tstr += 2; + /* * If pattern begins with '^', it is anchored to the * start of the word -- skip over it and flag pattern. @@ -1441,159 +1756,112 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr) tstr += 1; } - buf = Buf_Init(0); + cp = tstr; + if ((pattern.lhs = VarGetPattern(ctxt, err, &cp, delim, + &pattern.flags, &pattern.leftLen, NULL)) == NULL) + goto cleanup; + + if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim, + NULL, &pattern.rightLen, &pattern)) == NULL) + goto cleanup; /* - * Pass through the lhs 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). - * The result is left in the Buffer buf. + * Check for global substitution. If 'g' after the final + * delimiter, substitution is global and is marked that + * way. */ - for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { - if ((*cp == '\\') && - ((cp[1] == delim) || - (cp[1] == '$') || - (cp[1] == '\\'))) - { - Buf_AddByte(buf, (Byte)cp[1]); - cp++; - } else if (*cp == '$') { - if (cp[1] != delim) { - /* - * If unescaped dollar sign not before the - * delimiter, assume it's a variable - * substitution and recurse. - */ - char *cp2; - int len; - Boolean freeIt; - - cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); - Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); - if (freeIt) { - free(cp2); - } - cp += len - 1; - } else { - /* - * Unescaped $ at end of pattern => anchor - * pattern at end. - */ - pattern.flags |= VAR_MATCH_END; - } - } else { - Buf_AddByte(buf, (Byte)*cp); + for (;; cp++) { + switch (*cp) { + case 'g': + pattern.flags |= VAR_SUB_GLOBAL; + continue; + case '1': + pattern.flags |= VAR_SUB_ONE; + continue; } + break; } - Buf_AddByte(buf, (Byte)'\0'); + termc = *cp; + newStr = VarModify(str, VarSubstitute, + (ClientData)&pattern); /* - * If lhs didn't end with the delimiter, complain and - * return NULL + * Free the two strings. */ - if (*cp != delim) { - *lengthPtr = cp - start + 1; - if (*freePtr) { - free(str); - } - Buf_Destroy(buf, TRUE); - Error("Unclosed substitution for %s (%c missing)", - v->name, delim); - return (var_Error); - } + free(pattern.lhs); + free(pattern.rhs); + break; + } +#ifndef MAKE_BOOTSTRAP + case 'C': + { + VarREPattern pattern; + char *re; + int error; - /* - * Fetch pattern and destroy buffer, but preserve the data - * in it, since that's our lhs. Note that Buf_GetAll - * will return the actual number of bytes, which includes - * the null byte, so we have to decrement the length by - * one. - */ - pattern.lhs = (char *)Buf_GetAll(buf, &pattern.leftLen); - pattern.leftLen--; - Buf_Destroy(buf, FALSE); + pattern.flags = 0; + delim = tstr[1]; + tstr += 2; - /* - * Now comes the replacement string. Three things need to - * be done here: 1) need to compress escaped delimiters and - * ampersands and 2) need to replace unescaped ampersands - * with the l.h.s. (since this isn't regexp, we can do - * it right here) and 3) expand any variable substitutions. - */ - buf = Buf_Init(0); - - tstr = cp + 1; - for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { - if ((*cp == '\\') && - ((cp[1] == delim) || - (cp[1] == '&') || - (cp[1] == '\\') || - (cp[1] == '$'))) - { - Buf_AddByte(buf, (Byte)cp[1]); - cp++; - } else if ((*cp == '$') && (cp[1] != delim)) { - char *cp2; - int len; - Boolean freeIt; - - cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); - Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); - cp += len - 1; - if (freeIt) { - free(cp2); - } - } else if (*cp == '&') { - Buf_AddBytes(buf, pattern.leftLen, - (Byte *)pattern.lhs); - } else { - Buf_AddByte(buf, (Byte)*cp); - } - } + cp = tstr; - Buf_AddByte(buf, (Byte)'\0'); + if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL, + NULL, NULL)) == NULL) + goto cleanup; - /* - * If didn't end in delimiter character, complain - */ - if (*cp != delim) { - *lengthPtr = cp - start + 1; - if (*freePtr) { - free(str); + if ((pattern.replace = VarGetPattern(ctxt, err, &cp, + delim, NULL, NULL, NULL)) == NULL) { + free(re); + goto cleanup; + } + + for (;; cp++) { + switch (*cp) { + case 'g': + pattern.flags |= VAR_SUB_GLOBAL; + continue; + case '1': + pattern.flags |= VAR_SUB_ONE; + continue; } - Buf_Destroy(buf, TRUE); - Error("Unclosed substitution for %s (%c missing)", - v->name, delim); - return (var_Error); + break; } - pattern.rhs = (char *)Buf_GetAll(buf, &pattern.rightLen); - pattern.rightLen--; - Buf_Destroy(buf, FALSE); + termc = *cp; - /* - * Check for global substitution. If 'g' after the final - * delimiter, substitution is global and is marked that - * way. - */ - cp++; - if (*cp == 'g') { - pattern.flags |= VAR_SUB_GLOBAL; - cp++; + error = regcomp(&pattern.re, re, REG_EXTENDED); + free(re); + if (error) { + *lengthPtr = cp - start + 1; + VarREError(error, &pattern.re, "RE substitution error"); + free(pattern.replace); + return (var_Error); } - termc = *cp; - newStr = VarModify(str, VarSubstitute, - (ClientData)&pattern); - /* - * Free the two strings. - */ - free(pattern.lhs); - free(pattern.rhs); + pattern.nsub = pattern.re.re_nsub + 1; + if (pattern.nsub < 1) + pattern.nsub = 1; + if (pattern.nsub > 10) + pattern.nsub = 10; + pattern.matches = emalloc(pattern.nsub * + sizeof(regmatch_t)); + newStr = VarModify(str, VarRESubstitute, + (ClientData) &pattern); + regfree(&pattern.re); + free(pattern.replace); + free(pattern.matches); break; } +#endif + case 'Q': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarQuote (str); + cp = tstr + 1; + termc = *cp; + break; + } + /*FALLTHRU*/ case 'T': if (tstr[1] == endc || tstr[1] == ':') { newStr = VarModify (str, VarTail, (ClientData)0); @@ -1784,6 +2052,14 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr) } } return (str); + +cleanup: + *lengthPtr = cp - start + 1; + if (*freePtr) + free(str); + Error("Unclosed substitution for %s (%c missing)", + v->name, delim); + return (var_Error); } /*- -- 2.20.1