summaryrefslogtreecommitdiff
path: root/src/annotate.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/annotate.c')
-rw-r--r--src/annotate.c287
1 files changed, 287 insertions, 0 deletions
diff --git a/src/annotate.c b/src/annotate.c
new file mode 100644
index 0000000..962e9b5
--- /dev/null
+++ b/src/annotate.c
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ *
+ * Show last revision where each line modified
+ *
+ * Prints the specified files with each line annotated with the revision
+ * number where it was last modified. With no argument, annotates all
+ * all the files in the directory (recursive by default).
+ */
+
+#include "cvs.h"
+
+/* Options from the command line. */
+
+static int force_tag_match = 1;
+static int force_binary = 0;
+static char *tag = NULL;
+static int tag_validated;
+static char *date = NULL;
+
+static int is_rannotate;
+
+static int annotate_fileproc (void *callerdat, struct file_info *);
+static int rannotate_proc (int argc, char **argv, char *xwhere,
+ char *mwhere, char *mfile, int shorten,
+ int local, char *mname, char *msg);
+
+static const char *const annotate_usage[] =
+{
+ "Usage: %s %s [-lRfF] [-r rev] [-D date] [files...]\n",
+ "\t-l\tLocal directory only, no recursion.\n",
+ "\t-R\tProcess directories recursively.\n",
+ "\t-f\tUse head revision if tag/date not found.\n",
+ "\t-F\tAnnotate binary files.\n",
+ "\t-r rev\tAnnotate file as of specified revision/tag.\n",
+ "\t-D date\tAnnotate file as of specified date.\n",
+ "(Specify the --help global option for a list of other help options)\n",
+ NULL
+};
+
+/* Command to show the revision, date, and author where each line of a
+ file was modified. */
+
+int
+annotate (int argc, char **argv)
+{
+ int local = 0;
+ int err = 0;
+ int c;
+
+ is_rannotate = (strcmp(cvs_cmd_name, "rannotate") == 0);
+
+ if (argc == -1)
+ usage (annotate_usage);
+
+ optind = 0;
+ while ((c = getopt (argc, argv, "+lr:D:fFR")) != -1)
+ {
+ switch (c)
+ {
+ case 'l':
+ local = 1;
+ break;
+ case 'R':
+ local = 0;
+ break;
+ case 'r':
+ parse_tagdate (&tag, &date, optarg);
+ break;
+ case 'D':
+ if (date) free (date);
+ date = Make_Date (optarg);
+ break;
+ case 'f':
+ force_tag_match = 0;
+ break;
+ case 'F':
+ force_binary = 1;
+ break;
+ case '?':
+ default:
+ usage (annotate_usage);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef CLIENT_SUPPORT
+ if (current_parsed_root->isremote)
+ {
+ start_server ();
+
+ if (is_rannotate && !supported_request ("rannotate"))
+ error (1, 0, "server does not support rannotate");
+
+ ign_setup ();
+
+ if (local)
+ send_arg ("-l");
+ if (!force_tag_match)
+ send_arg ("-f");
+ if (force_binary)
+ send_arg ("-F");
+ option_with_arg ("-r", tag);
+ if (date)
+ client_senddate (date);
+ send_arg ("--");
+ if (is_rannotate)
+ {
+ int i;
+ for (i = 0; i < argc; i++)
+ send_arg (argv[i]);
+ send_to_server ("rannotate\012", 0);
+ }
+ else
+ {
+ send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
+ send_to_server ("annotate\012", 0);
+ }
+ return get_responses_and_close ();
+ }
+#endif /* CLIENT_SUPPORT */
+
+ if (is_rannotate)
+ {
+ DBM *db;
+ int i;
+ db = open_module ();
+ for (i = 0; i < argc; i++)
+ {
+ err += do_module (db, argv[i], MISC, "Annotating", rannotate_proc,
+ NULL, 0, local, 0, 0, NULL);
+ }
+ close_module (db);
+ }
+ else
+ {
+ err = rannotate_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0,
+ local, NULL, NULL);
+ }
+
+ return err;
+}
+
+
+static int
+rannotate_proc (int argc, char **argv, char *xwhere, char *mwhere,
+ char *mfile, int shorten, int local, 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_rannotate)
+ {
+ 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 = Xasprintf ("%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 (force_tag_match && tag != NULL)
+ which = W_REPOS | W_ATTIC;
+ else
+ which = W_REPOS;
+ }
+ else
+ {
+ where = NULL;
+ which = W_LOCAL;
+ repository = "";
+ }
+
+ if (tag != NULL && !tag_validated)
+ {
+ tag_check_valid (tag, argc - 1, argv + 1, local, 0, repository, false);
+ tag_validated = 1;
+ }
+
+ err = start_recursion (annotate_fileproc, NULL, NULL, NULL, NULL,
+ argc - 1, argv + 1, local, which, 0, CVS_LOCK_READ,
+ where, 1, repository);
+ if (which & W_REPOS)
+ free (repository);
+ if (where != NULL)
+ free (where);
+ return err;
+}
+
+
+static int
+annotate_fileproc (void *callerdat, struct file_info *finfo)
+{
+ char *expand, *version;
+
+ if (finfo->rcs == NULL)
+ return 1;
+
+ if (finfo->rcs->flags & PARTIAL)
+ RCS_reparsercsfile (finfo->rcs, NULL, NULL);
+
+ expand = RCS_getexpand (finfo->rcs);
+ version = RCS_getversion (finfo->rcs, tag, date, force_tag_match, NULL);
+
+ if (version == NULL)
+ return 0;
+
+ /* Distinguish output for various files if we are processing
+ several files. */
+ cvs_outerr ("\nAnnotations for ", 0);
+ cvs_outerr (finfo->fullname, 0);
+ cvs_outerr ("\n***************\n", 0);
+
+ if (!force_binary && expand && expand[0] == 'b')
+ {
+ cvs_outerr ("Skipping binary file -- -F not specified.\n", 0);
+ }
+ else
+ {
+ RCS_deltas (finfo->rcs, NULL, NULL,
+ version, RCS_ANNOTATE, NULL, NULL, NULL, NULL);
+ }
+ free (version);
+ return 0;
+}