mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-06-30 19:12:48 -05:00
Centralise PuTTY and Plink's non-option argument handling.
This is another piece of long-overdue refactoring similar to the
recent commit e3796cb77
. But where that one dealt with normalisation
of stuff already stored _in_ a Conf by whatever means (including, in
particular, handling a user typing 'username@host.name' into the
Hostname box of the GUI session dialog box), this one deals with
handling argv entries and putting them into the Conf.
This isn't exactly a pure no-functional-change-at-all refactoring. On
the other hand, it isn't a full-on cleanup that completely
rationalises all the user-visible behaviour as well as the code
structure. It's somewhere in between: I've preserved all the behaviour
quirks that I could imagine a reason for having intended, but taken
the opportunity to _not_ faithfully replicate anything I thought was
clearly just a bug.
So, for example, the following inconsistency is carefully preserved:
the command 'plink -load session nextword' treats 'nextword' as a host
name if the loaded session hasn't provided a hostname already, and
otherwise treats 'nextword' as the remote command to execute on the
already-specified remote host, but the same combination of arguments
to GUI PuTTY will _always_ treat 'nextword' as a hostname, overriding
a hostname (if any) in the saved session. That makes some sense to me
because of the different shapes of the overall command lines.
On the other hand, there are two behaviour changes I know of as a
result of this commit: a third argument to GUI PuTTY (after a hostname
and port) now provokes an error message instead of being silently
ignored, and in Plink, if you combine a -P option (specifying a port
number) with the historical comma-separated protocol selection prefix
on the hostname argument (which I'd completely forgotten even existed
until this piece of work), then the -P will now override the selected
protocol's default port number, whereas previously the default port
would win. For example, 'plink -P 12345 telnet,hostname' will now
connect via Telnet to port 12345 instead of to port 23.
There may be scope for removing or rethinking some of the command-
line syntax quirks in the wake of this change. If we do decide to do
anything like that, then hopefully having it all in one place will
make it easier to remove or change things consistently across the
tools.
This commit is contained in:
@ -360,6 +360,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
|
||||
hinst = inst;
|
||||
hwnd = NULL;
|
||||
flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
|
||||
cmdline_tooltype |= TOOLTYPE_HOST_ARG | TOOLTYPE_PORT_ARG;
|
||||
|
||||
sk_init();
|
||||
|
||||
@ -416,11 +417,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
|
||||
*/
|
||||
{
|
||||
char *p;
|
||||
int got_host = 0;
|
||||
/* By default, we bring up the config dialog, rather than launching
|
||||
* a session. This gets set to TRUE if something happens to change
|
||||
* that (e.g., a hostname is specified on the command-line). */
|
||||
int allow_launch = FALSE;
|
||||
int special_launchable_argument = FALSE;
|
||||
|
||||
default_protocol = be_default_protocol;
|
||||
/* Find the appropriate default port. */
|
||||
@ -470,7 +467,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
|
||||
if (!conf_launchable(conf) && !do_config()) {
|
||||
cleanup_exit(0);
|
||||
}
|
||||
allow_launch = TRUE; /* allow it to be launched directly */
|
||||
special_launchable_argument = TRUE;
|
||||
} else if (*p == '&') {
|
||||
/*
|
||||
* An initial & means we've been given a command line
|
||||
@ -490,7 +487,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
|
||||
} else if (!do_config()) {
|
||||
cleanup_exit(0);
|
||||
}
|
||||
allow_launch = TRUE;
|
||||
special_launchable_argument = TRUE;
|
||||
} else if (!*p) {
|
||||
/* Do-nothing case for an empty command line - or rather,
|
||||
* for a command line that's empty _after_ we strip off
|
||||
@ -545,52 +542,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
|
||||
pgp_fingerprints();
|
||||
exit(1);
|
||||
} else if (*p != '-') {
|
||||
char *q = p;
|
||||
if (got_host) {
|
||||
/*
|
||||
* If we already have a host name, treat
|
||||
* this argument as a port number. NB we
|
||||
* have to treat this as a saved -P
|
||||
* argument, so that it will be deferred
|
||||
* until it's a good moment to run it.
|
||||
*/
|
||||
int ret = cmdline_process_param("-P", p, 1, conf);
|
||||
assert(ret == 2);
|
||||
} else if (!strncmp(q, "telnet:", 7)) {
|
||||
/*
|
||||
* If the hostname starts with "telnet:",
|
||||
* set the protocol to Telnet and process
|
||||
* the string as a Telnet URL.
|
||||
*/
|
||||
char c;
|
||||
|
||||
q += 7;
|
||||
if (q[0] == '/' && q[1] == '/')
|
||||
q += 2;
|
||||
conf_set_int(conf, CONF_protocol, PROT_TELNET);
|
||||
p = q;
|
||||
p += host_strcspn(p, ":/");
|
||||
c = *p;
|
||||
if (*p)
|
||||
*p++ = '\0';
|
||||
if (c == ':')
|
||||
conf_set_int(conf, CONF_port, atoi(p));
|
||||
else
|
||||
conf_set_int(conf, CONF_port, -1);
|
||||
conf_set_str(conf, CONF_host, q);
|
||||
got_host = 1;
|
||||
} else {
|
||||
/*
|
||||
* Otherwise, treat this argument as a host
|
||||
* name.
|
||||
*/
|
||||
while (*p && !isspace(*p))
|
||||
p++;
|
||||
if (*p)
|
||||
*p++ = '\0';
|
||||
conf_set_str(conf, CONF_host, q);
|
||||
got_host = 1;
|
||||
}
|
||||
cmdline_error("unexpected argument \"%s\"", p);
|
||||
} else {
|
||||
cmdline_error("unknown option \"%s\"", p);
|
||||
}
|
||||
@ -599,11 +551,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
|
||||
|
||||
cmdline_run_saved(conf);
|
||||
|
||||
if (loaded_session || got_host)
|
||||
allow_launch = TRUE;
|
||||
|
||||
if ((!allow_launch || !conf_launchable(conf)) && !do_config()) {
|
||||
cleanup_exit(0);
|
||||
/*
|
||||
* Bring up the config dialog if the command line hasn't
|
||||
* (explicitly) specified a launchable configuration.
|
||||
*/
|
||||
if (!(special_launchable_argument || cmdline_host_ok(conf))) {
|
||||
if (!do_config())
|
||||
cleanup_exit(0);
|
||||
}
|
||||
|
||||
prepare_session(conf);
|
||||
|
@ -293,7 +293,6 @@ const int share_can_be_upstream = TRUE;
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int sending;
|
||||
int portnumber = -1;
|
||||
SOCKET *sklist;
|
||||
int skcount, sksize;
|
||||
int exitcode;
|
||||
@ -315,6 +314,12 @@ int main(int argc, char **argv)
|
||||
default_port = 22;
|
||||
|
||||
flags = FLAG_STDERR;
|
||||
cmdline_tooltype |=
|
||||
(TOOLTYPE_HOST_ARG |
|
||||
TOOLTYPE_HOST_ARG_CAN_BE_SESSION |
|
||||
TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX |
|
||||
TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD);
|
||||
|
||||
/*
|
||||
* Process the command line.
|
||||
*/
|
||||
@ -341,160 +346,68 @@ int main(int argc, char **argv)
|
||||
}
|
||||
while (--argc) {
|
||||
char *p = *++argv;
|
||||
if (*p == '-') {
|
||||
int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
|
||||
1, conf);
|
||||
if (ret == -2) {
|
||||
fprintf(stderr,
|
||||
"plink: option \"%s\" requires an argument\n", p);
|
||||
errors = 1;
|
||||
} else if (ret == 2) {
|
||||
--argc, ++argv;
|
||||
} else if (ret == 1) {
|
||||
continue;
|
||||
} else if (!strcmp(p, "-batch")) {
|
||||
console_batch_mode = 1;
|
||||
} else if (!strcmp(p, "-s")) {
|
||||
/* Save status to write to conf later. */
|
||||
use_subsystem = 1;
|
||||
} else if (!strcmp(p, "-V") || !strcmp(p, "--version")) {
|
||||
version();
|
||||
} else if (!strcmp(p, "--help")) {
|
||||
usage();
|
||||
} else if (!strcmp(p, "-pgpfp")) {
|
||||
pgp_fingerprints();
|
||||
exit(1);
|
||||
} else if (!strcmp(p, "-shareexists")) {
|
||||
just_test_share_exists = TRUE;
|
||||
} else {
|
||||
fprintf(stderr, "plink: unknown option \"%s\"\n", p);
|
||||
errors = 1;
|
||||
}
|
||||
} else if (*p) {
|
||||
if (!conf_launchable(conf) || !(got_host || loaded_session)) {
|
||||
char *q = p;
|
||||
/*
|
||||
* If the hostname starts with "telnet:", set the
|
||||
* protocol to Telnet and process the string as a
|
||||
* Telnet URL.
|
||||
*/
|
||||
if (!strncmp(q, "telnet:", 7)) {
|
||||
char c;
|
||||
int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
|
||||
1, conf);
|
||||
if (ret == -2) {
|
||||
fprintf(stderr,
|
||||
"plink: option \"%s\" requires an argument\n", p);
|
||||
errors = 1;
|
||||
} else if (ret == 2) {
|
||||
--argc, ++argv;
|
||||
} else if (ret == 1) {
|
||||
continue;
|
||||
} else if (!strcmp(p, "-batch")) {
|
||||
console_batch_mode = 1;
|
||||
} else if (!strcmp(p, "-s")) {
|
||||
/* Save status to write to conf later. */
|
||||
use_subsystem = 1;
|
||||
} else if (!strcmp(p, "-V") || !strcmp(p, "--version")) {
|
||||
version();
|
||||
} else if (!strcmp(p, "--help")) {
|
||||
usage();
|
||||
} else if (!strcmp(p, "-pgpfp")) {
|
||||
pgp_fingerprints();
|
||||
exit(1);
|
||||
} else if (!strcmp(p, "-shareexists")) {
|
||||
just_test_share_exists = TRUE;
|
||||
} else if (*p != '-') {
|
||||
char *command;
|
||||
int cmdlen, cmdsize;
|
||||
cmdlen = cmdsize = 0;
|
||||
command = NULL;
|
||||
|
||||
q += 7;
|
||||
if (q[0] == '/' && q[1] == '/')
|
||||
q += 2;
|
||||
conf_set_int(conf, CONF_protocol, PROT_TELNET);
|
||||
p = q;
|
||||
p += host_strcspn(p, ":/");
|
||||
c = *p;
|
||||
if (*p)
|
||||
*p++ = '\0';
|
||||
if (c == ':')
|
||||
conf_set_int(conf, CONF_port, atoi(p));
|
||||
else
|
||||
conf_set_int(conf, CONF_port, -1);
|
||||
conf_set_str(conf, CONF_host, q);
|
||||
got_host = TRUE;
|
||||
} else {
|
||||
char *r, *user, *host;
|
||||
/*
|
||||
* Before we process the [user@]host string, we
|
||||
* first check for the presence of a protocol
|
||||
* prefix (a protocol name followed by ",").
|
||||
*/
|
||||
r = strchr(p, ',');
|
||||
if (r) {
|
||||
const Backend *b;
|
||||
*r = '\0';
|
||||
b = backend_from_name(p);
|
||||
if (b) {
|
||||
default_protocol = b->protocol;
|
||||
conf_set_int(conf, CONF_protocol,
|
||||
default_protocol);
|
||||
portnumber = b->default_port;
|
||||
}
|
||||
p = r + 1;
|
||||
}
|
||||
while (argc) {
|
||||
while (*p) {
|
||||
if (cmdlen >= cmdsize) {
|
||||
cmdsize = cmdlen + 512;
|
||||
command = sresize(command, cmdsize, char);
|
||||
}
|
||||
command[cmdlen++]=*p++;
|
||||
}
|
||||
if (cmdlen >= cmdsize) {
|
||||
cmdsize = cmdlen + 512;
|
||||
command = sresize(command, cmdsize, char);
|
||||
}
|
||||
command[cmdlen++]=' '; /* always add trailing space */
|
||||
if (--argc) p = *++argv;
|
||||
}
|
||||
if (cmdlen) command[--cmdlen]='\0';
|
||||
/* change trailing blank to NUL */
|
||||
conf_set_str(conf, CONF_remote_cmd, command);
|
||||
conf_set_str(conf, CONF_remote_cmd2, "");
|
||||
conf_set_int(conf, CONF_nopty, TRUE); /* command => no tty */
|
||||
|
||||
/*
|
||||
* A nonzero length string followed by an @ is treated
|
||||
* as a username. (We discount an _initial_ @.) The
|
||||
* rest of the string (or the whole string if no @)
|
||||
* is treated as a session name and/or hostname.
|
||||
*/
|
||||
r = strrchr(p, '@');
|
||||
if (r == p)
|
||||
p++, r = NULL; /* discount initial @ */
|
||||
if (r) {
|
||||
*r++ = '\0';
|
||||
user = p, host = r;
|
||||
} else {
|
||||
user = NULL, host = p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now attempt to load a saved session with the
|
||||
* same name as the hostname.
|
||||
*/
|
||||
{
|
||||
Conf *conf2 = conf_new();
|
||||
do_defaults(host, conf2);
|
||||
if (loaded_session || !conf_launchable(conf2)) {
|
||||
/* No settings for this host; use defaults */
|
||||
/* (or session was already loaded with -load) */
|
||||
conf_set_str(conf, CONF_host, host);
|
||||
conf_set_int(conf, CONF_port, default_port);
|
||||
got_host = TRUE;
|
||||
} else {
|
||||
conf_copy_into(conf, conf2);
|
||||
loaded_session = TRUE;
|
||||
}
|
||||
conf_free(conf2);
|
||||
}
|
||||
|
||||
if (user) {
|
||||
/* Patch in specified username. */
|
||||
conf_set_str(conf, CONF_username, user);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
char *command;
|
||||
int cmdlen, cmdsize;
|
||||
cmdlen = cmdsize = 0;
|
||||
command = NULL;
|
||||
|
||||
while (argc) {
|
||||
while (*p) {
|
||||
if (cmdlen >= cmdsize) {
|
||||
cmdsize = cmdlen + 512;
|
||||
command = sresize(command, cmdsize, char);
|
||||
}
|
||||
command[cmdlen++]=*p++;
|
||||
}
|
||||
if (cmdlen >= cmdsize) {
|
||||
cmdsize = cmdlen + 512;
|
||||
command = sresize(command, cmdsize, char);
|
||||
}
|
||||
command[cmdlen++]=' '; /* always add trailing space */
|
||||
if (--argc) p = *++argv;
|
||||
}
|
||||
if (cmdlen) command[--cmdlen]='\0';
|
||||
/* change trailing blank to NUL */
|
||||
conf_set_str(conf, CONF_remote_cmd, command);
|
||||
conf_set_str(conf, CONF_remote_cmd2, "");
|
||||
conf_set_int(conf, CONF_nopty, TRUE); /* command => no tty */
|
||||
|
||||
break; /* done with cmdline */
|
||||
}
|
||||
}
|
||||
break; /* done with cmdline */
|
||||
} else {
|
||||
fprintf(stderr, "plink: unknown option \"%s\"\n", p);
|
||||
errors = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (errors)
|
||||
return 1;
|
||||
|
||||
if (!conf_launchable(conf) || !(got_host || loaded_session)) {
|
||||
if (!cmdline_host_ok(conf)) {
|
||||
usage();
|
||||
}
|
||||
|
||||
@ -527,12 +440,6 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select port.
|
||||
*/
|
||||
if (portnumber != -1)
|
||||
conf_set_int(conf, CONF_port, portnumber);
|
||||
|
||||
sk_init();
|
||||
if (p_WSAEventSelect == NULL) {
|
||||
fprintf(stderr, "Plink requires WinSock 2\n");
|
||||
|
Reference in New Issue
Block a user