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

Diff for /src/usr.bin/make/varmodifiers.c between version 1.21 and 1.22

version 1.21, 2007/09/16 12:01:11 version 1.22, 2007/09/16 12:02:38
Line 103 
Line 103 
 #define VAR_BANG_EQUAL  0x100  #define VAR_BANG_EQUAL  0x100
   
 typedef struct {  typedef struct {
     char          *lbuffer; /* left string to free */          char      *lbuffer; /* left string to free */
     char          *lhs;     /* String to match */          char      *lhs;     /* String to match */
     size_t        leftLen;  /* Length of string */          size_t    leftLen;  /* Length of string */
     char          *rhs;     /* Replacement string (w/ &'s removed) */          char      *rhs;     /* Replacement string (w/ &'s removed) */
     size_t        rightLen; /* Length of replacement */          size_t    rightLen; /* Length of replacement */
     int           flags;          int       flags;
 } VarPattern;  } VarPattern;
   
 struct LoopStuff {  struct LoopStuff {
     struct LoopVar      *var;          struct LoopVar  *var;
     char        *expand;          char    *expand;
     bool        err;          bool    err;
 };  };
   
 static bool VarHead(struct Name *, bool, Buffer, void *);  static bool VarHead(struct Name *, bool, Buffer, void *);
Line 134 
Line 134 
 static char *do_regex(const char *, const struct Name *, void *);  static char *do_regex(const char *, const struct Name *, void *);
   
 typedef struct {  typedef struct {
     regex_t       re;          regex_t   re;
     int           nsub;          int       nsub;
     regmatch_t   *matches;          regmatch_t       *matches;
     char         *replace;          char     *replace;
     int           flags;          int       flags;
 } VarREPattern;  } VarREPattern;
 #endif  #endif
   
