2021-10-26 17:49:30 +00:00
|
|
|
|
/* Miscellaneous generic support functions for GNU Make.
|
2023-12-01 04:50:10 +00:00
|
|
|
|
Copyright (C) 1988-2023 Free Software Foundation, Inc.
|
2021-10-26 17:49:30 +00:00
|
|
|
|
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
|
2023-12-01 04:50:10 +00:00
|
|
|
|
this program. If not, see <https://www.gnu.org/licenses/>. */
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
#include "makeint.h"
|
|
|
|
|
#include "filedef.h"
|
|
|
|
|
#include "dep.h"
|
|
|
|
|
#include "os.h"
|
|
|
|
|
#include "debug.h"
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
|
|
|
|
|
#ifdef WINDOWS32
|
|
|
|
|
# include <windows.h>
|
|
|
|
|
# include <io.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef __EMX__
|
|
|
|
|
# define INCL_DOS
|
|
|
|
|
# include <os2.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_FCNTL_H
|
|
|
|
|
# include <fcntl.h>
|
|
|
|
|
#else
|
|
|
|
|
# include <sys/file.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
|
make_toui (const char *str, const char **error)
|
|
|
|
|
{
|
|
|
|
|
char *end;
|
|
|
|
|
unsigned long val = strtoul (str, &end, 10);
|
|
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
|
{
|
|
|
|
|
if (str[0] == '\0')
|
|
|
|
|
*error = "Missing value";
|
|
|
|
|
else if (*end != '\0')
|
|
|
|
|
*error = "Invalid value";
|
|
|
|
|
else
|
|
|
|
|
*error = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert val into a string, written to buf. buf must be large enough
|
|
|
|
|
to hold the largest possible value, plus a nul byte. Returns buf.
|
|
|
|
|
We can't use standard PRI* here: those are based on intNN_t types. */
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
make_lltoa (long long val, char *buf)
|
|
|
|
|
{
|
|
|
|
|
sprintf (buf, "%" MK_PRI64_PREFIX "d", val);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
make_ulltoa (unsigned long long val, char *buf)
|
|
|
|
|
{
|
|
|
|
|
sprintf (buf, "%" MK_PRI64_PREFIX "u", val);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Simple random number generator, for use with shuffle.
|
|
|
|
|
This doesn't need to be truly random, just pretty random. Use our own
|
|
|
|
|
implementation rather than relying on the C runtime's rand() so we always
|
|
|
|
|
get the same results for a given seed, regardless of C runtime. */
|
|
|
|
|
|
|
|
|
|
static unsigned int mk_state = 0;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
make_seed (unsigned int seed)
|
|
|
|
|
{
|
|
|
|
|
mk_state = seed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
|
make_rand ()
|
|
|
|
|
{
|
|
|
|
|
/* mk_state must never be 0. */
|
|
|
|
|
if (mk_state == 0)
|
|
|
|
|
mk_state = (unsigned int)(time (NULL) ^ make_pid ()) + 1;
|
|
|
|
|
|
|
|
|
|
/* A simple xorshift RNG. */
|
|
|
|
|
mk_state ^= mk_state << 13;
|
|
|
|
|
mk_state ^= mk_state >> 17;
|
|
|
|
|
mk_state ^= mk_state << 5;
|
|
|
|
|
|
|
|
|
|
return mk_state;
|
|
|
|
|
}
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
|
|
|
|
/* 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:
|
2023-12-01 04:50:10 +00:00
|
|
|
|
In traditional GNU Make all trailing whitespace, consecutive
|
2021-10-26 17:49:30 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
#ifndef HAVE_UNISTD_H
|
|
|
|
|
pid_t getpid ();
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
pid_t make_pid ()
|
|
|
|
|
{
|
|
|
|
|
return getpid ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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 (size_t size)
|
|
|
|
|
{
|
|
|
|
|
/* Make sure we don't allocate 0, for pre-ISO implementations. */
|
|
|
|
|
void *result = malloc (size ? size : 1);
|
|
|
|
|
if (result == 0)
|
|
|
|
|
out_of_memory ();
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
xcalloc (size_t size)
|
|
|
|
|
{
|
|
|
|
|
/* Make sure we don't allocate 0, for pre-ISO implementations. */
|
|
|
|
|
void *result = calloc (size ? size : 1, 1);
|
|
|
|
|
if (result == 0)
|
|
|
|
|
out_of_memory ();
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
xrealloc (void *ptr, size_t 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_memory ();
|
|
|
|
|
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_memory ();
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_STRDUP
|
|
|
|
|
return result;
|
|
|
|
|
#else
|
|
|
|
|
return strcpy (result, ptr);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* HAVE_DMALLOC_H */
|
|
|
|
|
|
2021-10-26 17:49:30 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
#ifndef HAVE_MEMRCHR
|
|
|
|
|
void *
|
|
|
|
|
memrchr(const void* str, int ch, size_t len)
|
|
|
|
|
{
|
|
|
|
|
const char* sp = str;
|
|
|
|
|
const char* cp = sp;
|
|
|
|
|
|
|
|
|
|
if (len == 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
cp += len - 1;
|
|
|
|
|
|
|
|
|
|
while (cp[0] != ch)
|
|
|
|
|
{
|
|
|
|
|
if (cp == sp)
|
|
|
|
|
return NULL;
|
|
|
|
|
--cp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (void*)cp;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
2023-12-01 04:50:10 +00:00
|
|
|
|
while (! END_OF_TOKEN (*s))
|
|
|
|
|
++s;
|
2021-10-26 17:49:30 +00:00
|
|
|
|
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
|
2023-12-01 04:50:10 +00:00
|
|
|
|
#ifdef WINDOWS32
|
|
|
|
|
Sleep (1000);
|
|
|
|
|
#else
|
2021-10-26 17:49:30 +00:00
|
|
|
|
sleep (1);
|
2023-12-01 04:50:10 +00:00
|
|
|
|
#endif
|
2021-10-26 17:49:30 +00:00
|
|
|
|
while (stat (filenm, &dummy) == 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
void
|
|
|
|
|
dbg (const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
FILE *fp = fopen ("/tmp/gmkdebug.log", "a+");
|
|
|
|
|
va_list args;
|
|
|
|
|
char buf[4096];
|
|
|
|
|
|
|
|
|
|
va_start (args, fmt);
|
|
|
|
|
vsprintf (buf, fmt, args);
|
|
|
|
|
va_end (args);
|
|
|
|
|
|
|
|
|
|
fprintf(fp, "%u: %s\n", (unsigned) make_pid (), buf);
|
|
|
|
|
fflush (fp);
|
|
|
|
|
fclose (fp);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-26 17:49:30 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Provide support for temporary files. */
|
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
#ifdef VMS
|
|
|
|
|
# define DEFAULT_TMPFILE "sys$scratch:gnv$make_cmdXXXXXX.com"
|
2021-10-26 17:49:30 +00:00
|
|
|
|
#else
|
2023-12-01 04:50:10 +00:00
|
|
|
|
# define DEFAULT_TMPFILE "GmXXXXXX"
|
2021-10-26 17:49:30 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
const char *
|
|
|
|
|
get_tmpdir ()
|
|
|
|
|
{
|
|
|
|
|
static const char *tmpdir = NULL;
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
if (!tmpdir)
|
|
|
|
|
{
|
|
|
|
|
#if defined (__MSDOS__) || defined (WINDOWS32) || defined (__EMX__)
|
|
|
|
|
# define TMP_EXTRAS "TMP", "TEMP",
|
|
|
|
|
#else
|
|
|
|
|
# define TMP_EXTRAS
|
2021-10-26 17:49:30 +00:00
|
|
|
|
#endif
|
2023-12-01 04:50:10 +00:00
|
|
|
|
const char *tlist[] = { "MAKE_TMPDIR", "TMPDIR", TMP_EXTRAS NULL };
|
|
|
|
|
const char **tp;
|
|
|
|
|
unsigned int found = 0;
|
|
|
|
|
|
|
|
|
|
for (tp = tlist; *tp; ++tp)
|
|
|
|
|
if ((tmpdir = getenv (*tp)) && *tmpdir != '\0')
|
|
|
|
|
{
|
|
|
|
|
struct stat st;
|
|
|
|
|
int r;
|
|
|
|
|
found = 1;
|
|
|
|
|
EINTRLOOP(r, stat (tmpdir, &st));
|
|
|
|
|
if (r < 0)
|
|
|
|
|
OSSS (error, NILF,
|
|
|
|
|
_("%s value %s: %s"), *tp, tmpdir, strerror (errno));
|
|
|
|
|
else if (! S_ISDIR (st.st_mode))
|
|
|
|
|
OSS (error, NILF,
|
|
|
|
|
_("%s value %s: not a directory"), *tp, tmpdir);
|
|
|
|
|
else
|
|
|
|
|
return tmpdir;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmpdir = DEFAULT_TMPDIR;
|
|
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
|
OS (error, NILF, _("using default temporary directory '%s'"), tmpdir);
|
|
|
|
|
}
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
return tmpdir;
|
2021-10-26 17:49:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
static char *
|
|
|
|
|
get_tmptemplate ()
|
|
|
|
|
{
|
|
|
|
|
const char *tmpdir = get_tmpdir ();
|
|
|
|
|
char *template;
|
|
|
|
|
char *cp;
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
template = xmalloc (strlen (tmpdir) + CSTRLEN (DEFAULT_TMPFILE) + 2);
|
|
|
|
|
cp = stpcpy (template, tmpdir);
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
#if !defined VMS
|
|
|
|
|
/* It's not possible for tmpdir to be empty. */
|
|
|
|
|
if (! ISDIRSEP (cp[-1]))
|
|
|
|
|
*(cp++) = '/';
|
|
|
|
|
#endif
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
strcpy (cp, DEFAULT_TMPFILE);
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
return template;
|
|
|
|
|
}
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
#if !HAVE_MKSTEMP || !HAVE_FDOPEN
|
|
|
|
|
/* Generate a temporary filename. This is not safe as another program could
|
|
|
|
|
snipe our filename after we've generated it: use this only on systems
|
|
|
|
|
without more secure alternatives. */
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
static char *
|
|
|
|
|
get_tmppath ()
|
|
|
|
|
{
|
|
|
|
|
char *path;
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
# ifdef HAVE_MKTEMP
|
|
|
|
|
path = get_tmptemplate ();
|
|
|
|
|
if (*mktemp (path) == '\0')
|
|
|
|
|
{
|
|
|
|
|
OSS (error, NILF,
|
|
|
|
|
_("cannot generate temp path from %s: %s"), path, strerror (errno));
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
# else
|
|
|
|
|
path = xmalloc (L_tmpnam + 1);
|
|
|
|
|
if (tmpnam (path) == NULL)
|
|
|
|
|
{
|
|
|
|
|
OS (error, NILF,
|
|
|
|
|
_("cannot generate temp name: %s"), strerror (errno));
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
# endif
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
/* Generate a temporary file and return an fd for it. If name is NULL then
|
|
|
|
|
the temp file is anonymous and will be deleted when the process exits. If
|
|
|
|
|
name is not null then *name will point to an allocated buffer, or set to
|
|
|
|
|
NULL on failure. */
|
|
|
|
|
int
|
|
|
|
|
get_tmpfd (char **name)
|
2021-10-26 17:49:30 +00:00
|
|
|
|
{
|
2023-12-01 04:50:10 +00:00
|
|
|
|
int fd = -1;
|
|
|
|
|
char *tmpnm;
|
|
|
|
|
mode_t mask;
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
if (name)
|
|
|
|
|
*name = NULL;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* If there's an os-specific way to get an anonymous temp file use it. */
|
|
|
|
|
fd = os_anontmp ();
|
|
|
|
|
if (fd >= 0)
|
|
|
|
|
return fd;
|
|
|
|
|
}
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
/* Preserve the current umask, and set a restrictive one for temp files.
|
|
|
|
|
Only really needed for mkstemp() but won't hurt for the open method. */
|
|
|
|
|
mask = umask (0077);
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
#if defined(HAVE_MKSTEMP)
|
|
|
|
|
tmpnm = get_tmptemplate ();
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
/* It's safest to use mkstemp(), if we can. */
|
|
|
|
|
EINTRLOOP (fd, mkstemp (tmpnm));
|
|
|
|
|
#else
|
|
|
|
|
tmpnm = get_tmppath ();
|
|
|
|
|
if (!tmpnm)
|
|
|
|
|
return -1;
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
/* Can't use mkstemp(), but try to guard against a race condition. */
|
|
|
|
|
EINTRLOOP (fd, open (tmpnm, O_CREAT|O_EXCL|O_RDWR, 0600));
|
|
|
|
|
#endif
|
|
|
|
|
if (fd < 0)
|
|
|
|
|
{
|
|
|
|
|
OSS (error, NILF,
|
|
|
|
|
_("cannot create temporary file %s: %s"), tmpnm, strerror (errno));
|
|
|
|
|
free (tmpnm);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
if (name)
|
|
|
|
|
*name = tmpnm;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int r;
|
|
|
|
|
EINTRLOOP (r, unlink (tmpnm));
|
|
|
|
|
if (r < 0)
|
|
|
|
|
OSS (error, NILF,
|
|
|
|
|
_("cannot unlink temporary file %s: %s"), tmpnm, strerror (errno));
|
|
|
|
|
free (tmpnm);
|
|
|
|
|
}
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
umask (mask);
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
return fd;
|
2021-10-26 17:49:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
/* Return a FILE* for a temporary file, opened in the safest way possible.
|
|
|
|
|
Set name to point to an allocated buffer containing the name of the file,
|
|
|
|
|
or NULL on failure. Note, name cannot be NULL! */
|
|
|
|
|
FILE *
|
|
|
|
|
get_tmpfile (char **name)
|
2021-10-26 17:49:30 +00:00
|
|
|
|
{
|
2023-12-01 04:50:10 +00:00
|
|
|
|
/* Be consistent with tmpfile, which opens as if by "wb+". */
|
|
|
|
|
const char *tmpfile_mode = "wb+";
|
|
|
|
|
FILE *file;
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
#if defined(HAVE_FDOPEN)
|
|
|
|
|
int fd;
|
|
|
|
|
assert (name);
|
|
|
|
|
fd = get_tmpfd (name);
|
|
|
|
|
if (fd < 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
assert (*name);
|
|
|
|
|
|
|
|
|
|
ENULLLOOP (file, fdopen (fd, tmpfile_mode));
|
|
|
|
|
if (file == NULL)
|
|
|
|
|
OSS (error, NILF,
|
|
|
|
|
_("fdopen: temporary file %s: %s"), *name, strerror (errno));
|
|
|
|
|
#else
|
|
|
|
|
/* Preserve the current umask, and set a restrictive one for temp files. */
|
|
|
|
|
mode_t mask = umask (0077);
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
assert (name);
|
|
|
|
|
*name = get_tmppath ();
|
|
|
|
|
if (!*name)
|
|
|
|
|
return NULL;
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
/* Although this fopen is insecure, it is executed only on non-fdopen
|
|
|
|
|
platforms, which should be a rarity nowadays. */
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
ENULLLOOP (file, fopen (*name, tmpfile_mode));
|
|
|
|
|
if (file == NULL)
|
|
|
|
|
{
|
|
|
|
|
OSS (error, NILF,
|
|
|
|
|
_("fopen: temporary file %s: %s"), *name, strerror (errno));
|
|
|
|
|
free (*name);
|
|
|
|
|
*name = NULL;
|
|
|
|
|
}
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
umask (mask);
|
|
|
|
|
#endif
|
2021-10-26 17:49:30 +00:00
|
|
|
|
|
2023-12-01 04:50:10 +00:00
|
|
|
|
return file;
|
|
|
|
|
}
|