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