xref: /csrg-svn/contrib/rcs-V5.6/src/rcsfnms.c (revision 60446)
160442Sbostic /*
260442Sbostic  *                     RCS file name handling
360442Sbostic  */
460442Sbostic /****************************************************************************
560442Sbostic  *                     creation and deletion of /tmp temporaries
660442Sbostic  *                     pairing of RCS file names and working file names.
760442Sbostic  *                     Testprogram: define PAIRTEST
860442Sbostic  ****************************************************************************
960442Sbostic  */
1060442Sbostic 
1160442Sbostic /* Copyright (C) 1982, 1988, 1989 Walter Tichy
1260442Sbostic    Copyright 1990, 1991 by Paul Eggert
1360442Sbostic    Distributed under license by the Free Software Foundation, Inc.
1460442Sbostic 
1560442Sbostic This file is part of RCS.
1660442Sbostic 
1760442Sbostic RCS is free software; you can redistribute it and/or modify
1860442Sbostic it under the terms of the GNU General Public License as published by
1960442Sbostic the Free Software Foundation; either version 2, or (at your option)
2060442Sbostic any later version.
2160442Sbostic 
2260442Sbostic RCS is distributed in the hope that it will be useful,
2360442Sbostic but WITHOUT ANY WARRANTY; without even the implied warranty of
2460442Sbostic MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2560442Sbostic GNU General Public License for more details.
2660442Sbostic 
2760442Sbostic You should have received a copy of the GNU General Public License
2860442Sbostic along with RCS; see the file COPYING.  If not, write to
2960442Sbostic the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
3060442Sbostic 
3160442Sbostic Report problems and direct all questions to:
3260442Sbostic 
3360442Sbostic     rcs-bugs@cs.purdue.edu
3460442Sbostic 
3560442Sbostic */
3660442Sbostic 
3760442Sbostic 
3860442Sbostic 
3960442Sbostic 
4060442Sbostic /* $Log: rcsfnms.c,v $
4160442Sbostic  * Revision 5.8  1991/09/24  00:28:40  eggert
4260442Sbostic  * Don't export bindex().
4360442Sbostic  *
4460442Sbostic  * Revision 5.7  1991/08/19  03:13:55  eggert
4560442Sbostic  * Fix messages when rcswriteopen fails.
4660442Sbostic  * Look in $TMP and $TEMP if $TMPDIR isn't set.  Tune.
4760442Sbostic  *
4860442Sbostic  * Revision 5.6  1991/04/21  11:58:23  eggert
4960442Sbostic  * Fix errno bugs.  Add -x, RCSINIT, MS-DOS support.
5060442Sbostic  *
5160442Sbostic  * Revision 5.5  1991/02/26  17:48:38  eggert
5260442Sbostic  * Fix setuid bug.  Support new link behavior.
5360442Sbostic  * Define more portable getcwd().
5460442Sbostic  *
5560442Sbostic  * Revision 5.4  1990/11/01  05:03:43  eggert
5660442Sbostic  * Permit arbitrary data in comment leaders.
5760442Sbostic  *
5860442Sbostic  * Revision 5.3  1990/09/14  22:56:16  hammer
5960442Sbostic  * added more filename extensions and their comment leaders
6060442Sbostic  *
6160442Sbostic  * Revision 5.2  1990/09/04  08:02:23  eggert
6260442Sbostic  * Fix typo when !RCSSEP.
6360442Sbostic  *
6460442Sbostic  * Revision 5.1  1990/08/29  07:13:59  eggert
6560442Sbostic  * Work around buggy compilers with defective argument promotion.
6660442Sbostic  *
6760442Sbostic  * Revision 5.0  1990/08/22  08:12:50  eggert
6860442Sbostic  * Ignore signals when manipulating the semaphore file.
6960442Sbostic  * Modernize list of file name extensions.
7060442Sbostic  * Permit paths of arbitrary length.  Beware file names beginning with "-".
7160442Sbostic  * Remove compile-time limits; use malloc instead.
7260442Sbostic  * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
7360442Sbostic  * Ansify and Posixate.
7460442Sbostic  * Don't use access().  Fix test for non-regular files.  Tune.
7560442Sbostic  *
7660442Sbostic  * Revision 4.8  89/05/01  15:09:41  narten
7760442Sbostic  * changed getwd to not stat empty directories.
7860442Sbostic  *
7960442Sbostic  * Revision 4.7  88/08/09  19:12:53  eggert
8060442Sbostic  * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint.
8160442Sbostic  *
8260442Sbostic  * Revision 4.6  87/12/18  11:40:23  narten
8360442Sbostic  * additional file types added from 4.3 BSD version, and SPARC assembler
8460442Sbostic  * comment character added. Also, more lint cleanups. (Guy Harris)
8560442Sbostic  *
8660442Sbostic  * Revision 4.5  87/10/18  10:34:16  narten
8760442Sbostic  * Updating version numbers. Changes relative to 1.1 actually relative
8860442Sbostic  * to verion 4.3
8960442Sbostic  *
9060442Sbostic  * Revision 1.3  87/03/27  14:22:21  jenkins
9160442Sbostic  * Port to suns
9260442Sbostic  *
9360442Sbostic  * Revision 1.2  85/06/26  07:34:28  svb
9460442Sbostic  * Comment leader '% ' for '*.tex' files added.
9560442Sbostic  *
9660442Sbostic  * Revision 4.3  83/12/15  12:26:48  wft
9760442Sbostic  * Added check for KDELIM in file names to pairfilenames().
9860442Sbostic  *
9960442Sbostic  * Revision 4.2  83/12/02  22:47:45  wft
10060442Sbostic  * Added csh, red, and sl file name suffixes.
10160442Sbostic  *
10260442Sbostic  * Revision 4.1  83/05/11  16:23:39  wft
10360442Sbostic  * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
10460442Sbostic  * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
10560442Sbostic  * 2. added getting the file status of RCS and working files;
10660442Sbostic  * 3. added ignoring of directories.
10760442Sbostic  *
10860442Sbostic  * Revision 3.7  83/05/11  15:01:58  wft
10960442Sbostic  * Added comtable[] which pairs file name suffixes with comment leaders;
11060442Sbostic  * updated InitAdmin() accordingly.
11160442Sbostic  *
11260442Sbostic  * Revision 3.6  83/04/05  14:47:36  wft
11360442Sbostic  * fixed Suffix in InitAdmin().
11460442Sbostic  *
11560442Sbostic  * Revision 3.5  83/01/17  18:01:04  wft
11660442Sbostic  * Added getwd() and rename(); these can be removed by defining
11760442Sbostic  * V4_2BSD, since they are not needed in 4.2 bsd.
11860442Sbostic  * Changed sys/param.h to sys/types.h.
11960442Sbostic  *
12060442Sbostic  * Revision 3.4  82/12/08  21:55:20  wft
12160442Sbostic  * removed unused variable.
12260442Sbostic  *
12360442Sbostic  * Revision 3.3  82/11/28  20:31:37  wft
12460442Sbostic  * Changed mktempfile() to store the generated file names.
12560442Sbostic  * Changed getfullRCSname() to store the file and pathname, and to
12660442Sbostic  * delete leading "../" and "./".
12760442Sbostic  *
12860442Sbostic  * Revision 3.2  82/11/12  14:29:40  wft
12960442Sbostic  * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
13060442Sbostic  * checksuffix(), checkfullpath(). Semaphore name generation updated.
13160442Sbostic  * mktempfile() now checks for nil path; freefilename initialized properly.
13260442Sbostic  * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
13360442Sbostic  * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
13460442Sbostic  *
13560442Sbostic  * Revision 3.1  82/10/18  14:51:28  wft
13660442Sbostic  * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
13760442Sbostic  * renamed checkpath() to checkfullpath().
13860442Sbostic  */
13960442Sbostic 
14060442Sbostic 
14160442Sbostic #include "rcsbase.h"
14260442Sbostic 
14360442Sbostic libId(fnmsId, "$Id: rcsfnms.c,v 5.8 1991/09/24 00:28:40 eggert Exp $")
14460442Sbostic 
14560442Sbostic char const *RCSfilename;
14660442Sbostic char *workfilename;
14760442Sbostic FILE *workstdout;
14860442Sbostic struct stat RCSstat;
14960442Sbostic char const *suffixes;
15060442Sbostic 
15160442Sbostic static char const rcsdir[] = "RCS";
15260442Sbostic #define rcsdirlen (sizeof(rcsdir)-1)
15360442Sbostic 
15460442Sbostic static struct buf RCSbuf, RCSb;
15560442Sbostic static int RCSerrno;
15660442Sbostic 
15760442Sbostic 
15860442Sbostic /* Temp file names to be unlinked when done, if they are not nil.  */
15960442Sbostic #define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */
16060442Sbostic static char *volatile tfnames[TEMPNAMES];
16160442Sbostic 
16260442Sbostic 
16360442Sbostic struct compair {
16460442Sbostic 	char const *suffix, *comlead;
16560442Sbostic };
16660442Sbostic 
16760442Sbostic static struct compair const comtable[] = {
16860442Sbostic /* comtable pairs each filename suffix with a comment leader. The comment   */
16960442Sbostic /* leader is placed before each line generated by the $Log keyword. This    */
17060442Sbostic /* table is used to guess the proper comment leader from the working file's */
17160442Sbostic /* suffix during initial ci (see InitAdmin()). Comment leaders are needed   */
17260442Sbostic /* for languages without multiline comments; for others they are optional.  */
17360442Sbostic 	"a",   "-- ",   /* Ada         */
17460442Sbostic 	"ada", "-- ",
17560442Sbostic 	"asm", ";; ",	/* assembler (MS-DOS) */
17660442Sbostic 	"bat", ":: ",	/* batch (MS-DOS) */
17760442Sbostic         "c",   " * ",   /* C           */
17860442Sbostic 	"c++", "// ",	/* C++ in all its infinite guises */
17960442Sbostic 	"cc",  "// ",
18060442Sbostic 	"cpp", "// ",
18160442Sbostic 	"cxx", "// ",
18260442Sbostic 	"cl",  ";;; ",  /* Common Lisp */
18360442Sbostic 	"cmd", ":: ",	/* command (OS/2) */
18460442Sbostic 	"cmf", "c ",	/* CM Fortran  */
18560442Sbostic 	"cs",  " * ",	/* C*          */
18660442Sbostic 	"el",  "; ",    /* Emacs Lisp  */
18760442Sbostic 	"f",   "c ",    /* Fortran     */
18860442Sbostic 	"for", "c ",
18960442Sbostic         "h",   " * ",   /* C-header    */
19060442Sbostic 	"hpp", "// ",	/* C++ header  */
19160442Sbostic 	"hxx", "// ",
19260442Sbostic         "l",   " * ",   /* lex      NOTE: conflict between lex and franzlisp */
19360442Sbostic 	"lisp",";;; ",	/* Lucid Lisp  */
19460442Sbostic 	"lsp", ";; ",	/* Microsoft Lisp */
19560442Sbostic 	"mac", ";; ",	/* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
19660442Sbostic 	"me",  ".\\\" ",/* me-macros   t/nroff*/
19760442Sbostic 	"ml",  "; ",    /* mocklisp    */
19860442Sbostic 	"mm",  ".\\\" ",/* mm-macros   t/nroff*/
19960442Sbostic 	"ms",  ".\\\" ",/* ms-macros   t/nroff*/
20060442Sbostic 	"p",   " * ",   /* Pascal      */
20160442Sbostic 	"pas", " * ",
20260442Sbostic 	"pl",  "% ",	/* Prolog      */
20360442Sbostic 	"tex", "% ",	/* TeX	       */
20460442Sbostic         "y",   " * ",   /* yacc        */
20560442Sbostic 	nil,   "# "     /* default for unknown suffix; must always be last */
20660442Sbostic };
20760442Sbostic 
20860442Sbostic #if has_mktemp
20960442Sbostic 	static char const *
tmp()21060442Sbostic tmp()
21160442Sbostic /* Yield the name of the tmp directory.  */
21260442Sbostic {
21360442Sbostic 	static char const *s;
21460442Sbostic 	if (!s
21560442Sbostic 		&&  !(s = cgetenv("TMPDIR"))	/* Unix tradition */
21660442Sbostic 		&&  !(s = cgetenv("TMP"))	/* DOS tradition */
21760442Sbostic 		&&  !(s = cgetenv("TEMP"))	/* another DOS tradition */
21860442Sbostic 	)
21960442Sbostic 		s = TMPDIR;
22060442Sbostic 	return s;
22160442Sbostic }
22260442Sbostic #endif
22360442Sbostic 
22460442Sbostic 	char const *
maketemp(n)22560442Sbostic maketemp(n)
22660442Sbostic 	int n;
22760442Sbostic /* Create a unique filename using n and the process id and store it
22860442Sbostic  * into the nth slot in tfnames.
22960442Sbostic  * Because of storage in tfnames, tempunlink() can unlink the file later.
23060442Sbostic  * Returns a pointer to the filename created.
23160442Sbostic  */
23260442Sbostic {
23360442Sbostic 	char *p;
23460442Sbostic 	char const *t = tfnames[n];
23560442Sbostic 
23660442Sbostic 	if (t)
23760442Sbostic 		return t;
23860442Sbostic 
23960442Sbostic 	catchints();
24060442Sbostic 	{
24160442Sbostic #	if has_mktemp
24260442Sbostic 	    char const *tp = tmp();
24360442Sbostic 	    p = testalloc(strlen(tp) + 10);
24460442Sbostic 	    VOID sprintf(p, "%s%cT%cXXXXXX", tp, SLASH, '0'+n);
24560442Sbostic 	    if (!mktemp(p) || !*p)
24660442Sbostic 		faterror("can't make temporary file name `%s%cT%cXXXXXX'",
24760442Sbostic 			tp, SLASH, '0'+n
24860442Sbostic 		);
24960442Sbostic #	else
25060442Sbostic 	    static char tfnamebuf[TEMPNAMES][L_tmpnam];
25160442Sbostic 	    p = tfnamebuf[n];
25260442Sbostic 	    if (!tmpnam(p) || !*p)
25360442Sbostic #		ifdef P_tmpdir
25460442Sbostic 		    faterror("can't make temporary file name `%s...'",P_tmpdir);
25560442Sbostic #		else
25660442Sbostic 		    faterror("can't make temporary file name");
25760442Sbostic #		endif
25860442Sbostic #	endif
25960442Sbostic 	}
26060442Sbostic 
26160442Sbostic 	tfnames[n] = p;
26260442Sbostic 	return p;
26360442Sbostic }
26460442Sbostic 
26560442Sbostic 	void
tempunlink()26660442Sbostic tempunlink()
26760442Sbostic /* Clean up maketemp() files.  May be invoked by signal handler.
26860442Sbostic  */
26960442Sbostic {
27060442Sbostic 	register int i;
27160442Sbostic 	register char *p;
27260442Sbostic 
27360442Sbostic 	for (i = TEMPNAMES;  0 <= --i;  )
27460442Sbostic 	    if ((p = tfnames[i])) {
27560442Sbostic 		VOID unlink(p);
27660442Sbostic 		/*
27760442Sbostic 		 * We would tfree(p) here,
27860442Sbostic 		 * but this might dump core if we're handing a signal.
27960442Sbostic 		 * We're about to exit anyway, so we won't bother.
28060442Sbostic 		 */
28160442Sbostic 		tfnames[i] = 0;
28260442Sbostic 	    }
28360442Sbostic }
28460442Sbostic 
28560442Sbostic 
28660442Sbostic 	static char const *
bindex(sp,ch)28760442Sbostic bindex(sp,ch)
28860442Sbostic 	register char const *sp;
28960442Sbostic 	int ch;
29060442Sbostic /* Function: Finds the last occurrence of character c in string sp
29160442Sbostic  * and returns a pointer to the character just beyond it. If the
29260442Sbostic  * character doesn't occur in the string, sp is returned.
29360442Sbostic  */
29460442Sbostic {
29560442Sbostic 	register char const c=ch, *r;
29660442Sbostic         r = sp;
29760442Sbostic         while (*sp) {
29860442Sbostic                 if (*sp++ == c) r=sp;
29960442Sbostic         }
30060442Sbostic         return r;
30160442Sbostic }
30260442Sbostic 
30360442Sbostic 
30460442Sbostic 
30560442Sbostic 	static int
suffix_matches(suffix,pattern)30660442Sbostic suffix_matches(suffix, pattern)
30760442Sbostic 	register char const *suffix, *pattern;
30860442Sbostic {
30960442Sbostic 	register int c;
31060442Sbostic 	if (!pattern)
31160442Sbostic 		return true;
31260442Sbostic 	for (;;)
31360442Sbostic 		switch (*suffix++ - (c = *pattern++)) {
31460442Sbostic 		    case 0:
31560442Sbostic 			if (!c)
31660442Sbostic 				return true;
31760442Sbostic 			break;
31860442Sbostic 
31960442Sbostic 		    case 'A'-'a':
32060442Sbostic 			if (ctab[c] == Letter)
32160442Sbostic 				break;
32260442Sbostic 			/* fall into */
32360442Sbostic 		    default:
32460442Sbostic 			return false;
32560442Sbostic 		}
32660442Sbostic }
32760442Sbostic 
32860442Sbostic 
32960442Sbostic 	static void
InitAdmin()33060442Sbostic InitAdmin()
33160442Sbostic /* function: initializes an admin node */
33260442Sbostic {
33360442Sbostic 	register char const *Suffix;
33460442Sbostic         register int i;
33560442Sbostic 
33660442Sbostic 	Head=nil; Dbranch=nil; AccessList=nil; Symbols=nil; Locks=nil;
33760442Sbostic         StrictLocks=STRICT_LOCKING;
33860442Sbostic 
33960442Sbostic         /* guess the comment leader from the suffix*/
34060442Sbostic         Suffix=bindex(workfilename, '.');
34160442Sbostic         if (Suffix==workfilename) Suffix= ""; /* empty suffix; will get default*/
34260442Sbostic 	for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++)
34360442Sbostic 		;
34460442Sbostic 	Comment.string = comtable[i].comlead;
34560442Sbostic 	Comment.size = strlen(comtable[i].comlead);
34660442Sbostic 	Lexinit(); /* note: if !finptr, reads nothing; only initializes */
34760442Sbostic }
34860442Sbostic 
34960442Sbostic 
350*60446Sbostic #if defined(_POSIX_NO_TRUNC)
35160442Sbostic #	define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 0
35260442Sbostic #else
35360442Sbostic #	define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 1
35460442Sbostic #endif
35560442Sbostic 
35660442Sbostic #if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED
35760442Sbostic #ifdef NAME_MAX
35860442Sbostic #	define filenametoolong(path) (NAME_MAX < strlen(basename(path)))
35960442Sbostic #else
36060442Sbostic 	static int
filenametoolong(path)36160442Sbostic filenametoolong(path)
36260442Sbostic 	char *path;
36360442Sbostic /* Yield true if the last file name in PATH is too long. */
36460442Sbostic {
36560442Sbostic 	static unsigned long dot_namemax;
36660442Sbostic 
36760442Sbostic 	register size_t namelen;
36860442Sbostic 	register char *base;
36960442Sbostic 	register unsigned long namemax;
37060442Sbostic 
37160442Sbostic 	base = path + dirlen(path);
37260442Sbostic 	namelen = strlen(base);
37360442Sbostic 	if (namelen <= _POSIX_NAME_MAX) /* fast check for shorties */
37460442Sbostic 		return false;
37560442Sbostic 	if (base != path) {
37660442Sbostic 		*--base = 0;
37760442Sbostic 		namemax = pathconf(path, _PC_NAME_MAX);
37860442Sbostic 		*base = SLASH;
37960442Sbostic 	} else {
38060442Sbostic 		/* Cache the results for the working directory, for speed. */
38160442Sbostic 		if (!dot_namemax)
38260442Sbostic 			dot_namemax = pathconf(".", _PC_NAME_MAX);
38360442Sbostic 		namemax = dot_namemax;
38460442Sbostic 	}
38560442Sbostic 	/* If pathconf() yielded -1, namemax is now ULONG_MAX.  */
38660442Sbostic 	return namemax<namelen;
38760442Sbostic }
38860442Sbostic #endif
38960442Sbostic #endif
39060442Sbostic 
39160442Sbostic 	void
bufalloc(b,size)39260442Sbostic bufalloc(b, size)
39360442Sbostic 	register struct buf *b;
39460442Sbostic 	size_t size;
39560442Sbostic /* Ensure *B is a name buffer of at least SIZE bytes.
39660442Sbostic  * *B's old contents can be freed; *B's new contents are undefined.
39760442Sbostic  */
39860442Sbostic {
39960442Sbostic 	if (b->size < size) {
40060442Sbostic 		if (b->size)
40160442Sbostic 			tfree(b->string);
40260442Sbostic 		else
40360442Sbostic 			b->size = sizeof(malloc_type);
40460442Sbostic 		while (b->size < size)
40560442Sbostic 			b->size <<= 1;
40660442Sbostic 		b->string = tnalloc(char, b->size);
40760442Sbostic 	}
40860442Sbostic }
40960442Sbostic 
41060442Sbostic 	void
bufrealloc(b,size)41160442Sbostic bufrealloc(b, size)
41260442Sbostic 	register struct buf *b;
41360442Sbostic 	size_t size;
41460442Sbostic /* like bufalloc, except *B's old contents, if any, are preserved */
41560442Sbostic {
41660442Sbostic 	if (b->size < size) {
41760442Sbostic 		if (!b->size)
41860442Sbostic 			bufalloc(b, size);
41960442Sbostic 		else {
42060442Sbostic 			while ((b->size <<= 1)  <  size)
42160442Sbostic 				;
42260442Sbostic 			b->string = trealloc(char, b->string, b->size);
42360442Sbostic 		}
42460442Sbostic 	}
42560442Sbostic }
42660442Sbostic 
42760442Sbostic 	void
bufautoend(b)42860442Sbostic bufautoend(b)
42960442Sbostic 	struct buf *b;
43060442Sbostic /* Free an auto buffer at block exit. */
43160442Sbostic {
43260442Sbostic 	if (b->size)
43360442Sbostic 		tfree(b->string);
43460442Sbostic }
43560442Sbostic 
43660442Sbostic 	struct cbuf
bufremember(b,s)43760442Sbostic bufremember(b, s)
43860442Sbostic 	struct buf *b;
43960442Sbostic 	size_t s;
44060442Sbostic /*
44160442Sbostic  * Free the buffer B with used size S.
44260442Sbostic  * Yield a cbuf with identical contents.
44360442Sbostic  * The cbuf will be reclaimed when this input file is finished.
44460442Sbostic  */
44560442Sbostic {
44660442Sbostic 	struct cbuf cb;
44760442Sbostic 
44860442Sbostic 	if ((cb.size = s))
44960442Sbostic 		cb.string = fremember(trealloc(char, b->string, s));
45060442Sbostic 	else {
45160442Sbostic 		bufautoend(b); /* not really auto */
45260442Sbostic 		cb.string = "";
45360442Sbostic 	}
45460442Sbostic 	return cb;
45560442Sbostic }
45660442Sbostic 
45760442Sbostic 	char *
bufenlarge(b,alim)45860442Sbostic bufenlarge(b, alim)
45960442Sbostic 	register struct buf *b;
46060442Sbostic 	char const **alim;
46160442Sbostic /* Make *B larger.  Set *ALIM to its new limit, and yield the relocated value
46260442Sbostic  * of its old limit.
46360442Sbostic  */
46460442Sbostic {
46560442Sbostic 	size_t s = b->size;
46660442Sbostic 	bufrealloc(b, s + 1);
46760442Sbostic 	*alim = b->string + b->size;
46860442Sbostic 	return b->string + s;
46960442Sbostic }
47060442Sbostic 
47160442Sbostic 	void
bufscat(b,s)47260442Sbostic bufscat(b, s)
47360442Sbostic 	struct buf *b;
47460442Sbostic 	char const *s;
47560442Sbostic /* Concatenate S to B's end. */
47660442Sbostic {
47760442Sbostic 	size_t blen  =  b->string ? strlen(b->string) : 0;
47860442Sbostic 	bufrealloc(b, blen+strlen(s)+1);
47960442Sbostic 	VOID strcpy(b->string+blen, s);
48060442Sbostic }
48160442Sbostic 
48260442Sbostic 	void
bufscpy(b,s)48360442Sbostic bufscpy(b, s)
48460442Sbostic 	struct buf *b;
48560442Sbostic 	char const *s;
48660442Sbostic /* Copy S into B. */
48760442Sbostic {
48860442Sbostic 	bufalloc(b, strlen(s)+1);
48960442Sbostic 	VOID strcpy(b->string, s);
49060442Sbostic }
49160442Sbostic 
49260442Sbostic 
49360442Sbostic 	char const *
basename(p)49460442Sbostic basename(p)
49560442Sbostic 	char const *p;
49660442Sbostic /* Yield the address of the base filename of the pathname P.  */
49760442Sbostic {
49860442Sbostic 	register char const *b = p, *q = p;
49960442Sbostic 	for (;;)
50060442Sbostic 	    switch (*q++) {
50160442Sbostic 		case SLASHes: b = q; break;
50260442Sbostic 		case 0: return b;
50360442Sbostic 	    }
50460442Sbostic }
50560442Sbostic 
50660442Sbostic 	size_t
dirlen(p)50760442Sbostic dirlen(p)
50860442Sbostic 	char const *p;
50960442Sbostic /* Yield the length of P's directory, including its trailing SLASH.  */
51060442Sbostic {
51160442Sbostic 	return basename(p) - p;
51260442Sbostic }
51360442Sbostic 
51460442Sbostic 
51560442Sbostic 	static size_t
suffixlen(x)51660442Sbostic suffixlen(x)
51760442Sbostic 	char const *x;
51860442Sbostic /* Yield the length of X, an RCS filename suffix.  */
51960442Sbostic {
52060442Sbostic 	register char const *p;
52160442Sbostic 
52260442Sbostic 	p = x;
52360442Sbostic 	for (;;)
52460442Sbostic 	    switch (*p) {
52560442Sbostic 		case 0: case SLASHes:
52660442Sbostic 		    return p - x;
52760442Sbostic 
52860442Sbostic 		default:
52960442Sbostic 		    ++p;
53060442Sbostic 		    continue;
53160442Sbostic 	    }
53260442Sbostic }
53360442Sbostic 
53460442Sbostic 	char const *
rcssuffix(name)53560442Sbostic rcssuffix(name)
53660442Sbostic 	char const *name;
53760442Sbostic /* Yield the suffix of NAME if it is an RCS filename, 0 otherwise.  */
53860442Sbostic {
53960442Sbostic 	char const *x, *p, *nz;
54060442Sbostic 	size_t dl, nl, xl;
54160442Sbostic 
54260442Sbostic 	nl = strlen(name);
54360442Sbostic 	nz = name + nl;
54460442Sbostic 	x = suffixes;
54560442Sbostic 	do {
54660442Sbostic 	    if ((xl = suffixlen(x))) {
54760442Sbostic 		if (xl <= nl  &&  memcmp(p = nz-xl, x, xl) == 0)
54860442Sbostic 		    return p;
54960442Sbostic 	    } else {
55060442Sbostic 		dl = dirlen(name);
55160442Sbostic 		if (
55260442Sbostic 		    rcsdirlen < dl  &&
55360442Sbostic 		    !memcmp(p = name+(dl-=rcsdirlen+1), rcsdir, rcsdirlen) &&
55460442Sbostic 		    (!dl  ||  isSLASH(*--p))
55560442Sbostic 		)
55660442Sbostic 		    return nz;
55760442Sbostic 	    }
55860442Sbostic 	    x += xl;
55960442Sbostic 	} while (*x++);
56060442Sbostic 	return 0;
56160442Sbostic }
56260442Sbostic 
56360442Sbostic 	/*ARGSUSED*/ RILE *
rcsreadopen(RCSname,status,mustread)56460442Sbostic rcsreadopen(RCSname, status, mustread)
56560442Sbostic 	struct buf *RCSname;
56660442Sbostic 	struct stat *status;
56760442Sbostic 	int mustread;
56860442Sbostic /* Open RCSNAME for reading and yield its FILE* descriptor.
56960442Sbostic  * If successful, set *STATUS to its status.
57060442Sbostic  * Pass this routine to pairfilenames() for read-only access to the file.  */
57160442Sbostic {
57260442Sbostic 	return Iopen(RCSname->string, FOPEN_R, status);
57360442Sbostic }
57460442Sbostic 
57560442Sbostic 	static int
57660442Sbostic finopen(rcsopen, mustread)
57760442Sbostic 	/* jlf RILE *(*rcsopen)P((struct buf*,struct stat*,int)); */
57860442Sbostic 	RILE *(*rcsopen)();
57960442Sbostic 	int mustread;
58060442Sbostic /*
58160442Sbostic  * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
58260442Sbostic  * Set finptr to the result and yield true if successful.
58360442Sbostic  * RCSb holds the file's name.
58460442Sbostic  * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
58560442Sbostic  * Yield true if successful or if an unusual failure.
58660442Sbostic  */
58760442Sbostic {
58860442Sbostic 	int interesting, preferold;
58960442Sbostic 
59060442Sbostic 	/*
59160442Sbostic 	 * We prefer an old name to that of a nonexisting new RCS file,
59260442Sbostic 	 * unless we tried locking the old name and failed.
59360442Sbostic 	 */
59460442Sbostic 	preferold  =  RCSbuf.string[0] && (mustread||frewrite);
59560442Sbostic 
59660442Sbostic 	finptr = (*rcsopen)(&RCSb, &RCSstat, mustread);
59760442Sbostic 	interesting = finptr || errno!=ENOENT;
59860442Sbostic 	if (interesting || !preferold) {
59960442Sbostic 		/* Use the new name.  */
60060442Sbostic 		RCSerrno = errno;
60160442Sbostic 		bufscpy(&RCSbuf, RCSb.string);
60260442Sbostic 	}
60360442Sbostic 	return interesting;
60460442Sbostic }
60560442Sbostic 
60660442Sbostic 	static int
fin2open(d,dlen,base,baselen,x,xlen,rcsopen,mustread)60760442Sbostic fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread)
60860442Sbostic 	char const *d, *base, *x;
60960442Sbostic 	size_t dlen, baselen, xlen;
61060442Sbostic 	/* jlf RILE *(*rcsopen)P((struct buf*,struct stat*,int)); */
61160442Sbostic 	RILE *(*rcsopen)();
61260442Sbostic 	int mustread;
61360442Sbostic /*
61460442Sbostic  * D is a directory name with length DLEN (including trailing slash).
61560442Sbostic  * BASE is a filename with length BASELEN.
61660442Sbostic  * X is an RCS filename suffix with length XLEN.
61760442Sbostic  * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
61860442Sbostic  * Yield true if successful.
61960442Sbostic  * Try dRCS/basex first; if that fails and x is nonempty, try dbasex.
62060442Sbostic  * Put these potential names in RCSb.
62160442Sbostic  * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
62260442Sbostic  * Yield true if successful or if an unusual failure.
62360442Sbostic  */
62460442Sbostic {
62560442Sbostic 	register char *p;
62660442Sbostic 
62760442Sbostic 	bufalloc(&RCSb, dlen + rcsdirlen + 1 + baselen + xlen + 1);
62860442Sbostic 
62960442Sbostic 	/* Try dRCS/basex.  */
63060442Sbostic 	VOID memcpy(p = RCSb.string, d, dlen);
63160442Sbostic 	VOID memcpy(p += dlen, rcsdir, rcsdirlen);
63260442Sbostic 	p += rcsdirlen;
63360442Sbostic 	*p++ = SLASH;
63460442Sbostic 	VOID memcpy(p, base, baselen);
63560442Sbostic 	VOID memcpy(p += baselen, x, xlen);
63660442Sbostic 	p[xlen] = 0;
63760442Sbostic 	if (xlen) {
63860442Sbostic 	    if (finopen(rcsopen, mustread))
63960442Sbostic 		return true;
64060442Sbostic 
64160442Sbostic 	    /* Try dbasex.  */
64260442Sbostic 	    /* Start from scratch, because finopen() may have changed RCSb.  */
64360442Sbostic 	    VOID memcpy(p = RCSb.string, d, dlen);
64460442Sbostic 	    VOID memcpy(p += dlen, base, baselen);
64560442Sbostic 	    VOID memcpy(p += baselen, x, xlen);
64660442Sbostic 	    p[xlen] = 0;
64760442Sbostic 	}
64860442Sbostic 	return finopen(rcsopen, mustread);
64960442Sbostic }
65060442Sbostic 
65160442Sbostic 	int
pairfilenames(argc,argv,rcsopen,mustread,quiet)65260442Sbostic pairfilenames(argc, argv, rcsopen, mustread, quiet)
65360442Sbostic 	int argc;
65460442Sbostic 	char **argv;
65560442Sbostic 	/* jlf RILE *(*rcsopen)P((struct buf*,struct stat*,int)); */
65660442Sbostic 	RILE *(*rcsopen)();
65760442Sbostic 	int mustread, quiet;
65860442Sbostic /* Function: Pairs the filenames pointed to by argv; argc indicates
65960442Sbostic  * how many there are.
66060442Sbostic  * Places a pointer to the RCS filename into RCSfilename,
66160442Sbostic  * and a pointer to the name of the working file into workfilename.
66260442Sbostic  * If both the workfilename and the RCS filename are given, and workstdout
66360442Sbostic  * is set, a warning is printed.
66460442Sbostic  *
66560442Sbostic  * If the RCS file exists, places its status into RCSstat.
66660442Sbostic  *
66760442Sbostic  * If the RCS file exists, it is RCSOPENed for reading, the file pointer
66860442Sbostic  * is placed into finptr, and the admin-node is read in; returns 1.
66960442Sbostic  * If the RCS file does not exist and MUSTREAD,
67060442Sbostic  * print an error unless QUIET and return 0.
67160442Sbostic  * Otherwise, initialize the admin node and return -1.
67260442Sbostic  *
67360442Sbostic  * 0 is returned on all errors, e.g. files that are not regular files.
67460442Sbostic  */
67560442Sbostic {
67660442Sbostic 	static struct buf tempbuf;
67760442Sbostic 
67860442Sbostic 	register char *p, *arg, *RCS1;
67960442Sbostic 	char const *purefname, *pureRCSname, *x;
68060442Sbostic 	int paired;
68160442Sbostic 	size_t arglen, dlen, baselen, xlen;
68260442Sbostic 
68360442Sbostic 	if (!(arg = *argv)) return 0; /* already paired filename */
68460442Sbostic 	if (*arg == '-') {
68560442Sbostic 		error("%s option is ignored after file names", arg);
68660442Sbostic 		return 0;
68760442Sbostic 	}
68860442Sbostic 
68960442Sbostic 	purefname = basename(arg);
69060442Sbostic 
69160442Sbostic 	/* Allocate buffer temporary to hold the default paired file name. */
69260442Sbostic 	p = arg;
69360442Sbostic 	for (;;) {
69460442Sbostic 		switch (*p++) {
69560442Sbostic 		    /* Beware characters that cause havoc with ci -k. */
69660442Sbostic 		    case KDELIM:
69760442Sbostic 			error("RCS file name `%s' contains %c", arg, KDELIM);
69860442Sbostic 			return 0;
69960442Sbostic 		    case ' ': case '\n': case '\t':
70060442Sbostic 			error("RCS file name `%s' contains white space", arg);
70160442Sbostic 			return 0;
70260442Sbostic 		    default:
70360442Sbostic 			continue;
70460442Sbostic 		    case 0:
70560442Sbostic 			break;
70660442Sbostic 		}
70760442Sbostic 		break;
70860442Sbostic 	}
70960442Sbostic 
71060442Sbostic 	paired = false;
71160442Sbostic 
71260442Sbostic         /* first check suffix to see whether it is an RCS file or not */
71360442Sbostic 	if ((x = rcssuffix(arg)))
71460442Sbostic 	{
71560442Sbostic                 /* RCS file name given*/
71660442Sbostic 		RCS1 = arg;
71760442Sbostic 		pureRCSname = purefname;
71860442Sbostic 		baselen = x - purefname;
71960442Sbostic 		if (
72060442Sbostic 		    1 < argc  &&
72160442Sbostic 		    !rcssuffix(workfilename = p = argv[1])  &&
72260442Sbostic 		    baselen <= (arglen = strlen(p))  &&
72360442Sbostic 		    ((p+=arglen-baselen) == workfilename  ||  isSLASH(p[-1])) &&
72460442Sbostic 		    memcmp(purefname, p, baselen) == 0
72560442Sbostic 		) {
72660442Sbostic 			argv[1] = 0;
72760442Sbostic 			paired = true;
72860442Sbostic 		} else {
72960442Sbostic 			bufscpy(&tempbuf, purefname);
73060442Sbostic 			workfilename = p = tempbuf.string;
73160442Sbostic 			p[baselen] = 0;
73260442Sbostic 		}
73360442Sbostic         } else {
73460442Sbostic                 /* working file given; now try to find RCS file */
73560442Sbostic 		workfilename = arg;
73660442Sbostic 		baselen = p - purefname - 1;
73760442Sbostic                 /* derive RCS file name*/
73860442Sbostic 		if (
73960442Sbostic 		    1 < argc  &&
74060442Sbostic 		    (x = rcssuffix(RCS1 = argv[1]))  &&
74160442Sbostic 		    baselen  <=  x - RCS1  &&
74260442Sbostic 		    ((pureRCSname=x-baselen)==RCS1 || isSLASH(pureRCSname[-1])) &&
74360442Sbostic 		    memcmp(purefname, pureRCSname, baselen) == 0
74460442Sbostic 		) {
74560442Sbostic 			argv[1] = 0;
74660442Sbostic 			paired = true;
74760442Sbostic 		} else
74860442Sbostic 			pureRCSname = RCS1 = 0;
74960442Sbostic         }
75060442Sbostic         /* now we have a (tentative) RCS filename in RCS1 and workfilename  */
75160442Sbostic         /* Second, try to find the right RCS file */
75260442Sbostic         if (pureRCSname!=RCS1) {
75360442Sbostic                 /* a path for RCSfile is given; single RCS file to look for */
75460442Sbostic 		bufscpy(&RCSbuf, RCS1);
75560442Sbostic 		finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread);
75660442Sbostic 		RCSerrno = errno;
75760442Sbostic         } else {
75860442Sbostic 		bufscpy(&RCSbuf, "");
75960442Sbostic 		if (RCS1)
76060442Sbostic 			/* RCS file name was given without path.  */
76160442Sbostic 			VOID fin2open(arg, (size_t)0, pureRCSname, baselen,
76260442Sbostic 				x, strlen(x), rcsopen, mustread
76360442Sbostic 			);
76460442Sbostic 		else {
76560442Sbostic 			/* No RCS file name was given.  */
76660442Sbostic 			/* Try each suffix in turn.  */
76760442Sbostic 			dlen = purefname-arg;
76860442Sbostic 			x = suffixes;
76960442Sbostic 			while (! fin2open(arg, dlen, purefname, baselen,
77060442Sbostic 					x, xlen=suffixlen(x), rcsopen, mustread
77160442Sbostic 			)) {
77260442Sbostic 				x += xlen;
77360442Sbostic 				if (!*x++)
77460442Sbostic 					break;
77560442Sbostic 			}
77660442Sbostic 		}
77760442Sbostic         }
77860442Sbostic 	RCSfilename = p = RCSbuf.string;
77960442Sbostic 	if (finptr) {
78060442Sbostic 		if (!S_ISREG(RCSstat.st_mode)) {
78160442Sbostic 			error("%s isn't a regular file -- ignored", p);
78260442Sbostic                         return 0;
78360442Sbostic                 }
78460442Sbostic                 Lexinit(); getadmin();
78560442Sbostic 	} else {
78660442Sbostic 		if (RCSerrno!=ENOENT || mustread || !frewrite) {
78760442Sbostic 			if (RCSerrno == EEXIST)
78860442Sbostic 				error("RCS file %s is in use", p);
78960442Sbostic 			else if (!quiet || RCSerrno!=ENOENT)
79060442Sbostic 				enerror(RCSerrno, p);
79160442Sbostic 			return 0;
79260442Sbostic 		}
79360442Sbostic                 InitAdmin();
79460442Sbostic         };
79560442Sbostic #	if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED
79660442Sbostic 	    if (filenametoolong(p)) {
79760442Sbostic 		error("RCS file name %s is too long", p);
79860442Sbostic 		return 0;
79960442Sbostic 	    }
80060442Sbostic #	    ifndef NAME_MAX
80160442Sbostic 		/*
80260442Sbostic 		 * Check workfilename too, even though it cannot be longer,
80360442Sbostic 		 * because it may reside on a different filesystem.
80460442Sbostic 		 */
80560442Sbostic 		if (filenametoolong(workfilename)) {
80660442Sbostic 		    error("working file name %s is too long", workfilename);
80760442Sbostic 		    return 0;
80860442Sbostic 		}
80960442Sbostic #	    endif
81060442Sbostic #	endif
81160442Sbostic 
81260442Sbostic 	if (paired && workstdout)
81360442Sbostic                 warn("Option -p is set; ignoring output file %s",workfilename);
81460442Sbostic 
81560442Sbostic 	prevkeys = false;
81660442Sbostic 	return finptr ? 1 : -1;
81760442Sbostic }
81860442Sbostic 
81960442Sbostic 
82060442Sbostic 	char const *
getfullRCSname()82160442Sbostic getfullRCSname()
82260442Sbostic /* Function: returns a pointer to the full path name of the RCS file.
82360442Sbostic  * Gets the working directory's name at most once.
82460442Sbostic  * Removes leading "../" and "./".
82560442Sbostic  */
82660442Sbostic {
82760442Sbostic 	static char const *wdptr;
82860442Sbostic 	static struct buf rcsbuf, wdbuf;
82960442Sbostic 	static size_t pathlength;
83060442Sbostic 
83160442Sbostic 	register char const *realname;
83260442Sbostic 	register size_t parentdirlength;
83360442Sbostic 	register unsigned dotdotcounter;
83460442Sbostic 	register char *d;
83560442Sbostic 	register char const *wd;
83660442Sbostic 
83760442Sbostic 	if (ROOTPATH(RCSfilename)) {
83860442Sbostic                 return(RCSfilename);
83960442Sbostic         } else {
84060442Sbostic 		if (!(wd = wdptr)) {
84160442Sbostic 		    /* Get working directory for the first time.  */
84260442Sbostic 		    if (!(d = cgetenv("PWD"))) {
84360442Sbostic 			bufalloc(&wdbuf, SIZEABLE_PATH + 1);
84460442Sbostic #			if !has_getcwd && has_getwd
84560442Sbostic 			    d = getwd(wdbuf.string);
84660442Sbostic #			else
84760442Sbostic 			    while (
84860442Sbostic 				    !(d = getcwd(wdbuf.string, wdbuf.size))
84960442Sbostic 				&&  errno==ERANGE
85060442Sbostic 			    )
85160442Sbostic 				bufalloc(&wdbuf, wdbuf.size<<1);
85260442Sbostic #			endif
85360442Sbostic 			if (!d)
85460442Sbostic 			    efaterror("working directory");
85560442Sbostic 		    }
85660442Sbostic 		    parentdirlength = strlen(d);
85760442Sbostic 		    while (parentdirlength && isSLASH(d[parentdirlength-1])) {
85860442Sbostic 			d[--parentdirlength] = 0;
85960442Sbostic                         /* Check needed because some getwd implementations */
86060442Sbostic                         /* generate "/" for the root.                      */
86160442Sbostic                     }
86260442Sbostic 		    wdptr = wd = d;
86360442Sbostic 		    pathlength = parentdirlength;
86460442Sbostic                 }
86560442Sbostic                 /*the following must be redone since RCSfilename may change*/
86660442Sbostic 		/* Find how many `../'s to remove from RCSfilename.  */
86760442Sbostic                 dotdotcounter =0;
86860442Sbostic                 realname = RCSfilename;
86960442Sbostic 		while (realname[0]=='.') {
87060442Sbostic 			if (isSLASH(realname[1])) {
87160442Sbostic                             /* drop leading ./ */
87260442Sbostic                             realname += 2;
87360442Sbostic 			} else if (realname[1]=='.' && isSLASH(realname[2])) {
87460442Sbostic                             /* drop leading ../ and remember */
87560442Sbostic                             dotdotcounter++;
87660442Sbostic                             realname += 3;
87760442Sbostic 			} else
87860442Sbostic 			    break;
87960442Sbostic                 }
88060442Sbostic 		/* Now remove dotdotcounter trailing directories from wd. */
88160442Sbostic 		parentdirlength = pathlength;
88260442Sbostic 		while (dotdotcounter && parentdirlength) {
88360442Sbostic                     /* move pointer backwards over trailing directory */
88460442Sbostic 		    if (isSLASH(wd[--parentdirlength])) {
88560442Sbostic                         dotdotcounter--;
88660442Sbostic                     }
88760442Sbostic                 }
88860442Sbostic 		/* build full path name */
88960442Sbostic 		bufalloc(&rcsbuf, parentdirlength+strlen(realname)+2);
89060442Sbostic 		d = rcsbuf.string;
89160442Sbostic 		VOID memcpy(d, wd, parentdirlength);
89260442Sbostic 		d += parentdirlength;
89360442Sbostic 		*d++ = SLASH;
89460442Sbostic 		VOID strcpy(d, realname);
89560442Sbostic 		return rcsbuf.string;
89660442Sbostic         }
89760442Sbostic }
89860442Sbostic 
89960442Sbostic #ifndef isSLASH
90060442Sbostic 	int
isSLASH(c)90160442Sbostic isSLASH(c)
90260442Sbostic 	int c;
90360442Sbostic {
90460442Sbostic 	switch (c) {
90560442Sbostic 	    case SLASHes:
90660442Sbostic 		return true;
90760442Sbostic 	    default:
90860442Sbostic 		return false;
90960442Sbostic 	}
91060442Sbostic }
91160442Sbostic #endif
91260442Sbostic 
91360442Sbostic 
91460442Sbostic #if !has_getcwd && !has_getwd
91560442Sbostic 
91660442Sbostic 	char *
getcwd(path,size)91760442Sbostic getcwd(path, size)
91860442Sbostic 	char *path;
91960442Sbostic 	size_t size;
92060442Sbostic {
92160442Sbostic 	static char const usrbinpwd[] = "/usr/bin/pwd";
92260442Sbostic #	define binpwd (usrbinpwd+4)
92360442Sbostic 
92460442Sbostic 	register FILE *fp;
92560442Sbostic 	register int c;
92660442Sbostic 	register char *p, *lim;
92760442Sbostic 	int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus;
92860442Sbostic 	pid_t child;
92960442Sbostic #	if !has_waitpid
93060442Sbostic 		pid_t w;
93160442Sbostic #	endif
93260442Sbostic 
93360442Sbostic 	if (!size) {
93460442Sbostic 		errno = EINVAL;
93560442Sbostic 		return 0;
93660442Sbostic 	}
93760442Sbostic 	if (pipe(fd) != 0)
93860442Sbostic 		return 0;
93960442Sbostic 	if (!(child = vfork())) {
94060442Sbostic 		if (
94160442Sbostic 			close(fd[0]) == 0 &&
94260442Sbostic 			(fd[1] == STDOUT_FILENO ||
94360442Sbostic #				ifdef F_DUPFD
94460442Sbostic 					(VOID close(STDOUT_FILENO),
94560442Sbostic 					fcntl(fd[1], F_DUPFD, STDOUT_FILENO))
94660442Sbostic #				else
94760442Sbostic 					dup2(fd[1], STDOUT_FILENO)
94860442Sbostic #				endif
94960442Sbostic 				== STDOUT_FILENO &&
95060442Sbostic 				close(fd[1]) == 0
95160442Sbostic 			)
95260442Sbostic 		) {
95360442Sbostic 			VOID close(STDERR_FILENO);
95460442Sbostic 			VOID execl(binpwd, binpwd, (char *)0);
95560442Sbostic 			VOID execl(usrbinpwd, usrbinpwd, (char *)0);
95660442Sbostic 		}
95760442Sbostic 		_exit(EXIT_FAILURE);
95860442Sbostic 	}
95960442Sbostic 	e = errno;
96060442Sbostic 	closeerror = close(fd[1]);
96160442Sbostic 	closeerrno = errno;
96260442Sbostic 	fp = 0;
96360442Sbostic 	readerror = toolong = wstatus = 0;
96460442Sbostic 	p = path;
96560442Sbostic 	if (0 <= child) {
96660442Sbostic 		fp = fdopen(fd[0], "r");
96760442Sbostic 		e = errno;
96860442Sbostic 		if (fp) {
96960442Sbostic 			lim = p + size;
97060442Sbostic 			for (p = path;  ;  *p++ = c) {
97160442Sbostic 				if ((c=getc(fp)) < 0) {
97260442Sbostic 					if (feof(fp))
97360442Sbostic 						break;
97460442Sbostic 					if (ferror(fp)) {
97560442Sbostic 						readerror = 1;
97660442Sbostic 						e = errno;
97760442Sbostic 						break;
97860442Sbostic 					}
97960442Sbostic 				}
98060442Sbostic 				if (p == lim) {
98160442Sbostic 					toolong = 1;
98260442Sbostic 					break;
98360442Sbostic 				}
98460442Sbostic 			}
98560442Sbostic 		}
98660442Sbostic #		if has_waitpid
98760442Sbostic 			if (waitpid(child, &wstatus, 0) < 0)
98860442Sbostic 				wstatus = 1;
98960442Sbostic #		else
99060442Sbostic 			do {
99160442Sbostic 				if ((w = wait(&wstatus)) < 0) {
99260442Sbostic 					wstatus = 1;
99360442Sbostic 					break;
99460442Sbostic 				}
99560442Sbostic 			} while (w != child);
99660442Sbostic #		endif
99760442Sbostic 	}
99860442Sbostic 	if (!fp) {
99960442Sbostic 		VOID close(fd[0]);
100060442Sbostic 		errno = e;
100160442Sbostic 		return 0;
100260442Sbostic 	}
100360442Sbostic 	if (fclose(fp) != 0)
100460442Sbostic 		return 0;
100560442Sbostic 	if (readerror) {
100660442Sbostic 		errno = e;
100760442Sbostic 		return 0;
100860442Sbostic 	}
100960442Sbostic 	if (closeerror) {
101060442Sbostic 		errno = closeerrno;
101160442Sbostic 		return 0;
101260442Sbostic 	}
101360442Sbostic 	if (toolong) {
101460442Sbostic 		errno = ERANGE;
101560442Sbostic 		return 0;
101660442Sbostic 	}
101760442Sbostic 	if (wstatus  ||  p == path  ||  *--p != '\n') {
101860442Sbostic 		errno = EACCES;
101960442Sbostic 		return 0;
102060442Sbostic 	}
102160442Sbostic 	*p = '\0';
102260442Sbostic 	return path;
102360442Sbostic }
102460442Sbostic #endif
102560442Sbostic 
102660442Sbostic 
102760442Sbostic #ifdef PAIRTEST
102860442Sbostic /* test program for pairfilenames() and getfullRCSname() */
102960442Sbostic 
103060442Sbostic char const cmdid[] = "pair";
103160442Sbostic 
main(argc,argv)103260442Sbostic main(argc, argv)
103360442Sbostic int argc; char *argv[];
103460442Sbostic {
103560442Sbostic         int result;
103660442Sbostic 	int initflag;
103760442Sbostic 	quietflag = initflag = false;
103860442Sbostic 
103960442Sbostic         while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
104060442Sbostic                 switch ((*argv)[1]) {
104160442Sbostic 
104260442Sbostic 		case 'p':       workstdout = stdout;
104360442Sbostic                                 break;
104460442Sbostic                 case 'i':       initflag=true;
104560442Sbostic                                 break;
104660442Sbostic                 case 'q':       quietflag=true;
104760442Sbostic                                 break;
104860442Sbostic                 default:        error("unknown option: %s", *argv);
104960442Sbostic                                 break;
105060442Sbostic                 }
105160442Sbostic         }
105260442Sbostic 
105360442Sbostic         do {
105460442Sbostic                 RCSfilename=workfilename=nil;
105560442Sbostic 		result = pairfilenames(argc,argv,rcsreadopen,!initflag,quietflag);
105660442Sbostic                 if (result!=0) {
105760442Sbostic 		    diagnose("RCS file: %s; working file: %s\nFull RCS file name: %s\n",
105860442Sbostic 			     RCSfilename,workfilename,getfullRCSname()
105960442Sbostic 		    );
106060442Sbostic                 }
106160442Sbostic                 switch (result) {
106260442Sbostic                         case 0: continue; /* already paired file */
106360442Sbostic 
106460442Sbostic                         case 1: if (initflag) {
106560442Sbostic                                     error("RCS file %s exists already",RCSfilename);
106660442Sbostic                                 } else {
106760442Sbostic 				    diagnose("RCS file %s exists\n",RCSfilename);
106860442Sbostic                                 }
106960442Sbostic 				Ifclose(finptr);
107060442Sbostic                                 break;
107160442Sbostic 
107260442Sbostic 			case -1:diagnose("RCS file doesn't exist\n");
107360442Sbostic                                 break;
107460442Sbostic                 }
107560442Sbostic 
107660442Sbostic         } while (++argv, --argc>=1);
107760442Sbostic 
107860442Sbostic }
107960442Sbostic 
108060442Sbostic 	exiting void
exiterr()108160442Sbostic exiterr()
108260442Sbostic {
108360442Sbostic 	dirtempunlink();
108460442Sbostic 	tempunlink();
108560442Sbostic 	_exit(EXIT_FAILURE);
108660442Sbostic }
108760442Sbostic #endif
1088