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