From 74150633f199321d095601b25532a138e2334e12 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 24 Sep 2024 17:50:19 +0100 Subject: [PATCH] Add and use cmdline_arg_to_filename(). Converting a CmdlineArg straight to a Filename allows us to make the filename out of the wide-character version of the string on Windows. So now filenames specified on the command line should generally be able to handle pathnames containing Unicode characters not in the system code page. This change also involves making some char pointers _into_ Filename structs where they weren't previously: for example, the 'openssh_config_file' variable in Windows Pageant's WinMain(). --- cmdline.c | 25 ++++++++++++------------- putty.h | 1 + unix/main-gtk-simple.c | 3 +-- unix/utils/cmdline_arg.c | 9 +++++++++ windows/dialog.c | 2 +- windows/pageant.c | 18 +++++++++++------- windows/platform.h | 2 +- windows/putty.c | 8 ++++---- windows/puttygen.c | 12 +++++------- windows/test/test_screenshot.c | 5 +++-- windows/utils/cmdline_arg.c | 9 +++++++++ windows/utils/screenshot.c | 8 ++++---- 12 files changed, 61 insertions(+), 41 deletions(-) diff --git a/cmdline.c b/cmdline.c index 4a84fa1c..5e507af6 100644 --- a/cmdline.c +++ b/cmdline.c @@ -563,20 +563,23 @@ int cmdline_process_param(CmdlineArg *arg, CmdlineArg *nextarg, sfree(host); } if (!strcmp(p, "-m")) { - const char *filename; + Filename *filename; FILE *fp; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); - filename = value; + filename = cmdline_arg_to_filename(nextarg); - fp = fopen(filename, "r"); + fp = f_open(filename, "r", false); if (!fp) { - cmdline_error("unable to open command file \"%s\"", filename); + cmdline_error("unable to open command file \"%s\"", + filename_to_str(filename)); + filename_free(filename); return ret; } + filename_free(filename); strbuf *command = strbuf_new(); char readbuf[4096]; while (1) { @@ -628,7 +631,7 @@ int cmdline_process_param(CmdlineArg *arg, CmdlineArg *nextarg, cmdline_error("the -pwfile option can only be used with the " "SSH protocol"); else { - Filename *fn = filename_from_str(value); + Filename *fn = cmdline_arg_to_filename(nextarg); FILE *fp = f_open(fn, "r", false); if (!fp) { cmdline_error("unable to open password file '%s'", value); @@ -750,21 +753,19 @@ int cmdline_process_param(CmdlineArg *arg, CmdlineArg *nextarg, } if (!strcmp(p, "-i")) { - Filename *fn; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - fn = filename_from_str(value); + Filename *fn = cmdline_arg_to_filename(nextarg); conf_set_filename(conf, CONF_keyfile, fn); filename_free(fn); } if (!strcmp(p, "-cert")) { - Filename *fn; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - fn = filename_from_str(value); + Filename *fn = cmdline_arg_to_filename(nextarg); conf_set_filename(conf, CONF_detached_cert, fn); filename_free(fn); } @@ -868,12 +869,11 @@ int cmdline_process_param(CmdlineArg *arg, CmdlineArg *nextarg, } if (!strcmp(p, "-sessionlog")) { - Filename *fn; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER); /* but available even in TOOLTYPE_NONNETWORK, cf pterm "-log" */ SAVEABLE(0); - fn = filename_from_str(value); + Filename *fn = cmdline_arg_to_filename(nextarg); conf_set_filename(conf, CONF_logfilename, fn); conf_set_int(conf, CONF_logtype, LGTYP_DEBUG); filename_free(fn); @@ -881,11 +881,10 @@ int cmdline_process_param(CmdlineArg *arg, CmdlineArg *nextarg, if (!strcmp(p, "-sshlog") || !strcmp(p, "-sshrawlog")) { - Filename *fn; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); - fn = filename_from_str(value); + Filename *fn = cmdline_arg_to_filename(nextarg); conf_set_filename(conf, CONF_logfilename, fn); conf_set_int(conf, CONF_logtype, !strcmp(p, "-sshlog") ? LGTYP_PACKETS : diff --git a/putty.h b/putty.h index 97ecb60f..df843c47 100644 --- a/putty.h +++ b/putty.h @@ -2436,6 +2436,7 @@ struct CmdlineArg { }; const char *cmdline_arg_to_utf8(CmdlineArg *arg); /* may fail */ const char *cmdline_arg_to_str(CmdlineArg *arg); /* must not fail */ +Filename *cmdline_arg_to_filename(CmdlineArg *arg); /* caller must free */ void cmdline_arg_wipe(CmdlineArg *arg); CmdlineArg *cmdline_arg_from_str(CmdlineArgList *list, const char *string); /* Platforms provide their own constructors for CmdlineArgList */ diff --git a/unix/main-gtk-simple.c b/unix/main-gtk-simple.c index 4ffd9ecf..76487772 100644 --- a/unix/main-gtk-simple.c +++ b/unix/main-gtk-simple.c @@ -476,10 +476,9 @@ bool do_cmdline(int argc, char **argv, bool do_everything, Conf *conf) conf_set_str(conf, CONF_wintitle, val); } else if (!strcmp(p, "-log")) { - Filename *fn; EXPECTS_ARG; SECOND_PASS_ONLY; - fn = filename_from_str(val); + Filename *fn = cmdline_arg_to_filename(nextarg); conf_set_filename(conf, CONF_logfilename, fn); conf_set_int(conf, CONF_logtype, LGTYP_DEBUG); filename_free(fn); diff --git a/unix/utils/cmdline_arg.c b/unix/utils/cmdline_arg.c index a33df45e..e8a7a654 100644 --- a/unix/utils/cmdline_arg.c +++ b/unix/utils/cmdline_arg.c @@ -124,6 +124,15 @@ const char *cmdline_arg_to_utf8(CmdlineArg *argp) return NULL; } +Filename *cmdline_arg_to_filename(CmdlineArg *argp) +{ + if (!argp) + return NULL; + + CmdlineArgUnix *arg = container_of(argp, CmdlineArgUnix, argp); + return filename_from_str(arg->value); +} + void cmdline_arg_wipe(CmdlineArg *argp) { if (!argp) diff --git a/windows/dialog.c b/windows/dialog.c index e7faf6df..96dab3ab 100644 --- a/windows/dialog.c +++ b/windows/dialog.c @@ -459,7 +459,7 @@ static HTREEITEM treeview_insert(struct treeview_faff *faff, return newitem; } -const char *dialog_box_demo_screenshot_filename = NULL; +Filename *dialog_box_demo_screenshot_filename = NULL; /* ctrltrees indices for the main dialog box */ enum { diff --git a/windows/pageant.c b/windows/pageant.c index 1f71bd6d..f30d9cdc 100644 --- a/windows/pageant.c +++ b/windows/pageant.c @@ -1537,7 +1537,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) const char *command = NULL; const char *unixsocket = NULL; bool show_keylist_on_startup = false; - const char *openssh_config_file = NULL; + Filename *openssh_config_file = NULL; typedef struct CommandLineKey { Filename *fn; @@ -1612,7 +1612,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) */ sgrowarray(clkeys, clkeysize, nclkeys); CommandLineKey *clkey = &clkeys[nclkeys++]; - clkey->fn = filename_from_str(cmdline_arg_to_str(valarg)); + clkey->fn = cmdline_arg_to_filename(valarg); clkey->add_encrypted = add_keys_encrypted; } else if (match_opt("-pgpfp")) { pgp_fingerprints_msgbox(NULL); @@ -1628,8 +1628,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) } else if (match_opt("-keylist")) { show_keylist_on_startup = true; } else if (match_optval("-openssh-config", "-openssh_config")) { - openssh_config_file = cmdline_arg_to_str(valarg); + openssh_config_file = cmdline_arg_to_filename(valarg); } else if (match_optval("-unix")) { + /* UNICODE: should this be a Unicode filename? Is there a + * Unicode version of connect() that lets you give a + * Unicode pathname when making an AF_UNIX socket? */ unixsocket = cmdline_arg_to_str(valarg); } else if (match_opt("-c")) { /* @@ -1735,10 +1738,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) * pointing at the named pipe, do so. */ if (openssh_config_file) { - FILE *fp = fopen(openssh_config_file, "w"); + FILE *fp = f_open(openssh_config_file, "w", true); if (!fp) { - char *err = dupprintf("Unable to write OpenSSH config " - "file to %s", openssh_config_file); + char *err = dupprintf( + "Unable to write OpenSSH config file to %s", + filename_to_str(openssh_config_file)); MessageBox(NULL, err, "Pageant Error", MB_ICONERROR | MB_OK); return 1; @@ -1960,7 +1964,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) * Leave this file around, but empty it, so that it doesn't * refer to a pipe we aren't listening on any more. */ - FILE *fp = fopen(openssh_config_file, "w"); + FILE *fp = f_open(openssh_config_file, "w", true); if (fp) fclose(fp); } diff --git a/windows/platform.h b/windows/platform.h index 034b3c28..7e2227b7 100644 --- a/windows/platform.h +++ b/windows/platform.h @@ -859,7 +859,7 @@ bool aux_match_opt(AuxMatchOpt *amo, CmdlineArg **val, const char *optname, ...); bool aux_match_done(AuxMatchOpt *amo); -char *save_screenshot(HWND hwnd, const char *outfile); +char *save_screenshot(HWND hwnd, Filename *outfile); void gui_terminal_ready(HWND hwnd, Seat *seat, Backend *backend); void setup_gui_timing(void); diff --git a/windows/putty.c b/windows/putty.c index 299a381f..eb1386e8 100644 --- a/windows/putty.c +++ b/windows/putty.c @@ -2,9 +2,9 @@ #include "storage.h" extern bool sesslist_demo_mode; -extern const char *dialog_box_demo_screenshot_filename; +extern Filename *dialog_box_demo_screenshot_filename; static strbuf *demo_terminal_data = NULL; -static const char *terminal_demo_screenshot_filename; +static Filename *terminal_demo_screenshot_filename; const unsigned cmdline_tooltype = TOOLTYPE_HOST_ARG | @@ -99,7 +99,7 @@ void gui_term_process_cmdline(Conf *conf, char *cmdline) } else { demo_config_box = true; dialog_box_demo_screenshot_filename = - cmdline_arg_to_str(arglist->args[arglistpos++]); + cmdline_arg_to_filename(arglist->args[arglistpos++]); } } else if (!strcmp(p, "-demo-terminal")) { if (!arglist->args[arglistpos] || @@ -109,7 +109,7 @@ void gui_term_process_cmdline(Conf *conf, char *cmdline) const char *infile = cmdline_arg_to_str(arglist->args[arglistpos++]); terminal_demo_screenshot_filename = - cmdline_arg_to_str(arglist->args[arglistpos++]); + cmdline_arg_to_filename(arglist->args[arglistpos++]); FILE *fp = fopen(infile, "rb"); if (!fp) cmdline_error("can't open input file '%s'", infile); diff --git a/windows/puttygen.c b/windows/puttygen.c index 51574139..643e1113 100644 --- a/windows/puttygen.c +++ b/windows/puttygen.c @@ -26,9 +26,9 @@ #define DEFAULT_ECCURVE_INDEX 0 #define DEFAULT_EDCURVE_INDEX 0 -static const char *cmdline_keyfile = NULL; +static Filename *cmdline_keyfile = NULL; static ptrlen cmdline_demo_keystr; -static const char *demo_screenshot_filename = NULL; +static Filename *demo_screenshot_filename = NULL; /* * Print a modal (Really Bad) message box and perform a fatal exit. @@ -1737,9 +1737,7 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, * Load a key file if one was provided on the command line. */ if (cmdline_keyfile) { - Filename *fn = filename_from_str(cmdline_keyfile); - load_key_file(hwnd, state, fn, false); - filename_free(fn); + load_key_file(hwnd, state, cmdline_keyfile, false); } else if (cmdline_demo_keystr.ptr) { BinarySource src[1]; BinarySource_BARE_INIT_PL(src, cmdline_demo_keystr); @@ -2430,7 +2428,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) * Assume the first argument to be a private key file, and * attempt to load it. */ - cmdline_keyfile = cmdline_arg_to_str(valarg); + cmdline_keyfile = cmdline_arg_to_filename(valarg); continue; } else { opt_error("unexpected extra argument '%s'\n", @@ -2550,7 +2548,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) } sfree(val); } else if (match_optval("-demo-screenshot")) { - demo_screenshot_filename = cmdline_arg_to_str(valarg); + demo_screenshot_filename = cmdline_arg_to_filename(valarg); cmdline_demo_keystr = PTRLEN_LITERAL( "PuTTY-User-Key-File-3: ssh-ed25519\n" "Encryption: none\n" diff --git a/windows/test/test_screenshot.c b/windows/test/test_screenshot.c index 3d558bc8..01643f4e 100644 --- a/windows/test/test_screenshot.c +++ b/windows/test/test_screenshot.c @@ -15,7 +15,7 @@ void out_of_memory(void) { fatal_error("out of memory"); } int main(int argc, char **argv) { - const char *outfile = NULL; + Filename *outfile = NULL; AuxMatchOpt amo = aux_match_opt_init(fatal_error); while (!aux_match_done(&amo)) { @@ -28,7 +28,7 @@ int main(int argc, char **argv) if (aux_match_arg(&amo, &val)) { fatal_error("unexpected argument '%s'", cmdline_arg_to_str(val)); } else if (match_optval("-o", "--output")) { - outfile = cmdline_arg_to_str(val); + outfile = cmdline_arg_to_filename(val); } else { fatal_error("unrecognised option '%s'\n", cmdline_arg_to_str(amo.arglist->args[amo.index])); @@ -41,6 +41,7 @@ int main(int argc, char **argv) char *err = save_screenshot(NULL, outfile); if (err) fatal_error("%s", err); + filename_free(outfile); return 0; } diff --git a/windows/utils/cmdline_arg.c b/windows/utils/cmdline_arg.c index 55c105f1..cb232361 100644 --- a/windows/utils/cmdline_arg.c +++ b/windows/utils/cmdline_arg.c @@ -162,6 +162,15 @@ const char *cmdline_arg_to_utf8(CmdlineArg *argp) return arg->utf8; } +Filename *cmdline_arg_to_filename(CmdlineArg *argp) +{ + if (!argp) + return NULL; + + CmdlineArgWin *arg = container_of(argp, CmdlineArgWin, argp); + return filename_from_wstr(arg->wide); +} + void cmdline_arg_wipe(CmdlineArg *argp) { if (!argp) diff --git a/windows/utils/screenshot.c b/windows/utils/screenshot.c index 777520fd..6b53cd70 100644 --- a/windows/utils/screenshot.c +++ b/windows/utils/screenshot.c @@ -4,7 +4,7 @@ #include -char *save_screenshot(HWND hwnd, const char *outfile) +char *save_screenshot(HWND hwnd, Filename *outfile) { HDC dcWindow = NULL, dcSave = NULL; HBITMAP bmSave = NULL; @@ -89,9 +89,9 @@ char *save_screenshot(HWND hwnd, const char *outfile) err = dupprintf("GetDIBits (get data): %s", win_strerror(GetLastError())); - FILE *fp = fopen(outfile, "wb"); + FILE *fp = f_open(outfile, "wb", false); if (!fp) { - err = dupprintf("'%s': unable to open file", outfile); + err = dupprintf("'%s': unable to open file", filename_to_str(outfile)); goto out; } @@ -118,7 +118,7 @@ char *save_screenshot(HWND hwnd, const char *outfile) #else /* HAVE_DWMAPI_H */ /* Without we can't get the right window rectangle */ -char *save_screenshot(HWND hwnd, const char *outfile) +char *save_screenshot(HWND hwnd, Filename *outfile) { return dupstr("Demo screenshots not compiled in to this build"); }