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