1275970Scy/* RCS stream editor */ 2275970Scy 3275970Scy/****************************************************************************** 4275970Scy * edits the input file according to a 5275970Scy * script from stdin, generated by diff -n 6289999Sglebius * performs keyword expansion 7289999Sglebius ****************************************************************************** 8289999Sglebius */ 9289999Sglebius 10289999Sglebius/* Copyright 1982, 1988, 1989 Walter Tichy 11289999Sglebius Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert 12289999Sglebius Distributed under license by the Free Software Foundation, Inc. 13289999Sglebius 14289999SglebiusThis file is part of RCS. 15289999Sglebius 16289999SglebiusRCS is free software; you can redistribute it and/or modify 17289999Sglebiusit under the terms of the GNU General Public License as published by 18289999Sglebiusthe Free Software Foundation; either version 2, or (at your option) 19289999Sglebiusany later version. 20289999Sglebius 21289999SglebiusRCS is distributed in the hope that it will be useful, 22289999Sglebiusbut WITHOUT ANY WARRANTY; without even the implied warranty of 23289999SglebiusMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24289999SglebiusGNU General Public License for more details. 25289999Sglebius 26289999SglebiusYou should have received a copy of the GNU General Public License 27289999Sglebiusalong with RCS; see the file COPYING. 28289999SglebiusIf not, write to the Free Software Foundation, 29289999Sglebius59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 30289999Sglebius 31289999SglebiusReport problems and direct all questions to: 32289999Sglebius 33289999Sglebius rcs-bugs@cs.purdue.edu 34289999Sglebius 35289999Sglebius*/ 36289999Sglebius 37289999Sglebius/* 38289999Sglebius * Revision 5.19 1995/06/16 06:19:24 eggert 39289999Sglebius * Update FSF address. 40289999Sglebius * 41289999Sglebius * Revision 5.18 1995/06/01 16:23:43 eggert 42289999Sglebius * (dirtpname): No longer external. 43289999Sglebius * (do_link): Simplify logic. 44289999Sglebius * (finisheditline, finishedit): Replace Iseek/Itell with what they stand for. 45289999Sglebius * (fopen_update_truncate): Replace `#if' with `if'. 46289999Sglebius * (keyreplace, makedirtemp): dirlen(x) -> basefilename(x)-x. 47289999Sglebius * 48289999Sglebius * (edit_string): Fix bug: if !large_memory, a bogus trailing `@' was output 49289999Sglebius * at the end of incomplete lines. 50289999Sglebius * 51289999Sglebius * (keyreplace): Do not assume that seeking backwards 52289999Sglebius * at the start of a file will fail; on some systems it succeeds. 53275970Scy * Convert C- and Pascal-style comment starts to ` *' in comment leader. 54289999Sglebius * 55275970Scy * (rcswriteopen): Use fdSafer to get safer file descriptor. 56275970Scy * Open RCS file with FOPEN_RB. 57275970Scy * 58275970Scy * (chnamemod): Work around bad_NFS_rename bug; don't ignore un_link result. 59275970Scy * Fall back on chmod if fchmod fails, since it might be ENOSYS. 60275970Scy * 61275970Scy * (aflush): Move to rcslex.c. 62275970Scy * 63275970Scy * Revision 5.17 1994/03/20 04:52:58 eggert 64289999Sglebius * Normally calculate the $Log prefix from context, not from RCS file. 65289999Sglebius * Move setmtime here from rcsutil.c. Add ORCSerror. Remove lint. 66289999Sglebius * 67289999Sglebius * Revision 5.16 1993/11/03 17:42:27 eggert 68289999Sglebius * Add -z. Add Name keyword. If bad_unlink, ignore errno when unlink fails. 69289999Sglebius * Escape white space, $, and \ in keyword string file names. 70289999Sglebius * Don't output 2 spaces between date and time after Log. 71289999Sglebius * 72289999Sglebius * Revision 5.15 1992/07/28 16:12:44 eggert 73289999Sglebius * Some hosts have readlink but not ELOOP. Avoid `unsigned'. 74289999Sglebius * Preserve dates more systematically. Statement macro names now end in _. 75289999Sglebius * 76289999Sglebius * Revision 5.14 1992/02/17 23:02:24 eggert 77289999Sglebius * Add -T support. 78289999Sglebius * 79289999Sglebius * Revision 5.13 1992/01/24 18:44:19 eggert 80289999Sglebius * Add support for bad_chmod_close, bad_creat0. 81289999Sglebius * 82289999Sglebius * Revision 5.12 1992/01/06 02:42:34 eggert 83289999Sglebius * Add setmode parameter to chnamemod. addsymbol now reports changes. 84289999Sglebius * while (E) ; -> while (E) continue; 85289999Sglebius * 86289999Sglebius * Revision 5.11 1991/11/03 01:11:44 eggert 87289999Sglebius * Move the warning about link breaking to where they're actually being broken. 88289999Sglebius * 89289999Sglebius * Revision 5.10 1991/10/07 17:32:46 eggert 90289999Sglebius * Support piece tables even if !has_mmap. Fix rare NFS bugs. 91275970Scy * 92275970Scy * Revision 5.9 1991/09/17 19:07:40 eggert 93275970Scy * SGI readlink() yields ENXIO, not EINVAL, for nonlinks. 94310419Sdelphij * 95275970Scy * Revision 5.8 1991/08/19 03:13:55 eggert 96275970Scy * Add piece tables, NFS bug workarounds. Catch odd filenames. Tune. 97275970Scy * 98275970Scy * Revision 5.7 1991/04/21 11:58:21 eggert 99275970Scy * Fix errno bugs. Add -x, RCSINIT, MS-DOS support. 100275970Scy * 101275970Scy * Revision 5.6 1991/02/25 07:12:40 eggert 102275970Scy * Fix setuid bug. Support new link behavior. Work around broken "w+" fopen. 103275970Scy * 104275970Scy * Revision 5.5 1990/12/30 05:07:35 eggert 105275970Scy * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL). 106275970Scy * 107285612Sdelphij * Revision 5.4 1990/11/01 05:03:40 eggert 108275970Scy * Permit arbitrary data in comment leaders. 109275970Scy * 110275970Scy * Revision 5.3 1990/09/11 02:41:13 eggert 111275970Scy * Tune expandline(). 112275970Scy * 113275970Scy * Revision 5.2 1990/09/04 08:02:21 eggert 114275970Scy * Count RCS lines better. Improve incomplete line handling. 115275970Scy * 116275970Scy * Revision 5.1 1990/08/29 07:13:56 eggert 117275970Scy * Add -kkvl. 118275970Scy * Fix bug when getting revisions to files ending in incomplete lines. 119275970Scy * Fix bug in comment leader expansion. 120275970Scy * 121275970Scy * Revision 5.0 1990/08/22 08:12:47 eggert 122275970Scy * Don't require final newline. 123275970Scy * Don't append "checked in with -k by " to logs, 124275970Scy * so that checking in a program with -k doesn't change it. 125289999Sglebius * Don't generate trailing white space for empty comment leader. 126289999Sglebius * Remove compile-time limits; use malloc instead. Add -k, -V. 127289999Sglebius * Permit dates past 1999/12/31. Make lock and temp files faster and safer. 128289999Sglebius * Ansify and Posixate. Check diff's output. 129289999Sglebius * 130289999Sglebius * Revision 4.8 89/05/01 15:12:35 narten 131289999Sglebius * changed copyright header to reflect current distribution rules 132289999Sglebius * 133289999Sglebius * Revision 4.7 88/11/08 13:54:14 narten 134289999Sglebius * misplaced semicolon caused infinite loop 135289999Sglebius * 136289999Sglebius * Revision 4.6 88/08/09 19:12:45 eggert 137289999Sglebius * Shrink stdio code size; allow cc -R. 138289999Sglebius * 139289999Sglebius * Revision 4.5 87/12/18 11:38:46 narten 140289999Sglebius * Changes from the 43. version. Don't know the significance of the 141289999Sglebius * first change involving "rewind". Also, additional "lint" cleanup. 142289999Sglebius * (Guy Harris) 143289999Sglebius * 144289999Sglebius * Revision 4.4 87/10/18 10:32:21 narten 145289999Sglebius * Updating version numbers. Changes relative to version 1.1 actually 146289999Sglebius * relative to 4.1 147289999Sglebius * 148289999Sglebius * Revision 1.4 87/09/24 13:59:29 narten 149289999Sglebius * Sources now pass through lint (if you ignore printf/sprintf/fprintf 150289999Sglebius * warnings) 151289999Sglebius * 152289999Sglebius * Revision 1.3 87/09/15 16:39:39 shepler 153289999Sglebius * added an initializatin of the variables editline and linecorr 154289999Sglebius * this will be done each time a file is processed. 155289999Sglebius * (there was an obscure bug where if co was used to retrieve multiple files 156289999Sglebius * it would dump) 157289999Sglebius * fix attributed to Roy Morris @FileNet Corp ...!felix!roy 158289999Sglebius * 159289999Sglebius * Revision 1.2 87/03/27 14:22:17 jenkins 160289999Sglebius * Port to suns 161289999Sglebius * 162289999Sglebius * Revision 4.1 83/05/12 13:10:30 wft 163289999Sglebius * Added new markers Id and RCSfile; added locker to Header and Id. 164289999Sglebius * Overhauled expandline completely() (problem with $01234567890123456789@). 165289999Sglebius * Moved trymatch() and marker table to rcskeys.c. 166289999Sglebius * 167289999Sglebius * Revision 3.7 83/05/12 13:04:39 wft 168289999Sglebius * Added retry to expandline to resume after failed match which ended in $. 169289999Sglebius * Fixed truncation problem for $19chars followed by@@. 170289999Sglebius * Log no longer expands full path of RCS file. 171289999Sglebius * 172289999Sglebius * Revision 3.6 83/05/11 16:06:30 wft 173289999Sglebius * added retry to expandline to resume after failed match which ended in $. 174289999Sglebius * Fixed truncation problem for $19chars followed by@@. 175289999Sglebius * 176289999Sglebius * Revision 3.5 82/12/04 13:20:56 wft 177289999Sglebius * Added expansion of keyword Locker. 178289999Sglebius * 179289999Sglebius * Revision 3.4 82/12/03 12:26:54 wft 180289999Sglebius * Added line number correction in case editing does not start at the 181289999Sglebius * beginning of the file. 182289999Sglebius * Changed keyword expansion to always print a space before closing KDELIM; 183289999Sglebius * Expansion for Header shortened. 184289999Sglebius * 185289999Sglebius * Revision 3.3 82/11/14 14:49:30 wft 186289999Sglebius * removed Suffix from keyword expansion. Replaced fclose with ffclose. 187289999Sglebius * keyreplace() gets log message from delta, not from curlogmsg. 188289999Sglebius * fixed expression overflow in while(c=putc(GETC.... 189289999Sglebius * checked nil printing. 190289999Sglebius * 191289999Sglebius * Revision 3.2 82/10/18 21:13:39 wft 192289999Sglebius * I added checks for write errors during the co process, and renamed 193289999Sglebius * expandstring() to xpandstring(). 194289999Sglebius * 195289999Sglebius * Revision 3.1 82/10/13 15:52:55 wft 196289999Sglebius * changed type of result of getc() from char to int. 197289999Sglebius * made keyword expansion loop in expandline() portable to machines 198289999Sglebius * without sign-extension. 199289999Sglebius */ 200289999Sglebius 201289999Sglebius 202289999Sglebius#include "rcsbase.h" 203289999Sglebius 204289999SglebiuslibId(editId, "$FreeBSD$") 205289999Sglebius 206289999Sglebiusstatic void editEndsPrematurely P((void)) exiting; 207289999Sglebiusstatic void editLineNumberOverflow P((void)) exiting; 208289999Sglebiusstatic void escape_string P((FILE*,char const*)); 209289999Sglebiusstatic void keyreplace P((enum markers,struct hshentry const*,int,RILE*,FILE*,int)); 210289999Sglebius 211289999SglebiusFILE *fcopy; /* result file descriptor */ 212289999Sglebiuschar const *resultname; /* result pathname */ 213289999Sglebiusint locker_expansion; /* should the locker name be appended to Id val? */ 214289999Sglebius#if !large_memory 215289999Sglebius static RILE *fedit; /* edit file descriptor */ 216289999Sglebius static char const *editname; /* edit pathname */ 217289999Sglebius#endif 218289999Sglebiusstatic long editline; /* edit line counter; #lines before cursor */ 219289999Sglebiusstatic long linecorr; /* #adds - #deletes in each edit run. */ 220289999Sglebius /*used to correct editline in case file is not rewound after */ 221289999Sglebius /* applying one delta */ 222289999Sglebius 223289999Sglebius/* indexes into dirtpname */ 224289999Sglebius#define lockdirtp_index 0 225289999Sglebius#define newRCSdirtp_index bad_creat0 226289999Sglebius#define newworkdirtp_index (newRCSdirtp_index+1) 227289999Sglebius#define DIRTEMPNAMES (newworkdirtp_index + 1) 228289999Sglebius 229289999Sglebiusenum maker {notmade, real, effective}; 230289999Sglebiusstatic struct buf dirtpname[DIRTEMPNAMES]; /* unlink these when done */ 231289999Sglebiusstatic enum maker volatile dirtpmaker[DIRTEMPNAMES]; /* if these are set */ 232289999Sglebius#define lockname (dirtpname[lockdirtp_index].string) 233289999Sglebius#define newRCSname (dirtpname[newRCSdirtp_index].string) 234289999Sglebius 235289999Sglebius 236275970Scy#if has_NFS || bad_unlink 237275970Scy int 238275970Scyun_link(s) 239275970Scy char const *s; 240275970Scy/* 241275970Scy * Remove S, even if it is unwritable. 242275970Scy * Ignore unlink() ENOENT failures; NFS generates bogus ones. 243275970Scy */ 244275970Scy{ 245275970Scy# if bad_unlink 246275970Scy if (unlink(s) == 0) 247275970Scy return 0; 248275970Scy else { 249289999Sglebius int e = errno; 250275970Scy /* 251275970Scy * Forge ahead even if errno == ENOENT; some completely 252275970Scy * brain-damaged hosts (e.g. PCTCP 2.2) yield ENOENT 253275970Scy * even for existing unwritable files. 254275970Scy */ 255275970Scy if (chmod(s, S_IWUSR) != 0) { 256275970Scy errno = e; 257275970Scy return -1; 258275970Scy } 259289999Sglebius } 260275970Scy# endif 261275970Scy# if has_NFS 262275970Scy return unlink(s)==0 || errno==ENOENT ? 0 : -1; 263289999Sglebius# else 264275970Scy return unlink(s); 265275970Scy# endif 266275970Scy} 267275970Scy#endif 268275970Scy 269275970Scy#if !has_rename 270275970Scy# if !has_NFS 271275970Scy# define do_link(s,t) link(s,t) 272275970Scy# else 273275970Scy static int do_link P((char const*,char const*)); 274275970Scy static int 275275970Scydo_link(s, t) 276275970Scy char const *s, *t; 277275970Scy/* Link S to T, ignoring bogus EEXIST problems due to NFS failures. */ 278275970Scy{ 279289999Sglebius int r = link(s, t); 280275970Scy 281275970Scy if (r != 0 && errno == EEXIST) { 282275970Scy struct stat sb, tb; 283275970Scy if ( 284275970Scy stat(s, &sb) == 0 && 285275970Scy stat(t, &tb) == 0 && 286275970Scy same_file(sb, tb, 0) 287275970Scy ) 288275970Scy r = 0; 289275970Scy errno = EEXIST; 290275970Scy } 291275970Scy return r; 292289999Sglebius} 293275970Scy# endif 294275970Scy#endif 295275970Scy 296289999Sglebius 297275970Scy static void 298275970ScyeditEndsPrematurely() 299275970Scy{ 300289999Sglebius fatserror("edit script ends prematurely"); 301275970Scy} 302275970Scy 303275970Scy static void 304289999SglebiuseditLineNumberOverflow() 305275970Scy{ 306275970Scy fatserror("edit script refers to line past end of file"); 307285612Sdelphij} 308275970Scy 309275970Scy 310275970Scy#if large_memory 311275970Scy 312275970Scy#if has_memmove 313275970Scy# define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type)) 314275970Scy#else 315275970Scy static void movelines P((Iptr_type*,Iptr_type const*,long)); 316275970Scy static void 317275970Scymovelines(s1, s2, n) 318275970Scy register Iptr_type *s1; 319275970Scy register Iptr_type const *s2; 320275970Scy register long n; 321275970Scy{ 322275970Scy if (s1 < s2) 323275970Scy do { 324275970Scy *s1++ = *s2++; 325275970Scy } while (--n); 326275970Scy else { 327275970Scy s1 += n; 328275970Scy s2 += n; 329275970Scy do { 330275970Scy *--s1 = *--s2; 331275970Scy } while (--n); 332275970Scy } 333275970Scy} 334275970Scy#endif 335275970Scy 336275970Scystatic void deletelines P((long,long)); 337275970Scystatic void finisheditline P((RILE*,FILE*,Iptr_type,struct hshentry const*)); 338275970Scystatic void insertline P((long,Iptr_type)); 339275970Scystatic void snapshotline P((FILE*,Iptr_type)); 340275970Scy 341275970Scy/* 342289999Sglebius * `line' contains pointers to the lines in the currently `edited' file. 343280849Scy * It is a 0-origin array that represents linelim-gapsize lines. 344289999Sglebius * line[0 .. gap-1] and line[gap+gapsize .. linelim-1] hold pointers to lines. 345275970Scy * line[gap .. gap+gapsize-1] contains garbage. 346289999Sglebius * 347275970Scy * Any @s in lines are duplicated. 348275970Scy * Lines are terminated by \n, or (for a last partial line only) by single @. 349275970Scy */ 350275970Scystatic Iptr_type *line; 351275970Scystatic size_t gap, gapsize, linelim; 352275970Scy 353275970Scy static void 354275970Scyinsertline(n, l) 355275970Scy long n; 356289999Sglebius Iptr_type l; 357275970Scy/* Before line N, insert line L. N is 0-origin. */ 358289999Sglebius{ 359285612Sdelphij if (linelim-gapsize < n) 360275970Scy editLineNumberOverflow(); 361275970Scy if (!gapsize) 362275970Scy line = 363275970Scy !linelim ? 364275970Scy tnalloc(Iptr_type, linelim = gapsize = 1024) 365289999Sglebius : ( 366275970Scy gap = gapsize = linelim, 367275970Scy trealloc(Iptr_type, line, linelim <<= 1) 368275970Scy ); 369275970Scy if (n < gap) 370275970Scy movelines(line+n+gapsize, line+n, gap-n); 371275970Scy else if (gap < n) 372275970Scy movelines(line+gap, line+gap+gapsize, n-gap); 373275970Scy 374289999Sglebius line[n] = l; 375275970Scy gap = n + 1; 376275970Scy gapsize--; 377275970Scy} 378275970Scy 379275970Scy static void 380275970Scydeletelines(n, nlines) 381275970Scy long n, nlines; 382275970Scy/* Delete lines N through N+NLINES-1. N is 0-origin. */ 383275970Scy{ 384275970Scy long l = n + nlines; 385275970Scy if (linelim-gapsize < l || l < n) 386275970Scy editLineNumberOverflow(); 387275970Scy if (l < gap) 388275970Scy movelines(line+l+gapsize, line+l, gap-l); 389275970Scy else if (gap < n) 390275970Scy movelines(line+gap, line+gap+gapsize, n-gap); 391275970Scy 392275970Scy gap = n; 393275970Scy gapsize += nlines; 394275970Scy} 395275970Scy 396275970Scy static void 397275970Scysnapshotline(f, l) 398310419Sdelphij register FILE *f; 399275970Scy register Iptr_type l; 400275970Scy{ 401275970Scy register int c; 402275970Scy do { 403275970Scy if ((c = *l++) == SDELIM && *l++ != SDELIM) 404275970Scy return; 405275970Scy aputc_(c, f) 406275970Scy } while (c != '\n'); 407275970Scy} 408275970Scy 409275970Scy void 410275970Scysnapshotedit(f) 411275970Scy FILE *f; 412275970Scy/* Copy the current state of the edits to F. */ 413275970Scy{ 414275970Scy register Iptr_type *p, *lim, *l=line; 415275970Scy for (p=l, lim=l+gap; p<lim; ) 416275970Scy snapshotline(f, *p++); 417275970Scy for (p+=gapsize, lim=l+linelim; p<lim; ) 418275970Scy snapshotline(f, *p++); 419275970Scy} 420275970Scy 421275970Scy static void 422275970Scyfinisheditline(fin, fout, l, delta) 423275970Scy RILE *fin; 424275970Scy FILE *fout; 425275970Scy Iptr_type l; 426275970Scy struct hshentry const *delta; 427275970Scy{ 428275970Scy fin->ptr = l; 429275970Scy if (expandline(fin, fout, delta, true, (FILE*)0, true) < 0) 430275970Scy faterror("finisheditline internal error"); 431275970Scy} 432275970Scy 433275970Scy void 434275970Scyfinishedit(delta, outfile, done) 435275970Scy struct hshentry const *delta; 436275970Scy FILE *outfile; 437275970Scy int done; 438275970Scy/* 439275970Scy * Doing expansion if DELTA is set, output the state of the edits to OUTFILE. 440275970Scy * But do nothing unless DONE is set (which means we are on the last pass). 441275970Scy */ 442275970Scy{ 443275970Scy if (done) { 444275970Scy openfcopy(outfile); 445275970Scy outfile = fcopy; 446310419Sdelphij if (!delta) 447275970Scy snapshotedit(outfile); 448275970Scy else { 449275970Scy register Iptr_type *p, *lim, *l = line; 450310419Sdelphij register RILE *fin = finptr; 451275970Scy Iptr_type here = fin->ptr; 452275970Scy for (p=l, lim=l+gap; p<lim; ) 453275970Scy finisheditline(fin, outfile, *p++, delta); 454275970Scy for (p+=gapsize, lim=l+linelim; p<lim; ) 455275970Scy finisheditline(fin, outfile, *p++, delta); 456275970Scy fin->ptr = here; 457275970Scy } 458275970Scy } 459275970Scy} 460275970Scy 461275970Scy/* Open a temporary NAME for output, truncating any previous contents. */ 462275970Scy# define fopen_update_truncate(name) fopenSafer(name, FOPEN_W_WORK) 463275970Scy#else /* !large_memory */ 464275970Scy static FILE * fopen_update_truncate P((char const*)); 465275970Scy static FILE * 466275970Scyfopen_update_truncate(name) 467275970Scy char const *name; 468275970Scy{ 469275970Scy if (bad_fopen_wplus && un_link(name) != 0) 470275970Scy efaterror(name); 471275970Scy return fopenSafer(name, FOPEN_WPLUS_WORK); 472275970Scy} 473275970Scy#endif 474275970Scy 475275970Scy 476275970Scy void 477275970Scyopenfcopy(f) 478275970Scy FILE *f; 479275970Scy{ 480275970Scy if (!(fcopy = f)) { 481275970Scy if (!resultname) 482275970Scy resultname = maketemp(2); 483275970Scy if (!(fcopy = fopen_update_truncate(resultname))) 484275970Scy efaterror(resultname); 485275970Scy } 486275970Scy} 487275970Scy 488275970Scy 489275970Scy#if !large_memory 490275970Scy 491275970Scy static void swapeditfiles P((FILE*)); 492275970Scy static void 493275970Scyswapeditfiles(outfile) 494275970Scy FILE *outfile; 495275970Scy/* Function: swaps resultname and editname, assigns fedit=fcopy, 496275970Scy * and rewinds fedit for reading. Set fcopy to outfile if nonnull; 497275970Scy * otherwise, set fcopy to be resultname opened for reading and writing. 498310419Sdelphij */ 499275970Scy{ 500275970Scy char const *tmpptr; 501275970Scy 502275970Scy editline = 0; linecorr = 0; 503275970Scy Orewind(fcopy); 504275970Scy fedit = fcopy; 505275970Scy tmpptr=editname; editname=resultname; resultname=tmpptr; 506275970Scy openfcopy(outfile); 507275970Scy} 508275970Scy 509275970Scy void 510275970Scysnapshotedit(f) 511285612Sdelphij FILE *f; 512275970Scy/* Copy the current state of the edits to F. */ 513275970Scy{ 514275970Scy finishedit((struct hshentry *)0, (FILE*)0, false); 515275970Scy fastcopy(fedit, f); 516275970Scy Irewind(fedit); 517275970Scy} 518275970Scy 519275970Scy void 520275970Scyfinishedit(delta, outfile, done) 521275970Scy struct hshentry const *delta; 522275970Scy FILE *outfile; 523289999Sglebius int done; 524275970Scy/* copy the rest of the edit file and close it (if it exists). 525275970Scy * if delta, perform keyword substitution at the same time. 526289999Sglebius * If DONE is set, we are finishing the last pass. 527289999Sglebius */ 528275970Scy{ 529289999Sglebius register RILE *fe; 530289999Sglebius register FILE *fc; 531275970Scy 532275970Scy fe = fedit; 533275970Scy if (fe) { 534275970Scy fc = fcopy; 535275970Scy if (delta) { 536289999Sglebius while (1 < expandline(fe,fc,delta,false,(FILE*)0,true)) 537275970Scy ; 538275970Scy } else { 539289999Sglebius fastcopy(fe,fc); 540275970Scy } 541275970Scy Ifclose(fe); 542275970Scy } 543275970Scy if (!done) 544275970Scy swapeditfiles(outfile); 545310419Sdelphij} 546310419Sdelphij#endif 547310419Sdelphij 548310419Sdelphij 549310419Sdelphij 550310419Sdelphij#if large_memory 551310419Sdelphij# define copylines(upto,delta) (editline = (upto)) 552310419Sdelphij#else 553310419Sdelphij static void copylines P((long,struct hshentry const*)); 554310419Sdelphij static void 555310419Sdelphijcopylines(upto, delta) 556310419Sdelphij register long upto; 557310419Sdelphij struct hshentry const *delta; 558310419Sdelphij/* 559310419Sdelphij * Copy input lines editline+1..upto from fedit to fcopy. 560310419Sdelphij * If delta, keyword expansion is done simultaneously. 561310419Sdelphij * editline is updated. Rewinds a file only if necessary. 562310419Sdelphij */ 563310419Sdelphij{ 564310419Sdelphij register int c; 565310419Sdelphij declarecache; 566310419Sdelphij register FILE *fc; 567310419Sdelphij register RILE *fe; 568310419Sdelphij 569310419Sdelphij if (upto < editline) { 570310419Sdelphij /* swap files */ 571310419Sdelphij finishedit((struct hshentry *)0, (FILE*)0, false); 572275970Scy /* assumes edit only during last pass, from the beginning*/ 573310419Sdelphij } 574275970Scy fe = fedit; 575275970Scy fc = fcopy; 576275970Scy if (editline < upto) 577275970Scy if (delta) 578275970Scy do { 579275970Scy if (expandline(fe,fc,delta,false,(FILE*)0,true) <= 1) 580275970Scy editLineNumberOverflow(); 581275970Scy } while (++editline < upto); 582275970Scy else { 583310419Sdelphij setupcache(fe); cache(fe); 584275970Scy do { 585275970Scy do { 586275970Scy cachegeteof_(c, editLineNumberOverflow();) 587275970Scy aputc_(c, fc) 588275970Scy } while (c != '\n'); 589275970Scy } while (++editline < upto); 590275970Scy uncache(fe); 591275970Scy } 592275970Scy} 593289999Sglebius#endif 594275970Scy 595285612Sdelphij 596275970Scy 597285612Sdelphij void 598310419Sdelphijxpandstring(delta) 599275970Scy struct hshentry const *delta; 600275970Scy/* Function: Reads a string terminated by SDELIM from finptr and writes it 601275970Scy * to fcopy. Double SDELIM is replaced with single SDELIM. 602275970Scy * Keyword expansion is performed with data from delta. 603289999Sglebius * If foutptr is nonnull, the string is also copied unchanged to foutptr. 604285612Sdelphij */ 605275970Scy{ 606285612Sdelphij while (1 < expandline(finptr,fcopy,delta,true,foutptr,true)) 607285612Sdelphij continue; 608275970Scy} 609285612Sdelphij 610275970Scy 611310419Sdelphij void 612275970Scycopystring() 613275970Scy/* Function: copies a string terminated with a single SDELIM from finptr to 614275970Scy * fcopy, replacing all double SDELIM with a single SDELIM. 615275970Scy * If foutptr is nonnull, the string also copied unchanged to foutptr. 616289999Sglebius * editline is incremented by the number of lines copied. 617275970Scy * Assumption: next character read is first string character. 618275970Scy */ 619275970Scy{ register c; 620275970Scy declarecache; 621275970Scy register FILE *frew, *fcop; 622310419Sdelphij register int amidline; 623275970Scy register RILE *fin; 624275970Scy 625275970Scy fin = finptr; 626275970Scy setupcache(fin); cache(fin); 627275970Scy frew = foutptr; 628275970Scy fcop = fcopy; 629275970Scy amidline = false; 630275970Scy for (;;) { 631275970Scy GETC_(frew,c) 632310419Sdelphij switch (c) { 633275970Scy case '\n': 634275970Scy ++editline; 635275970Scy ++rcsline; 636275970Scy amidline = false; 637275970Scy break; 638275970Scy case SDELIM: 639275970Scy GETC_(frew,c) 640275970Scy if (c != SDELIM) { 641275970Scy /* end of string */ 642289999Sglebius nextc = c; 643275970Scy editline += amidline; 644275970Scy uncache(fin); 645275970Scy return; 646275970Scy } 647310419Sdelphij /* fall into */ 648275970Scy default: 649275970Scy amidline = true; 650275970Scy break; 651275970Scy } 652289999Sglebius aputc_(c,fcop) 653285612Sdelphij } 654275970Scy} 655285612Sdelphij 656275970Scy 657275970Scy void 658275970Scyenterstring() 659275970Scy/* Like copystring, except the string is put into the edit data structure. */ 660275970Scy{ 661275970Scy#if !large_memory 662275970Scy editname = 0; 663275970Scy fedit = 0; 664275970Scy editline = linecorr = 0; 665289999Sglebius resultname = maketemp(1); 666275970Scy if (!(fcopy = fopen_update_truncate(resultname))) 667275970Scy efaterror(resultname); 668275970Scy copystring(); 669275970Scy#else 670275970Scy register int c; 671275970Scy declarecache; 672310419Sdelphij register FILE *frew; 673275970Scy register long e, oe; 674275970Scy register int amidline, oamidline; 675275970Scy register Iptr_type optr; 676310419Sdelphij register RILE *fin; 677275970Scy 678275970Scy e = 0; 679275970Scy gap = 0; 680310419Sdelphij gapsize = linelim; 681275970Scy fin = finptr; 682275970Scy setupcache(fin); cache(fin); 683275970Scy advise_access(fin, MADV_NORMAL); 684275970Scy frew = foutptr; 685310419Sdelphij amidline = false; 686275970Scy for (;;) { 687275970Scy optr = cacheptr(); 688275970Scy GETC_(frew,c) 689275970Scy oamidline = amidline; 690275970Scy oe = e; 691275970Scy switch (c) { 692275970Scy case '\n': 693289999Sglebius ++e; 694275970Scy ++rcsline; 695289999Sglebius amidline = false; 696289999Sglebius break; 697289999Sglebius case SDELIM: 698289999Sglebius GETC_(frew,c) 699289999Sglebius if (c != SDELIM) { 700289999Sglebius /* end of string */ 701289999Sglebius nextc = c; 702289999Sglebius editline = e + amidline; 703289999Sglebius linecorr = 0; 704289999Sglebius uncache(fin); 705289999Sglebius return; 706275970Scy } 707289999Sglebius /* fall into */ 708275970Scy default: 709289999Sglebius amidline = true; 710275970Scy break; 711289999Sglebius } 712289999Sglebius if (!oamidline) 713275970Scy insertline(oe, optr); 714289999Sglebius } 715289999Sglebius#endif 716289999Sglebius} 717275970Scy 718289999Sglebius 719289999Sglebius 720289999Sglebius 721289999Sglebius void 722289999Sglebius#if large_memory 723289999Sglebiusedit_string() 724289999Sglebius#else 725289999Sglebius editstring(delta) 726289999Sglebius struct hshentry const *delta; 727289999Sglebius#endif 728289999Sglebius/* 729289999Sglebius * Read an edit script from finptr and applies it to the edit file. 730289999Sglebius#if !large_memory 731285612Sdelphij * The result is written to fcopy. 732289999Sglebius * If delta, keyword expansion is performed simultaneously. 733289999Sglebius * If running out of lines in fedit, fedit and fcopy are swapped. 734289999Sglebius * editname is the name of the file that goes with fedit. 735289999Sglebius#endif 736289999Sglebius * If foutptr is set, the edit script is also copied verbatim to foutptr. 737289999Sglebius * Assumes that all these files are open. 738289999Sglebius * resultname is the name of the file that goes with fcopy. 739289999Sglebius * Assumes the next input character from finptr is the first character of 740289999Sglebius * the edit script. Resets nextc on exit. 741275970Scy */ 742289999Sglebius{ 743289999Sglebius int ed; /* editor command */ 744275970Scy register int c; 745289999Sglebius declarecache; 746289999Sglebius register FILE *frew; 747275970Scy# if !large_memory 748289999Sglebius register FILE *f; 749289999Sglebius long line_lim = LONG_MAX; 750289999Sglebius register RILE *fe; 751275970Scy# endif 752289999Sglebius register long i; 753289999Sglebius register RILE *fin; 754289999Sglebius# if large_memory 755289999Sglebius register long j; 756289999Sglebius# endif 757289999Sglebius struct diffcmd dc; 758289999Sglebius 759289999Sglebius editline += linecorr; linecorr=0; /*correct line number*/ 760289999Sglebius frew = foutptr; 761275970Scy fin = finptr; 762275970Scy setupcache(fin); 763275970Scy initdiffcmd(&dc); 764275970Scy while (0 <= (ed = getdiffcmd(fin,true,frew,&dc))) 765275970Scy#if !large_memory 766310419Sdelphij if (line_lim <= dc.line1) 767275970Scy editLineNumberOverflow(); 768275970Scy else 769275970Scy#endif 770310419Sdelphij if (!ed) { 771275970Scy copylines(dc.line1-1, delta); 772275970Scy /* skip over unwanted lines */ 773275970Scy i = dc.nlines; 774275970Scy linecorr -= i; 775275970Scy editline += i; 776275970Scy# if large_memory 777275970Scy deletelines(editline+linecorr, i); 778289999Sglebius# else 779289999Sglebius fe = fedit; 780289999Sglebius do { 781289999Sglebius /*skip next line*/ 782289999Sglebius do { 783275970Scy Igeteof_(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } ) 784289999Sglebius } while (c != '\n'); 785289999Sglebius } while (--i); 786275970Scy# endif 787289999Sglebius } else { 788289999Sglebius /* Copy lines without deleting any. */ 789289999Sglebius copylines(dc.line1, delta); 790275970Scy i = dc.nlines; 791289999Sglebius# if large_memory 792289999Sglebius j = editline+linecorr; 793289999Sglebius# endif 794289999Sglebius linecorr += i; 795289999Sglebius#if !large_memory 796289999Sglebius f = fcopy; 797289999Sglebius if (delta) 798289999Sglebius do { 799289999Sglebius switch (expandline(fin,f,delta,true,frew,true)){ 800275970Scy case 0: case 1: 801275970Scy if (i==1) 802275970Scy return; 803310419Sdelphij /* fall into */ 804275970Scy case -1: 805275970Scy editEndsPrematurely(); 806275970Scy } 807275970Scy } while (--i); 808275970Scy else 809275970Scy#endif 810275970Scy { 811275970Scy cache(fin); 812275970Scy do { 813275970Scy# if large_memory 814275970Scy insertline(j++, cacheptr()); 815275970Scy# endif 816275970Scy for (;;) { 817275970Scy GETC_(frew, c) 818289999Sglebius if (c==SDELIM) { 819289999Sglebius GETC_(frew, c) 820289999Sglebius if (c!=SDELIM) { 821289999Sglebius if (--i) 822289999Sglebius editEndsPrematurely(); 823289999Sglebius nextc = c; 824289999Sglebius uncache(fin); 825275970Scy return; 826289999Sglebius } 827289999Sglebius } 828285612Sdelphij# if !large_memory 829289999Sglebius aputc_(c, f) 830289999Sglebius# endif 831289999Sglebius if (c == '\n') 832289999Sglebius break; 833289999Sglebius } 834289999Sglebius ++rcsline; 835289999Sglebius } while (--i); 836289999Sglebius uncache(fin); 837289999Sglebius } 838289999Sglebius } 839289999Sglebius} 840289999Sglebius 841289999Sglebius 842285612Sdelphij 843289999Sglebius/* The rest is for keyword expansion */ 844289999Sglebius 845289999Sglebius 846289999Sglebius 847289999Sglebius int 848289999Sglebiusexpandline(infile, outfile, delta, delimstuffed, frewfile, dolog) 849289999Sglebius RILE *infile; 850289999Sglebius FILE *outfile, *frewfile; 851289999Sglebius struct hshentry const *delta; 852275970Scy int delimstuffed, dolog; 853289999Sglebius/* 854289999Sglebius * Read a line from INFILE and write it to OUTFILE. 855289999Sglebius * Do keyword expansion with data from DELTA. 856289999Sglebius * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM. 857275970Scy * If FREWFILE is set, copy the line unchanged to FREWFILE. 858275970Scy * DELIMSTUFFED must be true if FREWFILE is set. 859275970Scy * Append revision history to log only if DOLOG is set. 860275970Scy * Yields -1 if no data is copied, 0 if an incomplete line is copied, 861275970Scy * 2 if a complete line is copied; adds 1 to yield if expansion occurred. 862275970Scy */ 863275970Scy{ 864275970Scy register c; 865275970Scy declarecache; 866275970Scy register FILE *out, *frew; 867275970Scy register char * tp; 868275970Scy register int e, ds, r; 869275970Scy char const *tlim; 870275970Scy static struct buf keyval; 871275970Scy enum markers matchresult; 872275970Scy 873275970Scy setupcache(infile); cache(infile); 874275970Scy out = outfile; 875275970Scy frew = frewfile; 876275970Scy ds = delimstuffed; 877275970Scy bufalloc(&keyval, keylength+3); 878275970Scy e = 0; 879275970Scy r = -1; 880275970Scy 881275970Scy for (;;) { 882275970Scy if (ds) 883275970Scy GETC_(frew, c) 884275970Scy else 885275970Scy cachegeteof_(c, goto uncache_exit;) 886275970Scy for (;;) { 887275970Scy switch (c) { 888275970Scy case SDELIM: 889275970Scy if (ds) { 890275970Scy GETC_(frew, c) 891275970Scy if (c != SDELIM) { 892275970Scy /* end of string */ 893275970Scy nextc=c; 894275970Scy goto uncache_exit; 895275970Scy } 896275970Scy } 897275970Scy /* fall into */ 898275970Scy default: 899275970Scy aputc_(c,out) 900275970Scy r = 0; 901275970Scy break; 902275970Scy 903275970Scy case '\n': 904275970Scy rcsline += ds; 905275970Scy aputc_(c,out) 906289999Sglebius r = 2; 907289999Sglebius goto uncache_exit; 908275970Scy 909285612Sdelphij case KDELIM: 910285612Sdelphij r = 0; 911285612Sdelphij /* check for keyword */ 912285612Sdelphij /* first, copy a long enough string into keystring */ 913285612Sdelphij tp = keyval.string; 914285612Sdelphij *tp++ = KDELIM; 915285612Sdelphij for (;;) { 916289999Sglebius if (ds) 917289999Sglebius GETC_(frew, c) 918289999Sglebius else 919275970Scy cachegeteof_(c, goto keystring_eof;) 920289999Sglebius if (tp <= &keyval.string[keylength]) 921289999Sglebius switch (ctab[c]) { 922289999Sglebius case LETTER: case Letter: 923289999Sglebius *tp++ = c; 924289999Sglebius continue; 925289999Sglebius default: 926289999Sglebius break; 927289999Sglebius } 928289999Sglebius break; 929289999Sglebius } 930275970Scy *tp++ = c; *tp = '\0'; 931275970Scy matchresult = trymatch(keyval.string+1); 932275970Scy if (matchresult==Nomatch) { 933289999Sglebius tp[-1] = 0; 934275970Scy aputs(keyval.string, out); 935275970Scy continue; /* last c handled properly */ 936275970Scy } 937289999Sglebius 938275970Scy /* Now we have a keyword terminated with a K/VDELIM */ 939275970Scy if (c==VDELIM) { 940275970Scy /* try to find closing KDELIM, and replace value */ 941275970Scy tlim = keyval.string + keyval.size; 942275970Scy for (;;) { 943275970Scy if (ds) 944275970Scy GETC_(frew, c) 945275970Scy else 946275970Scy cachegeteof_(c, goto keystring_eof;) 947275970Scy if (c=='\n' || c==KDELIM) 948275970Scy break; 949275970Scy *tp++ =c; 950275970Scy if (tlim <= tp) 951275970Scy tp = bufenlarge(&keyval, &tlim); 952289999Sglebius if (c==SDELIM && ds) { /*skip next SDELIM */ 953275970Scy GETC_(frew, c) 954275970Scy if (c != SDELIM) { 955289999Sglebius /* end of string before closing KDELIM or newline */ 956275970Scy nextc = c; 957289999Sglebius goto keystring_eof; 958275970Scy } 959275970Scy } 960289999Sglebius } 961275970Scy if (c!=KDELIM) { 962275970Scy /* couldn't find closing KDELIM -- give up */ 963275970Scy *tp = 0; 964275970Scy aputs(keyval.string, out); 965289999Sglebius continue; /* last c handled properly */ 966275970Scy } 967275970Scy } 968275970Scy /* now put out the new keyword value */ 969289999Sglebius uncache(infile); 970275970Scy keyreplace(matchresult, delta, ds, infile, out, dolog); 971275970Scy cache(infile); 972275970Scy e = 1; 973275970Scy break; 974275970Scy } 975275970Scy break; 976275970Scy } 977275970Scy } 978275970Scy 979275970Scy keystring_eof: 980275970Scy *tp = 0; 981275970Scy aputs(keyval.string, out); 982275970Scy uncache_exit: 983275970Scy uncache(infile); 984275970Scy return r + e; 985275970Scy} 986285612Sdelphij 987275970Scy 988275970Scy static void 989275970Scyescape_string(out, s) 990275970Scy register FILE *out; 991275970Scy register char const *s; 992275970Scy/* Output to OUT the string S, escaping chars that would break `ci -k'. */ 993275970Scy{ 994275970Scy register char c; 995275970Scy for (;;) 996275970Scy switch ((c = *s++)) { 997275970Scy case 0: return; 998275970Scy case '\t': aputs("\\t", out); break; 999275970Scy case '\n': aputs("\\n", out); break; 1000275970Scy case ' ': aputs("\\040", out); break; 1001275970Scy case KDELIM: aputs("\\044", out); break; 1002275970Scy case '\\': if (VERSION(5)<=RCSversion) {aputs("\\\\", out); break;} 1003275970Scy /* fall into */ 1004275970Scy default: aputc_(c, out) break; 1005275970Scy } 1006275970Scy} 1007275970Scy 1008275970Scychar const ciklog[ciklogsize] = "checked in with -k by "; 1009285612Sdelphij 1010275970Scy static void 1011275970Scykeyreplace(marker, delta, delimstuffed, infile, out, dolog) 1012275970Scy enum markers marker; 1013275970Scy register struct hshentry const *delta; 1014275970Scy int delimstuffed; 1015275970Scy RILE *infile; 1016275970Scy register FILE *out; 1017275970Scy int dolog; 1018275970Scy/* function: outputs the keyword value(s) corresponding to marker. 1019275970Scy * Attributes are derived from delta. 1020275970Scy */ 1021275970Scy{ 1022275970Scy register char const *sp, *cp, *date; 1023275970Scy register int c; 1024275970Scy register size_t cs, cw, ls; 1025275970Scy char const *sp1; 1026275970Scy char datebuf[datesize + zonelenmax]; 1027275970Scy int RCSv; 1028275970Scy int exp; 1029275970Scy 1030275970Scy sp = Keyword[(int)marker]; 1031275970Scy exp = Expand; 1032275970Scy date = delta->date; 1033275970Scy RCSv = RCSversion; 1034275970Scy 1035275970Scy if (exp != VAL_EXPAND) 1036275970Scy aprintf(out, "%c%s", KDELIM, sp); 1037275970Scy if (exp != KEY_EXPAND) { 1038275970Scy 1039275970Scy if (exp != VAL_EXPAND) 1040275970Scy aprintf(out, "%c%c", VDELIM, 1041275970Scy marker==Log && RCSv<VERSION(5) ? '\t' : ' ' 1042275970Scy ); 1043275970Scy 1044275970Scy switch (marker) { 1045275970Scy case Author: 1046275970Scy aputs(delta->author, out); 1047275970Scy break; 1048275970Scy case Date: 1049275970Scy aputs(date2str(date,datebuf), out); 1050275970Scy break; 1051275970Scy case Id: 1052275970Scy case LocalId: 1053275970Scy case Header: 1054275970Scy case CVSHeader: 1055275970Scy if (marker == Id || RCSv < VERSION(4) || 1056275970Scy (marker == LocalId && LocalIdMode == Id)) 1057275970Scy escape_string(out, basefilename(RCSname)); 1058275970Scy else if (marker == CVSHeader || 1059275970Scy (marker == LocalId && LocalIdMode == CVSHeader)) 1060275970Scy escape_string(out, getfullCVSname()); 1061275970Scy else 1062275970Scy escape_string(out, getfullRCSname()); 1063275970Scy aprintf(out, " %s %s %s %s", 1064275970Scy delta->num, 1065275970Scy date2str(date, datebuf), 1066275970Scy delta->author, 1067275970Scy RCSv==VERSION(3) && delta->lockedby ? "Locked" 1068275970Scy : delta->state 1069275970Scy ); 1070275970Scy if (delta->lockedby) 1071275970Scy if (VERSION(5) <= RCSv) { 1072275970Scy if (locker_expansion || exp==KEYVALLOCK_EXPAND) 1073275970Scy aprintf(out, " %s", delta->lockedby); 1074275970Scy } else if (RCSv == VERSION(4)) 1075275970Scy aprintf(out, " Locker: %s", delta->lockedby); 1076275970Scy break; 1077275970Scy case Locker: 1078275970Scy if (delta->lockedby) 1079275970Scy if ( 1080275970Scy locker_expansion 1081275970Scy || exp == KEYVALLOCK_EXPAND 1082275970Scy || RCSv <= VERSION(4) 1083310419Sdelphij ) 1084275970Scy aputs(delta->lockedby, out); 1085275970Scy break; 1086275970Scy case Log: 1087310419Sdelphij case RCSfile: 1088275970Scy escape_string(out, basefilename(RCSname)); 1089275970Scy break; 1090275970Scy case Name: 1091275970Scy if (delta->name) 1092275970Scy aputs(delta->name, out); 1093275970Scy break; 1094275970Scy case Revision: 1095275970Scy aputs(delta->num, out); 1096275970Scy break; 1097275970Scy case Source: 1098275970Scy escape_string(out, getfullRCSname()); 1099275970Scy break; 1100275970Scy case State: 1101275970Scy aputs(delta->state, out); 1102275970Scy break; 1103275970Scy default: 1104289999Sglebius break; 1105275970Scy } 1106275970Scy if (exp != VAL_EXPAND) 1107275970Scy afputc(' ', out); 1108275970Scy } 1109275970Scy if (exp != VAL_EXPAND) 1110289999Sglebius afputc(KDELIM, out); 1111275970Scy 1112275970Scy if (marker == Log && dolog) { 1113275970Scy struct buf leader; 1114275970Scy 1115275970Scy sp = delta->log.string; 1116275970Scy ls = delta->log.size; 1117275970Scy if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1)) 1118275970Scy return; 1119275970Scy bufautobegin(&leader); 1120275970Scy if (RCSversion < VERSION(5)) { 1121275970Scy cp = Comment.string; 1122275970Scy cs = Comment.size; 1123275970Scy } else { 1124275970Scy int kdelim_found = 0; 1125275970Scy Ioffset_type chars_read = Itell(infile); 1126275970Scy declarecache; 1127275970Scy setupcache(infile); cache(infile); 1128275970Scy 1129275970Scy c = 0; /* Pacify `gcc -Wall'. */ 1130275970Scy 1131275970Scy /* 1132275970Scy * Back up to the start of the current input line, 1133275970Scy * setting CS to the number of characters before `$Log'. 1134275970Scy */ 1135275970Scy cs = 0; 1136275970Scy for (;;) { 1137275970Scy if (!--chars_read) 1138285612Sdelphij goto done_backing_up; 1139275970Scy cacheunget_(infile, c) 1140275970Scy if (c == '\n') 1141275970Scy break; 1142275970Scy if (c == SDELIM && delimstuffed) { 1143275970Scy if (!--chars_read) 1144275970Scy break; 1145275970Scy cacheunget_(infile, c) 1146275970Scy if (c != SDELIM) { 1147275970Scy cacheget_(c) 1148275970Scy break; 1149289999Sglebius } 1150275970Scy } 1151275970Scy cs += kdelim_found; 1152275970Scy kdelim_found |= c==KDELIM; 1153275970Scy } 1154275970Scy cacheget_(c) 1155275970Scy done_backing_up:; 1156289999Sglebius 1157275970Scy /* Copy characters before `$Log' into LEADER. */ 1158275970Scy bufalloc(&leader, cs); 1159275970Scy cp = leader.string; 1160289999Sglebius for (cw = 0; cw < cs; cw++) { 1161275970Scy leader.string[cw] = c; 1162275970Scy if (c == SDELIM && delimstuffed) 1163275970Scy cacheget_(c) 1164289999Sglebius cacheget_(c) 1165289999Sglebius } 1166289999Sglebius 1167289999Sglebius /* Convert traditional C or Pascal leader to ` *'. */ 1168289999Sglebius for (cw = 0; cw < cs; cw++) 1169275970Scy if (ctab[(unsigned char) cp[cw]] != SPACE) 1170289999Sglebius break; 1171275970Scy if ( 1172289999Sglebius cw+1 < cs 1173289999Sglebius && cp[cw+1] == '*' 1174289999Sglebius && (cp[cw] == '/' || cp[cw] == '(') 1175289999Sglebius ) { 1176289999Sglebius size_t i = cw+1; 1177289999Sglebius for (;;) 1178289999Sglebius if (++i == cs) { 1179289999Sglebius warn( 1180289999Sglebius "`%c* $Log' is obsolescent; use ` * $Log'.", 1181289999Sglebius cp[cw] 1182289999Sglebius ); 1183289999Sglebius leader.string[cw] = ' '; 1184289999Sglebius break; 1185275970Scy } else if (ctab[(unsigned char) cp[i]] != SPACE) 1186289999Sglebius break; 1187275970Scy } 1188275970Scy 1189275970Scy /* Skip `$Log ... $' string. */ 1190275970Scy do { 1191289999Sglebius cacheget_(c) 1192289999Sglebius } while (c != KDELIM); 1193289999Sglebius uncache(infile); 1194289999Sglebius } 1195289999Sglebius afputc('\n', out); 1196289999Sglebius awrite(cp, cs, out); 1197289999Sglebius sp1 = date2str(date, datebuf); 1198289999Sglebius if (VERSION(5) <= RCSv) { 1199289999Sglebius aprintf(out, "Revision %s %s %s", 1200289999Sglebius delta->num, sp1, delta->author 1201289999Sglebius ); 1202289999Sglebius } else { 1203289999Sglebius /* oddity: 2 spaces between date and time, not 1 as usual */ 1204275970Scy sp1 = strchr(sp1, ' '); 1205275970Scy aprintf(out, "Revision %s %.*s %s %s", 1206275970Scy delta->num, (int)(sp1-datebuf), datebuf, sp1, 1207275970Scy delta->author 1208275970Scy ); 1209275970Scy } 1210275970Scy /* Do not include state: it may change and is not updated. */ 1211275970Scy cw = cs; 1212275970Scy if (VERSION(5) <= RCSv) 1213275970Scy for (; cw && (cp[cw-1]==' ' || cp[cw-1]=='\t'); --cw) 1214275970Scy continue; 1215275970Scy for (;;) { 1216275970Scy afputc('\n', out); 1217275970Scy awrite(cp, cw, out); 1218275970Scy if (!ls) 1219275970Scy break; 1220275970Scy --ls; 1221275970Scy c = *sp++; 1222275970Scy if (c != '\n') { 1223275970Scy awrite(cp+cw, cs-cw, out); 1224275970Scy do { 1225289999Sglebius afputc(c,out); 1226289999Sglebius if (!ls) 1227289999Sglebius break; 1228289999Sglebius --ls; 1229289999Sglebius c = *sp++; 1230289999Sglebius } while (c != '\n'); 1231289999Sglebius } 1232275970Scy } 1233289999Sglebius bufautoend(&leader); 1234289999Sglebius } 1235289999Sglebius} 1236289999Sglebius 1237289999Sglebius#if has_readlink 1238289999Sglebius static int resolve_symlink P((struct buf*)); 1239275970Scy static int 1240289999Sglebiusresolve_symlink(L) 1241275970Scy struct buf *L; 1242275970Scy/* 1243275970Scy * If L is a symbolic link, resolve it to the name that it points to. 1244275970Scy * If unsuccessful, set errno and yield -1. 1245275970Scy * If it points to an existing file, yield 1. 1246275970Scy * Otherwise, set errno=ENOENT and yield 0. 1247275970Scy */ 1248275970Scy{ 1249275970Scy char *b, a[SIZEABLE_PATH]; 1250275970Scy int e; 1251275970Scy size_t s; 1252275970Scy ssize_t r; 1253275970Scy struct buf bigbuf; 1254275970Scy int linkcount = MAXSYMLINKS; 1255275970Scy 1256275970Scy b = a; 1257275970Scy s = sizeof(a); 1258275970Scy bufautobegin(&bigbuf); 1259275970Scy while ((r = readlink(L->string,b,s)) != -1) 1260275970Scy if (r == s) { 1261275970Scy bufalloc(&bigbuf, s<<1); 1262275970Scy b = bigbuf.string; 1263275970Scy s = bigbuf.size; 1264275970Scy } else if (!linkcount--) { 1265275970Scy# ifndef ELOOP 1266275970Scy /* 1267275970Scy * Some pedantic Posix 1003.1-1990 hosts have readlink 1268275970Scy * but not ELOOP. Approximate ELOOP with EMLINK. 1269275970Scy */ 1270275970Scy# define ELOOP EMLINK 1271275970Scy# endif 1272275970Scy errno = ELOOP; 1273275970Scy return -1; 1274275970Scy } else { 1275275970Scy /* Splice symbolic link into L. */ 1276275970Scy b[r] = '\0'; 1277275970Scy L->string[ 1278275970Scy ROOTPATH(b) ? 0 : basefilename(L->string) - L->string 1279275970Scy ] = '\0'; 1280275970Scy bufscat(L, b); 1281310419Sdelphij } 1282310419Sdelphij e = errno; 1283275970Scy bufautoend(&bigbuf); 1284275970Scy errno = e; 1285275970Scy switch (e) { 1286275970Scy case readlink_isreg_errno: return 1; 1287275970Scy case ENOENT: return 0; 1288275970Scy default: return -1; 1289275970Scy } 1290275970Scy} 1291275970Scy#endif 1292275970Scy 1293275970Scy RILE * 1294275970Scyrcswriteopen(RCSbuf, status, mustread) 1295275970Scy struct buf *RCSbuf; 1296275970Scy struct stat *status; 1297275970Scy int mustread; 1298275970Scy/* 1299275970Scy * Create the lock file corresponding to RCSBUF. 1300275970Scy * Then try to open RCSBUF for reading and yield its RILE* descriptor. 1301275970Scy * Put its status into *STATUS too. 1302275970Scy * MUSTREAD is true if the file must already exist, too. 1303275970Scy * If all goes well, discard any previously acquired locks, 1304275970Scy * and set fdlock to the file descriptor of the RCS lockfile. 1305275970Scy */ 1306275970Scy{ 1307275970Scy register char *tp; 1308275970Scy register char const *sp, *RCSpath, *x; 1309275970Scy RILE *f; 1310275970Scy size_t l; 1311275970Scy int e, exists, fdesc, fdescSafer, r, waslocked; 1312275970Scy struct buf *dirt; 1313275970Scy struct stat statbuf; 1314275970Scy 1315275970Scy waslocked = 0 <= fdlock; 1316275970Scy exists = 1317275970Scy# if has_readlink 1318275970Scy resolve_symlink(RCSbuf); 1319275970Scy# else 1320275970Scy stat(RCSbuf->string, &statbuf) == 0 ? 1 1321275970Scy : errno==ENOENT ? 0 : -1; 1322275970Scy# endif 1323275970Scy if (exists < (mustread|waslocked)) 1324275970Scy /* 1325275970Scy * There's an unusual problem with the RCS file; 1326275970Scy * or the RCS file doesn't exist, 1327275970Scy * and we must read or we already have a lock elsewhere. 1328275970Scy */ 1329275970Scy return 0; 1330275970Scy 1331275970Scy RCSpath = RCSbuf->string; 1332275970Scy sp = basefilename(RCSpath); 1333275970Scy l = sp - RCSpath; 1334275970Scy dirt = &dirtpname[waslocked]; 1335275970Scy bufscpy(dirt, RCSpath); 1336275970Scy tp = dirt->string + l; 1337275970Scy x = rcssuffix(RCSpath); 1338275970Scy# if has_readlink 1339275970Scy if (!x) { 1340275970Scy error("symbolic link to non RCS file `%s'", RCSpath); 1341275970Scy errno = EINVAL; 1342275970Scy return 0; 1343275970Scy } 1344275970Scy# endif 1345275970Scy if (*sp == *x) { 1346275970Scy error("RCS pathname `%s' incompatible with suffix `%s'", sp, x); 1347275970Scy errno = EINVAL; 1348275970Scy return 0; 1349275970Scy } 1350275970Scy /* Create a lock filename that is a function of the RCS filename. */ 1351275970Scy if (*x) { 1352275970Scy /* 1353275970Scy * The suffix is nonempty. 1354275970Scy * The lock filename is the first char of of the suffix, 1355275970Scy * followed by the RCS filename with last char removed. E.g.: 1356275970Scy * foo,v RCS filename with suffix ,v 1357275970Scy * ,foo, lock filename 1358275970Scy */ 1359275970Scy *tp++ = *x; 1360275970Scy while (*sp) 1361275970Scy *tp++ = *sp++; 1362275970Scy *--tp = 0; 1363275970Scy } else { 1364275970Scy /* 1365275970Scy * The suffix is empty. 1366275970Scy * The lock filename is the RCS filename 1367275970Scy * with last char replaced by '_'. 1368275970Scy */ 1369275970Scy while ((*tp++ = *sp++)) 1370275970Scy continue; 1371275970Scy tp -= 2; 1372275970Scy if (*tp == '_') { 1373275970Scy error("RCS pathname `%s' ends with `%c'", RCSpath, *tp); 1374275970Scy errno = EINVAL; 1375275970Scy return 0; 1376275970Scy } 1377275970Scy *tp = '_'; 1378275970Scy } 1379275970Scy 1380275970Scy sp = dirt->string; 1381275970Scy 1382275970Scy f = 0; 1383275970Scy 1384275970Scy /* 1385275970Scy * good news: 1386275970Scy * open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_READONLY) 1387275970Scy * is atomic according to Posix 1003.1-1990. 1388275970Scy * bad news: 1389275970Scy * NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990. 1390275970Scy * good news: 1391275970Scy * (O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity 1392275970Scy * even with NFS. 1393275970Scy * bad news: 1394275970Scy * If you're root, (O_TRUNC,OPEN_CREAT_READONLY) doesn't 1395275970Scy * guarantee atomicity. 1396275970Scy * good news: 1397275970Scy * Root-over-the-wire NFS access is rare for security reasons. 1398275970Scy * This bug has never been reported in practice with RCS. 1399275970Scy * So we don't worry about this bug. 1400275970Scy * 1401275970Scy * An even rarer NFS bug can occur when clients retry requests. 1402275970Scy * This can happen in the usual case of NFS over UDP. 1403275970Scy * Suppose client A releases a lock by renaming ",f," to "f,v" at 1404275970Scy * about the same time that client B obtains a lock by creating ",f,", 1405275970Scy * and suppose A's first rename request is delayed, so A reissues it. 1406275970Scy * The sequence of events might be: 1407275970Scy * A sends rename(",f,", "f,v") 1408275970Scy * B sends create(",f,") 1409275970Scy * A sends retry of rename(",f,", "f,v") 1410275970Scy * server receives, does, and acknowledges A's first rename() 1411275970Scy * A receives acknowledgment, and its RCS program exits 1412275970Scy * server receives, does, and acknowledges B's create() 1413275970Scy * server receives, does, and acknowledges A's retry of rename() 1414275970Scy * This not only wrongly deletes B's lock, it removes the RCS file! 1415275970Scy * Most NFS implementations have idempotency caches that usually prevent 1416275970Scy * this scenario, but such caches are finite and can be overrun. 1417275970Scy * This problem afflicts not only RCS, which uses open() and rename() 1418275970Scy * to get and release locks; it also afflicts the traditional 1419275970Scy * Unix method of using link() and unlink() to get and release locks, 1420275970Scy * and the less traditional method of using mkdir() and rmdir(). 1421275970Scy * There is no easy workaround. 1422275970Scy * Any new method based on lockf() seemingly would be incompatible with 1423275970Scy * the old methods; besides, lockf() is notoriously buggy under NFS. 1424275970Scy * Since this problem afflicts scads of Unix programs, but is so rare 1425275970Scy * that nobody seems to be worried about it, we won't worry either. 1426275970Scy */ 1427275970Scy# if !open_can_creat 1428275970Scy# define create(f) creat(f, OPEN_CREAT_READONLY) 1429275970Scy# else 1430275970Scy# define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_READONLY) 1431275970Scy# endif 1432275970Scy 1433275970Scy catchints(); 1434275970Scy ignoreints(); 1435275970Scy 1436275970Scy /* 1437275970Scy * Create a lock file for an RCS file. This should be atomic, i.e. 1438275970Scy * if two processes try it simultaneously, at most one should succeed. 1439275970Scy */ 1440275970Scy seteid(); 1441275970Scy fdesc = create(sp); 1442275970Scy fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr. */ 1443275970Scy e = errno; 1444275970Scy setrid(); 1445275970Scy 1446275970Scy if (0 <= fdesc) 1447275970Scy dirtpmaker[0] = effective; 1448275970Scy 1449275970Scy if (fdescSafer < 0) { 1450275970Scy if (e == EACCES && stat(sp,&statbuf) == 0) 1451275970Scy /* The RCS file is busy. */ 1452275970Scy e = EEXIST; 1453275970Scy } else { 1454275970Scy e = ENOENT; 1455275970Scy if (exists) { 1456275970Scy f = Iopen(RCSpath, FOPEN_RB, status); 1457275970Scy e = errno; 1458275970Scy if (f && waslocked) { 1459275970Scy /* Discard the previous lock in favor of this one. */ 1460275970Scy ORCSclose(); 1461275970Scy seteid(); 1462275970Scy r = un_link(lockname); 1463275970Scy e = errno; 1464310419Sdelphij setrid(); 1465275970Scy if (r != 0) 1466275970Scy enfaterror(e, lockname); 1467275970Scy bufscpy(&dirtpname[lockdirtp_index], sp); 1468310419Sdelphij } 1469275970Scy } 1470275970Scy fdlock = fdescSafer; 1471275970Scy } 1472275970Scy 1473275970Scy restoreints(); 1474275970Scy 1475275970Scy errno = e; 1476275970Scy return f; 1477285612Sdelphij} 1478275970Scy 1479275970Scy void 1480275970Scykeepdirtemp(name) 1481275970Scy char const *name; 1482275970Scy/* Do not unlink name, either because it's not there any more, 1483275970Scy * or because it has already been unlinked. 1484275970Scy */ 1485275970Scy{ 1486275970Scy register int i; 1487275970Scy for (i=DIRTEMPNAMES; 0<=--i; ) 1488275970Scy if (dirtpname[i].string == name) { 1489275970Scy dirtpmaker[i] = notmade; 1490275970Scy return; 1491275970Scy } 1492285612Sdelphij faterror("keepdirtemp"); 1493275970Scy} 1494275970Scy 1495275970Scy char const * 1496275970Scymakedirtemp(isworkfile) 1497275970Scy int isworkfile; 1498275970Scy/* 1499275970Scy * Create a unique pathname and store it into dirtpname. 1500275970Scy * Because of storage in tpnames, dirtempunlink() can unlink the file later. 1501275970Scy * Return a pointer to the pathname created. 1502275970Scy * If ISWORKFILE is 1, put it into the working file's directory; 1503275970Scy * if 0, put the unique file in RCSfile's directory. 1504275970Scy */ 1505275970Scy{ 1506275970Scy register char *tp, *np; 1507275970Scy register size_t dl; 1508275970Scy register struct buf *bn; 1509275970Scy register char const *name = isworkfile ? workname : RCSname; 1510275970Scy# if has_mktemp 1511275970Scy int fd; 1512275970Scy# endif 1513275970Scy 1514275970Scy dl = basefilename(name) - name; 1515275970Scy bn = &dirtpname[newRCSdirtp_index + isworkfile]; 1516275970Scy bufalloc(bn, 1517275970Scy# if has_mktemp 1518275970Scy dl + 9 1519275970Scy# else 1520275970Scy strlen(name) + 3 1521275970Scy# endif 1522275970Scy ); 1523275970Scy bufscpy(bn, name); 1524275970Scy np = tp = bn->string; 1525275970Scy tp += dl; 1526275970Scy *tp++ = '_'; 1527275970Scy *tp++ = '0'+isworkfile; 1528275970Scy catchints(); 1529275970Scy# if has_mktemp 1530310419Sdelphij VOID strcpy(tp, "XXXXXX"); 1531275970Scy fd = mkstemp(np); 1532275970Scy if (fd < 0 || !*np) 1533275970Scy faterror("can't make temporary pathname `%.*s_%cXXXXXX'", 1534310419Sdelphij (int)dl, name, '0'+isworkfile 1535275970Scy ); 1536275970Scy close(fd); 1537275970Scy# else 1538275970Scy /* 1539275970Scy * Posix 1003.1-1990 has no reliable way 1540275970Scy * to create a unique file in a named directory. 1541275970Scy * We fudge here. If the filename is abcde, 1542275970Scy * the temp filename is _Ncde where N is a digit. 1543275970Scy */ 1544275970Scy name += dl; 1545275970Scy if (*name) name++; 1546275970Scy if (*name) name++; 1547275970Scy VOID strcpy(tp, name); 1548275970Scy# endif 1549275970Scy dirtpmaker[newRCSdirtp_index + isworkfile] = real; 1550275970Scy return np; 1551275970Scy} 1552275970Scy 1553275970Scy void 1554275970Scydirtempunlink() 1555275970Scy/* Clean up makedirtemp() files. May be invoked by signal handler. */ 1556275970Scy{ 1557275970Scy register int i; 1558275970Scy enum maker m; 1559275970Scy 1560275970Scy for (i = DIRTEMPNAMES; 0 <= --i; ) 1561275970Scy if ((m = dirtpmaker[i]) != notmade) { 1562275970Scy if (m == effective) 1563275970Scy seteid(); 1564275970Scy VOID un_link(dirtpname[i].string); 1565275970Scy if (m == effective) 1566275970Scy setrid(); 1567275970Scy dirtpmaker[i] = notmade; 1568275970Scy } 1569275970Scy} 1570275970Scy 1571275970Scy 1572275970Scy int 1573275970Scy#if has_prototypes 1574275970Scychnamemod( 1575275970Scy FILE **fromp, char const *from, char const *to, 1576275970Scy int set_mode, mode_t mode, time_t mtime 1577275970Scy) 1578275970Scy /* The `#if has_prototypes' is needed because mode_t might promote to int. */ 1579275970Scy#else 1580275970Scy chnamemod(fromp, from, to, set_mode, mode, mtime) 1581275970Scy FILE **fromp; char const *from,*to; 1582275970Scy int set_mode; mode_t mode; time_t mtime; 1583275970Scy#endif 1584275970Scy/* 1585275970Scy * Rename a file (with stream pointer *FROMP) from FROM to TO. 1586275970Scy * FROM already exists. 1587310419Sdelphij * If 0 < SET_MODE, change the mode to MODE, before renaming if possible. 1588275970Scy * If MTIME is not -1, change its mtime to MTIME before renaming. 1589275970Scy * Close and clear *FROMP before renaming it. 1590275970Scy * Unlink TO if it already exists. 1591275970Scy * Return -1 on error (setting errno), 0 otherwise. 1592275970Scy */ 1593275970Scy{ 1594275970Scy mode_t mode_while_renaming = mode; 1595275970Scy int fchmod_set_mode = 0; 1596275970Scy 1597275970Scy# if bad_a_rename || bad_NFS_rename 1598275970Scy struct stat st; 1599275970Scy if (bad_NFS_rename || (bad_a_rename && set_mode <= 0)) { 1600275970Scy if (fstat(fileno(*fromp), &st) != 0) 1601275970Scy return -1; 1602275970Scy if (bad_a_rename && set_mode <= 0) 1603275970Scy mode = st.st_mode; 1604275970Scy } 1605275970Scy# endif 1606275970Scy 1607275970Scy# if bad_a_rename 1608275970Scy /* 1609275970Scy * There's a short window of inconsistency 1610275970Scy * during which the lock file is writable. 1611275970Scy */ 1612275970Scy mode_while_renaming = mode|S_IWUSR; 1613275970Scy if (mode != mode_while_renaming) 1614275970Scy set_mode = 1; 1615275970Scy# endif 1616275970Scy 1617275970Scy# if has_fchmod 1618275970Scy if (0<set_mode && fchmod(fileno(*fromp),mode_while_renaming) == 0) 1619275970Scy fchmod_set_mode = set_mode; 1620275970Scy# endif 1621275970Scy /* If bad_chmod_close, we must close before chmod. */ 1622275970Scy Ozclose(fromp); 1623275970Scy if (fchmod_set_mode<set_mode && chmod(from, mode_while_renaming) != 0) 1624275970Scy return -1; 1625275970Scy 1626275970Scy if (setmtime(from, mtime) != 0) 1627275970Scy return -1; 1628275970Scy 1629275970Scy# if !has_rename || bad_b_rename 1630275970Scy /* 1631310419Sdelphij * There's a short window of inconsistency 1632275970Scy * during which TO does not exist. 1633275970Scy */ 1634275970Scy if (un_link(to) != 0 && errno != ENOENT) 1635275970Scy return -1; 1636275970Scy# endif 1637275970Scy 1638275970Scy# if has_rename 1639275970Scy if (rename(from,to) != 0 && !(has_NFS && errno==ENOENT)) 1640275970Scy return -1; 1641275970Scy# else 1642275970Scy if (do_link(from,to) != 0 || un_link(from) != 0) 1643289999Sglebius return -1; 1644275970Scy# endif 1645275970Scy 1646275970Scy# if bad_NFS_rename 1647289999Sglebius { 1648275970Scy /* 1649289999Sglebius * Check whether the rename falsely reported success. 1650289999Sglebius * A race condition can occur between the rename and the stat. 1651275970Scy */ 1652289999Sglebius struct stat tostat; 1653289999Sglebius if (stat(to, &tostat) != 0) 1654289999Sglebius return -1; 1655289999Sglebius if (! same_file(st, tostat, 0)) { 1656289999Sglebius errno = EIO; 1657289999Sglebius return -1; 1658275970Scy } 1659289999Sglebius } 1660289999Sglebius# endif 1661289999Sglebius 1662289999Sglebius# if bad_a_rename 1663289999Sglebius if (0 < set_mode && chmod(to, mode) != 0) 1664289999Sglebius return -1; 1665289999Sglebius# endif 1666289999Sglebius 1667275970Scy return 0; 1668289999Sglebius} 1669289999Sglebius 1670289999Sglebius int 1671289999Sglebiussetmtime(file, mtime) 1672289999Sglebius char const *file; 1673289999Sglebius time_t mtime; 1674289999Sglebius/* Set FILE's last modified time to MTIME, but do nothing if MTIME is -1. */ 1675289999Sglebius{ 1676289999Sglebius static struct utimbuf amtime; /* static so unused fields are zero */ 1677275970Scy if (mtime == -1) 1678289999Sglebius return 0; 1679275970Scy amtime.actime = now(); 1680275970Scy amtime.modtime = mtime; 1681275970Scy return utime(file, &amtime); 1682275970Scy} 1683275970Scy 1684275970Scy 1685275970Scy 1686275970Scy int 1687275970Scyfindlock(delete, target) 1688275970Scy int delete; 1689275970Scy struct hshentry **target; 1690275970Scy/* 1691275970Scy * Find the first lock held by caller and return a pointer 1692275970Scy * to the locked delta; also removes the lock if DELETE. 1693275970Scy * If one lock, put it into *TARGET. 1694275970Scy * Return 0 for no locks, 1 for one, 2 for two or more. 1695289999Sglebius */ 1696289999Sglebius{ 1697289999Sglebius register struct rcslock *next, **trail, **found; 1698275970Scy 1699289999Sglebius found = 0; 1700289999Sglebius for (trail = &Locks; (next = *trail); trail = &next->nextlock) 1701275970Scy if (strcmp(getcaller(), next->login) == 0) { 1702289999Sglebius if (found) { 1703289999Sglebius rcserror("multiple revisions locked by %s; please specify one", getcaller()); 1704289999Sglebius return 2; 1705289999Sglebius } 1706289999Sglebius found = trail; 1707289999Sglebius } 1708289999Sglebius if (!found) 1709275970Scy return 0; 1710289999Sglebius next = *found; 1711289999Sglebius *target = next->delta; 1712289999Sglebius if (delete) { 1713289999Sglebius next->delta->lockedby = 0; 1714289999Sglebius *found = next->nextlock; 1715289999Sglebius } 1716289999Sglebius return 1; 1717275970Scy} 1718289999Sglebius 1719289999Sglebius int 1720289999Sglebiusaddlock(delta, verbose) 1721289999Sglebius struct hshentry * delta; 1722289999Sglebius int verbose; 1723289999Sglebius/* 1724289999Sglebius * Add a lock held by caller to DELTA and yield 1 if successful. 1725289999Sglebius * Print an error message if verbose and yield -1 if no lock is added because 1726289999Sglebius * DELTA is locked by somebody other than caller. 1727275970Scy * Return 0 if the caller already holds the lock. 1728289999Sglebius */ 1729289999Sglebius{ 1730285612Sdelphij register struct rcslock *next; 1731275970Scy 1732275970Scy for (next = Locks; next; next = next->nextlock) 1733275970Scy if (cmpnum(delta->num, next->delta->num) == 0) 1734275970Scy if (strcmp(getcaller(), next->login) == 0) 1735275970Scy return 0; 1736275970Scy else { 1737275970Scy if (verbose) 1738275970Scy rcserror("Revision %s is already locked by %s.", 1739275970Scy delta->num, next->login 1740275970Scy ); 1741275970Scy return -1; 1742275970Scy } 1743275970Scy next = ftalloc(struct rcslock); 1744275970Scy delta->lockedby = next->login = getcaller(); 1745275970Scy next->delta = delta; 1746275970Scy next->nextlock = Locks; 1747289999Sglebius Locks = next; 1748285612Sdelphij return 1; 1749275970Scy} 1750275970Scy 1751275970Scy 1752275970Scy int 1753275970Scyaddsymbol(num, name, rebind) 1754275970Scy char const *num, *name; 1755275970Scy int rebind; 1756275970Scy/* 1757275970Scy * Associate with revision NUM the new symbolic NAME. 1758275970Scy * If NAME already exists and REBIND is set, associate NAME with NUM; 1759275970Scy * otherwise, print an error message and return false; 1760275970Scy * Return -1 if unsuccessful, 0 if no change, 1 if change. 1761289999Sglebius */ 1762289999Sglebius{ 1763289999Sglebius register struct assoc *next; 1764289999Sglebius 1765289999Sglebius for (next = Symbols; next; next = next->nextassoc) 1766289999Sglebius if (strcmp(name, next->symbol) == 0) 1767289999Sglebius if (strcmp(next->num,num) == 0) 1768289999Sglebius return 0; 1769289999Sglebius else if (rebind) { 1770275970Scy next->num = num; 1771275970Scy return 1; 1772289999Sglebius } else { 1773275970Scy rcserror("symbolic name %s already bound to %s", 1774275970Scy name, next->num 1775275970Scy ); 1776275970Scy return -1; 1777280849Scy } 1778275970Scy next = ftalloc(struct assoc); 1779275970Scy next->symbol = name; 1780275970Scy next->num = num; 1781275970Scy next->nextassoc = Symbols; 1782275970Scy Symbols = next; 1783275970Scy return 1; 1784275970Scy} 1785275970Scy 1786275970Scy 1787275970Scy 1788285612Sdelphij char const * 1789275970Scygetcaller() 1790275970Scy/* Get the caller's login name. */ 1791275970Scy{ 1792275970Scy# if has_setuid 1793275970Scy return getusername(euid()!=ruid()); 1794275970Scy# else 1795275970Scy return getusername(false); 1796275970Scy# endif 1797275970Scy} 1798275970Scy 1799275970Scy 1800275970Scy int 1801275970Scycheckaccesslist() 1802275970Scy/* 1803275970Scy * Return true if caller is the superuser, the owner of the 1804275970Scy * file, the access list is empty, or caller is on the access list. 1805275970Scy * Otherwise, print an error message and return false. 1806275970Scy */ 1807275970Scy{ 1808275970Scy register struct access const *next; 1809275970Scy 1810275970Scy if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0) 1811275970Scy return true; 1812275970Scy 1813275970Scy next = AccessList; 1814275970Scy do { 1815275970Scy if (strcmp(getcaller(), next->login) == 0) 1816275970Scy return true; 1817275970Scy } while ((next = next->nextaccess)); 1818275970Scy 1819275970Scy rcserror("user %s not on the access list", getcaller()); 1820275970Scy return false; 1821275970Scy} 1822275970Scy 1823275970Scy 1824275970Scy int 1825275970Scydorewrite(lockflag, changed) 1826275970Scy int lockflag, changed; 1827275970Scy/* 1828275970Scy * Do nothing if LOCKFLAG is zero. 1829 * Prepare to rewrite an RCS file if CHANGED is positive. 1830 * Stop rewriting if CHANGED is zero, because there won't be any changes. 1831 * Fail if CHANGED is negative. 1832 * Return 0 on success, -1 on failure. 1833 */ 1834{ 1835 int r = 0, e; 1836 1837 if (lockflag) 1838 if (changed) { 1839 if (changed < 0) 1840 return -1; 1841 putadmin(); 1842 puttree(Head, frewrite); 1843 aprintf(frewrite, "\n\n%s%c", Kdesc, nextc); 1844 foutptr = frewrite; 1845 } else { 1846# if bad_creat0 1847 int nr = !!frewrite, ne = 0; 1848# endif 1849 ORCSclose(); 1850 seteid(); 1851 ignoreints(); 1852# if bad_creat0 1853 if (nr) { 1854 nr = un_link(newRCSname); 1855 ne = errno; 1856 keepdirtemp(newRCSname); 1857 } 1858# endif 1859 r = un_link(lockname); 1860 e = errno; 1861 keepdirtemp(lockname); 1862 restoreints(); 1863 setrid(); 1864 if (r != 0) 1865 enerror(e, lockname); 1866# if bad_creat0 1867 if (nr != 0) { 1868 enerror(ne, newRCSname); 1869 r = -1; 1870 } 1871# endif 1872 } 1873 return r; 1874} 1875 1876 int 1877donerewrite(changed, newRCStime) 1878 int changed; 1879 time_t newRCStime; 1880/* 1881 * Finish rewriting an RCS file if CHANGED is nonzero. 1882 * Set its mode if CHANGED is positive. 1883 * Set its modification time to NEWRCSTIME unless it is -1. 1884 * Return 0 on success, -1 on failure. 1885 */ 1886{ 1887 int r = 0, e = 0; 1888# if bad_creat0 1889 int lr, le; 1890# endif 1891 1892 if (changed && !nerror) { 1893 if (finptr) { 1894 fastcopy(finptr, frewrite); 1895 Izclose(&finptr); 1896 } 1897 if (1 < RCSstat.st_nlink) 1898 rcswarn("breaking hard link"); 1899 aflush(frewrite); 1900 seteid(); 1901 ignoreints(); 1902 r = chnamemod( 1903 &frewrite, newRCSname, RCSname, changed, 1904 RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH), 1905 newRCStime 1906 ); 1907 e = errno; 1908 keepdirtemp(newRCSname); 1909# if bad_creat0 1910 lr = un_link(lockname); 1911 le = errno; 1912 keepdirtemp(lockname); 1913# endif 1914 restoreints(); 1915 setrid(); 1916 if (r != 0) { 1917 enerror(e, RCSname); 1918 error("saved in %s", newRCSname); 1919 } 1920# if bad_creat0 1921 if (lr != 0) { 1922 enerror(le, lockname); 1923 r = -1; 1924 } 1925# endif 1926 } 1927 return r; 1928} 1929 1930 void 1931ORCSclose() 1932{ 1933 if (0 <= fdlock) { 1934 if (close(fdlock) != 0) 1935 efaterror(lockname); 1936 fdlock = -1; 1937 } 1938 Ozclose(&frewrite); 1939} 1940 1941 void 1942ORCSerror() 1943/* 1944* Like ORCSclose, except we are cleaning up after an interrupt or fatal error. 1945* Do not report errors, since this may loop. This is needed only because 1946* some brain-damaged hosts (e.g. OS/2) cannot unlink files that are open, and 1947* some nearly-Posix hosts (e.g. NFS) work better if the files are closed first. 1948* This isn't a completely reliable away to work around brain-damaged hosts, 1949* because of the gap between actual file opening and setting frewrite etc., 1950* but it's better than nothing. 1951*/ 1952{ 1953 if (0 <= fdlock) 1954 VOID close(fdlock); 1955 if (frewrite) 1956 /* Avoid fclose, since stdio may not be reentrant. */ 1957 VOID close(fileno(frewrite)); 1958} 1959