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