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