Annotation of src/usr.bin/cvs/tag.c, Revision 1.87
1.87 ! joris 1: /* $OpenBSD: tag.c,v 1.86 2017/05/31 16:13:25 joris Exp $ */
1.1 jfb 2: /*
1.45 xsa 3: * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org>
1.1 jfb 4: *
1.45 xsa 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.
1.1 jfb 8: *
1.45 xsa 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.
1.1 jfb 16: */
17:
1.60 tobias 18: #include <errno.h>
1.82 nicm 19: #include <stdlib.h>
1.60 tobias 20: #include <string.h>
1.55 otto 21: #include <unistd.h>
1.1 jfb 22:
23: #include "cvs.h"
1.50 xsa 24: #include "remote.h"
1.1 jfb 25:
1.52 xsa 26: #define T_CHECK_UPTODATE 0x01
27: #define T_DELETE 0x02
28: #define T_FORCE_MOVE 0x04
1.59 tobias 29: #define T_BRANCH 0x08
1.52 xsa 30:
1.74 joris 31: void cvs_tag_check_files(struct cvs_file *);
1.45 xsa 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:
1.74 joris 37: struct file_info_list files_info;
38:
1.86 joris 39: static int runflags = 0;
40: static int tag_errors = 0;
1.45 xsa 41: static char *tag = NULL;
42: static char *tag_date = NULL;
43: static char *tag_name = NULL;
44: static char *tag_oldname = NULL;
1.7 joris 45:
1.60 tobias 46: struct cvs_cmd cvs_cmd_rtag = {
1.71 joris 47: CVS_OP_RTAG, CVS_LOCK_REPO, "rtag",
1.60 tobias 48: { "rt", "rfreeze" },
49: "Add a symbolic tag to a module",
1.73 reyk 50: "[-bcdFflR] [-D date | -r rev] symbolic_tag module ...",
1.60 tobias 51: "bcD:dFflRr:",
52: NULL,
53: cvs_tag
54: };
55:
1.17 jfb 56: struct cvs_cmd cvs_cmd_tag = {
1.71 joris 57: CVS_OP_TAG, CVS_USE_WDIR | CVS_LOCK_REPO, "tag",
1.17 jfb 58: { "ta", "freeze" },
59: "Add a symbolic tag to checked out version of files",
1.73 reyk 60: "[-bcdFflR] [-D date | -r rev] symbolic_tag [file ...]",
1.17 jfb 61: "bcD:dFflRr:",
62: NULL,
1.45 xsa 63: cvs_tag
1.17 jfb 64: };
65:
1.45 xsa 66: int
67: cvs_tag(int argc, char **argv)
1.1 jfb 68: {
1.60 tobias 69: int ch, flags, i;
1.81 deraadt 70: char repo[PATH_MAX];
1.49 reyk 71: char *arg = ".";
1.45 xsa 72: struct cvs_recursion cr;
1.74 joris 73: struct trigger_list *line_list;
1.1 jfb 74:
1.45 xsa 75: flags = CR_RECURSE_DIRS;
1.1 jfb 76:
1.65 tobias 77: while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_TAG ?
78: cvs_cmd_tag.cmd_opts : cvs_cmd_rtag.cmd_opts)) != -1) {
1.1 jfb 79: switch (ch) {
1.59 tobias 80: case 'b':
81: runflags |= T_BRANCH;
82: break;
1.52 xsa 83: case 'c':
84: runflags |= T_CHECK_UPTODATE;
85: break;
1.45 xsa 86: case 'D':
87: tag_date = optarg;
1.1 jfb 88: break;
89: case 'd':
1.52 xsa 90: runflags |= T_DELETE;
1.3 jfb 91: break;
1.46 xsa 92: case 'F':
1.52 xsa 93: runflags |= T_FORCE_MOVE;
1.46 xsa 94: break;
1.1 jfb 95: case 'l':
1.45 xsa 96: flags &= ~CR_RECURSE_DIRS;
1.17 jfb 97: break;
98: case 'R':
1.62 tobias 99: flags |= CR_RECURSE_DIRS;
1.1 jfb 100: break;
101: case 'r':
1.14 jfb 102: tag_oldname = optarg;
1.1 jfb 103: break;
104: default:
1.70 tobias 105: fatal("%s", cvs_cmdop == CVS_OP_TAG ?
106: cvs_cmd_tag.cmd_synopsis :
107: cvs_cmd_rtag.cmd_synopsis);
1.1 jfb 108: }
109: }
110:
111: argc -= optind;
112: argv += optind;
113:
1.60 tobias 114: if (cvs_cmdop == CVS_OP_RTAG) {
1.63 tobias 115: flags |= CR_REPO;
116:
1.60 tobias 117: if (argc < 2)
118: fatal("%s", cvs_cmd_rtag.cmd_synopsis);
1.64 xsa 119:
1.66 joris 120: for (i = 1; i < argc; i++) {
1.86 joris 121: if (argv[i][0] == '/') {
1.66 joris 122: fatal("Absolute path name is invalid: %s",
123: argv[i]);
1.86 joris 124: }
1.66 joris 125: }
1.86 joris 126: } else if (cvs_cmdop == CVS_OP_TAG && argc == 0) {
1.45 xsa 127: fatal("%s", cvs_cmd_tag.cmd_synopsis);
1.86 joris 128: }
1.45 xsa 129:
130: tag_name = argv[0];
131: argc--;
132: argv++;
1.15 jfb 133:
1.86 joris 134: if (!rcs_sym_check(tag_name)) {
1.38 xsa 135: fatal("tag `%s' must not contain the characters `%s'",
1.15 jfb 136: tag_name, RCS_SYM_INVALCHAR);
1.86 joris 137: }
1.1 jfb 138:
1.45 xsa 139: if (tag_oldname != NULL) {
1.52 xsa 140: if (runflags & T_DELETE)
1.45 xsa 141: tag_oldname = NULL;
142: else
143: tag = tag_oldname;
144: }
145:
146: if (tag_date != NULL) {
1.52 xsa 147: if (runflags & T_DELETE)
1.45 xsa 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;
1.50 xsa 158:
1.87 ! joris 159: if (cvsroot_is_remote()) {
1.53 joris 160: cvs_client_connect_to_server();
1.50 xsa 161: cr.fileproc = cvs_client_sendfile;
162:
1.86 joris 163: if (argc > 0)
164: cvs_file_run(argc, argv, &cr);
165: else
166: cvs_file_run(1, &arg, &cr);
167:
1.59 tobias 168: if (runflags & T_BRANCH)
169: cvs_client_send_request("Argument -b");
170:
1.52 xsa 171: if (runflags & T_CHECK_UPTODATE)
172: cvs_client_send_request("Argument -c");
173:
174: if (runflags & T_DELETE)
1.50 xsa 175: cvs_client_send_request("Argument -d");
176:
1.52 xsa 177: if (runflags & T_FORCE_MOVE)
1.50 xsa 178: cvs_client_send_request("Argument -F");
179:
180: if (!(flags & CR_RECURSE_DIRS))
181: cvs_client_send_request("Argument -l");
1.76 tobias 182:
183: if (tag_date != NULL)
184: cvs_client_send_request("Argument -D%s", tag_date);
1.50 xsa 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);
1.86 joris 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);
1.50 xsa 197: }
198:
1.86 joris 199: if (cvs_cmdop == CVS_OP_RTAG && chdir(current_cvsroot->cr_dir) == -1)
200: fatal("cvs_tag: %s", strerror(errno));
1.1 jfb 201:
1.86 joris 202: TAILQ_INIT(&files_info);
1.81 deraadt 203: cvs_get_repository_name(".", repo, PATH_MAX);
1.74 joris 204: line_list = cvs_trigger_getlines(CVS_PATH_TAGINFO, repo);
1.86 joris 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:
1.74 joris 217: if (line_list != NULL) {
218: if (cvs_trigger_handle(CVS_TRIGGER_TAGINFO, repo, NULL,
1.86 joris 219: line_list, &files_info))
220: fatal("Pre-tag check failed");
1.74 joris 221: cvs_trigger_freelist(line_list);
222: }
223:
224: cr.fileproc = cvs_tag_local;
225:
1.86 joris 226: if (argc > 0)
227: cvs_file_run(argc, argv, &cr);
228: else
229: cvs_file_run(1, &arg, &cr);
1.3 jfb 230:
1.79 joris 231: if (line_list != NULL)
232: cvs_trigger_freeinfo(&files_info);
233:
1.7 joris 234: return (0);
1.74 joris 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:
1.86 joris 248: if (cf->file_type == CVS_DIR || cf->file_status == FILE_UNKNOWN)
1.74 joris 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) {
1.86 joris 255: tag_errors++;
256: cvs_log(LP_NOTICE,
257: "%s is locally modified", cf->file_path);
1.74 joris 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));
1.77 tobias 278: fi = xcalloc(1, sizeof(*fi));
279: fi->nrevstr = xstrdup(rbuf);
1.74 joris 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);
1.84 fcambus 292: free(rev);
1.74 joris 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:
1.82 nicm 316: free(fi->file_path);
317: free(fi->crevstr);
318: free(fi->nrevstr);
319: free(fi->tag_new);
320: free(fi->tag_old);
1.84 fcambus 321: free(rev);
1.82 nicm 322: free(fi);
1.7 joris 323: }
324:
1.45 xsa 325: void
326: cvs_tag_local(struct cvs_file *cf)
1.7 joris 327: {
1.45 xsa 328: cvs_log(LP_TRACE, "cvs_tag_local(%s)", cf->file_path);
1.17 jfb 329:
1.60 tobias 330: cvs_file_classify(cf, tag);
331:
1.45 xsa 332: if (cf->file_type == CVS_DIR) {
333: if (verbosity > 1) {
334: cvs_log(LP_NOTICE, "%s %s",
1.52 xsa 335: (runflags & T_DELETE) ? "Untagging" : "Tagging",
1.45 xsa 336: cf->file_path);
337: }
338: return;
1.52 xsa 339: }
340:
341: if (runflags & T_DELETE) {
1.45 xsa 342: if (tag_del(cf) == 0) {
343: if (verbosity > 0)
344: cvs_printf("D %s\n", cf->file_path);
345: }
346: return;
347: }
1.21 xsa 348:
1.69 deraadt 349: switch (cf->file_status) {
1.45 xsa 350: case FILE_ADDED:
351: if (verbosity > 1) {
352: cvs_log(LP_NOTICE,
1.83 mmcc 353: "couldn't tag added but un-committed file `%s'",
1.45 xsa 354: cf->file_path);
1.35 joris 355: }
1.78 tobias 356: break;
1.45 xsa 357: case FILE_REMOVED:
358: if (verbosity > 1) {
359: cvs_log(LP_NOTICE,
1.83 mmcc 360: "skipping removed but un-committed file `%s'",
1.45 xsa 361: cf->file_path);
362: }
1.78 tobias 363: break;
1.45 xsa 364: case FILE_CHECKOUT:
365: case FILE_MODIFIED:
1.60 tobias 366: case FILE_PATCH:
1.45 xsa 367: case FILE_UPTODATE:
368: if (tag_add(cf) == 0) {
369: if (verbosity > 0)
370: cvs_printf("T %s\n", cf->file_path);
1.56 joris 371: cvs_history_add(CVS_HISTORY_TAG, cf, tag_name);
1.35 joris 372: }
1.45 xsa 373: break;
374: default:
375: break;
1.1 jfb 376: }
377: }
378:
1.14 jfb 379: static int
1.45 xsa 380: tag_del(struct cvs_file *cf)
1.1 jfb 381: {
1.45 xsa 382: if (cf->file_rcs == NULL)
383: return (-1);
1.1 jfb 384:
1.45 xsa 385: if (cvs_noexec == 1)
1.35 joris 386: return (0);
1.1 jfb 387:
1.45 xsa 388: return (rcs_sym_remove(cf->file_rcs, tag_name));
1.14 jfb 389: }
390:
391: static int
1.45 xsa 392: tag_add(struct cvs_file *cf)
1.14 jfb 393: {
1.68 joris 394: int ret;
1.57 xsa 395: char revbuf[CVS_REV_BUFSZ], trevbuf[CVS_REV_BUFSZ];
1.60 tobias 396: RCSNUM *srev, *trev;
1.46 xsa 397: struct rcs_sym *sym;
1.14 jfb 398:
1.45 xsa 399: if (cf->file_rcs == NULL) {
1.23 xsa 400: if (verbosity > 1)
1.45 xsa 401: cvs_log(LP_NOTICE, "cannot find revision "
402: "control file for `%s'", cf->file_name);
403: return (-1);
1.14 jfb 404: }
1.1 jfb 405:
1.60 tobias 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:
1.45 xsa 413: if (cvs_noexec == 1)
1.32 xsa 414: return (0);
1.1 jfb 415:
1.60 tobias 416: (void)rcsnum_tostr(srev, revbuf, sizeof(revbuf));
1.58 tobias 417:
1.46 xsa 418: trev = rcs_sym_getrev(cf->file_rcs, tag_name);
419: if (trev != NULL) {
1.60 tobias 420: if (rcsnum_cmp(srev, trev, 0) == 0) {
1.84 fcambus 421: free(trev);
1.46 xsa 422: return (-1);
423: }
424: (void)rcsnum_tostr(trev, trevbuf, sizeof(trevbuf));
1.84 fcambus 425: free(trev);
1.46 xsa 426:
1.52 xsa 427: if (!(runflags & T_FORCE_MOVE)) {
1.46 xsa 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);
1.67 joris 433: } else {
1.46 xsa 434: sym = rcs_sym_get(cf->file_rcs, tag_name);
1.60 tobias 435: rcsnum_cpy(srev, sym->rs_num, 0);
1.46 xsa 436: cf->file_rcs->rf_flags &= ~RCS_SYNCED;
437:
438: return (0);
439: }
440: }
441:
1.59 tobias 442: if (runflags & T_BRANCH) {
1.75 tobias 443: if ((trev = rcs_branch_new(cf->file_rcs, srev)) == NULL)
1.59 tobias 444: fatal("Cannot create a new branch");
445: } else {
446: trev = rcsnum_alloc();
1.60 tobias 447: rcsnum_cpy(srev, trev, 0);
1.59 tobias 448: }
449:
1.68 joris 450: if ((ret = rcs_sym_add(cf->file_rcs, tag_name, trev)) != 0) {
451: if (ret != 1) {
1.45 xsa 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);
1.39 xsa 455: }
1.84 fcambus 456: free(trev);
1.45 xsa 457: return (-1);
1.1 jfb 458: }
1.17 jfb 459:
1.84 fcambus 460: free(trev);
1.14 jfb 461: return (0);
1.1 jfb 462: }