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

Diff for /src/usr.bin/make/var.c between version 1.5 and 1.6

version 1.5, 1996/11/30 21:09:07 version 1.6, 1997/04/01 07:28:28
Line 1 
Line 1 
 /*      $OpenBSD$       */  /*      $OpenBSD$       */
 /*      $NetBSD: var.c,v 1.15 1996/11/06 17:59:29 christos Exp $        */  /*      $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $        */
   
 /*  /*
  * Copyright (c) 1988, 1989, 1990, 1993   * Copyright (c) 1988, 1989, 1990, 1993
Line 90 
Line 90 
  */   */
   
 #include    <ctype.h>  #include    <ctype.h>
   #ifndef MAKE_BOOTSTRAP
   #include    <regex.h>
   #endif
   #include    <stdlib.h>
 #include    "make.h"  #include    "make.h"
 #include    "buf.h"  #include    "buf.h"
   
Line 145 
Line 149 
                                      * modified variables */                                       * modified variables */
 }  Var;  }  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 {  typedef struct {
     char          *lhs;     /* String to match */      char          *lhs;     /* String to match */
     int           leftLen;  /* Length of string */      int           leftLen;  /* Length of string */
     char          *rhs;     /* Replacement string (w/ &'s removed) */      char          *rhs;     /* Replacement string (w/ &'s removed) */
     int           rightLen; /* Length of replacement */      int           rightLen; /* Length of replacement */
     int           flags;      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;  } 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 int VarCmp __P((ClientData, ClientData));
 static Var *VarFind __P((char *, GNode *, int));  static Var *VarFind __P((char *, GNode *, int));
 static void VarAdd __P((char *, char *, GNode *));  static void VarAdd __P((char *, char *, GNode *));
Line 169 
Line 187 
 static Boolean VarSYSVMatch __P((char *, Boolean, Buffer, ClientData));  static Boolean VarSYSVMatch __P((char *, Boolean, Buffer, ClientData));
 #endif  #endif
 static Boolean VarNoMatch __P((char *, Boolean, Buffer, ClientData));  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 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,  static char *VarModify __P((char *, Boolean (*)(char *, Boolean, Buffer,
                                                 ClientData),                                                  ClientData),
                             ClientData));                              ClientData));
Line 891 
Line 916 
     VarPattern  *pattern = (VarPattern *) patternp;      VarPattern  *pattern = (VarPattern *) patternp;
   
     wordLen = strlen(word);      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.           * and if none of them fits, perform the general substitution case.
          */           */
         if ((pattern->flags & VAR_MATCH_START) &&          if ((pattern->flags & VAR_MATCH_START) &&
Line 916 
Line 942 
                             Buf_AddBytes(buf, pattern->rightLen,                              Buf_AddBytes(buf, pattern->rightLen,
                                          (Byte *)pattern->rhs);                                           (Byte *)pattern->rhs);
                         }                          }
                           pattern->flags |= VAR_SUB_MATCHED;
                 } else if (pattern->flags & VAR_MATCH_END) {                  } else if (pattern->flags & VAR_MATCH_END) {
                     /*                      /*
                      * Doesn't match to end -- copy word wholesale                       * Doesn't match to end -- copy word wholesale
Line 934 
Line 961 
                     Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);                      Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
                     Buf_AddBytes(buf, wordLen - pattern->leftLen,                      Buf_AddBytes(buf, wordLen - pattern->leftLen,
                                  (Byte *)(word + pattern->leftLen));                                   (Byte *)(word + pattern->leftLen));
                       pattern->flags |= VAR_SUB_MATCHED;
                 }                  }
         } else if (pattern->flags & VAR_MATCH_START) {          } else if (pattern->flags & VAR_MATCH_START) {
             /*              /*
Line 964 
Line 992 
                 }                  }
                 Buf_AddBytes(buf, cp - word, (Byte *)word);                  Buf_AddBytes(buf, cp - word, (Byte *)word);
                 Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);                  Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
                   pattern->flags |= VAR_SUB_MATCHED;
             } else {              } else {
                 /*                  /*
                  * Had to match at end and didn't. Copy entire word.                   * Had to match at end and didn't. Copy entire word.
Line 1001 
Line 1030 
                     if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0){                      if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0){
                         done = TRUE;                          done = TRUE;
                     }                      }
                       pattern->flags |= VAR_SUB_MATCHED;
                 } else {                  } else {
                     done = TRUE;                      done = TRUE;
                 }                  }
Line 1018 
Line 1048 
              */               */
             return ((Buf_Size(buf) != origSize) || addSpace);              return ((Buf_Size(buf) != origSize) || addSpace);
         }          }
         /*  
          * Common code for anchored substitutions:  
          * addSpace was set TRUE if characters were added to the buffer.  
          */  
         return (addSpace);          return (addSpace);
     }      }
  nosub:   nosub:
