1 /* $OpenBSD: tag.c,v 1.73 2008/03/18 13:00:12 reyk 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_local(struct cvs_file *); 31 32 static int tag_del(struct cvs_file *); 33 static int tag_add(struct cvs_file *); 34 35 static int runflags = 0; 36 static char *tag = NULL; 37 static char *tag_date = NULL; 38 static char *tag_name = NULL; 39 static char *tag_oldname = NULL; 40 41 struct cvs_cmd cvs_cmd_rtag = { 42 CVS_OP_RTAG, CVS_LOCK_REPO, "rtag", 43 { "rt", "rfreeze" }, 44 "Add a symbolic tag to a module", 45 "[-bcdFflR] [-D date | -r rev] symbolic_tag module ...", 46 "bcD:dFflRr:", 47 NULL, 48 cvs_tag 49 }; 50 51 struct cvs_cmd cvs_cmd_tag = { 52 CVS_OP_TAG, CVS_USE_WDIR | CVS_LOCK_REPO, "tag", 53 { "ta", "freeze" }, 54 "Add a symbolic tag to checked out version of files", 55 "[-bcdFflR] [-D date | -r rev] symbolic_tag [file ...]", 56 "bcD:dFflRr:", 57 NULL, 58 cvs_tag 59 }; 60 61 int 62 cvs_tag(int argc, char **argv) 63 { 64 int ch, flags, i; 65 char *arg = "."; 66 struct cvs_recursion cr; 67 68 flags = CR_RECURSE_DIRS; 69 70 while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_TAG ? 71 cvs_cmd_tag.cmd_opts : cvs_cmd_rtag.cmd_opts)) != -1) { 72 switch (ch) { 73 case 'b': 74 runflags |= T_BRANCH; 75 break; 76 case 'c': 77 runflags |= T_CHECK_UPTODATE; 78 break; 79 case 'D': 80 tag_date = optarg; 81 break; 82 case 'd': 83 runflags |= T_DELETE; 84 break; 85 case 'F': 86 runflags |= T_FORCE_MOVE; 87 break; 88 case 'l': 89 flags &= ~CR_RECURSE_DIRS; 90 break; 91 case 'R': 92 flags |= CR_RECURSE_DIRS; 93 break; 94 case 'r': 95 tag_oldname = optarg; 96 break; 97 default: 98 fatal("%s", cvs_cmdop == CVS_OP_TAG ? 99 cvs_cmd_tag.cmd_synopsis : 100 cvs_cmd_rtag.cmd_synopsis); 101 } 102 } 103 104 argc -= optind; 105 argv += optind; 106 107 if (cvs_cmdop == CVS_OP_RTAG) { 108 flags |= CR_REPO; 109 110 if (argc < 2) 111 fatal("%s", cvs_cmd_rtag.cmd_synopsis); 112 113 for (i = 1; i < argc; i++) { 114 if (argv[i][0] == '/') 115 fatal("Absolute path name is invalid: %s", 116 argv[i]); 117 } 118 } else if (cvs_cmdop == CVS_OP_TAG && argc == 0) 119 fatal("%s", cvs_cmd_tag.cmd_synopsis); 120 121 tag_name = argv[0]; 122 argc--; 123 argv++; 124 125 if (!rcs_sym_check(tag_name)) 126 fatal("tag `%s' must not contain the characters `%s'", 127 tag_name, RCS_SYM_INVALCHAR); 128 129 if (tag_oldname != NULL) { 130 if (runflags & T_DELETE) 131 tag_oldname = NULL; 132 else 133 tag = tag_oldname; 134 } 135 136 if (tag_date != NULL) { 137 if (runflags & T_DELETE) 138 tag_date = NULL; 139 else 140 tag = tag_date; 141 } 142 143 if (tag_oldname != NULL && tag_date != NULL) 144 fatal("-r and -D options are mutually exclusive"); 145 146 cr.enterdir = NULL; 147 cr.leavedir = NULL; 148 149 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 150 cvs_client_connect_to_server(); 151 cr.fileproc = cvs_client_sendfile; 152 153 if (runflags & T_BRANCH) 154 cvs_client_send_request("Argument -b"); 155 156 if (runflags & T_CHECK_UPTODATE) 157 cvs_client_send_request("Argument -c"); 158 159 if (runflags & T_DELETE) 160 cvs_client_send_request("Argument -d"); 161 162 if (runflags & T_FORCE_MOVE) 163 cvs_client_send_request("Argument -F"); 164 165 if (!(flags & CR_RECURSE_DIRS)) 166 cvs_client_send_request("Argument -l"); 167 168 if (tag_oldname != NULL) 169 cvs_client_send_request("Argument -r%s", tag_oldname); 170 171 cvs_client_send_request("Argument %s", tag_name); 172 } else { 173 if (cvs_cmdop == CVS_OP_RTAG && 174 chdir(current_cvsroot->cr_dir) == -1) 175 fatal("cvs_tag: %s", strerror(errno)); 176 177 cr.fileproc = cvs_tag_local; 178 } 179 180 cr.flags = flags; 181 182 if (cvs_cmdop == CVS_OP_TAG || 183 current_cvsroot->cr_method == CVS_METHOD_LOCAL) { 184 if (argc > 0) 185 cvs_file_run(argc, argv, &cr); 186 else 187 cvs_file_run(1, &arg, &cr); 188 } 189 190 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 191 cvs_client_send_files(argv, argc); 192 cvs_client_senddir("."); 193 194 cvs_client_send_request((cvs_cmdop == CVS_OP_RTAG) ? 195 "rtag" : "tag"); 196 197 cvs_client_get_responses(); 198 } 199 200 return (0); 201 } 202 203 void 204 cvs_tag_local(struct cvs_file *cf) 205 { 206 cvs_log(LP_TRACE, "cvs_tag_local(%s)", cf->file_path); 207 208 cvs_file_classify(cf, tag); 209 210 if (cf->file_type == CVS_DIR) { 211 if (verbosity > 1) { 212 cvs_log(LP_NOTICE, "%s %s", 213 (runflags & T_DELETE) ? "Untagging" : "Tagging", 214 cf->file_path); 215 } 216 return; 217 } 218 219 if (runflags & T_CHECK_UPTODATE) { 220 if (cf->file_status != FILE_UPTODATE && 221 cf->file_status != FILE_CHECKOUT && 222 cf->file_status != FILE_PATCH) { 223 cvs_log(LP_NOTICE, 224 "%s is locally modified", cf->file_path); 225 return; 226 } 227 } 228 229 if (runflags & T_DELETE) { 230 if (tag_del(cf) == 0) { 231 if (verbosity > 0) 232 cvs_printf("D %s\n", cf->file_path); 233 234 rcs_write(cf->file_rcs); 235 } 236 return; 237 } 238 239 switch (cf->file_status) { 240 case FILE_ADDED: 241 if (verbosity > 1) { 242 cvs_log(LP_NOTICE, 243 "couldn't tag added but un-commited file `%s'", 244 cf->file_path); 245 } 246 return; 247 case FILE_REMOVED: 248 if (verbosity > 1) { 249 cvs_log(LP_NOTICE, 250 "skipping removed but un-commited file `%s'", 251 cf->file_path); 252 } 253 return; 254 case FILE_CHECKOUT: 255 case FILE_MODIFIED: 256 case FILE_PATCH: 257 case FILE_UPTODATE: 258 if (tag_add(cf) == 0) { 259 if (verbosity > 0) 260 cvs_printf("T %s\n", cf->file_path); 261 262 rcs_write(cf->file_rcs); 263 cvs_history_add(CVS_HISTORY_TAG, cf, tag_name); 264 } 265 break; 266 default: 267 break; 268 } 269 } 270 271 static int 272 tag_del(struct cvs_file *cf) 273 { 274 if (cf->file_rcs == NULL) 275 return (-1); 276 277 if (cvs_noexec == 1) 278 return (0); 279 280 return (rcs_sym_remove(cf->file_rcs, tag_name)); 281 } 282 283 static int 284 tag_add(struct cvs_file *cf) 285 { 286 int ret; 287 char revbuf[CVS_REV_BUFSZ], trevbuf[CVS_REV_BUFSZ]; 288 RCSNUM *srev, *trev; 289 struct rcs_sym *sym; 290 291 if (cf->file_rcs == NULL) { 292 if (verbosity > 1) 293 cvs_log(LP_NOTICE, "cannot find revision " 294 "control file for `%s'", cf->file_name); 295 return (-1); 296 } 297 298 if (cvs_cmdop == CVS_OP_TAG) { 299 if (cf->file_ent == NULL) 300 return (-1); 301 srev = cf->file_ent->ce_rev; 302 } else 303 srev = cf->file_rcsrev; 304 305 if (cvs_noexec == 1) 306 return (0); 307 308 (void)rcsnum_tostr(srev, revbuf, sizeof(revbuf)); 309 310 trev = rcs_sym_getrev(cf->file_rcs, tag_name); 311 if (trev != NULL) { 312 if (rcsnum_cmp(srev, trev, 0) == 0) { 313 rcsnum_free(trev); 314 return (-1); 315 } 316 (void)rcsnum_tostr(trev, trevbuf, sizeof(trevbuf)); 317 rcsnum_free(trev); 318 319 if (!(runflags & T_FORCE_MOVE)) { 320 cvs_printf("W %s : %s ", cf->file_path, tag_name); 321 cvs_printf("already exists on version %s", trevbuf); 322 cvs_printf(" : NOT MOVING tag to version %s\n", revbuf); 323 324 return (-1); 325 } else { 326 sym = rcs_sym_get(cf->file_rcs, tag_name); 327 rcsnum_cpy(srev, sym->rs_num, 0); 328 cf->file_rcs->rf_flags &= ~RCS_SYNCED; 329 330 return (0); 331 } 332 } 333 334 if (runflags & T_BRANCH) { 335 if ((trev = rcsnum_new_branch(srev)) == NULL) 336 fatal("Cannot create a new branch"); 337 338 for (;;) { 339 TAILQ_FOREACH(sym, &(cf->file_rcs->rf_symbols), rs_list) 340 if (!rcsnum_cmp(sym->rs_num, trev, 0)) 341 break; 342 343 if (sym != NULL) { 344 if (rcsnum_inc(trev) == NULL) 345 fatal("New revision too high"); 346 if (rcsnum_inc(trev) == NULL) 347 fatal("New revision too high"); 348 } else 349 break; 350 } 351 } else { 352 trev = rcsnum_alloc(); 353 rcsnum_cpy(srev, trev, 0); 354 } 355 356 if ((ret = rcs_sym_add(cf->file_rcs, tag_name, trev)) != 0) { 357 if (ret != 1) { 358 cvs_log(LP_NOTICE, 359 "failed to set tag %s to revision %s in %s", 360 tag_name, revbuf, cf->file_rcs->rf_path); 361 } 362 rcsnum_free(trev); 363 return (-1); 364 } 365 366 rcsnum_free(trev); 367 return (0); 368 } 369