classify.c revision 179826
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 15179826Sobrien#include <sys/cdefs.h> 16179826Sobrien__FBSDID("$FreeBSD: head/contrib/cvs/src/classify.c 179826 2008-06-16 17:06:17Z obrien $"); 17179826Sobrien 1817721Speter#include "cvs.h" 1917721Speter 2032785Speterstatic void sticky_ck PROTO ((struct file_info *finfo, int aflag, 2132785Speter Vers_TS * vers)); 2217721Speter 23177391Sobrien 24177391Sobrien 25177391Sobrienstatic inline int keywords_may_change PROTO ((int aflag, Vers_TS * vers)); 26177391Sobrienstatic inline int 27177391Sobrienkeywords_may_change (aflag, vers) 28177391Sobrien int aflag; 29177391Sobrien Vers_TS * vers; 30177391Sobrien{ 31177391Sobrien int retval; 32177391Sobrien 33177391Sobrien if (/* Options are different... */ 34177391Sobrien strcmp (vers->entdata->options, vers->options) 35177391Sobrien /* ...or... */ 36177391Sobrien || (/* ...clearing stickies... */ 37177391Sobrien aflag 38177391Sobrien /* ...and... */ 39177391Sobrien && (/* ...there used to be a tag which subs in Name keys... */ 40179561Sobrien (vers->entdata->tag && !isdigit (vers->entdata->tag[0]) 41179561Sobrien && vers->tag && !isdigit (vers->tag[0]) 42179561Sobrien && strcmp (vers->entdata->tag, vers->tag)) 43177391Sobrien /* ...or there used to be a keyword mode which may be 44177391Sobrien * changed by -A... 45177391Sobrien */ 46177391Sobrien || (strlen (vers->entdata->options) 47179619Sobrien && strcmp (vers->entdata->options, vers->options) 48177391Sobrien && strcmp (vers->entdata->options, "-kkv") 49177391Sobrien && strcmp (vers->entdata->options, "-kb")))) 50177391Sobrien /* ...or... */ 51177391Sobrien || (/* ...this is not commit... */ 52177391Sobrien strcmp (cvs_cmd_name, "commit") 53177391Sobrien /* ...and... */ 54177391Sobrien && (/* ...the tag is changing in a way that affects Name keys... */ 55177391Sobrien (vers->entdata->tag && vers->tag 56177391Sobrien && strcmp (vers->entdata->tag, vers->tag) 57177391Sobrien && !(isdigit (vers->entdata->tag[0]) 58177391Sobrien && isdigit (vers->entdata->tag[0]))) 59177391Sobrien || (!vers->entdata->tag && vers->tag 60177391Sobrien && !isdigit (vers->tag[0]))))) 61177391Sobrien retval = 1; 62177391Sobrien else 63177391Sobrien retval = 0; 64177391Sobrien 65177391Sobrien return retval; 66177391Sobrien} 67177391Sobrien 68177391Sobrien 69177391Sobrien 7017721Speter/* 7117721Speter * Classify the state of a file 7217721Speter */ 7317721SpeterCtype 7425839SpeterClassify_File (finfo, tag, date, options, force_tag_match, aflag, versp, 7525839Speter pipeout) 7625839Speter struct file_info *finfo; 7717721Speter char *tag; 7817721Speter char *date; 7932785Speter 8032785Speter /* Keyword expansion options. Can be either NULL or "" to 8132785Speter indicate none are specified here. */ 8217721Speter char *options; 8332785Speter 8417721Speter int force_tag_match; 8517721Speter int aflag; 8617721Speter Vers_TS **versp; 8717721Speter int pipeout; 8817721Speter{ 8917721Speter Vers_TS *vers; 9017721Speter Ctype ret; 9117721Speter 9217721Speter /* get all kinds of good data about the file */ 9325839Speter vers = Version_TS (finfo, options, tag, date, 9425839Speter force_tag_match, 0); 9517721Speter 9617721Speter if (vers->vn_user == NULL) 9717721Speter { 9817721Speter /* No entry available, ts_rcs is invalid */ 9917721Speter if (vers->vn_rcs == NULL) 10017721Speter { 10117721Speter /* there is no RCS file either */ 10217721Speter if (vers->ts_user == NULL) 10317721Speter { 10417721Speter /* there is no user file */ 10525839Speter /* FIXME: Why do we skip this message if vers->tag or 10625839Speter vers->date is set? It causes "cvs update -r tag98 foo" 10725839Speter to silently do nothing, which is seriously confusing 10825839Speter behavior. "cvs update foo" gives this message, which 10925839Speter is what I would expect. */ 11017721Speter if (!force_tag_match || !(vers->tag || vers->date)) 11117721Speter if (!really_quiet) 11225839Speter error (0, 0, "nothing known about %s", finfo->fullname); 11317721Speter ret = T_UNKNOWN; 11417721Speter } 11517721Speter else 11617721Speter { 11717721Speter /* there is a user file */ 11825839Speter /* FIXME: Why do we skip this message if vers->tag or 11925839Speter vers->date is set? It causes "cvs update -r tag98 foo" 12025839Speter to silently do nothing, which is seriously confusing 12125839Speter behavior. "cvs update foo" gives this message, which 12225839Speter is what I would expect. */ 12317721Speter if (!force_tag_match || !(vers->tag || vers->date)) 12417721Speter if (!really_quiet) 12532785Speter error (0, 0, "use `%s add' to create an entry for %s", 12632785Speter program_name, finfo->fullname); 12717721Speter ret = T_UNKNOWN; 12817721Speter } 12917721Speter } 13017721Speter else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) 13117721Speter { 13281404Speter /* there is an RCS file, but it's dead */ 13317721Speter if (vers->ts_user == NULL) 13425839Speter ret = T_UPTODATE; 13517721Speter else 13617721Speter { 13732785Speter error (0, 0, "use `%s add' to create an entry for %s", 13832785Speter program_name, finfo->fullname); 13917721Speter ret = T_UNKNOWN; 14017721Speter } 14117721Speter } 14281404Speter else if (!pipeout && vers->ts_user && No_Difference (finfo, vers)) 14317721Speter { 14481404Speter /* the files were different so it is a conflict */ 14581404Speter if (!really_quiet) 14681404Speter error (0, 0, "move away %s; it is in the way", 14781404Speter finfo->fullname); 14881404Speter ret = T_CONFLICT; 14917721Speter } 15081404Speter else 15181404Speter /* no user file or no difference, just checkout */ 15281404Speter ret = T_CHECKOUT; 15317721Speter } 15417721Speter else if (strcmp (vers->vn_user, "0") == 0) 15517721Speter { 15617721Speter /* An entry for a new-born file; ts_rcs is dummy */ 15717721Speter 15817721Speter if (vers->ts_user == NULL) 15917721Speter { 160107484Speter if (pipeout) 161107484Speter { 162107484Speter ret = T_CHECKOUT; 163107484Speter } 164107484Speter else 165107484Speter { 166107484Speter /* 167107484Speter * There is no user file, but there should be one; remove the 168107484Speter * entry 169107484Speter */ 170107484Speter if (!really_quiet) 171107484Speter error (0, 0, "warning: new-born %s has disappeared", 172107484Speter finfo->fullname); 173107484Speter ret = T_REMOVE_ENTRY; 174107484Speter } 17517721Speter } 17681404Speter else if (vers->vn_rcs == NULL || 17781404Speter RCS_isdead (vers->srcfile, vers->vn_rcs)) 17881404Speter /* No RCS file or RCS file revision is dead */ 17981404Speter ret = T_ADDED; 18017721Speter else 18117721Speter { 182107484Speter if (pipeout) 18381404Speter { 184107484Speter ret = T_CHECKOUT; 18581404Speter } 18617721Speter else 18717721Speter { 188107484Speter if (vers->srcfile->flags & INATTIC 189107484Speter && vers->srcfile->flags & VALID) 190107484Speter { 191107484Speter /* This file has been added on some branch other than 192107484Speter the one we are looking at. In the branch we are 193107484Speter looking at, the file was already valid. */ 194107484Speter if (!really_quiet) 195107484Speter error (0, 0, 196107484Speter "conflict: %s has been added, but already exists", 197107484Speter finfo->fullname); 198107484Speter } 199107484Speter else 200107484Speter { 201107484Speter /* 202107484Speter * There is an RCS file, so someone else must have checked 203107484Speter * one in behind our back; conflict 204107484Speter */ 205107484Speter if (!really_quiet) 206107484Speter error (0, 0, 20781404Speter "conflict: %s created independently by second party", 208107484Speter finfo->fullname); 209107484Speter } 210107484Speter ret = T_CONFLICT; 21117721Speter } 21217721Speter } 21317721Speter } 21417721Speter else if (vers->vn_user[0] == '-') 21517721Speter { 21617721Speter /* An entry for a removed file, ts_rcs is invalid */ 21717721Speter 21817721Speter if (vers->ts_user == NULL) 21917721Speter { 22017721Speter /* There is no user file (as it should be) */ 22117721Speter 22225839Speter if (vers->vn_rcs == NULL 22325839Speter || RCS_isdead (vers->srcfile, vers->vn_rcs)) 22417721Speter { 22517721Speter 22617721Speter /* 22717721Speter * There is no RCS file; this is all-right, but it has been 22817721Speter * removed independently by a second party; remove the entry 22917721Speter */ 23017721Speter ret = T_REMOVE_ENTRY; 23117721Speter } 23281404Speter else if (strcmp (vers->vn_rcs, vers->vn_user + 1) == 0) 23317721Speter /* 23417721Speter * The RCS file is the same version as the user file was, and 23517721Speter * that's OK; remove it 23617721Speter */ 23717721Speter ret = T_REMOVED; 23881404Speter else if (pipeout) 23981404Speter /* 24081404Speter * The RCS file doesn't match the user's file, but it doesn't 24181404Speter * matter in this case 24281404Speter */ 24381404Speter ret = T_NEEDS_MERGE; 24417721Speter else 24517721Speter { 24617721Speter 24717721Speter /* 24817721Speter * The RCS file is a newer version than the removed user file 24917721Speter * and this is definitely not OK; make it a conflict. 25017721Speter */ 25117721Speter if (!really_quiet) 25217721Speter error (0, 0, 25317721Speter "conflict: removed %s was modified by second party", 25425839Speter finfo->fullname); 25517721Speter ret = T_CONFLICT; 25617721Speter } 25717721Speter } 25817721Speter else 25917721Speter { 26017721Speter /* The user file shouldn't be there */ 26117721Speter if (!really_quiet) 26217721Speter error (0, 0, "%s should be removed and is still there", 26325839Speter finfo->fullname); 26417721Speter ret = T_REMOVED; 26517721Speter } 26617721Speter } 26717721Speter else 26817721Speter { 26917721Speter /* A normal entry, TS_Rcs is valid */ 27081404Speter if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs)) 27117721Speter { 27217721Speter /* There is no RCS file */ 27317721Speter 27417721Speter if (vers->ts_user == NULL) 27517721Speter { 27617721Speter /* There is no user file, so just remove the entry */ 27717721Speter if (!really_quiet) 27817721Speter error (0, 0, "warning: %s is not (any longer) pertinent", 27925839Speter finfo->fullname); 28017721Speter ret = T_REMOVE_ENTRY; 28117721Speter } 28217721Speter else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 28317721Speter { 28417721Speter 28517721Speter /* 28617721Speter * The user file is still unmodified, so just remove it from 28717721Speter * the entry list 28817721Speter */ 28917721Speter if (!really_quiet) 29017721Speter error (0, 0, "%s is no longer in the repository", 29125839Speter finfo->fullname); 29217721Speter ret = T_REMOVE_ENTRY; 29317721Speter } 29481404Speter else if (No_Difference (finfo, vers)) 29517721Speter { 29681404Speter /* they are different -> conflict */ 29781404Speter if (!really_quiet) 29881404Speter error (0, 0, 29917721Speter "conflict: %s is modified but no longer in the repository", 30025839Speter finfo->fullname); 30181404Speter ret = T_CONFLICT; 30217721Speter } 30381404Speter else 30481404Speter { 30581404Speter /* they weren't really different */ 30681404Speter if (!really_quiet) 30781404Speter error (0, 0, 30881404Speter "warning: %s is not (any longer) pertinent", 30981404Speter finfo->fullname); 31081404Speter ret = T_REMOVE_ENTRY; 31181404Speter } 31217721Speter } 31317721Speter else if (strcmp (vers->vn_rcs, vers->vn_user) == 0) 31417721Speter { 31517721Speter /* The RCS file is the same version as the user file */ 31617721Speter 31717721Speter if (vers->ts_user == NULL) 31817721Speter { 31917721Speter 32017721Speter /* 32117721Speter * There is no user file, so note that it was lost and 32217721Speter * extract a new version 32317721Speter */ 324128266Speter /* Comparing the cvs_cmd_name against "update", in 32544852Speter addition to being an ugly way to operate, means 32644852Speter that this message does not get printed by the 32744852Speter server. That might be considered just a straight 32844852Speter bug, although there is one subtlety: that case also 32944852Speter gets hit when a patch fails and the client fetches 33044852Speter a file. I'm not sure there is currently any way 33144852Speter for the server to distinguish those two cases. */ 332128266Speter if (strcmp (cvs_cmd_name, "update") == 0) 33317721Speter if (!really_quiet) 33425839Speter error (0, 0, "warning: %s was lost", finfo->fullname); 33517721Speter ret = T_CHECKOUT; 33617721Speter } 337175261Sobrien else if (!strcmp (vers->ts_user, 338175261Sobrien vers->ts_conflict 339175261Sobrien ? vers->ts_conflict : vers->ts_rcs)) 34017721Speter { 34117721Speter 34217721Speter /* 34317721Speter * The user file is still unmodified, so nothing special at 34417721Speter * all to do -- no lists updated, unless the sticky -k option 34517721Speter * has changed. If the sticky tag has changed, we just need 34617721Speter * to re-register the entry 34717721Speter */ 34834461Speter /* TODO: decide whether we need to check file permissions 34934461Speter for a mismatch, and return T_CONFLICT if so. */ 350177391Sobrien if (keywords_may_change (aflag, vers)) 351177391Sobrien ret = T_PATCH; 352175261Sobrien else if (vers->ts_conflict) 353175261Sobrien ret = T_CONFLICT; 35417721Speter else 35517721Speter { 356177391Sobrien ret = T_UPTODATE; 35732785Speter sticky_ck (finfo, aflag, vers); 35817721Speter } 35917721Speter } 36081404Speter else if (No_Difference (finfo, vers)) 36117721Speter { 36217721Speter 36317721Speter /* 36481404Speter * they really are different; modified if we aren't 36581404Speter * changing any sticky -k options, else needs merge 36617721Speter */ 36717721Speter#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED 36881404Speter if (strcmp (vers->entdata->options ? 36981404Speter vers->entdata->options : "", vers->options) == 0) 37081404Speter ret = T_MODIFIED; 37181404Speter else 37281404Speter ret = T_NEEDS_MERGE; 37317721Speter#else 374175261Sobrien /* Files with conflict markers and new timestamps fall through 375175261Sobrien * here, but they need to. T_CONFLICT is an error in 376175261Sobrien * commit_fileproc, whereas T_CONFLICT with conflict markers 377175261Sobrien * is caught but only warned about. Similarly, update_fileproc 378175261Sobrien * currently reregisters a file that was conflicted but lost 379175261Sobrien * its markers. 380175261Sobrien */ 38181404Speter ret = T_MODIFIED; 38281404Speter sticky_ck (finfo, aflag, vers); 38317721Speter#endif 38481404Speter } 38581404Speter else if (strcmp (vers->entdata->options ? 38681404Speter vers->entdata->options : "", vers->options) != 0) 38781404Speter { 38881404Speter /* file has not changed; check out if -k changed */ 38981404Speter ret = T_CHECKOUT; 39081404Speter } 39181404Speter else 39281404Speter { 39317721Speter 39481404Speter /* 39581404Speter * else -> note that No_Difference will Register the 39681404Speter * file already for us, using the new tag/date. This 39781404Speter * is the desired behaviour 39881404Speter */ 39981404Speter ret = T_UPTODATE; 40017721Speter } 40117721Speter } 40217721Speter else 40317721Speter { 40417721Speter /* The RCS file is a newer version than the user file */ 40517721Speter 40617721Speter if (vers->ts_user == NULL) 40717721Speter { 40817721Speter /* There is no user file, so just get it */ 40917721Speter 41044852Speter /* See comment at other "update" compare, for more 41144852Speter thoughts on this comparison. */ 412128266Speter if (strcmp (cvs_cmd_name, "update") == 0) 41317721Speter if (!really_quiet) 41425839Speter error (0, 0, "warning: %s was lost", finfo->fullname); 41517721Speter ret = T_CHECKOUT; 41617721Speter } 41717721Speter else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 41817721Speter 41917721Speter /* 42017721Speter * The user file is still unmodified, so just get it as well 42117721Speter */ 422177391Sobrien ret = T_PATCH; 42381404Speter else if (No_Difference (finfo, vers)) 42481404Speter /* really modified, needs to merge */ 42581404Speter ret = T_NEEDS_MERGE; 42617721Speter else 42781404Speter ret = T_PATCH; 42817721Speter } 42917721Speter } 43017721Speter 43117721Speter /* free up the vers struct, or just return it */ 43217721Speter if (versp != (Vers_TS **) NULL) 43317721Speter *versp = vers; 43417721Speter else 43517721Speter freevers_ts (&vers); 43617721Speter 43717721Speter /* return the status of the file */ 43817721Speter return (ret); 43917721Speter} 44017721Speter 44117721Speterstatic void 44232785Spetersticky_ck (finfo, aflag, vers) 44332785Speter struct file_info *finfo; 44417721Speter int aflag; 44517721Speter Vers_TS *vers; 44617721Speter{ 44717721Speter if (aflag || vers->tag || vers->date) 44817721Speter { 44917721Speter char *enttag = vers->entdata->tag; 45017721Speter char *entdate = vers->entdata->date; 45117721Speter 45217721Speter if ((enttag && vers->tag && strcmp (enttag, vers->tag)) || 45317721Speter ((enttag && !vers->tag) || (!enttag && vers->tag)) || 45417721Speter (entdate && vers->date && strcmp (entdate, vers->date)) || 45517721Speter ((entdate && !vers->date) || (!entdate && vers->date))) 45617721Speter { 45732785Speter Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs, 45817721Speter vers->options, vers->tag, vers->date, vers->ts_conflict); 45917721Speter 46017721Speter#ifdef SERVER_SUPPORT 46117721Speter if (server_active) 46217721Speter { 46317721Speter /* We need to update the entries line on the client side. 46417721Speter It is possible we will later update it again via 46517721Speter server_updated or some such, but that is OK. */ 46617721Speter server_update_entries 46732785Speter (finfo->file, finfo->update_dir, finfo->repository, 46817721Speter strcmp (vers->ts_rcs, vers->ts_user) == 0 ? 46917721Speter SERVER_UPDATED : SERVER_MERGED); 47017721Speter } 47117721Speter#endif 47217721Speter } 47317721Speter } 47417721Speter} 475