classify.c revision 179561
117721Speter/* 2175261Sobrien * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 3175261Sobrien * 4175261Sobrien * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5175261Sobrien * and others. 6175261Sobrien * 7175261Sobrien * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8175261Sobrien * Portions Copyright (C) 1989-1992, Brian Berliner 917721Speter * 1017721Speter * You may distribute under the terms of the GNU General Public License as 1132785Speter * specified in the README file that comes with the CVS source distribution. 1217721Speter * 1317721Speter */ 1417721Speter 1517721Speter#include "cvs.h" 1617721Speter 1732785Speterstatic void sticky_ck PROTO ((struct file_info *finfo, int aflag, 1832785Speter Vers_TS * vers)); 1917721Speter 20177391Sobrien 21177391Sobrien 22177391Sobrienstatic inline int keywords_may_change PROTO ((int aflag, Vers_TS * vers)); 23177391Sobrienstatic inline int 24177391Sobrienkeywords_may_change (aflag, vers) 25177391Sobrien int aflag; 26177391Sobrien Vers_TS * vers; 27177391Sobrien{ 28177391Sobrien int retval; 29177391Sobrien 30177391Sobrien if (/* Options are different... */ 31177391Sobrien strcmp (vers->entdata->options, vers->options) 32177391Sobrien /* ...or... */ 33177391Sobrien || (/* ...clearing stickies... */ 34177391Sobrien aflag 35177391Sobrien /* ...and... */ 36177391Sobrien && (/* ...there used to be a tag which subs in Name keys... */ 37179561Sobrien (vers->entdata->tag && !isdigit (vers->entdata->tag[0]) 38179561Sobrien && vers->tag && !isdigit (vers->tag[0]) 39179561Sobrien && strcmp (vers->entdata->tag, vers->tag)) 40177391Sobrien /* ...or there used to be a keyword mode which may be 41177391Sobrien * changed by -A... 42177391Sobrien */ 43177391Sobrien || (strlen (vers->entdata->options) 44177391Sobrien && strcmp (vers->entdata->options, "-kkv") 45177391Sobrien && strcmp (vers->entdata->options, "-kb")))) 46177391Sobrien /* ...or... */ 47177391Sobrien || (/* ...this is not commit... */ 48177391Sobrien strcmp (cvs_cmd_name, "commit") 49177391Sobrien /* ...and... */ 50177391Sobrien && (/* ...the tag is changing in a way that affects Name keys... */ 51177391Sobrien (vers->entdata->tag && vers->tag 52177391Sobrien && strcmp (vers->entdata->tag, vers->tag) 53177391Sobrien && !(isdigit (vers->entdata->tag[0]) 54177391Sobrien && isdigit (vers->entdata->tag[0]))) 55177391Sobrien || (!vers->entdata->tag && vers->tag 56177391Sobrien && !isdigit (vers->tag[0]))))) 57177391Sobrien retval = 1; 58177391Sobrien else 59177391Sobrien retval = 0; 60177391Sobrien 61177391Sobrien return retval; 62177391Sobrien} 63177391Sobrien 64177391Sobrien 65177391Sobrien 6617721Speter/* 6717721Speter * Classify the state of a file 6817721Speter */ 6917721SpeterCtype 7025839SpeterClassify_File (finfo, tag, date, options, force_tag_match, aflag, versp, 7125839Speter pipeout) 7225839Speter struct file_info *finfo; 7317721Speter char *tag; 7417721Speter char *date; 7532785Speter 7632785Speter /* Keyword expansion options. Can be either NULL or "" to 7732785Speter indicate none are specified here. */ 7817721Speter char *options; 7932785Speter 8017721Speter int force_tag_match; 8117721Speter int aflag; 8217721Speter Vers_TS **versp; 8317721Speter int pipeout; 8417721Speter{ 8517721Speter Vers_TS *vers; 8617721Speter Ctype ret; 8717721Speter 8817721Speter /* get all kinds of good data about the file */ 8925839Speter vers = Version_TS (finfo, options, tag, date, 9025839Speter force_tag_match, 0); 9117721Speter 9217721Speter if (vers->vn_user == NULL) 9317721Speter { 9417721Speter /* No entry available, ts_rcs is invalid */ 9517721Speter if (vers->vn_rcs == NULL) 9617721Speter { 9717721Speter /* there is no RCS file either */ 9817721Speter if (vers->ts_user == NULL) 9917721Speter { 10017721Speter /* there is no user file */ 10125839Speter /* FIXME: Why do we skip this message if vers->tag or 10225839Speter vers->date is set? It causes "cvs update -r tag98 foo" 10325839Speter to silently do nothing, which is seriously confusing 10425839Speter behavior. "cvs update foo" gives this message, which 10525839Speter is what I would expect. */ 10617721Speter if (!force_tag_match || !(vers->tag || vers->date)) 10717721Speter if (!really_quiet) 10825839Speter error (0, 0, "nothing known about %s", finfo->fullname); 10917721Speter ret = T_UNKNOWN; 11017721Speter } 11117721Speter else 11217721Speter { 11317721Speter /* there is a user file */ 11425839Speter /* FIXME: Why do we skip this message if vers->tag or 11525839Speter vers->date is set? It causes "cvs update -r tag98 foo" 11625839Speter to silently do nothing, which is seriously confusing 11725839Speter behavior. "cvs update foo" gives this message, which 11825839Speter is what I would expect. */ 11917721Speter if (!force_tag_match || !(vers->tag || vers->date)) 12017721Speter if (!really_quiet) 12132785Speter error (0, 0, "use `%s add' to create an entry for %s", 12232785Speter program_name, finfo->fullname); 12317721Speter ret = T_UNKNOWN; 12417721Speter } 12517721Speter } 12617721Speter else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) 12717721Speter { 12881404Speter /* there is an RCS file, but it's dead */ 12917721Speter if (vers->ts_user == NULL) 13025839Speter ret = T_UPTODATE; 13117721Speter else 13217721Speter { 13332785Speter error (0, 0, "use `%s add' to create an entry for %s", 13432785Speter program_name, finfo->fullname); 13517721Speter ret = T_UNKNOWN; 13617721Speter } 13717721Speter } 13881404Speter else if (!pipeout && vers->ts_user && No_Difference (finfo, vers)) 13917721Speter { 14081404Speter /* the files were different so it is a conflict */ 14181404Speter if (!really_quiet) 14281404Speter error (0, 0, "move away %s; it is in the way", 14381404Speter finfo->fullname); 14481404Speter ret = T_CONFLICT; 14517721Speter } 14681404Speter else 14781404Speter /* no user file or no difference, just checkout */ 14881404Speter ret = T_CHECKOUT; 14917721Speter } 15017721Speter else if (strcmp (vers->vn_user, "0") == 0) 15117721Speter { 15217721Speter /* An entry for a new-born file; ts_rcs is dummy */ 15317721Speter 15417721Speter if (vers->ts_user == NULL) 15517721Speter { 156107484Speter if (pipeout) 157107484Speter { 158107484Speter ret = T_CHECKOUT; 159107484Speter } 160107484Speter else 161107484Speter { 162107484Speter /* 163107484Speter * There is no user file, but there should be one; remove the 164107484Speter * entry 165107484Speter */ 166107484Speter if (!really_quiet) 167107484Speter error (0, 0, "warning: new-born %s has disappeared", 168107484Speter finfo->fullname); 169107484Speter ret = T_REMOVE_ENTRY; 170107484Speter } 17117721Speter } 17281404Speter else if (vers->vn_rcs == NULL || 17381404Speter RCS_isdead (vers->srcfile, vers->vn_rcs)) 17481404Speter /* No RCS file or RCS file revision is dead */ 17581404Speter ret = T_ADDED; 17617721Speter else 17717721Speter { 178107484Speter if (pipeout) 17981404Speter { 180107484Speter ret = T_CHECKOUT; 18181404Speter } 18217721Speter else 18317721Speter { 184107484Speter if (vers->srcfile->flags & INATTIC 185107484Speter && vers->srcfile->flags & VALID) 186107484Speter { 187107484Speter /* This file has been added on some branch other than 188107484Speter the one we are looking at. In the branch we are 189107484Speter looking at, the file was already valid. */ 190107484Speter if (!really_quiet) 191107484Speter error (0, 0, 192107484Speter "conflict: %s has been added, but already exists", 193107484Speter finfo->fullname); 194107484Speter } 195107484Speter else 196107484Speter { 197107484Speter /* 198107484Speter * There is an RCS file, so someone else must have checked 199107484Speter * one in behind our back; conflict 200107484Speter */ 201107484Speter if (!really_quiet) 202107484Speter error (0, 0, 20381404Speter "conflict: %s created independently by second party", 204107484Speter finfo->fullname); 205107484Speter } 206107484Speter ret = T_CONFLICT; 20717721Speter } 20817721Speter } 20917721Speter } 21017721Speter else if (vers->vn_user[0] == '-') 21117721Speter { 21217721Speter /* An entry for a removed file, ts_rcs is invalid */ 21317721Speter 21417721Speter if (vers->ts_user == NULL) 21517721Speter { 21617721Speter /* There is no user file (as it should be) */ 21717721Speter 21825839Speter if (vers->vn_rcs == NULL 21925839Speter || RCS_isdead (vers->srcfile, vers->vn_rcs)) 22017721Speter { 22117721Speter 22217721Speter /* 22317721Speter * There is no RCS file; this is all-right, but it has been 22417721Speter * removed independently by a second party; remove the entry 22517721Speter */ 22617721Speter ret = T_REMOVE_ENTRY; 22717721Speter } 22881404Speter else if (strcmp (vers->vn_rcs, vers->vn_user + 1) == 0) 22917721Speter /* 23017721Speter * The RCS file is the same version as the user file was, and 23117721Speter * that's OK; remove it 23217721Speter */ 23317721Speter ret = T_REMOVED; 23481404Speter else if (pipeout) 23581404Speter /* 23681404Speter * The RCS file doesn't match the user's file, but it doesn't 23781404Speter * matter in this case 23881404Speter */ 23981404Speter ret = T_NEEDS_MERGE; 24017721Speter else 24117721Speter { 24217721Speter 24317721Speter /* 24417721Speter * The RCS file is a newer version than the removed user file 24517721Speter * and this is definitely not OK; make it a conflict. 24617721Speter */ 24717721Speter if (!really_quiet) 24817721Speter error (0, 0, 24917721Speter "conflict: removed %s was modified by second party", 25025839Speter finfo->fullname); 25117721Speter ret = T_CONFLICT; 25217721Speter } 25317721Speter } 25417721Speter else 25517721Speter { 25617721Speter /* The user file shouldn't be there */ 25717721Speter if (!really_quiet) 25817721Speter error (0, 0, "%s should be removed and is still there", 25925839Speter finfo->fullname); 26017721Speter ret = T_REMOVED; 26117721Speter } 26217721Speter } 26317721Speter else 26417721Speter { 26517721Speter /* A normal entry, TS_Rcs is valid */ 26681404Speter if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs)) 26717721Speter { 26817721Speter /* There is no RCS file */ 26917721Speter 27017721Speter if (vers->ts_user == NULL) 27117721Speter { 27217721Speter /* There is no user file, so just remove the entry */ 27317721Speter if (!really_quiet) 27417721Speter error (0, 0, "warning: %s is not (any longer) pertinent", 27525839Speter finfo->fullname); 27617721Speter ret = T_REMOVE_ENTRY; 27717721Speter } 27817721Speter else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 27917721Speter { 28017721Speter 28117721Speter /* 28217721Speter * The user file is still unmodified, so just remove it from 28317721Speter * the entry list 28417721Speter */ 28517721Speter if (!really_quiet) 28617721Speter error (0, 0, "%s is no longer in the repository", 28725839Speter finfo->fullname); 28817721Speter ret = T_REMOVE_ENTRY; 28917721Speter } 29081404Speter else if (No_Difference (finfo, vers)) 29117721Speter { 29281404Speter /* they are different -> conflict */ 29381404Speter if (!really_quiet) 29481404Speter error (0, 0, 29517721Speter "conflict: %s is modified but no longer in the repository", 29625839Speter finfo->fullname); 29781404Speter ret = T_CONFLICT; 29817721Speter } 29981404Speter else 30081404Speter { 30181404Speter /* they weren't really different */ 30281404Speter if (!really_quiet) 30381404Speter error (0, 0, 30481404Speter "warning: %s is not (any longer) pertinent", 30581404Speter finfo->fullname); 30681404Speter ret = T_REMOVE_ENTRY; 30781404Speter } 30817721Speter } 30917721Speter else if (strcmp (vers->vn_rcs, vers->vn_user) == 0) 31017721Speter { 31117721Speter /* The RCS file is the same version as the user file */ 31217721Speter 31317721Speter if (vers->ts_user == NULL) 31417721Speter { 31517721Speter 31617721Speter /* 31717721Speter * There is no user file, so note that it was lost and 31817721Speter * extract a new version 31917721Speter */ 320128266Speter /* Comparing the cvs_cmd_name against "update", in 32144852Speter addition to being an ugly way to operate, means 32244852Speter that this message does not get printed by the 32344852Speter server. That might be considered just a straight 32444852Speter bug, although there is one subtlety: that case also 32544852Speter gets hit when a patch fails and the client fetches 32644852Speter a file. I'm not sure there is currently any way 32744852Speter for the server to distinguish those two cases. */ 328128266Speter if (strcmp (cvs_cmd_name, "update") == 0) 32917721Speter if (!really_quiet) 33025839Speter error (0, 0, "warning: %s was lost", finfo->fullname); 33117721Speter ret = T_CHECKOUT; 33217721Speter } 333175261Sobrien else if (!strcmp (vers->ts_user, 334175261Sobrien vers->ts_conflict 335175261Sobrien ? vers->ts_conflict : vers->ts_rcs)) 33617721Speter { 33717721Speter 33817721Speter /* 33917721Speter * The user file is still unmodified, so nothing special at 34017721Speter * all to do -- no lists updated, unless the sticky -k option 34117721Speter * has changed. If the sticky tag has changed, we just need 34217721Speter * to re-register the entry 34317721Speter */ 34434461Speter /* TODO: decide whether we need to check file permissions 34534461Speter for a mismatch, and return T_CONFLICT if so. */ 346177391Sobrien if (keywords_may_change (aflag, vers)) 347177391Sobrien ret = T_PATCH; 348175261Sobrien else if (vers->ts_conflict) 349175261Sobrien ret = T_CONFLICT; 35017721Speter else 35117721Speter { 352177391Sobrien ret = T_UPTODATE; 35332785Speter sticky_ck (finfo, aflag, vers); 35417721Speter } 35517721Speter } 35681404Speter else if (No_Difference (finfo, vers)) 35717721Speter { 35817721Speter 35917721Speter /* 36081404Speter * they really are different; modified if we aren't 36181404Speter * changing any sticky -k options, else needs merge 36217721Speter */ 36317721Speter#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED 36481404Speter if (strcmp (vers->entdata->options ? 36581404Speter vers->entdata->options : "", vers->options) == 0) 36681404Speter ret = T_MODIFIED; 36781404Speter else 36881404Speter ret = T_NEEDS_MERGE; 36917721Speter#else 370175261Sobrien /* Files with conflict markers and new timestamps fall through 371175261Sobrien * here, but they need to. T_CONFLICT is an error in 372175261Sobrien * commit_fileproc, whereas T_CONFLICT with conflict markers 373175261Sobrien * is caught but only warned about. Similarly, update_fileproc 374175261Sobrien * currently reregisters a file that was conflicted but lost 375175261Sobrien * its markers. 376175261Sobrien */ 37781404Speter ret = T_MODIFIED; 37881404Speter sticky_ck (finfo, aflag, vers); 37917721Speter#endif 38081404Speter } 38181404Speter else if (strcmp (vers->entdata->options ? 38281404Speter vers->entdata->options : "", vers->options) != 0) 38381404Speter { 38481404Speter /* file has not changed; check out if -k changed */ 38581404Speter ret = T_CHECKOUT; 38681404Speter } 38781404Speter else 38881404Speter { 38917721Speter 39081404Speter /* 39181404Speter * else -> note that No_Difference will Register the 39281404Speter * file already for us, using the new tag/date. This 39381404Speter * is the desired behaviour 39481404Speter */ 39581404Speter ret = T_UPTODATE; 39617721Speter } 39717721Speter } 39817721Speter else 39917721Speter { 40017721Speter /* The RCS file is a newer version than the user file */ 40117721Speter 40217721Speter if (vers->ts_user == NULL) 40317721Speter { 40417721Speter /* There is no user file, so just get it */ 40517721Speter 40644852Speter /* See comment at other "update" compare, for more 40744852Speter thoughts on this comparison. */ 408128266Speter if (strcmp (cvs_cmd_name, "update") == 0) 40917721Speter if (!really_quiet) 41025839Speter error (0, 0, "warning: %s was lost", finfo->fullname); 41117721Speter ret = T_CHECKOUT; 41217721Speter } 41317721Speter else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 41417721Speter 41517721Speter /* 41617721Speter * The user file is still unmodified, so just get it as well 41717721Speter */ 418177391Sobrien ret = T_PATCH; 41981404Speter else if (No_Difference (finfo, vers)) 42081404Speter /* really modified, needs to merge */ 42181404Speter ret = T_NEEDS_MERGE; 42217721Speter else 42381404Speter ret = T_PATCH; 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