Line 181 
Line 181 
 static struct Name *dummy_arg = &dummy;  static struct Name *dummy_arg = &dummy;
   
 static struct modifier {  static struct modifier {
         bool atstart;              bool atstart;
         void * (*getarg)(const char **, SymTable *, bool, int);              void * (*getarg)(const char **, SymTable *, bool, int);
         char * (*apply)(const char *, const struct Name *, void *);              char * (*apply)(const char *, const struct Name *, void *);
         bool (*word_apply)(struct Name *, bool, Buffer, void *);              bool (*word_apply)(struct Name *, bool, Buffer, void *);
         void   (*freearg)(void *);              void   (*freearg)(void *);
 } *choose_mod[256],  } *choose_mod[256],
     match_mod = {false, get_stringarg, NULL, VarMatch, free_stringarg},          match_mod = {false, get_stringarg, NULL, VarMatch, free_stringarg},
     nomatch_mod = {false, get_stringarg, NULL, VarNoMatch, free_stringarg},          nomatch_mod = {false, get_stringarg, NULL, VarNoMatch, free_stringarg},
     subst_mod = {false, get_spatternarg, NULL, VarSubstitute, free_patternarg},          subst_mod = {false, get_spatternarg, NULL, VarSubstitute, free_patternarg},
 #ifndef MAKE_BOOTSTRAP  #ifndef MAKE_BOOTSTRAP
     resubst_mod = {false, get_patternarg, do_regex, NULL, free_patternarg},          resubst_mod = {false, get_patternarg, do_regex, NULL, free_patternarg},
 #endif  #endif
     quote_mod = {false, check_empty, VarQuote, NULL , NULL},          quote_mod = {false, check_empty, VarQuote, NULL , NULL},
     tail_mod = {false, check_empty, NULL, VarTail, NULL},          tail_mod = {false, check_empty, NULL, VarTail, NULL},
     head_mod = {false, check_empty, NULL, VarHead, NULL},          head_mod = {false, check_empty, NULL, VarHead, NULL},
     suffix_mod = {false, check_empty, NULL, VarSuffix, NULL},          suffix_mod = {false, check_empty, NULL, VarSuffix, NULL},
     root_mod = {false, check_empty, NULL, VarRoot, NULL},          root_mod = {false, check_empty, NULL, VarRoot, NULL},
     upper_mod = {false, check_empty, do_upper, NULL, NULL},          upper_mod = {false, check_empty, do_upper, NULL, NULL},
     lower_mod = {false, check_empty, do_lower, NULL, NULL},          lower_mod = {false, check_empty, do_lower, NULL, NULL},
     shcmd_mod = {false, check_shcmd, do_shcmd, NULL, NULL},          shcmd_mod = {false, check_shcmd, do_shcmd, NULL, NULL},
     sysv_mod = {false, get_sysvpattern, NULL, VarSYSVMatch, free_patternarg},          sysv_mod = {false, get_sysvpattern, NULL, VarSYSVMatch, free_patternarg},
     uniq_mod = {false, check_empty, NULL, VarUniq, NULL},          uniq_mod = {false, check_empty, NULL, VarUniq, NULL},
     sort_mod = {false, check_empty, do_sort, NULL, NULL},          sort_mod = {false, check_empty, do_sort, NULL, NULL},
     loop_mod = {false, get_loop, finish_loop, VarLoop, free_looparg},          loop_mod = {false, get_loop, finish_loop, VarLoop, free_looparg},
     undef_mod = {true, get_value, do_undef, NULL, NULL},          undef_mod = {true, get_value, do_undef, NULL, NULL},
     def_mod = {true, get_value, do_def, NULL, NULL},          def_mod = {true, get_value, do_def, NULL, NULL},
     label_mod = {true, check_empty, do_label, NULL, NULL},          label_mod = {true, check_empty, do_label, NULL, NULL},
     path_mod = {true, check_empty, do_path, NULL, NULL},          path_mod = {true, check_empty, do_path, NULL, NULL},
     assign_mod = {true, assign_get_value, do_assign, NULL, free_patternarg},          assign_mod = {true, assign_get_value, do_assign, NULL, free_patternarg},
     exec_mod = {true, get_cmd, do_exec, NULL, free_patternarg}          exec_mod = {true, get_cmd, do_exec, NULL, free_patternarg}
 ;  ;
   
 void  void
 VarModifiers_Init()  VarModifiers_Init()
 {  {
     choose_mod['M'] = &match_mod;          choose_mod['M'] = &match_mod;
     choose_mod['N'] = &nomatch_mod;          choose_mod['N'] = &nomatch_mod;
     choose_mod['S'] = &subst_mod;          choose_mod['S'] = &subst_mod;
 #ifndef MAKE_BOOTSTRAP  #ifndef MAKE_BOOTSTRAP
     choose_mod['C'] = &resubst_mod;          choose_mod['C'] = &resubst_mod;
 #endif  #endif
     choose_mod['Q'] = &quote_mod;          choose_mod['Q'] = &quote_mod;
     choose_mod['T'] = &tail_mod;          choose_mod['T'] = &tail_mod;
     choose_mod['H'] = &head_mod;          choose_mod['H'] = &head_mod;
     choose_mod['E'] = &suffix_mod;          choose_mod['E'] = &suffix_mod;
     choose_mod['R'] = &root_mod;          choose_mod['R'] = &root_mod;
     if (FEATURES(FEATURE_UPPERLOWER)) {          if (FEATURES(FEATURE_UPPERLOWER)) {
         choose_mod['U'] = &upper_mod;                  choose_mod['U'] = &upper_mod;
         choose_mod['L'] = &lower_mod;                  choose_mod['L'] = &lower_mod;
     }          }
     if (FEATURES(FEATURE_SUNSHCMD))          if (FEATURES(FEATURE_SUNSHCMD))
         choose_mod['s'] = &shcmd_mod;                  choose_mod['s'] = &shcmd_mod;
     if (FEATURES(FEATURE_UNIQ))          if (FEATURES(FEATURE_UNIQ))
         choose_mod['u'] = &uniq_mod;                  choose_mod['u'] = &uniq_mod;
     if (FEATURES(FEATURE_SORT))          if (FEATURES(FEATURE_SORT))
         choose_mod['O'] = &sort_mod;                  choose_mod['O'] = &sort_mod;
     if (FEATURES(FEATURE_ODE)) {          if (FEATURES(FEATURE_ODE)) {
         choose_mod['@'] = &loop_mod;                  choose_mod['@'] = &loop_mod;
         choose_mod['D'] = &def_mod;                  choose_mod['D'] = &def_mod;
         choose_mod['U'] = &undef_mod;                  choose_mod['U'] = &undef_mod;
         choose_mod['L'] = &label_mod;                  choose_mod['L'] = &label_mod;
         choose_mod['P'] = &path_mod;                  choose_mod['P'] = &path_mod;
     }          }
     if (FEATURES(FEATURE_ASSIGN))          if (FEATURES(FEATURE_ASSIGN))
         choose_mod[':'] = &assign_mod;                  choose_mod[':'] = &assign_mod;
     if (FEATURES(FEATURE_EXECMOD))          if (FEATURES(FEATURE_EXECMOD))
         choose_mod['!'] = &exec_mod;                  choose_mod['!'] = &exec_mod;
 }  }
   
 /* All modifiers handle addSpace (need to add a space before placing the  /* All modifiers handle addSpace (need to add a space before placing the
Line 264 
Line 264 
 static bool  static bool
 VarHead(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)  VarHead(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
 {  {
     const char  *slash;          const char      *slash;
   
     slash = Str_rchri(word->s, word->e, '/');          slash = Str_rchri(word->s, word->e, '/');
     if (slash != NULL) {          if (slash != NULL) {
         if (addSpace)                  if (addSpace)
             Buf_AddSpace(buf);                          Buf_AddSpace(buf);
         Buf_Addi(buf, word->s, slash);                  Buf_Addi(buf, word->s, slash);
     } else {          } else {
         /* If no directory part, give . (q.v. the POSIX standard).  */                  /* If no directory part, give . (q.v. the POSIX standard).  */
         if (addSpace)                  if (addSpace)
             Buf_AddString(buf, " .");                          Buf_AddString(buf, " .");
         else                  else
             Buf_AddChar(buf, '.');                          Buf_AddChar(buf, '.');
     }          }
     return true;          return true;
 }  }
   
 /*-  /*-
Line 291 
Line 291 
 static bool  static bool
 VarTail(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)  VarTail(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
 {  {
     const char  *slash;          const char      *slash;
   
     if (addSpace)          if (addSpace)
         Buf_AddSpace(buf);                  Buf_AddSpace(buf);
     slash = Str_rchri(word->s, word->e, '/');          slash = Str_rchri(word->s, word->e, '/');
     if (slash != NULL)          if (slash != NULL)
         Buf_Addi(buf, slash+1, word->e);                  Buf_Addi(buf, slash+1, word->e);
     else          else
         Buf_Addi(buf, word->s, word->e);                  Buf_Addi(buf, word->s, word->e);
     return true;          return true;
 }  }
   
 /*-  /*-
Line 312 
Line 312 
 static bool  static bool
 VarSuffix(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)  VarSuffix(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
 {  {
     const char  *dot;          const char      *dot;
   
     dot = Str_rchri(word->s, word->e, '.');          dot = Str_rchri(word->s, word->e, '.');
     if (dot != NULL) {          if (dot != NULL) {
         if (addSpace)                  if (addSpace)
             Buf_AddSpace(buf);                          Buf_AddSpace(buf);
         Buf_Addi(buf, dot+1, word->e);                  Buf_Addi(buf, dot+1, word->e);
         addSpace = true;                  addSpace = true;
     }          }
     return addSpace;          return addSpace;
 }  }
   
 /*-  /*-
Line 334 
Line 334 
 static bool  static bool
 VarRoot(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)  VarRoot(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
 {  {
     const char  *dot;          const char      *dot;
   
     if (addSpace)          if (addSpace)
         Buf_AddSpace(buf);                  Buf_AddSpace(buf);
     dot = Str_rchri(word->s, word->e, '.');          dot = Str_rchri(word->s, word->e, '.');
     if (dot != NULL)          if (dot != NULL)
         Buf_Addi(buf, word->s, dot);                  Buf_Addi(buf, word->s, dot);
     else          else
         Buf_Addi(buf, word->s, word->e);                  Buf_Addi(buf, word->s, word->e);
     return true;          return true;
 }  }
   
 /*-  /*-
Line 356 
Line 356 
 VarMatch(struct Name *word, bool addSpace, Buffer buf,  VarMatch(struct Name *word, bool addSpace, Buffer buf,
     void *pattern) /* Pattern the word must match */      void *pattern) /* Pattern the word must match */
 {  {
     const char *pat = (const char *)pattern;          const char *pat = (const char *)pattern;
   
     if (Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) {          if (Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) {
         if (addSpace)                  if (addSpace)
             Buf_AddSpace(buf);                          Buf_AddSpace(buf);
         Buf_Addi(buf, word->s, word->e);                  Buf_Addi(buf, word->s, word->e);
         return true;                  return true;
     } else          } else
         return addSpace;                  return addSpace;
 }  }
   
 /*-  /*-
Line 377 
Line 377 
 VarNoMatch(struct Name *word, bool addSpace, Buffer buf,  VarNoMatch(struct Name *word, bool addSpace, Buffer buf,
     void *pattern) /* Pattern the word must not match */      void *pattern) /* Pattern the word must not match */
 {  {
     const char *pat = (const char *)pattern;          const char *pat = (const char *)pattern;
   
     if (!Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) {          if (!Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) {
         if (addSpace)                  if (addSpace)
             Buf_AddSpace(buf);                          Buf_AddSpace(buf);
         Buf_Addi(buf, word->s, word->e);                  Buf_Addi(buf, word->s, word->e);
         return true;                  return true;
     } else          } else
         return addSpace;                  return addSpace;
 }  }
   
 static bool  static bool
 VarUniq(struct Name *word, bool addSpace, Buffer buf, void *lastp)  VarUniq(struct Name *word, bool addSpace, Buffer buf, void *lastp)
 {  {
     struct Name *last = (struct Name *)lastp;          struct Name *last = (struct Name *)lastp;
   
         /* does not match */          /* does not match */
     if (last->s == NULL || last->e - last->s != word->e - word->s ||          if (last->s == NULL || last->e - last->s != word->e - word->s ||
         strncmp(word->s, last->s, word->e - word->s) != 0) {              strncmp(word->s, last->s, word->e - word->s) != 0) {
         if (addSpace)                  if (addSpace)
             Buf_AddSpace(buf);                          Buf_AddSpace(buf);
         Buf_Addi(buf, word->s, word->e);                  Buf_Addi(buf, word->s, word->e);
         addSpace = true;                  addSpace = true;
     }          }
     last->s = word->s;          last->s = word->s;
     last->e = word->e;          last->e = word->e;
     return addSpace;          return addSpace;
 }  }
   
 static bool  static bool
 VarLoop(struct Name *word, bool addSpace, Buffer buf, void *vp)  VarLoop(struct Name *word, bool addSpace, Buffer buf, void *vp)
 {  {
     struct LoopStuff *v = (struct LoopStuff *)vp;          struct LoopStuff *v = (struct LoopStuff *)vp;
   
     if (addSpace)          if (addSpace)
         Buf_AddSpace(buf);                  Buf_AddSpace(buf);
     Var_SubstVar(buf, v->expand, v->var, word->s);          Var_SubstVar(buf, v->expand, v->var, word->s);
     return true;          return true;
 }  }
   
 static char *  static char *
