2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
5 * You may distribute under the terms of the GNU General Public License as
6 * specified in the README file that comes with the CVS source distribution.
8 * Administration ("cvs admin")
13 #ifdef CVS_ADMIN_GROUP
18 static Dtype admin_dirproc PROTO ((void *callerdat, const char *dir,
19 const char *repos, const char *update_dir,
21 static int admin_fileproc PROTO ((void *callerdat, struct file_info *finfo));
23 static const char *const admin_usage[] =
25 "Usage: %s %s [options] files...\n",
26 "\t-a users Append (comma-separated) user names to access list.\n",
27 "\t-A file Append another file's access list.\n",
28 "\t-b[rev] Set default branch (highest branch on trunk if omitted).\n",
29 "\t-c string Set comment leader.\n",
30 "\t-e[users] Remove (comma-separated) user names from access list\n",
31 "\t (all names if omitted).\n",
32 "\t-I Run interactively.\n",
33 "\t-k subst Set keyword substitution mode:\n",
34 "\t kv (Default) Substitute keyword and value.\n",
35 "\t kvl Substitute keyword, value, and locker (if any).\n",
36 "\t k Substitute keyword only.\n",
37 "\t o Preserve original string.\n",
38 "\t b Like o, but mark file as binary.\n",
39 "\t v Substitute value only.\n",
40 "\t-l[rev] Lock revision (latest revision on branch,\n",
41 "\t latest revision on trunk if omitted).\n",
42 "\t-L Set strict locking.\n",
43 "\t-m rev:msg Replace revision's log message.\n",
44 "\t-n tag[:[rev]] Tag branch or revision. If :rev is omitted,\n",
45 "\t delete the tag; if rev is omitted, tag the latest\n",
46 "\t revision on the default branch.\n",
47 "\t-N tag[:[rev]] Same as -n except override existing tag.\n",
48 "\t-o range Delete (outdate) specified range of revisions:\n",
49 "\t rev1:rev2 Between rev1 and rev2, including rev1 and rev2.\n",
50 "\t rev1::rev2 Between rev1 and rev2, excluding rev1 and rev2.\n",
51 "\t rev: rev and following revisions on the same branch.\n",
52 "\t rev:: After rev on the same branch.\n",
53 "\t :rev rev and previous revisions on the same branch.\n",
54 "\t ::rev Before rev on the same branch.\n",
56 "\t-q Run quietly.\n",
57 "\t-s state[:rev] Set revision state (latest revision on branch,\n",
58 "\t latest revision on trunk if omitted).\n",
59 "\t-t[file] Get descriptive text from file (stdin if omitted).\n",
60 "\t-t-string Set descriptive text.\n",
61 "\t-u[rev] Unlock the revision (latest revision on branch,\n",
62 "\t latest revision on trunk if omitted).\n",
63 "\t-U Unset strict locking.\n",
64 "(Specify the --help global option for a list of other help options)\n",
68 /* This structure is used to pass information through start_recursion. */
71 /* Set default branch (-b). It is "-b" followed by the value
72 given, or NULL if not specified, or merely "-b" if -b is
73 specified without a value. */
76 /* Set comment leader (-c). It is "-c" followed by the value
77 given, or NULL if not specified. The comment leader is
78 relevant only for old versions of RCS, but we let people set it
82 /* Set strict locking (-L). */
85 /* Set nonstrict locking (-U). */
88 /* Delete revisions (-o). It is "-o" followed by the value specified. */
91 /* Keyword substitution mode (-k), e.g. "-kb". */
94 /* Description (-t). */
97 /* Interactive (-I). Problematic with client/server. */
100 /* This is the cheesy part. It is a vector with the options which
101 we don't deal with above (e.g. "-afoo" "-abar,baz"). In the future
102 this presumably will be replaced by other variables which break
103 out the data in a more convenient fashion. AV as well as each of
104 the strings it points to is malloc'd. */
110 /* Add an argument. OPT is the option letter, e.g. 'a'. ARG is the
111 argument to that option, or NULL if omitted (whether NULL can actually
112 happen depends on whether the option was specified as optional to
115 arg_add (dat, opt, arg)
116 struct admin_data *dat;
120 char *newelt = xmalloc ((arg == NULL ? 0 : strlen (arg)) + 3);
121 strcpy (newelt, "-");
126 strcpy (newelt + 2, arg);
128 if (dat->av_alloc == 0)
131 dat->av = (char **) xmalloc (dat->av_alloc * sizeof (*dat->av));
133 else if (dat->ac >= dat->av_alloc)
136 dat->av = (char **) xrealloc (dat->av,
137 dat->av_alloc * sizeof (*dat->av));
139 dat->av[dat->ac++] = newelt;
148 #ifdef CVS_ADMIN_GROUP
150 struct group *getgrnam();
152 struct admin_data admin_data;
162 memset (&admin_data, 0, sizeof admin_data);
164 /* TODO: get rid of `-' switch notation in admin_data. For
165 example, admin_data->branch should be not `-bfoo' but simply `foo'. */
169 while ((c = getopt (argc, argv,
170 "+ib::c:a:A:e::l::u::LUn:N:m:o:s:t::IqxV:k:")) != -1)
172 if (c != 'k' && c != 'q')
178 /* This has always been documented as useless in cvs.texinfo
179 and it really is--admin_fileproc silently does nothing
180 if vers->vn_user is NULL. */
181 error (0, 0, "the -i option to admin is not supported");
182 error (0, 0, "run add or import to create an RCS file");
186 if (admin_data.branch != NULL)
188 error (0, 0, "duplicate 'b' option");
192 admin_data.branch = xstrdup ("-b");
195 admin_data.branch = xmalloc (strlen (optarg) + 5);
196 strcpy (admin_data.branch, "-b");
197 strcat (admin_data.branch, optarg);
202 if (admin_data.comment != NULL)
204 error (0, 0, "duplicate 'c' option");
207 admin_data.comment = xmalloc (strlen (optarg) + 5);
208 strcpy (admin_data.comment, "-c");
209 strcat (admin_data.comment, optarg);
213 arg_add (&admin_data, 'a', optarg);
217 /* In the client/server case, this is cheesy because
218 we just pass along the name of the RCS file, which
219 then will want to exist on the server. This is
220 accidental; having the client specify a pathname on
221 the server is not a design feature of the protocol. */
222 arg_add (&admin_data, 'A', optarg);
226 arg_add (&admin_data, 'e', optarg);
230 /* Note that multiple -l options are legal. */
231 arg_add (&admin_data, 'l', optarg);
235 /* Note that multiple -u options are legal. */
236 arg_add (&admin_data, 'u', optarg);
240 /* Probably could also complain if -L is specified multiple
241 times, although RCS doesn't and I suppose it is reasonable
242 just to have it mean the same as a single -L. */
243 if (admin_data.set_nonstrict)
245 error (0, 0, "-U and -L are incompatible");
248 admin_data.set_strict = 1;
252 /* Probably could also complain if -U is specified multiple
253 times, although RCS doesn't and I suppose it is reasonable
254 just to have it mean the same as a single -U. */
255 if (admin_data.set_strict)
257 error (0, 0, "-U and -L are incompatible");
260 admin_data.set_nonstrict = 1;
264 /* Mostly similar to cvs tag. Could also be parsing
265 the syntax of optarg, although for now we just pass
266 it to rcs as-is. Note that multiple -n options are
268 arg_add (&admin_data, 'n', optarg);
272 /* Mostly similar to cvs tag. Could also be parsing
273 the syntax of optarg, although for now we just pass
274 it to rcs as-is. Note that multiple -N options are
276 arg_add (&admin_data, 'N', optarg);
280 /* Change log message. Could also be parsing the syntax
281 of optarg, although for now we just pass it to rcs
282 as-is. Note that multiple -m options are legal. */
283 arg_add (&admin_data, 'm', optarg);
287 /* Delete revisions. Probably should also be parsing the
288 syntax of optarg, so that the client can give errors
289 rather than making the server take care of that.
290 Other than that I'm not sure whether it matters much
291 whether we parse it here or in admin_fileproc.
293 Note that multiple -o options are illegal, in RCS
296 if (admin_data.delete_revs != NULL)
298 error (0, 0, "duplicate '-o' option");
301 admin_data.delete_revs = xmalloc (strlen (optarg) + 5);
302 strcpy (admin_data.delete_revs, "-o");
303 strcat (admin_data.delete_revs, optarg);
307 /* Note that multiple -s options are legal. */
308 arg_add (&admin_data, 's', optarg);
312 if (admin_data.desc != NULL)
314 error (0, 0, "duplicate 't' option");
317 if (optarg != NULL && optarg[0] == '-')
318 admin_data.desc = xstrdup (optarg + 1);
324 get_file (optarg, optarg, "r", &admin_data.desc,
330 /* At least in RCS this can be specified several times,
331 with the same meaning as being specified once. */
332 admin_data.interactive = 1;
336 /* Silently set the global really_quiet flag. This keeps admin in
337 * sync with the RCS man page and allows us to silently support
338 * older servers when necessary.
340 * Some logic says we might want to output a deprecation warning
341 * here, but I'm opting not to in order to stay quietly in sync
342 * with the RCS man page.
348 error (0, 0, "the -x option has never done anything useful");
349 error (0, 0, "RCS files in CVS always end in ,v");
353 /* No longer supported. */
354 error (0, 0, "the `-V' option is obsolete");
358 if (admin_data.kflag != NULL)
360 error (0, 0, "duplicate '-k' option");
363 admin_data.kflag = RCS_check_kflag (optarg);
367 /* getopt will have printed an error message. */
370 /* Don't use cvs_cmd_name; it might be "server". */
371 error (1, 0, "specify %s -H admin for usage information",
378 #ifdef CVS_ADMIN_GROUP
379 /* The use of `cvs admin -k' is unrestricted. However, any other
380 option is restricted if the group CVS_ADMIN_GROUP exists on the
383 # ifdef CLIENT_SUPPORT
384 /* This is only "secure" on the server, since the user could edit the
385 * RCS file on a local host, but some people like this kind of
386 * check anyhow. The alternative would be to check only when
387 * (server_active) rather than when not on the client.
389 !current_parsed_root->isremote &&
390 # endif /* CLIENT_SUPPORT */
392 && (grp = getgrnam(CVS_ADMIN_GROUP)) != NULL)
394 #ifdef HAVE_GETGROUPS
398 /* get number of auxiliary groups */
399 n = getgroups (0, NULL);
401 error (1, errno, "unable to get number of auxiliary groups");
402 grps = (gid_t *) xmalloc((n + 1) * sizeof *grps);
403 n = getgroups (n, grps);
405 error (1, errno, "unable to get list of auxiliary groups");
407 for (i = 0; i <= n; i++)
408 if (grps[i] == grp->gr_gid) break;
411 error (1, 0, "usage is restricted to members of the group %s",
414 char *me = getcaller();
417 for (grnam = grp->gr_mem; *grnam; grnam++)
418 if (strcmp (*grnam, me) == 0) break;
419 if (!*grnam && getgid() != grp->gr_gid)
420 error (1, 0, "usage is restricted to members of the group %s",
424 #endif /* defined CVS_ADMIN_GROUP */
426 for (i = 0; i < admin_data.ac; ++i)
428 assert (admin_data.av[i][0] == '-');
429 switch (admin_data.av[i][1])
434 check_numeric (&admin_data.av[i][2], argc, argv);
440 if (admin_data.branch != NULL)
441 check_numeric (admin_data.branch + 2, argc, argv);
442 if (admin_data.delete_revs != NULL)
446 check_numeric (admin_data.delete_revs + 2, argc, argv);
447 p = strchr (admin_data.delete_revs + 2, ':');
448 if (p != NULL && isdigit ((unsigned char) p[1]))
449 check_numeric (p + 1, argc, argv);
450 else if (p != NULL && p[1] == ':' && isdigit ((unsigned char) p[2]))
451 check_numeric (p + 2, argc, argv);
454 #ifdef CLIENT_SUPPORT
455 if (current_parsed_root->isremote)
457 /* We're the client side. Fire up the remote server. */
462 /* Note that option_with_arg does not work for us, because some
463 of the options must be sent without a space between the option
465 if (admin_data.interactive)
466 error (1, 0, "-I option not useful with client/server");
467 if (admin_data.branch != NULL)
468 send_arg (admin_data.branch);
469 if (admin_data.comment != NULL)
470 send_arg (admin_data.comment);
471 if (admin_data.set_strict)
473 if (admin_data.set_nonstrict)
475 if (admin_data.delete_revs != NULL)
476 send_arg (admin_data.delete_revs);
477 if (admin_data.desc != NULL)
479 char *p = admin_data.desc;
480 send_to_server ("Argument -t-", 0);
485 send_to_server ("\012Argumentx ", 0);
490 char *q = strchr (p, '\n');
491 if (q == NULL) q = p + strlen (p);
492 send_to_server (p, q - p);
496 send_to_server ("\012", 1);
498 /* Send this for all really_quiets since we know that it will be silently
499 * ignored when unneeded. This supports old servers.
503 if (admin_data.kflag != NULL)
504 send_arg (admin_data.kflag);
506 for (i = 0; i < admin_data.ac; ++i)
507 send_arg (admin_data.av[i]);
510 send_files (argc, argv, 0, 0, SEND_NO_CONTENTS);
511 send_file_names (argc, argv, SEND_EXPAND_WILD);
512 send_to_server ("admin\012", 0);
513 err = get_responses_and_close ();
516 #endif /* CLIENT_SUPPORT */
518 lock_tree_for_write (argc, argv, 0, W_LOCAL, 0);
520 err = start_recursion (admin_fileproc, (FILESDONEPROC) NULL, admin_dirproc,
521 (DIRLEAVEPROC) NULL, (void *)&admin_data,
523 W_LOCAL, 0, CVS_LOCK_NONE, (char *) NULL, 1,
528 if (admin_data.branch != NULL)
529 free (admin_data.branch);
530 if (admin_data.comment != NULL)
531 free (admin_data.comment);
532 if (admin_data.delete_revs != NULL)
533 free (admin_data.delete_revs);
534 if (admin_data.kflag != NULL)
535 free (admin_data.kflag);
536 if (admin_data.desc != NULL)
537 free (admin_data.desc);
538 for (i = 0; i < admin_data.ac; ++i)
539 free (admin_data.av[i]);
540 if (admin_data.av != NULL)
541 free (admin_data.av);
547 * Called to run "rcs" on a particular file.
551 admin_fileproc (callerdat, finfo)
553 struct file_info *finfo;
555 struct admin_data *admin_data = (struct admin_data *) callerdat;
562 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
564 version = vers->vn_user;
565 if (version != NULL && strcmp (version, "0") == 0)
567 error (0, 0, "cannot admin newly added file `%s'", finfo->file);
576 error (0, 0, "nothing known about %s", finfo->file);
581 if (rcs->flags & PARTIAL)
582 RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
586 cvs_output ("RCS file: ", 0);
587 cvs_output (rcs->path, 0);
588 cvs_output ("\n", 1);
591 if (admin_data->branch != NULL)
593 char *branch = &admin_data->branch[2];
594 if (*branch != '\0' && ! isdigit ((unsigned char) *branch))
596 branch = RCS_whatbranch (rcs, admin_data->branch + 2);
599 error (0, 0, "%s: Symbolic name %s is undefined.",
600 rcs->path, admin_data->branch + 2);
605 RCS_setbranch (rcs, branch);
606 if (branch != NULL && branch != &admin_data->branch[2])
609 if (admin_data->comment != NULL)
611 if (rcs->comment != NULL)
613 rcs->comment = xstrdup (admin_data->comment + 2);
615 if (admin_data->set_strict)
616 rcs->strict_locks = 1;
617 if (admin_data->set_nonstrict)
618 rcs->strict_locks = 0;
619 if (admin_data->delete_revs != NULL)
621 char *s, *t, *rev1, *rev2;
622 /* Set for :, clear for ::. */
626 s = admin_data->delete_revs + 2;
640 /* Note that we don't support '-' for ranges. RCS considers it
641 obsolete and it is problematic with tags containing '-'. "cvs log"
642 has made the same decision. */
660 *t = ':'; /* probably unnecessary */
669 if (rev1 == NULL && rev2 == NULL)
671 /* RCS segfaults if `-o:' is given */
672 error (0, 0, "no valid revisions specified in `%s' option",
673 admin_data->delete_revs);
678 status |= RCS_delete_revs (rcs, rev1, rev2, inclusive);
685 if (admin_data->desc != NULL)
688 rcs->desc = xstrdup (admin_data->desc);
690 if (admin_data->kflag != NULL)
692 char *kflag = admin_data->kflag + 2;
693 char *oldexpand = RCS_getexpand (rcs);
694 if (oldexpand == NULL || strcmp (oldexpand, kflag) != 0)
695 RCS_setexpand (rcs, kflag);
698 /* Handle miscellaneous options. TODO: decide whether any or all
699 of these should have their own fields in the admin_data
701 for (i = 0; i < admin_data->ac; ++i)
704 char *p, *rev, *revnum, *tag, *msg;
710 arg = admin_data->av[i];
713 case 'a': /* fall through */
715 line2argv (&argc, &users, arg + 2, " ,\t\n");
717 for (u = 0; u < argc; ++u)
718 RCS_addaccess (rcs, users[u]);
720 RCS_delaccess (rcs, NULL);
722 for (u = 0; u < argc; ++u)
723 RCS_delaccess (rcs, users[u]);
724 free_names (&argc, users);
728 /* See admin-19a-admin and friends in sanity.sh for
729 relative pathnames. It makes sense to think in
730 terms of a syntax which give pathnames relative to
731 the repository or repository corresponding to the
732 current directory or some such (and perhaps don't
733 include ,v), but trying to worry about such things
734 is a little pointless unless you first worry about
735 whether "cvs admin -A" as a whole makes any sense
736 (currently probably not, as access lists don't
737 affect the behavior of CVS). */
739 rcs2 = RCS_parsercsfile (arg + 2);
741 error (1, 0, "cannot continue");
743 p = xstrdup (RCS_getaccess (rcs2));
744 line2argv (&argc, &users, p, " \t\n");
748 for (u = 0; u < argc; ++u)
749 RCS_addaccess (rcs, users[u]);
750 free_names (&argc, users);
752 case 'n': /* fall through */
756 cvs_outerr ("missing symbolic name after ", 0);
758 cvs_outerr ("\n", 1);
761 p = strchr (arg, ':');
764 if (RCS_deltag (rcs, arg + 2) != 0)
766 error (0, 0, "%s: Symbolic name %s is undefined.",
775 tag = xstrdup (arg + 2);
778 /* Option `n' signals an error if this tag is already bound. */
781 n = findnode (RCS_symbols (rcs), tag);
785 "%s: symbolic name %s already bound to %s",
787 tag, (char *)n->data);
794 /* Attempt to perform the requested tagging. */
796 if ((*p == 0 && (rev = RCS_head (rcs)))
797 || (rev = RCS_tag2rev (rcs, p))) /* tag2rev may exit */
799 RCS_check_tag (tag); /* exit if not a valid tag */
800 RCS_settag (rcs, tag, rev);
807 "%s: Symbolic name or revision %s is undefined.",
814 p = strchr (arg, ':');
817 tag = xstrdup (arg + 2);
818 rev = RCS_head (rcs);
823 tag = xstrdup (arg + 2);
827 revnum = RCS_gettag (rcs, rev, 0, NULL);
830 n = findnode (rcs->versions, revnum);
838 "%s: can't set state of nonexisting revision %s",
852 p = strchr (arg, ':');
855 error (0, 0, "%s: -m option lacks revision number",
860 *p = '\0'; /* temporarily make arg+2 its own string */
861 rev = RCS_gettag (rcs, arg + 2, 1, NULL); /* Force tag match */
864 error (0, 0, "%s: no such revision %s", rcs->path, arg+2);
866 *p = ':'; /* restore the full text of the -m argument */
871 n = findnode (rcs->versions, rev);
872 /* tags may exist against non-existing versions */
875 error (0, 0, "%s: no such revision %s: %s",
876 rcs->path, arg+2, rev);
878 *p = ':'; /* restore the full text of the -m argument */
882 *p = ':'; /* restore the full text of the -m argument */
886 if (delta->text == NULL)
888 delta->text = (Deltatext *) xmalloc (sizeof (Deltatext));
889 memset ((void *) delta->text, 0, sizeof (Deltatext));
891 delta->text->version = xstrdup (delta->version);
892 delta->text->log = make_message_rcslegal (msg);
896 status |= RCS_lock (rcs, arg[2] ? arg + 2 : NULL, 0);
899 status |= RCS_unlock (rcs, arg[2] ? arg + 2 : NULL, 0);
901 default: assert(0); /* can't happen */
907 RCS_rewrite (rcs, NULL, NULL);
909 cvs_output ("done\n", 5);
913 /* Note that this message should only occur after another
914 message has given a more specific error. The point of this
915 additional message is to make it clear that the previous problems
916 caused CVS to forget about the idea of modifying the RCS file. */
918 error (0, 0, "RCS file for `%s' not modified.", finfo->file);
928 * Print a warm fuzzy message
932 admin_dirproc (callerdat, dir, repos, update_dir, entries)
936 const char *update_dir;
940 error (0, 0, "Administrating %s", update_dir);