1 /* RCS utility functions */
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.
7 This file is part of RCS.
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)
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.
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.
24 Report problems and direct all questions to:
26 rcs-bugs@cs.purdue.edu
34 * $FreeBSD: src/gnu/usr.bin/rcs/lib/rcsutil.c,v 1.8 1999/08/27 23:36:49 peter Exp $
35 * $DragonFly: src/gnu/usr.bin/rcs/lib/rcsutil.c,v 1.2 2003/06/17 04:25:47 dillon Exp $
37 * Revision 5.20 1995/06/16 06:19:24 eggert
38 * (catchsig): Remove `return'.
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.
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.
56 * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions.
57 * (concatenate): Remove.
59 * (runv): Work around bad_wait_if_SIGCHLD_ignored bug.
60 * Remove reference to OPEN_O_WORK.
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.
68 * Revision 5.16 1993/11/09 17:40:15 eggert
69 * -V now prints version on stdout and exits.
71 * Revision 5.15 1993/11/03 17:42:27 eggert
72 * Use psiginfo and setreuid if available. Move date2str to maketime.c.
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.
78 * Revision 5.13 1992/02/17 23:02:28 eggert
79 * Work around NFS mmap SIGBUS problem. Add -T support.
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
84 * Revision 5.11 1992/01/06 02:42:34 eggert
85 * O_BINARY -> OPEN_O_WORK
86 * while (E) ; -> while (E) continue;
88 * Revision 5.10 1991/10/07 17:32:46 eggert
89 * Support piece tables even if !has_mmap.
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.
95 * Revision 5.8 1991/04/21 11:58:30 eggert
96 * Plug setuid security hole.
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)
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.
107 * Revision 5.4 1990/11/01 05:03:53 eggert
108 * Remove unneeded setid check. Add awrite(), fremember().
110 * Revision 5.3 1990/10/06 00:16:45 eggert
111 * Don't fread F if feof(F).
113 * Revision 5.2 1990/09/04 08:02:31 eggert
114 * Store fread()'s result in an fread_type object.
116 * Revision 5.1 1990/08/29 07:14:07 eggert
117 * Declare getpwuid() more carefully.
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.
129 * Revision 4.6 89/05/01 15:13:40 narten
130 * changed copyright header to reflect current distribution rules
132 * Revision 4.5 88/11/08 16:01:02 narten
133 * corrected use of varargs routines
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.
140 * Revision 4.3 87/10/18 10:40:22 narten
141 * Updating version numbers. Changes relative to 1.1 actually
144 * Revision 1.3 87/09/24 14:01:01 narten
145 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
148 * Revision 1.2 87/03/27 14:22:43 jenkins
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().
157 * Revision 3.8 83/02/15 15:41:49 wft
158 * Added routine fastcopy() to copy remainder of a file in blocks.
160 * Revision 3.7 82/12/24 15:25:19 wft
161 * added catchints(), ignoreints() for catching and ingnoring interrupts;
164 * Revision 3.6 82/12/08 21:52:05 wft
165 * Using DATEFORM to format dates.
167 * Revision 3.5 82/12/04 18:20:49 wft
168 * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
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().
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.
180 * Revision 3.2 82/10/18 21:15:11 wft
181 * added function getfullRCSname().
183 * Revision 3.1 82/10/13 16:17:37 wft
184 * Cleanup message is now suppressed in quiet mode.
192 libId(utilId, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcsutil.c,v 1.2 2003/06/17 04:25:47 dillon Exp $")
200 register unsigned char const
201 *p1 = (unsigned char const*)s1,
202 *p2 = (unsigned char const*)s2;
203 register size_t i = n;
205 while (i-- && !(r = (*p1++ - *p2++)))
218 register char *p1 = (char*)s1;
219 register char const *p2 = (char const*)s2;
227 malloc_type lintalloc;
231 * list of blocks allocated with ftestalloc()
232 * These blocks can be freed by ffree when we're done with the current file.
233 * We could put the free block inside struct alloclist, rather than a pointer
234 * to the free block, but that would be less portable.
238 struct alloclist *nextalloc;
240 static struct alloclist *alloced;
243 static malloc_type okalloc P((malloc_type));
249 faterror("out of memory");
256 /* Allocate a block, testing that the allocation succeeded. */
258 return okalloc(malloc(size));
262 testrealloc(ptr, size)
265 /* Reallocate a block, testing that the allocation succeeded. */
267 return okalloc(realloc(ptr, size));
273 /* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */
275 register struct alloclist *q = talloc(struct alloclist);
276 q->nextalloc = alloced;
278 return q->alloc = ptr;
284 /* Allocate a block, putting it in 'alloced' so it can be freed later. */
286 return fremember(testalloc(size));
291 /* Free all blocks allocated with ftestalloc(). */
293 register struct alloclist *p, *q;
294 for (p = alloced; p; p = q) {
304 register char const *f;
305 /* Free the block f, which was allocated by ftestalloc. */
307 register struct alloclist *p, **a = &alloced;
309 while ((p = *a)->alloc != f)
319 /* Save s in permanently allocated storage. */
321 return strcpy(tnalloc(char, strlen(s)+1), s);
327 /* Save s in storage that will be deallocated when we're done with this file. */
329 return strcpy(ftnalloc(char, strlen(s)+1), s);
335 /* Like getenv(), but yield a copy; getenv() can overwrite old results. */
339 return (p=getenv(name)) ? str_save(p) : p;
343 getusername(suspicious)
345 /* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */
351 /* Prefer getenv() unless suspicious; it's much faster. */
352 # if getlogin_is_secure
355 !(name = cgetenv("LOGNAME"))
356 && !(name = cgetenv("USER"))
358 && !(name = getlogin())
362 !(name = cgetenv("LOGNAME"))
363 && !(name = cgetenv("USER"))
364 && !(name = getlogin())
368 #if has_getuid && has_getpwuid
369 struct passwd const *pw = getpwuid(ruid());
371 faterror("no password entry for userid %lu",
372 (unsigned long)ruid()
377 faterror("setuid not supported");
379 faterror("Who are you? Please setenv LOGNAME.");
396 * Standard C places too many restrictions on signal handlers.
397 * We obey as many of them as we can.
398 * Posix places fewer restrictions, and we are Posix-compatible here.
401 static sig_atomic_t volatile heldsignal, holdlevel;
403 static int unsupported_SA_SIGINFO;
404 static siginfo_t bufsiginfo;
405 static siginfo_t *volatile heldsiginfo;
409 #if has_NFS && has_mmap && large_memory && mmap_signal
410 static char const *accessName;
413 readAccessFilenameBuffer(filename, p)
414 char const *filename;
415 unsigned char const *p;
417 unsigned char volatile t;
418 accessName = filename;
423 # define accessName ((char const *) 0)
429 # define psignal my_psignal
430 static void my_psignal P((int,char const*));
436 char const *sname = "Unknown signal";
437 # if has_sys_siglist && defined(NSIG)
438 if ((unsigned)sig < NSIG)
439 sname = sys_siglist[sig];
443 case SIGHUP: sname = "Hangup"; break;
446 case SIGINT: sname = "Interrupt"; break;
449 case SIGPIPE: sname = "Broken pipe"; break;
452 case SIGQUIT: sname = "Quit"; break;
455 case SIGTERM: sname = "Terminated"; break;
458 case SIGXCPU: sname = "Cputime limit exceeded"; break;
461 case SIGXFSZ: sname = "Filesize limit exceeded"; break;
463 # if has_mmap && large_memory
464 # if defined(SIGBUS) && mmap_signal==SIGBUS
465 case SIGBUS: sname = "Bus error"; break;
467 # if defined(SIGSEGV) && mmap_signal==SIGSEGV
468 case SIGSEGV: sname = "Segmentation fault"; break;
474 /* Avoid calling sprintf etc., in case they're not reentrant. */
477 char buf[BUFSIZ], *b = buf;
478 for (p = s; *p; *b++ = *p++)
482 for (p = sname; *p; *b++ = *p++)
485 VOID write(STDERR_FILENO, buf, b - buf);
490 static signal_type catchsig P((int));
492 static signal_type catchsigaction P((int,siginfo_t*,void*));
500 catchsigaction(s, (siginfo_t *)0, (void *)0);
503 catchsigaction(s, i, c)
509 # if sig_zaps_handler
510 /* If a signal arrives before we reset the handler, we lose. */
511 VOID signal(s, SIG_IGN);
515 if (!unsupported_SA_SIGINFO)
524 heldsiginfo = &bufsiginfo;
533 /* Avoid calling sprintf etc., in case they're not reentrant. */
535 char buf[BUFSIZ], *b = buf;
538 # if has_mmap && large_memory && mmap_signal
539 /* Check whether this signal was planned. */
540 s == mmap_signal && accessName
545 char const *nRCS = "\nRCS";
546 # if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal
547 if (s == mmap_signal && i && i->si_errno) {
552 # if defined(SA_SIGINFO) && has_psiginfo
562 for (p = "RCS: "; *p; *b++ = *p++)
564 # if has_mmap && large_memory && mmap_signal
565 if (s == mmap_signal) {
568 p = "Was a file changed by some other process? ";
571 for (p1 = p; *p1; p1++)
573 VOID write(STDERR_FILENO, buf, b - buf);
574 VOID write(STDERR_FILENO, p, p1 - p);
576 p = ": Permission denied. ";
582 for (p = "Cleaning up.\n"; *p; *b++ = *p++)
584 VOID write(STDERR_FILENO, buf, b - buf);
598 if (!--holdlevel && heldsignal)
600 VOID catchsigaction(heldsignal, heldsiginfo, (void *)0);
602 VOID catchsig(heldsignal);
607 static void setup_catchsig P((int const*,int));
611 static void check_sig P((int));
617 efaterror("signal handling");
621 setup_catchsig(sig, sigs)
626 struct sigaction act;
628 for (i=sigs; 0<=--i; ) {
629 check_sig(sigaction(sig[i], (struct sigaction*)0, &act));
630 if (act.sa_handler != SIG_IGN) {
631 act.sa_handler = catchsig;
633 if (!unsupported_SA_SIGINFO) {
634 # if has_sa_sigaction
635 act.sa_sigaction = catchsigaction;
637 act.sa_handler = catchsigaction;
639 act.sa_flags |= SA_SIGINFO;
642 for (j=sigs; 0<=--j; )
643 check_sig(sigaddset(&act.sa_mask, sig[j]));
644 if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) {
645 # if defined(SA_SIGINFO) && defined(ENOTSUP)
646 if (errno == ENOTSUP && !unsupported_SA_SIGINFO) {
647 /* Turn off use of SA_SIGINFO and try again. */
648 unsupported_SA_SIGINFO = 1;
663 setup_catchsig(sig, sigs)
671 for (i=sigs; 0<=--i; )
672 mask |= sigmask(sig[i]);
673 mask = sigblock(mask);
674 for (i=sigs; 0<=--i; )
676 signal(sig[i], catchsig) == SIG_IGN &&
677 signal(sig[i], SIG_IGN) != catchsig
679 faterror("signal catcher failure");
680 VOID sigsetmask(mask);
686 setup_catchsig(sig, sigs)
692 for (i=sigs; 0<=--i; )
694 signal(sig[i], SIG_IGN) != SIG_IGN &&
695 signal(sig[i], catchsig) != SIG_IGN
697 faterror("signal catcher failure");
704 static int const regsigs[] = {
731 static int catching_ints;
732 if (!catching_ints) {
733 catching_ints = true;
734 setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs)));
738 #if has_mmap && large_memory && mmap_signal
741 * If you mmap an NFS file, and someone on another client removes the last
742 * link to that file, and you later reference an uncached part of that file,
743 * you'll get a SIGBUS or SIGSEGV (depending on the operating system).
744 * Catch the signal and report the problem to the user.
745 * Unfortunately, there's no portable way to differentiate between this
746 * problem and actual bugs in the program.
747 * This NFS problem is rare, thank goodness.
749 * This can also occur if someone truncates the file, even without NFS.
752 static int const mmapsigs[] = { mmap_signal };
757 static int catching_mmap_ints;
758 if (!catching_mmap_ints) {
759 catching_mmap_ints = true;
760 setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs)));
765 #endif /* has_signal */
772 /* Function: copies the remainder of file inf to outf.
777 awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
781 awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf);
782 inf->ptr = inf->readlim;
783 if (inf->ptr == inf->lim)
790 register fread_type rcount;
792 /*now read the rest of the file in blocks*/
794 if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) {
798 awrite(buf, (size_t)rcount, outf);
804 /* This does not work in #ifs, but it's good enough for us. */
805 /* Underestimating SSIZE_MAX may slow us down, but it won't break us. */
806 # define SSIZE_MAX ((unsigned)-1 >> 1)
810 awrite(buf, chars, f)
815 /* Posix 1003.1-1990 ssize_t hack */
816 while (SSIZE_MAX < chars) {
817 if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX)
823 if (Fwrite(buf, sizeof(*buf), chars, f) != chars)
827 /* dup a file descriptor; the result must not be stdin, stdout, or stderr. */
828 static int dupSafer P((int));
834 return fcntl(fd, F_DUPFD, STDERR_FILENO + 1);
836 int e, f, i, used = 0;
837 while (STDIN_FILENO <= (f = dup(fd)) && f <= STDERR_FILENO)
840 for (i = STDIN_FILENO; i <= STDERR_FILENO; i++)
848 /* Renumber a file descriptor so that it's not stdin, stdout, or stderr. */
853 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) {
854 int f = dupSafer(fd);
863 /* Like fopen, except the result is never stdin, stdout, or stderr. */
865 fopenSafer(filename, type)
866 char const *filename;
869 FILE *stream = fopen(filename, type);
871 int fd = fileno(stream);
872 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) {
873 int f = dupSafer(fd);
880 if (fclose(stream) != 0) {
886 stream = fdopen(f, type);
895 # define dup(fd) fcntl(fd, F_DUPFD, 0)
899 #if has_fork || has_spawn
901 static int movefd P((int,int));
906 if (old < 0 || old == new)
909 new = fcntl(old, F_DUPFD, new);
911 new = dup2(old, new);
913 return close(old)==0 ? new : -1;
916 static int fdreopen P((int,char const*,int));
918 fdreopen(fd, file, flags)
927 flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
929 open(file, flags, S_IRUSR|S_IWUSR);
930 return movefd(newfd, fd);
934 static void redirect P((int,int));
939 * Move file descriptor OLD to NEW.
940 * If OLD is -1, do nothing.
941 * If OLD is -2, just close NEW.
944 if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0))
945 efaterror("spawn I/O redirection");
950 #else /* !has_fork && !has_spawn */
952 static void bufargcat P((struct buf*,int,char const*));
955 register struct buf *b;
957 register char const *s;
958 /* Append to B a copy of C, plus a quoted copy of S. */
961 register char const *t;
964 for (t=s, sl=0; *t; )
965 sl += 3*(*t++=='\'') + 1;
966 bl = strlen(b->string);
967 bufrealloc(b, bl + sl + 4);
985 #if !has_spawn && has_fork
987 * Output the string S to stderr, without touching any I/O buffers.
988 * This is useful if you are a child process, whose buffers are usually wrong.
989 * Exit immediately if the write does not completely succeed.
991 static void write_stderr P((char const *));
996 size_t slen = strlen(s);
997 if (write(STDERR_FILENO, s, slen) != slen)
1004 * infd, if not -1, is the input file descriptor.
1005 * outname, if nonzero, is the name of the output file.
1006 * args[1..] form the command to be run; args[0] might be modified.
1009 runv(infd, outname, args)
1011 char const *outname, **args;
1015 #if bad_wait_if_SIGCHLD_ignored
1016 static int fixed_SIGCHLD;
1017 if (!fixed_SIGCHLD) {
1018 fixed_SIGCHLD = true;
1020 # define SIGCHLD SIGCLD
1022 VOID signal(SIGCHLD, SIG_DFL);
1034 if (infd != -1 && infd != STDIN_FILENO) {
1035 if ((in = dup(STDIN_FILENO)) < 0) {
1037 efaterror("spawn input setup");
1041 if (close(STDIN_FILENO) != 0)
1042 efaterror("spawn input close");
1047 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO
1049 dup2(infd, STDIN_FILENO) != STDIN_FILENO
1052 efaterror("spawn input redirection");
1057 if ((out = dup(STDOUT_FILENO)) < 0) {
1059 efaterror("spawn output setup");
1063 STDOUT_FILENO, outname,
1064 O_CREAT | O_TRUNC | O_WRONLY
1069 wstatus = spawn_RCS(0, args[1], (char**)(args + 1));
1071 if (wstatus == -1 && errno == ENOEXEC) {
1072 args[0] = RCS_SHELL;
1073 wstatus = spawnv(0, args[0], (char**)args);
1076 redirect(in, STDIN_FILENO);
1077 redirect(out, STDOUT_FILENO);
1081 if (!(pid = vfork())) {
1082 char const *notfound;
1083 if (infd != -1 && infd != STDIN_FILENO && (
1085 (VOID close(STDIN_FILENO),
1086 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO)
1088 dup2(infd, STDIN_FILENO) != STDIN_FILENO
1091 /* Avoid perror since it may misuse buffers. */
1092 write_stderr(args[1]);
1093 write_stderr(": I/O redirection failed\n");
1094 _exit(EXIT_TROUBLE);
1099 STDOUT_FILENO, outname,
1100 O_CREAT | O_TRUNC | O_WRONLY
1102 /* Avoid perror since it may misuse buffers. */
1103 write_stderr(args[1]);
1105 write_stderr(outname);
1106 write_stderr(": cannot create\n");
1107 _exit(EXIT_TROUBLE);
1109 VOID exec_RCS(args[1], (char**)(args + 1));
1112 if (errno == ENOEXEC) {
1113 args[0] = notfound = RCS_SHELL;
1114 VOID execv(args[0], (char**)args);
1118 /* Avoid perror since it may misuse buffers. */
1119 write_stderr(notfound);
1120 write_stderr(": not found\n");
1121 _exit(EXIT_TROUBLE);
1126 if (waitpid(pid, &wstatus, 0) < 0)
1127 efaterror("waitpid");
1132 if ((w = wait(&wstatus)) < 0)
1138 static struct buf b;
1141 /* Use system(). On many hosts system() discards signals. Yuck! */
1145 bufargcat(&b, ' ', *p);
1146 if (infd != -1 && infd != STDIN_FILENO) {
1147 char redirection[32];
1148 VOID sprintf(redirection, "<&%d", infd);
1149 bufscat(&b, redirection);
1152 bufargcat(&b, '>', outname);
1153 wstatus = system(b.string);
1157 if (!WIFEXITED(wstatus)) {
1158 if (WIFSIGNALED(wstatus)) {
1159 psignal(WTERMSIG(wstatus), args[1]);
1162 faterror("%s failed for unknown reason", args[1]);
1164 return WEXITSTATUS(wstatus);
1170 * infd, if not -1, is the input file descriptor.
1171 * outname, if nonzero, is the name of the output file.
1172 * The remaining arguments specify the command and its arguments.
1176 run(int infd, char const *outname, ...)
1179 run(infd, outname, va_alist)
1181 char const *outname;
1186 char const *rgargs[CARGSMAX];
1188 vararg_start(ap, outname);
1189 for (i = 1; (rgargs[i++] = va_arg(ap, char const*)); )
1191 faterror("too many command arguments");
1193 return runv(infd, outname, rgargs);
1203 static int oldversion;
1205 register char const *s = str + 2;
1208 int v = VERSION_DEFAULT;
1215 v = 10*v + *s++ - '0';
1217 error("%s isn't a number", str);
1218 else if (v < VERSION_min || VERSION_max < v)
1219 error("%s out of range %d..%d",
1220 str, VERSION_min, VERSION_max
1223 RCSversion = VERSION(v);
1225 printf("RCS version %s\n", RCS_version_string);
1231 getRCSINIT(argc, argv, newargv)
1233 char **argv, ***newargv;
1235 register char *p, *q, **pp;
1239 if ((ev = cgetenv("RCSLOCALID")))
1242 if ((ev = cgetenv("RCSINCEXC")))
1245 if (!(q = cgetenv("RCSINIT")))
1250 * Count spaces in RCSINIT to allocate a new arg vector.
1251 * This is an upper bound, but it's OK even if too large.
1259 case '\b': case '\f': case '\n':
1260 case '\r': case '\t': case '\v':
1269 *newargv = pp = tnalloc(char*, n);
1270 *pp++ = *argv++; /* copy program name */
1278 case '\b': case '\f': case '\n':
1279 case '\r': case '\t': case '\v':
1288 switch ((*p++ = *q++)) {
1302 case '\b': case '\f': case '\n':
1303 case '\r': case '\t': case '\v':
1311 while ((*pp++ = *argv++))
1318 #define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
1321 uid_t ruid() { cacheid(getuid()); }
1324 uid_t euid() { cacheid(geteuid()); }
1331 * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
1332 * because it lets us switch back and forth between arbitrary users.
1333 * If seteuid() doesn't work, we fall back on setuid(),
1334 * which works if saved setuid is supported,
1335 * unless the real or effective user is root.
1336 * This area is such a mess that we always check switches at runtime.
1343 set_uid_to(u) uid_t u;
1345 /* Become user u. */
1349 if (euid() == ruid())
1351 #if (has_fork||has_spawn) && DIFF_ABSOLUTE
1353 if (setreuid(u==euid() ? ruid() : euid(), u) != 0)
1354 efaterror("setuid");
1356 if (seteuid(u) != 0)
1357 efaterror("setuid");
1360 if (geteuid() != u) {
1364 faterror("root setuid not supported" + (u?5:0));
1368 static int stick_with_euid;
1371 /* Ignore all calls to seteid() and setrid(). */
1374 stick_with_euid = true;
1379 /* Become effective user. */
1381 if (!stick_with_euid)
1387 /* Become real user. */
1389 if (!stick_with_euid)
1398 if (!t && time(&t) == -1)