Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / contrib / cvs-1.12 / 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         if (!server_active && strlen (vers->ts_rcs) > 0)
218         {
219             /* Convert from the asctime() format to ISO 8601 */
220             char *buf;
221
222             cvs_output ("\t", 0);
223
224             /* Allow conversion from CVS/Entries asctime() to ISO 8601 */
225             buf = Xasprintf ("%s UTC", vers->ts_rcs);
226             cvs_output_tagged ("date", buf);
227             free (buf);
228         }
229         cvs_output ("\n", 0);
230     }
231
232     if (vers->vn_rcs == NULL)
233         cvs_output ("   Repository revision:\tNo revision control file\n", 0);
234     else
235     {
236         cvs_output ("   Repository revision:\t", 0);
237         cvs_output (vers->vn_rcs, 0);
238         cvs_output ("\t", 0);
239         cvs_output (vers->srcfile->print_path, 0);
240         cvs_output ("\n", 0);
241
242         node = findnode(vers->srcfile->versions,vers->vn_rcs);
243         if (node)
244         {
245             RCSVers *v;
246             v=(RCSVers*)node->data;
247             node = findnode(v->other_delta,"commitid");
248             cvs_output("   Commit Identifier:\t", 0);
249             if(node && node->data)
250                 cvs_output(node->data, 0);
251             else
252                 cvs_output("(none)",0);
253             cvs_output("\n",0);
254         }
255     }
256
257     if (vers->entdata)
258     {
259         Entnode *edata;
260
261         edata = vers->entdata;
262         if (edata->tag)
263         {
264             if (vers->vn_rcs == NULL)
265             {
266                 cvs_output ("   Sticky Tag:\t\t", 0);
267                 cvs_output (edata->tag, 0);
268                 cvs_output (" - MISSING from RCS file!\n", 0);
269             }
270             else
271             {
272                 if (isdigit ((unsigned char) edata->tag[0]))
273                 {
274                     cvs_output ("   Sticky Tag:\t\t", 0);
275                     cvs_output (edata->tag, 0);
276                     cvs_output ("\n", 0);
277                 }
278                 else
279                 {
280                     char *branch = NULL;
281
282                     if (RCS_nodeisbranch (finfo->rcs, edata->tag))
283                         branch = RCS_whatbranch(finfo->rcs, edata->tag);
284
285                     cvs_output ("   Sticky Tag:\t\t", 0);
286                     cvs_output (edata->tag, 0);
287                     cvs_output (" (", 0);
288                     cvs_output (branch ? "branch" : "revision", 0);
289                     cvs_output (": ", 0);
290                     cvs_output (branch ? branch : vers->vn_rcs, 0);
291                     cvs_output (")\n", 0);
292
293                     if (branch)
294                         free (branch);
295                 }
296             }
297         }
298         else if (!really_quiet)
299             cvs_output ("   Sticky Tag:\t\t(none)\n", 0);
300
301         if (edata->date)
302         {
303             cvs_output ("   Sticky Date:\t\t", 0);
304             cvs_output (edata->date, 0);
305             cvs_output ("\n", 0);
306         }
307         else if (!really_quiet)
308             cvs_output ("   Sticky Date:\t\t(none)\n", 0);
309
310         if (edata->options && edata->options[0])
311         {
312             cvs_output ("   Sticky Options:\t", 0);
313             cvs_output (edata->options, 0);
314             cvs_output ("\n", 0);
315         }
316         else if (!really_quiet)
317             cvs_output ("   Sticky Options:\t(none)\n", 0);
318     }
319
320     if (long_format && vers->srcfile)
321     {
322         List *symbols = RCS_symbols(vers->srcfile);
323
324         cvs_output ("\n   Existing Tags:\n", 0);
325         if (symbols)
326         {
327             xrcsnode = finfo->rcs;
328             (void) walklist (symbols, tag_list_proc, NULL);
329         }
330         else
331             cvs_output ("\tNo Tags Exist\n", 0);
332     }
333
334     cvs_output ("\n", 0);
335     freevers_ts (&vers);
336     return (0);
337 }
338
339
340
341 /*
342  * Print a warm fuzzy message
343  */
344 /* ARGSUSED */
345 static Dtype
346 status_dirproc (void *callerdat, const char *dir, const char *repos,
347                 const char *update_dir, List *entries)
348 {
349     if (!quiet)
350         error (0, 0, "Examining %s", update_dir);
351     return (R_PROCESS);
352 }
353
354
355
356 /*
357  * Print out a tag and its type
358  */
359 static int
360 tag_list_proc (Node *p, void *closure)
361 {
362     char *branch = NULL;
363     char *buf;
364
365     if (RCS_nodeisbranch (xrcsnode, p->key))
366         branch = RCS_whatbranch(xrcsnode, p->key) ;
367
368     buf = Xasprintf ("\t%-25s\t(%s: %s)\n", p->key,
369                      branch ? "branch" : "revision",
370                      branch ? branch : (char *)p->data);
371     cvs_output (buf, 0);
372     free (buf);
373
374     if (branch)
375         free (branch);
376
377     return (0);
378 }