Sync usage with man page.
[netbsd-mini2440.git] / external / gpl2 / xcvs / dist / src / annotate.c
blob070b4256022da3597b9f7e149f660bcc61361299
1 /*
2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5 * and others.
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.
13 * Show last revision where each line modified
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).
20 #include "cvs.h"
22 /* Options from the command line. */
24 static int force_tag_match = 1;
25 static int force_binary = 0;
26 static char *tag = NULL;
27 static int tag_validated;
28 static char *date = NULL;
30 static int is_rannotate;
32 static int annotate_fileproc (void *callerdat, struct file_info *);
33 static int rannotate_proc (int argc, char **argv, char *xwhere,
34 char *mwhere, char *mfile, int shorten,
35 int local, char *mname, char *msg);
37 static const char *const annotate_usage[] =
39 "Usage: %s %s [-lRfF] [-r rev] [-D date] [files...]\n",
40 "\t-l\tLocal directory only, no recursion.\n",
41 "\t-R\tProcess directories recursively.\n",
42 "\t-f\tUse head revision if tag/date not found.\n",
43 "\t-F\tAnnotate binary files.\n",
44 "\t-r rev\tAnnotate file as of specified revision/tag.\n",
45 "\t-D date\tAnnotate file as of specified date.\n",
46 "(Specify the --help global option for a list of other help options)\n",
47 NULL
50 /* Command to show the revision, date, and author where each line of a
51 file was modified. */
53 int
54 annotate (int argc, char **argv)
56 int local = 0;
57 int err = 0;
58 int c;
60 is_rannotate = (strcmp(cvs_cmd_name, "rannotate") == 0);
62 if (argc == -1)
63 usage (annotate_usage);
65 getoptreset ();
66 while ((c = getopt (argc, argv, "+lr:D:fFR")) != -1)
68 switch (c)
70 case 'l':
71 local = 1;
72 break;
73 case 'R':
74 local = 0;
75 break;
76 case 'r':
77 parse_tagdate (&tag, &date, optarg);
78 break;
79 case 'D':
80 if (date) free (date);
81 date = Make_Date (optarg);
82 break;
83 case 'f':
84 force_tag_match = 0;
85 break;
86 case 'F':
87 force_binary = 1;
88 break;
89 case '?':
90 default:
91 usage (annotate_usage);
92 break;
95 argc -= optind;
96 argv += optind;
98 #ifdef CLIENT_SUPPORT
99 if (current_parsed_root->isremote)
101 start_server ();
103 if (is_rannotate && !supported_request ("rannotate"))
104 error (1, 0, "server does not support rannotate");
106 ign_setup ();
108 if (local)
109 send_arg ("-l");
110 if (!force_tag_match)
111 send_arg ("-f");
112 if (force_binary)
113 send_arg ("-F");
114 option_with_arg ("-r", tag);
115 if (date)
116 client_senddate (date);
117 send_arg ("--");
118 if (is_rannotate)
120 int i;
121 for (i = 0; i < argc; i++)
122 send_arg (argv[i]);
123 send_to_server ("rannotate\012", 0);
125 else
127 send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
128 send_file_names (argc, argv, SEND_EXPAND_WILD);
129 send_to_server ("annotate\012", 0);
131 return get_responses_and_close ();
133 #endif /* CLIENT_SUPPORT */
135 if (is_rannotate)
137 DBM *db;
138 int i;
139 db = open_module ();
140 for (i = 0; i < argc; i++)
142 err += do_module (db, argv[i], MISC, "Annotating", rannotate_proc,
143 NULL, 0, local, 0, 0, NULL);
145 close_module (db);
147 else
149 err = rannotate_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0,
150 local, NULL, NULL);
153 return err;
157 static int
158 rannotate_proc (int argc, char **argv, char *xwhere, char *mwhere,
159 char *mfile, int shorten, int local, char *mname, char *msg)
161 /* Begin section which is identical to patch_proc--should this
162 be abstracted out somehow? */
163 char *myargv[2];
164 int err = 0;
165 int which;
166 char *repository;
167 char *where;
169 if (is_rannotate)
171 repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0])
172 + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
173 (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
174 where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1)
175 + 1);
176 (void) strcpy (where, argv[0]);
178 /* if mfile isn't null, we need to set up to do only part of the module */
179 if (mfile != NULL)
181 char *cp;
182 char *path;
184 /* if the portion of the module is a path, put the dir part on repos */
185 if ((cp = strrchr (mfile, '/')) != NULL)
187 *cp = '\0';
188 (void) strcat (repository, "/");
189 (void) strcat (repository, mfile);
190 (void) strcat (where, "/");
191 (void) strcat (where, mfile);
192 mfile = cp + 1;
195 /* take care of the rest */
196 path = Xasprintf ("%s/%s", repository, mfile);
197 if (isdir (path))
199 /* directory means repository gets the dir tacked on */
200 (void) strcpy (repository, path);
201 (void) strcat (where, "/");
202 (void) strcat (where, mfile);
204 else
206 myargv[0] = argv[0];
207 myargv[1] = mfile;
208 argc = 2;
209 argv = myargv;
211 free (path);
214 /* cd to the starting repository */
215 if (CVS_CHDIR (repository) < 0)
217 error (0, errno, "cannot chdir to %s", repository);
218 free (repository);
219 free (where);
220 return 1;
222 /* End section which is identical to patch_proc. */
224 if (force_tag_match && tag != NULL)
225 which = W_REPOS | W_ATTIC;
226 else
227 which = W_REPOS;
229 else
231 where = NULL;
232 which = W_LOCAL;
233 repository = "";
236 if (tag != NULL && !tag_validated)
238 tag_check_valid (tag, argc - 1, argv + 1, local, 0, repository, false);
239 tag_validated = 1;
242 err = start_recursion (annotate_fileproc, NULL, NULL, NULL, NULL,
243 argc - 1, argv + 1, local, which, 0, CVS_LOCK_READ,
244 where, 1, repository);
245 if (which & W_REPOS)
246 free (repository);
247 if (where != NULL)
248 free (where);
249 return err;
253 static int
254 annotate_fileproc (void *callerdat, struct file_info *finfo)
256 char *expand, *version;
258 if (finfo->rcs == NULL)
259 return 1;
261 if (finfo->rcs->flags & PARTIAL)
262 RCS_reparsercsfile (finfo->rcs, NULL, NULL);
264 expand = RCS_getexpand (finfo->rcs);
265 version = RCS_getversion (finfo->rcs, tag, date, force_tag_match, NULL);
267 if (version == NULL)
268 return 0;
270 /* Distinguish output for various files if we are processing
271 several files. */
272 cvs_outerr ("\nAnnotations for ", 0);
273 cvs_outerr (finfo->fullname, 0);
274 cvs_outerr ("\n***************\n", 0);
276 if (!force_binary && expand && expand[0] == 'b')
278 cvs_outerr ("Skipping binary file -- -F not specified.\n", 0);
280 else
282 RCS_deltas (finfo->rcs, NULL, NULL,
283 version, RCS_ANNOTATE, NULL, NULL, NULL, NULL);
285 free (version);
286 return 0;