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 /* there is an RCS file, but it's dead */ 78 if (vers->ts_user == NULL) 79 ret = T_UPTODATE; 80 else 81 { 82 error (0, 0, "use `%s add' to create an entry for %s", 83 program_name, finfo->fullname); 84 ret = T_UNKNOWN; 85 } 86 } 87 else if (!pipeout && vers->ts_user && No_Difference (finfo, vers)) 88 { 89 /* the files were different so it is a conflict */ 90 if (!really_quiet) 91 error (0, 0, "move away %s; it is in the way", 92 finfo->fullname); 93 ret = T_CONFLICT; 94 } 95 else 96 /* no user file or no difference, just checkout */ 97 ret = T_CHECKOUT; 98 } 99 else if (strcmp (vers->vn_user, "0") == 0) 100 { 101 /* An entry for a new-born file; ts_rcs is dummy */ 102 103 if (vers->ts_user == NULL) 104 { 105 /* 106 * There is no user file, but there should be one; remove the 107 * entry 108 */ 109 if (!really_quiet) 110 error (0, 0, "warning: new-born %s has disappeared", finfo->fullname); 111 ret = T_REMOVE_ENTRY; 112 } 113 else if (vers->vn_rcs == NULL || 114 RCS_isdead (vers->srcfile, vers->vn_rcs)) 115 /* No RCS file or RCS file revision is dead */ 116 ret = T_ADDED; 117 else 118 { 119 if (vers->srcfile->flags & INATTIC 120 && vers->srcfile->flags & VALID) 121 { 122 /* This file has been added on some branch other than 123 the one we are looking at. In the branch we are 124 looking at, the file was already valid. */ 125 if (!really_quiet) 126 error (0, 0, 127 "conflict: %s has been added, but already exists", 128 finfo->fullname); 129 } 130 else 131 { 132 /* 133 * There is an RCS file, so someone else must have checked 134 * one in behind our back; conflict 135 */ 136 if (!really_quiet) 137 error (0, 0, 138 "conflict: %s created independently by second party", 139 finfo->fullname); 140 } 141 ret = T_CONFLICT; 142 } 143 } 144 else if (vers->vn_user[0] == '-') 145 { 146 /* An entry for a removed file, ts_rcs is invalid */ 147 148 if (vers->ts_user == NULL) 149 { 150 /* There is no user file (as it should be) */ 151 152 if (vers->vn_rcs == NULL 153 || RCS_isdead (vers->srcfile, vers->vn_rcs)) 154 { 155 156 /* 157 * There is no RCS file; this is all-right, but it has been 158 * removed independently by a second party; remove the entry 159 */ 160 ret = T_REMOVE_ENTRY; 161 } 162 else if (strcmp (vers->vn_rcs, vers->vn_user + 1) == 0) 163 /* 164 * The RCS file is the same version as the user file was, and 165 * that's OK; remove it 166 */ 167 ret = T_REMOVED; 168 else if (pipeout) 169 /* 170 * The RCS file doesn't match the user's file, but it doesn't 171 * matter in this case 172 */ 173 ret = T_NEEDS_MERGE; 174 else 175 { 176 177 /* 178 * The RCS file is a newer version than the removed user file 179 * and this is definitely not OK; make it a conflict. 180 */ 181 if (!really_quiet) 182 error (0, 0, 183 "conflict: removed %s was modified by second party", 184 finfo->fullname); 185 ret = T_CONFLICT; 186 } 187 } 188 else 189 { 190 /* The user file shouldn't be there */ 191 if (!really_quiet) 192 error (0, 0, "%s should be removed and is still there", 193 finfo->fullname); 194 ret = T_REMOVED; 195 } 196 } 197 else 198 { 199 /* A normal entry, TS_Rcs is valid */ 200 if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs)) 201 { 202 /* There is no RCS file */ 203 204 if (vers->ts_user == NULL) 205 { 206 /* There is no user file, so just remove the entry */ 207 if (!really_quiet) 208 error (0, 0, "warning: %s is not (any longer) pertinent", 209 finfo->fullname); 210 ret = T_REMOVE_ENTRY; 211 } 212 else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 213 { 214 215 /* 216 * The user file is still unmodified, so just remove it from 217 * the entry list 218 */ 219 if (!really_quiet) 220 error (0, 0, "%s is no longer in the repository", 221 finfo->fullname); 222 ret = T_REMOVE_ENTRY; 223 } 224 else if (No_Difference (finfo, vers)) 225 { 226 /* they are different -> conflict */ 227 if (!really_quiet) 228 error (0, 0, 229 "conflict: %s is modified but no longer in the repository", 230 finfo->fullname); 231 ret = T_CONFLICT; 232 } 233 else 234 { 235 /* they weren't really different */ 236 if (!really_quiet) 237 error (0, 0, 238 "warning: %s is not (any longer) pertinent", 239 finfo->fullname); 240 ret = T_REMOVE_ENTRY; 241 } 242 } 243 else if (strcmp (vers->vn_rcs, vers->vn_user) == 0) 244 { 245 /* The RCS file is the same version as the user file */ 246 247 if (vers->ts_user == NULL) 248 { 249 250 /* 251 * There is no user file, so note that it was lost and 252 * extract a new version 253 */ 254 /* Comparing the command_name against "update", in 255 addition to being an ugly way to operate, means 256 that this message does not get printed by the 257 server. That might be considered just a straight 258 bug, although there is one subtlety: that case also 259 gets hit when a patch fails and the client fetches 260 a file. I'm not sure there is currently any way 261 for the server to distinguish those two cases. */ 262 if (strcmp (command_name, "update") == 0) 263 if (!really_quiet) 264 error (0, 0, "warning: %s was lost", finfo->fullname); 265 ret = T_CHECKOUT; 266 } 267 else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 268 { 269 270 /* 271 * The user file is still unmodified, so nothing special at 272 * all to do -- no lists updated, unless the sticky -k option 273 * has changed. If the sticky tag has changed, we just need 274 * to re-register the entry 275 */ 276 /* TODO: decide whether we need to check file permissions 277 for a mismatch, and return T_CONFLICT if so. */ 278 if (vers->entdata->options && 279 strcmp (vers->entdata->options, vers->options) != 0) 280 ret = T_CHECKOUT; 281 else 282 { 283 sticky_ck (finfo, aflag, vers); 284 ret = T_UPTODATE; 285 } 286 } 287 else if (No_Difference (finfo, vers)) 288 { 289 290 /* 291 * they really are different; modified if we aren't 292 * changing any sticky -k options, else needs merge 293 */ 294 #ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED 295 if (strcmp (vers->entdata->options ? 296 vers->entdata->options : "", vers->options) == 0) 297 ret = T_MODIFIED; 298 else 299 ret = T_NEEDS_MERGE; 300 #else 301 ret = T_MODIFIED; 302 sticky_ck (finfo, aflag, vers); 303 #endif 304 } 305 else if (strcmp (vers->entdata->options ? 306 vers->entdata->options : "", vers->options) != 0) 307 { 308 /* file has not changed; check out if -k changed */ 309 ret = T_CHECKOUT; 310 } 311 else 312 { 313 314 /* 315 * else -> note that No_Difference will Register the 316 * file already for us, using the new tag/date. This 317 * is the desired behaviour 318 */ 319 ret = T_UPTODATE; 320 } 321 } 322 else 323 { 324 /* The RCS file is a newer version than the user file */ 325 326 if (vers->ts_user == NULL) 327 { 328 /* There is no user file, so just get it */ 329 330 /* See comment at other "update" compare, for more 331 thoughts on this comparison. */ 332 if (strcmp (command_name, "update") == 0) 333 if (!really_quiet) 334 error (0, 0, "warning: %s was lost", finfo->fullname); 335 ret = T_CHECKOUT; 336 } 337 else if (strcmp (vers->ts_user, vers->ts_rcs) == 0) 338 { 339 340 /* 341 * The user file is still unmodified, so just get it as well 342 */ 343 if (strcmp (vers->entdata->options ? 344 vers->entdata->options : "", vers->options) != 0 345 || (vers->srcfile != NULL 346 && (vers->srcfile->flags & INATTIC) != 0)) 347 ret = T_CHECKOUT; 348 else 349 ret = T_PATCH; 350 } 351 else if (No_Difference (finfo, vers)) 352 /* really modified, needs to merge */ 353 ret = T_NEEDS_MERGE; 354 else if ((strcmp (vers->entdata->options ? 355 vers->entdata->options : "", vers->options) 356 != 0) 357 || (vers->srcfile != NULL 358 && (vers->srcfile->flags & INATTIC) != 0)) 359 /* not really modified, check it out */ 360 ret = T_CHECKOUT; 361 else 362 ret = T_PATCH; 363 } 364 } 365 366 /* free up the vers struct, or just return it */ 367 if (versp != (Vers_TS **) NULL) 368 *versp = vers; 369 else 370 freevers_ts (&vers); 371 372 /* return the status of the file */ 373 return (ret); 374 } 375 376 static void 377 sticky_ck (finfo, aflag, vers) 378 struct file_info *finfo; 379 int aflag; 380 Vers_TS *vers; 381 { 382 if (aflag || vers->tag || vers->date) 383 { 384 char *enttag = vers->entdata->tag; 385 char *entdate = vers->entdata->date; 386 387 if ((enttag && vers->tag && strcmp (enttag, vers->tag)) || 388 ((enttag && !vers->tag) || (!enttag && vers->tag)) || 389 (entdate && vers->date && strcmp (entdate, vers->date)) || 390 ((entdate && !vers->date) || (!entdate && vers->date))) 391 { 392 Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs, 393 vers->options, vers->tag, vers->date, vers->ts_conflict); 394 395 #ifdef SERVER_SUPPORT 396 if (server_active) 397 { 398 /* We need to update the entries line on the client side. 399 It is possible we will later update it again via 400 server_updated or some such, but that is OK. */ 401 server_update_entries 402 (finfo->file, finfo->update_dir, finfo->repository, 403 strcmp (vers->ts_rcs, vers->ts_user) == 0 ? 404 SERVER_UPDATED : SERVER_MERGED); 405 } 406 #endif 407 } 408 } 409 } 410