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