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