xref: /openbsd-src/usr.bin/rcs/rlog.c (revision 66d939b20149a39ad35d0358df25fb9b3eaaf70f)
1*66d939b2Snaddy /*	$OpenBSD: rlog.c,v 1.75 2020/10/15 19:47:46 naddy Exp $	*/
267970ff5Sjoris /*
38d628116Sjoris  * Copyright (c) 2005, 2009 Joris Vink <joris@openbsd.org>
431a1e4b2Sxsa  * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org>
567970ff5Sjoris  * All rights reserved.
667970ff5Sjoris  *
767970ff5Sjoris  * Redistribution and use in source and binary forms, with or without
867970ff5Sjoris  * modification, are permitted provided that the following conditions
967970ff5Sjoris  * are met:
1067970ff5Sjoris  *
1167970ff5Sjoris  * 1. Redistributions of source code must retain the above copyright
1267970ff5Sjoris  *    notice, this list of conditions and the following disclaimer.
1367970ff5Sjoris  * 2. The name of the author may not be used to endorse or promote products
1467970ff5Sjoris  *    derived from this software without specific prior written permission.
1567970ff5Sjoris  *
1667970ff5Sjoris  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
1767970ff5Sjoris  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
1867970ff5Sjoris  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
1967970ff5Sjoris  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2067970ff5Sjoris  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2167970ff5Sjoris  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2267970ff5Sjoris  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2367970ff5Sjoris  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2467970ff5Sjoris  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
2567970ff5Sjoris  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2667970ff5Sjoris  */
2767970ff5Sjoris 
288d628116Sjoris #include <ctype.h>
294781e2faSxsa #include <err.h>
304781e2faSxsa #include <libgen.h>
31*66d939b2Snaddy #include <limits.h>
324781e2faSxsa #include <stdio.h>
334781e2faSxsa #include <stdlib.h>
344781e2faSxsa #include <string.h>
35099bc362Sguenther #include <time.h>
364781e2faSxsa #include <unistd.h>
3767970ff5Sjoris 
3867970ff5Sjoris #include "rcsprog.h"
39cd8dad74Sxsa #include "diff.h"
4067970ff5Sjoris 
418d628116Sjoris #define RLOG_DATE_LATER		0x01
428d628116Sjoris #define RLOG_DATE_EARLIER	0x02
438d628116Sjoris #define RLOG_DATE_SINGLE	0x04
448d628116Sjoris #define RLOG_DATE_RANGE		0x08
458d628116Sjoris #define RLOG_DATE_INCLUSIVE	0x10
468d628116Sjoris 
478d628116Sjoris static int	rlog_select_daterev(RCSFILE *, char *);
4844a7e1f3Sray static void	rlog_file(const char *, RCSFILE *);
4931a1e4b2Sxsa static void	rlog_rev_print(struct rcs_delta *);
5067970ff5Sjoris 
51119dfb76Sjcs #define RLOG_OPTSTRING	"d:E:hLl::NqRr::S:s:TtVw::x::z::"
5267970ff5Sjoris 
538d628116Sjoris static int dflag, hflag, Lflag, lflag, rflag, tflag, Nflag, wflag;
544dc9061dSxsa static char *llist = NULL;
5531a1e4b2Sxsa static char *slist = NULL;
5631a1e4b2Sxsa static char *wlist = NULL;
573c6748a1Sray static char *revisions = NULL;
588d628116Sjoris static char *rlog_dates = NULL;
59119dfb76Sjcs static char *revsep = "----------------------------";
60119dfb76Sjcs static char *revend = "====================================================="
61119dfb76Sjcs     "========================";
6267970ff5Sjoris 
63960e00beSotto __dead void
rlog_usage(void)647484a6bcSxsa rlog_usage(void)
657484a6bcSxsa {
667484a6bcSxsa 	fprintf(stderr,
67119dfb76Sjcs 	    "usage: rlog [-bhLNRtV] [-ddates] [-Eendsep] [-l[lockers]] "
68119dfb76Sjcs 	    "[-r[revs]]\n"
69119dfb76Sjcs 	    "            [-Srevsep] [-sstates] [-w[logins]] [-xsuffixes] "
7033066b9dSjmc 	    "[-ztz] file ...\n");
71960e00beSotto 
72960e00beSotto 	exit(1);
737484a6bcSxsa }
747484a6bcSxsa 
7567970ff5Sjoris int
rlog_main(int argc,char ** argv)7667970ff5Sjoris rlog_main(int argc, char **argv)
7767970ff5Sjoris {
7844a7e1f3Sray 	RCSFILE *file;
7967970ff5Sjoris 	int Rflag;
80b3e140bbSray 	int i, ch, fd, status;
81b9fc9a72Sderaadt 	char fpath[PATH_MAX];
8267970ff5Sjoris 
836d7e6850Sjoris 	rcsnum_flags |= RCSNUM_NO_MAGIC;
84b3e140bbSray 	hflag = Rflag = rflag = status = 0;
853c6748a1Sray 	while ((ch = rcs_getopt(argc, argv, RLOG_OPTSTRING)) != -1) {
8667970ff5Sjoris 		switch (ch) {
878d628116Sjoris 		case 'd':
888d628116Sjoris 			dflag = 1;
898d628116Sjoris 			rlog_dates = rcs_optarg;
908d628116Sjoris 			break;
91119dfb76Sjcs 		case 'E':
92119dfb76Sjcs 			revend = rcs_optarg;
93119dfb76Sjcs 			break;
9467970ff5Sjoris 		case 'h':
9567970ff5Sjoris 			hflag = 1;
9667970ff5Sjoris 			break;
976a77db31Sxsa 		case 'L':
986a77db31Sxsa 			Lflag = 1;
996a77db31Sxsa 			break;
1004dc9061dSxsa 		case 'l':
1014dc9061dSxsa 			lflag = 1;
1024dc9061dSxsa 			llist = rcs_optarg;
1034dc9061dSxsa 			break;
10467970ff5Sjoris 		case 'N':
10567970ff5Sjoris 			Nflag = 1;
10667970ff5Sjoris 			break;
10767970ff5Sjoris 		case 'q':
108913586deSxsa 			/*
109913586deSxsa 			 * kept for compatibility
110913586deSxsa 			 */
11167970ff5Sjoris 			break;
1124db406c7Sxsa 		case 'R':
1134db406c7Sxsa 			Rflag = 1;
1144db406c7Sxsa 			break;
1153c6748a1Sray 		case 'r':
1163c6748a1Sray 			rflag = 1;
1173c6748a1Sray 			revisions = rcs_optarg;
1183c6748a1Sray 			break;
119119dfb76Sjcs 		case 'S':
120119dfb76Sjcs 			revsep = rcs_optarg;
121119dfb76Sjcs 			break;
12231a1e4b2Sxsa 		case 's':
12331a1e4b2Sxsa 			slist = rcs_optarg;
12431a1e4b2Sxsa 			break;
12528892be7Sxsa 		case 'T':
12628892be7Sxsa 			/*
12728892be7Sxsa 			 * kept for compatibility
12828892be7Sxsa 			 */
12928892be7Sxsa 			break;
13067970ff5Sjoris 		case 't':
13167970ff5Sjoris 			tflag = 1;
13267970ff5Sjoris 			break;
13367970ff5Sjoris 		case 'V':
13467970ff5Sjoris 			printf("%s\n", rcs_version);
13567970ff5Sjoris 			exit(0);
13631a1e4b2Sxsa 		case 'w':
13731a1e4b2Sxsa 			wflag = 1;
13831a1e4b2Sxsa 			wlist = rcs_optarg;
13931a1e4b2Sxsa 			break;
1407518c1e9Sxsa 		case 'x':
1414365263eSray 			/* Use blank extension if none given. */
1424365263eSray 			rcs_suffixes = rcs_optarg ? rcs_optarg : "";
1437518c1e9Sxsa 			break;
1446baf4b21Sjoris 		case 'z':
1456baf4b21Sjoris 			timezone_flag = rcs_optarg;
146237f1a74Sxsa 			break;
14767970ff5Sjoris 		default:
14871c5d535Sotto 			(usage)();
14967970ff5Sjoris 		}
15067970ff5Sjoris 	}
15167970ff5Sjoris 
152a2b34663Sjoris 	argc -= rcs_optind;
153a2b34663Sjoris 	argv += rcs_optind;
15467970ff5Sjoris 
15567970ff5Sjoris 	if (argc == 0) {
15682ca7eaaSxsa 		warnx("no input file");
15767970ff5Sjoris 		(usage)();
15867970ff5Sjoris 	}
15967970ff5Sjoris 
1607249ec9bSderaadt 	if (hflag == 1 && tflag == 1) {
16182ca7eaaSxsa 		warnx("warning: -t overrides -h.");
162708afa3eSxsa 		hflag = 0;
163708afa3eSxsa 	}
16441beedfcSxsa 
16567970ff5Sjoris 	for (i = 0; i < argc; i++) {
166f3876e23Sray 		fd = rcs_choosefile(argv[i], fpath, sizeof(fpath));
167f3876e23Sray 		if (fd < 0) {
16899e66255Sniallo 			warn("%s", fpath);
169b3e140bbSray 			status = 1;
17067970ff5Sjoris 			continue;
171f3876e23Sray 		}
17267970ff5Sjoris 
173bc8d37c3Sjoris 		if ((file = rcs_open(fpath, fd,
174b3e140bbSray 		    RCS_READ|RCS_PARSE_FULLY)) == NULL) {
175b3e140bbSray 			status = 1;
1766a77db31Sxsa 			continue;
177b3e140bbSray 		}
1786a77db31Sxsa 
1797249ec9bSderaadt 		if (Lflag == 1 && TAILQ_EMPTY(&(file->rf_locks))) {
1806a77db31Sxsa 			rcs_close(file);
181d6b28c1cSxsa 			continue;
182d6b28c1cSxsa 		}
183d6b28c1cSxsa 
1846a77db31Sxsa 		if (Rflag == 1) {
1856a77db31Sxsa 			printf("%s\n", fpath);
1866a77db31Sxsa 			rcs_close(file);
18767970ff5Sjoris 			continue;
1886a77db31Sxsa 		}
18967970ff5Sjoris 
19044a7e1f3Sray 		rlog_file(argv[i], file);
191d6b28c1cSxsa 
19267970ff5Sjoris 		rcs_close(file);
19367970ff5Sjoris 	}
19467970ff5Sjoris 
195b3e140bbSray 	return (status);
19667970ff5Sjoris }
19767970ff5Sjoris 
1988d628116Sjoris static int
rlog_select_daterev(RCSFILE * rcsfile,char * date)1998d628116Sjoris rlog_select_daterev(RCSFILE *rcsfile, char *date)
2008d628116Sjoris {
2018d628116Sjoris 	int i, nrev, flags;
2028d628116Sjoris 	struct rcs_delta *rdp;
2038d628116Sjoris 	struct rcs_argvector *args;
2048d628116Sjoris 	char *first, *last, delim;
2058d628116Sjoris 	time_t firstdate, lastdate, rcsdate;
2068d628116Sjoris 
2078d628116Sjoris 	nrev = 0;
2088d628116Sjoris 	args = rcs_strsplit(date, ";");
2098d628116Sjoris 
2108d628116Sjoris 	for (i = 0; args->argv[i] != NULL; i++) {
2118d628116Sjoris 		flags = 0;
2128d628116Sjoris 		firstdate = lastdate = -1;
2138d628116Sjoris 
2148d628116Sjoris 		first = args->argv[i];
2158d628116Sjoris 		last = strchr(args->argv[i], '<');
2168d628116Sjoris 		if (last != NULL) {
2178d628116Sjoris 			delim = *last;
2188d628116Sjoris 			*last++ = '\0';
2198d628116Sjoris 
2208d628116Sjoris 			if (*last == '=') {
2218d628116Sjoris 				last++;
2228d628116Sjoris 				flags |= RLOG_DATE_INCLUSIVE;
2238d628116Sjoris 			}
2248d628116Sjoris 		} else {
2258d628116Sjoris 			last = strchr(args->argv[i], '>');
2268d628116Sjoris 			if (last != NULL) {
2278d628116Sjoris 				delim = *last;
2288d628116Sjoris 				*last++ = '\0';
2298d628116Sjoris 
2308d628116Sjoris 				if (*last == '=') {
2318d628116Sjoris 					last++;
2328d628116Sjoris 					flags |= RLOG_DATE_INCLUSIVE;
2338d628116Sjoris 				}
2348d628116Sjoris 			}
2358d628116Sjoris 		}
2368d628116Sjoris 
2378d628116Sjoris 		if (last == NULL) {
2388d628116Sjoris 			flags |= RLOG_DATE_SINGLE;
239092db204Sray 			if ((firstdate = date_parse(first)) == -1)
240092db204Sray 				return -1;
2418d628116Sjoris 			delim = '\0';
2428d628116Sjoris 			last = "\0";
2438d628116Sjoris 		} else {
24403829ff5Sderaadt 			while (*last && isspace((unsigned char)*last))
2458d628116Sjoris 				last++;
2468d628116Sjoris 		}
2478d628116Sjoris 
2488d628116Sjoris 		if (delim == '>' && *last == '\0') {
2498d628116Sjoris 			flags |= RLOG_DATE_EARLIER;
250092db204Sray 			if ((firstdate = date_parse(first)) == -1)
251092db204Sray 				return -1;
2528d628116Sjoris 		}
2538d628116Sjoris 
2548d628116Sjoris 		if (delim == '>' && *first == '\0' && *last != '\0') {
2558d628116Sjoris 			flags |= RLOG_DATE_LATER;
256092db204Sray 			if ((firstdate = date_parse(last)) == -1)
257092db204Sray 				return -1;
2588d628116Sjoris 		}
2598d628116Sjoris 
2608d628116Sjoris 		if (delim == '<' && *last == '\0') {
2618d628116Sjoris 			flags |= RLOG_DATE_LATER;
262092db204Sray 			if ((firstdate = date_parse(first)) == -1)
263092db204Sray 				return -1;
2648d628116Sjoris 		}
2658d628116Sjoris 
2668d628116Sjoris 		if (delim == '<' && *first == '\0' && *last != '\0') {
2678d628116Sjoris 			flags |= RLOG_DATE_EARLIER;
268092db204Sray 			if ((firstdate = date_parse(last)) == -1)
269092db204Sray 				return -1;
2708d628116Sjoris 		}
2718d628116Sjoris 
2728d628116Sjoris 		if (*first != '\0' && *last != '\0') {
2738d628116Sjoris 			flags |= RLOG_DATE_RANGE;
2748d628116Sjoris 
2758d628116Sjoris 			if (delim == '<') {
2767bb3ddb0Sray 				firstdate = date_parse(first);
2777bb3ddb0Sray 				lastdate = date_parse(last);
2788d628116Sjoris 			} else {
2797bb3ddb0Sray 				firstdate = date_parse(last);
2807bb3ddb0Sray 				lastdate = date_parse(first);
2818d628116Sjoris 			}
282092db204Sray 			if (firstdate == -1 || lastdate == -1)
283092db204Sray 				return -1;
2848d628116Sjoris 		}
2858d628116Sjoris 
2868d628116Sjoris 		TAILQ_FOREACH(rdp, &(rcsfile->rf_delta), rd_list) {
2878d628116Sjoris 			rcsdate = mktime(&(rdp->rd_date));
2888d628116Sjoris 
2898d628116Sjoris 			if (flags & RLOG_DATE_SINGLE) {
2908d628116Sjoris 				if (rcsdate <= firstdate) {
2918d628116Sjoris 					rdp->rd_flags |= RCS_RD_SELECT;
2928d628116Sjoris 					nrev++;
2938d628116Sjoris 					break;
2948d628116Sjoris 				}
2958d628116Sjoris 			}
2968d628116Sjoris 
2978d628116Sjoris 			if (flags & RLOG_DATE_EARLIER) {
2988d628116Sjoris 				if (rcsdate < firstdate) {
2998d628116Sjoris 					rdp->rd_flags |= RCS_RD_SELECT;
3008d628116Sjoris 					nrev++;
3018d628116Sjoris 					continue;
3028d628116Sjoris 				}
3038d628116Sjoris 
3048d628116Sjoris 				if (flags & RLOG_DATE_INCLUSIVE &&
3058d628116Sjoris 				    (rcsdate <= firstdate)) {
3068d628116Sjoris 					rdp->rd_flags |= RCS_RD_SELECT;
3078d628116Sjoris 					nrev++;
3088d628116Sjoris 					continue;
3098d628116Sjoris 				}
3108d628116Sjoris 			}
3118d628116Sjoris 
3128d628116Sjoris 			if (flags & RLOG_DATE_LATER) {
3138d628116Sjoris 				if (rcsdate > firstdate) {
3148d628116Sjoris 					rdp->rd_flags |= RCS_RD_SELECT;
3158d628116Sjoris 					nrev++;
3168d628116Sjoris 					continue;
3178d628116Sjoris 				}
3188d628116Sjoris 
3198d628116Sjoris 				if (flags & RLOG_DATE_INCLUSIVE &&
3208d628116Sjoris 				    (rcsdate >= firstdate)) {
3218d628116Sjoris 					rdp->rd_flags |= RCS_RD_SELECT;
3228d628116Sjoris 					nrev++;
3238d628116Sjoris 					continue;
3248d628116Sjoris 				}
3258d628116Sjoris 			}
3268d628116Sjoris 
3278d628116Sjoris 			if (flags & RLOG_DATE_RANGE) {
3288d628116Sjoris 				if ((rcsdate > firstdate) &&
3298d628116Sjoris 				    (rcsdate < lastdate)) {
3308d628116Sjoris 					rdp->rd_flags |= RCS_RD_SELECT;
3318d628116Sjoris 					nrev++;
3328d628116Sjoris 					continue;
3338d628116Sjoris 				}
3348d628116Sjoris 
3358d628116Sjoris 				if (flags & RLOG_DATE_INCLUSIVE &&
3368d628116Sjoris 				    ((rcsdate >= firstdate) &&
3378d628116Sjoris 				    (rcsdate <= lastdate))) {
3388d628116Sjoris 					rdp->rd_flags |= RCS_RD_SELECT;
3398d628116Sjoris 					nrev++;
3408d628116Sjoris 					continue;
3418d628116Sjoris 				}
3428d628116Sjoris 			}
3438d628116Sjoris 		}
3448d628116Sjoris 	}
3458d628116Sjoris 
3468d628116Sjoris 	return (nrev);
3478d628116Sjoris }
3488d628116Sjoris 
3490117a5bfSxsa static void
rlog_file(const char * fname,RCSFILE * file)35044a7e1f3Sray rlog_file(const char *fname, RCSFILE *file)
35167970ff5Sjoris {
352*66d939b2Snaddy 	char fnamebuf[PATH_MAX], numb[RCS_REV_BUFSZ];
3533c6748a1Sray 	u_int nrev;
35467970ff5Sjoris 	struct rcs_sym *sym;
35567970ff5Sjoris 	struct rcs_access *acp;
35631a1e4b2Sxsa 	struct rcs_delta *rdp;
3570cc34477Sxsa 	struct rcs_lock *lkp;
3586d7e6850Sjoris 	char *workfile, *p;
35967970ff5Sjoris 
3603c6748a1Sray 	if (rflag == 1)
36188afc69bSxsa 		nrev = rcs_rev_select(file, revisions);
362092db204Sray 	else if (dflag == 1) {
363257be878Sokan 		if ((nrev = rlog_select_daterev(file, rlog_dates)) == (u_int)-1)
364092db204Sray 			errx(1, "invalid date: %s", rlog_dates);
365092db204Sray 	} else
3663c6748a1Sray 		nrev = file->rf_ndelta;
3673c6748a1Sray 
368*66d939b2Snaddy 	if (strlcpy(fnamebuf, fname, sizeof(fnamebuf)) >= sizeof(fnamebuf))
369*66d939b2Snaddy 		errx(1, "rlog_file: truncation");
370*66d939b2Snaddy 
371*66d939b2Snaddy 	if ((workfile = basename(fnamebuf)) == NULL)
372a3660ae3Sxsa 		err(1, "basename");
3736d7e6850Sjoris 
3746d7e6850Sjoris 	/*
3756d7e6850Sjoris 	 * In case they specified 'foo,v' as argument.
3766d7e6850Sjoris 	 */
3776d7e6850Sjoris 	if ((p = strrchr(workfile, ',')) != NULL)
3786d7e6850Sjoris 		*p = '\0';
3796d7e6850Sjoris 
380011fd0adSray 	printf("\nRCS file: %s", file->rf_path);
3816d7e6850Sjoris 	printf("\nWorking file: %s", workfile);
38267970ff5Sjoris 	printf("\nhead:");
38367970ff5Sjoris 	if (file->rf_head != NULL)
38467970ff5Sjoris 		printf(" %s", rcsnum_tostr(file->rf_head, numb, sizeof(numb)));
38567970ff5Sjoris 
38667970ff5Sjoris 	printf("\nbranch:");
38767970ff5Sjoris 	if (rcs_branch_get(file) != NULL) {
38867970ff5Sjoris 		printf(" %s", rcsnum_tostr(rcs_branch_get(file),
38967970ff5Sjoris 		    numb, sizeof(numb)));
39067970ff5Sjoris 	}
39167970ff5Sjoris 
39267970ff5Sjoris 	printf("\nlocks: %s", (file->rf_flags & RCS_SLOCK) ? "strict" : "");
3930cc34477Sxsa 	TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list)
3940cc34477Sxsa 		printf("\n\t%s: %s", lkp->rl_name,
3950cc34477Sxsa 		    rcsnum_tostr(lkp->rl_num, numb, sizeof(numb)));
39667970ff5Sjoris 	printf("\naccess list:\n");
39767970ff5Sjoris 	TAILQ_FOREACH(acp, &(file->rf_access), ra_list)
39867970ff5Sjoris 		printf("\t%s\n", acp->ra_name);
39967970ff5Sjoris 
40067970ff5Sjoris 	if (Nflag == 0) {
40167970ff5Sjoris 		printf("symbolic names:\n");
40267970ff5Sjoris 		TAILQ_FOREACH(sym, &(file->rf_symbols), rs_list) {
40367970ff5Sjoris 			printf("\t%s: %s\n", sym->rs_name,
40467970ff5Sjoris 			    rcsnum_tostr(sym->rs_num, numb, sizeof(numb)));
40567970ff5Sjoris 		}
40667970ff5Sjoris 	}
40767970ff5Sjoris 
40867970ff5Sjoris 	printf("keyword substitution: %s\n",
40967970ff5Sjoris 	    file->rf_expand == NULL ? "kv" : file->rf_expand);
41067970ff5Sjoris 
41131a1e4b2Sxsa 	printf("total revisions: %u", file->rf_ndelta);
41231a1e4b2Sxsa 
4137249ec9bSderaadt 	if (file->rf_head != NULL && hflag == 0 && tflag == 0)
4143c6748a1Sray 		printf(";\tselected revisions: %u", nrev);
41531a1e4b2Sxsa 
41631a1e4b2Sxsa 	printf("\n");
41731a1e4b2Sxsa 
41867970ff5Sjoris 
4197249ec9bSderaadt 	if (hflag == 0 || tflag == 1)
42041beedfcSxsa 		printf("description:\n%s", file->rf_desc);
42167970ff5Sjoris 
42287d0ec1aSray 	if (hflag == 0 && tflag == 0 &&
42387d0ec1aSray 	    !(lflag == 1 && TAILQ_EMPTY(&file->rf_locks))) {
4243c6748a1Sray 		TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
4253c6748a1Sray 			/*
4263c6748a1Sray 			 * if selections are enabled verify that entry is
4273c6748a1Sray 			 * selected.
4283c6748a1Sray 			 */
4298d628116Sjoris 			if ((rflag == 0 && dflag == 0)
4308d628116Sjoris 			    || (rdp->rd_flags & RCS_RD_SELECT))
43131a1e4b2Sxsa 				rlog_rev_print(rdp);
43231a1e4b2Sxsa 		}
4333c6748a1Sray 	}
434b7ae33e2Sxsa 
435119dfb76Sjcs 	printf("%s\n", revend);
436b7ae33e2Sxsa }
437b7ae33e2Sxsa 
438b7ae33e2Sxsa static void
rlog_rev_print(struct rcs_delta * rdp)43931a1e4b2Sxsa rlog_rev_print(struct rcs_delta *rdp)
440b7ae33e2Sxsa {
44131a1e4b2Sxsa 	int i, found;
44225939815Sjoris 	struct tm t;
443950e09dcSxsa 	char *author, numb[RCS_REV_BUFSZ], *fmt, timeb[RCS_TIME_BUFSZ];
4442dc36bedSjoris 	struct rcs_argvector *largv, *sargv, *wargv;
445be0b58deSxsa 	struct rcs_branch *rb;
446be0b58deSxsa 	struct rcs_delta *nrdp;
447b7ae33e2Sxsa 
44893443395Sotto 	found = 0;
44931a1e4b2Sxsa 	author = NULL;
45031a1e4b2Sxsa 
4514dc9061dSxsa 	/* -l[lockers] */
4524dc9061dSxsa 	if (lflag == 1) {
4534dc9061dSxsa 		if (rdp->rd_locker != NULL)
4544dc9061dSxsa 			found++;
4554dc9061dSxsa 
4564dc9061dSxsa 		if (llist != NULL) {
4574dc9061dSxsa 			/* if locker is empty, no need to go further. */
4584dc9061dSxsa 			if (rdp->rd_locker == NULL)
4594dc9061dSxsa 				return;
4602dc36bedSjoris 			largv = rcs_strsplit(llist, ",");
461948d9c85Spat 			for (i = 0; largv->argv[i] != NULL; i++) {
462948d9c85Spat 				if (strcmp(rdp->rd_locker, largv->argv[i])
463948d9c85Spat 				    == 0) {
4644dc9061dSxsa 					found++;
4654dc9061dSxsa 					break;
4664dc9061dSxsa 				}
4674dc9061dSxsa 				found = 0;
4684dc9061dSxsa 			}
4692dc36bedSjoris 			rcs_argv_destroy(largv);
4704dc9061dSxsa 		}
4714dc9061dSxsa 	}
47225939815Sjoris 
47331a1e4b2Sxsa 	/* -sstates */
47431a1e4b2Sxsa 	if (slist != NULL) {
4752dc36bedSjoris 		sargv = rcs_strsplit(slist, ",");
476948d9c85Spat 		for (i = 0; sargv->argv[i] != NULL; i++) {
477948d9c85Spat 			if (strcmp(rdp->rd_state, sargv->argv[i]) == 0) {
47831a1e4b2Sxsa 				found++;
47931a1e4b2Sxsa 				break;
48031a1e4b2Sxsa 			}
48131a1e4b2Sxsa 			found = 0;
48231a1e4b2Sxsa 		}
4832dc36bedSjoris 		rcs_argv_destroy(sargv);
48431a1e4b2Sxsa 	}
48525939815Sjoris 
48631a1e4b2Sxsa 	/* -w[logins] */
48731a1e4b2Sxsa 	if (wflag == 1) {
48831a1e4b2Sxsa 		if (wlist != NULL) {
4892dc36bedSjoris 			wargv = rcs_strsplit(wlist, ",");
490948d9c85Spat 			for (i = 0; wargv->argv[i] != NULL; i++) {
491948d9c85Spat 				if (strcmp(rdp->rd_author, wargv->argv[i])
492948d9c85Spat 				    == 0) {
49331a1e4b2Sxsa 					found++;
49431a1e4b2Sxsa 					break;
49531a1e4b2Sxsa 				}
49631a1e4b2Sxsa 				found = 0;
49731a1e4b2Sxsa 			}
4982dc36bedSjoris 			rcs_argv_destroy(wargv);
49931a1e4b2Sxsa 		} else {
50031a1e4b2Sxsa 			if ((author = getlogin()) == NULL)
501a3660ae3Sxsa 				err(1, "getlogin");
50231a1e4b2Sxsa 
50331a1e4b2Sxsa 			if (strcmp(rdp->rd_author, author) == 0)
50431a1e4b2Sxsa 				found++;
50531a1e4b2Sxsa 		}
50631a1e4b2Sxsa 	}
50731a1e4b2Sxsa 
50831a1e4b2Sxsa 	/* XXX dirty... */
5097249ec9bSderaadt 	if ((((slist != NULL && wflag == 1) ||
5107249ec9bSderaadt 	    (slist != NULL && lflag == 1) ||
5117249ec9bSderaadt 	    (lflag == 1 && wflag == 1)) && found < 2) ||
5127249ec9bSderaadt 	    (((slist != NULL && lflag == 1 && wflag == 1) ||
5137249ec9bSderaadt 	    (slist != NULL || lflag == 1 || wflag == 1)) && found == 0))
51431a1e4b2Sxsa 		return;
51531a1e4b2Sxsa 
516119dfb76Sjcs 	printf("%s\n", revsep);
517b7ae33e2Sxsa 
51867970ff5Sjoris 	rcsnum_tostr(rdp->rd_num, numb, sizeof(numb));
519b7ae33e2Sxsa 
520f11ffaffSxsa 	printf("revision %s", numb);
521f11ffaffSxsa 	if (rdp->rd_locker != NULL)
522f11ffaffSxsa 		printf("\tlocked by: %s;", rdp->rd_locker);
52325939815Sjoris 
52425939815Sjoris 	if (timezone_flag != NULL) {
52525939815Sjoris 		rcs_set_tz(timezone_flag, rdp, &t);
52643d0245aSxsa 		fmt = "%Y-%m-%d %H:%M:%S%z";
52725939815Sjoris 	} else {
52825939815Sjoris 		t = rdp->rd_date;
52925939815Sjoris 		fmt = "%Y/%m/%d %H:%M:%S";
53025939815Sjoris 	}
53125939815Sjoris 
532be0b58deSxsa 	(void)strftime(timeb, sizeof(timeb), fmt, &t);
53325939815Sjoris 
534be0b58deSxsa 	printf("\ndate: %s;  author: %s;  state: %s;", timeb, rdp->rd_author,
53525939815Sjoris 	    rdp->rd_state);
53625939815Sjoris 
537be0b58deSxsa 	/*
538be0b58deSxsa 	 * If we are a branch revision, the diff of this revision is stored
539be0b58deSxsa 	 * in place.
540be0b58deSxsa 	 * Otherwise, it is stored in the previous revision as a reversed diff.
541be0b58deSxsa 	 */
542be0b58deSxsa 	if (RCSNUM_ISBRANCHREV(rdp->rd_num))
543be0b58deSxsa 		nrdp = rdp;
544be0b58deSxsa 	else
545be0b58deSxsa 		nrdp = TAILQ_NEXT(rdp, rd_list);
546be0b58deSxsa 
547be0b58deSxsa 	/*
548be0b58deSxsa 	 * We do not write diff stats for the first revision of the default
549be0b58deSxsa 	 * branch, since it was not a diff but a full text.
550be0b58deSxsa 	 */
551be0b58deSxsa 	if (nrdp != NULL && rdp->rd_num->rn_len == nrdp->rd_num->rn_len) {
552be0b58deSxsa 		int added, removed;
55359399cf7Sxsa 
554be0b58deSxsa 		rcs_delta_stats(nrdp, &added, &removed);
555be0b58deSxsa 		if (RCSNUM_ISBRANCHREV(rdp->rd_num))
55652248372Sjcs 			printf("  lines: +%d -%d;", added, removed);
557be0b58deSxsa 		else
55852248372Sjcs 			printf("  lines: +%d -%d;", removed, added);
559be0b58deSxsa 	}
56052248372Sjcs 
56152248372Sjcs 	if (rdp->rd_commitid != NULL)
56252248372Sjcs 		printf("  commitid: %s;", rdp->rd_commitid);
56352248372Sjcs 
564be0b58deSxsa 	printf("\n");
565be0b58deSxsa 
566be0b58deSxsa 	if (!TAILQ_EMPTY(&(rdp->rd_branches))) {
567be0b58deSxsa 		printf("branches:");
568be0b58deSxsa 		TAILQ_FOREACH(rb, &(rdp->rd_branches), rb_list) {
569be0b58deSxsa 			RCSNUM *branch;
570be0b58deSxsa 			branch = rcsnum_revtobr(rb->rb_num);
571be0b58deSxsa 			(void)rcsnum_tostr(branch, numb, sizeof(numb));
572be0b58deSxsa 			printf("  %s;", numb);
573be0b58deSxsa 			rcsnum_free(branch);
574be0b58deSxsa 		}
575be0b58deSxsa 		printf("\n");
576be0b58deSxsa 	}
577be0b58deSxsa 
57867970ff5Sjoris 	printf("%s", rdp->rd_log);
57967970ff5Sjoris }
580