xref: /openbsd-src/usr.bin/cvs/tag.c (revision 66ad965f4873a0970dea06fb53c307b8385e5a94)
1 /*	$OpenBSD: tag.c,v 1.73 2008/03/18 13:00:12 reyk 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 <errno.h>
19 #include <string.h>
20 #include <unistd.h>
21 
22 #include "cvs.h"
23 #include "remote.h"
24 
25 #define T_CHECK_UPTODATE	0x01
26 #define T_DELETE		0x02
27 #define T_FORCE_MOVE		0x04
28 #define T_BRANCH		0x08
29 
30 void	cvs_tag_local(struct cvs_file *);
31 
32 static int tag_del(struct cvs_file *);
33 static int tag_add(struct cvs_file *);
34 
35 static int	 runflags = 0;
36 static char	*tag = NULL;
37 static char	*tag_date = NULL;
38 static char	*tag_name = NULL;
39 static char	*tag_oldname = NULL;
40 
41 struct cvs_cmd cvs_cmd_rtag = {
42 	CVS_OP_RTAG, CVS_LOCK_REPO, "rtag",
43 	{ "rt", "rfreeze" },
44 	"Add a symbolic tag to a module",
45 	"[-bcdFflR] [-D date | -r rev] symbolic_tag module ...",
46 	"bcD:dFflRr:",
47 	NULL,
48 	cvs_tag
49 };
50 
51 struct cvs_cmd cvs_cmd_tag = {
52 	CVS_OP_TAG, CVS_USE_WDIR | CVS_LOCK_REPO, "tag",
53 	{ "ta", "freeze" },
54 	"Add a symbolic tag to checked out version of files",
55 	"[-bcdFflR] [-D date | -r rev] symbolic_tag [file ...]",
56 	"bcD:dFflRr:",
57 	NULL,
58 	cvs_tag
59 };
60 
61 int
62 cvs_tag(int argc, char **argv)
63 {
64 	int ch, flags, i;
65 	char *arg = ".";
66 	struct cvs_recursion cr;
67 
68 	flags = CR_RECURSE_DIRS;
69 
70 	while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_TAG ?
71 	    cvs_cmd_tag.cmd_opts : cvs_cmd_rtag.cmd_opts)) != -1) {
72 		switch (ch) {
73 		case 'b':
74 			runflags |= T_BRANCH;
75 			break;
76 		case 'c':
77 			runflags |= T_CHECK_UPTODATE;
78 			break;
79 		case 'D':
80 			tag_date = optarg;
81 			break;
82 		case 'd':
83 			runflags |= T_DELETE;
84 			break;
85 		case 'F':
86 			runflags |= T_FORCE_MOVE;
87 			break;
88 		case 'l':
89 			flags &= ~CR_RECURSE_DIRS;
90 			break;
91 		case 'R':
92 			flags |= CR_RECURSE_DIRS;
93 			break;
94 		case 'r':
95 			tag_oldname = optarg;
96 			break;
97 		default:
98 			fatal("%s", cvs_cmdop == CVS_OP_TAG ?
99 			    cvs_cmd_tag.cmd_synopsis :
100 			    cvs_cmd_rtag.cmd_synopsis);
101 		}
102 	}
103 
104 	argc -= optind;
105 	argv += optind;
106 
107 	if (cvs_cmdop == CVS_OP_RTAG) {
108 		flags |= CR_REPO;
109 
110 		if (argc < 2)
111 			fatal("%s", cvs_cmd_rtag.cmd_synopsis);
112 
113 		for (i = 1; i < argc; i++) {
114 			if (argv[i][0] == '/')
115 				fatal("Absolute path name is invalid: %s",
116 				    argv[i]);
117 		}
118         } else if (cvs_cmdop == CVS_OP_TAG && argc == 0)
119 		fatal("%s", cvs_cmd_tag.cmd_synopsis);
120 
121 	tag_name = argv[0];
122 	argc--;
123 	argv++;
124 
125 	if (!rcs_sym_check(tag_name))
126 		fatal("tag `%s' must not contain the characters `%s'",
127 		    tag_name, RCS_SYM_INVALCHAR);
128 
129 	if (tag_oldname != NULL) {
130 		if (runflags & T_DELETE)
131 			tag_oldname = NULL;
132 		else
133 			tag = tag_oldname;
134 	}
135 
136 	if (tag_date != NULL) {
137 		if (runflags & T_DELETE)
138 			tag_date = NULL;
139 		else
140 			tag = tag_date;
141 	}
142 
143 	if (tag_oldname != NULL && tag_date != NULL)
144 		fatal("-r and -D options are mutually exclusive");
145 
146 	cr.enterdir = NULL;
147 	cr.leavedir = NULL;
148 
149 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
150 		cvs_client_connect_to_server();
151 		cr.fileproc = cvs_client_sendfile;
152 
153 		if (runflags & T_BRANCH)
154 			cvs_client_send_request("Argument -b");
155 
156 		if (runflags & T_CHECK_UPTODATE)
157 			cvs_client_send_request("Argument -c");
158 
159 		if (runflags & T_DELETE)
160 			cvs_client_send_request("Argument -d");
161 
162 		if (runflags & T_FORCE_MOVE)
163 			cvs_client_send_request("Argument -F");
164 
165 		if (!(flags & CR_RECURSE_DIRS))
166 			cvs_client_send_request("Argument -l");
167 
168 		if (tag_oldname != NULL)
169 			cvs_client_send_request("Argument -r%s", tag_oldname);
170 
171 		cvs_client_send_request("Argument %s", tag_name);
172 	} else {
173 		if (cvs_cmdop == CVS_OP_RTAG &&
174 		    chdir(current_cvsroot->cr_dir) == -1)
175 			fatal("cvs_tag: %s", strerror(errno));
176 
177 		cr.fileproc = cvs_tag_local;
178 	}
179 
180 	cr.flags = flags;
181 
182 	if (cvs_cmdop == CVS_OP_TAG ||
183 	    current_cvsroot->cr_method == CVS_METHOD_LOCAL) {
184 		if (argc > 0)
185 			cvs_file_run(argc, argv, &cr);
186 		else
187 			cvs_file_run(1, &arg, &cr);
188 	}
189 
190 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
191 		cvs_client_send_files(argv, argc);
192 		cvs_client_senddir(".");
193 
194 		cvs_client_send_request((cvs_cmdop == CVS_OP_RTAG) ?
195 		    "rtag" : "tag");
196 
197 		cvs_client_get_responses();
198 	}
199 
200 	return (0);
201 }
202 
203 void
204 cvs_tag_local(struct cvs_file *cf)
205 {
206 	cvs_log(LP_TRACE, "cvs_tag_local(%s)", cf->file_path);
207 
208 	cvs_file_classify(cf, tag);
209 
210 	if (cf->file_type == CVS_DIR) {
211 		if (verbosity > 1) {
212 			cvs_log(LP_NOTICE, "%s %s",
213 			    (runflags & T_DELETE) ? "Untagging" : "Tagging",
214 			    cf->file_path);
215 		}
216 		return;
217 	}
218 
219 	if (runflags & T_CHECK_UPTODATE) {
220 		if (cf->file_status != FILE_UPTODATE &&
221 		    cf->file_status != FILE_CHECKOUT &&
222 		    cf->file_status != FILE_PATCH) {
223 			cvs_log(LP_NOTICE,
224 			    "%s is locally modified", cf->file_path);
225 			return;
226 		}
227 	}
228 
229 	if (runflags & T_DELETE) {
230 		if (tag_del(cf) == 0) {
231 			if (verbosity > 0)
232 				cvs_printf("D %s\n", cf->file_path);
233 
234 			rcs_write(cf->file_rcs);
235 		}
236 		return;
237 	}
238 
239 	switch (cf->file_status) {
240 	case FILE_ADDED:
241 		if (verbosity > 1) {
242 			cvs_log(LP_NOTICE,
243 			    "couldn't tag added but un-commited file `%s'",
244 			    cf->file_path);
245 		}
246 		return;
247 	case FILE_REMOVED:
248 		if (verbosity > 1) {
249 			cvs_log(LP_NOTICE,
250 			    "skipping removed but un-commited file `%s'",
251 			    cf->file_path);
252 		}
253 		return;
254 	case FILE_CHECKOUT:
255 	case FILE_MODIFIED:
256 	case FILE_PATCH:
257 	case FILE_UPTODATE:
258 		if (tag_add(cf) == 0) {
259 			if (verbosity > 0)
260 				cvs_printf("T %s\n", cf->file_path);
261 
262 			rcs_write(cf->file_rcs);
263 			cvs_history_add(CVS_HISTORY_TAG, cf, tag_name);
264 		}
265 		break;
266 	default:
267 		break;
268 	}
269 }
270 
271 static int
272 tag_del(struct cvs_file *cf)
273 {
274 	if (cf->file_rcs == NULL)
275 		return (-1);
276 
277 	if (cvs_noexec == 1)
278 		return (0);
279 
280 	return (rcs_sym_remove(cf->file_rcs, tag_name));
281 }
282 
283 static int
284 tag_add(struct cvs_file *cf)
285 {
286 	int ret;
287 	char revbuf[CVS_REV_BUFSZ], trevbuf[CVS_REV_BUFSZ];
288 	RCSNUM *srev, *trev;
289 	struct rcs_sym *sym;
290 
291 	if (cf->file_rcs == NULL) {
292 		if (verbosity > 1)
293 			cvs_log(LP_NOTICE, "cannot find revision "
294 			    "control file for `%s'", cf->file_name);
295 		return (-1);
296 	}
297 
298 	if (cvs_cmdop == CVS_OP_TAG) {
299 		if (cf->file_ent == NULL)
300 			return (-1);
301 		srev = cf->file_ent->ce_rev;
302 	} else
303 		srev = cf->file_rcsrev;
304 
305 	if (cvs_noexec == 1)
306 		return (0);
307 
308 	(void)rcsnum_tostr(srev, revbuf, sizeof(revbuf));
309 
310 	trev = rcs_sym_getrev(cf->file_rcs, tag_name);
311 	if (trev != NULL) {
312 		if (rcsnum_cmp(srev, trev, 0) == 0) {
313 			rcsnum_free(trev);
314 			return (-1);
315 		}
316 		(void)rcsnum_tostr(trev, trevbuf, sizeof(trevbuf));
317 		rcsnum_free(trev);
318 
319 		if (!(runflags & T_FORCE_MOVE)) {
320 			cvs_printf("W %s : %s ", cf->file_path, tag_name);
321 			cvs_printf("already exists on version %s", trevbuf);
322 			cvs_printf(" : NOT MOVING tag to version %s\n", revbuf);
323 
324 			return (-1);
325 		} else {
326 			sym = rcs_sym_get(cf->file_rcs, tag_name);
327 			rcsnum_cpy(srev, sym->rs_num, 0);
328 			cf->file_rcs->rf_flags &= ~RCS_SYNCED;
329 
330 			return (0);
331 		}
332 	}
333 
334 	if (runflags & T_BRANCH) {
335 		if ((trev = rcsnum_new_branch(srev)) == NULL)
336 			fatal("Cannot create a new branch");
337 
338 		for (;;) {
339 			TAILQ_FOREACH(sym, &(cf->file_rcs->rf_symbols), rs_list)
340 				if (!rcsnum_cmp(sym->rs_num, trev, 0))
341 					break;
342 
343 			if (sym != NULL) {
344 				if (rcsnum_inc(trev) == NULL)
345 					fatal("New revision too high");
346 				if (rcsnum_inc(trev) == NULL)
347 					fatal("New revision too high");
348 			} else
349 				break;
350 		}
351 	} else {
352 		trev = rcsnum_alloc();
353 		rcsnum_cpy(srev, trev, 0);
354 	}
355 
356 	if ((ret = rcs_sym_add(cf->file_rcs, tag_name, trev)) != 0) {
357 		if (ret != 1) {
358 			cvs_log(LP_NOTICE,
359 			    "failed to set tag %s to revision %s in %s",
360 			    tag_name, revbuf, cf->file_rcs->rf_path);
361 		}
362 		rcsnum_free(trev);
363 		return (-1);
364 	}
365 
366 	rcsnum_free(trev);
367 	return (0);
368 }
369