xref: /dragonfly/contrib/cvs-1.12/src/status.c (revision 86d7f5d3)
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  * Status Information
14*86d7f5d3SJohn Marino  */
15*86d7f5d3SJohn Marino 
16*86d7f5d3SJohn Marino #include "cvs.h"
17*86d7f5d3SJohn Marino 
18*86d7f5d3SJohn Marino static Dtype status_dirproc (void *callerdat, const char *dir,
19*86d7f5d3SJohn Marino                              const char *repos, const char *update_dir,
20*86d7f5d3SJohn Marino                              List *entries);
21*86d7f5d3SJohn Marino static int status_fileproc (void *callerdat, struct file_info *finfo);
22*86d7f5d3SJohn Marino static int tag_list_proc (Node * p, void *closure);
23*86d7f5d3SJohn Marino 
24*86d7f5d3SJohn Marino static int local = 0;
25*86d7f5d3SJohn Marino static int long_format = 0;
26*86d7f5d3SJohn Marino static RCSNode *xrcsnode;
27*86d7f5d3SJohn Marino 
28*86d7f5d3SJohn Marino static const char *const status_usage[] =
29*86d7f5d3SJohn Marino {
30*86d7f5d3SJohn Marino     "Usage: %s %s [-vlR] [files...]\n",
31*86d7f5d3SJohn Marino     "\t-v\tVerbose format; includes tag information for the file\n",
32*86d7f5d3SJohn Marino     "\t-l\tProcess this directory only (not recursive).\n",
33*86d7f5d3SJohn Marino     "\t-R\tProcess directories recursively.\n",
34*86d7f5d3SJohn Marino     "(Specify the --help global option for a list of other help options)\n",
35*86d7f5d3SJohn Marino     NULL
36*86d7f5d3SJohn Marino };
37*86d7f5d3SJohn Marino 
38*86d7f5d3SJohn Marino int
cvsstatus(int argc,char ** argv)39*86d7f5d3SJohn Marino cvsstatus (int argc, char **argv)
40*86d7f5d3SJohn Marino {
41*86d7f5d3SJohn Marino     int c;
42*86d7f5d3SJohn Marino     int err = 0;
43*86d7f5d3SJohn Marino 
44*86d7f5d3SJohn Marino     if (argc == -1)
45*86d7f5d3SJohn Marino 	usage (status_usage);
46*86d7f5d3SJohn Marino 
47*86d7f5d3SJohn Marino     optind = 0;
48*86d7f5d3SJohn Marino     while ((c = getopt (argc, argv, "+vlR")) != -1)
49*86d7f5d3SJohn Marino     {
50*86d7f5d3SJohn Marino 	switch (c)
51*86d7f5d3SJohn Marino 	{
52*86d7f5d3SJohn Marino 	    case 'v':
53*86d7f5d3SJohn Marino 		long_format = 1;
54*86d7f5d3SJohn Marino 		break;
55*86d7f5d3SJohn Marino 	    case 'l':
56*86d7f5d3SJohn Marino 		local = 1;
57*86d7f5d3SJohn Marino 		break;
58*86d7f5d3SJohn Marino 	    case 'R':
59*86d7f5d3SJohn Marino 		local = 0;
60*86d7f5d3SJohn Marino 		break;
61*86d7f5d3SJohn Marino 	    case '?':
62*86d7f5d3SJohn Marino 	    default:
63*86d7f5d3SJohn Marino 		usage (status_usage);
64*86d7f5d3SJohn Marino 		break;
65*86d7f5d3SJohn Marino 	}
66*86d7f5d3SJohn Marino     }
67*86d7f5d3SJohn Marino     argc -= optind;
68*86d7f5d3SJohn Marino     argv += optind;
69*86d7f5d3SJohn Marino 
70*86d7f5d3SJohn Marino     wrap_setup ();
71*86d7f5d3SJohn Marino 
72*86d7f5d3SJohn Marino #ifdef CLIENT_SUPPORT
73*86d7f5d3SJohn Marino     if (current_parsed_root->isremote)
74*86d7f5d3SJohn Marino     {
75*86d7f5d3SJohn Marino 	start_server ();
76*86d7f5d3SJohn Marino 
77*86d7f5d3SJohn Marino 	ign_setup ();
78*86d7f5d3SJohn Marino 
79*86d7f5d3SJohn Marino 	if (long_format)
80*86d7f5d3SJohn Marino 	    send_arg("-v");
81*86d7f5d3SJohn Marino 	if (local)
82*86d7f5d3SJohn Marino 	    send_arg("-l");
83*86d7f5d3SJohn Marino 	send_arg ("--");
84*86d7f5d3SJohn Marino 
85*86d7f5d3SJohn Marino 	/* For a while, we tried setting SEND_NO_CONTENTS here so this
86*86d7f5d3SJohn Marino 	   could be a fast operation.  That prevents the
87*86d7f5d3SJohn Marino 	   server from updating our timestamp if the timestamp is
88*86d7f5d3SJohn Marino 	   changed but the file is unmodified.  Worse, it is user-visible
89*86d7f5d3SJohn Marino 	   (shows "locally modified" instead of "up to date" if
90*86d7f5d3SJohn Marino 	   timestamp is changed but file is not).  And there is no good
91*86d7f5d3SJohn Marino 	   workaround (you might not want to run "cvs update"; "cvs -n
92*86d7f5d3SJohn Marino 	   update" doesn't update CVS/Entries; "cvs diff --brief" or
93*86d7f5d3SJohn Marino 	   something perhaps could be made to work but somehow that
94*86d7f5d3SJohn Marino 	   seems nonintuitive to me even if so).  Given that timestamps
95*86d7f5d3SJohn Marino 	   seem to have the potential to get munged for any number of
96*86d7f5d3SJohn Marino 	   reasons, it seems better to not rely too much on them.  */
97*86d7f5d3SJohn Marino 
98*86d7f5d3SJohn Marino 	send_files (argc, argv, local, 0, 0);
99*86d7f5d3SJohn Marino 
100*86d7f5d3SJohn Marino 	send_file_names (argc, argv, SEND_EXPAND_WILD);
101*86d7f5d3SJohn Marino 
102*86d7f5d3SJohn Marino 	send_to_server ("status\012", 0);
103*86d7f5d3SJohn Marino 	err = get_responses_and_close ();
104*86d7f5d3SJohn Marino 
105*86d7f5d3SJohn Marino 	return err;
106*86d7f5d3SJohn Marino     }
107*86d7f5d3SJohn Marino #endif
108*86d7f5d3SJohn Marino 
109*86d7f5d3SJohn Marino     /* start the recursion processor */
110*86d7f5d3SJohn Marino     err = start_recursion (status_fileproc, NULL, status_dirproc,
111*86d7f5d3SJohn Marino 			   NULL, NULL, argc, argv, local, W_LOCAL,
112*86d7f5d3SJohn Marino 			   0, CVS_LOCK_READ, NULL, 1, NULL);
113*86d7f5d3SJohn Marino 
114*86d7f5d3SJohn Marino     return (err);
115*86d7f5d3SJohn Marino }
116*86d7f5d3SJohn Marino 
117*86d7f5d3SJohn Marino /*
118*86d7f5d3SJohn Marino  * display the status of a file
119*86d7f5d3SJohn Marino  */
120*86d7f5d3SJohn Marino /* ARGSUSED */
121*86d7f5d3SJohn Marino static int
status_fileproc(void * callerdat,struct file_info * finfo)122*86d7f5d3SJohn Marino status_fileproc (void *callerdat, struct file_info *finfo)
123*86d7f5d3SJohn Marino {
124*86d7f5d3SJohn Marino     Ctype status;
125*86d7f5d3SJohn Marino     char *sstat;
126*86d7f5d3SJohn Marino     Vers_TS *vers;
127*86d7f5d3SJohn Marino     Node *node;
128*86d7f5d3SJohn Marino 
129*86d7f5d3SJohn Marino     status = Classify_File (finfo, NULL, NULL, NULL, 1, 0, &vers, 0);
130*86d7f5d3SJohn Marino     sstat = "Classify Error";
131*86d7f5d3SJohn Marino     switch (status)
132*86d7f5d3SJohn Marino     {
133*86d7f5d3SJohn Marino 	case T_UNKNOWN:
134*86d7f5d3SJohn Marino 	    sstat = "Unknown";
135*86d7f5d3SJohn Marino 	    break;
136*86d7f5d3SJohn Marino 	case T_CHECKOUT:
137*86d7f5d3SJohn Marino 	    sstat = "Needs Checkout";
138*86d7f5d3SJohn Marino 	    break;
139*86d7f5d3SJohn Marino 	case T_PATCH:
140*86d7f5d3SJohn Marino 	    sstat = "Needs Patch";
141*86d7f5d3SJohn Marino 	    break;
142*86d7f5d3SJohn Marino 	case T_CONFLICT:
143*86d7f5d3SJohn Marino 	    /* FIXME - This message could be clearer.  It comes up
144*86d7f5d3SJohn Marino 	     * when a file exists or has been added in the local sandbox
145*86d7f5d3SJohn Marino 	     * and a file of the same name has been committed indepenently to
146*86d7f5d3SJohn Marino 	     * the repository from a different sandbox, as well as when a
147*86d7f5d3SJohn Marino 	     * timestamp hasn't changed since a merge resulted in conflicts.
148*86d7f5d3SJohn Marino 	     * It also comes up whether an update has been attempted or not, so
149*86d7f5d3SJohn Marino 	     * technically, I think the double-add case is not actually a
150*86d7f5d3SJohn Marino 	     * conflict yet.
151*86d7f5d3SJohn Marino 	     */
152*86d7f5d3SJohn Marino 	    sstat = "Unresolved Conflict";
153*86d7f5d3SJohn Marino 	    break;
154*86d7f5d3SJohn Marino 	case T_ADDED:
155*86d7f5d3SJohn Marino 	    sstat = "Locally Added";
156*86d7f5d3SJohn Marino 	    break;
157*86d7f5d3SJohn Marino 	case T_REMOVED:
158*86d7f5d3SJohn Marino 	    sstat = "Locally Removed";
159*86d7f5d3SJohn Marino 	    break;
160*86d7f5d3SJohn Marino 	case T_MODIFIED:
161*86d7f5d3SJohn Marino 	    if (file_has_markers (finfo))
162*86d7f5d3SJohn Marino 		sstat = "File had conflicts on merge";
163*86d7f5d3SJohn Marino 	    else
164*86d7f5d3SJohn Marino 		/* Note that we do not re Register() the file when we spot
165*86d7f5d3SJohn Marino 		 * a resolved conflict like update_fileproc() does on the
166*86d7f5d3SJohn Marino 		 * premise that status should not alter the sandbox.
167*86d7f5d3SJohn Marino 		 */
168*86d7f5d3SJohn Marino 		sstat = "Locally Modified";
169*86d7f5d3SJohn Marino 	    break;
170*86d7f5d3SJohn Marino 	case T_REMOVE_ENTRY:
171*86d7f5d3SJohn Marino 	    sstat = "Entry Invalid";
172*86d7f5d3SJohn Marino 	    break;
173*86d7f5d3SJohn Marino 	case T_UPTODATE:
174*86d7f5d3SJohn Marino 	    sstat = "Up-to-date";
175*86d7f5d3SJohn Marino 	    break;
176*86d7f5d3SJohn Marino 	case T_NEEDS_MERGE:
177*86d7f5d3SJohn Marino 	    sstat = "Needs Merge";
178*86d7f5d3SJohn Marino 	    break;
179*86d7f5d3SJohn Marino 	case T_TITLE:
180*86d7f5d3SJohn Marino 	    /* I don't think this case can occur here.  Just print
181*86d7f5d3SJohn Marino 	       "Classify Error".  */
182*86d7f5d3SJohn Marino 	    break;
183*86d7f5d3SJohn Marino     }
184*86d7f5d3SJohn Marino 
185*86d7f5d3SJohn Marino     cvs_output ("\
186*86d7f5d3SJohn Marino ===================================================================\n", 0);
187*86d7f5d3SJohn Marino     if (vers->ts_user == NULL)
188*86d7f5d3SJohn Marino     {
189*86d7f5d3SJohn Marino 	cvs_output ("File: no file ", 0);
190*86d7f5d3SJohn Marino 	cvs_output (finfo->file, 0);
191*86d7f5d3SJohn Marino 	cvs_output ("\t\tStatus: ", 0);
192*86d7f5d3SJohn Marino 	cvs_output (sstat, 0);
193*86d7f5d3SJohn Marino 	cvs_output ("\n\n", 0);
194*86d7f5d3SJohn Marino     }
195*86d7f5d3SJohn Marino     else
196*86d7f5d3SJohn Marino     {
197*86d7f5d3SJohn Marino 	char *buf;
198*86d7f5d3SJohn Marino 	buf = Xasprintf ("File: %-17s\tStatus: %s\n\n", finfo->file, sstat);
199*86d7f5d3SJohn Marino 	cvs_output (buf, 0);
200*86d7f5d3SJohn Marino 	free (buf);
201*86d7f5d3SJohn Marino     }
202*86d7f5d3SJohn Marino 
203*86d7f5d3SJohn Marino     if (vers->vn_user == NULL)
204*86d7f5d3SJohn Marino     {
205*86d7f5d3SJohn Marino 	cvs_output ("   Working revision:\tNo entry for ", 0);
206*86d7f5d3SJohn Marino 	cvs_output (finfo->file, 0);
207*86d7f5d3SJohn Marino 	cvs_output ("\n", 0);
208*86d7f5d3SJohn Marino     }
209*86d7f5d3SJohn Marino     else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
210*86d7f5d3SJohn Marino 	cvs_output ("   Working revision:\tNew file!\n", 0);
211*86d7f5d3SJohn Marino     else
212*86d7f5d3SJohn Marino     {
213*86d7f5d3SJohn Marino 	cvs_output ("   Working revision:\t", 0);
214*86d7f5d3SJohn Marino 	cvs_output (vers->vn_user, 0);
215*86d7f5d3SJohn Marino 
216*86d7f5d3SJohn Marino 	/* Only add the UTC timezone if there is a time to use. */
217*86d7f5d3SJohn Marino 	if (!server_active && strlen (vers->ts_rcs) > 0)
218*86d7f5d3SJohn Marino 	{
219*86d7f5d3SJohn Marino 	    /* Convert from the asctime() format to ISO 8601 */
220*86d7f5d3SJohn Marino 	    char *buf;
221*86d7f5d3SJohn Marino 
222*86d7f5d3SJohn Marino 	    cvs_output ("\t", 0);
223*86d7f5d3SJohn Marino 
224*86d7f5d3SJohn Marino 	    /* Allow conversion from CVS/Entries asctime() to ISO 8601 */
225*86d7f5d3SJohn Marino 	    buf = Xasprintf ("%s UTC", vers->ts_rcs);
226*86d7f5d3SJohn Marino 	    cvs_output_tagged ("date", buf);
227*86d7f5d3SJohn Marino 	    free (buf);
228*86d7f5d3SJohn Marino 	}
229*86d7f5d3SJohn Marino 	cvs_output ("\n", 0);
230*86d7f5d3SJohn Marino     }
231*86d7f5d3SJohn Marino 
232*86d7f5d3SJohn Marino     if (vers->vn_rcs == NULL)
233*86d7f5d3SJohn Marino 	cvs_output ("   Repository revision:\tNo revision control file\n", 0);
234*86d7f5d3SJohn Marino     else
235*86d7f5d3SJohn Marino     {
236*86d7f5d3SJohn Marino 	cvs_output ("   Repository revision:\t", 0);
237*86d7f5d3SJohn Marino 	cvs_output (vers->vn_rcs, 0);
238*86d7f5d3SJohn Marino 	cvs_output ("\t", 0);
239*86d7f5d3SJohn Marino 	cvs_output (vers->srcfile->print_path, 0);
240*86d7f5d3SJohn Marino 	cvs_output ("\n", 0);
241*86d7f5d3SJohn Marino 
242*86d7f5d3SJohn Marino 	node = findnode(vers->srcfile->versions,vers->vn_rcs);
243*86d7f5d3SJohn Marino 	if (node)
244*86d7f5d3SJohn Marino 	{
245*86d7f5d3SJohn Marino 	    RCSVers *v;
246*86d7f5d3SJohn Marino 	    v=(RCSVers*)node->data;
247*86d7f5d3SJohn Marino 	    node = findnode(v->other_delta,"commitid");
248*86d7f5d3SJohn Marino 	    cvs_output("   Commit Identifier:\t", 0);
249*86d7f5d3SJohn Marino 	    if(node && node->data)
250*86d7f5d3SJohn Marino 	        cvs_output(node->data, 0);
251*86d7f5d3SJohn Marino 	    else
252*86d7f5d3SJohn Marino 	        cvs_output("(none)",0);
253*86d7f5d3SJohn Marino 	    cvs_output("\n",0);
254*86d7f5d3SJohn Marino 	}
255*86d7f5d3SJohn Marino     }
256*86d7f5d3SJohn Marino 
257*86d7f5d3SJohn Marino     if (vers->entdata)
258*86d7f5d3SJohn Marino     {
259*86d7f5d3SJohn Marino 	Entnode *edata;
260*86d7f5d3SJohn Marino 
261*86d7f5d3SJohn Marino 	edata = vers->entdata;
262*86d7f5d3SJohn Marino 	if (edata->tag)
263*86d7f5d3SJohn Marino 	{
264*86d7f5d3SJohn Marino 	    if (vers->vn_rcs == NULL)
265*86d7f5d3SJohn Marino 	    {
266*86d7f5d3SJohn Marino 		cvs_output ("   Sticky Tag:\t\t", 0);
267*86d7f5d3SJohn Marino 		cvs_output (edata->tag, 0);
268*86d7f5d3SJohn Marino 		cvs_output (" - MISSING from RCS file!\n", 0);
269*86d7f5d3SJohn Marino 	    }
270*86d7f5d3SJohn Marino 	    else
271*86d7f5d3SJohn Marino 	    {
272*86d7f5d3SJohn Marino 		if (isdigit ((unsigned char) edata->tag[0]))
273*86d7f5d3SJohn Marino 		{
274*86d7f5d3SJohn Marino 		    cvs_output ("   Sticky Tag:\t\t", 0);
275*86d7f5d3SJohn Marino 		    cvs_output (edata->tag, 0);
276*86d7f5d3SJohn Marino 		    cvs_output ("\n", 0);
277*86d7f5d3SJohn Marino 		}
278*86d7f5d3SJohn Marino 		else
279*86d7f5d3SJohn Marino 		{
280*86d7f5d3SJohn Marino 		    char *branch = NULL;
281*86d7f5d3SJohn Marino 
282*86d7f5d3SJohn Marino 		    if (RCS_nodeisbranch (finfo->rcs, edata->tag))
283*86d7f5d3SJohn Marino 			branch = RCS_whatbranch(finfo->rcs, edata->tag);
284*86d7f5d3SJohn Marino 
285*86d7f5d3SJohn Marino 		    cvs_output ("   Sticky Tag:\t\t", 0);
286*86d7f5d3SJohn Marino 		    cvs_output (edata->tag, 0);
287*86d7f5d3SJohn Marino 		    cvs_output (" (", 0);
288*86d7f5d3SJohn Marino 		    cvs_output (branch ? "branch" : "revision", 0);
289*86d7f5d3SJohn Marino 		    cvs_output (": ", 0);
290*86d7f5d3SJohn Marino 		    cvs_output (branch ? branch : vers->vn_rcs, 0);
291*86d7f5d3SJohn Marino 		    cvs_output (")\n", 0);
292*86d7f5d3SJohn Marino 
293*86d7f5d3SJohn Marino 		    if (branch)
294*86d7f5d3SJohn Marino 			free (branch);
295*86d7f5d3SJohn Marino 		}
296*86d7f5d3SJohn Marino 	    }
297*86d7f5d3SJohn Marino 	}
298*86d7f5d3SJohn Marino 	else if (!really_quiet)
299*86d7f5d3SJohn Marino 	    cvs_output ("   Sticky Tag:\t\t(none)\n", 0);
300*86d7f5d3SJohn Marino 
301*86d7f5d3SJohn Marino 	if (edata->date)
302*86d7f5d3SJohn Marino 	{
303*86d7f5d3SJohn Marino 	    cvs_output ("   Sticky Date:\t\t", 0);
304*86d7f5d3SJohn Marino 	    cvs_output (edata->date, 0);
305*86d7f5d3SJohn Marino 	    cvs_output ("\n", 0);
306*86d7f5d3SJohn Marino 	}
307*86d7f5d3SJohn Marino 	else if (!really_quiet)
308*86d7f5d3SJohn Marino 	    cvs_output ("   Sticky Date:\t\t(none)\n", 0);
309*86d7f5d3SJohn Marino 
310*86d7f5d3SJohn Marino 	if (edata->options && edata->options[0])
311*86d7f5d3SJohn Marino 	{
312*86d7f5d3SJohn Marino 	    cvs_output ("   Sticky Options:\t", 0);
313*86d7f5d3SJohn Marino 	    cvs_output (edata->options, 0);
314*86d7f5d3SJohn Marino 	    cvs_output ("\n", 0);
315*86d7f5d3SJohn Marino 	}
316*86d7f5d3SJohn Marino 	else if (!really_quiet)
317*86d7f5d3SJohn Marino 	    cvs_output ("   Sticky Options:\t(none)\n", 0);
318*86d7f5d3SJohn Marino     }
319*86d7f5d3SJohn Marino 
320*86d7f5d3SJohn Marino     if (long_format && vers->srcfile)
321*86d7f5d3SJohn Marino     {
322*86d7f5d3SJohn Marino 	List *symbols = RCS_symbols(vers->srcfile);
323*86d7f5d3SJohn Marino 
324*86d7f5d3SJohn Marino 	cvs_output ("\n   Existing Tags:\n", 0);
325*86d7f5d3SJohn Marino 	if (symbols)
326*86d7f5d3SJohn Marino 	{
327*86d7f5d3SJohn Marino 	    xrcsnode = finfo->rcs;
328*86d7f5d3SJohn Marino 	    (void) walklist (symbols, tag_list_proc, NULL);
329*86d7f5d3SJohn Marino 	}
330*86d7f5d3SJohn Marino 	else
331*86d7f5d3SJohn Marino 	    cvs_output ("\tNo Tags Exist\n", 0);
332*86d7f5d3SJohn Marino     }
333*86d7f5d3SJohn Marino 
334*86d7f5d3SJohn Marino     cvs_output ("\n", 0);
335*86d7f5d3SJohn Marino     freevers_ts (&vers);
336*86d7f5d3SJohn Marino     return (0);
337*86d7f5d3SJohn Marino }
338*86d7f5d3SJohn Marino 
339*86d7f5d3SJohn Marino 
340*86d7f5d3SJohn Marino 
341*86d7f5d3SJohn Marino /*
342*86d7f5d3SJohn Marino  * Print a warm fuzzy message
343*86d7f5d3SJohn Marino  */
344*86d7f5d3SJohn Marino /* ARGSUSED */
345*86d7f5d3SJohn Marino static Dtype
status_dirproc(void * callerdat,const char * dir,const char * repos,const char * update_dir,List * entries)346*86d7f5d3SJohn Marino status_dirproc (void *callerdat, const char *dir, const char *repos,
347*86d7f5d3SJohn Marino                 const char *update_dir, List *entries)
348*86d7f5d3SJohn Marino {
349*86d7f5d3SJohn Marino     if (!quiet)
350*86d7f5d3SJohn Marino 	error (0, 0, "Examining %s", update_dir);
351*86d7f5d3SJohn Marino     return (R_PROCESS);
352*86d7f5d3SJohn Marino }
353*86d7f5d3SJohn Marino 
354*86d7f5d3SJohn Marino 
355*86d7f5d3SJohn Marino 
356*86d7f5d3SJohn Marino /*
357*86d7f5d3SJohn Marino  * Print out a tag and its type
358*86d7f5d3SJohn Marino  */
359*86d7f5d3SJohn Marino static int
tag_list_proc(Node * p,void * closure)360*86d7f5d3SJohn Marino tag_list_proc (Node *p, void *closure)
361*86d7f5d3SJohn Marino {
362*86d7f5d3SJohn Marino     char *branch = NULL;
363*86d7f5d3SJohn Marino     char *buf;
364*86d7f5d3SJohn Marino 
365*86d7f5d3SJohn Marino     if (RCS_nodeisbranch (xrcsnode, p->key))
366*86d7f5d3SJohn Marino 	branch = RCS_whatbranch(xrcsnode, p->key) ;
367*86d7f5d3SJohn Marino 
368*86d7f5d3SJohn Marino     buf = Xasprintf ("\t%-25s\t(%s: %s)\n", p->key,
369*86d7f5d3SJohn Marino 		     branch ? "branch" : "revision",
370*86d7f5d3SJohn Marino 		     branch ? branch : (char *)p->data);
371*86d7f5d3SJohn Marino     cvs_output (buf, 0);
372*86d7f5d3SJohn Marino     free (buf);
373*86d7f5d3SJohn Marino 
374*86d7f5d3SJohn Marino     if (branch)
375*86d7f5d3SJohn Marino 	free (branch);
376*86d7f5d3SJohn Marino 
377*86d7f5d3SJohn Marino     return (0);
378*86d7f5d3SJohn Marino }
379