mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-07-07 06:22:47 -05:00
Line discipline module now uses dynamically allocated data. Also
fixed one or two other minor problems. [originally from svn r2141]
This commit is contained in:
185
ldisc.c
185
ldisc.c
@ -11,26 +11,33 @@
|
||||
#include "putty.h"
|
||||
#include "terminal.h"
|
||||
|
||||
typedef struct ldisc_tag {
|
||||
Terminal *term;
|
||||
Backend *back;
|
||||
void *backhandle;
|
||||
void *frontend;
|
||||
|
||||
char *buf;
|
||||
int buflen, bufsiz, quotenext;
|
||||
} *Ldisc;
|
||||
|
||||
#define ECHOING (cfg.localecho == LD_YES || \
|
||||
(cfg.localecho == LD_BACKEND && \
|
||||
(back->ldisc(backhandle, LD_ECHO) || \
|
||||
term_ldisc(term, LD_ECHO))))
|
||||
(ldisc->back->ldisc(ldisc->backhandle, LD_ECHO) || \
|
||||
term_ldisc(ldisc->term, LD_ECHO))))
|
||||
#define EDITING (cfg.localedit == LD_YES || \
|
||||
(cfg.localedit == LD_BACKEND && \
|
||||
(back->ldisc(backhandle, LD_EDIT) || \
|
||||
term_ldisc(term, LD_EDIT))))
|
||||
(ldisc->back->ldisc(ldisc->backhandle, LD_EDIT) || \
|
||||
term_ldisc(ldisc->term, LD_EDIT))))
|
||||
|
||||
static void c_write(char *buf, int len)
|
||||
static void c_write(Ldisc ldisc, char *buf, int len)
|
||||
{
|
||||
from_backend(term, 0, buf, len);
|
||||
from_backend(ldisc->term, 0, buf, len);
|
||||
}
|
||||
|
||||
static char *term_buf = NULL;
|
||||
static int term_buflen = 0, term_bufsiz = 0, term_quotenext = 0;
|
||||
|
||||
static int plen(unsigned char c)
|
||||
static int plen(Ldisc ldisc, unsigned char c)
|
||||
{
|
||||
if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(term)))
|
||||
if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(ldisc->term)))
|
||||
return 1;
|
||||
else if (c < 128)
|
||||
return 2; /* ^x for some x */
|
||||
@ -38,41 +45,66 @@ static int plen(unsigned char c)
|
||||
return 4; /* <XY> for hex XY */
|
||||
}
|
||||
|
||||
static void pwrite(unsigned char c)
|
||||
static void pwrite(Ldisc ldisc, unsigned char c)
|
||||
{
|
||||
if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(term))) {
|
||||
c_write(&c, 1);
|
||||
if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(ldisc->term))) {
|
||||
c_write(ldisc, &c, 1);
|
||||
} else if (c < 128) {
|
||||
char cc[2];
|
||||
cc[1] = (c == 127 ? '?' : c + 0x40);
|
||||
cc[0] = '^';
|
||||
c_write(cc, 2);
|
||||
c_write(ldisc, cc, 2);
|
||||
} else {
|
||||
char cc[5];
|
||||
sprintf(cc, "<%02X>", c);
|
||||
c_write(cc, 4);
|
||||
c_write(ldisc, cc, 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void bsb(int n)
|
||||
static void bsb(Ldisc ldisc, int n)
|
||||
{
|
||||
while (n--)
|
||||
c_write("\010 \010", 3);
|
||||
c_write(ldisc, "\010 \010", 3);
|
||||
}
|
||||
|
||||
#define CTRL(x) (x^'@')
|
||||
#define KCTRL(x) ((x^'@') | 0x100)
|
||||
|
||||
void ldisc_send(char *buf, int len, int interactive)
|
||||
void *ldisc_create(Terminal *term,
|
||||
Backend *back, void *backhandle,
|
||||
void *frontend)
|
||||
{
|
||||
Ldisc ldisc = smalloc(sizeof(*ldisc));
|
||||
|
||||
ldisc->buf = NULL;
|
||||
ldisc->buflen = 0;
|
||||
ldisc->bufsiz = 0;
|
||||
ldisc->quotenext = 0;
|
||||
|
||||
ldisc->back = back;
|
||||
ldisc->backhandle = backhandle;
|
||||
ldisc->term = term;
|
||||
ldisc->frontend = frontend;
|
||||
|
||||
/* Link ourselves into the backend and the terminal */
|
||||
if (term)
|
||||
term->ldisc = ldisc;
|
||||
if (back)
|
||||
back->provide_ldisc(backhandle, ldisc);
|
||||
|
||||
return ldisc;
|
||||
}
|
||||
|
||||
void ldisc_send(void *handle, char *buf, int len, int interactive)
|
||||
{
|
||||
Ldisc ldisc = (Ldisc) handle;
|
||||
int keyflag = 0;
|
||||
/*
|
||||
* Called with len=0 when the options change. We must inform
|
||||
* the front end in case it needs to know.
|
||||
*/
|
||||
if (len == 0) {
|
||||
void ldisc_update(int echo, int edit);
|
||||
ldisc_update(ECHOING, EDITING);
|
||||
ldisc_update(ldisc->frontend, ECHOING, EDITING);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
@ -80,7 +112,8 @@ void ldisc_send(char *buf, int len, int interactive)
|
||||
* it's depending on finding out (e.g. keypress termination for
|
||||
* Close On Exit).
|
||||
*/
|
||||
frontend_keypress();
|
||||
frontend_keypress(ldisc->frontend);
|
||||
|
||||
/*
|
||||
* Less than zero means null terminated special string.
|
||||
*/
|
||||
@ -97,7 +130,7 @@ void ldisc_send(char *buf, int len, int interactive)
|
||||
c = *buf++ + keyflag;
|
||||
if (!interactive && c == '\r')
|
||||
c += KCTRL('@');
|
||||
switch (term_quotenext ? ' ' : c) {
|
||||
switch (ldisc->quotenext ? ' ' : c) {
|
||||
/*
|
||||
* ^h/^?: delete one char and output one BSB
|
||||
* ^w: delete, and output BSBs, to return to last
|
||||
@ -114,20 +147,20 @@ void ldisc_send(char *buf, int len, int interactive)
|
||||
*/
|
||||
case KCTRL('H'):
|
||||
case KCTRL('?'): /* backspace/delete */
|
||||
if (term_buflen > 0) {
|
||||
if (ldisc->buflen > 0) {
|
||||
if (ECHOING)
|
||||
bsb(plen(term_buf[term_buflen - 1]));
|
||||
term_buflen--;
|
||||
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
|
||||
ldisc->buflen--;
|
||||
}
|
||||
break;
|
||||
case CTRL('W'): /* delete word */
|
||||
while (term_buflen > 0) {
|
||||
while (ldisc->buflen > 0) {
|
||||
if (ECHOING)
|
||||
bsb(plen(term_buf[term_buflen - 1]));
|
||||
term_buflen--;
|
||||
if (term_buflen > 0 &&
|
||||
isspace(term_buf[term_buflen - 1]) &&
|
||||
!isspace(term_buf[term_buflen]))
|
||||
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
|
||||
ldisc->buflen--;
|
||||
if (ldisc->buflen > 0 &&
|
||||
isspace(ldisc->buf[ldisc->buflen - 1]) &&
|
||||
!isspace(ldisc->buf[ldisc->buflen]))
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -135,12 +168,12 @@ void ldisc_send(char *buf, int len, int interactive)
|
||||
case CTRL('C'): /* Send IP */
|
||||
case CTRL('\\'): /* Quit */
|
||||
case CTRL('Z'): /* Suspend */
|
||||
while (term_buflen > 0) {
|
||||
while (ldisc->buflen > 0) {
|
||||
if (ECHOING)
|
||||
bsb(plen(term_buf[term_buflen - 1]));
|
||||
term_buflen--;
|
||||
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
|
||||
ldisc->buflen--;
|
||||
}
|
||||
back->special(backhandle, TS_EL);
|
||||
ldisc->back->special(ldisc->backhandle, TS_EL);
|
||||
/*
|
||||
* We don't send IP, SUSP or ABORT if the user has
|
||||
* configured telnet specials off! This breaks
|
||||
@ -149,29 +182,29 @@ void ldisc_send(char *buf, int len, int interactive)
|
||||
if (!cfg.telnet_keyboard)
|
||||
goto default_case;
|
||||
if (c == CTRL('C'))
|
||||
back->special(backhandle, TS_IP);
|
||||
ldisc->back->special(ldisc->backhandle, TS_IP);
|
||||
if (c == CTRL('Z'))
|
||||
back->special(backhandle, TS_SUSP);
|
||||
ldisc->back->special(ldisc->backhandle, TS_SUSP);
|
||||
if (c == CTRL('\\'))
|
||||
back->special(backhandle, TS_ABORT);
|
||||
ldisc->back->special(ldisc->backhandle, TS_ABORT);
|
||||
break;
|
||||
case CTRL('R'): /* redraw line */
|
||||
if (ECHOING) {
|
||||
int i;
|
||||
c_write("^R\r\n", 4);
|
||||
for (i = 0; i < term_buflen; i++)
|
||||
pwrite(term_buf[i]);
|
||||
c_write(ldisc, "^R\r\n", 4);
|
||||
for (i = 0; i < ldisc->buflen; i++)
|
||||
pwrite(ldisc, ldisc->buf[i]);
|
||||
}
|
||||
break;
|
||||
case CTRL('V'): /* quote next char */
|
||||
term_quotenext = TRUE;
|
||||
ldisc->quotenext = TRUE;
|
||||
break;
|
||||
case CTRL('D'): /* logout or send */
|
||||
if (term_buflen == 0) {
|
||||
back->special(backhandle, TS_EOF);
|
||||
if (ldisc->buflen == 0) {
|
||||
ldisc->back->special(ldisc->backhandle, TS_EOF);
|
||||
} else {
|
||||
back->send(backhandle, term_buf, term_buflen);
|
||||
term_buflen = 0;
|
||||
ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);
|
||||
ldisc->buflen = 0;
|
||||
}
|
||||
break;
|
||||
/*
|
||||
@ -199,81 +232,81 @@ void ldisc_send(char *buf, int len, int interactive)
|
||||
*/
|
||||
case CTRL('J'):
|
||||
if (cfg.protocol == PROT_RAW &&
|
||||
term_buflen > 0 && term_buf[term_buflen - 1] == '\r') {
|
||||
ldisc->buflen > 0 && ldisc->buf[ldisc->buflen - 1] == '\r') {
|
||||
if (ECHOING)
|
||||
bsb(plen(term_buf[term_buflen - 1]));
|
||||
term_buflen--;
|
||||
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
|
||||
ldisc->buflen--;
|
||||
/* FALLTHROUGH */
|
||||
case KCTRL('M'): /* send with newline */
|
||||
if (term_buflen > 0)
|
||||
back->send(backhandle, term_buf, term_buflen);
|
||||
if (ldisc->buflen > 0)
|
||||
ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);
|
||||
if (cfg.protocol == PROT_RAW)
|
||||
back->send(backhandle, "\r\n", 2);
|
||||
ldisc->back->send(ldisc->backhandle, "\r\n", 2);
|
||||
else if (cfg.protocol == PROT_TELNET && cfg.telnet_newline)
|
||||
back->special(backhandle, TS_EOL);
|
||||
ldisc->back->special(ldisc->backhandle, TS_EOL);
|
||||
else
|
||||
back->send(backhandle, "\r", 1);
|
||||
ldisc->back->send(ldisc->backhandle, "\r", 1);
|
||||
if (ECHOING)
|
||||
c_write("\r\n", 2);
|
||||
term_buflen = 0;
|
||||
c_write(ldisc, "\r\n", 2);
|
||||
ldisc->buflen = 0;
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
default: /* get to this label from ^V handler */
|
||||
default_case:
|
||||
if (term_buflen >= term_bufsiz) {
|
||||
term_bufsiz = term_buflen + 256;
|
||||
term_buf = saferealloc(term_buf, term_bufsiz);
|
||||
if (ldisc->buflen >= ldisc->bufsiz) {
|
||||
ldisc->bufsiz = ldisc->buflen + 256;
|
||||
ldisc->buf = srealloc(ldisc->buf, ldisc->bufsiz);
|
||||
}
|
||||
term_buf[term_buflen++] = c;
|
||||
ldisc->buf[ldisc->buflen++] = c;
|
||||
if (ECHOING)
|
||||
pwrite((unsigned char) c);
|
||||
term_quotenext = FALSE;
|
||||
pwrite(ldisc, (unsigned char) c);
|
||||
ldisc->quotenext = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (term_buflen != 0) {
|
||||
back->send(backhandle, term_buf, term_buflen);
|
||||
while (term_buflen > 0) {
|
||||
bsb(plen(term_buf[term_buflen - 1]));
|
||||
term_buflen--;
|
||||
if (ldisc->buflen != 0) {
|
||||
ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);
|
||||
while (ldisc->buflen > 0) {
|
||||
bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
|
||||
ldisc->buflen--;
|
||||
}
|
||||
}
|
||||
if (len > 0) {
|
||||
if (ECHOING)
|
||||
c_write(buf, len);
|
||||
c_write(ldisc, buf, len);
|
||||
if (keyflag && cfg.protocol == PROT_TELNET && len == 1) {
|
||||
switch (buf[0]) {
|
||||
case CTRL('M'):
|
||||
if (cfg.protocol == PROT_TELNET && cfg.telnet_newline)
|
||||
back->special(backhandle, TS_EOL);
|
||||
ldisc->back->special(ldisc->backhandle, TS_EOL);
|
||||
else
|
||||
back->send(backhandle, "\r", 1);
|
||||
ldisc->back->send(ldisc->backhandle, "\r", 1);
|
||||
break;
|
||||
case CTRL('?'):
|
||||
case CTRL('H'):
|
||||
if (cfg.telnet_keyboard) {
|
||||
back->special(backhandle, TS_EC);
|
||||
ldisc->back->special(ldisc->backhandle, TS_EC);
|
||||
break;
|
||||
}
|
||||
case CTRL('C'):
|
||||
if (cfg.telnet_keyboard) {
|
||||
back->special(backhandle, TS_IP);
|
||||
ldisc->back->special(ldisc->backhandle, TS_IP);
|
||||
break;
|
||||
}
|
||||
case CTRL('Z'):
|
||||
if (cfg.telnet_keyboard) {
|
||||
back->special(backhandle, TS_SUSP);
|
||||
ldisc->back->special(ldisc->backhandle, TS_SUSP);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
back->send(backhandle, buf, len);
|
||||
ldisc->back->send(ldisc->backhandle, buf, len);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
back->send(backhandle, buf, len);
|
||||
ldisc->back->send(ldisc->backhandle, buf, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user