GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/cvs/annotate.c Lines: 63 107 58.9 %
Date: 2017-11-07 Branches: 29 68 42.6 %

Line Branch Exec Source
1
/*	$OpenBSD: annotate.c,v 1.69 2017/06/01 08:08:24 joris Exp $	*/
2
/*
3
 * Copyright (c) 2007 Tobias Stoeckmann <tobias@openbsd.org>
4
 * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/dirent.h>
21
22
#include <errno.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <time.h>
26
#include <unistd.h>
27
28
#include "cvs.h"
29
#include "remote.h"
30
31
void	cvs_annotate_local(struct cvs_file *);
32
33
extern char	*cvs_specified_tag;
34
35
static int	 force_head = 0;
36
37
struct cvs_cmd cvs_cmd_annotate = {
38
	CVS_OP_ANNOTATE, CVS_USE_WDIR, "annotate",
39
	{ "ann", "blame" },
40
	"Show last revision where each line was modified",
41
	"[-flR] [-D date | -r rev] [file ...]",
42
	"D:flRr:",
43
	NULL,
44
	cvs_annotate
45
};
46
47
struct cvs_cmd cvs_cmd_rannotate = {
48
	CVS_OP_RANNOTATE, 0, "rannotate",
49
	{ "rann", "ra" },
50
	"Show last revision where each line was modified",
51
	"[-flR] [-D date | -r rev] module ...",
52
	"D:flRr:",
53
	NULL,
54
	cvs_annotate
55
};
56
57
int
58
cvs_annotate(int argc, char **argv)
59
{
60
	int ch, flags;
61
8
	char *arg = ".";
62
	char *dateflag = NULL;
63
4
	struct cvs_recursion cr;
64
65
	flags = CR_RECURSE_DIRS;
66
67
22
	while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_ANNOTATE ?
68
9
	    cvs_cmd_annotate.cmd_opts : cvs_cmd_rannotate.cmd_opts)) != -1) {
69

5
		switch (ch) {
70
		case 'D':
71
			dateflag = optarg;
72
			if ((cvs_specified_date = date_parse(dateflag)) == -1)
73
				fatal("invalid date: %s", dateflag);
74
			break;
75
		case 'f':
76
2
			force_head = 1;
77
2
			break;
78
		case 'l':
79
			flags &= ~CR_RECURSE_DIRS;
80
			break;
81
		case 'R':
82
			flags |= CR_RECURSE_DIRS;
83
			break;
84
		case 'r':
85
3
			cvs_specified_tag = optarg;
86
3
			break;
87
		default:
88
			fatal("%s", cvs_cmdop == CVS_OP_ANNOTATE ?
89
			    cvs_cmd_annotate.cmd_synopsis :
90
			    cvs_cmd_rannotate.cmd_synopsis);
91
		}
92
	}
93
94
4
	argc -= optind;
95
4
	argv += optind;
96
97
4
	if (cvs_cmdop == CVS_OP_RANNOTATE) {
98
		if (argc == 0)
99
			fatal("%s", cvs_cmd_rannotate.cmd_synopsis);
100
101
		flags |= CR_REPO;
102
	}
103
104
4
	cr.enterdir = NULL;
105
4
	cr.leavedir = NULL;
106
107
4
	if (cvsroot_is_remote()) {
108
		cvs_client_connect_to_server();
109
		cr.fileproc = cvs_client_sendfile;
110
111
		if (dateflag != NULL)
112
			cvs_client_send_request("Argument -D%s", dateflag);
113
114
		if (force_head == 1)
115
			cvs_client_send_request("Argument -f");
116
117
		if (!(flags & CR_RECURSE_DIRS))
118
			cvs_client_send_request("Argument -l");
119
120
		if (cvs_specified_tag != NULL)
121
			cvs_client_send_request("Argument -r%s",
122
			    cvs_specified_tag);
123
	} else {
124

4
		if (cvs_cmdop == CVS_OP_RANNOTATE &&
125
		    chdir(current_cvsroot->cr_dir) == -1)
126
			fatal("cvs_annotate: %s", strerror(errno));
127
128
4
		cr.fileproc = cvs_annotate_local;
129
	}
130
131
4
	cr.flags = flags;
132
133

4
	if (cvs_cmdop == CVS_OP_ANNOTATE || cvsroot_is_local()) {
134
4
		if (argc > 0)
135
4
			cvs_file_run(argc, argv, &cr);
136
		else
137
			cvs_file_run(1, &arg, &cr);
138
	}
139
140
3
	if (cvsroot_is_remote()) {
141
		cvs_client_send_files(argv, argc);
142
		cvs_client_senddir(".");
143
144
		cvs_client_send_request((cvs_cmdop == CVS_OP_RANNOTATE) ?
145
		    "rannotate" : "annotate");
146
147
		cvs_client_get_responses();
148
	}
149
150
3
	return (0);
151
3
}
152
153
void
154
cvs_annotate_local(struct cvs_file *cf)
155
{
156
	int i;
157
	u_char *p;
158
8
	char date[10], rnum[13];
159
	RCSNUM *bnum, *rev;
160
	struct rcs_line *line;
161
4
	struct rcs_line **alines;
162
163
4
	cvs_log(LP_TRACE, "cvs_annotate_local(%s)", cf->file_path);
164
165
4
	cvs_file_classify(cf, cvs_directory_tag);
166
167

8
	if (cf->file_rcs == NULL || cf->file_rcs->rf_head == NULL)
168
		return;
169
170
4
	if (cvs_specified_tag != NULL) {
171
6
		if ((rev = rcs_translate_tag(cvs_specified_tag,
172
3
		    cf->file_rcs)) == NULL) {
173
3
			if (!force_head)
174
				/* Stick at weird GNU cvs, ignore error. */
