1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-05-09 21:52:10 -05:00

Yet more trunk merges. Jacob's SSH signals stuff, some docs changes,

and stability stuff in ssh.c.

[originally from svn r4674]
This commit is contained in:
Simon Tatham 2004-10-24 13:31:55 +00:00
parent 5db33359a2
commit f12be80774
8 changed files with 442 additions and 122 deletions

View File

@ -1,4 +1,4 @@
\versionid $Id: config.but,v 1.92.2.2 2004/10/24 13:29:02 simon Exp $
\versionid $Id: config.but,v 1.92.2.3 2004/10/24 13:31:55 simon Exp $
\C{config} Configuring PuTTY
@ -1199,11 +1199,22 @@ native keyboard layout is not US or UK.
\cfg{winhelp-topic}{translation.linedraw}
VT100-series terminals allow the server to send control sequences
that shift temporarily into a separate character set for drawing
lines and boxes. PuTTY has a variety of ways to support this
capability. In general you should probably try lots of options until
you find one that your particular font supports.
VT100-series terminals allow the server to send control sequences that
shift temporarily into a separate character set for drawing simple
lines and boxes. However, there are a variety of ways in which PuTTY
can attempt to find appropriate characters, and the right one to use
depends on the locally configured font. In general you should probably
try lots of options until you find one that your particular font
supports.
\b \q{Use Unicode line drawing code points} tries to use the box
characters that are present in Unicode. For good Unicode-supporting
fonts this is probably the most reliable and functional option.
\b \q{Poor man's line drawing} assumes that the font \e{cannot}
generate the line and box characters at all, so it will use the
\c{+}, \c{-} and \c{|} characters to draw approximations to boxes.
You should use this option if none of the other options works.
\b \q{Font has XWindows encoding} is for use with fonts that have a
special encoding, where the lowest 32 character positions (below the
@ -1220,15 +1231,6 @@ different size depending on which character set you try to use.
\b \q{Use font in OEM mode only} is more reliable than that, but can
miss out other characters from the main character set.
\b \q{Poor man's line drawing} assumes that the font \e{cannot}
generate the line and box characters at all, so it will use the
\c{+}, \c{-} and \c{|} characters to draw approximations to boxes.
You should use this option if none of the other options works.
\b \q{Unicode mode} tries to use the box characters that are present
in Unicode. For good Unicode-supporting fonts this is probably the
most reliable and functional option.
\S{config-linedrawpaste} Controlling copy and paste of line drawing
characters
@ -1248,7 +1250,8 @@ layout in another program, for example.
Note that this option only applies to line-drawing characters which
\e{were} printed by using the VT100 mechanism. Line-drawing
characters displayed using Unicode will paste as Unicode always.
characters that were received as Unicode code points will paste as
Unicode always.
\H{config-selection} The Selection panel

View File

@ -1,4 +1,4 @@
\versionid $Id: using.but,v 1.33.2.1 2004/10/24 13:21:11 simon Exp $
\versionid $Id: using.but,v 1.33.2.2 2004/10/24 13:31:55 simon Exp $
\C{using} Using PuTTY
@ -175,19 +175,27 @@ PuTTY can also be configured to send this when Ctrl-Z is typed; see
In an SSH connection, the following special commands are available:
\b \I{Break, SSH special command}Break
\lcont{
Optional extension; may not be supported by server. PuTTY requests the
server's default break length.
}
\b \I{IGNORE message, SSH special command}\I{No-op, in SSH}IGNORE message
\lcont{
Should have no effect.
}
\b \I{Break, SSH special command}Break
\lcont{
Only available in SSH-2, and only during a session. Optional
extension; may not be supported by server. PuTTY requests the server's
default break length.
}
\b \I{Signal, SSH special command}Signals (SIGINT, SIGTERM etc)
\lcont{
Only available in SSH-2, and only during a session. Sends various
POSIX signals. Not honoured by all servers.
}
\S2{using-newsession} Starting new sessions
PuTTY's system menu provides some shortcut ways to start new
@ -278,15 +286,17 @@ See \k{config-logging} for more details and options.
\H{using-translation} Altering your \i{character set} configuration
If you find that special characters (\i{accented characters}, for
example) are not being displayed correctly in your PuTTY session, it
may be that PuTTY is interpreting the characters sent by the server
according to the wrong \e{character set}. There are a lot of
different character sets available, so it's entirely possible for
this to happen.
example, or \i{line-drawing characters}) are not being displayed
correctly in your PuTTY session, it may be that PuTTY is interpreting
the characters sent by the server according to the wrong \e{character
set}. There are a lot of different character sets available, so it's
entirely possible for this to happen.
If you click \q{Change Settings} and look at the \i{\q{Translation}
panel}, you should see a large number of character sets which you
can select. Now all you need is to find out which of them you want!
panel}, you should see a large number of character sets which you can
select, and other related options. Now all you need is to find out
which of them you want! (See \k{config-translation} for more
information.)
\H{using-x-forwarding} Using \i{X11 forwarding} in SSH

14
putty.h
View File

@ -129,13 +129,23 @@ struct unicode_data {
#define LGTYP_PACKETS 3 /* logmode: SSH data packets */
typedef enum {
/* Actual special commands. Originally Telnet, but some codes have
* been re-used for similar specials in other protocols. */
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_EOL
TS_EOL,
/* POSIX-style signals. (not Telnet) */
TS_SIGABRT, TS_SIGALRM, TS_SIGFPE, TS_SIGHUP, TS_SIGILL,
TS_SIGINT, TS_SIGKILL, TS_SIGPIPE, TS_SIGQUIT, TS_SIGSEGV,
TS_SIGTERM, TS_SIGUSR1, TS_SIGUSR2,
/* Pseudo-specials used for constructing the specials menu. */
TS_SEP, /* Separator */
TS_SUBMENU, /* Start a new submenu with specified name */
TS_EXITMENU /* Exit current submenu or end of specials */
} Telnet_Special;
struct telnet_special {
const char *name; /* NULL==end, ""==separator */
const char *name;
int code;
};

233
ssh.c
View File

@ -460,7 +460,7 @@ struct ssh_channel {
struct ssh_agent_channel {
unsigned char *message;
unsigned char msglen[4];
int lensofar, totallen;
unsigned lensofar, totallen;
} a;
struct ssh_x11_channel {
Socket s;
@ -524,6 +524,8 @@ static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
static void ssh2_set_window(struct ssh_channel *c, unsigned newwin);
static int ssh_sendbuffer(void *handle);
static void ssh_do_close(Ssh ssh);
static unsigned long ssh_pkt_getuint32(Ssh ssh);
static void ssh_pkt_getstring(Ssh ssh, char **p, int *length);
struct rdpkt1_state_tag {
long len, pad, biglen, to_read;
@ -972,15 +974,14 @@ static int ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
}
if (ssh->pktin.type == SSH1_MSG_DEBUG) {
/* log debug message */
char buf[512];
int stringlen = GET_32BIT(ssh->pktin.body);
strcpy(buf, "Remote debug message: ");
if (stringlen > 480)
stringlen = 480;
memcpy(buf + 8, ssh->pktin.body + 4, stringlen);
buf[8 + stringlen] = '\0';
char *buf, *msg;
int msglen;
ssh_pkt_getstring(ssh, &msg, &msglen);
buf = dupprintf("Remote debug message: %.*s", msglen, msg);
logevent(buf);
sfree(buf);
goto next_packet;
} else if (ssh->pktin.type == SSH1_MSG_IGNORE) {
/* do nothing */
@ -989,17 +990,12 @@ static int ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
if (ssh->pktin.type == SSH1_MSG_DISCONNECT) {
/* log reason code in disconnect message */
char buf[256];
unsigned msglen = GET_32BIT(ssh->pktin.body);
unsigned nowlen;
strcpy(buf, "Remote sent disconnect: ");
nowlen = strlen(buf);
if (msglen > sizeof(buf) - nowlen - 1)
msglen = sizeof(buf) - nowlen - 1;
memcpy(buf + nowlen, ssh->pktin.body + 4, msglen);
buf[nowlen + msglen] = '\0';
/* logevent(buf); (this is now done within the bombout macro) */
bombout(("Server sent disconnect message:\n\"%s\"", buf+nowlen));
char *msg;
int msglen;
ssh_pkt_getstring(ssh, &msg, &msglen);
bombout(("Server sent disconnect message:\n\"%.*s\"", msglen, msg));
crStop(0);
}
@ -1168,10 +1164,11 @@ static int ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
case SSH2_MSG_DISCONNECT:
{
/* log reason code in disconnect message */
char *buf;
int nowlen;
int reason = GET_32BIT(ssh->pktin.data + 6);
unsigned msglen = GET_32BIT(ssh->pktin.data + 10);
char *buf, *msg;
int nowlen, reason, msglen;
reason = ssh_pkt_getuint32(ssh);
ssh_pkt_getstring(ssh, &msg, &msglen);
if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) {
buf = dupprintf("Received disconnect message (%s)",
@ -1183,7 +1180,7 @@ static int ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
logevent(buf);
sfree(buf);
buf = dupprintf("Disconnection message text: %n%.*s",
&nowlen, msglen, ssh->pktin.data + 14);
&nowlen, msglen, msg);
logevent(buf);
bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"",
reason,
@ -1199,19 +1196,16 @@ static int ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
case SSH2_MSG_DEBUG:
{
/* log the debug message */
char buf[512];
/* int display = ssh->pktin.body[6]; */
int stringlen = GET_32BIT(ssh->pktin.data+7);
int prefix;
strcpy(buf, "Remote debug message: ");
prefix = strlen(buf);
if (stringlen > (int)(sizeof(buf)-prefix-1))
stringlen = sizeof(buf)-prefix-1;
memcpy(buf + prefix, ssh->pktin.data + 11, stringlen);
buf[prefix + stringlen] = '\0';
char *buf, *msg;
int msglen;
ssh_pkt_getstring(ssh, &msg, &msglen);
buf = dupprintf("Remote debug message: %.*s", msglen, msg);
logevent(buf);
sfree(buf);
}
goto next_packet; /* FIXME: print the debug message */
goto next_packet;
/*
* These packets we need do nothing about here.
@ -5254,7 +5248,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
ssh2_pkt_addstring_data(ssh, (char *)pub_blob,
pub_blob_len);
ssh2_pkt_send(ssh);
logevent("Offered public key"); /* FIXME */
logevent("Offered public key");
crWaitUntilV(ispkt);
if (ssh->pktin.type != SSH2_MSG_USERAUTH_PK_OK) {
@ -6354,6 +6348,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
unsigned localid;
char *type;
int typelen, want_reply;
int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */
struct ssh_channel *c;
localid = ssh_pkt_getuint32(ssh);
@ -6385,18 +6380,94 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
* the request type string to see if it's something
* we recognise.
*/
if (typelen == 11 && !memcmp(type, "exit-status", 11) &&
c == ssh->mainchan) {
/* We recognise "exit-status" on the primary channel. */
char buf[100];
ssh->exitcode = ssh_pkt_getuint32(ssh);
sprintf(buf, "Server sent command exit status %d",
ssh->exitcode);
logevent(buf);
if (want_reply) {
ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_SUCCESS);
ssh2_pkt_adduint32(ssh, c->remoteid);
ssh2_pkt_send(ssh);
if (c == ssh->mainchan) {
/*
* We recognise "exit-status" and "exit-signal" on
* the primary channel.
*/
if (typelen == 11 &&
!memcmp(type, "exit-status", 11)) {
ssh->exitcode = ssh_pkt_getuint32(ssh);
logeventf(ssh, "Server sent command exit status %d",
ssh->exitcode);
reply = SSH2_MSG_CHANNEL_SUCCESS;
} else if (typelen == 11 &&
!memcmp(type, "exit-signal", 11)) {
int is_plausible = TRUE, is_int = FALSE;
char *fmt_sig = "", *fmt_msg = "";
char *msg;
int msglen = 0, core = FALSE;
/* ICK: older versions of OpenSSH (e.g. 3.4p1)
* provide an `int' for the signal, despite its
* having been a `string' in the drafts since at
* least 2001. (Fixed in session.c 1.147.) Try to
* infer which we can safely parse it as. */
{
unsigned char *p = ssh->pktin.body +
ssh->pktin.savedpos;
long len = ssh->pktin.length - ssh->pktin.savedpos;
unsigned long num = GET_32BIT(p); /* what is it? */
/* If it's 0, it hardly matters; assume string */
if (num == 0) {
is_int = FALSE;
} else {
int maybe_int = FALSE, maybe_str = FALSE;
#define CHECK_HYPOTHESIS(offset, result) \
do { \
long q = offset; \
if (q >= 0 && q+4 <= len) { \
q = q + 4 + GET_32BIT(p+q); \
if (q >= 0 && q+4 <= len && \
(q = q + 4 + GET_32BIT(p+q)) && q == len) \
result = TRUE; \
} \
} while(0)
CHECK_HYPOTHESIS(4+1, maybe_int);
CHECK_HYPOTHESIS(4+num+1, maybe_str);
#undef CHECK_HYPOTHESIS
if (maybe_int && !maybe_str)
is_int = TRUE;
else if (!maybe_int && maybe_str)
is_int = FALSE;
else
/* Crikey. Either or neither. Panic. */
is_plausible = FALSE;
}
}
if (is_plausible) {
if (is_int) {
/* Old non-standard OpenSSH. */
int signum = ssh_pkt_getuint32(ssh);
fmt_sig = dupprintf(" %d", signum);
} else {
/* As per the drafts. */
char *sig;
int siglen;
ssh_pkt_getstring(ssh, &sig, &siglen);
/* Signal name isn't supposed to be blank, but
* let's cope gracefully if it is. */
if (siglen) {
fmt_sig = dupprintf(" \"%.*s\"",
siglen, sig);
}
}
core = ssh2_pkt_getbool(ssh);
ssh_pkt_getstring(ssh, &msg, &msglen);
if (msglen) {
fmt_msg = dupprintf(" (\"%.*s\")", msglen, msg);
}
/* ignore lang tag */
} /* else don't attempt to parse */
logeventf(ssh, "Server exited on signal%s%s%s",
fmt_sig, core ? " (core dumped)" : "",
fmt_msg);
if (*fmt_sig) sfree(fmt_sig);
if (*fmt_msg) sfree(fmt_msg);
reply = SSH2_MSG_CHANNEL_SUCCESS;
}
} else {
/*
@ -6405,11 +6476,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
* or respond with CHANNEL_FAILURE, depending
* on want_reply.
*/
if (want_reply) {
ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_FAILURE);
ssh2_pkt_adduint32(ssh, c->remoteid);
ssh2_pkt_send(ssh);
}
reply = SSH2_MSG_CHANNEL_FAILURE;
}
if (want_reply) {
ssh2_pkt_init(ssh, reply);
ssh2_pkt_adduint32(ssh, c->remoteid);
ssh2_pkt_send(ssh);
}
} else if (ssh->pktin.type == SSH2_MSG_GLOBAL_REQUEST) {
char *type;
@ -6885,12 +6957,25 @@ static const struct telnet_special *ssh_get_specials(void *handle)
{"IGNORE message", TS_NOP},
};
static const struct telnet_special ssh2_session_specials[] = {
{"", 0},
{"Break", TS_BRK}
/* XXX we should also support signals */
{NULL, TS_SEP},
{"Break", TS_BRK},
/* These are the signal names defined by draft-ietf-secsh-connect-19.
* They include all the ISO C signals, but are a subset of the POSIX
* required signals. */
{"SIGINT (Interrupt)", TS_SIGINT},
{"SIGTERM (Terminate)", TS_SIGTERM},
{"SIGKILL (Kill)", TS_SIGKILL},
{"SIGQUIT (Quit)", TS_SIGQUIT},
{"SIGHUP (Hangup)", TS_SIGHUP},
{"More signals", TS_SUBMENU},
{"SIGABRT", TS_SIGABRT}, {"SIGALRM", TS_SIGALRM},
{"SIGFPE", TS_SIGFPE}, {"SIGILL", TS_SIGILL},
{"SIGPIPE", TS_SIGPIPE}, {"SIGSEGV", TS_SIGSEGV},
{"SIGUSR1", TS_SIGUSR1}, {"SIGUSR2", TS_SIGUSR2},
{NULL, TS_EXITMENU}
};
static const struct telnet_special specials_end[] = {
{NULL, 0}
{NULL, TS_EXITMENU}
};
static struct telnet_special ssh_specials[lenof(ignore_special) +
lenof(ssh2_session_specials) +
@ -6978,7 +7063,37 @@ static void ssh_special(void *handle, Telnet_Special code)
ssh2_pkt_send(ssh);
}
} else {
/* do nothing */
/* Is is a POSIX signal? */
char *signame = NULL;
if (code == TS_SIGABRT) signame = "ABRT";
if (code == TS_SIGALRM) signame = "ALRM";
if (code == TS_SIGFPE) signame = "FPE";
if (code == TS_SIGHUP) signame = "HUP";
if (code == TS_SIGILL) signame = "ILL";
if (code == TS_SIGINT) signame = "INT";
if (code == TS_SIGKILL) signame = "KILL";
if (code == TS_SIGPIPE) signame = "PIPE";
if (code == TS_SIGQUIT) signame = "QUIT";
if (code == TS_SIGSEGV) signame = "SEGV";
if (code == TS_SIGTERM) signame = "TERM";
if (code == TS_SIGUSR1) signame = "USR1";
if (code == TS_SIGUSR2) signame = "USR2";
/* The SSH-2 protocol does in principle support arbitrary named
* signals, including signame@domain, but we don't support those. */
if (signame) {
/* It's a signal. */
if (ssh->version == 2 && ssh->mainchan) {
ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
ssh2_pkt_addstring(ssh, "signal");
ssh2_pkt_addbool(ssh, 0);
ssh2_pkt_addstring(ssh, signame);
ssh2_pkt_send(ssh);
logeventf(ssh, "Sent signal SIG%s", signame);
}
} else {
/* Never heard of it. Do nothing */
}
}
}

136
sshzlib.c
View File

@ -40,7 +40,25 @@
#include <stdlib.h>
#include <assert.h>
#ifdef ZLIB_STANDALONE
/*
* This module also makes a handy zlib decoding tool for when
* you're picking apart Zip files or PDFs or PNGs. If you compile
* it with ZLIB_STANDALONE defined, it builds on its own and
* becomes a command-line utility.
*
* Therefore, here I provide a self-contained implementation of the
* macros required from the rest of the PuTTY sources.
*/
#define snew(type) ( (type *) malloc(sizeof(type)) )
#define snewn(n, type) ( (type *) malloc((n) * sizeof(type)) )
#define sresize(x, n, type) ( (type *) realloc((x), (n) * sizeof(type)) )
#define sfree(x) ( free((x)) )
#else
#include "ssh.h"
#endif
#ifndef FALSE
#define FALSE 0
@ -1027,13 +1045,14 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len,
{
struct zlib_decompress_ctx *dctx = (struct zlib_decompress_ctx *)handle;
const coderecord *rec;
int code, blktype, rep, dist, nlen;
int code, blktype, rep, dist, nlen, header;
static const unsigned char lenlenmap[] = {
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
dctx->outblk = NULL;
dctx->outsize = dctx->outlen = 0;
dctx->outblk = snewn(256, unsigned char);
dctx->outsize = 256;
dctx->outlen = 0;
while (len > 0 || dctx->nbits > 0) {
while (dctx->nbits < 24 && len > 0) {
@ -1043,10 +1062,35 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len,
}
switch (dctx->state) {
case START:
/* Expect 16-bit zlib header, which we'll dishonourably ignore. */
/* Expect 16-bit zlib header. */
if (dctx->nbits < 16)
goto finished; /* done all we can */
EATBITS(16);
/*
* The header is stored as a big-endian 16-bit integer,
* in contrast to the general little-endian policy in
* the rest of the format :-(
*/
header = (((dctx->bits & 0xFF00) >> 8) |
((dctx->bits & 0x00FF) << 8));
EATBITS(16);
/*
* Check the header:
*
* - bits 8-11 should be 1000 (Deflate/RFC1951)
* - bits 12-15 should be at most 0111 (window size)
* - bit 5 should be zero (no dictionary present)
* - we don't care about bits 6-7 (compression rate)
* - bits 0-4 should be set up to make the whole thing
* a multiple of 31 (checksum).
*/
if ((header & 0x0F00) != 0x0800 ||
(header & 0xF000) > 0x7000 ||
(header & 0x0020) != 0x0000 ||
(header % 31) != 0)
goto decode_error;
dctx->state = OUTSIDEBLK;
break;
case OUTSIDEBLK:
@ -1242,6 +1286,86 @@ int zlib_decompress_block(void *handle, unsigned char *block, int len,
return 0;
}
#ifdef ZLIB_STANDALONE
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
unsigned char buf[16], *outbuf;
int ret, outlen;
void *handle;
int noheader = FALSE, opts = TRUE;
char *filename = NULL;
FILE *fp;
while (--argc) {
char *p = *++argv;
if (p[0] == '-' && opts) {
if (!strcmp(p, "-d"))
noheader = TRUE;
else if (!strcmp(p, "--"))
opts = FALSE; /* next thing is filename */
else {
fprintf(stderr, "unknown command line option '%s'\n", p);
return 1;
}
} else if (!filename) {
filename = p;
} else {
fprintf(stderr, "can only handle one filename\n");
return 1;
}
}
handle = zlib_decompress_init();
if (noheader) {
/*
* Provide missing zlib header if -d was specified.
*/
zlib_decompress_block(handle, "\x78\x9C", 2, &outbuf, &outlen);
assert(outlen == 0);
}
if (filename)
fp = fopen(filename, "rb");
else
fp = stdin;
if (!fp) {
assert(filename);
fprintf(stderr, "unable to open '%s'\n", filename);
return 1;
}
while (1) {
ret = fread(buf, 1, sizeof(buf), fp);
if (ret <= 0)
break;
zlib_decompress_block(handle, buf, ret, &outbuf, &outlen);
if (outbuf) {
if (outlen)
fwrite(outbuf, 1, outlen, stdout);
sfree(outbuf);
} else {
fprintf(stderr, "decoding error\n");
return 1;
}
}
zlib_decompress_cleanup(handle);
if (filename)
fclose(fp);
return 0;
}
#else
const struct ssh_compress ssh_zlib = {
"zlib",
zlib_compress_init,
@ -1253,3 +1377,5 @@ const struct ssh_compress ssh_zlib = {
zlib_disable_compression,
"zlib (RFC1950)"
};
#endif

View File

@ -963,6 +963,8 @@ static void telnet_special(void *handle, Telnet_Special code)
telnet->bufsize = sk_write(telnet->s, (char *)b, 2);
}
break;
default:
break; /* never heard of it */
}
}
@ -976,15 +978,15 @@ static const struct telnet_special *telnet_get_specials(void *handle)
{"Erase Line", TS_EL},
{"Go Ahead", TS_GA},
{"No Operation", TS_NOP},
{"", 0},
{NULL, TS_SEP},
{"Abort Process", TS_ABORT},
{"Abort Output", TS_AO},
{"Interrupt Process", TS_IP},
{"Suspend Process", TS_SUSP},
{"", 0},
{NULL, TS_SEP},
{"End Of Record", TS_EOR},
{"End Of File", TS_EOF},
{NULL, 0}
{NULL, TS_EXITMENU}
};
return specials;
}

View File

@ -3144,22 +3144,51 @@ void update_specials_menu(void *frontend)
else
specials = NULL;
/* I believe this disposes of submenus too. */
gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu),
(GtkCallback)gtk_widget_destroy, NULL);
if (specials) {
int i;
GtkWidget *menuitem;
for (i = 0; specials[i].name; i++) {
if (*specials[i].name) {
GtkWidget *menu = inst->specialsmenu;
/* A lame "stack" for submenus that will do for now. */
GtkWidget *saved_menu = NULL;
int nesting = 1;
for (i = 0; nesting > 0; i++) {
GtkWidget *menuitem = NULL;
switch (specials[i].code) {
case TS_SUBMENU:
assert (nesting < 2);
saved_menu = menu; /* XXX lame stacking */
menu = gtk_menu_new();
menuitem = gtk_menu_item_new_with_label(specials[i].name);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
gtk_container_add(GTK_CONTAINER(saved_menu), menuitem);
gtk_widget_show(menuitem);
menuitem = NULL;
nesting++;
break;
case TS_EXITMENU:
nesting--;
if (nesting) {
menu = saved_menu; /* XXX lame stacking */
saved_menu = NULL;
}
break;
case TS_SEP:
menuitem = gtk_menu_item_new();
break;
default:
menuitem = gtk_menu_item_new_with_label(specials[i].name);
gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
GINT_TO_POINTER(specials[i].code));
gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
GTK_SIGNAL_FUNC(special_menuitem), inst);
} else
menuitem = gtk_menu_item_new();
gtk_container_add(GTK_CONTAINER(inst->specialsmenu), menuitem);
gtk_widget_show(menuitem);
break;
}
if (menuitem) {
gtk_container_add(GTK_CONTAINER(menu), menuitem);
gtk_widget_show(menuitem);
}
}
gtk_widget_show(inst->specialsitem1);
gtk_widget_show(inst->specialsitem2);

View File

@ -111,6 +111,7 @@ static struct unicode_data ucsdata;
static int session_closed;
static const struct telnet_special *specials;
static int n_specials;
static struct {
HMENU menu;
@ -915,20 +916,48 @@ void update_specials_menu(void *frontend)
specials = NULL;
if (specials) {
p = CreateMenu();
for (i = 0; specials[i].name; i++) {
/* We can't use Windows to provide a stack for submenus, so
* here's a lame "stack" that will do for now. */
HMENU saved_menu = NULL;
int nesting = 1;
p = CreatePopupMenu();
for (i = 0; nesting > 0; i++) {
assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX);
if (*specials[i].name)
switch (specials[i].code) {
case TS_SEP:
AppendMenu(p, MF_SEPARATOR, 0, 0);
break;
case TS_SUBMENU:
assert(nesting < 2);
nesting++;
saved_menu = p; /* XXX lame stacking */
p = CreatePopupMenu();
AppendMenu(saved_menu, MF_POPUP | MF_ENABLED,
(UINT) p, specials[i].name);
break;
case TS_EXITMENU:
nesting--;
if (nesting) {
p = saved_menu; /* XXX lame stacking */
saved_menu = NULL;
}
break;
default:
AppendMenu(p, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i,
specials[i].name);
else
AppendMenu(p, MF_SEPARATOR, 0, 0);
break;
}
}
} else
/* Squirrel the highest special. */
n_specials = i - 1;
} else {
p = NULL;
n_specials = 0;
}
for (j = 0; j < lenof(popup_menus); j++) {
if (menu_already_exists) {
/* XXX does this free up all submenus? */
DeleteMenu(popup_menus[j].menu,
popup_menus[j].specials_submenu_pos,
MF_BYPOSITION);
@ -2087,20 +2116,16 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
}
if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) {
int i = (wParam - IDM_SPECIAL_MIN) / 0x10;
int j;
/*
* Ensure we haven't been sent a bogus SYSCOMMAND
* which would cause us to reference invalid memory
* and crash. Perhaps I'm just too paranoid here.
*/
for (j = 0; j < i; j++)
if (!specials || !specials[j].name)
break;
if (j == i) {
if (back)
back->special(backhandle, specials[i].code);
net_pending_errors();
}
if (i >= n_specials)
break;
if (back)
back->special(backhandle, specials[i].code);
net_pending_errors();
}
}
break;