xref: /openbsd/gnu/usr.bin/cvs/src/status.c (revision c133e2ca)
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