Bug Summary

File:src/gnu/usr.bin/cvs/src/tag.c
Warning:line 1213, column 5
Value stored to 'err' is never read

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 tag.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/tag.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 * Tag and Rtag
9 *
10 * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
11 * Tag uses the checked out revision in the current directory, rtag uses
12 * the modules database, if necessary.
13 */
14
15#include "cvs.h"
16#include "savecwd.h"
17
18static int rtag_proc PROTO((int argc, char **argv, char *xwhere,(int argc, char **argv, char *xwhere, char *mwhere, char *mfile
, int shorten, int local_specified, char *mname, char *msg)
19 char *mwhere, char *mfile, int shorten,(int argc, char **argv, char *xwhere, char *mwhere, char *mfile
, int shorten, int local_specified, char *mname, char *msg)
20 int local_specified, char *mname, char *msg))(int argc, char **argv, char *xwhere, char *mwhere, char *mfile
, int shorten, int local_specified, char *mname, char *msg)
;
21static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo))(void *callerdat, struct file_info *finfo);
22static int check_filesdoneproc PROTO ((void *callerdat, int err,(void *callerdat, int err, char *repos, char *update_dir, List
*entries)
23 char *repos, char *update_dir,(void *callerdat, int err, char *repos, char *update_dir, List
*entries)
24 List *entries))(void *callerdat, int err, char *repos, char *update_dir, List
*entries)
;
25static int pretag_proc PROTO((char *repository, char *filter))(char *repository, char *filter);
26static void masterlist_delproc PROTO((Node *p))(Node *p);
27static void tag_delproc PROTO((Node *p))(Node *p);
28static int pretag_list_proc PROTO((Node *p, void *closure))(Node *p, void *closure);
29
30static Dtype tag_dirproc PROTO ((void *callerdat, char *dir,(void *callerdat, char *dir, char *repos, char *update_dir, List
*entries)
31 char *repos, char *update_dir,(void *callerdat, char *dir, char *repos, char *update_dir, List
*entries)
32 List *entries))(void *callerdat, char *dir, char *repos, char *update_dir, List
*entries)
;
33static int rtag_fileproc PROTO ((void *callerdat, struct file_info *finfo))(void *callerdat, struct file_info *finfo);
34static int rtag_delete PROTO((RCSNode *rcsfile))(RCSNode *rcsfile);
35static int tag_fileproc PROTO ((void *callerdat, struct file_info *finfo))(void *callerdat, struct file_info *finfo);
36
37static char *numtag; /* specific revision to tag */
38static int numtag_validated = 0;
39static char *date = NULL((void*)0);
40static char *symtag; /* tag to add or delete */
41static int delete_flag; /* adding a tag by default */
42static int branch_mode; /* make an automagic "branch" tag */
43static int local; /* recursive by default */
44static int force_tag_match = 1; /* force tag to match by default */
45static int force_tag_move; /* don't force tag to move by default */
46static int check_uptodate; /* no uptodate-check by default */
47static int attic_too; /* remove tag from Attic files */
48static int is_rtag;
49
50struct tag_info
51{
52 Ctype status;
53 char *rev;
54 char *tag;
55 char *options;
56};
57
58struct master_lists
59{
60 List *tlist;
61};
62
63static List *mtlist;
64static List *tlist;
65
66static const char rtag_opts[] = "+abdFflnQqRr:D:";
67static const char *const rtag_usage[] =
68{
69 "Usage: %s %s [-abdFflnR] [-r rev|-D date] tag modules...\n",
70 "\t-a\tClear tag from removed files that would not otherwise be tagged.\n",
71 "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
72 "\t-d\tDelete the given tag.\n",
73 "\t-F\tMove tag if it already exists.\n",
74 "\t-f\tForce a head revision match if tag/date not found.\n",
75 "\t-l\tLocal directory only, not recursive.\n",
76 "\t-n\tNo execution of 'tag program'.\n",
77 "\t-R\tProcess directories recursively.\n",
78 "\t-r rev\tExisting revision/tag.\n",
79 "\t-D\tExisting date.\n",
80 "(Specify the --help global option for a list of other help options)\n",
81 NULL((void*)0)
82};
83
84static const char tag_opts[] = "+bcdFflQqRr:D:";
85static const char *const tag_usage[] =
86{
87 "Usage: %s %s [-bcdFflR] [-r rev|-D date] tag [files...]\n",
88 "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
89 "\t-c\tCheck that working files are unmodified.\n",
90 "\t-d\tDelete the given tag.\n",
91 "\t-F\tMove tag if it already exists.\n",
92 "\t-f\tForce a head revision match if tag/date not found.\n",
93 "\t-l\tLocal directory only, not recursive.\n",
94 "\t-R\tProcess directories recursively.\n",
95 "\t-r rev\tExisting revision/tag.\n",
96 "\t-D\tExisting date.\n",
97 "(Specify the --help global option for a list of other help options)\n",
98 NULL((void*)0)
99};
100
101int
102cvstag (argc, argv)
103 int argc;
104 char **argv;
105{
106 int c;
107 int err = 0;
108 int run_module_prog = 1;
109
110 is_rtag = (strcmp (command_name, "rtag") == 0);
111
112 if (argc == -1)
113 usage (is_rtag ? rtag_usage : tag_usage);
114
115 optind = 0;
116 while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1)
117 {
118 switch (c)
119 {
120 case 'a':
121 attic_too = 1;
122 break;
123 case 'b':
124 branch_mode = 1;
125 break;
126 case 'c':
127 check_uptodate = 1;
128 break;
129 case 'd':
130 delete_flag = 1;
131 break;
132 case 'F':
133 force_tag_move = 1;
134 break;
135 case 'f':
136 force_tag_match = 0;
137 break;
138 case 'l':
139 local = 1;
140 break;
141 case 'n':
142 run_module_prog = 0;
143 break;
144 case 'Q':
145 case 'q':
146#ifdef SERVER_SUPPORT1
147 /* The CVS 1.5 client sends these options (in addition to
148 Global_option requests), so we must ignore them. */
149 if (!server_active)
150#endif
151 error (1, 0,
152 "-q or -Q must be specified before \"%s\"",
153 command_name);
154 break;
155 case 'R':
156 local = 0;
157 break;
158 case 'r':
159 numtag = optarg;
160 break;
161 case 'D':
162 if (date)
163 free (date);
164 date = Make_Date (optarg);
165 break;
166 case '?':
167 default:
168 usage (is_rtag ? rtag_usage : tag_usage);
169 break;
170 }
171 }
172 argc -= optind;
173 argv += optind;
174
175 if (argc < (is_rtag ? 2 : 1))
176 usage (is_rtag ? rtag_usage : tag_usage);
177 symtag = argv[0];
178 argc--;
179 argv++;
180
181 if (date && numtag)
182 error (1, 0, "-r and -D options are mutually exclusive");
183 if (delete_flag && branch_mode)
184 error (0, 0, "warning: -b ignored with -d options");
185 RCS_check_tag (symtag);
186
187#ifdef CLIENT_SUPPORT1
188 if (current_parsed_root->isremote)
189 {
190 /* We're the client side. Fire up the remote server. */
191 start_server ();
192
193 ign_setup ();
194
195 if (attic_too)
196 send_arg("-a");
197 if (branch_mode)
198 send_arg("-b");
199 if (check_uptodate)
200 send_arg("-c");
201 if (delete_flag)
202 send_arg("-d");
203 if (force_tag_move)
204 send_arg("-F");
205 if (!force_tag_match)
206 send_arg ("-f");
207 if (local)
208 send_arg("-l");
209 if (!run_module_prog)
210 send_arg("-n");
211
212 if (numtag)
213 option_with_arg ("-r", numtag);
214 if (date)
215 client_senddate (date);
216
217 send_arg (symtag);
218
219 if (is_rtag)
220 {
221 int i;
222 for (i = 0; i < argc; ++i)
223 send_arg (argv[i]);
224 send_to_server ("rtag\012", 0);
225 }
226 else
227 {
228
229 send_files (argc, argv, local, 0,
230
231 /* I think the -c case is like "cvs status", in
232 which we really better be correct rather than
233 being fast; it is just too confusing otherwise. */
234 check_uptodate ? 0 : SEND_NO_CONTENTS4);
235 send_file_names (argc, argv, SEND_EXPAND_WILD1);
236 send_to_server ("tag\012", 0);
237 }
238
239 return get_responses_and_close ();
240 }
241#endif
242
243 if (is_rtag)
244 {
245 DBM *db;
246 int i;
247 db = open_module ();
248 for (i = 0; i < argc; i++)
249 {
250 /* XXX last arg should be repository, but doesn't make sense here */
251 history_write ('T', (delete_flag ? "D" : (numtag ? numtag :
252 (date ? date : "A"))), symtag, argv[i], "");
253 err += do_module (db, argv[i], TAG,
254 delete_flag ? "Untagging" : "Tagging",
255 rtag_proc, (char *) NULL((void*)0), 0, 0, run_module_prog,
256 0, symtag);
257 }
258 close_module (db);
259 }
260 else
261 {
262 err = rtag_proc (argc + 1, argv - 1, NULL((void*)0), NULL((void*)0), NULL((void*)0), 0, 0, NULL((void*)0),
263 NULL((void*)0));
264 }
265
266 return (err);
267}
268
269/*
270 * callback proc for doing the real work of tagging
271 */
272/* ARGSUSED */
273static int
274rtag_proc (argc, argv, xwhere, mwhere, mfile, shorten, local_specified,
275 mname, msg)
276 int argc;
277 char **argv;
278 char *xwhere;
279 char *mwhere;
280 char *mfile;
281 int shorten;
282 int local_specified;
283 char *mname;
284 char *msg;
285{
286 /* Begin section which is identical to patch_proc--should this
287 be abstracted out somehow? */
288 char *myargv[2];
289 int err = 0;
290 int which;
291 char *repository;
292 char *where;
293
294 if (is_rtag)
295 {
296 repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0])
297 + (mfile == NULL((void*)0) ? 0 : strlen (mfile) + 1) + 2);
298 (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
299 where = xmalloc (strlen (argv[0]) + (mfile == NULL((void*)0) ? 0 : strlen (mfile) + 1)
300 + 1);
301 (void) strcpy (where, argv[0]);
302
303 /* if mfile isn't null, we need to set up to do only part of the module */
304 if (mfile != NULL((void*)0))
305 {
306 char *cp;
307 char *path;
308
309 /* if the portion of the module is a path, put the dir part on repos */
310 if ((cp = strrchr (mfile, '/')) != NULL((void*)0))
311 {
312 *cp = '\0';
313 (void) strcat (repository, "/");
314 (void) strcat (repository, mfile);
315 (void) strcat (where, "/");
316 (void) strcat (where, mfile);
317 mfile = cp + 1;
318 }
319
320 /* take care of the rest */
321 path = xmalloc (strlen (repository) + strlen (mfile) + 5);
322 (void) sprintf (path, "%s/%s", repository, mfile);
323 if (isdir (path))
324 {
325 /* directory means repository gets the dir tacked on */
326 (void) strcpy (repository, path);
327 (void) strcat (where, "/");
328 (void) strcat (where, mfile);
329 }
330 else
331 {
332 myargv[0] = argv[0];
333 myargv[1] = mfile;
334 argc = 2;
335 argv = myargv;
336 }
337 free (path);
338 }
339
340 /* cd to the starting repository */
341 if ( CVS_CHDIRchdir (repository) < 0)
342 {
343 error (0, errno(*__errno()), "cannot chdir to %s", repository);
344 free (repository);
345 return (1);
346 }
347 free (repository);
348 /* End section which is identical to patch_proc. */
349
350 if (delete_flag || attic_too || (force_tag_match && numtag))
351 which = W_REPOS0x02 | W_ATTIC0x04;
352 else
353 which = W_REPOS0x02;
354 repository = NULL((void*)0);
355 }
356 else
357 {
358 where = NULL((void*)0);
359 which = W_LOCAL0x01;
360 repository = "";
361 }
362
363 if (numtag != NULL((void*)0) && !numtag_validated)
364 {
365 tag_check_valid (numtag, argc - 1, argv + 1, local, 0, repository);
366 numtag_validated = 1;
367 }
368
369 /* check to make sure they are authorized to tag all the
370 specified files in the repository */
371
372 mtlist = getlist();
373 err = start_recursion (check_fileproc, check_filesdoneproc,
374 (DIRENTPROC) NULL((void*)0), (DIRLEAVEPROC) NULL((void*)0), NULL((void*)0),
375 argc - 1, argv + 1, local, which, 0, 1,
376 where, 1);
377
378 if (err)
379 {
380 error (1, 0, "correct the above errors first!");
381 }
382
383 /* It would be nice to provide consistency with respect to
384 commits; however CVS lacks the infrastructure to do that (see
385 Concurrency in cvs.texinfo and comment in do_recursion). We
386 do need to ensure that the RCS file info that gets read and
387 cached in do_recursion isn't stale by the time we get around
388 to using it to rewrite the RCS file in the callback, and this
389 is the easiest way to accomplish that. */
390 lock_tree_for_write (argc - 1, argv + 1, local, which, 0);
391
392 /* start the recursion processor */
393 err = start_recursion (is_rtag ? rtag_fileproc : tag_fileproc,
394 (FILESDONEPROC) NULL((void*)0), tag_dirproc,
395 (DIRLEAVEPROC) NULL((void*)0), NULL((void*)0), argc - 1, argv + 1,
396 local, which, 0, 0, where, 1);
397 Lock_Cleanup ();
398 dellist (&mtlist);
399 if (where != NULL((void*)0))
400 free (where);
401 return (err);
402}
403
404/* check file that is to be tagged */
405/* All we do here is add it to our list */
406
407static int
408check_fileproc (callerdat, finfo)
409 void *callerdat;
410 struct file_info *finfo;
411{
412 char *xdir;
413 Node *p;
414 Vers_TS *vers;
415
416 if (check_uptodate)
417 {
418 Ctype status = Classify_File (finfo, (char *) NULL((void*)0), (char *) NULL((void*)0),
419 (char *) NULL((void*)0), 1, 0, &vers, 0);
420 if ((status != T_UPTODATE) && (status != T_CHECKOUT) &&
421 (status != T_PATCH))
422 {
423 error (0, 0, "%s is locally modified", finfo->fullname);
424 freevers_ts (&vers);
425 return (1);
426 }
427 }
428 else
429 vers = Version_TS (finfo, NULL((void*)0), NULL((void*)0), NULL((void*)0), 0, 0);
430
431 if (finfo->update_dir[0] == '\0')
432 xdir = ".";
433 else
434 xdir = finfo->update_dir;
435 if ((p = findnode (mtlist, xdir)) != NULL((void*)0))
436 {
437 tlist = ((struct master_lists *) p->data)->tlist;
438 }
439 else
440 {
441 struct master_lists *ml;
442
443 tlist = getlist ();
444 p = getnode ();
445 p->key = xstrdup (xdir);
446 p->type = UPDATE;
447 ml = (struct master_lists *)
448 xmalloc (sizeof (struct master_lists));
449 ml->tlist = tlist;
450 p->data = (char *) ml;
451 p->delproc = masterlist_delproc;
452 (void) addnode (mtlist, p);
453 }
454 /* do tlist */
455 p = getnode ();
456 p->key = xstrdup (finfo->file);
457 p->type = UPDATE;
458 p->delproc = tag_delproc;
459 if (vers->srcfile == NULL((void*)0))
460 {
461 if (!really_quiet)
462 error (0, 0, "nothing known about %s", finfo->file);
463 freevers_ts (&vers);
464 freenode (p);
465 return (1);
466 }
467
468 /* Here we duplicate the calculation in tag_fileproc about which
469 version we are going to tag. There probably are some subtle races
470 (e.g. numtag is "foo" which gets moved between here and
471 tag_fileproc). */
472 if (!is_rtag && numtag == NULL((void*)0) && date == NULL((void*)0))
473 p->data = xstrdup (vers->vn_user);
474 else
475 p->data = RCS_getversion (vers->srcfile, numtag, date,
476 force_tag_match, NULL((void*)0));
477
478 if (p->data != NULL((void*)0))
479 {
480 int addit = 1;
481 char *oversion;
482
483 oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL((void*)0), 1,
484 (int *) NULL((void*)0));
485 if (oversion == NULL((void*)0))
486 {
487 if (delete_flag)
488 {
489 /* Deleting a tag which did not exist is a noop and
490 should not be logged. */
491 addit = 0;
492 }
493 }
494 else if (delete_flag)
495 {
496 free (p->data);
497 p->data = xstrdup (oversion);
498 }
499 else if (strcmp(oversion, p->data) == 0)
500 {
501 addit = 0;
502 }
503 else if (!force_tag_move)
504 {
505 addit = 0;
506 }
507 if (oversion != NULL((void*)0))
508 {
509 free(oversion);
510 }
511 if (!addit)
512 {
513 free(p->data);
514 p->data = NULL((void*)0);
515 }
516 }
517 freevers_ts (&vers);
518 (void) addnode (tlist, p);
519 return (0);
520}
521
522static int
523check_filesdoneproc (callerdat, err, repos, update_dir, entries)
524 void *callerdat;
525 int err;
526 char *repos;
527 char *update_dir;
528 List *entries;
529{
530 int n;
531 Node *p;
532
533 p = findnode(mtlist, update_dir);
534 if (p != NULL((void*)0))
535 {
536 tlist = ((struct master_lists *) p->data)->tlist;
537 }
538 else
539 {
540 tlist = (List *) NULL((void*)0);
541 }
542 if ((tlist == NULL((void*)0)) || (tlist->list->next == tlist->list))
543 {
544 return (err);
545 }
546 if ((n = Parse_Info(CVSROOTADM_TAGINFO"taginfo", repos, pretag_proc, 1)) > 0)
547 {
548 error (0, 0, "Pre-tag check failed");
549 err += n;
550 }
551 return (err);
552}
553
554static int
555pretag_proc(repository, filter)
556 char *repository;
557 char *filter;
558{
559 if (filter[0] == '/')
560 {
561 char *s, *cp;
562
563 s = xstrdup(filter);
564 for (cp=s; *cp; cp++)
565 {
566 if (isspace ((unsigned char) *cp))
567 {
568 *cp = '\0';
569 break;
570 }
571 }
572 if (!isfile(s))
573 {
574 error (0, errno(*__errno()), "cannot find pre-tag filter '%s'", s);
575 free(s);
576 return (1);
577 }
578 free(s);
579 }
580 run_setup (filter);
581 run_arg (symtag);
582 run_arg (delete_flag ? "del" : force_tag_move ? "mov" : "add");
583 run_arg (repository);
584 walklist(tlist, pretag_list_proc, NULL((void*)0));
585 return (run_exec (RUN_TTY(char *)0, RUN_TTY(char *)0, RUN_TTY(char *)0, RUN_NORMAL0x0000));
586}
587
588static void
589masterlist_delproc(p)
590 Node *p;
591{
592 struct master_lists *ml;
593
594 ml = (struct master_lists *)p->data;
595 dellist(&ml->tlist);
596 free(ml);
597 return;
598}
599
600static void
601tag_delproc(p)
602 Node *p;
603{
604 if (p->data != NULL((void*)0))
605 {
606 free(p->data);
607 p->data = NULL((void*)0);
608 }
609 return;
610}
611
612static int
613pretag_list_proc(p, closure)
614 Node *p;
615 void *closure;
616{
617 if (p->data != NULL((void*)0))
618 {
619 run_arg(p->key);
620 run_arg(p->data);
621 }
622 return (0);
623}
624
625
626/*
627 * Called to rtag a particular file, as appropriate with the options that were
628 * set above.
629 */
630/* ARGSUSED */
631static int
632rtag_fileproc (callerdat, finfo)
633 void *callerdat;
634 struct file_info *finfo;
635{
636 RCSNode *rcsfile;
637 char *version, *rev;
638 int retcode = 0;
639
640 /* find the parsed RCS data */
641 if ((rcsfile = finfo->rcs) == NULL((void*)0))
642 return (1);
643
644 /*
645 * For tagging an RCS file which is a symbolic link, you'd best be
646 * running with RCS 5.6, since it knows how to handle symbolic links
647 * correctly without breaking your link!
648 */
649
650 if (delete_flag)
651 return (rtag_delete (rcsfile));
652
653 /*
654 * If we get here, we are adding a tag. But, if -a was specified, we
655 * need to check to see if a -r or -D option was specified. If neither
656 * was specified and the file is in the Attic, remove the tag.
657 */
658 if (attic_too && (!numtag && !date))
659 {
660 if ((rcsfile->flags & VALID0x1) && (rcsfile->flags & INATTIC0x2))
661 return (rtag_delete (rcsfile));
662 }
663
664 version = RCS_getversion (rcsfile, numtag, date, force_tag_match,
665 (int *) NULL((void*)0));
666 if (version == NULL((void*)0))
667 {
668 /* If -a specified, clean up any old tags */
669 if (attic_too)
670 (void) rtag_delete (rcsfile);
671
672 if (!quiet && !force_tag_match)
673 {
674 error (0, 0, "cannot find tag `%s' in `%s'",
675 numtag ? numtag : "head", rcsfile->path);
676 return (1);
677 }
678 return (0);
679 }
680 if (numtag
681 && isdigit ((unsigned char) *numtag)
682 && strcmp (numtag, version) != 0)
683 {
684
685 /*
686 * We didn't find a match for the numeric tag that was specified, but
687 * that's OK. just pass the numeric tag on to rcs, to be tagged as
688 * specified. Could get here if one tried to tag "1.1.1" and there
689 * was a 1.1.1 branch with some head revision. In this case, we want
690 * the tag to reference "1.1.1" and not the revision at the head of
691 * the branch. Use a symbolic tag for that.
692 */
693 rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag;
694 retcode = RCS_settag(rcsfile, symtag, numtag);
695 if (retcode == 0)
696 RCS_rewrite (rcsfile, NULL((void*)0), NULL((void*)0));
697 }
698 else
699 {
700 char *oversion;
701
702 /*
703 * As an enhancement for the case where a tag is being re-applied to
704 * a large body of a module, make one extra call to RCS_getversion to
705 * see if the tag is already set in the RCS file. If so, check to
706 * see if it needs to be moved. If not, do nothing. This will
707 * likely save a lot of time when simply moving the tag to the
708 * "current" head revisions of a module -- which I have found to be a
709 * typical tagging operation.
710 */
711 rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
712 oversion = RCS_getversion (rcsfile, symtag, (char *) NULL((void*)0), 1,
713 (int *) NULL((void*)0));
714 if (oversion != NULL((void*)0))
715 {
716 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
717
718 /*
719 * if versions the same and neither old or new are branches don't
720 * have to do anything
721 */
722 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
723 {
724 free (oversion);
725 free (version);
726 return (0);
727 }
728
729 if (!force_tag_move)
730 {
731 /* we're NOT going to move the tag */
732 (void) printf ("W %s", finfo->fullname);
733
734 (void) printf (" : %s already exists on %s %s",
735 symtag, isbranch ? "branch" : "version",
736 oversion);
737 (void) printf (" : NOT MOVING tag to %s %s\n",
738 branch_mode ? "branch" : "version", rev);
739 free (oversion);
740 free (version);
741 return (0);
742 }
743 free (oversion);
744 }
745 retcode = RCS_settag(rcsfile, symtag, rev);
746 if (retcode == 0)
747 RCS_rewrite (rcsfile, NULL((void*)0), NULL((void*)0));
748 }
749
750 if (retcode != 0)
751 {
752 error (1, retcode == -1 ? errno(*__errno()) : 0,
753 "failed to set tag `%s' to revision `%s' in `%s'",
754 symtag, rev, rcsfile->path);
755 if (branch_mode)
756 free (rev);
757 free (version);
758 return (1);
759 }
760 if (branch_mode)
761 free (rev);
762 free (version);
763 return (0);
764}
765
766/*
767 * If -d is specified, "force_tag_match" is set, so that this call to
768 * RCS_getversion() will return a NULL version string if the symbolic
769 * tag does not exist in the RCS file.
770 *
771 * If the -r flag was used, numtag is set, and we only delete the
772 * symtag from files that have numtag.
773 *
774 * This is done here because it's MUCH faster than just blindly calling
775 * "rcs" to remove the tag... trust me.
776 */
777static int
778rtag_delete (rcsfile)
779 RCSNode *rcsfile;
780{
781 char *version;
782 int retcode;
783
784 if (numtag)
785 {
786 version = RCS_getversion (rcsfile, numtag, (char *) NULL((void*)0), 1,
787 (int *) NULL((void*)0));
788 if (version == NULL((void*)0))
789 return (0);
790 free (version);
791 }
792
793 version = RCS_getversion (rcsfile, symtag, (char *) NULL((void*)0), 1,
794 (int *) NULL((void*)0));
795 if (version == NULL((void*)0))
796 return (0);
797 free (version);
798
799 if ((retcode = RCS_deltag(rcsfile, symtag)) != 0)
800 {
801 if (!quiet)
802 error (0, retcode == -1 ? errno(*__errno()) : 0,
803 "failed to remove tag `%s' from `%s'", symtag,
804 rcsfile->path);
805 return (1);
806 }
807 RCS_rewrite (rcsfile, NULL((void*)0), NULL((void*)0));
808 return (0);
809}
810
811
812/*
813 * Called to tag a particular file (the currently checked out version is
814 * tagged with the specified tag - or the specified tag is deleted).
815 */
816/* ARGSUSED */
817static int
818tag_fileproc (callerdat, finfo)
819 void *callerdat;
820 struct file_info *finfo;
821{
822 char *version, *oversion;
823 char *nversion = NULL((void*)0);
824 char *rev;
825 Vers_TS *vers;
826 int retcode = 0;
827
828 vers = Version_TS (finfo, NULL((void*)0), NULL((void*)0), NULL((void*)0), 0, 0);
829
830 if ((numtag != NULL((void*)0)) || (date != NULL((void*)0)))
831 {
832 nversion = RCS_getversion(vers->srcfile,
833 numtag,
834 date,
835 force_tag_match,
836 (int *) NULL((void*)0));
837 if (nversion == NULL((void*)0))
838 {
839 freevers_ts (&vers);
840 return (0);
841 }
842 }
843 if (delete_flag)
844 {
845
846 /*
847 * If -d is specified, "force_tag_match" is set, so that this call to
848 * RCS_getversion() will return a NULL version string if the symbolic
849 * tag does not exist in the RCS file.
850 *
851 * This is done here because it's MUCH faster than just blindly calling
852 * "rcs" to remove the tag... trust me.
853 */
854
855 version = RCS_getversion (vers->srcfile, symtag, (char *) NULL((void*)0), 1,
856 (int *) NULL((void*)0));
857 if (version == NULL((void*)0) || vers->srcfile == NULL((void*)0))
858 {
859 freevers_ts (&vers);
860 return (0);
861 }
862 free (version);
863
864 if ((retcode = RCS_deltag(vers->srcfile, symtag)) != 0)
865 {
866 if (!quiet)
867 error (0, retcode == -1 ? errno(*__errno()) : 0,
868 "failed to remove tag %s from %s", symtag,
869 vers->srcfile->path);
870 freevers_ts (&vers);
871 return (1);
872 }
873 RCS_rewrite (vers->srcfile, NULL((void*)0), NULL((void*)0));
874
875 /* warm fuzzies */
876 if (!really_quiet)
877 {
878 cvs_output ("D ", 2);
879 cvs_output (finfo->fullname, 0);
880 cvs_output ("\n", 1);
881 }
882
883 freevers_ts (&vers);
884 return (0);
885 }
886
887 /*
888 * If we are adding a tag, we need to know which version we have checked
889 * out and we'll tag that version.
890 */
891 if (nversion == NULL((void*)0))
892 {
893 version = vers->vn_user;
894 }
895 else
896 {
897 version = nversion;
898 }
899 if (version == NULL((void*)0))
900 {
901 freevers_ts (&vers);
902 return (0);
903 }
904 else if (strcmp (version, "0") == 0)
905 {
906 if (!quiet)
907 error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file);
908 freevers_ts (&vers);
909 return (0);
910 }
911 else if (version[0] == '-')
912 {
913 if (!quiet)
914 error (0, 0, "skipping removed but un-commited file `%s'", finfo->file);
915 freevers_ts (&vers);
916 return (0);
917 }
918 else if (vers->srcfile == NULL((void*)0))
919 {
920 if (!quiet)
921 error (0, 0, "cannot find revision control file for `%s'", finfo->file);
922 freevers_ts (&vers);
923 return (0);
924 }
925
926 /*
927 * As an enhancement for the case where a tag is being re-applied to a
928 * large number of files, make one extra call to RCS_getversion to see
929 * if the tag is already set in the RCS file. If so, check to see if it
930 * needs to be moved. If not, do nothing. This will likely save a lot of
931 * time when simply moving the tag to the "current" head revisions of a
932 * module -- which I have found to be a typical tagging operation.
933 */
934 rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
935 oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL((void*)0), 1,
936 (int *) NULL((void*)0));
937 if (oversion != NULL((void*)0))
938 {
939 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
940
941 /*
942 * if versions the same and neither old or new are branches don't have
943 * to do anything
944 */
945 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
946 {
947 free (oversion);
948 if (branch_mode)
949 free (rev);
950 freevers_ts (&vers);
951 return (0);
952 }
953
954 if (!force_tag_move)
955 {
956 /* we're NOT going to move the tag */
957 cvs_output ("W ", 2);
958 cvs_output (finfo->fullname, 0);
959 cvs_output (" : ", 0);
960 cvs_output (symtag, 0);
961 cvs_output (" already exists on ", 0);
962 cvs_output (isbranch ? "branch" : "version", 0);
963 cvs_output (" ", 0);
964 cvs_output (oversion, 0);
965 cvs_output (" : NOT MOVING tag to ", 0);
966 cvs_output (branch_mode ? "branch" : "version", 0);
967 cvs_output (" ", 0);
968 cvs_output (rev, 0);
969 cvs_output ("\n", 1);
970 free (oversion);
971 if (branch_mode)
972 free (rev);
973 freevers_ts (&vers);
974 return (0);
975 }
976 free (oversion);
977 }
978
979 if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0)
980 {
981 error (1, retcode == -1 ? errno(*__errno()) : 0,
982 "failed to set tag %s to revision %s in %s",
983 symtag, rev, vers->srcfile->path);
984 if (branch_mode)
985 free (rev);
986 freevers_ts (&vers);
987 return (1);
988 }
989 if (branch_mode)
990 free (rev);
991 RCS_rewrite (vers->srcfile, NULL((void*)0), NULL((void*)0));
992
993 /* more warm fuzzies */
994 if (!really_quiet)
995 {
996 cvs_output ("T ", 2);
997 cvs_output (finfo->fullname, 0);
998 cvs_output ("\n", 1);
999 }
1000
1001 if (nversion != NULL((void*)0))
1002 {
1003 free (nversion);
1004 }
1005 freevers_ts (&vers);
1006 return (0);
1007}
1008
1009/*
1010 * Print a warm fuzzy message
1011 */
1012/* ARGSUSED */
1013static Dtype
1014tag_dirproc (callerdat, dir, repos, update_dir, entries)
1015 void *callerdat;
1016 char *dir;
1017 char *repos;
1018 char *update_dir;
1019 List *entries;
1020{
1021
1022 if (ignore_directory (update_dir))
1023 {
1024 /* print the warm fuzzy message */
1025 if (!quiet)
1026 error (0, 0, "Ignoring %s", update_dir);
1027 return R_SKIP_ALL;
1028 }
1029
1030 if (!quiet)
1031 error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir);
1032 return (R_PROCESS);
1033}
1034
1035/* Code relating to the val-tags file. Note that this file has no way
1036 of knowing when a tag has been deleted. The problem is that there
1037 is no way of knowing whether a tag still exists somewhere, when we
1038 delete it some places. Using per-directory val-tags files (in
1039 CVSREP) might be better, but that might slow down the process of
1040 verifying that a tag is correct (maybe not, for the likely cases,
1041 if carefully done), and/or be harder to implement correctly. */
1042
1043struct val_args {
1044 char *name;
1045 int found;
1046};
1047
1048static int val_fileproc PROTO ((void *callerdat, struct file_info *finfo))(void *callerdat, struct file_info *finfo);
1049
1050static int
1051val_fileproc (callerdat, finfo)
1052 void *callerdat;
1053 struct file_info *finfo;
1054{
1055 RCSNode *rcsdata;
1056 struct val_args *args = (struct val_args *)callerdat;
1057 char *tag;
1058
1059 if ((rcsdata = finfo->rcs) == NULL((void*)0))
1060 /* Not sure this can happen, after all we passed only
1061 W_REPOS | W_ATTIC. */
1062 return 0;
1063
1064 tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL((void*)0));
1065 if (tag != NULL((void*)0))
1066 {
1067 /* FIXME: should find out a way to stop the search at this point. */
1068 args->found = 1;
1069 free (tag);
1070 }
1071 return 0;
1072}
1073
1074static Dtype val_direntproc PROTO ((void *, char *, char *, char *, List *))(void *, char *, char *, char *, List *);
1075
1076static Dtype
1077val_direntproc (callerdat, dir, repository, update_dir, entries)
1078 void *callerdat;
1079 char *dir;
1080 char *repository;
1081 char *update_dir;
1082 List *entries;
1083{
1084 /* This is not quite right--it doesn't get right the case of "cvs
1085 update -d -r foobar" where foobar is a tag which exists only in
1086 files in a directory which does not exist yet, but which is
1087 about to be created. */
1088 if (isdir (dir))
1089 return R_PROCESS;
1090 return R_SKIP_ALL;
1091}
1092
1093/* Check to see whether NAME is a valid tag. If so, return. If not
1094 print an error message and exit. ARGC, ARGV, LOCAL, and AFLAG specify
1095 which files we will be operating on.
1096
1097 REPOSITORY is the repository if we need to cd into it, or NULL if
1098 we are already there, or "" if we should do a W_LOCAL recursion.
1099 Sorry for three cases, but the "" case is needed in case the
1100 working directories come from diverse parts of the repository, the
1101 NULL case avoids an unneccesary chdir, and the non-NULL, non-""
1102 case is needed for checkout, where we don't want to chdir if the
1103 tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any
1104 local directory. */
1105void
1106tag_check_valid (name, argc, argv, local, aflag, repository)
1107 char *name;
1108 int argc;
1109 char **argv;
1110 int local;
1111 int aflag;
1112 char *repository;
1113{
1114 DBM *db;
1115 char *valtags_filename;
1116 int err;
1117 int nowrite = 0;
1118 datum mytag;
1119 struct val_args the_val_args;
1120 struct saved_cwd cwd;
1121 int which;
1122
1123 /* Numeric tags require only a syntactic check. */
1124 if (isdigit ((unsigned char) name[0]))
1125 {
1126 char *p;
1127 for (p = name; *p != '\0'; ++p)
1128 {
1129 if (!(isdigit ((unsigned char) *p) || *p == '.'))
1130 error (1, 0, "\
1131Numeric tag %s contains characters other than digits and '.'", name);
1132 }
1133 return;
1134 }
1135
1136 /* Special tags are always valid. */
1137 if (strcmp (name, TAG_BASE"BASE") == 0
1138 || strcmp (name, TAG_HEAD"HEAD") == 0)
1139 return;
1140
1141 /* FIXME: This routine doesn't seem to do any locking whatsoever
1142 (and it is called from places which don't have locks in place).
1143 If two processes try to write val-tags at the same time, it would
1144 seem like we are in trouble. */
1145
1146 mytag.dptr = name;
1147 mytag.dsize = strlen (name);
1148
1149 valtags_filename = xmalloc (strlen (current_parsed_root->directory)
1150 + sizeof CVSROOTADM"CVSROOT"
1151 + sizeof CVSROOTADM_VALTAGS"val-tags" + 3);
1152 sprintf (valtags_filename, "%s/%s/%s", current_parsed_root->directory,
1153 CVSROOTADM"CVSROOT", CVSROOTADM_VALTAGS"val-tags");
1154 db = dbm_openmydbm_open (valtags_filename, O_RDWR0x0002, 0666);
1155 if (db == NULL((void*)0))
1156 {
1157 if (!existence_error (errno)(((*__errno())) == 2))
1158 {
1159 error (0, errno(*__errno()), "warning: cannot open %s read/write",
1160 valtags_filename);
1161 db = dbm_openmydbm_open (valtags_filename, O_RDONLY0x0000, 0666);
1162 if (db != NULL((void*)0))
1163 nowrite = 1;
1164 else if (!existence_error (errno)(((*__errno())) == 2))
1165 error (1, errno(*__errno()), "cannot read %s", valtags_filename);
1166 }
1167 /* If the file merely fails to exist, we just keep going and create
1168 it later if need be. */
1169 }
1170 if (db != NULL((void*)0))
1171 {
1172 datum val;
1173
1174 val = dbm_fetchmydbm_fetch (db, mytag);
1175 if (val.dptr != NULL((void*)0))
1176 {
1177 /* Found. The tag is valid. */
1178 dbm_closemydbm_close (db);
1179 free (valtags_filename);
1180 return;
1181 }
1182 /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */
1183 }
1184
1185 /* We didn't find the tag in val-tags, so look through all the RCS files
1186 to see whether it exists there. Yes, this is expensive, but there
1187 is no other way to cope with a tag which might have been created
1188 by an old version of CVS, from before val-tags was invented.
1189
1190 Since we need this code anyway, we also use it to create
1191 entries in val-tags in general (that is, the val-tags entry
1192 will get created the first time the tag is used, not when the
1193 tag is created). */
1194
1195 the_val_args.name = name;
1196 the_val_args.found = 0;
1197
1198 which = W_REPOS0x02 | W_ATTIC0x04;
1199
1200 if (repository != NULL((void*)0))
1201 {
1202 if (repository[0] == '\0')
1203 which |= W_LOCAL0x01;
1204 else
1205 {
1206 if (save_cwd (&cwd))
1207 error_exit ();
1208 if ( CVS_CHDIRchdir (repository) < 0)
1209 error (1, errno(*__errno()), "cannot change to %s directory", repository);
1210 }
1211 }
1212
1213 err = start_recursion (val_fileproc, (FILESDONEPROC) NULL((void*)0),
Value stored to 'err' is never read
1214 val_direntproc, (DIRLEAVEPROC) NULL((void*)0),
1215 (void *)&the_val_args,
1216 argc, argv, local, which, aflag,
1217 1, NULL((void*)0), 1);
1218 if (repository != NULL((void*)0) && repository[0] != '\0')
1219 {
1220 if (restore_cwd (&cwd, NULL((void*)0)))
1221 exit (EXIT_FAILURE1);
1222 free_cwd (&cwd);
1223 }
1224
1225 if (!the_val_args.found)
1226 error (1, 0, "no such tag %s", name);
1227 else
1228 {
1229 /* The tags is valid but not mentioned in val-tags. Add it. */
1230 datum value;
1231
1232 if (noexec || nowrite)
1233 {
1234 if (db != NULL((void*)0))
1235 dbm_closemydbm_close (db);
1236 free (valtags_filename);
1237 return;
1238 }
1239
1240 if (db == NULL((void*)0))
1241 {
1242 mode_t omask;
1243 omask = umask (cvsumask);
1244 db = dbm_openmydbm_open (valtags_filename, O_RDWR0x0002 | O_CREAT0x0200 | O_TRUNC0x0400, 0666);
1245 (void) umask (omask);
1246
1247 if (db == NULL((void*)0))
1248 {
1249 error (0, errno(*__errno()), "warning: cannot create %s", valtags_filename);
1250 free (valtags_filename);
1251 return;
1252 }
1253 }
1254 value.dptr = "y";
1255 value.dsize = 1;
1256 if (dbm_storemydbm_store (db, mytag, value, DBM_REPLACE1) < 0)
1257 error (0, errno(*__errno()), "cannot store %s into %s", name,
1258 valtags_filename);
1259 dbm_closemydbm_close (db);
1260 }
1261 free (valtags_filename);
1262}
1263
1264/*
1265 * Check whether a join tag is valid. This is just like
1266 * tag_check_valid, but we must stop before the colon if there is one.
1267 */
1268
1269void
1270tag_check_valid_join (join_tag, argc, argv, local, aflag, repository)
1271 char *join_tag;
1272 int argc;
1273 char **argv;
1274 int local;
1275 int aflag;
1276 char *repository;
1277{
1278 char *c, *s;
1279
1280 c = xstrdup (join_tag);
1281 s = strchr (c, ':');
1282 if (s != NULL((void*)0))
1283 {
1284 if (isdigit ((unsigned char) join_tag[0]))
1285 error (1, 0,
1286 "Numeric join tag %s may not contain a date specifier",
1287 join_tag);
1288
1289 *s = '\0';
1290 /* hmmm... I think it makes sense to allow -j:<date>, but
1291 * for now this fixes a bug where CVS just spins and spins (I
1292 * think in the RCS code) looking for a zero length tag.
1293 */
1294 if (!*c)
1295 error (1, 0,
1296 "argument to join may not contain a date specifier without a tag");
1297 }
1298
1299 tag_check_valid (c, argc, argv, local, aflag, repository);
1300
1301 free (c);
1302}