diff options
Diffstat (limited to 'gnu/usr.bin/rcs/lib/rcsedit.c')
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsedit.c | 1958 |
1 files changed, 0 insertions, 1958 deletions
diff --git a/gnu/usr.bin/rcs/lib/rcsedit.c b/gnu/usr.bin/rcs/lib/rcsedit.c deleted file mode 100644 index dc9dd30..0000000 --- a/gnu/usr.bin/rcs/lib/rcsedit.c +++ /dev/null @@ -1,1958 +0,0 @@ -/* RCS stream editor */ - -/****************************************************************************** - * edits the input file according to a - * script from stdin, generated by diff -n - * performs keyword expansion - ****************************************************************************** - */ - -/* 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. - -RCS is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -RCS is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -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, -59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -Report problems and direct all questions to: - - rcs-bugs@cs.purdue.edu - -*/ - -/* - * 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. - * - * (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. - * - * Revision 5.10 1991/10/07 17:32:46 eggert - * Support piece tables even if !has_mmap. Fix rare NFS bugs. - * - * Revision 5.9 1991/09/17 19:07:40 eggert - * SGI readlink() yields ENXIO, not EINVAL, for nonlinks. - * - * Revision 5.8 1991/08/19 03:13:55 eggert - * Add piece tables, NFS bug workarounds. Catch odd filenames. Tune. - * - * Revision 5.7 1991/04/21 11:58:21 eggert - * Fix errno bugs. Add -x, RCSINIT, MS-DOS support. - * - * Revision 5.6 1991/02/25 07:12:40 eggert - * Fix setuid bug. Support new link behavior. Work around broken "w+" fopen. - * - * Revision 5.5 1990/12/30 05:07:35 eggert - * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL). - * - * Revision 5.4 1990/11/01 05:03:40 eggert - * Permit arbitrary data in comment leaders. - * - * Revision 5.3 1990/09/11 02:41:13 eggert - * Tune expandline(). - * - * Revision 5.2 1990/09/04 08:02:21 eggert - * Count RCS lines better. Improve incomplete line handling. - * - * Revision 5.1 1990/08/29 07:13:56 eggert - * Add -kkvl. - * Fix bug when getting revisions to files ending in incomplete lines. - * Fix bug in comment leader expansion. - * - * Revision 5.0 1990/08/22 08:12:47 eggert - * Don't require final newline. - * Don't append "checked in with -k by " to logs, - * so that checking in a program with -k doesn't change it. - * Don't generate trailing white space for empty comment leader. - * Remove compile-time limits; use malloc instead. Add -k, -V. - * Permit dates past 1999/12/31. Make lock and temp files faster and safer. - * Ansify and Posixate. Check diff's output. - * - * Revision 4.8 89/05/01 15:12:35 narten - * changed copyright header to reflect current distribution rules - * - * Revision 4.7 88/11/08 13:54:14 narten - * misplaced semicolon caused infinite loop - * - * Revision 4.6 88/08/09 19:12:45 eggert - * Shrink stdio code size; allow cc -R. - * - * Revision 4.5 87/12/18 11:38:46 narten - * Changes from the 43. version. Don't know the significance of the - * first change involving "rewind". Also, additional "lint" cleanup. - * (Guy Harris) - * - * Revision 4.4 87/10/18 10:32:21 narten - * Updating version numbers. Changes relative to version 1.1 actually - * relative to 4.1 - * - * Revision 1.4 87/09/24 13:59:29 narten - * Sources now pass through lint (if you ignore printf/sprintf/fprintf - * warnings) - * - * Revision 1.3 87/09/15 16:39:39 shepler - * added an initializatin of the variables editline and linecorr - * this will be done each time a file is processed. - * (there was an obscure bug where if co was used to retrieve multiple files - * it would dump) - * fix attributed to Roy Morris @FileNet Corp ...!felix!roy - * - * Revision 1.2 87/03/27 14:22:17 jenkins - * Port to suns - * - * Revision 4.1 83/05/12 13:10:30 wft - * Added new markers Id and RCSfile; added locker to Header and Id. - * Overhauled expandline completely() (problem with $01234567890123456789@). - * Moved trymatch() and marker table to rcskeys.c. - * - * Revision 3.7 83/05/12 13:04:39 wft - * Added retry to expandline to resume after failed match which ended in $. - * Fixed truncation problem for $19chars followed by@@. - * Log no longer expands full path of RCS file. - * - * Revision 3.6 83/05/11 16:06:30 wft - * added retry to expandline to resume after failed match which ended in $. - * Fixed truncation problem for $19chars followed by@@. - * - * Revision 3.5 82/12/04 13:20:56 wft - * Added expansion of keyword Locker. - * - * Revision 3.4 82/12/03 12:26:54 wft - * Added line number correction in case editing does not start at the - * beginning of the file. - * Changed keyword expansion to always print a space before closing KDELIM; - * Expansion for Header shortened. - * - * Revision 3.3 82/11/14 14:49:30 wft - * removed Suffix from keyword expansion. Replaced fclose with ffclose. - * keyreplace() gets log message from delta, not from curlogmsg. - * fixed expression overflow in while(c=putc(GETC.... - * checked nil printing. - * - * Revision 3.2 82/10/18 21:13:39 wft - * I added checks for write errors during the co process, and renamed - * expandstring() to xpandstring(). - * - * Revision 3.1 82/10/13 15:52:55 wft - * changed type of result of getc() from char to int. - * made keyword expansion loop in expandline() portable to machines - * without sign-extension. - */ - - -#include "rcsbase.h" - -libId(editId, "$FreeBSD$") - -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 *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 *editname; /* edit pathname */ -#endif -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 */ - -/* 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}; -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 - int -un_link(s) - char const *s; -/* - * Remove S, even if it is unwritable. - * Ignore unlink() ENOENT failures; NFS generates bogus ones. - */ -{ -# if bad_unlink - if (unlink(s) == 0) - return 0; - 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 - return unlink(s)==0 || errno==ENOENT ? 0 : -1; -# else - return unlink(s); -# endif -} -#endif - -#if !has_rename -# 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. */ -{ - 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 void -editEndsPrematurely() -{ - fatserror("edit script ends prematurely"); -} - - static void -editLineNumberOverflow() -{ - fatserror("edit script refers to line past end of file"); -} - - -#if large_memory - -#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 long n; -{ - if (s1 < s2) - do { - *s1++ = *s2++; - } while (--n); - else { - s1 += n; - s2 += n; - do { - *--s1 = *--s2; - } while (--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] 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 size_t gap, gapsize, linelim; - - static void -insertline(n, l) - long n; - Iptr_type l; -/* Before line N, insert line L. N is 0-origin. */ -{ - if (linelim-gapsize < n) - editLineNumberOverflow(); - if (!gapsize) - line = - !linelim ? - tnalloc(Iptr_type, linelim = gapsize = 1024) - : ( - gap = gapsize = linelim, - trealloc(Iptr_type, line, linelim <<= 1) - ); - if (n < gap) - movelines(line+n+gapsize, line+n, gap-n); - else if (gap < n) - movelines(line+gap, line+gap+gapsize, n-gap); - - line[n] = l; - gap = n + 1; - gapsize--; -} - - static void -deletelines(n, nlines) - long n, nlines; -/* Delete lines N through N+NLINES-1. N is 0-origin. */ -{ - long l = n + nlines; - if (linelim-gapsize < l || l < n) - editLineNumberOverflow(); - if (l < gap) - movelines(line+l+gapsize, line+l, gap-l); - else if (gap < n) - movelines(line+gap, line+gap+gapsize, n-gap); - - gap = n; - gapsize += nlines; -} - - static void -snapshotline(f, l) - register FILE *f; - register Iptr_type l; -{ - register int c; - do { - if ((c = *l++) == SDELIM && *l++ != SDELIM) - return; - aputc_(c, f) - } while (c != '\n'); -} - - void -snapshotedit(f) - FILE *f; -/* Copy the current state of the edits to F. */ -{ - register Iptr_type *p, *lim, *l=line; - for (p=l, lim=l+gap; p<lim; ) - snapshotline(f, *p++); - for (p+=gapsize, lim=l+linelim; p<lim; ) - snapshotline(f, *p++); -} - - static void -finisheditline(fin, fout, l, delta) - RILE *fin; - FILE *fout; - Iptr_type l; - struct hshentry const *delta; -{ - fin->ptr = l; - if (expandline(fin, fout, delta, true, (FILE*)0, true) < 0) - faterror("finisheditline internal error"); -} - - void -finishedit(delta, outfile, done) - struct hshentry const *delta; - FILE *outfile; - int done; -/* - * Doing expansion if DELTA is set, output the state of the edits to OUTFILE. - * But do nothing unless DONE is set (which means we are on the last pass). - */ -{ - if (done) { - openfcopy(outfile); - outfile = fcopy; - if (!delta) - snapshotedit(outfile); - else { - register Iptr_type *p, *lim, *l = line; - register RILE *fin = finptr; - 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); - fin->ptr = here; - } - } -} - -/* 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(name) - char const *name; -{ - if (bad_fopen_wplus && un_link(name) != 0) - efaterror(name); - return fopenSafer(name, FOPEN_WPLUS_WORK); -} -#endif - - - void -openfcopy(f) - FILE *f; -{ - if (!(fcopy = f)) { - 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 resultname and editname, assigns fedit=fcopy, - * and rewinds fedit for reading. Set fcopy to outfile if nonnull; - * otherwise, set fcopy to be resultname opened for reading and writing. - */ -{ - char const *tmpptr; - - editline = 0; linecorr = 0; - Orewind(fcopy); - fedit = fcopy; - tmpptr=editname; editname=resultname; resultname=tmpptr; - openfcopy(outfile); -} - - void -snapshotedit(f) - FILE *f; -/* Copy the current state of the edits to F. */ -{ - finishedit((struct hshentry *)0, (FILE*)0, false); - fastcopy(fedit, f); - Irewind(fedit); -} - - void -finishedit(delta, outfile, done) - struct hshentry const *delta; - FILE *outfile; - int done; -/* copy the rest of the edit file and close it (if it exists). - * if delta, perform keyword substitution at the same time. - * If DONE is set, we are finishing the last pass. - */ -{ - register RILE *fe; - register FILE *fc; - - fe = fedit; - if (fe) { - fc = fcopy; - if (delta) { - while (1 < expandline(fe,fc,delta,false,(FILE*)0,true)) - ; - } else { - fastcopy(fe,fc); - } - Ifclose(fe); - } - if (!done) - swapeditfiles(outfile); -} -#endif - - - -#if large_memory -# define copylines(upto,delta) (editline = (upto)) -#else - static void copylines P((long,struct hshentry const*)); - static void -copylines(upto, delta) - register long upto; - struct hshentry const *delta; -/* - * Copy input lines editline+1..upto from fedit to fcopy. - * If delta, keyword expansion is done simultaneously. - * editline is updated. Rewinds a file only if necessary. - */ -{ - register int c; - declarecache; - register FILE *fc; - register RILE *fe; - - if (upto < editline) { - /* swap files */ - finishedit((struct hshentry *)0, (FILE*)0, false); - /* assumes edit only during last pass, from the beginning*/ - } - fe = fedit; - fc = fcopy; - if (editline < upto) - if (delta) - do { - 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) - } while (c != '\n'); - } while (++editline < upto); - uncache(fe); - } -} -#endif - - - - void -xpandstring(delta) - struct hshentry const *delta; -/* Function: Reads a string terminated by SDELIM from finptr and writes it - * to fcopy. Double SDELIM is replaced with single SDELIM. - * Keyword expansion is performed with data from delta. - * If foutptr is nonnull, the string is also copied unchanged to foutptr. - */ -{ - while (1 < expandline(finptr,fcopy,delta,true,foutptr,true)) - continue; -} - - - void -copystring() -/* Function: copies a string terminated with a single SDELIM from finptr to - * fcopy, replacing all double SDELIM with a single SDELIM. - * If foutptr is nonnull, the string also copied unchanged to foutptr. - * editline is incremented by the number of lines copied. - * Assumption: next character read is first string character. - */ -{ register c; - declarecache; - register FILE *frew, *fcop; - register int amidline; - register RILE *fin; - - fin = finptr; - setupcache(fin); cache(fin); - frew = foutptr; - fcop = fcopy; - amidline = false; - for (;;) { - GETC_(frew,c) - switch (c) { - case '\n': - ++editline; - ++rcsline; - amidline = false; - break; - case SDELIM: - GETC_(frew,c) - if (c != SDELIM) { - /* end of string */ - nextc = c; - editline += amidline; - uncache(fin); - return; - } - /* fall into */ - default: - amidline = true; - break; - } - aputc_(c,fcop) - } -} - - - void -enterstring() -/* Like copystring, except the string is put into the edit data structure. */ -{ -#if !large_memory - editname = 0; - fedit = 0; - editline = linecorr = 0; - resultname = maketemp(1); - if (!(fcopy = fopen_update_truncate(resultname))) - efaterror(resultname); - copystring(); -#else - register int c; - declarecache; - register FILE *frew; - register long e, oe; - register int amidline, oamidline; - register Iptr_type optr; - register RILE *fin; - - e = 0; - gap = 0; - gapsize = linelim; - fin = finptr; - setupcache(fin); cache(fin); - advise_access(fin, MADV_NORMAL); - frew = foutptr; - amidline = false; - for (;;) { - optr = cacheptr(); - GETC_(frew,c) - oamidline = amidline; - oe = e; - switch (c) { - case '\n': - ++e; - ++rcsline; - amidline = false; - break; - case SDELIM: - GETC_(frew,c) - if (c != SDELIM) { - /* end of string */ - nextc = c; - editline = e + amidline; - linecorr = 0; - uncache(fin); - return; - } - /* fall into */ - default: - amidline = true; - break; - } - if (!oamidline) - insertline(oe, optr); - } -#endif -} - - - - - void -#if large_memory -edit_string() -#else - editstring(delta) - struct hshentry const *delta; -#endif -/* - * Read an edit script from finptr and applies it to the edit file. -#if !large_memory - * The result is written to fcopy. - * If delta, keyword expansion is performed simultaneously. - * If running out of lines in fedit, fedit and fcopy are swapped. - * 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. - * 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. - */ -{ - int ed; /* editor command */ - register int c; - declarecache; - register FILE *frew; -# if !large_memory - register FILE *f; - long line_lim = LONG_MAX; - register RILE *fe; -# endif - register long i; - register RILE *fin; -# if large_memory - register long j; -# endif - struct diffcmd dc; - - editline += linecorr; linecorr=0; /*correct line number*/ - frew = foutptr; - fin = finptr; - setupcache(fin); - initdiffcmd(&dc); - while (0 <= (ed = getdiffcmd(fin,true,frew,&dc))) -#if !large_memory - if (line_lim <= dc.line1) - editLineNumberOverflow(); - else -#endif - if (!ed) { - copylines(dc.line1-1, delta); - /* skip over unwanted lines */ - i = dc.nlines; - linecorr -= i; - editline += i; -# if large_memory - deletelines(editline+linecorr, i); -# else - fe = fedit; - do { - /*skip next line*/ - do { - Igeteof_(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } ) - } while (c != '\n'); - } while (--i); -# endif - } else { - /* Copy lines without deleting any. */ - copylines(dc.line1, delta); - i = dc.nlines; -# if large_memory - j = editline+linecorr; -# endif - linecorr += i; -#if !large_memory - f = fcopy; - if (delta) - do { - switch (expandline(fin,f,delta,true,frew,true)){ - case 0: case 1: - if (i==1) - return; - /* fall into */ - case -1: - editEndsPrematurely(); - } - } while (--i); - else -#endif - { - cache(fin); - do { -# if large_memory - insertline(j++, cacheptr()); -# endif - for (;;) { - GETC_(frew, c) - if (c==SDELIM) { - GETC_(frew, c) - if (c!=SDELIM) { - if (--i) - editEndsPrematurely(); - nextc = c; - uncache(fin); - return; - } - } -# if !large_memory - aputc_(c, f) -# endif - if (c == '\n') - break; - } - ++rcsline; - } while (--i); - uncache(fin); - } - } -} - - - -/* The rest is for keyword expansion */ - - - - int -expandline(infile, outfile, delta, delimstuffed, frewfile, dolog) - RILE *infile; - FILE *outfile, *frewfile; - struct hshentry const *delta; - 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. - * 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. - */ -{ - register c; - declarecache; - register FILE *out, *frew; - register char * tp; - register int e, ds, r; - char const *tlim; - static struct buf keyval; - enum markers matchresult; - - setupcache(infile); cache(infile); - out = outfile; - frew = frewfile; - ds = delimstuffed; - bufalloc(&keyval, keylength+3); - e = 0; - r = -1; - - for (;;) { - if (ds) - GETC_(frew, c) - else - cachegeteof_(c, goto uncache_exit;) - for (;;) { - switch (c) { - case SDELIM: - if (ds) { - GETC_(frew, c) - if (c != SDELIM) { - /* end of string */ - nextc=c; - goto uncache_exit; - } - } - /* fall into */ - default: - aputc_(c,out) - r = 0; - break; - - case '\n': - rcsline += ds; - aputc_(c,out) - r = 2; - goto uncache_exit; - - case KDELIM: - r = 0; - /* check for keyword */ - /* first, copy a long enough string into keystring */ - tp = keyval.string; - *tp++ = KDELIM; - for (;;) { - 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; - continue; - default: - break; - } - break; - } - *tp++ = c; *tp = '\0'; - matchresult = trymatch(keyval.string+1); - if (matchresult==Nomatch) { - tp[-1] = 0; - aputs(keyval.string, out); - continue; /* last c handled properly */ - } - - /* Now we have a keyword terminated with a K/VDELIM */ - if (c==VDELIM) { - /* 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 (c=='\n' || c==KDELIM) - break; - *tp++ =c; - if (tlim <= tp) - tp = bufenlarge(&keyval, &tlim); - if (c==SDELIM && ds) { /*skip next SDELIM */ - GETC_(frew, c) - if (c != SDELIM) { - /* end of string before closing KDELIM or newline */ - nextc = c; - goto keystring_eof; - } - } - } - if (c!=KDELIM) { - /* couldn't find closing KDELIM -- give up */ - *tp = 0; - aputs(keyval.string, out); - continue; /* last c handled properly */ - } - } - /* now put out the new keyword value */ - uncache(infile); - keyreplace(matchresult, delta, ds, infile, out, dolog); - cache(infile); - e = 1; - break; - } - break; - } - } - - keystring_eof: - *tp = 0; - aputs(keyval.string, out); - uncache_exit: - uncache(infile); - return r + e; -} - - - 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, 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 int c; - register size_t cs, cw, ls; - char const *sp1; - char datebuf[datesize + zonelenmax]; - int RCSv; - int exp; - - sp = Keyword[(int)marker]; - exp = Expand; - date = delta->date; - RCSv = RCSversion; - - 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: - aputs(delta->author, out); - break; - case Date: - aputs(date2str(date,datebuf), out); - break; - case Id: - case LocalId: - case Header: - case CVSHeader: - if (marker == Id || RCSv < VERSION(4) || - (marker == LocalId && LocalIdMode == Id)) - escape_string(out, basefilename(RCSname)); - else if (marker == CVSHeader || - (marker == LocalId && LocalIdMode == CVSHeader)) - escape_string(out, getfullCVSname()); - else - escape_string(out, 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) - if (VERSION(5) <= RCSv) { - 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: - if (delta->lockedby) - if ( - locker_expansion - || exp == KEYVALLOCK_EXPAND - || RCSv <= VERSION(4) - ) - aputs(delta->lockedby, out); - break; - case Log: - case RCSfile: - escape_string(out, basefilename(RCSname)); - break; - case Name: - if (delta->name) - aputs(delta->name, out); - break; - case Revision: - aputs(delta->num, out); - break; - case Source: - escape_string(out, getfullRCSname()); - break; - case State: - aputs(delta->state, out); - break; - default: - break; - } - if (exp != VAL_EXPAND) - afputc(' ', out); - } - 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); - awrite(cp, cs, out); - 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. */ - 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); - if (!ls) - break; - --ls; - c = *sp++; - if (c != '\n') { - awrite(cp+cw, cs-cw, out); - do { - afputc(c,out); - if (!ls) - break; - --ls; - c = *sp++; - } while (c != '\n'); - } - } - bufautoend(&leader); - } -} - -#if has_readlink - static int resolve_symlink P((struct buf*)); - static int -resolve_symlink(L) - struct buf *L; -/* - * If L is a symbolic link, resolve it to the name that it points to. - * If unsuccessful, set errno and yield -1. - * If it points to an existing file, yield 1. - * Otherwise, set errno=ENOENT and yield 0. - */ -{ - char *b, a[SIZEABLE_PATH]; - int e; - size_t s; - ssize_t r; - struct buf bigbuf; - int linkcount = MAXSYMLINKS; - - b = a; - s = sizeof(a); - bufautobegin(&bigbuf); - while ((r = readlink(L->string,b,s)) != -1) - if (r == s) { - bufalloc(&bigbuf, s<<1); - b = bigbuf.string; - s = bigbuf.size; - } 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) ? 0 : basefilename(L->string) - L->string - ] = '\0'; - bufscat(L, b); - } - e = errno; - bufautoend(&bigbuf); - errno = e; - switch (e) { - case readlink_isreg_errno: return 1; - case ENOENT: return 0; - default: return -1; - } -} -#endif - - RILE * -rcswriteopen(RCSbuf, status, mustread) - struct buf *RCSbuf; - struct stat *status; - int mustread; -/* - * 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 fdlock to the file descriptor of the RCS lockfile. - */ -{ - register char *tp; - register char const *sp, *RCSpath, *x; - RILE *f; - size_t l; - int e, exists, fdesc, fdescSafer, r, waslocked; - struct buf *dirt; - struct stat statbuf; - - waslocked = 0 <= fdlock; - exists = -# if has_readlink - resolve_symlink(RCSbuf); -# else - stat(RCSbuf->string, &statbuf) == 0 ? 1 - : errno==ENOENT ? 0 : -1; -# endif - if (exists < (mustread|waslocked)) - /* - * There's an unusual problem with the RCS file; - * or the RCS file doesn't exist, - * and we must read or we already have a lock elsewhere. - */ - return 0; - - RCSpath = RCSbuf->string; - sp = basefilename(RCSpath); - l = sp - RCSpath; - dirt = &dirtpname[waslocked]; - bufscpy(dirt, RCSpath); - tp = dirt->string + l; - x = rcssuffix(RCSpath); -# if has_readlink - if (!x) { - error("symbolic link to non RCS file `%s'", RCSpath); - errno = EINVAL; - return 0; - } -# endif - if (*sp == *x) { - error("RCS pathname `%s' incompatible with suffix `%s'", sp, x); - errno = EINVAL; - return 0; - } - /* Create a lock filename that is a function of the RCS filename. */ - if (*x) { - /* - * The suffix is nonempty. - * The lock filename is the first char of of the suffix, - * followed by the RCS filename with last char removed. E.g.: - * foo,v RCS filename with suffix ,v - * ,foo, lock filename - */ - *tp++ = *x; - while (*sp) - *tp++ = *sp++; - *--tp = 0; - } else { - /* - * The suffix is empty. - * The lock filename is the RCS filename - * with last char replaced by '_'. - */ - while ((*tp++ = *sp++)) - continue; - tp -= 2; - if (*tp == '_') { - error("RCS pathname `%s' ends with `%c'", RCSpath, *tp); - errno = EINVAL; - return 0; - } - *tp = '_'; - } - - sp = dirt->string; - - f = 0; - - /* - * good news: - * 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,OPEN_CREAT_READONLY) normally guarantees atomicity - * even with NFS. - * bad news: - * 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. - * 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") - * B sends create(",f,") - * A sends retry of rename(",f,", "f,v") - * server receives, does, and acknowledges A's first rename() - * A receives acknowledgment, and its RCS program exits - * server receives, does, and acknowledges B's create() - * server receives, does, and acknowledges A's retry of rename() - * 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 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, - * 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. - */ -# if !open_can_creat -# define create(f) creat(f, OPEN_CREAT_READONLY) -# else -# 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(); - ignoreints(); - - /* - * Create a lock file for an RCS file. This should be atomic, i.e. - * if two processes try it simultaneously, at most one should succeed. - */ - seteid(); - fdesc = create(sp); - fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr. */ - e = errno; - setrid(); - - if (0 <= fdesc) - dirtpmaker[0] = effective; - - if (fdescSafer < 0) { - if (e == EACCES && stat(sp,&statbuf) == 0) - /* The RCS file is busy. */ - e = EEXIST; - } else { - e = ENOENT; - if (exists) { - f = Iopen(RCSpath, FOPEN_RB, status); - e = errno; - if (f && waslocked) { - /* Discard the previous lock in favor of this one. */ - ORCSclose(); - seteid(); - r = un_link(lockname); - e = errno; - setrid(); - if (r != 0) - enfaterror(e, lockname); - bufscpy(&dirtpname[lockdirtp_index], sp); - } - } - fdlock = fdescSafer; - } - - restoreints(); - - errno = e; - return f; -} - - void -keepdirtemp(name) - char const *name; -/* Do not unlink name, either because it's not there any more, - * or because it has already been unlinked. - */ -{ - register int i; - for (i=DIRTEMPNAMES; 0<=--i; ) - if (dirtpname[i].string == name) { - dirtpmaker[i] = notmade; - return; - } - faterror("keepdirtemp"); -} - - char const * -makedirtemp(isworkfile) - int isworkfile; -/* - * 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 has_mktemp - int fd; -# endif - - dl = basefilename(name) - name; - bn = &dirtpname[newRCSdirtp_index + isworkfile]; - bufalloc(bn, -# if has_mktemp - dl + 9 -# else - strlen(name) + 3 -# endif - ); - bufscpy(bn, name); - np = tp = bn->string; - tp += dl; - *tp++ = '_'; - *tp++ = '0'+isworkfile; - catchints(); -# if has_mktemp - VOID strcpy(tp, "XXXXXX"); - fd = mkstemp(np); - if (fd < 0 || !*np) - faterror("can't make temporary pathname `%.*s_%cXXXXXX'", - (int)dl, name, '0'+isworkfile - ); - close(fd); -# else - /* - * Posix 1003.1-1990 has no reliable way - * to create a unique file in a named directory. - * We fudge here. If the filename is abcde, - * the temp filename is _Ncde where N is a digit. - */ - name += dl; - if (*name) name++; - if (*name) name++; - VOID strcpy(tp, name); -# endif - dirtpmaker[newRCSdirtp_index + isworkfile] = real; - return np; -} - - void -dirtempunlink() -/* Clean up makedirtemp() files. May be invoked by signal handler. */ -{ - register int i; - enum maker m; - - for (i = DIRTEMPNAMES; 0 <= --i; ) - if ((m = dirtpmaker[i]) != notmade) { - if (m == effective) - seteid(); - VOID un_link(dirtpname[i].string); - if (m == effective) - setrid(); - dirtpmaker[i] = notmade; - } -} - - - int -#if has_prototypes -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, set_mode, mode, mtime) - FILE **fromp; char const *from,*to; - int set_mode; mode_t mode; time_t mtime; -#endif -/* - * Rename a file (with stream pointer *FROMP) from FROM to TO. - * FROM already exists. - * 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 - /* - * 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 has_fchmod - if (0<set_mode && fchmod(fileno(*fromp),mode_while_renaming) == 0) - fchmod_set_mode = set_mode; -# endif - /* 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 - /* - * There's a short window of inconsistency - * during which TO does not exist. - */ - if (un_link(to) != 0 && errno != ENOENT) - return -1; -# 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 - -# 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); -} - - - - int -findlock(delete, target) - int delete; - struct hshentry **target; -/* - * Find the first lock held by caller and return a pointer - * to the locked delta; also removes the lock if DELETE. - * If one lock, put it into *TARGET. - * Return 0 for no locks, 1 for one, 2 for two or more. - */ -{ - register struct rcslock *next, **trail, **found; - - found = 0; - for (trail = &Locks; (next = *trail); trail = &next->nextlock) - if (strcmp(getcaller(), next->login) == 0) { - if (found) { - rcserror("multiple revisions locked by %s; please specify one", getcaller()); - return 2; - } - found = trail; - } - if (!found) - return 0; - next = *found; - *target = next->delta; - if (delete) { - next->delta->lockedby = 0; - *found = next->nextlock; - } - return 1; -} - - int -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 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 rcslock *next; - - for (next = Locks; next; next = next->nextlock) - if (cmpnum(delta->num, next->delta->num) == 0) - if (strcmp(getcaller(), next->login) == 0) - return 0; - else { - if (verbose) - rcserror("Revision %s is already locked by %s.", - delta->num, next->login - ); - return -1; - } - next = ftalloc(struct rcslock); - delta->lockedby = next->login = getcaller(); - next->delta = delta; - next->nextlock = Locks; - Locks = next; - return 1; -} - - - int -addsymbol(num, name, rebind) - char const *num, *name; - int 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 -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 (strcmp(next->num,num) == 0) - return 0; - else if (rebind) { - next->num = num; - return 1; - } else { - rcserror("symbolic name %s already bound to %s", - name, next->num - ); - return -1; - } - next = ftalloc(struct assoc); - next->symbol = name; - next->num = num; - next->nextassoc = Symbols; - Symbols = next; - return 1; -} - - - - char const * -getcaller() -/* Get the caller's login name. */ -{ -# if has_setuid - return getusername(euid()!=ruid()); -# else - return getusername(false); -# endif -} - - - int -checkaccesslist() -/* - * Return true if caller is the superuser, the owner of the - * file, the access list is empty, or caller is on the access list. - * Otherwise, print an error message and return false. - */ -{ - register struct access const *next; - - if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0) - return true; - - next = AccessList; - do { - if (strcmp(getcaller(), next->login) == 0) - return true; - } while ((next = next->nextaccess)); - - rcserror("user %s not on the access list", getcaller()); - return false; -} - - - int -dorewrite(lockflag, changed) - int lockflag, changed; -/* - * Do nothing if LOCKFLAG is zero. - * 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 0 on success, -1 on failure. - */ -{ - int r = 0, e; - - if (lockflag) - if (changed) { - if (changed < 0) - return -1; - putadmin(); - puttree(Head, frewrite); - aprintf(frewrite, "\n\n%s%c", Kdesc, nextc); - foutptr = frewrite; - } else { -# if bad_creat0 - int nr = !!frewrite, ne = 0; -# endif - ORCSclose(); - seteid(); - ignoreints(); -# if bad_creat0 - if (nr) { - nr = un_link(newRCSname); - ne = errno; - keepdirtemp(newRCSname); - } -# endif - r = un_link(lockname); - e = errno; - keepdirtemp(lockname); - restoreints(); - setrid(); - if (r != 0) - enerror(e, lockname); -# if bad_creat0 - if (nr != 0) { - enerror(ne, newRCSname); - r = -1; - } -# endif - } - return r; -} - - int -donerewrite(changed, newRCStime) - int changed; - time_t newRCStime; -/* - * Finish rewriting an RCS file if CHANGED is nonzero. - * 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 = 0, e = 0; -# if bad_creat0 - int lr, le; -# endif - - if (changed && !nerror) { - if (finptr) { - fastcopy(finptr, frewrite); - Izclose(&finptr); - } - if (1 < RCSstat.st_nlink) - rcswarn("breaking hard link"); - aflush(frewrite); - seteid(); - ignoreints(); - r = chnamemod( - &frewrite, newRCSname, RCSname, changed, - RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH), - newRCStime - ); - e = errno; - keepdirtemp(newRCSname); -# if bad_creat0 - lr = un_link(lockname); - le = errno; - keepdirtemp(lockname); -# endif - restoreints(); - setrid(); - if (r != 0) { - enerror(e, RCSname); - error("saved in %s", newRCSname); - } -# if bad_creat0 - if (lr != 0) { - enerror(le, lockname); - r = -1; - } -# endif - } - return r; -} - - void -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 (0 <= fdlock) - VOID close(fdlock); - if (frewrite) - /* Avoid fclose, since stdio may not be reentrant. */ - VOID close(fileno(frewrite)); -} |