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, "$FreeBSD$") 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# if has_mktemp 288 int fd; 289# endif 290 291 if (t) 292 return t; 293 294 catchints(); 295 { 296# if has_mktemp 297 char const *tp = tmp(); 298 size_t tplen = dir_useful_len(tp); 299 p = testalloc(tplen + 10); 300 VOID sprintf(p, "%.*s%cT%cXXXXXX", (int)tplen, tp, SLASH, '0'+n); 301 fd = mkstemp(p); 302 if (fd < 0 || !*p) 303 faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'", 304 (int)tplen, tp, SLASH, '0'+n 305 ); 306 close(fd); 307# else 308 static char tpnamebuf[TEMPNAMES][L_tmpnam]; 309 p = tpnamebuf[n]; 310 if (!tmpnam(p) || !*p) 311# ifdef P_tmpdir 312 faterror("can't make temporary pathname `%s...'",P_tmpdir); 313# else 314 faterror("can't make temporary pathname"); 315# endif 316# endif 317 } 318 319 tpnames[n] = p; 320 return p; 321} 322 323 void 324tempunlink() 325/* Clean up maketemp() files. May be invoked by signal handler. 326 */ 327{ 328 register int i; 329 register char *p; 330 331 for (i = TEMPNAMES; 0 <= --i; ) 332 if ((p = tpnames[i])) { 333 VOID unlink(p); 334 /* 335 * We would tfree(p) here, 336 * but this might dump core if we're handing a signal. 337 * We're about to exit anyway, so we won't bother. 338 */ 339 tpnames[i] = 0; 340 } 341} 342 343 344 static char const * 345bindex(sp, c) 346 register char const *sp; 347 register int c; 348/* Function: Finds the last occurrence of character c in string sp 349 * and returns a pointer to the character just beyond it. If the 350 * character doesn't occur in the string, sp is returned. 351 */ 352{ 353 register char const *r; 354 r = sp; 355 while (*sp) { 356 if (*sp++ == c) r=sp; 357 } 358 return r; 359} 360 361 362 363 static int 364suffix_matches(suffix, pattern) 365 register char const *suffix, *pattern; 366{ 367 register int c; 368 if (!pattern) 369 return true; 370 for (;;) 371 switch (*suffix++ - (c = *pattern++)) { 372 case 0: 373 if (!c) 374 return true; 375 break; 376 377 case 'A'-'a': 378 if (ctab[c] == Letter) 379 break; 380 /* fall into */ 381 default: 382 return false; 383 } 384} 385 386 387 static void 388InitAdmin() 389/* function: initializes an admin node */ 390{ 391 register char const *Suffix; 392 register int i; 393 394 Head=0; Dbranch=0; AccessList=0; Symbols=0; Locks=0; 395 StrictLocks=STRICT_LOCKING; 396 397 /* guess the comment leader from the suffix*/ 398 Suffix = bindex(workname, '.'); 399 if (Suffix==workname) Suffix= ""; /* empty suffix; will get default*/ 400 for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++) 401 continue; 402 Comment.string = comtable[i].comlead; 403 Comment.size = strlen(comtable[i].comlead); 404 Expand = KEYVAL_EXPAND; 405 clear_buf(&Ignored); 406 Lexinit(); /* note: if !finptr, reads nothing; only initializes */ 407} 408 409 410 411 void 412bufalloc(b, size) 413 register struct buf *b; 414 size_t size; 415/* Ensure *B is a name buffer of at least SIZE bytes. 416 * *B's old contents can be freed; *B's new contents are undefined. 417 */ 418{ 419 if (b->size < size) { 420 if (b->size) 421 tfree(b->string); 422 else 423 b->size = sizeof(malloc_type); 424 while (b->size < size) 425 b->size <<= 1; 426 b->string = tnalloc(char, b->size); 427 } 428} 429 430 void 431bufrealloc(b, size) 432 register struct buf *b; 433 size_t size; 434/* like bufalloc, except *B's old contents, if any, are preserved */ 435{ 436 if (b->size < size) { 437 if (!b->size) 438 bufalloc(b, size); 439 else { 440 while ((b->size <<= 1) < size) 441 continue; 442 b->string = trealloc(char, b->string, b->size); 443 } 444 } 445} 446 447 void 448bufautoend(b) 449 struct buf *b; 450/* Free an auto buffer at block exit. */ 451{ 452 if (b->size) 453 tfree(b->string); 454} 455 456 struct cbuf 457bufremember(b, s) 458 struct buf *b; 459 size_t s; 460/* 461 * Free the buffer B with used size S. 462 * Yield a cbuf with identical contents. 463 * The cbuf will be reclaimed when this input file is finished. 464 */ 465{ 466 struct cbuf cb; 467 468 if ((cb.size = s)) 469 cb.string = fremember(trealloc(char, b->string, s)); 470 else { 471 bufautoend(b); /* not really auto */ 472 cb.string = ""; 473 } 474 return cb; 475} 476 477 char * 478bufenlarge(b, alim) 479 register struct buf *b; 480 char const **alim; 481/* Make *B larger. Set *ALIM to its new limit, and yield the relocated value 482 * of its old limit. 483 */ 484{ 485 size_t s = b->size; 486 bufrealloc(b, s + 1); 487 *alim = b->string + b->size; 488 return b->string + s; 489} 490 491 void 492bufscat(b, s) 493 struct buf *b; 494 char const *s; 495/* Concatenate S to B's end. */ 496{ 497 size_t blen = b->string ? strlen(b->string) : 0; 498 bufrealloc(b, blen+strlen(s)+1); 499 VOID strcpy(b->string+blen, s); 500} 501 502 void 503bufscpy(b, s) 504 struct buf *b; 505 char const *s; 506/* Copy S into B. */ 507{ 508 bufalloc(b, strlen(s)+1); 509 VOID strcpy(b->string, s); 510} 511 512 513 char const * 514basefilename(p) 515 char const *p; 516/* Yield the address of the base filename of the pathname P. */ 517{ 518 register char const *b = p, *q = p; 519 for (;;) 520 switch (*q++) { 521 case SLASHes: b = q; break; 522 case 0: return b; 523 } 524} 525 526 527 static size_t 528suffixlen(x) 529 char const *x; 530/* Yield the length of X, an RCS pathname suffix. */ 531{ 532 register char const *p; 533 534 p = x; 535 for (;;) 536 switch (*p) { 537 case 0: case SLASHes: 538 return p - x; 539 540 default: 541 ++p; 542 continue; 543 } 544} 545 546 char const * 547rcssuffix(name) 548 char const *name; 549/* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise. */ 550{ 551 char const *x, *p, *nz; 552 size_t nl, xl; 553 554 nl = strlen(name); 555 nz = name + nl; 556 x = suffixes; 557 do { 558 if ((xl = suffixlen(x))) { 559 if (xl <= nl && memcmp(p = nz-xl, x, xl) == 0) 560 return p; 561 } else 562 for (p = name; p < nz - rcslen; p++) 563 if ( 564 isSLASH(p[rcslen]) 565 && (p==name || isSLASH(p[-1])) 566 && memcmp(p, rcsdir, rcslen) == 0 567 ) 568 return nz; 569 x += xl; 570 } while (*x++); 571 return 0; 572} 573 574 /*ARGSUSED*/ RILE * 575rcsreadopen(RCSpath, status, mustread) 576 struct buf *RCSpath; 577 struct stat *status; 578 int mustread; 579/* Open RCSPATH for reading and yield its FILE* descriptor. 580 * If successful, set *STATUS to its status. 581 * Pass this routine to pairnames() for read-only access to the file. */ 582{ 583 return Iopen(RCSpath->string, FOPEN_RB, status); 584} 585 586 static int 587finopen(rcsopen, mustread) 588 RILE *(*rcsopen)P((struct buf*,struct stat*,int)); 589 int mustread; 590/* 591 * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read. 592 * Set finptr to the result and yield true if successful. 593 * RCSb holds the file's name. 594 * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno. 595 * Yield true if successful or if an unusual failure. 596 */ 597{ 598 int interesting, preferold; 599 600 /* 601 * We prefer an old name to that of a nonexisting new RCS file, 602 * unless we tried locking the old name and failed. 603 */ 604 preferold = RCSbuf.string[0] && (mustread||0<=fdlock); 605 606 finptr = (*rcsopen)(&RCSb, &RCSstat, mustread); 607 interesting = finptr || errno!=ENOENT; 608 if (interesting || !preferold) { 609 /* Use the new name. */ 610 RCSerrno = errno; 611 bufscpy(&RCSbuf, RCSb.string); 612 } 613 return interesting; 614} 615 616 static int 617fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread) 618 char const *d, *base, *x; 619 size_t dlen, baselen, xlen; 620 RILE *(*rcsopen)P((struct buf*,struct stat*,int)); 621 int mustread; 622/* 623 * D is a directory name with length DLEN (including trailing slash). 624 * BASE is a filename with length BASELEN. 625 * X is an RCS pathname suffix with length XLEN. 626 * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read. 627 * Yield true if successful. 628 * Try dRCS/basex first; if that fails and x is nonempty, try dbasex. 629 * Put these potential names in RCSb. 630 * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno. 631 * Yield true if successful or if an unusual failure. 632 */ 633{ 634 register char *p; 635 636 bufalloc(&RCSb, dlen + rcslen + 1 + baselen + xlen + 1); 637 638 /* Try dRCS/basex. */ 639 VOID memcpy(p = RCSb.string, d, dlen); 640 VOID memcpy(p += dlen, rcsdir, rcslen); 641 p += rcslen; 642 *p++ = SLASH; 643 VOID memcpy(p, base, baselen); 644 VOID memcpy(p += baselen, x, xlen); 645 p[xlen] = 0; 646 if (xlen) { 647 if (finopen(rcsopen, mustread)) 648 return true; 649 650 /* Try dbasex. */ 651 /* Start from scratch, because finopen() may have changed RCSb. */ 652 VOID memcpy(p = RCSb.string, d, dlen); 653 VOID memcpy(p += dlen, base, baselen); 654 VOID memcpy(p += baselen, x, xlen); 655 p[xlen] = 0; 656 } 657 return finopen(rcsopen, mustread); 658} 659 660 int 661pairnames(argc, argv, rcsopen, mustread, quiet) 662 int argc; 663 char **argv; 664 RILE *(*rcsopen)P((struct buf*,struct stat*,int)); 665 int mustread, quiet; 666/* 667 * Pair the pathnames pointed to by argv; argc indicates 668 * how many there are. 669 * Place a pointer to the RCS pathname into RCSname, 670 * and a pointer to the pathname of the working file into workname. 671 * If both are given, and workstdout 672 * is set, a warning is printed. 673 * 674 * If the RCS file exists, places its status into RCSstat. 675 * 676 * If the RCS file exists, it is RCSOPENed for reading, the file pointer 677 * is placed into finptr, and the admin-node is read in; returns 1. 678 * If the RCS file does not exist and MUSTREAD, 679 * print an error unless QUIET and return 0. 680 * Otherwise, initialize the admin node and return -1. 681 * 682 * 0 is returned on all errors, e.g. files that are not regular files. 683 */ 684{ 685 static struct buf tempbuf; 686 687 register char *p, *arg, *RCS1; 688 char const *base, *RCSbase, *x; 689 int paired; 690 size_t arglen, dlen, baselen, xlen; 691 692 fdlock = -1; 693 694 if (!(arg = *argv)) return 0; /* already paired pathname */ 695 if (*arg == '-') { 696 error("%s option is ignored after pathnames", arg); 697 return 0; 698 } 699 700 base = basefilename(arg); 701 paired = false; 702 703 /* first check suffix to see whether it is an RCS file or not */ 704 if ((x = rcssuffix(arg))) 705 { 706 /* RCS pathname given */ 707 RCS1 = arg; 708 RCSbase = base; 709 baselen = x - base; 710 if ( 711 1 < argc && 712 !rcssuffix(workname = p = argv[1]) && 713 baselen <= (arglen = strlen(p)) && 714 ((p+=arglen-baselen) == workname || isSLASH(p[-1])) && 715 memcmp(base, p, baselen) == 0 716 ) { 717 argv[1] = 0; 718 paired = true; 719 } else { 720 bufscpy(&tempbuf, base); 721 workname = p = tempbuf.string; 722 p[baselen] = 0; 723 } 724 } else { 725 /* working file given; now try to find RCS file */ 726 workname = arg; 727 baselen = strlen(base); 728 /* Derive RCS pathname. */ 729 if ( 730 1 < argc && 731 (x = rcssuffix(RCS1 = argv[1])) && 732 baselen <= x - RCS1 && 733 ((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) && 734 memcmp(base, RCSbase, baselen) == 0 735 ) { 736 argv[1] = 0; 737 paired = true; 738 } else 739 RCSbase = RCS1 = 0; 740 } 741 /* Now we have a (tentative) RCS pathname in RCS1 and workname. */ 742 /* Second, try to find the right RCS file */ 743 if (RCSbase!=RCS1) { 744 /* a path for RCSfile is given; single RCS file to look for */ 745 bufscpy(&RCSbuf, RCS1); 746 finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread); 747 RCSerrno = errno; 748 } else { 749 bufscpy(&RCSbuf, ""); 750 if (RCS1) 751 /* RCS filename was given without path. */ 752 VOID fin2open(arg, (size_t)0, RCSbase, baselen, 753 x, strlen(x), rcsopen, mustread 754 ); 755 else { 756 /* No RCS pathname was given. */ 757 /* Try each suffix in turn. */ 758 dlen = base-arg; 759 x = suffixes; 760 while (! fin2open(arg, dlen, base, baselen, 761 x, xlen=suffixlen(x), rcsopen, mustread 762 )) { 763 x += xlen; 764 if (!*x++) 765 break; 766 } 767 } 768 } 769 RCSname = p = RCSbuf.string; 770 if (finptr) { 771 if (!S_ISREG(RCSstat.st_mode)) { 772 error("%s isn't a regular file -- ignored", p); 773 return 0; 774 } 775 Lexinit(); getadmin(); 776 } else { 777 if (RCSerrno!=ENOENT || mustread || fdlock<0) { 778 if (RCSerrno == EEXIST) 779 error("RCS file %s is in use", p); 780 else if (!quiet || RCSerrno!=ENOENT) 781 enerror(RCSerrno, p); 782 return 0; 783 } 784 InitAdmin(); 785 }; 786 787 if (paired && workstdout) 788 workwarn("Working file ignored due to -p option"); 789 790 prevkeys = false; 791 return finptr ? 1 : -1; 792} 793 794 795 char const * 796getfullRCSname() 797/* 798 * Return a pointer to the full pathname of the RCS file. 799 * Remove leading `./'. 800 */ 801{ 802 if (ROOTPATH(RCSname)) { 803 return RCSname; 804 } else { 805 static struct buf rcsbuf; 806# if needs_getabsname 807 bufalloc(&rcsbuf, SIZEABLE_PATH + 1); 808 while (getabsname(RCSname, rcsbuf.string, rcsbuf.size) != 0) 809 if (errno == ERANGE) 810 bufalloc(&rcsbuf, rcsbuf.size<<1); 811 else 812 efaterror("getabsname"); 813# else 814 static char const *wdptr; 815 static struct buf wdbuf; 816 static size_t wdlen; 817 818 register char const *r; 819 register size_t dlen; 820 register char *d; 821 register char const *wd; 822 823 if (!(wd = wdptr)) { 824 /* Get working directory for the first time. */ 825 char *PWD = cgetenv("PWD"); 826 struct stat PWDstat, dotstat; 827 if (! ( 828 (d = PWD) && 829 ROOTPATH(PWD) && 830 stat(PWD, &PWDstat) == 0 && 831 stat(".", &dotstat) == 0 && 832 same_file(PWDstat, dotstat, 1) 833 )) { 834 bufalloc(&wdbuf, SIZEABLE_PATH + 1); 835# if has_getcwd || !has_getwd 836 while (!(d = getcwd(wdbuf.string, wdbuf.size))) 837 if (errno == ERANGE) 838 bufalloc(&wdbuf, wdbuf.size<<1); 839 else if ((d = PWD)) 840 break; 841 else 842 efaterror("getcwd"); 843# else 844 d = getwd(wdbuf.string); 845 if (!d && !(d = PWD)) 846 efaterror("getwd"); 847# endif 848 } 849 wdlen = dir_useful_len(d); 850 d[wdlen] = 0; 851 wdptr = wd = d; 852 } 853 /* 854 * Remove leading `./'s from RCSname. 855 * Do not try to handle `../', since removing it may yield 856 * the wrong answer in the presence of symbolic links. 857 */ 858 for (r = RCSname; r[0]=='.' && isSLASH(r[1]); r += 2) 859 /* `.////' is equivalent to `./'. */ 860 while (isSLASH(r[2])) 861 r++; 862 /* Build full pathname. */ 863 dlen = wdlen; 864 bufalloc(&rcsbuf, dlen + strlen(r) + 2); 865 d = rcsbuf.string; 866 VOID memcpy(d, wd, dlen); 867 d += dlen; 868 *d++ = SLASH; 869 VOID strcpy(d, r); 870# endif 871 return rcsbuf.string; 872 } 873} 874 875/* Derived from code from the XFree86 project */ 876 char const * 877getfullCVSname() 878/* Function: returns a pointer to the path name of the RCS file with the 879 * CVSROOT part stripped off, and with 'Attic/' stripped off (if present). 880 */ 881{ 882 883#define ATTICDIR "/Attic" 884 885 char const *namebuf = getfullRCSname(); 886 char *cvsroot = cgetenv("CVSROOT"); 887 int cvsrootlen; 888 char *c = NULL; 889 int alen = strlen(ATTICDIR); 890 891 if ((c = strrchr(namebuf, '/')) != NULL) { 892 if (namebuf - c >= alen) { 893 if (!strncmp(c - alen, ATTICDIR, alen)) { 894 while(*c != '\0') { 895 *(c - alen) = *c; 896 c++; 897 } 898 *(c - alen) = '\0'; 899 } 900 } 901 } 902 903 if (!cvsroot) 904 return(namebuf); 905 else 906 { 907 cvsrootlen = strlen(cvsroot); 908 if (!strncmp(namebuf, cvsroot, cvsrootlen) && 909 namebuf[cvsrootlen] == '/') 910 return(namebuf + cvsrootlen + 1); 911 else 912 return(namebuf); 913 } 914} 915 916 static size_t 917dir_useful_len(d) 918 char const *d; 919/* 920* D names a directory; yield the number of characters of D's useful part. 921* To create a file in D, append a SLASH and a file name to D's useful part. 922* Ignore trailing slashes if possible; not only are they ugly, 923* but some non-Posix systems misbehave unless the slashes are omitted. 924*/ 925{ 926# ifndef SLASHSLASH_is_SLASH 927# define SLASHSLASH_is_SLASH 0 928# endif 929 size_t dlen = strlen(d); 930 if (!SLASHSLASH_is_SLASH && dlen==2 && isSLASH(d[0]) && isSLASH(d[1])) 931 --dlen; 932 else 933 while (dlen && isSLASH(d[dlen-1])) 934 --dlen; 935 return dlen; 936} 937 938#ifndef isSLASH 939 int 940isSLASH(c) 941 int c; 942{ 943 switch (c) { 944 case SLASHes: 945 return true; 946 default: 947 return false; 948 } 949} 950#endif 951 952 953#if !has_getcwd && !has_getwd 954 955 char * 956getcwd(path, size) 957 char *path; 958 size_t size; 959{ 960 static char const usrbinpwd[] = "/usr/bin/pwd"; 961# define binpwd (usrbinpwd+4) 962 963 register FILE *fp; 964 register int c; 965 register char *p, *lim; 966 int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus; 967 pid_t child; 968 969 if (!size) { 970 errno = EINVAL; 971 return 0; 972 } 973 if (pipe(fd) != 0) 974 return 0; 975# if bad_wait_if_SIGCHLD_ignored 976# ifndef SIGCHLD 977# define SIGCHLD SIGCLD 978# endif 979 VOID signal(SIGCHLD, SIG_DFL); 980# endif 981 if (!(child = vfork())) { 982 if ( 983 close(fd[0]) == 0 && 984 (fd[1] == STDOUT_FILENO || 985# ifdef F_DUPFD 986 (VOID close(STDOUT_FILENO), 987 fcntl(fd[1], F_DUPFD, STDOUT_FILENO)) 988# else 989 dup2(fd[1], STDOUT_FILENO) 990# endif 991 == STDOUT_FILENO && 992 close(fd[1]) == 0 993 ) 994 ) { 995 VOID close(STDERR_FILENO); 996 VOID execl(binpwd, binpwd, (char *)0); 997 VOID execl(usrbinpwd, usrbinpwd, (char *)0); 998 } 999 _exit(EXIT_FAILURE); 1000 } 1001 e = errno; 1002 closeerror = close(fd[1]); 1003 closeerrno = errno; 1004 fp = 0; 1005 readerror = toolong = wstatus = 0; 1006 p = path; 1007 if (0 <= child) { 1008 fp = fdopen(fd[0], "r"); 1009 e = errno; 1010 if (fp) { 1011 lim = p + size; 1012 for (p = path; ; *p++ = c) { 1013 if ((c=getc(fp)) < 0) { 1014 if (feof(fp)) 1015 break; 1016 if (ferror(fp)) { 1017 readerror = 1; 1018 e = errno; 1019 break; 1020 } 1021 } 1022 if (p == lim) { 1023 toolong = 1; 1024 break; 1025 } 1026 } 1027 } 1028# if has_waitpid 1029 if (waitpid(child, &wstatus, 0) < 0) 1030 wstatus = 1; 1031# else 1032 { 1033 pid_t w; 1034 do { 1035 if ((w = wait(&wstatus)) < 0) { 1036 wstatus = 1; 1037 break; 1038 } 1039 } while (w != child); 1040 } 1041# endif 1042 } 1043 if (!fp) { 1044 VOID close(fd[0]); 1045 errno = e; 1046 return 0; 1047 } 1048 if (fclose(fp) != 0) 1049 return 0; 1050 if (readerror) { 1051 errno = e; 1052 return 0; 1053 } 1054 if (closeerror) { 1055 errno = closeerrno; 1056 return 0; 1057 } 1058 if (toolong) { 1059 errno = ERANGE; 1060 return 0; 1061 } 1062 if (wstatus || p == path || *--p != '\n') { 1063 errno = EACCES; 1064 return 0; 1065 } 1066 *p = '\0'; 1067 return path; 1068} 1069#endif 1070 1071 1072#ifdef PAIRTEST 1073/* test program for pairnames() and getfullRCSname() */ 1074 1075char const cmdid[] = "pair"; 1076 1077main(argc, argv) 1078int argc; char *argv[]; 1079{ 1080 int result; 1081 int initflag; 1082 quietflag = initflag = false; 1083 1084 while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) { 1085 switch ((*argv)[1]) { 1086 1087 case 'p': workstdout = stdout; 1088 break; 1089 case 'i': initflag=true; 1090 break; 1091 case 'q': quietflag=true; 1092 break; 1093 default: error("unknown option: %s", *argv); 1094 break; 1095 } 1096 } 1097 1098 do { 1099 RCSname = workname = 0; 1100 result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag); 1101 if (result!=0) { 1102 diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n", 1103 RCSname, workname, getfullRCSname() 1104 ); 1105 } 1106 switch (result) { 1107 case 0: continue; /* already paired file */ 1108 1109 case 1: if (initflag) { 1110 rcserror("already exists"); 1111 } else { 1112 diagnose("RCS file %s exists\n", RCSname); 1113 } 1114 Ifclose(finptr); 1115 break; 1116 1117 case -1:diagnose("RCS file doesn't exist\n"); 1118 break; 1119 } 1120 1121 } while (++argv, --argc>=1); 1122 1123} 1124 1125 void 1126exiterr() 1127{ 1128 dirtempunlink(); 1129 tempunlink(); 1130 _exit(EXIT_FAILURE); 1131} 1132#endif 1133