diff options
Diffstat (limited to 'gnu/usr.bin/cvs')
-rw-r--r-- | gnu/usr.bin/cvs/doc/RCSFILES | 172 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/man/cvs.1 | 12 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/cvs.h | 2 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/expand_path.c | 407 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/import.c | 12 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/log.c | 9 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/main.c | 23 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/rcs.c | 12 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/status.c | 275 |
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); } |