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