Line 455 
Line 455 
 static char *  static char *
 do_sort(const char *s, const struct Name *dummy UNUSED, void *arg UNUSED)  do_sort(const char *s, const struct Name *dummy UNUSED, void *arg UNUSED)
 {  {
     struct Name *t;          struct Name *t;
     unsigned long n, i, j;          unsigned long n, i, j;
     const char *start, *end;          const char *start, *end;
   
     n = 1024;   /* start at 1024 words */          n = 1024;       /* start at 1024 words */
     t = (struct Name *)emalloc(sizeof(struct Name) * n);          t = (struct Name *)emalloc(sizeof(struct Name) * n);
     start = s;          start = s;
     end = start;          end = start;
   
     for (i = 0;; i++) {          for (i = 0;; i++) {
         if (i == n) {                  if (i == n) {
                 n *= 2;                          n *= 2;
                 t = (struct Name *)erealloc(t, sizeof(struct Name) * n);                          t = (struct Name *)erealloc(t, sizeof(struct Name) * n);
                   }
                   start = iterate_words(&end);
                   if (start == NULL)
                           break;
                   t[i].s = start;
                   t[i].e = end;
         }          }
         start = iterate_words(&end);          if (i > 0) {
         if (start == NULL)                  BUFFER buf;
             break;  
         t[i].s = start;  
         t[i].e = end;  
     }  
     if (i > 0) {  
         BUFFER buf;  
   
         Buf_Init(&buf, end - s);                  Buf_Init(&buf, end - s);
         qsort(t, i, sizeof(struct Name), NameCompare);                  qsort(t, i, sizeof(struct Name), NameCompare);
         Buf_Addi(&buf, t[0].s, t[0].e);                  Buf_Addi(&buf, t[0].s, t[0].e);
         for (j = 1; j < i; j++) {                  for (j = 1; j < i; j++) {
                 Buf_AddSpace(&buf);                          Buf_AddSpace(&buf);
                 Buf_Addi(&buf, t[j].s, t[j].e);                          Buf_Addi(&buf, t[j].s, t[j].e);
                   }
                   free(t);
                   return Buf_Retrieve(&buf);
           } else {
                   free(t);
                   return "";
         }          }
         free(t);  
         return Buf_Retrieve(&buf);  
     } else {  
         free(t);  
         return "";  
     }  
 }  }
   
 static char *  static char *
 do_label(const char *s UNUSED, const struct Name *n, void *arg UNUSED)  do_label(const char *s UNUSED, const struct Name *n, void *arg UNUSED)
 {  {
     return Str_dupi(n->s, n->e);          return Str_dupi(n->s, n->e);
 }  }
   
 static char *  static char *
 do_path(const char *s UNUSED, const struct Name *n, void *arg UNUSED)  do_path(const char *s UNUSED, const struct Name *n, void *arg UNUSED)
 {  {
     GNode *gn;          GNode *gn;
   
     gn = Targ_FindNodei(n->s, n->e, TARG_NOCREATE);          gn = Targ_FindNodei(n->s, n->e, TARG_NOCREATE);
     if (gn == NULL)          if (gn == NULL)
         return Str_dupi(n->s, n->e);                  return Str_dupi(n->s, n->e);
     else          else
         return strdup(gn->path);                  return strdup(gn->path);
 }  }
   
 static char *  static char *
 do_def(const char *s, const struct Name *n UNUSED, void *arg)  do_def(const char *s, const struct Name *n UNUSED, void *arg)
 {  {
     VarPattern *v = (VarPattern *)arg;          VarPattern *v = (VarPattern *)arg;
     if (s == NULL) {          if (s == NULL) {
         free_patternarg(v);                  free_patternarg(v);
         return NULL;                  return NULL;
     } else          } else
         return v->lbuffer;                  return v->lbuffer;
 }  }
   
 static char *  static char *
 do_undef(const char *s, const struct Name *n UNUSED, void *arg)  do_undef(const char *s, const struct Name *n UNUSED, void *arg)
 {  {
     VarPattern *v = (VarPattern *)arg;          VarPattern *v = (VarPattern *)arg;
     if (s != NULL) {          if (s != NULL) {
         free_patternarg(v);                  free_patternarg(v);
         return NULL;                  return NULL;
     } else          } else
         return v->lbuffer;                  return v->lbuffer;
 }  }
   
 static char *  static char *
 do_assign(const char *s, const struct Name *n, void *arg)  do_assign(const char *s, const struct Name *n, void *arg)
 {  {
     VarPattern *v = (VarPattern *)arg;          VarPattern *v = (VarPattern *)arg;
     char *msg;          char *msg;
     char *result;          char *result;
   
     switch (v->flags) {          switch (v->flags) {
     case VAR_EQUAL:          case VAR_EQUAL:
         Var_Seti(n->s, n->e, v->lbuffer);                  Var_Seti(n->s, n->e, v->lbuffer);
         break;                  break;
     case VAR_MAY_EQUAL:          case VAR_MAY_EQUAL:
         if (s == NULL)                  if (s == NULL)
             Var_Seti(n->s, n->e, v->lbuffer);                          Var_Seti(n->s, n->e, v->lbuffer);
         break;                  break;
     case VAR_ADD_EQUAL:          case VAR_ADD_EQUAL:
         if (s == NULL)                  if (s == NULL)
             Var_Seti(n->s, n->e, v->lbuffer);                          Var_Seti(n->s, n->e, v->lbuffer);
         else                  else
             Var_Appendi(n->s, n->e, v->lbuffer);                          Var_Appendi(n->s, n->e, v->lbuffer);
         break;                  break;
     case VAR_BANG_EQUAL:          case VAR_BANG_EQUAL:
         result = Cmd_Exec(v->lbuffer, &msg);                  result = Cmd_Exec(v->lbuffer, &msg);
         if (result != NULL) {                  if (result != NULL) {
                 Var_Seti(n->s, n->e, result);                          Var_Seti(n->s, n->e, result);
                 free(result);                          free(result);
         } else                  } else
                 Error(msg, v->lbuffer);                          Error(msg, v->lbuffer);
         break;                  break;
   
     }          }
     return NULL;          return NULL;
 }  }
   
 static char *  static char *
 do_exec(const char *s UNUSED, const struct Name *n UNUSED, void *arg)  do_exec(const char *s UNUSED, const struct Name *n UNUSED, void *arg)
 {  {
     VarPattern *v = (VarPattern *)arg;          VarPattern *v = (VarPattern *)arg;
     char *msg;          char *msg;
     char *result;          char *result;
   
     result = Cmd_Exec(v->lbuffer, &msg);          result = Cmd_Exec(v->lbuffer, &msg);
     if (result == NULL)          if (result == NULL)
         Error(msg, v->lbuffer);                  Error(msg, v->lbuffer);
     return result;          return result;
 }  }
   
 /*-  /*-
Line 588 
Line 588 
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static bool  static bool
 VarSYSVMatch(struct Name *word, bool addSpace, Buffer buf,  VarSYSVMatch(struct Name *word, bool addSpace, Buffer buf, void *patp)
     void *patp) /* Pattern the word must match */  
 {  {
     size_t      len;          size_t  len;
     const char  *ptr;          const char      *ptr;
     VarPattern  *pat = (VarPattern *)patp;          VarPattern      *pat = (VarPattern *)patp;
   
     if (*word->s != '\0') {          if (*word->s != '\0') {
             if (addSpace)                  if (addSpace)
                 Buf_AddSpace(buf);                          Buf_AddSpace(buf);
             if ((ptr = Str_SYSVMatch(word->s, pat->lhs, &len)) != NULL)                  if ((ptr = Str_SYSVMatch(word->s, pat->lhs, &len)) != NULL)
                 Str_SYSVSubst(buf, pat->rhs, ptr, len);                          Str_SYSVSubst(buf, pat->rhs, ptr, len);
             else                  else
                 Buf_Addi(buf, word->s, word->e);                          Buf_Addi(buf, word->s, word->e);
             return true;                  return true;
     } else          } else
         return addSpace;                  return addSpace;
 }  }
   
 void *  void *
 get_sysvpattern(const char **p, SymTable *ctxt UNUSED, bool err UNUSED,  get_sysvpattern(const char **p, SymTable *ctxt UNUSED, bool err UNUSED,
     int endc)      int endc)
 {  {
     VarPattern          *pattern;          VarPattern              *pattern;
     const char          *cp, *cp2;          const char              *cp, *cp2;
     int cnt = 0;          int cnt = 0;
     char startc = endc == ')' ? '(' : '{';          char startc = endc == ')' ? '(' : '{';
   
     for (cp = *p;; cp++) {          for (cp = *p;; cp++) {
         if (*cp == '=' && cnt == 0)                  if (*cp == '=' && cnt == 0)
             break;                          break;
         if (*cp == '\0')                  if (*cp == '\0')
             return NULL;                          return NULL;
         if (*cp == startc)                  if (*cp == startc)
             cnt++;                          cnt++;
         else if (*cp == endc) {                  else if (*cp == endc) {
             cnt--;                          cnt--;
             if (cnt < 0)                          if (cnt < 0)
                 return NULL;                                  return NULL;
                   }
         }          }
     }          for (cp2 = cp+1;; cp2++) {
     for (cp2 = cp+1;; cp2++) {                  if ((*cp2 == ':' || *cp2 == endc) && cnt == 0)
         if ((*cp2 == ':' || *cp2 == endc) && cnt == 0)                          break;
             break;                  if (*cp2 == '\0')
         if (*cp2 == '\0')                          return NULL;
             return NULL;                  if (*cp2 == startc)
         if (*cp2 == startc)                          cnt++;
             cnt++;                  else if (*cp2 == endc) {
         else if (*cp2 == endc) {                          cnt--;
             cnt--;                          if (cnt < 0)
             if (cnt < 0)                                  return NULL;
                 return NULL;                  }
         }          }
     }  
           pattern = (VarPattern *)emalloc(sizeof(VarPattern));
     pattern = (VarPattern *)emalloc(sizeof(VarPattern));          pattern->lbuffer = pattern->lhs = Str_dupi(*p, cp);
     pattern->lbuffer = pattern->lhs = Str_dupi(*p, cp);          pattern->leftLen = cp - *p;
     pattern->leftLen = cp - *p;          pattern->rhs = Str_dupi(cp+1, cp2);
     pattern->rhs = Str_dupi(cp+1, cp2);          pattern->rightLen = cp2 - (cp+1);
     pattern->rightLen = cp2 - (cp+1);          pattern->flags = 0;
     pattern->flags = 0;          *p = cp2;
     *p = cp2;          return pattern;
     return pattern;  
 }  }
   
   
