classify.c revision 177391
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... */ 37177391Sobrien (vers->entdata->tag && !isdigit (vers->entdata->tag[0])) 38177391Sobrien /* ...or there used to be a keyword mode which may be 39177391Sobrien * changed by -A... 40177391Sobrien */ 41177391Sobrien || (strlen (vers->entdata->options) 42177391Sobrien && strcmp (vers->entdata->options, "-kkv") 43177391Sobrien && strcmp (vers->entdata->options, "-kb")))) 44177391Sobrien /* ...or... */ 45177391Sobrien || (/* ...this is not commit... */ 46177391Sobrien strcmp (cvs_cmd_name, "commit") 47177391Sobrien /* ...and... */ 48177391Sobrien && (/* ...the tag is changing in a way that affects Name keys... */ 49177391Sobrien (vers->entdata->tag && vers->tag 50177391Sobrien && strcmp (vers->entdata->tag, vers->tag) 51177391Sobrien && !(isdigit (vers->entdata->tag[0]) 52177391Sobrien && isdigit (vers->entdata->tag[0]))) 53177391Sobrien || (!vers->entdata->tag && vers->tag 54177391Sobrien && !isdigit (vers->tag[0]))))) 55177391Sobrien retval = 1; 56177391Sobrien else 57177391Sobrien retval = 0; 58177391Sobrien 59177391Sobrien return retval; 60177391Sobrien} 61177391Sobrien 62177391Sobrien 63177391Sobrien 6417721Speter/* 6517721Speter * Classify the state of a file 6617721Speter */ 6717721SpeterCtype 6825839SpeterClassify_File (finfo, tag, date, options, force_tag_match, aflag, versp, 6925839Speter pipeout) 7025839Speter struct file_info *finfo; 7117721Speter char *tag; 7217721Speter char *date; 7332785Speter 7432785Speter /* Keyword expansion options. Can be either NULL or "" to 7532785Speter indicate none are specified here. */ 7617721Speter char *options; 7732785Speter 7817721Speter int force_tag_match; 7917721Speter int aflag; 8017721Speter Vers_TS **versp; 8117721Speter int pipeout; 8217721Speter{ 8317721Speter Vers_TS *vers; 8417721Speter Ctype ret; 8517721Speter 8617721Speter /* get all kinds of good data about the file */ 8725839Speter vers = Version_TS (finfo, options, tag, date, 8825839Speter force_tag_match, 0); 8917721Speter 9017721Speter if (vers->vn_user == NULL) 9117721Speter { 9217721Speter /* No entry available, ts_rcs is invalid */ 9317721Speter if (vers->vn_rcs == NULL) 9417721Speter { 9517721Speter /* there is no RCS file either */ 9617721Speter if (vers->ts_user == NULL) 9717721Speter { 9817721Speter /* there is no user file */ 9925839Speter /* FIXME: Why do we skip this message if vers->tag or 10025839Speter vers->date is set? It causes "cvs update -r tag98 foo" 10125839Speter to silently do nothing, which is seriously confusing 10225839Speter behavior. "cvs update foo" gives this message, which 10325839Speter is what I would expect. */ 10417721Speter if (!force_tag_match || !(vers->tag || vers->date)) 10517721Speter if (!really_quiet) 10625839Speter error (0, 0, "nothing known about %s", finfo->fullname); 10717721Speter ret = T_UNKNOWN; 10817721Speter } 10917721Speter else 11017721Speter { 11117721Speter /* there is a user file */ 11225839Speter /* FIXME: Why do we skip this message if vers->tag or 11325839Speter vers->date is set? It causes "cvs update -r tag98 foo" 11425839Speter to silently do nothing, which is seriously confusing 11525839Speter behavior. "cvs update foo" gives this message, which 11625839Speter is what I would expect. */ 11717721Speter if (!force_tag_match || !(vers->tag || vers->date)) 11817721Speter if (!really_quiet) 11932785Speter error (0, 0, "use `%s add' to create an entry for %s", 12032785Speter program_name, finfo->fullname); 12117721Speter ret = T_UNKNOWN; 12217721Speter } 12317721Speter } 12417721Speter else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) 12517721Speter { 12681404Speter /* there is an RCS file, but it's dead */ 12717721Speter if (vers->ts_user == NULL) 12825839Speter ret = T_UPTODATE; 12917721Speter else 13017721Speter { 13132785Speter error (0, 0, "use `%s add' to create an entry for %s", 13232785Speter program_name, finfo->fullname); 13317721Speter ret = T_UNKNOWN; 13417721Speter } 13517721Speter } 13681404Speter else if (!pipeout && vers->ts_user && No_Difference (finfo, vers)) 13717721Speter { 13881404Speter /* the files were different so it is a conflict */ 13981404Speter if (!really_quiet) 14081404Speter error (0, 0, "move away %s; it is in the way", 14181404Speter finfo->fullname); 14281404Speter ret = T_CONFLICT; 14317721Speter } 14481404Speter else 14581404Speter /* no user file or no difference, just checkout */ 14681404Speter ret = T_CHECKOUT; 14717721Speter } 14817721Speter else if (strcmp (vers->vn_user, "0") == 0) 14917721Speter { 15017721Speter /* An entry for a new-born file; ts_rcs is dummy */ 15117721Speter 15217721Speter if (vers->ts_user == NULL) 15317721Speter { 154107484Speter if (pipeout) 155107484Speter { 156107484Speter ret = T_CHECKOUT; 157107484Speter } 158107484Speter else 159107484Speter { 160107484Speter /* 161107484Speter * There is no user file, but there should be one; remove the 162107484Speter * entry 163107484Speter */ 164107484Speter if (!really_quiet) 165107484Speter error (0, 0, "warning: new-born %s has disappeared", 166107484Speter finfo->fullname); 167107484Speter ret = T_REMOVE_ENTRY; 168107484Speter } 16917721Speter } 17081404Speter else if (vers->vn_rcs == NULL || 17181404Speter RCS_isdead (vers->srcfile, vers->vn_rcs)) 17281404Speter /* No RCS file or RCS file revision is dead */ 17381404Speter ret = T_ADDED; 17417721Speter else 17517721Speter { 176107484Speter if (pipeout) 17781404Speter { 178107484Speter ret = T_CHECKOUT; 17981404Speter } 18017721Speter else 18117721Speter { 182107484Speter if (vers->srcfile->flags & INATTIC 183107484Speter && vers->srcfile->flags & VALID) 184107484Speter { 185107484Speter /* This file has been added on some branch other than 186107484Speter the one we are looking at. In the branch we are 187107484Speter looking at, the file was already valid. */ 188107484Speter if (!really_quiet) 189107484Speter error (0, 0, 190107484Speter "conflict: %s has been added, but already exists", 191107484Speter finfo->fullname); 192107484Speter } 193107484Speter else 194107484Speter { 195107484Speter /* 196107484Speter * There is an RCS file, so someone else must have checked 197107484Speter * one in behind our back; conflict 198107484Speter */ 199107484Speter if (!really_quiet) 200107484Speter error (0, 0, 20181404Speter "conflict: %s created independently by second party", 202107484Speter finfo->fullname); 203107484Speter } 204107484Speter ret = T_CONFLICT; 20517721Speter } 20617721Speter } 20717721Speter } 20817721Speter else if (vers->vn_user[0] == '-') 20917721Speter { 21017721Speter /* An entry for a removed file, ts_rcs is invalid */ 21117721Speter 21217721Speter if (vers->ts_user == NULL) 21317721Speter { 21417721Speter /* There is no user file (as it should be) */ 21517721Speter 21625839Speter if (vers->vn_rcs == NULL 21725839Speter || RCS_isdead (vers->srcfile, vers->vn_rcs)) 21817721Speter { 21917721Speter 22017721Speter /* 22117721Speter * There is no RCS file; this is all-right, but it has been 22217721Speter * removed independently by a second party; remove the entry 22317721Speter */ 22417721Speter ret = T_REMOVE_ENTRY; 22517721Speter } 22681404Speter else if (strcmp (vers->vn_rcs, vers->vn_user + 1) == 0) 22717721Speter /* 22817721Speter * The RCS file is the same version as the user file was, and 22917721Speter * that's OK; remove it 23017721Speter */ 23117721Speter ret = T_REMOVED; 23281404Speter else if (pipeout) 23381404Speter /* 23481404Speter * The RCS file doesn't match the user's file, but it doesn't 23581404Speter * matter in this case 23681404Speter */ 23781404Speter ret = T_NEEDS_MERGE; 23817721Speter else 23917721Speter { 24017721Speter 24117721Speter /* 24217721Speter * The RCS file is a newer version than the removed user file 24317721Speter * and this is definitely not OK; make it a conflict. 24417721Speter */ 24517721Speter if (!really_quiet) 24617721Speter error (0, 0, 24717721Speter "conflict: removed %s was modified by second party", 24825839Speter finfo->fullname); 24917721Speter ret = T_CONFLICT; 25017721Speter } 25117721Speter } 25217721Speter else 25317721Speter { 25417721Speter /* The user file shouldn't be there */ 25517721Speter if (!really_quiet) 25617721Speter error (0, 0, "%s should be removed and is still there", 25725839Speter finfo->fullname); 25817721Speter ret = T_REMOVED; 25917721Speter } 26017721Speter } 26117721Speter else 26217721Speter { 26317721Speter /* A normal entry, TS_Rcs is valid */ 26481404Speter if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs)) 26517721Speter { 26617721Speter /* There is no RCS file */ 26717721Speter 26817721Speter if (vers->ts_user == NULL) 26917721Speter { 27017721Speter /* There is no user file, so just remove the entry */ 27117721Speter if (!really_quiet) 27217721Speter error (0, 0, "warning: %s is not (any longer) pertinent", 27325839Speter finfo->fullname); 27417721Speter ret = T_REMOVE_ENTRY; 27517721Speter } 27617721Speter else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 27717721Speter { 27817721Speter 27917721Speter /* 28017721Speter * The user file is still unmodified, so just remove it from 28117721Speter * the entry list 28217721Speter */ 28317721Speter if (!really_quiet) 28417721Speter error (0, 0, "%s is no longer in the repository", 28525839Speter finfo->fullname); 28617721Speter ret = T_REMOVE_ENTRY; 28717721Speter } 28881404Speter else if (No_Difference (finfo, vers)) 28917721Speter { 29081404Speter /* they are different -> conflict */ 29181404Speter if (!really_quiet) 29281404Speter error (0, 0, 29317721Speter "conflict: %s is modified but no longer in the repository", 29425839Speter finfo->fullname); 29581404Speter ret = T_CONFLICT; 29617721Speter } 29781404Speter else 29881404Speter { 29981404Speter /* they weren't really different */ 30081404Speter if (!really_quiet) 30181404Speter error (0, 0, 30281404Speter "warning: %s is not (any longer) pertinent", 30381404Speter finfo->fullname); 30481404Speter ret = T_REMOVE_ENTRY; 30581404Speter } 30617721Speter } 30717721Speter else if (strcmp (vers->vn_rcs, vers->vn_user) == 0) 30817721Speter { 30917721Speter /* The RCS file is the same version as the user file */ 31017721Speter 31117721Speter if (vers->ts_user == NULL) 31217721Speter { 31317721Speter 31417721Speter /* 31517721Speter * There is no user file, so note that it was lost and 31617721Speter * extract a new version 31717721Speter */ 318128266Speter /* Comparing the cvs_cmd_name against "update", in 31944852Speter addition to being an ugly way to operate, means 32044852Speter that this message does not get printed by the 32144852Speter server. That might be considered just a straight 32244852Speter bug, although there is one subtlety: that case also 32344852Speter gets hit when a patch fails and the client fetches 32444852Speter a file. I'm not sure there is currently any way 32544852Speter for the server to distinguish those two cases. */ 326128266Speter if (strcmp (cvs_cmd_name, "update") == 0) 32717721Speter if (!really_quiet) 32825839Speter error (0, 0, "warning: %s was lost", finfo->fullname); 32917721Speter ret = T_CHECKOUT; 33017721Speter } 331175261Sobrien else if (!strcmp (vers->ts_user, 332175261Sobrien vers->ts_conflict 333175261Sobrien ? vers->ts_conflict : vers->ts_rcs)) 33417721Speter { 33517721Speter 33617721Speter /* 33717721Speter * The user file is still unmodified, so nothing special at 33817721Speter * all to do -- no lists updated, unless the sticky -k option 33917721Speter * has changed. If the sticky tag has changed, we just need 34017721Speter * to re-register the entry 34117721Speter */ 34234461Speter /* TODO: decide whether we need to check file permissions 34334461Speter for a mismatch, and return T_CONFLICT if so. */ 344177391Sobrien if (keywords_may_change (aflag, vers)) 345177391Sobrien ret = T_PATCH; 346175261Sobrien else if (vers->ts_conflict) 347175261Sobrien ret = T_CONFLICT; 34817721Speter else 34917721Speter { 350177391Sobrien ret = T_UPTODATE; 35132785Speter sticky_ck (finfo, aflag, vers); 35217721Speter } 35317721Speter } 35481404Speter else if (No_Difference (finfo, vers)) 35517721Speter { 35617721Speter 35717721Speter /* 35881404Speter * they really are different; modified if we aren't 35981404Speter * changing any sticky -k options, else needs merge 36017721Speter */ 36117721Speter#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED 36281404Speter if (strcmp (vers->entdata->options ? 36381404Speter vers->entdata->options : "", vers->options) == 0) 36481404Speter ret = T_MODIFIED; 36581404Speter else 36681404Speter ret = T_NEEDS_MERGE; 36717721Speter#else 368175261Sobrien /* Files with conflict markers and new timestamps fall through 369175261Sobrien * here, but they need to. T_CONFLICT is an error in 370175261Sobrien * commit_fileproc, whereas T_CONFLICT with conflict markers 371175261Sobrien * is caught but only warned about. Similarly, update_fileproc 372175261Sobrien * currently reregisters a file that was conflicted but lost 373175261Sobrien * its markers. 374175261Sobrien */ 37581404Speter ret = T_MODIFIED; 37681404Speter sticky_ck (finfo, aflag, vers); 37717721Speter#endif 37881404Speter } 37981404Speter else if (strcmp (vers->entdata->options ? 38081404Speter vers->entdata->options : "", vers->options) != 0) 38181404Speter { 38281404Speter /* file has not changed; check out if -k changed */ 38381404Speter ret = T_CHECKOUT; 38481404Speter } 38581404Speter else 38681404Speter { 38717721Speter 38881404Speter /* 38981404Speter * else -> note that No_Difference will Register the 39081404Speter * file already for us, using the new tag/date. This 39181404Speter * is the desired behaviour 39281404Speter */ 39381404Speter ret = T_UPTODATE; 39417721Speter } 39517721Speter } 39617721Speter else 39717721Speter { 39817721Speter /* The RCS file is a newer version than the user file */ 39917721Speter 40017721Speter if (vers->ts_user == NULL) 40117721Speter { 40217721Speter /* There is no user file, so just get it */ 40317721Speter 40444852Speter /* See comment at other "update" compare, for more 40544852Speter thoughts on this comparison. */ 406128266Speter if (strcmp (cvs_cmd_name, "update") == 0) 40717721Speter if (!really_quiet) 40825839Speter error (0, 0, "warning: %s was lost", finfo->fullname); 40917721Speter ret = T_CHECKOUT; 41017721Speter } 41117721Speter else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 41217721Speter 41317721Speter /* 41417721Speter * The user file is still unmodified, so just get it as well 41517721Speter */ 416177391Sobrien ret = T_PATCH; 41781404Speter else if (No_Difference (finfo, vers)) 41881404Speter /* really modified, needs to merge */ 41981404Speter ret = T_NEEDS_MERGE; 42017721Speter else 42181404Speter ret = T_PATCH; 42217721Speter } 42317721Speter } 42417721Speter 42517721Speter /* free up the vers struct, or just return it */ 42617721Speter if (versp != (Vers_TS **) NULL) 42717721Speter *versp = vers; 42817721Speter else 42917721Speter freevers_ts (&vers); 43017721Speter 43117721Speter /* return the status of the file */ 43217721Speter return (ret); 43317721Speter} 43417721Speter 43517721Speterstatic void 43632785Spetersticky_ck (finfo, aflag, vers) 43732785Speter struct file_info *finfo; 43817721Speter int aflag; 43917721Speter Vers_TS *vers; 44017721Speter{ 44117721Speter if (aflag || vers->tag || vers->date) 44217721Speter { 44317721Speter char *enttag = vers->entdata->tag; 44417721Speter char *entdate = vers->entdata->date; 44517721Speter 44617721Speter if ((enttag && vers->tag && strcmp (enttag, vers->tag)) || 44717721Speter ((enttag && !vers->tag) || (!enttag && vers->tag)) || 44817721Speter (entdate && vers->date && strcmp (entdate, vers->date)) || 44917721Speter ((entdate && !vers->date) || (!entdate && vers->date))) 45017721Speter { 45132785Speter Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs, 45217721Speter vers->options, vers->tag, vers->date, vers->ts_conflict); 45317721Speter 45417721Speter#ifdef SERVER_SUPPORT 45517721Speter if (server_active) 45617721Speter { 45717721Speter /* We need to update the entries line on the client side. 45817721Speter It is possible we will later update it again via 45917721Speter server_updated or some such, but that is OK. */ 46017721Speter server_update_entries 46132785Speter (finfo->file, finfo->update_dir, finfo->repository, 46217721Speter strcmp (vers->ts_rcs, vers->ts_user) == 0 ? 46317721Speter SERVER_UPDATED : SERVER_MERGED); 46417721Speter } 46517721Speter#endif 46617721Speter } 46717721Speter } 46817721Speter} 469