xref: /openbsd-src/usr.bin/cvs/getlog.c (revision 799f675f6700f14e59124f9825c723e9f2ce19dc)
1 /*	$OpenBSD: getlog.c,v 1.69 2007/01/11 02:35:55 joris Exp $	*/
2 /*
3  * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org>
4  * Copyright (c) 2006 Joris Vink <joris@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 "includes.h"
20 
21 #include "cvs.h"
22 #include "diff.h"
23 #include "log.h"
24 #include "remote.h"
25 
26 #define LOG_REVSEP \
27 "----------------------------"
28 
29 #define LOG_REVEND \
30  "============================================================================="
31 
32 #define L_HEAD		0x01
33 #define L_HEAD_DESCR	0x02
34 #define L_NAME		0x04
35 #define L_NOTAGS	0x08
36 #define L_LOGINS	0x10
37 #define L_STATES	0x20
38 
39 void	cvs_log_local(struct cvs_file *);
40 
41 static void	log_rev_print(struct rcs_delta *);
42 
43 int	 runflags = 0;
44 char 	*logrev = NULL;
45 char	*slist = NULL;
46 char	*wlist = NULL;
47 
48 struct cvs_cmd cvs_cmd_log = {
49 	CVS_OP_LOG, 0, "log",
50 	{ "lo" },
51 	"Print out history information for files",
52 	"[-bhlNRt] [-d dates] [-r revisions] [-s states] [-w logins]",
53 	"bd:hlNRr:s:tw:",
54 	NULL,
55 	cvs_getlog
56 };
57 
58 int
59 cvs_getlog(int argc, char **argv)
60 {
61 	int ch;
62 	int flags;
63 	char *arg = ".";
64 	struct cvs_recursion cr;
65 
66 	rcsnum_flags |= RCSNUM_NO_MAGIC;
67 	flags = CR_RECURSE_DIRS;
68 
69 	while ((ch = getopt(argc, argv, cvs_cmd_log.cmd_opts)) != -1) {
70 		switch (ch) {
71 		case 'h':
72 			runflags |= L_HEAD;
73 			break;
74 		case 'l':
75 			flags &= ~CR_RECURSE_DIRS;
76 			break;
77 		case 'N':
78 			runflags |= L_NOTAGS;
79 			break;
80 		case 'R':
81 			runflags |= L_NAME;
82 		case 'r':
83 			logrev = optarg;
84 			break;
85 		case 's':
86 			runflags |= L_STATES;
87 			slist = optarg;
88 			break;
89 		case 't':
90 			runflags |= L_HEAD_DESCR;
91 			break;
92 		case 'w':
93 			runflags |= L_LOGINS;
94 			wlist = optarg;
95 			break;
96 		default:
97 			fatal("%s", cvs_cmd_log.cmd_synopsis);
98 		}
99 	}
100 
101 	argc -= optind;
102 	argv += optind;
103 
104 	cr.enterdir = NULL;
105 	cr.leavedir = NULL;
106 
107 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
108 		cvs_client_connect_to_server();
109 		cr.fileproc = cvs_client_sendfile;
110 
111 		if (runflags & L_HEAD)
112 			cvs_client_send_request("Argument -h");
113 
114 		if (!(flags & CR_RECURSE_DIRS))
115 			cvs_client_send_request("Argument -l");
116 
117 		if (runflags & L_NOTAGS)
118 			cvs_client_send_request("Argument -N");
119 
120 		if (runflags & L_NAME)
121 			cvs_client_send_request("Argument -R");
122 
123 		if (logrev != NULL)
124 			cvs_client_send_request("Argument -r%s", logrev);
125 
126 		if (runflags & L_STATES)
127 			cvs_client_send_request("Argument -s%s", slist);
128 
129 		if (runflags & L_HEAD_DESCR)
130 			cvs_client_send_request("Argument -t");
131 
132 		if (runflags & L_LOGINS)
133 			cvs_client_send_request("Argument -w%s", wlist);
134 	} else {
135 		cr.fileproc = cvs_log_local;
136 	}
137 
138 	cr.flags = flags;
139 
140 	if (argc > 0)
141 		cvs_file_run(argc, argv, &cr);
142 	else
143 		cvs_file_run(1, &arg, &cr);
144 
145 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
146 		cvs_client_send_files(argv, argc);
147 		cvs_client_senddir(".");
148 		cvs_client_send_request("log");
149 		cvs_client_get_responses();
150 	}
151 
152 	return (0);
153 }
154 
155 void
156 cvs_log_local(struct cvs_file *cf)
157 {
158 	u_int nrev;
159 	struct rcs_sym *sym;
160 	struct rcs_lock *lkp;
161 	struct rcs_delta *rdp;
162 	struct rcs_access *acp;
163 	char numb[32];
164 
165 	cvs_log(LP_TRACE, "cvs_log_local(%s)", cf->file_path);
166 
167 	cvs_file_classify(cf, NULL, 0);
168 
169 	if (cf->file_status == FILE_UNKNOWN) {
170 		if (verbosity > 0)
171 			cvs_log(LP_ERR, "nothing known about %s",
172 			    cf->file_path);
173 		return;
174 	} else if (cf->file_status == FILE_ADDED) {
175 		if (verbosity > 0)
176 			cvs_log(LP_ERR, "%s has been added, but not committed",
177 			    cf->file_path);
178 		return;
179 	}
180 
181 	if (cf->file_type == CVS_DIR) {
182 		if (verbosity > 1)
183 			cvs_log(LP_NOTICE, "Logging %s", cf->file_path);
184 		return;
185 	}
186 
187 	if (runflags & L_NAME) {
188 		cvs_printf("%s\n", cf->file_rpath);
189 		return;
190 	}
191 
192 	cvs_printf("\nRCS file: %s", cf->file_rpath);
193 	cvs_printf("\nWorking file: %s", cf->file_path);
194 	cvs_printf("\nhead:");
195 	if (cf->file_rcs->rf_head != NULL)
196 		cvs_printf(" %s", rcsnum_tostr(cf->file_rcs->rf_head,
197 		    numb, sizeof(numb)));
198 
199 	cvs_printf("\nbranch:");
200 	if (rcs_branch_get(cf->file_rcs) != NULL) {
201 		cvs_printf(" %s", rcsnum_tostr(rcs_branch_get(cf->file_rcs),
202 		    numb, sizeof(numb)));
203 	}
204 
205 	cvs_printf("\nlocks: %s", (cf->file_rcs->rf_flags & RCS_SLOCK)
206 	    ? "strict" : "");
207 	TAILQ_FOREACH(lkp, &(cf->file_rcs->rf_locks), rl_list)
208 		cvs_printf("\n\t%s: %s", lkp->rl_name,
209 		    rcsnum_tostr(lkp->rl_num, numb, sizeof(numb)));
210 
211 	cvs_printf("\naccess list:\n");
212 	TAILQ_FOREACH(acp, &(cf->file_rcs->rf_access), ra_list)
213 		cvs_printf("\t%s\n", acp->ra_name);
214 
215 	if (!(runflags & L_NOTAGS)) {
216 		cvs_printf("symbolic names:\n");
217 		TAILQ_FOREACH(sym, &(cf->file_rcs->rf_symbols), rs_list) {
218 			cvs_printf("\t%s: %s\n", sym->rs_name,
219 			    rcsnum_tostr(sym->rs_num, numb, sizeof(numb)));
220 		}
221 	}
222 
223 	cvs_printf("keyword substitution: %s\n",
224 	    cf->file_rcs->rf_expand == NULL ? "kv" : cf->file_rcs->rf_expand);
225 
226 	cvs_printf("total revisions: %u", cf->file_rcs->rf_ndelta);
227 
228 	if (logrev != NULL)
229 		nrev = cvs_revision_select(cf->file_rcs, logrev);
230 	else
231 		nrev = cf->file_rcs->rf_ndelta;
232 
233 	if (cf->file_rcs->rf_head != NULL &&
234 	    !(runflags & L_HEAD) && !(runflags & L_HEAD_DESCR))
235 		cvs_printf(";\tselected revisions: %u", nrev);
236 
237 	cvs_printf("\n");
238 
239 	if (!(runflags & L_HEAD) || (runflags & L_HEAD_DESCR))
240 		cvs_printf("description:\n%s", cf->file_rcs->rf_desc);
241 
242 	if (!(runflags & L_HEAD) && !(runflags & L_HEAD_DESCR)) {
243 		TAILQ_FOREACH(rdp, &(cf->file_rcs->rf_delta), rd_list) {
244 			/*
245 			 * if selections are enabled verify that entry is
246 			 * selected.
247 			 */
248 			if (logrev == NULL || (rdp->rd_flags & RCS_RD_SELECT))
249 				log_rev_print(rdp);
250 		}
251 	}
252 
253 	cvs_printf("%s\n", LOG_REVEND);
254 }
255 
256 static void
257 log_rev_print(struct rcs_delta *rdp)
258 {
259 	int i, found;
260 	char numb[32], timeb[32];
261 	struct cvs_argvector *sargv, *wargv;
262 
263 	i = found = 0;
264 
265 	/* -s states */
266 	if (runflags & L_STATES) {
267 		sargv = cvs_strsplit(slist, ",");
268 		for (i = 0; sargv->argv[i] != NULL; i++) {
269 			if (strcmp(rdp->rd_state, sargv->argv[i]) == 0) {
270 				found++;
271 				break;
272 			}
273 			found = 0;
274 		}
275 		cvs_argv_destroy(sargv);
276 	}
277 
278 	/* -w[logins] */
279 	if (runflags & L_LOGINS) {
280 		wargv = cvs_strsplit(wlist, ",");
281 		for (i = 0; wargv->argv[i] != NULL; i++) {
282 			if (strcmp(rdp->rd_author, wargv->argv[i]) == 0) {
283 				found++;
284 				break;
285 			}
286 			found = 0;
287 		}
288 		cvs_argv_destroy(wargv);
289 	}
290 
291 	if ((runflags & (L_STATES|L_LOGINS)) && found == 0)
292 		return;
293 
294 	cvs_printf("%s\n", LOG_REVSEP);
295 
296 	rcsnum_tostr(rdp->rd_num, numb, sizeof(numb));
297 	cvs_printf("revision %s", numb);
298 
299 	strftime(timeb, sizeof(timeb), "%Y/%m/%d %H:%M:%S", &rdp->rd_date);
300 	cvs_printf("\ndate: %s;  author: %s;  state: %s;\n",
301 	    timeb, rdp->rd_author, rdp->rd_state);
302 	cvs_printf("%s", rdp->rd_log);
303 }
304