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