2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (C) 1989-1992, Brian Berliner
10 * You may distribute under the terms of the GNU General Public License as
11 * specified in the README file that comes with the CVS source distribution.
15 * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
16 * Tag uses the checked out revision in the current directory, rtag uses
17 * the modules database, if necessary.
23 static int rtag_proc (int argc, char **argv, char *xwhere,
24 char *mwhere, char *mfile, int shorten,
25 int local_specified, char *mname, char *msg);
26 static int check_fileproc (void *callerdat, struct file_info *finfo);
27 static int check_filesdoneproc (void *callerdat, int err,
28 const char *repos, const char *update_dir,
30 static int pretag_proc (const char *_repository, const char *_filter,
32 static void masterlist_delproc (Node *_p);
33 static void tag_delproc (Node *_p);
34 static int pretag_list_to_args_proc (Node *_p, void *_closure);
36 static Dtype tag_dirproc (void *callerdat, const char *dir,
37 const char *repos, const char *update_dir,
39 static int rtag_fileproc (void *callerdat, struct file_info *finfo);
40 static int rtag_delete (RCSNode *rcsfile);
41 static int tag_fileproc (void *callerdat, struct file_info *finfo);
43 static char *numtag; /* specific revision to tag */
44 static bool numtag_validated = false;
45 static char *date = NULL;
46 static char *symtag; /* tag to add or delete */
47 static bool delete_flag; /* adding a tag by default */
48 static bool branch_mode; /* make an automagic "branch" tag */
49 static bool disturb_branch_tags = false;/* allow -F,-d to disturb branch tags */
50 static bool force_tag_match = true; /* force tag to match by default */
51 static bool force_tag_move; /* don't force tag to move by default */
52 static bool check_uptodate; /* no uptodate-check by default */
53 static bool attic_too; /* remove tag from Attic files */
72 static const char rtag_opts[] = "+aBbdFflnQqRr:D:";
73 static const char *const rtag_usage[] =
75 "Usage: %s %s [-abdFflnR] [-r rev|-D date] tag modules...\n",
76 "\t-a\tClear tag from removed files that would not otherwise be tagged.\n",
77 "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
78 "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n",
79 "\t-d\tDelete the given tag.\n",
80 "\t-F\tMove tag if it already exists.\n",
81 "\t-f\tForce a head revision match if tag/date not found.\n",
82 "\t-l\tLocal directory only, not recursive.\n",
83 "\t-n\tNo execution of 'tag program'.\n",
84 "\t-R\tProcess directories recursively.\n",
85 "\t-r rev\tExisting revision/tag.\n",
86 "\t-D\tExisting date.\n",
87 "(Specify the --help global option for a list of other help options)\n",
91 static const char tag_opts[] = "+BbcdFflQqRr:D:";
92 static const char *const tag_usage[] =
94 "Usage: %s %s [-bcdFflR] [-r rev|-D date] tag [files...]\n",
95 "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
96 "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n",
97 "\t-c\tCheck that working files are unmodified.\n",
98 "\t-d\tDelete the given tag.\n",
99 "\t-F\tMove tag if it already exists.\n",
100 "\t-f\tForce a head revision match if tag/date not found.\n",
101 "\t-l\tLocal directory only, not recursive.\n",
102 "\t-R\tProcess directories recursively.\n",
103 "\t-r rev\tExisting revision/tag.\n",
104 "\t-D\tExisting date.\n",
105 "(Specify the --help global option for a list of other help options)\n",
112 cvstag (int argc, char **argv)
114 bool local = false; /* recursive by default */
117 bool run_module_prog = true;
119 is_rtag = (strcmp (cvs_cmd_name, "rtag") == 0);
122 usage (is_rtag ? rtag_usage : tag_usage);
125 while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1)
136 disturb_branch_tags = true;
139 check_uptodate = true;
145 force_tag_move = true;
148 force_tag_match = false;
154 run_module_prog = false;
158 /* The CVS 1.5 client sends these options (in addition to
159 Global_option requests), so we must ignore them. */
162 "-q or -Q must be specified before \"%s\"",
169 parse_tagdate (&numtag, &date, optarg);
172 if (date) free (date);
173 date = Make_Date (optarg);
177 usage (is_rtag ? rtag_usage : tag_usage);
184 if (argc < (is_rtag ? 2 : 1))
185 usage (is_rtag ? rtag_usage : tag_usage);
190 if (date && delete_flag)
191 error (1, 0, "-d makes no sense with a date specification.");
192 if (delete_flag && branch_mode)
193 error (0, 0, "warning: -b ignored with -d options");
194 RCS_check_tag (symtag);
196 #ifdef CLIENT_SUPPORT
197 if (current_parsed_root->isremote)
199 /* We're the client side. Fire up the remote server. */
208 if (disturb_branch_tags)
216 if (!force_tag_match)
220 if (!run_module_prog)
224 option_with_arg ("-r", numtag);
226 client_senddate (date);
235 for (i = 0; i < argc; ++i)
237 send_to_server ("rtag\012", 0);
241 send_files (argc, argv, local, 0,
243 /* I think the -c case is like "cvs status", in
244 which we really better be correct rather than
245 being fast; it is just too confusing otherwise. */
246 check_uptodate ? 0 : SEND_NO_CONTENTS);
247 send_file_names (argc, argv, SEND_EXPAND_WILD);
248 send_to_server ("tag\012", 0);
251 return get_responses_and_close ();
260 for (i = 0; i < argc; i++)
262 /* XXX last arg should be repository, but doesn't make sense here */
263 history_write ('T', (delete_flag ? "D" : (numtag ? numtag :
264 (date ? date : "A"))), symtag, argv[i], "");
265 err += do_module (db, argv[i], TAG,
266 delete_flag ? "Untagging" : "Tagging",
267 rtag_proc, NULL, 0, local, run_module_prog,
274 err = rtag_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL,
283 struct pretag_proc_data {
291 * called from Parse_Info, this routine processes a line that came out
292 * of the posttag file and turns it into a command and executes it.
295 * the absolute value of the return value of run_exec, which may or
296 * may not be the return value of the child process. this is
297 * contrained to return positive values because Parse_Info is summing
298 * return values and testing for non-zeroness to signify one or more
299 * of its callbacks having returned an error.
302 posttag_proc (const char *repository, const char *filter, void *closure)
305 const char *srepos = Short_Repository (repository);
306 struct pretag_proc_data *ppd = closure;
308 /* %t = tag being added/moved/removed
309 * %o = operation = "add" | "mov" | "del"
310 * %b = branch mode = "?" (delete ops - unknown) | "T" (branch)
314 * %p = path from $CVSROOT
315 * %r = path from root
316 * %{sVv} = attribute list = file name, old version tag will be deleted
317 * from, new version tag will be added to (or
319 * SUPPORT_OLD_INFO_FMT_STRINGS is undefined).
322 * Cast any NULL arguments as appropriate pointers as this is an
323 * stdarg function and we need to be certain the caller gets what
326 cmdline = format_cmdline (
327 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
329 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
331 "t", "s", ppd->symtag,
332 "o", "s", ppd->delete_flag
333 ? "del" : ppd->force_tag_move ? "mov" : "add",
334 "b", "c", delete_flag
335 ? '?' : branch_mode ? 'T' : 'N',
336 "c", "s", cvs_cmd_name,
337 "I", "s", global_session_id,
338 #ifdef SERVER_SUPPORT
339 "R", "s", referrer ? referrer->original : "NONE",
340 #endif /* SERVER_SUPPORT */
342 "r", "s", current_parsed_root->directory,
343 "sVv", ",", ppd->tlist,
344 pretag_list_to_args_proc, (void *) NULL,
347 if (!cmdline || !strlen (cmdline))
349 if (cmdline) free (cmdline);
350 error (0, 0, "pretag proc resolved to the empty string!");
357 return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
363 * Call any postadmin procs.
366 tag_filesdoneproc (void *callerdat, int err, const char *repository,
367 const char *update_dir, List *entries)
370 List *mtlist, *tlist;
371 struct pretag_proc_data ppd;
373 TRACE (TRACE_FUNCTION, "tag_filesdoneproc (%d, %s, %s)", err, repository,
377 p = findnode (mtlist, update_dir);
379 tlist = ((struct master_lists *) p->data)->tlist;
382 if (tlist == NULL || tlist->list->next == tlist->list)
386 ppd.delete_flag = delete_flag;
387 ppd.force_tag_move = force_tag_move;
389 Parse_Info (CVSROOTADM_POSTTAG, repository, posttag_proc,
398 * callback proc for doing the real work of tagging
402 rtag_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
403 int shorten, int local_specified, char *mname, char *msg)
405 /* Begin section which is identical to patch_proc--should this
406 be abstracted out somehow? */
413 #ifdef HAVE_PRINTF_PTR
414 TRACE (TRACE_FUNCTION,
415 "rtag_proc (argc=%d, argv=%p, xwhere=%s,\n"
416 " mwhere=%s, mfile=%s, shorten=%d,\n"
417 " local_specified=%d, mname=%s, msg=%s)",
418 argc, (void *)argv, xwhere ? xwhere : "(null)",
419 mwhere ? mwhere : "(null)", mfile ? mfile : "(null)",
420 shorten, local_specified,
421 mname ? mname : "(null)", msg ? msg : "(null)" );
423 TRACE (TRACE_FUNCTION,
424 "rtag_proc (argc=%d, argv=%lx, xwhere=%s,\n"
425 " mwhere=%s, mfile=%s, shorten=%d,\n"
426 " local_specified=%d, mname=%s, msg=%s )",
427 argc, (unsigned long)argv, xwhere ? xwhere : "(null)",
428 mwhere ? mwhere : "(null)", mfile ? mfile : "(null)",
429 shorten, local_specified,
430 mname ? mname : "(null)", msg ? msg : "(null)" );
435 repository = xmalloc (strlen (current_parsed_root->directory)
437 + (mfile == NULL ? 0 : strlen (mfile) + 1)
439 (void) sprintf (repository, "%s/%s", current_parsed_root->directory,
441 where = xmalloc (strlen (argv[0])
442 + (mfile == NULL ? 0 : strlen (mfile) + 1)
444 (void) strcpy (where, argv[0]);
446 /* If MFILE isn't null, we need to set up to do only part of the
454 /* If the portion of the module is a path, put the dir part on
457 if ((cp = strrchr (mfile, '/')) != NULL)
460 (void) strcat (repository, "/");
461 (void) strcat (repository, mfile);
462 (void) strcat (where, "/");
463 (void) strcat (where, mfile);
467 /* take care of the rest */
468 path = xmalloc (strlen (repository) + strlen (mfile) + 5);
469 (void) sprintf (path, "%s/%s", repository, mfile);
472 /* directory means repository gets the dir tacked on */
473 (void) strcpy (repository, path);
474 (void) strcat (where, "/");
475 (void) strcat (where, mfile);
487 /* cd to the starting repository */
488 if (CVS_CHDIR (repository) < 0)
490 error (0, errno, "cannot chdir to %s", repository);
495 /* End section which is identical to patch_proc. */
497 if (delete_flag || attic_too || (force_tag_match && numtag))
498 which = W_REPOS | W_ATTIC;
509 if (numtag != NULL && !numtag_validated)
511 tag_check_valid (numtag, argc - 1, argv + 1, local_specified, 0,
513 numtag_validated = true;
516 /* check to make sure they are authorized to tag all the
517 specified files in the repository */
520 err = start_recursion (check_fileproc, check_filesdoneproc,
522 argc - 1, argv + 1, local_specified, which, 0,
523 CVS_LOCK_READ, where, 1, repository);
527 error (1, 0, "correct the above errors first!");
530 /* It would be nice to provide consistency with respect to
531 commits; however CVS lacks the infrastructure to do that (see
532 Concurrency in cvs.texinfo and comment in do_recursion). */
534 /* start the recursion processor */
535 err = start_recursion
536 (is_rtag ? rtag_fileproc : tag_fileproc,
537 tag_filesdoneproc, tag_dirproc, NULL, mtlist, argc - 1, argv + 1,
538 local_specified, which, 0, CVS_LOCK_WRITE, where, 1,
541 if (which & W_REPOS) free (repository);
549 /* check file that is to be tagged */
550 /* All we do here is add it to our list */
552 check_fileproc (void *callerdat, struct file_info *finfo)
561 TRACE (TRACE_FUNCTION, "check_fileproc (%s, %s, %s)",
562 finfo->repository ? finfo->repository : "(null)",
563 finfo->fullname ? finfo->fullname : "(null)",
564 finfo->rcs ? (finfo->rcs->path ? finfo->rcs->path : "(null)")
569 switch (Classify_File (finfo, NULL, NULL, NULL, 1, 0, &vers, 0))
583 error (0, 0, "%s is locally modified", finfo->fullname);
589 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
591 if (finfo->update_dir[0] == '\0')
594 xdir = finfo->update_dir;
595 if ((p = findnode (mtlist, xdir)) != NULL)
597 tlist = ((struct master_lists *) p->data)->tlist;
601 struct master_lists *ml;
605 p->key = xstrdup (xdir);
607 ml = xmalloc (sizeof (struct master_lists));
610 p->delproc = masterlist_delproc;
611 (void) addnode (mtlist, p);
615 p->key = xstrdup (finfo->file);
617 p->delproc = tag_delproc;
618 if (vers->srcfile == NULL)
621 error (0, 0, "nothing known about %s", finfo->file);
627 /* Here we duplicate the calculation in tag_fileproc about which
628 version we are going to tag. There probably are some subtle races
629 (e.g. numtag is "foo" which gets moved between here and
631 p->data = ti = xmalloc (sizeof (struct tag_info));
632 ti->tag = xstrdup (numtag ? numtag : vers->tag);
633 if (!is_rtag && numtag == NULL && date == NULL)
634 ti->rev = xstrdup (vers->vn_user);
636 ti->rev = RCS_getversion (vers->srcfile, numtag, date,
637 force_tag_match, NULL);
641 ti->oldrev = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
643 if (ti->oldrev == NULL)
647 /* Deleting a tag which did not exist is a noop and
648 should not be logged. */
652 else if (delete_flag)
655 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
656 /* a hack since %v used to mean old or new rev */
657 ti->rev = xstrdup (ti->oldrev);
658 #else /* SUPPORT_OLD_INFO_FMT_STRINGS */
660 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
662 else if (strcmp(ti->oldrev, p->data) == 0)
664 else if (!force_tag_move)
675 (void)addnode (tlist, p);
682 check_filesdoneproc (void *callerdat, int err, const char *repos,
683 const char *update_dir, List *entries)
688 struct pretag_proc_data ppd;
690 p = findnode (mtlist, update_dir);
692 tlist = ((struct master_lists *) p->data)->tlist;
695 if (tlist == NULL || tlist->list->next == tlist->list)
699 ppd.delete_flag = delete_flag;
700 ppd.force_tag_move = force_tag_move;
702 if ((n = Parse_Info (CVSROOTADM_TAGINFO, repos, pretag_proc, PIOPT_ALL,
705 error (0, 0, "Pre-tag check failed");
714 * called from Parse_Info, this routine processes a line that came out
715 * of a taginfo file and turns it into a command and executes it.
718 * the absolute value of the return value of run_exec, which may or
719 * may not be the return value of the child process. this is
720 * contrained to return positive values because Parse_Info is adding up
721 * return values and testing for non-zeroness to signify one or more
722 * of its callbacks having returned an error.
725 pretag_proc (const char *repository, const char *filter, void *closure)
727 char *newfilter = NULL;
729 const char *srepos = Short_Repository (repository);
730 struct pretag_proc_data *ppd = closure;
732 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
733 if (!strchr (filter, '%'))
736 "warning: taginfo line contains no format strings:\n"
738 "Filling in old defaults ('%%t %%o %%p %%{sv}'), but please be aware that this\n"
739 "usage is deprecated.", filter);
740 newfilter = xmalloc (strlen (filter) + 16);
741 strcpy (newfilter, filter);
742 strcat (newfilter, " %t %o %p %{sv}");
745 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
747 /* %t = tag being added/moved/removed
748 * %o = operation = "add" | "mov" | "del"
749 * %b = branch mode = "?" (delete ops - unknown) | "T" (branch)
753 * %p = path from $CVSROOT
754 * %r = path from root
755 * %{sVv} = attribute list = file name, old version tag will be deleted
756 * from, new version tag will be added to (or
758 * SUPPORT_OLD_INFO_FMT_STRINGS is undefined)
761 * Cast any NULL arguments as appropriate pointers as this is an
762 * stdarg function and we need to be certain the caller gets what
765 cmdline = format_cmdline (
766 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
768 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
770 "t", "s", ppd->symtag,
771 "o", "s", ppd->delete_flag ? "del" :
772 ppd->force_tag_move ? "mov" : "add",
773 "b", "c", delete_flag
774 ? '?' : branch_mode ? 'T' : 'N',
775 "c", "s", cvs_cmd_name,
776 "I", "s", global_session_id,
777 #ifdef SERVER_SUPPORT
778 "R", "s", referrer ? referrer->original : "NONE",
779 #endif /* SERVER_SUPPORT */
781 "r", "s", current_parsed_root->directory,
782 "sVv", ",", ppd->tlist,
783 pretag_list_to_args_proc, (void *) NULL,
786 if (newfilter) free (newfilter);
788 if (!cmdline || !strlen (cmdline))
790 if (cmdline) free (cmdline);
791 error (0, 0, "pretag proc resolved to the empty string!");
797 /* FIXME - the old code used to run the following here:
801 * error (0, errno, "cannot find pre-tag filter '%s'", s);
806 * not sure this is really necessary. it might give a little finer grained
807 * error than letting the execution attempt fail but i'm not sure. in any
808 * case it should be easy enough to add a function in run.c to test its
809 * first arg for fileness & executability.
813 return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
819 masterlist_delproc (Node *p)
821 struct master_lists *ml = p->data;
823 dellist (&ml->tlist);
831 tag_delproc (Node *p)
836 ti = (struct tag_info *) p->data;
837 if (ti->oldrev) free (ti->oldrev);
838 if (ti->rev) free (ti->rev);
848 /* to be passed into walklist with a list of tags
850 * p->data = struct tag_info *
851 * p->data->oldrev = rev tag will be deleted from
852 * p->data->rev = rev tag will be added to
853 * p->data->tag = tag oldrev is attached to, if any
855 * closure will be a struct format_cmdline_walklist_closure
856 * where closure is undefined
859 pretag_list_to_args_proc (Node *p, void *closure)
861 struct tag_info *taginfo = (struct tag_info *)p->data;
862 struct format_cmdline_walklist_closure *c =
863 (struct format_cmdline_walklist_closure *)closure;
869 if (!p->data) return 1;
873 /* foreach requested attribute */
882 arg = taginfo->tag ? taginfo->tag : "";
885 arg = taginfo->rev ? taginfo->rev : "NONE";
888 arg = taginfo->oldrev ? taginfo->oldrev : "NONE";
892 "Unknown format character or not a list attribute: %c",
896 /* copy the attribute into an argument */
899 arg = cmdlineescape (c->quotes, arg);
903 arg = cmdlinequote ('"', arg);
907 expand_string (c->buf, c->length, doff + strlen (arg));
909 strncpy (d, arg, strlen (arg));
914 /* and always put the extra space on. we'll have to back up a char when we're
915 * done, but that seems most efficient
918 expand_string (c->buf, c->length, doff + 1);
922 /* correct our original pointer into the buff */
929 * Called to rtag a particular file, as appropriate with the options that were
934 rtag_fileproc (void *callerdat, struct file_info *finfo)
937 char *version = NULL, *rev = NULL;
940 static bool valtagged = false;
942 /* find the parsed RCS data */
943 if ((rcsfile = finfo->rcs) == NULL)
946 goto free_vars_and_return;
950 * For tagging an RCS file which is a symbolic link, you'd best be
951 * running with RCS 5.6, since it knows how to handle symbolic links
952 * correctly without breaking your link!
957 retval = rtag_delete (rcsfile);
958 goto free_vars_and_return;
962 * If we get here, we are adding a tag. But, if -a was specified, we
963 * need to check to see if a -r or -D option was specified. If neither
964 * was specified and the file is in the Attic, remove the tag.
966 if (attic_too && (!numtag && !date))
968 if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
970 retval = rtag_delete (rcsfile);
971 goto free_vars_and_return;
975 version = RCS_getversion (rcsfile, numtag, date, force_tag_match, NULL);
978 /* If -a specified, clean up any old tags */
980 (void)rtag_delete (rcsfile);
982 if (!quiet && !force_tag_match)
984 error (0, 0, "cannot find tag `%s' in `%s'",
985 numtag ? numtag : "head", rcsfile->path);
988 goto free_vars_and_return;
991 && isdigit ((unsigned char)*numtag)
992 && strcmp (numtag, version) != 0)
996 * We didn't find a match for the numeric tag that was specified, but
997 * that's OK. just pass the numeric tag on to rcs, to be tagged as
998 * specified. Could get here if one tried to tag "1.1.1" and there
999 * was a 1.1.1 branch with some head revision. In this case, we want
1000 * the tag to reference "1.1.1" and not the revision at the head of
1001 * the branch. Use a symbolic tag for that.
1003 rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag;
1004 retcode = RCS_settag(rcsfile, symtag, numtag);
1006 RCS_rewrite (rcsfile, NULL, NULL);
1013 * As an enhancement for the case where a tag is being re-applied to
1014 * a large body of a module, make one extra call to RCS_getversion to
1015 * see if the tag is already set in the RCS file. If so, check to
1016 * see if it needs to be moved. If not, do nothing. This will
1017 * likely save a lot of time when simply moving the tag to the
1018 * "current" head revisions of a module -- which I have found to be a
1019 * typical tagging operation.
1021 rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
1022 oversion = RCS_getversion (rcsfile, symtag, NULL, 1, NULL);
1023 if (oversion != NULL)
1025 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
1028 * if versions the same and neither old or new are branches don't
1029 * have to do anything
1031 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
1034 goto free_vars_and_return;
1037 if (!force_tag_move)
1039 /* we're NOT going to move the tag */
1040 (void)printf ("W %s", finfo->fullname);
1042 (void)printf (" : %s already exists on %s %s",
1043 symtag, isbranch ? "branch" : "version",
1045 (void)printf (" : NOT MOVING tag to %s %s\n",
1046 branch_mode ? "branch" : "version", rev);
1048 goto free_vars_and_return;
1050 else /* force_tag_move is set and... */
1051 if ((isbranch && !disturb_branch_tags) ||
1052 (!isbranch && disturb_branch_tags))
1054 error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
1056 isbranch ? "branch" : "non-branch",
1057 symtag, oversion, rev,
1058 isbranch ? "" : " due to `-B' option");
1060 goto free_vars_and_return;
1064 retcode = RCS_settag (rcsfile, symtag, rev);
1066 RCS_rewrite (rcsfile, NULL, NULL);
1071 error (1, retcode == -1 ? errno : 0,
1072 "failed to set tag `%s' to revision `%s' in `%s'",
1073 symtag, rev, rcsfile->path);
1075 goto free_vars_and_return;
1078 free_vars_and_return:
1079 if (branch_mode && rev) free (rev);
1080 if (version) free (version);
1081 if (!delete_flag && !retval && !valtagged)
1083 tag_check_valid (symtag, 0, NULL, 0, 0, NULL, true);
1092 * If -d is specified, "force_tag_match" is set, so that this call to
1093 * RCS_getversion() will return a NULL version string if the symbolic
1094 * tag does not exist in the RCS file.
1096 * If the -r flag was used, numtag is set, and we only delete the
1097 * symtag from files that have numtag.
1099 * This is done here because it's MUCH faster than just blindly calling
1100 * "rcs" to remove the tag... trust me.
1103 rtag_delete (RCSNode *rcsfile)
1106 int retcode, isbranch;
1110 version = RCS_getversion (rcsfile, numtag, NULL, 1, NULL);
1111 if (version == NULL)
1116 version = RCS_getversion (rcsfile, symtag, NULL, 1, NULL);
1117 if (version == NULL)
1122 isbranch = RCS_nodeisbranch (rcsfile, symtag);
1123 if ((isbranch && !disturb_branch_tags) ||
1124 (!isbranch && disturb_branch_tags))
1128 "Not removing %s tag `%s' from `%s'%s.",
1129 isbranch ? "branch" : "non-branch",
1130 symtag, rcsfile->path,
1131 isbranch ? "" : " due to `-B' option");
1135 if ((retcode = RCS_deltag(rcsfile, symtag)) != 0)
1138 error (0, retcode == -1 ? errno : 0,
1139 "failed to remove tag `%s' from `%s'", symtag,
1143 RCS_rewrite (rcsfile, NULL, NULL);
1150 * Called to tag a particular file (the currently checked out version is
1151 * tagged with the specified tag - or the specified tag is deleted).
1155 tag_fileproc (void *callerdat, struct file_info *finfo)
1157 char *version, *oversion;
1158 char *nversion = NULL;
1163 static bool valtagged = false;
1165 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
1169 nversion = RCS_getversion (vers->srcfile, numtag, date,
1170 force_tag_match, NULL);
1172 goto free_vars_and_return;
1179 * If -d is specified, "force_tag_match" is set, so that this call to
1180 * RCS_getversion() will return a NULL version string if the symbolic
1181 * tag does not exist in the RCS file.
1183 * This is done here because it's MUCH faster than just blindly calling
1184 * "rcs" to remove the tag... trust me.
1187 version = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
1188 if (version == NULL || vers->srcfile == NULL)
1189 goto free_vars_and_return;
1193 isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
1194 if ((isbranch && !disturb_branch_tags) ||
1195 (!isbranch && disturb_branch_tags))
1199 "Not removing %s tag `%s' from `%s'%s.",
1200 isbranch ? "branch" : "non-branch",
1201 symtag, vers->srcfile->path,
1202 isbranch ? "" : " due to `-B' option");
1204 goto free_vars_and_return;
1207 if ((retcode = RCS_deltag (vers->srcfile, symtag)) != 0)
1210 error (0, retcode == -1 ? errno : 0,
1211 "failed to remove tag %s from %s", symtag,
1212 vers->srcfile->path);
1214 goto free_vars_and_return;
1216 RCS_rewrite (vers->srcfile, NULL, NULL);
1221 cvs_output ("D ", 2);
1222 cvs_output (finfo->fullname, 0);
1223 cvs_output ("\n", 1);
1226 goto free_vars_and_return;
1230 * If we are adding a tag, we need to know which version we have checked
1231 * out and we'll tag that version.
1234 version = vers->vn_user;
1238 goto free_vars_and_return;
1239 else if (strcmp (version, "0") == 0)
1242 error (0, 0, "couldn't tag added but un-committed file `%s'",
1244 goto free_vars_and_return;
1246 else if (version[0] == '-')
1249 error (0, 0, "skipping removed but un-committed file `%s'",
1251 goto free_vars_and_return;
1253 else if (vers->srcfile == NULL)
1256 error (0, 0, "cannot find revision control file for `%s'",
1258 goto free_vars_and_return;
1262 * As an enhancement for the case where a tag is being re-applied to a
1263 * large number of files, make one extra call to RCS_getversion to see
1264 * if the tag is already set in the RCS file. If so, check to see if it
1265 * needs to be moved. If not, do nothing. This will likely save a lot of
1266 * time when simply moving the tag to the "current" head revisions of a
1267 * module -- which I have found to be a typical tagging operation.
1269 rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
1270 oversion = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
1271 if (oversion != NULL)
1273 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
1276 * if versions the same and neither old or new are branches don't have
1279 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
1284 goto free_vars_and_return;
1287 if (!force_tag_move)
1289 /* we're NOT going to move the tag */
1290 cvs_output ("W ", 2);
1291 cvs_output (finfo->fullname, 0);
1292 cvs_output (" : ", 0);
1293 cvs_output (symtag, 0);
1294 cvs_output (" already exists on ", 0);
1295 cvs_output (isbranch ? "branch" : "version", 0);
1296 cvs_output (" ", 0);
1297 cvs_output (oversion, 0);
1298 cvs_output (" : NOT MOVING tag to ", 0);
1299 cvs_output (branch_mode ? "branch" : "version", 0);
1300 cvs_output (" ", 0);
1301 cvs_output (rev, 0);
1302 cvs_output ("\n", 1);
1306 goto free_vars_and_return;
1308 else /* force_tag_move == 1 and... */
1309 if ((isbranch && !disturb_branch_tags) ||
1310 (!isbranch && disturb_branch_tags))
1312 error (0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
1314 isbranch ? "branch" : "non-branch",
1315 symtag, oversion, rev,
1316 isbranch ? "" : " due to `-B' option");
1320 goto free_vars_and_return;
1325 if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0)
1327 error (1, retcode == -1 ? errno : 0,
1328 "failed to set tag %s to revision %s in %s",
1329 symtag, rev, vers->srcfile->path);
1333 goto free_vars_and_return;
1337 RCS_rewrite (vers->srcfile, NULL, NULL);
1339 /* more warm fuzzies */
1342 cvs_output ("T ", 2);
1343 cvs_output (finfo->fullname, 0);
1344 cvs_output ("\n", 1);
1347 free_vars_and_return:
1348 if (nversion != NULL)
1350 freevers_ts (&vers);
1351 if (!delete_flag && !retval && !valtagged)
1353 tag_check_valid (symtag, 0, NULL, 0, 0, NULL, true);
1362 * Print a warm fuzzy message
1366 tag_dirproc (void *callerdat, const char *dir, const char *repos,
1367 const char *update_dir, List *entries)
1370 if (ignore_directory (update_dir))
1372 /* print the warm fuzzy message */
1374 error (0, 0, "Ignoring %s", update_dir);
1379 error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging",
1386 /* Code relating to the val-tags file. Note that this file has no way
1387 of knowing when a tag has been deleted. The problem is that there
1388 is no way of knowing whether a tag still exists somewhere, when we
1389 delete it some places. Using per-directory val-tags files (in
1390 CVSREP) might be better, but that might slow down the process of
1391 verifying that a tag is correct (maybe not, for the likely cases,
1392 if carefully done), and/or be harder to implement correctly. */
1400 val_fileproc (void *callerdat, struct file_info *finfo)
1403 struct val_args *args = callerdat;
1406 if ((rcsdata = finfo->rcs) == NULL)
1407 /* Not sure this can happen, after all we passed only
1408 W_REPOS | W_ATTIC. */
1411 tag = RCS_gettag (rcsdata, args->name, 1, NULL);
1414 /* FIXME: should find out a way to stop the search at this point. */
1423 /* This routine determines whether a tag appears in CVSROOT/val-tags.
1425 * The val-tags file will be open read-only when IDB is NULL. Since writes to
1426 * val-tags always append to it, the lack of locking is okay. The worst case
1427 * race condition might misinterpret a partially written "foobar" matched, for
1428 * instance, a request for "f", "foo", of "foob". Such a mismatch would be
1429 * caught harmlessly later.
1431 * Before CVS adds a tag to val-tags, it will lock val-tags for write and
1432 * verify that the tag is still not present to avoid adding it twice.
1435 * This function expects its parent to handle any necessary locking of the
1439 * idb When this value is NULL, the val-tags file is opened in
1440 * in read-only mode. When present, the val-tags file is opened
1441 * in read-write mode and the DBM handle is stored in *IDB.
1442 * name The tag to search for.
1445 * *idb The val-tags file opened for read/write, or NULL if it couldn't
1449 * Exits with an error message if the val-tags file cannot be opened for
1450 * read (failure to open val-tags read/write is harmless - see below).
1453 * true 1. If NAME exists in val-tags.
1454 * 2. If IDB is non-NULL and val-tags cannot be opened for write.
1455 * This allows callers to ignore the harmless inability to
1456 * update the val-tags cache.
1457 * 3. If CVSREADONLYFS is set (same as #2 above).
1458 * false If the file could be opened and the tag is not present.
1460 static int is_in_val_tags (DBM **idb, const char *name)
1463 char *valtags_filename;
1467 /* do nothing if we know we fail anyway */
1471 /* Casting out const should be safe here - input datums are not
1472 * written to by the myndbm functions.
1474 mytag.dptr = (char *)name;
1475 mytag.dsize = strlen (name);
1477 valtags_filename = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
1478 CVSROOTADM, CVSROOTADM_VALTAGS);
1484 omask = umask (cvsumask);
1485 db = dbm_open (valtags_filename, O_RDWR | O_CREAT, 0666);
1491 error (0, errno, "warning: cannot open `%s' read/write",
1501 db = dbm_open (valtags_filename, O_RDONLY, 0444);
1502 if (!db && !existence_error (errno))
1503 error (1, errno, "cannot read %s", valtags_filename);
1506 /* If the file merely fails to exist, we just keep going and create
1507 it later if need be. */
1514 val = dbm_fetch (db, mytag);
1515 if (val.dptr != NULL)
1516 /* Found. The tag is valid. */
1519 /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */
1521 if (!idb) dbm_close (db);
1524 free (valtags_filename);
1530 /* Add a tag to the CVSROOT/val-tags cache. Establishes a write lock and
1531 * reverifies that the tag does not exist before adding it.
1533 static void add_to_val_tags (const char *name)
1541 val_tags_lock (current_parsed_root->directory);
1543 /* Check for presence again since we have a lock now. */
1544 if (is_in_val_tags (&db, name)) return;
1546 /* Casting out const should be safe here - input datums are not
1547 * written to by the myndbm functions.
1549 mytag.dptr = (char *)name;
1550 mytag.dsize = strlen (name);
1554 if (dbm_store (db, mytag, value, DBM_REPLACE) < 0)
1555 error (0, errno, "failed to store %s into val-tags", name);
1558 clear_val_tags_lock ();
1564 val_direntproc (void *callerdat, const char *dir, const char *repository,
1565 const char *update_dir, List *entries)
1567 /* This is not quite right--it doesn't get right the case of "cvs
1568 update -d -r foobar" where foobar is a tag which exists only in
1569 files in a directory which does not exist yet, but which is
1570 about to be created. */
1578 /* With VALID set, insert NAME into val-tags if it is not already present
1581 * Without VALID set, check to see whether NAME is a valid tag. If so, return.
1582 * If not print an error message and exit.
1586 * ARGC, ARGV, LOCAL, and AFLAG specify which files we will be operating on.
1588 * REPOSITORY is the repository if we need to cd into it, or NULL if
1589 * we are already there, or "" if we should do a W_LOCAL recursion.
1590 * Sorry for three cases, but the "" case is needed in case the
1591 * working directories come from diverse parts of the repository, the
1592 * NULL case avoids an unneccesary chdir, and the non-NULL, non-""
1593 * case is needed for checkout, where we don't want to chdir if the
1594 * tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any
1598 * Errors may be encountered opening and accessing the DBM file. Write
1599 * errors generate warnings and read errors are fatal. When !VALID and NAME
1600 * is not in val-tags, errors may also be generated as per start_recursion.
1601 * When !VALID, non-existance of tags both in val-tags and in the archive
1602 * files also causes a fatal error.
1608 tag_check_valid (const char *name, int argc, char **argv, int local, int aflag,
1609 char *repository, bool valid)
1611 struct val_args the_val_args;
1612 struct saved_cwd cwd;
1615 #ifdef HAVE_PRINTF_PTR
1616 TRACE (TRACE_FUNCTION,
1617 "tag_check_valid (name=%s, argc=%d, argv=%p, local=%d,\n"
1618 " aflag=%d, repository=%s, valid=%s)",
1619 name ? name : "(name)", argc, (void *)argv, local, aflag,
1620 repository ? repository : "(null)",
1621 valid ? "true" : "false");
1623 TRACE (TRACE_FUNCTION,
1624 "tag_check_valid (name=%s, argc=%d, argv=%lx, local=%d,\n"
1625 " aflag=%d, repository=%s, valid=%s)",
1626 name ? name : "(name)", argc, (unsigned long)argv, local, aflag,
1627 repository ? repository : "(null)",
1628 valid ? "true" : "false");
1631 /* Numeric tags require only a syntactic check. */
1632 if (isdigit ((unsigned char) name[0]))
1634 /* insert is not possible for numeric revisions */
1636 if (RCS_valid_rev (name)) return;
1639 Numeric tag %s invalid. Numeric tags should be of the form X[.X]...", name);
1642 /* Special tags are always valid. */
1643 if (strcmp (name, TAG_BASE) == 0
1644 || strcmp (name, TAG_HEAD) == 0)
1646 /* insert is not possible for numeric revisions */
1651 /* Verify that the tag is valid syntactically. Some later code once made
1652 * assumptions about this.
1654 RCS_check_tag (name);
1656 if (is_in_val_tags (NULL, name)) return;
1660 /* We didn't find the tag in val-tags, so look through all the RCS files
1661 * to see whether it exists there. Yes, this is expensive, but there
1662 * is no other way to cope with a tag which might have been created
1663 * by an old version of CVS, from before val-tags was invented
1666 the_val_args.name = name;
1667 the_val_args.found = 0;
1668 which = W_REPOS | W_ATTIC;
1670 if (repository == NULL || repository[0] == '\0')
1674 if (save_cwd (&cwd))
1675 error (1, errno, "Failed to save current directory.");
1676 if (CVS_CHDIR (repository) < 0)
1677 error (1, errno, "cannot change to %s directory", repository);
1681 (val_fileproc, NULL, val_direntproc, NULL,
1682 &the_val_args, argc, argv, local, which, aflag,
1683 CVS_LOCK_READ, NULL, 1, repository);
1684 if (repository != NULL && repository[0] != '\0')
1686 if (restore_cwd (&cwd))
1687 error (1, errno, "Failed to restore current directory, `%s'.",
1692 if (!the_val_args.found)
1693 error (1, 0, "no such tag `%s'", name);
1696 /* The tags is valid but not mentioned in val-tags. Add it. */
1697 add_to_val_tags (name);