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