Home | History | Annotate | Line # | Download | only in src
      1 /*	$NetBSD: rcsutil.c,v 1.2 2016/01/14 04:22:39 christos Exp $	*/
      2 
      3 /* RCS utility functions */
      4 
      5 /* Copyright 1982, 1988, 1989 Walter Tichy
      6    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
      7    Distributed under license by the Free Software Foundation, Inc.
      8 
      9 This file is part of RCS.
     10 
     11 RCS is free software; you can redistribute it and/or modify
     12 it under the terms of the GNU General Public License as published by
     13 the Free Software Foundation; either version 2, or (at your option)
     14 any later version.
     15 
     16 RCS is distributed in the hope that it will be useful,
     17 but WITHOUT ANY WARRANTY; without even the implied warranty of
     18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     19 GNU General Public License for more details.
     20 
     21 You should have received a copy of the GNU General Public License
     22 along with RCS; see the file COPYING.
     23 If not, write to the Free Software Foundation,
     24 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     25 
     26 Report problems and direct all questions to:
     27 
     28     rcs-bugs (at) cs.purdue.edu
     29 
     30 */
     31 
     32 
     33 
     34 
     35 /*
     36  * Log: rcsutil.c,v
     37  * Revision 5.20  1995/06/16 06:19:24  eggert
     38  * (catchsig): Remove `return'.
     39  * Update FSF address.
     40  *
     41  * Revision 5.19  1995/06/02 18:19:00  eggert
     42  * (catchsigaction): New name for `catchsig', for sa_sigaction signature.
     43  * Use nRCS even if !has_psiginfo, to remove unused variable warning.
     44  * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction.
     45  * Use ENOTSUP only if defined.
     46  *
     47  * Revision 5.18  1995/06/01 16:23:43  eggert
     48  * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo,
     49  * to determine whether to use SA_SIGINFO feature,
     50  * but also check at runtime whether the feature works.
     51  * (catchsig): If an mmap_signal occurs, report the affected file name.
     52  * (unsupported_SA_SIGINFO, accessName): New variables.
     53  * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler.
     54  * If SA_SIGINFO fails, fall back on sa_handler method.
     55  *
     56  * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions.
     57  * (concatenate): Remove.
     58  *
     59  * (runv): Work around bad_wait_if_SIGCHLD_ignored bug.
     60  * Remove reference to OPEN_O_WORK.
     61  *
     62  * Revision 5.17  1994/03/20 04:52:58  eggert
     63  * Specify subprocess input via file descriptor, not file name.
     64  * Avoid messing with I/O buffers in the child process.
     65  * Define dup in terms of F_DUPFD if it exists.
     66  * Move setmtime to rcsedit.c.  Remove lint.
     67  *
     68  * Revision 5.16  1993/11/09 17:40:15  eggert
     69  * -V now prints version on stdout and exits.
     70  *
     71  * Revision 5.15  1993/11/03 17:42:27  eggert
     72  * Use psiginfo and setreuid if available.  Move date2str to maketime.c.
     73  *
     74  * Revision 5.14  1992/07/28  16:12:44  eggert
     75  * Add -V.  has_sigaction overrides sig_zaps_handler.  Fix -M bug.
     76  * Add mmap_signal, which minimizes signal handling for non-mmap hosts.
     77  *
     78  * Revision 5.13  1992/02/17  23:02:28  eggert
     79  * Work around NFS mmap SIGBUS problem.  Add -T support.
     80  *
     81  * Revision 5.12  1992/01/24  18:44:19  eggert
     82  * Work around NFS mmap bug that leads to SIGBUS core dumps.  lint -> RCS_lint
     83  *
     84  * Revision 5.11  1992/01/06  02:42:34  eggert
     85  * O_BINARY -> OPEN_O_WORK
     86  * while (E) ; -> while (E) continue;
     87  *
     88  * Revision 5.10  1991/10/07  17:32:46  eggert
     89  * Support piece tables even if !has_mmap.
     90  *
     91  * Revision 5.9  1991/08/19  03:13:55  eggert
     92  * Add spawn() support.  Explicate assumptions about getting invoker's name.
     93  * Standardize user-visible dates.  Tune.
     94  *
     95  * Revision 5.8  1991/04/21  11:58:30  eggert
     96  * Plug setuid security hole.
     97  *
     98  * Revision 5.6  1991/02/26  17:48:39  eggert
     99  * Fix setuid bug.  Use fread, fwrite more portably.
    100  * Support waitpid.  Don't assume -1 is acceptable to W* macros.
    101  * strsave -> str_save (DG/UX name clash)
    102  *
    103  * Revision 5.5  1990/12/04  05:18:49  eggert
    104  * Don't output a blank line after a signal diagnostic.
    105  * Use -I for prompts and -q for diagnostics.
    106  *
    107  * Revision 5.4  1990/11/01  05:03:53  eggert
    108  * Remove unneeded setid check.  Add awrite(), fremember().
    109  *
    110  * Revision 5.3  1990/10/06  00:16:45  eggert
    111  * Don't fread F if feof(F).
    112  *
    113  * Revision 5.2  1990/09/04  08:02:31  eggert
    114  * Store fread()'s result in an fread_type object.
    115  *
    116  * Revision 5.1  1990/08/29  07:14:07  eggert
    117  * Declare getpwuid() more carefully.
    118  *
    119  * Revision 5.0  1990/08/22  08:13:46  eggert
    120  * Add setuid support.  Permit multiple locks per user.
    121  * Remove compile-time limits; use malloc instead.
    122  * Switch to GMT.  Permit dates past 1999/12/31.
    123  * Add -V.  Remove snooping.  Ansify and Posixate.
    124  * Tune.  Some USG hosts define NSIG but not sys_siglist.
    125  * Don't run /bin/sh if it's hopeless.
    126  * Don't leave garbage behind if the output is an empty pipe.
    127  * Clean up after SIGXCPU or SIGXFSZ.  Print name of signal that caused cleanup.
    128  *
    129  * Revision 4.6  89/05/01  15:13:40  narten
    130  * changed copyright header to reflect current distribution rules
    131  *
    132  * Revision 4.5  88/11/08  16:01:02  narten
    133  * corrected use of varargs routines
    134  *
    135  * Revision 4.4  88/08/09  19:13:24  eggert
    136  * Check for memory exhaustion.
    137  * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
    138  * Use execv(), not system(); yield exit status like diff(1)'s.
    139  *
    140  * Revision 4.3  87/10/18  10:40:22  narten
    141  * Updating version numbers. Changes relative to 1.1 actually
    142  * relative to 4.1
    143  *
    144  * Revision 1.3  87/09/24  14:01:01  narten
    145  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
    146  * warnings)
    147  *
    148  * Revision 1.2  87/03/27  14:22:43  jenkins
    149  * Port to suns
    150  *
    151  * Revision 4.1  83/05/10  15:53:13  wft
    152  * Added getcaller() and findlock().
    153  * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
    154  * (needed for background jobs in older shells). Added restoreints().
    155  * Removed printing of full RCS path from logcommand().
    156  *
    157  * Revision 3.8  83/02/15  15:41:49  wft
    158  * Added routine fastcopy() to copy remainder of a file in blocks.
    159  *
    160  * Revision 3.7  82/12/24  15:25:19  wft
    161  * added catchints(), ignoreints() for catching and ingnoring interrupts;
    162  * fixed catchsig().
    163  *
    164  * Revision 3.6  82/12/08  21:52:05  wft
    165  * Using DATEFORM to format dates.
    166  *
    167  * Revision 3.5  82/12/04  18:20:49  wft
    168  * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
    169  * lockedby-field.
    170  *
    171  * Revision 3.4  82/12/03  17:17:43  wft
    172  * Added check to addlock() ensuring only one lock per person.
    173  * Addlock also returns a pointer to the lock created. Deleted fancydate().
    174  *
    175  * Revision 3.3  82/11/27  12:24:37  wft
    176  * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
    177  * Introduced macro SNOOP so that snoop can be placed in directory other than
    178  * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
    179  *
    180  * Revision 3.2  82/10/18  21:15:11  wft
    181  * added function getfullRCSname().
    182  *
    183  * Revision 3.1  82/10/13  16:17:37  wft
    184  * Cleanup message is now suppressed in quiet mode.
    185  */
    186 
    187 
    188 
    189 
    190 #include "rcsbase.h"
    191 
    192 libId(utilId, "Id: rcsutil.c,v 5.20 1995/06/16 06:19:24 eggert Exp ")
    193 
    194 #if !has_memcmp
    195 	int
    196 memcmp(s1, s2, n)
    197 	void const *s1, *s2;
    198 	size_t n;
    199 {
    200 	register unsigned char const
    201 		*p1 = (unsigned char const*)s1,
    202 		*p2 = (unsigned char const*)s2;
    203 	register size_t i = n;
    204 	register int r = 0;
    205 	while (i--  &&  !(r = (*p1++ - *p2++)))
    206 		;
    207 	return r;
    208 }
    209 #endif
    210 
    211 #if !has_memcpy
    212 	void *
    213 memcpy(s1, s2, n)
    214 	void *s1;
    215 	void const *s2;
    216 	size_t n;
    217 {
    218 	register char *p1 = (char*)s1;
    219 	register char const *p2 = (char const*)s2;
    220 	while (n--)
    221 		*p1++ = *p2++;
    222 	return s1;
    223 }
    224 #endif
    225 
    226 #if RCS_lint
    227 	malloc_type lintalloc;
    228 #endif
    229 
    230 /*
    231  * list of blocks allocated with ftestalloc()
    232  * These blocks can be freed by ffree when we're done with the current file.
    233  * We could put the free block inside struct alloclist, rather than a pointer
    234  * to the free block, but that would be less portable.
    235  */
    236 struct alloclist {
    237 	malloc_type alloc;
    238 	struct alloclist *nextalloc;
    239 };
    240 static struct alloclist *alloced;
    241 
    242 
    243 	static malloc_type okalloc P((malloc_type));
    244 	static malloc_type
    245 okalloc(p)
    246 	malloc_type p;
    247 {
    248 	if (!p)
    249 		faterror("out of memory");
    250 	return p;
    251 }
    252 
    253 	malloc_type
    254 testalloc(size)
    255 	size_t size;
    256 /* Allocate a block, testing that the allocation succeeded.  */
    257 {
    258 	return okalloc(malloc(size));
    259 }
    260 
    261 	malloc_type
    262 testrealloc(ptr, size)
    263 	malloc_type ptr;
    264 	size_t size;
    265 /* Reallocate a block, testing that the allocation succeeded.  */
    266 {
    267 	return okalloc(realloc(ptr, size));
    268 }
    269 
    270 	malloc_type
    271 fremember(ptr)
    272 	malloc_type ptr;
    273 /* Remember PTR in 'alloced' so that it can be freed later.  Yield PTR.  */
    274 {
    275 	register struct alloclist *q = talloc(struct alloclist);
    276 	q->nextalloc = alloced;
    277 	alloced = q;
    278 	return q->alloc = ptr;
    279 }
    280 
    281 	malloc_type
    282 ftestalloc(size)
    283 	size_t size;
    284 /* Allocate a block, putting it in 'alloced' so it can be freed later. */
    285 {
    286 	return fremember(testalloc(size));
    287 }
    288 
    289 	void
    290 ffree()
    291 /* Free all blocks allocated with ftestalloc().  */
    292 {
    293 	register struct alloclist *p, *q;
    294 	for (p = alloced;  p;  p = q) {
    295 		q = p->nextalloc;
    296 		tfree(p->alloc);
    297 		tfree(p);
    298 	}
    299 	alloced = 0;
    300 }
    301 
    302 	void
    303 ffree1(f)
    304 	register char const *f;
    305 /* Free the block f, which was allocated by ftestalloc.  */
    306 {
    307 	register struct alloclist *p, **a = &alloced;
    308 
    309 	while ((p = *a)->alloc  !=  f)
    310 		a = &p->nextalloc;
    311 	*a = p->nextalloc;
    312 	tfree(p->alloc);
    313 	tfree(p);
    314 }
    315 
    316 	char *
    317 str_save(s)
    318 	char const *s;
    319 /* Save s in permanently allocated storage. */
    320 {
    321 	return strcpy(tnalloc(char, strlen(s)+1), s);
    322 }
    323 
    324 	char *
    325 fstr_save(s)
    326 	char const *s;
    327 /* Save s in storage that will be deallocated when we're done with this file. */
    328 {
    329 	return strcpy(ftnalloc(char, strlen(s)+1), s);
    330 }
    331 
    332 	char *
    333 cgetenv(name)
    334 	char const *name;
    335 /* Like getenv(), but yield a copy; getenv() can overwrite old results. */
    336 {
    337 	register char *p;
    338 
    339 	return (p=getenv(name)) ? str_save(p) : p;
    340 }
    341 
    342 	char const *
    343 getusername(suspicious)
    344 	int suspicious;
    345 /* Get the caller's login name.  Trust only getwpuid if SUSPICIOUS.  */
    346 {
    347 	static char *name;
    348 
    349 	if (!name) {
    350 		if (
    351 		    /* Prefer getenv() unless suspicious; it's much faster.  */
    352 #		    if getlogin_is_secure
    353 			    (suspicious
    354 			    || (
    355 				    !(name = cgetenv("LOGNAME"))
    356 				&&  !(name = cgetenv("USER"))
    357 			    ))
    358 			&&  !(name = getlogin())
    359 #		    else
    360 			suspicious
    361 			|| (
    362 				!(name = cgetenv("LOGNAME"))
    363 			    &&  !(name = cgetenv("USER"))
    364 			    &&  !(name = getlogin())
    365 			)
    366 #		    endif
    367 		) {
    368 #if has_getuid && has_getpwuid
    369 			struct passwd const *pw = getpwuid(ruid());
    370 			if (!pw)
    371 			    faterror("no password entry for userid %lu",
    372 				     (unsigned long)ruid()
    373 			    );
    374 			name = strdup(pw->pw_name);
    375 #else
    376 #if has_setuid
    377 			faterror("setuid not supported");
    378 #else
    379 			faterror("Who are you?  Please setenv LOGNAME.");
    380 #endif
    381 #endif
    382 		}
    383 		checksid(name);
    384 	}
    385 	return name;
    386 }
    387 
    388 
    389 
    390 
    391 #if has_signal
    392 
    393 /*
    394  *	 Signal handling
    395  *
    396  * Standard C places too many restrictions on signal handlers.
    397  * We obey as many of them as we can.
    398  * Posix places fewer restrictions, and we are Posix-compatible here.
    399  */
    400 
    401 static sig_atomic_t volatile heldsignal, holdlevel;
    402 #ifdef SA_SIGINFO
    403 	static int unsupported_SA_SIGINFO;
    404 	static siginfo_t bufsiginfo;
    405 	static siginfo_t *volatile heldsiginfo;
    406 #endif
    407 
    408 
    409 #if has_NFS && has_mmap && large_memory && mmap_signal
    410     static char const *accessName;
    411 
    412 	  void
    413     readAccessFilenameBuffer(filename, p)
    414 	char const *filename;
    415 	unsigned char const *p;
    416     {
    417 	unsigned char volatile t;
    418 	accessName = filename;
    419 	t = *p;
    420 	accessName = 0;
    421     }
    422 #else
    423 #   define accessName ((char const *) 0)
    424 #endif
    425 
    426 
    427 #if !has_psignal
    428 
    429 # define psignal my_psignal
    430 	static void my_psignal P((int,char const*));
    431 	static void
    432 my_psignal(sig, s)
    433 	int sig;
    434 	char const *s;
    435 {
    436 	char const *sname = "Unknown signal";
    437 #	if has_sys_siglist && defined(NSIG)
    438 	    if ((unsigned)sig < NSIG)
    439 		sname = sys_siglist[sig];
    440 #	else
    441 	    switch (sig) {
    442 #	       ifdef SIGHUP
    443 		case SIGHUP:	sname = "Hangup";  break;
    444 #	       endif
    445 #	       ifdef SIGINT
    446 		case SIGINT:	sname = "Interrupt";  break;
    447 #	       endif
    448 #	       ifdef SIGPIPE
    449 		case SIGPIPE:	sname = "Broken pipe";  break;
    450 #	       endif
    451 #	       ifdef SIGQUIT
    452 		case SIGQUIT:	sname = "Quit";  break;
    453 #	       endif
    454 #	       ifdef SIGTERM
    455 		case SIGTERM:	sname = "Terminated";  break;
    456 #	       endif
    457 #	       ifdef SIGXCPU
    458 		case SIGXCPU:	sname = "Cputime limit exceeded";  break;
    459 #	       endif
    460 #	       ifdef SIGXFSZ
    461 		case SIGXFSZ:	sname = "Filesize limit exceeded";  break;
    462 #	       endif
    463 #	      if has_mmap && large_memory
    464 #	       if defined(SIGBUS) && mmap_signal==SIGBUS
    465 		case SIGBUS:	sname = "Bus error";  break;
    466 #	       endif
    467 #	       if defined(SIGSEGV) && mmap_signal==SIGSEGV
    468 		case SIGSEGV:	sname = "Segmentation fault";  break;
    469 #	       endif
    470 #	      endif
    471 	    }
    472 #	endif
    473 
    474 	/* Avoid calling sprintf etc., in case they're not reentrant.  */
    475 	{
    476 	    char const *p;
    477 	    char buf[BUFSIZ], *b = buf;
    478 	    for (p = s;  *p;  *b++ = *p++)
    479 		continue;
    480 	    *b++ = ':';
    481 	    *b++ = ' ';
    482 	    for (p = sname;  *p;  *b++ = *p++)
    483 		continue;
    484 	    *b++ = '\n';
    485 	    VOID write(STDERR_FILENO, buf, b - buf);
    486 	}
    487 }
    488 #endif
    489 
    490 static signal_type catchsig P((int));
    491 #ifdef SA_SIGINFO
    492 	static signal_type catchsigaction P((int,siginfo_t*,void*));
    493 #endif
    494 
    495 	static signal_type
    496 catchsig(s)
    497 	int s;
    498 #ifdef SA_SIGINFO
    499 {
    500 	catchsigaction(s, (siginfo_t *)0, (void *)0);
    501 }
    502 	static signal_type
    503 catchsigaction(s, i, c)
    504 	int s;
    505 	siginfo_t *i;
    506 	void *c;
    507 #endif
    508 {
    509 #   if sig_zaps_handler
    510 	/* If a signal arrives before we reset the handler, we lose. */
    511 	VOID signal(s, SIG_IGN);
    512 #   endif
    513 
    514 #   ifdef SA_SIGINFO
    515 	if (!unsupported_SA_SIGINFO)
    516 	    i = 0;
    517 #   endif
    518 
    519     if (holdlevel) {
    520 	heldsignal = s;
    521 #	ifdef SA_SIGINFO
    522 	    if (i) {
    523 		bufsiginfo = *i;
    524 		heldsiginfo = &bufsiginfo;
    525 	    }
    526 #	endif
    527 	return;
    528     }
    529 
    530     ignoreints();
    531     setrid();
    532     if (!quietflag) {
    533 	/* Avoid calling sprintf etc., in case they're not reentrant.  */
    534 	char const *p;
    535 	char buf[BUFSIZ], *b = buf;
    536 
    537 	if ( !	(
    538 #		if has_mmap && large_memory && mmap_signal
    539 			/* Check whether this signal was planned.  */
    540 			s == mmap_signal && accessName
    541 #		else
    542 			0
    543 #		endif
    544 	)) {
    545 	    char const *nRCS = "\nRCS";
    546 #	    if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal
    547 		if (s == mmap_signal  &&  i  &&  i->si_errno) {
    548 		    errno = i->si_errno;
    549 		    perror(nRCS++);
    550 		}
    551 #	    endif
    552 #	    if defined(SA_SIGINFO) && has_psiginfo
    553 		if (i)
    554 		    psiginfo(i, nRCS);
    555 		else
    556 		    psignal(s, nRCS);
    557 #	    else
    558 		psignal(s, nRCS);
    559 #	    endif
    560 	}
    561 
    562 	for (p = "RCS: ";  *p;  *b++ = *p++)
    563 	    continue;
    564 #	if has_mmap && large_memory && mmap_signal
    565 	    if (s == mmap_signal) {
    566 		p = accessName;
    567 		if (!p)
    568 		    p = "Was a file changed by some other process?  ";
    569 		else {
    570 		    char const *p1;
    571 		    for (p1 = p;  *p1;  p1++)
    572 			continue;
    573 		    VOID write(STDERR_FILENO, buf, b - buf);
    574 		    VOID write(STDERR_FILENO, p, p1 - p);
    575 		    b = buf;
    576 		    p = ": Permission denied.  ";
    577 		}
    578 		while (*p)
    579 		    *b++ = *p++;
    580 	    }
    581 #	endif
    582 	for (p = "Cleaning up.\n";  *p;  *b++ = *p++)
    583 	    continue;
    584 	VOID write(STDERR_FILENO, buf, b - buf);
    585     }
    586     exiterr();
    587 }
    588 
    589 	void
    590 ignoreints()
    591 {
    592 	++holdlevel;
    593 }
    594 
    595 	void
    596 restoreints()
    597 {
    598 	if (!--holdlevel && heldsignal)
    599 #	    ifdef SA_SIGINFO
    600 		VOID catchsigaction(heldsignal, heldsiginfo, (void *)0);
    601 #	    else
    602 		VOID catchsig(heldsignal);
    603 #	    endif
    604 }
    605 
    606 
    607 static void setup_catchsig P((int const*,int));
    608 
    609 #if has_sigaction
    610 
    611 	static void check_sig P((int));
    612 	static void
    613   check_sig(r)
    614 	int r;
    615   {
    616 	if (r != 0)
    617 		efaterror("signal handling");
    618   }
    619 
    620 	static void
    621   setup_catchsig(sig, sigs)
    622 	int const *sig;
    623 	int sigs;
    624   {
    625 	register int i, j;
    626 	struct sigaction act;
    627 
    628 	for (i=sigs; 0<=--i; ) {
    629 	    check_sig(sigaction(sig[i], (struct sigaction*)0, &act));
    630 	    if (act.sa_handler != SIG_IGN) {
    631 		act.sa_handler = catchsig;
    632 #		ifdef SA_SIGINFO
    633 		    if (!unsupported_SA_SIGINFO) {
    634 #			if has_sa_sigaction
    635 			    act.sa_sigaction = catchsigaction;
    636 #			else
    637 			    act.sa_handler = catchsigaction;
    638 #			endif
    639 			act.sa_flags |= SA_SIGINFO;
    640 		    }
    641 #		endif
    642 		for (j=sigs; 0<=--j; )
    643 		    check_sig(sigaddset(&act.sa_mask, sig[j]));
    644 		if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) {
    645 #		    if defined(SA_SIGINFO) && defined(ENOTSUP)
    646 			if (errno == ENOTSUP  &&  !unsupported_SA_SIGINFO) {
    647 			    /* Turn off use of SA_SIGINFO and try again.  */
    648 			    unsupported_SA_SIGINFO = 1;
    649 			    i++;
    650 			    continue;
    651 			}
    652 #		    endif
    653 		    check_sig(-1);
    654 		}
    655 	    }
    656 	}
    657   }
    658 
    659 #else
    660 #if has_sigblock
    661 
    662 	static void
    663   setup_catchsig(sig, sigs)
    664 	int const *sig;
    665 	int sigs;
    666   {
    667 	register int i;
    668 	int mask;
    669 
    670 	mask = 0;
    671 	for (i=sigs; 0<=--i; )
    672 		mask |= sigmask(sig[i]);
    673 	mask = sigblock(mask);
    674 	for (i=sigs; 0<=--i; )
    675 		if (
    676 		    signal(sig[i], catchsig) == SIG_IGN  &&
    677 		    signal(sig[i], SIG_IGN) != catchsig
    678 		)
    679 			faterror("signal catcher failure");
    680 	VOID sigsetmask(mask);
    681   }
    682 
    683 #else
    684 
    685 	static void
    686   setup_catchsig(sig, sigs)
    687 	int const *sig;
    688 	int sigs;
    689   {
    690 	register i;
    691 
    692 	for (i=sigs; 0<=--i; )
    693 		if (
    694 		    signal(sig[i], SIG_IGN) != SIG_IGN  &&
    695 		    signal(sig[i], catchsig) != SIG_IGN
    696 		)
    697 			faterror("signal catcher failure");
    698   }
    699 
    700 #endif
    701 #endif
    702 
    703 
    704 static int const regsigs[] = {
    705 # ifdef SIGHUP
    706 	SIGHUP,
    707 # endif
    708 # ifdef SIGINT
    709 	SIGINT,
    710 # endif
    711 # ifdef SIGPIPE
    712 	SIGPIPE,
    713 # endif
    714 # ifdef SIGQUIT
    715 	SIGQUIT,
    716 # endif
    717 # ifdef SIGTERM
    718 	SIGTERM,
    719 # endif
    720 # ifdef SIGXCPU
    721 	SIGXCPU,
    722 # endif
    723 # ifdef SIGXFSZ
    724 	SIGXFSZ,
    725 # endif
    726 };
    727 
    728 	void
    729 catchints()
    730 {
    731 	static int catching_ints;
    732 	if (!catching_ints) {
    733 	    catching_ints = true;
    734 	    setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs)));
    735 	}
    736 }
    737 
    738 #if has_mmap && large_memory && mmap_signal
    739 
    740     /*
    741     * If you mmap an NFS file, and someone on another client removes the last
    742     * link to that file, and you later reference an uncached part of that file,
    743     * you'll get a SIGBUS or SIGSEGV (depending on the operating system).
    744     * Catch the signal and report the problem to the user.
    745     * Unfortunately, there's no portable way to differentiate between this
    746     * problem and actual bugs in the program.
    747     * This NFS problem is rare, thank goodness.
    748     *
    749     * This can also occur if someone truncates the file, even without NFS.
    750     */
    751 
    752     static int const mmapsigs[] = { mmap_signal };
    753 
    754 	    void
    755     catchmmapints()
    756     {
    757 	static int catching_mmap_ints;
    758 	if (!catching_mmap_ints) {
    759 	    catching_mmap_ints = true;
    760 	    setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs)));
    761 	}
    762     }
    763 #endif
    764 
    765 #endif /* has_signal */
    766 
    767 
    768 	void
    769 fastcopy(inf,outf)
    770 	register RILE *inf;
    771 	FILE *outf;
    772 /* Function: copies the remainder of file inf to outf.
    773  */
    774 {
    775 #if large_memory
    776 #	if maps_memory
    777 	    awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
    778 	    inf->ptr = inf->lim;
    779 #	else
    780 	    for (;;) {
    781 		awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf);
    782 		inf->ptr = inf->readlim;
    783 		if (inf->ptr == inf->lim)
    784 		    break;
    785 		VOID Igetmore(inf);
    786 	    }
    787 #	endif
    788 #else
    789 	char buf[BUFSIZ*8];
    790 	register fread_type rcount;
    791 
    792         /*now read the rest of the file in blocks*/
    793 	while (!feof(inf)) {
    794 		if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) {
    795 			testIerror(inf);
    796 			return;
    797 		}
    798 		awrite(buf, (size_t)rcount, outf);
    799         }
    800 #endif
    801 }
    802 
    803 #ifndef SSIZE_MAX
    804  /* This does not work in #ifs, but it's good enough for us.  */
    805  /* Underestimating SSIZE_MAX may slow us down, but it won't break us.  */
    806 #	define SSIZE_MAX ((unsigned)-1 >> 1)
    807 #endif
    808 
    809 	void
    810 awrite(buf, chars, f)
    811 	char const *buf;
    812 	size_t chars;
    813 	FILE *f;
    814 {
    815 	if (buf == NULL)
    816 		return;
    817 
    818 	/* Posix 1003.1-1990 ssize_t hack */
    819 	while (SSIZE_MAX < chars) {
    820 		if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f)  !=  SSIZE_MAX)
    821 			Oerror();
    822 		buf += SSIZE_MAX;
    823 		chars -= SSIZE_MAX;
    824 	}
    825 
    826 	if (Fwrite(buf, sizeof(*buf), chars, f)  !=  chars)
    827 		Oerror();
    828 }
    829 
    830 /* dup a file descriptor; the result must not be stdin, stdout, or stderr.  */
    831 	static int dupSafer P((int));
    832 	static int
    833 dupSafer(fd)
    834 	int fd;
    835 {
    836 #	ifdef F_DUPFD
    837 	    return fcntl(fd, F_DUPFD, STDERR_FILENO + 1);
    838 #	else
    839 	    int e, f, i, used = 0;
    840 	    while (STDIN_FILENO <= (f = dup(fd))  &&  f <= STDERR_FILENO)
    841 		    used |= 1<<f;
    842 	    e = errno;
    843 	    for (i = STDIN_FILENO;  i <= STDERR_FILENO;  i++)
    844 		    if (used & (1<<i))
    845 			    VOID close(i);
    846 	    errno = e;
    847 	    return f;
    848 #	endif
    849 }
    850 
    851 /* Renumber a file descriptor so that it's not stdin, stdout, or stderr.  */
    852 	int
    853 fdSafer(fd)
    854 	int fd;
    855 {
    856 	if (STDIN_FILENO <= fd  &&  fd <= STDERR_FILENO) {
    857 		int f = dupSafer(fd);
    858 		int e = errno;
    859 		VOID close(fd);
    860 		errno = e;
    861 		fd = f;
    862 	}
    863 	return fd;
    864 }
    865 
    866 /* Like fopen, except the result is never stdin, stdout, or stderr.  */
    867 	FILE *
    868 fopenSafer(filename, type)
    869 	char const *filename;
    870 	char const *type;
    871 {
    872 	FILE *stream = fopen(filename, type);
    873 	if (stream) {
    874 		int fd = fileno(stream);
    875 		if (STDIN_FILENO <= fd  &&  fd <= STDERR_FILENO) {
    876 			int f = dupSafer(fd);
    877 			if (f < 0) {
    878 				int e = errno;
    879 				VOID fclose(stream);
    880 				errno = e;
    881 				return 0;
    882 			}
    883 			if (fclose(stream) != 0) {
    884 				int e = errno;
    885 				VOID close(f);
    886 				errno = e;
    887 				return 0;
    888 			}
    889 			stream = fdopen(f, type);
    890 		}
    891 	}
    892 	return stream;
    893 }
    894 
    895 
    896 #ifdef F_DUPFD
    897 #	undef dup
    898 #	define dup(fd) fcntl(fd, F_DUPFD, 0)
    899 #endif
    900 
    901 
    902 #if has_fork || has_spawn
    903 
    904 	static int movefd P((int,int));
    905 	static int
    906 movefd(old, new)
    907 	int old, new;
    908 {
    909 	if (old < 0  ||  old == new)
    910 		return old;
    911 #	ifdef F_DUPFD
    912 		new = fcntl(old, F_DUPFD, new);
    913 #	else
    914 		new = dup2(old, new);
    915 #	endif
    916 	return close(old)==0 ? new : -1;
    917 }
    918 
    919 	static int fdreopen P((int,char const*,int));
    920 	static int
    921 fdreopen(fd, file, flags)
    922 	int fd;
    923 	char const *file;
    924 	int flags;
    925 {
    926 	int newfd;
    927 	VOID close(fd);
    928 	newfd =
    929 #if !open_can_creat
    930 		flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
    931 #endif
    932 		open(file, flags, S_IRUSR|S_IWUSR);
    933 	return movefd(newfd, fd);
    934 }
    935 
    936 #if has_spawn
    937 	static void redirect P((int,int));
    938 	static void
    939 redirect(old, new)
    940 	int old, new;
    941 /*
    942 * Move file descriptor OLD to NEW.
    943 * If OLD is -1, do nothing.
    944 * If OLD is -2, just close NEW.
    945 */
    946 {
    947 	if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0))
    948 		efaterror("spawn I/O redirection");
    949 }
    950 #endif
    951 
    952 
    953 #else /* !has_fork && !has_spawn */
    954 
    955 	static void bufargcat P((struct buf*,int,char const*));
    956 	static void
    957 bufargcat(b, c, s)
    958 	register struct buf *b;
    959 	int c;
    960 	register char const *s;
    961 /* Append to B a copy of C, plus a quoted copy of S.  */
    962 {
    963 	register char *p;
    964 	register char const *t;
    965 	size_t bl, sl;
    966 
    967 	for (t=s, sl=0;  *t;  )
    968 		sl  +=  3*(*t++=='\'') + 1;
    969 	bl = strlen(b->string);
    970 	bufrealloc(b, bl + sl + 4);
    971 	p = b->string + bl;
    972 	*p++ = c;
    973 	*p++ = '\'';
    974 	while (*s) {
    975 		if (*s == '\'') {
    976 			*p++ = '\'';
    977 			*p++ = '\\';
    978 			*p++ = '\'';
    979 		}
    980 		*p++ = *s++;
    981 	}
    982 	*p++ = '\'';
    983 	*p = 0;
    984 }
    985 
    986 #endif
    987 
    988 #if !has_spawn && has_fork
    989 /*
    990 * Output the string S to stderr, without touching any I/O buffers.
    991 * This is useful if you are a child process, whose buffers are usually wrong.
    992 * Exit immediately if the write does not completely succeed.
    993 */
    994 static void write_stderr P((char const *));
    995 	static void
    996 write_stderr(s)
    997 	char const *s;
    998 {
    999 	size_t slen = strlen(s);
   1000 	if (write(STDERR_FILENO, s, slen) != slen)
   1001 		_exit(EXIT_TROUBLE);
   1002 }
   1003 #endif
   1004 
   1005 /*
   1006 * Run a command.
   1007 * infd, if not -1, is the input file descriptor.
   1008 * outname, if nonzero, is the name of the output file.
   1009 * args[1..] form the command to be run; args[0] might be modified.
   1010 */
   1011 	int
   1012 runv(infd, outname, args)
   1013 	int infd;
   1014 	char const *outname, **args;
   1015 {
   1016 	int wstatus;
   1017 
   1018 #if bad_wait_if_SIGCHLD_ignored
   1019 	static int fixed_SIGCHLD;
   1020 	if (!fixed_SIGCHLD) {
   1021 	    fixed_SIGCHLD = true;
   1022 #	    ifndef SIGCHLD
   1023 #	    define SIGCHLD SIGCLD
   1024 #	    endif
   1025 	    VOID signal(SIGCHLD, SIG_DFL);
   1026 	}
   1027 #endif
   1028 
   1029 	oflush();
   1030 	eflush();
   1031     {
   1032 #if has_spawn
   1033 	int in, out;
   1034 	char const *file;
   1035 
   1036 	in = -1;
   1037 	if (infd != -1  &&  infd != STDIN_FILENO) {
   1038 	    if ((in = dup(STDIN_FILENO)) < 0) {
   1039 		if (errno != EBADF)
   1040 		    efaterror("spawn input setup");
   1041 		in = -2;
   1042 	    } else {
   1043 #		ifdef F_DUPFD
   1044 		    if (close(STDIN_FILENO) != 0)
   1045 			efaterror("spawn input close");
   1046 #		endif
   1047 	    }
   1048 	    if (
   1049 #		ifdef F_DUPFD
   1050 		    fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO
   1051 #		else
   1052 		    dup2(infd, STDIN_FILENO) != STDIN_FILENO
   1053 #		endif
   1054 	    )
   1055 		efaterror("spawn input redirection");
   1056 	}
   1057 
   1058 	out = -1;
   1059 	if (outname) {
   1060 	    if ((out = dup(STDOUT_FILENO)) < 0) {
   1061 		if (errno != EBADF)
   1062 		    efaterror("spawn output setup");
   1063 		out = -2;
   1064 	    }
   1065 	    if (fdreopen(
   1066 		STDOUT_FILENO, outname,
   1067 		O_CREAT | O_TRUNC | O_WRONLY
   1068 	    ) < 0)
   1069 		efaterror(outname);
   1070 	}
   1071 
   1072 	wstatus = spawn_RCS(0, args[1], (char**)(args + 1));
   1073 #	ifdef RCS_SHELL
   1074 	    if (wstatus == -1  &&  errno == ENOEXEC) {
   1075 		args[0] = RCS_SHELL;
   1076 		wstatus = spawnv(0, args[0], (char**)args);
   1077 	    }
   1078 #	endif
   1079 	redirect(in, STDIN_FILENO);
   1080 	redirect(out, STDOUT_FILENO);
   1081 #else
   1082 #if has_fork
   1083 	pid_t pid;
   1084 	if (!(pid = vfork())) {
   1085 		char const *notfound;
   1086 		if (infd != -1  &&  infd != STDIN_FILENO  &&  (
   1087 #		    ifdef F_DUPFD
   1088 			(VOID close(STDIN_FILENO),
   1089 			fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO)
   1090 #		    else
   1091 			dup2(infd, STDIN_FILENO) != STDIN_FILENO
   1092 #		    endif
   1093 		)) {
   1094 		    /* Avoid perror since it may misuse buffers.  */
   1095 		    write_stderr(args[1]);
   1096 		    write_stderr(": I/O redirection failed\n");
   1097 		    _exit(EXIT_TROUBLE);
   1098 		}
   1099 
   1100 		if (outname)
   1101 		    if (fdreopen(
   1102 			STDOUT_FILENO, outname,
   1103 			O_CREAT | O_TRUNC | O_WRONLY
   1104 		    ) < 0) {
   1105 			/* Avoid perror since it may misuse buffers.  */
   1106 			write_stderr(args[1]);
   1107 			write_stderr(": ");
   1108 			write_stderr(outname);
   1109 			write_stderr(": cannot create\n");
   1110 			_exit(EXIT_TROUBLE);
   1111 		    }
   1112 		VOID exec_RCS(args[1], (char**)(args + 1));
   1113 		notfound = args[1];
   1114 #		ifdef RCS_SHELL
   1115 		    if (errno == ENOEXEC) {
   1116 			args[0] = notfound = RCS_SHELL;
   1117 			VOID execv(args[0], (char**)args);
   1118 		    }
   1119 #		endif
   1120 
   1121 		/* Avoid perror since it may misuse buffers.  */
   1122 		write_stderr(notfound);
   1123 		write_stderr(": not found\n");
   1124 		_exit(EXIT_TROUBLE);
   1125 	}
   1126 	if (pid < 0)
   1127 		efaterror("fork");
   1128 #	if has_waitpid
   1129 		if (waitpid(pid, &wstatus, 0) < 0)
   1130 			efaterror("waitpid");
   1131 #	else
   1132 		{
   1133 			pid_t w;
   1134 			do {
   1135 				if ((w = wait(&wstatus)) < 0)
   1136 					efaterror("wait");
   1137 			} while (w != pid);
   1138 		}
   1139 #	endif
   1140 #else
   1141 	static struct buf b;
   1142 	char const *p;
   1143 
   1144 	/* Use system().  On many hosts system() discards signals.  Yuck!  */
   1145 	p = args + 1;
   1146 	bufscpy(&b, *p);
   1147 	while (*++p)
   1148 		bufargcat(&b, ' ', *p);
   1149 	if (infd != -1  &&  infd != STDIN_FILENO) {
   1150 		char redirection[32];
   1151 		VOID sprintf(redirection, "<&%d", infd);
   1152 		bufscat(&b, redirection);
   1153 	}
   1154 	if (outname)
   1155 		bufargcat(&b, '>', outname);
   1156 	wstatus = system(b.string);
   1157 #endif
   1158 #endif
   1159     }
   1160 	if (!WIFEXITED(wstatus)) {
   1161 		if (WIFSIGNALED(wstatus)) {
   1162 			psignal(WTERMSIG(wstatus), args[1]);
   1163 			fatcleanup(1);
   1164 		}
   1165 		faterror("%s failed for unknown reason", args[1]);
   1166 	}
   1167 	return WEXITSTATUS(wstatus);
   1168 }
   1169 
   1170 #define CARGSMAX 20
   1171 /*
   1172 * Run a command.
   1173 * infd, if not -1, is the input file descriptor.
   1174 * outname, if nonzero, is the name of the output file.
   1175 * The remaining arguments specify the command and its arguments.
   1176 */
   1177 	int
   1178 #if has_prototypes
   1179 run(int infd, char const *outname, ...)
   1180 #else
   1181 	/*VARARGS2*/
   1182 run(infd, outname, va_alist)
   1183 	int infd;
   1184 	char const *outname;
   1185 	va_dcl
   1186 #endif
   1187 {
   1188 	va_list ap;
   1189 	char const *rgargs[CARGSMAX];
   1190 	register int i;
   1191 	vararg_start(ap, outname);
   1192 	for (i = 1;  (rgargs[i++] = va_arg(ap, char const*));  )
   1193 		if (CARGSMAX <= i)
   1194 			faterror("too many command arguments");
   1195 	va_end(ap);
   1196 	return runv(infd, outname, rgargs);
   1197 }
   1198 
   1199 
   1200 int RCSversion;
   1201 
   1202 	void
   1203 setRCSversion(str)
   1204 	char const *str;
   1205 {
   1206 	static int oldversion;
   1207 
   1208 	register char const *s = str + 2;
   1209 
   1210 	if (*s) {
   1211 		int v = VERSION_DEFAULT;
   1212 
   1213 		if (oldversion)
   1214 			redefined('V');
   1215 		oldversion = true;
   1216 		v = 0;
   1217 		while (isdigit(*s))
   1218 			v  =  10*v + *s++ - '0';
   1219 		if (*s)
   1220 			error("%s isn't a number", str);
   1221 		else if (v < VERSION_min  ||  VERSION_max < v)
   1222 			error("%s out of range %d..%d",
   1223 				str, VERSION_min, VERSION_max
   1224 			);
   1225 
   1226 		RCSversion = VERSION(v);
   1227 	} else {
   1228 		printf("RCS version %s\n", RCS_version_string);
   1229 		exit(0);
   1230 	}
   1231 }
   1232 
   1233 	int
   1234 getRCSINIT(argc, argv, newargv)
   1235 	int argc;
   1236 	char **argv, ***newargv;
   1237 {
   1238 	register char *p, *q, **pp;
   1239 	size_t n;
   1240 
   1241 	if (!(q = cgetenv("RCSINIT")))
   1242 		*newargv = argv;
   1243 	else {
   1244 		n = argc + 2;
   1245 		/*
   1246 		 * Count spaces in RCSINIT to allocate a new arg vector.
   1247 		 * This is an upper bound, but it's OK even if too large.
   1248 		 */
   1249 		for (p = q;  ;  ) {
   1250 			switch (*p++) {
   1251 			    default:
   1252 				continue;
   1253 
   1254 			    case ' ':
   1255 			    case '\b': case '\f': case '\n':
   1256 			    case '\r': case '\t': case '\v':
   1257 				n++;
   1258 				continue;
   1259 
   1260 			    case '\0':
   1261 				break;
   1262 			}
   1263 			break;
   1264 		}
   1265 		*newargv = pp = tnalloc(char*, n);
   1266 		*pp++ = *argv++; /* copy program name */
   1267 		for (p = q;  ;  ) {
   1268 			for (;;) {
   1269 				switch (*q) {
   1270 				    case '\0':
   1271 					goto copyrest;
   1272 
   1273 				    case ' ':
   1274 				    case '\b': case '\f': case '\n':
   1275 				    case '\r': case '\t': case '\v':
   1276 					q++;
   1277 					continue;
   1278 				}
   1279 				break;
   1280 			}
   1281 			*pp++ = p;
   1282 			++argc;
   1283 			for (;;) {
   1284 				switch ((*p++ = *q++)) {
   1285 				    case '\0':
   1286 					goto copyrest;
   1287 
   1288 				    case '\\':
   1289 					if (!*q)
   1290 						goto copyrest;
   1291 					p[-1] = *q++;
   1292 					continue;
   1293 
   1294 				    default:
   1295 					continue;
   1296 
   1297 				    case ' ':
   1298 				    case '\b': case '\f': case '\n':
   1299 				    case '\r': case '\t': case '\v':
   1300 					break;
   1301 				}
   1302 				break;
   1303 			}
   1304 			p[-1] = '\0';
   1305 		}
   1306 	    copyrest:
   1307 		while ((*pp++ = *argv++))
   1308 			continue;
   1309 	}
   1310 	return argc;
   1311 }
   1312 
   1313 
   1314 #define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
   1315 
   1316 #if has_getuid
   1317 	uid_t ruid() { cacheid(getuid()); }
   1318 #endif
   1319 #if has_setuid
   1320 	uid_t euid() { cacheid(geteuid()); }
   1321 #endif
   1322 
   1323 
   1324 #if has_setuid
   1325 
   1326 /*
   1327  * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
   1328  * because it lets us switch back and forth between arbitrary users.
   1329  * If seteuid() doesn't work, we fall back on setuid(),
   1330  * which works if saved setuid is supported,
   1331  * unless the real or effective user is root.
   1332  * This area is such a mess that we always check switches at runtime.
   1333  */
   1334 
   1335 	static void
   1336 #if has_prototypes
   1337 set_uid_to(uid_t u)
   1338 #else
   1339  set_uid_to(u) uid_t u;
   1340 #endif
   1341 /* Become user u.  */
   1342 {
   1343 	static int looping;
   1344 
   1345 	if (euid() == ruid())
   1346 		return;
   1347 #if (has_fork||has_spawn) && DIFF_ABSOLUTE
   1348 #	if has_setreuid
   1349 		if (setreuid(u==euid() ? ruid() : euid(), u) != 0)
   1350 			efaterror("setuid");
   1351 #	else
   1352 		if (seteuid(u) != 0)
   1353 			efaterror("setuid");
   1354 #	endif
   1355 #endif
   1356 	if (geteuid() != u) {
   1357 		if (looping)
   1358 			return;
   1359 		looping = true;
   1360 		faterror("root setuid not supported" + (u?5:0));
   1361 	}
   1362 }
   1363 
   1364 static int stick_with_euid;
   1365 
   1366 	void
   1367 /* Ignore all calls to seteid() and setrid().  */
   1368 nosetid()
   1369 {
   1370 	stick_with_euid = true;
   1371 }
   1372 
   1373 	void
   1374 seteid()
   1375 /* Become effective user.  */
   1376 {
   1377 	if (!stick_with_euid)
   1378 		set_uid_to(euid());
   1379 }
   1380 
   1381 	void
   1382 setrid()
   1383 /* Become real user.  */
   1384 {
   1385 	if (!stick_with_euid)
   1386 		set_uid_to(ruid());
   1387 }
   1388 #endif
   1389 
   1390 	time_t
   1391 now()
   1392 {
   1393 	static time_t t;
   1394 	if (!t  &&  time(&t) == -1)
   1395 		efaterror("time");
   1396 	return t;
   1397 }
   1398