rcsfnms.c revision 22996
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$") 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 static size_t 871dir_useful_len(d) 872 char const *d; 873/* 874* D names a directory; yield the number of characters of D's useful part. 875* To create a file in D, append a SLASH and a file name to D's useful part. 876* Ignore trailing slashes if possible; not only are they ugly, 877* but some non-Posix systems misbehave unless the slashes are omitted. 878*/ 879{ 880# ifndef SLASHSLASH_is_SLASH 881# define SLASHSLASH_is_SLASH 0 882# endif 883 size_t dlen = strlen(d); 884 if (!SLASHSLASH_is_SLASH && dlen==2 && isSLASH(d[0]) && isSLASH(d[1])) 885 --dlen; 886 else 887 while (dlen && isSLASH(d[dlen-1])) 888 --dlen; 889 return dlen; 890} 891 892#ifndef isSLASH 893 int 894isSLASH(c) 895 int c; 896{ 897 switch (c) { 898 case SLASHes: 899 return true; 900 default: 901 return false; 902 } 903} 904#endif 905 906 907#if !has_getcwd && !has_getwd 908 909 char * 910getcwd(path, size) 911 char *path; 912 size_t size; 913{ 914 static char const usrbinpwd[] = "/usr/bin/pwd"; 915# define binpwd (usrbinpwd+4) 916 917 register FILE *fp; 918 register int c; 919 register char *p, *lim; 920 int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus; 921 pid_t child; 922 923 if (!size) { 924 errno = EINVAL; 925 return 0; 926 } 927 if (pipe(fd) != 0) 928 return 0; 929# if bad_wait_if_SIGCHLD_ignored 930# ifndef SIGCHLD 931# define SIGCHLD SIGCLD 932# endif 933 VOID signal(SIGCHLD, SIG_DFL); 934# endif 935 if (!(child = vfork())) { 936 if ( 937 close(fd[0]) == 0 && 938 (fd[1] == STDOUT_FILENO || 939# ifdef F_DUPFD 940 (VOID close(STDOUT_FILENO), 941 fcntl(fd[1], F_DUPFD, STDOUT_FILENO)) 942# else 943 dup2(fd[1], STDOUT_FILENO) 944# endif 945 == STDOUT_FILENO && 946 close(fd[1]) == 0 947 ) 948 ) { 949 VOID close(STDERR_FILENO); 950 VOID execl(binpwd, binpwd, (char *)0); 951 VOID execl(usrbinpwd, usrbinpwd, (char *)0); 952 } 953 _exit(EXIT_FAILURE); 954 } 955 e = errno; 956 closeerror = close(fd[1]); 957 closeerrno = errno; 958 fp = 0; 959 readerror = toolong = wstatus = 0; 960 p = path; 961 if (0 <= child) { 962 fp = fdopen(fd[0], "r"); 963 e = errno; 964 if (fp) { 965 lim = p + size; 966 for (p = path; ; *p++ = c) { 967 if ((c=getc(fp)) < 0) { 968 if (feof(fp)) 969 break; 970 if (ferror(fp)) { 971 readerror = 1; 972 e = errno; 973 break; 974 } 975 } 976 if (p == lim) { 977 toolong = 1; 978 break; 979 } 980 } 981 } 982# if has_waitpid 983 if (waitpid(child, &wstatus, 0) < 0) 984 wstatus = 1; 985# else 986 { 987 pid_t w; 988 do { 989 if ((w = wait(&wstatus)) < 0) { 990 wstatus = 1; 991 break; 992 } 993 } while (w != child); 994 } 995# endif 996 } 997 if (!fp) { 998 VOID close(fd[0]); 999 errno = e; 1000 return 0; 1001 } 1002 if (fclose(fp) != 0) 1003 return 0; 1004 if (readerror) { 1005 errno = e; 1006 return 0; 1007 } 1008 if (closeerror) { 1009 errno = closeerrno; 1010 return 0; 1011 } 1012 if (toolong) { 1013 errno = ERANGE; 1014 return 0; 1015 } 1016 if (wstatus || p == path || *--p != '\n') { 1017 errno = EACCES; 1018 return 0; 1019 } 1020 *p = '\0'; 1021 return path; 1022} 1023#endif 1024 1025 1026#ifdef PAIRTEST 1027/* test program for pairnames() and getfullRCSname() */ 1028 1029char const cmdid[] = "pair"; 1030 1031main(argc, argv) 1032int argc; char *argv[]; 1033{ 1034 int result; 1035 int initflag; 1036 quietflag = initflag = false; 1037 1038 while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) { 1039 switch ((*argv)[1]) { 1040 1041 case 'p': workstdout = stdout; 1042 break; 1043 case 'i': initflag=true; 1044 break; 1045 case 'q': quietflag=true; 1046 break; 1047 default: error("unknown option: %s", *argv); 1048 break; 1049 } 1050 } 1051 1052 do { 1053 RCSname = workname = 0; 1054 result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag); 1055 if (result!=0) { 1056 diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n", 1057 RCSname, workname, getfullRCSname() 1058 ); 1059 } 1060 switch (result) { 1061 case 0: continue; /* already paired file */ 1062 1063 case 1: if (initflag) { 1064 rcserror("already exists"); 1065 } else { 1066 diagnose("RCS file %s exists\n", RCSname); 1067 } 1068 Ifclose(finptr); 1069 break; 1070 1071 case -1:diagnose("RCS file doesn't exist\n"); 1072 break; 1073 } 1074 1075 } while (++argv, --argc>=1); 1076 1077} 1078 1079 void 1080exiterr() 1081{ 1082 dirtempunlink(); 1083 tempunlink(); 1084 _exit(EXIT_FAILURE); 1085} 1086#endif 1087