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