1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-03-31 10:42:50 -05:00

New Seat method, seat_nonfatal().

This is like the seat-independent nonfatal(), but specifies a Seat,
which allows the GUI dialog box to have the right terminal window as
its parent (if there are multiple ones).

Changed over all the nonfatal() calls in the code base that could be
localised to a Seat, which means all the ones that come up if
something goes horribly wrong in host key storage. To make that
possible, I've added a 'seat' parameter to store_host_key(); it turns
out that all its call sites had one available already.
This commit is contained in:
Simon Tatham 2022-09-13 08:49:38 +01:00
parent c674b2da4f
commit 4249b39ed3
24 changed files with 104 additions and 19 deletions

View File

@ -79,6 +79,11 @@ void console_connection_fatal(Seat *seat, const char *msg)
cleanup_exit(1); cleanup_exit(1);
} }
void console_nonfatal(Seat *seat, const char *msg)
{
console_print_error_msg("ERROR", msg);
}
/* /*
* Console front ends redo their select() or equivalent every time, so * Console front ends redo their select() or equivalent every time, so
* they don't need separate timer handling. * they don't need separate timer handling.

View File

@ -405,6 +405,14 @@ static void sshproxy_connection_fatal(Seat *seat, const char *message)
} }
} }
static void sshproxy_nonfatal(Seat *seat, const char *message)
{
SshProxy *sp = container_of(seat, SshProxy, seat);
if (sp->clientseat)
seat_nonfatal(sp->clientseat, "error in proxy SSH connection: %s",
message);
}
static SeatPromptResult sshproxy_confirm_ssh_host_key( static SeatPromptResult sshproxy_confirm_ssh_host_key(
Seat *seat, const char *host, int port, const char *keytype, Seat *seat, const char *host, int port, const char *keytype,
char *keystr, SeatDialogText *text, HelpCtx helpctx, char *keystr, SeatDialogText *text, HelpCtx helpctx,
@ -541,6 +549,7 @@ static const SeatVtable SshProxy_seat_vt = {
.notify_remote_exit = nullseat_notify_remote_exit, .notify_remote_exit = nullseat_notify_remote_exit,
.notify_remote_disconnect = sshproxy_notify_remote_disconnect, .notify_remote_disconnect = sshproxy_notify_remote_disconnect,
.connection_fatal = sshproxy_connection_fatal, .connection_fatal = sshproxy_connection_fatal,
.nonfatal = sshproxy_nonfatal,
.update_specials_menu = nullseat_update_specials_menu, .update_specials_menu = nullseat_update_specials_menu,
.get_ttymode = nullseat_get_ttymode, .get_ttymode = nullseat_get_ttymode,
.set_busy_status = nullseat_set_busy_status, .set_busy_status = nullseat_set_busy_status,

1
pscp.c
View File

@ -71,6 +71,7 @@ static const SeatVtable pscp_seat_vt = {
.notify_remote_exit = nullseat_notify_remote_exit, .notify_remote_exit = nullseat_notify_remote_exit,
.notify_remote_disconnect = nullseat_notify_remote_disconnect, .notify_remote_disconnect = nullseat_notify_remote_disconnect,
.connection_fatal = console_connection_fatal, .connection_fatal = console_connection_fatal,
.nonfatal = console_nonfatal,
.update_specials_menu = nullseat_update_specials_menu, .update_specials_menu = nullseat_update_specials_menu,
.get_ttymode = nullseat_get_ttymode, .get_ttymode = nullseat_get_ttymode,
.set_busy_status = nullseat_set_busy_status, .set_busy_status = nullseat_set_busy_status,

View File

@ -52,6 +52,7 @@ static const SeatVtable psftp_seat_vt = {
.notify_remote_exit = nullseat_notify_remote_exit, .notify_remote_exit = nullseat_notify_remote_exit,
.notify_remote_disconnect = nullseat_notify_remote_disconnect, .notify_remote_disconnect = nullseat_notify_remote_disconnect,
.connection_fatal = console_connection_fatal, .connection_fatal = console_connection_fatal,
.nonfatal = console_nonfatal,
.update_specials_menu = nullseat_update_specials_menu, .update_specials_menu = nullseat_update_specials_menu,
.get_ttymode = nullseat_get_ttymode, .get_ttymode = nullseat_get_ttymode,
.set_busy_status = nullseat_set_busy_status, .set_busy_status = nullseat_set_busy_status,

View File

@ -529,7 +529,7 @@ int check_stored_host_key(const char *hostname, int port,
unreachable("host keys not handled in this tool"); unreachable("host keys not handled in this tool");
} }
void store_host_key(const char *hostname, int port, void store_host_key(Seat *seat, const char *hostname, int port,
const char *keytype, const char *key) const char *keytype, const char *key)
{ {
unreachable("host keys not handled in this tool"); unreachable("host keys not handled in this tool");

17
putty.h
View File

@ -1214,9 +1214,15 @@ struct SeatVtable {
void (*notify_remote_disconnect)(Seat *seat); void (*notify_remote_disconnect)(Seat *seat);
/* /*
* Notify the seat that the connection has suffered a fatal error. * Notify the seat that the connection has suffered an error,
* either fatal to the whole connection or not.
*
* The latter kind of error is expected to be things along the
* lines of 'I/O error storing the new host key', which has
* traditionally been presented via a dialog box or similar.
*/ */
void (*connection_fatal)(Seat *seat, const char *message); void (*connection_fatal)(Seat *seat, const char *message);
void (*nonfatal)(Seat *seat, const char *message);
/* /*
* Notify the seat that the list of special commands available * Notify the seat that the list of special commands available
@ -1481,10 +1487,11 @@ static inline bool seat_interactive(Seat *seat)
static inline bool seat_get_cursor_position(Seat *seat, int *x, int *y) static inline bool seat_get_cursor_position(Seat *seat, int *x, int *y)
{ return seat->vt->get_cursor_position(seat, x, y); } { return seat->vt->get_cursor_position(seat, x, y); }
/* Unlike the seat's actual method, the public entry point /* Unlike the seat's actual method, the public entry points
* seat_connection_fatal is a wrapper function with a printf-like API, * seat_connection_fatal and seat_nonfatal are wrapper functions with
* defined in utils. */ * a printf-like API, defined in utils. */
void seat_connection_fatal(Seat *seat, const char *fmt, ...) PRINTF_LIKE(2, 3); void seat_connection_fatal(Seat *seat, const char *fmt, ...) PRINTF_LIKE(2, 3);
void seat_nonfatal(Seat *seat, const char *fmt, ...) PRINTF_LIKE(2, 3);
/* Handy aliases for seat_output which set is_stderr to a fixed value. */ /* Handy aliases for seat_output which set is_stderr to a fixed value. */
static inline size_t seat_stdout(Seat *seat, const void *data, size_t len) static inline size_t seat_stdout(Seat *seat, const void *data, size_t len)
@ -1528,6 +1535,7 @@ void nullseat_notify_session_started(Seat *seat);
void nullseat_notify_remote_exit(Seat *seat); void nullseat_notify_remote_exit(Seat *seat);
void nullseat_notify_remote_disconnect(Seat *seat); void nullseat_notify_remote_disconnect(Seat *seat);
void nullseat_connection_fatal(Seat *seat, const char *message); void nullseat_connection_fatal(Seat *seat, const char *message);
void nullseat_nonfatal(Seat *seat, const char *message);
void nullseat_update_specials_menu(Seat *seat); void nullseat_update_specials_menu(Seat *seat);
char *nullseat_get_ttymode(Seat *seat, const char *mode); char *nullseat_get_ttymode(Seat *seat, const char *mode);
void nullseat_set_busy_status(Seat *seat, BusyStatus status); void nullseat_set_busy_status(Seat *seat, BusyStatus status);
@ -1567,6 +1575,7 @@ bool nullseat_get_cursor_position(Seat *seat, int *x, int *y);
*/ */
void console_connection_fatal(Seat *seat, const char *message); void console_connection_fatal(Seat *seat, const char *message);
void console_nonfatal(Seat *seat, const char *message);
SeatPromptResult console_confirm_ssh_host_key( SeatPromptResult console_confirm_ssh_host_key(
Seat *seat, const char *host, int port, const char *keytype, Seat *seat, const char *host, int port, const char *keytype,
char *keystr, SeatDialogText *text, HelpCtx helpctx, char *keystr, SeatDialogText *text, HelpCtx helpctx,

View File

@ -1021,7 +1021,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
ppl_logevent("%s", fingerprint); ppl_logevent("%s", fingerprint);
sfree(fingerprint); sfree(fingerprint);
store_host_key(s->savedhost, s->savedport, store_host_key(s->ppl.seat, s->savedhost, s->savedport,
ssh_key_cache_id(s->hkey), s->keystr); ssh_key_cache_id(s->hkey), s->keystr);
/* /*
* Don't forget to store the new key as the one we'll be * Don't forget to store the new key as the one we'll be

View File

@ -116,6 +116,7 @@ static const SeatVtable server_seat_vt = {
.notify_remote_exit = nullseat_notify_remote_exit, .notify_remote_exit = nullseat_notify_remote_exit,
.notify_remote_disconnect = nullseat_notify_remote_disconnect, .notify_remote_disconnect = nullseat_notify_remote_disconnect,
.connection_fatal = nullseat_connection_fatal, .connection_fatal = nullseat_connection_fatal,
.nonfatal = nullseat_nonfatal,
.update_specials_menu = nullseat_update_specials_menu, .update_specials_menu = nullseat_update_specials_menu,
.get_ttymode = nullseat_get_ttymode, .get_ttymode = nullseat_get_ttymode,
.set_busy_status = nullseat_set_busy_status, .set_busy_status = nullseat_set_busy_status,

View File

@ -193,6 +193,7 @@ static const SeatVtable sesschan_seat_vt = {
.notify_remote_exit = sesschan_notify_remote_exit, .notify_remote_exit = sesschan_notify_remote_exit,
.notify_remote_disconnect = nullseat_notify_remote_disconnect, .notify_remote_disconnect = nullseat_notify_remote_disconnect,
.connection_fatal = sesschan_connection_fatal, .connection_fatal = sesschan_connection_fatal,
.nonfatal = nullseat_nonfatal,
.update_specials_menu = nullseat_update_specials_menu, .update_specials_menu = nullseat_update_specials_menu,
.get_ttymode = nullseat_get_ttymode, .get_ttymode = nullseat_get_ttymode,
.set_busy_status = nullseat_set_busy_status, .set_busy_status = nullseat_set_busy_status,

View File

@ -89,8 +89,10 @@ int check_stored_host_key(const char *hostname, int port,
/* /*
* Write a host key into the database, overwriting any previous * Write a host key into the database, overwriting any previous
* entry that might have been there. * entry that might have been there.
*
* A Seat is provided for error-reporting purposes.
*/ */
void store_host_key(const char *hostname, int port, void store_host_key(Seat *seat, const char *hostname, int port,
const char *keytype, const char *key); const char *keytype, const char *key);
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------

View File

@ -17,6 +17,7 @@ void nullseat_notify_session_started(Seat *seat) {}
void nullseat_notify_remote_exit(Seat *seat) {} void nullseat_notify_remote_exit(Seat *seat) {}
void nullseat_notify_remote_disconnect(Seat *seat) {} void nullseat_notify_remote_disconnect(Seat *seat) {}
void nullseat_connection_fatal(Seat *seat, const char *message) {} void nullseat_connection_fatal(Seat *seat, const char *message) {}
void nullseat_nonfatal(Seat *seat, const char *message) {}
void nullseat_update_specials_menu(Seat *seat) {} void nullseat_update_specials_menu(Seat *seat) {}
char *nullseat_get_ttymode(Seat *seat, const char *mode) { return NULL; } char *nullseat_get_ttymode(Seat *seat, const char *mode) { return NULL; }
void nullseat_set_busy_status(Seat *seat, BusyStatus status) {} void nullseat_set_busy_status(Seat *seat, BusyStatus status) {}

View File

@ -191,7 +191,7 @@ SeatPromptResult console_confirm_ssh_host_key(
if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n' && if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n' &&
line[0] != 'q' && line[0] != 'Q') { line[0] != 'q' && line[0] != 'Q') {
if (line[0] == 'y' || line[0] == 'Y') if (line[0] == 'y' || line[0] == 'Y')
store_host_key(host, port, keytype, keystr); store_host_key(seat, host, port, keytype, keystr);
postmsg(&cf); postmsg(&cf);
return SPR_OK; return SPR_OK;
} else { } else {

View File

@ -3529,7 +3529,8 @@ static void confirm_ssh_host_key_result_callback(void *vctx, int result)
* doesn't care whether we saved the host key or not). * doesn't care whether we saved the host key or not).
*/ */
if (result == 2) { if (result == 2) {
store_host_key(ctx->host, ctx->port, ctx->keytype, ctx->keystr); store_host_key(ctx->seat, ctx->host, ctx->port,
ctx->keytype, ctx->keystr);
logical_result = SPR_OK; logical_result = SPR_OK;
} else if (result == 1) { } else if (result == 1) {
logical_result = SPR_OK; logical_result = SPR_OK;

View File

@ -409,6 +409,7 @@ static const SeatVtable plink_seat_vt = {
.notify_remote_exit = nullseat_notify_remote_exit, .notify_remote_exit = nullseat_notify_remote_exit,
.notify_remote_disconnect = nullseat_notify_remote_disconnect, .notify_remote_disconnect = nullseat_notify_remote_disconnect,
.connection_fatal = console_connection_fatal, .connection_fatal = console_connection_fatal,
.nonfatal = console_nonfatal,
.update_specials_menu = nullseat_update_specials_menu, .update_specials_menu = nullseat_update_specials_menu,
.get_ttymode = plink_get_ttymode, .get_ttymode = plink_get_ttymode,
.set_busy_status = nullseat_set_busy_status, .set_busy_status = nullseat_set_busy_status,

View File

@ -828,7 +828,7 @@ bool have_ssh_host_key(const char *hostname, int port,
return check_stored_host_key(hostname, port, keytype, "") != 1; return check_stored_host_key(hostname, port, keytype, "") != 1;
} }
void store_host_key(const char *hostname, int port, void store_host_key(Seat *seat, const char *hostname, int port,
const char *keytype, const char *key) const char *keytype, const char *key)
{ {
FILE *rfp, *wfp; FILE *rfp, *wfp;
@ -846,7 +846,7 @@ void store_host_key(const char *hostname, int port,
dir = make_filename(INDEX_DIR, NULL); dir = make_filename(INDEX_DIR, NULL);
if ((errmsg = make_dir_path(dir, 0700)) != NULL) { if ((errmsg = make_dir_path(dir, 0700)) != NULL) {
nonfatal("Unable to store host key: %s", errmsg); seat_nonfatal(seat, "Unable to store host key: %s", errmsg);
sfree(errmsg); sfree(errmsg);
sfree(dir); sfree(dir);
sfree(tmpfilename); sfree(tmpfilename);
@ -857,8 +857,8 @@ void store_host_key(const char *hostname, int port,
wfp = fopen(tmpfilename, "w"); wfp = fopen(tmpfilename, "w");
} }
if (!wfp) { if (!wfp) {
nonfatal("Unable to store host key: open(\"%s\") " seat_nonfatal(seat, "Unable to store host key: open(\"%s\") "
"returned '%s'", tmpfilename, strerror(errno)); "returned '%s'", tmpfilename, strerror(errno));
sfree(tmpfilename); sfree(tmpfilename);
return; return;
} }
@ -889,9 +889,9 @@ void store_host_key(const char *hostname, int port,
fclose(wfp); fclose(wfp);
if (rename(tmpfilename, filename) < 0) { if (rename(tmpfilename, filename) < 0) {
nonfatal("Unable to store host key: rename(\"%s\",\"%s\")" seat_nonfatal(seat, "Unable to store host key: rename(\"%s\",\"%s\")"
" returned '%s'", tmpfilename, filename, " returned '%s'", tmpfilename, filename,
strerror(errno)); strerror(errno));
} }
sfree(tmpfilename); sfree(tmpfilename);

View File

@ -290,6 +290,12 @@ static void gtk_seat_connection_fatal(Seat *seat, const char *msg)
queue_toplevel_callback(connection_fatal_callback, inst); queue_toplevel_callback(connection_fatal_callback, inst);
} }
static void gtk_seat_nonfatal(Seat *seat, const char *msg)
{
GtkFrontend *inst = container_of(seat, GtkFrontend, seat);
nonfatal_message_box(inst->window, msg);
}
/* /*
* Default settings that are specific to pterm. * Default settings that are specific to pterm.
*/ */
@ -423,6 +429,7 @@ static const SeatVtable gtk_seat_vt = {
.notify_remote_exit = gtk_seat_notify_remote_exit, .notify_remote_exit = gtk_seat_notify_remote_exit,
.notify_remote_disconnect = nullseat_notify_remote_disconnect, .notify_remote_disconnect = nullseat_notify_remote_disconnect,
.connection_fatal = gtk_seat_connection_fatal, .connection_fatal = gtk_seat_connection_fatal,
.nonfatal = gtk_seat_nonfatal,
.update_specials_menu = gtk_seat_update_specials_menu, .update_specials_menu = gtk_seat_update_specials_menu,
.get_ttymode = gtk_seat_get_ttymode, .get_ttymode = gtk_seat_get_ttymode,
.set_busy_status = gtk_seat_set_busy_status, .set_busy_status = gtk_seat_set_busy_status,

View File

@ -51,6 +51,7 @@ add_sources_from_current_dir(utils
read_file_into.c read_file_into.c
seat_connection_fatal.c seat_connection_fatal.c
seat_dialog_text.c seat_dialog_text.c
seat_nonfatal.c
sessprep.c sessprep.c
sk_free_peer_info.c sk_free_peer_info.c
smemclr.c smemclr.c

19
utils/seat_nonfatal.c Normal file
View File

@ -0,0 +1,19 @@
/*
* Wrapper function for the nonfatal() method of a Seat,
* providing printf-style formatting.
*/
#include "putty.h"
void seat_nonfatal(Seat *seat, const char *fmt, ...)
{
va_list ap;
char *msg;
va_start(ap, fmt);
msg = dupvprintf(fmt, ap);
va_end(ap);
seat->vt->nonfatal(seat, msg);
sfree(msg);
}

View File

@ -288,6 +288,17 @@ static void tempseat_connection_fatal(Seat *seat, const char *message)
unreachable("connection_fatal should never be called on TempSeat"); unreachable("connection_fatal should never be called on TempSeat");
} }
static void tempseat_nonfatal(Seat *seat, const char *message)
{
/*
* Non-fatal errors specific to a Seat should also not occur,
* because those will be for things like I/O errors writing the
* host key collection, and a backend's not _doing_ that when we
* haven't connected it to the host yet.
*/
unreachable("nonfatal should never be called on TempSeat");
}
static bool tempseat_eof(Seat *seat) static bool tempseat_eof(Seat *seat)
{ {
/* /*
@ -327,6 +338,7 @@ static const struct SeatVtable tempseat_vt = {
.notify_remote_exit = tempseat_notify_remote_exit, .notify_remote_exit = tempseat_notify_remote_exit,
.notify_remote_disconnect = tempseat_notify_remote_disconnect, .notify_remote_disconnect = tempseat_notify_remote_disconnect,
.connection_fatal = tempseat_connection_fatal, .connection_fatal = tempseat_connection_fatal,
.nonfatal = tempseat_nonfatal,
.update_specials_menu = tempseat_update_specials_menu, .update_specials_menu = tempseat_update_specials_menu,
.get_ttymode = tempseat_get_ttymode, .get_ttymode = tempseat_get_ttymode,
.set_busy_status = tempseat_set_busy_status, .set_busy_status = tempseat_set_busy_status,

View File

@ -119,7 +119,7 @@ SeatPromptResult console_confirm_ssh_host_key(
if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n' && if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n' &&
line[0] != 'q' && line[0] != 'Q') { line[0] != 'q' && line[0] != 'Q') {
if (line[0] == 'y' || line[0] == 'Y') if (line[0] == 'y' || line[0] == 'Y')
store_host_key(host, port, keytype, keystr); store_host_key(seat, host, port, keytype, keystr);
return SPR_OK; return SPR_OK;
} else { } else {
fputs(console_abandoned_msg, stderr); fputs(console_abandoned_msg, stderr);

View File

@ -1155,7 +1155,7 @@ SeatPromptResult win_seat_confirm_ssh_host_key(
wgs->term_hwnd, HostKeyDialogProc, ctx); wgs->term_hwnd, HostKeyDialogProc, ctx);
assert(mbret==IDC_HK_ACCEPT || mbret==IDC_HK_ONCE || mbret==IDCANCEL); assert(mbret==IDC_HK_ACCEPT || mbret==IDC_HK_ONCE || mbret==IDCANCEL);
if (mbret == IDC_HK_ACCEPT) { if (mbret == IDC_HK_ACCEPT) {
store_host_key(host, port, keytype, keystr); store_host_key(seat, host, port, keytype, keystr);
return SPR_OK; return SPR_OK;
} else if (mbret == IDC_HK_ONCE) { } else if (mbret == IDC_HK_ONCE) {
return SPR_OK; return SPR_OK;

View File

@ -97,6 +97,7 @@ static const SeatVtable plink_seat_vt = {
.notify_remote_exit = nullseat_notify_remote_exit, .notify_remote_exit = nullseat_notify_remote_exit,
.notify_remote_disconnect = nullseat_notify_remote_disconnect, .notify_remote_disconnect = nullseat_notify_remote_disconnect,
.connection_fatal = console_connection_fatal, .connection_fatal = console_connection_fatal,
.nonfatal = console_nonfatal,
.update_specials_menu = nullseat_update_specials_menu, .update_specials_menu = nullseat_update_specials_menu,
.get_ttymode = nullseat_get_ttymode, .get_ttymode = nullseat_get_ttymode,
.set_busy_status = nullseat_set_busy_status, .set_busy_status = nullseat_set_busy_status,

View File

@ -357,7 +357,7 @@ bool have_ssh_host_key(const char *hostname, int port,
return check_stored_host_key(hostname, port, keytype, "") != 1; return check_stored_host_key(hostname, port, keytype, "") != 1;
} }
void store_host_key(const char *hostname, int port, void store_host_key(Seat *seat, const char *hostname, int port,
const char *keytype, const char *key) const char *keytype, const char *key)
{ {
strbuf *regname = strbuf_new(); strbuf *regname = strbuf_new();

View File

@ -321,6 +321,7 @@ static bool win_seat_eof(Seat *seat);
static SeatPromptResult win_seat_get_userpass_input(Seat *seat, prompts_t *p); static SeatPromptResult win_seat_get_userpass_input(Seat *seat, prompts_t *p);
static void win_seat_notify_remote_exit(Seat *seat); static void win_seat_notify_remote_exit(Seat *seat);
static void win_seat_connection_fatal(Seat *seat, const char *msg); static void win_seat_connection_fatal(Seat *seat, const char *msg);
static void win_seat_nonfatal(Seat *seat, const char *msg);
static void win_seat_update_specials_menu(Seat *seat); static void win_seat_update_specials_menu(Seat *seat);
static void win_seat_set_busy_status(Seat *seat, BusyStatus status); static void win_seat_set_busy_status(Seat *seat, BusyStatus status);
static void win_seat_set_trust_status(Seat *seat, bool trusted); static void win_seat_set_trust_status(Seat *seat, bool trusted);
@ -338,6 +339,7 @@ static const SeatVtable win_seat_vt = {
.notify_remote_exit = win_seat_notify_remote_exit, .notify_remote_exit = win_seat_notify_remote_exit,
.notify_remote_disconnect = nullseat_notify_remote_disconnect, .notify_remote_disconnect = nullseat_notify_remote_disconnect,
.connection_fatal = win_seat_connection_fatal, .connection_fatal = win_seat_connection_fatal,
.nonfatal = win_seat_nonfatal,
.update_specials_menu = win_seat_update_specials_menu, .update_specials_menu = win_seat_update_specials_menu,
.get_ttymode = win_seat_get_ttymode, .get_ttymode = win_seat_get_ttymode,
.set_busy_status = win_seat_set_busy_status, .set_busy_status = win_seat_set_busy_status,
@ -1188,6 +1190,17 @@ static void win_seat_connection_fatal(Seat *seat, const char *msg)
} }
} }
/*
* Print a message box and don't close the connection.
*/
static void win_seat_nonfatal(Seat *seat, const char *msg)
{
char *title = dupprintf("%s Error", appname);
show_mouseptr(true);
MessageBox(wgs.term_hwnd, msg, title, MB_ICONERROR | MB_OK);
sfree(title);
}
/* /*
* Report an error at the command-line parsing stage. * Report an error at the command-line parsing stage.
*/ */