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