1/* RCS stream editor */ 2 3/****************************************************************************** 4 * edits the input file according to a 5 * script from stdin, generated by diff -n 6 * performs keyword expansion 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 * $Log: rcsedit.c,v $ 39 * Revision 1.1 2003/06/11 15:56:09 darkwyrm 40 * Added rcs, gzip, sed, and associated utilities. 41 * 42 * Revision 5.19 1995/06/16 06:19:24 eggert 43 * Update FSF address. 44 * 45 * Revision 5.18 1995/06/01 16:23:43 eggert 46 * (dirtpname): No longer external. 47 * (do_link): Simplify logic. 48 * (finisheditline, finishedit): Replace Iseek/Itell with what they stand for. 49 * (fopen_update_truncate): Replace `#if' with `if'. 50 * (keyreplace, makedirtemp): dirlen(x) -> basefilename(x)-x. 51 * 52 * (edit_string): Fix bug: if !large_memory, a bogus trailing `@' was output 53 * at the end of incomplete lines. 54 * 55 * (keyreplace): Do not assume that seeking backwards 56 * at the start of a file will fail; on some systems it succeeds. 57 * Convert C- and Pascal-style comment starts to ` *' in comment leader. 58 * 59 * (rcswriteopen): Use fdSafer to get safer file descriptor. 60 * Open RCS file with FOPEN_RB. 61 * 62 * (chnamemod): Work around bad_NFS_rename bug; don't ignore un_link result. 63 * Fall back on chmod if fchmod fails, since it might be ENOSYS. 64 * 65 * (aflush): Move to rcslex.c. 66 * 67 * Revision 5.17 1994/03/20 04:52:58 eggert 68 * Normally calculate the $Log prefix from context, not from RCS file. 69 * Move setmtime here from rcsutil.c. Add ORCSerror. Remove lint. 70 * 71 * Revision 5.16 1993/11/03 17:42:27 eggert 72 * Add -z. Add Name keyword. If bad_unlink, ignore errno when unlink fails. 73 * Escape white space, $, and \ in keyword string file names. 74 * Don't output 2 spaces between date and time after Log. 75 * 76 * Revision 5.15 1992/07/28 16:12:44 eggert 77 * Some hosts have readlink but not ELOOP. Avoid `unsigned'. 78 * Preserve dates more systematically. Statement macro names now end in _. 79 * 80 * Revision 5.14 1992/02/17 23:02:24 eggert 81 * Add -T support. 82 * 83 * Revision 5.13 1992/01/24 18:44:19 eggert 84 * Add support for bad_chmod_close, bad_creat0. 85 * 86 * Revision 5.12 1992/01/06 02:42:34 eggert 87 * Add setmode parameter to chnamemod. addsymbol now reports changes. 88 * while (E) ; -> while (E) continue; 89 * 90 * Revision 5.11 1991/11/03 01:11:44 eggert 91 * Move the warning about link breaking to where they're actually being broken. 92 * 93 * Revision 5.10 1991/10/07 17:32:46 eggert 94 * Support piece tables even if !has_mmap. Fix rare NFS bugs. 95 * 96 * Revision 5.9 1991/09/17 19:07:40 eggert 97 * SGI readlink() yields ENXIO, not EINVAL, for nonlinks. 98 * 99 * Revision 5.8 1991/08/19 03:13:55 eggert 100 * Add piece tables, NFS bug workarounds. Catch odd filenames. Tune. 101 * 102 * Revision 5.7 1991/04/21 11:58:21 eggert 103 * Fix errno bugs. Add -x, RCSINIT, MS-DOS support. 104 * 105 * Revision 5.6 1991/02/25 07:12:40 eggert 106 * Fix setuid bug. Support new link behavior. Work around broken "w+" fopen. 107 * 108 * Revision 5.5 1990/12/30 05:07:35 eggert 109 * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL). 110 * 111 * Revision 5.4 1990/11/01 05:03:40 eggert 112 * Permit arbitrary data in comment leaders. 113 * 114 * Revision 5.3 1990/09/11 02:41:13 eggert 115 * Tune expandline(). 116 * 117 * Revision 5.2 1990/09/04 08:02:21 eggert 118 * Count RCS lines better. Improve incomplete line handling. 119 * 120 * Revision 5.1 1990/08/29 07:13:56 eggert 121 * Add -kkvl. 122 * Fix bug when getting revisions to files ending in incomplete lines. 123 * Fix bug in comment leader expansion. 124 * 125 * Revision 5.0 1990/08/22 08:12:47 eggert 126 * Don't require final newline. 127 * Don't append "checked in with -k by " to logs, 128 * so that checking in a program with -k doesn't change it. 129 * Don't generate trailing white space for empty comment leader. 130 * Remove compile-time limits; use malloc instead. Add -k, -V. 131 * Permit dates past 1999/12/31. Make lock and temp files faster and safer. 132 * Ansify and Posixate. Check diff's output. 133 * 134 * Revision 4.8 89/05/01 15:12:35 narten 135 * changed copyright header to reflect current distribution rules 136 * 137 * Revision 4.7 88/11/08 13:54:14 narten 138 * misplaced semicolon caused infinite loop 139 * 140 * Revision 4.6 88/08/09 19:12:45 eggert 141 * Shrink stdio code size; allow cc -R. 142 * 143 * Revision 4.5 87/12/18 11:38:46 narten 144 * Changes from the 43. version. Don't know the significance of the 145 * first change involving "rewind". Also, additional "lint" cleanup. 146 * (Guy Harris) 147 * 148 * Revision 4.4 87/10/18 10:32:21 narten 149 * Updating version numbers. Changes relative to version 1.1 actually 150 * relative to 4.1 151 * 152 * Revision 1.4 87/09/24 13:59:29 narten 153 * Sources now pass through lint (if you ignore printf/sprintf/fprintf 154 * warnings) 155 * 156 * Revision 1.3 87/09/15 16:39:39 shepler 157 * added an initializatin of the variables editline and linecorr 158 * this will be done each time a file is processed. 159 * (there was an obscure bug where if co was used to retrieve multiple files 160 * it would dump) 161 * fix attributed to Roy Morris @FileNet Corp ...!felix!roy 162 * 163 * Revision 1.2 87/03/27 14:22:17 jenkins 164 * Port to suns 165 * 166 * Revision 4.1 83/05/12 13:10:30 wft 167 * Added new markers Id and RCSfile; added locker to Header and Id. 168 * Overhauled expandline completely() (problem with $01234567890123456789@). 169 * Moved trymatch() and marker table to rcskeys.c. 170 * 171 * Revision 3.7 83/05/12 13:04:39 wft 172 * Added retry to expandline to resume after failed match which ended in $. 173 * Fixed truncation problem for $19chars followed by@@. 174 * Log no longer expands full path of RCS file. 175 * 176 * Revision 3.6 83/05/11 16:06:30 wft 177 * added retry to expandline to resume after failed match which ended in $. 178 * Fixed truncation problem for $19chars followed by@@. 179 * 180 * Revision 3.5 82/12/04 13:20:56 wft 181 * Added expansion of keyword Locker. 182 * 183 * Revision 3.4 82/12/03 12:26:54 wft 184 * Added line number correction in case editing does not start at the 185 * beginning of the file. 186 * Changed keyword expansion to always print a space before closing KDELIM; 187 * Expansion for Header shortened. 188 * 189 * Revision 3.3 82/11/14 14:49:30 wft 190 * removed Suffix from keyword expansion. Replaced fclose with ffclose. 191 * keyreplace() gets log message from delta, not from curlogmsg. 192 * fixed expression overflow in while(c=putc(GETC.... 193 * checked nil printing. 194 * 195 * Revision 3.2 82/10/18 21:13:39 wft 196 * I added checks for write errors during the co process, and renamed 197 * expandstring() to xpandstring(). 198 * 199 * Revision 3.1 82/10/13 15:52:55 wft 200 * changed type of result of getc() from char to int. 201 * made keyword expansion loop in expandline() portable to machines 202 * without sign-extension. 203 */ 204 205 206#include "rcsbase.h" 207 208libId(editId, "$Id: rcsedit.c 3476 2003-06-11 15:56:10Z darkwyrm $") 209 210static void editEndsPrematurely P((void)) exiting; 211static void editLineNumberOverflow P((void)) exiting; 212static void escape_string P((FILE*,char const*)); 213static void keyreplace P((enum markers,struct hshentry const*,int,RILE*,FILE*,int)); 214 215FILE *fcopy; /* result file descriptor */ 216char const *resultname; /* result pathname */ 217int locker_expansion; /* should the locker name be appended to Id val? */ 218#if !large_memory 219 static RILE *fedit; /* edit file descriptor */ 220 static char const *editname; /* edit pathname */ 221#endif 222static long editline; /* edit line counter; #lines before cursor */ 223static long linecorr; /* #adds - #deletes in each edit run. */ 224 /*used to correct editline in case file is not rewound after */ 225 /* applying one delta */ 226 227/* indexes into dirtpname */ 228#define lockdirtp_index 0 229#define newRCSdirtp_index bad_creat0 230#define newworkdirtp_index (newRCSdirtp_index+1) 231#define DIRTEMPNAMES (newworkdirtp_index + 1) 232 233enum maker {notmade, real, effective}; 234static struct buf dirtpname[DIRTEMPNAMES]; /* unlink these when done */ 235static enum maker volatile dirtpmaker[DIRTEMPNAMES]; /* if these are set */ 236#define lockname (dirtpname[lockdirtp_index].string) 237#define newRCSname (dirtpname[newRCSdirtp_index].string) 238 239 240#if has_NFS || bad_unlink 241 int 242un_link(s) 243 char const *s; 244/* 245 * Remove S, even if it is unwritable. 246 * Ignore unlink() ENOENT failures; NFS generates bogus ones. 247 */ 248{ 249# if bad_unlink 250 if (unlink(s) == 0) 251 return 0; 252 else { 253 int e = errno; 254 /* 255 * Forge ahead even if errno == ENOENT; some completely 256 * brain-damaged hosts (e.g. PCTCP 2.2) yield ENOENT 257 * even for existing unwritable files. 258 */ 259 if (chmod(s, S_IWUSR) != 0) { 260 errno = e; 261 return -1; 262 } 263 } 264# endif 265# if has_NFS 266 return unlink(s)==0 || errno==ENOENT ? 0 : -1; 267# else 268 return unlink(s); 269# endif 270} 271#endif 272 273#if !has_rename 274# if !has_NFS 275# define do_link(s,t) link(s,t) 276# else 277 static int do_link P((char const*,char const*)); 278 static int 279do_link(s, t) 280 char const *s, *t; 281/* Link S to T, ignoring bogus EEXIST problems due to NFS failures. */ 282{ 283 int r = link(s, t); 284 285 if (r != 0 && errno == EEXIST) { 286 struct stat sb, tb; 287 if ( 288 stat(s, &sb) == 0 && 289 stat(t, &tb) == 0 && 290 same_file(sb, tb, 0) 291 ) 292 r = 0; 293 errno = EEXIST; 294 } 295 return r; 296} 297# endif 298#endif 299 300 301 static void 302editEndsPrematurely() 303{ 304 fatserror("edit script ends prematurely"); 305} 306 307 static void 308editLineNumberOverflow() 309{ 310 fatserror("edit script refers to line past end of file"); 311} 312 313 314#if large_memory 315 316#if has_memmove 317# define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type)) 318#else 319 static void movelines P((Iptr_type*,Iptr_type const*,long)); 320 static void 321movelines(s1, s2, n) 322 register Iptr_type *s1; 323 register Iptr_type const *s2; 324 register long n; 325{ 326 if (s1 < s2) 327 do { 328 *s1++ = *s2++; 329 } while (--n); 330 else { 331 s1 += n; 332 s2 += n; 333 do { 334 *--s1 = *--s2; 335 } while (--n); 336 } 337} 338#endif 339 340static void deletelines P((long,long)); 341static void finisheditline P((RILE*,FILE*,Iptr_type,struct hshentry const*)); 342static void insertline P((long,Iptr_type)); 343static void snapshotline P((FILE*,Iptr_type)); 344 345/* 346 * `line' contains pointers to the lines in the currently `edited' file. 347 * It is a 0-origin array that represents linelim-gapsize lines. 348 * line[0 .. gap-1] and line[gap+gapsize .. linelim-1] hold pointers to lines. 349 * line[gap .. gap+gapsize-1] contains garbage. 350 * 351 * Any @s in lines are duplicated. 352 * Lines are terminated by \n, or (for a last partial line only) by single @. 353 */ 354static Iptr_type *line; 355static size_t gap, gapsize, linelim; 356 357 static void 358insertline(n, l) 359 long n; 360 Iptr_type l; 361/* Before line N, insert line L. N is 0-origin. */ 362{ 363 if (linelim-gapsize < n) 364 editLineNumberOverflow(); 365 if (!gapsize) 366 line = 367 !linelim ? 368 tnalloc(Iptr_type, linelim = gapsize = 1024) 369 : ( 370 gap = gapsize = linelim, 371 trealloc(Iptr_type, line, linelim <<= 1) 372 ); 373 if (n < gap) 374 movelines(line+n+gapsize, line+n, gap-n); 375 else if (gap < n) 376 movelines(line+gap, line+gap+gapsize, n-gap); 377 378 line[n] = l; 379 gap = n + 1; 380 gapsize--; 381} 382 383 static void 384deletelines(n, nlines) 385 long n, nlines; 386/* Delete lines N through N+NLINES-1. N is 0-origin. */ 387{ 388 long l = n + nlines; 389 if (linelim-gapsize < l || l < n) 390 editLineNumberOverflow(); 391 if (l < gap) 392 movelines(line+l+gapsize, line+l, gap-l); 393 else if (gap < n) 394 movelines(line+gap, line+gap+gapsize, n-gap); 395 396 gap = n; 397 gapsize += nlines; 398} 399 400 static void 401snapshotline(f, l) 402 register FILE *f; 403 register Iptr_type l; 404{ 405 register int c; 406 do { 407 if ((c = *l++) == SDELIM && *l++ != SDELIM) 408 return; 409 aputc_(c, f) 410 } while (c != '\n'); 411} 412 413 void 414snapshotedit(f) 415 FILE *f; 416/* Copy the current state of the edits to F. */ 417{ 418 register Iptr_type *p, *lim, *l=line; 419 for (p=l, lim=l+gap; p<lim; ) 420 snapshotline(f, *p++); 421 for (p+=gapsize, lim=l+linelim; p<lim; ) 422 snapshotline(f, *p++); 423} 424 425 static void 426finisheditline(fin, fout, l, delta) 427 RILE *fin; 428 FILE *fout; 429 Iptr_type l; 430 struct hshentry const *delta; 431{ 432 fin->ptr = l; 433 if (expandline(fin, fout, delta, true, (FILE*)0, true) < 0) 434 faterror("finisheditline internal error"); 435} 436 437 void 438finishedit(delta, outfile, done) 439 struct hshentry const *delta; 440 FILE *outfile; 441 int done; 442/* 443 * Doing expansion if DELTA is set, output the state of the edits to OUTFILE. 444 * But do nothing unless DONE is set (which means we are on the last pass). 445 */ 446{ 447 if (done) { 448 openfcopy(outfile); 449 outfile = fcopy; 450 if (!delta) 451 snapshotedit(outfile); 452 else { 453 register Iptr_type *p, *lim, *l = line; 454 register RILE *fin = finptr; 455 Iptr_type here = fin->ptr; 456 for (p=l, lim=l+gap; p<lim; ) 457 finisheditline(fin, outfile, *p++, delta); 458 for (p+=gapsize, lim=l+linelim; p<lim; ) 459 finisheditline(fin, outfile, *p++, delta); 460 fin->ptr = here; 461 } 462 } 463} 464 465/* Open a temporary NAME for output, truncating any previous contents. */ 466# define fopen_update_truncate(name) fopenSafer(name, FOPEN_W_WORK) 467#else /* !large_memory */ 468 static FILE * fopen_update_truncate P((char const*)); 469 static FILE * 470fopen_update_truncate(name) 471 char const *name; 472{ 473 if (bad_fopen_wplus && un_link(name) != 0) 474 efaterror(name); 475 return fopenSafer(name, FOPEN_WPLUS_WORK); 476} 477#endif 478 479 480 void 481openfcopy(f) 482 FILE *f; 483{ 484 if (!(fcopy = f)) { 485 if (!resultname) 486 resultname = maketemp(2); 487 if (!(fcopy = fopen_update_truncate(resultname))) 488 efaterror(resultname); 489 } 490} 491 492 493#if !large_memory 494 495 static void swapeditfiles P((FILE*)); 496 static void 497swapeditfiles(outfile) 498 FILE *outfile; 499/* Function: swaps resultname and editname, assigns fedit=fcopy, 500 * and rewinds fedit for reading. Set fcopy to outfile if nonnull; 501 * otherwise, set fcopy to be resultname opened for reading and writing. 502 */ 503{ 504 char const *tmpptr; 505 506 editline = 0; linecorr = 0; 507 Orewind(fcopy); 508 fedit = fcopy; 509 tmpptr=editname; editname=resultname; resultname=tmpptr; 510 openfcopy(outfile); 511} 512 513 void 514snapshotedit(f) 515 FILE *f; 516/* Copy the current state of the edits to F. */ 517{ 518 finishedit((struct hshentry *)0, (FILE*)0, false); 519 fastcopy(fedit, f); 520 Irewind(fedit); 521} 522 523 void 524finishedit(delta, outfile, done) 525 struct hshentry const *delta; 526 FILE *outfile; 527 int done; 528/* copy the rest of the edit file and close it (if it exists). 529 * if delta, perform keyword substitution at the same time. 530 * If DONE is set, we are finishing the last pass. 531 */ 532{ 533 register RILE *fe; 534 register FILE *fc; 535 536 fe = fedit; 537 if (fe) { 538 fc = fcopy; 539 if (delta) { 540 while (1 < expandline(fe,fc,delta,false,(FILE*)0,true)) 541 ; 542 } else { 543 fastcopy(fe,fc); 544 } 545 Ifclose(fe); 546 } 547 if (!done) 548 swapeditfiles(outfile); 549} 550#endif 551 552 553 554#if large_memory 555# define copylines(upto,delta) (editline = (upto)) 556#else 557 static void copylines P((long,struct hshentry const*)); 558 static void 559copylines(upto, delta) 560 register long upto; 561 struct hshentry const *delta; 562/* 563 * Copy input lines editline+1..upto from fedit to fcopy. 564 * If delta, keyword expansion is done simultaneously. 565 * editline is updated. Rewinds a file only if necessary. 566 */ 567{ 568 register int c; 569 declarecache; 570 register FILE *fc; 571 register RILE *fe; 572 573 if (upto < editline) { 574 /* swap files */ 575 finishedit((struct hshentry *)0, (FILE*)0, false); 576 /* assumes edit only during last pass, from the beginning*/ 577 } 578 fe = fedit; 579 fc = fcopy; 580 if (editline < upto) 581 if (delta) 582 do { 583 if (expandline(fe,fc,delta,false,(FILE*)0,true) <= 1) 584 editLineNumberOverflow(); 585 } while (++editline < upto); 586 else { 587 setupcache(fe); cache(fe); 588 do { 589 do { 590 cachegeteof_(c, editLineNumberOverflow();) 591 aputc_(c, fc) 592 } while (c != '\n'); 593 } while (++editline < upto); 594 uncache(fe); 595 } 596} 597#endif 598 599 600 601 void 602xpandstring(delta) 603 struct hshentry const *delta; 604/* Function: Reads a string terminated by SDELIM from finptr and writes it 605 * to fcopy. Double SDELIM is replaced with single SDELIM. 606 * Keyword expansion is performed with data from delta. 607 * If foutptr is nonnull, the string is also copied unchanged to foutptr. 608 */ 609{ 610 while (1 < expandline(finptr,fcopy,delta,true,foutptr,true)) 611 continue; 612} 613 614 615 void 616copystring() 617/* Function: copies a string terminated with a single SDELIM from finptr to 618 * fcopy, replacing all double SDELIM with a single SDELIM. 619 * If foutptr is nonnull, the string also copied unchanged to foutptr. 620 * editline is incremented by the number of lines copied. 621 * Assumption: next character read is first string character. 622 */ 623{ register c; 624 declarecache; 625 register FILE *frew, *fcop; 626 register int amidline; 627 register RILE *fin; 628 629 fin = finptr; 630 setupcache(fin); cache(fin); 631 frew = foutptr; 632 fcop = fcopy; 633 amidline = false; 634 for (;;) { 635 GETC_(frew,c) 636 switch (c) { 637 case '\n': 638 ++editline; 639 ++rcsline; 640 amidline = false; 641 break; 642 case SDELIM: 643 GETC_(frew,c) 644 if (c != SDELIM) { 645 /* end of string */ 646 nextc = c; 647 editline += amidline; 648 uncache(fin); 649 return; 650 } 651 /* fall into */ 652 default: 653 amidline = true; 654 break; 655 } 656 aputc_(c,fcop) 657 } 658} 659 660 661 void 662enterstring() 663/* Like copystring, except the string is put into the edit data structure. */ 664{ 665#if !large_memory 666 editname = 0; 667 fedit = 0; 668 editline = linecorr = 0; 669 resultname = maketemp(1); 670 if (!(fcopy = fopen_update_truncate(resultname))) 671 efaterror(resultname); 672 copystring(); 673#else 674 register int c; 675 declarecache; 676 register FILE *frew; 677 register long e, oe; 678 register int amidline, oamidline; 679 register Iptr_type optr; 680 register RILE *fin; 681 682 e = 0; 683 gap = 0; 684 gapsize = linelim; 685 fin = finptr; 686 setupcache(fin); cache(fin); 687 advise_access(fin, MADV_NORMAL); 688 frew = foutptr; 689 amidline = false; 690 for (;;) { 691 optr = cacheptr(); 692 GETC_(frew,c) 693 oamidline = amidline; 694 oe = e; 695 switch (c) { 696 case '\n': 697 ++e; 698 ++rcsline; 699 amidline = false; 700 break; 701 case SDELIM: 702 GETC_(frew,c) 703 if (c != SDELIM) { 704 /* end of string */ 705 nextc = c; 706 editline = e + amidline; 707 linecorr = 0; 708 uncache(fin); 709 return; 710 } 711 /* fall into */ 712 default: 713 amidline = true; 714 break; 715 } 716 if (!oamidline) 717 insertline(oe, optr); 718 } 719#endif 720} 721 722 723 724 725 void 726#if large_memory 727edit_string() 728#else 729 editstring(delta) 730 struct hshentry const *delta; 731#endif 732/* 733 * Read an edit script from finptr and applies it to the edit file. 734#if !large_memory 735 * The result is written to fcopy. 736 * If delta, keyword expansion is performed simultaneously. 737 * If running out of lines in fedit, fedit and fcopy are swapped. 738 * editname is the name of the file that goes with fedit. 739#endif 740 * If foutptr is set, the edit script is also copied verbatim to foutptr. 741 * Assumes that all these files are open. 742 * resultname is the name of the file that goes with fcopy. 743 * Assumes the next input character from finptr is the first character of 744 * the edit script. Resets nextc on exit. 745 */ 746{ 747 int ed; /* editor command */ 748 register int c; 749 declarecache; 750 register FILE *frew; 751# if !large_memory 752 register FILE *f; 753 long line_lim = LONG_MAX; 754 register RILE *fe; 755# endif 756 register long i; 757 register RILE *fin; 758# if large_memory 759 register long j; 760# endif 761 struct diffcmd dc; 762 763 editline += linecorr; linecorr=0; /*correct line number*/ 764 frew = foutptr; 765 fin = finptr; 766 setupcache(fin); 767 initdiffcmd(&dc); 768 while (0 <= (ed = getdiffcmd(fin,true,frew,&dc))) 769#if !large_memory 770 if (line_lim <= dc.line1) 771 editLineNumberOverflow(); 772 else 773#endif 774 if (!ed) { 775 copylines(dc.line1-1, delta); 776 /* skip over unwanted lines */ 777 i = dc.nlines; 778 linecorr -= i; 779 editline += i; 780# if large_memory 781 deletelines(editline+linecorr, i); 782# else 783 fe = fedit; 784 do { 785 /*skip next line*/ 786 do { 787 Igeteof_(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } ) 788 } while (c != '\n'); 789 } while (--i); 790# endif 791 } else { 792 /* Copy lines without deleting any. */ 793 copylines(dc.line1, delta); 794 i = dc.nlines; 795# if large_memory 796 j = editline+linecorr; 797# endif 798 linecorr += i; 799#if !large_memory 800 f = fcopy; 801 if (delta) 802 do { 803 switch (expandline(fin,f,delta,true,frew,true)){ 804 case 0: case 1: 805 if (i==1) 806 return; 807 /* fall into */ 808 case -1: 809 editEndsPrematurely(); 810 } 811 } while (--i); 812 else 813#endif 814 { 815 cache(fin); 816 do { 817# if large_memory 818 insertline(j++, cacheptr()); 819# endif 820 for (;;) { 821 GETC_(frew, c) 822 if (c==SDELIM) { 823 GETC_(frew, c) 824 if (c!=SDELIM) { 825 if (--i) 826 editEndsPrematurely(); 827 nextc = c; 828 uncache(fin); 829 return; 830 } 831 } 832# if !large_memory 833 aputc_(c, f) 834# endif 835 if (c == '\n') 836 break; 837 } 838 ++rcsline; 839 } while (--i); 840 uncache(fin); 841 } 842 } 843} 844 845 846 847/* The rest is for keyword expansion */ 848 849 850 851 int 852expandline(infile, outfile, delta, delimstuffed, frewfile, dolog) 853 RILE *infile; 854 FILE *outfile, *frewfile; 855 struct hshentry const *delta; 856 int delimstuffed, dolog; 857/* 858 * Read a line from INFILE and write it to OUTFILE. 859 * Do keyword expansion with data from DELTA. 860 * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM. 861 * If FREWFILE is set, copy the line unchanged to FREWFILE. 862 * DELIMSTUFFED must be true if FREWFILE is set. 863 * Append revision history to log only if DOLOG is set. 864 * Yields -1 if no data is copied, 0 if an incomplete line is copied, 865 * 2 if a complete line is copied; adds 1 to yield if expansion occurred. 866 */ 867{ 868 register c; 869 declarecache; 870 register FILE *out, *frew; 871 register char * tp; 872 register int e, ds, r; 873 char const *tlim; 874 static struct buf keyval; 875 enum markers matchresult; 876 877 setupcache(infile); cache(infile); 878 out = outfile; 879 frew = frewfile; 880 ds = delimstuffed; 881 bufalloc(&keyval, keylength+3); 882 e = 0; 883 r = -1; 884 885 for (;;) { 886 if (ds) 887 GETC_(frew, c) 888 else 889 cachegeteof_(c, goto uncache_exit;) 890 for (;;) { 891 switch (c) { 892 case SDELIM: 893 if (ds) { 894 GETC_(frew, c) 895 if (c != SDELIM) { 896 /* end of string */ 897 nextc=c; 898 goto uncache_exit; 899 } 900 } 901 /* fall into */ 902 default: 903 aputc_(c,out) 904 r = 0; 905 break; 906 907 case '\n': 908 rcsline += ds; 909 aputc_(c,out) 910 r = 2; 911 goto uncache_exit; 912 913 case KDELIM: 914 r = 0; 915 /* check for keyword */ 916 /* first, copy a long enough string into keystring */ 917 tp = keyval.string; 918 *tp++ = KDELIM; 919 for (;;) { 920 if (ds) 921 GETC_(frew, c) 922 else 923 cachegeteof_(c, goto keystring_eof;) 924 if (tp <= &keyval.string[keylength]) 925 switch (ctab[c]) { 926 case LETTER: case Letter: 927 *tp++ = c; 928 continue; 929 default: 930 break; 931 } 932 break; 933 } 934 *tp++ = c; *tp = '\0'; 935 matchresult = trymatch(keyval.string+1); 936 if (matchresult==Nomatch) { 937 tp[-1] = 0; 938 aputs(keyval.string, out); 939 continue; /* last c handled properly */ 940 } 941 942 /* Now we have a keyword terminated with a K/VDELIM */ 943 if (c==VDELIM) { 944 /* try to find closing KDELIM, and replace value */ 945 tlim = keyval.string + keyval.size; 946 for (;;) { 947 if (ds) 948 GETC_(frew, c) 949 else 950 cachegeteof_(c, goto keystring_eof;) 951 if (c=='\n' || c==KDELIM) 952 break; 953 *tp++ =c; 954 if (tlim <= tp) 955 tp = bufenlarge(&keyval, &tlim); 956 if (c==SDELIM && ds) { /*skip next SDELIM */ 957 GETC_(frew, c) 958 if (c != SDELIM) { 959 /* end of string before closing KDELIM or newline */ 960 nextc = c; 961 goto keystring_eof; 962 } 963 } 964 } 965 if (c!=KDELIM) { 966 /* couldn't find closing KDELIM -- give up */ 967 *tp = 0; 968 aputs(keyval.string, out); 969 continue; /* last c handled properly */ 970 } 971 } 972 /* now put out the new keyword value */ 973 uncache(infile); 974 keyreplace(matchresult, delta, ds, infile, out, dolog); 975 cache(infile); 976 e = 1; 977 break; 978 } 979 break; 980 } 981 } 982 983 keystring_eof: 984 *tp = 0; 985 aputs(keyval.string, out); 986 uncache_exit: 987 uncache(infile); 988 return r + e; 989} 990 991 992 static void 993escape_string(out, s) 994 register FILE *out; 995 register char const *s; 996/* Output to OUT the string S, escaping chars that would break `ci -k'. */ 997{ 998 register char c; 999 for (;;) 1000 switch ((c = *s++)) { 1001 case 0: return; 1002 case '\t': aputs("\\t", out); break; 1003 case '\n': aputs("\\n", out); break; 1004 case ' ': aputs("\\040", out); break; 1005 case KDELIM: aputs("\\044", out); break; 1006 case '\\': if (VERSION(5)<=RCSversion) {aputs("\\\\", out); break;} 1007 /* fall into */ 1008 default: aputc_(c, out) break; 1009 } 1010} 1011 1012char const ciklog[ciklogsize] = "checked in with -k by "; 1013 1014 static void 1015keyreplace(marker, delta, delimstuffed, infile, out, dolog) 1016 enum markers marker; 1017 register struct hshentry const *delta; 1018 int delimstuffed; 1019 RILE *infile; 1020 register FILE *out; 1021 int dolog; 1022/* function: outputs the keyword value(s) corresponding to marker. 1023 * Attributes are derived from delta. 1024 */ 1025{ 1026 register char const *sp, *cp, *date; 1027 register int c; 1028 register size_t cs, cw, ls; 1029 char const *sp1; 1030 char datebuf[datesize + zonelenmax]; 1031 int RCSv; 1032 int exp; 1033 1034 sp = Keyword[(int)marker]; 1035 exp = Expand; 1036 date = delta->date; 1037 RCSv = RCSversion; 1038 1039 if (exp != VAL_EXPAND) 1040 aprintf(out, "%c%s", KDELIM, sp); 1041 if (exp != KEY_EXPAND) { 1042 1043 if (exp != VAL_EXPAND) 1044 aprintf(out, "%c%c", VDELIM, 1045 marker==Log && RCSv<VERSION(5) ? '\t' : ' ' 1046 ); 1047 1048 switch (marker) { 1049 case Author: 1050 aputs(delta->author, out); 1051 break; 1052 case Date: 1053 aputs(date2str(date,datebuf), out); 1054 break; 1055 case Id: 1056 case Header: 1057 escape_string(out, 1058 marker==Id || RCSv<VERSION(4) 1059 ? basefilename(RCSname) 1060 : getfullRCSname() 1061 ); 1062 aprintf(out, " %s %s %s %s", 1063 delta->num, 1064 date2str(date, datebuf), 1065 delta->author, 1066 RCSv==VERSION(3) && delta->lockedby ? "Locked" 1067 : delta->state 1068 ); 1069 if (delta->lockedby) 1070 if (VERSION(5) <= RCSv) { 1071 if (locker_expansion || exp==KEYVALLOCK_EXPAND) 1072 aprintf(out, " %s", delta->lockedby); 1073 } else if (RCSv == VERSION(4)) 1074 aprintf(out, " Locker: %s", delta->lockedby); 1075 break; 1076 case Locker: 1077 if (delta->lockedby) 1078 if ( 1079 locker_expansion 1080 || exp == KEYVALLOCK_EXPAND 1081 || RCSv <= VERSION(4) 1082 ) 1083 aputs(delta->lockedby, out); 1084 break; 1085 case Log: 1086 case RCSfile: 1087 escape_string(out, basefilename(RCSname)); 1088 break; 1089 case Name: 1090 if (delta->name) 1091 aputs(delta->name, out); 1092 break; 1093 case Revision: 1094 aputs(delta->num, out); 1095 break; 1096 case Source: 1097 escape_string(out, getfullRCSname()); 1098 break; 1099 case State: 1100 aputs(delta->state, out); 1101 break; 1102 default: 1103 break; 1104 } 1105 if (exp != VAL_EXPAND) 1106 afputc(' ', out); 1107 } 1108 if (exp != VAL_EXPAND) 1109 afputc(KDELIM, out); 1110 1111 if (marker == Log && dolog) { 1112 struct buf leader; 1113 1114 sp = delta->log.string; 1115 ls = delta->log.size; 1116 if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1)) 1117 return; 1118 bufautobegin(&leader); 1119 if (RCSversion < VERSION(5)) { 1120 cp = Comment.string; 1121 cs = Comment.size; 1122 } else { 1123 int kdelim_found = 0; 1124 Ioffset_type chars_read = Itell(infile); 1125 declarecache; 1126 setupcache(infile); cache(infile); 1127 1128 c = 0; /* Pacify `gcc -Wall'. */ 1129 1130 /* 1131 * Back up to the start of the current input line, 1132 * setting CS to the number of characters before `$Log'. 1133 */ 1134 cs = 0; 1135 for (;;) { 1136 if (!--chars_read) 1137 goto done_backing_up; 1138 cacheunget_(infile, c) 1139 if (c == '\n') 1140 break; 1141 if (c == SDELIM && delimstuffed) { 1142 if (!--chars_read) 1143 break; 1144 cacheunget_(infile, c) 1145 if (c != SDELIM) { 1146 cacheget_(c) 1147 break; 1148 } 1149 } 1150 cs += kdelim_found; 1151 kdelim_found |= c==KDELIM; 1152 } 1153 cacheget_(c) 1154 done_backing_up:; 1155 1156 /* Copy characters before `$Log' into LEADER. */ 1157 bufalloc(&leader, cs); 1158 cp = leader.string; 1159 for (cw = 0; cw < cs; cw++) { 1160 leader.string[cw] = c; 1161 if (c == SDELIM && delimstuffed) 1162 cacheget_(c) 1163 cacheget_(c) 1164 } 1165 1166 /* Convert traditional C or Pascal leader to ` *'. */ 1167 for (cw = 0; cw < cs; cw++) 1168 if (ctab[(unsigned char) cp[cw]] != SPACE) 1169 break; 1170 if ( 1171 cw+1 < cs 1172 && cp[cw+1] == '*' 1173 && (cp[cw] == '/' || cp[cw] == '(') 1174 ) { 1175 size_t i = cw+1; 1176 for (;;) 1177 if (++i == cs) { 1178 warn( 1179 "`%c* $Log' is obsolescent; use ` * $Log'.", 1180 cp[cw] 1181 ); 1182 leader.string[cw] = ' '; 1183 break; 1184 } else if (ctab[(unsigned char) cp[i]] != SPACE) 1185 break; 1186 } 1187 1188 /* Skip `$Log ... $' string. */ 1189 do { 1190 cacheget_(c) 1191 } while (c != KDELIM); 1192 uncache(infile); 1193 } 1194 afputc('\n', out); 1195 awrite(cp, cs, out); 1196 sp1 = date2str(date, datebuf); 1197 if (VERSION(5) <= RCSv) { 1198 aprintf(out, "Revision %s %s %s", 1199 delta->num, sp1, delta->author 1200 ); 1201 } else { 1202 /* oddity: 2 spaces between date and time, not 1 as usual */ 1203 sp1 = strchr(sp1, ' '); 1204 aprintf(out, "Revision %s %.*s %s %s", 1205 delta->num, (int)(sp1-datebuf), datebuf, sp1, 1206 delta->author 1207 ); 1208 } 1209 /* Do not include state: it may change and is not updated. */ 1210 cw = cs; 1211 if (VERSION(5) <= RCSv) 1212 for (; cw && (cp[cw-1]==' ' || cp[cw-1]=='\t'); --cw) 1213 continue; 1214 for (;;) { 1215 afputc('\n', out); 1216 awrite(cp, cw, out); 1217 if (!ls) 1218 break; 1219 --ls; 1220 c = *sp++; 1221 if (c != '\n') { 1222 awrite(cp+cw, cs-cw, out); 1223 do { 1224 afputc(c,out); 1225 if (!ls) 1226 break; 1227 --ls; 1228 c = *sp++; 1229 } while (c != '\n'); 1230 } 1231 } 1232 bufautoend(&leader); 1233 } 1234} 1235 1236#if has_readlink 1237 static int resolve_symlink P((struct buf*)); 1238 static int 1239resolve_symlink(L) 1240 struct buf *L; 1241/* 1242 * If L is a symbolic link, resolve it to the name that it points to. 1243 * If unsuccessful, set errno and yield -1. 1244 * If it points to an existing file, yield 1. 1245 * Otherwise, set errno=ENOENT and yield 0. 1246 */ 1247{ 1248 char *b, a[SIZEABLE_PATH]; 1249 int e; 1250 size_t s; 1251 ssize_t r; 1252 struct buf bigbuf; 1253 int linkcount = MAXSYMLINKS; 1254 1255 b = a; 1256 s = sizeof(a); 1257 bufautobegin(&bigbuf); 1258 while ((r = readlink(L->string,b,s)) != -1) 1259 if (r == s) { 1260 bufalloc(&bigbuf, s<<1); 1261 b = bigbuf.string; 1262 s = bigbuf.size; 1263 } else if (!linkcount--) { 1264# ifndef ELOOP 1265 /* 1266 * Some pedantic Posix 1003.1-1990 hosts have readlink 1267 * but not ELOOP. Approximate ELOOP with EMLINK. 1268 */ 1269# define ELOOP EMLINK 1270# endif 1271 errno = ELOOP; 1272 return -1; 1273 } else { 1274 /* Splice symbolic link into L. */ 1275 b[r] = '\0'; 1276 L->string[ 1277 ROOTPATH(b) ? 0 : basefilename(L->string) - L->string 1278 ] = '\0'; 1279 bufscat(L, b); 1280 } 1281 e = errno; 1282 bufautoend(&bigbuf); 1283 errno = e; 1284 switch (e) { 1285 case readlink_isreg_errno: return 1; 1286 case ENOENT: return 0; 1287 default: return -1; 1288 } 1289} 1290#endif 1291 1292 RILE * 1293rcswriteopen(RCSbuf, status, mustread) 1294 struct buf *RCSbuf; 1295 struct stat *status; 1296 int mustread; 1297/* 1298 * Create the lock file corresponding to RCSBUF. 1299 * Then try to open RCSBUF for reading and yield its RILE* descriptor. 1300 * Put its status into *STATUS too. 1301 * MUSTREAD is true if the file must already exist, too. 1302 * If all goes well, discard any previously acquired locks, 1303 * and set fdlock to the file descriptor of the RCS lockfile. 1304 */ 1305{ 1306 register char *tp; 1307 register char const *sp, *RCSpath, *x; 1308 RILE *f; 1309 size_t l; 1310 int e, exists, fdesc, fdescSafer, r, waslocked; 1311 struct buf *dirt; 1312 struct stat statbuf; 1313 1314 waslocked = 0 <= fdlock; 1315 exists = 1316# if has_readlink 1317 resolve_symlink(RCSbuf); 1318# else 1319 stat(RCSbuf->string, &statbuf) == 0 ? 1 1320 : errno==ENOENT ? 0 : -1; 1321# endif 1322 if (exists < (mustread|waslocked)) 1323 /* 1324 * There's an unusual problem with the RCS file; 1325 * or the RCS file doesn't exist, 1326 * and we must read or we already have a lock elsewhere. 1327 */ 1328 return 0; 1329 1330 RCSpath = RCSbuf->string; 1331 sp = basefilename(RCSpath); 1332 l = sp - RCSpath; 1333 dirt = &dirtpname[waslocked]; 1334 bufscpy(dirt, RCSpath); 1335 tp = dirt->string + l; 1336 x = rcssuffix(RCSpath); 1337# if has_readlink 1338 if (!x) { 1339 error("symbolic link to non RCS file `%s'", RCSpath); 1340 errno = EINVAL; 1341 return 0; 1342 } 1343# endif 1344 if (*sp == *x) { 1345 error("RCS pathname `%s' incompatible with suffix `%s'", sp, x); 1346 errno = EINVAL; 1347 return 0; 1348 } 1349 /* Create a lock filename that is a function of the RCS filename. */ 1350 if (*x) { 1351 /* 1352 * The suffix is nonempty. 1353 * The lock filename is the first char of of the suffix, 1354 * followed by the RCS filename with last char removed. E.g.: 1355 * foo,v RCS filename with suffix ,v 1356 * ,foo, lock filename 1357 */ 1358 *tp++ = *x; 1359 while (*sp) 1360 *tp++ = *sp++; 1361 *--tp = 0; 1362 } else { 1363 /* 1364 * The suffix is empty. 1365 * The lock filename is the RCS filename 1366 * with last char replaced by '_'. 1367 */ 1368 while ((*tp++ = *sp++)) 1369 continue; 1370 tp -= 2; 1371 if (*tp == '_') { 1372 error("RCS pathname `%s' ends with `%c'", RCSpath, *tp); 1373 errno = EINVAL; 1374 return 0; 1375 } 1376 *tp = '_'; 1377 } 1378 1379 sp = dirt->string; 1380 1381 f = 0; 1382 1383 /* 1384 * good news: 1385 * open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_READONLY) 1386 * is atomic according to Posix 1003.1-1990. 1387 * bad news: 1388 * NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990. 1389 * good news: 1390 * (O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity 1391 * even with NFS. 1392 * bad news: 1393 * If you're root, (O_TRUNC,OPEN_CREAT_READONLY) doesn't 1394 * guarantee atomicity. 1395 * good news: 1396 * Root-over-the-wire NFS access is rare for security reasons. 1397 * This bug has never been reported in practice with RCS. 1398 * So we don't worry about this bug. 1399 * 1400 * An even rarer NFS bug can occur when clients retry requests. 1401 * This can happen in the usual case of NFS over UDP. 1402 * Suppose client A releases a lock by renaming ",f," to "f,v" at 1403 * about the same time that client B obtains a lock by creating ",f,", 1404 * and suppose A's first rename request is delayed, so A reissues it. 1405 * The sequence of events might be: 1406 * A sends rename(",f,", "f,v") 1407 * B sends create(",f,") 1408 * A sends retry of rename(",f,", "f,v") 1409 * server receives, does, and acknowledges A's first rename() 1410 * A receives acknowledgment, and its RCS program exits 1411 * server receives, does, and acknowledges B's create() 1412 * server receives, does, and acknowledges A's retry of rename() 1413 * This not only wrongly deletes B's lock, it removes the RCS file! 1414 * Most NFS implementations have idempotency caches that usually prevent 1415 * this scenario, but such caches are finite and can be overrun. 1416 * This problem afflicts not only RCS, which uses open() and rename() 1417 * to get and release locks; it also afflicts the traditional 1418 * Unix method of using link() and unlink() to get and release locks, 1419 * and the less traditional method of using mkdir() and rmdir(). 1420 * There is no easy workaround. 1421 * Any new method based on lockf() seemingly would be incompatible with 1422 * the old methods; besides, lockf() is notoriously buggy under NFS. 1423 * Since this problem afflicts scads of Unix programs, but is so rare 1424 * that nobody seems to be worried about it, we won't worry either. 1425 */ 1426# if !open_can_creat 1427# define create(f) creat(f, OPEN_CREAT_READONLY) 1428# else 1429# define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_READONLY) 1430# endif 1431 1432 catchints(); 1433 ignoreints(); 1434 1435 /* 1436 * Create a lock file for an RCS file. This should be atomic, i.e. 1437 * if two processes try it simultaneously, at most one should succeed. 1438 */ 1439 seteid(); 1440 fdesc = create(sp); 1441 fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr. */ 1442 e = errno; 1443 setrid(); 1444 1445 if (0 <= fdesc) 1446 dirtpmaker[0] = effective; 1447 1448 if (fdescSafer < 0) { 1449 if (e == EACCES && stat(sp,&statbuf) == 0) 1450 /* The RCS file is busy. */ 1451 e = EEXIST; 1452 } else { 1453 e = ENOENT; 1454 if (exists) { 1455 f = Iopen(RCSpath, FOPEN_RB, status); 1456 e = errno; 1457 if (f && waslocked) { 1458 /* Discard the previous lock in favor of this one. */ 1459 ORCSclose(); 1460 seteid(); 1461 r = un_link(lockname); 1462 e = errno; 1463 setrid(); 1464 if (r != 0) 1465 enfaterror(e, lockname); 1466 bufscpy(&dirtpname[lockdirtp_index], sp); 1467 } 1468 } 1469 fdlock = fdescSafer; 1470 } 1471 1472 restoreints(); 1473 1474 errno = e; 1475 return f; 1476} 1477 1478 void 1479keepdirtemp(name) 1480 char const *name; 1481/* Do not unlink name, either because it's not there any more, 1482 * or because it has already been unlinked. 1483 */ 1484{ 1485 register int i; 1486 for (i=DIRTEMPNAMES; 0<=--i; ) 1487 if (dirtpname[i].string == name) { 1488 dirtpmaker[i] = notmade; 1489 return; 1490 } 1491 faterror("keepdirtemp"); 1492} 1493 1494 char const * 1495makedirtemp(isworkfile) 1496 int isworkfile; 1497/* 1498 * Create a unique pathname and store it into dirtpname. 1499 * Because of storage in tpnames, dirtempunlink() can unlink the file later. 1500 * Return a pointer to the pathname created. 1501 * If ISWORKFILE is 1, put it into the working file's directory; 1502 * if 0, put the unique file in RCSfile's directory. 1503 */ 1504{ 1505 register char *tp, *np; 1506 register size_t dl; 1507 register struct buf *bn; 1508 register char const *name = isworkfile ? workname : RCSname; 1509 1510 dl = basefilename(name) - name; 1511 bn = &dirtpname[newRCSdirtp_index + isworkfile]; 1512 bufalloc(bn, 1513# if has_mktemp 1514 dl + 9 1515# else 1516 strlen(name) + 3 1517# endif 1518 ); 1519 bufscpy(bn, name); 1520 np = tp = bn->string; 1521 tp += dl; 1522 *tp++ = '_'; 1523 *tp++ = '0'+isworkfile; 1524 catchints(); 1525# if has_mktemp 1526 VOID strcpy(tp, "XXXXXX"); 1527 if (!mktemp(np) || !*np) 1528 faterror("can't make temporary pathname `%.*s_%cXXXXXX'", 1529 (int)dl, name, '0'+isworkfile 1530 ); 1531# else 1532 /* 1533 * Posix 1003.1-1990 has no reliable way 1534 * to create a unique file in a named directory. 1535 * We fudge here. If the filename is abcde, 1536 * the temp filename is _Ncde where N is a digit. 1537 */ 1538 name += dl; 1539 if (*name) name++; 1540 if (*name) name++; 1541 VOID strcpy(tp, name); 1542# endif 1543 dirtpmaker[newRCSdirtp_index + isworkfile] = real; 1544 return np; 1545} 1546 1547 void 1548dirtempunlink() 1549/* Clean up makedirtemp() files. May be invoked by signal handler. */ 1550{ 1551 register int i; 1552 enum maker m; 1553 1554 for (i = DIRTEMPNAMES; 0 <= --i; ) 1555 if ((m = dirtpmaker[i]) != notmade) { 1556 if (m == effective) 1557 seteid(); 1558 VOID un_link(dirtpname[i].string); 1559 if (m == effective) 1560 setrid(); 1561 dirtpmaker[i] = notmade; 1562 } 1563} 1564 1565 1566 int 1567#if has_prototypes 1568chnamemod( 1569 FILE **fromp, char const *from, char const *to, 1570 int set_mode, mode_t mode, time_t mtime 1571) 1572 /* The `#if has_prototypes' is needed because mode_t might promote to int. */ 1573#else 1574 chnamemod(fromp, from, to, set_mode, mode, mtime) 1575 FILE **fromp; char const *from,*to; 1576 int set_mode; mode_t mode; time_t mtime; 1577#endif 1578/* 1579 * Rename a file (with stream pointer *FROMP) from FROM to TO. 1580 * FROM already exists. 1581 * If 0 < SET_MODE, change the mode to MODE, before renaming if possible. 1582 * If MTIME is not -1, change its mtime to MTIME before renaming. 1583 * Close and clear *FROMP before renaming it. 1584 * Unlink TO if it already exists. 1585 * Return -1 on error (setting errno), 0 otherwise. 1586 */ 1587{ 1588 mode_t mode_while_renaming = mode; 1589 int fchmod_set_mode = 0; 1590 1591# if bad_a_rename || bad_NFS_rename 1592 struct stat st; 1593 if (bad_NFS_rename || (bad_a_rename && set_mode <= 0)) { 1594 if (fstat(fileno(*fromp), &st) != 0) 1595 return -1; 1596 if (bad_a_rename && set_mode <= 0) 1597 mode = st.st_mode; 1598 } 1599# endif 1600 1601# if bad_a_rename 1602 /* 1603 * There's a short window of inconsistency 1604 * during which the lock file is writable. 1605 */ 1606 mode_while_renaming = mode|S_IWUSR; 1607 if (mode != mode_while_renaming) 1608 set_mode = 1; 1609# endif 1610 1611# if has_fchmod 1612 if (0<set_mode && fchmod(fileno(*fromp),mode_while_renaming) == 0) 1613 fchmod_set_mode = set_mode; 1614# endif 1615 /* If bad_chmod_close, we must close before chmod. */ 1616 Ozclose(fromp); 1617 if (fchmod_set_mode<set_mode && chmod(from, mode_while_renaming) != 0) 1618 return -1; 1619 1620 if (setmtime(from, mtime) != 0) 1621 return -1; 1622 1623# if !has_rename || bad_b_rename 1624 /* 1625 * There's a short window of inconsistency 1626 * during which TO does not exist. 1627 */ 1628 if (un_link(to) != 0 && errno != ENOENT) 1629 return -1; 1630# endif 1631 1632# if has_rename 1633 if (rename(from,to) != 0 && !(has_NFS && errno==ENOENT)) 1634 return -1; 1635# else 1636 if (do_link(from,to) != 0 || un_link(from) != 0) 1637 return -1; 1638# endif 1639 1640# if bad_NFS_rename 1641 { 1642 /* 1643 * Check whether the rename falsely reported success. 1644 * A race condition can occur between the rename and the stat. 1645 */ 1646 struct stat tostat; 1647 if (stat(to, &tostat) != 0) 1648 return -1; 1649 if (! same_file(st, tostat, 0)) { 1650 errno = EIO; 1651 return -1; 1652 } 1653 } 1654# endif 1655 1656# if bad_a_rename 1657 if (0 < set_mode && chmod(to, mode) != 0) 1658 return -1; 1659# endif 1660 1661 return 0; 1662} 1663 1664 int 1665setmtime(file, mtime) 1666 char const *file; 1667 time_t mtime; 1668/* Set FILE's last modified time to MTIME, but do nothing if MTIME is -1. */ 1669{ 1670 static struct utimbuf amtime; /* static so unused fields are zero */ 1671 if (mtime == -1) 1672 return 0; 1673 amtime.actime = now(); 1674 amtime.modtime = mtime; 1675 return utime(file, &amtime); 1676} 1677 1678 1679 1680 int 1681findlock(delete, target) 1682 int delete; 1683 struct hshentry **target; 1684/* 1685 * Find the first lock held by caller and return a pointer 1686 * to the locked delta; also removes the lock if DELETE. 1687 * If one lock, put it into *TARGET. 1688 * Return 0 for no locks, 1 for one, 2 for two or more. 1689 */ 1690{ 1691 register struct rcslock *next, **trail, **found; 1692 1693 found = 0; 1694 for (trail = &Locks; (next = *trail); trail = &next->nextlock) 1695 if (strcmp(getcaller(), next->login) == 0) { 1696 if (found) { 1697 rcserror("multiple revisions locked by %s; please specify one", getcaller()); 1698 return 2; 1699 } 1700 found = trail; 1701 } 1702 if (!found) 1703 return 0; 1704 next = *found; 1705 *target = next->delta; 1706 if (delete) { 1707 next->delta->lockedby = 0; 1708 *found = next->nextlock; 1709 } 1710 return 1; 1711} 1712 1713 int 1714addlock(delta, verbose) 1715 struct hshentry * delta; 1716 int verbose; 1717/* 1718 * Add a lock held by caller to DELTA and yield 1 if successful. 1719 * Print an error message if verbose and yield -1 if no lock is added because 1720 * DELTA is locked by somebody other than caller. 1721 * Return 0 if the caller already holds the lock. 1722 */ 1723{ 1724 register struct rcslock *next; 1725 1726 for (next = Locks; next; next = next->nextlock) 1727 if (cmpnum(delta->num, next->delta->num) == 0) 1728 if (strcmp(getcaller(), next->login) == 0) 1729 return 0; 1730 else { 1731 if (verbose) 1732 rcserror("Revision %s is already locked by %s.", 1733 delta->num, next->login 1734 ); 1735 return -1; 1736 } 1737 next = ftalloc(struct rcslock); 1738 delta->lockedby = next->login = getcaller(); 1739 next->delta = delta; 1740 next->nextlock = Locks; 1741 Locks = next; 1742 return 1; 1743} 1744 1745 1746 int 1747addsymbol(num, name, rebind) 1748 char const *num, *name; 1749 int rebind; 1750/* 1751 * Associate with revision NUM the new symbolic NAME. 1752 * If NAME already exists and REBIND is set, associate NAME with NUM; 1753 * otherwise, print an error message and return false; 1754 * Return -1 if unsuccessful, 0 if no change, 1 if change. 1755 */ 1756{ 1757 register struct assoc *next; 1758 1759 for (next = Symbols; next; next = next->nextassoc) 1760 if (strcmp(name, next->symbol) == 0) 1761 if (strcmp(next->num,num) == 0) 1762 return 0; 1763 else if (rebind) { 1764 next->num = num; 1765 return 1; 1766 } else { 1767 rcserror("symbolic name %s already bound to %s", 1768 name, next->num 1769 ); 1770 return -1; 1771 } 1772 next = ftalloc(struct assoc); 1773 next->symbol = name; 1774 next->num = num; 1775 next->nextassoc = Symbols; 1776 Symbols = next; 1777 return 1; 1778} 1779 1780 1781 1782 char const * 1783getcaller() 1784/* Get the caller's login name. */ 1785{ 1786# if has_setuid 1787 return getusername(euid()!=ruid()); 1788# else 1789 return getusername(false); 1790# endif 1791} 1792 1793 1794 int 1795checkaccesslist() 1796/* 1797 * Return true if caller is the superuser, the owner of the 1798 * file, the access list is empty, or caller is on the access list. 1799 * Otherwise, print an error message and return false. 1800 */ 1801{ 1802 register struct access const *next; 1803 1804 if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0) 1805 return true; 1806 1807 next = AccessList; 1808 do { 1809 if (strcmp(getcaller(), next->login) == 0) 1810 return true; 1811 } while ((next = next->nextaccess)); 1812 1813 rcserror("user %s not on the access list", getcaller()); 1814 return false; 1815} 1816 1817 1818 int 1819dorewrite(lockflag, changed) 1820 int lockflag, changed; 1821/* 1822 * Do nothing if LOCKFLAG is zero. 1823 * Prepare to rewrite an RCS file if CHANGED is positive. 1824 * Stop rewriting if CHANGED is zero, because there won't be any changes. 1825 * Fail if CHANGED is negative. 1826 * Return 0 on success, -1 on failure. 1827 */ 1828{ 1829 int r = 0, e; 1830 1831 if (lockflag) 1832 if (changed) { 1833 if (changed < 0) 1834 return -1; 1835 putadmin(); 1836 puttree(Head, frewrite); 1837 aprintf(frewrite, "\n\n%s%c", Kdesc, nextc); 1838 foutptr = frewrite; 1839 } else { 1840# if bad_creat0 1841 int nr = !!frewrite, ne = 0; 1842# endif 1843 ORCSclose(); 1844 seteid(); 1845 ignoreints(); 1846# if bad_creat0 1847 if (nr) { 1848 nr = un_link(newRCSname); 1849 ne = errno; 1850 keepdirtemp(newRCSname); 1851 } 1852# endif 1853 r = un_link(lockname); 1854 e = errno; 1855 keepdirtemp(lockname); 1856 restoreints(); 1857 setrid(); 1858 if (r != 0) 1859 enerror(e, lockname); 1860# if bad_creat0 1861 if (nr != 0) { 1862 enerror(ne, newRCSname); 1863 r = -1; 1864 } 1865# endif 1866 } 1867 return r; 1868} 1869 1870 int 1871donerewrite(changed, newRCStime) 1872 int changed; 1873 time_t newRCStime; 1874/* 1875 * Finish rewriting an RCS file if CHANGED is nonzero. 1876 * Set its mode if CHANGED is positive. 1877 * Set its modification time to NEWRCSTIME unless it is -1. 1878 * Return 0 on success, -1 on failure. 1879 */ 1880{ 1881 int r = 0, e = 0; 1882# if bad_creat0 1883 int lr, le; 1884# endif 1885 1886 if (changed && !nerror) { 1887 if (finptr) { 1888 fastcopy(finptr, frewrite); 1889 Izclose(&finptr); 1890 } 1891 if (1 < RCSstat.st_nlink) 1892 rcswarn("breaking hard link"); 1893 aflush(frewrite); 1894 seteid(); 1895 ignoreints(); 1896 r = chnamemod( 1897 &frewrite, newRCSname, RCSname, changed, 1898 RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH), 1899 newRCStime 1900 ); 1901 e = errno; 1902 keepdirtemp(newRCSname); 1903# if bad_creat0 1904 lr = un_link(lockname); 1905 le = errno; 1906 keepdirtemp(lockname); 1907# endif 1908 restoreints(); 1909 setrid(); 1910 if (r != 0) { 1911 enerror(e, RCSname); 1912 error("saved in %s", newRCSname); 1913 } 1914# if bad_creat0 1915 if (lr != 0) { 1916 enerror(le, lockname); 1917 r = -1; 1918 } 1919# endif 1920 } 1921 return r; 1922} 1923 1924 void 1925ORCSclose() 1926{ 1927 if (0 <= fdlock) { 1928 if (close(fdlock) != 0) 1929 efaterror(lockname); 1930 fdlock = -1; 1931 } 1932 Ozclose(&frewrite); 1933} 1934 1935 void 1936ORCSerror() 1937/* 1938* Like ORCSclose, except we are cleaning up after an interrupt or fatal error. 1939* Do not report errors, since this may loop. This is needed only because 1940* some brain-damaged hosts (e.g. OS/2) cannot unlink files that are open, and 1941* some nearly-Posix hosts (e.g. NFS) work better if the files are closed first. 1942* This isn't a completely reliable away to work around brain-damaged hosts, 1943* because of the gap between actual file opening and setting frewrite etc., 1944* but it's better than nothing. 1945*/ 1946{ 1947 if (0 <= fdlock) 1948 VOID close(fdlock); 1949 if (frewrite) 1950 /* Avoid fclose, since stdio may not be reentrant. */ 1951 VOID close(fileno(frewrite)); 1952} 1953