xref: /netbsd-src/external/gpl2/xcvs/dist/src/status.c (revision 5a6c14c844c4c665da5632061aebde7bb2cb5766)
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