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