xref: /dragonfly/gnu/usr.bin/rcs/lib/rcsutil.c (revision 984263bc)
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  * 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 
189 libId(utilId, "$FreeBSD: src/gnu/usr.bin/rcs/lib/rcsutil.c,v 1.8 1999/08/27 23:36:49 peter Exp $")
190 
191 #if !has_memcmp
192 	int
193 memcmp(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 *
210 memcpy(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  */
233 struct alloclist {
234 	malloc_type alloc;
235 	struct alloclist *nextalloc;
236 };
237 static struct alloclist *alloced;
238 
239 
240 	static malloc_type okalloc P((malloc_type));
241 	static malloc_type
242 okalloc(p)
243 	malloc_type p;
244 {
245 	if (!p)
246 		faterror("out of memory");
247 	return p;
248 }
249 
250 	malloc_type
251 testalloc(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
259 testrealloc(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
268 fremember(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
279 ftestalloc(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
287 ffree()
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
300 ffree1(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 *
314 str_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 *
322 fstr_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 *
330 cgetenv(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 *
340 getusername(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 
398 static 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
429 my_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 
487 static 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
493 catchsig(s)
494 	int s;
495 #ifdef SA_SIGINFO
496 {
497 	catchsigaction(s, (siginfo_t *)0, (void *)0);
498 }
499 	static signal_type
500 catchsigaction(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
587 ignoreints()
588 {
589 	++holdlevel;
590 }
591 
592 	void
593 restoreints()
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 
604 static 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 
701 static 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
726 catchints()
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
766 fastcopy(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
807 awrite(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
827 dupSafer(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
847 fdSafer(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 *
862 fopenSafer(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
900 movefd(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
915 fdreopen(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
933 redirect(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
951 bufargcat(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 */
988 static void write_stderr P((char const *));
989 	static void
990 write_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
1006 runv(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
1173 run(int infd, char const *outname, ...)
1174 #else
1175 	/*VARARGS2*/
1176 run(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 
1194 int RCSversion;
1195 
1196 	void
1197 setRCSversion(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
1228 getRCSINIT(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
1338 set_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 
1365 static int stick_with_euid;
1366 
1367 	void
1368 /* Ignore all calls to seteid() and setrid().  */
1369 nosetid()
1370 {
1371 	stick_with_euid = true;
1372 }
1373 
1374 	void
1375 seteid()
1376 /* Become effective user.  */
1377 {
1378 	if (!stick_with_euid)
1379 		set_uid_to(euid());
1380 }
1381 
1382 	void
1383 setrid()
1384 /* Become real user.  */
1385 {
1386 	if (!stick_with_euid)
1387 		set_uid_to(ruid());
1388 }
1389 #endif
1390 
1391 	time_t
1392 now()
1393 {
1394 	static time_t t;
1395 	if (!t  &&  time(&t) == -1)
1396 		efaterror("time");
1397 	return t;
1398 }
1399