111894Speter/* RCS filename and pathname handling */
211894Speter
39Sjkh/****************************************************************************
49Sjkh *                     creation and deletion of /tmp temporaries
511894Speter *		       pairing of RCS pathnames and working pathnames.
69Sjkh *                     Testprogram: define PAIRTEST
79Sjkh ****************************************************************************
89Sjkh */
99Sjkh
1011894Speter/* Copyright 1982, 1988, 1989 Walter Tichy
1111894Speter   Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
129Sjkh   Distributed under license by the Free Software Foundation, Inc.
139Sjkh
149SjkhThis file is part of RCS.
159Sjkh
169SjkhRCS is free software; you can redistribute it and/or modify
179Sjkhit under the terms of the GNU General Public License as published by
189Sjkhthe Free Software Foundation; either version 2, or (at your option)
199Sjkhany later version.
209Sjkh
219SjkhRCS is distributed in the hope that it will be useful,
229Sjkhbut WITHOUT ANY WARRANTY; without even the implied warranty of
239SjkhMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
249SjkhGNU General Public License for more details.
259Sjkh
269SjkhYou should have received a copy of the GNU General Public License
2711894Speteralong with RCS; see the file COPYING.
2811894SpeterIf not, write to the Free Software Foundation,
2911894Speter59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
309Sjkh
319SjkhReport problems and direct all questions to:
329Sjkh
339Sjkh    rcs-bugs@cs.purdue.edu
349Sjkh
359Sjkh*/
369Sjkh
379Sjkh
389Sjkh
399Sjkh
4011894Speter/*
4111894Speter * Revision 5.16  1995/06/16 06:19:24  eggert
4211894Speter * Update FSF address.
438858Srgrimes *
4411894Speter * Revision 5.15  1995/06/01 16:23:43  eggert
4511894Speter * (basefilename): Renamed from basename to avoid collisions.
4611894Speter * (dirlen): Remove (for similar reasons).
4711894Speter * (rcsreadopen): Open with FOPEN_RB.
4811894Speter * (SLASHSLASH_is_SLASH): Default is 0.
4911894Speter * (getcwd): Work around bad_wait_if_SIGCHLD_ignored bug.
5011894Speter *
5111894Speter * Revision 5.14  1994/03/17 14:05:48  eggert
5211894Speter * Strip trailing SLASHes from TMPDIR; some systems need this.  Remove lint.
5311894Speter *
5411894Speter * Revision 5.13  1993/11/03 17:42:27  eggert
5511894Speter * Determine whether a file name is too long indirectly,
5611894Speter * by examining inode numbers, instead of trying to use operating system
5711894Speter * primitives like pathconf, which are not trustworthy in general.
5811894Speter * File names may now hold white space or $.
5911894Speter * Do not flatten ../X in pathnames; that may yield wrong answer for symlinks.
6011894Speter * Add getabsname hook.  Improve quality of diagnostics.
6111894Speter *
6211894Speter * Revision 5.12  1992/07/28  16:12:44  eggert
6311894Speter * Add .sty.  .pl now implies Perl, not Prolog.  Fix fdlock initialization bug.
6411894Speter * Check that $PWD is really ".".  Be consistent about pathnames vs filenames.
6511894Speter *
6611894Speter * Revision 5.11  1992/02/17  23:02:25  eggert
6711894Speter * `a/RCS/b/c' is now an RCS file with an empty extension, not just `a/b/RCS/c'.
6811894Speter *
6911894Speter * Revision 5.10  1992/01/24  18:44:19  eggert
7011894Speter * Fix bug: Expand and Ignored weren't reinitialized.
7111894Speter * Avoid `char const c=ch;' compiler bug.
7211894Speter * Add support for bad_creat0.
7311894Speter *
7411894Speter * Revision 5.9  1992/01/06  02:42:34  eggert
7511894Speter * Shorten long (>31 chars) name.
7611894Speter * while (E) ; -> while (E) continue;
7711894Speter *
789Sjkh * Revision 5.8  1991/09/24  00:28:40  eggert
799Sjkh * Don't export bindex().
809Sjkh *
819Sjkh * Revision 5.7  1991/08/19  03:13:55  eggert
829Sjkh * Fix messages when rcswriteopen fails.
839Sjkh * Look in $TMP and $TEMP if $TMPDIR isn't set.  Tune.
849Sjkh *
859Sjkh * Revision 5.6  1991/04/21  11:58:23  eggert
869Sjkh * Fix errno bugs.  Add -x, RCSINIT, MS-DOS support.
879Sjkh *
889Sjkh * Revision 5.5  1991/02/26  17:48:38  eggert
899Sjkh * Fix setuid bug.  Support new link behavior.
909Sjkh * Define more portable getcwd().
919Sjkh *
929Sjkh * Revision 5.4  1990/11/01  05:03:43  eggert
939Sjkh * Permit arbitrary data in comment leaders.
949Sjkh *
959Sjkh * Revision 5.3  1990/09/14  22:56:16  hammer
969Sjkh * added more filename extensions and their comment leaders
979Sjkh *
989Sjkh * Revision 5.2  1990/09/04  08:02:23  eggert
999Sjkh * Fix typo when !RCSSEP.
1009Sjkh *
1019Sjkh * Revision 5.1  1990/08/29  07:13:59  eggert
1029Sjkh * Work around buggy compilers with defective argument promotion.
1039Sjkh *
1049Sjkh * Revision 5.0  1990/08/22  08:12:50  eggert
1059Sjkh * Ignore signals when manipulating the semaphore file.
10611894Speter * Modernize list of filename extensions.
10711894Speter * Permit paths of arbitrary length.  Beware filenames beginning with "-".
1089Sjkh * Remove compile-time limits; use malloc instead.
1099Sjkh * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
1109Sjkh * Ansify and Posixate.
1119Sjkh * Don't use access().  Fix test for non-regular files.  Tune.
1129Sjkh *
1139Sjkh * Revision 4.8  89/05/01  15:09:41  narten
1149Sjkh * changed getwd to not stat empty directories.
1158858Srgrimes *
1169Sjkh * Revision 4.7  88/08/09  19:12:53  eggert
1179Sjkh * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint.
1188858Srgrimes *
1199Sjkh * Revision 4.6  87/12/18  11:40:23  narten
1209Sjkh * additional file types added from 4.3 BSD version, and SPARC assembler
1219Sjkh * comment character added. Also, more lint cleanups. (Guy Harris)
1228858Srgrimes *
1239Sjkh * Revision 4.5  87/10/18  10:34:16  narten
1249Sjkh * Updating version numbers. Changes relative to 1.1 actually relative
1259Sjkh * to verion 4.3
1268858Srgrimes *
1279Sjkh * Revision 1.3  87/03/27  14:22:21  jenkins
1289Sjkh * Port to suns
1298858Srgrimes *
1309Sjkh * Revision 1.2  85/06/26  07:34:28  svb
1319Sjkh * Comment leader '% ' for '*.tex' files added.
1328858Srgrimes *
1339Sjkh * Revision 4.3  83/12/15  12:26:48  wft
13411894Speter * Added check for KDELIM in filenames to pairfilenames().
1358858Srgrimes *
1369Sjkh * Revision 4.2  83/12/02  22:47:45  wft
13711894Speter * Added csh, red, and sl filename suffixes.
1388858Srgrimes *
1399Sjkh * Revision 4.1  83/05/11  16:23:39  wft
1409Sjkh * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
1419Sjkh * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
1429Sjkh * 2. added getting the file status of RCS and working files;
1439Sjkh * 3. added ignoring of directories.
1448858Srgrimes *
1459Sjkh * Revision 3.7  83/05/11  15:01:58  wft
14611894Speter * Added comtable[] which pairs filename suffixes with comment leaders;
1479Sjkh * updated InitAdmin() accordingly.
1488858Srgrimes *
1499Sjkh * Revision 3.6  83/04/05  14:47:36  wft
1509Sjkh * fixed Suffix in InitAdmin().
1518858Srgrimes *
1529Sjkh * Revision 3.5  83/01/17  18:01:04  wft
1539Sjkh * Added getwd() and rename(); these can be removed by defining
1549Sjkh * V4_2BSD, since they are not needed in 4.2 bsd.
1559Sjkh * Changed sys/param.h to sys/types.h.
1569Sjkh *
1579Sjkh * Revision 3.4  82/12/08  21:55:20  wft
1589Sjkh * removed unused variable.
1599Sjkh *
1609Sjkh * Revision 3.3  82/11/28  20:31:37  wft
16111894Speter * Changed mktempfile() to store the generated filenames.
1629Sjkh * Changed getfullRCSname() to store the file and pathname, and to
1639Sjkh * delete leading "../" and "./".
1649Sjkh *
1659Sjkh * Revision 3.2  82/11/12  14:29:40  wft
1669Sjkh * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
1679Sjkh * checksuffix(), checkfullpath(). Semaphore name generation updated.
1689Sjkh * mktempfile() now checks for nil path; freefilename initialized properly.
1699Sjkh * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
1709Sjkh * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
1719Sjkh *
1729Sjkh * Revision 3.1  82/10/18  14:51:28  wft
1739Sjkh * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
1749Sjkh * renamed checkpath() to checkfullpath().
1759Sjkh */
1769Sjkh
1779Sjkh
1789Sjkh#include "rcsbase.h"
1799Sjkh
18050472SpeterlibId(fnmsId, "$FreeBSD$")
1819Sjkh
18211894Speterstatic char const *bindex P((char const*,int));
18311894Speterstatic int fin2open P((char const*, size_t, char const*, size_t, char const*, size_t, RILE*(*)P((struct buf*,struct stat*,int)), int));
18411894Speterstatic int finopen P((RILE*(*)P((struct buf*,struct stat*,int)), int));
18511894Speterstatic int suffix_matches P((char const*,char const*));
18611894Speterstatic size_t dir_useful_len P((char const*));
18711894Speterstatic size_t suffixlen P((char const*));
18811894Speterstatic void InitAdmin P((void));
18911894Speter
19011894Speterchar const *RCSname;
19111894Speterchar *workname;
19211894Speterint fdlock;
1939SjkhFILE *workstdout;
1949Sjkhstruct stat RCSstat;
1959Sjkhchar const *suffixes;
1969Sjkh
1979Sjkhstatic char const rcsdir[] = "RCS";
19811894Speter#define rcslen (sizeof(rcsdir)-1)
1999Sjkh
2009Sjkhstatic struct buf RCSbuf, RCSb;
2019Sjkhstatic int RCSerrno;
2029Sjkh
2039Sjkh
20411894Speter/* Temp names to be unlinked when done, if they are not 0.  */
2059Sjkh#define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */
20611894Speterstatic char *volatile tpnames[TEMPNAMES];
2079Sjkh
2089Sjkh
2099Sjkhstruct compair {
2109Sjkh	char const *suffix, *comlead;
2119Sjkh};
2129Sjkh
21311894Speter/*
21411894Speter* This table is present only for backwards compatibility.
21511894Speter* Normally we ignore this table, and use the prefix of the `$Log' line instead.
21611894Speter*/
2179Sjkhstatic struct compair const comtable[] = {
21811894Speter	{ "a"	, "-- "	},	/* Ada */
21911894Speter	{ "ada"	, "-- "	},
22011894Speter	{ "adb"	, "-- "	},
22111894Speter	{ "ads"	, "-- "	},
22211894Speter	{ "asm"	, ";; "	},	/* assembler (MS-DOS) */
22311894Speter	{ "bat"	, ":: "	},	/* batch (MS-DOS) */
22411894Speter	{ "body", "-- "	},	/* Ada */
22511894Speter	{ "c"	, " * "	},	/* C */
22611894Speter	{ "c++"	, "// "	},	/* C++ in all its infinite guises */
22711894Speter	{ "cc"	, "// "	},
22811894Speter	{ "cpp"	, "// "	},
22911894Speter	{ "cxx"	, "// "	},
23011894Speter	{ "cl"	, ";;; "},	/* Common Lisp */
23111894Speter	{ "cmd"	, ":: "	},	/* command (OS/2) */
23211894Speter	{ "cmf"	, "c "	},	/* CM Fortran */
23311894Speter	{ "cs"	, " * "	},	/* C* */
23411894Speter	{ "el"	, "; "	},	/* Emacs Lisp */
23511894Speter	{ "f"	, "c "	},	/* Fortran */
23611894Speter	{ "for"	, "c "	},
23711894Speter	{ "h"	, " * "	},	/* C-header */
23811894Speter	{ "hpp"	, "// "	},	/* C++ header */
23911894Speter	{ "hxx"	, "// "	},
24011894Speter	{ "l"	, " * "	},	/* lex (NOTE: franzlisp disagrees) */
24111894Speter	{ "lisp", ";;; "},	/* Lucid Lisp */
24211894Speter	{ "lsp"	, ";; "	},	/* Microsoft Lisp */
24311894Speter	{ "m"   , "// " },	/* Objective C */
24411894Speter	{ "mac"	, ";; "	},	/* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
24511894Speter	{ "me"	, ".\\\" "},	/* troff -me */
24611894Speter	{ "ml"	, "; "	},	/* mocklisp */
24711894Speter	{ "mm"	, ".\\\" "},	/* troff -mm */
24811894Speter	{ "ms"	, ".\\\" "},	/* troff -ms */
24911894Speter	{ "p"	, " * "	},	/* Pascal */
25011894Speter	{ "pas"	, " * "	},
25111894Speter	{ "ps"	, "% "	},	/* PostScript */
25211894Speter	{ "spec", "-- "	},	/* Ada */
25311894Speter	{ "sty"	, "% "	},	/* LaTeX style */
25411894Speter	{ "tex"	, "% "	},	/* TeX */
25511894Speter	{ "y"	, " * "	},	/* yacc */
25611894Speter	{ 0	, "# "	}	/* default for unknown suffix; must be last */
2579Sjkh};
2589Sjkh
2599Sjkh#if has_mktemp
26011894Speter	static char const *tmp P((void));
2619Sjkh	static char const *
2629Sjkhtmp()
2639Sjkh/* Yield the name of the tmp directory.  */
2649Sjkh{
2659Sjkh	static char const *s;
2669Sjkh	if (!s
2679Sjkh		&&  !(s = cgetenv("TMPDIR"))	/* Unix tradition */
2689Sjkh		&&  !(s = cgetenv("TMP"))	/* DOS tradition */
2699Sjkh		&&  !(s = cgetenv("TEMP"))	/* another DOS tradition */
2709Sjkh	)
2719Sjkh		s = TMPDIR;
2729Sjkh	return s;
2739Sjkh}
2749Sjkh#endif
2759Sjkh
2769Sjkh	char const *
2779Sjkhmaketemp(n)
2789Sjkh	int n;
27911894Speter/* Create a unique pathname using n and the process id and store it
28011894Speter * into the nth slot in tpnames.
28111894Speter * Because of storage in tpnames, tempunlink() can unlink the file later.
28211894Speter * Return a pointer to the pathname created.
2839Sjkh */
2849Sjkh{
2859Sjkh	char *p;
28611894Speter	char const *t = tpnames[n];
28776301Skris#	if has_mktemp
28876301Skris	int fd;
28976301Skris#	endif
2909Sjkh
2919Sjkh	if (t)
2929Sjkh		return t;
2939Sjkh
2949Sjkh	catchints();
2959Sjkh	{
2969Sjkh#	if has_mktemp
2979Sjkh	    char const *tp = tmp();
29811894Speter	    size_t tplen = dir_useful_len(tp);
29911894Speter	    p = testalloc(tplen + 10);
30011894Speter	    VOID sprintf(p, "%.*s%cT%cXXXXXX", (int)tplen, tp, SLASH, '0'+n);
30176301Skris	    fd = mkstemp(p);
30276301Skris	    if (fd < 0 || !*p)
30311894Speter		faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'",
30411894Speter			(int)tplen, tp, SLASH, '0'+n
3059Sjkh		);
30676301Skris	    close(fd);
3079Sjkh#	else
30811894Speter	    static char tpnamebuf[TEMPNAMES][L_tmpnam];
30911894Speter	    p = tpnamebuf[n];
3109Sjkh	    if (!tmpnam(p) || !*p)
3119Sjkh#		ifdef P_tmpdir
31211894Speter		    faterror("can't make temporary pathname `%s...'",P_tmpdir);
3139Sjkh#		else
31411894Speter		    faterror("can't make temporary pathname");
3159Sjkh#		endif
3169Sjkh#	endif
3179Sjkh	}
3189Sjkh
31911894Speter	tpnames[n] = p;
3209Sjkh	return p;
3219Sjkh}
3229Sjkh
3239Sjkh	void
3249Sjkhtempunlink()
3259Sjkh/* Clean up maketemp() files.  May be invoked by signal handler.
3269Sjkh */
3279Sjkh{
3289Sjkh	register int i;
3299Sjkh	register char *p;
3309Sjkh
3319Sjkh	for (i = TEMPNAMES;  0 <= --i;  )
33211894Speter	    if ((p = tpnames[i])) {
3339Sjkh		VOID unlink(p);
3349Sjkh		/*
3359Sjkh		 * We would tfree(p) here,
3369Sjkh		 * but this might dump core if we're handing a signal.
3379Sjkh		 * We're about to exit anyway, so we won't bother.
3389Sjkh		 */
33911894Speter		tpnames[i] = 0;
3409Sjkh	    }
3419Sjkh}
3429Sjkh
3439Sjkh
3449Sjkh	static char const *
34511894Speterbindex(sp, c)
3469Sjkh	register char const *sp;
34711894Speter	register int c;
3489Sjkh/* Function: Finds the last occurrence of character c in string sp
3499Sjkh * and returns a pointer to the character just beyond it. If the
3509Sjkh * character doesn't occur in the string, sp is returned.
3519Sjkh */
3529Sjkh{
35311894Speter	register char const *r;
3549Sjkh        r = sp;
3559Sjkh        while (*sp) {
3569Sjkh                if (*sp++ == c) r=sp;
3579Sjkh        }
3589Sjkh        return r;
3599Sjkh}
3609Sjkh
3619Sjkh
3629Sjkh
3639Sjkh	static int
3649Sjkhsuffix_matches(suffix, pattern)
3659Sjkh	register char const *suffix, *pattern;
3669Sjkh{
3679Sjkh	register int c;
3689Sjkh	if (!pattern)
3699Sjkh		return true;
3709Sjkh	for (;;)
3719Sjkh		switch (*suffix++ - (c = *pattern++)) {
3729Sjkh		    case 0:
3739Sjkh			if (!c)
3749Sjkh				return true;
3759Sjkh			break;
3769Sjkh
3779Sjkh		    case 'A'-'a':
3789Sjkh			if (ctab[c] == Letter)
3799Sjkh				break;
3809Sjkh			/* fall into */
3819Sjkh		    default:
3829Sjkh			return false;
3839Sjkh		}
3849Sjkh}
3859Sjkh
3869Sjkh
3879Sjkh	static void
3889SjkhInitAdmin()
3899Sjkh/* function: initializes an admin node */
3909Sjkh{
3919Sjkh	register char const *Suffix;
3929Sjkh        register int i;
3939Sjkh
39411894Speter	Head=0; Dbranch=0; AccessList=0; Symbols=0; Locks=0;
3959Sjkh        StrictLocks=STRICT_LOCKING;
3969Sjkh
3979Sjkh        /* guess the comment leader from the suffix*/
39811894Speter	Suffix = bindex(workname, '.');
39911894Speter	if (Suffix==workname) Suffix= ""; /* empty suffix; will get default*/
4009Sjkh	for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++)
40111894Speter		continue;
4029Sjkh	Comment.string = comtable[i].comlead;
4039Sjkh	Comment.size = strlen(comtable[i].comlead);
40411894Speter	Expand = KEYVAL_EXPAND;
40511894Speter	clear_buf(&Ignored);
4069Sjkh	Lexinit(); /* note: if !finptr, reads nothing; only initializes */
4079Sjkh}
4089Sjkh
4099Sjkh
4109Sjkh
4119Sjkh	void
4129Sjkhbufalloc(b, size)
4139Sjkh	register struct buf *b;
4149Sjkh	size_t size;
4159Sjkh/* Ensure *B is a name buffer of at least SIZE bytes.
4169Sjkh * *B's old contents can be freed; *B's new contents are undefined.
4179Sjkh */
4189Sjkh{
4199Sjkh	if (b->size < size) {
4209Sjkh		if (b->size)
4219Sjkh			tfree(b->string);
4229Sjkh		else
4239Sjkh			b->size = sizeof(malloc_type);
4249Sjkh		while (b->size < size)
4259Sjkh			b->size <<= 1;
4269Sjkh		b->string = tnalloc(char, b->size);
4279Sjkh	}
4289Sjkh}
4299Sjkh
4309Sjkh	void
4319Sjkhbufrealloc(b, size)
4329Sjkh	register struct buf *b;
4339Sjkh	size_t size;
4349Sjkh/* like bufalloc, except *B's old contents, if any, are preserved */
4359Sjkh{
4369Sjkh	if (b->size < size) {
4379Sjkh		if (!b->size)
4389Sjkh			bufalloc(b, size);
4399Sjkh		else {
4409Sjkh			while ((b->size <<= 1)  <  size)
44111894Speter				continue;
4429Sjkh			b->string = trealloc(char, b->string, b->size);
4439Sjkh		}
4449Sjkh	}
4459Sjkh}
4469Sjkh
4479Sjkh	void
4489Sjkhbufautoend(b)
4499Sjkh	struct buf *b;
4509Sjkh/* Free an auto buffer at block exit. */
4519Sjkh{
4529Sjkh	if (b->size)
4539Sjkh		tfree(b->string);
4549Sjkh}
4559Sjkh
4569Sjkh	struct cbuf
4579Sjkhbufremember(b, s)
4589Sjkh	struct buf *b;
4599Sjkh	size_t s;
4609Sjkh/*
4619Sjkh * Free the buffer B with used size S.
4629Sjkh * Yield a cbuf with identical contents.
4639Sjkh * The cbuf will be reclaimed when this input file is finished.
4649Sjkh */
4659Sjkh{
4669Sjkh	struct cbuf cb;
4679Sjkh
4689Sjkh	if ((cb.size = s))
4699Sjkh		cb.string = fremember(trealloc(char, b->string, s));
4709Sjkh	else {
4719Sjkh		bufautoend(b); /* not really auto */
4729Sjkh		cb.string = "";
4739Sjkh	}
4749Sjkh	return cb;
4759Sjkh}
4769Sjkh
4779Sjkh	char *
4789Sjkhbufenlarge(b, alim)
4799Sjkh	register struct buf *b;
4809Sjkh	char const **alim;
4819Sjkh/* Make *B larger.  Set *ALIM to its new limit, and yield the relocated value
4829Sjkh * of its old limit.
4839Sjkh */
4849Sjkh{
4859Sjkh	size_t s = b->size;
4869Sjkh	bufrealloc(b, s + 1);
4879Sjkh	*alim = b->string + b->size;
4889Sjkh	return b->string + s;
4899Sjkh}
4909Sjkh
4919Sjkh	void
4929Sjkhbufscat(b, s)
4939Sjkh	struct buf *b;
4949Sjkh	char const *s;
4959Sjkh/* Concatenate S to B's end. */
4969Sjkh{
4979Sjkh	size_t blen  =  b->string ? strlen(b->string) : 0;
4989Sjkh	bufrealloc(b, blen+strlen(s)+1);
4999Sjkh	VOID strcpy(b->string+blen, s);
5009Sjkh}
5019Sjkh
5029Sjkh	void
5039Sjkhbufscpy(b, s)
5049Sjkh	struct buf *b;
5059Sjkh	char const *s;
5069Sjkh/* Copy S into B. */
5079Sjkh{
5089Sjkh	bufalloc(b, strlen(s)+1);
5099Sjkh	VOID strcpy(b->string, s);
5109Sjkh}
5119Sjkh
5129Sjkh
5139Sjkh	char const *
51411894Speterbasefilename(p)
5159Sjkh	char const *p;
5169Sjkh/* Yield the address of the base filename of the pathname P.  */
5179Sjkh{
5189Sjkh	register char const *b = p, *q = p;
5199Sjkh	for (;;)
5209Sjkh	    switch (*q++) {
5219Sjkh		case SLASHes: b = q; break;
5229Sjkh		case 0: return b;
5239Sjkh	    }
5249Sjkh}
5259Sjkh
5269Sjkh
5279Sjkh	static size_t
5289Sjkhsuffixlen(x)
5299Sjkh	char const *x;
53011894Speter/* Yield the length of X, an RCS pathname suffix.  */
5319Sjkh{
5329Sjkh	register char const *p;
5339Sjkh
5349Sjkh	p = x;
5359Sjkh	for (;;)
5369Sjkh	    switch (*p) {
5379Sjkh		case 0: case SLASHes:
5389Sjkh		    return p - x;
5399Sjkh
5409Sjkh		default:
5419Sjkh		    ++p;
5429Sjkh		    continue;
5439Sjkh	    }
5449Sjkh}
5459Sjkh
5469Sjkh	char const *
5479Sjkhrcssuffix(name)
5489Sjkh	char const *name;
54911894Speter/* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise.  */
5509Sjkh{
5519Sjkh	char const *x, *p, *nz;
55211894Speter	size_t nl, xl;
5539Sjkh
5549Sjkh	nl = strlen(name);
5559Sjkh	nz = name + nl;
5569Sjkh	x = suffixes;
5579Sjkh	do {
5589Sjkh	    if ((xl = suffixlen(x))) {
5599Sjkh		if (xl <= nl  &&  memcmp(p = nz-xl, x, xl) == 0)
5609Sjkh		    return p;
56111894Speter	    } else
56211894Speter		for (p = name;  p < nz - rcslen;  p++)
56311894Speter		    if (
56411894Speter			isSLASH(p[rcslen])
56511894Speter			&& (p==name || isSLASH(p[-1]))
56611894Speter			&& memcmp(p, rcsdir, rcslen) == 0
56711894Speter		    )
56811894Speter			return nz;
5699Sjkh	    x += xl;
5709Sjkh	} while (*x++);
5719Sjkh	return 0;
5729Sjkh}
5739Sjkh
5749Sjkh	/*ARGSUSED*/ RILE *
57511894Speterrcsreadopen(RCSpath, status, mustread)
57611894Speter	struct buf *RCSpath;
5779Sjkh	struct stat *status;
5789Sjkh	int mustread;
57911894Speter/* Open RCSPATH for reading and yield its FILE* descriptor.
5809Sjkh * If successful, set *STATUS to its status.
58111894Speter * Pass this routine to pairnames() for read-only access to the file.  */
5829Sjkh{
58311894Speter	return Iopen(RCSpath->string, FOPEN_RB, status);
5849Sjkh}
5859Sjkh
5869Sjkh	static int
5879Sjkhfinopen(rcsopen, mustread)
5889Sjkh	RILE *(*rcsopen)P((struct buf*,struct stat*,int));
5899Sjkh	int mustread;
5909Sjkh/*
5919Sjkh * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
5929Sjkh * Set finptr to the result and yield true if successful.
5939Sjkh * RCSb holds the file's name.
5949Sjkh * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
5959Sjkh * Yield true if successful or if an unusual failure.
5969Sjkh */
5979Sjkh{
5989Sjkh	int interesting, preferold;
5999Sjkh
6009Sjkh	/*
6019Sjkh	 * We prefer an old name to that of a nonexisting new RCS file,
6029Sjkh	 * unless we tried locking the old name and failed.
6039Sjkh	 */
60411894Speter	preferold  =  RCSbuf.string[0] && (mustread||0<=fdlock);
6059Sjkh
6069Sjkh	finptr = (*rcsopen)(&RCSb, &RCSstat, mustread);
6079Sjkh	interesting = finptr || errno!=ENOENT;
6089Sjkh	if (interesting || !preferold) {
6099Sjkh		/* Use the new name.  */
6109Sjkh		RCSerrno = errno;
6119Sjkh		bufscpy(&RCSbuf, RCSb.string);
6129Sjkh	}
6139Sjkh	return interesting;
6149Sjkh}
6159Sjkh
6169Sjkh	static int
6179Sjkhfin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread)
6189Sjkh	char const *d, *base, *x;
6199Sjkh	size_t dlen, baselen, xlen;
6209Sjkh	RILE *(*rcsopen)P((struct buf*,struct stat*,int));
6219Sjkh	int mustread;
6229Sjkh/*
6239Sjkh * D is a directory name with length DLEN (including trailing slash).
6249Sjkh * BASE is a filename with length BASELEN.
62511894Speter * X is an RCS pathname suffix with length XLEN.
6269Sjkh * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
6279Sjkh * Yield true if successful.
6289Sjkh * Try dRCS/basex first; if that fails and x is nonempty, try dbasex.
6299Sjkh * Put these potential names in RCSb.
6309Sjkh * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
6319Sjkh * Yield true if successful or if an unusual failure.
6329Sjkh */
6339Sjkh{
6349Sjkh	register char *p;
6359Sjkh
63611894Speter	bufalloc(&RCSb, dlen + rcslen + 1 + baselen + xlen + 1);
6379Sjkh
6389Sjkh	/* Try dRCS/basex.  */
6399Sjkh	VOID memcpy(p = RCSb.string, d, dlen);
64011894Speter	VOID memcpy(p += dlen, rcsdir, rcslen);
64111894Speter	p += rcslen;
6429Sjkh	*p++ = SLASH;
6439Sjkh	VOID memcpy(p, base, baselen);
6449Sjkh	VOID memcpy(p += baselen, x, xlen);
6459Sjkh	p[xlen] = 0;
6469Sjkh	if (xlen) {
6479Sjkh	    if (finopen(rcsopen, mustread))
6489Sjkh		return true;
6499Sjkh
6509Sjkh	    /* Try dbasex.  */
6519Sjkh	    /* Start from scratch, because finopen() may have changed RCSb.  */
6529Sjkh	    VOID memcpy(p = RCSb.string, d, dlen);
6539Sjkh	    VOID memcpy(p += dlen, base, baselen);
6549Sjkh	    VOID memcpy(p += baselen, x, xlen);
6559Sjkh	    p[xlen] = 0;
6569Sjkh	}
6579Sjkh	return finopen(rcsopen, mustread);
6589Sjkh}
6599Sjkh
6609Sjkh	int
66111894Speterpairnames(argc, argv, rcsopen, mustread, quiet)
6629Sjkh	int argc;
6639Sjkh	char **argv;
6649Sjkh	RILE *(*rcsopen)P((struct buf*,struct stat*,int));
6659Sjkh	int mustread, quiet;
66611894Speter/*
66711894Speter * Pair the pathnames pointed to by argv; argc indicates
6689Sjkh * how many there are.
66911894Speter * Place a pointer to the RCS pathname into RCSname,
67011894Speter * and a pointer to the pathname of the working file into workname.
67111894Speter * If both are given, and workstdout
6729Sjkh * is set, a warning is printed.
6739Sjkh *
6749Sjkh * If the RCS file exists, places its status into RCSstat.
6759Sjkh *
6769Sjkh * If the RCS file exists, it is RCSOPENed for reading, the file pointer
6779Sjkh * is placed into finptr, and the admin-node is read in; returns 1.
6789Sjkh * If the RCS file does not exist and MUSTREAD,
6799Sjkh * print an error unless QUIET and return 0.
6809Sjkh * Otherwise, initialize the admin node and return -1.
6819Sjkh *
6829Sjkh * 0 is returned on all errors, e.g. files that are not regular files.
6839Sjkh */
6849Sjkh{
6859Sjkh	static struct buf tempbuf;
6869Sjkh
6879Sjkh	register char *p, *arg, *RCS1;
68811894Speter	char const *base, *RCSbase, *x;
6899Sjkh	int paired;
6909Sjkh	size_t arglen, dlen, baselen, xlen;
6919Sjkh
69211894Speter	fdlock = -1;
69311894Speter
69411894Speter	if (!(arg = *argv)) return 0; /* already paired pathname */
6959Sjkh	if (*arg == '-') {
69611894Speter		error("%s option is ignored after pathnames", arg);
6979Sjkh		return 0;
6989Sjkh	}
6999Sjkh
70011894Speter	base = basefilename(arg);
7019Sjkh	paired = false;
7029Sjkh
7039Sjkh        /* first check suffix to see whether it is an RCS file or not */
7049Sjkh	if ((x = rcssuffix(arg)))
7059Sjkh	{
70611894Speter		/* RCS pathname given */
7079Sjkh		RCS1 = arg;
70811894Speter		RCSbase = base;
70911894Speter		baselen = x - base;
7109Sjkh		if (
7119Sjkh		    1 < argc  &&
71211894Speter		    !rcssuffix(workname = p = argv[1])  &&
7139Sjkh		    baselen <= (arglen = strlen(p))  &&
71411894Speter		    ((p+=arglen-baselen) == workname  ||  isSLASH(p[-1])) &&
71511894Speter		    memcmp(base, p, baselen) == 0
7169Sjkh		) {
7179Sjkh			argv[1] = 0;
7189Sjkh			paired = true;
7199Sjkh		} else {
72011894Speter			bufscpy(&tempbuf, base);
72111894Speter			workname = p = tempbuf.string;
7229Sjkh			p[baselen] = 0;
7239Sjkh		}
7249Sjkh        } else {
7259Sjkh                /* working file given; now try to find RCS file */
72611894Speter		workname = arg;
72711894Speter		baselen = strlen(base);
72811894Speter		/* Derive RCS pathname.  */
7299Sjkh		if (
7309Sjkh		    1 < argc  &&
7319Sjkh		    (x = rcssuffix(RCS1 = argv[1]))  &&
7329Sjkh		    baselen  <=  x - RCS1  &&
73311894Speter		    ((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) &&
73411894Speter		    memcmp(base, RCSbase, baselen) == 0
7359Sjkh		) {
7369Sjkh			argv[1] = 0;
7379Sjkh			paired = true;
7389Sjkh		} else
73911894Speter			RCSbase = RCS1 = 0;
7409Sjkh        }
74111894Speter	/* Now we have a (tentative) RCS pathname in RCS1 and workname.  */
7429Sjkh        /* Second, try to find the right RCS file */
74311894Speter	if (RCSbase!=RCS1) {
7449Sjkh                /* a path for RCSfile is given; single RCS file to look for */
7459Sjkh		bufscpy(&RCSbuf, RCS1);
7469Sjkh		finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread);
7479Sjkh		RCSerrno = errno;
7489Sjkh        } else {
7499Sjkh		bufscpy(&RCSbuf, "");
7509Sjkh		if (RCS1)
75111894Speter			/* RCS filename was given without path.  */
75211894Speter			VOID fin2open(arg, (size_t)0, RCSbase, baselen,
7539Sjkh				x, strlen(x), rcsopen, mustread
7549Sjkh			);
7559Sjkh		else {
75611894Speter			/* No RCS pathname was given.  */
7579Sjkh			/* Try each suffix in turn.  */
75811894Speter			dlen = base-arg;
7599Sjkh			x = suffixes;
76011894Speter			while (! fin2open(arg, dlen, base, baselen,
7619Sjkh					x, xlen=suffixlen(x), rcsopen, mustread
7629Sjkh			)) {
7639Sjkh				x += xlen;
7649Sjkh				if (!*x++)
7659Sjkh					break;
7669Sjkh			}
7679Sjkh		}
7689Sjkh        }
76911894Speter	RCSname = p = RCSbuf.string;
7709Sjkh	if (finptr) {
7719Sjkh		if (!S_ISREG(RCSstat.st_mode)) {
7729Sjkh			error("%s isn't a regular file -- ignored", p);
7739Sjkh                        return 0;
7749Sjkh                }
7759Sjkh                Lexinit(); getadmin();
7769Sjkh	} else {
77711894Speter		if (RCSerrno!=ENOENT || mustread || fdlock<0) {
7789Sjkh			if (RCSerrno == EEXIST)
7799Sjkh				error("RCS file %s is in use", p);
7809Sjkh			else if (!quiet || RCSerrno!=ENOENT)
7819Sjkh				enerror(RCSerrno, p);
7829Sjkh			return 0;
7839Sjkh		}
7849Sjkh                InitAdmin();
7859Sjkh        };
7869Sjkh
7879Sjkh	if (paired && workstdout)
78811894Speter		workwarn("Working file ignored due to -p option");
7899Sjkh
7909Sjkh	prevkeys = false;
7919Sjkh	return finptr ? 1 : -1;
7929Sjkh}
7939Sjkh
7949Sjkh
7959Sjkh	char const *
7969SjkhgetfullRCSname()
79711894Speter/*
79811894Speter * Return a pointer to the full pathname of the RCS file.
79911894Speter * Remove leading `./'.
8009Sjkh */
8019Sjkh{
80211894Speter	if (ROOTPATH(RCSname)) {
80311894Speter	    return RCSname;
80411894Speter	} else {
80511894Speter	    static struct buf rcsbuf;
80611894Speter#	    if needs_getabsname
80711894Speter		bufalloc(&rcsbuf, SIZEABLE_PATH + 1);
80811894Speter		while (getabsname(RCSname, rcsbuf.string, rcsbuf.size) != 0)
80911894Speter		    if (errno == ERANGE)
81011894Speter			bufalloc(&rcsbuf, rcsbuf.size<<1);
81111894Speter		    else
81211894Speter			efaterror("getabsname");
81311894Speter#	    else
81411894Speter		static char const *wdptr;
81511894Speter		static struct buf wdbuf;
81611894Speter		static size_t wdlen;
8179Sjkh
81811894Speter		register char const *r;
81911894Speter		register size_t dlen;
82011894Speter		register char *d;
82111894Speter		register char const *wd;
8229Sjkh
8239Sjkh		if (!(wd = wdptr)) {
8249Sjkh		    /* Get working directory for the first time.  */
82511894Speter		    char *PWD = cgetenv("PWD");
82611894Speter		    struct stat PWDstat, dotstat;
82711894Speter		    if (! (
82811894Speter			(d = PWD) &&
82911894Speter			ROOTPATH(PWD) &&
83011894Speter			stat(PWD, &PWDstat) == 0 &&
83111894Speter			stat(".", &dotstat) == 0 &&
83211894Speter			same_file(PWDstat, dotstat, 1)
83311894Speter		    )) {
8349Sjkh			bufalloc(&wdbuf, SIZEABLE_PATH + 1);
83511894Speter#			if has_getcwd || !has_getwd
83611894Speter			    while (!(d = getcwd(wdbuf.string, wdbuf.size)))
83711894Speter				if (errno == ERANGE)
83811894Speter				    bufalloc(&wdbuf, wdbuf.size<<1);
83911894Speter				else if ((d = PWD))
84011894Speter				    break;
84111894Speter				else
84211894Speter				    efaterror("getcwd");
84311894Speter#			else
8449Sjkh			    d = getwd(wdbuf.string);
84511894Speter			    if (!d  &&  !(d = PWD))
84611894Speter				efaterror("getwd");
8479Sjkh#			endif
8489Sjkh		    }
84911894Speter		    wdlen = dir_useful_len(d);
85011894Speter		    d[wdlen] = 0;
8519Sjkh		    wdptr = wd = d;
8529Sjkh                }
85311894Speter		/*
85411894Speter		* Remove leading `./'s from RCSname.
85511894Speter		* Do not try to handle `../', since removing it may yield
85611894Speter		* the wrong answer in the presence of symbolic links.
85711894Speter		*/
85811894Speter		for (r = RCSname;  r[0]=='.' && isSLASH(r[1]);  r += 2)
85911894Speter		    /* `.////' is equivalent to `./'.  */
86011894Speter		    while (isSLASH(r[2]))
86111894Speter			r++;
86211894Speter		/* Build full pathname.  */
86311894Speter		dlen = wdlen;
86411894Speter		bufalloc(&rcsbuf, dlen + strlen(r) + 2);
8659Sjkh		d = rcsbuf.string;
86611894Speter		VOID memcpy(d, wd, dlen);
86711894Speter		d += dlen;
8689Sjkh		*d++ = SLASH;
86911894Speter		VOID strcpy(d, r);
87011894Speter#	    endif
87111894Speter	    return rcsbuf.string;
8729Sjkh        }
8739Sjkh}
8749Sjkh
87528407Speter/* Derived from code from the XFree86 project */
87625699Speter	char const *
87725699SpetergetfullCVSname()
87828407Speter/* Function: returns a pointer to the path name of the RCS file with the
87928407Speter * CVSROOT part stripped off, and with 'Attic/' stripped off (if present).
88025699Speter */
88125699Speter{
88225699Speter
88328407Speter#define ATTICDIR "/Attic"
88425699Speter
88528407Speter	char const *namebuf = getfullRCSname();
88628407Speter	char *cvsroot = cgetenv("CVSROOT");
88728407Speter	int cvsrootlen;
88828407Speter	char *c = NULL;
88928407Speter	int alen = strlen(ATTICDIR);
89028407Speter
89128407Speter	if ((c = strrchr(namebuf, '/')) != NULL) {
89228566Speter	    if (namebuf - c >= alen) {
89328407Speter		if (!strncmp(c - alen, ATTICDIR, alen)) {
89428407Speter		    while(*c != '\0') {
89528407Speter			*(c - alen) = *c;
89628407Speter			c++;
89728407Speter		    }
89828407Speter		    *(c - alen) = '\0';
89925699Speter		}
90028407Speter	    }
90125699Speter	}
90228407Speter
90328407Speter	if (!cvsroot)
90428407Speter	    return(namebuf);
90528407Speter	else
90628407Speter	{
90728407Speter	    cvsrootlen = strlen(cvsroot);
90828407Speter	    if (!strncmp(namebuf, cvsroot, cvsrootlen) &&
90928407Speter	        namebuf[cvsrootlen] == '/')
91028407Speter		return(namebuf + cvsrootlen + 1);
91128407Speter	    else
91228407Speter		return(namebuf);
91328407Speter	}
91425699Speter}
91525699Speter
91611894Speter	static size_t
91711894Speterdir_useful_len(d)
91811894Speter	char const *d;
91911894Speter/*
92011894Speter* D names a directory; yield the number of characters of D's useful part.
92111894Speter* To create a file in D, append a SLASH and a file name to D's useful part.
92211894Speter* Ignore trailing slashes if possible; not only are they ugly,
92311894Speter* but some non-Posix systems misbehave unless the slashes are omitted.
92411894Speter*/
92511894Speter{
92611894Speter#	ifndef SLASHSLASH_is_SLASH
92711894Speter#	define SLASHSLASH_is_SLASH 0
92811894Speter#	endif
92911894Speter	size_t dlen = strlen(d);
93011894Speter	if (!SLASHSLASH_is_SLASH && dlen==2 && isSLASH(d[0]) && isSLASH(d[1]))
93111894Speter	    --dlen;
93211894Speter	else
93311894Speter	    while (dlen && isSLASH(d[dlen-1]))
93411894Speter		--dlen;
93511894Speter	return dlen;
93611894Speter}
93711894Speter
9389Sjkh#ifndef isSLASH
9399Sjkh	int
9409SjkhisSLASH(c)
9419Sjkh	int c;
9429Sjkh{
9439Sjkh	switch (c) {
9449Sjkh	    case SLASHes:
9459Sjkh		return true;
9469Sjkh	    default:
9479Sjkh		return false;
9489Sjkh	}
9499Sjkh}
9509Sjkh#endif
9519Sjkh
9529Sjkh
9539Sjkh#if !has_getcwd && !has_getwd
9549Sjkh
9559Sjkh	char *
9569Sjkhgetcwd(path, size)
9579Sjkh	char *path;
9589Sjkh	size_t size;
9599Sjkh{
9609Sjkh	static char const usrbinpwd[] = "/usr/bin/pwd";
9619Sjkh#	define binpwd (usrbinpwd+4)
9629Sjkh
9639Sjkh	register FILE *fp;
9649Sjkh	register int c;
9659Sjkh	register char *p, *lim;
9669Sjkh	int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus;
9679Sjkh	pid_t child;
9689Sjkh
9699Sjkh	if (!size) {
9709Sjkh		errno = EINVAL;
9719Sjkh		return 0;
9729Sjkh	}
9739Sjkh	if (pipe(fd) != 0)
9749Sjkh		return 0;
97511894Speter#	if bad_wait_if_SIGCHLD_ignored
97611894Speter#		ifndef SIGCHLD
97711894Speter#		define SIGCHLD SIGCLD
97811894Speter#		endif
97911894Speter		VOID signal(SIGCHLD, SIG_DFL);
98011894Speter#	endif
9819Sjkh	if (!(child = vfork())) {
9829Sjkh		if (
9839Sjkh			close(fd[0]) == 0 &&
9849Sjkh			(fd[1] == STDOUT_FILENO ||
9859Sjkh#				ifdef F_DUPFD
9869Sjkh					(VOID close(STDOUT_FILENO),
9879Sjkh					fcntl(fd[1], F_DUPFD, STDOUT_FILENO))
9889Sjkh#				else
9899Sjkh					dup2(fd[1], STDOUT_FILENO)
9909Sjkh#				endif
9919Sjkh				== STDOUT_FILENO &&
9929Sjkh				close(fd[1]) == 0
9939Sjkh			)
9949Sjkh		) {
9959Sjkh			VOID close(STDERR_FILENO);
9969Sjkh			VOID execl(binpwd, binpwd, (char *)0);
9979Sjkh			VOID execl(usrbinpwd, usrbinpwd, (char *)0);
9989Sjkh		}
9999Sjkh		_exit(EXIT_FAILURE);
10009Sjkh	}
10019Sjkh	e = errno;
10029Sjkh	closeerror = close(fd[1]);
10039Sjkh	closeerrno = errno;
10049Sjkh	fp = 0;
10059Sjkh	readerror = toolong = wstatus = 0;
10069Sjkh	p = path;
10079Sjkh	if (0 <= child) {
10089Sjkh		fp = fdopen(fd[0], "r");
10099Sjkh		e = errno;
10109Sjkh		if (fp) {
10119Sjkh			lim = p + size;
10129Sjkh			for (p = path;  ;  *p++ = c) {
10139Sjkh				if ((c=getc(fp)) < 0) {
10149Sjkh					if (feof(fp))
10159Sjkh						break;
10169Sjkh					if (ferror(fp)) {
10179Sjkh						readerror = 1;
10189Sjkh						e = errno;
10199Sjkh						break;
10209Sjkh					}
10219Sjkh				}
10229Sjkh				if (p == lim) {
10239Sjkh					toolong = 1;
10249Sjkh					break;
10259Sjkh				}
10269Sjkh			}
10279Sjkh		}
10289Sjkh#		if has_waitpid
10299Sjkh			if (waitpid(child, &wstatus, 0) < 0)
10309Sjkh				wstatus = 1;
10319Sjkh#		else
103211894Speter			{
103311894Speter				pid_t w;
103411894Speter				do {
103511894Speter					if ((w = wait(&wstatus)) < 0) {
103611894Speter						wstatus = 1;
103711894Speter						break;
103811894Speter					}
103911894Speter				} while (w != child);
104011894Speter			}
10419Sjkh#		endif
10429Sjkh	}
10439Sjkh	if (!fp) {
10449Sjkh		VOID close(fd[0]);
10459Sjkh		errno = e;
10469Sjkh		return 0;
10479Sjkh	}
10489Sjkh	if (fclose(fp) != 0)
10499Sjkh		return 0;
10509Sjkh	if (readerror) {
10519Sjkh		errno = e;
10529Sjkh		return 0;
10539Sjkh	}
10549Sjkh	if (closeerror) {
10559Sjkh		errno = closeerrno;
10569Sjkh		return 0;
10579Sjkh	}
10589Sjkh	if (toolong) {
10599Sjkh		errno = ERANGE;
10609Sjkh		return 0;
10619Sjkh	}
10629Sjkh	if (wstatus  ||  p == path  ||  *--p != '\n') {
10639Sjkh		errno = EACCES;
10649Sjkh		return 0;
10659Sjkh	}
10669Sjkh	*p = '\0';
10679Sjkh	return path;
10689Sjkh}
10699Sjkh#endif
10709Sjkh
10719Sjkh
10729Sjkh#ifdef PAIRTEST
107311894Speter/* test program for pairnames() and getfullRCSname() */
10749Sjkh
10759Sjkhchar const cmdid[] = "pair";
10769Sjkh
10779Sjkhmain(argc, argv)
10789Sjkhint argc; char *argv[];
10799Sjkh{
10809Sjkh        int result;
10819Sjkh	int initflag;
10829Sjkh	quietflag = initflag = false;
10839Sjkh
10849Sjkh        while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
10859Sjkh                switch ((*argv)[1]) {
10869Sjkh
10879Sjkh		case 'p':       workstdout = stdout;
10889Sjkh                                break;
10899Sjkh                case 'i':       initflag=true;
10909Sjkh                                break;
10919Sjkh                case 'q':       quietflag=true;
10929Sjkh                                break;
10939Sjkh                default:        error("unknown option: %s", *argv);
10949Sjkh                                break;
10959Sjkh                }
10969Sjkh        }
10979Sjkh
10989Sjkh        do {
109911894Speter		RCSname = workname = 0;
110011894Speter		result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag);
11019Sjkh                if (result!=0) {
110211894Speter		    diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n",
110311894Speter			     RCSname, workname, getfullRCSname()
11049Sjkh		    );
11059Sjkh                }
11069Sjkh                switch (result) {
11079Sjkh                        case 0: continue; /* already paired file */
11089Sjkh
11099Sjkh                        case 1: if (initflag) {
111011894Speter				    rcserror("already exists");
11119Sjkh                                } else {
111211894Speter				    diagnose("RCS file %s exists\n", RCSname);
11139Sjkh                                }
11149Sjkh				Ifclose(finptr);
11159Sjkh                                break;
11169Sjkh
11179Sjkh			case -1:diagnose("RCS file doesn't exist\n");
11189Sjkh                                break;
11199Sjkh                }
11209Sjkh
11219Sjkh        } while (++argv, --argc>=1);
11229Sjkh
11239Sjkh}
11249Sjkh
112511894Speter	void
11269Sjkhexiterr()
11279Sjkh{
11289Sjkh	dirtempunlink();
11299Sjkh	tempunlink();
11309Sjkh	_exit(EXIT_FAILURE);
11319Sjkh}
11329Sjkh#endif
1133