summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>1991-10-07 22:04:20 +0000
committerRoland McGrath <roland@redhat.com>1991-10-07 22:04:20 +0000
commit97d38d08176611af5e10ab84e4c812962bf98600 (patch)
tree67d966aad231da27fcfb620aa4e4573c1d231c48
parentbd3a693fd4cd0f91bc0a6e598dac260438d2acbd (diff)
downloadgunmake-97d38d08176611af5e10ab84e4c812962bf98600.tar.gz
Initial revision
-rw-r--r--misc.c598
-rw-r--r--variable.c768
2 files changed, 1366 insertions, 0 deletions
diff --git a/misc.c b/misc.c
new file mode 100644
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);
+}
+
+/* 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);
+ }
+}
+
+/* Print N spaces (used by DEBUGPR for target-depth). */
+
+void
+print_spaces (n)
+ register unsigned int n;
+{
+ while (n-- > 0)
+ putchar (' ');
+}
+
+
+/* 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;
+}
+
+/* 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 */
+}
+
+/* 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;
+}
+
+/* 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;
+}
+
+/* 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;
+}
+
+/* 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;
+}
+
+#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
+
+#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
index 0000000..019d802
--- /dev/null
+++ b/variable.c
@@ -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;
+
+/* 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);
+}
+
+/* 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;
+}
+
+/* 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;
+ }
+}
+
+/* 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;
+}
+
+/* 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;
+ }
+}
+
+/* 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);
+ }
+}
+
+/* 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;
+}
+
+/* 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;
+}
+
+/* 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;
+}
+
+/* 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, "# ");
+}
+
+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);
+}