175
1
				return;
176
177
			/* -f is not allowed for unknown symbols */
178
2
			rev = rcsnum_parse(cvs_specified_tag);
179
2
			if (rev == NULL)
180
				fatal("no such tag %s", cvs_specified_tag);
181
1
			free(rev);
182
1
			rev = rcsnum_alloc();
183
1
			rcsnum_cpy(cf->file_rcs->rf_head, rev, 0);
184
1
		}
185
186
		/*
187
		 * If this is a revision in a branch, we have to go first
188
		 * from HEAD to branch, then down to 1.1. After that, take
189
		 * annotated branch and go up to branch revision. This must
190
		 * be done this way due to different handling of "a" and
191
		 * "d" in rcs file for annotation.
192
		 */
193

2
		if (!RCSNUM_ISBRANCHREV(rev)) {
194
			bnum = rev;
195
1
		} else {
196
			bnum = rcsnum_alloc();
197
			rcsnum_cpy(rev, bnum, 2);
198
		}
199
200
1
		rcs_rev_getlines(cf->file_rcs, bnum, &alines);
201
202
		/*
203
		 * Go into branch and receive annotations for branch revision,
204
		 * with inverted "a" and "d" meaning.
205
		 */
206
1
		if (bnum != rev) {
207
			rcs_annotate_getlines(cf->file_rcs, rev, &alines);
208
			free(bnum);
209
		}
210
1
		free(rev);
211
1
	} else {
212
4
		rcs_rev_getlines(cf->file_rcs, (cvs_specified_date != -1 ||
213
1
		    cvs_directory_date != -1) ? cf->file_rcsrev :
214
		    cf->file_rcs->rf_head, &alines);
215
	}
216
217
	/* Stick at weird GNU cvs, ignore error. */
218
2
	if (alines == NULL)
219
		return;
220
221
2
	cvs_log(LP_RCS, "Annotations for %s", cf->file_path);
222
2
	cvs_log(LP_RCS, "***************");
223
224
16
	for (i = 0; alines[i] != NULL; i++) {
225
		line = alines[i];
226
227
6
		rcsnum_tostr(line->l_delta->rd_num, rnum, sizeof(rnum));
228
12
		strftime(date, sizeof(date), "%d-%b-%y",
229
6
		    &(line->l_delta->rd_date));
230

12
		if (line->l_len && line->l_line[line->l_len - 1] == '\n')
231
6
			line->l_line[line->l_len - 1] = '\0';
232
		else {
233
			p = xmalloc(line->l_len + 1);
234
			memcpy(p, line->l_line, line->l_len);
235
			p[line->l_len] = '\0';
236
237
			if (line->l_needsfree)
238
				free(line->l_line);
239
			line->l_line = p;
240
			line->l_len++;
241
			line->l_needsfree = 1;
242
		}
243
6
		cvs_printf("%-12.12s (%-8.8s %s): %s\n", rnum,
244
6
		    line->l_delta->rd_author, date, line->l_line);
245
246
6
		if (line->l_needsfree)
247
			free(line->l_line);
248
6
		free(line);
249
	}
250
251
2
	free(alines);
252
5
}