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