classify.c revision 107484
117721Speter/*
217721Speter * Copyright (c) 1992, Brian Berliner and Jeff Polk
317721Speter * Copyright (c) 1989-1992, Brian Berliner
417721Speter *
517721Speter * You may distribute under the terms of the GNU General Public License as
632785Speter * specified in the README file that comes with the CVS source distribution.
717721Speter *
817721Speter */
917721Speter
1017721Speter#include "cvs.h"
1117721Speter
1232785Speterstatic void sticky_ck PROTO ((struct file_info *finfo, int aflag,
1332785Speter			      Vers_TS * vers));
1417721Speter
1517721Speter/*
1617721Speter * Classify the state of a file
1717721Speter */
1817721SpeterCtype
1925839SpeterClassify_File (finfo, tag, date, options, force_tag_match, aflag, versp,
2025839Speter	       pipeout)
2125839Speter    struct file_info *finfo;
2217721Speter    char *tag;
2317721Speter    char *date;
2432785Speter
2532785Speter    /* Keyword expansion options.  Can be either NULL or "" to
2632785Speter       indicate none are specified here.  */
2717721Speter    char *options;
2832785Speter
2917721Speter    int force_tag_match;
3017721Speter    int aflag;
3117721Speter    Vers_TS **versp;
3217721Speter    int pipeout;
3317721Speter{
3417721Speter    Vers_TS *vers;
3517721Speter    Ctype ret;
3617721Speter
3717721Speter    /* get all kinds of good data about the file */
3825839Speter    vers = Version_TS (finfo, options, tag, date,
3925839Speter		       force_tag_match, 0);
4017721Speter
4117721Speter    if (vers->vn_user == NULL)
4217721Speter    {
4317721Speter	/* No entry available, ts_rcs is invalid */
4417721Speter	if (vers->vn_rcs == NULL)
4517721Speter	{
4617721Speter	    /* there is no RCS file either */
4717721Speter	    if (vers->ts_user == NULL)
4817721Speter	    {
4917721Speter		/* there is no user file */
5025839Speter		/* FIXME: Why do we skip this message if vers->tag or
5125839Speter		   vers->date is set?  It causes "cvs update -r tag98 foo"
5225839Speter		   to silently do nothing, which is seriously confusing
5325839Speter		   behavior.  "cvs update foo" gives this message, which
5425839Speter		   is what I would expect.  */
5517721Speter		if (!force_tag_match || !(vers->tag || vers->date))
5617721Speter		    if (!really_quiet)
5725839Speter			error (0, 0, "nothing known about %s", finfo->fullname);
5817721Speter		ret = T_UNKNOWN;
5917721Speter	    }
6017721Speter	    else
6117721Speter	    {
6217721Speter		/* there is a user file */
6325839Speter		/* FIXME: Why do we skip this message if vers->tag or
6425839Speter		   vers->date is set?  It causes "cvs update -r tag98 foo"
6525839Speter		   to silently do nothing, which is seriously confusing
6625839Speter		   behavior.  "cvs update foo" gives this message, which
6725839Speter		   is what I would expect.  */
6817721Speter		if (!force_tag_match || !(vers->tag || vers->date))
6917721Speter		    if (!really_quiet)
7032785Speter			error (0, 0, "use `%s add' to create an entry for %s",
7132785Speter			       program_name, finfo->fullname);
7217721Speter		ret = T_UNKNOWN;
7317721Speter	    }
7417721Speter	}
7517721Speter	else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
7617721Speter	{
7781404Speter	    /* there is an RCS file, but it's dead */
7817721Speter	    if (vers->ts_user == NULL)
7925839Speter		ret = T_UPTODATE;
8017721Speter	    else
8117721Speter	    {
8232785Speter		error (0, 0, "use `%s add' to create an entry for %s",
8332785Speter		       program_name, finfo->fullname);
8417721Speter		ret = T_UNKNOWN;
8517721Speter	    }
8617721Speter	}
8781404Speter	else if (!pipeout && vers->ts_user && No_Difference (finfo, vers))
8817721Speter	{
8981404Speter	    /* the files were different so it is a conflict */
9081404Speter	    if (!really_quiet)
9181404Speter		error (0, 0, "move away %s; it is in the way",
9281404Speter		       finfo->fullname);
9381404Speter	    ret = T_CONFLICT;
9417721Speter	}
9581404Speter	else
9681404Speter	    /* no user file or no difference, just checkout */
9781404Speter	    ret = T_CHECKOUT;
9817721Speter    }
9917721Speter    else if (strcmp (vers->vn_user, "0") == 0)
10017721Speter    {
10117721Speter	/* An entry for a new-born file; ts_rcs is dummy */
10217721Speter
10317721Speter	if (vers->ts_user == NULL)
10417721Speter	{
105107484Speter	    if (pipeout)
106107484Speter	    {
107107484Speter		ret = T_CHECKOUT;
108107484Speter	    }
109107484Speter	    else
110107484Speter	    {
111107484Speter		/*
112107484Speter		 * There is no user file, but there should be one; remove the
113107484Speter		 * entry
114107484Speter		 */
115107484Speter		if (!really_quiet)
116107484Speter		    error (0, 0, "warning: new-born %s has disappeared",
117107484Speter			   finfo->fullname);
118107484Speter		ret = T_REMOVE_ENTRY;
119107484Speter	    }
12017721Speter	}
12181404Speter	else if (vers->vn_rcs == NULL ||
12281404Speter		 RCS_isdead (vers->srcfile, vers->vn_rcs))
12381404Speter	    /* No RCS file or RCS file revision is dead  */
12481404Speter	    ret = T_ADDED;
12517721Speter	else
12617721Speter	{
127107484Speter	    if (pipeout)
12881404Speter	    {
129107484Speter		ret = T_CHECKOUT;
13081404Speter	    }
13117721Speter	    else
13217721Speter	    {
133107484Speter		if (vers->srcfile->flags & INATTIC
134107484Speter		    && vers->srcfile->flags & VALID)
135107484Speter		{
136107484Speter		    /* This file has been added on some branch other than
137107484Speter		       the one we are looking at.  In the branch we are
138107484Speter		       looking at, the file was already valid.  */
139107484Speter		    if (!really_quiet)
140107484Speter			error (0, 0,
141107484Speter			   "conflict: %s has been added, but already exists",
142107484Speter			       finfo->fullname);
143107484Speter		}
144107484Speter		else
145107484Speter		{
146107484Speter		    /*
147107484Speter		     * There is an RCS file, so someone else must have checked
148107484Speter		     * one in behind our back; conflict
149107484Speter		     */
150107484Speter		    if (!really_quiet)
151107484Speter			error (0, 0,
15281404Speter			   "conflict: %s created independently by second party",
153107484Speter			       finfo->fullname);
154107484Speter		}
155107484Speter		ret = T_CONFLICT;
15617721Speter	    }
15717721Speter	}
15817721Speter    }
15917721Speter    else if (vers->vn_user[0] == '-')
16017721Speter    {
16117721Speter	/* An entry for a removed file, ts_rcs is invalid */
16217721Speter
16317721Speter	if (vers->ts_user == NULL)
16417721Speter	{
16517721Speter	    /* There is no user file (as it should be) */
16617721Speter
16725839Speter	    if (vers->vn_rcs == NULL
16825839Speter		|| RCS_isdead (vers->srcfile, vers->vn_rcs))
16917721Speter	    {
17017721Speter
17117721Speter		/*
17217721Speter		 * There is no RCS file; this is all-right, but it has been
17317721Speter		 * removed independently by a second party; remove the entry
17417721Speter		 */
17517721Speter		ret = T_REMOVE_ENTRY;
17617721Speter	    }
17781404Speter	    else if (strcmp (vers->vn_rcs, vers->vn_user + 1) == 0)
17817721Speter		/*
17917721Speter		 * The RCS file is the same version as the user file was, and
18017721Speter		 * that's OK; remove it
18117721Speter		 */
18217721Speter		ret = T_REMOVED;
18381404Speter	    else if (pipeout)
18481404Speter		/*
18581404Speter		 * The RCS file doesn't match the user's file, but it doesn't
18681404Speter		 * matter in this case
18781404Speter		 */
18881404Speter		ret = T_NEEDS_MERGE;
18917721Speter	    else
19017721Speter	    {
19117721Speter
19217721Speter		/*
19317721Speter		 * The RCS file is a newer version than the removed user file
19417721Speter		 * and this is definitely not OK; make it a conflict.
19517721Speter		 */
19617721Speter		if (!really_quiet)
19717721Speter		    error (0, 0,
19817721Speter			   "conflict: removed %s was modified by second party",
19925839Speter			   finfo->fullname);
20017721Speter		ret = T_CONFLICT;
20117721Speter	    }
20217721Speter	}
20317721Speter	else
20417721Speter	{
20517721Speter	    /* The user file shouldn't be there */
20617721Speter	    if (!really_quiet)
20717721Speter		error (0, 0, "%s should be removed and is still there",
20825839Speter		       finfo->fullname);
20917721Speter	    ret = T_REMOVED;
21017721Speter	}
21117721Speter    }
21217721Speter    else
21317721Speter    {
21417721Speter	/* A normal entry, TS_Rcs is valid */
21581404Speter	if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs))
21617721Speter	{
21717721Speter	    /* There is no RCS file */
21817721Speter
21917721Speter	    if (vers->ts_user == NULL)
22017721Speter	    {
22117721Speter		/* There is no user file, so just remove the entry */
22217721Speter		if (!really_quiet)
22317721Speter		    error (0, 0, "warning: %s is not (any longer) pertinent",
22425839Speter			   finfo->fullname);
22517721Speter		ret = T_REMOVE_ENTRY;
22617721Speter	    }
22717721Speter	    else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
22817721Speter	    {
22917721Speter
23017721Speter		/*
23117721Speter		 * The user file is still unmodified, so just remove it from
23217721Speter		 * the entry list
23317721Speter		 */
23417721Speter		if (!really_quiet)
23517721Speter		    error (0, 0, "%s is no longer in the repository",
23625839Speter			   finfo->fullname);
23717721Speter		ret = T_REMOVE_ENTRY;
23817721Speter	    }
23981404Speter	    else if (No_Difference (finfo, vers))
24017721Speter	    {
24181404Speter		/* they are different -> conflict */
24281404Speter		if (!really_quiet)
24381404Speter		    error (0, 0,
24417721Speter	       "conflict: %s is modified but no longer in the repository",
24525839Speter			   finfo->fullname);
24681404Speter		ret = T_CONFLICT;
24717721Speter	    }
24881404Speter	    else
24981404Speter	    {
25081404Speter		/* they weren't really different */
25181404Speter		if (!really_quiet)
25281404Speter		    error (0, 0,
25381404Speter			   "warning: %s is not (any longer) pertinent",
25481404Speter			   finfo->fullname);
25581404Speter		ret = T_REMOVE_ENTRY;
25681404Speter	    }
25717721Speter	}
25817721Speter	else if (strcmp (vers->vn_rcs, vers->vn_user) == 0)
25917721Speter	{
26017721Speter	    /* The RCS file is the same version as the user file */
26117721Speter
26217721Speter	    if (vers->ts_user == NULL)
26317721Speter	    {
26417721Speter
26517721Speter		/*
26617721Speter		 * There is no user file, so note that it was lost and
26717721Speter		 * extract a new version
26817721Speter		 */
26944852Speter		/* Comparing the command_name against "update", in
27044852Speter		   addition to being an ugly way to operate, means
27144852Speter		   that this message does not get printed by the
27244852Speter		   server.  That might be considered just a straight
27344852Speter		   bug, although there is one subtlety: that case also
27444852Speter		   gets hit when a patch fails and the client fetches
27544852Speter		   a file.  I'm not sure there is currently any way
27644852Speter		   for the server to distinguish those two cases.  */
27717721Speter		if (strcmp (command_name, "update") == 0)
27817721Speter		    if (!really_quiet)
27925839Speter			error (0, 0, "warning: %s was lost", finfo->fullname);
28017721Speter		ret = T_CHECKOUT;
28117721Speter	    }
28217721Speter	    else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
28317721Speter	    {
28417721Speter
28517721Speter		/*
28617721Speter		 * The user file is still unmodified, so nothing special at
28717721Speter		 * all to do -- no lists updated, unless the sticky -k option
28817721Speter		 * has changed.  If the sticky tag has changed, we just need
28917721Speter		 * to re-register the entry
29017721Speter		 */
29134461Speter		/* TODO: decide whether we need to check file permissions
29234461Speter		   for a mismatch, and return T_CONFLICT if so. */
29317721Speter		if (vers->entdata->options &&
29417721Speter		    strcmp (vers->entdata->options, vers->options) != 0)
29517721Speter		    ret = T_CHECKOUT;
29617721Speter		else
29717721Speter		{
29832785Speter		    sticky_ck (finfo, aflag, vers);
29917721Speter		    ret = T_UPTODATE;
30017721Speter		}
30117721Speter	    }
30281404Speter	    else if (No_Difference (finfo, vers))
30317721Speter	    {
30417721Speter
30517721Speter		/*
30681404Speter		 * they really are different; modified if we aren't
30781404Speter		 * changing any sticky -k options, else needs merge
30817721Speter		 */
30917721Speter#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED
31081404Speter		if (strcmp (vers->entdata->options ?
31181404Speter		       vers->entdata->options : "", vers->options) == 0)
31281404Speter		    ret = T_MODIFIED;
31381404Speter		else
31481404Speter		    ret = T_NEEDS_MERGE;
31517721Speter#else
31681404Speter		ret = T_MODIFIED;
31781404Speter		sticky_ck (finfo, aflag, vers);
31817721Speter#endif
31981404Speter	    }
32081404Speter	    else if (strcmp (vers->entdata->options ?
32181404Speter		       vers->entdata->options : "", vers->options) != 0)
32281404Speter	    {
32381404Speter		/* file has not changed; check out if -k changed */
32481404Speter		ret = T_CHECKOUT;
32581404Speter	    }
32681404Speter	    else
32781404Speter	    {
32817721Speter
32981404Speter		/*
33081404Speter		 * else -> note that No_Difference will Register the
33181404Speter		 * file already for us, using the new tag/date. This
33281404Speter		 * is the desired behaviour
33381404Speter		 */
33481404Speter		ret = T_UPTODATE;
33517721Speter	    }
33617721Speter	}
33717721Speter	else
33817721Speter	{
33917721Speter	    /* The RCS file is a newer version than the user file */
34017721Speter
34117721Speter	    if (vers->ts_user == NULL)
34217721Speter	    {
34317721Speter		/* There is no user file, so just get it */
34417721Speter
34544852Speter		/* See comment at other "update" compare, for more
34644852Speter		   thoughts on this comparison.  */
34717721Speter		if (strcmp (command_name, "update") == 0)
34817721Speter		    if (!really_quiet)
34925839Speter			error (0, 0, "warning: %s was lost", finfo->fullname);
35017721Speter		ret = T_CHECKOUT;
35117721Speter	    }
35217721Speter	    else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
35317721Speter	    {
35417721Speter
35517721Speter		/*
35617721Speter		 * The user file is still unmodified, so just get it as well
35717721Speter		 */
35834461Speter		if (strcmp (vers->entdata->options ?
35917721Speter			    vers->entdata->options : "", vers->options) != 0
36017721Speter		    || (vers->srcfile != NULL
36117721Speter			&& (vers->srcfile->flags & INATTIC) != 0))
36217721Speter		    ret = T_CHECKOUT;
36317721Speter		else
36417721Speter		    ret = T_PATCH;
36581404Speter	    }
36681404Speter	    else if (No_Difference (finfo, vers))
36781404Speter		/* really modified, needs to merge */
36881404Speter		ret = T_NEEDS_MERGE;
36981404Speter	    else if ((strcmp (vers->entdata->options ?
37081404Speter			      vers->entdata->options : "", vers->options)
37181404Speter		      != 0)
37281404Speter		     || (vers->srcfile != NULL
37381404Speter		         && (vers->srcfile->flags & INATTIC) != 0))
37481404Speter		/* not really modified, check it out */
37517721Speter		ret = T_CHECKOUT;
37617721Speter	    else
37781404Speter		ret = T_PATCH;
37817721Speter	}
37917721Speter    }
38017721Speter
38117721Speter    /* free up the vers struct, or just return it */
38217721Speter    if (versp != (Vers_TS **) NULL)
38317721Speter	*versp = vers;
38417721Speter    else
38517721Speter	freevers_ts (&vers);
38617721Speter
38717721Speter    /* return the status of the file */
38817721Speter    return (ret);
38917721Speter}
39017721Speter
39117721Speterstatic void
39232785Spetersticky_ck (finfo, aflag, vers)
39332785Speter    struct file_info *finfo;
39417721Speter    int aflag;
39517721Speter    Vers_TS *vers;
39617721Speter{
39717721Speter    if (aflag || vers->tag || vers->date)
39817721Speter    {
39917721Speter	char *enttag = vers->entdata->tag;
40017721Speter	char *entdate = vers->entdata->date;
40117721Speter
40217721Speter	if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
40317721Speter	    ((enttag && !vers->tag) || (!enttag && vers->tag)) ||
40417721Speter	    (entdate && vers->date && strcmp (entdate, vers->date)) ||
40517721Speter	    ((entdate && !vers->date) || (!entdate && vers->date)))
40617721Speter	{
40732785Speter	    Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs,
40817721Speter		      vers->options, vers->tag, vers->date, vers->ts_conflict);
40917721Speter
41017721Speter#ifdef SERVER_SUPPORT
41117721Speter	    if (server_active)
41217721Speter	    {
41317721Speter		/* We need to update the entries line on the client side.
41417721Speter		   It is possible we will later update it again via
41517721Speter		   server_updated or some such, but that is OK.  */
41617721Speter		server_update_entries
41732785Speter		  (finfo->file, finfo->update_dir, finfo->repository,
41817721Speter		   strcmp (vers->ts_rcs, vers->ts_user) == 0 ?
41917721Speter		   SERVER_UPDATED : SERVER_MERGED);
42017721Speter	    }
42117721Speter#endif
42217721Speter	}
42317721Speter    }
42417721Speter}
425