xref: /openbsd-src/usr.bin/cvs/annotate.c (revision f331ff59831f242d2460f823c6dba145a2176394)
1*f331ff59Stobias /*	$OpenBSD: annotate.c,v 1.45 2008/01/31 10:15:05 tobias Exp $	*/
2e0d97c9bSxsa /*
35e4c4390Stobias  * Copyright (c) 2007 Tobias Stoeckmann <tobias@openbsd.org>
4efaa2accStobias  * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org>
5e0d97c9bSxsa  *
6e0d97c9bSxsa  * Permission to use, copy, modify, and distribute this software for any
7e0d97c9bSxsa  * purpose with or without fee is hereby granted, provided that the above
8e0d97c9bSxsa  * copyright notice and this permission notice appear in all copies.
9e0d97c9bSxsa  *
10e0d97c9bSxsa  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11e0d97c9bSxsa  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12e0d97c9bSxsa  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13e0d97c9bSxsa  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14e0d97c9bSxsa  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15e0d97c9bSxsa  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16e0d97c9bSxsa  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17e0d97c9bSxsa  */
18e0d97c9bSxsa 
191f8531bdSotto #include <sys/param.h>
201f8531bdSotto #include <sys/dirent.h>
215e4c4390Stobias 
225e4c4390Stobias #include <stdlib.h>
235e4c4390Stobias #include <string.h>
245e4c4390Stobias #include <time.h>
251f8531bdSotto #include <unistd.h>
26e0d97c9bSxsa 
27e0d97c9bSxsa #include "cvs.h"
28e0d97c9bSxsa #include "remote.h"
29e0d97c9bSxsa 
30e0d97c9bSxsa void	cvs_annotate_local(struct cvs_file *);
31e0d97c9bSxsa 
3237cbc181Stobias extern char	*cvs_specified_tag;
3337cbc181Stobias 
34e0d97c9bSxsa static int	 force_head = 0;
35e0d97c9bSxsa 
36e0d97c9bSxsa struct cvs_cmd cvs_cmd_annotate = {
37*f331ff59Stobias 	CVS_OP_ANNOTATE, CVS_USE_WDIR, "annotate",
38e0d97c9bSxsa 	{ "ann", "blame" },
39e0d97c9bSxsa 	"Show last revision where each line was modified",
40e0d97c9bSxsa 	"[-flR] [-D date | -r rev] [file ...]",
41e0d97c9bSxsa 	"D:flRr:",
42e0d97c9bSxsa 	NULL,
43e0d97c9bSxsa 	cvs_annotate
44e0d97c9bSxsa };
45e0d97c9bSxsa 
46e0d97c9bSxsa int
47e0d97c9bSxsa cvs_annotate(int argc, char **argv)
48e0d97c9bSxsa {
49e0d97c9bSxsa 	int ch, flags;
50e0d97c9bSxsa 	char *arg = ".";
51e0d97c9bSxsa 	struct cvs_recursion cr;
52e0d97c9bSxsa 
53e0d97c9bSxsa 	flags = CR_RECURSE_DIRS;
54e0d97c9bSxsa 
55e0d97c9bSxsa 	while ((ch = getopt(argc, argv, cvs_cmd_annotate.cmd_opts)) != -1) {
56e0d97c9bSxsa 		switch (ch) {
57e0d97c9bSxsa 		case 'D':
58e0d97c9bSxsa 			break;
59e0d97c9bSxsa 		case 'f':
60e0d97c9bSxsa 			force_head = 1;
61e0d97c9bSxsa 			break;
62e0d97c9bSxsa 		case 'l':
63e0d97c9bSxsa 			flags &= ~CR_RECURSE_DIRS;
64e0d97c9bSxsa 			break;
65e0d97c9bSxsa 		case 'R':
66bcf22459Stobias 			flags |= CR_RECURSE_DIRS;
67e0d97c9bSxsa 			break;
68e0d97c9bSxsa 		case 'r':
69efaa2accStobias 			cvs_specified_tag = optarg;
70e0d97c9bSxsa 			break;
71e0d97c9bSxsa 		default:
72e0d97c9bSxsa 			fatal("%s", cvs_cmd_annotate.cmd_synopsis);
73e0d97c9bSxsa 		}
74e0d97c9bSxsa 	}
75e0d97c9bSxsa 
76e0d97c9bSxsa 	argc -= optind;
77e0d97c9bSxsa 	argv += optind;
78e0d97c9bSxsa 
79e0d97c9bSxsa 	cr.enterdir = NULL;
80e0d97c9bSxsa 	cr.leavedir = NULL;
81e0d97c9bSxsa 
82e0d97c9bSxsa 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
8380f6ca9bSjoris 		cvs_client_connect_to_server();
84e0d97c9bSxsa 		cr.fileproc = cvs_client_sendfile;
85e0d97c9bSxsa 
86e0d97c9bSxsa 		if (force_head == 1)
87e0d97c9bSxsa 			cvs_client_send_request("Argument -f");
88e0d97c9bSxsa 
89e0d97c9bSxsa 		if (!(flags & CR_RECURSE_DIRS))
90e0d97c9bSxsa 			cvs_client_send_request("Argument -l");
91e0d97c9bSxsa 
92efaa2accStobias 		if (cvs_specified_tag != NULL)
93efaa2accStobias 			cvs_client_send_request("Argument -r%s",
94efaa2accStobias 			    cvs_specified_tag);
95e0d97c9bSxsa 	} else {
96e0d97c9bSxsa 		cr.fileproc = cvs_annotate_local;
97e0d97c9bSxsa 	}
98e0d97c9bSxsa 
99e0d97c9bSxsa 	cr.flags = flags;
100e0d97c9bSxsa 
101e0d97c9bSxsa 	if (argc > 0)
102e0d97c9bSxsa 		cvs_file_run(argc, argv, &cr);
103e0d97c9bSxsa 	else
104e0d97c9bSxsa 		cvs_file_run(1, &arg, &cr);
105e0d97c9bSxsa 
106e0d97c9bSxsa 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
107e0d97c9bSxsa 		cvs_client_send_files(argv, argc);
108e0d97c9bSxsa 		cvs_client_senddir(".");
109e0d97c9bSxsa 		cvs_client_send_request("annotate");
110e0d97c9bSxsa 		cvs_client_get_responses();
111e0d97c9bSxsa 	}
112e0d97c9bSxsa 
113e0d97c9bSxsa 	return (0);
114e0d97c9bSxsa }
115e0d97c9bSxsa 
116e0d97c9bSxsa void
117e0d97c9bSxsa cvs_annotate_local(struct cvs_file *cf)
118e0d97c9bSxsa {
1195e4c4390Stobias 	int i;
1205e4c4390Stobias 	char date[10], rnum[13], *p;
12137cbc181Stobias 	RCSNUM *bnum, *rev;
122efaa2accStobias 	struct cvs_line *line;
1235e4c4390Stobias 	struct cvs_line **alines;
1245e4c4390Stobias 
125e0d97c9bSxsa 	cvs_log(LP_TRACE, "cvs_annotate_local(%s)", cf->file_path);
126e0d97c9bSxsa 
12751ef6581Sjoris 	cvs_file_classify(cf, cvs_directory_tag);
128e0d97c9bSxsa 
1295e4c4390Stobias 	if (cf->file_status == FILE_UNKNOWN || cf->file_status == FILE_UNLINK ||
1305e4c4390Stobias 	    cf->file_type != CVS_FILE)
131e0d97c9bSxsa 		return;
132e0d97c9bSxsa 
13337cbc181Stobias 	if (cvs_specified_tag != NULL) {
134f1f4c0cfStobias 		if ((rev = rcs_translate_tag(cvs_specified_tag,
135f1f4c0cfStobias 		    cf->file_rcs)) == NULL) {
136f1f4c0cfStobias 			if (!force_head)
137f1f4c0cfStobias 				/* Stick at weird GNU cvs, ignore error. */
138f1f4c0cfStobias 				return;
13937cbc181Stobias 
140f1f4c0cfStobias 			rev = rcsnum_alloc();
141f1f4c0cfStobias 			rcsnum_cpy(cf->file_rcs->rf_head, rev, 0);
142f1f4c0cfStobias 		}
1435e4c4390Stobias 
14437cbc181Stobias 		/*
14537cbc181Stobias 		 * If this is a revision in a branch, we have to go first
14637cbc181Stobias 		 * from HEAD to branch, then down to 1.1. After that, take
14737cbc181Stobias 		 * annotated branch and go up to branch revision. This must
14837cbc181Stobias 		 * be done this way due to different handling of "a" and
14937cbc181Stobias 		 * "d" in rcs file for annotation.
15037cbc181Stobias 		 */
15137cbc181Stobias 		if (!RCSNUM_ISBRANCHREV(rev)) {
15237cbc181Stobias 			bnum = rev;
15337cbc181Stobias 		} else {
15437cbc181Stobias 			bnum = rcsnum_alloc();
15537cbc181Stobias 			rcsnum_cpy(rev, bnum, 2);
1565e4c4390Stobias 		}
15737cbc181Stobias 
15837cbc181Stobias 		rcs_rev_getlines(cf->file_rcs, bnum, &alines);
15937cbc181Stobias 
16037cbc181Stobias 		/*
16137cbc181Stobias 		 * Go into branch and receive annotations for branch revision,
16237cbc181Stobias 		 * with inverted "a" and "d" meaning.
16337cbc181Stobias 		 */
16437cbc181Stobias 		if (bnum != rev) {
16537cbc181Stobias 			rcs_annotate_getlines(cf->file_rcs, rev, &alines);
16637cbc181Stobias 			rcsnum_free(bnum);
1675e4c4390Stobias 		}
168efaa2accStobias 		rcsnum_free(rev);
16937cbc181Stobias 	} else {
17037cbc181Stobias 		rcs_rev_getlines(cf->file_rcs, cf->file_rcs->rf_head, &alines);
1715e4c4390Stobias 	}
1725e4c4390Stobias 
1735e4c4390Stobias 	/* Stick at weird GNU cvs, ignore error. */
1745e4c4390Stobias 	if (alines == NULL)
1755e4c4390Stobias 		return;
1765e4c4390Stobias 
1775e4c4390Stobias 	cvs_log(LP_RCS, "Annotations for %s", cf->file_path);
1785e4c4390Stobias 	cvs_log(LP_RCS, "***************");
1795e4c4390Stobias 
1805e4c4390Stobias 	for (i = 0; alines[i] != NULL; i++) {
181efaa2accStobias 		line = alines[i];
1825e4c4390Stobias 
183efaa2accStobias 		rcsnum_tostr(line->l_delta->rd_num, rnum, sizeof(rnum));
184efaa2accStobias 		strftime(date, sizeof(date), "%d-%b-%y",
185efaa2accStobias 		    &(line->l_delta->rd_date));
186efaa2accStobias 		if (line->l_len && line->l_line[line->l_len - 1] == '\n')
187efaa2accStobias 			line->l_line[line->l_len - 1] = '\0';
188efaa2accStobias 		else {
189efaa2accStobias 			p = xmalloc(line->l_len + 1);
190efaa2accStobias 			memcpy(p, line->l_line, line->l_len);
191efaa2accStobias 			p[line->l_len] = '\0';
192efaa2accStobias 
193efaa2accStobias 			if (line->l_needsfree)
194efaa2accStobias 				xfree(line->l_line);
195efaa2accStobias 			line->l_line = p;
196efaa2accStobias 			line->l_len++;
197efaa2accStobias 			line->l_needsfree = 1;
1985e4c4390Stobias 		}
1995e4c4390Stobias 		cvs_printf("%-12.12s (%-8.8s %s): %s\n", rnum,
200efaa2accStobias 		    line->l_delta->rd_author, date, line->l_line);
2015e4c4390Stobias 
202efaa2accStobias 		if (line->l_needsfree)
203efaa2accStobias 			xfree(line->l_line);
204efaa2accStobias 		xfree(line);
2055e4c4390Stobias 	}
2065e4c4390Stobias 
2075e4c4390Stobias 	xfree(alines);
208e0d97c9bSxsa }
209