1 /* $OpenBSD: dired.c,v 1.51 2012/03/14 13:56:35 lum Exp $ */
3 /* This file is in the public domain. */
5 /* dired module for mg 2a
13 #include <sys/types.h>
16 #include <sys/resource.h>
26 void dired_init(void);
27 static int dired(int, int);
28 static int d_otherwindow(int, int);
29 static int d_undel(int, int);
30 static int d_undelbak(int, int);
31 static int d_findfile(int, int);
32 static int d_ffotherwindow(int, int);
33 static int d_expunge(int, int);
34 static int d_copy(int, int);
35 static int d_del(int, int);
36 static int d_rename(int, int);
37 static int d_exec(int, struct buffer *, const char *, const char *, ...);
38 static int d_shell_command(int, int);
39 static int d_create_directory(int, int);
40 static int d_makename(struct line *, char *, size_t);
41 static int d_warpdot(struct line *, int *);
42 static int d_forwpage(int, int);
43 static int d_backpage(int, int);
44 static int d_forwline(int, int);
45 static int d_backline(int, int);
46 static void reaper(int);
48 extern struct keymap_s helpmap, cXmap, metamap;
50 static PF dirednul[] = {
62 static PF diredcl[] = {
72 universal_argument, /* ^U */
78 static PF diredcz[] = {
86 d_shell_command, /* ! */
96 d_create_directory /* + */
99 static PF diredc[] = {
106 static PF diredn[] = {
108 d_ffotherwindow, /* o */
120 static PF direddl[] = {
124 static PF diredbp[] = {
128 static PF dirednull[] = {
133 #define NDIRED_XMAPS 0 /* number of extra map sections */
134 #endif /* DIRED_XMAPS */
136 static struct KEYMAPE (1 + IMAPEXT) d_backpagemap = {
142 'v', 'v', diredbp, NULL
147 static struct KEYMAPE (7 + NDIRED_XMAPS + IMAPEXT) diredmap = {
149 7 + NDIRED_XMAPS + IMAPEXT,
153 CCHR('@'), CCHR('H'), dirednul, (KEYMAP *) & helpmap
156 CCHR('L'), CCHR('X'), diredcl, (KEYMAP *) & cXmap
159 CCHR('['), CCHR('['), dirednull, (KEYMAP *) &
163 CCHR('Z'), '+', diredcz, (KEYMAP *) & metamap
166 'c', 'f', diredc, NULL
169 'n', 'x', diredn, NULL
172 CCHR('?'), CCHR('?'), direddl, NULL
175 DIRED_XMAPS, /* map sections for dired mode keys */
176 #endif /* DIRED_XMAPS */
183 funmap_add(dired, "dired");
184 funmap_add(d_undelbak, "dired-backup-unflag");
185 funmap_add(d_copy, "dired-copy-file");
186 funmap_add(d_expunge, "dired-do-deletions");
187 funmap_add(d_findfile, "dired-find-file");
188 funmap_add(d_ffotherwindow, "dired-find-file-other-window");
189 funmap_add(d_del, "dired-flag-file-deleted");
190 funmap_add(d_forwline, "dired-next-line");
191 funmap_add(d_otherwindow, "dired-other-window");
192 funmap_add(d_backline, "dired-previous-line");
193 funmap_add(d_rename, "dired-rename-file");
194 funmap_add(d_backpage, "dired-scroll-down");
195 funmap_add(d_forwpage, "dired-scroll-up");
196 funmap_add(d_undel, "dired-unflag");
197 maps_add((KEYMAP *)&diredmap, "dired");
198 dobindkey(fundamental_map, "dired", "^Xd");
205 char dname[NFILEN], *bufp, *slash;
208 if (curbp->b_fname && curbp->b_fname[0] != '\0') {
209 (void)strlcpy(dname, curbp->b_fname, sizeof(dname));
210 if ((slash = strrchr(dname, '/')) != NULL) {
214 if (getcwd(dname, sizeof(dname)) == NULL)
218 if ((bufp = eread("Dired: ", dname, NFILEN,
219 EFDEF | EFNEW | EFCR)) == NULL)
223 if ((bp = dired_(bufp)) == NULL)
227 return (showbuffer(bp, curwp, WFFULL | WFMODE));
232 d_otherwindow(int f, int n)
234 char dname[NFILEN], *bufp, *slash;
238 if (curbp->b_fname && curbp->b_fname[0] != '\0') {
239 (void)strlcpy(dname, curbp->b_fname, sizeof(dname));
240 if ((slash = strrchr(dname, '/')) != NULL) {
244 if (getcwd(dname, sizeof(dname)) == NULL)
248 if ((bufp = eread("Dired other window: ", dname, NFILEN,
249 EFDEF | EFNEW | EFCR)) == NULL)
251 else if (bufp[0] == '\0')
253 if ((bp = dired_(bufp)) == NULL)
255 if ((wp = popbuf(bp, WNONE)) == NULL)
269 if (llength(curwp->w_dotp) > 0)
270 lputc(curwp->w_dotp, 0, 'D');
271 if (lforw(curwp->w_dotp) != curbp->b_headp)
272 curwp->w_dotp = lforw(curwp->w_dotp);
274 curwp->w_rflag |= WFEDIT | WFMOVE;
281 d_undel(int f, int n)
284 return (d_undelbak(f, -n));
286 if (llength(curwp->w_dotp) > 0)
287 lputc(curwp->w_dotp, 0, ' ');
288 if (lforw(curwp->w_dotp) != curbp->b_headp)
289 curwp->w_dotp = lforw(curwp->w_dotp);
291 curwp->w_rflag |= WFEDIT | WFMOVE;
298 d_undelbak(int f, int n)
301 return (d_undel(f, -n));
303 if (llength(curwp->w_dotp) > 0)
304 lputc(curwp->w_dotp, 0, ' ');
305 if (lback(curwp->w_dotp) != curbp->b_headp)
306 curwp->w_dotp = lback(curwp->w_dotp);
309 curwp->w_rflag |= WFEDIT | WFMOVE;
315 d_findfile(int f, int n)
321 if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT)
326 bp = findbuffer(fname);
330 if (showbuffer(bp, curwp, WFFULL) != TRUE)
332 if (bp->b_fname[0] != 0)
334 return (readin(fname));
339 d_ffotherwindow(int f, int n)
346 if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT)
348 if ((bp = (s ? dired_(fname) : findbuffer(fname))) == NULL)
350 if ((wp = popbuf(bp, WNONE)) == NULL)
354 if (bp->b_fname[0] != 0)
355 return (TRUE); /* never true for dired buffers */
356 return (readin(fname));
361 d_expunge(int f, int n)
363 struct line *lp, *nlp;
364 char fname[NFILEN], sname[NFILEN];
366 for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) {
368 if (llength(lp) && lgetc(lp, 0) == 'D') {
369 switch (d_makename(lp, fname, sizeof(fname))) {
371 ewprintf("Bad line in dired buffer");
374 if (unlink(fname) < 0) {
375 (void)xbasename(sname, fname, NFILEN);
376 ewprintf("Could not delete '%s'", sname);
381 if (rmdir(fname) < 0) {
382 (void)xbasename(sname, fname, NFILEN);
383 ewprintf("Could not delete directory "
390 curwp->w_bufp->b_lines--;
391 curwp->w_rflag |= WFFULL;
401 char frname[NFILEN], toname[NFILEN], sname[NFILEN], *bufp;
406 if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) {
407 ewprintf("Not a file");
410 off = strlcpy(toname, curbp->b_fname, sizeof(toname));
411 if (off >= sizeof(toname) - 1) { /* can't happen, really */
412 ewprintf("Directory name too long");
415 (void)xbasename(sname, frname, NFILEN);
416 bufp = eread("Copy %s to: ", toname, sizeof(toname),
417 EFDEF | EFNEW | EFCR, sname);
420 else if (bufp[0] == '\0')
422 ret = (copy(frname, toname) >= 0) ? TRUE : FALSE;
425 bp = dired_(curbp->b_fname);
426 return (showbuffer(bp, curwp, WFFULL | WFMODE));
431 d_rename(int f, int n)
433 char frname[NFILEN], toname[NFILEN], *bufp;
439 if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) {
440 ewprintf("Not a file");
443 off = strlcpy(toname, curbp->b_fname, sizeof(toname));
444 if (off >= sizeof(toname) - 1) { /* can't happen, really */
445 ewprintf("Directory name too long");
448 (void)xbasename(sname, frname, NFILEN);
449 bufp = eread("Rename %s to: ", toname,
450 sizeof(toname), EFDEF | EFNEW | EFCR, sname);
453 else if (bufp[0] == '\0')
455 ret = (rename(frname, toname) >= 0) ? TRUE : FALSE;
458 bp = dired_(curbp->b_fname);
459 return (showbuffer(bp, curwp, WFFULL | WFMODE));
464 reaper(int signo __attribute__((unused)))
466 int save_errno = errno, status;
468 while (waitpid(-1, &status, WNOHANG) >= 0)
474 * Pipe the currently selected file through a shell command.
478 d_shell_command(int f, int n)
480 char command[512], fname[MAXPATHLEN], *bufp;
485 bp = bfind("*Shell Command Output*", TRUE);
486 if (bclear(bp) != TRUE)
489 if (d_makename(curwp->w_dotp, fname, sizeof(fname)) != FALSE) {
490 ewprintf("bad line");
495 (void)xbasename(sname, fname, NFILEN);
496 bufp = eread("! on %s: ", command, sizeof(command), EFNEW, sname);
500 if (d_exec(0, bp, fname, "sh", "-c", command, NULL) != TRUE)
503 if ((wp = popbuf(bp, WNONE)) == NULL)
504 return (ABORT); /* XXX - free the buffer?? */
511 * Pipe input file to cmd and insert the command's output in the
512 * given buffer. Each line will be prefixed with the given
516 d_exec(int space, struct buffer *bp, const char *input, const char *cmd, ...)
520 struct sigaction olda, newa;
521 char **argv = NULL, *cp;
523 int fds[2] = { -1, -1 };
525 int ret = (ABORT), n;
528 if (sigaction(SIGCHLD, NULL, &olda) == -1)
531 /* Find the number of arguments. */
533 for (n = 2; va_arg(ap, char *) != NULL; n++)
537 /* Allocate and build the argv. */
538 if ((argv = calloc(n, sizeof(*argv))) == NULL) {
539 ewprintf("Can't allocate argv : %s", strerror(errno));
544 argv[0] = (char *)cmd;
546 while ((argv[n] = va_arg(ap, char *)) != NULL)
553 if ((infd = open(input, O_RDONLY)) == -1) {
554 ewprintf("Can't open input file : %s", strerror(errno));
558 if (pipe(fds) == -1) {
559 ewprintf("Can't create pipe : %s", strerror(errno));
563 newa.sa_handler = reaper;
565 if (sigaction(SIGCHLD, &newa, NULL) == -1)
568 if ((pid = fork()) == -1) {
569 ewprintf("Can't fork");
576 dup2(infd, STDIN_FILENO);
577 dup2(fds[1], STDOUT_FILENO);
578 dup2(fds[1], STDERR_FILENO);
579 if (execvp(argv[0], argv) == -1)
580 ewprintf("Can't exec %s: %s", argv[0], strerror(errno));
583 default: /* Parent */
587 if ((fin = fdopen(fds[0], "r")) == NULL)
589 while (fgets(buf, sizeof(buf), fin) != NULL) {
590 cp = strrchr(buf, '\n');
591 if (cp == NULL && !feof(fin)) { /* too long a line */
593 addlinef(bp, "%*s%s...", space, "", buf);
594 while ((c = getc(fin)) != EOF && c != '\n')
599 addlinef(bp, "%*s%s", space, "", buf);
607 if (sigaction(SIGCHLD, &olda, NULL) == -1)
608 ewprintf("Warning, couldn't reset previous signal handler");
622 d_create_directory(int f, int n)
624 char tocreate[MAXPATHLEN], *bufp;
628 off = strlcpy(tocreate, curbp->b_fname, sizeof(tocreate));
629 if (off >= sizeof(tocreate) - 1)
631 if ((bufp = eread("Create directory: ", tocreate,
632 sizeof(tocreate), EFDEF | EFNEW | EFCR)) == NULL)
634 else if (bufp[0] == '\0')
636 if (mkdir(tocreate, 0755) == -1) {
637 ewprintf("Creating directory: %s, %s", strerror(errno),
641 bp = dired_(curbp->b_fname);
642 return (showbuffer(bp, curwp, WFFULL | WFMODE));
646 d_makename(struct line *lp, char *fn, size_t len)
651 if (d_warpdot(lp, &start) == FALSE)
653 namep = &lp->l_text[start];
654 nlen = llength(lp) - start;
656 if (snprintf(fn, len, "%s%.*s", curbp->b_fname, nlen, namep) >= len)
657 return (ABORT); /* Name is too long. */
659 /* Return TRUE if the entry is a directory. */
660 return ((lgetc(lp, 2) == 'd') ? TRUE : FALSE);
666 d_warpdot(struct line *dotp, int *doto)
668 char *tp = dotp->l_text;
669 int off = 0, field = 0, len;
672 * Find the byte offset to the (space-delimited) filename
673 * field in formatted ls output.
677 if (tp[off++] == ' ') {
678 if (++field == NAME_FIELD) {
682 /* Skip the space. */
683 while (off < len && tp[off] == ' ')
687 /* We didn't find the field. */
693 d_forwpage(int f, int n)
695 forwpage(f | FFRAND, n);
696 return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
700 d_backpage (int f, int n)
702 backpage(f | FFRAND, n);
703 return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
707 d_forwline (int f, int n)
709 forwline(f | FFRAND, n);
710 return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
714 d_backline (int f, int n)
716 backline(f | FFRAND, n);
717 return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
721 * XXX dname needs to have enough place to store an additional '/'.
729 if ((fopen(dname,"r")) == NULL) {
731 ewprintf("Permission denied");
734 if ((dname = adjustname(dname, FALSE)) == NULL) {
735 ewprintf("Bad directory name");
738 /* this should not be done, instead adjustname() should get a flag */
740 if (dname[len - 1] != '/') {
744 if ((bp = findbuffer(dname)) == NULL) {
745 ewprintf("Could not create buffer");
748 if (bclear(bp) != TRUE)
750 bp->b_flag |= BFREADONLY;
752 if ((d_exec(2, bp, NULL, "ls", "-al", dname, NULL)) != TRUE)
755 /* Find the line with ".." on it. */
756 bp->b_dotp = bfirstlp(bp);
757 for (i = 0; i < bp->b_lines; i++) {
758 bp->b_dotp = lforw(bp->b_dotp);
759 if (d_warpdot(bp->b_dotp, &bp->b_doto) == FALSE)
761 if (strcmp(ltext(bp->b_dotp) + bp->b_doto, "..") == 0)
765 /* We want dot on the entry right after "..", if possible. */
766 if (++i < bp->b_lines - 2)
767 bp->b_dotp = lforw(bp->b_dotp);
768 d_warpdot(bp->b_dotp, &bp->b_doto);
770 (void)strlcpy(bp->b_fname, dname, sizeof(bp->b_fname));
771 (void)strlcpy(bp->b_cwd, dname, sizeof(bp->b_cwd));
772 if ((bp->b_modes[1] = name_mode("dired")) == NULL) {
773 bp->b_modes[0] = name_mode("fundamental");
774 ewprintf("Could not find mode dired");