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