111894Speter/* RCS utility functions */
29Sjkh
311894Speter/* Copyright 1982, 1988, 1989 Walter Tichy
411894Speter   Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
59Sjkh   Distributed under license by the Free Software Foundation, Inc.
69Sjkh
79SjkhThis file is part of RCS.
89Sjkh
99SjkhRCS is free software; you can redistribute it and/or modify
109Sjkhit under the terms of the GNU General Public License as published by
119Sjkhthe Free Software Foundation; either version 2, or (at your option)
129Sjkhany later version.
139Sjkh
149SjkhRCS is distributed in the hope that it will be useful,
159Sjkhbut WITHOUT ANY WARRANTY; without even the implied warranty of
169SjkhMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
179SjkhGNU General Public License for more details.
189Sjkh
199SjkhYou should have received a copy of the GNU General Public License
2011894Speteralong with RCS; see the file COPYING.
2111894SpeterIf not, write to the Free Software Foundation,
2211894Speter59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
239Sjkh
249SjkhReport problems and direct all questions to:
259Sjkh
269Sjkh    rcs-bugs@cs.purdue.edu
279Sjkh
289Sjkh*/
299Sjkh
309Sjkh
319Sjkh
329Sjkh
3311894Speter/*
3411894Speter * Revision 5.20  1995/06/16 06:19:24  eggert
3511894Speter * (catchsig): Remove `return'.
3611894Speter * Update FSF address.
378858Srgrimes *
3811894Speter * Revision 5.19  1995/06/02 18:19:00  eggert
3911894Speter * (catchsigaction): New name for `catchsig', for sa_sigaction signature.
4011894Speter * Use nRCS even if !has_psiginfo, to remove unused variable warning.
4111894Speter * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction.
4211894Speter * Use ENOTSUP only if defined.
4311894Speter *
4411894Speter * Revision 5.18  1995/06/01 16:23:43  eggert
4511894Speter * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo,
4611894Speter * to determine whether to use SA_SIGINFO feature,
4711894Speter * but also check at runtime whether the feature works.
4811894Speter * (catchsig): If an mmap_signal occurs, report the affected file name.
4911894Speter * (unsupported_SA_SIGINFO, accessName): New variables.
5011894Speter * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler.
5111894Speter * If SA_SIGINFO fails, fall back on sa_handler method.
5211894Speter *
5311894Speter * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions.
5411894Speter * (concatenate): Remove.
5511894Speter *
5611894Speter * (runv): Work around bad_wait_if_SIGCHLD_ignored bug.
5711894Speter * Remove reference to OPEN_O_WORK.
5811894Speter *
5911894Speter * Revision 5.17  1994/03/20 04:52:58  eggert
6011894Speter * Specify subprocess input via file descriptor, not file name.
6111894Speter * Avoid messing with I/O buffers in the child process.
6211894Speter * Define dup in terms of F_DUPFD if it exists.
6311894Speter * Move setmtime to rcsedit.c.  Remove lint.
6411894Speter *
6511894Speter * Revision 5.16  1993/11/09 17:40:15  eggert
6611894Speter * -V now prints version on stdout and exits.
6711894Speter *
6811894Speter * Revision 5.15  1993/11/03 17:42:27  eggert
6911894Speter * Use psiginfo and setreuid if available.  Move date2str to maketime.c.
7011894Speter *
7111894Speter * Revision 5.14  1992/07/28  16:12:44  eggert
7211894Speter * Add -V.  has_sigaction overrides sig_zaps_handler.  Fix -M bug.
7311894Speter * Add mmap_signal, which minimizes signal handling for non-mmap hosts.
7411894Speter *
7511894Speter * Revision 5.13  1992/02/17  23:02:28  eggert
7611894Speter * Work around NFS mmap SIGBUS problem.  Add -T support.
7711894Speter *
7811894Speter * Revision 5.12  1992/01/24  18:44:19  eggert
7911894Speter * Work around NFS mmap bug that leads to SIGBUS core dumps.  lint -> RCS_lint
8011894Speter *
8111894Speter * Revision 5.11  1992/01/06  02:42:34  eggert
8211894Speter * O_BINARY -> OPEN_O_WORK
8311894Speter * while (E) ; -> while (E) continue;
8411894Speter *
859Sjkh * Revision 5.10  1991/10/07  17:32:46  eggert
869Sjkh * Support piece tables even if !has_mmap.
879Sjkh *
889Sjkh * Revision 5.9  1991/08/19  03:13:55  eggert
899Sjkh * Add spawn() support.  Explicate assumptions about getting invoker's name.
909Sjkh * Standardize user-visible dates.  Tune.
919Sjkh *
929Sjkh * Revision 5.8  1991/04/21  11:58:30  eggert
939Sjkh * Plug setuid security hole.
949Sjkh *
959Sjkh * Revision 5.6  1991/02/26  17:48:39  eggert
969Sjkh * Fix setuid bug.  Use fread, fwrite more portably.
979Sjkh * Support waitpid.  Don't assume -1 is acceptable to W* macros.
989Sjkh * strsave -> str_save (DG/UX name clash)
999Sjkh *
1009Sjkh * Revision 5.5  1990/12/04  05:18:49  eggert
1019Sjkh * Don't output a blank line after a signal diagnostic.
1029Sjkh * Use -I for prompts and -q for diagnostics.
1039Sjkh *
1049Sjkh * Revision 5.4  1990/11/01  05:03:53  eggert
1059Sjkh * Remove unneeded setid check.  Add awrite(), fremember().
1069Sjkh *
1079Sjkh * Revision 5.3  1990/10/06  00:16:45  eggert
1089Sjkh * Don't fread F if feof(F).
1099Sjkh *
1109Sjkh * Revision 5.2  1990/09/04  08:02:31  eggert
1119Sjkh * Store fread()'s result in an fread_type object.
1129Sjkh *
1139Sjkh * Revision 5.1  1990/08/29  07:14:07  eggert
1149Sjkh * Declare getpwuid() more carefully.
1159Sjkh *
1169Sjkh * Revision 5.0  1990/08/22  08:13:46  eggert
1179Sjkh * Add setuid support.  Permit multiple locks per user.
1189Sjkh * Remove compile-time limits; use malloc instead.
1199Sjkh * Switch to GMT.  Permit dates past 1999/12/31.
1209Sjkh * Add -V.  Remove snooping.  Ansify and Posixate.
1219Sjkh * Tune.  Some USG hosts define NSIG but not sys_siglist.
1229Sjkh * Don't run /bin/sh if it's hopeless.
1239Sjkh * Don't leave garbage behind if the output is an empty pipe.
1249Sjkh * Clean up after SIGXCPU or SIGXFSZ.  Print name of signal that caused cleanup.
1259Sjkh *
1269Sjkh * Revision 4.6  89/05/01  15:13:40  narten
1279Sjkh * changed copyright header to reflect current distribution rules
1288858Srgrimes *
1299Sjkh * Revision 4.5  88/11/08  16:01:02  narten
1309Sjkh * corrected use of varargs routines
1318858Srgrimes *
1329Sjkh * Revision 4.4  88/08/09  19:13:24  eggert
1339Sjkh * Check for memory exhaustion.
1349Sjkh * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
1359Sjkh * Use execv(), not system(); yield exit status like diff(1)'s.
1368858Srgrimes *
1379Sjkh * Revision 4.3  87/10/18  10:40:22  narten
1389Sjkh * Updating version numbers. Changes relative to 1.1 actually
1399Sjkh * relative to 4.1
1408858Srgrimes *
1419Sjkh * Revision 1.3  87/09/24  14:01:01  narten
1428858Srgrimes * Sources now pass through lint (if you ignore printf/sprintf/fprintf
1439Sjkh * warnings)
1448858Srgrimes *
1459Sjkh * Revision 1.2  87/03/27  14:22:43  jenkins
1469Sjkh * Port to suns
1478858Srgrimes *
1489Sjkh * Revision 4.1  83/05/10  15:53:13  wft
1499Sjkh * Added getcaller() and findlock().
1509Sjkh * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
1519Sjkh * (needed for background jobs in older shells). Added restoreints().
1529Sjkh * Removed printing of full RCS path from logcommand().
1538858Srgrimes *
1549Sjkh * Revision 3.8  83/02/15  15:41:49  wft
1559Sjkh * Added routine fastcopy() to copy remainder of a file in blocks.
1569Sjkh *
1579Sjkh * Revision 3.7  82/12/24  15:25:19  wft
1589Sjkh * added catchints(), ignoreints() for catching and ingnoring interrupts;
1599Sjkh * fixed catchsig().
1609Sjkh *
1619Sjkh * Revision 3.6  82/12/08  21:52:05  wft
1629Sjkh * Using DATEFORM to format dates.
1639Sjkh *
1649Sjkh * Revision 3.5  82/12/04  18:20:49  wft
1659Sjkh * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
1669Sjkh * lockedby-field.
1679Sjkh *
1689Sjkh * Revision 3.4  82/12/03  17:17:43  wft
1699Sjkh * Added check to addlock() ensuring only one lock per person.
1709Sjkh * Addlock also returns a pointer to the lock created. Deleted fancydate().
1719Sjkh *
1729Sjkh * Revision 3.3  82/11/27  12:24:37  wft
1739Sjkh * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
1749Sjkh * Introduced macro SNOOP so that snoop can be placed in directory other than
1759Sjkh * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
1769Sjkh *
1779Sjkh * Revision 3.2  82/10/18  21:15:11  wft
1789Sjkh * added function getfullRCSname().
1799Sjkh *
1809Sjkh * Revision 3.1  82/10/13  16:17:37  wft
1819Sjkh * Cleanup message is now suppressed in quiet mode.
1829Sjkh */
1839Sjkh
1849Sjkh
1859Sjkh
1869Sjkh
1879Sjkh#include "rcsbase.h"
1889Sjkh
18950472SpeterlibId(utilId, "$FreeBSD: releng/10.3/gnu/usr.bin/rcs/lib/rcsutil.c 50472 1999-08-27 23:37:10Z peter $")
1909Sjkh
1919Sjkh#if !has_memcmp
1929Sjkh	int
1939Sjkhmemcmp(s1, s2, n)
1949Sjkh	void const *s1, *s2;
1959Sjkh	size_t n;
1969Sjkh{
1979Sjkh	register unsigned char const
1989Sjkh		*p1 = (unsigned char const*)s1,
1999Sjkh		*p2 = (unsigned char const*)s2;
2009Sjkh	register size_t i = n;
2019Sjkh	register int r = 0;
2029Sjkh	while (i--  &&  !(r = (*p1++ - *p2++)))
2039Sjkh		;
2049Sjkh	return r;
2059Sjkh}
2069Sjkh#endif
2079Sjkh
2089Sjkh#if !has_memcpy
2099Sjkh	void *
2109Sjkhmemcpy(s1, s2, n)
2119Sjkh	void *s1;
2129Sjkh	void const *s2;
2139Sjkh	size_t n;
2149Sjkh{
2159Sjkh	register char *p1 = (char*)s1;
2169Sjkh	register char const *p2 = (char const*)s2;
2179Sjkh	while (n--)
2189Sjkh		*p1++ = *p2++;
2199Sjkh	return s1;
2209Sjkh}
2219Sjkh#endif
2229Sjkh
22311894Speter#if RCS_lint
2249Sjkh	malloc_type lintalloc;
2259Sjkh#endif
2269Sjkh
2279Sjkh/*
2289Sjkh * list of blocks allocated with ftestalloc()
2299Sjkh * These blocks can be freed by ffree when we're done with the current file.
2309Sjkh * We could put the free block inside struct alloclist, rather than a pointer
2319Sjkh * to the free block, but that would be less portable.
2329Sjkh */
2339Sjkhstruct alloclist {
2349Sjkh	malloc_type alloc;
2359Sjkh	struct alloclist *nextalloc;
2369Sjkh};
2379Sjkhstatic struct alloclist *alloced;
2389Sjkh
2399Sjkh
24011894Speter	static malloc_type okalloc P((malloc_type));
2419Sjkh	static malloc_type
2429Sjkhokalloc(p)
2439Sjkh	malloc_type p;
2449Sjkh{
2459Sjkh	if (!p)
2469Sjkh		faterror("out of memory");
2479Sjkh	return p;
2489Sjkh}
2499Sjkh
2509Sjkh	malloc_type
2519Sjkhtestalloc(size)
2529Sjkh	size_t size;
2539Sjkh/* Allocate a block, testing that the allocation succeeded.  */
2549Sjkh{
2559Sjkh	return okalloc(malloc(size));
2569Sjkh}
2579Sjkh
2589Sjkh	malloc_type
2599Sjkhtestrealloc(ptr, size)
2609Sjkh	malloc_type ptr;
2619Sjkh	size_t size;
2629Sjkh/* Reallocate a block, testing that the allocation succeeded.  */
2639Sjkh{
2649Sjkh	return okalloc(realloc(ptr, size));
2659Sjkh}
2669Sjkh
2679Sjkh	malloc_type
2689Sjkhfremember(ptr)
2699Sjkh	malloc_type ptr;
2709Sjkh/* Remember PTR in 'alloced' so that it can be freed later.  Yield PTR.  */
2719Sjkh{
2729Sjkh	register struct alloclist *q = talloc(struct alloclist);
2739Sjkh	q->nextalloc = alloced;
2749Sjkh	alloced = q;
2759Sjkh	return q->alloc = ptr;
2769Sjkh}
2779Sjkh
2789Sjkh	malloc_type
2799Sjkhftestalloc(size)
2809Sjkh	size_t size;
2819Sjkh/* Allocate a block, putting it in 'alloced' so it can be freed later. */
2829Sjkh{
2839Sjkh	return fremember(testalloc(size));
2849Sjkh}
2859Sjkh
2869Sjkh	void
2879Sjkhffree()
2889Sjkh/* Free all blocks allocated with ftestalloc().  */
2899Sjkh{
2909Sjkh	register struct alloclist *p, *q;
2919Sjkh	for (p = alloced;  p;  p = q) {
2929Sjkh		q = p->nextalloc;
2939Sjkh		tfree(p->alloc);
2949Sjkh		tfree(p);
2959Sjkh	}
29611894Speter	alloced = 0;
2979Sjkh}
2989Sjkh
2999Sjkh	void
3009Sjkhffree1(f)
3019Sjkh	register char const *f;
3029Sjkh/* Free the block f, which was allocated by ftestalloc.  */
3039Sjkh{
3049Sjkh	register struct alloclist *p, **a = &alloced;
3059Sjkh
3069Sjkh	while ((p = *a)->alloc  !=  f)
3079Sjkh		a = &p->nextalloc;
3089Sjkh	*a = p->nextalloc;
3099Sjkh	tfree(p->alloc);
3109Sjkh	tfree(p);
3119Sjkh}
3129Sjkh
3139Sjkh	char *
3149Sjkhstr_save(s)
3159Sjkh	char const *s;
3169Sjkh/* Save s in permanently allocated storage. */
3179Sjkh{
3189Sjkh	return strcpy(tnalloc(char, strlen(s)+1), s);
3199Sjkh}
3209Sjkh
3219Sjkh	char *
3229Sjkhfstr_save(s)
3239Sjkh	char const *s;
3249Sjkh/* Save s in storage that will be deallocated when we're done with this file. */
3259Sjkh{
3269Sjkh	return strcpy(ftnalloc(char, strlen(s)+1), s);
3279Sjkh}
3289Sjkh
3299Sjkh	char *
3309Sjkhcgetenv(name)
3319Sjkh	char const *name;
3329Sjkh/* Like getenv(), but yield a copy; getenv() can overwrite old results. */
3339Sjkh{
3349Sjkh	register char *p;
3359Sjkh
3369Sjkh	return (p=getenv(name)) ? str_save(p) : p;
3379Sjkh}
3389Sjkh
3399Sjkh	char const *
3409Sjkhgetusername(suspicious)
3419Sjkh	int suspicious;
3429Sjkh/* Get the caller's login name.  Trust only getwpuid if SUSPICIOUS.  */
3439Sjkh{
3449Sjkh	static char *name;
3459Sjkh
3469Sjkh	if (!name) {
3479Sjkh		if (
3489Sjkh		    /* Prefer getenv() unless suspicious; it's much faster.  */
3499Sjkh#		    if getlogin_is_secure
3509Sjkh			    (suspicious
35111894Speter			    || (
35211894Speter				    !(name = cgetenv("LOGNAME"))
35311894Speter				&&  !(name = cgetenv("USER"))
35411894Speter			    ))
3559Sjkh			&&  !(name = getlogin())
3569Sjkh#		    else
3579Sjkh			suspicious
35811894Speter			|| (
3599Sjkh				!(name = cgetenv("LOGNAME"))
3609Sjkh			    &&  !(name = cgetenv("USER"))
3619Sjkh			    &&  !(name = getlogin())
36211894Speter			)
3639Sjkh#		    endif
3649Sjkh		) {
3659Sjkh#if has_getuid && has_getpwuid
3669Sjkh			struct passwd const *pw = getpwuid(ruid());
3679Sjkh			if (!pw)
3689Sjkh			    faterror("no password entry for userid %lu",
3699Sjkh				     (unsigned long)ruid()
3709Sjkh			    );
3719Sjkh			name = pw->pw_name;
3729Sjkh#else
3739Sjkh#if has_setuid
3749Sjkh			faterror("setuid not supported");
3759Sjkh#else
37611894Speter			faterror("Who are you?  Please setenv LOGNAME.");
3779Sjkh#endif
3789Sjkh#endif
3799Sjkh		}
3809Sjkh		checksid(name);
3819Sjkh	}
3829Sjkh	return name;
3839Sjkh}
3849Sjkh
3859Sjkh
3869Sjkh
3879Sjkh
3889Sjkh#if has_signal
3899Sjkh
3909Sjkh/*
3919Sjkh *	 Signal handling
3929Sjkh *
3939Sjkh * Standard C places too many restrictions on signal handlers.
3949Sjkh * We obey as many of them as we can.
3959Sjkh * Posix places fewer restrictions, and we are Posix-compatible here.
3969Sjkh */
3979Sjkh
3989Sjkhstatic sig_atomic_t volatile heldsignal, holdlevel;
39911894Speter#ifdef SA_SIGINFO
40011894Speter	static int unsupported_SA_SIGINFO;
40111894Speter	static siginfo_t bufsiginfo;
40211894Speter	static siginfo_t *volatile heldsiginfo;
40311894Speter#endif
4049Sjkh
4059Sjkh
40611894Speter#if has_NFS && has_mmap && large_memory && mmap_signal
40711894Speter    static char const *accessName;
40811894Speter
40911894Speter	  void
41011894Speter    readAccessFilenameBuffer(filename, p)
41111894Speter	char const *filename;
41211894Speter	unsigned char const *p;
41311894Speter    {
41411894Speter	unsigned char volatile t;
41511894Speter	accessName = filename;
41611894Speter	t = *p;
41711894Speter	accessName = 0;
41811894Speter    }
41911894Speter#else
42011894Speter#   define accessName ((char const *) 0)
4219Sjkh#endif
42211894Speter
42311894Speter
42411894Speter#if !has_psignal
42511894Speter
42611894Speter# define psignal my_psignal
42711894Speter	static void my_psignal P((int,char const*));
42811894Speter	static void
42911894Spetermy_psignal(sig, s)
43011894Speter	int sig;
43111894Speter	char const *s;
43211894Speter{
43311894Speter	char const *sname = "Unknown signal";
43411894Speter#	if has_sys_siglist && defined(NSIG)
43511894Speter	    if ((unsigned)sig < NSIG)
43611894Speter		sname = sys_siglist[sig];
43711894Speter#	else
43811894Speter	    switch (sig) {
43911894Speter#	       ifdef SIGHUP
4409Sjkh		case SIGHUP:	sname = "Hangup";  break;
44111894Speter#	       endif
44211894Speter#	       ifdef SIGINT
4439Sjkh		case SIGINT:	sname = "Interrupt";  break;
44411894Speter#	       endif
44511894Speter#	       ifdef SIGPIPE
4469Sjkh		case SIGPIPE:	sname = "Broken pipe";  break;
44711894Speter#	       endif
44811894Speter#	       ifdef SIGQUIT
4499Sjkh		case SIGQUIT:	sname = "Quit";  break;
45011894Speter#	       endif
45111894Speter#	       ifdef SIGTERM
4529Sjkh		case SIGTERM:	sname = "Terminated";  break;
45311894Speter#	       endif
45411894Speter#	       ifdef SIGXCPU
4559Sjkh		case SIGXCPU:	sname = "Cputime limit exceeded";  break;
45611894Speter#	       endif
45711894Speter#	       ifdef SIGXFSZ
4589Sjkh		case SIGXFSZ:	sname = "Filesize limit exceeded";  break;
45911894Speter#	       endif
46011894Speter#	      if has_mmap && large_memory
46111894Speter#	       if defined(SIGBUS) && mmap_signal==SIGBUS
46211894Speter		case SIGBUS:	sname = "Bus error";  break;
46311894Speter#	       endif
46411894Speter#	       if defined(SIGSEGV) && mmap_signal==SIGSEGV
46511894Speter		case SIGSEGV:	sname = "Segmentation fault";  break;
46611894Speter#	       endif
46711894Speter#	      endif
46811894Speter	    }
46911894Speter#	endif
47011894Speter
47111894Speter	/* Avoid calling sprintf etc., in case they're not reentrant.  */
47211894Speter	{
47311894Speter	    char const *p;
47411894Speter	    char buf[BUFSIZ], *b = buf;
47511894Speter	    for (p = s;  *p;  *b++ = *p++)
47611894Speter		continue;
47711894Speter	    *b++ = ':';
47811894Speter	    *b++ = ' ';
47911894Speter	    for (p = sname;  *p;  *b++ = *p++)
48011894Speter		continue;
48111894Speter	    *b++ = '\n';
48211894Speter	    VOID write(STDERR_FILENO, buf, b - buf);
48311894Speter	}
48411894Speter}
4859Sjkh#endif
48611894Speter
48711894Speterstatic signal_type catchsig P((int));
48811894Speter#ifdef SA_SIGINFO
48911894Speter	static signal_type catchsigaction P((int,siginfo_t*,void*));
49011894Speter#endif
49111894Speter
49211894Speter	static signal_type
49311894Spetercatchsig(s)
49411894Speter	int s;
49511894Speter#ifdef SA_SIGINFO
49611894Speter{
49711894Speter	catchsigaction(s, (siginfo_t *)0, (void *)0);
49811894Speter}
49911894Speter	static signal_type
50011894Spetercatchsigaction(s, i, c)
50111894Speter	int s;
50211894Speter	siginfo_t *i;
50311894Speter	void *c;
50411894Speter#endif
50511894Speter{
50611894Speter#   if sig_zaps_handler
50711894Speter	/* If a signal arrives before we reset the handler, we lose. */
50811894Speter	VOID signal(s, SIG_IGN);
50911894Speter#   endif
51011894Speter
51111894Speter#   ifdef SA_SIGINFO
51211894Speter	if (!unsupported_SA_SIGINFO)
51311894Speter	    i = 0;
51411894Speter#   endif
51511894Speter
51611894Speter    if (holdlevel) {
51711894Speter	heldsignal = s;
51811894Speter#	ifdef SA_SIGINFO
51911894Speter	    if (i) {
52011894Speter		bufsiginfo = *i;
52111894Speter		heldsiginfo = &bufsiginfo;
5229Sjkh	    }
52311894Speter#	endif
52411894Speter	return;
52511894Speter    }
52611894Speter
52711894Speter    ignoreints();
52811894Speter    setrid();
52911894Speter    if (!quietflag) {
53011894Speter	/* Avoid calling sprintf etc., in case they're not reentrant.  */
53111894Speter	char const *p;
53211894Speter	char buf[BUFSIZ], *b = buf;
53311894Speter
53411894Speter	if ( !	(
53511894Speter#		if has_mmap && large_memory && mmap_signal
53611894Speter			/* Check whether this signal was planned.  */
53711894Speter			s == mmap_signal && accessName
53811894Speter#		else
53911894Speter			0
54011894Speter#		endif
54111894Speter	)) {
54211894Speter	    char const *nRCS = "\nRCS";
54311894Speter#	    if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal
54411894Speter		if (s == mmap_signal  &&  i  &&  i->si_errno) {
54511894Speter		    errno = i->si_errno;
54611894Speter		    perror(nRCS++);
54711894Speter		}
54811894Speter#	    endif
54911894Speter#	    if defined(SA_SIGINFO) && has_psiginfo
55011894Speter		if (i)
55111894Speter		    psiginfo(i, nRCS);
55211894Speter		else
55311894Speter		    psignal(s, nRCS);
55411894Speter#	    else
55511894Speter		psignal(s, nRCS);
55611894Speter#	    endif
5579Sjkh	}
55811894Speter
55911894Speter	for (p = "RCS: ";  *p;  *b++ = *p++)
56011894Speter	    continue;
56111894Speter#	if has_mmap && large_memory && mmap_signal
56211894Speter	    if (s == mmap_signal) {
56311894Speter		p = accessName;
56411894Speter		if (!p)
56511894Speter		    p = "Was a file changed by some other process?  ";
56611894Speter		else {
56711894Speter		    char const *p1;
56811894Speter		    for (p1 = p;  *p1;  p1++)
56911894Speter			continue;
57011894Speter		    VOID write(STDERR_FILENO, buf, b - buf);
57111894Speter		    VOID write(STDERR_FILENO, p, p1 - p);
57211894Speter		    b = buf;
57311894Speter		    p = ": Permission denied.  ";
57411894Speter		}
57511894Speter		while (*p)
57611894Speter		    *b++ = *p++;
57711894Speter	    }
57811894Speter#	endif
57911894Speter	for (p = "Cleaning up.\n";  *p;  *b++ = *p++)
58011894Speter	    continue;
58111894Speter	VOID write(STDERR_FILENO, buf, b - buf);
58211894Speter    }
58311894Speter    exiterr();
5849Sjkh}
5859Sjkh
5869Sjkh	void
5879Sjkhignoreints()
5889Sjkh{
5899Sjkh	++holdlevel;
5909Sjkh}
5919Sjkh
5929Sjkh	void
5939Sjkhrestoreints()
5949Sjkh{
5959Sjkh	if (!--holdlevel && heldsignal)
59611894Speter#	    ifdef SA_SIGINFO
59711894Speter		VOID catchsigaction(heldsignal, heldsiginfo, (void *)0);
59811894Speter#	    else
5999Sjkh		VOID catchsig(heldsignal);
60011894Speter#	    endif
6019Sjkh}
6029Sjkh
6039Sjkh
60411894Speterstatic void setup_catchsig P((int const*,int));
6059Sjkh
6069Sjkh#if has_sigaction
6079Sjkh
60811894Speter	static void check_sig P((int));
6099Sjkh	static void
6109Sjkh  check_sig(r)
6119Sjkh	int r;
6129Sjkh  {
6139Sjkh	if (r != 0)
61411894Speter		efaterror("signal handling");
6159Sjkh  }
6169Sjkh
6179Sjkh	static void
61811894Speter  setup_catchsig(sig, sigs)
61911894Speter	int const *sig;
62011894Speter	int sigs;
6219Sjkh  {
62211894Speter	register int i, j;
6239Sjkh	struct sigaction act;
6249Sjkh
62511894Speter	for (i=sigs; 0<=--i; ) {
62611894Speter	    check_sig(sigaction(sig[i], (struct sigaction*)0, &act));
6279Sjkh	    if (act.sa_handler != SIG_IGN) {
62811894Speter		act.sa_handler = catchsig;
62911894Speter#		ifdef SA_SIGINFO
63011894Speter		    if (!unsupported_SA_SIGINFO) {
63111894Speter#			if has_sa_sigaction
63211894Speter			    act.sa_sigaction = catchsigaction;
63311894Speter#			else
63411894Speter			    act.sa_handler = catchsigaction;
63511894Speter#			endif
63611894Speter			act.sa_flags |= SA_SIGINFO;
63711894Speter		    }
63811894Speter#		endif
63911894Speter		for (j=sigs; 0<=--j; )
64011894Speter		    check_sig(sigaddset(&act.sa_mask, sig[j]));
64111894Speter		if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) {
64211894Speter#		    if defined(SA_SIGINFO) && defined(ENOTSUP)
64311894Speter			if (errno == ENOTSUP  &&  !unsupported_SA_SIGINFO) {
64411894Speter			    /* Turn off use of SA_SIGINFO and try again.  */
64511894Speter			    unsupported_SA_SIGINFO = 1;
64611894Speter			    i++;
64711894Speter			    continue;
64811894Speter			}
64911894Speter#		    endif
65011894Speter		    check_sig(-1);
65111894Speter		}
6529Sjkh	    }
6539Sjkh	}
6549Sjkh  }
6559Sjkh
6569Sjkh#else
6579Sjkh#if has_sigblock
6589Sjkh
6599Sjkh	static void
66011894Speter  setup_catchsig(sig, sigs)
66111894Speter	int const *sig;
66211894Speter	int sigs;
6639Sjkh  {
6649Sjkh	register int i;
6659Sjkh	int mask;
6669Sjkh
6679Sjkh	mask = 0;
66811894Speter	for (i=sigs; 0<=--i; )
6699Sjkh		mask |= sigmask(sig[i]);
6709Sjkh	mask = sigblock(mask);
67111894Speter	for (i=sigs; 0<=--i; )
6729Sjkh		if (
6739Sjkh		    signal(sig[i], catchsig) == SIG_IGN  &&
6749Sjkh		    signal(sig[i], SIG_IGN) != catchsig
6759Sjkh		)
6769Sjkh			faterror("signal catcher failure");
6779Sjkh	VOID sigsetmask(mask);
6789Sjkh  }
6799Sjkh
6809Sjkh#else
6819Sjkh
6829Sjkh	static void
68311894Speter  setup_catchsig(sig, sigs)
68411894Speter	int const *sig;
68511894Speter	int sigs;
6869Sjkh  {
6879Sjkh	register i;
6889Sjkh
68911894Speter	for (i=sigs; 0<=--i; )
6909Sjkh		if (
6919Sjkh		    signal(sig[i], SIG_IGN) != SIG_IGN  &&
6929Sjkh		    signal(sig[i], catchsig) != SIG_IGN
6939Sjkh		)
6949Sjkh			faterror("signal catcher failure");
6959Sjkh  }
6969Sjkh
6979Sjkh#endif
6989Sjkh#endif
6999Sjkh
70011894Speter
70111894Speterstatic int const regsigs[] = {
70211894Speter# ifdef SIGHUP
70311894Speter	SIGHUP,
70411894Speter# endif
70511894Speter# ifdef SIGINT
70611894Speter	SIGINT,
70711894Speter# endif
70811894Speter# ifdef SIGPIPE
70911894Speter	SIGPIPE,
71011894Speter# endif
71111894Speter# ifdef SIGQUIT
71211894Speter	SIGQUIT,
71311894Speter# endif
71411894Speter# ifdef SIGTERM
71511894Speter	SIGTERM,
71611894Speter# endif
71711894Speter# ifdef SIGXCPU
71811894Speter	SIGXCPU,
71911894Speter# endif
72011894Speter# ifdef SIGXFSZ
72111894Speter	SIGXFSZ,
72211894Speter# endif
72311894Speter};
72411894Speter
7259Sjkh	void
7269Sjkhcatchints()
7279Sjkh{
7289Sjkh	static int catching_ints;
7299Sjkh	if (!catching_ints) {
73011894Speter	    catching_ints = true;
73111894Speter	    setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs)));
7329Sjkh	}
7339Sjkh}
7349Sjkh
73511894Speter#if has_mmap && large_memory && mmap_signal
73611894Speter
73711894Speter    /*
73811894Speter    * If you mmap an NFS file, and someone on another client removes the last
73911894Speter    * link to that file, and you later reference an uncached part of that file,
74011894Speter    * you'll get a SIGBUS or SIGSEGV (depending on the operating system).
74111894Speter    * Catch the signal and report the problem to the user.
74211894Speter    * Unfortunately, there's no portable way to differentiate between this
74311894Speter    * problem and actual bugs in the program.
74411894Speter    * This NFS problem is rare, thank goodness.
74511894Speter    *
74611894Speter    * This can also occur if someone truncates the file, even without NFS.
74711894Speter    */
74811894Speter
74911894Speter    static int const mmapsigs[] = { mmap_signal };
75011894Speter
75111894Speter	    void
75211894Speter    catchmmapints()
75311894Speter    {
75411894Speter	static int catching_mmap_ints;
75511894Speter	if (!catching_mmap_ints) {
75611894Speter	    catching_mmap_ints = true;
75711894Speter	    setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs)));
75811894Speter	}
75911894Speter    }
76011894Speter#endif
76111894Speter
7629Sjkh#endif /* has_signal */
7639Sjkh
7649Sjkh
7659Sjkh	void
7669Sjkhfastcopy(inf,outf)
7679Sjkh	register RILE *inf;
7689Sjkh	FILE *outf;
7699Sjkh/* Function: copies the remainder of file inf to outf.
7709Sjkh */
7719Sjkh{
7729Sjkh#if large_memory
77311894Speter#	if maps_memory
7749Sjkh	    awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
7759Sjkh	    inf->ptr = inf->lim;
7769Sjkh#	else
7779Sjkh	    for (;;) {
7789Sjkh		awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf);
7799Sjkh		inf->ptr = inf->readlim;
7809Sjkh		if (inf->ptr == inf->lim)
7819Sjkh		    break;
7829Sjkh		VOID Igetmore(inf);
7839Sjkh	    }
7849Sjkh#	endif
7859Sjkh#else
7869Sjkh	char buf[BUFSIZ*8];
7879Sjkh	register fread_type rcount;
7889Sjkh
7899Sjkh        /*now read the rest of the file in blocks*/
7909Sjkh	while (!feof(inf)) {
7919Sjkh		if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) {
7929Sjkh			testIerror(inf);
7939Sjkh			return;
7949Sjkh		}
7959Sjkh		awrite(buf, (size_t)rcount, outf);
7969Sjkh        }
7979Sjkh#endif
7989Sjkh}
7999Sjkh
8009Sjkh#ifndef SSIZE_MAX
8019Sjkh /* This does not work in #ifs, but it's good enough for us.  */
8029Sjkh /* Underestimating SSIZE_MAX may slow us down, but it won't break us.  */
8039Sjkh#	define SSIZE_MAX ((unsigned)-1 >> 1)
8049Sjkh#endif
8059Sjkh
8069Sjkh	void
8079Sjkhawrite(buf, chars, f)
8089Sjkh	char const *buf;
8099Sjkh	size_t chars;
8109Sjkh	FILE *f;
8119Sjkh{
8129Sjkh	/* Posix 1003.1-1990 ssize_t hack */
8139Sjkh	while (SSIZE_MAX < chars) {
8149Sjkh		if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f)  !=  SSIZE_MAX)
8159Sjkh			Oerror();
8169Sjkh		buf += SSIZE_MAX;
8179Sjkh		chars -= SSIZE_MAX;
8189Sjkh	}
8199Sjkh
8209Sjkh	if (Fwrite(buf, sizeof(*buf), chars, f)  !=  chars)
8219Sjkh		Oerror();
8229Sjkh}
8239Sjkh
82411894Speter/* dup a file descriptor; the result must not be stdin, stdout, or stderr.  */
82511894Speter	static int dupSafer P((int));
82611894Speter	static int
82711894SpeterdupSafer(fd)
82811894Speter	int fd;
82911894Speter{
83011894Speter#	ifdef F_DUPFD
83111894Speter	    return fcntl(fd, F_DUPFD, STDERR_FILENO + 1);
83211894Speter#	else
83311894Speter	    int e, f, i, used = 0;
83411894Speter	    while (STDIN_FILENO <= (f = dup(fd))  &&  f <= STDERR_FILENO)
83511894Speter		    used |= 1<<f;
83611894Speter	    e = errno;
83711894Speter	    for (i = STDIN_FILENO;  i <= STDERR_FILENO;  i++)
83811894Speter		    if (used & (1<<i))
83911894Speter			    VOID close(i);
84011894Speter	    errno = e;
84111894Speter	    return f;
84211894Speter#	endif
84311894Speter}
8449Sjkh
84511894Speter/* Renumber a file descriptor so that it's not stdin, stdout, or stderr.  */
84611894Speter	int
84711894SpeterfdSafer(fd)
84811894Speter	int fd;
84911894Speter{
85011894Speter	if (STDIN_FILENO <= fd  &&  fd <= STDERR_FILENO) {
85111894Speter		int f = dupSafer(fd);
85211894Speter		int e = errno;
85311894Speter		VOID close(fd);
85411894Speter		errno = e;
85511894Speter		fd = f;
85611894Speter	}
85711894Speter	return fd;
85811894Speter}
8599Sjkh
86011894Speter/* Like fopen, except the result is never stdin, stdout, or stderr.  */
86111894Speter	FILE *
86211894SpeterfopenSafer(filename, type)
86311894Speter	char const *filename;
86411894Speter	char const *type;
86511894Speter{
86611894Speter	FILE *stream = fopen(filename, type);
86711894Speter	if (stream) {
86811894Speter		int fd = fileno(stream);
86911894Speter		if (STDIN_FILENO <= fd  &&  fd <= STDERR_FILENO) {
87011894Speter			int f = dupSafer(fd);
87111894Speter			if (f < 0) {
87211894Speter				int e = errno;
87311894Speter				VOID fclose(stream);
87411894Speter				errno = e;
87511894Speter				return 0;
87611894Speter			}
87711894Speter			if (fclose(stream) != 0) {
87811894Speter				int e = errno;
87911894Speter				VOID close(f);
88011894Speter				errno = e;
88111894Speter				return 0;
88211894Speter			}
88311894Speter			stream = fdopen(f, type);
88411894Speter		}
88511894Speter	}
88611894Speter	return stream;
88711894Speter}
8889Sjkh
8899Sjkh
89011894Speter#ifdef F_DUPFD
89111894Speter#	undef dup
89211894Speter#	define dup(fd) fcntl(fd, F_DUPFD, 0)
89311894Speter#endif
89411894Speter
89511894Speter
89611894Speter#if has_fork || has_spawn
89711894Speter
89811894Speter	static int movefd P((int,int));
8999Sjkh	static int
9009Sjkhmovefd(old, new)
9019Sjkh	int old, new;
9029Sjkh{
9039Sjkh	if (old < 0  ||  old == new)
9049Sjkh		return old;
9059Sjkh#	ifdef F_DUPFD
9069Sjkh		new = fcntl(old, F_DUPFD, new);
9079Sjkh#	else
9089Sjkh		new = dup2(old, new);
9099Sjkh#	endif
9109Sjkh	return close(old)==0 ? new : -1;
9119Sjkh}
9129Sjkh
91311894Speter	static int fdreopen P((int,char const*,int));
9149Sjkh	static int
9159Sjkhfdreopen(fd, file, flags)
9169Sjkh	int fd;
9179Sjkh	char const *file;
9189Sjkh	int flags;
9199Sjkh{
9209Sjkh	int newfd;
9219Sjkh	VOID close(fd);
9229Sjkh	newfd =
9239Sjkh#if !open_can_creat
9249Sjkh		flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
9259Sjkh#endif
9269Sjkh		open(file, flags, S_IRUSR|S_IWUSR);
9279Sjkh	return movefd(newfd, fd);
9289Sjkh}
9299Sjkh
93011894Speter#if has_spawn
93111894Speter	static void redirect P((int,int));
9329Sjkh	static void
9339Sjkhredirect(old, new)
9349Sjkh	int old, new;
93511894Speter/*
93611894Speter* Move file descriptor OLD to NEW.
93711894Speter* If OLD is -1, do nothing.
93811894Speter* If OLD is -2, just close NEW.
93911894Speter*/
9409Sjkh{
94111894Speter	if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0))
9429Sjkh		efaterror("spawn I/O redirection");
9439Sjkh}
9449Sjkh#endif
9459Sjkh
9469Sjkh
94711894Speter#else /* !has_fork && !has_spawn */
9489Sjkh
94911894Speter	static void bufargcat P((struct buf*,int,char const*));
9509Sjkh	static void
9519Sjkhbufargcat(b, c, s)
9529Sjkh	register struct buf *b;
9539Sjkh	int c;
9549Sjkh	register char const *s;
9559Sjkh/* Append to B a copy of C, plus a quoted copy of S.  */
9569Sjkh{
9579Sjkh	register char *p;
9589Sjkh	register char const *t;
9599Sjkh	size_t bl, sl;
9609Sjkh
9619Sjkh	for (t=s, sl=0;  *t;  )
9629Sjkh		sl  +=  3*(*t++=='\'') + 1;
9639Sjkh	bl = strlen(b->string);
9649Sjkh	bufrealloc(b, bl + sl + 4);
9659Sjkh	p = b->string + bl;
9669Sjkh	*p++ = c;
9679Sjkh	*p++ = '\'';
9689Sjkh	while (*s) {
9699Sjkh		if (*s == '\'') {
9709Sjkh			*p++ = '\'';
9719Sjkh			*p++ = '\\';
9729Sjkh			*p++ = '\'';
9739Sjkh		}
9749Sjkh		*p++ = *s++;
9759Sjkh	}
9769Sjkh	*p++ = '\'';
9779Sjkh	*p = 0;
9789Sjkh}
97911894Speter
9809Sjkh#endif
9819Sjkh
98211894Speter#if !has_spawn && has_fork
9839Sjkh/*
98411894Speter* Output the string S to stderr, without touching any I/O buffers.
98511894Speter* This is useful if you are a child process, whose buffers are usually wrong.
98611894Speter* Exit immediately if the write does not completely succeed.
9879Sjkh*/
98811894Speterstatic void write_stderr P((char const *));
98911894Speter	static void
99011894Speterwrite_stderr(s)
99111894Speter	char const *s;
99211894Speter{
99311894Speter	size_t slen = strlen(s);
99411894Speter	if (write(STDERR_FILENO, s, slen) != slen)
99511894Speter		_exit(EXIT_TROUBLE);
99611894Speter}
99711894Speter#endif
99811894Speter
99911894Speter/*
100011894Speter* Run a command.
100111894Speter* infd, if not -1, is the input file descriptor.
100211894Speter* outname, if nonzero, is the name of the output file.
100311894Speter* args[1..] form the command to be run; args[0] might be modified.
100411894Speter*/
10059Sjkh	int
100611894Speterrunv(infd, outname, args)
100711894Speter	int infd;
100811894Speter	char const *outname, **args;
10099Sjkh{
10109Sjkh	int wstatus;
10119Sjkh
101211894Speter#if bad_wait_if_SIGCHLD_ignored
101311894Speter	static int fixed_SIGCHLD;
101411894Speter	if (!fixed_SIGCHLD) {
101511894Speter	    fixed_SIGCHLD = true;
101611894Speter#	    ifndef SIGCHLD
101711894Speter#	    define SIGCHLD SIGCLD
101811894Speter#	    endif
101911894Speter	    VOID signal(SIGCHLD, SIG_DFL);
102011894Speter	}
102111894Speter#endif
102211894Speter
10239Sjkh	oflush();
10249Sjkh	eflush();
10259Sjkh    {
10269Sjkh#if has_spawn
10279Sjkh	int in, out;
102811894Speter	char const *file;
102911894Speter
103011894Speter	in = -1;
103111894Speter	if (infd != -1  &&  infd != STDIN_FILENO) {
103211894Speter	    if ((in = dup(STDIN_FILENO)) < 0) {
103311894Speter		if (errno != EBADF)
103411894Speter		    efaterror("spawn input setup");
103511894Speter		in = -2;
103611894Speter	    } else {
103711894Speter#		ifdef F_DUPFD
103811894Speter		    if (close(STDIN_FILENO) != 0)
103911894Speter			efaterror("spawn input close");
104011894Speter#		endif
104111894Speter	    }
104211894Speter	    if (
104311894Speter#		ifdef F_DUPFD
104411894Speter		    fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO
104511894Speter#		else
104611894Speter		    dup2(infd, STDIN_FILENO) != STDIN_FILENO
104711894Speter#		endif
104811894Speter	    )
104911894Speter		efaterror("spawn input redirection");
10509Sjkh	}
105111894Speter
105211894Speter	out = -1;
105311894Speter	if (outname) {
105411894Speter	    if ((out = dup(STDOUT_FILENO)) < 0) {
105511894Speter		if (errno != EBADF)
105611894Speter		    efaterror("spawn output setup");
105711894Speter		out = -2;
105811894Speter	    }
105911894Speter	    if (fdreopen(
106011894Speter		STDOUT_FILENO, outname,
106111894Speter		O_CREAT | O_TRUNC | O_WRONLY
106211894Speter	    ) < 0)
106311894Speter		efaterror(outname);
106411894Speter	}
106511894Speter
106611894Speter	wstatus = spawn_RCS(0, args[1], (char**)(args + 1));
106711894Speter#	ifdef RCS_SHELL
106811894Speter	    if (wstatus == -1  &&  errno == ENOEXEC) {
106911894Speter		args[0] = RCS_SHELL;
107011894Speter		wstatus = spawnv(0, args[0], (char**)args);
107111894Speter	    }
107211894Speter#	endif
10739Sjkh	redirect(in, STDIN_FILENO);
10749Sjkh	redirect(out, STDOUT_FILENO);
10759Sjkh#else
10769Sjkh#if has_fork
10779Sjkh	pid_t pid;
10789Sjkh	if (!(pid = vfork())) {
107911894Speter		char const *notfound;
108011894Speter		if (infd != -1  &&  infd != STDIN_FILENO  &&  (
108111894Speter#		    ifdef F_DUPFD
108211894Speter			(VOID close(STDIN_FILENO),
108311894Speter			fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO)
108411894Speter#		    else
108511894Speter			dup2(infd, STDIN_FILENO) != STDIN_FILENO
108611894Speter#		    endif
108711894Speter		)) {
108811894Speter		    /* Avoid perror since it may misuse buffers.  */
108911894Speter		    write_stderr(args[1]);
109011894Speter		    write_stderr(": I/O redirection failed\n");
109111894Speter		    _exit(EXIT_TROUBLE);
10929Sjkh		}
109311894Speter
109411894Speter		if (outname)
109511894Speter		    if (fdreopen(
109611894Speter			STDOUT_FILENO, outname,
109711894Speter			O_CREAT | O_TRUNC | O_WRONLY
109811894Speter		    ) < 0) {
109911894Speter			/* Avoid perror since it may misuse buffers.  */
110011894Speter			write_stderr(args[1]);
110111894Speter			write_stderr(": ");
110211894Speter			write_stderr(outname);
110311894Speter			write_stderr(": cannot create\n");
110411894Speter			_exit(EXIT_TROUBLE);
110511894Speter		    }
110611894Speter		VOID exec_RCS(args[1], (char**)(args + 1));
110711894Speter		notfound = args[1];
110811894Speter#		ifdef RCS_SHELL
110911894Speter		    if (errno == ENOEXEC) {
111011894Speter			args[0] = notfound = RCS_SHELL;
111111894Speter			VOID execv(args[0], (char**)args);
111211894Speter		    }
111311894Speter#		endif
111411894Speter
111511894Speter		/* Avoid perror since it may misuse buffers.  */
111611894Speter		write_stderr(notfound);
111711894Speter		write_stderr(": not found\n");
11189Sjkh		_exit(EXIT_TROUBLE);
11199Sjkh	}
11209Sjkh	if (pid < 0)
11219Sjkh		efaterror("fork");
11229Sjkh#	if has_waitpid
11239Sjkh		if (waitpid(pid, &wstatus, 0) < 0)
11249Sjkh			efaterror("waitpid");
11259Sjkh#	else
112611894Speter		{
112711894Speter			pid_t w;
112811894Speter			do {
112911894Speter				if ((w = wait(&wstatus)) < 0)
113011894Speter					efaterror("wait");
113111894Speter			} while (w != pid);
113211894Speter		}
11339Sjkh#	endif
11349Sjkh#else
11359Sjkh	static struct buf b;
113611894Speter	char const *p;
11379Sjkh
11389Sjkh	/* Use system().  On many hosts system() discards signals.  Yuck!  */
113911894Speter	p = args + 1;
11409Sjkh	bufscpy(&b, *p);
11419Sjkh	while (*++p)
11429Sjkh		bufargcat(&b, ' ', *p);
114311894Speter	if (infd != -1  &&  infd != STDIN_FILENO) {
114411894Speter		char redirection[32];
114511894Speter		VOID sprintf(redirection, "<&%d", infd);
114611894Speter		bufscat(&b, redirection);
114711894Speter	}
114811894Speter	if (outname)
114911894Speter		bufargcat(&b, '>', outname);
11509Sjkh	wstatus = system(b.string);
11519Sjkh#endif
11529Sjkh#endif
11539Sjkh    }
115411894Speter	if (!WIFEXITED(wstatus)) {
115511894Speter		if (WIFSIGNALED(wstatus)) {
115611894Speter			psignal(WTERMSIG(wstatus), args[1]);
115711894Speter			fatcleanup(1);
115811894Speter		}
115911894Speter		faterror("%s failed for unknown reason", args[1]);
116011894Speter	}
11619Sjkh	return WEXITSTATUS(wstatus);
11629Sjkh}
11639Sjkh
11649Sjkh#define CARGSMAX 20
11659Sjkh/*
11669Sjkh* Run a command.
116711894Speter* infd, if not -1, is the input file descriptor.
116811894Speter* outname, if nonzero, is the name of the output file.
116911894Speter* The remaining arguments specify the command and its arguments.
11709Sjkh*/
11719Sjkh	int
11729Sjkh#if has_prototypes
117311894Speterrun(int infd, char const *outname, ...)
11749Sjkh#else
11759Sjkh	/*VARARGS2*/
117611894Speterrun(infd, outname, va_alist)
117711894Speter	int infd;
117811894Speter	char const *outname;
11799Sjkh	va_dcl
11809Sjkh#endif
11819Sjkh{
11829Sjkh	va_list ap;
11839Sjkh	char const *rgargs[CARGSMAX];
118411894Speter	register int i;
118511894Speter	vararg_start(ap, outname);
118611894Speter	for (i = 1;  (rgargs[i++] = va_arg(ap, char const*));  )
11879Sjkh		if (CARGSMAX <= i)
11889Sjkh			faterror("too many command arguments");
11899Sjkh	va_end(ap);
119011894Speter	return runv(infd, outname, rgargs);
11919Sjkh}
11929Sjkh
11939Sjkh
11949Sjkhint RCSversion;
11959Sjkh
11969Sjkh	void
11979SjkhsetRCSversion(str)
11989Sjkh	char const *str;
11999Sjkh{
12009Sjkh	static int oldversion;
12019Sjkh
12029Sjkh	register char const *s = str + 2;
12039Sjkh
120411894Speter	if (*s) {
120511894Speter		int v = VERSION_DEFAULT;
12069Sjkh
120711894Speter		if (oldversion)
120811894Speter			redefined('V');
120911894Speter		oldversion = true;
12109Sjkh		v = 0;
12119Sjkh		while (isdigit(*s))
12129Sjkh			v  =  10*v + *s++ - '0';
12139Sjkh		if (*s)
121411894Speter			error("%s isn't a number", str);
121511894Speter		else if (v < VERSION_min  ||  VERSION_max < v)
121611894Speter			error("%s out of range %d..%d",
121711894Speter				str, VERSION_min, VERSION_max
121811894Speter			);
121911894Speter
122011894Speter		RCSversion = VERSION(v);
122111894Speter	} else {
122211894Speter		printf("RCS version %s\n", RCS_version_string);
122311894Speter		exit(0);
12249Sjkh	}
12259Sjkh}
12269Sjkh
12279Sjkh	int
12289SjkhgetRCSINIT(argc, argv, newargv)
12299Sjkh	int argc;
12309Sjkh	char **argv, ***newargv;
12319Sjkh{
12329Sjkh	register char *p, *q, **pp;
123325699Speter	char const *ev;
123411894Speter	size_t n;
12359Sjkh
123625699Speter	if ((ev = cgetenv("RCSLOCALID")))
123725699Speter		setRCSLocalId(ev);
123825699Speter
123925699Speter	if ((ev = cgetenv("RCSINCEXC")))
124025699Speter		setIncExc(ev);
124125699Speter
12429Sjkh	if (!(q = cgetenv("RCSINIT")))
12439Sjkh		*newargv = argv;
12449Sjkh	else {
12459Sjkh		n = argc + 2;
12469Sjkh		/*
12479Sjkh		 * Count spaces in RCSINIT to allocate a new arg vector.
12489Sjkh		 * This is an upper bound, but it's OK even if too large.
12499Sjkh		 */
12509Sjkh		for (p = q;  ;  ) {
12519Sjkh			switch (*p++) {
12529Sjkh			    default:
12539Sjkh				continue;
12549Sjkh
12559Sjkh			    case ' ':
12569Sjkh			    case '\b': case '\f': case '\n':
12579Sjkh			    case '\r': case '\t': case '\v':
12589Sjkh				n++;
12599Sjkh				continue;
12609Sjkh
12619Sjkh			    case '\0':
12629Sjkh				break;
12639Sjkh			}
12649Sjkh			break;
12659Sjkh		}
12669Sjkh		*newargv = pp = tnalloc(char*, n);
12679Sjkh		*pp++ = *argv++; /* copy program name */
12689Sjkh		for (p = q;  ;  ) {
12699Sjkh			for (;;) {
12709Sjkh				switch (*q) {
12719Sjkh				    case '\0':
12729Sjkh					goto copyrest;
12739Sjkh
12749Sjkh				    case ' ':
12759Sjkh				    case '\b': case '\f': case '\n':
12769Sjkh				    case '\r': case '\t': case '\v':
12779Sjkh					q++;
12789Sjkh					continue;
12799Sjkh				}
12809Sjkh				break;
12819Sjkh			}
12829Sjkh			*pp++ = p;
12839Sjkh			++argc;
12849Sjkh			for (;;) {
12859Sjkh				switch ((*p++ = *q++)) {
12869Sjkh				    case '\0':
12879Sjkh					goto copyrest;
12889Sjkh
12899Sjkh				    case '\\':
12909Sjkh					if (!*q)
12919Sjkh						goto copyrest;
12929Sjkh					p[-1] = *q++;
12939Sjkh					continue;
12949Sjkh
12959Sjkh				    default:
12969Sjkh					continue;
12979Sjkh
12989Sjkh				    case ' ':
12999Sjkh				    case '\b': case '\f': case '\n':
13009Sjkh				    case '\r': case '\t': case '\v':
13019Sjkh					break;
13029Sjkh				}
13039Sjkh				break;
13049Sjkh			}
13059Sjkh			p[-1] = '\0';
13069Sjkh		}
13079Sjkh	    copyrest:
13089Sjkh		while ((*pp++ = *argv++))
130911894Speter			continue;
13109Sjkh	}
13119Sjkh	return argc;
13129Sjkh}
13139Sjkh
13149Sjkh
13159Sjkh#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
13169Sjkh
13179Sjkh#if has_getuid
13189Sjkh	uid_t ruid() { cacheid(getuid()); }
13199Sjkh#endif
13209Sjkh#if has_setuid
13219Sjkh	uid_t euid() { cacheid(geteuid()); }
13229Sjkh#endif
13239Sjkh
13249Sjkh
13259Sjkh#if has_setuid
13269Sjkh
13279Sjkh/*
13289Sjkh * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
13299Sjkh * because it lets us switch back and forth between arbitrary users.
13309Sjkh * If seteuid() doesn't work, we fall back on setuid(),
13319Sjkh * which works if saved setuid is supported,
13329Sjkh * unless the real or effective user is root.
13339Sjkh * This area is such a mess that we always check switches at runtime.
13349Sjkh */
13359Sjkh
13369Sjkh	static void
133711894Speter#if has_prototypes
133811894Speterset_uid_to(uid_t u)
133911894Speter#else
134011894Speter set_uid_to(u) uid_t u;
134111894Speter#endif
13429Sjkh/* Become user u.  */
13439Sjkh{
13449Sjkh	static int looping;
13459Sjkh
13469Sjkh	if (euid() == ruid())
13479Sjkh		return;
13489Sjkh#if (has_fork||has_spawn) && DIFF_ABSOLUTE
134911894Speter#	if has_setreuid
135011894Speter		if (setreuid(u==euid() ? ruid() : euid(), u) != 0)
135111894Speter			efaterror("setuid");
135211894Speter#	else
135311894Speter		if (seteuid(u) != 0)
135411894Speter			efaterror("setuid");
135511894Speter#	endif
13569Sjkh#endif
13579Sjkh	if (geteuid() != u) {
13589Sjkh		if (looping)
13599Sjkh			return;
13609Sjkh		looping = true;
13619Sjkh		faterror("root setuid not supported" + (u?5:0));
13629Sjkh	}
13639Sjkh}
13649Sjkh
13659Sjkhstatic int stick_with_euid;
13669Sjkh
13679Sjkh	void
13689Sjkh/* Ignore all calls to seteid() and setrid().  */
13699Sjkhnosetid()
13709Sjkh{
13719Sjkh	stick_with_euid = true;
13729Sjkh}
13739Sjkh
13749Sjkh	void
13759Sjkhseteid()
13769Sjkh/* Become effective user.  */
13779Sjkh{
13789Sjkh	if (!stick_with_euid)
13799Sjkh		set_uid_to(euid());
13809Sjkh}
13819Sjkh
13829Sjkh	void
13839Sjkhsetrid()
13849Sjkh/* Become real user.  */
13859Sjkh{
13869Sjkh	if (!stick_with_euid)
13879Sjkh		set_uid_to(ruid());
13889Sjkh}
13899Sjkh#endif
139011894Speter
139111894Speter	time_t
139211894Speternow()
139311894Speter{
139411894Speter	static time_t t;
139511894Speter	if (!t  &&  time(&t) == -1)
139611894Speter		efaterror("time");
139711894Speter	return t;
139811894Speter}
1399