UPROTO_BOOT_KEYBOARD is already defined in usb.h as UIPROTO_BOOT_KEYBOARD
[dragonfly.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  * $FreeBSD: src/gnu/usr.bin/rcs/lib/rcsutil.c,v 1.8 1999/08/27 23:36:49 peter Exp $
35  * $DragonFly: src/gnu/usr.bin/rcs/lib/rcsutil.c,v 1.2 2003/06/17 04:25:47 dillon Exp $
36  *
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, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcsutil.c,v 1.2 2003/06/17 04:25:47 dillon 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 *
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
245 okalloc(p)
246         malloc_type p;
247 {
248         if (!p)
249                 faterror("out of memory");
250         return p;
251 }
252
253         malloc_type
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
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
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
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
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
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 *
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 *
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 *
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 *
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 = 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
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
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
496 catchsig(s)
497         int s;
498 #ifdef SA_SIGINFO
499 {
500         catchsigaction(s, (siginfo_t *)0, (void *)0);
501 }
502         static signal_type
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
590 ignoreints()
591 {
592         ++holdlevel;
593 }
594
595         void
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
613   check_sig(r)
614         int r;
615   {
616         if (r != 0)
617                 efaterror("signal handling");
618   }
619
620         static void
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
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
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
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
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
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
810 awrite(buf, chars, f)
811         char const *buf;
812         size_t chars;
813         FILE *f;
814 {
815         /* Posix 1003.1-1990 ssize_t hack */
816         while (SSIZE_MAX < chars) {
817                 if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f)  !=  SSIZE_MAX)
818                         Oerror();
819                 buf += SSIZE_MAX;
820                 chars -= SSIZE_MAX;
821         }
822
823         if (Fwrite(buf, sizeof(*buf), chars, f)  !=  chars)
824                 Oerror();
825 }
826
827 /* dup a file descriptor; the result must not be stdin, stdout, or stderr.  */
828         static int dupSafer P((int));
829         static int
830 dupSafer(fd)
831         int fd;
832 {
833 #       ifdef F_DUPFD
834             return fcntl(fd, F_DUPFD, STDERR_FILENO + 1);
835 #       else
836             int e, f, i, used = 0;
837             while (STDIN_FILENO <= (f = dup(fd))  &&  f <= STDERR_FILENO)
838                     used |= 1<<f;
839             e = errno;
840             for (i = STDIN_FILENO;  i <= STDERR_FILENO;  i++)
841                     if (used & (1<<i))
842                             VOID close(i);
843             errno = e;
844             return f;
845 #       endif
846 }
847
848 /* Renumber a file descriptor so that it's not stdin, stdout, or stderr.  */
849         int
850 fdSafer(fd)
851         int fd;
852 {
853         if (STDIN_FILENO <= fd  &&  fd <= STDERR_FILENO) {
854                 int f = dupSafer(fd);
855                 int e = errno;
856                 VOID close(fd);
857                 errno = e;
858                 fd = f;
859         }
860         return fd;
861 }
862
863 /* Like fopen, except the result is never stdin, stdout, or stderr.  */
864         FILE *
865 fopenSafer(filename, type)
866         char const *filename;
867         char const *type;
868 {
869         FILE *stream = fopen(filename, type);
870         if (stream) {
871                 int fd = fileno(stream);
872                 if (STDIN_FILENO <= fd  &&  fd <= STDERR_FILENO) {
873                         int f = dupSafer(fd);
874                         if (f < 0) {
875                                 int e = errno;
876                                 VOID fclose(stream);
877                                 errno = e;
878                                 return 0;
879                         }
880                         if (fclose(stream) != 0) {
881                                 int e = errno;
882                                 VOID close(f);
883                                 errno = e;
884                                 return 0;
885                         }
886                         stream = fdopen(f, type);
887                 }
888         }
889         return stream;
890 }
891
892
893 #ifdef F_DUPFD
894 #       undef dup
895 #       define dup(fd) fcntl(fd, F_DUPFD, 0)
896 #endif
897
898
899 #if has_fork || has_spawn
900
901         static int movefd P((int,int));
902         static int
903 movefd(old, new)
904         int old, new;
905 {
906         if (old < 0  ||  old == new)
907                 return old;
908 #       ifdef F_DUPFD
909                 new = fcntl(old, F_DUPFD, new);
910 #       else
911                 new = dup2(old, new);
912 #       endif
913         return close(old)==0 ? new : -1;
914 }
915
916         static int fdreopen P((int,char const*,int));
917         static int
918 fdreopen(fd, file, flags)
919         int fd;
920         char const *file;
921         int flags;
922 {
923         int newfd;
924         VOID close(fd);
925         newfd =
926 #if !open_can_creat
927                 flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
928 #endif
929                 open(file, flags, S_IRUSR|S_IWUSR);
930         return movefd(newfd, fd);
931 }
932
933 #if has_spawn
934         static void redirect P((int,int));
935         static void
936 redirect(old, new)
937         int old, new;
938 /*
939 * Move file descriptor OLD to NEW.
940 * If OLD is -1, do nothing.
941 * If OLD is -2, just close NEW.
942 */
943 {
944         if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0))
945                 efaterror("spawn I/O redirection");
946 }
947 #endif
948
949
950 #else /* !has_fork && !has_spawn */
951
952         static void bufargcat P((struct buf*,int,char const*));
953         static void
954 bufargcat(b, c, s)
955         register struct buf *b;
956         int c;
957         register char const *s;
958 /* Append to B a copy of C, plus a quoted copy of S.  */
959 {
960         register char *p;
961         register char const *t;
962         size_t bl, sl;
963
964         for (t=s, sl=0;  *t;  )
965                 sl  +=  3*(*t++=='\'') + 1;
966         bl = strlen(b->string);
967         bufrealloc(b, bl + sl + 4);
968         p = b->string + bl;
969         *p++ = c;
970         *p++ = '\'';
971         while (*s) {
972                 if (*s == '\'') {
973                         *p++ = '\'';
974                         *p++ = '\\';
975                         *p++ = '\'';
976                 }
977                 *p++ = *s++;
978         }
979         *p++ = '\'';
980         *p = 0;
981 }
982
983 #endif
984
985 #if !has_spawn && has_fork
986 /*
987 * Output the string S to stderr, without touching any I/O buffers.
988 * This is useful if you are a child process, whose buffers are usually wrong.
989 * Exit immediately if the write does not completely succeed.
990 */
991 static void write_stderr P((char const *));
992         static void
993 write_stderr(s)
994         char const *s;
995 {
996         size_t slen = strlen(s);
997         if (write(STDERR_FILENO, s, slen) != slen)
998                 _exit(EXIT_TROUBLE);
999 }
1000 #endif
1001
1002 /*
1003 * Run a command.
1004 * infd, if not -1, is the input file descriptor.
1005 * outname, if nonzero, is the name of the output file.
1006 * args[1..] form the command to be run; args[0] might be modified.
1007 */
1008         int
1009 runv(infd, outname, args)
1010         int infd;
1011         char const *outname, **args;
1012 {
1013         int wstatus;
1014
1015 #if bad_wait_if_SIGCHLD_ignored
1016         static int fixed_SIGCHLD;
1017         if (!fixed_SIGCHLD) {
1018             fixed_SIGCHLD = true;
1019 #           ifndef SIGCHLD
1020 #           define SIGCHLD SIGCLD
1021 #           endif
1022             VOID signal(SIGCHLD, SIG_DFL);
1023         }
1024 #endif
1025
1026         oflush();
1027         eflush();
1028     {
1029 #if has_spawn
1030         int in, out;
1031         char const *file;
1032
1033         in = -1;
1034         if (infd != -1  &&  infd != STDIN_FILENO) {
1035             if ((in = dup(STDIN_FILENO)) < 0) {
1036                 if (errno != EBADF)
1037                     efaterror("spawn input setup");
1038                 in = -2;
1039             } else {
1040 #               ifdef F_DUPFD
1041                     if (close(STDIN_FILENO) != 0)
1042                         efaterror("spawn input close");
1043 #               endif
1044             }
1045             if (
1046 #               ifdef F_DUPFD
1047                     fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO
1048 #               else
1049                     dup2(infd, STDIN_FILENO) != STDIN_FILENO
1050 #               endif
1051             )
1052                 efaterror("spawn input redirection");
1053         }
1054
1055         out = -1;
1056         if (outname) {
1057             if ((out = dup(STDOUT_FILENO)) < 0) {
1058                 if (errno != EBADF)
1059                     efaterror("spawn output setup");
1060                 out = -2;
1061             }
1062             if (fdreopen(
1063                 STDOUT_FILENO, outname,
1064                 O_CREAT | O_TRUNC | O_WRONLY
1065             ) < 0)
1066                 efaterror(outname);
1067         }
1068
1069         wstatus = spawn_RCS(0, args[1], (char**)(args + 1));
1070 #       ifdef RCS_SHELL
1071             if (wstatus == -1  &&  errno == ENOEXEC) {
1072                 args[0] = RCS_SHELL;
1073                 wstatus = spawnv(0, args[0], (char**)args);
1074             }
1075 #       endif
1076         redirect(in, STDIN_FILENO);
1077         redirect(out, STDOUT_FILENO);
1078 #else
1079 #if has_fork
1080         pid_t pid;
1081         if (!(pid = vfork())) {
1082                 char const *notfound;
1083                 if (infd != -1  &&  infd != STDIN_FILENO  &&  (
1084 #                   ifdef F_DUPFD
1085                         (VOID close(STDIN_FILENO),
1086                         fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO)
1087 #                   else
1088                         dup2(infd, STDIN_FILENO) != STDIN_FILENO
1089 #                   endif
1090                 )) {
1091                     /* Avoid perror since it may misuse buffers.  */
1092                     write_stderr(args[1]);
1093                     write_stderr(": I/O redirection failed\n");
1094                     _exit(EXIT_TROUBLE);
1095                 }
1096
1097                 if (outname)
1098                     if (fdreopen(
1099                         STDOUT_FILENO, outname,
1100                         O_CREAT | O_TRUNC | O_WRONLY
1101                     ) < 0) {
1102                         /* Avoid perror since it may misuse buffers.  */
1103                         write_stderr(args[1]);
1104                         write_stderr(": ");
1105                         write_stderr(outname);
1106                         write_stderr(": cannot create\n");
1107                         _exit(EXIT_TROUBLE);
1108                     }
1109                 VOID exec_RCS(args[1], (char**)(args + 1));
1110                 notfound = args[1];
1111 #               ifdef RCS_SHELL
1112                     if (errno == ENOEXEC) {
1113                         args[0] = notfound = RCS_SHELL;
1114                         VOID execv(args[0], (char**)args);
1115                     }
1116 #               endif
1117
1118                 /* Avoid perror since it may misuse buffers.  */
1119                 write_stderr(notfound);
1120                 write_stderr(": not found\n");
1121                 _exit(EXIT_TROUBLE);
1122         }
1123         if (pid < 0)
1124                 efaterror("fork");
1125 #       if has_waitpid
1126                 if (waitpid(pid, &wstatus, 0) < 0)
1127                         efaterror("waitpid");
1128 #       else
1129                 {
1130                         pid_t w;
1131                         do {
1132                                 if ((w = wait(&wstatus)) < 0)
1133                                         efaterror("wait");
1134                         } while (w != pid);
1135                 }
1136 #       endif
1137 #else
1138         static struct buf b;
1139         char const *p;
1140
1141         /* Use system().  On many hosts system() discards signals.  Yuck!  */
1142         p = args + 1;
1143         bufscpy(&b, *p);
1144         while (*++p)
1145                 bufargcat(&b, ' ', *p);
1146         if (infd != -1  &&  infd != STDIN_FILENO) {
1147                 char redirection[32];
1148                 VOID sprintf(redirection, "<&%d", infd);
1149                 bufscat(&b, redirection);
1150         }
1151         if (outname)
1152                 bufargcat(&b, '>', outname);
1153         wstatus = system(b.string);
1154 #endif
1155 #endif
1156     }
1157         if (!WIFEXITED(wstatus)) {
1158                 if (WIFSIGNALED(wstatus)) {
1159                         psignal(WTERMSIG(wstatus), args[1]);
1160                         fatcleanup(1);
1161                 }
1162                 faterror("%s failed for unknown reason", args[1]);
1163         }
1164         return WEXITSTATUS(wstatus);
1165 }
1166
1167 #define CARGSMAX 20
1168 /*
1169 * Run a command.
1170 * infd, if not -1, is the input file descriptor.
1171 * outname, if nonzero, is the name of the output file.
1172 * The remaining arguments specify the command and its arguments.
1173 */
1174         int
1175 #if has_prototypes
1176 run(int infd, char const *outname, ...)
1177 #else
1178         /*VARARGS2*/
1179 run(infd, outname, va_alist)
1180         int infd;
1181         char const *outname;
1182         va_dcl
1183 #endif
1184 {
1185         va_list ap;
1186         char const *rgargs[CARGSMAX];
1187         register int i;
1188         vararg_start(ap, outname);
1189         for (i = 1;  (rgargs[i++] = va_arg(ap, char const*));  )
1190                 if (CARGSMAX <= i)
1191                         faterror("too many command arguments");
1192         va_end(ap);
1193         return runv(infd, outname, rgargs);
1194 }
1195
1196
1197 int RCSversion;
1198
1199         void
1200 setRCSversion(str)
1201         char const *str;
1202 {
1203         static int oldversion;
1204
1205         register char const *s = str + 2;
1206
1207         if (*s) {
1208                 int v = VERSION_DEFAULT;
1209
1210                 if (oldversion)
1211                         redefined('V');
1212                 oldversion = true;
1213                 v = 0;
1214                 while (isdigit(*s))
1215                         v  =  10*v + *s++ - '0';
1216                 if (*s)
1217                         error("%s isn't a number", str);
1218                 else if (v < VERSION_min  ||  VERSION_max < v)
1219                         error("%s out of range %d..%d",
1220                                 str, VERSION_min, VERSION_max
1221                         );
1222
1223                 RCSversion = VERSION(v);
1224         } else {
1225                 printf("RCS version %s\n", RCS_version_string);
1226                 exit(0);
1227         }
1228 }
1229
1230         int
1231 getRCSINIT(argc, argv, newargv)
1232         int argc;
1233         char **argv, ***newargv;
1234 {
1235         register char *p, *q, **pp;
1236         char const *ev;
1237         size_t n;
1238
1239         if ((ev = cgetenv("RCSLOCALID")))
1240                 setRCSLocalId(ev);
1241
1242         if ((ev = cgetenv("RCSINCEXC")))
1243                 setIncExc(ev);
1244
1245         if (!(q = cgetenv("RCSINIT")))
1246                 *newargv = argv;
1247         else {
1248                 n = argc + 2;
1249                 /*
1250                  * Count spaces in RCSINIT to allocate a new arg vector.
1251                  * This is an upper bound, but it's OK even if too large.
1252                  */
1253                 for (p = q;  ;  ) {
1254                         switch (*p++) {
1255                             default:
1256                                 continue;
1257
1258                             case ' ':
1259                             case '\b': case '\f': case '\n':
1260                             case '\r': case '\t': case '\v':
1261                                 n++;
1262                                 continue;
1263
1264                             case '\0':
1265                                 break;
1266                         }
1267                         break;
1268                 }
1269                 *newargv = pp = tnalloc(char*, n);
1270                 *pp++ = *argv++; /* copy program name */
1271                 for (p = q;  ;  ) {
1272                         for (;;) {
1273                                 switch (*q) {
1274                                     case '\0':
1275                                         goto copyrest;
1276
1277                                     case ' ':
1278                                     case '\b': case '\f': case '\n':
1279                                     case '\r': case '\t': case '\v':
1280                                         q++;
1281                                         continue;
1282                                 }
1283                                 break;
1284                         }
1285                         *pp++ = p;
1286                         ++argc;
1287                         for (;;) {
1288                                 switch ((*p++ = *q++)) {
1289                                     case '\0':
1290                                         goto copyrest;
1291
1292                                     case '\\':
1293                                         if (!*q)
1294                                                 goto copyrest;
1295                                         p[-1] = *q++;
1296                                         continue;
1297
1298                                     default:
1299                                         continue;
1300
1301                                     case ' ':
1302                                     case '\b': case '\f': case '\n':
1303                                     case '\r': case '\t': case '\v':
1304                                         break;
1305                                 }
1306                                 break;
1307                         }
1308                         p[-1] = '\0';
1309                 }
1310             copyrest:
1311                 while ((*pp++ = *argv++))
1312                         continue;
1313         }
1314         return argc;
1315 }
1316
1317
1318 #define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
1319
1320 #if has_getuid
1321         uid_t ruid() { cacheid(getuid()); }
1322 #endif
1323 #if has_setuid
1324         uid_t euid() { cacheid(geteuid()); }
1325 #endif
1326
1327
1328 #if has_setuid
1329
1330 /*
1331  * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
1332  * because it lets us switch back and forth between arbitrary users.
1333  * If seteuid() doesn't work, we fall back on setuid(),
1334  * which works if saved setuid is supported,
1335  * unless the real or effective user is root.
1336  * This area is such a mess that we always check switches at runtime.
1337  */
1338
1339         static void
1340 #if has_prototypes
1341 set_uid_to(uid_t u)
1342 #else
1343  set_uid_to(u) uid_t u;
1344 #endif
1345 /* Become user u.  */
1346 {
1347         static int looping;
1348
1349         if (euid() == ruid())
1350                 return;
1351 #if (has_fork||has_spawn) && DIFF_ABSOLUTE
1352 #       if has_setreuid
1353                 if (setreuid(u==euid() ? ruid() : euid(), u) != 0)
1354                         efaterror("setuid");
1355 #       else
1356                 if (seteuid(u) != 0)
1357                         efaterror("setuid");
1358 #       endif
1359 #endif
1360         if (geteuid() != u) {
1361                 if (looping)
1362                         return;
1363                 looping = true;
1364                 faterror("root setuid not supported" + (u?5:0));
1365         }
1366 }
1367
1368 static int stick_with_euid;
1369
1370         void
1371 /* Ignore all calls to seteid() and setrid().  */
1372 nosetid()
1373 {
1374         stick_with_euid = true;
1375 }
1376
1377         void
1378 seteid()
1379 /* Become effective user.  */
1380 {
1381         if (!stick_with_euid)
1382                 set_uid_to(euid());
1383 }
1384
1385         void
1386 setrid()
1387 /* Become real user.  */
1388 {
1389         if (!stick_with_euid)
1390                 set_uid_to(ruid());
1391 }
1392 #endif
1393
1394         time_t
1395 now()
1396 {
1397         static time_t t;
1398         if (!t  &&  time(&t) == -1)
1399                 efaterror("time");
1400         return t;
1401 }