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)
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
37 * Revision 1.7 1998/07/27 01:21:18 mycroft
40 * Revision 1.6 1996/10/15 07:00:27 veego
43 * Revision 5.20 1995/06/16 06:19:24 eggert
44 * (catchsig): Remove `return'.
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
150 * Revision 1.3 87/09/24 14:01:01 narten
151 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
154 * Revision 1.2 87/03/27 14:22:43 jenkins
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;
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
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.
198 libId(utilId
, "Id: rcsutil.c,v 5.20 1995/06/16 06:19:24 eggert Exp")
206 register unsigned char const
207 *p1
= (unsigned char const*)s1
,
208 *p2
= (unsigned char const*)s2
;
209 register size_t i
= n
;
211 while (i
-- && !(r
= (*p1
++ - *p2
++)))
224 register char *p1
= (char*)s1
;
225 register char const *p2
= (char const*)s2
;
233 malloc_type lintalloc
;
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.
244 struct alloclist
*nextalloc
;
246 static struct alloclist
*alloced
;
249 static malloc_type okalloc
P((malloc_type
));
255 faterror("out of memory");
262 /* Allocate a block, testing that the allocation succeeded. */
264 return okalloc(malloc(size
));
268 testrealloc(ptr
, size
)
271 /* Reallocate a block, testing that the allocation succeeded. */
273 return okalloc(realloc(ptr
, size
));
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
;
284 return q
->alloc
= ptr
;
290 /* Allocate a block, putting it in 'alloced' so it can be freed later. */
292 return fremember(testalloc(size
));
297 /* Free all blocks allocated with ftestalloc(). */
299 register struct alloclist
*p
, *q
;
300 for (p
= alloced
; p
; p
= q
) {
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
)
325 /* Save s in permanently allocated storage. */
327 return strcpy(tnalloc(char, strlen(s
)+1), 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
);
341 /* Like getenv(), but yield a copy; getenv() can overwrite old results. */
345 return (p
=getenv(name
)) ? str_save(p
) : p
;
349 getusername(suspicious
)
351 /* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */
357 /* Prefer getenv() unless suspicious; it's much faster. */
358 # if getlogin_is_secure
361 !(name
= cgetenv("LOGNAME"))
362 && !(name
= cgetenv("USER"))
364 && !(name
= getlogin())
368 !(name
= cgetenv("LOGNAME"))
369 && !(name
= cgetenv("USER"))
370 && !(name
= getlogin())
374 #if has_getuid && has_getpwuid
375 struct passwd
const *pw
= getpwuid(ruid());
377 faterror("no password entry for userid %lu",
378 (unsigned long)ruid()
380 name
= strdup(pw
->pw_name
);
383 faterror("setuid not supported");
385 faterror("Who are you? Please setenv LOGNAME.");
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
;
409 static int unsupported_SA_SIGINFO
;
410 static siginfo_t bufsiginfo
;
411 static siginfo_t
*volatile heldsiginfo
;
415 #if has_NFS && has_mmap && large_memory && mmap_signal
416 static char const *accessName
;
419 readAccessFilenameBuffer(filename
, p
)
420 char const *filename
;
421 unsigned char const *p
;
423 unsigned char volatile t
;
424 accessName
= filename
;
429 # define accessName ((char const *) 0)
435 # define psignal my_psignal
436 static void my_psignal
P((int,char const*));
442 char const *sname
= "Unknown signal";
443 # if has_sys_siglist && defined(NSIG)
444 if ((unsigned)sig
< NSIG
)
445 sname
= sys_siglist
[sig
];
449 case SIGHUP
: sname
= "Hangup"; break;
452 case SIGINT
: sname
= "Interrupt"; break;
455 case SIGPIPE
: sname
= "Broken pipe"; break;
458 case SIGQUIT
: sname
= "Quit"; break;
461 case SIGTERM
: sname
= "Terminated"; break;
464 case SIGXCPU
: sname
= "Cputime limit exceeded"; break;
467 case SIGXFSZ
: sname
= "Filesize limit exceeded"; break;
469 # if has_mmap && large_memory
470 # if defined(SIGBUS) && mmap_signal==SIGBUS
471 case SIGBUS
: sname
= "Bus error"; break;
473 # if defined(SIGSEGV) && mmap_signal==SIGSEGV
474 case SIGSEGV
: sname
= "Segmentation fault"; break;
480 /* Avoid calling sprintf etc., in case they're not reentrant. */
483 char buf
[BUFSIZ
], *b
= buf
;
484 for (p
= s
; *p
; *b
++ = *p
++)
488 for (p
= sname
; *p
; *b
++ = *p
++)
491 VOID
write(STDERR_FILENO
, buf
, b
- buf
);
496 static signal_type catchsig
P((int));
498 static signal_type catchsigaction
P((int,siginfo_t
*,void*));
506 catchsigaction(s
, (siginfo_t
*)0, (void *)0);
509 catchsigaction(s
, i
, c
)
515 # if sig_zaps_handler
516 /* If a signal arrives before we reset the handler, we lose. */
517 VOID
signal(s
, SIG_IGN
);
521 if (!unsupported_SA_SIGINFO
)
530 heldsiginfo
= &bufsiginfo
;
539 /* Avoid calling sprintf etc., in case they're not reentrant. */
541 char buf
[BUFSIZ
], *b
= buf
;
544 # if has_mmap && large_memory && mmap_signal
545 /* Check whether this signal was planned. */
546 s
== mmap_signal
&& accessName
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
) {
558 # if defined(SA_SIGINFO) && has_psiginfo
568 for (p
= "RCS: "; *p
; *b
++ = *p
++)
570 # if has_mmap && large_memory && mmap_signal
571 if (s
== mmap_signal
) {
574 p
= "Was a file changed by some other process? ";
577 for (p1
= p
; *p1
; p1
++)
579 VOID
write(STDERR_FILENO
, buf
, b
- buf
);
580 VOID
write(STDERR_FILENO
, p
, p1
- p
);
582 p
= ": Permission denied. ";
588 for (p
= "Cleaning up.\n"; *p
; *b
++ = *p
++)
590 VOID
write(STDERR_FILENO
, buf
, b
- buf
);
604 if (!--holdlevel
&& heldsignal
)
606 VOID
catchsigaction(heldsignal
, heldsiginfo
, (void *)0);
608 VOID
catchsig(heldsignal
);
613 static void setup_catchsig
P((int const*,int));
617 static void check_sig
P((int));
623 efaterror("signal handling");
627 setup_catchsig(sig
, sigs
)
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
;
639 if (!unsupported_SA_SIGINFO
) {
640 # if has_sa_sigaction
641 act
.sa_sigaction
= catchsigaction
;
643 act
.sa_handler
= catchsigaction
;
645 act
.sa_flags
|= SA_SIGINFO
;
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;
669 setup_catchsig(sig
, sigs
)
677 for (i
=sigs
; 0<=--i
; )
678 mask
|= sigmask(sig
[i
]);
679 mask
= sigblock(mask
);
680 for (i
=sigs
; 0<=--i
; )
682 signal(sig
[i
], catchsig
) == SIG_IGN
&&
683 signal(sig
[i
], SIG_IGN
) != catchsig
685 faterror("signal catcher failure");
686 VOID
sigsetmask(mask
);
692 setup_catchsig(sig
, sigs
)
698 for (i
=sigs
; 0<=--i
; )
700 signal(sig
[i
], SIG_IGN
) != SIG_IGN
&&
701 signal(sig
[i
], catchsig
) != SIG_IGN
703 faterror("signal catcher failure");
710 static int const regsigs
[] = {
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
};
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
)));
771 #endif /* has_signal */
778 /* Function: copies the remainder of file inf to outf.
783 awrite((char const*)inf
->ptr
, (size_t)(inf
->lim
- inf
->ptr
), outf
);
787 awrite((char const*)inf
->ptr
, (size_t)(inf
->readlim
- inf
->ptr
), outf
);
788 inf
->ptr
= inf
->readlim
;
789 if (inf
->ptr
== inf
->lim
)
796 register fread_type rcount
;
798 /*now read the rest of the file in blocks*/
800 if (!(rcount
= Fread(buf
,sizeof(*buf
),sizeof(buf
),inf
))) {
804 awrite(buf
, (size_t)rcount
, outf
);
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)
816 awrite(buf
, chars
, f
)
824 /* Posix 1003.1-1990 ssize_t hack */
825 while (SSIZE_MAX
< chars
) {
826 if (Fwrite(buf
, sizeof(*buf
), SSIZE_MAX
, f
) != SSIZE_MAX
)
832 if (Fwrite(buf
, sizeof(*buf
), chars
, f
) != chars
)
836 /* dup a file descriptor; the result must not be stdin, stdout, or stderr. */
837 static int dupSafer
P((int));
843 return fcntl(fd
, F_DUPFD
, STDERR_FILENO
+ 1);
845 int e
, f
, i
, used
= 0;
846 while (STDIN_FILENO
<= (f
= dup(fd
)) && f
<= STDERR_FILENO
)
849 for (i
= STDIN_FILENO
; i
<= STDERR_FILENO
; i
++)
857 /* Renumber a file descriptor so that it's not stdin, stdout, or stderr. */
862 if (STDIN_FILENO
<= fd
&& fd
<= STDERR_FILENO
) {
863 int f
= dupSafer(fd
);
872 /* Like fopen, except the result is never stdin, stdout, or stderr. */
874 fopenSafer(filename
, type
)
875 char const *filename
;
878 FILE *stream
= fopen(filename
, type
);
880 int fd
= fileno(stream
);
881 if (STDIN_FILENO
<= fd
&& fd
<= STDERR_FILENO
) {
882 int f
= dupSafer(fd
);
889 if (fclose(stream
) != 0) {
895 stream
= fdopen(f
, type
);
904 # define dup(fd) fcntl(fd, F_DUPFD, 0)
908 #if has_fork || has_spawn
910 static int movefd
P((int,int));
915 if (old
< 0 || old
== new)
918 new = fcntl(old
, F_DUPFD
, new);
920 new = dup2(old
, new);
922 return close(old
)==0 ? new : -1;
925 static int fdreopen
P((int,char const*,int));
927 fdreopen(fd
, file
, flags
)
936 flags
&O_CREAT
? creat(file
, S_IRUSR
|S_IWUSR
) :
938 open(file
, flags
, S_IRUSR
|S_IWUSR
);
939 return movefd(newfd
, fd
);
943 static void redirect
P((int,int));
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");
959 #else /* !has_fork && !has_spawn */
961 static void bufargcat
P((struct buf
*,int,char const*));
964 register struct buf
*b
;
966 register char const *s
;
967 /* Append to B a copy of C, plus a quoted copy of S. */
970 register char const *t
;
973 for (t
=s
, sl
=0; *t
; )
974 sl
+= 3*(*t
++=='\'') + 1;
975 bl
= strlen(b
->string
);
976 bufrealloc(b
, bl
+ sl
+ 4);
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 *));
1005 size_t slen
= strlen(s
);
1006 if (write(STDERR_FILENO
, s
, slen
) != slen
)
1007 _exit(EXIT_TROUBLE
);
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
)
1020 char const *outname
, **args
;
1024 #if bad_wait_if_SIGCHLD_ignored
1025 static int fixed_SIGCHLD
;
1026 if (!fixed_SIGCHLD
) {
1027 fixed_SIGCHLD
= true;
1029 # define SIGCHLD SIGCLD
1031 VOID
signal(SIGCHLD
, SIG_DFL
);
1043 if (infd
!= -1 && infd
!= STDIN_FILENO
) {
1044 if ((in
= dup(STDIN_FILENO
)) < 0) {
1046 efaterror("spawn input setup");
1050 if (close(STDIN_FILENO
) != 0)
1051 efaterror("spawn input close");
1056 fcntl(infd
, F_DUPFD
, STDIN_FILENO
) != STDIN_FILENO
1058 dup2(infd
, STDIN_FILENO
) != STDIN_FILENO
1061 efaterror("spawn input redirection");
1066 if ((out
= dup(STDOUT_FILENO
)) < 0) {
1068 efaterror("spawn output setup");
1072 STDOUT_FILENO
, outname
,
1073 O_CREAT
| O_TRUNC
| O_WRONLY
1078 wstatus
= spawn_RCS(0, args
[1], (char**)(args
+ 1));
1080 if (wstatus
== -1 && errno
== ENOEXEC
) {
1081 args
[0] = RCS_SHELL
;
1082 wstatus
= spawnv(0, args
[0], (char**)args
);
1085 redirect(in
, STDIN_FILENO
);
1086 redirect(out
, STDOUT_FILENO
);
1090 if (!(pid
= vfork())) {
1091 char const *notfound
;
1092 if (infd
!= -1 && infd
!= STDIN_FILENO
&& (
1094 (VOID
close(STDIN_FILENO
),
1095 fcntl(infd
, F_DUPFD
, STDIN_FILENO
) != STDIN_FILENO
)
1097 dup2(infd
, STDIN_FILENO
) != STDIN_FILENO
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
);
1108 STDOUT_FILENO
, outname
,
1109 O_CREAT
| O_TRUNC
| O_WRONLY
1111 /* Avoid perror since it may misuse buffers. */
1112 write_stderr(args
[1]);
1114 write_stderr(outname
);
1115 write_stderr(": cannot create\n");
1116 _exit(EXIT_TROUBLE
);
1118 VOID
exec_RCS(args
[1], (char**)(args
+ 1));
1121 if (errno
== ENOEXEC
) {
1122 args
[0] = notfound
= RCS_SHELL
;
1123 VOID
execv(args
[0], (char**)args
);
1127 /* Avoid perror since it may misuse buffers. */
1128 write_stderr(notfound
);
1129 write_stderr(": not found\n");
1130 _exit(EXIT_TROUBLE
);
1135 if (waitpid(pid
, &wstatus
, 0) < 0)
1136 efaterror("waitpid");
1141 if ((w
= wait(&wstatus
)) < 0)
1147 static struct buf b
;
1150 /* Use system(). On many hosts system() discards signals. Yuck! */
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
);
1161 bufargcat(&b
, '>', outname
);
1162 wstatus
= system(b
.string
);
1166 if (!WIFEXITED(wstatus
)) {
1167 if (WIFSIGNALED(wstatus
)) {
1168 psignal(WTERMSIG(wstatus
), args
[1]);
1171 faterror("%s failed for unknown reason", args
[1]);
1173 return WEXITSTATUS(wstatus
);
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.
1185 run(int infd
, char const *outname
, ...)
1188 run(infd
, outname
, va_alist
)
1190 char const *outname
;
1195 char const *rgargs
[CARGSMAX
];
1197 vararg_start(ap
, outname
);
1198 for (i
= 1; (rgargs
[i
++] = va_arg(ap
, char const*)); )
1200 faterror("too many command arguments");
1202 return runv(infd
, outname
, rgargs
);
1212 static int oldversion
;
1214 register char const *s
= str
+ 2;
1217 int v
= VERSION_DEFAULT
;
1224 v
= 10*v
+ *s
++ - '0';
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
);
1234 printf("RCS version %s\n", RCS_version_string
);
1240 getRCSINIT(argc
, argv
, newargv
)
1242 char **argv
, ***newargv
;
1244 register char *p
, *q
, **pp
;
1247 if (!(q
= cgetenv("RCSINIT")))
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.
1261 case '\b': case '\f': case '\n':
1262 case '\r': case '\t': case '\v':
1271 *newargv
= pp
= tnalloc(char*, n
);
1272 *pp
++ = *argv
++; /* copy program name */
1280 case '\b': case '\f': case '\n':
1281 case '\r': case '\t': case '\v':
1290 switch ((*p
++ = *q
++)) {
1304 case '\b': case '\f': case '\n':
1305 case '\r': case '\t': case '\v':
1313 while ((*pp
++ = *argv
++))
1320 #define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
1323 uid_t
ruid() { cacheid(getuid()); }
1326 uid_t
euid() { cacheid(geteuid()); }
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.
1345 set_uid_to(u
) uid_t u
;
1347 /* Become user u. */
1351 if (euid() == ruid())
1353 #if (has_fork||has_spawn) && DIFF_ABSOLUTE
1355 if (setreuid(u
==euid() ? ruid() : euid(), u
) != 0)
1356 efaterror("setuid");
1358 if (seteuid(u
) != 0)
1359 efaterror("setuid");
1362 if (geteuid() != u
) {
1366 faterror("root setuid not supported" + (u
?5:0));
1370 static int stick_with_euid
;
1373 /* Ignore all calls to seteid() and setrid(). */
1376 stick_with_euid
= true;
1381 /* Become effective user. */
1383 if (!stick_with_euid
)
1389 /* Become real user. */
1391 if (!stick_with_euid
)
1400 if (!t
&& time(&t
) == -1)