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