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