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