diff --git a/cmdline.c b/cmdline.c index d2a81592..1a14cc99 100644 --- a/cmdline.c +++ b/cmdline.c @@ -159,11 +159,242 @@ static int cmdline_check_unavailable(int flag, const char *p) if (need_save < 0) return x; \ } while (0) +static int seen_hostname_argument = FALSE; +static int seen_port_argument = FALSE; + int cmdline_process_param(const char *p, char *value, int need_save, Conf *conf) { int ret = 0; + if (p[0] != '-') { + if (need_save < 0) + return 0; + + /* + * Common handling for the tools whose initial command-line + * arguments specify a hostname to connect to, i.e. PuTTY and + * Plink. Doesn't count the file transfer tools, because their + * hostname specification appears as part of a more + * complicated scheme. + */ + + if ((cmdline_tooltype & TOOLTYPE_HOST_ARG) && + !seen_hostname_argument && + (!(cmdline_tooltype & TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD) || + !loaded_session || !conf_launchable(conf))) { + /* + * Treat this argument as a host name, if we have not yet + * seen a host name argument or -load. + * + * Exception, in some tools (Plink): if we have seen -load + * but it didn't create a launchable session, then we + * still accept a hostname argument following that -load. + * This allows you to make saved sessions that configure + * lots of other stuff (colour schemes, terminal settings + * etc) and then say 'putty -load sessionname hostname'. + * + * Also, we carefully _don't_ test conf for launchability + * if we haven't been explicitly told to load a session + * (otherwise saving a host name into Default Settings + * would cause 'putty' on its own to immediately launch + * the default session and never be able to do anything + * else). + */ + if (!strncmp(p, "telnet:", 7)) { + /* + * If the argument starts with "telnet:", set the + * protocol to Telnet and process the string as a + * Telnet URL. + */ + + /* + * Skip the "telnet:" or "telnet://" prefix. + */ + p += 7; + if (p[0] == '/' && p[1] == '/') + p += 2; + conf_set_int(conf, CONF_protocol, PROT_TELNET); + + /* + * The next thing we expect is a host name. + */ + { + const char *host = p; + char *buf; + + p += host_strcspn(p, ":/"); + buf = dupprintf("%.*s", (int)(p - host), host); + conf_set_str(conf, CONF_host, buf); + sfree(buf); + seen_hostname_argument = TRUE; + } + + /* + * If the host name is followed by a colon, then + * expect a port number after it. + */ + if (*p == ':') { + p++; + + conf_set_int(conf, CONF_port, atoi(p)); + /* + * Set the flag that will stop us from treating + * the next argument as a separate port; this one + * counts as explicitly provided. + */ + seen_port_argument = TRUE; + } else { + conf_set_int(conf, CONF_port, -1); + } + } else { + char *user = NULL, *hostname = NULL; + const char *hostname_after_user; + int port_override = -1; + size_t len; + + /* + * Otherwise, treat it as a bare host name. + */ + + if (cmdline_tooltype & TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX) { + /* + * Here Plink checks for a comma-separated + * protocol prefix, e.g. 'ssh,hostname' or + * 'ssh,user@hostname'. + * + * I'm not entirely sure why; this behaviour dates + * from 2000 and isn't explained. But I _think_ it + * has to do with CVS transport or similar use + * cases, in which the end user invokes the SSH + * client indirectly, via some means that only + * lets them pass a single string argument, and it + * was occasionally useful to shoehorn the choice + * of protocol into that argument. + */ + const char *comma = strchr(p, ','); + if (comma) { + char *prefix = dupprintf("%.*s", (int)(comma - p), p); + const Backend *b = backend_from_name(prefix); + + if (b) { + default_protocol = b->protocol; + conf_set_int(conf, CONF_protocol, + default_protocol); + port_override = b->default_port; + } else { + cmdline_error("unrecognised protocol prefix '%s'", + prefix); + } + + sfree(prefix); + p = comma + 1; + } + } + + hostname_after_user = p; + if (cmdline_tooltype & TOOLTYPE_HOST_ARG_CAN_BE_SESSION) { + /* + * If the hostname argument can also be a saved + * session (see below), then here we also check + * for a user@ prefix, which will override the + * username from the saved session. + * + * (If the hostname argument _isn't_ a saved + * session, we don't do this.) + */ + const char *at = strrchr(p, '@'); + if (at) { + user = dupprintf("%.*s", (int)(at - p), p); + hostname_after_user = at + 1; + } + } + + /* + * Write the whole hostname argument (minus only that + * optional protocol prefix) into the existing Conf, + * for tools that don't treat it as a saved session + * and as a fallback for those that do. + */ + hostname = dupstr(p + strspn(p, " \t")); + len = strlen(hostname); + while (len > 0 && (hostname[len-1] == ' ' || + hostname[len-1] == '\t')) + hostname[--len] = '\0'; + seen_hostname_argument = TRUE; + conf_set_str(conf, CONF_host, hostname); + + if ((cmdline_tooltype & TOOLTYPE_HOST_ARG_CAN_BE_SESSION) && + !loaded_session) { + /* + * For some tools, we equivocate between a + * hostname argument and an argument naming a + * saved session. Here we attempt to load a + * session with the specified name, and if that + * succeeds, we overwrite the entire Conf with it. + * + * We skip this check if a -load option has + * already happened, so that + * + * plink -load non-launchable-session hostname + * + * will treat 'hostname' as a hostname _even_ if a + * saved session called 'hostname' exists. (This + * doesn't lose any functionality someone could + * have needed, because if 'hostname' did cause a + * session to be loaded, then it would overwrite + * everything from the previously loaded session. + * So if that was the behaviour someone wanted, + * then they could get it by leaving off the + * -load completely.) + */ + Conf *conf2 = conf_new(); + do_defaults(hostname_after_user, conf2); + if (conf_launchable(conf2)) { + conf_copy_into(conf, conf2); + loaded_session = TRUE; + /* And override the username if one was given. */ + if (user) + conf_set_str(conf, CONF_username, user); + } + conf_free(conf2); + } + + sfree(hostname); + sfree(user); + + if (port_override >= 0) + conf_set_int(conf, CONF_port, port_override); + } + + return 1; + } else if ((cmdline_tooltype & TOOLTYPE_PORT_ARG) && + !seen_port_argument) { + /* + * If we've already got a host name from the command line + * (either as a hostname argument or a qualifying -load), + * but not a port number, then treat the next argument as + * a port number. + * + * We handle this by calling ourself recursively to + * pretend we received a -P argument, so that it will be + * deferred until it's a good moment to run it. + */ + char *dup = dupstr(p); /* 'value' is not a const char * */ + int retd = cmdline_process_param("-P", dup, 1, conf); + sfree(dup); + assert(retd == 2); + seen_port_argument = TRUE; + return 1; + } else { + /* + * Refuse to recognise this argument, and give it back to + * the tool's own command-line processing. + */ + return 0; + } + } + if (!strcmp(p, "-load")) { RETURN(2); /* This parameter must be processed immediately rather than being @@ -648,3 +879,35 @@ void cmdline_run_saved(Conf *conf) saves[pri].nsaved = 0; } } + +int cmdline_host_ok(Conf *conf) +{ + /* + * Return TRUE if the command-line arguments we've processed in + * TOOLTYPE_HOST_ARG mode are sufficient to justify launching a + * session. + */ + assert(cmdline_tooltype & TOOLTYPE_HOST_ARG); + + /* + * Of course, if we _can't_ launch a session, the answer is + * clearly no. + */ + if (!conf_launchable(conf)) + return FALSE; + + /* + * But also, if we haven't seen either a -load option or a + * hostname argument, i.e. the only saved settings we've loaded + * are Default Settings plus any non-hostname-based stuff from the + * command line, then the answer is still no, _even_ if this Conf + * is launchable. Otherwise, if you saved your favourite hostname + * into Default Settings, then just running 'putty' without + * arguments would connect to it without ever offering you the + * option to connect to something else or change the setting. + */ + if (!seen_hostname_argument && !loaded_session) + return FALSE; + + return TRUE; +} diff --git a/putty.h b/putty.h index 3ca82b62..97ef7130 100644 --- a/putty.h +++ b/putty.h @@ -1350,8 +1350,14 @@ int cmdline_process_param(const char *, char *, int, Conf *); void cmdline_run_saved(Conf *); void cmdline_cleanup(void); int cmdline_get_passwd_input(prompts_t *p, const unsigned char *in, int inlen); +int cmdline_host_ok(Conf *); #define TOOLTYPE_FILETRANSFER 1 #define TOOLTYPE_NONNETWORK 2 +#define TOOLTYPE_HOST_ARG 4 +#define TOOLTYPE_HOST_ARG_CAN_BE_SESSION 8 +#define TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX 16 +#define TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD 32 +#define TOOLTYPE_PORT_ARG 64 extern int cmdline_tooltype; void cmdline_error(const char *, ...); diff --git a/unix/gtkmain.c b/unix/gtkmain.c index 9860cd79..bd9a8e31 100644 --- a/unix/gtkmain.c +++ b/unix/gtkmain.c @@ -315,8 +315,7 @@ void window_setup_error(const char *errmsg) exit(1); } -int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, - Conf *conf) +int do_cmdline(int argc, char **argv, int do_everything, Conf *conf) { int err = 0; char *val; @@ -536,10 +535,13 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch, pgp_fingerprints(); exit(1); - } else if(p[0] != '-' && (!do_everything || - process_nonoption_arg(p, conf, - allow_launch))) { - /* do nothing */ + } else if (p[0] != '-') { + /* Non-option arguments not handled by cmdline.c are errors. */ + if (do_everything) { + err = 1; + fprintf(stderr, "%s: unexpected non-option argument '%s'\n", + appname, p); + } } else { err = 1; @@ -636,22 +638,15 @@ int main(int argc, char **argv) assert(!dup_check_launchable || conf_launchable(conf)); need_config_box = FALSE; } else { - /* 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; - if (do_cmdline(argc, argv, 0, &allow_launch, conf)) + if (do_cmdline(argc, argv, 0, conf)) exit(1); /* pre-defaults pass to get -class */ do_defaults(NULL, conf); - if (do_cmdline(argc, argv, 1, &allow_launch, conf)) + if (do_cmdline(argc, argv, 1, conf)) exit(1); /* post-defaults, do everything */ cmdline_run_saved(conf); - if (loaded_session) - allow_launch = TRUE; - - need_config_box = (!allow_launch || !conf_launchable(conf)); + need_config_box = !cmdline_host_ok(conf); } if (need_config_box) { diff --git a/unix/unix.h b/unix/unix.h index 2516bf3e..bedb28d0 100644 --- a/unix/unix.h +++ b/unix/unix.h @@ -194,7 +194,6 @@ GtkWidget *create_message_box( /* Things pterm.c needs from {ptermm,uxputty}.c */ char *make_default_wintitle(char *hostname); -int process_nonoption_arg(const char *arg, Conf *conf, int *allow_launch); /* pterm.c needs this special function in xkeysym.c */ int keysym_to_unicode(int keysym); diff --git a/unix/uxplink.c b/unix/uxplink.c index ebf14458..93c12b22 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -597,14 +597,12 @@ const int buildinfo_gtk_relevant = FALSE; int main(int argc, char **argv) { int sending; - int portnumber = -1; int *fdlist; int fd; int i, fdcount, fdsize, fdstate; int exitcode; int errors; int use_subsystem = 0; - int got_host = FALSE; int just_test_share_exists = FALSE; unsigned long now; struct winsize size; @@ -623,6 +621,11 @@ int main(int argc, char **argv) outgoingeof = EOF_NO; flags = FLAG_STDERR | FLAG_STDERR_TTY; + cmdline_tooltype |= + (TOOLTYPE_HOST_ARG | + TOOLTYPE_HOST_ARG_CAN_BE_SESSION | + TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX | + TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD); stderr_tty_init(); /* @@ -651,175 +654,81 @@ 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(); - exit(0); - } else if (!strcmp(p, "-pgpfp")) { - pgp_fingerprints(); - exit(1); - } else if (!strcmp(p, "-o")) { - if (argc <= 1) { - fprintf(stderr, - "plink: option \"-o\" requires an argument\n"); - errors = 1; - } else { - --argc; - provide_xrm_string(*++argv); - } - } else if (!strcmp(p, "-shareexists")) { - just_test_share_exists = TRUE; - } else if (!strcmp(p, "-fuzznet")) { - conf_set_int(conf, CONF_proxy_type, PROXY_FUZZ); - conf_set_str(conf, CONF_proxy_telnet_command, - "%host"); - } 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; + 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(); + exit(0); + } else if (!strcmp(p, "-pgpfp")) { + pgp_fingerprints(); + exit(1); + } else if (!strcmp(p, "-o")) { + if (argc <= 1) { + fprintf(stderr, + "plink: option \"-o\" requires an argument\n"); + errors = 1; + } else { + --argc; + provide_xrm_string(*++argv); + } + } else if (!strcmp(p, "-shareexists")) { + just_test_share_exists = TRUE; + } else if (!strcmp(p, "-fuzznet")) { + conf_set_int(conf, CONF_proxy_type, PROXY_FUZZ); + conf_set_str(conf, CONF_proxy_telnet_command, "%host"); + } else if (*p != '-') { + char *command; + int cmdlen, cmdsize; + cmdlen = cmdsize = 0; + command = NULL; - /* - * 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; + 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 */ - 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; - } - - /* - * 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(); } @@ -864,12 +773,6 @@ int main(int argc, char **argv) return 1; } - /* - * Select port. - */ - if (portnumber != -1) - conf_set_int(conf, CONF_port, portnumber); - /* * Block SIGPIPE, so that we'll get EPIPE individually on * particular network connections that go wrong. diff --git a/unix/uxpterm.c b/unix/uxpterm.c index 687974a2..d80c3780 100644 --- a/unix/uxpterm.c +++ b/unix/uxpterm.c @@ -35,11 +35,6 @@ void cleanup_exit(int code) exit(code); } -int process_nonoption_arg(const char *arg, Conf *conf, int *allow_launch) -{ - return 0; /* pterm doesn't have any. */ -} - char *make_default_wintitle(char *hostname) { return dupstr("pterm"); diff --git a/unix/uxputty.c b/unix/uxputty.c index c982e224..fb280187 100644 --- a/unix/uxputty.c +++ b/unix/uxputty.c @@ -51,69 +51,9 @@ void initial_config_box(Conf *conf, post_dialog_fn_t after, void *afterctx) sfree(title); } -static int got_host = 0; - const int use_event_log = 1, new_session = 1, saved_sessions = 1; const int dup_check_launchable = 1; -int process_nonoption_arg(const char *arg, Conf *conf, int *allow_launch) -{ - char *argdup, *p, *q; - argdup = dupstr(arg); - q = argdup; - - 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", argdup, 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. - */ - p = argdup; - while (*p && !isspace((unsigned char)*p)) - p++; - if (*p) - *p++ = '\0'; - conf_set_str(conf, CONF_host, q); - got_host = 1; - } - if (got_host) - *allow_launch = TRUE; - - sfree(argdup); - - return 1; -} - char *make_default_wintitle(char *hostname) { return dupcat(hostname, " - ", appname, NULL); @@ -139,6 +79,7 @@ void setup(int single) { sk_init(); flags = FLAG_VERBOSE | FLAG_INTERACTIVE; + cmdline_tooltype |= TOOLTYPE_HOST_ARG | TOOLTYPE_PORT_ARG; default_protocol = be_default_protocol; /* Find the appropriate default port. */ { diff --git a/windows/window.c b/windows/window.c index 5a7040c4..e5bd5b7f 100644 --- a/windows/window.c +++ b/windows/window.c @@ -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); diff --git a/windows/winplink.c b/windows/winplink.c index 959db5b0..a6b518ec 100644 --- a/windows/winplink.c +++ b/windows/winplink.c @@ -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");