11e72d8d2Sderaadt /*
21e72d8d2Sderaadt * Copyright (c) 1992, Brian Berliner and Jeff Polk
31e72d8d2Sderaadt * Copyright (c) 1989-1992, Brian Berliner
41e72d8d2Sderaadt *
51e72d8d2Sderaadt * You may distribute under the terms of the GNU General Public License as
62286d8edStholo * specified in the README file that comes with the CVS source distribution.
71e72d8d2Sderaadt *
81e72d8d2Sderaadt */
91e72d8d2Sderaadt
101e72d8d2Sderaadt #include "cvs.h"
111e72d8d2Sderaadt
122286d8edStholo static void sticky_ck PROTO ((struct file_info *finfo, int aflag,
132286d8edStholo Vers_TS * vers));
141e72d8d2Sderaadt
151e72d8d2Sderaadt /*
161e72d8d2Sderaadt * Classify the state of a file
171e72d8d2Sderaadt */
181e72d8d2Sderaadt Ctype
Classify_File(finfo,tag,date,options,force_tag_match,aflag,versp,pipeout)1950bf276cStholo Classify_File (finfo, tag, date, options, force_tag_match, aflag, versp,
2050bf276cStholo pipeout)
2150bf276cStholo struct file_info *finfo;
221e72d8d2Sderaadt char *tag;
231e72d8d2Sderaadt char *date;
242286d8edStholo
252286d8edStholo /* Keyword expansion options. Can be either NULL or "" to
262286d8edStholo indicate none are specified here. */
271e72d8d2Sderaadt char *options;
282286d8edStholo
291e72d8d2Sderaadt int force_tag_match;
301e72d8d2Sderaadt int aflag;
311e72d8d2Sderaadt Vers_TS **versp;
321e72d8d2Sderaadt int pipeout;
331e72d8d2Sderaadt {
341e72d8d2Sderaadt Vers_TS *vers;
351e72d8d2Sderaadt Ctype ret;
361e72d8d2Sderaadt
371e72d8d2Sderaadt /* get all kinds of good data about the file */
3850bf276cStholo vers = Version_TS (finfo, options, tag, date,
3950bf276cStholo force_tag_match, 0);
401e72d8d2Sderaadt
411e72d8d2Sderaadt if (vers->vn_user == NULL)
421e72d8d2Sderaadt {
431e72d8d2Sderaadt /* No entry available, ts_rcs is invalid */
441e72d8d2Sderaadt if (vers->vn_rcs == NULL)
451e72d8d2Sderaadt {
461e72d8d2Sderaadt /* there is no RCS file either */
471e72d8d2Sderaadt if (vers->ts_user == NULL)
481e72d8d2Sderaadt {
491e72d8d2Sderaadt /* there is no user file */
50b78423f6Stholo /* FIXME: Why do we skip this message if vers->tag or
51b78423f6Stholo vers->date is set? It causes "cvs update -r tag98 foo"
52b78423f6Stholo to silently do nothing, which is seriously confusing
53b78423f6Stholo behavior. "cvs update foo" gives this message, which
54b78423f6Stholo is what I would expect. */
551e72d8d2Sderaadt if (!force_tag_match || !(vers->tag || vers->date))
561e72d8d2Sderaadt if (!really_quiet)
5750bf276cStholo error (0, 0, "nothing known about %s", finfo->fullname);
581e72d8d2Sderaadt ret = T_UNKNOWN;
591e72d8d2Sderaadt }
601e72d8d2Sderaadt else
611e72d8d2Sderaadt {
621e72d8d2Sderaadt /* there is a user file */
63b78423f6Stholo /* FIXME: Why do we skip this message if vers->tag or
64b78423f6Stholo vers->date is set? It causes "cvs update -r tag98 foo"
65b78423f6Stholo to silently do nothing, which is seriously confusing
66b78423f6Stholo behavior. "cvs update foo" gives this message, which
67b78423f6Stholo is what I would expect. */
681e72d8d2Sderaadt if (!force_tag_match || !(vers->tag || vers->date))
691e72d8d2Sderaadt if (!really_quiet)
702286d8edStholo error (0, 0, "use `%s add' to create an entry for %s",
712286d8edStholo program_name, finfo->fullname);
721e72d8d2Sderaadt ret = T_UNKNOWN;
731e72d8d2Sderaadt }
741e72d8d2Sderaadt }
751e72d8d2Sderaadt else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
761e72d8d2Sderaadt {
77*43c1707eStholo /* there is an RCS file, but it's dead */
781e72d8d2Sderaadt if (vers->ts_user == NULL)
7950bf276cStholo ret = T_UPTODATE;
801e72d8d2Sderaadt else
811e72d8d2Sderaadt {
822286d8edStholo error (0, 0, "use `%s add' to create an entry for %s",
832286d8edStholo program_name, finfo->fullname);
841e72d8d2Sderaadt ret = T_UNKNOWN;
851e72d8d2Sderaadt }
861e72d8d2Sderaadt }
87*43c1707eStholo else if (!pipeout && vers->ts_user && No_Difference (finfo, vers))
881e72d8d2Sderaadt {
891e72d8d2Sderaadt /* the files were different so it is a conflict */
901e72d8d2Sderaadt if (!really_quiet)
911e72d8d2Sderaadt error (0, 0, "move away %s; it is in the way",
9250bf276cStholo finfo->fullname);
931e72d8d2Sderaadt ret = T_CONFLICT;
941e72d8d2Sderaadt }
951e72d8d2Sderaadt else
96*43c1707eStholo /* no user file or no difference, just checkout */
971e72d8d2Sderaadt ret = T_CHECKOUT;
981e72d8d2Sderaadt }
991e72d8d2Sderaadt else if (strcmp (vers->vn_user, "0") == 0)
1001e72d8d2Sderaadt {
1011e72d8d2Sderaadt /* An entry for a new-born file; ts_rcs is dummy */
1021e72d8d2Sderaadt
1031e72d8d2Sderaadt if (vers->ts_user == NULL)
1041e72d8d2Sderaadt {
1051e72d8d2Sderaadt /*
1061e72d8d2Sderaadt * There is no user file, but there should be one; remove the
1071e72d8d2Sderaadt * entry
1081e72d8d2Sderaadt */
1091e72d8d2Sderaadt if (!really_quiet)
11050bf276cStholo error (0, 0, "warning: new-born %s has disappeared", finfo->fullname);
1111e72d8d2Sderaadt ret = T_REMOVE_ENTRY;
1121e72d8d2Sderaadt }
113*43c1707eStholo else if (vers->vn_rcs == NULL ||
114*43c1707eStholo RCS_isdead (vers->srcfile, vers->vn_rcs))
115*43c1707eStholo /* No RCS file or RCS file revision is dead */
1161e72d8d2Sderaadt ret = T_ADDED;
1171e72d8d2Sderaadt else
1181e72d8d2Sderaadt {
1191e72d8d2Sderaadt if (vers->srcfile->flags & INATTIC
1201e72d8d2Sderaadt && vers->srcfile->flags & VALID)
1211e72d8d2Sderaadt {
1221e72d8d2Sderaadt /* This file has been added on some branch other than
1231e72d8d2Sderaadt the one we are looking at. In the branch we are
1241e72d8d2Sderaadt looking at, the file was already valid. */
1251e72d8d2Sderaadt if (!really_quiet)
1261e72d8d2Sderaadt error (0, 0,
127*43c1707eStholo "conflict: %s has been added, but already exists",
12850bf276cStholo finfo->fullname);
1291e72d8d2Sderaadt }
1301e72d8d2Sderaadt else
1311e72d8d2Sderaadt {
1321e72d8d2Sderaadt /*
1331e72d8d2Sderaadt * There is an RCS file, so someone else must have checked
1341e72d8d2Sderaadt * one in behind our back; conflict
1351e72d8d2Sderaadt */
1361e72d8d2Sderaadt if (!really_quiet)
1371e72d8d2Sderaadt error (0, 0,
138*43c1707eStholo "conflict: %s created independently by second party",
13950bf276cStholo finfo->fullname);
1401e72d8d2Sderaadt }
1411e72d8d2Sderaadt ret = T_CONFLICT;
1421e72d8d2Sderaadt }
1431e72d8d2Sderaadt }
1441e72d8d2Sderaadt else if (vers->vn_user[0] == '-')
1451e72d8d2Sderaadt {
1461e72d8d2Sderaadt /* An entry for a removed file, ts_rcs is invalid */
1471e72d8d2Sderaadt
1481e72d8d2Sderaadt if (vers->ts_user == NULL)
1491e72d8d2Sderaadt {
1501e72d8d2Sderaadt /* There is no user file (as it should be) */
1511e72d8d2Sderaadt
15250bf276cStholo if (vers->vn_rcs == NULL
15350bf276cStholo || RCS_isdead (vers->srcfile, vers->vn_rcs))
1541e72d8d2Sderaadt {
1551e72d8d2Sderaadt
1561e72d8d2Sderaadt /*
1571e72d8d2Sderaadt * There is no RCS file; this is all-right, but it has been
1581e72d8d2Sderaadt * removed independently by a second party; remove the entry
1591e72d8d2Sderaadt */
1601e72d8d2Sderaadt ret = T_REMOVE_ENTRY;
1611e72d8d2Sderaadt }
162*43c1707eStholo else if (strcmp (vers->vn_rcs, vers->vn_user + 1) == 0)
1631e72d8d2Sderaadt /*
1641e72d8d2Sderaadt * The RCS file is the same version as the user file was, and
1651e72d8d2Sderaadt * that's OK; remove it
1661e72d8d2Sderaadt */
1671e72d8d2Sderaadt ret = T_REMOVED;
168*43c1707eStholo else if (pipeout)
169*43c1707eStholo /*
170*43c1707eStholo * The RCS file doesn't match the user's file, but it doesn't
171*43c1707eStholo * matter in this case
172*43c1707eStholo */
173*43c1707eStholo ret = T_NEEDS_MERGE;
1741e72d8d2Sderaadt else
1751e72d8d2Sderaadt {
1761e72d8d2Sderaadt
1771e72d8d2Sderaadt /*
1781e72d8d2Sderaadt * The RCS file is a newer version than the removed user file
1791e72d8d2Sderaadt * and this is definitely not OK; make it a conflict.
1801e72d8d2Sderaadt */
1811e72d8d2Sderaadt if (!really_quiet)
1821e72d8d2Sderaadt error (0, 0,
1831e72d8d2Sderaadt "conflict: removed %s was modified by second party",
18450bf276cStholo finfo->fullname);
1851e72d8d2Sderaadt ret = T_CONFLICT;
1861e72d8d2Sderaadt }
1871e72d8d2Sderaadt }
1881e72d8d2Sderaadt else
1891e72d8d2Sderaadt {
1901e72d8d2Sderaadt /* The user file shouldn't be there */
1911e72d8d2Sderaadt if (!really_quiet)
1921e72d8d2Sderaadt error (0, 0, "%s should be removed and is still there",
19350bf276cStholo finfo->fullname);
1941e72d8d2Sderaadt ret = T_REMOVED;
1951e72d8d2Sderaadt }
1961e72d8d2Sderaadt }
1971e72d8d2Sderaadt else
1981e72d8d2Sderaadt {
1991e72d8d2Sderaadt /* A normal entry, TS_Rcs is valid */
200*43c1707eStholo if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs))
2011e72d8d2Sderaadt {
2021e72d8d2Sderaadt /* There is no RCS file */
2031e72d8d2Sderaadt
2041e72d8d2Sderaadt if (vers->ts_user == NULL)
2051e72d8d2Sderaadt {
2061e72d8d2Sderaadt /* There is no user file, so just remove the entry */
2071e72d8d2Sderaadt if (!really_quiet)
2081e72d8d2Sderaadt error (0, 0, "warning: %s is not (any longer) pertinent",
20950bf276cStholo finfo->fullname);
2101e72d8d2Sderaadt ret = T_REMOVE_ENTRY;
2111e72d8d2Sderaadt }
2121e72d8d2Sderaadt else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
2131e72d8d2Sderaadt {
2141e72d8d2Sderaadt
2151e72d8d2Sderaadt /*
2161e72d8d2Sderaadt * The user file is still unmodified, so just remove it from
2171e72d8d2Sderaadt * the entry list
2181e72d8d2Sderaadt */
2191e72d8d2Sderaadt if (!really_quiet)
2201e72d8d2Sderaadt error (0, 0, "%s is no longer in the repository",
22150bf276cStholo finfo->fullname);
2221e72d8d2Sderaadt ret = T_REMOVE_ENTRY;
2231e72d8d2Sderaadt }
224*43c1707eStholo else if (No_Difference (finfo, vers))
2251e72d8d2Sderaadt {
2261e72d8d2Sderaadt /* they are different -> conflict */
2271e72d8d2Sderaadt if (!really_quiet)
2281e72d8d2Sderaadt error (0, 0,
2291e72d8d2Sderaadt "conflict: %s is modified but no longer in the repository",
23050bf276cStholo finfo->fullname);
2311e72d8d2Sderaadt ret = T_CONFLICT;
2321e72d8d2Sderaadt }
2331e72d8d2Sderaadt else
2341e72d8d2Sderaadt {
2351e72d8d2Sderaadt /* they weren't really different */
2361e72d8d2Sderaadt if (!really_quiet)
2371e72d8d2Sderaadt error (0, 0,
2381e72d8d2Sderaadt "warning: %s is not (any longer) pertinent",
23950bf276cStholo finfo->fullname);
2401e72d8d2Sderaadt ret = T_REMOVE_ENTRY;
2411e72d8d2Sderaadt }
2421e72d8d2Sderaadt }
2431e72d8d2Sderaadt else if (strcmp (vers->vn_rcs, vers->vn_user) == 0)
2441e72d8d2Sderaadt {
2451e72d8d2Sderaadt /* The RCS file is the same version as the user file */
2461e72d8d2Sderaadt
2471e72d8d2Sderaadt if (vers->ts_user == NULL)
2481e72d8d2Sderaadt {
2491e72d8d2Sderaadt
2501e72d8d2Sderaadt /*
2511e72d8d2Sderaadt * There is no user file, so note that it was lost and
2521e72d8d2Sderaadt * extract a new version
2531e72d8d2Sderaadt */
254b6f6614eStholo /* Comparing the command_name against "update", in
255b6f6614eStholo addition to being an ugly way to operate, means
256b6f6614eStholo that this message does not get printed by the
257b6f6614eStholo server. That might be considered just a straight
258b6f6614eStholo bug, although there is one subtlety: that case also
259b6f6614eStholo gets hit when a patch fails and the client fetches
260b6f6614eStholo a file. I'm not sure there is currently any way
261b6f6614eStholo for the server to distinguish those two cases. */
2621e72d8d2Sderaadt if (strcmp (command_name, "update") == 0)
2631e72d8d2Sderaadt if (!really_quiet)
26450bf276cStholo error (0, 0, "warning: %s was lost", finfo->fullname);
2651e72d8d2Sderaadt ret = T_CHECKOUT;
2661e72d8d2Sderaadt }
2671e72d8d2Sderaadt else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
2681e72d8d2Sderaadt {
2691e72d8d2Sderaadt
2701e72d8d2Sderaadt /*
2711e72d8d2Sderaadt * The user file is still unmodified, so nothing special at
2721e72d8d2Sderaadt * all to do -- no lists updated, unless the sticky -k option
2731e72d8d2Sderaadt * has changed. If the sticky tag has changed, we just need
2741e72d8d2Sderaadt * to re-register the entry
2751e72d8d2Sderaadt */
2765e617892Stholo /* TODO: decide whether we need to check file permissions
2775e617892Stholo for a mismatch, and return T_CONFLICT if so. */
2781e72d8d2Sderaadt if (vers->entdata->options &&
2791e72d8d2Sderaadt strcmp (vers->entdata->options, vers->options) != 0)
2801e72d8d2Sderaadt ret = T_CHECKOUT;
2811e72d8d2Sderaadt else
2821e72d8d2Sderaadt {
2832286d8edStholo sticky_ck (finfo, aflag, vers);
2841e72d8d2Sderaadt ret = T_UPTODATE;
2851e72d8d2Sderaadt }
2861e72d8d2Sderaadt }
287*43c1707eStholo else if (No_Difference (finfo, vers))
2881e72d8d2Sderaadt {
2891e72d8d2Sderaadt
2901e72d8d2Sderaadt /*
2911e72d8d2Sderaadt * they really are different; modified if we aren't
2921e72d8d2Sderaadt * changing any sticky -k options, else needs merge
2931e72d8d2Sderaadt */
2941e72d8d2Sderaadt #ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED
2951e72d8d2Sderaadt if (strcmp (vers->entdata->options ?
2961e72d8d2Sderaadt vers->entdata->options : "", vers->options) == 0)
2971e72d8d2Sderaadt ret = T_MODIFIED;
2981e72d8d2Sderaadt else
2991e72d8d2Sderaadt ret = T_NEEDS_MERGE;
3001e72d8d2Sderaadt #else
3011e72d8d2Sderaadt ret = T_MODIFIED;
3022286d8edStholo sticky_ck (finfo, aflag, vers);
3031e72d8d2Sderaadt #endif
3041e72d8d2Sderaadt }
305*43c1707eStholo else if (strcmp (vers->entdata->options ?
3061e72d8d2Sderaadt vers->entdata->options : "", vers->options) != 0)
3071e72d8d2Sderaadt {
308*43c1707eStholo /* file has not changed; check out if -k changed */
3091e72d8d2Sderaadt ret = T_CHECKOUT;
3101e72d8d2Sderaadt }
3111e72d8d2Sderaadt else
3121e72d8d2Sderaadt {
3131e72d8d2Sderaadt
3141e72d8d2Sderaadt /*
3151e72d8d2Sderaadt * else -> note that No_Difference will Register the
3161e72d8d2Sderaadt * file already for us, using the new tag/date. This
3171e72d8d2Sderaadt * is the desired behaviour
3181e72d8d2Sderaadt */
3191e72d8d2Sderaadt ret = T_UPTODATE;
3201e72d8d2Sderaadt }
3211e72d8d2Sderaadt }
3221e72d8d2Sderaadt else
3231e72d8d2Sderaadt {
3241e72d8d2Sderaadt /* The RCS file is a newer version than the user file */
3251e72d8d2Sderaadt
3261e72d8d2Sderaadt if (vers->ts_user == NULL)
3271e72d8d2Sderaadt {
3281e72d8d2Sderaadt /* There is no user file, so just get it */
3291e72d8d2Sderaadt
330b6f6614eStholo /* See comment at other "update" compare, for more
331b6f6614eStholo thoughts on this comparison. */
3321e72d8d2Sderaadt if (strcmp (command_name, "update") == 0)
3331e72d8d2Sderaadt if (!really_quiet)
33450bf276cStholo error (0, 0, "warning: %s was lost", finfo->fullname);
3351e72d8d2Sderaadt ret = T_CHECKOUT;
3361e72d8d2Sderaadt }
3371e72d8d2Sderaadt else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
3381e72d8d2Sderaadt {
3391e72d8d2Sderaadt
3401e72d8d2Sderaadt /*
3411e72d8d2Sderaadt * The user file is still unmodified, so just get it as well
3421e72d8d2Sderaadt */
3431e72d8d2Sderaadt if (strcmp (vers->entdata->options ?
3441e72d8d2Sderaadt vers->entdata->options : "", vers->options) != 0
3451e72d8d2Sderaadt || (vers->srcfile != NULL
3461e72d8d2Sderaadt && (vers->srcfile->flags & INATTIC) != 0))
3471e72d8d2Sderaadt ret = T_CHECKOUT;
3481e72d8d2Sderaadt else
3491e72d8d2Sderaadt ret = T_PATCH;
3501e72d8d2Sderaadt }
351*43c1707eStholo else if (No_Difference (finfo, vers))
3521e72d8d2Sderaadt /* really modified, needs to merge */
3531e72d8d2Sderaadt ret = T_NEEDS_MERGE;
3541e72d8d2Sderaadt else if ((strcmp (vers->entdata->options ?
3551e72d8d2Sderaadt vers->entdata->options : "", vers->options)
3561e72d8d2Sderaadt != 0)
3571e72d8d2Sderaadt || (vers->srcfile != NULL
3581e72d8d2Sderaadt && (vers->srcfile->flags & INATTIC) != 0))
3591e72d8d2Sderaadt /* not really modified, check it out */
3601e72d8d2Sderaadt ret = T_CHECKOUT;
3611e72d8d2Sderaadt else
3621e72d8d2Sderaadt ret = T_PATCH;
3631e72d8d2Sderaadt }
3641e72d8d2Sderaadt }
3651e72d8d2Sderaadt
3661e72d8d2Sderaadt /* free up the vers struct, or just return it */
3671e72d8d2Sderaadt if (versp != (Vers_TS **) NULL)
3681e72d8d2Sderaadt *versp = vers;
3691e72d8d2Sderaadt else
3701e72d8d2Sderaadt freevers_ts (&vers);
3711e72d8d2Sderaadt
3721e72d8d2Sderaadt /* return the status of the file */
3731e72d8d2Sderaadt return (ret);
3741e72d8d2Sderaadt }
3751e72d8d2Sderaadt
3761e72d8d2Sderaadt static void
sticky_ck(finfo,aflag,vers)3772286d8edStholo sticky_ck (finfo, aflag, vers)
3782286d8edStholo struct file_info *finfo;
3791e72d8d2Sderaadt int aflag;
3801e72d8d2Sderaadt Vers_TS *vers;
3811e72d8d2Sderaadt {
3821e72d8d2Sderaadt if (aflag || vers->tag || vers->date)
3831e72d8d2Sderaadt {
3841e72d8d2Sderaadt char *enttag = vers->entdata->tag;
3851e72d8d2Sderaadt char *entdate = vers->entdata->date;
3861e72d8d2Sderaadt
3871e72d8d2Sderaadt if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
3881e72d8d2Sderaadt ((enttag && !vers->tag) || (!enttag && vers->tag)) ||
3891e72d8d2Sderaadt (entdate && vers->date && strcmp (entdate, vers->date)) ||
3901e72d8d2Sderaadt ((entdate && !vers->date) || (!entdate && vers->date)))
3911e72d8d2Sderaadt {
3922286d8edStholo Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs,
3931e72d8d2Sderaadt vers->options, vers->tag, vers->date, vers->ts_conflict);
3941e72d8d2Sderaadt
3951e72d8d2Sderaadt #ifdef SERVER_SUPPORT
3961e72d8d2Sderaadt if (server_active)
3971e72d8d2Sderaadt {
3981e72d8d2Sderaadt /* We need to update the entries line on the client side.
3991e72d8d2Sderaadt It is possible we will later update it again via
4001e72d8d2Sderaadt server_updated or some such, but that is OK. */
4011e72d8d2Sderaadt server_update_entries
4022286d8edStholo (finfo->file, finfo->update_dir, finfo->repository,
4031e72d8d2Sderaadt strcmp (vers->ts_rcs, vers->ts_user) == 0 ?
4041e72d8d2Sderaadt SERVER_UPDATED : SERVER_MERGED);
4051e72d8d2Sderaadt }
4061e72d8d2Sderaadt #endif
4071e72d8d2Sderaadt }
4081e72d8d2Sderaadt }
4091e72d8d2Sderaadt }
410