tag.c revision 26065
117721Speter/* 217721Speter * Copyright (c) 1992, Brian Berliner and Jeff Polk 317721Speter * Copyright (c) 1989-1992, Brian Berliner 417721Speter * 517721Speter * You may distribute under the terms of the GNU General Public License as 617721Speter * specified in the README file that comes with the CVS 1.4 kit. 717721Speter * 817721Speter * Tag 917721Speter * 1017721Speter * Add or delete a symbolic name to an RCS file, or a collection of RCS files. 1117721Speter * Uses the checked out revision in the current directory. 1217721Speter */ 1317721Speter 1417721Speter#include "cvs.h" 1517721Speter#include "savecwd.h" 1617721Speter 1725839Speterstatic int check_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 1825839Speterstatic int check_filesdoneproc PROTO ((void *callerdat, int err, 1925839Speter char *repos, char *update_dir, 2025839Speter List *entries)); 2117721Speterstatic int pretag_proc PROTO((char *repository, char *filter)); 2217721Speterstatic void masterlist_delproc PROTO((Node *p)); 2317721Speterstatic void tag_delproc PROTO((Node *p)); 2417721Speterstatic int pretag_list_proc PROTO((Node *p, void *closure)); 2517721Speter 2625839Speterstatic Dtype tag_dirproc PROTO ((void *callerdat, char *dir, 2725839Speter char *repos, char *update_dir, 2825839Speter List *entries)); 2925839Speterstatic int tag_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 3025839Speterstatic int tag_filesdoneproc PROTO ((void *callerdat, int err, 3125839Speter char *repos, char *update_dir, 3225839Speter List *entries)); 3317721Speter 3417721Speterstatic char *numtag; 3517721Speterstatic char *date = NULL; 3617721Speterstatic char *symtag; 3717721Speterstatic int delete_flag; /* adding a tag by default */ 3817721Speterstatic int branch_mode; /* make an automagic "branch" tag */ 3917721Speterstatic int local; /* recursive by default */ 4017721Speterstatic int force_tag_match = 1; /* force tag to match by default */ 4117721Speterstatic int force_tag_move; /* don't force tag to move by default */ 4225839Speterstatic int check_uptodate; /* no uptodate-check by default */ 4317721Speter 4417721Speterstruct tag_info 4517721Speter{ 4617721Speter Ctype status; 4717721Speter char *rev; 4817721Speter char *tag; 4917721Speter char *options; 5017721Speter}; 5117721Speter 5217721Speterstruct master_lists 5317721Speter{ 5417721Speter List *tlist; 5517721Speter}; 5617721Speter 5717721Speterstatic List *mtlist; 5817721Speterstatic List *tlist; 5917721Speter 6017721Speterstatic const char *const tag_usage[] = 6117721Speter{ 6225839Speter "Usage: %s %s [-lRF] [-b] [-d] [-c] [-r tag|-D date] tag [files...]\n", 6317721Speter "\t-l\tLocal directory only, not recursive.\n", 6417721Speter "\t-R\tProcess directories recursively.\n", 6525839Speter "\t-d\tDelete the given tag.\n", 6617721Speter "\t-[rD]\tExisting tag or date.\n", 6725839Speter "\t-f\tForce a head revision if specified tag not found.\n", 6817721Speter "\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n", 6925839Speter "\t-F\tMove tag if it already exists.\n", 7025839Speter "\t-c\tCheck that working files are unmodified.\n", 7117721Speter NULL 7217721Speter}; 7317721Speter 7417721Speterint 7525839Spetercvstag (argc, argv) 7617721Speter int argc; 7717721Speter char **argv; 7817721Speter{ 7917721Speter int c; 8017721Speter int err = 0; 8117721Speter 8217721Speter if (argc == -1) 8317721Speter usage (tag_usage); 8417721Speter 8526065Speter optind = 0; 8625839Speter while ((c = getopt (argc, argv, "+FQqlRcdr:D:bf")) != -1) 8717721Speter { 8817721Speter switch (c) 8917721Speter { 9017721Speter case 'Q': 9117721Speter case 'q': 9217721Speter#ifdef SERVER_SUPPORT 9317721Speter /* The CVS 1.5 client sends these options (in addition to 9417721Speter Global_option requests), so we must ignore them. */ 9517721Speter if (!server_active) 9617721Speter#endif 9717721Speter error (1, 0, 9817721Speter "-q or -Q must be specified before \"%s\"", 9917721Speter command_name); 10017721Speter break; 10117721Speter case 'l': 10217721Speter local = 1; 10317721Speter break; 10417721Speter case 'R': 10517721Speter local = 0; 10617721Speter break; 10717721Speter case 'd': 10817721Speter delete_flag = 1; 10917721Speter break; 11025839Speter case 'c': 11125839Speter check_uptodate = 1; 11225839Speter break; 11317721Speter case 'r': 11417721Speter numtag = optarg; 11517721Speter break; 11617721Speter case 'D': 11717721Speter if (date) 11817721Speter free (date); 11917721Speter date = Make_Date (optarg); 12017721Speter break; 12117721Speter case 'f': 12217721Speter force_tag_match = 0; 12317721Speter break; 12417721Speter case 'b': 12517721Speter branch_mode = 1; 12617721Speter break; 12717721Speter case 'F': 12817721Speter force_tag_move = 1; 12917721Speter break; 13017721Speter case '?': 13117721Speter default: 13217721Speter usage (tag_usage); 13317721Speter break; 13417721Speter } 13517721Speter } 13617721Speter argc -= optind; 13717721Speter argv += optind; 13817721Speter 13917721Speter if (argc == 0) 14017721Speter usage (tag_usage); 14117721Speter symtag = argv[0]; 14217721Speter argc--; 14317721Speter argv++; 14417721Speter 14517721Speter if (date && numtag) 14617721Speter error (1, 0, "-r and -D options are mutually exclusive"); 14717721Speter if (delete_flag && branch_mode) 14817721Speter error (0, 0, "warning: -b ignored with -d options"); 14917721Speter RCS_check_tag (symtag); 15017721Speter 15117721Speter#ifdef CLIENT_SUPPORT 15217721Speter if (client_active) 15317721Speter { 15417721Speter /* We're the client side. Fire up the remote server. */ 15517721Speter start_server (); 15617721Speter 15717721Speter ign_setup (); 15817721Speter 15925839Speter if (!force_tag_match) 16025839Speter send_arg ("-f"); 16117721Speter if (local) 16217721Speter send_arg("-l"); 16317721Speter if (delete_flag) 16417721Speter send_arg("-d"); 16525839Speter if (check_uptodate) 16625839Speter send_arg("-c"); 16717721Speter if (branch_mode) 16817721Speter send_arg("-b"); 16917721Speter if (force_tag_move) 17017721Speter send_arg("-F"); 17117721Speter 17217721Speter if (numtag) 17317721Speter option_with_arg ("-r", numtag); 17417721Speter if (date) 17517721Speter client_senddate (date); 17617721Speter 17717721Speter send_arg (symtag); 17817721Speter 17917721Speter send_file_names (argc, argv, SEND_EXPAND_WILD); 18025839Speter 18125839Speter /* SEND_NO_CONTENTS has a mildly bizarre interaction with 18225839Speter check_uptodate; if the timestamp is modified but the file 18325839Speter is unmodified, the check will fail, only to have "cvs diff" 18425839Speter show no differences (and one must do "update" or something to 18525839Speter reset the client's notion of the timestamp). */ 18625839Speter 18725839Speter send_files (argc, argv, local, 0, SEND_NO_CONTENTS); 18817721Speter send_to_server ("tag\012", 0); 18917721Speter return get_responses_and_close (); 19017721Speter } 19117721Speter#endif 19217721Speter 19317721Speter if (numtag != NULL) 19417721Speter tag_check_valid (numtag, argc, argv, local, 0, ""); 19517721Speter 19617721Speter /* check to make sure they are authorized to tag all the 19717721Speter specified files in the repository */ 19817721Speter 19917721Speter mtlist = getlist(); 20017721Speter err = start_recursion (check_fileproc, check_filesdoneproc, 20125839Speter (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, 20217721Speter argc, argv, local, W_LOCAL, 0, 1, 20325839Speter (char *) NULL, 1); 20417721Speter 20517721Speter if (err) 20617721Speter { 20717721Speter error (1, 0, "correct the above errors first!"); 20817721Speter } 20917721Speter 21017721Speter /* start the recursion processor */ 21125839Speter err = start_recursion (tag_fileproc, tag_filesdoneproc, tag_dirproc, 21225839Speter (DIRLEAVEPROC) NULL, NULL, argc, argv, local, 21325839Speter W_LOCAL, 0, 0, (char *) NULL, 1); 21417721Speter dellist(&mtlist); 21517721Speter return (err); 21617721Speter} 21717721Speter 21817721Speter/* check file that is to be tagged */ 21917721Speter/* All we do here is add it to our list */ 22017721Speter 22117721Speterstatic int 22225839Spetercheck_fileproc (callerdat, finfo) 22325839Speter void *callerdat; 22417721Speter struct file_info *finfo; 22517721Speter{ 22617721Speter char *xdir; 22717721Speter Node *p; 22817721Speter Vers_TS *vers; 22917721Speter 23025839Speter if (check_uptodate) 23125839Speter { 23225839Speter Ctype status = Classify_File (finfo, (char *) NULL, (char *) NULL, 23325839Speter (char *) NULL, 1, 0, &vers, 0); 23425839Speter if ((status != T_UPTODATE) && (status != T_CHECKOUT)) 23525839Speter { 23625839Speter error (0, 0, "%s is locally modified", finfo->fullname); 23725839Speter return (1); 23825839Speter } 23925839Speter } 24025839Speter 24117721Speter if (finfo->update_dir[0] == '\0') 24217721Speter xdir = "."; 24317721Speter else 24417721Speter xdir = finfo->update_dir; 24517721Speter if ((p = findnode (mtlist, xdir)) != NULL) 24617721Speter { 24717721Speter tlist = ((struct master_lists *) p->data)->tlist; 24817721Speter } 24917721Speter else 25017721Speter { 25117721Speter struct master_lists *ml; 25217721Speter 25317721Speter tlist = getlist (); 25417721Speter p = getnode (); 25517721Speter p->key = xstrdup (xdir); 25617721Speter p->type = UPDATE; 25717721Speter ml = (struct master_lists *) 25817721Speter xmalloc (sizeof (struct master_lists)); 25917721Speter ml->tlist = tlist; 26017721Speter p->data = (char *) ml; 26117721Speter p->delproc = masterlist_delproc; 26217721Speter (void) addnode (mtlist, p); 26317721Speter } 26417721Speter /* do tlist */ 26517721Speter p = getnode (); 26617721Speter p->key = xstrdup (finfo->file); 26717721Speter p->type = UPDATE; 26817721Speter p->delproc = tag_delproc; 26925839Speter vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); 27017721Speter if (vers->srcfile == NULL) 27117721Speter { 27217721Speter if (!really_quiet) 27317721Speter error (0, 0, "nothing known about %s", finfo->file); 27417721Speter return (1); 27517721Speter } 27625839Speter p->data = RCS_getversion(vers->srcfile, numtag, date, force_tag_match, 27725839Speter (int *) NULL); 27817721Speter if (p->data != NULL) 27917721Speter { 28017721Speter int addit = 1; 28117721Speter char *oversion; 28217721Speter 28325839Speter oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 28425839Speter (int *) NULL); 28517721Speter if (oversion == NULL) 28617721Speter { 28717721Speter if (delete_flag) 28817721Speter { 28917721Speter addit = 0; 29017721Speter } 29117721Speter } 29217721Speter else if (strcmp(oversion, p->data) == 0) 29317721Speter { 29417721Speter addit = 0; 29517721Speter } 29617721Speter else if (!force_tag_move) 29717721Speter { 29817721Speter addit = 0; 29917721Speter } 30017721Speter if (oversion != NULL) 30117721Speter { 30217721Speter free(oversion); 30317721Speter } 30417721Speter if (!addit) 30517721Speter { 30617721Speter free(p->data); 30717721Speter p->data = NULL; 30817721Speter } 30917721Speter } 31017721Speter freevers_ts(&vers); 31117721Speter (void) addnode (tlist, p); 31217721Speter return (0); 31317721Speter} 31417721Speter 31517721Speterstatic int 31625839Spetercheck_filesdoneproc (callerdat, err, repos, update_dir, entries) 31725839Speter void *callerdat; 31817721Speter int err; 31917721Speter char *repos; 32017721Speter char *update_dir; 32125839Speter List *entries; 32217721Speter{ 32317721Speter int n; 32417721Speter Node *p; 32517721Speter 32617721Speter p = findnode(mtlist, update_dir); 32717721Speter if (p != NULL) 32817721Speter { 32917721Speter tlist = ((struct master_lists *) p->data)->tlist; 33017721Speter } 33117721Speter else 33217721Speter { 33317721Speter tlist = (List *) NULL; 33417721Speter } 33517721Speter if ((tlist == NULL) || (tlist->list->next == tlist->list)) 33617721Speter { 33717721Speter return (err); 33817721Speter } 33917721Speter if ((n = Parse_Info(CVSROOTADM_TAGINFO, repos, pretag_proc, 1)) > 0) 34017721Speter { 34117721Speter error (0, 0, "Pre-tag check failed"); 34217721Speter err += n; 34317721Speter } 34417721Speter return (err); 34517721Speter} 34617721Speter 34717721Speterstatic int 34817721Speterpretag_proc(repository, filter) 34917721Speter char *repository; 35017721Speter char *filter; 35117721Speter{ 35217721Speter if (filter[0] == '/') 35317721Speter { 35417721Speter char *s, *cp; 35517721Speter 35617721Speter s = xstrdup(filter); 35717721Speter for (cp=s; *cp; cp++) 35817721Speter { 35917721Speter if (isspace(*cp)) 36017721Speter { 36117721Speter *cp = '\0'; 36217721Speter break; 36317721Speter } 36417721Speter } 36517721Speter if (!isfile(s)) 36617721Speter { 36717721Speter error (0, errno, "cannot find pre-tag filter '%s'", s); 36817721Speter free(s); 36917721Speter return (1); 37017721Speter } 37117721Speter free(s); 37217721Speter } 37317721Speter run_setup("%s %s %s %s", 37417721Speter filter, 37517721Speter symtag, 37617721Speter delete_flag ? "del" : force_tag_move ? "mov" : "add", 37717721Speter repository); 37817721Speter walklist(tlist, pretag_list_proc, NULL); 37917721Speter return (run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY)); 38017721Speter} 38117721Speter 38217721Speterstatic void 38317721Spetermasterlist_delproc(p) 38417721Speter Node *p; 38517721Speter{ 38617721Speter struct master_lists *ml; 38717721Speter 38817721Speter ml = (struct master_lists *)p->data; 38917721Speter dellist(&ml->tlist); 39017721Speter free(ml); 39117721Speter return; 39217721Speter} 39317721Speter 39417721Speterstatic void 39517721Spetertag_delproc(p) 39617721Speter Node *p; 39717721Speter{ 39817721Speter if (p->data != NULL) 39917721Speter { 40017721Speter free(p->data); 40117721Speter p->data = NULL; 40217721Speter } 40317721Speter return; 40417721Speter} 40517721Speter 40617721Speterstatic int 40717721Speterpretag_list_proc(p, closure) 40817721Speter Node *p; 40917721Speter void *closure; 41017721Speter{ 41117721Speter if (p->data != NULL) 41217721Speter { 41317721Speter run_arg(p->key); 41417721Speter run_arg(p->data); 41517721Speter } 41617721Speter return (0); 41717721Speter} 41817721Speter 41917721Speter 42017721Speter/* 42117721Speter * Called to tag a particular file (the currently checked out version is 42217721Speter * tagged with the specified tag - or the specified tag is deleted). 42317721Speter */ 42417721Speter/* ARGSUSED */ 42517721Speterstatic int 42625839Spetertag_fileproc (callerdat, finfo) 42725839Speter void *callerdat; 42817721Speter struct file_info *finfo; 42917721Speter{ 43017721Speter char *version, *oversion; 43117721Speter char *nversion = NULL; 43217721Speter char *rev; 43317721Speter Vers_TS *vers; 43417721Speter int retcode = 0; 43517721Speter 43625839Speter /* Lock the directory if it is not already locked. We can't rely 43725839Speter on tag_dirproc because it won't handle the case where the user 43825839Speter specifies a list of files on the command line. */ 43925839Speter /* We do not need to acquire a full write lock for the tag operation: 44025839Speter the revisions are obtained from the working directory, so we do not 44125839Speter require consistency across the entire repository. However, we do 44225839Speter need to prevent simultaneous tag operations from interfering with 44325839Speter each other. Therefore, we write lock each directory as we enter 44425839Speter it, and unlock it as we leave it. */ 44525839Speter lock_dir_for_write (finfo->repository); 44617721Speter 44725839Speter vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0); 44825839Speter 44917721Speter if ((numtag != NULL) || (date != NULL)) 45017721Speter { 45117721Speter nversion = RCS_getversion(vers->srcfile, 45217721Speter numtag, 45317721Speter date, 45425839Speter force_tag_match, 45525839Speter (int *) NULL); 45617721Speter if (nversion == NULL) 45717721Speter { 45817721Speter freevers_ts (&vers); 45917721Speter return (0); 46017721Speter } 46117721Speter } 46217721Speter if (delete_flag) 46317721Speter { 46417721Speter 46517721Speter /* 46617721Speter * If -d is specified, "force_tag_match" is set, so that this call to 46717721Speter * RCS_getversion() will return a NULL version string if the symbolic 46817721Speter * tag does not exist in the RCS file. 46917721Speter * 47017721Speter * This is done here because it's MUCH faster than just blindly calling 47117721Speter * "rcs" to remove the tag... trust me. 47217721Speter */ 47317721Speter 47425839Speter version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 47525839Speter (int *) NULL); 47617721Speter if (version == NULL || vers->srcfile == NULL) 47717721Speter { 47817721Speter freevers_ts (&vers); 47917721Speter return (0); 48017721Speter } 48117721Speter free (version); 48217721Speter 48325839Speter if ((retcode = RCS_deltag(vers->srcfile, symtag, 1)) != 0) 48417721Speter { 48517721Speter if (!quiet) 48617721Speter error (0, retcode == -1 ? errno : 0, 48717721Speter "failed to remove tag %s from %s", symtag, 48817721Speter vers->srcfile->path); 48917721Speter freevers_ts (&vers); 49017721Speter return (1); 49117721Speter } 49217721Speter 49317721Speter /* warm fuzzies */ 49417721Speter if (!really_quiet) 49517721Speter { 49625839Speter cvs_output ("D ", 2); 49725839Speter cvs_output (finfo->fullname, 0); 49825839Speter cvs_output ("\n", 1); 49917721Speter } 50017721Speter 50117721Speter freevers_ts (&vers); 50217721Speter return (0); 50317721Speter } 50417721Speter 50517721Speter /* 50617721Speter * If we are adding a tag, we need to know which version we have checked 50717721Speter * out and we'll tag that version. 50817721Speter */ 50917721Speter if (nversion == NULL) 51017721Speter { 51117721Speter version = vers->vn_user; 51217721Speter } 51317721Speter else 51417721Speter { 51517721Speter version = nversion; 51617721Speter } 51717721Speter if (version == NULL) 51817721Speter { 51917721Speter freevers_ts (&vers); 52017721Speter return (0); 52117721Speter } 52217721Speter else if (strcmp (version, "0") == 0) 52317721Speter { 52417721Speter if (!quiet) 52517721Speter error (0, 0, "couldn't tag added but un-commited file `%s'", finfo->file); 52617721Speter freevers_ts (&vers); 52717721Speter return (0); 52817721Speter } 52917721Speter else if (version[0] == '-') 53017721Speter { 53117721Speter if (!quiet) 53217721Speter error (0, 0, "skipping removed but un-commited file `%s'", finfo->file); 53317721Speter freevers_ts (&vers); 53417721Speter return (0); 53517721Speter } 53617721Speter else if (vers->srcfile == NULL) 53717721Speter { 53817721Speter if (!quiet) 53917721Speter error (0, 0, "cannot find revision control file for `%s'", finfo->file); 54017721Speter freevers_ts (&vers); 54117721Speter return (0); 54217721Speter } 54317721Speter 54417721Speter /* 54517721Speter * As an enhancement for the case where a tag is being re-applied to a 54617721Speter * large number of files, make one extra call to RCS_getversion to see 54717721Speter * if the tag is already set in the RCS file. If so, check to see if it 54817721Speter * needs to be moved. If not, do nothing. This will likely save a lot of 54917721Speter * time when simply moving the tag to the "current" head revisions of a 55017721Speter * module -- which I have found to be a typical tagging operation. 55117721Speter */ 55217721Speter rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version; 55325839Speter oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1, 55425839Speter (int *) NULL); 55517721Speter if (oversion != NULL) 55617721Speter { 55717721Speter int isbranch = RCS_isbranch (finfo->rcs, symtag); 55817721Speter 55917721Speter /* 56017721Speter * if versions the same and neither old or new are branches don't have 56117721Speter * to do anything 56217721Speter */ 56317721Speter if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch) 56417721Speter { 56517721Speter free (oversion); 56617721Speter freevers_ts (&vers); 56717721Speter return (0); 56817721Speter } 56917721Speter 57017721Speter if (!force_tag_move) 57117721Speter { 57217721Speter /* we're NOT going to move the tag */ 57325839Speter cvs_output ("W ", 2); 57425839Speter cvs_output (finfo->fullname, 0); 57525839Speter cvs_output (" : ", 0); 57625839Speter cvs_output (symtag, 0); 57725839Speter cvs_output (" already exists on ", 0); 57825839Speter cvs_output (isbranch ? "branch" : "version", 0); 57925839Speter cvs_output (" ", 0); 58025839Speter cvs_output (oversion, 0); 58125839Speter cvs_output (" : NOT MOVING tag to ", 0); 58225839Speter cvs_output (branch_mode ? "branch" : "version", 0); 58325839Speter cvs_output (" ", 0); 58425839Speter cvs_output (rev, 0); 58525839Speter cvs_output ("\n", 1); 58617721Speter free (oversion); 58717721Speter freevers_ts (&vers); 58817721Speter return (0); 58917721Speter } 59017721Speter free (oversion); 59117721Speter } 59217721Speter 59325839Speter if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0) 59417721Speter { 59517721Speter error (1, retcode == -1 ? errno : 0, 59617721Speter "failed to set tag %s to revision %s in %s", 59717721Speter symtag, rev, vers->srcfile->path); 59817721Speter freevers_ts (&vers); 59917721Speter return (1); 60017721Speter } 60117721Speter 60217721Speter /* more warm fuzzies */ 60317721Speter if (!really_quiet) 60417721Speter { 60525839Speter cvs_output ("T ", 2); 60625839Speter cvs_output (finfo->fullname, 0); 60725839Speter cvs_output ("\n", 1); 60817721Speter } 60917721Speter 61017721Speter if (nversion != NULL) 61117721Speter { 61217721Speter free (nversion); 61317721Speter } 61417721Speter freevers_ts (&vers); 61517721Speter return (0); 61617721Speter} 61717721Speter 61825839Speter/* Clear any lock we may hold on the current directory. */ 61925839Speter 62025839Speterstatic int 62125839Spetertag_filesdoneproc (callerdat, err, repos, update_dir, entries) 62225839Speter void *callerdat; 62325839Speter int err; 62425839Speter char *repos; 62525839Speter char *update_dir; 62625839Speter List *entries; 62725839Speter{ 62825839Speter Lock_Cleanup (); 62925839Speter 63025839Speter return (err); 63125839Speter} 63225839Speter 63317721Speter/* 63417721Speter * Print a warm fuzzy message 63517721Speter */ 63617721Speter/* ARGSUSED */ 63717721Speterstatic Dtype 63825839Spetertag_dirproc (callerdat, dir, repos, update_dir, entries) 63925839Speter void *callerdat; 64017721Speter char *dir; 64117721Speter char *repos; 64217721Speter char *update_dir; 64325839Speter List *entries; 64417721Speter{ 64517721Speter if (!quiet) 64617721Speter error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging", update_dir); 64717721Speter return (R_PROCESS); 64817721Speter} 64917721Speter 65017721Speter/* Code relating to the val-tags file. Note that this file has no way 65117721Speter of knowing when a tag has been deleted. The problem is that there 65217721Speter is no way of knowing whether a tag still exists somewhere, when we 65317721Speter delete it some places. Using per-directory val-tags files (in 65417721Speter CVSREP) might be better, but that might slow down the process of 65517721Speter verifying that a tag is correct (maybe not, for the likely cases, 65617721Speter if carefully done), and/or be harder to implement correctly. */ 65717721Speter 65817721Speterstruct val_args { 65917721Speter char *name; 66017721Speter int found; 66117721Speter}; 66217721Speter 66325839Speterstatic int val_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 66417721Speter 66517721Speterstatic int 66625839Speterval_fileproc (callerdat, finfo) 66725839Speter void *callerdat; 66817721Speter struct file_info *finfo; 66917721Speter{ 67017721Speter RCSNode *rcsdata; 67125839Speter struct val_args *args = (struct val_args *)callerdat; 67217721Speter char *tag; 67317721Speter 67417721Speter if ((rcsdata = finfo->rcs) == NULL) 67517721Speter /* Not sure this can happen, after all we passed only 67617721Speter W_REPOS | W_ATTIC. */ 67717721Speter return 0; 67817721Speter 67925839Speter tag = RCS_gettag (rcsdata, args->name, 1, (int *) NULL); 68017721Speter if (tag != NULL) 68117721Speter { 68217721Speter /* FIXME: should find out a way to stop the search at this point. */ 68317721Speter args->found = 1; 68417721Speter free (tag); 68517721Speter } 68617721Speter return 0; 68717721Speter} 68817721Speter 68925839Speterstatic Dtype val_direntproc PROTO ((void *, char *, char *, char *, List *)); 69017721Speter 69117721Speterstatic Dtype 69225839Speterval_direntproc (callerdat, dir, repository, update_dir, entries) 69325839Speter void *callerdat; 69417721Speter char *dir; 69517721Speter char *repository; 69617721Speter char *update_dir; 69725839Speter List *entries; 69817721Speter{ 69917721Speter /* This is not quite right--it doesn't get right the case of "cvs 70017721Speter update -d -r foobar" where foobar is a tag which exists only in 70117721Speter files in a directory which does not exist yet, but which is 70217721Speter about to be created. */ 70317721Speter if (isdir (dir)) 70417721Speter return 0; 70517721Speter return R_SKIP_ALL; 70617721Speter} 70717721Speter 70817721Speter/* Check to see whether NAME is a valid tag. If so, return. If not 70917721Speter print an error message and exit. ARGC, ARGV, LOCAL, and AFLAG specify 71017721Speter which files we will be operating on. 71117721Speter 71217721Speter REPOSITORY is the repository if we need to cd into it, or NULL if 71317721Speter we are already there, or "" if we should do a W_LOCAL recursion. 71417721Speter Sorry for three cases, but the "" case is needed in case the 71517721Speter working directories come from diverse parts of the repository, the 71617721Speter NULL case avoids an unneccesary chdir, and the non-NULL, non-"" 71717721Speter case is needed for checkout, where we don't want to chdir if the 71817721Speter tag is found in CVSROOTADM_VALTAGS, but there is not (yet) any 71917721Speter local directory. */ 72017721Spetervoid 72117721Spetertag_check_valid (name, argc, argv, local, aflag, repository) 72217721Speter char *name; 72317721Speter int argc; 72417721Speter char **argv; 72517721Speter int local; 72617721Speter int aflag; 72717721Speter char *repository; 72817721Speter{ 72917721Speter DBM *db; 73017721Speter char *valtags_filename; 73117721Speter int err; 73217721Speter datum mytag; 73317721Speter struct val_args the_val_args; 73417721Speter struct saved_cwd cwd; 73517721Speter int which; 73617721Speter 73717721Speter /* Numeric tags require only a syntactic check. */ 73817721Speter if (isdigit (name[0])) 73917721Speter { 74017721Speter char *p; 74117721Speter for (p = name; *p != '\0'; ++p) 74217721Speter { 74317721Speter if (!(isdigit (*p) || *p == '.')) 74417721Speter error (1, 0, "\ 74517721SpeterNumeric tag %s contains characters other than digits and '.'", name); 74617721Speter } 74717721Speter return; 74817721Speter } 74917721Speter 75025839Speter /* Special tags are always valid. */ 75125839Speter if (strcmp (name, TAG_BASE) == 0 75225839Speter || strcmp (name, TAG_HEAD) == 0) 75325839Speter return; 75425839Speter 75517721Speter mytag.dptr = name; 75617721Speter mytag.dsize = strlen (name); 75717721Speter 75825839Speter valtags_filename = xmalloc (strlen (CVSroot_directory) 75925839Speter + sizeof CVSROOTADM 76025839Speter + sizeof CVSROOTADM_VALTAGS + 20); 76125839Speter strcpy (valtags_filename, CVSroot_directory); 76217721Speter strcat (valtags_filename, "/"); 76317721Speter strcat (valtags_filename, CVSROOTADM); 76417721Speter strcat (valtags_filename, "/"); 76517721Speter strcat (valtags_filename, CVSROOTADM_VALTAGS); 76617721Speter db = dbm_open (valtags_filename, O_RDWR, 0666); 76717721Speter if (db == NULL) 76817721Speter { 76917721Speter if (!existence_error (errno)) 77017721Speter error (1, errno, "cannot read %s", valtags_filename); 77117721Speter 77217721Speter /* If the file merely fails to exist, we just keep going and create 77317721Speter it later if need be. */ 77417721Speter } 77517721Speter else 77617721Speter { 77717721Speter datum val; 77817721Speter 77917721Speter val = dbm_fetch (db, mytag); 78017721Speter if (val.dptr != NULL) 78117721Speter { 78217721Speter /* Found. The tag is valid. */ 78317721Speter dbm_close (db); 78417721Speter free (valtags_filename); 78517721Speter return; 78617721Speter } 78717721Speter /* FIXME: should check errors somehow (add dbm_error to myndbm.c?). */ 78817721Speter } 78917721Speter 79017721Speter /* We didn't find the tag in val-tags, so look through all the RCS files 79117721Speter to see whether it exists there. Yes, this is expensive, but there 79217721Speter is no other way to cope with a tag which might have been created 79325839Speter by an old version of CVS, from before val-tags was invented. 79417721Speter 79525839Speter Since we need this code anyway, we also use it to create 79625839Speter entries in val-tags in general (that is, the val-tags entry 79725839Speter will get created the first time the tag is used, not when the 79825839Speter tag is created). */ 79925839Speter 80017721Speter the_val_args.name = name; 80117721Speter the_val_args.found = 0; 80217721Speter 80317721Speter which = W_REPOS | W_ATTIC; 80417721Speter 80517721Speter if (repository != NULL) 80617721Speter { 80717721Speter if (repository[0] == '\0') 80817721Speter which |= W_LOCAL; 80917721Speter else 81017721Speter { 81117721Speter if (save_cwd (&cwd)) 81225839Speter error_exit (); 81325839Speter if ( CVS_CHDIR (repository) < 0) 81417721Speter error (1, errno, "cannot change to %s directory", repository); 81517721Speter } 81617721Speter } 81717721Speter 81817721Speter err = start_recursion (val_fileproc, (FILESDONEPROC) NULL, 81917721Speter val_direntproc, (DIRLEAVEPROC) NULL, 82025839Speter (void *)&the_val_args, 82117721Speter argc, argv, local, which, aflag, 82225839Speter 1, NULL, 1); 82317721Speter if (repository != NULL && repository[0] != '\0') 82417721Speter { 82517721Speter if (restore_cwd (&cwd, NULL)) 82617721Speter exit (EXIT_FAILURE); 82717721Speter free_cwd (&cwd); 82817721Speter } 82917721Speter 83017721Speter if (!the_val_args.found) 83117721Speter error (1, 0, "no such tag %s", name); 83217721Speter else 83317721Speter { 83417721Speter /* The tags is valid but not mentioned in val-tags. Add it. */ 83517721Speter datum value; 83617721Speter 83717721Speter if (noexec) 83817721Speter { 83917721Speter if (db != NULL) 84017721Speter dbm_close (db); 84117721Speter free (valtags_filename); 84217721Speter return; 84317721Speter } 84417721Speter 84517721Speter if (db == NULL) 84617721Speter { 84717721Speter mode_t omask; 84817721Speter omask = umask (cvsumask); 84917721Speter db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666); 85017721Speter (void) umask (omask); 85117721Speter 85217721Speter if (db == NULL) 85317721Speter { 85417721Speter error (0, errno, "cannot create %s", valtags_filename); 85517721Speter free (valtags_filename); 85617721Speter return; 85717721Speter } 85817721Speter } 85917721Speter value.dptr = "y"; 86017721Speter value.dsize = 1; 86117721Speter if (dbm_store (db, mytag, value, DBM_REPLACE) < 0) 86217721Speter error (0, errno, "cannot store %s into %s", name, 86317721Speter valtags_filename); 86417721Speter dbm_close (db); 86517721Speter } 86617721Speter free (valtags_filename); 86717721Speter} 86825839Speter 86925839Speter/* 87025839Speter * Check whether a join tag is valid. This is just like 87125839Speter * tag_check_valid, but we must stop before the colon if there is one. 87225839Speter */ 87325839Speter 87425839Spetervoid 87525839Spetertag_check_valid_join (join_tag, argc, argv, local, aflag, repository) 87625839Speter char *join_tag; 87725839Speter int argc; 87825839Speter char **argv; 87925839Speter int local; 88025839Speter int aflag; 88125839Speter char *repository; 88225839Speter{ 88325839Speter char *c, *s; 88425839Speter 88525839Speter c = xstrdup (join_tag); 88625839Speter s = strchr (c, ':'); 88725839Speter if (s != NULL) 88825839Speter { 88925839Speter if (isdigit (join_tag[0])) 89025839Speter error (1, 0, 89125839Speter "Numeric join tag %s may not contain a date specifier", 89225839Speter join_tag); 89325839Speter 89425839Speter *s = '\0'; 89525839Speter } 89625839Speter 89725839Speter tag_check_valid (c, argc, argv, local, aflag, repository); 89825839Speter 89925839Speter free (c); 90025839Speter} 901