summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gnu/usr.bin/cvs/src/admin.c909
-rw-r--r--usr.bin/cvs/admin.c7
2 files changed, 845 insertions, 71 deletions
diff --git a/gnu/usr.bin/cvs/src/admin.c b/gnu/usr.bin/cvs/src/admin.c
index b85df3622a9..effd18fe4fb 100644
--- a/gnu/usr.bin/cvs/src/admin.c
+++ b/gnu/usr.bin/cvs/src/admin.c
@@ -3,34 +3,141 @@
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
- * specified in the README file that comes with the CVS 1.4 kit.
+ * specified in the README file that comes with the CVS source distribution.
*
- * Administration
+ * Administration ("cvs admin")
*
- * For now, this is basically a front end for rcs. All options are passed
- * directly on.
*/
#include "cvs.h"
-
-#ifndef lint
-static const char rcsid[] = "$CVSid: @(#)admin.c 1.20 94/09/30 $";
-USE(rcsid);
+#ifdef CVS_ADMIN_GROUP
+#include <grp.h>
#endif
+#include <assert.h>
-static Dtype admin_dirproc PROTO((char *dir, char *repos, char *update_dir));
-static int admin_fileproc PROTO((char *file, char *update_dir,
- char *repository, List *entries,
- List *srcfiles));
+static Dtype admin_dirproc PROTO ((void *callerdat, char *dir,
+ char *repos, char *update_dir,
+ List *entries));
+static int admin_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static const char *const admin_usage[] =
{
- "Usage: %s %s rcs-options files...\n",
+ "Usage: %s %s [options] files...\n",
+ "\t-a users Append (comma-separated) user names to access list.\n",
+ "\t-A file Append another file's access list.\n",
+ "\t-b[rev] Set default branch (highest branch on trunk if omitted).\n",
+ "\t-c string Set comment leader.\n",
+ "\t-e[users] Remove (comma-separated) user names from access list\n",
+ "\t (all names if omitted).\n",
+ "\t-I Run interactively.\n",
+ "\t-k subst Set keyword substitution mode:\n",
+ "\t kv (Default) Substitue keyword and value.\n",
+ "\t kvl Substitue keyword, value, and locker (if any).\n",
+ "\t k Substitue keyword only.\n",
+ "\t o Preserve original string.\n",
+ "\t b Like o, but mark file as binary.\n",
+ "\t v Substitue value only.\n",
+ "\t-l[rev] Lock revision (latest revision on branch,\n",
+ "\t latest revision on trunk if omitted).\n",
+ "\t-L Set strict locking.\n",
+ "\t-m rev:msg Replace revision's log message.\n",
+ "\t-n tag[:[rev]] Tag branch or revision. If :rev is omitted,\n",
+ "\t delete the tag; if rev is omitted, tag the latest\n",
+ "\t revision on the default branch.\n",
+ "\t-N tag[:[rev]] Same as -n except override existing tag.\n",
+ "\t-o range Delete (outdate) specified range of revisions:\n",
+ "\t rev1:rev2 Between rev1 and rev2, including rev1 and rev2.\n",
+ "\t rev1::rev2 Between rev1 and rev2, excluding rev1 and rev2.\n",
+ "\t rev: rev and following revisions on the same branch.\n",
+ "\t rev:: After rev on the same branch.\n",
+ "\t :rev rev and previous revisions on the same branch.\n",
+ "\t ::rev Before rev on the same branch.\n",
+ "\t rev Just rev.\n",
+ "\t-q Run quietly.\n",
+ "\t-s state[:rev] Set revision state (latest revision on branch,\n",
+ "\t latest revision on trunk if omitted).\n",
+ "\t-t[file] Get descriptive text from file (stdin if omitted).\n",
+ "\t-t-string Set descriptive text.\n",
+ "\t-u[rev] Unlock the revision (latest revision on branch,\n",
+ "\t latest revision on trunk if omitted).\n",
+ "\t-U Unset strict locking.\n",
+ "(Specify the --help global option for a list of other help options)\n",
NULL
};
-static int ac;
-static char **av;
+/* This structure is used to pass information through start_recursion. */
+struct admin_data
+{
+ /* Set default branch (-b). It is "-b" followed by the value
+ given, or NULL if not specified, or merely "-b" if -b is
+ specified without a value. */
+ char *branch;
+
+ /* Set comment leader (-c). It is "-c" followed by the value
+ given, or NULL if not specified. The comment leader is
+ relevant only for old versions of RCS, but we let people set it
+ anyway. */
+ char *comment;
+
+ /* Set strict locking (-L). */
+ int set_strict;
+
+ /* Set nonstrict locking (-U). */
+ int set_nonstrict;
+
+ /* Delete revisions (-o). It is "-o" followed by the value specified. */
+ char *delete_revs;
+
+ /* Keyword substitution mode (-k), e.g. "-kb". */
+ char *kflag;
+
+ /* Description (-t). */
+ char *desc;
+
+ /* Interactive (-I). Problematic with client/server. */
+ int interactive;
+
+ /* This is the cheesy part. It is a vector with the options which
+ we don't deal with above (e.g. "-afoo" "-abar,baz"). In the future
+ this presumably will be replaced by other variables which break
+ out the data in a more convenient fashion. AV as well as each of
+ the strings it points to is malloc'd. */
+ int ac;
+ char **av;
+ int av_alloc;
+};
+
+/* Add an argument. OPT is the option letter, e.g. 'a'. ARG is the
+ argument to that option, or NULL if omitted (whether NULL can actually
+ happen depends on whether the option was specified as optional to
+ getopt). */
+static void
+arg_add (dat, opt, arg)
+ struct admin_data *dat;
+ int opt;
+ char *arg;
+{
+ char *newelt = xmalloc ((arg == NULL ? 0 : strlen (arg)) + 3);
+ strcpy (newelt, "-");
+ newelt[1] = opt;
+ if (arg == NULL)
+ newelt[2] = '\0';
+ else
+ strcpy (newelt + 2, arg);
+
+ if (dat->av_alloc == 0)
+ {
+ dat->av_alloc = 1;
+ dat->av = (char **) xmalloc (dat->av_alloc * sizeof (*dat->av));
+ }
+ else if (dat->ac >= dat->av_alloc)
+ {
+ dat->av_alloc *= 2;
+ dat->av = (char **) xrealloc (dat->av,
+ dat->av_alloc * sizeof (*dat->av));
+ }
+ dat->av[dat->ac++] = newelt;
+}
int
admin (argc, argv)
@@ -38,54 +145,389 @@ admin (argc, argv)
char **argv;
{
int err;
+#ifdef CVS_ADMIN_GROUP
+ struct group *grp;
+ struct group *getgrnam();
+#endif
+ struct admin_data admin_data;
+ int c;
+ int i;
+ int only_k_option;
if (argc <= 1)
usage (admin_usage);
wrap_setup ();
- /* skip all optional arguments to see if we have any file names */
- for (ac = 1; ac < argc; ac++)
- if (argv[ac][0] != '-')
- break;
- argc -= ac;
- av = argv + 1;
- argv += ac;
- ac--;
- if (ac == 0 || argc == 0)
- usage (admin_usage);
+ memset (&admin_data, 0, sizeof admin_data);
-#ifdef CLIENT_SUPPORT
- if (client_active)
+ /* TODO: get rid of `-' switch notation in admin_data. For
+ example, admin_data->branch should be not `-bfoo' but simply `foo'. */
+
+ optind = 0;
+ only_k_option = 1;
+ while ((c = getopt (argc, argv,
+ "+ib::c:a:A:e::l::u::LUn:N:m:o:s:t::IqxV:k:")) != -1)
+ {
+ if (c != 'k')
+ only_k_option = 0;
+
+ switch (c)
+ {
+ case 'i':
+ /* This has always been documented as useless in cvs.texinfo
+ and it really is--admin_fileproc silently does nothing
+ if vers->vn_user is NULL. */
+ error (0, 0, "the -i option to admin is not supported");
+ error (0, 0, "run add or import to create an RCS file");
+ goto usage_error;
+
+ case 'b':
+ if (admin_data.branch != NULL)
+ {
+ error (0, 0, "duplicate 'b' option");
+ goto usage_error;
+ }
+ if (optarg == NULL)
+ admin_data.branch = xstrdup ("-b");
+ else
+ {
+ admin_data.branch = xmalloc (strlen (optarg) + 5);
+ strcpy (admin_data.branch, "-b");
+ strcat (admin_data.branch, optarg);
+ }
+ break;
+
+ case 'c':
+ if (admin_data.comment != NULL)
+ {
+ error (0, 0, "duplicate 'c' option");
+ goto usage_error;
+ }
+ admin_data.comment = xmalloc (strlen (optarg) + 5);
+ strcpy (admin_data.comment, "-c");
+ strcat (admin_data.comment, optarg);
+ break;
+
+ case 'a':
+ arg_add (&admin_data, 'a', optarg);
+ break;
+
+ case 'A':
+ /* In the client/server case, this is cheesy because
+ we just pass along the name of the RCS file, which
+ then will want to exist on the server. This is
+ accidental; having the client specify a pathname on
+ the server is not a design feature of the protocol. */
+ arg_add (&admin_data, 'A', optarg);
+ break;
+
+ case 'e':
+ arg_add (&admin_data, 'e', optarg);
+ break;
+
+ case 'l':
+ /* Note that multiple -l options are legal. */
+ arg_add (&admin_data, 'l', optarg);
+ break;
+
+ case 'u':
+ /* Note that multiple -u options are legal. */
+ arg_add (&admin_data, 'u', optarg);
+ break;
+
+ case 'L':
+ /* Probably could also complain if -L is specified multiple
+ times, although RCS doesn't and I suppose it is reasonable
+ just to have it mean the same as a single -L. */
+ if (admin_data.set_nonstrict)
+ {
+ error (0, 0, "-U and -L are incompatible");
+ goto usage_error;
+ }
+ admin_data.set_strict = 1;
+ break;
+
+ case 'U':
+ /* Probably could also complain if -U is specified multiple
+ times, although RCS doesn't and I suppose it is reasonable
+ just to have it mean the same as a single -U. */
+ if (admin_data.set_strict)
+ {
+ error (0, 0, "-U and -L are incompatible");
+ goto usage_error;
+ }
+ admin_data.set_nonstrict = 1;
+ break;
+
+ case 'n':
+ /* Mostly similar to cvs tag. Could also be parsing
+ the syntax of optarg, although for now we just pass
+ it to rcs as-is. Note that multiple -n options are
+ legal. */
+ arg_add (&admin_data, 'n', optarg);
+ break;
+
+ case 'N':
+ /* Mostly similar to cvs tag. Could also be parsing
+ the syntax of optarg, although for now we just pass
+ it to rcs as-is. Note that multiple -N options are
+ legal. */
+ arg_add (&admin_data, 'N', optarg);
+ break;
+
+ case 'm':
+ /* Change log message. Could also be parsing the syntax
+ of optarg, although for now we just pass it to rcs
+ as-is. Note that multiple -m options are legal. */
+ arg_add (&admin_data, 'm', optarg);
+ break;
+
+ case 'o':
+ /* Delete revisions. Probably should also be parsing the
+ syntax of optarg, so that the client can give errors
+ rather than making the server take care of that.
+ Other than that I'm not sure whether it matters much
+ whether we parse it here or in admin_fileproc.
+
+ Note that multiple -o options are illegal, in RCS
+ as well as here. */
+
+ if (admin_data.delete_revs != NULL)
+ {
+ error (0, 0, "duplicate '-o' option");
+ goto usage_error;
+ }
+ admin_data.delete_revs = xmalloc (strlen (optarg) + 5);
+ strcpy (admin_data.delete_revs, "-o");
+ strcat (admin_data.delete_revs, optarg);
+ break;
+
+ case 's':
+ /* Note that multiple -s options are legal. */
+ arg_add (&admin_data, 's', optarg);
+ break;
+
+ case 't':
+ if (admin_data.desc != NULL)
+ {
+ error (0, 0, "duplicate 't' option");
+ goto usage_error;
+ }
+ if (optarg != NULL && optarg[0] == '-')
+ admin_data.desc = xstrdup (optarg + 1);
+ else
+ {
+ size_t bufsize = 0;
+ size_t len;
+
+ get_file (optarg, optarg, "r", &admin_data.desc,
+ &bufsize, &len);
+ }
+ break;
+
+ case 'I':
+ /* At least in RCS this can be specified several times,
+ with the same meaning as being specified once. */
+ admin_data.interactive = 1;
+ break;
+
+ case 'q':
+ /* Silently set the global really_quiet flag. This keeps admin in
+ * sync with the RCS man page and allows us to silently support
+ * older servers when necessary.
+ *
+ * Some logic says we might want to output a deprecation warning
+ * here, but I'm opting not to in order to stay quietly in sync
+ * with the RCS man page.
+ */
+ really_quiet = 1;
+ break;
+
+ case 'x':
+ error (0, 0, "the -x option has never done anything useful");
+ error (0, 0, "RCS files in CVS always end in ,v");
+ goto usage_error;
+
+ case 'V':
+ /* No longer supported. */
+ error (0, 0, "the `-V' option is obsolete");
+ break;
+
+ case 'k':
+ if (admin_data.kflag != NULL)
+ {
+ error (0, 0, "duplicate '-k' option");
+ goto usage_error;
+ }
+ admin_data.kflag = RCS_check_kflag (optarg);
+ break;
+ default:
+ case '?':
+ /* getopt will have printed an error message. */
+
+ usage_error:
+ /* Don't use command_name; it might be "server". */
+ error (1, 0, "specify %s -H admin for usage information",
+ program_name);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef CVS_ADMIN_GROUP
+ /* The use of `cvs admin -k' is unrestricted. However, any other
+ option is restricted if the group CVS_ADMIN_GROUP exists. */
+ if (!only_k_option &&
+ (grp = getgrnam(CVS_ADMIN_GROUP)) != NULL)
{
- int i;
+#ifdef HAVE_GETGROUPS
+ gid_t *grps;
+ int n;
+ /* get number of auxiliary groups */
+ n = getgroups (0, NULL);
+ if (n < 0)
+ error (1, errno, "unable to get number of auxiliary groups");
+ grps = (gid_t *) xmalloc((n + 1) * sizeof *grps);
+ n = getgroups (n, grps);
+ if (n < 0)
+ error (1, errno, "unable to get list of auxiliary groups");
+ grps[n] = getgid();
+ for (i = 0; i <= n; i++)
+ if (grps[i] == grp->gr_gid) break;
+ free (grps);
+ if (i > n)
+ error (1, 0, "usage is restricted to members of the group %s",
+ CVS_ADMIN_GROUP);
+#else
+ char *me = getcaller();
+ char **grnam;
+
+ for (grnam = grp->gr_mem; *grnam; grnam++)
+ if (strcmp (*grnam, me) == 0) break;
+ if (!*grnam && getgid() != grp->gr_gid)
+ error (1, 0, "usage is restricted to members of the group %s",
+ CVS_ADMIN_GROUP);
+#endif
+ }
+#endif
+
+ for (i = 0; i < admin_data.ac; ++i)
+ {
+ assert (admin_data.av[i][0] == '-');
+ switch (admin_data.av[i][1])
+ {
+ case 'm':
+ case 'l':
+ case 'u':
+ check_numeric (&admin_data.av[i][2], argc, argv);
+ break;
+ default:
+ break;
+ }
+ }
+ if (admin_data.branch != NULL)
+ check_numeric (admin_data.branch + 2, argc, argv);
+ if (admin_data.delete_revs != NULL)
+ {
+ char *p;
+
+ check_numeric (admin_data.delete_revs + 2, argc, argv);
+ p = strchr (admin_data.delete_revs + 2, ':');
+ if (p != NULL && isdigit ((unsigned char) p[1]))
+ check_numeric (p + 1, argc, argv);
+ else if (p != NULL && p[1] == ':' && isdigit ((unsigned char) p[2]))
+ check_numeric (p + 2, argc, argv);
+ }
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
/* We're the client side. Fire up the remote server. */
start_server ();
ign_setup ();
- for (i = 1; i <= ac; ++i)
- send_arg (av[i]);
+ /* Note that option_with_arg does not work for us, because some
+ of the options must be sent without a space between the option
+ and its argument. */
+ if (admin_data.interactive)
+ error (1, 0, "-I option not useful with client/server");
+ if (admin_data.branch != NULL)
+ send_arg (admin_data.branch);
+ if (admin_data.comment != NULL)
+ send_arg (admin_data.comment);
+ if (admin_data.set_strict)
+ send_arg ("-L");
+ if (admin_data.set_nonstrict)
+ send_arg ("-U");
+ if (admin_data.delete_revs != NULL)
+ send_arg (admin_data.delete_revs);
+ if (admin_data.desc != NULL)
+ {
+ char *p = admin_data.desc;
+ send_to_server ("Argument -t-", 0);
+ while (*p)
+ {
+ if (*p == '\n')
+ {
+ send_to_server ("\012Argumentx ", 0);
+ ++p;
+ }
+ else
+ {
+ char *q = strchr (p, '\n');
+ if (q == NULL) q = p + strlen (p);
+ send_to_server (p, q - p);
+ p = q;
+ }
+ }
+ send_to_server ("\012", 1);
+ }
+ /* Send this for all really_quiets since we know that it will be silently
+ * ignored when unneeded. This supports old servers.
+ */
+ if (really_quiet)
+ send_arg ("-q");
+ if (admin_data.kflag != NULL)
+ send_arg (admin_data.kflag);
-#if 0
- /* FIXME: We shouldn't have to send current files, but I'm not sure
- whether it works. So send the files --
- it's slower but it works. */
- send_file_names (argc, argv);
-#else
- send_files (argc, argv, 0, 0);
-#endif
- if (fprintf (to_server, "admin\n") < 0)
- error (1, errno, "writing to server");
- return get_responses_and_close ();
+ for (i = 0; i < admin_data.ac; ++i)
+ send_arg (admin_data.av[i]);
+
+ send_files (argc, argv, 0, 0, SEND_NO_CONTENTS);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
+ send_to_server ("admin\012", 0);
+ err = get_responses_and_close ();
+ goto return_it;
}
#endif /* CLIENT_SUPPORT */
- /* start the recursion processor */
- err = start_recursion (admin_fileproc, (int (*) ()) NULL, admin_dirproc,
- (int (*) ()) NULL, argc, argv, 0,
- W_LOCAL, 0, 1, (char *) NULL, 1, 0);
+ lock_tree_for_write (argc, argv, 0, W_LOCAL, 0);
+
+ err = start_recursion (admin_fileproc, (FILESDONEPROC) NULL, admin_dirproc,
+ (DIRLEAVEPROC) NULL, (void *)&admin_data,
+ argc, argv, 0,
+ W_LOCAL, 0, 0, (char *) NULL, 1);
+ Lock_Cleanup ();
+
+ return_it:
+ if (admin_data.branch != NULL)
+ free (admin_data.branch);
+ if (admin_data.comment != NULL)
+ free (admin_data.comment);
+ if (admin_data.delete_revs != NULL)
+ free (admin_data.delete_revs);
+ if (admin_data.kflag != NULL)
+ free (admin_data.kflag);
+ if (admin_data.desc != NULL)
+ free (admin_data.desc);
+ for (i = 0; i < admin_data.ac; ++i)
+ free (admin_data.av[i]);
+ if (admin_data.av != NULL)
+ free (admin_data.av);
+
return (err);
}
@@ -94,43 +536,368 @@ admin (argc, argv)
*/
/* ARGSUSED */
static int
-admin_fileproc (file, update_dir, repository, entries, srcfiles)
- char *file;
- char *update_dir;
- char *repository;
- List *entries;
- List *srcfiles;
+admin_fileproc (callerdat, finfo)
+ void *callerdat;
+ struct file_info *finfo;
{
+ struct admin_data *admin_data = (struct admin_data *) callerdat;
Vers_TS *vers;
char *version;
- char **argv;
- int argc;
- int retcode = 0;
+ int i;
+ int status = 0;
+ RCSNode *rcs, *rcs2;
- vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
- file, 0, 0, entries, srcfiles);
+ vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
version = vers->vn_user;
if (version == NULL)
- return (0);
+ goto exitfunc;
else if (strcmp (version, "0") == 0)
{
- error (0, 0, "cannot admin newly added file `%s'", file);
- return (0);
+ error (0, 0, "cannot admin newly added file `%s'", finfo->file);
+ goto exitfunc;
+ }
+
+ rcs = vers->srcfile;
+ if (rcs == NULL)
+ {
+ error (0, 0, "lost revision control file for `%s'", finfo->file);
+ goto exitfunc;
+ }
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
+
+ status = 0;
+
+ if (!really_quiet)
+ {
+ cvs_output ("RCS file: ", 0);
+ cvs_output (rcs->path, 0);
+ cvs_output ("\n", 1);
}
- run_setup ("%s%s", Rcsbin, RCS);
- for (argc = ac, argv = av; argc; argc--, argv++)
- run_arg (*argv);
- run_arg (vers->srcfile->path);
- if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ if (admin_data->branch != NULL)
+ {
+ char *branch = &admin_data->branch[2];
+ if (*branch != '\0' && ! isdigit ((unsigned char) *branch))
+ {
+ branch = RCS_whatbranch (rcs, admin_data->branch + 2);
+ if (branch == NULL)
+ {
+ error (0, 0, "%s: Symbolic name %s is undefined.",
+ rcs->path, admin_data->branch + 2);
+ status = 1;
+ }
+ }
+ if (status == 0)
+ RCS_setbranch (rcs, branch);
+ if (branch != NULL && branch != &admin_data->branch[2])
+ free (branch);
+ }
+ if (admin_data->comment != NULL)
{
- if (!quiet)
- error (0, retcode == -1 ? errno : 0,
- "%s failed for `%s'", RCS, file);
- return (1);
+ if (rcs->comment != NULL)
+ free (rcs->comment);
+ rcs->comment = xstrdup (admin_data->comment + 2);
}
- return (0);
+ if (admin_data->set_strict)
+ rcs->strict_locks = 1;
+ if (admin_data->set_nonstrict)
+ rcs->strict_locks = 0;
+ if (admin_data->delete_revs != NULL)
+ {
+ char *s, *t, *rev1, *rev2;
+ /* Set for :, clear for ::. */
+ int inclusive;
+ char *t2;
+
+ s = admin_data->delete_revs + 2;
+ inclusive = 1;
+ t = strchr (s, ':');
+ if (t != NULL)
+ {
+ if (t[1] == ':')
+ {
+ inclusive = 0;
+ t2 = t + 2;
+ }
+ else
+ t2 = t + 1;
+ }
+
+ /* Note that we don't support '-' for ranges. RCS considers it
+ obsolete and it is problematic with tags containing '-'. "cvs log"
+ has made the same decision. */
+
+ if (t == NULL)
+ {
+ /* -orev */
+ rev1 = xstrdup (s);
+ rev2 = xstrdup (s);
+ }
+ else if (t == s)
+ {
+ /* -o:rev2 */
+ rev1 = NULL;
+ rev2 = xstrdup (t2);
+ }
+ else
+ {
+ *t = '\0';
+ rev1 = xstrdup (s);
+ *t = ':'; /* probably unnecessary */
+ if (*t2 == '\0')
+ /* -orev1: */
+ rev2 = NULL;
+ else
+ /* -orev1:rev2 */
+ rev2 = xstrdup (t2);
+ }
+
+ if (rev1 == NULL && rev2 == NULL)
+ {
+ /* RCS segfaults if `-o:' is given */
+ error (0, 0, "no valid revisions specified in `%s' option",
+ admin_data->delete_revs);
+ status = 1;
+ }
+ else
+ {
+ status |= RCS_delete_revs (rcs, rev1, rev2, inclusive);
+ if (rev1)
+ free (rev1);
+ if (rev2)
+ free (rev2);
+ }
+ }
+ if (admin_data->desc != NULL)
+ {
+ free (rcs->desc);
+ rcs->desc = xstrdup (admin_data->desc);
+ }
+ if (admin_data->kflag != NULL)
+ {
+ char *kflag = admin_data->kflag + 2;
+ char *oldexpand = RCS_getexpand (rcs);
+ if (oldexpand == NULL || strcmp (oldexpand, kflag) != 0)
+ RCS_setexpand (rcs, kflag);
+ }
+
+ /* Handle miscellaneous options. TODO: decide whether any or all
+ of these should have their own fields in the admin_data
+ structure. */
+ for (i = 0; i < admin_data->ac; ++i)
+ {
+ char *arg;
+ char *p, *rev, *revnum, *tag, *msg;
+ char **users;
+ int argc, u;
+ Node *n;
+ RCSVers *delta;
+
+ arg = admin_data->av[i];
+ switch (arg[1])
+ {
+ case 'a': /* fall through */
+ case 'e':
+ line2argv (&argc, &users, arg + 2, " ,\t\n");
+ if (arg[1] == 'a')
+ for (u = 0; u < argc; ++u)
+ RCS_addaccess (rcs, users[u]);
+ else if (argc == 0)
+ RCS_delaccess (rcs, NULL);
+ else
+ for (u = 0; u < argc; ++u)
+ RCS_delaccess (rcs, users[u]);
+ free_names (&argc, users);
+ break;
+ case 'A':
+
+ /* See admin-19a-admin and friends in sanity.sh for
+ relative pathnames. It makes sense to think in
+ terms of a syntax which give pathnames relative to
+ the repository or repository corresponding to the
+ current directory or some such (and perhaps don't
+ include ,v), but trying to worry about such things
+ is a little pointless unless you first worry about
+ whether "cvs admin -A" as a whole makes any sense
+ (currently probably not, as access lists don't
+ affect the behavior of CVS). */
+
+ rcs2 = RCS_parsercsfile (arg + 2);
+ if (rcs2 == NULL)
+ error (1, 0, "cannot continue");
+
+ p = xstrdup (RCS_getaccess (rcs2));
+ line2argv (&argc, &users, p, " \t\n");
+ free (p);
+ freercsnode (&rcs2);
+
+ for (u = 0; u < argc; ++u)
+ RCS_addaccess (rcs, users[u]);
+ free_names (&argc, users);
+ break;
+ case 'n': /* fall through */
+ case 'N':
+ if (arg[2] == '\0')
+ {
+ cvs_outerr ("missing symbolic name after ", 0);
+ cvs_outerr (arg, 0);
+ cvs_outerr ("\n", 1);
+ break;
+ }
+ p = strchr (arg, ':');
+ if (p == NULL)
+ {
+ if (RCS_deltag (rcs, arg + 2) != 0)
+ {
+ error (0, 0, "%s: Symbolic name %s is undefined.",
+ rcs->path,
+ arg + 2);
+ status = 1;
+ continue;
+ }
+ break;
+ }
+ *p = '\0';
+ tag = xstrdup (arg + 2);
+ *p++ = ':';
+
+ /* Option `n' signals an error if this tag is already bound. */
+ if (arg[1] == 'n')
+ {
+ n = findnode (RCS_symbols (rcs), tag);
+ if (n != NULL)
+ {
+ error (0, 0,
+ "%s: symbolic name %s already bound to %s",
+ rcs->path,
+ tag, n->data);
+ status = 1;
+ free (tag);
+ continue;
+ }
+ }
+
+ /* Attempt to perform the requested tagging. */
+
+ if ((*p == 0 && (rev = RCS_head (rcs)))
+ || (rev = RCS_tag2rev (rcs, p))) /* tag2rev may exit */
+ {
+ RCS_check_tag (tag); /* exit if not a valid tag */
+ RCS_settag (rcs, tag, rev);
+ free (rev);
+ }
+ else
+ {
+ if (!really_quiet)
+ error (0, 0,
+ "%s: Symbolic name or revision %s is undefined.",
+ rcs->path, p);
+ status = 1;
+ }
+ free (tag);
+ break;
+ case 's':
+ p = strchr (arg, ':');
+ if (p == NULL)
+ {
+ tag = xstrdup (arg + 2);
+ rev = RCS_head (rcs);
+ }
+ else
+ {
+ *p = '\0';
+ tag = xstrdup (arg + 2);
+ *p++ = ':';
+ rev = xstrdup (p);
+ }
+ revnum = RCS_gettag (rcs, rev, 0, NULL);
+ if (revnum != NULL)
+ {
+ n = findnode (rcs->versions, revnum);
+ free (revnum);
+ }
+ else
+ n = NULL;
+ if (n == NULL)
+ {
+ error (0, 0,
+ "%s: can't set state of nonexisting revision %s",
+ rcs->path,
+ rev);
+ free (rev);
+ status = 1;
+ continue;
+ }
+ free (rev);
+ delta = (RCSVers *) n->data;
+ free (delta->state);
+ delta->state = tag;
+ break;
+
+ case 'm':
+ p = strchr (arg, ':');
+ if (p == NULL)
+ {
+ error (0, 0, "%s: -m option lacks revision number",
+ rcs->path);
+ status = 1;
+ continue;
+ }
+ *p = '\0';
+ rev = RCS_gettag (rcs, arg + 2, 0, NULL);
+ if (rev == NULL)
+ {
+ error (0, 0, "%s: no such revision %s", rcs->path, rev);
+ status = 1;
+ continue;
+ }
+ *p++ = ':';
+ msg = p;
+
+ n = findnode (rcs->versions, rev);
+ free (rev);
+ delta = (RCSVers *) n->data;
+ if (delta->text == NULL)
+ {
+ delta->text = (Deltatext *) xmalloc (sizeof (Deltatext));
+ memset ((void *) delta->text, 0, sizeof (Deltatext));
+ }
+ delta->text->version = xstrdup (delta->version);
+ delta->text->log = make_message_rcslegal (msg);
+ break;
+
+ case 'l':
+ status |= RCS_lock (rcs, arg[2] ? arg + 2 : NULL, 0);
+ break;
+ case 'u':
+ status |= RCS_unlock (rcs, arg[2] ? arg + 2 : NULL, 0);
+ break;
+ default: assert(0); /* can't happen */
+ }
+ }
+
+ if (status == 0)
+ {
+ RCS_rewrite (rcs, NULL, NULL);
+ if (!really_quiet)
+ cvs_output ("done\n", 5);
+ }
+ else
+ {
+ /* Note that this message should only occur after another
+ message has given a more specific error. The point of this
+ additional message is to make it clear that the previous problems
+ caused CVS to forget about the idea of modifying the RCS file. */
+ if (!really_quiet)
+ error (0, 0, "RCS file for `%s' not modified.", finfo->file);
+ RCS_abandon (rcs);
+ }
+
+ exitfunc:
+ freevers_ts (&vers);
+ return status;
}
/*
@@ -138,10 +905,12 @@ admin_fileproc (file, update_dir, repository, entries, srcfiles)
*/
/* ARGSUSED */
static Dtype
-admin_dirproc (dir, repos, update_dir)
+admin_dirproc (callerdat, dir, repos, update_dir, entries)
+ void *callerdat;
char *dir;
char *repos;
char *update_dir;
+ List *entries;
{
if (!quiet)
error (0, 0, "Administrating %s", update_dir);
diff --git a/usr.bin/cvs/admin.c b/usr.bin/cvs/admin.c
index 452af8f1615..c5796ca5b33 100644
--- a/usr.bin/cvs/admin.c
+++ b/usr.bin/cvs/admin.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: admin.c,v 1.57 2008/03/09 03:14:52 joris Exp $ */
+/* $OpenBSD: admin.c,v 1.58 2008/05/11 12:16:00 tobias Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* Copyright (c) 2005 Joris Vink <joris@openbsd.org>
@@ -222,6 +222,11 @@ cvs_admin_local(struct cvs_file *cf)
return;
}
+ if (cf->file_rcs == NULL) {
+ cvs_log(LP_ERR, "lost RCS file for `%s'", cf->file_path);
+ return;
+ }
+
if (verbosity > 0)
cvs_printf("RCS file: %s\n", cf->file_rcs->rf_path);