2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
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.
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.
18 static int rtag_proc (int argc, char **argv, char *xwhere,
19 char *mwhere, char *mfile, int shorten,
20 int local_specified, char *mname, char *msg);
21 static int check_fileproc (void *callerdat, struct file_info *finfo);
22 static int check_filesdoneproc (void *callerdat, int err,
23 const char *repos, const char *update_dir,
25 static int pretag_proc (const char *_repository, const char *_filter,
27 static void masterlist_delproc (Node *_p);
28 static void tag_delproc (Node *_p);
29 static int pretag_list_to_args_proc (Node *_p, void *_closure);
31 static Dtype tag_dirproc (void *callerdat, const char *dir,
32 const char *repos, const char *update_dir,
34 static int rtag_fileproc (void *callerdat, struct file_info *finfo);
35 static int rtag_delete (RCSNode *rcsfile);
36 static int tag_fileproc (void *callerdat, struct file_info *finfo);
38 static char *numtag; /* specific revision to tag */
39 static bool numtag_validated = false;
40 static char *date = NULL;
41 static char *symtag; /* tag to add or delete */
42 static bool delete_flag; /* adding a tag by default */
43 static bool branch_mode; /* make an automagic "branch" tag */
44 static bool disturb_branch_tags = false;/* allow -F,-d to disturb branch tags */
45 static bool force_tag_match = true; /* force tag to match by default */
46 static bool force_tag_move; /* don't force tag to move by default */
47 static bool check_uptodate; /* no uptodate-check by default */
48 static bool attic_too; /* remove tag from Attic files */
67 static const char rtag_opts[] = "+aBbdFflnQqRr:D:";
68 static const char *const rtag_usage[] =
70 "Usage: %s %s [-abdFflnR] [-r rev|-D date] tag modules...\n",
71 "\t-a\tClear tag from removed files that would not otherwise be tagged.\n",
72 "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
73 "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n",
74 "\t-d\tDelete the given tag.\n",
75 "\t-F\tMove tag if it already exists.\n",
76 "\t-f\tForce a head revision match if tag/date not found.\n",
77 "\t-l\tLocal directory only, not recursive.\n",
78 "\t-n\tNo execution of 'tag program'.\n",
79 "\t-R\tProcess directories recursively.\n",
80 "\t-r rev\tExisting revision/tag.\n",
81 "\t-D\tExisting date.\n",
82 "(Specify the --help global option for a list of other help options)\n",
86 static const char tag_opts[] = "+BbcdFflQqRr:D:";
87 static const char *const tag_usage[] =
89 "Usage: %s %s [-bcdFflR] [-r rev|-D date] tag [files...]\n",
90 "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
91 "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n",
92 "\t-c\tCheck that working files are unmodified.\n",
93 "\t-d\tDelete the given tag.\n",
94 "\t-F\tMove tag if it already exists.\n",
95 "\t-f\tForce a head revision match if tag/date not found.\n",
96 "\t-l\tLocal directory only, not recursive.\n",
97 "\t-R\tProcess directories recursively.\n",
98 "\t-r rev\tExisting revision/tag.\n",
99 "\t-D\tExisting date.\n",
100 "(Specify the --help global option for a list of other help options)\n",
107 cvstag (int argc, char **argv)
109 bool local = false; /* recursive by default */
112 bool run_module_prog = true;
114 is_rtag = (strcmp (cvs_cmd_name, "rtag") == 0);
117 usage (is_rtag ? rtag_usage : tag_usage);
120 while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1)
131 disturb_branch_tags = true;
134 check_uptodate = true;
140 force_tag_move = true;
143 force_tag_match = false;
149 run_module_prog = false;
153 #ifdef SERVER_SUPPORT
154 /* The CVS 1.5 client sends these options (in addition to
155 Global_option requests), so we must ignore them. */
159 "-q or -Q must be specified before \"%s\"",
171 date = Make_Date (optarg);
175 usage (is_rtag ? rtag_usage : tag_usage);
182 if (argc < (is_rtag ? 2 : 1))
183 usage (is_rtag ? rtag_usage : tag_usage);
189 error (1, 0, "-r and -D options are mutually exclusive");
190 if (delete_flag && branch_mode)
191 error (0, 0, "warning: -b ignored with -d options");
192 RCS_check_tag (symtag);
194 #ifdef CLIENT_SUPPORT
195 if (current_parsed_root->isremote)
197 /* We're the client side. Fire up the remote server. */
206 if (disturb_branch_tags)
214 if (!force_tag_match)
218 if (!run_module_prog)
222 option_with_arg ("-r", numtag);
224 client_senddate (date);
233 for (i = 0; i < argc; ++i)
235 send_to_server ("rtag\012", 0);
239 send_files (argc, argv, local, 0,
241 /* I think the -c case is like "cvs status", in
242 which we really better be correct rather than
243 being fast; it is just too confusing otherwise. */
244 check_uptodate ? 0 : SEND_NO_CONTENTS);
245 send_file_names (argc, argv, SEND_EXPAND_WILD);
246 send_to_server ("tag\012", 0);
249 return get_responses_and_close ();
258 for (i = 0; i < argc; i++)
260 /* XXX last arg should be repository, but doesn't make sense here */
261 history_write ('T', (delete_flag ? "D" : (numtag ? numtag :
262 (date ? date : "A"))), symtag, argv[i], "");
263 err += do_module (db, argv[i], TAG,
264 delete_flag ? "Untagging" : "Tagging",
265 rtag_proc, NULL, 0, local, run_module_prog,
272 err = rtag_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL,
281 struct pretag_proc_data {
289 * called from Parse_Info, this routine processes a line that came out
290 * of the posttag file and turns it into a command and executes it.
293 * the absolute value of the return value of run_exec, which may or
294 * may not be the return value of the child process. this is
295 * contrained to return positive values because Parse_Info is summing
296 * return values and testing for non-zeroness to signify one or more
297 * of its callbacks having returned an error.
300 posttag_proc (const char *repository, const char *filter, void *closure)
303 const char *srepos = Short_Repository (repository);
304 struct pretag_proc_data *ppd = closure;
306 /* %t = tag being added/moved/removed
307 * %o = operation = "add" | "mov" | "del"
308 * %b = branch mode = "?" (delete ops - unknown) | "T" (branch)
311 * %p = path from $CVSROOT
312 * %r = path from root
313 * %{sVv} = attribute list = file name, old version tag will be deleted
314 * from, new version tag will be added to (or
316 * SUPPORT_OLD_INFO_FMT_STRINGS is undefined).
318 cmdline = format_cmdline (
319 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
321 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
323 "t", "s", ppd->symtag,
324 "o", "s", ppd->delete_flag ? "del" :
325 ppd->force_tag_move ? "mov" : "add",
326 "b", "c", delete_flag ? '?' : branch_mode ? 'T' : 'N',
327 "c", "s", cvs_cmd_name,
328 #ifdef SERVER_SUPPORT
329 "R", "s", referrer ? referrer->original : "NONE",
330 #endif /* SERVER_SUPPORT */
332 "r", "s", current_parsed_root->directory,
333 "sVv", ",", ppd->tlist, pretag_list_to_args_proc, (void *)NULL,
337 if (!cmdline || !strlen (cmdline))
339 if (cmdline) free (cmdline);
340 error (0, 0, "pretag proc resolved to the empty string!");
347 return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
353 * Call any postadmin procs.
356 tag_filesdoneproc (void *callerdat, int err, const char *repository,
357 const char *update_dir, List *entries)
360 List *mtlist, *tlist;
361 struct pretag_proc_data ppd;
363 TRACE (TRACE_FUNCTION, "tag_filesdoneproc (%d, %s, %s)", err, repository,
367 p = findnode (mtlist, update_dir);
369 tlist = ((struct master_lists *) p->data)->tlist;
372 if (tlist == NULL || tlist->list->next == tlist->list)
376 ppd.delete_flag = delete_flag;
377 ppd.force_tag_move = force_tag_move;
379 Parse_Info (CVSROOTADM_POSTTAG, repository, posttag_proc,
388 * callback proc for doing the real work of tagging
392 rtag_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
393 int shorten, int local_specified, char *mname, char *msg)
395 /* Begin section which is identical to patch_proc--should this
396 be abstracted out somehow? */
403 #ifdef HAVE_PRINTF_PTR
404 TRACE (TRACE_FUNCTION,
405 "rtag_proc (argc=%d, argv=%p, xwhere=%s,\n"
406 " mwhere=%s, mfile=%s, shorten=%d,\n"
407 " local_specified=%d, mname=%s, msg=%s)",
408 argc, (void *)argv, xwhere ? xwhere : "(null)",
409 mwhere ? mwhere : "(null)", mfile ? mfile : "(null)",
410 shorten, local_specified,
411 mname ? mname : "(null)", msg ? msg : "(null)" );
413 TRACE (TRACE_FUNCTION,
414 "rtag_proc (argc=%d, argv=%lx, xwhere=%s,\n"
415 " mwhere=%s, mfile=%s, shorten=%d,\n"
416 " local_specified=%d, mname=%s, msg=%s )",
417 argc, (unsigned long)argv, xwhere ? xwhere : "(null)",
418 mwhere ? mwhere : "(null)", mfile ? mfile : "(null)",
419 shorten, local_specified,
420 mname ? mname : "(null)", msg ? msg : "(null)" );
425 repository = xmalloc (strlen (current_parsed_root->directory)
427 + (mfile == NULL ? 0 : strlen (mfile) + 1)
429 (void) sprintf (repository, "%s/%s", current_parsed_root->directory,
431 where = xmalloc (strlen (argv[0])
432 + (mfile == NULL ? 0 : strlen (mfile) + 1)
434 (void) strcpy (where, argv[0]);
436 /* If MFILE isn't null, we need to set up to do only part of the
444 /* If the portion of the module is a path, put the dir part on
447 if ((cp = strrchr (mfile, '/')) != NULL)
450 (void) strcat (repository, "/");
451 (void) strcat (repository, mfile);
452 (void) strcat (where, "/");
453 (void) strcat (where, mfile);
457 /* take care of the rest */
458 path = xmalloc (strlen (repository) + strlen (mfile) + 5);
459 (void) sprintf (path, "%s/%s", repository, mfile);
462 /* directory means repository gets the dir tacked on */
463 (void) strcpy (repository, path);
464 (void) strcat (where, "/");
465 (void) strcat (where, mfile);
477 /* cd to the starting repository */
478 if (CVS_CHDIR (repository) < 0)
480 error (0, errno, "cannot chdir to %s", repository);
484 /* End section which is identical to patch_proc. */
486 if (delete_flag || attic_too || (force_tag_match && numtag))
487 which = W_REPOS | W_ATTIC;
498 if (numtag != NULL && !numtag_validated)
500 tag_check_valid (numtag, argc - 1, argv + 1, local_specified, 0,
502 numtag_validated = true;
505 /* check to make sure they are authorized to tag all the
506 specified files in the repository */
509 err = start_recursion (check_fileproc, check_filesdoneproc,
511 argc - 1, argv + 1, local_specified, which, 0,
512 CVS_LOCK_READ, where, 1, repository);
516 error (1, 0, "correct the above errors first!");
519 /* It would be nice to provide consistency with respect to
520 commits; however CVS lacks the infrastructure to do that (see
521 Concurrency in cvs.texinfo and comment in do_recursion). */
523 /* start the recursion processor */
524 err = start_recursion
525 (is_rtag ? rtag_fileproc : tag_fileproc,
526 tag_filesdoneproc, tag_dirproc, NULL, mtlist, argc - 1, argv + 1,
527 local_specified, which, 0, CVS_LOCK_WRITE, where, 1,
530 if (which & W_REPOS) free (repository);
538 /* check file that is to be tagged */
539 /* All we do here is add it to our list */
541 check_fileproc (void *callerdat, struct file_info *finfo)
550 TRACE (TRACE_FUNCTION, "check_fileproc (%s, %s, %s)",
551 finfo->repository ? finfo->repository : "(null)",
552 finfo->fullname ? finfo->fullname : "(null)",
553 finfo->rcs ? (finfo->rcs->path ? finfo->rcs->path : "(null)")
558 switch (Classify_File (finfo, NULL, NULL, NULL, 1, 0, &vers, 0))
572 error (0, 0, "%s is locally modified", finfo->fullname);
578 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
580 if (finfo->update_dir[0] == '\0')
583 xdir = finfo->update_dir;
584 if ((p = findnode (mtlist, xdir)) != NULL)
586 tlist = ((struct master_lists *) p->data)->tlist;
590 struct master_lists *ml;
594 p->key = xstrdup (xdir);
596 ml = xmalloc (sizeof (struct master_lists));
599 p->delproc = masterlist_delproc;
600 (void) addnode (mtlist, p);
604 p->key = xstrdup (finfo->file);
606 p->delproc = tag_delproc;
607 if (vers->srcfile == NULL)
610 error (0, 0, "nothing known about %s", finfo->file);
616 /* Here we duplicate the calculation in tag_fileproc about which
617 version we are going to tag. There probably are some subtle races
618 (e.g. numtag is "foo" which gets moved between here and
620 p->data = ti = xmalloc (sizeof (struct tag_info));
621 if (!is_rtag && numtag == NULL && date == NULL)
622 ti->rev = xstrdup (vers->vn_user);
624 ti->rev = RCS_getversion (vers->srcfile, numtag, date,
625 force_tag_match, NULL);
629 ti->oldrev = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
631 if (ti->oldrev == NULL)
635 /* Deleting a tag which did not exist is a noop and
636 should not be logged. */
640 else if (delete_flag)
643 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
644 /* a hack since %v used to mean old or new rev */
645 ti->rev = xstrdup (ti->oldrev);
646 #else /* SUPPORT_OLD_INFO_FMT_STRINGS */
648 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
650 else if (strcmp(ti->oldrev, p->data) == 0)
652 else if (!force_tag_move)
663 (void)addnode (tlist, p);
670 check_filesdoneproc (void *callerdat, int err, const char *repos,
671 const char *update_dir, List *entries)
676 struct pretag_proc_data ppd;
678 p = findnode (mtlist, update_dir);
680 tlist = ((struct master_lists *) p->data)->tlist;
683 if (tlist == NULL || tlist->list->next == tlist->list)
687 ppd.delete_flag = delete_flag;
688 ppd.force_tag_move = force_tag_move;
690 if ((n = Parse_Info (CVSROOTADM_TAGINFO, repos, pretag_proc, PIOPT_ALL,
693 error (0, 0, "Pre-tag check failed");
702 * called from Parse_Info, this routine processes a line that came out
703 * of a taginfo file and turns it into a command and executes it.
706 * the absolute value of the return value of run_exec, which may or
707 * may not be the return value of the child process. this is
708 * contrained to return positive values because Parse_Info is adding up
709 * return values and testing for non-zeroness to signify one or more
710 * of its callbacks having returned an error.
713 pretag_proc (const char *repository, const char *filter, void *closure)
715 char *newfilter = NULL;
717 const char *srepos = Short_Repository (repository);
718 struct pretag_proc_data *ppd = closure;
720 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
721 if (!strchr (filter, '%'))
724 "warning: taginfo line contains no format strings:\n"
726 "Filling in old defaults ('%%t %%o %%p %%{sv}'), but please be aware that this\n"
727 "usage is deprecated.", filter);
728 newfilter = xmalloc (strlen (filter) + 16);
729 strcpy (newfilter, filter);
730 strcat (newfilter, " %t %o %p %{sv}");
733 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
735 /* %t = tag being added/moved/removed
736 * %o = operation = "add" | "mov" | "del"
737 * %b = branch mode = "?" (delete ops - unknown) | "T" (branch)
740 * %p = path from $CVSROOT
741 * %r = path from root
742 * %{sVv} = attribute list = file name, old version tag will be deleted
743 * from, new version tag will be added to (or
745 * SUPPORT_OLD_INFO_FMT_STRINGS is undefined)
747 cmdline = format_cmdline (
748 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
750 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
752 "t", "s", ppd->symtag,
753 "o", "s", ppd->delete_flag ? "del" :
754 ppd->force_tag_move ? "mov" : "add",
755 "b", "c", delete_flag ? '?' : branch_mode ? 'T' : 'N',
756 "c", "s", cvs_cmd_name,
757 #ifdef SERVER_SUPPORT
758 "R", "s", referrer ? referrer->original : "NONE",
759 #endif /* SERVER_SUPPORT */
761 "r", "s", current_parsed_root->directory,
762 "sVv", ",", ppd->tlist, pretag_list_to_args_proc, (void *) NULL,
766 if (newfilter) free (newfilter);
768 if (!cmdline || !strlen (cmdline))
770 if (cmdline) free (cmdline);
771 error (0, 0, "pretag proc resolved to the empty string!");
777 /* FIXME - the old code used to run the following here:
781 * error (0, errno, "cannot find pre-tag filter '%s'", s);
786 * not sure this is really necessary. it might give a little finer grained
787 * error than letting the execution attempt fail but i'm not sure. in any
788 * case it should be easy enough to add a function in run.c to test its
789 * first arg for fileness & executability.
793 return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
799 masterlist_delproc (Node *p)
801 struct master_lists *ml = p->data;
803 dellist (&ml->tlist);
811 tag_delproc (Node *p)
816 ti = (struct tag_info *) p->data;
817 if (ti->oldrev) free (ti->oldrev);
818 if (ti->rev) free (ti->rev);
825 /* to be passed into walklist with a list of tags
827 * p->data = struct tag_info *
828 * p->data->oldrev = rev tag will be deleted from
829 * p->data->rev = rev tag will be added to
831 * closure will be a struct format_cmdline_walklist_closure
832 * where closure is undefined
835 pretag_list_to_args_proc(Node *p, void *closure)
837 struct tag_info *taginfo = (struct tag_info *)p->data;
838 struct format_cmdline_walklist_closure *c =
839 (struct format_cmdline_walklist_closure *)closure;
845 if (p->data == NULL) return 1;
849 /* foreach requested attribute */
858 arg = taginfo->rev ? taginfo->rev : "NONE";
861 arg = taginfo->oldrev ? taginfo->oldrev : "NONE";
865 "Unknown format character or not a list attribute: %c",
869 /* copy the attribute into an argument */
872 arg = cmdlineescape (c->quotes, arg);
876 arg = cmdlinequote ('"', arg);
880 expand_string (c->buf, c->length, doff + strlen (arg));
882 strncpy (d, arg, strlen (arg));
887 /* and always put the extra space on. we'll have to back up a char when we're
888 * done, but that seems most efficient
891 expand_string (c->buf, c->length, doff + 1);
895 /* correct our original pointer into the buff */
902 * Called to rtag a particular file, as appropriate with the options that were
907 rtag_fileproc (void *callerdat, struct file_info *finfo)
910 char *version = NULL, *rev = NULL;
913 static bool valtagged = false;
915 /* find the parsed RCS data */
916 if ((rcsfile = finfo->rcs) == NULL)
919 goto free_vars_and_return;
923 * For tagging an RCS file which is a symbolic link, you'd best be
924 * running with RCS 5.6, since it knows how to handle symbolic links
925 * correctly without breaking your link!
930 retval = rtag_delete (rcsfile);
931 goto free_vars_and_return;
935 * If we get here, we are adding a tag. But, if -a was specified, we
936 * need to check to see if a -r or -D option was specified. If neither
937 * was specified and the file is in the Attic, remove the tag.
939 if (attic_too && (!numtag && !date))
941 if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
943 retval = rtag_delete (rcsfile);
944 goto free_vars_and_return;
948 version = RCS_getversion (rcsfile, numtag, date, force_tag_match, NULL);
951 /* If -a specified, clean up any old tags */
953 (void)rtag_delete (rcsfile);
955 if (!quiet && !force_tag_match)
957 error (0, 0, "cannot find tag `%s' in `%s'",
958 numtag ? numtag : "head", rcsfile->path);
961 goto free_vars_and_return;
964 && isdigit ((unsigned char)*numtag)
965 && strcmp (numtag, version) != 0)
969 * We didn't find a match for the numeric tag that was specified, but
970 * that's OK. just pass the numeric tag on to rcs, to be tagged as
971 * specified. Could get here if one tried to tag "1.1.1" and there
972 * was a 1.1.1 branch with some head revision. In this case, we want
973 * the tag to reference "1.1.1" and not the revision at the head of
974 * the branch. Use a symbolic tag for that.
976 rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag;
977 retcode = RCS_settag(rcsfile, symtag, numtag);
979 RCS_rewrite (rcsfile, NULL, NULL);
986 * As an enhancement for the case where a tag is being re-applied to
987 * a large body of a module, make one extra call to RCS_getversion to
988 * see if the tag is already set in the RCS file. If so, check to
989 * see if it needs to be moved. If not, do nothing. This will
990 * likely save a lot of time when simply moving the tag to the
991 * "current" head revisions of a module -- which I have found to be a
992 * typical tagging operation.
994 rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
995 oversion = RCS_getversion (rcsfile, symtag, NULL, 1, NULL);
996 if (oversion != NULL)
998 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
1001 * if versions the same and neither old or new are branches don't
1002 * have to do anything
1004 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
1007 goto free_vars_and_return;
1010 if (!force_tag_move)
1012 /* we're NOT going to move the tag */
1013 (void)printf ("W %s", finfo->fullname);
1015 (void)printf (" : %s already exists on %s %s",
1016 symtag, isbranch ? "branch" : "version",
1018 (void)printf (" : NOT MOVING tag to %s %s\n",
1019 branch_mode ? "branch" : "version", rev);
1021 goto free_vars_and_return;
1023 else /* force_tag_move is set and... */
1024 if ((isbranch && !disturb_branch_tags) ||
1025 (!isbranch && disturb_branch_tags))
1027 error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
1029 isbranch ? "branch" : "non-branch",
1030 symtag, oversion, rev,
1031 isbranch ? "" : " due to `-B' option");
1033 goto free_vars_and_return;
1037 retcode = RCS_settag (rcsfile, symtag, rev);
1039 RCS_rewrite (rcsfile, NULL, NULL);
1044 error (1, retcode == -1 ? errno : 0,
1045 "failed to set tag `%s' to revision `%s' in `%s'",
1046 symtag, rev, rcsfile->path);
1048 goto free_vars_and_return;
1051 free_vars_and_return:
1052 if (branch_mode && rev) free (rev);
1053 if (version) free (version);
1054 if (!delete_flag && !retval && !valtagged)
1056 tag_check_valid (symtag, 0, NULL, 0, 0, NULL, true);
1065 * If -d is specified, "force_tag_match" is set, so that this call to
1066 * RCS_getversion() will return a NULL version string if the symbolic
1067 * tag does not exist in the RCS file.
1069 * If the -r flag was used, numtag is set, and we only delete the
1070 * symtag from files that have numtag.
1072 * This is done here because it's MUCH faster than just blindly calling
1073 * "rcs" to remove the tag... trust me.
1076 rtag_delete (RCSNode *rcsfile)
1079 int retcode, isbranch;
1083 version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1,
1085 if (version == NULL)
1090 version = RCS_getversion (rcsfile, symtag, NULL, 1, NULL);
1091 if (version == NULL)
1096 isbranch = RCS_nodeisbranch (rcsfile, symtag);
1097 if ((isbranch && !disturb_branch_tags) ||
1098 (!isbranch && disturb_branch_tags))
1102 "Not removing %s tag `%s' from `%s'%s.",
1103 isbranch ? "branch" : "non-branch",
1104 symtag, rcsfile->path,
1105 isbranch ? "" : " due to `-B' option");
1109 if ((retcode = RCS_deltag(rcsfile, symtag)) != 0)
1112 error (0, retcode == -1 ? errno : 0,
1113 "failed to remove tag `%s' from `%s'", symtag,
1117 RCS_rewrite (rcsfile, NULL, NULL);
1124 * Called to tag a particular file (the currently checked out version is
1125 * tagged with the specified tag - or the specified tag is deleted).
1129 tag_fileproc (void *callerdat, struct file_info *finfo)
1131 char *version, *oversion;
1132 char *nversion = NULL;
1137 static bool valtagged = false;
1139 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
1141 if ((numtag != NULL) || (date != NULL))
1143 nversion = RCS_getversion(vers->srcfile,
1148 if (nversion == NULL)
1149 goto free_vars_and_return;
1156 * If -d is specified, "force_tag_match" is set, so that this call to
1157 * RCS_getversion() will return a NULL version string if the symbolic
1158 * tag does not exist in the RCS file.
1160 * This is done here because it's MUCH faster than just blindly calling
1161 * "rcs" to remove the tag... trust me.
1164 version = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
1165 if (version == NULL || vers->srcfile == NULL)
1166 goto free_vars_and_return;
1170 isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
1171 if ((isbranch && !disturb_branch_tags) ||
1172 (!isbranch && disturb_branch_tags))
1176 "Not removing %s tag `%s' from `%s'%s.",
1177 isbranch ? "branch" : "non-branch",
1178 symtag, vers->srcfile->path,
1179 isbranch ? "" : " due to `-B' option");
1181 goto free_vars_and_return;
1184 if ((retcode = RCS_deltag (vers->srcfile, symtag)) != 0)
1187 error (0, retcode == -1 ? errno : 0,
1188 "failed to remove tag %s from %s", symtag,
1189 vers->srcfile->path);
1191 goto free_vars_and_return;
1193 RCS_rewrite (vers->srcfile, NULL, NULL);
1198 cvs_output ("D ", 2);
1199 cvs_output (finfo->fullname, 0);
1200 cvs_output ("\n", 1);
1203 goto free_vars_and_return;
1207 * If we are adding a tag, we need to know which version we have checked
1208 * out and we'll tag that version.
1210 if (nversion == NULL)
1211 version = vers->vn_user;
1214 if (version == NULL)
1215 goto free_vars_and_return;
1216 else if (strcmp (version, "0") == 0)
1219 error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file);
1220 goto free_vars_and_return;
1222 else if (version[0] == '-')
1225 error (0, 0, "skipping removed but un-commited file `%s'",
1227 goto free_vars_and_return;
1229 else if (vers->srcfile == NULL)
1232 error (0, 0, "cannot find revision control file for `%s'",
1234 goto free_vars_and_return;
1238 * As an enhancement for the case where a tag is being re-applied to a
1239 * large number of files, make one extra call to RCS_getversion to see
1240 * if the tag is already set in the RCS file. If so, check to see if it
1241 * needs to be moved. If not, do nothing. This will likely save a lot of
1242 * time when simply moving the tag to the "current" head revisions of a
1243 * module -- which I have found to be a typical tagging operation.
1245 rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
1246 oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
1248 if (oversion != NULL)
1250 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
1253 * if versions the same and neither old or new are branches don't have
1256 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
1261 goto free_vars_and_return;
1264 if (!force_tag_move)
1266 /* we're NOT going to move the tag */
1267 cvs_output ("W ", 2);
1268 cvs_output (finfo->fullname, 0);
1269 cvs_output (" : ", 0);
1270 cvs_output (symtag, 0);
1271 cvs_output (" already exists on ", 0);
1272 cvs_output (isbranch ? "branch" : "version", 0);
1273 cvs_output (" ", 0);
1274 cvs_output (oversion, 0);
1275 cvs_output (" : NOT MOVING tag to ", 0);
1276 cvs_output (branch_mode ? "branch" : "version", 0);
1277 cvs_output (" ", 0);
1278 cvs_output (rev, 0);
1279 cvs_output ("\n", 1);
1283 goto free_vars_and_return;
1285 else /* force_tag_move == 1 and... */
1286 if ((isbranch && !disturb_branch_tags) ||
1287 (!isbranch && disturb_branch_tags))
1289 error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
1291 isbranch ? "branch" : "non-branch",
1292 symtag, oversion, rev,
1293 isbranch ? "" : " due to `-B' option");
1297 goto free_vars_and_return;
1302 if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0)
1304 error (1, retcode == -1 ? errno : 0,
1305 "failed to set tag %s to revision %s in %s",
1306 symtag, rev, vers->srcfile->path);
1310 goto free_vars_and_return;
1314 RCS_rewrite (vers->srcfile, NULL, NULL);
1316 /* more warm fuzzies */
1319 cvs_output ("T ", 2);
1320 cvs_output (finfo->fullname, 0);
1321 cvs_output ("\n", 1);
1324 free_vars_and_return:
1325 if (nversion != NULL)
1327 freevers_ts (&vers);
1328 if (!delete_flag && !retval && !valtagged)
1330 tag_check_valid (symtag, 0, NULL, 0, 0, NULL, true);
1339 * Print a warm fuzzy message
1343 tag_dirproc (void *callerdat, const char *dir, const char *repos,
1344 const char *update_dir, List *entries)
1347 if (ignore_directory (update_dir))
1349 /* print the warm fuzzy message */
1351 error (0, 0, "Ignoring %s", update_dir);
1356 error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging",
1363 /* Code relating to the val-tags file. Note that this file has no way
1364 of knowing when a tag has been deleted. The problem is that there
1365 is no way of knowing whether a tag still exists somewhere, when we
1366 delete it some places. Using per-directory val-tags files (in
1367 CVSREP) might be better, but that might slow down the process of
1368 verifying that a tag is correct (maybe not, for the likely cases,
1369 if carefully done), and/or be harder to implement correctly. */
1377 val_fileproc (void *callerdat, struct file_info *finfo)
1380 struct val_args *args = callerdat;
1383 if ((rcsdata = finfo->rcs) == NULL)
1384 /* Not sure this can happen, after all we passed only
1385 W_REPOS | W_ATTIC. */
1388 tag = RCS_gettag (rcsdata, args->name, 1, NULL);
1391 /* FIXME: should find out a way to stop the search at this point. */
1401 val_direntproc (void *callerdat, const char *dir, const char *repository,
1402 const char *update_dir, List *entries)
1404 /* This is not quite right--it doesn't get right the case of "cvs
1405 update -d -r foobar" where foobar is a tag which exists only in
1406 files in a directory which does not exist yet, but which is
1407 about to be created. */
1415 /* With VALID set, insert NAME into val-tags if it is not already present
1418 * Without VALID set, check to see whether NAME is a valid tag. If so, return.
1419 * If not print an error message and exit.
1423 * ARGC, ARGV, LOCAL, and AFLAG specify which files we will be operating on.
1425 * REPOSITORY is the repository if we need to cd into it, or NULL if
1426 * we are already there, or "" if we should do a W_LOCAL recursion.
1427 * Sorry for three cases, but the "" case is needed in case the
1428 * working directories come from diverse parts of the repository, the
1429 * NULL case avoids an unneccesary chdir, and the non-NULL, non-""
1430 * case is needed for checkout, where we don't want to chdir if the
1431 * tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any
1435 * Errors may be encountered opening and accessing the DBM file. Write
1436 * errors generate warnings and read errors are fatal. When !VALID and NAME
1437 * is not in val-tags, errors may also be generated as per start_recursion.
1438 * When !VALID, non-existance of tags both in val-tags and in the archive
1439 * files also causes a fatal error.
1445 tag_check_valid (char *name, int argc, char **argv, int local, int aflag,
1446 char *repository, bool valid)
1449 char *valtags_filename;
1452 struct val_args the_val_args;
1453 struct saved_cwd cwd;
1456 #ifdef HAVE_PRINTF_PTR
1457 TRACE (TRACE_FUNCTION,
1458 "tag_check_valid (name=%s, argc=%d, argv=%p, local=%d,\n"
1459 " aflag=%d, repository=%s, valid=%s)",
1460 name ? name : "(name)", argc, (void *)argv, local, aflag,
1461 repository ? repository : "(null)",
1462 valid ? "true" : "false");
1464 TRACE (TRACE_FUNCTION,
1465 "tag_check_valid (name=%s, argc=%d, argv=%lx, local=%d,\n"
1466 " aflag=%d, repository=%s, valid=%s)",
1467 name ? name : "(name)", argc, (unsigned long)argv, local, aflag,
1468 repository ? repository : "(null)",
1469 valid ? "true" : "false");
1472 /* Numeric tags require only a syntactic check. */
1473 if (isdigit ((unsigned char) name[0]))
1475 /* insert is not possible for numeric revisions */
1477 if (RCS_valid_rev (name)) return;
1480 Numeric tag %s invalid. Numeric tags should be of the form X[.X]...", name);
1483 /* Special tags are always valid. */
1484 if (strcmp (name, TAG_BASE) == 0
1485 || strcmp (name, TAG_HEAD) == 0)
1487 /* insert is not possible for numeric revisions */
1492 /* Verify that the tag is valid syntactically. Some later code once made
1493 * assumptions about this.
1495 RCS_check_tag (name);
1497 /* FIXME: This routine doesn't seem to do any locking whatsoever
1498 (and it is called from places which don't have locks in place).
1499 If two processes try to write val-tags at the same time, it would
1500 seem like we are in trouble. */
1503 mytag.dsize = strlen (name);
1505 valtags_filename = xmalloc (strlen (current_parsed_root->directory)
1507 + sizeof CVSROOTADM_VALTAGS + 3);
1508 sprintf (valtags_filename, "%s/%s/%s", current_parsed_root->directory,
1509 CVSROOTADM, CVSROOTADM_VALTAGS);
1510 db = dbm_open (valtags_filename, O_RDWR, 0666);
1513 if (!existence_error (errno))
1515 error (0, errno, "warning: cannot open %s read/write",
1517 db = dbm_open (valtags_filename, O_RDONLY, 0666);
1520 else if (!existence_error (errno))
1521 error (1, errno, "cannot read %s", valtags_filename);
1523 /* If the file merely fails to exist, we just keep going and create
1524 it later if need be. */
1528 val = dbm_fetch (db, mytag);
1531 /* The tag is already in val-tags - return valid and don't insert
1535 free (valtags_filename);
1538 /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */
1543 /* We didn't find the tag in val-tags, so look through all the RCS files
1544 * to see whether it exists there. Yes, this is expensive, but there
1545 * is no other way to cope with a tag which might have been created
1546 * by an old version of CVS, from before val-tags was invented
1549 the_val_args.name = name;
1550 the_val_args.found = 0;
1551 which = W_REPOS | W_ATTIC;
1553 if (repository == NULL || repository[0] == '\0')
1557 if (save_cwd (&cwd))
1558 error (1, errno, "Failed to save current directory.");
1559 if (CVS_CHDIR (repository) < 0)
1560 error (1, errno, "cannot change to %s directory", repository);
1564 (val_fileproc, NULL, val_direntproc, NULL,
1565 &the_val_args, argc, argv, local, which, aflag,
1566 CVS_LOCK_READ, NULL, 1, repository);
1567 if (repository != NULL && repository[0] != '\0')
1569 if (restore_cwd (&cwd))
1570 error (1, errno, "Failed to restore current directory, `%s'.",
1575 if (!the_val_args.found)
1576 error (1, 0, "no such tag `%s'", name);
1579 /* The tags is valid but not mentioned in val-tags. Add it. */
1580 if (noexec || nowrite)
1584 free (valtags_filename);
1591 omask = umask (cvsumask);
1592 db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
1593 (void)umask (omask);
1597 error (0, errno, "warning: cannot create %s", valtags_filename);
1598 free (valtags_filename);
1604 if (dbm_store (db, mytag, val, DBM_REPLACE) < 0)
1605 error (0, errno, "cannot store %s into %s", name,
1608 free (valtags_filename);
1614 * Check whether a join tag is valid. This is just like
1615 * tag_check_valid, but we must stop before the colon if there is one.
1618 tag_check_valid_join (char *join_tag, int argc, char **argv, int local,
1619 int aflag, char *repository)
1623 c = xstrdup (join_tag);
1624 s = strchr (c, ':');
1627 if (isdigit ((unsigned char) join_tag[0]))
1629 "Numeric join tag %s may not contain a date specifier",
1633 /* hmmm... I think it makes sense to allow -j:<date>, but
1634 * for now this fixes a bug where CVS just spins and spins (I
1635 * think in the RCS code) looking for a zero length tag.
1639 "argument to join may not contain a date specifier without a tag");
1642 tag_check_valid (c, argc, argv, local, aflag, repository, false);