Sync with NetBSD (mostly by christos initial substitution/regexp from Der Mouse)
authormillert <millert@openbsd.org>
Tue, 1 Apr 1997 07:28:02 +0000 (07:28 +0000)
committermillert <millert@openbsd.org>
Tue, 1 Apr 1997 07:28:02 +0000 (07:28 +0000)
- 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 <sys/cdefs.h>; use BSD4_4 instead and include
  <sys/param.h> 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 <stdlib.h> 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.

13 files changed:
usr.bin/make/Makefile.boot
usr.bin/make/buf.c
usr.bin/make/buf.h
usr.bin/make/compat.c
usr.bin/make/dir.c
usr.bin/make/main.c
usr.bin/make/make.1
usr.bin/make/make.c
usr.bin/make/make.h
usr.bin/make/parse.c
usr.bin/make/targ.c
usr.bin/make/util.c
usr.bin/make/var.c

index 02bb006..c51fb1e 100644 (file)
@@ -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}\" \
index 7d95b9d..c9feb11 100644 (file)
@@ -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);
index 52899a3..bc753c7 100644 (file)
@@ -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 */
index 8cffa92..a7e63cc 100644 (file)
@@ -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
index 79622bc..185c320 100644 (file)
@@ -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);
 }
 
 /*-
index 5adbb27..a57654e 100644 (file)
@@ -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 <sys/resource.h>
 #include <sys/signal.h>
 #include <sys/stat.h>
-#ifndef MACHINE
+#ifndef MAKE_BOOTSTRAP
 #include <sys/utsname.h>
 #endif
 #include <sys/wait.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
-#if __STDC__
+#include <stdlib.h>
+#ifdef __STDC__
 #include <stdarg.h>
 #else
 #include <varargs.h>
@@ -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;
index 45975a8..d514dcf 100644 (file)
@@ -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
index 1188710..f84b75d 100644 (file)
@@ -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));
 }
 \f
 /*-
@@ -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);
 }
+\f
+/*-
+ *-----------------------------------------------------------------------
+ * 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);
+}
+\f
 /*-
  *-----------------------------------------------------------------------
  * 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));
 }
 \f
 /*-
@@ -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);
 }
 \f
+
 /*-
  *-----------------------------------------------------------------------
- * 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) {
        /*
index aa21d9e..ab47ff0 100644 (file)
@@ -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
 #define _MAKE_H_
 
 #include <sys/types.h>
+#include <sys/param.h>
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
-#if !defined(MAKE_BOOTSTRAP) && defined(BSD)
-#include <sys/cdefs.h>
-#else
-#ifndef __P
-#if defined(__STDC__) || defined(__cplusplus)
-#define        __P(protos)     protos          /* full-blown ANSI C */
+
+#if !defined(MAKE_BOOTSTRAP) && defined(BSD4_4)
+# include <sys/cdefs.h>
 #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 <stdlib.h>
 #include <unistd.h>
  */
 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 *));
index 934d27a..9c930d6 100644 (file)
@@ -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);
 }
index 48b4923..80a9293 100644 (file)
@@ -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);
 
index 4032472..929391b 100644 (file)
@@ -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 <stdio.h>
 #include "make.h"
+#include <sys/param.h>
 
 #if !__STDC__
 # ifndef const
@@ -351,3 +352,70 @@ signal(s, a)) ()
 }
 
 #endif
+
+#ifndef BSD4_4
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#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
index 642c07c..fdd8051 100644 (file)
@@ -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    <ctype.h>
+#ifndef MAKE_BOOTSTRAP
+#include    <regex.h>
+#endif
+#include    <stdlib.h>
 #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<d><pat1><d><pat2><d>[g]
      *                         Substitute <pat2> for <pat1> in the value
+     *           :C<d><pat1><d><pat2><d>[g]
+     *                         Substitute <pat2> for regex <pat1> in the value
      *           :H            Substitute the head of each word
      *           :T            Substitute the tail of each word
      *           :E            Substitute the extension (minus '.') of
@@ -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);
 }
 
 /*-