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