Line 794 
Line 793 
 static void  static void
 VarREError(int err, regex_t *pat, const char *str)  VarREError(int err, regex_t *pat, const char *str)
 {  {
     char        *errbuf;          char    *errbuf;
     int         errlen;          int     errlen;
   
     errlen = regerror(err, pat, 0, 0);          errlen = regerror(err, pat, 0, 0);
     errbuf = emalloc(errlen);          errbuf = emalloc(errlen);
     regerror(err, pat, errbuf, errlen);          regerror(err, pat, errbuf, errlen);
     Error("%s: %s", str, errbuf);          Error("%s: %s", str, errbuf);
     free(errbuf);          free(errbuf);
 }  }
   
 /*-  /*-
Line 814 
Line 813 
 static bool  static bool
 VarRESubstitute(struct Name *word, bool addSpace, Buffer buf, void *patternp)  VarRESubstitute(struct Name *word, bool addSpace, Buffer buf, void *patternp)
 {  {
     VarREPattern        *pat;          VarREPattern    *pat;
     int                 xrv;          int             xrv;
     const char          *wp;          const char              *wp;
     char                *rp;          char            *rp;
     int                 added;          int             added;
   
 #define MAYBE_ADD_SPACE()               \  #define MAYBE_ADD_SPACE()               \
         if (addSpace && !added)         \          if (addSpace && !added)         \
             Buf_AddSpace(buf);          \                  Buf_AddSpace(buf);      \
         added = 1          added = 1
   
     added = 0;          added = 0;
     wp = word->s;          wp = word->s;
     pat = patternp;          pat = patternp;
   
     if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) ==          if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) ==
         (VAR_SUB_ONE|VAR_SUB_MATCHED))              (VAR_SUB_ONE|VAR_SUB_MATCHED))
         xrv = REG_NOMATCH;                  xrv = REG_NOMATCH;
     else {          else {
     tryagain:          tryagain:
         xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0);                  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_AddChars(buf, pat->matches[0].rm_so, wp);  
         }          }
   
         for (rp = pat->replace; *rp; rp++) {          switch (xrv) {
             if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {          case 0:
                 MAYBE_ADD_SPACE();                  pat->flags |= VAR_SUB_MATCHED;
                 Buf_AddChar(buf,rp[1]);                  if (pat->matches[0].rm_so > 0) {
                 rp++;                          MAYBE_ADD_SPACE();
             }                          Buf_AddChars(buf, pat->matches[0].rm_so, wp);
             else if (*rp == '&' || (*rp == '\\' && isdigit(rp[1]))) {  
                 int n;  
                 const char *subbuf;  
                 int sublen;  
                 char errstr[3];  
   
                 if (*rp == '&') {  
                     n = 0;  
                     errstr[0] = '&';  
                     errstr[1] = '\0';  
                 } else {  
                     n = rp[1] - '0';  
                     errstr[0] = '\\';  
                     errstr[1] = rp[1];  
                     errstr[2] = '\0';  
                     rp++;  
                 }                  }
   
                 if (n > pat->nsub) {                  for (rp = pat->replace; *rp; rp++) {
                     Error("No subexpression %s", &errstr[0]);                          if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
                     subbuf = "";                                  MAYBE_ADD_SPACE();
                     sublen = 0;                                  Buf_AddChar(buf,rp[1]);
                 } else if (pat->matches[n].rm_so == -1 &&                                  rp++;
                            pat->matches[n].rm_eo == -1) {                          }
                     Error("No match for subexpression %s", &errstr[0]);                          else if (*rp == '&' ||
                     subbuf = "";                              (*rp == '\\' && isdigit(rp[1]))) {
                     sublen = 0;                                  int n;
                 } else {                                  const char *subbuf;
                     subbuf = wp + pat->matches[n].rm_so;                                  int sublen;
                     sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so;                                  char errstr[3];
                 }  
   
                 if (sublen > 0) {                                  if (*rp == '&') {
                     MAYBE_ADD_SPACE();                                          n = 0;
                     Buf_AddChars(buf, sublen, subbuf);                                          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_AddChars(buf, sublen, subbuf);
                                   }
                           } else {
                                   MAYBE_ADD_SPACE();
                                   Buf_AddChar(buf, *rp);
                           }
                 }                  }
             } else {                  wp += pat->matches[0].rm_eo;
                 MAYBE_ADD_SPACE();                  if (pat->flags & VAR_SUB_GLOBAL)
                 Buf_AddChar(buf, *rp);                          goto tryagain;
             }                  if (*wp) {
                           MAYBE_ADD_SPACE();
                           Buf_AddString(buf, wp);
                   }
                   break;
           default:
                   VarREError(xrv, &pat->re, "Unexpected regex error");
                  /* fall through */
           case REG_NOMATCH:
                   if (*wp) {
                           MAYBE_ADD_SPACE();
                           Buf_AddString(buf, wp);
                   }
                   break;
         }          }
         wp += pat->matches[0].rm_eo;          return addSpace||added;
         if (pat->flags & VAR_SUB_GLOBAL)  
             goto tryagain;  
         if (*wp) {  
             MAYBE_ADD_SPACE();  
             Buf_AddString(buf, wp);  
         }  
         break;  
     default:  
         VarREError(xrv, &pat->re, "Unexpected regex error");  
        /* fall through */  
     case REG_NOMATCH:  
         if (*wp) {  
             MAYBE_ADD_SPACE();  
             Buf_AddString(buf, wp);  
         }  
         break;  
     }  
     return addSpace||added;  
 }  }
 #endif  #endif
   
