tag.c revision 175270
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 * Tag and Rtag 14 * 15 * Add or delete a symbolic name to an RCS file, or a collection of RCS files. 16 * Tag uses the checked out revision in the current directory, rtag uses 17 * the modules database, if necessary. 18 * 19 * $FreeBSD: head/contrib/cvs/src/tag.c 175270 2008-01-13 06:06:41Z obrien $ 20 */ 21 22#include "cvs.h" 23#include "savecwd.h" 24 25static int rtag_proc PROTO((int argc, char **argv, char *xwhere, 26 char *mwhere, char *mfile, int shorten, 27 int local_specified, char *mname, char *msg)); 28static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 29static int check_filesdoneproc PROTO ((void *callerdat, int err, 30 const char *repos, 31 const char *update_dir, 32 List *entries)); 33static int pretag_proc PROTO((const char *repository, const char *filter)); 34static void masterlist_delproc PROTO((Node *p)); 35static void tag_delproc PROTO((Node *p)); 36static int pretag_list_proc PROTO((Node *p, void *closure)); 37 38static Dtype tag_dirproc PROTO ((void *callerdat, const char *dir, 39 const char *repos, const char *update_dir, 40 List *entries)); 41static int rtag_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 42static int rtag_delete PROTO((RCSNode *rcsfile)); 43static int tag_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 44 45static char *numtag; /* specific revision to tag */ 46static int numtag_validated = 0; 47static char *date = NULL; 48static char *symtag; /* tag to add or delete */ 49static int delete_flag; /* adding a tag by default */ 50static int branch_mode; /* make an automagic "branch" tag */ 51static int disturb_branch_tags = 0; /* allow -F,-d to disturb branch tags */ 52static int force_tag_match = 1; /* force tag to match by default */ 53static int force_tag_move; /* don't force tag to move by default */ 54static int check_uptodate; /* no uptodate-check by default */ 55static int attic_too; /* remove tag from Attic files */ 56static int is_rtag; 57 58struct tag_info 59{ 60 Ctype status; 61 char *rev; 62 char *tag; 63 char *options; 64}; 65 66struct master_lists 67{ 68 List *tlist; 69}; 70 71static List *mtlist; 72static List *tlist; 73 74static const char rtag_opts[] = "+aBbdFflnQqRr:D:"; 75static const char *const rtag_usage[] = 76{ 77 "Usage: %s %s [-abdFflnR] [-r rev|-D date] tag modules...\n", 78 "\t-a\tClear tag from removed files that would not otherwise be tagged.\n", 79 "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", 80 "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n", 81 "\t-d\tDelete the given tag.\n", 82 "\t-F\tMove tag if it already exists.\n", 83 "\t-f\tForce a head revision match if tag/date not found.\n", 84 "\t-l\tLocal directory only, not recursive.\n", 85 "\t-n\tNo execution of 'tag program'.\n", 86 "\t-R\tProcess directories recursively.\n", 87 "\t-r rev\tExisting revision/tag.\n", 88 "\t-D\tExisting date.\n", 89 "(Specify the --help global option for a list of other help options)\n", 90 NULL 91}; 92 93static const char tag_opts[] = "+BbcdFflQqRr:D:"; 94static const char *const tag_usage[] = 95{ 96 "Usage: %s %s [-bcdFflR] [-r rev|-D date] tag [files...]\n", 97 "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", 98 "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n", 99 "\t-c\tCheck that working files are unmodified.\n", 100 "\t-d\tDelete the given tag.\n", 101 "\t-F\tMove tag if it already exists.\n", 102 "\t-f\tForce a head revision match if tag/date not found.\n", 103 "\t-l\tLocal directory only, not recursive.\n", 104 "\t-R\tProcess directories recursively.\n", 105 "\t-r rev\tExisting revision/tag.\n", 106 "\t-D\tExisting date.\n", 107 "(Specify the --help global option for a list of other help options)\n", 108 NULL 109}; 110 111int 112cvstag (argc, argv) 113 int argc; 114 char **argv; 115{ 116 int local = 0; /* recursive by default */ 117 int c; 118 int err = 0; 119 int run_module_prog = 1; 120 121 is_rtag = (strcmp (cvs_cmd_name, "rtag") == 0); 122 123 if (argc == -1) 124 usage (is_rtag ? rtag_usage : tag_usage); 125 126 optind = 0; 127 while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1) 128 { 129 switch (c) 130 { 131 case 'a': 132 attic_too = 1; 133 break; 134 case 'b': 135 branch_mode = 1; 136 break; 137 case 'B': 138 disturb_branch_tags = 1; 139 break; 140 case 'c': 141 check_uptodate = 1; 142 break; 143 case 'd': 144 delete_flag = 1; 145 break; 146 case 'F': 147 force_tag_move = 1; 148 break; 149 case 'f': 150 force_tag_match = 0; 151 break; 152 case 'l': 153 local = 1; 154 break; 155 case 'n': 156 run_module_prog = 0; 157 break; 158 case 'Q': 159 case 'q': 160 /* The CVS 1.5 client sends these options (in addition to 161 Global_option requests), so we must ignore them. */ 162 if (!server_active) 163 error (1, 0, 164 "-q or -Q must be specified before \"%s\"", 165 cvs_cmd_name); 166 break; 167 case 'R': 168 local = 0; 169 break; 170 case 'r': 171 numtag = optarg; 172 break; 173 case 'D': 174 if (date) 175 free (date); 176 date = Make_Date (optarg); 177 break; 178 case '?': 179 default: 180 usage (is_rtag ? rtag_usage : tag_usage); 181 break; 182 } 183 } 184 argc -= optind; 185 argv += optind; 186 187 if (argc < (is_rtag ? 2 : 1)) 188 usage (is_rtag ? rtag_usage : tag_usage); 189 symtag = argv[0]; 190 argc--; 191 argv++; 192 193 if (date && numtag) 194 error (1, 0, "-r and -D options are mutually exclusive"); 195 if (delete_flag && branch_mode) 196 error (0, 0, "warning: -b ignored with -d options"); 197 RCS_check_tag (symtag); 198 199#ifdef CLIENT_SUPPORT 200 if (current_parsed_root->isremote) 201 { 202 /* We're the client side. Fire up the remote server. */ 203 start_server (); 204 205 ign_setup (); 206 207 if (attic_too) 208 send_arg("-a"); 209 if (branch_mode) 210 send_arg("-b"); 211 if (disturb_branch_tags) 212 send_arg("-B"); 213 if (check_uptodate) 214 send_arg("-c"); 215 if (delete_flag) 216 send_arg("-d"); 217 if (force_tag_move) 218 send_arg("-F"); 219 if (!force_tag_match) 220 send_arg ("-f"); 221 if (local) 222 send_arg("-l"); 223 if (!run_module_prog) 224 send_arg("-n"); 225 226 if (numtag) 227 option_with_arg ("-r", numtag); 228 if (date) 229 client_senddate (date); 230 231 send_arg ("--"); 232 233 send_arg (symtag); 234 235 if (is_rtag) 236 { 237 int i; 238 for (i = 0; i < argc; ++i) 239 send_arg (argv[i]); 240 send_to_server ("rtag\012", 0); 241 } 242 else 243 { 244 send_files (argc, argv, local, 0, 245 246 /* I think the -c case is like "cvs status", in 247 which we really better be correct rather than 248 being fast; it is just too confusing otherwise. */ 249 check_uptodate ? 0 : SEND_NO_CONTENTS); 250 send_file_names (argc, argv, SEND_EXPAND_WILD); 251 send_to_server ("tag\012", 0); 252 } 253 254 return get_responses_and_close (); 255 } 256#endif 257 258 if (is_rtag) 259 { 260 DBM *db; 261 int i; 262 db = open_module (); 263 for (i = 0; i < argc; i++) 264 { 265 /* XXX last arg should be repository, but doesn't make sense here */ 266 history_write ('T', (delete_flag ? "D" : (numtag ? numtag : 267 (date ? date : "A"))), symtag, argv[i], ""); 268 err += do_module (db, argv[i], TAG, 269 delete_flag ? "Untagging" : "Tagging", 270 rtag_proc, (char *) NULL, 0, local, run_module_prog, 271 0, symtag); 272 } 273 close_module (db); 274 } 275 else 276 { 277 err = rtag_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL, 278 NULL); 279 } 280 281 return (err); 282} 283 284/* 285 * callback proc for doing the real work of tagging 286 */ 287/* ARGSUSED */ 288static int 289rtag_proc (argc, argv, xwhere, mwhere, mfile, shorten, local_specified, 290 mname, msg) 291 int argc; 292 char **argv; 293 char *xwhere; 294 char *mwhere; 295 char *mfile; 296 int shorten; 297 int local_specified; 298 char *mname; 299 char *msg; 300{ 301 /* Begin section which is identical to patch_proc--should this 302 be abstracted out somehow? */ 303 char *myargv[2]; 304 int err = 0; 305 int which; 306 char *repository; 307 char *where; 308 309 if (is_rtag) 310 { 311 repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0]) 312 + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2); 313 (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]); 314 where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1) 315 + 1); 316 (void) strcpy (where, argv[0]); 317 318 /* if mfile isn't null, we need to set up to do only part of the module */ 319 if (mfile != NULL) 320 { 321 char *cp; 322 char *path; 323 324 /* if the portion of the module is a path, put the dir part on repos */ 325 if ((cp = strrchr (mfile, '/')) != NULL) 326 { 327 *cp = '\0'; 328 (void) strcat (repository, "/"); 329 (void) strcat (repository, mfile); 330 (void) strcat (where, "/"); 331 (void) strcat (where, mfile); 332 mfile = cp + 1; 333 } 334 335 /* take care of the rest */ 336 path = xmalloc (strlen (repository) + strlen (mfile) + 5); 337 (void) sprintf (path, "%s/%s", repository, mfile); 338 if (isdir (path)) 339 { 340 /* directory means repository gets the dir tacked on */ 341 (void) strcpy (repository, path); 342 (void) strcat (where, "/"); 343 (void) strcat (where, mfile); 344 } 345 else 346 { 347 myargv[0] = argv[0]; 348 myargv[1] = mfile; 349 argc = 2; 350 argv = myargv; 351 } 352 free (path); 353 } 354 355 /* cd to the starting repository */ 356 if ( CVS_CHDIR (repository) < 0) 357 { 358 error (0, errno, "cannot chdir to %s", repository); 359 free (repository); 360 free (where); 361 return (1); 362 } 363 /* End section which is identical to patch_proc. */ 364 365 if (delete_flag || force_tag_move || attic_too || numtag) 366 which = W_REPOS | W_ATTIC; 367 else 368 which = W_REPOS; 369 } 370 else 371 { 372 where = NULL; 373 which = W_LOCAL; 374 repository = ""; 375 } 376 377 if (numtag != NULL && !numtag_validated) 378 { 379 tag_check_valid (numtag, argc - 1, argv + 1, local_specified, 0, repository); 380 numtag_validated = 1; 381 } 382 383 /* check to make sure they are authorized to tag all the 384 specified files in the repository */ 385 386 mtlist = getlist(); 387 err = start_recursion (check_fileproc, check_filesdoneproc, 388 (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, 389 argc - 1, argv + 1, local_specified, which, 0, 390 CVS_LOCK_READ, where, 1, repository); 391 392 if (err) 393 { 394 error (1, 0, "correct the above errors first!"); 395 } 396 397 /* It would be nice to provide consistency with respect to 398 commits; however CVS lacks the infrastructure to do that (see 399 Concurrency in cvs.texinfo and comment in do_recursion). */ 400 401 /* start the recursion processor */ 402 err = start_recursion (is_rtag ? rtag_fileproc : tag_fileproc, 403 (FILESDONEPROC) NULL, tag_dirproc, 404 (DIRLEAVEPROC) NULL, NULL, argc - 1, argv + 1, 405 local_specified, which, 0, CVS_LOCK_WRITE, where, 1, 406 repository); 407 if ( which & W_REPOS ) free ( repository ); 408 dellist (&mtlist); 409 if (where != NULL) 410 free (where); 411 return (err); 412} 413 414/* check file that is to be tagged */ 415/* All we do here is add it to our list */ 416 417static int 418check_fileproc (callerdat, finfo) 419 void *callerdat; 420 struct file_info *finfo; 421{ 422 const char *xdir; 423 Node *p; 424 Vers_TS *vers; 425 426 if (check_uptodate) 427 { 428 switch (Classify_File (finfo, (char *) NULL, (char *) NULL, 429 (char *) NULL, 1, 0, &vers, 0)) 430 { 431 case T_UPTODATE: 432 case T_CHECKOUT: 433 case T_PATCH: 434 case T_REMOVE_ENTRY: 435 break; 436 case T_UNKNOWN: 437 case T_CONFLICT: 438 case T_NEEDS_MERGE: 439 case T_MODIFIED: 440 case T_ADDED: 441 case T_REMOVED: 442 default: 443 error (0, 0, "%s is locally modified", finfo->fullname); 444 freevers_ts (&vers); 445 return (1); 446 } 447 } 448 else 449 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); 450 451 if (finfo->update_dir[0] == '\0') 452 xdir = "."; 453 else 454 xdir = finfo->update_dir; 455 if ((p = findnode (mtlist, xdir)) != NULL) 456 { 457 tlist = ((struct master_lists *) p->data)->tlist; 458 } 459 else 460 { 461 struct master_lists *ml; 462 463 tlist = getlist (); 464 p = getnode (); 465 p->key = xstrdup (xdir); 466 p->type = UPDATE; 467 ml = (struct master_lists *) 468 xmalloc (sizeof (struct master_lists)); 469 ml->tlist = tlist; 470 p->data = ml; 471 p->delproc = masterlist_delproc; 472 (void) addnode (mtlist, p); 473 } 474 /* do tlist */ 475 p = getnode (); 476 p->key = xstrdup (finfo->file); 477 p->type = UPDATE; 478 p->delproc = tag_delproc; 479 if (vers->srcfile == NULL) 480 { 481 if (!really_quiet) 482 error (0, 0, "nothing known about %s", finfo->file); 483 freevers_ts (&vers); 484 freenode (p); 485 return (1); 486 } 487 488 /* Here we duplicate the calculation in tag_fileproc about which 489 version we are going to tag. There probably are some subtle races 490 (e.g. numtag is "foo" which gets moved between here and 491 tag_fileproc). */ 492 if (!is_rtag && numtag == NULL && date == NULL) 493 p->data = xstrdup (vers->vn_user); 494 else 495 p->data = RCS_getversion (vers->srcfile, numtag, date, 496 force_tag_match, NULL); 497 498 if (p->data != NULL) 499 { 500 int addit = 1; 501 char *oversion; 502 503 oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 504 (int *) NULL); 505 if (oversion == NULL) 506 { 507 if (delete_flag) 508 { 509 /* Deleting a tag which did not exist is a noop and 510 should not be logged. */ 511 addit = 0; 512 } 513 } 514 else if (delete_flag) 515 { 516 free (p->data); 517 p->data = xstrdup (oversion); 518 } 519 else if (strcmp(oversion, p->data) == 0) 520 { 521 addit = 0; 522 } 523 else if (!force_tag_move) 524 { 525 addit = 0; 526 } 527 if (oversion != NULL) 528 { 529 free(oversion); 530 } 531 if (!addit) 532 { 533 free(p->data); 534 p->data = NULL; 535 } 536 } 537 freevers_ts (&vers); 538 (void) addnode (tlist, p); 539 return (0); 540} 541 542static int 543check_filesdoneproc (callerdat, err, repos, update_dir, entries) 544 void *callerdat; 545 int err; 546 const char *repos; 547 const char *update_dir; 548 List *entries; 549{ 550 int n; 551 Node *p; 552 553 p = findnode(mtlist, update_dir); 554 if (p != NULL) 555 { 556 tlist = ((struct master_lists *) p->data)->tlist; 557 } 558 else 559 { 560 tlist = (List *) NULL; 561 } 562 if ((tlist == NULL) || (tlist->list->next == tlist->list)) 563 { 564 return (err); 565 } 566 if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0) 567 { 568 error (0, 0, "Pre-tag check failed"); 569 err += n; 570 } 571 return (err); 572} 573 574static int 575pretag_proc (repository, filter) 576 const char *repository; 577 const char *filter; 578{ 579 if (filter[0] == '/') 580 { 581 char *s, *cp; 582 583 s = xstrdup(filter); 584 for (cp=s; *cp; cp++) 585 { 586 if (isspace ((unsigned char) *cp)) 587 { 588 *cp = '\0'; 589 break; 590 } 591 } 592 if (!isfile(s)) 593 { 594 error (0, errno, "cannot find pre-tag filter '%s'", s); 595 free(s); 596 return (1); 597 } 598 free(s); 599 } 600 run_setup (filter); 601 run_arg (symtag); 602 run_arg (delete_flag ? "del" : force_tag_move ? "mov" : "add"); 603 run_arg (repository); 604 walklist(tlist, pretag_list_proc, NULL); 605 return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)); 606} 607 608static void 609masterlist_delproc(p) 610 Node *p; 611{ 612 struct master_lists *ml = p->data; 613 614 dellist(&ml->tlist); 615 free(ml); 616 return; 617} 618 619static void 620tag_delproc(p) 621 Node *p; 622{ 623 if (p->data != NULL) 624 { 625 free(p->data); 626 p->data = NULL; 627 } 628 return; 629} 630 631static int 632pretag_list_proc(p, closure) 633 Node *p; 634 void *closure; 635{ 636 if (p->data != NULL) 637 { 638 run_arg(p->key); 639 run_arg(p->data); 640 } 641 return (0); 642} 643 644 645/* 646 * Called to rtag a particular file, as appropriate with the options that were 647 * set above. 648 */ 649/* ARGSUSED */ 650static int 651rtag_fileproc (callerdat, finfo) 652 void *callerdat; 653 struct file_info *finfo; 654{ 655 RCSNode *rcsfile; 656 char *version, *rev; 657 int retcode = 0; 658 659 /* find the parsed RCS data */ 660 if ((rcsfile = finfo->rcs) == NULL) 661 return (1); 662 663 /* 664 * For tagging an RCS file which is a symbolic link, you'd best be 665 * running with RCS 5.6, since it knows how to handle symbolic links 666 * correctly without breaking your link! 667 */ 668 669 if (delete_flag) 670 return (rtag_delete (rcsfile)); 671 672 /* 673 * If we get here, we are adding a tag. But, if -a was specified, we 674 * need to check to see if a -r or -D option was specified. If neither 675 * was specified and the file is in the Attic, remove the tag. 676 */ 677 if (attic_too && (!numtag && !date)) 678 { 679 if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC)) 680 return (rtag_delete (rcsfile)); 681 } 682 683 version = RCS_getversion (rcsfile, numtag, date, force_tag_match, 684 (int *) NULL); 685 if (version == NULL) 686 { 687 /* If -a specified, clean up any old tags */ 688 if (attic_too) 689 (void) rtag_delete (rcsfile); 690 691 if (!quiet && !force_tag_match) 692 { 693 error (0, 0, "cannot find tag `%s' in `%s'", 694 numtag ? numtag : "head", rcsfile->path); 695 return (1); 696 } 697 return (0); 698 } 699 if (numtag 700 && isdigit ((unsigned char) *numtag) 701 && strcmp (numtag, version) != 0) 702 { 703 704 /* 705 * We didn't find a match for the numeric tag that was specified, but 706 * that's OK. just pass the numeric tag on to rcs, to be tagged as 707 * specified. Could get here if one tried to tag "1.1.1" and there 708 * was a 1.1.1 branch with some head revision. In this case, we want 709 * the tag to reference "1.1.1" and not the revision at the head of 710 * the branch. Use a symbolic tag for that. 711 */ 712 rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag; 713 retcode = RCS_settag(rcsfile, symtag, numtag); 714 if (retcode == 0) 715 RCS_rewrite (rcsfile, NULL, NULL); 716 } 717 else 718 { 719 char *oversion; 720 721 /* 722 * As an enhancement for the case where a tag is being re-applied to 723 * a large body of a module, make one extra call to RCS_getversion to 724 * see if the tag is already set in the RCS file. If so, check to 725 * see if it needs to be moved. If not, do nothing. This will 726 * likely save a lot of time when simply moving the tag to the 727 * "current" head revisions of a module -- which I have found to be a 728 * typical tagging operation. 729 */ 730 rev = branch_mode ? RCS_magicrev (rcsfile, version) : version; 731 oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 732 (int *) NULL); 733 if (oversion != NULL) 734 { 735 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag); 736 737 /* 738 * if versions the same and neither old or new are branches don't 739 * have to do anything 740 */ 741 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch) 742 { 743 free (oversion); 744 free (version); 745 return (0); 746 } 747 748 if (!force_tag_move) 749 { 750 /* we're NOT going to move the tag */ 751 (void) printf ("W %s", finfo->fullname); 752 753 (void) printf (" : %s already exists on %s %s", 754 symtag, isbranch ? "branch" : "version", 755 oversion); 756 (void) printf (" : NOT MOVING tag to %s %s\n", 757 branch_mode ? "branch" : "version", rev); 758 free (oversion); 759 free (version); 760 if (branch_mode) free(rev); 761 return (0); 762 } 763 else /* force_tag_move is set and... */ 764 if ((isbranch && !disturb_branch_tags) || 765 (!isbranch && disturb_branch_tags)) 766 { 767 error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.", 768 finfo->fullname, 769 isbranch ? "branch" : "non-branch", 770 symtag, oversion, rev, 771 isbranch ? "" : " due to `-B' option"); 772 if (branch_mode) free(rev); 773 free (oversion); 774 free (version); 775 return (0); 776 } 777 free (oversion); 778 } 779 retcode = RCS_settag(rcsfile, symtag, rev); 780 if (retcode == 0) 781 RCS_rewrite (rcsfile, NULL, NULL); 782 } 783 784 if (retcode != 0) 785 { 786 error (1, retcode == -1 ? errno : 0, 787 "failed to set tag `%s' to revision `%s' in `%s'", 788 symtag, rev, rcsfile->path); 789 if (branch_mode) 790 free (rev); 791 free (version); 792 return (1); 793 } 794 if (branch_mode) 795 free (rev); 796 free (version); 797 return (0); 798} 799 800/* 801 * If -d is specified, "force_tag_match" is set, so that this call to 802 * RCS_getversion() will return a NULL version string if the symbolic 803 * tag does not exist in the RCS file. 804 * 805 * If the -r flag was used, numtag is set, and we only delete the 806 * symtag from files that have numtag. 807 * 808 * This is done here because it's MUCH faster than just blindly calling 809 * "rcs" to remove the tag... trust me. 810 */ 811static int 812rtag_delete (rcsfile) 813 RCSNode *rcsfile; 814{ 815 char *version; 816 int retcode, isbranch; 817 818 if (numtag) 819 { 820 version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1, 821 (int *) NULL); 822 if (version == NULL) 823 return (0); 824 free (version); 825 } 826 827 version = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 828 (int *) NULL); 829 if (version == NULL) 830 return (0); 831 free (version); 832 833 834 isbranch = RCS_nodeisbranch (rcsfile, symtag); 835 if ((isbranch && !disturb_branch_tags) || 836 (!isbranch && disturb_branch_tags)) 837 { 838 if (!quiet) 839 error(0, 0, 840 "Not removing %s tag `%s' from `%s'%s.", 841 isbranch ? "branch" : "non-branch", 842 symtag, rcsfile->path, 843 isbranch ? "" : " due to `-B' option"); 844 return (1); 845 } 846 847 if ((retcode = RCS_deltag(rcsfile, symtag)) != 0) 848 { 849 if (!quiet) 850 error (0, retcode == -1 ? errno : 0, 851 "failed to remove tag `%s' from `%s'", symtag, 852 rcsfile->path); 853 return (1); 854 } 855 RCS_rewrite (rcsfile, NULL, NULL); 856 return (0); 857} 858 859 860/* 861 * Called to tag a particular file (the currently checked out version is 862 * tagged with the specified tag - or the specified tag is deleted). 863 */ 864/* ARGSUSED */ 865static int 866tag_fileproc (callerdat, finfo) 867 void *callerdat; 868 struct file_info *finfo; 869{ 870 char *version, *oversion; 871 char *nversion = NULL; 872 char *rev; 873 Vers_TS *vers; 874 int retcode = 0; 875 int retval = 0; 876 877 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); 878 879 if ((numtag != NULL) || (date != NULL)) 880 { 881 nversion = RCS_getversion(vers->srcfile, 882 numtag, 883 date, 884 force_tag_match, 885 (int *) NULL); 886 if (nversion == NULL) 887 goto free_vars_and_return; 888 } 889 if (delete_flag) 890 { 891 892 int isbranch; 893 /* 894 * If -d is specified, "force_tag_match" is set, so that this call to 895 * RCS_getversion() will return a NULL version string if the symbolic 896 * tag does not exist in the RCS file. 897 * 898 * This is done here because it's MUCH faster than just blindly calling 899 * "rcs" to remove the tag... trust me. 900 */ 901 902 version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 903 (int *) NULL); 904 if (version == NULL || vers->srcfile == NULL) 905 goto free_vars_and_return; 906 907 free (version); 908 909 isbranch = RCS_nodeisbranch (finfo->rcs, symtag); 910 if ((isbranch && !disturb_branch_tags) || 911 (!isbranch && disturb_branch_tags)) 912 { 913 if (!quiet) 914 error(0, 0, 915 "Not removing %s tag `%s' from `%s'%s.", 916 isbranch ? "branch" : "non-branch", 917 symtag, vers->srcfile->path, 918 isbranch ? "" : " due to `-B' option"); 919 retval = 1; 920 goto free_vars_and_return; 921 } 922 923 if ((retcode = RCS_deltag(vers->srcfile, symtag)) != 0) 924 { 925 if (!quiet) 926 error (0, retcode == -1 ? errno : 0, 927 "failed to remove tag %s from %s", symtag, 928 vers->srcfile->path); 929 retval = 1; 930 goto free_vars_and_return; 931 } 932 RCS_rewrite (vers->srcfile, NULL, NULL); 933 934 /* warm fuzzies */ 935 if (!really_quiet) 936 { 937 cvs_output ("D ", 2); 938 cvs_output (finfo->fullname, 0); 939 cvs_output ("\n", 1); 940 } 941 942 goto free_vars_and_return; 943 } 944 945 /* 946 * If we are adding a tag, we need to know which version we have checked 947 * out and we'll tag that version. 948 */ 949 if (nversion == NULL) 950 { 951 version = vers->vn_user; 952 } 953 else 954 { 955 version = nversion; 956 } 957 if (version == NULL) 958 { 959 goto free_vars_and_return; 960 } 961 else if (strcmp (version, "0") == 0) 962 { 963 if (!quiet) 964 error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file); 965 goto free_vars_and_return; 966 } 967 else if (version[0] == '-') 968 { 969 if (!quiet) 970 error (0, 0, "skipping removed but un-commited file `%s'", finfo->file); 971 goto free_vars_and_return; 972 } 973 else if (vers->srcfile == NULL) 974 { 975 if (!quiet) 976 error (0, 0, "cannot find revision control file for `%s'", finfo->file); 977 goto free_vars_and_return; 978 } 979 980 /* 981 * As an enhancement for the case where a tag is being re-applied to a 982 * large number of files, make one extra call to RCS_getversion to see 983 * if the tag is already set in the RCS file. If so, check to see if it 984 * needs to be moved. If not, do nothing. This will likely save a lot of 985 * time when simply moving the tag to the "current" head revisions of a 986 * module -- which I have found to be a typical tagging operation. 987 */ 988 rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version; 989 oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 990 (int *) NULL); 991 if (oversion != NULL) 992 { 993 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag); 994 995 /* 996 * if versions the same and neither old or new are branches don't have 997 * to do anything 998 */ 999 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch) 1000 { 1001 free (oversion); 1002 if (branch_mode) 1003 free (rev); 1004 goto free_vars_and_return; 1005 } 1006 1007 if (!force_tag_move) 1008 { 1009 /* we're NOT going to move the tag */ 1010 cvs_output ("W ", 2); 1011 cvs_output (finfo->fullname, 0); 1012 cvs_output (" : ", 0); 1013 cvs_output (symtag, 0); 1014 cvs_output (" already exists on ", 0); 1015 cvs_output (isbranch ? "branch" : "version", 0); 1016 cvs_output (" ", 0); 1017 cvs_output (oversion, 0); 1018 cvs_output (" : NOT MOVING tag to ", 0); 1019 cvs_output (branch_mode ? "branch" : "version", 0); 1020 cvs_output (" ", 0); 1021 cvs_output (rev, 0); 1022 cvs_output ("\n", 1); 1023 free (oversion); 1024 if (branch_mode) 1025 free (rev); 1026 goto free_vars_and_return; 1027 } 1028 else /* force_tag_move == 1 and... */ 1029 if ((isbranch && !disturb_branch_tags) || 1030 (!isbranch && disturb_branch_tags)) 1031 { 1032 error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.", 1033 finfo->fullname, 1034 isbranch ? "branch" : "non-branch", 1035 symtag, oversion, rev, 1036 isbranch ? "" : " due to `-B' option"); 1037 free (oversion); 1038 if (branch_mode) 1039 free (rev); 1040 goto free_vars_and_return; 1041 } 1042 free (oversion); 1043 } 1044 1045 if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0) 1046 { 1047 error (1, retcode == -1 ? errno : 0, 1048 "failed to set tag %s to revision %s in %s", 1049 symtag, rev, vers->srcfile->path); 1050 if (branch_mode) 1051 free (rev); 1052 retval = 1; 1053 goto free_vars_and_return; 1054 } 1055 if (branch_mode) 1056 free (rev); 1057 RCS_rewrite (vers->srcfile, NULL, NULL); 1058 1059 /* more warm fuzzies */ 1060 if (!really_quiet) 1061 { 1062 cvs_output ("T ", 2); 1063 cvs_output (finfo->fullname, 0); 1064 cvs_output ("\n", 1); 1065 } 1066 1067 free_vars_and_return: 1068 if (nversion != NULL) 1069 free (nversion); 1070 freevers_ts (&vers); 1071 return (retval); 1072} 1073 1074/* 1075 * Print a warm fuzzy message 1076 */ 1077/* ARGSUSED */ 1078static Dtype 1079tag_dirproc (callerdat, dir, repos, update_dir, entries) 1080 void *callerdat; 1081 const char *dir; 1082 const char *repos; 1083 const char *update_dir; 1084 List *entries; 1085{ 1086 1087 if (ignore_directory (update_dir)) 1088 { 1089 /* print the warm fuzzy message */ 1090 if (!quiet) 1091 error (0, 0, "Ignoring %s", update_dir); 1092 return R_SKIP_ALL; 1093 } 1094 1095 if (!quiet) 1096 error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir); 1097 return (R_PROCESS); 1098} 1099 1100/* Code relating to the val-tags file. Note that this file has no way 1101 of knowing when a tag has been deleted. The problem is that there 1102 is no way of knowing whether a tag still exists somewhere, when we 1103 delete it some places. Using per-directory val-tags files (in 1104 CVSREP) might be better, but that might slow down the process of 1105 verifying that a tag is correct (maybe not, for the likely cases, 1106 if carefully done), and/or be harder to implement correctly. */ 1107 1108struct val_args { 1109 char *name; 1110 int found; 1111}; 1112 1113static int val_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 1114 1115static int 1116val_fileproc (callerdat, finfo) 1117 void *callerdat; 1118 struct file_info *finfo; 1119{ 1120 RCSNode *rcsdata; 1121 struct val_args *args = (struct val_args *)callerdat; 1122 char *tag; 1123 1124 if ((rcsdata = finfo->rcs) == NULL) 1125 /* Not sure this can happen, after all we passed only 1126 W_REPOS | W_ATTIC. */ 1127 return 0; 1128 1129 tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL); 1130 if (tag != NULL) 1131 { 1132 /* FIXME: should find out a way to stop the search at this point. */ 1133 args->found = 1; 1134 free (tag); 1135 } 1136 return 0; 1137} 1138 1139 1140 1141/* This routine determines whether a tag appears in CVSROOT/val-tags. 1142 * 1143 * The val-tags file will be open read-only when IDB is NULL. Since writes to 1144 * val-tags always append to it, the lack of locking is okay. The worst case 1145 * race condition might misinterpret a partially written "foobar" matched, for 1146 * instance, a request for "f", "foo", of "foob". Such a mismatch would be 1147 * caught harmlessly later. 1148 * 1149 * Before CVS adds a tag to val-tags, it will lock val-tags for write and 1150 * verify that the tag is still not present to avoid adding it twice. 1151 * 1152 * NOTES 1153 * This function expects its parent to handle any necessary locking of the 1154 * val-tags file. 1155 * 1156 * INPUTS 1157 * idb When this value is NULL, the val-tags file is opened in 1158 * in read-only mode. When present, the val-tags file is opened 1159 * in read-write mode and the DBM handle is stored in *IDB. 1160 * name The tag to search for. 1161 * 1162 * OUTPUTS 1163 * *idb The val-tags file opened for read/write, or NULL if it couldn't 1164 * be opened. 1165 * 1166 * ERRORS 1167 * Exits with an error message if the val-tags file cannot be opened for 1168 * read (failure to open val-tags read/write is harmless - see below). 1169 * 1170 * RETURNS 1171 * true 1. If NAME exists in val-tags. 1172 * 2. If IDB is non-NULL and val-tags cannot be opened for write. 1173 * This allows callers to ignore the harmless inability to 1174 * update the val-tags cache. 1175 * false If the file could be opened and the tag is not present. 1176 */ 1177static int is_in_val_tags PROTO((DBM **idb, const char *name)); 1178static int 1179is_in_val_tags (idb, name) 1180 DBM **idb; 1181 const char *name; 1182{ 1183 DBM *db = NULL; 1184 char *valtags_filename; 1185 datum mytag; 1186 int status; 1187 1188 /* Casting out const should be safe here - input datums are not 1189 * written to by the myndbm functions. 1190 */ 1191 mytag.dptr = (char *)name; 1192 mytag.dsize = strlen (name); 1193 1194 valtags_filename = xmalloc (strlen (current_parsed_root->directory) 1195 + sizeof CVSROOTADM 1196 + sizeof CVSROOTADM_VALTAGS + 3); 1197 sprintf (valtags_filename, "%s/%s/%s", current_parsed_root->directory, 1198 CVSROOTADM, CVSROOTADM_VALTAGS); 1199 1200 if (idb) 1201 { 1202 db = dbm_open (valtags_filename, O_RDWR, 0666); 1203 if (!db) 1204 { 1205 mode_t omask; 1206 1207 if (!existence_error (errno)) 1208 { 1209 error (0, errno, "warning: cannot open %s read/write", 1210 valtags_filename); 1211 *idb = NULL; 1212 return 1; 1213 } 1214 1215 omask = umask (cvsumask); 1216 db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666); 1217 umask (omask); 1218 if (!db) 1219 { 1220 error (0, errno, "warning: cannot create %s", 1221 valtags_filename); 1222 *idb = NULL; 1223 return 1; 1224 } 1225 1226 *idb = db; 1227 return 0; 1228 } 1229 1230 *idb = db; 1231 } 1232 else 1233 { 1234 db = dbm_open (valtags_filename, O_RDONLY, 0444); 1235 if (!db && !existence_error (errno)) 1236 error (1, errno, "cannot read %s", valtags_filename); 1237 } 1238 1239 /* If the file merely fails to exist, we just keep going and create 1240 it later if need be. */ 1241 1242 status = 0; 1243 if (db) 1244 { 1245 datum val; 1246 1247 val = dbm_fetch (db, mytag); 1248 if (val.dptr != NULL) 1249 /* Found. The tag is valid. */ 1250 status = 1; 1251 1252 /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */ 1253 1254 if (!idb) dbm_close (db); 1255 } 1256 1257 free (valtags_filename); 1258 return status; 1259} 1260 1261 1262 1263/* Add a tag to the CVSROOT/val-tags cache. Establishes a write lock and 1264 * reverifies that the tag does not exist before adding it. 1265 */ 1266static void add_to_val_tags PROTO((const char *name)); 1267static void 1268add_to_val_tags (name) 1269 const char *name; 1270{ 1271 DBM *db; 1272 datum mytag; 1273 datum value; 1274 1275 if (noexec) return; 1276 1277 val_tags_lock (current_parsed_root->directory); 1278 1279 /* Check for presence again since we have a lock now. */ 1280 if (is_in_val_tags (&db, name)) 1281 { 1282 clear_val_tags_lock (); 1283 if (db) 1284 dbm_close (db); 1285 return; 1286 } 1287 1288 /* Casting out const should be safe here - input datums are not 1289 * written to by the myndbm functions. 1290 */ 1291 mytag.dptr = (char *)name; 1292 mytag.dsize = strlen (name); 1293 value.dptr = "y"; 1294 value.dsize = 1; 1295 1296 if (dbm_store (db, mytag, value, DBM_REPLACE) < 0) 1297 error (0, errno, "failed to store %s into val-tags", name); 1298 dbm_close (db); 1299 1300 clear_val_tags_lock (); 1301} 1302 1303 1304 1305static Dtype val_direntproc PROTO ((void *, const char *, const char *, 1306 const char *, List *)); 1307 1308static Dtype 1309val_direntproc (callerdat, dir, repository, update_dir, entries) 1310 void *callerdat; 1311 const char *dir; 1312 const char *repository; 1313 const char *update_dir; 1314 List *entries; 1315{ 1316 /* This is not quite right--it doesn't get right the case of "cvs 1317 update -d -r foobar" where foobar is a tag which exists only in 1318 files in a directory which does not exist yet, but which is 1319 about to be created. */ 1320 if (isdir (dir)) 1321 return R_PROCESS; 1322 return R_SKIP_ALL; 1323} 1324 1325/* Check to see whether NAME is a valid tag. If so, return. If not 1326 print an error message and exit. ARGC, ARGV, LOCAL, and AFLAG specify 1327 which files we will be operating on. 1328 1329 REPOSITORY is the repository if we need to cd into it, or NULL if 1330 we are already there, or "" if we should do a W_LOCAL recursion. 1331 Sorry for three cases, but the "" case is needed in case the 1332 working directories come from diverse parts of the repository, the 1333 NULL case avoids an unneccesary chdir, and the non-NULL, non-"" 1334 case is needed for checkout, where we don't want to chdir if the 1335 tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any 1336 local directory. */ 1337void 1338tag_check_valid (name, argc, argv, local, aflag, repository) 1339 char *name; 1340 int argc; 1341 char **argv; 1342 int local; 1343 int aflag; 1344 char *repository; 1345{ 1346 struct val_args the_val_args; 1347 struct saved_cwd cwd; 1348 int which; 1349 1350 /* Numeric tags require only a syntactic check. */ 1351 if (isdigit ((unsigned char) name[0])) 1352 { 1353 char *p; 1354 for (p = name; *p != '\0'; ++p) 1355 { 1356 if (!(isdigit ((unsigned char) *p) || *p == '.')) 1357 error (1, 0, "\ 1358Numeric tag %s contains characters other than digits and '.'", name); 1359 } 1360 return; 1361 } 1362 1363 /* Special tags are always valid. */ 1364 if (strcmp (name, TAG_BASE) == 0 1365 || strcmp (name, TAG_HEAD) == 0) 1366 return; 1367 1368 if (readonlyfs) 1369 return; 1370 1371 /* Verify that the tag is valid syntactically. Some later code once made 1372 * assumptions about this. 1373 */ 1374 RCS_check_tag (name); 1375 1376 if (is_in_val_tags (NULL, name)) return; 1377 1378 /* We didn't find the tag in val-tags, so look through all the RCS files 1379 to see whether it exists there. Yes, this is expensive, but there 1380 is no other way to cope with a tag which might have been created 1381 by an old version of CVS, from before val-tags was invented. 1382 1383 Since we need this code anyway, we also use it to create 1384 entries in val-tags in general (that is, the val-tags entry 1385 will get created the first time the tag is used, not when the 1386 tag is created). */ 1387 1388 the_val_args.name = name; 1389 the_val_args.found = 0; 1390 1391 which = W_REPOS | W_ATTIC; 1392 1393 if (repository != NULL) 1394 { 1395 if (repository[0] == '\0') 1396 which |= W_LOCAL; 1397 else 1398 { 1399 if (save_cwd (&cwd)) 1400 error_exit (); 1401 if (CVS_CHDIR (repository) < 0) 1402 error (1, errno, "cannot change to %s directory", repository); 1403 } 1404 } 1405 1406 start_recursion (val_fileproc, (FILESDONEPROC) NULL, 1407 val_direntproc, (DIRLEAVEPROC) NULL, 1408 (void *)&the_val_args, 1409 argc, argv, local, which, aflag, 1410 CVS_LOCK_READ, NULL, 1, repository); 1411 if (repository != NULL && repository[0] != '\0') 1412 { 1413 if (restore_cwd (&cwd, NULL)) 1414 error_exit (); 1415 free_cwd (&cwd); 1416 } 1417 1418 if (!the_val_args.found) 1419 error (1, 0, "no such tag %s", name); 1420 else 1421 /* The tags is valid but not mentioned in val-tags. Add it. */ 1422 add_to_val_tags (name); 1423} 1424 1425 1426 1427/* 1428 * Check whether a join tag is valid. This is just like 1429 * tag_check_valid, but we must stop before the colon if there is one. 1430 */ 1431 1432void 1433tag_check_valid_join (join_tag, argc, argv, local, aflag, repository) 1434 char *join_tag; 1435 int argc; 1436 char **argv; 1437 int local; 1438 int aflag; 1439 char *repository; 1440{ 1441 char *c, *s; 1442 1443 c = xstrdup (join_tag); 1444 s = strchr (c, ':'); 1445 if (s != NULL) 1446 { 1447 if (isdigit ((unsigned char) join_tag[0])) 1448 error (1, 0, 1449 "Numeric join tag %s may not contain a date specifier", 1450 join_tag); 1451 1452 *s = '\0'; 1453 /* hmmm... I think it makes sense to allow -j:<date>, but 1454 * for now this fixes a bug where CVS just spins and spins (I 1455 * think in the RCS code) looking for a zero length tag. 1456 */ 1457 if (!*c) 1458 error (1, 0, 1459 "argument to join may not contain a date specifier without a tag"); 1460 } 1461 1462 tag_check_valid (c, argc, argv, local, aflag, repository); 1463 1464 free (c); 1465} 1466