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