[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.65 and 1.66

version 1.65, 2007/07/20 12:18:47 version 1.66, 2007/07/20 12:32:45
Line 98 
Line 98 
  * identical string instances...   * identical string instances...
  */   */
 static char     varNoError[] = "";  static char     varNoError[] = "";
 bool            errorIsOkay;  bool            errorIsOkay;
 static bool     checkEnvFirst;  /* true if environment should be searched for  static bool     checkEnvFirst;  /* true if environment should be searched for
                                  * variables before the global context */                                   * variables before the global context */
   
 void  void
 Var_setCheckEnvFirst(bool yes)  Var_setCheckEnvFirst(bool yes)
Line 109 
Line 109 
 }  }
   
 /*  /*
  * Variable values are obtained from four different contexts:   * The rules for variable look-up are complicated.
  *      1) the process environment. The process environment itself   *
  *         may not be changed, but these variables may be modified,   * - Dynamic variables like $@ and $* are special. They always pertain to
  *         unless make is invoked with -e, in which case those variables   * a given variable.  In this implementation of make, it is an error to
  *         are unmodifiable and supersede the global context.   * try to affect them manually. They are stored in a local symtable directly
  *      2) the global context. Variables set in the Makefile are located in   * inside the gnode.
  *          the global context. It is the penultimate context searched when   *
  *          substituting.   * Global variables can be obtained:
  *      3) the command-line context. All variables set on the command line   * - from the command line
  *         are placed in this context. They are UNALTERABLE once placed here.   * - from the environment
  *      4) the local context. Each target has associated with it a context   * - from the Makefile proper.
  *         list. On this list are located the structures describing such   * All of these are stored in a hash global_variables.
  *         local variables as $(@) and $(*)   *
  * The four contexts are searched in the reverse order from which they are   * Variables set on the command line override Makefile contents, are
  * listed.   * passed to submakes (see Var_AddCmdLine), and are also exported to the
    * environment.
    *
    * Without -e (!checkEnvFirst), make will see variables set in the
    * Makefile, and default to the environment otherwise.
    *
    * With -e (checkEnvFirst), make will see the environment first, and that
    * will override anything that's set in the Makefile (but not set on
    * the command line).
    *
    * The SHELL variable is very special: it is never obtained from the
    * environment, and never passed to the environment.
  */   */
   
   /* definitions pertaining to dynamic variables */
   
   /* full names of dynamic variables */
 static char *varnames[] = {  static char *varnames[] = {
     TARGET,          TARGET,
     PREFIX,          PREFIX,
     ARCHIVE,          ARCHIVE,
     MEMBER,          MEMBER,
     OODATE,          OODATE,
     ALLSRC,          ALLSRC,
     IMPSRC,          IMPSRC,
     FTARGET,          FTARGET,
     DTARGET,          DTARGET,
     FPREFIX,          FPREFIX,
     DPREFIX,          DPREFIX,
     FARCHIVE,          FARCHIVE,
     DARCHIVE,          DARCHIVE,
     FMEMBER,          FMEMBER,
     DMEMBER          DMEMBER
     };  };
   
 /* retrieve the hashed values  for well-known variables.  */  /* hashed names of dynamic variables */
 #include    "varhashconsts.h"  #include    "varhashconsts.h"
   
 /* extended indices for System V stuff */  /* extended indices for System V stuff */
