2013-12-06 12:04:52 +01:00
|
|
|
/* $NetBSD: ip_read.c,v 1.6 2013/11/29 15:24:08 christos Exp $ */
|
2013-01-22 12:03:53 +01:00
|
|
|
/*-
|
|
|
|
* Copyright (c) 1996
|
|
|
|
* Keith Bostic. All rights reserved.
|
|
|
|
*
|
|
|
|
* See the LICENSE file for redistribution information.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#ifndef lint
|
2013-12-06 12:04:52 +01:00
|
|
|
static const char sccsid[] = "Id: ip_read.c,v 8.23 2001/06/25 15:19:24 skimo Exp (Berkeley) Date: 2001/06/25 15:19:24 ";
|
2013-01-22 12:03:53 +01:00
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/queue.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
|
|
#include <bitstring.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <termios.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
|
|
|
#include "../common/common.h"
|
|
|
|
#include "../ex/script.h"
|
|
|
|
#include "../ipc/ip.h"
|
|
|
|
|
|
|
|
extern GS *__global_list;
|
|
|
|
|
|
|
|
VIPFUNLIST const vipfuns[] = {
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_BOL 1 Cursor to start of line. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_BOTTOM 2 Cursor to bottom. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_DEL 3 Cursor delete. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_DOWN 4 Cursor down N lines: IPO_INT. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"1", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_EOL 5 Cursor to end of line. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_INSERT 6 Cursor: enter insert mode. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_LEFT 7 Cursor left. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_PGDOWN 8 Cursor down N pages: IPO_INT. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"1", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_PGUP 9 Cursor up N lines: IPO_INT. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"1", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_RIGHT 10 Cursor right. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_SEARCH 11 Cursor: search: IPO_INT, IPO_STR. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"a1", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_SETTOP 12 Cursor: set screen top line: IPO_INT. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"1", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_TOP 13 Cursor to top. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_C_UP 14 Cursor up N lines: IPO_INT. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"1", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_EDIT 15 Edit a file: IPO_STR. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"a", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_EDITOPT 16 Edit option: 2 * IPO_STR, IPO_INT. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"ab1", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_EDITSPLIT 17 Split to a file: IPO_STR. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"a", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_EOF 18 End of input (NOT ^D). */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_EOF},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_ERR 19 Input error. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_ERR},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_FLAGS 20 Flags */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"1", E_FLAGS},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_INTERRUPT 21 Interrupt. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_INTERRUPT},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_MOUSE_MOVE 22 Mouse click move: IPO_INT, IPO_INT. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"12", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_QUIT 23 Quit. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_RESIZE 24 Screen resize: IPO_INT, IPO_INT. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"12", E_WRESIZE},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_SEL_END 25 Select end: IPO_INT, IPO_INT. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"12", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_SEL_START 26 Select start: IPO_INT, IPO_INT. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"12", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_SIGHUP 27 SIGHUP. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_SIGHUP},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_SIGTERM 28 SIGTERM. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_SIGTERM},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_STRING 29 Input string: IPO_STR. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"a", E_STRING},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_TAG 30 Tag. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_TAGAS 31 Tag to a string: IPO_STR. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"a", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_TAGSPLIT 32 Split to a tag. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_UNDO 33 Undo. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_WQ 34 Write and quit. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_WRITE 35 Write. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_WRITEAS 36 Write as another file: IPO_STR. */
|
2013-01-22 12:03:53 +01:00
|
|
|
{"a", E_IPCOMMAND},
|
2013-12-06 12:04:52 +01:00
|
|
|
/* VI_EVENT_SUP 37 */
|
2013-01-22 12:03:53 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef enum { INP_OK=0, INP_EOF, INP_ERR, INP_TIMEOUT } input_t;
|
|
|
|
|
|
|
|
static input_t ip_read __P((SCR *, IP_PRIVATE *, struct timeval *, int, int*));
|
|
|
|
static int ip_resize __P((SCR *, u_int32_t, u_int32_t));
|
|
|
|
static int ip_trans __P((SCR *, IP_PRIVATE *, EVENT *));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ip_event --
|
|
|
|
* Return a single event.
|
|
|
|
*
|
|
|
|
* PUBLIC: int ip_event __P((SCR *, EVENT *, u_int32_t, int));
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ip_event(SCR *sp, EVENT *evp, u_int32_t flags, int ms)
|
|
|
|
{
|
|
|
|
return ip_wevent(sp->wp, sp, evp, flags, ms);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX probably better to require new_window to send size
|
|
|
|
* so we never have to call ip_wevent with sp == NULL
|
|
|
|
*
|
|
|
|
* ip_wevent --
|
|
|
|
* Return a single event.
|
|
|
|
*
|
|
|
|
* PUBLIC: int ip_wevent __P((WIN *, SCR *, EVENT *, u_int32_t, int));
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ip_wevent(WIN *wp, SCR *sp, EVENT *evp, u_int32_t flags, int ms)
|
|
|
|
{
|
|
|
|
IP_PRIVATE *ipp;
|
|
|
|
struct timeval t, *tp;
|
|
|
|
int termread;
|
2013-12-06 12:04:52 +01:00
|
|
|
int nr = 0;
|
2013-01-22 12:03:53 +01:00
|
|
|
|
|
|
|
if (LF_ISSET(EC_INTERRUPT)) { /* XXX */
|
|
|
|
evp->e_event = E_TIMEOUT;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
ipp = sp == NULL ? WIPP(wp) : IPP(sp);
|
|
|
|
|
|
|
|
/* Discard the last command. */
|
|
|
|
if (ipp->iskip != 0) {
|
|
|
|
ipp->iblen -= ipp->iskip;
|
|
|
|
memmove(ipp->ibuf, ipp->ibuf + ipp->iskip, ipp->iblen);
|
|
|
|
ipp->iskip = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
termread = F_ISSET(ipp, IP_IN_EX) ||
|
|
|
|
(sp && F_ISSET(sp, SC_SCR_EXWROTE));
|
|
|
|
|
|
|
|
/* Process possible remaining commands */
|
|
|
|
if (!termread && ipp->iblen >= IPO_CODE_LEN && ip_trans(sp, ipp, evp))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Set timer. */
|
|
|
|
if (ms == 0)
|
|
|
|
tp = NULL;
|
|
|
|
else {
|
|
|
|
t.tv_sec = ms / 1000;
|
|
|
|
t.tv_usec = (ms % 1000) * 1000;
|
|
|
|
tp = &t;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read input events. */
|
|
|
|
for (;;) {
|
|
|
|
switch (ip_read(sp, ipp, tp, termread, &nr)) {
|
|
|
|
case INP_OK:
|
|
|
|
if (termread) {
|
|
|
|
evp->e_csp = ipp->tbuf;
|
|
|
|
evp->e_len = nr;
|
|
|
|
evp->e_event = E_STRING;
|
|
|
|
} else if (!ip_trans(sp, ipp, evp))
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
case INP_EOF:
|
|
|
|
evp->e_event = E_EOF;
|
|
|
|
break;
|
|
|
|
case INP_ERR:
|
|
|
|
evp->e_event = E_ERR;
|
|
|
|
break;
|
|
|
|
case INP_TIMEOUT:
|
|
|
|
evp->e_event = E_TIMEOUT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ip_read --
|
|
|
|
* Read characters from the input.
|
|
|
|
*/
|
|
|
|
static input_t
|
|
|
|
ip_read(SCR *sp, IP_PRIVATE *ipp, struct timeval *tp, int termread, int *nr)
|
|
|
|
{
|
|
|
|
GS *gp;
|
|
|
|
fd_set rdfd;
|
|
|
|
input_t rval;
|
|
|
|
size_t blen;
|
|
|
|
int maxfd;
|
|
|
|
char *bp;
|
|
|
|
int fd;
|
2013-12-06 12:04:52 +01:00
|
|
|
const CHAR_T *wp;
|
2013-01-22 12:03:53 +01:00
|
|
|
size_t wlen;
|
|
|
|
|
|
|
|
gp = sp == NULL ? __global_list : sp->gp;
|
|
|
|
bp = ipp->ibuf + ipp->iblen;
|
|
|
|
blen = sizeof(ipp->ibuf) - ipp->iblen;
|
|
|
|
fd = termread ? ipp->t_fd : ipp->i_fd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 1: A read with an associated timeout, e.g., trying to complete
|
|
|
|
* a map sequence. If input exists, we fall into #2.
|
|
|
|
*/
|
|
|
|
FD_ZERO(&rdfd);
|
|
|
|
if (tp != NULL) {
|
|
|
|
FD_SET(fd, &rdfd);
|
2013-12-06 12:04:52 +01:00
|
|
|
switch (select(fd + 1, &rdfd, NULL, NULL, tp)) {
|
2013-01-22 12:03:53 +01:00
|
|
|
case 0:
|
|
|
|
return (INP_TIMEOUT);
|
|
|
|
case -1:
|
|
|
|
goto err;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 2: Wait for input.
|
|
|
|
*
|
|
|
|
* Select on the command input and scripting window file descriptors.
|
|
|
|
* It's ugly that we wait on scripting file descriptors here, but it's
|
|
|
|
* the only way to keep from locking out scripting windows.
|
|
|
|
*/
|
|
|
|
if (sp != NULL && F_ISSET(gp, G_SCRWIN)) {
|
|
|
|
FD_ZERO(&rdfd);
|
|
|
|
FD_SET(fd, &rdfd);
|
|
|
|
maxfd = fd;
|
|
|
|
if (sscr_check_input(sp, &rdfd, maxfd))
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 3: Read the input.
|
|
|
|
*/
|
|
|
|
switch (*nr = read(fd, termread ? (char *)ipp->tbuf : bp,
|
|
|
|
termread ? sizeof(ipp->tbuf)/sizeof(CHAR_T)
|
|
|
|
: blen)) {
|
|
|
|
case 0: /* EOF. */
|
|
|
|
rval = INP_EOF;
|
|
|
|
break;
|
|
|
|
case -1: /* Error or interrupt. */
|
|
|
|
err: rval = INP_ERR;
|
|
|
|
msgq(sp, M_SYSERR, "input");
|
|
|
|
break;
|
|
|
|
default: /* Input characters. */
|
2013-12-06 12:04:52 +01:00
|
|
|
if (sp == NULL) {
|
|
|
|
rval = INP_ERR;
|
|
|
|
msgq(sp, M_SYSERR,
|
|
|
|
"Can't convert input with NULL screen");
|
|
|
|
break;
|
|
|
|
}
|
2013-01-22 12:03:53 +01:00
|
|
|
if (!termread) ipp->iblen += *nr;
|
|
|
|
else {
|
|
|
|
CHAR2INT(sp, (char *)ipp->tbuf, *nr, wp, wlen);
|
|
|
|
MEMMOVEW(ipp->tbuf, wp, wlen);
|
|
|
|
}
|
|
|
|
rval = INP_OK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (rval);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ip_trans --
|
|
|
|
* Translate messages into events.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ip_trans(SCR *sp, IP_PRIVATE *ipp, EVENT *evp)
|
|
|
|
{
|
|
|
|
u_int32_t skip, val;
|
2013-12-06 12:04:52 +01:00
|
|
|
const char *fmt;
|
|
|
|
const CHAR_T *wp;
|
2013-01-22 12:03:53 +01:00
|
|
|
size_t wlen;
|
|
|
|
|
|
|
|
if (ipp->ibuf[0] == CODE_OOB ||
|
|
|
|
ipp->ibuf[0] >= VI_EVENT_SUP)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* XXX: Protocol is out of sync?
|
|
|
|
*/
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
fmt = vipfuns[ipp->ibuf[0]-1].format;
|
|
|
|
evp->e_event = vipfuns[ipp->ibuf[0]-1].e_event;
|
|
|
|
evp->e_ipcom = ipp->ibuf[0];
|
|
|
|
|
|
|
|
for (skip = IPO_CODE_LEN; *fmt != '\0'; ++fmt)
|
|
|
|
switch (*fmt) {
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
if (ipp->iblen < skip + IPO_INT_LEN)
|
|
|
|
return (0);
|
|
|
|
memcpy(&val, ipp->ibuf + skip, IPO_INT_LEN);
|
|
|
|
val = ntohl(val);
|
|
|
|
if (*fmt == '1')
|
|
|
|
evp->e_val1 = val;
|
|
|
|
else
|
|
|
|
evp->e_val2 = val;
|
|
|
|
skip += IPO_INT_LEN;
|
|
|
|
break;
|
|
|
|
case 'a':
|
|
|
|
case 'b':
|
|
|
|
if (ipp->iblen < skip + IPO_INT_LEN)
|
|
|
|
return (0);
|
|
|
|
memcpy(&val, ipp->ibuf + skip, IPO_INT_LEN);
|
|
|
|
val = ntohl(val);
|
|
|
|
skip += IPO_INT_LEN;
|
|
|
|
if (ipp->iblen < skip + val)
|
|
|
|
return (0);
|
|
|
|
if (*fmt == 'a') {
|
|
|
|
CHAR2INT(sp, ipp->ibuf + skip, val,
|
|
|
|
wp, wlen);
|
|
|
|
MEMCPYW(ipp->tbuf, wp, wlen);
|
|
|
|
evp->e_str1 = ipp->tbuf;
|
|
|
|
evp->e_len1 = wlen;
|
|
|
|
} else {
|
|
|
|
CHAR2INT(sp, ipp->ibuf + skip, val,
|
|
|
|
wp, wlen);
|
|
|
|
MEMCPYW(ipp->tbuf, wp, wlen);
|
|
|
|
evp->e_str2 = ipp->tbuf;
|
|
|
|
evp->e_len2 = wlen;
|
|
|
|
}
|
|
|
|
skip += val;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ipp->iskip = skip;
|
|
|
|
|
|
|
|
if (evp->e_event == E_WRESIZE)
|
|
|
|
(void)ip_resize(sp, evp->e_val1, evp->e_val2);
|
|
|
|
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ip_resize --
|
|
|
|
* Reset the options for a resize event.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ip_resize(SCR *sp, u_int32_t lines, u_int32_t columns)
|
|
|
|
{
|
|
|
|
GS *gp;
|
|
|
|
int rval;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX
|
|
|
|
* The IP screen has to know the lines and columns before anything
|
|
|
|
* else happens. So, we may not have a valid SCR pointer, and we
|
|
|
|
* have to deal with that.
|
|
|
|
*/
|
|
|
|
if (sp == NULL) {
|
|
|
|
gp = __global_list;
|
|
|
|
OG_VAL(gp, GO_LINES) = OG_D_VAL(gp, GO_LINES) = lines;
|
|
|
|
OG_VAL(gp, GO_COLUMNS) = OG_D_VAL(gp, GO_COLUMNS) = columns;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
rval = api_opts_set(sp, L("lines"), NULL, lines, 0);
|
|
|
|
if (api_opts_set(sp, L("columns"), NULL, columns, 0))
|
|
|
|
rval = 1;
|
|
|
|
return (rval);
|
|
|
|
}
|