}
#define CTRL(x) (x^'@')
+#define KCTRL(x) ((x^'@') | 0x100)
void ldisc_send(char *buf, int len)
{
+ int keyflag = 0;
/*
* Called with len=0 when the options change. We must inform
* the front end in case it needs to know.
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.
*/
if (EDITING) {
while (len--) {
- char c;
- c = *buf++;
+ int c;
+ c = *buf++ + keyflag;
switch (term_quotenext ? ' ' : c) {
/*
* ^h/^?: delete one char and output one BSB
* else send line and reset to BOL
* ^m: send line-plus-\r\n and reset to BOL
*/
- case CTRL('H'):
- case CTRL('?'): /* backspace/delete */
+ case KCTRL('H'):
+ case KCTRL('?'): /* backspace/delete */
if (term_buflen > 0) {
if (ECHOING)
bsb(plen(term_buf[term_buflen - 1]));
term_buflen = 0;
}
break;
- case CTRL('M'): /* send with newline */
- if (term_buflen > 0)
- back->send(term_buf, term_buflen);
- 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;
+ /*
+ * 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)
+ back->send(term_buf, term_buflen);
+ if (cfg.protocol == PROT_RAW)
+ back->send("\r\n", 2);
+ else if (cfg.protocol == PROT_TELNET)
+ back->special(TS_EOL);
+ else
+ back->send("\r", 1);
+ if (ECHOING)
+ c_write("\r\n", 2);
+ term_buflen = 0;
+ break;
+ }
+ /* FALLTHROUGH */
default: /* get to this label from ^V handler */
if (term_buflen >= term_bufsiz) {
term_bufsiz = term_buflen + 256;
}
term_buf[term_buflen++] = c;
if (ECHOING)
- pwrite(c);
+ pwrite((unsigned char) c);
term_quotenext = FALSE;
break;
}
if (len > 0) {
if (ECHOING)
c_write(buf, len);
- back->send(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);
}
}
}
typedef enum {
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;
typedef enum {
int app_cursor;
int app_keypad;
int nethack_keypad;
+ int telnet_keyboard;
int alt_f4; /* is it special? */
int alt_space; /* is it special? */
int alt_only; /* is it special? */
write_setting_i(sesskey, "AltOnly", cfg->alt_only);
write_setting_i(sesskey, "ComposeKey", cfg->compose_key);
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, "LocalEdit", cfg->localedit);
write_setting_s(sesskey, "Answerback", cfg->answerback);
gppi(sesskey, "AltOnly", 0, &cfg->alt_only);
gppi(sesskey, "ComposeKey", 0, &cfg->compose_key);
gppi(sesskey, "CtrlAltKeys", 1, &cfg->ctrlaltkeys);
+ gppi(sesskey, "TelnetKey", 0, &cfg->telnet_keyboard);
gppi(sesskey, "LocalEcho", LD_BACKEND, &cfg->localecho);
gppi(sesskey, "LocalEdit", LD_BACKEND, &cfg->localedit);
gpps(sesskey, "Answerback", "PuTTY", cfg->answerback,
char *p;
static unsigned char iac[2] = { IAC, IAC };
static unsigned char cr[2] = { CR, NUL };
+#if 0
static unsigned char nl[2] = { CR, LF };
+#endif
if (s == NULL)
return;
sk_write(s, q, p - q);
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++;
}
}
b[1] = xEOF;
sk_write(s, b, 2);
break;
+ case TS_EOL:
+ sk_write(s, "\r\n", 2);
+ break;
case TS_SYNCH:
b[1] = DM;
sk_write(s, b, 1);
IDC_CURAPPLIC,
IDC_COMPOSEKEY,
IDC_CTRLALTKEYS,
+ IDC_TELNETKEY,
keyboardpanelend,
terminalpanelstart,
CheckDlgButton(hwnd, IDC_ALTONLY, cfg.alt_only);
CheckDlgButton(hwnd, IDC_COMPOSEKEY, cfg.compose_key);
CheckDlgButton(hwnd, IDC_CTRLALTKEYS, cfg.ctrlaltkeys);
+ CheckDlgButton(hwnd, IDC_TELNETKEY, cfg.telnet_keyboard);
CheckRadioButton(hwnd, IDC_ECHOBACKEND, IDC_ECHONO,
cfg.localecho == LD_BACKEND ? IDC_ECHOBACKEND :
cfg.localecho == LD_YES ? IDC_ECHOYES : IDC_ECHONO);
staticedit(&cp, "Auto-login &username", IDC_LOGSTATIC,
IDC_LOGEDIT, 50);
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",
IDC_BOX_CONNECTION2);
}
if (panel == telnetpanelstart) {
- /* The Telnet panel. Accelerators used: [acgo] svldr bft */
+ /* The Telnet panel. Accelerators used: [acgo] svldr bftk */
struct ctlpos cp;
ctlposinit(&cp, hwnd, 80, 3, 13);
if (dlgtype == 0) {
radioline(&cp, "&Telnet negotiation mode:", IDC_ACTSTATIC, 2,
"Passive", IDC_TPASSIVE, "Active",
IDC_TACTIVE, NULL);
+ checkbox(&cp, "&Keyboard sends telnet Backspace and Interrupt",
+ IDC_TELNETKEY);
endbox(&cp);
}
}
cfg.ctrlaltkeys =
IsDlgButtonChecked(hwnd, IDC_CTRLALTKEYS);
break;
+ case IDC_TELNETKEY:
+ if (HIWORD(wParam) == BN_CLICKED ||
+ HIWORD(wParam) == BN_DOUBLECLICKED)
+ cfg.telnet_keyboard =
+ IsDlgButtonChecked(hwnd, IDC_TELNETKEY);
+ break;
case IDC_WRAPMODE:
if (HIWORD(wParam) == BN_CLICKED ||
HIWORD(wParam) == BN_DOUBLECLICKED)
if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
*p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
- return p - output;
+ *p++ = 0;
+ return -2;
}
if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
*p++ = 0x1B;
}
if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
*p++ = 3;
- return p - output;
+ *p++ = 0;
+ return -2;
}
if (wParam == VK_PAUSE) { /* Break/Pause */
*p++ = 26;
*/
if (wParam == VK_RETURN) { /* Return */
*p++ = 0x0D;
- return p - output;
+ *p++ = 0;
+ return -2;
}
}