xref: /openbsd-src/usr.bin/cvs/tag.c (revision 94fd4554194a14f126fba33b837cc68a1df42468)
1 /*	$OpenBSD: tag.c,v 1.55 2007/02/22 06:42:09 otto Exp $	*/
2 /*
3  * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <unistd.h>
19 
20 #include "cvs.h"
21 #include "remote.h"
22 
23 #define T_CHECK_UPTODATE	0x01
24 #define T_DELETE		0x02
25 #define T_FORCE_MOVE		0x04
26 
27 void	cvs_tag_local(struct cvs_file *);
28 
29 static int tag_del(struct cvs_file *);
30 static int tag_add(struct cvs_file *);
31 
32 static int	 runflags = 0;
33 static char	*tag = NULL;
34 static char	*tag_date = NULL;
35 static char	*tag_name = NULL;
36 static char	*tag_oldname = NULL;
37 
38 struct cvs_cmd cvs_cmd_tag = {
39 	CVS_OP_TAG, 0, "tag",
40 	{ "ta", "freeze" },
41 	"Add a symbolic tag to checked out version of files",
42 	"[-bcdFflR] [-D date | -r rev] tag [file ...]",
43 	"bcD:dFflRr:",
44 	NULL,
45 	cvs_tag
46 };
47 
48 int
49 cvs_tag(int argc, char **argv)
50 {
51 	int ch, flags;
52 	char *arg = ".";
53 	struct cvs_recursion cr;
54 
55 	flags = CR_RECURSE_DIRS;
56 
57 	while ((ch = getopt(argc, argv, cvs_cmd_tag.cmd_opts)) != -1) {
58 		switch (ch) {
59 		case 'c':
60 			runflags |= T_CHECK_UPTODATE;
61 			break;
62 		case 'D':
63 			tag_date = optarg;
64 			break;
65 		case 'd':
66 			runflags |= T_DELETE;
67 			break;
68 		case 'F':
69 			runflags |= T_FORCE_MOVE;
70 			break;
71 		case 'l':
72 			flags &= ~CR_RECURSE_DIRS;
73 			break;
74 		case 'R':
75 			break;
76 		case 'r':
77 			tag_oldname = optarg;
78 			break;
79 		default:
80 			fatal("%s", cvs_cmd_tag.cmd_synopsis);
81 		}
82 	}
83 
84 	argc -= optind;
85 	argv += optind;
86 
87 	if (argc == 0)
88 		fatal("%s", cvs_cmd_tag.cmd_synopsis);
89 
90 	tag_name = argv[0];
91 	argc--;
92 	argv++;
93 
94 	if (!rcs_sym_check(tag_name))
95 		fatal("tag `%s' must not contain the characters `%s'",
96 		    tag_name, RCS_SYM_INVALCHAR);
97 
98 	if (tag_oldname != NULL) {
99 		if (runflags & T_DELETE)
100 			tag_oldname = NULL;
101 		else
102 			tag = tag_oldname;
103 	}
104 
105 	if (tag_date != NULL) {
106 		if (runflags & T_DELETE)
107 			tag_date = NULL;
108 		else
109 			tag = tag_date;
110 	}
111 
112 	if (tag_oldname != NULL && tag_date != NULL)
113 		fatal("-r and -D options are mutually exclusive");
114 
115 	cr.enterdir = NULL;
116 	cr.leavedir = NULL;
117 
118 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
119 		cvs_client_connect_to_server();
120 		cr.fileproc = cvs_client_sendfile;
121 
122 		if (runflags & T_CHECK_UPTODATE)
123 			cvs_client_send_request("Argument -c");
124 
125 		if (runflags & T_DELETE)
126 			cvs_client_send_request("Argument -d");
127 
128 		if (runflags & T_FORCE_MOVE)
129 			cvs_client_send_request("Argument -F");
130 
131 		if (!(flags & CR_RECURSE_DIRS))
132 			cvs_client_send_request("Argument -l");
133 
134 		if (tag_oldname != NULL)
135 			cvs_client_send_request("Argument -r%s", tag_oldname);
136 
137 		cvs_client_send_request("Argument %s", tag_name);
138 	} else {
139 		cr.fileproc = cvs_tag_local;
140 	}
141 
142 	cr.flags = flags;
143 
144 	if (argc > 0)
145 		cvs_file_run(argc, argv, &cr);
146 	else
147 		cvs_file_run(1, &arg, &cr);
148 
149 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
150 		cvs_client_send_files(argv, argc);
151 		cvs_client_senddir(".");
152 		cvs_client_send_request("tag");
153 		cvs_client_get_responses();
154 	}
155 
156 	return (0);
157 }
158 
159 void
160 cvs_tag_local(struct cvs_file *cf)
161 {
162 	cvs_log(LP_TRACE, "cvs_tag_local(%s)", cf->file_path);
163 
164 	if (cf->file_type == CVS_DIR) {
165 		if (verbosity > 1) {
166 			cvs_log(LP_NOTICE, "%s %s",
167 			    (runflags & T_DELETE) ? "Untagging" : "Tagging",
168 			    cf->file_path);
169 		}
170 		return;
171 	}
172 
173 	cvs_file_classify(cf, tag);
174 
175 	if (runflags & T_CHECK_UPTODATE) {
176 		if (cf->file_status != FILE_UPTODATE &&
177 		    cf->file_status != FILE_CHECKOUT &&
178 		    cf->file_status != FILE_PATCH) {
179 			cvs_log(LP_NOTICE,
180 			    "%s is locally modified", cf->file_path);
181 			return;
182 		}
183 	}
184 
185 	if (runflags & T_DELETE) {
186 		if (tag_del(cf) == 0) {
187 			if (verbosity > 0)
188 				cvs_printf("D %s\n", cf->file_path);
189 
190 			rcs_write(cf->file_rcs);
191 		}
192 		return;
193 	}
194 
195 	switch(cf->file_status) {
196 	case FILE_ADDED:
197 		if (verbosity > 1) {
198 			cvs_log(LP_NOTICE,
199 			    "couldn't tag added but un-commited file `%s'",
200 			    cf->file_path);
201 		}
202 		return;
203 	case FILE_REMOVED:
204 		if (verbosity > 1) {
205 			cvs_log(LP_NOTICE,
206 			    "skipping removed but un-commited file `%s'",
207 			    cf->file_path);
208 		}
209 		return;
210 	case FILE_CHECKOUT:
211 	case FILE_MODIFIED:
212 	case FILE_UPTODATE:
213 		if (tag_add(cf) == 0) {
214 			if (verbosity > 0)
215 				cvs_printf("T %s\n", cf->file_path);
216 
217 			rcs_write(cf->file_rcs);
218 		}
219 		break;
220 	default:
221 		break;
222 	}
223 }
224 
225 static int
226 tag_del(struct cvs_file *cf)
227 {
228 	if (cf->file_rcs == NULL)
229 		return (-1);
230 
231 	if (cvs_noexec == 1)
232 		return (0);
233 
234 	return (rcs_sym_remove(cf->file_rcs, tag_name));
235 }
236 
237 static int
238 tag_add(struct cvs_file *cf)
239 {
240 	char revbuf[16], trevbuf[16];
241 	RCSNUM *trev;
242 	struct rcs_sym *sym;
243 
244 	if (cf->file_rcs == NULL) {
245 		if (verbosity > 1)
246 			cvs_log(LP_NOTICE, "cannot find revision "
247 			    "control file for `%s'", cf->file_name);
248 		return (-1);
249 	}
250 
251 	if (cvs_noexec == 1)
252 		return (0);
253 
254 	trev = rcs_sym_getrev(cf->file_rcs, tag_name);
255 	if (trev != NULL) {
256 		if (rcsnum_cmp(cf->file_rcsrev, trev, 0) == 0) {
257 			rcsnum_free(trev);
258 			return (-1);
259 		}
260 		(void)rcsnum_tostr(trev, trevbuf, sizeof(trevbuf));
261 
262 		if (!(runflags & T_FORCE_MOVE)) {
263 			cvs_printf("W %s : %s ", cf->file_path, tag_name);
264 			cvs_printf("already exists on version %s", trevbuf);
265 			cvs_printf(" : NOT MOVING tag to version %s\n", revbuf);
266 
267 			return (-1);
268 		} else if (runflags & T_FORCE_MOVE) {
269 			sym = rcs_sym_get(cf->file_rcs, tag_name);
270 			rcsnum_cpy(cf->file_rcsrev, sym->rs_num, 0);
271 			cf->file_rcs->rf_flags &= ~RCS_SYNCED;
272 
273 			return (0);
274 		}
275 	}
276 
277 	if (rcs_sym_add(cf->file_rcs, tag_name, cf->file_rcsrev) == -1) {
278 		if (rcs_errno != RCS_ERR_DUPENT) {
279 			(void)rcsnum_tostr(cf->file_rcsrev, revbuf,
280 			    sizeof(revbuf));
281 			cvs_log(LP_NOTICE,
282 			    "failed to set tag %s to revision %s in %s",
283 			    tag_name, revbuf, cf->file_rcs->rf_path);
284 		}
285 		return (-1);
286 	}
287 
288 	return (0);
289 }
290