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