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