/contrib/cvs/src/annotate.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 302 lines · 236 code · 35 blank · 31 comment · 44 complexity · d8d1ce52265f7bb17dd6a607b6e5e3d4 MD5 · raw file

  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. #include "cvs.h"
  20. /* Options from the command line. */
  21. static int force_tag_match = 1;
  22. static int force_binary = 0;
  23. static char *tag = NULL;
  24. static int tag_validated;
  25. static char *date = NULL;
  26. static int is_rannotate;
  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. static const char *const annotate_usage[] =
  32. {
  33. "Usage: %s %s [-lRfF] [-r rev] [-D date] [files...]\n",
  34. "\t-l\tLocal directory only, no recursion.\n",
  35. "\t-R\tProcess directories recursively.\n",
  36. "\t-f\tUse head revision if tag/date not found.\n",
  37. "\t-F\tAnnotate binary files.\n",
  38. "\t-r rev\tAnnotate file as of specified revision/tag.\n",
  39. "\t-D date\tAnnotate file as of specified date.\n",
  40. "(Specify the --help global option for a list of other help options)\n",
  41. NULL
  42. };
  43. /* Command to show the revision, date, and author where each line of a
  44. file was modified. */
  45. int
  46. annotate (argc, argv)
  47. int argc;
  48. char **argv;
  49. {
  50. int local = 0;
  51. int err = 0;
  52. int c;
  53. is_rannotate = (strcmp(cvs_cmd_name, "rannotate") == 0);
  54. if (argc == -1)
  55. usage (annotate_usage);
  56. optind = 0;
  57. while ((c = getopt (argc, argv, "+lr:D:fFR")) != -1)
  58. {
  59. switch (c)
  60. {
  61. case 'l':
  62. local = 1;
  63. break;
  64. case 'R':
  65. local = 0;
  66. break;
  67. case 'r':
  68. tag = optarg;
  69. break;
  70. case 'D':
  71. date = Make_Date (optarg);
  72. break;
  73. case 'f':
  74. force_tag_match = 0;
  75. break;
  76. case 'F':
  77. force_binary = 1;
  78. break;
  79. case '?':
  80. default:
  81. usage (annotate_usage);
  82. break;
  83. }
  84. }
  85. argc -= optind;
  86. argv += optind;
  87. #ifdef CLIENT_SUPPORT
  88. if (current_parsed_root->isremote)
  89. {
  90. start_server ();
  91. if (is_rannotate && !supported_request ("rannotate"))
  92. error (1, 0, "server does not support rannotate");
  93. ign_setup ();
  94. if (local)
  95. send_arg ("-l");
  96. if (!force_tag_match)
  97. send_arg ("-f");
  98. if (force_binary)
  99. send_arg ("-F");
  100. option_with_arg ("-r", tag);
  101. if (date)
  102. client_senddate (date);
  103. send_arg ("--");
  104. if (is_rannotate)
  105. {
  106. int i;
  107. for (i = 0; i < argc; i++)
  108. send_arg (argv[i]);
  109. send_to_server ("rannotate\012", 0);
  110. }
  111. else
  112. {
  113. send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
  114. send_file_names (argc, argv, SEND_EXPAND_WILD);
  115. send_to_server ("annotate\012", 0);
  116. }
  117. return get_responses_and_close ();
  118. }
  119. #endif /* CLIENT_SUPPORT */
  120. if (is_rannotate)
  121. {
  122. DBM *db;
  123. int i;
  124. db = open_module ();
  125. for (i = 0; i < argc; i++)
  126. {
  127. err += do_module (db, argv[i], MISC, "Annotating", rannotate_proc,
  128. (char *) NULL, 0, local, 0, 0, (char *) NULL);
  129. }
  130. close_module (db);
  131. }
  132. else
  133. {
  134. err = rannotate_proc (argc + 1, argv - 1, (char *) NULL,
  135. (char *) NULL, (char *) NULL, 0, local, (char *) NULL,
  136. (char *) NULL);
  137. }
  138. return err;
  139. }
  140. static int
  141. rannotate_proc (argc, argv, xwhere, mwhere, mfile, shorten, local, mname, msg)
  142. int argc;
  143. char **argv;
  144. char *xwhere;
  145. char *mwhere;
  146. char *mfile;
  147. int shorten;
  148. int local;
  149. char *mname;
  150. char *msg;
  151. {
  152. /* Begin section which is identical to patch_proc--should this
  153. be abstracted out somehow? */
  154. char *myargv[2];
  155. int err = 0;
  156. int which;
  157. char *repository;
  158. char *where;
  159. if (is_rannotate)
  160. {
  161. repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0])
  162. + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
  163. (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]);
  164. where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1)
  165. + 1);
  166. (void) strcpy (where, argv[0]);
  167. /* if mfile isn't null, we need to set up to do only part of the module */
  168. if (mfile != NULL)
  169. {
  170. char *cp;
  171. char *path;
  172. /* if the portion of the module is a path, put the dir part on repos */
  173. if ((cp = strrchr (mfile, '/')) != NULL)
  174. {
  175. *cp = '\0';
  176. (void) strcat (repository, "/");
  177. (void) strcat (repository, mfile);
  178. (void) strcat (where, "/");
  179. (void) strcat (where, mfile);
  180. mfile = cp + 1;
  181. }
  182. /* take care of the rest */
  183. path = xmalloc (strlen (repository) + strlen (mfile) + 5);
  184. (void) sprintf (path, "%s/%s", repository, mfile);
  185. if (isdir (path))
  186. {
  187. /* directory means repository gets the dir tacked on */
  188. (void) strcpy (repository, path);
  189. (void) strcat (where, "/");
  190. (void) strcat (where, mfile);
  191. }
  192. else
  193. {
  194. myargv[0] = argv[0];
  195. myargv[1] = mfile;
  196. argc = 2;
  197. argv = myargv;
  198. }
  199. free (path);
  200. }
  201. /* cd to the starting repository */
  202. if ( CVS_CHDIR (repository) < 0)
  203. {
  204. error (0, errno, "cannot chdir to %s", repository);
  205. free (repository);
  206. free (where);
  207. return (1);
  208. }
  209. /* End section which is identical to patch_proc. */
  210. if (force_tag_match && tag != NULL)
  211. which = W_REPOS | W_ATTIC;
  212. else
  213. which = W_REPOS;
  214. }
  215. else
  216. {
  217. where = NULL;
  218. which = W_LOCAL;
  219. repository = "";
  220. }
  221. if (tag != NULL && !tag_validated)
  222. {
  223. tag_check_valid (tag, argc - 1, argv + 1, local, 0, repository);
  224. tag_validated = 1;
  225. }
  226. err = start_recursion (annotate_fileproc, (FILESDONEPROC) NULL,
  227. (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
  228. argc - 1, argv + 1, local, which, 0, CVS_LOCK_READ,
  229. where, 1, repository);
  230. if ( which & W_REPOS )
  231. free ( repository );
  232. if ( where != NULL )
  233. free (where);
  234. return err;
  235. }
  236. static int
  237. annotate_fileproc (callerdat, finfo)
  238. void *callerdat;
  239. struct file_info *finfo;
  240. {
  241. char *expand, *version;
  242. if (finfo->rcs == NULL)
  243. return (1);
  244. if (finfo->rcs->flags & PARTIAL)
  245. RCS_reparsercsfile (finfo->rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
  246. expand = RCS_getexpand (finfo->rcs);
  247. version = RCS_getversion (finfo->rcs, tag, date, force_tag_match,
  248. (int *) NULL);
  249. if (version == NULL)
  250. return 0;
  251. /* Distinguish output for various files if we are processing
  252. several files. */
  253. cvs_outerr ("\nAnnotations for ", 0);
  254. cvs_outerr (finfo->fullname, 0);
  255. cvs_outerr ("\n***************\n", 0);
  256. if (!force_binary && expand && expand[0] == 'b')
  257. {
  258. cvs_outerr ("Skipping binary file -- -F not specified.\n", 0);
  259. }
  260. else
  261. {
  262. RCS_deltas (finfo->rcs, (FILE *) NULL, (struct rcsbuffer *) NULL,
  263. version, RCS_ANNOTATE, NULL, NULL, NULL, NULL);
  264. }
  265. free (version);
  266. return 0;
  267. }