summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src/rcs.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cvs/src/rcs.c')
-rw-r--r--contrib/cvs/src/rcs.c417
1 files changed, 316 insertions, 101 deletions
diff --git a/contrib/cvs/src/rcs.c b/contrib/cvs/src/rcs.c
index 951736e..64524b1 100644
--- a/contrib/cvs/src/rcs.c
+++ b/contrib/cvs/src/rcs.c
@@ -153,6 +153,26 @@ static const char spacetab[] = {
#define whitespace(c) (spacetab[(unsigned char)c] != 0)
+static char *rcs_lockfile;
+
+/* A few generic thoughts on error handling, in particular the
+ printing of unexpected characters that we find in the RCS file
+ (that is, why we use '\x%x' rather than %c or some such).
+
+ * Avoiding %c means we don't have to worry about what is printable
+ and other such stuff. In error handling, often better to keep it
+ simple.
+
+ * Hex rather than decimal or octal because character set standards
+ tend to use hex.
+
+ * Saying "character 0x%x" might make it sound like we are printing
+ a file offset. So we use '\x%x'.
+
+ * Would be nice to print the offset within the file, but I can
+ imagine various portability hassles (in particular, whether
+ unsigned long is always big enough to hold file offsets). */
+
/* Parse an rcsfile given a user file name and a repository. If there is
an error, we print an error message and return NULL. If the file
does not exist, we return NULL without printing anything (I'm not
@@ -364,7 +384,9 @@ RCS_parsercsfile_i (fp, rcsfile)
break;
}
- for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
+ for (cp = key;
+ (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
+ cp++)
/* do nothing */ ;
if (*cp == '\0')
break;
@@ -498,7 +520,9 @@ RCS_reparsercsfile (rdata, pfp, rcsbufp)
* revision or `desc', we are done with the headers and are down to the
* revision deltas, so we break out of the loop
*/
- for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
+ for (cp = key;
+ (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
+ cp++)
/* do nothing */ ;
/* Note that when comparing with RCSDATE, we are not massaging
VALUE from the string found in the RCS file. This is OK
@@ -583,6 +607,98 @@ RCS_reparsercsfile (rdata, pfp, rcsbufp)
rdata->flags &= ~PARTIAL;
}
+/* Move RCS into or out of the Attic, depending on TOATTIC. If the
+ file is already in the desired place, return without doing
+ anything. At some point may want to think about how this relates
+ to RCS_rewrite but that is a bit hairy (if one wants renames to be
+ atomic, or that kind of thing). If there is an error, print a message
+ and return 1. On success, return 0. */
+int
+RCS_setattic (rcs, toattic)
+ RCSNode *rcs;
+ int toattic;
+{
+ char *newpath;
+ char *p;
+ char *q;
+
+ /* Some systems aren't going to let us rename an open file. */
+ rcsbuf_cache_close ();
+
+ /* Could make the pathname computations in this file, and probably
+ in other parts of rcs.c too, easier if the REPOS and FILE
+ arguments to RCS_parse got stashed in the RCSNode. */
+
+ if (toattic)
+ {
+ mode_t omask;
+
+ if (rcs->flags & INATTIC)
+ return 0;
+
+ /* Example: rcs->path is "/foo/bar/baz,v". */
+ newpath = xmalloc (strlen (rcs->path) + sizeof CVSATTIC + 5);
+ p = last_component (rcs->path);
+ strncpy (newpath, rcs->path, p - rcs->path);
+ strcpy (newpath + (p - rcs->path), CVSATTIC);
+
+ /* Create the Attic directory if it doesn't exist. */
+ omask = umask (cvsumask);
+ if (CVS_MKDIR (newpath, 0777) < 0 && errno != EEXIST)
+ error (0, errno, "cannot make directory %s", newpath);
+ (void) umask (omask);
+
+ strcat (newpath, "/");
+ strcat (newpath, p);
+
+ if (CVS_RENAME (rcs->path, newpath) < 0)
+ {
+ int save_errno = errno;
+
+ /* The checks for isreadable look awfully fishy, but
+ I'm going to leave them here for now until I
+ can think harder about whether they take care of
+ some cases which should be handled somehow. */
+
+ if (isreadable (rcs->path) || !isreadable (newpath))
+ {
+ error (0, save_errno, "cannot rename %s to %s",
+ rcs->path, newpath);
+ free (newpath);
+ return 1;
+ }
+ }
+ }
+ else
+ {
+ if (!(rcs->flags & INATTIC))
+ return 0;
+
+ newpath = xmalloc (strlen (rcs->path));
+
+ /* Example: rcs->path is "/foo/bar/Attic/baz,v". */
+ p = last_component (rcs->path);
+ strncpy (newpath, rcs->path, p - rcs->path - 1);
+ newpath[p - rcs->path - 1] = '\0';
+ q = newpath + (p - rcs->path - 1) - (sizeof CVSATTIC - 1);
+ assert (strncmp (q, CVSATTIC, sizeof CVSATTIC - 1) == 0);
+ strcpy (q, p);
+
+ if (CVS_RENAME (rcs->path, newpath) < 0)
+ {
+ error (0, errno, "failed to move `%s' out of the attic",
+ rcs->path);
+ free (newpath);
+ return 1;
+ }
+ }
+
+ free (rcs->path);
+ rcs->path = newpath;
+
+ return 0;
+}
+
/*
* Fully parse the RCS file. Store all keyword/value pairs, fetch the
* log messages for each revision, and fetch add and delete counts for
@@ -671,7 +787,8 @@ warning: duplicate key `%s' in version `%s' of RCS file `%s'",
op = *cp++;
if (op != 'a' && op != 'd')
- error (1, 0, "unrecognized operation '%c' in %s",
+ error (1, 0, "\
+unrecognized operation '\\x%x' in %s",
op, rcs->path);
(void) strtoul (cp, (char **) &cp, 10);
if (*cp++ != ' ')
@@ -1479,7 +1596,8 @@ rcsbuf_getstring (rcsbuf, strp)
/* PTR should now point to the start of a string. */
if (c != '@')
- error (1, 0, "expected @-string at `%c' in %s", c, rcsbuf->filename);
+ error (1, 0, "expected @-string at '\\x%x' in %s",
+ c, rcsbuf->filename);
/* Optimize the common case of a value composed of a single
'@' string. */
@@ -1736,7 +1854,7 @@ rcsbuf_getword (rcsbuf, wordp)
printing character that is not a special.' This test ought
to do the trick. */
c = *ptr;
- if (isprint (c) &&
+ if (isprint ((unsigned char) c) &&
c != ';' && c != '$' && c != ',' && c != '@' && c != ':')
{
++ptr;
@@ -1804,9 +1922,10 @@ rcsbuf_getrevnum (rcsbuf, revp)
++ptr;
}
- if (! isdigit (c) && c != '.')
+ if (! isdigit ((unsigned char) c) && c != '.')
error (1, 0,
- "unexpected `%c' reading revision number in RCS file %s",
+ "\
+unexpected '\\x%x' reading revision number in RCS file %s",
c, rcsbuf->filename);
*revp = ptr;
@@ -1826,10 +1945,11 @@ rcsbuf_getrevnum (rcsbuf, revp)
c = *ptr;
}
- while (isdigit (c) || c == '.');
+ while (isdigit ((unsigned char) c) || c == '.');
if (! whitespace (c))
- error (1, 0, "unexpected `%c' reading revision number in RCS file %s",
+ error (1, 0, "\
+unexpected '\\x%x' reading revision number in RCS file %s",
c, rcsbuf->filename);
*ptr = '\0';
@@ -2337,7 +2457,7 @@ RCS_getversion (rcs, tag, date, force_tag_match, simple_tag)
}
/* Work out the branch. */
- if (! isdigit (tag[0]))
+ if (! isdigit ((unsigned char) tag[0]))
branch = RCS_whatbranch (rcs, tag);
else
branch = xstrdup (tag);
@@ -2402,6 +2522,16 @@ RCS_tag2rev (rcs, tag)
}
}
+ /* Try for a real (that is, exists in the RCS deltas) branch
+ (RCS_exist_rev just checks for real revisions and revisions
+ which have tags pointing to them). */
+ pa = RCS_getbranch (rcs, rev, 1);
+ if (pa != NULL)
+ {
+ free (pa);
+ return rev;
+ }
+
/* Tag is branch, but does not exist, try corresponding
* magic branch tag.
*
@@ -2425,7 +2555,7 @@ RCS_tag2rev (rcs, tag)
RCS_check_tag (tag); /* exit if not a valid tag */
/* If tag is "HEAD", special case to get head RCS revision */
- if (tag && (strcmp (tag, TAG_HEAD) == 0))
+ if (tag && STREQ (tag, TAG_HEAD))
return (RCS_head (rcs));
/* If valid tag let translate_symtag say yea or nay. */
@@ -2478,7 +2608,7 @@ RCS_gettag (rcs, symtag, force_tag_match, simple_tag)
#endif
return (RCS_head (rcs));
- if (!isdigit (tag[0]))
+ if (!isdigit ((unsigned char) tag[0]))
{
char *version;
@@ -2675,7 +2805,7 @@ RCS_isbranch (rcs, rev)
const char *rev;
{
/* numeric revisions are easy -- even number of dots is a branch */
- if (isdigit (*rev))
+ if (isdigit ((unsigned char) *rev))
return ((numdots (rev) & 1) == 0);
/* assume a revision if you can't find the RCS info */
@@ -2702,7 +2832,7 @@ RCS_nodeisbranch (rcs, rev)
assert (rcs != NULL);
/* numeric revisions are easy -- even number of dots is a branch */
- if (isdigit (*rev))
+ if (isdigit ((unsigned char) *rev))
return ((numdots (rev) & 1) == 0);
version = translate_symtag (rcs, rev);
@@ -2929,7 +3059,7 @@ RCS_branch_head (rcs, rev)
if (RCS_nodeisbranch (rcs, rev))
return RCS_getbranch (rcs, rev, 1);
- if (isdigit (*rev))
+ if (isdigit ((unsigned char) *rev))
num = xstrdup (rev);
else
{
@@ -3101,8 +3231,23 @@ RCS_getdate (rcs, date, force_tag_match)
*/
/* if we found what we're looking for, and it's not 1.1 return it */
- if (cur_rev != NULL && ! STREQ (cur_rev, "1.1"))
- return (xstrdup (cur_rev));
+ if (cur_rev != NULL)
+ {
+ if (! STREQ (cur_rev, "1.1"))
+ return (xstrdup (cur_rev));
+
+ /* This is 1.1; if the date of 1.1 is not the same as that for the
+ 1.1.1.1 version, then return 1.1. This happens when the first
+ version of a file is created by a regular cvs add and commit,
+ and there is a subsequent cvs import of the same file. */
+ p = findnode (rcs->versions, "1.1.1.1");
+ if (p)
+ {
+ vers = (RCSVers *) p->data;
+ if (RCS_datecmp (vers->date, date) != 0)
+ return xstrdup ("1.1");
+ }
+ }
/* look on the vendor branch */
retval = RCS_getdatebranch (rcs, date, CVSBRANCH);
@@ -3454,11 +3599,11 @@ RCS_check_tag (tag)
* characters cannot be non-visible graphic characters, and must not be
* in the set of "invalid" RCS identifier characters.
*/
- if (isalpha (*tag))
+ if (isalpha ((unsigned char) *tag))
{
for (cp = tag; *cp; cp++)
{
- if (!isgraph (*cp))
+ if (!isgraph ((unsigned char) *cp))
error (1, 0, "tag `%s' has non-visible graphic characters",
tag);
if (strchr (invalid, *cp))
@@ -3485,7 +3630,7 @@ RCS_valid_rev (rev)
{
char last, c;
last = *rev++;
- if (!isdigit (last))
+ if (!isdigit ((unsigned char) last))
return 0;
while ((c = *rev++)) /* Extra parens placate -Wall gcc option */
{
@@ -3496,10 +3641,10 @@ RCS_valid_rev (rev)
continue;
}
last = c;
- if (!isdigit (c))
+ if (!isdigit ((unsigned char) c))
return 0;
}
- if (!isdigit (last))
+ if (!isdigit ((unsigned char) last))
return 0;
return 1;
}
@@ -3535,10 +3680,26 @@ char *
RCS_getexpand (rcs)
RCSNode *rcs;
{
+ /* Since RCS_parsercsfile_i now reads expand, don't need to worry
+ about RCS_reparsercsfile. */
assert (rcs != NULL);
return rcs->expand;
}
+/* Set keyword expansion mode to EXPAND. For example "b" for binary. */
+void
+RCS_setexpand (rcs, expand)
+ RCSNode *rcs;
+ char *expand;
+{
+ /* Since RCS_parsercsfile_i now reads expand, don't need to worry
+ about RCS_reparsercsfile. */
+ assert (rcs != NULL);
+ if (rcs->expand != NULL)
+ free (rcs->expand);
+ rcs->expand = xstrdup (expand);
+}
+
/* RCS keywords, and a matching enum. */
struct rcs_keyword
{
@@ -3740,7 +3901,7 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen)
/* Look for the first non alphabetic character after the '$'. */
send = srch + srch_len;
for (s = srch; s < send; s++)
- if (! isalpha (*s))
+ if (! isalpha ((unsigned char) *s))
break;
/* If the first non alphabetic character is not '$' or ':',
@@ -3843,7 +4004,7 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen)
break;
case KEYWORD_NAME:
- if (name != NULL && ! isdigit (*name))
+ if (name != NULL && ! isdigit ((unsigned char) *name))
value = (char *) name;
else
value = NULL;
@@ -4186,7 +4347,7 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat)
: (sout != RUN_TTY ? sout : "(stdout)"))));
}
- assert (rev == NULL || isdigit (*rev));
+ assert (rev == NULL || isdigit ((unsigned char) *rev));
if (noexec && workfile != NULL)
return 0;
@@ -4434,9 +4595,9 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat)
error (1, 0, "%s:%s has bad `special' newphrase %s",
workfile, vers->version, info->data);
devnum = devnum_long;
- if (strcmp (devtype, "character") == 0)
+ if (STREQ (devtype, "character"))
special_file = S_IFCHR;
- else if (strcmp (devtype, "block") == 0)
+ else if (STREQ (devtype, "block"))
special_file = S_IFBLK;
else
error (0, 0, "%s is a special file of unsupported type `%s'",
@@ -4997,6 +5158,9 @@ RCS_checkin (rcs, workfile, message, rev, flags)
struct tm *ftm;
time_t modtime;
int adding_branch = 0;
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ struct stat sb;
+#endif
commitpt = NULL;
@@ -5017,40 +5181,11 @@ RCS_checkin (rcs, workfile, message, rev, flags)
allocated_workfile = 1;
}
- /* Is the backend file a symbolic link? Follow it and replace the
- filename with the destination of the link. */
-
- while (islink (rcs->path))
- {
- char *newname;
-#ifdef HAVE_READLINK
- /* The clean thing to do is probably to have each filesubr.c
- implement this (with an error if not supported by the
- platform, in which case islink would presumably return 0).
- But that would require editing each filesubr.c and so the
- expedient hack seems to be looking at HAVE_READLINK. */
- newname = xreadlink (rcs->path);
-#else
- error (1, 0, "internal error: islink doesn't like readlink");
-#endif
-
- if (isabsolute (newname))
- {
- free (rcs->path);
- rcs->path = newname;
- }
- else
- {
- char *oldname = last_component (rcs->path);
- int dirlen = oldname - rcs->path;
- char *fullnewname = xmalloc (dirlen + strlen (newname) + 1);
- strncpy (fullnewname, rcs->path, dirlen);
- strcpy (fullnewname + dirlen, newname);
- free (newname);
- free (rcs->path);
- rcs->path = fullnewname;
- }
- }
+ /* If the filename is a symbolic link, follow it and replace it
+ with the destination of the link. We need to do this before
+ calling rcs_internal_lockfile, or else we won't put the lock in
+ the right place. */
+ resolve_symlink (&(rcs->path));
checkin_quiet = flags & RCS_FLAGS_QUIET;
if (!checkin_quiet)
@@ -5096,7 +5231,6 @@ RCS_checkin (rcs, workfile, message, rev, flags)
if (preserve_perms)
{
Node *np;
- struct stat sb;
char buf[64]; /* static buffer should be safe: see usage. -twp */
delta->other_delta = getlist();
@@ -5195,6 +5329,12 @@ RCS_checkin (rcs, workfile, message, rev, flags)
dtext->version = xstrdup (newrev);
bufsize = 0;
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ if (preserve_perms && !S_ISREG (sb.st_mode))
+ /* Pretend file is empty. */
+ bufsize = 0;
+ else
+#endif
get_file (workfile, workfile,
rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
&dtext->text, &bufsize, &dtext->len);
@@ -5277,7 +5417,7 @@ RCS_checkin (rcs, workfile, message, rev, flags)
char *branch, *tip, *newrev, *p;
int dots, isrevnum;
- assert (isdigit(*rev));
+ assert (isdigit ((unsigned char) *rev));
newrev = xstrdup (rev);
dots = numdots (newrev);
@@ -5432,6 +5572,12 @@ RCS_checkin (rcs, workfile, message, rev, flags)
/* If this revision is being inserted on the trunk, the change text
for the new delta should be the contents of the working file ... */
bufsize = 0;
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ if (preserve_perms && !S_ISREG (sb.st_mode))
+ /* Pretend file is empty. */
+ ;
+ else
+#endif
get_file (workfile, workfile,
rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
&dtext->text, &bufsize, &dtext->len);
@@ -6506,8 +6652,23 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive)
char *diffbuf;
size_t bufsize, len;
+#if defined (__CYGWIN32__) || defined (_WIN32)
+ /* FIXME: This is an awful kludge, but at least until I have
+ time to work on it a little more and test it, I'd rather
+ give a fatal error than corrupt the file. I think that we
+ need to use "-kb" and "--binary" and "rb" to get_file
+ (probably can do it always, not just for binary files, if
+ we are consistent between the RCS_checkout and the diff). */
+ {
+ char *expand = RCS_getexpand (rcs);
+ if (expand != NULL && STREQ (expand, "b"))
+ error (1, 0,
+ "admin -o not implemented yet for binary on this system");
+ }
+#endif
+
afterfile = cvs_temp_name();
- status = RCS_checkout (rcs, NULL, after, NULL, NULL, afterfile,
+ status = RCS_checkout (rcs, NULL, after, NULL, "-ko", afterfile,
(RCSCHECKOUTPROC)0, NULL);
if (status > 0)
goto delrev_done;
@@ -6535,13 +6696,13 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive)
else
{
beforefile = cvs_temp_name();
- status = RCS_checkout (rcs, NULL, before, NULL, NULL, beforefile,
+ status = RCS_checkout (rcs, NULL, before, NULL, "-ko", beforefile,
(RCSCHECKOUTPROC)0, NULL);
if (status > 0)
goto delrev_done;
outfile = cvs_temp_name();
- status = diff_exec (beforefile, afterfile, "-n", outfile);
+ status = diff_exec (beforefile, afterfile, "-an", outfile);
if (status == 2)
{
@@ -6991,7 +7152,7 @@ apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers)
we define a deltafrag as an add or a delete) need to be applied
in reverse order. So we stick them into a linked list. */
struct deltafrag {
- enum {ADD, DELETE} type;
+ enum {FRAG_ADD, FRAG_DELETE} type;
unsigned long pos;
unsigned long nlines;
const char *new_lines;
@@ -7008,7 +7169,8 @@ apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers)
if (op != 'a' && op != 'd')
/* Can't just skip over the deltafrag, because the value
of op determines the syntax. */
- error (1, 0, "unrecognized operation '%c' in %s", op, name);
+ error (1, 0, "unrecognized operation '\\x%x' in %s",
+ op, name);
df = (struct deltafrag *) xmalloc (sizeof (struct deltafrag));
df->next = dfhead;
dfhead = df;
@@ -7030,7 +7192,7 @@ apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers)
{
unsigned int i;
- df->type = ADD;
+ df->type = FRAG_ADD;
i = df->nlines;
/* The text we want is the number of lines specified, or
until the end of the value, whichever comes first (it
@@ -7060,7 +7222,7 @@ apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers)
--df->pos;
assert (op == 'd');
- df->type = DELETE;
+ df->type = FRAG_DELETE;
}
}
@@ -7070,12 +7232,12 @@ apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers)
switch (df->type)
{
- case ADD:
+ case FRAG_ADD:
if (! linevector_add (lines, df->new_lines, df->len, addvers,
df->pos))
return 0;
break;
- case DELETE:
+ case FRAG_DELETE:
if (df->pos > lines->nlines
|| df->pos + df->nlines > lines->nlines)
return 0;
@@ -7534,7 +7696,9 @@ getdelta (rcsbuf, rcsfile, keyp, valp)
/* Make sure that it is a revision number and not a cabbage
or something. */
- for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
+ for (cp = key;
+ (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
+ cp++)
/* do nothing */ ;
/* Note that when comparing with RCSDATE, we are not massaging
VALUE from the string found in the RCS file. This is OK since
@@ -7718,7 +7882,9 @@ unable to parse %s; `state' not in the expected place", rcsfile);
continue;
}
/* if we have a new revision number, we're done with this delta */
- for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
+ for (cp = key;
+ (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
+ cp++)
/* do nothing */ ;
/* Note that when comparing with RCSDATE, we are not massaging
VALUE from the string found in the RCS file. This is OK
@@ -8300,6 +8466,30 @@ count_delta_actions (np, ignore)
return 0;
}
+/*
+ * Clean up temporary files
+ */
+static RETSIGTYPE
+rcs_cleanup ()
+{
+ /* Note that the checks for existence_error are because we are
+ called from a signal handler, so we don't know whether the
+ files got created. */
+
+ /* FIXME: Do not perform buffered I/O from an interrupt handler like
+ this (via error). However, I'm leaving the error-calling code there
+ in the hope that on the rare occasion the error call is actually made
+ (e.g., a fluky I/O error or permissions problem prevents the deletion
+ of a just-created file) reentrancy won't be an issue. */
+ if (rcs_lockfile != NULL)
+ {
+ if (unlink_file (rcs_lockfile) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", rcs_lockfile);
+ }
+ rcs_lockfile = NULL;
+}
+
/* RCS_internal_lockfile and RCS_internal_unlockfile perform RCS-style
locking on the specified RCSFILE: for a file called `foo,v', open
for writing a file called `,foo,'.
@@ -8324,10 +8514,6 @@ count_delta_actions (np, ignore)
processes from stomping all over each other's laundry. Hence,
they are `internal' locking functions.
- Note that we don't clean up the ,foo, file on ^C. We probably should.
- I'm not completely sure whether RCS does or not (I looked at the code
- a little, and didn't find it).
-
If there is an error, give a fatal error; if we return we always
return a non-NULL value. */
@@ -8335,13 +8521,35 @@ static FILE *
rcs_internal_lockfile (rcsfile)
char *rcsfile;
{
- char *lockfile;
int fd;
struct stat rstat;
FILE *fp;
+ static int first_call = 1;
+
+ if (first_call)
+ {
+ first_call = 0;
+ /* clean up if we get a signal */
+#ifdef SIGHUP
+ (void) SIG_register (SIGHUP, rcs_cleanup);
+#endif
+#ifdef SIGINT
+ (void) SIG_register (SIGINT, rcs_cleanup);
+#endif
+#ifdef SIGQUIT
+ (void) SIG_register (SIGQUIT, rcs_cleanup);
+#endif
+#ifdef SIGPIPE
+ (void) SIG_register (SIGPIPE, rcs_cleanup);
+#endif
+#ifdef SIGTERM
+ (void) SIG_register (SIGTERM, rcs_cleanup);
+#endif
+ }
/* Get the lock file name: `,file,' for RCS file `file,v'. */
- lockfile = rcs_lockfilename (rcsfile);
+ assert (rcs_lockfile == NULL);
+ rcs_lockfile = rcs_lockfilename (rcsfile);
/* Use the existing RCS file mode, or read-only if this is a new
file. (Really, this is a lie -- if this is a new file,
@@ -8367,12 +8575,13 @@ rcs_internal_lockfile (rcsfile)
rely on O_EXCL these days. This might be true for unix (I
don't really know), but I am still pretty skeptical in the case
of the non-unix systems. */
- fd = open (lockfile, OPEN_BINARY | O_WRONLY | O_CREAT | O_EXCL | O_TRUNC,
+ fd = open (rcs_lockfile,
+ OPEN_BINARY | O_WRONLY | O_CREAT | O_EXCL | O_TRUNC,
S_IRUSR | S_IRGRP | S_IROTH);
if (fd < 0)
{
- error (1, errno, "could not open lock file `%s'", lockfile);
+ error (1, errno, "could not open lock file `%s'", rcs_lockfile);
}
/* Force the file permissions, and return a stream object. */
@@ -8380,13 +8589,11 @@ rcs_internal_lockfile (rcsfile)
this in the non-HAVE_FCHMOD case. */
#ifdef HAVE_FCHMOD
if (fchmod (fd, rstat.st_mode) < 0)
- error (1, errno, "cannot change mode for %s", lockfile);
+ error (1, errno, "cannot change mode for %s", rcs_lockfile);
#endif
fp = fdopen (fd, FOPEN_BINARY_WRITE);
if (fp == NULL)
- error (1, errno, "cannot fdopen %s", lockfile);
-
- free (lockfile);
+ error (1, errno, "cannot fdopen %s", rcs_lockfile);
return fp;
}
@@ -8396,10 +8603,7 @@ rcs_internal_unlockfile (fp, rcsfile)
FILE *fp;
char *rcsfile;
{
- char *lockfile;
-
- /* Get the lock file name: `,file,' for RCS file `file,v'. */
- lockfile = rcs_lockfilename (rcsfile);
+ assert (rcs_lockfile != NULL);
/* Abort if we could not write everything successfully to LOCKFILE.
This is not a great error-handling mechanism, but should prevent
@@ -8412,12 +8616,21 @@ rcs_internal_unlockfile (fp, rcsfile)
fragile even if it happens to sometimes be true. The real
solution is to check each call to fprintf rather than waiting
until the end like this. */
- error (1, 0, "error writing to lock file %s", lockfile);
+ error (1, 0, "error writing to lock file %s", rcs_lockfile);
if (fclose (fp) == EOF)
- error (1, errno, "error closing lock file %s", lockfile);
+ error (1, errno, "error closing lock file %s", rcs_lockfile);
- rename_file (lockfile, rcsfile);
- free (lockfile);
+ rename_file (rcs_lockfile, rcsfile);
+
+ {
+ /* Use a temporary to make sure there's no interval
+ (after rcs_lockfile has been freed but before it's set to NULL)
+ during which the signal handler's use of rcs_lockfile would
+ reference freed memory. */
+ char *tmp = rcs_lockfile;
+ rcs_lockfile = NULL;
+ free (tmp);
+ }
}
static char *
@@ -8460,6 +8673,9 @@ RCS_rewrite (rcs, newdtext, insertpt)
if (noexec)
return;
+ /* Make sure we're operating on an actual file and not a symlink. */
+ resolve_symlink (&(rcs->path));
+
fout = rcs_internal_lockfile (rcs->path);
RCS_putadmin (rcs, fout);
@@ -8538,8 +8754,8 @@ annotate_fileproc (callerdat, finfo)
cvs_outerr (finfo->fullname, 0);
cvs_outerr ("\n***************\n", 0);
- RCS_deltas (finfo->rcs, fp, rcsbufp, version, RCS_ANNOTATE, (char **) NULL,
- (size_t) NULL, (char **) NULL, (size_t *) NULL);
+ RCS_deltas (finfo->rcs, fp, rcsbufp, version, RCS_ANNOTATE, NULL,
+ NULL, NULL, NULL);
free (version);
return 0;
}
@@ -8612,8 +8828,8 @@ annotate (argc, argv)
option_with_arg ("-r", tag);
if (date)
client_senddate (date);
- send_file_names (argc, argv, SEND_EXPAND_WILD);
send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
send_to_server ("annotate\012", 0);
return get_responses_and_close ();
}
@@ -8649,7 +8865,7 @@ make_file_label (path, rev, rcs)
char *file;
file = last_component (path);
- label = (char *) xmalloc (strlen (file)
+ label = (char *) xmalloc (strlen (path)
+ (rev == NULL ? 0 : strlen (rev))
+ 50);
@@ -8658,7 +8874,7 @@ make_file_label (path, rev, rcs)
char *date;
RCS_getrevtime (rcs, rev, datebuf, 0);
date = printable_date (datebuf);
- (void) sprintf (label, "-L%s\t%s\t%s", file, date, rev);
+ (void) sprintf (label, "-L%s\t%s\t%s", path, date, rev);
free (date);
}
else
@@ -8675,9 +8891,8 @@ make_file_label (path, rev, rcs)
wm->tm_year + 1900, wm->tm_mon + 1,
wm->tm_mday, wm->tm_hour,
wm->tm_min, wm->tm_sec);
- (void) sprintf (label, "-L%s\t%s", file, datebuf);
+ (void) sprintf (label, "-L%s\t%s", path, datebuf);
}
}
return label;
}
-
OpenPOWER on IntegriCloud