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