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