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