[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Cvs-cvs] ccvs/src ChangeLog client.c client.h commit.c l... [signed-com
From: |
Derek Robert Price |
Subject: |
[Cvs-cvs] ccvs/src ChangeLog client.c client.h commit.c l... [signed-commits3] |
Date: |
Fri, 06 Jan 2006 20:04:39 +0000 |
CVSROOT: /cvsroot/cvs
Module name: ccvs
Branch: signed-commits3
Changes by: Derek Robert Price <address@hidden> 06/01/06 20:04:38
Modified files:
src : ChangeLog client.c client.h commit.c log.c
main.c mkmodules.c patch.c rcs.c rcs.h
sanity.sh server.c server.h sign.c sign.h
status.c
Log message:
* client.c, client.h, commit.c, log.c, mkmodules.c, patch.c, server.c,
status.c: Use stricter include formatting.
(update_entries): Generalize sig_cache handling.
(client_write_sigfile): New function factored from...
(client_base_checkout): ...here.
(client_base_signatures, handle_base_signatures): New functions.
(responses): Add Base-signatures.
(struct send_data, send_files): Remove sign flag.
(send_fileproc): Compensate. Send signatures for sign command.
* client.h [SEND_SIGNATURES]: Remove.
* commit.c (check_direntproc): Compensate.
* main.c (cmd): Add `sign' command.
* rcs.c (RCS_add_openpgp_signature): New function.
* server.c (server_write_sigfile): New function factored from...
(serve_modified): ...here.
(serve_unchanged): Write signatures.
(requests): Add `sign' request.
(serve_sign, server_base_signatures): New functions.
* server.h (server_write_sigfile): Add proto.
* sign.c (sign_fileproc, sign): New functions.
(sign_usage): Add.
* sanity.sh (verify): Rename to...
(openpgp): ...this and add sign test.
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/ChangeLog.diff?only_with_tag=signed-commits3&tr1=1.3328.2.15&tr2=1.3328.2.16&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/client.c.diff?only_with_tag=signed-commits3&tr1=1.438.2.3&tr2=1.438.2.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/client.h.diff?only_with_tag=signed-commits3&tr1=1.61.4.1&tr2=1.61.4.2&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/commit.c.diff?only_with_tag=signed-commits3&tr1=1.257.2.1&tr2=1.257.2.2&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/log.c.diff?only_with_tag=signed-commits3&tr1=1.103.6.1&tr2=1.103.6.2&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/main.c.diff?only_with_tag=signed-commits3&tr1=1.262.6.3&tr2=1.262.6.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/mkmodules.c.diff?only_with_tag=signed-commits3&tr1=1.95&tr2=1.95.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/patch.c.diff?only_with_tag=signed-commits3&tr1=1.106&tr2=1.106.6.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/rcs.c.diff?only_with_tag=signed-commits3&tr1=1.356.6.2&tr2=1.356.6.3&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/rcs.h.diff?only_with_tag=signed-commits3&tr1=1.82.8.1&tr2=1.82.8.2&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/sanity.sh.diff?only_with_tag=signed-commits3&tr1=1.1105.2.4&tr2=1.1105.2.5&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/server.c.diff?only_with_tag=signed-commits3&tr1=1.453.2.1&tr2=1.453.2.2&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/server.h.diff?only_with_tag=signed-commits3&tr1=1.44.6.1&tr2=1.44.6.2&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/sign.c.diff?only_with_tag=signed-commits3&tr1=1.1.6.3&tr2=1.1.6.4&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/sign.h.diff?only_with_tag=signed-commits3&tr1=1.1.6.2&tr2=1.1.6.3&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/cvs/ccvs/src/status.c.diff?only_with_tag=signed-commits3&tr1=1.68&tr2=1.68.6.1&r1=text&r2=text
Patches:
Index: ccvs/src/ChangeLog
diff -u ccvs/src/ChangeLog:1.3328.2.15 ccvs/src/ChangeLog:1.3328.2.16
--- ccvs/src/ChangeLog:1.3328.2.15 Fri Jan 6 19:34:15 2006
+++ ccvs/src/ChangeLog Fri Jan 6 20:04:37 2006
@@ -1,5 +1,30 @@
2006-01-06 Derek Price <address@hidden>
+ * client.c, client.h, commit.c, log.c, mkmodules.c, patch.c, server.c,
+ status.c: Use stricter include formatting.
+ (update_entries): Generalize sig_cache handling.
+ (client_write_sigfile): New function factored from...
+ (client_base_checkout): ...here.
+ (client_base_signatures, handle_base_signatures): New functions.
+ (responses): Add Base-signatures.
+ (struct send_data, send_files): Remove sign flag.
+ (send_fileproc): Compensate. Send signatures for sign command.
+ * client.h [SEND_SIGNATURES]: Remove.
+ * commit.c (check_direntproc): Compensate.
+ * main.c (cmd): Add `sign' command.
+ * rcs.c (RCS_add_openpgp_signature): New function.
+ * server.c (server_write_sigfile): New function factored from...
+ (serve_modified): ...here.
+ (serve_unchanged): Write signatures.
+ (requests): Add `sign' request.
+ (serve_sign, server_base_signatures): New functions.
+ * server.h (server_write_sigfile): Add proto.
+ * sign.c (sign_fileproc, sign): New functions.
+ (sign_usage): Add.
+
+ * sanity.sh (verify): Rename to...
+ (openpgp): ...this and add sign test.
+
* add.c, admin.c, annotate.c, base.c, buffer.c, checkin.c, checkout.c,
classify.c, diff.c, edit.c, edit.h, entries.c, fileattr.c,
find_names.c, history.c, ignore.c, import.c, lock.c, logmsg.c, ls.c,
Index: ccvs/src/client.c
diff -u ccvs/src/client.c:1.438.2.3 ccvs/src/client.c:1.438.2.4
--- ccvs/src/client.c:1.438.2.3 Sun Jan 1 23:12:37 2006
+++ ccvs/src/client.c Fri Jan 6 20:04:37 2006
@@ -14,18 +14,25 @@
# include "config.h"
#endif /* HAVE_CONFIG_H */
-/* GNULIB */
+/* Verify interface. */
+#include "client.h"
+
+/* GNULIB headers. */
#include "base64.h"
#include "getline.h"
#include "save-cwd.h"
-/* CVS */
+/* CVS headers. */
#include "base.h"
+#include "buffer.h"
#include "difflib.h"
+#include "edit.h"
+#include "ignore.h"
+#include "recurse.h"
#include "cvs.h"
-#include "buffer.h"
-#include "edit.h"
+
+
#ifdef CLIENT_SUPPORT
@@ -1888,6 +1895,7 @@
/* On checkin, create the base file. */
Node *n;
bool makebase = true;
+
if ((n = findnode_fn (ent_list, filename)))
{
/* This could be a readd of a locally removed file or, for
@@ -1902,31 +1910,26 @@
/* The version number has not changed. */
makebase = false;
}
+
if (makebase)
{
/* A real checkin. */
char *basefile = make_base_file_name (filename, vn);
+
mkdir_if_needed (CVSADM_BASE);
copy_file (filename, basefile);
- if (get_sign_commits (false, supported_request ("Signature")))
+ if ((n = findnode_fn (sig_cache, short_pathname)))
{
- if ((n = findnode_fn (sig_cache, short_pathname)))
- {
- char *sigfile = Xasprintf ("%s%s", basefile, ".sig");
- write_file (sigfile, n->data, n->len);
- delnode (n);
- free (sigfile);
- }
- else
- {
- error (0, 0,
-"Internal error: OpenPGP signature for `%s' not found in cache.",
- short_pathname);
- printlist (sig_cache);
- }
+ char *sigfile = Xasprintf ("%s%s", basefile, ".sig");
+ write_file (sigfile, n->data, n->len);
+ delnode (n);
+ free (sigfile);
}
-
+ else if (get_sign_commits (false, supported_request ("Signature")))
+ error (0, 0,
+"Internal error: OpenPGP signature for `%s' not found in cache.",
+ short_pathname);
free (basefile);
}
}
@@ -2198,6 +2201,32 @@
static void
+client_write_sigfile (const char *sigfile, bool writable)
+{
+ FILE *e;
+
+ if (!stored_signatures)
+ return;
+
+ if (!writable && isfile (sigfile))
+ xchmod (sigfile, true);
+ e = xfopen (sigfile, FOPEN_BINARY_WRITE);
+ if (fwrite (stored_signatures, sizeof *stored_signatures,
+ stored_signatures_len, e) != stored_signatures_len)
+ error (1, errno, "cannot write signature file `%s'", sigfile);
+ if (fclose (e) == EOF)
+ error (0, errno, "cannot close signature file `%s'", sigfile);
+
+ if (!writable)
+ xchmod (sigfile, false);
+
+ free (stored_signatures);
+ stored_signatures = NULL;
+}
+
+
+
+static void
client_base_checkout (void *data_arg, List *ent_list,
const char *short_pathname, const char *filename)
{
@@ -2332,28 +2361,16 @@
if (stored_signatures)
{
char *sigfile = Xasprintf ("%s.sig", basefile);
-
- if (!*istemp && isfile (sigfile))
- xchmod (sigfile, true);
- e = xfopen (sigfile, FOPEN_BINARY_WRITE);
- if (fwrite (stored_signatures, sizeof *stored_signatures,
- stored_signatures_len, e) != stored_signatures_len)
- error (1, errno, "cannot write signature file `%s'", sigfile);
- if (fclose (e) == EOF)
- error (0, errno, "cannot close signature file `%s'", sigfile);
- if (!*istemp)
- xchmod (sigfile, false);
+ client_write_sigfile (sigfile, *istemp);
/* FIXME: Verify the signature here, when configured to do so. */
- if (*istemp && CVS_UNLINK (sigfile) < 0)
+ if (istemp && CVS_UNLINK (sigfile) < 0)
error (0, errno, "Failed to remove temp sig file `%s'",
sigfile);
free (sigfile);
- free (stored_signatures);
- stored_signatures = NULL;
}
}
@@ -2392,6 +2409,45 @@
+static void
+client_base_signatures (void *data_arg, List *ent_list,
+ const char *short_pathname, const char *filename)
+{
+ char *rev;
+ char *basefile;
+ char *sigfile;
+
+ TRACE (TRACE_FUNCTION, "client_base_signatures (%s)", short_pathname);
+
+ if (!stored_signatures)
+ error (1, 0,
+ "Server sent `Base-signatures' response without signature.");
+
+ /* Read OPTIONS, PREV, and REV from the server. */
+ read_line (&rev);
+
+ basefile = make_base_file_name (filename, rev);
+ sigfile = Xasprintf ("%s.sig", basefile);
+
+ client_write_sigfile (sigfile, false);
+
+ free (rev);
+ free (basefile);
+ free (sigfile);
+}
+
+
+
+static void
+handle_base_signatures (char *args, size_t len)
+{
+ if (suppress_bases)
+ error (1, 0, "Server sent Base-* response when asked not to.");
+ call_in_directory (args, client_base_signatures, NULL);
+}
+
+
+
/* Create am up-to-date temporary workfile from a base file. */
static void
client_base_copy (void *data_arg, List *ent_list, const char *short_pathname,
@@ -3750,6 +3806,8 @@
RSP_LINE("Base-merged", handle_base_merged, response_type_normal,
rs_optional),
+ RSP_LINE("Base-signatures", handle_base_signatures, response_type_normal,
+ rs_optional),
RSP_LINE("OpenPGP-signatures", handle_openpgp_signatures,
response_type_normal, rs_optional),
@@ -5141,7 +5199,6 @@
bool force;
bool no_contents;
bool backup_modified;
- bool sign;
};
/* Deal with one file. */
@@ -5247,6 +5304,18 @@
? vers->ts_conflict : vers->ts_rcs, vers->ts_user)
|| (vers->ts_conflict && !strcmp (cvs_cmd_name, "diff")))
{
+ if (!strcmp (cvs_cmd_name, "sign")
+ || (!strcmp (cvs_cmd_name, "commit")
+ && get_sign_commits (false, supported_request ("Signature"))))
+ {
+ if (!supported_request ("Signature"))
+ error (1, 0, "Server doesn't support commit signatures.");
+
+ send_signature (Short_Repository (finfo->repository),
+ finfo->file, finfo->fullname,
+ vers && !strcmp (vers->options, "-kb"));
+ }
+
if (args->no_contents
&& supported_request ("Is-modified"))
{
@@ -5255,19 +5324,7 @@
send_to_server ("\012", 1);
}
else
- {
- if (args->sign
- && get_sign_commits (false, supported_request ("Signature")))
- {
- if (!supported_request ("Signature"))
- error (1, 0, "Server doesn't support commit signatures.");
-
- send_signature (Short_Repository (finfo->repository),
- finfo->file, finfo->fullname,
- vers && !strcmp (vers->options, "-kb"));
- }
send_modified (filename, finfo->fullname, vers);
- }
if (args->backup_modified)
{
@@ -5283,6 +5340,16 @@
}
else
{
+ if (!strcmp (cvs_cmd_name, "sign"))
+ {
+ if (!supported_request ("Signature"))
+ error (1, 0, "Server doesn't support commit signatures.");
+
+ send_signature (Short_Repository (finfo->repository),
+ finfo->file, finfo->fullname,
+ vers && !strcmp (vers->options, "-kb"));
+ }
+
send_to_server ("Unchanged ", 0);
send_to_server (filename, 0);
send_to_server ("\012", 1);
@@ -5666,7 +5733,6 @@
* server as though they were modified.
* FLAGS & SEND_NO_CONTENTS means that this command only needs to
* know _whether_ a file is modified, not the contents.
- * sign Whether to send files signatures.
*
* RETURNS
* Nothing.
@@ -5688,7 +5754,6 @@
args.force = flags & SEND_FORCE;
args.no_contents = flags & SEND_NO_CONTENTS;
args.backup_modified = flags & BACKUP_MODIFIED_FILES;
- args.sign = flags & SEND_SIGNATURES;
err = start_recursion
(send_fileproc, send_filesdoneproc, send_dirent_proc,
send_dirleave_proc, &args, argc, argv, local, W_LOCAL, aflag,
Index: ccvs/src/client.h
diff -u ccvs/src/client.h:1.61.4.1 ccvs/src/client.h:1.61.4.2
--- ccvs/src/client.h:1.61.4.1 Wed Dec 21 13:25:10 2005
+++ ccvs/src/client.h Fri Jan 6 20:04:37 2006
@@ -14,6 +14,14 @@
/* Interface between the client and the rest of CVS. */
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include <sys/types.h>
+#include "root.h"
+
+
+
/* Stuff shared with the server. */
char *mode_to_string (mode_t);
int change_mode (const char *, const char *, int);
@@ -120,7 +128,6 @@
# define SEND_FORCE (1 << 1)
# define SEND_NO_CONTENTS (1 << 2)
# define BACKUP_MODIFIED_FILES (1 << 3)
-# define SEND_SIGNATURES (1 << 4)
/* Send an argument to the remote server. */
void
@@ -219,3 +226,5 @@
#endif
#endif /* CLIENT_SUPPORT */
+
+#endif /* CLIENT_H */
Index: ccvs/src/commit.c
diff -u ccvs/src/commit.c:1.257.2.1 ccvs/src/commit.c:1.257.2.2
--- ccvs/src/commit.c:1.257.2.1 Wed Dec 21 13:25:10 2005
+++ ccvs/src/commit.c Fri Jan 6 20:04:37 2006
@@ -19,12 +19,23 @@
*
*/
-#include "cvs.h"
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* GNULIB headers. */
#include "getline.h"
+
+/* CVS headers. */
#include "edit.h"
+#include "ignore.h"
+#include "logmsg.h"
+#include "recurse.h"
+#include "sign.h"
+
+#include "cvs.h"
#include "fileattr.h"
#include "hardlink.h"
-#include "sign.h"
static Dtype check_direntproc (void *callerdat, const char *dir,
const char *repos, const char *update_dir,
@@ -612,7 +623,6 @@
program, which we used to call, wanted the file to exist,
then it would be relatively simple to fix in the server. */
flags = find_args.force ? SEND_FORCE : 0;
- flags |= SEND_SIGNATURES;
send_files (find_args.argc, find_args.argv, local, 0, flags);
/* Sending only the names of the files which were modified, added,
@@ -937,9 +947,10 @@
* be intentionally signing a file with keywords. Such a file
* may still be verified when checked out -ko.
*/
- error (0, 0,
+ if (!quiet)
+ error (0, 0,
"warning: signed file `%s' contains at least one RCS keyword",
- finfo->fullname);
+ finfo->fullname);
}
if (status == T_REMOVED)
Index: ccvs/src/log.c
diff -u ccvs/src/log.c:1.103.6.1 ccvs/src/log.c:1.103.6.2
--- ccvs/src/log.c:1.103.6.1 Fri Dec 30 23:26:32 2005
+++ ccvs/src/log.c Fri Jan 6 20:04:38 2006
@@ -29,6 +29,8 @@
/* CVS Headers. */
#include "gpg.h"
+#include "ignore.h"
+#include "recurse.h"
#include "cvs.h"
Index: ccvs/src/main.c
diff -u ccvs/src/main.c:1.262.6.3 ccvs/src/main.c:1.262.6.4
--- ccvs/src/main.c:1.262.6.3 Sun Jan 1 23:12:37 2006
+++ ccvs/src/main.c Fri Jan 6 20:04:38 2006
@@ -182,6 +182,7 @@
#ifdef SERVER_SUPPORT
{ "server", NULL, NULL, server,
CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
#endif
+ { "sign", "sig", NULL, sign, 0 },
{ "status", "st", "stat", cvsstatus, CVS_CMD_USES_WORK_DIR },
{ "tag", "ta", "freeze", cvstag,
CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
{ "unedit", NULL, NULL, unedit,
CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
Index: ccvs/src/mkmodules.c
diff -u /dev/null ccvs/src/mkmodules.c:1.95.2.1
--- /dev/null Fri Jan 6 20:04:39 2006
+++ ccvs/src/mkmodules.c Fri Jan 6 20:04:38 2006
@@ -0,0 +1,1295 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions 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 kit. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* GNULIB headers. */
+#include "getline.h"
+#include "save-cwd.h"
+
+/* CVS headers. */
+#include "history.h"
+#include "ignore.h"
+
+#include "cvs.h"
+
+
+
+#ifndef DBLKSIZ
+#define DBLKSIZ 4096 /* since GNU ndbm doesn't
define it */
+#endif
+
+static int checkout_file (char *file, char *temp);
+static char *make_tempfile (void);
+static void rename_rcsfile (char *temp, char *real);
+
+#ifndef MY_NDBM
+static void rename_dbmfile (char *temp);
+static void write_dbmfile (char *temp);
+#endif /* !MY_NDBM */
+
+/* Structure which describes an administrative file. */
+struct admin_file {
+ /* Name of the file, within the CVSROOT directory. */
+ char *filename;
+
+ /* This is a one line description of what the file is for. It is not
+ currently used, although one wonders whether it should be, somehow.
+ If NULL, then don't process this file in mkmodules (FIXME?: a bit of
+ a kludge; probably should replace this with a flags field). */
+ char *errormsg;
+
+ /* Contents which the file should have in a new repository. To avoid
+ problems with brain-dead compilers which choke on long string constants,
+ this is a pointer to an array of char * terminated by NULL--each of
+ the strings is concatenated.
+
+ If this field is NULL, the file is not created in a new
+ repository, but it can be added with "cvs add" (just as if one
+ had created the repository with a version of CVS which didn't
+ know about the file) and the checked-out copy will be updated
+ without having to add it to checkoutlist. */
+ const char * const *contents;
+};
+
+static const char *const loginfo_contents[] = {
+ "# The \"loginfo\" file controls where \"cvs commit\" log information
is\n",
+ "# sent. The first entry on a line is a regular expression which must\n",
+ "# match the directory that the change is being made to, relative to
the\n",
+ "# $CVSROOT. If a match is found, then the remainder of the line is a\n",
+ "# filter program that should expect log information on its standard
input.\n",
+ "#\n",
+ "# If the repository name does not match any of the regular expressions in
this\n",
+ "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+ "#\n",
+ "# If the name ALL appears as a regular expression it is always used\n",
+ "# in addition to the first matching regex or DEFAULT.\n",
+ "#\n",
+ "# If any format strings are present in the filter, they will be
replaced\n",
+ "# as follows:\n",
+ "# %c = canonical name of the command being executed\n",
+#ifdef PROXY_SUPPORT
+ "# %R = the name of the referrer, if any, otherwise the value NONE\n",
+#endif
+ "# %p = path relative to repository\n",
+ "# %r = repository (path portion of $CVSROOT)\n",
+ "# %{sVv} = attribute list = file name, old version number
(pre-checkin),\n",
+ "# new version number (post-checkin). When either old or new
revision\n",
+ "# is unknown, doesn't exist, or isn't applicable, the string
\"NONE\"\n",
+ "# will be placed on the command line instead.\n",
+ "#\n",
+ "# Note that %{sVv} is a list operator and not all elements are
necessary.\n",
+ "# Thus %{sv} is a legal format string, but will only be replaced with\n",
+ "# file name and new revision.\n",
+ "# It also generates multiple arguments for each file being operated
upon.\n",
+ "# That is, if two files, file1 & file2, are being commited from 1.1 to\n",
+ "# version 1.1.2.1 and from 1.1.2.2 to 1.1.2.3, respectively, %{sVv}
will\n",
+ "# generate the following six arguments in this order:\n",
+ "# file1, 1.1, 1.1.2.1, file2, 1.1.2.2, 1.1.2.3.\n",
+ "#\n",
+ "# For example:\n",
+ "#DEFAULT (echo \"\"; id; echo %s; date; cat) >>
$CVSROOT/CVSROOT/commitlog\n",
+ "# or\n",
+ "#DEFAULT (echo \"\"; id; echo %{sVv}; date; cat) >>
$CVSROOT/CVSROOT/commitlog\n",
+ NULL
+};
+
+static const char *const rcsinfo_contents[] = {
+ "# The \"rcsinfo\" file is used to control templates with which the
editor\n",
+ "# is invoked on commit and import.\n",
+ "#\n",
+ "# The first entry on a line is a regular expression which is tested\n",
+ "# against the directory that the change is being made to, relative to
the\n",
+ "# $CVSROOT. For the first match that is found, then the remainder of
the\n",
+ "# line is the name of the file that contains the template.\n",
+ "#\n",
+ "# If the repository name does not match any of the regular expressions in
this\n",
+ "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+ "#\n",
+ "# If the name \"ALL\" appears as a regular expression it is always
used\n",
+ "# in addition to the first matching regex or \"DEFAULT\".\n",
+ NULL
+};
+
+
+
+static const char *const verifymsg_contents[] = {
+ "# The \"verifymsg\" file is used to allow verification of logging\n",
+ "# information. It works best when a template (as specified in the\n",
+ "# rcsinfo file) is provided for the logging procedure. Given a\n",
+ "# template with locations for, a bug-id number, a list of people who\n",
+ "# reviewed the code before it can be checked in, and an external\n",
+ "# process to catalog the differences that were code reviewed, the\n",
+ "# following test can be applied to the code:\n",
+ "#\n",
+ "# Making sure that the entered bug-id number is correct.\n",
+ "# Validating that the code that was reviewed is indeed the code
being\n",
+ "# checked in (using the bug-id number or a seperate review\n",
+ "# number to identify this particular code set.).\n",
+ "#\n",
+ "# If any of the above test failed, then the commit would be aborted.\n",
+ "#\n",
+ "# Format strings present in the filter will be replaced as follows:\n",
+ "# %c = canonical name of the command being executed\n",
+#ifdef PROXY_SUPPORT
+ "# %R = the name of the referrer, if any, otherwise the value NONE\n",
+#endif
+ "# %p = path relative to repository\n",
+ "# %r = repository (path portion of $CVSROOT)\n",
+ "# %l = name of log file to be verified.\n",
+ "#\n",
+ "# If no format strings are present in the filter, a default \" %l\"
will\n",
+ "# be appended to the filter, but this usage is deprecated.\n",
+ "#\n",
+ "# Actions such as mailing a copy of the report to each reviewer are\n",
+ "# better handled by an entry in the loginfo file.\n",
+ "#\n",
+ "# One thing that should be noted is the the ALL keyword is not\n",
+ "# supported. There can be only one entry that matches a given\n",
+ "# repository.\n",
+ NULL
+};
+
+static const char *const commitinfo_contents[] = {
+ "# The \"commitinfo\" file is used to control pre-commit checks.\n",
+ "# The filter on the right is invoked with the repository and a list \n",
+ "# of files to check. A non-zero exit of the filter program will \n",
+ "# cause the commit to be aborted.\n",
+ "#\n",
+ "# The first entry on a line is a regular expression which is tested\n",
+ "# against the directory that the change is being committed to,
relative\n",
+ "# to the $CVSROOT. For the first match that is found, then the
remainder\n",
+ "# of the line is the name of the filter to run.\n",
+ "#\n",
+ "# Format strings present in the filter will be replaced as follows:\n",
+ "# %c = canonical name of the command being executed\n",
+#ifdef PROXY_SUPPORT
+ "# %R = the name of the referrer, if any, otherwise the value NONE\n",
+#endif
+ "# %p = path relative to repository\n",
+ "# %r = repository (path portion of $CVSROOT)\n",
+ "# %{s} = file name, file name, ...\n",
+ "#\n",
+ "# If no format strings are present in the filter string, a default of\n",
+ "# \" %r %s\" will be appended to the filter string, but this usage is\n",
+ "# deprecated.\n",
+ "#\n",
+ "# If the repository name does not match any of the regular expressions in
this\n",
+ "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+ "#\n",
+ "# If the name \"ALL\" appears as a regular expression it is always
used\n",
+ "# in addition to the first matching regex or \"DEFAULT\".\n",
+ NULL
+};
+
+static const char *const taginfo_contents[] = {
+ "# The \"taginfo\" file is used to control pre-tag checks.\n",
+ "# The filter on the right is invoked with the following arguments\n",
+ "# if no format strings are present:\n",
+ "#\n",
+ "# $1 -- tagname\n",
+ "# $2 -- operation \"add\" for tag, \"mov\" for tag -F, and \"del\" for
tag -d\n",
+ "# $3 -- tagtype \"?\" on delete, \"T\" for branch, \"N\" for static\n",
+ "# $4 -- repository\n",
+ "# $5-> file revision [file revision ...]\n",
+ "#\n",
+ "# If any format strings are present in the filter, they will be
replaced\n",
+ "# as follows:\n",
+ "# %b = branch mode = \"?\" (delete ops - unknown) | \"T\" (branch)\n",
+ "# | \"N\" (not branch)\n",
+ "# %o = operation = \"add\" | \"mov\" | \"del\"\n",
+ "# %c = canonical name of the command being executed\n",
+#ifdef PROXY_SUPPORT
+ "# %R = the name of the referrer, if any, otherwise the value NONE\n",
+#endif
+ "# %p = path relative to repository\n",
+ "# %r = repository (path portion of $CVSROOT)\n",
+ "# %t = tagname\n",
+ "# %{sVv} = attribute list = file name, old version tag will be
deleted\n",
+ "# from, new version tag will be added to (or deleted from,
but\n",
+ "# this feature is deprecated. When either old or new
revision is\n",
+ "# unknown, doesn't exist, or isn't applicable, the string
\"NONE\"\n",
+ "# will be placed on the command line.\n",
+ "#\n",
+ "# Note that %{sVv} is a list operator and not all elements are
necessary.\n",
+ "# Thus %{sV} is a legal format string, but will only be replaced with
file\n",
+ "# name and old revision. it also generates multiple arguments for each
file\n",
+ "# being operated upon. i.e. if two files, file1 & file2, are having a
tag\n",
+ "# moved from version 1.1 to version 1.1.2.9, %{sVv} will generate the\n",
+ "# following six arguments in this order:\n",
+ "# file1, 1.1, 1.1.2.9, file2, 1.1, 1.1.2.9.\n",
+ "#\n",
+ "# A non-zero exit of the filter program will cause the tag to be
aborted.\n",
+ "#\n",
+ "# The first entry on a line is a regular expression which is tested\n",
+ "# against the directory that the change is being committed to,
relative\n",
+ "# to the $CVSROOT. For the first match that is found, then the
remainder\n",
+ "# of the line is the name of the filter to run.\n",
+ "#\n",
+ "# If the repository name does not match any of the regular expressions in
this\n",
+ "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+ "#\n",
+ "# If the name \"ALL\" appears as a regular expression it is always
used\n",
+ "# in addition to the first matching regex or \"DEFAULT\".\n",
+ NULL
+};
+
+static const char *const preproxy_contents[] = {
+ "# The \"preproxy\" file is called form the secondary server as soon as\n",
+ "# the secondary server determines that it will be proxying a write\n",
+ "# command to a primary server and immediately before it opens a\n",
+ "# connection to the primary server. This script might, for example,
be\n",
+ "# used to launch a dial up or VPN connection to the primary server's\n",
+ "# network.\n",
+ "#\n",
+ "# If any format strings are present in the filter, they will be
replaced\n",
+ "# as follows:\n",
+ "# %c = canonical name of the command being executed\n",
+#ifdef PROXY_SUPPORT
+ "# %R = the name of the referrer, if any, otherwise the value NONE\n",
+#endif
+ "# %p = path relative to repository (currently always \".\")\n",
+ "# %r = repository (path portion of $CVSROOT)\n",
+ "#\n",
+ "# The first entry on a line is a regular expression which is tested\n",
+ "# against the directory that the change is being committed to,
relative\n",
+ "# to the $CVSROOT. For the first match that is found, then the
remainder\n",
+ "# of the line is the name of the filter to run.\n",
+ "#\n",
+ "# If the repository name does not match any of the regular expressions in
this\n",
+ "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+ "#\n",
+ "# If the name \"ALL\" appears as a regular expression it is always
used\n",
+ "# in addition to the first matching regex or \"DEFAULT\".\n",
+ NULL
+};
+
+static const char *const postadmin_contents[] = {
+ "# The \"postadmin\" file is called after the \"admin\" command
finishes\n",
+ "# processing a directory.\n",
+ "#\n",
+ "# If any format strings are present in the filter, they will be
replaced\n",
+ "# as follows:\n",
+ "# %c = canonical name of the command being executed\n",
+#ifdef PROXY_SUPPORT
+ "# %R = the name of the referrer, if any, otherwise the value NONE\n",
+#endif
+ "# %p = path relative to repository\n",
+ "# %r = repository (path portion of $CVSROOT)\n",
+ "#\n",
+ "# The first entry on a line is a regular expression which is tested\n",
+ "# against the directory that the change is being committed to,
relative\n",
+ "# to the $CVSROOT. For the first match that is found, then the
remainder\n",
+ "# of the line is the name of the filter to run.\n",
+ "#\n",
+ "# If the repository name does not match any of the regular expressions in
this\n",
+ "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+ "#\n",
+ "# If the name \"ALL\" appears as a regular expression it is always
used\n",
+ "# in addition to the first matching regex or \"DEFAULT\".\n",
+ NULL
+};
+
+static const char *const postproxy_contents[] = {
+ "# The \"postproxy\" file is called from a secondary server as soon as\n",
+ "# the secondary server closes its connection to the primary server.\n",
+ "# This script might, for example, be used to shut down a dial up\n",
+ "# or VPN connection to the primary server's network.\n",
+ "#\n",
+ "# If any format strings are present in the filter, they will be
replaced\n",
+ "# as follows:\n",
+ "# %c = canonical name of the command being executed\n",
+#ifdef PROXY_SUPPORT
+ "# %R = the name of the referrer, if any, otherwise the value NONE\n",
+#endif
+ "# %p = path relative to repository (currently always \".\")\n",
+ "# %r = repository (path portion of $CVSROOT)\n",
+ "#\n",
+ "# The first entry on a line is a regular expression which is tested\n",
+ "# against the directory that the change is being committed to,
relative\n",
+ "# to the $CVSROOT. For the first match that is found, then the
remainder\n",
+ "# of the line is the name of the filter to run.\n",
+ "#\n",
+ "# If the repository name does not match any of the regular expressions in
this\n",
+ "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+ "#\n",
+ "# If the name \"ALL\" appears as a regular expression it is always
used\n",
+ "# in addition to the first matching regex or \"DEFAULT\".\n",
+ NULL
+};
+
+static const char *const posttag_contents[] = {
+ "# The \"posttag\" file is called after the \"tag\" command finishes\n",
+ "# processing a directory.\n",
+ "#\n",
+ "# If any format strings are present in the filter, they will be
replaced\n",
+ "# as follows:\n",
+ "# %b = branch mode = \"?\" (delete ops - unknown) | \"T\" (branch)\n",
+ "# | \"N\" (not branch)\n",
+ "# %o = operation = \"add\" | \"mov\" | \"del\"\n",
+ "# %c = canonical name of the command being executed\n",
+#ifdef PROXY_SUPPORT
+ "# %R = the name of the referrer, if any, otherwise the value NONE\n",
+#endif
+ "# %p = path relative to repository\n",
+ "# %r = repository (path portion of $CVSROOT)\n",
+ "# %t = tagname\n",
+ "# %{sVv} = attribute list = file name, old version tag will be
deleted\n",
+ "# from, new version tag will be added to (or deleted from,
but\n",
+ "# this feature is deprecated. When either old or new
revision is\n",
+ "# unknown, doesn't exist, or isn't applicable, the string
\"NONE\"\n",
+ "# will be placed on the command line.\n",
+ "#\n",
+ "# Note that %{sVv} is a list operator and not all elements are
necessary.\n",
+ "# Thus %{sV} is a legal format string, but will only be replaced with
file\n",
+ "# name and old revision. it also generates multiple arguments for each
file\n",
+ "# being operated upon. i.e. if two files, file1 & file2, are having a
tag\n",
+ "# moved from version 1.1 to version 1.1.2.9, %{sVv} will generate the\n",
+ "# following six arguments in this order:\n",
+ "# file1, 1.1, 1.1.2.9, file2, 1.1, 1.1.2.9.\n",
+ "#\n",
+ "# The first entry on a line is a regular expression which is tested\n",
+ "# against the directory that the change is being committed to,
relative\n",
+ "# to the $CVSROOT. For the first match that is found, then the
remainder\n",
+ "# of the line is the name of the filter to run.\n",
+ "#\n",
+ "# If the repository name does not match any of the regular expressions in
this\n",
+ "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+ "#\n",
+ "# If the name \"ALL\" appears as a regular expression it is always
used\n",
+ "# in addition to the first matching regex or \"DEFAULT\".\n",
+ NULL
+};
+
+static const char *const postwatch_contents[] = {
+ "# The \"postwatch\" file is called after any command finishes writing
new\n",
+ "# file attibute (watch/edit) information in a directory.\n",
+ "#\n",
+ "# If any format strings are present in the filter, they will be
replaced\n",
+ "# as follows:\n",
+ "# %c = canonical name of the command being executed\n",
+#ifdef PROXY_SUPPORT
+ "# %R = the name of the referrer, if any, otherwise the value NONE\n",
+#endif
+ "# %p = path relative to repository\n",
+ "# %r = repository (path portion of $CVSROOT)\n",
+ "#\n",
+ "# The first entry on a line is a regular expression which is tested\n",
+ "# against the directory that the change is being committed to,
relative\n",
+ "# to the $CVSROOT. For the first match that is found, then the
remainder\n",
+ "# of the line is the name of the filter to run.\n",
+ "#\n",
+ "# If the repository name does not match any of the regular expressions in
this\n",
+ "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+ "#\n",
+ "# If the name \"ALL\" appears as a regular expression it is always
used\n",
+ "# in addition to the first matching regex or \"DEFAULT\".\n",
+ NULL
+};
+
+static const char *const checkoutlist_contents[] = {
+ "# The \"checkoutlist\" file is used to support additional version
controlled\n",
+ "# administrative files in $CVSROOT/CVSROOT, such as template files.\n",
+ "#\n",
+ "# The first entry on a line is a filename which will be checked out
from\n",
+ "# the corresponding RCS file in the $CVSROOT/CVSROOT directory.\n",
+ "# The remainder of the line is an error message to use if the file
cannot\n",
+ "# be checked out.\n",
+ "#\n",
+ "# File format:\n",
+ "#\n",
+ "# [<whitespace>]<filename>[<whitespace><error message>]<end-of-line>\n",
+ "#\n",
+ "# comment lines begin with '#'\n",
+ NULL
+};
+
+static const char *const cvswrappers_contents[] = {
+ "# This file affects handling of files based on their names.\n",
+ "#\n",
+#if 0 /* see comments in wrap_add in wrapper.c */
+ "# The -t/-f options allow one to treat directories of files\n",
+ "# as a single file, or to transform a file in other ways on\n",
+ "# its way in and out of CVS.\n",
+ "#\n",
+#endif
+ "# The -m option specifies whether CVS attempts to merge files.\n",
+ "#\n",
+ "# The -k option specifies keyword expansion (e.g. -kb for binary).\n",
+ "#\n",
+ "# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or
.cvswrappers)\n",
+ "#\n",
+ "# wildcard [option value][option value]...\n",
+ "#\n",
+ "# where option is one of\n",
+ "# -f from cvs filter value: path to filter\n",
+ "# -t to cvs filter value: path to filter\n",
+ "# -m update methodology value: MERGE or COPY\n",
+ "# -k expansion mode value: b, o, kkv, &c\n",
+ "#\n",
+ "# and value is a single-quote delimited value.\n",
+ "# For example:\n",
+ "#*.gif -k 'b'\n",
+ NULL
+};
+
+static const char *const notify_contents[] = {
+ "# The \"notify\" file controls where notifications from watches set by\n",
+ "# \"cvs watch add\" or \"cvs edit\" are sent. The first entry on a line
is\n",
+ "# a regular expression which is tested against the directory that the\n",
+ "# change is being made to, relative to the $CVSROOT. If it matches,\n",
+ "# then the remainder of the line is a filter program that should
contain\n",
+ "# one occurrence of %s for the user to notify, and information on its\n",
+ "# standard input.\n",
+ "#\n",
+ "# \"ALL\" or \"DEFAULT\" can be used in place of the regular
expression.\n",
+ "#\n",
+ "# format strings are replaceed as follows:\n",
+ "# %c = canonical name of the command being executed\n",
+#ifdef PROXY_SUPPORT
+ "# %R = the name of the referrer, if any, otherwise the value NONE\n",
+#endif
+ "# %p = path relative to repository\n",
+ "# %r = repository (path portion of $CVSROOT)\n",
+ "# %s = user to notify\n",
+ "#\n",
+ "# For example:\n",
+ "#ALL (echo Committed to %r/%p; cat) |mail %s -s \"CVS notification\"\n",
+ NULL
+};
+
+static const char *const modules_contents[] = {
+ "# Three different line formats are valid:\n",
+ "# key -a aliases...\n",
+ "# key [options] directory\n",
+ "# key [options] directory files...\n",
+ "#\n",
+ "# Where \"options\" are composed of:\n",
+ "# -i prog Run \"prog\" on \"cvs commit\" from top-level of
module.\n",
+ "# -o prog Run \"prog\" on \"cvs checkout\" of module.\n",
+ "# -e prog Run \"prog\" on \"cvs export\" of module.\n",
+ "# -t prog Run \"prog\" on \"cvs rtag\" of module.\n",
+ "# -u prog Run \"prog\" on \"cvs update\" of module.\n",
+ "# -d dir Place module in directory \"dir\" instead of module
name.\n",
+ "# -l Top-level directory only -- do not recurse.\n",
+ "#\n",
+ "# NOTE: If you change any of the \"Run\" options above, you'll have
to\n",
+ "# release and re-checkout any working directories of these modules.\n",
+ "#\n",
+ "# And \"directory\" is a path to a directory relative to $CVSROOT.\n",
+ "#\n",
+ "# The \"-a\" option specifies an alias. An alias is interpreted as if\n",
+ "# everything on the right of the \"-a\" had been typed on the command
line.\n",
+ "#\n",
+ "# You can encode a module within a module by using the special '&'\n",
+ "# character to interpose another module into the current module. This\n",
+ "# can be useful for creating a module that consists of many
directories\n",
+ "# spread out over the entire source repository.\n",
+ NULL
+};
+
+static const char *const config_contents[] = {
+ "# Set `SystemAuth' to `no' if pserver shouldn't check system
users/passwords.\n",
+ "#SystemAuth=no\n",
+ "\n",
+ "# Set `LocalKeyword' to specify a local alias for a standard keyword.\n",
+ "#LocalKeyword=MYCVS=CVSHeader\n",
+ "\n",
+ "# Set `KeywordExpand' to `i' followed by a list of keywords to expand
or\n",
+ "# `e' followed by a list of keywords to not expand.\n"
+ "#KeywordExpand=iMYCVS,Name,Date\n",
+ "#KeywordExpand=eCVSHeader\n",
+ "\n",
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ "# Set `PreservePermissions' to `yes' to save file status information\n",
+ "# in the repository.\n",
+ "#PreservePermissions=no\n",
+ "\n",
+#endif
+ "# Set `TopLevelAdmin' to `yes' to create a CVS directory at the top\n",
+ "# level of the new working directory when using the `cvs checkout'\n",
+ "# command.\n",
+ "#TopLevelAdmin=no\n",
+ "\n",
+ "# Put CVS lock files in this directory rather than directly in the
repository.\n",
+ "#LockDir=/var/lock/cvs\n",
+ "\n",
+ "# Set `LogHistory' to `all' or `" ALL_HISTORY_REC_TYPES "' to log all
transactions to the\n",
+ "# history file, or a subset as needed (ie `TMAR' logs all write
operations)\n",
+ "#LogHistory=" ALL_HISTORY_REC_TYPES "\n",
+ "\n",
+ "# Set `RereadLogAfterVerify' to `always' (the default) to allow the
verifymsg\n",
+ "# script to change the log message. Set it to `stat' to force CVS to
verify\n",
+ "# that the file has changed before reading it (this can take up to an
extra\n",
+ "# second per directory being committed, so it is not recommended for
large\n",
+ "# repositories. Set it to `never' (the previous CVS behavior) to
prevent\n",
+ "# verifymsg scripts from changing the log message.\n",
+ "#RereadLogAfterVerify=always\n",
+ "\n",
+ "# Set `UserAdminOptions' to the list of `cvs admin' commands (options)\n",
+ "# that users not in the `cvsadmin' group are allowed to run. This\n",
+ "# defaults to `k', or only allowing the changing of the default\n",
+ "# keyword expansion mode for files for users not in the `cvsadmin'
group.\n",
+ "# This value is ignored if the `cvsadmin' group does not exist.\n",
+ "#\n",
+ "# The following string would enable all `cvs admin' commands for all\n",
+ "# users:\n",
+ "#UserAdminOptions=aAbceIklLmnNostuU;execute;no-execute\n",
+#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
+ "\n",
+ "# Set `UseNewInfoFmtStrings' to `no' if you must support a legacy system
by\n",
+ "# enabling the deprecated old style info file command line format
strings.\n",
+ "# Be warned that these strings could be disabled in any new version of
CVS.\n",
+ "UseNewInfoFmtStrings=yes\n",
+#endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
+ "\n",
+ "# Set `ImportNewFilesToVendorBranchOnly' to `yes' if you wish to force\n",
+ "# every `cvs import' command to behave as if the `-X' flag was\n",
+ "# specified.\n",
+ "#ImportNewFilesToVendorBranchOnly=no\n",
+#ifdef PROXY_SUPPORT
+ "\n",
+ "# Set `PrimaryServer' to the CVSROOT to the primary, or write, server
when\n",
+ "# establishing one or more read-only mirrors which serve as proxies
for\n",
+ "# the write server in write mode or redirect the client to the primary
for\n",
+ "# write requests.\n",
+ "#\n",
+ "# For example:\n",
+ "#\n",
+ "# PrimaryServer=:fork:localhost/cvsroot\n",
+ "\n",
+ "# Set `MaxProxyBufferSize' to the the maximum allowable secondary\n",
+ "# buffer memory cache size before the buffer begins being stored to disk,
in\n",
+ "# bytes. Must be a positive integer but may end in `k', `M', `G', or `T'
(for\n",
+ "# kiilo, mega, giga, & tera, respectively). If an otherwise valid number
you\n",
+ "# specify is greater than the SIZE_MAX defined by your system's C
compiler,\n",
+ "# then it will be resolved to SIZE_MAX without a warning. Defaults to 8M
(8\n",
+ "# megabytes).\n",
+ "#\n",
+ "# High values for MaxProxyBufferSize may speed up a secondary server\n",
+ "# with old hardware and a lot of available memory but can actually slow
a\n",
+ "# modern system down slightly.\n",
+ "#\n",
+ "# For example:\n",
+ "#\n",
+ "# MaxProxyBufferSize=1G\n",
+#endif /* PROXY_SUPPORT */
+ "\n",
+ "# Set `MaxCommentLeaderLength' to the maximum length permitted for the\n",
+ "# automagically determined comment leader used when expanding the Log\n",
+ "# keyword, in bytes. CVS's behavior when the automagically determined\n",
+ "# comment leader exceeds this length is dependant on the value of\n",
+ "# `UseArchiveCommentLeader' set in this file. `unlimited' is a valid\n",
+ "# setting for this value. Defaults to 20 bytes.\n",
+ "#\n",
+ "# For example:\n",
+ "#\n",
+ "# MaxCommentLeaderLength=20\n",
+ "\n",
+ "# Set `UseArchiveCommentLeader' to `yes' to cause CVS to fall back on\n",
+ "# the comment leader set in the RCS archive file, if any, when the\n",
+ "# automagically determined comment leader exceeds
`MaxCommentLeaderLength'\n",
+ "# bytes. If `UseArchiveCommentLeader' is not set and a comment leader\n",
+ "# greater than `MaxCommentLeaderLength' is calculated, the Log keyword\n",
+ "# being examined will not be expanded. Defaults to `no'.\n",
+ "#\n",
+ "# For example:\n",
+ "#\n",
+ "# UseArchiveCommentLeader=no\n",
+ NULL
+};
+
+static const struct admin_file filelist[] = {
+ {CVSROOTADM_CHECKOUTLIST,
+ "a %s file can specify extra CVSROOT files to auto-checkout",
+ checkoutlist_contents},
+ {CVSROOTADM_COMMITINFO,
+ "a %s file can be used to configure 'cvs commit' checking",
+ commitinfo_contents},
+ {CVSROOTADM_IGNORE,
+ "a %s file can be used to specify files to ignore",
+ NULL},
+ {CVSROOTADM_LOGINFO,
+ "no logging of 'cvs commit' messages is done without a %s file",
+ &loginfo_contents[0]},
+ {CVSROOTADM_MODULES,
+ /* modules is special-cased in mkmodules. */
+ NULL,
+ modules_contents},
+ {CVSROOTADM_NOTIFY,
+ "a %s file can be used to specify where notifications go",
+ notify_contents},
+ {CVSROOTADM_POSTADMIN,
+ "a %s file can be used to configure 'cvs admin' logging",
+ postadmin_contents},
+ {CVSROOTADM_POSTPROXY,
+ "a %s file can be used to close or log connections to a primary server",
+ postproxy_contents},
+ {CVSROOTADM_POSTTAG,
+ "a %s file can be used to configure 'cvs tag' logging",
+ posttag_contents},
+ {CVSROOTADM_POSTWATCH,
+ "a %s file can be used to configure 'cvs watch' logging",
+ postwatch_contents},
+ {CVSROOTADM_PREPROXY,
+ "a %s file can be used to open or log connections to a primary server",
+ preproxy_contents},
+ {CVSROOTADM_RCSINFO,
+ "a %s file can be used to configure 'cvs commit' templates",
+ rcsinfo_contents},
+ {CVSROOTADM_READERS,
+ "a %s file specifies read-only users",
+ NULL},
+ {CVSROOTADM_TAGINFO,
+ "a %s file can be used to configure 'cvs tag' checking",
+ taginfo_contents},
+ {CVSROOTADM_VERIFYMSG,
+ "a %s file can be used to validate log messages",
+ verifymsg_contents},
+ {CVSROOTADM_WRAPPER,
+ "a %s file can be used to specify files to treat as wrappers",
+ cvswrappers_contents},
+ {CVSROOTADM_WRITERS,
+ "a %s file specifies read/write users",
+ NULL},
+
+ /* Some have suggested listing CVSROOTADM_PASSWD here too. This
+ would mean that CVS commands which operate on the
+ CVSROOTADM_PASSWD file would transmit hashed passwords over the
+ net. This might seem to be no big deal, as pserver normally
+ transmits cleartext passwords, but the difference is that
+ CVSROOTADM_PASSWD contains *all* passwords, not just the ones
+ currently being used. For example, it could be too easy to
+ accidentally give someone readonly access to CVSROOTADM_PASSWD
+ (e.g. via anonymous CVS or cvsweb), and then if there are any
+ guessable passwords for read/write access (usually there will be)
+ they get read/write access.
+
+ Another worry is the implications of storing old passwords--if
+ someone used a password in the past they might be using it
+ elsewhere, using a similar password, etc, and so saving old
+ passwords, even hashed, is probably not a good idea. */
+
+ {CVSROOTADM_CONFIG,
+ "a %s file configures various behaviors",
+ config_contents},
+ {NULL, NULL, NULL}
+};
+
+/* Rebuild the checked out administrative files in directory DIR. */
+int
+mkmodules (char *dir)
+{
+ struct saved_cwd cwd;
+ char *temp;
+ char *cp, *last, *fname;
+#ifdef MY_NDBM
+ DBM *db;
+#endif
+ FILE *fp;
+ char *line = NULL;
+ size_t line_allocated = 0;
+ const struct admin_file *fileptr;
+
+ if (noexec)
+ return 0;
+
+ if (save_cwd (&cwd))
+ error (1, errno, "Failed to save current directory.");
+
+ if (CVS_CHDIR (dir) < 0)
+ error (1, errno, "cannot chdir to %s", dir);
+
+ /*
+ * First, do the work necessary to update the "modules" database.
+ */
+ temp = make_tempfile ();
+ switch (checkout_file (CVSROOTADM_MODULES, temp))
+ {
+
+ case 0: /* everything ok */
+#ifdef MY_NDBM
+ /* open it, to generate any duplicate errors */
+ if ((db = dbm_open (temp, O_RDONLY, 0666)) != NULL)
+ dbm_close (db);
+#else
+ write_dbmfile (temp);
+ rename_dbmfile (temp);
+#endif
+ rename_rcsfile (temp, CVSROOTADM_MODULES);
+ break;
+
+ default:
+ error (0, 0,
+ "'cvs checkout' is less functional without a %s file",
+ CVSROOTADM_MODULES);
+ break;
+ } /* switch on checkout_file() */
+
+ if (unlink_file (temp) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", temp);
+ free (temp);
+
+ /* Checkout the files that need it in CVSROOT dir */
+ for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) {
+ if (fileptr->errormsg == NULL)
+ continue;
+ temp = make_tempfile ();
+ if (checkout_file (fileptr->filename, temp) == 0)
+ rename_rcsfile (temp, fileptr->filename);
+ /* else
+ * If there was some problem other than the file not existing,
+ * checkout_file already printed a real error message. If the
+ * file does not exist, it is harmless--it probably just means
+ * that the repository was created with an old version of CVS
+ * which didn't have so many files in CVSROOT.
+ */
+
+ if (unlink_file (temp) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", temp);
+ free (temp);
+ }
+
+ fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r");
+ if (fp)
+ {
+ /*
+ * File format:
+ * [<whitespace>]<filename>[<whitespace><error message>]<end-of-line>
+ *
+ * comment lines begin with '#'
+ */
+ while (getline (&line, &line_allocated, fp) >= 0)
+ {
+ /* skip lines starting with # */
+ if (line[0] == '#')
+ continue;
+
+ if ((last = strrchr (line, '\n')) != NULL)
+ *last = '\0'; /* strip the newline */
+
+ /* Skip leading white space. */
+ for (fname = line;
+ *fname && isspace ((unsigned char) *fname);
+ fname++)
+ ;
+
+ /* Find end of filename. */
+ for (cp = fname; *cp && !isspace ((unsigned char) *cp); cp++)
+ ;
+ *cp = '\0';
+
+ temp = make_tempfile ();
+ if (checkout_file (fname, temp) == 0)
+ {
+ rename_rcsfile (temp, fname);
+ }
+ else
+ {
+ /* Skip leading white space before the error message. */
+ for (cp++;
+ cp < last && *cp && isspace ((unsigned char) *cp);
+ cp++)
+ ;
+ if (cp < last && *cp)
+ error (0, 0, "%s", cp);
+ }
+ if (unlink_file (temp) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", temp);
+ free (temp);
+ }
+ if (line)
+ free (line);
+ if (ferror (fp))
+ error (0, errno, "cannot read %s", CVSROOTADM_CHECKOUTLIST);
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", CVSROOTADM_CHECKOUTLIST);
+ }
+ else
+ {
+ /* Error from CVS_FOPEN. */
+ if (!existence_error (errno))
+ error (0, errno, "cannot open %s", CVSROOTADM_CHECKOUTLIST);
+ }
+
+ if (restore_cwd (&cwd))
+ error (1, errno, "Failed to restore current directory, `%s'.",
+ cwd.name);
+ free_cwd (&cwd);
+
+ return 0;
+}
+
+
+
+/*
+ * Yeah, I know, there are NFS race conditions here.
+ */
+static char *
+make_tempfile (void)
+{
+ static int seed = 0;
+ int fd;
+ char *temp;
+
+ if (seed == 0)
+ seed = getpid ();
+ temp = xmalloc (sizeof (BAKPREFIX) + 40);
+ while (1)
+ {
+ (void) sprintf (temp, "%s%d", BAKPREFIX, seed++);
+ if ((fd = CVS_OPEN (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1)
+ break;
+ if (errno != EEXIST)
+ error (1, errno, "cannot create temporary file %s", temp);
+ }
+ if (close(fd) < 0)
+ error(1, errno, "cannot close temporary file %s", temp);
+ return temp;
+}
+
+
+
+/* Get a file. If the file does not exist, return 1 silently. If
+ there is an error, print a message and return 1 (FIXME: probably
+ not a very clean convention). On success, return 0. */
+static int
+checkout_file (char *file, char *temp)
+{
+ char *rcs;
+ RCSNode *rcsnode;
+ int retcode = 0;
+
+ if (noexec)
+ return 0;
+
+ rcs = Xasprintf ("%s%s", file, RCSEXT);
+ if (!isfile (rcs))
+ {
+ free (rcs);
+ return 1;
+ }
+
+ rcsnode = RCS_parsercsfile (rcs);
+ if (!rcsnode)
+ {
+ /* Probably not necessary (?); RCS_parsercsfile already printed a
+ message. */
+ error (0, 0, "Failed to parse `%s'.", rcs);
+ free (rcs);
+ return 1;
+ }
+
+ retcode = RCS_checkout (rcsnode, NULL, NULL, NULL, NULL, temp, NULL, NULL);
+ if (retcode != 0)
+ {
+ /* Probably not necessary (?); RCS_checkout already printed a
+ message. */
+ error (0, 0, "failed to check out %s file",
+ file);
+ }
+ freercsnode (&rcsnode);
+ free (rcs);
+ return retcode;
+}
+
+
+
+#ifndef MY_NDBM
+
+static void
+write_dbmfile( char *temp )
+{
+ char line[DBLKSIZ], value[DBLKSIZ];
+ FILE *fp;
+ DBM *db;
+ char *cp, *vp;
+ datum key, val;
+ int len, cont, err = 0;
+
+ fp = xfopen (temp, "r");
+ if ((db = dbm_open (temp, O_RDWR | O_CREAT | O_TRUNC, 0666)) == NULL)
+ error (1, errno, "cannot open dbm file %s for creation", temp);
+ for (cont = 0; fgets (line, sizeof (line), fp) != NULL;)
+ {
+ if ((cp = strrchr (line, '\n')) != NULL)
+ *cp = '\0'; /* strip the newline */
+
+ /*
+ * Add the line to the value, at the end if this is a continuation
+ * line; otherwise at the beginning, but only after any trailing
+ * backslash is removed.
+ */
+ vp = value;
+ if (cont)
+ vp += strlen (value);
+
+ /*
+ * See if the line we read is a continuation line, and strip the
+ * backslash if so.
+ */
+ len = strlen (line);
+ if (len > 0)
+ cp = &line[len - 1];
+ else
+ cp = line;
+ if (*cp == '\\')
+ {
+ cont = 1;
+ *cp = '\0';
+ }
+ else
+ {
+ cont = 0;
+ }
+ (void) strcpy (vp, line);
+ if (value[0] == '#')
+ continue; /* comment line */
+ vp = value;
+ while (*vp && isspace ((unsigned char) *vp))
+ vp++;
+ if (*vp == '\0')
+ continue; /* empty line */
+
+ /*
+ * If this was not a continuation line, add the entry to the database
+ */
+ if (!cont)
+ {
+ key.dptr = vp;
+ while (*vp && !isspace ((unsigned char) *vp))
+ vp++;
+ key.dsize = vp - key.dptr;
+ *vp++ = '\0'; /* NULL terminate the key */
+ while (*vp && isspace ((unsigned char) *vp))
+ vp++; /* skip whitespace to value */
+ if (*vp == '\0')
+ {
+ error (0, 0, "warning: NULL value for key `%s'", key.dptr);
+ continue;
+ }
+ val.dptr = vp;
+ val.dsize = strlen (vp);
+ if (dbm_store (db, key, val, DBM_INSERT) == 1)
+ {
+ error (0, 0, "duplicate key found for `%s'", key.dptr);
+ err++;
+ }
+ }
+ }
+ dbm_close (db);
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", temp);
+ if (err)
+ {
+ /* I think that the size of the buffer needed here is
+ just determined by sizeof (CVSROOTADM_MODULES), the
+ filenames created by make_tempfile, and other things that won't
+ overflow. */
+ char dotdir[50], dotpag[50], dotdb[50];
+
+ (void) sprintf (dotdir, "%s.dir", temp);
+ (void) sprintf (dotpag, "%s.pag", temp);
+ (void) sprintf (dotdb, "%s.db", temp);
+ if (unlink_file (dotdir) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", dotdir);
+ if (unlink_file (dotpag) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", dotpag);
+ if (unlink_file (dotdb) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", dotdb);
+ error (1, 0, "DBM creation failed; correct above errors");
+ }
+}
+
+static void
+rename_dbmfile( char *temp )
+{
+ /* I think that the size of the buffer needed here is
+ just determined by sizeof (CVSROOTADM_MODULES), the
+ filenames created by make_tempfile, and other things that won't
+ overflow. */
+ char newdir[50], newpag[50], newdb[50];
+ char dotdir[50], dotpag[50], dotdb[50];
+ char bakdir[50], bakpag[50], bakdb[50];
+
+ int dir1_errno = 0, pag1_errno = 0, db1_errno = 0;
+ int dir2_errno = 0, pag2_errno = 0, db2_errno = 0;
+ int dir3_errno = 0, pag3_errno = 0, db3_errno = 0;
+
+ (void) sprintf (dotdir, "%s.dir", CVSROOTADM_MODULES);
+ (void) sprintf (dotpag, "%s.pag", CVSROOTADM_MODULES);
+ (void) sprintf (dotdb, "%s.db", CVSROOTADM_MODULES);
+ (void) sprintf (bakdir, "%s%s.dir", BAKPREFIX, CVSROOTADM_MODULES);
+ (void) sprintf (bakpag, "%s%s.pag", BAKPREFIX, CVSROOTADM_MODULES);
+ (void) sprintf (bakdb, "%s%s.db", BAKPREFIX, CVSROOTADM_MODULES);
+ (void) sprintf (newdir, "%s.dir", temp);
+ (void) sprintf (newpag, "%s.pag", temp);
+ (void) sprintf (newdb, "%s.db", temp);
+
+ (void) chmod (newdir, 0666);
+ (void) chmod (newpag, 0666);
+ (void) chmod (newdb, 0666);
+
+ /* don't mess with me */
+ SIG_beginCrSect ();
+
+ /* rm .#modules.dir .#modules.pag */
+ if (unlink_file (bakdir) < 0)
+ dir1_errno = errno;
+ if (unlink_file (bakpag) < 0)
+ pag1_errno = errno;
+ if (unlink_file (bakdb) < 0)
+ db1_errno = errno;
+
+ /* mv modules.dir .#modules.dir */
+ if (CVS_RENAME (dotdir, bakdir) < 0)
+ dir2_errno = errno;
+ /* mv modules.pag .#modules.pag */
+ if (CVS_RENAME (dotpag, bakpag) < 0)
+ pag2_errno = errno;
+ /* mv modules.db .#modules.db */
+ if (CVS_RENAME (dotdb, bakdb) < 0)
+ db2_errno = errno;
+
+ /* mv "temp".dir modules.dir */
+ if (CVS_RENAME (newdir, dotdir) < 0)
+ dir3_errno = errno;
+ /* mv "temp".pag modules.pag */
+ if (CVS_RENAME (newpag, dotpag) < 0)
+ pag3_errno = errno;
+ /* mv "temp".db modules.db */
+ if (CVS_RENAME (newdb, dotdb) < 0)
+ db3_errno = errno;
+
+ /* OK -- make my day */
+ SIG_endCrSect ();
+
+ /* I didn't want to call error() when we had signals blocked
+ (unnecessary?), but do it now. */
+ if (dir1_errno && !existence_error (dir1_errno))
+ error (0, dir1_errno, "cannot remove %s", bakdir);
+ if (pag1_errno && !existence_error (pag1_errno))
+ error (0, pag1_errno, "cannot remove %s", bakpag);
+ if (db1_errno && !existence_error (db1_errno))
+ error (0, db1_errno, "cannot remove %s", bakdb);
+
+ if (dir2_errno && !existence_error (dir2_errno))
+ error (0, dir2_errno, "cannot remove %s", bakdir);
+ if (pag2_errno && !existence_error (pag2_errno))
+ error (0, pag2_errno, "cannot remove %s", bakpag);
+ if (db2_errno && !existence_error (db2_errno))
+ error (0, db2_errno, "cannot remove %s", bakdb);
+
+ if (dir3_errno && !existence_error (dir3_errno))
+ error (0, dir3_errno, "cannot remove %s", bakdir);
+ if (pag3_errno && !existence_error (pag3_errno))
+ error (0, pag3_errno, "cannot remove %s", bakpag);
+ if (db3_errno && !existence_error (db3_errno))
+ error (0, db3_errno, "cannot remove %s", bakdb);
+}
+
+#endif /* !MY_NDBM */
+
+static void
+rename_rcsfile (char *temp, char *real)
+{
+ char *bak;
+ struct stat statbuf;
+ char *rcs;
+
+ /* Set "x" bits if set in original. */
+ rcs = Xasprintf ("%s%s", real, RCSEXT);
+ statbuf.st_mode = 0; /* in case rcs file doesn't exist, but it should... */
+ if (stat (rcs, &statbuf) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot stat %s", rcs);
+ free (rcs);
+
+ if (chmod (temp, 0444 | (statbuf.st_mode & 0111)) < 0)
+ error (0, errno, "warning: cannot chmod %s", temp);
+ bak = Xasprintf ("%s%s", BAKPREFIX, real);
+
+ /* rm .#loginfo */
+ if (unlink_file (bak) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", bak);
+
+ /* mv loginfo .#loginfo */
+ if (CVS_RENAME (real, bak) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot rename %s to %s", real, bak);
+
+ /* mv "temp" loginfo */
+ if (CVS_RENAME (temp, real) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot rename %s to %s", temp, real);
+
+ free (bak);
+}
+
+const char *const init_usage[] = {
+ "Usage: %s %s\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL
+};
+
+int
+init (int argc, char **argv)
+{
+ /* Name of CVSROOT directory. */
+ char *adm;
+ /* Name of this administrative file. */
+ char *info;
+ /* Name of ,v file for this administrative file. */
+ char *info_v;
+ /* Exit status. */
+ int err = 0;
+
+ const struct admin_file *fileptr;
+
+ umask (cvsumask);
+
+ if (argc == -1 || argc > 1)
+ usage (init_usage);
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ start_server ();
+
+ ign_setup ();
+ send_init_command ();
+ return get_responses_and_close ();
+ }
+#endif /* CLIENT_SUPPORT */
+
+ /* Note: we do *not* create parent directories as needed like the
+ old cvsinit.sh script did. Few utilities do that, and a
+ non-existent parent directory is as likely to be a typo as something
+ which needs to be created. */
+ mkdir_if_needed (current_parsed_root->directory);
+
+ adm = Xasprintf ("%s/%s", current_parsed_root->directory, CVSROOTADM);
+ mkdir_if_needed (adm);
+
+ /* This is needed because we pass "fileptr->filename" not "info"
+ to add_rcs_file below. I think this would be easy to change,
+ thus nuking the need for CVS_CHDIR here, but I haven't looked
+ closely (e.g. see wrappers calls within add_rcs_file). */
+ if ( CVS_CHDIR (adm) < 0)
+ error (1, errno, "cannot change to directory %s", adm);
+
+ /* Make Emptydir so it's there if we need it */
+ mkdir_if_needed (CVSNULLREPOS);
+
+ /* 80 is long enough for all the administrative file names, plus
+ "/" and so on. */
+ info = xmalloc (strlen (adm) + 80);
+ info_v = xmalloc (strlen (adm) + 80);
+ for (fileptr = filelist; fileptr && fileptr->filename; ++fileptr)
+ {
+ if (fileptr->contents == NULL)
+ continue;
+ strcpy (info, adm);
+ strcat (info, "/");
+ strcat (info, fileptr->filename);
+ strcpy (info_v, info);
+ strcat (info_v, RCSEXT);
+ if (isfile (info_v))
+ /* We will check out this file in the mkmodules step.
+ Nothing else is required. */
+ ;
+ else
+ {
+ int retcode;
+
+ if (!isfile (info))
+ {
+ FILE *fp;
+ const char * const *p;
+
+ fp = xfopen (info, "w");
+ for (p = fileptr->contents; *p != NULL; ++p)
+ if (fputs (*p, fp) < 0)
+ error (1, errno, "cannot write %s", info);
+ if (fclose (fp) < 0)
+ error (1, errno, "cannot close %s", info);
+ }
+ /* The message used to say " of " and fileptr->filename after
+ "initial checkin" but I fail to see the point as we know what
+ file it is from the name. */
+ retcode = add_rcs_file ("initial checkin", info_v,
+ fileptr->filename, "1.1", NULL,
+
+ /* No vendor branch. */
+ NULL, NULL, 0, NULL,
+
+ NULL, 0, NULL, 0);
+ if (retcode != 0)
+ /* add_rcs_file already printed an error message. */
+ err = 1;
+ }
+ }
+
+ /* Turn on history logging by default. The user can remove the file
+ to disable it. */
+ strcpy (info, adm);
+ strcat (info, "/");
+ strcat (info, CVSROOTADM_HISTORY);
+ if (!isfile (info))
+ {
+ FILE *fp;
+
+ fp = xfopen (info, "w");
+ if (fclose (fp) < 0)
+ error (1, errno, "cannot close %s", info);
+
+ /* Make the new history file world-writeable, since every CVS
+ user will need to be able to write to it. We use chmod()
+ because xchmod() is too shy. */
+ chmod (info, 0666);
+ }
+
+ /* Make an empty val-tags file to prevent problems creating it later. */
+ strcpy (info, adm);
+ strcat (info, "/");
+ strcat (info, CVSROOTADM_VALTAGS);
+ if (!isfile (info))
+ {
+ FILE *fp;
+
+ fp = xfopen (info, "w");
+ if (fclose (fp) < 0)
+ error (1, errno, "cannot close %s", info);
+
+ /* Make the new val-tags file world-writeable, since every CVS
+ user will need to be able to write to it. We use chmod()
+ because xchmod() is too shy. */
+ chmod (info, 0666);
+ }
+
+ free (info);
+ free (info_v);
+
+ mkmodules (adm);
+
+ free (adm);
+ return err;
+}
Index: ccvs/src/patch.c
diff -u /dev/null ccvs/src/patch.c:1.106.6.1
--- /dev/null Fri Jan 6 20:04:39 2006
+++ ccvs/src/patch.c Fri Jan 6 20:04:38 2006
@@ -0,0 +1,858 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions 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 source distribution.
+ *
+ * Patch
+ *
+ * Create a Larry Wall format "patch" file between a previous release and the
+ * current head of a module, or between two releases. Can specify the
+ * release as either a date or a revision number.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* GNULIB headers. */
+#include "getline.h"
+
+/* CVS headers. */
+#include "ignore.h"
+#include "recurse.h"
+
+#include "cvs.h"
+
+
+
+static RETSIGTYPE patch_cleanup (int);
+static Dtype patch_dirproc (void *callerdat, const char *dir,
+ const char *repos, const char *update_dir,
+ List *entries);
+static int patch_fileproc (void *callerdat, struct file_info *finfo);
+static int patch_proc (int argc, char **argv, char *xwhere,
+ char *mwhere, char *mfile, int shorten,
+ int local_specified, char *mname, char *msg);
+
+static int force_tag_match = 1;
+static int patch_short = 0;
+static int toptwo_diffs = 0;
+static char *options = NULL;
+static char *rev1 = NULL;
+static int rev1_validated = 0;
+static char *rev2 = NULL;
+static int rev2_validated = 0;
+static char *date1 = NULL;
+static char *date2 = NULL;
+static char *tmpfile1 = NULL;
+static char *tmpfile2 = NULL;
+static char *tmpfile3 = NULL;
+static int unidiff = 0;
+
+static const char *const patch_usage[] =
+{
+ "Usage: %s %s [-flR] [-c|-u] [-s|-t] [-V %%d] [-k kopt]\n",
+ " -r rev|-D date [-r rev2 | -D date2] modules...\n",
+ "\t-f\tForce a head revision match if tag/date not found.\n",
+ "\t-l\tLocal directory only, not recursive\n",
+ "\t-R\tProcess directories recursively.\n",
+ "\t-c\tContext diffs (default)\n",
+ "\t-u\tUnidiff format.\n",
+ "\t-s\tShort patch - one liner per file.\n",
+ "\t-t\tTop two diffs - last change made to the file.\n",
+ "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
+ "\t-k kopt\tSpecify keyword expansion mode.\n",
+ "\t-D date\tDate.\n",
+ "\t-r rev\tRevision - symbolic or numeric.\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL
+};
+
+
+
+int
+patch (int argc, char **argv)
+{
+ register int i;
+ int local = 0;
+ int c;
+ int err = 0;
+ DBM *db;
+
+ if (argc == -1)
+ usage (patch_usage);
+
+ optind = 0;
+ while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1)
+ {
+ switch (c)
+ {
+ case 'Q':
+ case 'q':
+ /* The CVS 1.5 client sends these options (in addition to
+ Global_option requests), so we must ignore them. */
+ if (!server_active)
+ error (1, 0,
+ "-q or -Q must be specified before \"%s\"",
+ cvs_cmd_name);
+ break;
+ case 'f':
+ force_tag_match = 0;
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 't':
+ toptwo_diffs = 1;
+ break;
+ case 's':
+ patch_short = 1;
+ break;
+ case 'D':
+ if (rev2 != NULL || date2 != NULL)
+ error (1, 0,
+ "no more than two revisions/dates can be specified");
+ if (rev1 != NULL || date1 != NULL)
+ date2 = Make_Date (optarg);
+ else
+ date1 = Make_Date (optarg);
+ break;
+ case 'r':
+ if (rev2 != NULL || date2 != NULL)
+ error (1, 0,
+ "no more than two revisions/dates can be specified");
+ if (rev1 != NULL || date1 != NULL)
+ rev2 = optarg;
+ else
+ rev1 = optarg;
+ break;
+ case 'k':
+ if (options)
+ free (options);
+ options = RCS_check_kflag (optarg);
+ break;
+ case 'V':
+ /* This option is pretty seriously broken:
+ 1. It is not clear what it does (does it change keyword
+ expansion behavior? If so, how? Or does it have
+ something to do with what version of RCS we are using?
+ Or the format we write RCS files in?).
+ 2. Because both it and -k use the options variable,
+ specifying both -V and -k doesn't work.
+ 3. At least as of CVS 1.9, it doesn't work (failed
+ assertion in RCS_checkout where it asserts that options
+ starts with -k). Few people seem to be complaining.
+ In the future (perhaps the near future), I have in mind
+ removing it entirely, and updating NEWS and cvs.texinfo,
+ but in case it is a good idea to give people more time
+ to complain if they would miss it, I'll just add this
+ quick and dirty error message for now. */
+ error (1, 0,
+ "the -V option is obsolete and should not be used");
+ break;
+ case 'u':
+ unidiff = 1; /* Unidiff */
+ break;
+ case 'c': /* Context diff */
+ unidiff = 0;
+ break;
+ case '?':
+ default:
+ usage (patch_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Sanity checks */
+ if (argc < 1)
+ usage (patch_usage);
+
+ if (toptwo_diffs && patch_short)
+ error (1, 0, "-t and -s options are mutually exclusive");
+ if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
+ rev1 != NULL || rev2 != NULL))
+ error (1, 0, "must not specify revisions/dates with -t option!");
+
+ if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
+ rev1 == NULL && rev2 == NULL))
+ error (1, 0, "must specify at least one revision/date!");
+ if (date1 != NULL && date2 != NULL)
+ if (RCS_datecmp (date1, date2) >= 0)
+ error (1, 0, "second date must come after first date!");
+
+ /* if options is NULL, make it a NULL string */
+ if (options == NULL)
+ options = xstrdup ("");
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ /* We're the client side. Fire up the remote server. */
+ start_server ();
+
+ ign_setup ();
+
+ if (local)
+ send_arg("-l");
+ if (!force_tag_match)
+ send_arg("-f");
+ if (toptwo_diffs)
+ send_arg("-t");
+ if (patch_short)
+ send_arg("-s");
+ if (unidiff)
+ send_arg("-u");
+
+ if (rev1)
+ option_with_arg ("-r", rev1);
+ if (date1)
+ client_senddate (date1);
+ if (rev2)
+ option_with_arg ("-r", rev2);
+ if (date2)
+ client_senddate (date2);
+ if (options[0] != '\0')
+ send_arg (options);
+
+ {
+ int i;
+ for (i = 0; i < argc; ++i)
+ send_arg (argv[i]);
+ }
+
+ send_to_server ("rdiff\012", 0);
+ return get_responses_and_close ();
+ }
+#endif
+
+ /* clean up if we get a signal */
+#ifdef SIGABRT
+ (void)SIG_register (SIGABRT, patch_cleanup);
+#endif
+#ifdef SIGHUP
+ (void)SIG_register (SIGHUP, patch_cleanup);
+#endif
+#ifdef SIGINT
+ (void)SIG_register (SIGINT, patch_cleanup);
+#endif
+#ifdef SIGQUIT
+ (void)SIG_register (SIGQUIT, patch_cleanup);
+#endif
+#ifdef SIGPIPE
+ (void)SIG_register (SIGPIPE, patch_cleanup);
+#endif
+#ifdef SIGTERM
+ (void)SIG_register (SIGTERM, patch_cleanup);
+#endif
+
+ db = open_module ();
+ for (i = 0; i < argc; i++)
+ err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
+ NULL, 0, local, 0, 0, NULL);
+ close_module (db);
+ free (options);
+ patch_cleanup (0);
+ return err;
+}
+
+
+
+/*
+ * callback proc for doing the real work of patching
+ */
+/* ARGSUSED */
+static int
+patch_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
+ int shorten, int local_specified, char *mname, char *msg)
+{
+ char *myargv[2];
+ int err = 0;
+ int which;
+ char *repository;
+ char *where;
+
+ TRACE ( TRACE_FUNCTION, "patch_proc ( %s, %s, %s, %d, %d, %s, %s )",
+ xwhere ? xwhere : "(null)",
+ mwhere ? mwhere : "(null)",
+ mfile ? mfile : "(null)",
+ shorten, local_specified,
+ mname ? mname : "(null)",
+ msg ? msg : "(null)" );
+
+ repository = xmalloc (strlen (current_parsed_root->directory)
+ + strlen (argv[0])
+ + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
+ (void)sprintf (repository, "%s/%s",
+ current_parsed_root->directory, argv[0]);
+ where = xmalloc (strlen (argv[0])
+ + (mfile == NULL ? 0 : strlen (mfile) + 1)
+ + 1);
+ (void)strcpy (where, argv[0]);
+
+ /* if mfile isn't null, we need to set up to do only part of the module */
+ if (mfile != NULL)
+ {
+ char *cp;
+ char *path;
+
+ /* if the portion of the module is a path, put the dir part on repos */
+ if ((cp = strrchr (mfile, '/')) != NULL)
+ {
+ *cp = '\0';
+ (void)strcat (repository, "/");
+ (void)strcat (repository, mfile);
+ (void)strcat (where, "/");
+ (void)strcat (where, mfile);
+ mfile = cp + 1;
+ }
+
+ /* take care of the rest */
+ path = xmalloc (strlen (repository) + strlen (mfile) + 2);
+ (void)sprintf (path, "%s/%s", repository, mfile);
+ if (isdir (path))
+ {
+ /* directory means repository gets the dir tacked on */
+ (void)strcpy (repository, path);
+ (void)strcat (where, "/");
+ (void)strcat (where, mfile);
+ }
+ else
+ {
+ myargv[0] = argv[0];
+ myargv[1] = mfile;
+ argc = 2;
+ argv = myargv;
+ }
+ free (path);
+ }
+
+ /* cd to the starting repository */
+ if (CVS_CHDIR (repository) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", repository);
+ free (repository);
+ free (where);
+ return 1;
+ }
+
+ if (force_tag_match)
+ which = W_REPOS | W_ATTIC;
+ else
+ which = W_REPOS;
+
+ if (rev1 != NULL && !rev1_validated)
+ {
+ tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0,
+ repository, false);
+ rev1_validated = 1;
+ }
+ if (rev2 != NULL && !rev2_validated)
+ {
+ tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0,
+ repository, false);
+ rev2_validated = 1;
+ }
+
+ /* start the recursion processor */
+ err = start_recursion (patch_fileproc, NULL, patch_dirproc, NULL, NULL,
+ argc - 1, argv + 1, local_specified,
+ which, 0, CVS_LOCK_READ, where, 1, repository );
+ free (repository);
+ free (where);
+
+ return err;
+}
+
+
+
+/*
+ * Called to examine a particular RCS file, as appropriate with the options
+ * that were set above.
+ */
+/* ARGSUSED */
+static int
+patch_fileproc (void *callerdat, struct file_info *finfo)
+{
+ struct utimbuf t;
+ char *vers_tag, *vers_head;
+ char *rcs = NULL;
+ char *rcs_orig = NULL;
+ RCSNode *rcsfile;
+ FILE *fp1, *fp2, *fp3;
+ int ret = 0;
+ int isattic = 0;
+ int retcode = 0;
+ char *file1;
+ char *file2;
+ char *strippath;
+ char *line1, *line2;
+ size_t line1_chars_allocated;
+ size_t line2_chars_allocated;
+ char *cp1, *cp2;
+ FILE *fp;
+ int line_length;
+ int dargc = 0;
+ size_t darg_allocated = 0;
+ char **dargv = NULL;
+
+ line1 = NULL;
+ line1_chars_allocated = 0;
+ line2 = NULL;
+ line2_chars_allocated = 0;
+ vers_tag = vers_head = NULL;
+
+ /* find the parsed rcs file */
+ if ((rcsfile = finfo->rcs) == NULL)
+ {
+ ret = 1;
+ goto out2;
+ }
+ if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
+ isattic = 1;
+
+ rcs_orig = rcs = Xasprintf ("%s%s", finfo->file, RCSEXT);
+
+ /* if vers_head is NULL, may have been removed from the release */
+ if (isattic && rev2 == NULL && date2 == NULL)
+ vers_head = NULL;
+ else
+ {
+ vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match,
+ NULL);
+ if (vers_head != NULL && RCS_isdead (rcsfile, vers_head))
+ {
+ free (vers_head);
+ vers_head = NULL;
+ }
+ }
+
+ if (toptwo_diffs)
+ {
+ if (vers_head == NULL)
+ {
+ ret = 1;
+ goto out2;
+ }
+
+ if (!date1)
+ date1 = xmalloc (MAXDATELEN);
+ *date1 = '\0';
+ if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == (time_t)-1)
+ {
+ if (!really_quiet)
+ error (0, 0, "cannot find date in rcs file %s revision %s",
+ rcs, vers_head);
+ ret = 1;
+ goto out2;
+ }
+ }
+ vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match, NULL);
+ if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag))
+ {
+ free (vers_tag);
+ vers_tag = NULL;
+ }
+
+ if ((vers_tag == NULL && vers_head == NULL) ||
+ (vers_tag != NULL && vers_head != NULL &&
+ strcmp (vers_head, vers_tag) == 0))
+ {
+ /* Nothing known about specified revs or
+ * not changed between releases.
+ */
+ ret = 0;
+ goto out2;
+ }
+
+ if (patch_short && (vers_tag == NULL || vers_head == NULL))
+ {
+ /* For adds & removes with a short patch requested, we can print our
+ * error message now and get out.
+ */
+ cvs_output ("File ", 0);
+ cvs_output (finfo->fullname, 0);
+ if (vers_tag == NULL)
+ {
+ cvs_output (" is new; ", 0);
+ cvs_output (rev2 ? rev2 : date2 ? date2 : "current", 0);
+ cvs_output (" revision ", 0);
+ cvs_output (vers_head, 0);
+ cvs_output ("\n", 1);
+ }
+ else
+ {
+ cvs_output (" is removed; ", 0);
+ cvs_output (rev1 ? rev1 : date1, 0);
+ cvs_output (" revision ", 0);
+ cvs_output (vers_tag, 0);
+ cvs_output ("\n", 1);
+ }
+ ret = 0;
+ goto out2;
+ }
+
+ /* Create 3 empty files. I'm not really sure there is any advantage
+ * to doing so now rather than just waiting until later.
+ *
+ * There is - cvs_temp_file opens the file so that it can guarantee that
+ * we have exclusive write access to the file. Unfortunately we spoil that
+ * by closing it and reopening it again. Of course any better solution
+ * requires that the RCS functions accept open file pointers rather than
+ * simple file names.
+ */
+ if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL)
+ {
+ error (0, errno, "cannot create temporary file %s", tmpfile1);
+ ret = 1;
+ goto out;
+ }
+ else
+ if (fclose (fp1) < 0)
+ error (0, errno, "warning: cannot close %s", tmpfile1);
+ if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL)
+ {
+ error (0, errno, "cannot create temporary file %s", tmpfile2);
+ ret = 1;
+ goto out;
+ }
+ else
+ if (fclose (fp2) < 0)
+ error (0, errno, "warning: cannot close %s", tmpfile2);
+ if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL)
+ {
+ error (0, errno, "cannot create temporary file %s", tmpfile3);
+ ret = 1;
+ goto out;
+ }
+ else
+ if (fclose (fp3) < 0)
+ error (0, errno, "warning: cannot close %s", tmpfile3);
+
+ if (vers_tag != NULL)
+ {
+ retcode = RCS_checkout (rcsfile, NULL, vers_tag, rev1, options,
+ tmpfile1, NULL, NULL);
+ if (retcode != 0)
+ {
+ error (0, 0,
+ "cannot check out revision %s of %s", vers_tag, rcs);
+ ret = 1;
+ goto out;
+ }
+ memset ((char *) &t, 0, sizeof (t));
+ if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag,
+ NULL, 0)) != -1)
+ /* I believe this timestamp only affects the dates in our diffs,
+ and therefore should be on the server, not the client. */
+ (void)utime (tmpfile1, &t);
+ }
+ else if (toptwo_diffs)
+ {
+ ret = 1;
+ goto out;
+ }
+ if (vers_head != NULL)
+ {
+ retcode = RCS_checkout (rcsfile, NULL, vers_head, rev2, options,
+ tmpfile2, NULL, NULL);
+ if (retcode != 0)
+ {
+ error (0, 0,
+ "cannot check out revision %s of %s", vers_head, rcs);
+ ret = 1;
+ goto out;
+ }
+ if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
+ NULL, 0)) != -1)
+ /* I believe this timestamp only affects the dates in our diffs,
+ and therefore should be on the server, not the client. */
+ (void)utime (tmpfile2, &t);
+ }
+
+ if (unidiff) run_add_arg_p (&dargc, &darg_allocated, &dargv, "-u");
+ else run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c");
+ switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, dargc, dargv,
+ tmpfile3))
+ {
+ case -1: /* fork/wait failure */
+ error (1, errno, "fork for diff failed on %s", rcs);
+ break;
+ case 0: /* nothing to do */
+ break;
+ case 1:
+ /*
+ * The two revisions are really different, so read the first two
+ * lines of the diff output file, and munge them to include more
+ * reasonable file names that "patch" will understand, unless the
+ * user wanted a short patch. In that case, just output the short
+ * message.
+ */
+ if (patch_short)
+ {
+ cvs_output ("File ", 0);
+ cvs_output (finfo->fullname, 0);
+ cvs_output (" changed from revision ", 0);
+ cvs_output (vers_tag, 0);
+ cvs_output (" to ", 0);
+ cvs_output (vers_head, 0);
+ cvs_output ("\n", 1);
+ ret = 0;
+ goto out;
+ }
+
+ /* Output an "Index:" line for patch to use */
+ cvs_output ("Index: ", 0);
+ cvs_output (finfo->fullname, 0);
+ cvs_output ("\n", 1);
+
+ /* Now the munging. */
+ fp = xfopen (tmpfile3, "r");
+ if (getline (&line1, &line1_chars_allocated, fp) < 0 ||
+ getline (&line2, &line2_chars_allocated, fp) < 0)
+ {
+ if (feof (fp))
+ error (0, 0, "\
+failed to read diff file header %s for %s: end of file", tmpfile3, rcs);
+ else
+ error (0, errno,
+ "failed to read diff file header %s for %s",
+ tmpfile3, rcs);
+ ret = 1;
+ if (fclose (fp) < 0)
+ error (0, errno, "error closing %s", tmpfile3);
+ goto out;
+ }
+ if (!unidiff)
+ {
+ if (strncmp (line1, "*** ", 4) != 0 ||
+ strncmp (line2, "--- ", 4) != 0 ||
+ (cp1 = strchr (line1, '\t')) == NULL ||
+ (cp2 = strchr (line2, '\t')) == NULL)
+ {
+ error (0, 0, "invalid diff header for %s", rcs);
+ ret = 1;
+ if (fclose (fp) < 0)
+ error (0, errno, "error closing %s", tmpfile3);
+ goto out;
+ }
+ }
+ else
+ {
+ if (strncmp (line1, "--- ", 4) != 0 ||
+ strncmp (line2, "+++ ", 4) != 0 ||
+ (cp1 = strchr (line1, '\t')) == NULL ||
+ (cp2 = strchr (line2, '\t')) == NULL)
+ {
+ error (0, 0, "invalid unidiff header for %s", rcs);
+ ret = 1;
+ if (fclose (fp) < 0)
+ error (0, errno, "error closing %s", tmpfile3);
+ goto out;
+ }
+ }
+ assert (current_parsed_root != NULL);
+ assert (current_parsed_root->directory != NULL);
+
+ strippath = Xasprintf ("%s/", current_parsed_root->directory);
+
+ if (strncmp (rcs, strippath, strlen (strippath)) == 0)
+ rcs += strlen (strippath);
+ free (strippath);
+ if (vers_tag != NULL)
+ file1 = Xasprintf ("%s:%s", finfo->fullname, vers_tag);
+ else
+ file1 = xstrdup (DEVNULL);
+
+ file2 = Xasprintf ("%s:%s", finfo->fullname,
+ vers_head ? vers_head : "removed");
+
+ /* Note that the string "diff" is specified by POSIX (for -c)
+ and is part of the diff output format, not the name of a
+ program. */
+ if (unidiff)
+ {
+ cvs_output ("diff -u ", 0);
+ cvs_output (file1, 0);
+ cvs_output (" ", 1);
+ cvs_output (file2, 0);
+ cvs_output ("\n", 1);
+
+ cvs_output ("--- ", 0);
+ cvs_output (file1, 0);
+ cvs_output (cp1, 0);
+ cvs_output ("+++ ", 0);
+ }
+ else
+ {
+ cvs_output ("diff -c ", 0);
+ cvs_output (file1, 0);
+ cvs_output (" ", 1);
+ cvs_output (file2, 0);
+ cvs_output ("\n", 1);
+
+ cvs_output ("*** ", 0);
+ cvs_output (file1, 0);
+ cvs_output (cp1, 0);
+ cvs_output ("--- ", 0);
+ }
+
+ cvs_output (finfo->fullname, 0);
+ cvs_output (cp2, 0);
+
+ /* spew the rest of the diff out */
+ while ((line_length
+ = getline (&line1, &line1_chars_allocated, fp))
+ >= 0)
+ cvs_output (line1, 0);
+ if (line_length < 0 && !feof (fp))
+ error (0, errno, "cannot read %s", tmpfile3);
+
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", tmpfile3);
+ free (file1);
+ free (file2);
+ break;
+ default:
+ error (0, 0, "diff failed for %s", finfo->fullname);
+ }
+ out:
+ if (line1)
+ free (line1);
+ if (line2)
+ free (line2);
+ if (CVS_UNLINK (tmpfile1) < 0)
+ error (0, errno, "cannot unlink %s", tmpfile1);
+ if (CVS_UNLINK (tmpfile2) < 0)
+ error (0, errno, "cannot unlink %s", tmpfile2);
+ if (CVS_UNLINK (tmpfile3) < 0)
+ error (0, errno, "cannot unlink %s", tmpfile3);
+ free (tmpfile1);
+ free (tmpfile2);
+ free (tmpfile3);
+ tmpfile1 = tmpfile2 = tmpfile3 = NULL;
+ if (darg_allocated)
+ {
+ run_arg_free_p (dargc, dargv);
+ free (dargv);
+ }
+
+ out2:
+ if (vers_tag != NULL)
+ free (vers_tag);
+ if (vers_head != NULL)
+ free (vers_head);
+ if (rcs_orig)
+ free (rcs_orig);
+ return ret;
+}
+
+
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+patch_dirproc (void *callerdat, const char *dir, const char *repos,
+ const char *update_dir, List *entries)
+{
+ if (!quiet)
+ error (0, 0, "Diffing %s", update_dir);
+ return R_PROCESS;
+}
+
+
+
+/*
+ * Clean up temporary files
+ */
+static RETSIGTYPE
+patch_cleanup (int sig)
+{
+ /* Note that the checks for existence_error are because we are
+ called from a signal handler, without SIG_begincrsect, so
+ we don't know whether the files got created. */
+
+ if (tmpfile1 != NULL)
+ {
+ if (unlink_file (tmpfile1) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", tmpfile1);
+ free (tmpfile1);
+ }
+ if (tmpfile2 != NULL)
+ {
+ if (unlink_file (tmpfile2) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", tmpfile2);
+ free (tmpfile2);
+ }
+ if (tmpfile3 != NULL)
+ {
+ if (unlink_file (tmpfile3) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", tmpfile3);
+ free (tmpfile3);
+ }
+ tmpfile1 = tmpfile2 = tmpfile3 = NULL;
+
+ if (sig != 0)
+ {
+ const char *name;
+ char temp[10];
+
+ switch (sig)
+ {
+#ifdef SIGABRT
+ case SIGABRT:
+ name = "abort";
+ break;
+#endif
+#ifdef SIGHUP
+ case SIGHUP:
+ name = "hangup";
+ break;
+#endif
+#ifdef SIGINT
+ case SIGINT:
+ name = "interrupt";
+ break;
+#endif
+#ifdef SIGQUIT
+ case SIGQUIT:
+ name = "quit";
+ break;
+#endif
+#ifdef SIGPIPE
+ case SIGPIPE:
+ name = "broken pipe";
+ break;
+#endif
+#ifdef SIGTERM
+ case SIGTERM:
+ name = "termination";
+ break;
+#endif
+ default:
+ /* This case should never be reached, because we list
+ above all the signals for which we actually establish a
+ signal handler. */
+ sprintf (temp, "%d", sig);
+ name = temp;
+ break;
+ }
+ error (0, 0, "received %s signal", name);
+ }
+}
Index: ccvs/src/rcs.c
diff -u ccvs/src/rcs.c:1.356.6.2 ccvs/src/rcs.c:1.356.6.3
--- ccvs/src/rcs.c:1.356.6.2 Fri Dec 30 23:28:22 2005
+++ ccvs/src/rcs.c Fri Jan 6 20:04:38 2006
@@ -4791,6 +4791,72 @@
+void
+RCS_add_openpgp_signature (struct file_info *finfo, const char *rev)
+{
+ RCSVers *vers;
+ Node *n;
+ char *oldsigs;
+ size_t oldlen;
+ char *newsig;
+ size_t newlen;
+
+ TRACE (TRACE_FUNCTION, "RCS_add_openpgp_signature (%s, %s)",
+ finfo->fullname, rev);
+
+ if (finfo->rcs->flags & PARTIAL)
+ RCS_reparsercsfile (finfo->rcs, NULL, NULL);
+
+ n = findnode (finfo->rcs->versions, rev);
+ if (!n)
+ error (1, 0, "internal error: no revision information for %s", rev);
+ vers = n->data;
+
+ n = findnode (vers->other_delta, "openpgp-signatures");
+ if (!n)
+ {
+ n = getnode();
+ n->type = RCSSTRING;
+ n->key = xstrdup ("openpgp-signatures");
+ oldsigs = NULL;
+ oldlen = 0;
+ addnode (vers->other_delta, n);
+ }
+ else
+ {
+ TRACE (TRACE_DATA,
+ "RCS_add_openpgp_signature: found oldsigs = %s, len = %u",
+ (char *)n->data, (unsigned int)n->len);
+ if (!base64_decode_alloc (n->data, n->len, &oldsigs, &oldlen))
+ error (1, 0, "Invalid binhex data in signature (`%s', rev %s)",
+ finfo->rcs->print_path, rev);
+ if (!oldsigs)
+ error (1, errno, "Memory allocation error");
+ free (n->data);
+ }
+
+ newsig = get_signature (server_active,
+ Short_Repository (finfo->repository), finfo->file,
+ finfo->rcs->expand
+ && STREQ (finfo->rcs->expand, "b"),
+ &newlen);
+
+ oldsigs = xrealloc (oldsigs, oldlen + newlen);
+ memcpy (oldsigs + oldlen, newsig, newlen);
+ free (newsig);
+
+ n->len = base64_encode_alloc (oldsigs, oldlen + newlen, (char **)&n->data);
+ free (oldsigs);
+
+ TRACE (TRACE_DATA,
+ "RCS_add_openpgp_signature: found oldsigs = %s, len = %u",
+ (char *)n->data, (unsigned int)n->len);
+
+ RCS_rewrite (finfo->rcs, NULL, NULL);
+}
+
+
+
/* Find the delta currently locked by the user. From the `ci' man page:
"If rev is omitted, ci tries to derive the new revision
Index: ccvs/src/rcs.h
diff -u ccvs/src/rcs.h:1.82.8.1 ccvs/src/rcs.h:1.82.8.2
--- ccvs/src/rcs.h:1.82.8.1 Wed Dec 21 13:25:10 2005
+++ ccvs/src/rcs.h Fri Jan 6 20:04:38 2006
@@ -275,6 +275,7 @@
int RCS_checkout (RCSNode *, const char *, const char *, const char *,
const char *, const char *, RCSCHECKOUTPROC, void *);
const char *RCS_get_openpgp_signatures (RCSNode *rcs, const char *rev);
+void RCS_add_openpgp_signature (struct file_info *finfo, const char *rev);
int RCS_checkin (RCSNode *rcs, const char *update_dir, const char *workfile,
const char *message, const char *rev, time_t citime,
int flags);
Index: ccvs/src/sanity.sh
diff -u ccvs/src/sanity.sh:1.1105.2.4 ccvs/src/sanity.sh:1.1105.2.5
--- ccvs/src/sanity.sh:1.1105.2.4 Tue Jan 3 18:09:24 2006
+++ ccvs/src/sanity.sh Fri Jan 6 20:04:38 2006
@@ -1872,7 +1872,7 @@
tests="${tests} dottedroot fork commit-d template"
tests="${tests} writeproxy writeproxy-noredirect writeproxy-ssh"
tests="${tests} writeproxy-ssh-noredirect"
- tests="$tests verify"
+ tests="$tests openpgp"
else
tests="$*"
fi
@@ -32700,28 +32700,33 @@
- verify)
- # More tests of basic/miscellaneous functionality.
- mkdir verify; cd verify
+ openpgp)
+ # More tests of basic/miscellaneous openpgp functionality.
+ mkdir openpgp; cd openpgp
mkdir top; cd top
- dotest verify-init-1 "$testcvs -q co -l ."
- mkdir verify
- dotest verify-init-2 "$testcvs -Q add verify"
+ dotest openpgp-init-1 "$testcvs -q co -l ."
+ mkdir openpgp
+ dotest openpgp-init-2 "$testcvs -Q add openpgp"
cd ..
- dotest verify-init-3 "$testcvs -q co verify"
- cd verify
+ dotest openpgp-init-3 "$testcvs -q co openpgp"
+ cd openpgp
echo some content >file1
- dotest verify-init-4 "$testcvs -Q add file1"
- dotest verify-init-5 "$testcvs -Q ci -m newfile file1"
- dotest verify-1 "$testcvs verify file1" \
+ dotest openpgp-init-4 "$testcvs -Q add file1"
+ dotest openpgp-init-5 "$testcvs -Q ci -m newfile file1"
+ dotest openpgp-1 "$testcvs verify file1" \
"$DOTSTAR Good signature from \"CVS Test Script $DOTSTAR"
- dotest verify-2 "$testcvs verify -p file1 >tmp"
- dotest verify-3 "cmp tmp CVS/Base/.#file1.1.1.sig"
+ dotest openpgp-2 "$testcvs verify -p file1 >tmp"
+ dotest openpgp-3 "cmp tmp CVS/Base/.#file1.1.1.sig"
+
+ dotest openpgp-4 "$testcvs sign file1"
+ dotest openpgp-5 "$testcvs verify file1" \
+"$DOTSTAR Good signature from \"CVS Test Script $DOTSTAR
+$DOTSTAR Good signature from \"CVS Test Script $DOTSTAR"
dokeep
cd ../..
- rm -rf verify
- modify_repo rm -rf $CVSROOT_DIRNAME/verify
+ rm -rf openpgp
+ modify_repo rm -rf $CVSROOT_DIRNAME/openpgp
;;
Index: ccvs/src/server.c
diff -u ccvs/src/server.c:1.453.2.1 ccvs/src/server.c:1.453.2.2
--- ccvs/src/server.c:1.453.2.1 Wed Dec 21 13:25:10 2005
+++ ccvs/src/server.c Fri Jan 6 20:04:38 2006
@@ -15,23 +15,25 @@
/* Validate API. */
#include "server.h"
-
-/* GNULIB */
+/* GNULIB headers. */
#include "getline.h"
#include "getnline.h"
#include "setenv.h"
#include "wait.h"
-/* CVS */
+/* CVS headers. */
#include "base.h"
#include "buffer.h"
+#include "edit.h"
#include "gpg.h"
+#include "ignore.h"
#include "cvs.h"
-#include "edit.h"
#include "fileattr.h"
#include "watch.h"
+
+
int server_active = 0;
#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
@@ -1879,6 +1881,81 @@
+/* Returns false on errors.
+ */
+static bool
+server_write_sigfile (const char *file, struct buffer *sig_buf)
+{
+ char *sigfile_name, *sig_data;
+ int fd, rc;
+ size_t got;
+ bool err = false;
+
+ /* Write the file. */
+ sigfile_name = get_sigfile_name (file);
+ fd = CVS_OPEN (sigfile_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ if (fd < 0)
+ {
+ int save_errno = errno;
+ if (alloc_pending (40 + strlen (sigfile_name)))
+ sprintf (pending_error_text, "E cannot open `%s'",
+ sigfile_name);
+ pending_error = save_errno;
+ err = true;
+ goto done;
+ }
+
+ while (!buf_empty_p (sig_buf))
+ {
+ if ((rc = buf_read_data (sig_buf, buf_length (sig_buf), &sig_data,
+ &got)))
+ {
+ /* Since !buf_empty_p confirmed that the buffer was not empty,
+ * it should be impossible to get EOF here.
+ */
+ assert (rc != -1);
+
+ if (rc == -2)
+ pending_error = ENOMEM;
+ else if (alloc_pending (80))
+ sprintf (pending_error_text,
+ "E error reading signature buffer.");
+ pending_error = rc;
+ err = true;
+ goto done;
+ }
+
+ if (write (fd, sig_data, got) < 0)
+ {
+ int save_errno = errno;
+ if (alloc_pending (80 + strlen (sigfile_name)))
+ sprintf (pending_error_text,
+ "E error writing temporary signature file `%s'.",
+ sigfile_name);
+ pending_error = save_errno;
+ err = true;
+ goto done;
+ }
+ }
+
+done:
+ if (fd >= 0
+ && close (fd) < 0)
+ {
+ if (!error_pending ()
+ && alloc_pending_warning (80 + strlen (sigfile_name)))
+ sprintf (pending_warning_text,
+ "E error closing temporary signature file `%s'.",
+ sigfile_name);
+ err = true;
+ }
+
+ free (sigfile_name);
+ return !err;
+}
+
+
+
static void
serve_modified (char *arg)
{
@@ -2059,64 +2136,13 @@
*/
if (sig_buf)
{
- char *sigfile_name, *sig_data;
- int fd, rc;
- size_t got;
-
- /* Write the file. */
- sigfile_name = get_sigfile_name (arg);
- fd = CVS_OPEN (sigfile_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
- if (fd < 0)
- {
- int save_errno = errno;
- if (alloc_pending (40 + strlen (arg)))
- sprintf (pending_error_text, "E cannot open `%s'",
- sigfile_name);
- pending_error = save_errno;
- return;
- }
-
- while (!buf_empty_p (sig_buf))
- {
- if ((rc = buf_read_data (sig_buf, buf_length (sig_buf), &sig_data,
- &got)))
- {
- /* Since !buf_empty_p confirmed that the buffer was not empty,
- * it should be impossible to get EOF here.
- */
- assert (rc != -1);
-
- if (rc == -2)
- pending_error = ENOMEM;
- else if (alloc_pending (80))
- sprintf (pending_error_text,
- "E error reading signature buffer.");
- pending_error = rc;
- return;
- }
-
- if (write (fd, sig_data, got) < 0)
- {
- int save_errno = errno;
- if (alloc_pending (80 + strlen (sigfile_name)))
- sprintf (pending_error_text,
- "E error writing temporary signature file `%s'.",
- sigfile_name);
- pending_error = save_errno;
- return;
- }
- }
-
- if (close (fd) < 0
- && alloc_pending_warning (80 + strlen (sigfile_name)))
- sprintf (pending_warning_text,
- "E error closing temporary signature file `%s'.",
- sigfile_name);
- free (sigfile_name);
+ bool err = !server_write_sigfile (arg, sig_buf);
/* We're done with the SIG_BUF. */
buf_free (sig_buf);
sig_buf = NULL;
+
+ if (err) return;
}
}
@@ -2264,6 +2290,20 @@
break;
}
}
+
+ /* If an OpenPGP signature was sent for this file, write it to a temp
+ * file.
+ */
+ if (sig_buf)
+ {
+ bool err = !server_write_sigfile (arg, sig_buf);
+
+ /* We're done with the SIG_BUF. */
+ buf_free (sig_buf);
+ sig_buf = NULL;
+
+ if (err) return;
+ }
}
@@ -4756,6 +4796,14 @@
static void
+serve_sign (char *arg)
+{
+ do_cvs_command ("sign", sign);
+}
+
+
+
+static void
serve_status (char *arg)
{
do_cvs_command ("status", cvsstatus);
@@ -6111,6 +6159,7 @@
REQ_LINE("remove", serve_remove, 0),
REQ_LINE("update-patches", serve_ignore, 0),
REQ_LINE("gzip-file-contents", serve_gzip_contents, RQ_ROOTLESS),
+ REQ_LINE("sign", serve_sign, 0),
REQ_LINE("status", serve_status, 0),
REQ_LINE("rdiff", serve_rdiff, 0),
REQ_LINE("tag", serve_tag, 0),
@@ -8387,6 +8436,23 @@
+void
+server_base_signatures (struct file_info *finfo, const char *rev)
+{
+ server_send_signatures (finfo->rcs, rev);
+
+ buf_output0 (protocol, "Base-signatures ");
+ output_dir (finfo->update_dir, finfo->repository);
+ buf_output0 (protocol, finfo->file);
+ buf_output (protocol, "\n", 1);
+ buf_output0 (protocol, rev);
+ buf_output (protocol, "\n", 1);
+ buf_send_counted (protocol);
+ return;
+}
+
+
+
/*
* void cvs_trace(int level, const char *fmt, ...)
*
Index: ccvs/src/server.h
diff -u ccvs/src/server.h:1.44.6.1 ccvs/src/server.h:1.44.6.2
--- ccvs/src/server.h:1.44.6.1 Wed Dec 21 13:25:10 2005
+++ ccvs/src/server.h Fri Jan 6 20:04:38 2006
@@ -242,6 +242,7 @@
const char *flags);
void server_base_merge (struct file_info *finfo, const char *rev1,
const char *rev2);
+void server_base_signatures (struct file_info *finfo, const char *rev);
bool server_use_bases (void);
void cvs_output (const char *, size_t);
Index: ccvs/src/sign.c
diff -u ccvs/src/sign.c:1.1.6.3 ccvs/src/sign.c:1.1.6.4
--- ccvs/src/sign.c:1.1.6.3 Sun Jan 1 23:06:21 2006
+++ ccvs/src/sign.c Fri Jan 6 20:04:38 2006
@@ -26,6 +26,7 @@
/* Standard headers. */
#include <assert.h>
#include <errno.h>
+#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -36,16 +37,27 @@
#include "xalloc.h"
/* CVS headers. */
+#include "classify.h"
+#include "client.h"
#include "filesubr.h"
+#include "ignore.h"
+#include "recurse.h"
#include "root.h"
#include "run.h"
+#include "server.h" /* Get TRACE (). */
#include "stack.h"
#include "stack.h"
#include "subr.h"
+#include "vers_ts.h"
+/* FIXME: Once cvs.h is pared ot the bare essentials, it may be included for
+ * the following.
+ */
extern int noexec;
+extern int quiet, really_quiet;
+void usage (const char *const *cpp);
@@ -367,3 +379,147 @@
if (server_active) return read_signature (filename, len);
/* else */ return gen_signature (srepos, filename, bin, len);
}
+
+
+
+static int
+sign_fileproc (void *callerdat, struct file_info *finfo)
+{
+ Vers_TS *vers;
+ int err = 0;
+ Ctype status;
+
+ TRACE (TRACE_FUNCTION, "sign_fileproc (%s)", finfo->fullname);
+
+ status = Classify_File (finfo, NULL, NULL, NULL, true,
+ false, &vers, false);
+
+ switch (status)
+ {
+ case T_UNKNOWN: /* unknown file was explicitly asked
+ * about */
+ error (0, 0, "Nothing known about `%s'", finfo->fullname);
+ err++;
+ break;
+ case T_CONFLICT: /* old punt-type errors */
+ case T_NEEDS_MERGE: /* needs merging */
+ case T_MODIFIED: /* locally modified */
+ case T_ADDED: /* added but not committed */
+ case T_REMOVED: /* removed but not committed */
+ error (0, 0, "Locally modified file `%s' may not be signed.",
+ finfo->fullname);
+ err++;
+ break;
+ case T_CHECKOUT: /* needs checkout */
+ if (!vers->ts_user)
+ {
+ assert (vers->vn_user);
+ error (0, 0,
+"File `%s' not present locally (checkout before signing)",
+ finfo->fullname);
+ err++;
+ break;
+ }
+ /* else, fall through */
+ case T_REMOVE_ENTRY: /* needs to be un-registered */
+ case T_PATCH: /* needs patch */
+ case T_UPTODATE: /* file was already up-to-date */
+ if (!isfile (finfo->file))
+ RCS_checkout (finfo->rcs, finfo->file, vers->vn_user,
+ vers->tag, vers->options, NULL, NULL, NULL);
+ if (file_contains_keyword (finfo))
+ {
+ /* Make this a warning, not an error, because the user may
+ * be intentionally signing a file with keywords. Such a file
+ * may still be verified when checked out -ko.
+ */
+ if (!quiet)
+ error (0, 0,
+"warning: signed file `%s' contains at least one RCS keyword",
+ finfo->fullname);
+ }
+
+ RCS_add_openpgp_signature (finfo, vers->vn_user);
+ if (server_active)
+ server_base_signatures (finfo, vers->vn_user);
+ break;
+ default: /* can't ever happen :-) */
+ error (0, 0,
+ "unknown file status %d for file `%s'",
+ status, finfo->file);
+ err++;
+ break;
+ }
+
+ return err;
+}
+
+
+
+static const char *const sign_usage[] =
+{
+ "Usage: %s %s [-lR] [files...]\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
+sign (int argc, char **argv)
+{
+ int c;
+ int err = 0;
+ bool local = false;
+
+ if (argc == -1)
+ usage (sign_usage);
+
+ optind = 0;
+ while ((c = getopt (argc, argv, "+lR")) != -1)
+ {
+ switch (c)
+ {
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case '?':
+ default:
+ usage (sign_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ start_server ();
+
+ ign_setup ();
+
+ if (local)
+ send_arg("-l");
+ send_arg ("--");
+
+ send_files (argc, argv, local, false, 0);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
+
+ send_to_server ("sign\012", 0);
+ err = get_responses_and_close ();
+
+ return err;
+ }
+#endif
+
+ /* start the recursion processor */
+ err = start_recursion (sign_fileproc, NULL, NULL, NULL, NULL, argc, argv,
+ local, W_LOCAL, false, CVS_LOCK_WRITE, NULL, true,
+ NULL);
+
+ return err;
+}
Index: ccvs/src/sign.h
diff -u ccvs/src/sign.h:1.1.6.2 ccvs/src/sign.h:1.1.6.3
--- ccvs/src/sign.h:1.1.6.2 Sat Dec 31 19:51:11 2005
+++ ccvs/src/sign.h Fri Jan 6 20:04:38 2006
@@ -48,4 +48,8 @@
/* Other utilities. */
bool have_sigfile (bool server_active, const char *fn);
char *get_sigfile_name (const char *fn);
+
+/* Sign command. */
+int sign (int argc, char **argv);
+
#endif /* SIGN_H */
Index: ccvs/src/status.c
diff -u /dev/null ccvs/src/status.c:1.68.6.1
--- /dev/null Fri Jan 6 20:04:39 2006
+++ ccvs/src/status.c Fri Jan 6 20:04:38 2006
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ * Portions 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 source distribution.
+ *
+ * Status Information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* CVS headers. */
+#include "classify.h"
+#include "ignore.h"
+#include "recurse.h"
+
+#include "cvs.h"
+
+
+
+static Dtype status_dirproc (void *callerdat, const char *dir,
+ const char *repos, const char *update_dir,
+ List *entries);
+static int status_fileproc (void *callerdat, struct file_info *finfo);
+static int tag_list_proc (Node * p, void *closure);
+
+static int local = 0;
+static int long_format = 0;
+static RCSNode *xrcsnode;
+
+static const char *const status_usage[] =
+{
+ "Usage: %s %s [-vlR] [files...]\n",
+ "\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
+cvsstatus (int argc, char **argv)
+{
+ int c;
+ int err = 0;
+
+ if (argc == -1)
+ usage (status_usage);
+
+ optind = 0;
+ while ((c = getopt (argc, argv, "+vlR")) != -1)
+ {
+ switch (c)
+ {
+ case 'v':
+ long_format = 1;
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case '?':
+ default:
+ usage (status_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ wrap_setup ();
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ start_server ();
+
+ ign_setup ();
+
+ if (long_format)
+ send_arg("-v");
+ if (local)
+ send_arg("-l");
+ send_arg ("--");
+
+ /* 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. */
+
+ send_files (argc, argv, local, 0, 0);
+
+ 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, NULL, status_dirproc,
+ NULL, NULL, argc, argv, local, W_LOCAL,
+ 0, CVS_LOCK_READ, NULL, 1, NULL);
+
+ return (err);
+}
+
+/*
+ * display the status of a file
+ */
+/* ARGSUSED */
+static int
+status_fileproc (void *callerdat, struct file_info *finfo)
+{
+ Ctype status;
+ char *sstat;
+ Vers_TS *vers;
+ Node *node;
+
+ status = Classify_File (finfo, NULL, NULL, NULL, 1, 0, &vers, 0);
+ sstat = "Classify Error";
+ switch (status)
+ {
+ case T_UNKNOWN:
+ sstat = "Unknown";
+ break;
+ case T_CHECKOUT:
+ sstat = "Needs Checkout";
+ break;
+ case T_PATCH:
+ sstat = "Needs Patch";
+ break;
+ case T_CONFLICT:
+ /* FIXME - This message could be clearer. It comes up
+ * when a file exists or has been added in the local sandbox
+ * and a file of the same name has been committed indepenently to
+ * the repository from a different sandbox, as well as when a
+ * timestamp hasn't changed since a merge resulted in conflicts.
+ * It also comes up whether an update has been attempted or not, so
+ * technically, I think the double-add case is not actually a
+ * conflict yet.
+ */
+ sstat = "Unresolved Conflict";
+ break;
+ case T_ADDED:
+ sstat = "Locally Added";
+ break;
+ case T_REMOVED:
+ sstat = "Locally Removed";
+ break;
+ case T_MODIFIED:
+ if (file_has_markers (finfo))
+ sstat = "File had conflicts on merge";
+ else
+ /* Note that we do not re Register() the file when we spot
+ * a resolved conflict like update_fileproc() does on the
+ * premise that status should not alter the sandbox.
+ */
+ sstat = "Locally Modified";
+ break;
+ case T_REMOVE_ENTRY:
+ sstat = "Entry Invalid";
+ break;
+ case T_UPTODATE:
+ sstat = "Up-to-date";
+ break;
+ case T_NEEDS_MERGE:
+ sstat = "Needs Merge";
+ break;
+ case T_TITLE:
+ /* I don't think this case can occur here. Just print
+ "Classify Error". */
+ break;
+ }
+
+ cvs_output ("\
+===================================================================\n", 0);
+ if (vers->ts_user == NULL)
+ {
+ 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
+ {
+ char *buf;
+ buf = Xasprintf ("File: %-17s\tStatus: %s\n\n", finfo->file, sstat);
+ cvs_output (buf, 0);
+ free (buf);
+ }
+
+ if (vers->vn_user == NULL)
+ {
+ 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')
+ cvs_output (" Working revision:\tNew file!\n", 0);
+ else
+ {
+ cvs_output (" Working revision:\t", 0);
+ cvs_output (vers->vn_user, 0);
+
+ /* Only add the UTC timezone if there is a time to use. */
+ if (!server_active && strlen (vers->ts_rcs) > 0)
+ {
+ /* Convert from the asctime() format to ISO 8601 */
+ char *buf;
+
+ cvs_output ("\t", 0);
+
+ /* Allow conversion from CVS/Entries asctime() to ISO 8601 */
+ buf = Xasprintf ("%s UTC", vers->ts_rcs);
+ cvs_output_tagged ("date", buf);
+ free (buf);
+ }
+ cvs_output ("\n", 0);
+ }
+
+ if (vers->vn_rcs == NULL)
+ cvs_output (" Repository revision:\tNo revision control file\n", 0);
+ else
+ {
+ cvs_output (" Repository revision:\t", 0);
+ cvs_output (vers->vn_rcs, 0);
+ cvs_output ("\t", 0);
+ cvs_output (vers->srcfile->print_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)
+ {
+ Entnode *edata;
+
+ edata = vers->entdata;
+ if (edata->tag)
+ {
+ if (vers->vn_rcs == NULL)
+ {
+ cvs_output (" Sticky Tag:\t\t", 0);
+ cvs_output (edata->tag, 0);
+ cvs_output (" - MISSING from RCS file!\n", 0);
+ }
+ else
+ {
+ if (isdigit ((unsigned char) edata->tag[0]))
+ {
+ cvs_output (" Sticky Tag:\t\t", 0);
+ cvs_output (edata->tag, 0);
+ cvs_output ("\n", 0);
+ }
+ else
+ {
+ 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)
+ cvs_output (" Sticky Tag:\t\t(none)\n", 0);
+
+ if (edata->date)
+ {
+ cvs_output (" Sticky Date:\t\t", 0);
+ cvs_output (edata->date, 0);
+ cvs_output ("\n", 0);
+ }
+ else if (!really_quiet)
+ cvs_output (" Sticky Date:\t\t(none)\n", 0);
+
+ if (edata->options && edata->options[0])
+ {
+ cvs_output (" Sticky Options:\t", 0);
+ cvs_output (edata->options, 0);
+ cvs_output ("\n", 0);
+ }
+ else if (!really_quiet)
+ cvs_output (" Sticky Options:\t(none)\n", 0);
+ }
+
+ if (long_format && vers->srcfile)
+ {
+ List *symbols = RCS_symbols(vers->srcfile);
+
+ 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);
+ }
+
+ cvs_output ("\n", 0);
+ freevers_ts (&vers);
+ return (0);
+}
+
+
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+status_dirproc (void *callerdat, const char *dir, const char *repos,
+ const char *update_dir, List *entries)
+{
+ if (!quiet)
+ error (0, 0, "Examining %s", update_dir);
+ return (R_PROCESS);
+}
+
+
+
+/*
+ * Print out a tag and its type
+ */
+static int
+tag_list_proc (Node *p, void *closure)
+{
+ char *branch = NULL;
+ char *buf;
+
+ if (RCS_nodeisbranch (xrcsnode, p->key))
+ branch = RCS_whatbranch(xrcsnode, p->key) ;
+
+ buf = Xasprintf ("\t%-25s\t(%s: %s)\n", p->key,
+ branch ? "branch" : "revision",
+ branch ? branch : (char *)p->data);
+ cvs_output (buf, 0);
+ free (buf);
+
+ if (branch)
+ free (branch);
+
+ return (0);
+}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Cvs-cvs] ccvs/src ChangeLog client.c client.h commit.c l... [signed-commits3],
Derek Robert Price <=