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 * Revision 5.20 1995/06/16 06:19:24 eggert
35 * (catchsig): Remove `return'.
38 * Revision 5.19 1995/06/02 18:19:00 eggert
39 * (catchsigaction): New name for `catchsig', for sa_sigaction signature.
40 * Use nRCS even if !has_psiginfo, to remove unused variable warning.
41 * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction.
42 * Use ENOTSUP only if defined.
44 * Revision 5.18 1995/06/01 16:23:43 eggert
45 * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo,
46 * to determine whether to use SA_SIGINFO feature,
47 * but also check at runtime whether the feature works.
48 * (catchsig): If an mmap_signal occurs, report the affected file name.
49 * (unsupported_SA_SIGINFO, accessName): New variables.
50 * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler.
51 * If SA_SIGINFO fails, fall back on sa_handler method.
53 * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions.
54 * (concatenate): Remove.
56 * (runv): Work around bad_wait_if_SIGCHLD_ignored bug.
57 * Remove reference to OPEN_O_WORK.
59 * Revision 5.17 1994/03/20 04:52:58 eggert
60 * Specify subprocess input via file descriptor, not file name.
61 * Avoid messing with I/O buffers in the child process.
62 * Define dup in terms of F_DUPFD if it exists.
63 * Move setmtime to rcsedit.c. Remove lint.
65 * Revision 5.16 1993/11/09 17:40:15 eggert
66 * -V now prints version on stdout and exits.
68 * Revision 5.15 1993/11/03 17:42:27 eggert
69 * Use psiginfo and setreuid if available. Move date2str to maketime.c.
71 * Revision 5.14 1992/07/28 16:12:44 eggert
72 * Add -V. has_sigaction overrides sig_zaps_handler. Fix -M bug.
73 * Add mmap_signal, which minimizes signal handling for non-mmap hosts.
75 * Revision 5.13 1992/02/17 23:02:28 eggert
76 * Work around NFS mmap SIGBUS problem. Add -T support.
78 * Revision 5.12 1992/01/24 18:44:19 eggert
79 * Work around NFS mmap bug that leads to SIGBUS core dumps. lint -> RCS_lint
81 * Revision 5.11 1992/01/06 02:42:34 eggert
82 * O_BINARY -> OPEN_O_WORK
83 * while (E) ; -> while (E) continue;
85 * Revision 5.10 1991/10/07 17:32:46 eggert
86 * Support piece tables even if !has_mmap.
88 * Revision 5.9 1991/08/19 03:13:55 eggert
89 * Add spawn() support. Explicate assumptions about getting invoker's name.
90 * Standardize user-visible dates. Tune.
92 * Revision 5.8 1991/04/21 11:58:30 eggert
93 * Plug setuid security hole.
95 * Revision 5.6 1991/02/26 17:48:39 eggert
96 * Fix setuid bug. Use fread, fwrite more portably.
97 * Support waitpid. Don't assume -1 is acceptable to W* macros.
98 * strsave -> str_save (DG/UX name clash)
100 * Revision 5.5 1990/12/04 05:18:49 eggert
101 * Don't output a blank line after a signal diagnostic.
102 * Use -I for prompts and -q for diagnostics.
104 * Revision 5.4 1990/11/01 05:03:53 eggert
105 * Remove unneeded setid check. Add awrite(), fremember().
107 * Revision 5.3 1990/10/06 00:16:45 eggert
108 * Don't fread F if feof(F).
110 * Revision 5.2 1990/09/04 08:02:31 eggert
111 * Store fread()'s result in an fread_type object.
113 * Revision 5.1 1990/08/29 07:14:07 eggert
114 * Declare getpwuid() more carefully.
116 * Revision 5.0 1990/08/22 08:13:46 eggert
117 * Add setuid support. Permit multiple locks per user.
118 * Remove compile-time limits; use malloc instead.
119 * Switch to GMT. Permit dates past 1999/12/31.
120 * Add -V. Remove snooping. Ansify and Posixate.
121 * Tune. Some USG hosts define NSIG but not sys_siglist.
122 * Don't run /bin/sh if it's hopeless.
123 * Don't leave garbage behind if the output is an empty pipe.
124 * Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup.
126 * Revision 4.6 89/05/01 15:13:40 narten
127 * changed copyright header to reflect current distribution rules
129 * Revision 4.5 88/11/08 16:01:02 narten
130 * corrected use of varargs routines
132 * Revision 4.4 88/08/09 19:13:24 eggert
133 * Check for memory exhaustion.
134 * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
135 * Use execv(), not system(); yield exit status like diff(1)'s.
137 * Revision 4.3 87/10/18 10:40:22 narten
138 * Updating version numbers. Changes relative to 1.1 actually
141 * Revision 1.3 87/09/24 14:01:01 narten
142 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
145 * Revision 1.2 87/03/27 14:22:43 jenkins
148 * Revision 4.1 83/05/10 15:53:13 wft
149 * Added getcaller() and findlock().
150 * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
151 * (needed for background jobs in older shells). Added restoreints().
152 * Removed printing of full RCS path from logcommand().
154 * Revision 3.8 83/02/15 15:41:49 wft
155 * Added routine fastcopy() to copy remainder of a file in blocks.
157 * Revision 3.7 82/12/24 15:25:19 wft
158 * added catchints(), ignoreints() for catching and ingnoring interrupts;
161 * Revision 3.6 82/12/08 21:52:05 wft
162 * Using DATEFORM to format dates.
164 * Revision 3.5 82/12/04 18:20:49 wft
165 * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
168 * Revision 3.4 82/12/03 17:17:43 wft
169 * Added check to addlock() ensuring only one lock per person.
170 * Addlock also returns a pointer to the lock created. Deleted fancydate().
172 * Revision 3.3 82/11/27 12:24:37 wft
173 * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
174 * Introduced macro SNOOP so that snoop can be placed in directory other than
175 * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
177 * Revision 3.2 82/10/18 21:15:11 wft
178 * added function getfullRCSname().
180 * Revision 3.1 82/10/13 16:17:37 wft
181 * Cleanup message is now suppressed in quiet mode.
189 libId(utilId, "$FreeBSD$")
197 register unsigned char const
198 *p1 = (unsigned char const*)s1,
199 *p2 = (unsigned char const*)s2;
200 register size_t i = n;
202 while (i-- && !(r = (*p1++ - *p2++)))
215 register char *p1 = (char*)s1;
216 register char const *p2 = (char const*)s2;
224 malloc_type lintalloc;
228 * list of blocks allocated with ftestalloc()
229 * These blocks can be freed by ffree when we're done with the current file.
230 * We could put the free block inside struct alloclist, rather than a pointer
231 * to the free block, but that would be less portable.
235 struct alloclist *nextalloc;
237 static struct alloclist *alloced;
240 static malloc_type okalloc P((malloc_type));
246 faterror("out of memory");
253 /* Allocate a block, testing that the allocation succeeded. */
255 return okalloc(malloc(size));
259 testrealloc(ptr, size)
262 /* Reallocate a block, testing that the allocation succeeded. */
264 return okalloc(realloc(ptr, size));
270 /* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */
272 register struct alloclist *q = talloc(struct alloclist);
273 q->nextalloc = alloced;
275 return q->alloc = ptr;
281 /* Allocate a block, putting it in 'alloced' so it can be freed later. */
283 return fremember(testalloc(size));
288 /* Free all blocks allocated with ftestalloc(). */
290 register struct alloclist *p, *q;
291 for (p = alloced; p; p = q) {
301 register char const *f;
302 /* Free the block f, which was allocated by ftestalloc. */
304 register struct alloclist *p, **a = &alloced;
306 while ((p = *a)->alloc != f)
316 /* Save s in permanently allocated storage. */
318 return strcpy(tnalloc(char, strlen(s)+1), s);
324 /* Save s in storage that will be deallocated when we're done with this file. */
326 return strcpy(ftnalloc(char, strlen(s)+1), s);
332 /* Like getenv(), but yield a copy; getenv() can overwrite old results. */
336 return (p=getenv(name)) ? str_save(p) : p;
340 getusername(suspicious)
342 /* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */
348 /* Prefer getenv() unless suspicious; it's much faster. */
349 # if getlogin_is_secure
352 !(name = cgetenv("LOGNAME"))
353 && !(name = cgetenv("USER"))
355 && !(name = getlogin())
359 !(name = cgetenv("LOGNAME"))
360 && !(name = cgetenv("USER"))
361 && !(name = getlogin())
365 #if has_getuid && has_getpwuid
366 struct passwd const *pw = getpwuid(ruid());
368 faterror("no password entry for userid %lu",
369 (unsigned long)ruid()
374 faterror("setuid not supported");
376 faterror("Who are you? Please setenv LOGNAME.");
393 * Standard C places too many restrictions on signal handlers.
394 * We obey as many of them as we can.
395 * Posix places fewer restrictions, and we are Posix-compatible here.
398 static sig_atomic_t volatile heldsignal, holdlevel;
400 static int unsupported_SA_SIGINFO;
401 static siginfo_t bufsiginfo;
402 static siginfo_t *volatile heldsiginfo;
406 #if has_NFS && has_mmap && large_memory && mmap_signal
407 static char const *accessName;
410 readAccessFilenameBuffer(filename, p)
411 char const *filename;
412 unsigned char const *p;
414 unsigned char volatile t;
415 accessName = filename;
420 # define accessName ((char const *) 0)
426 # define psignal my_psignal
427 static void my_psignal P((int,char const*));
433 char const *sname = "Unknown signal";
434 # if has_sys_siglist && defined(NSIG)
435 if ((unsigned)sig < NSIG)
436 sname = sys_siglist[sig];
440 case SIGHUP: sname = "Hangup"; break;
443 case SIGINT: sname = "Interrupt"; break;
446 case SIGPIPE: sname = "Broken pipe"; break;
449 case SIGQUIT: sname = "Quit"; break;
452 case SIGTERM: sname = "Terminated"; break;
455 case SIGXCPU: sname = "Cputime limit exceeded"; break;
458 case SIGXFSZ: sname = "Filesize limit exceeded"; break;
460 # if has_mmap && large_memory
461 # if defined(SIGBUS) && mmap_signal==SIGBUS
462 case SIGBUS: sname = "Bus error"; break;
464 # if defined(SIGSEGV) && mmap_signal==SIGSEGV
465 case SIGSEGV: sname = "Segmentation fault"; break;
471 /* Avoid calling sprintf etc., in case they're not reentrant. */
474 char buf[BUFSIZ], *b = buf;
475 for (p = s; *p; *b++ = *p++)
479 for (p = sname; *p; *b++ = *p++)
482 VOID write(STDERR_FILENO, buf, b - buf);
487 static signal_type catchsig P((int));
489 static signal_type catchsigaction P((int,siginfo_t*,void*));
497 catchsigaction(s, (siginfo_t *)0, (void *)0);
500 catchsigaction(s, i, c)
506 # if sig_zaps_handler
507 /* If a signal arrives before we reset the handler, we lose. */
508 VOID signal(s, SIG_IGN);
512 if (!unsupported_SA_SIGINFO)
521 heldsiginfo = &bufsiginfo;
530 /* Avoid calling sprintf etc., in case they're not reentrant. */
532 char buf[BUFSIZ], *b = buf;
535 # if has_mmap && large_memory && mmap_signal
536 /* Check whether this signal was planned. */
537 s == mmap_signal && accessName
542 char const *nRCS = "\nRCS";
543 # if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal
544 if (s == mmap_signal && i && i->si_errno) {
549 # if defined(SA_SIGINFO) && has_psiginfo
559 for (p = "RCS: "; *p; *b++ = *p++)
561 # if has_mmap && large_memory && mmap_signal
562 if (s == mmap_signal) {
565 p = "Was a file changed by some other process? ";
568 for (p1 = p; *p1; p1++)
570 VOID write(STDERR_FILENO, buf, b - buf);
571 VOID write(STDERR_FILENO, p, p1 - p);
573 p = ": Permission denied. ";
579 for (p = "Cleaning up.\n"; *p; *b++ = *p++)
581 VOID write(STDERR_FILENO, buf, b - buf);
595 if (!--holdlevel && heldsignal)
597 VOID catchsigaction(heldsignal, heldsiginfo, (void *)0);
599 VOID catchsig(heldsignal);
604 static void setup_catchsig P((int const*,int));
608 static void check_sig P((int));
614 efaterror("signal handling");
618 setup_catchsig(sig, sigs)
623 struct sigaction act;
625 for (i=sigs; 0<=--i; ) {
626 check_sig(sigaction(sig[i], (struct sigaction*)0, &act));
627 if (act.sa_handler != SIG_IGN) {
628 act.sa_handler = catchsig;
630 if (!unsupported_SA_SIGINFO) {
631 # if has_sa_sigaction
632 act.sa_sigaction = catchsigaction;
634 act.sa_handler = catchsigaction;
636 act.sa_flags |= SA_SIGINFO;
639 for (j=sigs; 0<=--j; )
640 check_sig(sigaddset(&act.sa_mask, sig[j]));
641 if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) {
642 # if defined(SA_SIGINFO) && defined(ENOTSUP)
643 if (errno == ENOTSUP && !unsupported_SA_SIGINFO) {
644 /* Turn off use of SA_SIGINFO and try again. */
645 unsupported_SA_SIGINFO = 1;
660 setup_catchsig(sig, sigs)
668 for (i=sigs; 0<=--i; )
669 mask |= sigmask(sig[i]);
670 mask = sigblock(mask);
671 for (i=sigs; 0<=--i; )
673 signal(sig[i], catchsig) == SIG_IGN &&
674 signal(sig[i], SIG_IGN) != catchsig
676 faterror("signal catcher failure");
677 VOID sigsetmask(mask);
683 setup_catchsig(sig, sigs)
689 for (i=sigs; 0<=--i; )
691 signal(sig[i], SIG_IGN) != SIG_IGN &&
692 signal(sig[i], catchsig) != SIG_IGN
694 faterror("signal catcher failure");
701 static int const regsigs[] = {
728 static int catching_ints;
729 if (!catching_ints) {
730 catching_ints = true;
731 setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs)));
735 #if has_mmap && large_memory && mmap_signal
738 * If you mmap an NFS file, and someone on another client removes the last
739 * link to that file, and you later reference an uncached part of that file,
740 * you'll get a SIGBUS or SIGSEGV (depending on the operating system).
741 * Catch the signal and report the problem to the user.
742 * Unfortunately, there's no portable way to differentiate between this
743 * problem and actual bugs in the program.
744 * This NFS problem is rare, thank goodness.
746 * This can also occur if someone truncates the file, even without NFS.
749 static int const mmapsigs[] = { mmap_signal };
754 static int catching_mmap_ints;
755 if (!catching_mmap_ints) {
756 catching_mmap_ints = true;
757 setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs)));
762 #endif /* has_signal */
769 /* Function: copies the remainder of file inf to outf.
774 awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
778 awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf);
779 inf->ptr = inf->readlim;
780 if (inf->ptr == inf->lim)
787 register fread_type rcount;
789 /*now read the rest of the file in blocks*/
791 if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) {
795 awrite(buf, (size_t)rcount, outf);
801 /* This does not work in #ifs, but it's good enough for us. */
802 /* Underestimating SSIZE_MAX may slow us down, but it won't break us. */
803 # define SSIZE_MAX ((unsigned)-1 >> 1)
807 awrite(buf, chars, f)
812 /* Posix 1003.1-1990 ssize_t hack */
813 while (SSIZE_MAX < chars) {
814 if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX)
820 if (Fwrite(buf, sizeof(*buf), chars, f) != chars)
824 /* dup a file descriptor; the result must not be stdin, stdout, or stderr. */
825 static int dupSafer P((int));
831 return fcntl(fd, F_DUPFD, STDERR_FILENO + 1);
833 int e, f, i, used = 0;
834 while (STDIN_FILENO <= (f = dup(fd)) && f <= STDERR_FILENO)
837 for (i = STDIN_FILENO; i <= STDERR_FILENO; i++)
845 /* Renumber a file descriptor so that it's not stdin, stdout, or stderr. */
850 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) {
851 int f = dupSafer(fd);
860 /* Like fopen, except the result is never stdin, stdout, or stderr. */
862 fopenSafer(filename, type)
863 char const *filename;
866 FILE *stream = fopen(filename, type);
868 int fd = fileno(stream);
869 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) {
870 int f = dupSafer(fd);
877 if (fclose(stream) != 0) {
883 stream = fdopen(f, type);
892 # define dup(fd) fcntl(fd, F_DUPFD, 0)
896 #if has_fork || has_spawn
898 static int movefd P((int,int));
903 if (old < 0 || old == new)
906 new = fcntl(old, F_DUPFD, new);
908 new = dup2(old, new);
910 return close(old)==0 ? new : -1;
913 static int fdreopen P((int,char const*,int));
915 fdreopen(fd, file, flags)
924 flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
926 open(file, flags, S_IRUSR|S_IWUSR);
927 return movefd(newfd, fd);
931 static void redirect P((int,int));
936 * Move file descriptor OLD to NEW.
937 * If OLD is -1, do nothing.
938 * If OLD is -2, just close NEW.
941 if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0))
942 efaterror("spawn I/O redirection");
947 #else /* !has_fork && !has_spawn */
949 static void bufargcat P((struct buf*,int,char const*));
952 register struct buf *b;
954 register char const *s;
955 /* Append to B a copy of C, plus a quoted copy of S. */
958 register char const *t;
961 for (t=s, sl=0; *t; )
962 sl += 3*(*t++=='\'') + 1;
963 bl = strlen(b->string);
964 bufrealloc(b, bl + sl + 4);
982 #if !has_spawn && has_fork
984 * Output the string S to stderr, without touching any I/O buffers.
985 * This is useful if you are a child process, whose buffers are usually wrong.
986 * Exit immediately if the write does not completely succeed.
988 static void write_stderr P((char const *));
993 size_t slen = strlen(s);
994 if (write(STDERR_FILENO, s, slen) != slen)
1001 * infd, if not -1, is the input file descriptor.
1002 * outname, if nonzero, is the name of the output file.
1003 * args[1..] form the command to be run; args[0] might be modified.
1006 runv(infd, outname, args)
1008 char const *outname, **args;
1012 #if bad_wait_if_SIGCHLD_ignored
1013 static int fixed_SIGCHLD;
1014 if (!fixed_SIGCHLD) {
1015 fixed_SIGCHLD = true;
1017 # define SIGCHLD SIGCLD
1019 VOID signal(SIGCHLD, SIG_DFL);
1031 if (infd != -1 && infd != STDIN_FILENO) {
1032 if ((in = dup(STDIN_FILENO)) < 0) {
1034 efaterror("spawn input setup");
1038 if (close(STDIN_FILENO) != 0)
1039 efaterror("spawn input close");
1044 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO
1046 dup2(infd, STDIN_FILENO) != STDIN_FILENO
1049 efaterror("spawn input redirection");
1054 if ((out = dup(STDOUT_FILENO)) < 0) {
1056 efaterror("spawn output setup");
1060 STDOUT_FILENO, outname,
1061 O_CREAT | O_TRUNC | O_WRONLY
1066 wstatus = spawn_RCS(0, args[1], (char**)(args + 1));
1068 if (wstatus == -1 && errno == ENOEXEC) {
1069 args[0] = RCS_SHELL;
1070 wstatus = spawnv(0, args[0], (char**)args);
1073 redirect(in, STDIN_FILENO);
1074 redirect(out, STDOUT_FILENO);
1078 if (!(pid = vfork())) {
1079 char const *notfound;
1080 if (infd != -1 && infd != STDIN_FILENO && (
1082 (VOID close(STDIN_FILENO),
1083 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO)
1085 dup2(infd, STDIN_FILENO) != STDIN_FILENO
1088 /* Avoid perror since it may misuse buffers. */
1089 write_stderr(args[1]);
1090 write_stderr(": I/O redirection failed\n");
1091 _exit(EXIT_TROUBLE);
1096 STDOUT_FILENO, outname,
1097 O_CREAT | O_TRUNC | O_WRONLY
1099 /* Avoid perror since it may misuse buffers. */
1100 write_stderr(args[1]);
1102 write_stderr(outname);
1103 write_stderr(": cannot create\n");
1104 _exit(EXIT_TROUBLE);
1106 VOID exec_RCS(args[1], (char**)(args + 1));
1109 if (errno == ENOEXEC) {
1110 args[0] = notfound = RCS_SHELL;
1111 VOID execv(args[0], (char**)args);
1115 /* Avoid perror since it may misuse buffers. */
1116 write_stderr(notfound);
1117 write_stderr(": not found\n");
1118 _exit(EXIT_TROUBLE);
1123 if (waitpid(pid, &wstatus, 0) < 0)
1124 efaterror("waitpid");
1129 if ((w = wait(&wstatus)) < 0)
1135 static struct buf b;
1138 /* Use system(). On many hosts system() discards signals. Yuck! */
1142 bufargcat(&b, ' ', *p);
1143 if (infd != -1 && infd != STDIN_FILENO) {
1144 char redirection[32];
1145 VOID sprintf(redirection, "<&%d", infd);
1146 bufscat(&b, redirection);
1149 bufargcat(&b, '>', outname);
1150 wstatus = system(b.string);
1154 if (!WIFEXITED(wstatus)) {
1155 if (WIFSIGNALED(wstatus)) {
1156 psignal(WTERMSIG(wstatus), args[1]);
1159 faterror("%s failed for unknown reason", args[1]);
1161 return WEXITSTATUS(wstatus);
1167 * infd, if not -1, is the input file descriptor.
1168 * outname, if nonzero, is the name of the output file.
1169 * The remaining arguments specify the command and its arguments.
1173 run(int infd, char const *outname, ...)
1176 run(infd, outname, va_alist)
1178 char const *outname;
1183 char const *rgargs[CARGSMAX];
1185 vararg_start(ap, outname);
1186 for (i = 1; (rgargs[i++] = va_arg(ap, char const*)); )
1188 faterror("too many command arguments");
1190 return runv(infd, outname, rgargs);
1200 static int oldversion;
1202 register char const *s = str + 2;
1205 int v = VERSION_DEFAULT;
1212 v = 10*v + *s++ - '0';
1214 error("%s isn't a number", str);
1215 else if (v < VERSION_min || VERSION_max < v)
1216 error("%s out of range %d..%d",
1217 str, VERSION_min, VERSION_max
1220 RCSversion = VERSION(v);
1222 printf("RCS version %s\n", RCS_version_string);
1228 getRCSINIT(argc, argv, newargv)
1230 char **argv, ***newargv;
1232 register char *p, *q, **pp;
1236 if ((ev = cgetenv("RCSLOCALID")))
1239 if ((ev = cgetenv("RCSINCEXC")))
1242 if (!(q = cgetenv("RCSINIT")))
1247 * Count spaces in RCSINIT to allocate a new arg vector.
1248 * This is an upper bound, but it's OK even if too large.
1256 case '\b': case '\f': case '\n':
1257 case '\r': case '\t': case '\v':
1266 *newargv = pp = tnalloc(char*, n);
1267 *pp++ = *argv++; /* copy program name */
1275 case '\b': case '\f': case '\n':
1276 case '\r': case '\t': case '\v':
1285 switch ((*p++ = *q++)) {
1299 case '\b': case '\f': case '\n':
1300 case '\r': case '\t': case '\v':
1308 while ((*pp++ = *argv++))
1315 #define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
1318 uid_t ruid() { cacheid(getuid()); }
1321 uid_t euid() { cacheid(geteuid()); }
1328 * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
1329 * because it lets us switch back and forth between arbitrary users.
1330 * If seteuid() doesn't work, we fall back on setuid(),
1331 * which works if saved setuid is supported,
1332 * unless the real or effective user is root.
1333 * This area is such a mess that we always check switches at runtime.
1340 set_uid_to(u) uid_t u;
1342 /* Become user u. */
1346 if (euid() == ruid())
1348 #if (has_fork||has_spawn) && DIFF_ABSOLUTE
1350 if (setreuid(u==euid() ? ruid() : euid(), u) != 0)
1351 efaterror("setuid");
1353 if (seteuid(u) != 0)
1354 efaterror("setuid");
1357 if (geteuid() != u) {
1361 faterror("root setuid not supported" + (u?5:0));
1365 static int stick_with_euid;
1368 /* Ignore all calls to seteid() and setrid(). */
1371 stick_with_euid = true;
1376 /* Become effective user. */
1378 if (!stick_with_euid)
1384 /* Become real user. */
1386 if (!stick_with_euid)
1395 if (!t && time(&t) == -1)