diff options
Diffstat (limited to 'gnu/usr.bin/rcs/lib/rcsedit.c')
| -rw-r--r-- | gnu/usr.bin/rcs/lib/rcsedit.c | 997 |
1 files changed, 639 insertions, 358 deletions
diff --git a/gnu/usr.bin/rcs/lib/rcsedit.c b/gnu/usr.bin/rcs/lib/rcsedit.c index 32ff7e29a6aa..fc8b78453a63 100644 --- a/gnu/usr.bin/rcs/lib/rcsedit.c +++ b/gnu/usr.bin/rcs/lib/rcsedit.c @@ -1,15 +1,14 @@ -/* - * RCS stream editor - */ -/********************************************************************************** +/* RCS stream editor */ + +/****************************************************************************** * edits the input file according to a * script from stdin, generated by diff -n * performs keyword expansion - ********************************************************************************** + ****************************************************************************** */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* Copyright 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. @@ -25,8 +24,9 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with RCS; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +along with RCS; see the file COPYING. +If not, write to the Free Software Foundation, +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Report problems and direct all questions to: @@ -34,15 +34,54 @@ Report problems and direct all questions to: */ - -/* $Log: rcsedit.c,v $ - * Revision 1.2 1994/05/14 07:00:22 rgrimes - * Add new option -K from David Dawes that allows you to turn on and off - * specific keyword substitution during a rcs co command. - * Add the new keyword FreeBSD that is IDENTICAL in operation to $Id$. +/* + * Revision 5.19 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.18 1995/06/01 16:23:43 eggert + * (dirtpname): No longer external. + * (do_link): Simplify logic. + * (finisheditline, finishedit): Replace Iseek/Itell with what they stand for. + * (fopen_update_truncate): Replace `#if' with `if'. + * (keyreplace, makedirtemp): dirlen(x) -> basefilename(x)-x. + * + * (edit_string): Fix bug: if !large_memory, a bogus trailing `@' was output + * at the end of incomplete lines. + * + * (keyreplace): Do not assume that seeking backwards + * at the start of a file will fail; on some systems it succeeds. + * Convert C- and Pascal-style comment starts to ` *' in comment leader. + * + * (rcswriteopen): Use fdSafer to get safer file descriptor. + * Open RCS file with FOPEN_RB. + * + * (chnamemod): Work around bad_NFS_rename bug; don't ignore un_link result. + * Fall back on chmod if fchmod fails, since it might be ENOSYS. * - * Revision 1.1.1.1 1993/06/18 04:22:12 jkh - * Updated GNU utilities + * (aflush): Move to rcslex.c. + * + * Revision 5.17 1994/03/20 04:52:58 eggert + * Normally calculate the $Log prefix from context, not from RCS file. + * Move setmtime here from rcsutil.c. Add ORCSerror. Remove lint. + * + * Revision 5.16 1993/11/03 17:42:27 eggert + * Add -z. Add Name keyword. If bad_unlink, ignore errno when unlink fails. + * Escape white space, $, and \ in keyword string file names. + * Don't output 2 spaces between date and time after Log. + * + * Revision 5.15 1992/07/28 16:12:44 eggert + * Some hosts have readlink but not ELOOP. Avoid `unsigned'. + * Preserve dates more systematically. Statement macro names now end in _. + * + * Revision 5.14 1992/02/17 23:02:24 eggert + * Add -T support. + * + * Revision 5.13 1992/01/24 18:44:19 eggert + * Add support for bad_chmod_close, bad_creat0. + * + * Revision 5.12 1992/01/06 02:42:34 eggert + * Add setmode parameter to chnamemod. addsymbol now reports changes. + * while (E) ; -> while (E) continue; * * Revision 5.11 1991/11/03 01:11:44 eggert * Move the warning about link breaking to where they're actually being broken. @@ -162,27 +201,36 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(editId, "$Id: rcsedit.c,v 1.2 1994/05/14 07:00:22 rgrimes Exp $") - -static void keyreplace P((enum markers,struct hshentry const*,FILE*)); +libId(editId, "$Id: rcsedit.c,v 1.6 1995/10/29 22:06:12 peter Exp $") +static void editEndsPrematurely P((void)) exiting; +static void editLineNumberOverflow P((void)) exiting; +static void escape_string P((FILE*,char const*)); +static void keyreplace P((enum markers,struct hshentry const*,int,RILE*,FILE*,int)); FILE *fcopy; /* result file descriptor */ -char const *resultfile; /* result file name */ +char const *resultname; /* result pathname */ int locker_expansion; /* should the locker name be appended to Id val? */ #if !large_memory static RILE *fedit; /* edit file descriptor */ - static char const *editfile; /* edit pathname */ + static char const *editname; /* edit pathname */ #endif -static unsigned long editline; /* edit line counter; #lines before cursor */ +static long editline; /* edit line counter; #lines before cursor */ static long linecorr; /* #adds - #deletes in each edit run. */ /*used to correct editline in case file is not rewound after */ /* applying one delta */ -#define DIRTEMPNAMES 2 +/* indexes into dirtpname */ +#define lockdirtp_index 0 +#define newRCSdirtp_index bad_creat0 +#define newworkdirtp_index (newRCSdirtp_index+1) +#define DIRTEMPNAMES (newworkdirtp_index + 1) + enum maker {notmade, real, effective}; -struct buf dirtfname[DIRTEMPNAMES]; /* unlink these when done */ -static enum maker volatile dirtfmaker[DIRTEMPNAMES]; /* if these are set */ +static struct buf dirtpname[DIRTEMPNAMES]; /* unlink these when done */ +static enum maker volatile dirtpmaker[DIRTEMPNAMES]; /* if these are set */ +#define lockname (dirtpname[lockdirtp_index].string) +#define newRCSname (dirtpname[newRCSdirtp_index].string) #if has_NFS || bad_unlink @@ -195,17 +243,19 @@ un_link(s) */ { # if bad_unlink - int e; if (unlink(s) == 0) return 0; - e = errno; -# if has_NFS - if (e == ENOENT) - return 0; -# endif - if (chmod(s, S_IWUSR) != 0) { - errno = e; - return -1; + else { + int e = errno; + /* + * Forge ahead even if errno == ENOENT; some completely + * brain-damaged hosts (e.g. PCTCP 2.2) yield ENOENT + * even for existing unwritable files. + */ + if (chmod(s, S_IWUSR) != 0) { + errno = e; + return -1; + } } # endif # if has_NFS @@ -220,38 +270,37 @@ un_link(s) # if !has_NFS # define do_link(s,t) link(s,t) # else + static int do_link P((char const*,char const*)); static int do_link(s, t) char const *s, *t; /* Link S to T, ignoring bogus EEXIST problems due to NFS failures. */ { - struct stat sb, tb; - - if (link(s,t) == 0) - return 0; - if (errno != EEXIST) - return -1; - if ( - stat(s, &sb) == 0 && - stat(t, &tb) == 0 && - sb.st_ino == tb.st_ino && - sb.st_dev == tb.st_dev - ) - return 0; - errno = EEXIST; - return -1; + int r = link(s, t); + + if (r != 0 && errno == EEXIST) { + struct stat sb, tb; + if ( + stat(s, &sb) == 0 && + stat(t, &tb) == 0 && + same_file(sb, tb, 0) + ) + r = 0; + errno = EEXIST; + } + return r; } # endif #endif - static exiting void + static void editEndsPrematurely() { fatserror("edit script ends prematurely"); } - static exiting void + static void editLineNumberOverflow() { fatserror("edit script refers to line past end of file"); @@ -263,11 +312,12 @@ editLineNumberOverflow() #if has_memmove # define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type)) #else + static void movelines P((Iptr_type*,Iptr_type const*,long)); static void movelines(s1, s2, n) register Iptr_type *s1; register Iptr_type const *s2; - register unsigned long n; + register long n; { if (s1 < s2) do { @@ -283,22 +333,26 @@ movelines(s1, s2, n) } #endif +static void deletelines P((long,long)); +static void finisheditline P((RILE*,FILE*,Iptr_type,struct hshentry const*)); +static void insertline P((long,Iptr_type)); +static void snapshotline P((FILE*,Iptr_type)); + /* * `line' contains pointers to the lines in the currently `edited' file. * It is a 0-origin array that represents linelim-gapsize lines. - * line[0..gap-1] and line[gap+gapsize..linelim-1] contain pointers to lines. - * line[gap..gap+gapsize-1] contains garbage. + * line[0 .. gap-1] and line[gap+gapsize .. linelim-1] hold pointers to lines. + * line[gap .. gap+gapsize-1] contains garbage. * * Any @s in lines are duplicated. * Lines are terminated by \n, or (for a last partial line only) by single @. */ static Iptr_type *line; -static unsigned long gap, gapsize, linelim; - +static size_t gap, gapsize, linelim; static void insertline(n, l) - unsigned long n; + long n; Iptr_type l; /* Before line N, insert line L. N is 0-origin. */ { @@ -324,10 +378,10 @@ insertline(n, l) static void deletelines(n, nlines) - unsigned long n, nlines; + long n, nlines; /* Delete lines N through N+NLINES-1. N is 0-origin. */ { - unsigned long l = n + nlines; + long l = n + nlines; if (linelim-gapsize < l || l < n) editLineNumberOverflow(); if (l < gap) @@ -348,7 +402,7 @@ snapshotline(f, l) do { if ((c = *l++) == SDELIM && *l++ != SDELIM) return; - aputc(c, f); + aputc_(c, f) } while (c != '\n'); } @@ -371,8 +425,8 @@ finisheditline(fin, fout, l, delta) Iptr_type l; struct hshentry const *delta; { - Iseek(fin, l); - if (expandline(fin, fout, delta, true, (FILE*)0) < 0) + fin->ptr = l; + if (expandline(fin, fout, delta, true, (FILE*)0, true) < 0) faterror("finisheditline internal error"); } @@ -394,28 +448,27 @@ finishedit(delta, outfile, done) else { register Iptr_type *p, *lim, *l = line; register RILE *fin = finptr; - Iptr_type here = Itell(fin); + Iptr_type here = fin->ptr; for (p=l, lim=l+gap; p<lim; ) finisheditline(fin, outfile, *p++, delta); for (p+=gapsize, lim=l+linelim; p<lim; ) finisheditline(fin, outfile, *p++, delta); - Iseek(fin, here); + fin->ptr = here; } } } -/* Open a temporary FILENAME for output, truncating any previous contents. */ -# define fopen_update_truncate(filename) fopen(filename, FOPEN_W_WORK) +/* Open a temporary NAME for output, truncating any previous contents. */ +# define fopen_update_truncate(name) fopenSafer(name, FOPEN_W_WORK) #else /* !large_memory */ + static FILE * fopen_update_truncate P((char const*)); static FILE * -fopen_update_truncate(filename) - char const *filename; +fopen_update_truncate(name) + char const *name; { -# if bad_fopen_wplus - if (un_link(filename) != 0) - efaterror(filename); -# endif - return fopen(filename, FOPEN_WPLUS_WORK); + if (bad_fopen_wplus && un_link(name) != 0) + efaterror(name); + return fopenSafer(name, FOPEN_WPLUS_WORK); } #endif @@ -425,31 +478,31 @@ openfcopy(f) FILE *f; { if (!(fcopy = f)) { - if (!resultfile) - resultfile = maketemp(2); - if (!(fcopy = fopen_update_truncate(resultfile))) - efaterror(resultfile); + if (!resultname) + resultname = maketemp(2); + if (!(fcopy = fopen_update_truncate(resultname))) + efaterror(resultname); } } #if !large_memory + static void swapeditfiles P((FILE*)); static void swapeditfiles(outfile) FILE *outfile; -/* Function: swaps resultfile and editfile, assigns fedit=fcopy, +/* Function: swaps resultname and editname, assigns fedit=fcopy, * and rewinds fedit for reading. Set fcopy to outfile if nonnull; - * otherwise, set fcopy to be resultfile opened for reading and writing. + * otherwise, set fcopy to be resultname opened for reading and writing. */ { char const *tmpptr; editline = 0; linecorr = 0; - if (fseek(fcopy, 0L, SEEK_SET) != 0) - Oerror(); + Orewind(fcopy); fedit = fcopy; - tmpptr=editfile; editfile=resultfile; resultfile=tmpptr; + tmpptr=editname; editname=resultname; resultname=tmpptr; openfcopy(outfile); } @@ -458,7 +511,7 @@ snapshotedit(f) FILE *f; /* Copy the current state of the edits to F. */ { - finishedit((struct hshentry *)nil, (FILE*)0, false); + finishedit((struct hshentry *)0, (FILE*)0, false); fastcopy(fedit, f); Irewind(fedit); } @@ -469,7 +522,7 @@ finishedit(delta, outfile, done) FILE *outfile; int done; /* copy the rest of the edit file and close it (if it exists). - * if delta!=nil, perform keyword substitution at the same time. + * if delta, perform keyword substitution at the same time. * If DONE is set, we are finishing the last pass. */ { @@ -479,8 +532,8 @@ finishedit(delta, outfile, done) fe = fedit; if (fe) { fc = fcopy; - if (delta!=nil) { - while (1 < expandline(fe,fc,delta,false,(FILE*)0)) + if (delta) { + while (1 < expandline(fe,fc,delta,false,(FILE*)0,true)) ; } else { fastcopy(fe,fc); @@ -497,13 +550,14 @@ finishedit(delta, outfile, done) #if large_memory # define copylines(upto,delta) (editline = (upto)) #else + static void copylines P((long,struct hshentry const*)); static void -copylines(upto,delta) - register unsigned long upto; +copylines(upto, delta) + register long upto; struct hshentry const *delta; /* * Copy input lines editline+1..upto from fedit to fcopy. - * If delta != nil, keyword expansion is done simultaneously. + * If delta, keyword expansion is done simultaneously. * editline is updated. Rewinds a file only if necessary. */ { @@ -514,7 +568,7 @@ copylines(upto,delta) if (upto < editline) { /* swap files */ - finishedit((struct hshentry *)nil, (FILE*)0, false); + finishedit((struct hshentry *)0, (FILE*)0, false); /* assumes edit only during last pass, from the beginning*/ } fe = fedit; @@ -522,15 +576,15 @@ copylines(upto,delta) if (editline < upto) if (delta) do { - if (expandline(fe,fc,delta,false,(FILE*)0) <= 1) - editLineNumberOverflow(); + if (expandline(fe,fc,delta,false,(FILE*)0,true) <= 1) + editLineNumberOverflow(); } while (++editline < upto); else { setupcache(fe); cache(fe); do { do { - cachegeteof(c, editLineNumberOverflow();); - aputc(c, fc); + cachegeteof_(c, editLineNumberOverflow();) + aputc_(c, fc) } while (c != '\n'); } while (++editline < upto); uncache(fe); @@ -549,8 +603,8 @@ xpandstring(delta) * If foutptr is nonnull, the string is also copied unchanged to foutptr. */ { - while (1 < expandline(finptr,fcopy,delta,true,foutptr)) - ; + while (1 < expandline(finptr,fcopy,delta,true,foutptr,true)) + continue; } @@ -574,7 +628,7 @@ copystring() fcop = fcopy; amidline = false; for (;;) { - GETC(frew,c); + GETC_(frew,c) switch (c) { case '\n': ++editline; @@ -582,7 +636,7 @@ copystring() amidline = false; break; case SDELIM: - GETC(frew,c); + GETC_(frew,c) if (c != SDELIM) { /* end of string */ nextc = c; @@ -595,7 +649,7 @@ copystring() amidline = true; break; } - aputc(c,fcop); + aputc_(c,fcop) } } @@ -605,18 +659,18 @@ enterstring() /* Like copystring, except the string is put into the edit data structure. */ { #if !large_memory - editfile = 0; + editname = 0; fedit = 0; editline = linecorr = 0; - resultfile = maketemp(1); - if (!(fcopy = fopen_update_truncate(resultfile))) - efaterror(resultfile); + resultname = maketemp(1); + if (!(fcopy = fopen_update_truncate(resultname))) + efaterror(resultname); copystring(); #else register int c; declarecache; register FILE *frew; - register unsigned long e, oe; + register long e, oe; register int amidline, oamidline; register Iptr_type optr; register RILE *fin; @@ -630,8 +684,8 @@ enterstring() frew = foutptr; amidline = false; for (;;) { - optr = cachetell(); - GETC(frew,c); + optr = cacheptr(); + GETC_(frew,c) oamidline = amidline; oe = e; switch (c) { @@ -641,7 +695,7 @@ enterstring() amidline = false; break; case SDELIM: - GETC(frew,c); + GETC_(frew,c) if (c != SDELIM) { /* end of string */ nextc = c; @@ -675,13 +729,13 @@ edit_string() * Read an edit script from finptr and applies it to the edit file. #if !large_memory * The result is written to fcopy. - * If delta!=nil, keyword expansion is performed simultaneously. + * If delta, keyword expansion is performed simultaneously. * If running out of lines in fedit, fedit and fcopy are swapped. - * editfile is the name of the file that goes with fedit. + * editname is the name of the file that goes with fedit. #endif * If foutptr is set, the edit script is also copied verbatim to foutptr. * Assumes that all these files are open. - * resultfile is the name of the file that goes with fcopy. + * resultname is the name of the file that goes with fcopy. * Assumes the next input character from finptr is the first character of * the edit script. Resets nextc on exit. */ @@ -692,13 +746,13 @@ edit_string() register FILE *frew; # if !large_memory register FILE *f; - unsigned long line_lim = ULONG_MAX; + long line_lim = LONG_MAX; register RILE *fe; # endif - register unsigned long i; + register long i; register RILE *fin; # if large_memory - register unsigned long j; + register long j; # endif struct diffcmd dc; @@ -726,12 +780,13 @@ edit_string() do { /*skip next line*/ do { - Igeteof(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } ); + Igeteof_(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } ) } while (c != '\n'); } while (--i); # endif } else { - copylines(dc.line1, delta); /*copy only; no delete*/ + /* Copy lines without deleting any. */ + copylines(dc.line1, delta); i = dc.nlines; # if large_memory j = editline+linecorr; @@ -741,7 +796,7 @@ edit_string() f = fcopy; if (delta) do { - switch (expandline(fin,f,delta,true,frew)) { + switch (expandline(fin,f,delta,true,frew,true)){ case 0: case 1: if (i==1) return; @@ -756,17 +811,12 @@ edit_string() cache(fin); do { # if large_memory - insertline(j++, cachetell()); + insertline(j++, cacheptr()); # endif for (;;) { - GETC(frew, c); -# if !large_memory - aputc(c, f); -# endif - if (c == '\n') - break; + GETC_(frew, c) if (c==SDELIM) { - GETC(frew, c); + GETC_(frew, c) if (c!=SDELIM) { if (--i) editEndsPrematurely(); @@ -775,6 +825,11 @@ edit_string() return; } } +# if !large_memory + aputc_(c, f) +# endif + if (c == '\n') + break; } ++rcsline; } while (--i); @@ -790,17 +845,18 @@ edit_string() int -expandline(infile, outfile, delta, delimstuffed, frewfile) +expandline(infile, outfile, delta, delimstuffed, frewfile, dolog) RILE *infile; FILE *outfile, *frewfile; struct hshentry const *delta; - int delimstuffed; + int delimstuffed, dolog; /* * Read a line from INFILE and write it to OUTFILE. + * Do keyword expansion with data from DELTA. * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM. - * Keyword expansion is performed with data from delta. * If FREWFILE is set, copy the line unchanged to FREWFILE. * DELIMSTUFFED must be true if FREWFILE is set. + * Append revision history to log only if DOLOG is set. * Yields -1 if no data is copied, 0 if an incomplete line is copied, * 2 if a complete line is copied; adds 1 to yield if expansion occurred. */ @@ -823,15 +879,15 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) r = -1; for (;;) { - if (ds) { - GETC(frew, c); - } else - cachegeteof(c, goto uncache_exit;); + if (ds) + GETC_(frew, c) + else + cachegeteof_(c, goto uncache_exit;) for (;;) { switch (c) { case SDELIM: if (ds) { - GETC(frew, c); + GETC_(frew, c) if (c != SDELIM) { /* end of string */ nextc=c; @@ -840,13 +896,13 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) } /* fall into */ default: - aputc(c,out); + aputc_(c,out) r = 0; break; case '\n': rcsline += ds; - aputc(c,out); + aputc_(c,out) r = 2; goto uncache_exit; @@ -857,11 +913,11 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) tp = keyval.string; *tp++ = KDELIM; for (;;) { - if (ds) { - GETC(frew, c); - } else - cachegeteof(c, goto keystring_eof;); - if (tp < keyval.string+keylength+1) + if (ds) + GETC_(frew, c) + else + cachegeteof_(c, goto keystring_eof;) + if (tp <= &keyval.string[keylength]) switch (ctab[c]) { case LETTER: case Letter: *tp++ = c; @@ -884,17 +940,17 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) /* try to find closing KDELIM, and replace value */ tlim = keyval.string + keyval.size; for (;;) { - if (ds) { - GETC(frew, c); - } else - cachegeteof(c, goto keystring_eof;); + if (ds) + GETC_(frew, c) + else + cachegeteof_(c, goto keystring_eof;) if (c=='\n' || c==KDELIM) break; *tp++ =c; if (tlim <= tp) tp = bufenlarge(&keyval, &tlim); if (c==SDELIM && ds) { /*skip next SDELIM */ - GETC(frew, c); + GETC_(frew, c) if (c != SDELIM) { /* end of string before closing KDELIM or newline */ nextc = c; @@ -910,7 +966,9 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) } } /* now put out the new keyword value */ - keyreplace(matchresult,delta,out); + uncache(infile); + keyreplace(matchresult, delta, ds, infile, out, dolog); + cache(infile); e = 1; break; } @@ -927,117 +985,229 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) } + static void +escape_string(out, s) + register FILE *out; + register char const *s; +/* Output to OUT the string S, escaping chars that would break `ci -k'. */ +{ + register char c; + for (;;) + switch ((c = *s++)) { + case 0: return; + case '\t': aputs("\\t", out); break; + case '\n': aputs("\\n", out); break; + case ' ': aputs("\\040", out); break; + case KDELIM: aputs("\\044", out); break; + case '\\': if (VERSION(5)<=RCSversion) {aputs("\\\\", out); break;} + /* fall into */ + default: aputc_(c, out) break; + } +} + char const ciklog[ciklogsize] = "checked in with -k by "; static void -keyreplace(marker,delta,out) +keyreplace(marker, delta, delimstuffed, infile, out, dolog) enum markers marker; register struct hshentry const *delta; + int delimstuffed; + RILE *infile; register FILE *out; + int dolog; /* function: outputs the keyword value(s) corresponding to marker. * Attributes are derived from delta. */ { register char const *sp, *cp, *date; - register char c; + register int c; register size_t cs, cw, ls; char const *sp1; - char datebuf[datesize]; + char datebuf[datesize + zonelenmax]; int RCSv; + int exp; sp = Keyword[(int)marker]; - - if (Expand == KEY_EXPAND) { - aprintf(out, "%c%s%c", KDELIM, sp, KDELIM); - return; - } - - date= delta->date; + exp = Expand; + date = delta->date; RCSv = RCSversion; - if (Expand == KEYVAL_EXPAND || Expand == KEYVALLOCK_EXPAND) - aprintf(out, "%c%s%c%c", KDELIM, sp, VDELIM, + if (exp != VAL_EXPAND) + aprintf(out, "%c%s", KDELIM, sp); + if (exp != KEY_EXPAND) { + + if (exp != VAL_EXPAND) + aprintf(out, "%c%c", VDELIM, marker==Log && RCSv<VERSION(5) ? '\t' : ' ' ); - switch (marker) { - case Author: + switch (marker) { + case Author: aputs(delta->author, out); break; - case Date: + case Date: aputs(date2str(date,datebuf), out); break; - /* - * The FreeBSD keyword is identical to Id. - */ - case FreeBSD: - case Id: - case Header: - aprintf(out, "%s %s %s %s %s", - marker==Id || marker==FreeBSD || RCSv<VERSION(4) - ? basename(RCSfilename) - : getfullRCSname(), + case FreeBSD: + case Id: + case Header: + escape_string(out, + marker==Id || marker==FreeBSD || RCSv<VERSION(4) + ? basefilename(RCSname) + : getfullRCSname() + ); + aprintf(out, " %s %s %s %s", delta->num, date2str(date, datebuf), delta->author, RCSv==VERSION(3) && delta->lockedby ? "Locked" : delta->state ); - if (delta->lockedby!=nil) + if (delta->lockedby) if (VERSION(5) <= RCSv) { - if (locker_expansion || Expand==KEYVALLOCK_EXPAND) + if (locker_expansion || exp==KEYVALLOCK_EXPAND) aprintf(out, " %s", delta->lockedby); } else if (RCSv == VERSION(4)) aprintf(out, " Locker: %s", delta->lockedby); break; - case Locker: + case Locker: if (delta->lockedby) if ( locker_expansion - || Expand == KEYVALLOCK_EXPAND + || exp == KEYVALLOCK_EXPAND || RCSv <= VERSION(4) ) aputs(delta->lockedby, out); break; - case Log: - case RCSfile: - aputs(basename(RCSfilename), out); + case Log: + case RCSfile: + escape_string(out, basefilename(RCSname)); break; - case Revision: + case Name: + if (delta->name) + aputs(delta->name, out); + break; + case Revision: aputs(delta->num, out); break; - case Source: - aputs(getfullRCSname(), out); + case Source: + escape_string(out, getfullRCSname()); break; - case State: + case State: aputs(delta->state, out); break; - default: + default: break; - } - if (Expand == KEYVAL_EXPAND || Expand == KEYVALLOCK_EXPAND) { + } + if (exp != VAL_EXPAND) afputc(' ', out); - afputc(KDELIM, out); } - if (marker == Log) { + if (exp != VAL_EXPAND) + afputc(KDELIM, out); + + if (marker == Log && dolog) { + struct buf leader; + sp = delta->log.string; ls = delta->log.size; if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1)) return; + bufautobegin(&leader); + if (RCSversion < VERSION(5)) { + cp = Comment.string; + cs = Comment.size; + } else { + int kdelim_found = 0; + Ioffset_type chars_read = Itell(infile); + declarecache; + setupcache(infile); cache(infile); + + c = 0; /* Pacify `gcc -Wall'. */ + + /* + * Back up to the start of the current input line, + * setting CS to the number of characters before `$Log'. + */ + cs = 0; + for (;;) { + if (!--chars_read) + goto done_backing_up; + cacheunget_(infile, c) + if (c == '\n') + break; + if (c == SDELIM && delimstuffed) { + if (!--chars_read) + break; + cacheunget_(infile, c) + if (c != SDELIM) { + cacheget_(c) + break; + } + } + cs += kdelim_found; + kdelim_found |= c==KDELIM; + } + cacheget_(c) + done_backing_up:; + + /* Copy characters before `$Log' into LEADER. */ + bufalloc(&leader, cs); + cp = leader.string; + for (cw = 0; cw < cs; cw++) { + leader.string[cw] = c; + if (c == SDELIM && delimstuffed) + cacheget_(c) + cacheget_(c) + } + + /* Convert traditional C or Pascal leader to ` *'. */ + for (cw = 0; cw < cs; cw++) + if (ctab[(unsigned char) cp[cw]] != SPACE) + break; + if ( + cw+1 < cs + && cp[cw+1] == '*' + && (cp[cw] == '/' || cp[cw] == '(') + ) { + size_t i = cw+1; + for (;;) + if (++i == cs) { + warn( + "`%c* $Log' is obsolescent; use ` * $Log'.", + cp[cw] + ); + leader.string[cw] = ' '; + break; + } else if (ctab[(unsigned char) cp[i]] != SPACE) + break; + } + + /* Skip `$Log ... $' string. */ + do { + cacheget_(c) + } while (c != KDELIM); + uncache(infile); + } afputc('\n', out); - cp = Comment.string; - cw = cs = Comment.size; awrite(cp, cs, out); - /* oddity: 2 spaces between date and time, not 1 as usual */ - sp1 = strchr(date2str(date,datebuf), ' '); - aprintf(out, "Revision %s %.*s %s %s", - delta->num, (int)(sp1-datebuf), datebuf, sp1, delta->author - ); + sp1 = date2str(date, datebuf); + if (VERSION(5) <= RCSv) { + aprintf(out, "Revision %s %s %s", + delta->num, sp1, delta->author + ); + } else { + /* oddity: 2 spaces between date and time, not 1 as usual */ + sp1 = strchr(sp1, ' '); + aprintf(out, "Revision %s %.*s %s %s", + delta->num, (int)(sp1-datebuf), datebuf, sp1, + delta->author + ); + } /* Do not include state: it may change and is not updated. */ - /* Comment is the comment leader. */ + cw = cs; if (VERSION(5) <= RCSv) for (; cw && (cp[cw-1]==' ' || cp[cw-1]=='\t'); --cw) - ; + continue; for (;;) { afputc('\n', out); awrite(cp, cw, out); @@ -1056,10 +1226,12 @@ keyreplace(marker,delta,out) } while (c != '\n'); } } + bufautoend(&leader); } } #if has_readlink + static int resolve_symlink P((struct buf*)); static int resolve_symlink(L) struct buf *L; @@ -1075,7 +1247,7 @@ resolve_symlink(L) size_t s; ssize_t r; struct buf bigbuf; - unsigned linkcount = MAXSYMLINKS + 1; + int linkcount = MAXSYMLINKS; b = a; s = sizeof(a); @@ -1085,21 +1257,29 @@ resolve_symlink(L) bufalloc(&bigbuf, s<<1); b = bigbuf.string; s = bigbuf.size; - } else if (!--linkcount) { + } else if (!linkcount--) { +# ifndef ELOOP + /* + * Some pedantic Posix 1003.1-1990 hosts have readlink + * but not ELOOP. Approximate ELOOP with EMLINK. + */ +# define ELOOP EMLINK +# endif errno = ELOOP; return -1; } else { /* Splice symbolic link into L. */ b[r] = '\0'; - L->string[ROOTPATH(b) ? (size_t)0 : dirlen(L->string)] = '\0'; + L->string[ + ROOTPATH(b) ? 0 : basefilename(L->string) - L->string + ] = '\0'; bufscat(L, b); } e = errno; bufautoend(&bigbuf); errno = e; switch (e) { - case ENXIO: - case EINVAL: return 1; + case readlink_isreg_errno: return 1; case ENOENT: return 0; default: return -1; } @@ -1112,24 +1292,23 @@ rcswriteopen(RCSbuf, status, mustread) struct stat *status; int mustread; /* - * Create the lock file corresponding to RCSNAME. - * Then try to open RCSNAME for reading and yield its FILE* descriptor. + * Create the lock file corresponding to RCSBUF. + * Then try to open RCSBUF for reading and yield its RILE* descriptor. * Put its status into *STATUS too. * MUSTREAD is true if the file must already exist, too. * If all goes well, discard any previously acquired locks, - * and set frewrite to the FILE* descriptor of the lock file, - * which will eventually turn into the new RCS file. + * and set fdlock to the file descriptor of the RCS lockfile. */ { register char *tp; - register char const *sp, *RCSname, *x; + register char const *sp, *RCSpath, *x; RILE *f; size_t l; - int e, exists, fdesc, previouslock, r; + int e, exists, fdesc, fdescSafer, r, waslocked; struct buf *dirt; struct stat statbuf; - previouslock = frewrite != 0; + waslocked = 0 <= fdlock; exists = # if has_readlink resolve_symlink(RCSbuf); @@ -1137,7 +1316,7 @@ rcswriteopen(RCSbuf, status, mustread) stat(RCSbuf->string, &statbuf) == 0 ? 1 : errno==ENOENT ? 0 : -1; # endif - if (exists < (mustread|previouslock)) + if (exists < (mustread|waslocked)) /* * There's an unusual problem with the RCS file; * or the RCS file doesn't exist, @@ -1145,26 +1324,26 @@ rcswriteopen(RCSbuf, status, mustread) */ return 0; - RCSname = RCSbuf->string; - sp = basename(RCSname); - l = sp - RCSname; - dirt = &dirtfname[previouslock]; - bufscpy(dirt, RCSname); + RCSpath = RCSbuf->string; + sp = basefilename(RCSpath); + l = sp - RCSpath; + dirt = &dirtpname[waslocked]; + bufscpy(dirt, RCSpath); tp = dirt->string + l; - x = rcssuffix(RCSname); + x = rcssuffix(RCSpath); # if has_readlink if (!x) { - error("symbolic link to non RCS filename `%s'", RCSname); + error("symbolic link to non RCS file `%s'", RCSpath); errno = EINVAL; return 0; } # endif if (*sp == *x) { - error("RCS filename `%s' incompatible with suffix `%s'", sp, x); + error("RCS pathname `%s' incompatible with suffix `%s'", sp, x); errno = EINVAL; return 0; } - /* Create a lock file whose name is a function of the RCS filename. */ + /* Create a lock filename that is a function of the RCS filename. */ if (*x) { /* * The suffix is nonempty. @@ -1184,38 +1363,41 @@ rcswriteopen(RCSbuf, status, mustread) * with last char replaced by '_'. */ while ((*tp++ = *sp++)) - ; + continue; tp -= 2; if (*tp == '_') { - error("RCS filename `%s' ends with `%c'", RCSname, *tp); + error("RCS pathname `%s' ends with `%c'", RCSpath, *tp); errno = EINVAL; return 0; } *tp = '_'; } - sp = tp = dirt->string; + sp = dirt->string; f = 0; /* * good news: - * open(f, O_CREAT|O_EXCL|O_TRUNC|O_WRONLY, READONLY) is atomic - * according to Posix 1003.1-1990. + * open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_READONLY) + * is atomic according to Posix 1003.1-1990. * bad news: * NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990. * good news: - * (O_TRUNC,READONLY) normally guarantees atomicity even with NFS. + * (O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity + * even with NFS. * bad news: - * If you're root, (O_TRUNC,READONLY) doesn't guarantee atomicity. + * If you're root, (O_TRUNC,OPEN_CREAT_READONLY) doesn't + * guarantee atomicity. * good news: * Root-over-the-wire NFS access is rare for security reasons. * This bug has never been reported in practice with RCS. * So we don't worry about this bug. * * An even rarer NFS bug can occur when clients retry requests. - * Suppose client A renames the lock file ",f," to "f,v" - * at about the same time that client B creates ",f,", + * This can happen in the usual case of NFS over UDP. + * Suppose client A releases a lock by renaming ",f," to "f,v" at + * about the same time that client B obtains a lock by creating ",f,", * and suppose A's first rename request is delayed, so A reissues it. * The sequence of events might be: * A sends rename(",f,", "f,v") @@ -1228,20 +1410,20 @@ rcswriteopen(RCSbuf, status, mustread) * This not only wrongly deletes B's lock, it removes the RCS file! * Most NFS implementations have idempotency caches that usually prevent * this scenario, but such caches are finite and can be overrun. - * This problem afflicts programs that use the traditional + * This problem afflicts not only RCS, which uses open() and rename() + * to get and release locks; it also afflicts the traditional * Unix method of using link() and unlink() to get and release locks, - * as well as RCS's method of using open() and rename(). - * There is no easy workaround for either link-unlink or open-rename. + * and the less traditional method of using mkdir() and rmdir(). + * There is no easy workaround. * Any new method based on lockf() seemingly would be incompatible with * the old methods; besides, lockf() is notoriously buggy under NFS. * Since this problem afflicts scads of Unix programs, but is so rare * that nobody seems to be worried about it, we won't worry either. */ -# define READONLY (S_IRUSR|S_IRGRP|S_IROTH) # if !open_can_creat -# define create(f) creat(f, READONLY) +# define create(f) creat(f, OPEN_CREAT_READONLY) # else -# define create(f) open(f, O_BINARY|O_CREAT|O_EXCL|O_TRUNC|O_WRONLY, READONLY) +# define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_READONLY) # endif catchints(); @@ -1253,34 +1435,35 @@ rcswriteopen(RCSbuf, status, mustread) */ seteid(); fdesc = create(sp); + fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr. */ e = errno; setrid(); - if (fdesc < 0) { - if (e == EACCES && stat(tp,&statbuf) == 0) + if (0 <= fdesc) + dirtpmaker[0] = effective; + + if (fdescSafer < 0) { + if (e == EACCES && stat(sp,&statbuf) == 0) /* The RCS file is busy. */ e = EEXIST; } else { - dirtfmaker[0] = effective; e = ENOENT; if (exists) { - f = Iopen(RCSname, FOPEN_R, status); + f = Iopen(RCSpath, FOPEN_RB, status); e = errno; - if (f && previouslock) { + if (f && waslocked) { /* Discard the previous lock in favor of this one. */ - Ozclose(&frewrite); + ORCSclose(); seteid(); - if ((r = un_link(newRCSfilename)) != 0) - e = errno; + r = un_link(lockname); + e = errno; setrid(); if (r != 0) - enfaterror(e, newRCSfilename); - bufscpy(&dirtfname[0], tp); + enfaterror(e, lockname); + bufscpy(&dirtpname[lockdirtp_index], sp); } } - if (!(frewrite = fdopen(fdesc, FOPEN_W))) { - efaterror(newRCSfilename); - } + fdlock = fdescSafer; } restoreints(); @@ -1298,33 +1481,31 @@ keepdirtemp(name) { register int i; for (i=DIRTEMPNAMES; 0<=--i; ) - if (dirtfname[i].string == name) { - dirtfmaker[i] = notmade; + if (dirtpname[i].string == name) { + dirtpmaker[i] = notmade; return; } faterror("keepdirtemp"); } char const * -makedirtemp(name, n) - register char const *name; - int n; +makedirtemp(isworkfile) + int isworkfile; /* - * Have maketemp() do all the work if name is null. - * Otherwise, create a unique filename in name's dir using n and name - * and store it into the dirtfname[n]. - * Because of storage in tfnames, dirtempunlink() can unlink the file later. - * Return a pointer to the filename created. + * Create a unique pathname and store it into dirtpname. + * Because of storage in tpnames, dirtempunlink() can unlink the file later. + * Return a pointer to the pathname created. + * If ISWORKFILE is 1, put it into the working file's directory; + * if 0, put the unique file in RCSfile's directory. */ { register char *tp, *np; register size_t dl; register struct buf *bn; + register char const *name = isworkfile ? workname : RCSname; - if (!name) - return maketemp(n); - dl = dirlen(name); - bn = &dirtfname[n]; + dl = basefilename(name) - name; + bn = &dirtpname[newRCSdirtp_index + isworkfile]; bufalloc(bn, # if has_mktemp dl + 9 @@ -1336,19 +1517,19 @@ makedirtemp(name, n) np = tp = bn->string; tp += dl; *tp++ = '_'; - *tp++ = '0'+n; + *tp++ = '0'+isworkfile; catchints(); # if has_mktemp VOID strcpy(tp, "XXXXXX"); if (!mktemp(np) || !*np) - faterror("can't make temporary file name `%.*s%c_%cXXXXXX'", - (int)dl, name, SLASH, '0'+n + faterror("can't make temporary pathname `%.*s_%cXXXXXX'", + (int)dl, name, '0'+isworkfile ); # else /* * Posix 1003.1-1990 has no reliable way * to create a unique file in a named directory. - * We fudge here. If the working file name is abcde, + * We fudge here. If the filename is abcde, * the temp filename is _Ncde where N is a digit. */ name += dl; @@ -1356,7 +1537,7 @@ makedirtemp(name, n) if (*name) name++; VOID strcpy(tp, name); # endif - dirtfmaker[n] = real; + dirtpmaker[newRCSdirtp_index + isworkfile] = real; return np; } @@ -1368,84 +1549,127 @@ dirtempunlink() enum maker m; for (i = DIRTEMPNAMES; 0 <= --i; ) - if ((m = dirtfmaker[i]) != notmade) { + if ((m = dirtpmaker[i]) != notmade) { if (m == effective) seteid(); - VOID un_link(dirtfname[i].string); + VOID un_link(dirtpname[i].string); if (m == effective) setrid(); - dirtfmaker[i] = notmade; + dirtpmaker[i] = notmade; } } int #if has_prototypes -chnamemod(FILE **fromp, char const *from, char const *to, mode_t mode) +chnamemod( + FILE **fromp, char const *from, char const *to, + int set_mode, mode_t mode, time_t mtime +) /* The `#if has_prototypes' is needed because mode_t might promote to int. */ #else - chnamemod(fromp,from,to,mode) FILE **fromp; char const *from,*to; mode_t mode; + chnamemod(fromp, from, to, set_mode, mode, mtime) + FILE **fromp; char const *from,*to; + int set_mode; mode_t mode; time_t mtime; #endif /* - * Rename a file (with optional stream pointer *FROMP) from FROM to TO. + * Rename a file (with stream pointer *FROMP) from FROM to TO. * FROM already exists. - * Change its mode to MODE, before renaming if possible. - * If FROMP, close and clear *FROMP before renaming it. + * If 0 < SET_MODE, change the mode to MODE, before renaming if possible. + * If MTIME is not -1, change its mtime to MTIME before renaming. + * Close and clear *FROMP before renaming it. * Unlink TO if it already exists. * Return -1 on error (setting errno), 0 otherwise. */ { + mode_t mode_while_renaming = mode; + int fchmod_set_mode = 0; + +# if bad_a_rename || bad_NFS_rename + struct stat st; + if (bad_NFS_rename || (bad_a_rename && set_mode <= 0)) { + if (fstat(fileno(*fromp), &st) != 0) + return -1; + if (bad_a_rename && set_mode <= 0) + mode = st.st_mode; + } +# endif + # if bad_a_rename /* - * This host is brain damaged. A race condition is possible - * while the lock file is temporarily writable. - * There doesn't seem to be a workaround. - */ - mode_t mode_while_renaming = mode|S_IWUSR; -# else -# define mode_while_renaming mode + * There's a short window of inconsistency + * during which the lock file is writable. + */ + mode_while_renaming = mode|S_IWUSR; + if (mode != mode_while_renaming) + set_mode = 1; # endif - if (fromp) { -# if has_fchmod - if (fchmod(fileno(*fromp), mode_while_renaming) != 0) - return -1; -# endif - Ozclose(fromp); - } + # if has_fchmod - else + if (0<set_mode && fchmod(fileno(*fromp),mode_while_renaming) == 0) + fchmod_set_mode = set_mode; # endif - if (chmod(from, mode_while_renaming) != 0) + /* If bad_chmod_close, we must close before chmod. */ + Ozclose(fromp); + if (fchmod_set_mode<set_mode && chmod(from, mode_while_renaming) != 0) + return -1; + + if (setmtime(from, mtime) != 0) return -1; # if !has_rename || bad_b_rename - VOID un_link(to); /* - * We need not check the result; - * link() or rename() will catch it. - * No harm is done if TO does not exist. - * However, there's a short window of inconsistency - * during which TO does not exist. - */ + * There's a short window of inconsistency + * during which TO does not exist. + */ + if (un_link(to) != 0 && errno != ENOENT) + return -1; # endif - return -# if !has_rename - do_link(from,to) != 0 ? -1 : un_link(from) -# else - rename(from, to) != 0 -# if has_NFS - && errno != ENOENT -# endif - ? -1 -# if bad_a_rename - : mode != mode_while_renaming ? chmod(to, mode) -# endif - : 0 -# endif - ; +# if has_rename + if (rename(from,to) != 0 && !(has_NFS && errno==ENOENT)) + return -1; +# else + if (do_link(from,to) != 0 || un_link(from) != 0) + return -1; +# endif -# undef mode_while_renaming +# if bad_NFS_rename + { + /* + * Check whether the rename falsely reported success. + * A race condition can occur between the rename and the stat. + */ + struct stat tostat; + if (stat(to, &tostat) != 0) + return -1; + if (! same_file(st, tostat, 0)) { + errno = EIO; + return -1; + } + } +# endif + +# if bad_a_rename + if (0 < set_mode && chmod(to, mode) != 0) + return -1; +# endif + + return 0; +} + + int +setmtime(file, mtime) + char const *file; + time_t mtime; +/* Set FILE's last modified time to MTIME, but do nothing if MTIME is -1. */ +{ + static struct utimbuf amtime; /* static so unused fields are zero */ + if (mtime == -1) + return 0; + amtime.actime = now(); + amtime.modtime = mtime; + return utime(file, &amtime); } @@ -1461,13 +1685,13 @@ findlock(delete, target) * Return 0 for no locks, 1 for one, 2 for two or more. */ { - register struct lock *next, **trail, **found; + register struct rcslock *next, **trail, **found; found = 0; for (trail = &Locks; (next = *trail); trail = &next->nextlock) if (strcmp(getcaller(), next->login) == 0) { if (found) { - error("multiple revisions locked by %s; please specify one", getcaller()); + rcserror("multiple revisions locked by %s; please specify one", getcaller()); return 2; } found = trail; @@ -1477,36 +1701,37 @@ findlock(delete, target) next = *found; *target = next->delta; if (delete) { - next->delta->lockedby = nil; + next->delta->lockedby = 0; *found = next->nextlock; } return 1; } int -addlock(delta) +addlock(delta, verbose) struct hshentry * delta; + int verbose; /* * Add a lock held by caller to DELTA and yield 1 if successful. - * Print an error message and yield -1 if no lock is added because + * Print an error message if verbose and yield -1 if no lock is added because * DELTA is locked by somebody other than caller. * Return 0 if the caller already holds the lock. */ { - register struct lock *next; + register struct rcslock *next; - next=Locks; for (next = Locks; next; next = next->nextlock) if (cmpnum(delta->num, next->delta->num) == 0) if (strcmp(getcaller(), next->login) == 0) return 0; else { - error("revision %s already locked by %s", - delta->num, next->login - ); + if (verbose) + rcserror("Revision %s is already locked by %s.", + delta->num, next->login + ); return -1; } - next = ftalloc(struct lock); + next = ftalloc(struct rcslock); delta->lockedby = next->login = getcaller(); next->delta = delta; next->nextlock = Locks; @@ -1523,28 +1748,30 @@ addsymbol(num, name, rebind) * Associate with revision NUM the new symbolic NAME. * If NAME already exists and REBIND is set, associate NAME with NUM; * otherwise, print an error message and return false; - * Return true if successful. + * Return -1 if unsuccessful, 0 if no change, 1 if change. */ { register struct assoc *next; for (next = Symbols; next; next = next->nextassoc) if (strcmp(name, next->symbol) == 0) - if (rebind || strcmp(next->num,num) == 0) { + if (strcmp(next->num,num) == 0) + return 0; + else if (rebind) { next->num = num; - return true; + return 1; } else { - error("symbolic name %s already bound to %s", + rcserror("symbolic name %s already bound to %s", name, next->num ); - return false; + return -1; } next = ftalloc(struct assoc); next->symbol = name; next->num = num; next->nextassoc = Symbols; Symbols = next; - return true; + return 1; } @@ -1580,7 +1807,7 @@ checkaccesslist() return true; } while ((next = next->nextaccess)); - error("user %s not on the access list", getcaller()); + rcserror("user %s not on the access list", getcaller()); return false; } @@ -1593,45 +1820,65 @@ dorewrite(lockflag, changed) * Prepare to rewrite an RCS file if CHANGED is positive. * Stop rewriting if CHANGED is zero, because there won't be any changes. * Fail if CHANGED is negative. - * Return true on success. + * Return 0 on success, -1 on failure. */ { - int r, e; + int r = 0, e; if (lockflag) if (changed) { if (changed < 0) - return false; - putadmin(frewrite); + return -1; + putadmin(); puttree(Head, frewrite); aprintf(frewrite, "\n\n%s%c", Kdesc, nextc); foutptr = frewrite; } else { - Ozclose(&frewrite); +# if bad_creat0 + int nr = !!frewrite, ne = 0; +# endif + ORCSclose(); seteid(); ignoreints(); - r = un_link(newRCSfilename); +# if bad_creat0 + if (nr) { + nr = un_link(newRCSname); + ne = errno; + keepdirtemp(newRCSname); + } +# endif + r = un_link(lockname); e = errno; - keepdirtemp(newRCSfilename); + keepdirtemp(lockname); restoreints(); setrid(); - if (r != 0) { - enerror(e, RCSfilename); - return false; - } + if (r != 0) + enerror(e, lockname); +# if bad_creat0 + if (nr != 0) { + enerror(ne, newRCSname); + r = -1; + } +# endif } - return true; + return r; } int -donerewrite(changed) +donerewrite(changed, newRCStime) int changed; + time_t newRCStime; /* * Finish rewriting an RCS file if CHANGED is nonzero. - * Return true on success. + * Set its mode if CHANGED is positive. + * Set its modification time to NEWRCSTIME unless it is -1. + * Return 0 on success, -1 on failure. */ { - int r, e; + int r = 0, e = 0; +# if bad_creat0 + int lr, le; +# endif if (changed && !nerror) { if (finptr) { @@ -1639,30 +1886,64 @@ donerewrite(changed) Izclose(&finptr); } if (1 < RCSstat.st_nlink) - warn("breaking hard link to %s", RCSfilename); + rcswarn("breaking hard link"); + aflush(frewrite); seteid(); ignoreints(); - r = chnamemod(&frewrite, newRCSfilename, RCSfilename, - RCSstat.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH) + r = chnamemod( + &frewrite, newRCSname, RCSname, changed, + RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH), + newRCStime ); e = errno; - keepdirtemp(newRCSfilename); + keepdirtemp(newRCSname); +# if bad_creat0 + lr = un_link(lockname); + le = errno; + keepdirtemp(lockname); +# endif restoreints(); setrid(); if (r != 0) { - enerror(e, RCSfilename); - error("saved in %s", newRCSfilename); - dirtempunlink(); - return false; + enerror(e, RCSname); + error("saved in %s", newRCSname); } +# if bad_creat0 + if (lr != 0) { + enerror(le, lockname); + r = -1; + } +# endif } - return true; + return r; } void -aflush(f) - FILE *f; +ORCSclose() +{ + if (0 <= fdlock) { + if (close(fdlock) != 0) + efaterror(lockname); + fdlock = -1; + } + Ozclose(&frewrite); +} + + void +ORCSerror() +/* +* Like ORCSclose, except we are cleaning up after an interrupt or fatal error. +* Do not report errors, since this may loop. This is needed only because +* some brain-damaged hosts (e.g. OS/2) cannot unlink files that are open, and +* some nearly-Posix hosts (e.g. NFS) work better if the files are closed first. +* This isn't a completely reliable away to work around brain-damaged hosts, +* because of the gap between actual file opening and setting frewrite etc., +* but it's better than nothing. +*/ { - if (fflush(f) != 0) - Oerror(); + if (0 <= fdlock) + VOID close(fdlock); + if (frewrite) + /* Avoid fclose, since stdio may not be reentrant. */ + VOID close(fileno(frewrite)); } |
