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