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