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