home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / prgramer / rcs / sources / rcsutil.c < prev    next >
C/C++ Source or Header  |  1992-02-17  |  24KB  |  1,092 lines

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