2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
7 * Portions Copyright (c) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (c) 1989-1992, Brian Berliner
10 * You may distribute under the terms of the GNU General Public License as
11 * specified in the README file that comes with the CVS source distribution.
13 * Administration ("cvs admin")
18 #ifdef CVS_ADMIN_GROUP
23 static Dtype admin_dirproc PROTO ((void *callerdat, const char *dir,
24 const char *repos, const char *update_dir,
26 static int admin_fileproc PROTO ((void *callerdat, struct file_info *finfo));
28 static const char *const admin_usage[] =
30 "Usage: %s %s [options] files...\n",
31 "\t-a users Append (comma-separated) user names to access list.\n",
32 "\t-A file Append another file's access list.\n",
33 "\t-b[rev] Set default branch (highest branch on trunk if omitted).\n",
34 "\t-c string Set comment leader.\n",
35 "\t-e[users] Remove (comma-separated) user names from access list\n",
36 "\t (all names if omitted).\n",
37 "\t-I Run interactively.\n",
38 "\t-k subst Set keyword substitution mode:\n",
39 "\t kv (Default) Substitute keyword and value.\n",
40 "\t kvl Substitute keyword, value, and locker (if any).\n",
41 "\t k Substitute keyword only.\n",
42 "\t o Preserve original string.\n",
43 "\t b Like o, but mark file as binary.\n",
44 "\t v Substitute value only.\n",
45 "\t-l[rev] Lock revision (latest revision on branch,\n",
46 "\t latest revision on trunk if omitted).\n",
47 "\t-L Set strict locking.\n",
48 "\t-m rev:msg Replace revision's log message.\n",
49 "\t-n tag[:[rev]] Tag branch or revision. If :rev is omitted,\n",
50 "\t delete the tag; if rev is omitted, tag the latest\n",
51 "\t revision on the default branch.\n",
52 "\t-N tag[:[rev]] Same as -n except override existing tag.\n",
53 "\t-o range Delete (outdate) specified range of revisions:\n",
54 "\t rev1:rev2 Between rev1 and rev2, including rev1 and rev2.\n",
55 "\t rev1::rev2 Between rev1 and rev2, excluding rev1 and rev2.\n",
56 "\t rev: rev and following revisions on the same branch.\n",
57 "\t rev:: After rev on the same branch.\n",
58 "\t :rev rev and previous revisions on the same branch.\n",
59 "\t ::rev Before rev on the same branch.\n",
61 "\t-q Run quietly.\n",
62 "\t-s state[:rev] Set revision state (latest revision on branch,\n",
63 "\t latest revision on trunk if omitted).\n",
64 "\t-t[file] Get descriptive text from file (stdin if omitted).\n",
65 "\t-t-string Set descriptive text.\n",
66 "\t-u[rev] Unlock the revision (latest revision on branch,\n",
67 "\t latest revision on trunk if omitted).\n",
68 "\t-U Unset strict locking.\n",
69 "(Specify the --help global option for a list of other help options)\n",
73 /* This structure is used to pass information through start_recursion. */
76 /* Set default branch (-b). It is "-b" followed by the value
77 given, or NULL if not specified, or merely "-b" if -b is
78 specified without a value. */
81 /* Set comment leader (-c). It is "-c" followed by the value
82 given, or NULL if not specified. The comment leader is
83 relevant only for old versions of RCS, but we let people set it
87 /* Set strict locking (-L). */
90 /* Set nonstrict locking (-U). */
93 /* Delete revisions (-o). It is "-o" followed by the value specified. */
96 /* Keyword substitution mode (-k), e.g. "-kb". */
99 /* Description (-t). */
102 /* Interactive (-I). Problematic with client/server. */
105 /* This is the cheesy part. It is a vector with the options which
106 we don't deal with above (e.g. "-afoo" "-abar,baz"). In the future
107 this presumably will be replaced by other variables which break
108 out the data in a more convenient fashion. AV as well as each of
109 the strings it points to is malloc'd. */
115 /* Add an argument. OPT is the option letter, e.g. 'a'. ARG is the
116 argument to that option, or NULL if omitted (whether NULL can actually
117 happen depends on whether the option was specified as optional to
120 arg_add (dat, opt, arg)
121 struct admin_data *dat;
125 char *newelt = xmalloc ((arg == NULL ? 0 : strlen (arg)) + 3);
126 strcpy (newelt, "-");
131 strcpy (newelt + 2, arg);
133 if (dat->av_alloc == 0)
136 dat->av = (char **) xmalloc (dat->av_alloc * sizeof (*dat->av));
138 else if (dat->ac >= dat->av_alloc)
141 dat->av = (char **) xrealloc (dat->av,
142 dat->av_alloc * sizeof (*dat->av));
144 dat->av[dat->ac++] = newelt;
153 #ifdef CVS_ADMIN_GROUP
155 struct group *getgrnam();
157 struct admin_data admin_data;
167 memset (&admin_data, 0, sizeof admin_data);
169 /* TODO: get rid of `-' switch notation in admin_data. For
170 example, admin_data->branch should be not `-bfoo' but simply `foo'. */
174 while ((c = getopt (argc, argv,
175 "+ib::c:a:A:e::l::u::LUn:N:m:o:s:t::IqxV:k:")) != -1)
177 if (c != 'k' && c != 'q')
183 /* This has always been documented as useless in cvs.texinfo
184 and it really is--admin_fileproc silently does nothing
185 if vers->vn_user is NULL. */
186 error (0, 0, "the -i option to admin is not supported");
187 error (0, 0, "run add or import to create an RCS file");
191 if (admin_data.branch != NULL)
193 error (0, 0, "duplicate 'b' option");
197 admin_data.branch = xstrdup ("-b");
200 admin_data.branch = xmalloc (strlen (optarg) + 5);
201 strcpy (admin_data.branch, "-b");
202 strcat (admin_data.branch, optarg);
207 if (admin_data.comment != NULL)
209 error (0, 0, "duplicate 'c' option");
212 admin_data.comment = xmalloc (strlen (optarg) + 5);
213 strcpy (admin_data.comment, "-c");
214 strcat (admin_data.comment, optarg);
218 arg_add (&admin_data, 'a', optarg);
222 /* In the client/server case, this is cheesy because
223 we just pass along the name of the RCS file, which
224 then will want to exist on the server. This is
225 accidental; having the client specify a pathname on
226 the server is not a design feature of the protocol. */
227 arg_add (&admin_data, 'A', optarg);
231 arg_add (&admin_data, 'e', optarg);
235 /* Note that multiple -l options are legal. */
236 arg_add (&admin_data, 'l', optarg);
240 /* Note that multiple -u options are legal. */
241 arg_add (&admin_data, 'u', optarg);
245 /* Probably could also complain if -L is specified multiple
246 times, although RCS doesn't and I suppose it is reasonable
247 just to have it mean the same as a single -L. */
248 if (admin_data.set_nonstrict)
250 error (0, 0, "-U and -L are incompatible");
253 admin_data.set_strict = 1;
257 /* Probably could also complain if -U is specified multiple
258 times, although RCS doesn't and I suppose it is reasonable
259 just to have it mean the same as a single -U. */
260 if (admin_data.set_strict)
262 error (0, 0, "-U and -L are incompatible");
265 admin_data.set_nonstrict = 1;
269 /* Mostly similar to cvs tag. Could also be parsing
270 the syntax of optarg, although for now we just pass
271 it to rcs as-is. Note that multiple -n options are
273 arg_add (&admin_data, 'n', optarg);
277 /* Mostly similar to cvs tag. Could also be parsing
278 the syntax of optarg, although for now we just pass
279 it to rcs as-is. Note that multiple -N options are
281 arg_add (&admin_data, 'N', optarg);
285 /* Change log message. Could also be parsing the syntax
286 of optarg, although for now we just pass it to rcs
287 as-is. Note that multiple -m options are legal. */
288 arg_add (&admin_data, 'm', optarg);
292 /* Delete revisions. Probably should also be parsing the
293 syntax of optarg, so that the client can give errors
294 rather than making the server take care of that.
295 Other than that I'm not sure whether it matters much
296 whether we parse it here or in admin_fileproc.
298 Note that multiple -o options are illegal, in RCS
301 if (admin_data.delete_revs != NULL)
303 error (0, 0, "duplicate '-o' option");
306 admin_data.delete_revs = xmalloc (strlen (optarg) + 5);
307 strcpy (admin_data.delete_revs, "-o");
308 strcat (admin_data.delete_revs, optarg);
312 /* Note that multiple -s options are legal. */
313 arg_add (&admin_data, 's', optarg);
317 if (admin_data.desc != NULL)
319 error (0, 0, "duplicate 't' option");
322 if (optarg != NULL && optarg[0] == '-')
323 admin_data.desc = xstrdup (optarg + 1);
329 get_file (optarg, optarg, "r", &admin_data.desc,
335 /* At least in RCS this can be specified several times,
336 with the same meaning as being specified once. */
337 admin_data.interactive = 1;
341 /* Silently set the global really_quiet flag. This keeps admin in
342 * sync with the RCS man page and allows us to silently support
343 * older servers when necessary.
345 * Some logic says we might want to output a deprecation warning
346 * here, but I'm opting not to in order to stay quietly in sync
347 * with the RCS man page.
353 error (0, 0, "the -x option has never done anything useful");
354 error (0, 0, "RCS files in CVS always end in ,v");
358 /* No longer supported. */
359 error (0, 0, "the `-V' option is obsolete");
363 if (admin_data.kflag != NULL)
365 error (0, 0, "duplicate '-k' option");
368 admin_data.kflag = RCS_check_kflag (optarg);
372 /* getopt will have printed an error message. */
375 /* Don't use cvs_cmd_name; it might be "server". */
376 error (1, 0, "specify %s -H admin for usage information",
383 #ifdef CVS_ADMIN_GROUP
384 /* The use of `cvs admin -k' is unrestricted. However, any other
385 option is restricted if the group CVS_ADMIN_GROUP exists on the
387 /* This is only "secure" on the server, since the user could edit the
388 * RCS file on a local host, but some people like this kind of
389 * check anyhow. The alternative would be to check only when
390 * (server_active) rather than when not on the client.
392 if (!current_parsed_root->isremote && !only_k_option &&
393 (grp = getgrnam(CVS_ADMIN_GROUP)) != NULL)
395 #ifdef HAVE_GETGROUPS
399 /* get number of auxiliary groups */
400 n = getgroups (0, NULL);
402 error (1, errno, "unable to get number of auxiliary groups");
403 grps = (gid_t *) xmalloc((n + 1) * sizeof *grps);
404 n = getgroups (n, grps);
406 error (1, errno, "unable to get list of auxiliary groups");
408 for (i = 0; i <= n; i++)
409 if (grps[i] == grp->gr_gid) break;
412 error (1, 0, "usage is restricted to members of the group %s",
415 char *me = getcaller();
418 for (grnam = grp->gr_mem; *grnam; grnam++)
419 if (strcmp (*grnam, me) == 0) break;
420 if (!*grnam && getgid() != grp->gr_gid)
421 error (1, 0, "usage is restricted to members of the group %s",
425 #endif /* defined CVS_ADMIN_GROUP */
427 for (i = 0; i < admin_data.ac; ++i)
429 assert (admin_data.av[i][0] == '-');
430 switch (admin_data.av[i][1])
435 check_numeric (&admin_data.av[i][2], argc, argv);
441 if (admin_data.branch != NULL)
442 check_numeric (admin_data.branch + 2, argc, argv);
443 if (admin_data.delete_revs != NULL)
447 check_numeric (admin_data.delete_revs + 2, argc, argv);
448 p = strchr (admin_data.delete_revs + 2, ':');
449 if (p != NULL && isdigit ((unsigned char) p[1]))
450 check_numeric (p + 1, argc, argv);
451 else if (p != NULL && p[1] == ':' && isdigit ((unsigned char) p[2]))
452 check_numeric (p + 2, argc, argv);
455 #ifdef CLIENT_SUPPORT
456 if (current_parsed_root->isremote)
458 /* We're the client side. Fire up the remote server. */
463 /* Note that option_with_arg does not work for us, because some
464 of the options must be sent without a space between the option
466 if (admin_data.interactive)
467 error (1, 0, "-I option not useful with client/server");
468 if (admin_data.branch != NULL)
469 send_arg (admin_data.branch);
470 if (admin_data.comment != NULL)
471 send_arg (admin_data.comment);
472 if (admin_data.set_strict)
474 if (admin_data.set_nonstrict)
476 if (admin_data.delete_revs != NULL)
477 send_arg (admin_data.delete_revs);
478 if (admin_data.desc != NULL)
480 char *p = admin_data.desc;
481 send_to_server ("Argument -t-", 0);
486 send_to_server ("\012Argumentx ", 0);
491 char *q = strchr (p, '\n');
492 if (q == NULL) q = p + strlen (p);
493 send_to_server (p, q - p);
497 send_to_server ("\012", 1);
499 /* Send this for all really_quiets since we know that it will be silently
500 * ignored when unneeded. This supports old servers.
504 if (admin_data.kflag != NULL)
505 send_arg (admin_data.kflag);
507 for (i = 0; i < admin_data.ac; ++i)
508 send_arg (admin_data.av[i]);
511 send_files (argc, argv, 0, 0, SEND_NO_CONTENTS);
512 send_file_names (argc, argv, SEND_EXPAND_WILD);
513 send_to_server ("admin\012", 0);
514 err = get_responses_and_close ();
517 #endif /* CLIENT_SUPPORT */
519 lock_tree_for_write (argc, argv, 0, W_LOCAL, 0);
521 err = start_recursion (admin_fileproc, (FILESDONEPROC) NULL, admin_dirproc,
522 (DIRLEAVEPROC) NULL, (void *)&admin_data,
524 W_LOCAL, 0, CVS_LOCK_NONE, (char *) NULL, 1,
529 if (admin_data.branch != NULL)
530 free (admin_data.branch);
531 if (admin_data.comment != NULL)
532 free (admin_data.comment);
533 if (admin_data.delete_revs != NULL)
534 free (admin_data.delete_revs);
535 if (admin_data.kflag != NULL)
536 free (admin_data.kflag);
537 if (admin_data.desc != NULL)
538 free (admin_data.desc);
539 for (i = 0; i < admin_data.ac; ++i)
540 free (admin_data.av[i]);
541 if (admin_data.av != NULL)
542 free (admin_data.av);
548 * Called to run "rcs" on a particular file.
552 admin_fileproc (callerdat, finfo)
554 struct file_info *finfo;
556 struct admin_data *admin_data = (struct admin_data *) callerdat;
563 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
565 version = vers->vn_user;
566 if (version != NULL && strcmp (version, "0") == 0)
568 error (0, 0, "cannot admin newly added file `%s'", finfo->file);
577 error (0, 0, "nothing known about %s", finfo->file);
582 if (rcs->flags & PARTIAL)
583 RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
587 cvs_output ("RCS file: ", 0);
588 cvs_output (rcs->path, 0);
589 cvs_output ("\n", 1);
592 if (admin_data->branch != NULL)
594 char *branch = &admin_data->branch[2];
595 if (*branch != '\0' && ! isdigit ((unsigned char) *branch))
597 branch = RCS_whatbranch (rcs, admin_data->branch + 2);
600 error (0, 0, "%s: Symbolic name %s is undefined.",
601 rcs->path, admin_data->branch + 2);
606 RCS_setbranch (rcs, branch);
607 if (branch != NULL && branch != &admin_data->branch[2])
610 if (admin_data->comment != NULL)
612 if (rcs->comment != NULL)
614 rcs->comment = xstrdup (admin_data->comment + 2);
616 if (admin_data->set_strict)
617 rcs->strict_locks = 1;
618 if (admin_data->set_nonstrict)
619 rcs->strict_locks = 0;
620 if (admin_data->delete_revs != NULL)
622 char *s, *t, *rev1, *rev2;
623 /* Set for :, clear for ::. */
627 s = admin_data->delete_revs + 2;
641 /* Note that we don't support '-' for ranges. RCS considers it
642 obsolete and it is problematic with tags containing '-'. "cvs log"
643 has made the same decision. */
661 *t = ':'; /* probably unnecessary */
670 if (rev1 == NULL && rev2 == NULL)
672 /* RCS segfaults if `-o:' is given */
673 error (0, 0, "no valid revisions specified in `%s' option",
674 admin_data->delete_revs);
679 status |= RCS_delete_revs (rcs, rev1, rev2, inclusive);
686 if (admin_data->desc != NULL)
689 rcs->desc = xstrdup (admin_data->desc);
691 if (admin_data->kflag != NULL)
693 char *kflag = admin_data->kflag + 2;
694 char *oldexpand = RCS_getexpand (rcs);
695 if (oldexpand == NULL || strcmp (oldexpand, kflag) != 0)
696 RCS_setexpand (rcs, kflag);
699 /* Handle miscellaneous options. TODO: decide whether any or all
700 of these should have their own fields in the admin_data
702 for (i = 0; i < admin_data->ac; ++i)
705 char *p, *rev, *revnum, *tag, *msg;
711 arg = admin_data->av[i];
714 case 'a': /* fall through */
716 line2argv (&argc, &users, arg + 2, " ,\t\n");
718 for (u = 0; u < argc; ++u)
719 RCS_addaccess (rcs, users[u]);
721 RCS_delaccess (rcs, NULL);
723 for (u = 0; u < argc; ++u)
724 RCS_delaccess (rcs, users[u]);
725 free_names (&argc, users);
729 /* See admin-19a-admin and friends in sanity.sh for
730 relative pathnames. It makes sense to think in
731 terms of a syntax which give pathnames relative to
732 the repository or repository corresponding to the
733 current directory or some such (and perhaps don't
734 include ,v), but trying to worry about such things
735 is a little pointless unless you first worry about
736 whether "cvs admin -A" as a whole makes any sense
737 (currently probably not, as access lists don't
738 affect the behavior of CVS). */
740 rcs2 = RCS_parsercsfile (arg + 2);
742 error (1, 0, "cannot continue");
744 p = xstrdup (RCS_getaccess (rcs2));
745 line2argv (&argc, &users, p, " \t\n");
749 for (u = 0; u < argc; ++u)
750 RCS_addaccess (rcs, users[u]);
751 free_names (&argc, users);
753 case 'n': /* fall through */
757 cvs_outerr ("missing symbolic name after ", 0);
759 cvs_outerr ("\n", 1);
762 p = strchr (arg, ':');
765 if (RCS_deltag (rcs, arg + 2) != 0)
767 error (0, 0, "%s: Symbolic name %s is undefined.",
776 tag = xstrdup (arg + 2);
779 /* Option `n' signals an error if this tag is already bound. */
782 n = findnode (RCS_symbols (rcs), tag);
786 "%s: symbolic name %s already bound to %s",
788 tag, (char *)n->data);
795 /* Attempt to perform the requested tagging. */
797 if ((*p == 0 && (rev = RCS_head (rcs)))
798 || (rev = RCS_tag2rev (rcs, p))) /* tag2rev may exit */
800 RCS_check_tag (tag); /* exit if not a valid tag */
801 RCS_settag (rcs, tag, rev);
808 "%s: Symbolic name or revision %s is undefined.",
815 p = strchr (arg, ':');
818 tag = xstrdup (arg + 2);
819 rev = RCS_head (rcs);
822 error (0, 0, "No head revision in archive file `%s'.",
831 tag = xstrdup (arg + 2);
835 revnum = RCS_gettag (rcs, rev, 0, NULL);
838 n = findnode (rcs->versions, revnum);
846 "%s: can't set state of nonexisting revision %s",
860 p = strchr (arg, ':');
863 error (0, 0, "%s: -m option lacks revision number",
868 *p = '\0'; /* temporarily make arg+2 its own string */
869 rev = RCS_gettag (rcs, arg + 2, 1, NULL); /* Force tag match */
872 error (0, 0, "%s: no such revision %s", rcs->path, arg+2);
874 *p = ':'; /* restore the full text of the -m argument */
879 n = findnode (rcs->versions, rev);
880 /* tags may exist against non-existing versions */
883 error (0, 0, "%s: no such revision %s: %s",
884 rcs->path, arg+2, rev);
886 *p = ':'; /* restore the full text of the -m argument */
890 *p = ':'; /* restore the full text of the -m argument */
894 if (delta->text == NULL)
896 delta->text = (Deltatext *) xmalloc (sizeof (Deltatext));
897 memset ((void *) delta->text, 0, sizeof (Deltatext));
899 delta->text->version = xstrdup (delta->version);
900 delta->text->log = make_message_rcslegal (msg);
904 status |= RCS_lock (rcs, arg[2] ? arg + 2 : NULL, 0);
907 status |= RCS_unlock (rcs, arg[2] ? arg + 2 : NULL, 0);
909 default: assert(0); /* can't happen */
915 RCS_rewrite (rcs, NULL, NULL);
917 cvs_output ("done\n", 5);
921 /* Note that this message should only occur after another
922 message has given a more specific error. The point of this
923 additional message is to make it clear that the previous problems
924 caused CVS to forget about the idea of modifying the RCS file. */
926 error (0, 0, "RCS file for `%s' not modified.", finfo->file);
936 * Print a warm fuzzy message
940 admin_dirproc (callerdat, dir, repos, update_dir, entries)
944 const char *update_dir;
948 error (0, 0, "Administrating %s", update_dir);