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