X-Git-Url: https://pd.if.org/git/?p=pd_readline;a=blobdiff_plain;f=mg%2Fdired.c;fp=mg%2Fdired.c;h=184c9f7de4ba44559cf30a9af7b7c3886d8740ec;hp=0000000000000000000000000000000000000000;hb=a9843085ec916c175bd245a8398f30e6cc03f984;hpb=26fe4e09c6c3c250334fdeed60ce3061febecde2 diff --git a/mg/dired.c b/mg/dired.c new file mode 100644 index 0000000..184c9f7 --- /dev/null +++ b/mg/dired.c @@ -0,0 +1,779 @@ +/* $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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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); +}