# GUI front end and terminal emulator (putty, puttytel).
GUITERM = TERMINAL window windlg winctrls sizetip winucs winprint
- + winutils wincfg
+ + winutils wincfg sercfg
# Same thing on Unix.
UXTERM = TERMINAL uxcfg uxucs uxprint timing
BE_NOSSH = be_nossh nocproxy
BE_SSH = be_none cproxy
BE_NONE = be_none nocproxy
+# More backend sets, with the additional Windows serial-port module.
+W_BE_ALL = be_all_s winser cproxy
+W_BE_NOSSH = be_nos_s winser nocproxy
# ------------------------------------------------------------
# Definitions of actual programs. The program name, followed by a
# keywords [G] for Windows GUI app, [C] for Console app, [X] for
# X/GTK Unix app, [U] for command-line Unix app, [M] for Macintosh app.
-putty : [G] GUITERM NONSSH WINSSH BE_ALL WINMISC putty.res LIBS
-puttytel : [G] GUITERM NONSSH BE_NOSSH WINMISC puttytel.res LIBS
-plink : [C] winplink wincons NONSSH WINSSH BE_ALL logging WINMISC
+putty : [G] GUITERM NONSSH WINSSH W_BE_ALL WINMISC putty.res LIBS
+puttytel : [G] GUITERM NONSSH W_BE_NOSSH WINMISC puttytel.res LIBS
+plink : [C] winplink wincons NONSSH WINSSH W_BE_ALL logging WINMISC
+ plink.res LIBS
pscp : [C] pscp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC
+ pscp.res LIBS
--- /dev/null
+/*
+ * Linking module for PuTTY proper: list the available backends
+ * including ssh, plus the serial backend.
+ */
+
+#include <stdio.h>
+#include "putty.h"
+
+/*
+ * This appname is not strictly in the right place, since Plink
+ * also uses this module. However, Plink doesn't currently use any
+ * of the dialog-box sorts of things that make use of appname, so
+ * it shouldn't do any harm here. I'm trying to avoid having to
+ * have tiny little source modules containing nothing but
+ * declarations of appname, for as long as I can...
+ */
+const char *const appname = "PuTTY";
+
+#ifdef TELNET_DEFAULT
+const int be_default_protocol = PROT_TELNET;
+#else
+const int be_default_protocol = PROT_SSH;
+#endif
+
+struct backend_list backends[] = {
+ {PROT_SSH, "ssh", &ssh_backend},
+ {PROT_TELNET, "telnet", &telnet_backend},
+ {PROT_RLOGIN, "rlogin", &rlogin_backend},
+ {PROT_RAW, "raw", &raw_backend},
+ {PROT_SERIAL, "serial", &serial_backend},
+ {0, NULL}
+};
--- /dev/null
+/*
+ * Linking module for PuTTYtel: list the available backends not
+ * including ssh.
+ */
+
+#include <stdio.h>
+#include "putty.h"
+
+const int be_default_protocol = PROT_TELNET;
+
+const char *const appname = "PuTTYtel";
+
+struct backend_list backends[] = {
+ {PROT_TELNET, "telnet", &telnet_backend},
+ {PROT_RLOGIN, "rlogin", &rlogin_backend},
+ {PROT_RAW, "raw", &raw_backend},
+ {PROT_SERIAL, "serial", &serial_backend},
+ {0, NULL}
+};
+
+/*
+ * Stub implementations of functions not used in non-ssh versions.
+ */
+void random_save_seed(void)
+{
+}
+
+void random_destroy_seed(void)
+{
+}
+
+void noise_ultralight(unsigned long data)
+{
+}
#define PRINTER_DISABLED_STRING "None (printing disabled)"
-static void protocolbuttons_handler(union control *ctrl, void *dlg,
+#define HOST_BOX_TITLE "Host Name (or IP address)"
+#define PORT_BOX_TITLE "Port"
+
+static void config_host_handler(union control *ctrl, void *dlg,
+ void *data, int event)
+{
+ Config *cfg = (Config *)data;
+
+ /*
+ * This function works just like the standard edit box handler,
+ * only it has to choose the control's label and text from two
+ * different places depending on the protocol.
+ */
+ if (event == EVENT_REFRESH) {
+ if (cfg->protocol == PROT_SERIAL) {
+ /*
+ * This label text is carefully chosen to contain an n,
+ * since that's the shortcut for the host name control.
+ */
+ dlg_label_change(ctrl, dlg, "Serial line");
+ dlg_editbox_set(ctrl, dlg, cfg->serline);
+ } else {
+ dlg_label_change(ctrl, dlg, HOST_BOX_TITLE);
+ dlg_editbox_set(ctrl, dlg, cfg->host);
+ }
+ } else if (event == EVENT_VALCHANGE) {
+ if (cfg->protocol == PROT_SERIAL)
+ dlg_editbox_get(ctrl, dlg, cfg->serline, lenof(cfg->serline));
+ else
+ dlg_editbox_get(ctrl, dlg, cfg->host, lenof(cfg->host));
+ }
+}
+
+static void config_port_handler(union control *ctrl, void *dlg,
+ void *data, int event)
+{
+ Config *cfg = (Config *)data;
+ char buf[80];
+
+ /*
+ * This function works just like the standard edit box handler,
+ * only it has to choose the control's label and text from two
+ * different places depending on the protocol.
+ */
+ if (event == EVENT_REFRESH) {
+ if (cfg->protocol == PROT_SERIAL) {
+ /*
+ * This label text is carefully chosen to contain a p,
+ * since that's the shortcut for the port control.
+ */
+ dlg_label_change(ctrl, dlg, "Speed");
+ sprintf(buf, "%d", cfg->serspeed);
+ } else {
+ dlg_label_change(ctrl, dlg, PORT_BOX_TITLE);
+ sprintf(buf, "%d", cfg->port);
+ }
+ dlg_editbox_set(ctrl, dlg, buf);
+ } else if (event == EVENT_VALCHANGE) {
+ dlg_editbox_get(ctrl, dlg, buf, lenof(buf));
+ if (cfg->protocol == PROT_SERIAL)
+ cfg->serspeed = atoi(buf);
+ else
+ cfg->port = atoi(buf);
+ }
+}
+
+struct hostport {
+ union control *host, *port;
+};
+
+/*
+ * We export this function so that platform-specific config
+ * routines can use it to conveniently identify the protocol radio
+ * buttons in order to add to them.
+ */
+void config_protocolbuttons_handler(union control *ctrl, void *dlg,
void *data, int event)
{
int button, defport;
Config *cfg = (Config *)data;
+ struct hostport *hp = (struct hostport *)ctrl->radio.context.p;
+
/*
* This function works just like the standard radio-button
* handler, except that it also has to change the setting of
- * the port box. We expect the context parameter to point at
- * the `union control' structure for the port box.
+ * the port box, and refresh both host and port boxes when. We
+ * expect the context parameter to point at a hostport
+ * structure giving the `union control's for both.
*/
if (event == EVENT_REFRESH) {
for (button = 0; button < ctrl->radio.nbuttons; button++)
}
if (defport > 0 && cfg->port != defport) {
cfg->port = defport;
- dlg_refresh((union control *)ctrl->radio.context.p, dlg);
}
}
+ dlg_refresh(hp->host, dlg);
+ dlg_refresh(hp->port, dlg);
}
}
* contains a hostname.
*/
if (load_selected_session(ssd, savedsession, dlg, cfg) &&
- (ctrl == ssd->listbox && cfg->host[0])) {
+ (ctrl == ssd->listbox && cfg_launchable(cfg))) {
dlg_end(dlg, 1); /* it's all over, and succeeded */
}
} else if (ctrl == ssd->savebutton) {
* there was a session selected in that which had a
* valid host name in it, then load it and go.
*/
- if (dlg_last_focused(ctrl, dlg) == ssd->listbox && !*cfg->host) {
+ if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
+ !cfg_launchable(cfg)) {
Config cfg2;
if (!load_selected_session(ssd, savedsession, dlg, &cfg2)) {
dlg_beep(dlg);
* Otherwise, do the normal thing: if we have a valid
* session, get going.
*/
- if (*cfg->host) {
+ if (cfg_launchable(cfg)) {
dlg_end(dlg, 1);
} else
dlg_beep(dlg);
sfree(str);
if (!midsession) {
+ struct hostport *hp = (struct hostport *)
+ ctrl_alloc(b, sizeof(struct hostport));
+ int i, gotssh;
+
s = ctrl_getset(b, "Session", "hostport",
- "Specify your connection by host name or IP address");
+ "Specify the destination you want to connect to");
ctrl_columns(s, 2, 75, 25);
- c = ctrl_editbox(s, "Host Name (or IP address)", 'n', 100,
+ c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
HELPCTX(session_hostname),
- dlg_stdeditbox_handler, I(offsetof(Config,host)),
- I(sizeof(((Config *)0)->host)));
+ config_host_handler, I(0), I(0));
c->generic.column = 0;
- c = ctrl_editbox(s, "Port", 'p', 100, HELPCTX(session_hostname),
- dlg_stdeditbox_handler,
- I(offsetof(Config,port)), I(-1));
+ hp->host = c;
+ c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
+ HELPCTX(session_hostname),
+ config_port_handler, I(0), I(0));
c->generic.column = 1;
+ hp->port = c;
ctrl_columns(s, 1, 100);
- if (backends[3].name == NULL) {
- ctrl_radiobuttons(s, "Protocol:", NO_SHORTCUT, 3,
+
+ gotssh = FALSE;
+ for (i = 0; backends[i].name; i++)
+ if (backends[i].protocol == PROT_SSH) {
+ gotssh = TRUE;
+ break;
+ }
+ if (!gotssh) {
+ ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
HELPCTX(session_hostname),
- protocolbuttons_handler, P(c),
+ config_protocolbuttons_handler, P(hp),
"Raw", 'r', I(PROT_RAW),
"Telnet", 't', I(PROT_TELNET),
"Rlogin", 'i', I(PROT_RLOGIN),
NULL);
} else {
- ctrl_radiobuttons(s, "Protocol:", NO_SHORTCUT, 4,
+ ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,
HELPCTX(session_hostname),
- protocolbuttons_handler, P(c),
+ config_protocolbuttons_handler, P(hp),
"Raw", 'r', I(PROT_RAW),
"Telnet", 't', I(PROT_TELNET),
"Rlogin", 'i', I(PROT_RLOGIN),
*/
void dlg_set_focus(union control *ctrl, void *dlg);
/*
+ * Change the label text on a control.
+ */
+void dlg_label_change(union control *ctrl, void *dlg, char const *text);
+/*
* Return the `ctrl' structure for the most recent control that had
* the input focus apart from the one mentioned. This is NOT
* GUARANTEED to work on all platforms, so don't base any critical
\b The \q{Host Name} box is where you type the name, or the \i{IP
address}, of the server you want to connect to.
-\b The \q{Protocol} radio buttons let you choose what type of
+\b The \q{Connection type} radio buttons let you choose what type of
connection you want to make: a \I{raw TCP connections}raw
-connection, a \i{Telnet} connection, an \i{Rlogin} connection
-or an \i{SSH} connection. (See \k{which-one} for a
-summary of the differences between SSH, Telnet and rlogin, and
-\k{using-rawprot} for an explanation of \q{raw} connections.)
-
-\b The \q{Port} box lets you specify which \i{port number} on the server
-to connect to. If you select Telnet, Rlogin, or SSH, this box will
-be filled in automatically to the usual value, and you will only
-need to change it if you have an unusual server. If you select Raw
-mode, you will almost certainly need to fill in the \q{Port} box.
+connection, a \i{Telnet} connection, an \i{Rlogin} connection, an
+\i{SSH} connection, or a connection to a local \i{serial line}. (See
+\k{which-one} for a summary of the differences between SSH, Telnet
+and rlogin; see \k{using-rawprot} for an explanation of \q{raw}
+connections; see \k{using-serial} for information about using a
+serial line.)
+
+\b The \q{Port} box lets you specify which \i{port number} on the
+server to connect to. If you select Telnet, Rlogin, or SSH, this box
+will be filled in automatically to the usual value, and you will
+only need to change it if you have an unusual server. If you select
+Raw mode, you will almost certainly need to fill in the \q{Port} box
+yourself.
+
+If you select \q{Serial} from the \q{Connection type} radio buttons,
+the \q{Host Name} and \q{Port} boxes are replaced by \q{Serial line}
+and \q{Speed}; see \k{config-serial} for more details of these.
\S{config-saving} \ii{Loading and storing saved sessions}
This is an SSH-2-specific bug.
+\H{config-serial} The Serial panel
+
+The \i{Serial} panel allows you to configure options that only apply
+when PuTTY is connecting to a local \I{serial port}\i{serial line}.
+
+\S{config-serial-line} Selecting a serial line to connect to
+
+\cfg{winhelp-topic}{serial.line}
+
+The \q{Serial line to connect to} box allows you to choose which
+serial line you want PuTTY to talk to, if your computer has more
+than one serial port.
+
+On Windows, the first serial line is called \cw{COM1}, and if there
+is a second it is called \cw{COM2}, and so on.
+
+This configuration setting is also visible on the Session panel,
+where it replaces the \q{Host Name} box (see \k{config-hostname}) if
+the connection type is set to \q{Serial}.
+
+\S{config-serial-speed} Selecting the speed of your serial line
+
+\cfg{winhelp-topic}{serial.speed}
+
+The \q{Speed} box allows you to choose the speed (or \q{baud rate})
+at which to talk to the serial line. Typical values might be 9600,
+19200, 38400 or 57600. Which one you need will depend on the device
+at the other end of the serial cable; consult the manual for that
+device if you are in doubt.
+
+This configuration setting is also visible on the Session panel,
+where it replaces the \q{Port} box (see \k{config-hostname}) if the
+connection type is set to \q{Serial}.
+
+\S{config-serial-databits} Selecting the number of data bits
+
+\cfg{winhelp-topic}{serial.databits}
+
+The \q{Data bits} box allows you to choose how many data bits are
+transmitted in each byte sent or received through the serial line.
+Typical values are 7 or 8.
+
+\S{config-serial-stopbits} Selecting the number of stop bits
+
+\cfg{winhelp-topic}{serial.stopbits}
+
+The \q{Stop bits} box allows you to choose how many stop bits are
+used in the serial line protocol. Typical values are 1, 1.5 or 2.
+
+\S{config-serial-parity} Selecting the serial parity checking scheme
+
+\cfg{winhelp-topic}{serial.parity}
+
+The \q{Parity} box allows you to choose what type of parity checking
+is used on the serial line. The settings are:
+
+\b \q{None}: no parity bit is sent at all.
+
+\b \q{Odd}: an extra parity bit is sent alongside each byte, and
+arranged so that the total number of 1 bits is odd.
+
+\b \q{Even}: an extra parity bit is sent alongside each byte, and
+arranged so that the total number of 1 bits is even.
+
+\b \q{Mark}: an extra parity bit is sent alongside each byte, and
+always set to 1.
+
+\b \q{Space}: an extra parity bit is sent alongside each byte, and
+always set to 0.
+
+\S{config-serial-flow} Selecting the serial flow control scheme
+
+\cfg{winhelp-topic}{serial.flow}
+
+The \q{Flow control} box allows you to choose what type of flow
+control checking is used on the serial line. The settings are:
+
+\b \q{None}: no flow control is done. Data may be lost if either
+side attempts to send faster than the serial line permits.
+
+\b \q{XON/XOFF}: flow control is done by sending XON and XOFF
+characters within the data stream.
+
+\b \q{RTS/CTS}: flow control is done using the RTS and CTS wires on
+the serial line.
+
+\b \q{DSR/DTR}: flow control is done using the DSR and DTR wires on
+the serial line.
+
\H{config-file} \ii{Storing configuration in a file}
PuTTY does not currently support storing its configuration in a file
you want to connect to. You should have been told this by the
provider of your login account.
-Now select a login \i{protocol} to use, from the \q{Protocol}
+Now select a login \i{protocol} to use, from the \q{Connection type}
buttons. For a login session, you should select \i{Telnet},
\i{Rlogin} or \i{SSH}. See \k{which-one} for a description of the
differences between the three protocols, and advice on which one to
use. The fourth protocol, \I{raw protocol}\e{Raw}, is not used for
interactive login sessions; you would usually use this for debugging
-other Internet services (see \k{using-rawprot}).
+other Internet services (see \k{using-rawprot}). The fifth option,
+\e{Serial}, is used for connecting to a local serial line, and works
+somewhat differently: see \k{using-serial} for more information on
+this.
When you change the selected protocol, the number in the \q{Port}
box will change. This is normal: it happens because the various
configuration panel. (See \k{config-hostname}.) You can then enter a
host name and a port number, and make the connection.
+\H{using-serial} Connecting to a local serial line
+
+PuTTY can connect directly to a local serial line as an alternative
+to making a network connection. In this mode, text typed into the
+PuTTY window will be sent straight out of your computer's serial
+port, and data received through that port will be displayed in the
+PuTTY window. You might use this mode, for example, if your serial
+port is connected to another computer which has a serial connection.
+
+To make a connection of this type, simply select \q{Serial} from the
+\q{Connection type} radio buttons on the \q{Session} configuration
+panel (see \k{config-hostname}). The \q{Host Name} and \q{Port}
+boxes will transform into \q{Serial line} and \q{Speed}, allowing
+you to specify which serial line to use (if your computer has more
+than one) and what speed (baud rate) to use when transferring data.
+For further configuration options (data bits, stop bits, parity,
+flow control), you can use the \q{Serial} configuration panel (see
+\k{config-serial}).
+
+After you start up PuTTY in serial mode, you might find that you
+have to make the first move, by sending some data out of the serial
+line in order to notify the device at the other end that someone is
+there for it to talk to. This probably depends on the device. If you
+start up a PuTTY serial session and nothing appears in the window,
+try pressing Return a few times and see if that helps.
+
+A serial line provides no well defined means for one end of the
+connection to notify the other that the connection is finished.
+Therefore, PuTTY in serial mode will remain connected until you
+close the window using the close button.
+
\H{using-cmdline} The PuTTY command line
PuTTY can be made to do various things without user intervention by
return 0;
}
+void dlg_label_change(union control *ctrl, void *dlg, char const *text)
+{
+ /*
+ * This function is currently only used by the config box to
+ * switch the labels on the host and port boxes between serial
+ * and network modes. Since the Mac port does not have a serial
+ * back end, this function can safely do nothing.
+ */
+}
+
+
/*
* Local Variables:
* c-file-style: "simon"
[c->textview setString:[NSString stringWithCString:text]];
}
+void dlg_label_change(union control *ctrl, void *dlg, char const *text)
+{
+ /*
+ * This function is currently only used by the config box to
+ * switch the labels on the host and port boxes between serial
+ * and network modes. Since OS X does not (yet?) have a serial
+ * back end, this function can safely do nothing for the
+ * moment.
+ */
+}
+
void dlg_filesel_set(union control *ctrl, void *dv, Filename fn)
{
/* FIXME */
}
#endif /* def DEBUG */
+
+/*
+ * Determine whether or not a Config structure represents a session
+ * which can sensibly be launched right now.
+ */
+int cfg_launchable(const Config *cfg)
+{
+ if (cfg->protocol == PROT_SERIAL)
+ return cfg->serline[0] != 0;
+ else
+ return cfg->host[0] != 0;
+}
+
+char const *cfg_dest(const Config *cfg)
+{
+ if (cfg->protocol == PROT_SERIAL)
+ return cfg->serline;
+ else
+ return cfg->host;
+}
enum {
/* Protocol back ends. (cfg.protocol) */
- PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH
+ PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH,
+ /* PROT_SERIAL is supported on a subset of platforms, but it doesn't
+ * hurt to define it globally. */
+ PROT_SERIAL
};
enum {
FQ_DEFAULT, FQ_ANTIALIASED, FQ_NONANTIALIASED, FQ_CLEARTYPE
};
+enum {
+ SER_PAR_NONE, SER_PAR_ODD, SER_PAR_EVEN, SER_PAR_MARK, SER_PAR_SPACE
+};
+
+enum {
+ SER_FLOW_NONE, SER_FLOW_XONXOFF, SER_FLOW_RTSCTS, SER_FLOW_DSRDTR
+};
+
extern const char *const ttymodes[];
enum {
char localusername[100];
int rfc_environ;
int passive_telnet;
+ /* Serial port options */
+ char serline[256];
+ int serspeed;
+ int serdatabits, serstopbits;
+ int serparity;
+ int serflow;
/* Keyboard options */
int bksp_is_delete;
int rxvt_homeend;
*/
#include "misc.h"
+int cfg_launchable(const Config *cfg);
+char const *cfg_dest(const Config *cfg);
+
+/*
+ * Exports from sercfg.c.
+ */
+void ser_setup_config_box(struct controlbox *b, int midsession);
/*
* Exports from version.c.
--- /dev/null
+/*\r
+ * sercfg.c - the serial-port specific parts of the PuTTY\r
+ * configuration box. Centralised as cross-platform code because\r
+ * more than one platform will want to use it, but not part of the\r
+ * main configuration. The expectation is that each platform's\r
+ * local config function will call out to ser_setup_config_box() if\r
+ * it needs to set up the standard serial stuff. (Of course, it can\r
+ * then apply local tweaks after ser_setup_config_box() returns, if\r
+ * it needs to.)\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+#include "dialog.h"\r
+#include "storage.h"\r
+\r
+static void serial_parity_handler(union control *ctrl, void *dlg,\r
+ void *data, int event)\r
+{\r
+ static const struct {\r
+ const char *name;\r
+ int val;\r
+ } parities[] = {\r
+ {"None", SER_PAR_NONE},\r
+ {"Odd", SER_PAR_ODD},\r
+ {"Even", SER_PAR_EVEN},\r
+ {"Mark", SER_PAR_MARK},\r
+ {"Space", SER_PAR_SPACE},\r
+ };\r
+ int i;\r
+ Config *cfg = (Config *)data;\r
+\r
+ if (event == EVENT_REFRESH) {\r
+ dlg_update_start(ctrl, dlg);\r
+ dlg_listbox_clear(ctrl, dlg);\r
+ for (i = 0; i < lenof(parities); i++)\r
+ dlg_listbox_addwithid(ctrl, dlg, parities[i].name,\r
+ parities[i].val);\r
+ for (i = 0; i < lenof(parities); i++)\r
+ if (cfg->serparity == parities[i].val)\r
+ dlg_listbox_select(ctrl, dlg, i);\r
+ dlg_update_done(ctrl, dlg);\r
+ } else if (event == EVENT_SELCHANGE) {\r
+ int i = dlg_listbox_index(ctrl, dlg);\r
+ if (i < 0)\r
+ i = SER_PAR_NONE;\r
+ else\r
+ i = dlg_listbox_getid(ctrl, dlg, i);\r
+ cfg->serparity = i;\r
+ }\r
+}\r
+\r
+static void serial_flow_handler(union control *ctrl, void *dlg,\r
+ void *data, int event)\r
+{\r
+ static const struct {\r
+ const char *name;\r
+ int val;\r
+ } flows[] = {\r
+ {"None", SER_FLOW_NONE},\r
+ {"XON/XOFF", SER_FLOW_XONXOFF},\r
+ {"RTS/CTS", SER_FLOW_RTSCTS},\r
+ {"DSR/DTR", SER_FLOW_DSRDTR},\r
+ };\r
+ int i;\r
+ Config *cfg = (Config *)data;\r
+\r
+ if (event == EVENT_REFRESH) {\r
+ dlg_update_start(ctrl, dlg);\r
+ dlg_listbox_clear(ctrl, dlg);\r
+ for (i = 0; i < lenof(flows); i++)\r
+ dlg_listbox_addwithid(ctrl, dlg, flows[i].name,\r
+ flows[i].val);\r
+ for (i = 0; i < lenof(flows); i++)\r
+ if (cfg->serflow == flows[i].val)\r
+ dlg_listbox_select(ctrl, dlg, i);\r
+ dlg_update_done(ctrl, dlg);\r
+ } else if (event == EVENT_SELCHANGE) {\r
+ int i = dlg_listbox_index(ctrl, dlg);\r
+ if (i < 0)\r
+ i = SER_PAR_NONE;\r
+ else\r
+ i = dlg_listbox_getid(ctrl, dlg, i);\r
+ cfg->serflow = i;\r
+ }\r
+}\r
+\r
+void ser_setup_config_box(struct controlbox *b, int midsession)\r
+{\r
+ struct controlset *s;\r
+ union control *c;\r
+\r
+ /*\r
+ * Add the serial back end to the protocols list at the top of\r
+ * the config box.\r
+ */\r
+ s = ctrl_getset(b, "Session", "hostport",\r
+ "Specify your connection by host name or IP address");\r
+ {\r
+ int i;\r
+ extern void config_protocolbuttons_handler(union control *, void *,\r
+ void *, int);\r
+ for (i = 0; i < s->ncontrols; i++) {\r
+ c = s->ctrls[i];\r
+ if (c->generic.type == CTRL_RADIO &&\r
+ c->generic.handler == config_protocolbuttons_handler) {\r
+ c->radio.nbuttons++;\r
+ c->radio.ncolumns++;\r
+ c->radio.buttons =\r
+ sresize(c->radio.buttons, c->radio.nbuttons, char *);\r
+ c->radio.buttons[c->radio.nbuttons-1] =\r
+ dupstr("Serial");\r
+ c->radio.buttondata =\r
+ sresize(c->radio.buttondata, c->radio.nbuttons, intorptr);\r
+ c->radio.buttondata[c->radio.nbuttons-1] = I(PROT_SERIAL);\r
+ if (c->radio.shortcuts) {\r
+ c->radio.shortcuts =\r
+ sresize(c->radio.shortcuts, c->radio.nbuttons, char);\r
+ c->radio.shortcuts[c->radio.nbuttons-1] = NO_SHORTCUT;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /*\r
+ * Entirely new Connection/Serial panel for serial port\r
+ * configuration.\r
+ */\r
+ ctrl_settitle(b, "Connection/Serial",\r
+ "Options controlling local serial lines");\r
+\r
+ if (!midsession) {\r
+ /*\r
+ * We don't permit switching to a different serial port in\r
+ * midflight, although we do allow all other\r
+ * reconfiguration.\r
+ */\r
+ s = ctrl_getset(b, "Connection/Serial", "serline",\r
+ "Select a serial line");\r
+ ctrl_editbox(s, "Serial line to connect to", 'l', 40,\r
+ HELPCTX(serial_line),\r
+ dlg_stdeditbox_handler, I(offsetof(Config,serline)),\r
+ I(sizeof(((Config *)0)->serline)));\r
+ }\r
+\r
+ s = ctrl_getset(b, "Connection/Serial", "sercfg", "Configure the serial line");\r
+ ctrl_editbox(s, "Speed (baud)", 's', 40,\r
+ HELPCTX(serial_speed),\r
+ dlg_stdeditbox_handler, I(offsetof(Config,serspeed)), I(-1));\r
+ ctrl_editbox(s, "Data bits", 'b', 40,\r
+ HELPCTX(serial_databits),\r
+ dlg_stdeditbox_handler,I(offsetof(Config,serdatabits)),I(-1));\r
+ /*\r
+ * Stop bits come in units of one half.\r
+ */\r
+ ctrl_editbox(s, "Stop bits", 't', 40,\r
+ HELPCTX(serial_stopbits),\r
+ dlg_stdeditbox_handler,I(offsetof(Config,serstopbits)),I(-2));\r
+ ctrl_droplist(s, "Parity", 'p', 40,\r
+ HELPCTX(serial_parity),\r
+ serial_parity_handler, I(0));\r
+ ctrl_droplist(s, "Flow control", 'f', 40,\r
+ HELPCTX(serial_flow),\r
+ serial_flow_handler, I(0));\r
+}\r
write_setting_fontspec(sesskey, "WideBoldFont", cfg->wideboldfont);
write_setting_i(sesskey, "ShadowBold", cfg->shadowbold);
write_setting_i(sesskey, "ShadowBoldOffset", cfg->shadowboldoffset);
+ write_setting_s(sesskey, "SerialLine", cfg->serline);
+ write_setting_i(sesskey, "SerialSpeed", cfg->serspeed);
+ write_setting_i(sesskey, "SerialDataBits", cfg->serdatabits);
+ write_setting_i(sesskey, "SerialStopHalfbits", cfg->serstopbits);
+ write_setting_i(sesskey, "SerialParity", cfg->serparity);
+ write_setting_i(sesskey, "SerialFlowControl", cfg->serflow);
}
void load_settings(char *section, int do_host, Config * cfg)
gppfont(sesskey, "WideFont", &cfg->widefont);
gppfont(sesskey, "WideBoldFont", &cfg->wideboldfont);
gppi(sesskey, "ShadowBoldOffset", 1, &cfg->shadowboldoffset);
+ gpps(sesskey, "SerialLine", "", cfg->serline, sizeof(cfg->serline));
+ gppi(sesskey, "SerialSpeed", 9600, &cfg->serspeed);
+ gppi(sesskey, "SerialDataBits", 8, &cfg->serdatabits);
+ gppi(sesskey, "SerialStopHalfbits", 2, &cfg->serstopbits);
+ gppi(sesskey, "SerialParity", 0, &cfg->serparity);
+ gppi(sesskey, "SerialFlowControl", 0, &cfg->serflow);
}
void do_defaults(char *session, Config * cfg)
gtk_label_set_text(GTK_LABEL(uc->text), text);
}
+void dlg_label_change(union control *ctrl, void *dlg, char const *text)
+{
+ /*
+ * This function is currently only used by the config box to
+ * switch the labels on the host and port boxes between serial
+ * and network modes. Since Unix does not (yet) have a serial
+ * back end, this function can safely do nothing for the
+ * moment.
+ */
+}
+
void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
}
}
}
+
+ /*
+ * Serial back end is available on Windows.
+ */
+ ser_setup_config_box(b, midsession);
}
* Return value is a malloc'ed copy of the processed version of the
* string.
*/
-static char *shortcut_escape(char *text, char shortcut)
+static char *shortcut_escape(const char *text, char shortcut)
{
char *ret;
- char *p, *q;
+ char const *p;
+ char *q;
if (!text)
return NULL; /* sfree won't choke on this */
SetDlgItemText(dp->hwnd, c->base_id, text);
}
+void dlg_label_change(union control *ctrl, void *dlg, char const *text)
+{
+ struct dlgparam *dp = (struct dlgparam *)dlg;
+ struct winctrl *c = dlg_findbyctrl(dp, ctrl);
+ char *escaped = NULL;
+ int id = -1;
+
+ assert(c);
+ switch (c->ctrl->generic.type) {
+ case CTRL_EDITBOX:
+ escaped = shortcut_escape(text, c->ctrl->editbox.shortcut);
+ id = c->base_id;
+ break;
+ case CTRL_RADIO:
+ escaped = shortcut_escape(text, c->ctrl->radio.shortcut);
+ id = c->base_id;
+ break;
+ case CTRL_CHECKBOX:
+ escaped = shortcut_escape(text, ctrl->checkbox.shortcut);
+ id = c->base_id;
+ break;
+ case CTRL_BUTTON:
+ escaped = shortcut_escape(text, ctrl->button.shortcut);
+ id = c->base_id;
+ break;
+ case CTRL_LISTBOX:
+ escaped = shortcut_escape(text, ctrl->listbox.shortcut);
+ id = c->base_id;
+ break;
+ case CTRL_FILESELECT:
+ escaped = shortcut_escape(text, ctrl->fileselect.shortcut);
+ id = c->base_id;
+ break;
+ case CTRL_FONTSELECT:
+ escaped = shortcut_escape(text, ctrl->fontselect.shortcut);
+ id = c->base_id;
+ break;
+ default:
+ assert(!"Can't happen");
+ break;
+ }
+ if (escaped) {
+ SetDlgItemText(dp->hwnd, id, escaped);
+ sfree(escaped);
+ }
+}
+
void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
char *platform_default_s(const char *name)
{
+ if (!strcmp(name, "SerialLine"))
+ return dupstr("COM1");
return NULL;
}
if (error) {
char *str = dupprintf("%s Error", appname);
sprintf(msg, "Unable to open connection to\n"
- "%.800s\n" "%s", cfg.host, error);
+ "%.800s\n" "%s", cfg_dest(&cfg), error);
MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK);
sfree(str);
exit(0);
#define WINHELP_CTX_ssh_bugs_rsapad2 "ssh.bugs.rsapad2"
#define WINHELP_CTX_ssh_bugs_pksessid2 "ssh.bugs.pksessid2"
#define WINHELP_CTX_ssh_bugs_rekey2 "ssh.bugs.rekey2"
+#define WINHELP_CTX_serial_line "serial.line"
+#define WINHELP_CTX_serial_speed "serial.speed"
+#define WINHELP_CTX_serial_databits "serial.databits"
+#define WINHELP_CTX_serial_stopbits "serial.stopbits"
+#define WINHELP_CTX_serial_parity "serial.parity"
+#define WINHELP_CTX_serial_flow "serial.flow"
/* These are used in Windows-specific bits of the frontend.
* We (ab)use "help context identifiers" (dwContextId) to identify them. */
errors = 1;
}
} else if (*p) {
- if (!*cfg.host) {
+ if (!cfg_launchable(&cfg)) {
char *q = p;
/*
* If the hostname starts with "telnet:", set the
{
Config cfg2;
do_defaults(host, &cfg2);
- if (loaded_session || cfg2.host[0] == '\0') {
+ if (loaded_session || !cfg_launchable(&cfg2)) {
/* No settings for this host; use defaults */
/* (or session was already loaded with -load) */
strncpy(cfg.host, host, sizeof(cfg.host) - 1);
if (errors)
return 1;
- if (!*cfg.host) {
+ if (!cfg_launchable(&cfg)) {
usage();
}
}
/* See if host is of the form user@host */
- if (cfg.host[0] != '\0') {
+ if (cfg_launchable(&cfg)) {
char *atsign = strrchr(cfg.host, '@');
/* Make sure we're not overflowing the user field */
if (atsign) {
--- /dev/null
+/*\r
+ * Serial back end (Windows-specific).\r
+ */\r
+\r
+/*\r
+ * TODO:\r
+ * \r
+ * - sending breaks?\r
+ * + looks as if you do this by calling SetCommBreak(handle),\r
+ * then waiting a bit, then doing ClearCommBreak(handle). A\r
+ * small job for timing.c, methinks.\r
+ *\r
+ * - why are we dropping data when talking to judicator?\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <limits.h>\r
+\r
+#include "putty.h"\r
+\r
+#define SERIAL_MAX_BACKLOG 4096\r
+\r
+typedef struct serial_backend_data {\r
+ HANDLE port;\r
+ struct handle *out, *in;\r
+ void *frontend;\r
+ int bufsize;\r
+} *Serial;\r
+\r
+static void serial_terminate(Serial serial)\r
+{\r
+ if (serial->out) {\r
+ handle_free(serial->out);\r
+ serial->out = NULL;\r
+ }\r
+ if (serial->in) {\r
+ handle_free(serial->in);\r
+ serial->in = NULL;\r
+ }\r
+ if (serial->port) {\r
+ CloseHandle(serial->port);\r
+ serial->port = NULL;\r
+ }\r
+}\r
+\r
+static int serial_gotdata(struct handle *h, void *data, int len)\r
+{\r
+ Serial serial = (Serial)handle_get_privdata(h);\r
+ if (len <= 0) {\r
+ const char *error_msg;\r
+\r
+ /*\r
+ * Currently, len==0 should never happen because we're\r
+ * ignoring EOFs. However, it seems not totally impossible\r
+ * that this same back end might be usable to talk to named\r
+ * pipes or some other non-serial device, in which case EOF\r
+ * may become meaningful here.\r
+ */\r
+ if (len == 0)\r
+ error_msg = "End of file reading from serial device";\r
+ else\r
+ error_msg = "Error reading from serial device";\r
+\r
+ serial_terminate(serial);\r
+\r
+ notify_remote_exit(serial->frontend);\r
+\r
+ logevent(serial->frontend, error_msg);\r
+\r
+ connection_fatal(serial->frontend, "%s", error_msg);\r
+\r
+ return 0; /* placate optimiser */\r
+ } else {\r
+ return from_backend(serial->frontend, 0, data, len);\r
+ }\r
+}\r
+\r
+static void serial_sentdata(struct handle *h, int new_backlog)\r
+{\r
+ Serial serial = (Serial)handle_get_privdata(h);\r
+ if (new_backlog < 0) {\r
+ const char *error_msg = "Error writing to serial device";\r
+\r
+ serial_terminate(serial);\r
+\r
+ notify_remote_exit(serial->frontend);\r
+\r
+ logevent(serial->frontend, error_msg);\r
+\r
+ connection_fatal(serial->frontend, "%s", error_msg);\r
+ } else {\r
+ serial->bufsize = new_backlog;\r
+ }\r
+}\r
+\r
+static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg)\r
+{\r
+ DCB dcb;\r
+ COMMTIMEOUTS timeouts;\r
+\r
+ /*\r
+ * Set up the serial port parameters. If we can't even\r
+ * GetCommState, we ignore the problem on the grounds that the\r
+ * user might have pointed us at some other type of two-way\r
+ * device instead of a serial port.\r
+ */\r
+ if (GetCommState(serport, &dcb)) {\r
+ char *msg;\r
+ const char *str;\r
+\r
+ /*\r
+ * Boilerplate.\r
+ */\r
+ dcb.fBinary = TRUE;\r
+ dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
+ dcb.fDsrSensitivity = FALSE;\r
+ dcb.fTXContinueOnXoff = FALSE;\r
+ dcb.fOutX = FALSE;\r
+ dcb.fInX = FALSE;\r
+ dcb.fErrorChar = FALSE;\r
+ dcb.fNull = FALSE;\r
+ dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
+ dcb.fAbortOnError = FALSE;\r
+ dcb.fOutxCtsFlow = FALSE;\r
+ dcb.fOutxDsrFlow = FALSE;\r
+\r
+ /*\r
+ * Configurable parameters.\r
+ */\r
+ dcb.BaudRate = cfg->serspeed;\r
+ msg = dupprintf("Configuring baud rate %d", cfg->serspeed);\r
+ logevent(serial->frontend, msg);\r
+ sfree(msg);\r
+\r
+ dcb.ByteSize = cfg->serdatabits;\r
+ msg = dupprintf("Configuring %d data bits", cfg->serdatabits);\r
+ logevent(serial->frontend, msg);\r
+ sfree(msg);\r
+\r
+ switch (cfg->serstopbits) {\r
+ case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break;\r
+ case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break;\r
+ case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break;\r
+ default: return "Invalid number of stop bits (need 1, 1.5 or 2)";\r
+ }\r
+ msg = dupprintf("Configuring %s data bits", str);\r
+ logevent(serial->frontend, msg);\r
+ sfree(msg);\r
+\r
+ switch (cfg->serparity) {\r
+ case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break;\r
+ case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break;\r
+ case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break;\r
+ case SER_PAR_MARK: dcb.Parity = MARKPARITY; str = "mark"; break;\r
+ case SER_PAR_SPACE: dcb.Parity = SPACEPARITY; str = "space"; break;\r
+ }\r
+ msg = dupprintf("Configuring %s parity", str);\r
+ logevent(serial->frontend, msg);\r
+ sfree(msg);\r
+\r
+ switch (cfg->serflow) {\r
+ case SER_FLOW_NONE:\r
+ str = "no";\r
+ break;\r
+ case SER_FLOW_XONXOFF:\r
+ dcb.fOutX = dcb.fInX = TRUE;\r
+ str = "XON/XOFF";\r
+ break;\r
+ case SER_FLOW_RTSCTS:\r
+ dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;\r
+ dcb.fOutxCtsFlow = TRUE;\r
+ str = "RTS/CTS";\r
+ break;\r
+ case SER_FLOW_DSRDTR:\r
+ dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;\r
+ dcb.fOutxDsrFlow = TRUE;\r
+ str = "DSR/DTR";\r
+ break;\r
+ }\r
+ msg = dupprintf("Configuring %s flow control", str);\r
+ logevent(serial->frontend, msg);\r
+ sfree(msg);\r
+\r
+ if (!SetCommState(serport, &dcb))\r
+ return "Unable to configure serial port";\r
+\r
+ timeouts.ReadIntervalTimeout = 1;\r
+ timeouts.ReadTotalTimeoutMultiplier = 0;\r
+ timeouts.ReadTotalTimeoutConstant = 0;\r
+ timeouts.WriteTotalTimeoutMultiplier = 0;\r
+ timeouts.WriteTotalTimeoutConstant = 0;\r
+ if (!SetCommTimeouts(serport, &timeouts))\r
+ return "Unable to configure serial timeouts";\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/*\r
+ * Called to set up the serial connection.\r
+ * \r
+ * Returns an error message, or NULL on success.\r
+ *\r
+ * Also places the canonical host name into `realhost'. It must be\r
+ * freed by the caller.\r
+ */\r
+static const char *serial_init(void *frontend_handle, void **backend_handle,\r
+ Config *cfg,\r
+ char *host, int port, char **realhost, int nodelay,\r
+ int keepalive)\r
+{\r
+ Serial serial;\r
+ HANDLE serport;\r
+ const char *err;\r
+\r
+ serial = snew(struct serial_backend_data);\r
+ serial->port = NULL;\r
+ serial->out = serial->in = NULL;\r
+ *backend_handle = serial;\r
+\r
+ serial->frontend = frontend_handle;\r
+\r
+ {\r
+ char *msg = dupprintf("Opening serial device %s", host);\r
+ logevent(serial->frontend, msg);\r
+ }\r
+\r
+ serport = CreateFile(cfg->serline, GENERIC_READ | GENERIC_WRITE, 0, NULL,\r
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
+ if (serport == INVALID_HANDLE_VALUE)\r
+ return "Unable to open serial port";\r
+\r
+ err = serial_configure(serial, serport, cfg);\r
+ if (err)\r
+ return err;\r
+\r
+ serial->port = serport;\r
+ serial->out = handle_output_new(serport, serial_sentdata, serial,\r
+ HANDLE_FLAG_OVERLAPPED);\r
+ serial->in = handle_input_new(serport, serial_gotdata, serial,\r
+ HANDLE_FLAG_OVERLAPPED |\r
+ HANDLE_FLAG_IGNOREEOF);\r
+\r
+ *realhost = dupstr(cfg->serline);\r
+\r
+ return NULL;\r
+}\r
+\r
+static void serial_free(void *handle)\r
+{\r
+ Serial serial = (Serial) handle;\r
+\r
+ serial_terminate(serial);\r
+ sfree(serial);\r
+}\r
+\r
+static void serial_reconfig(void *handle, Config *cfg)\r
+{\r
+ Serial serial = (Serial) handle;\r
+ const char *err;\r
+\r
+ err = serial_configure(serial, serial->port, cfg);\r
+\r
+ /*\r
+ * FIXME: what should we do if err returns something?\r
+ */\r
+}\r
+\r
+/*\r
+ * Called to send data down the serial connection.\r
+ */\r
+static int serial_send(void *handle, char *buf, int len)\r
+{\r
+ Serial serial = (Serial) handle;\r
+\r
+ if (serial->out == NULL)\r
+ return 0;\r
+\r
+ serial->bufsize = handle_write(serial->out, buf, len);\r
+ return serial->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to query the current sendability status.\r
+ */\r
+static int serial_sendbuffer(void *handle)\r
+{\r
+ Serial serial = (Serial) handle;\r
+ return serial->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to set the size of the window\r
+ */\r
+static void serial_size(void *handle, int width, int height)\r
+{\r
+ /* Do nothing! */\r
+ return;\r
+}\r
+\r
+/*\r
+ * Send serial special codes.\r
+ */\r
+static void serial_special(void *handle, Telnet_Special code)\r
+{\r
+ /*\r
+ * FIXME: serial break? XON? XOFF?\r
+ */\r
+ return;\r
+}\r
+\r
+/*\r
+ * Return a list of the special codes that make sense in this\r
+ * protocol.\r
+ */\r
+static const struct telnet_special *serial_get_specials(void *handle)\r
+{\r
+ /*\r
+ * FIXME: serial break? XON? XOFF?\r
+ */\r
+ return NULL;\r
+}\r
+\r
+static int serial_connected(void *handle)\r
+{\r
+ return 1; /* always connected */\r
+}\r
+\r
+static int serial_sendok(void *handle)\r
+{\r
+ return 1;\r
+}\r
+\r
+static void serial_unthrottle(void *handle, int backlog)\r
+{\r
+ Serial serial = (Serial) handle;\r
+ if (serial->in)\r
+ handle_unthrottle(serial->in, backlog);\r
+}\r
+\r
+static int serial_ldisc(void *handle, int option)\r
+{\r
+ /*\r
+ * Local editing and local echo are off by default.\r
+ */\r
+ return 0;\r
+}\r
+\r
+static void serial_provide_ldisc(void *handle, void *ldisc)\r
+{\r
+ /* This is a stub. */\r
+}\r
+\r
+static void serial_provide_logctx(void *handle, void *logctx)\r
+{\r
+ /* This is a stub. */\r
+}\r
+\r
+static int serial_exitcode(void *handle)\r
+{\r
+ Serial serial = (Serial) handle;\r
+ if (serial->port != NULL)\r
+ return -1; /* still connected */\r
+ else\r
+ /* Exit codes are a meaningless concept with serial ports */\r
+ return INT_MAX;\r
+}\r
+\r
+/*\r
+ * cfg_info for Serial does nothing at all.\r
+ */\r
+static int serial_cfg_info(void *handle)\r
+{\r
+ return 0;\r
+}\r
+\r
+Backend serial_backend = {\r
+ serial_init,\r
+ serial_free,\r
+ serial_reconfig,\r
+ serial_send,\r
+ serial_sendbuffer,\r
+ serial_size,\r
+ serial_special,\r
+ serial_get_specials,\r
+ serial_connected,\r
+ serial_exitcode,\r
+ serial_sendok,\r
+ serial_ldisc,\r
+ serial_provide_ldisc,\r
+ serial_provide_logctx,\r
+ serial_unthrottle,\r
+ serial_cfg_info,\r
+ 1\r
+};\r
void *callback_ctx, void *data, int len);
#define FLAG_SYNCAGENT 0x1000
+/*
+ * Exports from winser.c.
+ */
+extern Backend serial_backend;
+
#endif