xref: /dragonfly/contrib/cvs-1.12/src/status.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
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
cvsstatus(int argc,char ** argv)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
status_fileproc(void * callerdat,struct file_info * finfo)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
status_dirproc(void * callerdat,const char * dir,const char * repos,const char * update_dir,List * entries)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
tag_list_proc(Node * p,void * closure)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 }
379