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