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