Line 930 
Line 933 
     bool (*modProc)(struct Name *, bool, Buffer, void *),      bool (*modProc)(struct Name *, bool, Buffer, void *),
     void *datum)                /* Datum to pass it */      void *datum)                /* Datum to pass it */
 {  {
     BUFFER        buf;          /* Buffer for the new string */          BUFFER    buf;          /* Buffer for the new string */
     bool          addSpace;     /* true if need to add a space to the          bool      addSpace;     /* true if need to add a space to the
                                  * buffer before adding the trimmed                                       * buffer before adding the trimmed
                                  * word */                                       * word */
     struct Name   word;          struct Name       word;
   
     Buf_Init(&buf, 0);          Buf_Init(&buf, 0);
     addSpace = false;          addSpace = false;
   
     word.e = str;          word.e = str;
   
     while ((word.s = iterate_words(&word.e)) != NULL) {          while ((word.s = iterate_words(&word.e)) != NULL) {
         char termc;                  char termc;
   
         termc = *word.e;                  termc = *word.e;
         *((char *)(word.e)) = '\0';                  *((char *)(word.e)) = '\0';
         addSpace = (*modProc)(&word, addSpace, &buf, datum);                  addSpace = (*modProc)(&word, addSpace, &buf, datum);
         *((char *)(word.e)) = termc;                  *((char *)(word.e)) = termc;
     }          }
     return Buf_Retrieve(&buf);          return Buf_Retrieve(&buf);
 }  }
   
 /*-  /*-
Line 972 
Line 975 
 VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1,  VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1,
     int delim2, size_t *length, VarPattern *pattern)      int delim2, size_t *length, VarPattern *pattern)
 {  {
     const char  *cp;          const char      *cp;
     char        *result;          char    *result;
     BUFFER      buf;          BUFFER  buf;
     size_t      junk;          size_t  junk;
   
     Buf_Init(&buf, 0);          Buf_Init(&buf, 0);
     if (length == NULL)          if (length == NULL)
         length = &junk;                  length = &junk;
   
 #define IS_A_MATCH(cp, delim1, delim2) \  #define IS_A_MATCH(cp, delim1, delim2) \
     (cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \          (cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \
      cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&')))           cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&')))
   
     /*          /*
      * Skim through until the matching delimiter is found;           * Skim through until the matching delimiter is found;
      * pick up variable substitutions on the way. Also allow           * pick up variable substitutions on the way. Also allow
      * backslashes to quote the delimiter, $, and \, but don't           * backslashes to quote the delimiter, $, and \, but don't
      * touch other backslashes.           * touch other backslashes.
      */           */
     for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) {          for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) {
         if (IS_A_MATCH(cp, delim1, delim2)) {                  if (IS_A_MATCH(cp, delim1, delim2)) {
             Buf_AddChar(&buf, cp[1]);                          Buf_AddChar(&buf, cp[1]);
             cp++;                          cp++;
         } else if (*cp == '$') {                  } else if (*cp == '$') {
                 /* Allowed at end of pattern */                          /* Allowed at end of pattern */
             if (cp[1] == delim1 || cp[1] == delim2)                          if (cp[1] == delim1 || cp[1] == delim2)
                 Buf_AddChar(&buf, *cp);                                  Buf_AddChar(&buf, *cp);
             else {                          else {
                 size_t len;                                  size_t len;
   
                 /* If unescaped dollar sign not before the delimiter,                                  /* If unescaped dollar sign not before the
                  * assume it's a variable substitution and recurse.  */                                   * delimiter, assume it's a variable
                 (void)Var_ParseBuffer(&buf, cp, ctxt, err, &len);                                   * substitution and recurse.  */
                 cp += len - 1;                                  (void)Var_ParseBuffer(&buf, cp, ctxt, err,
             }                                      &len);
         } else if (pattern && *cp == '&')                                  cp += len - 1;
             Buf_AddChars(&buf, pattern->leftLen, pattern->lhs);                          }
         else                  } else if (pattern && *cp == '&')
             Buf_AddChar(&buf, *cp);                          Buf_AddChars(&buf, pattern->leftLen, pattern->lhs);
     }                  else
                           Buf_AddChar(&buf, *cp);
           }
   
     *length = Buf_Size(&buf);          *length = Buf_Size(&buf);
     result = Buf_Retrieve(&buf);          result = Buf_Retrieve(&buf);
   
     if (*cp != delim1 && *cp != delim2) {          if (*cp != delim1 && *cp != delim2) {
         *tstr = cp;                  *tstr = cp;
         *length = 0;                  *length = 0;
         free(result);                  free(result);
         return NULL;                  return NULL;
     }          }
     else {          else {
         *tstr = ++cp;                  *tstr = ++cp;
         return result;                  return result;
     }          }
 }  }
   
 /*-  /*-
Line 1041 
Line 1046 
 VarQuote(const char *str, const struct Name *n UNUSED, void *dummy UNUSED)  VarQuote(const char *str, const struct Name *n UNUSED, void *dummy UNUSED)
 {  {
   
     BUFFER        buf;          BUFFER    buf;
     /* This should cover most shells :-( */          /* This should cover most shells :-( */
     static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";          static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
   
     Buf_Init(&buf, MAKE_BSIZE);          Buf_Init(&buf, MAKE_BSIZE);
     for (; *str; str++) {          for (; *str; str++) {
         if (strchr(meta, *str) != NULL)                  if (strchr(meta, *str) != NULL)
             Buf_AddChar(&buf, '\\');                          Buf_AddChar(&buf, '\\');
         Buf_AddChar(&buf, *str);                  Buf_AddChar(&buf, *str);
     }          }
     return Buf_Retrieve(&buf);          return Buf_Retrieve(&buf);
 }  }
   
 static void *  static void *
 check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)  check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
 {  {
     dummy_arg->s = NULL;          dummy_arg->s = NULL;
     if ((*p)[1] == endc || (*p)[1] == ':') {          if ((*p)[1] == endc || (*p)[1] == ':') {
         (*p)++;                  (*p)++;
         return dummy_arg;                  return dummy_arg;
     } else          } else
         return NULL;                  return NULL;
 }  }
   
 static void *  static void *
 check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)  check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
 {  {
     if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) {          if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) {
         (*p)+=2;                  (*p)+=2;
         return dummy_arg;                  return dummy_arg;
     } else          } else
         return NULL;                  return NULL;
 }  }
   
   
 static char *  static char *
 do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED)  do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
 {  {
     char        *err;          char *err;
     char        *t;          char *t;
   
     t = Cmd_Exec(s, &err);          t = Cmd_Exec(s, &err);
     if (err)          if (err)
         Error(err, s);                  Error(err, s);
     return t;          return t;
 }  }
   
 static void *  static void *
 get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)  get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
 {  {
     const char  *cp;          const char *cp;
     char        *s;          char *s;
   
     for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) {          for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) {
         if (*cp == '\\') {                  if (*cp == '\\') {
             if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\')                          if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\')
                 cp++;                                  cp++;
         } else if (*cp == '\0')                  } else if (*cp == '\0')
             return NULL;                          return NULL;
     }          }
     s = escape_dupi(*p+1, cp, ":)}");          s = escape_dupi(*p+1, cp, ":)}");
     *p = cp;          *p = cp;
     return s;          return s;
 }  }
   
 static void  static void
 free_stringarg(void *arg)  free_stringarg(void *arg)
 {  {
     free(arg);          free(arg);
 }  }
   
 static char *  static char *
 do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED)  do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
 {  {
     size_t      len, i;          size_t len, i;
     char        *t;          char *t;
   
     len = strlen(s);          len = strlen(s);
     t = emalloc(len+1);          t = emalloc(len+1);
     for (i = 0; i < len; i++)          for (i = 0; i < len; i++)
         t[i] = toupper(s[i]);                  t[i] = toupper(s[i]);
     t[len] = '\0';          t[len] = '\0';
     return t;          return t;
 }  }
   
 static char *  static char *
 do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED)  do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
 {  {
     size_t      len, i;          size_t  len, i;
     char        *t;          char    *t;
   
     len = strlen(s);          len = strlen(s);
     t = emalloc(len+1);          t = emalloc(len+1);
     for (i = 0; i < len; i++)          for (i = 0; i < len; i++)
         t[i] = tolower(s[i]);                  t[i] = tolower(s[i]);
     t[len] = '\0';          t[len] = '\0';
     return t;          return t;
 }  }
   
 static void *  static void *
 get_patternarg(const char **p, SymTable *ctxt, bool err, int endc)  get_patternarg(const char **p, SymTable *ctxt, bool err, int endc)
 {  {
     return common_get_patternarg(p, ctxt, err, endc, false);          return common_get_patternarg(p, ctxt, err, endc, false);
 }  }
   
 /* Extract anchors */  /* Extract anchors */
 static void *  static void *
 get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc)  get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc)
 {  {
     VarPattern *pattern;          VarPattern *pattern;
   
     pattern = common_get_patternarg(p, ctxt, err, endc, true);          pattern = common_get_patternarg(p, ctxt, err, endc, true);
     if (pattern != NULL && pattern->leftLen > 0) {          if (pattern != NULL && pattern->leftLen > 0) {
         if (pattern->lhs[pattern->leftLen-1] == '$') {                  if (pattern->lhs[pattern->leftLen-1] == '$') {
                 pattern->leftLen--;                              pattern->leftLen--;
                 pattern->flags |= VAR_MATCH_END;                              pattern->flags |= VAR_MATCH_END;
         }  
         if (pattern->lhs[0] == '^') {  
                 pattern->lhs++;  
                 pattern->leftLen--;  
                 pattern->flags |= VAR_MATCH_START;  
                 }                  }
     }                  if (pattern->lhs[0] == '^') {
     return pattern;                              pattern->lhs++;
                               pattern->leftLen--;
                               pattern->flags |= VAR_MATCH_START;
                   }
           }
           return pattern;
 }  }
   
 static void  static void
 free_looparg(void *arg)  free_looparg(void *arg)
 {  {
     struct LoopStuff *l = (struct LoopStuff *)arg;          struct LoopStuff *l = (struct LoopStuff *)arg;
   
     Var_DeleteLoopVar(l->var);          Var_DeleteLoopVar(l->var);
     free(l->expand);          free(l->expand);
 }  }
   
 static char *  static char *
 LoopGrab(const char **s)  LoopGrab(const char **s)
 {  {
     const char *p, *start;          const char *p, *start;
   
     start = *s;          start = *s;
     for (p = start; *p != '@'; p++) {          for (p = start; *p != '@'; p++) {
         if (*p == '\\')                  if (*p == '\\')
             p++;                          p++;
         if (*p == 0)                  if (*p == 0)
                 return NULL;                          return NULL;
     }          }
     *s = p+1;          *s = p+1;
     return escape_dupi(start, p, "@\\");          return escape_dupi(start, p, "@\\");
 }  }
   
 static void *  static void *
 get_loop(const char **p, SymTable *ctxt UNUSED, bool err, int endc)  get_loop(const char **p, SymTable *ctxt UNUSED, bool err, int endc)
 {  {
     static struct LoopStuff     loop;          static struct LoopStuff loop;
     const char *s;          const char *s;
     const char *var;          const char *var;
   
     s = *p +1;          s = *p +1;
   
     loop.var = NULL;          loop.var = NULL;
     loop.expand = NULL;          loop.expand = NULL;
     loop.err = err;          loop.err = err;
     var = LoopGrab(&s);          var = LoopGrab(&s);
     if (var != NULL) {          if (var != NULL) {
         loop.expand = LoopGrab(&s);                  loop.expand = LoopGrab(&s);
         if (*s == endc || *s == ':') {                  if (*s == endc || *s == ':') {
             *p = s;                          *p = s;
             loop.var = Var_NewLoopVar(var, NULL);                          loop.var = Var_NewLoopVar(var, NULL);
             return &loop;                          return &loop;
                   }
         }          }
     }          free_looparg(&loop);
     free_looparg(&loop);          return NULL;
     return NULL;  
 }  }
   
 static void *  static void *
 common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc,  common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc,
     bool dosubst)      bool dosubst)
 {  {
     VarPattern *pattern;          VarPattern *pattern;
     char        delim;          char delim;
     const char  *s;          const char *s;
   
     pattern = (VarPattern *)emalloc(sizeof(VarPattern));          pattern = (VarPattern *)emalloc(sizeof(VarPattern));
     pattern->flags = 0;          pattern->flags = 0;
     s = *p;          s = *p;
   
     delim = s[1];          delim = s[1];
     if (delim == '\0')          if (delim == '\0')
         return NULL;                  return NULL;
     s += 2;          s += 2;
   
     pattern->rhs = NULL;          pattern->rhs = NULL;
     pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim,          pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim,
         &pattern->leftLen, NULL);              &pattern->leftLen, NULL);
     pattern->lbuffer = pattern->lhs;          pattern->lbuffer = pattern->lhs;
     if (pattern->lhs != NULL) {          if (pattern->lhs != NULL) {
         pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim,                  pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim,
             &pattern->rightLen, dosubst ? pattern: NULL);                      &pattern->rightLen, dosubst ? pattern: NULL);
         if (pattern->rhs != NULL) {                  if (pattern->rhs != NULL) {
             /* Check for global substitution. If 'g' after the final                          /* Check for global substitution. If 'g' after the
              * delimiter, substitution is global and is marked that                           * final delimiter, substitution is global and is
              * way.  */                           * marked that way.  */
             for (;; s++) {                          for (;; s++) {
                 switch (*s) {                                  switch (*s) {
                 case 'g':                                  case 'g':
                     pattern->flags |= VAR_SUB_GLOBAL;                                          pattern->flags |= VAR_SUB_GLOBAL;
                     continue;                                          continue;
                 case '1':                                  case '1':
                     pattern->flags |= VAR_SUB_ONE;                                          pattern->flags |= VAR_SUB_ONE;
                     continue;                                          continue;
                                   }
                                   break;
                           }
                           if (*s == endc || *s == ':') {
                                   *p = s;
                                   return pattern;
                           }
                 }                  }
                 break;  
             }  
             if (*s == endc || *s == ':') {  
                 *p = s;  
                 return pattern;  
             }  
         }          }
     }          free_patternarg(pattern);
     free_patternarg(pattern);          return NULL;
     return NULL;  
 }  }
   
 static void *  static void *
 assign_get_value(const char **p, SymTable *ctxt, bool err, int endc)  assign_get_value(const char **p, SymTable *ctxt, bool err, int endc)
 {  {
     const char *s;          const char *s;
     int flags;          int flags;
     VarPattern *arg;          VarPattern *arg;
   
     s = *p + 1;          s = *p + 1;
     if (s[0] == '=')          if (s[0] == '=')
         flags = VAR_EQUAL;                  flags = VAR_EQUAL;
     else if (s[0] == '?' && s[1] == '=')          else if (s[0] == '?' && s[1] == '=')
         flags = VAR_MAY_EQUAL;                  flags = VAR_MAY_EQUAL;
     else if (s[0] == '+' && s[1] == '=')          else if (s[0] == '+' && s[1] == '=')
         flags = VAR_ADD_EQUAL;                  flags = VAR_ADD_EQUAL;
     else if (s[0] == '!' && s[1] == '=')          else if (s[0] == '!' && s[1] == '=')
         flags = VAR_BANG_EQUAL;                  flags = VAR_BANG_EQUAL;
     else          else
         return NULL;                  return NULL;
   
     arg = get_value(&s, ctxt, err, endc);          arg = get_value(&s, ctxt, err, endc);
     if (arg != NULL) {          if (arg != NULL) {
         *p = s;                  *p = s;
         arg->flags = flags;                  arg->flags = flags;
     }          }
     return arg;          return arg;
 }  }
   
 static void *  static void *
 get_value(const char **p, SymTable *ctxt, bool err, int endc)  get_value(const char **p, SymTable *ctxt, bool err, int endc)
 {  {
     VarPattern *pattern;          VarPattern *pattern;
     const char *s;          const char *s;
   
     pattern = (VarPattern *)emalloc(sizeof(VarPattern));          pattern = (VarPattern *)emalloc(sizeof(VarPattern));
     s = *p + 1;          s = *p + 1;
     pattern->rhs = NULL;          pattern->rhs = NULL;
     pattern->lbuffer = VarGetPattern(ctxt, err, &s, ':', endc,          pattern->lbuffer = VarGetPattern(ctxt, err, &s, ':', endc,
         &pattern->leftLen, NULL);              &pattern->leftLen, NULL);
     if (s[-1] == endc || s[-1] == ':') {          if (s[-1] == endc || s[-1] == ':') {
         *p = s-1;                  *p = s-1;
         return pattern;                  return pattern;
     }          }
     free_patternarg(pattern);          free_patternarg(pattern);
     return NULL;          return NULL;
 }  }
   
 static void *  static void *
 get_cmd(const char **p, SymTable *ctxt, bool err, int endc UNUSED)  get_cmd(const char **p, SymTable *ctxt, bool err, int endc UNUSED)
 {  {
     VarPattern *pattern;          VarPattern *pattern;
     const char *s;          const char *s;
   
     pattern = (VarPattern *)emalloc(sizeof(VarPattern));          pattern = (VarPattern *)emalloc(sizeof(VarPattern));
     s = *p + 1;          s = *p + 1;
     pattern->rhs = NULL;          pattern->rhs = NULL;
     pattern->lbuffer = VarGetPattern(ctxt, err, &s, '!', '!',          pattern->lbuffer = VarGetPattern(ctxt, err, &s, '!', '!',
         &pattern->leftLen, NULL);              &pattern->leftLen, NULL);
     if (s[-1] == '!') {          if (s[-1] == '!') {
         *p = s-1;                  *p = s-1;
         return pattern;                  return pattern;
     }          }
     free_patternarg(pattern);          free_patternarg(pattern);
     return NULL;          return NULL;
 }  }
   
 static void  static void
 free_patternarg(void *p)  free_patternarg(void *p)
 {  {
     VarPattern *vp = (VarPattern *)p;          VarPattern *vp = (VarPattern *)p;
   
     free(vp->lbuffer);          free(vp->lbuffer);
     free(vp->rhs);          free(vp->rhs);
     free(vp);          free(vp);
 }  }
   
 #ifndef MAKE_BOOTSTRAP  #ifndef MAKE_BOOTSTRAP
 static char *  static char *
 do_regex(const char *s, const struct Name *n UNUSED, void *arg)  do_regex(const char *s, const struct Name *n UNUSED, void *arg)
 {  {
     VarREPattern p2;          VarREPattern p2;
     VarPattern  *p = (VarPattern *)arg;          VarPattern *p = (VarPattern *)arg;
     int         error;          int error;
     char        *result;          char *result;
   
     error = regcomp(&p2.re, p->lhs, REG_EXTENDED);          error = regcomp(&p2.re, p->lhs, REG_EXTENDED);
     if (error) {          if (error) {
         VarREError(error, &p2.re, "RE substitution error");                  VarREError(error, &p2.re, "RE substitution error");
         return var_Error;                  return var_Error;
     }          }
     p2.nsub = p2.re.re_nsub + 1;          p2.nsub = p2.re.re_nsub + 1;
     p2.replace = p->rhs;          p2.replace = p->rhs;
     p2.flags = p->flags;          p2.flags = p->flags;
     if (p2.nsub < 1)          if (p2.nsub < 1)
         p2.nsub = 1;                  p2.nsub = 1;
     if (p2.nsub > 10)          if (p2.nsub > 10)
         p2.nsub = 10;                  p2.nsub = 10;
     p2.matches = emalloc(p2.nsub * sizeof(regmatch_t));          p2.matches = emalloc(p2.nsub * sizeof(regmatch_t));
     result = VarModify((char *)s, VarRESubstitute, &p2);          result = VarModify((char *)s, VarRESubstitute, &p2);
     regfree(&p2.re);          regfree(&p2.re);
     free(p2.matches);          free(p2.matches);
     return result;          return result;
 }  }
 #endif  #endif
   
