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