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