summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/rcs/lib/rcsedit.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/rcs/lib/rcsedit.c')
-rw-r--r--gnu/usr.bin/rcs/lib/rcsedit.c997
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));
}