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