xref: /dragonfly/contrib/cvs-1.12/src/annotate.c (revision 3170ffd7)
1 /*
2  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3  *
4  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5  *                                  and others.
6  *
7  * Portions Copyright (c) 1992, Brian Berliner and Jeff Polk
8  * Portions Copyright (c) 1989-1992, Brian Berliner
9  *
10  * You may distribute under the terms of the GNU General Public License as
11  * specified in the README file that comes with the CVS source distribution.
12  *
13  * Show last revision where each line modified
14  *
15  * Prints the specified files with each line annotated with the revision
16  * number where it was last modified.  With no argument, annotates all
17  * all the files in the directory (recursive by default).
18  */
19 
20 #include "cvs.h"
21 
22 /* Options from the command line.  */
23 
24 static int backwards = 0;
25 static int force_tag_match = 1;
26 static int force_binary = 0;
27 static char *tag = NULL;
28 static int tag_validated;
29 static char *date = NULL;
30 
31 static int is_rannotate;
32 
33 static int annotate_fileproc (void *callerdat, struct file_info *);
34 static int rannotate_proc (int argc, char **argv, char *xwhere,
35 				 char *mwhere, char *mfile, int shorten,
36 				 int local, char *mname, char *msg);
37 
38 static const char *const annotate_usage[] =
39 {
40     "Usage: %s %s [-blRfF] [-r rev] [-D date] [files...]\n",
41     "\t-b\tBackwards, show when a line was removed.\n",
42     "\t-l\tLocal directory only, no recursion.\n",
43     "\t-R\tProcess directories recursively.\n",
44     "\t-f\tUse head revision if tag/date not found.\n",
45     "\t-F\tAnnotate binary files.\n",
46     "\t-r rev\tAnnotate file as of specified revision/tag.\n",
47     "\t-D date\tAnnotate file as of specified date.\n",
48     "(Specify the --help global option for a list of other help options)\n",
49     NULL
50 };
51 
52 /* Command to show the revision, date, and author where each line of a
53    file was modified.  */
54 
55 int
56 annotate (int argc, char **argv)
57 {
58     int local = 0;
59     int err = 0;
60     int c;
61 
62     is_rannotate = (strcmp(cvs_cmd_name, "rannotate") == 0);
63 
64     if (argc == -1)
65 	usage (annotate_usage);
66 
67     optind = 0;
68     while ((c = getopt (argc, argv, "+blr:D:fFR")) != -1)
69     {
70 	switch (c)
71 	{
72 	    case 'b':
73 		backwards = 1;
74 	    case 'l':
75 		local = 1;
76 		break;
77 	    case 'R':
78 		local = 0;
79 		break;
80 	    case 'r':
81 		parse_tagdate (&tag, &date, optarg);
82 		break;
83 	    case 'D':
84 		if (date) free (date);
85 	        date = Make_Date (optarg);
86 		break;
87 	    case 'f':
88 	        force_tag_match = 0;
89 		break;
90 	    case 'F':
91 	        force_binary = 1;
92 		break;
93 	    case '?':
94 	    default:
95 		usage (annotate_usage);
96 		break;
97 	}
98     }
99     argc -= optind;
100     argv += optind;
101 
102 #ifdef CLIENT_SUPPORT
103     if (current_parsed_root->isremote)
104     {
105 	start_server ();
106 
107 	if (is_rannotate && !supported_request ("rannotate"))
108 	    error (1, 0, "server does not support rannotate");
109 
110 	ign_setup ();
111 
112 	if (backwards)
113 	    send_arg ("-b");
114 	if (local)
115 	    send_arg ("-l");
116 	if (!force_tag_match)
117 	    send_arg ("-f");
118 	if (force_binary)
119 	    send_arg ("-F");
120 	option_with_arg ("-r", tag);
121 	if (date)
122 	    client_senddate (date);
123 	send_arg ("--");
124 	if (is_rannotate)
125 	{
126 	    int i;
127 	    for (i = 0; i < argc; i++)
128 		send_arg (argv[i]);
129 	    send_to_server ("rannotate\012", 0);
130 	}
131 	else
132 	{
133 	    send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
134 	    send_file_names (argc, argv, SEND_EXPAND_WILD);
135 	    send_to_server ("annotate\012", 0);
136 	}
137 	return get_responses_and_close ();
138     }
139 #endif /* CLIENT_SUPPORT */
140 
141     if (is_rannotate)
142     {
143 	DBM *db;
144 	int i;
145 	db = open_module ();
146 	for (i = 0; i < argc; i++)
147 	{
148 	    err += do_module (db, argv[i], MISC, "Annotating", rannotate_proc,
149 			      NULL, 0, local, 0, 0, NULL);
150 	}
151 	close_module (db);
152     }
153     else
154     {
155 	err = rannotate_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0,
156 			      local, NULL, NULL);
157     }
158 
159     return err;
160 }
161 
162 
163 static int
164 rannotate_proc (int argc, char **argv, char *xwhere, char *mwhere,
165 		char *mfile, int shorten, int local, char *mname, char *msg)
166 {
167     /* Begin section which is identical to patch_proc--should this
168        be abstracted out somehow?  */
169     char *myargv[2];
170     int err = 0;
171     int which;
172     char *repository;
173     char *where;
174 
175     if (is_rannotate)
176     {
177 	repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0])
178 			      + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
179 	(void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
180 	where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1)
181 			 + 1);
182 	(void) strcpy (where, argv[0]);
183 
184 	/* if mfile isn't null, we need to set up to do only part of the module */
185 	if (mfile != NULL)
186 	{
187 	    char *cp;
188 	    char *path;
189 
190 	    /* if the portion of the module is a path, put the dir part on repos */
191 	    if ((cp = strrchr (mfile, '/')) != NULL)
192 	    {
193 		*cp = '\0';
194 		(void) strcat (repository, "/");
195 		(void) strcat (repository, mfile);
196 		(void) strcat (where, "/");
197 		(void) strcat (where, mfile);
198 		mfile = cp + 1;
199 	    }
200 
201 	    /* take care of the rest */
202 	    path = Xasprintf ("%s/%s", repository, mfile);
203 	    if (isdir (path))
204 	    {
205 		/* directory means repository gets the dir tacked on */
206 		(void) strcpy (repository, path);
207 		(void) strcat (where, "/");
208 		(void) strcat (where, mfile);
209 	    }
210 	    else
211 	    {
212 		myargv[0] = argv[0];
213 		myargv[1] = mfile;
214 		argc = 2;
215 		argv = myargv;
216 	    }
217 	    free (path);
218 	}
219 
220 	/* cd to the starting repository */
221 	if (CVS_CHDIR (repository) < 0)
222 	{
223 	    error (0, errno, "cannot chdir to %s", repository);
224 	    free (repository);
225 	    free (where);
226 	    return 1;
227 	}
228 	/* End section which is identical to patch_proc.  */
229 
230 	if (force_tag_match && tag != NULL)
231 	    which = W_REPOS | W_ATTIC;
232 	else
233 	    which = W_REPOS;
234     }
235     else
236     {
237         where = NULL;
238         which = W_LOCAL;
239         repository = "";
240     }
241 
242     if (tag != NULL && !tag_validated)
243     {
244 	tag_check_valid (tag, argc - 1, argv + 1, local, 0, repository, false);
245 	tag_validated = 1;
246     }
247 
248     err = start_recursion (annotate_fileproc, NULL, NULL, NULL, NULL,
249 			   argc - 1, argv + 1, local, which, 0, CVS_LOCK_READ,
250 			   where, 1, repository);
251     if (which & W_REPOS)
252 	free (repository);
253     if (where != NULL)
254 	free (where);
255     return err;
256 }
257 
258 
259 static int
260 annotate_fileproc (void *callerdat, struct file_info *finfo)
261 {
262     char *expand, *version;
263 
264     if (finfo->rcs == NULL)
265         return 1;
266 
267     if (finfo->rcs->flags & PARTIAL)
268         RCS_reparsercsfile (finfo->rcs, NULL, NULL);
269 
270     expand = RCS_getexpand (finfo->rcs);
271     version = RCS_getversion (finfo->rcs, tag, date, force_tag_match, NULL);
272 
273     if (version == NULL)
274         return 0;
275 
276     /* Distinguish output for various files if we are processing
277        several files.  */
278     cvs_outerr ("\nAnnotations for ", 0);
279     cvs_outerr (finfo->fullname, 0);
280     cvs_outerr ("\n***************\n", 0);
281 
282     if (!force_binary && expand && expand[0] == 'b')
283     {
284         cvs_outerr ("Skipping binary file -- -F not specified.\n", 0);
285     }
286     else
287     {
288 	RCS_deltas (finfo->rcs, NULL, NULL,
289 		    version,
290 		    backwards ? RCS_ANNOTATE_BACKWARDS : RCS_ANNOTATE,
291 		    NULL, NULL, NULL, NULL);
292     }
293     free (version);
294     return 0;
295 }
296