1 /* $OpenBSD: tag.c,v 1.76 2008/06/20 14:04:29 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 cvs_trigger_freeinfo(&files_info); 230 return (0); 231 } 232 233 void 234 cvs_tag_check_files(struct cvs_file *cf) 235 { 236 RCSNUM *srev = NULL, *rev = NULL; 237 char rbuf[CVS_REV_BUFSZ]; 238 struct file_info *fi; 239 240 cvs_log(LP_TRACE, "cvs_tag_check_files(%s)", cf->file_path); 241 242 cvs_file_classify(cf, tag); 243 244 if (cf->file_type == CVS_DIR) 245 return; 246 247 if (runflags & T_CHECK_UPTODATE) { 248 if (cf->file_status != FILE_UPTODATE && 249 cf->file_status != FILE_CHECKOUT && 250 cf->file_status != FILE_PATCH) { 251 return; 252 } 253 } 254 255 switch (cf->file_status) { 256 case FILE_ADDED: 257 case FILE_REMOVED: 258 return; 259 default: 260 break; 261 } 262 263 fi = xcalloc(1, sizeof(*fi)); 264 fi->nrevstr = xstrdup(rbuf); 265 266 if (cvs_cmdop == CVS_OP_TAG) { 267 if (cf->file_ent == NULL) 268 return; 269 srev = cf->file_ent->ce_rev; 270 } else 271 srev = cf->file_rcsrev; 272 273 rcsnum_tostr(srev, rbuf, sizeof(rbuf)); 274 fi->file_path = xstrdup(cf->file_path); 275 276 if (tag_oldname != NULL) 277 fi->tag_old = xstrdup(tag_oldname); 278 else if (tag_date != NULL) 279 fi->tag_old = xstrdup(tag_date); 280 281 if ((rev = rcs_sym_getrev(cf->file_rcs, tag_name)) != NULL) { 282 if (!rcsnum_differ(srev, rev)) 283 goto bad; 284 rcsnum_tostr(rev, rbuf, sizeof(rbuf)); 285 fi->crevstr = xstrdup(rbuf); 286 } else if (runflags & T_DELETE) 287 goto bad; 288 289 fi->tag_new = xstrdup(tag_name); 290 291 if (runflags & T_BRANCH) 292 fi->tag_type = 'T'; 293 else if (runflags & T_DELETE) 294 fi->tag_type = '?'; 295 else 296 fi->tag_type = 'N'; 297 298 if (runflags & T_FORCE_MOVE) 299 fi->tag_op = "mov"; 300 else if (runflags & T_DELETE) 301 fi->tag_op = "del"; 302 else 303 fi->tag_op = "add"; 304 305 TAILQ_INSERT_TAIL(&files_info, fi, flist); 306 return; 307 308 bad: 309 if (fi->file_path != NULL) 310 xfree(fi->file_path); 311 if (fi->crevstr != NULL) 312 xfree(fi->crevstr); 313 if (fi->nrevstr != NULL) 314 xfree(fi->nrevstr); 315 if (fi->tag_new != NULL) 316 xfree(fi->tag_new); 317 if (fi->tag_old != NULL) 318 xfree(fi->tag_old); 319 if (rev != NULL) 320 rcsnum_free(rev); 321 xfree(fi); 322 } 323 324 void 325 cvs_tag_local(struct cvs_file *cf) 326 { 327 cvs_log(LP_TRACE, "cvs_tag_local(%s)", cf->file_path); 328 329 cvs_file_classify(cf, tag); 330 331 if (cf->file_type == CVS_DIR) { 332 if (verbosity > 1) { 333 cvs_log(LP_NOTICE, "%s %s", 334 (runflags & T_DELETE) ? "Untagging" : "Tagging", 335 cf->file_path); 336 } 337 return; 338 } 339 340 if (runflags & T_CHECK_UPTODATE) { 341 if (cf->file_status != FILE_UPTODATE && 342 cf->file_status != FILE_CHECKOUT && 343 cf->file_status != FILE_PATCH) { 344 cvs_log(LP_NOTICE, 345 "%s is locally modified", cf->file_path); 346 return; 347 } 348 } 349 350 if (runflags & T_DELETE) { 351 if (tag_del(cf) == 0) { 352 if (verbosity > 0) 353 cvs_printf("D %s\n", cf->file_path); 354 355 rcs_write(cf->file_rcs); 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 return; 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 return; 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 383 rcs_write(cf->file_rcs); 384 cvs_history_add(CVS_HISTORY_TAG, cf, tag_name); 385 } 386 break; 387 default: 388 break; 389 } 390 } 391 392 static int 393 tag_del(struct cvs_file *cf) 394 { 395 if (cf->file_rcs == NULL) 396 return (-1); 397 398 if (cvs_noexec == 1) 399 return (0); 400 401 return (rcs_sym_remove(cf->file_rcs, tag_name)); 402 } 403 404 static int 405 tag_add(struct cvs_file *cf) 406 { 407 int ret; 408 char revbuf[CVS_REV_BUFSZ], trevbuf[CVS_REV_BUFSZ]; 409 RCSNUM *srev, *trev; 410 struct rcs_sym *sym; 411 412 if (cf->file_rcs == NULL) { 413 if (verbosity > 1) 414 cvs_log(LP_NOTICE, "cannot find revision " 415 "control file for `%s'", cf->file_name); 416 return (-1); 417 } 418 419 if (cvs_cmdop == CVS_OP_TAG) { 420 if (cf->file_ent == NULL) 421 return (-1); 422 srev = cf->file_ent->ce_rev; 423 } else 424 srev = cf->file_rcsrev; 425 426 if (cvs_noexec == 1) 427 return (0); 428 429 (void)rcsnum_tostr(srev, revbuf, sizeof(revbuf)); 430 431 trev = rcs_sym_getrev(cf->file_rcs, tag_name); 432 if (trev != NULL) { 433 if (rcsnum_cmp(srev, trev, 0) == 0) { 434 rcsnum_free(trev); 435 return (-1); 436 } 437 (void)rcsnum_tostr(trev, trevbuf, sizeof(trevbuf)); 438 rcsnum_free(trev); 439 440 if (!(runflags & T_FORCE_MOVE)) { 441 cvs_printf("W %s : %s ", cf->file_path, tag_name); 442 cvs_printf("already exists on version %s", trevbuf); 443 cvs_printf(" : NOT MOVING tag to version %s\n", revbuf); 444 445 return (-1); 446 } else { 447 sym = rcs_sym_get(cf->file_rcs, tag_name); 448 rcsnum_cpy(srev, sym->rs_num, 0); 449 cf->file_rcs->rf_flags &= ~RCS_SYNCED; 450 451 return (0); 452 } 453 } 454 455 if (runflags & T_BRANCH) { 456 if ((trev = rcs_branch_new(cf->file_rcs, srev)) == NULL) 457 fatal("Cannot create a new branch"); 458 } else { 459 trev = rcsnum_alloc(); 460 rcsnum_cpy(srev, trev, 0); 461 } 462 463 if ((ret = rcs_sym_add(cf->file_rcs, tag_name, trev)) != 0) { 464 if (ret != 1) { 465 cvs_log(LP_NOTICE, 466 "failed to set tag %s to revision %s in %s", 467 tag_name, revbuf, cf->file_rcs->rf_path); 468 } 469 rcsnum_free(trev); 470 return (-1); 471 } 472 473 rcsnum_free(trev); 474 return (0); 475 } 476