1*4dcde513Sjoris /* $OpenBSD: getlog.c,v 1.101 2017/06/01 08:08:24 joris Exp $ */
26c121f58Sjfb /*
37573783fSxsa * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org>
4e8c16debSjoris * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
56c121f58Sjfb *
6e8c16debSjoris * Permission to use, copy, modify, and distribute this software for any
7e8c16debSjoris * purpose with or without fee is hereby granted, provided that the above
8e8c16debSjoris * copyright notice and this permission notice appear in all copies.
96c121f58Sjfb *
10e8c16debSjoris * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11e8c16debSjoris * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12e8c16debSjoris * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13e8c16debSjoris * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14e8c16debSjoris * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15e8c16debSjoris * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16e8c16debSjoris * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176c121f58Sjfb */
186c121f58Sjfb
191f8531bdSotto #include <unistd.h>
2053ce2177Sfcambus #include <stdlib.h>
211f8531bdSotto #include <string.h>
22449bca81Sniallo #include <errno.h>
23d2a7fc76Sjoris #include <ctype.h>
246c121f58Sjfb
256c121f58Sjfb #include "cvs.h"
269fac60a5Sjoris #include "remote.h"
276c121f58Sjfb
287573783fSxsa #define L_HEAD 0x01
297573783fSxsa #define L_HEAD_DESCR 0x02
307573783fSxsa #define L_NAME 0x04
317573783fSxsa #define L_NOTAGS 0x08
328371e6b6Sxsa #define L_LOGINS 0x10
338371e6b6Sxsa #define L_STATES 0x20
347573783fSxsa
35d2a7fc76Sjoris #define LDATE_LATER 0x01
36d2a7fc76Sjoris #define LDATE_EARLIER 0x02
37d2a7fc76Sjoris #define LDATE_SINGLE 0x04
38d2a7fc76Sjoris #define LDATE_RANGE 0x08
39d2a7fc76Sjoris #define LDATE_INCLUSIVE 0x10
4069d26cb1Sxsa
41d2a7fc76Sjoris void cvs_log_local(struct cvs_file *);
428371e6b6Sxsa static void log_rev_print(struct rcs_delta *);
43d2a7fc76Sjoris static char *push_date(char *dest, const char *);
44d2a7fc76Sjoris static u_int date_select(RCSFILE *, char *);
458371e6b6Sxsa
467573783fSxsa int runflags = 0;
47e8c16debSjoris char *logrev = NULL;
48d2a7fc76Sjoris char *logdate = NULL;
498371e6b6Sxsa char *slist = NULL;
508371e6b6Sxsa char *wlist = NULL;
516c121f58Sjfb
52e4276007Sjfb struct cvs_cmd cvs_cmd_log = {
53f331ff59Stobias CVS_OP_LOG, CVS_USE_WDIR, "log",
54e4276007Sjfb { "lo" },
55e4276007Sjfb "Print out history information for files",
56e4276007Sjfb "[-bhlNRt] [-d dates] [-r revisions] [-s states] [-w logins]",
57a347ceb8Sxsa "bd:hlNRr:s:tw:",
58e4276007Sjfb NULL,
59e8c16debSjoris cvs_getlog
60e4276007Sjfb };
61e4276007Sjfb
62449bca81Sniallo struct cvs_cmd cvs_cmd_rlog = {
63449bca81Sniallo CVS_OP_RLOG, 0, "rlog",
64449bca81Sniallo { "rlo" },
65449bca81Sniallo "Print out history information for files",
66449bca81Sniallo "[-bhlNRt] [-d dates] [-r revisions] [-s states] [-w logins]",
67449bca81Sniallo "bd:hlNRr:s:tw:",
68449bca81Sniallo NULL,
69449bca81Sniallo cvs_getlog
70449bca81Sniallo };
71449bca81Sniallo
72e8c16debSjoris int
cvs_getlog(int argc,char ** argv)73e8c16debSjoris cvs_getlog(int argc, char **argv)
746c121f58Sjfb {
75f2271752Stobias int ch, flags, i;
76e8c16debSjoris char *arg = ".";
77e8c16debSjoris struct cvs_recursion cr;
786c121f58Sjfb
79e8c16debSjoris flags = CR_RECURSE_DIRS;
80e8c16debSjoris
81e9d83458Stobias while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_LOG ?
82e9d83458Stobias cvs_cmd_log.cmd_opts : cvs_cmd_rlog.cmd_opts)) != -1) {
8316cfc147Sjoris switch (ch) {
84d2a7fc76Sjoris case 'd':
85d2a7fc76Sjoris logdate = push_date(logdate, optarg);
86d2a7fc76Sjoris break;
877573783fSxsa case 'h':
887573783fSxsa runflags |= L_HEAD;
897573783fSxsa break;
906c121f58Sjfb case 'l':
91e8c16debSjoris flags &= ~CR_RECURSE_DIRS;
926c121f58Sjfb break;
937573783fSxsa case 'N':
947573783fSxsa runflags |= L_NOTAGS;
957573783fSxsa break;
967573783fSxsa case 'R':
977573783fSxsa runflags |= L_NAME;
98528e2d0cStobias break;
996c121f58Sjfb case 'r':
100e8c16debSjoris logrev = optarg;
10184c3cb67Sxsa break;
1028371e6b6Sxsa case 's':
1038371e6b6Sxsa runflags |= L_STATES;
1048371e6b6Sxsa slist = optarg;
1058371e6b6Sxsa break;
1067573783fSxsa case 't':
1077573783fSxsa runflags |= L_HEAD_DESCR;
1087573783fSxsa break;
1098371e6b6Sxsa case 'w':
1108371e6b6Sxsa runflags |= L_LOGINS;
1118371e6b6Sxsa wlist = optarg;
1128371e6b6Sxsa break;
1136c121f58Sjfb default:
114aeaaf4a6Stobias fatal("%s", cvs_cmdop == CVS_OP_LOG ?
115aeaaf4a6Stobias cvs_cmd_log.cmd_synopsis :
116aeaaf4a6Stobias cvs_cmd_rlog.cmd_synopsis);
1176c121f58Sjfb }
1186c121f58Sjfb }
1196c121f58Sjfb
120e8c16debSjoris argc -= optind;
121e8c16debSjoris argv += optind;
1226c121f58Sjfb
123f2271752Stobias if (cvs_cmdop == CVS_OP_RLOG) {
124f331ff59Stobias flags |= CR_REPO;
125f331ff59Stobias
126f2271752Stobias if (argc == 0)
127f2271752Stobias return 0;
128f2271752Stobias
129f2271752Stobias for (i = 0; i < argc; i++)
130f2271752Stobias if (argv[i][0] == '/')
131f2271752Stobias fatal("Absolute path name is invalid: %s",
132f2271752Stobias argv[i]);
133f2271752Stobias }
134f2271752Stobias
135e8c16debSjoris cr.enterdir = NULL;
136e8c16debSjoris cr.leavedir = NULL;
1379fac60a5Sjoris
138*4dcde513Sjoris if (cvsroot_is_remote()) {
13980f6ca9bSjoris cvs_client_connect_to_server();
1409fac60a5Sjoris cr.fileproc = cvs_client_sendfile;
1419fac60a5Sjoris
142d2a7fc76Sjoris if (logdate != NULL)
143d2a7fc76Sjoris cvs_client_send_request("Argument -d%s", logdate);
144d2a7fc76Sjoris
1457573783fSxsa if (runflags & L_HEAD)
1467573783fSxsa cvs_client_send_request("Argument -h");
1477573783fSxsa
1489fac60a5Sjoris if (!(flags & CR_RECURSE_DIRS))
1499fac60a5Sjoris cvs_client_send_request("Argument -l");
1509fac60a5Sjoris
1517573783fSxsa if (runflags & L_NOTAGS)
1527573783fSxsa cvs_client_send_request("Argument -N");
1537573783fSxsa
1547573783fSxsa if (runflags & L_NAME)
1557573783fSxsa cvs_client_send_request("Argument -R");
1567573783fSxsa
1579fac60a5Sjoris if (logrev != NULL)
1589fac60a5Sjoris cvs_client_send_request("Argument -r%s", logrev);
1597573783fSxsa
1608371e6b6Sxsa if (runflags & L_STATES)
1618371e6b6Sxsa cvs_client_send_request("Argument -s%s", slist);
1628371e6b6Sxsa
1637573783fSxsa if (runflags & L_HEAD_DESCR)
1647573783fSxsa cvs_client_send_request("Argument -t");
1658371e6b6Sxsa
1668371e6b6Sxsa if (runflags & L_LOGINS)
1678371e6b6Sxsa cvs_client_send_request("Argument -w%s", wlist);
1689fac60a5Sjoris } else {
169e0b2359bSxsa if (cvs_cmdop == CVS_OP_RLOG &&
170449bca81Sniallo chdir(current_cvsroot->cr_dir) == -1)
17186fbe768Sxsa fatal("cvs_getlog: %s", strerror(errno));
172449bca81Sniallo
173bc5d89feSjoris cr.fileproc = cvs_log_local;
1749fac60a5Sjoris }
1759fac60a5Sjoris
176e8c16debSjoris cr.flags = flags;
17769d26cb1Sxsa
178*4dcde513Sjoris if (cvs_cmdop == CVS_OP_LOG || cvsroot_is_local()) {
179e8c16debSjoris if (argc > 0)
180e8c16debSjoris cvs_file_run(argc, argv, &cr);
18125c4314aSjfb else
182e8c16debSjoris cvs_file_run(1, &arg, &cr);
183f2271752Stobias }
18425c4314aSjfb
185*4dcde513Sjoris if (cvsroot_is_remote()) {
1869fac60a5Sjoris cvs_client_send_files(argv, argc);
1879fac60a5Sjoris cvs_client_senddir(".");
188e0b2359bSxsa
189e0b2359bSxsa cvs_client_send_request((cvs_cmdop == CVS_OP_RLOG) ?
190e0b2359bSxsa "rlog" : "log");
191e0b2359bSxsa
1929fac60a5Sjoris cvs_client_get_responses();
1939fac60a5Sjoris }
1949fac60a5Sjoris
1957e393898Sjoris return (0);
196a96bf98bSjfb }
197df745765Sjfb
198e8c16debSjoris void
cvs_log_local(struct cvs_file * cf)199e8c16debSjoris cvs_log_local(struct cvs_file *cf)
2009931af54Sjfb {
201e8c16debSjoris u_int nrev;
202e6e4e30cSjoris RCSNUM *rev;
2039931af54Sjfb struct rcs_sym *sym;
204e8c16debSjoris struct rcs_lock *lkp;
2059931af54Sjfb struct rcs_delta *rdp;
206874e0b9fSjfb struct rcs_access *acp;
2070a7da307Sxsa char numb[CVS_REV_BUFSZ];
2089931af54Sjfb
209c76dba03Sjoris cvs_log(LP_TRACE, "cvs_log_local(%s)", cf->file_path);
210c76dba03Sjoris
2115a1806edSjoris cvs_file_classify(cf, cvs_directory_tag);
2125bf4e3f1Sxsa
2139869cf0fSjoris if (cf->file_type == CVS_DIR) {
2149869cf0fSjoris if (verbosity > 1)
2159869cf0fSjoris cvs_log(LP_ERR, "Logging %s", cf->file_path);
2169869cf0fSjoris return;
2179869cf0fSjoris }
2189869cf0fSjoris
219e28eda4eStobias if (cf->file_rcs == NULL) {
220e8c16debSjoris return;
221e8c16debSjoris } else if (cf->file_status == FILE_ADDED) {
222ef407d49Sxsa if (verbosity > 0)
2239a192d08Sdavid cvs_log(LP_ERR, "%s has been added, but not committed",
224e8c16debSjoris cf->file_path);
225e8c16debSjoris return;
2269931af54Sjfb }
2279931af54Sjfb
2287573783fSxsa if (runflags & L_NAME) {
2297573783fSxsa cvs_printf("%s\n", cf->file_rpath);
2307573783fSxsa return;
2317573783fSxsa }
2327573783fSxsa
23342f3571fStobias if (logrev != NULL)
23442f3571fStobias nrev = cvs_revision_select(cf->file_rcs, logrev);
235092db204Sray else if (logdate != NULL) {
236ae886706Smillert if ((nrev = date_select(cf->file_rcs, logdate)) == (u_int)-1) {
237092db204Sray cvs_log(LP_ERR, "invalid date: %s", logdate);
238092db204Sray return;
239092db204Sray }
240092db204Sray } else
24142f3571fStobias nrev = cf->file_rcs->rf_ndelta;
24242f3571fStobias
2439fac60a5Sjoris cvs_printf("\nRCS file: %s", cf->file_rpath);
2445f291a7eSxsa
2455f291a7eSxsa if (cvs_cmdop != CVS_OP_RLOG)
2469fac60a5Sjoris cvs_printf("\nWorking file: %s", cf->file_path);
2475f291a7eSxsa
2489fac60a5Sjoris cvs_printf("\nhead:");
249e8c16debSjoris if (cf->file_rcs->rf_head != NULL)
2509fac60a5Sjoris cvs_printf(" %s", rcsnum_tostr(cf->file_rcs->rf_head,
251e8c16debSjoris numb, sizeof(numb)));
252e8c16debSjoris
2539fac60a5Sjoris cvs_printf("\nbranch:");
254e8c16debSjoris if (rcs_branch_get(cf->file_rcs) != NULL) {
2559fac60a5Sjoris cvs_printf(" %s", rcsnum_tostr(rcs_branch_get(cf->file_rcs),
256e8c16debSjoris numb, sizeof(numb)));
257874e0b9fSjfb }
2589931af54Sjfb
2599fac60a5Sjoris cvs_printf("\nlocks: %s", (cf->file_rcs->rf_flags & RCS_SLOCK)
260e8c16debSjoris ? "strict" : "");
261e8c16debSjoris TAILQ_FOREACH(lkp, &(cf->file_rcs->rf_locks), rl_list)
2629fac60a5Sjoris cvs_printf("\n\t%s: %s", lkp->rl_name,
263e8c16debSjoris rcsnum_tostr(lkp->rl_num, numb, sizeof(numb)));
2649931af54Sjfb
2659fac60a5Sjoris cvs_printf("\naccess list:\n");
266e8c16debSjoris TAILQ_FOREACH(acp, &(cf->file_rcs->rf_access), ra_list)
2679fac60a5Sjoris cvs_printf("\t%s\n", acp->ra_name);
268e8c16debSjoris
2697573783fSxsa if (!(runflags & L_NOTAGS)) {
2709fac60a5Sjoris cvs_printf("symbolic names:\n");
271e8c16debSjoris TAILQ_FOREACH(sym, &(cf->file_rcs->rf_symbols), rs_list) {
272e6e4e30cSjoris rev = rcsnum_alloc();
273e6e4e30cSjoris rcsnum_cpy(sym->rs_num, rev, 0);
274a3fbd90cSjoris if (RCSNUM_ISBRANCH(sym->rs_num))
275e6e4e30cSjoris rcsnum_addmagic(rev);
276a3fbd90cSjoris
2779fac60a5Sjoris cvs_printf("\t%s: %s\n", sym->rs_name,
278e6e4e30cSjoris rcsnum_tostr(rev, numb, sizeof(numb)));
27953ce2177Sfcambus free(rev);
280874e0b9fSjfb }
2817573783fSxsa }
282874e0b9fSjfb
2839fac60a5Sjoris cvs_printf("keyword substitution: %s\n",
284e8c16debSjoris cf->file_rcs->rf_expand == NULL ? "kv" : cf->file_rcs->rf_expand);
2859931af54Sjfb
2869fac60a5Sjoris cvs_printf("total revisions: %u", cf->file_rcs->rf_ndelta);
2879931af54Sjfb
2887573783fSxsa if (cf->file_rcs->rf_head != NULL &&
2897573783fSxsa !(runflags & L_HEAD) && !(runflags & L_HEAD_DESCR))
2909fac60a5Sjoris cvs_printf(";\tselected revisions: %u", nrev);
2917573783fSxsa
2929fac60a5Sjoris cvs_printf("\n");
2937573783fSxsa
2947573783fSxsa if (!(runflags & L_HEAD) || (runflags & L_HEAD_DESCR))
2959fac60a5Sjoris cvs_printf("description:\n%s", cf->file_rcs->rf_desc);
296874e0b9fSjfb
2977573783fSxsa if (!(runflags & L_HEAD) && !(runflags & L_HEAD_DESCR)) {
298e8c16debSjoris TAILQ_FOREACH(rdp, &(cf->file_rcs->rf_delta), rd_list) {
2998371e6b6Sxsa /*
3008371e6b6Sxsa * if selections are enabled verify that entry is
3018371e6b6Sxsa * selected.
3028371e6b6Sxsa */
303d2a7fc76Sjoris if ((logrev == NULL && logdate == NULL) ||
304d2a7fc76Sjoris (rdp->rd_flags & RCS_RD_SELECT))
3058371e6b6Sxsa log_rev_print(rdp);
3068371e6b6Sxsa }
3078371e6b6Sxsa }
3088371e6b6Sxsa
3098371e6b6Sxsa cvs_printf("%s\n", LOG_REVEND);
3108371e6b6Sxsa }
3118371e6b6Sxsa
3128371e6b6Sxsa static void
log_rev_print(struct rcs_delta * rdp)3138371e6b6Sxsa log_rev_print(struct rcs_delta *rdp)
3148371e6b6Sxsa {
3158371e6b6Sxsa int i, found;
3160a7da307Sxsa char numb[CVS_REV_BUFSZ], timeb[CVS_TIME_BUFSZ];
3178371e6b6Sxsa struct cvs_argvector *sargv, *wargv;
3189f5450fbSjoris struct rcs_branch *rb;
3199f5450fbSjoris struct rcs_delta *nrdp;
3208371e6b6Sxsa
3218371e6b6Sxsa i = found = 0;
3228371e6b6Sxsa
3238371e6b6Sxsa /* -s states */
3248371e6b6Sxsa if (runflags & L_STATES) {
3258371e6b6Sxsa sargv = cvs_strsplit(slist, ",");
3268371e6b6Sxsa for (i = 0; sargv->argv[i] != NULL; i++) {
3278371e6b6Sxsa if (strcmp(rdp->rd_state, sargv->argv[i]) == 0) {
3288371e6b6Sxsa found++;
3298371e6b6Sxsa break;
3308371e6b6Sxsa }
3318371e6b6Sxsa found = 0;
3328371e6b6Sxsa }
3338371e6b6Sxsa cvs_argv_destroy(sargv);
3348371e6b6Sxsa }
3358371e6b6Sxsa
3368371e6b6Sxsa /* -w[logins] */
3378371e6b6Sxsa if (runflags & L_LOGINS) {
3388371e6b6Sxsa wargv = cvs_strsplit(wlist, ",");
3398371e6b6Sxsa for (i = 0; wargv->argv[i] != NULL; i++) {
3408371e6b6Sxsa if (strcmp(rdp->rd_author, wargv->argv[i]) == 0) {
3418371e6b6Sxsa found++;
3428371e6b6Sxsa break;
3438371e6b6Sxsa }
3448371e6b6Sxsa found = 0;
3458371e6b6Sxsa }
3468371e6b6Sxsa cvs_argv_destroy(wargv);
3478371e6b6Sxsa }
3488371e6b6Sxsa
349a347ceb8Sxsa if ((runflags & (L_STATES|L_LOGINS)) && found == 0)
3508371e6b6Sxsa return;
3513fe70097Sxsa
3529fac60a5Sjoris cvs_printf("%s\n", LOG_REVSEP);
353daf00afbSxsa
35440440ce1Sjoris rcsnum_tostr(rdp->rd_num, numb, sizeof(numb));
3559fac60a5Sjoris cvs_printf("revision %s", numb);
356daf00afbSxsa
3578371e6b6Sxsa strftime(timeb, sizeof(timeb), "%Y/%m/%d %H:%M:%S", &rdp->rd_date);
3589f5450fbSjoris cvs_printf("\ndate: %s; author: %s; state: %s;",
3597573783fSxsa timeb, rdp->rd_author, rdp->rd_state);
3609f5450fbSjoris
3619f5450fbSjoris /*
3629f5450fbSjoris * If we are a branch revision, the diff of this revision is stored
3639f5450fbSjoris * in place.
3649f5450fbSjoris * Otherwise, it is stored in the previous revision as a reversed diff.
3659f5450fbSjoris */
3669f5450fbSjoris if (RCSNUM_ISBRANCHREV(rdp->rd_num))
3679f5450fbSjoris nrdp = rdp;
3689f5450fbSjoris else
3699f5450fbSjoris nrdp = TAILQ_NEXT(rdp, rd_list);
3709f5450fbSjoris
3719f5450fbSjoris /*
3729f5450fbSjoris * We do not write diff stats for the first revision of the default
3739f5450fbSjoris * branch, since it was not a diff but a full text.
3749f5450fbSjoris */
3759f5450fbSjoris if (nrdp != NULL && rdp->rd_num->rn_len == nrdp->rd_num->rn_len) {
3769f5450fbSjoris int added, removed;
3779f5450fbSjoris rcs_delta_stats(nrdp, &added, &removed);
3789f5450fbSjoris if (RCSNUM_ISBRANCHREV(rdp->rd_num))
37952248372Sjcs cvs_printf(" lines: +%d -%d;", added, removed);
3809f5450fbSjoris else
38152248372Sjcs cvs_printf(" lines: +%d -%d;", removed, added);
3829f5450fbSjoris }
38352248372Sjcs
38452248372Sjcs if (rdp->rd_commitid != NULL)
38552248372Sjcs printf(" commitid: %s;", rdp->rd_commitid);
38652248372Sjcs
3879f5450fbSjoris cvs_printf("\n");
3889f5450fbSjoris
3899f5450fbSjoris if (!TAILQ_EMPTY(&(rdp->rd_branches))) {
3909f5450fbSjoris cvs_printf("branches:");
3919f5450fbSjoris TAILQ_FOREACH(rb, &(rdp->rd_branches), rb_list) {
3929f5450fbSjoris RCSNUM *branch;
3939f5450fbSjoris branch = rcsnum_revtobr(rb->rb_num);
3949f5450fbSjoris rcsnum_tostr(branch, numb, sizeof(numb));
3959f5450fbSjoris cvs_printf(" %s;", numb);
39653ce2177Sfcambus free(branch);
3979f5450fbSjoris }
3989f5450fbSjoris cvs_printf("\n");
3999f5450fbSjoris }
4009f5450fbSjoris
4019fac60a5Sjoris cvs_printf("%s", rdp->rd_log);
402874e0b9fSjfb }
403d2a7fc76Sjoris
404d2a7fc76Sjoris static char *
push_date(char * dest,const char * src)405d2a7fc76Sjoris push_date(char *dest, const char *src)
406d2a7fc76Sjoris {
407d2a7fc76Sjoris size_t len;
408d2a7fc76Sjoris
409d2a7fc76Sjoris if (dest == NULL)
410d2a7fc76Sjoris return (xstrdup(src));
411d2a7fc76Sjoris
412d2a7fc76Sjoris /* 2 = ; and '\0' */
413d2a7fc76Sjoris len = strlen(dest) + strlen(src) + 2;
414d2a7fc76Sjoris
415d2a7fc76Sjoris dest[strlen(dest)] = ';';
416caa2ffb0Sderaadt dest = xreallocarray(dest, len, 1);
417d2a7fc76Sjoris strlcat(dest, src, len);
418d2a7fc76Sjoris return (dest);
419d2a7fc76Sjoris }
420d2a7fc76Sjoris
421d2a7fc76Sjoris static u_int
date_select(RCSFILE * file,char * date)422d2a7fc76Sjoris date_select(RCSFILE *file, char *date)
423d2a7fc76Sjoris {
424d2a7fc76Sjoris int i, nrev, flags;
425d2a7fc76Sjoris struct rcs_delta *rdp;
426d2a7fc76Sjoris struct cvs_argvector *args;
427d2a7fc76Sjoris char *first, *last, delim;
428d2a7fc76Sjoris time_t firstdate, lastdate, rcsdate;
429d2a7fc76Sjoris
430d2a7fc76Sjoris nrev = 0;
431d2a7fc76Sjoris args = cvs_strsplit(date, ";");
432d2a7fc76Sjoris
433d2a7fc76Sjoris for (i = 0; args->argv[i] != NULL; i++) {
434d2a7fc76Sjoris flags = 0;
435d2a7fc76Sjoris firstdate = lastdate = -1;
436d2a7fc76Sjoris
437d2a7fc76Sjoris first = args->argv[i];
438d2a7fc76Sjoris last = strchr(args->argv[i], '<');
439d2a7fc76Sjoris if (last != NULL) {
440d2a7fc76Sjoris delim = *last;
441d2a7fc76Sjoris *last++ = '\0';
442d2a7fc76Sjoris
443d2a7fc76Sjoris if (*last == '=') {
444d2a7fc76Sjoris last++;
445d2a7fc76Sjoris flags |= LDATE_INCLUSIVE;
446d2a7fc76Sjoris }
447d2a7fc76Sjoris } else {
448d2a7fc76Sjoris last = strchr(args->argv[i], '>');
449d2a7fc76Sjoris if (last != NULL) {
450d2a7fc76Sjoris delim = *last;
451d2a7fc76Sjoris *last++ = '\0';
452d2a7fc76Sjoris
453d2a7fc76Sjoris if (*last == '=') {
454d2a7fc76Sjoris last++;
455d2a7fc76Sjoris flags |= LDATE_INCLUSIVE;
456d2a7fc76Sjoris }
457d2a7fc76Sjoris }
458d2a7fc76Sjoris }
459d2a7fc76Sjoris
460d2a7fc76Sjoris if (last == NULL) {
461d2a7fc76Sjoris flags |= LDATE_SINGLE;
462092db204Sray if ((firstdate = date_parse(first)) == -1)
463092db204Sray return -1;
464d2a7fc76Sjoris delim = '\0';
465d2a7fc76Sjoris last = "\0";
466d2a7fc76Sjoris } else {
467f6ac027fSokan while (*last && isspace((unsigned char)*last))
468d2a7fc76Sjoris last++;
469d2a7fc76Sjoris }
470d2a7fc76Sjoris
471d2a7fc76Sjoris if (delim == '>' && *last == '\0') {
472d2a7fc76Sjoris flags |= LDATE_EARLIER;
473092db204Sray if ((firstdate = date_parse(first)) == -1)
474092db204Sray return -1;
475d2a7fc76Sjoris }
476d2a7fc76Sjoris
477d2a7fc76Sjoris if (delim == '>' && *first == '\0' && *last != '\0') {
478d2a7fc76Sjoris flags |= LDATE_LATER;
479092db204Sray if ((firstdate = date_parse(last)) == -1)
480092db204Sray return -1;
481d2a7fc76Sjoris }
482d2a7fc76Sjoris
483d2a7fc76Sjoris if (delim == '<' && *last == '\0') {
484d2a7fc76Sjoris flags |= LDATE_LATER;
485092db204Sray if ((firstdate = date_parse(first)) == -1)
486092db204Sray return -1;
487d2a7fc76Sjoris }
488d2a7fc76Sjoris
489d2a7fc76Sjoris if (delim == '<' && *first == '\0' && *last != '\0') {
490d2a7fc76Sjoris flags |= LDATE_EARLIER;
491092db204Sray if ((firstdate = date_parse(last)) == -1)
492092db204Sray return -1;
493d2a7fc76Sjoris }
494d2a7fc76Sjoris
495d2a7fc76Sjoris if (*first != '\0' && *last != '\0') {
496d2a7fc76Sjoris flags |= LDATE_RANGE;
497d2a7fc76Sjoris
498d2a7fc76Sjoris if (delim == '<') {
4997bb3ddb0Sray firstdate = date_parse(first);
5007bb3ddb0Sray lastdate = date_parse(last);
501d2a7fc76Sjoris } else {
5027bb3ddb0Sray firstdate = date_parse(last);
5037bb3ddb0Sray lastdate = date_parse(first);
504d2a7fc76Sjoris }
505092db204Sray if (firstdate == -1 || lastdate == -1)
506092db204Sray return -1;
507d2a7fc76Sjoris }
508d2a7fc76Sjoris
509d2a7fc76Sjoris TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
510d2a7fc76Sjoris rcsdate = mktime(&(rdp->rd_date));
511d2a7fc76Sjoris
512d2a7fc76Sjoris if (flags & LDATE_SINGLE) {
513d2a7fc76Sjoris if (rcsdate <= firstdate) {
514d2a7fc76Sjoris rdp->rd_flags |= RCS_RD_SELECT;
515d2a7fc76Sjoris nrev++;
516d2a7fc76Sjoris break;
517d2a7fc76Sjoris }
518d2a7fc76Sjoris }
519d2a7fc76Sjoris
520d2a7fc76Sjoris if (flags & LDATE_EARLIER) {
521d2a7fc76Sjoris if (rcsdate < firstdate) {
522d2a7fc76Sjoris rdp->rd_flags |= RCS_RD_SELECT;
523d2a7fc76Sjoris nrev++;
524d2a7fc76Sjoris continue;
525d2a7fc76Sjoris }
526d2a7fc76Sjoris
527d2a7fc76Sjoris if (flags & LDATE_INCLUSIVE &&
528d2a7fc76Sjoris (rcsdate <= firstdate)) {
529d2a7fc76Sjoris rdp->rd_flags |= RCS_RD_SELECT;
530d2a7fc76Sjoris nrev++;
531d2a7fc76Sjoris continue;
532d2a7fc76Sjoris }
533d2a7fc76Sjoris }
534d2a7fc76Sjoris
535d2a7fc76Sjoris if (flags & LDATE_LATER) {
536d2a7fc76Sjoris if (rcsdate > firstdate) {
537d2a7fc76Sjoris rdp->rd_flags |= RCS_RD_SELECT;
538d2a7fc76Sjoris nrev++;
539d2a7fc76Sjoris continue;
540d2a7fc76Sjoris }
541d2a7fc76Sjoris
542d2a7fc76Sjoris if (flags & LDATE_INCLUSIVE &&
543d2a7fc76Sjoris (rcsdate >= firstdate)) {
544d2a7fc76Sjoris rdp->rd_flags |= RCS_RD_SELECT;
545d2a7fc76Sjoris nrev++;
546d2a7fc76Sjoris continue;
547d2a7fc76Sjoris }
548d2a7fc76Sjoris }
549d2a7fc76Sjoris
550d2a7fc76Sjoris if (flags & LDATE_RANGE) {
551d2a7fc76Sjoris if ((rcsdate > firstdate) &&
552d2a7fc76Sjoris (rcsdate < lastdate)) {
553d2a7fc76Sjoris rdp->rd_flags |= RCS_RD_SELECT;
554d2a7fc76Sjoris nrev++;
555d2a7fc76Sjoris continue;
556d2a7fc76Sjoris }
557d2a7fc76Sjoris
558d2a7fc76Sjoris if (flags & LDATE_INCLUSIVE &&
559d2a7fc76Sjoris ((rcsdate >= firstdate) &&
560d2a7fc76Sjoris (rcsdate <= lastdate))) {
561d2a7fc76Sjoris rdp->rd_flags |= RCS_RD_SELECT;
562d2a7fc76Sjoris nrev++;
563d2a7fc76Sjoris continue;
564d2a7fc76Sjoris }
565d2a7fc76Sjoris }
566d2a7fc76Sjoris }
567d2a7fc76Sjoris }
568d2a7fc76Sjoris
569d2a7fc76Sjoris cvs_argv_destroy(args);
570d2a7fc76Sjoris
571d2a7fc76Sjoris return (nrev);
572d2a7fc76Sjoris }
573