tag.c revision 17721
1193323Sed/* 2193323Sed * Copyright (c) 1992, Brian Berliner and Jeff Polk 3193323Sed * Copyright (c) 1989-1992, Brian Berliner 4193323Sed * 5193323Sed * You may distribute under the terms of the GNU General Public License as 6193323Sed * specified in the README file that comes with the CVS 1.4 kit. 7193323Sed * 8193323Sed * Tag 9193323Sed * 10193323Sed * Add or delete a symbolic name to an RCS file, or a collection of RCS files. 11193323Sed * Uses the checked out revision in the current directory. 12193323Sed */ 13193323Sed 14193323Sed#include "cvs.h" 15193323Sed#include "savecwd.h" 16193323Sed 17193323Sedstatic int check_fileproc PROTO((struct file_info *finfo)); 18193323Sedstatic int check_filesdoneproc PROTO((int err, char *repos, char *update_dir)); 19198090Srdivackystatic int pretag_proc PROTO((char *repository, char *filter)); 20193323Sedstatic void masterlist_delproc PROTO((Node *p)); 21193323Sedstatic void tag_delproc PROTO((Node *p)); 22193323Sedstatic int pretag_list_proc PROTO((Node *p, void *closure)); 23193323Sed 24198090Srdivackystatic Dtype tag_dirproc PROTO((char *dir, char *repos, char *update_dir)); 25193323Sedstatic int tag_fileproc PROTO((struct file_info *finfo)); 26193323Sed 27193323Sedstatic char *numtag; 28198090Srdivackystatic char *date = NULL; 29193323Sedstatic char *symtag; 30193323Sedstatic int delete_flag; /* adding a tag by default */ 31193323Sedstatic int branch_mode; /* make an automagic "branch" tag */ 32193323Sedstatic int local; /* recursive by default */ 33193323Sedstatic int force_tag_match = 1; /* force tag to match by default */ 34193323Sedstatic int force_tag_move; /* don't force tag to move by default */ 35195340Sed 36193323Sedstruct tag_info 37193323Sed{ 38193323Sed Ctype status; 39195340Sed char *rev; 40193323Sed char *tag; 41193323Sed char *options; 42193323Sed}; 43198090Srdivacky 44198090Srdivackystruct master_lists 45198090Srdivacky{ 46198090Srdivacky List *tlist; 47198090Srdivacky}; 48198090Srdivacky 49198090Srdivackystatic List *mtlist; 50198090Srdivackystatic List *tlist; 51193323Sed 52198090Srdivackystatic const char *const tag_usage[] = 53198090Srdivacky{ 54198090Srdivacky "Usage: %s %s [-lRF] [-b] [-d] [-r tag|-D date] tag [files...]\n", 55198090Srdivacky "\t-l\tLocal directory only, not recursive.\n", 56198090Srdivacky "\t-R\tProcess directories recursively.\n", 57198090Srdivacky "\t-d\tDelete the given Tag.\n", 58198090Srdivacky "\t-[rD]\tExisting tag or date.\n", 59198090Srdivacky "\t-f\tForce a head revision if tag etc not found.\n", 60198090Srdivacky "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", 61198090Srdivacky "\t-F\tMove tag if it already exists\n", 62198090Srdivacky NULL 63198090Srdivacky}; 64198090Srdivacky 65198090Srdivackyint 66198090Srdivackytag (argc, argv) 67207618Srdivacky int argc; 68198090Srdivacky char **argv; 69198090Srdivacky{ 70198090Srdivacky int c; 71198090Srdivacky int err = 0; 72198090Srdivacky 73198090Srdivacky if (argc == -1) 74198090Srdivacky usage (tag_usage); 75193323Sed 76193323Sed optind = 1; 77193323Sed while ((c = getopt (argc, argv, "FQqlRdr:D:bf")) != -1) 78193323Sed { 79193323Sed switch (c) 80193323Sed { 81193323Sed case 'Q': 82193323Sed case 'q': 83193323Sed#ifdef SERVER_SUPPORT 84193323Sed /* The CVS 1.5 client sends these options (in addition to 85193323Sed Global_option requests), so we must ignore them. */ 86193323Sed if (!server_active) 87198090Srdivacky#endif 88193323Sed error (1, 0, 89193323Sed "-q or -Q must be specified before \"%s\"", 90193323Sed command_name); 91193323Sed break; 92193323Sed case 'l': 93193323Sed local = 1; 94193323Sed break; 95193323Sed case 'R': 96193323Sed local = 0; 97193323Sed break; 98193323Sed case 'd': 99193323Sed delete_flag = 1; 100193323Sed break; 101193323Sed case 'r': 102193323Sed numtag = optarg; 103199481Srdivacky break; 104199481Srdivacky case 'D': 105193323Sed if (date) 106193323Sed free (date); 107193323Sed date = Make_Date (optarg); 108193323Sed break; 109198090Srdivacky case 'f': 110198090Srdivacky force_tag_match = 0; 111198090Srdivacky break; 112198090Srdivacky case 'b': 113193323Sed branch_mode = 1; 114193323Sed break; 115198090Srdivacky case 'F': 116198090Srdivacky force_tag_move = 1; 117198090Srdivacky break; 118198090Srdivacky case '?': 119193323Sed default: 120193323Sed usage (tag_usage); 121193323Sed break; 122193323Sed } 123193323Sed } 124193323Sed argc -= optind; 125193323Sed argv += optind; 126193323Sed 127193323Sed if (argc == 0) 128200581Srdivacky usage (tag_usage); 129193323Sed symtag = argv[0]; 130193323Sed argc--; 131193323Sed argv++; 132193323Sed 133193323Sed if (date && numtag) 134193323Sed error (1, 0, "-r and -D options are mutually exclusive"); 135193323Sed if (delete_flag && branch_mode) 136193323Sed error (0, 0, "warning: -b ignored with -d options"); 137193323Sed RCS_check_tag (symtag); 138193323Sed 139207618Srdivacky#ifdef CLIENT_SUPPORT 140207618Srdivacky if (client_active) 141207618Srdivacky { 142198090Srdivacky /* We're the client side. Fire up the remote server. */ 143198090Srdivacky start_server (); 144210299Sed 145210299Sed ign_setup (); 146210299Sed 147210299Sed if (local) 148198090Srdivacky send_arg("-l"); 149198090Srdivacky if (delete_flag) 150198090Srdivacky send_arg("-d"); 151198090Srdivacky if (branch_mode) 152193323Sed send_arg("-b"); 153193323Sed if (force_tag_move) 154198090Srdivacky send_arg("-F"); 155193323Sed 156193323Sed if (numtag) 157198090Srdivacky option_with_arg ("-r", numtag); 158195340Sed if (date) 159193323Sed client_senddate (date); 160198090Srdivacky 161198090Srdivacky send_arg (symtag); 162210299Sed 163198090Srdivacky send_file_names (argc, argv, SEND_EXPAND_WILD); 164198090Srdivacky /* FIXME: We shouldn't have to send current files, but I'm not sure 165198090Srdivacky whether it works. So send the files -- 166210299Sed it's slower but it works. */ 167198090Srdivacky send_files (argc, argv, local, 0); 168198090Srdivacky send_to_server ("tag\012", 0); 169198090Srdivacky return get_responses_and_close (); 170198090Srdivacky } 171198090Srdivacky#endif 172193323Sed 173193323Sed if (numtag != NULL) 174193323Sed tag_check_valid (numtag, argc, argv, local, 0, ""); 175198090Srdivacky 176193323Sed /* check to make sure they are authorized to tag all the 177193323Sed specified files in the repository */ 178198090Srdivacky 179193323Sed mtlist = getlist(); 180198090Srdivacky err = start_recursion (check_fileproc, check_filesdoneproc, 181198090Srdivacky (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, 182195340Sed argc, argv, local, W_LOCAL, 0, 1, 183198090Srdivacky (char *) NULL, 1, 0); 184198892Srdivacky 185198892Srdivacky if (err) 186198892Srdivacky { 187198892Srdivacky error (1, 0, "correct the above errors first!"); 188198892Srdivacky } 189198892Srdivacky 190198892Srdivacky /* start the recursion processor */ 191198892Srdivacky err = start_recursion (tag_fileproc, (FILESDONEPROC) NULL, tag_dirproc, 192198892Srdivacky (DIRLEAVEPROC) NULL, argc, argv, local, 193198892Srdivacky W_LOCAL, 0, 1, (char *) NULL, 1, 0); 194198892Srdivacky dellist(&mtlist); 195198892Srdivacky return (err); 196198892Srdivacky} 197198892Srdivacky 198198892Srdivacky/* check file that is to be tagged */ 199198892Srdivacky/* All we do here is add it to our list */ 200198892Srdivacky 201198892Srdivackystatic int 202198090Srdivackycheck_fileproc (finfo) 203198090Srdivacky struct file_info *finfo; 204198090Srdivacky{ 205198090Srdivacky char *xdir; 206198090Srdivacky Node *p; 207198090Srdivacky Vers_TS *vers; 208198090Srdivacky 209198090Srdivacky if (finfo->update_dir[0] == '\0') 210198090Srdivacky xdir = "."; 211198090Srdivacky else 212198090Srdivacky xdir = finfo->update_dir; 213198090Srdivacky if ((p = findnode (mtlist, xdir)) != NULL) 214193323Sed { 215193323Sed tlist = ((struct master_lists *) p->data)->tlist; 216198090Srdivacky } 217198090Srdivacky else 218198090Srdivacky { 219198090Srdivacky struct master_lists *ml; 220193323Sed 221193323Sed tlist = getlist (); 222193323Sed p = getnode (); 223193323Sed p->key = xstrdup (xdir); 224193323Sed p->type = UPDATE; 225193323Sed ml = (struct master_lists *) 226193323Sed xmalloc (sizeof (struct master_lists)); 227193323Sed ml->tlist = tlist; 228193323Sed p->data = (char *) ml; 229193323Sed p->delproc = masterlist_delproc; 230193323Sed (void) addnode (mtlist, p); 231193323Sed } 232193323Sed /* do tlist */ 233193323Sed p = getnode (); 234193323Sed p->key = xstrdup (finfo->file); 235193323Sed p->type = UPDATE; 236193323Sed p->delproc = tag_delproc; 237198090Srdivacky vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, 238198090Srdivacky (char *) NULL, finfo->file, 0, 0, 239198090Srdivacky finfo->entries, finfo->rcs); 240198090Srdivacky if (vers->srcfile == NULL) 241198090Srdivacky { 242198090Srdivacky if (!really_quiet) 243198090Srdivacky error (0, 0, "nothing known about %s", finfo->file); 244193323Sed return (1); 245210299Sed } 246210299Sed p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 0); 247210299Sed if (p->data != NULL) 248210299Sed { 249210299Sed int addit = 1; 250210299Sed char *oversion; 251210299Sed 252210299Sed oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); 253210299Sed if (oversion == NULL) 254210299Sed { 255198090Srdivacky if (delete_flag) 256210299Sed { 257198090Srdivacky addit = 0; 258198090Srdivacky } 259198090Srdivacky } 260198090Srdivacky else if (strcmp(oversion, p->data) == 0) 261198892Srdivacky { 262198090Srdivacky addit = 0; 263198090Srdivacky } 264198090Srdivacky else if (!force_tag_move) 265198090Srdivacky { 266198090Srdivacky addit = 0; 267193323Sed } 268198090Srdivacky if (oversion != NULL) 269198892Srdivacky { 270198892Srdivacky free(oversion); 271198892Srdivacky } 272193323Sed if (!addit) 273198892Srdivacky { 274198090Srdivacky free(p->data); 275210299Sed p->data = NULL; 276210299Sed } 277210299Sed } 278210299Sed freevers_ts(&vers); 279198892Srdivacky (void) addnode (tlist, p); 280198892Srdivacky return (0); 281198090Srdivacky} 282198090Srdivacky 283198090Srdivackystatic int 284198892Srdivackycheck_filesdoneproc(err, repos, update_dir) 285198090Srdivacky int err; 286198892Srdivacky char *repos; 287198892Srdivacky char *update_dir; 288198892Srdivacky{ 289198892Srdivacky int n; 290198892Srdivacky Node *p; 291198892Srdivacky 292198892Srdivacky p = findnode(mtlist, update_dir); 293198090Srdivacky if (p != NULL) 294198090Srdivacky { 295198090Srdivacky tlist = ((struct master_lists *) p->data)->tlist; 296198090Srdivacky } 297198892Srdivacky else 298198892Srdivacky { 299198892Srdivacky tlist = (List *) NULL; 300193323Sed } 301198892Srdivacky if ((tlist == NULL) || (tlist->list->next == tlist->list)) 302198892Srdivacky { 303198892Srdivacky return (err); 304198892Srdivacky } 305198090Srdivacky if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0) 306198090Srdivacky { 307198090Srdivacky error (0, 0, "Pre-tag check failed"); 308193323Sed err += n; 309198090Srdivacky } 310198090Srdivacky return (err); 311198090Srdivacky} 312193323Sed 313198090Srdivackystatic int 314193323Sedpretag_proc(repository, filter) 315198892Srdivacky char *repository; 316198892Srdivacky char *filter; 317198892Srdivacky{ 318198892Srdivacky if (filter[0] == '/') 319198090Srdivacky { 320198090Srdivacky char *s, *cp; 321198892Srdivacky 322198090Srdivacky s = xstrdup(filter); 323193323Sed for (cp=s; *cp; cp++) 324193323Sed { 325193323Sed if (isspace(*cp)) 326193323Sed { 327193323Sed *cp = '\0'; 328193323Sed break; 329193323Sed } 330193323Sed } 331198090Srdivacky if (!isfile(s)) 332198090Srdivacky { 333193323Sed error (0, errno, "cannot find pre-tag filter '%s'", s); 334193323Sed free(s); 335193323Sed return (1); 336193323Sed } 337198090Srdivacky free(s); 338198090Srdivacky } 339193323Sed run_setup("%s %s %s %s", 340193323Sed filter, 341193323Sed symtag, 342210299Sed delete_flag ? "del" : force_tag_move ? "mov" : "add", 343210299Sed repository); 344210299Sed walklist(tlist, pretag_list_proc, NULL); 345210299Sed return (run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY)); 346210299Sed} 347193323Sed 348198090Srdivackystatic void 349198090Srdivackymasterlist_delproc(p) 350193323Sed Node *p; 351210299Sed{ 352198090Srdivacky struct master_lists *ml; 353198090Srdivacky 354193323Sed ml = (struct master_lists *)p->data; 355198090Srdivacky dellist(&ml->tlist); 356198090Srdivacky free(ml); 357193323Sed return; 358198090Srdivacky} 359193323Sed 360198090Srdivackystatic void 361198090Srdivackytag_delproc(p) 362198090Srdivacky Node *p; 363198396Srdivacky{ 364198090Srdivacky if (p->data != NULL) 365198090Srdivacky { 366198090Srdivacky free(p->data); 367208599Srdivacky p->data = NULL; 368198090Srdivacky } 369198090Srdivacky return; 370198090Srdivacky} 371198090Srdivacky 372208599Srdivackystatic int 373198396Srdivackypretag_list_proc(p, closure) 374198396Srdivacky Node *p; 375198396Srdivacky void *closure; 376198090Srdivacky{ 377198090Srdivacky if (p->data != NULL) 378198090Srdivacky { 379198090Srdivacky run_arg(p->key); 380198090Srdivacky run_arg(p->data); 381193323Sed } 382193323Sed return (0); 383193323Sed} 384193323Sed 385 386/* 387 * Called to tag a particular file (the currently checked out version is 388 * tagged with the specified tag - or the specified tag is deleted). 389 */ 390/* ARGSUSED */ 391static int 392tag_fileproc (finfo) 393 struct file_info *finfo; 394{ 395 char *version, *oversion; 396 char *nversion = NULL; 397 char *rev; 398 Vers_TS *vers; 399 int retcode = 0; 400 401 vers = Version_TS (finfo->repository, (char *) NULL, (char *) NULL, (char *) NULL, 402 finfo->file, 0, 0, finfo->entries, finfo->rcs); 403 404 if ((numtag != NULL) || (date != NULL)) 405 { 406 nversion = RCS_getversion(vers->srcfile, 407 numtag, 408 date, 409 force_tag_match, 0); 410 if (nversion == NULL) 411 { 412 freevers_ts (&vers); 413 return (0); 414 } 415 } 416 if (delete_flag) 417 { 418 419 /* 420 * If -d is specified, "force_tag_match" is set, so that this call to 421 * RCS_getversion() will return a NULL version string if the symbolic 422 * tag does not exist in the RCS file. 423 * 424 * This is done here because it's MUCH faster than just blindly calling 425 * "rcs" to remove the tag... trust me. 426 */ 427 428 version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); 429 if (version == NULL || vers->srcfile == NULL) 430 { 431 freevers_ts (&vers); 432 return (0); 433 } 434 free (version); 435 436 if ((retcode = RCS_deltag(vers->srcfile->path, symtag, 1)) != 0) 437 { 438 if (!quiet) 439 error (0, retcode == -1 ? errno : 0, 440 "failed to remove tag %s from %s", symtag, 441 vers->srcfile->path); 442 freevers_ts (&vers); 443 return (1); 444 } 445 446 /* warm fuzzies */ 447 if (!really_quiet) 448 { 449 (void) printf ("D %s\n", finfo->fullname); 450 } 451 452 freevers_ts (&vers); 453 return (0); 454 } 455 456 /* 457 * If we are adding a tag, we need to know which version we have checked 458 * out and we'll tag that version. 459 */ 460 if (nversion == NULL) 461 { 462 version = vers->vn_user; 463 } 464 else 465 { 466 version = nversion; 467 } 468 if (version == NULL) 469 { 470 freevers_ts (&vers); 471 return (0); 472 } 473 else if (strcmp (version, "0") == 0) 474 { 475 if (!quiet) 476 error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file); 477 freevers_ts (&vers); 478 return (0); 479 } 480 else if (version[0] == '-') 481 { 482 if (!quiet) 483 error (0, 0, "skipping removed but un-commited file `%s'", finfo->file); 484 freevers_ts (&vers); 485 return (0); 486 } 487 else if (vers->srcfile == NULL) 488 { 489 if (!quiet) 490 error (0, 0, "cannot find revision control file for `%s'", finfo->file); 491 freevers_ts (&vers); 492 return (0); 493 } 494 495 /* 496 * As an enhancement for the case where a tag is being re-applied to a 497 * large number of files, make one extra call to RCS_getversion to see 498 * if the tag is already set in the RCS file. If so, check to see if it 499 * needs to be moved. If not, do nothing. This will likely save a lot of 500 * time when simply moving the tag to the "current" head revisions of a 501 * module -- which I have found to be a typical tagging operation. 502 */ 503 rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version; 504 oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 0); 505 if (oversion != NULL) 506 { 507 int isbranch = RCS_isbranch (finfo->rcs, symtag); 508 509 /* 510 * if versions the same and neither old or new are branches don't have 511 * to do anything 512 */ 513 if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch) 514 { 515 free (oversion); 516 freevers_ts (&vers); 517 return (0); 518 } 519 520 if (!force_tag_move) 521 { 522 /* we're NOT going to move the tag */ 523 (void) printf ("W %s", finfo->fullname); 524 525 (void) printf (" : %s already exists on %s %s", 526 symtag, isbranch ? "branch" : "version", oversion); 527 (void) printf (" : NOT MOVING tag to %s %s\n", 528 branch_mode ? "branch" : "version", rev); 529 free (oversion); 530 freevers_ts (&vers); 531 return (0); 532 } 533 free (oversion); 534 } 535 536 if ((retcode = RCS_settag(vers->srcfile->path, symtag, rev)) != 0) 537 { 538 error (1, retcode == -1 ? errno : 0, 539 "failed to set tag %s to revision %s in %s", 540 symtag, rev, vers->srcfile->path); 541 freevers_ts (&vers); 542 return (1); 543 } 544 545 /* more warm fuzzies */ 546 if (!really_quiet) 547 { 548 (void) printf ("T %s\n", finfo->fullname); 549 } 550 551 if (nversion != NULL) 552 { 553 free (nversion); 554 } 555 freevers_ts (&vers); 556 return (0); 557} 558 559/* 560 * Print a warm fuzzy message 561 */ 562/* ARGSUSED */ 563static Dtype 564tag_dirproc (dir, repos, update_dir) 565 char *dir; 566 char *repos; 567 char *update_dir; 568{ 569 if (!quiet) 570 error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir); 571 return (R_PROCESS); 572} 573 574/* Code relating to the val-tags file. Note that this file has no way 575 of knowing when a tag has been deleted. The problem is that there 576 is no way of knowing whether a tag still exists somewhere, when we 577 delete it some places. Using per-directory val-tags files (in 578 CVSREP) might be better, but that might slow down the process of 579 verifying that a tag is correct (maybe not, for the likely cases, 580 if carefully done), and/or be harder to implement correctly. */ 581 582struct val_args { 583 char *name; 584 int found; 585}; 586 587/* Pass as a static until we get around to fixing start_recursion to pass along 588 a void * where we can stash it. */ 589static struct val_args *val_args_static; 590 591static int val_fileproc PROTO ((struct file_info *finfo)); 592 593static int 594val_fileproc (finfo) 595 struct file_info *finfo; 596{ 597 RCSNode *rcsdata; 598 struct val_args *args = val_args_static; 599 char *tag; 600 601 if ((rcsdata = finfo->rcs) == NULL) 602 /* Not sure this can happen, after all we passed only 603 W_REPOS | W_ATTIC. */ 604 return 0; 605 606 tag = RCS_gettag (rcsdata, args->name, 1, 0); 607 if (tag != NULL) 608 { 609 /* FIXME: should find out a way to stop the search at this point. */ 610 args->found = 1; 611 free (tag); 612 } 613 return 0; 614} 615 616static Dtype val_direntproc PROTO ((char *, char *, char *)); 617 618static Dtype 619val_direntproc (dir, repository, update_dir) 620 char *dir; 621 char *repository; 622 char *update_dir; 623{ 624 /* This is not quite right--it doesn't get right the case of "cvs 625 update -d -r foobar" where foobar is a tag which exists only in 626 files in a directory which does not exist yet, but which is 627 about to be created. */ 628 if (isdir (dir)) 629 return 0; 630 return R_SKIP_ALL; 631} 632 633/* Check to see whether NAME is a valid tag. If so, return. If not 634 print an error message and exit. ARGC, ARGV, LOCAL, and AFLAG specify 635 which files we will be operating on. 636 637 REPOSITORY is the repository if we need to cd into it, or NULL if 638 we are already there, or "" if we should do a W_LOCAL recursion. 639 Sorry for three cases, but the "" case is needed in case the 640 working directories come from diverse parts of the repository, the 641 NULL case avoids an unneccesary chdir, and the non-NULL, non-"" 642 case is needed for checkout, where we don't want to chdir if the 643 tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any 644 local directory. */ 645void 646tag_check_valid (name, argc, argv, local, aflag, repository) 647 char *name; 648 int argc; 649 char **argv; 650 int local; 651 int aflag; 652 char *repository; 653{ 654 DBM *db; 655 char *valtags_filename; 656 int err; 657 datum mytag; 658 struct val_args the_val_args; 659 struct saved_cwd cwd; 660 int which; 661 662 /* Numeric tags require only a syntactic check. */ 663 if (isdigit (name[0])) 664 { 665 char *p; 666 for (p = name; *p != '\0'; ++p) 667 { 668 if (!(isdigit (*p) || *p == '.')) 669 error (1, 0, "\ 670Numeric tag %s contains characters other than digits and '.'", name); 671 } 672 return; 673 } 674 675 mytag.dptr = name; 676 mytag.dsize = strlen (name); 677 678 valtags_filename = xmalloc (strlen (CVSroot) + sizeof CVSROOTADM 679 + sizeof CVSROOTADM_HISTORY + 20); 680 strcpy (valtags_filename, CVSroot); 681 strcat (valtags_filename, "/"); 682 strcat (valtags_filename, CVSROOTADM); 683 strcat (valtags_filename, "/"); 684 strcat (valtags_filename, CVSROOTADM_VALTAGS); 685 db = dbm_open (valtags_filename, O_RDWR, 0666); 686 if (db == NULL) 687 { 688 if (!existence_error (errno)) 689 error (1, errno, "cannot read %s", valtags_filename); 690 691 /* If the file merely fails to exist, we just keep going and create 692 it later if need be. */ 693 } 694 else 695 { 696 datum val; 697 698 val = dbm_fetch (db, mytag); 699 if (val.dptr != NULL) 700 { 701 /* Found. The tag is valid. */ 702 dbm_close (db); 703 free (valtags_filename); 704 return; 705 } 706 /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */ 707 } 708 709 /* We didn't find the tag in val-tags, so look through all the RCS files 710 to see whether it exists there. Yes, this is expensive, but there 711 is no other way to cope with a tag which might have been created 712 by an old version of CVS, from before val-tags was invented. */ 713 714 the_val_args.name = name; 715 the_val_args.found = 0; 716 val_args_static = &the_val_args; 717 718 which = W_REPOS | W_ATTIC; 719 720 if (repository != NULL) 721 { 722 if (repository[0] == '\0') 723 which |= W_LOCAL; 724 else 725 { 726 if (save_cwd (&cwd)) 727 exit (EXIT_FAILURE); 728 if (chdir (repository) < 0) 729 error (1, errno, "cannot change to %s directory", repository); 730 } 731 } 732 733 err = start_recursion (val_fileproc, (FILESDONEPROC) NULL, 734 val_direntproc, (DIRLEAVEPROC) NULL, 735 argc, argv, local, which, aflag, 736 1, NULL, 1, 0); 737 if (repository != NULL && repository[0] != '\0') 738 { 739 if (restore_cwd (&cwd, NULL)) 740 exit (EXIT_FAILURE); 741 free_cwd (&cwd); 742 } 743 744 if (!the_val_args.found) 745 error (1, 0, "no such tag %s", name); 746 else 747 { 748 /* The tags is valid but not mentioned in val-tags. Add it. */ 749 datum value; 750 751 if (noexec) 752 { 753 if (db != NULL) 754 dbm_close (db); 755 free (valtags_filename); 756 return; 757 } 758 759 if (db == NULL) 760 { 761 mode_t omask; 762 omask = umask (cvsumask); 763 db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666); 764 (void) umask (omask); 765 766 if (db == NULL) 767 { 768 error (0, errno, "cannot create %s", valtags_filename); 769 free (valtags_filename); 770 return; 771 } 772 } 773 value.dptr = "y"; 774 value.dsize = 1; 775 if (dbm_store (db, mytag, value, DBM_REPLACE) < 0) 776 error (0, errno, "cannot store %s into %s", name, 777 valtags_filename); 778 dbm_close (db); 779 } 780 free (valtags_filename); 781} 782