Line 1032 
Line 1058 
     return(TRUE);      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 --   * VarModify --
  *      Modify each of the words of the passed string using the given   *      Modify each of the words of the passed string using the given
  *      function. Used to implement all modifiers.   *      function. Used to implement all modifiers.
Line 1076 
Line 1254 
   
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
    * 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 --   * Var_Parse --
  *      Given the start of a variable invocation, extract the variable   *      Given the start of a variable invocation, extract the variable
  *      name and find its value, then modify it according to the   *      name and find its value, then modify it according to the
Line 1104 
Line 1417 
 {  {
     register char   *tstr;      /* Pointer into str */      register char   *tstr;      /* Pointer into str */
     Var             *v;         /* Variable in invocation */      Var             *v;         /* Variable in invocation */
     register char   *cp;        /* Secondary pointer into str (place marker      char            *cp;        /* Secondary pointer into str (place marker
                                  * for tstr) */                                   * for tstr) */
     Boolean         haveModifier;/* TRUE if have modifiers for the variable */      Boolean         haveModifier;/* TRUE if have modifiers for the variable */
     register char   endc;       /* Ending character when variable in parens      register char   endc;       /* Ending character when variable in parens
Line 1114 
Line 1427 
     int             cnt;        /* Used to count brace pairs when variable in      int             cnt;        /* Used to count brace pairs when variable in
                                  * in parens or braces */                                   * in parens or braces */
     char            *start;      char            *start;
       char             delim;
     Boolean         dynamic;    /* TRUE if the variable is local and we're      Boolean         dynamic;    /* TRUE if the variable is local and we're
                                  * expanding it in a non-local context. This                                   * expanding it in a non-local context. This
                                  * is done to support dynamic sources. The                                   * is done to support dynamic sources. The
Line 1348 
Line 1662 
      *                          wildcarding form.       *                          wildcarding form.
      *            :S<d><pat1><d><pat2><d>[g]       *            :S<d><pat1><d><pat2><d>[g]
      *                          Substitute <pat2> for <pat1> in the value       *                          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       *            :H            Substitute the head of each word
      *            :T            Substitute the tail of each word       *            :T            Substitute the tail of each word
      *            :E            Substitute the extension (minus '.') of       *            :E            Substitute the extension (minus '.') of
Line 1426 
Line 1742 
                 case 'S':                  case 'S':
                 {                  {
                     VarPattern      pattern;                      VarPattern      pattern;
                     register char   delim;  
                     Buffer          buf;        /* Buffer for patterns */  
   
                     pattern.flags = 0;                      pattern.flags = 0;
                     delim = tstr[1];                      delim = tstr[1];
                     tstr += 2;                      tstr += 2;
   
                     /*                      /*
                      * If pattern begins with '^', it is anchored to the                       * If pattern begins with '^', it is anchored to the
                      * start of the word -- skip over it and flag pattern.                       * start of the word -- skip over it and flag pattern.
Line 1441 
Line 1756 
                         tstr += 1;                          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,                       * Check for global substitution. If 'g' after the final
                      * '$'s and backslashes (place the escaped character in                       * delimiter, substitution is global and is marked that
                      * uninterpreted) and 2) unescaped $'s that aren't before                       * way.
                      * the delimiter (expand the variable substitution).  
                      * The result is left in the Buffer buf.  
                      */                       */
                     for (cp = tstr; *cp != '\0' && *cp != delim; cp++) {                      for (;; cp++) {
                         if ((*cp == '\\') &&                          switch (*cp) {
                             ((cp[1] == delim) ||                          case 'g':
                              (cp[1] == '$') ||                              pattern.flags |= VAR_SUB_GLOBAL;
                              (cp[1] == '\\')))                              continue;
                         {                          case '1':
                             Buf_AddByte(buf, (Byte)cp[1]);                              pattern.flags |= VAR_SUB_ONE;
                             cp++;                              continue;
                         } 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);  
                         }                          }
                           break;
                     }                      }
   
                     Buf_AddByte(buf, (Byte)'\0');                      termc = *cp;
                       newStr = VarModify(str, VarSubstitute,
                                          (ClientData)&pattern);
   
                     /*                      /*
                      * If lhs didn't end with the delimiter, complain and                       * Free the two strings.
                      * return NULL  
                      */                       */
                     if (*cp != delim) {                      free(pattern.lhs);
                         *lengthPtr = cp - start + 1;                      free(pattern.rhs);
                         if (*freePtr) {                      break;
                             free(str);                  }
                         }  #ifndef MAKE_BOOTSTRAP
                         Buf_Destroy(buf, TRUE);                  case 'C':
                         Error("Unclosed substitution for %s (%c missing)",                  {
                               v->name, delim);                      VarREPattern    pattern;
                         return (var_Error);                      char           *re;
                     }                      int             error;
   
                     /*                      pattern.flags = 0;
                      * Fetch pattern and destroy buffer, but preserve the data                      delim = tstr[1];
                      * in it, since that's our lhs. Note that Buf_GetAll                      tstr += 2;
                      * 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);  
   
                     /*                      cp = tstr;
                      * 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;                      if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL,
                     for (cp = tstr; *cp != '\0' && *cp != delim; cp++) {                          NULL, NULL)) == NULL)
                         if ((*cp == '\\') &&                          goto cleanup;
                             ((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);                      if ((pattern.replace = VarGetPattern(ctxt, err, &cp,
                             Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);                          delim, NULL, NULL, NULL)) == NULL) {
                             cp += len - 1;                          free(re);
                             if (freeIt) {                          goto cleanup;
                                 free(cp2);                      }
                             }  
                         } else if (*cp == '&') {                      for (;; cp++) {
                             Buf_AddBytes(buf, pattern.leftLen,                          switch (*cp) {
                                          (Byte *)pattern.lhs);                          case 'g':
                         } else {                              pattern.flags |= VAR_SUB_GLOBAL;
                             Buf_AddByte(buf, (Byte)*cp);                              continue;
                           case '1':
                               pattern.flags |= VAR_SUB_ONE;
                               continue;
                         }                          }
                           break;
                     }                      }
   
                     Buf_AddByte(buf, (Byte)'\0');                      termc = *cp;
   
                     /*                      error = regcomp(&pattern.re, re, REG_EXTENDED);
                      * If didn't end in delimiter character, complain                      free(re);
                      */                      if (error) {
                     if (*cp != delim) {  
                         *lengthPtr = cp - start + 1;                          *lengthPtr = cp - start + 1;
                         if (*freePtr) {                          VarREError(error, &pattern.re, "RE substitution error");
                             free(str);                          free(pattern.replace);
                         }  
                         Buf_Destroy(buf, TRUE);  
                         Error("Unclosed substitution for %s (%c missing)",  
                               v->name, delim);  
                         return (var_Error);                          return (var_Error);
                     }                      }
   
                     pattern.rhs = (char *)Buf_GetAll(buf, &pattern.rightLen);                      pattern.nsub = pattern.re.re_nsub + 1;
                     pattern.rightLen--;                      if (pattern.nsub < 1)
                     Buf_Destroy(buf, FALSE);                          pattern.nsub = 1;
                       if (pattern.nsub > 10)
                     /*                          pattern.nsub = 10;
                      * Check for global substitution. If 'g' after the final                      pattern.matches = emalloc(pattern.nsub *
                      * delimiter, substitution is global and is marked that                                                sizeof(regmatch_t));
                      * way.                      newStr = VarModify(str, VarRESubstitute,
                      */                                         (ClientData) &pattern);
                     cp++;                      regfree(&pattern.re);
                     if (*cp == 'g') {                      free(pattern.replace);
                         pattern.flags |= VAR_SUB_GLOBAL;                      free(pattern.matches);
                         cp++;  
                     }  
   
                     termc = *cp;  
                     newStr = VarModify(str, VarSubstitute,  
                                        (ClientData)&pattern);  
                     /*  
                      * Free the two strings.  
                      */  
                     free(pattern.lhs);  
                     free(pattern.rhs);  
                     break;                      break;
                 }                  }
   #endif
                   case 'Q':
                       if (tstr[1] == endc || tstr[1] == ':') {
                           newStr = VarQuote (str);
                           cp = tstr + 1;
                           termc = *cp;
                           break;
                       }
                       /*FALLTHRU*/
                 case 'T':                  case 'T':
                     if (tstr[1] == endc || tstr[1] == ':') {                      if (tstr[1] == endc || tstr[1] == ':') {
                         newStr = VarModify (str, VarTail, (ClientData)0);                          newStr = VarModify (str, VarTail, (ClientData)0);
Line 1784 
Line 2052 
         }          }
     }      }
     return (str);      return (str);
   
   cleanup:
       *lengthPtr = cp - start + 1;
       if (*freePtr)
           free(str);
       Error("Unclosed substitution for %s (%c missing)",
             v->name, delim);
       return (var_Error);
 }  }
   
 /*-  /*-

Legend:
Removed from v.1.5  
changed lines
  Added in v.1.6