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