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