tag.c revision 54427
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 9 * 10 * Add or delete a symbolic name to an RCS file, or a collection of RCS files. 11 * Uses the checked out revision in the current directory. 12 */ 13 14#include "cvs.h" 15#include "savecwd.h" 16 17static int check_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 18static int check_filesdoneproc PROTO ((void *callerdat, int err, 19 char *repos, char *update_dir, 20 List *entries)); 21static int pretag_proc PROTO((char *repository, char *filter)); 22static void masterlist_delproc PROTO((Node *p)); 23static void tag_delproc PROTO((Node *p)); 24static int pretag_list_proc PROTO((Node *p, void *closure)); 25 26static Dtype tag_dirproc PROTO ((void *callerdat, char *dir, 27 char *repos, char *update_dir, 28 List *entries)); 29static int tag_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 30static int tag_filesdoneproc PROTO ((void *callerdat, int err, 31 char *repos, char *update_dir, 32 List *entries)); 33 34static char *numtag; 35static char *date = NULL; 36static char *symtag; 37static int delete_flag; /* adding a tag by default */ 38static int branch_mode; /* make an automagic "branch" tag */ 39static int local; /* recursive by default */ 40static int force_tag_match = 1; /* force tag to match by default */ 41static int force_tag_move; /* don't force tag to move by default */ 42static int check_uptodate; /* no uptodate-check by default */ 43 44struct tag_info 45{ 46 Ctype status; 47 char *rev; 48 char *tag; 49 char *options; 50}; 51 52struct master_lists 53{ 54 List *tlist; 55}; 56 57static List *mtlist; 58static List *tlist; 59 60static const char *const tag_usage[] = 61{ 62 "Usage: %s %s [-lRF] [-b] [-d] [-c] [-r rev|-D date] tag [files...]\n", 63 "\t-l\tLocal directory only, not recursive.\n", 64 "\t-R\tProcess directories recursively.\n", 65 "\t-d\tDelete the given tag.\n", 66 "\t-r rev\tExisting revision/tag.\n", 67 "\t-D\tExisting date.\n", 68 "\t-f\tForce a head revision if specified tag not found.\n", 69 "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", 70 "\t-F\tMove tag if it already exists.\n", 71 "\t-c\tCheck that working files are unmodified.\n", 72 "(Specify the --help global option for a list of other help options)\n", 73 NULL 74}; 75 76int 77cvstag (argc, argv) 78 int argc; 79 char **argv; 80{ 81 int c; 82 int err = 0; 83 84 if (argc == -1) 85 usage (tag_usage); 86 87 optind = 0; 88 while ((c = getopt (argc, argv, "+FQqlRcdr:D:bf")) != -1) 89 { 90 switch (c) 91 { 92 case 'Q': 93 case 'q': 94#ifdef SERVER_SUPPORT 95 /* The CVS 1.5 client sends these options (in addition to 96 Global_option requests), so we must ignore them. */ 97 if (!server_active) 98#endif 99 error (1, 0, 100 "-q or -Q must be specified before \"%s\"", 101 command_name); 102 break; 103 case 'l': 104 local = 1; 105 break; 106 case 'R': 107 local = 0; 108 break; 109 case 'd': 110 delete_flag = 1; 111 break; 112 case 'c': 113 check_uptodate = 1; 114 break; 115 case 'r': 116 numtag = optarg; 117 break; 118 case 'D': 119 if (date) 120 free (date); 121 date = Make_Date (optarg); 122 break; 123 case 'f': 124 force_tag_match = 0; 125 break; 126 case 'b': 127 branch_mode = 1; 128 break; 129 case 'F': 130 force_tag_move = 1; 131 break; 132 case '?': 133 default: 134 usage (tag_usage); 135 break; 136 } 137 } 138 argc -= optind; 139 argv += optind; 140 141 if (argc == 0) 142 usage (tag_usage); 143 symtag = argv[0]; 144 argc--; 145 argv++; 146 147 if (date && numtag) 148 error (1, 0, "-r and -D options are mutually exclusive"); 149 if (delete_flag && branch_mode) 150 error (0, 0, "warning: -b ignored with -d options"); 151 RCS_check_tag (symtag); 152 153#ifdef CLIENT_SUPPORT 154 if (client_active) 155 { 156 /* We're the client side. Fire up the remote server. */ 157 start_server (); 158 159 ign_setup (); 160 161 if (!force_tag_match) 162 send_arg ("-f"); 163 if (local) 164 send_arg("-l"); 165 if (delete_flag) 166 send_arg("-d"); 167 if (check_uptodate) 168 send_arg("-c"); 169 if (branch_mode) 170 send_arg("-b"); 171 if (force_tag_move) 172 send_arg("-F"); 173 174 if (numtag) 175 option_with_arg ("-r", numtag); 176 if (date) 177 client_senddate (date); 178 179 send_arg (symtag); 180 181 send_files (argc, argv, local, 0, 182 183 /* I think the -c case is like "cvs status", in 184 which we really better be correct rather than 185 being fast; it is just too confusing otherwise. */ 186 check_uptodate ? 0 : SEND_NO_CONTENTS); 187 send_file_names (argc, argv, SEND_EXPAND_WILD); 188 send_to_server ("tag\012", 0); 189 return get_responses_and_close (); 190 } 191#endif 192 193 if (numtag != NULL) 194 tag_check_valid (numtag, argc, argv, local, 0, ""); 195 196 /* check to make sure they are authorized to tag all the 197 specified files in the repository */ 198 199 mtlist = getlist(); 200 err = start_recursion (check_fileproc, check_filesdoneproc, 201 (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, 202 argc, argv, local, W_LOCAL, 0, 1, 203 (char *) NULL, 1); 204 205 if (err) 206 { 207 error (1, 0, "correct the above errors first!"); 208 } 209 210 /* start the recursion processor */ 211 err = start_recursion (tag_fileproc, tag_filesdoneproc, tag_dirproc, 212 (DIRLEAVEPROC) NULL, NULL, argc, argv, local, 213 W_LOCAL, 0, 0, (char *) NULL, 1); 214 dellist(&mtlist); 215 return (err); 216} 217 218/* check file that is to be tagged */ 219/* All we do here is add it to our list */ 220 221static int 222check_fileproc (callerdat, finfo) 223 void *callerdat; 224 struct file_info *finfo; 225{ 226 char *xdir; 227 Node *p; 228 Vers_TS *vers; 229 230 if (check_uptodate) 231 { 232 Ctype status = Classify_File (finfo, (char *) NULL, (char *) NULL, 233 (char *) NULL, 1, 0, &vers, 0); 234 if ((status != T_UPTODATE) && (status != T_CHECKOUT)) 235 { 236 error (0, 0, "%s is locally modified", finfo->fullname); 237 return (1); 238 } 239 } 240 241 if (finfo->update_dir[0] == '\0') 242 xdir = "."; 243 else 244 xdir = finfo->update_dir; 245 if ((p = findnode (mtlist, xdir)) != NULL) 246 { 247 tlist = ((struct master_lists *) p->data)->tlist; 248 } 249 else 250 { 251 struct master_lists *ml; 252 253 tlist = getlist (); 254 p = getnode (); 255 p->key = xstrdup (xdir); 256 p->type = UPDATE; 257 ml = (struct master_lists *) 258 xmalloc (sizeof (struct master_lists)); 259 ml->tlist = tlist; 260 p->data = (char *) ml; 261 p->delproc = masterlist_delproc; 262 (void) addnode (mtlist, p); 263 } 264 /* do tlist */ 265 p = getnode (); 266 p->key = xstrdup (finfo->file); 267 p->type = UPDATE; 268 p->delproc = tag_delproc; 269 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); 270 if (vers->srcfile == NULL) 271 { 272 if (!really_quiet) 273 error (0, 0, "nothing known about %s", finfo->file); 274 return (1); 275 } 276 277 /* Here we duplicate the calculation in tag_fileproc about which 278 version we are going to tag. There probably are some subtle races 279 (e.g. numtag is "foo" which gets moved between here and 280 tag_fileproc). */ 281 if (numtag == NULL && date == NULL) 282 p->data = xstrdup (vers->vn_user); 283 else 284 p->data = RCS_getversion (vers->srcfile, numtag, date, 285 force_tag_match, NULL); 286 287 if (p->data != NULL) 288 { 289 int addit = 1; 290 char *oversion; 291 292 oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 293 (int *) NULL); 294 if (oversion == NULL) 295 { 296 if (delete_flag) 297 { 298 /* Deleting a tag which did not exist is a noop and 299 should not be logged. */ 300 addit = 0; 301 } 302 } 303 else if (delete_flag) 304 { 305 free (p->data); 306 p->data = xstrdup (oversion); 307 } 308 else if (strcmp(oversion, p->data) == 0) 309 { 310 addit = 0; 311 } 312 else if (!force_tag_move) 313 { 314 addit = 0; 315 } 316 if (oversion != NULL) 317 { 318 free(oversion); 319 } 320 if (!addit) 321 { 322 free(p->data); 323 p->data = NULL; 324 } 325 } 326 freevers_ts(&vers); 327 (void) addnode (tlist, p); 328 return (0); 329} 330 331static int 332check_filesdoneproc (callerdat, err, repos, update_dir, entries) 333 void *callerdat; 334 int err; 335 char *repos; 336 char *update_dir; 337 List *entries; 338{ 339 int n; 340 Node *p; 341 342 p = findnode(mtlist, update_dir); 343 if (p != NULL) 344 { 345 tlist = ((struct master_lists *) p->data)->tlist; 346 } 347 else 348 { 349 tlist = (List *) NULL; 350 } 351 if ((tlist == NULL) || (tlist->list->next == tlist->list)) 352 { 353 return (err); 354 } 355 if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0) 356 { 357 error (0, 0, "Pre-tag check failed"); 358 err += n; 359 } 360 return (err); 361} 362 363static int 364pretag_proc(repository, filter) 365 char *repository; 366 char *filter; 367{ 368 if (filter[0] == '/') 369 { 370 char *s, *cp; 371 372 s = xstrdup(filter); 373 for (cp=s; *cp; cp++) 374 { 375 if (isspace ((unsigned char) *cp)) 376 { 377 *cp = '\0'; 378 break; 379 } 380 } 381 if (!isfile(s)) 382 { 383 error (0, errno, "cannot find pre-tag filter '%s'", s); 384 free(s); 385 return (1); 386 } 387 free(s); 388 } 389 run_setup (filter); 390 run_arg (symtag); 391 run_arg (delete_flag ? "del" : force_tag_move ? "mov" : "add"); 392 run_arg (repository); 393 walklist(tlist, pretag_list_proc, NULL); 394 return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)); 395} 396 397static void 398masterlist_delproc(p) 399 Node *p; 400{ 401 struct master_lists *ml; 402 403 ml = (struct master_lists *)p->data; 404 dellist(&ml->tlist); 405 free(ml); 406 return; 407} 408 409static void 410tag_delproc(p) 411 Node *p; 412{ 413 if (p->data != NULL) 414 { 415 free(p->data); 416 p->data = NULL; 417 } 418 return; 419} 420 421static int 422pretag_list_proc(p, closure) 423 Node *p; 424 void *closure; 425{ 426 if (p->data != NULL) 427 { 428 run_arg(p->key); 429 run_arg(p->data); 430 } 431 return (0); 432} 433 434 435/* 436 * Called to tag a particular file (the currently checked out version is 437 * tagged with the specified tag - or the specified tag is deleted). 438 */ 439/* ARGSUSED */ 440static int 441tag_fileproc (callerdat, finfo) 442 void *callerdat; 443 struct file_info *finfo; 444{ 445 char *version, *oversion; 446 char *nversion = NULL; 447 char *rev; 448 Vers_TS *vers; 449 int retcode = 0; 450 451 /* Lock the directory if it is not already locked. We can't rely 452 on tag_dirproc because it won't handle the case where the user 453 specifies a list of files on the command line. */ 454 /* We do not need to acquire a full write lock for the tag operation: 455 the revisions are obtained from the working directory, so we do not 456 require consistency across the entire repository. However, we do 457 need to prevent simultaneous tag operations from interfering with 458 each other. Therefore, we write lock each directory as we enter 459 it, and unlock it as we leave it. */ 460 lock_dir_for_write (finfo->repository); 461 462 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); 463 464 if ((numtag != NULL) || (date != NULL)) 465 { 466 nversion = RCS_getversion(vers->srcfile, 467 numtag, 468 date, 469 force_tag_match, 470 (int *) NULL); 471 if (nversion == NULL) 472 { 473 freevers_ts (&vers); 474 return (0); 475 } 476 } 477 if (delete_flag) 478 { 479 480 /* 481 * If -d is specified, "force_tag_match" is set, so that this call to 482 * RCS_getversion() will return a NULL version string if the symbolic 483 * tag does not exist in the RCS file. 484 * 485 * This is done here because it's MUCH faster than just blindly calling 486 * "rcs" to remove the tag... trust me. 487 */ 488 489 version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 490 (int *) NULL); 491 if (version == NULL || vers->srcfile == NULL) 492 { 493 freevers_ts (&vers); 494 return (0); 495 } 496 free (version); 497 498 if ((retcode = RCS_deltag(vers->srcfile, symtag)) != 0) 499 { 500 if (!quiet) 501 error (0, retcode == -1 ? errno : 0, 502 "failed to remove tag %s from %s", symtag, 503 vers->srcfile->path); 504 freevers_ts (&vers); 505 return (1); 506 } 507 RCS_rewrite (vers->srcfile, NULL, NULL); 508 509 /* warm fuzzies */ 510 if (!really_quiet) 511 { 512 cvs_output ("D ", 2); 513 cvs_output (finfo->fullname, 0); 514 cvs_output ("\n", 1); 515 } 516 517 freevers_ts (&vers); 518 return (0); 519 } 520 521 /* 522 * If we are adding a tag, we need to know which version we have checked 523 * out and we'll tag that version. 524 */ 525 if (nversion == NULL) 526 { 527 version = vers->vn_user; 528 } 529 else 530 { 531 version = nversion; 532 } 533 if (version == NULL) 534 { 535 freevers_ts (&vers); 536 return (0); 537 } 538 else if (strcmp (version, "0") == 0) 539 { 540 if (!quiet) 541 error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file); 542 freevers_ts (&vers); 543 return (0); 544 } 545 else if (version[0] == '-') 546 { 547 if (!quiet) 548 error (0, 0, "skipping removed but un-commited file `%s'", finfo->file); 549 freevers_ts (&vers); 550 return (0); 551 } 552 else if (vers->srcfile == NULL) 553 { 554 if (!quiet) 555 error (0, 0, "cannot find revision control file for `%s'", finfo->file); 556 freevers_ts (&vers); 557 return (0); 558 } 559 560 /* 561 * As an enhancement for the case where a tag is being re-applied to a 562 * large number of files, make one extra call to RCS_getversion to see 563 * if the tag is already set in the RCS file. If so, check to see if it 564 * needs to be moved. If not, do nothing. This will likely save a lot of 565 * time when simply moving the tag to the "current" head revisions of a 566 * module -- which I have found to be a typical tagging operation. 567 */ 568 rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version; 569 oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 570 (int *) NULL); 571 if (oversion != NULL) 572 { 573 int isbranch = RCS_nodeisbranch (finfo->rcs, symtag); 574 575 /* 576 * if versions the same and neither old or new are branches don't have 577 * to do anything 578 */ 579 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch) 580 { 581 free (oversion); 582 freevers_ts (&vers); 583 return (0); 584 } 585 586 if (!force_tag_move) 587 { 588 /* we're NOT going to move the tag */ 589 cvs_output ("W ", 2); 590 cvs_output (finfo->fullname, 0); 591 cvs_output (" : ", 0); 592 cvs_output (symtag, 0); 593 cvs_output (" already exists on ", 0); 594 cvs_output (isbranch ? "branch" : "version", 0); 595 cvs_output (" ", 0); 596 cvs_output (oversion, 0); 597 cvs_output (" : NOT MOVING tag to ", 0); 598 cvs_output (branch_mode ? "branch" : "version", 0); 599 cvs_output (" ", 0); 600 cvs_output (rev, 0); 601 cvs_output ("\n", 1); 602 free (oversion); 603 freevers_ts (&vers); 604 return (0); 605 } 606 free (oversion); 607 } 608 609 if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0) 610 { 611 error (1, retcode == -1 ? errno : 0, 612 "failed to set tag %s to revision %s in %s", 613 symtag, rev, vers->srcfile->path); 614 freevers_ts (&vers); 615 return (1); 616 } 617 RCS_rewrite (vers->srcfile, NULL, NULL); 618 619 /* more warm fuzzies */ 620 if (!really_quiet) 621 { 622 cvs_output ("T ", 2); 623 cvs_output (finfo->fullname, 0); 624 cvs_output ("\n", 1); 625 } 626 627 if (nversion != NULL) 628 { 629 free (nversion); 630 } 631 freevers_ts (&vers); 632 return (0); 633} 634 635/* Clear any lock we may hold on the current directory. */ 636 637static int 638tag_filesdoneproc (callerdat, err, repos, update_dir, entries) 639 void *callerdat; 640 int err; 641 char *repos; 642 char *update_dir; 643 List *entries; 644{ 645 Lock_Cleanup (); 646 647 return (err); 648} 649 650/* 651 * Print a warm fuzzy message 652 */ 653/* ARGSUSED */ 654static Dtype 655tag_dirproc (callerdat, dir, repos, update_dir, entries) 656 void *callerdat; 657 char *dir; 658 char *repos; 659 char *update_dir; 660 List *entries; 661{ 662 if (!quiet) 663 error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir); 664 return (R_PROCESS); 665} 666 667/* Code relating to the val-tags file. Note that this file has no way 668 of knowing when a tag has been deleted. The problem is that there 669 is no way of knowing whether a tag still exists somewhere, when we 670 delete it some places. Using per-directory val-tags files (in 671 CVSREP) might be better, but that might slow down the process of 672 verifying that a tag is correct (maybe not, for the likely cases, 673 if carefully done), and/or be harder to implement correctly. */ 674 675struct val_args { 676 char *name; 677 int found; 678}; 679 680static int val_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 681 682static int 683val_fileproc (callerdat, finfo) 684 void *callerdat; 685 struct file_info *finfo; 686{ 687 RCSNode *rcsdata; 688 struct val_args *args = (struct val_args *)callerdat; 689 char *tag; 690 691 if ((rcsdata = finfo->rcs) == NULL) 692 /* Not sure this can happen, after all we passed only 693 W_REPOS | W_ATTIC. */ 694 return 0; 695 696 tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL); 697 if (tag != NULL) 698 { 699 /* FIXME: should find out a way to stop the search at this point. */ 700 args->found = 1; 701 free (tag); 702 } 703 return 0; 704} 705 706static Dtype val_direntproc PROTO ((void *, char *, char *, char *, List *)); 707 708static Dtype 709val_direntproc (callerdat, dir, repository, update_dir, entries) 710 void *callerdat; 711 char *dir; 712 char *repository; 713 char *update_dir; 714 List *entries; 715{ 716 /* This is not quite right--it doesn't get right the case of "cvs 717 update -d -r foobar" where foobar is a tag which exists only in 718 files in a directory which does not exist yet, but which is 719 about to be created. */ 720 if (isdir (dir)) 721 return 0; 722 return R_SKIP_ALL; 723} 724 725/* Check to see whether NAME is a valid tag. If so, return. If not 726 print an error message and exit. ARGC, ARGV, LOCAL, and AFLAG specify 727 which files we will be operating on. 728 729 REPOSITORY is the repository if we need to cd into it, or NULL if 730 we are already there, or "" if we should do a W_LOCAL recursion. 731 Sorry for three cases, but the "" case is needed in case the 732 working directories come from diverse parts of the repository, the 733 NULL case avoids an unneccesary chdir, and the non-NULL, non-"" 734 case is needed for checkout, where we don't want to chdir if the 735 tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any 736 local directory. */ 737void 738tag_check_valid (name, argc, argv, local, aflag, repository) 739 char *name; 740 int argc; 741 char **argv; 742 int local; 743 int aflag; 744 char *repository; 745{ 746 DBM *db; 747 char *valtags_filename; 748 int err; 749 datum mytag; 750 struct val_args the_val_args; 751 struct saved_cwd cwd; 752 int which; 753 754 /* Numeric tags require only a syntactic check. */ 755 if (isdigit ((unsigned char) name[0])) 756 { 757 char *p; 758 for (p = name; *p != '\0'; ++p) 759 { 760 if (!(isdigit ((unsigned char) *p) || *p == '.')) 761 error (1, 0, "\ 762Numeric tag %s contains characters other than digits and '.'", name); 763 } 764 return; 765 } 766 767 /* Special tags are always valid. */ 768 if (strcmp (name, TAG_BASE) == 0 769 || strcmp (name, TAG_HEAD) == 0) 770 return; 771 772 /* FIXME: This routine doesn't seem to do any locking whatsoever 773 (and it is called from places which don't have locks in place). 774 If two processes try to write val-tags at the same time, it would 775 seem like we are in trouble. */ 776 777 mytag.dptr = name; 778 mytag.dsize = strlen (name); 779 780 valtags_filename = xmalloc (strlen (CVSroot_directory) 781 + sizeof CVSROOTADM 782 + sizeof CVSROOTADM_VALTAGS + 20); 783 strcpy (valtags_filename, CVSroot_directory); 784 strcat (valtags_filename, "/"); 785 strcat (valtags_filename, CVSROOTADM); 786 strcat (valtags_filename, "/"); 787 strcat (valtags_filename, CVSROOTADM_VALTAGS); 788 db = dbm_open (valtags_filename, O_RDWR, 0666); 789 if (db == NULL) 790 { 791 if (!existence_error (errno)) 792 error (1, errno, "cannot read %s", valtags_filename); 793 794 /* If the file merely fails to exist, we just keep going and create 795 it later if need be. */ 796 } 797 else 798 { 799 datum val; 800 801 val = dbm_fetch (db, mytag); 802 if (val.dptr != NULL) 803 { 804 /* Found. The tag is valid. */ 805 dbm_close (db); 806 free (valtags_filename); 807 return; 808 } 809 /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */ 810 } 811 812 /* We didn't find the tag in val-tags, so look through all the RCS files 813 to see whether it exists there. Yes, this is expensive, but there 814 is no other way to cope with a tag which might have been created 815 by an old version of CVS, from before val-tags was invented. 816 817 Since we need this code anyway, we also use it to create 818 entries in val-tags in general (that is, the val-tags entry 819 will get created the first time the tag is used, not when the 820 tag is created). */ 821 822 the_val_args.name = name; 823 the_val_args.found = 0; 824 825 which = W_REPOS | W_ATTIC; 826 827 if (repository != NULL) 828 { 829 if (repository[0] == '\0') 830 which |= W_LOCAL; 831 else 832 { 833 if (save_cwd (&cwd)) 834 error_exit (); 835 if ( CVS_CHDIR (repository) < 0) 836 error (1, errno, "cannot change to %s directory", repository); 837 } 838 } 839 840 err = start_recursion (val_fileproc, (FILESDONEPROC) NULL, 841 val_direntproc, (DIRLEAVEPROC) NULL, 842 (void *)&the_val_args, 843 argc, argv, local, which, aflag, 844 1, NULL, 1); 845 if (repository != NULL && repository[0] != '\0') 846 { 847 if (restore_cwd (&cwd, NULL)) 848 exit (EXIT_FAILURE); 849 free_cwd (&cwd); 850 } 851 852 if (!the_val_args.found) 853 error (1, 0, "no such tag %s", name); 854 else 855 { 856 /* The tags is valid but not mentioned in val-tags. Add it. */ 857 datum value; 858 859 if (noexec) 860 { 861 if (db != NULL) 862 dbm_close (db); 863 free (valtags_filename); 864 return; 865 } 866 867 if (db == NULL) 868 { 869 mode_t omask; 870 omask = umask (cvsumask); 871 db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666); 872 (void) umask (omask); 873 874 if (db == NULL) 875 { 876 error (0, errno, "cannot create %s", valtags_filename); 877 free (valtags_filename); 878 return; 879 } 880 } 881 value.dptr = "y"; 882 value.dsize = 1; 883 if (dbm_store (db, mytag, value, DBM_REPLACE) < 0) 884 error (0, errno, "cannot store %s into %s", name, 885 valtags_filename); 886 dbm_close (db); 887 } 888 free (valtags_filename); 889} 890 891/* 892 * Check whether a join tag is valid. This is just like 893 * tag_check_valid, but we must stop before the colon if there is one. 894 */ 895 896void 897tag_check_valid_join (join_tag, argc, argv, local, aflag, repository) 898 char *join_tag; 899 int argc; 900 char **argv; 901 int local; 902 int aflag; 903 char *repository; 904{ 905 char *c, *s; 906 907 c = xstrdup (join_tag); 908 s = strchr (c, ':'); 909 if (s != NULL) 910 { 911 if (isdigit ((unsigned char) join_tag[0])) 912 error (1, 0, 913 "Numeric join tag %s may not contain a date specifier", 914 join_tag); 915 916 *s = '\0'; 917 /* hmmm... I think it makes sense to allow -j:<date>, but 918 * for now this fixes a bug where CVS just spins and spins (I 919 * think in the RCS code) looking for a zero length tag. 920 */ 921 if (!*c) 922 error (1, 0, 923 "argument to join may not contain a date specifier without a tag"); 924 } 925 926 tag_check_valid (c, argc, argv, local, aflag, repository); 927 928 free (c); 929} 930