[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.41 and 1.42

version 1.41, 2000/07/17 23:01:20 version 1.42, 2000/07/17 23:06:32
Line 242 
Line 242 
                                 VarPattern *));                                  VarPattern *));
 static char *VarQuote __P((const char *));  static char *VarQuote __P((const char *));
 static char *VarModify __P((const char *, Boolean (*)(const char *, Boolean, Buffer, void *), void *));  static char *VarModify __P((const char *, Boolean (*)(const char *, Boolean, Buffer, void *), void *));
   char *VarModifiers_Apply __P((char *, SymTable *, Boolean,
           Boolean *, char *, char, size_t *));
 static void VarPrintVar __P((void *));  static void VarPrintVar __P((void *));
 static Boolean VarUppercase __P((const char *, Boolean, Buffer, void *));  static Boolean VarUppercase __P((const char *, Boolean, Buffer, void *));
 static Boolean VarLowercase __P((const char *, Boolean, Buffer, void *));  static Boolean VarLowercase __P((const char *, Boolean, Buffer, void *));
Line 532 
Line 534 
   
     if ((flags & FIND_ENV)) {      if ((flags & FIND_ENV)) {
         char *env;          char *env;
           char *n;
   
         v = getvar(VAR_ENV, name, end, k);          v = getvar(VAR_ENV, name, end, k);
         if (v != NULL)          if (v != NULL)
             return v;              return v;
   
         if ((env = getenv(name)) != NULL)          /* getenv requires a null-terminated name */
           n = interval_dup(name, end);
           env = getenv(n);
           free(n);
           if (env != NULL)
             return VarAdd(name, env, VAR_ENV);              return VarAdd(name, env, VAR_ENV);
     }      }
   
