classify.c revision 34461
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		 */
29117721Speter		if (strcmp (command_name, "update") == 0)
29217721Speter		    if (!really_quiet)
29325839Speter			error (0, 0, "warning: %s was lost", finfo->fullname);
29417721Speter		ret = T_CHECKOUT;
29517721Speter	    }
29617721Speter	    else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
29717721Speter	    {
29817721Speter
29917721Speter		/*
30017721Speter		 * The user file is still unmodified, so nothing special at
30117721Speter		 * all to do -- no lists updated, unless the sticky -k option
30217721Speter		 * has changed.  If the sticky tag has changed, we just need
30317721Speter		 * to re-register the entry
30417721Speter		 */
30534461Speter		/* TODO: decide whether we need to check file permissions
30634461Speter		   for a mismatch, and return T_CONFLICT if so. */
30717721Speter		if (vers->entdata->options &&
30817721Speter		    strcmp (vers->entdata->options, vers->options) != 0)
30917721Speter		    ret = T_CHECKOUT;
31017721Speter		else
31117721Speter		{
31232785Speter		    sticky_ck (finfo, aflag, vers);
31317721Speter		    ret = T_UPTODATE;
31417721Speter		}
31517721Speter	    }
31617721Speter	    else
31717721Speter	    {
31817721Speter
31917721Speter		/*
32017721Speter		 * The user file appears to have been modified, but we call
32117721Speter		 * No_Difference to verify that it really has been modified
32217721Speter		 */
32325839Speter		if (No_Difference (finfo, vers))
32417721Speter		{
32517721Speter
32617721Speter		    /*
32717721Speter		     * they really are different; modified if we aren't
32817721Speter		     * changing any sticky -k options, else needs merge
32917721Speter		     */
33017721Speter#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED
33117721Speter		    if (strcmp (vers->entdata->options ?
33217721Speter			   vers->entdata->options : "", vers->options) == 0)
33317721Speter			ret = T_MODIFIED;
33417721Speter		    else
33517721Speter			ret = T_NEEDS_MERGE;
33617721Speter#else
33717721Speter		    ret = T_MODIFIED;
33832785Speter		    sticky_ck (finfo, aflag, vers);
33917721Speter#endif
34017721Speter		}
34117721Speter		else
34217721Speter		{
34317721Speter		    /* file has not changed; check out if -k changed */
34417721Speter		    if (strcmp (vers->entdata->options ?
34517721Speter			   vers->entdata->options : "", vers->options) != 0)
34617721Speter		    {
34717721Speter			ret = T_CHECKOUT;
34817721Speter		    }
34917721Speter		    else
35017721Speter		    {
35117721Speter
35217721Speter			/*
35317721Speter			 * else -> note that No_Difference will Register the
35417721Speter			 * file already for us, using the new tag/date. This
35517721Speter			 * is the desired behaviour
35617721Speter			 */
35717721Speter			ret = T_UPTODATE;
35817721Speter		    }
35917721Speter		}
36017721Speter	    }
36117721Speter	}
36217721Speter	else
36317721Speter	{
36417721Speter	    /* The RCS file is a newer version than the user file */
36517721Speter
36617721Speter	    if (vers->ts_user == NULL)
36717721Speter	    {
36817721Speter		/* There is no user file, so just get it */
36917721Speter
37017721Speter		if (strcmp (command_name, "update") == 0)
37117721Speter		    if (!really_quiet)
37225839Speter			error (0, 0, "warning: %s was lost", finfo->fullname);
37317721Speter		ret = T_CHECKOUT;
37417721Speter	    }
37517721Speter	    else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
37617721Speter	    {
37717721Speter
37817721Speter		/*
37917721Speter		 * The user file is still unmodified, so just get it as well
38017721Speter		 */
38117721Speter#ifdef SERVER_SUPPORT
38234461Speter		if (strcmp (vers->entdata->options ?
38317721Speter			    vers->entdata->options : "", vers->options) != 0
38417721Speter		    || (vers->srcfile != NULL
38517721Speter			&& (vers->srcfile->flags & INATTIC) != 0))
38617721Speter		    ret = T_CHECKOUT;
38717721Speter		else
38817721Speter		    ret = T_PATCH;
38917721Speter#else
39017721Speter		ret = T_CHECKOUT;
39117721Speter#endif
39217721Speter	    }
39317721Speter	    else
39417721Speter	    {
39525839Speter		if (No_Difference (finfo, vers))
39617721Speter		    /* really modified, needs to merge */
39717721Speter		    ret = T_NEEDS_MERGE;
39817721Speter#ifdef SERVER_SUPPORT
39917721Speter	        else if ((strcmp (vers->entdata->options ?
40017721Speter				  vers->entdata->options : "", vers->options)
40117721Speter			  != 0)
40217721Speter			 || (vers->srcfile != NULL
40317721Speter			     && (vers->srcfile->flags & INATTIC) != 0))
40417721Speter		    /* not really modified, check it out */
40517721Speter		    ret = T_CHECKOUT;
40617721Speter		else
40717721Speter		    ret = T_PATCH;
40817721Speter#else
40917721Speter		else
41017721Speter		    /* not really modified, check it out */
41117721Speter		    ret = T_CHECKOUT;
41217721Speter#endif
41317721Speter	    }
41417721Speter	}
41517721Speter    }
41617721Speter
41717721Speter    /* free up the vers struct, or just return it */
41817721Speter    if (versp != (Vers_TS **) NULL)
41917721Speter	*versp = vers;
42017721Speter    else
42117721Speter	freevers_ts (&vers);
42217721Speter
42317721Speter    /* return the status of the file */
42417721Speter    return (ret);
42517721Speter}
42617721Speter
42717721Speterstatic void
42832785Spetersticky_ck (finfo, aflag, vers)
42932785Speter    struct file_info *finfo;
43017721Speter    int aflag;
43117721Speter    Vers_TS *vers;
43217721Speter{
43317721Speter    if (aflag || vers->tag || vers->date)
43417721Speter    {
43517721Speter	char *enttag = vers->entdata->tag;
43617721Speter	char *entdate = vers->entdata->date;
43717721Speter
43817721Speter	if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
43917721Speter	    ((enttag && !vers->tag) || (!enttag && vers->tag)) ||
44017721Speter	    (entdate && vers->date && strcmp (entdate, vers->date)) ||
44117721Speter	    ((entdate && !vers->date) || (!entdate && vers->date)))
44217721Speter	{
44332785Speter	    Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs,
44417721Speter		      vers->options, vers->tag, vers->date, vers->ts_conflict);
44517721Speter
44617721Speter#ifdef SERVER_SUPPORT
44717721Speter	    if (server_active)
44817721Speter	    {
44917721Speter		/* We need to update the entries line on the client side.
45017721Speter		   It is possible we will later update it again via
45117721Speter		   server_updated or some such, but that is OK.  */
45217721Speter		server_update_entries
45332785Speter		  (finfo->file, finfo->update_dir, finfo->repository,
45417721Speter		   strcmp (vers->ts_rcs, vers->ts_user) == 0 ?
45517721Speter		   SERVER_UPDATED : SERVER_MERGED);
45617721Speter	    }
45717721Speter#endif
45817721Speter	}
45917721Speter    }
46017721Speter}
461