117721Speter/* 2175270Sobrien * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 3175270Sobrien * 4175270Sobrien * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5175270Sobrien * and others. 6175270Sobrien * 7175270Sobrien * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8175270Sobrien * Portions Copyright (C) 1989-1992, Brian Berliner 917721Speter * 1017721Speter * You may distribute under the terms of the GNU General Public License as 1132785Speter * specified in the README file that comes with the CVS source distribution. 1217721Speter * 1381404Speter * Tag and Rtag 1417721Speter * 1517721Speter * Add or delete a symbolic name to an RCS file, or a collection of RCS files. 1681404Speter * Tag uses the checked out revision in the current directory, rtag uses 1781404Speter * the modules database, if necessary. 18133180Sdes * 19133180Sdes * $FreeBSD$ 2017721Speter */ 2117721Speter 2217721Speter#include "cvs.h" 2317721Speter#include "savecwd.h" 2417721Speter 2581404Speterstatic int rtag_proc PROTO((int argc, char **argv, char *xwhere, 2681404Speter char *mwhere, char *mfile, int shorten, 2781404Speter int local_specified, char *mname, char *msg)); 2825839Speterstatic int check_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 2925839Speterstatic int check_filesdoneproc PROTO ((void *callerdat, int err, 30128266Speter const char *repos, 31128266Speter const char *update_dir, 32128266Speter List *entries)); 33128266Speterstatic int pretag_proc PROTO((const char *repository, const char *filter)); 3417721Speterstatic void masterlist_delproc PROTO((Node *p)); 3517721Speterstatic void tag_delproc PROTO((Node *p)); 3617721Speterstatic int pretag_list_proc PROTO((Node *p, void *closure)); 3717721Speter 38128266Speterstatic Dtype tag_dirproc PROTO ((void *callerdat, const char *dir, 39128266Speter const char *repos, const char *update_dir, 40128266Speter List *entries)); 4181404Speterstatic int rtag_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 4281404Speterstatic int rtag_delete PROTO((RCSNode *rcsfile)); 4325839Speterstatic int tag_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 4417721Speter 4581404Speterstatic char *numtag; /* specific revision to tag */ 4681404Speterstatic int numtag_validated = 0; 4717721Speterstatic char *date = NULL; 4881404Speterstatic char *symtag; /* tag to add or delete */ 4917721Speterstatic int delete_flag; /* adding a tag by default */ 5017721Speterstatic int branch_mode; /* make an automagic "branch" tag */ 51102840Speterstatic int disturb_branch_tags = 0; /* allow -F,-d to disturb branch tags */ 5281404Speterstatic int force_tag_match = 1; /* force tag to match by default */ 5317721Speterstatic int force_tag_move; /* don't force tag to move by default */ 5425839Speterstatic int check_uptodate; /* no uptodate-check by default */ 5581404Speterstatic int attic_too; /* remove tag from Attic files */ 5681404Speterstatic int is_rtag; 5717721Speter 5817721Speterstruct tag_info 5917721Speter{ 6017721Speter Ctype status; 6117721Speter char *rev; 6217721Speter char *tag; 6317721Speter char *options; 6417721Speter}; 6517721Speter 6617721Speterstruct master_lists 6717721Speter{ 6817721Speter List *tlist; 6917721Speter}; 7017721Speter 7117721Speterstatic List *mtlist; 7217721Speterstatic List *tlist; 7317721Speter 74102840Speterstatic const char rtag_opts[] = "+aBbdFflnQqRr:D:"; 7581404Speterstatic const char *const rtag_usage[] = 7617721Speter{ 7781404Speter "Usage: %s %s [-abdFflnR] [-r rev|-D date] tag modules...\n", 7881404Speter "\t-a\tClear tag from removed files that would not otherwise be tagged.\n", 7981404Speter "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", 80102840Speter "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n", 8181404Speter "\t-d\tDelete the given tag.\n", 8281404Speter "\t-F\tMove tag if it already exists.\n", 8381404Speter "\t-f\tForce a head revision match if tag/date not found.\n", 8417721Speter "\t-l\tLocal directory only, not recursive.\n", 8581404Speter "\t-n\tNo execution of 'tag program'.\n", 8617721Speter "\t-R\tProcess directories recursively.\n", 8732785Speter "\t-r rev\tExisting revision/tag.\n", 8832785Speter "\t-D\tExisting date.\n", 8981404Speter "(Specify the --help global option for a list of other help options)\n", 9081404Speter NULL 9181404Speter}; 9281404Speter 93102840Speterstatic const char tag_opts[] = "+BbcdFflQqRr:D:"; 9481404Speterstatic const char *const tag_usage[] = 9581404Speter{ 9681404Speter "Usage: %s %s [-bcdFflR] [-r rev|-D date] tag [files...]\n", 9717721Speter "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", 98102840Speter "\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n", 9981404Speter "\t-c\tCheck that working files are unmodified.\n", 10081404Speter "\t-d\tDelete the given tag.\n", 10125839Speter "\t-F\tMove tag if it already exists.\n", 10281404Speter "\t-f\tForce a head revision match if tag/date not found.\n", 10381404Speter "\t-l\tLocal directory only, not recursive.\n", 10481404Speter "\t-R\tProcess directories recursively.\n", 10581404Speter "\t-r rev\tExisting revision/tag.\n", 10681404Speter "\t-D\tExisting date.\n", 10732785Speter "(Specify the --help global option for a list of other help options)\n", 10817721Speter NULL 10917721Speter}; 11017721Speter 11117721Speterint 11225839Spetercvstag (argc, argv) 11317721Speter int argc; 11417721Speter char **argv; 11517721Speter{ 116102840Speter int local = 0; /* recursive by default */ 11717721Speter int c; 11817721Speter int err = 0; 11981404Speter int run_module_prog = 1; 12017721Speter 121128266Speter is_rtag = (strcmp (cvs_cmd_name, "rtag") == 0); 12281404Speter 12317721Speter if (argc == -1) 12481404Speter usage (is_rtag ? rtag_usage : tag_usage); 12517721Speter 12626065Speter optind = 0; 12781404Speter while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1) 12817721Speter { 12917721Speter switch (c) 13017721Speter { 13181404Speter case 'a': 13281404Speter attic_too = 1; 13381404Speter break; 13481404Speter case 'b': 13581404Speter branch_mode = 1; 13681404Speter break; 137102840Speter case 'B': 138102840Speter disturb_branch_tags = 1; 139102840Speter break; 14081404Speter case 'c': 14181404Speter check_uptodate = 1; 14281404Speter break; 14381404Speter case 'd': 14481404Speter delete_flag = 1; 14581404Speter break; 14681404Speter case 'F': 14781404Speter force_tag_move = 1; 14881404Speter break; 14981404Speter case 'f': 15081404Speter force_tag_match = 0; 15181404Speter break; 15281404Speter case 'l': 15381404Speter local = 1; 15481404Speter break; 15581404Speter case 'n': 15681404Speter run_module_prog = 0; 15781404Speter break; 15817721Speter case 'Q': 15917721Speter case 'q': 16017721Speter /* The CVS 1.5 client sends these options (in addition to 16117721Speter Global_option requests), so we must ignore them. */ 16217721Speter if (!server_active) 16317721Speter error (1, 0, 16417721Speter "-q or -Q must be specified before \"%s\"", 165128266Speter cvs_cmd_name); 16617721Speter break; 16717721Speter case 'R': 16817721Speter local = 0; 16917721Speter break; 17017721Speter case 'r': 17117721Speter numtag = optarg; 17217721Speter break; 17317721Speter case 'D': 17417721Speter if (date) 17517721Speter free (date); 17617721Speter date = Make_Date (optarg); 17717721Speter break; 17817721Speter case '?': 17917721Speter default: 18081404Speter usage (is_rtag ? rtag_usage : tag_usage); 18117721Speter break; 18217721Speter } 18317721Speter } 18417721Speter argc -= optind; 18517721Speter argv += optind; 18617721Speter 18781404Speter if (argc < (is_rtag ? 2 : 1)) 18881404Speter usage (is_rtag ? rtag_usage : tag_usage); 18917721Speter symtag = argv[0]; 19017721Speter argc--; 19117721Speter argv++; 19217721Speter 19317721Speter if (date && numtag) 19417721Speter error (1, 0, "-r and -D options are mutually exclusive"); 19517721Speter if (delete_flag && branch_mode) 19617721Speter error (0, 0, "warning: -b ignored with -d options"); 19717721Speter RCS_check_tag (symtag); 19817721Speter 19917721Speter#ifdef CLIENT_SUPPORT 20081404Speter if (current_parsed_root->isremote) 20117721Speter { 20217721Speter /* We're the client side. Fire up the remote server. */ 20317721Speter start_server (); 20417721Speter 20517721Speter ign_setup (); 20617721Speter 20781404Speter if (attic_too) 20881404Speter send_arg("-a"); 20981404Speter if (branch_mode) 21081404Speter send_arg("-b"); 211102840Speter if (disturb_branch_tags) 212102840Speter send_arg("-B"); 21381404Speter if (check_uptodate) 21481404Speter send_arg("-c"); 21581404Speter if (delete_flag) 21681404Speter send_arg("-d"); 21781404Speter if (force_tag_move) 21881404Speter send_arg("-F"); 21925839Speter if (!force_tag_match) 22025839Speter send_arg ("-f"); 22117721Speter if (local) 22217721Speter send_arg("-l"); 22381404Speter if (!run_module_prog) 22481404Speter send_arg("-n"); 22517721Speter 22617721Speter if (numtag) 22717721Speter option_with_arg ("-r", numtag); 22817721Speter if (date) 22917721Speter client_senddate (date); 23017721Speter 231107484Speter send_arg ("--"); 232107484Speter 23317721Speter send_arg (symtag); 23417721Speter 23581404Speter if (is_rtag) 23681404Speter { 23781404Speter int i; 23881404Speter for (i = 0; i < argc; ++i) 23981404Speter send_arg (argv[i]); 24081404Speter send_to_server ("rtag\012", 0); 24181404Speter } 24281404Speter else 24381404Speter { 24481404Speter send_files (argc, argv, local, 0, 24581404Speter 24654427Speter /* I think the -c case is like "cvs status", in 24754427Speter which we really better be correct rather than 24854427Speter being fast; it is just too confusing otherwise. */ 24981404Speter check_uptodate ? 0 : SEND_NO_CONTENTS); 25081404Speter send_file_names (argc, argv, SEND_EXPAND_WILD); 25181404Speter send_to_server ("tag\012", 0); 25281404Speter } 25381404Speter 25417721Speter return get_responses_and_close (); 25517721Speter } 25617721Speter#endif 25717721Speter 25881404Speter if (is_rtag) 25981404Speter { 26081404Speter DBM *db; 26181404Speter int i; 26281404Speter db = open_module (); 26381404Speter for (i = 0; i < argc; i++) 26481404Speter { 26581404Speter /* XXX last arg should be repository, but doesn't make sense here */ 26681404Speter history_write ('T', (delete_flag ? "D" : (numtag ? numtag : 26781404Speter (date ? date : "A"))), symtag, argv[i], ""); 26881404Speter err += do_module (db, argv[i], TAG, 26981404Speter delete_flag ? "Untagging" : "Tagging", 270102840Speter rtag_proc, (char *) NULL, 0, local, run_module_prog, 27181404Speter 0, symtag); 27281404Speter } 27381404Speter close_module (db); 27481404Speter } 27581404Speter else 27681404Speter { 277102840Speter err = rtag_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL, 27881404Speter NULL); 27981404Speter } 28017721Speter 28181404Speter return (err); 28281404Speter} 28381404Speter 28481404Speter/* 28581404Speter * callback proc for doing the real work of tagging 28681404Speter */ 28781404Speter/* ARGSUSED */ 28881404Speterstatic int 28981404Speterrtag_proc (argc, argv, xwhere, mwhere, mfile, shorten, local_specified, 29081404Speter mname, msg) 29181404Speter int argc; 29281404Speter char **argv; 29381404Speter char *xwhere; 29481404Speter char *mwhere; 29581404Speter char *mfile; 29681404Speter int shorten; 29781404Speter int local_specified; 29881404Speter char *mname; 29981404Speter char *msg; 30081404Speter{ 30181404Speter /* Begin section which is identical to patch_proc--should this 30281404Speter be abstracted out somehow? */ 30381404Speter char *myargv[2]; 30481404Speter int err = 0; 30581404Speter int which; 30681404Speter char *repository; 30781404Speter char *where; 30881404Speter 30981404Speter if (is_rtag) 31081404Speter { 31181404Speter repository = xmalloc (strlen (current_parsed_root->directory) + strlen (argv[0]) 31281404Speter + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2); 31381404Speter (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]); 31481404Speter where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1) 31581404Speter + 1); 31681404Speter (void) strcpy (where, argv[0]); 31781404Speter 31881404Speter /* if mfile isn't null, we need to set up to do only part of the module */ 31981404Speter if (mfile != NULL) 32081404Speter { 32181404Speter char *cp; 32281404Speter char *path; 32381404Speter 32481404Speter /* if the portion of the module is a path, put the dir part on repos */ 32581404Speter if ((cp = strrchr (mfile, '/')) != NULL) 32681404Speter { 32781404Speter *cp = '\0'; 32881404Speter (void) strcat (repository, "/"); 32981404Speter (void) strcat (repository, mfile); 33081404Speter (void) strcat (where, "/"); 33181404Speter (void) strcat (where, mfile); 33281404Speter mfile = cp + 1; 33381404Speter } 33481404Speter 33581404Speter /* take care of the rest */ 33681404Speter path = xmalloc (strlen (repository) + strlen (mfile) + 5); 33781404Speter (void) sprintf (path, "%s/%s", repository, mfile); 33881404Speter if (isdir (path)) 33981404Speter { 34081404Speter /* directory means repository gets the dir tacked on */ 34181404Speter (void) strcpy (repository, path); 34281404Speter (void) strcat (where, "/"); 34381404Speter (void) strcat (where, mfile); 34481404Speter } 34581404Speter else 34681404Speter { 34781404Speter myargv[0] = argv[0]; 34881404Speter myargv[1] = mfile; 34981404Speter argc = 2; 35081404Speter argv = myargv; 35181404Speter } 35281404Speter free (path); 35381404Speter } 35481404Speter 35581404Speter /* cd to the starting repository */ 35681404Speter if ( CVS_CHDIR (repository) < 0) 35781404Speter { 35881404Speter error (0, errno, "cannot chdir to %s", repository); 35981404Speter free (repository); 360175270Sobrien free (where); 36181404Speter return (1); 36281404Speter } 36381404Speter /* End section which is identical to patch_proc. */ 36481404Speter 365175270Sobrien if (delete_flag || force_tag_move || attic_too || numtag) 36681404Speter which = W_REPOS | W_ATTIC; 36781404Speter else 36881404Speter which = W_REPOS; 36981404Speter } 37081404Speter else 37181404Speter { 37281404Speter where = NULL; 37381404Speter which = W_LOCAL; 37481404Speter repository = ""; 37581404Speter } 37681404Speter 37781404Speter if (numtag != NULL && !numtag_validated) 37881404Speter { 379102840Speter tag_check_valid (numtag, argc - 1, argv + 1, local_specified, 0, repository); 38081404Speter numtag_validated = 1; 38181404Speter } 38281404Speter 38317721Speter /* check to make sure they are authorized to tag all the 38417721Speter specified files in the repository */ 38517721Speter 38617721Speter mtlist = getlist(); 38717721Speter err = start_recursion (check_fileproc, check_filesdoneproc, 38825839Speter (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, 389107484Speter argc - 1, argv + 1, local_specified, which, 0, 390128266Speter CVS_LOCK_READ, where, 1, repository); 39117721Speter 39217721Speter if (err) 39317721Speter { 39417721Speter error (1, 0, "correct the above errors first!"); 39517721Speter } 39617721Speter 39781404Speter /* It would be nice to provide consistency with respect to 39881404Speter commits; however CVS lacks the infrastructure to do that (see 399107484Speter Concurrency in cvs.texinfo and comment in do_recursion). */ 40081404Speter 40117721Speter /* start the recursion processor */ 40281404Speter err = start_recursion (is_rtag ? rtag_fileproc : tag_fileproc, 40381404Speter (FILESDONEPROC) NULL, tag_dirproc, 40481404Speter (DIRLEAVEPROC) NULL, NULL, argc - 1, argv + 1, 405128266Speter local_specified, which, 0, CVS_LOCK_WRITE, where, 1, 406128266Speter repository); 407128266Speter if ( which & W_REPOS ) free ( repository ); 40881404Speter dellist (&mtlist); 40981404Speter if (where != NULL) 41081404Speter free (where); 41117721Speter return (err); 41217721Speter} 41317721Speter 41417721Speter/* check file that is to be tagged */ 41517721Speter/* All we do here is add it to our list */ 41617721Speter 41717721Speterstatic int 41825839Spetercheck_fileproc (callerdat, finfo) 41925839Speter void *callerdat; 42017721Speter struct file_info *finfo; 42117721Speter{ 422128266Speter const char *xdir; 42317721Speter Node *p; 42417721Speter Vers_TS *vers; 42517721Speter 42625839Speter if (check_uptodate) 42725839Speter { 428107484Speter switch (Classify_File (finfo, (char *) NULL, (char *) NULL, 429107484Speter (char *) NULL, 1, 0, &vers, 0)) 43025839Speter { 431107484Speter case T_UPTODATE: 432107484Speter case T_CHECKOUT: 433107484Speter case T_PATCH: 434107484Speter case T_REMOVE_ENTRY: 435107484Speter break; 436107484Speter case T_UNKNOWN: 437107484Speter case T_CONFLICT: 438107484Speter case T_NEEDS_MERGE: 439107484Speter case T_MODIFIED: 440107484Speter case T_ADDED: 441107484Speter case T_REMOVED: 442107484Speter default: 44325839Speter error (0, 0, "%s is locally modified", finfo->fullname); 44466525Speter freevers_ts (&vers); 44525839Speter return (1); 44625839Speter } 44725839Speter } 44866525Speter else 44966525Speter vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); 45025839Speter 45117721Speter if (finfo->update_dir[0] == '\0') 45217721Speter xdir = "."; 45317721Speter else 45417721Speter xdir = finfo->update_dir; 45517721Speter if ((p = findnode (mtlist, xdir)) != NULL) 45617721Speter { 45717721Speter tlist = ((struct master_lists *) p->data)->tlist; 45817721Speter } 45917721Speter else 46017721Speter { 46117721Speter struct master_lists *ml; 46217721Speter 46317721Speter tlist = getlist (); 46417721Speter p = getnode (); 46517721Speter p->key = xstrdup (xdir); 46617721Speter p->type = UPDATE; 46717721Speter ml = (struct master_lists *) 46817721Speter xmalloc (sizeof (struct master_lists)); 46917721Speter ml->tlist = tlist; 470128266Speter p->data = ml; 47117721Speter p->delproc = masterlist_delproc; 47217721Speter (void) addnode (mtlist, p); 47317721Speter } 47417721Speter /* do tlist */ 47517721Speter p = getnode (); 47617721Speter p->key = xstrdup (finfo->file); 47717721Speter p->type = UPDATE; 47817721Speter p->delproc = tag_delproc; 47917721Speter if (vers->srcfile == NULL) 48017721Speter { 48117721Speter if (!really_quiet) 48217721Speter error (0, 0, "nothing known about %s", finfo->file); 48366525Speter freevers_ts (&vers); 48466525Speter freenode (p); 48517721Speter return (1); 48617721Speter } 48744852Speter 48844852Speter /* Here we duplicate the calculation in tag_fileproc about which 48944852Speter version we are going to tag. There probably are some subtle races 49044852Speter (e.g. numtag is "foo" which gets moved between here and 49144852Speter tag_fileproc). */ 49281404Speter if (!is_rtag && numtag == NULL && date == NULL) 49344852Speter p->data = xstrdup (vers->vn_user); 49444852Speter else 49544852Speter p->data = RCS_getversion (vers->srcfile, numtag, date, 49644852Speter force_tag_match, NULL); 49744852Speter 49817721Speter if (p->data != NULL) 49917721Speter { 50017721Speter int addit = 1; 50117721Speter char *oversion; 50217721Speter 50325839Speter oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 50425839Speter (int *) NULL); 50517721Speter if (oversion == NULL) 50617721Speter { 50717721Speter if (delete_flag) 50817721Speter { 50954427Speter /* Deleting a tag which did not exist is a noop and 51054427Speter should not be logged. */ 51117721Speter addit = 0; 51217721Speter } 51317721Speter } 51454427Speter else if (delete_flag) 51554427Speter { 51654427Speter free (p->data); 51754427Speter p->data = xstrdup (oversion); 51854427Speter } 51917721Speter else if (strcmp(oversion, p->data) == 0) 52017721Speter { 52117721Speter addit = 0; 52217721Speter } 52317721Speter else if (!force_tag_move) 52417721Speter { 52517721Speter addit = 0; 52617721Speter } 52717721Speter if (oversion != NULL) 52817721Speter { 52917721Speter free(oversion); 53017721Speter } 53117721Speter if (!addit) 53217721Speter { 53317721Speter free(p->data); 53417721Speter p->data = NULL; 53517721Speter } 53617721Speter } 53781404Speter freevers_ts (&vers); 53817721Speter (void) addnode (tlist, p); 53917721Speter return (0); 54017721Speter} 54117721Speter 54217721Speterstatic int 54325839Spetercheck_filesdoneproc (callerdat, err, repos, update_dir, entries) 54425839Speter void *callerdat; 54517721Speter int err; 546128266Speter const char *repos; 547128266Speter const char *update_dir; 54825839Speter List *entries; 54917721Speter{ 55017721Speter int n; 55117721Speter Node *p; 55217721Speter 55317721Speter p = findnode(mtlist, update_dir); 55417721Speter if (p != NULL) 55517721Speter { 55617721Speter tlist = ((struct master_lists *) p->data)->tlist; 55717721Speter } 55817721Speter else 55917721Speter { 56017721Speter tlist = (List *) NULL; 56117721Speter } 56217721Speter if ((tlist == NULL) || (tlist->list->next == tlist->list)) 56317721Speter { 56417721Speter return (err); 56517721Speter } 56617721Speter if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0) 56717721Speter { 56817721Speter error (0, 0, "Pre-tag check failed"); 56917721Speter err += n; 57017721Speter } 57117721Speter return (err); 57217721Speter} 57317721Speter 57417721Speterstatic int 575128266Speterpretag_proc (repository, filter) 576128266Speter const char *repository; 577128266Speter const char *filter; 57817721Speter{ 57917721Speter if (filter[0] == '/') 58017721Speter { 58117721Speter char *s, *cp; 58217721Speter 58317721Speter s = xstrdup(filter); 58417721Speter for (cp=s; *cp; cp++) 58517721Speter { 58654427Speter if (isspace ((unsigned char) *cp)) 58717721Speter { 58817721Speter *cp = '\0'; 58917721Speter break; 59017721Speter } 59117721Speter } 59217721Speter if (!isfile(s)) 59317721Speter { 59417721Speter error (0, errno, "cannot find pre-tag filter '%s'", s); 59517721Speter free(s); 59617721Speter return (1); 59717721Speter } 59817721Speter free(s); 59917721Speter } 60032785Speter run_setup (filter); 60132785Speter run_arg (symtag); 60232785Speter run_arg (delete_flag ? "del" : force_tag_move ? "mov" : "add"); 60332785Speter run_arg (repository); 60417721Speter walklist(tlist, pretag_list_proc, NULL); 60544852Speter return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)); 60617721Speter} 60717721Speter 60817721Speterstatic void 60917721Spetermasterlist_delproc(p) 61017721Speter Node *p; 61117721Speter{ 612128266Speter struct master_lists *ml = p->data; 61317721Speter 61417721Speter dellist(&ml->tlist); 61517721Speter free(ml); 61617721Speter return; 61717721Speter} 61817721Speter 61917721Speterstatic void 62017721Spetertag_delproc(p) 62117721Speter Node *p; 62217721Speter{ 62317721Speter if (p->data != NULL) 62417721Speter { 62517721Speter free(p->data); 62617721Speter p->data = NULL; 62717721Speter } 62817721Speter return; 62917721Speter} 63017721Speter 63117721Speterstatic int 63217721Speterpretag_list_proc(p, closure) 63317721Speter Node *p; 63417721Speter void *closure; 63517721Speter{ 63617721Speter if (p->data != NULL) 63717721Speter { 63817721Speter run_arg(p->key); 63917721Speter run_arg(p->data); 64017721Speter } 64117721Speter return (0); 64217721Speter} 64317721Speter 64417721Speter 64517721Speter/* 64681404Speter * Called to rtag a particular file, as appropriate with the options that were 64781404Speter * set above. 64881404Speter */ 64981404Speter/* ARGSUSED */ 65081404Speterstatic int 65181404Speterrtag_fileproc (callerdat, finfo) 65281404Speter void *callerdat; 65381404Speter struct file_info *finfo; 65481404Speter{ 65581404Speter RCSNode *rcsfile; 65681404Speter char *version, *rev; 65781404Speter int retcode = 0; 65881404Speter 65981404Speter /* find the parsed RCS data */ 66081404Speter if ((rcsfile = finfo->rcs) == NULL) 66181404Speter return (1); 66281404Speter 66381404Speter /* 66481404Speter * For tagging an RCS file which is a symbolic link, you'd best be 66581404Speter * running with RCS 5.6, since it knows how to handle symbolic links 66681404Speter * correctly without breaking your link! 66781404Speter */ 66881404Speter 66981404Speter if (delete_flag) 67081404Speter return (rtag_delete (rcsfile)); 67181404Speter 67281404Speter /* 67381404Speter * If we get here, we are adding a tag. But, if -a was specified, we 67481404Speter * need to check to see if a -r or -D option was specified. If neither 67581404Speter * was specified and the file is in the Attic, remove the tag. 67681404Speter */ 67781404Speter if (attic_too && (!numtag && !date)) 67881404Speter { 67981404Speter if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC)) 68081404Speter return (rtag_delete (rcsfile)); 68181404Speter } 68281404Speter 68381404Speter version = RCS_getversion (rcsfile, numtag, date, force_tag_match, 68481404Speter (int *) NULL); 68581404Speter if (version == NULL) 68681404Speter { 68781404Speter /* If -a specified, clean up any old tags */ 68881404Speter if (attic_too) 68981404Speter (void) rtag_delete (rcsfile); 69081404Speter 69181404Speter if (!quiet && !force_tag_match) 69281404Speter { 69381404Speter error (0, 0, "cannot find tag `%s' in `%s'", 69481404Speter numtag ? numtag : "head", rcsfile->path); 69581404Speter return (1); 69681404Speter } 69781404Speter return (0); 69881404Speter } 69981404Speter if (numtag 70081404Speter && isdigit ((unsigned char) *numtag) 70181404Speter && strcmp (numtag, version) != 0) 70281404Speter { 70381404Speter 70481404Speter /* 70581404Speter * We didn't find a match for the numeric tag that was specified, but 70681404Speter * that's OK. just pass the numeric tag on to rcs, to be tagged as 70781404Speter * specified. Could get here if one tried to tag "1.1.1" and there 70881404Speter * was a 1.1.1 branch with some head revision. In this case, we want 70981404Speter * the tag to reference "1.1.1" and not the revision at the head of 71081404Speter * the branch. Use a symbolic tag for that. 71181404Speter */ 71281404Speter rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag; 71381404Speter retcode = RCS_settag(rcsfile, symtag, numtag); 71481404Speter if (retcode == 0) 71581404Speter RCS_rewrite (rcsfile, NULL, NULL); 71681404Speter } 71781404Speter else 71881404Speter { 71981404Speter char *oversion; 72081404Speter 72181404Speter /* 72281404Speter * As an enhancement for the case where a tag is being re-applied to 72381404Speter * a large body of a module, make one extra call to RCS_getversion to 72481404Speter * see if the tag is already set in the RCS file. If so, check to 72581404Speter * see if it needs to be moved. If not, do nothing. This will 72681404Speter * likely save a lot of time when simply moving the tag to the 72781404Speter * "current" head revisions of a module -- which I have found to be a 72881404Speter * typical tagging operation. 72981404Speter */ 73081404Speter rev = branch_mode ? RCS_magicrev (rcsfile, version) : version; 73181404Speter oversion = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 73281404Speter (int *) NULL); 73381404Speter if (oversion != NULL) 73481404Speter { 73581404Speter int isbranch = RCS_nodeisbranch (finfo->rcs, symtag); 73681404Speter 73781404Speter /* 73881404Speter * if versions the same and neither old or new are branches don't 73981404Speter * have to do anything 74081404Speter */ 74181404Speter if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch) 74281404Speter { 74381404Speter free (oversion); 74481404Speter free (version); 74581404Speter return (0); 74681404Speter } 74781404Speter 74881404Speter if (!force_tag_move) 74981404Speter { 75081404Speter /* we're NOT going to move the tag */ 75181404Speter (void) printf ("W %s", finfo->fullname); 75281404Speter 75381404Speter (void) printf (" : %s already exists on %s %s", 75481404Speter symtag, isbranch ? "branch" : "version", 75581404Speter oversion); 75681404Speter (void) printf (" : NOT MOVING tag to %s %s\n", 75781404Speter branch_mode ? "branch" : "version", rev); 75881404Speter free (oversion); 75981404Speter free (version); 760102840Speter if (branch_mode) free(rev); 76181404Speter return (0); 76281404Speter } 763102840Speter else /* force_tag_move is set and... */ 764102840Speter if ((isbranch && !disturb_branch_tags) || 765102840Speter (!isbranch && disturb_branch_tags)) 766102840Speter { 767102840Speter error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.", 768102840Speter finfo->fullname, 769102840Speter isbranch ? "branch" : "non-branch", 770102840Speter symtag, oversion, rev, 771102840Speter isbranch ? "" : " due to `-B' option"); 772102840Speter if (branch_mode) free(rev); 773102840Speter free (oversion); 774102840Speter free (version); 775102840Speter return (0); 776102840Speter } 77781404Speter free (oversion); 77881404Speter } 77981404Speter retcode = RCS_settag(rcsfile, symtag, rev); 78081404Speter if (retcode == 0) 78181404Speter RCS_rewrite (rcsfile, NULL, NULL); 78281404Speter } 78381404Speter 78481404Speter if (retcode != 0) 78581404Speter { 78681404Speter error (1, retcode == -1 ? errno : 0, 78781404Speter "failed to set tag `%s' to revision `%s' in `%s'", 78881404Speter symtag, rev, rcsfile->path); 78981404Speter if (branch_mode) 79081404Speter free (rev); 79181404Speter free (version); 79281404Speter return (1); 79381404Speter } 79481404Speter if (branch_mode) 79581404Speter free (rev); 79681404Speter free (version); 79781404Speter return (0); 79881404Speter} 79981404Speter 80081404Speter/* 80181404Speter * If -d is specified, "force_tag_match" is set, so that this call to 80281404Speter * RCS_getversion() will return a NULL version string if the symbolic 80381404Speter * tag does not exist in the RCS file. 80481404Speter * 80581404Speter * If the -r flag was used, numtag is set, and we only delete the 80681404Speter * symtag from files that have numtag. 80781404Speter * 80881404Speter * This is done here because it's MUCH faster than just blindly calling 80981404Speter * "rcs" to remove the tag... trust me. 81081404Speter */ 81181404Speterstatic int 81281404Speterrtag_delete (rcsfile) 81381404Speter RCSNode *rcsfile; 81481404Speter{ 81581404Speter char *version; 816102840Speter int retcode, isbranch; 81781404Speter 81881404Speter if (numtag) 81981404Speter { 82081404Speter version = RCS_getversion (rcsfile, numtag, (char *) NULL, 1, 82181404Speter (int *) NULL); 82281404Speter if (version == NULL) 82381404Speter return (0); 82481404Speter free (version); 82581404Speter } 82681404Speter 82781404Speter version = RCS_getversion (rcsfile, symtag, (char *) NULL, 1, 82881404Speter (int *) NULL); 82981404Speter if (version == NULL) 83081404Speter return (0); 83181404Speter free (version); 83281404Speter 833102840Speter 834102840Speter isbranch = RCS_nodeisbranch (rcsfile, symtag); 835102840Speter if ((isbranch && !disturb_branch_tags) || 836102840Speter (!isbranch && disturb_branch_tags)) 837102840Speter { 838102840Speter if (!quiet) 839102840Speter error(0, 0, 840102840Speter "Not removing %s tag `%s' from `%s'%s.", 841102840Speter isbranch ? "branch" : "non-branch", 842102840Speter symtag, rcsfile->path, 843102840Speter isbranch ? "" : " due to `-B' option"); 844102840Speter return (1); 845102840Speter } 846102840Speter 84781404Speter if ((retcode = RCS_deltag(rcsfile, symtag)) != 0) 84881404Speter { 84981404Speter if (!quiet) 85081404Speter error (0, retcode == -1 ? errno : 0, 85181404Speter "failed to remove tag `%s' from `%s'", symtag, 85281404Speter rcsfile->path); 85381404Speter return (1); 85481404Speter } 85581404Speter RCS_rewrite (rcsfile, NULL, NULL); 85681404Speter return (0); 85781404Speter} 85881404Speter 85981404Speter 86081404Speter/* 86117721Speter * Called to tag a particular file (the currently checked out version is 86217721Speter * tagged with the specified tag - or the specified tag is deleted). 86317721Speter */ 86417721Speter/* ARGSUSED */ 86517721Speterstatic int 86625839Spetertag_fileproc (callerdat, finfo) 86725839Speter void *callerdat; 86817721Speter struct file_info *finfo; 86917721Speter{ 87017721Speter char *version, *oversion; 87117721Speter char *nversion = NULL; 87217721Speter char *rev; 87317721Speter Vers_TS *vers; 87417721Speter int retcode = 0; 875128266Speter int retval = 0; 87617721Speter 87725839Speter vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); 87825839Speter 87917721Speter if ((numtag != NULL) || (date != NULL)) 88017721Speter { 88117721Speter nversion = RCS_getversion(vers->srcfile, 88217721Speter numtag, 88317721Speter date, 88425839Speter force_tag_match, 88525839Speter (int *) NULL); 88617721Speter if (nversion == NULL) 887128266Speter goto free_vars_and_return; 88817721Speter } 88917721Speter if (delete_flag) 89017721Speter { 89117721Speter 892102840Speter int isbranch; 89317721Speter /* 89417721Speter * If -d is specified, "force_tag_match" is set, so that this call to 89517721Speter * RCS_getversion() will return a NULL version string if the symbolic 89617721Speter * tag does not exist in the RCS file. 89717721Speter * 89817721Speter * This is done here because it's MUCH faster than just blindly calling 89917721Speter * "rcs" to remove the tag... trust me. 90017721Speter */ 90117721Speter 90225839Speter version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 90325839Speter (int *) NULL); 90417721Speter if (version == NULL || vers->srcfile == NULL) 905128266Speter goto free_vars_and_return; 906128266Speter 90717721Speter free (version); 90817721Speter 909102840Speter isbranch = RCS_nodeisbranch (finfo->rcs, symtag); 910102840Speter if ((isbranch && !disturb_branch_tags) || 911102840Speter (!isbranch && disturb_branch_tags)) 912102840Speter { 913102840Speter if (!quiet) 914102840Speter error(0, 0, 915102840Speter "Not removing %s tag `%s' from `%s'%s.", 916102840Speter isbranch ? "branch" : "non-branch", 917102840Speter symtag, vers->srcfile->path, 918102840Speter isbranch ? "" : " due to `-B' option"); 919128266Speter retval = 1; 920128266Speter goto free_vars_and_return; 921102840Speter } 922102840Speter 92332785Speter if ((retcode = RCS_deltag(vers->srcfile, symtag)) != 0) 92417721Speter { 92517721Speter if (!quiet) 92617721Speter error (0, retcode == -1 ? errno : 0, 92717721Speter "failed to remove tag %s from %s", symtag, 92817721Speter vers->srcfile->path); 929128266Speter retval = 1; 930128266Speter goto free_vars_and_return; 93117721Speter } 93232785Speter RCS_rewrite (vers->srcfile, NULL, NULL); 93317721Speter 93417721Speter /* warm fuzzies */ 93517721Speter if (!really_quiet) 93617721Speter { 93725839Speter cvs_output ("D ", 2); 93825839Speter cvs_output (finfo->fullname, 0); 93925839Speter cvs_output ("\n", 1); 94017721Speter } 94117721Speter 942128266Speter goto free_vars_and_return; 94317721Speter } 94417721Speter 94517721Speter /* 94617721Speter * If we are adding a tag, we need to know which version we have checked 94717721Speter * out and we'll tag that version. 94817721Speter */ 94917721Speter if (nversion == NULL) 95017721Speter { 95117721Speter version = vers->vn_user; 95217721Speter } 95317721Speter else 95417721Speter { 95517721Speter version = nversion; 95617721Speter } 95717721Speter if (version == NULL) 95817721Speter { 959128266Speter goto free_vars_and_return; 96017721Speter } 96117721Speter else if (strcmp (version, "0") == 0) 96217721Speter { 96317721Speter if (!quiet) 96417721Speter error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file); 965128266Speter goto free_vars_and_return; 96617721Speter } 96717721Speter else if (version[0] == '-') 96817721Speter { 96917721Speter if (!quiet) 97017721Speter error (0, 0, "skipping removed but un-commited file `%s'", finfo->file); 971128266Speter goto free_vars_and_return; 97217721Speter } 97317721Speter else if (vers->srcfile == NULL) 97417721Speter { 97517721Speter if (!quiet) 97617721Speter error (0, 0, "cannot find revision control file for `%s'", finfo->file); 977128266Speter goto free_vars_and_return; 97817721Speter } 97917721Speter 98017721Speter /* 98117721Speter * As an enhancement for the case where a tag is being re-applied to a 98217721Speter * large number of files, make one extra call to RCS_getversion to see 98317721Speter * if the tag is already set in the RCS file. If so, check to see if it 98417721Speter * needs to be moved. If not, do nothing. This will likely save a lot of 98517721Speter * time when simply moving the tag to the "current" head revisions of a 98617721Speter * module -- which I have found to be a typical tagging operation. 98717721Speter */ 98817721Speter rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version; 98925839Speter oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 99025839Speter (int *) NULL); 99117721Speter if (oversion != NULL) 99217721Speter { 99332785Speter int isbranch = RCS_nodeisbranch (finfo->rcs, symtag); 99417721Speter 99517721Speter /* 99617721Speter * if versions the same and neither old or new are branches don't have 99717721Speter * to do anything 99817721Speter */ 99917721Speter if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch) 100017721Speter { 100117721Speter free (oversion); 100266525Speter if (branch_mode) 100366525Speter free (rev); 1004128266Speter goto free_vars_and_return; 100517721Speter } 100617721Speter 100717721Speter if (!force_tag_move) 100817721Speter { 100917721Speter /* we're NOT going to move the tag */ 101025839Speter cvs_output ("W ", 2); 101125839Speter cvs_output (finfo->fullname, 0); 101225839Speter cvs_output (" : ", 0); 101325839Speter cvs_output (symtag, 0); 101425839Speter cvs_output (" already exists on ", 0); 101525839Speter cvs_output (isbranch ? "branch" : "version", 0); 101625839Speter cvs_output (" ", 0); 101725839Speter cvs_output (oversion, 0); 101825839Speter cvs_output (" : NOT MOVING tag to ", 0); 101925839Speter cvs_output (branch_mode ? "branch" : "version", 0); 102025839Speter cvs_output (" ", 0); 102125839Speter cvs_output (rev, 0); 102225839Speter cvs_output ("\n", 1); 102317721Speter free (oversion); 102466525Speter if (branch_mode) 102566525Speter free (rev); 1026128266Speter goto free_vars_and_return; 102717721Speter } 1028102840Speter else /* force_tag_move == 1 and... */ 1029102840Speter if ((isbranch && !disturb_branch_tags) || 1030102840Speter (!isbranch && disturb_branch_tags)) 1031102840Speter { 1032102840Speter error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.", 1033102840Speter finfo->fullname, 1034102840Speter isbranch ? "branch" : "non-branch", 1035102840Speter symtag, oversion, rev, 1036102840Speter isbranch ? "" : " due to `-B' option"); 1037102840Speter free (oversion); 1038128266Speter if (branch_mode) 1039128266Speter free (rev); 1040128266Speter goto free_vars_and_return; 1041102840Speter } 104217721Speter free (oversion); 104317721Speter } 104417721Speter 104525839Speter if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0) 104617721Speter { 104717721Speter error (1, retcode == -1 ? errno : 0, 104817721Speter "failed to set tag %s to revision %s in %s", 104917721Speter symtag, rev, vers->srcfile->path); 105066525Speter if (branch_mode) 105166525Speter free (rev); 1052128266Speter retval = 1; 1053128266Speter goto free_vars_and_return; 105417721Speter } 105566525Speter if (branch_mode) 105666525Speter free (rev); 105732785Speter RCS_rewrite (vers->srcfile, NULL, NULL); 105817721Speter 105917721Speter /* more warm fuzzies */ 106017721Speter if (!really_quiet) 106117721Speter { 106225839Speter cvs_output ("T ", 2); 106325839Speter cvs_output (finfo->fullname, 0); 106425839Speter cvs_output ("\n", 1); 106517721Speter } 106617721Speter 1067128266Speter free_vars_and_return: 106817721Speter if (nversion != NULL) 106917721Speter free (nversion); 107017721Speter freevers_ts (&vers); 1071128266Speter return (retval); 107217721Speter} 107317721Speter 107417721Speter/* 107517721Speter * Print a warm fuzzy message 107617721Speter */ 107717721Speter/* ARGSUSED */ 107817721Speterstatic Dtype 107925839Spetertag_dirproc (callerdat, dir, repos, update_dir, entries) 108025839Speter void *callerdat; 1081128266Speter const char *dir; 1082128266Speter const char *repos; 1083128266Speter const char *update_dir; 108425839Speter List *entries; 108517721Speter{ 108681404Speter 108781404Speter if (ignore_directory (update_dir)) 108881404Speter { 108981404Speter /* print the warm fuzzy message */ 109081404Speter if (!quiet) 109181404Speter error (0, 0, "Ignoring %s", update_dir); 109281404Speter return R_SKIP_ALL; 109381404Speter } 109481404Speter 109517721Speter if (!quiet) 109617721Speter error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir); 109717721Speter return (R_PROCESS); 109817721Speter} 109917721Speter 110017721Speter/* Code relating to the val-tags file. Note that this file has no way 110117721Speter of knowing when a tag has been deleted. The problem is that there 110217721Speter is no way of knowing whether a tag still exists somewhere, when we 110317721Speter delete it some places. Using per-directory val-tags files (in 110417721Speter CVSREP) might be better, but that might slow down the process of 110517721Speter verifying that a tag is correct (maybe not, for the likely cases, 110617721Speter if carefully done), and/or be harder to implement correctly. */ 110717721Speter 110817721Speterstruct val_args { 110917721Speter char *name; 111017721Speter int found; 111117721Speter}; 111217721Speter 111325839Speterstatic int val_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 111417721Speter 111517721Speterstatic int 111625839Speterval_fileproc (callerdat, finfo) 111725839Speter void *callerdat; 111817721Speter struct file_info *finfo; 111917721Speter{ 112017721Speter RCSNode *rcsdata; 112125839Speter struct val_args *args = (struct val_args *)callerdat; 112217721Speter char *tag; 112317721Speter 112417721Speter if ((rcsdata = finfo->rcs) == NULL) 112517721Speter /* Not sure this can happen, after all we passed only 112617721Speter W_REPOS | W_ATTIC. */ 112717721Speter return 0; 112817721Speter 112925839Speter tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL); 113017721Speter if (tag != NULL) 113117721Speter { 113217721Speter /* FIXME: should find out a way to stop the search at this point. */ 113317721Speter args->found = 1; 113417721Speter free (tag); 113517721Speter } 113617721Speter return 0; 113717721Speter} 113817721Speter 113917721Speter 1140128266Speter 1141175270Sobrien/* This routine determines whether a tag appears in CVSROOT/val-tags. 1142175270Sobrien * 1143175270Sobrien * The val-tags file will be open read-only when IDB is NULL. Since writes to 1144175270Sobrien * val-tags always append to it, the lack of locking is okay. The worst case 1145175270Sobrien * race condition might misinterpret a partially written "foobar" matched, for 1146175270Sobrien * instance, a request for "f", "foo", of "foob". Such a mismatch would be 1147175270Sobrien * caught harmlessly later. 1148175270Sobrien * 1149175270Sobrien * Before CVS adds a tag to val-tags, it will lock val-tags for write and 1150175270Sobrien * verify that the tag is still not present to avoid adding it twice. 1151175270Sobrien * 1152175270Sobrien * NOTES 1153175270Sobrien * This function expects its parent to handle any necessary locking of the 1154175270Sobrien * val-tags file. 1155175270Sobrien * 1156175270Sobrien * INPUTS 1157175270Sobrien * idb When this value is NULL, the val-tags file is opened in 1158175270Sobrien * in read-only mode. When present, the val-tags file is opened 1159175270Sobrien * in read-write mode and the DBM handle is stored in *IDB. 1160175270Sobrien * name The tag to search for. 1161175270Sobrien * 1162175270Sobrien * OUTPUTS 1163175270Sobrien * *idb The val-tags file opened for read/write, or NULL if it couldn't 1164175270Sobrien * be opened. 1165175270Sobrien * 1166175270Sobrien * ERRORS 1167175270Sobrien * Exits with an error message if the val-tags file cannot be opened for 1168175270Sobrien * read (failure to open val-tags read/write is harmless - see below). 1169175270Sobrien * 1170175270Sobrien * RETURNS 1171175270Sobrien * true 1. If NAME exists in val-tags. 1172175270Sobrien * 2. If IDB is non-NULL and val-tags cannot be opened for write. 1173175270Sobrien * This allows callers to ignore the harmless inability to 1174175270Sobrien * update the val-tags cache. 1175175270Sobrien * false If the file could be opened and the tag is not present. 1176175270Sobrien */ 1177175270Sobrienstatic int is_in_val_tags PROTO((DBM **idb, const char *name)); 1178175270Sobrienstatic int 1179175270Sobrienis_in_val_tags (idb, name) 1180175270Sobrien DBM **idb; 1181175270Sobrien const char *name; 1182175270Sobrien{ 1183175270Sobrien DBM *db = NULL; 1184175270Sobrien char *valtags_filename; 1185175270Sobrien datum mytag; 1186175270Sobrien int status; 1187175270Sobrien 1188175270Sobrien /* Casting out const should be safe here - input datums are not 1189175270Sobrien * written to by the myndbm functions. 1190175270Sobrien */ 1191175270Sobrien mytag.dptr = (char *)name; 1192175270Sobrien mytag.dsize = strlen (name); 1193175270Sobrien 1194175270Sobrien valtags_filename = xmalloc (strlen (current_parsed_root->directory) 1195175270Sobrien + sizeof CVSROOTADM 1196175270Sobrien + sizeof CVSROOTADM_VALTAGS + 3); 1197175270Sobrien sprintf (valtags_filename, "%s/%s/%s", current_parsed_root->directory, 1198175270Sobrien CVSROOTADM, CVSROOTADM_VALTAGS); 1199175270Sobrien 1200175270Sobrien if (idb) 1201175270Sobrien { 1202175270Sobrien db = dbm_open (valtags_filename, O_RDWR, 0666); 1203175270Sobrien if (!db) 1204175270Sobrien { 1205175270Sobrien mode_t omask; 1206175270Sobrien 1207175270Sobrien if (!existence_error (errno)) 1208175270Sobrien { 1209175270Sobrien error (0, errno, "warning: cannot open %s read/write", 1210175270Sobrien valtags_filename); 1211175270Sobrien *idb = NULL; 1212175270Sobrien return 1; 1213175270Sobrien } 1214175270Sobrien 1215175270Sobrien omask = umask (cvsumask); 1216175270Sobrien db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666); 1217175270Sobrien umask (omask); 1218175270Sobrien if (!db) 1219175270Sobrien { 1220175270Sobrien error (0, errno, "warning: cannot create %s", 1221175270Sobrien valtags_filename); 1222175270Sobrien *idb = NULL; 1223175270Sobrien return 1; 1224175270Sobrien } 1225175270Sobrien 1226175270Sobrien *idb = db; 1227175270Sobrien return 0; 1228175270Sobrien } 1229175270Sobrien 1230175270Sobrien *idb = db; 1231175270Sobrien } 1232175270Sobrien else 1233175270Sobrien { 1234175270Sobrien db = dbm_open (valtags_filename, O_RDONLY, 0444); 1235175270Sobrien if (!db && !existence_error (errno)) 1236175270Sobrien error (1, errno, "cannot read %s", valtags_filename); 1237175270Sobrien } 1238175270Sobrien 1239175270Sobrien /* If the file merely fails to exist, we just keep going and create 1240175270Sobrien it later if need be. */ 1241175270Sobrien 1242175270Sobrien status = 0; 1243175270Sobrien if (db) 1244175270Sobrien { 1245175270Sobrien datum val; 1246175270Sobrien 1247175270Sobrien val = dbm_fetch (db, mytag); 1248175270Sobrien if (val.dptr != NULL) 1249175270Sobrien /* Found. The tag is valid. */ 1250175270Sobrien status = 1; 1251175270Sobrien 1252175270Sobrien /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */ 1253175270Sobrien 1254175270Sobrien if (!idb) dbm_close (db); 1255175270Sobrien } 1256175270Sobrien 1257175270Sobrien free (valtags_filename); 1258175270Sobrien return status; 1259175270Sobrien} 1260175270Sobrien 1261175270Sobrien 1262175270Sobrien 1263175270Sobrien/* Add a tag to the CVSROOT/val-tags cache. Establishes a write lock and 1264175270Sobrien * reverifies that the tag does not exist before adding it. 1265175270Sobrien */ 1266175270Sobrienstatic void add_to_val_tags PROTO((const char *name)); 1267175270Sobrienstatic void 1268175270Sobrienadd_to_val_tags (name) 1269175270Sobrien const char *name; 1270175270Sobrien{ 1271175270Sobrien DBM *db; 1272175270Sobrien datum mytag; 1273175270Sobrien datum value; 1274175270Sobrien 1275175270Sobrien if (noexec) return; 1276175270Sobrien 1277175270Sobrien val_tags_lock (current_parsed_root->directory); 1278175270Sobrien 1279175270Sobrien /* Check for presence again since we have a lock now. */ 1280175270Sobrien if (is_in_val_tags (&db, name)) 1281175270Sobrien { 1282175270Sobrien clear_val_tags_lock (); 1283175270Sobrien if (db) 1284175270Sobrien dbm_close (db); 1285175270Sobrien return; 1286175270Sobrien } 1287175270Sobrien 1288175270Sobrien /* Casting out const should be safe here - input datums are not 1289175270Sobrien * written to by the myndbm functions. 1290175270Sobrien */ 1291175270Sobrien mytag.dptr = (char *)name; 1292175270Sobrien mytag.dsize = strlen (name); 1293175270Sobrien value.dptr = "y"; 1294175270Sobrien value.dsize = 1; 1295175270Sobrien 1296175270Sobrien if (dbm_store (db, mytag, value, DBM_REPLACE) < 0) 1297175270Sobrien error (0, errno, "failed to store %s into val-tags", name); 1298175270Sobrien dbm_close (db); 1299175270Sobrien 1300175270Sobrien clear_val_tags_lock (); 1301175270Sobrien} 1302175270Sobrien 1303175270Sobrien 1304175270Sobrien 1305128266Speterstatic Dtype val_direntproc PROTO ((void *, const char *, const char *, 1306128266Speter const char *, List *)); 1307128266Speter 130817721Speterstatic Dtype 130925839Speterval_direntproc (callerdat, dir, repository, update_dir, entries) 131025839Speter void *callerdat; 1311128266Speter const char *dir; 1312128266Speter const char *repository; 1313128266Speter const char *update_dir; 131425839Speter List *entries; 131517721Speter{ 131617721Speter /* This is not quite right--it doesn't get right the case of "cvs 131717721Speter update -d -r foobar" where foobar is a tag which exists only in 131817721Speter files in a directory which does not exist yet, but which is 131917721Speter about to be created. */ 132017721Speter if (isdir (dir)) 132166525Speter return R_PROCESS; 132217721Speter return R_SKIP_ALL; 132317721Speter} 132417721Speter 132517721Speter/* Check to see whether NAME is a valid tag. If so, return. If not 132617721Speter print an error message and exit. ARGC, ARGV, LOCAL, and AFLAG specify 132717721Speter which files we will be operating on. 132817721Speter 132917721Speter REPOSITORY is the repository if we need to cd into it, or NULL if 133017721Speter we are already there, or "" if we should do a W_LOCAL recursion. 133117721Speter Sorry for three cases, but the "" case is needed in case the 133217721Speter working directories come from diverse parts of the repository, the 133317721Speter NULL case avoids an unneccesary chdir, and the non-NULL, non-"" 133417721Speter case is needed for checkout, where we don't want to chdir if the 133517721Speter tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any 133617721Speter local directory. */ 133717721Spetervoid 133817721Spetertag_check_valid (name, argc, argv, local, aflag, repository) 133917721Speter char *name; 134017721Speter int argc; 134117721Speter char **argv; 134217721Speter int local; 134317721Speter int aflag; 134417721Speter char *repository; 134517721Speter{ 134617721Speter struct val_args the_val_args; 134717721Speter struct saved_cwd cwd; 134817721Speter int which; 134917721Speter 135017721Speter /* Numeric tags require only a syntactic check. */ 135154427Speter if (isdigit ((unsigned char) name[0])) 135217721Speter { 135317721Speter char *p; 135417721Speter for (p = name; *p != '\0'; ++p) 135517721Speter { 135654427Speter if (!(isdigit ((unsigned char) *p) || *p == '.')) 135717721Speter error (1, 0, "\ 135817721SpeterNumeric tag %s contains characters other than digits and '.'", name); 135917721Speter } 136017721Speter return; 136117721Speter } 136217721Speter 136325839Speter /* Special tags are always valid. */ 136425839Speter if (strcmp (name, TAG_BASE) == 0 136525839Speter || strcmp (name, TAG_HEAD) == 0) 136625839Speter return; 136725839Speter 1368133180Sdes if (readonlyfs) 1369133180Sdes return; 1370133180Sdes 1371175270Sobrien /* Verify that the tag is valid syntactically. Some later code once made 1372175270Sobrien * assumptions about this. 1373175270Sobrien */ 1374175270Sobrien RCS_check_tag (name); 137532785Speter 1376175270Sobrien if (is_in_val_tags (NULL, name)) return; 137717721Speter 137817721Speter /* We didn't find the tag in val-tags, so look through all the RCS files 137917721Speter to see whether it exists there. Yes, this is expensive, but there 138017721Speter is no other way to cope with a tag which might have been created 138125839Speter by an old version of CVS, from before val-tags was invented. 138217721Speter 138325839Speter Since we need this code anyway, we also use it to create 138425839Speter entries in val-tags in general (that is, the val-tags entry 138525839Speter will get created the first time the tag is used, not when the 138625839Speter tag is created). */ 138725839Speter 138817721Speter the_val_args.name = name; 138917721Speter the_val_args.found = 0; 139017721Speter 139117721Speter which = W_REPOS | W_ATTIC; 139217721Speter 139317721Speter if (repository != NULL) 139417721Speter { 139517721Speter if (repository[0] == '\0') 139617721Speter which |= W_LOCAL; 139717721Speter else 139817721Speter { 139917721Speter if (save_cwd (&cwd)) 140025839Speter error_exit (); 1401128266Speter if (CVS_CHDIR (repository) < 0) 140217721Speter error (1, errno, "cannot change to %s directory", repository); 140317721Speter } 140417721Speter } 140517721Speter 1406128266Speter start_recursion (val_fileproc, (FILESDONEPROC) NULL, 1407128266Speter val_direntproc, (DIRLEAVEPROC) NULL, 1408128266Speter (void *)&the_val_args, 1409128266Speter argc, argv, local, which, aflag, 1410128266Speter CVS_LOCK_READ, NULL, 1, repository); 141117721Speter if (repository != NULL && repository[0] != '\0') 141217721Speter { 141317721Speter if (restore_cwd (&cwd, NULL)) 1414107484Speter error_exit (); 141517721Speter free_cwd (&cwd); 141617721Speter } 141717721Speter 141817721Speter if (!the_val_args.found) 141917721Speter error (1, 0, "no such tag %s", name); 142017721Speter else 142117721Speter /* The tags is valid but not mentioned in val-tags. Add it. */ 1422175270Sobrien add_to_val_tags (name); 1423175270Sobrien} 142417721Speter 142517721Speter 142617721Speter 142725839Speter/* 142825839Speter * Check whether a join tag is valid. This is just like 142925839Speter * tag_check_valid, but we must stop before the colon if there is one. 143025839Speter */ 143125839Speter 143225839Spetervoid 143325839Spetertag_check_valid_join (join_tag, argc, argv, local, aflag, repository) 143425839Speter char *join_tag; 143525839Speter int argc; 143625839Speter char **argv; 143725839Speter int local; 143825839Speter int aflag; 143925839Speter char *repository; 144025839Speter{ 144125839Speter char *c, *s; 144225839Speter 144325839Speter c = xstrdup (join_tag); 144425839Speter s = strchr (c, ':'); 144525839Speter if (s != NULL) 144625839Speter { 144754427Speter if (isdigit ((unsigned char) join_tag[0])) 144825839Speter error (1, 0, 144925839Speter "Numeric join tag %s may not contain a date specifier", 145025839Speter join_tag); 145125839Speter 145225839Speter *s = '\0'; 145354427Speter /* hmmm... I think it makes sense to allow -j:<date>, but 145454427Speter * for now this fixes a bug where CVS just spins and spins (I 145554427Speter * think in the RCS code) looking for a zero length tag. 145654427Speter */ 145754427Speter if (!*c) 145854427Speter error (1, 0, 145954427Speter "argument to join may not contain a date specifier without a tag"); 146025839Speter } 146125839Speter 146225839Speter tag_check_valid (c, argc, argv, local, aflag, repository); 146325839Speter 146425839Speter free (c); 146525839Speter} 1466