home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / rcs57pc3.zip / rcs / src / rcsfnms.c < prev    next >
C/C++ Source or Header  |  1999-03-14  |  30KB  |  1,137 lines

  1. /* RCS filename and pathname handling */
  2.  
  3. /****************************************************************************
  4.  *                     creation and deletion of /tmp temporaries
  5.  *               pairing of RCS pathnames and working pathnames.
  6.  *                     Testprogram: define PAIRTEST
  7.  ****************************************************************************
  8.  */
  9.  
  10. /* Copyright 1982, 1988, 1989 Walter Tichy
  11.    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
  12.    Distributed under license by the Free Software Foundation, Inc.
  13.  
  14. This file is part of RCS.
  15.  
  16. RCS is free software; you can redistribute it and/or modify
  17. it under the terms of the GNU General Public License as published by
  18. the Free Software Foundation; either version 2, or (at your option)
  19. any later version.
  20.  
  21. RCS is distributed in the hope that it will be useful,
  22. but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  24. GNU General Public License for more details.
  25.  
  26. You should have received a copy of the GNU General Public License
  27. along with RCS; see the file COPYING.
  28. If not, write to the Free Software Foundation,
  29. 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  30.  
  31. Report problems and direct all questions to:
  32.  
  33.     rcs-bugs@cs.purdue.edu
  34.  
  35. */
  36.  
  37.  
  38.  
  39.  
  40. /*
  41.  * $Log: rcsfnms.c,v $
  42.  * Revision 5.16  1995/06/16 06:19:24  eggert
  43.  * Update FSF address.
  44.  *
  45.  * Revision 5.15  1995/06/01 16:23:43  eggert
  46.  * (basefilename): Renamed from basename to avoid collisions.
  47.  * (dirlen): Remove (for similar reasons).
  48.  * (rcsreadopen): Open with FOPEN_RB.
  49.  * (SLASHSLASH_is_SLASH): Default is 0.
  50.  * (getcwd): Work around bad_wait_if_SIGCHLD_ignored bug.
  51.  *
  52.  * Revision 5.14  1994/03/17 14:05:48  eggert
  53.  * Strip trailing SLASHes from TMPDIR; some systems need this.  Remove lint.
  54.  *
  55.  * Revision 5.13  1993/11/03 17:42:27  eggert
  56.  * Determine whether a file name is too long indirectly,
  57.  * by examining inode numbers, instead of trying to use operating system
  58.  * primitives like pathconf, which are not trustworthy in general.
  59.  * File names may now hold white space or $.
  60.  * Do not flatten ../X in pathnames; that may yield wrong answer for symlinks.
  61.  * Add getabsname hook.  Improve quality of diagnostics.
  62.  *
  63.  * Revision 5.12  1992/07/28  16:12:44  eggert
  64.  * Add .sty.  .pl now implies Perl, not Prolog.  Fix fdlock initialization bug.
  65.  * Check that $PWD is really ".".  Be consistent about pathnames vs filenames.
  66.  *
  67.  * Revision 5.11  1992/02/17  23:02:25  eggert
  68.  * `a/RCS/b/c' is now an RCS file with an empty extension, not just `a/b/RCS/c'.
  69.  *
  70.  * Revision 5.10  1992/01/24  18:44:19  eggert
  71.  * Fix bug: Expand and Ignored weren't reinitialized.
  72.  * Avoid `char const c=ch;' compiler bug.
  73.  * Add support for bad_creat0.
  74.  *
  75.  * Revision 5.9  1992/01/06  02:42:34  eggert
  76.  * Shorten long (>31 chars) name.
  77.  * while (E) ; -> while (E) continue;
  78.  *
  79.  * Revision 5.8  1991/09/24  00:28:40  eggert
  80.  * Don't export bindex().
  81.  *
  82.  * Revision 5.7  1991/08/19  03:13:55  eggert
  83.  * Fix messages when rcswriteopen fails.
  84.  * Look in $TMP and $TEMP if $TMPDIR isn't set.  Tune.
  85.  *
  86.  * Revision 5.6  1991/04/21  11:58:23  eggert
  87.  * Fix errno bugs.  Add -x, RCSINIT, MS-DOS support.
  88.  *
  89.  * Revision 5.5  1991/02/26  17:48:38  eggert
  90.  * Fix setuid bug.  Support new link behavior.
  91.  * Define more portable getcwd().
  92.  *
  93.  * Revision 5.4  1990/11/01  05:03:43  eggert
  94.  * Permit arbitrary data in comment leaders.
  95.  *
  96.  * Revision 5.3  1990/09/14  22:56:16  hammer
  97.  * added more filename extensions and their comment leaders
  98.  *
  99.  * Revision 5.2  1990/09/04  08:02:23  eggert
  100.  * Fix typo when !RCSSEP.
  101.  *
  102.  * Revision 5.1  1990/08/29  07:13:59  eggert
  103.  * Work around buggy compilers with defective argument promotion.
  104.  *
  105.  * Revision 5.0  1990/08/22  08:12:50  eggert
  106.  * Ignore signals when manipulating the semaphore file.
  107.  * Modernize list of filename extensions.
  108.  * Permit paths of arbitrary length.  Beware filenames beginning with "-".
  109.  * Remove compile-time limits; use malloc instead.
  110.  * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
  111.  * Ansify and Posixate.
  112.  * Don't use access().  Fix test for non-regular files.  Tune.
  113.  *
  114.  * Revision 4.8  89/05/01  15:09:41  narten
  115.  * changed getwd to not stat empty directories.
  116.  * 
  117.  * Revision 4.7  88/08/09  19:12:53  eggert
  118.  * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint.
  119.  * 
  120.  * Revision 4.6  87/12/18  11:40:23  narten
  121.  * additional file types added from 4.3 BSD version, and SPARC assembler
  122.  * comment character added. Also, more lint cleanups. (Guy Harris)
  123.  * 
  124.  * Revision 4.5  87/10/18  10:34:16  narten
  125.  * Updating version numbers. Changes relative to 1.1 actually relative
  126.  * to verion 4.3
  127.  * 
  128.  * Revision 1.3  87/03/27  14:22:21  jenkins
  129.  * Port to suns
  130.  * 
  131.  * Revision 1.2  85/06/26  07:34:28  svb
  132.  * Comment leader '% ' for '*.tex' files added.
  133.  * 
  134.  * Revision 4.3  83/12/15  12:26:48  wft
  135.  * Added check for KDELIM in filenames to pairfilenames().
  136.  * 
  137.  * Revision 4.2  83/12/02  22:47:45  wft
  138.  * Added csh, red, and sl filename suffixes.
  139.  * 
  140.  * Revision 4.1  83/05/11  16:23:39  wft
  141.  * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
  142.  * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
  143.  * 2. added getting the file status of RCS and working files;
  144.  * 3. added ignoring of directories.
  145.  * 
  146.  * Revision 3.7  83/05/11  15:01:58  wft
  147.  * Added comtable[] which pairs filename suffixes with comment leaders;
  148.  * updated InitAdmin() accordingly.
  149.  * 
  150.  * Revision 3.6  83/04/05  14:47:36  wft
  151.  * fixed Suffix in InitAdmin().
  152.  * 
  153.  * Revision 3.5  83/01/17  18:01:04  wft
  154.  * Added getwd() and rename(); these can be removed by defining
  155.  * V4_2BSD, since they are not needed in 4.2 bsd.
  156.  * Changed sys/param.h to sys/types.h.
  157.  *
  158.  * Revision 3.4  82/12/08  21:55:20  wft
  159.  * removed unused variable.
  160.  *
  161.  * Revision 3.3  82/11/28  20:31:37  wft
  162.  * Changed mktempfile() to store the generated filenames.
  163.  * Changed getfullRCSname() to store the file and pathname, and to
  164.  * delete leading "../" and "./".
  165.  *
  166.  * Revision 3.2  82/11/12  14:29:40  wft
  167.  * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
  168.  * checksuffix(), checkfullpath(). Semaphore name generation updated.
  169.  * mktempfile() now checks for nil path; freefilename initialized properly.
  170.  * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
  171.  * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
  172.  *
  173.  * Revision 3.1  82/10/18  14:51:28  wft
  174.  * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
  175.  * renamed checkpath() to checkfullpath().
  176.  */
  177.  
  178.  
  179. #include "rcsbase.h"
  180.  
  181. libId(fnmsId, "$Id: rcsfnms.c,v 5.16 1995/06/16 06:19:24 eggert Exp $")
  182.  
  183. static char const *bindex P((char const*,int));
  184. static int fin2open P((char const*, size_t, char const*, size_t, char const*, size_t, RILE*(*)P((struct buf*,struct stat*,int)), int));
  185. static int finopen P((RILE*(*)P((struct buf*,struct stat*,int)), int));
  186. static int suffix_matches P((char const*,char const*));
  187. static size_t dir_useful_len P((char const*));
  188. static size_t suffixlen P((char const*));
  189. static void InitAdmin P((void));
  190.  
  191. char const *RCSname;
  192. char *workname;
  193. int fdlock;
  194. FILE *workstdout;
  195. struct stat RCSstat;
  196. char const *suffixes;
  197.  
  198. static char const rcsdir[] = "RCS";
  199. #define rcslen (sizeof(rcsdir)-1)
  200.  
  201. static struct buf RCSbuf, RCSb;
  202. static int RCSerrno;
  203.  
  204.  
  205. /* Temp names to be unlinked when done, if they are not 0.  */
  206. #define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */
  207. static char *volatile tpnames[TEMPNAMES];
  208.  
  209.  
  210. struct compair {
  211.     char const *suffix, *comlead;
  212. };
  213.  
  214. /*
  215. * This table is present only for backwards compatibility.
  216. * Normally we ignore this table, and use the prefix of the `$Log' line instead.
  217. */
  218. static struct compair const comtable[] = {
  219.     { "a"    , "-- "    },    /* Ada */
  220.     { "ada"    , "-- "    },    
  221.     { "adb"    , "-- "    },    
  222.     { "ads"    , "-- "    },    
  223.     { "asm"    , ";; "    },    /* assembler (MS-DOS) */
  224.     { "bat"    , ":: "    },    /* batch (MS-DOS) */
  225.     { "body", "-- "    },    /* Ada */
  226.     { "c"    , " * "    },    /* C */
  227.     { "c++"    , "// "    },    /* C++ in all its infinite guises */
  228.     { "cc"    , "// "    },    
  229.     { "cpp"    , "// "    },    
  230.     { "cxx"    , "// "    },    
  231.     { "cl"    , ";;; "},    /* Common Lisp */
  232.     { "cmd"    , ":: "    },    /* command (OS/2) */
  233.     { "cmf"    , "c "    },    /* CM Fortran */
  234.     { "cs"    , " * "    },    /* C* */
  235.     { "el"    , "; "    },    /* Emacs Lisp */
  236.     { "f"    , "c "    },    /* Fortran */
  237.     { "for"    , "c "    },    
  238.     { "h"    , " * "    },    /* C-header */
  239.     { "hpp"    , "// "    },    /* C++ header */
  240.     { "hxx"    , "// "    },    
  241.     { "l"    , " * "    },    /* lex (NOTE: franzlisp disagrees) */
  242.     { "lisp", ";;; "},    /* Lucid Lisp */
  243.     { "lsp"    , ";; "    },    /* Microsoft Lisp */
  244.     { "m"   , "// " },    /* Objective C */
  245.     { "mac"    , ";; "    },    /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
  246.     { "me"    , ".\\\" "},    /* troff -me */
  247.     { "ml"    , "; "    },    /* mocklisp */
  248.     { "mm"    , ".\\\" "},    /* troff -mm */
  249.     { "ms"    , ".\\\" "},    /* troff -ms */
  250.     { "p"    , " * "    },    /* Pascal */
  251.     { "pas"    , " * "    },    
  252.     { "ps"    , "% "    },    /* PostScript */
  253.     { "spec", "-- "    },    /* Ada */
  254.     { "sty"    , "% "    },    /* LaTeX style */
  255.     { "tex"    , "% "    },    /* TeX */
  256.     { "y"    , " * "    },    /* yacc */
  257.     { 0    , "# "    }    /* default for unknown suffix; must be last */
  258. };
  259.  
  260. #if has_mktemp
  261.     static char const *tmp P((void));
  262.     static char const *
  263. tmp()
  264. /* Yield the name of the tmp directory.  */
  265. {
  266.     static char const *s;
  267.     if (!s
  268.         &&  !(s = cgetenv("TMPDIR"))    /* Unix tradition */
  269.         &&  !(s = cgetenv("TMP"))    /* DOS tradition */
  270.         &&  !(s = cgetenv("TEMP"))    /* another DOS tradition */
  271.     )
  272.         s = TMPDIR;
  273.     return s;
  274. }
  275. #endif
  276.  
  277.     char const *
  278. maketemp(n)
  279.     int n;
  280. /* Create a unique pathname using n and the process id and store it
  281.  * into the nth slot in tpnames.
  282.  * Because of storage in tpnames, tempunlink() can unlink the file later.
  283.  * Return a pointer to the pathname created.
  284.  */
  285. {
  286.     char *p;
  287.     char const *t = tpnames[n];
  288.  
  289.     if (t)
  290.         return t;
  291.  
  292.     catchints();
  293.     {
  294. #    if has_mktemp
  295.         char const *tp = tmp();
  296.         size_t tplen = dir_useful_len(tp);
  297.         p = testalloc(tplen + 10);
  298.         VOID sprintf(p, "%.*s%cT%cXXXXXX", (int)tplen, tp, SLASH, '0'+n);
  299.         if (!mktemp(p) || !*p)
  300.         faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'",
  301.             (int)tplen, tp, SLASH, '0'+n
  302.         );
  303. #    else
  304.         static char tpnamebuf[TEMPNAMES][L_tmpnam];
  305.         p = tpnamebuf[n];
  306.         if (!tmpnam(p) || !*p)
  307. #        ifdef P_tmpdir
  308.             faterror("can't make temporary pathname `%s...'",P_tmpdir);
  309. #        else
  310.             faterror("can't make temporary pathname");
  311. #        endif
  312. #    endif
  313.     }
  314.  
  315.     tpnames[n] = p;
  316.     return p;
  317. }
  318.  
  319.     void
  320. tempunlink()
  321. /* Clean up maketemp() files.  May be invoked by signal handler.
  322.  */
  323. {
  324.     register int i;
  325.     register char *p;
  326.  
  327.     for (i = TEMPNAMES;  0 <= --i;  )
  328.         if ((p = tpnames[i])) {
  329.         VOID unlink(p);
  330.         /*
  331.          * We would tfree(p) here,
  332.          * but this might dump core if we're handing a signal.
  333.          * We're about to exit anyway, so we won't bother.
  334.          */
  335.         tpnames[i] = 0;
  336.         }
  337. }
  338.  
  339.  
  340.     static char const *
  341. bindex(sp, c)
  342.     register char const *sp;
  343.     register int c;
  344. /* Function: Finds the last occurrence of character c in string sp
  345.  * and returns a pointer to the character just beyond it. If the
  346.  * character doesn't occur in the string, sp is returned.
  347.  */
  348. {
  349.     register char const *r;
  350.         r = sp;
  351.         while (*sp) {
  352.                 if (*sp++ == c) r=sp;
  353.         }
  354.         return r;
  355. }
  356.  
  357.  
  358.  
  359.     static int
  360. suffix_matches(suffix, pattern)
  361.     register char const *suffix, *pattern;
  362. {
  363.     register int c;
  364.     if (!pattern)
  365.         return true;
  366.     for (;;)
  367.         switch (*suffix++ - (c = *pattern++)) {
  368.             case 0:
  369.             if (!c)
  370.                 return true;
  371.             break;
  372.  
  373.             case 'A'-'a':
  374.             if (ctab[c] == Letter)
  375.                 break;
  376.             /* fall into */
  377.             default:
  378.             return false;
  379.         }
  380. }
  381.  
  382.  
  383.     static void
  384. InitAdmin()
  385. /* function: initializes an admin node */
  386. {
  387.     register char const *Suffix;
  388.         register int i;
  389.  
  390.     Head=0; Dbranch=0; AccessList=0; Symbols=0; Locks=0;
  391.         StrictLocks=STRICT_LOCKING;
  392.  
  393.         /* guess the comment leader from the suffix*/
  394.     Suffix = bindex(workname, '.');
  395.     if (Suffix==workname) Suffix= ""; /* empty suffix; will get default*/
  396.     for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++)
  397.         continue;
  398.     Comment.string = comtable[i].comlead;
  399.     Comment.size = strlen(comtable[i].comlead);
  400.     Expand = KEYVAL_EXPAND;
  401.     clear_buf(&Ignored);
  402.     Lexinit(); /* note: if !finptr, reads nothing; only initializes */
  403. }
  404.  
  405.  
  406.  
  407.     void
  408. bufalloc(b, size)
  409.     register struct buf *b;
  410.     size_t size;
  411. /* Ensure *B is a name buffer of at least SIZE bytes.
  412.  * *B's old contents can be freed; *B's new contents are undefined.
  413.  */
  414. {
  415.     if (b->size < size) {
  416.         if (b->size)
  417.             tfree(b->string);
  418.         else
  419.             b->size = sizeof(malloc_type);
  420.         while (b->size < size)
  421.             b->size <<= 1;
  422.         b->string = tnalloc(char, b->size);
  423.     }
  424. }
  425.  
  426.     void
  427. bufrealloc(b, size)
  428.     register struct buf *b;
  429.     size_t size;
  430. /* like bufalloc, except *B's old contents, if any, are preserved */
  431. {
  432.     if (b->size < size) {
  433.         if (!b->size)
  434.             bufalloc(b, size);
  435.         else {
  436.             while ((b->size <<= 1)  <  size)
  437.                 continue;
  438.             b->string = trealloc(char, b->string, b->size);
  439.         }
  440.     }
  441. }
  442.  
  443.     void
  444. bufautoend(b)
  445.     struct buf *b;
  446. /* Free an auto buffer at block exit. */
  447. {
  448.     if (b->size)
  449.         tfree(b->string);
  450. }
  451.  
  452.     struct cbuf
  453. bufremember(b, s)
  454.     struct buf *b;
  455.     size_t s;
  456. /*
  457.  * Free the buffer B with used size S.
  458.  * Yield a cbuf with identical contents.
  459.  * The cbuf will be reclaimed when this input file is finished.
  460.  */
  461. {
  462.     struct cbuf cb;
  463.  
  464.     if ((cb.size = s))
  465.         cb.string = fremember(trealloc(char, b->string, s));
  466.     else {
  467.         bufautoend(b); /* not really auto */
  468.         cb.string = "";
  469.     }
  470.     return cb;
  471. }
  472.  
  473.     char *
  474. bufenlarge(b, alim)
  475.     register struct buf *b;
  476.     char const **alim;
  477. /* Make *B larger.  Set *ALIM to its new limit, and yield the relocated value
  478.  * of its old limit.
  479.  */
  480. {
  481.     size_t s = b->size;
  482.     bufrealloc(b, s + 1);
  483.     *alim = b->string + b->size;
  484.     return b->string + s;
  485. }
  486.  
  487.     void
  488. bufscat(b, s)
  489.     struct buf *b;
  490.     char const *s;
  491. /* Concatenate S to B's end. */
  492. {
  493.     size_t blen  =  b->string ? strlen(b->string) : 0;
  494.     bufrealloc(b, blen+strlen(s)+1);
  495.     VOID strcpy(b->string+blen, s);
  496. }
  497.  
  498.     void
  499. bufscpy(b, s)
  500.     struct buf *b;
  501.     char const *s;
  502. /* Copy S into B. */
  503. {
  504.     bufalloc(b, strlen(s)+1);
  505.     VOID strcpy(b->string, s);
  506. }
  507.  
  508.  
  509.     char const *
  510. basefilename(p)
  511.     char const *p;
  512. /* Yield the address of the base filename of the pathname P.  */
  513. {
  514.     register char const *b = p, *q = p;
  515.     for (;;)
  516.         switch (*q++) {
  517.         case SLASHes: b = q; break;
  518.         case 0: return b;
  519.         }
  520. }
  521.  
  522.  
  523.     static size_t
  524. suffixlen(x)
  525.     char const *x;
  526. /* Yield the length of X, an RCS pathname suffix.  */
  527. {
  528.     register char const *p;
  529.  
  530.     p = x;
  531.     for (;;)
  532.         switch (*p) {
  533.         case 0: case SLASHes:
  534.             return p - x;
  535.  
  536.         default:
  537.             ++p;
  538.             continue;
  539.         }
  540. }
  541.  
  542.     char const *
  543. rcssuffix(name)
  544.     char const *name;
  545. /* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise.  */
  546. {
  547.     char const *x, *p, *nz;
  548.     size_t nl, xl;
  549.  
  550.     nl = strlen(name);
  551.     nz = name + nl;
  552.     x = suffixes;
  553.     do {
  554.         if ((xl = suffixlen(x))) {
  555.         if (xl <= nl  &&  memcmp(p = nz-xl, x, xl) == 0)
  556.             return p;
  557.         } else
  558.         for (p = name;  p < nz - rcslen;  p++)
  559.             if (
  560.             isSLASH(p[rcslen])
  561.             && (p==name || isSLASH(p[-1]))
  562.             && memcmp(p, rcsdir, rcslen) == 0
  563.             )
  564.             return nz;
  565.         x += xl;
  566.     } while (*x++);
  567.     return 0;
  568. }
  569.  
  570.     /*ARGSUSED*/ RILE *
  571. rcsreadopen(RCSpath, status, mustread)
  572.     struct buf *RCSpath;
  573.     struct stat *status;
  574.     int mustread;
  575. /* Open RCSPATH for reading and yield its FILE* descriptor.
  576.  * If successful, set *STATUS to its status.
  577.  * Pass this routine to pairnames() for read-only access to the file.  */
  578. {
  579.     return Iopen(RCSpath->string, FOPEN_RB, status);
  580. }
  581.  
  582.     static int
  583. finopen(rcsopen, mustread)
  584.     RILE *(*rcsopen)P((struct buf*,struct stat*,int));
  585.     int mustread;
  586. /*
  587.  * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
  588.  * Set finptr to the result and yield true if successful.
  589.  * RCSb holds the file's name.
  590.  * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
  591.  * Yield true if successful or if an unusual failure.
  592.  */
  593. {
  594.     int interesting, preferold;
  595.  
  596.     /*
  597.      * We prefer an old name to that of a nonexisting new RCS file,
  598.      * unless we tried locking the old name and failed.
  599.      */
  600.     preferold  =  RCSbuf.string[0] && (mustread||0<=fdlock);
  601.  
  602.     finptr = (*rcsopen)(&RCSb, &RCSstat, mustread);
  603.     interesting = finptr || (errno!=ENOENT && errno!=ENOTDIR);
  604.     if (interesting || !preferold) {
  605.         /* Use the new name.  */
  606.         RCSerrno = errno;
  607.         bufscpy(&RCSbuf, RCSb.string);
  608.     }
  609.     return interesting;
  610. }
  611.  
  612. #if pseudo_symlinks
  613.  
  614.     static int
  615. expand_symlink(RCSb, namelen)
  616.     struct buf *RCSb;
  617.     int namelen;
  618. {
  619.   struct stat test;
  620.   FILE *file;
  621.   char buffer[256];
  622.   int len;
  623.  
  624.   if (stat(RCSb->string, &test) || S_ISDIR(test.st_mode))
  625.     return 0;
  626.     
  627.   if ((file = fopen(RCSb->string, "r")) == NULL)
  628.     return 0;
  629.  
  630.   if (fgets(buffer, sizeof(buffer), file) == NULL)
  631.     return 0;
  632.  
  633.   fclose(file);
  634.   len = strlen(buffer);
  635.  
  636.   if (buffer[len - 1] == '\n')
  637.     buffer[--len] = 0;
  638.  
  639.   if (buffer[0] == '/' || buffer[0] == '\\' || 
  640.       (isalpha(buffer[0]) && buffer[1] == ':'))
  641.   {
  642.     bufrealloc(RCSb, len + namelen);
  643.     strcpy(RCSb->string, buffer);
  644.   }
  645.   else
  646.   {
  647.     bufrealloc(RCSb, strlen(RCSb->string) - rcslen + len + namelen);
  648.     strcpy(RCSb->string + strlen(RCSb->string) - rcslen, buffer);
  649.   }
  650.  
  651.   return 1;
  652. }
  653.  
  654. #endif
  655.  
  656.     static int
  657. fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread)
  658.     char const *d, *base, *x;
  659.     size_t dlen, baselen, xlen;
  660.     RILE *(*rcsopen)P((struct buf*,struct stat*,int));
  661.     int mustread;
  662. /*
  663.  * D is a directory name with length DLEN (including trailing slash).
  664.  * BASE is a filename with length BASELEN.
  665.  * X is an RCS pathname suffix with length XLEN.
  666.  * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
  667.  * Yield true if successful.
  668.  * Try dRCS/basex first; if that fails and x is nonempty, try dbasex.
  669.  * Put these potential names in RCSb.
  670.  * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
  671.  * Yield true if successful or if an unusual failure.
  672.  */
  673. {
  674.     register char *p;
  675.  
  676.     bufalloc(&RCSb, dlen + rcslen + 1 + baselen + xlen + 1);
  677.  
  678.     /* Try dRCS/basex.  */
  679.     VOID memcpy(p = RCSb.string, d, dlen);
  680.     VOID memcpy(p += dlen, rcsdir, rcslen);
  681.     p += rcslen;
  682. #if pseudo_symlinks
  683.     *p = 0;
  684.     if (expand_symlink(&RCSb, baselen + xlen + 2))
  685.       p = RCSb.string + strlen(RCSb.string);
  686. #endif
  687.     *p++ = SLASH;
  688.     VOID memcpy(p, base, baselen);
  689.     VOID memcpy(p += baselen, x, xlen);
  690.     p[xlen] = 0;
  691.     if (xlen) {
  692.         if (finopen(rcsopen, mustread))
  693.         return true;
  694.  
  695.         /* Try dbasex.  */
  696.         /* Start from scratch, because finopen() may have changed RCSb.  */
  697.         VOID memcpy(p = RCSb.string, d, dlen);
  698.         VOID memcpy(p += dlen, base, baselen);
  699.         VOID memcpy(p += baselen, x, xlen);
  700.         p[xlen] = 0;
  701.     }
  702.     return finopen(rcsopen, mustread);
  703. }
  704.  
  705.     int
  706. pairnames(argc, argv, rcsopen, mustread, quiet)
  707.     int argc;
  708.     char **argv;
  709.     RILE *(*rcsopen)P((struct buf*,struct stat*,int));
  710.     int mustread, quiet;
  711. /*
  712.  * Pair the pathnames pointed to by argv; argc indicates
  713.  * how many there are.
  714.  * Place a pointer to the RCS pathname into RCSname,
  715.  * and a pointer to the pathname of the working file into workname.
  716.  * If both are given, and workstdout
  717.  * is set, a warning is printed.
  718.  *
  719.  * If the RCS file exists, places its status into RCSstat.
  720.  *
  721.  * If the RCS file exists, it is RCSOPENed for reading, the file pointer
  722.  * is placed into finptr, and the admin-node is read in; returns 1.
  723.  * If the RCS file does not exist and MUSTREAD,
  724.  * print an error unless QUIET and return 0.
  725.  * Otherwise, initialize the admin node and return -1.
  726.  *
  727.  * 0 is returned on all errors, e.g. files that are not regular files.
  728.  */
  729. {
  730.     static struct buf tempbuf;
  731.  
  732.     register char *p, *arg, *RCS1;
  733.     char const *base, *RCSbase, *x;
  734.     int paired;
  735.     size_t arglen, dlen, baselen, xlen;
  736.  
  737.     fdlock = -1;
  738.  
  739.     if (!(arg = *argv)) return 0; /* already paired pathname */
  740.     if (*arg == '-') {
  741.         error("%s option is ignored after pathnames", arg);
  742.         return 0;
  743.     }
  744.  
  745.     base = basefilename(arg);
  746.     paired = false;
  747.  
  748.         /* first check suffix to see whether it is an RCS file or not */
  749.     if ((x = rcssuffix(arg)))
  750.     {
  751.         /* RCS pathname given */
  752.         RCS1 = arg;
  753.         RCSbase = base;
  754.         baselen = x - base;
  755.         if (
  756.             1 < argc  &&
  757.             !rcssuffix(workname = p = argv[1])  &&
  758.             baselen <= (arglen = strlen(p))  &&
  759.             ((p+=arglen-baselen) == workname  ||  isSLASH(p[-1])) &&
  760.             memcmp(base, p, baselen) == 0
  761.         ) {
  762.             argv[1] = 0;
  763.             paired = true;
  764.         } else {
  765.             bufscpy(&tempbuf, base);
  766.             workname = p = tempbuf.string;
  767.             p[baselen] = 0;
  768.         }
  769.         } else {
  770.                 /* working file given; now try to find RCS file */
  771.         workname = arg;
  772.         baselen = strlen(base);
  773.         /* Derive RCS pathname.  */
  774.         if (
  775.             1 < argc  &&
  776.             (x = rcssuffix(RCS1 = argv[1]))  &&
  777.             baselen  <=  (x - RCS1)  &&
  778.             ((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) &&
  779.             memcmp(base, RCSbase, baselen) == 0
  780.         ) {
  781.             argv[1] = 0;
  782.             paired = true;
  783.         } else
  784.             RCSbase = RCS1 = 0;
  785.         }
  786.     /* Now we have a (tentative) RCS pathname in RCS1 and workname.  */
  787.         /* Second, try to find the right RCS file */
  788.     if (RCSbase!=RCS1) {
  789.                 /* a path for RCSfile is given; single RCS file to look for */
  790.         bufscpy(&RCSbuf, RCS1);
  791.         finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread);
  792.         RCSerrno = errno;
  793.         } else {
  794.         bufscpy(&RCSbuf, "");
  795.         if (RCS1)
  796.             /* RCS filename was given without path.  */
  797.             VOID fin2open(arg, (size_t)0, RCSbase, baselen,
  798.                 x, strlen(x), rcsopen, mustread
  799.             );
  800.         else {
  801.             /* No RCS pathname was given.  */
  802.             /* Try each suffix in turn.  */
  803.             dlen = base-arg;
  804.             x = suffixes;
  805.             while (! fin2open(arg, dlen, base, baselen,
  806.                     x, xlen=suffixlen(x), rcsopen, mustread
  807.             )) {
  808.                 x += xlen;
  809.                 if (!*x++)
  810.                     break;
  811.             }
  812.         }
  813.         }
  814.     RCSname = p = RCSbuf.string;
  815.     if (finptr) {
  816.         if (!S_ISREG(RCSstat.st_mode)) {
  817.             error("%s isn't a regular file -- ignored", p);
  818.                         return 0;
  819.                 }
  820.                 Lexinit(); getadmin();
  821.     } else {
  822.         if (RCSerrno!=ENOENT || mustread || fdlock<0) {
  823.             if (RCSerrno == EEXIST)
  824.                 error("RCS file %s is in use", p);
  825.             else if (!quiet || RCSerrno!=ENOENT)
  826.                 enerror(RCSerrno, p);
  827.             return 0;
  828.         }
  829.                 InitAdmin();
  830.         };
  831.  
  832.     if (paired && workstdout)
  833.         workwarn("Working file ignored due to -p option");
  834.  
  835.     prevkeys = false;
  836.     return finptr ? 1 : -1;
  837. }
  838.  
  839.  
  840.     char const *
  841. getfullRCSname()
  842. /*
  843.  * Return a pointer to the full pathname of the RCS file.
  844.  * Remove leading `./'.
  845.  */
  846. {
  847.     if (ROOTPATH(RCSname)) {
  848.         return RCSname;
  849.     } else {
  850.         static struct buf rcsbuf;
  851. #        if needs_getabsname
  852.         bufalloc(&rcsbuf, SIZEABLE_PATH + 1);
  853.         while (getabsname(RCSname, rcsbuf.string, rcsbuf.size) != 0)
  854.             if (errno == ERANGE)
  855.             bufalloc(&rcsbuf, rcsbuf.size<<1);
  856.             else
  857.             efaterror("getabsname");
  858. #        else
  859.         static char const *wdptr;
  860.         static struct buf wdbuf;
  861.         static size_t wdlen;
  862.  
  863.         register char const *r;
  864.         register size_t dlen;
  865.         register char *d;
  866.         register char const *wd;
  867.  
  868.         if (!(wd = wdptr)) {
  869.             /* Get working directory for the first time.  */
  870.             char *PWD = cgetenv("PWD");
  871.             struct stat PWDstat, dotstat;
  872.             if (! (
  873.             (d = PWD) &&
  874.             ROOTPATH(PWD) &&
  875.             stat(PWD, &PWDstat) == 0 &&
  876.             stat(".", &dotstat) == 0 &&
  877.             same_file(PWDstat, dotstat, 1)
  878.             )) {
  879.             bufalloc(&wdbuf, SIZEABLE_PATH + 1);
  880. #            if has_getcwd || !has_getwd
  881.                 while (!(d = getcwd(wdbuf.string, wdbuf.size)))
  882.                 if (errno == ERANGE)
  883.                     bufalloc(&wdbuf, wdbuf.size<<1);
  884.                 else if ((d = PWD))
  885.                     break;
  886.                 else
  887.                     efaterror("getcwd");
  888. #            else
  889.                 d = getwd(wdbuf.string);
  890.                 if (!d  &&  !(d = PWD))
  891.                 efaterror("getwd");
  892. #            endif
  893.             }
  894.             wdlen = dir_useful_len(d);
  895.             d[wdlen] = 0;
  896.             wdptr = wd = d;
  897.                 }
  898.         /*
  899.         * Remove leading `./'s from RCSname.
  900.         * Do not try to handle `../', since removing it may yield
  901.         * the wrong answer in the presence of symbolic links.
  902.         */
  903.         for (r = RCSname;  r[0]=='.' && isSLASH(r[1]);  r += 2)
  904.             /* `.////' is equivalent to `./'.  */
  905.             while (isSLASH(r[2]))
  906.             r++;
  907.         /* Build full pathname.  */
  908.         dlen = wdlen;
  909.         bufalloc(&rcsbuf, dlen + strlen(r) + 2);
  910.         d = rcsbuf.string;
  911.         VOID memcpy(d, wd, dlen);
  912.         d += dlen;
  913.         *d++ = SLASH;
  914.         VOID strcpy(d, r);
  915. #        endif
  916.         return rcsbuf.string;
  917.         }
  918. }
  919.  
  920.     static size_t
  921. dir_useful_len(d)
  922.     char const *d;
  923. /*
  924. * D names a directory; yield the number of characters of D's useful part.
  925. * To create a file in D, append a SLASH and a file name to D's useful part.
  926. * Ignore trailing slashes if possible; not only are they ugly,
  927. * but some non-Posix systems misbehave unless the slashes are omitted.
  928. */
  929. {
  930. #    ifndef SLASHSLASH_is_SLASH
  931. #    define SLASHSLASH_is_SLASH 0
  932. #    endif
  933.     size_t dlen = strlen(d);
  934.     if (!SLASHSLASH_is_SLASH && dlen==2 && isSLASH(d[0]) && isSLASH(d[1]))
  935.         --dlen;
  936.     else
  937.         while (dlen && isSLASH(d[dlen-1]))
  938.         --dlen;
  939.     return dlen;
  940. }
  941.  
  942. #ifndef isSLASH
  943.     int
  944. isSLASH(c)
  945.     int c;
  946. {
  947.     switch (c) {
  948.         case SLASHes:
  949.         return true;
  950.         default:
  951.         return false;
  952.     }
  953. }
  954. #endif
  955.  
  956.  
  957. #if !has_getcwd && !has_getwd
  958.  
  959.     char *
  960. getcwd(path, size)
  961.     char *path;
  962.     size_t size;
  963. {
  964.     static char const usrbinpwd[] = "/usr/bin/pwd";
  965. #    define binpwd (usrbinpwd+4)
  966.  
  967.     register FILE *fp;
  968.     register int c;
  969.     register char *p, *lim;
  970.     int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus;
  971.     pid_t child;
  972.  
  973.     if (!size) {
  974.         errno = EINVAL;
  975.         return 0;
  976.     }
  977.     if (pipe(fd) != 0)
  978.         return 0;
  979. #    if bad_wait_if_SIGCHLD_ignored
  980. #        ifndef SIGCHLD
  981. #        define SIGCHLD SIGCLD
  982. #        endif
  983.         VOID signal(SIGCHLD, SIG_DFL);
  984. #    endif
  985.     if (!(child = vfork())) {
  986.         if (
  987.             close(fd[0]) == 0 &&
  988.             (fd[1] == STDOUT_FILENO ||
  989. #                ifdef F_DUPFD
  990.                     (VOID close(STDOUT_FILENO),
  991.                     fcntl(fd[1], F_DUPFD, STDOUT_FILENO))
  992. #                else
  993.                     dup2(fd[1], STDOUT_FILENO)
  994. #                endif
  995.                 == STDOUT_FILENO &&
  996.                 close(fd[1]) == 0
  997.             )
  998.         ) {
  999.             VOID close(STDERR_FILENO);
  1000.             VOID execl(binpwd, binpwd, (char *)0);
  1001.             VOID execl(usrbinpwd, usrbinpwd, (char *)0);
  1002.         }
  1003.         _exit(EXIT_FAILURE);
  1004.     }
  1005.     e = errno;
  1006.     closeerror = close(fd[1]);
  1007.     closeerrno = errno;
  1008.     fp = 0;
  1009.     readerror = toolong = wstatus = 0;
  1010.     p = path;
  1011.     if (0 <= child) {
  1012.         fp = fdopen(fd[0], "r");
  1013.         e = errno;
  1014.         if (fp) {
  1015.             lim = p + size;
  1016.             for (p = path;  ;  *p++ = c) {
  1017.                 if ((c=getc(fp)) < 0) {
  1018.                     if (feof(fp))
  1019.                         break;
  1020.                     if (ferror(fp)) {
  1021.                         readerror = 1;
  1022.                         e = errno;
  1023.                         break;
  1024.                     }
  1025.                 }
  1026.                 if (p == lim) {
  1027.                     toolong = 1;
  1028.                     break;
  1029.                 }
  1030.             }
  1031.         }
  1032. #        if has_waitpid
  1033.             if (waitpid(child, &wstatus, 0) < 0)
  1034.                 wstatus = 1;
  1035. #        else
  1036.             {
  1037.                 pid_t w;
  1038.                 do {
  1039.                     if ((w = wait(&wstatus)) < 0) {
  1040.                         wstatus = 1;
  1041.                         break;
  1042.                     }
  1043.                 } while (w != child);
  1044.             }
  1045. #        endif
  1046.     }
  1047.     if (!fp) {
  1048.         VOID close(fd[0]);
  1049.         errno = e;
  1050.         return 0;
  1051.     }
  1052.     if (fclose(fp) != 0)
  1053.         return 0;
  1054.     if (readerror) {
  1055.         errno = e;
  1056.         return 0;
  1057.     }
  1058.     if (closeerror) {
  1059.         errno = closeerrno;
  1060.         return 0;
  1061.     }
  1062.     if (toolong) {
  1063.         errno = ERANGE;
  1064.         return 0;
  1065.     }
  1066.     if (wstatus  ||  p == path  ||  *--p != '\n') {
  1067.         errno = EACCES;
  1068.         return 0;
  1069.     }
  1070.     *p = '\0';
  1071.     return path;
  1072. }
  1073. #endif
  1074.  
  1075.  
  1076. #ifdef PAIRTEST
  1077. /* test program for pairnames() and getfullRCSname() */
  1078.  
  1079. char const cmdid[] = "pair";
  1080.  
  1081. main(argc, argv)
  1082. int argc; char *argv[];
  1083. {
  1084.         int result;
  1085.     int initflag;
  1086.     quietflag = initflag = false;
  1087.  
  1088.         while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
  1089.                 switch ((*argv)[1]) {
  1090.  
  1091.         case 'p':       workstdout = stdout;
  1092.                                 break;
  1093.                 case 'i':       initflag=true;
  1094.                                 break;
  1095.                 case 'q':       quietflag=true;
  1096.                                 break;
  1097.                 default:        error("unknown option: %s", *argv);
  1098.                                 break;
  1099.                 }
  1100.         }
  1101.  
  1102.         do {
  1103.         RCSname = workname = 0;
  1104.         result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag);
  1105.                 if (result!=0) {
  1106.             diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n",
  1107.                  RCSname, workname, getfullRCSname()
  1108.             );
  1109.                 }
  1110.                 switch (result) {
  1111.                         case 0: continue; /* already paired file */
  1112.  
  1113.                         case 1: if (initflag) {
  1114.                     rcserror("already exists");
  1115.                                 } else {
  1116.                     diagnose("RCS file %s exists\n", RCSname);
  1117.                                 }
  1118.                 Ifclose(finptr);
  1119.                                 break;
  1120.  
  1121.             case -1:diagnose("RCS file doesn't exist\n");
  1122.                                 break;
  1123.                 }
  1124.  
  1125.         } while (++argv, --argc>=1);
  1126.  
  1127. }
  1128.  
  1129.     void
  1130. exiterr()
  1131. {
  1132.     dirtempunlink();
  1133.     tempunlink();
  1134.     _exit(EXIT_FAILURE);
  1135. }
  1136. #endif
  1137.