1 /* $MirOS: src/gnu/usr.bin/rcs/src/rcsutil.c,v 1.3 2005/03/15 20:15:15 tg Exp $ */
2 
3 /* RCS utility functions */
4 
5 /* Copyright 1982, 1988, 1989 Walter Tichy
6    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
7    Distributed under license by the Free Software Foundation, Inc.
8 
9 This file is part of RCS.
10 
11 RCS is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2, or (at your option)
14 any later version.
15 
16 RCS is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 GNU General Public License for more details.
20 
21 You should have received a copy of the GNU General Public License
22 along with RCS; see the file COPYING.
23 If not, write to the Free Software Foundation,
24 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 
26 Report problems and direct all questions to:
27 
28     rcs-bugs@cs.purdue.edu
29 
30 */
31 
32 
33 
34 
35 /*
36  * $Log: rcsutil.c,v $
37  * Revision 5.20  1995/06/16 06:19:24  eggert
38  * (catchsig): Remove `return'.
39  * Update FSF address.
40  *
41  * Revision 5.19  1995/06/02 18:19:00  eggert
42  * (catchsigaction): New name for `catchsig', for sa_sigaction signature.
43  * Use nRCS even if !has_psiginfo, to remove unused variable warning.
44  * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction.
45  * Use ENOTSUP only if defined.
46  *
47  * Revision 5.18  1995/06/01 16:23:43  eggert
48  * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo,
49  * to determine whether to use SA_SIGINFO feature,
50  * but also check at runtime whether the feature works.
51  * (catchsig): If an mmap_signal occurs, report the affected file name.
52  * (unsupported_SA_SIGINFO, accessName): New variables.
53  * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler.
54  * If SA_SIGINFO fails, fall back on sa_handler method.
55  *
56  * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions.
57  * (concatenate): Remove.
58  *
59  * (runv): Work around bad_wait_if_SIGCHLD_ignored bug.
60  * Remove reference to OPEN_O_WORK.
61  *
62  * Revision 5.17  1994/03/20 04:52:58  eggert
63  * Specify subprocess input via file descriptor, not file name.
64  * Avoid messing with I/O buffers in the child process.
65  * Define dup in terms of F_DUPFD if it exists.
66  * Move setmtime to rcsedit.c.  Remove lint.
67  *
68  * Revision 5.16  1993/11/09 17:40:15  eggert
69  * -V now prints version on stdout and exits.
70  *
71  * Revision 5.15  1993/11/03 17:42:27  eggert
72  * Use psiginfo and setreuid if available.  Move date2str to maketime.c.
73  *
74  * Revision 5.14  1992/07/28  16:12:44  eggert
75  * Add -V.  has_sigaction overrides sig_zaps_handler.  Fix -M bug.
76  * Add mmap_signal, which minimizes signal handling for non-mmap hosts.
77  *
78  * Revision 5.13  1992/02/17  23:02:28  eggert
79  * Work around NFS mmap SIGBUS problem.  Add -T support.
80  *
81  * Revision 5.12  1992/01/24  18:44:19  eggert
82  * Work around NFS mmap bug that leads to SIGBUS core dumps.  lint -> RCS_lint
83  *
84  * Revision 5.11  1992/01/06  02:42:34  eggert
85  * O_BINARY -> OPEN_O_WORK
86  * while (E) ; -> while (E) continue;
87  *
88  * Revision 5.10  1991/10/07  17:32:46  eggert
89  * Support piece tables even if !has_mmap.
90  *
91  * Revision 5.9  1991/08/19  03:13:55  eggert
92  * Add spawn() support.  Explicate assumptions about getting invoker's name.
93  * Standardize user-visible dates.  Tune.
94  *
95  * Revision 5.8  1991/04/21  11:58:30  eggert
96  * Plug setuid security hole.
97  *
98  * Revision 5.6  1991/02/26  17:48:39  eggert
99  * Fix setuid bug.  Use fread, fwrite more portably.
100  * Support waitpid.  Don't assume -1 is acceptable to W* macros.
101  * strsave -> str_save (DG/UX name clash)
102  *
103  * Revision 5.5  1990/12/04  05:18:49  eggert
104  * Don't output a blank line after a signal diagnostic.
105  * Use -I for prompts and -q for diagnostics.
106  *
107  * Revision 5.4  1990/11/01  05:03:53  eggert
108  * Remove unneeded setid check.  Add awrite(), fremember().
109  *
110  * Revision 5.3  1990/10/06  00:16:45  eggert
111  * Don't fread F if feof(F).
112  *
113  * Revision 5.2  1990/09/04  08:02:31  eggert
114  * Store fread()'s result in an fread_type object.
115  *
116  * Revision 5.1  1990/08/29  07:14:07  eggert
117  * Declare getpwuid() more carefully.
118  *
119  * Revision 5.0  1990/08/22  08:13:46  eggert
120  * Add setuid support.  Permit multiple locks per user.
121  * Remove compile-time limits; use malloc instead.
122  * Switch to GMT.  Permit dates past 1999/12/31.
123  * Add -V.  Remove snooping.  Ansify and Posixate.
124  * Tune.  Some USG hosts define NSIG but not sys_siglist.
125  * Don't run /bin/sh if it's hopeless.
126  * Don't leave garbage behind if the output is an empty pipe.
127  * Clean up after SIGXCPU or SIGXFSZ.  Print name of signal that caused cleanup.
128  *
129  * Revision 4.6  89/05/01  15:13:40  narten
130  * changed copyright header to reflect current distribution rules
131  *
132  * Revision 4.5  88/11/08  16:01:02  narten
133  * corrected use of varargs routines
134  *
135  * Revision 4.4  88/08/09  19:13:24  eggert
136  * Check for memory exhaustion.
137  * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
138  * Use execv(), not system(); yield exit status like diff(1)'s.
139  *
140  * Revision 4.3  87/10/18  10:40:22  narten
141  * Updating version numbers. Changes relative to 1.1 actually
142  * relative to 4.1
143  *
144  * Revision 1.3  87/09/24  14:01:01  narten
145  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
146  * warnings)
147  *
148  * Revision 1.2  87/03/27  14:22:43  jenkins
149  * Port to suns
150  *
151  * Revision 4.1  83/05/10  15:53:13  wft
152  * Added getcaller() and findlock().
153  * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
154  * (needed for background jobs in older shells). Added restoreints().
155  * Removed printing of full RCS path from logcommand().
156  *
157  * Revision 3.8  83/02/15  15:41:49  wft
158  * Added routine fastcopy() to copy remainder of a file in blocks.
159  *
160  * Revision 3.7  82/12/24  15:25:19  wft
161  * added catchints(), ignoreints() for catching and ingnoring interrupts;
162  * fixed catchsig().
163  *
164  * Revision 3.6  82/12/08  21:52:05  wft
165  * Using DATEFORM to format dates.
166  *
167  * Revision 3.5  82/12/04  18:20:49  wft
168  * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
169  * lockedby-field.
170  *
171  * Revision 3.4  82/12/03  17:17:43  wft
172  * Added check to addlock() ensuring only one lock per person.
173  * Addlock also returns a pointer to the lock created. Deleted fancydate().
174  *
175  * Revision 3.3  82/11/27  12:24:37  wft
176  * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
177  * Introduced macro SNOOP so that snoop can be placed in directory other than
178  * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
179  *
180  * Revision 3.2  82/10/18  21:15:11  wft
181  * added function getfullRCSname().
182  *
183  * Revision 3.1  82/10/13  16:17:37  wft
184  * Cleanup message is now suppressed in quiet mode.
185  */
186 
187 
188 
189 
190 #include "rcsbase.h"
191 
192 __RCSID("$MirOS: src/gnu/usr.bin/rcs/src/rcsutil.c,v 1.3 2005/03/15 20:15:15 tg Exp $");
193 
194 /*
195  * list of blocks allocated with ftestalloc()
196  * These blocks can be freed by ffree when we're done with the current file.
197  * We could put the free block inside struct alloclist, rather than a pointer
198  * to the free block, but that would be less portable.
199  */
200 struct alloclist {
201 	void *alloc;
202 	struct alloclist *nextalloc;
203 };
204 static struct alloclist *alloced;
205 
206 
207 	static void *okalloc(void *);
208 	static void *
okalloc(p)209 okalloc(p)
210 	void *p;
211 {
212 	if (!p)
213 		faterror("out of memory");
214 	return p;
215 }
216 
217 	void *
testalloc(size)218 testalloc(size)
219 	size_t size;
220 /* Allocate a block, testing that the allocation succeeded.  */
221 {
222 	return okalloc(malloc(size));
223 }
224 
225 	void *
testrealloc(ptr,size)226 testrealloc(ptr, size)
227 	void *ptr;
228 	size_t size;
229 /* Reallocate a block, testing that the allocation succeeded.  */
230 {
231 	return okalloc(realloc(ptr, size));
232 }
233 
234 	void *
fremember(ptr)235 fremember(ptr)
236 	void *ptr;
237 /* Remember PTR in 'alloced' so that it can be freed later.  Yield PTR.  */
238 {
239 	register struct alloclist *q = talloc(struct alloclist);
240 	q->nextalloc = alloced;
241 	alloced = q;
242 	return q->alloc = ptr;
243 }
244 
245 	void *
ftestalloc(size)246 ftestalloc(size)
247 	size_t size;
248 /* Allocate a block, putting it in 'alloced' so it can be freed later. */
249 {
250 	return fremember(testalloc(size));
251 }
252 
253 	void
ffree()254 ffree()
255 /* Free all blocks allocated with ftestalloc().  */
256 {
257 	register struct alloclist *p, *q;
258 	for (p = alloced;  p;  p = q) {
259 		q = p->nextalloc;
260 		tfree(p->alloc);
261 		tfree(p);
262 	}
263 	alloced = 0;
264 }
265 
266 	void
ffree1(f)267 ffree1(f)
268 	register char const *f;
269 /* Free the block f, which was allocated by ftestalloc.  */
270 {
271 	register struct alloclist *p, **a = &alloced;
272 
273 	while ((p = *a)->alloc  !=  f)
274 		a = &p->nextalloc;
275 	*a = p->nextalloc;
276 	tfree(p->alloc);
277 	tfree(p);
278 }
279 
280 	char *
str_save(s)281 str_save(s)
282 	char const *s;
283 /* Save s in permanently allocated storage. */
284 {
285 	size_t l = strlen(s)+1;
286 	char *res = tnalloc(char, l);
287 	strlcpy(res, s, l);
288 	return res;
289 }
290 
291 	char *
fstr_save(s)292 fstr_save(s)
293 	char const *s;
294 /* Save s in storage that will be deallocated when we're done with this file. */
295 {
296 	size_t l = strlen(s)+1;
297 	char *res = ftnalloc(char, l);
298 	strlcpy(res, s, l);
299 	return res;
300 }
301 
302 	char *
cgetenv(name)303 cgetenv(name)
304 	char const *name;
305 /* Like getenv(), but yield a copy; getenv() can overwrite old results. */
306 {
307 	register char *p;
308 
309 	return (p=getenv(name)) ? str_save(p) : p;
310 }
311 
312 	char const *
getusername(suspicious)313 getusername(suspicious)
314 	int suspicious;
315 /* Get the caller's login name.  Trust only getwpuid if SUSPICIOUS.  */
316 {
317 	static char *name;
318 
319 	if (!name) {
320 		if (
321 		    /* Prefer getenv() unless suspicious; it's much faster.  */
322 #		    if getlogin_is_secure
323 			    (suspicious
324 			    || (
325 				    !(name = cgetenv("LOGNAME"))
326 				&&  !(name = cgetenv("USER"))
327 			    ))
328 			&&  !(name = getlogin())
329 #		    else
330 			suspicious
331 			|| (
332 				!(name = cgetenv("LOGNAME"))
333 			    &&  !(name = cgetenv("USER"))
334 			    &&  !(name = getlogin())
335 			)
336 #		    endif
337 		) {
338 			struct passwd const *pw = getpwuid(ruid());
339 			if (!pw)
340 			    faterror("no password entry for userid %lu",
341 				     (unsigned long)ruid()
342 			    );
343 			name = pw->pw_name;
344 		}
345 		checksid(name);
346 	}
347 	return name;
348 }
349 
350 
351 
352 
353 /*
354  *	 Signal handling
355  *
356  * Standard C places too many restrictions on signal handlers.
357  * We obey as many of them as we can.
358  * Posix places fewer restrictions, and we are Posix-compatible here.
359  */
360 
361 static sig_atomic_t volatile heldsignal, holdlevel;
362 #ifdef SA_SIGINFO
363 	static int unsupported_SA_SIGINFO;
364 	static siginfo_t bufsiginfo;
365 	static siginfo_t *volatile heldsiginfo;
366 #endif
367 
368 
369     static char const *accessName;
370 
371 	  void
readAccessFilenameBuffer(filename,p)372     readAccessFilenameBuffer(filename, p)
373 	char const *filename;
374 	unsigned char const *p;
375     {
376 	unsigned char volatile t;
377 	accessName = filename;
378 	t = *p;
379 	accessName = 0;
380     }
381 
382 
383 static signal_type catchsig(int);
384 #ifdef SA_SIGINFO
385 	static signal_type catchsigaction(int,siginfo_t*,void*);
386 #endif
387 
388 	static signal_type
catchsig(s)389 catchsig(s)
390 	int s;
391 #ifdef SA_SIGINFO
392 {
393 	catchsigaction(s, (siginfo_t *)0, (void *)0);
394 }
395 	static signal_type
catchsigaction(s,i,c)396 catchsigaction(s, i, c)
397 	int s;
398 	siginfo_t *i;
399 	void *c __attribute__((unused));
400 #endif
401 {
402 #   if sig_zaps_handler
403 	/* If a signal arrives before we reset the handler, we lose. */
404 	signal(s, SIG_IGN);
405 #   endif
406 
407 #   ifdef SA_SIGINFO
408 	if (!unsupported_SA_SIGINFO)
409 	    i = 0;
410 #   endif
411 
412     if (holdlevel) {
413 	heldsignal = s;
414 #	ifdef SA_SIGINFO
415 	    if (i) {
416 		bufsiginfo = *i;
417 		heldsiginfo = &bufsiginfo;
418 	    }
419 #	endif
420 	return;
421     }
422 
423     ignoreints();
424     setrid();
425     if (!quietflag) {
426 	/* Avoid calling sprintf etc., in case they're not reentrant.  */
427 	char const *p;
428 	char buf[BUFSIZ], *b = buf;
429 
430 	if ( !	(
431 			/* Check whether this signal was planned.  */
432 			s == mmap_signal && accessName
433 	)) {
434 	    char const *nRCS = "\nRCS";
435 #	    if defined(SA_SIGINFO) && has_si_errno
436 		if (s == mmap_signal  &&  i  &&  i->si_errno) {
437 		    errno = i->si_errno;
438 		    perror(nRCS++);
439 		}
440 #	    endif
441 		psignal(s, nRCS);
442 	}
443 
444 	for (p = "RCS: ";  *p;  *b++ = *p++)
445 	    continue;
446 	    if (s == mmap_signal) {
447 		p = accessName;
448 		if (!p)
449 		    p = "Was a file changed by some other process?  ";
450 		else {
451 		    char const *p1;
452 		    for (p1 = p;  *p1;  p1++)
453 			continue;
454 		    write(STDERR_FILENO, buf, b - buf);
455 		    write(STDERR_FILENO, p, p1 - p);
456 		    b = buf;
457 		    p = ": Permission denied.  ";
458 		}
459 		while (*p)
460 		    *b++ = *p++;
461 	    }
462 	for (p = "Cleaning up.\n";  *p;  *b++ = *p++)
463 	    continue;
464 	write(STDERR_FILENO, buf, b - buf);
465     }
466     exiterr();
467 }
468 
469 	void
ignoreints()470 ignoreints()
471 {
472 	++holdlevel;
473 }
474 
475 	void
restoreints()476 restoreints()
477 {
478 	if (!--holdlevel && heldsignal)
479 #	    ifdef SA_SIGINFO
480 		catchsigaction(heldsignal, heldsiginfo, (void *)0);
481 #	    else
482 		catchsig(heldsignal);
483 #	    endif
484 }
485 
486 
487 static void setup_catchsig(int const*,int);
488 
489 	static void check_sig(int);
490 	static void
check_sig(r)491   check_sig(r)
492 	int r;
493   {
494 	if (r != 0)
495 		efaterror("signal handling");
496   }
497 
498 	static void
setup_catchsig(sig,sigs)499   setup_catchsig(sig, sigs)
500 	int const *sig;
501 	int sigs;
502   {
503 	register int i, j;
504 	struct sigaction act;
505 
506 	for (i=sigs; 0<=--i; ) {
507 	    check_sig(sigaction(sig[i], (struct sigaction*)0, &act));
508 	    if (act.sa_handler != SIG_IGN) {
509 		act.sa_handler = catchsig;
510 #		ifdef SA_SIGINFO
511 		    if (!unsupported_SA_SIGINFO) {
512 			    act.sa_sigaction = catchsigaction;
513 			act.sa_flags |= SA_SIGINFO;
514 		    }
515 #		endif
516 		for (j=sigs; 0<=--j; )
517 		    check_sig(sigaddset(&act.sa_mask, sig[j]));
518 		if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) {
519 #		    if defined(SA_SIGINFO) && defined(ENOTSUP)
520 			if (errno == ENOTSUP  &&  !unsupported_SA_SIGINFO) {
521 			    /* Turn off use of SA_SIGINFO and try again.  */
522 			    unsupported_SA_SIGINFO = 1;
523 			    i++;
524 			    continue;
525 			}
526 #		    endif
527 		    check_sig(-1);
528 		}
529 	    }
530 	}
531   }
532 
533 static int const regsigs[] = {
534 # ifdef SIGHUP
535 	SIGHUP,
536 # endif
537 # ifdef SIGINT
538 	SIGINT,
539 # endif
540 # ifdef SIGPIPE
541 	SIGPIPE,
542 # endif
543 # ifdef SIGQUIT
544 	SIGQUIT,
545 # endif
546 # ifdef SIGTERM
547 	SIGTERM,
548 # endif
549 # ifdef SIGXCPU
550 	SIGXCPU,
551 # endif
552 # ifdef SIGXFSZ
553 	SIGXFSZ,
554 # endif
555 };
556 
557 	void
catchints()558 catchints()
559 {
560 	static int catching_ints;
561 	if (!catching_ints) {
562 	    catching_ints = true;
563 	    setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs)));
564 	}
565 }
566 
567     /*
568     * If you mmap an NFS file, and someone on another client removes the last
569     * link to that file, and you later reference an uncached part of that file,
570     * you'll get a SIGBUS or SIGSEGV (depending on the operating system).
571     * Catch the signal and report the problem to the user.
572     * Unfortunately, there's no portable way to differentiate between this
573     * problem and actual bugs in the program.
574     * This NFS problem is rare, thank goodness.
575     *
576     * This can also occur if someone truncates the file, even without NFS.
577     */
578 
579     static int const mmapsigs[] = { mmap_signal };
580 
581 	    void
catchmmapints()582     catchmmapints()
583     {
584 	static int catching_mmap_ints;
585 	if (!catching_mmap_ints) {
586 	    catching_mmap_ints = true;
587 	    setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs)));
588 	}
589     }
590 
591 
592 	void
fastcopy(inf,outf)593 fastcopy(inf,outf)
594 	register RILE *inf;
595 	FILE *outf;
596 /* Function: copies the remainder of file inf to outf.
597  */
598 {
599 	    awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
600 	    inf->ptr = inf->lim;
601 }
602 
603 	void
awrite(buf,chars,f)604 awrite(buf, chars, f)
605 	char const *buf;
606 	size_t chars;
607 	FILE *f;
608 {
609 	/* Posix 1003.1-1990 ssize_t hack */
610 	while (SSIZE_MAX < chars) {
611 		if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f)  !=  SSIZE_MAX)
612 			Oerror();
613 		buf += SSIZE_MAX;
614 		chars -= SSIZE_MAX;
615 	}
616 
617 	if (Fwrite(buf, sizeof(*buf), chars, f)  !=  chars)
618 		Oerror();
619 }
620 
621 /* dup a file descriptor; the result must not be stdin, stdout, or stderr.  */
622 	static int dupSafer(int);
623 	static int
dupSafer(fd)624 dupSafer(fd)
625 	int fd;
626 {
627 #	ifdef F_DUPFD
628 	    return fcntl(fd, F_DUPFD, STDERR_FILENO + 1);
629 #	else
630 	    int e, f, i, used = 0;
631 	    while (STDIN_FILENO <= (f = dup(fd))  &&  f <= STDERR_FILENO)
632 		    used |= 1<<f;
633 	    e = errno;
634 	    for (i = STDIN_FILENO;  i <= STDERR_FILENO;  i++)
635 		    if (used & (1<<i))
636 			    close(i);
637 	    errno = e;
638 	    return f;
639 #	endif
640 }
641 
642 /* Renumber a file descriptor so that it's not stdin, stdout, or stderr.  */
643 	int
fdSafer(fd)644 fdSafer(fd)
645 	int fd;
646 {
647 	if (STDIN_FILENO <= fd  &&  fd <= STDERR_FILENO) {
648 		int f = dupSafer(fd);
649 		int e = errno;
650 		close(fd);
651 		errno = e;
652 		fd = f;
653 	}
654 	return fd;
655 }
656 
657 /* Like fopen, except the result is never stdin, stdout, or stderr.  */
658 	FILE *
fopenSafer(filename,type)659 fopenSafer(filename, type)
660 	char const *filename;
661 	char const *type;
662 {
663 	FILE *stream = fopen(filename, type);
664 	if (stream) {
665 		int fd = fileno(stream);
666 		if (STDIN_FILENO <= fd  &&  fd <= STDERR_FILENO) {
667 			int f = dupSafer(fd);
668 			if (f < 0) {
669 				int e = errno;
670 				fclose(stream);
671 				errno = e;
672 				return 0;
673 			}
674 			if (fclose(stream) != 0) {
675 				int e = errno;
676 				close(f);
677 				errno = e;
678 				return 0;
679 			}
680 			stream = fdopen(f, type);
681 		}
682 	}
683 	return stream;
684 }
685 
686 
687 #ifdef F_DUPFD
688 #	undef dup
689 #	define dup(fd) fcntl(fd, F_DUPFD, 0)
690 #endif
691 
692 
693 	static int movefd(int,int);
694 	static int
movefd(old,new)695 movefd(old, new)
696 	int old, new;
697 {
698 	if (old < 0  ||  old == new)
699 		return old;
700 #	ifdef F_DUPFD
701 		new = fcntl(old, F_DUPFD, new);
702 #	else
703 		new = dup2(old, new);
704 #	endif
705 	return close(old)==0 ? new : -1;
706 }
707 
708 	static int fdreopen(int,char const*,int);
709 	static int
fdreopen(fd,file,flags)710 fdreopen(fd, file, flags)
711 	int fd;
712 	char const *file;
713 	int flags;
714 {
715 	int newfd;
716 	close(fd);
717 	newfd =
718 		open(file, flags, S_IRUSR|S_IWUSR);
719 	return movefd(newfd, fd);
720 }
721 
722 /*
723 * Output the string S to stderr, without touching any I/O buffers.
724 * This is useful if you are a child process, whose buffers are usually wrong.
725 * Exit immediately if the write does not completely succeed.
726 */
727 static void write_stderr(char const *);
728 	static void
write_stderr(s)729 write_stderr(s)
730 	char const *s;
731 {
732 	size_t slen = strlen(s);
733 	if ((size_t)write(STDERR_FILENO, s, slen) != slen)
734 		_exit(EXIT_TROUBLE);
735 }
736 
737 /*
738 * Run a command.
739 * infd, if not -1, is the input file descriptor.
740 * outname, if nonzero, is the name of the output file.
741 * args[1..] form the command to be run; args[0] might be modified.
742 */
743 	int
runv(infd,outname,args)744 runv(infd, outname, args)
745 	int infd;
746 	char const *outname, **args;
747 {
748 	int wstatus;
749 
750 	static int fixed_SIGCHLD;
751 	if (!fixed_SIGCHLD) {
752 	    fixed_SIGCHLD = true;
753 	    signal(SIGCHLD, SIG_DFL);
754 	}
755 
756 	oflush();
757 	eflush();
758     {
759 	pid_t pid;
760 	if (!(pid = vfork())) {
761 		char const *notfound;
762 		if (infd != -1  &&  infd != STDIN_FILENO  &&  (
763 #		    ifdef F_DUPFD
764 			(close(STDIN_FILENO),
765 			fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO)
766 #		    else
767 			dup2(infd, STDIN_FILENO) != STDIN_FILENO
768 #		    endif
769 		)) {
770 		    /* Avoid perror since it may misuse buffers.  */
771 		    write_stderr(args[1]);
772 		    write_stderr(": I/O redirection failed\n");
773 		    _exit(EXIT_TROUBLE);
774 		}
775 
776 		if (outname)
777 		    if (fdreopen(
778 			STDOUT_FILENO, outname,
779 			O_CREAT | O_TRUNC | O_WRONLY
780 		    ) < 0) {
781 			/* Avoid perror since it may misuse buffers.  */
782 			write_stderr(args[1]);
783 			write_stderr(": ");
784 			write_stderr(outname);
785 			write_stderr(": cannot create\n");
786 			_exit(EXIT_TROUBLE);
787 		    }
788 		execv(args[1], (char**)(args + 1));
789 		notfound = args[1];
790 		    if (errno == ENOEXEC) {
791 			args[0] = notfound = RCS_SHELL;
792 			execv(args[0], (char**)args);
793 		    }
794 
795 		/* Avoid perror since it may misuse buffers.  */
796 		write_stderr(notfound);
797 		write_stderr(": not found\n");
798 		_exit(EXIT_TROUBLE);
799 	}
800 	if (pid < 0)
801 		efaterror("fork");
802 		if (waitpid(pid, &wstatus, 0) < 0)
803 			efaterror("waitpid");
804     }
805 	if (!WIFEXITED(wstatus)) {
806 		if (WIFSIGNALED(wstatus)) {
807 			psignal(WTERMSIG(wstatus), args[1]);
808 			fatcleanup(1);
809 		}
810 		faterror("%s failed for unknown reason", args[1]);
811 	}
812 	return WEXITSTATUS(wstatus);
813 }
814 
815 #define CARGSMAX 20
816 /*
817 * Run a command.
818 * infd, if not -1, is the input file descriptor.
819 * outname, if nonzero, is the name of the output file.
820 * The remaining arguments specify the command and its arguments.
821 */
822 	int
run(int infd,char const * outname,...)823 run(int infd, char const *outname, ...)
824 {
825 	va_list ap;
826 	char const *rgargs[CARGSMAX];
827 	register int i;
828 	va_start(ap, outname);
829 	for (i = 1;  (rgargs[i++] = va_arg(ap, char const*));  )
830 		if (CARGSMAX <= i)
831 			faterror("too many command arguments");
832 	va_end(ap);
833 	return runv(infd, outname, rgargs);
834 }
835 
836 
837 int RCSversion;
838 
839 	void
setRCSversion(str)840 setRCSversion(str)
841 	char const *str;
842 {
843 	static int oldversion;
844 
845 	register char const *s = str + 2;
846 
847 	if (*s) {
848 		int v = VERSION_DEFAULT;
849 
850 		if (oldversion)
851 			redefined('V');
852 		oldversion = true;
853 		v = 0;
854 		while (isdigit(*s))
855 			v  =  10*v + *s++ - '0';
856 		if (*s)
857 			error("%s isn't a number", str);
858 		else if (v < VERSION_min  ||  VERSION_max < v)
859 			error("%s out of range %d..%d",
860 				str, VERSION_min, VERSION_max
861 			);
862 
863 		RCSversion = VERSION(v);
864 	} else {
865 		printf("RCS version %s\n", RCS_version_string);
866 		exit(0);
867 	}
868 }
869 
870 	int
getRCSINIT(argc,argv,newargv)871 getRCSINIT(argc, argv, newargv)
872 	int argc;
873 	char **argv, ***newargv;
874 {
875 	register char *p, *q, **pp;
876 	size_t n;
877 
878 	if ((q = cgetenv("RCSLOCALID")))
879 		setRCSlocalId(q);
880 
881 	if (!(q = cgetenv("RCSINIT")))
882 		*newargv = argv;
883 	else {
884 		n = argc + 2;
885 		/*
886 		 * Count spaces in RCSINIT to allocate a new arg vector.
887 		 * This is an upper bound, but it's OK even if too large.
888 		 */
889 		for (p = q;  ;  ) {
890 			switch (*p++) {
891 			    default:
892 				continue;
893 
894 			    case ' ':
895 			    case '\b': case '\f': case '\n':
896 			    case '\r': case '\t': case '\v':
897 				n++;
898 				continue;
899 
900 			    case '\0':
901 				break;
902 			}
903 			break;
904 		}
905 		*newargv = pp = tnalloc(char*, n);
906 		*pp++ = *argv++; /* copy program name */
907 		for (p = q;  ;  ) {
908 			for (;;) {
909 				switch (*q) {
910 				    case '\0':
911 					goto copyrest;
912 
913 				    case ' ':
914 				    case '\b': case '\f': case '\n':
915 				    case '\r': case '\t': case '\v':
916 					q++;
917 					continue;
918 				}
919 				break;
920 			}
921 			*pp++ = p;
922 			++argc;
923 			for (;;) {
924 				switch ((*p++ = *q++)) {
925 				    case '\0':
926 					goto copyrest;
927 
928 				    case '\\':
929 					if (!*q)
930 						goto copyrest;
931 					p[-1] = *q++;
932 					continue;
933 
934 				    default:
935 					continue;
936 
937 				    case ' ':
938 				    case '\b': case '\f': case '\n':
939 				    case '\r': case '\t': case '\v':
940 					break;
941 				}
942 				break;
943 			}
944 			p[-1] = '\0';
945 		}
946 	    copyrest:
947 		while ((*pp++ = *argv++))
948 			continue;
949 	}
950 	return argc;
951 }
952 
953 
954 #define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
955 
ruid()956 	uid_t ruid() { cacheid(getuid()); }
euid()957 	uid_t euid() { cacheid(geteuid()); }
958 
959 
960 /*
961  * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
962  * because it lets us switch back and forth between arbitrary users.
963  * If seteuid() doesn't work, we fall back on setuid(),
964  * which works if saved setuid is supported,
965  * unless the real or effective user is root.
966  * This area is such a mess that we always check switches at runtime.
967  */
968 
969 	static void
set_uid_to(uid_t u)970 set_uid_to(uid_t u)
971 /* Become user u.  */
972 {
973 	static int looping;
974 
975 	if (euid() == ruid())
976 		return;
977 	if (seteuid(u) != 0)
978 		efaterror("setuid");
979 	if (geteuid() != u) {
980 		if (looping)
981 			return;
982 		looping = true;
983 		faterror("root setuid not supported" + (u?5:0));
984 	}
985 }
986 
987 static int stick_with_euid;
988 
989 	void
990 /* Ignore all calls to seteid() and setrid().  */
nosetid()991 nosetid()
992 {
993 	stick_with_euid = true;
994 }
995 
996 	void
seteid()997 seteid()
998 /* Become effective user.  */
999 {
1000 	if (!stick_with_euid)
1001 		set_uid_to(euid());
1002 }
1003 
1004 	void
setrid()1005 setrid()
1006 /* Become real user.  */
1007 {
1008 	if (!stick_with_euid)
1009 		set_uid_to(ruid());
1010 }
1011 
1012 	time_t
now()1013 now()
1014 {
1015 	static time_t t;
1016 	if (!t  &&  time(&t) == -1)
1017 		efaterror("time");
1018 	return t;
1019 }
1020