1 /* $OpenBSD: tag.c,v 1.85 2016/10/27 07:12:02 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 <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 cr.flags = flags; 188 189 cvs_get_repository_name(".", repo, PATH_MAX); 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 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 free(fi->file_path); 312 free(fi->crevstr); 313 free(fi->nrevstr); 314 free(fi->tag_new); 315 free(fi->tag_old); 316 free(rev); 317 free(fi); 318 } 319 320 void 321 cvs_tag_local(struct cvs_file *cf) 322 { 323 cvs_log(LP_TRACE, "cvs_tag_local(%s)", cf->file_path); 324 325 cvs_file_classify(cf, tag); 326 327 if (cf->file_type == CVS_DIR) { 328 if (verbosity > 1) { 329 cvs_log(LP_NOTICE, "%s %s", 330 (runflags & T_DELETE) ? "Untagging" : "Tagging", 331 cf->file_path); 332 } 333 return; 334 } 335 336 if (runflags & T_CHECK_UPTODATE) { 337 if (cf->file_status != FILE_UPTODATE && 338 cf->file_status != FILE_CHECKOUT && 339 cf->file_status != FILE_PATCH) { 340 cvs_log(LP_NOTICE, 341 "%s is locally modified", cf->file_path); 342 return; 343 } 344 } 345 346 if (runflags & T_DELETE) { 347 if (tag_del(cf) == 0) { 348 if (verbosity > 0) 349 cvs_printf("D %s\n", cf->file_path); 350 } 351 return; 352 } 353 354 switch (cf->file_status) { 355 case FILE_ADDED: 356 if (verbosity > 1) { 357 cvs_log(LP_NOTICE, 358 "couldn't tag added but un-committed file `%s'", 359 cf->file_path); 360 } 361 break; 362 case FILE_REMOVED: 363 if (verbosity > 1) { 364 cvs_log(LP_NOTICE, 365 "skipping removed but un-committed file `%s'", 366 cf->file_path); 367 } 368 break; 369 case FILE_CHECKOUT: 370 case FILE_MODIFIED: 371 case FILE_PATCH: 372 case FILE_UPTODATE: 373 if (tag_add(cf) == 0) { 374 if (verbosity > 0) 375 cvs_printf("T %s\n", cf->file_path); 376 cvs_history_add(CVS_HISTORY_TAG, cf, tag_name); 377 } 378 break; 379 default: 380 break; 381 } 382 } 383 384 static int 385 tag_del(struct cvs_file *cf) 386 { 387 if (cf->file_rcs == NULL) 388 return (-1); 389 390 if (cvs_noexec == 1) 391 return (0); 392 393 return (rcs_sym_remove(cf->file_rcs, tag_name)); 394 } 395 396 static int 397 tag_add(struct cvs_file *cf) 398 { 399 int ret; 400 char revbuf[CVS_REV_BUFSZ], trevbuf[CVS_REV_BUFSZ]; 401 RCSNUM *srev, *trev; 402 struct rcs_sym *sym; 403 404 if (cf->file_rcs == NULL) { 405 if (verbosity > 1) 406 cvs_log(LP_NOTICE, "cannot find revision " 407 "control file for `%s'", cf->file_name); 408 return (-1); 409 } 410 411 if (cvs_cmdop == CVS_OP_TAG) { 412 if (cf->file_ent == NULL) 413 return (-1); 414 srev = cf->file_ent->ce_rev; 415 } else 416 srev = cf->file_rcsrev; 417 418 if (cvs_noexec == 1) 419 return (0); 420 421 (void)rcsnum_tostr(srev, revbuf, sizeof(revbuf)); 422 423 trev = rcs_sym_getrev(cf->file_rcs, tag_name); 424 if (trev != NULL) { 425 if (rcsnum_cmp(srev, trev, 0) == 0) { 426 free(trev); 427 return (-1); 428 } 429 (void)rcsnum_tostr(trev, trevbuf, sizeof(trevbuf)); 430 free(trev); 431 432 if (!(runflags & T_FORCE_MOVE)) { 433 cvs_printf("W %s : %s ", cf->file_path, tag_name); 434 cvs_printf("already exists on version %s", trevbuf); 435 cvs_printf(" : NOT MOVING tag to version %s\n", revbuf); 436 437 return (-1); 438 } else { 439 sym = rcs_sym_get(cf->file_rcs, tag_name); 440 rcsnum_cpy(srev, sym->rs_num, 0); 441 cf->file_rcs->rf_flags &= ~RCS_SYNCED; 442 443 return (0); 444 } 445 } 446 447 if (runflags & T_BRANCH) { 448 if ((trev = rcs_branch_new(cf->file_rcs, srev)) == NULL) 449 fatal("Cannot create a new branch"); 450 } else { 451 trev = rcsnum_alloc(); 452 rcsnum_cpy(srev, trev, 0); 453 } 454 455 if ((ret = rcs_sym_add(cf->file_rcs, tag_name, trev)) != 0) { 456 if (ret != 1) { 457 cvs_log(LP_NOTICE, 458 "failed to set tag %s to revision %s in %s", 459 tag_name, revbuf, cf->file_rcs->rf_path); 460 } 461 free(trev); 462 return (-1); 463 } 464 465 free(trev); 466 return (0); 467 } 468