xref: /openbsd-src/gnu/usr.bin/cvs/src/status.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
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
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
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 
127     status = Classify_File (finfo, (char *) NULL, (char *) NULL, (char *) NULL,
128 			    1, 0, &vers, 0);
129     sstat = "Classify Error";
130     switch (status)
131     {
132 	case T_UNKNOWN:
133 	    sstat = "Unknown";
134 	    break;
135 	case T_CHECKOUT:
136 	    sstat = "Needs Checkout";
137 	    break;
138 	case T_PATCH:
139 	    sstat = "Needs Patch";
140 	    break;
141 	case T_CONFLICT:
142 	    /* I _think_ that "unresolved" is correct; that if it has
143 	       been resolved then the status will change.  But I'm not
144 	       sure about that.  */
145 	    sstat = "Unresolved Conflict";
146 	    break;
147 	case T_ADDED:
148 	    sstat = "Locally Added";
149 	    break;
150 	case T_REMOVED:
151 	    sstat = "Locally Removed";
152 	    break;
153 	case T_MODIFIED:
154 	    if (vers->ts_conflict)
155 		sstat = "File had conflicts on merge";
156 	    else
157 		sstat = "Locally Modified";
158 	    break;
159 	case T_REMOVE_ENTRY:
160 	    sstat = "Entry Invalid";
161 	    break;
162 	case T_UPTODATE:
163 	    sstat = "Up-to-date";
164 	    break;
165 	case T_NEEDS_MERGE:
166 	    sstat = "Needs Merge";
167 	    break;
168 	case T_TITLE:
169 	    /* I don't think this case can occur here.  Just print
170 	       "Classify Error".  */
171 	    break;
172     }
173 
174     cvs_output ("\
175 ===================================================================\n", 0);
176     if (vers->ts_user == NULL)
177     {
178 	cvs_output ("File: no file ", 0);
179 	cvs_output (finfo->file, 0);
180 	cvs_output ("\t\tStatus: ", 0);
181 	cvs_output (sstat, 0);
182 	cvs_output ("\n\n", 0);
183     }
184     else
185     {
186 	char *buf;
187 	buf = xmalloc (strlen (finfo->file) + strlen (sstat) + 80);
188 	sprintf (buf, "File: %-17s\tStatus: %s\n\n", finfo->file, sstat);
189 	cvs_output (buf, 0);
190 	free (buf);
191     }
192 
193     if (vers->vn_user == NULL)
194     {
195 	cvs_output ("   Working revision:\tNo entry for ", 0);
196 	cvs_output (finfo->file, 0);
197 	cvs_output ("\n", 0);
198     }
199     else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
200 	cvs_output ("   Working revision:\tNew file!\n", 0);
201 #ifdef SERVER_SUPPORT
202     else if (server_active)
203     {
204 	cvs_output ("   Working revision:\t", 0);
205 	cvs_output (vers->vn_user, 0);
206 	cvs_output ("\n", 0);
207     }
208 #endif
209     else
210     {
211 	cvs_output ("   Working revision:\t", 0);
212 	cvs_output (vers->vn_user, 0);
213 	cvs_output ("\t", 0);
214 	cvs_output (vers->ts_rcs, 0);
215 	cvs_output ("\n", 0);
216     }
217 
218     if (vers->vn_rcs == NULL)
219 	cvs_output ("   Repository revision:\tNo revision control file\n", 0);
220     else
221     {
222 	cvs_output ("   Repository revision:\t", 0);
223 	cvs_output (vers->vn_rcs, 0);
224 	cvs_output ("\t", 0);
225 	cvs_output (vers->srcfile->path, 0);
226 	cvs_output ("\n", 0);
227     }
228 
229     if (vers->entdata)
230     {
231 	Entnode *edata;
232 
233 	edata = vers->entdata;
234 	if (edata->tag)
235 	{
236 	    if (vers->vn_rcs == NULL)
237 	    {
238 		cvs_output ("   Sticky Tag:\t\t", 0);
239 		cvs_output (edata->tag, 0);
240 		cvs_output (" - MISSING from RCS file!\n", 0);
241 	    }
242 	    else
243 	    {
244 		if (isdigit ((unsigned char) edata->tag[0]))
245 		{
246 		    cvs_output ("   Sticky Tag:\t\t", 0);
247 		    cvs_output (edata->tag, 0);
248 		    cvs_output ("\n", 0);
249 		}
250 		else
251 		{
252 		    char *branch = NULL;
253 
254 		    if (RCS_nodeisbranch (finfo->rcs, edata->tag))
255 			branch = RCS_whatbranch(finfo->rcs, edata->tag);
256 
257 		    cvs_output ("   Sticky Tag:\t\t", 0);
258 		    cvs_output (edata->tag, 0);
259 		    cvs_output (" (", 0);
260 		    cvs_output (branch ? "branch" : "revision", 0);
261 		    cvs_output (": ", 0);
262 		    cvs_output (branch ? branch : vers->vn_rcs, 0);
263 		    cvs_output (")\n", 0);
264 
265 		    if (branch)
266 			free (branch);
267 		}
268 	    }
269 	}
270 	else if (!really_quiet)
271 	    cvs_output ("   Sticky Tag:\t\t(none)\n", 0);
272 
273 	if (edata->date)
274 	{
275 	    cvs_output ("   Sticky Date:\t\t", 0);
276 	    cvs_output (edata->date, 0);
277 	    cvs_output ("\n", 0);
278 	}
279 	else if (!really_quiet)
280 	    cvs_output ("   Sticky Date:\t\t(none)\n", 0);
281 
282 	if (edata->options && edata->options[0])
283 	{
284 	    cvs_output ("   Sticky Options:\t", 0);
285 	    cvs_output (edata->options, 0);
286 	    cvs_output ("\n", 0);
287 	}
288 	else if (!really_quiet)
289 	    cvs_output ("   Sticky Options:\t(none)\n", 0);
290     }
291 
292     if (long_format && vers->srcfile)
293     {
294 	List *symbols = RCS_symbols(vers->srcfile);
295 
296 	cvs_output ("\n   Existing Tags:\n", 0);
297 	if (symbols)
298 	{
299 	    xrcsnode = finfo->rcs;
300 	    (void) walklist (symbols, tag_list_proc, NULL);
301 	}
302 	else
303 	    cvs_output ("\tNo Tags Exist\n", 0);
304     }
305 
306     cvs_output ("\n", 0);
307     freevers_ts (&vers);
308     return (0);
309 }
310 
311 /*
312  * Print a warm fuzzy message
313  */
314 /* ARGSUSED */
315 static Dtype
316 status_dirproc (callerdat, dir, repos, update_dir, entries)
317     void *callerdat;
318     char *dir;
319     char *repos;
320     char *update_dir;
321     List *entries;
322 {
323     if (!quiet)
324 	error (0, 0, "Examining %s", update_dir);
325     return (R_PROCESS);
326 }
327 
328 /*
329  * Print out a tag and its type
330  */
331 static int
332 tag_list_proc (p, closure)
333     Node *p;
334     void *closure;
335 {
336     char *branch = NULL;
337     char *buf;
338 
339     if (RCS_nodeisbranch (xrcsnode, p->key))
340 	branch = RCS_whatbranch(xrcsnode, p->key) ;
341 
342     buf = xmalloc (80 + strlen (p->key)
343 		   + (branch ? strlen (branch) : strlen (p->data)));
344     sprintf (buf, "\t%-25s\t(%s: %s)\n", p->key,
345 	     branch ? "branch" : "revision",
346 	     branch ? branch : p->data);
347     cvs_output (buf, 0);
348     free (buf);
349 
350     if (branch)
351 	free (branch);
352 
353     return (0);
354 }
355