Line 1374 
Line 1379 
 VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt,  VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt,
     bool err, bool *freePtr, const char **pscan, int paren)      bool err, bool *freePtr, const char **pscan, int paren)
 {  {
     const char  *tstr;          const char *tstr;
     bool        atstart;    /* Some ODE modifiers only make sense at start */          bool atstart;    /* Some ODE modifiers only make sense at start */
     char endc = paren == '(' ? ')' : '}';          char endc = paren == '(' ? ')' : '}';
     const char *start = *pscan;          const char *start = *pscan;
   
     tstr = start;          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:
      *            :M<pattern>   words which match the given <pattern>.           *                :M<pattern>   words which match the given <pattern>.
      *                          <pattern> is of the standard file           *                              <pattern> is of the standard file
      *                          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
      *            :C<d><pat1><d><pat2><d>[g]           *                              value
      *                          Substitute <pat2> for regex <pat1> in the value           *                :C<d><pat1><d><pat2><d>[g]
      *            :H            Substitute the head of each word           *                              Substitute <pat2> for regex <pat1> in
      *            :T            Substitute the tail of each word           *                              the value
      *            :E            Substitute the extension (minus '.') of           *                :H            Substitute the head of each word
      *                          each word           *                :T            Substitute the tail of each word
      *            :R            Substitute the root of each word           *                :E            Substitute the extension (minus '.') of
      *                          (pathname minus the suffix).           *                              each word
      *            :lhs=rhs      Like :S, but the rhs goes to the end of           *                :R            Substitute the root of each word
      *                          the invocation.           *                              (pathname minus the suffix).
      */           *                :lhs=rhs      Like :S, but the rhs goes to the end of
            *                              the invocation.
     atstart = true;           */
     while (*tstr != endc && *tstr != '\0') {  
         struct modifier *mod;          atstart = true;
         void            *arg;          while (*tstr != endc && *tstr != '\0') {
         char            *newStr;                  struct modifier *mod;
                   void *arg;
                   char *newStr;
   
         tstr++;                  tstr++;
         if (DEBUG(VAR))                  if (DEBUG(VAR))
             printf("Applying :%c to \"%s\"\n", *tstr, str);                          printf("Applying :%c to \"%s\"\n", *tstr, str);
   
         mod = choose_mod[*tstr];                  mod = choose_mod[*tstr];
         arg = NULL;                  arg = NULL;
   
         if (mod != NULL && (!mod->atstart || atstart))                  if (mod != NULL && (!mod->atstart || atstart))
             arg = mod->getarg(&tstr, ctxt, err, endc);                          arg = mod->getarg(&tstr, ctxt, err, endc);
         if (FEATURES(FEATURE_SYSVVARSUB) && arg == NULL) {                  if (FEATURES(FEATURE_SYSVVARSUB) && arg == NULL) {
             mod = &sysv_mod;                          mod = &sysv_mod;
             arg = mod->getarg(&tstr, ctxt, err, endc);                          arg = mod->getarg(&tstr, ctxt, err, endc);
         }                  }
         atstart = false;                  atstart = false;
         if (arg != NULL) {                  if (arg != NULL) {
             if (str != NULL || (mod->atstart && name != NULL)) {                          if (str != NULL || (mod->atstart && name != NULL)) {
                 if (mod->word_apply != NULL) {                                  if (mod->word_apply != NULL) {
                     newStr = VarModify(str, mod->word_apply, arg);                                          newStr = VarModify(str,
                     if (mod->apply != NULL) {                                              mod->word_apply, arg);
                         char *newStr2;                                          if (mod->apply != NULL) {
                                                   char *newStr2;
   
                         newStr2 = mod->apply(newStr, name, arg);                                                  newStr2 = mod->apply(newStr,
                         free(newStr);                                                      name, arg);
                         newStr = newStr2;                                                  free(newStr);
                     }                                                  newStr = newStr2;
                 } else                                          }
                     newStr = mod->apply(str, name, arg);                                  } else
                 if (*freePtr)                                          newStr = mod->apply(str, name, arg);
                     free(str);                                  if (*freePtr)
                 str = newStr;                                          free(str);
                 if (str != var_Error)                                  str = newStr;
                     *freePtr = true;                                  if (str != var_Error)
                 else                                          *freePtr = true;
                     *freePtr = false;                                  else
             }                                          *freePtr = false;
             if (mod->freearg != NULL)                          }
                 mod->freearg(arg);                          if (mod->freearg != NULL)
         } else {                                  mod->freearg(arg);
             Error("Bad modifier: %s\n", tstr);                  } else {
             /* Try skipping to end of var... */                          Error("Bad modifier: %s\n", tstr);
             for (tstr++; *tstr != endc && *tstr != '\0';)                          /* Try skipping to end of var... */
                 tstr++;                          for (tstr++; *tstr != endc && *tstr != '\0';)
             if (str != NULL && *freePtr)                                  tstr++;
                 free(str);                          if (str != NULL && *freePtr)
             str = var_Error;                                  free(str);
             *freePtr = false;                          str = var_Error;
             break;                          *freePtr = false;
                           break;
                   }
                   if (DEBUG(VAR))
                           printf("Result is \"%s\"\n", str);
         }          }
         if (DEBUG(VAR))          if (*tstr == '\0')
             printf("Result is \"%s\"\n", str);                  Error("Unclosed variable specification");
     }          else
     if (*tstr == '\0')                  tstr++;
         Error("Unclosed variable specification");  
     else  
         tstr++;  
   
     *pscan = tstr;          *pscan = tstr;
     return str;          return str;
 }  }
   
 char *  char *
 Var_GetHead(char *s)  Var_GetHead(char *s)
 {  {
     return VarModify(s, VarHead, NULL);          return VarModify(s, VarHead, NULL);
 }  }
   
 char *  char *
 Var_GetTail(char *s)  Var_GetTail(char *s)
 {  {
     return VarModify(s, VarTail, NULL);          return VarModify(s, VarTail, NULL);
 }  }

Legend:
Removed from v.1.21  
changed lines
  Added in v.1.22