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