xref: /openbsd-src/usr.bin/cvs/tag.c (revision 2f43a3f5488752f16be4ec77bf146703768436fa)
1*2f43a3f5Sderaadt /*	$OpenBSD: tag.c,v 1.88 2021/01/27 07:18:17 deraadt Exp $	*/
2a7e730bbSxsa /*
3a7e730bbSxsa  * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org>
4a7e730bbSxsa  *
5a7e730bbSxsa  * Permission to use, copy, modify, and distribute this software for any
6a7e730bbSxsa  * purpose with or without fee is hereby granted, provided that the above
7a7e730bbSxsa  * copyright notice and this permission notice appear in all copies.
8a7e730bbSxsa  *
9a7e730bbSxsa  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10a7e730bbSxsa  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11a7e730bbSxsa  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12a7e730bbSxsa  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13a7e730bbSxsa  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14a7e730bbSxsa  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15a7e730bbSxsa  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16a7e730bbSxsa  */
17a7e730bbSxsa 
184b15c6ebStobias #include <errno.h>
19397ddb8aSnicm #include <stdlib.h>
204b15c6ebStobias #include <string.h>
211f8531bdSotto #include <unistd.h>
22a7e730bbSxsa 
23a7e730bbSxsa #include "cvs.h"
24ddb6a81aSxsa #include "remote.h"
25a7e730bbSxsa 
26d6e0edd4Sxsa #define T_CHECK_UPTODATE	0x01
27d6e0edd4Sxsa #define T_DELETE		0x02
28d6e0edd4Sxsa #define T_FORCE_MOVE		0x04
29e8f8196dStobias #define T_BRANCH		0x08
30d6e0edd4Sxsa 
31b034d592Sjoris void	cvs_tag_check_files(struct cvs_file *);
32a7e730bbSxsa void	cvs_tag_local(struct cvs_file *);
33a7e730bbSxsa 
34a7e730bbSxsa static int tag_del(struct cvs_file *);
35a7e730bbSxsa static int tag_add(struct cvs_file *);
36a7e730bbSxsa 
37*2f43a3f5Sderaadt struct file_info_list	tag_files_info;
38b034d592Sjoris 
39d6e0edd4Sxsa static int	runflags = 0;
402097a44aSjoris static int	tag_errors = 0;
41a7e730bbSxsa static char	*tag = NULL;
42a7e730bbSxsa static char	*tag_date = NULL;
43a7e730bbSxsa static char	*tag_name = NULL;
44a7e730bbSxsa static char	*tag_oldname = NULL;
45a7e730bbSxsa 
464b15c6ebStobias struct cvs_cmd cvs_cmd_rtag = {
47fb3beb6cSjoris 	CVS_OP_RTAG, CVS_LOCK_REPO, "rtag",
484b15c6ebStobias 	{ "rt", "rfreeze" },
494b15c6ebStobias 	"Add a symbolic tag to a module",
50963c2fefSreyk 	"[-bcdFflR] [-D date | -r rev] symbolic_tag module ...",
514b15c6ebStobias 	"bcD:dFflRr:",
524b15c6ebStobias 	NULL,
534b15c6ebStobias 	cvs_tag
544b15c6ebStobias };
554b15c6ebStobias 
56a7e730bbSxsa struct cvs_cmd cvs_cmd_tag = {
57fb3beb6cSjoris 	CVS_OP_TAG, CVS_USE_WDIR | CVS_LOCK_REPO, "tag",
58a7e730bbSxsa 	{ "ta", "freeze" },
59a7e730bbSxsa 	"Add a symbolic tag to checked out version of files",
60963c2fefSreyk 	"[-bcdFflR] [-D date | -r rev] symbolic_tag [file ...]",
61a7e730bbSxsa 	"bcD:dFflRr:",
62a7e730bbSxsa 	NULL,
63a7e730bbSxsa 	cvs_tag
64a7e730bbSxsa };
65a7e730bbSxsa 
66a7e730bbSxsa int
cvs_tag(int argc,char ** argv)67a7e730bbSxsa cvs_tag(int argc, char **argv)
68a7e730bbSxsa {
694b15c6ebStobias 	int ch, flags, i;
70b9fc9a72Sderaadt 	char repo[PATH_MAX];
7161c65abbSreyk 	char *arg = ".";
72a7e730bbSxsa 	struct cvs_recursion cr;
73b034d592Sjoris 	struct trigger_list *line_list;
74a7e730bbSxsa 
75a7e730bbSxsa 	flags = CR_RECURSE_DIRS;
76a7e730bbSxsa 
77e9d83458Stobias 	while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_TAG ?
78e9d83458Stobias 	    cvs_cmd_tag.cmd_opts : cvs_cmd_rtag.cmd_opts)) != -1) {
79a7e730bbSxsa 		switch (ch) {
80e8f8196dStobias 		case 'b':
81e8f8196dStobias 			runflags |= T_BRANCH;
82e8f8196dStobias 			break;
83d6e0edd4Sxsa 		case 'c':
84d6e0edd4Sxsa 			runflags |= T_CHECK_UPTODATE;
85d6e0edd4Sxsa 			break;
86a7e730bbSxsa 		case 'D':
87a7e730bbSxsa 			tag_date = optarg;
88a7e730bbSxsa 			break;
89a7e730bbSxsa 		case 'd':
90d6e0edd4Sxsa 			runflags |= T_DELETE;
91a7e730bbSxsa 			break;
92a0d919f1Sxsa 		case 'F':
93d6e0edd4Sxsa 			runflags |= T_FORCE_MOVE;
94a0d919f1Sxsa 			break;
95a7e730bbSxsa 		case 'l':
96a7e730bbSxsa 			flags &= ~CR_RECURSE_DIRS;
97a7e730bbSxsa 			break;
98a7e730bbSxsa 		case 'R':
99bcf22459Stobias 			flags |= CR_RECURSE_DIRS;
100a7e730bbSxsa 			break;
101a7e730bbSxsa 		case 'r':
102a7e730bbSxsa 			tag_oldname = optarg;
103a7e730bbSxsa 			break;
104a7e730bbSxsa 		default:
105aeaaf4a6Stobias 			fatal("%s", cvs_cmdop == CVS_OP_TAG ?
106aeaaf4a6Stobias 			    cvs_cmd_tag.cmd_synopsis :
107aeaaf4a6Stobias 			    cvs_cmd_rtag.cmd_synopsis);
108a7e730bbSxsa 		}
109a7e730bbSxsa 	}
110a7e730bbSxsa 
111a7e730bbSxsa 	argc -= optind;
112a7e730bbSxsa 	argv += optind;
113a7e730bbSxsa 
1144b15c6ebStobias 	if (cvs_cmdop == CVS_OP_RTAG) {
115f331ff59Stobias 		flags |= CR_REPO;
116f331ff59Stobias 
1174b15c6ebStobias 		if (argc < 2)
1184b15c6ebStobias 			fatal("%s", cvs_cmd_rtag.cmd_synopsis);
1194b15c6ebStobias 
12033a2bb40Sjoris 		for (i = 1; i < argc; i++) {
1212097a44aSjoris 			if (argv[i][0] == '/') {
1224b15c6ebStobias 				fatal("Absolute path name is invalid: %s",
1234b15c6ebStobias 				    argv[i]);
12433a2bb40Sjoris 			}
1252097a44aSjoris 		}
1262097a44aSjoris 	} else if (cvs_cmdop == CVS_OP_TAG && argc == 0) {
127a7e730bbSxsa 		fatal("%s", cvs_cmd_tag.cmd_synopsis);
1282097a44aSjoris 	}
129a7e730bbSxsa 
130a7e730bbSxsa 	tag_name = argv[0];
131a7e730bbSxsa 	argc--;
132a7e730bbSxsa 	argv++;
133a7e730bbSxsa 
1342097a44aSjoris 	if (!rcs_sym_check(tag_name)) {
135a7e730bbSxsa 		fatal("tag `%s' must not contain the characters `%s'",
136a7e730bbSxsa 		    tag_name, RCS_SYM_INVALCHAR);
1372097a44aSjoris 	}
138a7e730bbSxsa 
139a7e730bbSxsa 	if (tag_oldname != NULL) {
140d6e0edd4Sxsa 		if (runflags & T_DELETE)
141a7e730bbSxsa 			tag_oldname = NULL;
142a7e730bbSxsa 		else
143a7e730bbSxsa 			tag = tag_oldname;
144a7e730bbSxsa 	}
145a7e730bbSxsa 
146a7e730bbSxsa 	if (tag_date != NULL) {
147d6e0edd4Sxsa 		if (runflags & T_DELETE)
148a7e730bbSxsa 			tag_date = NULL;
149a7e730bbSxsa 		else
150a7e730bbSxsa 			tag = tag_date;
151a7e730bbSxsa 	}
152a7e730bbSxsa 
153a7e730bbSxsa 	if (tag_oldname != NULL && tag_date != NULL)
154a7e730bbSxsa 		fatal("-r and -D options are mutually exclusive");
155a7e730bbSxsa 
156a7e730bbSxsa 	cr.enterdir = NULL;
157a7e730bbSxsa 	cr.leavedir = NULL;
158ddb6a81aSxsa 
1594dcde513Sjoris 	if (cvsroot_is_remote()) {
16080f6ca9bSjoris 		cvs_client_connect_to_server();
161ddb6a81aSxsa 		cr.fileproc = cvs_client_sendfile;
162ddb6a81aSxsa 
1632097a44aSjoris 		if (argc > 0)
1642097a44aSjoris 			cvs_file_run(argc, argv, &cr);
1652097a44aSjoris 		else
1662097a44aSjoris 			cvs_file_run(1, &arg, &cr);
1672097a44aSjoris 
168e8f8196dStobias 		if (runflags & T_BRANCH)
169e8f8196dStobias 			cvs_client_send_request("Argument -b");
170e8f8196dStobias 
171d6e0edd4Sxsa 		if (runflags & T_CHECK_UPTODATE)
172d6e0edd4Sxsa 			cvs_client_send_request("Argument -c");
173d6e0edd4Sxsa 
174d6e0edd4Sxsa 		if (runflags & T_DELETE)
175ddb6a81aSxsa 			cvs_client_send_request("Argument -d");
176ddb6a81aSxsa 
177d6e0edd4Sxsa 		if (runflags & T_FORCE_MOVE)
178ddb6a81aSxsa 			cvs_client_send_request("Argument -F");
179ddb6a81aSxsa 
180ddb6a81aSxsa 		if (!(flags & CR_RECURSE_DIRS))
181ddb6a81aSxsa 			cvs_client_send_request("Argument -l");
182ddb6a81aSxsa 
183909bf3e0Stobias 		if (tag_date != NULL)
184909bf3e0Stobias 			cvs_client_send_request("Argument -D%s", tag_date);
185909bf3e0Stobias 
186ddb6a81aSxsa 		if (tag_oldname != NULL)
187ddb6a81aSxsa 			cvs_client_send_request("Argument -r%s", tag_oldname);
188ddb6a81aSxsa 
189ddb6a81aSxsa 		cvs_client_send_request("Argument %s", tag_name);
1902097a44aSjoris 		cvs_client_send_files(argv, argc);
1912097a44aSjoris 		cvs_client_senddir(".");
1922097a44aSjoris 		cvs_client_send_request((cvs_cmdop == CVS_OP_RTAG) ?
1932097a44aSjoris 		    "rtag" : "tag");
1942097a44aSjoris 		cvs_client_get_responses();
1952097a44aSjoris 
1962097a44aSjoris 		return (0);
197ddb6a81aSxsa 	}
198ddb6a81aSxsa 
1992097a44aSjoris 	if (cvs_cmdop == CVS_OP_RTAG && chdir(current_cvsroot->cr_dir) == -1)
2002097a44aSjoris 		fatal("cvs_tag: %s", strerror(errno));
201a7e730bbSxsa 
202*2f43a3f5Sderaadt 	TAILQ_INIT(&tag_files_info);
203b9fc9a72Sderaadt 	cvs_get_repository_name(".", repo, PATH_MAX);
204b034d592Sjoris 	line_list = cvs_trigger_getlines(CVS_PATH_TAGINFO, repo);
2052097a44aSjoris 
2062097a44aSjoris 	cr.flags = flags;
207b034d592Sjoris 	cr.fileproc = cvs_tag_check_files;
2082097a44aSjoris 
209b034d592Sjoris 	if (argc > 0)
210b034d592Sjoris 		cvs_file_run(argc, argv, &cr);
211b034d592Sjoris 	else
212b034d592Sjoris 		cvs_file_run(1, &arg, &cr);
213b034d592Sjoris 
2142097a44aSjoris 	if (tag_errors)
2152097a44aSjoris 		fatal("correct the above errors first!");
2162097a44aSjoris 
2172097a44aSjoris 	if (line_list != NULL) {
218b034d592Sjoris 		if (cvs_trigger_handle(CVS_TRIGGER_TAGINFO, repo, NULL,
219*2f43a3f5Sderaadt 		    line_list, &tag_files_info))
2202097a44aSjoris 			fatal("Pre-tag check failed");
221b034d592Sjoris 		cvs_trigger_freelist(line_list);
222b034d592Sjoris 	}
223b034d592Sjoris 
224b034d592Sjoris 	cr.fileproc = cvs_tag_local;
225b034d592Sjoris 
22661c65abbSreyk 	if (argc > 0)
227a7e730bbSxsa 		cvs_file_run(argc, argv, &cr);
22861c65abbSreyk 	else
22961c65abbSreyk 		cvs_file_run(1, &arg, &cr);
230a7e730bbSxsa 
2314cb5121aSjoris 	if (line_list != NULL)
232*2f43a3f5Sderaadt 		cvs_trigger_freeinfo(&tag_files_info);
2334cb5121aSjoris 
234a7e730bbSxsa 	return (0);
235a7e730bbSxsa }
236a7e730bbSxsa 
237a7e730bbSxsa void
cvs_tag_check_files(struct cvs_file * cf)238b034d592Sjoris cvs_tag_check_files(struct cvs_file *cf)
239b034d592Sjoris {
240b034d592Sjoris 	RCSNUM *srev = NULL, *rev = NULL;
241b034d592Sjoris 	char rbuf[CVS_REV_BUFSZ];
242b034d592Sjoris 	struct file_info *fi;
243b034d592Sjoris 
244b034d592Sjoris 	cvs_log(LP_TRACE, "cvs_tag_check_files(%s)", cf->file_path);
245b034d592Sjoris 
246b034d592Sjoris 	cvs_file_classify(cf, tag);
247b034d592Sjoris 
2482097a44aSjoris 	if (cf->file_type == CVS_DIR || cf->file_status == FILE_UNKNOWN)
249b034d592Sjoris 		return;
250b034d592Sjoris 
251b034d592Sjoris 	if (runflags & T_CHECK_UPTODATE) {
252b034d592Sjoris 		if (cf->file_status != FILE_UPTODATE &&
253b034d592Sjoris 		    cf->file_status != FILE_CHECKOUT &&
254b034d592Sjoris 		    cf->file_status != FILE_PATCH) {
2552097a44aSjoris 			tag_errors++;
2562097a44aSjoris 			cvs_log(LP_NOTICE,
2572097a44aSjoris 			    "%s is locally modified", cf->file_path);
258b034d592Sjoris 			return;
259b034d592Sjoris 		}
260b034d592Sjoris 	}
261b034d592Sjoris 
262b034d592Sjoris 	switch (cf->file_status) {
263b034d592Sjoris 	case FILE_ADDED:
264b034d592Sjoris 	case FILE_REMOVED:
265b034d592Sjoris 		return;
266b034d592Sjoris 	default:
267b034d592Sjoris 		break;
268b034d592Sjoris 	}
269b034d592Sjoris 
270b034d592Sjoris 	if (cvs_cmdop == CVS_OP_TAG) {
271b034d592Sjoris 		if (cf->file_ent == NULL)
272b034d592Sjoris 			return;
273b034d592Sjoris 		srev = cf->file_ent->ce_rev;
274b034d592Sjoris 	} else
275b034d592Sjoris 		srev = cf->file_rcsrev;
276b034d592Sjoris 
277b034d592Sjoris 	rcsnum_tostr(srev, rbuf, sizeof(rbuf));
27877f7cc85Stobias 	fi = xcalloc(1, sizeof(*fi));
27977f7cc85Stobias 	fi->nrevstr = xstrdup(rbuf);
280b034d592Sjoris 	fi->file_path = xstrdup(cf->file_path);
281b034d592Sjoris 
282b034d592Sjoris 	if (tag_oldname != NULL)
283b034d592Sjoris 		fi->tag_old = xstrdup(tag_oldname);
284b034d592Sjoris 	else if (tag_date != NULL)
285b034d592Sjoris 		fi->tag_old = xstrdup(tag_date);
286b034d592Sjoris 
287b034d592Sjoris 	if ((rev = rcs_sym_getrev(cf->file_rcs, tag_name)) != NULL) {
288b034d592Sjoris 		if (!rcsnum_differ(srev, rev))
289b034d592Sjoris 			goto bad;
290b034d592Sjoris 		rcsnum_tostr(rev, rbuf, sizeof(rbuf));
291b034d592Sjoris 		fi->crevstr = xstrdup(rbuf);
29253ce2177Sfcambus 		free(rev);
293b034d592Sjoris 	} else if (runflags & T_DELETE)
294b034d592Sjoris 		goto bad;
295b034d592Sjoris 
296b034d592Sjoris 	fi->tag_new = xstrdup(tag_name);
297b034d592Sjoris 
298b034d592Sjoris 	if (runflags & T_BRANCH)
299b034d592Sjoris 		fi->tag_type = 'T';
300b034d592Sjoris 	else if (runflags & T_DELETE)
301b034d592Sjoris 		fi->tag_type = '?';
302b034d592Sjoris 	else
303b034d592Sjoris 		fi->tag_type = 'N';
304b034d592Sjoris 
305b034d592Sjoris 	if (runflags & T_FORCE_MOVE)
306b034d592Sjoris 		fi->tag_op = "mov";
307b034d592Sjoris 	else if (runflags & T_DELETE)
308b034d592Sjoris 		fi->tag_op = "del";
309b034d592Sjoris 	else
310b034d592Sjoris 		fi->tag_op = "add";
311b034d592Sjoris 
312*2f43a3f5Sderaadt 	TAILQ_INSERT_TAIL(&tag_files_info, fi, flist);
313b034d592Sjoris 	return;
314b034d592Sjoris 
315b034d592Sjoris bad:
316397ddb8aSnicm 	free(fi->file_path);
317397ddb8aSnicm 	free(fi->crevstr);
318397ddb8aSnicm 	free(fi->nrevstr);
319397ddb8aSnicm 	free(fi->tag_new);
320397ddb8aSnicm 	free(fi->tag_old);
32153ce2177Sfcambus 	free(rev);
322397ddb8aSnicm 	free(fi);
323b034d592Sjoris }
324b034d592Sjoris 
325b034d592Sjoris void
cvs_tag_local(struct cvs_file * cf)326a7e730bbSxsa cvs_tag_local(struct cvs_file *cf)
327a7e730bbSxsa {
328a7e730bbSxsa 	cvs_log(LP_TRACE, "cvs_tag_local(%s)", cf->file_path);
329a7e730bbSxsa 
3304b15c6ebStobias 	cvs_file_classify(cf, tag);
3314b15c6ebStobias 
332a7e730bbSxsa 	if (cf->file_type == CVS_DIR) {
333a7e730bbSxsa 		if (verbosity > 1) {
334a7e730bbSxsa 			cvs_log(LP_NOTICE, "%s %s",
335d6e0edd4Sxsa 			    (runflags & T_DELETE) ? "Untagging" : "Tagging",
336a7e730bbSxsa 			    cf->file_path);
337a7e730bbSxsa 		}
338a7e730bbSxsa 		return;
339a7e730bbSxsa 	}
340a7e730bbSxsa 
341d6e0edd4Sxsa 	if (runflags & T_DELETE) {
342a7e730bbSxsa 		if (tag_del(cf) == 0) {
343a7e730bbSxsa 			if (verbosity > 0)
344a7e730bbSxsa 				cvs_printf("D %s\n", cf->file_path);
345a7e730bbSxsa 		}
346a7e730bbSxsa 		return;
347a7e730bbSxsa 	}
348a7e730bbSxsa 
349a7e730bbSxsa 	switch (cf->file_status) {
350a7e730bbSxsa 	case FILE_ADDED:
351a7e730bbSxsa 		if (verbosity > 1) {
352a7e730bbSxsa 			cvs_log(LP_NOTICE,
3536953ed44Smmcc 			    "couldn't tag added but un-committed file `%s'",
354a7e730bbSxsa 			    cf->file_path);
355a7e730bbSxsa 		}
356a3d408c4Stobias 		break;
357a7e730bbSxsa 	case FILE_REMOVED:
358a7e730bbSxsa 		if (verbosity > 1) {
359a7e730bbSxsa 			cvs_log(LP_NOTICE,
3606953ed44Smmcc 			    "skipping removed but un-committed file `%s'",
361a7e730bbSxsa 			    cf->file_path);
362a7e730bbSxsa 		}
363a3d408c4Stobias 		break;
364a7e730bbSxsa 	case FILE_CHECKOUT:
365a7e730bbSxsa 	case FILE_MODIFIED:
3664b15c6ebStobias 	case FILE_PATCH:
367a7e730bbSxsa 	case FILE_UPTODATE:
368a7e730bbSxsa 		if (tag_add(cf) == 0) {
369a7e730bbSxsa 			if (verbosity > 0)
370a7e730bbSxsa 				cvs_printf("T %s\n", cf->file_path);
3713901dfa5Sjoris 			cvs_history_add(CVS_HISTORY_TAG, cf, tag_name);
372a7e730bbSxsa 		}
373a7e730bbSxsa 		break;
374a7e730bbSxsa 	default:
375a7e730bbSxsa 		break;
376a7e730bbSxsa 	}
377a7e730bbSxsa }
378a7e730bbSxsa 
379a7e730bbSxsa static int
tag_del(struct cvs_file * cf)380a7e730bbSxsa tag_del(struct cvs_file *cf)
381a7e730bbSxsa {
382a7e730bbSxsa 	if (cf->file_rcs == NULL)
383a7e730bbSxsa 		return (-1);
384a7e730bbSxsa 
385a7e730bbSxsa 	if (cvs_noexec == 1)
386a7e730bbSxsa 		return (0);
387a7e730bbSxsa 
388a7e730bbSxsa 	return (rcs_sym_remove(cf->file_rcs, tag_name));
389a7e730bbSxsa }
390a7e730bbSxsa 
391a7e730bbSxsa static int
tag_add(struct cvs_file * cf)392a7e730bbSxsa tag_add(struct cvs_file *cf)
393a7e730bbSxsa {
3945b95d21fSjoris 	int ret;
3950a7da307Sxsa 	char revbuf[CVS_REV_BUFSZ], trevbuf[CVS_REV_BUFSZ];
3964b15c6ebStobias 	RCSNUM *srev, *trev;
397a0d919f1Sxsa 	struct rcs_sym *sym;
398a7e730bbSxsa 
399a7e730bbSxsa 	if (cf->file_rcs == NULL) {
400a7e730bbSxsa 		if (verbosity > 1)
401a7e730bbSxsa 			cvs_log(LP_NOTICE, "cannot find revision "
402a7e730bbSxsa 			    "control file for `%s'", cf->file_name);
403a7e730bbSxsa 		return (-1);
404a7e730bbSxsa 	}
405a7e730bbSxsa 
4064b15c6ebStobias 	if (cvs_cmdop == CVS_OP_TAG) {
4074b15c6ebStobias 		if (cf->file_ent == NULL)
4084b15c6ebStobias 			return (-1);
4094b15c6ebStobias 		srev = cf->file_ent->ce_rev;
4104b15c6ebStobias 	} else
4114b15c6ebStobias 		srev = cf->file_rcsrev;
4124b15c6ebStobias 
413a7e730bbSxsa 	if (cvs_noexec == 1)
414a7e730bbSxsa 		return (0);
415a7e730bbSxsa 
4164b15c6ebStobias 	(void)rcsnum_tostr(srev, revbuf, sizeof(revbuf));
417fc9a8513Stobias 
418a0d919f1Sxsa 	trev = rcs_sym_getrev(cf->file_rcs, tag_name);
419a0d919f1Sxsa 	if (trev != NULL) {
4204b15c6ebStobias 		if (rcsnum_cmp(srev, trev, 0) == 0) {
42153ce2177Sfcambus 			free(trev);
422a0d919f1Sxsa 			return (-1);
423a0d919f1Sxsa 		}
424a0d919f1Sxsa 		(void)rcsnum_tostr(trev, trevbuf, sizeof(trevbuf));
42553ce2177Sfcambus 		free(trev);
426a0d919f1Sxsa 
427d6e0edd4Sxsa 		if (!(runflags & T_FORCE_MOVE)) {
428a0d919f1Sxsa 			cvs_printf("W %s : %s ", cf->file_path, tag_name);
429a0d919f1Sxsa 			cvs_printf("already exists on version %s", trevbuf);
430a0d919f1Sxsa 			cvs_printf(" : NOT MOVING tag to version %s\n", revbuf);
431a0d919f1Sxsa 
432a0d919f1Sxsa 			return (-1);
43393548cecSjoris 		} else {
434a0d919f1Sxsa 			sym = rcs_sym_get(cf->file_rcs, tag_name);
4354b15c6ebStobias 			rcsnum_cpy(srev, sym->rs_num, 0);
436a0d919f1Sxsa 			cf->file_rcs->rf_flags &= ~RCS_SYNCED;
437a0d919f1Sxsa 
438a0d919f1Sxsa 			return (0);
439a0d919f1Sxsa 		}
440a0d919f1Sxsa 	}
441a0d919f1Sxsa 
442e8f8196dStobias 	if (runflags & T_BRANCH) {
44345b98c07Stobias 		if ((trev = rcs_branch_new(cf->file_rcs, srev)) == NULL)
444e8f8196dStobias 			fatal("Cannot create a new branch");
445e8f8196dStobias 	} else {
446e8f8196dStobias 		trev = rcsnum_alloc();
4474b15c6ebStobias 		rcsnum_cpy(srev, trev, 0);
448e8f8196dStobias 	}
449e8f8196dStobias 
4505b95d21fSjoris 	if ((ret = rcs_sym_add(cf->file_rcs, tag_name, trev)) != 0) {
4515b95d21fSjoris 		if (ret != 1) {
452a7e730bbSxsa 			cvs_log(LP_NOTICE,
453a7e730bbSxsa 			    "failed to set tag %s to revision %s in %s",
454a7e730bbSxsa 			    tag_name, revbuf, cf->file_rcs->rf_path);
455a7e730bbSxsa 		}
45653ce2177Sfcambus 		free(trev);
457a7e730bbSxsa 		return (-1);
458a7e730bbSxsa 	}
459a7e730bbSxsa 
46053ce2177Sfcambus 	free(trev);
461a7e730bbSxsa 	return (0);
462a7e730bbSxsa }
463