--- /dev/null
+/* $OpenBSD: dired.c,v 1.51 2012/03/14 13:56:35 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/* dired module for mg 2a
+ * by Robert A. Larson
+ */
+
+#include "def.h"
+#include "funmap.h"
+#include "kbd.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <libgen.h>
+#include <stdarg.h>
+
+void dired_init(void);
+static int dired(int, int);
+static int d_otherwindow(int, int);
+static int d_undel(int, int);
+static int d_undelbak(int, int);
+static int d_findfile(int, int);
+static int d_ffotherwindow(int, int);
+static int d_expunge(int, int);
+static int d_copy(int, int);
+static int d_del(int, int);
+static int d_rename(int, int);
+static int d_exec(int, struct buffer *, const char *, const char *, ...);
+static int d_shell_command(int, int);
+static int d_create_directory(int, int);
+static int d_makename(struct line *, char *, size_t);
+static int d_warpdot(struct line *, int *);
+static int d_forwpage(int, int);
+static int d_backpage(int, int);
+static int d_forwline(int, int);
+static int d_backline(int, int);
+static void reaper(int);
+
+extern struct keymap_s helpmap, cXmap, metamap;
+
+static PF dirednul[] = {
+ setmark, /* ^@ */
+ gotobol, /* ^A */
+ backchar, /* ^B */
+ rescan, /* ^C */
+ d_del, /* ^D */
+ gotoeol, /* ^E */
+ forwchar, /* ^F */
+ ctrlg, /* ^G */
+ NULL, /* ^H */
+};
+
+static PF diredcl[] = {
+ reposition, /* ^L */
+ d_findfile, /* ^M */
+ d_forwline, /* ^N */
+ rescan, /* ^O */
+ d_backline, /* ^P */
+ rescan, /* ^Q */
+ backisearch, /* ^R */
+ forwisearch, /* ^S */
+ rescan, /* ^T */
+ universal_argument, /* ^U */
+ d_forwpage, /* ^V */
+ rescan, /* ^W */
+ NULL /* ^X */
+};
+
+static PF diredcz[] = {
+ spawncli, /* ^Z */
+ NULL, /* esc */
+ rescan, /* ^\ */
+ rescan, /* ^] */
+ rescan, /* ^^ */
+ rescan, /* ^_ */
+ d_forwline, /* SP */
+ d_shell_command, /* ! */
+ rescan, /* " */
+ rescan, /* # */
+ rescan, /* $ */
+ rescan, /* % */
+ rescan, /* & */
+ rescan, /* ' */
+ rescan, /* ( */
+ rescan, /* ) */
+ rescan, /* * */
+ d_create_directory /* + */
+};
+
+static PF diredc[] = {
+ d_copy, /* c */
+ d_del, /* d */
+ d_findfile, /* e */
+ d_findfile /* f */
+};
+
+static PF diredn[] = {
+ d_forwline, /* n */
+ d_ffotherwindow, /* o */
+ d_backline, /* p */
+ rescan, /* q */
+ d_rename, /* r */
+ rescan, /* s */
+ rescan, /* t */
+ d_undel, /* u */
+ rescan, /* v */
+ rescan, /* w */
+ d_expunge /* x */
+};
+
+static PF direddl[] = {
+ d_undelbak /* del */
+};
+
+static PF diredbp[] = {
+ d_backpage /* v */
+};
+
+static PF dirednull[] = {
+ NULL
+};
+
+#ifndef DIRED_XMAPS
+#define NDIRED_XMAPS 0 /* number of extra map sections */
+#endif /* DIRED_XMAPS */
+
+static struct KEYMAPE (1 + IMAPEXT) d_backpagemap = {
+ 1,
+ 1 + IMAPEXT,
+ rescan,
+ {
+ {
+ 'v', 'v', diredbp, NULL
+ }
+ }
+};
+
+static struct KEYMAPE (7 + NDIRED_XMAPS + IMAPEXT) diredmap = {
+ 7 + NDIRED_XMAPS,
+ 7 + NDIRED_XMAPS + IMAPEXT,
+ rescan,
+ {
+ {
+ CCHR('@'), CCHR('H'), dirednul, (KEYMAP *) & helpmap
+ },
+ {
+ CCHR('L'), CCHR('X'), diredcl, (KEYMAP *) & cXmap
+ },
+ {
+ CCHR('['), CCHR('['), dirednull, (KEYMAP *) &
+ d_backpagemap
+ },
+ {
+ CCHR('Z'), '+', diredcz, (KEYMAP *) & metamap
+ },
+ {
+ 'c', 'f', diredc, NULL
+ },
+ {
+ 'n', 'x', diredn, NULL
+ },
+ {
+ CCHR('?'), CCHR('?'), direddl, NULL
+ },
+#ifdef DIRED_XMAPS
+ DIRED_XMAPS, /* map sections for dired mode keys */
+#endif /* DIRED_XMAPS */
+ }
+};
+
+void
+dired_init(void)
+{
+ funmap_add(dired, "dired");
+ funmap_add(d_undelbak, "dired-backup-unflag");
+ funmap_add(d_copy, "dired-copy-file");
+ funmap_add(d_expunge, "dired-do-deletions");
+ funmap_add(d_findfile, "dired-find-file");
+ funmap_add(d_ffotherwindow, "dired-find-file-other-window");
+ funmap_add(d_del, "dired-flag-file-deleted");
+ funmap_add(d_forwline, "dired-next-line");
+ funmap_add(d_otherwindow, "dired-other-window");
+ funmap_add(d_backline, "dired-previous-line");
+ funmap_add(d_rename, "dired-rename-file");
+ funmap_add(d_backpage, "dired-scroll-down");
+ funmap_add(d_forwpage, "dired-scroll-up");
+ funmap_add(d_undel, "dired-unflag");
+ maps_add((KEYMAP *)&diredmap, "dired");
+ dobindkey(fundamental_map, "dired", "^Xd");
+}
+
+/* ARGSUSED */
+int
+dired(int f, int n)
+{
+ char dname[NFILEN], *bufp, *slash;
+ struct buffer *bp;
+
+ if (curbp->b_fname && curbp->b_fname[0] != '\0') {
+ (void)strlcpy(dname, curbp->b_fname, sizeof(dname));
+ if ((slash = strrchr(dname, '/')) != NULL) {
+ *(slash + 1) = '\0';
+ }
+ } else {
+ if (getcwd(dname, sizeof(dname)) == NULL)
+ dname[0] = '\0';
+ }
+
+ if ((bufp = eread("Dired: ", dname, NFILEN,
+ EFDEF | EFNEW | EFCR)) == NULL)
+ return (ABORT);
+ if (bufp[0] == '\0')
+ return (FALSE);
+ if ((bp = dired_(bufp)) == NULL)
+ return (FALSE);
+
+ curbp = bp;
+ return (showbuffer(bp, curwp, WFFULL | WFMODE));
+}
+
+/* ARGSUSED */
+int
+d_otherwindow(int f, int n)
+{
+ char dname[NFILEN], *bufp, *slash;
+ struct buffer *bp;
+ struct mgwin *wp;
+
+ if (curbp->b_fname && curbp->b_fname[0] != '\0') {
+ (void)strlcpy(dname, curbp->b_fname, sizeof(dname));
+ if ((slash = strrchr(dname, '/')) != NULL) {
+ *(slash + 1) = '\0';
+ }
+ } else {
+ if (getcwd(dname, sizeof(dname)) == NULL)
+ dname[0] = '\0';
+ }
+
+ if ((bufp = eread("Dired other window: ", dname, NFILEN,
+ EFDEF | EFNEW | EFCR)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ if ((bp = dired_(bufp)) == NULL)
+ return (FALSE);
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ return (FALSE);
+ curbp = bp;
+ curwp = wp;
+ return (TRUE);
+}
+
+/* ARGSUSED */
+int
+d_del(int f, int n)
+{
+ if (n < 0)
+ return (FALSE);
+ while (n--) {
+ if (llength(curwp->w_dotp) > 0)
+ lputc(curwp->w_dotp, 0, 'D');
+ if (lforw(curwp->w_dotp) != curbp->b_headp)
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ }
+ curwp->w_rflag |= WFEDIT | WFMOVE;
+ curwp->w_doto = 0;
+ return (TRUE);
+}
+
+/* ARGSUSED */
+int
+d_undel(int f, int n)
+{
+ if (n < 0)
+ return (d_undelbak(f, -n));
+ while (n--) {
+ if (llength(curwp->w_dotp) > 0)
+ lputc(curwp->w_dotp, 0, ' ');
+ if (lforw(curwp->w_dotp) != curbp->b_headp)
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ }
+ curwp->w_rflag |= WFEDIT | WFMOVE;
+ curwp->w_doto = 0;
+ return (TRUE);
+}
+
+/* ARGSUSED */
+int
+d_undelbak(int f, int n)
+{
+ if (n < 0)
+ return (d_undel(f, -n));
+ while (n--) {
+ if (llength(curwp->w_dotp) > 0)
+ lputc(curwp->w_dotp, 0, ' ');
+ if (lback(curwp->w_dotp) != curbp->b_headp)
+ curwp->w_dotp = lback(curwp->w_dotp);
+ }
+ curwp->w_doto = 0;
+ curwp->w_rflag |= WFEDIT | WFMOVE;
+ return (TRUE);
+}
+
+/* ARGSUSED */
+int
+d_findfile(int f, int n)
+{
+ struct buffer *bp;
+ int s;
+ char fname[NFILEN];
+
+ if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT)
+ return (FALSE);
+ if (s == TRUE)
+ bp = dired_(fname);
+ else
+ bp = findbuffer(fname);
+ if (bp == NULL)
+ return (FALSE);
+ curbp = bp;
+ if (showbuffer(bp, curwp, WFFULL) != TRUE)
+ return (FALSE);
+ if (bp->b_fname[0] != 0)
+ return (TRUE);
+ return (readin(fname));
+}
+
+/* ARGSUSED */
+int
+d_ffotherwindow(int f, int n)
+{
+ char fname[NFILEN];
+ int s;
+ struct buffer *bp;
+ struct mgwin *wp;
+
+ if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT)
+ return (FALSE);
+ if ((bp = (s ? dired_(fname) : findbuffer(fname))) == NULL)
+ return (FALSE);
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ return (FALSE);
+ curbp = bp;
+ curwp = wp;
+ if (bp->b_fname[0] != 0)
+ return (TRUE); /* never true for dired buffers */
+ return (readin(fname));
+}
+
+/* ARGSUSED */
+int
+d_expunge(int f, int n)
+{
+ struct line *lp, *nlp;
+ char fname[NFILEN], sname[NFILEN];
+
+ for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) {
+ nlp = lforw(lp);
+ if (llength(lp) && lgetc(lp, 0) == 'D') {
+ switch (d_makename(lp, fname, sizeof(fname))) {
+ case ABORT:
+ ewprintf("Bad line in dired buffer");
+ return (FALSE);
+ case FALSE:
+ if (unlink(fname) < 0) {
+ (void)xbasename(sname, fname, NFILEN);
+ ewprintf("Could not delete '%s'", sname);
+ return (FALSE);
+ }
+ break;
+ case TRUE:
+ if (rmdir(fname) < 0) {
+ (void)xbasename(sname, fname, NFILEN);
+ ewprintf("Could not delete directory "
+ "'%s'", sname);
+ return (FALSE);
+ }
+ break;
+ }
+ lfree(lp);
+ curwp->w_bufp->b_lines--;
+ curwp->w_rflag |= WFFULL;
+ }
+ }
+ return (TRUE);
+}
+
+/* ARGSUSED */
+int
+d_copy(int f, int n)
+{
+ char frname[NFILEN], toname[NFILEN], sname[NFILEN], *bufp;
+ int ret;
+ size_t off;
+ struct buffer *bp;
+
+ if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) {
+ ewprintf("Not a file");
+ return (FALSE);
+ }
+ off = strlcpy(toname, curbp->b_fname, sizeof(toname));
+ if (off >= sizeof(toname) - 1) { /* can't happen, really */
+ ewprintf("Directory name too long");
+ return (FALSE);
+ }
+ (void)xbasename(sname, frname, NFILEN);
+ bufp = eread("Copy %s to: ", toname, sizeof(toname),
+ EFDEF | EFNEW | EFCR, sname);
+ if (bufp == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ ret = (copy(frname, toname) >= 0) ? TRUE : FALSE;
+ if (ret != TRUE)
+ return (ret);
+ bp = dired_(curbp->b_fname);
+ return (showbuffer(bp, curwp, WFFULL | WFMODE));
+}
+
+/* ARGSUSED */
+int
+d_rename(int f, int n)
+{
+ char frname[NFILEN], toname[NFILEN], *bufp;
+ int ret;
+ size_t off;
+ struct buffer *bp;
+ char sname[NFILEN];
+
+ if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) {
+ ewprintf("Not a file");
+ return (FALSE);
+ }
+ off = strlcpy(toname, curbp->b_fname, sizeof(toname));
+ if (off >= sizeof(toname) - 1) { /* can't happen, really */
+ ewprintf("Directory name too long");
+ return (FALSE);
+ }
+ (void)xbasename(sname, frname, NFILEN);
+ bufp = eread("Rename %s to: ", toname,
+ sizeof(toname), EFDEF | EFNEW | EFCR, sname);
+ if (bufp == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ ret = (rename(frname, toname) >= 0) ? TRUE : FALSE;
+ if (ret != TRUE)
+ return (ret);
+ bp = dired_(curbp->b_fname);
+ return (showbuffer(bp, curwp, WFFULL | WFMODE));
+}
+
+/* ARGSUSED */
+void
+reaper(int signo __attribute__((unused)))
+{
+ int save_errno = errno, status;
+
+ while (waitpid(-1, &status, WNOHANG) >= 0)
+ ;
+ errno = save_errno;
+}
+
+/*
+ * Pipe the currently selected file through a shell command.
+ */
+/* ARGSUSED */
+int
+d_shell_command(int f, int n)
+{
+ char command[512], fname[MAXPATHLEN], *bufp;
+ struct buffer *bp;
+ struct mgwin *wp;
+ char sname[NFILEN];
+
+ bp = bfind("*Shell Command Output*", TRUE);
+ if (bclear(bp) != TRUE)
+ return (ABORT);
+
+ if (d_makename(curwp->w_dotp, fname, sizeof(fname)) != FALSE) {
+ ewprintf("bad line");
+ return (ABORT);
+ }
+
+ command[0] = '\0';
+ (void)xbasename(sname, fname, NFILEN);
+ bufp = eread("! on %s: ", command, sizeof(command), EFNEW, sname);
+ if (bufp == NULL)
+ return (ABORT);
+
+ if (d_exec(0, bp, fname, "sh", "-c", command, NULL) != TRUE)
+ return (ABORT);
+
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ return (ABORT); /* XXX - free the buffer?? */
+ curwp = wp;
+ curbp = wp->w_bufp;
+ return (TRUE);
+}
+
+/*
+ * Pipe input file to cmd and insert the command's output in the
+ * given buffer. Each line will be prefixed with the given
+ * number of spaces.
+ */
+static int
+d_exec(int space, struct buffer *bp, const char *input, const char *cmd, ...)
+{
+ char buf[BUFSIZ];
+ va_list ap;
+ struct sigaction olda, newa;
+ char **argv = NULL, *cp;
+ FILE *fin;
+ int fds[2] = { -1, -1 };
+ int infd = -1;
+ int ret = (ABORT), n;
+ pid_t pid;
+
+ if (sigaction(SIGCHLD, NULL, &olda) == -1)
+ return (ABORT);
+
+ /* Find the number of arguments. */
+ va_start(ap, cmd);
+ for (n = 2; va_arg(ap, char *) != NULL; n++)
+ ;
+ va_end(ap);
+
+ /* Allocate and build the argv. */
+ if ((argv = calloc(n, sizeof(*argv))) == NULL) {
+ ewprintf("Can't allocate argv : %s", strerror(errno));
+ goto out;
+ }
+
+ n = 1;
+ argv[0] = (char *)cmd;
+ va_start(ap, cmd);
+ while ((argv[n] = va_arg(ap, char *)) != NULL)
+ n++;
+ va_end(ap);
+
+ if (input == NULL)
+ input = "/dev/null";
+
+ if ((infd = open(input, O_RDONLY)) == -1) {
+ ewprintf("Can't open input file : %s", strerror(errno));
+ goto out;
+ }
+
+ if (pipe(fds) == -1) {
+ ewprintf("Can't create pipe : %s", strerror(errno));
+ goto out;
+ }
+
+ newa.sa_handler = reaper;
+ newa.sa_flags = 0;
+ if (sigaction(SIGCHLD, &newa, NULL) == -1)
+ goto out;
+
+ if ((pid = fork()) == -1) {
+ ewprintf("Can't fork");
+ goto out;
+ }
+
+ switch (pid) {
+ case 0: /* Child */
+ close(fds[0]);
+ dup2(infd, STDIN_FILENO);
+ dup2(fds[1], STDOUT_FILENO);
+ dup2(fds[1], STDERR_FILENO);
+ if (execvp(argv[0], argv) == -1)
+ ewprintf("Can't exec %s: %s", argv[0], strerror(errno));
+ exit(1);
+ break;
+ default: /* Parent */
+ close(infd);
+ close(fds[1]);
+ infd = fds[1] = -1;
+ if ((fin = fdopen(fds[0], "r")) == NULL)
+ goto out;
+ while (fgets(buf, sizeof(buf), fin) != NULL) {
+ cp = strrchr(buf, '\n');
+ if (cp == NULL && !feof(fin)) { /* too long a line */
+ int c;
+ addlinef(bp, "%*s%s...", space, "", buf);
+ while ((c = getc(fin)) != EOF && c != '\n')
+ ;
+ continue;
+ } else if (cp)
+ *cp = '\0';
+ addlinef(bp, "%*s%s", space, "", buf);
+ }
+ fclose(fin);
+ break;
+ }
+ ret = (TRUE);
+
+out:
+ if (sigaction(SIGCHLD, &olda, NULL) == -1)
+ ewprintf("Warning, couldn't reset previous signal handler");
+ if (fds[0] != -1)
+ close(fds[0]);
+ if (fds[1] != -1)
+ close(fds[1]);
+ if (infd != -1)
+ close(infd);
+ if (argv != NULL)
+ free(argv);
+ return ret;
+}
+
+/* ARGSUSED */
+int
+d_create_directory(int f, int n)
+{
+ char tocreate[MAXPATHLEN], *bufp;
+ size_t off;
+ struct buffer *bp;
+
+ off = strlcpy(tocreate, curbp->b_fname, sizeof(tocreate));
+ if (off >= sizeof(tocreate) - 1)
+ return (FALSE);
+ if ((bufp = eread("Create directory: ", tocreate,
+ sizeof(tocreate), EFDEF | EFNEW | EFCR)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ if (mkdir(tocreate, 0755) == -1) {
+ ewprintf("Creating directory: %s, %s", strerror(errno),
+ tocreate);
+ return (FALSE);
+ }
+ bp = dired_(curbp->b_fname);
+ return (showbuffer(bp, curwp, WFFULL | WFMODE));
+}
+
+static int
+d_makename(struct line *lp, char *fn, size_t len)
+{
+ int start, nlen;
+ char *namep;
+
+ if (d_warpdot(lp, &start) == FALSE)
+ return (ABORT);
+ namep = &lp->l_text[start];
+ nlen = llength(lp) - start;
+
+ if (snprintf(fn, len, "%s%.*s", curbp->b_fname, nlen, namep) >= len)
+ return (ABORT); /* Name is too long. */
+
+ /* Return TRUE if the entry is a directory. */
+ return ((lgetc(lp, 2) == 'd') ? TRUE : FALSE);
+}
+
+#define NAME_FIELD 9
+
+static int
+d_warpdot(struct line *dotp, int *doto)
+{
+ char *tp = dotp->l_text;
+ int off = 0, field = 0, len;
+
+ /*
+ * Find the byte offset to the (space-delimited) filename
+ * field in formatted ls output.
+ */
+ len = llength(dotp);
+ while (off < len) {
+ if (tp[off++] == ' ') {
+ if (++field == NAME_FIELD) {
+ *doto = off;
+ return (TRUE);
+ }
+ /* Skip the space. */
+ while (off < len && tp[off] == ' ')
+ off++;
+ }
+ }
+ /* We didn't find the field. */
+ *doto = 0;
+ return (FALSE);
+}
+
+static int
+d_forwpage(int f, int n)
+{
+ forwpage(f | FFRAND, n);
+ return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
+}
+
+static int
+d_backpage (int f, int n)
+{
+ backpage(f | FFRAND, n);
+ return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
+}
+
+static int
+d_forwline (int f, int n)
+{
+ forwline(f | FFRAND, n);
+ return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
+}
+
+static int
+d_backline (int f, int n)
+{
+ backline(f | FFRAND, n);
+ return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
+}
+
+/*
+ * XXX dname needs to have enough place to store an additional '/'.
+ */
+struct buffer *
+dired_(char *dname)
+{
+ struct buffer *bp;
+ int len, i;
+
+ if ((fopen(dname,"r")) == NULL) {
+ if (errno == EACCES)
+ ewprintf("Permission denied");
+ return (NULL);
+ }
+ if ((dname = adjustname(dname, FALSE)) == NULL) {
+ ewprintf("Bad directory name");
+ return (NULL);
+ }
+ /* this should not be done, instead adjustname() should get a flag */
+ len = strlen(dname);
+ if (dname[len - 1] != '/') {
+ dname[len++] = '/';
+ dname[len] = '\0';
+ }
+ if ((bp = findbuffer(dname)) == NULL) {
+ ewprintf("Could not create buffer");
+ return (NULL);
+ }
+ if (bclear(bp) != TRUE)
+ return (NULL);
+ bp->b_flag |= BFREADONLY;
+
+ if ((d_exec(2, bp, NULL, "ls", "-al", dname, NULL)) != TRUE)
+ return (NULL);
+
+ /* Find the line with ".." on it. */
+ bp->b_dotp = bfirstlp(bp);
+ for (i = 0; i < bp->b_lines; i++) {
+ bp->b_dotp = lforw(bp->b_dotp);
+ if (d_warpdot(bp->b_dotp, &bp->b_doto) == FALSE)
+ continue;
+ if (strcmp(ltext(bp->b_dotp) + bp->b_doto, "..") == 0)
+ break;
+ }
+
+ /* We want dot on the entry right after "..", if possible. */
+ if (++i < bp->b_lines - 2)
+ bp->b_dotp = lforw(bp->b_dotp);
+ d_warpdot(bp->b_dotp, &bp->b_doto);
+
+ (void)strlcpy(bp->b_fname, dname, sizeof(bp->b_fname));
+ (void)strlcpy(bp->b_cwd, dname, sizeof(bp->b_cwd));
+ if ((bp->b_modes[1] = name_mode("dired")) == NULL) {
+ bp->b_modes[0] = name_mode("fundamental");
+ ewprintf("Could not find mode dired");
+ return (NULL);
+ }
+ bp->b_nmodes = 1;
+ return (bp);
+}