1 /* $OpenBSD: tag.c,v 1.55 2007/02/22 06:42:09 otto 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 <unistd.h> 19 20 #include "cvs.h" 21 #include "remote.h" 22 23 #define T_CHECK_UPTODATE 0x01 24 #define T_DELETE 0x02 25 #define T_FORCE_MOVE 0x04 26 27 void cvs_tag_local(struct cvs_file *); 28 29 static int tag_del(struct cvs_file *); 30 static int tag_add(struct cvs_file *); 31 32 static int runflags = 0; 33 static char *tag = NULL; 34 static char *tag_date = NULL; 35 static char *tag_name = NULL; 36 static char *tag_oldname = NULL; 37 38 struct cvs_cmd cvs_cmd_tag = { 39 CVS_OP_TAG, 0, "tag", 40 { "ta", "freeze" }, 41 "Add a symbolic tag to checked out version of files", 42 "[-bcdFflR] [-D date | -r rev] tag [file ...]", 43 "bcD:dFflRr:", 44 NULL, 45 cvs_tag 46 }; 47 48 int 49 cvs_tag(int argc, char **argv) 50 { 51 int ch, flags; 52 char *arg = "."; 53 struct cvs_recursion cr; 54 55 flags = CR_RECURSE_DIRS; 56 57 while ((ch = getopt(argc, argv, cvs_cmd_tag.cmd_opts)) != -1) { 58 switch (ch) { 59 case 'c': 60 runflags |= T_CHECK_UPTODATE; 61 break; 62 case 'D': 63 tag_date = optarg; 64 break; 65 case 'd': 66 runflags |= T_DELETE; 67 break; 68 case 'F': 69 runflags |= T_FORCE_MOVE; 70 break; 71 case 'l': 72 flags &= ~CR_RECURSE_DIRS; 73 break; 74 case 'R': 75 break; 76 case 'r': 77 tag_oldname = optarg; 78 break; 79 default: 80 fatal("%s", cvs_cmd_tag.cmd_synopsis); 81 } 82 } 83 84 argc -= optind; 85 argv += optind; 86 87 if (argc == 0) 88 fatal("%s", cvs_cmd_tag.cmd_synopsis); 89 90 tag_name = argv[0]; 91 argc--; 92 argv++; 93 94 if (!rcs_sym_check(tag_name)) 95 fatal("tag `%s' must not contain the characters `%s'", 96 tag_name, RCS_SYM_INVALCHAR); 97 98 if (tag_oldname != NULL) { 99 if (runflags & T_DELETE) 100 tag_oldname = NULL; 101 else 102 tag = tag_oldname; 103 } 104 105 if (tag_date != NULL) { 106 if (runflags & T_DELETE) 107 tag_date = NULL; 108 else 109 tag = tag_date; 110 } 111 112 if (tag_oldname != NULL && tag_date != NULL) 113 fatal("-r and -D options are mutually exclusive"); 114 115 cr.enterdir = NULL; 116 cr.leavedir = NULL; 117 118 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 119 cvs_client_connect_to_server(); 120 cr.fileproc = cvs_client_sendfile; 121 122 if (runflags & T_CHECK_UPTODATE) 123 cvs_client_send_request("Argument -c"); 124 125 if (runflags & T_DELETE) 126 cvs_client_send_request("Argument -d"); 127 128 if (runflags & T_FORCE_MOVE) 129 cvs_client_send_request("Argument -F"); 130 131 if (!(flags & CR_RECURSE_DIRS)) 132 cvs_client_send_request("Argument -l"); 133 134 if (tag_oldname != NULL) 135 cvs_client_send_request("Argument -r%s", tag_oldname); 136 137 cvs_client_send_request("Argument %s", tag_name); 138 } else { 139 cr.fileproc = cvs_tag_local; 140 } 141 142 cr.flags = flags; 143 144 if (argc > 0) 145 cvs_file_run(argc, argv, &cr); 146 else 147 cvs_file_run(1, &arg, &cr); 148 149 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 150 cvs_client_send_files(argv, argc); 151 cvs_client_senddir("."); 152 cvs_client_send_request("tag"); 153 cvs_client_get_responses(); 154 } 155 156 return (0); 157 } 158 159 void 160 cvs_tag_local(struct cvs_file *cf) 161 { 162 cvs_log(LP_TRACE, "cvs_tag_local(%s)", cf->file_path); 163 164 if (cf->file_type == CVS_DIR) { 165 if (verbosity > 1) { 166 cvs_log(LP_NOTICE, "%s %s", 167 (runflags & T_DELETE) ? "Untagging" : "Tagging", 168 cf->file_path); 169 } 170 return; 171 } 172 173 cvs_file_classify(cf, tag); 174 175 if (runflags & T_CHECK_UPTODATE) { 176 if (cf->file_status != FILE_UPTODATE && 177 cf->file_status != FILE_CHECKOUT && 178 cf->file_status != FILE_PATCH) { 179 cvs_log(LP_NOTICE, 180 "%s is locally modified", cf->file_path); 181 return; 182 } 183 } 184 185 if (runflags & T_DELETE) { 186 if (tag_del(cf) == 0) { 187 if (verbosity > 0) 188 cvs_printf("D %s\n", cf->file_path); 189 190 rcs_write(cf->file_rcs); 191 } 192 return; 193 } 194 195 switch(cf->file_status) { 196 case FILE_ADDED: 197 if (verbosity > 1) { 198 cvs_log(LP_NOTICE, 199 "couldn't tag added but un-commited file `%s'", 200 cf->file_path); 201 } 202 return; 203 case FILE_REMOVED: 204 if (verbosity > 1) { 205 cvs_log(LP_NOTICE, 206 "skipping removed but un-commited file `%s'", 207 cf->file_path); 208 } 209 return; 210 case FILE_CHECKOUT: 211 case FILE_MODIFIED: 212 case FILE_UPTODATE: 213 if (tag_add(cf) == 0) { 214 if (verbosity > 0) 215 cvs_printf("T %s\n", cf->file_path); 216 217 rcs_write(cf->file_rcs); 218 } 219 break; 220 default: 221 break; 222 } 223 } 224 225 static int 226 tag_del(struct cvs_file *cf) 227 { 228 if (cf->file_rcs == NULL) 229 return (-1); 230 231 if (cvs_noexec == 1) 232 return (0); 233 234 return (rcs_sym_remove(cf->file_rcs, tag_name)); 235 } 236 237 static int 238 tag_add(struct cvs_file *cf) 239 { 240 char revbuf[16], trevbuf[16]; 241 RCSNUM *trev; 242 struct rcs_sym *sym; 243 244 if (cf->file_rcs == NULL) { 245 if (verbosity > 1) 246 cvs_log(LP_NOTICE, "cannot find revision " 247 "control file for `%s'", cf->file_name); 248 return (-1); 249 } 250 251 if (cvs_noexec == 1) 252 return (0); 253 254 trev = rcs_sym_getrev(cf->file_rcs, tag_name); 255 if (trev != NULL) { 256 if (rcsnum_cmp(cf->file_rcsrev, trev, 0) == 0) { 257 rcsnum_free(trev); 258 return (-1); 259 } 260 (void)rcsnum_tostr(trev, trevbuf, sizeof(trevbuf)); 261 262 if (!(runflags & T_FORCE_MOVE)) { 263 cvs_printf("W %s : %s ", cf->file_path, tag_name); 264 cvs_printf("already exists on version %s", trevbuf); 265 cvs_printf(" : NOT MOVING tag to version %s\n", revbuf); 266 267 return (-1); 268 } else if (runflags & T_FORCE_MOVE) { 269 sym = rcs_sym_get(cf->file_rcs, tag_name); 270 rcsnum_cpy(cf->file_rcsrev, sym->rs_num, 0); 271 cf->file_rcs->rf_flags &= ~RCS_SYNCED; 272 273 return (0); 274 } 275 } 276 277 if (rcs_sym_add(cf->file_rcs, tag_name, cf->file_rcsrev) == -1) { 278 if (rcs_errno != RCS_ERR_DUPENT) { 279 (void)rcsnum_tostr(cf->file_rcsrev, revbuf, 280 sizeof(revbuf)); 281 cvs_log(LP_NOTICE, 282 "failed to set tag %s to revision %s in %s", 283 tag_name, revbuf, cf->file_rcs->rf_path); 284 } 285 return (-1); 286 } 287 288 return (0); 289 } 290