Line 157 
Line 171 
 #define FMEMBER_INDEX   13  #define FMEMBER_INDEX   13
 #define DMEMBER_INDEX   14  #define DMEMBER_INDEX   14
   
   #define GLOBAL_INDEX    -1
   
 #define EXTENDED2SIMPLE(i)      (((i)-LOCAL_SIZE)/2)  #define EXTENDED2SIMPLE(i)      (((i)-LOCAL_SIZE)/2)
 #define IS_EXTENDED_F(i)        ((i)%2 == 1)  #define IS_EXTENDED_F(i)        ((i)%2 == 1)
   
   
 static struct ohash global_variables;  static struct ohash global_variables;
   
   
 typedef struct Var_ {  typedef struct Var_ {
     BUFFER        val;          /* its value */          BUFFER val;             /* the variable value */
     unsigned int  flags;        /* miscellaneous status flags */          unsigned int flags;     /* miscellaneous status flags */
 #define VAR_IN_USE      1       /* Variable's value currently being used. */  #define VAR_IN_USE      1       /* Variable's value currently being used. */
                                 /* Used to avoid recursion */                                  /* (Used to avoid recursion) */
 #define VAR_DUMMY       2       /* Placeholder: already looked up */  #define VAR_DUMMY       2       /* Variable is currently just a name */
 #define VAR_FROM_CMD    4       /* From the command line */                                  /* In particular: BUFFER is invalid */
 #define VAR_FROM_ENV    8       /* Read from environment */  #define VAR_FROM_CMD    4       /* Special source: command line */
 #define VAR_SEEN_ENV    16      /* Already seen environment */  #define VAR_FROM_ENV    8       /* Special source: environment */
 #define VAR_SHELL       32      /* magic, see posix */  #define VAR_SEEN_ENV    16      /* No need to go look up environment again */
   #define VAR_SHELL       32      /* Magic behavior */
   
 #define POISONS (POISON_NORMAL | POISON_EMPTY | POISON_NOT_DEFINED)  #define POISONS (POISON_NORMAL | POISON_EMPTY | POISON_NOT_DEFINED)
     char          name[1];      /* the variable's name */                                  /* Defined in var.h */
           char name[1];           /* the variable's name */
 }  Var;  }  Var;
   
   
 static struct ohash_info var_info = {  static struct ohash_info var_info = {
         offsetof(Var, name),          offsetof(Var, name),
     NULL, hash_alloc, hash_free, element_alloc };          NULL,
 static int quick_lookup(const char *, const char **, uint32_t *);          hash_alloc, hash_free, element_alloc
 #define VarValue(v)     Buf_Retrieve(&((v)->val))  };
 static Var *varfind(const char *, const char *, SymTable *, int, uint32_t);  
 static Var *find_global_var(const char *, const char *, uint32_t);  
 static void VarDelete(Var *);  
 static void VarPrintVar(Var *);  
   
 static Var *obtain_global_var(const char *, const char *, uint32_t);  static int classify_var(const char *, const char **, uint32_t *);
   static Var *find_any_var(const char *, const char *, SymTable *, int, uint32_t);
   static Var *find_global_var(const char *, const char *, uint32_t);
   static Var *find_global_var_without_env(const char *, const char *, uint32_t);
 static void fill_from_env(Var *);  static void fill_from_env(Var *);
 static Var *create_var(const char *, const char *);  static Var *create_var(const char *, const char *);
   static void var_set_initial_value(Var *, const char *);
   static void var_set_value(Var *, const char *);
   #define var_get_value(v)        Buf_Retrieve(&((v)->val))
   static void var_append_value(Var *, const char *);
   static void poison_check(Var *);
 static void varq_set_append(int, const char *, GNode *, bool);  static void varq_set_append(int, const char *, GNode *, bool);
 static void var_init_string(Var *, const char *);  
 static void var_set_string(Var *, const char *);  
 static void var_append_string(Var *, const char *);  
 static void var_set_append(const char *, const char *, const char *, int, bool);  static void var_set_append(const char *, const char *, const char *, int, bool);
 static void set_magic_shell_variable(void);  static void set_magic_shell_variable(void);
 static void poison_check(Var *);  
 static const char *find_0(const char *);  static void delete_var(Var *);
   static void print_var(Var *);
   
   
 static const char *find_rparen(const char *);  static const char *find_rparen(const char *);
 static const char *find_ket(const char *);  static const char *find_ket(const char *);
 typedef const char * (*find_t)(const char *);  typedef const char * (*find_t)(const char *);
 static find_t find_pos(int);  static find_t find_pos(int);
   
   
   
   /* Variable lookup function: return idx for dynamic variable, or
    * GLOBAL_INDEX if name is not dynamic. Set up *pk for further use.
    */
 static int  static int
 quick_lookup(const char *name, const char **enamePtr, uint32_t *pk)  classify_var(const char *name, const char **enamePtr, uint32_t *pk)
 {  {
     size_t len;          size_t len;
   
     *pk = ohash_interval(name, enamePtr);          *pk = ohash_interval(name, enamePtr);
     len = *enamePtr - name;          len = *enamePtr - name;
         /* substitute short version for long local name */              /* substitute short version for long local name */
     switch (*pk % MAGICSLOTS1) {            /* MAGICSLOTS should be the    */          switch (*pk % MAGICSLOTS1) {    /* MAGICSLOTS should be the    */
     case K_LONGALLSRC % MAGICSLOTS1:        /* smallest constant yielding  */          case K_LONGALLSRC % MAGICSLOTS1:/* smallest constant yielding  */
                                             /* distinct case values        */                                          /* distinct case values    */
         if (*pk == K_LONGALLSRC && len == strlen(LONGALLSRC) &&                  if (*pk == K_LONGALLSRC && len == strlen(LONGALLSRC) &&
             strncmp(name, LONGALLSRC, len) == 0)                      strncmp(name, LONGALLSRC, len) == 0)
             return ALLSRC_INDEX;                          return ALLSRC_INDEX;
         break;                  break;
     case K_LONGARCHIVE % MAGICSLOTS1:          case K_LONGARCHIVE % MAGICSLOTS1:
         if (*pk == K_LONGARCHIVE && len == strlen(LONGARCHIVE) &&                  if (*pk == K_LONGARCHIVE && len == strlen(LONGARCHIVE) &&
             strncmp(name, LONGARCHIVE, len) == 0)                      strncmp(name, LONGARCHIVE, len) == 0)
             return ARCHIVE_INDEX;                          return ARCHIVE_INDEX;
         break;                  break;
     case K_LONGIMPSRC % MAGICSLOTS1:          case K_LONGIMPSRC % MAGICSLOTS1:
         if (*pk == K_LONGIMPSRC && len == strlen(LONGIMPSRC) &&                  if (*pk == K_LONGIMPSRC && len == strlen(LONGIMPSRC) &&
             strncmp(name, LONGIMPSRC, len) == 0)                      strncmp(name, LONGIMPSRC, len) == 0)
             return IMPSRC_INDEX;                          return IMPSRC_INDEX;
         break;                  break;
     case K_LONGMEMBER % MAGICSLOTS1:          case K_LONGMEMBER % MAGICSLOTS1:
         if (*pk == K_LONGMEMBER && len == strlen(LONGMEMBER) &&                  if (*pk == K_LONGMEMBER && len == strlen(LONGMEMBER) &&
             strncmp(name, LONGMEMBER, len) == 0)                      strncmp(name, LONGMEMBER, len) == 0)
             return MEMBER_INDEX;                          return MEMBER_INDEX;
         break;                  break;
     case K_LONGOODATE % MAGICSLOTS1:          case K_LONGOODATE % MAGICSLOTS1:
         if (*pk == K_LONGOODATE && len == strlen(LONGOODATE) &&                  if (*pk == K_LONGOODATE && len == strlen(LONGOODATE) &&
             strncmp(name, LONGOODATE, len) == 0)                      strncmp(name, LONGOODATE, len) == 0)
             return OODATE_INDEX;                          return OODATE_INDEX;
         break;                  break;
     case K_LONGPREFIX % MAGICSLOTS1:          case K_LONGPREFIX % MAGICSLOTS1:
         if (*pk == K_LONGPREFIX && len == strlen(LONGPREFIX) &&                  if (*pk == K_LONGPREFIX && len == strlen(LONGPREFIX) &&
             strncmp(name, LONGPREFIX, len) == 0)                      strncmp(name, LONGPREFIX, len) == 0)
             return PREFIX_INDEX;                          return PREFIX_INDEX;
         break;                  break;
     case K_LONGTARGET % MAGICSLOTS1:          case K_LONGTARGET % MAGICSLOTS1:
         if (*pk == K_LONGTARGET && len == strlen(LONGTARGET) &&                  if (*pk == K_LONGTARGET && len == strlen(LONGTARGET) &&
             strncmp(name, LONGTARGET, len) == 0)                      strncmp(name, LONGTARGET, len) == 0)
             return TARGET_INDEX;                          return TARGET_INDEX;
         break;                  break;
     case K_TARGET % MAGICSLOTS1:          case K_TARGET % MAGICSLOTS1:
         if (name[0] == TARGET[0] && len == 1)                  if (name[0] == TARGET[0] && len == 1)
             return TARGET_INDEX;                          return TARGET_INDEX;
         break;                  break;
     case K_OODATE % MAGICSLOTS1:          case K_OODATE % MAGICSLOTS1:
         if (name[0] == OODATE[0] && len == 1)                  if (name[0] == OODATE[0] && len == 1)
             return OODATE_INDEX;                          return OODATE_INDEX;
         break;                  break;
     case K_ALLSRC % MAGICSLOTS1:          case K_ALLSRC % MAGICSLOTS1:
         if (name[0] == ALLSRC[0] && len == 1)                  if (name[0] == ALLSRC[0] && len == 1)
             return ALLSRC_INDEX;                          return ALLSRC_INDEX;
         break;                  break;
     case K_IMPSRC % MAGICSLOTS1:          case K_IMPSRC % MAGICSLOTS1:
         if (name[0] == IMPSRC[0] && len == 1)                  if (name[0] == IMPSRC[0] && len == 1)
             return IMPSRC_INDEX;                          return IMPSRC_INDEX;
         break;                  break;
     case K_PREFIX % MAGICSLOTS1:          case K_PREFIX % MAGICSLOTS1:
         if (name[0] == PREFIX[0] && len == 1)                  if (name[0] == PREFIX[0] && len == 1)
             return PREFIX_INDEX;                          return PREFIX_INDEX;
         break;                  break;
     case K_ARCHIVE % MAGICSLOTS1:          case K_ARCHIVE % MAGICSLOTS1:
         if (name[0] == ARCHIVE[0] && len == 1)                  if (name[0] == ARCHIVE[0] && len == 1)
             return ARCHIVE_INDEX;                          return ARCHIVE_INDEX;
         break;                  break;
     case K_MEMBER % MAGICSLOTS1:          case K_MEMBER % MAGICSLOTS1:
         if (name[0] == MEMBER[0] && len == 1)                  if (name[0] == MEMBER[0] && len == 1)
             return MEMBER_INDEX;                          return MEMBER_INDEX;
         break;                  break;
     case K_FTARGET % MAGICSLOTS1:          case K_FTARGET % MAGICSLOTS1:
         if (name[0] == FTARGET[0] && name[1] == FTARGET[1] && len == 2)                  if (name[0] == FTARGET[0] && name[1] == FTARGET[1] && len == 2)
             return FTARGET_INDEX;                          return FTARGET_INDEX;
         break;                  break;
     case K_DTARGET % MAGICSLOTS1:          case K_DTARGET % MAGICSLOTS1:
         if (name[0] == DTARGET[0] && name[1] == DTARGET[1] && len == 2)                  if (name[0] == DTARGET[0] && name[1] == DTARGET[1] && len == 2)
             return DTARGET_INDEX;                          return DTARGET_INDEX;
         break;                  break;
     case K_FPREFIX % MAGICSLOTS1:          case K_FPREFIX % MAGICSLOTS1:
         if (name[0] == FPREFIX[0] && name[1] == FPREFIX[1] && len == 2)                  if (name[0] == FPREFIX[0] && name[1] == FPREFIX[1] && len == 2)
             return FPREFIX_INDEX;                          return FPREFIX_INDEX;
         break;                  break;
     case K_DPREFIX % MAGICSLOTS1:          case K_DPREFIX % MAGICSLOTS1:
         if (name[0] == DPREFIX[0] && name[1] == DPREFIX[1] && len == 2)                  if (name[0] == DPREFIX[0] && name[1] == DPREFIX[1] && len == 2)
             return DPREFIX_INDEX;                          return DPREFIX_INDEX;
         break;                  break;
     case K_FARCHIVE % MAGICSLOTS1:          case K_FARCHIVE % MAGICSLOTS1:
         if (name[0] == FARCHIVE[0] && name[1] == FARCHIVE[1] && len == 2)                  if (name[0] == FARCHIVE[0] && name[1] == FARCHIVE[1] &&
             return FARCHIVE_INDEX;                      len == 2)
         break;                          return FARCHIVE_INDEX;
     case K_DARCHIVE % MAGICSLOTS1:                  break;
         if (name[0] == DARCHIVE[0] && name[1] == DARCHIVE[1] && len == 2)          case K_DARCHIVE % MAGICSLOTS1:
             return DARCHIVE_INDEX;                  if (name[0] == DARCHIVE[0] && name[1] == DARCHIVE[1] &&
         break;                      len == 2)
     case K_FMEMBER % MAGICSLOTS1:                          return DARCHIVE_INDEX;
         if (name[0] == FMEMBER[0] && name[1] == FMEMBER[1] && len == 2)                  break;
             return FMEMBER_INDEX;          case K_FMEMBER % MAGICSLOTS1:
         break;                  if (name[0] == FMEMBER[0] && name[1] == FMEMBER[1] && len == 2)
     case K_DMEMBER % MAGICSLOTS1:                          return FMEMBER_INDEX;
         if (name[0] == DMEMBER[0] && name[1] == DMEMBER[1] && len == 2)                  break;
             return DMEMBER_INDEX;          case K_DMEMBER % MAGICSLOTS1:
         break;                  if (name[0] == DMEMBER[0] && name[1] == DMEMBER[1] && len == 2)
     default:                      return DMEMBER_INDEX;
         break;                  break;
     }          default:
     return -1;                  break;
           }
           return GLOBAL_INDEX;
 }  }
   
   
   /***
    ***    Internal handling of variables.
    ***/
   
   
   /* Create a new variable, does not initialize anything except the name.
    * in particular, buffer is invalid, and flag value is invalid. Accordingly,
    * must either:
    * - set flags to VAR_DUMMY
    * - set flags to !VAR_DUMMY, and initialize buffer, for instance with
    * var_set_initial_value().
    */
 static Var *  static Var *
 create_var(const char *name, const char *ename)  create_var(const char *name, const char *ename)
 {  {
     return ohash_create_entry(&var_info, name, &ename);          return ohash_create_entry(&var_info, name, &ename);
 }  }
   
 /* Set the initial value a var should have */  /* Initial version of var_set_value(), to be called after create_var().
    */
 static void  static void
 var_init_string(Var *v, const char *val)  var_set_initial_value(Var *v, const char *val)
 {  {
     size_t len;          size_t len;
   
     len = strlen(val);          len = strlen(val);
     Buf_Init(&(v->val), len+1);          Buf_Init(&(v->val), len+1);
     Buf_AddChars(&(v->val), len, val);          Buf_AddChars(&(v->val), len, val);
 }  }
   
   /* Normal version of var_set_value(), to be called after variable is fully
    * initialized.
    */
 static void  static void
 var_set_string(Var *v, const char *val)  var_set_value(Var *v, const char *val)
 {  {
     if ((v->flags & VAR_DUMMY) == 0) {          if ((v->flags & VAR_DUMMY) == 0) {
         Buf_Reset(&(v->val));                  Buf_Reset(&(v->val));
         Buf_AddString(&(v->val), val);                  Buf_AddString(&(v->val), val);
     } else {          } else {
         var_init_string(v, val);                  var_set_initial_value(v, val);
         v->flags &= ~VAR_DUMMY;                  v->flags &= ~VAR_DUMMY;
     }          }
 }  }
   
   /* Add to a variable, insert a separating space if the variable was already
    * defined.
    */
 static void  static void
 var_append_string(Var *v, const char *val)  var_append_value(Var *v, const char *val)
 {  {
     if ((v->flags & VAR_DUMMY) == 0) {          if ((v->flags & VAR_DUMMY) == 0) {
         Buf_AddSpace(&(v->val));                  Buf_AddSpace(&(v->val));
         Buf_AddString(&(v->val), val);                  Buf_AddString(&(v->val), val);
     } else {          } else {
         var_init_string(v, val);                  var_set_initial_value(v, val);
         v->flags &= ~VAR_DUMMY;                  v->flags &= ~VAR_DUMMY;
     }          }
 }  }
   
 /*-  
  *-----------------------------------------------------------------------  /* Delete a variable and all the space associated with it.
  * VarDelete  --  
  *      Delete a variable and all the space associated with it.  
  *-----------------------------------------------------------------------  
  */   */
 static void  static void
 VarDelete(Var *v)  delete_var(Var *v)
 {  {
     if ((v->flags & VAR_DUMMY) == 0)          if ((v->flags & VAR_DUMMY) == 0)
         Buf_Destroy(&(v->val));                  Buf_Destroy(&(v->val));
     free(v);          free(v);
 }  }
   
   
   
   
   /***
    ***    Dynamic variable handling.
    ***/
   
   
   
   /* create empty symtable.
    * XXX: to save space, dynamic variables may be NULL pointers.
    */
 void  void
 SymTable_Init(SymTable *ctxt)  SymTable_Init(SymTable *ctxt)
 {  {
     static SymTable sym_template;          static SymTable sym_template;
     memcpy(ctxt, &sym_template, sizeof(*ctxt));          memcpy(ctxt, &sym_template, sizeof(*ctxt));
 }  }
   
   /* free symtable.
    */
 #ifdef CLEANUP  #ifdef CLEANUP
 void  void
 SymTable_Destroy(SymTable *ctxt)  SymTable_Destroy(SymTable *ctxt)
 {  {
     int i;          int i;
   
     for (i = 0; i < LOCAL_SIZE; i++)          for (i = 0; i < LOCAL_SIZE; i++)
         if (ctxt->locals[i] != NULL)                  if (ctxt->locals[i] != NULL)
             VarDelete(ctxt->locals[i]);                          delete_var(ctxt->locals[i]);
 }  }
 #endif  #endif
   
   /* set or append to dynamic variable.
    */
 static void  static void
 varq_set_append(int idx, const char *val, GNode *gn, bool append)  varq_set_append(int idx, const char *val, GNode *gn, bool append)
 {  {
     Var *v = gn->context.locals[idx];          Var *v = gn->context.locals[idx];
   
     if (v == NULL) {          if (v == NULL) {
         v = create_var(varnames[idx], NULL);                  v = create_var(varnames[idx], NULL);
 #ifdef STATS_VAR_LOOKUP  #ifdef STATS_VAR_LOOKUP
         STAT_VAR_CREATION++;                  STAT_VAR_CREATION++;
 #endif  #endif
         if (val != NULL)                  if (val != NULL)
             var_init_string(v, val);                          var_set_initial_value(v, val);
         else                  else
             Buf_Init(&(v->val), 1);                          Buf_Init(&(v->val), 1);
         v->flags = 0;                  v->flags = 0;
         gn->context.locals[idx] = v;                  gn->context.locals[idx] = v;
     } else {          } else {
         if (append)                  if (append)
                 Buf_AddSpace(&(v->val));                          Buf_AddSpace(&(v->val));
         else                  else
                 Buf_Reset(&(v->val));                          Buf_Reset(&(v->val));
         Buf_AddString(&(v->val), val);                  Buf_AddString(&(v->val), val);
     }          }
     if (DEBUG(VAR))          if (DEBUG(VAR))
         printf("%s:%s = %s\n", gn->name, varnames[idx], VarValue(v));                  printf("%s:%s = %s\n", gn->name, varnames[idx],
                       var_get_value(v));
 }  }
   
 void  void
 Varq_Set(int idx, const char *val, GNode *gn)  Varq_Set(int idx, const char *val, GNode *gn)
 {  {
     varq_set_append(idx, val, gn, false);          varq_set_append(idx, val, gn, false);
 }  }
   
 void  void
 Varq_Append(int idx, const char *val, GNode *gn)  Varq_Append(int idx, const char *val, GNode *gn)
 {  {
     varq_set_append(idx, val, gn, true);          varq_set_append(idx, val, gn, true);
 }  }
   
 char *  char *
 Varq_Value(int idx, GNode *gn)  Varq_Value(int idx, GNode *gn)
 {  {
     Var *v = gn->context.locals[idx];          Var *v = gn->context.locals[idx];
   
     if (v == NULL)          if (v == NULL)
         return NULL;                  return NULL;
     else          else
         return VarValue(v);                  return var_get_value(v);
 }  }
   
   /***
    ***    Global variable handling.
    ***/
   
   /* Create a new global var if necessary, and set it up correctly.
    * Do not take environment into account.
    */
 static Var *  static Var *
 obtain_global_var(const char *name, const char *ename, uint32_t k)  find_global_var_without_env(const char *name, const char *ename, uint32_t k)
 {  {
         unsigned int slot;          unsigned int slot;
         Var *v;          Var *v;
Line 456 
Line 527 
         return v;          return v;
 }  }
   
   /* Helper for find_global_var(): grab environment value if needed.
    */
 static void  static void
 fill_from_env(Var *v)  fill_from_env(Var *v)
 {  {
     char        *env;          char    *env;
   
     env = getenv(v->name);          env = getenv(v->name);
     if (env == NULL)          if (env == NULL)
         v->flags |= VAR_SEEN_ENV;                  v->flags |= VAR_SEEN_ENV;
     else {          else {
         var_set_string(v, env);                  var_set_value(v, env);
         v->flags |= VAR_FROM_ENV | VAR_SEEN_ENV;                  v->flags |= VAR_FROM_ENV | VAR_SEEN_ENV;
     }          }
   
 #ifdef STATS_VAR_LOOKUP  #ifdef STATS_VAR_LOOKUP
     STAT_VAR_FROM_ENV++;          STAT_VAR_FROM_ENV++;
 #endif  #endif
 }  }
   
   /* Find global var, and obtain its value from the environment if needed.
    */
 static Var *  static Var *
 find_global_var(const char *name, const char *ename, uint32_t k)  find_global_var(const char *name, const char *ename, uint32_t k)
 {  {
     Var                 *v;          Var *v;
   
     v = obtain_global_var(name, ename, k);          v = find_global_var_without_env(name, ename, k);
   
     if ((v->flags & VAR_SEEN_ENV) == 0 &&          if ((v->flags & VAR_SEEN_ENV) == 0 &&
         (checkEnvFirst  && (v->flags & VAR_FROM_CMD) == 0 ||              (checkEnvFirst  && (v->flags & VAR_FROM_CMD) == 0 ||
             (v->flags & VAR_DUMMY) != 0))                  (v->flags & VAR_DUMMY) != 0))
                 fill_from_env(v);                      fill_from_env(v);
   
     return v;          return v;
 }  }
   
   /* mark variable as poisoned, in a given setup.
    */
 void  void
 Var_MarkPoisoned(const char *name, const char *ename, unsigned int type)  Var_MarkPoisoned(const char *name, const char *ename, unsigned int type)
 {  {
         Var   *v;          Var   *v;
         uint32_t        k;          uint32_t        k;
         int             idx;          int             idx;
         idx = quick_lookup(name, &ename, &k);          idx = classify_var(name, &ename, &k);
   
         if (idx != -1) {          if (idx != GLOBAL_INDEX) {
                 Parse_Error(PARSE_FATAL,                  Parse_Error(PARSE_FATAL,
                     "Trying to poison dynamic variable $%s",                      "Trying to poison dynamic variable $%s",
                     varnames[idx]);                      varnames[idx]);
                 return;                  return;
Line 507 
Line 583 
   
         v = find_global_var(name, ename, k);          v = find_global_var(name, ename, k);
         v->flags |= type;          v->flags |= type;
           /* POISON_NORMAL is not lazy: if the variable already exists in
            * the Makefile, then it's a mistake.
            */
         if (v->flags & POISON_NORMAL) {          if (v->flags & POISON_NORMAL) {
                 if (v->flags & VAR_DUMMY)                  if (v->flags & VAR_DUMMY)
                         return;                          return;
Line 517 
Line 596 
         }          }
 }  }
   
   /* Check if there's any reason not to use the variable in this context.
    */
 static void  static void
 poison_check(Var *v)  poison_check(Var *v)
 {  {
         if (v->flags & POISON_NORMAL) {          if (v->flags & POISON_NORMAL) {
                 Parse_Error(PARSE_FATAL,                  Parse_Error(PARSE_FATAL,
                     "Poisoned variable %s has been referenced\n", v->name);                      "Poisoned variable %s has been referenced\n", v->name);
                 return;                  return;
         }          }
Line 531 
Line 612 
                 return;                  return;
         }          }
         if (v->flags & POISON_EMPTY)          if (v->flags & POISON_EMPTY)
                 if (strcmp(VarValue(v), "") == 0)                  if (strcmp(var_get_value(v), "") == 0)
                         Parse_Error(PARSE_FATAL,                          Parse_Error(PARSE_FATAL,
                             "Poisoned variable %s is empty\n", v->name);                              "Poisoned variable %s is empty\n", v->name);
 }  }
   
   /* Delete global variable.
    */
 void  void
 Var_Delete(const char *name)  Var_Deletei(const char *name, const char *ename)
 {  {
         Var     *v;          Var *v;
         uint32_t        k;          uint32_t k;
         unsigned int slot;          unsigned int slot;
         const char      *ename = NULL;          int idx;
         int             idx;  
   
           idx = classify_var(name, &ename, &k);
         if (DEBUG(VAR))          if (idx != GLOBAL_INDEX) {
                 printf("delete %s\n", name);                  Parse_Error(PARSE_FATAL,
                       "Trying to delete dynamic variable $%s", varnames[idx]);
         idx = quick_lookup(name, &ename, &k);                  return;
         if (idx != -1)          }
                 Parse_Error(PARSE_FATAL, "Trying to delete dynamic variable");  
         slot = ohash_lookup_interval(&global_variables, name, ename, k);          slot = ohash_lookup_interval(&global_variables, name, ename, k);
         v = ohash_find(&global_variables, slot);          v = ohash_find(&global_variables, slot);
   
         if (v == NULL)          if (v == NULL)
                 return;                  return;
   
         if (checkEnvFirst && (v->flags & VAR_FROM_ENV))          if (checkEnvFirst && (v->flags & VAR_FROM_ENV))
                 return;                  return;
   
Line 564 
Line 646 
                 return;                  return;
   
         ohash_remove(&global_variables, slot);          ohash_remove(&global_variables, slot);
         VarDelete(v);          delete_var(v);
 }  }
   
   /* Set or add a global variable, in VAR_CMD or VAR_GLOBAL context.
    */
 static void  static void
 var_set_append(const char *name, const char *ename, const char *val, int ctxt,  var_set_append(const char *name, const char *ename, const char *val, int ctxt,
     bool append)      bool append)
 {  {
         Var   *v;          Var *v;
         uint32_t        k;          uint32_t k;
         int             idx;          int idx;
   
         idx = quick_lookup(name, &ename, &k);          idx = classify_var(name, &ename, &k);
         if (idx != -1) {          if (idx != GLOBAL_INDEX) {
                 Parse_Error(PARSE_FATAL, "Trying to %s dynamic variable $%s",                  Parse_Error(PARSE_FATAL, "Trying to %s dynamic variable $%s",
                     append ? "append to" : "set", varnames[idx]);                      append ? "append to" : "set", varnames[idx]);
                 return;                  return;
Line 587 
Line 671 
                 Parse_Error(PARSE_FATAL, "Trying to %s poisoned variable %s\n",                  Parse_Error(PARSE_FATAL, "Trying to %s poisoned variable %s\n",
                     append ? "append to" : "set", v->name);                      append ? "append to" : "set", v->name);
         /* so can we write to it ? */          /* so can we write to it ? */
         if (ctxt == VAR_CMD) {  /* always for command line */          if (ctxt == VAR_CMD) {  /* always for command line */
                 (append ? var_append_string : var_set_string)(v, val);                  (append ? var_append_value : var_set_value)(v, val);
                 v->flags |= VAR_FROM_CMD;                  v->flags |= VAR_FROM_CMD;
                 if ((v->flags & VAR_SHELL) == 0) {                  if ((v->flags & VAR_SHELL) == 0) {
                         /* Any variables given on the command line are                          /* Any variables given on the command line are
                          * automatically exported to the environment,                           * automatically exported to the environment,
                          * except for SHELL (as per POSIX standard).                           * except for SHELL (as per POSIX standard).
                          */                           */
                         esetenv(v->name, val);                          esetenv(v->name, val);
                 }                  }
                 if (DEBUG(VAR))                  if (DEBUG(VAR))
                         printf("command:%s = %s\n", v->name, VarValue(v));                          printf("command:%s = %s\n", v->name, var_get_value(v));
         } else if ((v->flags & VAR_FROM_CMD) == 0 &&          } else if ((v->flags & VAR_FROM_CMD) == 0 &&
              (!checkEnvFirst || (v->flags & VAR_FROM_ENV) == 0)) {               (!checkEnvFirst || (v->flags & VAR_FROM_ENV) == 0)) {
                 (append ? var_append_string : var_set_string)(v, val);                  (append ? var_append_value : var_set_value)(v, val);
                 if (DEBUG(VAR))                  if (DEBUG(VAR))
                         printf("global:%s = %s\n", v->name, VarValue(v));                          printf("global:%s = %s\n", v->name, var_get_value(v));
         } else if (DEBUG(VAR))          } else if (DEBUG(VAR))
                         printf("overriden:%s = %s\n", v->name, VarValue(v));                  printf("overriden:%s = %s\n", v->name, var_get_value(v));
 }  }
   
 void  void
Line 620 
Line 704 
         var_set_append(name, ename, val, ctxt, true);          var_set_append(name, ename, val, ctxt, true);
 }  }
   
   /* XXX different semantics for Var_Valuei() and Var_Definedi():
    * references to poisoned value variables will error out in Var_Valuei(),
    * but not in Var_Definedi(), so the following construct works:
    *      .poison BINDIR
    *      BINDIR ?= /usr/bin
    */
 char *  char *
 Var_Valuei(const char *name, const char *ename)  Var_Valuei(const char *name, const char *ename)
 {  {
         Var        *v;          Var *v;
         uint32_t                k;          uint32_t k;
         int             idx;          int idx;
   
         idx = quick_lookup(name, &ename, &k);          idx = classify_var(name, &ename, &k);
         if (idx == -1) {          if (idx != GLOBAL_INDEX) {
                 v = find_global_var(name, ename, k);                  Parse_Error(PARSE_FATAL,
                 if (v->flags & POISONS)                      "Trying to get value of dynamic variable $%s",
                     poison_check(v);                          varnames[idx]);
                 if ((v->flags & VAR_DUMMY) == 0)                  return NULL;
                         return VarValue(v);  
         }          }
           v = find_global_var(name, ename, k);
         return NULL;          if (v->flags & POISONS)
                   poison_check(v);
           if ((v->flags & VAR_DUMMY) == 0)
                   return var_get_value(v);
           else
                   return NULL;
 }  }
   
 bool  bool
 Var_Definedi(const char *name, const char *ename)  Var_Definedi(const char *name, const char *ename)
 {  {
         Var             *v;          Var *v;
         uint32_t        k;          uint32_t k;
         int             idx;          int idx;
   
         idx = quick_lookup(name, &ename, &k);          idx = classify_var(name, &ename, &k);
         if (idx == -1) {          /* We don't bother writing an error message for dynamic variables,
            * these will be caught when getting set later, usually.
            */
           if (idx == GLOBAL_INDEX) {
                 v = find_global_var(name, ename, k);                  v = find_global_var(name, ename, k);
                 if (v->flags & POISON_NORMAL)                  if (v->flags & POISON_NORMAL)
                     poison_check(v);                          poison_check(v);
                 if ((v->flags & VAR_DUMMY) == 0)                  if ((v->flags & VAR_DUMMY) == 0)
                         return true;                          return true;
         }          }
   
         return false;          return false;
 }  }
   
   
   /***
    ***    Substitution functions, handling both global and dynamic variables.
    ***/
   
   
   /* XXX contrary to find_global_var(), find_any_var() can return NULL pointers.
    */
 static Var *  static Var *
 varfind(const char *name, const char *ename, SymTable *ctxt,  find_any_var(const char *name, const char *ename, SymTable *ctxt,
     int idx, uint32_t k)      int idx, uint32_t k)
 {  {
     /* Handle local variables first */          /* Handle local variables first */
     if (idx != -1) {          if (idx != GLOBAL_INDEX) {
         if (ctxt != NULL) {                  if (ctxt != NULL) {
                 if (idx < LOCAL_SIZE)                          if (idx < LOCAL_SIZE)
                     return ctxt->locals[idx];                                  return ctxt->locals[idx];
                 else                          else
                     return ctxt->locals[EXTENDED2SIMPLE(idx)];                                  return ctxt->locals[EXTENDED2SIMPLE(idx)];
         } else                  } else
                 return NULL;                          return NULL;
     } else {          } else {
         return find_global_var(name, ename, k);                  return find_global_var(name, ename, k);
     }          }
 }  }
   
 static const char *  /* All the scanning functions needed to account for all the forms of
 find_0(const char *p)   * variable names that exist:
 {   *      $A, ${AB}, $(ABC), ${A:mod}, $(A:mod)
         while (*p != '$' && *p != '\0' && *p != ':')   */
                 p++;  
         return p;  
 }  
   
 static const char *  static const char *
 find_rparen(const char *p)  find_rparen(const char *p)
