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