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 PROTO ((void *callerdat, char *dir,
14 char *repos, char *update_dir,
15 List *entries));
16 static int status_fileproc PROTO ((void *callerdat, struct file_info *finfo));
17 static int tag_list_proc PROTO((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
cvsstatus(argc,argv)34 cvsstatus (argc, argv)
35 int argc;
36 char **argv;
37 {
38 int c;
39 int err = 0;
40
41 if (argc == -1)
42 usage (status_usage);
43
44 optind = 0;
45 while ((c = getopt (argc, argv, "+vlR")) != -1)
46 {
47 switch (c)
48 {
49 case 'v':
50 long_format = 1;
51 break;
52 case 'l':
53 local = 1;
54 break;
55 case 'R':
56 local = 0;
57 break;
58 case '?':
59 default:
60 usage (status_usage);
61 break;
62 }
63 }
64 argc -= optind;
65 argv += optind;
66
67 wrap_setup ();
68
69 #ifdef CLIENT_SUPPORT
70 if (current_parsed_root->isremote)
71 {
72 start_server ();
73
74 ign_setup ();
75
76 if (long_format)
77 send_arg("-v");
78 if (local)
79 send_arg("-l");
80
81 /* For a while, we tried setting SEND_NO_CONTENTS here so this
82 could be a fast operation. That prevents the
83 server from updating our timestamp if the timestamp is
84 changed but the file is unmodified. Worse, it is user-visible
85 (shows "locally modified" instead of "up to date" if
86 timestamp is changed but file is not). And there is no good
87 workaround (you might not want to run "cvs update"; "cvs -n
88 update" doesn't update CVS/Entries; "cvs diff --brief" or
89 something perhaps could be made to work but somehow that
90 seems nonintuitive to me even if so). Given that timestamps
91 seem to have the potential to get munged for any number of
92 reasons, it seems better to not rely too much on them. */
93
94 send_files (argc, argv, local, 0, 0);
95
96 send_file_names (argc, argv, SEND_EXPAND_WILD);
97
98 send_to_server ("status\012", 0);
99 err = get_responses_and_close ();
100
101 return err;
102 }
103 #endif
104
105 /* start the recursion processor */
106 err = start_recursion (status_fileproc, (FILESDONEPROC) NULL,
107 status_dirproc, (DIRLEAVEPROC) NULL, NULL,
108 argc, argv, local,
109 W_LOCAL, 0, 1, (char *) NULL, 1);
110
111 return (err);
112 }
113
114 /*
115 * display the status of a file
116 */
117 /* ARGSUSED */
118 static int
status_fileproc(callerdat,finfo)119 status_fileproc (callerdat, finfo)
120 void *callerdat;
121 struct file_info *finfo;
122 {
123 Ctype status;
124 char *sstat;
125 Vers_TS *vers;
126 Node *node;
127
128 status = Classify_File (finfo, (char *) NULL, (char *) NULL, (char *) NULL,
129 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 /* I _think_ that "unresolved" is correct; that if it has
144 been resolved then the status will change. But I'm not
145 sure about that. */
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 sstat = "File had conflicts on merge";
157 else
158 sstat = "Locally Modified";
159 break;
160 case T_REMOVE_ENTRY:
161 sstat = "Entry Invalid";
162 break;
163 case T_UPTODATE:
164 sstat = "Up-to-date";
165 break;
166 case T_NEEDS_MERGE:
167 sstat = "Needs Merge";
168 break;
169 case T_TITLE:
170 /* I don't think this case can occur here. Just print
171 "Classify Error". */
172 break;
173 }
174
175 cvs_output ("\
176 ===================================================================\n", 0);
177 if (vers->ts_user == NULL)
178 {
179 cvs_output ("File: no file ", 0);
180 cvs_output (finfo->file, 0);
181 cvs_output ("\t\tStatus: ", 0);
182 cvs_output (sstat, 0);
183 cvs_output ("\n\n", 0);
184 }
185 else
186 {
187 char *buf;
188 buf = xmalloc (strlen (finfo->file) + strlen (sstat) + 80);
189 sprintf (buf, "File: %-17s\tStatus: %s\n\n", finfo->file, sstat);
190 cvs_output (buf, 0);
191 free (buf);
192 }
193
194 if (vers->vn_user == NULL)
195 {
196 cvs_output (" Working revision:\tNo entry for ", 0);
197 cvs_output (finfo->file, 0);
198 cvs_output ("\n", 0);
199 }
200 else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
201 cvs_output (" Working revision:\tNew file!\n", 0);
202 #ifdef SERVER_SUPPORT
203 else if (server_active)
204 {
205 cvs_output (" Working revision:\t", 0);
206 cvs_output (vers->vn_user, 0);
207 cvs_output ("\n", 0);
208 }
209 #endif
210 else
211 {
212 cvs_output (" Working revision:\t", 0);
213 cvs_output (vers->vn_user, 0);
214 cvs_output ("\t", 0);
215 cvs_output (vers->ts_rcs, 0);
216 cvs_output ("\n", 0);
217 }
218
219 if (vers->vn_rcs == NULL)
220 cvs_output (" Repository revision:\tNo revision control file\n", 0);
221 else
222 {
223 cvs_output (" Repository revision:\t", 0);
224 cvs_output (vers->vn_rcs, 0);
225 cvs_output ("\t", 0);
226 cvs_output (vers->srcfile->path, 0);
227 cvs_output ("\n", 0);
228
229 node = findnode(vers->srcfile->versions,vers->vn_rcs);
230 if (node)
231 {
232 RCSVers *v;
233 v=(RCSVers*)node->data;
234 node = findnode(v->other_delta,"commitid");
235 cvs_output(" Commit Identifier:\t", 0);
236 if(node && node->data)
237 cvs_output(node->data, 0);
238 else
239 cvs_output("(none)",0);
240 cvs_output("\n",0);
241 }
242 }
243
244 if (vers->entdata)
245 {
246 Entnode *edata;
247
248 edata = vers->entdata;
249 if (edata->tag)
250 {
251 if (vers->vn_rcs == NULL)
252 {
253 cvs_output (" Sticky Tag:\t\t", 0);
254 cvs_output (edata->tag, 0);
255 cvs_output (" - MISSING from RCS file!\n", 0);
256 }
257 else
258 {
259 if (isdigit ((unsigned char) edata->tag[0]))
260 {
261 cvs_output (" Sticky Tag:\t\t", 0);
262 cvs_output (edata->tag, 0);
263 cvs_output ("\n", 0);
264 }
265 else
266 {
267 char *branch = NULL;
268
269 if (RCS_nodeisbranch (finfo->rcs, edata->tag))
270 branch = RCS_whatbranch(finfo->rcs, edata->tag);
271
272 cvs_output (" Sticky Tag:\t\t", 0);
273 cvs_output (edata->tag, 0);
274 cvs_output (" (", 0);
275 cvs_output (branch ? "branch" : "revision", 0);
276 cvs_output (": ", 0);
277 cvs_output (branch ? branch : vers->vn_rcs, 0);
278 cvs_output (")\n", 0);
279
280 if (branch)
281 free (branch);
282 }
283 }
284 }
285 else if (!really_quiet)
286 cvs_output (" Sticky Tag:\t\t(none)\n", 0);
287
288 if (edata->date)
289 {
290 cvs_output (" Sticky Date:\t\t", 0);
291 cvs_output (edata->date, 0);
292 cvs_output ("\n", 0);
293 }
294 else if (!really_quiet)
295 cvs_output (" Sticky Date:\t\t(none)\n", 0);
296
297 if (edata->options && edata->options[0])
298 {
299 cvs_output (" Sticky Options:\t", 0);
300 cvs_output (edata->options, 0);
301 cvs_output ("\n", 0);
302 }
303 else if (!really_quiet)
304 cvs_output (" Sticky Options:\t(none)\n", 0);
305 }
306
307 if (long_format && vers->srcfile)
308 {
309 List *symbols = RCS_symbols(vers->srcfile);
310
311 cvs_output ("\n Existing Tags:\n", 0);
312 if (symbols)
313 {
314 xrcsnode = finfo->rcs;
315 (void) walklist (symbols, tag_list_proc, NULL);
316 }
317 else
318 cvs_output ("\tNo Tags Exist\n", 0);
319 }
320
321 cvs_output ("\n", 0);
322 freevers_ts (&vers);
323 return (0);
324 }
325
326 /*
327 * Print a warm fuzzy message
328 */
329 /* ARGSUSED */
330 static Dtype
status_dirproc(callerdat,dir,repos,update_dir,entries)331 status_dirproc (callerdat, dir, repos, update_dir, entries)
332 void *callerdat;
333 char *dir;
334 char *repos;
335 char *update_dir;
336 List *entries;
337 {
338 if (!quiet)
339 error (0, 0, "Examining %s", update_dir);
340 return (R_PROCESS);
341 }
342
343 /*
344 * Print out a tag and its type
345 */
346 static int
tag_list_proc(p,closure)347 tag_list_proc (p, closure)
348 Node *p;
349 void *closure;
350 {
351 char *branch = NULL;
352 char *buf;
353
354 if (RCS_nodeisbranch (xrcsnode, p->key))
355 branch = RCS_whatbranch(xrcsnode, p->key) ;
356
357 buf = xmalloc (80 + strlen (p->key)
358 + (branch ? strlen (branch) : strlen (p->data)));
359 sprintf (buf, "\t%-25s\t(%s: %s)\n", p->key,
360 branch ? "branch" : "revision",
361 branch ? branch : p->data);
362 cvs_output (buf, 0);
363 free (buf);
364
365 if (branch)
366 free (branch);
367
368 return (0);
369 }
370