Bug Summary

File:src/gnu/usr.bin/cvs/src/admin.c
Warning:line 208, column 33
Null pointer passed as 1st argument to string length function

Annotated Source Code

Press '?' to see keyboard shortcuts

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