summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/cvs
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/cvs')
-rw-r--r--gnu/usr.bin/cvs/doc/RCSFILES172
-rw-r--r--gnu/usr.bin/cvs/man/cvs.112
-rw-r--r--gnu/usr.bin/cvs/src/cvs.h2
-rw-r--r--gnu/usr.bin/cvs/src/expand_path.c407
-rw-r--r--gnu/usr.bin/cvs/src/import.c12
-rw-r--r--gnu/usr.bin/cvs/src/log.c9
-rw-r--r--gnu/usr.bin/cvs/src/main.c23
-rw-r--r--gnu/usr.bin/cvs/src/rcs.c12
-rw-r--r--gnu/usr.bin/cvs/src/status.c275
9 files changed, 720 insertions, 204 deletions
diff --git a/gnu/usr.bin/cvs/doc/RCSFILES b/gnu/usr.bin/cvs/doc/RCSFILES
index 0ac61aa1d42..35e30ab5b61 100644
--- a/gnu/usr.bin/cvs/doc/RCSFILES
+++ b/gnu/usr.bin/cvs/doc/RCSFILES
@@ -1,4 +1,4 @@
-It would be nice for the RCS file format (which is implemented by a
+It would be nice if the RCS file format (which is implemented by a
great many tools, both free and non-free, both by calling GNU RCS and
by reimplementing access to RCS files) were documented in some
standard separate from any one tool. But as far as I know no such
@@ -24,12 +24,37 @@ several other output formats. If you just want some source code to
look at, the part of CVS which applies these is RCS_deltas in
src/rcs.c.
-The first time I read rcsfile.5 I didn't really notice the part about
-the order of the revisions. This order _is_ important and CVS relies
-on it. It is documented but it would be clearer if the example in
-rcsfile.5 also showed the order of the revisions (and the "next" and
-"branch" fields and anything else where it would be useful to have an
-example of how a revision tree is represented in an RCS file).
+The rcsfile.5 documentation only _very_ briefly touches on the order
+of the revisions. The order _is_ important and CVS relies on it.
+Here is an example of what I was able to find, based on the join3
+sanity.sh testcase (and the behavior I am documenting here seems to be
+the same for RCS 5.7 and CVS 1.9.27):
+
+ 1.1 -----------------> 1.2
+ \---> 1.1.2.1 \---> 1.2.2.1
+
+Here is how this shows up in the RCS file (omitting irrelevant parts):
+
+ admin: head 1.2;
+ deltas:
+ 1.2 branches 1.2.2.1; next 1.1;
+ 1.1 branches 1.1.2.1; next;
+ 1.1.2.1 branches; next;
+ 1.2.2.1 branches; next;
+ deltatexts:
+ 1.2
+ 1.2.2.1
+ 1.1
+ 1.1.2.1
+
+Yes, the order seems to differ between the deltas and the deltatexts.
+I have no idea how much of this should actually be considered part of
+the RCS file format, and how much programs reading it should expect to
+encounter any order.
+
+The rcsfile.5 grammar shows the {num} after "next" as optional; if it
+is omitted then there is no next delta node (for example 1.1 or the
+head of a branch will typically have no next).
There is one case where CVS uses CVS-specific, non-compatible changes
to the RCS file format, and this is magic branches. See cvs.texinfo
@@ -37,6 +62,139 @@ for more information on them. CVS also sets the RCS state to "dead"
to indicate that a file does not exist in a given revision (this is
stored just as any other RCS state is).
+The RCS file format allows quite a variety of extensions to be added
+in a compatible manner by use of the "newphrase" feature documented in
+rcsfile.5. We won't try to document extensions not used by CVS in any
+detail, but we will briefly list them. Each occurrence of a newphrase
+begins with an identifier, which is what we list here. Future
+designers of extensions are strongly encouraged to pick
+non-conflicting identifiers. Note that newphrase occurs several
+places in the RCS grammar, and a given extension may not be legal in
+all locations. However, it seems better to reserve a particular
+identifier for all locations, to avoid confusion and complicated
+rules.
+
+ Identifier Used by
+ ---------- -------
+ namespace RCS library done at Silicon Graphics Inc. (SGI) in 1996
+ (a modified RCS 5.7--not sure it has any other name).
+ dead A set of RCS patches developed by Rich Pixley at
+ Cygnus about 1992. These were for CVS, and predated
+ the current CVS death support, which uses a state "dead"
+ rather than a "dead" newphrase.
+
+CVS does use newphrases to implement the `PreservePermissions'
+extension introduced in CVS 1.9.26. The following new keywords are
+defined when PreservePermissions=yes:
+
+ owner
+ group
+ permissions
+ special
+ symlink
+ hardlinks
+
+The contents of the `owner' and `group' field should be a numeric uid
+and a numeric gid, respectively, representing the user and group who
+own the file. The `permissions' field contains an octal integer,
+representing the permissions that should be applied to the file. The
+`special' field contains two words; the first must be either `block'
+or `character', and the second is the file's device number. The
+`symlink' field should be present only in files which are symbolic
+links to other files, and absent on all regular files. The
+`hardlinks' field contains a list of filenames to which the current
+file is linked, in alphabetical order. Because files often contain
+characters special to RCS, like `.' and sometimes even contain spaces
+or eight-bit characters, the filenames in the hardlinks field will
+usually be enclosed in RCS strings. For example:
+
+ hardlinks README @install.txt@ @Installation Notes@;
+
+The hardlinks field should always include the name of the current
+file. That is, in the repository file README,v, any hardlinks fields
+in the delta nodes should include `README'; CVS will not operate
+properly if this is not done.
+
+Newphrases are also used to implement the 'commitid' feature. The
+following new keyword is defined:
+
+ commitid
+
+The rules regarding keyword expansion are not documented along with
+the rest of the RCS file format; they are documented in the co(1)
+manpage in the RCS 5.7 distribution. See also the "Keyword
+substitution" chapter of cvs.texinfo. The co(1) manpage refers to
+special behavior if the log prefix for the $Log keyword is /* or (*.
+RCS 5.7 produces a warning whenever it behaves that way, and current
+versions of CVS do not handle this case in a special way (CVS 1.9 and
+earlier invoke RCS to perform keyword expansion).
+
+Note that if the "expand" keyword is omitted from the RCS file, the
+default is "kv".
+
+Note that the "comment {string};" syntax from rcsfile.5 specifies a
+comment leader, which affects expansion of the $Log keyword for old
+versions of RCS. The comment leader is not used by RCS 5.7 or current
+versions of CVS.
+
+Both RCS 5.7 and current versions of CVS handle the $Log keyword in a
+different way if the log message starts with "checked in with -k by ".
+I don't think this behavior is documented anywhere.
+
+Here is a clarification regarding characters versus bytes in certain
+character sets like JIS and Big5:
+
+ The RCS file format, as described in the rcsfile(5) man page, is
+ actually byte-oriented, not character-oriented, despite hints to
+ the contrary in the man page. This distinction is important for
+ multibyte characters. For example, if a multibyte character
+ contains a `@' byte, the `@' must be doubled within strings in RCS
+ files, since RCS uses `@' bytes as escapes.
+
+ This point is not an issue for encodings like ISO 8859, which do
+ not have multibyte characters. Nor is it an issue for encodings
+ like UTF-8 and EUC-JIS, which never uses ASCII bytes within a
+ multibyte character. It is an issue only for multibyte encodings
+ like JIS and BIG5, which _do_ usurp ASCII bytes.
+
+ If `@' doubling occurs within a multibyte char, the resulting RCS
+ file is not a properly encoded text file. Instead, it is a byte
+ stream that does not use a consistent character encoding that can
+ be understood by the usual text tools, since doubling `@' messes
+ up the encoding. This point affects only programs that examine
+ the RCS files -- it doesn't affect the external RCS interface, as
+ the RCS commands always give you the properly encoded text files
+ and logs (assuming that you always check in properly encoded
+ text).
+
+ CVS 1.10 (and earlier) probably has some bugs in this area on
+ systems where a C "char" is signed and where the data contains
+ bytes with the eighth bit set.
+
+One common concern about the RCS file format is the fact that to get
+the head of a branch, one must apply deltas from the head of the trunk
+to the branchpoint, and then from the branchpoint to the head of the
+branch. While more detailed analyses might be worth doing, we will
+note:
+
+ * The performance bottleneck for CVS generally is figuring out which
+ files to operate on and that sort of thing, not applying deltas.
+
+ * Here is one quick test (probably not a very good test; a better test
+ would use a normally sized file (say 50-200K) instead of a small one):
+
+ I just did a quick test with a small file (on a Sun Ultra 1/170E
+ running Solaris 5.5.1), with 1000 revisions on the main branch and
+ 1000 revisions on branch that forked at the root (i.e., RCS revisions
+ 1.1, 1.2, ..., 1.1000, and branch revisions 1.1.1.1, 1.1.1.2, ...,
+ 1.1.1.1000). It took about 0.15 seconds real time to check in the
+ first revision, and about 0.6 seconds to check in and 0.3 seconds to
+ retrieve revision 1.1.1.1000 (the worst case).
+
+ * Any attempt to "fix" this problem should be careful not to interfere
+ with other features, such as lightweight creation of branches
+ (particularly using CVS magic branches).
+
Diff follows:
(Note that in the following diff the old value for the Id keyword was:
diff --git a/gnu/usr.bin/cvs/man/cvs.1 b/gnu/usr.bin/cvs/man/cvs.1
index b582e687f3b..58fb3ee566a 100644
--- a/gnu/usr.bin/cvs/man/cvs.1
+++ b/gnu/usr.bin/cvs/man/cvs.1
@@ -966,6 +966,18 @@ option, thus suppressing the editor invocation, or use the
.B \-F
option to specify that the argument \fIfile\fP contains the log message.
.sp
+At
+.B commit
+a unique commitid is placed in the
+.SM rcs
+file inside the repository.
+All files committed at once get the same commitid.
+The commitid can be retrieved with the
+.B log
+and
+.B status
+commands.
+.sp
The
.B \-r
option can be used to commit to a particular symbolic or numeric revision.
diff --git a/gnu/usr.bin/cvs/src/cvs.h b/gnu/usr.bin/cvs/src/cvs.h
index cfcb5a24350..30ed9da4068 100644
--- a/gnu/usr.bin/cvs/src/cvs.h
+++ b/gnu/usr.bin/cvs/src/cvs.h
@@ -907,3 +907,5 @@ extern void cvs_outerr PROTO ((const char *, size_t));
extern void cvs_flusherr PROTO ((void));
extern void cvs_flushout PROTO ((void));
extern void cvs_output_tagged PROTO ((char *, char *));
+
+extern char *global_session_id;
diff --git a/gnu/usr.bin/cvs/src/expand_path.c b/gnu/usr.bin/cvs/src/expand_path.c
index f63ddff0035..e4baf774bb0 100644
--- a/gnu/usr.bin/cvs/src/expand_path.c
+++ b/gnu/usr.bin/cvs/src/expand_path.c
@@ -15,120 +15,323 @@
#include "cvs.h"
#include <sys/types.h>
-static char *expand_variable PROTO((char *env));
-extern char *xmalloc ();
-extern void free ();
+static char *expand_variable PROTO((char *env, char *file, int line));
-/* char *expand_pathname(char *name)
- *
- * This routine will expand the pathname to account for ~
- * and $ characters as described above. If an error occurs, NULL
- * is returned.
- * Will only expand Built in CVS variables all others are ignored.
- */
+
+/* User variables. */
+
+List *variable_list = NULL;
+
+static void variable_delproc PROTO ((Node *));
+
+static void
+variable_delproc (node)
+ Node *node;
+{
+ free (node->data);
+}
+
+/* Currently used by -s option; we might want a way to set user
+ variables in a file in the $CVSROOT/CVSROOT directory too. */
+
+void
+variable_set (nameval)
+ char *nameval;
+{
+ char *p;
+ char *name;
+ Node *node;
+
+ p = nameval;
+ while (isalnum ((unsigned char) *p) || *p == '_')
+ ++p;
+ if (*p != '=')
+ error (1, 0, "illegal character in user variable name in %s", nameval);
+ if (p == nameval)
+ error (1, 0, "empty user variable name in %s", nameval);
+ name = xmalloc (p - nameval + 1);
+ strncpy (name, nameval, p - nameval);
+ name[p - nameval] = '\0';
+ /* Make p point to the value. */
+ ++p;
+ if (strchr (p, '\012') != NULL)
+ error (1, 0, "linefeed in user variable value in %s", nameval);
+
+ if (variable_list == NULL)
+ variable_list = getlist ();
+
+ node = findnode (variable_list, name);
+ if (node == NULL)
+ {
+ node = getnode ();
+ node->type = VARIABLE;
+ node->delproc = variable_delproc;
+ node->key = name;
+ node->data = xstrdup (p);
+ (void) addnode (variable_list, node);
+ }
+ else
+ {
+ /* Replace the old value. For example, this means that -s
+ options on the command line override ones from .cvsrc. */
+ free (node->data);
+ node->data = xstrdup (p);
+ free (name);
+ }
+}
+
+/* This routine will expand the pathname to account for ~ and $
+ characters as described above. Returns a pointer to a newly
+ malloc'd string. If an error occurs, an error message is printed
+ via error() and NULL is returned. FILE and LINE are the filename
+ and linenumber to include in the error message. FILE must point
+ to something; LINE can be zero to indicate the line number is not
+ known. */
char *
-expand_path (name)
+expand_path (name, file, line)
char *name;
+ char *file;
+ int line;
{
- char *s;
- char *d;
- char mybuf[PATH_MAX];
- char buf[PATH_MAX];
- char *result;
- s = name;
- d = mybuf;
- while ((*d++ = *s))
- if (*s++ == '$')
- {
- char *p = d;
- char *e;
- int flag = (*s == '{');
-
- for (; (*d++ = *s); s++)
- if (flag ? *s =='}' :
- isalnum (*s) == 0 && *s!='_' )
- break;
- *--d = 0;
- e = expand_variable (&p[flag]);
-
- if (e)
- {
- for (d = &p[-1]; (*d++ = *e++);)
- ;
- --d;
- if (flag && *s)
- s++;
- }
- else
- return NULL; /* no env variable */
- }
- *d = 0;
- s = mybuf;
- d = buf;
- /* If you don't want ~username ~/ to be expanded simply remove
- * This entire if statement including the else portion
- */
- if (*s++ == '~')
+ char *s;
+ char *d;
+
+ char *mybuf = NULL;
+ size_t mybuf_size = 0;
+ char *buf = NULL;
+ size_t buf_size = 0;
+
+ size_t doff;
+
+ char *result;
+
+ /* Sorry this routine is so ugly; it is a head-on collision
+ between the `traditional' unix *d++ style and the need to
+ dynamically allocate. It would be much cleaner (and probably
+ faster, not that this is a bottleneck for CVS) with more use of
+ strcpy & friends, but I haven't taken the effort to rewrite it
+ thusly. */
+
+ /* First copy from NAME to MYBUF, expanding $<foo> as we go. */
+ s = name;
+ d = mybuf;
+ doff = d - mybuf;
+ expand_string (&mybuf, &mybuf_size, doff + 1);
+ d = mybuf + doff;
+ while ((*d++ = *s))
+ {
+ if (*s++ == '$')
{
- char *t;
- char *p=s;
- if (*s=='/' || *s==0)
- t = getenv ("HOME");
- else
+ char *p = d;
+ char *e;
+ int flag = (*s == '{');
+
+ doff = d - mybuf;
+ expand_string (&mybuf, &mybuf_size, doff + 1);
+ d = mybuf + doff;
+ for (; (*d++ = *s); s++)
+ {
+ if (flag
+ ? *s =='}'
+ : isalnum ((unsigned char) *s) == 0 && *s != '_')
+ break;
+ doff = d - mybuf;
+ expand_string (&mybuf, &mybuf_size, doff + 1);
+ d = mybuf + doff;
+ }
+ *--d = '\0';
+ e = expand_variable (&p[flag], file, line);
+
+ if (e)
+ {
+ doff = d - mybuf;
+ expand_string (&mybuf, &mybuf_size, doff + 1);
+ d = mybuf + doff;
+ for (d = &p[-1]; (*d++ = *e++);)
{
- struct passwd *ps;
- for (; *p!='/' && *p; p++)
- ;
- *p = 0;
- ps = getpwnam (s);
- if (ps == 0)
- return NULL; /* no such user */
- t = ps->pw_dir;
- }
- while ((*d++ = *t++))
- ;
+ doff = d - mybuf;
+ expand_string (&mybuf, &mybuf_size, doff + 1);
+ d = mybuf + doff;
+ }
--d;
- if (*p == 0)
- *p = '/'; /* always add / */
- s=p;
+ if (flag && *s)
+ s++;
+ }
+ else
+ /* expand_variable has already printed an error message. */
+ goto error_exit;
}
+ doff = d - mybuf;
+ expand_string (&mybuf, &mybuf_size, doff + 1);
+ d = mybuf + doff;
+ }
+ doff = d - mybuf;
+ expand_string (&mybuf, &mybuf_size, doff + 1);
+ d = mybuf + doff;
+ *d = '\0';
+
+ /* Then copy from MYBUF to BUF, expanding ~. */
+ s = mybuf;
+ d = buf;
+ /* If you don't want ~username ~/ to be expanded simply remove
+ * This entire if statement including the else portion
+ */
+ if (*s++ == '~')
+ {
+ char *t;
+ char *p=s;
+ if (*s=='/' || *s==0)
+ t = get_homedir ();
else
- --s;
- /* Kill up to here */
- while ((*d++ = *s++))
- ;
- *d=0;
- result = xmalloc (sizeof(char) * strlen(buf)+1);
- strcpy (result, buf);
- return result;
+ {
+#ifdef GETPWNAM_MISSING
+ for (; *p!='/' && *p; p++)
+ ;
+ *p = 0;
+ if (line != 0)
+ error (0, 0,
+ "%s:%d:tilde expansion not supported on this system",
+ file, line);
+ else
+ error (0, 0, "%s:tilde expansion not supported on this system",
+ file);
+ return NULL;
+#else
+ struct passwd *ps;
+ for (; *p!='/' && *p; p++)
+ ;
+ *p = 0;
+ ps = getpwnam (s);
+ if (ps == 0)
+ {
+ if (line != 0)
+ error (0, 0, "%s:%d: no such user %s",
+ file, line, s);
+ else
+ error (0, 0, "%s: no such user %s", file, s);
+ return NULL;
+ }
+ t = ps->pw_dir;
+#endif
+ }
+ if (t == NULL)
+ error (1, 0, "cannot find home directory");
+
+ doff = d - buf;
+ expand_string (&buf, &buf_size, doff + 1);
+ d = buf + doff;
+ while ((*d++ = *t++))
+ {
+ doff = d - buf;
+ expand_string (&buf, &buf_size, doff + 1);
+ d = buf + doff;
+ }
+ --d;
+ if (*p == 0)
+ *p = '/'; /* always add / */
+ s=p;
+ }
+ else
+ --s;
+ /* Kill up to here */
+ doff = d - buf;
+ expand_string (&buf, &buf_size, doff + 1);
+ d = buf + doff;
+ while ((*d++ = *s++))
+ {
+ doff = d - buf;
+ expand_string (&buf, &buf_size, doff + 1);
+ d = buf + doff;
+ }
+ doff = d - buf;
+ expand_string (&buf, &buf_size, doff + 1);
+ d = buf + doff;
+ *d = '\0';
+
+ /* OK, buf contains the value we want to return. Clean up and return
+ it. */
+ free (mybuf);
+ /* Save a little memory with xstrdup; buf will tend to allocate
+ more than it needs to. */
+ result = xstrdup (buf);
+ free (buf);
+ return result;
+
+ error_exit:
+ if (mybuf != NULL)
+ free (mybuf);
+ if (buf != NULL)
+ free (buf);
+ return NULL;
}
+
static char *
-expand_variable (name)
- char *name;
+expand_variable (name, file, line)
+ char *name;
+ char *file;
+ int line;
{
- /* There is nothing expanding this function to allow it
- * to read a file in the $CVSROOT/CVSROOT directory that
- * says which environmental variables could be expanded
- * or just say everything is fair game to be expanded
- */
- if ( strcmp (name, CVSROOT_ENV) == 0 )
- return CVSroot;
- else
- if ( strcmp (name, RCSBIN_ENV) == 0 )
- return Rcsbin;
+ if (strcmp (name, CVSROOT_ENV) == 0)
+ return current_parsed_root->original;
+ else if (strcmp (name, "RCSBIN") == 0)
+ {
+ error (0, 0, "RCSBIN internal variable is no longer supported");
+ return NULL;
+ }
+ else if (strcmp (name, EDITOR1_ENV) == 0)
+ return Editor;
+ else if (strcmp (name, EDITOR2_ENV) == 0)
+ return Editor;
+ else if (strcmp (name, EDITOR3_ENV) == 0)
+ return Editor;
+ else if (strcmp (name, "USER") == 0)
+ return getcaller ();
+ else if (strcmp (name, "SESSIONID") == 0 || strcmp (name, "COMMITID") == 0)
+ return global_session_id;
+ else if (isalpha ((unsigned char) name[0]))
+ {
+ /* These names are reserved for future versions of CVS,
+ so that is why it is an error. */
+ if (line != 0)
+ error (0, 0, "%s:%d: no such internal variable $%s",
+ file, line, name);
else
- if ( strcmp (name, EDITOR1_ENV) == 0 )
- return Editor;
- else
- if ( strcmp (name, EDITOR2_ENV) == 0 )
- return Editor;
- else
- if ( strcmp (name, EDITOR3_ENV) == 0 )
- return Editor;
+ error (0, 0, "%s: no such internal variable $%s",
+ file, name);
+ return NULL;
+ }
+ else if (name[0] == '=')
+ {
+ Node *node;
+ /* Crazy syntax for a user variable. But we want
+ *something* that lets the user name a user variable
+ anything he wants, without interference from
+ (existing or future) internal variables. */
+ node = findnode (variable_list, name + 1);
+ if (node == NULL)
+ {
+ if (line != 0)
+ error (0, 0, "%s:%d: no such user variable ${%s}",
+ file, line, name);
+ else
+ error (0, 0, "%s: no such user variable ${%s}",
+ file, name);
+ return NULL;
+ }
+ return node->data;
+ }
+ else
+ {
+ /* It is an unrecognized character. We return an error to
+ reserve these for future versions of CVS; it is plausible
+ that various crazy syntaxes might be invented for inserting
+ information about revisions, branches, etc. */
+ if (line != 0)
+ error (0, 0, "%s:%d: unrecognized variable syntax %s",
+ file, line, name);
else
- return NULL;
- /* The code here could also just
- * return whatever getenv would
- * return.
- */
+ error (0, 0, "%s: unrecognized variable syntax %s",
+ file, name);
+ return NULL;
+ }
}
diff --git a/gnu/usr.bin/cvs/src/import.c b/gnu/usr.bin/cvs/src/import.c
index 84a80cac36f..5699e2a2260 100644
--- a/gnu/usr.bin/cvs/src/import.c
+++ b/gnu/usr.bin/cvs/src/import.c
@@ -1198,6 +1198,11 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
if (fprintf (fprcs, "next ;\012") < 0)
goto write_error;
+#ifdef notyet
+ if (fprintf (fprcs, "commitid %s;\012", global_session_id) < 0)
+ goto write_error;
+#endif
+
#ifdef PRESERVE_PERMISSIONS_SUPPORT
/* Store initial permissions if necessary. */
if (preserve_perms)
@@ -1253,7 +1258,12 @@ userfile);
fprintf (fprcs, "date %s; author %s; state Exp;\012",
altdate1, author) < 0 ||
fprintf (fprcs, "branches ;\012") < 0 ||
- fprintf (fprcs, "next ;\012") < 0)
+ fprintf (fprcs, "next ;\012") < 0
+#ifdef notyet
+ ||
+ fprintf (fprcs, "commitid %s;\012", global_session_id) < 0
+#endif
+ )
goto write_error;
#ifdef PRESERVE_PERMISSIONS_SUPPORT
diff --git a/gnu/usr.bin/cvs/src/log.c b/gnu/usr.bin/cvs/src/log.c
index 096b229ab44..f7a906e4213 100644
--- a/gnu/usr.bin/cvs/src/log.c
+++ b/gnu/usr.bin/cvs/src/log.c
@@ -1553,6 +1553,15 @@ log_version (log_data, revlist, rcs, ver, trunk)
cvs_output (padd->data, 0);
cvs_output (" -", 2);
cvs_output (pdel->data, 0);
+ cvs_output (";", 1);
+ }
+
+ p = findnode (ver->other_delta,"commitid");
+ if (p != NULL && p->data)
+ {
+ cvs_output (" commitid: ", 12);
+ cvs_output (p->data, 0);
+ cvs_output (";", 1);
}
if (ver->branches != NULL)
diff --git a/gnu/usr.bin/cvs/src/main.c b/gnu/usr.bin/cvs/src/main.c
index 5a2df80b1d6..9c1f514afb0 100644
--- a/gnu/usr.bin/cvs/src/main.c
+++ b/gnu/usr.bin/cvs/src/main.c
@@ -25,6 +25,8 @@ char *program_name;
char *program_path;
char *command_name;
+char *global_session_id; /* Random session ID */
+
/* I'd dynamically allocate this, but it seems like gethostname
requires a fixed size array. If I'm remembering the RFCs right,
256 should be enough. */
@@ -641,6 +643,27 @@ Copyright (c) 1989-2001 Brian Berliner, david d `zoo' zuhn, \n\
if (argc < 1)
usage (usg);
+ /* Generate the cvs global session ID */
+
+ {
+ int i = 0;
+ u_int32_t c;
+ global_session_id = xmalloc(17);
+
+ while (i <= 16) {
+ c = arc4random_uniform(75) + 48;
+ if ((c >= 48 && c <= 57) || (c >= 65 && c <= 90) ||
+ (c >= 97 && c <= 122)) {
+ global_session_id[i] = c;
+ i++;
+ }
+ }
+ global_session_id[16] = '\0';
+ }
+
+ if (trace)
+ fprintf (stderr, "main: Session ID is %s", global_session_id);
+
/* Look up the command name. */
diff --git a/gnu/usr.bin/cvs/src/rcs.c b/gnu/usr.bin/cvs/src/rcs.c
index 6b003353e51..a21aba51234 100644
--- a/gnu/usr.bin/cvs/src/rcs.c
+++ b/gnu/usr.bin/cvs/src/rcs.c
@@ -4849,6 +4849,7 @@ RCS_checkin (rcs, workfile, message, rev, flags)
#ifdef PRESERVE_PERMISSIONS_SUPPORT
struct stat sb;
#endif
+ Node *np;
commitpt = NULL;
@@ -4913,6 +4914,17 @@ RCS_checkin (rcs, workfile, message, rev, flags)
else
delta->state = xstrdup ("Exp");
+ delta->other_delta = getlist();
+
+ /* save the commit ID */
+ np = getnode();
+ np->type = RCSFIELD;
+ np->key = xstrdup ("commitid");
+ np->data = xstrdup(global_session_id);
+#ifdef notyet
+ addnode (delta->other_delta, np);
+#endif
+
#ifdef PRESERVE_PERMISSIONS_SUPPORT
/* If permissions should be preserved on this project, then
save the permission info. */
diff --git a/gnu/usr.bin/cvs/src/status.c b/gnu/usr.bin/cvs/src/status.c
index d987a91ac8c..1f5ae830b58 100644
--- a/gnu/usr.bin/cvs/src/status.c
+++ b/gnu/usr.bin/cvs/src/status.c
@@ -3,28 +3,22 @@
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
- * specified in the README file that comes with the CVS 1.4 kit.
+ * specified in the README file that comes with the CVS source distribution.
*
* Status Information
*/
#include "cvs.h"
-#ifndef lint
-static const char rcsid[] = "$CVSid: @(#)status.c 1.56 94/10/07 $";
-USE(rcsid);
-#endif
-
-static Dtype status_dirproc PROTO((char *dir, char *repos, char *update_dir));
-static int status_fileproc PROTO((char *file, char *update_dir,
- char *repository, List * entries,
- List * srcfiles));
+static Dtype status_dirproc PROTO ((void *callerdat, char *dir,
+ char *repos, char *update_dir,
+ List *entries));
+static int status_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static int tag_list_proc PROTO((Node * p, void *closure));
static int local = 0;
static int long_format = 0;
-static char *xfile;
-static List *xsrcfiles;
+static RCSNode *xrcsnode;
static const char *const status_usage[] =
{
@@ -32,11 +26,12 @@ static const char *const status_usage[] =
"\t-v\tVerbose format; includes tag information for the file\n",
"\t-l\tProcess this directory only (not recursive).\n",
"\t-R\tProcess directories recursively.\n",
+ "(Specify the --help global option for a list of other help options)\n",
NULL
};
int
-status (argc, argv)
+cvsstatus (argc, argv)
int argc;
char **argv;
{
@@ -46,8 +41,8 @@ status (argc, argv)
if (argc == -1)
usage (status_usage);
- optind = 1;
- while ((c = getopt (argc, argv, "vlR")) != -1)
+ optind = 0;
+ while ((c = getopt (argc, argv, "+vlR")) != -1)
{
switch (c)
{
@@ -72,32 +67,46 @@ status (argc, argv)
wrap_setup ();
#ifdef CLIENT_SUPPORT
- if (client_active) {
- start_server ();
+ if (current_parsed_root->isremote)
+ {
+ start_server ();
- ign_setup ();
+ ign_setup ();
- if (long_format)
- send_arg("-v");
- if (local)
- send_arg("-l");
+ if (long_format)
+ send_arg("-v");
+ if (local)
+ send_arg("-l");
- /* XXX This should only need to send file info; the file
- contents themselves will not be examined. */
- send_files (argc, argv, local, 0);
+ /* For a while, we tried setting SEND_NO_CONTENTS here so this
+ could be a fast operation. That prevents the
+ server from updating our timestamp if the timestamp is
+ changed but the file is unmodified. Worse, it is user-visible
+ (shows "locally modified" instead of "up to date" if
+ timestamp is changed but file is not). And there is no good
+ workaround (you might not want to run "cvs update"; "cvs -n
+ update" doesn't update CVS/Entries; "cvs diff --brief" or
+ something perhaps could be made to work but somehow that
+ seems nonintuitive to me even if so). Given that timestamps
+ seem to have the potential to get munged for any number of
+ reasons, it seems better to not rely too much on them. */
- if (fprintf (to_server, "status\n") < 0)
- error (1, errno, "writing to server");
- err = get_responses_and_close ();
+ send_files (argc, argv, local, 0, 0);
- return err;
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
+
+ send_to_server ("status\012", 0);
+ err = get_responses_and_close ();
+
+ return err;
}
#endif
/* start the recursion processor */
- err = start_recursion (status_fileproc, (int (*) ()) NULL, status_dirproc,
- (int (*) ()) NULL, argc, argv, local,
- W_LOCAL, 0, 1, (char *) NULL, 1, 0);
+ err = start_recursion (status_fileproc, (FILESDONEPROC) NULL,
+ status_dirproc, (DIRLEAVEPROC) NULL, NULL,
+ argc, argv, local,
+ W_LOCAL, 0, 1, (char *) NULL, 1);
return (err);
}
@@ -107,20 +116,18 @@ status (argc, argv)
*/
/* ARGSUSED */
static int
-status_fileproc (file, update_dir, repository, entries, srcfiles)
- char *file;
- char *update_dir;
- char *repository;
- List *entries;
- List *srcfiles;
+status_fileproc (callerdat, finfo)
+ void *callerdat;
+ struct file_info *finfo;
{
Ctype status;
char *sstat;
Vers_TS *vers;
+ Node *node;
- status = Classify_File (file, (char *) NULL, (char *) NULL, (char *) NULL,
- 1, 0, repository, entries, srcfiles, &vers,
- update_dir, 0);
+ status = Classify_File (finfo, (char *) NULL, (char *) NULL, (char *) NULL,
+ 1, 0, &vers, 0);
+ sstat = "Classify Error";
switch (status)
{
case T_UNKNOWN:
@@ -129,12 +136,13 @@ status_fileproc (file, update_dir, repository, entries, srcfiles)
case T_CHECKOUT:
sstat = "Needs Checkout";
break;
-#ifdef SERVER_SUPPORT
case T_PATCH:
sstat = "Needs Patch";
break;
-#endif
case T_CONFLICT:
+ /* I _think_ that "unresolved" is correct; that if it has
+ been resolved then the status will change. But I'm not
+ sure about that. */
sstat = "Unresolved Conflict";
break;
case T_ADDED:
@@ -145,7 +153,7 @@ status_fileproc (file, update_dir, repository, entries, srcfiles)
break;
case T_MODIFIED:
if (vers->ts_conflict)
- sstat = "Unresolved Conflict";
+ sstat = "File had conflicts on merge";
else
sstat = "Locally Modified";
break;
@@ -158,34 +166,80 @@ status_fileproc (file, update_dir, repository, entries, srcfiles)
case T_NEEDS_MERGE:
sstat = "Needs Merge";
break;
- default:
- sstat = "Classify Error";
+ case T_TITLE:
+ /* I don't think this case can occur here. Just print
+ "Classify Error". */
break;
}
- (void) printf ("===================================================================\n");
+ cvs_output ("\
+===================================================================\n", 0);
if (vers->ts_user == NULL)
- (void) printf ("File: no file %s\t\tStatus: %s\n\n", file, sstat);
+ {
+ cvs_output ("File: no file ", 0);
+ cvs_output (finfo->file, 0);
+ cvs_output ("\t\tStatus: ", 0);
+ cvs_output (sstat, 0);
+ cvs_output ("\n\n", 0);
+ }
else
- (void) printf ("File: %-17s\tStatus: %s\n\n", file, sstat);
+ {
+ char *buf;
+ buf = xmalloc (strlen (finfo->file) + strlen (sstat) + 80);
+ sprintf (buf, "File: %-17s\tStatus: %s\n\n", finfo->file, sstat);
+ cvs_output (buf, 0);
+ free (buf);
+ }
if (vers->vn_user == NULL)
- (void) printf (" Working revision:\tNo entry for %s\n", file);
+ {
+ cvs_output (" Working revision:\tNo entry for ", 0);
+ cvs_output (finfo->file, 0);
+ cvs_output ("\n", 0);
+ }
else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
- (void) printf (" Working revision:\tNew file!\n");
+ cvs_output (" Working revision:\tNew file!\n", 0);
#ifdef SERVER_SUPPORT
else if (server_active)
- (void) printf (" Working revision:\t%s\n", vers->vn_user);
+ {
+ cvs_output (" Working revision:\t", 0);
+ cvs_output (vers->vn_user, 0);
+ cvs_output ("\n", 0);
+ }
#endif
else
- (void) printf (" Working revision:\t%s\t%s\n", vers->vn_user,
- vers->ts_rcs);
+ {
+ cvs_output (" Working revision:\t", 0);
+ cvs_output (vers->vn_user, 0);
+ cvs_output ("\t", 0);
+ cvs_output (vers->ts_rcs, 0);
+ cvs_output ("\n", 0);
+ }
if (vers->vn_rcs == NULL)
- (void) printf (" Repository revision:\tNo revision control file\n");
+ cvs_output (" Repository revision:\tNo revision control file\n", 0);
else
- (void) printf (" Repository revision:\t%s\t%s\n", vers->vn_rcs,
- vers->srcfile->path);
+ {
+ cvs_output (" Repository revision:\t", 0);
+ cvs_output (vers->vn_rcs, 0);
+ cvs_output ("\t", 0);
+ cvs_output (vers->srcfile->path, 0);
+ cvs_output ("\n", 0);
+
+ node = findnode(vers->srcfile->versions,vers->vn_rcs);
+ if (node)
+ {
+ RCSVers *v;
+ v=(RCSVers*)node->data;
+ node = findnode(v->other_delta,"commitid");
+ cvs_output(" Commit Identifier:\t", 0);
+ if(node && node->data)
+ cvs_output(node->data, 0);
+ else
+ cvs_output("(none)",0);
+ cvs_output("\n",0);
+ }
+ }
if (vers->entdata)
{
@@ -195,56 +249,76 @@ status_fileproc (file, update_dir, repository, entries, srcfiles)
if (edata->tag)
{
if (vers->vn_rcs == NULL)
- (void) printf (
- " Sticky Tag:\t\t%s - MISSING from RCS file!\n",
- edata->tag);
+ {
+ cvs_output (" Sticky Tag:\t\t", 0);
+ cvs_output (edata->tag, 0);
+ cvs_output (" - MISSING from RCS file!\n", 0);
+ }
else
{
- if (isdigit (edata->tag[0]))
- (void) printf (" Sticky Tag:\t\t%s\n", edata->tag);
+ if (isdigit ((unsigned char) edata->tag[0]))
+ {
+ cvs_output (" Sticky Tag:\t\t", 0);
+ cvs_output (edata->tag, 0);
+ cvs_output ("\n", 0);
+ }
else
{
- int isbranch = RCS_isbranch (file, edata->tag, srcfiles);
-
- (void) printf (" Sticky Tag:\t\t%s (%s: %s)\n",
- edata->tag,
- isbranch ? "branch" : "revision",
- isbranch ?
- RCS_whatbranch(file, edata->tag, srcfiles) :
- vers->vn_rcs);
+ char *branch = NULL;
+
+ if (RCS_nodeisbranch (finfo->rcs, edata->tag))
+ branch = RCS_whatbranch(finfo->rcs, edata->tag);
+
+ cvs_output (" Sticky Tag:\t\t", 0);
+ cvs_output (edata->tag, 0);
+ cvs_output (" (", 0);
+ cvs_output (branch ? "branch" : "revision", 0);
+ cvs_output (": ", 0);
+ cvs_output (branch ? branch : vers->vn_rcs, 0);
+ cvs_output (")\n", 0);
+
+ if (branch)
+ free (branch);
}
}
}
else if (!really_quiet)
- (void) printf (" Sticky Tag:\t\t(none)\n");
+ cvs_output (" Sticky Tag:\t\t(none)\n", 0);
if (edata->date)
- (void) printf (" Sticky Date:\t\t%s\n", edata->date);
+ {
+ cvs_output (" Sticky Date:\t\t", 0);
+ cvs_output (edata->date, 0);
+ cvs_output ("\n", 0);
+ }
else if (!really_quiet)
- (void) printf (" Sticky Date:\t\t(none)\n");
+ cvs_output (" Sticky Date:\t\t(none)\n", 0);
if (edata->options && edata->options[0])
- (void) printf (" Sticky Options:\t%s\n", edata->options);
+ {
+ cvs_output (" Sticky Options:\t", 0);
+ cvs_output (edata->options, 0);
+ cvs_output ("\n", 0);
+ }
else if (!really_quiet)
- (void) printf (" Sticky Options:\t(none)\n");
+ cvs_output (" Sticky Options:\t(none)\n", 0);
+ }
- if (long_format && vers->srcfile)
- {
- List *symbols = RCS_symbols(vers->srcfile);
+ if (long_format && vers->srcfile)
+ {
+ List *symbols = RCS_symbols(vers->srcfile);
- (void) printf ("\n Existing Tags:\n");
- if (symbols)
- {
- xfile = file;
- xsrcfiles = srcfiles;
- (void) walklist (symbols, tag_list_proc, NULL);
- }
- else
- (void) printf ("\tNo Tags Exist\n");
+ cvs_output ("\n Existing Tags:\n", 0);
+ if (symbols)
+ {
+ xrcsnode = finfo->rcs;
+ (void) walklist (symbols, tag_list_proc, NULL);
}
+ else
+ cvs_output ("\tNo Tags Exist\n", 0);
}
- (void) printf ("\n");
+ cvs_output ("\n", 0);
freevers_ts (&vers);
return (0);
}
@@ -254,10 +328,12 @@ status_fileproc (file, update_dir, repository, entries, srcfiles)
*/
/* ARGSUSED */
static Dtype
-status_dirproc (dir, repos, update_dir)
+status_dirproc (callerdat, dir, repos, update_dir, entries)
+ void *callerdat;
char *dir;
char *repos;
char *update_dir;
+ List *entries;
{
if (!quiet)
error (0, 0, "Examining %s", update_dir);
@@ -272,11 +348,22 @@ tag_list_proc (p, closure)
Node *p;
void *closure;
{
- int isbranch = RCS_isbranch (xfile, p->key, xsrcfiles);
+ char *branch = NULL;
+ char *buf;
+
+ if (RCS_nodeisbranch (xrcsnode, p->key))
+ branch = RCS_whatbranch(xrcsnode, p->key) ;
+
+ buf = xmalloc (80 + strlen (p->key)
+ + (branch ? strlen (branch) : strlen (p->data)));
+ sprintf (buf, "\t%-25s\t(%s: %s)\n", p->key,
+ branch ? "branch" : "revision",
+ branch ? branch : p->data);
+ cvs_output (buf, 0);
+ free (buf);
+
+ if (branch)
+ free (branch);
- (void) printf ("\t%-25.25s\t(%s: %s)\n", p->key,
- isbranch ? "branch" : "revision",
- isbranch ? RCS_whatbranch(xfile, p->key, xsrcfiles) :
- p->data);
return (0);
}