classify.c revision 34461
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 */
9
10#include "cvs.h"
11
12static void sticky_ck PROTO ((struct file_info *finfo, int aflag,
13			      Vers_TS * vers));
14
15/*
16 * Classify the state of a file
17 */
18Ctype
19Classify_File (finfo, tag, date, options, force_tag_match, aflag, versp,
20	       pipeout)
21    struct file_info *finfo;
22    char *tag;
23    char *date;
24
25    /* Keyword expansion options.  Can be either NULL or "" to
26       indicate none are specified here.  */
27    char *options;
28
29    int force_tag_match;
30    int aflag;
31    Vers_TS **versp;
32    int pipeout;
33{
34    Vers_TS *vers;
35    Ctype ret;
36
37    /* get all kinds of good data about the file */
38    vers = Version_TS (finfo, options, tag, date,
39		       force_tag_match, 0);
40
41    if (vers->vn_user == NULL)
42    {
43	/* No entry available, ts_rcs is invalid */
44	if (vers->vn_rcs == NULL)
45	{
46	    /* there is no RCS file either */
47	    if (vers->ts_user == NULL)
48	    {
49		/* there is no user file */
50		/* FIXME: Why do we skip this message if vers->tag or
51		   vers->date is set?  It causes "cvs update -r tag98 foo"
52		   to silently do nothing, which is seriously confusing
53		   behavior.  "cvs update foo" gives this message, which
54		   is what I would expect.  */
55		if (!force_tag_match || !(vers->tag || vers->date))
56		    if (!really_quiet)
57			error (0, 0, "nothing known about %s", finfo->fullname);
58		ret = T_UNKNOWN;
59	    }
60	    else
61	    {
62		/* there is a user file */
63		/* FIXME: Why do we skip this message if vers->tag or
64		   vers->date is set?  It causes "cvs update -r tag98 foo"
65		   to silently do nothing, which is seriously confusing
66		   behavior.  "cvs update foo" gives this message, which
67		   is what I would expect.  */
68		if (!force_tag_match || !(vers->tag || vers->date))
69		    if (!really_quiet)
70			error (0, 0, "use `%s add' to create an entry for %s",
71			       program_name, finfo->fullname);
72		ret = T_UNKNOWN;
73	    }
74	}
75	else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
76	{
77	    if (vers->ts_user == NULL)
78		ret = T_UPTODATE;
79	    else
80	    {
81		error (0, 0, "use `%s add' to create an entry for %s",
82		       program_name, finfo->fullname);
83		ret = T_UNKNOWN;
84	    }
85	}
86	else
87	{
88	    /* there is an rcs file */
89
90	    if (vers->ts_user == NULL)
91	    {
92		/* There is no user file; needs checkout */
93		ret = T_CHECKOUT;
94	    }
95	    else
96	    {
97		if (pipeout)
98		{
99		    /*
100		     * The user file doesn't necessarily have anything
101		     * to do with this.
102		     */
103		    ret = T_CHECKOUT;
104		}
105		/*
106		 * There is a user file; print a warning and add it to the
107		 * conflict list, only if it is indeed different from what we
108		 * plan to extract
109		 */
110		else if (No_Difference (finfo, vers))
111		{
112		    /* the files were different so it is a conflict */
113		    if (!really_quiet)
114			error (0, 0, "move away %s; it is in the way",
115			       finfo->fullname);
116		    ret = T_CONFLICT;
117		}
118		else
119		    /* since there was no difference, still needs checkout */
120		    ret = T_CHECKOUT;
121	    }
122	}
123    }
124    else if (strcmp (vers->vn_user, "0") == 0)
125    {
126	/* An entry for a new-born file; ts_rcs is dummy */
127
128	if (vers->ts_user == NULL)
129	{
130	    /*
131	     * There is no user file, but there should be one; remove the
132	     * entry
133	     */
134	    if (!really_quiet)
135		error (0, 0, "warning: new-born %s has disappeared", finfo->fullname);
136	    ret = T_REMOVE_ENTRY;
137	}
138	else
139	{
140	    /* There is a user file */
141
142	    if (vers->vn_rcs == NULL)
143		/* There is no RCS file, added file */
144		ret = T_ADDED;
145	    else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
146		/* we are resurrecting. */
147		ret = T_ADDED;
148	    else
149	    {
150		if (vers->srcfile->flags & INATTIC
151		    && vers->srcfile->flags & VALID)
152		{
153		    /* This file has been added on some branch other than
154		       the one we are looking at.  In the branch we are
155		       looking at, the file was already valid.  */
156		    if (!really_quiet)
157			error (0, 0,
158			       "\
159conflict: %s has been added, but already exists",
160			       finfo->fullname);
161		}
162		else
163		{
164		    /*
165		     * There is an RCS file, so someone else must have checked
166		     * one in behind our back; conflict
167		     */
168		    if (!really_quiet)
169			error (0, 0,
170			       "\
171conflict: %s created independently by second party",
172			       finfo->fullname);
173		}
174		ret = T_CONFLICT;
175	    }
176	}
177    }
178    else if (vers->vn_user[0] == '-')
179    {
180	/* An entry for a removed file, ts_rcs is invalid */
181
182	if (vers->ts_user == NULL)
183	{
184	    /* There is no user file (as it should be) */
185
186	    if (vers->vn_rcs == NULL
187		|| RCS_isdead (vers->srcfile, vers->vn_rcs))
188	    {
189
190		/*
191		 * There is no RCS file; this is all-right, but it has been
192		 * removed independently by a second party; remove the entry
193		 */
194		ret = T_REMOVE_ENTRY;
195	    }
196	    else if (vers->vn_rcs == NULL
197		     ? vers->vn_user[1] == '\0'
198		     : strcmp (vers->vn_rcs, vers->vn_user + 1) == 0)
199		/*
200		 * The RCS file is the same version as the user file was, and
201		 * that's OK; remove it
202		 */
203		ret = T_REMOVED;
204	    else
205	    {
206
207		/*
208		 * The RCS file is a newer version than the removed user file
209		 * and this is definitely not OK; make it a conflict.
210		 */
211		if (!really_quiet)
212		    error (0, 0,
213			   "conflict: removed %s was modified by second party",
214			   finfo->fullname);
215		ret = T_CONFLICT;
216	    }
217	}
218	else
219	{
220	    /* The user file shouldn't be there */
221	    if (!really_quiet)
222		error (0, 0, "%s should be removed and is still there",
223		       finfo->fullname);
224	    ret = T_REMOVED;
225	}
226    }
227    else
228    {
229	/* A normal entry, TS_Rcs is valid */
230	if (vers->vn_rcs == NULL)
231	{
232	    /* There is no RCS file */
233
234	    if (vers->ts_user == NULL)
235	    {
236		/* There is no user file, so just remove the entry */
237		if (!really_quiet)
238		    error (0, 0, "warning: %s is not (any longer) pertinent",
239			   finfo->fullname);
240		ret = T_REMOVE_ENTRY;
241	    }
242	    else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
243	    {
244
245		/*
246		 * The user file is still unmodified, so just remove it from
247		 * the entry list
248		 */
249		if (!really_quiet)
250		    error (0, 0, "%s is no longer in the repository",
251			   finfo->fullname);
252		ret = T_REMOVE_ENTRY;
253	    }
254	    else
255	    {
256		/*
257		 * The user file has been modified and since it is no longer
258		 * in the repository, a conflict is raised
259		 */
260		if (No_Difference (finfo, vers))
261		{
262		    /* they are different -> conflict */
263		    if (!really_quiet)
264			error (0, 0,
265	       "conflict: %s is modified but no longer in the repository",
266			   finfo->fullname);
267		    ret = T_CONFLICT;
268		}
269		else
270		{
271		    /* they weren't really different */
272		    if (!really_quiet)
273			error (0, 0,
274			       "warning: %s is not (any longer) pertinent",
275			       finfo->fullname);
276		    ret = T_REMOVE_ENTRY;
277		}
278	    }
279	}
280	else if (strcmp (vers->vn_rcs, vers->vn_user) == 0)
281	{
282	    /* The RCS file is the same version as the user file */
283
284	    if (vers->ts_user == NULL)
285	    {
286
287		/*
288		 * There is no user file, so note that it was lost and
289		 * extract a new version
290		 */
291		if (strcmp (command_name, "update") == 0)
292		    if (!really_quiet)
293			error (0, 0, "warning: %s was lost", finfo->fullname);
294		ret = T_CHECKOUT;
295	    }
296	    else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
297	    {
298
299		/*
300		 * The user file is still unmodified, so nothing special at
301		 * all to do -- no lists updated, unless the sticky -k option
302		 * has changed.  If the sticky tag has changed, we just need
303		 * to re-register the entry
304		 */
305		/* TODO: decide whether we need to check file permissions
306		   for a mismatch, and return T_CONFLICT if so. */
307		if (vers->entdata->options &&
308		    strcmp (vers->entdata->options, vers->options) != 0)
309		    ret = T_CHECKOUT;
310		else
311		{
312		    sticky_ck (finfo, aflag, vers);
313		    ret = T_UPTODATE;
314		}
315	    }
316	    else
317	    {
318
319		/*
320		 * The user file appears to have been modified, but we call
321		 * No_Difference to verify that it really has been modified
322		 */
323		if (No_Difference (finfo, vers))
324		{
325
326		    /*
327		     * they really are different; modified if we aren't
328		     * changing any sticky -k options, else needs merge
329		     */
330#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED
331		    if (strcmp (vers->entdata->options ?
332			   vers->entdata->options : "", vers->options) == 0)
333			ret = T_MODIFIED;
334		    else
335			ret = T_NEEDS_MERGE;
336#else
337		    ret = T_MODIFIED;
338		    sticky_ck (finfo, aflag, vers);
339#endif
340		}
341		else
342		{
343		    /* file has not changed; check out if -k changed */
344		    if (strcmp (vers->entdata->options ?
345			   vers->entdata->options : "", vers->options) != 0)
346		    {
347			ret = T_CHECKOUT;
348		    }
349		    else
350		    {
351
352			/*
353			 * else -> note that No_Difference will Register the
354			 * file already for us, using the new tag/date. This
355			 * is the desired behaviour
356			 */
357			ret = T_UPTODATE;
358		    }
359		}
360	    }
361	}
362	else
363	{
364	    /* The RCS file is a newer version than the user file */
365
366	    if (vers->ts_user == NULL)
367	    {
368		/* There is no user file, so just get it */
369
370		if (strcmp (command_name, "update") == 0)
371		    if (!really_quiet)
372			error (0, 0, "warning: %s was lost", finfo->fullname);
373		ret = T_CHECKOUT;
374	    }
375	    else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
376	    {
377
378		/*
379		 * The user file is still unmodified, so just get it as well
380		 */
381#ifdef SERVER_SUPPORT
382		if (strcmp (vers->entdata->options ?
383			    vers->entdata->options : "", vers->options) != 0
384		    || (vers->srcfile != NULL
385			&& (vers->srcfile->flags & INATTIC) != 0))
386		    ret = T_CHECKOUT;
387		else
388		    ret = T_PATCH;
389#else
390		ret = T_CHECKOUT;
391#endif
392	    }
393	    else
394	    {
395		if (No_Difference (finfo, vers))
396		    /* really modified, needs to merge */
397		    ret = T_NEEDS_MERGE;
398#ifdef SERVER_SUPPORT
399	        else if ((strcmp (vers->entdata->options ?
400				  vers->entdata->options : "", vers->options)
401			  != 0)
402			 || (vers->srcfile != NULL
403			     && (vers->srcfile->flags & INATTIC) != 0))
404		    /* not really modified, check it out */
405		    ret = T_CHECKOUT;
406		else
407		    ret = T_PATCH;
408#else
409		else
410		    /* not really modified, check it out */
411		    ret = T_CHECKOUT;
412#endif
413	    }
414	}
415    }
416
417    /* free up the vers struct, or just return it */
418    if (versp != (Vers_TS **) NULL)
419	*versp = vers;
420    else
421	freevers_ts (&vers);
422
423    /* return the status of the file */
424    return (ret);
425}
426
427static void
428sticky_ck (finfo, aflag, vers)
429    struct file_info *finfo;
430    int aflag;
431    Vers_TS *vers;
432{
433    if (aflag || vers->tag || vers->date)
434    {
435	char *enttag = vers->entdata->tag;
436	char *entdate = vers->entdata->date;
437
438	if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
439	    ((enttag && !vers->tag) || (!enttag && vers->tag)) ||
440	    (entdate && vers->date && strcmp (entdate, vers->date)) ||
441	    ((entdate && !vers->date) || (!entdate && vers->date)))
442	{
443	    Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs,
444		      vers->options, vers->tag, vers->date, vers->ts_conflict);
445
446#ifdef SERVER_SUPPORT
447	    if (server_active)
448	    {
449		/* We need to update the entries line on the client side.
450		   It is possible we will later update it again via
451		   server_updated or some such, but that is OK.  */
452		server_update_entries
453		  (finfo->file, finfo->update_dir, finfo->repository,
454		   strcmp (vers->ts_rcs, vers->ts_user) == 0 ?
455		   SERVER_UPDATED : SERVER_MERGED);
456	    }
457#endif
458	}
459    }
460}
461