1 /* $NetBSD: rcsutil.c,v 1.2 2016/01/14 04:22:39 christos 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 libId(utilId, "Id: rcsutil.c,v 5.20 1995/06/16 06:19:24 eggert Exp ")
193
194 #if !has_memcmp
195 int
196 memcmp(s1, s2, n)
197 void const *s1, *s2;
198 size_t n;
199 {
200 register unsigned char const
201 *p1 = (unsigned char const*)s1,
202 *p2 = (unsigned char const*)s2;
203 register size_t i = n;
204 register int r = 0;
205 while (i-- && !(r = (*p1++ - *p2++)))
206 ;
207 return r;
208 }
209 #endif
210
211 #if !has_memcpy
212 void *
memcpy(s1,s2,n)213 memcpy(s1, s2, n)
214 void *s1;
215 void const *s2;
216 size_t n;
217 {
218 register char *p1 = (char*)s1;
219 register char const *p2 = (char const*)s2;
220 while (n--)
221 *p1++ = *p2++;
222 return s1;
223 }
224 #endif
225
226 #if RCS_lint
227 malloc_type lintalloc;
228 #endif
229
230 /*
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.
235 */
236 struct alloclist {
237 malloc_type alloc;
238 struct alloclist *nextalloc;
239 };
240 static struct alloclist *alloced;
241
242
243 static malloc_type okalloc P((malloc_type));
244 static malloc_type
okalloc(p)245 okalloc(p)
246 malloc_type p;
247 {
248 if (!p)
249 faterror("out of memory");
250 return p;
251 }
252
253 malloc_type
testalloc(size)254 testalloc(size)
255 size_t size;
256 /* Allocate a block, testing that the allocation succeeded. */
257 {
258 return okalloc(malloc(size));
259 }
260
261 malloc_type
testrealloc(ptr,size)262 testrealloc(ptr, size)
263 malloc_type ptr;
264 size_t size;
265 /* Reallocate a block, testing that the allocation succeeded. */
266 {
267 return okalloc(realloc(ptr, size));
268 }
269
270 malloc_type
fremember(ptr)271 fremember(ptr)
272 malloc_type ptr;
273 /* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */
274 {
275 register struct alloclist *q = talloc(struct alloclist);
276 q->nextalloc = alloced;
277 alloced = q;
278 return q->alloc = ptr;
279 }
280
281 malloc_type
ftestalloc(size)282 ftestalloc(size)
283 size_t size;
284 /* Allocate a block, putting it in 'alloced' so it can be freed later. */
285 {
286 return fremember(testalloc(size));
287 }
288
289 void
ffree()290 ffree()
291 /* Free all blocks allocated with ftestalloc(). */
292 {
293 register struct alloclist *p, *q;
294 for (p = alloced; p; p = q) {
295 q = p->nextalloc;
296 tfree(p->alloc);
297 tfree(p);
298 }
299 alloced = 0;
300 }
301
302 void
ffree1(f)303 ffree1(f)
304 register char const *f;
305 /* Free the block f, which was allocated by ftestalloc. */
306 {
307 register struct alloclist *p, **a = &alloced;
308
309 while ((p = *a)->alloc != f)
310 a = &p->nextalloc;
311 *a = p->nextalloc;
312 tfree(p->alloc);
313 tfree(p);
314 }
315
316 char *
str_save(s)317 str_save(s)
318 char const *s;
319 /* Save s in permanently allocated storage. */
320 {
321 return strcpy(tnalloc(char, strlen(s)+1), s);
322 }
323
324 char *
fstr_save(s)325 fstr_save(s)
326 char const *s;
327 /* Save s in storage that will be deallocated when we're done with this file. */
328 {
329 return strcpy(ftnalloc(char, strlen(s)+1), s);
330 }
331
332 char *
cgetenv(name)333 cgetenv(name)
334 char const *name;
335 /* Like getenv(), but yield a copy; getenv() can overwrite old results. */
336 {
337 register char *p;
338
339 return (p=getenv(name)) ? str_save(p) : p;
340 }
341
342 char const *
getusername(suspicious)343 getusername(suspicious)
344 int suspicious;
345 /* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */
346 {
347 static char *name;
348
349 if (!name) {
350 if (
351 /* Prefer getenv() unless suspicious; it's much faster. */
352 # if getlogin_is_secure
353 (suspicious
354 || (
355 !(name = cgetenv("LOGNAME"))
356 && !(name = cgetenv("USER"))
357 ))
358 && !(name = getlogin())
359 # else
360 suspicious
361 || (
362 !(name = cgetenv("LOGNAME"))
363 && !(name = cgetenv("USER"))
364 && !(name = getlogin())
365 )
366 # endif
367 ) {
368 #if has_getuid && has_getpwuid
369 struct passwd const *pw = getpwuid(ruid());
370 if (!pw)
371 faterror("no password entry for userid %lu",
372 (unsigned long)ruid()
373 );
374 name = strdup(pw->pw_name);
375 #else
376 #if has_setuid
377 faterror("setuid not supported");
378 #else
379 faterror("Who are you? Please setenv LOGNAME.");
380 #endif
381 #endif
382 }
383 checksid(name);
384 }
385 return name;
386 }
387
388
389
390
391 #if has_signal
392
393 /*
394 * Signal handling
395 *
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.
399 */
400
401 static sig_atomic_t volatile heldsignal, holdlevel;
402 #ifdef SA_SIGINFO
403 static int unsupported_SA_SIGINFO;
404 static siginfo_t bufsiginfo;
405 static siginfo_t *volatile heldsiginfo;
406 #endif
407
408
409 #if has_NFS && has_mmap && large_memory && mmap_signal
410 static char const *accessName;
411
412 void
readAccessFilenameBuffer(filename,p)413 readAccessFilenameBuffer(filename, p)
414 char const *filename;
415 unsigned char const *p;
416 {
417 unsigned char volatile t;
418 accessName = filename;
419 t = *p;
420 accessName = 0;
421 }
422 #else
423 # define accessName ((char const *) 0)
424 #endif
425
426
427 #if !has_psignal
428
429 # define psignal my_psignal
430 static void my_psignal P((int,char const*));
431 static void
my_psignal(sig,s)432 my_psignal(sig, s)
433 int sig;
434 char const *s;
435 {
436 char const *sname = "Unknown signal";
437 # if has_sys_siglist && defined(NSIG)
438 if ((unsigned)sig < NSIG)
439 sname = sys_siglist[sig];
440 # else
441 switch (sig) {
442 # ifdef SIGHUP
443 case SIGHUP: sname = "Hangup"; break;
444 # endif
445 # ifdef SIGINT
446 case SIGINT: sname = "Interrupt"; break;
447 # endif
448 # ifdef SIGPIPE
449 case SIGPIPE: sname = "Broken pipe"; break;
450 # endif
451 # ifdef SIGQUIT
452 case SIGQUIT: sname = "Quit"; break;
453 # endif
454 # ifdef SIGTERM
455 case SIGTERM: sname = "Terminated"; break;
456 # endif
457 # ifdef SIGXCPU
458 case SIGXCPU: sname = "Cputime limit exceeded"; break;
459 # endif
460 # ifdef SIGXFSZ
461 case SIGXFSZ: sname = "Filesize limit exceeded"; break;
462 # endif
463 # if has_mmap && large_memory
464 # if defined(SIGBUS) && mmap_signal==SIGBUS
465 case SIGBUS: sname = "Bus error"; break;
466 # endif
467 # if defined(SIGSEGV) && mmap_signal==SIGSEGV
468 case SIGSEGV: sname = "Segmentation fault"; break;
469 # endif
470 # endif
471 }
472 # endif
473
474 /* Avoid calling sprintf etc., in case they're not reentrant. */
475 {
476 char const *p;
477 char buf[BUFSIZ], *b = buf;
478 for (p = s; *p; *b++ = *p++)
479 continue;
480 *b++ = ':';
481 *b++ = ' ';
482 for (p = sname; *p; *b++ = *p++)
483 continue;
484 *b++ = '\n';
485 VOID write(STDERR_FILENO, buf, b - buf);
486 }
487 }
488 #endif
489
490 static signal_type catchsig P((int));
491 #ifdef SA_SIGINFO
492 static signal_type catchsigaction P((int,siginfo_t*,void*));
493 #endif
494
495 static signal_type
catchsig(s)496 catchsig(s)
497 int s;
498 #ifdef SA_SIGINFO
499 {
500 catchsigaction(s, (siginfo_t *)0, (void *)0);
501 }
502 static signal_type
catchsigaction(s,i,c)503 catchsigaction(s, i, c)
504 int s;
505 siginfo_t *i;
506 void *c;
507 #endif
508 {
509 # if sig_zaps_handler
510 /* If a signal arrives before we reset the handler, we lose. */
511 VOID signal(s, SIG_IGN);
512 # endif
513
514 # ifdef SA_SIGINFO
515 if (!unsupported_SA_SIGINFO)
516 i = 0;
517 # endif
518
519 if (holdlevel) {
520 heldsignal = s;
521 # ifdef SA_SIGINFO
522 if (i) {
523 bufsiginfo = *i;
524 heldsiginfo = &bufsiginfo;
525 }
526 # endif
527 return;
528 }
529
530 ignoreints();
531 setrid();
532 if (!quietflag) {
533 /* Avoid calling sprintf etc., in case they're not reentrant. */
534 char const *p;
535 char buf[BUFSIZ], *b = buf;
536
537 if ( ! (
538 # if has_mmap && large_memory && mmap_signal
539 /* Check whether this signal was planned. */
540 s == mmap_signal && accessName
541 # else
542 0
543 # endif
544 )) {
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) {
548 errno = i->si_errno;
549 perror(nRCS++);
550 }
551 # endif
552 # if defined(SA_SIGINFO) && has_psiginfo
553 if (i)
554 psiginfo(i, nRCS);
555 else
556 psignal(s, nRCS);
557 # else
558 psignal(s, nRCS);
559 # endif
560 }
561
562 for (p = "RCS: "; *p; *b++ = *p++)
563 continue;
564 # if has_mmap && large_memory && mmap_signal
565 if (s == mmap_signal) {
566 p = accessName;
567 if (!p)
568 p = "Was a file changed by some other process? ";
569 else {
570 char const *p1;
571 for (p1 = p; *p1; p1++)
572 continue;
573 VOID write(STDERR_FILENO, buf, b - buf);
574 VOID write(STDERR_FILENO, p, p1 - p);
575 b = buf;
576 p = ": Permission denied. ";
577 }
578 while (*p)
579 *b++ = *p++;
580 }
581 # endif
582 for (p = "Cleaning up.\n"; *p; *b++ = *p++)
583 continue;
584 VOID write(STDERR_FILENO, buf, b - buf);
585 }
586 exiterr();
587 }
588
589 void
ignoreints()590 ignoreints()
591 {
592 ++holdlevel;
593 }
594
595 void
restoreints()596 restoreints()
597 {
598 if (!--holdlevel && heldsignal)
599 # ifdef SA_SIGINFO
600 VOID catchsigaction(heldsignal, heldsiginfo, (void *)0);
601 # else
602 VOID catchsig(heldsignal);
603 # endif
604 }
605
606
607 static void setup_catchsig P((int const*,int));
608
609 #if has_sigaction
610
611 static void check_sig P((int));
612 static void
check_sig(r)613 check_sig(r)
614 int r;
615 {
616 if (r != 0)
617 efaterror("signal handling");
618 }
619
620 static void
setup_catchsig(sig,sigs)621 setup_catchsig(sig, sigs)
622 int const *sig;
623 int sigs;
624 {
625 register int i, j;
626 struct sigaction act;
627
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;
632 # ifdef SA_SIGINFO
633 if (!unsupported_SA_SIGINFO) {
634 # if has_sa_sigaction
635 act.sa_sigaction = catchsigaction;
636 # else
637 act.sa_handler = catchsigaction;
638 # endif
639 act.sa_flags |= SA_SIGINFO;
640 }
641 # endif
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;
649 i++;
650 continue;
651 }
652 # endif
653 check_sig(-1);
654 }
655 }
656 }
657 }
658
659 #else
660 #if has_sigblock
661
662 static void
setup_catchsig(sig,sigs)663 setup_catchsig(sig, sigs)
664 int const *sig;
665 int sigs;
666 {
667 register int i;
668 int mask;
669
670 mask = 0;
671 for (i=sigs; 0<=--i; )
672 mask |= sigmask(sig[i]);
673 mask = sigblock(mask);
674 for (i=sigs; 0<=--i; )
675 if (
676 signal(sig[i], catchsig) == SIG_IGN &&
677 signal(sig[i], SIG_IGN) != catchsig
678 )
679 faterror("signal catcher failure");
680 VOID sigsetmask(mask);
681 }
682
683 #else
684
685 static void
setup_catchsig(sig,sigs)686 setup_catchsig(sig, sigs)
687 int const *sig;
688 int sigs;
689 {
690 register i;
691
692 for (i=sigs; 0<=--i; )
693 if (
694 signal(sig[i], SIG_IGN) != SIG_IGN &&
695 signal(sig[i], catchsig) != SIG_IGN
696 )
697 faterror("signal catcher failure");
698 }
699
700 #endif
701 #endif
702
703
704 static int const regsigs[] = {
705 # ifdef SIGHUP
706 SIGHUP,
707 # endif
708 # ifdef SIGINT
709 SIGINT,
710 # endif
711 # ifdef SIGPIPE
712 SIGPIPE,
713 # endif
714 # ifdef SIGQUIT
715 SIGQUIT,
716 # endif
717 # ifdef SIGTERM
718 SIGTERM,
719 # endif
720 # ifdef SIGXCPU
721 SIGXCPU,
722 # endif
723 # ifdef SIGXFSZ
724 SIGXFSZ,
725 # endif
726 };
727
728 void
catchints()729 catchints()
730 {
731 static int catching_ints;
732 if (!catching_ints) {
733 catching_ints = true;
734 setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs)));
735 }
736 }
737
738 #if has_mmap && large_memory && mmap_signal
739
740 /*
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.
748 *
749 * This can also occur if someone truncates the file, even without NFS.
750 */
751
752 static int const mmapsigs[] = { mmap_signal };
753
754 void
catchmmapints()755 catchmmapints()
756 {
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)));
761 }
762 }
763 #endif
764
765 #endif /* has_signal */
766
767
768 void
fastcopy(inf,outf)769 fastcopy(inf,outf)
770 register RILE *inf;
771 FILE *outf;
772 /* Function: copies the remainder of file inf to outf.
773 */
774 {
775 #if large_memory
776 # if maps_memory
777 awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
778 inf->ptr = inf->lim;
779 # else
780 for (;;) {
781 awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf);
782 inf->ptr = inf->readlim;
783 if (inf->ptr == inf->lim)
784 break;
785 VOID Igetmore(inf);
786 }
787 # endif
788 #else
789 char buf[BUFSIZ*8];
790 register fread_type rcount;
791
792 /*now read the rest of the file in blocks*/
793 while (!feof(inf)) {
794 if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) {
795 testIerror(inf);
796 return;
797 }
798 awrite(buf, (size_t)rcount, outf);
799 }
800 #endif
801 }
802
803 #ifndef SSIZE_MAX
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)
807 #endif
808
809 void
awrite(buf,chars,f)810 awrite(buf, chars, f)
811 char const *buf;
812 size_t chars;
813 FILE *f;
814 {
815 if (buf == NULL)
816 return;
817
818 /* Posix 1003.1-1990 ssize_t hack */
819 while (SSIZE_MAX < chars) {
820 if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX)
821 Oerror();
822 buf += SSIZE_MAX;
823 chars -= SSIZE_MAX;
824 }
825
826 if (Fwrite(buf, sizeof(*buf), chars, f) != chars)
827 Oerror();
828 }
829
830 /* dup a file descriptor; the result must not be stdin, stdout, or stderr. */
831 static int dupSafer P((int));
832 static int
dupSafer(fd)833 dupSafer(fd)
834 int fd;
835 {
836 # ifdef F_DUPFD
837 return fcntl(fd, F_DUPFD, STDERR_FILENO + 1);
838 # else
839 int e, f, i, used = 0;
840 while (STDIN_FILENO <= (f = dup(fd)) && f <= STDERR_FILENO)
841 used |= 1<<f;
842 e = errno;
843 for (i = STDIN_FILENO; i <= STDERR_FILENO; i++)
844 if (used & (1<<i))
845 VOID close(i);
846 errno = e;
847 return f;
848 # endif
849 }
850
851 /* Renumber a file descriptor so that it's not stdin, stdout, or stderr. */
852 int
fdSafer(fd)853 fdSafer(fd)
854 int fd;
855 {
856 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) {
857 int f = dupSafer(fd);
858 int e = errno;
859 VOID close(fd);
860 errno = e;
861 fd = f;
862 }
863 return fd;
864 }
865
866 /* Like fopen, except the result is never stdin, stdout, or stderr. */
867 FILE *
fopenSafer(filename,type)868 fopenSafer(filename, type)
869 char const *filename;
870 char const *type;
871 {
872 FILE *stream = fopen(filename, type);
873 if (stream) {
874 int fd = fileno(stream);
875 if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) {
876 int f = dupSafer(fd);
877 if (f < 0) {
878 int e = errno;
879 VOID fclose(stream);
880 errno = e;
881 return 0;
882 }
883 if (fclose(stream) != 0) {
884 int e = errno;
885 VOID close(f);
886 errno = e;
887 return 0;
888 }
889 stream = fdopen(f, type);
890 }
891 }
892 return stream;
893 }
894
895
896 #ifdef F_DUPFD
897 # undef dup
898 # define dup(fd) fcntl(fd, F_DUPFD, 0)
899 #endif
900
901
902 #if has_fork || has_spawn
903
904 static int movefd P((int,int));
905 static int
movefd(old,new)906 movefd(old, new)
907 int old, new;
908 {
909 if (old < 0 || old == new)
910 return old;
911 # ifdef F_DUPFD
912 new = fcntl(old, F_DUPFD, new);
913 # else
914 new = dup2(old, new);
915 # endif
916 return close(old)==0 ? new : -1;
917 }
918
919 static int fdreopen P((int,char const*,int));
920 static int
fdreopen(fd,file,flags)921 fdreopen(fd, file, flags)
922 int fd;
923 char const *file;
924 int flags;
925 {
926 int newfd;
927 VOID close(fd);
928 newfd =
929 #if !open_can_creat
930 flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
931 #endif
932 open(file, flags, S_IRUSR|S_IWUSR);
933 return movefd(newfd, fd);
934 }
935
936 #if has_spawn
937 static void redirect P((int,int));
938 static void
redirect(old,new)939 redirect(old, new)
940 int old, new;
941 /*
942 * Move file descriptor OLD to NEW.
943 * If OLD is -1, do nothing.
944 * If OLD is -2, just close NEW.
945 */
946 {
947 if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0))
948 efaterror("spawn I/O redirection");
949 }
950 #endif
951
952
953 #else /* !has_fork && !has_spawn */
954
955 static void bufargcat P((struct buf*,int,char const*));
956 static void
bufargcat(b,c,s)957 bufargcat(b, c, s)
958 register struct buf *b;
959 int c;
960 register char const *s;
961 /* Append to B a copy of C, plus a quoted copy of S. */
962 {
963 register char *p;
964 register char const *t;
965 size_t bl, sl;
966
967 for (t=s, sl=0; *t; )
968 sl += 3*(*t++=='\'') + 1;
969 bl = strlen(b->string);
970 bufrealloc(b, bl + sl + 4);
971 p = b->string + bl;
972 *p++ = c;
973 *p++ = '\'';
974 while (*s) {
975 if (*s == '\'') {
976 *p++ = '\'';
977 *p++ = '\\';
978 *p++ = '\'';
979 }
980 *p++ = *s++;
981 }
982 *p++ = '\'';
983 *p = 0;
984 }
985
986 #endif
987
988 #if !has_spawn && has_fork
989 /*
990 * Output the string S to stderr, without touching any I/O buffers.
991 * This is useful if you are a child process, whose buffers are usually wrong.
992 * Exit immediately if the write does not completely succeed.
993 */
994 static void write_stderr P((char const *));
995 static void
write_stderr(s)996 write_stderr(s)
997 char const *s;
998 {
999 size_t slen = strlen(s);
1000 if (write(STDERR_FILENO, s, slen) != slen)
1001 _exit(EXIT_TROUBLE);
1002 }
1003 #endif
1004
1005 /*
1006 * Run a command.
1007 * infd, if not -1, is the input file descriptor.
1008 * outname, if nonzero, is the name of the output file.
1009 * args[1..] form the command to be run; args[0] might be modified.
1010 */
1011 int
runv(infd,outname,args)1012 runv(infd, outname, args)
1013 int infd;
1014 char const *outname, **args;
1015 {
1016 int wstatus;
1017
1018 #if bad_wait_if_SIGCHLD_ignored
1019 static int fixed_SIGCHLD;
1020 if (!fixed_SIGCHLD) {
1021 fixed_SIGCHLD = true;
1022 # ifndef SIGCHLD
1023 # define SIGCHLD SIGCLD
1024 # endif
1025 VOID signal(SIGCHLD, SIG_DFL);
1026 }
1027 #endif
1028
1029 oflush();
1030 eflush();
1031 {
1032 #if has_spawn
1033 int in, out;
1034 char const *file;
1035
1036 in = -1;
1037 if (infd != -1 && infd != STDIN_FILENO) {
1038 if ((in = dup(STDIN_FILENO)) < 0) {
1039 if (errno != EBADF)
1040 efaterror("spawn input setup");
1041 in = -2;
1042 } else {
1043 # ifdef F_DUPFD
1044 if (close(STDIN_FILENO) != 0)
1045 efaterror("spawn input close");
1046 # endif
1047 }
1048 if (
1049 # ifdef F_DUPFD
1050 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO
1051 # else
1052 dup2(infd, STDIN_FILENO) != STDIN_FILENO
1053 # endif
1054 )
1055 efaterror("spawn input redirection");
1056 }
1057
1058 out = -1;
1059 if (outname) {
1060 if ((out = dup(STDOUT_FILENO)) < 0) {
1061 if (errno != EBADF)
1062 efaterror("spawn output setup");
1063 out = -2;
1064 }
1065 if (fdreopen(
1066 STDOUT_FILENO, outname,
1067 O_CREAT | O_TRUNC | O_WRONLY
1068 ) < 0)
1069 efaterror(outname);
1070 }
1071
1072 wstatus = spawn_RCS(0, args[1], (char**)(args + 1));
1073 # ifdef RCS_SHELL
1074 if (wstatus == -1 && errno == ENOEXEC) {
1075 args[0] = RCS_SHELL;
1076 wstatus = spawnv(0, args[0], (char**)args);
1077 }
1078 # endif
1079 redirect(in, STDIN_FILENO);
1080 redirect(out, STDOUT_FILENO);
1081 #else
1082 #if has_fork
1083 pid_t pid;
1084 if (!(pid = vfork())) {
1085 char const *notfound;
1086 if (infd != -1 && infd != STDIN_FILENO && (
1087 # ifdef F_DUPFD
1088 (VOID close(STDIN_FILENO),
1089 fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO)
1090 # else
1091 dup2(infd, STDIN_FILENO) != STDIN_FILENO
1092 # endif
1093 )) {
1094 /* Avoid perror since it may misuse buffers. */
1095 write_stderr(args[1]);
1096 write_stderr(": I/O redirection failed\n");
1097 _exit(EXIT_TROUBLE);
1098 }
1099
1100 if (outname)
1101 if (fdreopen(
1102 STDOUT_FILENO, outname,
1103 O_CREAT | O_TRUNC | O_WRONLY
1104 ) < 0) {
1105 /* Avoid perror since it may misuse buffers. */
1106 write_stderr(args[1]);
1107 write_stderr(": ");
1108 write_stderr(outname);
1109 write_stderr(": cannot create\n");
1110 _exit(EXIT_TROUBLE);
1111 }
1112 VOID exec_RCS(args[1], (char**)(args + 1));
1113 notfound = args[1];
1114 # ifdef RCS_SHELL
1115 if (errno == ENOEXEC) {
1116 args[0] = notfound = RCS_SHELL;
1117 VOID execv(args[0], (char**)args);
1118 }
1119 # endif
1120
1121 /* Avoid perror since it may misuse buffers. */
1122 write_stderr(notfound);
1123 write_stderr(": not found\n");
1124 _exit(EXIT_TROUBLE);
1125 }
1126 if (pid < 0)
1127 efaterror("fork");
1128 # if has_waitpid
1129 if (waitpid(pid, &wstatus, 0) < 0)
1130 efaterror("waitpid");
1131 # else
1132 {
1133 pid_t w;
1134 do {
1135 if ((w = wait(&wstatus)) < 0)
1136 efaterror("wait");
1137 } while (w != pid);
1138 }
1139 # endif
1140 #else
1141 static struct buf b;
1142 char const *p;
1143
1144 /* Use system(). On many hosts system() discards signals. Yuck! */
1145 p = args + 1;
1146 bufscpy(&b, *p);
1147 while (*++p)
1148 bufargcat(&b, ' ', *p);
1149 if (infd != -1 && infd != STDIN_FILENO) {
1150 char redirection[32];
1151 VOID sprintf(redirection, "<&%d", infd);
1152 bufscat(&b, redirection);
1153 }
1154 if (outname)
1155 bufargcat(&b, '>', outname);
1156 wstatus = system(b.string);
1157 #endif
1158 #endif
1159 }
1160 if (!WIFEXITED(wstatus)) {
1161 if (WIFSIGNALED(wstatus)) {
1162 psignal(WTERMSIG(wstatus), args[1]);
1163 fatcleanup(1);
1164 }
1165 faterror("%s failed for unknown reason", args[1]);
1166 }
1167 return WEXITSTATUS(wstatus);
1168 }
1169
1170 #define CARGSMAX 20
1171 /*
1172 * Run a command.
1173 * infd, if not -1, is the input file descriptor.
1174 * outname, if nonzero, is the name of the output file.
1175 * The remaining arguments specify the command and its arguments.
1176 */
1177 int
1178 #if has_prototypes
run(int infd,char const * outname,...)1179 run(int infd, char const *outname, ...)
1180 #else
1181 /*VARARGS2*/
1182 run(infd, outname, va_alist)
1183 int infd;
1184 char const *outname;
1185 va_dcl
1186 #endif
1187 {
1188 va_list ap;
1189 char const *rgargs[CARGSMAX];
1190 register int i;
1191 vararg_start(ap, outname);
1192 for (i = 1; (rgargs[i++] = va_arg(ap, char const*)); )
1193 if (CARGSMAX <= i)
1194 faterror("too many command arguments");
1195 va_end(ap);
1196 return runv(infd, outname, rgargs);
1197 }
1198
1199
1200 int RCSversion;
1201
1202 void
setRCSversion(str)1203 setRCSversion(str)
1204 char const *str;
1205 {
1206 static int oldversion;
1207
1208 register char const *s = str + 2;
1209
1210 if (*s) {
1211 int v = VERSION_DEFAULT;
1212
1213 if (oldversion)
1214 redefined('V');
1215 oldversion = true;
1216 v = 0;
1217 while (isdigit(*s))
1218 v = 10*v + *s++ - '0';
1219 if (*s)
1220 error("%s isn't a number", str);
1221 else if (v < VERSION_min || VERSION_max < v)
1222 error("%s out of range %d..%d",
1223 str, VERSION_min, VERSION_max
1224 );
1225
1226 RCSversion = VERSION(v);
1227 } else {
1228 printf("RCS version %s\n", RCS_version_string);
1229 exit(0);
1230 }
1231 }
1232
1233 int
getRCSINIT(argc,argv,newargv)1234 getRCSINIT(argc, argv, newargv)
1235 int argc;
1236 char **argv, ***newargv;
1237 {
1238 register char *p, *q, **pp;
1239 size_t n;
1240
1241 if (!(q = cgetenv("RCSINIT")))
1242 *newargv = argv;
1243 else {
1244 n = argc + 2;
1245 /*
1246 * Count spaces in RCSINIT to allocate a new arg vector.
1247 * This is an upper bound, but it's OK even if too large.
1248 */
1249 for (p = q; ; ) {
1250 switch (*p++) {
1251 default:
1252 continue;
1253
1254 case ' ':
1255 case '\b': case '\f': case '\n':
1256 case '\r': case '\t': case '\v':
1257 n++;
1258 continue;
1259
1260 case '\0':
1261 break;
1262 }
1263 break;
1264 }
1265 *newargv = pp = tnalloc(char*, n);
1266 *pp++ = *argv++; /* copy program name */
1267 for (p = q; ; ) {
1268 for (;;) {
1269 switch (*q) {
1270 case '\0':
1271 goto copyrest;
1272
1273 case ' ':
1274 case '\b': case '\f': case '\n':
1275 case '\r': case '\t': case '\v':
1276 q++;
1277 continue;
1278 }
1279 break;
1280 }
1281 *pp++ = p;
1282 ++argc;
1283 for (;;) {
1284 switch ((*p++ = *q++)) {
1285 case '\0':
1286 goto copyrest;
1287
1288 case '\\':
1289 if (!*q)
1290 goto copyrest;
1291 p[-1] = *q++;
1292 continue;
1293
1294 default:
1295 continue;
1296
1297 case ' ':
1298 case '\b': case '\f': case '\n':
1299 case '\r': case '\t': case '\v':
1300 break;
1301 }
1302 break;
1303 }
1304 p[-1] = '\0';
1305 }
1306 copyrest:
1307 while ((*pp++ = *argv++))
1308 continue;
1309 }
1310 return argc;
1311 }
1312
1313
1314 #define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
1315
1316 #if has_getuid
ruid()1317 uid_t ruid() { cacheid(getuid()); }
1318 #endif
1319 #if has_setuid
euid()1320 uid_t euid() { cacheid(geteuid()); }
1321 #endif
1322
1323
1324 #if has_setuid
1325
1326 /*
1327 * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
1328 * because it lets us switch back and forth between arbitrary users.
1329 * If seteuid() doesn't work, we fall back on setuid(),
1330 * which works if saved setuid is supported,
1331 * unless the real or effective user is root.
1332 * This area is such a mess that we always check switches at runtime.
1333 */
1334
1335 static void
1336 #if has_prototypes
set_uid_to(uid_t u)1337 set_uid_to(uid_t u)
1338 #else
1339 set_uid_to(u) uid_t u;
1340 #endif
1341 /* Become user u. */
1342 {
1343 static int looping;
1344
1345 if (euid() == ruid())
1346 return;
1347 #if (has_fork||has_spawn) && DIFF_ABSOLUTE
1348 # if has_setreuid
1349 if (setreuid(u==euid() ? ruid() : euid(), u) != 0)
1350 efaterror("setuid");
1351 # else
1352 if (seteuid(u) != 0)
1353 efaterror("setuid");
1354 # endif
1355 #endif
1356 if (geteuid() != u) {
1357 if (looping)
1358 return;
1359 looping = true;
1360 faterror("root setuid not supported" + (u?5:0));
1361 }
1362 }
1363
1364 static int stick_with_euid;
1365
1366 void
1367 /* Ignore all calls to seteid() and setrid(). */
nosetid()1368 nosetid()
1369 {
1370 stick_with_euid = true;
1371 }
1372
1373 void
seteid()1374 seteid()
1375 /* Become effective user. */
1376 {
1377 if (!stick_with_euid)
1378 set_uid_to(euid());
1379 }
1380
1381 void
setrid()1382 setrid()
1383 /* Become real user. */
1384 {
1385 if (!stick_with_euid)
1386 set_uid_to(ruid());
1387 }
1388 #endif
1389
1390 time_t
now()1391 now()
1392 {
1393 static time_t t;
1394 if (!t && time(&t) == -1)
1395 efaterror("time");
1396 return t;
1397 }
1398