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