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