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