xref: /dflybsd-src/contrib/cvs-1.12/src/log.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1*86d7f5d3SJohn Marino /*
2*86d7f5d3SJohn Marino  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3*86d7f5d3SJohn Marino  *
4*86d7f5d3SJohn Marino  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5*86d7f5d3SJohn Marino  *                                  and others.
6*86d7f5d3SJohn Marino  *
7*86d7f5d3SJohn Marino  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8*86d7f5d3SJohn Marino  * Portions Copyright (C) 1989-1992, Brian Berliner
9*86d7f5d3SJohn Marino  *
10*86d7f5d3SJohn Marino  * You may distribute under the terms of the GNU General Public License as
11*86d7f5d3SJohn Marino  * specified in the README file that comes with the CVS source distribution.
12*86d7f5d3SJohn Marino  *
13*86d7f5d3SJohn Marino  * Print Log Information
14*86d7f5d3SJohn Marino  *
15*86d7f5d3SJohn Marino  * Prints the RCS "log" (rlog) information for the specified files.  With no
16*86d7f5d3SJohn Marino  * argument, prints the log information for all the files in the directory
17*86d7f5d3SJohn Marino  * (recursive by default).
18*86d7f5d3SJohn Marino  */
19*86d7f5d3SJohn Marino 
20*86d7f5d3SJohn Marino #include "cvs.h"
21*86d7f5d3SJohn Marino #include <assert.h>
22*86d7f5d3SJohn Marino 
23*86d7f5d3SJohn Marino /* This structure holds information parsed from the -r option.  */
24*86d7f5d3SJohn Marino 
25*86d7f5d3SJohn Marino struct option_revlist
26*86d7f5d3SJohn Marino {
27*86d7f5d3SJohn Marino     /* The next -r option.  */
28*86d7f5d3SJohn Marino     struct option_revlist *next;
29*86d7f5d3SJohn Marino     /* The first revision to print.  This is NULL if the range is
30*86d7f5d3SJohn Marino        :rev, or if no revision is given.  */
31*86d7f5d3SJohn Marino     char *first;
32*86d7f5d3SJohn Marino     /* The last revision to print.  This is NULL if the range is rev:,
33*86d7f5d3SJohn Marino        or if no revision is given.  If there is no colon, first and
34*86d7f5d3SJohn Marino        last are the same.  */
35*86d7f5d3SJohn Marino     char *last;
36*86d7f5d3SJohn Marino     /* Nonzero if there was a trailing `.', which means to print only
37*86d7f5d3SJohn Marino        the head revision of a branch.  */
38*86d7f5d3SJohn Marino     int branchhead;
39*86d7f5d3SJohn Marino     /* Nonzero if first and last are inclusive.  */
40*86d7f5d3SJohn Marino     int inclusive;
41*86d7f5d3SJohn Marino };
42*86d7f5d3SJohn Marino 
43*86d7f5d3SJohn Marino /* This structure holds information derived from option_revlist given
44*86d7f5d3SJohn Marino    a particular RCS file.  */
45*86d7f5d3SJohn Marino 
46*86d7f5d3SJohn Marino struct revlist
47*86d7f5d3SJohn Marino {
48*86d7f5d3SJohn Marino     /* The next pair.  */
49*86d7f5d3SJohn Marino     struct revlist *next;
50*86d7f5d3SJohn Marino     /* The first numeric revision to print.  */
51*86d7f5d3SJohn Marino     char *first;
52*86d7f5d3SJohn Marino     /* The last numeric revision to print.  */
53*86d7f5d3SJohn Marino     char *last;
54*86d7f5d3SJohn Marino     /* The number of fields in these revisions (one more than
55*86d7f5d3SJohn Marino        numdots).  */
56*86d7f5d3SJohn Marino     int fields;
57*86d7f5d3SJohn Marino     /* Whether first & last are to be included or excluded.  */
58*86d7f5d3SJohn Marino     int inclusive;
59*86d7f5d3SJohn Marino };
60*86d7f5d3SJohn Marino 
61*86d7f5d3SJohn Marino /* This structure holds information parsed from the -d option.  */
62*86d7f5d3SJohn Marino 
63*86d7f5d3SJohn Marino struct datelist
64*86d7f5d3SJohn Marino {
65*86d7f5d3SJohn Marino     /* The next date.  */
66*86d7f5d3SJohn Marino     struct datelist *next;
67*86d7f5d3SJohn Marino     /* The starting date.  */
68*86d7f5d3SJohn Marino     char *start;
69*86d7f5d3SJohn Marino     /* The ending date.  */
70*86d7f5d3SJohn Marino     char *end;
71*86d7f5d3SJohn Marino     /* Nonzero if the range is inclusive rather than exclusive.  */
72*86d7f5d3SJohn Marino     int inclusive;
73*86d7f5d3SJohn Marino };
74*86d7f5d3SJohn Marino 
75*86d7f5d3SJohn Marino /* This structure is used to pass information through start_recursion.  */
76*86d7f5d3SJohn Marino struct log_data
77*86d7f5d3SJohn Marino {
78*86d7f5d3SJohn Marino     /* Nonzero if the -R option was given, meaning that only the name
79*86d7f5d3SJohn Marino        of the RCS file should be printed.  */
80*86d7f5d3SJohn Marino     int nameonly;
81*86d7f5d3SJohn Marino     /* Nonzero if the -h option was given, meaning that only header
82*86d7f5d3SJohn Marino        information should be printed.  */
83*86d7f5d3SJohn Marino     int header;
84*86d7f5d3SJohn Marino     /* Nonzero if the -t option was given, meaning that only the
85*86d7f5d3SJohn Marino        header and the descriptive text should be printed.  */
86*86d7f5d3SJohn Marino     int long_header;
87*86d7f5d3SJohn Marino     /* Nonzero if the -N option was seen, meaning that tag information
88*86d7f5d3SJohn Marino        should not be printed.  */
89*86d7f5d3SJohn Marino     int notags;
90*86d7f5d3SJohn Marino     /* Nonzero if the -b option was seen, meaning that only revisions
91*86d7f5d3SJohn Marino        on the default branch should be printed.  */
92*86d7f5d3SJohn Marino     int default_branch;
93*86d7f5d3SJohn Marino     /* Nonzero if the -S option was seen, meaning that the header/name
94*86d7f5d3SJohn Marino        should be suppressed if no revisions are selected.  */
95*86d7f5d3SJohn Marino     int sup_header;
96*86d7f5d3SJohn Marino     /* If not NULL, the value given for the -r option, which lists
97*86d7f5d3SJohn Marino        sets of revisions to be printed.  */
98*86d7f5d3SJohn Marino     struct option_revlist *revlist;
99*86d7f5d3SJohn Marino     /* If not NULL, the date pairs given for the -d option, which
100*86d7f5d3SJohn Marino        select date ranges to print.  */
101*86d7f5d3SJohn Marino     struct datelist *datelist;
102*86d7f5d3SJohn Marino     /* If not NULL, the single dates given for the -d option, which
103*86d7f5d3SJohn Marino        select specific revisions to print based on a date.  */
104*86d7f5d3SJohn Marino     struct datelist *singledatelist;
105*86d7f5d3SJohn Marino     /* If not NULL, the list of states given for the -s option, which
106*86d7f5d3SJohn Marino        only prints revisions of given states.  */
107*86d7f5d3SJohn Marino     List *statelist;
108*86d7f5d3SJohn Marino     /* If not NULL, the list of login names given for the -w option,
109*86d7f5d3SJohn Marino        which only prints revisions checked in by given users.  */
110*86d7f5d3SJohn Marino     List *authorlist;
111*86d7f5d3SJohn Marino };
112*86d7f5d3SJohn Marino 
113*86d7f5d3SJohn Marino /* This structure is used to pass information through walklist.  */
114*86d7f5d3SJohn Marino struct log_data_and_rcs
115*86d7f5d3SJohn Marino {
116*86d7f5d3SJohn Marino     struct log_data *log_data;
117*86d7f5d3SJohn Marino     struct revlist *revlist;
118*86d7f5d3SJohn Marino     RCSNode *rcs;
119*86d7f5d3SJohn Marino };
120*86d7f5d3SJohn Marino 
121*86d7f5d3SJohn Marino static int rlog_proc (int argc, char **argv, char *xwhere,
122*86d7f5d3SJohn Marino                       char *mwhere, char *mfile, int shorten,
123*86d7f5d3SJohn Marino                       int local_specified, char *mname, char *msg);
124*86d7f5d3SJohn Marino static Dtype log_dirproc (void *callerdat, const char *dir,
125*86d7f5d3SJohn Marino                           const char *repository, const char *update_dir,
126*86d7f5d3SJohn Marino                           List *entries);
127*86d7f5d3SJohn Marino static int log_fileproc (void *callerdat, struct file_info *finfo);
128*86d7f5d3SJohn Marino static struct option_revlist *log_parse_revlist (const char *);
129*86d7f5d3SJohn Marino static void log_parse_date (struct log_data *, const char *);
130*86d7f5d3SJohn Marino static void log_parse_list (List **, const char *);
131*86d7f5d3SJohn Marino static struct revlist *log_expand_revlist (RCSNode *, char *,
132*86d7f5d3SJohn Marino                                            struct option_revlist *, int);
133*86d7f5d3SJohn Marino static void log_free_revlist (struct revlist *);
134*86d7f5d3SJohn Marino static int log_version_requested (struct log_data *, struct revlist *,
135*86d7f5d3SJohn Marino 					 RCSNode *, RCSVers *);
136*86d7f5d3SJohn Marino static int log_symbol (Node *, void *);
137*86d7f5d3SJohn Marino static int log_count (Node *, void *);
138*86d7f5d3SJohn Marino static int log_fix_singledate (Node *, void *);
139*86d7f5d3SJohn Marino static int log_count_print (Node *, void *);
140*86d7f5d3SJohn Marino static void log_tree (struct log_data *, struct revlist *,
141*86d7f5d3SJohn Marino 			     RCSNode *, const char *);
142*86d7f5d3SJohn Marino static void log_abranch (struct log_data *, struct revlist *,
143*86d7f5d3SJohn Marino 				RCSNode *, const char *);
144*86d7f5d3SJohn Marino static void log_version (struct log_data *, struct revlist *,
145*86d7f5d3SJohn Marino 				RCSNode *, RCSVers *, int);
146*86d7f5d3SJohn Marino static int log_branch (Node *, void *);
147*86d7f5d3SJohn Marino static int version_compare (const char *, const char *, int);
148*86d7f5d3SJohn Marino 
149*86d7f5d3SJohn Marino static struct log_data log_data;
150*86d7f5d3SJohn Marino static int is_rlog;
151*86d7f5d3SJohn Marino 
152*86d7f5d3SJohn Marino static const char *const log_usage[] =
153*86d7f5d3SJohn Marino {
154*86d7f5d3SJohn Marino     "Usage: %s %s [-lRhtNb] [-r[revisions]] [-d dates] [-s states]\n",
155*86d7f5d3SJohn Marino     "    [-w[logins]] [files...]\n",
156*86d7f5d3SJohn Marino     "\t-l\tLocal directory only, no recursion.\n",
157*86d7f5d3SJohn Marino     "\t-b\tOnly list revisions on the default branch.\n",
158*86d7f5d3SJohn Marino     "\t-h\tOnly print header.\n",
159*86d7f5d3SJohn Marino     "\t-R\tOnly print name of RCS file.\n",
160*86d7f5d3SJohn Marino     "\t-t\tOnly print header and descriptive text.\n",
161*86d7f5d3SJohn Marino     "\t-N\tDo not list tags.\n",
162*86d7f5d3SJohn Marino     "\t-S\tDo not print name/header if no revisions selected.  -d, -r,\n",
163*86d7f5d3SJohn Marino     "\t\t-s, & -w have little effect in conjunction with -b, -h, -R, and\n",
164*86d7f5d3SJohn Marino     "\t\t-t without this option.\n",
165*86d7f5d3SJohn Marino     "\t-r[revisions]\tA comma-separated list of revisions to print:\n",
166*86d7f5d3SJohn Marino     "\t   rev1:rev2   Between rev1 and rev2, including rev1 and rev2.\n",
167*86d7f5d3SJohn Marino     "\t   rev1::rev2  Between rev1 and rev2, excluding rev1.\n",
168*86d7f5d3SJohn Marino     "\t   rev:        rev and following revisions on the same branch.\n",
169*86d7f5d3SJohn Marino     "\t   rev::       After rev on the same branch.\n",
170*86d7f5d3SJohn Marino     "\t   :rev        rev and previous revisions on the same branch.\n",
171*86d7f5d3SJohn Marino     "\t   ::rev       rev and previous revisions on the same branch.\n",
172*86d7f5d3SJohn Marino     "\t   rev         Just rev.\n",
173*86d7f5d3SJohn Marino     "\t   branch      All revisions on the branch.\n",
174*86d7f5d3SJohn Marino     "\t   branch.     The last revision on the branch.\n",
175*86d7f5d3SJohn Marino     "\t-d dates\tA semicolon-separated list of dates\n",
176*86d7f5d3SJohn Marino     "\t        \t(D1<D2 for range, D for latest before).\n",
177*86d7f5d3SJohn Marino     "\t-s states\tOnly list revisions with specified states.\n",
178*86d7f5d3SJohn Marino     "\t-w[logins]\tOnly list revisions checked in by specified logins.\n",
179*86d7f5d3SJohn Marino     "(Specify the --help global option for a list of other help options)\n",
180*86d7f5d3SJohn Marino     NULL
181*86d7f5d3SJohn Marino };
182*86d7f5d3SJohn Marino 
183*86d7f5d3SJohn Marino #ifdef CLIENT_SUPPORT
184*86d7f5d3SJohn Marino 
185*86d7f5d3SJohn Marino 
186*86d7f5d3SJohn Marino 
187*86d7f5d3SJohn Marino /* Helper function for send_arg_list.  */
188*86d7f5d3SJohn Marino static int
send_one(Node * node,void * closure)189*86d7f5d3SJohn Marino send_one (Node *node, void *closure)
190*86d7f5d3SJohn Marino {
191*86d7f5d3SJohn Marino     char *option = closure;
192*86d7f5d3SJohn Marino 
193*86d7f5d3SJohn Marino     send_to_server ("Argument ", 0);
194*86d7f5d3SJohn Marino     send_to_server (option, 0);
195*86d7f5d3SJohn Marino     if (strcmp (node->key, "@@MYSELF") == 0)
196*86d7f5d3SJohn Marino 	/* It is a bare -w option.  Note that we must send it as
197*86d7f5d3SJohn Marino 	   -w rather than messing with getcaller() or something (which on
198*86d7f5d3SJohn Marino 	   the client will return garbage).  */
199*86d7f5d3SJohn Marino 	;
200*86d7f5d3SJohn Marino     else
201*86d7f5d3SJohn Marino 	send_to_server (node->key, 0);
202*86d7f5d3SJohn Marino     send_to_server ("\012", 0);
203*86d7f5d3SJohn Marino     return 0;
204*86d7f5d3SJohn Marino }
205*86d7f5d3SJohn Marino 
206*86d7f5d3SJohn Marino 
207*86d7f5d3SJohn Marino 
208*86d7f5d3SJohn Marino /* For each element in ARG, send an argument consisting of OPTION
209*86d7f5d3SJohn Marino    concatenated with that element.  */
210*86d7f5d3SJohn Marino static void
send_arg_list(char * option,List * arg)211*86d7f5d3SJohn Marino send_arg_list (char *option, List *arg)
212*86d7f5d3SJohn Marino {
213*86d7f5d3SJohn Marino     if (arg == NULL)
214*86d7f5d3SJohn Marino 	return;
215*86d7f5d3SJohn Marino     walklist (arg, send_one, option);
216*86d7f5d3SJohn Marino }
217*86d7f5d3SJohn Marino 
218*86d7f5d3SJohn Marino #endif
219*86d7f5d3SJohn Marino 
220*86d7f5d3SJohn Marino 
221*86d7f5d3SJohn Marino 
222*86d7f5d3SJohn Marino int
cvslog(int argc,char ** argv)223*86d7f5d3SJohn Marino cvslog (int argc, char **argv)
224*86d7f5d3SJohn Marino {
225*86d7f5d3SJohn Marino     int c;
226*86d7f5d3SJohn Marino     int err = 0;
227*86d7f5d3SJohn Marino     int local = 0;
228*86d7f5d3SJohn Marino     struct option_revlist **prl;
229*86d7f5d3SJohn Marino 
230*86d7f5d3SJohn Marino     is_rlog = (strcmp (cvs_cmd_name, "rlog") == 0);
231*86d7f5d3SJohn Marino 
232*86d7f5d3SJohn Marino     if (argc == -1)
233*86d7f5d3SJohn Marino 	usage (log_usage);
234*86d7f5d3SJohn Marino 
235*86d7f5d3SJohn Marino     memset (&log_data, 0, sizeof log_data);
236*86d7f5d3SJohn Marino     prl = &log_data.revlist;
237*86d7f5d3SJohn Marino 
238*86d7f5d3SJohn Marino     optind = 0;
239*86d7f5d3SJohn Marino     while ((c = getopt (argc, argv, "+bd:hlNSRr::s:tw::")) != -1)
240*86d7f5d3SJohn Marino     {
241*86d7f5d3SJohn Marino 	switch (c)
242*86d7f5d3SJohn Marino 	{
243*86d7f5d3SJohn Marino 	    case 'b':
244*86d7f5d3SJohn Marino 		log_data.default_branch = 1;
245*86d7f5d3SJohn Marino 		break;
246*86d7f5d3SJohn Marino 	    case 'd':
247*86d7f5d3SJohn Marino 		log_parse_date (&log_data, optarg);
248*86d7f5d3SJohn Marino 		break;
249*86d7f5d3SJohn Marino 	    case 'h':
250*86d7f5d3SJohn Marino 		log_data.header = 1;
251*86d7f5d3SJohn Marino 		break;
252*86d7f5d3SJohn Marino 	    case 'l':
253*86d7f5d3SJohn Marino 		local = 1;
254*86d7f5d3SJohn Marino 		break;
255*86d7f5d3SJohn Marino 	    case 'N':
256*86d7f5d3SJohn Marino 		log_data.notags = 1;
257*86d7f5d3SJohn Marino 		break;
258*86d7f5d3SJohn Marino 	    case 'S':
259*86d7f5d3SJohn Marino 		log_data.sup_header = 1;
260*86d7f5d3SJohn Marino 		break;
261*86d7f5d3SJohn Marino 	    case 'R':
262*86d7f5d3SJohn Marino 		log_data.nameonly = 1;
263*86d7f5d3SJohn Marino 		break;
264*86d7f5d3SJohn Marino 	    case 'r':
265*86d7f5d3SJohn Marino 		*prl = log_parse_revlist (optarg);
266*86d7f5d3SJohn Marino 		prl = &(*prl)->next;
267*86d7f5d3SJohn Marino 		break;
268*86d7f5d3SJohn Marino 	    case 's':
269*86d7f5d3SJohn Marino 		log_parse_list (&log_data.statelist, optarg);
270*86d7f5d3SJohn Marino 		break;
271*86d7f5d3SJohn Marino 	    case 't':
272*86d7f5d3SJohn Marino 		log_data.long_header = 1;
273*86d7f5d3SJohn Marino 		break;
274*86d7f5d3SJohn Marino 	    case 'w':
275*86d7f5d3SJohn Marino 		if (optarg != NULL)
276*86d7f5d3SJohn Marino 		    log_parse_list (&log_data.authorlist, optarg);
277*86d7f5d3SJohn Marino 		else
278*86d7f5d3SJohn Marino 		    log_parse_list (&log_data.authorlist, "@@MYSELF");
279*86d7f5d3SJohn Marino 		break;
280*86d7f5d3SJohn Marino 	    case '?':
281*86d7f5d3SJohn Marino 	    default:
282*86d7f5d3SJohn Marino 		usage (log_usage);
283*86d7f5d3SJohn Marino 		break;
284*86d7f5d3SJohn Marino 	}
285*86d7f5d3SJohn Marino     }
286*86d7f5d3SJohn Marino     argc -= optind;
287*86d7f5d3SJohn Marino     argv += optind;
288*86d7f5d3SJohn Marino 
289*86d7f5d3SJohn Marino     wrap_setup ();
290*86d7f5d3SJohn Marino 
291*86d7f5d3SJohn Marino #ifdef CLIENT_SUPPORT
292*86d7f5d3SJohn Marino     if (current_parsed_root->isremote)
293*86d7f5d3SJohn Marino     {
294*86d7f5d3SJohn Marino 	struct datelist *p;
295*86d7f5d3SJohn Marino 	struct option_revlist *rp;
296*86d7f5d3SJohn Marino 	char datetmp[MAXDATELEN];
297*86d7f5d3SJohn Marino 
298*86d7f5d3SJohn Marino 	/* We're the local client.  Fire up the remote server.  */
299*86d7f5d3SJohn Marino 	start_server ();
300*86d7f5d3SJohn Marino 
301*86d7f5d3SJohn Marino 	if (is_rlog && !supported_request ("rlog"))
302*86d7f5d3SJohn Marino 	    error (1, 0, "server does not support rlog");
303*86d7f5d3SJohn Marino 
304*86d7f5d3SJohn Marino 	ign_setup ();
305*86d7f5d3SJohn Marino 
306*86d7f5d3SJohn Marino 	if (log_data.default_branch)
307*86d7f5d3SJohn Marino 	    send_arg ("-b");
308*86d7f5d3SJohn Marino 
309*86d7f5d3SJohn Marino 	while (log_data.datelist != NULL)
310*86d7f5d3SJohn Marino 	{
311*86d7f5d3SJohn Marino 	    p = log_data.datelist;
312*86d7f5d3SJohn Marino 	    log_data.datelist = p->next;
313*86d7f5d3SJohn Marino 	    send_to_server ("Argument -d\012", 0);
314*86d7f5d3SJohn Marino 	    send_to_server ("Argument ", 0);
315*86d7f5d3SJohn Marino 	    date_to_internet (datetmp, p->start);
316*86d7f5d3SJohn Marino 	    send_to_server (datetmp, 0);
317*86d7f5d3SJohn Marino 	    if (p->inclusive)
318*86d7f5d3SJohn Marino 		send_to_server ("<=", 0);
319*86d7f5d3SJohn Marino 	    else
320*86d7f5d3SJohn Marino 		send_to_server ("<", 0);
321*86d7f5d3SJohn Marino 	    date_to_internet (datetmp, p->end);
322*86d7f5d3SJohn Marino 	    send_to_server (datetmp, 0);
323*86d7f5d3SJohn Marino 	    send_to_server ("\012", 0);
324*86d7f5d3SJohn Marino 	    if (p->start)
325*86d7f5d3SJohn Marino 		free (p->start);
326*86d7f5d3SJohn Marino 	    if (p->end)
327*86d7f5d3SJohn Marino 		free (p->end);
328*86d7f5d3SJohn Marino 	    free (p);
329*86d7f5d3SJohn Marino 	}
330*86d7f5d3SJohn Marino 	while (log_data.singledatelist != NULL)
331*86d7f5d3SJohn Marino 	{
332*86d7f5d3SJohn Marino 	    p = log_data.singledatelist;
333*86d7f5d3SJohn Marino 	    log_data.singledatelist = p->next;
334*86d7f5d3SJohn Marino 	    send_to_server ("Argument -d\012", 0);
335*86d7f5d3SJohn Marino 	    send_to_server ("Argument ", 0);
336*86d7f5d3SJohn Marino 	    date_to_internet (datetmp, p->end);
337*86d7f5d3SJohn Marino 	    send_to_server (datetmp, 0);
338*86d7f5d3SJohn Marino 	    send_to_server ("\012", 0);
339*86d7f5d3SJohn Marino 	    if (p->end)
340*86d7f5d3SJohn Marino 		free (p->end);
341*86d7f5d3SJohn Marino 	    free (p);
342*86d7f5d3SJohn Marino 	}
343*86d7f5d3SJohn Marino 
344*86d7f5d3SJohn Marino 	if (log_data.header)
345*86d7f5d3SJohn Marino 	    send_arg ("-h");
346*86d7f5d3SJohn Marino 	if (local)
347*86d7f5d3SJohn Marino 	    send_arg("-l");
348*86d7f5d3SJohn Marino 	if (log_data.notags)
349*86d7f5d3SJohn Marino 	    send_arg("-N");
350*86d7f5d3SJohn Marino 	if (log_data.sup_header)
351*86d7f5d3SJohn Marino 	    send_arg("-S");
352*86d7f5d3SJohn Marino 	if (log_data.nameonly)
353*86d7f5d3SJohn Marino 	    send_arg("-R");
354*86d7f5d3SJohn Marino 	if (log_data.long_header)
355*86d7f5d3SJohn Marino 	    send_arg("-t");
356*86d7f5d3SJohn Marino 
357*86d7f5d3SJohn Marino 	while (log_data.revlist != NULL)
358*86d7f5d3SJohn Marino 	{
359*86d7f5d3SJohn Marino 	    rp = log_data.revlist;
360*86d7f5d3SJohn Marino 	    log_data.revlist = rp->next;
361*86d7f5d3SJohn Marino 	    send_to_server ("Argument -r", 0);
362*86d7f5d3SJohn Marino 	    if (rp->branchhead)
363*86d7f5d3SJohn Marino 	    {
364*86d7f5d3SJohn Marino 		if (rp->first != NULL)
365*86d7f5d3SJohn Marino 		    send_to_server (rp->first, 0);
366*86d7f5d3SJohn Marino 		send_to_server (".", 1);
367*86d7f5d3SJohn Marino 	    }
368*86d7f5d3SJohn Marino 	    else
369*86d7f5d3SJohn Marino 	    {
370*86d7f5d3SJohn Marino 		if (rp->first != NULL)
371*86d7f5d3SJohn Marino 		    send_to_server (rp->first, 0);
372*86d7f5d3SJohn Marino 		send_to_server (":", 1);
373*86d7f5d3SJohn Marino 		if (!rp->inclusive)
374*86d7f5d3SJohn Marino 		    send_to_server (":", 1);
375*86d7f5d3SJohn Marino 		if (rp->last != NULL)
376*86d7f5d3SJohn Marino 		    send_to_server (rp->last, 0);
377*86d7f5d3SJohn Marino 	    }
378*86d7f5d3SJohn Marino 	    send_to_server ("\012", 0);
379*86d7f5d3SJohn Marino 	    if (rp->first)
380*86d7f5d3SJohn Marino 		free (rp->first);
381*86d7f5d3SJohn Marino 	    if (rp->last)
382*86d7f5d3SJohn Marino 		free (rp->last);
383*86d7f5d3SJohn Marino 	    free (rp);
384*86d7f5d3SJohn Marino 	}
385*86d7f5d3SJohn Marino 	send_arg_list ("-s", log_data.statelist);
386*86d7f5d3SJohn Marino 	dellist (&log_data.statelist);
387*86d7f5d3SJohn Marino 	send_arg_list ("-w", log_data.authorlist);
388*86d7f5d3SJohn Marino 	dellist (&log_data.authorlist);
389*86d7f5d3SJohn Marino 	send_arg ("--");
390*86d7f5d3SJohn Marino 
391*86d7f5d3SJohn Marino 	if (is_rlog)
392*86d7f5d3SJohn Marino 	{
393*86d7f5d3SJohn Marino 	    int i;
394*86d7f5d3SJohn Marino 	    for (i = 0; i < argc; i++)
395*86d7f5d3SJohn Marino 		send_arg (argv[i]);
396*86d7f5d3SJohn Marino 	    send_to_server ("rlog\012", 0);
397*86d7f5d3SJohn Marino 	}
398*86d7f5d3SJohn Marino 	else
399*86d7f5d3SJohn Marino 	{
400*86d7f5d3SJohn Marino 	    send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
401*86d7f5d3SJohn Marino 	    send_file_names (argc, argv, SEND_EXPAND_WILD);
402*86d7f5d3SJohn Marino 	    send_to_server ("log\012", 0);
403*86d7f5d3SJohn Marino 	}
404*86d7f5d3SJohn Marino         err = get_responses_and_close ();
405*86d7f5d3SJohn Marino 	return err;
406*86d7f5d3SJohn Marino     }
407*86d7f5d3SJohn Marino #endif
408*86d7f5d3SJohn Marino 
409*86d7f5d3SJohn Marino     /* OK, now that we know we are local/server, we can resolve @@MYSELF
410*86d7f5d3SJohn Marino        into our user name.  */
411*86d7f5d3SJohn Marino     if (findnode (log_data.authorlist, "@@MYSELF") != NULL)
412*86d7f5d3SJohn Marino 	log_parse_list (&log_data.authorlist, getcaller ());
413*86d7f5d3SJohn Marino 
414*86d7f5d3SJohn Marino     if (is_rlog)
415*86d7f5d3SJohn Marino     {
416*86d7f5d3SJohn Marino 	DBM *db;
417*86d7f5d3SJohn Marino 	int i;
418*86d7f5d3SJohn Marino 	db = open_module ();
419*86d7f5d3SJohn Marino 	for (i = 0; i < argc; i++)
420*86d7f5d3SJohn Marino 	{
421*86d7f5d3SJohn Marino              err += do_module (db, argv[i], MISC, "Logging", rlog_proc,
422*86d7f5d3SJohn Marino                                NULL, 0, local, 0, 0, NULL);
423*86d7f5d3SJohn Marino 	}
424*86d7f5d3SJohn Marino 	close_module (db);
425*86d7f5d3SJohn Marino     }
426*86d7f5d3SJohn Marino     else
427*86d7f5d3SJohn Marino     {
428*86d7f5d3SJohn Marino         err = rlog_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL,
429*86d7f5d3SJohn Marino                          NULL);
430*86d7f5d3SJohn Marino     }
431*86d7f5d3SJohn Marino 
432*86d7f5d3SJohn Marino     while (log_data.revlist)
433*86d7f5d3SJohn Marino     {
434*86d7f5d3SJohn Marino 	struct option_revlist *rl = log_data.revlist->next;
435*86d7f5d3SJohn Marino 	if (log_data.revlist->first)
436*86d7f5d3SJohn Marino 	    free (log_data.revlist->first);
437*86d7f5d3SJohn Marino 	if (log_data.revlist->last)
438*86d7f5d3SJohn Marino 	    free (log_data.revlist->last);
439*86d7f5d3SJohn Marino 	free (log_data.revlist);
440*86d7f5d3SJohn Marino 	log_data.revlist = rl;
441*86d7f5d3SJohn Marino     }
442*86d7f5d3SJohn Marino     while (log_data.datelist)
443*86d7f5d3SJohn Marino     {
444*86d7f5d3SJohn Marino 	struct datelist *nd = log_data.datelist->next;
445*86d7f5d3SJohn Marino 	if (log_data.datelist->start)
446*86d7f5d3SJohn Marino 	    free (log_data.datelist->start);
447*86d7f5d3SJohn Marino 	if (log_data.datelist->end)
448*86d7f5d3SJohn Marino 	    free (log_data.datelist->end);
449*86d7f5d3SJohn Marino 	free (log_data.datelist);
450*86d7f5d3SJohn Marino 	log_data.datelist = nd;
451*86d7f5d3SJohn Marino     }
452*86d7f5d3SJohn Marino     while (log_data.singledatelist)
453*86d7f5d3SJohn Marino     {
454*86d7f5d3SJohn Marino 	struct datelist *nd = log_data.singledatelist->next;
455*86d7f5d3SJohn Marino 	if (log_data.singledatelist->start)
456*86d7f5d3SJohn Marino 	    free (log_data.singledatelist->start);
457*86d7f5d3SJohn Marino 	if (log_data.singledatelist->end)
458*86d7f5d3SJohn Marino 	    free (log_data.singledatelist->end);
459*86d7f5d3SJohn Marino 	free (log_data.singledatelist);
460*86d7f5d3SJohn Marino 	log_data.singledatelist = nd;
461*86d7f5d3SJohn Marino     }
462*86d7f5d3SJohn Marino     dellist (&log_data.statelist);
463*86d7f5d3SJohn Marino     dellist (&log_data.authorlist);
464*86d7f5d3SJohn Marino 
465*86d7f5d3SJohn Marino     return err;
466*86d7f5d3SJohn Marino }
467*86d7f5d3SJohn Marino 
468*86d7f5d3SJohn Marino 
469*86d7f5d3SJohn Marino 
470*86d7f5d3SJohn Marino static int
rlog_proc(int argc,char ** argv,char * xwhere,char * mwhere,char * mfile,int shorten,int local,char * mname,char * msg)471*86d7f5d3SJohn Marino rlog_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
472*86d7f5d3SJohn Marino            int shorten, int local, char *mname, char *msg)
473*86d7f5d3SJohn Marino {
474*86d7f5d3SJohn Marino     /* Begin section which is identical to patch_proc--should this
475*86d7f5d3SJohn Marino        be abstracted out somehow?  */
476*86d7f5d3SJohn Marino     char *myargv[2];
477*86d7f5d3SJohn Marino     int err = 0;
478*86d7f5d3SJohn Marino     int which;
479*86d7f5d3SJohn Marino     char *repository = NULL;
480*86d7f5d3SJohn Marino     char *where;
481*86d7f5d3SJohn Marino 
482*86d7f5d3SJohn Marino     if (is_rlog)
483*86d7f5d3SJohn Marino     {
484*86d7f5d3SJohn Marino 	repository = xmalloc (strlen (current_parsed_root->directory)
485*86d7f5d3SJohn Marino                               + strlen (argv[0])
486*86d7f5d3SJohn Marino 			      + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
487*86d7f5d3SJohn Marino 	(void)sprintf (repository, "%s/%s",
488*86d7f5d3SJohn Marino                        current_parsed_root->directory, argv[0]);
489*86d7f5d3SJohn Marino 	where = xmalloc (strlen (argv[0])
490*86d7f5d3SJohn Marino                          + (mfile == NULL ? 0 : strlen (mfile) + 1)
491*86d7f5d3SJohn Marino 			 + 1);
492*86d7f5d3SJohn Marino 	(void)strcpy (where, argv[0]);
493*86d7f5d3SJohn Marino 
494*86d7f5d3SJohn Marino 	/* If mfile isn't null, we need to set up to do only part of theu
495*86d7f5d3SJohn Marino          * module.
496*86d7f5d3SJohn Marino          */
497*86d7f5d3SJohn Marino 	if (mfile != NULL)
498*86d7f5d3SJohn Marino 	{
499*86d7f5d3SJohn Marino 	    char *cp;
500*86d7f5d3SJohn Marino 	    char *path;
501*86d7f5d3SJohn Marino 
502*86d7f5d3SJohn Marino 	    /* If the portion of the module is a path, put the dir part on
503*86d7f5d3SJohn Marino              * repos.
504*86d7f5d3SJohn Marino              */
505*86d7f5d3SJohn Marino 	    if ((cp = strrchr (mfile, '/')) != NULL)
506*86d7f5d3SJohn Marino 	    {
507*86d7f5d3SJohn Marino 		*cp = '\0';
508*86d7f5d3SJohn Marino 		(void)strcat (repository, "/");
509*86d7f5d3SJohn Marino 		(void)strcat (repository, mfile);
510*86d7f5d3SJohn Marino 		(void)strcat (where, "/");
511*86d7f5d3SJohn Marino 		(void)strcat (where, mfile);
512*86d7f5d3SJohn Marino 		mfile = cp + 1;
513*86d7f5d3SJohn Marino 	    }
514*86d7f5d3SJohn Marino 
515*86d7f5d3SJohn Marino 	    /* take care of the rest */
516*86d7f5d3SJohn Marino 	    path = Xasprintf ("%s/%s", repository, mfile);
517*86d7f5d3SJohn Marino 	    if (isdir (path))
518*86d7f5d3SJohn Marino 	    {
519*86d7f5d3SJohn Marino 		/* directory means repository gets the dir tacked on */
520*86d7f5d3SJohn Marino 		(void)strcpy (repository, path);
521*86d7f5d3SJohn Marino 		(void)strcat (where, "/");
522*86d7f5d3SJohn Marino 		(void)strcat (where, mfile);
523*86d7f5d3SJohn Marino 	    }
524*86d7f5d3SJohn Marino 	    else
525*86d7f5d3SJohn Marino 	    {
526*86d7f5d3SJohn Marino 		myargv[0] = argv[0];
527*86d7f5d3SJohn Marino 		myargv[1] = mfile;
528*86d7f5d3SJohn Marino 		argc = 2;
529*86d7f5d3SJohn Marino 		argv = myargv;
530*86d7f5d3SJohn Marino 	    }
531*86d7f5d3SJohn Marino 	    free (path);
532*86d7f5d3SJohn Marino 	}
533*86d7f5d3SJohn Marino 
534*86d7f5d3SJohn Marino 	/* cd to the starting repository */
535*86d7f5d3SJohn Marino 	if (CVS_CHDIR (repository) < 0)
536*86d7f5d3SJohn Marino 	{
537*86d7f5d3SJohn Marino 	    error (0, errno, "cannot chdir to %s", repository);
538*86d7f5d3SJohn Marino 	    free (repository);
539*86d7f5d3SJohn Marino 	    free (where);
540*86d7f5d3SJohn Marino 	    return 1;
541*86d7f5d3SJohn Marino 	}
542*86d7f5d3SJohn Marino 	/* End section which is identical to patch_proc.  */
543*86d7f5d3SJohn Marino 
544*86d7f5d3SJohn Marino 	which = W_REPOS | W_ATTIC;
545*86d7f5d3SJohn Marino     }
546*86d7f5d3SJohn Marino     else
547*86d7f5d3SJohn Marino     {
548*86d7f5d3SJohn Marino         repository = NULL;
549*86d7f5d3SJohn Marino         where = NULL;
550*86d7f5d3SJohn Marino         which = W_LOCAL | W_REPOS | W_ATTIC;
551*86d7f5d3SJohn Marino     }
552*86d7f5d3SJohn Marino 
553*86d7f5d3SJohn Marino     err = start_recursion (log_fileproc, NULL, log_dirproc,
554*86d7f5d3SJohn Marino 			   NULL, &log_data,
555*86d7f5d3SJohn Marino 			   argc - 1, argv + 1, local, which, 0, CVS_LOCK_READ,
556*86d7f5d3SJohn Marino 			   where, 1, repository);
557*86d7f5d3SJohn Marino 
558*86d7f5d3SJohn Marino     if (!(which & W_LOCAL)) free (repository);
559*86d7f5d3SJohn Marino     if (where) free (where);
560*86d7f5d3SJohn Marino 
561*86d7f5d3SJohn Marino     return err;
562*86d7f5d3SJohn Marino }
563*86d7f5d3SJohn Marino 
564*86d7f5d3SJohn Marino 
565*86d7f5d3SJohn Marino 
566*86d7f5d3SJohn Marino /*
567*86d7f5d3SJohn Marino  * Parse a revision list specification.
568*86d7f5d3SJohn Marino  */
569*86d7f5d3SJohn Marino static struct option_revlist *
log_parse_revlist(const char * argstring)570*86d7f5d3SJohn Marino log_parse_revlist (const char *argstring)
571*86d7f5d3SJohn Marino {
572*86d7f5d3SJohn Marino     char *orig_copy, *copy;
573*86d7f5d3SJohn Marino     struct option_revlist *ret, **pr;
574*86d7f5d3SJohn Marino 
575*86d7f5d3SJohn Marino     /* Unfortunately, rlog accepts -r without an argument to mean that
576*86d7f5d3SJohn Marino        latest revision on the default branch, so we must support that
577*86d7f5d3SJohn Marino        for compatibility.  */
578*86d7f5d3SJohn Marino     if (argstring == NULL)
579*86d7f5d3SJohn Marino 	argstring = "";
580*86d7f5d3SJohn Marino 
581*86d7f5d3SJohn Marino     ret = NULL;
582*86d7f5d3SJohn Marino     pr = &ret;
583*86d7f5d3SJohn Marino 
584*86d7f5d3SJohn Marino     /* Copy the argument into memory so that we can change it.  We
585*86d7f5d3SJohn Marino        don't want to change the argument because, at least as of this
586*86d7f5d3SJohn Marino        writing, we will use it if we send the arguments to the server.  */
587*86d7f5d3SJohn Marino     orig_copy = copy = xstrdup (argstring);
588*86d7f5d3SJohn Marino     while (copy != NULL)
589*86d7f5d3SJohn Marino     {
590*86d7f5d3SJohn Marino 	char *comma;
591*86d7f5d3SJohn Marino 	struct option_revlist *r;
592*86d7f5d3SJohn Marino 
593*86d7f5d3SJohn Marino 	comma = strchr (copy, ',');
594*86d7f5d3SJohn Marino 	if (comma != NULL)
595*86d7f5d3SJohn Marino 	    *comma++ = '\0';
596*86d7f5d3SJohn Marino 
597*86d7f5d3SJohn Marino 	r = xmalloc (sizeof *r);
598*86d7f5d3SJohn Marino 	r->next = NULL;
599*86d7f5d3SJohn Marino 	r->first = copy;
600*86d7f5d3SJohn Marino 	r->branchhead = 0;
601*86d7f5d3SJohn Marino 	r->last = strchr (copy, ':');
602*86d7f5d3SJohn Marino 	if (r->last != NULL)
603*86d7f5d3SJohn Marino 	{
604*86d7f5d3SJohn Marino 	    *r->last++ = '\0';
605*86d7f5d3SJohn Marino 	    r->inclusive = (*r->last != ':');
606*86d7f5d3SJohn Marino 	    if (!r->inclusive)
607*86d7f5d3SJohn Marino 		r->last++;
608*86d7f5d3SJohn Marino 	}
609*86d7f5d3SJohn Marino 	else
610*86d7f5d3SJohn Marino 	{
611*86d7f5d3SJohn Marino 	    r->last = r->first;
612*86d7f5d3SJohn Marino 	    r->inclusive = 1;
613*86d7f5d3SJohn Marino 	    if (r->first[0] != '\0' && r->first[strlen (r->first) - 1] == '.')
614*86d7f5d3SJohn Marino 	    {
615*86d7f5d3SJohn Marino 		r->branchhead = 1;
616*86d7f5d3SJohn Marino 		r->first[strlen (r->first) - 1] = '\0';
617*86d7f5d3SJohn Marino 	    }
618*86d7f5d3SJohn Marino 	}
619*86d7f5d3SJohn Marino 
620*86d7f5d3SJohn Marino 	if (*r->first == '\0')
621*86d7f5d3SJohn Marino 	    r->first = NULL;
622*86d7f5d3SJohn Marino 	if (*r->last == '\0')
623*86d7f5d3SJohn Marino 	    r->last = NULL;
624*86d7f5d3SJohn Marino 
625*86d7f5d3SJohn Marino 	if (r->first != NULL)
626*86d7f5d3SJohn Marino 	    r->first = xstrdup (r->first);
627*86d7f5d3SJohn Marino 	if (r->last != NULL)
628*86d7f5d3SJohn Marino 	    r->last = xstrdup (r->last);
629*86d7f5d3SJohn Marino 
630*86d7f5d3SJohn Marino 	*pr = r;
631*86d7f5d3SJohn Marino 	pr = &r->next;
632*86d7f5d3SJohn Marino 
633*86d7f5d3SJohn Marino 	copy = comma;
634*86d7f5d3SJohn Marino     }
635*86d7f5d3SJohn Marino 
636*86d7f5d3SJohn Marino     free (orig_copy);
637*86d7f5d3SJohn Marino     return ret;
638*86d7f5d3SJohn Marino }
639*86d7f5d3SJohn Marino 
640*86d7f5d3SJohn Marino 
641*86d7f5d3SJohn Marino 
642*86d7f5d3SJohn Marino /*
643*86d7f5d3SJohn Marino  * Parse a date specification.
644*86d7f5d3SJohn Marino  */
645*86d7f5d3SJohn Marino static void
log_parse_date(struct log_data * log_data,const char * argstring)646*86d7f5d3SJohn Marino log_parse_date (struct log_data *log_data, const char *argstring)
647*86d7f5d3SJohn Marino {
648*86d7f5d3SJohn Marino     char *orig_copy, *copy;
649*86d7f5d3SJohn Marino 
650*86d7f5d3SJohn Marino     /* Copy the argument into memory so that we can change it.  We
651*86d7f5d3SJohn Marino        don't want to change the argument because, at least as of this
652*86d7f5d3SJohn Marino        writing, we will use it if we send the arguments to the server.  */
653*86d7f5d3SJohn Marino     orig_copy = copy = xstrdup (argstring);
654*86d7f5d3SJohn Marino     while (copy != NULL)
655*86d7f5d3SJohn Marino     {
656*86d7f5d3SJohn Marino 	struct datelist *nd, **pd;
657*86d7f5d3SJohn Marino 	char *cpend, *cp, *ds, *de;
658*86d7f5d3SJohn Marino 
659*86d7f5d3SJohn Marino 	nd = xmalloc (sizeof *nd);
660*86d7f5d3SJohn Marino 
661*86d7f5d3SJohn Marino 	cpend = strchr (copy, ';');
662*86d7f5d3SJohn Marino 	if (cpend != NULL)
663*86d7f5d3SJohn Marino 	    *cpend++ = '\0';
664*86d7f5d3SJohn Marino 
665*86d7f5d3SJohn Marino 	pd = &log_data->datelist;
666*86d7f5d3SJohn Marino 	nd->inclusive = 0;
667*86d7f5d3SJohn Marino 
668*86d7f5d3SJohn Marino 	if ((cp = strchr (copy, '>')) != NULL)
669*86d7f5d3SJohn Marino 	{
670*86d7f5d3SJohn Marino 	    *cp++ = '\0';
671*86d7f5d3SJohn Marino 	    if (*cp == '=')
672*86d7f5d3SJohn Marino 	    {
673*86d7f5d3SJohn Marino 		++cp;
674*86d7f5d3SJohn Marino 		nd->inclusive = 1;
675*86d7f5d3SJohn Marino 	    }
676*86d7f5d3SJohn Marino 	    ds = cp;
677*86d7f5d3SJohn Marino 	    de = copy;
678*86d7f5d3SJohn Marino 	}
679*86d7f5d3SJohn Marino 	else if ((cp = strchr (copy, '<')) != NULL)
680*86d7f5d3SJohn Marino 	{
681*86d7f5d3SJohn Marino 	    *cp++ = '\0';
682*86d7f5d3SJohn Marino 	    if (*cp == '=')
683*86d7f5d3SJohn Marino 	    {
684*86d7f5d3SJohn Marino 		++cp;
685*86d7f5d3SJohn Marino 		nd->inclusive = 1;
686*86d7f5d3SJohn Marino 	    }
687*86d7f5d3SJohn Marino 	    ds = copy;
688*86d7f5d3SJohn Marino 	    de = cp;
689*86d7f5d3SJohn Marino 	}
690*86d7f5d3SJohn Marino 	else
691*86d7f5d3SJohn Marino 	{
692*86d7f5d3SJohn Marino 	    ds = NULL;
693*86d7f5d3SJohn Marino 	    de = copy;
694*86d7f5d3SJohn Marino 	    pd = &log_data->singledatelist;
695*86d7f5d3SJohn Marino 	}
696*86d7f5d3SJohn Marino 
697*86d7f5d3SJohn Marino 	if (ds == NULL)
698*86d7f5d3SJohn Marino 	    nd->start = NULL;
699*86d7f5d3SJohn Marino 	else if (*ds != '\0')
700*86d7f5d3SJohn Marino 	    nd->start = Make_Date (ds);
701*86d7f5d3SJohn Marino 	else
702*86d7f5d3SJohn Marino 	{
703*86d7f5d3SJohn Marino 	  /* 1970 was the beginning of time, as far as get_date and
704*86d7f5d3SJohn Marino 	     Make_Date are concerned.  FIXME: That is true only if time_t
705*86d7f5d3SJohn Marino 	     is a POSIX-style time and there is nothing in ANSI that
706*86d7f5d3SJohn Marino 	     mandates that.  It would be cleaner to set a flag saying
707*86d7f5d3SJohn Marino 	     whether or not there is a start date.  */
708*86d7f5d3SJohn Marino 	    nd->start = Make_Date ("1/1/1970 UTC");
709*86d7f5d3SJohn Marino 	}
710*86d7f5d3SJohn Marino 
711*86d7f5d3SJohn Marino 	if (*de != '\0')
712*86d7f5d3SJohn Marino 	    nd->end = Make_Date (de);
713*86d7f5d3SJohn Marino 	else
714*86d7f5d3SJohn Marino 	{
715*86d7f5d3SJohn Marino 	    /* We want to set the end date to some time sufficiently far
716*86d7f5d3SJohn Marino 	       in the future to pick up all revisions that have been
717*86d7f5d3SJohn Marino 	       created since the specified date and the time `cvs log'
718*86d7f5d3SJohn Marino 	       completes.  FIXME: The date in question only makes sense
719*86d7f5d3SJohn Marino 	       if time_t is a POSIX-style time and it is 32 bits
720*86d7f5d3SJohn Marino 	       and signed.  We should instead be setting a flag saying
721*86d7f5d3SJohn Marino 	       whether or not there is an end date.  Note that using
722*86d7f5d3SJohn Marino 	       something like "next week" would break the testsuite (and,
723*86d7f5d3SJohn Marino 	       perhaps less importantly, loses if the clock is set grossly
724*86d7f5d3SJohn Marino 	       wrong).  */
725*86d7f5d3SJohn Marino 	    nd->end = Make_Date ("2038-01-01");
726*86d7f5d3SJohn Marino 	}
727*86d7f5d3SJohn Marino 
728*86d7f5d3SJohn Marino 	nd->next = *pd;
729*86d7f5d3SJohn Marino 	*pd = nd;
730*86d7f5d3SJohn Marino 
731*86d7f5d3SJohn Marino 	copy = cpend;
732*86d7f5d3SJohn Marino     }
733*86d7f5d3SJohn Marino 
734*86d7f5d3SJohn Marino     free (orig_copy);
735*86d7f5d3SJohn Marino }
736*86d7f5d3SJohn Marino 
737*86d7f5d3SJohn Marino 
738*86d7f5d3SJohn Marino 
739*86d7f5d3SJohn Marino /*
740*86d7f5d3SJohn Marino  * Parse a comma separated list of items, and add each one to *PLIST.
741*86d7f5d3SJohn Marino  */
742*86d7f5d3SJohn Marino static void
log_parse_list(List ** plist,const char * argstring)743*86d7f5d3SJohn Marino log_parse_list (List **plist, const char *argstring)
744*86d7f5d3SJohn Marino {
745*86d7f5d3SJohn Marino     while (1)
746*86d7f5d3SJohn Marino     {
747*86d7f5d3SJohn Marino 	Node *p;
748*86d7f5d3SJohn Marino 	char *cp;
749*86d7f5d3SJohn Marino 
750*86d7f5d3SJohn Marino 	p = getnode ();
751*86d7f5d3SJohn Marino 
752*86d7f5d3SJohn Marino 	cp = strchr (argstring, ',');
753*86d7f5d3SJohn Marino 	if (cp == NULL)
754*86d7f5d3SJohn Marino 	    p->key = xstrdup (argstring);
755*86d7f5d3SJohn Marino 	else
756*86d7f5d3SJohn Marino 	{
757*86d7f5d3SJohn Marino 	    size_t len;
758*86d7f5d3SJohn Marino 
759*86d7f5d3SJohn Marino 	    len = cp - argstring;
760*86d7f5d3SJohn Marino 	    p->key = xmalloc (len + 1);
761*86d7f5d3SJohn Marino 	    strncpy (p->key, argstring, len);
762*86d7f5d3SJohn Marino 	    p->key[len] = '\0';
763*86d7f5d3SJohn Marino 	}
764*86d7f5d3SJohn Marino 
765*86d7f5d3SJohn Marino 	if (*plist == NULL)
766*86d7f5d3SJohn Marino 	    *plist = getlist ();
767*86d7f5d3SJohn Marino 	if (addnode (*plist, p) != 0)
768*86d7f5d3SJohn Marino 	    freenode (p);
769*86d7f5d3SJohn Marino 
770*86d7f5d3SJohn Marino 	if (cp == NULL)
771*86d7f5d3SJohn Marino 	    break;
772*86d7f5d3SJohn Marino 
773*86d7f5d3SJohn Marino 	argstring = cp + 1;
774*86d7f5d3SJohn Marino     }
775*86d7f5d3SJohn Marino }
776*86d7f5d3SJohn Marino 
777*86d7f5d3SJohn Marino 
778*86d7f5d3SJohn Marino 
779*86d7f5d3SJohn Marino static int
printlock_proc(Node * lock,void * foo)780*86d7f5d3SJohn Marino printlock_proc (Node *lock, void *foo)
781*86d7f5d3SJohn Marino {
782*86d7f5d3SJohn Marino     cvs_output ("\n\t", 2);
783*86d7f5d3SJohn Marino     cvs_output (lock->data, 0);
784*86d7f5d3SJohn Marino     cvs_output (": ", 2);
785*86d7f5d3SJohn Marino     cvs_output (lock->key, 0);
786*86d7f5d3SJohn Marino     return 0;
787*86d7f5d3SJohn Marino }
788*86d7f5d3SJohn Marino 
789*86d7f5d3SJohn Marino 
790*86d7f5d3SJohn Marino 
791*86d7f5d3SJohn Marino /*
792*86d7f5d3SJohn Marino  * Do an rlog on a file
793*86d7f5d3SJohn Marino  */
794*86d7f5d3SJohn Marino static int
log_fileproc(void * callerdat,struct file_info * finfo)795*86d7f5d3SJohn Marino log_fileproc (void *callerdat, struct file_info *finfo)
796*86d7f5d3SJohn Marino {
797*86d7f5d3SJohn Marino     struct log_data *log_data = callerdat;
798*86d7f5d3SJohn Marino     Node *p;
799*86d7f5d3SJohn Marino     char *baserev;
800*86d7f5d3SJohn Marino     int selrev = -1;
801*86d7f5d3SJohn Marino     RCSNode *rcsfile;
802*86d7f5d3SJohn Marino     char buf[50];
803*86d7f5d3SJohn Marino     struct revlist *revlist = NULL;
804*86d7f5d3SJohn Marino     struct log_data_and_rcs log_data_and_rcs;
805*86d7f5d3SJohn Marino 
806*86d7f5d3SJohn Marino     rcsfile = finfo->rcs;
807*86d7f5d3SJohn Marino     p = findnode (finfo->entries, finfo->file);
808*86d7f5d3SJohn Marino     if (p != NULL)
809*86d7f5d3SJohn Marino     {
810*86d7f5d3SJohn Marino 	Entnode *e = p->data;
811*86d7f5d3SJohn Marino 	baserev = e->version;
812*86d7f5d3SJohn Marino 	if (baserev[0] == '-') ++baserev;
813*86d7f5d3SJohn Marino     }
814*86d7f5d3SJohn Marino     else
815*86d7f5d3SJohn Marino 	baserev = NULL;
816*86d7f5d3SJohn Marino 
817*86d7f5d3SJohn Marino     if (rcsfile == NULL)
818*86d7f5d3SJohn Marino     {
819*86d7f5d3SJohn Marino 	/* no rcs file.  What *do* we know about this file? */
820*86d7f5d3SJohn Marino 	if (baserev != NULL)
821*86d7f5d3SJohn Marino 	{
822*86d7f5d3SJohn Marino 	    if (baserev[0] == '0' && baserev[1] == '\0')
823*86d7f5d3SJohn Marino 	    {
824*86d7f5d3SJohn Marino 		if (!really_quiet)
825*86d7f5d3SJohn Marino 		    error (0, 0, "%s has been added, but not committed",
826*86d7f5d3SJohn Marino 			   finfo->file);
827*86d7f5d3SJohn Marino 		return 0;
828*86d7f5d3SJohn Marino 	    }
829*86d7f5d3SJohn Marino 	}
830*86d7f5d3SJohn Marino 
831*86d7f5d3SJohn Marino 	if (!really_quiet)
832*86d7f5d3SJohn Marino 	    error (0, 0, "nothing known about %s", finfo->file);
833*86d7f5d3SJohn Marino 
834*86d7f5d3SJohn Marino 	return 1;
835*86d7f5d3SJohn Marino     }
836*86d7f5d3SJohn Marino 
837*86d7f5d3SJohn Marino     if (log_data->sup_header || !log_data->nameonly)
838*86d7f5d3SJohn Marino     {
839*86d7f5d3SJohn Marino 
840*86d7f5d3SJohn Marino 	/* We will need all the information in the RCS file.  */
841*86d7f5d3SJohn Marino 	RCS_fully_parse (rcsfile);
842*86d7f5d3SJohn Marino 
843*86d7f5d3SJohn Marino 	/* Turn any symbolic revisions in the revision list into numeric
844*86d7f5d3SJohn Marino 	   revisions.  */
845*86d7f5d3SJohn Marino 	revlist = log_expand_revlist (rcsfile, baserev, log_data->revlist,
846*86d7f5d3SJohn Marino 				      log_data->default_branch);
847*86d7f5d3SJohn Marino 	if (log_data->sup_header
848*86d7f5d3SJohn Marino             || (!log_data->header && !log_data->long_header))
849*86d7f5d3SJohn Marino 	{
850*86d7f5d3SJohn Marino 	    log_data_and_rcs.log_data = log_data;
851*86d7f5d3SJohn Marino 	    log_data_and_rcs.revlist = revlist;
852*86d7f5d3SJohn Marino 	    log_data_and_rcs.rcs = rcsfile;
853*86d7f5d3SJohn Marino 
854*86d7f5d3SJohn Marino 	    /* If any single dates were specified, we need to identify the
855*86d7f5d3SJohn Marino 	       revisions they select.  Each one selects the single
856*86d7f5d3SJohn Marino 	       revision, which is otherwise selected, of that date or
857*86d7f5d3SJohn Marino 	       earlier.  The log_fix_singledate routine will fill in the
858*86d7f5d3SJohn Marino 	       start date for each specific revision.  */
859*86d7f5d3SJohn Marino 	    if (log_data->singledatelist != NULL)
860*86d7f5d3SJohn Marino 		walklist (rcsfile->versions, log_fix_singledate,
861*86d7f5d3SJohn Marino 			  &log_data_and_rcs);
862*86d7f5d3SJohn Marino 
863*86d7f5d3SJohn Marino 	    selrev = walklist (rcsfile->versions, log_count_print,
864*86d7f5d3SJohn Marino 			       &log_data_and_rcs);
865*86d7f5d3SJohn Marino 	    if (log_data->sup_header && selrev == 0)
866*86d7f5d3SJohn Marino 	    {
867*86d7f5d3SJohn Marino 		log_free_revlist (revlist);
868*86d7f5d3SJohn Marino 		return 0;
869*86d7f5d3SJohn Marino 	    }
870*86d7f5d3SJohn Marino 	}
871*86d7f5d3SJohn Marino 
872*86d7f5d3SJohn Marino     }
873*86d7f5d3SJohn Marino 
874*86d7f5d3SJohn Marino     if (log_data->nameonly)
875*86d7f5d3SJohn Marino     {
876*86d7f5d3SJohn Marino 	cvs_output (rcsfile->print_path, 0);
877*86d7f5d3SJohn Marino 	cvs_output ("\n", 1);
878*86d7f5d3SJohn Marino 	log_free_revlist (revlist);
879*86d7f5d3SJohn Marino 	return 0;
880*86d7f5d3SJohn Marino     }
881*86d7f5d3SJohn Marino 
882*86d7f5d3SJohn Marino     /* The output here is intended to be exactly compatible with the
883*86d7f5d3SJohn Marino        output of rlog.  I'm not sure whether this code should be here
884*86d7f5d3SJohn Marino        or in rcs.c; I put it here because it is specific to the log
885*86d7f5d3SJohn Marino        function, even though it uses information gathered by the
886*86d7f5d3SJohn Marino        functions in rcs.c.  */
887*86d7f5d3SJohn Marino 
888*86d7f5d3SJohn Marino     cvs_output ("\n", 1);
889*86d7f5d3SJohn Marino 
890*86d7f5d3SJohn Marino     cvs_output ("RCS file: ", 0);
891*86d7f5d3SJohn Marino     cvs_output (rcsfile->print_path, 0);
892*86d7f5d3SJohn Marino 
893*86d7f5d3SJohn Marino     if (!is_rlog)
894*86d7f5d3SJohn Marino     {
895*86d7f5d3SJohn Marino 	cvs_output ("\nWorking file: ", 0);
896*86d7f5d3SJohn Marino 	if (finfo->update_dir[0] != '\0')
897*86d7f5d3SJohn Marino 	{
898*86d7f5d3SJohn Marino 	    cvs_output (finfo->update_dir, 0);
899*86d7f5d3SJohn Marino 	    cvs_output ("/", 0);
900*86d7f5d3SJohn Marino 	}
901*86d7f5d3SJohn Marino 	cvs_output (finfo->file, 0);
902*86d7f5d3SJohn Marino     }
903*86d7f5d3SJohn Marino 
904*86d7f5d3SJohn Marino     cvs_output ("\nhead:", 0);
905*86d7f5d3SJohn Marino     if (rcsfile->head != NULL)
906*86d7f5d3SJohn Marino     {
907*86d7f5d3SJohn Marino 	cvs_output (" ", 1);
908*86d7f5d3SJohn Marino 	cvs_output (rcsfile->head, 0);
909*86d7f5d3SJohn Marino     }
910*86d7f5d3SJohn Marino 
911*86d7f5d3SJohn Marino     cvs_output ("\nbranch:", 0);
912*86d7f5d3SJohn Marino     if (rcsfile->branch != NULL)
913*86d7f5d3SJohn Marino     {
914*86d7f5d3SJohn Marino 	cvs_output (" ", 1);
915*86d7f5d3SJohn Marino 	cvs_output (rcsfile->branch, 0);
916*86d7f5d3SJohn Marino     }
917*86d7f5d3SJohn Marino 
918*86d7f5d3SJohn Marino     cvs_output ("\nlocks:", 0);
919*86d7f5d3SJohn Marino     if (rcsfile->strict_locks)
920*86d7f5d3SJohn Marino 	cvs_output (" strict", 0);
921*86d7f5d3SJohn Marino     walklist (RCS_getlocks (rcsfile), printlock_proc, NULL);
922*86d7f5d3SJohn Marino 
923*86d7f5d3SJohn Marino     cvs_output ("\naccess list:", 0);
924*86d7f5d3SJohn Marino     if (rcsfile->access != NULL)
925*86d7f5d3SJohn Marino     {
926*86d7f5d3SJohn Marino 	const char *cp;
927*86d7f5d3SJohn Marino 
928*86d7f5d3SJohn Marino 	cp = rcsfile->access;
929*86d7f5d3SJohn Marino 	while (*cp != '\0')
930*86d7f5d3SJohn Marino 	{
931*86d7f5d3SJohn Marino 		const char *cp2;
932*86d7f5d3SJohn Marino 
933*86d7f5d3SJohn Marino 		cvs_output ("\n\t", 2);
934*86d7f5d3SJohn Marino 		cp2 = cp;
935*86d7f5d3SJohn Marino 		while (!isspace ((unsigned char)*cp2) && *cp2 != '\0')
936*86d7f5d3SJohn Marino 		    ++cp2;
937*86d7f5d3SJohn Marino 		cvs_output (cp, cp2 - cp);
938*86d7f5d3SJohn Marino 		cp = cp2;
939*86d7f5d3SJohn Marino 		while (isspace ((unsigned char)*cp) && *cp != '\0')
940*86d7f5d3SJohn Marino 		    ++cp;
941*86d7f5d3SJohn Marino 	}
942*86d7f5d3SJohn Marino     }
943*86d7f5d3SJohn Marino 
944*86d7f5d3SJohn Marino     if (!log_data->notags)
945*86d7f5d3SJohn Marino     {
946*86d7f5d3SJohn Marino 	List *syms;
947*86d7f5d3SJohn Marino 
948*86d7f5d3SJohn Marino 	cvs_output ("\nsymbolic names:", 0);
949*86d7f5d3SJohn Marino 	syms = RCS_symbols (rcsfile);
950*86d7f5d3SJohn Marino 	walklist (syms, log_symbol, NULL);
951*86d7f5d3SJohn Marino     }
952*86d7f5d3SJohn Marino 
953*86d7f5d3SJohn Marino     cvs_output ("\nkeyword substitution: ", 0);
954*86d7f5d3SJohn Marino     if (rcsfile->expand == NULL)
955*86d7f5d3SJohn Marino 	cvs_output ("kv", 2);
956*86d7f5d3SJohn Marino     else
957*86d7f5d3SJohn Marino 	cvs_output (rcsfile->expand, 0);
958*86d7f5d3SJohn Marino 
959*86d7f5d3SJohn Marino     cvs_output ("\ntotal revisions: ", 0);
960*86d7f5d3SJohn Marino     sprintf (buf, "%d", walklist (rcsfile->versions, log_count, NULL));
961*86d7f5d3SJohn Marino     cvs_output (buf, 0);
962*86d7f5d3SJohn Marino 
963*86d7f5d3SJohn Marino     if (selrev >= 0)
964*86d7f5d3SJohn Marino     {
965*86d7f5d3SJohn Marino 	cvs_output (";\tselected revisions: ", 0);
966*86d7f5d3SJohn Marino 	sprintf (buf, "%d", selrev);
967*86d7f5d3SJohn Marino 	cvs_output (buf, 0);
968*86d7f5d3SJohn Marino     }
969*86d7f5d3SJohn Marino 
970*86d7f5d3SJohn Marino     cvs_output ("\n", 1);
971*86d7f5d3SJohn Marino 
972*86d7f5d3SJohn Marino     if (!log_data->header || log_data->long_header)
973*86d7f5d3SJohn Marino     {
974*86d7f5d3SJohn Marino 	cvs_output ("description:\n", 0);
975*86d7f5d3SJohn Marino 	if (rcsfile->desc != NULL)
976*86d7f5d3SJohn Marino 	    cvs_output (rcsfile->desc, 0);
977*86d7f5d3SJohn Marino     }
978*86d7f5d3SJohn Marino 
979*86d7f5d3SJohn Marino     if (!log_data->header && ! log_data->long_header && rcsfile->head != NULL)
980*86d7f5d3SJohn Marino     {
981*86d7f5d3SJohn Marino 	p = findnode (rcsfile->versions, rcsfile->head);
982*86d7f5d3SJohn Marino 	if (p == NULL)
983*86d7f5d3SJohn Marino 	    error (1, 0, "can not find head revision in `%s'",
984*86d7f5d3SJohn Marino 		   finfo->fullname);
985*86d7f5d3SJohn Marino 	while (p != NULL)
986*86d7f5d3SJohn Marino 	{
987*86d7f5d3SJohn Marino 	    RCSVers *vers = p->data;
988*86d7f5d3SJohn Marino 
989*86d7f5d3SJohn Marino 	    log_version (log_data, revlist, rcsfile, vers, 1);
990*86d7f5d3SJohn Marino 	    if (vers->next == NULL)
991*86d7f5d3SJohn Marino 		p = NULL;
992*86d7f5d3SJohn Marino 	    else
993*86d7f5d3SJohn Marino 	    {
994*86d7f5d3SJohn Marino 		p = findnode (rcsfile->versions, vers->next);
995*86d7f5d3SJohn Marino 		if (p == NULL)
996*86d7f5d3SJohn Marino 		    error (1, 0, "can not find next revision `%s' in `%s'",
997*86d7f5d3SJohn Marino 			   vers->next, finfo->fullname);
998*86d7f5d3SJohn Marino 	    }
999*86d7f5d3SJohn Marino 	}
1000*86d7f5d3SJohn Marino 
1001*86d7f5d3SJohn Marino 	log_tree (log_data, revlist, rcsfile, rcsfile->head);
1002*86d7f5d3SJohn Marino     }
1003*86d7f5d3SJohn Marino 
1004*86d7f5d3SJohn Marino     cvs_output("\
1005*86d7f5d3SJohn Marino =============================================================================\n",
1006*86d7f5d3SJohn Marino 	       0);
1007*86d7f5d3SJohn Marino 
1008*86d7f5d3SJohn Marino     /* Free up the new revlist and restore the old one.  */
1009*86d7f5d3SJohn Marino     log_free_revlist (revlist);
1010*86d7f5d3SJohn Marino 
1011*86d7f5d3SJohn Marino     /* If singledatelist is not NULL, free up the start dates we added
1012*86d7f5d3SJohn Marino        to it.  */
1013*86d7f5d3SJohn Marino     if (log_data->singledatelist != NULL)
1014*86d7f5d3SJohn Marino     {
1015*86d7f5d3SJohn Marino 	struct datelist *d;
1016*86d7f5d3SJohn Marino 
1017*86d7f5d3SJohn Marino 	for (d = log_data->singledatelist; d != NULL; d = d->next)
1018*86d7f5d3SJohn Marino 	{
1019*86d7f5d3SJohn Marino 	    if (d->start != NULL)
1020*86d7f5d3SJohn Marino 		free (d->start);
1021*86d7f5d3SJohn Marino 	    d->start = NULL;
1022*86d7f5d3SJohn Marino 	}
1023*86d7f5d3SJohn Marino     }
1024*86d7f5d3SJohn Marino 
1025*86d7f5d3SJohn Marino     return 0;
1026*86d7f5d3SJohn Marino }
1027*86d7f5d3SJohn Marino 
1028*86d7f5d3SJohn Marino 
1029*86d7f5d3SJohn Marino 
1030*86d7f5d3SJohn Marino /*
1031*86d7f5d3SJohn Marino  * Fix up a revision list in order to compare it against versions.
1032*86d7f5d3SJohn Marino  * Expand any symbolic revisions.
1033*86d7f5d3SJohn Marino  */
1034*86d7f5d3SJohn Marino static struct revlist *
log_expand_revlist(RCSNode * rcs,char * baserev,struct option_revlist * revlist,int default_branch)1035*86d7f5d3SJohn Marino log_expand_revlist (RCSNode *rcs, char *baserev,
1036*86d7f5d3SJohn Marino                     struct option_revlist *revlist, int default_branch)
1037*86d7f5d3SJohn Marino {
1038*86d7f5d3SJohn Marino     struct option_revlist *r;
1039*86d7f5d3SJohn Marino     struct revlist *ret, **pr;
1040*86d7f5d3SJohn Marino 
1041*86d7f5d3SJohn Marino     ret = NULL;
1042*86d7f5d3SJohn Marino     pr = &ret;
1043*86d7f5d3SJohn Marino     for (r = revlist; r != NULL; r = r->next)
1044*86d7f5d3SJohn Marino     {
1045*86d7f5d3SJohn Marino 	struct revlist *nr;
1046*86d7f5d3SJohn Marino 
1047*86d7f5d3SJohn Marino 	nr = xmalloc (sizeof *nr);
1048*86d7f5d3SJohn Marino 	nr->inclusive = r->inclusive;
1049*86d7f5d3SJohn Marino 
1050*86d7f5d3SJohn Marino 	if (r->first == NULL && r->last == NULL)
1051*86d7f5d3SJohn Marino 	{
1052*86d7f5d3SJohn Marino 	    /* If both first and last are NULL, it means that we want
1053*86d7f5d3SJohn Marino 	       just the head of the default branch, which is RCS_head.  */
1054*86d7f5d3SJohn Marino 	    nr->first = RCS_head (rcs);
1055*86d7f5d3SJohn Marino 	    if (!nr->first)
1056*86d7f5d3SJohn Marino 	    {
1057*86d7f5d3SJohn Marino 		if (!really_quiet)
1058*86d7f5d3SJohn Marino 		    error (0, 0, "No head revision in archive `%s'.",
1059*86d7f5d3SJohn Marino 		           rcs->path);
1060*86d7f5d3SJohn Marino 		nr->last = NULL;
1061*86d7f5d3SJohn Marino 		nr->fields = 0;
1062*86d7f5d3SJohn Marino 	    }
1063*86d7f5d3SJohn Marino 	    else
1064*86d7f5d3SJohn Marino 	    {
1065*86d7f5d3SJohn Marino 		nr->last = xstrdup (nr->first);
1066*86d7f5d3SJohn Marino 		nr->fields = numdots (nr->first) + 1;
1067*86d7f5d3SJohn Marino 	    }
1068*86d7f5d3SJohn Marino 	}
1069*86d7f5d3SJohn Marino 	else if (r->branchhead)
1070*86d7f5d3SJohn Marino 	{
1071*86d7f5d3SJohn Marino 	    char *branch;
1072*86d7f5d3SJohn Marino 
1073*86d7f5d3SJohn Marino 	    /* Print just the head of the branch.  */
1074*86d7f5d3SJohn Marino 	    if (isdigit ((unsigned char) r->first[0]))
1075*86d7f5d3SJohn Marino 		nr->first = RCS_getbranch (rcs, r->first, 1);
1076*86d7f5d3SJohn Marino 	    else
1077*86d7f5d3SJohn Marino 	    {
1078*86d7f5d3SJohn Marino 		branch = RCS_whatbranch (rcs, r->first);
1079*86d7f5d3SJohn Marino 		if (branch == NULL)
1080*86d7f5d3SJohn Marino 		    nr->first = NULL;
1081*86d7f5d3SJohn Marino 		else
1082*86d7f5d3SJohn Marino 		{
1083*86d7f5d3SJohn Marino 		    nr->first = RCS_getbranch (rcs, branch, 1);
1084*86d7f5d3SJohn Marino 		    free (branch);
1085*86d7f5d3SJohn Marino 		}
1086*86d7f5d3SJohn Marino 	    }
1087*86d7f5d3SJohn Marino 	    if (!nr->first)
1088*86d7f5d3SJohn Marino 	    {
1089*86d7f5d3SJohn Marino 		if (!really_quiet)
1090*86d7f5d3SJohn Marino 		    error (0, 0, "warning: no branch `%s' in `%s'",
1091*86d7f5d3SJohn Marino 			   r->first, rcs->print_path);
1092*86d7f5d3SJohn Marino 		nr->last = NULL;
1093*86d7f5d3SJohn Marino 		nr->fields = 0;
1094*86d7f5d3SJohn Marino 	    }
1095*86d7f5d3SJohn Marino 	    else
1096*86d7f5d3SJohn Marino 	    {
1097*86d7f5d3SJohn Marino 		nr->last = xstrdup (nr->first);
1098*86d7f5d3SJohn Marino 		nr->fields = numdots (nr->first) + 1;
1099*86d7f5d3SJohn Marino 	    }
1100*86d7f5d3SJohn Marino 	}
1101*86d7f5d3SJohn Marino 	else
1102*86d7f5d3SJohn Marino 	{
1103*86d7f5d3SJohn Marino 	    if (r->first == NULL || isdigit ((unsigned char) r->first[0]))
1104*86d7f5d3SJohn Marino 		nr->first = xstrdup (r->first);
1105*86d7f5d3SJohn Marino 	    else
1106*86d7f5d3SJohn Marino 	    {
1107*86d7f5d3SJohn Marino 		if (baserev && strcmp (r->first, TAG_BASE) == 0)
1108*86d7f5d3SJohn Marino 		    nr->first = xstrdup (baserev);
1109*86d7f5d3SJohn Marino 		else if (RCS_nodeisbranch (rcs, r->first))
1110*86d7f5d3SJohn Marino 		    nr->first = RCS_whatbranch (rcs, r->first);
1111*86d7f5d3SJohn Marino 		else
1112*86d7f5d3SJohn Marino 		    nr->first = RCS_gettag (rcs, r->first, 1, NULL);
1113*86d7f5d3SJohn Marino 		if (nr->first == NULL && !really_quiet)
1114*86d7f5d3SJohn Marino 		{
1115*86d7f5d3SJohn Marino 		    error (0, 0, "warning: no revision `%s' in `%s'",
1116*86d7f5d3SJohn Marino 			   r->first, rcs->print_path);
1117*86d7f5d3SJohn Marino 		}
1118*86d7f5d3SJohn Marino 	    }
1119*86d7f5d3SJohn Marino 
1120*86d7f5d3SJohn Marino 	    if (r->last == r->first || (r->last != NULL && r->first != NULL &&
1121*86d7f5d3SJohn Marino 					strcmp (r->last, r->first) == 0))
1122*86d7f5d3SJohn Marino 		nr->last = xstrdup (nr->first);
1123*86d7f5d3SJohn Marino 	    else if (r->last == NULL || isdigit ((unsigned char) r->last[0]))
1124*86d7f5d3SJohn Marino 		nr->last = xstrdup (r->last);
1125*86d7f5d3SJohn Marino 	    else
1126*86d7f5d3SJohn Marino 	    {
1127*86d7f5d3SJohn Marino 		if (baserev && strcmp (r->last, TAG_BASE) == 0)
1128*86d7f5d3SJohn Marino 		    nr->last = xstrdup (baserev);
1129*86d7f5d3SJohn Marino 		else if (RCS_nodeisbranch (rcs, r->last))
1130*86d7f5d3SJohn Marino 		    nr->last = RCS_whatbranch (rcs, r->last);
1131*86d7f5d3SJohn Marino 		else
1132*86d7f5d3SJohn Marino 		    nr->last = RCS_gettag (rcs, r->last, 1, NULL);
1133*86d7f5d3SJohn Marino 		if (nr->last == NULL && !really_quiet)
1134*86d7f5d3SJohn Marino 		{
1135*86d7f5d3SJohn Marino 		    error (0, 0, "warning: no revision `%s' in `%s'",
1136*86d7f5d3SJohn Marino 			   r->last, rcs->print_path);
1137*86d7f5d3SJohn Marino 		}
1138*86d7f5d3SJohn Marino 	    }
1139*86d7f5d3SJohn Marino 
1140*86d7f5d3SJohn Marino 	    /* Process the revision numbers the same way that rlog
1141*86d7f5d3SJohn Marino                does.  This code is a bit cryptic for my tastes, but
1142*86d7f5d3SJohn Marino                keeping the same implementation as rlog ensures a
1143*86d7f5d3SJohn Marino                certain degree of compatibility.  */
1144*86d7f5d3SJohn Marino 	    if (r->first == NULL && nr->last != NULL)
1145*86d7f5d3SJohn Marino 	    {
1146*86d7f5d3SJohn Marino 		nr->fields = numdots (nr->last) + 1;
1147*86d7f5d3SJohn Marino 		if (nr->fields < 2)
1148*86d7f5d3SJohn Marino 		    nr->first = xstrdup (".0");
1149*86d7f5d3SJohn Marino 		else
1150*86d7f5d3SJohn Marino 		{
1151*86d7f5d3SJohn Marino 		    char *cp;
1152*86d7f5d3SJohn Marino 
1153*86d7f5d3SJohn Marino 		    nr->first = xstrdup (nr->last);
1154*86d7f5d3SJohn Marino 		    cp = strrchr (nr->first, '.');
1155*86d7f5d3SJohn Marino 		    assert (cp);
1156*86d7f5d3SJohn Marino 		    strcpy (cp + 1, "0");
1157*86d7f5d3SJohn Marino 		}
1158*86d7f5d3SJohn Marino 	    }
1159*86d7f5d3SJohn Marino 	    else if (r->last == NULL && nr->first != NULL)
1160*86d7f5d3SJohn Marino 	    {
1161*86d7f5d3SJohn Marino 		nr->fields = numdots (nr->first) + 1;
1162*86d7f5d3SJohn Marino 		nr->last = xstrdup (nr->first);
1163*86d7f5d3SJohn Marino 		if (nr->fields < 2)
1164*86d7f5d3SJohn Marino 		    nr->last[0] = '\0';
1165*86d7f5d3SJohn Marino 		else
1166*86d7f5d3SJohn Marino 		{
1167*86d7f5d3SJohn Marino 		    char *cp;
1168*86d7f5d3SJohn Marino 
1169*86d7f5d3SJohn Marino 		    cp = strrchr (nr->last, '.');
1170*86d7f5d3SJohn Marino 		    assert (cp);
1171*86d7f5d3SJohn Marino 		    *cp = '\0';
1172*86d7f5d3SJohn Marino 		}
1173*86d7f5d3SJohn Marino 	    }
1174*86d7f5d3SJohn Marino 	    else if (nr->first == NULL || nr->last == NULL)
1175*86d7f5d3SJohn Marino 		nr->fields = 0;
1176*86d7f5d3SJohn Marino 	    else if (strcmp (nr->first, nr->last) == 0)
1177*86d7f5d3SJohn Marino 		nr->fields = numdots (nr->last) + 1;
1178*86d7f5d3SJohn Marino 	    else
1179*86d7f5d3SJohn Marino 	    {
1180*86d7f5d3SJohn Marino 		int ord;
1181*86d7f5d3SJohn Marino 		int dots1 = numdots (nr->first);
1182*86d7f5d3SJohn Marino 		int dots2 = numdots (nr->last);
1183*86d7f5d3SJohn Marino 		if (dots1 > dots2 || (dots1 == dots2 &&
1184*86d7f5d3SJohn Marino 		    version_compare (nr->first, nr->last, dots1 + 1) > 0))
1185*86d7f5d3SJohn Marino 		{
1186*86d7f5d3SJohn Marino 		    char *tmp = nr->first;
1187*86d7f5d3SJohn Marino 		    nr->first = nr->last;
1188*86d7f5d3SJohn Marino 		    nr->last = tmp;
1189*86d7f5d3SJohn Marino 		    nr->fields = dots2 + 1;
1190*86d7f5d3SJohn Marino 		    dots2 = dots1;
1191*86d7f5d3SJohn Marino 		    dots1 = nr->fields - 1;
1192*86d7f5d3SJohn Marino 		}
1193*86d7f5d3SJohn Marino 		else
1194*86d7f5d3SJohn Marino 		    nr->fields = dots1 + 1;
1195*86d7f5d3SJohn Marino 		dots1 += (nr->fields & 1);
1196*86d7f5d3SJohn Marino 		ord = version_compare (nr->first, nr->last, dots1);
1197*86d7f5d3SJohn Marino 		if (ord > 0 || (nr->fields > 2 && ord < 0))
1198*86d7f5d3SJohn Marino 		{
1199*86d7f5d3SJohn Marino 		    error (0, 0,
1200*86d7f5d3SJohn Marino 			   "invalid branch or revision pair %s:%s in `%s'",
1201*86d7f5d3SJohn Marino 			   r->first, r->last, rcs->print_path);
1202*86d7f5d3SJohn Marino 		    free (nr->first);
1203*86d7f5d3SJohn Marino 		    nr->first = NULL;
1204*86d7f5d3SJohn Marino 		    free (nr->last);
1205*86d7f5d3SJohn Marino 		    nr->last = NULL;
1206*86d7f5d3SJohn Marino 		    nr->fields = 0;
1207*86d7f5d3SJohn Marino 		}
1208*86d7f5d3SJohn Marino 		else
1209*86d7f5d3SJohn Marino 		{
1210*86d7f5d3SJohn Marino 		    if (nr->fields <= dots2 && (nr->fields & 1))
1211*86d7f5d3SJohn Marino 		    {
1212*86d7f5d3SJohn Marino 			char *p = Xasprintf ("%s.0", nr->first);
1213*86d7f5d3SJohn Marino 			free (nr->first);
1214*86d7f5d3SJohn Marino 			nr->first = p;
1215*86d7f5d3SJohn Marino 			++nr->fields;
1216*86d7f5d3SJohn Marino 		    }
1217*86d7f5d3SJohn Marino 		    while (nr->fields <= dots2)
1218*86d7f5d3SJohn Marino 		    {
1219*86d7f5d3SJohn Marino 			char *p;
1220*86d7f5d3SJohn Marino 			int i;
1221*86d7f5d3SJohn Marino 
1222*86d7f5d3SJohn Marino 			nr->next = NULL;
1223*86d7f5d3SJohn Marino 			*pr = nr;
1224*86d7f5d3SJohn Marino 			nr = xmalloc (sizeof *nr);
1225*86d7f5d3SJohn Marino 			nr->inclusive = 1;
1226*86d7f5d3SJohn Marino 			nr->first = xstrdup ((*pr)->last);
1227*86d7f5d3SJohn Marino 			nr->last = xstrdup ((*pr)->last);
1228*86d7f5d3SJohn Marino 			nr->fields = (*pr)->fields;
1229*86d7f5d3SJohn Marino 			p = (*pr)->last;
1230*86d7f5d3SJohn Marino 			for (i = 0; i < nr->fields; i++)
1231*86d7f5d3SJohn Marino 			    p = strchr (p, '.') + 1;
1232*86d7f5d3SJohn Marino 			p[-1] = '\0';
1233*86d7f5d3SJohn Marino 			p = strchr (nr->first + (p - (*pr)->last), '.');
1234*86d7f5d3SJohn Marino 			if (p != NULL)
1235*86d7f5d3SJohn Marino 			{
1236*86d7f5d3SJohn Marino 			    *++p = '0';
1237*86d7f5d3SJohn Marino 			    *++p = '\0';
1238*86d7f5d3SJohn Marino 			    nr->fields += 2;
1239*86d7f5d3SJohn Marino 			}
1240*86d7f5d3SJohn Marino 			else
1241*86d7f5d3SJohn Marino 			    ++nr->fields;
1242*86d7f5d3SJohn Marino 			pr = &(*pr)->next;
1243*86d7f5d3SJohn Marino 		    }
1244*86d7f5d3SJohn Marino 		}
1245*86d7f5d3SJohn Marino 	    }
1246*86d7f5d3SJohn Marino 	}
1247*86d7f5d3SJohn Marino 
1248*86d7f5d3SJohn Marino 	nr->next = NULL;
1249*86d7f5d3SJohn Marino 	*pr = nr;
1250*86d7f5d3SJohn Marino 	pr = &nr->next;
1251*86d7f5d3SJohn Marino     }
1252*86d7f5d3SJohn Marino 
1253*86d7f5d3SJohn Marino     /* If the default branch was requested, add a revlist entry for
1254*86d7f5d3SJohn Marino        it.  This is how rlog handles this option.  */
1255*86d7f5d3SJohn Marino     if (default_branch
1256*86d7f5d3SJohn Marino 	&& (rcs->head != NULL || rcs->branch != NULL))
1257*86d7f5d3SJohn Marino     {
1258*86d7f5d3SJohn Marino 	struct revlist *nr;
1259*86d7f5d3SJohn Marino 
1260*86d7f5d3SJohn Marino 	nr = xmalloc (sizeof *nr);
1261*86d7f5d3SJohn Marino 	if (rcs->branch != NULL)
1262*86d7f5d3SJohn Marino 	    nr->first = xstrdup (rcs->branch);
1263*86d7f5d3SJohn Marino 	else
1264*86d7f5d3SJohn Marino 	{
1265*86d7f5d3SJohn Marino 	    char *cp;
1266*86d7f5d3SJohn Marino 
1267*86d7f5d3SJohn Marino 	    nr->first = xstrdup (rcs->head);
1268*86d7f5d3SJohn Marino 	    assert (nr->first);
1269*86d7f5d3SJohn Marino 	    cp = strrchr (nr->first, '.');
1270*86d7f5d3SJohn Marino 	    assert (cp);
1271*86d7f5d3SJohn Marino 	    *cp = '\0';
1272*86d7f5d3SJohn Marino 	}
1273*86d7f5d3SJohn Marino 	nr->last = xstrdup (nr->first);
1274*86d7f5d3SJohn Marino 	nr->fields = numdots (nr->first) + 1;
1275*86d7f5d3SJohn Marino 	nr->inclusive = 1;
1276*86d7f5d3SJohn Marino 
1277*86d7f5d3SJohn Marino 	nr->next = NULL;
1278*86d7f5d3SJohn Marino 	*pr = nr;
1279*86d7f5d3SJohn Marino     }
1280*86d7f5d3SJohn Marino 
1281*86d7f5d3SJohn Marino     return ret;
1282*86d7f5d3SJohn Marino }
1283*86d7f5d3SJohn Marino 
1284*86d7f5d3SJohn Marino 
1285*86d7f5d3SJohn Marino 
1286*86d7f5d3SJohn Marino /*
1287*86d7f5d3SJohn Marino  * Free a revlist created by log_expand_revlist.
1288*86d7f5d3SJohn Marino  */
1289*86d7f5d3SJohn Marino static void
log_free_revlist(struct revlist * revlist)1290*86d7f5d3SJohn Marino log_free_revlist (struct revlist *revlist)
1291*86d7f5d3SJohn Marino {
1292*86d7f5d3SJohn Marino     struct revlist *r;
1293*86d7f5d3SJohn Marino 
1294*86d7f5d3SJohn Marino     r = revlist;
1295*86d7f5d3SJohn Marino     while (r != NULL)
1296*86d7f5d3SJohn Marino     {
1297*86d7f5d3SJohn Marino 	struct revlist *next;
1298*86d7f5d3SJohn Marino 
1299*86d7f5d3SJohn Marino 	if (r->first != NULL)
1300*86d7f5d3SJohn Marino 	    free (r->first);
1301*86d7f5d3SJohn Marino 	if (r->last != NULL)
1302*86d7f5d3SJohn Marino 	    free (r->last);
1303*86d7f5d3SJohn Marino 	next = r->next;
1304*86d7f5d3SJohn Marino 	free (r);
1305*86d7f5d3SJohn Marino 	r = next;
1306*86d7f5d3SJohn Marino     }
1307*86d7f5d3SJohn Marino }
1308*86d7f5d3SJohn Marino 
1309*86d7f5d3SJohn Marino 
1310*86d7f5d3SJohn Marino 
1311*86d7f5d3SJohn Marino /*
1312*86d7f5d3SJohn Marino  * Return nonzero if a revision should be printed, based on the
1313*86d7f5d3SJohn Marino  * options provided.
1314*86d7f5d3SJohn Marino  */
1315*86d7f5d3SJohn Marino static int
log_version_requested(struct log_data * log_data,struct revlist * revlist,RCSNode * rcs,RCSVers * vnode)1316*86d7f5d3SJohn Marino log_version_requested (struct log_data *log_data, struct revlist *revlist,
1317*86d7f5d3SJohn Marino                        RCSNode *rcs, RCSVers *vnode)
1318*86d7f5d3SJohn Marino {
1319*86d7f5d3SJohn Marino     /* Handle the list of states from the -s option.  */
1320*86d7f5d3SJohn Marino     if (log_data->statelist != NULL
1321*86d7f5d3SJohn Marino 	&& findnode (log_data->statelist, vnode->state) == NULL)
1322*86d7f5d3SJohn Marino     {
1323*86d7f5d3SJohn Marino 	return 0;
1324*86d7f5d3SJohn Marino     }
1325*86d7f5d3SJohn Marino 
1326*86d7f5d3SJohn Marino     /* Handle the list of authors from the -w option.  */
1327*86d7f5d3SJohn Marino     if (log_data->authorlist != NULL)
1328*86d7f5d3SJohn Marino     {
1329*86d7f5d3SJohn Marino 	if (vnode->author != NULL
1330*86d7f5d3SJohn Marino 	    && findnode (log_data->authorlist, vnode->author) == NULL)
1331*86d7f5d3SJohn Marino 	{
1332*86d7f5d3SJohn Marino 	    return 0;
1333*86d7f5d3SJohn Marino 	}
1334*86d7f5d3SJohn Marino     }
1335*86d7f5d3SJohn Marino 
1336*86d7f5d3SJohn Marino     /* rlog considers all the -d options together when it decides
1337*86d7f5d3SJohn Marino        whether to print a revision, so we must be compatible.  */
1338*86d7f5d3SJohn Marino     if (log_data->datelist != NULL || log_data->singledatelist != NULL)
1339*86d7f5d3SJohn Marino     {
1340*86d7f5d3SJohn Marino 	struct datelist *d;
1341*86d7f5d3SJohn Marino 
1342*86d7f5d3SJohn Marino 	for (d = log_data->datelist; d != NULL; d = d->next)
1343*86d7f5d3SJohn Marino 	{
1344*86d7f5d3SJohn Marino 	    int cmp;
1345*86d7f5d3SJohn Marino 
1346*86d7f5d3SJohn Marino 	    cmp = RCS_datecmp (vnode->date, d->start);
1347*86d7f5d3SJohn Marino 	    if (cmp > 0 || (cmp == 0 && d->inclusive))
1348*86d7f5d3SJohn Marino 	    {
1349*86d7f5d3SJohn Marino 		cmp = RCS_datecmp (vnode->date, d->end);
1350*86d7f5d3SJohn Marino 		if (cmp < 0 || (cmp == 0 && d->inclusive))
1351*86d7f5d3SJohn Marino 		    break;
1352*86d7f5d3SJohn Marino 	    }
1353*86d7f5d3SJohn Marino 	}
1354*86d7f5d3SJohn Marino 
1355*86d7f5d3SJohn Marino 	if (d == NULL)
1356*86d7f5d3SJohn Marino 	{
1357*86d7f5d3SJohn Marino 	    /* Look through the list of specific dates.  We want to
1358*86d7f5d3SJohn Marino 	       select the revision with the exact date found in the
1359*86d7f5d3SJohn Marino 	       start field.  The commit code ensures that it is
1360*86d7f5d3SJohn Marino 	       impossible to check in multiple revisions of a single
1361*86d7f5d3SJohn Marino 	       file in a single second, so checking the date this way
1362*86d7f5d3SJohn Marino 	       should never select more than one revision.  */
1363*86d7f5d3SJohn Marino 	    for (d = log_data->singledatelist; d != NULL; d = d->next)
1364*86d7f5d3SJohn Marino 	    {
1365*86d7f5d3SJohn Marino 		if (d->start != NULL
1366*86d7f5d3SJohn Marino 		    && RCS_datecmp (vnode->date, d->start) == 0)
1367*86d7f5d3SJohn Marino 		{
1368*86d7f5d3SJohn Marino 		    break;
1369*86d7f5d3SJohn Marino 		}
1370*86d7f5d3SJohn Marino 	    }
1371*86d7f5d3SJohn Marino 
1372*86d7f5d3SJohn Marino 	    if (d == NULL)
1373*86d7f5d3SJohn Marino 		return 0;
1374*86d7f5d3SJohn Marino 	}
1375*86d7f5d3SJohn Marino     }
1376*86d7f5d3SJohn Marino 
1377*86d7f5d3SJohn Marino     /* If the -r or -b options were used, REVLIST will be non NULL,
1378*86d7f5d3SJohn Marino        and we print the union of the specified revisions.  */
1379*86d7f5d3SJohn Marino     if (revlist != NULL)
1380*86d7f5d3SJohn Marino     {
1381*86d7f5d3SJohn Marino 	char *v;
1382*86d7f5d3SJohn Marino 	int vfields;
1383*86d7f5d3SJohn Marino 	struct revlist *r;
1384*86d7f5d3SJohn Marino 
1385*86d7f5d3SJohn Marino 	/* This code is taken from rlog.  */
1386*86d7f5d3SJohn Marino 	v = vnode->version;
1387*86d7f5d3SJohn Marino 	vfields = numdots (v) + 1;
1388*86d7f5d3SJohn Marino 	for (r = revlist; r != NULL; r = r->next)
1389*86d7f5d3SJohn Marino 	{
1390*86d7f5d3SJohn Marino             if (vfields == r->fields + (r->fields & 1) &&
1391*86d7f5d3SJohn Marino                 (r->inclusive ? version_compare (v, r->first, r->fields) >= 0 :
1392*86d7f5d3SJohn Marino                                 version_compare (v, r->first, r->fields) > 0)
1393*86d7f5d3SJohn Marino                 && version_compare (v, r->last, r->fields) <= 0)
1394*86d7f5d3SJohn Marino 	    {
1395*86d7f5d3SJohn Marino 		return 1;
1396*86d7f5d3SJohn Marino 	    }
1397*86d7f5d3SJohn Marino 	}
1398*86d7f5d3SJohn Marino 
1399*86d7f5d3SJohn Marino 	/* If we get here, then the -b and/or the -r option was used,
1400*86d7f5d3SJohn Marino            but did not match this revision, so we reject it.  */
1401*86d7f5d3SJohn Marino 
1402*86d7f5d3SJohn Marino 	return 0;
1403*86d7f5d3SJohn Marino     }
1404*86d7f5d3SJohn Marino 
1405*86d7f5d3SJohn Marino     /* By default, we print all revisions.  */
1406*86d7f5d3SJohn Marino     return 1;
1407*86d7f5d3SJohn Marino }
1408*86d7f5d3SJohn Marino 
1409*86d7f5d3SJohn Marino 
1410*86d7f5d3SJohn Marino 
1411*86d7f5d3SJohn Marino /*
1412*86d7f5d3SJohn Marino  * Output a single symbol.  This is called via walklist.
1413*86d7f5d3SJohn Marino  */
1414*86d7f5d3SJohn Marino /*ARGSUSED*/
1415*86d7f5d3SJohn Marino static int
log_symbol(Node * p,void * closure)1416*86d7f5d3SJohn Marino log_symbol (Node *p, void *closure)
1417*86d7f5d3SJohn Marino {
1418*86d7f5d3SJohn Marino     cvs_output ("\n\t", 2);
1419*86d7f5d3SJohn Marino     cvs_output (p->key, 0);
1420*86d7f5d3SJohn Marino     cvs_output (": ", 2);
1421*86d7f5d3SJohn Marino     cvs_output (p->data, 0);
1422*86d7f5d3SJohn Marino     return 0;
1423*86d7f5d3SJohn Marino }
1424*86d7f5d3SJohn Marino 
1425*86d7f5d3SJohn Marino 
1426*86d7f5d3SJohn Marino 
1427*86d7f5d3SJohn Marino /*
1428*86d7f5d3SJohn Marino  * Count the number of entries on a list.  This is called via walklist.
1429*86d7f5d3SJohn Marino  */
1430*86d7f5d3SJohn Marino /*ARGSUSED*/
1431*86d7f5d3SJohn Marino static int
log_count(Node * p,void * closure)1432*86d7f5d3SJohn Marino log_count (Node *p, void *closure)
1433*86d7f5d3SJohn Marino {
1434*86d7f5d3SJohn Marino     return 1;
1435*86d7f5d3SJohn Marino }
1436*86d7f5d3SJohn Marino 
1437*86d7f5d3SJohn Marino 
1438*86d7f5d3SJohn Marino 
1439*86d7f5d3SJohn Marino /*
1440*86d7f5d3SJohn Marino  * Sort out a single date specification by narrowing down the date
1441*86d7f5d3SJohn Marino  * until we find the specific selected revision.
1442*86d7f5d3SJohn Marino  */
1443*86d7f5d3SJohn Marino static int
log_fix_singledate(Node * p,void * closure)1444*86d7f5d3SJohn Marino log_fix_singledate (Node *p, void *closure)
1445*86d7f5d3SJohn Marino {
1446*86d7f5d3SJohn Marino     struct log_data_and_rcs *data = closure;
1447*86d7f5d3SJohn Marino     Node *pv;
1448*86d7f5d3SJohn Marino     RCSVers *vnode;
1449*86d7f5d3SJohn Marino     struct datelist *holdsingle, *holddate;
1450*86d7f5d3SJohn Marino     int requested;
1451*86d7f5d3SJohn Marino 
1452*86d7f5d3SJohn Marino     pv = findnode (data->rcs->versions, p->key);
1453*86d7f5d3SJohn Marino     if (pv == NULL)
1454*86d7f5d3SJohn Marino 	error (1, 0, "missing version `%s' in RCS file `%s'",
1455*86d7f5d3SJohn Marino 	       p->key, data->rcs->print_path);
1456*86d7f5d3SJohn Marino     vnode = pv->data;
1457*86d7f5d3SJohn Marino 
1458*86d7f5d3SJohn Marino     /* We are only interested if this revision passes any other tests.
1459*86d7f5d3SJohn Marino        Temporarily clear log_data->singledatelist to avoid confusing
1460*86d7f5d3SJohn Marino        log_version_requested.  We also clear log_data->datelist,
1461*86d7f5d3SJohn Marino        because rlog considers all the -d options together.  We don't
1462*86d7f5d3SJohn Marino        want to reject a revision because it does not match a date pair
1463*86d7f5d3SJohn Marino        if we are going to select it on the basis of the singledate.  */
1464*86d7f5d3SJohn Marino     holdsingle = data->log_data->singledatelist;
1465*86d7f5d3SJohn Marino     data->log_data->singledatelist = NULL;
1466*86d7f5d3SJohn Marino     holddate = data->log_data->datelist;
1467*86d7f5d3SJohn Marino     data->log_data->datelist = NULL;
1468*86d7f5d3SJohn Marino     requested = log_version_requested (data->log_data, data->revlist,
1469*86d7f5d3SJohn Marino 				       data->rcs, vnode);
1470*86d7f5d3SJohn Marino     data->log_data->singledatelist = holdsingle;
1471*86d7f5d3SJohn Marino     data->log_data->datelist = holddate;
1472*86d7f5d3SJohn Marino 
1473*86d7f5d3SJohn Marino     if (requested)
1474*86d7f5d3SJohn Marino     {
1475*86d7f5d3SJohn Marino 	struct datelist *d;
1476*86d7f5d3SJohn Marino 
1477*86d7f5d3SJohn Marino 	/* For each single date, if this revision is before the
1478*86d7f5d3SJohn Marino 	   specified date, but is closer than the previously selected
1479*86d7f5d3SJohn Marino 	   revision, select it instead.  */
1480*86d7f5d3SJohn Marino 	for (d = data->log_data->singledatelist; d != NULL; d = d->next)
1481*86d7f5d3SJohn Marino 	{
1482*86d7f5d3SJohn Marino 	    if (RCS_datecmp (vnode->date, d->end) <= 0
1483*86d7f5d3SJohn Marino 		&& (d->start == NULL
1484*86d7f5d3SJohn Marino 		    || RCS_datecmp (vnode->date, d->start) > 0))
1485*86d7f5d3SJohn Marino 	    {
1486*86d7f5d3SJohn Marino 		if (d->start != NULL)
1487*86d7f5d3SJohn Marino 		    free (d->start);
1488*86d7f5d3SJohn Marino 		d->start = xstrdup (vnode->date);
1489*86d7f5d3SJohn Marino 	    }
1490*86d7f5d3SJohn Marino 	}
1491*86d7f5d3SJohn Marino     }
1492*86d7f5d3SJohn Marino 
1493*86d7f5d3SJohn Marino     return 0;
1494*86d7f5d3SJohn Marino }
1495*86d7f5d3SJohn Marino 
1496*86d7f5d3SJohn Marino 
1497*86d7f5d3SJohn Marino 
1498*86d7f5d3SJohn Marino /*
1499*86d7f5d3SJohn Marino  * Count the number of revisions we are going to print.
1500*86d7f5d3SJohn Marino  */
1501*86d7f5d3SJohn Marino static int
log_count_print(Node * p,void * closure)1502*86d7f5d3SJohn Marino log_count_print (Node *p, void *closure)
1503*86d7f5d3SJohn Marino {
1504*86d7f5d3SJohn Marino     struct log_data_and_rcs *data = closure;
1505*86d7f5d3SJohn Marino     Node *pv;
1506*86d7f5d3SJohn Marino 
1507*86d7f5d3SJohn Marino     pv = findnode (data->rcs->versions, p->key);
1508*86d7f5d3SJohn Marino     if (pv == NULL)
1509*86d7f5d3SJohn Marino 	error (1, 0, "missing version `%s' in RCS file `%s'",
1510*86d7f5d3SJohn Marino 	       p->key, data->rcs->print_path);
1511*86d7f5d3SJohn Marino     if (log_version_requested (data->log_data, data->revlist, data->rcs,
1512*86d7f5d3SJohn Marino 			       pv->data))
1513*86d7f5d3SJohn Marino 	return 1;
1514*86d7f5d3SJohn Marino     else
1515*86d7f5d3SJohn Marino 	return 0;
1516*86d7f5d3SJohn Marino }
1517*86d7f5d3SJohn Marino 
1518*86d7f5d3SJohn Marino 
1519*86d7f5d3SJohn Marino 
1520*86d7f5d3SJohn Marino /*
1521*86d7f5d3SJohn Marino  * Print the list of changes, not including the trunk, in reverse
1522*86d7f5d3SJohn Marino  * order for each branch.
1523*86d7f5d3SJohn Marino  */
1524*86d7f5d3SJohn Marino static void
log_tree(struct log_data * log_data,struct revlist * revlist,RCSNode * rcs,const char * ver)1525*86d7f5d3SJohn Marino log_tree (struct log_data *log_data, struct revlist *revlist, RCSNode *rcs,
1526*86d7f5d3SJohn Marino           const char *ver)
1527*86d7f5d3SJohn Marino {
1528*86d7f5d3SJohn Marino     Node *p;
1529*86d7f5d3SJohn Marino     RCSVers *vnode;
1530*86d7f5d3SJohn Marino 
1531*86d7f5d3SJohn Marino     p = findnode (rcs->versions, ver);
1532*86d7f5d3SJohn Marino     if (p == NULL)
1533*86d7f5d3SJohn Marino 	error (1, 0, "missing version `%s' in RCS file `%s'",
1534*86d7f5d3SJohn Marino 	       ver, rcs->print_path);
1535*86d7f5d3SJohn Marino     vnode = p->data;
1536*86d7f5d3SJohn Marino     if (vnode->next != NULL)
1537*86d7f5d3SJohn Marino 	log_tree (log_data, revlist, rcs, vnode->next);
1538*86d7f5d3SJohn Marino     if (vnode->branches != NULL)
1539*86d7f5d3SJohn Marino     {
1540*86d7f5d3SJohn Marino 	Node *head, *branch;
1541*86d7f5d3SJohn Marino 
1542*86d7f5d3SJohn Marino 	/* We need to do the branches in reverse order.  This breaks
1543*86d7f5d3SJohn Marino            the List abstraction, but so does most of the branch
1544*86d7f5d3SJohn Marino            manipulation in rcs.c.  */
1545*86d7f5d3SJohn Marino 	head = vnode->branches->list;
1546*86d7f5d3SJohn Marino 	for (branch = head->prev; branch != head; branch = branch->prev)
1547*86d7f5d3SJohn Marino 	{
1548*86d7f5d3SJohn Marino 	    log_abranch (log_data, revlist, rcs, branch->key);
1549*86d7f5d3SJohn Marino 	    log_tree (log_data, revlist, rcs, branch->key);
1550*86d7f5d3SJohn Marino 	}
1551*86d7f5d3SJohn Marino     }
1552*86d7f5d3SJohn Marino }
1553*86d7f5d3SJohn Marino 
1554*86d7f5d3SJohn Marino 
1555*86d7f5d3SJohn Marino 
1556*86d7f5d3SJohn Marino /*
1557*86d7f5d3SJohn Marino  * Log the changes for a branch, in reverse order.
1558*86d7f5d3SJohn Marino  */
1559*86d7f5d3SJohn Marino static void
log_abranch(struct log_data * log_data,struct revlist * revlist,RCSNode * rcs,const char * ver)1560*86d7f5d3SJohn Marino log_abranch (struct log_data *log_data, struct revlist *revlist, RCSNode *rcs,
1561*86d7f5d3SJohn Marino              const char *ver)
1562*86d7f5d3SJohn Marino {
1563*86d7f5d3SJohn Marino     Node *p;
1564*86d7f5d3SJohn Marino     RCSVers *vnode;
1565*86d7f5d3SJohn Marino 
1566*86d7f5d3SJohn Marino     p = findnode (rcs->versions, ver);
1567*86d7f5d3SJohn Marino     if (p == NULL)
1568*86d7f5d3SJohn Marino 	error (1, 0, "missing version `%s' in RCS file `%s'",
1569*86d7f5d3SJohn Marino 	       ver, rcs->print_path);
1570*86d7f5d3SJohn Marino     vnode = p->data;
1571*86d7f5d3SJohn Marino     if (vnode->next != NULL)
1572*86d7f5d3SJohn Marino 	log_abranch (log_data, revlist, rcs, vnode->next);
1573*86d7f5d3SJohn Marino     log_version (log_data, revlist, rcs, vnode, 0);
1574*86d7f5d3SJohn Marino }
1575*86d7f5d3SJohn Marino 
1576*86d7f5d3SJohn Marino 
1577*86d7f5d3SJohn Marino 
1578*86d7f5d3SJohn Marino /*
1579*86d7f5d3SJohn Marino  * Print the log output for a single version.
1580*86d7f5d3SJohn Marino  */
1581*86d7f5d3SJohn Marino static void
log_version(struct log_data * log_data,struct revlist * revlist,RCSNode * rcs,RCSVers * ver,int trunk)1582*86d7f5d3SJohn Marino log_version (struct log_data *log_data, struct revlist *revlist, RCSNode *rcs,
1583*86d7f5d3SJohn Marino              RCSVers *ver, int trunk)
1584*86d7f5d3SJohn Marino {
1585*86d7f5d3SJohn Marino     Node *p;
1586*86d7f5d3SJohn Marino     int year, mon, mday, hour, min, sec;
1587*86d7f5d3SJohn Marino     char buf[100];
1588*86d7f5d3SJohn Marino     Node *padd, *pdel;
1589*86d7f5d3SJohn Marino 
1590*86d7f5d3SJohn Marino     if (! log_version_requested (log_data, revlist, rcs, ver))
1591*86d7f5d3SJohn Marino 	return;
1592*86d7f5d3SJohn Marino 
1593*86d7f5d3SJohn Marino     cvs_output ("----------------------------\nrevision ", 0);
1594*86d7f5d3SJohn Marino     cvs_output (ver->version, 0);
1595*86d7f5d3SJohn Marino 
1596*86d7f5d3SJohn Marino     p = findnode (RCS_getlocks (rcs), ver->version);
1597*86d7f5d3SJohn Marino     if (p != NULL)
1598*86d7f5d3SJohn Marino     {
1599*86d7f5d3SJohn Marino 	cvs_output ("\tlocked by: ", 0);
1600*86d7f5d3SJohn Marino 	cvs_output (p->data, 0);
1601*86d7f5d3SJohn Marino 	cvs_output (";", 1);
1602*86d7f5d3SJohn Marino     }
1603*86d7f5d3SJohn Marino     cvs_output ("\n", 1);
1604*86d7f5d3SJohn Marino 
1605*86d7f5d3SJohn Marino     cvs_output_tagged ("text", "date: ");
1606*86d7f5d3SJohn Marino     (void)sscanf (ver->date, SDATEFORM, &year, &mon, &mday, &hour, &min,
1607*86d7f5d3SJohn Marino 		  &sec);
1608*86d7f5d3SJohn Marino     if (year < 1900)
1609*86d7f5d3SJohn Marino 	year += 1900;
1610*86d7f5d3SJohn Marino     sprintf (buf, "%04d-%02d-%02d %02d:%02d:%02d +0000", year, mon, mday,
1611*86d7f5d3SJohn Marino 	     hour, min, sec);
1612*86d7f5d3SJohn Marino     cvs_output_tagged ("date", buf);
1613*86d7f5d3SJohn Marino 
1614*86d7f5d3SJohn Marino     cvs_output_tagged ("text", ";  author: ");
1615*86d7f5d3SJohn Marino     cvs_output_tagged ("text", ver->author);
1616*86d7f5d3SJohn Marino 
1617*86d7f5d3SJohn Marino     cvs_output_tagged ("text", ";  state: ");
1618*86d7f5d3SJohn Marino     cvs_output_tagged ("text", ver->state);
1619*86d7f5d3SJohn Marino     cvs_output_tagged ("text", ";");
1620*86d7f5d3SJohn Marino 
1621*86d7f5d3SJohn Marino     if (! trunk)
1622*86d7f5d3SJohn Marino     {
1623*86d7f5d3SJohn Marino 	padd = findnode (ver->other, ";add");
1624*86d7f5d3SJohn Marino 	pdel = findnode (ver->other, ";delete");
1625*86d7f5d3SJohn Marino     }
1626*86d7f5d3SJohn Marino     else if (ver->next == NULL)
1627*86d7f5d3SJohn Marino     {
1628*86d7f5d3SJohn Marino 	padd = NULL;
1629*86d7f5d3SJohn Marino 	pdel = NULL;
1630*86d7f5d3SJohn Marino     }
1631*86d7f5d3SJohn Marino     else
1632*86d7f5d3SJohn Marino     {
1633*86d7f5d3SJohn Marino 	Node *nextp;
1634*86d7f5d3SJohn Marino 	RCSVers *nextver;
1635*86d7f5d3SJohn Marino 
1636*86d7f5d3SJohn Marino 	nextp = findnode (rcs->versions, ver->next);
1637*86d7f5d3SJohn Marino 	if (nextp == NULL)
1638*86d7f5d3SJohn Marino 	    error (1, 0, "missing version `%s' in `%s'", ver->next,
1639*86d7f5d3SJohn Marino 		   rcs->print_path);
1640*86d7f5d3SJohn Marino 	nextver = nextp->data;
1641*86d7f5d3SJohn Marino 	pdel = findnode (nextver->other, ";add");
1642*86d7f5d3SJohn Marino 	padd = findnode (nextver->other, ";delete");
1643*86d7f5d3SJohn Marino     }
1644*86d7f5d3SJohn Marino 
1645*86d7f5d3SJohn Marino     if (padd != NULL)
1646*86d7f5d3SJohn Marino     {
1647*86d7f5d3SJohn Marino 	assert (pdel);
1648*86d7f5d3SJohn Marino 	cvs_output_tagged ("text", "  lines: +");
1649*86d7f5d3SJohn Marino 	cvs_output_tagged ("text", padd->data);
1650*86d7f5d3SJohn Marino 	cvs_output_tagged ("text", " -");
1651*86d7f5d3SJohn Marino 	cvs_output_tagged ("text", pdel->data);
1652*86d7f5d3SJohn Marino         cvs_output_tagged ("text", ";");
1653*86d7f5d3SJohn Marino     }
1654*86d7f5d3SJohn Marino 
1655*86d7f5d3SJohn Marino     p = findnode(ver->other_delta,"commitid");
1656*86d7f5d3SJohn Marino     if(p && p->data)
1657*86d7f5d3SJohn Marino     {
1658*86d7f5d3SJohn Marino         cvs_output_tagged ("text", "  commitid: ");
1659*86d7f5d3SJohn Marino 	cvs_output_tagged ("text", p->data);
1660*86d7f5d3SJohn Marino 	cvs_output_tagged ("text", ";");
1661*86d7f5d3SJohn Marino     }
1662*86d7f5d3SJohn Marino 
1663*86d7f5d3SJohn Marino     cvs_output_tagged ("newline", NULL);
1664*86d7f5d3SJohn Marino 
1665*86d7f5d3SJohn Marino     if (ver->branches != NULL)
1666*86d7f5d3SJohn Marino     {
1667*86d7f5d3SJohn Marino 	cvs_output ("branches:", 0);
1668*86d7f5d3SJohn Marino 	walklist (ver->branches, log_branch, NULL);
1669*86d7f5d3SJohn Marino 	cvs_output ("\n", 1);
1670*86d7f5d3SJohn Marino     }
1671*86d7f5d3SJohn Marino 
1672*86d7f5d3SJohn Marino     p = findnode (ver->other, "log");
1673*86d7f5d3SJohn Marino     /* The p->date == NULL case is the normal one for an empty log
1674*86d7f5d3SJohn Marino        message (rcs-14 in sanity.sh).  I don't think the case where
1675*86d7f5d3SJohn Marino        p->data is "" can happen (getrcskey in rcs.c checks for an
1676*86d7f5d3SJohn Marino        empty string and set the value to NULL in that case).  My guess
1677*86d7f5d3SJohn Marino        would be the p == NULL case would mean an RCS file which was
1678*86d7f5d3SJohn Marino        missing the "log" keyword (which is invalid according to
1679*86d7f5d3SJohn Marino        rcsfile.5).  */
1680*86d7f5d3SJohn Marino     if (p == NULL || p->data == NULL || *(char *)p->data == '\0')
1681*86d7f5d3SJohn Marino 	cvs_output ("*** empty log message ***\n", 0);
1682*86d7f5d3SJohn Marino     else
1683*86d7f5d3SJohn Marino     {
1684*86d7f5d3SJohn Marino 	/* FIXME: Technically, the log message could contain a null
1685*86d7f5d3SJohn Marino            byte.  */
1686*86d7f5d3SJohn Marino 	cvs_output (p->data, 0);
1687*86d7f5d3SJohn Marino 	if (((char *)p->data)[strlen (p->data) - 1] != '\n')
1688*86d7f5d3SJohn Marino 	    cvs_output ("\n", 1);
1689*86d7f5d3SJohn Marino     }
1690*86d7f5d3SJohn Marino }
1691*86d7f5d3SJohn Marino 
1692*86d7f5d3SJohn Marino 
1693*86d7f5d3SJohn Marino 
1694*86d7f5d3SJohn Marino /*
1695*86d7f5d3SJohn Marino  * Output a branch version.  This is called via walklist.
1696*86d7f5d3SJohn Marino  */
1697*86d7f5d3SJohn Marino /*ARGSUSED*/
1698*86d7f5d3SJohn Marino static int
log_branch(Node * p,void * closure)1699*86d7f5d3SJohn Marino log_branch (Node *p, void *closure)
1700*86d7f5d3SJohn Marino {
1701*86d7f5d3SJohn Marino     cvs_output ("  ", 2);
1702*86d7f5d3SJohn Marino     if ((numdots (p->key) & 1) == 0)
1703*86d7f5d3SJohn Marino 	cvs_output (p->key, 0);
1704*86d7f5d3SJohn Marino     else
1705*86d7f5d3SJohn Marino     {
1706*86d7f5d3SJohn Marino 	char *f, *cp;
1707*86d7f5d3SJohn Marino 
1708*86d7f5d3SJohn Marino 	f = xstrdup (p->key);
1709*86d7f5d3SJohn Marino 	cp = strrchr (f, '.');
1710*86d7f5d3SJohn Marino 	*cp = '\0';
1711*86d7f5d3SJohn Marino 	cvs_output (f, 0);
1712*86d7f5d3SJohn Marino 	free (f);
1713*86d7f5d3SJohn Marino     }
1714*86d7f5d3SJohn Marino     cvs_output (";", 1);
1715*86d7f5d3SJohn Marino     return 0;
1716*86d7f5d3SJohn Marino }
1717*86d7f5d3SJohn Marino 
1718*86d7f5d3SJohn Marino 
1719*86d7f5d3SJohn Marino 
1720*86d7f5d3SJohn Marino /*
1721*86d7f5d3SJohn Marino  * Print a warm fuzzy message
1722*86d7f5d3SJohn Marino  */
1723*86d7f5d3SJohn Marino /* ARGSUSED */
1724*86d7f5d3SJohn Marino static Dtype
log_dirproc(void * callerdat,const char * dir,const char * repository,const char * update_dir,List * entries)1725*86d7f5d3SJohn Marino log_dirproc (void *callerdat, const char *dir, const char *repository,
1726*86d7f5d3SJohn Marino              const char *update_dir, List *entries)
1727*86d7f5d3SJohn Marino {
1728*86d7f5d3SJohn Marino     if (!isdir (dir))
1729*86d7f5d3SJohn Marino 	return R_SKIP_ALL;
1730*86d7f5d3SJohn Marino 
1731*86d7f5d3SJohn Marino     if (!quiet)
1732*86d7f5d3SJohn Marino 	error (0, 0, "Logging %s", update_dir);
1733*86d7f5d3SJohn Marino     return R_PROCESS;
1734*86d7f5d3SJohn Marino }
1735*86d7f5d3SJohn Marino 
1736*86d7f5d3SJohn Marino 
1737*86d7f5d3SJohn Marino 
1738*86d7f5d3SJohn Marino /*
1739*86d7f5d3SJohn Marino  * Compare versions.  This is taken from RCS compartial.
1740*86d7f5d3SJohn Marino  */
1741*86d7f5d3SJohn Marino static int
version_compare(const char * v1,const char * v2,int len)1742*86d7f5d3SJohn Marino version_compare (const char *v1, const char *v2, int len)
1743*86d7f5d3SJohn Marino {
1744*86d7f5d3SJohn Marino     while (1)
1745*86d7f5d3SJohn Marino     {
1746*86d7f5d3SJohn Marino 	int d1, d2, r;
1747*86d7f5d3SJohn Marino 
1748*86d7f5d3SJohn Marino 	if (*v1 == '\0')
1749*86d7f5d3SJohn Marino 	    return 1;
1750*86d7f5d3SJohn Marino 	if (*v2 == '\0')
1751*86d7f5d3SJohn Marino 	    return -1;
1752*86d7f5d3SJohn Marino 
1753*86d7f5d3SJohn Marino 	while (*v1 == '0')
1754*86d7f5d3SJohn Marino 	    ++v1;
1755*86d7f5d3SJohn Marino 	for (d1 = 0; isdigit ((unsigned char) v1[d1]); ++d1)
1756*86d7f5d3SJohn Marino 	    ;
1757*86d7f5d3SJohn Marino 
1758*86d7f5d3SJohn Marino 	while (*v2 == '0')
1759*86d7f5d3SJohn Marino 	    ++v2;
1760*86d7f5d3SJohn Marino 	for (d2 = 0; isdigit ((unsigned char) v2[d2]); ++d2)
1761*86d7f5d3SJohn Marino 	    ;
1762*86d7f5d3SJohn Marino 
1763*86d7f5d3SJohn Marino 	if (d1 != d2)
1764*86d7f5d3SJohn Marino 	    return d1 < d2 ? -1 : 1;
1765*86d7f5d3SJohn Marino 
1766*86d7f5d3SJohn Marino 	r = memcmp (v1, v2, d1);
1767*86d7f5d3SJohn Marino 	if (r != 0)
1768*86d7f5d3SJohn Marino 	    return r;
1769*86d7f5d3SJohn Marino 
1770*86d7f5d3SJohn Marino 	--len;
1771*86d7f5d3SJohn Marino 	if (len == 0)
1772*86d7f5d3SJohn Marino 	    return 0;
1773*86d7f5d3SJohn Marino 
1774*86d7f5d3SJohn Marino 	v1 += d1;
1775*86d7f5d3SJohn Marino 	v2 += d1;
1776*86d7f5d3SJohn Marino 
1777*86d7f5d3SJohn Marino 	if (*v1 == '.')
1778*86d7f5d3SJohn Marino 	    ++v1;
1779*86d7f5d3SJohn Marino 	if (*v2 == '.')
1780*86d7f5d3SJohn Marino 	    ++v2;
1781*86d7f5d3SJohn Marino     }
1782*86d7f5d3SJohn Marino }
1783