summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/mg/README4
-rw-r--r--usr.bin/mg/def.h4
-rw-r--r--usr.bin/mg/funmap.c4
-rw-r--r--usr.bin/mg/keymap.c6
-rw-r--r--usr.bin/mg/mg.113
-rw-r--r--usr.bin/mg/region.c230
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(&region) != TRUE)
+ return (FALSE);
+
+ if ((text = malloc(region.r_size + 1)) == NULL)
+ return (ABORT);
+
+ region_get_data(&region, 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, &region.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);
+}