1 /* $OpenBSD: edit.c,v 1.31 2007/01/25 18:56:33 otto Exp $ */ 2 /* 3 * Copyright (c) 2006, 2007 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 E_COMMIT 0x01 25 #define E_EDIT 0x02 26 #define E_UNEDIT 0x04 27 #define E_ALL (E_EDIT|E_COMMIT|E_UNEDIT) 28 29 #define BASE_ADD 0x01 30 #define BASE_GET 0x02 31 #define BASE_REMOVE 0x04 32 33 static void cvs_edit_local(struct cvs_file *); 34 static void cvs_editors_local(struct cvs_file *); 35 static void cvs_unedit_local(struct cvs_file *); 36 37 static RCSNUM *cvs_base_handle(struct cvs_file *, int); 38 39 static int edit_aflags = 0; 40 41 struct cvs_cmd cvs_cmd_edit = { 42 CVS_OP_EDIT, 0, "edit", 43 { }, 44 "Get ready to edit a watched file", 45 "[-lR] [-a action] [file ...]", 46 "a:lR", 47 NULL, 48 cvs_edit 49 }; 50 51 struct cvs_cmd cvs_cmd_editors = { 52 CVS_OP_EDITORS, 0, "editors", 53 { }, 54 "See who is editing a watched file", 55 "[-lR] [file ...]", 56 "lR", 57 NULL, 58 cvs_editors 59 }; 60 61 struct cvs_cmd cvs_cmd_unedit = { 62 CVS_OP_UNEDIT, 0, "unedit", 63 { }, 64 "Undo an edit command", 65 "[-lR] [file ...]", 66 "lR", 67 NULL, 68 cvs_unedit 69 }; 70 71 int 72 cvs_edit(int argc, char **argv) 73 { 74 int ch; 75 int flags; 76 struct cvs_recursion cr; 77 78 flags = CR_RECURSE_DIRS; 79 80 while ((ch = getopt(argc, argv, cvs_cmd_edit.cmd_opts)) != -1) { 81 switch (ch) { 82 case 'a': 83 if (strcmp(optarg, "edit") == 0) 84 edit_aflags |= E_EDIT; 85 else if (strcmp(optarg, "unedit") == 0) 86 edit_aflags |= E_UNEDIT; 87 else if (strcmp(optarg, "commit") == 0) 88 edit_aflags |= E_COMMIT; 89 else if (strcmp(optarg, "all") == 0) 90 edit_aflags |= E_ALL; 91 else if (strcmp(optarg, "none") == 0) 92 edit_aflags &= ~E_ALL; 93 else 94 fatal("%s", cvs_cmd_edit.cmd_synopsis); 95 break; 96 case 'l': 97 flags &= ~CR_RECURSE_DIRS; 98 break; 99 case 'R': 100 break; 101 default: 102 fatal("%s", cvs_cmd_edit.cmd_synopsis); 103 } 104 } 105 106 argc -= optind; 107 argv += optind; 108 109 if (argc == 0) 110 fatal("%s", cvs_cmd_edit.cmd_synopsis); 111 112 if (edit_aflags == 0) 113 edit_aflags |= E_ALL; 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 (!(flags & CR_RECURSE_DIRS)) 123 cvs_client_send_request("Argument -l"); 124 } else { 125 cr.fileproc = cvs_edit_local; 126 } 127 128 cr.flags = flags; 129 130 cvs_file_run(argc, argv, &cr); 131 132 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 133 cvs_client_send_files(argv, argc); 134 cvs_client_senddir("."); 135 cvs_client_send_request("edit"); 136 cvs_client_get_responses(); 137 } 138 139 return (0); 140 } 141 142 int 143 cvs_editors(int argc, char **argv) 144 { 145 int ch; 146 int flags; 147 struct cvs_recursion cr; 148 149 flags = CR_RECURSE_DIRS; 150 151 while ((ch = getopt(argc, argv, cvs_cmd_editors.cmd_opts)) != -1) { 152 switch (ch) { 153 case 'l': 154 flags &= ~CR_RECURSE_DIRS; 155 break; 156 case 'R': 157 break; 158 default: 159 fatal("%s", cvs_cmd_editors.cmd_synopsis); 160 } 161 } 162 163 argc -= optind; 164 argv += optind; 165 166 if (argc == 0) 167 fatal("%s", cvs_cmd_editors.cmd_synopsis); 168 169 cr.enterdir = NULL; 170 cr.leavedir = NULL; 171 172 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 173 cvs_client_connect_to_server(); 174 cr.fileproc = cvs_client_sendfile; 175 176 if (!(flags & CR_RECURSE_DIRS)) 177 cvs_client_send_request("Argument -l"); 178 } else { 179 cr.fileproc = cvs_editors_local; 180 } 181 182 cr.flags = flags; 183 184 cvs_file_run(argc, argv, &cr); 185 186 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 187 cvs_client_send_files(argv, argc); 188 cvs_client_senddir("."); 189 cvs_client_send_request("editors"); 190 cvs_client_get_responses(); 191 } 192 193 return (0); 194 } 195 196 int 197 cvs_unedit(int argc, char **argv) 198 { 199 int ch; 200 int flags; 201 struct cvs_recursion cr; 202 203 flags = CR_RECURSE_DIRS; 204 205 while ((ch = getopt(argc, argv, cvs_cmd_unedit.cmd_opts)) != -1) { 206 switch (ch) { 207 case 'l': 208 flags &= ~CR_RECURSE_DIRS; 209 break; 210 case 'R': 211 break; 212 default: 213 fatal("%s", cvs_cmd_unedit.cmd_synopsis); 214 } 215 } 216 217 argc -= optind; 218 argv += optind; 219 220 if (argc == 0) 221 fatal("%s", cvs_cmd_unedit.cmd_synopsis); 222 223 cr.enterdir = NULL; 224 cr.leavedir = NULL; 225 226 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 227 cvs_client_connect_to_server(); 228 cr.fileproc = cvs_client_sendfile; 229 230 if (!(flags & CR_RECURSE_DIRS)) 231 cvs_client_send_request("Argument -l"); 232 } else { 233 cr.fileproc = cvs_unedit_local; 234 } 235 236 cr.flags = flags; 237 238 cvs_file_run(argc, argv, &cr); 239 240 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 241 cvs_client_send_files(argv, argc); 242 cvs_client_senddir("."); 243 cvs_client_send_request("unedit"); 244 cvs_client_get_responses(); 245 } 246 247 return (0); 248 } 249 250 static void 251 cvs_edit_local(struct cvs_file *cf) 252 { 253 FILE *fp; 254 struct tm *t; 255 time_t now; 256 char bfpath[MAXPATHLEN], timebuf[64], thishost[MAXHOSTNAMELEN]; 257 char wdir[MAXPATHLEN]; 258 259 if (cvs_noexec == 1) 260 return; 261 262 cvs_log(LP_TRACE, "cvs_edit_local(%s)", cf->file_path); 263 264 cvs_file_classify(cf, NULL, 0); 265 266 if ((fp = fopen(CVS_PATH_NOTIFY, "a")) == NULL) 267 fatal("cvs_edit_local: fopen: `%s': %s", 268 CVS_PATH_NOTIFY, strerror(errno)); 269 270 (void)time(&now); 271 if ((t = gmtime(&now)) == NULL) 272 fatal("gmtime failed"); 273 274 asctime_r(t, timebuf); 275 if (timebuf[strlen(timebuf) - 1] == '\n') 276 timebuf[strlen(timebuf) - 1] = '\0'; 277 278 if (gethostname(thishost, sizeof(thishost)) == -1) 279 fatal("gethostname failed"); 280 281 if (getcwd(wdir, sizeof(wdir)) == NULL) 282 fatal("getcwd failed"); 283 284 (void)fprintf(fp, "E%s\t%s GMT\t%s\t%s\t", 285 cf->file_name, timebuf, thishost, wdir); 286 287 if (edit_aflags & E_EDIT) 288 (void)fprintf(fp, "E"); 289 if (edit_aflags & E_UNEDIT) 290 (void)fprintf(fp, "U"); 291 if (edit_aflags & E_COMMIT) 292 (void)fprintf(fp, "C"); 293 294 (void)fprintf(fp, "\n"); 295 296 (void)fclose(fp); 297 298 if (fchmod(cf->fd, 0644) == -1) 299 fatal("cvs_edit_local: fchmod %s", strerror(errno)); 300 301 if (cvs_path_cat(CVS_PATH_BASEDIR, cf->file_name, bfpath, 302 MAXPATHLEN) >= MAXPATHLEN) 303 fatal("cvs_edit_local: truncation"); 304 305 if (mkdir(CVS_PATH_BASEDIR, 0755) == -1 && errno != EEXIST) 306 fatal("cvs_edit_local: `%s': %s", CVS_PATH_BASEDIR, 307 strerror(errno)); 308 309 if (cvs_file_copy(cf->file_path, bfpath) == -1) 310 fatal("cvs_edit_local: cvs_file_copy failed"); 311 312 (void)cvs_base_handle(cf, BASE_ADD); 313 } 314 315 static void 316 cvs_editors_local(struct cvs_file *cf) 317 { 318 } 319 320 static void 321 cvs_unedit_local(struct cvs_file *cf) 322 { 323 FILE *fp; 324 struct stat st; 325 struct tm *t; 326 time_t now; 327 char bfpath[MAXPATHLEN], timebuf[64], thishost[MAXHOSTNAMELEN]; 328 char wdir[MAXPATHLEN]; 329 RCSNUM *ba_rev; 330 331 if (cvs_noexec == 1) 332 return; 333 334 cvs_log(LP_TRACE, "cvs_unedit_local(%s)", cf->file_path); 335 336 cvs_file_classify(cf, NULL, 0); 337 338 if (cvs_path_cat(CVS_PATH_BASEDIR, cf->file_name, bfpath, 339 MAXPATHLEN) >= MAXPATHLEN) 340 fatal("cvs_unedit_local: truncation"); 341 342 if (stat(bfpath, &st) == -1) 343 return; 344 345 if (cvs_file_cmp(cf->file_path, bfpath) != 0) { 346 cvs_printf("%s has been modified; revert changes? ", 347 cf->file_name); 348 349 if (cvs_yesno() == -1) 350 return; 351 } 352 353 cvs_rename(bfpath, cf->file_path); 354 355 if ((fp = fopen(CVS_PATH_NOTIFY, "a")) == NULL) 356 fatal("cvs_unedit_local: fopen: `%s': %s", 357 CVS_PATH_NOTIFY, strerror(errno)); 358 359 (void)time(&now); 360 if ((t = gmtime(&now)) == NULL) 361 fatal("gmtime failed"); 362 363 asctime_r(t, timebuf); 364 if (timebuf[strlen(timebuf) - 1] == '\n') 365 timebuf[strlen(timebuf) - 1] = '\0'; 366 367 if (gethostname(thishost, sizeof(thishost)) == -1) 368 fatal("gethostname failed"); 369 370 if (getcwd(wdir, sizeof(wdir)) == NULL) 371 fatal("getcwd failed"); 372 373 (void)fprintf(fp, "U%s\t%s GMT\t%s\t%s\t\n", 374 cf->file_name, timebuf, thishost, wdir); 375 376 (void)fclose(fp); 377 378 if ((ba_rev = cvs_base_handle(cf, BASE_GET)) == NULL) { 379 cvs_log(LP_ERR, "%s not mentioned in %s", 380 cf->file_name, CVS_PATH_BASEREV); 381 return; 382 } 383 384 if (cf->file_ent != NULL) { 385 CVSENTRIES *entlist; 386 struct cvs_ent *ent; 387 char *entry, rbuf[16]; 388 389 entlist = cvs_ent_open(cf->file_wd); 390 391 if ((ent = cvs_ent_get(entlist, cf->file_name)) == NULL) 392 fatal("cvs_unedit_local: cvs_ent_get failed"); 393 394 (void)rcsnum_tostr(ba_rev, rbuf, sizeof(rbuf)); 395 396 memset(timebuf, 0, sizeof(timebuf)); 397 ctime_r(&cf->file_ent->ce_mtime, timebuf); 398 if (timebuf[strlen(timebuf) - 1] == '\n') 399 timebuf[strlen(timebuf) - 1] = '\0'; 400 401 (void)xasprintf(&entry, "/%s/%s/%s/%s/%s", 402 cf->file_name, rbuf, timebuf, 403 (cf->file_ent->ce_tag) ? cf->file_ent->ce_tag : "", 404 (cf->file_ent->ce_opts) ? cf->file_ent->ce_opts : ""); 405 406 cvs_ent_add(entlist, entry); 407 408 cvs_ent_free(ent); 409 cvs_ent_close(entlist, ENT_SYNC); 410 411 xfree(entry); 412 } 413 414 rcsnum_free(ba_rev); 415 416 (void)cvs_base_handle(cf, BASE_REMOVE); 417 418 if (fchmod(cf->fd, 0644) == -1) 419 fatal("cvs_unedit_local: fchmod %s", strerror(errno)); 420 } 421 422 static RCSNUM * 423 cvs_base_handle(struct cvs_file *cf, int flags) 424 { 425 FILE *fp, *tfp; 426 RCSNUM *ba_rev; 427 size_t len; 428 int i; 429 char *dp, *sp; 430 char buf[MAXPATHLEN], *fields[2], rbuf[16]; 431 432 cvs_log(LP_TRACE, "cvs_base_handle(%s)", cf->file_path); 433 434 tfp = NULL; 435 ba_rev = NULL; 436 437 if (((fp = fopen(CVS_PATH_BASEREV, "r")) == NULL) && errno != ENOENT) { 438 cvs_log(LP_ERRNO, "%s", CVS_PATH_BASEREV); 439 goto out; 440 } 441 442 if (flags & (BASE_ADD|BASE_REMOVE)) { 443 if ((tfp = fopen(CVS_PATH_BASEREVTMP, "w")) == NULL) { 444 cvs_log(LP_ERRNO, "%s", CVS_PATH_BASEREVTMP); 445 goto out; 446 } 447 } 448 449 if (fp != NULL) { 450 while(fgets(buf, sizeof(buf), fp)) { 451 len = strlen(buf); 452 if (len > 0 && buf[len - 1] == '\n') 453 buf[len - 1] = '\0'; 454 455 if (buf[0] != 'B') 456 continue; 457 458 sp = buf + 1; 459 i = 0; 460 do { 461 if ((dp = strchr(sp, '/')) != NULL) 462 *(dp++) = '\0'; 463 fields[i++] = sp; 464 sp = dp; 465 } while (dp != NULL && i < 2); 466 467 if (cvs_file_cmpname(fields[0], cf->file_path) == 0) { 468 if (flags & BASE_GET) { 469 ba_rev = rcsnum_parse(fields[1]); 470 if (ba_rev == NULL) 471 fatal("cvs_base_handle: " 472 "rcsnum_parse"); 473 goto got_rev; 474 } 475 } else { 476 if (flags & (BASE_ADD|BASE_REMOVE)) 477 (void)fprintf(tfp, "%s\n", buf); 478 } 479 } 480 } 481 482 got_rev: 483 if (flags & (BASE_ADD)) { 484 (void)rcsnum_tostr(cf->file_ent->ce_rev, rbuf, sizeof(rbuf)); 485 (void)fprintf(tfp, "B%s/%s/\n", cf->file_path, rbuf); 486 } 487 488 out: 489 if (fp != NULL) 490 (void)fclose(fp); 491 492 if (tfp != NULL) { 493 (void)fclose(tfp); 494 (void)cvs_rename(CVS_PATH_BASEREVTMP, CVS_PATH_BASEREV); 495 } 496 497 return (ba_rev); 498 } 499