/* 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 #include #include #undef XTERN #define XTERN #include #include #if !defined SIGCHLD && defined SIGCLD #define SIGCHLD SIGCLD #endif #ifdef __STDC__ # include # define vararg_start va_start #else # define vararg_start(ap,p) va_start (ap) # if HAVE_VARARGS_H # include # 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"); }