Rethink the whole line discipline architecture. Instead of having

multiple switchable line disciplines, we now have a single unified
one which changes its behaviour based on option settings. Each
option setting can be suggested by the back end and/or the terminal
handler, and can be forcibly overridden by the configuration. Local
echo and local line editing are separate, independently switchable,
options.

[originally from svn r895]
This commit is contained in:
Simon Tatham 2001-01-24 14:08:20 +00:00
Родитель 89505459e3
Коммит 7a79df8fe6
12 изменённых файлов: 329 добавлений и 217 удалений

244
ldisc.c
Просмотреть файл

@ -1,21 +1,27 @@
/*
* ldisc.c: PuTTY line discipline. Sits between the input coming
* from keypresses in the window, and the output channel leading to
* the back end. Implements echo and/or local line editing,
* depending on what's currently configured.
*/
#include <windows.h> #include <windows.h>
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
#include "putty.h" #include "putty.h"
/* #define ECHOING (cfg.localecho == LD_YES || \
* ldisc.c: PuTTY line disciplines (cfg.localecho == LD_BACKEND && \
*/ (back->ldisc(LD_ECHO) || term_ldisc(LD_ECHO))))
#define EDITING (cfg.localedit == LD_YES || \
(cfg.localedit == LD_BACKEND && \
(back->ldisc(LD_EDIT) || term_ldisc(LD_EDIT))))
static void c_write (char *buf, int len) { static void c_write (char *buf, int len) {
from_backend(0, buf, len); from_backend(0, buf, len);
} }
static void c_write1 (char c) {
c_write(&c, 1);
}
static char *term_buf = NULL; static char *term_buf = NULL;
static int term_buflen = 0, term_bufsiz = 0, term_quotenext = 0; static int term_buflen = 0, term_bufsiz = 0, term_quotenext = 0;
@ -32,7 +38,7 @@ static int plen(unsigned char c) {
static void pwrite(unsigned char c) { static void pwrite(unsigned char c) {
if ((c >= 32 && c <= 126) || if ((c >= 32 && c <= 126) ||
(c >= 160)) { (c >= 160)) {
c_write1(c); c_write(&c, 1);
} else if (c < 128) { } else if (c < 128) {
char cc[2]; char cc[2];
cc[1] = (c == 127 ? '?' : c + 0x40); cc[1] = (c == 127 ? '?' : c + 0x40);
@ -52,108 +58,124 @@ static void bsb(int n) {
#define CTRL(x) (x^'@') #define CTRL(x) (x^'@')
static void term_send(char *buf, int len) { void ldisc_send(char *buf, int len) {
while (len--) { /*
char c; * Called with len=0 when the options change. We must inform
c = *buf++; * the front end in case it needs to know.
switch (term_quotenext ? ' ' : c) { */
/* if (len == 0) {
* ^h/^?: delete one char and output one BSB void ldisc_update(int echo, int edit);
* ^w: delete, and output BSBs, to return to last space/nonspace ldisc_update(ECHOING, EDITING);
* boundary }
* ^u: delete, and output BSBs, to return to BOL /*
* ^c: Do a ^u then send a telnet IP * Either perform local editing, or just send characters.
* ^z: Do a ^u then send a telnet SUSP */
* ^\: Do a ^u then send a telnet ABORT if (EDITING) {
* ^r: echo "^R\n" and redraw line while (len--) {
* ^v: quote next char char c;
* ^d: if at BOL, end of file and close connection, else send line c = *buf++;
* and reset to BOL switch (term_quotenext ? ' ' : c) {
* ^m: send line-plus-\r\n and reset to BOL /*
*/ * ^h/^?: delete one char and output one BSB
case CTRL('H'): case CTRL('?'): /* backspace/delete */ * ^w: delete, and output BSBs, to return to last
if (term_buflen > 0) { * space/nonspace boundary
bsb(plen(term_buf[term_buflen-1])); * ^u: delete, and output BSBs, to return to BOL
term_buflen--; * ^c: Do a ^u then send a telnet IP
} * ^z: Do a ^u then send a telnet SUSP
break; * ^\: Do a ^u then send a telnet ABORT
case CTRL('W'): /* delete word */ * ^r: echo "^R\n" and redraw line
while (term_buflen > 0) { * ^v: quote next char
bsb(plen(term_buf[term_buflen-1])); * ^d: if at BOL, end of file and close connection,
term_buflen--; * else send line and reset to BOL
if (term_buflen > 0 && * ^m: send line-plus-\r\n and reset to BOL
isspace(term_buf[term_buflen-1]) && */
!isspace(term_buf[term_buflen])) case CTRL('H'): case CTRL('?'): /* backspace/delete */
break; if (term_buflen > 0) {
} if (ECHOING)
break; bsb(plen(term_buf[term_buflen-1]));
case CTRL('U'): /* delete line */ term_buflen--;
case CTRL('C'): /* Send IP */ }
case CTRL('\\'): /* Quit */ break;
case CTRL('Z'): /* Suspend */ case CTRL('W'): /* delete word */
while (term_buflen > 0) { while (term_buflen > 0) {
bsb(plen(term_buf[term_buflen-1])); if (ECHOING)
term_buflen--; bsb(plen(term_buf[term_buflen-1]));
} term_buflen--;
back->special (TS_EL); if (term_buflen > 0 &&
if( c == CTRL('C') ) back->special (TS_IP); isspace(term_buf[term_buflen-1]) &&
if( c == CTRL('Z') ) back->special (TS_SUSP); !isspace(term_buf[term_buflen]))
if( c == CTRL('\\') ) back->special (TS_ABORT); break;
break; }
case CTRL('R'): /* redraw line */ break;
c_write("^R\r\n", 4); case CTRL('U'): /* delete line */
{ case CTRL('C'): /* Send IP */
int i; case CTRL('\\'): /* Quit */
for (i = 0; i < term_buflen; i++) case CTRL('Z'): /* Suspend */
pwrite(term_buf[i]); while (term_buflen > 0) {
} if (ECHOING)
break; bsb(plen(term_buf[term_buflen-1]));
case CTRL('V'): /* quote next char */ term_buflen--;
term_quotenext = TRUE; }
break; back->special (TS_EL);
case CTRL('D'): /* logout or send */ if( c == CTRL('C') ) back->special (TS_IP);
if (term_buflen == 0) { if( c == CTRL('Z') ) back->special (TS_SUSP);
back->special (TS_EOF); if( c == CTRL('\\') ) back->special (TS_ABORT);
} else { break;
back->send(term_buf, term_buflen); case CTRL('R'): /* redraw line */
term_buflen = 0; if (ECHOING) {
} int i;
break; c_write("^R\r\n", 4);
case CTRL('M'): /* send with newline */ for (i = 0; i < term_buflen; i++)
if (term_buflen > 0) pwrite(term_buf[i]);
back->send(term_buf, term_buflen); }
if (cfg.protocol == PROT_RAW) break;
back->send("\r\n", 2); case CTRL('V'): /* quote next char */
else term_quotenext = TRUE;
back->send("\r", 1); break;
c_write("\r\n", 2); case CTRL('D'): /* logout or send */
term_buflen = 0; if (term_buflen == 0) {
break; back->special (TS_EOF);
default: /* get to this label from ^V handler */ } else {
if (term_buflen >= term_bufsiz) { back->send(term_buf, term_buflen);
term_bufsiz = term_buflen + 256; term_buflen = 0;
term_buf = saferealloc(term_buf, term_bufsiz); }
} break;
term_buf[term_buflen++] = c; case CTRL('M'): /* send with newline */
pwrite(c); if (term_buflen > 0)
term_quotenext = FALSE; back->send(term_buf, term_buflen);
break; if (cfg.protocol == PROT_RAW)
} back->send("\r\n", 2);
else
back->send("\r", 1);
if (ECHOING)
c_write("\r\n", 2);
term_buflen = 0;
break;
default: /* get to this label from ^V handler */
if (term_buflen >= term_bufsiz) {
term_bufsiz = term_buflen + 256;
term_buf = saferealloc(term_buf, term_bufsiz);
}
term_buf[term_buflen++] = c;
if (ECHOING)
pwrite(c);
term_quotenext = FALSE;
break;
}
}
} else {
if( term_buflen != 0 )
{
back->send(term_buf, term_buflen);
while (term_buflen > 0) {
bsb(plen(term_buf[term_buflen-1]));
term_buflen--;
}
}
if (len > 0) {
if (ECHOING)
c_write(buf, len);
back->send(buf, len);
}
} }
} }
static void simple_send(char *buf, int len) {
if( term_buflen != 0 )
{
back->send(term_buf, term_buflen);
while (term_buflen > 0) {
bsb(plen(term_buf[term_buflen-1]));
term_buflen--;
}
}
if (len > 0)
back->send(buf, len);
}
Ldisc ldisc_term = { term_send };
Ldisc ldisc_simple = { simple_send };

32
plink.c
Просмотреть файл

@ -119,18 +119,11 @@ void verify_ssh_host_key(char *host, int port, char *keytype,
} }
} }
HANDLE outhandle, errhandle; HANDLE inhandle, outhandle, errhandle;
DWORD orig_console_mode; DWORD orig_console_mode;
WSAEVENT netevent; WSAEVENT netevent;
void begin_session(void) {
if (!cfg.ldisc_term)
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT);
else
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), orig_console_mode);
}
void from_backend(int is_stderr, char *data, int len) { void from_backend(int is_stderr, char *data, int len) {
int pos; int pos;
DWORD ret; DWORD ret;
@ -144,6 +137,23 @@ void from_backend(int is_stderr, char *data, int len) {
} }
} }
int term_ldisc(int mode) { return FALSE; }
void ldisc_update(int echo, int edit) {
/* Update stdin read mode to reflect changes in line discipline. */
DWORD mode;
mode = ENABLE_PROCESSED_INPUT;
if (echo)
mode = mode | ENABLE_ECHO_INPUT;
else
mode = mode &~ ENABLE_ECHO_INPUT;
if (edit)
mode = mode | ENABLE_LINE_INPUT;
else
mode = mode &~ ENABLE_LINE_INPUT;
SetConsoleMode(inhandle, mode);
}
struct input_data { struct input_data {
DWORD len; DWORD len;
char buffer[4096]; char buffer[4096];
@ -403,7 +413,6 @@ int main(int argc, char **argv) {
len2 = strlen(cp); len -= len2; cp += len2; len2 = strlen(cp); len -= len2; cp += len2;
} }
cfg.nopty = TRUE; /* command => no terminal */ cfg.nopty = TRUE; /* command => no terminal */
cfg.ldisc_term = TRUE; /* use stdin like a line buffer */
break; /* done with cmdline */ break; /* done with cmdline */
} }
} }
@ -475,10 +484,11 @@ int main(int argc, char **argv) {
stdinevent = CreateEvent(NULL, FALSE, FALSE, NULL); stdinevent = CreateEvent(NULL, FALSE, FALSE, NULL);
GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &orig_console_mode); inhandle = GetStdHandle(STD_INPUT_HANDLE);
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT);
outhandle = GetStdHandle(STD_OUTPUT_HANDLE); outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
errhandle = GetStdHandle(STD_ERROR_HANDLE); errhandle = GetStdHandle(STD_ERROR_HANDLE);
GetConsoleMode(inhandle, &orig_console_mode);
SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT);
/* /*
* Turn off ECHO and LINE input modes. We don't care if this * Turn off ECHO and LINE input modes. We don't care if this

28
putty.h
Просмотреть файл

@ -115,6 +115,21 @@ typedef enum {
VT_XWINDOWS, VT_OEMANSI, VT_OEMONLY, VT_POORMAN VT_XWINDOWS, VT_OEMANSI, VT_OEMONLY, VT_POORMAN
} VT_Mode; } VT_Mode;
enum {
/*
* Line discipline option states: off, on, up to the backend.
*/
LD_YES, LD_NO, LD_BACKEND
};
enum {
/*
* Line discipline options which the backend might try to control.
*/
LD_EDIT, /* local line editing */
LD_ECHO /* local echo */
};
typedef struct { typedef struct {
char *(*init) (char *host, int port, char **realhost); char *(*init) (char *host, int port, char **realhost);
void (*send) (char *buf, int len); void (*send) (char *buf, int len);
@ -122,6 +137,7 @@ typedef struct {
void (*special) (Telnet_Special code); void (*special) (Telnet_Special code);
Socket (*socket) (void); Socket (*socket) (void);
int (*sendok) (void); int (*sendok) (void);
int (*ldisc) (int);
int default_port; int default_port;
} Backend; } Backend;
@ -133,12 +149,6 @@ extern struct backend_list {
Backend *backend; Backend *backend;
} backends[]; } backends[];
typedef struct {
void (*send) (char *buf, int len);
} Ldisc;
GLOBAL Ldisc *ldisc;
typedef struct { typedef struct {
/* Basic options */ /* Basic options */
char host[512]; char host[512];
@ -176,7 +186,8 @@ typedef struct {
int alt_f4; /* is it special? */ int alt_f4; /* is it special? */
int alt_space; /* is it special? */ int alt_space; /* is it special? */
int alt_only; /* is it special? */ int alt_only; /* is it special? */
int ldisc_term; int localecho;
int localedit;
int alwaysontop; int alwaysontop;
int scroll_on_key; int scroll_on_key;
int scroll_on_disp; int scroll_on_disp;
@ -333,6 +344,7 @@ void term_invalidate(void);
void term_blink(int set_cursor); void term_blink(int set_cursor);
void term_paste(void); void term_paste(void);
void term_nopaste(void); void term_nopaste(void);
int telnet_ldisc(int option);
void from_backend(int is_stderr, char *data, int len); void from_backend(int is_stderr, char *data, int len);
void logfopen (void); void logfopen (void);
void logfclose (void); void logfclose (void);
@ -367,7 +379,7 @@ extern Backend ssh_backend;
* Exports from ldisc.c. * Exports from ldisc.c.
*/ */
extern Ldisc ldisc_term, ldisc_simple; extern void ldisc_send(char *buf, int len);
/* /*
* Exports from sshrand.c. * Exports from sshrand.c.

12
raw.c
Просмотреть файл

@ -70,11 +70,6 @@ static char *raw_init (char *host, int port, char **realhost) {
sk_addr_free(addr); sk_addr_free(addr);
/*
* We have no pre-session phase.
*/
begin_session();
return NULL; return NULL;
} }
@ -109,6 +104,12 @@ static Socket raw_socket(void) { return s; }
static int raw_sendok(void) { return 1; } static int raw_sendok(void) { return 1; }
static int raw_ldisc(int option) {
if (option == LD_EDIT || option == LD_ECHO)
return 1;
return 0;
}
Backend raw_backend = { Backend raw_backend = {
raw_init, raw_init,
raw_send, raw_send,
@ -116,5 +117,6 @@ Backend raw_backend = {
raw_special, raw_special,
raw_socket, raw_socket,
raw_sendok, raw_sendok,
raw_ldisc,
1 1
}; };

Просмотреть файл

@ -102,8 +102,6 @@ static char *rlogin_init (char *host, int port, char **realhost) {
sk_write(s, &z, 1); sk_write(s, &z, 1);
} }
begin_session();
return NULL; return NULL;
} }
@ -142,6 +140,10 @@ static Socket rlogin_socket(void) { return s; }
static int rlogin_sendok(void) { return 1; } static int rlogin_sendok(void) { return 1; }
static int rlogin_ldisc(int option) {
return 0;
}
Backend rlogin_backend = { Backend rlogin_backend = {
rlogin_init, rlogin_init,
rlogin_send, rlogin_send,
@ -149,5 +151,6 @@ Backend rlogin_backend = {
rlogin_special, rlogin_special,
rlogin_socket, rlogin_socket,
rlogin_sendok, rlogin_sendok,
rlogin_ldisc,
1 1
}; };

1
scp.c
Просмотреть файл

@ -74,7 +74,6 @@ static void send_str_msg(unsigned int msg_id, char *str);
static void gui_update_stats(char *name, unsigned long size, static void gui_update_stats(char *name, unsigned long size,
int percentage, unsigned long elapsed); int percentage, unsigned long elapsed);
void begin_session(void) { }
void logevent(char *string) { } void logevent(char *string) { }
void verify_ssh_host_key(char *host, int port, char *keytype, void verify_ssh_host_key(char *host, int port, char *keytype,

Просмотреть файл

@ -93,7 +93,8 @@ void save_settings (char *section, int do_host, Config *cfg) {
write_setting_i (sesskey, "AltSpace", cfg->alt_space); write_setting_i (sesskey, "AltSpace", cfg->alt_space);
write_setting_i (sesskey, "AltOnly", cfg->alt_only); write_setting_i (sesskey, "AltOnly", cfg->alt_only);
write_setting_i (sesskey, "ComposeKey", cfg->compose_key); write_setting_i (sesskey, "ComposeKey", cfg->compose_key);
write_setting_i (sesskey, "LdiscTerm", cfg->ldisc_term); write_setting_i (sesskey, "LocalEcho", cfg->localecho);
write_setting_i (sesskey, "LocalEdit", cfg->localedit);
write_setting_i (sesskey, "AlwaysOnTop", cfg->alwaysontop); write_setting_i (sesskey, "AlwaysOnTop", cfg->alwaysontop);
write_setting_i (sesskey, "HideMousePtr", cfg->hide_mouseptr); write_setting_i (sesskey, "HideMousePtr", cfg->hide_mouseptr);
write_setting_i (sesskey, "CurType", cfg->cursor_type); write_setting_i (sesskey, "CurType", cfg->cursor_type);
@ -237,7 +238,8 @@ void load_settings (char *section, int do_host, Config *cfg) {
gppi (sesskey, "AltSpace", 0, &cfg->alt_space); gppi (sesskey, "AltSpace", 0, &cfg->alt_space);
gppi (sesskey, "AltOnly", 0, &cfg->alt_only); gppi (sesskey, "AltOnly", 0, &cfg->alt_only);
gppi (sesskey, "ComposeKey", 0, &cfg->compose_key); gppi (sesskey, "ComposeKey", 0, &cfg->compose_key);
gppi (sesskey, "LdiscTerm", 0, &cfg->ldisc_term); gppi (sesskey, "LocalEcho", LD_BACKEND, &cfg->localecho);
gppi (sesskey, "LocalEdit", LD_BACKEND, &cfg->localedit);
gppi (sesskey, "AlwaysOnTop", 0, &cfg->alwaysontop); gppi (sesskey, "AlwaysOnTop", 0, &cfg->alwaysontop);
gppi (sesskey, "HideMousePtr", 0, &cfg->hide_mouseptr); gppi (sesskey, "HideMousePtr", 0, &cfg->hide_mouseptr);
gppi (sesskey, "CurType", 0, &cfg->cursor_type); gppi (sesskey, "CurType", 0, &cfg->cursor_type);

20
ssh.c
Просмотреть файл

@ -272,6 +272,7 @@ int (*ssh_get_password)(const char *prompt, char *str, int maxlen) = NULL;
static char *savedhost; static char *savedhost;
static int savedport; static int savedport;
static int ssh_send_ok; static int ssh_send_ok;
static int ssh_echoing, ssh_editing;
static tree234 *ssh_channels; /* indexed by local id */ static tree234 *ssh_channels; /* indexed by local id */
static struct ssh_channel *mainchan; /* primary session channel */ static struct ssh_channel *mainchan; /* primary session channel */
@ -1840,8 +1841,11 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
crReturnV; crReturnV;
} else if (pktin.type == SSH1_SMSG_FAILURE) { } else if (pktin.type == SSH1_SMSG_FAILURE) {
c_write("Server refused to allocate pty\r\n", 32); c_write("Server refused to allocate pty\r\n", 32);
ssh_editing = ssh_echoing = 1;
} }
logevent("Allocated pty"); logevent("Allocated pty");
} else {
ssh_editing = ssh_echoing = 1;
} }
if (cfg.compression) { if (cfg.compression) {
@ -1871,9 +1875,9 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
if (eof_needed) if (eof_needed)
ssh_special(TS_EOF); ssh_special(TS_EOF);
ldisc_send(NULL, 0); /* cause ldisc to notice changes */
ssh_send_ok = 1; ssh_send_ok = 1;
ssh_channels = newtree234(ssh_channelcmp); ssh_channels = newtree234(ssh_channelcmp);
begin_session();
while (1) { while (1) {
crReturnV; crReturnV;
if (ispkt) { if (ispkt) {
@ -2743,9 +2747,12 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
crReturnV; crReturnV;
} }
c_write("Server refused to allocate pty\r\n", 32); c_write("Server refused to allocate pty\r\n", 32);
ssh_editing = ssh_echoing = 1;
} else { } else {
logevent("Allocated pty"); logevent("Allocated pty");
} }
} else {
ssh_editing = ssh_echoing = 1;
} }
/* /*
@ -2793,8 +2800,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
/* /*
* Transfer data! * Transfer data!
*/ */
ldisc_send(NULL, 0); /* cause ldisc to notice changes */
ssh_send_ok = 1; ssh_send_ok = 1;
begin_session();
while (1) { while (1) {
static int try_send; static int try_send;
crReturnV; crReturnV;
@ -3005,6 +3012,8 @@ static char *ssh_init (char *host, int port, char **realhost) {
#endif #endif
ssh_send_ok = 0; ssh_send_ok = 0;
ssh_editing = 0;
ssh_echoing = 0;
p = connect_to_host(host, port, realhost); p = connect_to_host(host, port, realhost);
if (p != NULL) if (p != NULL)
@ -3100,6 +3109,12 @@ static Socket ssh_socket(void) { return s; }
static int ssh_sendok(void) { return ssh_send_ok; } static int ssh_sendok(void) { return ssh_send_ok; }
static int ssh_ldisc(int option) {
if (option == LD_ECHO) return ssh_echoing;
if (option == LD_EDIT) return ssh_editing;
return FALSE;
}
Backend ssh_backend = { Backend ssh_backend = {
ssh_init, ssh_init,
ssh_send, ssh_send,
@ -3107,5 +3122,6 @@ Backend ssh_backend = {
ssh_special, ssh_special,
ssh_socket, ssh_socket,
ssh_sendok, ssh_sendok,
ssh_ldisc,
22 22
}; };

Просмотреть файл

@ -132,6 +132,8 @@ static struct Opt *opts[] = {
&o_we_sga, &o_they_sga, NULL &o_we_sga, &o_they_sga, NULL
}; };
static int echoing = TRUE, editing = TRUE;
static int in_synch; static int in_synch;
static int sb_opt, sb_len; static int sb_opt, sb_len;
static char *sb_buf = NULL; static char *sb_buf = NULL;
@ -170,8 +172,11 @@ static void deactivate_option (struct Opt *o) {
* Generate side effects of enabling or disabling an option. * Generate side effects of enabling or disabling an option.
*/ */
static void option_side_effects(struct Opt *o, int enabled) { static void option_side_effects(struct Opt *o, int enabled) {
if (o->option == TELOPT_ECHO && cfg.ldisc_term) if (o->option == TELOPT_ECHO && o->send == DO)
ldisc = enabled ? &ldisc_simple : &ldisc_term; echoing = !enabled;
else if (o->option = TELOPT_SGA && o->send == DO)
editing = !enabled;
ldisc_send(NULL, 0); /* cause ldisc to notice the change */
} }
static void activate_option (struct Opt *o) { static void activate_option (struct Opt *o) {
@ -509,15 +514,6 @@ static char *telnet_init (char *host, int port, char **realhost) {
/* /*
* Initialise option states. * Initialise option states.
*/ */
if( cfg.ldisc_term )
{
struct Opt **o;
for (o = opts; *o; o++)
if ((*o)->state == REQUESTED)
(*o)->state = INACTIVE;
}
else
{ {
struct Opt **o; struct Opt **o;
@ -531,11 +527,6 @@ static char *telnet_init (char *host, int port, char **realhost) {
*/ */
in_synch = FALSE; in_synch = FALSE;
/*
* We have no pre-session phase.
*/
begin_session();
return NULL; return NULL;
} }
@ -638,6 +629,12 @@ static Socket telnet_socket(void) { return s; }
static int telnet_sendok(void) { return 1; } static int telnet_sendok(void) { return 1; }
static int telnet_ldisc(int option) {
if (option == LD_ECHO) return echoing;
if (option == LD_EDIT) return editing;
return FALSE;
}
Backend telnet_backend = { Backend telnet_backend = {
telnet_init, telnet_init,
telnet_send, telnet_send,
@ -645,5 +642,6 @@ Backend telnet_backend = {
telnet_special, telnet_special,
telnet_socket, telnet_socket,
telnet_sendok, telnet_sendok,
telnet_ldisc,
23 23
}; };

Просмотреть файл

@ -78,6 +78,8 @@ static int use_bce; /* Use Background coloured erase */
static int blinker; /* When blinking is the cursor on ? */ static int blinker; /* When blinking is the cursor on ? */
static int tblinker; /* When the blinking text is on */ static int tblinker; /* When the blinking text is on */
static int blink_is_real; /* Actually blink blinking text */ static int blink_is_real; /* Actually blink blinking text */
static int term_echoing; /* Does terminal want local echo? */
static int term_editing; /* Does terminal want local edit? */
static unsigned long cset_attr[2]; static unsigned long cset_attr[2];
@ -183,6 +185,8 @@ static void power_on(void) {
rvideo = 0; rvideo = 0;
cursor_on = 1; cursor_on = 1;
save_attr = curr_attr = ATTR_DEFAULT; save_attr = curr_attr = ATTR_DEFAULT;
term_editing = term_echoing = FALSE;
ldisc_send(NULL, 0); /* cause ldisc to notice changes */
app_cursor_keys = cfg.app_cursor; app_cursor_keys = cfg.app_cursor;
app_keypad_keys = cfg.app_keypad; app_keypad_keys = cfg.app_keypad;
use_bce = cfg.bce; use_bce = cfg.bce;
@ -707,6 +711,10 @@ static void toggle_mode (int mode, int query, int state) {
case 8: /* auto key repeat */ case 8: /* auto key repeat */
repeat_off = !state; repeat_off = !state;
break; break;
case 10: /* set local edit mode */
term_editing = state;
ldisc_send(NULL, 0); /* cause ldisc to notice changes */
break;
case 25: /* enable/disable cursor */ case 25: /* enable/disable cursor */
compatibility2(OTHER,VT220); compatibility2(OTHER,VT220);
cursor_on = state; cursor_on = state;
@ -724,16 +732,8 @@ static void toggle_mode (int mode, int query, int state) {
insert = state; insert = state;
break; break;
case 12: /* set echo mode */ case 12: /* set echo mode */
/* term_echoing = !state;
* This may be very good in smcup and rmcup (or smkx & rmkx) if you ldisc_send(NULL, 0); /* cause ldisc to notice changes */
* have a long RTT and the telnet client/daemon doesn't understand
* linemode.
*
* DONT send TS_RECHO/TS_LECHO; the telnet daemon tries to fix the
* tty and _really_ confuses some programs.
*/
compatibility2(OTHER,VT220);
ldisc = (state? &ldisc_simple : &ldisc_term);
break; break;
case 20: /* Return sends ... */ case 20: /* Return sends ... */
cr_lf_return = state; cr_lf_return = state;
@ -804,7 +804,7 @@ static int beep_overload = 0;
* An xterm returns "xterm" (5 characters) * An xterm returns "xterm" (5 characters)
*/ */
compatibility(OTHER); compatibility(OTHER);
ldisc->send ("PuTTY", 5); ldisc_send ("PuTTY", 5);
break; break;
case '\007': case '\007':
beep_count++; beep_count++;
@ -1056,7 +1056,7 @@ static int beep_overload = 0;
break; break;
case 'Z': /* terminal type query */ case 'Z': /* terminal type query */
compatibility(VT100); compatibility(VT100);
ldisc->send (id_string, strlen(id_string)); ldisc_send (id_string, strlen(id_string));
break; break;
case 'c': /* restore power-on settings */ case 'c': /* restore power-on settings */
compatibility(VT100); compatibility(VT100);
@ -1199,16 +1199,16 @@ static int beep_overload = 0;
case 'c': /* terminal type query */ case 'c': /* terminal type query */
compatibility(VT100); compatibility(VT100);
/* This is the response for a VT102 */ /* This is the response for a VT102 */
ldisc->send (id_string, strlen(id_string)); ldisc_send (id_string, strlen(id_string));
break; break;
case 'n': /* cursor position query */ case 'n': /* cursor position query */
if (esc_args[0] == 6) { if (esc_args[0] == 6) {
char buf[32]; char buf[32];
sprintf (buf, "\033[%d;%dR", curs_y + 1, curs_x + 1); sprintf (buf, "\033[%d;%dR", curs_y + 1, curs_x + 1);
ldisc->send (buf, strlen(buf)); ldisc_send (buf, strlen(buf));
} }
else if (esc_args[0] == 5) { else if (esc_args[0] == 5) {
ldisc->send ("\033[0n", 4); ldisc_send ("\033[0n", 4);
} }
break; break;
case 'h': /* toggle modes to high */ case 'h': /* toggle modes to high */
@ -1420,7 +1420,7 @@ static int beep_overload = 0;
if (i == 0 || i == 1) { if (i == 0 || i == 1) {
strcpy (buf, "\033[2;1;1;112;112;1;0x"); strcpy (buf, "\033[2;1;1;112;112;1;0x");
buf[2] += i; buf[2] += i;
ldisc->send (buf, 20); ldisc_send (buf, 20);
} }
} }
break; break;
@ -1692,7 +1692,7 @@ static int beep_overload = 0;
termstate = VT52_Y1; termstate = VT52_Y1;
break; break;
case 'Z': case 'Z':
ldisc->send ("\033/Z", 3); ldisc_send ("\033/Z", 3);
break; break;
case '=': case '=':
app_keypad_keys = TRUE; app_keypad_keys = TRUE;
@ -2162,7 +2162,7 @@ void term_mouse (Mouse_Button b, Mouse_Action a, int x, int y) {
/* Assume a small paste will be OK in one go. */ /* Assume a small paste will be OK in one go. */
if (paste_len<256) { if (paste_len<256) {
ldisc->send (paste_buffer, paste_len); ldisc_send (paste_buffer, paste_len);
if (paste_buffer) sfree(paste_buffer); if (paste_buffer) sfree(paste_buffer);
paste_buffer = 0; paste_buffer = 0;
paste_pos = paste_hold = paste_len = 0; paste_pos = paste_hold = paste_len = 0;
@ -2203,7 +2203,7 @@ void term_paste() {
if (paste_buffer[paste_pos + n++] == '\r') if (paste_buffer[paste_pos + n++] == '\r')
break; break;
} }
ldisc->send (paste_buffer+paste_pos, n); ldisc_send (paste_buffer+paste_pos, n);
paste_pos += n; paste_pos += n;
if (paste_pos < paste_len) { if (paste_pos < paste_len) {
@ -2226,6 +2226,12 @@ void term_deselect (void) {
term_update(); term_update();
} }
int term_ldisc(int option) {
if (option == LD_ECHO) return term_echoing;
if (option == LD_EDIT) return term_editing;
return FALSE;
}
/* /*
* from_backend(), to get data from the backend for the terminal. * from_backend(), to get data from the backend for the terminal.
*/ */

Просмотреть файл

@ -200,6 +200,17 @@ enum { IDCX_ABOUT = IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, controlstartvalue,
IDC_CLOSEEXIT, IDC_CLOSEEXIT,
sessionpanelend, sessionpanelend,
loggingpanelstart,
IDC_BOX_LOGGING1,
IDC_LSTATSTATIC,
IDC_LSTATOFF,
IDC_LSTATASCII,
IDC_LSTATRAW,
IDC_LGFSTATIC,
IDC_LGFEDIT,
IDC_LGFBUTTON,
loggingpanelend,
keyboardpanelstart, keyboardpanelstart,
IDC_TITLE_KEYBOARD, IDC_TITLE_KEYBOARD,
IDC_BOX_KEYBOARD1, IDC_BOX_KEYBOARD1,
@ -238,14 +249,14 @@ enum { IDCX_ABOUT = IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, controlstartvalue,
IDC_BEEP, IDC_BEEP,
IDC_BCE, IDC_BCE,
IDC_BLINKTEXT, IDC_BLINKTEXT,
IDC_LDISCTERM, IDC_ECHOSTATIC,
IDC_LSTATSTATIC, IDC_ECHOBACKEND,
IDC_LSTATOFF, IDC_ECHOYES,
IDC_LSTATASCII, IDC_ECHONO,
IDC_LSTATRAW, IDC_EDITSTATIC,
IDC_LGFSTATIC, IDC_EDITBACKEND,
IDC_LGFEDIT, IDC_EDITYES,
IDC_LGFBUTTON, IDC_EDITNO,
terminalpanelend, terminalpanelend,
windowpanelstart, windowpanelstart,
@ -489,7 +500,12 @@ static void init_dlg_ctrls(HWND hwnd) {
CheckDlgButton (hwnd, IDC_ALTSPACE, cfg.alt_space); CheckDlgButton (hwnd, IDC_ALTSPACE, cfg.alt_space);
CheckDlgButton (hwnd, IDC_ALTONLY, cfg.alt_only); CheckDlgButton (hwnd, IDC_ALTONLY, cfg.alt_only);
CheckDlgButton (hwnd, IDC_COMPOSEKEY, cfg.compose_key); CheckDlgButton (hwnd, IDC_COMPOSEKEY, cfg.compose_key);
CheckDlgButton (hwnd, IDC_LDISCTERM, cfg.ldisc_term); CheckRadioButton (hwnd, IDC_ECHOBACKEND, IDC_ECHONO,
cfg.localecho == LD_BACKEND ? IDC_ECHOBACKEND:
cfg.localecho == LD_YES ? IDC_ECHOYES : IDC_ECHONO);
CheckRadioButton (hwnd, IDC_EDITBACKEND, IDC_EDITNO,
cfg.localedit == LD_BACKEND ? IDC_EDITBACKEND:
cfg.localedit == LD_YES ? IDC_EDITYES : IDC_EDITNO);
CheckDlgButton (hwnd, IDC_ALWAYSONTOP, cfg.alwaysontop); CheckDlgButton (hwnd, IDC_ALWAYSONTOP, cfg.alwaysontop);
CheckDlgButton (hwnd, IDC_SCROLLKEY, cfg.scroll_on_key); CheckDlgButton (hwnd, IDC_SCROLLKEY, cfg.scroll_on_key);
CheckDlgButton (hwnd, IDC_SCROLLDISP, cfg.scroll_on_disp); CheckDlgButton (hwnd, IDC_SCROLLDISP, cfg.scroll_on_disp);
@ -681,8 +697,26 @@ static void create_controls(HWND hwnd, int dlgtype, int panel) {
endbox(&cp); endbox(&cp);
} }
if (panel == loggingpanelstart) {
/* The Logging panel. Accelerators used: [acgo] lpt */
struct ctlpos cp;
ctlposinit(&cp, hwnd, 80, 3, 13);
bartitle(&cp, "Options controlling session logging",
IDC_TITLE_TERMINAL);
beginbox(&cp, NULL, IDC_BOX_LOGGING1);
radiobig(&cp,
"Session logging:", IDC_LSTATSTATIC,
"Logging &turned off completely", IDC_LSTATOFF,
"Log &printable output only", IDC_LSTATASCII,
"&Log all session output", IDC_LSTATRAW, NULL);
editbutton(&cp, "Log &file name:",
IDC_LGFSTATIC, IDC_LGFEDIT, "Bro&wse...",
IDC_LGFBUTTON);
endbox(&cp);
}
if (panel == terminalpanelstart) { if (panel == terminalpanelstart) {
/* The Terminal panel. Accelerators used: [acgo] &dflbenuw */ /* The Terminal panel. Accelerators used: [acgo] &dflbentuw */
struct ctlpos cp; struct ctlpos cp;
ctlposinit(&cp, hwnd, 80, 3, 13); ctlposinit(&cp, hwnd, 80, 3, 13);
bartitle(&cp, "Options controlling the terminal emulation", bartitle(&cp, "Options controlling the terminal emulation",
@ -695,19 +729,18 @@ static void create_controls(HWND hwnd, int dlgtype, int panel) {
checkbox(&cp, "&Beep enabled", IDC_BEEP); checkbox(&cp, "&Beep enabled", IDC_BEEP);
checkbox(&cp, "Use background colour to &erase screen", IDC_BCE); checkbox(&cp, "Use background colour to &erase screen", IDC_BCE);
checkbox(&cp, "Enable bli&nking text", IDC_BLINKTEXT); checkbox(&cp, "Enable bli&nking text", IDC_BLINKTEXT);
checkbox(&cp, "&Use local terminal line discipline", IDC_LDISCTERM);
endbox(&cp); endbox(&cp);
beginbox(&cp, "Control session logging", beginbox(&cp, "Line discipline options",
IDC_BOX_TERMINAL2); IDC_BOX_TERMINAL2);
radiobig(&cp, radioline(&cp, "Local echo:", IDC_ECHOSTATIC, 3,
"Session logging:", IDC_LSTATSTATIC, "A&uto", IDC_ECHOBACKEND,
"Logging turned &off completely", IDC_LSTATOFF, "Force on", IDC_ECHOYES,
"Log printable output only", IDC_LSTATASCII, "Force off", IDC_ECHONO, NULL);
"Log all session output", IDC_LSTATRAW, NULL); radioline(&cp, "Local line editing:", IDC_EDITSTATIC, 3,
editbutton(&cp, "Log &file name:", "Au&to", IDC_EDITBACKEND,
IDC_LGFSTATIC, IDC_LGFEDIT, "Bro&wse...", "Force on", IDC_EDITYES,
IDC_LGFBUTTON); "Force off", IDC_EDITNO, NULL);
endbox(&cp); endbox(&cp);
} }
@ -1081,6 +1114,7 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg,
* Set up the tree view contents. * Set up the tree view contents.
*/ */
hsession = treeview_insert(&tvfaff, 0, "Session"); hsession = treeview_insert(&tvfaff, 0, "Session");
treeview_insert(&tvfaff, 1, "Logging");
treeview_insert(&tvfaff, 0, "Terminal"); treeview_insert(&tvfaff, 0, "Terminal");
treeview_insert(&tvfaff, 1, "Keyboard"); treeview_insert(&tvfaff, 1, "Keyboard");
treeview_insert(&tvfaff, 0, "Window"); treeview_insert(&tvfaff, 0, "Window");
@ -1144,6 +1178,8 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg,
} }
if (!strcmp(buffer, "Session")) if (!strcmp(buffer, "Session"))
create_controls(hwnd, dlgtype, sessionpanelstart); create_controls(hwnd, dlgtype, sessionpanelstart);
if (!strcmp(buffer, "Logging"))
create_controls(hwnd, dlgtype, loggingpanelstart);
if (!strcmp(buffer, "Keyboard")) if (!strcmp(buffer, "Keyboard"))
create_controls(hwnd, dlgtype, keyboardpanelstart); create_controls(hwnd, dlgtype, keyboardpanelstart);
if (!strcmp(buffer, "Terminal")) if (!strcmp(buffer, "Terminal"))
@ -1398,10 +1434,25 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg,
HIWORD(wParam) == BN_DOUBLECLICKED) HIWORD(wParam) == BN_DOUBLECLICKED)
cfg.alt_only = IsDlgButtonChecked (hwnd, IDC_ALTONLY); cfg.alt_only = IsDlgButtonChecked (hwnd, IDC_ALTONLY);
break; break;
case IDC_LDISCTERM: case IDC_ECHOBACKEND:
case IDC_ECHOYES:
case IDC_ECHONO:
if (HIWORD(wParam) == BN_CLICKED || if (HIWORD(wParam) == BN_CLICKED ||
HIWORD(wParam) == BN_DOUBLECLICKED) HIWORD(wParam) == BN_DOUBLECLICKED) {
cfg.ldisc_term = IsDlgButtonChecked (hwnd, IDC_LDISCTERM); if (LOWORD(wParam)==IDC_ECHOBACKEND) cfg.localecho=LD_BACKEND;
if (LOWORD(wParam)==IDC_ECHOYES) cfg.localecho=LD_YES;
if (LOWORD(wParam)==IDC_ECHONO) cfg.localecho=LD_NO;
}
break;
case IDC_EDITBACKEND:
case IDC_EDITYES:
case IDC_EDITNO:
if (HIWORD(wParam) == BN_CLICKED ||
HIWORD(wParam) == BN_DOUBLECLICKED) {
if (LOWORD(wParam)==IDC_EDITBACKEND) cfg.localedit=LD_BACKEND;
if (LOWORD(wParam)==IDC_EDITYES) cfg.localedit=LD_YES;
if (LOWORD(wParam)==IDC_EDITNO) cfg.localedit=LD_NO;
}
break; break;
case IDC_ALWAYSONTOP: case IDC_ALWAYSONTOP:
if (HIWORD(wParam) == BN_CLICKED || if (HIWORD(wParam) == BN_CLICKED ||

Просмотреть файл

@ -101,13 +101,10 @@ static Mouse_Button lastbtn;
static char *window_name, *icon_name; static char *window_name, *icon_name;
static Ldisc *real_ldisc;
static int compose_state = 0; static int compose_state = 0;
void begin_session(void) { /* Dummy routine, only required in plink. */
ldisc = real_ldisc; void ldisc_update(int echo, int edit) {}
}
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
static char appname[] = "PuTTY"; static char appname[] = "PuTTY";
@ -318,11 +315,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
return 1; return 1;
} }
real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
/* To start with, we use the simple line discipline, so we can
* type passwords etc without fear of them being echoed... */
ldisc = &ldisc_simple;
if (!prev) { if (!prev) {
wndclass.style = 0; wndclass.style = 0;
wndclass.lpfnWndProc = WndProc; wndclass.lpfnWndProc = WndProc;
@ -1220,11 +1212,10 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
init_fonts(0); init_fonts(0);
sfree(logpal); sfree(logpal);
/* /*
* Telnet will change local echo -> remote if the * Flush the line discipline's edit buffer in the
* remote asks. * case where local editing has just been disabled.
*/ */
if (cfg.protocol != PROT_TELNET) ldisc_send(NULL, 0);
ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
if (pal) if (pal)
DeleteObject(pal); DeleteObject(pal);
logpal = NULL; logpal = NULL;
@ -1615,7 +1606,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
len = TranslateKey (message, wParam, lParam, buf); len = TranslateKey (message, wParam, lParam, buf);
if (len == -1) if (len == -1)
return DefWindowProc (hwnd, message, wParam, lParam); return DefWindowProc (hwnd, message, wParam, lParam);
ldisc->send (buf, len); ldisc_send (buf, len);
if (len > 0) if (len > 0)
show_mouseptr(0); show_mouseptr(0);
@ -1628,7 +1619,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
buf[1] = wParam; buf[1] = wParam;
buf[0] = wParam >> 8; buf[0] = wParam >> 8;
ldisc->send (buf, 2); ldisc_send (buf, 2);
} }
case WM_CHAR: case WM_CHAR:
case WM_SYSCHAR: case WM_SYSCHAR:
@ -1640,7 +1631,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
*/ */
{ {
char c = xlat_kbd2tty((unsigned char)wParam); char c = xlat_kbd2tty((unsigned char)wParam);
ldisc->send (&c, 1); ldisc_send (&c, 1);
} }
return 0; return 0;
} }