classify.c revision 34461
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 12static void sticky_ck PROTO ((struct file_info *finfo, int aflag, 13 Vers_TS * vers)); 14 15/* 16 * Classify the state of a file 17 */ 18Ctype 19Classify_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 "\ 159conflict: %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 "\ 171conflict: %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 if (strcmp (command_name, "update") == 0) 292 if (!really_quiet) 293 error (0, 0, "warning: %s was lost", finfo->fullname); 294 ret = T_CHECKOUT; 295 } 296 else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 297 { 298 299 /* 300 * The user file is still unmodified, so nothing special at 301 * all to do -- no lists updated, unless the sticky -k option 302 * has changed. If the sticky tag has changed, we just need 303 * to re-register the entry 304 */ 305 /* TODO: decide whether we need to check file permissions 306 for a mismatch, and return T_CONFLICT if so. */ 307 if (vers->entdata->options && 308 strcmp (vers->entdata->options, vers->options) != 0) 309 ret = T_CHECKOUT; 310 else 311 { 312 sticky_ck (finfo, aflag, vers); 313 ret = T_UPTODATE; 314 } 315 } 316 else 317 { 318 319 /* 320 * The user file appears to have been modified, but we call 321 * No_Difference to verify that it really has been modified 322 */ 323 if (No_Difference (finfo, vers)) 324 { 325 326 /* 327 * they really are different; modified if we aren't 328 * changing any sticky -k options, else needs merge 329 */ 330#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED 331 if (strcmp (vers->entdata->options ? 332 vers->entdata->options : "", vers->options) == 0) 333 ret = T_MODIFIED; 334 else 335 ret = T_NEEDS_MERGE; 336#else 337 ret = T_MODIFIED; 338 sticky_ck (finfo, aflag, vers); 339#endif 340 } 341 else 342 { 343 /* file has not changed; check out if -k changed */ 344 if (strcmp (vers->entdata->options ? 345 vers->entdata->options : "", vers->options) != 0) 346 { 347 ret = T_CHECKOUT; 348 } 349 else 350 { 351 352 /* 353 * else -> note that No_Difference will Register the 354 * file already for us, using the new tag/date. This 355 * is the desired behaviour 356 */ 357 ret = T_UPTODATE; 358 } 359 } 360 } 361 } 362 else 363 { 364 /* The RCS file is a newer version than the user file */ 365 366 if (vers->ts_user == NULL) 367 { 368 /* There is no user file, so just get it */ 369 370 if (strcmp (command_name, "update") == 0) 371 if (!really_quiet) 372 error (0, 0, "warning: %s was lost", finfo->fullname); 373 ret = T_CHECKOUT; 374 } 375 else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 376 { 377 378 /* 379 * The user file is still unmodified, so just get it as well 380 */ 381#ifdef SERVER_SUPPORT 382 if (strcmp (vers->entdata->options ? 383 vers->entdata->options : "", vers->options) != 0 384 || (vers->srcfile != NULL 385 && (vers->srcfile->flags & INATTIC) != 0)) 386 ret = T_CHECKOUT; 387 else 388 ret = T_PATCH; 389#else 390 ret = T_CHECKOUT; 391#endif 392 } 393 else 394 { 395 if (No_Difference (finfo, vers)) 396 /* really modified, needs to merge */ 397 ret = T_NEEDS_MERGE; 398#ifdef SERVER_SUPPORT 399 else if ((strcmp (vers->entdata->options ? 400 vers->entdata->options : "", vers->options) 401 != 0) 402 || (vers->srcfile != NULL 403 && (vers->srcfile->flags & INATTIC) != 0)) 404 /* not really modified, check it out */ 405 ret = T_CHECKOUT; 406 else 407 ret = T_PATCH; 408#else 409 else 410 /* not really modified, check it out */ 411 ret = T_CHECKOUT; 412#endif 413 } 414 } 415 } 416 417 /* free up the vers struct, or just return it */ 418 if (versp != (Vers_TS **) NULL) 419 *versp = vers; 420 else 421 freevers_ts (&vers); 422 423 /* return the status of the file */ 424 return (ret); 425} 426 427static void 428sticky_ck (finfo, aflag, vers) 429 struct file_info *finfo; 430 int aflag; 431 Vers_TS *vers; 432{ 433 if (aflag || vers->tag || vers->date) 434 { 435 char *enttag = vers->entdata->tag; 436 char *entdate = vers->entdata->date; 437 438 if ((enttag && vers->tag && strcmp (enttag, vers->tag)) || 439 ((enttag && !vers->tag) || (!enttag && vers->tag)) || 440 (entdate && vers->date && strcmp (entdate, vers->date)) || 441 ((entdate && !vers->date) || (!entdate && vers->date))) 442 { 443 Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs, 444 vers->options, vers->tag, vers->date, vers->ts_conflict); 445 446#ifdef SERVER_SUPPORT 447 if (server_active) 448 { 449 /* We need to update the entries line on the client side. 450 It is possible we will later update it again via 451 server_updated or some such, but that is OK. */ 452 server_update_entries 453 (finfo->file, finfo->update_dir, finfo->repository, 454 strcmp (vers->ts_rcs, vers->ts_user) == 0 ? 455 SERVER_UPDATED : SERVER_MERGED); 456 } 457#endif 458 } 459 } 460} 461