1 /* $OpenBSD: tag.c,v 1.79 2009/03/24 18:33:25 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 <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 } else if (runflags & T_DELETE) 288 goto bad; 289 290 fi->tag_new = xstrdup(tag_name); 291 292 if (runflags & T_BRANCH) 293 fi->tag_type = 'T'; 294 else if (runflags & T_DELETE) 295 fi->tag_type = '?'; 296 else 297 fi->tag_type = 'N'; 298 299 if (runflags & T_FORCE_MOVE) 300 fi->tag_op = "mov"; 301 else if (runflags & T_DELETE) 302 fi->tag_op = "del"; 303 else 304 fi->tag_op = "add"; 305 306 TAILQ_INSERT_TAIL(&files_info, fi, flist); 307 return; 308 309 bad: 310 if (fi->file_path != NULL) 311 xfree(fi->file_path); 312 if (fi->crevstr != NULL) 313 xfree(fi->crevstr); 314 if (fi->nrevstr != NULL) 315 xfree(fi->nrevstr); 316 if (fi->tag_new != NULL) 317 xfree(fi->tag_new); 318 if (fi->tag_old != NULL) 319 xfree(fi->tag_old); 320 if (rev != NULL) 321 rcsnum_free(rev); 322 xfree(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_CHECK_UPTODATE) { 342 if (cf->file_status != FILE_UPTODATE && 343 cf->file_status != FILE_CHECKOUT && 344 cf->file_status != FILE_PATCH) { 345 cvs_log(LP_NOTICE, 346 "%s is locally modified", cf->file_path); 347 return; 348 } 349 } 350 351 if (runflags & T_DELETE) { 352 if (tag_del(cf) == 0) { 353 if (verbosity > 0) 354 cvs_printf("D %s\n", cf->file_path); 355 } 356 return; 357 } 358 359 switch (cf->file_status) { 360 case FILE_ADDED: 361 if (verbosity > 1) { 362 cvs_log(LP_NOTICE, 363 "couldn't tag added but un-commited file `%s'", 364 cf->file_path); 365 } 366 break; 367 case FILE_REMOVED: 368 if (verbosity > 1) { 369 cvs_log(LP_NOTICE, 370 "skipping removed but un-commited file `%s'", 371 cf->file_path); 372 } 373 break; 374 case FILE_CHECKOUT: 375 case FILE_MODIFIED: 376 case FILE_PATCH: 377 case FILE_UPTODATE: 378 if (tag_add(cf) == 0) { 379 if (verbosity > 0) 380 cvs_printf("T %s\n", cf->file_path); 381 cvs_history_add(CVS_HISTORY_TAG, cf, tag_name); 382 } 383 break; 384 default: 385 break; 386 } 387 } 388 389 static int 390 tag_del(struct cvs_file *cf) 391 { 392 if (cf->file_rcs == NULL) 393 return (-1); 394 395 if (cvs_noexec == 1) 396 return (0); 397 398 return (rcs_sym_remove(cf->file_rcs, tag_name)); 399 } 400 401 static int 402 tag_add(struct cvs_file *cf) 403 { 404 int ret; 405 char revbuf[CVS_REV_BUFSZ], trevbuf[CVS_REV_BUFSZ]; 406 RCSNUM *srev, *trev; 407 struct rcs_sym *sym; 408 409 if (cf->file_rcs == NULL) { 410 if (verbosity > 1) 411 cvs_log(LP_NOTICE, "cannot find revision " 412 "control file for `%s'", cf->file_name); 413 return (-1); 414 } 415 416 if (cvs_cmdop == CVS_OP_TAG) { 417 if (cf->file_ent == NULL) 418 return (-1); 419 srev = cf->file_ent->ce_rev; 420 } else 421 srev = cf->file_rcsrev; 422 423 if (cvs_noexec == 1) 424 return (0); 425 426 (void)rcsnum_tostr(srev, revbuf, sizeof(revbuf)); 427 428 trev = rcs_sym_getrev(cf->file_rcs, tag_name); 429 if (trev != NULL) { 430 if (rcsnum_cmp(srev, trev, 0) == 0) { 431 rcsnum_free(trev); 432 return (-1); 433 } 434 (void)rcsnum_tostr(trev, trevbuf, sizeof(trevbuf)); 435 rcsnum_free(trev); 436 437 if (!(runflags & T_FORCE_MOVE)) { 438 cvs_printf("W %s : %s ", cf->file_path, tag_name); 439 cvs_printf("already exists on version %s", trevbuf); 440 cvs_printf(" : NOT MOVING tag to version %s\n", revbuf); 441 442 return (-1); 443 } else { 444 sym = rcs_sym_get(cf->file_rcs, tag_name); 445 rcsnum_cpy(srev, sym->rs_num, 0); 446 cf->file_rcs->rf_flags &= ~RCS_SYNCED; 447 448 return (0); 449 } 450 } 451 452 if (runflags & T_BRANCH) { 453 if ((trev = rcs_branch_new(cf->file_rcs, srev)) == NULL) 454 fatal("Cannot create a new branch"); 455 } else { 456 trev = rcsnum_alloc(); 457 rcsnum_cpy(srev, trev, 0); 458 } 459 460 if ((ret = rcs_sym_add(cf->file_rcs, tag_name, trev)) != 0) { 461 if (ret != 1) { 462 cvs_log(LP_NOTICE, 463 "failed to set tag %s to revision %s in %s", 464 tag_name, revbuf, cf->file_rcs->rf_path); 465 } 466 rcsnum_free(trev); 467 return (-1); 468 } 469 470 rcsnum_free(trev); 471 return (0); 472 } 473