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