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