xref: /dragonfly/contrib/cvs-1.12/src/status.c (revision 0bb9290e)
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
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
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
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
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