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, char *dir,
19 char *repos, 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 rcs-options files...\n",
26 "(Specify the --help global option for a list of other help options)\n",
30 /* This structure is used to pass information through start_recursion. */
33 /* Set default branch (-b). It is "-b" followed by the value
34 given, or NULL if not specified, or merely "-b" if -b is
35 specified without a value. */
38 /* Set comment leader (-c). It is "-c" followed by the value
39 given, or NULL if not specified. The comment leader is
40 relevant only for old versions of RCS, but we let people set it
44 /* Set strict locking (-L). */
47 /* Set nonstrict locking (-U). */
50 /* Delete revisions (-o). It is "-o" followed by the value specified. */
53 /* Keyword substitution mode (-k), e.g. "-kb". */
56 /* Description (-t). See sanity.sh for various moanings about
57 files and stdin and such. "" if -t specified without an
58 argument. It is "-t" followed by the argument. */
61 /* Interactive (-I). Problematic with client/server. */
64 /* Quiet (-q). Not the same as the global -q option, which is a bit
65 on the confusing side, perhaps. */
68 /* This is the cheesy part. It is a vector with the options which
69 we don't deal with above (e.g. "-afoo" "-abar,baz"). In the future
70 this presumably will be replaced by other variables which break
71 out the data in a more convenient fashion. AV as well as each of
72 the strings it points to is malloc'd. */
78 /* Add an argument. OPT is the option letter, e.g. 'a'. ARG is the
79 argument to that option, or NULL if omitted (whether NULL can actually
80 happen depends on whether the option was specified as optional to
83 arg_add (dat, opt, arg)
84 struct admin_data *dat;
88 char *newelt = xmalloc ((arg == NULL ? 0 : strlen (arg)) + 3);
94 strcpy (newelt + 2, arg);
96 if (dat->av_alloc == 0)
99 dat->av = (char **) xmalloc (dat->av_alloc * sizeof (*dat->av));
101 else if (dat->ac >= dat->av_alloc)
104 dat->av = (char **) xrealloc (dat->av,
105 dat->av_alloc * sizeof (*dat->av));
107 dat->av[dat->ac++] = newelt;
116 #ifdef CVS_ADMIN_GROUP
118 struct group *getgrnam();
120 struct admin_data admin_data;
130 memset (&admin_data, 0, sizeof admin_data);
132 /* TODO: get rid of `-' switch notation in admin_data. For
133 example, admin_data->branch should be not `-bfoo' but simply `foo'. */
137 while ((c = getopt (argc, argv,
138 "+ib::c:a:A:e::l::u::LUn:N:m:o:s:t::IqxV:k:")) != -1)
146 /* This has always been documented as useless in cvs.texinfo
147 and it really is--admin_fileproc silently does nothing
148 if vers->vn_user is NULL. */
149 error (0, 0, "the -i option to admin is not supported");
150 error (0, 0, "run add or import to create an RCS file");
154 if (admin_data.branch != NULL)
156 error (0, 0, "duplicate 'b' option");
160 admin_data.branch = xstrdup ("-b");
163 admin_data.branch = xmalloc (strlen (optarg) + 5);
164 strcpy (admin_data.branch, "-b");
165 strcat (admin_data.branch, optarg);
170 if (admin_data.comment != NULL)
172 error (0, 0, "duplicate 'c' option");
175 admin_data.comment = xmalloc (strlen (optarg) + 5);
176 strcpy (admin_data.comment, "-c");
177 strcat (admin_data.comment, optarg);
181 arg_add (&admin_data, 'a', optarg);
185 /* In the client/server case, this is cheesy because
186 we just pass along the name of the RCS file, which
187 then will want to exist on the server. This is
188 accidental; having the client specify a pathname on
189 the server is not a design feature of the protocol. */
190 arg_add (&admin_data, 'A', optarg);
197 "removing entire access list not yet implemented");
199 arg_add (&admin_data, 'e', optarg);
203 /* Note that multiple -l options are legal. */
204 arg_add (&admin_data, 'l', optarg);
208 /* Note that multiple -u options are legal. */
209 arg_add (&admin_data, 'u', optarg);
213 /* Probably could also complain if -L is specified multiple
214 times, although RCS doesn't and I suppose it is reasonable
215 just to have it mean the same as a single -L. */
216 if (admin_data.set_nonstrict)
218 error (0, 0, "-U and -L are incompatible");
221 admin_data.set_strict = 1;
225 /* Probably could also complain if -U is specified multiple
226 times, although RCS doesn't and I suppose it is reasonable
227 just to have it mean the same as a single -U. */
228 if (admin_data.set_strict)
230 error (0, 0, "-U and -L are incompatible");
233 admin_data.set_nonstrict = 1;
237 /* Mostly similar to cvs tag. Could also be parsing
238 the syntax of optarg, although for now we just pass
239 it to rcs as-is. Note that multiple -n options are
241 arg_add (&admin_data, 'n', optarg);
245 /* Mostly similar to cvs tag. Could also be parsing
246 the syntax of optarg, although for now we just pass
247 it to rcs as-is. Note that multiple -N options are
249 arg_add (&admin_data, 'N', optarg);
253 /* Change log message. Could also be parsing the syntax
254 of optarg, although for now we just pass it to rcs
255 as-is. Note that multiple -m options are legal. */
256 arg_add (&admin_data, 'm', optarg);
260 /* Delete revisions. Probably should also be parsing the
261 syntax of optarg, so that the client can give errors
262 rather than making the server take care of that.
263 Other than that I'm not sure whether it matters much
264 whether we parse it here or in admin_fileproc.
266 Note that multiple -o options are illegal, in RCS
269 if (admin_data.delete_revs != NULL)
271 error (0, 0, "duplicate '-o' option");
274 admin_data.delete_revs = xmalloc (strlen (optarg) + 5);
275 strcpy (admin_data.delete_revs, "-o");
276 strcat (admin_data.delete_revs, optarg);
280 /* Note that multiple -s options are legal. */
281 arg_add (&admin_data, 's', optarg);
285 if (admin_data.desc != NULL)
287 error (0, 0, "duplicate 't' option");
291 admin_data.desc = xstrdup ("-t");
294 admin_data.desc = xmalloc (strlen (optarg) + 5);
295 strcpy (admin_data.desc, "-t");
296 strcat (admin_data.desc, optarg);
301 /* At least in RCS this can be specified several times,
302 with the same meaning as being specified once. */
303 admin_data.interactive = 1;
307 admin_data.quiet = 1;
311 error (0, 0, "the -x option has never done anything useful");
312 error (0, 0, "RCS files in CVS always end in ,v");
316 /* No longer supported. */
317 error (0, 0, "the `-V' option is obsolete");
321 if (admin_data.kflag != NULL)
323 error (0, 0, "duplicate '-k' option");
326 admin_data.kflag = RCS_check_kflag (optarg);
330 /* getopt will have printed an error message. */
333 /* Don't use command_name; it might be "server". */
334 error (1, 0, "specify %s -H admin for usage information",
341 #ifdef CVS_ADMIN_GROUP
342 grp = getgrnam(CVS_ADMIN_GROUP);
343 /* skip usage right check if group CVS_ADMIN_GROUP does not exist */
346 char *me = getcaller();
347 char **grnam = grp->gr_mem;
348 /* The use of `cvs admin -k' is unrestricted. However, any
349 other option is restricted. */
350 int denied = ! only_k_option;
354 if (strcmp(*grnam, me) == 0)
363 error (1, 0, "usage is restricted to members of the group %s",
368 for (i = 0; i < admin_data.ac; ++i)
370 assert (admin_data.av[i][0] == '-');
371 switch (admin_data.av[i][1])
376 check_numeric (&admin_data.av[i][2], argc, argv);
382 if (admin_data.branch != NULL)
383 check_numeric (admin_data.branch + 2, argc, argv);
384 if (admin_data.delete_revs != NULL)
388 check_numeric (admin_data.delete_revs + 2, argc, argv);
389 p = strchr (admin_data.delete_revs + 2, ':');
390 if (p != NULL && isdigit ((unsigned char) p[1]))
391 check_numeric (p + 1, argc, argv);
392 else if (p != NULL && p[1] == ':' && isdigit ((unsigned char) p[2]))
393 check_numeric (p + 2, argc, argv);
396 #ifdef CLIENT_SUPPORT
399 /* We're the client side. Fire up the remote server. */
404 /* Note that option_with_arg does not work for us, because some
405 of the options must be sent without a space between the option
407 if (admin_data.interactive)
408 error (1, 0, "-I option not useful with client/server");
409 if (admin_data.branch != NULL)
410 send_arg (admin_data.branch);
411 if (admin_data.comment != NULL)
412 send_arg (admin_data.comment);
413 if (admin_data.set_strict)
415 if (admin_data.set_nonstrict)
417 if (admin_data.delete_revs != NULL)
418 send_arg (admin_data.delete_revs);
419 if (admin_data.desc != NULL)
420 send_arg (admin_data.desc);
421 if (admin_data.quiet)
423 if (admin_data.kflag != NULL)
424 send_arg (admin_data.kflag);
426 for (i = 0; i < admin_data.ac; ++i)
427 send_arg (admin_data.av[i]);
429 send_files (argc, argv, 0, 0, SEND_NO_CONTENTS);
430 send_file_names (argc, argv, SEND_EXPAND_WILD);
431 send_to_server ("admin\012", 0);
432 err = get_responses_and_close ();
435 #endif /* CLIENT_SUPPORT */
437 lock_tree_for_write (argc, argv, 0, 0);
439 err = start_recursion (admin_fileproc, (FILESDONEPROC) NULL, admin_dirproc,
440 (DIRLEAVEPROC) NULL, (void *)&admin_data,
442 W_LOCAL, 0, 0, (char *) NULL, 1);
446 if (admin_data.branch != NULL)
447 free (admin_data.branch);
448 if (admin_data.comment != NULL)
449 free (admin_data.comment);
450 if (admin_data.delete_revs != NULL)
451 free (admin_data.delete_revs);
452 if (admin_data.kflag != NULL)
453 free (admin_data.kflag);
454 if (admin_data.desc != NULL)
455 free (admin_data.desc);
456 for (i = 0; i < admin_data.ac; ++i)
457 free (admin_data.av[i]);
458 if (admin_data.av != NULL)
459 free (admin_data.av);
465 * Called to run "rcs" on a particular file.
469 admin_fileproc (callerdat, finfo)
471 struct file_info *finfo;
473 struct admin_data *admin_data = (struct admin_data *) callerdat;
480 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
482 version = vers->vn_user;
485 else if (strcmp (version, "0") == 0)
487 error (0, 0, "cannot admin newly added file `%s'", finfo->file);
492 if (rcs->flags & PARTIAL)
493 RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
497 if (!admin_data->quiet)
499 cvs_output ("RCS file: ", 0);
500 cvs_output (rcs->path, 0);
501 cvs_output ("\n", 1);
504 if (admin_data->branch != NULL)
506 char *branch = &admin_data->branch[2];
507 if (*branch != '\0' && ! isdigit ((unsigned char) *branch))
509 branch = RCS_whatbranch (rcs, admin_data->branch + 2);
512 error (0, 0, "%s: Symbolic name %s is undefined.",
513 rcs->path, admin_data->branch + 2);
518 RCS_setbranch (rcs, branch);
519 if (branch != NULL && branch != &admin_data->branch[2])
522 if (admin_data->comment != NULL)
524 if (rcs->comment != NULL)
526 rcs->comment = xstrdup (admin_data->comment + 2);
528 if (admin_data->set_strict)
529 rcs->strict_locks = 1;
530 if (admin_data->set_nonstrict)
531 rcs->strict_locks = 0;
532 if (admin_data->delete_revs != NULL)
534 char *s, *t, *rev1, *rev2;
535 /* Set for :, clear for ::. */
539 s = admin_data->delete_revs + 2;
553 /* Note that we don't support '-' for ranges. RCS considers it
554 obsolete and it is problematic with tags containing '-'. "cvs log"
555 has made the same decision. */
573 *t = ':'; /* probably unnecessary */
582 if (rev1 == NULL && rev2 == NULL)
584 /* RCS segfaults if `-o:' is given */
585 error (0, 0, "no valid revisions specified in `%s' option",
586 admin_data->delete_revs);
591 status |= RCS_delete_revs (rcs, rev1, rev2, inclusive);
598 if (admin_data->desc != NULL)
602 if (admin_data->desc[2] == '-')
603 rcs->desc = xstrdup (admin_data->desc + 3);
606 char *descfile = admin_data->desc + 2;
610 /* If -t specified with no argument, read from stdin. */
611 if (*descfile == '\0')
613 get_file (descfile, descfile, "r", &rcs->desc, &bufsize, &len);
616 if (admin_data->kflag != NULL)
618 char *kflag = admin_data->kflag + 2;
619 char *oldexpand = RCS_getexpand (rcs);
620 if (oldexpand == NULL || strcmp (oldexpand, kflag) != 0)
621 RCS_setexpand (rcs, kflag);
624 /* Handle miscellaneous options. TODO: decide whether any or all
625 of these should have their own fields in the admin_data
627 for (i = 0; i < admin_data->ac; ++i)
630 char *p, *rev, *revnum, *tag, *msg;
636 arg = admin_data->av[i];
639 case 'a': /* fall through */
641 line2argv (&argc, &users, arg + 2, " ,\t\n");
643 for (u = 0; u < argc; ++u)
644 RCS_addaccess (rcs, users[u]);
646 for (u = 0; u < argc; ++u)
647 RCS_delaccess (rcs, users[u]);
648 free_names (&argc, users);
652 /* See admin-19a-admin and friends in sanity.sh for
653 relative pathnames. It makes sense to think in
654 terms of a syntax which give pathnames relative to
655 the repository or repository corresponding to the
656 current directory or some such (and perhaps don't
657 include ,v), but trying to worry about such things
658 is a little pointless unless you first worry about
659 whether "cvs admin -A" as a whole makes any sense
660 (currently probably not, as access lists don't
661 affect the behavior of CVS). */
663 rcs2 = RCS_parsercsfile (arg + 2);
665 error (1, 0, "cannot continue");
667 p = xstrdup (RCS_getaccess (rcs2));
668 line2argv (&argc, &users, p, " \t\n");
672 for (u = 0; u < argc; ++u)
673 RCS_addaccess (rcs, users[u]);
674 free_names (&argc, users);
676 case 'n': /* fall through */
680 cvs_outerr ("missing symbolic name after ", 0);
682 cvs_outerr ("\n", 1);
685 p = strchr (arg, ':');
688 if (RCS_deltag (rcs, arg + 2) != 0)
690 error (0, 0, "%s: Symbolic name %s is undefined.",
699 tag = xstrdup (arg + 2);
702 /* Option `n' signals an error if this tag is already bound. */
705 n = findnode (RCS_symbols (rcs), tag);
709 "%s: symbolic name %s already bound to %s",
718 /* Attempt to perform the requested tagging. */
720 if ((*p == 0 && (rev = RCS_head (rcs)))
721 || (rev = RCS_tag2rev (rcs, p))) /* tag2rev may exit */
723 RCS_check_tag (tag); /* exit if not a valid tag */
724 RCS_settag (rcs, tag, rev);
730 "%s: Symbolic name or revision %s is undefined",
737 p = strchr (arg, ':');
740 tag = xstrdup (arg + 2);
741 rev = RCS_head (rcs);
746 tag = xstrdup (arg + 2);
750 revnum = RCS_gettag (rcs, rev, 0, NULL);
753 n = findnode (rcs->versions, revnum);
754 if (revnum == NULL || n == NULL)
757 "%s: can't set state of nonexisting revision %s",
765 delta = (RCSVers *) n->data;
771 p = strchr (arg, ':');
774 error (0, 0, "%s: -m option lacks revision number",
780 rev = RCS_gettag (rcs, arg + 2, 0, NULL);
783 error (0, 0, "%s: no such revision %s", rcs->path, rev);
790 n = findnode (rcs->versions, rev);
791 delta = (RCSVers *) n->data;
792 if (delta->text == NULL)
794 delta->text = (Deltatext *) xmalloc (sizeof (Deltatext));
795 memset ((void *) delta->text, 0, sizeof (Deltatext));
797 delta->text->version = xstrdup (delta->version);
798 delta->text->log = make_message_rcslegal (msg);
802 status |= RCS_lock (rcs, arg[2] ? arg + 2 : NULL, 0);
805 status |= RCS_unlock (rcs, arg[2] ? arg + 2 : NULL, 0);
807 default: assert(0); /* can't happen */
811 /* TODO: reconcile the weird discrepancies between
812 admin_data->quiet and quiet. */
815 RCS_rewrite (rcs, NULL, NULL);
816 if (!admin_data->quiet)
817 cvs_output ("done\n", 5);
821 /* Note that this message should only occur after another
822 message has given a more specific error. The point of this
823 additional message is to make it clear that the previous problems
824 caused CVS to forget about the idea of modifying the RCS file. */
825 error (0, 0, "cannot modify RCS file for `%s'", finfo->file);
827 /* Upon failure, we want to abandon any changes made to the
828 RCS data structure. Forcing a reparse does the trick,
829 but leaks memory and is kludgey. Should we export
830 free_rcsnode_contents for this purpose? */
831 RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
840 * Print a warm fuzzy message
844 admin_dirproc (callerdat, dir, repos, update_dir, entries)
852 error (0, 0, "Administrating %s", update_dir);