xref: /dragonfly/gnu/usr.bin/rcs/lib/rcsutil.c (revision eb5a70b41c1d89d79455837f8044a8944694f89a)
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