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