xref: /original-bsd/contrib/rcs-V5.6/src/rcsfnms.c (revision 0f30e20f)
15b4e6cb2Sbostic /*
25b4e6cb2Sbostic  *                     RCS file name handling
35b4e6cb2Sbostic  */
45b4e6cb2Sbostic /****************************************************************************
55b4e6cb2Sbostic  *                     creation and deletion of /tmp temporaries
65b4e6cb2Sbostic  *                     pairing of RCS file names and working file names.
75b4e6cb2Sbostic  *                     Testprogram: define PAIRTEST
85b4e6cb2Sbostic  ****************************************************************************
95b4e6cb2Sbostic  */
105b4e6cb2Sbostic 
115b4e6cb2Sbostic /* Copyright (C) 1982, 1988, 1989 Walter Tichy
125b4e6cb2Sbostic    Copyright 1990, 1991 by Paul Eggert
135b4e6cb2Sbostic    Distributed under license by the Free Software Foundation, Inc.
145b4e6cb2Sbostic 
155b4e6cb2Sbostic This file is part of RCS.
165b4e6cb2Sbostic 
175b4e6cb2Sbostic RCS is free software; you can redistribute it and/or modify
185b4e6cb2Sbostic it under the terms of the GNU General Public License as published by
195b4e6cb2Sbostic the Free Software Foundation; either version 2, or (at your option)
205b4e6cb2Sbostic any later version.
215b4e6cb2Sbostic 
225b4e6cb2Sbostic RCS is distributed in the hope that it will be useful,
235b4e6cb2Sbostic but WITHOUT ANY WARRANTY; without even the implied warranty of
245b4e6cb2Sbostic MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
255b4e6cb2Sbostic GNU General Public License for more details.
265b4e6cb2Sbostic 
275b4e6cb2Sbostic You should have received a copy of the GNU General Public License
285b4e6cb2Sbostic along with RCS; see the file COPYING.  If not, write to
295b4e6cb2Sbostic the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
305b4e6cb2Sbostic 
315b4e6cb2Sbostic Report problems and direct all questions to:
325b4e6cb2Sbostic 
335b4e6cb2Sbostic     rcs-bugs@cs.purdue.edu
345b4e6cb2Sbostic 
355b4e6cb2Sbostic */
365b4e6cb2Sbostic 
375b4e6cb2Sbostic 
385b4e6cb2Sbostic 
395b4e6cb2Sbostic 
405b4e6cb2Sbostic /* $Log: rcsfnms.c,v $
415b4e6cb2Sbostic  * Revision 5.8  1991/09/24  00:28:40  eggert
425b4e6cb2Sbostic  * Don't export bindex().
435b4e6cb2Sbostic  *
445b4e6cb2Sbostic  * Revision 5.7  1991/08/19  03:13:55  eggert
455b4e6cb2Sbostic  * Fix messages when rcswriteopen fails.
465b4e6cb2Sbostic  * Look in $TMP and $TEMP if $TMPDIR isn't set.  Tune.
475b4e6cb2Sbostic  *
485b4e6cb2Sbostic  * Revision 5.6  1991/04/21  11:58:23  eggert
495b4e6cb2Sbostic  * Fix errno bugs.  Add -x, RCSINIT, MS-DOS support.
505b4e6cb2Sbostic  *
515b4e6cb2Sbostic  * Revision 5.5  1991/02/26  17:48:38  eggert
525b4e6cb2Sbostic  * Fix setuid bug.  Support new link behavior.
535b4e6cb2Sbostic  * Define more portable getcwd().
545b4e6cb2Sbostic  *
555b4e6cb2Sbostic  * Revision 5.4  1990/11/01  05:03:43  eggert
565b4e6cb2Sbostic  * Permit arbitrary data in comment leaders.
575b4e6cb2Sbostic  *
585b4e6cb2Sbostic  * Revision 5.3  1990/09/14  22:56:16  hammer
595b4e6cb2Sbostic  * added more filename extensions and their comment leaders
605b4e6cb2Sbostic  *
615b4e6cb2Sbostic  * Revision 5.2  1990/09/04  08:02:23  eggert
625b4e6cb2Sbostic  * Fix typo when !RCSSEP.
635b4e6cb2Sbostic  *
645b4e6cb2Sbostic  * Revision 5.1  1990/08/29  07:13:59  eggert
655b4e6cb2Sbostic  * Work around buggy compilers with defective argument promotion.
665b4e6cb2Sbostic  *
675b4e6cb2Sbostic  * Revision 5.0  1990/08/22  08:12:50  eggert
685b4e6cb2Sbostic  * Ignore signals when manipulating the semaphore file.
695b4e6cb2Sbostic  * Modernize list of file name extensions.
705b4e6cb2Sbostic  * Permit paths of arbitrary length.  Beware file names beginning with "-".
715b4e6cb2Sbostic  * Remove compile-time limits; use malloc instead.
725b4e6cb2Sbostic  * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
735b4e6cb2Sbostic  * Ansify and Posixate.
745b4e6cb2Sbostic  * Don't use access().  Fix test for non-regular files.  Tune.
755b4e6cb2Sbostic  *
765b4e6cb2Sbostic  * Revision 4.8  89/05/01  15:09:41  narten
775b4e6cb2Sbostic  * changed getwd to not stat empty directories.
785b4e6cb2Sbostic  *
795b4e6cb2Sbostic  * Revision 4.7  88/08/09  19:12:53  eggert
805b4e6cb2Sbostic  * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint.
815b4e6cb2Sbostic  *
825b4e6cb2Sbostic  * Revision 4.6  87/12/18  11:40:23  narten
835b4e6cb2Sbostic  * additional file types added from 4.3 BSD version, and SPARC assembler
845b4e6cb2Sbostic  * comment character added. Also, more lint cleanups. (Guy Harris)
855b4e6cb2Sbostic  *
865b4e6cb2Sbostic  * Revision 4.5  87/10/18  10:34:16  narten
875b4e6cb2Sbostic  * Updating version numbers. Changes relative to 1.1 actually relative
885b4e6cb2Sbostic  * to verion 4.3
895b4e6cb2Sbostic  *
905b4e6cb2Sbostic  * Revision 1.3  87/03/27  14:22:21  jenkins
915b4e6cb2Sbostic  * Port to suns
925b4e6cb2Sbostic  *
935b4e6cb2Sbostic  * Revision 1.2  85/06/26  07:34:28  svb
945b4e6cb2Sbostic  * Comment leader '% ' for '*.tex' files added.
955b4e6cb2Sbostic  *
965b4e6cb2Sbostic  * Revision 4.3  83/12/15  12:26:48  wft
975b4e6cb2Sbostic  * Added check for KDELIM in file names to pairfilenames().
985b4e6cb2Sbostic  *
995b4e6cb2Sbostic  * Revision 4.2  83/12/02  22:47:45  wft
1005b4e6cb2Sbostic  * Added csh, red, and sl file name suffixes.
1015b4e6cb2Sbostic  *
1025b4e6cb2Sbostic  * Revision 4.1  83/05/11  16:23:39  wft
1035b4e6cb2Sbostic  * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
1045b4e6cb2Sbostic  * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
1055b4e6cb2Sbostic  * 2. added getting the file status of RCS and working files;
1065b4e6cb2Sbostic  * 3. added ignoring of directories.
1075b4e6cb2Sbostic  *
1085b4e6cb2Sbostic  * Revision 3.7  83/05/11  15:01:58  wft
1095b4e6cb2Sbostic  * Added comtable[] which pairs file name suffixes with comment leaders;
1105b4e6cb2Sbostic  * updated InitAdmin() accordingly.
1115b4e6cb2Sbostic  *
1125b4e6cb2Sbostic  * Revision 3.6  83/04/05  14:47:36  wft
1135b4e6cb2Sbostic  * fixed Suffix in InitAdmin().
1145b4e6cb2Sbostic  *
1155b4e6cb2Sbostic  * Revision 3.5  83/01/17  18:01:04  wft
1165b4e6cb2Sbostic  * Added getwd() and rename(); these can be removed by defining
1175b4e6cb2Sbostic  * V4_2BSD, since they are not needed in 4.2 bsd.
1185b4e6cb2Sbostic  * Changed sys/param.h to sys/types.h.
1195b4e6cb2Sbostic  *
1205b4e6cb2Sbostic  * Revision 3.4  82/12/08  21:55:20  wft
1215b4e6cb2Sbostic  * removed unused variable.
1225b4e6cb2Sbostic  *
1235b4e6cb2Sbostic  * Revision 3.3  82/11/28  20:31:37  wft
1245b4e6cb2Sbostic  * Changed mktempfile() to store the generated file names.
1255b4e6cb2Sbostic  * Changed getfullRCSname() to store the file and pathname, and to
1265b4e6cb2Sbostic  * delete leading "../" and "./".
1275b4e6cb2Sbostic  *
1285b4e6cb2Sbostic  * Revision 3.2  82/11/12  14:29:40  wft
1295b4e6cb2Sbostic  * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
1305b4e6cb2Sbostic  * checksuffix(), checkfullpath(). Semaphore name generation updated.
1315b4e6cb2Sbostic  * mktempfile() now checks for nil path; freefilename initialized properly.
1325b4e6cb2Sbostic  * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
1335b4e6cb2Sbostic  * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
1345b4e6cb2Sbostic  *
1355b4e6cb2Sbostic  * Revision 3.1  82/10/18  14:51:28  wft
1365b4e6cb2Sbostic  * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
1375b4e6cb2Sbostic  * renamed checkpath() to checkfullpath().
1385b4e6cb2Sbostic  */
1395b4e6cb2Sbostic 
1405b4e6cb2Sbostic 
1415b4e6cb2Sbostic #include "rcsbase.h"
1425b4e6cb2Sbostic 
1435b4e6cb2Sbostic libId(fnmsId, "$Id: rcsfnms.c,v 5.8 1991/09/24 00:28:40 eggert Exp $")
1445b4e6cb2Sbostic 
1455b4e6cb2Sbostic char const *RCSfilename;
1465b4e6cb2Sbostic char *workfilename;
1475b4e6cb2Sbostic FILE *workstdout;
1485b4e6cb2Sbostic struct stat RCSstat;
1495b4e6cb2Sbostic char const *suffixes;
1505b4e6cb2Sbostic 
1515b4e6cb2Sbostic static char const rcsdir[] = "RCS";
1525b4e6cb2Sbostic #define rcsdirlen (sizeof(rcsdir)-1)
1535b4e6cb2Sbostic 
1545b4e6cb2Sbostic static struct buf RCSbuf, RCSb;
1555b4e6cb2Sbostic static int RCSerrno;
1565b4e6cb2Sbostic 
1575b4e6cb2Sbostic 
1585b4e6cb2Sbostic /* Temp file names to be unlinked when done, if they are not nil.  */
1595b4e6cb2Sbostic #define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */
1605b4e6cb2Sbostic static char *volatile tfnames[TEMPNAMES];
1615b4e6cb2Sbostic 
1625b4e6cb2Sbostic 
1635b4e6cb2Sbostic struct compair {
1645b4e6cb2Sbostic 	char const *suffix, *comlead;
1655b4e6cb2Sbostic };
1665b4e6cb2Sbostic 
1675b4e6cb2Sbostic static struct compair const comtable[] = {
1685b4e6cb2Sbostic /* comtable pairs each filename suffix with a comment leader. The comment   */
1695b4e6cb2Sbostic /* leader is placed before each line generated by the $Log keyword. This    */
1705b4e6cb2Sbostic /* table is used to guess the proper comment leader from the working file's */
1715b4e6cb2Sbostic /* suffix during initial ci (see InitAdmin()). Comment leaders are needed   */
1725b4e6cb2Sbostic /* for languages without multiline comments; for others they are optional.  */
1735b4e6cb2Sbostic 	"a",   "-- ",   /* Ada         */
1745b4e6cb2Sbostic 	"ada", "-- ",
1755b4e6cb2Sbostic 	"asm", ";; ",	/* assembler (MS-DOS) */
1765b4e6cb2Sbostic 	"bat", ":: ",	/* batch (MS-DOS) */
1775b4e6cb2Sbostic         "c",   " * ",   /* C           */
1785b4e6cb2Sbostic 	"c++", "// ",	/* C++ in all its infinite guises */
1795b4e6cb2Sbostic 	"cc",  "// ",
1805b4e6cb2Sbostic 	"cpp", "// ",
1815b4e6cb2Sbostic 	"cxx", "// ",
1825b4e6cb2Sbostic 	"cl",  ";;; ",  /* Common Lisp */
1835b4e6cb2Sbostic 	"cmd", ":: ",	/* command (OS/2) */
1845b4e6cb2Sbostic 	"cmf", "c ",	/* CM Fortran  */
1855b4e6cb2Sbostic 	"cs",  " * ",	/* C*          */
1865b4e6cb2Sbostic 	"el",  "; ",    /* Emacs Lisp  */
1875b4e6cb2Sbostic 	"f",   "c ",    /* Fortran     */
1885b4e6cb2Sbostic 	"for", "c ",
1895b4e6cb2Sbostic         "h",   " * ",   /* C-header    */
1905b4e6cb2Sbostic 	"hpp", "// ",	/* C++ header  */
1915b4e6cb2Sbostic 	"hxx", "// ",
1925b4e6cb2Sbostic         "l",   " * ",   /* lex      NOTE: conflict between lex and franzlisp */
1935b4e6cb2Sbostic 	"lisp",";;; ",	/* Lucid Lisp  */
1945b4e6cb2Sbostic 	"lsp", ";; ",	/* Microsoft Lisp */
1955b4e6cb2Sbostic 	"mac", ";; ",	/* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
1965b4e6cb2Sbostic 	"me",  ".\\\" ",/* me-macros   t/nroff*/
1975b4e6cb2Sbostic 	"ml",  "; ",    /* mocklisp    */
1985b4e6cb2Sbostic 	"mm",  ".\\\" ",/* mm-macros   t/nroff*/
1995b4e6cb2Sbostic 	"ms",  ".\\\" ",/* ms-macros   t/nroff*/
2005b4e6cb2Sbostic 	"p",   " * ",   /* Pascal      */
2015b4e6cb2Sbostic 	"pas", " * ",
2025b4e6cb2Sbostic 	"pl",  "% ",	/* Prolog      */
2035b4e6cb2Sbostic 	"tex", "% ",	/* TeX	       */
2045b4e6cb2Sbostic         "y",   " * ",   /* yacc        */
2055b4e6cb2Sbostic 	nil,   "# "     /* default for unknown suffix; must always be last */
2065b4e6cb2Sbostic };
2075b4e6cb2Sbostic 
2085b4e6cb2Sbostic #if has_mktemp
2095b4e6cb2Sbostic 	static char const *
tmp()2105b4e6cb2Sbostic tmp()
2115b4e6cb2Sbostic /* Yield the name of the tmp directory.  */
2125b4e6cb2Sbostic {
2135b4e6cb2Sbostic 	static char const *s;
2145b4e6cb2Sbostic 	if (!s
2155b4e6cb2Sbostic 		&&  !(s = cgetenv("TMPDIR"))	/* Unix tradition */
2165b4e6cb2Sbostic 		&&  !(s = cgetenv("TMP"))	/* DOS tradition */
2175b4e6cb2Sbostic 		&&  !(s = cgetenv("TEMP"))	/* another DOS tradition */
2185b4e6cb2Sbostic 	)
2195b4e6cb2Sbostic 		s = TMPDIR;
2205b4e6cb2Sbostic 	return s;
2215b4e6cb2Sbostic }
2225b4e6cb2Sbostic #endif
2235b4e6cb2Sbostic 
2245b4e6cb2Sbostic 	char const *
maketemp(n)2255b4e6cb2Sbostic maketemp(n)
2265b4e6cb2Sbostic 	int n;
2275b4e6cb2Sbostic /* Create a unique filename using n and the process id and store it
2285b4e6cb2Sbostic  * into the nth slot in tfnames.
2295b4e6cb2Sbostic  * Because of storage in tfnames, tempunlink() can unlink the file later.
2305b4e6cb2Sbostic  * Returns a pointer to the filename created.
2315b4e6cb2Sbostic  */
2325b4e6cb2Sbostic {
2335b4e6cb2Sbostic 	char *p;
2345b4e6cb2Sbostic 	char const *t = tfnames[n];
2355b4e6cb2Sbostic 
2365b4e6cb2Sbostic 	if (t)
2375b4e6cb2Sbostic 		return t;
2385b4e6cb2Sbostic 
2395b4e6cb2Sbostic 	catchints();
2405b4e6cb2Sbostic 	{
2415b4e6cb2Sbostic #	if has_mktemp
2425b4e6cb2Sbostic 	    char const *tp = tmp();
2435b4e6cb2Sbostic 	    p = testalloc(strlen(tp) + 10);
2445b4e6cb2Sbostic 	    VOID sprintf(p, "%s%cT%cXXXXXX", tp, SLASH, '0'+n);
2455b4e6cb2Sbostic 	    if (!mktemp(p) || !*p)
2465b4e6cb2Sbostic 		faterror("can't make temporary file name `%s%cT%cXXXXXX'",
2475b4e6cb2Sbostic 			tp, SLASH, '0'+n
2485b4e6cb2Sbostic 		);
2495b4e6cb2Sbostic #	else
2505b4e6cb2Sbostic 	    static char tfnamebuf[TEMPNAMES][L_tmpnam];
2515b4e6cb2Sbostic 	    p = tfnamebuf[n];
2525b4e6cb2Sbostic 	    if (!tmpnam(p) || !*p)
2535b4e6cb2Sbostic #		ifdef P_tmpdir
2545b4e6cb2Sbostic 		    faterror("can't make temporary file name `%s...'",P_tmpdir);
2555b4e6cb2Sbostic #		else
2565b4e6cb2Sbostic 		    faterror("can't make temporary file name");
2575b4e6cb2Sbostic #		endif
2585b4e6cb2Sbostic #	endif
2595b4e6cb2Sbostic 	}
2605b4e6cb2Sbostic 
2615b4e6cb2Sbostic 	tfnames[n] = p;
2625b4e6cb2Sbostic 	return p;
2635b4e6cb2Sbostic }
2645b4e6cb2Sbostic 
2655b4e6cb2Sbostic 	void
tempunlink()2665b4e6cb2Sbostic tempunlink()
2675b4e6cb2Sbostic /* Clean up maketemp() files.  May be invoked by signal handler.
2685b4e6cb2Sbostic  */
2695b4e6cb2Sbostic {
2705b4e6cb2Sbostic 	register int i;
2715b4e6cb2Sbostic 	register char *p;
2725b4e6cb2Sbostic 
2735b4e6cb2Sbostic 	for (i = TEMPNAMES;  0 <= --i;  )
2745b4e6cb2Sbostic 	    if ((p = tfnames[i])) {
2755b4e6cb2Sbostic 		VOID unlink(p);
2765b4e6cb2Sbostic 		/*
2775b4e6cb2Sbostic 		 * We would tfree(p) here,
2785b4e6cb2Sbostic 		 * but this might dump core if we're handing a signal.
2795b4e6cb2Sbostic 		 * We're about to exit anyway, so we won't bother.
2805b4e6cb2Sbostic 		 */
2815b4e6cb2Sbostic 		tfnames[i] = 0;
2825b4e6cb2Sbostic 	    }
2835b4e6cb2Sbostic }
2845b4e6cb2Sbostic 
2855b4e6cb2Sbostic 
2865b4e6cb2Sbostic 	static char const *
bindex(sp,ch)2875b4e6cb2Sbostic bindex(sp,ch)
2885b4e6cb2Sbostic 	register char const *sp;
2895b4e6cb2Sbostic 	int ch;
2905b4e6cb2Sbostic /* Function: Finds the last occurrence of character c in string sp
2915b4e6cb2Sbostic  * and returns a pointer to the character just beyond it. If the
2925b4e6cb2Sbostic  * character doesn't occur in the string, sp is returned.
2935b4e6cb2Sbostic  */
2945b4e6cb2Sbostic {
2955b4e6cb2Sbostic 	register char const c=ch, *r;
2965b4e6cb2Sbostic         r = sp;
2975b4e6cb2Sbostic         while (*sp) {
2985b4e6cb2Sbostic                 if (*sp++ == c) r=sp;
2995b4e6cb2Sbostic         }
3005b4e6cb2Sbostic         return r;
3015b4e6cb2Sbostic }
3025b4e6cb2Sbostic 
3035b4e6cb2Sbostic 
3045b4e6cb2Sbostic 
3055b4e6cb2Sbostic 	static int
suffix_matches(suffix,pattern)3065b4e6cb2Sbostic suffix_matches(suffix, pattern)
3075b4e6cb2Sbostic 	register char const *suffix, *pattern;
3085b4e6cb2Sbostic {
3095b4e6cb2Sbostic 	register int c;
3105b4e6cb2Sbostic 	if (!pattern)
3115b4e6cb2Sbostic 		return true;
3125b4e6cb2Sbostic 	for (;;)
3135b4e6cb2Sbostic 		switch (*suffix++ - (c = *pattern++)) {
3145b4e6cb2Sbostic 		    case 0:
3155b4e6cb2Sbostic 			if (!c)
3165b4e6cb2Sbostic 				return true;
3175b4e6cb2Sbostic 			break;
3185b4e6cb2Sbostic 
3195b4e6cb2Sbostic 		    case 'A'-'a':
3205b4e6cb2Sbostic 			if (ctab[c] == Letter)
3215b4e6cb2Sbostic 				break;
3225b4e6cb2Sbostic 			/* fall into */
3235b4e6cb2Sbostic 		    default:
3245b4e6cb2Sbostic 			return false;
3255b4e6cb2Sbostic 		}
3265b4e6cb2Sbostic }
3275b4e6cb2Sbostic 
3285b4e6cb2Sbostic 
3295b4e6cb2Sbostic 	static void
InitAdmin()3305b4e6cb2Sbostic InitAdmin()
3315b4e6cb2Sbostic /* function: initializes an admin node */
3325b4e6cb2Sbostic {
3335b4e6cb2Sbostic 	register char const *Suffix;
3345b4e6cb2Sbostic         register int i;
3355b4e6cb2Sbostic 
3365b4e6cb2Sbostic 	Head=nil; Dbranch=nil; AccessList=nil; Symbols=nil; Locks=nil;
3375b4e6cb2Sbostic         StrictLocks=STRICT_LOCKING;
3385b4e6cb2Sbostic 
3395b4e6cb2Sbostic         /* guess the comment leader from the suffix*/
3405b4e6cb2Sbostic         Suffix=bindex(workfilename, '.');
3415b4e6cb2Sbostic         if (Suffix==workfilename) Suffix= ""; /* empty suffix; will get default*/
3425b4e6cb2Sbostic 	for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++)
3435b4e6cb2Sbostic 		;
3445b4e6cb2Sbostic 	Comment.string = comtable[i].comlead;
3455b4e6cb2Sbostic 	Comment.size = strlen(comtable[i].comlead);
3465b4e6cb2Sbostic 	Lexinit(); /* note: if !finptr, reads nothing; only initializes */
3475b4e6cb2Sbostic }
3485b4e6cb2Sbostic 
3495b4e6cb2Sbostic 
350*0f30e20fSbostic #if defined(_POSIX_NO_TRUNC)
3515b4e6cb2Sbostic #	define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 0
3525b4e6cb2Sbostic #else
3535b4e6cb2Sbostic #	define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 1
3545b4e6cb2Sbostic #endif
3555b4e6cb2Sbostic 
3565b4e6cb2Sbostic #if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED
3575b4e6cb2Sbostic #ifdef NAME_MAX
3585b4e6cb2Sbostic #	define filenametoolong(path) (NAME_MAX < strlen(basename(path)))
3595b4e6cb2Sbostic #else
3605b4e6cb2Sbostic 	static int
filenametoolong(path)3615b4e6cb2Sbostic filenametoolong(path)
3625b4e6cb2Sbostic 	char *path;
3635b4e6cb2Sbostic /* Yield true if the last file name in PATH is too long. */
3645b4e6cb2Sbostic {
3655b4e6cb2Sbostic 	static unsigned long dot_namemax;
3665b4e6cb2Sbostic 
3675b4e6cb2Sbostic 	register size_t namelen;
3685b4e6cb2Sbostic 	register char *base;
3695b4e6cb2Sbostic 	register unsigned long namemax;
3705b4e6cb2Sbostic 
3715b4e6cb2Sbostic 	base = path + dirlen(path);
3725b4e6cb2Sbostic 	namelen = strlen(base);
3735b4e6cb2Sbostic 	if (namelen <= _POSIX_NAME_MAX) /* fast check for shorties */
3745b4e6cb2Sbostic 		return false;
3755b4e6cb2Sbostic 	if (base != path) {
3765b4e6cb2Sbostic 		*--base = 0;
3775b4e6cb2Sbostic 		namemax = pathconf(path, _PC_NAME_MAX);
3785b4e6cb2Sbostic 		*base = SLASH;
3795b4e6cb2Sbostic 	} else {
3805b4e6cb2Sbostic 		/* Cache the results for the working directory, for speed. */
3815b4e6cb2Sbostic 		if (!dot_namemax)
3825b4e6cb2Sbostic 			dot_namemax = pathconf(".", _PC_NAME_MAX);
3835b4e6cb2Sbostic 		namemax = dot_namemax;
3845b4e6cb2Sbostic 	}
3855b4e6cb2Sbostic 	/* If pathconf() yielded -1, namemax is now ULONG_MAX.  */
3865b4e6cb2Sbostic 	return namemax<namelen;
3875b4e6cb2Sbostic }
3885b4e6cb2Sbostic #endif
3895b4e6cb2Sbostic #endif
3905b4e6cb2Sbostic 
3915b4e6cb2Sbostic 	void
bufalloc(b,size)3925b4e6cb2Sbostic bufalloc(b, size)
3935b4e6cb2Sbostic 	register struct buf *b;
3945b4e6cb2Sbostic 	size_t size;
3955b4e6cb2Sbostic /* Ensure *B is a name buffer of at least SIZE bytes.
3965b4e6cb2Sbostic  * *B's old contents can be freed; *B's new contents are undefined.
3975b4e6cb2Sbostic  */
3985b4e6cb2Sbostic {
3995b4e6cb2Sbostic 	if (b->size < size) {
4005b4e6cb2Sbostic 		if (b->size)
4015b4e6cb2Sbostic 			tfree(b->string);
4025b4e6cb2Sbostic 		else
4035b4e6cb2Sbostic 			b->size = sizeof(malloc_type);
4045b4e6cb2Sbostic 		while (b->size < size)
4055b4e6cb2Sbostic 			b->size <<= 1;
4065b4e6cb2Sbostic 		b->string = tnalloc(char, b->size);
4075b4e6cb2Sbostic 	}
4085b4e6cb2Sbostic }
4095b4e6cb2Sbostic 
4105b4e6cb2Sbostic 	void
bufrealloc(b,size)4115b4e6cb2Sbostic bufrealloc(b, size)
4125b4e6cb2Sbostic 	register struct buf *b;
4135b4e6cb2Sbostic 	size_t size;
4145b4e6cb2Sbostic /* like bufalloc, except *B's old contents, if any, are preserved */
4155b4e6cb2Sbostic {
4165b4e6cb2Sbostic 	if (b->size < size) {
4175b4e6cb2Sbostic 		if (!b->size)
4185b4e6cb2Sbostic 			bufalloc(b, size);
4195b4e6cb2Sbostic 		else {
4205b4e6cb2Sbostic 			while ((b->size <<= 1)  <  size)
4215b4e6cb2Sbostic 				;
4225b4e6cb2Sbostic 			b->string = trealloc(char, b->string, b->size);
4235b4e6cb2Sbostic 		}
4245b4e6cb2Sbostic 	}
4255b4e6cb2Sbostic }
4265b4e6cb2Sbostic 
4275b4e6cb2Sbostic 	void
bufautoend(b)4285b4e6cb2Sbostic bufautoend(b)
4295b4e6cb2Sbostic 	struct buf *b;
4305b4e6cb2Sbostic /* Free an auto buffer at block exit. */
4315b4e6cb2Sbostic {
4325b4e6cb2Sbostic 	if (b->size)
4335b4e6cb2Sbostic 		tfree(b->string);
4345b4e6cb2Sbostic }
4355b4e6cb2Sbostic 
4365b4e6cb2Sbostic 	struct cbuf
bufremember(b,s)4375b4e6cb2Sbostic bufremember(b, s)
4385b4e6cb2Sbostic 	struct buf *b;
4395b4e6cb2Sbostic 	size_t s;
4405b4e6cb2Sbostic /*
4415b4e6cb2Sbostic  * Free the buffer B with used size S.
4425b4e6cb2Sbostic  * Yield a cbuf with identical contents.
4435b4e6cb2Sbostic  * The cbuf will be reclaimed when this input file is finished.
4445b4e6cb2Sbostic  */
4455b4e6cb2Sbostic {
4465b4e6cb2Sbostic 	struct cbuf cb;
4475b4e6cb2Sbostic 
4485b4e6cb2Sbostic 	if ((cb.size = s))
4495b4e6cb2Sbostic 		cb.string = fremember(trealloc(char, b->string, s));
4505b4e6cb2Sbostic 	else {
4515b4e6cb2Sbostic 		bufautoend(b); /* not really auto */
4525b4e6cb2Sbostic 		cb.string = "";
4535b4e6cb2Sbostic 	}
4545b4e6cb2Sbostic 	return cb;
4555b4e6cb2Sbostic }
4565b4e6cb2Sbostic 
4575b4e6cb2Sbostic 	char *
bufenlarge(b,alim)4585b4e6cb2Sbostic bufenlarge(b, alim)
4595b4e6cb2Sbostic 	register struct buf *b;
4605b4e6cb2Sbostic 	char const **alim;
4615b4e6cb2Sbostic /* Make *B larger.  Set *ALIM to its new limit, and yield the relocated value
4625b4e6cb2Sbostic  * of its old limit.
4635b4e6cb2Sbostic  */
4645b4e6cb2Sbostic {
4655b4e6cb2Sbostic 	size_t s = b->size;
4665b4e6cb2Sbostic 	bufrealloc(b, s + 1);
4675b4e6cb2Sbostic 	*alim = b->string + b->size;
4685b4e6cb2Sbostic 	return b->string + s;
4695b4e6cb2Sbostic }
4705b4e6cb2Sbostic 
4715b4e6cb2Sbostic 	void
bufscat(b,s)4725b4e6cb2Sbostic bufscat(b, s)
4735b4e6cb2Sbostic 	struct buf *b;
4745b4e6cb2Sbostic 	char const *s;
4755b4e6cb2Sbostic /* Concatenate S to B's end. */
4765b4e6cb2Sbostic {
4775b4e6cb2Sbostic 	size_t blen  =  b->string ? strlen(b->string) : 0;
4785b4e6cb2Sbostic 	bufrealloc(b, blen+strlen(s)+1);
4795b4e6cb2Sbostic 	VOID strcpy(b->string+blen, s);
4805b4e6cb2Sbostic }
4815b4e6cb2Sbostic 
4825b4e6cb2Sbostic 	void
bufscpy(b,s)4835b4e6cb2Sbostic bufscpy(b, s)
4845b4e6cb2Sbostic 	struct buf *b;
4855b4e6cb2Sbostic 	char const *s;
4865b4e6cb2Sbostic /* Copy S into B. */
4875b4e6cb2Sbostic {
4885b4e6cb2Sbostic 	bufalloc(b, strlen(s)+1);
4895b4e6cb2Sbostic 	VOID strcpy(b->string, s);
4905b4e6cb2Sbostic }
4915b4e6cb2Sbostic 
4925b4e6cb2Sbostic 
4935b4e6cb2Sbostic 	char const *
basename(p)4945b4e6cb2Sbostic basename(p)
4955b4e6cb2Sbostic 	char const *p;
4965b4e6cb2Sbostic /* Yield the address of the base filename of the pathname P.  */
4975b4e6cb2Sbostic {
4985b4e6cb2Sbostic 	register char const *b = p, *q = p;
4995b4e6cb2Sbostic 	for (;;)
5005b4e6cb2Sbostic 	    switch (*q++) {
5015b4e6cb2Sbostic 		case SLASHes: b = q; break;
5025b4e6cb2Sbostic 		case 0: return b;
5035b4e6cb2Sbostic 	    }
5045b4e6cb2Sbostic }
5055b4e6cb2Sbostic 
5065b4e6cb2Sbostic 	size_t
dirlen(p)5075b4e6cb2Sbostic dirlen(p)
5085b4e6cb2Sbostic 	char const *p;
5095b4e6cb2Sbostic /* Yield the length of P's directory, including its trailing SLASH.  */
5105b4e6cb2Sbostic {
5115b4e6cb2Sbostic 	return basename(p) - p;
5125b4e6cb2Sbostic }
5135b4e6cb2Sbostic 
5145b4e6cb2Sbostic 
5155b4e6cb2Sbostic 	static size_t
suffixlen(x)5165b4e6cb2Sbostic suffixlen(x)
5175b4e6cb2Sbostic 	char const *x;
5185b4e6cb2Sbostic /* Yield the length of X, an RCS filename suffix.  */
5195b4e6cb2Sbostic {
5205b4e6cb2Sbostic 	register char const *p;
5215b4e6cb2Sbostic 
5225b4e6cb2Sbostic 	p = x;
5235b4e6cb2Sbostic 	for (;;)
5245b4e6cb2Sbostic 	    switch (*p) {
5255b4e6cb2Sbostic 		case 0: case SLASHes:
5265b4e6cb2Sbostic 		    return p - x;
5275b4e6cb2Sbostic 
5285b4e6cb2Sbostic 		default:
5295b4e6cb2Sbostic 		    ++p;
5305b4e6cb2Sbostic 		    continue;
5315b4e6cb2Sbostic 	    }
5325b4e6cb2Sbostic }
5335b4e6cb2Sbostic 
5345b4e6cb2Sbostic 	char const *
rcssuffix(name)5355b4e6cb2Sbostic rcssuffix(name)
5365b4e6cb2Sbostic 	char const *name;
5375b4e6cb2Sbostic /* Yield the suffix of NAME if it is an RCS filename, 0 otherwise.  */
5385b4e6cb2Sbostic {
5395b4e6cb2Sbostic 	char const *x, *p, *nz;
5405b4e6cb2Sbostic 	size_t dl, nl, xl;
5415b4e6cb2Sbostic 
5425b4e6cb2Sbostic 	nl = strlen(name);
5435b4e6cb2Sbostic 	nz = name + nl;
5445b4e6cb2Sbostic 	x = suffixes;
5455b4e6cb2Sbostic 	do {
5465b4e6cb2Sbostic 	    if ((xl = suffixlen(x))) {
5475b4e6cb2Sbostic 		if (xl <= nl  &&  memcmp(p = nz-xl, x, xl) == 0)
5485b4e6cb2Sbostic 		    return p;
5495b4e6cb2Sbostic 	    } else {
5505b4e6cb2Sbostic 		dl = dirlen(name);
5515b4e6cb2Sbostic 		if (
5525b4e6cb2Sbostic 		    rcsdirlen < dl  &&
5535b4e6cb2Sbostic 		    !memcmp(p = name+(dl-=rcsdirlen+1), rcsdir, rcsdirlen) &&
5545b4e6cb2Sbostic 		    (!dl  ||  isSLASH(*--p))
5555b4e6cb2Sbostic 		)
5565b4e6cb2Sbostic 		    return nz;
5575b4e6cb2Sbostic 	    }
5585b4e6cb2Sbostic 	    x += xl;
5595b4e6cb2Sbostic 	} while (*x++);
5605b4e6cb2Sbostic 	return 0;
5615b4e6cb2Sbostic }
5625b4e6cb2Sbostic 
5635b4e6cb2Sbostic 	/*ARGSUSED*/ RILE *
rcsreadopen(RCSname,status,mustread)5645b4e6cb2Sbostic rcsreadopen(RCSname, status, mustread)
5655b4e6cb2Sbostic 	struct buf *RCSname;
5665b4e6cb2Sbostic 	struct stat *status;
5675b4e6cb2Sbostic 	int mustread;
5685b4e6cb2Sbostic /* Open RCSNAME for reading and yield its FILE* descriptor.
5695b4e6cb2Sbostic  * If successful, set *STATUS to its status.
5705b4e6cb2Sbostic  * Pass this routine to pairfilenames() for read-only access to the file.  */
5715b4e6cb2Sbostic {
5725b4e6cb2Sbostic 	return Iopen(RCSname->string, FOPEN_R, status);
5735b4e6cb2Sbostic }
5745b4e6cb2Sbostic 
5755b4e6cb2Sbostic 	static int
5765b4e6cb2Sbostic finopen(rcsopen, mustread)
5775b4e6cb2Sbostic 	/* jlf RILE *(*rcsopen)P((struct buf*,struct stat*,int)); */
5785b4e6cb2Sbostic 	RILE *(*rcsopen)();
5795b4e6cb2Sbostic 	int mustread;
5805b4e6cb2Sbostic /*
5815b4e6cb2Sbostic  * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
5825b4e6cb2Sbostic  * Set finptr to the result and yield true if successful.
5835b4e6cb2Sbostic  * RCSb holds the file's name.
5845b4e6cb2Sbostic  * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
5855b4e6cb2Sbostic  * Yield true if successful or if an unusual failure.
5865b4e6cb2Sbostic  */
5875b4e6cb2Sbostic {
5885b4e6cb2Sbostic 	int interesting, preferold;
5895b4e6cb2Sbostic 
5905b4e6cb2Sbostic 	/*
5915b4e6cb2Sbostic 	 * We prefer an old name to that of a nonexisting new RCS file,
5925b4e6cb2Sbostic 	 * unless we tried locking the old name and failed.
5935b4e6cb2Sbostic 	 */
5945b4e6cb2Sbostic 	preferold  =  RCSbuf.string[0] && (mustread||frewrite);
5955b4e6cb2Sbostic 
5965b4e6cb2Sbostic 	finptr = (*rcsopen)(&RCSb, &RCSstat, mustread);
5975b4e6cb2Sbostic 	interesting = finptr || errno!=ENOENT;
5985b4e6cb2Sbostic 	if (interesting || !preferold) {
5995b4e6cb2Sbostic 		/* Use the new name.  */
6005b4e6cb2Sbostic 		RCSerrno = errno;
6015b4e6cb2Sbostic 		bufscpy(&RCSbuf, RCSb.string);
6025b4e6cb2Sbostic 	}
6035b4e6cb2Sbostic 	return interesting;
6045b4e6cb2Sbostic }
6055b4e6cb2Sbostic 
6065b4e6cb2Sbostic 	static int
fin2open(d,dlen,base,baselen,x,xlen,rcsopen,mustread)6075b4e6cb2Sbostic fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread)
6085b4e6cb2Sbostic 	char const *d, *base, *x;
6095b4e6cb2Sbostic 	size_t dlen, baselen, xlen;
6105b4e6cb2Sbostic 	/* jlf RILE *(*rcsopen)P((struct buf*,struct stat*,int)); */
6115b4e6cb2Sbostic 	RILE *(*rcsopen)();
6125b4e6cb2Sbostic 	int mustread;
6135b4e6cb2Sbostic /*
6145b4e6cb2Sbostic  * D is a directory name with length DLEN (including trailing slash).
6155b4e6cb2Sbostic  * BASE is a filename with length BASELEN.
6165b4e6cb2Sbostic  * X is an RCS filename suffix with length XLEN.
6175b4e6cb2Sbostic  * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
6185b4e6cb2Sbostic  * Yield true if successful.
6195b4e6cb2Sbostic  * Try dRCS/basex first; if that fails and x is nonempty, try dbasex.
6205b4e6cb2Sbostic  * Put these potential names in RCSb.
6215b4e6cb2Sbostic  * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
6225b4e6cb2Sbostic  * Yield true if successful or if an unusual failure.
6235b4e6cb2Sbostic  */
6245b4e6cb2Sbostic {
6255b4e6cb2Sbostic 	register char *p;
6265b4e6cb2Sbostic 
6275b4e6cb2Sbostic 	bufalloc(&RCSb, dlen + rcsdirlen + 1 + baselen + xlen + 1);
6285b4e6cb2Sbostic 
6295b4e6cb2Sbostic 	/* Try dRCS/basex.  */
6305b4e6cb2Sbostic 	VOID memcpy(p = RCSb.string, d, dlen);
6315b4e6cb2Sbostic 	VOID memcpy(p += dlen, rcsdir, rcsdirlen);
6325b4e6cb2Sbostic 	p += rcsdirlen;
6335b4e6cb2Sbostic 	*p++ = SLASH;
6345b4e6cb2Sbostic 	VOID memcpy(p, base, baselen);
6355b4e6cb2Sbostic 	VOID memcpy(p += baselen, x, xlen);
6365b4e6cb2Sbostic 	p[xlen] = 0;
6375b4e6cb2Sbostic 	if (xlen) {
6385b4e6cb2Sbostic 	    if (finopen(rcsopen, mustread))
6395b4e6cb2Sbostic 		return true;
6405b4e6cb2Sbostic 
6415b4e6cb2Sbostic 	    /* Try dbasex.  */
6425b4e6cb2Sbostic 	    /* Start from scratch, because finopen() may have changed RCSb.  */
6435b4e6cb2Sbostic 	    VOID memcpy(p = RCSb.string, d, dlen);
6445b4e6cb2Sbostic 	    VOID memcpy(p += dlen, base, baselen);
6455b4e6cb2Sbostic 	    VOID memcpy(p += baselen, x, xlen);
6465b4e6cb2Sbostic 	    p[xlen] = 0;
6475b4e6cb2Sbostic 	}
6485b4e6cb2Sbostic 	return finopen(rcsopen, mustread);
6495b4e6cb2Sbostic }
6505b4e6cb2Sbostic 
6515b4e6cb2Sbostic 	int
pairfilenames(argc,argv,rcsopen,mustread,quiet)6525b4e6cb2Sbostic pairfilenames(argc, argv, rcsopen, mustread, quiet)
6535b4e6cb2Sbostic 	int argc;
6545b4e6cb2Sbostic 	char **argv;
6555b4e6cb2Sbostic 	/* jlf RILE *(*rcsopen)P((struct buf*,struct stat*,int)); */
6565b4e6cb2Sbostic 	RILE *(*rcsopen)();
6575b4e6cb2Sbostic 	int mustread, quiet;
6585b4e6cb2Sbostic /* Function: Pairs the filenames pointed to by argv; argc indicates
6595b4e6cb2Sbostic  * how many there are.
6605b4e6cb2Sbostic  * Places a pointer to the RCS filename into RCSfilename,
6615b4e6cb2Sbostic  * and a pointer to the name of the working file into workfilename.
6625b4e6cb2Sbostic  * If both the workfilename and the RCS filename are given, and workstdout
6635b4e6cb2Sbostic  * is set, a warning is printed.
6645b4e6cb2Sbostic  *
6655b4e6cb2Sbostic  * If the RCS file exists, places its status into RCSstat.
6665b4e6cb2Sbostic  *
6675b4e6cb2Sbostic  * If the RCS file exists, it is RCSOPENed for reading, the file pointer
6685b4e6cb2Sbostic  * is placed into finptr, and the admin-node is read in; returns 1.
6695b4e6cb2Sbostic  * If the RCS file does not exist and MUSTREAD,
6705b4e6cb2Sbostic  * print an error unless QUIET and return 0.
6715b4e6cb2Sbostic  * Otherwise, initialize the admin node and return -1.
6725b4e6cb2Sbostic  *
6735b4e6cb2Sbostic  * 0 is returned on all errors, e.g. files that are not regular files.
6745b4e6cb2Sbostic  */
6755b4e6cb2Sbostic {
6765b4e6cb2Sbostic 	static struct buf tempbuf;
6775b4e6cb2Sbostic 
6785b4e6cb2Sbostic 	register char *p, *arg, *RCS1;
6795b4e6cb2Sbostic 	char const *purefname, *pureRCSname, *x;
6805b4e6cb2Sbostic 	int paired;
6815b4e6cb2Sbostic 	size_t arglen, dlen, baselen, xlen;
6825b4e6cb2Sbostic 
6835b4e6cb2Sbostic 	if (!(arg = *argv)) return 0; /* already paired filename */
6845b4e6cb2Sbostic 	if (*arg == '-') {
6855b4e6cb2Sbostic 		error("%s option is ignored after file names", arg);
6865b4e6cb2Sbostic 		return 0;
6875b4e6cb2Sbostic 	}
6885b4e6cb2Sbostic 
6895b4e6cb2Sbostic 	purefname = basename(arg);
6905b4e6cb2Sbostic 
6915b4e6cb2Sbostic 	/* Allocate buffer temporary to hold the default paired file name. */
6925b4e6cb2Sbostic 	p = arg;
6935b4e6cb2Sbostic 	for (;;) {
6945b4e6cb2Sbostic 		switch (*p++) {
6955b4e6cb2Sbostic 		    /* Beware characters that cause havoc with ci -k. */
6965b4e6cb2Sbostic 		    case KDELIM:
6975b4e6cb2Sbostic 			error("RCS file name `%s' contains %c", arg, KDELIM);
6985b4e6cb2Sbostic 			return 0;
6995b4e6cb2Sbostic 		    case ' ': case '\n': case '\t':
7005b4e6cb2Sbostic 			error("RCS file name `%s' contains white space", arg);
7015b4e6cb2Sbostic 			return 0;
7025b4e6cb2Sbostic 		    default:
7035b4e6cb2Sbostic 			continue;
7045b4e6cb2Sbostic 		    case 0:
7055b4e6cb2Sbostic 			break;
7065b4e6cb2Sbostic 		}
7075b4e6cb2Sbostic 		break;
7085b4e6cb2Sbostic 	}
7095b4e6cb2Sbostic 
7105b4e6cb2Sbostic 	paired = false;
7115b4e6cb2Sbostic 
7125b4e6cb2Sbostic         /* first check suffix to see whether it is an RCS file or not */
7135b4e6cb2Sbostic 	if ((x = rcssuffix(arg)))
7145b4e6cb2Sbostic 	{
7155b4e6cb2Sbostic                 /* RCS file name given*/
7165b4e6cb2Sbostic 		RCS1 = arg;
7175b4e6cb2Sbostic 		pureRCSname = purefname;
7185b4e6cb2Sbostic 		baselen = x - purefname;
7195b4e6cb2Sbostic 		if (
7205b4e6cb2Sbostic 		    1 < argc  &&
7215b4e6cb2Sbostic 		    !rcssuffix(workfilename = p = argv[1])  &&
7225b4e6cb2Sbostic 		    baselen <= (arglen = strlen(p))  &&
7235b4e6cb2Sbostic 		    ((p+=arglen-baselen) == workfilename  ||  isSLASH(p[-1])) &&
7245b4e6cb2Sbostic 		    memcmp(purefname, p, baselen) == 0
7255b4e6cb2Sbostic 		) {
7265b4e6cb2Sbostic 			argv[1] = 0;
7275b4e6cb2Sbostic 			paired = true;
7285b4e6cb2Sbostic 		} else {
7295b4e6cb2Sbostic 			bufscpy(&tempbuf, purefname);
7305b4e6cb2Sbostic 			workfilename = p = tempbuf.string;
7315b4e6cb2Sbostic 			p[baselen] = 0;
7325b4e6cb2Sbostic 		}
7335b4e6cb2Sbostic         } else {
7345b4e6cb2Sbostic                 /* working file given; now try to find RCS file */
7355b4e6cb2Sbostic 		workfilename = arg;
7365b4e6cb2Sbostic 		baselen = p - purefname - 1;
7375b4e6cb2Sbostic                 /* derive RCS file name*/
7385b4e6cb2Sbostic 		if (
7395b4e6cb2Sbostic 		    1 < argc  &&
7405b4e6cb2Sbostic 		    (x = rcssuffix(RCS1 = argv[1]))  &&
7415b4e6cb2Sbostic 		    baselen  <=  x - RCS1  &&
7425b4e6cb2Sbostic 		    ((pureRCSname=x-baselen)==RCS1 || isSLASH(pureRCSname[-1])) &&
7435b4e6cb2Sbostic 		    memcmp(purefname, pureRCSname, baselen) == 0
7445b4e6cb2Sbostic 		) {
7455b4e6cb2Sbostic 			argv[1] = 0;
7465b4e6cb2Sbostic 			paired = true;
7475b4e6cb2Sbostic 		} else
7485b4e6cb2Sbostic 			pureRCSname = RCS1 = 0;
7495b4e6cb2Sbostic         }
7505b4e6cb2Sbostic         /* now we have a (tentative) RCS filename in RCS1 and workfilename  */
7515b4e6cb2Sbostic         /* Second, try to find the right RCS file */
7525b4e6cb2Sbostic         if (pureRCSname!=RCS1) {
7535b4e6cb2Sbostic                 /* a path for RCSfile is given; single RCS file to look for */
7545b4e6cb2Sbostic 		bufscpy(&RCSbuf, RCS1);
7555b4e6cb2Sbostic 		finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread);
7565b4e6cb2Sbostic 		RCSerrno = errno;
7575b4e6cb2Sbostic         } else {
7585b4e6cb2Sbostic 		bufscpy(&RCSbuf, "");
7595b4e6cb2Sbostic 		if (RCS1)
7605b4e6cb2Sbostic 			/* RCS file name was given without path.  */
7615b4e6cb2Sbostic 			VOID fin2open(arg, (size_t)0, pureRCSname, baselen,
7625b4e6cb2Sbostic 				x, strlen(x), rcsopen, mustread
7635b4e6cb2Sbostic 			);
7645b4e6cb2Sbostic 		else {
7655b4e6cb2Sbostic 			/* No RCS file name was given.  */
7665b4e6cb2Sbostic 			/* Try each suffix in turn.  */
7675b4e6cb2Sbostic 			dlen = purefname-arg;
7685b4e6cb2Sbostic 			x = suffixes;
7695b4e6cb2Sbostic 			while (! fin2open(arg, dlen, purefname, baselen,
7705b4e6cb2Sbostic 					x, xlen=suffixlen(x), rcsopen, mustread
7715b4e6cb2Sbostic 			)) {
7725b4e6cb2Sbostic 				x += xlen;
7735b4e6cb2Sbostic 				if (!*x++)
7745b4e6cb2Sbostic 					break;
7755b4e6cb2Sbostic 			}
7765b4e6cb2Sbostic 		}
7775b4e6cb2Sbostic         }
7785b4e6cb2Sbostic 	RCSfilename = p = RCSbuf.string;
7795b4e6cb2Sbostic 	if (finptr) {
7805b4e6cb2Sbostic 		if (!S_ISREG(RCSstat.st_mode)) {
7815b4e6cb2Sbostic 			error("%s isn't a regular file -- ignored", p);
7825b4e6cb2Sbostic                         return 0;
7835b4e6cb2Sbostic                 }
7845b4e6cb2Sbostic                 Lexinit(); getadmin();
7855b4e6cb2Sbostic 	} else {
7865b4e6cb2Sbostic 		if (RCSerrno!=ENOENT || mustread || !frewrite) {
7875b4e6cb2Sbostic 			if (RCSerrno == EEXIST)
7885b4e6cb2Sbostic 				error("RCS file %s is in use", p);
7895b4e6cb2Sbostic 			else if (!quiet || RCSerrno!=ENOENT)
7905b4e6cb2Sbostic 				enerror(RCSerrno, p);
7915b4e6cb2Sbostic 			return 0;
7925b4e6cb2Sbostic 		}
7935b4e6cb2Sbostic                 InitAdmin();
7945b4e6cb2Sbostic         };
7955b4e6cb2Sbostic #	if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED
7965b4e6cb2Sbostic 	    if (filenametoolong(p)) {
7975b4e6cb2Sbostic 		error("RCS file name %s is too long", p);
7985b4e6cb2Sbostic 		return 0;
7995b4e6cb2Sbostic 	    }
8005b4e6cb2Sbostic #	    ifndef NAME_MAX
8015b4e6cb2Sbostic 		/*
8025b4e6cb2Sbostic 		 * Check workfilename too, even though it cannot be longer,
8035b4e6cb2Sbostic 		 * because it may reside on a different filesystem.
8045b4e6cb2Sbostic 		 */
8055b4e6cb2Sbostic 		if (filenametoolong(workfilename)) {
8065b4e6cb2Sbostic 		    error("working file name %s is too long", workfilename);
8075b4e6cb2Sbostic 		    return 0;
8085b4e6cb2Sbostic 		}
8095b4e6cb2Sbostic #	    endif
8105b4e6cb2Sbostic #	endif
8115b4e6cb2Sbostic 
8125b4e6cb2Sbostic 	if (paired && workstdout)
8135b4e6cb2Sbostic                 warn("Option -p is set; ignoring output file %s",workfilename);
8145b4e6cb2Sbostic 
8155b4e6cb2Sbostic 	prevkeys = false;
8165b4e6cb2Sbostic 	return finptr ? 1 : -1;
8175b4e6cb2Sbostic }
8185b4e6cb2Sbostic 
8195b4e6cb2Sbostic 
8205b4e6cb2Sbostic 	char const *
getfullRCSname()8215b4e6cb2Sbostic getfullRCSname()
8225b4e6cb2Sbostic /* Function: returns a pointer to the full path name of the RCS file.
8235b4e6cb2Sbostic  * Gets the working directory's name at most once.
8245b4e6cb2Sbostic  * Removes leading "../" and "./".
8255b4e6cb2Sbostic  */
8265b4e6cb2Sbostic {
8275b4e6cb2Sbostic 	static char const *wdptr;
8285b4e6cb2Sbostic 	static struct buf rcsbuf, wdbuf;
8295b4e6cb2Sbostic 	static size_t pathlength;
8305b4e6cb2Sbostic 
8315b4e6cb2Sbostic 	register char const *realname;
8325b4e6cb2Sbostic 	register size_t parentdirlength;
8335b4e6cb2Sbostic 	register unsigned dotdotcounter;
8345b4e6cb2Sbostic 	register char *d;
8355b4e6cb2Sbostic 	register char const *wd;
8365b4e6cb2Sbostic 
8375b4e6cb2Sbostic 	if (ROOTPATH(RCSfilename)) {
8385b4e6cb2Sbostic                 return(RCSfilename);
8395b4e6cb2Sbostic         } else {
8405b4e6cb2Sbostic 		if (!(wd = wdptr)) {
8415b4e6cb2Sbostic 		    /* Get working directory for the first time.  */
8425b4e6cb2Sbostic 		    if (!(d = cgetenv("PWD"))) {
8435b4e6cb2Sbostic 			bufalloc(&wdbuf, SIZEABLE_PATH + 1);
8445b4e6cb2Sbostic #			if !has_getcwd && has_getwd
8455b4e6cb2Sbostic 			    d = getwd(wdbuf.string);
8465b4e6cb2Sbostic #			else
8475b4e6cb2Sbostic 			    while (
8485b4e6cb2Sbostic 				    !(d = getcwd(wdbuf.string, wdbuf.size))
8495b4e6cb2Sbostic 				&&  errno==ERANGE
8505b4e6cb2Sbostic 			    )
8515b4e6cb2Sbostic 				bufalloc(&wdbuf, wdbuf.size<<1);
8525b4e6cb2Sbostic #			endif
8535b4e6cb2Sbostic 			if (!d)
8545b4e6cb2Sbostic 			    efaterror("working directory");
8555b4e6cb2Sbostic 		    }
8565b4e6cb2Sbostic 		    parentdirlength = strlen(d);
8575b4e6cb2Sbostic 		    while (parentdirlength && isSLASH(d[parentdirlength-1])) {
8585b4e6cb2Sbostic 			d[--parentdirlength] = 0;
8595b4e6cb2Sbostic                         /* Check needed because some getwd implementations */
8605b4e6cb2Sbostic                         /* generate "/" for the root.                      */
8615b4e6cb2Sbostic                     }
8625b4e6cb2Sbostic 		    wdptr = wd = d;
8635b4e6cb2Sbostic 		    pathlength = parentdirlength;
8645b4e6cb2Sbostic                 }
8655b4e6cb2Sbostic                 /*the following must be redone since RCSfilename may change*/
8665b4e6cb2Sbostic 		/* Find how many `../'s to remove from RCSfilename.  */
8675b4e6cb2Sbostic                 dotdotcounter =0;
8685b4e6cb2Sbostic                 realname = RCSfilename;
8695b4e6cb2Sbostic 		while (realname[0]=='.') {
8705b4e6cb2Sbostic 			if (isSLASH(realname[1])) {
8715b4e6cb2Sbostic                             /* drop leading ./ */
8725b4e6cb2Sbostic                             realname += 2;
8735b4e6cb2Sbostic 			} else if (realname[1]=='.' && isSLASH(realname[2])) {
8745b4e6cb2Sbostic                             /* drop leading ../ and remember */
8755b4e6cb2Sbostic                             dotdotcounter++;
8765b4e6cb2Sbostic                             realname += 3;
8775b4e6cb2Sbostic 			} else
8785b4e6cb2Sbostic 			    break;
8795b4e6cb2Sbostic                 }
8805b4e6cb2Sbostic 		/* Now remove dotdotcounter trailing directories from wd. */
8815b4e6cb2Sbostic 		parentdirlength = pathlength;
8825b4e6cb2Sbostic 		while (dotdotcounter && parentdirlength) {
8835b4e6cb2Sbostic                     /* move pointer backwards over trailing directory */
8845b4e6cb2Sbostic 		    if (isSLASH(wd[--parentdirlength])) {
8855b4e6cb2Sbostic                         dotdotcounter--;
8865b4e6cb2Sbostic                     }
8875b4e6cb2Sbostic                 }
8885b4e6cb2Sbostic 		/* build full path name */
8895b4e6cb2Sbostic 		bufalloc(&rcsbuf, parentdirlength+strlen(realname)+2);
8905b4e6cb2Sbostic 		d = rcsbuf.string;
8915b4e6cb2Sbostic 		VOID memcpy(d, wd, parentdirlength);
8925b4e6cb2Sbostic 		d += parentdirlength;
8935b4e6cb2Sbostic 		*d++ = SLASH;
8945b4e6cb2Sbostic 		VOID strcpy(d, realname);
8955b4e6cb2Sbostic 		return rcsbuf.string;
8965b4e6cb2Sbostic         }
8975b4e6cb2Sbostic }
8985b4e6cb2Sbostic 
8995b4e6cb2Sbostic #ifndef isSLASH
9005b4e6cb2Sbostic 	int
isSLASH(c)9015b4e6cb2Sbostic isSLASH(c)
9025b4e6cb2Sbostic 	int c;
9035b4e6cb2Sbostic {
9045b4e6cb2Sbostic 	switch (c) {
9055b4e6cb2Sbostic 	    case SLASHes:
9065b4e6cb2Sbostic 		return true;
9075b4e6cb2Sbostic 	    default:
9085b4e6cb2Sbostic 		return false;
9095b4e6cb2Sbostic 	}
9105b4e6cb2Sbostic }
9115b4e6cb2Sbostic #endif
9125b4e6cb2Sbostic 
9135b4e6cb2Sbostic 
9145b4e6cb2Sbostic #if !has_getcwd && !has_getwd
9155b4e6cb2Sbostic 
9165b4e6cb2Sbostic 	char *
getcwd(path,size)9175b4e6cb2Sbostic getcwd(path, size)
9185b4e6cb2Sbostic 	char *path;
9195b4e6cb2Sbostic 	size_t size;
9205b4e6cb2Sbostic {
9215b4e6cb2Sbostic 	static char const usrbinpwd[] = "/usr/bin/pwd";
9225b4e6cb2Sbostic #	define binpwd (usrbinpwd+4)
9235b4e6cb2Sbostic 
9245b4e6cb2Sbostic 	register FILE *fp;
9255b4e6cb2Sbostic 	register int c;
9265b4e6cb2Sbostic 	register char *p, *lim;
9275b4e6cb2Sbostic 	int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus;
9285b4e6cb2Sbostic 	pid_t child;
9295b4e6cb2Sbostic #	if !has_waitpid
9305b4e6cb2Sbostic 		pid_t w;
9315b4e6cb2Sbostic #	endif
9325b4e6cb2Sbostic 
9335b4e6cb2Sbostic 	if (!size) {
9345b4e6cb2Sbostic 		errno = EINVAL;
9355b4e6cb2Sbostic 		return 0;
9365b4e6cb2Sbostic 	}
9375b4e6cb2Sbostic 	if (pipe(fd) != 0)
9385b4e6cb2Sbostic 		return 0;
9395b4e6cb2Sbostic 	if (!(child = vfork())) {
9405b4e6cb2Sbostic 		if (
9415b4e6cb2Sbostic 			close(fd[0]) == 0 &&
9425b4e6cb2Sbostic 			(fd[1] == STDOUT_FILENO ||
9435b4e6cb2Sbostic #				ifdef F_DUPFD
9445b4e6cb2Sbostic 					(VOID close(STDOUT_FILENO),
9455b4e6cb2Sbostic 					fcntl(fd[1], F_DUPFD, STDOUT_FILENO))
9465b4e6cb2Sbostic #				else
9475b4e6cb2Sbostic 					dup2(fd[1], STDOUT_FILENO)
9485b4e6cb2Sbostic #				endif
9495b4e6cb2Sbostic 				== STDOUT_FILENO &&
9505b4e6cb2Sbostic 				close(fd[1]) == 0
9515b4e6cb2Sbostic 			)
9525b4e6cb2Sbostic 		) {
9535b4e6cb2Sbostic 			VOID close(STDERR_FILENO);
9545b4e6cb2Sbostic 			VOID execl(binpwd, binpwd, (char *)0);
9555b4e6cb2Sbostic 			VOID execl(usrbinpwd, usrbinpwd, (char *)0);
9565b4e6cb2Sbostic 		}
9575b4e6cb2Sbostic 		_exit(EXIT_FAILURE);
9585b4e6cb2Sbostic 	}
9595b4e6cb2Sbostic 	e = errno;
9605b4e6cb2Sbostic 	closeerror = close(fd[1]);
9615b4e6cb2Sbostic 	closeerrno = errno;
9625b4e6cb2Sbostic 	fp = 0;
9635b4e6cb2Sbostic 	readerror = toolong = wstatus = 0;
9645b4e6cb2Sbostic 	p = path;
9655b4e6cb2Sbostic 	if (0 <= child) {
9665b4e6cb2Sbostic 		fp = fdopen(fd[0], "r");
9675b4e6cb2Sbostic 		e = errno;
9685b4e6cb2Sbostic 		if (fp) {
9695b4e6cb2Sbostic 			lim = p + size;
9705b4e6cb2Sbostic 			for (p = path;  ;  *p++ = c) {
9715b4e6cb2Sbostic 				if ((c=getc(fp)) < 0) {
9725b4e6cb2Sbostic 					if (feof(fp))
9735b4e6cb2Sbostic 						break;
9745b4e6cb2Sbostic 					if (ferror(fp)) {
9755b4e6cb2Sbostic 						readerror = 1;
9765b4e6cb2Sbostic 						e = errno;
9775b4e6cb2Sbostic 						break;
9785b4e6cb2Sbostic 					}
9795b4e6cb2Sbostic 				}
9805b4e6cb2Sbostic 				if (p == lim) {
9815b4e6cb2Sbostic 					toolong = 1;
9825b4e6cb2Sbostic 					break;
9835b4e6cb2Sbostic 				}
9845b4e6cb2Sbostic 			}
9855b4e6cb2Sbostic 		}
9865b4e6cb2Sbostic #		if has_waitpid
9875b4e6cb2Sbostic 			if (waitpid(child, &wstatus, 0) < 0)
9885b4e6cb2Sbostic 				wstatus = 1;
9895b4e6cb2Sbostic #		else
9905b4e6cb2Sbostic 			do {
9915b4e6cb2Sbostic 				if ((w = wait(&wstatus)) < 0) {
9925b4e6cb2Sbostic 					wstatus = 1;
9935b4e6cb2Sbostic 					break;
9945b4e6cb2Sbostic 				}
9955b4e6cb2Sbostic 			} while (w != child);
9965b4e6cb2Sbostic #		endif
9975b4e6cb2Sbostic 	}
9985b4e6cb2Sbostic 	if (!fp) {
9995b4e6cb2Sbostic 		VOID close(fd[0]);
10005b4e6cb2Sbostic 		errno = e;
10015b4e6cb2Sbostic 		return 0;
10025b4e6cb2Sbostic 	}
10035b4e6cb2Sbostic 	if (fclose(fp) != 0)
10045b4e6cb2Sbostic 		return 0;
10055b4e6cb2Sbostic 	if (readerror) {
10065b4e6cb2Sbostic 		errno = e;
10075b4e6cb2Sbostic 		return 0;
10085b4e6cb2Sbostic 	}
10095b4e6cb2Sbostic 	if (closeerror) {
10105b4e6cb2Sbostic 		errno = closeerrno;
10115b4e6cb2Sbostic 		return 0;
10125b4e6cb2Sbostic 	}
10135b4e6cb2Sbostic 	if (toolong) {
10145b4e6cb2Sbostic 		errno = ERANGE;
10155b4e6cb2Sbostic 		return 0;
10165b4e6cb2Sbostic 	}
10175b4e6cb2Sbostic 	if (wstatus  ||  p == path  ||  *--p != '\n') {
10185b4e6cb2Sbostic 		errno = EACCES;
10195b4e6cb2Sbostic 		return 0;
10205b4e6cb2Sbostic 	}
10215b4e6cb2Sbostic 	*p = '\0';
10225b4e6cb2Sbostic 	return path;
10235b4e6cb2Sbostic }
10245b4e6cb2Sbostic #endif
10255b4e6cb2Sbostic 
10265b4e6cb2Sbostic 
10275b4e6cb2Sbostic #ifdef PAIRTEST
10285b4e6cb2Sbostic /* test program for pairfilenames() and getfullRCSname() */
10295b4e6cb2Sbostic 
10305b4e6cb2Sbostic char const cmdid[] = "pair";
10315b4e6cb2Sbostic 
main(argc,argv)10325b4e6cb2Sbostic main(argc, argv)
10335b4e6cb2Sbostic int argc; char *argv[];
10345b4e6cb2Sbostic {
10355b4e6cb2Sbostic         int result;
10365b4e6cb2Sbostic 	int initflag;
10375b4e6cb2Sbostic 	quietflag = initflag = false;
10385b4e6cb2Sbostic 
10395b4e6cb2Sbostic         while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
10405b4e6cb2Sbostic                 switch ((*argv)[1]) {
10415b4e6cb2Sbostic 
10425b4e6cb2Sbostic 		case 'p':       workstdout = stdout;
10435b4e6cb2Sbostic                                 break;
10445b4e6cb2Sbostic                 case 'i':       initflag=true;
10455b4e6cb2Sbostic                                 break;
10465b4e6cb2Sbostic                 case 'q':       quietflag=true;
10475b4e6cb2Sbostic                                 break;
10485b4e6cb2Sbostic                 default:        error("unknown option: %s", *argv);
10495b4e6cb2Sbostic                                 break;
10505b4e6cb2Sbostic                 }
10515b4e6cb2Sbostic         }
10525b4e6cb2Sbostic 
10535b4e6cb2Sbostic         do {
10545b4e6cb2Sbostic                 RCSfilename=workfilename=nil;
10555b4e6cb2Sbostic 		result = pairfilenames(argc,argv,rcsreadopen,!initflag,quietflag);
10565b4e6cb2Sbostic                 if (result!=0) {
10575b4e6cb2Sbostic 		    diagnose("RCS file: %s; working file: %s\nFull RCS file name: %s\n",
10585b4e6cb2Sbostic 			     RCSfilename,workfilename,getfullRCSname()
10595b4e6cb2Sbostic 		    );
10605b4e6cb2Sbostic                 }
10615b4e6cb2Sbostic                 switch (result) {
10625b4e6cb2Sbostic                         case 0: continue; /* already paired file */
10635b4e6cb2Sbostic 
10645b4e6cb2Sbostic                         case 1: if (initflag) {
10655b4e6cb2Sbostic                                     error("RCS file %s exists already",RCSfilename);
10665b4e6cb2Sbostic                                 } else {
10675b4e6cb2Sbostic 				    diagnose("RCS file %s exists\n",RCSfilename);
10685b4e6cb2Sbostic                                 }
10695b4e6cb2Sbostic 				Ifclose(finptr);
10705b4e6cb2Sbostic                                 break;
10715b4e6cb2Sbostic 
10725b4e6cb2Sbostic 			case -1:diagnose("RCS file doesn't exist\n");
10735b4e6cb2Sbostic                                 break;
10745b4e6cb2Sbostic                 }
10755b4e6cb2Sbostic 
10765b4e6cb2Sbostic         } while (++argv, --argc>=1);
10775b4e6cb2Sbostic 
10785b4e6cb2Sbostic }
10795b4e6cb2Sbostic 
10805b4e6cb2Sbostic 	exiting void
exiterr()10815b4e6cb2Sbostic exiterr()
10825b4e6cb2Sbostic {
10835b4e6cb2Sbostic 	dirtempunlink();
10845b4e6cb2Sbostic 	tempunlink();
10855b4e6cb2Sbostic 	_exit(EXIT_FAILURE);
10865b4e6cb2Sbostic }
10875b4e6cb2Sbostic #endif
1088