summaryrefslogtreecommitdiff
path: root/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'misc.c')
-rw-r--r--misc.c726
1 files changed, 726 insertions, 0 deletions
diff --git a/misc.c b/misc.c
new file mode 100644
index 0000000..00a3210
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,726 @@
+/* Miscellaneous generic support functions for GNU Make.
+Copyright (C) 1988-2014 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 3 of the License, 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
+this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "makeint.h"
+#include "filedef.h"
+#include "dep.h"
+#include "debug.h"
+
+/* GNU make no longer supports pre-ANSI89 environments. */
+
+#include <stdarg.h>
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#else
+# include <sys/file.h>
+#endif
+
+/* 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 (const void *v1, const void *v2)
+{
+ const char *s1 = *((char **)v1);
+ const char *s2 = *((char **)v2);
+
+ 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 (char *line)
+{
+ char *in, *out, *p;
+
+ in = strchr (line, '\n');
+ if (in == 0)
+ return;
+
+ out = in;
+ while (out > line && 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. */
+ unsigned int backslash = 0;
+ unsigned int 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 (backslash)
+ {
+ /* Backslash/newline handling:
+ In traditional GNU make all trailing whitespace, consecutive
+ backslash/newlines, and any leading whitespace on the next line
+ is reduced to a single space.
+ In POSIX, each backslash/newline and is replaced by a space. */
+ in = next_token (in);
+ if (! posix_pedantic)
+ while (out > line && isblank ((unsigned char)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';
+}
+
+/* Print N spaces (used in debug for target-depth). */
+
+void
+print_spaces (unsigned int n)
+{
+ while (n-- > 0)
+ putchar (' ');
+}
+
+
+/* Return a string whose contents concatenate the NUM strings provided
+ This string lives in static, re-used memory. */
+
+const char *
+concat (unsigned int num, ...)
+{
+ static unsigned int rlen = 0;
+ static char *result = NULL;
+ unsigned int ri = 0;
+ va_list args;
+
+ va_start (args, num);
+
+ while (num-- > 0)
+ {
+ const char *s = va_arg (args, const char *);
+ unsigned int l = xstrlen (s);
+
+ if (l == 0)
+ continue;
+
+ if (ri + l > rlen)
+ {
+ rlen = ((rlen ? rlen : 60) + l) * 2;
+ result = xrealloc (result, rlen);
+ }
+
+ memcpy (result + ri, s, l);
+ ri += l;
+ }
+
+ va_end (args);
+
+ /* Get some more memory if we don't have enough space for the
+ terminating '\0'. */
+ if (ri == rlen)
+ {
+ rlen = (rlen ? rlen : 60) * 2;
+ result = xrealloc (result, rlen);
+ }
+
+ result[ri] = '\0';
+
+ return result;
+}
+
+
+#ifndef HAVE_STRERROR
+#undef strerror
+char *
+strerror (int errnum)
+{
+ extern int errno, sys_nerr;
+#ifndef __DECC
+ extern char *sys_errlist[];
+#endif
+ static char buf[] = "Unknown error 12345678901234567890";
+
+ if (errno < sys_nerr)
+ return sys_errlist[errnum];
+
+ sprintf (buf, _("Unknown error %d"), errnum);
+ return buf;
+}
+#endif
+
+/* Like malloc but get fatal error if memory is exhausted. */
+/* Don't bother if we're using dmalloc; it provides these for us. */
+
+#ifndef HAVE_DMALLOC_H
+
+#undef xmalloc
+#undef xcalloc
+#undef xrealloc
+#undef xstrdup
+
+void *
+xmalloc (unsigned int size)
+{
+ /* Make sure we don't allocate 0, for pre-ISO implementations. */
+ void *result = malloc (size ? size : 1);
+ if (result == 0)
+ OUT_OF_MEM();
+ return result;
+}
+
+
+void *
+xcalloc (unsigned int size)
+{
+ /* Make sure we don't allocate 0, for pre-ISO implementations. */
+ void *result = calloc (size ? size : 1, 1);
+ if (result == 0)
+ OUT_OF_MEM();
+ return result;
+}
+
+
+void *
+xrealloc (void *ptr, unsigned int size)
+{
+ void *result;
+
+ /* Some older implementations of realloc() don't conform to ISO. */
+ if (! size)
+ size = 1;
+ result = ptr ? realloc (ptr, size) : malloc (size);
+ if (result == 0)
+ OUT_OF_MEM();
+ return result;
+}
+
+
+char *
+xstrdup (const char *ptr)
+{
+ char *result;
+
+#ifdef HAVE_STRDUP
+ result = strdup (ptr);
+#else
+ result = malloc (strlen (ptr) + 1);
+#endif
+
+ if (result == 0)
+ OUT_OF_MEM();
+
+#ifdef HAVE_STRDUP
+ return result;
+#else
+ return strcpy (result, ptr);
+#endif
+}
+
+#endif /* HAVE_DMALLOC_H */
+
+char *
+xstrndup (const char *str, unsigned int length)
+{
+ char *result;
+
+#ifdef HAVE_STRNDUP
+ result = strndup (str, length);
+ if (result == 0)
+ OUT_OF_MEM();
+#else
+ result = xmalloc (length + 1);
+ if (length > 0)
+ strncpy (result, str, length);
+ result[length] = '\0';
+#endif
+
+ return result;
+}
+
+
+/* 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 (const char *s, const char *limit, int c)
+{
+ while (s < limit)
+ if (*s++ == c)
+ return (char *)(s - 1);
+
+ return 0;
+}
+
+/* Return the address of the first whitespace or null in the string S. */
+
+char *
+end_of_token (const char *s)
+{
+ while (! STOP_SET (*s, MAP_BLANK|MAP_NUL))
+ ++s;
+ return (char *)s;
+}
+
+/* Return the address of the first nonwhitespace or null in the string S. */
+
+char *
+next_token (const char *s)
+{
+ while (isblank ((unsigned char)*s))
+ ++s;
+ return (char *)s;
+}
+
+/* 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. Set *PTR to the end
+ of the token, so this function can be called repeatedly in a loop. */
+
+char *
+find_next_token (const char **ptr, unsigned int *lengthptr)
+{
+ const char *p = next_token (*ptr);
+
+ if (*p == '\0')
+ return 0;
+
+ *ptr = end_of_token (p);
+ if (lengthptr != 0)
+ *lengthptr = *ptr - p;
+
+ return (char *)p;
+}
+
+
+/* Copy a chain of 'struct dep'. For 2nd expansion deps, dup the name. */
+
+struct dep *
+copy_dep_chain (const struct dep *d)
+{
+ struct dep *firstnew = 0;
+ struct dep *lastnew = 0;
+
+ while (d != 0)
+ {
+ struct dep *c = xmalloc (sizeof (struct dep));
+ memcpy (c, d, sizeof (struct dep));
+
+ if (c->need_2nd_expansion)
+ c->name = xstrdup (c->name);
+
+ c->next = 0;
+ if (firstnew == 0)
+ firstnew = lastnew = c;
+ else
+ lastnew = lastnew->next = c;
+
+ d = d->next;
+ }
+
+ return firstnew;
+}
+
+/* Free a chain of 'struct dep'. */
+
+void
+free_dep_chain (struct dep *d)
+{
+ while (d != 0)
+ {
+ struct dep *df = d;
+ d = d->next;
+ free_dep (df);
+ }
+}
+
+/* Free a chain of struct nameseq.
+ For struct dep chains use free_dep_chain. */
+
+void
+free_ns_chain (struct nameseq *ns)
+{
+ while (ns != 0)
+ {
+ struct nameseq *t = ns;
+ ns = ns->next;
+ free (t);
+ }
+}
+
+
+#if !HAVE_STRCASECMP && !HAVE_STRICMP && !HAVE_STRCMPI
+/* If we don't have strcasecmp() (from POSIX), or anything that can substitute
+ for it, define our own version. */
+
+int
+strcasecmp (const char *s1, const char *s2)
+{
+ while (1)
+ {
+ int c1 = (int) *(s1++);
+ int c2 = (int) *(s2++);
+
+ if (isalpha (c1))
+ c1 = tolower (c1);
+ if (isalpha (c2))
+ c2 = tolower (c2);
+
+ if (c1 != '\0' && c1 == c2)
+ continue;
+
+ return (c1 - c2);
+ }
+}
+#endif
+
+#if !HAVE_STRNCASECMP && !HAVE_STRNICMP && !HAVE_STRNCMPI
+/* If we don't have strncasecmp() (from POSIX), or anything that can
+ substitute for it, define our own version. */
+
+int
+strncasecmp (const char *s1, const char *s2, int n)
+{
+ while (n-- > 0)
+ {
+ int c1 = (int) *(s1++);
+ int c2 = (int) *(s2++);
+
+ if (isalpha (c1))
+ c1 = tolower (c1);
+ if (isalpha (c2))
+ c2 = tolower (c2);
+
+ if (c1 != '\0' && c1 == c2)
+ continue;
+
+ return (c1 - c2);
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef GETLOADAVG_PRIVILEGED
+
+#ifdef POSIX
+
+/* Hopefully if a system says it's POSIX.1 and has the setuid and setgid
+ functions, they work as POSIX.1 says. Some systems (Alpha OSF/1 1.2,
+ for example) which claim to be POSIX.1 also have the BSD setreuid and
+ setregid functions, but they don't work as in BSD and only the POSIX.1
+ way works. */
+
+#undef HAVE_SETREUID
+#undef HAVE_SETREGID
+
+#else /* Not POSIX. */
+
+/* Some POSIX.1 systems have the seteuid and setegid functions. In a
+ POSIX-like system, they are the best thing to use. However, some
+ non-POSIX systems have them too but they do not work in the POSIX style
+ and we must use setreuid and setregid instead. */
+
+#undef HAVE_SETEUID
+#undef HAVE_SETEGID
+
+#endif /* POSIX. */
+
+#ifndef HAVE_UNISTD_H
+extern int getuid (), getgid (), geteuid (), getegid ();
+extern int setuid (), setgid ();
+#ifdef HAVE_SETEUID
+extern int seteuid ();
+#else
+#ifdef HAVE_SETREUID
+extern int setreuid ();
+#endif /* Have setreuid. */
+#endif /* Have seteuid. */
+#ifdef HAVE_SETEGID
+extern int setegid ();
+#else
+#ifdef HAVE_SETREGID
+extern int setregid ();
+#endif /* Have setregid. */
+#endif /* Have setegid. */
+#endif /* No <unistd.h>. */
+
+/* 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;
+
+
+/* Under -d, write a message describing the current IDs. */
+
+static void
+log_access (const char *flavor)
+{
+ if (! ISDB (DB_JOBS))
+ return;
+
+ /* All the other debugging messages go to stdout,
+ but we write this one to stderr because it might be
+ run in a child fork whose stdout is piped. */
+
+ fprintf (stderr, _("%s: user %lu (real %lu), group %lu (real %lu)\n"),
+ flavor, (unsigned long) geteuid (), (unsigned long) getuid (),
+ (unsigned long) getegid (), (unsigned long) getgid ());
+ fflush (stderr);
+}
+
+
+static void
+init_access (void)
+{
+#ifndef VMS
+ 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");
+
+ log_access (_("Initialized access"));
+
+ current_access = make;
+#endif
+}
+
+#endif /* GETLOADAVG_PRIVILEGED */
+
+/* Give the process appropriate permissions for access to
+ user data (i.e., to stat files, or to spawn a child process). */
+void
+user_access (void)
+{
+#ifdef GETLOADAVG_PRIVILEGED
+
+ 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. */
+
+#ifdef HAVE_SETEUID
+
+ /* Modern systems have the seteuid/setegid calls which set only the
+ effective IDs, which is ideal. */
+
+ if (seteuid (user_uid) < 0)
+ pfatal_with_name ("user_access: seteuid");
+
+#else /* Not HAVE_SETEUID. */
+
+#ifndef HAVE_SETREUID
+
+ /* 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 ("user_access: setuid");
+
+#else /* HAVE_SETREUID. */
+
+ /* 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 ("user_access: setreuid");
+
+#endif /* Not HAVE_SETREUID. */
+#endif /* HAVE_SETEUID. */
+
+#ifdef HAVE_SETEGID
+ if (setegid (user_gid) < 0)
+ pfatal_with_name ("user_access: setegid");
+#else
+#ifndef HAVE_SETREGID
+ if (setgid (user_gid) < 0)
+ pfatal_with_name ("user_access: setgid");
+#else
+ if (setregid (make_gid, user_gid) < 0)
+ pfatal_with_name ("user_access: setregid");
+#endif
+#endif
+
+ current_access = user;
+
+ log_access (_("User access"));
+
+#endif /* GETLOADAVG_PRIVILEGED */
+}
+
+/* Give the process appropriate permissions for access to
+ make data (i.e., the load average). */
+void
+make_access (void)
+{
+#ifdef GETLOADAVG_PRIVILEGED
+
+ if (!access_inited)
+ init_access ();
+
+ if (current_access == make)
+ return;
+
+ /* See comments in user_access, above. */
+
+#ifdef HAVE_SETEUID
+ if (seteuid (make_uid) < 0)
+ pfatal_with_name ("make_access: seteuid");
+#else
+#ifndef HAVE_SETREUID
+ if (setuid (make_uid) < 0)
+ pfatal_with_name ("make_access: setuid");
+#else
+ if (setreuid (user_uid, make_uid) < 0)
+ pfatal_with_name ("make_access: setreuid");
+#endif
+#endif
+
+#ifdef HAVE_SETEGID
+ if (setegid (make_gid) < 0)
+ pfatal_with_name ("make_access: setegid");
+#else
+#ifndef HAVE_SETREGID
+ if (setgid (make_gid) < 0)
+ pfatal_with_name ("make_access: setgid");
+#else
+ if (setregid (user_gid, make_gid) < 0)
+ pfatal_with_name ("make_access: setregid");
+#endif
+#endif
+
+ current_access = make;
+
+ log_access (_("Make access"));
+
+#endif /* GETLOADAVG_PRIVILEGED */
+}
+
+/* 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 (void)
+{
+#ifdef GETLOADAVG_PRIVILEGED
+
+ if (!access_inited)
+ abort ();
+
+ /* Set both the real and effective UID and GID to the user's.
+ They cannot be changed back to make's. */
+
+#ifndef HAVE_SETREUID
+ if (setuid (user_uid) < 0)
+ pfatal_with_name ("child_access: setuid");
+#else
+ if (setreuid (user_uid, user_uid) < 0)
+ pfatal_with_name ("child_access: setreuid");
+#endif
+
+#ifndef HAVE_SETREGID
+ if (setgid (user_gid) < 0)
+ pfatal_with_name ("child_access: setgid");
+#else
+ if (setregid (user_gid, user_gid) < 0)
+ pfatal_with_name ("child_access: setregid");
+#endif
+
+ log_access (_("Child access"));
+
+#endif /* GETLOADAVG_PRIVILEGED */
+}
+
+#ifdef NEED_GET_PATH_MAX
+unsigned int
+get_path_max (void)
+{
+ static unsigned int value;
+
+ if (value == 0)
+ {
+ long int x = pathconf ("/", _PC_PATH_MAX);
+ if (x > 0)
+ value = x;
+ else
+ return MAXPATHLEN;
+ }
+
+ return value;
+}
+#endif