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