Sync with FreeBSD. This adds read-only support for zip and ISO9660.
[dragonfly.git] / contrib / cvs-1.12.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 needs to be clearer.  It comes up now
144              * only 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.  It also comes up
147              * whether an update has been attempted or not, so technically, I
148              * think it is not actually a conflict yet.
149              */
150             sstat = "Unresolved Conflict";
151             break;
152         case T_ADDED:
153             sstat = "Locally Added";
154             break;
155         case T_REMOVED:
156             sstat = "Locally Removed";
157             break;
158         case T_MODIFIED:
159             if ( vers->ts_conflict
160                  && ( file_has_conflict ( finfo, vers->ts_conflict )
161                        || 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 #ifdef SERVER_SUPPORT
212     else if (server_active)
213     {
214         cvs_output ("   Working revision:\t", 0);
215         cvs_output (vers->vn_user, 0);
216         cvs_output ("\n", 0);
217     }
218 #endif
219     else
220     {
221         cvs_output ("   Working revision:\t", 0);
222         cvs_output (vers->vn_user, 0);
223
224         /* Only add the UTC timezone if there is a time to use. */
225         if (strlen (vers->ts_rcs) > 0)
226         {
227             /* Convert from the asctime() format to ISO 8601 */
228             char *buf;
229
230             cvs_output ("\t", 0);
231
232             /* Allow conversion from CVS/Entries asctime() to ISO 8601 */
233             buf = Xasprintf ("%s UTC", vers->ts_rcs);
234             cvs_output_tagged ("date", buf);
235             free (buf);
236         }
237         cvs_output ("\n", 0);
238     }
239
240     if (vers->vn_rcs == NULL)
241         cvs_output ("   Repository revision:\tNo revision control file\n", 0);
242     else
243     {
244         cvs_output ("   Repository revision:\t", 0);
245         cvs_output (vers->vn_rcs, 0);
246         cvs_output ("\t", 0);
247         cvs_output (vers->srcfile->print_path, 0);
248         cvs_output ("\n", 0);
249
250         node = findnode(vers->srcfile->versions,vers->vn_rcs);
251         if (node)
252         {
253             RCSVers *v;
254             v=(RCSVers*)node->data;
255             node = findnode(v->other_delta,"commitid");
256             cvs_output("   Commit Identifier:\t", 0);
257             if(node && node->data)
258                 cvs_output(node->data, 0);
259             else
260                 cvs_output("(none)",0);
261             cvs_output("\n",0);
262         }
263     }
264
265     if (vers->entdata)
266     {
267         Entnode *edata;
268
269         edata = vers->entdata;
270         if (edata->tag)
271         {
272             if (vers->vn_rcs == NULL)
273             {
274                 cvs_output ("   Sticky Tag:\t\t", 0);
275                 cvs_output (edata->tag, 0);
276                 cvs_output (" - MISSING from RCS file!\n", 0);
277             }
278             else
279             {
280                 if (isdigit ((unsigned char) edata->tag[0]))
281                 {
282                     cvs_output ("   Sticky Tag:\t\t", 0);
283                     cvs_output (edata->tag, 0);
284                     cvs_output ("\n", 0);
285                 }
286                 else
287                 {
288                     char *branch = NULL;
289
290                     if (RCS_nodeisbranch (finfo->rcs, edata->tag))
291                         branch = RCS_whatbranch(finfo->rcs, edata->tag);
292
293                     cvs_output ("   Sticky Tag:\t\t", 0);
294                     cvs_output (edata->tag, 0);
295                     cvs_output (" (", 0);
296                     cvs_output (branch ? "branch" : "revision", 0);
297                     cvs_output (": ", 0);
298                     cvs_output (branch ? branch : vers->vn_rcs, 0);
299                     cvs_output (")\n", 0);
300
301                     if (branch)
302                         free (branch);
303                 }
304             }
305         }
306         else if (!really_quiet)
307             cvs_output ("   Sticky Tag:\t\t(none)\n", 0);
308
309         if (edata->date)
310         {
311             cvs_output ("   Sticky Date:\t\t", 0);
312             cvs_output (edata->date, 0);
313             cvs_output ("\n", 0);
314         }
315         else if (!really_quiet)
316             cvs_output ("   Sticky Date:\t\t(none)\n", 0);
317
318         if (edata->options && edata->options[0])
319         {
320             cvs_output ("   Sticky Options:\t", 0);
321             cvs_output (edata->options, 0);
322             cvs_output ("\n", 0);
323         }
324         else if (!really_quiet)
325             cvs_output ("   Sticky Options:\t(none)\n", 0);
326     }
327
328     if (long_format && vers->srcfile)
329     {
330         List *symbols = RCS_symbols(vers->srcfile);
331
332         cvs_output ("\n   Existing Tags:\n", 0);
333         if (symbols)
334         {
335             xrcsnode = finfo->rcs;
336             (void) walklist (symbols, tag_list_proc, NULL);
337         }
338         else
339             cvs_output ("\tNo Tags Exist\n", 0);
340     }
341
342     cvs_output ("\n", 0);
343     freevers_ts (&vers);
344     return (0);
345 }
346
347
348
349 /*
350  * Print a warm fuzzy message
351  */
352 /* ARGSUSED */
353 static Dtype
354 status_dirproc (void *callerdat, const char *dir, const char *repos,
355                 const char *update_dir, List *entries)
356 {
357     if (!quiet)
358         error (0, 0, "Examining %s", update_dir);
359     return (R_PROCESS);
360 }
361
362
363
364 /*
365  * Print out a tag and its type
366  */
367 static int
368 tag_list_proc (Node *p, void *closure)
369 {
370     char *branch = NULL;
371     char *buf;
372
373     if (RCS_nodeisbranch (xrcsnode, p->key))
374         branch = RCS_whatbranch(xrcsnode, p->key) ;
375
376     buf = Xasprintf ("\t%-25s\t(%s: %s)\n", p->key,
377                      branch ? "branch" : "revision",
378                      branch ? branch : (char *)p->data);
379     cvs_output (buf, 0);
380     free (buf);
381
382     if (branch)
383         free (branch);
384
385     return (0);
386 }