rlog.c revision 1.56
1/*	$OpenBSD: rlog.c,v 1.56 2006/10/12 17:20:12 niallo 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
33static void	rlog_file(const char *, RCSFILE *);
34static 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
41static int hflag, Lflag, lflag, rflag, tflag, Nflag, wflag;
42static char *llist = NULL;
43static char *slist = NULL;
44static char *wlist = NULL;
45static char *revisions = NULL;
46
47void
48rlog_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
56int
57rlog_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			warn("%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
168static void
169rlog_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
250static void
251rlog_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