rcsutil.c revision 21673
119304Speter/* RCS utility functions */
219304Speter
319304Speter/* Copyright 1982, 1988, 1989 Walter Tichy
419304Speter   Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
519304Speter   Distributed under license by the Free Software Foundation, Inc.
619304Speter
719304SpeterThis file is part of RCS.
819304Speter
919304SpeterRCS is free software; you can redistribute it and/or modify
1019304Speterit under the terms of the GNU General Public License as published by
1119304Speterthe Free Software Foundation; either version 2, or (at your option)
1219304Speterany later version.
13254225Speter
1419304SpeterRCS is distributed in the hope that it will be useful,
1519304Speterbut WITHOUT ANY WARRANTY; without even the implied warranty of
1619304SpeterMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1719304SpeterGNU General Public License for more details.
1819304Speter
1919304SpeterYou should have received a copy of the GNU General Public License
2019304Speteralong with RCS; see the file COPYING.
2119304SpeterIf not, write to the Free Software Foundation,
2219304Speter59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2319304Speter
24254225SpeterReport problems and direct all questions to:
2519304Speter
2619304Speter    rcs-bugs@cs.purdue.edu
2719304Speter
2819304Speter*/
2919304Speter
3019304Speter
3119304Speter
3219304Speter
3319304Speter/*
3419304Speter * Revision 5.20  1995/06/16 06:19:24  eggert
3519304Speter * (catchsig): Remove `return'.
3619304Speter * Update FSF address.
3719304Speter *
3819304Speter * Revision 5.19  1995/06/02 18:19:00  eggert
3919304Speter * (catchsigaction): New name for `catchsig', for sa_sigaction signature.
4019304Speter * Use nRCS even if !has_psiginfo, to remove unused variable warning.
4119304Speter * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction.
4219304Speter * Use ENOTSUP only if defined.
4319304Speter *
4419304Speter * Revision 5.18  1995/06/01 16:23:43  eggert
4519304Speter * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo,
4619304Speter * to determine whether to use SA_SIGINFO feature,
4719304Speter * but also check at runtime whether the feature works.
4819304Speter * (catchsig): If an mmap_signal occurs, report the affected file name.
4919304Speter * (unsupported_SA_SIGINFO, accessName): New variables.
5019304Speter * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler.
5119304Speter * If SA_SIGINFO fails, fall back on sa_handler method.
5219304Speter *
5319304Speter * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions.
5419304Speter * (concatenate): Remove.
5519304Speter *
5619304Speter * (runv): Work around bad_wait_if_SIGCHLD_ignored bug.
5719304Speter * Remove reference to OPEN_O_WORK.
5819304Speter *
5919304Speter * Revision 5.17  1994/03/20 04:52:58  eggert
6019304Speter * Specify subprocess input via file descriptor, not file name.
6119304Speter * Avoid messing with I/O buffers in the child process.
6219304Speter * Define dup in terms of F_DUPFD if it exists.
6319304Speter * Move setmtime to rcsedit.c.  Remove lint.
6419304Speter *
6519304Speter * Revision 5.16  1993/11/09 17:40:15  eggert
6619304Speter * -V now prints version on stdout and exits.
6719304Speter *
6819304Speter * Revision 5.15  1993/11/03 17:42:27  eggert
6919304Speter * Use psiginfo and setreuid if available.  Move date2str to maketime.c.
7019304Speter *
71254225Speter * Revision 5.14  1992/07/28  16:12:44  eggert
72254225Speter * Add -V.  has_sigaction overrides sig_zaps_handler.  Fix -M bug.
7319304Speter * Add mmap_signal, which minimizes signal handling for non-mmap hosts.
7419304Speter *
7519304Speter * Revision 5.13  1992/02/17  23:02:28  eggert
7619304Speter * Work around NFS mmap SIGBUS problem.  Add -T support.
7719304Speter *
7819304Speter * Revision 5.12  1992/01/24  18:44:19  eggert
7919304Speter * Work around NFS mmap bug that leads to SIGBUS core dumps.  lint -> RCS_lint
80254225Speter *
81254225Speter * Revision 5.11  1992/01/06  02:42:34  eggert
82254225Speter * O_BINARY -> OPEN_O_WORK
83254225Speter * while (E) ; -> while (E) continue;
84254225Speter *
85254225Speter * Revision 5.10  1991/10/07  17:32:46  eggert
86254225Speter * Support piece tables even if !has_mmap.
87254225Speter *
88254225Speter * Revision 5.9  1991/08/19  03:13:55  eggert
8919304Speter * Add spawn() support.  Explicate assumptions about getting invoker's name.
9019304Speter * Standardize user-visible dates.  Tune.
9119304Speter *
9219304Speter * Revision 5.8  1991/04/21  11:58:30  eggert
9319304Speter * Plug setuid security hole.
9419304Speter *
9519304Speter * Revision 5.6  1991/02/26  17:48:39  eggert
96254225Speter * Fix setuid bug.  Use fread, fwrite more portably.
97254225Speter * Support waitpid.  Don't assume -1 is acceptable to W* macros.
98254225Speter * strsave -> str_save (DG/UX name clash)
9919304Speter *
10019304Speter * Revision 5.5  1990/12/04  05:18:49  eggert
10119304Speter * Don't output a blank line after a signal diagnostic.
10219304Speter * Use -I for prompts and -q for diagnostics.
10319304Speter *
10419304Speter * Revision 5.4  1990/11/01  05:03:53  eggert
10519304Speter * Remove unneeded setid check.  Add awrite(), fremember().
10619304Speter *
10719304Speter * Revision 5.3  1990/10/06  00:16:45  eggert
10819304Speter * Don't fread F if feof(F).
10919304Speter *
11019304Speter * Revision 5.2  1990/09/04  08:02:31  eggert
11119304Speter * Store fread()'s result in an fread_type object.
11219304Speter *
11319304Speter * Revision 5.1  1990/08/29  07:14:07  eggert
11419304Speter * Declare getpwuid() more carefully.
11519304Speter *
11619304Speter * Revision 5.0  1990/08/22  08:13:46  eggert
11719304Speter * Add setuid support.  Permit multiple locks per user.
11819304Speter * Remove compile-time limits; use malloc instead.
11919304Speter * Switch to GMT.  Permit dates past 1999/12/31.
12019304Speter * Add -V.  Remove snooping.  Ansify and Posixate.
12119304Speter * Tune.  Some USG hosts define NSIG but not sys_siglist.
12219304Speter * Don't run /bin/sh if it's hopeless.
12319304Speter * Don't leave garbage behind if the output is an empty pipe.
12419304Speter * Clean up after SIGXCPU or SIGXFSZ.  Print name of signal that caused cleanup.
12519304Speter *
12619304Speter * Revision 4.6  89/05/01  15:13:40  narten
12719304Speter * changed copyright header to reflect current distribution rules
12819304Speter *
12919304Speter * Revision 4.5  88/11/08  16:01:02  narten
13019304Speter * corrected use of varargs routines
13119304Speter *
132254225Speter * Revision 4.4  88/08/09  19:13:24  eggert
133254225Speter * Check for memory exhaustion.
134254225Speter * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
13519304Speter * Use execv(), not system(); yield exit status like diff(1)'s.
13619304Speter *
13719304Speter * Revision 4.3  87/10/18  10:40:22  narten
13819304Speter * Updating version numbers. Changes relative to 1.1 actually
13919304Speter * relative to 4.1
14019304Speter *
14119304Speter * Revision 1.3  87/09/24  14:01:01  narten
14219304Speter * Sources now pass through lint (if you ignore printf/sprintf/fprintf
14319304Speter * warnings)
14419304Speter *
14519304Speter * Revision 1.2  87/03/27  14:22:43  jenkins
14619304Speter * Port to suns
14719304Speter *
14819304Speter * Revision 4.1  83/05/10  15:53:13  wft
14919304Speter * Added getcaller() and findlock().
15019304Speter * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
15119304Speter * (needed for background jobs in older shells). Added restoreints().
15219304Speter * Removed printing of full RCS path from logcommand().
15319304Speter *
15419304Speter * Revision 3.8  83/02/15  15:41:49  wft
15519304Speter * Added routine fastcopy() to copy remainder of a file in blocks.
15619304Speter *
15719304Speter * Revision 3.7  82/12/24  15:25:19  wft
15819304Speter * added catchints(), ignoreints() for catching and ingnoring interrupts;
15919304Speter * fixed catchsig().
16019304Speter *
16119304Speter * Revision 3.6  82/12/08  21:52:05  wft
162254225Speter * Using DATEFORM to format dates.
16319304Speter *
16419304Speter * Revision 3.5  82/12/04  18:20:49  wft
16519304Speter * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
16619304Speter * lockedby-field.
16719304Speter *
16819304Speter * Revision 3.4  82/12/03  17:17:43  wft
16919304Speter * Added check to addlock() ensuring only one lock per person.
17019304Speter * Addlock also returns a pointer to the lock created. Deleted fancydate().
17119304Speter *
17219304Speter * Revision 3.3  82/11/27  12:24:37  wft
17319304Speter * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
17419304Speter * Introduced macro SNOOP so that snoop can be placed in directory other than
17519304Speter * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
17619304Speter *
17719304Speter * Revision 3.2  82/10/18  21:15:11  wft
17819304Speter * added function getfullRCSname().
17919304Speter *
18019304Speter * Revision 3.1  82/10/13  16:17:37  wft
18119304Speter * Cleanup message is now suppressed in quiet mode.
18219304Speter */
18319304Speter
18419304Speter
18519304Speter
18619304Speter
18719304Speter#include "rcsbase.h"
18819304Speter
189254225SpeterlibId(utilId, "$FreeBSD: head/gnu/usr.bin/rcs/lib/rcsutil.c 21673 1997-01-14 07:20:47Z jkh $")
190254225Speter
191254225Speter#if !has_memcmp
19219304Speter	int
19319304Spetermemcmp(s1, s2, n)
19419304Speter	void const *s1, *s2;
19519304Speter	size_t n;
19619304Speter{
197254225Speter	register unsigned char const
198254225Speter		*p1 = (unsigned char const*)s1,
19919304Speter		*p2 = (unsigned char const*)s2;
20019304Speter	register size_t i = n;
20119304Speter	register int r = 0;
20219304Speter	while (i--  &&  !(r = (*p1++ - *p2++)))
20319304Speter		;
20419304Speter	return r;
20519304Speter}
20619304Speter#endif
20719304Speter
20819304Speter#if !has_memcpy
20919304Speter	void *
21019304Spetermemcpy(s1, s2, n)
21119304Speter	void *s1;
21219304Speter	void const *s2;
21319304Speter	size_t n;
21419304Speter{
21519304Speter	register char *p1 = (char*)s1;
21619304Speter	register char const *p2 = (char const*)s2;
21719304Speter	while (n--)
21819304Speter		*p1++ = *p2++;
21919304Speter	return s1;
22019304Speter}
22119304Speter#endif
22219304Speter
22319304Speter#if RCS_lint
22419304Speter	malloc_type lintalloc;
22519304Speter#endif
22619304Speter
227254225Speter/*
228254225Speter * list of blocks allocated with ftestalloc()
229254225Speter * These blocks can be freed by ffree when we're done with the current file.
230254225Speter * We could put the free block inside struct alloclist, rather than a pointer
23119304Speter * to the free block, but that would be less portable.
23219304Speter */
23319304Speterstruct alloclist {
23419304Speter	malloc_type alloc;
235254225Speter	struct alloclist *nextalloc;
236254225Speter};
23719304Speterstatic struct alloclist *alloced;
23819304Speter
23919304Speter
24019304Speter	static malloc_type okalloc P((malloc_type));
24119304Speter	static malloc_type
24219304Speterokalloc(p)
24319304Speter	malloc_type p;
24419304Speter{
24519304Speter	if (!p)
24619304Speter		faterror("out of memory");
24719304Speter	return p;
24819304Speter}
24919304Speter
25019304Speter	malloc_type
25119304Spetertestalloc(size)
25219304Speter	size_t size;
25319304Speter/* Allocate a block, testing that the allocation succeeded.  */
25419304Speter{
25519304Speter	return okalloc(malloc(size));
25619304Speter}
25719304Speter
25819304Speter	malloc_type
25919304Spetertestrealloc(ptr, size)
26019304Speter	malloc_type ptr;
26119304Speter	size_t size;
26219304Speter/* Reallocate a block, testing that the allocation succeeded.  */
26319304Speter{
26419304Speter	return okalloc(realloc(ptr, size));
26519304Speter}
26619304Speter
26719304Speter	malloc_type
26819304Speterfremember(ptr)
26919304Speter	malloc_type ptr;
270254225Speter/* Remember PTR in 'alloced' so that it can be freed later.  Yield PTR.  */
27119304Speter{
27219304Speter	register struct alloclist *q = talloc(struct alloclist);
27319304Speter	q->nextalloc = alloced;
27419304Speter	alloced = q;
275254225Speter	return q->alloc = ptr;
276254225Speter}
277254225Speter
27819304Speter	malloc_type
27919304Speterftestalloc(size)
280254225Speter	size_t size;
28119304Speter/* Allocate a block, putting it in 'alloced' so it can be freed later. */
282254225Speter{
283254225Speter	return fremember(testalloc(size));
28419304Speter}
28519304Speter
286254225Speter	void
28719304Speterffree()
28819304Speter/* Free all blocks allocated with ftestalloc().  */
28919304Speter{
29019304Speter	register struct alloclist *p, *q;
29119304Speter	for (p = alloced;  p;  p = q) {
29219304Speter		q = p->nextalloc;
293254225Speter		tfree(p->alloc);
29419304Speter		tfree(p);
29519304Speter	}
29619304Speter	alloced = 0;
29719304Speter}
29819304Speter
29919304Speter	void
30019304Speterffree1(f)
30119304Speter	register char const *f;
30219304Speter/* Free the block f, which was allocated by ftestalloc.  */
30319304Speter{
30419304Speter	register struct alloclist *p, **a = &alloced;
30519304Speter
30619304Speter	while ((p = *a)->alloc  !=  f)
30719304Speter		a = &p->nextalloc;
30819304Speter	*a = p->nextalloc;
30919304Speter	tfree(p->alloc);
31019304Speter	tfree(p);
31119304Speter}
31219304Speter
31319304Speter	char *
31419304Speterstr_save(s)
31519304Speter	char const *s;
31619304Speter/* Save s in permanently allocated storage. */
31719304Speter{
31819304Speter	return strcpy(tnalloc(char, strlen(s)+1), s);
31919304Speter}
32019304Speter
32119304Speter	char *
32219304Speterfstr_save(s)
32319304Speter	char const *s;
32419304Speter/* Save s in storage that will be deallocated when we're done with this file. */
32519304Speter{
32619304Speter	return strcpy(ftnalloc(char, strlen(s)+1), s);
32719304Speter}
32819304Speter
32919304Speter	char *
330254225Spetercgetenv(name)
331254225Speter	char const *name;
332254225Speter/* Like getenv(), but yield a copy; getenv() can overwrite old results. */
33319304Speter{
33419304Speter	register char *p;
33519304Speter
33619304Speter	return (p=getenv(name)) ? str_save(p) : p;
33719304Speter}
33819304Speter
33919304Speter	char const *
34019304Spetergetusername(suspicious)
34119304Speter	int suspicious;
34219304Speter/* Get the caller's login name.  Trust only getwpuid if SUSPICIOUS.  */
34319304Speter{
34419304Speter	static char *name;
34519304Speter
34619304Speter	if (!name) {
34719304Speter		if (
348254225Speter		    /* Prefer getenv() unless suspicious; it's much faster.  */
34919304Speter#		    if getlogin_is_secure
35019304Speter			    (suspicious
35119304Speter			    || (
35219304Speter				    !(name = cgetenv("LOGNAME"))
35319304Speter				&&  !(name = cgetenv("USER"))
35419304Speter			    ))
35519304Speter			&&  !(name = getlogin())
35619304Speter#		    else
35719304Speter			suspicious
35819304Speter			|| (
35919304Speter				!(name = cgetenv("LOGNAME"))
36019304Speter			    &&  !(name = cgetenv("USER"))
36119304Speter			    &&  !(name = getlogin())
36219304Speter			)
36319304Speter#		    endif
36419304Speter		) {
36519304Speter#if has_getuid && has_getpwuid
36619304Speter			struct passwd const *pw = getpwuid(ruid());
36719304Speter			if (!pw)
36819304Speter			    faterror("no password entry for userid %lu",
36919304Speter				     (unsigned long)ruid()
37019304Speter			    );
37119304Speter			name = pw->pw_name;
37219304Speter#else
37319304Speter#if has_setuid
37419304Speter			faterror("setuid not supported");
37519304Speter#else
376254225Speter			faterror("Who are you?  Please setenv LOGNAME.");
377254225Speter#endif
378254225Speter#endif
37919304Speter		}
38019304Speter		checksid(name);
38119304Speter	}
38219304Speter	return name;
38319304Speter}
38419304Speter
38519304Speter
38619304Speter
38719304Speter
38819304Speter#if has_signal
38919304Speter
39019304Speter/*
39119304Speter *	 Signal handling
39219304Speter *
39319304Speter * Standard C places too many restrictions on signal handlers.
39419304Speter * We obey as many of them as we can.
39519304Speter * Posix places fewer restrictions, and we are Posix-compatible here.
39619304Speter */
39719304Speter
39819304Speterstatic sig_atomic_t volatile heldsignal, holdlevel;
39919304Speter#ifdef SA_SIGINFO
40019304Speter	static int unsupported_SA_SIGINFO;
40119304Speter	static siginfo_t bufsiginfo;
40219304Speter	static siginfo_t *volatile heldsiginfo;
40319304Speter#endif
40419304Speter
40519304Speter
40619304Speter#if has_NFS && has_mmap && large_memory && mmap_signal
40719304Speter    static char const *accessName;
40819304Speter
40919304Speter	  void
41019304Speter    readAccessFilenameBuffer(filename, p)
41119304Speter	char const *filename;
41219304Speter	unsigned char const *p;
41319304Speter    {
41419304Speter	unsigned char volatile t;
41519304Speter	accessName = filename;
41619304Speter	t = *p;
41719304Speter	accessName = 0;
41819304Speter    }
41919304Speter#else
42019304Speter#   define accessName ((char const *) 0)
42119304Speter#endif
42219304Speter
42319304Speter
42419304Speter#if !has_psignal
42519304Speter
42619304Speter# define psignal my_psignal
42719304Speter	static void my_psignal P((int,char const*));
42819304Speter	static void
42919304Spetermy_psignal(sig, s)
43019304Speter	int sig;
43119304Speter	char const *s;
432254225Speter{
433254225Speter	char const *sname = "Unknown signal";
43419304Speter#	if has_sys_siglist && defined(NSIG)
43519304Speter	    if ((unsigned)sig < NSIG)
43619304Speter		sname = sys_siglist[sig];
43719304Speter#	else
43819304Speter	    switch (sig) {
43919304Speter#	       ifdef SIGHUP
44019304Speter		case SIGHUP:	sname = "Hangup";  break;
44119304Speter#	       endif
442254225Speter#	       ifdef SIGINT
443254225Speter		case SIGINT:	sname = "Interrupt";  break;
44419304Speter#	       endif
44519304Speter#	       ifdef SIGPIPE
44619304Speter		case SIGPIPE:	sname = "Broken pipe";  break;
44719304Speter#	       endif
44819304Speter#	       ifdef SIGQUIT
44919304Speter		case SIGQUIT:	sname = "Quit";  break;
45019304Speter#	       endif
45119304Speter#	       ifdef SIGTERM
45219304Speter		case SIGTERM:	sname = "Terminated";  break;
45319304Speter#	       endif
45419304Speter#	       ifdef SIGXCPU
45519304Speter		case SIGXCPU:	sname = "Cputime limit exceeded";  break;
45619304Speter#	       endif
45719304Speter#	       ifdef SIGXFSZ
45819304Speter		case SIGXFSZ:	sname = "Filesize limit exceeded";  break;
45919304Speter#	       endif
46019304Speter#	      if has_mmap && large_memory
46119304Speter#	       if defined(SIGBUS) && mmap_signal==SIGBUS
46219304Speter		case SIGBUS:	sname = "Bus error";  break;
46319304Speter#	       endif
46419304Speter#	       if defined(SIGSEGV) && mmap_signal==SIGSEGV
46519304Speter		case SIGSEGV:	sname = "Segmentation fault";  break;
46619304Speter#	       endif
46719304Speter#	      endif
46819304Speter	    }
46919304Speter#	endif
47019304Speter
47119304Speter	/* Avoid calling sprintf etc., in case they're not reentrant.  */
47219304Speter	{
47319304Speter	    char const *p;
47419304Speter	    char buf[BUFSIZ], *b = buf;
47519304Speter	    for (p = s;  *p;  *b++ = *p++)
47619304Speter		continue;
47719304Speter	    *b++ = ':';
47819304Speter	    *b++ = ' ';
47919304Speter	    for (p = sname;  *p;  *b++ = *p++)
480254225Speter		continue;
48119304Speter	    *b++ = '\n';
48219304Speter	    VOID write(STDERR_FILENO, buf, b - buf);
48319304Speter	}
48419304Speter}
48519304Speter#endif
48619304Speter
48719304Speterstatic signal_type catchsig P((int));
48819304Speter#ifdef SA_SIGINFO
48919304Speter	static signal_type catchsigaction P((int,siginfo_t*,void*));
49019304Speter#endif
49119304Speter
49219304Speter	static signal_type
49319304Spetercatchsig(s)
49419304Speter	int s;
49519304Speter#ifdef SA_SIGINFO
49619304Speter{
49719304Speter	catchsigaction(s, (siginfo_t *)0, (void *)0);
49819304Speter}
49919304Speter	static signal_type
50019304Spetercatchsigaction(s, i, c)
50119304Speter	int s;
50219304Speter	siginfo_t *i;
50319304Speter	void *c;
50419304Speter#endif
50519304Speter{
50619304Speter#   if sig_zaps_handler
50719304Speter	/* If a signal arrives before we reset the handler, we lose. */
50819304Speter	VOID signal(s, SIG_IGN);
50919304Speter#   endif
51019304Speter
51119304Speter#   ifdef SA_SIGINFO
51219304Speter	if (!unsupported_SA_SIGINFO)
51319304Speter	    i = 0;
51419304Speter#   endif
51519304Speter
51619304Speter    if (holdlevel) {
51719304Speter	heldsignal = s;
51819304Speter#	ifdef SA_SIGINFO
51919304Speter	    if (i) {
52019304Speter		bufsiginfo = *i;
52119304Speter		heldsiginfo = &bufsiginfo;
52219304Speter	    }
52319304Speter#	endif
52419304Speter	return;
52519304Speter    }
52619304Speter
52719304Speter    ignoreints();
52819304Speter    setrid();
52919304Speter    if (!quietflag) {
53019304Speter	/* Avoid calling sprintf etc., in case they're not reentrant.  */
53119304Speter	char const *p;
53219304Speter	char buf[BUFSIZ], *b = buf;
53319304Speter
534254225Speter	if ( !	(
535254225Speter#		if has_mmap && large_memory && mmap_signal
53619304Speter			/* Check whether this signal was planned.  */
53719304Speter			s == mmap_signal && accessName
53819304Speter#		else
53919304Speter			0
54019304Speter#		endif
54119304Speter	)) {
54219304Speter	    char const *nRCS = "\nRCS";
54319304Speter#	    if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal
54419304Speter		if (s == mmap_signal  &&  i  &&  i->si_errno) {
54519304Speter		    errno = i->si_errno;
54619304Speter		    perror(nRCS++);
54719304Speter		}
54819304Speter#	    endif
54919304Speter#	    if defined(SA_SIGINFO) && has_psiginfo
55019304Speter		if (i)
55119304Speter		    psiginfo(i, nRCS);
55219304Speter		else
55319304Speter		    psignal(s, nRCS);
55419304Speter#	    else
55519304Speter		psignal(s, nRCS);
55619304Speter#	    endif
55719304Speter	}
55819304Speter
55919304Speter	for (p = "RCS: ";  *p;  *b++ = *p++)
56019304Speter	    continue;
56119304Speter#	if has_mmap && large_memory && mmap_signal
56219304Speter	    if (s == mmap_signal) {
56319304Speter		p = accessName;
564254225Speter		if (!p)
565254225Speter		    p = "Was a file changed by some other process?  ";
566254225Speter		else {
56719304Speter		    char const *p1;
56819304Speter		    for (p1 = p;  *p1;  p1++)
56919304Speter			continue;
57019304Speter		    VOID write(STDERR_FILENO, buf, b - buf);
57119304Speter		    VOID write(STDERR_FILENO, p, p1 - p);
57219304Speter		    b = buf;
57319304Speter		    p = ": Permission denied.  ";
57419304Speter		}
57519304Speter		while (*p)
57619304Speter		    *b++ = *p++;
57719304Speter	    }
57819304Speter#	endif
57919304Speter	for (p = "Cleaning up.\n";  *p;  *b++ = *p++)
58019304Speter	    continue;
58119304Speter	VOID write(STDERR_FILENO, buf, b - buf);
58219304Speter    }
58319304Speter    exiterr();
58419304Speter}
58519304Speter
58619304Speter	void
58719304Speterignoreints()
58819304Speter{
58919304Speter	++holdlevel;
59019304Speter}
59119304Speter
59219304Speter	void
59319304Speterrestoreints()
59419304Speter{
59519304Speter	if (!--holdlevel && heldsignal)
59619304Speter#	    ifdef SA_SIGINFO
59719304Speter		VOID catchsigaction(heldsignal, heldsiginfo, (void *)0);
59819304Speter#	    else
59919304Speter		VOID catchsig(heldsignal);
60019304Speter#	    endif
60119304Speter}
60219304Speter
60319304Speter
60419304Speterstatic void setup_catchsig P((int const*,int));
60519304Speter
60619304Speter#if has_sigaction
60719304Speter
60819304Speter	static void check_sig P((int));
60919304Speter	static void
61019304Speter  check_sig(r)
61119304Speter	int r;
61219304Speter  {
61319304Speter	if (r != 0)
614254225Speter		efaterror("signal handling");
615254225Speter  }
61619304Speter
61719304Speter	static void
61819304Speter  setup_catchsig(sig, sigs)
61919304Speter	int const *sig;
62019304Speter	int sigs;
62119304Speter  {
62219304Speter	register int i, j;
62319304Speter	struct sigaction act;
62419304Speter
62519304Speter	for (i=sigs; 0<=--i; ) {
62619304Speter	    check_sig(sigaction(sig[i], (struct sigaction*)0, &act));
62719304Speter	    if (act.sa_handler != SIG_IGN) {
62819304Speter		act.sa_handler = catchsig;
62919304Speter#		ifdef SA_SIGINFO
63019304Speter		    if (!unsupported_SA_SIGINFO) {
631254225Speter#			if has_sa_sigaction
632254225Speter			    act.sa_sigaction = catchsigaction;
63319304Speter#			else
63419304Speter			    act.sa_handler = catchsigaction;
63519304Speter#			endif
63619304Speter			act.sa_flags |= SA_SIGINFO;
63719304Speter		    }
63819304Speter#		endif
63919304Speter		for (j=sigs; 0<=--j; )
64019304Speter		    check_sig(sigaddset(&act.sa_mask, sig[j]));
64119304Speter		if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) {
64219304Speter#		    if defined(SA_SIGINFO) && defined(ENOTSUP)
64319304Speter			if (errno == ENOTSUP  &&  !unsupported_SA_SIGINFO) {
64419304Speter			    /* Turn off use of SA_SIGINFO and try again.  */
64519304Speter			    unsupported_SA_SIGINFO = 1;
64619304Speter			    i++;
64719304Speter			    continue;
64819304Speter			}
64919304Speter#		    endif
65019304Speter		    check_sig(-1);
65119304Speter		}
65219304Speter	    }
65319304Speter	}
65419304Speter  }
65519304Speter
65619304Speter#else
65719304Speter#if has_sigblock
65819304Speter
65919304Speter	static void
66019304Speter  setup_catchsig(sig, sigs)
661254225Speter	int const *sig;
662254225Speter	int sigs;
663254225Speter  {
664254225Speter	register int i;
66519304Speter	int mask;
66619304Speter
66719304Speter	mask = 0;
66819304Speter	for (i=sigs; 0<=--i; )
66919304Speter		mask |= sigmask(sig[i]);
67019304Speter	mask = sigblock(mask);
67119304Speter	for (i=sigs; 0<=--i; )
67219304Speter		if (
67319304Speter		    signal(sig[i], catchsig) == SIG_IGN  &&
67419304Speter		    signal(sig[i], SIG_IGN) != catchsig
67519304Speter		)
67619304Speter			faterror("signal catcher failure");
677254225Speter	VOID sigsetmask(mask);
678254225Speter  }
679254225Speter
680254225Speter#else
681254225Speter
68219304Speter	static void
68319304Speter  setup_catchsig(sig, sigs)
68419304Speter	int const *sig;
68519304Speter	int sigs;
68619304Speter  {
68719304Speter	register i;
68819304Speter
68919304Speter	for (i=sigs; 0<=--i; )
69019304Speter		if (
69119304Speter		    signal(sig[i], SIG_IGN) != SIG_IGN  &&
69219304Speter		    signal(sig[i], catchsig) != SIG_IGN
69319304Speter		)
69419304Speter			faterror("signal catcher failure");
69519304Speter  }
69619304Speter
69719304Speter#endif
69819304Speter#endif
69919304Speter
70019304Speter
70119304Speterstatic int const regsigs[] = {
70219304Speter# ifdef SIGHUP
70319304Speter	SIGHUP,
70419304Speter# endif
70519304Speter# ifdef SIGINT
70619304Speter	SIGINT,
70719304Speter# endif
70819304Speter# ifdef SIGPIPE
70919304Speter	SIGPIPE,
71019304Speter# endif
71119304Speter# ifdef SIGQUIT
71219304Speter	SIGQUIT,
71319304Speter# endif
71419304Speter# ifdef SIGTERM
71519304Speter	SIGTERM,
71619304Speter# endif
71719304Speter# ifdef SIGXCPU
71819304Speter	SIGXCPU,
71919304Speter# endif
72019304Speter# ifdef SIGXFSZ
72119304Speter	SIGXFSZ,
72219304Speter# endif
72319304Speter};
72419304Speter
72519304Speter	void
726254225Spetercatchints()
727254225Speter{
728254225Speter	static int catching_ints;
729254225Speter	if (!catching_ints) {
730254225Speter	    catching_ints = true;
731254225Speter	    setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs)));
732254225Speter	}
733254225Speter}
734254225Speter
735254225Speter#if has_mmap && large_memory && mmap_signal
736254225Speter
737254225Speter    /*
738254225Speter    * If you mmap an NFS file, and someone on another client removes the last
739254225Speter    * link to that file, and you later reference an uncached part of that file,
740254225Speter    * you'll get a SIGBUS or SIGSEGV (depending on the operating system).
741254225Speter    * Catch the signal and report the problem to the user.
742254225Speter    * Unfortunately, there's no portable way to differentiate between this
743254225Speter    * problem and actual bugs in the program.
744254225Speter    * This NFS problem is rare, thank goodness.
745254225Speter    *
746254225Speter    * This can also occur if someone truncates the file, even without NFS.
747254225Speter    */
748254225Speter
749254225Speter    static int const mmapsigs[] = { mmap_signal };
750254225Speter
751254225Speter	    void
752254225Speter    catchmmapints()
753254225Speter    {
754254225Speter	static int catching_mmap_ints;
755254225Speter	if (!catching_mmap_ints) {
756254225Speter	    catching_mmap_ints = true;
757254225Speter	    setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs)));
758254225Speter	}
759254225Speter    }
760254225Speter#endif
761254225Speter
762254225Speter#endif /* has_signal */
763254225Speter
764254225Speter
765254225Speter	void
766254225Speterfastcopy(inf,outf)
767254225Speter	register RILE *inf;
768	FILE *outf;
769/* Function: copies the remainder of file inf to outf.
770 */
771{
772#if large_memory
773#	if maps_memory
774	    awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
775	    inf->ptr = inf->lim;
776#	else
777	    for (;;) {
778		awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf);
779		inf->ptr = inf->readlim;
780		if (inf->ptr == inf->lim)
781		    break;
782		VOID Igetmore(inf);
783	    }
784#	endif
785#else
786	char buf[BUFSIZ*8];
787	register fread_type rcount;
788
789        /*now read the rest of the file in blocks*/
790	while (!feof(inf)) {
791		if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) {
792			testIerror(inf);
793			return;
794		}
795		awrite(buf, (size_t)rcount, outf);
796        }
797#endif
798}
799
800#ifndef SSIZE_MAX
801 /* This does not work in #ifs, but it's good enough for us.  */
802 /* Underestimating SSIZE_MAX may slow us down, but it won't break us.  */
803#	define SSIZE_MAX ((unsigned)-1 >> 1)
804#endif
805
806	void
807awrite(buf, chars, f)
808	char const *buf;
809	size_t chars;
810	FILE *f;
811{
812	/* Posix 1003.1-1990 ssize_t hack */
813	while (SSIZE_MAX < chars) {
814		if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f)  !=  SSIZE_MAX)
815			Oerror();
816		buf += SSIZE_MAX;
817		chars -= SSIZE_MAX;
818	}
819
820	if (Fwrite(buf, sizeof(*buf), chars, f)  !=  chars)
821		Oerror();
822}
823
824/* dup a file descriptor; the result must not be stdin, stdout, or stderr.  */
825	static int dupSafer P((int));
826	static int
827dupSafer(fd)
828	int fd;
829{
830#	ifdef F_DUPFD
831	    return fcntl(fd, F_DUPFD, STDERR_FILENO + 1);
832#	else
833	    int e, f, i, used = 0;
834	    while (STDIN_FILENO <= (f = dup(fd))  &&  f <= STDERR_FILENO)
835		    used |= 1<<f;
836	    e = errno;
837	    for (i = STDIN_FILENO;  i <= STDERR_FILENO;  i++)
838		    if (used & (1<<i))
839			    VOID close(i);
840	    errno = e;
841	    return f;
842#	endif
843}
844
845/* Renumber a file descriptor so that it's not stdin, stdout, or stderr.  */
846	int
847fdSafer(fd)
848	int fd;
849{
850	if (STDIN_FILENO <= fd  &&  fd <= STDERR_FILENO) {
851		int f = dupSafer(fd);
852		int e = errno;
853		VOID close(fd);
854		errno = e;
855		fd = f;
856	}
857	return fd;
858}
859
860/* Like fopen, except the result is never stdin, stdout, or stderr.  */
861	FILE *
862fopenSafer(filename, type)
863	char const *filename;
864	char const *type;
865{
866	FILE *stream = fopen(filename, type);
867	if (stream) {
868		int fd = fileno(stream);
869		if (STDIN_FILENO <= fd  &&  fd <= STDERR_FILENO) {
870			int f = dupSafer(fd);
871			if (f < 0) {
872				int e = errno;
873				VOID fclose(stream);
874				errno = e;
875				return 0;
876			}
877			if (fclose(stream) != 0) {
878				int e = errno;
879				VOID close(f);
880				errno = e;
881				return 0;
882			}
883			stream = fdopen(f, type);
884		}
885	}
886	return stream;
887}
888
889
890#ifdef F_DUPFD
891#	undef dup
892#	define dup(fd) fcntl(fd, F_DUPFD, 0)
893#endif
894
895
896#if has_fork || has_spawn
897
898	static int movefd P((int,int));
899	static int
900movefd(old, new)
901	int old, new;
902{
903	if (old < 0  ||  old == new)
904		return old;
905#	ifdef F_DUPFD
906		new = fcntl(old, F_DUPFD, new);
907#	else
908		new = dup2(old, new);
909#	endif
910	return close(old)==0 ? new : -1;
911}
912
913	static int fdreopen P((int,char const*,int));
914	static int
915fdreopen(fd, file, flags)
916	int fd;
917	char const *file;
918	int flags;
919{
920	int newfd;
921	VOID close(fd);
922	newfd =
923#if !open_can_creat
924		flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
925#endif
926		open(file, flags, S_IRUSR|S_IWUSR);
927	return movefd(newfd, fd);
928}
929
930#if has_spawn
931	static void redirect P((int,int));
932	static void
933redirect(old, new)
934	int old, new;
935/*
936* Move file descriptor OLD to NEW.
937* If OLD is -1, do nothing.
938* If OLD is -2, just close NEW.
939*/
940{
941	if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0))
942		efaterror("spawn I/O redirection");
943}
944#endif
945
946
947#else /* !has_fork && !has_spawn */
948
949	static void bufargcat P((struct buf*,int,char const*));
950	static void
951bufargcat(b, c, s)
952	register struct buf *b;
953	int c;
954	register char const *s;
955/* Append to B a copy of C, plus a quoted copy of S.  */
956{
957	register char *p;
958	register char const *t;
959	size_t bl, sl;
960
961	for (t=s, sl=0;  *t;  )
962		sl  +=  3*(*t++=='\'') + 1;
963	bl = strlen(b->string);
964	bufrealloc(b, bl + sl + 4);
965	p = b->string + bl;
966	*p++ = c;
967	*p++ = '\'';
968	while (*s) {
969		if (*s == '\'') {
970			*p++ = '\'';
971			*p++ = '\\';
972			*p++ = '\'';
973		}
974		*p++ = *s++;
975	}
976	*p++ = '\'';
977	*p = 0;
978}
979
980#endif
981
982#if !has_spawn && has_fork
983/*
984* Output the string S to stderr, without touching any I/O buffers.
985* This is useful if you are a child process, whose buffers are usually wrong.
986* Exit immediately if the write does not completely succeed.
987*/
988static void write_stderr P((char const *));
989	static void
990write_stderr(s)
991	char const *s;
992{
993	size_t slen = strlen(s);
994	if (write(STDERR_FILENO, s, slen) != slen)
995		_exit(EXIT_TROUBLE);
996}
997#endif
998
999/*
1000* Run a command.
1001* infd, if not -1, is the input file descriptor.
1002* outname, if nonzero, is the name of the output file.
1003* args[1..] form the command to be run; args[0] might be modified.
1004*/
1005	int
1006runv(infd, outname, args)
1007	int infd;
1008	char const *outname, **args;
1009{
1010	int wstatus;
1011
1012#if bad_wait_if_SIGCHLD_ignored
1013	static int fixed_SIGCHLD;
1014	if (!fixed_SIGCHLD) {
1015	    fixed_SIGCHLD = true;
1016#	    ifndef SIGCHLD
1017#	    define SIGCHLD SIGCLD
1018#	    endif
1019	    VOID signal(SIGCHLD, SIG_DFL);
1020	}
1021#endif
1022
1023	oflush();
1024	eflush();
1025    {
1026#if has_spawn
1027	int in, out;
1028	char const *file;
1029
1030	in = -1;
1031	if (infd != -1  &&  infd != STDIN_FILENO) {
1032	    if ((in = dup(STDIN_FILENO)) < 0) {
1033		if (errno != EBADF)
1034		    efaterror("spawn input setup");
1035		in = -2;
1036	    } else {
1037#		ifdef F_DUPFD
1038		    if (close(STDIN_FILENO) != 0)
1039			efaterror("spawn input close");
1040#		endif
1041	    }
1042	    if (
1043#		ifdef F_DUPFD
1044		    fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO
1045#		else
1046		    dup2(infd, STDIN_FILENO) != STDIN_FILENO
1047#		endif
1048	    )
1049		efaterror("spawn input redirection");
1050	}
1051
1052	out = -1;
1053	if (outname) {
1054	    if ((out = dup(STDOUT_FILENO)) < 0) {
1055		if (errno != EBADF)
1056		    efaterror("spawn output setup");
1057		out = -2;
1058	    }
1059	    if (fdreopen(
1060		STDOUT_FILENO, outname,
1061		O_CREAT | O_TRUNC | O_WRONLY
1062	    ) < 0)
1063		efaterror(outname);
1064	}
1065
1066	wstatus = spawn_RCS(0, args[1], (char**)(args + 1));
1067#	ifdef RCS_SHELL
1068	    if (wstatus == -1  &&  errno == ENOEXEC) {
1069		args[0] = RCS_SHELL;
1070		wstatus = spawnv(0, args[0], (char**)args);
1071	    }
1072#	endif
1073	redirect(in, STDIN_FILENO);
1074	redirect(out, STDOUT_FILENO);
1075#else
1076#if has_fork
1077	pid_t pid;
1078	if (!(pid = vfork())) {
1079		char const *notfound;
1080		if (infd != -1  &&  infd != STDIN_FILENO  &&  (
1081#		    ifdef F_DUPFD
1082			(VOID close(STDIN_FILENO),
1083			fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO)
1084#		    else
1085			dup2(infd, STDIN_FILENO) != STDIN_FILENO
1086#		    endif
1087		)) {
1088		    /* Avoid perror since it may misuse buffers.  */
1089		    write_stderr(args[1]);
1090		    write_stderr(": I/O redirection failed\n");
1091		    _exit(EXIT_TROUBLE);
1092		}
1093
1094		if (outname)
1095		    if (fdreopen(
1096			STDOUT_FILENO, outname,
1097			O_CREAT | O_TRUNC | O_WRONLY
1098		    ) < 0) {
1099			/* Avoid perror since it may misuse buffers.  */
1100			write_stderr(args[1]);
1101			write_stderr(": ");
1102			write_stderr(outname);
1103			write_stderr(": cannot create\n");
1104			_exit(EXIT_TROUBLE);
1105		    }
1106		VOID exec_RCS(args[1], (char**)(args + 1));
1107		notfound = args[1];
1108#		ifdef RCS_SHELL
1109		    if (errno == ENOEXEC) {
1110			args[0] = notfound = RCS_SHELL;
1111			VOID execv(args[0], (char**)args);
1112		    }
1113#		endif
1114
1115		/* Avoid perror since it may misuse buffers.  */
1116		write_stderr(notfound);
1117		write_stderr(": not found\n");
1118		_exit(EXIT_TROUBLE);
1119	}
1120	if (pid < 0)
1121		efaterror("fork");
1122#	if has_waitpid
1123		if (waitpid(pid, &wstatus, 0) < 0)
1124			efaterror("waitpid");
1125#	else
1126		{
1127			pid_t w;
1128			do {
1129				if ((w = wait(&wstatus)) < 0)
1130					efaterror("wait");
1131			} while (w != pid);
1132		}
1133#	endif
1134#else
1135	static struct buf b;
1136	char const *p;
1137
1138	/* Use system().  On many hosts system() discards signals.  Yuck!  */
1139	p = args + 1;
1140	bufscpy(&b, *p);
1141	while (*++p)
1142		bufargcat(&b, ' ', *p);
1143	if (infd != -1  &&  infd != STDIN_FILENO) {
1144		char redirection[32];
1145		VOID sprintf(redirection, "<&%d", infd);
1146		bufscat(&b, redirection);
1147	}
1148	if (outname)
1149		bufargcat(&b, '>', outname);
1150	wstatus = system(b.string);
1151#endif
1152#endif
1153    }
1154	if (!WIFEXITED(wstatus)) {
1155		if (WIFSIGNALED(wstatus)) {
1156			psignal(WTERMSIG(wstatus), args[1]);
1157			fatcleanup(1);
1158		}
1159		faterror("%s failed for unknown reason", args[1]);
1160	}
1161	return WEXITSTATUS(wstatus);
1162}
1163
1164#define CARGSMAX 20
1165/*
1166* Run a command.
1167* infd, if not -1, is the input file descriptor.
1168* outname, if nonzero, is the name of the output file.
1169* The remaining arguments specify the command and its arguments.
1170*/
1171	int
1172#if has_prototypes
1173run(int infd, char const *outname, ...)
1174#else
1175	/*VARARGS2*/
1176run(infd, outname, va_alist)
1177	int infd;
1178	char const *outname;
1179	va_dcl
1180#endif
1181{
1182	va_list ap;
1183	char const *rgargs[CARGSMAX];
1184	register int i;
1185	vararg_start(ap, outname);
1186	for (i = 1;  (rgargs[i++] = va_arg(ap, char const*));  )
1187		if (CARGSMAX <= i)
1188			faterror("too many command arguments");
1189	va_end(ap);
1190	return runv(infd, outname, rgargs);
1191}
1192
1193
1194int RCSversion;
1195
1196	void
1197setRCSversion(str)
1198	char const *str;
1199{
1200	static int oldversion;
1201
1202	register char const *s = str + 2;
1203
1204	if (*s) {
1205		int v = VERSION_DEFAULT;
1206
1207		if (oldversion)
1208			redefined('V');
1209		oldversion = true;
1210		v = 0;
1211		while (isdigit(*s))
1212			v  =  10*v + *s++ - '0';
1213		if (*s)
1214			error("%s isn't a number", str);
1215		else if (v < VERSION_min  ||  VERSION_max < v)
1216			error("%s out of range %d..%d",
1217				str, VERSION_min, VERSION_max
1218			);
1219
1220		RCSversion = VERSION(v);
1221	} else {
1222		printf("RCS version %s\n", RCS_version_string);
1223		exit(0);
1224	}
1225}
1226
1227	int
1228getRCSINIT(argc, argv, newargv)
1229	int argc;
1230	char **argv, ***newargv;
1231{
1232	register char *p, *q, **pp;
1233	size_t n;
1234
1235	if (!(q = cgetenv("RCSINIT")))
1236		*newargv = argv;
1237	else {
1238		n = argc + 2;
1239		/*
1240		 * Count spaces in RCSINIT to allocate a new arg vector.
1241		 * This is an upper bound, but it's OK even if too large.
1242		 */
1243		for (p = q;  ;  ) {
1244			switch (*p++) {
1245			    default:
1246				continue;
1247
1248			    case ' ':
1249			    case '\b': case '\f': case '\n':
1250			    case '\r': case '\t': case '\v':
1251				n++;
1252				continue;
1253
1254			    case '\0':
1255				break;
1256			}
1257			break;
1258		}
1259		*newargv = pp = tnalloc(char*, n);
1260		*pp++ = *argv++; /* copy program name */
1261		for (p = q;  ;  ) {
1262			for (;;) {
1263				switch (*q) {
1264				    case '\0':
1265					goto copyrest;
1266
1267				    case ' ':
1268				    case '\b': case '\f': case '\n':
1269				    case '\r': case '\t': case '\v':
1270					q++;
1271					continue;
1272				}
1273				break;
1274			}
1275			*pp++ = p;
1276			++argc;
1277			for (;;) {
1278				switch ((*p++ = *q++)) {
1279				    case '\0':
1280					goto copyrest;
1281
1282				    case '\\':
1283					if (!*q)
1284						goto copyrest;
1285					p[-1] = *q++;
1286					continue;
1287
1288				    default:
1289					continue;
1290
1291				    case ' ':
1292				    case '\b': case '\f': case '\n':
1293				    case '\r': case '\t': case '\v':
1294					break;
1295				}
1296				break;
1297			}
1298			p[-1] = '\0';
1299		}
1300	    copyrest:
1301		while ((*pp++ = *argv++))
1302			continue;
1303	}
1304	return argc;
1305}
1306
1307
1308#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
1309
1310#if has_getuid
1311	uid_t ruid() { cacheid(getuid()); }
1312#endif
1313#if has_setuid
1314	uid_t euid() { cacheid(geteuid()); }
1315#endif
1316
1317
1318#if has_setuid
1319
1320/*
1321 * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
1322 * because it lets us switch back and forth between arbitrary users.
1323 * If seteuid() doesn't work, we fall back on setuid(),
1324 * which works if saved setuid is supported,
1325 * unless the real or effective user is root.
1326 * This area is such a mess that we always check switches at runtime.
1327 */
1328
1329	static void
1330#if has_prototypes
1331set_uid_to(uid_t u)
1332#else
1333 set_uid_to(u) uid_t u;
1334#endif
1335/* Become user u.  */
1336{
1337	static int looping;
1338
1339	if (euid() == ruid())
1340		return;
1341#if (has_fork||has_spawn) && DIFF_ABSOLUTE
1342#	if has_setreuid
1343		if (setreuid(u==euid() ? ruid() : euid(), u) != 0)
1344			efaterror("setuid");
1345#	else
1346		if (seteuid(u) != 0)
1347			efaterror("setuid");
1348#	endif
1349#endif
1350	if (geteuid() != u) {
1351		if (looping)
1352			return;
1353		looping = true;
1354		faterror("root setuid not supported" + (u?5:0));
1355	}
1356}
1357
1358static int stick_with_euid;
1359
1360	void
1361/* Ignore all calls to seteid() and setrid().  */
1362nosetid()
1363{
1364	stick_with_euid = true;
1365}
1366
1367	void
1368seteid()
1369/* Become effective user.  */
1370{
1371	if (!stick_with_euid)
1372		set_uid_to(euid());
1373}
1374
1375	void
1376setrid()
1377/* Become real user.  */
1378{
1379	if (!stick_with_euid)
1380		set_uid_to(ruid());
1381}
1382#endif
1383
1384	time_t
1385now()
1386{
1387	static time_t t;
1388	if (!t  &&  time(&t) == -1)
1389		efaterror("time");
1390	return t;
1391}
1392