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