]> git.ipfire.org Git - thirdparty/make.git/commitdiff
Initial revision
authorRoland McGrath <roland@redhat.com>
Mon, 7 Oct 1991 22:04:20 +0000 (22:04 +0000)
committerRoland McGrath <roland@redhat.com>
Mon, 7 Oct 1991 22:04:20 +0000 (22:04 +0000)
misc.c [new file with mode: 0644]
variable.c [new file with mode: 0644]

diff --git a/misc.c b/misc.c
new file mode 100644 (file)
index 0000000..77b2f30
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,598 @@
+/* Copyright (C) 1988-1991 Free Software Foundation, Inc.
+This file is part of GNU Make.
+
+GNU Make is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Make is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Make; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "make.h"
+#include "dep.h"
+
+
+/* Compare strings *S1 and *S2.
+   Return negative if the first is less, positive if it is greater,
+   zero if they are equal.  */
+
+int
+alpha_compare (s1, s2)
+     char **s1, **s2;
+{
+  if (**s1 != **s2)
+    return **s1 - **s2;
+  return strcmp (*s1, *s2);
+}
+\f
+/* Discard each backslash-newline combination from LINE.
+   Backslash-backslash-newline combinations become backslash-newlines.
+   This is done by copying the text at LINE into itself.  */
+
+void
+collapse_continuations (line)
+     char *line;
+{
+  register char *in, *out, *p;
+  register int backslash;
+  register unsigned int bs_write;
+
+  in = index (line, '\n');
+  if (in == 0)
+    return;
+
+  out = in;
+  if (out > line)
+    while (out[-1] == '\\')
+      --out;
+
+  while (*in != '\0')
+    {
+      /* BS_WRITE gets the number of quoted backslashes at
+        the end just before IN, and BACKSLASH gets nonzero
+        if the next character is quoted.  */
+      backslash = 0;
+      bs_write = 0;
+      for (p = in - 1; p >= line && *p == '\\'; --p)
+       {
+         if (backslash)
+           ++bs_write;
+         backslash = !backslash;
+
+         /* It should be impossible to go back this far without exiting,
+            but if we do, we can't get the right answer.  */
+         if (in == out - 1)
+           abort ();
+       }
+
+      /* Output the appropriate number of backslashes.  */
+      while (bs_write-- > 0)
+       *out++ = '\\';
+
+      /* Skip the newline.  */
+      ++in;
+
+      /* If the newline is quoted, discard following whitespace
+        and any preceding whitespace; leave just one space.  */
+      if (backslash)
+       {
+         in = next_token (in);
+         while (out > line && isblank (out[-1]))
+           --out;
+         *out++ = ' ';
+       }
+      else
+       /* If the newline isn't quoted, put it in the output.  */
+       *out++ = '\n';
+
+      /* Now copy the following line to the output.
+        Stop when we find backslashes followed by a newline.  */
+      while (*in != '\0')
+       if (*in == '\\')
+         {
+           p = in + 1;
+           while (*p == '\\')
+             ++p;
+           if (*p == '\n')
+             {
+               in = p;
+               break;
+             }
+           while (in < p)
+             *out++ = *in++;
+         }
+       else
+         *out++ = *in++;
+    }
+
+  *out = '\0';
+}
+
+
+/* Remove comments from LINE.
+   This is done by copying the text at LINE onto itself.  */
+
+void
+remove_comments (line)
+     char *line;
+{
+  register char *p, *p2;
+  register int backslash;
+  register unsigned int bs_write;
+
+  while (1)
+    {
+      p = index (line, '#');
+      if (p == 0)
+       break;
+
+      backslash = 0;
+      bs_write = 0;
+      for (p2 = p - 1; p2 > line && *p2 == '\\'; --p2)
+       {
+         if (backslash)
+           ++bs_write;
+         backslash = !backslash;
+       }
+
+      if (!backslash)
+       {
+         /* Cut off the line at the #.  */
+         *p = '\0';
+         break;
+       }
+
+      /* strcpy better copy left to right.  */
+      line = p;
+      strcpy (p2 + 1 + bs_write, line);
+    }
+}
+\f
+/* Print N spaces (used by DEBUGPR for target-depth).  */
+
+void
+print_spaces (n)
+     register unsigned int n;
+{
+  while (n-- > 0)
+    putchar (' ');
+}
+
+\f
+/* Return a newly-allocated string whose contents
+   concatenate those of s1, s2, s3.  */
+
+char *
+concat (s1, s2, s3)
+     register char *s1, *s2, *s3;
+{
+  register unsigned int len1, len2, len3;
+  register char *result;
+
+  len1 = *s1 != '\0' ? strlen (s1) : 0;
+  len2 = *s2 != '\0' ? strlen (s2) : 0;
+  len3 = *s3 != '\0' ? strlen (s3) : 0;
+
+  result = (char *) xmalloc (len1 + len2 + len3 + 1);
+
+  if (*s1 != '\0')
+    bcopy (s1, result, len1);
+  if (*s2 != '\0')
+    bcopy (s2, result + len1, len2);
+  if (*s3 != '\0')
+    bcopy (s3, result + len1 + len2, len3);
+  *(result + len1 + len2 + len3) = '\0';
+
+  return result;
+}
+\f
+/* Print a message on stdout.  */
+
+void
+message (s1, s2, s3, s4, s5, s6)
+     char *s1, *s2, *s3, *s4, *s5, *s6;
+{
+  if (makelevel == 0)
+    printf ("%s: ", program);
+  else
+    printf ("%s[%u]: ", program, makelevel);
+  printf (s1, s2, s3, s4, s5, s6);
+  putchar ('\n');
+  fflush (stdout);
+}
+
+/* Print an error message and exit.  */
+
+/* VARARGS1 */
+void
+fatal (s1, s2, s3, s4, s5, s6)
+     char *s1, *s2, *s3, *s4, *s5, *s6;
+{
+  if (makelevel == 0)
+    fprintf (stderr, "%s: ", program);
+  else
+    fprintf (stderr, "%s[%u]: ", program, makelevel);
+  fprintf (stderr, s1, s2, s3, s4, s5, s6);
+  fputs (".  Stop.\n", stderr);
+
+  die (1);
+}
+
+/* Print error message.  `s1' is printf control string, `s2' is arg for it. */
+
+/* VARARGS1 */
+
+void
+error (s1, s2, s3, s4, s5, s6)
+     char *s1, *s2, *s3, *s4, *s5, *s6;
+{
+  if (makelevel == 0)
+    fprintf (stderr, "%s: ", program);
+  else
+    fprintf (stderr, "%s[%u]: ", program, makelevel);
+  fprintf (stderr, s1, s2, s3, s4, s5, s6);
+  putc ('\n', stderr);
+  fflush (stderr);
+}
+
+void
+makefile_error (file, lineno, s1, s2, s3, s4, s5, s6)
+     char *file;
+     unsigned int lineno;
+     char *s1, *s2, *s3, *s4, *s5, *s6;
+{
+  fprintf (stderr, "%s:%u: ", file, lineno);
+  fprintf (stderr, s1, s2, s3, s4, s5, s6);
+  putc ('\n', stderr);
+  fflush (stderr);
+}
+
+void
+makefile_fatal (file, lineno, s1, s2, s3, s4, s5, s6)
+     char *file;
+     unsigned int lineno;
+     char *s1, *s2, *s3, *s4, *s5, *s6;
+{
+  fprintf (stderr, "%s:%u: ", file, lineno);
+  fprintf (stderr, s1, s2, s3, s4, s5, s6);
+  fputs (".  Stop.\n", stderr);
+
+  die (1);
+}
+
+/* Print an error message from errno.  */
+
+void
+perror_with_name (str, name)
+     char *str, *name;
+{
+  extern int errno, sys_nerr;
+  extern char *sys_errlist[];
+
+  if (errno < sys_nerr)
+    error ("%s%s: %s", str, name, sys_errlist[errno]);
+  else
+    error ("%s%s: Unknown error %d", str, name, errno);
+}
+
+/* Print an error message from errno and exit.  */
+
+void
+pfatal_with_name (name)
+     char *name;
+{
+  extern int errno, sys_nerr;
+  extern char *sys_errlist[];
+
+  if (errno < sys_nerr)
+    fatal ("%s: %s", name, sys_errlist[errno]);
+  else
+    fatal ("%s: Unknown error %d", name, errno);
+
+  /* NOTREACHED */
+}
+\f
+/* Like malloc but get fatal error if memory is exhausted.  */
+
+#undef xmalloc
+#undef xrealloc
+
+char *
+xmalloc (size)
+     unsigned int size;
+{
+  char *result = malloc (size);
+  if (result == 0)
+    fatal ("virtual memory exhausted");
+  return result;
+}
+
+
+char *
+xrealloc (ptr, size)
+     char *ptr;
+     unsigned int size;
+{
+  char *result = realloc (ptr, size);
+  if (result == 0)
+    fatal ("virtual memory exhausted");
+  return result;
+}
+
+char *
+savestring (str, length)
+     char *str;
+     unsigned int length;
+{
+  register char *out = (char *) xmalloc (length + 1);
+  if (length > 0)
+    bcopy (str, out, length);
+  out[length] = '\0';
+  return out;
+}
+\f
+/* Search string BIG (length BLEN) for an occurrence of
+   string SMALL (length SLEN).  Return a pointer to the
+   beginning of the first occurrence, or return nil if none found.  */
+
+char *
+sindex (big, blen, small, slen)
+     char *big;
+     unsigned int blen;
+     char *small;
+     unsigned int slen;
+{
+  register unsigned int b;
+
+  if (blen < 1)
+    blen = strlen (big);
+  if (slen < 1)
+    slen = strlen (small);
+
+  for (b = 0; b < blen; ++b)
+    if (big[b] == *small && !strncmp (&big[b + 1], small + 1, slen - 1))
+      return (&big[b]);
+
+  return 0;
+}
+
+/* Limited INDEX:
+   Search through the string STRING, which ends at LIMIT, for the character C.
+   Returns a pointer to the first occurrence, or nil if none is found.
+   Like INDEX except that the string searched ends where specified
+   instead of at the first null.  */
+
+char *
+lindex (s, limit, c)
+     register char *s, *limit;
+     int c;
+{
+  while (s < limit)
+    if (*s++ == c)
+      return s - 1;
+
+  return 0;
+}
+\f
+/* Return the address of the first whitespace or null in the string S.  */
+
+char *
+end_of_token (s)
+     char *s;
+{
+  register char *p = s;
+  register int backslash = 0;
+
+  while (*p != '\0' && (backslash || !isblank (*p)))
+    {
+      if (*p++ == '\\')
+       {
+         backslash = !backslash;
+         while (*p == '\\')
+           {
+             backslash = !backslash;
+             ++p;
+           }
+       }
+      else
+       backslash = 0;
+    }
+
+  return p;
+}
+
+/* Return the address of the first nonwhitespace or null in the string S.  */
+
+char *
+next_token (s)
+     char *s;
+{
+  register char *p = s;
+
+  while (isblank (*p))
+    ++p;
+  return p;
+}
+
+/* Find the next token in PTR; return the address of it, and store the
+   length of the token into *LENGTHPTR if LENGTHPTR is not nil.  */
+
+char *
+find_next_token (ptr, lengthptr)
+     char **ptr;
+     unsigned int *lengthptr;
+{
+  char *p = next_token (*ptr);
+  char *end;
+
+  if (*p == '\0')
+    return 0;
+
+  *ptr = end = end_of_token (p);
+  if (lengthptr != 0)
+    *lengthptr = end - p;
+  return p;
+}
+\f
+/* Copy a chain of `struct dep', making a new chain
+   with the same contents as the old one.  */
+
+struct dep *
+copy_dep_chain (d)
+     register struct dep *d;
+{
+  register struct dep *c;
+  struct dep *firstnew = 0;
+  struct dep *lastnew;
+
+  while (d != 0)
+    {
+      c = (struct dep *) xmalloc (sizeof (struct dep));
+      bcopy ((char *) d, (char *) c, sizeof (struct dep));
+      if (c->name != 0)
+       c->name = savestring (c->name, strlen (c->name));
+      c->next = 0;
+      if (firstnew == 0)
+       firstnew = lastnew = c;
+      else
+       lastnew = lastnew->next = c;
+
+      d = d->next;
+    }
+
+  return firstnew;
+}
+\f
+#ifdef iAPX286
+/* The losing compiler on this machine can't handle this macro.  */
+
+char *
+dep_name (dep)
+     struct dep *dep;
+{
+  return dep->name == 0 ? dep->file->name : dep->name;
+}
+#endif
+\f
+#if    !defined(POSIX) && !defined(__GNU_LIBRARY__)
+extern int getuid (), getgid (), geteuid (), getegid ();
+#ifdef USG
+extern int setuid (), setgid ();
+#else
+extern int setreuid (), setregid ();
+#endif /* USG.  */
+#endif /* Not POSIX and not GNU C library.  */
+
+/* Keep track of the user and group IDs for user- and make- access.  */
+static int user_uid = -1, user_gid = -1, make_uid = -1, make_gid = -1;
+#define        access_inited   (user_uid != -1)
+static enum { make, user } current_access;
+
+static void
+init_access ()
+{
+  user_uid = getuid ();
+  user_gid = getgid ();
+
+  make_uid = geteuid ();
+  make_gid = getegid ();
+
+  /* Do these ever fail?  */
+  if (user_uid == -1 || user_gid == -1 || make_uid == -1 || make_gid == -1)
+    pfatal_with_name ("get{e}[gu]id");
+
+  current_access = make;
+}
+
+/* Give the process appropriate permissions for access to
+   user data (i.e., to stat files, or to spawn a child process).  */
+void
+user_access ()
+{
+  if (!access_inited)
+    init_access ();
+
+  if (current_access == user)
+    return;
+
+  /* We are in "make access" mode.  This means that the effective user and
+     group IDs are those of make (if it was installed setuid or setgid).
+     We now want to set the effective user and group IDs to the real IDs,
+     which are the IDs of the process that exec'd make.  */
+
+#if    defined (USG) || defined (POSIX)
+  /* System V has only the setuid/setgid calls to set user/group IDs.
+     There is an effective ID, which can be set by setuid/setgid.
+     It can be set (unless you are root) only to either what it already is
+     (returned by geteuid/getegid, now in make_uid/make_gid),
+     the real ID (return by getuid/getgid, now in user_uid/user_gid),
+     or the saved set ID (what the effective ID was before this set-ID
+     executable (make) was exec'd).  */
+  if (setuid (user_uid) < 0)
+    pfatal_with_name ("setuid");
+  if (setgid (user_gid) < 0)
+    pfatal_with_name ("setgid");
+#else
+  /* In 4BSD, the setreuid/setregid calls set both the real and effective IDs.
+     They may be set to themselves or each other.  So you have two alternatives
+     at any one time.  If you use setuid/setgid, the effective will be set to
+     the real, leaving only one alternative.  Using setreuid/setregid, however,
+     you can toggle between your two alternatives by swapping the values in a
+     single setreuid or setregid call.  */
+  if (setreuid (make_uid, user_uid) < 0)
+    pfatal_with_name ("setreuid");
+  if (setregid (make_gid, user_gid) < 0)
+    pfatal_with_name ("setregid");
+#endif
+
+  current_access = user;
+}
+
+/* Give the process appropriate permissions for access to
+   make data (i.e., the load average).  */
+void
+make_access ()
+{
+  if (!access_inited)
+    init_access ();
+
+  if (current_access == make)
+    return;
+
+  /* See comments in user_access, above.  */
+
+#if    defined (USG) || defined (POSIX)
+  if (setuid (make_uid) < 0)
+    pfatal_with_name ("setuid");
+  if (setgid (make_gid) < 0)
+    pfatal_with_name ("setgid");
+#else
+  if (setreuid (user_uid, make_uid) < 0)
+    pfatal_with_name ("setreuid");
+  if (setregid (user_gid, make_gid) < 0)
+    pfatal_with_name ("setregid");
+#endif
+
+  current_access = make;
+}
+
+/* Give the process appropriate permissions for a child process.
+   This is like user_access, but you can't get back to make_access.  */
+void
+child_access ()
+{
+  /* Set both the real and effective UID and GID to the user's.
+     They cannot be changed back to make's.  */
+
+  if (setuid (user_uid) < 0)
+    pfatal_with_name ("setuid");
+  if (setgid (user_gid) < 0)
+    pfatal_with_name ("setgid");
+}
diff --git a/variable.c b/variable.c
new file mode 100644 (file)
index 0000000..019d802
--- /dev/null
@@ -0,0 +1,768 @@
+/* Internals of variables for GNU Make.
+Copyright (C) 1988, 1989, 1990, 1991 Free Software Foundation, Inc.
+This file is part of GNU Make.
+
+GNU Make is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Make is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Make; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "make.h"
+#include "commands.h"
+#include "variable.h"
+#include "dep.h"
+#include "file.h"
+
+#ifdef __GNUC__
+#define        max(a, b) \
+  ({ register int __a = (a), __b = (b); __a > __b ? __a : __b; })
+#else
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+
+/* Hash table of all global variable definitions.  */
+
+#ifndef        VARIABLE_BUCKETS
+#define VARIABLE_BUCKETS               523
+#endif
+#ifndef        PERFILE_VARIABLE_BUCKETS
+#define        PERFILE_VARIABLE_BUCKETS        23
+#endif
+#ifndef        SMALL_SCOPE_VARIABLE_BUCKETS
+#define        SMALL_SCOPE_VARIABLE_BUCKETS    13
+#endif
+static struct variable *variable_table[VARIABLE_BUCKETS];
+static struct variable_set global_variable_set
+  = { variable_table, VARIABLE_BUCKETS };
+static struct variable_set_list global_setlist
+  = { 0, &global_variable_set };
+struct variable_set_list *current_variable_set_list = &global_setlist;
+
+/* The next two describe the variable output buffer.
+   This buffer is used to hold the variable-expansion of a line of the
+   makefile.  It is made bigger with realloc whenever it is too small.
+   variable_buffer_length is the size currently allocated.
+   variable_buffer is the address of the buffer.  */
+
+static unsigned int variable_buffer_length;
+static char *variable_buffer;
+\f
+/* Implement variables.  */
+
+/* Define variable named NAME with value VALUE in SET.  VALUE is copied.
+   LENGTH is the length of NAME, which does not need to be null-terminated.
+   ORIGIN specifies the origin of the variable (makefile, command line
+   or environment).
+   If RECURSIVE is nonzero a flag is set in the variable saying
+   that it should be recursively re-expanded.  */
+
+static struct variable *
+define_variable_in_set (name, length, value, origin, recursive, set)
+     char *name;
+     unsigned int length;
+     char *value;
+     enum variable_origin origin;
+     int recursive;
+     struct variable_set *set;
+{
+  register unsigned int i;
+  register unsigned int hashval;
+  register struct variable *v;
+
+  hashval = 0;
+  for (i = 0; i < length; ++i)
+    HASH (hashval, name[i]);
+  hashval %= set->buckets;
+
+  for (v = set->table[hashval]; v != 0; v = v->next)
+    if (*v->name == *name
+       && !strncmp (v->name + 1, name + 1, length - 1)
+       && v->name[length] == '\0')
+      break;
+
+  if (env_overrides && origin == o_env)
+    origin = o_env_override;
+
+  if (v != 0)
+    {
+      if (env_overrides && v->origin == o_env)
+       /* V came from in the environment.  Since it was defined
+          before the switches were parsed, it wasn't affected by -e.  */
+       v->origin = o_env_override;
+
+      /* A variable of this name is already defined.
+        If the old definition is from a stronger source
+        than this one, don't redefine it.  */
+      if ((int) origin >= (int) v->origin)
+       {
+         v->value = savestring (value, strlen (value));
+         v->origin = origin;
+         v->recursive = recursive;
+       }
+      return v;
+    }
+
+  /* Create a new variable definition and add it to the hash table.  */
+
+  v = (struct variable *) xmalloc (sizeof (struct variable));
+  v->name = savestring (name, length);
+  v->value = savestring (value, strlen (value));
+  v->origin = origin;
+  v->recursive = recursive;
+  v->expanding = 0;
+  v->next = set->table[hashval];
+  set->table[hashval] = v;
+  return v;
+}
+
+/* Define a variable in the current variable set.  */
+
+struct variable *
+define_variable (name, length, value, origin, recursive)
+     char *name;
+     unsigned int length;
+     char *value;
+     enum variable_origin origin;
+     int recursive;
+{
+  return define_variable_in_set (name, length, value, origin, recursive,
+                                current_variable_set_list->set);
+}
+
+/* Define a variable in FILE's variable set.  */
+
+struct variable *
+define_variable_for_file (name, length, value, origin, recursive, file)
+     char *name;
+     unsigned int length;
+     char *value;
+     enum variable_origin origin;
+     int recursive;
+     struct file *file;
+{
+  return define_variable_in_set (name, length, value, origin, recursive,
+                                file->variables->set);
+}
+\f
+/* Lookup a variable whose name is a string starting at NAME
+   and with LENGTH chars.  NAME need not be null-terminated.
+   Returns address of the `struct variable' containing all info
+   on the variable, or nil if no such variable is defined.  */
+
+struct variable *
+lookup_variable (name, length)
+     char *name;
+     unsigned int length;
+{
+  register struct variable_set_list *setlist;
+
+  register unsigned int i;
+  register unsigned int rawhash = 0;
+
+  for (i = 0; i < length; ++i)
+    HASH (rawhash, name[i]);
+
+  for (setlist = current_variable_set_list;
+       setlist != 0; setlist = setlist->next)
+    {
+      register struct variable_set *set = setlist->set;
+      register unsigned int hashval = rawhash % set->buckets;
+      register struct variable *v;
+
+      for (v = set->table[hashval]; v != 0; v = v->next)
+       if (*v->name == *name
+           && !strncmp (v->name + 1, name + 1, length - 1)
+           && v->name[length] == 0)
+         return v;
+    }
+
+  return 0;
+}
+\f
+/* Initialize FILE's variable set list.  If FILE already has a variable set
+   list, the topmost variable set is left intact, but the the rest of the
+   chain is replaced with FILE->parent's setlist.  */
+
+void
+initialize_file_variables (file)
+     struct file *file;
+{
+  register struct variable_set_list *l = file->variables;
+  if (l == 0)
+    {
+      l = (struct variable_set_list *)
+       xmalloc (sizeof (struct variable_set_list));
+      l->set = (struct variable_set *) xmalloc (sizeof (struct variable_set));
+      l->set->buckets = PERFILE_VARIABLE_BUCKETS;
+      l->set->table = (struct variable **)
+       xmalloc (l->set->buckets * sizeof (struct variable *));
+      bzero ((char *) l->set->table,
+            l->set->buckets * sizeof (struct variable *));
+      file->variables = l;
+    }
+
+  if (file->parent == 0)
+    l->next = &global_setlist;
+  else
+    {
+      if (file->parent->variables == 0)
+       initialize_file_variables (file->parent);
+      l->next = file->parent->variables;
+    }
+}
+\f
+/* Pop the top set off the current variable set list,
+   and free all its storage.  */
+
+void
+pop_variable_scope ()
+{
+  register struct variable_set_list *setlist = current_variable_set_list;
+  register struct variable_set *set = setlist->set;
+  register unsigned int i;
+
+  current_variable_set_list = setlist->next;
+  free ((char *) setlist);
+
+  for (i = 0; i < set->buckets; ++i)
+    {
+      register struct variable *next = set->table[i];
+      while (next != 0)
+       {
+         register struct variable *v = next;
+         next = v->next;
+
+         free (v->name);
+         free ((char *) v);
+       }
+    }
+  free ((char *) set->table);
+  free ((char *) set);
+}
+
+/* Create a new variable set and push it on the current setlist.  */
+
+void
+push_new_variable_scope ()
+{
+  register struct variable_set_list *setlist;
+  register struct variable_set *set;
+
+  set = (struct variable_set *) xmalloc (sizeof (struct variable_set));
+  set->buckets = SMALL_SCOPE_VARIABLE_BUCKETS;
+  set->table = (struct variable **)
+    xmalloc (set->buckets * sizeof (struct variable *));
+  bzero ((char *) set->table, set->buckets * sizeof (struct variable *));
+
+  setlist = (struct variable_set_list *)
+    xmalloc (sizeof (struct variable_set_list));
+  setlist->set = set;
+  setlist->next = current_variable_set_list;
+  current_variable_set_list = setlist;
+}
+\f
+/* Merge SET1 into SET0, freeing unused storage in SET1.  */
+
+static void
+merge_variable_sets (set0, set1)
+     struct variable_set *set0, *set1;
+{
+  register unsigned int bucket1;
+
+  for (bucket1 = 0; bucket1 < set1->buckets; ++bucket1)
+    {
+      register struct variable *v1 = set1->table[bucket1];
+      while (v1 != 0)
+       {
+         struct variable *next = v1->next;
+         unsigned int bucket0;
+         register struct variable *v0;
+
+         if (set1->buckets >= set0->buckets)
+           bucket0 = bucket1;
+         else
+           {
+             register char *n;
+             bucket0 = 0;
+             for (n = v1->name; *n != '\0'; ++n)
+               HASH (bucket0, *n);
+           }
+         bucket0 %= set0->buckets;
+
+         for (v0 = set0->table[bucket0]; v0 != 0; v0 = v0->next)
+           if (streq (v0->name, v1->name))
+             break;
+
+         if (v0 == 0)
+           {
+             /* There is no variable in SET0 with the same name.  */
+             v1->next = set0->table[bucket0];
+             set0->table[bucket0] = v1;
+           }
+         else
+           {
+             /* The same variable exists in both sets.
+                SET0 takes precedence.  */
+             free (v1->value);
+             free ((char *) v1);
+           }
+
+         v1 = next;
+       }
+    }
+}
+
+/* Merge SETLIST1 into SETLIST0, freeing unused storage in SETLIST1.  */
+
+void
+merge_variable_set_lists (setlist0, setlist1)
+     struct variable_set_list **setlist0, *setlist1;
+{
+  register struct variable_set_list *list0 = *setlist0;
+  struct variable_set_list *last0 = 0;
+
+  while (setlist1 != 0 && list0 != 0)
+    {
+      struct variable_set_list *next = setlist1;
+      setlist1 = setlist1->next;
+
+      merge_variable_sets (list0->set, next->set);
+
+      free ((char *) next);
+
+      last0 = list0;
+      list0 = list0->next;
+    }
+
+  if (setlist1 != 0)
+    {
+      if (last0 == 0)
+       *setlist0 = setlist1;
+      else
+       last0->next = setlist1;
+    }
+}
+\f
+/* Define the automatic variables, and record the addresses
+   of their structures so we can change their values quickly.  */
+
+void
+define_automatic_variables ()
+{
+  extern char default_shell[];
+  register struct variable *v;
+  char buf[100];
+
+  sprintf (buf, "%u", makelevel);
+  (void) define_variable ("MAKELEVEL", 9, buf, o_env, 0);
+
+  /* This won't override any definition, but it
+     will provide one if there isn't one there.  */
+  v = define_variable ("SHELL", 5, default_shell, o_default, 0);
+
+  /* Don't let SHELL come from the environment
+     if MAKELEVEL is 0.  Also, SHELL must not be empty.  */
+  if (*v->value == '\0' || (v->origin == o_env && makelevel == 0))
+    {
+      v->origin = o_file;
+      v->value = savestring ("/bin/sh", 7);
+    }
+}
+\f
+/* Subroutine of variable_expand and friends:
+   The text to add is LENGTH chars starting at STRING to the variable_buffer.
+   The text is added to the buffer at PTR, and the updated pointer into
+   the buffer is returned as the value.  Thus, the value returned by
+   each call to variable_buffer_output should be the first argument to
+   the following call.  */
+
+char *
+variable_buffer_output (ptr, string, length)
+     char *ptr, *string;
+     unsigned int length;
+{
+  register unsigned int newlen = length + (ptr - variable_buffer);
+
+  if (newlen > variable_buffer_length)
+    {
+      unsigned int offset = ptr - variable_buffer;
+      variable_buffer_length = max (2 * variable_buffer_length, newlen + 100);
+      variable_buffer = (char *) xrealloc (variable_buffer,
+                                          variable_buffer_length);
+      ptr = variable_buffer + offset;
+    }
+
+  bcopy (string, ptr, length);
+  return ptr + length;
+}
+
+/* Return a pointer to the beginning of the variable buffer.  */
+
+char *
+initialize_variable_output ()
+{
+  /* If we don't have a variable output buffer yet, get one.  */
+
+  if (variable_buffer == 0)
+    {
+      variable_buffer_length = 200;
+      variable_buffer = (char *) xmalloc (variable_buffer_length);
+    }
+
+  return variable_buffer;
+}
+\f
+/* Create a new environment for FILE's commands.
+   The child's MAKELEVEL variable is incremented.  */
+
+char **
+target_environment (file)
+     struct file *file;
+{
+  register struct variable_set_list *s;
+  struct variable_bucket
+    {
+      struct variable_bucket *next;
+      struct variable *variable;
+    };
+  struct variable_bucket **table;
+  unsigned int buckets;
+  register unsigned int i;
+  register unsigned nvariables;
+  char **result;
+
+  int noexport = enter_file (".NOEXPORT")->is_target;
+
+  /* Find the lowest number of buckets in any set in the list.  */
+  s = file->variables;
+  buckets = s->set->buckets;
+  for (s = s->next; s != 0; s = s->next)
+    if (s->set->buckets < buckets)
+      buckets = s->set->buckets;
+
+  /* Temporarily allocate a table with that many buckets.  */
+  table = (struct variable_bucket **)
+    alloca (buckets * sizeof (struct variable_bucket *));
+  bzero ((char *) table, buckets * sizeof (struct variable_bucket *));
+
+  /* Run through all the variable sets in the list,
+     accumulating variables in TABLE.  */
+  nvariables = 0;
+  for (s = file->variables; s != 0; s = s->next)
+    {
+      register struct variable_set *set = s->set;
+      for (i = 0; i < set->buckets; ++i)
+       {
+         register struct variable *v;
+         for (v = set->table[i]; v != 0; v = v->next)
+           {
+             extern char *getenv ();
+             unsigned int j = i % buckets;
+             register struct variable_bucket *ov;
+             register char *p = v->name;
+
+             /* If `.NOEXPORT' was specified, only export command-line and
+                environment variables.  This is a temporary (very ugly) hack
+                until I fix this problem the right way in version 4.  Ick.  */
+             if (noexport
+                 && (v->origin != o_command
+                     && v->origin != o_env && v->origin != o_env_override
+                     && !(v->origin == o_file && getenv (p) != 0)))
+               continue;
+
+             if (v->origin == o_default
+                 || streq (p, "MAKELEVEL"))
+               continue;
+
+             if (*p != '_' && (*p < 'A' || *p > 'Z')
+                 && (*p < 'a' || *p > 'z'))
+               continue;
+             for (++p; *p != '\0'; ++p)
+               if (*p != '_' && (*p < 'a' || *p > 'z')
+                   && (*p < 'A' || *p > 'Z') && (*p < '0' || *p > '9'))
+                 break;
+             if (*p != '\0')
+               continue;
+
+             for (ov = table[j]; ov != 0; ov = ov->next)
+               if (streq (v->name, ov->variable->name))
+                 break;
+             if (ov == 0)
+               {
+                 register struct variable_bucket *entry;
+                 entry = (struct variable_bucket *)
+                   alloca (sizeof (struct variable_bucket));
+                 entry->next = table[j];
+                 entry->variable = v;
+                 table[j] = entry;
+                 ++nvariables;
+               }
+           }
+       }
+    }
+
+  result = (char **) xmalloc ((nvariables + 2) * sizeof (char *));
+  nvariables = 0;
+  for (i = 0; i < buckets; ++i)
+    {
+      register struct variable_bucket *b;
+      for (b = table[i]; b != 0; b = b->next)
+       {
+         register struct variable *v = b->variable;
+         result[nvariables++] = concat (v->name, "=", v->value);
+       }
+    }
+  result[nvariables] = (char *) xmalloc (100);
+  (void) sprintf (result[nvariables], "MAKELEVEL=%u", makelevel + 1);
+  result[++nvariables] = 0;
+
+  return result;
+}
+\f
+/* Try to interpret LINE (a null-terminated string)
+   as a variable definition.  If it is one, define the
+   variable and return 1.  Otherwise return 0.
+
+   ORIGIN may be o_file, o_override, o_env, o_env_override,
+   or o_command specifying that the variable definition comes
+   from a makefile, an override directive, the environment with
+   or without the -e switch, or the command line.
+
+   A variable definition has the form "name = value" or "name := value".
+   Any whitespace around the "=" or ":=" is removed.  The first form
+   defines a variable that is recursively re-evaluated.  The second form
+   defines a variable whose value is variable-expanded at the time of
+   definition and then is evaluated only once at the time of expansion.  */
+
+int
+try_variable_definition (line, origin)
+     char *line;
+     enum variable_origin origin;
+{
+  register int c;
+  register char *p = line;
+  register char *beg;
+  register char *end;
+  register int recursive;
+
+  if (*p == '\t')
+    return 0;
+  while (1)
+    {
+      c = *p++;
+      if (c == '\0' || c == '#')
+       return 0;
+      if (c == '=')
+       {
+         recursive = 1;
+         break;
+       }
+      else if (c == ':')
+       if (*p == '=')
+         {
+           ++p;
+           recursive = 0;
+           break;
+         }
+       else
+         return 0;
+    }
+
+  beg = next_token (line);
+  end = p - 1;
+  if (!recursive)
+    --end;
+  while (isblank (end[-1]))
+    --end;
+  p = next_token (p);
+
+  (void) define_variable (beg, end - beg, recursive ? p : variable_expand (p),
+                         origin, recursive);
+
+  return 1;
+}
+\f
+/* Print information for variable V, prefixing it with PREFIX.  */
+
+static void
+print_variable (v, prefix)
+     register struct variable *v;
+     char *prefix;
+{
+  char *origin;
+
+  switch (v->origin)
+    {
+    case o_default:
+      origin = "default";
+      break;
+    case o_env:
+      origin = "environment";
+      break;
+    case o_file:
+      origin = "makefile";
+      break;
+    case o_env_override:
+      origin = "environment under -e";
+      break;
+    case o_command:
+      origin = "command line";
+      break;
+    case o_override:
+      origin = "`override' directive";
+      break;
+    case o_automatic:
+      origin = "automatic";
+      break;
+    case o_invalid:
+    default:
+      abort ();
+      break;
+    }
+  printf ("# %s\n", origin);
+
+  fputs (prefix, stdout);
+
+  /* Is this a `define'?  */
+  if (v->recursive && index (v->value, '\n') != 0)
+    printf ("define %s\n%s\nendef\n", v->name, v->value);
+  else
+    {
+      register char *p;
+
+      printf ("%s %s= ", v->name, v->recursive ? "" : ":");
+
+      /* Check if the value is just whitespace.  */
+      p = next_token (v->value);
+      if (p != v->value && *p == '\0')
+       /* All whitespace.  */
+       printf ("$(subst ,,%s)", v->value);
+      else if (v->recursive)
+       fputs (v->value, stdout);
+      else
+       /* Double up dollar signs.  */
+       for (p = v->value; *p != '\0'; ++p)
+         {
+           if (*p == '$')
+             putchar ('$');
+           putchar (*p);
+         }
+      putchar ('\n');
+    }
+}
+
+
+/* Print all the variables in SET.  PREFIX is printed before
+   the actual variable definitions (everything else is comments).  */
+
+static void
+print_variable_set (set, prefix)
+     register struct variable_set *set;
+     char *prefix;
+{
+  register unsigned int i, nvariables, per_bucket;
+  register struct variable *v;
+
+  per_bucket = nvariables = 0;
+  for (i = 0; i < set->buckets; ++i)
+    {
+      register unsigned int this_bucket = 0;
+
+      for (v = set->table[i]; v != 0; v = v->next)
+       {
+         ++this_bucket;
+         print_variable (v, prefix);
+       }
+
+      nvariables += this_bucket;
+      if (this_bucket > per_bucket)
+       per_bucket = this_bucket;
+    }
+
+  if (nvariables == 0)
+    puts ("# No variables.");
+  else
+    {
+      printf ("# %u variables in %u hash buckets.\n",
+             nvariables, set->buckets);
+#ifndef        NO_FLOAT
+      printf ("# average of %.1f variables per bucket, \
+max %u in one bucket.\n",
+             ((double) nvariables) * 100.0 / (double) set->buckets,
+             per_bucket);
+#endif
+    }
+}
+
+
+/* Print the data base of variables.  */
+
+void
+print_variable_data_base ()
+{
+  puts ("\n# Variables\n");
+
+  print_variable_set (&global_variable_set, "");
+}
+
+
+/* Print all the local variables of FILE.  */
+
+void
+print_file_variables (file)
+     struct file *file;
+{
+  if (file->variables != 0)
+    print_variable_set (file->variables->set, "# ");
+}
+\f
+struct output_state
+  {
+    char *buffer;
+    unsigned int length;
+  };
+
+/* Save the current variable output state and return a pointer
+   to storage describing it.  Then reset the output state.  */
+
+char *
+save_variable_output ()
+{
+  struct output_state *state;
+
+  state = (struct output_state *) xmalloc (sizeof (struct output_state));
+  state->buffer = variable_buffer;
+  state->length = variable_buffer_length;
+
+  variable_buffer = 0;
+  variable_buffer_length = 0;
+
+  return (char *) state;
+}
+
+/* Restore the variable output state saved in SAVE.  */
+
+void
+restore_variable_output (save)
+     char *save;
+{
+  register struct output_state *state = (struct output_state *) save;
+
+  if (variable_buffer != 0)
+    free (variable_buffer);
+
+  variable_buffer = state->buffer;
+  variable_buffer_length = state->length;
+
+  free ((char *) state);
+}