freebsd-dev/gnu/usr.bin/rcs/lib/rcsfnms.c

1133 lines
28 KiB
C
Raw Normal View History

/* RCS filename and pathname handling */
1993-06-18 04:22:21 +00:00
/****************************************************************************
* creation and deletion of /tmp temporaries
* pairing of RCS pathnames and working pathnames.
1993-06-18 04:22:21 +00:00
* Testprogram: define PAIRTEST
****************************************************************************
*/
/* Copyright 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
1993-06-18 04:22:21 +00:00
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
1993-06-18 04:22:21 +00:00
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* Revision 5.16 1995/06/16 06:19:24 eggert
* Update FSF address.
*
* Revision 5.15 1995/06/01 16:23:43 eggert
* (basefilename): Renamed from basename to avoid collisions.
* (dirlen): Remove (for similar reasons).
* (rcsreadopen): Open with FOPEN_RB.
* (SLASHSLASH_is_SLASH): Default is 0.
* (getcwd): Work around bad_wait_if_SIGCHLD_ignored bug.
*
* Revision 5.14 1994/03/17 14:05:48 eggert
* Strip trailing SLASHes from TMPDIR; some systems need this. Remove lint.
*
* Revision 5.13 1993/11/03 17:42:27 eggert
* Determine whether a file name is too long indirectly,
* by examining inode numbers, instead of trying to use operating system
* primitives like pathconf, which are not trustworthy in general.
* File names may now hold white space or $.
* Do not flatten ../X in pathnames; that may yield wrong answer for symlinks.
* Add getabsname hook. Improve quality of diagnostics.
*
* Revision 5.12 1992/07/28 16:12:44 eggert
* Add .sty. .pl now implies Perl, not Prolog. Fix fdlock initialization bug.
* Check that $PWD is really ".". Be consistent about pathnames vs filenames.
*
* Revision 5.11 1992/02/17 23:02:25 eggert
* `a/RCS/b/c' is now an RCS file with an empty extension, not just `a/b/RCS/c'.
*
* Revision 5.10 1992/01/24 18:44:19 eggert
* Fix bug: Expand and Ignored weren't reinitialized.
* Avoid `char const c=ch;' compiler bug.
* Add support for bad_creat0.
*
* Revision 5.9 1992/01/06 02:42:34 eggert
* Shorten long (>31 chars) name.
* while (E) ; -> while (E) continue;
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 5.8 1991/09/24 00:28:40 eggert
* Don't export bindex().
*
* Revision 5.7 1991/08/19 03:13:55 eggert
* Fix messages when rcswriteopen fails.
* Look in $TMP and $TEMP if $TMPDIR isn't set. Tune.
*
* Revision 5.6 1991/04/21 11:58:23 eggert
* Fix errno bugs. Add -x, RCSINIT, MS-DOS support.
*
* Revision 5.5 1991/02/26 17:48:38 eggert
* Fix setuid bug. Support new link behavior.
* Define more portable getcwd().
*
* Revision 5.4 1990/11/01 05:03:43 eggert
* Permit arbitrary data in comment leaders.
*
* Revision 5.3 1990/09/14 22:56:16 hammer
* added more filename extensions and their comment leaders
*
* Revision 5.2 1990/09/04 08:02:23 eggert
* Fix typo when !RCSSEP.
*
* Revision 5.1 1990/08/29 07:13:59 eggert
* Work around buggy compilers with defective argument promotion.
*
* Revision 5.0 1990/08/22 08:12:50 eggert
* Ignore signals when manipulating the semaphore file.
* Modernize list of filename extensions.
* Permit paths of arbitrary length. Beware filenames beginning with "-".
1993-06-18 04:22:21 +00:00
* Remove compile-time limits; use malloc instead.
* Permit dates past 1999/12/31. Make lock and temp files faster and safer.
* Ansify and Posixate.
* Don't use access(). Fix test for non-regular files. Tune.
*
* Revision 4.8 89/05/01 15:09:41 narten
* changed getwd to not stat empty directories.
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 4.7 88/08/09 19:12:53 eggert
* Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint.
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 4.6 87/12/18 11:40:23 narten
* additional file types added from 4.3 BSD version, and SPARC assembler
* comment character added. Also, more lint cleanups. (Guy Harris)
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 4.5 87/10/18 10:34:16 narten
* Updating version numbers. Changes relative to 1.1 actually relative
* to verion 4.3
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 1.3 87/03/27 14:22:21 jenkins
* Port to suns
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 1.2 85/06/26 07:34:28 svb
* Comment leader '% ' for '*.tex' files added.
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 4.3 83/12/15 12:26:48 wft
* Added check for KDELIM in filenames to pairfilenames().
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 4.2 83/12/02 22:47:45 wft
* Added csh, red, and sl filename suffixes.
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 4.1 83/05/11 16:23:39 wft
* Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
* 1. added copying of path from workfile to RCS file, if RCS file is omitted;
* 2. added getting the file status of RCS and working files;
* 3. added ignoring of directories.
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 3.7 83/05/11 15:01:58 wft
* Added comtable[] which pairs filename suffixes with comment leaders;
1993-06-18 04:22:21 +00:00
* updated InitAdmin() accordingly.
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 3.6 83/04/05 14:47:36 wft
* fixed Suffix in InitAdmin().
1995-05-30 05:05:38 +00:00
*
1993-06-18 04:22:21 +00:00
* Revision 3.5 83/01/17 18:01:04 wft
* Added getwd() and rename(); these can be removed by defining
* V4_2BSD, since they are not needed in 4.2 bsd.
* Changed sys/param.h to sys/types.h.
*
* Revision 3.4 82/12/08 21:55:20 wft
* removed unused variable.
*
* Revision 3.3 82/11/28 20:31:37 wft
* Changed mktempfile() to store the generated filenames.
1993-06-18 04:22:21 +00:00
* Changed getfullRCSname() to store the file and pathname, and to
* delete leading "../" and "./".
*
* Revision 3.2 82/11/12 14:29:40 wft
* changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
* checksuffix(), checkfullpath(). Semaphore name generation updated.
* mktempfile() now checks for nil path; freefilename initialized properly.
* Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
* Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
*
* Revision 3.1 82/10/18 14:51:28 wft
* InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
* renamed checkpath() to checkfullpath().
*/
#include "rcsbase.h"
1999-08-27 23:37:10 +00:00
libId(fnmsId, "$FreeBSD$")
static char const *bindex P((char const*,int));
static int fin2open P((char const*, size_t, char const*, size_t, char const*, size_t, RILE*(*)P((struct buf*,struct stat*,int)), int));
static int finopen P((RILE*(*)P((struct buf*,struct stat*,int)), int));
static int suffix_matches P((char const*,char const*));
static size_t dir_useful_len P((char const*));
static size_t suffixlen P((char const*));
static void InitAdmin P((void));
1993-06-18 04:22:21 +00:00
char const *RCSname;
char *workname;
int fdlock;
1993-06-18 04:22:21 +00:00
FILE *workstdout;
struct stat RCSstat;
char const *suffixes;
static char const rcsdir[] = "RCS";
#define rcslen (sizeof(rcsdir)-1)
1993-06-18 04:22:21 +00:00
static struct buf RCSbuf, RCSb;
static int RCSerrno;
/* Temp names to be unlinked when done, if they are not 0. */
1993-06-18 04:22:21 +00:00
#define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */
static char *volatile tpnames[TEMPNAMES];
1993-06-18 04:22:21 +00:00
struct compair {
char const *suffix, *comlead;
};
/*
* This table is present only for backwards compatibility.
* Normally we ignore this table, and use the prefix of the `$Log' line instead.
*/
1993-06-18 04:22:21 +00:00
static struct compair const comtable[] = {
{ "a" , "-- " }, /* Ada */
{ "ada" , "-- " },
{ "adb" , "-- " },
{ "ads" , "-- " },
{ "asm" , ";; " }, /* assembler (MS-DOS) */
{ "bat" , ":: " }, /* batch (MS-DOS) */
{ "body", "-- " }, /* Ada */
{ "c" , " * " }, /* C */
{ "c++" , "// " }, /* C++ in all its infinite guises */
{ "cc" , "// " },
{ "cpp" , "// " },
{ "cxx" , "// " },
{ "cl" , ";;; "}, /* Common Lisp */
{ "cmd" , ":: " }, /* command (OS/2) */
{ "cmf" , "c " }, /* CM Fortran */
{ "cs" , " * " }, /* C* */
{ "el" , "; " }, /* Emacs Lisp */
{ "f" , "c " }, /* Fortran */
{ "for" , "c " },
{ "h" , " * " }, /* C-header */
{ "hpp" , "// " }, /* C++ header */
{ "hxx" , "// " },
{ "l" , " * " }, /* lex (NOTE: franzlisp disagrees) */
{ "lisp", ";;; "}, /* Lucid Lisp */
{ "lsp" , ";; " }, /* Microsoft Lisp */
{ "m" , "// " }, /* Objective C */
{ "mac" , ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
{ "me" , ".\\\" "}, /* troff -me */
{ "ml" , "; " }, /* mocklisp */
{ "mm" , ".\\\" "}, /* troff -mm */
{ "ms" , ".\\\" "}, /* troff -ms */
{ "p" , " * " }, /* Pascal */
{ "pas" , " * " },
{ "ps" , "% " }, /* PostScript */
{ "spec", "-- " }, /* Ada */
{ "sty" , "% " }, /* LaTeX style */
{ "tex" , "% " }, /* TeX */
{ "y" , " * " }, /* yacc */
{ 0 , "# " } /* default for unknown suffix; must be last */
1993-06-18 04:22:21 +00:00
};
#if has_mktemp
static char const *tmp P((void));
1993-06-18 04:22:21 +00:00
static char const *
tmp()
/* Yield the name of the tmp directory. */
{
static char const *s;
if (!s
&& !(s = cgetenv("TMPDIR")) /* Unix tradition */
&& !(s = cgetenv("TMP")) /* DOS tradition */
&& !(s = cgetenv("TEMP")) /* another DOS tradition */
)
s = TMPDIR;
return s;
}
#endif
char const *
maketemp(n)
int n;
/* Create a unique pathname using n and the process id and store it
* into the nth slot in tpnames.
* Because of storage in tpnames, tempunlink() can unlink the file later.
* Return a pointer to the pathname created.
1993-06-18 04:22:21 +00:00
*/
{
char *p;
char const *t = tpnames[n];
# if has_mktemp
int fd;
# endif
1993-06-18 04:22:21 +00:00
if (t)
return t;
catchints();
{
# if has_mktemp
char const *tp = tmp();
size_t tplen = dir_useful_len(tp);
p = testalloc(tplen + 10);
VOID sprintf(p, "%.*s%cT%cXXXXXX", (int)tplen, tp, SLASH, '0'+n);
fd = mkstemp(p);
if (fd < 0 || !*p)
faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'",
(int)tplen, tp, SLASH, '0'+n
1993-06-18 04:22:21 +00:00
);
close(fd);
1993-06-18 04:22:21 +00:00
# else
static char tpnamebuf[TEMPNAMES][L_tmpnam];
p = tpnamebuf[n];
1993-06-18 04:22:21 +00:00
if (!tmpnam(p) || !*p)
# ifdef P_tmpdir
faterror("can't make temporary pathname `%s...'",P_tmpdir);
1993-06-18 04:22:21 +00:00
# else
faterror("can't make temporary pathname");
1993-06-18 04:22:21 +00:00
# endif
# endif
}
tpnames[n] = p;
1993-06-18 04:22:21 +00:00
return p;
}
void
tempunlink()
/* Clean up maketemp() files. May be invoked by signal handler.
*/
{
register int i;
register char *p;
for (i = TEMPNAMES; 0 <= --i; )
if ((p = tpnames[i])) {
1993-06-18 04:22:21 +00:00
VOID unlink(p);
/*
* We would tfree(p) here,
* but this might dump core if we're handing a signal.
* We're about to exit anyway, so we won't bother.
*/
tpnames[i] = 0;
1993-06-18 04:22:21 +00:00
}
}
static char const *
bindex(sp, c)
1993-06-18 04:22:21 +00:00
register char const *sp;
register int c;
1993-06-18 04:22:21 +00:00
/* Function: Finds the last occurrence of character c in string sp
* and returns a pointer to the character just beyond it. If the
* character doesn't occur in the string, sp is returned.
*/
{
register char const *r;
1993-06-18 04:22:21 +00:00
r = sp;
while (*sp) {
if (*sp++ == c) r=sp;
}
return r;
}
static int
suffix_matches(suffix, pattern)
register char const *suffix, *pattern;
{
register int c;
if (!pattern)
return true;
for (;;)
switch (*suffix++ - (c = *pattern++)) {
case 0:
if (!c)
return true;
break;
case 'A'-'a':
if (ctab[c] == Letter)
break;
/* fall into */
default:
return false;
}
}
static void
InitAdmin()
/* function: initializes an admin node */
{
register char const *Suffix;
register int i;
Head=0; Dbranch=0; AccessList=0; Symbols=0; Locks=0;
1993-06-18 04:22:21 +00:00
StrictLocks=STRICT_LOCKING;
/* guess the comment leader from the suffix*/
Suffix = bindex(workname, '.');
if (Suffix==workname) Suffix= ""; /* empty suffix; will get default*/
1993-06-18 04:22:21 +00:00
for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++)
continue;
1993-06-18 04:22:21 +00:00
Comment.string = comtable[i].comlead;
Comment.size = strlen(comtable[i].comlead);
Expand = KEYVAL_EXPAND;
clear_buf(&Ignored);
1993-06-18 04:22:21 +00:00
Lexinit(); /* note: if !finptr, reads nothing; only initializes */
}
void
bufalloc(b, size)
register struct buf *b;
size_t size;
/* Ensure *B is a name buffer of at least SIZE bytes.
* *B's old contents can be freed; *B's new contents are undefined.
*/
{
if (b->size < size) {
if (b->size)
tfree(b->string);
else
b->size = sizeof(malloc_type);
while (b->size < size)
b->size <<= 1;
b->string = tnalloc(char, b->size);
}
}
void
bufrealloc(b, size)
register struct buf *b;
size_t size;
/* like bufalloc, except *B's old contents, if any, are preserved */
{
if (b->size < size) {
if (!b->size)
bufalloc(b, size);
else {
while ((b->size <<= 1) < size)
continue;
1993-06-18 04:22:21 +00:00
b->string = trealloc(char, b->string, b->size);
}
}
}
void
bufautoend(b)
struct buf *b;
/* Free an auto buffer at block exit. */
{
if (b->size)
tfree(b->string);
}
struct cbuf
bufremember(b, s)
struct buf *b;
size_t s;
/*
* Free the buffer B with used size S.
* Yield a cbuf with identical contents.
* The cbuf will be reclaimed when this input file is finished.
*/
{
struct cbuf cb;
if ((cb.size = s))
cb.string = fremember(trealloc(char, b->string, s));
else {
bufautoend(b); /* not really auto */
cb.string = "";
}
return cb;
}
char *
bufenlarge(b, alim)
register struct buf *b;
char const **alim;
/* Make *B larger. Set *ALIM to its new limit, and yield the relocated value
* of its old limit.
*/
{
size_t s = b->size;
bufrealloc(b, s + 1);
*alim = b->string + b->size;
return b->string + s;
}
void
bufscat(b, s)
struct buf *b;
char const *s;
/* Concatenate S to B's end. */
{
size_t blen = b->string ? strlen(b->string) : 0;
bufrealloc(b, blen+strlen(s)+1);
VOID strcpy(b->string+blen, s);
}
void
bufscpy(b, s)
struct buf *b;
char const *s;
/* Copy S into B. */
{
bufalloc(b, strlen(s)+1);
VOID strcpy(b->string, s);
}
char const *
basefilename(p)
1993-06-18 04:22:21 +00:00
char const *p;
/* Yield the address of the base filename of the pathname P. */
{
register char const *b = p, *q = p;
for (;;)
switch (*q++) {
case SLASHes: b = q; break;
case 0: return b;
}
}
static size_t
suffixlen(x)
char const *x;
/* Yield the length of X, an RCS pathname suffix. */
1993-06-18 04:22:21 +00:00
{
register char const *p;
p = x;
for (;;)
switch (*p) {
case 0: case SLASHes:
return p - x;
default:
++p;
continue;
}
}
char const *
rcssuffix(name)
char const *name;
/* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise. */
1993-06-18 04:22:21 +00:00
{
char const *x, *p, *nz;
size_t nl, xl;
1993-06-18 04:22:21 +00:00
nl = strlen(name);
nz = name + nl;
x = suffixes;
do {
if ((xl = suffixlen(x))) {
if (xl <= nl && memcmp(p = nz-xl, x, xl) == 0)
return p;
} else
for (p = name; p < nz - rcslen; p++)
if (
isSLASH(p[rcslen])
&& (p==name || isSLASH(p[-1]))
&& memcmp(p, rcsdir, rcslen) == 0
)
return nz;
1993-06-18 04:22:21 +00:00
x += xl;
} while (*x++);
return 0;
}
/*ARGSUSED*/ RILE *
rcsreadopen(RCSpath, status, mustread)
struct buf *RCSpath;
1993-06-18 04:22:21 +00:00
struct stat *status;
int mustread;
/* Open RCSPATH for reading and yield its FILE* descriptor.
1993-06-18 04:22:21 +00:00
* If successful, set *STATUS to its status.
* Pass this routine to pairnames() for read-only access to the file. */
1993-06-18 04:22:21 +00:00
{
return Iopen(RCSpath->string, FOPEN_RB, status);
1993-06-18 04:22:21 +00:00
}
static int
finopen(rcsopen, mustread)
RILE *(*rcsopen)P((struct buf*,struct stat*,int));
int mustread;
/*
* Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
* Set finptr to the result and yield true if successful.
* RCSb holds the file's name.
* Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
* Yield true if successful or if an unusual failure.
*/
{
int interesting, preferold;
/*
* We prefer an old name to that of a nonexisting new RCS file,
* unless we tried locking the old name and failed.
*/
preferold = RCSbuf.string[0] && (mustread||0<=fdlock);
1993-06-18 04:22:21 +00:00
finptr = (*rcsopen)(&RCSb, &RCSstat, mustread);
interesting = finptr || errno!=ENOENT;
if (interesting || !preferold) {
/* Use the new name. */
RCSerrno = errno;
bufscpy(&RCSbuf, RCSb.string);
}
return interesting;
}
static int
fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread)
char const *d, *base, *x;
size_t dlen, baselen, xlen;
RILE *(*rcsopen)P((struct buf*,struct stat*,int));
int mustread;
/*
* D is a directory name with length DLEN (including trailing slash).
* BASE is a filename with length BASELEN.
* X is an RCS pathname suffix with length XLEN.
1993-06-18 04:22:21 +00:00
* Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
* Yield true if successful.
* Try dRCS/basex first; if that fails and x is nonempty, try dbasex.
* Put these potential names in RCSb.
* Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
* Yield true if successful or if an unusual failure.
*/
{
register char *p;
bufalloc(&RCSb, dlen + rcslen + 1 + baselen + xlen + 1);
1993-06-18 04:22:21 +00:00
/* Try dRCS/basex. */
VOID memcpy(p = RCSb.string, d, dlen);
VOID memcpy(p += dlen, rcsdir, rcslen);
p += rcslen;
1993-06-18 04:22:21 +00:00
*p++ = SLASH;
VOID memcpy(p, base, baselen);
VOID memcpy(p += baselen, x, xlen);
p[xlen] = 0;
if (xlen) {
if (finopen(rcsopen, mustread))
return true;
/* Try dbasex. */
/* Start from scratch, because finopen() may have changed RCSb. */
VOID memcpy(p = RCSb.string, d, dlen);
VOID memcpy(p += dlen, base, baselen);
VOID memcpy(p += baselen, x, xlen);
p[xlen] = 0;
}
return finopen(rcsopen, mustread);
}
int
pairnames(argc, argv, rcsopen, mustread, quiet)
1993-06-18 04:22:21 +00:00
int argc;
char **argv;
RILE *(*rcsopen)P((struct buf*,struct stat*,int));
int mustread, quiet;
/*
* Pair the pathnames pointed to by argv; argc indicates
1993-06-18 04:22:21 +00:00
* how many there are.
* Place a pointer to the RCS pathname into RCSname,
* and a pointer to the pathname of the working file into workname.
* If both are given, and workstdout
1993-06-18 04:22:21 +00:00
* is set, a warning is printed.
*
* If the RCS file exists, places its status into RCSstat.
*
* If the RCS file exists, it is RCSOPENed for reading, the file pointer
* is placed into finptr, and the admin-node is read in; returns 1.
* If the RCS file does not exist and MUSTREAD,
* print an error unless QUIET and return 0.
* Otherwise, initialize the admin node and return -1.
*
* 0 is returned on all errors, e.g. files that are not regular files.
*/
{
static struct buf tempbuf;
register char *p, *arg, *RCS1;
char const *base, *RCSbase, *x;
1993-06-18 04:22:21 +00:00
int paired;
size_t arglen, dlen, baselen, xlen;
fdlock = -1;
if (!(arg = *argv)) return 0; /* already paired pathname */
1993-06-18 04:22:21 +00:00
if (*arg == '-') {
error("%s option is ignored after pathnames", arg);
1993-06-18 04:22:21 +00:00
return 0;
}
base = basefilename(arg);
1993-06-18 04:22:21 +00:00
paired = false;
/* first check suffix to see whether it is an RCS file or not */
if ((x = rcssuffix(arg)))
{
/* RCS pathname given */
1993-06-18 04:22:21 +00:00
RCS1 = arg;
RCSbase = base;
baselen = x - base;
1993-06-18 04:22:21 +00:00
if (
1 < argc &&
!rcssuffix(workname = p = argv[1]) &&
1993-06-18 04:22:21 +00:00
baselen <= (arglen = strlen(p)) &&
((p+=arglen-baselen) == workname || isSLASH(p[-1])) &&
memcmp(base, p, baselen) == 0
1993-06-18 04:22:21 +00:00
) {
argv[1] = 0;
paired = true;
} else {
bufscpy(&tempbuf, base);
workname = p = tempbuf.string;
1993-06-18 04:22:21 +00:00
p[baselen] = 0;
}
} else {
/* working file given; now try to find RCS file */
workname = arg;
baselen = strlen(base);
/* Derive RCS pathname. */
1993-06-18 04:22:21 +00:00
if (
1 < argc &&
(x = rcssuffix(RCS1 = argv[1])) &&
baselen <= x - RCS1 &&
((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) &&
memcmp(base, RCSbase, baselen) == 0
1993-06-18 04:22:21 +00:00
) {
argv[1] = 0;
paired = true;
} else
RCSbase = RCS1 = 0;
1993-06-18 04:22:21 +00:00
}
/* Now we have a (tentative) RCS pathname in RCS1 and workname. */
1993-06-18 04:22:21 +00:00
/* Second, try to find the right RCS file */
if (RCSbase!=RCS1) {
1993-06-18 04:22:21 +00:00
/* a path for RCSfile is given; single RCS file to look for */
bufscpy(&RCSbuf, RCS1);
finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread);
RCSerrno = errno;
} else {
bufscpy(&RCSbuf, "");
if (RCS1)
/* RCS filename was given without path. */
VOID fin2open(arg, (size_t)0, RCSbase, baselen,
1993-06-18 04:22:21 +00:00
x, strlen(x), rcsopen, mustread
);
else {
/* No RCS pathname was given. */
1993-06-18 04:22:21 +00:00
/* Try each suffix in turn. */
dlen = base-arg;
1993-06-18 04:22:21 +00:00
x = suffixes;
while (! fin2open(arg, dlen, base, baselen,
1993-06-18 04:22:21 +00:00
x, xlen=suffixlen(x), rcsopen, mustread
)) {
x += xlen;
if (!*x++)
break;
}
}
}
RCSname = p = RCSbuf.string;
1993-06-18 04:22:21 +00:00
if (finptr) {
if (!S_ISREG(RCSstat.st_mode)) {
error("%s isn't a regular file -- ignored", p);
return 0;
}
Lexinit(); getadmin();
} else {
if (RCSerrno!=ENOENT || mustread || fdlock<0) {
1993-06-18 04:22:21 +00:00
if (RCSerrno == EEXIST)
error("RCS file %s is in use", p);
else if (!quiet || RCSerrno!=ENOENT)
enerror(RCSerrno, p);
return 0;
}
InitAdmin();
};
if (paired && workstdout)
workwarn("Working file ignored due to -p option");
1993-06-18 04:22:21 +00:00
prevkeys = false;
return finptr ? 1 : -1;
}
char const *
getfullRCSname()
/*
* Return a pointer to the full pathname of the RCS file.
* Remove leading `./'.
1993-06-18 04:22:21 +00:00
*/
{
if (ROOTPATH(RCSname)) {
return RCSname;
} else {
static struct buf rcsbuf;
# if needs_getabsname
bufalloc(&rcsbuf, SIZEABLE_PATH + 1);
while (getabsname(RCSname, rcsbuf.string, rcsbuf.size) != 0)
if (errno == ERANGE)
bufalloc(&rcsbuf, rcsbuf.size<<1);
else
efaterror("getabsname");
# else
static char const *wdptr;
static struct buf wdbuf;
static size_t wdlen;
register char const *r;
register size_t dlen;
register char *d;
register char const *wd;
1993-06-18 04:22:21 +00:00
if (!(wd = wdptr)) {
/* Get working directory for the first time. */
char *PWD = cgetenv("PWD");
struct stat PWDstat, dotstat;
if (! (
(d = PWD) &&
ROOTPATH(PWD) &&
stat(PWD, &PWDstat) == 0 &&
stat(".", &dotstat) == 0 &&
same_file(PWDstat, dotstat, 1)
)) {
1993-06-18 04:22:21 +00:00
bufalloc(&wdbuf, SIZEABLE_PATH + 1);
# if has_getcwd || !has_getwd
while (!(d = getcwd(wdbuf.string, wdbuf.size)))
if (errno == ERANGE)
bufalloc(&wdbuf, wdbuf.size<<1);
else if ((d = PWD))
break;
else
efaterror("getcwd");
1993-06-18 04:22:21 +00:00
# else
d = getwd(wdbuf.string);
if (!d && !(d = PWD))
efaterror("getwd");
1993-06-18 04:22:21 +00:00
# endif
}
wdlen = dir_useful_len(d);
d[wdlen] = 0;
1993-06-18 04:22:21 +00:00
wdptr = wd = d;
}
/*
* Remove leading `./'s from RCSname.
* Do not try to handle `../', since removing it may yield
* the wrong answer in the presence of symbolic links.
*/
for (r = RCSname; r[0]=='.' && isSLASH(r[1]); r += 2)
/* `.////' is equivalent to `./'. */
while (isSLASH(r[2]))
r++;
/* Build full pathname. */
dlen = wdlen;
bufalloc(&rcsbuf, dlen + strlen(r) + 2);
1993-06-18 04:22:21 +00:00
d = rcsbuf.string;
VOID memcpy(d, wd, dlen);
d += dlen;
1993-06-18 04:22:21 +00:00
*d++ = SLASH;
VOID strcpy(d, r);
# endif
return rcsbuf.string;
1993-06-18 04:22:21 +00:00
}
}
/* Derived from code from the XFree86 project */
char const *
getfullCVSname()
/* Function: returns a pointer to the path name of the RCS file with the
* CVSROOT part stripped off, and with 'Attic/' stripped off (if present).
*/
{
#define ATTICDIR "/Attic"
char const *namebuf = getfullRCSname();
char *cvsroot = cgetenv("CVSROOT");
int cvsrootlen;
char *c = NULL;
int alen = strlen(ATTICDIR);
if ((c = strrchr(namebuf, '/')) != NULL) {
if (namebuf - c >= alen) {
if (!strncmp(c - alen, ATTICDIR, alen)) {
while(*c != '\0') {
*(c - alen) = *c;
c++;
}
*(c - alen) = '\0';
}
}
}
if (!cvsroot)
return(namebuf);
else
{
cvsrootlen = strlen(cvsroot);
if (!strncmp(namebuf, cvsroot, cvsrootlen) &&
namebuf[cvsrootlen] == '/')
return(namebuf + cvsrootlen + 1);
else
return(namebuf);
}
}
static size_t
dir_useful_len(d)
char const *d;
/*
* D names a directory; yield the number of characters of D's useful part.
* To create a file in D, append a SLASH and a file name to D's useful part.
* Ignore trailing slashes if possible; not only are they ugly,
* but some non-Posix systems misbehave unless the slashes are omitted.
*/
{
# ifndef SLASHSLASH_is_SLASH
# define SLASHSLASH_is_SLASH 0
# endif
size_t dlen = strlen(d);
if (!SLASHSLASH_is_SLASH && dlen==2 && isSLASH(d[0]) && isSLASH(d[1]))
--dlen;
else
while (dlen && isSLASH(d[dlen-1]))
--dlen;
return dlen;
}
1993-06-18 04:22:21 +00:00
#ifndef isSLASH
int
isSLASH(c)
int c;
{
switch (c) {
case SLASHes:
return true;
default:
return false;
}
}
#endif
#if !has_getcwd && !has_getwd
char *
getcwd(path, size)
char *path;
size_t size;
{
static char const usrbinpwd[] = "/usr/bin/pwd";
# define binpwd (usrbinpwd+4)
register FILE *fp;
register int c;
register char *p, *lim;
int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus;
pid_t child;
if (!size) {
errno = EINVAL;
return 0;
}
if (pipe(fd) != 0)
return 0;
# if bad_wait_if_SIGCHLD_ignored
# ifndef SIGCHLD
# define SIGCHLD SIGCLD
# endif
VOID signal(SIGCHLD, SIG_DFL);
# endif
1993-06-18 04:22:21 +00:00
if (!(child = vfork())) {
if (
close(fd[0]) == 0 &&
(fd[1] == STDOUT_FILENO ||
# ifdef F_DUPFD
(VOID close(STDOUT_FILENO),
fcntl(fd[1], F_DUPFD, STDOUT_FILENO))
# else
dup2(fd[1], STDOUT_FILENO)
# endif
== STDOUT_FILENO &&
close(fd[1]) == 0
)
) {
VOID close(STDERR_FILENO);
VOID execl(binpwd, binpwd, (char *)0);
VOID execl(usrbinpwd, usrbinpwd, (char *)0);
}
_exit(EXIT_FAILURE);
}
e = errno;
closeerror = close(fd[1]);
closeerrno = errno;
fp = 0;
readerror = toolong = wstatus = 0;
p = path;
if (0 <= child) {
fp = fdopen(fd[0], "r");
e = errno;
if (fp) {
lim = p + size;
for (p = path; ; *p++ = c) {
if ((c=getc(fp)) < 0) {
if (feof(fp))
break;
if (ferror(fp)) {
readerror = 1;
e = errno;
break;
}
}
if (p == lim) {
toolong = 1;
break;
}
}
}
# if has_waitpid
if (waitpid(child, &wstatus, 0) < 0)
wstatus = 1;
# else
{
pid_t w;
do {
if ((w = wait(&wstatus)) < 0) {
wstatus = 1;
break;
}
} while (w != child);
}
1993-06-18 04:22:21 +00:00
# endif
}
if (!fp) {
VOID close(fd[0]);
errno = e;
return 0;
}
if (fclose(fp) != 0)
return 0;
if (readerror) {
errno = e;
return 0;
}
if (closeerror) {
errno = closeerrno;
return 0;
}
if (toolong) {
errno = ERANGE;
return 0;
}
if (wstatus || p == path || *--p != '\n') {
errno = EACCES;
return 0;
}
*p = '\0';
return path;
}
#endif
#ifdef PAIRTEST
/* test program for pairnames() and getfullRCSname() */
1993-06-18 04:22:21 +00:00
char const cmdid[] = "pair";
main(argc, argv)
int argc; char *argv[];
{
int result;
int initflag;
quietflag = initflag = false;
while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
switch ((*argv)[1]) {
case 'p': workstdout = stdout;
break;
case 'i': initflag=true;
break;
case 'q': quietflag=true;
break;
default: error("unknown option: %s", *argv);
break;
}
}
do {
RCSname = workname = 0;
result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag);
1993-06-18 04:22:21 +00:00
if (result!=0) {
diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n",
RCSname, workname, getfullRCSname()
1993-06-18 04:22:21 +00:00
);
}
switch (result) {
case 0: continue; /* already paired file */
case 1: if (initflag) {
rcserror("already exists");
1993-06-18 04:22:21 +00:00
} else {
diagnose("RCS file %s exists\n", RCSname);
1993-06-18 04:22:21 +00:00
}
Ifclose(finptr);
break;
case -1:diagnose("RCS file doesn't exist\n");
break;
}
} while (++argv, --argc>=1);
}
void
1993-06-18 04:22:21 +00:00
exiterr()
{
dirtempunlink();
tempunlink();
_exit(EXIT_FAILURE);
}
#endif