diff options
Diffstat (limited to 'gnu/usr.bin/rcs/lib/rcsfnms.c')
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsfnms.c | 1132 |
1 files changed, 1132 insertions, 0 deletions
diff --git a/gnu/usr.bin/rcs/lib/rcsfnms.c b/gnu/usr.bin/rcs/lib/rcsfnms.c new file mode 100644 index 0000000..00caec5 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsfnms.c @@ -0,0 +1,1132 @@ +/* RCS filename and pathname handling */ + +/**************************************************************************** + * creation and deletion of /tmp temporaries + * pairing of RCS pathnames and working pathnames. + * Testprogram: define PAIRTEST + **************************************************************************** + */ + +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert + 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. + +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; + * + * 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 "-". + * 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. + * + * Revision 4.7 88/08/09 19:12:53 eggert + * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint. + * + * 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) + * + * Revision 4.5 87/10/18 10:34:16 narten + * Updating version numbers. Changes relative to 1.1 actually relative + * to verion 4.3 + * + * Revision 1.3 87/03/27 14:22:21 jenkins + * Port to suns + * + * Revision 1.2 85/06/26 07:34:28 svb + * Comment leader '% ' for '*.tex' files added. + * + * Revision 4.3 83/12/15 12:26:48 wft + * Added check for KDELIM in filenames to pairfilenames(). + * + * Revision 4.2 83/12/02 22:47:45 wft + * Added csh, red, and sl filename suffixes. + * + * 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. + * + * Revision 3.7 83/05/11 15:01:58 wft + * Added comtable[] which pairs filename suffixes with comment leaders; + * updated InitAdmin() accordingly. + * + * Revision 3.6 83/04/05 14:47:36 wft + * fixed Suffix in InitAdmin(). + * + * 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. + * 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" + +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)); + +char const *RCSname; +char *workname; +int fdlock; +FILE *workstdout; +struct stat RCSstat; +char const *suffixes; + +static char const rcsdir[] = "RCS"; +#define rcslen (sizeof(rcsdir)-1) + +static struct buf RCSbuf, RCSb; +static int RCSerrno; + + +/* Temp names to be unlinked when done, if they are not 0. */ +#define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */ +static char *volatile tpnames[TEMPNAMES]; + + +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. +*/ +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 */ +}; + +#if has_mktemp + static char const *tmp P((void)); + 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. + */ +{ + char *p; + char const *t = tpnames[n]; +# if has_mktemp + int fd; +# endif + + 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 + ); + close(fd); +# else + static char tpnamebuf[TEMPNAMES][L_tmpnam]; + p = tpnamebuf[n]; + if (!tmpnam(p) || !*p) +# ifdef P_tmpdir + faterror("can't make temporary pathname `%s...'",P_tmpdir); +# else + faterror("can't make temporary pathname"); +# endif +# endif + } + + tpnames[n] = p; + 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])) { + 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; + } +} + + + static char const * +bindex(sp, c) + register char const *sp; + register int c; +/* 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; + 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; + StrictLocks=STRICT_LOCKING; + + /* guess the comment leader from the suffix*/ + Suffix = bindex(workname, '.'); + if (Suffix==workname) Suffix= ""; /* empty suffix; will get default*/ + for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++) + continue; + Comment.string = comtable[i].comlead; + Comment.size = strlen(comtable[i].comlead); + Expand = KEYVAL_EXPAND; + clear_buf(&Ignored); + 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; + 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) + 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. */ +{ + 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. */ +{ + char const *x, *p, *nz; + size_t nl, xl; + + 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; + x += xl; + } while (*x++); + return 0; +} + + /*ARGSUSED*/ RILE * +rcsreadopen(RCSpath, status, mustread) + struct buf *RCSpath; + struct stat *status; + int mustread; +/* Open RCSPATH for reading and yield its FILE* descriptor. + * If successful, set *STATUS to its status. + * Pass this routine to pairnames() for read-only access to the file. */ +{ + return Iopen(RCSpath->string, FOPEN_RB, status); +} + + 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); + + 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. + * 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); + + /* Try dRCS/basex. */ + VOID memcpy(p = RCSb.string, d, dlen); + VOID memcpy(p += dlen, rcsdir, rcslen); + p += rcslen; + *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) + int argc; + char **argv; + RILE *(*rcsopen)P((struct buf*,struct stat*,int)); + int mustread, quiet; +/* + * Pair the pathnames pointed to by argv; argc indicates + * 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 + * 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; + int paired; + size_t arglen, dlen, baselen, xlen; + + fdlock = -1; + + if (!(arg = *argv)) return 0; /* already paired pathname */ + if (*arg == '-') { + error("%s option is ignored after pathnames", arg); + return 0; + } + + base = basefilename(arg); + paired = false; + + /* first check suffix to see whether it is an RCS file or not */ + if ((x = rcssuffix(arg))) + { + /* RCS pathname given */ + RCS1 = arg; + RCSbase = base; + baselen = x - base; + if ( + 1 < argc && + !rcssuffix(workname = p = argv[1]) && + baselen <= (arglen = strlen(p)) && + ((p+=arglen-baselen) == workname || isSLASH(p[-1])) && + memcmp(base, p, baselen) == 0 + ) { + argv[1] = 0; + paired = true; + } else { + bufscpy(&tempbuf, base); + workname = p = tempbuf.string; + p[baselen] = 0; + } + } else { + /* working file given; now try to find RCS file */ + workname = arg; + baselen = strlen(base); + /* Derive RCS pathname. */ + if ( + 1 < argc && + (x = rcssuffix(RCS1 = argv[1])) && + baselen <= x - RCS1 && + ((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) && + memcmp(base, RCSbase, baselen) == 0 + ) { + argv[1] = 0; + paired = true; + } else + RCSbase = RCS1 = 0; + } + /* Now we have a (tentative) RCS pathname in RCS1 and workname. */ + /* Second, try to find the right RCS file */ + if (RCSbase!=RCS1) { + /* 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, + x, strlen(x), rcsopen, mustread + ); + else { + /* No RCS pathname was given. */ + /* Try each suffix in turn. */ + dlen = base-arg; + x = suffixes; + while (! fin2open(arg, dlen, base, baselen, + x, xlen=suffixlen(x), rcsopen, mustread + )) { + x += xlen; + if (!*x++) + break; + } + } + } + RCSname = p = RCSbuf.string; + 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) { + 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"); + + prevkeys = false; + return finptr ? 1 : -1; +} + + + char const * +getfullRCSname() +/* + * Return a pointer to the full pathname of the RCS file. + * Remove leading `./'. + */ +{ + 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; + + 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) + )) { + 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"); +# else + d = getwd(wdbuf.string); + if (!d && !(d = PWD)) + efaterror("getwd"); +# endif + } + wdlen = dir_useful_len(d); + d[wdlen] = 0; + 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); + d = rcsbuf.string; + VOID memcpy(d, wd, dlen); + d += dlen; + *d++ = SLASH; + VOID strcpy(d, r); +# endif + return rcsbuf.string; + } +} + +/* 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; +} + +#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 + 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); + } +# 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() */ + +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); + if (result!=0) { + diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n", + RCSname, workname, getfullRCSname() + ); + } + switch (result) { + case 0: continue; /* already paired file */ + + case 1: if (initflag) { + rcserror("already exists"); + } else { + diagnose("RCS file %s exists\n", RCSname); + } + Ifclose(finptr); + break; + + case -1:diagnose("RCS file doesn't exist\n"); + break; + } + + } while (++argv, --argc>=1); + +} + + void +exiterr() +{ + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} +#endif |