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