classify.c revision 44852
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 { 7717721Speter if (vers->ts_user == NULL) 7825839Speter ret = T_UPTODATE; 7917721Speter else 8017721Speter { 8132785Speter error (0, 0, "use `%s add' to create an entry for %s", 8232785Speter program_name, finfo->fullname); 8317721Speter ret = T_UNKNOWN; 8417721Speter } 8517721Speter } 8617721Speter else 8717721Speter { 8817721Speter /* there is an rcs file */ 8917721Speter 9017721Speter if (vers->ts_user == NULL) 9117721Speter { 9217721Speter /* There is no user file; needs checkout */ 9317721Speter ret = T_CHECKOUT; 9417721Speter } 9517721Speter else 9617721Speter { 9717721Speter if (pipeout) 9817721Speter { 9917721Speter /* 10017721Speter * The user file doesn't necessarily have anything 10117721Speter * to do with this. 10217721Speter */ 10317721Speter ret = T_CHECKOUT; 10417721Speter } 10517721Speter /* 10617721Speter * There is a user file; print a warning and add it to the 10717721Speter * conflict list, only if it is indeed different from what we 10817721Speter * plan to extract 10917721Speter */ 11025839Speter else if (No_Difference (finfo, vers)) 11117721Speter { 11217721Speter /* the files were different so it is a conflict */ 11317721Speter if (!really_quiet) 11417721Speter error (0, 0, "move away %s; it is in the way", 11525839Speter finfo->fullname); 11617721Speter ret = T_CONFLICT; 11717721Speter } 11817721Speter else 11917721Speter /* since there was no difference, still needs checkout */ 12017721Speter ret = T_CHECKOUT; 12117721Speter } 12217721Speter } 12317721Speter } 12417721Speter else if (strcmp (vers->vn_user, "0") == 0) 12517721Speter { 12617721Speter /* An entry for a new-born file; ts_rcs is dummy */ 12717721Speter 12817721Speter if (vers->ts_user == NULL) 12917721Speter { 13017721Speter /* 13117721Speter * There is no user file, but there should be one; remove the 13217721Speter * entry 13317721Speter */ 13417721Speter if (!really_quiet) 13525839Speter error (0, 0, "warning: new-born %s has disappeared", finfo->fullname); 13617721Speter ret = T_REMOVE_ENTRY; 13717721Speter } 13817721Speter else 13917721Speter { 14017721Speter /* There is a user file */ 14117721Speter 14217721Speter if (vers->vn_rcs == NULL) 14317721Speter /* There is no RCS file, added file */ 14417721Speter ret = T_ADDED; 14517721Speter else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) 14617721Speter /* we are resurrecting. */ 14717721Speter ret = T_ADDED; 14817721Speter else 14917721Speter { 15017721Speter if (vers->srcfile->flags & INATTIC 15117721Speter && vers->srcfile->flags & VALID) 15217721Speter { 15317721Speter /* This file has been added on some branch other than 15417721Speter the one we are looking at. In the branch we are 15517721Speter looking at, the file was already valid. */ 15617721Speter if (!really_quiet) 15717721Speter error (0, 0, 15817721Speter "\ 15917721Speterconflict: %s has been added, but already exists", 16025839Speter finfo->fullname); 16117721Speter } 16217721Speter else 16317721Speter { 16417721Speter /* 16517721Speter * There is an RCS file, so someone else must have checked 16617721Speter * one in behind our back; conflict 16717721Speter */ 16817721Speter if (!really_quiet) 16917721Speter error (0, 0, 17017721Speter "\ 17117721Speterconflict: %s created independently by second party", 17225839Speter finfo->fullname); 17317721Speter } 17417721Speter ret = T_CONFLICT; 17517721Speter } 17617721Speter } 17717721Speter } 17817721Speter else if (vers->vn_user[0] == '-') 17917721Speter { 18017721Speter /* An entry for a removed file, ts_rcs is invalid */ 18117721Speter 18217721Speter if (vers->ts_user == NULL) 18317721Speter { 18417721Speter /* There is no user file (as it should be) */ 18517721Speter 18625839Speter if (vers->vn_rcs == NULL 18725839Speter || RCS_isdead (vers->srcfile, vers->vn_rcs)) 18817721Speter { 18917721Speter 19017721Speter /* 19117721Speter * There is no RCS file; this is all-right, but it has been 19217721Speter * removed independently by a second party; remove the entry 19317721Speter */ 19417721Speter ret = T_REMOVE_ENTRY; 19517721Speter } 19625839Speter else if (vers->vn_rcs == NULL 19725839Speter ? vers->vn_user[1] == '\0' 19825839Speter : strcmp (vers->vn_rcs, vers->vn_user + 1) == 0) 19917721Speter /* 20017721Speter * The RCS file is the same version as the user file was, and 20117721Speter * that's OK; remove it 20217721Speter */ 20317721Speter ret = T_REMOVED; 20417721Speter else 20517721Speter { 20617721Speter 20717721Speter /* 20817721Speter * The RCS file is a newer version than the removed user file 20917721Speter * and this is definitely not OK; make it a conflict. 21017721Speter */ 21117721Speter if (!really_quiet) 21217721Speter error (0, 0, 21317721Speter "conflict: removed %s was modified by second party", 21425839Speter finfo->fullname); 21517721Speter ret = T_CONFLICT; 21617721Speter } 21717721Speter } 21817721Speter else 21917721Speter { 22017721Speter /* The user file shouldn't be there */ 22117721Speter if (!really_quiet) 22217721Speter error (0, 0, "%s should be removed and is still there", 22325839Speter finfo->fullname); 22417721Speter ret = T_REMOVED; 22517721Speter } 22617721Speter } 22717721Speter else 22817721Speter { 22917721Speter /* A normal entry, TS_Rcs is valid */ 23017721Speter if (vers->vn_rcs == NULL) 23117721Speter { 23217721Speter /* There is no RCS file */ 23317721Speter 23417721Speter if (vers->ts_user == NULL) 23517721Speter { 23617721Speter /* There is no user file, so just remove the entry */ 23717721Speter if (!really_quiet) 23817721Speter error (0, 0, "warning: %s is not (any longer) pertinent", 23925839Speter finfo->fullname); 24017721Speter ret = T_REMOVE_ENTRY; 24117721Speter } 24217721Speter else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 24317721Speter { 24417721Speter 24517721Speter /* 24617721Speter * The user file is still unmodified, so just remove it from 24717721Speter * the entry list 24817721Speter */ 24917721Speter if (!really_quiet) 25017721Speter error (0, 0, "%s is no longer in the repository", 25125839Speter finfo->fullname); 25217721Speter ret = T_REMOVE_ENTRY; 25317721Speter } 25417721Speter else 25517721Speter { 25617721Speter /* 25717721Speter * The user file has been modified and since it is no longer 25817721Speter * in the repository, a conflict is raised 25917721Speter */ 26025839Speter if (No_Difference (finfo, vers)) 26117721Speter { 26217721Speter /* they are different -> conflict */ 26317721Speter if (!really_quiet) 26417721Speter error (0, 0, 26517721Speter "conflict: %s is modified but no longer in the repository", 26625839Speter finfo->fullname); 26717721Speter ret = T_CONFLICT; 26817721Speter } 26917721Speter else 27017721Speter { 27117721Speter /* they weren't really different */ 27217721Speter if (!really_quiet) 27317721Speter error (0, 0, 27417721Speter "warning: %s is not (any longer) pertinent", 27525839Speter finfo->fullname); 27617721Speter ret = T_REMOVE_ENTRY; 27717721Speter } 27817721Speter } 27917721Speter } 28017721Speter else if (strcmp (vers->vn_rcs, vers->vn_user) == 0) 28117721Speter { 28217721Speter /* The RCS file is the same version as the user file */ 28317721Speter 28417721Speter if (vers->ts_user == NULL) 28517721Speter { 28617721Speter 28717721Speter /* 28817721Speter * There is no user file, so note that it was lost and 28917721Speter * extract a new version 29017721Speter */ 29144852Speter /* Comparing the command_name against "update", in 29244852Speter addition to being an ugly way to operate, means 29344852Speter that this message does not get printed by the 29444852Speter server. That might be considered just a straight 29544852Speter bug, although there is one subtlety: that case also 29644852Speter gets hit when a patch fails and the client fetches 29744852Speter a file. I'm not sure there is currently any way 29844852Speter for the server to distinguish those two cases. */ 29917721Speter if (strcmp (command_name, "update") == 0) 30017721Speter if (!really_quiet) 30125839Speter error (0, 0, "warning: %s was lost", finfo->fullname); 30217721Speter ret = T_CHECKOUT; 30317721Speter } 30417721Speter else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 30517721Speter { 30617721Speter 30717721Speter /* 30817721Speter * The user file is still unmodified, so nothing special at 30917721Speter * all to do -- no lists updated, unless the sticky -k option 31017721Speter * has changed. If the sticky tag has changed, we just need 31117721Speter * to re-register the entry 31217721Speter */ 31334461Speter /* TODO: decide whether we need to check file permissions 31434461Speter for a mismatch, and return T_CONFLICT if so. */ 31517721Speter if (vers->entdata->options && 31617721Speter strcmp (vers->entdata->options, vers->options) != 0) 31717721Speter ret = T_CHECKOUT; 31817721Speter else 31917721Speter { 32032785Speter sticky_ck (finfo, aflag, vers); 32117721Speter ret = T_UPTODATE; 32217721Speter } 32317721Speter } 32417721Speter else 32517721Speter { 32617721Speter 32717721Speter /* 32817721Speter * The user file appears to have been modified, but we call 32917721Speter * No_Difference to verify that it really has been modified 33017721Speter */ 33125839Speter if (No_Difference (finfo, vers)) 33217721Speter { 33317721Speter 33417721Speter /* 33517721Speter * they really are different; modified if we aren't 33617721Speter * changing any sticky -k options, else needs merge 33717721Speter */ 33817721Speter#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED 33917721Speter if (strcmp (vers->entdata->options ? 34017721Speter vers->entdata->options : "", vers->options) == 0) 34117721Speter ret = T_MODIFIED; 34217721Speter else 34317721Speter ret = T_NEEDS_MERGE; 34417721Speter#else 34517721Speter ret = T_MODIFIED; 34632785Speter sticky_ck (finfo, aflag, vers); 34717721Speter#endif 34817721Speter } 34917721Speter else 35017721Speter { 35117721Speter /* file has not changed; check out if -k changed */ 35217721Speter if (strcmp (vers->entdata->options ? 35317721Speter vers->entdata->options : "", vers->options) != 0) 35417721Speter { 35517721Speter ret = T_CHECKOUT; 35617721Speter } 35717721Speter else 35817721Speter { 35917721Speter 36017721Speter /* 36117721Speter * else -> note that No_Difference will Register the 36217721Speter * file already for us, using the new tag/date. This 36317721Speter * is the desired behaviour 36417721Speter */ 36517721Speter ret = T_UPTODATE; 36617721Speter } 36717721Speter } 36817721Speter } 36917721Speter } 37017721Speter else 37117721Speter { 37217721Speter /* The RCS file is a newer version than the user file */ 37317721Speter 37417721Speter if (vers->ts_user == NULL) 37517721Speter { 37617721Speter /* There is no user file, so just get it */ 37717721Speter 37844852Speter /* See comment at other "update" compare, for more 37944852Speter thoughts on this comparison. */ 38017721Speter if (strcmp (command_name, "update") == 0) 38117721Speter if (!really_quiet) 38225839Speter error (0, 0, "warning: %s was lost", finfo->fullname); 38317721Speter ret = T_CHECKOUT; 38417721Speter } 38517721Speter else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 38617721Speter { 38717721Speter 38817721Speter /* 38917721Speter * The user file is still unmodified, so just get it as well 39017721Speter */ 39117721Speter#ifdef SERVER_SUPPORT 39234461Speter if (strcmp (vers->entdata->options ? 39317721Speter vers->entdata->options : "", vers->options) != 0 39417721Speter || (vers->srcfile != NULL 39517721Speter && (vers->srcfile->flags & INATTIC) != 0)) 39617721Speter ret = T_CHECKOUT; 39717721Speter else 39817721Speter ret = T_PATCH; 39917721Speter#else 40017721Speter ret = T_CHECKOUT; 40117721Speter#endif 40217721Speter } 40317721Speter else 40417721Speter { 40525839Speter if (No_Difference (finfo, vers)) 40617721Speter /* really modified, needs to merge */ 40717721Speter ret = T_NEEDS_MERGE; 40817721Speter#ifdef SERVER_SUPPORT 40917721Speter else if ((strcmp (vers->entdata->options ? 41017721Speter vers->entdata->options : "", vers->options) 41117721Speter != 0) 41217721Speter || (vers->srcfile != NULL 41317721Speter && (vers->srcfile->flags & INATTIC) != 0)) 41417721Speter /* not really modified, check it out */ 41517721Speter ret = T_CHECKOUT; 41617721Speter else 41717721Speter ret = T_PATCH; 41817721Speter#else 41917721Speter else 42017721Speter /* not really modified, check it out */ 42117721Speter ret = T_CHECKOUT; 42217721Speter#endif 42317721Speter } 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