xref: /openbsd/usr.bin/cvs/getlog.c (revision 4dcde513)
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