classify.c revision 177391
1/* 2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 3 * 4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5 * and others. 6 * 7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8 * Portions Copyright (C) 1989-1992, Brian Berliner 9 * 10 * You may distribute under the terms of the GNU General Public License as 11 * specified in the README file that comes with the CVS source distribution. 12 * 13 */ 14 15#include "cvs.h" 16 17static void sticky_ck PROTO ((struct file_info *finfo, int aflag, 18 Vers_TS * vers)); 19 20 21 22static inline int keywords_may_change PROTO ((int aflag, Vers_TS * vers)); 23static inline int 24keywords_may_change (aflag, vers) 25 int aflag; 26 Vers_TS * vers; 27{ 28 int retval; 29 30 if (/* Options are different... */ 31 strcmp (vers->entdata->options, vers->options) 32 /* ...or... */ 33 || (/* ...clearing stickies... */ 34 aflag 35 /* ...and... */ 36 && (/* ...there used to be a tag which subs in Name keys... */ 37 (vers->entdata->tag && !isdigit (vers->entdata->tag[0])) 38 /* ...or there used to be a keyword mode which may be 39 * changed by -A... 40 */ 41 || (strlen (vers->entdata->options) 42 && strcmp (vers->entdata->options, "-kkv") 43 && strcmp (vers->entdata->options, "-kb")))) 44 /* ...or... */ 45 || (/* ...this is not commit... */ 46 strcmp (cvs_cmd_name, "commit") 47 /* ...and... */ 48 && (/* ...the tag is changing in a way that affects Name keys... */ 49 (vers->entdata->tag && vers->tag 50 && strcmp (vers->entdata->tag, vers->tag) 51 && !(isdigit (vers->entdata->tag[0]) 52 && isdigit (vers->entdata->tag[0]))) 53 || (!vers->entdata->tag && vers->tag 54 && !isdigit (vers->tag[0]))))) 55 retval = 1; 56 else 57 retval = 0; 58 59 return retval; 60} 61 62 63 64/* 65 * Classify the state of a file 66 */ 67Ctype 68Classify_File (finfo, tag, date, options, force_tag_match, aflag, versp, 69 pipeout) 70 struct file_info *finfo; 71 char *tag; 72 char *date; 73 74 /* Keyword expansion options. Can be either NULL or "" to 75 indicate none are specified here. */ 76 char *options; 77 78 int force_tag_match; 79 int aflag; 80 Vers_TS **versp; 81 int pipeout; 82{ 83 Vers_TS *vers; 84 Ctype ret; 85 86 /* get all kinds of good data about the file */ 87 vers = Version_TS (finfo, options, tag, date, 88 force_tag_match, 0); 89 90 if (vers->vn_user == NULL) 91 { 92 /* No entry available, ts_rcs is invalid */ 93 if (vers->vn_rcs == NULL) 94 { 95 /* there is no RCS file either */ 96 if (vers->ts_user == NULL) 97 { 98 /* there is no user file */ 99 /* FIXME: Why do we skip this message if vers->tag or 100 vers->date is set? It causes "cvs update -r tag98 foo" 101 to silently do nothing, which is seriously confusing 102 behavior. "cvs update foo" gives this message, which 103 is what I would expect. */ 104 if (!force_tag_match || !(vers->tag || vers->date)) 105 if (!really_quiet) 106 error (0, 0, "nothing known about %s", finfo->fullname); 107 ret = T_UNKNOWN; 108 } 109 else 110 { 111 /* there is a user file */ 112 /* FIXME: Why do we skip this message if vers->tag or 113 vers->date is set? It causes "cvs update -r tag98 foo" 114 to silently do nothing, which is seriously confusing 115 behavior. "cvs update foo" gives this message, which 116 is what I would expect. */ 117 if (!force_tag_match || !(vers->tag || vers->date)) 118 if (!really_quiet) 119 error (0, 0, "use `%s add' to create an entry for %s", 120 program_name, finfo->fullname); 121 ret = T_UNKNOWN; 122 } 123 } 124 else if (RCS_isdead (vers->srcfile, vers->vn_rcs)) 125 { 126 /* there is an RCS file, but it's dead */ 127 if (vers->ts_user == NULL) 128 ret = T_UPTODATE; 129 else 130 { 131 error (0, 0, "use `%s add' to create an entry for %s", 132 program_name, finfo->fullname); 133 ret = T_UNKNOWN; 134 } 135 } 136 else if (!pipeout && vers->ts_user && No_Difference (finfo, vers)) 137 { 138 /* the files were different so it is a conflict */ 139 if (!really_quiet) 140 error (0, 0, "move away %s; it is in the way", 141 finfo->fullname); 142 ret = T_CONFLICT; 143 } 144 else 145 /* no user file or no difference, just checkout */ 146 ret = T_CHECKOUT; 147 } 148 else if (strcmp (vers->vn_user, "0") == 0) 149 { 150 /* An entry for a new-born file; ts_rcs is dummy */ 151 152 if (vers->ts_user == NULL) 153 { 154 if (pipeout) 155 { 156 ret = T_CHECKOUT; 157 } 158 else 159 { 160 /* 161 * There is no user file, but there should be one; remove the 162 * entry 163 */ 164 if (!really_quiet) 165 error (0, 0, "warning: new-born %s has disappeared", 166 finfo->fullname); 167 ret = T_REMOVE_ENTRY; 168 } 169 } 170 else if (vers->vn_rcs == NULL || 171 RCS_isdead (vers->srcfile, vers->vn_rcs)) 172 /* No RCS file or RCS file revision is dead */ 173 ret = T_ADDED; 174 else 175 { 176 if (pipeout) 177 { 178 ret = T_CHECKOUT; 179 } 180 else 181 { 182 if (vers->srcfile->flags & INATTIC 183 && vers->srcfile->flags & VALID) 184 { 185 /* This file has been added on some branch other than 186 the one we are looking at. In the branch we are 187 looking at, the file was already valid. */ 188 if (!really_quiet) 189 error (0, 0, 190 "conflict: %s has been added, but already exists", 191 finfo->fullname); 192 } 193 else 194 { 195 /* 196 * There is an RCS file, so someone else must have checked 197 * one in behind our back; conflict 198 */ 199 if (!really_quiet) 200 error (0, 0, 201 "conflict: %s created independently by second party", 202 finfo->fullname); 203 } 204 ret = T_CONFLICT; 205 } 206 } 207 } 208 else if (vers->vn_user[0] == '-') 209 { 210 /* An entry for a removed file, ts_rcs is invalid */ 211 212 if (vers->ts_user == NULL) 213 { 214 /* There is no user file (as it should be) */ 215 216 if (vers->vn_rcs == NULL 217 || RCS_isdead (vers->srcfile, vers->vn_rcs)) 218 { 219 220 /* 221 * There is no RCS file; this is all-right, but it has been 222 * removed independently by a second party; remove the entry 223 */ 224 ret = T_REMOVE_ENTRY; 225 } 226 else if (strcmp (vers->vn_rcs, vers->vn_user + 1) == 0) 227 /* 228 * The RCS file is the same version as the user file was, and 229 * that's OK; remove it 230 */ 231 ret = T_REMOVED; 232 else if (pipeout) 233 /* 234 * The RCS file doesn't match the user's file, but it doesn't 235 * matter in this case 236 */ 237 ret = T_NEEDS_MERGE; 238 else 239 { 240 241 /* 242 * The RCS file is a newer version than the removed user file 243 * and this is definitely not OK; make it a conflict. 244 */ 245 if (!really_quiet) 246 error (0, 0, 247 "conflict: removed %s was modified by second party", 248 finfo->fullname); 249 ret = T_CONFLICT; 250 } 251 } 252 else 253 { 254 /* The user file shouldn't be there */ 255 if (!really_quiet) 256 error (0, 0, "%s should be removed and is still there", 257 finfo->fullname); 258 ret = T_REMOVED; 259 } 260 } 261 else 262 { 263 /* A normal entry, TS_Rcs is valid */ 264 if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs)) 265 { 266 /* There is no RCS file */ 267 268 if (vers->ts_user == NULL) 269 { 270 /* There is no user file, so just remove the entry */ 271 if (!really_quiet) 272 error (0, 0, "warning: %s is not (any longer) pertinent", 273 finfo->fullname); 274 ret = T_REMOVE_ENTRY; 275 } 276 else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 277 { 278 279 /* 280 * The user file is still unmodified, so just remove it from 281 * the entry list 282 */ 283 if (!really_quiet) 284 error (0, 0, "%s is no longer in the repository", 285 finfo->fullname); 286 ret = T_REMOVE_ENTRY; 287 } 288 else if (No_Difference (finfo, vers)) 289 { 290 /* they are different -> conflict */ 291 if (!really_quiet) 292 error (0, 0, 293 "conflict: %s is modified but no longer in the repository", 294 finfo->fullname); 295 ret = T_CONFLICT; 296 } 297 else 298 { 299 /* they weren't really different */ 300 if (!really_quiet) 301 error (0, 0, 302 "warning: %s is not (any longer) pertinent", 303 finfo->fullname); 304 ret = T_REMOVE_ENTRY; 305 } 306 } 307 else if (strcmp (vers->vn_rcs, vers->vn_user) == 0) 308 { 309 /* The RCS file is the same version as the user file */ 310 311 if (vers->ts_user == NULL) 312 { 313 314 /* 315 * There is no user file, so note that it was lost and 316 * extract a new version 317 */ 318 /* Comparing the cvs_cmd_name against "update", in 319 addition to being an ugly way to operate, means 320 that this message does not get printed by the 321 server. That might be considered just a straight 322 bug, although there is one subtlety: that case also 323 gets hit when a patch fails and the client fetches 324 a file. I'm not sure there is currently any way 325 for the server to distinguish those two cases. */ 326 if (strcmp (cvs_cmd_name, "update") == 0) 327 if (!really_quiet) 328 error (0, 0, "warning: %s was lost", finfo->fullname); 329 ret = T_CHECKOUT; 330 } 331 else if (!strcmp (vers->ts_user, 332 vers->ts_conflict 333 ? vers->ts_conflict : vers->ts_rcs)) 334 { 335 336 /* 337 * The user file is still unmodified, so nothing special at 338 * all to do -- no lists updated, unless the sticky -k option 339 * has changed. If the sticky tag has changed, we just need 340 * to re-register the entry 341 */ 342 /* TODO: decide whether we need to check file permissions 343 for a mismatch, and return T_CONFLICT if so. */ 344 if (keywords_may_change (aflag, vers)) 345 ret = T_PATCH; 346 else if (vers->ts_conflict) 347 ret = T_CONFLICT; 348 else 349 { 350 ret = T_UPTODATE; 351 sticky_ck (finfo, aflag, vers); 352 } 353 } 354 else if (No_Difference (finfo, vers)) 355 { 356 357 /* 358 * they really are different; modified if we aren't 359 * changing any sticky -k options, else needs merge 360 */ 361#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED 362 if (strcmp (vers->entdata->options ? 363 vers->entdata->options : "", vers->options) == 0) 364 ret = T_MODIFIED; 365 else 366 ret = T_NEEDS_MERGE; 367#else 368 /* Files with conflict markers and new timestamps fall through 369 * here, but they need to. T_CONFLICT is an error in 370 * commit_fileproc, whereas T_CONFLICT with conflict markers 371 * is caught but only warned about. Similarly, update_fileproc 372 * currently reregisters a file that was conflicted but lost 373 * its markers. 374 */ 375 ret = T_MODIFIED; 376 sticky_ck (finfo, aflag, vers); 377#endif 378 } 379 else if (strcmp (vers->entdata->options ? 380 vers->entdata->options : "", vers->options) != 0) 381 { 382 /* file has not changed; check out if -k changed */ 383 ret = T_CHECKOUT; 384 } 385 else 386 { 387 388 /* 389 * else -> note that No_Difference will Register the 390 * file already for us, using the new tag/date. This 391 * is the desired behaviour 392 */ 393 ret = T_UPTODATE; 394 } 395 } 396 else 397 { 398 /* The RCS file is a newer version than the user file */ 399 400 if (vers->ts_user == NULL) 401 { 402 /* There is no user file, so just get it */ 403 404 /* See comment at other "update" compare, for more 405 thoughts on this comparison. */ 406 if (strcmp (cvs_cmd_name, "update") == 0) 407 if (!really_quiet) 408 error (0, 0, "warning: %s was lost", finfo->fullname); 409 ret = T_CHECKOUT; 410 } 411 else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 412 413 /* 414 * The user file is still unmodified, so just get it as well 415 */ 416 ret = T_PATCH; 417 else if (No_Difference (finfo, vers)) 418 /* really modified, needs to merge */ 419 ret = T_NEEDS_MERGE; 420 else 421 ret = T_PATCH; 422 } 423 } 424 425 /* free up the vers struct, or just return it */ 426 if (versp != (Vers_TS **) NULL) 427 *versp = vers; 428 else 429 freevers_ts (&vers); 430 431 /* return the status of the file */ 432 return (ret); 433} 434 435static void 436sticky_ck (finfo, aflag, vers) 437 struct file_info *finfo; 438 int aflag; 439 Vers_TS *vers; 440{ 441 if (aflag || vers->tag || vers->date) 442 { 443 char *enttag = vers->entdata->tag; 444 char *entdate = vers->entdata->date; 445 446 if ((enttag && vers->tag && strcmp (enttag, vers->tag)) || 447 ((enttag && !vers->tag) || (!enttag && vers->tag)) || 448 (entdate && vers->date && strcmp (entdate, vers->date)) || 449 ((entdate && !vers->date) || (!entdate && vers->date))) 450 { 451 Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs, 452 vers->options, vers->tag, vers->date, vers->ts_conflict); 453 454#ifdef SERVER_SUPPORT 455 if (server_active) 456 { 457 /* We need to update the entries line on the client side. 458 It is possible we will later update it again via 459 server_updated or some such, but that is OK. */ 460 server_update_entries 461 (finfo->file, finfo->update_dir, finfo->repository, 462 strcmp (vers->ts_rcs, vers->ts_user) == 0 ? 463 SERVER_UPDATED : SERVER_MERGED); 464 } 465#endif 466 } 467 } 468} 469