1/* utility functions for `patch' */
2
3/* $Id: util.c 8008 2004-06-16 21:22:10Z korli $ */
4
5/* Copyright 1986 Larry Wall
6   Copyright 1992, 1993, 1997-1998, 1999 Free Software Foundation, Inc.
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2, or (at your option)
11   any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; see the file COPYING.
20   If not, write to the Free Software Foundation,
21   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
22
23#define XTERN extern
24#include <common.h>
25#include <backupfile.h>
26#include <basename.h>
27#include <quotearg.h>
28#include <quotesys.h>
29#include <version.h>
30#undef XTERN
31#define XTERN
32#include <util.h>
33#include <xalloc.h>
34
35#include <maketime.h>
36#include <partime.h>
37
38#include <signal.h>
39#if !defined SIGCHLD && defined SIGCLD
40#define SIGCHLD SIGCLD
41#endif
42#if ! HAVE_RAISE && ! defined raise
43# define raise(sig) kill (getpid (), sig)
44#endif
45
46#ifdef __STDC__
47# include <stdarg.h>
48# define vararg_start va_start
49#else
50# define vararg_start(ap,p) va_start (ap)
51# if HAVE_VARARGS_H
52#  include <varargs.h>
53# else
54   typedef char *va_list;
55#  define va_dcl int va_alist;
56#  define va_start(ap) ((ap) = (va_list) &va_alist)
57#  define va_arg(ap, t) (((t *) ((ap) += sizeof (t)))  [-1])
58#  define va_end(ap)
59# endif
60#endif
61
62static void makedirs PARAMS ((char *));
63
64/* Move a file FROM (where *FROM_NEEDS_REMOVAL is nonzero if FROM
65   needs removal when cleaning up at the end of execution)
66   to TO, renaming it if possible and copying it if necessary.
67   If we must create TO, use MODE to create it.
68   If FROM is null, remove TO (ignoring FROMSTAT).
69   FROM_NEEDS_REMOVAL must be nonnull if FROM is nonnull.
70   Back up TO if BACKUP is nonzero.  */
71
72void
73move_file (char const *from, int volatile *from_needs_removal,
74	   char *to, mode_t mode, int backup)
75{
76  struct stat to_st;
77  int to_errno = ! backup ? -1 : stat (to, &to_st) == 0 ? 0 : errno;
78
79  if (backup)
80    {
81      int try_makedirs_errno = 0;
82      char *bakname;
83
84      if (origprae || origbase)
85	{
86	  char const *p = origprae ? origprae : "";
87	  char const *b = origbase ? origbase : "";
88	  char const *o = base_name (to);
89	  size_t plen = strlen (p);
90	  size_t tlen = o - to;
91	  size_t blen = strlen (b);
92	  size_t osize = strlen (o) + 1;
93	  bakname = xmalloc (plen + tlen + blen + osize);
94	  memcpy (bakname, p, plen);
95	  memcpy (bakname + plen, to, tlen);
96	  memcpy (bakname + plen + tlen, b, blen);
97	  memcpy (bakname + plen + tlen + blen, o, osize);
98	  for (p += FILESYSTEM_PREFIX_LEN (p);  *p;  p++)
99	    if (ISSLASH (*p))
100	      {
101		try_makedirs_errno = ENOENT;
102		break;
103	      }
104	}
105      else
106	{
107	  bakname = find_backup_file_name (to, backup_type);
108	  if (!bakname)
109	    memory_fatal ();
110	}
111
112      if (to_errno)
113	{
114	  int fd;
115
116	  if (debug & 4)
117	    say ("Creating empty unreadable file %s\n", quotearg (bakname));
118
119	  try_makedirs_errno = ENOENT;
120	  unlink (bakname);
121	  while ((fd = creat (bakname, 0)) < 0)
122	    {
123	      if (errno != try_makedirs_errno)
124		pfatal ("Can't create file %s", quotearg (bakname));
125	      makedirs (bakname);
126	      try_makedirs_errno = 0;
127	    }
128	  if (close (fd) != 0)
129	    pfatal ("Can't close file %s", quotearg (bakname));
130	}
131      else
132	{
133	  if (debug & 4)
134	    say ("Renaming file %s to %s\n",
135		 quotearg_n (0, to), quotearg_n (1, bakname));
136	  while (rename (to, bakname) != 0)
137	    {
138	      if (errno != try_makedirs_errno)
139		pfatal ("Can't rename file %s to %s",
140			quotearg_n (0, to), quotearg_n (1, bakname));
141	      makedirs (bakname);
142	      try_makedirs_errno = 0;
143	    }
144	}
145
146      free (bakname);
147    }
148
149  if (from)
150    {
151      if (debug & 4)
152	say ("Renaming file %s to %s\n",
153	     quotearg_n (0, from), quotearg_n (1, to));
154
155      if (rename (from, to) == 0)
156	*from_needs_removal = 0;
157      else
158	{
159	  int to_dir_known_to_exist = 0;
160
161	  if (errno == ENOENT
162	      && (to_errno == -1 || to_errno == ENOENT))
163	    {
164	      makedirs (to);
165	      to_dir_known_to_exist = 1;
166	      if (rename (from, to) == 0)
167		{
168		  *from_needs_removal = 0;
169		  return;
170		}
171	    }
172
173	  if (errno == EXDEV)
174	    {
175	      if (! backup)
176		{
177		  if (unlink (to) == 0)
178		    to_dir_known_to_exist = 1;
179		  else if (errno != ENOENT)
180		    pfatal ("Can't remove file %s", quotearg (to));
181		}
182	      if (! to_dir_known_to_exist)
183		makedirs (to);
184	      copy_file (from, to, 0, mode);
185	      return;
186	    }
187
188	  pfatal ("Can't rename file %s to %s",
189		  quotearg_n (0, from), quotearg_n (1, to));
190	}
191    }
192  else if (! backup)
193    {
194      if (debug & 4)
195	say ("Removing file %s\n", quotearg (to));
196      if (unlink (to) != 0)
197	pfatal ("Can't remove file %s", quotearg (to));
198    }
199}
200
201/* Create FILE with OPEN_FLAGS, and with MODE adjusted so that
202   we can read and write the file and that the file is not executable.
203   Return the file descriptor.  */
204int
205create_file (char const *file, int open_flags, mode_t mode)
206{
207  int fd;
208  mode |= S_IRUSR | S_IWUSR;
209  mode &= ~ (S_IXUSR | S_IXGRP | S_IXOTH);
210  if (! (O_CREAT && O_TRUNC))
211    close (creat (file, mode));
212  fd = open (file, O_CREAT | O_TRUNC | open_flags, mode);
213  if (fd < 0)
214    pfatal ("Can't create file %s", quotearg (file));
215  return fd;
216}
217
218/* Copy a file. */
219
220void
221copy_file (char const *from, char const *to, int to_flags, mode_t mode)
222{
223  int tofd;
224  int fromfd;
225  size_t i;
226
227  if ((fromfd = open (from, O_RDONLY | O_BINARY)) < 0)
228    pfatal ("Can't reopen file %s", quotearg (from));
229  tofd = create_file (to, O_WRONLY | O_BINARY | to_flags, mode);
230  while ((i = read (fromfd, buf, bufsize)) != 0)
231    {
232      if (i == (size_t) -1)
233	read_fatal ();
234      if (write (tofd, buf, i) != i)
235	write_fatal ();
236    }
237  if (close (fromfd) != 0)
238    read_fatal ();
239  if (close (tofd) != 0)
240    write_fatal ();
241}
242
243static char const DEV_NULL[] = NULL_DEVICE;
244
245static char const RCSSUFFIX[] = ",v";
246static char const CHECKOUT[] = "co %s";
247static char const CHECKOUT_LOCKED[] = "co -l %s";
248static char const RCSDIFF1[] = "rcsdiff %s";
249
250static char const SCCSPREFIX[] = "s.";
251static char const GET[] = "get ";
252static char const GET_LOCKED[] = "get -e ";
253static char const SCCSDIFF1[] = "get -p ";
254static char const SCCSDIFF2[] = "|diff - %s";
255
256static char const CLEARTOOL_CO[] = "cleartool co -unr -nc ";
257
258/* Return "RCS" if FILENAME is controlled by RCS,
259   "SCCS" if it is controlled by SCCS,
260   "ClearCase" if it is controlled by Clearcase, and 0 otherwise.
261   READONLY is nonzero if we desire only readonly access to FILENAME.
262   FILESTAT describes FILENAME's status or is 0 if FILENAME does not exist.
263   If successful and if GETBUF is nonzero, set *GETBUF to a command
264   that gets the file; similarly for DIFFBUF and a command to diff the file
265   (but set *DIFFBUF to 0 if the diff operation is meaningless).
266   *GETBUF and *DIFFBUF must be freed by the caller.  */
267char const *
268version_controller (char const *filename, int readonly,
269		    struct stat const *filestat, char **getbuf, char **diffbuf)
270{
271  struct stat cstat;
272  char const *filebase = base_name (filename);
273  char const *dotslash = *filename == '-' ? "./" : "";
274  size_t dir_len = filebase - filename;
275  size_t filenamelen = strlen (filename);
276  size_t maxfixlen = sizeof "SCCS/" - 1 + sizeof SCCSPREFIX - 1;
277  size_t maxtrysize = filenamelen + maxfixlen + 1;
278  size_t quotelen = quote_system_arg (0, filename);
279  size_t maxgetsize = sizeof CLEARTOOL_CO + quotelen + maxfixlen;
280  size_t maxdiffsize =
281    (sizeof SCCSDIFF1 + sizeof SCCSDIFF2 + sizeof DEV_NULL - 1
282     + 2 * quotelen + maxfixlen);
283  char *trybuf = xmalloc (maxtrysize);
284  char const *r = 0;
285
286  strcpy (trybuf, filename);
287
288#define try1(f,a1)    (sprintf (trybuf + dir_len, f, a1),    stat (trybuf, &cstat) == 0)
289#define try2(f,a1,a2) (sprintf (trybuf + dir_len, f, a1,a2), stat (trybuf, &cstat) == 0)
290
291  /* Check that RCS file is not working file.
292     Some hosts don't report file name length errors.  */
293
294  if ((try2 ("RCS/%s%s", filebase, RCSSUFFIX)
295       || try1 ("RCS/%s", filebase)
296       || try2 ("%s%s", filebase, RCSSUFFIX))
297      && ! (filestat
298	    && filestat->st_dev == cstat.st_dev
299	    && filestat->st_ino == cstat.st_ino))
300    {
301      if (getbuf)
302	{
303	  char *p = *getbuf = xmalloc (maxgetsize);
304	  sprintf (p, readonly ? CHECKOUT : CHECKOUT_LOCKED, dotslash);
305	  p += strlen (p);
306	  p += quote_system_arg (p, filename);
307	  *p = '\0';
308	}
309
310      if (diffbuf)
311	{
312	  char *p = *diffbuf = xmalloc (maxdiffsize);
313	  sprintf (p, RCSDIFF1, dotslash);
314	  p += strlen (p);
315	  p += quote_system_arg (p, filename);
316	  *p++ = '>';
317	  strcpy (p, DEV_NULL);
318	}
319
320      r = "RCS";
321    }
322  else if (try2 ("SCCS/%s%s", SCCSPREFIX, filebase)
323	   || try2 ("%s%s", SCCSPREFIX, filebase))
324    {
325      if (getbuf)
326	{
327	  char *p = *getbuf = xmalloc (maxgetsize);
328	  sprintf (p, readonly ? GET : GET_LOCKED);
329	  p += strlen (p);
330	  p += quote_system_arg (p, trybuf);
331	  *p = '\0';
332	}
333
334      if (diffbuf)
335	{
336	  char *p = *diffbuf = xmalloc (maxdiffsize);
337	  strcpy (p, SCCSDIFF1);
338	  p += sizeof SCCSDIFF1 - 1;
339	  p += quote_system_arg (p, trybuf);
340	  sprintf (p, SCCSDIFF2, dotslash);
341	  p += strlen (p);
342	  p += quote_system_arg (p, filename);
343	  *p++ = '>';
344	  strcpy (p, DEV_NULL);
345	}
346
347      r = "SCCS";
348    }
349  else if (!readonly && filestat
350	   && try1 ("%s@@", filebase) && S_ISDIR (cstat.st_mode))
351    {
352      if (getbuf)
353	{
354	  char *p = *getbuf = xmalloc (maxgetsize);
355	  strcpy (p, CLEARTOOL_CO);
356	  p += sizeof CLEARTOOL_CO - 1;
357	  p += quote_system_arg (p, filename);
358	  *p = '\0';
359	}
360
361      if (diffbuf)
362	*diffbuf = 0;
363
364      r = "ClearCase";
365    }
366
367  free (trybuf);
368  return r;
369}
370
371/* Get FILENAME from version control system CS.  The file already exists if
372   EXISTS is nonzero.  Only readonly access is needed if READONLY is nonzero.
373   Use the command GETBUF to actually get the named file.
374   Store the resulting file status into *FILESTAT.
375   Return nonzero if successful.  */
376int
377version_get (char const *filename, char const *cs, int exists, int readonly,
378	     char const *getbuf, struct stat *filestat)
379{
380  if (patch_get < 0)
381    {
382      ask ("Get file %s from %s%s? [y] ",
383	   quotearg (filename), cs, readonly ? "" : " with lock");
384      if (*buf == 'n')
385	return 0;
386    }
387
388  if (dry_run)
389    {
390      if (! exists)
391	fatal ("can't do dry run on nonexistent version-controlled file %s; invoke `%s' and try again",
392	       quotearg (filename), getbuf);
393    }
394  else
395    {
396      if (verbosity == VERBOSE)
397	say ("Getting file %s from %s%s...\n", quotearg (filename),
398	     cs, readonly ? "" : " with lock");
399      if (systemic (getbuf) != 0)
400	fatal ("Can't get file %s from %s", quotearg (filename), cs);
401      if (stat (filename, filestat) != 0)
402	pfatal ("%s", quotearg (filename));
403    }
404
405  return 1;
406}
407
408/* Allocate a unique area for a string. */
409
410char *
411savebuf (register char const *s, register size_t size)
412{
413  register char *rv;
414
415  assert (s && size);
416  rv = malloc (size);
417
418  if (! rv)
419    {
420      if (! using_plan_a)
421	memory_fatal ();
422    }
423  else
424    memcpy (rv, s, size);
425
426  return rv;
427}
428
429char *
430savestr (char const *s)
431{
432  return savebuf (s, strlen (s) + 1);
433}
434
435void
436remove_prefix (char *p, size_t prefixlen)
437{
438  char const *s = p + prefixlen;
439  while ((*p++ = *s++))
440    continue;
441}
442
443char *
444format_linenum (char numbuf[LINENUM_LENGTH_BOUND + 1], LINENUM n)
445{
446  char *p = numbuf + LINENUM_LENGTH_BOUND;
447  *p = '\0';
448
449  if (n < 0)
450    {
451      do
452	*--p = '0' - (int) (n % 10);
453      while ((n /= 10) != 0);
454
455      *--p = '-';
456    }
457  else
458    {
459      do
460	*--p = '0' + (int) (n % 10);
461      while ((n /= 10) != 0);
462    }
463
464  return p;
465}
466
467#if !HAVE_VPRINTF
468#define vfprintf my_vfprintf
469static int
470vfprintf (FILE *stream, char const *format, va_list args)
471{
472#if !HAVE_DOPRNT && HAVE__DOPRINTF
473# define _doprnt _doprintf
474#endif
475#if HAVE_DOPRNT || HAVE__DOPRINTF
476  _doprnt (format, args, stream);
477  return ferror (stream) ? -1 : 0;
478#else
479  int *a = (int *) args;
480  return fprintf (stream, format,
481		  a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]);
482#endif
483}
484#endif /* !HAVE_VPRINTF */
485
486/* Terminal output, pun intended. */
487
488void
489fatal (char const *format, ...)
490{
491  va_list args;
492  fprintf (stderr, "%s: **** ", program_name);
493  vararg_start (args, format);
494  vfprintf (stderr, format, args);
495  va_end (args);
496  putc ('\n', stderr);
497  fflush (stderr);
498  fatal_exit (0);
499}
500
501void
502memory_fatal (void)
503{
504  fatal ("out of memory");
505}
506
507void
508read_fatal (void)
509{
510  pfatal ("read error");
511}
512
513void
514write_fatal (void)
515{
516  pfatal ("write error");
517}
518
519/* Say something from patch, something from the system, then silence . . . */
520
521void
522pfatal (char const *format, ...)
523{
524  int errnum = errno;
525  va_list args;
526  fprintf (stderr, "%s: **** ", program_name);
527  vararg_start (args, format);
528  vfprintf (stderr, format, args);
529  va_end (args);
530  fflush (stderr); /* perror bypasses stdio on some hosts.  */
531  errno = errnum;
532  perror (" ");
533  fflush (stderr);
534  fatal_exit (0);
535}
536
537/* Tell the user something.  */
538
539void
540say (char const *format, ...)
541{
542  va_list args;
543  vararg_start (args, format);
544  vfprintf (stdout, format, args);
545  va_end (args);
546  fflush (stdout);
547}
548
549/* Get a response from the user, somehow or other. */
550
551void
552ask (char const *format, ...)
553{
554  static int ttyfd = -2;
555  int r;
556  va_list args;
557
558  vararg_start (args, format);
559  vfprintf (stdout, format, args);
560  va_end (args);
561  fflush (stdout);
562
563  if (ttyfd == -2)
564    {
565      /* If standard output is not a tty, don't bother opening /dev/tty,
566	 since it's unlikely that stdout will be seen by the tty user.
567	 The isatty test also works around a bug in GNU Emacs 19.34 under Linux
568	 which makes a call-process `patch' hang when it reads from /dev/tty.
569	 POSIX.2 requires that we read /dev/tty, though.  */
570      ttyfd = (posixly_correct || isatty (STDOUT_FILENO)
571	       ? open (TTY_DEVICE, O_RDONLY)
572	       : -1);
573    }
574
575  if (ttyfd < 0)
576    {
577      /* No terminal at all -- default it.  */
578      printf ("\n");
579      buf[0] = '\n';
580      buf[1] = '\0';
581    }
582  else
583    {
584      size_t s = 0;
585      while ((r = read (ttyfd, buf + s, bufsize - 1 - s)) == bufsize - 1 - s
586	     && buf[bufsize - 2] != '\n')
587	{
588	  s = bufsize - 1;
589	  bufsize *= 2;
590	  buf = realloc (buf, bufsize);
591	  if (!buf)
592	    memory_fatal ();
593	}
594      if (r == 0)
595	printf ("EOF\n");
596      else if (r < 0)
597	{
598	  perror ("tty read");
599	  fflush (stderr);
600	  close (ttyfd);
601	  ttyfd = -1;
602	  r = 0;
603	}
604      buf[s + r] = '\0';
605    }
606}
607
608/* Return nonzero if it OK to reverse a patch.  */
609
610int
611ok_to_reverse (char const *format, ...)
612{
613  int r = 0;
614
615  if (noreverse || ! (force && verbosity == SILENT))
616    {
617      va_list args;
618      vararg_start (args, format);
619      vfprintf (stdout, format, args);
620      va_end (args);
621    }
622
623  if (noreverse)
624    {
625      printf ("  Skipping patch.\n");
626      skip_rest_of_patch = TRUE;
627      r = 0;
628    }
629  else if (force)
630    {
631      if (verbosity != SILENT)
632	printf ("  Applying it anyway.\n");
633      r = 0;
634    }
635  else if (batch)
636    {
637      say (reverse ? "  Ignoring -R.\n" : "  Assuming -R.\n");
638      r = 1;
639    }
640  else
641    {
642      ask (reverse ? "  Ignore -R? [n] " : "  Assume -R? [n] ");
643      r = *buf == 'y';
644      if (! r)
645	{
646	  ask ("Apply anyway? [n] ");
647	  if (*buf != 'y')
648	    {
649	      if (verbosity != SILENT)
650		say ("Skipping patch.\n");
651	      skip_rest_of_patch = TRUE;
652	    }
653	}
654    }
655
656  return r;
657}
658
659/* How to handle certain events when not in a critical region. */
660
661#define NUM_SIGS (sizeof (sigs) / sizeof (*sigs))
662static int const sigs[] = {
663#ifdef SIGHUP
664       SIGHUP,
665#endif
666#ifdef SIGPIPE
667       SIGPIPE,
668#endif
669#ifdef SIGTERM
670       SIGTERM,
671#endif
672#ifdef SIGXCPU
673       SIGXCPU,
674#endif
675#ifdef SIGXFSZ
676       SIGXFSZ,
677#endif
678       SIGINT
679};
680
681#if !HAVE_SIGPROCMASK
682#define sigset_t int
683#define sigemptyset(s) (*(s) = 0)
684#ifndef sigmask
685#define sigmask(sig) (1 << ((sig) - 1))
686#endif
687#define sigaddset(s, sig) (*(s) |= sigmask (sig))
688#define sigismember(s, sig) ((*(s) & sigmask (sig)) != 0)
689#ifndef SIG_BLOCK
690#define SIG_BLOCK 0
691#endif
692#ifndef SIG_UNBLOCK
693#define SIG_UNBLOCK (SIG_BLOCK + 1)
694#endif
695#ifndef SIG_SETMASK
696#define SIG_SETMASK (SIG_BLOCK + 2)
697#endif
698#define sigprocmask(how, n, o) \
699  ((how) == SIG_BLOCK \
700   ? ((o) ? *(o) = sigblock (*(n)) : sigblock (*(n))) \
701   : (how) == SIG_UNBLOCK \
702   ? sigsetmask (((o) ? *(o) = sigblock (0) : sigblock (0)) & ~*(n)) \
703   : (o ? *(o) = sigsetmask (*(n)) : sigsetmask (*(n))))
704#if !HAVE_SIGSETMASK
705#define sigblock(mask) 0
706#define sigsetmask(mask) 0
707#endif
708#endif
709
710static sigset_t initial_signal_mask;
711static sigset_t signals_to_block;
712
713#if ! HAVE_SIGACTION
714static RETSIGTYPE fatal_exit_handler PARAMS ((int)) __attribute__ ((noreturn));
715static RETSIGTYPE
716fatal_exit_handler (int sig)
717{
718  signal (sig, SIG_IGN);
719  fatal_exit (sig);
720}
721#endif
722
723void
724set_signals (int reset)
725{
726  int i;
727#if HAVE_SIGACTION
728  struct sigaction initial_act, fatal_act;
729  fatal_act.sa_handler = fatal_exit;
730  sigemptyset (&fatal_act.sa_mask);
731  fatal_act.sa_flags = 0;
732#define setup_handler(sig) sigaction (sig, &fatal_act, (struct sigaction *) 0)
733#else
734#define setup_handler(sig) signal (sig, fatal_exit_handler)
735#endif
736
737  if (!reset)
738    {
739#ifdef SIGCHLD
740      /* System V fork+wait does not work if SIGCHLD is ignored.  */
741      signal (SIGCHLD, SIG_DFL);
742#endif
743      sigemptyset (&signals_to_block);
744      for (i = 0;  i < NUM_SIGS;  i++)
745	{
746	  int ignoring_signal;
747#if HAVE_SIGACTION
748	  if (sigaction (sigs[i], (struct sigaction *) 0, &initial_act) != 0)
749	    continue;
750	  ignoring_signal = initial_act.sa_handler == SIG_IGN;
751#else
752	  ignoring_signal = signal (sigs[i], SIG_IGN) == SIG_IGN;
753#endif
754	  if (! ignoring_signal)
755	    {
756	      sigaddset (&signals_to_block, sigs[i]);
757	      setup_handler (sigs[i]);
758	    }
759	}
760    }
761  else
762    {
763      /* Undo the effect of ignore_signals.  */
764#if HAVE_SIGPROCMASK || HAVE_SIGSETMASK
765      sigprocmask (SIG_SETMASK, &initial_signal_mask, (sigset_t *) 0);
766#else
767      for (i = 0;  i < NUM_SIGS;  i++)
768	if (sigismember (&signals_to_block, sigs[i]))
769	  setup_handler (sigs[i]);
770#endif
771    }
772}
773
774/* How to handle certain events when in a critical region. */
775
776void
777ignore_signals (void)
778{
779#if HAVE_SIGPROCMASK || HAVE_SIGSETMASK
780  sigprocmask (SIG_BLOCK, &signals_to_block, &initial_signal_mask);
781#else
782  int i;
783  for (i = 0;  i < NUM_SIGS;  i++)
784    if (sigismember (&signals_to_block, sigs[i]))
785      signal (sigs[i], SIG_IGN);
786#endif
787}
788
789void
790exit_with_signal (int sig)
791{
792  sigset_t s;
793  signal (sig, SIG_DFL);
794  sigemptyset (&s);
795  sigaddset (&s, sig);
796  sigprocmask (SIG_UNBLOCK, &s, (sigset_t *) 0);
797  raise (sig);
798  exit (2);
799}
800
801int
802systemic (char const *command)
803{
804  if (debug & 8)
805    say ("+ %s\n", command);
806  fflush (stdout);
807  return system (command);
808}
809
810/* Replace '/' with '\0' in FILENAME if it marks a place that
811   needs testing for the existence of directory.  Return the address
812   of the last location replaced, or 0 if none were replaced.  */
813static char *
814replace_slashes (char *filename)
815{
816  char *f;
817  char *last_location_replaced = 0;
818  char const *component_start;
819
820  for (f = filename + FILESYSTEM_PREFIX_LEN (filename);  ISSLASH (*f);  f++)
821    continue;
822
823  component_start = f;
824
825  for (; *f; f++)
826    if (ISSLASH (*f))
827      {
828	char *slash = f;
829
830	/* Treat multiple slashes as if they were one slash.  */
831	while (ISSLASH (f[1]))
832	  f++;
833
834	/* Ignore slashes at the end of the path.  */
835	if (! f[1])
836	  break;
837
838	/* "." and ".." need not be tested.  */
839	if (! (slash - component_start <= 2
840	       && component_start[0] == '.' && slash[-1] == '.'))
841	  {
842	    *slash = '\0';
843	    last_location_replaced = slash;
844	  }
845
846	component_start = f + 1;
847      }
848
849  return last_location_replaced;
850}
851
852/* Make sure we'll have the directories to create a file.
853   Ignore the last element of `filename'.  */
854
855static void
856makedirs (register char *filename)
857{
858  register char *f;
859  register char *flim = replace_slashes (filename);
860
861  if (flim)
862    {
863      /* Create any missing directories, replacing NULs by '/'s.
864	 Ignore errors.  We may have to keep going even after an EEXIST,
865	 since the path may contain ".."s; and when there is an EEXIST
866	 failure the system may return some other error number.
867	 Any problems will eventually be reported when we create the file.  */
868      for (f = filename;  f <= flim;  f++)
869	if (!*f)
870	  {
871	    mkdir (filename,
872		   S_IRUSR|S_IWUSR|S_IXUSR
873		   |S_IRGRP|S_IWGRP|S_IXGRP
874		   |S_IROTH|S_IWOTH|S_IXOTH);
875	    *f = '/';
876	  }
877    }
878}
879
880/* Remove empty ancestor directories of FILENAME.
881   Ignore errors, since the path may contain ".."s, and when there
882   is an EEXIST failure the system may return some other error number.  */
883void
884removedirs (char *filename)
885{
886  size_t i;
887
888  for (i = strlen (filename);  i != 0;  i--)
889    if (ISSLASH (filename[i])
890	&& ! (ISSLASH (filename[i - 1])
891	      || (filename[i - 1] == '.'
892		  && (i == 1
893		      || ISSLASH (filename[i - 2])
894		      || (filename[i - 2] == '.'
895			  && (i == 2
896			      || ISSLASH (filename[i - 3])))))))
897      {
898	filename[i] = '\0';
899	if (rmdir (filename) == 0 && verbosity == VERBOSE)
900	  say ("Removed empty directory %s\n", quotearg (filename));
901	filename[i] = '/';
902      }
903}
904
905static time_t initial_time;
906
907void
908init_time (void)
909{
910  time (&initial_time);
911}
912
913/* Make filenames more reasonable. */
914
915char *
916fetchname (char *at, int strip_leading, time_t *pstamp)
917{
918    char *name;
919    register char *t;
920    int sleading = strip_leading;
921    time_t stamp = (time_t) -1;
922
923    while (ISSPACE ((unsigned char) *at))
924	at++;
925    if (debug & 128)
926	say ("fetchname %s %d\n", at, strip_leading);
927
928    name = at;
929    /* Strip up to `strip_leading' leading slashes and null terminate.
930       If `strip_leading' is negative, strip all leading slashes.  */
931    for (t = at;  *t;  t++)
932      {
933	if (ISSLASH (*t))
934	  {
935	    while (ISSLASH (t[1]))
936	      t++;
937	    if (strip_leading < 0 || --sleading >= 0)
938		name = t+1;
939	  }
940	else if (ISSPACE ((unsigned char) *t))
941	  {
942	    char const *u = t;
943
944	    if (set_time | set_utc)
945	      stamp = str2time (&u, initial_time,
946				set_utc ? 0L : TM_LOCAL_ZONE);
947	    else
948	      {
949		/* The head says the file is nonexistent if the timestamp
950		   is the epoch; but the listed time is local time, not UTC,
951		   and POSIX.1 allows local time offset anywhere in the range
952		   -25:00 < offset < +26:00.  Match any time in that
953		   range by assuming local time is -25:00 and then matching
954		   any ``local'' time T in the range 0 < T < 25+26 hours.  */
955		stamp = str2time (&u, initial_time, -25L * 60 * 60);
956		if (0 < stamp && stamp < (25 + 26) * 60L * 60)
957		  stamp = 0;
958	      }
959
960	    if (*u && ! ISSPACE ((unsigned char) *u))
961	      stamp = (time_t) -1;
962
963	    *t = '\0';
964	    break;
965	  }
966      }
967
968    if (!*name)
969      return 0;
970
971    /* If the name is "/dev/null", ignore the name and mark the file
972       as being nonexistent.  The name "/dev/null" appears in patches
973       regardless of how NULL_DEVICE is spelled.  */
974    if (strcmp (at, "/dev/null") == 0)
975      {
976	if (pstamp)
977	  *pstamp = 0;
978	return 0;
979      }
980
981    /* Ignore the name if it doesn't have enough slashes to strip off.  */
982    if (0 < sleading)
983      return 0;
984
985    if (pstamp)
986      *pstamp = stamp;
987
988    return savestr (name);
989}
990
991void
992Fseek (FILE *stream, file_offset offset, int ptrname)
993{
994  if (file_seek (stream, offset, ptrname) != 0)
995    pfatal ("fseek");
996}
997