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