aboutsummaryrefslogtreecommitdiff
path: root/contrib/cvs/src/tag.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cvs/src/tag.c')
-rw-r--r--contrib/cvs/src/tag.c1465
1 files changed, 0 insertions, 1465 deletions
diff --git a/contrib/cvs/src/tag.c b/contrib/cvs/src/tag.c
deleted file mode 100644
index 43451ce32939..000000000000
--- a/contrib/cvs/src/tag.c
+++ /dev/null
@@ -1,1465 +0,0 @@
-/*
- * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
- *
- * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
- * and others.
- *
- * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
- * Portions Copyright (C) 1989-1992, Brian Berliner
- *
- * You may distribute under the terms of the GNU General Public License as
- * specified in the README file that comes with the CVS source distribution.
- *
- * Tag and Rtag
- *
- * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
- * Tag uses the checked out revision in the current directory, rtag uses
- * the modules database, if necessary.
- *
- * $FreeBSD$
- */
-
-#include "cvs.h"
-#include "savecwd.h"
-
-static int rtag_proc PROTO((int argc, char **argv, char *xwhere,
- char *mwhere, char *mfile, int shorten,
- int local_specified, char *mname, char *msg));
-static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo));
-static int check_filesdoneproc PROTO ((void *callerdat, int err,
- const char *repos,
- const char *update_dir,
- List *entries));
-static int pretag_proc PROTO((const char *repository, const char *filter));
-static void masterlist_delproc PROTO((Node *p));
-static void tag_delproc PROTO((Node *p));
-static int pretag_list_proc PROTO((Node *p, void *closure));
-
-static Dtype tag_dirproc PROTO ((void *callerdat, const char *dir,
- const char *repos, const char *update_dir,
- List *entries));
-static int rtag_fileproc PROTO ((void *callerdat, struct file_info *finfo));
-static int rtag_delete PROTO((RCSNode *rcsfile));
-static int tag_fileproc PROTO ((void *callerdat, struct file_info *finfo));
-
-static char *numtag; /* specific revision to tag */
-static int numtag_validated = 0;
-static char *date = NULL;
-static char *symtag; /* tag to add or delete */
-static int delete_flag; /* adding a tag by default */
-static int branch_mode; /* make an automagic "branch" tag */
-static int disturb_branch_tags = 0; /* allow -F,-d to disturb branch tags */
-static int force_tag_match = 1; /* force tag to match by default */
-static int force_tag_move; /* don't force tag to move by default */
-static int check_uptodate; /* no uptodate-check by default */
-static int attic_too; /* remove tag from Attic files */
-static int is_rtag;
-
-struct tag_info
-{
- Ctype status;
- char *rev;
- char *tag;
- char *options;
-};
-
-struct master_lists
-{
- List *tlist;
-};
-
-static List *mtlist;
-static List *tlist;
-
-static const char rtag_opts[] = "+aBbdFflnQqRr:D:";
-static const char *const rtag_usage[] =
-{
- "Usage: %s %s [-abdFflnR] [-r rev|-D date] tag modules...\n",
- "\t-a\tClear tag from removed files that would not otherwise be tagged.\n",
- "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
- "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n",
- "\t-d\tDelete the given tag.\n",
- "\t-F\tMove tag if it already exists.\n",
- "\t-f\tForce a head revision match if tag/date not found.\n",
- "\t-l\tLocal directory only, not recursive.\n",
- "\t-n\tNo execution of 'tag program'.\n",
- "\t-R\tProcess directories recursively.\n",
- "\t-r rev\tExisting revision/tag.\n",
- "\t-D\tExisting date.\n",
- "(Specify the --help global option for a list of other help options)\n",
- NULL
-};
-
-static const char tag_opts[] = "+BbcdFflQqRr:D:";
-static const char *const tag_usage[] =
-{
- "Usage: %s %s [-bcdFflR] [-r rev|-D date] tag [files...]\n",
- "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
- "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n",
- "\t-c\tCheck that working files are unmodified.\n",
- "\t-d\tDelete the given tag.\n",
- "\t-F\tMove tag if it already exists.\n",
- "\t-f\tForce a head revision match if tag/date not found.\n",
- "\t-l\tLocal directory only, not recursive.\n",
- "\t-R\tProcess directories recursively.\n",
- "\t-r rev\tExisting revision/tag.\n",
- "\t-D\tExisting date.\n",
- "(Specify the --help global option for a list of other help options)\n",
- NULL
-};
-
-int
-cvstag (argc, argv)
- int argc;
- char **argv;
-{
- int local = 0; /* recursive by default */
- int c;
- int err = 0;
- int run_module_prog = 1;
-
- is_rtag = (strcmp (cvs_cmd_name, "rtag") == 0);
-
- if (argc == -1)
- usage (is_rtag ? rtag_usage : tag_usage);
-
- optind = 0;
- while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1)
- {
- switch (c)
- {
- case 'a':
- attic_too = 1;
- break;
- case 'b':
- branch_mode = 1;
- break;
- case 'B':
- disturb_branch_tags = 1;
- break;
- case 'c':
- check_uptodate = 1;
- break;
- case 'd':
- delete_flag = 1;
- break;
- case 'F':
- force_tag_move = 1;
- break;
- case 'f':
- force_tag_match = 0;
- break;
- case 'l':
- local = 1;
- break;
- case 'n':
- run_module_prog = 0;
- break;
- case 'Q':
- case 'q':
- /* The CVS 1.5 client sends these options (in addition to
- Global_option requests), so we must ignore them. */
- if (!server_active)
- error (1, 0,
- "-q or -Q must be specified before \"%s\"",
- cvs_cmd_name);
- break;
- case 'R':
- local = 0;
- break;
- case 'r':
- numtag = optarg;
- break;
- case 'D':
- if (date)
- free (date);
- date = Make_Date (optarg);
- break;
- case '?':
- default:
- usage (is_rtag ? rtag_usage : tag_usage);
- break;
- }
- }
- argc -= optind;
- argv += optind;
-
- if (argc < (is_rtag ? 2 : 1))
- usage (is_rtag ? rtag_usage : tag_usage);
- symtag = argv[0];
- argc--;
- argv++;
-
- if (date && numtag)
- error (1, 0, "-r and -D options are mutually exclusive");
- if (delete_flag && branch_mode)
- error (0, 0, "warning: -b ignored with -d options");
- RCS_check_tag (symtag);
-
-#ifdef CLIENT_SUPPORT
- if (current_parsed_root->isremote)
- {
- /* We're the client side. Fire up the remote server. */
- start_server ();
-
- ign_setup ();
-
- if (attic_too)
- send_arg("-a");
- if (branch_mode)
- send_arg("-b");
- if (disturb_branch_tags)
- send_arg("-B");
- if (check_uptodate)
- send_arg("-c");
- if (delete_flag)
- send_arg("-d");
- if (force_tag_move)
- send_arg("-F");
- if (!force_tag_match)
- send_arg ("-f");
- if (local)
- send_arg("-l");
- if (!run_module_prog)
- send_arg("-n");
-
- if (numtag)
- option_with_arg ("-r", numtag);
- if (date)
- client_senddate (date);
-
- send_arg ("--");
-
- send_arg (symtag);
-
- if (is_rtag)
- {
- int i;
- for (i = 0; i < argc; ++i)
- send_arg (argv[i]);
- send_to_server ("rtag\012", 0);
- }
- else
- {
- send_files (argc, argv, local, 0,
-
- /* I think the -c case is like "cvs status", in
- which we really better be correct rather than
- being fast; it is just too confusing otherwise. */
- check_uptodate ? 0 : SEND_NO_CONTENTS);
- send_file_names (argc, argv, SEND_EXPAND_WILD);
- send_to_server ("tag\012", 0);
- }
-
- return get_responses_and_close ();
- }
-#endif
-
- if (is_rtag)
- {
- DBM *db;
- int i;
- db = open_module ();
- for (i = 0; i < argc; i++)
- {
- /* XXX last arg should be repository, but doesn't make sense here */
- history_write ('T', (delete_flag ? "D" : (numtag ? numtag :
- (date ? date : "A"))), symtag, argv[i], "");
- err += do_module (db, argv[i], TAG,
- delete_flag ? "Untagging" : "Tagging",
- rtag_proc, (char *) NULL, 0, local, run_module_prog,
- 0, symtag);
- }
- close_module (db);
- }
- else
- {
- err = rtag_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL,
- NULL);
- }
-
- return (err);
-}
-
-/*
- * callback proc for doing the real work of tagging
- */
-/* ARGSUSED */
-static int
-rtag_proc (argc, argv, xwhere, mwhere, mfile, shorten, local_specified,
- mname, msg)
- int argc;
- char **argv;
- char *xwhere;
- char *mwhere;
- char *mfile;
- int shorten;
- int local_specified;
- char *mname;
- char *msg;
-{
- /* Begin section which is identical to patch_proc--should this
- be abstracted out somehow? */
- char *myargv[2];
- int err = 0;
- int which;
- char *repository;
- char *where;
-
- if (is_rtag)
- {
- repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0])
- + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
- (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
- where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1)
- + 1);
- (void) strcpy (where, argv[0]);
-
- /* if mfile isn't null, we need to set up to do only part of the module */
- if (mfile != NULL)
- {
- char *cp;
- char *path;
-
- /* if the portion of the module is a path, put the dir part on repos */
- if ((cp = strrchr (mfile, '/')) != NULL)
- {
- *cp = '\0';
- (void) strcat (repository, "/");
- (void) strcat (repository, mfile);
- (void) strcat (where, "/");
- (void) strcat (where, mfile);
- mfile = cp + 1;
- }
-
- /* take care of the rest */
- path = xmalloc (strlen (repository) + strlen (mfile) + 5);
- (void) sprintf (path, "%s/%s", repository, mfile);
- if (isdir (path))
- {
- /* directory means repository gets the dir tacked on */
- (void) strcpy (repository, path);
- (void) strcat (where, "/");
- (void) strcat (where, mfile);
- }
- else
- {
- myargv[0] = argv[0];
- myargv[1] = mfile;
- argc = 2;
- argv = myargv;
- }
- free (path);
- }
-
- /* cd to the starting repository */
- if ( CVS_CHDIR (repository) < 0)
- {
- error (0, errno, "cannot chdir to %s", repository);
- free (repository);
- free (where);
- return (1);
- }
- /* End section which is identical to patch_proc. */
-
- if (delete_flag || force_tag_move || attic_too || numtag)
- which = W_REPOS | W_ATTIC;
- else
- which = W_REPOS;
- }
- else
- {
- where = NULL;
- which = W_LOCAL;
- repository = "";
- }
-
- if (numtag != NULL && !numtag_validated)
- {
- tag_check_valid (numtag, argc - 1, argv + 1, local_specified, 0, repository);
- numtag_validated = 1;
- }
-
- /* check to make sure they are authorized to tag all the
- specified files in the repository */
-
- mtlist = getlist();
- err = start_recursion (check_fileproc, check_filesdoneproc,
- (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
- argc - 1, argv + 1, local_specified, which, 0,
- CVS_LOCK_READ, where, 1, repository);
-
- if (err)
- {
- error (1, 0, "correct the above errors first!");
- }
-
- /* It would be nice to provide consistency with respect to
- commits; however CVS lacks the infrastructure to do that (see
- Concurrency in cvs.texinfo and comment in do_recursion). */
-
- /* start the recursion processor */
- err = start_recursion (is_rtag ? rtag_fileproc : tag_fileproc,
- (FILESDONEPROC) NULL, tag_dirproc,
- (DIRLEAVEPROC) NULL, NULL, argc - 1, argv + 1,
- local_specified, which, 0, CVS_LOCK_WRITE, where, 1,
- repository);
- if ( which & W_REPOS ) free ( repository );
- dellist (&mtlist);
- if (where != NULL)
- free (where);
- return (err);
-}
-
-/* check file that is to be tagged */
-/* All we do here is add it to our list */
-
-static int
-check_fileproc (callerdat, finfo)
- void *callerdat;
- struct file_info *finfo;
-{
- const char *xdir;
- Node *p;
- Vers_TS *vers;
-
- if (check_uptodate)
- {
- switch (Classify_File (finfo, (char *) NULL, (char *) NULL,
- (char *) NULL, 1, 0, &vers, 0))
- {
- case T_UPTODATE:
- case T_CHECKOUT:
- case T_PATCH:
- case T_REMOVE_ENTRY:
- break;
- case T_UNKNOWN:
- case T_CONFLICT:
- case T_NEEDS_MERGE:
- case T_MODIFIED:
- case T_ADDED:
- case T_REMOVED:
- default:
- error (0, 0, "%s is locally modified", finfo->fullname);
- freevers_ts (&vers);
- return (1);
- }
- }
- else
- vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
-
- if (finfo->update_dir[0] == '\0')
- xdir = ".";
- else
- xdir = finfo->update_dir;
- if ((p = findnode (mtlist, xdir)) != NULL)
- {
- tlist = ((struct master_lists *) p->data)->tlist;
- }
- else
- {
- struct master_lists *ml;
-
- tlist = getlist ();
- p = getnode ();
- p->key = xstrdup (xdir);
- p->type = UPDATE;
- ml = (struct master_lists *)
- xmalloc (sizeof (struct master_lists));
- ml->tlist = tlist;
- p->data = ml;
- p->delproc = masterlist_delproc;
- (void) addnode (mtlist, p);
- }
- /* do tlist */
- p = getnode ();
- p->key = xstrdup (finfo->file);
- p->type = UPDATE;
- p->delproc = tag_delproc;
- if (vers->srcfile == NULL)
- {
- if (!really_quiet)
- error (0, 0, "nothing known about %s", finfo->file);
- freevers_ts (&vers);
- freenode (p);
- return (1);
- }
-
- /* Here we duplicate the calculation in tag_fileproc about which
- version we are going to tag. There probably are some subtle races
- (e.g. numtag is "foo" which gets moved between here and
- tag_fileproc). */
- if (!is_rtag && numtag == NULL && date == NULL)
- p->data = xstrdup (vers->vn_user);
- else
- p->data = RCS_getversion (vers->srcfile, numtag, date,
- force_tag_match, NULL);
-
- if (p->data != NULL)
- {
- int addit = 1;
- char *oversion;
-
- oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
- (int *) NULL);
- if (oversion == NULL)
- {
- if (delete_flag)
- {
- /* Deleting a tag which did not exist is a noop and
- should not be logged. */
- addit = 0;
- }
- }
- else if (delete_flag)
- {
- free (p->data);
- p->data = xstrdup (oversion);
- }
- else if (strcmp(oversion, p->data) == 0)
- {
- addit = 0;
- }
- else if (!force_tag_move)
- {
- addit = 0;
- }
- if (oversion != NULL)
- {
- free(oversion);
- }
- if (!addit)
- {
- free(p->data);
- p->data = NULL;
- }
- }
- freevers_ts (&vers);
- (void) addnode (tlist, p);
- return (0);
-}
-
-static int
-check_filesdoneproc (callerdat, err, repos, update_dir, entries)
- void *callerdat;
- int err;
- const char *repos;
- const char *update_dir;
- List *entries;
-{
- int n;
- Node *p;
-
- p = findnode(mtlist, update_dir);
- if (p != NULL)
- {
- tlist = ((struct master_lists *) p->data)->tlist;
- }
- else
- {
- tlist = (List *) NULL;
- }
- if ((tlist == NULL) || (tlist->list->next == tlist->list))
- {
- return (err);
- }
- if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0)
- {
- error (0, 0, "Pre-tag check failed");
- err += n;
- }
- return (err);
-}
-
-static int
-pretag_proc (repository, filter)
- const char *repository;
- const char *filter;
-{
- if (filter[0] == '/')
- {
- char *s, *cp;
-
- s = xstrdup(filter);
- for (cp=s; *cp; cp++)
- {
- if (isspace ((unsigned char) *cp))
- {
- *cp = '\0';
- break;
- }
- }
- if (!isfile(s))
- {
- error (0, errno, "cannot find pre-tag filter '%s'", s);
- free(s);
- return (1);
- }
- free(s);
- }
- run_setup (filter);
- run_arg (symtag);
- run_arg (delete_flag ? "del" : force_tag_move ? "mov" : "add");
- run_arg (repository);
- walklist(tlist, pretag_list_proc, NULL);
- return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
-}
-
-static void
-masterlist_delproc(p)
- Node *p;
-{
- struct master_lists *ml = p->data;
-
- dellist(&ml->tlist);
- free(ml);
- return;
-}
-
-static void
-tag_delproc(p)
- Node *p;
-{
- if (p->data != NULL)
- {
- free(p->data);
- p->data = NULL;
- }
- return;
-}
-
-static int
-pretag_list_proc(p, closure)
- Node *p;
- void *closure;
-{
- if (p->data != NULL)
- {
- run_arg(p->key);
- run_arg(p->data);
- }
- return (0);
-}
-
-
-/*
- * Called to rtag a particular file, as appropriate with the options that were
- * set above.
- */
-/* ARGSUSED */
-static int
-rtag_fileproc (callerdat, finfo)
- void *callerdat;
- struct file_info *finfo;
-{
- RCSNode *rcsfile;
- char *version, *rev;
- int retcode = 0;
-
- /* find the parsed RCS data */
- if ((rcsfile = finfo->rcs) == NULL)
- return (1);
-
- /*
- * For tagging an RCS file which is a symbolic link, you'd best be
- * running with RCS 5.6, since it knows how to handle symbolic links
- * correctly without breaking your link!
- */
-
- if (delete_flag)
- return (rtag_delete (rcsfile));
-
- /*
- * If we get here, we are adding a tag. But, if -a was specified, we
- * need to check to see if a -r or -D option was specified. If neither
- * was specified and the file is in the Attic, remove the tag.
- */
- if (attic_too && (!numtag && !date))
- {
- if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
- return (rtag_delete (rcsfile));
- }
-
- version = RCS_getversion (rcsfile, numtag, date, force_tag_match,
- (int *) NULL);
- if (version == NULL)
- {
- /* If -a specified, clean up any old tags */
- if (attic_too)
- (void) rtag_delete (rcsfile);
-
- if (!quiet && !force_tag_match)
- {
- error (0, 0, "cannot find tag `%s' in `%s'",
- numtag ? numtag : "head", rcsfile->path);
- return (1);
- }
- return (0);
- }
- if (numtag
- && isdigit ((unsigned char) *numtag)
- && strcmp (numtag, version) != 0)
- {
-
- /*
- * We didn't find a match for the numeric tag that was specified, but
- * that's OK. just pass the numeric tag on to rcs, to be tagged as
- * specified. Could get here if one tried to tag "1.1.1" and there
- * was a 1.1.1 branch with some head revision. In this case, we want
- * the tag to reference "1.1.1" and not the revision at the head of
- * the branch. Use a symbolic tag for that.
- */
- rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag;
- retcode = RCS_settag(rcsfile, symtag, numtag);
- if (retcode == 0)
- RCS_rewrite (rcsfile, NULL, NULL);
- }
- else
- {
- char *oversion;
-
- /*
- * As an enhancement for the case where a tag is being re-applied to
- * a large body of a module, make one extra call to RCS_getversion to
- * see if the tag is already set in the RCS file. If so, check to
- * see if it needs to be moved. If not, do nothing. This will
- * likely save a lot of time when simply moving the tag to the
- * "current" head revisions of a module -- which I have found to be a
- * typical tagging operation.
- */
- rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
- oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1,
- (int *) NULL);
- if (oversion != NULL)
- {
- int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
-
- /*
- * if versions the same and neither old or new are branches don't
- * have to do anything
- */
- if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
- {
- free (oversion);
- free (version);
- return (0);
- }
-
- if (!force_tag_move)
- {
- /* we're NOT going to move the tag */
- (void) printf ("W %s", finfo->fullname);
-
- (void) printf (" : %s already exists on %s %s",
- symtag, isbranch ? "branch" : "version",
- oversion);
- (void) printf (" : NOT MOVING tag to %s %s\n",
- branch_mode ? "branch" : "version", rev);
- free (oversion);
- free (version);
- if (branch_mode) free(rev);
- return (0);
- }
- else /* force_tag_move is set and... */
- if ((isbranch && !disturb_branch_tags) ||
- (!isbranch && disturb_branch_tags))
- {
- error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
- finfo->fullname,
- isbranch ? "branch" : "non-branch",
- symtag, oversion, rev,
- isbranch ? "" : " due to `-B' option");
- if (branch_mode) free(rev);
- free (oversion);
- free (version);
- return (0);
- }
- free (oversion);
- }
- retcode = RCS_settag(rcsfile, symtag, rev);
- if (retcode == 0)
- RCS_rewrite (rcsfile, NULL, NULL);
- }
-
- if (retcode != 0)
- {
- error (1, retcode == -1 ? errno : 0,
- "failed to set tag `%s' to revision `%s' in `%s'",
- symtag, rev, rcsfile->path);
- if (branch_mode)
- free (rev);
- free (version);
- return (1);
- }
- if (branch_mode)
- free (rev);
- free (version);
- return (0);
-}
-
-/*
- * If -d is specified, "force_tag_match" is set, so that this call to
- * RCS_getversion() will return a NULL version string if the symbolic
- * tag does not exist in the RCS file.
- *
- * If the -r flag was used, numtag is set, and we only delete the
- * symtag from files that have numtag.
- *
- * This is done here because it's MUCH faster than just blindly calling
- * "rcs" to remove the tag... trust me.
- */
-static int
-rtag_delete (rcsfile)
- RCSNode *rcsfile;
-{
- char *version;
- int retcode, isbranch;
-
- if (numtag)
- {
- version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1,
- (int *) NULL);
- if (version == NULL)
- return (0);
- free (version);
- }
-
- version = RCS_getversion (rcsfile, symtag, (char *) NULL, 1,
- (int *) NULL);
- if (version == NULL)
- return (0);
- free (version);
-
-
- isbranch = RCS_nodeisbranch (rcsfile, symtag);
- if ((isbranch && !disturb_branch_tags) ||
- (!isbranch && disturb_branch_tags))
- {
- if (!quiet)
- error(0, 0,
- "Not removing %s tag `%s' from `%s'%s.",
- isbranch ? "branch" : "non-branch",
- symtag, rcsfile->path,
- isbranch ? "" : " due to `-B' option");
- return (1);
- }
-
- if ((retcode = RCS_deltag(rcsfile, symtag)) != 0)
- {
- if (!quiet)
- error (0, retcode == -1 ? errno : 0,
- "failed to remove tag `%s' from `%s'", symtag,
- rcsfile->path);
- return (1);
- }
- RCS_rewrite (rcsfile, NULL, NULL);
- return (0);
-}
-
-
-/*
- * Called to tag a particular file (the currently checked out version is
- * tagged with the specified tag - or the specified tag is deleted).
- */
-/* ARGSUSED */
-static int
-tag_fileproc (callerdat, finfo)
- void *callerdat;
- struct file_info *finfo;
-{
- char *version, *oversion;
- char *nversion = NULL;
- char *rev;
- Vers_TS *vers;
- int retcode = 0;
- int retval = 0;
-
- vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
-
- if ((numtag != NULL) || (date != NULL))
- {
- nversion = RCS_getversion(vers->srcfile,
- numtag,
- date,
- force_tag_match,
- (int *) NULL);
- if (nversion == NULL)
- goto free_vars_and_return;
- }
- if (delete_flag)
- {
-
- int isbranch;
- /*
- * If -d is specified, "force_tag_match" is set, so that this call to
- * RCS_getversion() will return a NULL version string if the symbolic
- * tag does not exist in the RCS file.
- *
- * This is done here because it's MUCH faster than just blindly calling
- * "rcs" to remove the tag... trust me.
- */
-
- version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
- (int *) NULL);
- if (version == NULL || vers->srcfile == NULL)
- goto free_vars_and_return;
-
- free (version);
-
- isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
- if ((isbranch && !disturb_branch_tags) ||
- (!isbranch && disturb_branch_tags))
- {
- if (!quiet)
- error(0, 0,
- "Not removing %s tag `%s' from `%s'%s.",
- isbranch ? "branch" : "non-branch",
- symtag, vers->srcfile->path,
- isbranch ? "" : " due to `-B' option");
- retval = 1;
- goto free_vars_and_return;
- }
-
- if ((retcode = RCS_deltag(vers->srcfile, symtag)) != 0)
- {
- if (!quiet)
- error (0, retcode == -1 ? errno : 0,
- "failed to remove tag %s from %s", symtag,
- vers->srcfile->path);
- retval = 1;
- goto free_vars_and_return;
- }
- RCS_rewrite (vers->srcfile, NULL, NULL);
-
- /* warm fuzzies */
- if (!really_quiet)
- {
- cvs_output ("D ", 2);
- cvs_output (finfo->fullname, 0);
- cvs_output ("\n", 1);
- }
-
- goto free_vars_and_return;
- }
-
- /*
- * If we are adding a tag, we need to know which version we have checked
- * out and we'll tag that version.
- */
- if (nversion == NULL)
- {
- version = vers->vn_user;
- }
- else
- {
- version = nversion;
- }
- if (version == NULL)
- {
- goto free_vars_and_return;
- }
- else if (strcmp (version, "0") == 0)
- {
- if (!quiet)
- error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file);
- goto free_vars_and_return;
- }
- else if (version[0] == '-')
- {
- if (!quiet)
- error (0, 0, "skipping removed but un-commited file `%s'", finfo->file);
- goto free_vars_and_return;
- }
- else if (vers->srcfile == NULL)
- {
- if (!quiet)
- error (0, 0, "cannot find revision control file for `%s'", finfo->file);
- goto free_vars_and_return;
- }
-
- /*
- * As an enhancement for the case where a tag is being re-applied to a
- * large number of files, make one extra call to RCS_getversion to see
- * if the tag is already set in the RCS file. If so, check to see if it
- * needs to be moved. If not, do nothing. This will likely save a lot of
- * time when simply moving the tag to the "current" head revisions of a
- * module -- which I have found to be a typical tagging operation.
- */
- rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
- oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1,
- (int *) NULL);
- if (oversion != NULL)
- {
- int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
-
- /*
- * if versions the same and neither old or new are branches don't have
- * to do anything
- */
- if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
- {
- free (oversion);
- if (branch_mode)
- free (rev);
- goto free_vars_and_return;
- }
-
- if (!force_tag_move)
- {
- /* we're NOT going to move the tag */
- cvs_output ("W ", 2);
- cvs_output (finfo->fullname, 0);
- cvs_output (" : ", 0);
- cvs_output (symtag, 0);
- cvs_output (" already exists on ", 0);
- cvs_output (isbranch ? "branch" : "version", 0);
- cvs_output (" ", 0);
- cvs_output (oversion, 0);
- cvs_output (" : NOT MOVING tag to ", 0);
- cvs_output (branch_mode ? "branch" : "version", 0);
- cvs_output (" ", 0);
- cvs_output (rev, 0);
- cvs_output ("\n", 1);
- free (oversion);
- if (branch_mode)
- free (rev);
- goto free_vars_and_return;
- }
- else /* force_tag_move == 1 and... */
- if ((isbranch && !disturb_branch_tags) ||
- (!isbranch && disturb_branch_tags))
- {
- error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
- finfo->fullname,
- isbranch ? "branch" : "non-branch",
- symtag, oversion, rev,
- isbranch ? "" : " due to `-B' option");
- free (oversion);
- if (branch_mode)
- free (rev);
- goto free_vars_and_return;
- }
- free (oversion);
- }
-
- if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0)
- {
- error (1, retcode == -1 ? errno : 0,
- "failed to set tag %s to revision %s in %s",
- symtag, rev, vers->srcfile->path);
- if (branch_mode)
- free (rev);
- retval = 1;
- goto free_vars_and_return;
- }
- if (branch_mode)
- free (rev);
- RCS_rewrite (vers->srcfile, NULL, NULL);
-
- /* more warm fuzzies */
- if (!really_quiet)
- {
- cvs_output ("T ", 2);
- cvs_output (finfo->fullname, 0);
- cvs_output ("\n", 1);
- }
-
- free_vars_and_return:
- if (nversion != NULL)
- free (nversion);
- freevers_ts (&vers);
- return (retval);
-}
-
-/*
- * Print a warm fuzzy message
- */
-/* ARGSUSED */
-static Dtype
-tag_dirproc (callerdat, dir, repos, update_dir, entries)
- void *callerdat;
- const char *dir;
- const char *repos;
- const char *update_dir;
- List *entries;
-{
-
- if (ignore_directory (update_dir))
- {
- /* print the warm fuzzy message */
- if (!quiet)
- error (0, 0, "Ignoring %s", update_dir);
- return R_SKIP_ALL;
- }
-
- if (!quiet)
- error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir);
- return (R_PROCESS);
-}
-
-/* Code relating to the val-tags file. Note that this file has no way
- of knowing when a tag has been deleted. The problem is that there
- is no way of knowing whether a tag still exists somewhere, when we
- delete it some places. Using per-directory val-tags files (in
- CVSREP) might be better, but that might slow down the process of
- verifying that a tag is correct (maybe not, for the likely cases,
- if carefully done), and/or be harder to implement correctly. */
-
-struct val_args {
- char *name;
- int found;
-};
-
-static int val_fileproc PROTO ((void *callerdat, struct file_info *finfo));
-
-static int
-val_fileproc (callerdat, finfo)
- void *callerdat;
- struct file_info *finfo;
-{
- RCSNode *rcsdata;
- struct val_args *args = (struct val_args *)callerdat;
- char *tag;
-
- if ((rcsdata = finfo->rcs) == NULL)
- /* Not sure this can happen, after all we passed only
- W_REPOS | W_ATTIC. */
- return 0;
-
- tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL);
- if (tag != NULL)
- {
- /* FIXME: should find out a way to stop the search at this point. */
- args->found = 1;
- free (tag);
- }
- return 0;
-}
-
-
-
-/* This routine determines whether a tag appears in CVSROOT/val-tags.
- *
- * The val-tags file will be open read-only when IDB is NULL. Since writes to
- * val-tags always append to it, the lack of locking is okay. The worst case
- * race condition might misinterpret a partially written "foobar" matched, for
- * instance, a request for "f", "foo", of "foob". Such a mismatch would be
- * caught harmlessly later.
- *
- * Before CVS adds a tag to val-tags, it will lock val-tags for write and
- * verify that the tag is still not present to avoid adding it twice.
- *
- * NOTES
- * This function expects its parent to handle any necessary locking of the
- * val-tags file.
- *
- * INPUTS
- * idb When this value is NULL, the val-tags file is opened in
- * in read-only mode. When present, the val-tags file is opened
- * in read-write mode and the DBM handle is stored in *IDB.
- * name The tag to search for.
- *
- * OUTPUTS
- * *idb The val-tags file opened for read/write, or NULL if it couldn't
- * be opened.
- *
- * ERRORS
- * Exits with an error message if the val-tags file cannot be opened for
- * read (failure to open val-tags read/write is harmless - see below).
- *
- * RETURNS
- * true 1. If NAME exists in val-tags.
- * 2. If IDB is non-NULL and val-tags cannot be opened for write.
- * This allows callers to ignore the harmless inability to
- * update the val-tags cache.
- * false If the file could be opened and the tag is not present.
- */
-static int is_in_val_tags PROTO((DBM **idb, const char *name));
-static int
-is_in_val_tags (idb, name)
- DBM **idb;
- const char *name;
-{
- DBM *db = NULL;
- char *valtags_filename;
- datum mytag;
- int status;
-
- /* Casting out const should be safe here - input datums are not
- * written to by the myndbm functions.
- */
- mytag.dptr = (char *)name;
- mytag.dsize = strlen (name);
-
- valtags_filename = xmalloc (strlen (current_parsed_root->directory)
- + sizeof CVSROOTADM
- + sizeof CVSROOTADM_VALTAGS + 3);
- sprintf (valtags_filename, "%s/%s/%s", current_parsed_root->directory,
- CVSROOTADM, CVSROOTADM_VALTAGS);
-
- if (idb)
- {
- db = dbm_open (valtags_filename, O_RDWR, 0666);
- if (!db)
- {
- mode_t omask;
-
- if (!existence_error (errno))
- {
- error (0, errno, "warning: cannot open %s read/write",
- valtags_filename);
- *idb = NULL;
- return 1;
- }
-
- omask = umask (cvsumask);
- db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
- umask (omask);
- if (!db)
- {
- error (0, errno, "warning: cannot create %s",
- valtags_filename);
- *idb = NULL;
- return 1;
- }
-
- *idb = db;
- return 0;
- }
-
- *idb = db;
- }
- else
- {
- db = dbm_open (valtags_filename, O_RDONLY, 0444);
- if (!db && !existence_error (errno))
- error (1, errno, "cannot read %s", valtags_filename);
- }
-
- /* If the file merely fails to exist, we just keep going and create
- it later if need be. */
-
- status = 0;
- if (db)
- {
- datum val;
-
- val = dbm_fetch (db, mytag);
- if (val.dptr != NULL)
- /* Found. The tag is valid. */
- status = 1;
-
- /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */
-
- if (!idb) dbm_close (db);
- }
-
- free (valtags_filename);
- return status;
-}
-
-
-
-/* Add a tag to the CVSROOT/val-tags cache. Establishes a write lock and
- * reverifies that the tag does not exist before adding it.
- */
-static void add_to_val_tags PROTO((const char *name));
-static void
-add_to_val_tags (name)
- const char *name;
-{
- DBM *db;
- datum mytag;
- datum value;
-
- if (noexec) return;
-
- val_tags_lock (current_parsed_root->directory);
-
- /* Check for presence again since we have a lock now. */
- if (is_in_val_tags (&db, name))
- {
- clear_val_tags_lock ();
- if (db)
- dbm_close (db);
- return;
- }
-
- /* Casting out const should be safe here - input datums are not
- * written to by the myndbm functions.
- */
- mytag.dptr = (char *)name;
- mytag.dsize = strlen (name);
- value.dptr = "y";
- value.dsize = 1;
-
- if (dbm_store (db, mytag, value, DBM_REPLACE) < 0)
- error (0, errno, "failed to store %s into val-tags", name);
- dbm_close (db);
-
- clear_val_tags_lock ();
-}
-
-
-
-static Dtype val_direntproc PROTO ((void *, const char *, const char *,
- const char *, List *));
-
-static Dtype
-val_direntproc (callerdat, dir, repository, update_dir, entries)
- void *callerdat;
- const char *dir;
- const char *repository;
- const char *update_dir;
- List *entries;
-{
- /* This is not quite right--it doesn't get right the case of "cvs
- update -d -r foobar" where foobar is a tag which exists only in
- files in a directory which does not exist yet, but which is
- about to be created. */
- if (isdir (dir))
- return R_PROCESS;
- return R_SKIP_ALL;
-}
-
-/* Check to see whether NAME is a valid tag. If so, return. If not
- print an error message and exit. ARGC, ARGV, LOCAL, and AFLAG specify
- which files we will be operating on.
-
- REPOSITORY is the repository if we need to cd into it, or NULL if
- we are already there, or "" if we should do a W_LOCAL recursion.
- Sorry for three cases, but the "" case is needed in case the
- working directories come from diverse parts of the repository, the
- NULL case avoids an unneccesary chdir, and the non-NULL, non-""
- case is needed for checkout, where we don't want to chdir if the
- tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any
- local directory. */
-void
-tag_check_valid (name, argc, argv, local, aflag, repository)
- char *name;
- int argc;
- char **argv;
- int local;
- int aflag;
- char *repository;
-{
- struct val_args the_val_args;
- struct saved_cwd cwd;
- int which;
-
- /* Numeric tags require only a syntactic check. */
- if (isdigit ((unsigned char) name[0]))
- {
- char *p;
- for (p = name; *p != '\0'; ++p)
- {
- if (!(isdigit ((unsigned char) *p) || *p == '.'))
- error (1, 0, "\
-Numeric tag %s contains characters other than digits and '.'", name);
- }
- return;
- }
-
- /* Special tags are always valid. */
- if (strcmp (name, TAG_BASE) == 0
- || strcmp (name, TAG_HEAD) == 0)
- return;
-
- if (readonlyfs)
- return;
-
- /* Verify that the tag is valid syntactically. Some later code once made
- * assumptions about this.
- */
- RCS_check_tag (name);
-
- if (is_in_val_tags (NULL, name)) return;
-
- /* We didn't find the tag in val-tags, so look through all the RCS files
- to see whether it exists there. Yes, this is expensive, but there
- is no other way to cope with a tag which might have been created
- by an old version of CVS, from before val-tags was invented.
-
- Since we need this code anyway, we also use it to create
- entries in val-tags in general (that is, the val-tags entry
- will get created the first time the tag is used, not when the
- tag is created). */
-
- the_val_args.name = name;
- the_val_args.found = 0;
-
- which = W_REPOS | W_ATTIC;
-
- if (repository != NULL)
- {
- if (repository[0] == '\0')
- which |= W_LOCAL;
- else
- {
- if (save_cwd (&cwd))
- error_exit ();
- if (CVS_CHDIR (repository) < 0)
- error (1, errno, "cannot change to %s directory", repository);
- }
- }
-
- start_recursion (val_fileproc, (FILESDONEPROC) NULL,
- val_direntproc, (DIRLEAVEPROC) NULL,
- (void *)&the_val_args,
- argc, argv, local, which, aflag,
- CVS_LOCK_READ, NULL, 1, repository);
- if (repository != NULL && repository[0] != '\0')
- {
- if (restore_cwd (&cwd, NULL))
- error_exit ();
- free_cwd (&cwd);
- }
-
- if (!the_val_args.found)
- error (1, 0, "no such tag %s", name);
- else
- /* The tags is valid but not mentioned in val-tags. Add it. */
- add_to_val_tags (name);
-}
-
-
-
-/*
- * Check whether a join tag is valid. This is just like
- * tag_check_valid, but we must stop before the colon if there is one.
- */
-
-void
-tag_check_valid_join (join_tag, argc, argv, local, aflag, repository)
- char *join_tag;
- int argc;
- char **argv;
- int local;
- int aflag;
- char *repository;
-{
- char *c, *s;
-
- c = xstrdup (join_tag);
- s = strchr (c, ':');
- if (s != NULL)
- {
- if (isdigit ((unsigned char) join_tag[0]))
- error (1, 0,
- "Numeric join tag %s may not contain a date specifier",
- join_tag);
-
- *s = '\0';
- /* hmmm... I think it makes sense to allow -j:<date>, but
- * for now this fixes a bug where CVS just spins and spins (I
- * think in the RCS code) looking for a zero length tag.
- */
- if (!*c)
- error (1, 0,
- "argument to join may not contain a date specifier without a tag");
- }
-
- tag_check_valid (c, argc, argv, local, aflag, repository);
-
- free (c);
-}