mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-03-22 06:38:37 -05:00
From RDB: a patch to allow special keys (^C, ^Z, Delete, Return) to
send Telnet special sequences (Interrupt Process, Suspend, Erase Char, End Of Line) instead of their ASCII equivalents. In particular Return -> Telnet End Of Line is _always_ enabled irrespective of the configuration, while the others are optional. Also in this patch, an entertainingly ghastly use of `switch' to allow literal ^M^J to do the same thing as magic-^M (the Return key) when in Raw protocol. [originally from svn r1109]
This commit is contained in:
parent
c2eb57a034
commit
e001f1533e
82
ldisc.c
82
ldisc.c
@ -59,9 +59,11 @@ static void bsb(int n)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define CTRL(x) (x^'@')
|
#define CTRL(x) (x^'@')
|
||||||
|
#define KCTRL(x) ((x^'@') | 0x100)
|
||||||
|
|
||||||
void ldisc_send(char *buf, int len)
|
void ldisc_send(char *buf, int len)
|
||||||
{
|
{
|
||||||
|
int keyflag = 0;
|
||||||
/*
|
/*
|
||||||
* Called with len=0 when the options change. We must inform
|
* Called with len=0 when the options change. We must inform
|
||||||
* the front end in case it needs to know.
|
* the front end in case it needs to know.
|
||||||
@ -70,13 +72,20 @@ void ldisc_send(char *buf, int len)
|
|||||||
void ldisc_update(int echo, int edit);
|
void ldisc_update(int echo, int edit);
|
||||||
ldisc_update(ECHOING, EDITING);
|
ldisc_update(ECHOING, EDITING);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Less than zero means null terminated special string.
|
||||||
|
*/
|
||||||
|
if (len < 0) {
|
||||||
|
len = strlen(buf);
|
||||||
|
keyflag = KCTRL('@');
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Either perform local editing, or just send characters.
|
* Either perform local editing, or just send characters.
|
||||||
*/
|
*/
|
||||||
if (EDITING) {
|
if (EDITING) {
|
||||||
while (len--) {
|
while (len--) {
|
||||||
char c;
|
int c;
|
||||||
c = *buf++;
|
c = *buf++ + keyflag;
|
||||||
switch (term_quotenext ? ' ' : c) {
|
switch (term_quotenext ? ' ' : c) {
|
||||||
/*
|
/*
|
||||||
* ^h/^?: delete one char and output one BSB
|
* ^h/^?: delete one char and output one BSB
|
||||||
@ -92,8 +101,8 @@ void ldisc_send(char *buf, int len)
|
|||||||
* else send line and reset to BOL
|
* else send line and reset to BOL
|
||||||
* ^m: send line-plus-\r\n and reset to BOL
|
* ^m: send line-plus-\r\n and reset to BOL
|
||||||
*/
|
*/
|
||||||
case CTRL('H'):
|
case KCTRL('H'):
|
||||||
case CTRL('?'): /* backspace/delete */
|
case KCTRL('?'): /* backspace/delete */
|
||||||
if (term_buflen > 0) {
|
if (term_buflen > 0) {
|
||||||
if (ECHOING)
|
if (ECHOING)
|
||||||
bsb(plen(term_buf[term_buflen - 1]));
|
bsb(plen(term_buf[term_buflen - 1]));
|
||||||
@ -147,17 +156,51 @@ void ldisc_send(char *buf, int len)
|
|||||||
term_buflen = 0;
|
term_buflen = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CTRL('M'): /* send with newline */
|
/*
|
||||||
|
* This particularly hideous bit of code from RDB
|
||||||
|
* allows ordinary ^M^J to do the same thing as
|
||||||
|
* magic-^M when in Raw protocol. The line `case
|
||||||
|
* KCTRL('M'):' is _inside_ the if block. Thus:
|
||||||
|
*
|
||||||
|
* - receiving regular ^M goes straight to the
|
||||||
|
* default clause and inserts as a literal ^M.
|
||||||
|
* - receiving regular ^J _not_ directly after a
|
||||||
|
* literal ^M (or not in Raw protocol) fails the
|
||||||
|
* if condition, leaps to the bottom of the if,
|
||||||
|
* and falls through into the default clause
|
||||||
|
* again.
|
||||||
|
* - receiving regular ^J just after a literal ^M
|
||||||
|
* in Raw protocol passes the if condition,
|
||||||
|
* deletes the literal ^M, and falls through
|
||||||
|
* into the magic-^M code
|
||||||
|
* - receiving a magic-^M empties the line buffer,
|
||||||
|
* signals end-of-line in one of the various
|
||||||
|
* entertaining ways, and _doesn't_ fall out of
|
||||||
|
* the bottom of the if and through to the
|
||||||
|
* default clause because of the break.
|
||||||
|
*/
|
||||||
|
case CTRL('J'):
|
||||||
|
if (cfg.protocol == PROT_RAW &&
|
||||||
|
term_buflen > 0 && term_buf[term_buflen - 1] == '\r') {
|
||||||
|
if (ECHOING)
|
||||||
|
bsb(plen(term_buf[term_buflen - 1]));
|
||||||
|
term_buflen--;
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
case KCTRL('M'): /* send with newline */
|
||||||
if (term_buflen > 0)
|
if (term_buflen > 0)
|
||||||
back->send(term_buf, term_buflen);
|
back->send(term_buf, term_buflen);
|
||||||
if (cfg.protocol == PROT_RAW)
|
if (cfg.protocol == PROT_RAW)
|
||||||
back->send("\r\n", 2);
|
back->send("\r\n", 2);
|
||||||
|
else if (cfg.protocol == PROT_TELNET)
|
||||||
|
back->special(TS_EOL);
|
||||||
else
|
else
|
||||||
back->send("\r", 1);
|
back->send("\r", 1);
|
||||||
if (ECHOING)
|
if (ECHOING)
|
||||||
c_write("\r\n", 2);
|
c_write("\r\n", 2);
|
||||||
term_buflen = 0;
|
term_buflen = 0;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
/* FALLTHROUGH */
|
||||||
default: /* get to this label from ^V handler */
|
default: /* get to this label from ^V handler */
|
||||||
if (term_buflen >= term_bufsiz) {
|
if (term_buflen >= term_bufsiz) {
|
||||||
term_bufsiz = term_buflen + 256;
|
term_bufsiz = term_buflen + 256;
|
||||||
@ -165,7 +208,7 @@ void ldisc_send(char *buf, int len)
|
|||||||
}
|
}
|
||||||
term_buf[term_buflen++] = c;
|
term_buf[term_buflen++] = c;
|
||||||
if (ECHOING)
|
if (ECHOING)
|
||||||
pwrite(c);
|
pwrite((unsigned char) c);
|
||||||
term_quotenext = FALSE;
|
term_quotenext = FALSE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -181,6 +224,33 @@ void ldisc_send(char *buf, int len)
|
|||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
if (ECHOING)
|
if (ECHOING)
|
||||||
c_write(buf, len);
|
c_write(buf, len);
|
||||||
|
if (keyflag && cfg.protocol == PROT_TELNET && len == 1) {
|
||||||
|
switch (buf[0]) {
|
||||||
|
case CTRL('M'):
|
||||||
|
back->special(TS_EOL);
|
||||||
|
break;
|
||||||
|
case CTRL('?'):
|
||||||
|
case CTRL('H'):
|
||||||
|
if (cfg.telnet_keyboard) {
|
||||||
|
back->special(TS_EC);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CTRL('C'):
|
||||||
|
if (cfg.telnet_keyboard) {
|
||||||
|
back->special(TS_IP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CTRL('Z'):
|
||||||
|
if (cfg.telnet_keyboard) {
|
||||||
|
back->special(TS_SUSP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
back->send(buf, len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else
|
||||||
back->send(buf, len);
|
back->send(buf, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
putty.h
4
putty.h
@ -107,7 +107,8 @@ GLOBAL HWND logbox;
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT,
|
TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT,
|
||||||
TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF, TS_LECHO, TS_RECHO, TS_PING
|
TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF, TS_LECHO, TS_RECHO, TS_PING,
|
||||||
|
TS_EOL
|
||||||
} Telnet_Special;
|
} Telnet_Special;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -206,6 +207,7 @@ typedef struct {
|
|||||||
int app_cursor;
|
int app_cursor;
|
||||||
int app_keypad;
|
int app_keypad;
|
||||||
int nethack_keypad;
|
int nethack_keypad;
|
||||||
|
int telnet_keyboard;
|
||||||
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? */
|
||||||
|
@ -100,6 +100,7 @@ void save_settings(char *section, int do_host, Config * cfg)
|
|||||||
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, "CtrlAltKeys", cfg->ctrlaltkeys);
|
write_setting_i(sesskey, "CtrlAltKeys", cfg->ctrlaltkeys);
|
||||||
|
write_setting_i(sesskey, "TelnetKey", cfg->telnet_keyboard);
|
||||||
write_setting_i(sesskey, "LocalEcho", cfg->localecho);
|
write_setting_i(sesskey, "LocalEcho", cfg->localecho);
|
||||||
write_setting_i(sesskey, "LocalEdit", cfg->localedit);
|
write_setting_i(sesskey, "LocalEdit", cfg->localedit);
|
||||||
write_setting_s(sesskey, "Answerback", cfg->answerback);
|
write_setting_s(sesskey, "Answerback", cfg->answerback);
|
||||||
@ -263,6 +264,7 @@ void load_settings(char *section, int do_host, Config * cfg)
|
|||||||
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, "CtrlAltKeys", 1, &cfg->ctrlaltkeys);
|
gppi(sesskey, "CtrlAltKeys", 1, &cfg->ctrlaltkeys);
|
||||||
|
gppi(sesskey, "TelnetKey", 0, &cfg->telnet_keyboard);
|
||||||
gppi(sesskey, "LocalEcho", LD_BACKEND, &cfg->localecho);
|
gppi(sesskey, "LocalEcho", LD_BACKEND, &cfg->localecho);
|
||||||
gppi(sesskey, "LocalEdit", LD_BACKEND, &cfg->localedit);
|
gppi(sesskey, "LocalEdit", LD_BACKEND, &cfg->localedit);
|
||||||
gpps(sesskey, "Answerback", "PuTTY", cfg->answerback,
|
gpps(sesskey, "Answerback", "PuTTY", cfg->answerback,
|
||||||
|
7
telnet.c
7
telnet.c
@ -653,7 +653,9 @@ static void telnet_send(char *buf, int len)
|
|||||||
char *p;
|
char *p;
|
||||||
static unsigned char iac[2] = { IAC, IAC };
|
static unsigned char iac[2] = { IAC, IAC };
|
||||||
static unsigned char cr[2] = { CR, NUL };
|
static unsigned char cr[2] = { CR, NUL };
|
||||||
|
#if 0
|
||||||
static unsigned char nl[2] = { CR, LF };
|
static unsigned char nl[2] = { CR, LF };
|
||||||
|
#endif
|
||||||
|
|
||||||
if (s == NULL)
|
if (s == NULL)
|
||||||
return;
|
return;
|
||||||
@ -667,7 +669,7 @@ static void telnet_send(char *buf, int len)
|
|||||||
sk_write(s, q, p - q);
|
sk_write(s, q, p - q);
|
||||||
|
|
||||||
while (p < buf + len && !iswritable((unsigned char) *p)) {
|
while (p < buf + len && !iswritable((unsigned char) *p)) {
|
||||||
sk_write(s, (unsigned char) *p == IAC ? iac : nl, 2);
|
sk_write(s, (unsigned char) *p == IAC ? iac : cr, 2);
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -759,6 +761,9 @@ static void telnet_special(Telnet_Special code)
|
|||||||
b[1] = xEOF;
|
b[1] = xEOF;
|
||||||
sk_write(s, b, 2);
|
sk_write(s, b, 2);
|
||||||
break;
|
break;
|
||||||
|
case TS_EOL:
|
||||||
|
sk_write(s, "\r\n", 2);
|
||||||
|
break;
|
||||||
case TS_SYNCH:
|
case TS_SYNCH:
|
||||||
b[1] = DM;
|
b[1] = DM;
|
||||||
sk_write(s, b, 1);
|
sk_write(s, b, 1);
|
||||||
|
17
windlg.c
17
windlg.c
@ -291,6 +291,7 @@ enum { IDCX_ABOUT =
|
|||||||
IDC_CURAPPLIC,
|
IDC_CURAPPLIC,
|
||||||
IDC_COMPOSEKEY,
|
IDC_COMPOSEKEY,
|
||||||
IDC_CTRLALTKEYS,
|
IDC_CTRLALTKEYS,
|
||||||
|
IDC_TELNETKEY,
|
||||||
keyboardpanelend,
|
keyboardpanelend,
|
||||||
|
|
||||||
terminalpanelstart,
|
terminalpanelstart,
|
||||||
@ -588,6 +589,7 @@ static void init_dlg_ctrls(HWND hwnd)
|
|||||||
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_CTRLALTKEYS, cfg.ctrlaltkeys);
|
CheckDlgButton(hwnd, IDC_CTRLALTKEYS, cfg.ctrlaltkeys);
|
||||||
|
CheckDlgButton(hwnd, IDC_TELNETKEY, cfg.telnet_keyboard);
|
||||||
CheckRadioButton(hwnd, IDC_ECHOBACKEND, IDC_ECHONO,
|
CheckRadioButton(hwnd, IDC_ECHOBACKEND, IDC_ECHONO,
|
||||||
cfg.localecho == LD_BACKEND ? IDC_ECHOBACKEND :
|
cfg.localecho == LD_BACKEND ? IDC_ECHOBACKEND :
|
||||||
cfg.localecho == LD_YES ? IDC_ECHOYES : IDC_ECHONO);
|
cfg.localecho == LD_YES ? IDC_ECHOYES : IDC_ECHONO);
|
||||||
@ -1098,6 +1100,11 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
|
|||||||
staticedit(&cp, "Auto-login &username", IDC_LOGSTATIC,
|
staticedit(&cp, "Auto-login &username", IDC_LOGSTATIC,
|
||||||
IDC_LOGEDIT, 50);
|
IDC_LOGEDIT, 50);
|
||||||
endbox(&cp);
|
endbox(&cp);
|
||||||
|
} else {
|
||||||
|
beginbox(&cp, "Adjust telnet session.", IDC_BOX_CONNECTION1);
|
||||||
|
checkbox(&cp, "Keyboard sends telnet Backspace and Interrupt",
|
||||||
|
IDC_TELNETKEY);
|
||||||
|
endbox(&cp);
|
||||||
}
|
}
|
||||||
beginbox(&cp, "Sending of null packets to keep session active",
|
beginbox(&cp, "Sending of null packets to keep session active",
|
||||||
IDC_BOX_CONNECTION2);
|
IDC_BOX_CONNECTION2);
|
||||||
@ -1107,7 +1114,7 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (panel == telnetpanelstart) {
|
if (panel == telnetpanelstart) {
|
||||||
/* The Telnet panel. Accelerators used: [acgo] svldr bft */
|
/* The Telnet panel. Accelerators used: [acgo] svldr bftk */
|
||||||
struct ctlpos cp;
|
struct ctlpos cp;
|
||||||
ctlposinit(&cp, hwnd, 80, 3, 13);
|
ctlposinit(&cp, hwnd, 80, 3, 13);
|
||||||
if (dlgtype == 0) {
|
if (dlgtype == 0) {
|
||||||
@ -1128,6 +1135,8 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
|
|||||||
radioline(&cp, "&Telnet negotiation mode:", IDC_ACTSTATIC, 2,
|
radioline(&cp, "&Telnet negotiation mode:", IDC_ACTSTATIC, 2,
|
||||||
"Passive", IDC_TPASSIVE, "Active",
|
"Passive", IDC_TPASSIVE, "Active",
|
||||||
IDC_TACTIVE, NULL);
|
IDC_TACTIVE, NULL);
|
||||||
|
checkbox(&cp, "&Keyboard sends telnet Backspace and Interrupt",
|
||||||
|
IDC_TELNETKEY);
|
||||||
endbox(&cp);
|
endbox(&cp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1698,6 +1707,12 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
|
|||||||
cfg.ctrlaltkeys =
|
cfg.ctrlaltkeys =
|
||||||
IsDlgButtonChecked(hwnd, IDC_CTRLALTKEYS);
|
IsDlgButtonChecked(hwnd, IDC_CTRLALTKEYS);
|
||||||
break;
|
break;
|
||||||
|
case IDC_TELNETKEY:
|
||||||
|
if (HIWORD(wParam) == BN_CLICKED ||
|
||||||
|
HIWORD(wParam) == BN_DOUBLECLICKED)
|
||||||
|
cfg.telnet_keyboard =
|
||||||
|
IsDlgButtonChecked(hwnd, IDC_TELNETKEY);
|
||||||
|
break;
|
||||||
case IDC_WRAPMODE:
|
case IDC_WRAPMODE:
|
||||||
if (HIWORD(wParam) == BN_CLICKED ||
|
if (HIWORD(wParam) == BN_CLICKED ||
|
||||||
HIWORD(wParam) == BN_DOUBLECLICKED)
|
HIWORD(wParam) == BN_DOUBLECLICKED)
|
||||||
|
9
window.c
9
window.c
@ -2554,7 +2554,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
|
|||||||
|
|
||||||
if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
|
if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
|
||||||
*p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
|
*p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
|
||||||
return p - output;
|
*p++ = 0;
|
||||||
|
return -2;
|
||||||
}
|
}
|
||||||
if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
|
if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
|
||||||
*p++ = 0x1B;
|
*p++ = 0x1B;
|
||||||
@ -2572,7 +2573,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
|
|||||||
}
|
}
|
||||||
if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
|
if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
|
||||||
*p++ = 3;
|
*p++ = 3;
|
||||||
return p - output;
|
*p++ = 0;
|
||||||
|
return -2;
|
||||||
}
|
}
|
||||||
if (wParam == VK_PAUSE) { /* Break/Pause */
|
if (wParam == VK_PAUSE) { /* Break/Pause */
|
||||||
*p++ = 26;
|
*p++ = 26;
|
||||||
@ -2786,7 +2788,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
|
|||||||
*/
|
*/
|
||||||
if (wParam == VK_RETURN) { /* Return */
|
if (wParam == VK_RETURN) { /* Return */
|
||||||
*p++ = 0x0D;
|
*p++ = 0x0D;
|
||||||
return p - output;
|
*p++ = 0;
|
||||||
|
return -2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user