xref: /openbsd/usr.bin/rcs/rlog.c (revision f3876e23)
1 /*	$OpenBSD: rlog.c,v 1.55 2006/05/27 05:49:14 ray Exp $	*/
2 /*
3  * Copyright (c) 2005 Joris Vink <joris@openbsd.org>
4  * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
19  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "includes.h"
29 
30 #include "rcsprog.h"
31 #include "diff.h"
32 
33 static void	rlog_file(const char *, RCSFILE *);
34 static void	rlog_rev_print(struct rcs_delta *);
35 
36 #define RLOG_OPTSTRING	"hLl::NqRr::s:TtVw::x::z::"
37 #define REVSEP		"----------------------------"
38 #define REVEND \
39  "============================================================================="
40 
41 static int hflag, Lflag, lflag, rflag, tflag, Nflag, wflag;
42 static char *llist = NULL;
43 static char *slist = NULL;
44 static char *wlist = NULL;
45 static char *revisions = NULL;
46 
47 void
48 rlog_usage(void)
49 {
50 	fprintf(stderr,
51 	    "usage: rlog [-bhLNqRtV] [-ddates] [-l[lockers]] [-r[revs]]\n"
52 	    "            [-sstates] [-w[logins]] [-xsuffixes]\n"
53 	    "            [-ztz] file ...\n");
54 }
55 
56 int
57 rlog_main(int argc, char **argv)
58 {
59 	RCSFILE *file;
60 	int Rflag;
61 	int i, ch, fd;
62 	char fpath[MAXPATHLEN];
63 
64 	rcsnum_flags |= RCSNUM_NO_MAGIC;
65 	hflag = Rflag = rflag = 0;
66 	while ((ch = rcs_getopt(argc, argv, RLOG_OPTSTRING)) != -1) {
67 		switch (ch) {
68 		case 'h':
69 			hflag = 1;
70 			break;
71 		case 'L':
72 			Lflag = 1;
73 			break;
74 		case 'l':
75 			lflag = 1;
76 			llist = rcs_optarg;
77 			break;
78 		case 'N':
79 			Nflag = 1;
80 			break;
81 		case 'q':
82 			/*
83 			 * kept for compatibility
84 			 */
85 			break;
86 		case 'R':
87 			Rflag = 1;
88 			break;
89 		case 'r':
90 			rflag = 1;
91 			revisions = rcs_optarg;
92 			break;
93 		case 's':
94 			slist = rcs_optarg;
95 			break;
96 		case 'T':
97 			/*
98 			 * kept for compatibility
99 			 */
100 			break;
101 		case 't':
102 			tflag = 1;
103 			break;
104 		case 'V':
105 			printf("%s\n", rcs_version);
106 			exit(0);
107 		case 'w':
108 			wflag = 1;
109 			wlist = rcs_optarg;
110 			break;
111 		case 'x':
112 			/* Use blank extension if none given. */
113 			rcs_suffixes = rcs_optarg ? rcs_optarg : "";
114 			break;
115 		case 'z':
116 			timezone_flag = rcs_optarg;
117 			break;
118 		default:
119 			(usage());
120 			exit(1);
121 		}
122 	}
123 
124 	argc -= rcs_optind;
125 	argv += rcs_optind;
126 
127 	if (argc == 0) {
128 		warnx("no input file");
129 		(usage)();
130 		exit(1);
131 	}
132 
133 	if (hflag == 1 && tflag == 1) {
134 		warnx("warning: -t overrides -h.");
135 		hflag = 0;
136 	}
137 
138 	for (i = 0; i < argc; i++) {
139 		fd = rcs_choosefile(argv[i], fpath, sizeof(fpath));
140 		if (fd < 0) {
141 			warnx("%s", fpath);
142 			continue;
143 		}
144 
145 		if ((file = rcs_open(fpath, fd,
146 		    RCS_READ|RCS_PARSE_FULLY)) == NULL)
147 			continue;
148 
149 		if (Lflag == 1 && TAILQ_EMPTY(&(file->rf_locks))) {
150 			rcs_close(file);
151 			continue;
152 		}
153 
154 		if (Rflag == 1) {
155 			printf("%s\n", fpath);
156 			rcs_close(file);
157 			continue;
158 		}
159 
160 		rlog_file(argv[i], file);
161 
162 		rcs_close(file);
163 	}
164 
165 	return (0);
166 }
167 
168 static void
169 rlog_file(const char *fname, RCSFILE *file)
170 {
171 	char numb[64];
172 	u_int nrev;
173 	struct rcs_sym *sym;
174 	struct rcs_access *acp;
175 	struct rcs_delta *rdp;
176 	struct rcs_lock *lkp;
177 	char *workfile, *p;
178 
179 	if (rflag == 1)
180 		nrev = rcs_rev_select(file, revisions);
181 	else
182 		nrev = file->rf_ndelta;
183 
184 	if ((workfile = basename(fname)) == NULL)
185 		err(1, "basename");
186 
187 	/*
188 	 * In case they specified 'foo,v' as argument.
189 	 */
190 	if ((p = strrchr(workfile, ',')) != NULL)
191 		*p = '\0';
192 
193 	printf("\nRCS file: %s", file->rf_path);
194 	printf("\nWorking file: %s", workfile);
195 	printf("\nhead:");
196 	if (file->rf_head != NULL)
197 		printf(" %s", rcsnum_tostr(file->rf_head, numb, sizeof(numb)));
198 
199 	printf("\nbranch:");
200 	if (rcs_branch_get(file) != NULL) {
201 		printf(" %s", rcsnum_tostr(rcs_branch_get(file),
202 		    numb, sizeof(numb)));
203 	}
204 
205 	printf("\nlocks: %s", (file->rf_flags & RCS_SLOCK) ? "strict" : "");
206 	TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list)
207 		printf("\n\t%s: %s", lkp->rl_name,
208 		    rcsnum_tostr(lkp->rl_num, numb, sizeof(numb)));
209 	printf("\naccess list:\n");
210 	TAILQ_FOREACH(acp, &(file->rf_access), ra_list)
211 		printf("\t%s\n", acp->ra_name);
212 
213 	if (Nflag == 0) {
214 		printf("symbolic names:\n");
215 		TAILQ_FOREACH(sym, &(file->rf_symbols), rs_list) {
216 			printf("\t%s: %s\n", sym->rs_name,
217 			    rcsnum_tostr(sym->rs_num, numb, sizeof(numb)));
218 		}
219 	}
220 
221 	printf("keyword substitution: %s\n",
222 	    file->rf_expand == NULL ? "kv" : file->rf_expand);
223 
224 	printf("total revisions: %u", file->rf_ndelta);
225 
226 	if (file->rf_head != NULL && hflag == 0 && tflag == 0)
227 		printf(";\tselected revisions: %u", nrev);
228 
229 	printf("\n");
230 
231 
232 	if (hflag == 0 || tflag == 1)
233 		printf("description:\n%s", file->rf_desc);
234 
235 	if (hflag == 0 && tflag == 0 &&
236 	    !(lflag == 1 && TAILQ_EMPTY(&file->rf_locks))) {
237 		TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
238 			/*
239 			 * if selections are enabled verify that entry is
240 			 * selected.
241 			 */
242 			if (rflag == 0 || (rdp->rd_flags & RCS_RD_SELECT))
243 				rlog_rev_print(rdp);
244 		}
245 	}
246 
247 	printf("%s\n", REVEND);
248 }
249 
250 static void
251 rlog_rev_print(struct rcs_delta *rdp)
252 {
253 	int i, found;
254 	struct tm t;
255 	char *author, numb[64], *fmt, timeb[64];
256 	struct rcs_argvector *largv, *sargv, *wargv;
257 
258 	i = found = 0;
259 	author = NULL;
260 
261 	/* -l[lockers] */
262 	if (lflag == 1) {
263 		if (rdp->rd_locker != NULL)
264 			found++;
265 
266 		if (llist != NULL) {
267 			/* if locker is empty, no need to go further. */
268 			if (rdp->rd_locker == NULL)
269 				return;
270 			largv = rcs_strsplit(llist, ",");
271 			for (i = 0; largv->argv[i] != NULL; i++) {
272 				if (strcmp(rdp->rd_locker, largv->argv[i])
273 				    == 0) {
274 					found++;
275 					break;
276 				}
277 				found = 0;
278 			}
279 			rcs_argv_destroy(largv);
280 		}
281 	}
282 
283 	/* -sstates */
284 	if (slist != NULL) {
285 		sargv = rcs_strsplit(slist, ",");
286 		for (i = 0; sargv->argv[i] != NULL; i++) {
287 			if (strcmp(rdp->rd_state, sargv->argv[i]) == 0) {
288 				found++;
289 				break;
290 			}
291 			found = 0;
292 		}
293 		rcs_argv_destroy(sargv);
294 	}
295 
296 	/* -w[logins] */
297 	if (wflag == 1) {
298 		if (wlist != NULL) {
299 			wargv = rcs_strsplit(wlist, ",");
300 			for (i = 0; wargv->argv[i] != NULL; i++) {
301 				if (strcmp(rdp->rd_author, wargv->argv[i])
302 				    == 0) {
303 					found++;
304 					break;
305 				}
306 				found = 0;
307 			}
308 			rcs_argv_destroy(wargv);
309 		} else {
310 			if ((author = getlogin()) == NULL)
311 				err(1, "getlogin");
312 
313 			if (strcmp(rdp->rd_author, author) == 0)
314 				found++;
315 		}
316 	}
317 
318 	/* XXX dirty... */
319 	if ((((slist != NULL && wflag == 1) ||
320 	    (slist != NULL && lflag == 1) ||
321 	    (lflag == 1 && wflag == 1)) && found < 2) ||
322 	    (((slist != NULL && lflag == 1 && wflag == 1) ||
323 	    (slist != NULL || lflag == 1 || wflag == 1)) && found == 0))
324 		return;
325 
326 	printf("%s\n", REVSEP);
327 
328 	rcsnum_tostr(rdp->rd_num, numb, sizeof(numb));
329 
330 	printf("revision %s", numb);
331 	if (rdp->rd_locker != NULL)
332 		printf("\tlocked by: %s;", rdp->rd_locker);
333 
334 	if (timezone_flag != NULL) {
335 		rcs_set_tz(timezone_flag, rdp, &t);
336 		fmt = "%Y-%m-%d %H:%M:%S%z";
337 	} else {
338 		t = rdp->rd_date;
339 		fmt = "%Y/%m/%d %H:%M:%S";
340 	}
341 
342 	strftime(timeb, sizeof(timeb), fmt, &t);
343 
344 	printf("\ndate: %s;  author: %s;  state: %s;\n", timeb, rdp->rd_author,
345 	    rdp->rd_state);
346 
347 	printf("%s", rdp->rd_log);
348 }
349