s/irrespective/regardless/
On Tue, Mar 27, 2012 at 12:57 PM, Sunil Nimmagadda <
[email protected]> wrote:
> This version implements some off-list review comments...
>
> 1. Discard explicit checking whether command exists and it's
> permissions since shell already does and reports error.
>
> 2. Remove unnecessary bzero call.
>
> 3. Document minor deviation from emacs behaviour in README.
>
> Index: README
> ===================================================================
> RCS file: /cvs/src/usr.bin/mg/README,v
> retrieving revision 1.8
> diff -u -p -r1.8 README
> --- README 1 Aug 2011 12:15:23 -0000 1.8
> +++ README 20 Mar 2012 17:54:12 -0000
> @@ -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:
>
> Index: def.h
> ===================================================================
> RCS file: /cvs/src/usr.bin/mg/def.h,v
> retrieving revision 1.118
> diff -u -p -r1.118 def.h
> --- def.h 10 Dec 2011 14:09:48 -0000 1.118
> +++ def.h 16 Mar 2012 04:59:14 -0000
> @@ -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);
> Index: funmap.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mg/funmap.c,v
> retrieving revision 1.36
> diff -u -p -r1.36 funmap.c
> --- funmap.c 14 Mar 2012 13:56:35 -0000 1.36
> +++ funmap.c 16 Mar 2012 04:59:14 -0000
> @@ -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",},
> Index: keymap.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mg/keymap.c,v
> retrieving revision 1.47
> diff -u -p -r1.47 keymap.c
> --- keymap.c 14 Mar 2012 13:56:35 -0000 1.47
> +++ keymap.c 16 Mar 2012 04:59:14 -0000
> @@ -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 /* } */
> };
>
> Index: mg.1
> ===================================================================
> RCS file: /cvs/src/usr.bin/mg/mg.1,v
> retrieving revision 1.58
> diff -u -p -r1.58 mg.1
> --- mg.1 9 Feb 2012 09:00:14 -0000 1.58
> +++ mg.1 16 Mar 2012 04:59:14 -0000
> @@ -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
> 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.
> Index: region.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mg/region.c,v
> retrieving revision 1.29
> diff -u -p -r1.29 region.c
> --- region.c 5 Jun 2009 18:02:06 -0000 1.29
> +++ region.c 21 Mar 2012 17:45:24 -0000
> @@ -9,9 +9,25 @@
> * internal use.
> */
>
> +#include <sys/types.h>
> +#include <sys/socket.h>
> +
> +#include <poll.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> #include "def.h"
>
> +#define TIMEOUT 10000
> +
> +static char leftover[BUFSIZ];
> +
> +static char *get_line(struct line *, int *);
> static int getregion(struct region *);
> +static int iomux(int);
> +static int pipeio(const char *);
> +static int preadin(int,struct buffer *);
> +static void pwriteout(int,struct region *);
> static int setsize(struct region *, RSIZE);
>
> /*
> @@ -366,4 +382,228 @@ region_put_data(const char *buf, int len
> else
> 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)
> + return (FALSE);
> + else if (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;
> +
> + if (getregion(®ion) != TRUE)
> + return (FALSE);
> +
> + /* 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, ®ion);
> + region.r_linep = lforw(region.r_linep);
> + } else if (pfd[0].revents & POLLIN)
> + if (preadin(fd, bp) == FALSE)
> + break;
> + }
> + close(fd);
> + /* In case if last line doesn't have a '\n' add the leftover
> + * characters to buffer.
> + */
> + if (leftover[0] != '\0')
> + addline(bp, leftover);
> + if (nfds == 0) {
> + ewprintf("poll timed out");
> + return (FALSE);
> + } else if (nfds == -1) {
> + ewprintf("poll error");
> + return (FALSE);
> + }
> + return (popbuftop(bp, WNONE));
> +}
> +
> +/*
> + * Write each line from region to fd. Once done shutdown the
> + * write end.
> + */
> +void
> +pwriteout(int fd, struct region *region)
> +{
> + struct line *linep;
> + int len, loffs;
> + char *l;
> +
> + linep = region->r_linep;
> + loffs = region->r_offset;
> + /* Reset the offset, needed only for first line of region. */
> + region->r_offset = 0;
> + if ((l = get_line(linep, &len)) == NULL)
> + return;
> + /* Take care of region's offset on first and last line */
> + len -= loffs;
> + len = (len < region->r_size) ? len : region->r_size;
> + if ((send(fd, l + loffs, len, MSG_NOSIGNAL) == -1)
> + && (errno == EPIPE))
> + region->r_size = -1;
> + else
> + region->r_size -= len;
> + if (region->r_size <= 0)
> + shutdown(fd, SHUT_WR);
> + free(l);
> +}
> +
> +/*
> + * Since struct line doesn't have a terminating '\n',
> + * make a copy and append '\n'.
> + */
> +char *
> +get_line(struct line *ln, int *lenp)
> +{
> + int len;
> + char *line;
> +
> + len = llength(ln);
> + if (len == INT_MAX)
> + return (NULL);
> +
> + if ((line = malloc(len + 1)) == NULL)
> + return (NULL);
> +
> + (void)memmove(line, ltext(ln), len);
> + line[len] = '\n';
> + *lenp = len + 1;
> + return (line);
> +}
> +
> +/*
> + * 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;
> + char buf[BUFSIZ], *p, *q;
> +
> + if ((len = read(fd, buf, BUFSIZ - 1)) == 0)
> + return (FALSE);
> + 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);
> }