classify.c revision 44852
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	{
7717721Speter	    if (vers->ts_user == NULL)
7825839Speter		ret = T_UPTODATE;
7917721Speter	    else
8017721Speter	    {
8132785Speter		error (0, 0, "use `%s add' to create an entry for %s",
8232785Speter		       program_name, finfo->fullname);
8317721Speter		ret = T_UNKNOWN;
8417721Speter	    }
8517721Speter	}
8617721Speter	else
8717721Speter	{
8817721Speter	    /* there is an rcs file */
8917721Speter
9017721Speter	    if (vers->ts_user == NULL)
9117721Speter	    {
9217721Speter		/* There is no user file; needs checkout */
9317721Speter		ret = T_CHECKOUT;
9417721Speter	    }
9517721Speter	    else
9617721Speter	    {
9717721Speter		if (pipeout)
9817721Speter		{
9917721Speter		    /*
10017721Speter		     * The user file doesn't necessarily have anything
10117721Speter		     * to do with this.
10217721Speter		     */
10317721Speter		    ret = T_CHECKOUT;
10417721Speter		}
10517721Speter		/*
10617721Speter		 * There is a user file; print a warning and add it to the
10717721Speter		 * conflict list, only if it is indeed different from what we
10817721Speter		 * plan to extract
10917721Speter		 */
11025839Speter		else if (No_Difference (finfo, vers))
11117721Speter		{
11217721Speter		    /* the files were different so it is a conflict */
11317721Speter		    if (!really_quiet)
11417721Speter			error (0, 0, "move away %s; it is in the way",
11525839Speter			       finfo->fullname);
11617721Speter		    ret = T_CONFLICT;
11717721Speter		}
11817721Speter		else
11917721Speter		    /* since there was no difference, still needs checkout */
12017721Speter		    ret = T_CHECKOUT;
12117721Speter	    }
12217721Speter	}
12317721Speter    }
12417721Speter    else if (strcmp (vers->vn_user, "0") == 0)
12517721Speter    {
12617721Speter	/* An entry for a new-born file; ts_rcs is dummy */
12717721Speter
12817721Speter	if (vers->ts_user == NULL)
12917721Speter	{
13017721Speter	    /*
13117721Speter	     * There is no user file, but there should be one; remove the
13217721Speter	     * entry
13317721Speter	     */
13417721Speter	    if (!really_quiet)
13525839Speter		error (0, 0, "warning: new-born %s has disappeared", finfo->fullname);
13617721Speter	    ret = T_REMOVE_ENTRY;
13717721Speter	}
13817721Speter	else
13917721Speter	{
14017721Speter	    /* There is a user file */
14117721Speter
14217721Speter	    if (vers->vn_rcs == NULL)
14317721Speter		/* There is no RCS file, added file */
14417721Speter		ret = T_ADDED;
14517721Speter	    else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
14617721Speter		/* we are resurrecting. */
14717721Speter		ret = T_ADDED;
14817721Speter	    else
14917721Speter	    {
15017721Speter		if (vers->srcfile->flags & INATTIC
15117721Speter		    && vers->srcfile->flags & VALID)
15217721Speter		{
15317721Speter		    /* This file has been added on some branch other than
15417721Speter		       the one we are looking at.  In the branch we are
15517721Speter		       looking at, the file was already valid.  */
15617721Speter		    if (!really_quiet)
15717721Speter			error (0, 0,
15817721Speter			       "\
15917721Speterconflict: %s has been added, but already exists",
16025839Speter			       finfo->fullname);
16117721Speter		}
16217721Speter		else
16317721Speter		{
16417721Speter		    /*
16517721Speter		     * There is an RCS file, so someone else must have checked
16617721Speter		     * one in behind our back; conflict
16717721Speter		     */
16817721Speter		    if (!really_quiet)
16917721Speter			error (0, 0,
17017721Speter			       "\
17117721Speterconflict: %s created independently by second party",
17225839Speter			       finfo->fullname);
17317721Speter		}
17417721Speter		ret = T_CONFLICT;
17517721Speter	    }
17617721Speter	}
17717721Speter    }
17817721Speter    else if (vers->vn_user[0] == '-')
17917721Speter    {
18017721Speter	/* An entry for a removed file, ts_rcs is invalid */
18117721Speter
18217721Speter	if (vers->ts_user == NULL)
18317721Speter	{
18417721Speter	    /* There is no user file (as it should be) */
18517721Speter
18625839Speter	    if (vers->vn_rcs == NULL
18725839Speter		|| RCS_isdead (vers->srcfile, vers->vn_rcs))
18817721Speter	    {
18917721Speter
19017721Speter		/*
19117721Speter		 * There is no RCS file; this is all-right, but it has been
19217721Speter		 * removed independently by a second party; remove the entry
19317721Speter		 */
19417721Speter		ret = T_REMOVE_ENTRY;
19517721Speter	    }
19625839Speter	    else if (vers->vn_rcs == NULL
19725839Speter		     ? vers->vn_user[1] == '\0'
19825839Speter		     : strcmp (vers->vn_rcs, vers->vn_user + 1) == 0)
19917721Speter		/*
20017721Speter		 * The RCS file is the same version as the user file was, and
20117721Speter		 * that's OK; remove it
20217721Speter		 */
20317721Speter		ret = T_REMOVED;
20417721Speter	    else
20517721Speter	    {
20617721Speter
20717721Speter		/*
20817721Speter		 * The RCS file is a newer version than the removed user file
20917721Speter		 * and this is definitely not OK; make it a conflict.
21017721Speter		 */
21117721Speter		if (!really_quiet)
21217721Speter		    error (0, 0,
21317721Speter			   "conflict: removed %s was modified by second party",
21425839Speter			   finfo->fullname);
21517721Speter		ret = T_CONFLICT;
21617721Speter	    }
21717721Speter	}
21817721Speter	else
21917721Speter	{
22017721Speter	    /* The user file shouldn't be there */
22117721Speter	    if (!really_quiet)
22217721Speter		error (0, 0, "%s should be removed and is still there",
22325839Speter		       finfo->fullname);
22417721Speter	    ret = T_REMOVED;
22517721Speter	}
22617721Speter    }
22717721Speter    else
22817721Speter    {
22917721Speter	/* A normal entry, TS_Rcs is valid */
23017721Speter	if (vers->vn_rcs == NULL)
23117721Speter	{
23217721Speter	    /* There is no RCS file */
23317721Speter
23417721Speter	    if (vers->ts_user == NULL)
23517721Speter	    {
23617721Speter		/* There is no user file, so just remove the entry */
23717721Speter		if (!really_quiet)
23817721Speter		    error (0, 0, "warning: %s is not (any longer) pertinent",
23925839Speter			   finfo->fullname);
24017721Speter		ret = T_REMOVE_ENTRY;
24117721Speter	    }
24217721Speter	    else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
24317721Speter	    {
24417721Speter
24517721Speter		/*
24617721Speter		 * The user file is still unmodified, so just remove it from
24717721Speter		 * the entry list
24817721Speter		 */
24917721Speter		if (!really_quiet)
25017721Speter		    error (0, 0, "%s is no longer in the repository",
25125839Speter			   finfo->fullname);
25217721Speter		ret = T_REMOVE_ENTRY;
25317721Speter	    }
25417721Speter	    else
25517721Speter	    {
25617721Speter		/*
25717721Speter		 * The user file has been modified and since it is no longer
25817721Speter		 * in the repository, a conflict is raised
25917721Speter		 */
26025839Speter		if (No_Difference (finfo, vers))
26117721Speter		{
26217721Speter		    /* they are different -> conflict */
26317721Speter		    if (!really_quiet)
26417721Speter			error (0, 0,
26517721Speter	       "conflict: %s is modified but no longer in the repository",
26625839Speter			   finfo->fullname);
26717721Speter		    ret = T_CONFLICT;
26817721Speter		}
26917721Speter		else
27017721Speter		{
27117721Speter		    /* they weren't really different */
27217721Speter		    if (!really_quiet)
27317721Speter			error (0, 0,
27417721Speter			       "warning: %s is not (any longer) pertinent",
27525839Speter			       finfo->fullname);
27617721Speter		    ret = T_REMOVE_ENTRY;
27717721Speter		}
27817721Speter	    }
27917721Speter	}
28017721Speter	else if (strcmp (vers->vn_rcs, vers->vn_user) == 0)
28117721Speter	{
28217721Speter	    /* The RCS file is the same version as the user file */
28317721Speter
28417721Speter	    if (vers->ts_user == NULL)
28517721Speter	    {
28617721Speter
28717721Speter		/*
28817721Speter		 * There is no user file, so note that it was lost and
28917721Speter		 * extract a new version
29017721Speter		 */
29144852Speter		/* Comparing the command_name against "update", in
29244852Speter		   addition to being an ugly way to operate, means
29344852Speter		   that this message does not get printed by the
29444852Speter		   server.  That might be considered just a straight
29544852Speter		   bug, although there is one subtlety: that case also
29644852Speter		   gets hit when a patch fails and the client fetches
29744852Speter		   a file.  I'm not sure there is currently any way
29844852Speter		   for the server to distinguish those two cases.  */
29917721Speter		if (strcmp (command_name, "update") == 0)
30017721Speter		    if (!really_quiet)
30125839Speter			error (0, 0, "warning: %s was lost", finfo->fullname);
30217721Speter		ret = T_CHECKOUT;
30317721Speter	    }
30417721Speter	    else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
30517721Speter	    {
30617721Speter
30717721Speter		/*
30817721Speter		 * The user file is still unmodified, so nothing special at
30917721Speter		 * all to do -- no lists updated, unless the sticky -k option
31017721Speter		 * has changed.  If the sticky tag has changed, we just need
31117721Speter		 * to re-register the entry
31217721Speter		 */
31334461Speter		/* TODO: decide whether we need to check file permissions
31434461Speter		   for a mismatch, and return T_CONFLICT if so. */
31517721Speter		if (vers->entdata->options &&
31617721Speter		    strcmp (vers->entdata->options, vers->options) != 0)
31717721Speter		    ret = T_CHECKOUT;
31817721Speter		else
31917721Speter		{
32032785Speter		    sticky_ck (finfo, aflag, vers);
32117721Speter		    ret = T_UPTODATE;
32217721Speter		}
32317721Speter	    }
32417721Speter	    else
32517721Speter	    {
32617721Speter
32717721Speter		/*
32817721Speter		 * The user file appears to have been modified, but we call
32917721Speter		 * No_Difference to verify that it really has been modified
33017721Speter		 */
33125839Speter		if (No_Difference (finfo, vers))
33217721Speter		{
33317721Speter
33417721Speter		    /*
33517721Speter		     * they really are different; modified if we aren't
33617721Speter		     * changing any sticky -k options, else needs merge
33717721Speter		     */
33817721Speter#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED
33917721Speter		    if (strcmp (vers->entdata->options ?
34017721Speter			   vers->entdata->options : "", vers->options) == 0)
34117721Speter			ret = T_MODIFIED;
34217721Speter		    else
34317721Speter			ret = T_NEEDS_MERGE;
34417721Speter#else
34517721Speter		    ret = T_MODIFIED;
34632785Speter		    sticky_ck (finfo, aflag, vers);
34717721Speter#endif
34817721Speter		}
34917721Speter		else
35017721Speter		{
35117721Speter		    /* file has not changed; check out if -k changed */
35217721Speter		    if (strcmp (vers->entdata->options ?
35317721Speter			   vers->entdata->options : "", vers->options) != 0)
35417721Speter		    {
35517721Speter			ret = T_CHECKOUT;
35617721Speter		    }
35717721Speter		    else
35817721Speter		    {
35917721Speter
36017721Speter			/*
36117721Speter			 * else -> note that No_Difference will Register the
36217721Speter			 * file already for us, using the new tag/date. This
36317721Speter			 * is the desired behaviour
36417721Speter			 */
36517721Speter			ret = T_UPTODATE;
36617721Speter		    }
36717721Speter		}
36817721Speter	    }
36917721Speter	}
37017721Speter	else
37117721Speter	{
37217721Speter	    /* The RCS file is a newer version than the user file */
37317721Speter
37417721Speter	    if (vers->ts_user == NULL)
37517721Speter	    {
37617721Speter		/* There is no user file, so just get it */
37717721Speter
37844852Speter		/* See comment at other "update" compare, for more
37944852Speter		   thoughts on this comparison.  */
38017721Speter		if (strcmp (command_name, "update") == 0)
38117721Speter		    if (!really_quiet)
38225839Speter			error (0, 0, "warning: %s was lost", finfo->fullname);
38317721Speter		ret = T_CHECKOUT;
38417721Speter	    }
38517721Speter	    else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
38617721Speter	    {
38717721Speter
38817721Speter		/*
38917721Speter		 * The user file is still unmodified, so just get it as well
39017721Speter		 */
39117721Speter#ifdef SERVER_SUPPORT
39234461Speter		if (strcmp (vers->entdata->options ?
39317721Speter			    vers->entdata->options : "", vers->options) != 0
39417721Speter		    || (vers->srcfile != NULL
39517721Speter			&& (vers->srcfile->flags & INATTIC) != 0))
39617721Speter		    ret = T_CHECKOUT;
39717721Speter		else
39817721Speter		    ret = T_PATCH;
39917721Speter#else
40017721Speter		ret = T_CHECKOUT;
40117721Speter#endif
40217721Speter	    }
40317721Speter	    else
40417721Speter	    {
40525839Speter		if (No_Difference (finfo, vers))
40617721Speter		    /* really modified, needs to merge */
40717721Speter		    ret = T_NEEDS_MERGE;
40817721Speter#ifdef SERVER_SUPPORT
40917721Speter	        else if ((strcmp (vers->entdata->options ?
41017721Speter				  vers->entdata->options : "", vers->options)
41117721Speter			  != 0)
41217721Speter			 || (vers->srcfile != NULL
41317721Speter			     && (vers->srcfile->flags & INATTIC) != 0))
41417721Speter		    /* not really modified, check it out */
41517721Speter		    ret = T_CHECKOUT;
41617721Speter		else
41717721Speter		    ret = T_PATCH;
41817721Speter#else
41917721Speter		else
42017721Speter		    /* not really modified, check it out */
42117721Speter		    ret = T_CHECKOUT;
42217721Speter#endif
42317721Speter	    }
42417721Speter	}
42517721Speter    }
42617721Speter
42717721Speter    /* free up the vers struct, or just return it */
42817721Speter    if (versp != (Vers_TS **) NULL)
42917721Speter	*versp = vers;
43017721Speter    else
43117721Speter	freevers_ts (&vers);
43217721Speter
43317721Speter    /* return the status of the file */
43417721Speter    return (ret);
43517721Speter}
43617721Speter
43717721Speterstatic void
43832785Spetersticky_ck (finfo, aflag, vers)
43932785Speter    struct file_info *finfo;
44017721Speter    int aflag;
44117721Speter    Vers_TS *vers;
44217721Speter{
44317721Speter    if (aflag || vers->tag || vers->date)
44417721Speter    {
44517721Speter	char *enttag = vers->entdata->tag;
44617721Speter	char *entdate = vers->entdata->date;
44717721Speter
44817721Speter	if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
44917721Speter	    ((enttag && !vers->tag) || (!enttag && vers->tag)) ||
45017721Speter	    (entdate && vers->date && strcmp (entdate, vers->date)) ||
45117721Speter	    ((entdate && !vers->date) || (!entdate && vers->date)))
45217721Speter	{
45332785Speter	    Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs,
45417721Speter		      vers->options, vers->tag, vers->date, vers->ts_conflict);
45517721Speter
45617721Speter#ifdef SERVER_SUPPORT
45717721Speter	    if (server_active)
45817721Speter	    {
45917721Speter		/* We need to update the entries line on the client side.
46017721Speter		   It is possible we will later update it again via
46117721Speter		   server_updated or some such, but that is OK.  */
46217721Speter		server_update_entries
46332785Speter		  (finfo->file, finfo->update_dir, finfo->repository,
46417721Speter		   strcmp (vers->ts_rcs, vers->ts_user) == 0 ?
46517721Speter		   SERVER_UPDATED : SERVER_MERGED);
46617721Speter	    }
46717721Speter#endif
46817721Speter	}
46917721Speter    }
47017721Speter}
471