#include "cvs.h"
#include "save-cwd.h"
static int rtag_proc (int argc, char **argv, char *xwhere,
char *mwhere, char *mfile, int shorten,
int local_specified, char *mname, char *msg);
static int check_fileproc (void *callerdat, struct file_info *finfo);
static int check_filesdoneproc (void *callerdat, int err,
const char *repos, const char *update_dir,
List *entries);
static int pretag_proc (const char *_repository, const char *_filter,
void *_closure);
static void masterlist_delproc (Node *_p);
static void tag_delproc (Node *_p);
static int pretag_list_to_args_proc (Node *_p, void *_closure);
static Dtype tag_dirproc (void *callerdat, const char *dir,
const char *repos, const char *update_dir,
List *entries);
static int rtag_fileproc (void *callerdat, struct file_info *finfo);
static int rtag_delete (RCSNode *rcsfile);
static int tag_fileproc (void *callerdat, struct file_info *finfo);
static char *numtag;
static bool numtag_validated = false;
static char *date = NULL;
static char *symtag;
static bool delete_flag;
static bool branch_mode;
static bool disturb_branch_tags = false;
static bool force_tag_match = true;
static bool force_tag_move;
static bool check_uptodate;
static bool attic_too;
static bool is_rtag;
struct tag_info
{
Ctype status;
char *oldrev;
char *rev;
char *tag;
char *options;
};
struct master_lists
{
List *tlist;
};
static List *mtlist;
static const char rtag_opts[] = "+aBbdFflnQqRr:D:";
static const char *const rtag_usage[] =
{
"Usage: %s %s [-abdFflnR] [-r rev|-D date] tag modules...\n",
"\t-a\tClear tag from removed files that would not otherwise be tagged.\n",
"\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
"\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n",
"\t-d\tDelete the given tag.\n",
"\t-F\tMove tag if it already exists.\n",
"\t-f\tForce a head revision match if tag/date not found.\n",
"\t-l\tLocal directory only, not recursive.\n",
"\t-n\tNo execution of 'tag program'.\n",
"\t-R\tProcess directories recursively.\n",
"\t-r rev\tExisting revision/tag.\n",
"\t-D\tExisting date.\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
static const char tag_opts[] = "+BbcdFflQqRr:D:";
static const char *const tag_usage[] =
{
"Usage: %s %s [-bcdFflR] [-r rev|-D date] tag [files...]\n",
"\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
"\t-B\tAllows -F and -d to disturb branch tags. Use with extreme care.\n",
"\t-c\tCheck that working files are unmodified.\n",
"\t-d\tDelete the given tag.\n",
"\t-F\tMove tag if it already exists.\n",
"\t-f\tForce a head revision match if tag/date not found.\n",
"\t-l\tLocal directory only, not recursive.\n",
"\t-R\tProcess directories recursively.\n",
"\t-r rev\tExisting revision/tag.\n",
"\t-D\tExisting date.\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
int
cvstag (int argc, char **argv)
{
bool local = false;
int c;
int err = 0;
bool run_module_prog = true;
is_rtag = (strcmp (cvs_cmd_name, "rtag") == 0);
if (argc == -1)
usage (is_rtag ? rtag_usage : tag_usage);
optind = 0;
while ((c = getopt (argc, argv, is_rtag ? rtag_opts : tag_opts)) != -1)
{
switch (c)
{
case 'a':
attic_too = true;
break;
case 'b':
branch_mode = true;
break;
case 'B':
disturb_branch_tags = true;
break;
case 'c':
check_uptodate = true;
break;
case 'd':
delete_flag = true;
break;
case 'F':
force_tag_move = true;
break;
case 'f':
force_tag_match = false;
break;
case 'l':
local = true;
break;
case 'n':
run_module_prog = false;
break;
case 'Q':
case 'q':
if (!server_active)
error (1, 0,
"-q or -Q must be specified before \"%s\"",
cvs_cmd_name);
break;
case 'R':
local = false;
break;
case 'r':
parse_tagdate (&numtag, &date, optarg);
break;
case 'D':
if (date) free (date);
date = Make_Date (optarg);
break;
case '?':
default:
usage (is_rtag ? rtag_usage : tag_usage);
break;
}
}
argc -= optind;
argv += optind;
if (argc < (is_rtag ? 2 : 1))
usage (is_rtag ? rtag_usage : tag_usage);
symtag = argv[0];
argc--;
argv++;
if (date && delete_flag)
error (1, 0, "-d makes no sense with a date specification.");
if (delete_flag && branch_mode)
error (0, 0, "warning: -b ignored with -d options");
RCS_check_tag (symtag);
#ifdef CLIENT_SUPPORT
if (current_parsed_root->isremote)
{
start_server ();
ign_setup ();
if (attic_too)
send_arg ("-a");
if (branch_mode)
send_arg ("-b");
if (disturb_branch_tags)
send_arg ("-B");
if (check_uptodate)
send_arg ("-c");
if (delete_flag)
send_arg ("-d");
if (force_tag_move)
send_arg ("-F");
if (!force_tag_match)
send_arg ("-f");
if (local)
send_arg ("-l");
if (!run_module_prog)
send_arg ("-n");
if (numtag)
option_with_arg ("-r", numtag);
if (date)
client_senddate (date);
send_arg ("--");
send_arg (symtag);
if (is_rtag)
{
int i;
for (i = 0; i < argc; ++i)
send_arg (argv[i]);
send_to_server ("rtag\012", 0);
}
else
{
send_files (argc, argv, local, 0,
check_uptodate ? 0 : SEND_NO_CONTENTS);
send_file_names (argc, argv, SEND_EXPAND_WILD);
send_to_server ("tag\012", 0);
}
return get_responses_and_close ();
}
#endif
if (is_rtag)
{
DBM *db;
int i;
db = open_module ();
for (i = 0; i < argc; i++)
{
history_write ('T', (delete_flag ? "D" : (numtag ? numtag :
(date ? date : "A"))), symtag, argv[i], "");
err += do_module (db, argv[i], TAG,
delete_flag ? "Untagging" : "Tagging",
rtag_proc, NULL, 0, local, run_module_prog,
0, symtag);
}
close_module (db);
}
else
{
err = rtag_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL,
NULL);
}
return err;
}
struct pretag_proc_data {
List *tlist;
bool delete_flag;
bool force_tag_move;
char *symtag;
};
static int
posttag_proc (const char *repository, const char *filter, void *closure)
{
char *cmdline;
const char *srepos = Short_Repository (repository);
struct pretag_proc_data *ppd = closure;
cmdline = format_cmdline (
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
false, srepos,
#endif
filter,
"t", "s", ppd->symtag,
"o", "s", ppd->delete_flag
? "del" : ppd->force_tag_move ? "mov" : "add",
"b", "c", delete_flag
? '?' : branch_mode ? 'T' : 'N',
"c", "s", cvs_cmd_name,
#ifdef SERVER_SUPPORT
"R", "s", referrer ? referrer->original : "NONE",
#endif
"p", "s", srepos,
"r", "s", current_parsed_root->directory,
"sVv", ",", ppd->tlist,
pretag_list_to_args_proc, (void *) NULL,
(char *) NULL);
if (!cmdline || !strlen (cmdline))
{
if (cmdline) free (cmdline);
error (0, 0, "pretag proc resolved to the empty string!");
return 1;
}
run_setup (cmdline);
free (cmdline);
return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
}
static int
tag_filesdoneproc (void *callerdat, int err, const char *repository,
const char *update_dir, List *entries)
{
Node *p;
List *mtlist, *tlist;
struct pretag_proc_data ppd;
TRACE (TRACE_FUNCTION, "tag_filesdoneproc (%d, %s, %s)", err, repository,
update_dir);
mtlist = callerdat;
p = findnode (mtlist, update_dir);
if (p != NULL)
tlist = ((struct master_lists *) p->data)->tlist;
else
tlist = NULL;
if (tlist == NULL || tlist->list->next == tlist->list)
return err;
ppd.tlist = tlist;
ppd.delete_flag = delete_flag;
ppd.force_tag_move = force_tag_move;
ppd.symtag = symtag;
Parse_Info (CVSROOTADM_POSTTAG, repository, posttag_proc,
PIOPT_ALL, &ppd);
return err;
}
static int
rtag_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
int shorten, int local_specified, char *mname, char *msg)
{
char *myargv[2];
int err = 0;
int which;
char *repository;
char *where;
#ifdef HAVE_PRINTF_PTR
TRACE (TRACE_FUNCTION,
"rtag_proc (argc=%d, argv=%p, xwhere=%s,\n"
" mwhere=%s, mfile=%s, shorten=%d,\n"
" local_specified=%d, mname=%s, msg=%s)",
argc, (void *)argv, xwhere ? xwhere : "(null)",
mwhere ? mwhere : "(null)", mfile ? mfile : "(null)",
shorten, local_specified,
mname ? mname : "(null)", msg ? msg : "(null)" );
#else
TRACE (TRACE_FUNCTION,
"rtag_proc (argc=%d, argv=%lx, xwhere=%s,\n"
" mwhere=%s, mfile=%s, shorten=%d,\n"
" local_specified=%d, mname=%s, msg=%s )",
argc, (unsigned long)argv, xwhere ? xwhere : "(null)",
mwhere ? mwhere : "(null)", mfile ? mfile : "(null)",
shorten, local_specified,
mname ? mname : "(null)", msg ? msg : "(null)" );
#endif
if (is_rtag)
{
repository = xmalloc (strlen (current_parsed_root->directory)
+ strlen (argv[0])
+ (mfile == NULL ? 0 : strlen (mfile) + 1)
+ 2);
(void) sprintf (repository, "%s/%s", current_parsed_root->directory,
argv[0]);
where = xmalloc (strlen (argv[0])
+ (mfile == NULL ? 0 : strlen (mfile) + 1)
+ 1);
(void) strcpy (where, argv[0]);
if (mfile != NULL)
{
char *cp;
char *path;
if ((cp = strrchr (mfile, '/')) != NULL)
{
*cp = '\0';
(void) strcat (repository, "/");
(void) strcat (repository, mfile);
(void) strcat (where, "/");
(void) strcat (where, mfile);
mfile = cp + 1;
}
path = xmalloc (strlen (repository) + strlen (mfile) + 5);
(void) sprintf (path, "%s/%s", repository, mfile);
if (isdir (path))
{
(void) strcpy (repository, path);
(void) strcat (where, "/");
(void) strcat (where, mfile);
}
else
{
myargv[0] = argv[0];
myargv[1] = mfile;
argc = 2;
argv = myargv;
}
free (path);
}
if (CVS_CHDIR (repository) < 0)
{
error (0, errno, "cannot chdir to %s", repository);
free (repository);
free (where);
return 1;
}
if (delete_flag || attic_too || (force_tag_match && numtag))
which = W_REPOS | W_ATTIC;
else
which = W_REPOS;
}
else
{
where = NULL;
which = W_LOCAL;
repository = "";
}
if (numtag != NULL && !numtag_validated)
{
tag_check_valid (numtag, argc - 1, argv + 1, local_specified, 0,
repository, false);
numtag_validated = true;
}
mtlist = getlist ();
err = start_recursion (check_fileproc, check_filesdoneproc,
NULL, NULL, NULL,
argc - 1, argv + 1, local_specified, which, 0,
CVS_LOCK_READ, where, 1, repository);
if (err)
{
error (1, 0, "correct the above errors first!");
}
err = start_recursion
(is_rtag ? rtag_fileproc : tag_fileproc,
tag_filesdoneproc, tag_dirproc, NULL, mtlist, argc - 1, argv + 1,
local_specified, which, 0, CVS_LOCK_WRITE, where, 1,
repository);
dellist (&mtlist);
if (which & W_REPOS) free (repository);
if (where != NULL)
free (where);
return err;
}
static int
check_fileproc (void *callerdat, struct file_info *finfo)
{
const char *xdir;
Node *p;
Vers_TS *vers;
List *tlist;
struct tag_info *ti;
int addit = 1;
TRACE (TRACE_FUNCTION, "check_fileproc (%s, %s, %s)",
finfo->repository ? finfo->repository : "(null)",
finfo->fullname ? finfo->fullname : "(null)",
finfo->rcs ? (finfo->rcs->path ? finfo->rcs->path : "(null)")
: "NULL");
if (check_uptodate)
{
switch (Classify_File (finfo, NULL, NULL, NULL, 1, 0, &vers, 0))
{
case T_UPTODATE:
case T_CHECKOUT:
case T_PATCH:
case T_REMOVE_ENTRY:
break;
case T_UNKNOWN:
case T_CONFLICT:
case T_NEEDS_MERGE:
case T_MODIFIED:
case T_ADDED:
case T_REMOVED:
default:
error (0, 0, "%s is locally modified", finfo->fullname);
freevers_ts (&vers);
return 1;
}
}
else
vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
if (finfo->update_dir[0] == '\0')
xdir = ".";
else
xdir = finfo->update_dir;
if ((p = findnode (mtlist, xdir)) != NULL)
{
tlist = ((struct master_lists *) p->data)->tlist;
}
else
{
struct master_lists *ml;
tlist = getlist ();
p = getnode ();
p->key = xstrdup (xdir);
p->type = UPDATE;
ml = xmalloc (sizeof (struct master_lists));
ml->tlist = tlist;
p->data = ml;
p->delproc = masterlist_delproc;
(void) addnode (mtlist, p);
}
p = getnode ();
p->key = xstrdup (finfo->file);
p->type = UPDATE;
p->delproc = tag_delproc;
if (vers->srcfile == NULL)
{
if (!really_quiet)
error (0, 0, "nothing known about %s", finfo->file);
freevers_ts (&vers);
freenode (p);
return 1;
}
p->data = ti = xmalloc (sizeof (struct tag_info));
ti->tag = xstrdup (numtag ? numtag : vers->tag);
if (!is_rtag && numtag == NULL && date == NULL)
ti->rev = xstrdup (vers->vn_user);
else
ti->rev = RCS_getversion (vers->srcfile, numtag, date,
force_tag_match, NULL);
if (ti->rev != NULL)
{
ti->oldrev = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
if (ti->oldrev == NULL)
{
if (delete_flag)
{
addit = 0;
}
}
else if (delete_flag)
{
free (ti->rev);
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
ti->rev = xstrdup (ti->oldrev);
#else
ti->rev = NULL;
#endif
}
else if (strcmp(ti->oldrev, p->data) == 0)
addit = 0;
else if (!force_tag_move)
addit = 0;
}
else
addit = 0;
if (!addit)
{
free(p->data);
p->data = NULL;
}
freevers_ts (&vers);
(void)addnode (tlist, p);
return 0;
}
static int
check_filesdoneproc (void *callerdat, int err, const char *repos,
const char *update_dir, List *entries)
{
int n;
Node *p;
List *tlist;
struct pretag_proc_data ppd;
p = findnode (mtlist, update_dir);
if (p != NULL)
tlist = ((struct master_lists *) p->data)->tlist;
else
tlist = NULL;
if (tlist == NULL || tlist->list->next == tlist->list)
return err;
ppd.tlist = tlist;
ppd.delete_flag = delete_flag;
ppd.force_tag_move = force_tag_move;
ppd.symtag = symtag;
if ((n = Parse_Info (CVSROOTADM_TAGINFO, repos, pretag_proc, PIOPT_ALL,
&ppd)) > 0)
{
error (0, 0, "Pre-tag check failed");
err += n;
}
return err;
}
static int
pretag_proc (const char *repository, const char *filter, void *closure)
{
char *newfilter = NULL;
char *cmdline;
const char *srepos = Short_Repository (repository);
struct pretag_proc_data *ppd = closure;
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
if (!strchr (filter, '%'))
{
error (0,0,
"warning: taginfo line contains no format strings:\n"
" \"%s\"\n"
"Filling in old defaults ('%%t %%o %%p %%{sv}'), but please be aware that this\n"
"usage is deprecated.", filter);
newfilter = xmalloc (strlen (filter) + 16);
strcpy (newfilter, filter);
strcat (newfilter, " %t %o %p %{sv}");
filter = newfilter;
}
#endif
cmdline = format_cmdline (
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
false, srepos,
#endif
filter,
"t", "s", ppd->symtag,
"o", "s", ppd->delete_flag ? "del" :
ppd->force_tag_move ? "mov" : "add",
"b", "c", delete_flag
? '?' : branch_mode ? 'T' : 'N',
"c", "s", cvs_cmd_name,
#ifdef SERVER_SUPPORT
"R", "s", referrer ? referrer->original : "NONE",
#endif
"p", "s", srepos,
"r", "s", current_parsed_root->directory,
"sVv", ",", ppd->tlist,
pretag_list_to_args_proc, (void *) NULL,
(char *) NULL);
if (newfilter) free (newfilter);
if (!cmdline || !strlen (cmdline))
{
if (cmdline) free (cmdline);
error (0, 0, "pretag proc resolved to the empty string!");
return 1;
}
run_setup (cmdline);
free (cmdline);
return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL));
}
static void
masterlist_delproc (Node *p)
{
struct master_lists *ml = p->data;
dellist (&ml->tlist);
free (ml);
return;
}
static void
tag_delproc (Node *p)
{
struct tag_info *ti;
if (p->data)
{
ti = (struct tag_info *) p->data;
if (ti->oldrev) free (ti->oldrev);
if (ti->rev) free (ti->rev);
free (ti->tag);
free (p->data);
p->data = NULL;
}
return;
}
static int
pretag_list_to_args_proc (Node *p, void *closure)
{
struct tag_info *taginfo = (struct tag_info *)p->data;
struct format_cmdline_walklist_closure *c =
(struct format_cmdline_walklist_closure *)closure;
char *arg = NULL;
const char *f;
char *d;
size_t doff;
if (!p->data) return 1;
f = c->format;
d = *c->d;
while (*f)
{
switch (*f++)
{
case 's':
arg = p->key;
break;
case 'T':
arg = taginfo->tag ? taginfo->tag : "";
break;
case 'v':
arg = taginfo->rev ? taginfo->rev : "NONE";
break;
case 'V':
arg = taginfo->oldrev ? taginfo->oldrev : "NONE";
break;
default:
error(1,0,
"Unknown format character or not a list attribute: %c",
f[-1]);
break;
}
if (c->quotes)
{
arg = cmdlineescape (c->quotes, arg);
}
else
{
arg = cmdlinequote ('"', arg);
}
doff = d - *c->buf;
expand_string (c->buf, c->length, doff + strlen (arg));
d = *c->buf + doff;
strncpy (d, arg, strlen (arg));
d += strlen (arg);
free (arg);
doff = d - *c->buf;
expand_string (c->buf, c->length, doff + 1);
d = *c->buf + doff;
*d++ = ' ';
}
*c->d = d;
return 0;
}
static int
rtag_fileproc (void *callerdat, struct file_info *finfo)
{
RCSNode *rcsfile;
char *version = NULL, *rev = NULL;
int retcode = 0;
int retval = 0;
static bool valtagged = false;
if ((rcsfile = finfo->rcs) == NULL)
{
retval = 1;
goto free_vars_and_return;
}
if (delete_flag)
{
retval = rtag_delete (rcsfile);
goto free_vars_and_return;
}
if (attic_too && (!numtag && !date))
{
if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
{
retval = rtag_delete (rcsfile);
goto free_vars_and_return;
}
}
version = RCS_getversion (rcsfile, numtag, date, force_tag_match, NULL);
if (version == NULL)
{
if (attic_too)
(void)rtag_delete (rcsfile);
if (!quiet && !force_tag_match)
{
error (0, 0, "cannot find tag `%s' in `%s'",
numtag ? numtag : "head", rcsfile->path);
retval = 1;
}
goto free_vars_and_return;
}
if (numtag
&& isdigit ((unsigned char)*numtag)
&& strcmp (numtag, version) != 0)
{
rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag;
retcode = RCS_settag(rcsfile, symtag, numtag);
if (retcode == 0)
RCS_rewrite (rcsfile, NULL, NULL);
}
else
{
char *oversion;
rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
oversion = RCS_getversion (rcsfile, symtag, NULL, 1, NULL);
if (oversion != NULL)
{
int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
{
free (oversion);
goto free_vars_and_return;
}
if (!force_tag_move)
{
(void)printf ("W %s", finfo->fullname);
(void)printf (" : %s already exists on %s %s",
symtag, isbranch ? "branch" : "version",
oversion);
(void)printf (" : NOT MOVING tag to %s %s\n",
branch_mode ? "branch" : "version", rev);
free (oversion);
goto free_vars_and_return;
}
else
if ((isbranch && !disturb_branch_tags) ||
(!isbranch && disturb_branch_tags))
{
error(0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
finfo->fullname,
isbranch ? "branch" : "non-branch",
symtag, oversion, rev,
isbranch ? "" : " due to `-B' option");
free (oversion);
goto free_vars_and_return;
}
free (oversion);
}
retcode = RCS_settag (rcsfile, symtag, rev);
if (retcode == 0)
RCS_rewrite (rcsfile, NULL, NULL);
}
if (retcode != 0)
{
error (1, retcode == -1 ? errno : 0,
"failed to set tag `%s' to revision `%s' in `%s'",
symtag, rev, rcsfile->path);
retval = 1;
goto free_vars_and_return;
}
free_vars_and_return:
if (branch_mode && rev) free (rev);
if (version) free (version);
if (!delete_flag && !retval && !valtagged)
{
tag_check_valid (symtag, 0, NULL, 0, 0, NULL, true);
valtagged = true;
}
return retval;
}
static int
rtag_delete (RCSNode *rcsfile)
{
char *version;
int retcode, isbranch;
if (numtag)
{
version = RCS_getversion (rcsfile, numtag, NULL, 1, NULL);
if (version == NULL)
return (0);
free (version);
}
version = RCS_getversion (rcsfile, symtag, NULL, 1, NULL);
if (version == NULL)
return 0;
free (version);
isbranch = RCS_nodeisbranch (rcsfile, symtag);
if ((isbranch && !disturb_branch_tags) ||
(!isbranch && disturb_branch_tags))
{
if (!quiet)
error (0, 0,
"Not removing %s tag `%s' from `%s'%s.",
isbranch ? "branch" : "non-branch",
symtag, rcsfile->path,
isbranch ? "" : " due to `-B' option");
return 1;
}
if ((retcode = RCS_deltag(rcsfile, symtag)) != 0)
{
if (!quiet)
error (0, retcode == -1 ? errno : 0,
"failed to remove tag `%s' from `%s'", symtag,
rcsfile->path);
return 1;
}
RCS_rewrite (rcsfile, NULL, NULL);
return 0;
}
static int
tag_fileproc (void *callerdat, struct file_info *finfo)
{
char *version, *oversion;
char *nversion = NULL;
char *rev;
Vers_TS *vers;
int retcode = 0;
int retval = 0;
static bool valtagged = false;
vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
if (numtag || date)
{
nversion = RCS_getversion (vers->srcfile, numtag, date,
force_tag_match, NULL);
if (!nversion)
goto free_vars_and_return;
}
if (delete_flag)
{
int isbranch;
version = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
if (version == NULL || vers->srcfile == NULL)
goto free_vars_and_return;
free (version);
isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
if ((isbranch && !disturb_branch_tags) ||
(!isbranch && disturb_branch_tags))
{
if (!quiet)
error(0, 0,
"Not removing %s tag `%s' from `%s'%s.",
isbranch ? "branch" : "non-branch",
symtag, vers->srcfile->path,
isbranch ? "" : " due to `-B' option");
retval = 1;
goto free_vars_and_return;
}
if ((retcode = RCS_deltag (vers->srcfile, symtag)) != 0)
{
if (!quiet)
error (0, retcode == -1 ? errno : 0,
"failed to remove tag %s from %s", symtag,
vers->srcfile->path);
retval = 1;
goto free_vars_and_return;
}
RCS_rewrite (vers->srcfile, NULL, NULL);
if (!really_quiet)
{
cvs_output ("D ", 2);
cvs_output (finfo->fullname, 0);
cvs_output ("\n", 1);
}
goto free_vars_and_return;
}
if (!nversion)
version = vers->vn_user;
else
version = nversion;
if (!version)
goto free_vars_and_return;
else if (strcmp (version, "0") == 0)
{
if (!quiet)
error (0, 0, "couldn't tag added but un-commited file `%s'",
finfo->file);
goto free_vars_and_return;
}
else if (version[0] == '-')
{
if (!quiet)
error (0, 0, "skipping removed but un-commited file `%s'",
finfo->file);
goto free_vars_and_return;
}
else if (vers->srcfile == NULL)
{
if (!quiet)
error (0, 0, "cannot find revision control file for `%s'",
finfo->file);
goto free_vars_and_return;
}
rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
oversion = RCS_getversion (vers->srcfile, symtag, NULL, 1, NULL);
if (oversion != NULL)
{
int isbranch = RCS_nodeisbranch (finfo->rcs, symtag);
if (strcmp (version, oversion) == 0 && !branch_mode && !isbranch)
{
free (oversion);
if (branch_mode)
free (rev);
goto free_vars_and_return;
}
if (!force_tag_move)
{
cvs_output ("W ", 2);
cvs_output (finfo->fullname, 0);
cvs_output (" : ", 0);
cvs_output (symtag, 0);
cvs_output (" already exists on ", 0);
cvs_output (isbranch ? "branch" : "version", 0);
cvs_output (" ", 0);
cvs_output (oversion, 0);
cvs_output (" : NOT MOVING tag to ", 0);
cvs_output (branch_mode ? "branch" : "version", 0);
cvs_output (" ", 0);
cvs_output (rev, 0);
cvs_output ("\n", 1);
free (oversion);
if (branch_mode)
free (rev);
goto free_vars_and_return;
}
else
if ((isbranch && !disturb_branch_tags) ||
(!isbranch && disturb_branch_tags))
{
error (0,0, "%s: Not moving %s tag `%s' from %s to %s%s.",
finfo->fullname,
isbranch ? "branch" : "non-branch",
symtag, oversion, rev,
isbranch ? "" : " due to `-B' option");
free (oversion);
if (branch_mode)
free (rev);
goto free_vars_and_return;
}
free (oversion);
}
if ((retcode = RCS_settag(vers->srcfile, symtag, rev)) != 0)
{
error (1, retcode == -1 ? errno : 0,
"failed to set tag %s to revision %s in %s",
symtag, rev, vers->srcfile->path);
if (branch_mode)
free (rev);
retval = 1;
goto free_vars_and_return;
}
if (branch_mode)
free (rev);
RCS_rewrite (vers->srcfile, NULL, NULL);
if (!really_quiet)
{
cvs_output ("T ", 2);
cvs_output (finfo->fullname, 0);
cvs_output ("\n", 1);
}
free_vars_and_return:
if (nversion != NULL)
free (nversion);
freevers_ts (&vers);
if (!delete_flag && !retval && !valtagged)
{
tag_check_valid (symtag, 0, NULL, 0, 0, NULL, true);
valtagged = true;
}
return retval;
}
static Dtype
tag_dirproc (void *callerdat, const char *dir, const char *repos,
const char *update_dir, List *entries)
{
if (ignore_directory (update_dir))
{
if (!quiet)
error (0, 0, "Ignoring %s", update_dir);
return R_SKIP_ALL;
}
if (!quiet)
error (0, 0, "%s %s", delete_flag ? "Untagging" : "Tagging",
update_dir);
return R_PROCESS;
}
struct val_args {
const char *name;
int found;
};
static int
val_fileproc (void *callerdat, struct file_info *finfo)
{
RCSNode *rcsdata;
struct val_args *args = callerdat;
char *tag;
if ((rcsdata = finfo->rcs) == NULL)
return 0;
tag = RCS_gettag (rcsdata, args->name, 1, NULL);
if (tag != NULL)
{
args->found = 1;
free (tag);
}
return 0;
}
static int is_in_val_tags (DBM **idb, const char *name)
{
DBM *db = NULL;
char *valtags_filename;
datum mytag;
int status;
mytag.dptr = (char *)name;
mytag.dsize = strlen (name);
valtags_filename = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
CVSROOTADM, CVSROOTADM_VALTAGS);
if (idb)
{
mode_t omask;
omask = umask (cvsumask);
db = dbm_open (valtags_filename, O_RDWR | O_CREAT, 0666);
umask (omask);
if (!db)
{
error (0, errno, "warning: cannot open `%s' read/write",
valtags_filename);
*idb = NULL;
return 1;
}
*idb = db;
}
else
{
db = dbm_open (valtags_filename, O_RDONLY, 0444);
if (!db && !existence_error (errno))
error (1, errno, "cannot read %s", valtags_filename);
}
status = 0;
if (db)
{
datum val;
val = dbm_fetch (db, mytag);
if (val.dptr != NULL)
status = 1;
if (!idb) dbm_close (db);
}
free (valtags_filename);
return status;
}
static void add_to_val_tags (const char *name)
{
DBM *db;
datum mytag;
datum value;
if (noexec) return;
val_tags_lock (current_parsed_root->directory);
if (is_in_val_tags (&db, name)) return;
mytag.dptr = (char *)name;
mytag.dsize = strlen (name);
value.dptr = "y";
value.dsize = 1;
if (dbm_store (db, mytag, value, DBM_REPLACE) < 0)
error (0, errno, "failed to store %s into val-tags", name);
dbm_close (db);
clear_val_tags_lock ();
}
static Dtype
val_direntproc (void *callerdat, const char *dir, const char *repository,
const char *update_dir, List *entries)
{
if (isdir (dir))
return R_PROCESS;
return R_SKIP_ALL;
}
void
tag_check_valid (const char *name, int argc, char **argv, int local, int aflag,
char *repository, bool valid)
{
struct val_args the_val_args;
struct saved_cwd cwd;
int which;
#ifdef HAVE_PRINTF_PTR
TRACE (TRACE_FUNCTION,
"tag_check_valid (name=%s, argc=%d, argv=%p, local=%d,\n"
" aflag=%d, repository=%s, valid=%s)",
name ? name : "(name)", argc, (void *)argv, local, aflag,
repository ? repository : "(null)",
valid ? "true" : "false");
#else
TRACE (TRACE_FUNCTION,
"tag_check_valid (name=%s, argc=%d, argv=%lx, local=%d,\n"
" aflag=%d, repository=%s, valid=%s)",
name ? name : "(name)", argc, (unsigned long)argv, local, aflag,
repository ? repository : "(null)",
valid ? "true" : "false");
#endif
if (isdigit ((unsigned char) name[0]))
{
assert (!valid);
if (RCS_valid_rev (name)) return;
else
error (1, 0, "\
Numeric tag %s invalid. Numeric tags should be of the form X[.X]...", name);
}
if (strcmp (name, TAG_BASE) == 0
|| strcmp (name, TAG_HEAD) == 0)
{
assert (!valid);
return;
}
RCS_check_tag (name);
if (is_in_val_tags (NULL, name)) return;
if (!valid)
{
the_val_args.name = name;
the_val_args.found = 0;
which = W_REPOS | W_ATTIC;
if (repository == NULL || repository[0] == '\0')
which |= W_LOCAL;
else
{
if (save_cwd (&cwd))
error (1, errno, "Failed to save current directory.");
if (CVS_CHDIR (repository) < 0)
error (1, errno, "cannot change to %s directory", repository);
}
start_recursion
(val_fileproc, NULL, val_direntproc, NULL,
&the_val_args, argc, argv, local, which, aflag,
CVS_LOCK_READ, NULL, 1, repository);
if (repository != NULL && repository[0] != '\0')
{
if (restore_cwd (&cwd))
error (1, errno, "Failed to restore current directory, `%s'.",
cwd.name);
free_cwd (&cwd);
}
if (!the_val_args.found)
error (1, 0, "no such tag `%s'", name);
}
add_to_val_tags (name);
}