Sync usage with man page.
[netbsd-mini2440.git] / external / gpl2 / xcvs / dist / src / admin.c
blobf3a2be459e931053c85e34dfbe242fd0cd31216b
1 /*
2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5 * and others.
7 * Portions Copyright (c) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (c) 1989-1992, Brian Berliner
9 *
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")
17 #include "cvs.h"
18 #include <grp.h>
20 static Dtype admin_dirproc (void *callerdat, const char *dir,
21 const char *repos, const char *update_dir,
22 List *entries);
23 static int admin_fileproc (void *callerdat, struct file_info *finfo);
25 static const char *const admin_usage[] =
27 "Usage: %s %s [options] files...\n",
28 "\t-a users Append (comma-separated) user names to access list.\n",
29 "\t-A file Append another file's access list.\n",
30 "\t-b[rev] Set default branch (highest branch on trunk if omitted).\n",
31 "\t-c string Set comment leader.\n",
32 "\t-e[users] Remove (comma-separated) user names from access list\n",
33 "\t (all names if omitted).\n",
34 "\t-I Run interactively.\n",
35 "\t-k subst Set keyword substitution mode:\n",
36 "\t kv (Default) Substitute keyword and value.\n",
37 "\t kvl Substitute keyword, value, and locker (if any).\n",
38 "\t k Substitute keyword only.\n",
39 "\t o Preserve original string.\n",
40 "\t b Like o, but mark file as binary.\n",
41 "\t v Substitute value only.\n",
42 "\t-l[rev] Lock revision (latest revision on branch,\n",
43 "\t latest revision on trunk if omitted).\n",
44 "\t-L Set strict locking.\n",
45 "\t-m rev:msg Replace revision's log message.\n",
46 "\t-n tag[:[rev]] Tag branch or revision. If :rev is omitted,\n",
47 "\t delete the tag; if rev is omitted, tag the latest\n",
48 "\t revision on the default branch.\n",
49 "\t-N tag[:[rev]] Same as -n except override existing tag.\n",
50 "\t-o range Delete (outdate) specified range of revisions:\n",
51 "\t rev1:rev2 Between rev1 and rev2, including rev1 and rev2.\n",
52 "\t rev1::rev2 Between rev1 and rev2, excluding rev1 and rev2.\n",
53 "\t rev: rev and following revisions on the same branch.\n",
54 "\t rev:: After rev on the same branch.\n",
55 "\t :rev rev and previous revisions on the same branch.\n",
56 "\t ::rev Before rev on the same branch.\n",
57 "\t rev Just rev.\n",
58 "\t-q Run quietly.\n",
59 "\t-s state[:rev] Set revision state (latest revision on branch,\n",
60 "\t latest revision on trunk if omitted).\n",
61 "\t-t[file] Get descriptive text from file (stdin if omitted).\n",
62 "\t-t-string Set descriptive text.\n",
63 "\t-u[rev] Unlock the revision (latest revision on branch,\n",
64 "\t latest revision on trunk if omitted).\n",
65 "\t-U Unset strict locking.\n",
66 "(Specify the --help global option for a list of other help options)\n",
67 NULL
70 /* This structure is used to pass information through start_recursion. */
71 struct admin_data
73 /* Set default branch (-b). It is "-b" followed by the value
74 given, or NULL if not specified, or merely "-b" if -b is
75 specified without a value. */
76 char *branch;
78 /* Set comment leader (-c). It is "-c" followed by the value
79 given, or NULL if not specified. The comment leader is
80 relevant only for old versions of RCS, but we let people set it
81 anyway. */
82 char *comment;
84 /* Set strict locking (-L). */
85 int set_strict;
87 /* Set nonstrict locking (-U). */
88 int set_nonstrict;
90 /* Delete revisions (-o). It is "-o" followed by the value specified. */
91 char *delete_revs;
93 /* Keyword substitution mode (-k), e.g. "-kb". */
94 char *kflag;
96 /* Description (-t). */
97 char *desc;
99 /* Interactive (-I). Problematic with client/server. */
100 int interactive;
102 /* This is the cheesy part. It is a vector with the options which
103 we don't deal with above (e.g. "-afoo" "-abar,baz"). In the future
104 this presumably will be replaced by other variables which break
105 out the data in a more convenient fashion. AV as well as each of
106 the strings it points to is malloc'd. */
107 int ac;
108 char **av;
109 int av_alloc;
111 /* This contains a printable version of the command line used
112 * for logging
114 char *cmdline;
117 /* Add an argument. OPT is the option letter, e.g. 'a'. ARG is the
118 argument to that option, or NULL if omitted (whether NULL can actually
119 happen depends on whether the option was specified as optional to
120 getopt). */
121 static void
122 arg_add (struct admin_data *dat, int opt, char *arg)
124 char *newelt = Xasprintf ("-%c%s", opt, arg ? arg : "");
126 if (dat->av_alloc == 0)
128 dat->av_alloc = 1;
129 dat->av = xnmalloc (dat->av_alloc, sizeof (*dat->av));
131 else if (dat->ac >= dat->av_alloc)
133 dat->av_alloc *= 2;
134 dat->av = xnrealloc (dat->av, dat->av_alloc, sizeof (*dat->av));
136 dat->av[dat->ac++] = newelt;
142 * callback proc to run a script when admin finishes.
144 static int
145 postadmin_proc (const char *repository, const char *filter, void *closure)
147 char *cmdline;
148 const char *srepos = Short_Repository (repository);
150 TRACE (TRACE_FUNCTION, "postadmin_proc (%s, %s)", repository, filter);
152 /* %c = cvs_cmd_name
153 * %R = referrer
154 * %p = shortrepos
155 * %r = repository
158 * Cast any NULL arguments as appropriate pointers as this is an
159 * stdarg function and we need to be certain the caller gets what
160 * is expected.
162 cmdline = format_cmdline (
163 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
164 false, srepos,
165 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
166 filter,
167 "c", "s", cvs_cmd_name,
168 #ifdef SERVER_SUPPORT
169 "R", "s", referrer ? referrer->original : "NONE",
170 #endif /* SERVER_SUPPORT */
171 "p", "s", srepos,
172 "r", "s", current_parsed_root->directory,
173 (char *) NULL);
175 if (!cmdline || !strlen (cmdline))
177 if (cmdline) free (cmdline);
178 error (0, 0, "postadmin proc resolved to the empty string!");
179 return 1;
182 run_setup (cmdline);
184 free (cmdline);
186 /* FIXME - read the comment in verifymsg_proc() about why we use abs()
187 * below() and shouldn't.
189 return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
190 RUN_NORMAL | RUN_SIGIGNORE));
196 * Call any postadmin procs.
198 static int
199 admin_filesdoneproc (void *callerdat, int err, const char *repository,
200 const char *update_dir, List *entries)
202 TRACE (TRACE_FUNCTION, "admin_filesdoneproc (%d, %s, %s)", err, repository,
203 update_dir);
204 Parse_Info (CVSROOTADM_POSTADMIN, repository, postadmin_proc, PIOPT_ALL,
205 NULL);
207 return err;
211 static size_t
212 wescape (char *dst, const char *src)
214 const unsigned char *s = src;
215 char *d = dst;
216 for (; *s; s++) {
217 if (!isprint(*s) || isspace(*s) || *s == '|') {
218 *d++ = '\\';
219 *d++ = ((*s >> 6) & 3) + '0';
220 *d++ = ((*s >> 3) & 7) + '0';
221 *d++ = ((*s >> 0) & 7) + '0';
222 } else {
223 *d++ = *s;
226 *d = '\0';
227 return d - dst;
230 static char *
231 makecmdline (int argc, char **argv)
233 size_t clen = 1024, wlen = 1024, len, cpos = 0, i;
234 char *cmd = xmalloc(clen);
235 char *word = xmalloc(wlen);
237 for (i = 0; i < argc; i++) {
238 char *arg = (strncmp(argv[i], "cvs ", 4) == 0) ? argv[i] + 4 : argv[i];
239 len = strlen(arg);
240 if (len * 4 < wlen) {
241 wlen += len * 4;
242 word = xrealloc(word, wlen);
244 len = wescape(word, arg);
245 if (clen - cpos < len + 2) {
246 clen += len + 2;
247 cmd = xrealloc(cmd, clen);
249 memcpy(&cmd[cpos], word, len);
250 cpos += len;
251 cmd[cpos++] = ' ';
253 if (cpos != 0)
254 cmd[cpos - 1] = '\0';
255 else
256 cmd[cpos] = '\0';
257 free(word);
258 return cmd;
262 admin_group_member (void)
264 struct group *grp;
265 int i;
267 if (config == NULL || config->UserAdminGroup == NULL)
268 return 1;
270 if ((grp = getgrnam(config->UserAdminGroup)) == NULL)
271 return 0;
274 #ifdef HAVE_GETGROUPS
275 gid_t *grps;
276 int n;
278 /* get number of auxiliary groups */
279 n = getgroups (0, NULL);
280 if (n < 0)
281 error (1, errno, "unable to get number of auxiliary groups");
282 grps = (gid_t *) xmalloc((n + 1) * sizeof *grps);
283 n = getgroups (n, grps);
284 if (n < 0)
285 error (1, errno, "unable to get list of auxiliary groups");
286 grps[n] = getgid();
287 for (i = 0; i <= n; i++)
288 if (grps[i] == grp->gr_gid) break;
289 free (grps);
290 if (i > n)
291 return 0;
292 #else
293 char *me = getcaller();
294 char **grnam;
296 for (grnam = grp->gr_mem; *grnam; grnam++)
297 if (strcmp (*grnam, me) == 0) break;
298 if (!*grnam && getgid() != grp->gr_gid)
299 return 0;
300 #endif
302 return 1;
306 admin (int argc, char **argv)
308 int err;
309 struct admin_data admin_data;
310 int c;
311 int i;
312 bool only_allowed_options;
314 if (argc <= 1)
315 usage (admin_usage);
317 wrap_setup ();
319 memset (&admin_data, 0, sizeof admin_data);
320 admin_data.cmdline = makecmdline (argc, argv);
322 /* TODO: get rid of `-' switch notation in admin_data. For
323 example, admin_data->branch should be not `-bfoo' but simply `foo'. */
325 getoptreset ();
326 only_allowed_options = true;
327 while ((c = getopt (argc, argv,
328 "+ib::c:a:A:e::l::u::LUn:N:m:o:s:t::IqxV:k:")) != -1)
330 if (config != NULL) {
331 if (c != 'q' && !strchr (config->UserAdminOptions, c))
332 only_allowed_options = false;
333 } else {
334 #ifdef CLIENT_SUPPORT
335 assert(current_parsed_root->isremote);
336 only_allowed_options = false;
337 #else
338 assert(0); /* config should not be NULL, except in a client */
339 #endif
342 switch (c)
344 case 'i':
345 /* This has always been documented as useless in cvs.texinfo
346 and it really is--admin_fileproc silently does nothing
347 if vers->vn_user is NULL. */
348 error (0, 0, "the -i option to admin is not supported");
349 error (0, 0, "run add or import to create an RCS file");
350 goto usage_error;
352 case 'b':
353 if (admin_data.branch != NULL)
355 error (0, 0, "duplicate 'b' option");
356 goto usage_error;
358 if (optarg == NULL)
359 admin_data.branch = xstrdup ("-b");
360 else
361 admin_data.branch = Xasprintf ("-b%s", optarg);
362 break;
364 case 'c':
365 if (admin_data.comment != NULL)
367 error (0, 0, "duplicate 'c' option");
368 goto usage_error;
370 admin_data.comment = Xasprintf ("-c%s", optarg);
371 break;
373 case 'a':
374 arg_add (&admin_data, 'a', optarg);
375 break;
377 case 'A':
378 /* In the client/server case, this is cheesy because
379 we just pass along the name of the RCS file, which
380 then will want to exist on the server. This is
381 accidental; having the client specify a pathname on
382 the server is not a design feature of the protocol. */
383 arg_add (&admin_data, 'A', optarg);
384 break;
386 case 'e':
387 arg_add (&admin_data, 'e', optarg);
388 break;
390 case 'l':
391 /* Note that multiple -l options are valid. */
392 arg_add (&admin_data, 'l', optarg);
393 break;
395 case 'u':
396 /* Note that multiple -u options are valid. */
397 arg_add (&admin_data, 'u', optarg);
398 break;
400 case 'L':
401 /* Probably could also complain if -L is specified multiple
402 times, although RCS doesn't and I suppose it is reasonable
403 just to have it mean the same as a single -L. */
404 if (admin_data.set_nonstrict)
406 error (0, 0, "-U and -L are incompatible");
407 goto usage_error;
409 admin_data.set_strict = 1;
410 break;
412 case 'U':
413 /* Probably could also complain if -U is specified multiple
414 times, although RCS doesn't and I suppose it is reasonable
415 just to have it mean the same as a single -U. */
416 if (admin_data.set_strict)
418 error (0, 0, "-U and -L are incompatible");
419 goto usage_error;
421 admin_data.set_nonstrict = 1;
422 break;
424 case 'n':
425 /* Mostly similar to cvs tag. Could also be parsing
426 the syntax of optarg, although for now we just pass
427 it to rcs as-is. Note that multiple -n options are
428 valid. */
429 arg_add (&admin_data, 'n', optarg);
430 break;
432 case 'N':
433 /* Mostly similar to cvs tag. Could also be parsing
434 the syntax of optarg, although for now we just pass
435 it to rcs as-is. Note that multiple -N options are
436 valid. */
437 arg_add (&admin_data, 'N', optarg);
438 break;
440 case 'm':
441 /* Change log message. Could also be parsing the syntax
442 of optarg, although for now we just pass it to rcs
443 as-is. Note that multiple -m options are valid. */
444 arg_add (&admin_data, 'm', optarg);
445 break;
447 case 'o':
448 /* Delete revisions. Probably should also be parsing the
449 syntax of optarg, so that the client can give errors
450 rather than making the server take care of that.
451 Other than that I'm not sure whether it matters much
452 whether we parse it here or in admin_fileproc.
454 Note that multiple -o options are invalid, in RCS
455 as well as here. */
457 if (admin_data.delete_revs != NULL)
459 error (0, 0, "duplicate '-o' option");
460 goto usage_error;
462 admin_data.delete_revs = Xasprintf ("-o%s", optarg);
463 break;
465 case 's':
466 /* Note that multiple -s options are valid. */
467 arg_add (&admin_data, 's', optarg);
468 break;
470 case 't':
471 if (admin_data.desc != NULL)
473 error (0, 0, "duplicate 't' option");
474 goto usage_error;
476 if (optarg != NULL && optarg[0] == '-')
477 admin_data.desc = xstrdup (optarg + 1);
478 else
480 size_t bufsize = 0;
481 size_t len;
483 get_file (optarg, optarg, "r", &admin_data.desc,
484 &bufsize, &len);
486 break;
488 case 'I':
489 /* At least in RCS this can be specified several times,
490 with the same meaning as being specified once. */
491 admin_data.interactive = 1;
492 break;
494 case 'q':
495 /* Silently set the global really_quiet flag. This keeps admin in
496 * sync with the RCS man page and allows us to silently support
497 * older servers when necessary.
499 * Some logic says we might want to output a deprecation warning
500 * here, but I'm opting not to in order to stay quietly in sync
501 * with the RCS man page.
503 really_quiet = 1;
504 break;
506 case 'x':
507 error (0, 0, "the -x option has never done anything useful");
508 error (0, 0, "RCS files in CVS always end in ,v");
509 goto usage_error;
511 case 'V':
512 /* No longer supported. */
513 error (0, 0, "the `-V' option is obsolete");
514 break;
516 case 'k':
517 if (admin_data.kflag != NULL)
519 error (0, 0, "duplicate '-k' option");
520 goto usage_error;
522 admin_data.kflag = RCS_check_kflag (optarg);
523 break;
524 default:
525 case '?':
526 /* getopt will have printed an error message. */
528 usage_error:
529 /* Don't use cvs_cmd_name; it might be "server". */
530 error (1, 0, "specify %s -H admin for usage information",
531 program_name);
534 argc -= optind;
535 argv += optind;
537 /* The use of `cvs admin -k' is unrestricted. However, any other
538 option is restricted if the group CVS_ADMIN_GROUP exists on the
539 server. */
540 /* This is only "secure" on the server, since the user could edit the
541 * RCS file on a local host, but some people like this kind of
542 * check anyhow. The alternative would be to check only when
543 * (server_active) rather than when not on the client.
545 if (!only_allowed_options && !admin_group_member())
546 error (1, 0, "usage is restricted to members of the group %s",
547 CVS_ADMIN_GROUP);
549 for (i = 0; i < admin_data.ac; ++i)
551 assert (admin_data.av[i][0] == '-');
552 switch (admin_data.av[i][1])
554 case 'm':
555 case 'l':
556 case 'u':
557 check_numeric (&admin_data.av[i][2], argc, argv);
558 break;
559 default:
560 break;
563 if (admin_data.branch != NULL)
564 check_numeric (admin_data.branch + 2, argc, argv);
565 if (admin_data.delete_revs != NULL)
567 char *p;
569 check_numeric (admin_data.delete_revs + 2, argc, argv);
570 p = strchr (admin_data.delete_revs + 2, ':');
571 if (p != NULL && isdigit ((unsigned char) p[1]))
572 check_numeric (p + 1, argc, argv);
573 else if (p != NULL && p[1] == ':' && isdigit ((unsigned char) p[2]))
574 check_numeric (p + 2, argc, argv);
577 #ifdef CLIENT_SUPPORT
578 if (current_parsed_root->isremote)
580 /* We're the client side. Fire up the remote server. */
581 start_server ();
583 ign_setup ();
585 /* Note that option_with_arg does not work for us, because some
586 of the options must be sent without a space between the option
587 and its argument. */
588 if (admin_data.interactive)
589 error (1, 0, "-I option not useful with client/server");
590 if (admin_data.branch != NULL)
591 send_arg (admin_data.branch);
592 if (admin_data.comment != NULL)
593 send_arg (admin_data.comment);
594 if (admin_data.set_strict)
595 send_arg ("-L");
596 if (admin_data.set_nonstrict)
597 send_arg ("-U");
598 if (admin_data.delete_revs != NULL)
599 send_arg (admin_data.delete_revs);
600 if (admin_data.desc != NULL)
602 char *p = admin_data.desc;
603 send_to_server ("Argument -t-", 0);
604 while (*p)
606 if (*p == '\n')
608 send_to_server ("\012Argumentx ", 0);
609 ++p;
611 else
613 char *q = strchr (p, '\n');
614 if (q == NULL) q = p + strlen (p);
615 send_to_server (p, q - p);
616 p = q;
619 send_to_server ("\012", 1);
621 /* Send this for all really_quiets since we know that it will be silently
622 * ignored when unneeded. This supports old servers.
624 if (really_quiet)
625 send_arg ("-q");
626 if (admin_data.kflag != NULL)
627 send_arg (admin_data.kflag);
629 for (i = 0; i < admin_data.ac; ++i)
630 send_arg (admin_data.av[i]);
632 send_arg ("--");
633 send_files (argc, argv, 0, 0, SEND_NO_CONTENTS);
634 send_file_names (argc, argv, SEND_EXPAND_WILD);
635 send_to_server ("admin\012", 0);
636 err = get_responses_and_close ();
637 goto return_it;
639 #endif /* CLIENT_SUPPORT */
641 lock_tree_promotably (argc, argv, 0, W_LOCAL, 0);
643 err = start_recursion
644 (admin_fileproc, admin_filesdoneproc, admin_dirproc,
645 NULL, &admin_data,
646 argc, argv, 0,
647 W_LOCAL, 0, CVS_LOCK_WRITE, NULL, 1, NULL);
649 Lock_Cleanup ();
651 /* This just suppresses a warning from -Wall. */
652 #ifdef CLIENT_SUPPORT
653 return_it:
654 #endif /* CLIENT_SUPPORT */
655 if (admin_data.cmdline != NULL)
656 free (admin_data.cmdline);
657 if (admin_data.branch != NULL)
658 free (admin_data.branch);
659 if (admin_data.comment != NULL)
660 free (admin_data.comment);
661 if (admin_data.delete_revs != NULL)
662 free (admin_data.delete_revs);
663 if (admin_data.kflag != NULL)
664 free (admin_data.kflag);
665 if (admin_data.desc != NULL)
666 free (admin_data.desc);
667 for (i = 0; i < admin_data.ac; ++i)
668 free (admin_data.av[i]);
669 if (admin_data.av != NULL)
670 free (admin_data.av);
672 return err;
678 * Called to run "rcs" on a particular file.
680 /* ARGSUSED */
681 static int
682 admin_fileproc (void *callerdat, struct file_info *finfo)
684 struct admin_data *admin_data = (struct admin_data *) callerdat;
685 Vers_TS *vers;
686 char *version;
687 int i;
688 int status = 0;
689 RCSNode *rcs, *rcs2;
691 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
693 version = vers->vn_user;
694 if (version != NULL && strcmp (version, "0") == 0)
696 error (0, 0, "cannot admin newly added file `%s'", finfo->file);
697 status = 1;
698 goto exitfunc;
701 history_write ('X', finfo->update_dir, admin_data->cmdline, finfo->file,
702 finfo->repository);
703 rcs = vers->srcfile;
704 if (rcs == NULL)
706 if (!really_quiet)
707 error (0, 0, "nothing known about %s", finfo->file);
708 status = 1;
709 goto exitfunc;
712 if (rcs->flags & PARTIAL)
713 RCS_reparsercsfile (rcs, NULL, NULL);
715 if (!really_quiet)
717 cvs_output ("RCS file: ", 0);
718 cvs_output (rcs->path, 0);
719 cvs_output ("\n", 1);
722 if (admin_data->branch != NULL)
724 char *branch = &admin_data->branch[2];
725 if (*branch != '\0' && ! isdigit ((unsigned char) *branch))
727 branch = RCS_whatbranch (rcs, admin_data->branch + 2);
728 if (branch == NULL)
730 error (0, 0, "%s: Symbolic name %s is undefined.",
731 rcs->path, admin_data->branch + 2);
732 status = 1;
735 if (status == 0)
736 RCS_setbranch (rcs, branch);
737 if (branch != NULL && branch != &admin_data->branch[2])
738 free (branch);
740 if (admin_data->comment != NULL)
742 if (rcs->comment != NULL)
743 free (rcs->comment);
744 rcs->comment = xstrdup (admin_data->comment + 2);
746 if (admin_data->set_strict)
747 rcs->strict_locks = 1;
748 if (admin_data->set_nonstrict)
749 rcs->strict_locks = 0;
750 if (admin_data->delete_revs != NULL)
752 char *s, *t, *rev1, *rev2;
753 /* Set for :, clear for ::. */
754 int inclusive;
755 char *t2;
757 s = admin_data->delete_revs + 2;
758 inclusive = 1;
759 t = strchr (s, ':');
760 if (t != NULL)
762 if (t[1] == ':')
764 inclusive = 0;
765 t2 = t + 2;
767 else
768 t2 = t + 1;
771 /* Note that we don't support '-' for ranges. RCS considers it
772 obsolete and it is problematic with tags containing '-'. "cvs log"
773 has made the same decision. */
775 if (t == NULL)
777 /* -orev */
778 rev1 = xstrdup (s);
779 rev2 = xstrdup (s);
781 else if (t == s)
783 /* -o:rev2 */
784 rev1 = NULL;
785 rev2 = xstrdup (t2);
787 else
789 *t = '\0';
790 rev1 = xstrdup (s);
791 *t = ':'; /* probably unnecessary */
792 if (*t2 == '\0')
793 /* -orev1: */
794 rev2 = NULL;
795 else
796 /* -orev1:rev2 */
797 rev2 = xstrdup (t2);
800 if (rev1 == NULL && rev2 == NULL)
802 /* RCS segfaults if `-o:' is given */
803 error (0, 0, "no valid revisions specified in `%s' option",
804 admin_data->delete_revs);
805 status = 1;
807 else
809 status |= RCS_delete_revs (rcs, rev1, rev2, inclusive);
810 if (rev1)
811 free (rev1);
812 if (rev2)
813 free (rev2);
816 if (admin_data->desc != NULL)
818 free (rcs->desc);
819 rcs->desc = xstrdup (admin_data->desc);
821 if (admin_data->kflag != NULL)
823 char *kflag = admin_data->kflag + 2;
824 char *oldexpand = RCS_getexpand (rcs);
825 if (oldexpand == NULL || strcmp (oldexpand, kflag) != 0)
826 RCS_setexpand (rcs, kflag);
829 /* Handle miscellaneous options. TODO: decide whether any or all
830 of these should have their own fields in the admin_data
831 structure. */
832 for (i = 0; i < admin_data->ac; ++i)
834 char *arg;
835 char *p, *rev, *revnum, *tag, *msg;
836 char **users;
837 int argc, u;
838 Node *n;
839 RCSVers *delta;
841 arg = admin_data->av[i];
842 switch (arg[1])
844 case 'a': /* fall through */
845 case 'e':
846 line2argv (&argc, &users, arg + 2, " ,\t\n");
847 if (arg[1] == 'a')
848 for (u = 0; u < argc; ++u)
849 RCS_addaccess (rcs, users[u]);
850 else if (argc == 0)
851 RCS_delaccess (rcs, NULL);
852 else
853 for (u = 0; u < argc; ++u)
854 RCS_delaccess (rcs, users[u]);
855 free_names (&argc, users);
856 break;
857 case 'A':
859 /* See admin-19a-admin and friends in sanity.sh for
860 relative pathnames. It makes sense to think in
861 terms of a syntax which give pathnames relative to
862 the repository or repository corresponding to the
863 current directory or some such (and perhaps don't
864 include ,v), but trying to worry about such things
865 is a little pointless unless you first worry about
866 whether "cvs admin -A" as a whole makes any sense
867 (currently probably not, as access lists don't
868 affect the behavior of CVS). */
870 rcs2 = RCS_parsercsfile (arg + 2);
871 if (rcs2 == NULL)
872 error (1, 0, "cannot continue");
874 p = xstrdup (RCS_getaccess (rcs2));
875 line2argv (&argc, &users, p, " \t\n");
876 free (p);
877 freercsnode (&rcs2);
879 for (u = 0; u < argc; ++u)
880 RCS_addaccess (rcs, users[u]);
881 free_names (&argc, users);
882 break;
883 case 'n': /* fall through */
884 case 'N':
885 if (arg[2] == '\0')
887 cvs_outerr ("missing symbolic name after ", 0);
888 cvs_outerr (arg, 0);
889 cvs_outerr ("\n", 1);
890 break;
892 p = strchr (arg, ':');
893 if (p == NULL)
895 if (RCS_deltag (rcs, arg + 2) != 0)
897 error (0, 0, "%s: Symbolic name %s is undefined.",
898 rcs->path,
899 arg + 2);
900 status = 1;
901 continue;
903 break;
905 *p = '\0';
906 tag = xstrdup (arg + 2);
907 *p++ = ':';
909 /* Option `n' signals an error if this tag is already bound. */
910 if (arg[1] == 'n')
912 n = findnode (RCS_symbols (rcs), tag);
913 if (n != NULL)
915 error (0, 0,
916 "%s: symbolic name %s already bound to %s",
917 rcs->path,
918 tag, (char *)n->data);
919 status = 1;
920 free (tag);
921 continue;
925 /* Attempt to perform the requested tagging. */
927 if ((*p == 0 && (rev = RCS_head (rcs)))
928 || (rev = RCS_tag2rev (rcs, p))) /* tag2rev may exit */
930 RCS_check_tag (tag); /* exit if not a valid tag */
931 RCS_settag (rcs, tag, rev);
932 free (rev);
934 else
936 if (!really_quiet)
937 error (0, 0,
938 "%s: Symbolic name or revision %s is undefined.",
939 rcs->path, p);
940 status = 1;
942 free (tag);
943 break;
944 case 's':
945 p = strchr (arg, ':');
946 if (p == NULL)
948 tag = xstrdup (arg + 2);
949 rev = RCS_head (rcs);
950 if (!rev)
952 error (0, 0, "No head revision in archive file `%s'.",
953 rcs->path);
954 status = 1;
955 continue;
958 else
960 *p = '\0';
961 tag = xstrdup (arg + 2);
962 *p++ = ':';
963 rev = xstrdup (p);
965 revnum = RCS_gettag (rcs, rev, 0, NULL);
966 if (revnum != NULL)
968 n = findnode (rcs->versions, revnum);
969 free (revnum);
971 else
972 n = NULL;
973 if (n == NULL)
975 error (0, 0,
976 "%s: can't set state of nonexisting revision %s",
977 rcs->path,
978 rev);
979 free (rev);
980 status = 1;
981 continue;
983 free (rev);
984 delta = n->data;
985 free (delta->state);
986 delta->state = tag;
987 break;
989 case 'm':
990 p = strchr (arg, ':');
991 if (p == NULL)
993 error (0, 0, "%s: -m option lacks revision number",
994 rcs->path);
995 status = 1;
996 continue;
998 *p = '\0'; /* temporarily make arg+2 its own string */
999 rev = RCS_gettag (rcs, arg + 2, 1, NULL); /* Force tag match */
1000 if (rev == NULL)
1002 error (0, 0, "%s: no such revision %s", rcs->path, arg+2);
1003 status = 1;
1004 *p = ':'; /* restore the full text of the -m argument */
1005 continue;
1007 msg = p+1;
1009 n = findnode (rcs->versions, rev);
1010 /* tags may exist against non-existing versions */
1011 if (n == NULL)
1013 error (0, 0, "%s: no such revision %s: %s",
1014 rcs->path, arg+2, rev);
1015 status = 1;
1016 *p = ':'; /* restore the full text of the -m argument */
1017 free (rev);
1018 continue;
1020 *p = ':'; /* restore the full text of the -m argument */
1021 free (rev);
1023 delta = n->data;
1024 if (delta->text == NULL)
1026 delta->text = xmalloc (sizeof (Deltatext));
1027 memset (delta->text, 0, sizeof (Deltatext));
1029 delta->text->version = xstrdup (delta->version);
1030 delta->text->log = make_message_rcsvalid (msg);
1031 break;
1033 case 'l':
1034 status |= RCS_lock (rcs, arg[2] ? arg + 2 : NULL, 0);
1035 break;
1036 case 'u':
1037 status |= RCS_unlock (rcs, arg[2] ? arg + 2 : NULL, 0);
1038 break;
1039 default: assert(0); /* can't happen */
1043 if (status == 0)
1045 RCS_rewrite (rcs, NULL, NULL);
1046 if (!really_quiet)
1047 cvs_output ("done\n", 5);
1049 else
1051 /* Note that this message should only occur after another
1052 message has given a more specific error. The point of this
1053 additional message is to make it clear that the previous problems
1054 caused CVS to forget about the idea of modifying the RCS file. */
1055 if (!really_quiet)
1056 error (0, 0, "RCS file for `%s' not modified.", finfo->file);
1057 RCS_abandon (rcs);
1060 exitfunc:
1061 freevers_ts (&vers);
1062 return status;
1068 * Print a warm fuzzy message
1070 /* ARGSUSED */
1071 static Dtype
1072 admin_dirproc (void *callerdat, const char *dir, const char *repos,
1073 const char *update_dir, List *entries)
1075 if (!quiet)
1076 error (0, 0, "Administrating %s", update_dir);
1077 return R_PROCESS;