1 /* $OpenBSD: dired.c,v 1.45 2009/06/04 23:39:37 kjell 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 "def.h" 10 #include "funmap.h" 11 #include "kbd.h" 12 13 #include <sys/types.h> 14 #include <sys/stat.h> 15 #include <sys/time.h> 16 #include <sys/resource.h> 17 #include <sys/wait.h> 18 19 #include <ctype.h> 20 #include <signal.h> 21 #include <fcntl.h> 22 #include <errno.h> 23 #include <libgen.h> 24 25 void dired_init(void); 26 static int dired(int, int); 27 static int d_otherwindow(int, int); 28 static int d_undel(int, int); 29 static int d_undelbak(int, int); 30 static int d_findfile(int, int); 31 static int d_ffotherwindow(int, int); 32 static int d_expunge(int, int); 33 static int d_copy(int, int); 34 static int d_del(int, int); 35 static int d_rename(int, int); 36 static int d_shell_command(int, int); 37 static int d_create_directory(int, int); 38 static int d_makename(struct line *, char *, size_t); 39 40 extern struct keymap_s helpmap, cXmap, metamap; 41 42 static PF dirednul[] = { 43 setmark, /* ^@ */ 44 gotobol, /* ^A */ 45 backchar, /* ^B */ 46 rescan, /* ^C */ 47 d_del, /* ^D */ 48 gotoeol, /* ^E */ 49 forwchar, /* ^F */ 50 ctrlg, /* ^G */ 51 #ifndef NO_HELP 52 NULL, /* ^H */ 53 #endif /* !NO_HELP */ 54 }; 55 56 static PF diredcl[] = { 57 reposition, /* ^L */ 58 d_findfile, /* ^M */ 59 forwline, /* ^N */ 60 rescan, /* ^O */ 61 backline, /* ^P */ 62 rescan, /* ^Q */ 63 backisearch, /* ^R */ 64 forwisearch, /* ^S */ 65 rescan, /* ^T */ 66 universal_argument, /* ^U */ 67 forwpage, /* ^V */ 68 rescan, /* ^W */ 69 NULL /* ^X */ 70 }; 71 72 static PF diredcz[] = { 73 spawncli, /* ^Z */ 74 NULL, /* esc */ 75 rescan, /* ^\ */ 76 rescan, /* ^] */ 77 rescan, /* ^^ */ 78 rescan, /* ^_ */ 79 forwline, /* SP */ 80 d_shell_command, /* ! */ 81 rescan, /* " */ 82 rescan, /* # */ 83 rescan, /* $ */ 84 rescan, /* % */ 85 rescan, /* & */ 86 rescan, /* ' */ 87 rescan, /* ( */ 88 rescan, /* ) */ 89 rescan, /* * */ 90 d_create_directory /* + */ 91 }; 92 93 static PF diredc[] = { 94 d_copy, /* c */ 95 d_del, /* d */ 96 d_findfile, /* e */ 97 d_findfile /* f */ 98 }; 99 100 static PF diredn[] = { 101 forwline, /* n */ 102 d_ffotherwindow, /* o */ 103 backline, /* p */ 104 rescan, /* q */ 105 d_rename, /* r */ 106 rescan, /* s */ 107 rescan, /* t */ 108 d_undel, /* u */ 109 rescan, /* v */ 110 rescan, /* w */ 111 d_expunge /* x */ 112 }; 113 114 static PF direddl[] = { 115 d_undelbak /* del */ 116 }; 117 118 #ifndef DIRED_XMAPS 119 #define NDIRED_XMAPS 0 /* number of extra map sections */ 120 #endif /* DIRED_XMAPS */ 121 122 static struct KEYMAPE (6 + NDIRED_XMAPS + IMAPEXT) diredmap = { 123 6 + NDIRED_XMAPS, 124 6 + NDIRED_XMAPS + IMAPEXT, 125 rescan, 126 { 127 #ifndef NO_HELP 128 { 129 CCHR('@'), CCHR('H'), dirednul, (KEYMAP *) & helpmap 130 }, 131 #else /* !NO_HELP */ 132 { 133 CCHR('@'), CCHR('G'), dirednul, NULL 134 }, 135 #endif /* !NO_HELP */ 136 { 137 CCHR('L'), CCHR('X'), diredcl, (KEYMAP *) & cXmap 138 }, 139 { 140 CCHR('Z'), '+', diredcz, (KEYMAP *) & metamap 141 }, 142 { 143 'c', 'f', diredc, NULL 144 }, 145 { 146 'n', 'x', diredn, NULL 147 }, 148 { 149 CCHR('?'), CCHR('?'), direddl, NULL 150 }, 151 #ifdef DIRED_XMAPS 152 DIRED_XMAPS, /* map sections for dired mode keys */ 153 #endif /* DIRED_XMAPS */ 154 } 155 }; 156 157 void 158 dired_init(void) 159 { 160 funmap_add(dired, "dired"); 161 funmap_add(d_undelbak, "dired-backup-unflag"); 162 funmap_add(d_copy, "dired-copy-file"); 163 funmap_add(d_expunge, "dired-do-deletions"); 164 funmap_add(d_findfile, "dired-find-file"); 165 funmap_add(d_ffotherwindow, "dired-find-file-other-window"); 166 funmap_add(d_del, "dired-flag-file-deleted"); 167 funmap_add(d_otherwindow, "dired-other-window"); 168 funmap_add(d_rename, "dired-rename-file"); 169 funmap_add(d_undel, "dired-unflag"); 170 maps_add((KEYMAP *)&diredmap, "dired"); 171 dobindkey(fundamental_map, "dired", "^Xd"); 172 } 173 174 /* ARGSUSED */ 175 int 176 dired(int f, int n) 177 { 178 char dname[NFILEN], *bufp, *slash; 179 struct buffer *bp; 180 181 if (curbp->b_fname && curbp->b_fname[0] != '\0') { 182 (void)strlcpy(dname, curbp->b_fname, sizeof(dname)); 183 if ((slash = strrchr(dname, '/')) != NULL) { 184 *(slash + 1) = '\0'; 185 } 186 } else { 187 if (getcwd(dname, sizeof(dname)) == NULL) 188 dname[0] = '\0'; 189 } 190 191 if ((bufp = eread("Dired: ", dname, NFILEN, 192 EFDEF | EFNEW | EFCR)) == NULL) 193 return (ABORT); 194 if (bufp[0] == '\0') 195 return (FALSE); 196 if ((bp = dired_(bufp)) == NULL) 197 return (FALSE); 198 199 curbp = bp; 200 return (showbuffer(bp, curwp, WFFULL | WFMODE)); 201 } 202 203 /* ARGSUSED */ 204 int 205 d_otherwindow(int f, int n) 206 { 207 char dname[NFILEN], *bufp, *slash; 208 struct buffer *bp; 209 struct mgwin *wp; 210 211 if (curbp->b_fname && curbp->b_fname[0] != '\0') { 212 (void)strlcpy(dname, curbp->b_fname, sizeof(dname)); 213 if ((slash = strrchr(dname, '/')) != NULL) { 214 *(slash + 1) = '\0'; 215 } 216 } else { 217 if (getcwd(dname, sizeof(dname)) == NULL) 218 dname[0] = '\0'; 219 } 220 221 if ((bufp = eread("Dired other window: ", dname, NFILEN, 222 EFDEF | EFNEW | EFCR)) == NULL) 223 return (ABORT); 224 else if (bufp[0] == '\0') 225 return (FALSE); 226 if ((bp = dired_(bufp)) == NULL) 227 return (FALSE); 228 if ((wp = popbuf(bp, WNONE)) == NULL) 229 return (FALSE); 230 curbp = bp; 231 curwp = wp; 232 return (TRUE); 233 } 234 235 /* ARGSUSED */ 236 int 237 d_del(int f, int n) 238 { 239 if (n < 0) 240 return (FALSE); 241 while (n--) { 242 if (llength(curwp->w_dotp) > 0) 243 lputc(curwp->w_dotp, 0, 'D'); 244 if (lforw(curwp->w_dotp) != curbp->b_headp) 245 curwp->w_dotp = lforw(curwp->w_dotp); 246 } 247 curwp->w_rflag |= WFEDIT | WFMOVE; 248 curwp->w_doto = 0; 249 return (TRUE); 250 } 251 252 /* ARGSUSED */ 253 int 254 d_undel(int f, int n) 255 { 256 if (n < 0) 257 return (d_undelbak(f, -n)); 258 while (n--) { 259 if (llength(curwp->w_dotp) > 0) 260 lputc(curwp->w_dotp, 0, ' '); 261 if (lforw(curwp->w_dotp) != curbp->b_headp) 262 curwp->w_dotp = lforw(curwp->w_dotp); 263 } 264 curwp->w_rflag |= WFEDIT | WFMOVE; 265 curwp->w_doto = 0; 266 return (TRUE); 267 } 268 269 /* ARGSUSED */ 270 int 271 d_undelbak(int f, int n) 272 { 273 if (n < 0) 274 return (d_undel(f, -n)); 275 while (n--) { 276 if (llength(curwp->w_dotp) > 0) 277 lputc(curwp->w_dotp, 0, ' '); 278 if (lback(curwp->w_dotp) != curbp->b_headp) 279 curwp->w_dotp = lback(curwp->w_dotp); 280 } 281 curwp->w_doto = 0; 282 curwp->w_rflag |= WFEDIT | WFMOVE; 283 return (TRUE); 284 } 285 286 /* ARGSUSED */ 287 int 288 d_findfile(int f, int n) 289 { 290 struct buffer *bp; 291 int s; 292 char fname[NFILEN]; 293 294 if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT) 295 return (FALSE); 296 if (s == TRUE) 297 bp = dired_(fname); 298 else 299 bp = findbuffer(fname); 300 if (bp == NULL) 301 return (FALSE); 302 curbp = bp; 303 if (showbuffer(bp, curwp, WFFULL) != TRUE) 304 return (FALSE); 305 if (bp->b_fname[0] != 0) 306 return (TRUE); 307 return (readin(fname)); 308 } 309 310 /* ARGSUSED */ 311 int 312 d_ffotherwindow(int f, int n) 313 { 314 char fname[NFILEN]; 315 int s; 316 struct buffer *bp; 317 struct mgwin *wp; 318 319 if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT) 320 return (FALSE); 321 if ((bp = (s ? dired_(fname) : findbuffer(fname))) == NULL) 322 return (FALSE); 323 if ((wp = popbuf(bp, WNONE)) == NULL) 324 return (FALSE); 325 curbp = bp; 326 curwp = wp; 327 if (bp->b_fname[0] != 0) 328 return (TRUE); /* never true for dired buffers */ 329 return (readin(fname)); 330 } 331 332 /* ARGSUSED */ 333 int 334 d_expunge(int f, int n) 335 { 336 struct line *lp, *nlp; 337 char fname[NFILEN]; 338 339 for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) { 340 nlp = lforw(lp); 341 if (llength(lp) && lgetc(lp, 0) == 'D') { 342 switch (d_makename(lp, fname, sizeof(fname))) { 343 case ABORT: 344 ewprintf("Bad line in dired buffer"); 345 return (FALSE); 346 case FALSE: 347 if (unlink(fname) < 0) { 348 ewprintf("Could not delete '%s'", 349 basename(fname)); 350 return (FALSE); 351 } 352 break; 353 case TRUE: 354 if (rmdir(fname) < 0) { 355 ewprintf("Could not delete directory '%s'", 356 basename(fname)); 357 return (FALSE); 358 } 359 break; 360 } 361 lfree(lp); 362 curwp->w_bufp->b_lines--; 363 curwp->w_rflag |= WFFULL; 364 } 365 } 366 return (TRUE); 367 } 368 369 /* ARGSUSED */ 370 int 371 d_copy(int f, int n) 372 { 373 char frname[NFILEN], toname[NFILEN], *bufp; 374 int ret; 375 size_t off; 376 struct buffer *bp; 377 378 if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) { 379 ewprintf("Not a file"); 380 return (FALSE); 381 } 382 off = strlcpy(toname, curbp->b_fname, sizeof(toname)); 383 if (off >= sizeof(toname) - 1) { /* can't happen, really */ 384 ewprintf("Directory name too long"); 385 return (FALSE); 386 } 387 if ((bufp = eread("Copy %s to: ", toname, sizeof(toname), 388 EFDEF | EFNEW | EFCR, basename(frname))) == NULL) 389 return (ABORT); 390 else if (bufp[0] == '\0') 391 return (FALSE); 392 ret = (copy(frname, toname) >= 0) ? TRUE : FALSE; 393 if (ret != TRUE) 394 return (ret); 395 bp = dired_(curbp->b_fname); 396 return (showbuffer(bp, curwp, WFFULL | WFMODE)); 397 } 398 399 /* ARGSUSED */ 400 int 401 d_rename(int f, int n) 402 { 403 char frname[NFILEN], toname[NFILEN], *bufp; 404 int ret; 405 size_t off; 406 struct buffer *bp; 407 408 if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) { 409 ewprintf("Not a file"); 410 return (FALSE); 411 } 412 off = strlcpy(toname, curbp->b_fname, sizeof(toname)); 413 if (off >= sizeof(toname) - 1) { /* can't happen, really */ 414 ewprintf("Directory name too long"); 415 return (FALSE); 416 } 417 if ((bufp = eread("Rename %s to: ", toname, 418 sizeof(toname), EFDEF | EFNEW | EFCR, basename(frname))) == NULL) 419 return (ABORT); 420 else if (bufp[0] == '\0') 421 return (FALSE); 422 ret = (rename(frname, toname) >= 0) ? TRUE : FALSE; 423 if (ret != TRUE) 424 return (ret); 425 bp = dired_(curbp->b_fname); 426 return (showbuffer(bp, curwp, WFFULL | WFMODE)); 427 } 428 429 /* ARGSUSED */ 430 void 431 reaper(int signo __attribute__((unused))) 432 { 433 int save_errno = errno, status; 434 435 while (waitpid(-1, &status, WNOHANG) >= 0) 436 ; 437 errno = save_errno; 438 } 439 440 /* 441 * Pipe the currently selected file through a shell command. 442 */ 443 /* ARGSUSED */ 444 int 445 d_shell_command(int f, int n) 446 { 447 char command[512], fname[MAXPATHLEN], buf[BUFSIZ], *bufp, *cp; 448 int infd, fds[2]; 449 pid_t pid; 450 struct sigaction olda, newa; 451 struct buffer *bp; 452 struct mgwin *wp; 453 FILE *fin; 454 455 bp = bfind("*Shell Command Output*", TRUE); 456 if (bclear(bp) != TRUE) 457 return (ABORT); 458 459 if (d_makename(curwp->w_dotp, fname, sizeof(fname)) != FALSE) { 460 ewprintf("bad line"); 461 return (ABORT); 462 } 463 464 command[0] = '\0'; 465 if ((bufp = eread("! on %s: ", command, sizeof(command), EFNEW, 466 basename(fname))) == NULL) 467 return (ABORT); 468 infd = open(fname, O_RDONLY); 469 if (infd == -1) { 470 ewprintf("Can't open input file : %s", strerror(errno)); 471 return (FALSE); 472 } 473 if (pipe(fds) == -1) { 474 ewprintf("Can't create pipe : %s", strerror(errno)); 475 close(infd); 476 return (FALSE); 477 } 478 479 newa.sa_handler = reaper; 480 newa.sa_flags = 0; 481 if (sigaction(SIGCHLD, &newa, &olda) == -1) { 482 close(infd); 483 close(fds[0]); 484 close(fds[1]); 485 return (ABORT); 486 } 487 pid = fork(); 488 switch (pid) { 489 case -1: 490 ewprintf("Can't fork"); 491 return (ABORT); 492 case 0: 493 close(fds[0]); 494 dup2(infd, STDIN_FILENO); 495 dup2(fds[1], STDOUT_FILENO); 496 dup2(fds[1], STDERR_FILENO); 497 execl("/bin/sh", "sh", "-c", bufp, (char *)NULL); 498 exit(1); 499 break; 500 default: 501 close(infd); 502 close(fds[1]); 503 fin = fdopen(fds[0], "r"); 504 if (fin == NULL) /* "r" is surely a valid mode! */ 505 panic("can't happen"); 506 while (fgets(buf, sizeof(buf), fin) != NULL) { 507 cp = strrchr(buf, '\n'); 508 if (cp == NULL && !feof(fin)) { /* too long a line */ 509 int c; 510 addlinef(bp, "%s...", buf); 511 while ((c = getc(fin)) != EOF && c != '\n') 512 ; 513 continue; 514 } else if (cp) 515 *cp = '\0'; 516 addline(bp, buf); 517 } 518 fclose(fin); 519 close(fds[0]); 520 break; 521 } 522 wp = popbuf(bp, WNONE); 523 if (wp == NULL) 524 return (ABORT); /* XXX - free the buffer?? */ 525 curwp = wp; 526 curbp = wp->w_bufp; 527 if (sigaction(SIGCHLD, &olda, NULL) == -1) 528 ewprintf("Warning, couldn't reset previous signal handler"); 529 return (TRUE); 530 } 531 532 /* ARGSUSED */ 533 int 534 d_create_directory(int f, int n) 535 { 536 char tocreate[MAXPATHLEN], *bufp; 537 size_t off; 538 struct buffer *bp; 539 540 off = strlcpy(tocreate, curbp->b_fname, sizeof(tocreate)); 541 if (off >= sizeof(tocreate) - 1) 542 return (FALSE); 543 if ((bufp = eread("Create directory: ", tocreate, 544 sizeof(tocreate), EFDEF | EFNEW | EFCR)) == NULL) 545 return (ABORT); 546 else if (bufp[0] == '\0') 547 return (FALSE); 548 if (mkdir(tocreate, 0755) == -1) { 549 ewprintf("Creating directory: %s, %s", strerror(errno), 550 tocreate); 551 return (FALSE); 552 } 553 bp = dired_(curbp->b_fname); 554 return (showbuffer(bp, curwp, WFFULL | WFMODE)); 555 } 556 557 #define NAME_FIELD 8 558 559 static int 560 d_makename(struct line *lp, char *fn, size_t len) 561 { 562 int i; 563 char *p, *ep; 564 565 if (strlcpy(fn, curbp->b_fname, len) >= len) 566 return (FALSE); 567 if ((p = lp->l_text) == NULL) 568 return (ABORT); 569 ep = lp->l_text + llength(lp); 570 p++; /* skip action letter, if any */ 571 for (i = 0; i < NAME_FIELD; i++) { 572 while (p < ep && isspace(*p)) 573 p++; 574 while (p < ep && !isspace(*p)) 575 p++; 576 while (p < ep && isspace(*p)) 577 p++; 578 if (p == ep) 579 return (ABORT); 580 } 581 if (strlcat(fn, p, len) >= len) 582 return (FALSE); 583 return ((lgetc(lp, 2) == 'd') ? TRUE : FALSE); 584 } 585 586 /* 587 * XXX dname needs to have enough place to store an additional '/'. 588 */ 589 struct buffer * 590 dired_(char *dname) 591 { 592 struct buffer *bp; 593 FILE *dirpipe; 594 char line[256]; 595 int len, ret; 596 597 if ((dname = adjustname(dname, FALSE)) == NULL) { 598 ewprintf("Bad directory name"); 599 return (NULL); 600 } 601 /* this should not be done, instead adjustname() should get a flag */ 602 len = strlen(dname); 603 if (dname[len - 1] != '/') { 604 dname[len++] = '/'; 605 dname[len] = '\0'; 606 } 607 if ((bp = findbuffer(dname)) == NULL) { 608 ewprintf("Could not create buffer"); 609 return (NULL); 610 } 611 if (bclear(bp) != TRUE) 612 return (NULL); 613 bp->b_flag |= BFREADONLY; 614 ret = snprintf(line, sizeof(line), "ls -al %s", dname); 615 if (ret < 0 || ret >= sizeof(line)) { 616 ewprintf("Path too long"); 617 return (NULL); 618 } 619 if ((dirpipe = popen(line, "r")) == NULL) { 620 ewprintf("Problem opening pipe to ls"); 621 return (NULL); 622 } 623 line[0] = line[1] = ' '; 624 while (fgets(&line[2], sizeof(line) - 2, dirpipe) != NULL) { 625 line[strcspn(line, "\n")] = '\0'; /* remove ^J */ 626 (void) addline(bp, line); 627 } 628 if (pclose(dirpipe) == -1) { 629 ewprintf("Problem closing pipe to ls : %s", 630 strerror(errno)); 631 return (NULL); 632 } 633 bp->b_dotp = bfirstlp(bp); 634 (void)strlcpy(bp->b_fname, dname, sizeof(bp->b_fname)); 635 (void)strlcpy(bp->b_cwd, dname, sizeof(bp->b_cwd)); 636 if ((bp->b_modes[1] = name_mode("dired")) == NULL) { 637 bp->b_modes[0] = name_mode("fundamental"); 638 ewprintf("Could not find mode dired"); 639 return (NULL); 640 } 641 bp->b_nmodes = 1; 642 return (bp); 643 } 644