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