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