1/* RCS utility functions */
2
3/* Copyright 1982, 1988, 1989 Walter Tichy
4   Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5   Distributed under license by the Free Software Foundation, Inc.
6
7This file is part of RCS.
8
9RCS is free software; you can redistribute it and/or modify
10it under the terms of the GNU General Public License as published by
11the Free Software Foundation; either version 2, or (at your option)
12any later version.
13
14RCS is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with RCS; see the file COPYING.
21If not, write to the Free Software Foundation,
2259 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24Report problems and direct all questions to:
25
26    rcs-bugs@cs.purdue.edu
27
28*/
29
30
31
32
33/*
34 * Revision 5.20  1995/06/16 06:19:24  eggert
35 * (catchsig): Remove `return'.
36 * Update FSF address.
37 *
38 * Revision 5.19  1995/06/02 18:19:00  eggert
39 * (catchsigaction): New name for `catchsig', for sa_sigaction signature.
40 * Use nRCS even if !has_psiginfo, to remove unused variable warning.
41 * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction.
42 * Use ENOTSUP only if defined.
43 *
44 * Revision 5.18  1995/06/01 16:23:43  eggert
45 * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo,
46 * to determine whether to use SA_SIGINFO feature,
47 * but also check at runtime whether the feature works.
48 * (catchsig): If an mmap_signal occurs, report the affected file name.
49 * (unsupported_SA_SIGINFO, accessName): New variables.
50 * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler.
51 * If SA_SIGINFO fails, fall back on sa_handler method.
52 *
53 * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions.
54 * (concatenate): Remove.
55 *
56 * (runv): Work around bad_wait_if_SIGCHLD_ignored bug.
57 * Remove reference to OPEN_O_WORK.
58 *
59 * Revision 5.17  1994/03/20 04:52:58  eggert
60 * Specify subprocess input via file descriptor, not file name.
61 * Avoid messing with I/O buffers in the child process.
62 * Define dup in terms of F_DUPFD if it exists.
63 * Move setmtime to rcsedit.c.  Remove lint.
64 *
65 * Revision 5.16  1993/11/09 17:40:15  eggert
66 * -V now prints version on stdout and exits.
67 *
68 * Revision 5.15  1993/11/03 17:42:27  eggert
69 * Use psiginfo and setreuid if available.  Move date2str to maketime.c.
70 *
71 * Revision 5.14  1992/07/28  16:12:44  eggert
72 * Add -V.  has_sigaction overrides sig_zaps_handler.  Fix -M bug.
73 * Add mmap_signal, which minimizes signal handling for non-mmap hosts.
74 *
75 * Revision 5.13  1992/02/17  23:02:28  eggert
76 * Work around NFS mmap SIGBUS problem.  Add -T support.
77 *
78 * Revision 5.12  1992/01/24  18:44:19  eggert
79 * Work around NFS mmap bug that leads to SIGBUS core dumps.  lint -> RCS_lint
80 *
81 * Revision 5.11  1992/01/06  02:42:34  eggert
82 * O_BINARY -> OPEN_O_WORK
83 * while (E) ; -> while (E) continue;
84 *
85 * Revision 5.10  1991/10/07  17:32:46  eggert
86 * Support piece tables even if !has_mmap.
87 *
88 * Revision 5.9  1991/08/19  03:13:55  eggert
89 * Add spawn() support.  Explicate assumptions about getting invoker's name.
90 * Standardize user-visible dates.  Tune.
91 *
92 * Revision 5.8  1991/04/21  11:58:30  eggert
93 * Plug setuid security hole.
94 *
95 * Revision 5.6  1991/02/26  17:48:39  eggert
96 * Fix setuid bug.  Use fread, fwrite more portably.
97 * Support waitpid.  Don't assume -1 is acceptable to W* macros.
98 * strsave -> str_save (DG/UX name clash)
99 *
100 * Revision 5.5  1990/12/04  05:18:49  eggert
101 * Don't output a blank line after a signal diagnostic.
102 * Use -I for prompts and -q for diagnostics.
103 *
104 * Revision 5.4  1990/11/01  05:03:53  eggert
105 * Remove unneeded setid check.  Add awrite(), fremember().
106 *
107 * Revision 5.3  1990/10/06  00:16:45  eggert
108 * Don't fread F if feof(F).
109 *
110 * Revision 5.2  1990/09/04  08:02:31  eggert
111 * Store fread()'s result in an fread_type object.
112 *
113 * Revision 5.1  1990/08/29  07:14:07  eggert
114 * Declare getpwuid() more carefully.
115 *
116 * Revision 5.0  1990/08/22  08:13:46  eggert
117 * Add setuid support.  Permit multiple locks per user.
118 * Remove compile-time limits; use malloc instead.
119 * Switch to GMT.  Permit dates past 1999/12/31.
120 * Add -V.  Remove snooping.  Ansify and Posixate.
121 * Tune.  Some USG hosts define NSIG but not sys_siglist.
122 * Don't run /bin/sh if it's hopeless.
123 * Don't leave garbage behind if the output is an empty pipe.
124 * Clean up after SIGXCPU or SIGXFSZ.  Print name of signal that caused cleanup.
125 *
126 * Revision 4.6  89/05/01  15:13:40  narten
127 * changed copyright header to reflect current distribution rules
128 *
129 * Revision 4.5  88/11/08  16:01:02  narten
130 * corrected use of varargs routines
131 *
132 * Revision 4.4  88/08/09  19:13:24  eggert
133 * Check for memory exhaustion.
134 * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
135 * Use execv(), not system(); yield exit status like diff(1)'s.
136 *
137 * Revision 4.3  87/10/18  10:40:22  narten
138 * Updating version numbers. Changes relative to 1.1 actually
139 * relative to 4.1
140 *
141 * Revision 1.3  87/09/24  14:01:01  narten
142 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
143 * warnings)
144 *
145 * Revision 1.2  87/03/27  14:22:43  jenkins
146 * Port to suns
147 *
148 * Revision 4.1  83/05/10  15:53:13  wft
149 * Added getcaller() and findlock().
150 * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
151 * (needed for background jobs in older shells). Added restoreints().
152 * Removed printing of full RCS path from logcommand().
153 *
154 * Revision 3.8  83/02/15  15:41:49  wft
155 * Added routine fastcopy() to copy remainder of a file in blocks.
156 *
157 * Revision 3.7  82/12/24  15:25:19  wft
158 * added catchints(), ignoreints() for catching and ingnoring interrupts;
159 * fixed catchsig().
160 *
161 * Revision 3.6  82/12/08  21:52:05  wft
162 * Using DATEFORM to format dates.
163 *
164 * Revision 3.5  82/12/04  18:20:49  wft
165 * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
166 * lockedby-field.
167 *
168 * Revision 3.4  82/12/03  17:17:43  wft
169 * Added check to addlock() ensuring only one lock per person.
170 * Addlock also returns a pointer to the lock created. Deleted fancydate().
171 *
172 * Revision 3.3  82/11/27  12:24:37  wft
173 * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
174 * Introduced macro SNOOP so that snoop can be placed in directory other than
175 * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
176 *
177 * Revision 3.2  82/10/18  21:15:11  wft
178 * added function getfullRCSname().
179 *
180 * Revision 3.1  82/10/13  16:17:37  wft
181 * Cleanup message is now suppressed in quiet mode.
182 */
183
184
185
186
187#include "rcsbase.h"
188
189libId(utilId, "$FreeBSD$")
190
191#if !has_memcmp
192	int
193memcmp(s1, s2, n)
194	void const *s1, *s2;
195	size_t n;
196{
197	register unsigned char const
198		*p1 = (unsigned char const*)s1,
199		*p2 = (unsigned char const*)s2;
200	register size_t i = n;
201	register int r = 0;
202	while (i--  &&  !(r = (*p1++ - *p2++)))
203		;
204	return r;
205}
206#endif
207
208#if !has_memcpy
209	void *
210memcpy(s1, s2, n)
211	void *s1;
212	void const *s2;
213	size_t n;
214{
215	register char *p1 = (char*)s1;
216	register char const *p2 = (char const*)s2;
217	while (n--)
218		*p1++ = *p2++;
219	return s1;
220}
221#endif
222
223#if RCS_lint
224	malloc_type lintalloc;
225#endif
226
227/*
228 * list of blocks allocated with ftestalloc()
229 * These blocks can be freed by ffree when we're done with the current file.
230 * We could put the free block inside struct alloclist, rather than a pointer
231 * to the free block, but that would be less portable.
232 */
233struct alloclist {
234	malloc_type alloc;
235	struct alloclist *nextalloc;
236};
237static struct alloclist *alloced;
238
239
240	static malloc_type okalloc P((malloc_type));
241	static malloc_type
242okalloc(p)
243	malloc_type p;
244{
245	if (!p)
246		faterror("out of memory");
247	return p;
248}
249
250	malloc_type
251testalloc(size)
252	size_t size;
253/* Allocate a block, testing that the allocation succeeded.  */
254{
255	return okalloc(malloc(size));
256}
257
258	malloc_type
259testrealloc(ptr, size)
260	malloc_type ptr;
261	size_t size;
262/* Reallocate a block, testing that the allocation succeeded.  */
263{
264	return okalloc(realloc(ptr, size));
265}
266
267	malloc_type
268fremember(ptr)
269	malloc_type ptr;
270/* Remember PTR in 'alloced' so that it can be freed later.  Yield PTR.  */
271{
272	register struct alloclist *q = talloc(struct alloclist);
273	q->nextalloc = alloced;
274	alloced = q;
275	return q->alloc = ptr;
276}
277
278	malloc_type
279ftestalloc(size)
280	size_t size;
281/* Allocate a block, putting it in 'alloced' so it can be freed later. */
282{
283	return fremember(testalloc(size));
284}
285
286	void
287ffree()
288/* Free all blocks allocated with ftestalloc().  */
289{
290	register struct alloclist *p, *q;
291	for (p = alloced;  p;  p = q) {
292		q = p->nextalloc;
293		tfree(p->alloc);
294		tfree(p);
295	}
296	alloced = 0;
297}
298
299	void
300ffree1(f)
301	register char const *f;
302/* Free the block f, which was allocated by ftestalloc.  */
303{
304	register struct alloclist *p, **a = &alloced;
305
306	while ((p = *a)->alloc  !=  f)
307		a = &p->nextalloc;
308	*a = p->nextalloc;
309	tfree(p->alloc);
310	tfree(p);
311}
312
313	char *
314str_save(s)
315	char const *s;
316/* Save s in permanently allocated storage. */
317{
318	return strcpy(tnalloc(char, strlen(s)+1), s);
319}
320
321	char *
322fstr_save(s)
323	char const *s;
324/* Save s in storage that will be deallocated when we're done with this file. */
325{
326	return strcpy(ftnalloc(char, strlen(s)+1), s);
327}
328
329	char *
330cgetenv(name)
331	char const *name;
332/* Like getenv(), but yield a copy; getenv() can overwrite old results. */
333{
334	register char *p;
335
336	return (p=getenv(name)) ? str_save(p) : p;
337}
338
339	char const *
340getusername(suspicious)
341	int suspicious;
342/* Get the caller's login name.  Trust only getwpuid if SUSPICIOUS.  */
343{
344	static char *name;
345
346	if (!name) {
347		if (
348		    /* Prefer getenv() unless suspicious; it's much faster.  */
349#		    if getlogin_is_secure
350			    (suspicious
351			    || (
352				    !(name = cgetenv("LOGNAME"))
353				&&  !(name = cgetenv("USER"))
354			    ))
355			&&  !(name = getlogin())
356#		    else
357			suspicious
358			|| (
359				!(name = cgetenv("LOGNAME"))
360			    &&  !(name = cgetenv("USER"))
361			    &&  !(name = getlogin())
362			)
363#		    endif
364		) {
365#if has_getuid && has_getpwuid
366			struct passwd const *pw = getpwuid(ruid());
367			if (!pw)
368			    faterror("no password entry for userid %lu",
369				     (unsigned long)ruid()
370			    );
371			name = pw->pw_name;
372#else
373#if has_setuid
374			faterror("setuid not supported");
375#else
376			faterror("Who are you?  Please setenv LOGNAME.");
377#endif
378#endif
379		}
380		checksid(name);
381	}
382	return name;
383}
384
385
386
387
388#if has_signal
389
390/*
391 *	 Signal handling
392 *
393 * Standard C places too many restrictions on signal handlers.
394 * We obey as many of them as we can.
395 * Posix places fewer restrictions, and we are Posix-compatible here.
396 */
397
398static sig_atomic_t volatile heldsignal, holdlevel;
399#ifdef SA_SIGINFO
400	static int unsupported_SA_SIGINFO;
401	static siginfo_t bufsiginfo;
402	static siginfo_t *volatile heldsiginfo;
403#endif
404
405
406#if has_NFS && has_mmap && large_memory && mmap_signal
407    static char const *accessName;
408
409	  void
410    readAccessFilenameBuffer(filename, p)
411	char const *filename;
412	unsigned char const *p;
413    {
414	unsigned char volatile t;
415	accessName = filename;
416	t = *p;
417	accessName = 0;
418    }
419#else
420#   define accessName ((char const *) 0)
421#endif
422
423
424#if !has_psignal
425
426# define psignal my_psignal
427	static void my_psignal P((int,char const*));
428	static void
429my_psignal(sig, s)
430	int sig;
431	char const *s;
432{
433	char const *sname = "Unknown signal";
434#	if has_sys_siglist && defined(NSIG)
435	    if ((unsigned)sig < NSIG)
436		sname = sys_siglist[sig];
437#	else
438	    switch (sig) {
439#	       ifdef SIGHUP
440		case SIGHUP:	sname = "Hangup";  break;
441#	       endif
442#	       ifdef SIGINT
443		case SIGINT:	sname = "Interrupt";  break;
444#	       endif
445#	       ifdef SIGPIPE
446		case SIGPIPE:	sname = "Broken pipe";  break;
447#	       endif
448#	       ifdef SIGQUIT
449		case SIGQUIT:	sname = "Quit";  break;
450#	       endif
451#	       ifdef SIGTERM
452		case SIGTERM:	sname = "Terminated";  break;
453#	       endif
454#	       ifdef SIGXCPU
455		case SIGXCPU:	sname = "Cputime limit exceeded";  break;
456#	       endif
457#	       ifdef SIGXFSZ
458		case SIGXFSZ:	sname = "Filesize limit exceeded";  break;
459#	       endif
460#	      if has_mmap && large_memory
461#	       if defined(SIGBUS) && mmap_signal==SIGBUS
462		case SIGBUS:	sname = "Bus error";  break;
463#	       endif
464#	       if defined(SIGSEGV) && mmap_signal==SIGSEGV
465		case SIGSEGV:	sname = "Segmentation fault";  break;
466#	       endif
467#	      endif
468	    }
469#	endif
470
471	/* Avoid calling sprintf etc., in case they're not reentrant.  */
472	{
473	    char const *p;
474	    char buf[BUFSIZ], *b = buf;
475	    for (p = s;  *p;  *b++ = *p++)
476		continue;
477	    *b++ = ':';
478	    *b++ = ' ';
479	    for (p = sname;  *p;  *b++ = *p++)
480		continue;
481	    *b++ = '\n';
482	    VOID write(STDERR_FILENO, buf, b - buf);
483	}
484}
485#endif
486
487static signal_type catchsig P((int));
488#ifdef SA_SIGINFO
489	static signal_type catchsigaction P((int,siginfo_t*,void*));
490#endif
491
492	static signal_type
493catchsig(s)
494	int s;
495#ifdef SA_SIGINFO
496{
497	catchsigaction(s, (siginfo_t *)0, (void *)0);
498}
499	static signal_type
500catchsigaction(s, i, c)
501	int s;
502	siginfo_t *i;
503	void *c;
504#endif
505{
506#   if sig_zaps_handler
507	/* If a signal arrives before we reset the handler, we lose. */
508	VOID signal(s, SIG_IGN);
509#   endif
510
511#   ifdef SA_SIGINFO
512	if (!unsupported_SA_SIGINFO)
513	    i = 0;
514#   endif
515
516    if (holdlevel) {
517	heldsignal = s;
518#	ifdef SA_SIGINFO
519	    if (i) {
520		bufsiginfo = *i;
521		heldsiginfo = &bufsiginfo;
522	    }
523#	endif
524	return;
525    }
526
527    ignoreints();
528    setrid();
529    if (!quietflag) {
530	/* Avoid calling sprintf etc., in case they're not reentrant.  */
531	char const *p;
532	char buf[BUFSIZ], *b = buf;
533
534	if ( !	(
535#		if has_mmap && large_memory && mmap_signal
536			/* Check whether this signal was planned.  */
537			s == mmap_signal && accessName
538#		else
539			0
540#		endif
541	)) {
542	    char const *nRCS = "\nRCS";
543#	    if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal
544		if (s == mmap_signal  &&  i  &&  i->si_errno) {
545		    errno = i->si_errno;
546		    perror(nRCS++);
547		}
548#	    endif
549#	    if defined(SA_SIGINFO) && has_psiginfo
550		if (i)
551		    psiginfo(i, nRCS);
552		else
553		    psignal(s, nRCS);
554#	    else
555		psignal(s, nRCS);
556#	    endif
557	}
558
559	for (p = "RCS: ";  *p;  *b++ = *p++)
560	    continue;
561#	if has_mmap && large_memory && mmap_signal
562	    if (s == mmap_signal) {
563		p = accessName;
564		if (!p)
565		    p = "Was a file changed by some other process?  ";
566		else {
567		    char const *p1;
568		    for (p1 = p;  *p1;  p1++)
569			continue;
570		    VOID write(STDERR_FILENO, buf, b - buf);
571		    VOID write(STDERR_FILENO, p, p1 - p);
572		    b = buf;
573		    p = ": Permission denied.  ";
574		}
575		while (*p)
576		    *b++ = *p++;
577	    }
578#	endif
579	for (p = "Cleaning up.\n";  *p;  *b++ = *p++)
580	    continue;
581	VOID write(STDERR_FILENO, buf, b - buf);
582    }
583    exiterr();
584}
585
586	void
587ignoreints()
588{
589	++holdlevel;
590}
591
592	void
593restoreints()
594{
595	if (!--holdlevel && heldsignal)
596#	    ifdef SA_SIGINFO
597		VOID catchsigaction(heldsignal, heldsiginfo, (void *)0);
598#	    else
599		VOID catchsig(heldsignal);
600#	    endif
601}
602
603
604static void setup_catchsig P((int const*,int));
605
606#if has_sigaction
607
608	static void check_sig P((int));
609	static void
610  check_sig(r)
611	int r;
612  {
613	if (r != 0)
614		efaterror("signal handling");
615  }
616
617	static void
618  setup_catchsig(sig, sigs)
619	int const *sig;
620	int sigs;
621  {
622	register int i, j;
623	struct sigaction act;
624
625	for (i=sigs; 0<=--i; ) {
626	    check_sig(sigaction(sig[i], (struct sigaction*)0, &act));
627	    if (act.sa_handler != SIG_IGN) {
628		act.sa_handler = catchsig;
629#		ifdef SA_SIGINFO
630		    if (!unsupported_SA_SIGINFO) {
631#			if has_sa_sigaction
632			    act.sa_sigaction = catchsigaction;
633#			else
634			    act.sa_handler = catchsigaction;
635#			endif
636			act.sa_flags |= SA_SIGINFO;
637		    }
638#		endif
639		for (j=sigs; 0<=--j; )
640		    check_sig(sigaddset(&act.sa_mask, sig[j]));
641		if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) {
642#		    if defined(SA_SIGINFO) && defined(ENOTSUP)
643			if (errno == ENOTSUP  &&  !unsupported_SA_SIGINFO) {
644			    /* Turn off use of SA_SIGINFO and try again.  */
645			    unsupported_SA_SIGINFO = 1;
646			    i++;
647			    continue;
648			}
649#		    endif
650		    check_sig(-1);
651		}
652	    }
653	}
654  }
655
656#else
657#if has_sigblock
658
659	static void
660  setup_catchsig(sig, sigs)
661	int const *sig;
662	int sigs;
663  {
664	register int i;
665	int mask;
666
667	mask = 0;
668	for (i=sigs; 0<=--i; )
669		mask |= sigmask(sig[i]);
670	mask = sigblock(mask);
671	for (i=sigs; 0<=--i; )
672		if (
673		    signal(sig[i], catchsig) == SIG_IGN  &&
674		    signal(sig[i], SIG_IGN) != catchsig
675		)
676			faterror("signal catcher failure");
677	VOID sigsetmask(mask);
678  }
679
680#else
681
682	static void
683  setup_catchsig(sig, sigs)
684	int const *sig;
685	int sigs;
686  {
687	register i;
688
689	for (i=sigs; 0<=--i; )
690		if (
691		    signal(sig[i], SIG_IGN) != SIG_IGN  &&
692		    signal(sig[i], catchsig) != SIG_IGN
693		)
694			faterror("signal catcher failure");
695  }
696
697#endif
698#endif
699
700
701static int const regsigs[] = {
702# ifdef SIGHUP
703	SIGHUP,
704# endif
705# ifdef SIGINT
706	SIGINT,
707# endif
708# ifdef SIGPIPE
709	SIGPIPE,
710# endif
711# ifdef SIGQUIT
712	SIGQUIT,
713# endif
714# ifdef SIGTERM
715	SIGTERM,
716# endif
717# ifdef SIGXCPU
718	SIGXCPU,
719# endif
720# ifdef SIGXFSZ
721	SIGXFSZ,
722# endif
723};
724
725	void
726catchints()
727{
728	static int catching_ints;
729	if (!catching_ints) {
730	    catching_ints = true;
731	    setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs)));
732	}
733}
734
735#if has_mmap && large_memory && mmap_signal
736
737    /*
738    * If you mmap an NFS file, and someone on another client removes the last
739    * link to that file, and you later reference an uncached part of that file,
740    * you'll get a SIGBUS or SIGSEGV (depending on the operating system).
741    * Catch the signal and report the problem to the user.
742    * Unfortunately, there's no portable way to differentiate between this
743    * problem and actual bugs in the program.
744    * This NFS problem is rare, thank goodness.
745    *
746    * This can also occur if someone truncates the file, even without NFS.
747    */
748
749    static int const mmapsigs[] = { mmap_signal };
750
751	    void
752    catchmmapints()
753    {
754	static int catching_mmap_ints;
755	if (!catching_mmap_ints) {
756	    catching_mmap_ints = true;
757	    setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs)));
758	}
759    }
760#endif
761
762#endif /* has_signal */
763
764
765	void
766fastcopy(inf,outf)
767	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	char const *ev;
1234	size_t n;
1235
1236	if ((ev = cgetenv("RCSLOCALID")))
1237		setRCSLocalId(ev);
1238
1239	if ((ev = cgetenv("RCSINCEXC")))
1240		setIncExc(ev);
1241
1242	if (!(q = cgetenv("RCSINIT")))
1243		*newargv = argv;
1244	else {
1245		n = argc + 2;
1246		/*
1247		 * Count spaces in RCSINIT to allocate a new arg vector.
1248		 * This is an upper bound, but it's OK even if too large.
1249		 */
1250		for (p = q;  ;  ) {
1251			switch (*p++) {
1252			    default:
1253				continue;
1254
1255			    case ' ':
1256			    case '\b': case '\f': case '\n':
1257			    case '\r': case '\t': case '\v':
1258				n++;
1259				continue;
1260
1261			    case '\0':
1262				break;
1263			}
1264			break;
1265		}
1266		*newargv = pp = tnalloc(char*, n);
1267		*pp++ = *argv++; /* copy program name */
1268		for (p = q;  ;  ) {
1269			for (;;) {
1270				switch (*q) {
1271				    case '\0':
1272					goto copyrest;
1273
1274				    case ' ':
1275				    case '\b': case '\f': case '\n':
1276				    case '\r': case '\t': case '\v':
1277					q++;
1278					continue;
1279				}
1280				break;
1281			}
1282			*pp++ = p;
1283			++argc;
1284			for (;;) {
1285				switch ((*p++ = *q++)) {
1286				    case '\0':
1287					goto copyrest;
1288
1289				    case '\\':
1290					if (!*q)
1291						goto copyrest;
1292					p[-1] = *q++;
1293					continue;
1294
1295				    default:
1296					continue;
1297
1298				    case ' ':
1299				    case '\b': case '\f': case '\n':
1300				    case '\r': case '\t': case '\v':
1301					break;
1302				}
1303				break;
1304			}
1305			p[-1] = '\0';
1306		}
1307	    copyrest:
1308		while ((*pp++ = *argv++))
1309			continue;
1310	}
1311	return argc;
1312}
1313
1314
1315#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
1316
1317#if has_getuid
1318	uid_t ruid() { cacheid(getuid()); }
1319#endif
1320#if has_setuid
1321	uid_t euid() { cacheid(geteuid()); }
1322#endif
1323
1324
1325#if has_setuid
1326
1327/*
1328 * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
1329 * because it lets us switch back and forth between arbitrary users.
1330 * If seteuid() doesn't work, we fall back on setuid(),
1331 * which works if saved setuid is supported,
1332 * unless the real or effective user is root.
1333 * This area is such a mess that we always check switches at runtime.
1334 */
1335
1336	static void
1337#if has_prototypes
1338set_uid_to(uid_t u)
1339#else
1340 set_uid_to(u) uid_t u;
1341#endif
1342/* Become user u.  */
1343{
1344	static int looping;
1345
1346	if (euid() == ruid())
1347		return;
1348#if (has_fork||has_spawn) && DIFF_ABSOLUTE
1349#	if has_setreuid
1350		if (setreuid(u==euid() ? ruid() : euid(), u) != 0)
1351			efaterror("setuid");
1352#	else
1353		if (seteuid(u) != 0)
1354			efaterror("setuid");
1355#	endif
1356#endif
1357	if (geteuid() != u) {
1358		if (looping)
1359			return;
1360		looping = true;
1361		faterror("root setuid not supported" + (u?5:0));
1362	}
1363}
1364
1365static int stick_with_euid;
1366
1367	void
1368/* Ignore all calls to seteid() and setrid().  */
1369nosetid()
1370{
1371	stick_with_euid = true;
1372}
1373
1374	void
1375seteid()
1376/* Become effective user.  */
1377{
1378	if (!stick_with_euid)
1379		set_uid_to(euid());
1380}
1381
1382	void
1383setrid()
1384/* Become real user.  */
1385{
1386	if (!stick_with_euid)
1387		set_uid_to(ruid());
1388}
1389#endif
1390
1391	time_t
1392now()
1393{
1394	static time_t t;
1395	if (!t  &&  time(&t) == -1)
1396		efaterror("time");
1397	return t;
1398}
1399