#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "def.h"
#define TIMEOUT 10000
static int getregion(struct region *);
static int iomux(int, char * const, int);
static int preadin(int);
static void pwriteout(int, char **, int *);
static int setsize(struct region *, RSIZE);
static int shellcmdoutput(char * const, char * const, int,
struct buffer *);
int
killregion(int f, int n)
{
int s;
struct region region;
if ((s = getregion(®ion)) != TRUE)
return (s);
if ((lastflag & CFKILL) == 0)
kdelete();
thisflag |= CFKILL;
curwp->w_dotp = region.r_linep;
curwp->w_doto = region.r_offset;
curwp->w_dotline = region.r_lineno;
s = ldelete(region.r_size, KFORW | KREG);
clearmark(FFARG, 0);
return (s);
}
int
copyregion(int f, int n)
{
struct line *linep;
struct region region;
int loffs;
int s;
if ((s = getregion(®ion)) != TRUE)
return (s);
if ((lastflag & CFKILL) == 0)
kdelete();
thisflag |= CFKILL;
linep = region.r_linep;
loffs = region.r_offset;
while (region.r_size--) {
if (loffs == llength(linep)) {
if ((s = kinsert(*curbp->b_nlchr, KFORW)) != TRUE)
return (s);
linep = lforw(linep);
loffs = 0;
} else {
if ((s = kinsert(lgetc(linep, loffs), KFORW)) != TRUE)
return (s);
++loffs;
}
}
clearmark(FFARG, 0);
return (TRUE);
}
int
lowerregion(int f, int n)
{
struct line *linep;
struct region region;
int loffs, c, s;
if ((s = checkdirty(curbp)) != TRUE)
return (s);
if (curbp->b_flag & BFREADONLY) {
dobeep();
ewprintf("Buffer is read-only");
return (FALSE);
}
if ((s = getregion(®ion)) != TRUE)
return (s);
undo_add_change(region.r_linep, region.r_offset, region.r_size);
lchange(WFFULL);
linep = region.r_linep;
loffs = region.r_offset;
while (region.r_size--) {
if (loffs == llength(linep)) {
linep = lforw(linep);
loffs = 0;
} else {
c = lgetc(linep, loffs);
if (ISUPPER(c) != FALSE)
lputc(linep, loffs, TOLOWER(c));
++loffs;
}
}
return (TRUE);
}
int
upperregion(int f, int n)
{
struct line *linep;
struct region region;
int loffs, c, s;
if ((s = checkdirty(curbp)) != TRUE)
return (s);
if (curbp->b_flag & BFREADONLY) {
dobeep();
ewprintf("Buffer is read-only");
return (FALSE);
}
if ((s = getregion(®ion)) != TRUE)
return (s);
undo_add_change(region.r_linep, region.r_offset, region.r_size);
lchange(WFFULL);
linep = region.r_linep;
loffs = region.r_offset;
while (region.r_size--) {
if (loffs == llength(linep)) {
linep = lforw(linep);
loffs = 0;
} else {
c = lgetc(linep, loffs);
if (ISLOWER(c) != FALSE)
lputc(linep, loffs, TOUPPER(c));
++loffs;
}
}
return (TRUE);
}
static int
getregion(struct region *rp)
{
struct line *flp, *blp;
long fsize, bsize;
if (curwp->w_markp == NULL) {
dobeep();
ewprintf("No mark set in this window");
return (FALSE);
}
if (curwp->w_dotp == curwp->w_markp) {
rp->r_linep = curwp->w_dotp;
rp->r_lineno = curwp->w_dotline;
if (curwp->w_doto < curwp->w_marko) {
rp->r_offset = curwp->w_doto;
rp->r_size = (RSIZE)(curwp->w_marko - curwp->w_doto);
} else {
rp->r_offset = curwp->w_marko;
rp->r_size = (RSIZE)(curwp->w_doto - curwp->w_marko);
}
return (TRUE);
}
flp = blp = curwp->w_dotp;
bsize = curwp->w_doto;
fsize = llength(flp) - curwp->w_doto + 1;
while (lforw(flp) != curbp->b_headp || lback(blp) != curbp->b_headp) {
if (lforw(flp) != curbp->b_headp) {
flp = lforw(flp);
if (flp == curwp->w_markp) {
rp->r_linep = curwp->w_dotp;
rp->r_offset = curwp->w_doto;
rp->r_lineno = curwp->w_dotline;
return (setsize(rp,
(RSIZE)(fsize + curwp->w_marko)));
}
fsize += llength(flp) + 1;
}
if (lback(blp) != curbp->b_headp) {
blp = lback(blp);
bsize += llength(blp) + 1;
if (blp == curwp->w_markp) {
rp->r_linep = blp;
rp->r_offset = curwp->w_marko;
rp->r_lineno = curwp->w_markline;
return (setsize(rp,
(RSIZE)(bsize - curwp->w_marko)));
}
}
}
dobeep();
ewprintf("Bug: lost mark");
return (FALSE);
}
static int
setsize(struct region *rp, RSIZE size)
{
rp->r_size = size;
if (rp->r_size != size) {
dobeep();
ewprintf("Region is too large");
return (FALSE);
}
return (TRUE);
}
#define PREFIXLENGTH 40
static char prefix_string[PREFIXLENGTH] = {'>', '\0'};
int
prefixregion(int f, int n)
{
struct line *first, *last;
struct region region;
char *prefix = prefix_string;
int nline;
int s;
if ((s = checkdirty(curbp)) != TRUE)
return (s);
if (curbp->b_flag & BFREADONLY) {
dobeep();
ewprintf("Buffer is read-only");
return (FALSE);
}
if ((f == TRUE) && ((s = setprefix(FFRAND, 1)) != TRUE))
return (s);
if ((s = getregion(®ion)) != TRUE)
return (s);
first = region.r_linep;
last = (first == curwp->w_dotp) ? curwp->w_markp : curwp->w_dotp;
for (nline = 1; first != last; nline++)
first = lforw(first);
curwp->w_dotp = region.r_linep;
curwp->w_doto = region.r_offset;
curwp->w_dotline = region.r_lineno;
while (nline--) {
(void)gotobol(FFRAND, 1);
for (prefix = prefix_string; *prefix; prefix++)
(void)linsert(1, *prefix);
(void)forwline(FFRAND, 1);
}
(void)gotobol(FFRAND, 1);
return (TRUE);
}
int
setprefix(int f, int n)
{
char buf[PREFIXLENGTH], *rep;
int retval;
if (prefix_string[0] == '\0')
rep = eread("Prefix string: ", buf, sizeof(buf),
EFNEW | EFCR);
else
rep = eread("Prefix string (default %s): ", buf, sizeof(buf),
EFNUL | EFNEW | EFCR, prefix_string);
if (rep == NULL)
return (ABORT);
if (rep[0] != '\0') {
(void)strlcpy(prefix_string, rep, sizeof(prefix_string));
retval = TRUE;
} else if (rep[0] == '\0' && prefix_string[0] != '\0') {
retval = TRUE;
} else
retval = FALSE;
return (retval);
}
int
region_get_data(struct region *reg, char *buf, int len)
{
int i, off;
struct line *lp;
off = reg->r_offset;
lp = reg->r_linep;
for (i = 0; i < len; i++) {
if (off == llength(lp)) {
lp = lforw(lp);
if (lp == curbp->b_headp)
break;
off = 0;
buf[i] = *curbp->b_nlchr;
} else {
buf[i] = lgetc(lp, off);
off++;
}
}
buf[i] = '\0';
return (i);
}
void
region_put_data(const char *buf, int len)
{
int i;
for (i = 0; buf[i] != '\0' && i < len; i++) {
if (buf[i] == *curbp->b_nlchr)
lnewline();
else
linsert(1, buf[i]);
}
}
int
markbuffer(int f, int n)
{
if (gotoeob(f,n) == FALSE)
return (FALSE);
(void) clearmark(f, n);
if (gotobob(f,n) == FALSE)
return (FALSE);
return (TRUE);
}
int
piperegion(int f, int n)
{
struct region region;
struct buffer *bp = NULL;
int len;
char *cmd, cmdbuf[NFILEN], *text;
if (curwp->w_markp == NULL) {
dobeep();
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);
if (getregion(®ion) != TRUE)
return (FALSE);
len = region.r_size;
if ((text = malloc(len + 1)) == NULL) {
dobeep();
ewprintf("Cannot allocate memory.");
return (FALSE);
}
region_get_data(®ion, text, len);
if (n > 1) {
bp = curbp;
undo_boundary_enable(FFRAND, 0);
killregion(FFRAND, 1);
kdelete();
}
return (shellcmdoutput(cmd, text, len, bp));
}
int
shellcommand(int f, int n)
{
struct buffer *bp = NULL;
char *cmd, cmdbuf[NFILEN];
if (n > 1)
bp = curbp;
if ((cmd = eread("Shell command: ", cmdbuf, sizeof(cmdbuf),
EFNEW | EFCR)) == NULL || (cmd[0] == '\0'))
return (ABORT);
return (shellcmdoutput(cmd, NULL, 0, bp));
}
int
shellcmdoutput(char* const cmd, char* const text, int len,
struct buffer *bp)
{
struct mgwin *wp;
struct line *tlp;
char *argv[] = {NULL, "-c", cmd, NULL};
char *shellp;
int tbo, ret, special = 0;
if (bp == NULL) {
special = 1;
bp = bfind("*Shell Command Output*", TRUE);
bp->b_flag &= ~BFREADONLY;
wp = popbuf(bp, WNONE);
if (wp == NULL || bclear(bp) != TRUE) {
free(text);
return (FALSE);
}
curbp = bp;
curwp = wp;
}
if (bp->b_flag & BFREADONLY) {
dobeep();
ewprintf("Buffer is read-only");
return (FALSE);
}
tlp = curwp->w_dotp;
tbo = curwp->w_doto;
if ((shellp = getenv("SHELL")) == NULL)
shellp = _PATH_BSHELL;
if ((argv[0] = strrchr(shellp, '/')) != NULL)
argv[0]++;
else
argv[0] = shellp;
ret = pipeio(shellp, argv, text, len, bp);
if (ret == TRUE) {
eerase();
if (special && lforw(bp->b_headp) == bp->b_headp)
addline(bp, "(Shell command succeeded with no output)");
}
free(text);
if (special) {
bp->b_flag |= BFREADONLY;
gotobob(0, 0);
} else {
curwp->w_dotp = tlp;
curwp->w_doto = tbo;
}
return (ret);
}
int
pipeio(const char* const path, char* const argv[], char* const text, int len,
struct buffer *outbp)
{
int s[2], ret;
char *err;
pid_t pid;
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s) == -1) {
dobeep();
ewprintf("socketpair error");
return (FALSE);
}
switch((pid = fork())) {
case -1:
dobeep();
ewprintf("Can't fork");
return (FALSE);
case 0:
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);
execv(path, argv);
err = strerror(errno);
write(s[1], err, strlen(err));
_exit(1);
default:
close(s[1]);
undo_boundary_enable(FFRAND, 0);
ret = iomux(s[0], text, len);
undo_boundary_enable(FFRAND, 1);
waitpid(pid, NULL, 0);
return (ret);
}
return (FALSE);
}
int
iomux(int fd, char* const text, int len)
{
struct pollfd pfd[1];
int nfds;
char *textcopy;
textcopy = text;
fcntl(fd, F_SETFL, O_NONBLOCK);
pfd[0].fd = fd;
if (len == 0) {
shutdown(fd, SHUT_WR);
pfd[0].events = POLLIN;
} else
pfd[0].events = POLLIN | POLLOUT;
while ((nfds = poll(pfd, 1, TIMEOUT)) != -1 ||
(pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL))) {
if (pfd[0].revents & POLLOUT && len > 0)
pwriteout(fd, &textcopy, &len);
else if (pfd[0].revents & POLLIN)
if (preadin(fd) == FALSE)
break;
if (len == 0 && pfd[0].events & POLLOUT)
pfd[0].events = POLLIN;
}
close(fd);
if (nfds == 0) {
dobeep();
ewprintf("poll timed out");
return (FALSE);
} else if (nfds == -1) {
dobeep();
ewprintf("poll error");
return (FALSE);
}
return (TRUE);
}
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);
}
int
preadin(int fd)
{
char buf[BUFSIZ];
ssize_t len;
if ((len = read(fd, buf, BUFSIZ)) <= 0)
return (FALSE);
region_put_data(buf, len);
return (TRUE);
}