summaryrefslogtreecommitdiffstats
path: root/gnu/usr.bin/rcs/lib/rcsedit.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>1995-10-28 21:50:58 +0000
committerpeter <peter@FreeBSD.org>1995-10-28 21:50:58 +0000
commit822bceb624e3740412b90941feb1158d82f50edc (patch)
tree5072ccf03f140b698e07703cd04f14e6e4d75591 /gnu/usr.bin/rcs/lib/rcsedit.c
parent9d7e3d8dc0cccad8e42e4822ffa52ee28b6f7d2f (diff)
downloadFreeBSD-src-822bceb624e3740412b90941feb1158d82f50edc.zip
FreeBSD-src-822bceb624e3740412b90941feb1158d82f50edc.tar.gz
First part of import conflict merge from rcs-5.7 import.
All those $Log$ entries, combined with the whitespace changes are a real pain. I'm committing this now, before it's completely finished to get it compiling and working again ASAP. Some of the FreeBSD specific features are not working in this commit yet (mainly rlog stuff and $FreeBSD$ support)
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 32ff7e2..79408f3 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,55 @@ 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$.
+/*
+ * $Log: rcsedit.c,v $
+ * 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 +202,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 5.19 1995/06/16 06:19:24 eggert 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 +244,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 +271,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 +313,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 +334,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 +379,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 +403,7 @@ snapshotline(f, l)
do {
if ((c = *l++) == SDELIM && *l++ != SDELIM)
return;
- aputc(c, f);
+ aputc_(c, f)
} while (c != '\n');
}
@@ -371,8 +426,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 +449,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 +479,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 +512,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 +523,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 +533,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 +551,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 +569,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 +577,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 +604,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 +629,7 @@ copystring()
fcop = fcopy;
amidline = false;
for (;;) {
- GETC(frew,c);
+ GETC_(frew,c)
switch (c) {
case '\n':
++editline;
@@ -582,7 +637,7 @@ copystring()
amidline = false;
break;
case SDELIM:
- GETC(frew,c);
+ GETC_(frew,c)
if (c != SDELIM) {
/* end of string */
nextc = c;
@@ -595,7 +650,7 @@ copystring()
amidline = true;
break;
}
- aputc(c,fcop);
+ aputc_(c,fcop)
}
}
@@ -605,18 +660,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 +685,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 +696,7 @@ enterstring()
amidline = false;
break;
case SDELIM:
- GETC(frew,c);
+ GETC_(frew,c)
if (c != SDELIM) {
/* end of string */
nextc = c;
@@ -675,13 +730,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 +747,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 +781,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 +797,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 +812,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 +826,11 @@ edit_string()
return;
}
}
+# if !large_memory
+ aputc_(c, f)
+# endif
+ if (c == '\n')
+ break;
}
++rcsline;
} while (--i);
@@ -790,17 +846,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 +880,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 +897,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 +914,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 +941,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 +967,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 +986,228 @@ 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 Id:
+ case Header:
+ escape_string(out,
+ marker==Id || 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));
}
OpenPOWER on IntegriCloud