mirror of
https://https.git.savannah.gnu.org/git/patch.git
synced 2026-01-28 10:24:39 +00:00
682 lines
14 KiB
C
682 lines
14 KiB
C
/* utility functions for `patch' */
|
|
|
|
/* $Id: util.c,v 1.9 1997/04/14 05:32:30 eggert Exp $ */
|
|
|
|
/*
|
|
Copyright 1986 Larry Wall
|
|
Copyright 1992, 1993, 1997 Free Software Foundation, Inc.
|
|
|
|
This program 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.
|
|
|
|
This program 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; see the file COPYING.
|
|
If not, write to the Free Software Foundation,
|
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#define XTERN extern
|
|
#include <common.h>
|
|
#include <backupfile.h>
|
|
#include <version.h>
|
|
#undef XTERN
|
|
#define XTERN
|
|
#include <util.h>
|
|
|
|
#include <signal.h>
|
|
#if !defined SIGCHLD && defined SIGCLD
|
|
#define SIGCHLD SIGCLD
|
|
#endif
|
|
|
|
#ifdef __STDC__
|
|
# include <stdarg.h>
|
|
# define vararg_start va_start
|
|
#else
|
|
# define vararg_start(ap,p) va_start (ap)
|
|
# if HAVE_VARARGS_H
|
|
# include <varargs.h>
|
|
# else
|
|
typedef char *va_list;
|
|
# define va_dcl int va_alist;
|
|
# define va_start(ap) ((ap) = (va_list) &va_alist)
|
|
# define va_arg(ap, t) (((t *) ((ap) += sizeof (t))) [-1])
|
|
# define va_end(ap)
|
|
# endif
|
|
#endif
|
|
|
|
/* Rename a file, copying it if necessary. */
|
|
|
|
void
|
|
move_file (from, to, backup)
|
|
char const *from, *to;
|
|
int backup;
|
|
{
|
|
register char *s;
|
|
register char *bakname;
|
|
struct stat tost, bakst;
|
|
|
|
if (backup && stat (to, &tost) == 0)
|
|
{
|
|
char *simplename;
|
|
|
|
if (origprae || origbase)
|
|
{
|
|
char const *p = origprae ? origprae : "";
|
|
char const *b = origbase ? origbase : "";
|
|
char const *o = base_name (to);
|
|
size_t plen = strlen (p);
|
|
size_t tlen = o - to;
|
|
size_t blen = strlen (b);
|
|
size_t osize = strlen (o) + 1;
|
|
bakname = xmalloc (plen + tlen + blen + osize);
|
|
memcpy (bakname, p, plen);
|
|
memcpy (bakname + plen, to, tlen);
|
|
memcpy (bakname + plen + tlen, b, blen);
|
|
memcpy (bakname + plen + tlen + blen, o, osize);
|
|
}
|
|
else
|
|
{
|
|
bakname = find_backup_file_name (to);
|
|
if (!bakname)
|
|
memory_fatal ();
|
|
}
|
|
|
|
simplename = base_name (bakname);
|
|
/* Find a backup name that is not the same file.
|
|
Change the first lowercase char into uppercase;
|
|
if that doesn't suffice, chop off the first char and try again. */
|
|
while (stat (bakname, &bakst) == 0
|
|
&& tost.st_dev == bakst.st_dev
|
|
&& tost.st_ino == bakst.st_ino)
|
|
{
|
|
/* Skip initial non-lowercase chars. */
|
|
for (s=simplename; *s && !ISLOWER ((unsigned char) *s); s++)
|
|
continue;
|
|
if (*s)
|
|
*s = toupper ((unsigned char) *s);
|
|
else
|
|
remove_prefix (simplename, 1);
|
|
}
|
|
if (debug & 4)
|
|
say ("Moving %s to %s.\n", to, bakname);
|
|
if (rename (to, bakname) != 0)
|
|
pfatal ("Can't rename `%s' to `%s'", to, bakname);
|
|
free (bakname);
|
|
}
|
|
|
|
if (debug & 4)
|
|
say ("Moving %s to %s.\n", from, to);
|
|
|
|
if (rename (from, to) != 0)
|
|
{
|
|
#ifdef EXDEV
|
|
if (errno == EXDEV)
|
|
{
|
|
if (! backup && unlink (to) != 0
|
|
&& errno != ENOENT && errno != ENOTDIR)
|
|
pfatal ("Can't remove `%s'", to);
|
|
copy_file (from, to);
|
|
if (unlink (from) != 0)
|
|
pfatal ("Can't remove `%s'", from);
|
|
return;
|
|
}
|
|
#endif
|
|
pfatal ("Can't rename `%s' to `%s'", from, to);
|
|
}
|
|
}
|
|
|
|
/* Copy a file. */
|
|
|
|
void
|
|
copy_file(from,to)
|
|
char const *from;
|
|
char const *to;
|
|
{
|
|
int tofd;
|
|
int fromfd = open (from, O_RDONLY);
|
|
long i;
|
|
|
|
if (fromfd < 0)
|
|
pfatal ("can't reopen %s", from);
|
|
tofd = creat (to, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
|
|
if (tofd < 0)
|
|
pfatal ("can't create %s", to);
|
|
while ((i = read (fromfd, buf, bufsize)) > 0)
|
|
if (write (tofd, buf, (size_t) i) != i)
|
|
write_fatal ();
|
|
if (i < 0 || close (fromfd) != 0)
|
|
read_fatal ();
|
|
if (close (tofd) != 0)
|
|
write_fatal ();
|
|
}
|
|
|
|
/* Allocate a unique area for a string. */
|
|
|
|
char *
|
|
savebuf (s, size)
|
|
register char const *s;
|
|
register size_t size;
|
|
{
|
|
register char *rv;
|
|
|
|
assert (s && size);
|
|
rv = malloc (size);
|
|
|
|
if (! rv)
|
|
{
|
|
if (! using_plan_a)
|
|
memory_fatal ();
|
|
}
|
|
else
|
|
memcpy (rv, s, size);
|
|
|
|
return rv;
|
|
}
|
|
|
|
char *
|
|
savestr(s)
|
|
char const *s;
|
|
{
|
|
return savebuf (s, strlen (s) + 1);
|
|
}
|
|
|
|
void
|
|
remove_prefix (p, prefixlen)
|
|
char *p;
|
|
size_t prefixlen;
|
|
{
|
|
char const *s = p + prefixlen;
|
|
while ((*p++ = *s++))
|
|
continue;
|
|
}
|
|
|
|
#if !HAVE_VPRINTF
|
|
#define vfprintf my_vfprintf
|
|
static int vfprintf PARAMS ((FILE *, char const *, va_list));
|
|
static int
|
|
vfprintf (stream, format, args)
|
|
FILE *stream;
|
|
char const *format;
|
|
va_list args;
|
|
{
|
|
#if !HAVE_DOPRNT && HAVE__DOPRINTF
|
|
# define _doprnt _doprintf
|
|
#endif
|
|
#if HAVE_DOPRNT || HAVE__DOPRINTF
|
|
_doprnt (format, args, stream);
|
|
return ferror (stream) ? -1 : 0;
|
|
#else
|
|
int *a = (int *) args;
|
|
return fprintf (stream, format,
|
|
a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]);
|
|
#endif
|
|
}
|
|
#endif /* !HAVE_VPRINTF */
|
|
|
|
/* Terminal output, pun intended. */
|
|
|
|
#ifdef __STDC__
|
|
void
|
|
fatal (char const *format, ...)
|
|
#else
|
|
/*VARARGS1*/ void
|
|
fatal (format, va_alist)
|
|
char const *format;
|
|
va_dcl
|
|
#endif
|
|
{
|
|
va_list args;
|
|
fprintf (stderr, "%s: **** ", program_name);
|
|
vararg_start (args, format);
|
|
vfprintf (stderr, format, args);
|
|
va_end (args);
|
|
putc ('\n', stderr);
|
|
fflush (stderr);
|
|
fatal_exit (0);
|
|
}
|
|
|
|
void
|
|
memory_fatal ()
|
|
{
|
|
fatal ("out of memory");
|
|
}
|
|
|
|
void
|
|
read_fatal ()
|
|
{
|
|
pfatal ("read error");
|
|
}
|
|
|
|
void
|
|
write_fatal ()
|
|
{
|
|
pfatal ("write error");
|
|
}
|
|
|
|
/* Say something from patch, something from the system, then silence . . . */
|
|
|
|
#ifdef __STDC__
|
|
void
|
|
pfatal (char const *format, ...)
|
|
#else
|
|
/*VARARGS1*/ void
|
|
pfatal (format, va_alist)
|
|
char const *format;
|
|
va_dcl
|
|
#endif
|
|
{
|
|
int errnum = errno;
|
|
va_list args;
|
|
fprintf (stderr, "%s: **** ", program_name);
|
|
vararg_start (args, format);
|
|
vfprintf (stderr, format, args);
|
|
va_end (args);
|
|
fflush (stderr);
|
|
errno = errnum;
|
|
perror (" ");
|
|
fflush (stderr);
|
|
fatal_exit (0);
|
|
}
|
|
|
|
/* Tell the user something. */
|
|
|
|
#ifdef __STDC__
|
|
void
|
|
say (char const *format, ...)
|
|
#else
|
|
/*VARARGS1*/ void
|
|
say (format, va_alist)
|
|
char const *format;
|
|
va_dcl
|
|
#endif
|
|
{
|
|
va_list args;
|
|
vararg_start (args, format);
|
|
vfprintf (stdout, format, args);
|
|
va_end (args);
|
|
fflush (stdout);
|
|
}
|
|
|
|
/* Get a response from the user, somehow or other. */
|
|
|
|
#ifdef __STDC__
|
|
void
|
|
ask (char const *format, ...)
|
|
#else
|
|
/*VARARGS1*/ void
|
|
ask (format, va_alist)
|
|
char const *format;
|
|
va_dcl
|
|
#endif
|
|
{
|
|
static int ttyfd = -2;
|
|
int r;
|
|
va_list args;
|
|
|
|
vararg_start (args, format);
|
|
vfprintf (stdout, format, args);
|
|
va_end (args);
|
|
fflush (stdout);
|
|
|
|
if (ttyfd == -2)
|
|
{
|
|
ttyfd = open ("/dev/tty", O_RDONLY);
|
|
if (ttyfd < 0)
|
|
{
|
|
close (ttyfd);
|
|
for (ttyfd = STDERR_FILENO; 0 <= ttyfd; ttyfd--)
|
|
if (isatty (ttyfd))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ttyfd < 0)
|
|
{
|
|
/* No terminal at all -- default it. */
|
|
buf[0] = '\n';
|
|
r = 1;
|
|
}
|
|
else
|
|
{
|
|
r = read (ttyfd, buf, bufsize - 1);
|
|
if (r == 0)
|
|
printf ("EOF\n");
|
|
else if (r < 0)
|
|
{
|
|
close (ttyfd);
|
|
ttyfd = -1;
|
|
r = 0;
|
|
}
|
|
}
|
|
|
|
buf[r] = '\0';
|
|
}
|
|
|
|
/* How to handle certain events when not in a critical region. */
|
|
|
|
#define NUM_SIGS (sizeof (sigs) / sizeof (*sigs))
|
|
static int const sigs[] = {
|
|
#ifdef SIGHUP
|
|
SIGHUP,
|
|
#endif
|
|
#ifdef SIGTERM
|
|
SIGTERM,
|
|
#endif
|
|
#ifdef SIGXCPU
|
|
SIGXCPU,
|
|
#endif
|
|
#ifdef SIGXFSZ
|
|
SIGXFSZ,
|
|
#endif
|
|
SIGINT,
|
|
SIGPIPE
|
|
};
|
|
|
|
#if !HAVE_SIGPROCMASK
|
|
#define sigset_t int
|
|
#define sigemptyset(s) (*(s) = 0)
|
|
#ifndef sigmask
|
|
#define sigmask(sig) (1 << ((sig) - 1))
|
|
#endif
|
|
#define sigaddset(s, sig) (*(s) |= sigmask (sig))
|
|
#define sigismember(s, sig) ((*(s) & sigmask (sig)) != 0)
|
|
#ifndef SIG_BLOCK
|
|
#define SIG_BLOCK 0
|
|
#endif
|
|
#ifndef SIG_UNBLOCK
|
|
#define SIG_UNBLOCK (SIG_BLOCK + 1)
|
|
#endif
|
|
#ifndef SIG_SETMASK
|
|
#define SIG_SETMASK (SIG_BLOCK + 2)
|
|
#endif
|
|
#define sigprocmask(how, n, o) \
|
|
((how) == SIG_BLOCK \
|
|
? ((o) ? *(o) = sigblock (*(n)) : sigblock (*(n))) \
|
|
: (how) == SIG_UNBLOCK \
|
|
? sigsetmask (((o) ? *(o) = sigblock (0) : sigblock (0)) & ~*(n)) \
|
|
: (o ? *(o) = sigsetmask (*(n)) : sigsetmask (*(n))))
|
|
#if !HAVE_SIGSETMASK
|
|
#define sigblock(mask) 0
|
|
#define sigsetmask(mask) 0
|
|
#endif
|
|
#endif
|
|
|
|
static sigset_t initial_signal_mask;
|
|
static sigset_t signals_to_block;
|
|
|
|
#if ! HAVE_SIGACTION
|
|
static RETSIGTYPE fatal_exit_handler PARAMS ((int)) __attribute__ ((noreturn));
|
|
static RETSIGTYPE
|
|
fatal_exit_handler (sig)
|
|
int sig;
|
|
{
|
|
signal (sig, SIG_IGN);
|
|
fatal_exit (sig);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
set_signals(reset)
|
|
int reset;
|
|
{
|
|
int i;
|
|
#if HAVE_SIGACTION
|
|
struct sigaction initial_act, fatal_act;
|
|
fatal_act.sa_handler = fatal_exit;
|
|
sigemptyset (&fatal_act.sa_mask);
|
|
fatal_act.sa_flags = 0;
|
|
#define setup_handler(sig) sigaction (sig, &fatal_act, (struct sigaction *) 0)
|
|
#else
|
|
#define setup_handler(sig) signal (sig, fatal_exit_handler)
|
|
#endif
|
|
|
|
if (!reset)
|
|
{
|
|
#ifdef SIGCHLD
|
|
/* System V fork+wait does not work if SIGCHLD is ignored. */
|
|
signal (SIGCHLD, SIG_DFL);
|
|
#endif
|
|
sigemptyset (&signals_to_block);
|
|
for (i = 0; i < NUM_SIGS; i++)
|
|
{
|
|
int ignoring_signal;
|
|
#if HAVE_SIGACTION
|
|
if (sigaction (sigs[i], (struct sigaction *) 0, &initial_act) != 0)
|
|
continue;
|
|
ignoring_signal = initial_act.sa_handler == SIG_IGN;
|
|
#else
|
|
ignoring_signal = signal (sigs[i], SIG_IGN) == SIG_IGN;
|
|
#endif
|
|
if (! ignoring_signal)
|
|
{
|
|
sigaddset (&signals_to_block, sigs[i]);
|
|
setup_handler (sigs[i]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Undo the effect of ignore_signals. */
|
|
#if HAVE_SIGPROCMASK || HAVE_SIGSETMASK
|
|
sigprocmask (SIG_SETMASK, &initial_signal_mask, (sigset_t *) 0);
|
|
#else
|
|
for (i = 0; i < NUM_SIGS; i++)
|
|
if (sigismember (&signals_to_block, sigs[i]))
|
|
setup_handler (sigs[i]);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* How to handle certain events when in a critical region. */
|
|
|
|
void
|
|
ignore_signals()
|
|
{
|
|
#if HAVE_SIGPROCMASK || HAVE_SIGSETMASK
|
|
sigprocmask (SIG_BLOCK, &signals_to_block, &initial_signal_mask);
|
|
#else
|
|
int i;
|
|
for (i = 0; i < NUM_SIGS; i++)
|
|
if (sigismember (&signals_to_block, sigs[i]))
|
|
signal (sigs[i], SIG_IGN);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
exit_with_signal (sig)
|
|
int sig;
|
|
{
|
|
sigset_t s;
|
|
signal (sig, SIG_DFL);
|
|
sigemptyset (&s);
|
|
sigaddset (&s, sig);
|
|
sigprocmask (SIG_UNBLOCK, &s, (sigset_t *) 0);
|
|
kill (getpid (), sig);
|
|
exit (2);
|
|
}
|
|
|
|
#if !HAVE_MKDIR
|
|
/* This is good enough for `patch'; it's not a general emulator. */
|
|
static int mkdir PARAMS ((char const *, int));
|
|
static int
|
|
mkdir (path, mode)
|
|
char const *path;
|
|
int mode; /* ignored */
|
|
{
|
|
char *cmd = xmalloc (strlen (path) + 9);
|
|
int r;
|
|
sprintf (cmd, "mkdir '%s'", path);
|
|
r = system (cmd);
|
|
free (cmd);
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
/* Replace '/' with '\0' in FILENAME if it marks a place that
|
|
needs testing for the existence of directory. Return the address
|
|
of the last location replaced, or FILENAME if none were replaced. */
|
|
static char *replace_slashes PARAMS ((char *));
|
|
static char *
|
|
replace_slashes (filename)
|
|
char *filename;
|
|
{
|
|
char *f;
|
|
for (f = filename; *f == '/'; f++)
|
|
continue;
|
|
for (; *f; f++)
|
|
if (*f == '/')
|
|
{
|
|
*f = '\0';
|
|
while (f[1] == '/')
|
|
f++;
|
|
}
|
|
while (filename != f && *--f)
|
|
continue;
|
|
return f;
|
|
}
|
|
|
|
/* Count the number of path name components in the existing leading prefix
|
|
of `filename'. Do not count the last element, or the root dir. */
|
|
int
|
|
countdirs (filename)
|
|
char *filename;
|
|
{
|
|
int count = 0;
|
|
|
|
if (*filename)
|
|
{
|
|
register char *f;
|
|
register char *flim = replace_slashes (filename);
|
|
|
|
/* Now turn the '\0's back to '/'s, calling stat as we go. */
|
|
for (f = filename; f <= flim; f++)
|
|
if (!*f)
|
|
{
|
|
struct stat sbuf;
|
|
if (stat (filename, &sbuf) != 0)
|
|
break;
|
|
count++;
|
|
*f = '/';
|
|
}
|
|
|
|
for (; f <= flim; f++)
|
|
if (!*f)
|
|
*f = '/';
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/* Make sure we'll have the directories to create a file.
|
|
Ignore the last element of `filename'. */
|
|
|
|
void
|
|
makedirs (filename)
|
|
register char *filename;
|
|
{
|
|
if (*filename)
|
|
{
|
|
register char *f;
|
|
register char *flim = replace_slashes (filename);
|
|
|
|
/* Now turn the NULs back to '/'s; stop when the path doesn't exist. */
|
|
errno = 0;
|
|
for (f = filename; f <= flim; f++)
|
|
if (!*f)
|
|
{
|
|
struct stat sbuf;
|
|
if (stat (filename, &sbuf) != 0)
|
|
break;
|
|
*f = '/';
|
|
}
|
|
|
|
/* Create the missing directories, replacing NULs by '/'s. */
|
|
if (errno == ENOENT)
|
|
for (; f <= flim; f++)
|
|
if (!*f)
|
|
{
|
|
if (mkdir (filename,
|
|
S_IRUSR|S_IWUSR|S_IXUSR
|
|
|S_IRGRP|S_IWGRP|S_IXGRP
|
|
|S_IROTH|S_IWOTH|S_IXOTH) != 0)
|
|
break;
|
|
*f = '/';
|
|
}
|
|
|
|
for (; f <= flim; f++)
|
|
if (!*f)
|
|
*f = '/';
|
|
}
|
|
}
|
|
|
|
/* Make filenames more reasonable. */
|
|
|
|
char *
|
|
fetchname (at, strip_leading)
|
|
char *at;
|
|
int strip_leading;
|
|
{
|
|
char *name;
|
|
register char *t;
|
|
int sleading = strip_leading;
|
|
|
|
if (!at)
|
|
return 0;
|
|
while (ISSPACE (*at))
|
|
at++;
|
|
if (debug & 128)
|
|
say ("fetchname %s %d\n", at, strip_leading);
|
|
|
|
name = at;
|
|
/* Strip off up to `sleading' leading slashes and null terminate. */
|
|
for (t = at; *t; t++)
|
|
if (*t == '/')
|
|
{
|
|
while (t[1] == '/')
|
|
t++;
|
|
if (--sleading >= 0)
|
|
name = t+1;
|
|
}
|
|
else if (ISSPACE (*t))
|
|
{
|
|
*t = '\0';
|
|
break;
|
|
}
|
|
|
|
if (!*name)
|
|
return 0;
|
|
|
|
/* Allow files to be created by diffing against /dev/null. */
|
|
if (strcmp (at, "/dev/null") == 0)
|
|
return 0;
|
|
|
|
return savestr (name);
|
|
}
|
|
|
|
VOID *
|
|
xmalloc (size)
|
|
size_t size;
|
|
{
|
|
register VOID *p = malloc (size);
|
|
if (!p)
|
|
memory_fatal ();
|
|
return p;
|
|
}
|
|
|
|
void
|
|
Fseek (stream, offset, ptrname)
|
|
FILE *stream;
|
|
long offset;
|
|
int ptrname;
|
|
{
|
|
if (fseek (stream, offset, ptrname) != 0)
|
|
pfatal ("fseek");
|
|
}
|