Line 700 
Line 801 
         return p;          return p;
 }  }
   
   /* Figure out what kind of name we're looking for from a start character.
    */
 static find_t  static find_t
 find_pos(int c)  find_pos(int c)
 {  {
         switch(c) {          switch(c) {
         case '\0':          case '(':
                 return find_0;  
         case ')':  
                 return find_rparen;                  return find_rparen;
         case '}':          case '{':
                 return find_ket;                  return find_ket;
         default:          default:
                 return 0;                  Parse_Error(PARSE_FATAL,
                       "Wrong character in variable spec %c (can't happen)");
                   return find_rparen;
         }          }
 }  }
   
 size_t  size_t
 Var_ParseSkip(const char *str, SymTable *ctxt, bool *result)  Var_ParseSkip(const char *str, SymTable *ctxt, bool *result)
 {  {
     const char  *tstr;          /* Pointer into str */          const char *tstr;       /* Pointer into str */
     Var         *v;             /* Variable in invocation */          Var *v;                 /* Variable in invocation */
     char        endc;           /* Ending character when variable in parens          char paren;             /* Parenthesis or brace or nothing */
                                  * or braces */          const char *start;
     const char  *start;          size_t length;
     size_t      length;          struct Name name;
     struct Name name;  
   
     v = NULL;          v = NULL;
     start = str;          start = str;
     str++;  
   
     if (*str != '(' && *str != '{') {  
         name.tofree = false;  
         tstr = str + 1;  
         length = 2;  
         endc = '\0';  
     } else {  
         endc = *str == '(' ? ')' : '}';  
         str++;          str++;
   
         /* Find eventual modifiers in the variable */          if (*str != '(' && *str != '{') {
         tstr = VarName_Get(str, &name, ctxt, false, find_pos(endc));                  name.tofree = false;
         VarName_Free(&name);                  tstr = str + 1;
         length = tstr - start;                  length = 2;
         if (*tstr != 0)                  paren = '\0';
             length++;          } else {
     }                  paren = *str;
                   str++;
   
     if (result != NULL)                  /* Find eventual modifiers in the variable */
         *result = true;                  tstr = VarName_Get(str, &name, ctxt, false, find_pos(paren));
     if (*tstr == ':' && endc != '\0')                  VarName_Free(&name);
          if (VarModifiers_Apply(NULL, NULL, ctxt, true, NULL, tstr, endc,                  length = tstr - start;
             &length) == var_Error)                  if (*tstr != 0)
                 if (result != NULL)                          length++;
                     *result = false;          }
     return length;  
           if (result != NULL)
                   *result = true;
           if (*tstr == ':' && paren != '\0')
                    if (VarModifiers_Apply(NULL, NULL, ctxt, true, NULL, tstr,
                       paren, &length) == var_Error)
                           if (result != NULL)
                                   *result = false;
           return length;
 }  }
   
 /* As of now, Var_ParseBuffer is just a wrapper around Var_Parse. For  /* As of now, Var_ParseBuffer is just a wrapper around Var_Parse. For
  * speed, it may be better to revisit the implementation to do things   * speed, it may be better to revisit the implementation to do things
  * directly. */   * directly. */
 bool  bool
 Var_ParseBuffer(Buffer buf, const char *str, SymTable *ctxt, bool err,  Var_ParseBuffer(Buffer buf, const char *str, SymTable *ctxt, bool err,
     size_t *lengthPtr)      size_t *lengthPtr)
 {  {
     char        *result;          char *result;
     bool        freeIt;          bool freeIt;
   
     result = Var_Parse(str, ctxt, err, lengthPtr, &freeIt);          result = Var_Parse(str, ctxt, err, lengthPtr, &freeIt);
     if (result == var_Error)          if (result == var_Error)
         return false;                  return false;
   
     Buf_AddString(buf, result);          Buf_AddString(buf, result);
     if (freeIt)          if (freeIt)
         free(result);                  free(result);
     return true;          return true;
 }  }
   
 char *  char *
 Var_Parse(const char *str,      /* The string to parse */  Var_Parse(const char *str,      /* The string to parse */
     SymTable *ctxt,             /* The context for the variable */      SymTable *ctxt,             /* The context for the variable */
     bool err,                   /* true if undefined variables are an error */      bool err,                   /* true if undefined variables are an error */
     size_t *lengthPtr,          /* OUT: The length of the specification */      size_t *lengthPtr,          /* OUT: The length of the specification */
     bool *freePtr)              /* OUT: true if caller should free result */      bool *freePtr)              /* OUT: true if caller should free result */
 {  {
     const char  *tstr;          /* Pointer into str */          const char *tstr;       /* Pointer into str */
     Var         *v;             /* Variable in invocation */          Var *v;                 /* Variable in invocation */
     char        endc;           /* Ending character when variable in parens          char paren;             /* Parenthesis or brace or nothing */
                                  * or braces */          struct Name name;
     struct Name name;          const char *start;
     const char  *start;          char *val;              /* Variable value  */
     char        *val;           /* Variable value  */          uint32_t k;
     uint32_t    k;          int idx;
     int         idx;  
   
     *freePtr = false;          *freePtr = false;
     start = str++;          start = str++;
   
     val = NULL;          val = NULL;
     v = NULL;          v = NULL;
     idx = -1;          idx = GLOBAL_INDEX;
   
     if (*str != '(' && *str != '{') {          if (*str != '(' && *str != '{') {
         name.s = str;                  name.s = str;
         name.e = str+1;                  name.e = str+1;
         name.tofree = false;                  name.tofree = false;
         tstr = str + 1;                  tstr = str + 1;
         *lengthPtr = 2;                  *lengthPtr = 2;
         endc = '\0';                  paren = '\0';
     } else {          } else {
         endc = *str == '(' ? ')' : '}';                  paren = *str;
         str++;                  str++;
   
         /* Find eventual modifiers in the variable */                  /* Find eventual modifiers in the variable */
         tstr = VarName_Get(str, &name, ctxt, false, find_pos(endc));                  tstr = VarName_Get(str, &name, ctxt, false, find_pos(paren));
         *lengthPtr = tstr - start;                  *lengthPtr = tstr - start;
         if (*tstr != '\0')                  if (*tstr != '\0')
                 (*lengthPtr)++;                          (*lengthPtr)++;
     }          }
   
     idx = quick_lookup(name.s, &name.e, &k);          idx = classify_var(name.s, &name.e, &k);
     v = varfind(name.s, name.e, ctxt, idx, k);          v = find_any_var(name.s, name.e, ctxt, idx, k);
     if (v != NULL && (v->flags & POISONS) != 0)          if (v != NULL && (v->flags & POISONS) != 0)
         poison_check(v);                  poison_check(v);
     if (v != NULL && (v->flags & VAR_DUMMY) == 0) {          if (v != NULL && (v->flags & VAR_DUMMY) == 0) {
         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
          * has been fully expanded. If it looks like recursion might be                   * value has been fully expanded. If it looks like recursion
          * necessary (there's a dollar sign somewhere in the variable's value)                   * might be necessary (there's a dollar sign somewhere in
          * we just call Var_Subst to do any other substitutions that are                   * the variable's value) we just call Var_Subst to do any
          * necessary. Note that the value returned by Var_Subst will have                   * other substitutions that are necessary. Note that the
          * been dynamically-allocated, so it will need freeing when we                   * value returned by Var_Subst will have been dynamically
          * return.  */                   * allocated, so it will need freeing when we return.
         val = VarValue(v);                   */
         if (idx == -1) {                  val = var_get_value(v);
             if (strchr(val, '$') != NULL) {                  if (idx == GLOBAL_INDEX) {
                 val = Var_Subst(val, ctxt, err);                          if (strchr(val, '$') != NULL) {
                 *freePtr = true;                                  val = Var_Subst(val, ctxt, err);
             }                                  *freePtr = true;
         } else if (idx >= LOCAL_SIZE) {                          }
             if (IS_EXTENDED_F(idx))                  } else if (idx >= LOCAL_SIZE) {
                 val = Var_GetTail(val);                          if (IS_EXTENDED_F(idx))
             else                                  val = Var_GetTail(val);
                 val = Var_GetHead(val);                          else
             *freePtr = true;                                  val = Var_GetHead(val);
                           *freePtr = true;
                   }
                   v->flags &= ~VAR_IN_USE;
         }          }
         v->flags &= ~VAR_IN_USE;          if (*tstr == ':' && paren != '\0')
     }                  val = VarModifiers_Apply(val, &name, ctxt, err, freePtr,
     if (*tstr == ':' && endc != '\0')                      tstr, paren, lengthPtr);
         val = VarModifiers_Apply(val, &name, ctxt, err, freePtr, tstr, endc,          if (val == NULL) {
             lengthPtr);                  val = err ? var_Error : varNoError;
     if (val == NULL) {                  /* Dynamic source */
         val = err ? var_Error : varNoError;                  if (idx != GLOBAL_INDEX) {
         /* Dynamic source */                          /* can't be expanded for now: copy the spec instead. */
         if (idx != -1) {                          if (ctxt == NULL) {
             /* can't be expanded for now: copy the var spec instead. */                                  *freePtr = true;
             if (ctxt == NULL) {                                  val = Str_dupi(start, start+ *lengthPtr);
                 *freePtr = true;                          } else {
                 val = Str_dupi(start, start+ *lengthPtr);                          /* somehow, this should have been expanded already. */
             } else {                                  GNode *n;
             /* somehow, this should have been expanded already. */  
                 GNode *n;  
   
                 n = (GNode *)(((char *)ctxt) - offsetof(GNode, context));                                  /* XXX */
                 if (idx >= LOCAL_SIZE)                                  n = (GNode *)(((char *)ctxt) -
                         idx = EXTENDED2SIMPLE(idx);                                      offsetof(GNode, context));
                 switch(idx) {                                  if (idx >= LOCAL_SIZE)
                 case IMPSRC_INDEX:                                          idx = EXTENDED2SIMPLE(idx);
                     Fatal("Using $< in a non-suffix rule context is a GNUmake idiom (line %lu of %s)",                                  switch(idx) {
                         n->lineno, n->fname);                                  case IMPSRC_INDEX:
                 default:                                          Fatal(
                     Error("Using undefined dynamic variable $%s (line %lu of %s)",  "Using $< in a non-suffix rule context is a GNUmake idiom (line %lu of %s)",
                         varnames[idx], n->lineno, n->fname);                                              n->lineno, n->fname);
                     break;                                  default:
                                           Error(
   "Using undefined dynamic variable $%s (line %lu of %s)",
                                               varnames[idx], n->lineno, n->fname);
                                           break;
                                   }
                           }
                 }                  }
             }  
         }          }
     }          VarName_Free(&name);
     VarName_Free(&name);          return val;
     return val;  
 }  }
   
   
 char *  char *
 Var_Subst(const char *str,      /* the string in which to substitute */  Var_Subst(const char *str,      /* the string in which to substitute */
     SymTable *ctxt,             /* the context wherein to find variables */      SymTable *ctxt,             /* the context wherein to find variables */
     bool undefErr)              /* true if undefineds are an error */      bool undefErr)              /* true if undefineds are an error */
 {  {
     BUFFER        buf;          /* Buffer for forming things */          BUFFER buf;             /* Buffer for forming things */
     static bool errorReported;  /* Set true if an error has already          static bool errorReported;
                                  * been reported to prevent a plethora  
                                  * of messages when recursing */  
   
     Buf_Init(&buf, MAKE_BSIZE);          Buf_Init(&buf, MAKE_BSIZE);
     errorReported = false;          errorReported = false;
   
     for (;;) {          for (;;) {
         char            *val;   /* Value to substitute for a variable */                  char *val;      /* Value to substitute for a variable */
         size_t          length; /* Length of the variable invocation */                  size_t length;  /* Length of the variable invocation */
         bool    doFree;         /* Set true if val should be freed */                  bool doFree;    /* Set true if val should be freed */
         const char *cp;                  const char *cp;
   
         /* copy uninteresting stuff */                  /* copy uninteresting stuff */
         for (cp = str; *str != '\0' && *str != '$'; str++)                  for (cp = str; *str != '\0' && *str != '$'; str++)
             ;                          ;
         Buf_Addi(&buf, cp, str);                  Buf_Addi(&buf, cp, str);
         if (*str == '\0')                  if (*str == '\0')
             break;                          break;
         if (str[1] == '$') {                  if (str[1] == '$') {
             /* A dollar sign may be escaped with another dollar sign.  */                          /* A $ may be escaped with another $. */
             Buf_AddChar(&buf, '$');                          Buf_AddChar(&buf, '$');
             str += 2;                          str += 2;
             continue;                          continue;
         }                  }
         val = Var_Parse(str, ctxt, undefErr, &length, &doFree);                  val = Var_Parse(str, ctxt, undefErr, &length, &doFree);
         /* When we come down here, val should either point to the                  /* When we come down here, val should either point to the
          * value of this variable, suitably modified, or be NULL.                   * value of this variable, suitably modified, or be NULL.
          * Length should be the total length of the potential                   * Length should be the total length of the potential
          * variable invocation (from $ to end character...) */                   * variable invocation (from $ to end character...) */
         if (val == var_Error || val == varNoError) {                  if (val == var_Error || val == varNoError) {
             /* If performing old-time variable substitution, skip over                          /* If errors are not an issue, skip over the variable
              * the variable and continue with the substitution. Otherwise,                           * and continue with the substitution. Otherwise, store
              * store the dollar sign and advance str so we continue with                           * the dollar sign and advance str so we continue with
              * the string...  */                           * the string...  */
             if (errorIsOkay)                          if (errorIsOkay)
                 str += length;                                  str += length;
             else if (undefErr) {                          else if (undefErr) {
                 /* If variable is undefined, complain and skip the                                  /* If variable is undefined, complain and
                  * variable. The complaint will stop us from doing anything                                   * skip the variable name. The complaint
                  * when the file is parsed.  */                                   * will stop us from doing anything when
                 if (!errorReported)                                   * the file is parsed.  */
                     Parse_Error(PARSE_FATAL,                                  if (!errorReported)
                                  "Undefined variable \"%.*s\"",length,str);                                          Parse_Error(PARSE_FATAL,
                 str += length;                                               "Undefined variable \"%.*s\"",
                 errorReported = true;                                               length, str);
             } else {                                  str += length;
                 Buf_AddChar(&buf, *str);                                  errorReported = true;
                 str++;                          } else {
             }                                  Buf_AddChar(&buf, *str);
         } else {                                  str++;
             /* We've now got a variable structure to store in. But first,                          }
              * advance the string pointer.  */                  } else {
             str += length;                          /* We've now got a variable structure to store in.
                            * But first, advance the string pointer.  */
                           str += length;
   
             /* Copy all the characters from the variable value straight                          /* Copy all the characters from the variable value
              * into the new string.  */                           * straight into the new string.  */
             Buf_AddString(&buf, val);                          Buf_AddString(&buf, val);
             if (doFree)                          if (doFree)
                 free(val);                                  free(val);
                   }
         }          }
     }          return  Buf_Retrieve(&buf);
     return  Buf_Retrieve(&buf);  
 }  }
   
   
   /***
    ***    Supplementary support for .for loops.
    ***/
   
   
   
   struct LoopVar
   {
           Var old;        /* keep old variable value (before the loop) */
           Var *me;        /* the variable we're dealing with */
   };
   
   
   struct LoopVar *
   Var_NewLoopVar(const char *name, const char *ename)
   {
           struct LoopVar *l;
           uint32_t k;
   
           l = emalloc(sizeof(struct LoopVar));
   
           /* we obtain a new variable quickly, make a snapshot of its old
            * value, and make sure the environment cannot touch us.
            */
           /* XXX: should we avoid dynamic variables ? */
           k = ohash_interval(name, &ename);
   
           l->me = find_global_var_without_env(name, ename, k);
           l->old = *(l->me);
           l->me->flags |= VAR_SEEN_ENV;
           return l;
   }
   
 void  void
 Var_SubstVar(Buffer buf,        /* To store result */  Var_DeleteLoopVar(struct LoopVar *l)
     const char *str,            /* The string in which to substitute */  {
     const char *var,            /* Named variable */          *(l->me) = l->old;
           free(l);
   }
   
   void
   Var_SubstVar(Buffer buf,        /* To store result */
       const char *str,            /* The string in which to substitute */
       struct LoopVar *l,          /* Handle */
     const char *val)            /* Its value */      const char *val)            /* Its value */
 {  {
     /* we save the old value and affect the new value temporarily */          const char *var = l->me->name;
     Var old;  
     const char *ename = NULL;  
     uint32_t k;  
     Var *me;  
     k = ohash_interval(var, &ename);  
     me = obtain_global_var(var, ename, k);  
     old = *me;  
     var_init_string(me, val);  
     me->flags = VAR_SEEN_ENV;  
   
     assert(*var != '\0');          var_set_value(l->me, val);
   
     for (;;) {          for (;;) {
         const char *start;                  const char *start;
         /* Copy uninteresting stuff */                  /* Copy uninteresting stuff */
         for (start = str; *str != '\0' && *str != '$'; str++)                  for (start = str; *str != '\0' && *str != '$'; str++)
             ;                          ;
         Buf_Addi(buf, start, str);                  Buf_Addi(buf, start, str);
   
         start = str;                  start = str;
         if (*str++ == '\0')                  if (*str++ == '\0')
             break;                          break;
         str++;                  str++;
         /* and escaped dollars */                  /* and escaped dollars */
         if (start[1] == '$') {                  if (start[1] == '$') {
             Buf_Addi(buf, start, start+2);                          Buf_Addi(buf, start, start+2);
             continue;                          continue;
         }                  }
         /* Simple variable, if it's not us, copy.  */                  /* Simple variable, if it's not us, copy.  */
         if (start[1] != '(' && start[1] != '{') {                  if (start[1] != '(' && start[1] != '{') {
             if (start[1] != *var || var[1] != '\0') {                          if (start[1] != *var || var[1] != '\0') {
                 Buf_AddChars(buf, 2, start);                                  Buf_AddChars(buf, 2, start);
                 continue;                                  continue;
             }                      }
         } else {                  } else {
             const char *p;                          const char *p;
             char endc;                          char paren = start[1];
   
             if (start[1] == '(')  
                 endc = ')';  
             else  
                 endc = '}';  
   
             /* Find the end of the variable specification.  */                          /* Find the end of the variable specification.  */
             p = str;                          p = find_pos(paren)(str);
             while (*p != '\0' && *p != ':' && *p != endc && *p != '$')                          /* A variable inside the variable. We don't know how to
                 p++;                           * expand the external variable at this point, so we
             /* A variable inside the variable.  We don't know how to                           * try  again with the nested variable. */
              * expand the external variable at this point, so we try                          if (*p == '$') {
              * again with the nested variable.  */                                  Buf_Addi(buf, start, p);
             if (*p == '$') {                                  str = p;
                 Buf_Addi(buf, start, p);                                  continue;
                 str = p;                          }
                 continue;  
             }  
   
             if (strncmp(var, str, p - str) != 0 ||                          if (strncmp(var, str, p - str) != 0 ||
                 var[p - str] != '\0') {                                  var[p - str] != '\0') {
                 /* Not the variable we want to expand.  */                                  /* Not the variable we want to expand.  */
                 Buf_Addi(buf, start, p);                                  Buf_Addi(buf, start, p);
                 str = p;                                  str = p;
                 continue;                                  continue;
             }                          }
             if (*p == ':') {                          if (*p == ':') {
                 size_t  length;         /* Length of the variable invocation */                                  size_t length;  /* Length of variable name */
                 bool doFree;    /* Set true if val should be freed */                                  bool doFree;    /* should val be freed ? */
                 char    *newval;        /* Value substituted for a variable */                                  char *newval;
                 struct Name name;                                  struct Name name;
   
                 length = p - str + 1;                                  length = p - str + 1;
                 doFree = false;                                  doFree = false;
                 name.s = var;                                  name.s = var;
                 name.e = var + (p-str);                                  name.e = var + (p-str);
   
                 /* val won't be freed since doFree == false, but                                  /* val won't be freed since !doFree, but
                  * VarModifiers_Apply doesn't know that, hence the cast. */                                   * VarModifiers_Apply doesn't know that,
                 newval = VarModifiers_Apply((char *)val, &name, NULL, false,                                   * hence the cast. */
                     &doFree, p, endc, &length);                                  newval = VarModifiers_Apply((char *)val, &name,
                 Buf_AddString(buf, newval);                                      NULL, false, &doFree, p, paren, &length);
                 if (doFree)                                  Buf_AddString(buf, newval);
                     free(newval);                                  if (doFree)
                 str += length;                                          free(newval);
                 continue;                                  str += length;
             } else                                  continue;
                 str = p+1;                          } else
                                   str = p+1;
                   }
                   Buf_AddString(buf, val);
         }          }
         Buf_AddString(buf, val);  
     }  
     *me = old;  
 }  }
   
   /***
    ***    Odds and ends
    ***/
   
 static void  static void
 set_magic_shell_variable()  set_magic_shell_variable()
 {  {
     const char *name = "SHELL";          const char *name = "SHELL";
     const char *ename = NULL;          const char *ename = NULL;
     uint32_t k;          uint32_t k;
     Var *v;          Var *v;
     k = ohash_interval(name, &ename);  
     v = create_var(name, ename);          k = ohash_interval(name, &ename);
     ohash_insert(&global_variables,          v = find_global_var_without_env(name, ename, k);
         ohash_lookup_interval(&global_variables, name, ename, k), v);          var_set_value(v, _PATH_BSHELL);
         /* the environment shall not affect it */          /* XXX the environment shall never affect it */
     v->flags = VAR_SHELL | VAR_SEEN_ENV;          v->flags = VAR_SHELL | VAR_SEEN_ENV;
     var_init_string(v, _PATH_BSHELL);  
 }  }
   
 /*-  /*
  *-----------------------------------------------------------------------   * Var_Init
  * Var_Init --  
  *      Initialize the module   *      Initialize the module
  *  
  * Side Effects:  
  *      The CTXT_CMD and CTXT_GLOBAL contexts are initialized  
  *-----------------------------------------------------------------------  
  */   */
 void  void
 Var_Init(void)  Var_Init(void)
 {  {
     ohash_init(&global_variables, 10, &var_info);          ohash_init(&global_variables, 10, &var_info);
     set_magic_shell_variable();          set_magic_shell_variable();
   
   
     errorIsOkay = true;          errorIsOkay = true;
     Var_setCheckEnvFirst(false);          Var_setCheckEnvFirst(false);
   
     VarModifiers_Init();          VarModifiers_Init();
 }  }
   
   
Line 1098 
Line 1229 
 void  void
 Var_End(void)  Var_End(void)
 {  {
     Var *v;          Var *v;
     unsigned int i;          unsigned int i;
   
     for (v = ohash_first(&global_variables, &i); v != NULL;          for (v = ohash_first(&global_variables, &i); v != NULL;
         v = ohash_next(&global_variables, &i))              v = ohash_next(&global_variables, &i))
             VarDelete(v);                  delete_var(v);
 }  }
 #endif  #endif
   
Line 1112 
Line 1243 
 static const char *  static const char *
 interpret(int f)  interpret(int f)
 {  {
     if (f & VAR_DUMMY)          if (f & VAR_DUMMY)
         return "(D)";                  return "(D)";
     return "";          return "";
 }  }
   
   
 /****************** PRINT DEBUGGING INFO *****************/  
 static void  static void
 VarPrintVar(Var *v)  print_var(Var *v)
 {  {
     printf("%-16s%s = %s\n", v->name, interpret(v->flags),          printf("%-16s%s = %s\n", v->name, interpret(v->flags),
         (v->flags & VAR_DUMMY) == 0 ? VarValue(v) : "(none)");              (v->flags & VAR_DUMMY) == 0 ? var_get_value(v) : "(none)");
 }  }
   
 void  void
 Var_Dump(void)  Var_Dump(void)
 {  {
     Var *v;          Var *v;
     unsigned int i;          unsigned int i;
   
     printf("#*** Global Variables:\n");          printf("#*** Global Variables:\n");
   
     for (v = ohash_first(&global_variables, &i); v != NULL;          for (v = ohash_first(&global_variables, &i); v != NULL;
         v = ohash_next(&global_variables, &i))              v = ohash_next(&global_variables, &i))
         VarPrintVar(v);                  print_var(v);
   
 }  }
   
 static const char *quotable = " \t\n\\'\"";  static const char *quotable = " \t\n\\'\"";
   
 /* In POSIX mode, variable assignments passed on the command line are  /* POSIX says that variable assignments passed on the command line should be
  * propagated to sub makes through MAKEFLAGS.   * propagated to sub makes through MAKEFLAGS.
  */   */
 void  void
 Var_AddCmdline(const char *name)  Var_AddCmdline(const char *name)
 {  {
     Var *v;          Var *v;
     unsigned int i;          unsigned int i;
     BUFFER buf;          BUFFER buf;
     char *s;          char *s;
   
     Buf_Init(&buf, MAKE_BSIZE);          Buf_Init(&buf, MAKE_BSIZE);
   
     for (v = ohash_first(&global_variables, &i); v != NULL;          for (v = ohash_first(&global_variables, &i); v != NULL;
         v = ohash_next(&global_variables, &i)) {              v = ohash_next(&global_variables, &i)) {
                   /* This is not as expensive as it looks: this function is
                    * called before parsing Makefiles, so there are just a
                    * few non cmdling variables in there.
                    */
                 if (!(v->flags & VAR_FROM_CMD)) {                  if (!(v->flags & VAR_FROM_CMD)) {
                         continue;                          continue;
                 }                  }
                 /* We assume variable names don't need quoting */                  /* We assume variable names don't need quoting */
                 Buf_AddString(&buf, v->name);                  Buf_AddString(&buf, v->name);
                 Buf_AddChar(&buf, '=');                  Buf_AddChar(&buf, '=');
                 for (s = VarValue(v); *s != '\0'; s++) {                  for (s = var_get_value(v); *s != '\0'; s++) {
                         if (strchr(quotable, *s))                          if (strchr(quotable, *s))
                                 Buf_AddChar(&buf, '\\');                                  Buf_AddChar(&buf, '\\');
                         Buf_AddChar(&buf, *s);                          Buf_AddChar(&buf, *s);
                 }                  }
                 Buf_AddSpace(&buf);                  Buf_AddSpace(&buf);
     }          }
     Var_Append(name, Buf_Retrieve(&buf), VAR_GLOBAL);          Var_Append(name, Buf_Retrieve(&buf), VAR_GLOBAL);
     Buf_Destroy(&buf);          Buf_Destroy(&buf);
 }  }

Legend:
Removed from v.1.65  
changed lines
  Added in v.1.66