1 /* $OpenBSD: dired.c,v 1.93 2019/07/11 18:20:18 lum Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* dired module for mg 2a 6 * by Robert A. Larson 7 */ 8 9 #include <sys/queue.h> 10 #include <sys/resource.h> 11 #include <sys/stat.h> 12 #include <sys/time.h> 13 #include <sys/types.h> 14 #include <sys/wait.h> 15 #include <ctype.h> 16 #include <err.h> 17 #include <errno.h> 18 #include <fcntl.h> 19 #include <limits.h> 20 #include <signal.h> 21 #include <stdarg.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 #include "def.h" 28 #include "funmap.h" 29 #include "kbd.h" 30 31 void dired_init(void); 32 static int dired(int, int); 33 static int d_otherwindow(int, int); 34 static int d_undel(int, int); 35 static int d_undelbak(int, int); 36 static int d_findfile(int, int); 37 static int d_ffotherwindow(int, int); 38 static int d_expunge(int, int); 39 static int d_copy(int, int); 40 static int d_del(int, int); 41 static int d_rename(int, int); 42 static int d_exec(int, struct buffer *, const char *, const char *, ...); 43 static int d_shell_command(int, int); 44 static int d_create_directory(int, int); 45 static int d_makename(struct line *, char *, size_t); 46 static int d_warpdot(struct line *, int *); 47 static int d_forwpage(int, int); 48 static int d_backpage(int, int); 49 static int d_forwline(int, int); 50 static int d_backline(int, int); 51 static int d_killbuffer_cmd(int, int); 52 static int d_refreshbuffer(int, int); 53 static int d_filevisitalt(int, int); 54 static int d_gotofile(int, int); 55 static void reaper(int); 56 static struct buffer *refreshbuffer(struct buffer *); 57 static int createlist(struct buffer *); 58 static void redelete(struct buffer *); 59 static char *findfname(struct line *, char *); 60 61 extern struct keymap_s helpmap, cXmap, metamap; 62 63 const char DDELCHAR = 'D'; 64 65 /* 66 * Structure which holds a linked list of file names marked for 67 * deletion. Used to maintain dired buffer 'state' between refreshes. 68 */ 69 struct delentry { 70 SLIST_ENTRY(delentry) entry; 71 char *fn; 72 }; 73 SLIST_HEAD(slisthead, delentry) delhead = SLIST_HEAD_INITIALIZER(delhead); 74 75 static PF dirednul[] = { 76 setmark, /* ^@ */ 77 gotobol, /* ^A */ 78 backchar, /* ^B */ 79 rescan, /* ^C */ 80 d_del, /* ^D */ 81 gotoeol, /* ^E */ 82 forwchar, /* ^F */ 83 ctrlg, /* ^G */ 84 NULL, /* ^H */ 85 }; 86 87 static PF diredcl[] = { 88 reposition, /* ^L */ 89 d_findfile, /* ^M */ 90 d_forwline, /* ^N */ 91 rescan, /* ^O */ 92 d_backline, /* ^P */ 93 rescan, /* ^Q */ 94 backisearch, /* ^R */ 95 forwisearch, /* ^S */ 96 rescan, /* ^T */ 97 universal_argument, /* ^U */ 98 d_forwpage, /* ^V */ 99 rescan, /* ^W */ 100 NULL /* ^X */ 101 }; 102 103 static PF diredcz[] = { 104 spawncli, /* ^Z */ 105 NULL, /* esc */ 106 rescan, /* ^\ */ 107 rescan, /* ^] */ 108 rescan, /* ^^ */ 109 rescan, /* ^_ */ 110 d_forwline, /* SP */ 111 d_shell_command, /* ! */ 112 rescan, /* " */ 113 rescan, /* # */ 114 rescan, /* $ */ 115 rescan, /* % */ 116 rescan, /* & */ 117 rescan, /* ' */ 118 rescan, /* ( */ 119 rescan, /* ) */ 120 rescan, /* * */ 121 d_create_directory /* + */ 122 }; 123 124 static PF direda[] = { 125 d_filevisitalt, /* a */ 126 rescan, /* b */ 127 d_copy, /* c */ 128 d_del, /* d */ 129 d_findfile, /* e */ 130 d_findfile, /* f */ 131 d_refreshbuffer, /* g */ 132 rescan, /* h */ 133 rescan, /* i */ 134 d_gotofile /* j */ 135 }; 136 137 static PF diredn[] = { 138 d_forwline, /* n */ 139 d_ffotherwindow, /* o */ 140 d_backline, /* p */ 141 d_killbuffer_cmd, /* q */ 142 d_rename, /* r */ 143 rescan, /* s */ 144 rescan, /* t */ 145 d_undel, /* u */ 146 rescan, /* v */ 147 rescan, /* w */ 148 d_expunge /* x */ 149 }; 150 151 static PF direddl[] = { 152 d_undelbak /* del */ 153 }; 154 155 static PF diredbp[] = { 156 d_backpage /* v */ 157 }; 158 159 static PF dirednull[] = { 160 NULL 161 }; 162 163 static struct KEYMAPE (1) d_backpagemap = { 164 1, 165 1, 166 rescan, 167 { 168 { 169 'v', 'v', diredbp, NULL 170 } 171 } 172 }; 173 174 static struct KEYMAPE (7) diredmap = { 175 7, 176 7, 177 rescan, 178 { 179 { 180 CCHR('@'), CCHR('H'), dirednul, (KEYMAP *) & helpmap 181 }, 182 { 183 CCHR('L'), CCHR('X'), diredcl, (KEYMAP *) & cXmap 184 }, 185 { 186 CCHR('['), CCHR('['), dirednull, (KEYMAP *) & 187 d_backpagemap 188 }, 189 { 190 CCHR('Z'), '+', diredcz, (KEYMAP *) & metamap 191 }, 192 { 193 'a', 'j', direda, NULL 194 }, 195 { 196 'n', 'x', diredn, NULL 197 }, 198 { 199 CCHR('?'), CCHR('?'), direddl, NULL 200 }, 201 } 202 }; 203 204 void 205 dired_init(void) 206 { 207 funmap_add(dired, "dired", 1); 208 funmap_add(d_create_directory, "dired-create-directory", 1); 209 funmap_add(d_copy, "dired-do-copy", 1); 210 funmap_add(d_expunge, "dired-do-flagged-delete", 0); 211 funmap_add(d_rename, "dired-do-rename", 1); 212 funmap_add(d_findfile, "dired-find-file", 1); 213 funmap_add(d_ffotherwindow, "dired-find-file-other-window", 1); 214 funmap_add(d_del, "dired-flag-file-deletion", 0); 215 funmap_add(d_gotofile, "dired-goto-file", 1); 216 funmap_add(d_forwline, "dired-next-line", 0); 217 funmap_add(d_otherwindow, "dired-other-window", 0); 218 funmap_add(d_backline, "dired-previous-line", 0); 219 funmap_add(d_refreshbuffer, "dired-revert", 0); 220 funmap_add(d_backpage, "dired-scroll-down", 0); 221 funmap_add(d_forwpage, "dired-scroll-up", 0); 222 funmap_add(d_undel, "dired-unmark", 0); 223 funmap_add(d_undelbak, "dired-unmark-backward", 0); 224 funmap_add(d_killbuffer_cmd, "quit-window", 0); 225 maps_add((KEYMAP *)&diredmap, "dired"); 226 dobindkey(fundamental_map, "dired", "^Xd"); 227 } 228 229 /* ARGSUSED */ 230 int 231 dired(int f, int n) 232 { 233 char dname[NFILEN], *bufp, *slash; 234 struct buffer *bp; 235 236 if (curbp->b_fname[0] != '\0') { 237 (void)strlcpy(dname, curbp->b_fname, sizeof(dname)); 238 if ((slash = strrchr(dname, '/')) != NULL) { 239 *(slash + 1) = '\0'; 240 } 241 } else { 242 if (getcwd(dname, sizeof(dname)) == NULL) 243 dname[0] = '\0'; 244 } 245 246 if ((bufp = eread("Dired: ", dname, NFILEN, 247 EFDEF | EFNEW | EFCR)) == NULL) 248 return (ABORT); 249 if (bufp[0] == '\0') 250 return (FALSE); 251 if ((bp = dired_(bufp)) == NULL) 252 return (FALSE); 253 254 curbp = bp; 255 return (showbuffer(bp, curwp, WFFULL | WFMODE)); 256 } 257 258 /* ARGSUSED */ 259 int 260 d_otherwindow(int f, int n) 261 { 262 char dname[NFILEN], *bufp, *slash; 263 struct buffer *bp; 264 struct mgwin *wp; 265 266 if (curbp->b_fname[0] != '\0') { 267 (void)strlcpy(dname, curbp->b_fname, sizeof(dname)); 268 if ((slash = strrchr(dname, '/')) != NULL) { 269 *(slash + 1) = '\0'; 270 } 271 } else { 272 if (getcwd(dname, sizeof(dname)) == NULL) 273 dname[0] = '\0'; 274 } 275 276 if ((bufp = eread("Dired other window: ", dname, NFILEN, 277 EFDEF | EFNEW | EFCR)) == NULL) 278 return (ABORT); 279 else if (bufp[0] == '\0') 280 return (FALSE); 281 if ((bp = dired_(bufp)) == NULL) 282 return (FALSE); 283 if ((wp = popbuf(bp, WNONE)) == NULL) 284 return (FALSE); 285 curbp = bp; 286 curwp = wp; 287 return (TRUE); 288 } 289 290 /* ARGSUSED */ 291 int 292 d_del(int f, int n) 293 { 294 if (n < 0) 295 return (FALSE); 296 while (n--) { 297 if (d_warpdot(curwp->w_dotp, &curwp->w_doto) == TRUE) { 298 lputc(curwp->w_dotp, 0, DDELCHAR); 299 curbp->b_flag |= BFDIREDDEL; 300 } 301 if (lforw(curwp->w_dotp) != curbp->b_headp) { 302 curwp->w_dotp = lforw(curwp->w_dotp); 303 curwp->w_dotline++; 304 } 305 } 306 curwp->w_rflag |= WFEDIT | WFMOVE; 307 return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); 308 } 309 310 /* ARGSUSED */ 311 int 312 d_undel(int f, int n) 313 { 314 if (n < 0) 315 return (d_undelbak(f, -n)); 316 while (n--) { 317 if (llength(curwp->w_dotp) > 0) 318 lputc(curwp->w_dotp, 0, ' '); 319 if (lforw(curwp->w_dotp) != curbp->b_headp) { 320 curwp->w_dotp = lforw(curwp->w_dotp); 321 curwp->w_dotline++; 322 } 323 } 324 curwp->w_rflag |= WFEDIT | WFMOVE; 325 return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); 326 } 327 328 /* ARGSUSED */ 329 int 330 d_undelbak(int f, int n) 331 { 332 if (n < 0) 333 return (d_undel(f, -n)); 334 while (n--) { 335 if (lback(curwp->w_dotp) != curbp->b_headp) { 336 curwp->w_dotp = lback(curwp->w_dotp); 337 curwp->w_dotline--; 338 } 339 if (llength(curwp->w_dotp) > 0) 340 lputc(curwp->w_dotp, 0, ' '); 341 } 342 curwp->w_rflag |= WFEDIT | WFMOVE; 343 return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); 344 } 345 346 /* ARGSUSED */ 347 int 348 d_findfile(int f, int n) 349 { 350 struct buffer *bp; 351 int s; 352 char fname[NFILEN]; 353 354 if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT) 355 return (FALSE); 356 if (s == TRUE) 357 bp = dired_(fname); 358 else 359 bp = findbuffer(fname); 360 if (bp == NULL) 361 return (FALSE); 362 curbp = bp; 363 if (showbuffer(bp, curwp, WFFULL) != TRUE) 364 return (FALSE); 365 if (bp->b_fname[0] != 0) 366 return (TRUE); 367 return (readin(fname)); 368 } 369 370 /* ARGSUSED */ 371 int 372 d_ffotherwindow(int f, int n) 373 { 374 char fname[NFILEN]; 375 int s; 376 struct buffer *bp; 377 struct mgwin *wp; 378 379 if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT) 380 return (FALSE); 381 if ((bp = (s ? dired_(fname) : findbuffer(fname))) == NULL) 382 return (FALSE); 383 if ((wp = popbuf(bp, WNONE)) == NULL) 384 return (FALSE); 385 curbp = bp; 386 curwp = wp; 387 if (bp->b_fname[0] != 0) 388 return (TRUE); /* never true for dired buffers */ 389 return (readin(fname)); 390 } 391 392 /* ARGSUSED */ 393 int 394 d_expunge(int f, int n) 395 { 396 struct line *lp, *nlp; 397 char fname[NFILEN], sname[NFILEN]; 398 int tmp; 399 400 tmp = curwp->w_dotline; 401 curwp->w_dotline = 0; 402 403 for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) { 404 curwp->w_dotline++; 405 nlp = lforw(lp); 406 if (llength(lp) && lgetc(lp, 0) == 'D') { 407 switch (d_makename(lp, fname, sizeof(fname))) { 408 case ABORT: 409 dobeep(); 410 ewprintf("Bad line in dired buffer"); 411 curwp->w_dotline = tmp; 412 return (FALSE); 413 case FALSE: 414 if (unlink(fname) == -1) { 415 (void)xbasename(sname, fname, NFILEN); 416 dobeep(); 417 ewprintf("Could not delete '%s'", sname); 418 curwp->w_dotline = tmp; 419 return (FALSE); 420 } 421 break; 422 case TRUE: 423 if (rmdir(fname) == -1) { 424 (void)xbasename(sname, fname, NFILEN); 425 dobeep(); 426 ewprintf("Could not delete directory " 427 "'%s'", sname); 428 curwp->w_dotline = tmp; 429 return (FALSE); 430 } 431 break; 432 } 433 lfree(lp); 434 curwp->w_bufp->b_lines--; 435 if (tmp > curwp->w_dotline) 436 tmp--; 437 curwp->w_rflag |= WFFULL; 438 } 439 } 440 curwp->w_dotline = tmp; 441 d_warpdot(curwp->w_dotp, &curwp->w_doto); 442 443 /* we have deleted all items successfully, remove del flag */ 444 curbp->b_flag &= ~BFDIREDDEL; 445 446 return (TRUE); 447 } 448 449 /* ARGSUSED */ 450 int 451 d_copy(int f, int n) 452 { 453 struct stat statbuf; 454 char frname[NFILEN], toname[NFILEN], sname[NFILEN]; 455 char *topath, *bufp; 456 int ret; 457 size_t off; 458 struct buffer *bp; 459 460 if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) { 461 dobeep(); 462 ewprintf("Not a file"); 463 return (FALSE); 464 } 465 off = strlcpy(toname, curbp->b_fname, sizeof(toname)); 466 if (off >= sizeof(toname) - 1) { /* can't happen, really */ 467 dobeep(); 468 ewprintf("Directory name too long"); 469 return (FALSE); 470 } 471 (void)xbasename(sname, frname, NFILEN); 472 bufp = eread("Copy %s to: ", toname, sizeof(toname), 473 EFDEF | EFNEW | EFCR, sname); 474 if (bufp == NULL) 475 return (ABORT); 476 else if (bufp[0] == '\0') 477 return (FALSE); 478 479 topath = adjustname(toname, TRUE); 480 if (stat(topath, &statbuf) == 0) { 481 if (S_ISDIR(statbuf.st_mode)) { 482 off = snprintf(toname, sizeof(toname), "%s/%s", 483 topath, sname); 484 if (off < 0 || off >= sizeof(toname) - 1) { 485 dobeep(); 486 ewprintf("Directory name too long"); 487 return (FALSE); 488 } 489 topath = adjustname(toname, TRUE); 490 } 491 } 492 if (strcmp(frname, topath) == 0) { 493 ewprintf("Cannot copy to same file: %s", frname); 494 return (TRUE); 495 } 496 ret = (copy(frname, topath) >= 0) ? TRUE : FALSE; 497 if (ret != TRUE) 498 return (ret); 499 if ((bp = refreshbuffer(curbp)) == NULL) 500 return (FALSE); 501 502 ewprintf("Copy: 1 file"); 503 return (showbuffer(bp, curwp, WFFULL | WFMODE)); 504 } 505 506 /* ARGSUSED */ 507 int 508 d_rename(int f, int n) 509 { 510 struct stat statbuf; 511 char frname[NFILEN], toname[NFILEN]; 512 char *topath, *bufp; 513 int ret; 514 size_t off; 515 struct buffer *bp; 516 char sname[NFILEN]; 517 518 if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) { 519 dobeep(); 520 ewprintf("Not a file"); 521 return (FALSE); 522 } 523 off = strlcpy(toname, curbp->b_fname, sizeof(toname)); 524 if (off >= sizeof(toname) - 1) { /* can't happen, really */ 525 dobeep(); 526 ewprintf("Directory name too long"); 527 return (FALSE); 528 } 529 (void)xbasename(sname, frname, NFILEN); 530 bufp = eread("Rename %s to: ", toname, 531 sizeof(toname), EFDEF | EFNEW | EFCR, sname); 532 if (bufp == NULL) 533 return (ABORT); 534 else if (bufp[0] == '\0') 535 return (FALSE); 536 537 topath = adjustname(toname, TRUE); 538 if (stat(topath, &statbuf) == 0) { 539 if (S_ISDIR(statbuf.st_mode)) { 540 off = snprintf(toname, sizeof(toname), "%s/%s", 541 topath, sname); 542 if (off < 0 || off >= sizeof(toname) - 1) { 543 dobeep(); 544 ewprintf("Directory name too long"); 545 return (FALSE); 546 } 547 topath = adjustname(toname, TRUE); 548 } 549 } 550 if (strcmp(frname, topath) == 0) { 551 ewprintf("Cannot move to same file: %s", frname); 552 return (TRUE); 553 } 554 ret = (rename(frname, topath) >= 0) ? TRUE : FALSE; 555 if (ret != TRUE) 556 return (ret); 557 if ((bp = refreshbuffer(curbp)) == NULL) 558 return (FALSE); 559 560 ewprintf("Move: 1 file"); 561 return (showbuffer(bp, curwp, WFFULL | WFMODE)); 562 } 563 564 /* ARGSUSED */ 565 void 566 reaper(int signo __attribute__((unused))) 567 { 568 int save_errno = errno, status; 569 570 while (waitpid(-1, &status, WNOHANG) >= 0) 571 ; 572 errno = save_errno; 573 } 574 575 /* 576 * Pipe the currently selected file through a shell command. 577 */ 578 /* ARGSUSED */ 579 int 580 d_shell_command(int f, int n) 581 { 582 char command[512], fname[PATH_MAX], *bufp; 583 struct buffer *bp; 584 struct mgwin *wp; 585 char sname[NFILEN]; 586 587 bp = bfind("*Shell Command Output*", TRUE); 588 if (bclear(bp) != TRUE) 589 return (ABORT); 590 591 if (d_makename(curwp->w_dotp, fname, sizeof(fname)) != FALSE) { 592 dobeep(); 593 ewprintf("bad line"); 594 return (ABORT); 595 } 596 597 command[0] = '\0'; 598 (void)xbasename(sname, fname, NFILEN); 599 bufp = eread("! on %s: ", command, sizeof(command), EFNEW, sname); 600 if (bufp == NULL) 601 return (ABORT); 602 603 if (d_exec(0, bp, fname, "sh", "-c", command, NULL) != TRUE) 604 return (ABORT); 605 606 if ((wp = popbuf(bp, WNONE)) == NULL) 607 return (ABORT); /* XXX - free the buffer?? */ 608 curwp = wp; 609 curbp = wp->w_bufp; 610 return (TRUE); 611 } 612 613 /* 614 * Pipe input file to cmd and insert the command's output in the 615 * given buffer. Each line will be prefixed with the given 616 * number of spaces. 617 */ 618 static int 619 d_exec(int space, struct buffer *bp, const char *input, const char *cmd, ...) 620 { 621 char buf[BUFSIZ]; 622 va_list ap; 623 struct sigaction olda, newa; 624 char **argv = NULL, *cp; 625 FILE *fin; 626 int fds[2] = { -1, -1 }; 627 int infd = -1; 628 int ret = (ABORT), n; 629 pid_t pid; 630 631 if (sigaction(SIGCHLD, NULL, &olda) == -1) 632 return (ABORT); 633 634 /* Find the number of arguments. */ 635 va_start(ap, cmd); 636 for (n = 2; va_arg(ap, char *) != NULL; n++) 637 ; 638 va_end(ap); 639 640 /* Allocate and build the argv. */ 641 if ((argv = calloc(n, sizeof(*argv))) == NULL) { 642 dobeep(); 643 ewprintf("Can't allocate argv : %s", strerror(errno)); 644 goto out; 645 } 646 647 n = 1; 648 argv[0] = (char *)cmd; 649 va_start(ap, cmd); 650 while ((argv[n] = va_arg(ap, char *)) != NULL) 651 n++; 652 va_end(ap); 653 654 if (input == NULL) 655 input = "/dev/null"; 656 657 if ((infd = open(input, O_RDONLY)) == -1) { 658 dobeep(); 659 ewprintf("Can't open input file : %s", strerror(errno)); 660 goto out; 661 } 662 663 if (pipe(fds) == -1) { 664 dobeep(); 665 ewprintf("Can't create pipe : %s", strerror(errno)); 666 goto out; 667 } 668 669 newa.sa_handler = reaper; 670 newa.sa_flags = 0; 671 if (sigaction(SIGCHLD, &newa, NULL) == -1) 672 goto out; 673 674 if ((pid = fork()) == -1) { 675 dobeep(); 676 ewprintf("Can't fork"); 677 goto out; 678 } 679 680 switch (pid) { 681 case 0: /* Child */ 682 close(fds[0]); 683 dup2(infd, STDIN_FILENO); 684 dup2(fds[1], STDOUT_FILENO); 685 dup2(fds[1], STDERR_FILENO); 686 if (execvp(argv[0], argv) == -1) 687 ewprintf("Can't exec %s: %s", argv[0], strerror(errno)); 688 exit(1); 689 break; 690 default: /* Parent */ 691 close(infd); 692 close(fds[1]); 693 infd = fds[1] = -1; 694 if ((fin = fdopen(fds[0], "r")) == NULL) 695 goto out; 696 while (fgets(buf, sizeof(buf), fin) != NULL) { 697 cp = strrchr(buf, '\n'); 698 if (cp == NULL && !feof(fin)) { /* too long a line */ 699 int c; 700 addlinef(bp, "%*s%s...", space, "", buf); 701 while ((c = getc(fin)) != EOF && c != '\n') 702 ; 703 continue; 704 } else if (cp) 705 *cp = '\0'; 706 addlinef(bp, "%*s%s", space, "", buf); 707 } 708 fclose(fin); 709 break; 710 } 711 ret = (TRUE); 712 713 out: 714 if (sigaction(SIGCHLD, &olda, NULL) == -1) 715 ewprintf("Warning, couldn't reset previous signal handler"); 716 if (fds[0] != -1) 717 close(fds[0]); 718 if (fds[1] != -1) 719 close(fds[1]); 720 if (infd != -1) 721 close(infd); 722 free(argv); 723 return ret; 724 } 725 726 /* ARGSUSED */ 727 int 728 d_create_directory(int f, int n) 729 { 730 int ret; 731 struct buffer *bp; 732 733 ret = ask_makedir(); 734 if (ret != TRUE) 735 return(ret); 736 737 if ((bp = refreshbuffer(curbp)) == NULL) 738 return (FALSE); 739 740 return (showbuffer(bp, curwp, WFFULL | WFMODE)); 741 } 742 743 /* ARGSUSED */ 744 int 745 d_killbuffer_cmd(int f, int n) 746 { 747 return(killbuffer_cmd(FFRAND, 0)); 748 } 749 750 int 751 d_refreshbuffer(int f, int n) 752 { 753 struct buffer *bp; 754 755 if ((bp = refreshbuffer(curbp)) == NULL) 756 return (FALSE); 757 758 return (showbuffer(bp, curwp, WFFULL | WFMODE)); 759 } 760 761 /* 762 * Kill then re-open the requested dired buffer. 763 * If required, take a note of any files marked for deletion. Then once 764 * the buffer has been re-opened, remark the same files as deleted. 765 */ 766 struct buffer * 767 refreshbuffer(struct buffer *bp) 768 { 769 char *tmp_b_fname; 770 int i, tmp_w_dotline, ddel = 0; 771 772 /* remember directory path to open later */ 773 tmp_b_fname = strdup(bp->b_fname); 774 if (tmp_b_fname == NULL) { 775 dobeep(); 776 ewprintf("Out of memory"); 777 return (NULL); 778 } 779 tmp_w_dotline = curwp->w_dotline; 780 781 /* create a list of files for deletion */ 782 if (bp->b_flag & BFDIREDDEL) 783 ddel = createlist(bp); 784 785 killbuffer(bp); 786 787 /* dired_() uses findbuffer() to create new buffer */ 788 if ((bp = dired_(tmp_b_fname)) == NULL) { 789 free(tmp_b_fname); 790 return (NULL); 791 } 792 free(tmp_b_fname); 793 794 /* remark any previously deleted files with a 'D' */ 795 if (ddel) 796 redelete(bp); 797 798 /* find dot line */ 799 bp->b_dotp = bfirstlp(bp); 800 if (tmp_w_dotline > bp->b_lines) 801 tmp_w_dotline = bp->b_lines - 1; 802 for (i = 1; i < tmp_w_dotline; i++) 803 bp->b_dotp = lforw(bp->b_dotp); 804 805 bp->b_dotline = i; 806 bp->b_doto = 0; 807 d_warpdot(bp->b_dotp, &bp->b_doto); 808 809 curbp = bp; 810 811 return (bp); 812 } 813 814 static int 815 d_makename(struct line *lp, char *fn, size_t len) 816 { 817 int start, nlen; 818 char *namep; 819 820 if (d_warpdot(lp, &start) == FALSE) 821 return (ABORT); 822 namep = &lp->l_text[start]; 823 nlen = llength(lp) - start; 824 825 if (snprintf(fn, len, "%s%.*s", curbp->b_fname, nlen, namep) >= len) 826 return (ABORT); /* Name is too long. */ 827 828 /* Return TRUE if the entry is a directory. */ 829 return ((lgetc(lp, 2) == 'd') ? TRUE : FALSE); 830 } 831 832 #define NAME_FIELD 9 833 834 static int 835 d_warpdot(struct line *dotp, int *doto) 836 { 837 char *tp = dotp->l_text; 838 int off = 0, field = 0, len; 839 840 /* 841 * Find the byte offset to the (space-delimited) filename 842 * field in formatted ls output. 843 */ 844 len = llength(dotp); 845 while (off < len) { 846 if (tp[off++] == ' ') { 847 if (++field == NAME_FIELD) { 848 *doto = off; 849 return (TRUE); 850 } 851 /* Skip the space. */ 852 while (off < len && tp[off] == ' ') 853 off++; 854 } 855 } 856 /* We didn't find the field. */ 857 *doto = 0; 858 return (FALSE); 859 } 860 861 static int 862 d_forwpage(int f, int n) 863 { 864 forwpage(f | FFRAND, n); 865 return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); 866 } 867 868 static int 869 d_backpage (int f, int n) 870 { 871 backpage(f | FFRAND, n); 872 return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); 873 } 874 875 static int 876 d_forwline (int f, int n) 877 { 878 forwline(f | FFRAND, n); 879 return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); 880 } 881 882 static int 883 d_backline (int f, int n) 884 { 885 backline(f | FFRAND, n); 886 return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); 887 } 888 889 int 890 d_filevisitalt (int f, int n) 891 { 892 char fname[NFILEN]; 893 894 if (d_makename(curwp->w_dotp, fname, sizeof(fname)) == ABORT) 895 return (FALSE); 896 897 return(do_filevisitalt(fname)); 898 } 899 900 /* 901 * XXX dname needs to have enough place to store an additional '/'. 902 */ 903 struct buffer * 904 dired_(char *dname) 905 { 906 struct buffer *bp; 907 int i; 908 size_t len; 909 910 if ((dname = adjustname(dname, TRUE)) == NULL) { 911 dobeep(); 912 ewprintf("Bad directory name"); 913 return (NULL); 914 } 915 /* this should not be done, instead adjustname() should get a flag */ 916 len = strlen(dname); 917 if (dname[len - 1] != '/') { 918 dname[len++] = '/'; 919 dname[len] = '\0'; 920 } 921 if ((access(dname, R_OK | X_OK)) == -1) { 922 if (errno == EACCES) { 923 dobeep(); 924 ewprintf("Permission denied: %s", dname); 925 } 926 return (NULL); 927 } 928 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 929 if (strcmp(bp->b_fname, dname) == 0) { 930 if (fchecktime(bp) != TRUE) 931 ewprintf("Directory has changed on disk;" 932 " type g to update Dired"); 933 return (bp); 934 } 935 936 } 937 bp = bfind(dname, TRUE); 938 bp->b_flag |= BFREADONLY | BFIGNDIRTY; 939 940 if ((d_exec(2, bp, NULL, "ls", "-al", dname, NULL)) != TRUE) 941 return (NULL); 942 943 /* Find the line with ".." on it. */ 944 bp->b_dotp = bfirstlp(bp); 945 bp->b_dotline = 1; 946 for (i = 0; i < bp->b_lines; i++) { 947 bp->b_dotp = lforw(bp->b_dotp); 948 bp->b_dotline++; 949 if (d_warpdot(bp->b_dotp, &bp->b_doto) == FALSE) 950 continue; 951 if (strcmp(ltext(bp->b_dotp) + bp->b_doto, "..") == 0) 952 break; 953 } 954 955 /* We want dot on the entry right after "..", if possible. */ 956 if (++i < bp->b_lines - 2) { 957 bp->b_dotp = lforw(bp->b_dotp); 958 bp->b_dotline++; 959 } 960 d_warpdot(bp->b_dotp, &bp->b_doto); 961 962 (void)strlcpy(bp->b_fname, dname, sizeof(bp->b_fname)); 963 (void)strlcpy(bp->b_cwd, dname, sizeof(bp->b_cwd)); 964 if ((bp->b_modes[1] = name_mode("dired")) == NULL) { 965 bp->b_modes[0] = name_mode("fundamental"); 966 dobeep(); 967 ewprintf("Could not find mode dired"); 968 return (NULL); 969 } 970 (void)fupdstat(bp); 971 bp->b_nmodes = 1; 972 return (bp); 973 } 974 975 /* 976 * Iterate through the lines of the dired buffer looking for files 977 * collected in the linked list made in createlist(). If a line is found 978 * replace 'D' as first char in a line. As lines are found, remove the 979 * corresponding item from the linked list. Iterate for as long as there 980 * are items in the linked list or until end of buffer is found. 981 */ 982 void 983 redelete(struct buffer *bp) 984 { 985 struct delentry *dt, *d1 = NULL; 986 struct line *lp, *nlp; 987 char fname[NFILEN]; 988 char *p = fname; 989 size_t plen, fnlen; 990 int finished = 0; 991 992 /* reset the deleted file buffer flag until a deleted file is found */ 993 bp->b_flag &= ~BFDIREDDEL; 994 995 for (lp = bfirstlp(bp); lp != bp->b_headp; lp = nlp) { 996 bp->b_dotp = lp; 997 if ((p = findfname(lp, p)) == NULL) { 998 nlp = lforw(lp); 999 continue; 1000 } 1001 plen = strlen(p); 1002 SLIST_FOREACH_SAFE(d1, &delhead, entry, dt) { 1003 fnlen = strlen(d1->fn); 1004 if ((plen == fnlen) && 1005 (strncmp(p, d1->fn, plen) == 0)) { 1006 lputc(bp->b_dotp, 0, DDELCHAR); 1007 bp->b_flag |= BFDIREDDEL; 1008 SLIST_REMOVE(&delhead, d1, delentry, entry); 1009 if (SLIST_EMPTY(&delhead)) { 1010 finished = 1; 1011 break; 1012 } 1013 } 1014 } 1015 if (finished) 1016 break; 1017 nlp = lforw(lp); 1018 } 1019 while (!SLIST_EMPTY(&delhead)) { 1020 d1 = SLIST_FIRST(&delhead); 1021 SLIST_REMOVE_HEAD(&delhead, entry); 1022 free(d1->fn); 1023 free(d1); 1024 } 1025 return; 1026 } 1027 1028 /* 1029 * Create a list of files marked for deletion. 1030 */ 1031 int 1032 createlist(struct buffer *bp) 1033 { 1034 struct delentry *d1 = NULL, *d2; 1035 struct line *lp, *nlp; 1036 char fname[NFILEN]; 1037 char *p = fname; 1038 int ret = FALSE; 1039 1040 for (lp = bfirstlp(bp); lp != bp->b_headp; lp = nlp) { 1041 /* 1042 * Check if the line has 'D' on the first char and if a valid 1043 * filename can be extracted from it. 1044 */ 1045 if (((lp->l_text[0] != DDELCHAR)) || 1046 ((p = findfname(lp, p)) == NULL)) { 1047 nlp = lforw(lp); 1048 continue; 1049 } 1050 if (SLIST_EMPTY(&delhead)) { 1051 if ((d1 = malloc(sizeof(struct delentry))) 1052 == NULL) 1053 return (ABORT); 1054 if ((d1->fn = strdup(p)) == NULL) { 1055 free(d1); 1056 return (ABORT); 1057 } 1058 SLIST_INSERT_HEAD(&delhead, d1, entry); 1059 } else { 1060 if ((d2 = malloc(sizeof(struct delentry))) 1061 == NULL) { 1062 free(d1->fn); 1063 free(d1); 1064 return (ABORT); 1065 } 1066 if ((d2->fn = strdup(p)) == NULL) { 1067 free(d1->fn); 1068 free(d1); 1069 free(d2); 1070 return (ABORT); 1071 } 1072 SLIST_INSERT_AFTER(d1, d2, entry); 1073 d1 = d2; 1074 } 1075 ret = TRUE; 1076 nlp = lforw(lp); 1077 } 1078 return (ret); 1079 } 1080 1081 int 1082 d_gotofile(int f, int n) 1083 { 1084 struct line *lp, *nlp; 1085 struct buffer *curbp; 1086 size_t lenfpath; 1087 char fpath[NFILEN], fname[NFILEN]; 1088 char *p, *fpth, *fnp = NULL; 1089 int tmp; 1090 1091 if (getbufcwd(fpath, sizeof(fpath)) != TRUE) 1092 fpath[0] = '\0'; 1093 lenfpath = strlen(fpath); 1094 fnp = eread("Goto file: ", fpath, NFILEN, 1095 EFNEW | EFCR | EFFILE | EFDEF); 1096 if (fnp == NULL) 1097 return (ABORT); 1098 else if (fnp[0] == '\0') 1099 return (FALSE); 1100 1101 fpth = adjustname(fpath, TRUE); /* Removes last '/' if */ 1102 if (strlen(fpth) == lenfpath - 1) { /* directory, hence -1. */ 1103 ewprintf("No file to find"); /* Current directory given so */ 1104 return (TRUE); /* return at present location. */ 1105 } 1106 (void)xbasename(fname, fpth, NFILEN); 1107 curbp = curwp->w_bufp; 1108 tmp = 0; 1109 for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) { 1110 tmp++; 1111 if ((p = findfname(lp, p)) == NULL) { 1112 nlp = lforw(lp); 1113 continue; 1114 } 1115 if (strcmp(fname, p) == 0) { 1116 curwp->w_dotp = lp; 1117 curwp->w_dotline = tmp; 1118 (void)d_warpdot(curwp->w_dotp, &curwp->w_doto); 1119 tmp--; 1120 break; 1121 } 1122 nlp = lforw(lp); 1123 } 1124 if (tmp == curbp->b_lines - 1) { 1125 ewprintf("File not found %s", fname); 1126 return (FALSE); 1127 } else { 1128 ewprintf(""); 1129 return (TRUE); 1130 } 1131 } 1132 1133 /* 1134 * Look for and extract a file name on a dired buffer line. 1135 */ 1136 char * 1137 findfname(struct line *lp, char *fn) 1138 { 1139 int start; 1140 1141 (void)d_warpdot(lp, &start); 1142 if (start < 1) 1143 return NULL; 1144 fn = &lp->l_text[start]; 1145 return fn; 1146 } 1147