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