summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src/rtag.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>1996-08-20 23:46:10 +0000
committerpeter <peter@FreeBSD.org>1996-08-20 23:46:10 +0000
commit8982e501c77217c860f79bba431f46a62b607a21 (patch)
tree70187fdf5be4cbefd0baf46bddac7e5e32c13c24 /contrib/cvs/src/rtag.c
parent01ee40fd6a76f6ff7ef247fc1b2cf6e337f216c5 (diff)
downloadFreeBSD-src-8982e501c77217c860f79bba431f46a62b607a21.zip
FreeBSD-src-8982e501c77217c860f79bba431f46a62b607a21.tar.gz
Import of slightly trimmed cvs-1.8 distribution. Generated files
and non-unix code has been left out.
Diffstat (limited to 'contrib/cvs/src/rtag.c')
-rw-r--r--contrib/cvs/src/rtag.c682
1 files changed, 682 insertions, 0 deletions
diff --git a/contrib/cvs/src/rtag.c b/contrib/cvs/src/rtag.c
new file mode 100644
index 0000000..8609647
--- /dev/null
+++ b/contrib/cvs/src/rtag.c
@@ -0,0 +1,682 @@
+/*
+ * Copyright (c) 1992, Brian Berliner and Jeff Polk
+ * 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 1.4 kit.
+ *
+ * Rtag
+ *
+ * Add or delete a symbolic name to an RCS file, or a collection of RCS files.
+ * Uses the modules database, if necessary.
+ */
+
+#include "cvs.h"
+
+static int check_fileproc PROTO((struct file_info *finfo));
+static int check_filesdoneproc PROTO((int err, char *repos, char *update_dir));
+static int pretag_proc PROTO((char *repository, 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 rtag_dirproc PROTO((char *dir, char *repos, char *update_dir));
+static int rtag_fileproc PROTO((struct file_info *finfo));
+static int rtag_proc PROTO((int *pargc, char **argv, char *xwhere,
+ char *mwhere, char *mfile, int shorten,
+ int local_specified, char *mname, char *msg));
+static int rtag_delete PROTO((RCSNode *rcsfile));
+
+
+struct tag_info
+{
+ Ctype status;
+ char *rev;
+ char *tag;
+ char *options;
+};
+
+struct master_lists
+{
+ List *tlist;
+};
+
+static List *mtlist;
+static List *tlist;
+
+static char *symtag;
+static char *numtag;
+static int numtag_validated = 0;
+static int delete_flag; /* adding a tag by default */
+static int attic_too; /* remove tag from Attic files */
+static int branch_mode; /* make an automagic "branch" tag */
+static char *date;
+static int local; /* recursive by default */
+static int force_tag_match = 1; /* force by default */
+static int force_tag_move; /* don't move existing tags by default */
+
+static const char *const rtag_usage[] =
+{
+ "Usage: %s %s [-aflRnF] [-b] [-d] [-r tag|-D date] tag modules...\n",
+ "\t-a\tClear tag from removed files that would not otherwise be tagged.\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-n\tNo execution of 'tag program'\n",
+ "\t-d\tDelete the given Tag.\n",
+ "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
+ "\t-[rD]\tExisting tag or Date.\n",
+ "\t-F\tMove tag if it already exists\n",
+ NULL
+};
+
+int
+rtag (argc, argv)
+ int argc;
+ char **argv;
+{
+ register int i;
+ int c;
+ DBM *db;
+ int run_module_prog = 1;
+ int err = 0;
+
+ if (argc == -1)
+ usage (rtag_usage);
+
+ optind = 1;
+ while ((c = getopt (argc, argv, "FanfQqlRdbr:D:")) != -1)
+ {
+ switch (c)
+ {
+ case 'a':
+ attic_too = 1;
+ break;
+ case 'n':
+ run_module_prog = 0;
+ break;
+ case 'Q':
+ case 'q':
+#ifdef SERVER_SUPPORT
+ /* The CVS 1.5 client sends these options (in addition to
+ Global_option requests), so we must ignore them. */
+ if (!server_active)
+#endif
+ error (1, 0,
+ "-q or -Q must be specified before \"%s\"",
+ command_name);
+ break;
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'd':
+ delete_flag = 1;
+ break;
+ case 'f':
+ force_tag_match = 0;
+ break;
+ case 'b':
+ branch_mode = 1;
+ break;
+ case 'r':
+ numtag = optarg;
+ break;
+ case 'D':
+ if (date)
+ free (date);
+ date = Make_Date (optarg);
+ break;
+ case 'F':
+ force_tag_move = 1;
+ break;
+ case '?':
+ default:
+ usage (rtag_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 2)
+ usage (rtag_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 (client_active)
+ {
+ /* We're the client side. Fire up the remote server. */
+ start_server ();
+
+ ign_setup ();
+
+ if (local)
+ send_arg("-l");
+ if (delete_flag)
+ send_arg("-d");
+ if (branch_mode)
+ send_arg("-b");
+ if (force_tag_move)
+ send_arg("-F");
+ if (run_module_prog)
+ send_arg("-n");
+ if (attic_too)
+ send_arg("-a");
+
+ if (numtag)
+ option_with_arg ("-r", numtag);
+ if (date)
+ client_senddate (date);
+
+ send_arg (symtag);
+
+ {
+ int i;
+ for (i = 0; i < argc; ++i)
+ send_arg (argv[i]);
+ }
+
+ send_to_server ("rtag\012", 0);
+ return get_responses_and_close ();
+ }
+#endif
+
+ 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, 0, run_module_prog,
+ symtag);
+ }
+ close_module (db);
+ return (err);
+}
+
+/*
+ * callback proc for doing the real work of tagging
+ */
+/* ARGSUSED */
+static int
+rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
+ mname, msg)
+ int *pargc;
+ char **argv;
+ char *xwhere;
+ char *mwhere;
+ char *mfile;
+ int shorten;
+ int local_specified;
+ char *mname;
+ char *msg;
+{
+ int err = 0;
+ int which;
+ char repository[PATH_MAX];
+ char where[PATH_MAX];
+
+ (void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
+ (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[PATH_MAX];
+
+ /* 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 */
+ (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
+ {
+ int i;
+
+ /* a file means muck argv */
+ for (i = 1; i < *pargc; i++)
+ free (argv[i]);
+ argv[1] = xstrdup (mfile);
+ (*pargc) = 2;
+ }
+ }
+
+ /* chdir to the starting directory */
+ if (chdir (repository) < 0)
+ {
+ error (0, errno, "cannot chdir to %s", repository);
+ return (1);
+ }
+
+ if (delete_flag || attic_too || (force_tag_match && numtag))
+ which = W_REPOS | W_ATTIC;
+ else
+ which = W_REPOS;
+
+ if (numtag != NULL && !numtag_validated)
+ {
+ tag_check_valid (numtag, *pargc - 1, argv + 1, local, 0, NULL);
+ 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,
+ *pargc - 1, argv + 1, local, which, 0, 1,
+ where, 1, 1);
+
+ if (err)
+ {
+ error (1, 0, "correct the above errors first!");
+ }
+
+ /* start the recursion processor */
+ err = start_recursion (rtag_fileproc, (FILESDONEPROC) NULL, rtag_dirproc,
+ (DIRLEAVEPROC) NULL, *pargc - 1, argv + 1, local,
+ which, 0, 1, where, 1, 1);
+
+ dellist(&mtlist);
+
+ return (err);
+}
+
+/* check file that is to be tagged */
+/* All we do here is add it to our list */
+
+static int
+check_fileproc (finfo)
+ struct file_info *finfo;
+{
+ char *xdir;
+ Node *p;
+ Vers_TS *vers;
+
+ 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 = (char *) 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;
+ vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL,
+ (char *) NULL, finfo->file, 0, 0, finfo->entries, finfo->rcs);
+ p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0);
+ if (p->data != NULL)
+ {
+ int addit = 1;
+ char *oversion;
+
+ oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0);
+ if (oversion == NULL)
+ {
+ if (delete_flag)
+ {
+ addit = 0;
+ }
+ }
+ 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(err, repos, update_dir)
+ int err;
+ char *repos;
+ char *update_dir;
+{
+ 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)
+ char *repository;
+ char *filter;
+{
+ if (filter[0] == '/')
+ {
+ char *s, *cp;
+
+ s = xstrdup(filter);
+ for (cp=s; *cp; cp++)
+ {
+ if (isspace(*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("%s %s %s %s",
+ filter,
+ symtag,
+ delete_flag ? "del" : force_tag_move ? "mov" : "add",
+ repository);
+ walklist(tlist, pretag_list_proc, NULL);
+ return (run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY));
+}
+
+static void
+masterlist_delproc(p)
+ Node *p;
+{
+ struct master_lists *ml;
+
+ ml = (struct master_lists *)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 tag a particular file, as appropriate with the options that were
+ * set above.
+ */
+/* ARGSUSED */
+static int
+rtag_fileproc (finfo)
+ 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, 0);
+ 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 (*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->path, symtag, numtag);
+ }
+ 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, 0);
+ if (oversion != NULL)
+ {
+ int isbranch = RCS_isbranch (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);
+ return (0);
+ }
+ free (oversion);
+ }
+ retcode = RCS_settag(rcsfile->path, symtag, rev);
+ }
+
+ 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;
+
+ if (numtag)
+ {
+ version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1, 0);
+ if (version == NULL)
+ return (0);
+ free (version);
+ }
+
+ version = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 0);
+ if (version == NULL)
+ return (0);
+ free (version);
+
+ if ((retcode = RCS_deltag(rcsfile->path, symtag, 1)) != 0)
+ {
+ if (!quiet)
+ error (0, retcode == -1 ? errno : 0,
+ "failed to remove tag `%s' from `%s'", symtag,
+ rcsfile->path);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Print a warm fuzzy message
+ */
+/* ARGSUSED */
+static Dtype
+rtag_dirproc (dir, repos, update_dir)
+ char *dir;
+ char *repos;
+ char *update_dir;
+{
+ if (!quiet)
+ error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir);
+ return (R_PROCESS);
+}
+
+
+
OpenPOWER on IntegriCloud