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