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