Line 1624 
Line 1631 
     size_t          *lengthPtr; /* OUT: The length of the specification */      size_t          *lengthPtr; /* OUT: The length of the specification */
     Boolean         *freePtr;   /* OUT: TRUE if caller should free result */      Boolean         *freePtr;   /* OUT: TRUE if caller should free result */
 {  {
     register char   *tstr;      /* Pointer into str */      char            *tstr;      /* Pointer into str */
     Var             *v;         /* Variable in invocation */      Var             *v;         /* Variable in invocation */
     char            *cp;        /* Secondary pointer into str (place marker  
                                  * 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      char            endc;       /* Ending character when variable in parens
                                  * or braces */                                   * or braces */
     register char   startc=0;   /* Starting character when variable in parens  
                                  * or braces */  
     int             cnt;        /* Used to count brace pairs when variable in  
                                  * 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 1668 
Line 1668 
                  */                   */
                 switch (str[1]) {                  switch (str[1]) {
                     case '@':                      case '@':
                         return("$(.TARGET)");                          return "$(.TARGET)";
                     case '%':                      case '%':
                         return("$(.ARCHIVE)");                          return "$(.ARCHIVE)";
                     case '*':                      case '*':
                         return("$(.PREFIX)");                          return "$(.PREFIX)";
                     case '!':                      case '!':
                         return("$(.MEMBER)");                          return "$(.MEMBER)";
                 }                  }
             }              }
             /*              /* Error.  */
              * Error              return err ? var_Error : varNoError;
              */  
             return (err ? var_Error : varNoError);  
         } else {          } else {
             haveModifier = FALSE;              haveModifier = FALSE;
             tstr = &str[1];              tstr = &str[1];
             endc = str[1];              endc = str[1];
         }          }
     } else {      } else {
         startc = str[1];          endc = str[1] == '(' ? ')' : '}';
         endc = startc == '(' ? ')' : '}';  
   
         /*          /* Skip to the end character or a colon, whichever comes first.  */
          * Skip to the end character or a colon, whichever comes first.          for (tstr = str + 2; *tstr != '\0' && *tstr != endc && *tstr != ':';)
          */               tstr++;
         for (tstr = str + 2;          if (*tstr == ':')
              *tstr != '\0' && *tstr != endc && *tstr != ':';  
              tstr++)  
         {  
             continue;  
         }  
         if (*tstr == ':') {  
             haveModifier = TRUE;              haveModifier = TRUE;
         } else if (*tstr != '\0') {          else if (*tstr != '\0')
             haveModifier = FALSE;              haveModifier = FALSE;
         } else {          else {
             /*              /*
              * If we never did find the end character, return NULL               * If we never did find the end character, return NULL
              * right now, setting the length to be the distance to               * right now, setting the length to be the distance to
              * the end of the string, since that's what make does.               * the end of the string, since that's what make does.
              */               */
             *lengthPtr = tstr - str;              *lengthPtr = tstr - str;
             return (var_Error);              return var_Error;
         }          }
         *tstr = '\0';  
   
         v = VarFind_interval(str + 2, tstr, ctxt, FIND_ENV | FIND_MINE);          v = VarFind_interval(str + 2, tstr, ctxt, FIND_ENV | FIND_MINE);
         if (v == NULL && ctxt != CTXT_CMD && ctxt != CTXT_GLOBAL &&          if (v == NULL && ctxt != CTXT_CMD && ctxt != CTXT_GLOBAL &&
Line 1723 
Line 1713 
              * Check for bogus D and F forms of local variables since we're               * Check for bogus D and F forms of local variables since we're
              * in a local context and the name is the right length.               * in a local context and the name is the right length.
              */               */
             switch(str[2]) {              switch (str[2]) {
                 case '@':                  case '@':
                 case '%':                  case '%':
                 case '*':                  case '*':
Line 1737 
Line 1727 
                     v = VarFind_interval(str+2, str+3, ctxt, 0);                      v = VarFind_interval(str+2, str+3, ctxt, 0);
   
                     if (v != NULL) {                      if (v != NULL) {
                         /*                          /* No need for nested expansion or anything, as we're
                          * No need for nested expansion or anything, as we're  
                          * the only one who sets these things and we sure don't                           * the only one who sets these things and we sure don't
                          * but nested invocations in them...                           * but nested invocations in them...  */
                          */  
                         val = VarValue(v);                          val = VarValue(v);
   
                         if (str[3] == 'D') {                          if (str[3] == 'D')
                             val = VarModify(val, VarHead, NULL);                              val = Var_GetHead(val);
                         } else {                          else
                             val = VarModify(val, VarTail, NULL);                              val = Var_GetTail(val);
                         }                          /* Resulting string is dynamically allocated, so
                         /*                           * tell caller to free it.  */
                          * Resulting string is dynamically allocated, so  
                          * tell caller to free it.  
                          */  
                         *freePtr = TRUE;                          *freePtr = TRUE;
                         *lengthPtr = tstr-start+1;                          *lengthPtr = tstr-start+1;
                         *tstr = endc;                          *tstr = endc;
                         return(val);                          return val;
                     }                      }
                     break;                      break;
                 }                  }
Line 1765 
Line 1750 
   
         if (v == NULL) {          if (v == NULL) {
             if ((tstr-str == 3 ||              if ((tstr-str == 3 ||
                  ((tstr-str == 4 && (str[3] == 'F' ||                   (tstr-str == 4 && (str[3] == 'F' ||
                                          str[3] == 'D')))) &&                                           str[3] == 'D'))) &&
                 (ctxt == CTXT_CMD || ctxt == CTXT_GLOBAL || ctxt == NULL))                  (ctxt == CTXT_CMD || ctxt == CTXT_GLOBAL || ctxt == NULL))
             {              {
                 /*                  /* If substituting a local variable in a non-local context,
                  * If substituting a local variable in a non-local context,  
                  * assume it's for dynamic source stuff. We have to handle                   * assume it's for dynamic source stuff. We have to handle
                  * this specially and return the longhand for the variable                   * this specially and return the longhand for the variable
                  * with the dollar sign escaped so it makes it back to the                   * with the dollar sign escaped so it makes it back to the
                  * caller. Only four of the local variables are treated                   * caller. Only four of the local variables are treated
                  * specially as they are the only four that will be set                   * specially as they are the only four that will be set
                  * when dynamic sources are expanded.                   * when dynamic sources are expanded.  */
                  */  
                 switch (str[2]) {                  switch (str[2]) {
                     case '@':                      case '@':
                     case '%':                      case '%':
Line 1786 
Line 1769 
                         dynamic = TRUE;                          dynamic = TRUE;
                         break;                          break;
                 }                  }
             } else if (((tstr-str) > 4) && (str[2] == '.') &&              } else if (tstr-str > 4 && str[2] == '.' &&
                        isupper((unsigned char) str[3]) &&                         isupper((unsigned char) str[3]) &&
                        (ctxt == CTXT_CMD || ctxt == CTXT_GLOBAL || ctxt == NULL))                         (ctxt == CTXT_CMD || ctxt == CTXT_GLOBAL || ctxt == NULL))
             {              {
Line 1796 
Line 1779 
                 if ((strncmp(str+2, ".TARGET", len) == 0) ||                  if ((strncmp(str+2, ".TARGET", len) == 0) ||
                     (strncmp(str+2, ".ARCHIVE", len) == 0) ||                      (strncmp(str+2, ".ARCHIVE", len) == 0) ||
                     (strncmp(str+2, ".PREFIX", len) == 0) ||                      (strncmp(str+2, ".PREFIX", len) == 0) ||
                     (strncmp(str+2, ".MEMBER", len) == 0))                      (strncmp(str+2, ".MEMBER", len) == 0)) {
                 {  
                     dynamic = TRUE;                      dynamic = TRUE;
                 }                  }
             }              }
   
             if (!haveModifier) {              if (!haveModifier) {
                 /*                  /* No modifiers -- have specification length so we can return
                  * No modifiers -- have specification length so we can return                   * now.  */
                  * now.  
                  */  
                 *lengthPtr = tstr - start + 1;                  *lengthPtr = tstr - start + 1;
                 *tstr = endc;                  *tstr = endc;
                 if (dynamic) {                  if (dynamic) {
                     str = emalloc(*lengthPtr + 1);                      char *n;
                     strncpy(str, start, *lengthPtr);                      n = emalloc(*lengthPtr + 1);
                     str[*lengthPtr] = '\0';                      strncpy(n, start, *lengthPtr);
                       n[*lengthPtr] = '\0';
                     *freePtr = TRUE;                      *freePtr = TRUE;
                     return(str);                      return n;
                 } else {                  } else
                     return (err ? var_Error : varNoError);                      return err ? var_Error : varNoError;
                 }  
             } else {              } else {
                 /*                  /* Still need to get to the end of the variable specification,
                  * Still need to get to the end of the variable specification,                   * so kludge up a Var structure for the modifications */
                  * so kludge up a Var structure for the modifications  
                  */  
                 v = new_var(str+1, NULL);       /* junk has name, for error reports */                  v = new_var(str+1, NULL);       /* junk has name, for error reports */
                 v->flags = VAR_JUNK;                  v->flags = VAR_JUNK;
             }              }
         }          }
     }      }
   
     if (v->flags & VAR_IN_USE) {      if (v->flags & VAR_IN_USE)
         Fatal("Variable %s is recursive.", v->name);          Fatal("Variable %s is recursive.", v->name);
         /*NOTREACHED*/          /*NOTREACHED*/
     } else {      else
         v->flags |= VAR_IN_USE;          v->flags |= VAR_IN_USE;
     }      /* Before doing any modification, we have to make sure the value
     /*  
      * Before doing any modification, we have to make sure the value  
      * has been fully expanded. If it looks like recursion might be       * has been fully expanded. If it looks like recursion might be
      * necessary (there's a dollar sign somewhere in the variable's value)       * necessary (there's a dollar sign somewhere in the variable's value)
      * we just call Var_Subst to do any other substitutions that are       * we just call Var_Subst to do any other substitutions that are
      * necessary. Note that the value returned by Var_Subst will have       * necessary. Note that the value returned by Var_Subst will have
      * been dynamically-allocated, so it will need freeing when we       * been dynamically-allocated, so it will need freeing when we
      * return.       * return.  */
      */  
     str = VarValue(v);      str = VarValue(v);
     if (strchr (str, '$') != (char *)NULL) {      if (strchr(str, '$') != NULL) {
         str = Var_Subst(str, ctxt, err);          str = Var_Subst(str, ctxt, err);
         *freePtr = TRUE;          *freePtr = TRUE;
     }      }
   
     v->flags &= ~VAR_IN_USE;      v->flags &= ~VAR_IN_USE;
   
       *lengthPtr = tstr - start + 1;
       if (str != NULL && haveModifier)
           str = VarModifiers_Apply(str, ctxt, err, freePtr, tstr+1, endc,
               lengthPtr);
   
       if (v->flags & VAR_JUNK) {
           /* Perform any free'ing needed and set *freePtr to FALSE so the caller
            * doesn't try to free a static pointer.  */
           if (*freePtr)
               free(str);
           *freePtr = FALSE;
           Buf_Destroy(&(v->val));
           free(v);
           if (dynamic) {
               str = emalloc(*lengthPtr + 1);
               strncpy(str, start, *lengthPtr);
               str[*lengthPtr] = '\0';
               *freePtr = TRUE;
           } else {
               str = err ? var_Error : varNoError;
           }
       }
       return str;
   }
   
   char *
   VarModifiers_Apply(str, ctxt, err, freePtr, start, endc, lengthPtr)
       char        *str;
       SymTable    *ctxt;
       Boolean     err;
       Boolean     *freePtr;
       char        *start;
       char        endc;
       size_t      *lengthPtr;
   {
       char        *tstr;
       char        delim;
       char        *cp;
   
       tstr = start;
   
     /*      /*
      * Now we need to apply any modifiers the user wants applied.       * Now we need to apply any modifiers the user wants applied.
      * These are:       * These are:
Line 1871 
Line 1887 
      *            :lhs=rhs      Like :S, but the rhs goes to the end of       *            :lhs=rhs      Like :S, but the rhs goes to the end of
      *                          the invocation.       *                          the invocation.
      */       */
     if ((str != (char *)NULL) && haveModifier) {      while (*tstr != endc) {
         /*          char    *newStr;    /* New value to return */
          * Skip initial colon while putting it back.          char    termc;      /* Character which terminated scan */
          */  
         *tstr++ = ':';  
         while (*tstr != endc) {  
             char        *newStr;    /* New value to return */  
             char        termc;      /* Character which terminated scan */  
   
             if (DEBUG(VAR)) {          if (DEBUG(VAR))
                 printf("Applying :%c to \"%s\"\n", *tstr, str);              printf("Applying :%c to \"%s\"\n", *tstr, str);
             }          switch (*tstr) {
             switch (*tstr) {              case 'N':
                 case 'N':              case 'M':
                 case 'M':              {
                 {                  for (cp = tstr + 1;
                     for (cp = tstr + 1;                       *cp != '\0' && *cp != ':' && *cp != endc;
                          *cp != '\0' && *cp != ':' && *cp != endc;                       cp++) {
                          cp++) {                      if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){
                         if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){                          cp++;
                             cp++;  
                         }  
                     }                      }
                     termc = *cp;  
                     *cp = '\0';  
                     if (*tstr == 'M')  
                         newStr = VarModify(str, VarMatch, tstr+1);  
                     else  
                         newStr = VarModify(str, VarNoMatch, tstr+1);  
                     break;  
                 }                  }
                 case 'S':                  termc = *cp;
                 {                  *cp = '\0';
                     VarPattern      pattern;                  if (*tstr == 'M')
                       newStr = VarModify(str, VarMatch, tstr+1);
                   else
                       newStr = VarModify(str, VarNoMatch, tstr+1);
                   break;
               }
               case 'S':
               {
                   VarPattern          pattern;
   
                     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.                  if (*tstr == '^') {
                      */                      pattern.flags |= VAR_MATCH_START;
                     if (*tstr == '^') {                      tstr++;
                         pattern.flags |= VAR_MATCH_START;                  }
                         tstr += 1;  
                     }  
   
                     cp = tstr;                  cp = tstr;
                     if ((pattern.lhs = VarGetPattern(ctxt, err, &cp, delim,                  if ((pattern.lhs = VarGetPattern(ctxt, err, &cp, delim,
                         &pattern.flags, &pattern.leftLen, NULL)) == NULL)                      &pattern.flags, &pattern.leftLen, NULL)) == NULL)
                         goto cleanup;                      goto cleanup;
   
                     if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim,                  if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim,
                         NULL, &pattern.rightLen, &pattern)) == NULL)                      NULL, &pattern.rightLen, &pattern)) == NULL)
                         goto cleanup;                      goto cleanup;
   
                     /*                  /* Check for global substitution. If 'g' after the final
                      * Check for global substitution. If 'g' after the final                   * delimiter, substitution is global and is marked that
                      * delimiter, substitution is global and is marked that                   * way.  */
                      * way.                  for (;; cp++) {
                      */                      switch (*cp) {
                     for (;; cp++) {                      case 'g':
                         switch (*cp) {                          pattern.flags |= VAR_SUB_GLOBAL;
                         case 'g':                          continue;
                             pattern.flags |= VAR_SUB_GLOBAL;                      case '1':
                             continue;                          pattern.flags |= VAR_SUB_ONE;
                         case '1':                          continue;
                             pattern.flags |= VAR_SUB_ONE;  
                             continue;  
                         }  
                         break;  
                     }                      }
   
                     termc = *cp;  
                     newStr = VarModify(str, VarSubstitute, &pattern);  
   
                     /*  
                      * Free the two strings.  
                      */  
                     free(pattern.lhs);  
                     free(pattern.rhs);  
                     break;                      break;
                 }                  }
 #ifndef MAKE_BOOTSTRAP  
                 case 'C':  
                 {  
                     VarREPattern    pattern;  
                     char           *re;  
                     int             error;  
   
                     pattern.flags = 0;                  termc = *cp;
                     delim = tstr[1];                  newStr = VarModify(str, VarSubstitute, &pattern);
                     tstr += 2;  
   
                     cp = tstr;                  /* Free the two strings.  */
                   free(pattern.lhs);
                   free(pattern.rhs);
                   break;
               }
   #ifndef MAKE_BOOTSTRAP
               case 'C':
               {
                   VarREPattern    pattern;
                   char           *re;
                   int             error;
   
                     if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL,                  pattern.flags = 0;
                         NULL, NULL)) == NULL)                  delim = tstr[1];
                         goto cleanup;                  tstr += 2;
   
                     if ((pattern.replace = VarGetPattern(ctxt, err, &cp,                  cp = tstr;
                         delim, NULL, NULL, NULL)) == NULL) {  
                         free(re);  
                         goto cleanup;  
                     }  
   
                     for (;; cp++) {                  if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL,
                         switch (*cp) {                      NULL, NULL)) == NULL)
                         case 'g':                      goto cleanup;
                             pattern.flags |= VAR_SUB_GLOBAL;  
                             continue;  
                         case '1':  
                             pattern.flags |= VAR_SUB_ONE;  
                             continue;  
                         }  
                         break;  
                     }  
   
                     termc = *cp;                  if ((pattern.replace = VarGetPattern(ctxt, err, &cp,
                       delim, NULL, NULL, NULL)) == NULL) {
                     error = regcomp(&pattern.re, re, REG_EXTENDED);  
                     free(re);                      free(re);
                     if (error) {                      goto cleanup;
                         *lengthPtr = cp - start + 1;                  }
                         VarREError(error, &pattern.re, "RE substitution error");  
                         free(pattern.replace);                  for (;; cp++) {
                         return (var_Error);                      switch (*cp) {
                       case 'g':
                           pattern.flags |= VAR_SUB_GLOBAL;
                           continue;
                       case '1':
                           pattern.flags |= VAR_SUB_ONE;
                           continue;
                     }                      }
                       break;
                   }
   
                     pattern.nsub = pattern.re.re_nsub + 1;                  termc = *cp;
                     if (pattern.nsub < 1)  
                         pattern.nsub = 1;                  error = regcomp(&pattern.re, re, REG_EXTENDED);
                     if (pattern.nsub > 10)                  free(re);
                         pattern.nsub = 10;                  if (error) {
                     pattern.matches = emalloc(pattern.nsub *                      *lengthPtr = cp - start + 1;
                                               sizeof(regmatch_t));                      VarREError(error, &pattern.re, "RE substitution error");
                     newStr = VarModify(str, VarRESubstitute, &pattern);  
                     regfree(&pattern.re);  
                     free(pattern.replace);                      free(pattern.replace);
                     free(pattern.matches);                      return var_Error;
                     break;  
                 }                  }
   
                   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, &pattern);
                   regfree(&pattern.re);
                   free(pattern.replace);
                   free(pattern.matches);
                   break;
               }
 #endif  #endif
                 case 'Q':              case 'Q':
                     if (tstr[1] == endc || tstr[1] == ':') {                  if (tstr[1] == endc || tstr[1] == ':') {
                         newStr = VarQuote (str);                      newStr = VarQuote(str);
                         cp = tstr + 1;                      cp = tstr + 1;
                         termc = *cp;                      termc = *cp;
                         break;                      break;
                     }                  }
                     /*FALLTHRU*/                  /*FALLTHRU*/
                 case 'T':              case 'T':
                     if (tstr[1] == endc || tstr[1] == ':') {                  if (tstr[1] == endc || tstr[1] == ':') {
                         newStr = VarModify(str, VarTail, NULL);                      newStr = VarModify(str, VarTail, NULL);
                         cp = tstr + 1;                      cp = tstr + 1;
                         termc = *cp;                      termc = *cp;
                         break;                      break;
                     }                  }
                     /*FALLTHRU*/                  /*FALLTHRU*/
                 case 'H':              case 'H':
                     if (tstr[1] == endc || tstr[1] == ':') {                  if (tstr[1] == endc || tstr[1] == ':') {
                         newStr = VarModify(str, VarHead, NULL);                      newStr = VarModify(str, VarHead, NULL);
                         cp = tstr + 1;                      cp = tstr + 1;
                         termc = *cp;                      termc = *cp;
                         break;                      break;
                     }                  }
                     /*FALLTHRU*/                  /*FALLTHRU*/
                 case 'E':              case 'E':
                     if (tstr[1] == endc || tstr[1] == ':') {                  if (tstr[1] == endc || tstr[1] == ':') {
                         newStr = VarModify(str, VarSuffix, NULL);                      newStr = VarModify(str, VarSuffix, NULL);
                         cp = tstr + 1;                      cp = tstr + 1;
                         termc = *cp;                      termc = *cp;
                         break;                      break;
                     }                  }
                     /*FALLTHRU*/                  /*FALLTHRU*/
                 case 'R':              case 'R':
                     if (tstr[1] == endc || tstr[1] == ':') {                  if (tstr[1] == endc || tstr[1] == ':') {
                         newStr = VarModify(str, VarRoot, NULL);                      newStr = VarModify(str, VarRoot, NULL);
                         cp = tstr + 1;                      cp = tstr + 1;
                         termc = *cp;                      termc = *cp;
                         break;                      break;
                     }                  }
                     /*FALLTHRU*/                  /*FALLTHRU*/
                 case 'U':              case 'U':
                     if (tstr[1] == endc || tstr[1] == ':') {                  if (tstr[1] == endc || tstr[1] == ':') {
                         newStr = VarModify(str, VarUppercase, NULL);                      newStr = VarModify(str, VarUppercase, NULL);
                         cp = tstr + 1;                      cp = tstr + 1;
                         termc = *cp;                      termc = *cp;
                         break;                      break;
                     }                  }
                     /*FALLTHRU*/                  /*FALLTHRU*/
                 case 'L':              case 'L':
                     if (tstr[1] == endc || tstr[1] == ':') {                  if (tstr[1] == endc || tstr[1] == ':') {
                         newStr = VarModify(str, VarLowercase, NULL);                      newStr = VarModify(str, VarLowercase, NULL);
                         cp = tstr + 1;                      cp = tstr + 1;
                         termc = *cp;                      termc = *cp;
                         break;                      break;
                     }                  }
                     /*FALLTHRU*/                  /*FALLTHRU*/
 #ifdef SUNSHCMD  #ifdef SUNSHCMD
                 case 's':              case 's':
                     if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) {                  if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) {
                         char *err;                      char *err;
                         newStr = Cmd_Exec (str, &err);                      newStr = Cmd_Exec(str, &err);
                         if (err)                      if (err)
                             Error (err, str);                          Error(err, str);
                         cp = tstr + 2;                      cp = tstr + 2;
                         termc = *cp;                      termc = *cp;
                         break;                      break;
                     }                  }
                     /*FALLTHRU*/                  /*FALLTHRU*/
 #endif  #endif
                 default:              default:
                 {              {
 #ifdef SYSVVARSUB  #ifdef SYSVVARSUB
                     /*                  /* This can either be a bogus modifier or a System-V
                      * This can either be a bogus modifier or a System-V                   * substitution command.  */
                      * substitution command.                  VarPattern      pattern;
                      */                  Boolean         eqFound;
                     VarPattern      pattern;                  int             cnt;    /* Used to count brace pairs when
                     Boolean         eqFound;                                           * variable in in parens or braces */
                   char            startc;
   
                     pattern.flags = 0;                  if (endc == ')')
                     eqFound = FALSE;                      startc = '(';
                     /*                  else
                      * First we make a pass through the string trying                      startc = '{';
                      * to verify it is a SYSV-make-style translation:  
                      * it must be: <string1>=<string2>)                  pattern.flags = 0;
                      */                  eqFound = FALSE;
                     cp = tstr;                  /* First we make a pass through the string trying
                    * to verify it is a SYSV-make-style translation:
                    * it must be: <string1>=<string2>) */
                   cp = tstr;
                   cnt = 1;
                   while (*cp != '\0' && cnt) {
                       if (*cp == '=') {
                           eqFound = TRUE;
                           /* continue looking for endc */
                       }
                       else if (*cp == endc)
                           cnt--;
                       else if (*cp == startc)
                           cnt++;
                       if (cnt)
                           cp++;
                   }
                   if (*cp == endc && eqFound) {
   
                       /* Now we break this sucker into the lhs and
                        * rhs. We must null terminate them of course.  */
                       for (cp = tstr; *cp != '='; cp++)
                           continue;
                       pattern.lhs = tstr;
                       pattern.leftLen = cp - tstr;
                       *cp++ = '\0';
   
                       pattern.rhs = cp;
                     cnt = 1;                      cnt = 1;
                     while (*cp != '\0' && cnt) {                      while (cnt) {
                         if (*cp == '=') {                          if (*cp == endc)
                             eqFound = TRUE;  
                             /* continue looking for endc */  
                         }  
                         else if (*cp == endc)  
                             cnt--;                              cnt--;
                         else if (*cp == startc)                          else if (*cp == startc)
                             cnt++;                              cnt++;
                         if (cnt)                          if (cnt)
                             cp++;                              cp++;
                     }                      }
                     if (*cp == endc && eqFound) {                      pattern.rightLen = cp - pattern.rhs;
                       *cp = '\0';
   
                         /*                      /* SYSV modifications happen through the whole
                          * Now we break this sucker into the lhs and                       * string. Note the pattern is anchored at the end.  */
                          * rhs. We must null terminate them of course.                      newStr = VarModify(str, VarSYSVMatch, &pattern);
                          */  
                         for (cp = tstr; *cp != '='; cp++)  
                             continue;  
                         pattern.lhs = tstr;  
                         pattern.leftLen = cp - tstr;  
                         *cp++ = '\0';  
   
                         pattern.rhs = cp;                      /* Restore the nulled characters */
                         cnt = 1;                      pattern.lhs[pattern.leftLen] = '=';
                         while (cnt) {                      pattern.rhs[pattern.rightLen] = endc;
                             if (*cp == endc)                      termc = endc;
                                 cnt--;                  } else
                             else if (*cp == startc)  
                                 cnt++;  
                             if (cnt)  
                                 cp++;  
                         }  
                         pattern.rightLen = cp - pattern.rhs;  
                         *cp = '\0';  
   
                         /*  
                          * SYSV modifications happen through the whole  
                          * string. Note the pattern is anchored at the end.  
                          */  
                         newStr = VarModify(str, VarSYSVMatch, &pattern);  
   
                         /*  
                          * Restore the nulled characters  
                          */  
                         pattern.lhs[pattern.leftLen] = '=';  
                         pattern.rhs[pattern.rightLen] = endc;  
                         termc = endc;  
                     } else  
 #endif  #endif
                     {                  {
                         Error ("Unknown modifier '%c'\n", *tstr);                      Error ("Unknown modifier '%c'\n", *tstr);
                         for (cp = tstr+1;                      for (cp = tstr+1;
                              *cp != ':' && *cp != endc && *cp != '\0';                           *cp != ':' && *cp != endc && *cp != '\0';)
                              cp++)                           cp++;
                                  continue;                      termc = *cp;
                         termc = *cp;                      newStr = var_Error;
                         newStr = var_Error;  
                     }  
                 }                  }
             }              }
             if (DEBUG(VAR)) {  
                 printf("Result is \"%s\"\n", newStr);  
             }  
   
             if (*freePtr) {  
                 free (str);  
             }  
             str = newStr;  
             if (str != var_Error) {  
                 *freePtr = TRUE;  
             } else {  
                 *freePtr = FALSE;  
             }  
             if (termc == '\0') {  
                 Error("Unclosed variable specification for %s", v->name);  
             } else if (termc == ':') {  
                 *cp++ = termc;  
             } else {  
                 *cp = termc;  
             }  
             tstr = cp;  
         }          }
         *lengthPtr = tstr - start + 1;          if (DEBUG(VAR))
     } else {              printf("Result is \"%s\"\n", newStr);
         *lengthPtr = tstr - start + 1;  
         *tstr = endc;  
     }  
   
     if (v->flags & VAR_JUNK) {          if (*freePtr)
         /*  
          * Perform any free'ing needed and set *freePtr to FALSE so the caller  
          * doesn't try to free a static pointer.  
          */  
         if (*freePtr) {  
             free(str);              free(str);
         }          str = newStr;
         *freePtr = FALSE;          if (str != var_Error)
         Buf_Destroy(&(v->val));  
         free(v);  
         if (dynamic) {  
             str = emalloc(*lengthPtr + 1);  
             strncpy(str, start, *lengthPtr);  
             str[*lengthPtr] = '\0';  
             *freePtr = TRUE;              *freePtr = TRUE;
         } else {          else
             str = err ? var_Error : varNoError;              *freePtr = FALSE;
         }          if (termc == '\0')
               Error("Unclosed variable specification");
           else if (termc == ':')
               *cp++ = termc;
           else
               *cp = termc;
           tstr = cp;
     }      }
     return (str);      *lengthPtr += tstr - start+1;
       return str;
   
 cleanup:  cleanup:
     *lengthPtr = cp - start + 1;      *lengthPtr += cp - start +1;
     if (*freePtr)      if (*freePtr)
         free(str);          free(str);
     Error("Unclosed substitution for %s (%c missing)",      Error("Unclosed substitution for (%c missing)", delim);
           v->name, delim);      return var_Error;
     return (var_Error);  
 }  }
   
 /*-  /*-

Legend:
Removed from v.1.41  
changed lines
  Added in v.1.42