diff options
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/mg/README | 4 | ||||
-rw-r--r-- | usr.bin/mg/def.h | 4 | ||||
-rw-r--r-- | usr.bin/mg/funmap.c | 4 | ||||
-rw-r--r-- | usr.bin/mg/keymap.c | 6 | ||||
-rw-r--r-- | usr.bin/mg/mg.1 | 13 | ||||
-rw-r--r-- | usr.bin/mg/region.c | 230 |
6 files changed, 252 insertions, 9 deletions
diff --git a/usr.bin/mg/README b/usr.bin/mg/README index 7ca5573ab27..6db7d63b425 100644 --- a/usr.bin/mg/README +++ b/usr.bin/mg/README @@ -61,7 +61,9 @@ recognized as special cases. On systems with 16 bit integers, the kill buffer cannot exceed 32767 bytes. - +Unlike GNU Emacs, Mg's minibuffer isn't multi-line aware and hence +some commands like "shell-command-on-region" always pop up a buffer to +display output irrespective of output's size. New implementation oddities: diff --git a/usr.bin/mg/def.h b/usr.bin/mg/def.h index dab446b461a..867f3b127ca 100644 --- a/usr.bin/mg/def.h +++ b/usr.bin/mg/def.h @@ -1,4 +1,4 @@ -/* $OpenBSD: def.h,v 1.118 2011/12/10 14:09:48 lum Exp $ */ +/* $OpenBSD: def.h,v 1.119 2012/04/11 17:51:10 lum Exp $ */ /* This file is in the public domain. */ @@ -567,6 +567,8 @@ int prefixregion(int, int); int setprefix(int, int); int region_get_data(struct region *, char *, int); void region_put_data(const char *, int); +int markbuffer(int, int); +int piperegion(int, int); /* search.c X */ int forwsearch(int, int); diff --git a/usr.bin/mg/funmap.c b/usr.bin/mg/funmap.c index a1c05f77352..3a1132b0fbb 100644 --- a/usr.bin/mg/funmap.c +++ b/usr.bin/mg/funmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: funmap.c,v 1.36 2012/03/14 13:56:35 lum Exp $ */ +/* $OpenBSD: funmap.c,v 1.37 2012/04/11 17:51:10 lum Exp $ */ /* This file is in the public domain */ @@ -113,6 +113,7 @@ static struct funmap functnames[] = { {localbind, "local-set-key",}, {localunbind, "local-unset-key",}, {makebkfile, "make-backup-files",}, + {markbuffer, "mark-whole-buffer",}, {do_meta, "meta-key-mode",}, /* better name, anyone? */ {negative_argument, "negative-argument",}, {newline, "newline",}, @@ -166,6 +167,7 @@ static struct funmap functnames[] = { {setfillcol, "set-fill-column",}, {setmark, "set-mark-command",}, {setprefix, "set-prefix-string",}, + {piperegion, "shell-command-on-region",}, {shrinkwind, "shrink-window",}, #ifdef NOTAB {space_to_tabstop, "space-to-tabstop",}, diff --git a/usr.bin/mg/keymap.c b/usr.bin/mg/keymap.c index 2fde87e59e6..5bd97a920b2 100644 --- a/usr.bin/mg/keymap.c +++ b/usr.bin/mg/keymap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: keymap.c,v 1.47 2012/03/14 13:56:35 lum Exp $ */ +/* $OpenBSD: keymap.c,v 1.48 2012/04/11 17:51:10 lum Exp $ */ /* This file is in the public domain. */ @@ -135,7 +135,7 @@ static PF cXcar[] = { #endif /* !NO_MACRO */ setfillcol, /* f */ gotoline, /* g */ - rescan, /* h */ + markbuffer, /* h */ fileinsert, /* i */ rescan, /* j */ killbuffer_cmd, /* k */ @@ -257,7 +257,7 @@ static PF metal[] = { rescan, /* y */ rescan, /* z */ gotobop, /* { */ - rescan, /* | */ + piperegion, /* | */ gotoeop /* } */ }; diff --git a/usr.bin/mg/mg.1 b/usr.bin/mg/mg.1 index b1428a7e310..a2133b77445 100644 --- a/usr.bin/mg/mg.1 +++ b/usr.bin/mg/mg.1 @@ -1,7 +1,7 @@ -.\" $OpenBSD: mg.1,v 1.58 2012/02/09 09:00:14 lum Exp $ +.\" $OpenBSD: mg.1,v 1.59 2012/04/11 17:51:10 lum Exp $ .\" This file is in the public domain. .\" -.Dd $Mdocdate: February 9 2012 $ +.Dd $Mdocdate: April 11 2012 $ .Dt MG 1 .Os .Sh NAME @@ -196,6 +196,8 @@ call-last-kbd-macro set-fill-column .It C-x g goto-line +.It C-x h +mark-whole-buffer .It C-x i insert-file .It C-x k @@ -260,6 +262,8 @@ copy-region-as-kill execute-extended-command .It M-{ backward-paragraph +.It M-| +shell-command-on-region .It M-} forward-paragraph .It M-~ @@ -572,6 +576,9 @@ Bind a key mapping in the local (topmost) mode. Unbind a key mapping in the local (topmost) mode. .It make-backup-files Toggle generation of backup files. +.It mark-whole-buffer +Marks whole buffer as a region by putting dot at the beginning and mark +at the end of buffer. .It meta-key-mode When disabled, the meta key can be used to insert extended-ascii (8-bit) characters. @@ -734,6 +741,8 @@ Used by auto-fill-mode. Sets the mark in the current window to the current dot location. .It set-prefix-string Sets the prefix string to be used by the 'prefix-region' command. +.It shell-command-on-region +Provide the text in region to the shell command as input. .It shrink-window Shrink current window by one line. The window immediately below is expanded to pick up the slack. diff --git a/usr.bin/mg/region.c b/usr.bin/mg/region.c index eba11a25486..baca932ca1f 100644 --- a/usr.bin/mg/region.c +++ b/usr.bin/mg/region.c @@ -1,4 +1,4 @@ -/* $OpenBSD: region.c,v 1.29 2009/06/05 18:02:06 kjell Exp $ */ +/* $OpenBSD: region.c,v 1.30 2012/04/11 17:51:10 lum Exp $ */ /* This file is in the public domain. */ @@ -9,9 +9,25 @@ * internal use. */ +#include <sys/types.h> +#include <sys/socket.h> + +#include <fcntl.h> +#include <poll.h> +#include <string.h> +#include <unistd.h> + #include "def.h" +#define TIMEOUT 10000 + +static char leftover[BUFSIZ]; + static int getregion(struct region *); +static int iomux(int); +static int pipeio(const char *); +static int preadin(int, struct buffer *); +static void pwriteout(int, char **, int *); static int setsize(struct region *, RSIZE); /* @@ -367,3 +383,215 @@ region_put_data(const char *buf, int len) linsert(1, buf[i]); } } + +/* + * Mark whole buffer by first traversing to end-of-buffer + * and then to beginning-of-buffer. Mark, dot are implicitly + * set to eob, bob respectively during traversal. + */ +int +markbuffer(int f, int n) +{ + if (gotoeob(f,n) == FALSE) + return (FALSE); + if (gotobob(f,n) == FALSE) + return (FALSE); + return (TRUE); +} + +/* + * Pipe text from current region to external command. + */ +/*ARGSUSED */ +int +piperegion(int f, int n) +{ + char *cmd, cmdbuf[NFILEN]; + + /* C-u M-| is not supported yet */ + if (n > 1) + return (ABORT); + + if (curwp->w_markp == NULL) { + ewprintf("The mark is not set now, so there is no region"); + return (FALSE); + } + if ((cmd = eread("Shell command on region: ", cmdbuf, sizeof(cmdbuf), + EFNEW | EFCR)) == NULL || (cmd[0] == '\0')) + return (ABORT); + + return (pipeio(cmdbuf)); +} + +/* + * Create a socketpair, fork and execl cmd passed. STDIN, STDOUT + * and STDERR of child process are redirected to socket. + */ +int +pipeio(const char* const cmd) +{ + int s[2]; + char *shellp; + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s) == -1) { + ewprintf("socketpair error"); + return (FALSE); + } + switch(fork()) { + case -1: + ewprintf("Can't fork"); + return (FALSE); + case 0: + /* Child process */ + close(s[0]); + if (dup2(s[1], STDIN_FILENO) == -1) + _exit(1); + if (dup2(s[1], STDOUT_FILENO) == -1) + _exit(1); + if (dup2(s[1], STDERR_FILENO) == -1) + _exit(1); + if ((shellp = getenv("SHELL")) == NULL) + _exit(1); + execl(shellp, "sh", "-c", cmd, (char *)NULL); + _exit(1); + default: + /* Parent process */ + close(s[1]); + return iomux(s[0]); + } + return (FALSE); +} + +/* + * Multiplex read, write on socket fd passed. First get the region, + * find/create *Shell Command Output* buffer and clear it's contents. + * Poll on the fd for both read and write readiness. + */ +int +iomux(int fd) +{ + struct region region; + struct buffer *bp; + struct pollfd pfd[1]; + int nfds; + char *text, *textcopy; + + if (getregion(®ion) != TRUE) + return (FALSE); + + if ((text = malloc(region.r_size + 1)) == NULL) + return (ABORT); + + region_get_data(®ion, text, region.r_size); + textcopy = text; + fcntl(fd, F_SETFL, O_NONBLOCK); + + /* There is nothing to write if r_size is zero + * but the cmd's output should be read so shutdown + * the socket for writing only. + */ + if (region.r_size == 0) + shutdown(fd, SHUT_WR); + + bp = bfind("*Shell Command Output*", TRUE); + bp->b_flag |= BFREADONLY; + if (bclear(bp) != TRUE) + return (FALSE); + + pfd[0].fd = fd; + pfd[0].events = POLLIN | POLLOUT; + while ((nfds = poll(pfd, 1, TIMEOUT)) != -1 || + (pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL))) { + if (pfd[0].revents & POLLOUT && region.r_size > 0) + pwriteout(fd, &textcopy, ®ion.r_size); + else if (pfd[0].revents & POLLIN) + if (preadin(fd, bp) == FALSE) + break; + } + close(fd); + free(text); + /* In case if last line doesn't have a '\n' add the leftover + * characters to buffer. + */ + if (leftover[0] != '\0') { + addline(bp, leftover); + leftover[0] = '\0'; + } + if (nfds == 0) { + ewprintf("poll timed out"); + return (FALSE); + } else if (nfds == -1) { + ewprintf("poll error"); + return (FALSE); + } + return (popbuftop(bp, WNONE)); +} + +/* + * Write some text from region to fd. Once done shutdown the + * write end. + */ +void +pwriteout(int fd, char **text, int *len) +{ + int w; + + if (((w = send(fd, *text, *len, MSG_NOSIGNAL)) == -1)) { + switch(errno) { + case EPIPE: + *len = -1; + break; + case EAGAIN: + return; + } + } else + *len -= w; + + *text += w; + if (*len <= 0) + shutdown(fd, SHUT_WR); +} + +/* + * Read some data from socket fd, break on '\n' and add + * to buffer. If couldn't break on newline hold leftover + * characters and append in next iteration. + */ +int +preadin(int fd, struct buffer *bp) +{ + int len; + static int nooutput; + char buf[BUFSIZ], *p, *q; + + if ((len = read(fd, buf, BUFSIZ - 1)) == 0) { + if (nooutput == 0) + addline(bp, "(Shell command succeeded with no output)"); + nooutput = 0; + return (FALSE); + } + nooutput = 1; + buf[len] = '\0'; + p = q = buf; + if (leftover[0] != '\0' && ((q = strchr(p, '\n')) != NULL)) { + *q++ = '\0'; + if (strlcat(leftover, p, sizeof(leftover)) >= + sizeof(leftover)) { + ewprintf("line too long"); + return (FALSE); + } + addline(bp, leftover); + leftover[0] = '\0'; + p = q; + } + while ((q = strchr(p, '\n')) != NULL) { + *q++ = '\0'; + addline(bp, p); + p = q; + } + if (strlcpy(leftover, p, sizeof(leftover)) >= sizeof(leftover)) { + ewprintf("line too long"); + return (FALSE); + } + return (TRUE); +} |