1 |
|
|
/* $OpenBSD: tag.c,v 1.87 2017/06/01 08:08:24 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 <errno.h> |
19 |
|
|
#include <stdlib.h> |
20 |
|
|
#include <string.h> |
21 |
|
|
#include <unistd.h> |
22 |
|
|
|
23 |
|
|
#include "cvs.h" |
24 |
|
|
#include "remote.h" |
25 |
|
|
|
26 |
|
|
#define T_CHECK_UPTODATE 0x01 |
27 |
|
|
#define T_DELETE 0x02 |
28 |
|
|
#define T_FORCE_MOVE 0x04 |
29 |
|
|
#define T_BRANCH 0x08 |
30 |
|
|
|
31 |
|
|
void cvs_tag_check_files(struct cvs_file *); |
32 |
|
|
void cvs_tag_local(struct cvs_file *); |
33 |
|
|
|
34 |
|
|
static int tag_del(struct cvs_file *); |
35 |
|
|
static int tag_add(struct cvs_file *); |
36 |
|
|
|
37 |
|
|
struct file_info_list files_info; |
38 |
|
|
|
39 |
|
|
static int runflags = 0; |
40 |
|
|
static int tag_errors = 0; |
41 |
|
|
static char *tag = NULL; |
42 |
|
|
static char *tag_date = NULL; |
43 |
|
|
static char *tag_name = NULL; |
44 |
|
|
static char *tag_oldname = NULL; |
45 |
|
|
|
46 |
|
|
struct cvs_cmd cvs_cmd_rtag = { |
47 |
|
|
CVS_OP_RTAG, CVS_LOCK_REPO, "rtag", |
48 |
|
|
{ "rt", "rfreeze" }, |
49 |
|
|
"Add a symbolic tag to a module", |
50 |
|
|
"[-bcdFflR] [-D date | -r rev] symbolic_tag module ...", |
51 |
|
|
"bcD:dFflRr:", |
52 |
|
|
NULL, |
53 |
|
|
cvs_tag |
54 |
|
|
}; |
55 |
|
|
|
56 |
|
|
struct cvs_cmd cvs_cmd_tag = { |
57 |
|
|
CVS_OP_TAG, CVS_USE_WDIR | CVS_LOCK_REPO, "tag", |
58 |
|
|
{ "ta", "freeze" }, |
59 |
|
|
"Add a symbolic tag to checked out version of files", |
60 |
|
|
"[-bcdFflR] [-D date | -r rev] symbolic_tag [file ...]", |
61 |
|
|
"bcD:dFflRr:", |
62 |
|
|
NULL, |
63 |
|
|
cvs_tag |
64 |
|
|
}; |
65 |
|
|
|
66 |
|
|
int |
67 |
|
|
cvs_tag(int argc, char **argv) |
68 |
|
|
{ |
69 |
|
|
int ch, flags, i; |
70 |
|
|
char repo[PATH_MAX]; |
71 |
|
|
char *arg = "."; |
72 |
|
|
struct cvs_recursion cr; |
73 |
|
|
struct trigger_list *line_list; |
74 |
|
|
|
75 |
|
|
flags = CR_RECURSE_DIRS; |
76 |
|
|
|
77 |
|
|
while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_TAG ? |
78 |
|
|
cvs_cmd_tag.cmd_opts : cvs_cmd_rtag.cmd_opts)) != -1) { |
79 |
|
|
switch (ch) { |
80 |
|
|
case 'b': |
81 |
|
|
runflags |= T_BRANCH; |
82 |
|
|
break; |
83 |
|
|
case 'c': |
84 |
|
|
runflags |= T_CHECK_UPTODATE; |
85 |
|
|
break; |
86 |
|
|
case 'D': |
87 |
|
|
tag_date = optarg; |
88 |
|
|
break; |
89 |
|
|
case 'd': |
90 |
|
|
runflags |= T_DELETE; |
91 |
|
|
break; |
92 |
|
|
case 'F': |
93 |
|
|
runflags |= T_FORCE_MOVE; |
94 |
|
|
break; |
95 |
|
|
case 'l': |
96 |
|
|
flags &= ~CR_RECURSE_DIRS; |
97 |
|
|
break; |
98 |
|
|
case 'R': |
99 |
|
|
flags |= CR_RECURSE_DIRS; |
100 |
|
|
break; |
101 |
|
|
case 'r': |
102 |
|
|
tag_oldname = optarg; |
103 |
|
|
break; |
104 |
|
|
default: |
105 |
|
|
fatal("%s", cvs_cmdop == CVS_OP_TAG ? |
106 |
|
|
cvs_cmd_tag.cmd_synopsis : |
107 |
|
|
cvs_cmd_rtag.cmd_synopsis); |
108 |
|
|
} |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
argc -= optind; |
112 |
|
|
argv += optind; |
113 |
|
|
|
114 |
|
|
if (cvs_cmdop == CVS_OP_RTAG) { |
115 |
|
|
flags |= CR_REPO; |
116 |
|
|
|
117 |
|
|
if (argc < 2) |
118 |
|
|
fatal("%s", cvs_cmd_rtag.cmd_synopsis); |
119 |
|
|
|
120 |
|
|
for (i = 1; i < argc; i++) { |
121 |
|
|
if (argv[i][0] == '/') { |
122 |
|
|
fatal("Absolute path name is invalid: %s", |
123 |
|
|
argv[i]); |
124 |
|
|
} |
125 |
|
|
} |
126 |
|
|
} else if (cvs_cmdop == CVS_OP_TAG && argc == 0) { |
127 |
|
|
fatal("%s", cvs_cmd_tag.cmd_synopsis); |
128 |
|
|
} |
129 |
|
|
|
130 |
|
|
tag_name = argv[0]; |
131 |
|
|
argc--; |
132 |
|
|
argv++; |
133 |
|
|
|
134 |
|
|
if (!rcs_sym_check(tag_name)) { |
135 |
|
|
fatal("tag `%s' must not contain the characters `%s'", |
136 |
|
|
tag_name, RCS_SYM_INVALCHAR); |
137 |
|
|
} |
138 |
|
|
|
139 |
|
|
if (tag_oldname != NULL) { |
140 |
|
|
if (runflags & T_DELETE) |
141 |
|
|
tag_oldname = NULL; |
142 |
|
|
else |
143 |
|
|
tag = tag_oldname; |
144 |
|
|
} |
145 |
|
|
|
146 |
|
|
if (tag_date != NULL) { |
147 |
|
|
if (runflags & T_DELETE) |
148 |
|
|
tag_date = NULL; |
149 |
|
|
else |
150 |
|
|
tag = tag_date; |
151 |
|
|
} |
152 |
|
|
|
153 |
|
|
if (tag_oldname != NULL && tag_date != NULL) |
154 |
|
|
fatal("-r and -D options are mutually exclusive"); |
155 |
|
|
|
156 |
|
|
cr.enterdir = NULL; |
157 |
|
|
cr.leavedir = NULL; |
158 |
|
|
|
159 |
|
|
if (cvsroot_is_remote()) { |
160 |
|
|
cvs_client_connect_to_server(); |
161 |
|
|
cr.fileproc = cvs_client_sendfile; |
162 |
|
|
|
163 |
|
|
if (argc > 0) |
164 |
|
|
cvs_file_run(argc, argv, &cr); |
165 |
|
|
else |
166 |
|
|
cvs_file_run(1, &arg, &cr); |
167 |
|
|
|
168 |
|
|
if (runflags & T_BRANCH) |
169 |
|
|
cvs_client_send_request("Argument -b"); |
170 |
|
|
|
171 |
|
|
if (runflags & T_CHECK_UPTODATE) |
172 |
|
|
cvs_client_send_request("Argument -c"); |
173 |
|
|
|
174 |
|
|
if (runflags & T_DELETE) |
175 |
|
|
cvs_client_send_request("Argument -d"); |
176 |
|
|
|
177 |
|
|
if (runflags & T_FORCE_MOVE) |
178 |
|
|
cvs_client_send_request("Argument -F"); |
179 |
|
|
|
180 |
|
|
if (!(flags & CR_RECURSE_DIRS)) |
181 |
|
|
cvs_client_send_request("Argument -l"); |
182 |
|
|
|
183 |
|
|
if (tag_date != NULL) |
184 |
|
|
cvs_client_send_request("Argument -D%s", tag_date); |
185 |
|
|
|
186 |
|
|
if (tag_oldname != NULL) |
187 |
|
|
cvs_client_send_request("Argument -r%s", tag_oldname); |
188 |
|
|
|
189 |
|
|
cvs_client_send_request("Argument %s", tag_name); |
190 |
|
|
cvs_client_send_files(argv, argc); |
191 |
|
|
cvs_client_senddir("."); |
192 |
|
|
cvs_client_send_request((cvs_cmdop == CVS_OP_RTAG) ? |
193 |
|
|
"rtag" : "tag"); |
194 |
|
|
cvs_client_get_responses(); |
195 |
|
|
|
196 |
|
|
return (0); |
197 |
|
|
} |
198 |
|
|
|
199 |
|
|
if (cvs_cmdop == CVS_OP_RTAG && chdir(current_cvsroot->cr_dir) == -1) |
200 |
|
|
fatal("cvs_tag: %s", strerror(errno)); |
201 |
|
|
|
202 |
|
|
TAILQ_INIT(&files_info); |
203 |
|
|
cvs_get_repository_name(".", repo, PATH_MAX); |
204 |
|
|
line_list = cvs_trigger_getlines(CVS_PATH_TAGINFO, repo); |
205 |
|
|
|
206 |
|
|
cr.flags = flags; |
207 |
|
|
cr.fileproc = cvs_tag_check_files; |
208 |
|
|
|
209 |
|
|
if (argc > 0) |
210 |
|
|
cvs_file_run(argc, argv, &cr); |
211 |
|
|
else |
212 |
|
|
cvs_file_run(1, &arg, &cr); |
213 |
|
|
|
214 |
|
|
if (tag_errors) |
215 |
|
|
fatal("correct the above errors first!"); |
216 |
|
|
|
217 |
|
|
if (line_list != NULL) { |
218 |
|
|
if (cvs_trigger_handle(CVS_TRIGGER_TAGINFO, repo, NULL, |
219 |
|
|
line_list, &files_info)) |
220 |
|
|
fatal("Pre-tag check failed"); |
221 |
|
|
cvs_trigger_freelist(line_list); |
222 |
|
|
} |
223 |
|
|
|
224 |
|
|
cr.fileproc = cvs_tag_local; |
225 |
|
|
|
226 |
|
|
if (argc > 0) |
227 |
|
|
cvs_file_run(argc, argv, &cr); |
228 |
|
|
else |
229 |
|
|
cvs_file_run(1, &arg, &cr); |
230 |
|
|
|
231 |
|
|
if (line_list != NULL) |
232 |
|
|
cvs_trigger_freeinfo(&files_info); |
233 |
|
|
|
234 |
|
|
return (0); |
235 |
|
|
} |
236 |
|
|
|
237 |
|
|
void |
238 |
|
|
cvs_tag_check_files(struct cvs_file *cf) |
239 |
|
|
{ |
240 |
|
|
RCSNUM *srev = NULL, *rev = NULL; |
241 |
|
|
char rbuf[CVS_REV_BUFSZ]; |
242 |
|
|
struct file_info *fi; |
243 |
|
|
|
244 |
|
|
cvs_log(LP_TRACE, "cvs_tag_check_files(%s)", cf->file_path); |
245 |
|
|
|
246 |
|
|
cvs_file_classify(cf, tag); |
247 |
|
|
|
248 |
|
|
if (cf->file_type == CVS_DIR || cf->file_status == FILE_UNKNOWN) |
249 |
|
|
return; |
250 |
|
|
|
251 |
|
|
if (runflags & T_CHECK_UPTODATE) { |
252 |
|
|
if (cf->file_status != FILE_UPTODATE && |
253 |
|
|
cf->file_status != FILE_CHECKOUT && |
254 |
|
|
cf->file_status != FILE_PATCH) { |
255 |
|
|
tag_errors++; |
256 |
|
|
cvs_log(LP_NOTICE, |
257 |
|
|
"%s is locally modified", cf->file_path); |
258 |
|
|
return; |
259 |
|
|
} |
260 |
|
|
} |
261 |
|
|
|
262 |
|
|
switch (cf->file_status) { |
263 |
|
|
case FILE_ADDED: |
264 |
|
|
case FILE_REMOVED: |
265 |
|
|
return; |
266 |
|
|
default: |
267 |
|
|
break; |
268 |
|
|
} |
269 |
|
|
|
270 |
|
|
if (cvs_cmdop == CVS_OP_TAG) { |
271 |
|
|
if (cf->file_ent == NULL) |
272 |
|
|
return; |
273 |
|
|
srev = cf->file_ent->ce_rev; |
274 |
|
|
} else |
275 |
|
|
srev = cf->file_rcsrev; |
276 |
|
|
|
277 |
|
|
rcsnum_tostr(srev, rbuf, sizeof(rbuf)); |
278 |
|
|
fi = xcalloc(1, sizeof(*fi)); |
279 |
|
|
fi->nrevstr = xstrdup(rbuf); |
280 |
|
|
fi->file_path = xstrdup(cf->file_path); |
281 |
|
|
|
282 |
|
|
if (tag_oldname != NULL) |
283 |
|
|
fi->tag_old = xstrdup(tag_oldname); |
284 |
|
|
else if (tag_date != NULL) |
285 |
|
|
fi->tag_old = xstrdup(tag_date); |
286 |
|
|
|
287 |
|
|
if ((rev = rcs_sym_getrev(cf->file_rcs, tag_name)) != NULL) { |
288 |
|
|
if (!rcsnum_differ(srev, rev)) |
289 |
|
|
goto bad; |
290 |
|
|
rcsnum_tostr(rev, rbuf, sizeof(rbuf)); |
291 |
|
|
fi->crevstr = xstrdup(rbuf); |
292 |
|
|
free(rev); |
293 |
|
|
} else if (runflags & T_DELETE) |
294 |
|
|
goto bad; |
295 |
|
|
|
296 |
|
|
fi->tag_new = xstrdup(tag_name); |
297 |
|
|
|
298 |
|
|
if (runflags & T_BRANCH) |
299 |
|
|
fi->tag_type = 'T'; |
300 |
|
|
else if (runflags & T_DELETE) |
301 |
|
|
fi->tag_type = '?'; |
302 |
|
|
else |
303 |
|
|
fi->tag_type = 'N'; |
304 |
|
|
|
305 |
|
|
if (runflags & T_FORCE_MOVE) |
306 |
|
|
fi->tag_op = "mov"; |
307 |
|
|
else if (runflags & T_DELETE) |
308 |
|
|
fi->tag_op = "del"; |
309 |
|
|
else |
310 |
|
|
fi->tag_op = "add"; |
311 |
|
|
|
312 |
|
|
TAILQ_INSERT_TAIL(&files_info, fi, flist); |
313 |
|
|
return; |
314 |
|
|
|
315 |
|
|
bad: |
316 |
|
|
free(fi->file_path); |
317 |
|
|
free(fi->crevstr); |
318 |
|
|
free(fi->nrevstr); |
319 |
|
|
free(fi->tag_new); |
320 |
|
|
free(fi->tag_old); |
321 |
|
|
free(rev); |
322 |
|
|
free(fi); |
323 |
|
|
} |
324 |
|
|
|
325 |
|
|
void |
326 |
|
|
cvs_tag_local(struct cvs_file *cf) |
327 |
|
|
{ |
328 |
|
|
cvs_log(LP_TRACE, "cvs_tag_local(%s)", cf->file_path); |
329 |
|
|
|
330 |
|
|
cvs_file_classify(cf, tag); |
331 |
|
|
|
332 |
|
|
if (cf->file_type == CVS_DIR) { |
333 |
|
|
if (verbosity > 1) { |
334 |
|
|
cvs_log(LP_NOTICE, "%s %s", |
335 |
|
|
(runflags & T_DELETE) ? "Untagging" : "Tagging", |
336 |
|
|
cf->file_path); |
337 |
|
|
} |
338 |
|
|
return; |
339 |
|
|
} |
340 |
|
|
|
341 |
|
|
if (runflags & T_DELETE) { |
342 |
|
|
if (tag_del(cf) == 0) { |
343 |
|
|
if (verbosity > 0) |
344 |
|
|
cvs_printf("D %s\n", cf->file_path); |
345 |
|
|
} |
346 |
|
|
return; |
347 |
|
|
} |
348 |
|
|
|
349 |
|
|
switch (cf->file_status) { |
350 |
|
|
case FILE_ADDED: |
351 |
|
|
if (verbosity > 1) { |
352 |
|
|
cvs_log(LP_NOTICE, |
353 |
|
|
"couldn't tag added but un-committed file `%s'", |
354 |
|
|
cf->file_path); |
355 |
|
|
} |
356 |
|
|
break; |
357 |
|
|
case FILE_REMOVED: |
358 |
|
|
if (verbosity > 1) { |
359 |
|
|
cvs_log(LP_NOTICE, |
360 |
|
|
"skipping removed but un-committed file `%s'", |
361 |
|
|
cf->file_path); |
362 |
|
|
} |
363 |
|
|
break; |
364 |
|
|
case FILE_CHECKOUT: |
365 |
|
|
case FILE_MODIFIED: |
366 |
|
|
case FILE_PATCH: |
367 |
|
|
case FILE_UPTODATE: |
368 |
|
|
if (tag_add(cf) == 0) { |
369 |
|
|
if (verbosity > 0) |
370 |
|
|
cvs_printf("T %s\n", cf->file_path); |
371 |
|
|
cvs_history_add(CVS_HISTORY_TAG, cf, tag_name); |
372 |
|
|
} |
373 |
|
|
break; |
374 |
|
|
default: |
375 |
|
|
break; |
376 |
|
|
} |
377 |
|
|
} |
378 |
|
|
|
379 |
|
|
static int |
380 |
|
|
tag_del(struct cvs_file *cf) |
381 |
|
|
{ |
382 |
|
|
if (cf->file_rcs == NULL) |
383 |
|
|
return (-1); |
384 |
|
|
|
385 |
|
|
if (cvs_noexec == 1) |
386 |
|
|
return (0); |
387 |
|
|
|
388 |
|
|
return (rcs_sym_remove(cf->file_rcs, tag_name)); |
389 |
|
|
} |
390 |
|
|
|
391 |
|
|
static int |
392 |
|
|
tag_add(struct cvs_file *cf) |
393 |
|
|
{ |
394 |
|
|
int ret; |
395 |
|
|
char revbuf[CVS_REV_BUFSZ], trevbuf[CVS_REV_BUFSZ]; |
396 |
|
|
RCSNUM *srev, *trev; |
397 |
|
|
struct rcs_sym *sym; |
398 |
|
|
|
399 |
|
|
if (cf->file_rcs == NULL) { |
400 |
|
|
if (verbosity > 1) |
401 |
|
|
cvs_log(LP_NOTICE, "cannot find revision " |
402 |
|
|
"control file for `%s'", cf->file_name); |
403 |
|
|
return (-1); |
404 |
|
|
} |
405 |
|
|
|
406 |
|
|
if (cvs_cmdop == CVS_OP_TAG) { |
407 |
|
|
if (cf->file_ent == NULL) |
408 |
|
|
return (-1); |
409 |
|
|
srev = cf->file_ent->ce_rev; |
410 |
|
|
} else |
411 |
|
|
srev = cf->file_rcsrev; |
412 |
|
|
|
413 |
|
|
if (cvs_noexec == 1) |
414 |
|
|
return (0); |
415 |
|
|
|
416 |
|
|
(void)rcsnum_tostr(srev, revbuf, sizeof(revbuf)); |
417 |
|
|
|
418 |
|
|
trev = rcs_sym_getrev(cf->file_rcs, tag_name); |
419 |
|
|
if (trev != NULL) { |
420 |
|
|
if (rcsnum_cmp(srev, trev, 0) == 0) { |
421 |
|
|
free(trev); |
422 |
|
|
return (-1); |
423 |
|
|
} |
424 |
|
|
(void)rcsnum_tostr(trev, trevbuf, sizeof(trevbuf)); |
425 |
|
|
free(trev); |
426 |
|
|
|
427 |
|
|
if (!(runflags & T_FORCE_MOVE)) { |
428 |
|
|
cvs_printf("W %s : %s ", cf->file_path, tag_name); |
429 |
|
|
cvs_printf("already exists on version %s", trevbuf); |
430 |
|
|
cvs_printf(" : NOT MOVING tag to version %s\n", revbuf); |
431 |
|
|
|
432 |
|
|
return (-1); |
433 |
|
|
} else { |
434 |
|
|
sym = rcs_sym_get(cf->file_rcs, tag_name); |
435 |
|
|
rcsnum_cpy(srev, sym->rs_num, 0); |
436 |
|
|
cf->file_rcs->rf_flags &= ~RCS_SYNCED; |
437 |
|
|
|
438 |
|
|
return (0); |
439 |
|
|
} |
440 |
|
|
} |
441 |
|
|
|
442 |
|
|
if (runflags & T_BRANCH) { |
443 |
|
|
if ((trev = rcs_branch_new(cf->file_rcs, srev)) == NULL) |
444 |
|
|
fatal("Cannot create a new branch"); |
445 |
|
|
} else { |
446 |
|
|
trev = rcsnum_alloc(); |
447 |
|
|
rcsnum_cpy(srev, trev, 0); |
448 |
|
|
} |
449 |
|
|
|
450 |
|
|
if ((ret = rcs_sym_add(cf->file_rcs, tag_name, trev)) != 0) { |
451 |
|
|
if (ret != 1) { |
452 |
|
|
cvs_log(LP_NOTICE, |
453 |
|
|
"failed to set tag %s to revision %s in %s", |
454 |
|
|
tag_name, revbuf, cf->file_rcs->rf_path); |
455 |
|
|
} |
456 |
|
|
free(trev); |
457 |
|
|
return (-1); |
458 |
|
|
} |
459 |
|
|
|
460 |
|
|
free(trev); |
461 |
|
|
return (0); |
462 |
|
|
} |