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