mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-23 05:42:29 +00:00
Flatten GNU Make directory structure
This commit is contained in:
parent
04d39d47f1
commit
58d74ede4c
113 changed files with 241 additions and 7671 deletions
666
third_party/make/misc.c
vendored
Normal file
666
third_party/make/misc.c
vendored
Normal file
|
@ -0,0 +1,666 @@
|
|||
/* clang-format off */
|
||||
/* Miscellaneous generic support functions for GNU Make.
|
||||
Copyright (C) 1988-2020 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 "third_party/make/makeint.h"
|
||||
#include "third_party/make/filedef.h"
|
||||
#include "third_party/make/dep.h"
|
||||
#include "third_party/make/debug.h"
|
||||
#include "libc/calls/calls.h"
|
||||
|
||||
/* GNU make no longer supports pre-ANSI89 environments. */
|
||||
|
||||
/* 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 *out = line;
|
||||
char *in = line;
|
||||
char *q;
|
||||
|
||||
q = strchr(in, '\n');
|
||||
if (q == 0)
|
||||
return;
|
||||
|
||||
do
|
||||
{
|
||||
char *p = q;
|
||||
int i;
|
||||
size_t out_line_length;
|
||||
|
||||
if (q > line && q[-1] == '\\')
|
||||
{
|
||||
/* Search for more backslashes. */
|
||||
i = -2;
|
||||
while (&p[i] >= line && p[i] == '\\')
|
||||
--i;
|
||||
++i;
|
||||
}
|
||||
else
|
||||
i = 0;
|
||||
|
||||
/* The number of backslashes is now -I, keep half of them. */
|
||||
out_line_length = (p - in) + i - i/2;
|
||||
if (out != in)
|
||||
memmove (out, in, out_line_length);
|
||||
out += out_line_length;
|
||||
|
||||
/* When advancing IN, skip the newline too. */
|
||||
in = q + 1;
|
||||
|
||||
if (i & 1)
|
||||
{
|
||||
/* Backslash/newline handling:
|
||||
In traditional GNU make all trailing whitespace, consecutive
|
||||
backslash/newlines, and any leading non-newline whitespace on the
|
||||
next line is reduced to a single space.
|
||||
In POSIX, each backslash/newline and is replaced by a space. */
|
||||
while (ISBLANK (*in))
|
||||
++in;
|
||||
if (! posix_pedantic)
|
||||
while (out > line && ISBLANK (out[-1]))
|
||||
--out;
|
||||
*out++ = ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If the newline isn't quoted, put it in the output. */
|
||||
*out++ = '\n';
|
||||
}
|
||||
|
||||
q = strchr(in, '\n');
|
||||
}
|
||||
while (q);
|
||||
|
||||
memmove(out, in, strlen(in) + 1);
|
||||
}
|
||||
|
||||
/* 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 size_t rlen = 0;
|
||||
static char *result = NULL;
|
||||
size_t ri = 0;
|
||||
va_list args;
|
||||
|
||||
va_start (args, num);
|
||||
|
||||
while (num-- > 0)
|
||||
{
|
||||
const char *s = va_arg (args, const char *);
|
||||
size_t 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;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
xstrndup (const char *str, size_t length)
|
||||
{
|
||||
char *result;
|
||||
|
||||
#ifdef HAVE_STRNDUP
|
||||
result = strndup (str, length);
|
||||
if (result == 0)
|
||||
out_of_memory ();
|
||||
#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)
|
||||
{
|
||||
END_OF_TOKEN (s);
|
||||
return (char *)s;
|
||||
}
|
||||
|
||||
/* Return the address of the first nonwhitespace or null in the string S. */
|
||||
|
||||
char *
|
||||
next_token (const char *s)
|
||||
{
|
||||
NEXT_TOKEN (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, size_t *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;
|
||||
}
|
||||
|
||||
/* Write a BUFFER of size LEN to file descriptor FD.
|
||||
Retry short writes from EINTR. Return LEN, or -1 on error. */
|
||||
ssize_t
|
||||
writebuf (int fd, const void *buffer, size_t len)
|
||||
{
|
||||
const char *msg = buffer;
|
||||
size_t l = len;
|
||||
while (l)
|
||||
{
|
||||
ssize_t r;
|
||||
|
||||
EINTRLOOP (r, write (fd, msg, l));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
l -= r;
|
||||
msg += r;
|
||||
}
|
||||
|
||||
return (ssize_t)len;
|
||||
}
|
||||
|
||||
/* Read until we get LEN bytes from file descriptor FD, into BUFFER.
|
||||
Retry short reads on EINTR. If we get an error, return it.
|
||||
Return 0 at EOF. */
|
||||
ssize_t
|
||||
readbuf (int fd, void *buffer, size_t len)
|
||||
{
|
||||
char *msg = buffer;
|
||||
while (len)
|
||||
{
|
||||
ssize_t r;
|
||||
|
||||
EINTRLOOP (r, read (fd, msg, len));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
len -= r;
|
||||
msg += r;
|
||||
}
|
||||
|
||||
return (ssize_t)(msg - (char*)buffer);
|
||||
}
|
||||
|
||||
|
||||
/* 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 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_ns (t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef MAKE_MAINTAINER_MODE
|
||||
|
||||
void
|
||||
spin (const char* type)
|
||||
{
|
||||
char filenm[256];
|
||||
struct stat dummy;
|
||||
|
||||
sprintf (filenm, ".make-spin-%s", type);
|
||||
|
||||
if (stat (filenm, &dummy) == 0)
|
||||
{
|
||||
fprintf (stderr, "SPIN on %s\n", filenm);
|
||||
do
|
||||
sleep (1);
|
||||
while (stat (filenm, &dummy) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Provide support for temporary files. */
|
||||
|
||||
#ifndef HAVE_STDLIB_H
|
||||
# ifdef HAVE_MKSTEMP
|
||||
int mkstemp (char *template);
|
||||
# else
|
||||
char *mktemp (char *template);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_UMASK
|
||||
mode_t
|
||||
umask (mode_t mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
FILE *
|
||||
get_tmpfile (char **name, const char *template)
|
||||
{
|
||||
FILE *file;
|
||||
#ifdef HAVE_FDOPEN
|
||||
int fd;
|
||||
#endif
|
||||
|
||||
/* Preserve the current umask, and set a restrictive one for temp files. */
|
||||
mode_t mask = umask (0077);
|
||||
|
||||
#if defined(HAVE_MKSTEMP) || defined(HAVE_MKTEMP)
|
||||
# define TEMPLATE_LEN strlen (template)
|
||||
#else
|
||||
# define TEMPLATE_LEN L_tmpnam
|
||||
#endif
|
||||
*name = xmalloc (TEMPLATE_LEN + 1);
|
||||
strcpy (*name, template);
|
||||
|
||||
#if defined(HAVE_MKSTEMP) && defined(HAVE_FDOPEN)
|
||||
/* It's safest to use mkstemp(), if we can. */
|
||||
EINTRLOOP (fd, mkstemp (*name));
|
||||
if (fd == -1)
|
||||
file = NULL;
|
||||
else
|
||||
file = fdopen (fd, "w");
|
||||
#else
|
||||
# ifdef HAVE_MKTEMP
|
||||
(void) mktemp (*name);
|
||||
# else
|
||||
(void) tmpnam (*name);
|
||||
# endif
|
||||
|
||||
# ifdef HAVE_FDOPEN
|
||||
/* Can't use mkstemp(), but guard against a race condition. */
|
||||
EINTRLOOP (fd, open (*name, O_CREAT|O_EXCL|O_WRONLY, 0600));
|
||||
if (fd == -1)
|
||||
return 0;
|
||||
file = fdopen (fd, "w");
|
||||
# else
|
||||
/* Not secure, but what can we do? */
|
||||
file = fopen (*name, "w");
|
||||
# endif
|
||||
#endif
|
||||
|
||||
umask (mask);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
#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. */
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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 /* 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
|
Loading…
Add table
Add a link
Reference in a new issue