diff --git a/gsmartcontrol/src/applib/app_gtkmm_utils.cpp b/gsmartcontrol/src/applib/app_gtkmm_utils.cpp index 5fc857f..89032c4 100644 --- a/gsmartcontrol/src/applib/app_gtkmm_utils.cpp +++ b/gsmartcontrol/src/applib/app_gtkmm_utils.cpp @@ -12,6 +12,7 @@ #include #include // gtk_* stuff #include +#include #include "app_gtkmm_features.h" // APP_GTKMM_OLD_TOOLTIPS @@ -193,7 +194,6 @@ void gtkmm_set_iconview_tooltip_column(Gtk::IconView* iconview, - bool app_gtkmm_icon_theme_has_icon(Glib::RefPtr theme, const Glib::ustring& icon_name, int size) { @@ -212,6 +212,81 @@ bool app_gtkmm_icon_theme_has_icon(Glib::RefPtr theme, +namespace { + + /// This has been copied from _g_utf8_make_valid() (glib-2.20.4). + /// _g_utf8_make_valid() is GLib's private function for auto-correcting + /// the potentially invalid utf-8 data. + inline gchar* gsc_g_utf8_make_valid (const gchar* name) + { + GString* string; + const gchar* remainder, *invalid; + gint remaining_bytes, valid_bytes; + + g_return_val_if_fail (name != NULL, NULL); + + string = NULL; + remainder = name; + remaining_bytes = gint(strlen(name)); + + while (remaining_bytes != 0) { + if (g_utf8_validate (remainder, remaining_bytes, &invalid)) + break; + + valid_bytes = gint(invalid - remainder); + + if (string == NULL) + string = g_string_sized_new (remaining_bytes); + + g_string_append_len (string, remainder, valid_bytes); + /* append U+FFFD REPLACEMENT CHARACTER */ + g_string_append (string, "\357\277\275"); + + remaining_bytes -= valid_bytes + 1; + remainder = invalid + 1; + } + + if (string == NULL) + return g_strdup (name); + + g_string_append (string, remainder); + + g_assert (g_utf8_validate (string->str, -1, NULL)); + + return g_string_free (string, FALSE); + } + +} + + + +Glib::ustring app_utf8_make_valid(const Glib::ustring& str) +{ + char* s = gsc_g_utf8_make_valid(str.c_str()); + if (!s) { + return Glib::ustring(); + } + Glib::ustring res(s); + g_free(s); + return res; +} + + + +Glib::ustring app_output_make_valid(const Glib::ustring& str) +{ + #ifdef _WIN32 + try { + return app_utf8_make_valid(Glib::locale_to_utf8(str)); + } catch (Glib::ConvertError& e) { + // nothing, try to fix as it is + } + #endif + return app_utf8_make_valid(str); +} + + + diff --git a/gsmartcontrol/src/applib/app_gtkmm_utils.h b/gsmartcontrol/src/applib/app_gtkmm_utils.h index 68ec4ab..635dbd2 100644 --- a/gsmartcontrol/src/applib/app_gtkmm_utils.h +++ b/gsmartcontrol/src/applib/app_gtkmm_utils.h @@ -99,6 +99,17 @@ bool app_gtkmm_icon_theme_has_icon(Glib::RefPtr theme, const Glib::ustring& icon_name, int size); +/// Convert a possibly invalid utf-8 string to valid utf-8. +/// \param str string to test and fix. +/// \param in_locale If true, \c str will be converted from locale charset to utf-8 first. +Glib::ustring app_utf8_make_valid(const Glib::ustring& str); + + +/// Make command output a valid utf-8 string. Essentially, this calls app_utf8_make_valid(), +/// supplying true for \c in_locale under Win32, and false under other systems. +/// The reason for this is that in Win32 we can't execute commands under C locale, +/// but we do execute them under C in other systems. +Glib::ustring app_output_make_valid(const Glib::ustring& str); diff --git a/gsmartcontrol/src/gsc_executor_error_dialog.cpp b/gsmartcontrol/src/gsc_executor_error_dialog.cpp index 44d2f2f..7677ac1 100644 --- a/gsmartcontrol/src/gsc_executor_error_dialog.cpp +++ b/gsmartcontrol/src/gsc_executor_error_dialog.cpp @@ -86,18 +86,7 @@ void gsc_no_info_dialog_show(const std::string& message, const std::string& sec_ if (response == Gtk::RESPONSE_HELP) { GscTextWindow* win = GscTextWindow::create(); - // make save visible and enable monospace font - - std::string buf_text = output; - // We receive locale'd thousands separators in win32, so convert them. - #ifdef _WIN32 - try { - buf_text = Glib::locale_to_utf8(buf_text); - } catch (Glib::ConvertError& e) { - buf_text = ""; // inserting invalid utf8 may trigger a segfault, so empty is better. - } - #endif - win->set_text(output_window_title, buf_text, true, true); + win->set_text(output_window_title, output, true, true); if (!default_save_filename.empty()) win->set_save_filename(default_save_filename); diff --git a/gsmartcontrol/src/gsc_executor_log_window.cpp b/gsmartcontrol/src/gsc_executor_log_window.cpp index d6f3554..4323b9b 100644 --- a/gsmartcontrol/src/gsc_executor_log_window.cpp +++ b/gsmartcontrol/src/gsc_executor_log_window.cpp @@ -322,25 +322,7 @@ void GscExecutorLogWindow::on_tree_selection_changed() if (output_textview) { Glib::RefPtr buffer = output_textview->get_buffer(); if (buffer) { - - // Under win32, we can't execute smartctl under C locale. Smartctl - // uses locale information only for thousands separator in User Capacity. - // We can parse that, but we need to insert that text into a textarea - // widget, which may error out on invalid utf8 char. - // So, we convert the whole output to utf8, hoping that the result is ok. - // Note that a separator converted to utf8 may be a different sequence - // of chars, so we parse it as original charset, but insert into a textarea - // as utf8. - std::string buf_text = entry->std_output; - #ifdef _WIN32 - try { - buf_text = Glib::locale_to_utf8(buf_text); - } catch (Glib::ConvertError& e) { - buf_text = ""; // inserting invalid utf8 may trigger a segfault, so empty better. - } - #endif - - buffer->set_text(buf_text); + buffer->set_text(app_output_make_valid(entry->std_output)); Glib::RefPtr tag; Glib::RefPtr table = buffer->get_tag_table(); @@ -354,15 +336,11 @@ void GscExecutorLogWindow::on_tree_selection_changed() } } - // Hide in win32 because it is known to cause segfaults there (see above). - // Not anymore... -// #ifndef _WIN32 Gtk::Entry* command_entry = this->lookup_widget("command_entry"); if (command_entry) { std::string cmd_text = entry->command + " " + entry->parameters; - command_entry->set_text(cmd_text); + command_entry->set_text(app_output_make_valid(cmd_text)); } -// #endif Gtk::Button* window_save_current_button = this->lookup_widget("window_save_current_button"); if (window_save_current_button) diff --git a/gsmartcontrol/src/gsc_info_window.cpp b/gsmartcontrol/src/gsc_info_window.cpp index c5c5618..61c9be6 100644 --- a/gsmartcontrol/src/gsc_info_window.cpp +++ b/gsmartcontrol/src/gsc_info_window.cpp @@ -1261,22 +1261,12 @@ bool GscInfoWindow::on_delete_event_before(GdkEventAny* e) - void GscInfoWindow::on_view_output_button_clicked() { GscTextWindow* win = GscTextWindow::create(); // make save visible and enable monospace font - std::string buf_text = this->drive->get_full_output(); - // We receive locale'd thousands separators in win32, so convert them. - #ifdef _WIN32 - try { - buf_text = Glib::locale_to_utf8(buf_text); - } catch (Glib::ConvertError& e) { - buf_text = ""; // inserting invalid utf8 may trigger a segfault, so empty better. - } - #endif - win->set_text("Smartctl Output", buf_text, true, true); + win->set_text("Smartctl Output", this->drive->get_full_output(), true, true); std::string filename = drive->get_save_filename(); if (!filename.empty()) diff --git a/gsmartcontrol/src/gsc_text_window.h b/gsmartcontrol/src/gsc_text_window.h index 6516888..8ca7efe 100644 --- a/gsmartcontrol/src/gsc_text_window.h +++ b/gsmartcontrol/src/gsc_text_window.h @@ -18,6 +18,7 @@ #include "hz/fs_file.h" #include "applib/app_ui_res_utils.h" +#include "applib/app_gtkmm_utils.h" @@ -87,7 +88,7 @@ class GscTextWindow : public AppUIResWidget, Insta Gtk::TextView* textview = this->template lookup_widget("main_textview"); if (textview) { Glib::RefPtr buffer = textview->get_buffer(); - buffer->set_text(contents); + buffer->set_text(app_output_make_valid(contents)); if (use_monospace) { Glib::RefPtr tag = buffer->create_tag();