Started marking texts as translatable.

Fixed a few minor bugs.
This commit is contained in:
Alexander Shaduri
2019-07-23 10:45:17 +00:00
parent 5265ebb939
commit 6d3a097f20
36 changed files with 726 additions and 435 deletions
+4
View File
@@ -1,5 +1,9 @@
#!/bin/bash
# Default for all directories
svn propset svn:ignore -R -F .svnignore-default.txt .
# Properties for each directory
for dir in . autoconf.m4 po; do
pushd $dir
svn propset svn:ignore -F .svnignore.txt .
+10
View File
@@ -0,0 +1,10 @@
find_package(PkgConfig REQUIRED)
# Don't make it REQUIRED, we may want to build only the parsers
pkg_check_modules(Gtkmm IMPORTED_TARGET GLOBAL gtkmm-3.0 >= 3.0)
# pcrecpp from pcre1
pkg_check_modules(Pcrecpp REQUIRED IMPORTED_TARGET GLOBAL libpcrecpp)
+1 -12
View File
@@ -2,22 +2,11 @@
# This cmake file is only for IDE integration, it should not be used
# to compile this program.
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.13)
project(gsmartcontrol)
set(CMAKE_CXX_STANDARD 17)
find_package(PkgConfig)
pkg_check_modules(GTKMM gtkmm-3.0)
pkg_check_modules(PCRECPP libpcrecpp)
include_directories(SYSTEM
${GTKMM_INCLUDE_DIRS}
)
link_directories(${GTKMM_LIBRARY_DIRS})
# Clang5 doesn't understand libstdc++'s std::get(variant), so use libc++.
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+73 -1
View File
@@ -28,6 +28,15 @@ Don't rely on smartctl return code (2), parse the output instead.
Need usage cases.
JSON may fix this.
Fix:
Compilation under centos7 (compiler flags?) - see OBS.
<warn> [app] SmartctlParser::parse_section_info(): Unknown Info line encountered.
<dump> [app] ---------------- Begin unknown Info line ----------------
<dump> [app] NO MEDIUM present in device
<dump> [app] ----------------- End unknown Info line -----------------
convert to STL algorithms.
GTKMM 4.
GtkFileChooserNative
@@ -37,12 +46,52 @@ GTKMM 4.
local_glibmm.h
make dpi-aware?
Port to cmake
For external targets, list them in a separate cmakelists, set
IMPORTED_GLOBAL TRUE on them.
Create targets without sources first.
Use target_sources() with all files listed as PRIVATE.
Use IMPORTED targets for external libs (or find_package()).
We can use OBJECT libraries instead of statics (not sure of the benefit).
OBJECT libs have strange propagation, better not use them.
Make zip/exe installer creation as a separate target.
Require 3.12+ (or 3.13+).
http://cliutils.gitlab.io/modern-cmake/
Maybe add external deps (json, catch2) through some C++ package manager?
Conan?
Add Catch2 tests
Use Data Generators for testing the parser(s)
https://github.com/catchorg/Catch2/blob/master/docs/generators.md
Maybe use doctest for writing tests along the production code?
https://blog.jetbrains.com/rscpp/better-ways-testing-with-doctest/
No generators, but those can be done using catch.
Take care of tests in static libs - they need special registration.
Write multiple tests so that they can be executed in parallel with ctest.
https://a4z.bitbucket.io/blog/2018/05/17/Speed-up-your-test-cycles-with-CMake.html
?Implement Mocking
Trompeloeil
https://machinekoder.com/qt-unit-testing-catch-trompeloeil/
Can mock free an non-virtual member functions as well.
https://github.com/rollbear/trompeloeil/blob/master/docs/CookBook.md#-mocking-non-virtual-member-functions
JSON.
Port the rest to std::regex, get rid of pcre requirement.
Not sure, we still want to support old-format files.
Maybe make pcre optional (only for parsing the old format)
Add TESTS!
Fix gdk_pixbuf_from_pixdata() warning
gdk_pixdata_from_pixbuf() has been deprecated, but we don't use it.
Probably gtk uses it in iconview.
Use header-only formatting library:
For libdebug.
Instead of string_sprintf.
@@ -50,23 +99,46 @@ Use header-only formatting library:
Use std::from_chars() in string_is_numeric_impl_classic_locale() (gcc 8)
Fix win32 crash.
Waiting for filesystem to be included in mingw, otherwise can't compile.
Use std::visit for variant instead of if(holds_alternative...)
[[gnu::format]] attribute (replace all __attribute__'s)
Detect and link with clang's / gcc's experimental lib, if needed.
Support different @sbindir@ (see email by Jan Tojnar)
smartmontools 7.0 feature:
smartctl '-x': Now includes '-l defects'.
Check TODOs
Mark with gettext
Make sure desktop file is translated.
Add french translation?
Win32: Bundle intl.dll and translations with windows distribution.
Add translations to .spec and .deb
Add translation files to .spec and .deb
Win32: Bundle gtk/glib/... translations for languages we support.
Convert template<typename Container> to this:
https://stackoverflow.com/questions/50231612/a-function-template-that-accepts-both-stdvector-and-qvector
Self-tests:
For "Abort" button, add a confirmation (default: No), to avoid accidental abortions by pressing
space key.
Win7 reported bug: During extended test, while doing other things, at random times
the program window pops up on top (I guess it's smartctl execution window).
Before releasing 2.0.0:
Test with freebsd and macOS.
Make "Add Device" persistent.
Automatically print backtrace on crash.
Testing:
If ETA time has elapsed, but it's still only at 10% completion,
+19 -2
View File
@@ -1,3 +1,5 @@
src/hz/format_unit.h
src/ui/gsc_about_dialog.glade
src/ui/gsc_add_device_window.glade
src/ui/gsc_executor_log_window.glade
@@ -6,7 +8,7 @@ src/ui/gsc_main_window.glade
src/ui/gsc_preferences_window.glade
src/ui/gsc_text_window.glade
src/add_device_window.cpp
src/gsc_add_device_window.cpp
src/gsc_main_window.cpp
src/gsc_executor_error_dialog.cpp
src/gsc_executor_log_window.cpp
@@ -17,4 +19,19 @@ src/gsc_main_window_iconview.h
src/gsc_preferences_window.cpp
src/gsc_text_window.h
hz/format_unit.h
src/applib/app_builder_widget.h
src/applib/cli_executors.h
src/applib/cmdex_sync_gui.cpp
src/applib/selftest.cpp
src/applib/smartctl_executor.cpp
src/applib/smartctl_executor.h
src/applib/smartctl_parser.cpp
src/applib/storage_detector.cpp
src/applib/storage_detector_helpers.h
src/applib/storage_detector_linux.cpp
src/applib/storage_detector_other.cpp
src/applib/storage_detector_win32.cpp
src/applib/storage_device.cpp
src/applib/storage_property.cpp
src/applib/storage_property_colors.h
src/applib/storage_property_descr.h
@@ -13,8 +13,8 @@
#define APP_BUILDER_WIDGET_H
#include <string>
#include <type_traits>
#include <gtkmm.h>
#include <glibmm/i18n.h>
#include "hz/debug.h"
#include "hz/instance_manager.h"
@@ -33,7 +33,7 @@
if (!(ui_element)) \
this->lookup_widget(#ui_element, ui_element); \
if (ui_element) { \
(ui_element)->signal_ ## signal_name ().connect(sigc::mem_fun(*this, &std::remove_pointer_t<decltype(this)>::callback)); \
(ui_element)->signal_ ## signal_name ().connect(sigc::mem_fun(*this, &std::remove_reference_t<decltype(*this)>::callback)); \
} \
} else (void)0
@@ -78,9 +78,8 @@ class AppBuilderWidget : public WidgetType, public hz::InstanceManager<Child, Mu
ui->get_widget_derived(Child::ui_name, o); // Calls Child's constructor
if (!o) {
std::string msg = "Fatal error: Cannot get root widget from UI-resource-created hierarchy.";
debug_out_fatal("app", msg << "\n");
gui_show_error_dialog(msg);
debug_out_fatal("app", "Fatal error: Cannot get root widget from UI-resource-created hierarchy.\n");
gui_show_error_dialog(_("Fatal error: Cannot get root widget from UI-resource-created hierarchy."));
return nullptr;
}
@@ -94,9 +93,8 @@ class AppBuilderWidget : public WidgetType, public hz::InstanceManager<Child, Mu
}
if (!error_msg.empty()) {
std::string msg = "Fatal error: Cannot create UI-resource widgets: " + error_msg;
debug_out_fatal("app", msg << "\n");
gui_show_error_dialog(msg);
debug_out_fatal("app", "Fatal error: Cannot create UI-resource widgets: " << error_msg << "\n");
gui_show_error_dialog(Glib::ustring::compose(_("Fatal error: Cannot create UI-resource widgets: %1"), error_msg));
}
return nullptr;
}
+3 -3
View File
@@ -12,7 +12,7 @@
#ifndef CLI_EXECUTORS_H
#define CLI_EXECUTORS_H
#include <memory> // shared_ptr
#include <glibmm/i18n.h>
#include "cmdex.h"
#include "cmdex_sync.h"
@@ -49,7 +49,7 @@ class TwCliExecutorGeneric : public ExecutorSync {
void construct()
{
ExecutorSync::get_command_executor().set_exit_status_translator(&TwCliExecutorGeneric::translate_exit_status);
this->set_error_header("An error occurred while executing tw_cli:\n\n");
this->set_error_header(std::string(_("An error occurred while executing tw_cli:")) + "\n\n");
}
@@ -148,7 +148,7 @@ class ArecaCliExecutorGeneric : public ExecutorSync {
void construct()
{
ExecutorSync::get_command_executor().set_exit_status_translator(&ArecaCliExecutorGeneric::translate_exit_status);
this->set_error_header("An error occurred while executing Areca cli:\n\n");
this->set_error_header(std::string(_("An error occurred while executing Areca cli:")) + "\n\n");
}
+105
View File
@@ -10,6 +10,7 @@
/// @{
#include <glib.h> // g_usleep()
#include <glibmm/i18n.h>
#include "cmdex_sync.h"
@@ -31,6 +32,44 @@ cmdex_signal_execute_finish_type& cmdex_sync_signal_execute_finish()
CmdexSync::CmdexSync(std::string command_name, std::string command_args)
: CmdexSync()
{
this->set_command(std::move(command_name), std::move(command_args));
}
CmdexSync::CmdexSync()
{
/// Translators: {command} will be replaced by command name.
running_msg_ = _("Running {command}...");
set_error_header(std::string(_("An error occurred while executing command:")) + "\n\n");
}
void CmdexSync::set_command(std::string command_name, std::string command_args)
{
cmdex_.set_command(command_name, command_args);
// keep a copy locally to avoid locking on get() every time
command_name_ = std::move(command_name);
command_args_ = std::move(command_args);
}
std::string CmdexSync::get_command_name() const
{
return command_name_;
}
std::string CmdexSync::get_command_args() const
{
return command_args_;
}
@@ -118,6 +157,43 @@ bool CmdexSync::execute()
void CmdexSync::set_forced_kill_timeout(std::chrono::milliseconds timeout_msec)
{
forced_kill_timeout_msec_ = timeout_msec;
}
std::string CmdexSync::get_error_msg(bool with_header) const
{
if (with_header)
return error_header_ + error_msg_;
return error_msg_;
}
void CmdexSync::set_running_msg(const std::string& msg)
{
running_msg_ = msg;
}
void CmdexSync::set_error_header(const std::string& msg)
{
error_header_ = msg;
}
std::string CmdexSync::get_error_header()
{
return error_header_;
}
void CmdexSync::import_error()
{
Cmdex::error_list_t errors = cmdex_.get_errors(); // these are not clones
@@ -133,6 +209,35 @@ void CmdexSync::import_error()
void CmdexSync::on_error_warn(hz::ErrorBase* e)
{
if (e) {
set_error_msg(e->get_message()); // this message will be displayed
}
}
void CmdexSync::set_error_msg(const std::string& error_msg)
{
error_msg_ = error_msg;
}
std::string CmdexSync::get_running_msg() const
{
return running_msg_;
}
Cmdex& CmdexSync::get_command_executor()
{
return cmdex_;
}
+14 -69
View File
@@ -16,7 +16,6 @@
#include <string>
#include <chrono>
#include <utility>
#include <glibmm/i18n.h>
#include "hz/error.h"
#include "hz/process_signal.h" // hz::SIGNAL_*
@@ -58,48 +57,25 @@ class CmdexSync : public sigc::trackable {
public:
/// Constructor
CmdexSync(std::string command_name, std::string command_args)
: CmdexSync()
{
this->set_command(std::move(command_name), std::move(command_args));
}
CmdexSync();
/// Constructor
CmdexSync()
{
/// Translators: {command} will be replaced by command name.
running_msg_ = _("Running {command}...");
set_error_header("An error occurred while executing the command:\n\n");
}
CmdexSync(std::string command_name, std::string command_args);
/// Virtual destructor
virtual ~CmdexSync() = default;
/// Set command to execute and its parameters
void set_command(std::string command_name, std::string command_args)
{
cmdex_.set_command(command_name, command_args);
// keep a copy locally to avoid locking on get() every time
command_name_ = std::move(command_name);
command_args_ = std::move(command_args);
}
void set_command(std::string command_name, std::string command_args);
/// Get command to execute
std::string get_command_name() const
{
return command_name_;
}
std::string get_command_name() const;
/// Get command arguments
std::string get_command_args() const
{
return command_args_;
}
std::string get_command_args() const;
/// Execute the command. The function will return only after the command exits.
@@ -112,10 +88,7 @@ class CmdexSync : public sigc::trackable {
/// Set timeout (in ms) to send SIGKILL after sending SIGTERM.
/// Used if manual stop was requested through ticker.
void set_forced_kill_timeout(std::chrono::milliseconds timeout_msec)
{
forced_kill_timeout_msec_ = timeout_msec;
}
void set_forced_kill_timeout(std::chrono::milliseconds timeout_msec);
/// Try to stop the process. Call this from ticker slot while executing.
@@ -184,33 +157,19 @@ class CmdexSync : public sigc::trackable {
/// Get command execution error message. If \c with_header
/// is true, a header set using set_error_header() will be displayed first.
std::string get_error_msg(bool with_header = false) const
{
if (with_header)
return error_header_ + error_msg_;
return error_msg_;
}
std::string get_error_msg(bool with_header = false) const;
/// Set a message to display when running. "{command}" in \c msg will be replaced by the command.
void set_running_msg(const std::string& msg)
{
running_msg_ = msg;
}
void set_running_msg(const std::string& msg);
/// Set error header string. See get_error_msg()
void set_error_header(const std::string& msg)
{
error_header_ = msg;
}
void set_error_header(const std::string& msg);
/// Get error header string. See get_error_msg()
std::string get_error_header()
{
return error_header_;
}
std::string get_error_header();
// ----------------- Signals
@@ -240,33 +199,19 @@ class CmdexSync : public sigc::trackable {
/// The warnings are already printed via debug_* in cmdex.
/// Override if needed.
virtual void on_error_warn(hz::ErrorBase* e)
{
if (e) {
set_error_msg(e->get_message()); // this message will be displayed
}
}
virtual void on_error_warn(hz::ErrorBase* e);
/// Set error message
void set_error_msg(const std::string& error_msg)
{
error_msg_ = error_msg;
}
void set_error_msg(const std::string& error_msg);
/// Get "running" message
std::string get_running_msg() const
{
return running_msg_;
}
std::string get_running_msg() const;
/// Get command executor object
Cmdex& get_command_executor()
{
return cmdex_;
}
Cmdex& get_command_executor();
private:
+2 -1
View File
@@ -14,6 +14,7 @@
#include <gtkmm.h> // Gtk::Main
#include <gdkmm.h>
#include <glibmm/i18n.h>
#include "hz/string_algo.h"
#include "hz/fs_ns.h"
@@ -123,7 +124,7 @@ void CmdexSyncGui::set_running_dialog_abort_mode(bool aborting)
show_hide_dialog(false);
running_dialog_->set_message("\n Aborting... ");
running_dialog_->set_message(std::string("\n ") + _("Aborting...") + " ");
// the sensitive button switching is done after show(), to avoid some visual
// defects - cursor in label, selected label.
+32 -12
View File
@@ -12,6 +12,7 @@
#include <algorithm> // std::max, std::min
#include <cmath> // std::floor
#include <chrono>
#include <glibmm/i18n.h>
#include "app_pcrecpp.h"
#include "storage_property.h"
@@ -20,6 +21,23 @@
std::string SelfTest::get_test_displayable_name(SelfTest::TestType type)
{
static const std::unordered_map<TestType, std::string> m {
{TestType::immediate_offline, _("Immediate Offline Test")},
{TestType::short_test, _("Short Self-Test")},
{TestType::long_test, _("Extended Self-Test")},
{TestType::conveyance, _("Conveyance Self-Test")},
};
if (auto iter = m.find(type); iter != m.end()) {
return iter->second;
}
return "[internal_error]";
}
// Returns estimated time of completion for the test. returns -1 if n/a or unknown. 0 is a valid value.
std::chrono::seconds SelfTest::get_remaining_seconds() const
{
@@ -93,11 +111,13 @@ bool SelfTest::is_supported() const
std::string SelfTest::start(const std::shared_ptr<CmdexSync>& smartctl_ex)
{
if (!drive_)
return "Invalid drive given.";
return "[internal error: drive must not be NULL]";
if (drive_->get_test_is_active())
return "A test is already running on this drive.";
if (!this->is_supported())
return get_test_name(type_) + " is unsupported by this drive.";
return _("A test is already running on this drive.");
if (!this->is_supported()) {
/// Translators: %1 is test name - Short test, etc...
return Glib::ustring::compose(_("%1 is unsupported by this drive."), get_test_displayable_name(type_));
}
std::string test_param;
switch(type_) {
@@ -108,7 +128,7 @@ std::string SelfTest::start(const std::shared_ptr<CmdexSync>& smartctl_ex)
// no default - this way we get warned by compiler if we're not listing all of them.
}
if (test_param.empty())
return "Invalid test specified";
return _("Invalid test specified");
std::string output;
std::string error_msg = drive_->execute_device_smartctl("--test=" + test_param, smartctl_ex, output);
@@ -117,7 +137,7 @@ std::string SelfTest::start(const std::shared_ptr<CmdexSync>& smartctl_ex)
return error_msg;
if (!app_pcre_match(R"(/^Drive command .* successful\.\nTesting has begun\.$/mi)", output)) {
return "Sending command failed.";
return _("Sending command to drive failed.");
}
@@ -153,9 +173,9 @@ std::string SelfTest::start(const std::shared_ptr<CmdexSync>& smartctl_ex)
std::string SelfTest::force_stop(const std::shared_ptr<CmdexSync>& smartctl_ex)
{
if (!drive_)
return "Invalid drive given.";
return "[internal error: drive must not be NULL]";
if (!drive_->get_test_is_active())
return "No test is currently running on this drive.";
return _("No test is currently running on this drive.");
// To abort immediate offline test, the device MUST have
// "Abort Offline collection upon new command" capability,
@@ -164,7 +184,7 @@ std::string SelfTest::force_stop(const std::shared_ptr<CmdexSync>& smartctl_ex)
if (type_ == TestType::immediate_offline) {
StorageProperty p = drive_->lookup_property("iodc_command_suspends", StorageProperty::Section::internal);
if (!p.empty() && p.get_value<bool>()) { // if empty, give a chance to abort anyway.
return "Aborting this test is unsupported by the drive.";
return _("Aborting this test is unsupported by the drive.");
}
// else, proceed as any other test
}
@@ -178,7 +198,7 @@ std::string SelfTest::force_stop(const std::shared_ptr<CmdexSync>& smartctl_ex)
// this command prints success even if no test was running.
if (!app_pcre_match("/^Self-testing aborted!$/mi", output)) {
return "Sending command failed.";
return _("Sending command to drive failed.");
}
// update our members
@@ -209,7 +229,7 @@ std::string SelfTest::update(const std::shared_ptr<CmdexSync>& smartctl_ex)
using namespace std::literals;
if (!drive_)
return "Invalid drive given.";
return "[internal error: drive must not be NULL]";
std::string output;
// std::string error_msg = drive_->execute_device_smartctl("--log=selftest", smartctl_ex, output);
@@ -238,7 +258,7 @@ std::string SelfTest::update(const std::shared_ptr<CmdexSync>& smartctl_ex)
}
if (p.empty())
return "The drive doesn't report the test status.";
return _("The drive doesn't report the test status.");
status_ = p.get_value<StorageSelftestEntry>().status;
bool active = (status_ == StorageSelftestEntry::Status::in_progress);
+3 -16
View File
@@ -15,7 +15,7 @@
// TODO Remove this in gtkmm4.
#include "local_glibmm.h"
#include <glibmm.h>
#include <glibmm.h> // Timer
#include <string>
#include <cstdint>
#include <chrono>
@@ -40,25 +40,12 @@ class SelfTest {
/// Get displayable name for a test type
static std::string get_test_name(TestType type)
{
static const std::unordered_map<TestType, std::string> m {
{TestType::immediate_offline, "Immediate Offline Test"},
{TestType::short_test, "Short Self-Test"},
{TestType::long_test, "Extended Self-Test"},
{TestType::conveyance, "Conveyance Self-Test"},
};
if (auto iter = m.find(type); iter != m.end()) {
return iter->second;
}
return "[internal_error]";
return "[error]";
}
static std::string get_test_displayable_name(TestType type);
/// Constructor. \c drive must have the capabilities present in its properties.
SelfTest(StorageDevicePtr drive, TestType type)
: drive_(drive), type_(type)
: drive_(std::move(drive)), type_(type)
{ }
@@ -13,6 +13,7 @@
#include "local_glibmm.h"
#include <glibmm.h> // Glib::shell_quote()
#include <glibmm/i18n.h>
#include "smartctl_executor.h"
#include "hz/win32_tools.h"
@@ -80,7 +81,7 @@ std::string execute_smartctl(const std::string& device, const std::string& devic
std::string::size_type pos = device.rfind('/'); // find basename
if (pos == std::string::npos) {
debug_out_error("app", DBG_FUNC_MSG << "Invalid device name \"" << device << "\".\n");
return "Invalid device name specified.";
return _("Invalid device name specified.");
}
}
#endif
@@ -92,7 +93,7 @@ std::string execute_smartctl(const std::string& device, const std::string& devic
if (smartctl_binary.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Smartctl binary is not set in config.\n");
return "Smartctl binary is not specified in configuration.";
return _("Smartctl binary is not specified in configuration.");
}
auto smartctl_def_options = rconfig::get_data<std::string>("system/smartctl_options");
@@ -118,7 +119,7 @@ std::string execute_smartctl(const std::string& device, const std::string& devic
// check if it's a device permission error.
// Smartctl open device: /dev/sdb failed: Permission denied
if (app_pcre_match("/Smartctl open device.+Permission denied/mi", smartctl_output)) {
return "Permission denied while opening device.";
return _("Permission denied while opening device.");
}
// smartctl_output = smartctl_ex->get_stdout_str();
@@ -129,7 +130,7 @@ std::string execute_smartctl(const std::string& device, const std::string& devic
smartctl_output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_ex->get_stdout_str()));
if (smartctl_output.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Smartctl returned an empty output.\n");
return "Smartctl returned an empty output.";
return _("Smartctl returned an empty output.");
}
return std::string();
+13 -10
View File
@@ -12,6 +12,9 @@
#ifndef SMARTCTL_EXECUTOR_H
#define SMARTCTL_EXECUTOR_H
#include <vector>
#include <glibmm/i18n.h>
#include "cmdex.h"
#include "cmdex_sync.h"
#include "hz/fs_ns.h"
@@ -45,7 +48,7 @@ class SmartctlExecutorGeneric : public ExecutorSync {
void construct()
{
ExecutorSync::get_command_executor().set_exit_status_translator(&SmartctlExecutorGeneric::translate_exit_status);
this->set_error_header("An error occurred while executing smartctl:\n\n");
this->set_error_header(std::string(_("An error occurred while executing smartctl:")) + "\n\n");
}
@@ -64,15 +67,15 @@ class SmartctlExecutorGeneric : public ExecutorSync {
/// Translate smartctl error code to a readable message
static std::string translate_exit_status(int status)
{
static const char* const table[] = {
"Command line did not parse.",
"Device open failed, or device did not return an IDENTIFY DEVICE structure.",
"Some SMART command to the disk failed, or there was a checksum error in a SMART data structure",
"SMART status check returned \"DISK FAILING\"",
"SMART status check returned \"DISK OK\" but some prefail Attributes are less than threshold.",
"SMART status check returned \"DISK OK\" but we found that some (usage or prefail) Attributes have been less than threshold at some time in the past.",
"The device error log contains records of errors.",
"The device self-test log contains records of errors."
static const std::vector<std::string> table = {
_("Command line did not parse."),
_("Device open failed, or device did not return an IDENTIFY DEVICE structure."),
_("Some SMART command to the disk failed, or there was a checksum error in a SMART data structure"),
_("SMART status check returned \"DISK FAILING\""),
_("SMART status check returned \"DISK OK\" but some prefail Attributes are less than threshold."),
_("SMART status check returned \"DISK OK\" but we found that some (usage or prefail) Attributes have been less than threshold at some time in the past."),
_("The device error log contains records of errors."),
_("The device self-test log contains records of errors.")
};
std::string str;
+57 -4
View File
@@ -9,8 +9,13 @@
/// \weakgroup applib
/// @{
// TODO Remove this in gtkmm4.
#include "local_glibmm.h"
#include <clocale> // localeconv
#include <cstdint>
#include <glibmm.h> // compose()
#include <glibmm/i18n.h>
#include "hz/locale_tools.h" // ScopedCLocale, locale_c_get().
#include "hz/string_algo.h" // string_*
@@ -52,7 +57,7 @@ namespace {
p.set_name(name, "selftest_log_checksum_error");
}
p.readable_name = "Error in " + name + " structure";
p.displayable_name = "Error in " + name + " structure";
p.reported_value = "checksum error";
p.value = p.reported_value; // string-type value
@@ -330,7 +335,7 @@ std::string SmartctlParser::parse_byte_size(const std::string& str, int64_t& byt
// debug_out_dump("app", "Size reported as: " << str << "\n");
std::vector<std::string> to_replace = {" ", "'", ",", ".", std::string(1, 0xa0)};
std::vector<std::string> to_replace = {" ", "'", ",", ".", std::string(1, static_cast<char>(0xa0))};
#ifdef _WIN32
// if current locale is C, then probably we didn't change it at application
@@ -1682,7 +1687,7 @@ Error 1 [0] occurred at disk power-on lifetime: 1 hours (0 days + 1 hours)
if (re.PartialMatch(sub)) {
StorageProperty p(pt);
p.set_name("error_log_unsupported");
p.readable_name = "Warning";
p.displayable_name = "Warning";
p.readable_value = "Device does not support error logging";
add_property(p);
}
@@ -1835,7 +1840,7 @@ Num Test_Description Status Remaining LifeTime(hours) LBA
if (re.PartialMatch(sub)) {
StorageProperty p(pt);
p.set_name("selftest_log_unsupported");
p.readable_name = "Warning";
p.displayable_name = "Warning";
p.readable_value = "Device does not support self-test logging";
add_property(p);
@@ -2375,6 +2380,26 @@ ID Size Value Description
std::string SmartctlParser::get_data_full() const
{
return data_full_;
}
std::string SmartctlParser::get_error_msg() const
{
return Glib::ustring::compose(_("Cannot parse smartctl output: %1"), error_msg_);
}
const std::vector<StorageProperty>& SmartctlParser::get_properties() const
{
return properties_;
}
// adds a property into property list, looks up and sets its description.
// Yes, there's no place for this in the Parser, but whatever...
@@ -2389,6 +2414,34 @@ void SmartctlParser::add_property(StorageProperty p)
void SmartctlParser::set_data_full(const std::string& s)
{
data_full_ = s;
}
void SmartctlParser::set_data_section_info(const std::string& s)
{
data_section_info_ = s;
}
void SmartctlParser::set_data_section_data(const std::string& s)
{
data_section_data_ = s;
}
void SmartctlParser::set_error_msg(const std::string& s)
{
error_msg_ = s;
}
+7 -28
View File
@@ -89,10 +89,7 @@ class SmartctlParser {
/// Get "full" data, as passed to parse_full().
std::string get_data_full() const
{
return data_full_;
}
std::string get_data_full() const;
/*
std::string get_data_section_info() const
@@ -108,17 +105,11 @@ class SmartctlParser {
/// Get parse error message. Call this only if parsing doesn't succeed,
/// to get a friendly error message.
std::string get_error_msg() const
{
return "Cannot parse smartctl output: " + error_msg_;
}
std::string get_error_msg() const;
/// Get parse result properties
const std::vector<StorageProperty>& get_properties() const
{
return properties_;
}
const std::vector<StorageProperty>& get_properties() const;
@@ -130,31 +121,19 @@ class SmartctlParser {
/// Set "full" data ("smartctl -x" output)
void set_data_full(const std::string& s)
{
data_full_ = s;
}
void set_data_full(const std::string& s);
/// Set "info" section data ("smartctl -i" output, or the first part of "smartctl -x" output)
void set_data_section_info(const std::string& s)
{
data_section_info_ = s;
}
void set_data_section_info(const std::string& s);
/// Parse "data" section data (the second part of "smartctl -x" output).
void set_data_section_data(const std::string& s)
{
data_section_data_ = s;
}
void set_data_section_data(const std::string& s);
/// Set error message
void set_error_msg(const std::string& s)
{
error_msg_ = s;
}
void set_error_msg(const std::string& s);
@@ -9,6 +9,11 @@
/// \weakgroup applib
/// @{
// TODO Remove this in gtkmm4.
#include "local_glibmm.h"
#include <gtkmm.h> // compose()
#include <glibmm/i18n.h>
#include <algorithm>
#include "config.h" // CONFIG_*
@@ -104,7 +109,7 @@ std::string StorageDetector::fetch_basic_data(std::vector<StorageDevicePtr>& dri
for (auto& drive : drives) {
debug_out_info("app", "Retrieving basic information about the device...\n");
smartctl_ex->set_running_msg("Running %s on " + drive->get_device_with_type() + "...");
smartctl_ex->set_running_msg(Glib::ustring::compose(_("Running {command} on %1..."), drive->get_device_with_type()));
// don't show any errors here - we don't want a screen flood.
// no need for gui-based executors here, we already show the message in
@@ -130,8 +135,8 @@ std::string StorageDetector::fetch_basic_data(std::vector<StorageDevicePtr>& dri
debug_out_dump("app", "Device information for " << drive->get_device()
<< " (type: \"" << drive->get_type_argument() << "\"):\n"
<< "\tModel: " << drive->get_model_name() << "\n"
<< "\tDetected type: " << StorageDevice::get_type_readable_name(drive->get_detected_type()) << "\n"
<< "\tSMART status: " << StorageDevice::get_status_name(drive->get_smart_status()) << "\n"
<< "\tDetected type: " << StorageDevice::get_type_storable_name(drive->get_detected_type()) << "\n"
<< "\tSMART status: " << StorageDevice::get_status_displayable_name(drive->get_smart_status()) << "\n"
);
}
@@ -39,7 +39,7 @@ int main()
} else {
for (const auto& drive : drives) {
std::cerr << drive->get_device_with_type() <<
" (" << StorageDevice::get_type_readable_name(drive->get_detected_type()) << ")\n";
" (" << StorageDevice::get_type_storable_name(drive->get_detected_type()) << ")\n";
}
}
@@ -18,7 +18,8 @@
// TODO Remove this in gtkmm4.
#include "local_glibmm.h"
#include <glibmm.h> // Glib::shell_quote()
#include <glibmm.h> // Glib::shell_quote(), compose
#include <glibmm/i18n.h>
#include "executor_factory.h"
#include "storage_device.h"
@@ -38,7 +39,7 @@ inline std::string execute_tw_cli(const ExecutorFactoryPtr& ex_factory, const st
if (binary.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "tw_cli binary is not set in config.\n");
return "tw_cli binary is not specified in configuration.";
return Glib::ustring::compose(_("%1 binary is not specified in configuration."), "tw_cli");
}
std::vector<std::string> binaries; // binaries to try
@@ -50,8 +51,8 @@ inline std::string execute_tw_cli(const ExecutorFactoryPtr& ex_factory, const st
binaries.push_back(binary + ".x86");
#endif
for (std::size_t i = 0; i < binaries.size(); ++i) {
executor->set_command(Glib::shell_quote(binaries.at(i)), command_options);
for (const auto& bin : binaries) {
executor->set_command(Glib::shell_quote(bin), command_options);
if (!executor->execute() || !executor->get_error_msg().empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Error while executing tw_cli binary.\n");
@@ -64,7 +65,7 @@ inline std::string execute_tw_cli(const ExecutorFactoryPtr& ex_factory, const st
output = hz::string_trim_copy(hz::string_any_to_unix_copy(executor->get_stdout_str()));
if (output.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "tw_cli returned an empty output.\n");
return "tw_cli returned an empty output.";
return _("tw_cli returned an empty output.");
}
return std::string();
@@ -13,7 +13,7 @@
#if defined CONFIG_KERNEL_LINUX
#include <glibmm/i18n.h>
#include <algorithm> // std::find
#include <cstdio> // std::fgets(), std::FILE
#include <cerrno> // ENXIO
@@ -177,7 +177,7 @@ inline std::string read_proc_partitions_file(std::vector<std::string>& lines)
auto file = hz::fs::u8path(rconfig::get_data<std::string>("system/linux_proc_partitions_path"));
if (file.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Partitions file path is not set.\n");
return "Partitions file path is not set.";
return _("Partitions file path is not set.");
}
auto ec = read_proc_file_lines(file, lines);
@@ -202,7 +202,7 @@ inline std::string read_proc_devices_file(std::vector<std::string>& lines)
auto file = hz::fs::u8path(rconfig::get_data<std::string>("system/linux_proc_devices_path"));
if (file.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Devices file path is not set.\n");
return "Devices file path is not set.";
return _("Devices file path is not set.");
}
auto ec = read_proc_file_lines(file, lines);
@@ -229,7 +229,7 @@ inline std::string read_proc_scsi_scsi_file(std::vector< std::pair<int, std::str
auto file = hz::fs::u8path(rconfig::get_data<std::string>("system/linux_proc_scsi_scsi_path"));
if (file.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "SCSI file path is not set.\n");
return "SCSI file path is not set.";
return _("SCSI file path is not set.");
}
std::vector<std::string> lines;
@@ -276,7 +276,7 @@ inline std::string read_proc_scsi_sg_devices_file(std::vector<std::vector<int>>&
auto file = hz::fs::u8path(rconfig::get_data<std::string>("system/linux_proc_scsi_sg_devices_path"));
if (file.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Sg devices file path is not set.\n");
return "Sg devices file path is not set.";
return _("SCSI sg devices file path is not set.");
}
std::vector<std::string> lines;
@@ -13,7 +13,8 @@
#if !defined CONFIG_KERNEL_LINUX && !defined CONFIG_KERNEL_FAMILY_WINDOWS
#include <glibmm.h> // compose
#include <glibmm/i18n.h>
#include <algorithm> // std::sort
#if defined CONFIG_KERNEL_OPENBSD || defined CONFIG_KERNEL_NETBSD
@@ -46,14 +47,14 @@ std::string detect_drives_other(std::vector<StorageDevicePtr>& drives, const Exe
auto dev_dir = rconfig::get_data<std::string>(sdev_config_path);
if (dev_dir.empty()) {
debug_out_warn("app", DBG_FUNC_MSG << "Device directory path is not set.\n");
return "Device directory path is not set.";
return _("Device directory path is not set.");
}
auto dir = hz::fs::u8path(dev_dir);
std::error_code dummy_ec;
if (!hz::fs::exists(dir, dummy_ec)) {
debug_out_warn("app", DBG_FUNC_MSG << "Device directory doesn't exist.\n");
return "Device directory does not exist.";
return _("Device directory does not exist.");
}
@@ -206,7 +207,7 @@ std::string detect_drives_other(std::vector<StorageDevicePtr>& drives, const Exe
}
if (ec) {
debug_out_error("app", DBG_FUNC_MSG << "Cannot list device directory entries.\n");
return hz::string_sprintf("Cannot list device directory entries: %s", ec.message().c_str());
return Glib::ustring::compose(_("Cannot list device directory entries: %1"), ec.message());
}
@@ -255,9 +256,6 @@ std::string detect_drives_other(std::vector<StorageDevicePtr>& drives, const Exe
#endif
// TODO Sort using natural sort
std::sort(devices.begin(), devices.end());
for (auto& device : devices) {
drives.emplace_back(std::make_shared<StorageDevice>(device));
}
@@ -16,6 +16,7 @@
#include <windows.h> // CreateFileA(), CloseHandle(), etc...
#include <glibmm.h>
#include <glibmm/i18n.h>
#include <set>
#include <bitset>
#include <map>
@@ -184,7 +185,7 @@ std::string get_scan_open_multiport_devices(std::vector<StorageDevicePtr>& drive
if (smartctl_binary.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Smartctl binary is not set in config.\n");
return "Smartctl binary is not specified in configuration.";
return _("Smartctl binary is not specified in configuration.");
}
std::string smartctl_def_options = rconfig::get_data<std::string>("system/smartctl_options");
@@ -204,12 +205,13 @@ std::string get_scan_open_multiport_devices(std::vector<StorageDevicePtr>& drive
std::string output = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_ex->get_stdout_str()));
if (output.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Smartctl returned an empty output.\n");
return "Smartctl returned an empty output.";
return _("Smartctl returned an empty output.");
}
// if we've reached smartctl port limit (older versions may have smaller limits), abort.
if (app_pcre_match("/UNRECOGNIZED OPTION/mi", output)) {
return "Smartctl doesn't support --scan-open switch.";
// Our requirements list smartctl with --scan-open support, so this should never happen.
// Therefore, we don't translate it.
return "Unsupported smartctl version: Smartctl doesn't support --scan-open switch.";
}
@@ -274,7 +276,7 @@ inline std::string execute_areca_cli(const ExecutorFactoryPtr& ex_factory, const
output = hz::string_trim_copy(hz::string_any_to_unix_copy(executor->get_stdout_str()));
if (output.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "Areca cli returned an empty output.\n");
return "Areca CLI returned an empty output.";
return _("Areca CLI returned an empty output.");
}
return std::string();
@@ -409,7 +411,7 @@ inline std::string areca_cli_get_drives(const std::string& cli_binary, const std
}
if (format_type == FormatType::Unknown) {
debug_out_warn("app", "Could not read Areca CLI output: No valid header found.\n");
return "Could not read Areca CLI output: No valid header found.";
return _("Could not read Areca CLI output: No valid header found.");
}
// Note: These may not match the full model, but just the first part is sufficient for comparison with "N.A.".
+25 -22
View File
@@ -10,6 +10,8 @@ License: See LICENSE_gsmartcontrol.txt
/// @{
#include <unordered_map>
#include <glibmm.h> // compose()
#include <glibmm/i18n.h>
#include "rconfig/config.h"
#include "hz/string_algo.h" // string_trim_copy, string_any_to_unix_copy
@@ -25,7 +27,7 @@ License: See LICENSE_gsmartcontrol.txt
std::string StorageDevice::get_type_readable_name(DetectedType type)
std::string StorageDevice::get_type_storable_name(DetectedType type)
{
static const std::unordered_map<DetectedType, std::string> m {
{DetectedType::unknown, "unknown"},
@@ -41,13 +43,13 @@ std::string StorageDevice::get_type_readable_name(DetectedType type)
std::string StorageDevice::get_status_name(Status status)
std::string StorageDevice::get_status_displayable_name(Status status)
{
static const std::unordered_map<Status, std::string> m {
{Status::enabled, "Enabled"},
{Status::disabled, "Disabled"},
{Status::unsupported, "Unsupported"},
{Status::unknown, "Unknown"},
{Status::enabled, C_("status", "Enabled")},
{Status::disabled, C_("status", "Disabled")},
{Status::unsupported, C_("status", "Unsupported")},
{Status::unknown, C_("status", "Unknown")},
};
if (auto iter = m.find(status); iter != m.end()) {
return iter->second;
@@ -101,7 +103,7 @@ void StorageDevice::clear_fetched(bool including_outputs) {
std::string StorageDevice::fetch_basic_data_and_parse(const std::shared_ptr<CmdexSync>& smartctl_ex)
{
if (this->test_is_active_)
return "A test is currently being performed on this drive.";
return _("A test is currently being performed on this drive.");
this->clear_fetched(); // clear everything fetched before, including outputs
@@ -140,12 +142,12 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si
if (this->info_output_.empty()) {
debug_out_error("app", DBG_FUNC_MSG << "String to parse is empty.\n");
return "Cannot read information from an empty string.";
return _("Cannot read information from an empty string.");
}
std::string version, version_full;
if (!SmartctlParser::parse_version(this->info_output_, version, version_full)) // is this smartctl data at all?
return "Cannot get smartctl version information.";
return _("Cannot get smartctl version information.");
// Detect type. note: we can't distinguish between sata and scsi (on linux, for -d ata switch).
// Sample output line 1 (encountered on a CDRW drive):
@@ -256,7 +258,7 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si
std::string StorageDevice::fetch_data_and_parse(const std::shared_ptr<CmdexSync>& smartctl_ex)
{
if (this->test_is_active_)
return "A test is currently being performed on this drive.";
return _("A test is currently being performed on this drive.");
this->clear_fetched(); // clear everything fetched before, including outputs
@@ -348,7 +350,7 @@ StorageDevice::ParseStatus StorageDevice::get_parse_status() const
std::string StorageDevice::set_smart_enabled(bool b, const std::shared_ptr<CmdexSync>& smartctl_ex)
{
if (this->test_is_active_)
return "A test is currently being performed on this drive.";
return _("A test is currently being performed on this drive.");
// execute smartctl --smart=on|off /dev/...
// --saveauto=on is also executed when enabling smart.
@@ -375,10 +377,10 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm
return std::string(); // success
} else if (app_pcre_match("/^A mandatory SMART command failed/mi", output)) {
return "Mandatory SMART command failed.";
return _("Mandatory SMART command failed.");
}
return "Unknown error occurred.";
return _("Unknown error occurred.");
}
@@ -386,7 +388,7 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm
std::string StorageDevice::set_aodc_enabled(bool b, const std::shared_ptr<CmdexSync>& smartctl_ex)
{
if (this->test_is_active_)
return "A test is currently being performed on this drive.";
return _("A test is currently being performed on this drive.");
// execute smartctl --offlineauto=on|off /dev/...
// Output:
@@ -408,10 +410,10 @@ A mandatory SMART command failed: exiting. To continue, add one or more '-T perm
return std::string(); // success
} else if (app_pcre_match("/^A mandatory SMART command failed/mi", output)) {
return "Mandatory SMART command failed.";
return _("Mandatory SMART command failed.");
}
return "Unknown error occurred.";
return _("Unknown error occurred.");
}
@@ -487,7 +489,7 @@ StorageDevice::Status StorageDevice::get_aodc_status() const
aodc_status_ = status; // store to cache
debug_out_info("app", DBG_FUNC_MSG << "AODC status: " << get_status_name(status) << "\n");
debug_out_info("app", DBG_FUNC_MSG << "AODC status: " << get_status_displayable_name(status) << "\n");
return status;
}
@@ -539,14 +541,14 @@ std::string StorageDevice::get_device_base() const
std::string StorageDevice::get_device_with_type() const
{
if (this->get_is_virtual()) {
std::string ret = "Virtual";
std::string vf = this->get_virtual_filename();
ret += (" (" + (vf.empty() ? "[empty]" : vf) + ")");
/// Translators: %1 is filename
std::string ret = Glib::ustring::compose(C_("filename", "Virtual (%1)"), (vf.empty() ? (std::string("[") + C_("filename", "empty") + "]") : vf));
return ret;
}
std::string device = get_device();
if (!get_type_argument().empty()) {
device += " (" + get_type_argument() + ")";
device = Glib::ustring::compose(_("%1 (%2)"), device, get_type_argument());
}
return device;
}
@@ -615,7 +617,8 @@ std::string StorageDevice::format_drive_letters(bool with_volnames) const
for (const auto& iter : drive_letters_) {
drive_letters_decorated.push_back(std::string() + (char)std::toupper(iter.first) + ":");
if (with_volnames && !iter.second.empty()) {
drive_letters_decorated.back() += std::string(" (") + iter.second + ")";
// e.g. "C: (Local Drive)"
drive_letters_decorated.back() = Glib::ustring::compose(_("%1 (%2)"), drive_letters_decorated.back(), iter.second);
}
}
return hz::string_join(drive_letters_decorated, ", ");
@@ -809,7 +812,7 @@ std::string StorageDevice::execute_device_smartctl(const std::string& command_op
if (is_virtual_) {
debug_out_warn("app", DBG_FUNC_MSG << "Cannot execute smartctl on a virtual device.\n");
return "Cannot execute smartctl on a virtual device.";
return _("Cannot execute smartctl on a virtual device.");
}
std::string device = get_device();
+2 -2
View File
@@ -48,7 +48,7 @@ class StorageDevice {
/// This gives a string which can be displayed in outputs
static std::string get_type_readable_name(DetectedType type);
static std::string get_type_storable_name(DetectedType type);
/// Statuses of various states
@@ -60,7 +60,7 @@ class StorageDevice {
};
/// Get displayable name for Status.
static std::string get_status_name(Status status);
static std::string get_status_displayable_name(Status status);
/// Statuses of various parse states
+28 -19
View File
@@ -9,11 +9,16 @@
/// \weakgroup applib
/// @{
// TODO Remove this in gtkmm4.
#include "local_glibmm.h"
#include <map>
#include <ostream> // not iosfwd - it doesn't work
#include <sstream>
#include <iomanip>
#include <locale>
#include <glibmm.h> // compose
#include <glibmm/i18n.h>
#include "hz/string_num.h" // number_to_string
#include "hz/stream_cast.h" // stream_cast<>
@@ -88,23 +93,23 @@ std::ostream& operator<<(std::ostream& os, const StorageStatistic& p)
std::string StorageErrorBlock::get_readable_error_types(const std::vector<std::string>& types)
std::string StorageErrorBlock::get_displayable_error_types(const std::vector<std::string>& types)
{
static const std::map<std::string, std::string> m = {
{"ABRT", "Command aborted"},
{"AMNF", "Address mark not found"},
{"CCTO", "Command completion timed out"},
{"EOM", "End of media"},
{"ICRC", "Interface CRC error"},
{"IDNF", "Identity not found"},
{"ILI", "(Packet command-set specific)"},
{"MC", "Media changed"},
{"MCR", "Media change request"},
{"NM", "No media"},
{"obs", "Obsolete"},
{"TK0NF", "Track 0 not found"},
{"UNC", "Uncorrectable error in data"},
{"WP", "Media is write protected"},
{"ABRT", _("Command aborted")},
{"AMNF", _("Address mark not found")},
{"CCTO", _("Command completion timed out")},
{"EOM", _("End of media")},
{"ICRC", _("Interface CRC error")},
{"IDNF", _("Identity not found")},
{"ILI", _("(Packet command-set specific)")},
{"MC", _("Media changed")},
{"MCR", _("Media change request")},
{"NM", _("No media")},
{"obs", _("Obsolete")},
{"TK0NF", _("Track 0 not found")},
{"UNC", _("Uncorrectable error in data")},
{"WP", _("Media is write protected")},
};
std::vector<std::string> sv;
@@ -112,11 +117,15 @@ std::string StorageErrorBlock::get_readable_error_types(const std::vector<std::s
if (m.find(type) != m.end()) {
sv.push_back(m.at(type));
} else {
sv.push_back("[unknown type" + (type.empty() ? "" : (": " + type)) + "]");
std::string name = _("Uknown type");
if (!type.empty()) {
name = Glib::ustring::compose(_("Uknown type: %1"), type);
}
sv.push_back(name);
}
}
return hz::string_join(sv, ", ");
return hz::string_join(sv, _(", "));
}
@@ -162,7 +171,7 @@ std::ostream& operator<< (std::ostream& os, const StorageErrorBlock& b)
{
os << "Error number " << b.error_num << ": "
<< hz::string_join(b.reported_types, ", ")
<< " [" << StorageErrorBlock::get_readable_error_types(b.reported_types) << "]";
<< " [" << StorageErrorBlock::get_displayable_error_types(b.reported_types) << "]";
return os;
}
@@ -252,7 +261,7 @@ std::string StorageProperty::format_value(bool add_reported_too) const
if (std::holds_alternative<StorageSelftestEntry>(value))
return hz::stream_cast<std::string>(std::get<StorageSelftestEntry>(value));
return "[error]";
return "[internal_error]";
}
+6 -6
View File
@@ -178,7 +178,7 @@ class StorageErrorBlock {
public:
/// Get readable error types from reported types
static std::string get_readable_error_types(const std::vector<std::string>& types);
static std::string get_displayable_error_types(const std::vector<std::string>& types);
/// Get warning level (Warning) for an error type
static WarningLevel get_warning_level_for_error_type(const std::string& type);
@@ -229,7 +229,7 @@ class StorageSelftestEntry {
};
/// Get log entry status displayable name
static std::string get_status_name(Status s)
static std::string get_status_displayable_name(Status s)
{
static const std::unordered_map<Status, std::string> m {
{Status::unknown, "[unknown]"},
@@ -278,7 +278,7 @@ class StorageSelftestEntry {
/// Get error status as a string
std::string get_status_str() const
{
return (status == Status::unknown ? status_str : get_status_name(status));
return (status == Status::unknown ? status_str : get_status_displayable_name(status));
}
@@ -393,7 +393,7 @@ class StorageProperty {
return "error_block";
if (std::holds_alternative<StorageSelftestEntry>(value))
return "selftest_entry";
return "[error]";
return "[internal_error]";
}
@@ -449,13 +449,13 @@ class StorageProperty {
{
this->reported_name = rep_name;
this->generic_name = (gen_name.empty() ? this->reported_name : gen_name);
this->readable_name = (read_name.empty() ? this->reported_name : read_name);
this->displayable_name = (read_name.empty() ? this->reported_name : read_name);
}
std::string reported_name; ///< Property name as reported by smartctl.
std::string generic_name; ///< Generic (internal) name. May be same as reported_name, or something more program-identifiable.
std::string readable_name; ///< Readable property name. May be same as reported_name, or something more user-readable. Possibly translatable.
std::string displayable_name; ///< Readable property name. May be same as reported_name, or something more user-readable. Possibly translatable.
std::string description; ///< Property description (for tooltips, etc...)
@@ -12,6 +12,12 @@
#ifndef STORAGE_PROPERTY_COLORS_H
#define STORAGE_PROPERTY_COLORS_H
// TODO Remove this in gtkmm4.
#include "local_glibmm.h"
#include <glibmm.h> // compose()
#include <glibmm/i18n.h>
#include "storage_property.h"
@@ -62,23 +68,26 @@ inline bool app_property_get_label_highlight_color(WarningLevel warning, std::st
/// Format warning text, but without description
inline std::string storage_property_get_warning_reason(const StorageProperty& p)
{
std::string fg, start, stop;
std::string fg, start = "<b>", stop = "</b>";
if (app_property_get_label_highlight_color(p.warning, fg)) {
start = "<span color=\"" + fg + "\">";
stop = "</span>";
start += "<span color=\"" + fg + "\">";
stop = "</span>" + stop;
}
if (p.warning == WarningLevel::notice) {
return "<b>" + start + "Notice:" + stop + "</b> " + p.warning_reason;
/// Translators: %1 and %2 are HTML tags, %3 is a message.
return Glib::ustring::compose(_("%1Notice:%2 %3"), start, stop, p.warning_reason);
} else if (p.warning == WarningLevel::warning) {
return "<b>" + start + "Warning:" + stop + "</b> " + p.warning_reason;
/// Translators: %1 and %2 are HTML tags, %3 is a message.
return Glib::ustring::compose(_("%1Warning:%2 %3"), start, stop, p.warning_reason);
} else if (p.warning == WarningLevel::alert) {
return "<b>" + start + "ALERT:" + stop + "</b> " + p.warning_reason;
/// Translators: %1 and %2 are HTML tags, %3 is a message.
return Glib::ustring::compose(_("%1ALERT:%2 %3"), start, stop, p.warning_reason);
}
return "";
return std::string();
}
@@ -9,10 +9,15 @@
/// \weakgroup applib
/// @{
// TODO Remove this in gtkmm4.
#include "local_glibmm.h"
#include <utility>
#include <vector>
#include <map>
#include <unordered_map>
#include <glibmm.h>
#include <glibmm/i18n.h>
#include "hz/string_algo.h" // string_replace_copy
#include "applib/app_pcrecpp.h"
@@ -24,12 +29,13 @@
namespace {
const std::string s_unc_text = "When a drive encounters a surface error, it marks that sector as &quot;unstable&quot; (also known as &quot;pending reallocation&quot;). "
const std::string s_unc_text = Glib::Markup::escape_text(
_("When a drive encounters a surface error, it marks that sector as \"unstable\" (also known as \"pending reallocation\"). "
"If the sector is successfully read from or written to at some later point, it is unmarked. If the sector continues to be inaccessible, "
"the drive reallocates (remaps) it to a specially reserved area as soon as it has a chance (usually during write request or successful read), "
"transferring the data so that no changes are reported to the operating system. This is why you generally don't see &quot;bad blocks&quot; "
"transferring the data so that no changes are reported to the operating system. This is why you generally don't see \"bad blocks\" "
"on modern drives - if you do, it means that either they have not been remapped yet, or the drive is out of reserved area."
"\n\nNote: SSDs reallocate blocks as part of their normal operation, so low reallocation counts are not critical for them.";
"\n\nNote: SSDs reallocate blocks as part of their normal operation, so low reallocation counts are not critical for them."));
@@ -39,18 +45,18 @@ namespace {
AttributeDescription() = default;
/// Constructor
AttributeDescription(int32_t id_, StorageAttribute::DiskType type, std::string smartctl_name_,
std::string readable_name_, std::string generic_name_, std::string description_)
: id(id_), disk_type(type), smartctl_name(std::move(smartctl_name_)), readable_name(std::move(readable_name_)),
AttributeDescription(int32_t id_, StorageAttribute::DiskType type, std::string reported_name_,
std::string displayable_name_, std::string generic_name_, std::string description_)
: id(id_), disk_type(type), reported_name(std::move(reported_name_)), displayable_name(std::move(displayable_name_)),
generic_name(std::move(generic_name_)), description(std::move(description_))
{ }
int32_t id = -1; ///< e.g. 190
StorageAttribute::DiskType disk_type = StorageAttribute::DiskType::Any; ///< HDD-only, SSD-only or universal attribute
std::string smartctl_name; ///< e.g. Airflow_Temperature_Cel
std::string readable_name; ///< e.g. Airflow Temperature (C)
std::string generic_name; ///< Generic name to be set on the property.
std::string description; ///< Attribute description, can be "".
std::string reported_name; ///< e.g. Airflow_Temperature_Cel
std::string displayable_name; ///< e.g. Airflow Temperature (C). This is a translatable string.
std::string generic_name; ///< Generic name to be set on the property, e.g. "airflow_temperature". For lookups.
std::string description; ///< Attribute description, can be empty.
};
@@ -87,7 +93,8 @@ namespace {
"Average efficiency of a drive. Reduction of this attribute value can signal various internal problems.");
// Spin Up Time (smartctl) (some say it can also happen due to bad PSU or power connection (?))
add(3, "Spin_Up_Time", "Spin-Up Time", "",
"Average time of spindle spin-up time (from stopped to fully operational). Raw value may show this in milliseconds or seconds. Changes in spin-up time can reflect problems with the spindle motor or power.");
"Average time of spindle spin-up time (from stopped to fully operational). Raw value may show this in milliseconds or seconds. "
"Changes in spin-up time can reflect problems with the spindle motor or power.");
// Start/Stop Count (smartctl)
add(4, "Start_Stop_Count", "Start / Stop Count", "",
"Number of start/stop cycles of a spindle (Raw value). That is, number of drive spin-ups.");
@@ -100,7 +107,8 @@ namespace {
"Number of reallocated sectors (Raw value). High Raw value indicates an old age for an SSD.");
// SandForce SSD: Retired_Block_Count (smartctl)
add(5, StorageAttribute::DiskType::Ssd, "Retired_Block_Count", "Retired Block Rate", "attr_ssd_life_left",
"Indicates estimated remaining life of the drive. Normalized value is (100-100*RBC/MRB) where RBC is the number of retired blocks and MRB is the minimum required blocks.");
"Indicates estimated remaining life of the drive. Normalized value is (100-100*RBC/MRB) where RBC is the number of retired blocks "
"and MRB is the minimum required blocks.");
// Crucial/Micron SSD: Reallocate_NAND_Blk_Cnt (smartctl)
add(5, StorageAttribute::DiskType::Ssd, "Reallocate_NAND_Blk_Cnt", "Reallocated NAND Block Count", "",
"Number of reallocated blocks (Raw value). High Raw value indicates an old age for an SSD.");
@@ -118,7 +126,9 @@ namespace {
"Margin of a channel while reading data. The function of this attribute is not specified.");
// Seek Error Rate (smartctl)
add(7, StorageAttribute::DiskType::Hdd, "Seek_Error_Rate", "Seek Error Rate", "",
"Frequency of errors appearance while positioning. When a drive reads data, it positions heads in the needed place. If there is a failure in the mechanical positioning system, a seek error arises. More seek errors indicate worse condition of a disk surface and disk mechanical subsystem. The exact meaning of the Raw value is manufacturer-dependent.");
"Frequency of errors appearance while positioning. When a drive reads data, it positions heads in the needed place. "
"If there is a failure in the mechanical positioning system, a seek error arises. More seek errors indicate worse condition "
"of a disk surface and disk mechanical subsystem. The exact meaning of the Raw value is manufacturer-dependent.");
// Seek Time Performance (smartctl)
add(8, StorageAttribute::DiskType::Hdd, "Seek_Time_Performance", "Seek Time Performance", "",
"Average efficiency of seek operations of the magnetic heads. If this value is decreasing, it is a sign of problems in the hard disk drive mechanical subsystem.");
@@ -135,7 +145,8 @@ namespace {
"Number of retries of spin start attempts (Raw value). An increase of this attribute value is a sign of problems in the hard disk mechanical subsystem.");
// Calibration Retry Count (smartctl)
add(11, StorageAttribute::DiskType::Hdd, "Calibration_Retry_Count", "Calibration Retry Count", "",
"Number of times recalibration was requested, under the condition that the first attempt was unsuccessful (Raw value). A decrease is a sign of problems in the hard disk mechanical subsystem.");
"Number of times recalibration was requested, under the condition that the first attempt was unsuccessful (Raw value). "
"A decrease is a sign of problems in the hard disk mechanical subsystem.");
// Power Cycle Count (smartctl)
add(12, "Power_Cycle_Count", "Power Cycle Count", "",
"Number of complete power start / stop cycles of a drive.");
@@ -478,13 +489,15 @@ namespace {
"Number of load / unload cycles into Landing Zone position.");
// Temperature Celsius (smartctl) (same as 231). This is the most common one. Some Samsungs: 10xTemp.
add(194, "Temperature_Celsius", "Temperature (Celsius)", "attr_temperature_celsius",
"Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius). Increases in average drive temperature often signal spindle motor problems (unless the increases are caused by environmental factors).");
"Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius). "
"Increases in average drive temperature often signal spindle motor problems (unless the increases are caused by environmental factors).");
// Samsung SSD: Temperature Celsius (smartctl) (not sure about the value)
add(194, StorageAttribute::DiskType::Ssd, "Airflow_Temperature", "Airflow Temperature (Celsius)", "attr_temperature_celsius",
"Drive temperature (Celsius)");
// Temperature Celsius x 10 (smartctl)
add(194, "Temperature_Celsius_x10", "Temperature (Celsius) x 10", "attr_temperature_celsius_x10",
"Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius * 10). Increases in average drive temperature often signal spindle motor problems (unless the increases are caused by environmental factors).");
"Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius * 10). "
"Increases in average drive temperature often signal spindle motor problems (unless the increases are caused by environmental factors).");
// Smart Storage Systems SSD (smartctl)
add(194, StorageAttribute::DiskType::Ssd, "Proprietary_194", "Internal Attribute", "",
"This attribute has been reserved by vendor as internal.");
@@ -513,7 +526,8 @@ namespace {
"");
// Reallocation Event Count (smartctl)
add(196, StorageAttribute::DiskType::Any, "Reallocated_Event_Count", "Reallocation Event Count", "attr_reallocation_event_count",
"Number of reallocation (remap) operations. Raw value <i>should</i> show the total number of attempts (both successful and unsuccessful) to reallocate sectors. An increase in Raw value indicates a disk surface failure."
"Number of reallocation (remap) operations. Raw value <i>should</i> show the total number of attempts "
"(both successful and unsuccessful) to reallocate sectors. An increase in Raw value indicates a disk surface failure."
"\n\n" + s_unc_text);
// Indilinx Barefoot SSD: Erase_Failure_Blk_Ct (smartctl) (description?)
add(196, StorageAttribute::DiskType::Ssd, "Erase_Failure_Blk_Ct", "Erase Failure Block Count", "",
@@ -523,7 +537,9 @@ namespace {
"");
// Current Pending Sector Count (smartctl)
add(197, "Current_Pending_Sector", "Current Pending Sector Count", "attr_current_pending_sector_count",
"Number of &quot;unstable&quot; (waiting to be remapped) sectors (Raw value). If the unstable sector is subsequently read from or written to successfully, this value is decreased and the sector is not remapped. An increase in Raw value indicates a disk surface failure."
"Number of &quot;unstable&quot; (waiting to be remapped) sectors (Raw value). "
"If the unstable sector is subsequently read from or written to successfully, this value is decreased and the sector is not remapped. "
"An increase in Raw value indicates a disk surface failure."
"\n\n" + s_unc_text);
// Indilinx Barefoot SSD: Read_Failure_Blk_Ct (smartctl) (description?)
add(197, StorageAttribute::DiskType::Ssd, "Read_Failure_Blk_Ct", "Read Failure Block Count", "",
@@ -531,20 +547,24 @@ namespace {
// Samsung: Total_Pending_Sectors (smartctl). From smartctl man page:
// unlike Current_Pending_Sector, this won't decrease on reallocation.
add(197, "Total_Pending_Sectors", "Total Pending Sectors", "attr_total_pending_sectors",
"Number of &quot;unstable&quot; (waiting to be remapped) sectors and already remapped sectors (Raw value). An increase in Raw value indicates a disk surface failure."
"Number of &quot;unstable&quot; (waiting to be remapped) sectors and already remapped sectors (Raw value). "
"An increase in Raw value indicates a disk surface failure."
"\n\n" + s_unc_text);
// OCZ SSD (smartctl)
add(197, StorageAttribute::DiskType::Ssd, "Total_Unc_Read_Failures", "Total Uncorrectable Read Failures", "",
"");
// Offline Uncorrectable (smartctl)
add(198, "Offline_Uncorrectable", "Offline Uncorrectable", "attr_offline_uncorrectable",
"Number of sectors which couldn't be corrected during Offline Data Collection (Raw value). An increase in Raw value indicates a disk surface failure. "
"The value may be decreased automatically when the errors are corrected (e.g., when an unreadable sector is reallocated and the next Offline test is run to see the change)."
"Number of sectors which couldn't be corrected during Offline Data Collection (Raw value). "
"An increase in Raw value indicates a disk surface failure. "
"The value may be decreased automatically when the errors are corrected (e.g., when an unreadable sector is "
"reallocated and the next Offline test is run to see the change)."
"\n\n" + s_unc_text);
// Samsung: Offline Uncorrectable (smartctl). From smartctl man page:
// unlike Current_Pending_Sector, this won't decrease on reallocation.
add(198, "Total_Offl_Uncorrectabl", "Total Offline Uncorrectable", "attr_total_attr_offline_uncorrectable",
"Number of sectors which couldn't be corrected during Offline Data Collection (Raw value), currently and in the past. An increase in Raw value indicates a disk surface failure."
"Number of sectors which couldn't be corrected during Offline Data Collection (Raw value), currently and in the past. "
"An increase in Raw value indicates a disk surface failure."
"\n\n" + s_unc_text);
// Sandforce SSD: Uncorrectable_Sector_Ct (smartctl) (same description?)
add(198, StorageAttribute::DiskType::Ssd, "Uncorrectable_Sector_Ct");
@@ -560,7 +580,8 @@ namespace {
add(198, StorageAttribute::DiskType::Hdd, "Off-line_Scan_UNC_Sector_Ct");
// UDMA CRC Error Count (smartctl)
add(199, "UDMA_CRC_Error_Count", "UDMA CRC Error Count", "",
"Number of errors in data transfer via the interface cable in UDMA mode, as determined by ICRC (Interface Cyclic Redundancy Check) (Raw value).");
"Number of errors in data transfer via the interface cable in UDMA mode, as determined by ICRC "
"(Interface Cyclic Redundancy Check) (Raw value).");
// Sandforce SSD: SATA_CRC_Error_Count (smartctl) (description?)
add(199, "SATA_CRC_Error_Count", "SATA CRC Error Count", "",
"Number of errors in data transfer via the SATA interface cable (Raw value).");
@@ -763,7 +784,8 @@ namespace {
"Number of times the head armature entered / left the data zone.");
// Load Friction (smartctl)
add(224, StorageAttribute::DiskType::Hdd, "Load_Friction", "Load Friction", "",
"Resistance caused by friction in mechanical parts while operating. An increase of Raw value may mean that there is a problem with the mechanical subsystem of the drive.");
"Resistance caused by friction in mechanical parts while operating. An increase of Raw value may mean that there is "
"a problem with the mechanical subsystem of the drive.");
// OCZ SSD (smartctl) (description?)
add(224, StorageAttribute::DiskType::Ssd, "In_Warranty", "In Warranty", "",
"");
@@ -781,7 +803,8 @@ namespace {
"");
// Load-in Time (smartctl)
add(226, StorageAttribute::DiskType::Hdd, "Load-in_Time", "Load-in Time", "",
"Total time of loading on the magnetic heads actuator. Indicates total time in which the drive was under load (on the assumption that the magnetic heads were in operating mode and out of the parking area).");
"Total time of loading on the magnetic heads actuator. Indicates total time in which the drive was under load "
"(on the assumption that the magnetic heads were in operating mode and out of the parking area).");
// Intel SSD: Intel_Internal (smartctl)
add(226, StorageAttribute::DiskType::Ssd, "Intel_Internal", "Internal Attribute", "",
"This attribute has been reserved by vendor as internal.");
@@ -832,7 +855,8 @@ namespace {
"");
// Temperature (Some drives) (smartctl)
add(231, "Temperature_Celsius", "Temperature", "attr_temperature_celsius",
"Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius). Increases in average drive temperature often signal spindle motor problems (unless the increases are caused by environmental factors).");
"Drive temperature. The Raw value shows built-in heat sensor registrations (in Celsius). "
"Increases in average drive temperature often signal spindle motor problems (unless the increases are caused by environmental factors).");
// Sandforce SSD: SSD_Life_Left
add(231, StorageAttribute::DiskType::Ssd, "SSD_Life_Left", "SSD Life Left", "attr_ssd_life_left",
"A measure of drive's estimated life left. A Normalized value of 100 indicates a new drive. "
@@ -1031,55 +1055,57 @@ namespace {
/// Add an attribute description to the attribute database
void add(int32_t id, const std::string& smartctl_name, const std::string& readable_name,
const std::string& generic_name, const std::string& description)
void add(AttributeDescription descr)
{
add(AttributeDescription(id, StorageAttribute::DiskType::Any, smartctl_name, readable_name, generic_name, description));
id_db[descr.id].emplace_back(std::move(descr));
}
/// Add an attribute description to the attribute database
void add(int32_t id, std::string reported_name, std::string displayable_name,
std::string generic_name, std::string description)
{
add(AttributeDescription(id, StorageAttribute::DiskType::Any,
std::move(reported_name), std::move(displayable_name), std::move(generic_name), std::move(description)));
}
/// Add a previously added description to the attribute database under a
/// different smartctl name (fill the other members from the previous attribute).
// void add(int32_t id, const std::string& smartctl_name)
// void add(int32_t id, const std::string& reported_name)
// {
// auto iter = id_db.find(id);
// DBG_ASSERT(iter != id_db.end() && !iter->second.empty());
// if (iter != id_db.end() || iter->second.empty()) {
// AttributeDescription attr = iter->second.front();
// add(AttributeDescription(id, StorageAttribute::DiskType::Any, smartctl_name, attr.readable_name, attr.generic_name, attr.description));
// add(AttributeDescription(id, StorageAttribute::DiskType::Any, reported_name, attr.displayable_name, attr.generic_name, attr.description));
// }
// }
/// Add an attribute description to the attribute database
void add(int32_t id, StorageAttribute::DiskType type, const std::string& smartctl_name, const std::string& readable_name,
const std::string& generic_name, const std::string& description)
void add(int32_t id, StorageAttribute::DiskType type, std::string reported_name, std::string displayable_name,
std::string generic_name, std::string description)
{
add(AttributeDescription(id, type, smartctl_name, readable_name, generic_name, description));
add(AttributeDescription(id, type, std::move(reported_name), std::move(displayable_name), std::move(generic_name), std::move(description)));
}
/// Add a previously added description to the attribute database under a
/// different smartctl name (fill the other members from the previous attribute).
void add(int32_t id, StorageAttribute::DiskType type, const std::string& smartctl_name)
void add(int32_t id, StorageAttribute::DiskType type, std::string reported_name)
{
auto iter = id_db.find(id);
DBG_ASSERT(iter != id_db.end() && !iter->second.empty());
if (iter != id_db.end() || iter->second.empty()) {
AttributeDescription attr = iter->second.front();
add(AttributeDescription(id, type, smartctl_name, attr.readable_name, attr.generic_name, attr.description));
add(AttributeDescription(id, type,
std::move(reported_name), std::move(attr.displayable_name), std::move(attr.generic_name), std::move(attr.description)));
}
}
/// Add an attribute description to the attribute database
void add(const AttributeDescription& descr)
{
id_db[descr.id].push_back(descr);
}
/// Find the description by smartctl name or id, merging them if they're partial.
AttributeDescription find(const std::string& smartctl_name, int32_t id, StorageAttribute::DiskType type) const
AttributeDescription find(const std::string& reported_name, int32_t id, StorageAttribute::DiskType type) const
{
// search by ID first
auto id_iter = id_db.find(id);
@@ -1104,7 +1130,7 @@ namespace {
// search by smartctl name in ID-supplied vector
for (const auto& attr_iter : type_matched) {
// compare them case-insensitively, just in case
if ( hz::string_to_lower_copy(attr_iter.smartctl_name) == hz::string_to_lower_copy(smartctl_name)) {
if ( hz::string_to_lower_copy(attr_iter.reported_name) == hz::string_to_lower_copy(reported_name)) {
return attr_iter; // found it
}
}
@@ -1133,14 +1159,14 @@ namespace {
StatisticDescription() = default;
/// Constructor
StatisticDescription(std::string smartctl_name_,
std::string readable_name_, std::string generic_name_, std::string description_)
: smartctl_name(std::move(smartctl_name_)), readable_name(std::move(readable_name_)),
StatisticDescription(std::string reported_name_,
std::string displayable_name_, std::string generic_name_, std::string description_)
: reported_name(std::move(reported_name_)), displayable_name(std::move(displayable_name_)),
generic_name(std::move(generic_name_)), description(std::move(description_))
{ }
std::string smartctl_name; ///< e.g. Highest Temperature
std::string readable_name; ///< e.g. Highest Temperature (C)
std::string reported_name; ///< e.g. Highest Temperature
std::string displayable_name; ///< e.g. Highest Temperature (C)
std::string generic_name; ///< Generic name to be set on the property.
std::string description; ///< Attribute description, can be "".
};
@@ -1319,25 +1345,25 @@ namespace {
/// Add an attribute description to the attribute database
void add(const std::string& smartctl_name, const std::string& readable_name,
void add(const std::string& reported_name, const std::string& displayable_name,
const std::string& generic_name, const std::string& description)
{
add(StatisticDescription(smartctl_name, readable_name, generic_name, description));
add(StatisticDescription(reported_name, displayable_name, generic_name, description));
}
/// Add an devstat entry description to the devstat database
void add(const StatisticDescription& descr)
{
devstat_db[descr.smartctl_name] = descr;
devstat_db[descr.reported_name] = descr;
}
/// Find the description by smartctl name or id, merging them if they're partial.
StatisticDescription find(const std::string& smartctl_name) const
StatisticDescription find(const std::string& reported_name) const
{
// search by ID first
auto iter = devstat_db.find(smartctl_name);
auto iter = devstat_db.find(reported_name);
if (iter == devstat_db.end()) {
return StatisticDescription(); // not found
}
@@ -1347,7 +1373,7 @@ namespace {
private:
std::map<std::string, StatisticDescription> devstat_db; ///< smartctl_name => devstat entry description
std::map<std::string, StatisticDescription> devstat_db; ///< reported_name => devstat entry description
};
@@ -1394,11 +1420,11 @@ namespace {
{
AttributeDescription attr = s_attribute_db.find(p.reported_name, p.get_value<StorageAttribute>().id, disk_type);
std::string humanized_smartctl_name;
std::string humanized_reported_name;
std::string ssd_hdd_str;
bool known_by_smartctl = !app_pcre_match("/Unknown_(HDD|SSD)_?Attr.*/i", p.reported_name, &ssd_hdd_str);
if (known_by_smartctl) {
humanized_smartctl_name = " " + p.reported_name + " "; // spaces are for easy replacements
humanized_reported_name = " " + p.reported_name + " "; // spaces are for easy replacements
static std::unordered_map<std::string, std::string> replacement_map = {
{"_", " "},
@@ -1420,23 +1446,23 @@ namespace {
{" Min ", " Minimum "}
};
hz::string_replace_array(humanized_smartctl_name, replacement_map);
hz::string_trim(humanized_smartctl_name);
hz::string_remove_adjacent_duplicates(humanized_smartctl_name, ' '); // may happen with slashes
hz::string_replace_array(humanized_reported_name, replacement_map);
hz::string_trim(humanized_reported_name);
hz::string_remove_adjacent_duplicates(humanized_reported_name, ' '); // may happen with slashes
}
if (attr.readable_name.empty()) {
if (attr.displayable_name.empty()) {
// try to display something sensible (use humanized form of smartctl name)
if (!humanized_smartctl_name.empty()) {
attr.readable_name = humanized_smartctl_name;
if (!humanized_reported_name.empty()) {
attr.displayable_name = humanized_reported_name;
} else { // unknown to smartctl
if (hz::string_to_upper_copy(ssd_hdd_str) == "SSD") {
attr.readable_name = "Unknown SSD Attribute";
attr.displayable_name = "Unknown SSD Attribute";
} else if (hz::string_to_upper_copy(ssd_hdd_str) == "HDD") {
attr.readable_name = "Unknown HDD Attribute";
attr.displayable_name = "Unknown HDD Attribute";
} else {
attr.readable_name = "Unknown Attribute";
attr.displayable_name = "Unknown Attribute";
}
}
}
@@ -1451,8 +1477,8 @@ namespace {
if (known_by_smartctl) {
// See if humanized smartctl-reported name looks like our found name.
// If not, show it in description.
std::string match = " " + humanized_smartctl_name + " ";
std::string against = " " + attr.readable_name + " ";
std::string match = " " + humanized_reported_name + " ";
std::string against = " " + attr.displayable_name + " ";
static std::unordered_map<std::string, std::string> replacement_map = {
{" Percent ", " % "},
@@ -1467,10 +1493,10 @@ namespace {
same_names = app_pcre_match("/^" + app_pcre_escape(match) + "$/i", against);
}
std::string descr = std::string("<b>") + attr.readable_name + "</b>";
std::string descr = std::string("<b>") + attr.displayable_name + "</b>";
if (!same_names) {
std::string smartctl_name_for_descr = hz::string_replace_copy(p.reported_name, '_', ' ');
descr += "\n<small>Reported by smartctl as <b>\"" + smartctl_name_for_descr + "\"</b></small>\n";
std::string reported_name_for_descr = hz::string_replace_copy(p.reported_name, '_', ' ');
descr += "\n<small>Reported by smartctl as <b>\"" + reported_name_for_descr + "\"</b></small>\n";
}
descr += "\n";
descr += attr.description;
@@ -1478,7 +1504,7 @@ namespace {
attr.description = descr;
}
p.readable_name = attr.readable_name;
p.displayable_name = attr.displayable_name;
p.set_description(attr.description);
p.generic_name = attr.generic_name;
}
@@ -1491,14 +1517,14 @@ namespace {
{
StatisticDescription sd = s_devstat_db.find(p.reported_name);
std::string readable_name = (sd.readable_name.empty() ? sd.smartctl_name : sd.readable_name);
std::string displayable_name = (sd.displayable_name.empty() ? sd.reported_name : sd.displayable_name);
bool found = !sd.description.empty();
if (!found) {
sd.description = "No description is available for this attribute.";
} else {
std::string descr = std::string("<b>") + readable_name + "</b>\n";
std::string descr = std::string("<b>") + displayable_name + "</b>\n";
descr += sd.description;
if (p.get_value<StorageStatistic>().is_normalized()) {
@@ -1508,8 +1534,8 @@ namespace {
sd.description = descr;
}
if (!readable_name.empty()) {
p.readable_name = readable_name;
if (!displayable_name.empty()) {
p.displayable_name = displayable_name;
}
p.set_description(sd.description);
p.generic_name = sd.generic_name;
@@ -1538,7 +1564,8 @@ bool storage_property_autoset_description(StorageProperty& p, StorageAttribute::
|| auto_set(p, "device_model", "Device model")
|| auto_set(p, "serial_number", "Serial number, unique to each physical drive")
|| auto_set(p, "capacity", "User-serviceable drive capacity as reported to an operating system")
|| auto_set(p, "in_smartctl_db", "Whether the device is in smartctl database or not. If it is, additional information may be provided; otherwise, Raw values of some attributes may be incorrectly formatted.")
|| auto_set(p, "in_smartctl_db", "Whether the device is in smartctl database or not. "
"If it is, additional information may be provided; otherwise, Raw values of some attributes may be incorrectly formatted.")
|| auto_set(p, "smart_supported", "Whether the device supports SMART. If not, then only very limited information will be available.")
|| auto_set(p, "smart_enabled", "Whether the device has SMART enabled. If not, most of the reported values will be incorrect.")
|| auto_set(p, "aam_feature", "Automatic Acoustic Management (AAM) feature")
@@ -1550,7 +1577,7 @@ bool storage_property_autoset_description(StorageProperty& p, StorageAttribute::
// set just its name as a tooltip
if (!found) {
p.set_description(p.readable_name);
p.set_description(p.displayable_name);
found = true;
}
@@ -1569,7 +1596,8 @@ bool storage_property_autoset_description(StorageProperty& p, StorageAttribute::
"This value shows the estimated time required to perform this operation in idle conditions. A value of 0 means unsupported.")
|| auto_set(p, "short_total_time_length", "This value shows the estimated time required to perform a short self-test in idle conditions. A value of 0 means unsupported.")
|| auto_set(p, "long_total_time_length", "This value shows the estimated time required to perform a long self-test in idle conditions. A value of 0 means unsupported.")
|| auto_set(p, "conveyance_total_time_length", "This value shows the estimated time required to perform a conveyance self-test in idle conditions. A value of 0 means unsupported.")
|| auto_set(p, "conveyance_total_time_length", "This value shows the estimated time required to perform a conveyance self-test in idle conditions. "
"A value of 0 means unsupported.")
|| auto_set(p, "last_selftest_cap_group", "Status of the last self-test run.")
|| auto_set(p, "offline_cap_group", "Drive properties related to Offline Data Collection and self-tests.")
|| auto_set(p, "smart_cap_group", "Drive properties related to SMART handling.")
@@ -1578,7 +1606,7 @@ bool storage_property_autoset_description(StorageProperty& p, StorageAttribute::
break;
case StorageProperty::SubSection::attributes:
found = auto_set(p, "data_structure_version", p.readable_name.c_str());
found = auto_set(p, "data_structure_version", p.displayable_name.c_str());
if (!found) {
auto_set_attr(p, disk_type);
found = true; // true, because auto_set_attr() may set "Unknown attribute", which is still "found".
@@ -1590,20 +1618,20 @@ bool storage_property_autoset_description(StorageProperty& p, StorageAttribute::
break;
case StorageProperty::SubSection::error_log:
found = auto_set(p, "error_log_version", p.readable_name.c_str())
found = auto_set(p, "error_log_version", p.displayable_name.c_str())
|| auto_set(p, "error_log_error_count", "Number of errors in error log. Note: Some manufacturers may list completely harmless errors in this log "
"(e.g., command invalid, not implemented, etc...).");
// || auto_set(p, "error_log_unsupported", "This device does not support error logging."); // the property text already says that
if (p.is_value_type<StorageErrorBlock>()) {
for (size_t i = 0; i < p.get_value<StorageErrorBlock>().reported_types.size(); ++i) {
p.set_description(StorageErrorBlock::get_readable_error_types(p.get_value<StorageErrorBlock>().reported_types));
p.set_description(StorageErrorBlock::get_displayable_error_types(p.get_value<StorageErrorBlock>().reported_types));
found = true;
}
}
break;
case StorageProperty::SubSection::selftest_log:
found = auto_set(p, "selftest_log_version", p.readable_name.c_str())
found = auto_set(p, "selftest_log_version", p.displayable_name.c_str())
|| auto_set(p, "selftest_num_entries", "Number of tests in selftest log. Note: The number of entries may be limited to the newest manual tests.");
// || auto_set(p, "selftest_log_unsupported", "This device does not support self-test logging."); // the property text already says that
break;
@@ -1693,45 +1721,53 @@ WarningLevel storage_property_autoset_warning(StorageProperty& p)
// Reallocated Sector Count
if (attr_match(p, "attr_reallocated_sector_count") && attr.raw_value_int > 0) {
w = WarningLevel::notice;
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors.";
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. "
"This could be an indication of future failures and/or potential data loss in bad sectors.";
// Spin-up Retry Count
} else if (attr_match(p, "attr_spin_up_retry_count") && attr.raw_value_int > 0) {
w = WarningLevel::notice;
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. Your drive may have problems spinning up, which could lead to a complete mechanical failure. Please back up.";
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. "
"Your drive may have problems spinning up, which could lead to a complete mechanical failure. Please back up.";
// Soft Read Error Rate
} else if (attr_match(p, "attr_soft_read_error_rate") && attr.raw_value_int > 0) {
w = WarningLevel::notice;
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors.";
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. "
"This could be an indication of future failures and/or potential data loss in bad sectors.";
// Temperature (for some it may be 10xTemp, so limit the upper bound.)
} else if (attr_match(p, "attr_temperature_celsius")
&& attr.raw_value_int > 50 && attr.raw_value_int <= 120) { // 50C
w = WarningLevel::notice;
reason = "The temperature of the drive is higher than 50 degrees Celsius. This may shorten its lifespan and cause damage under severe load. Please install a cooling solution.";
reason = "The temperature of the drive is higher than 50 degrees Celsius. "
"This may shorten its lifespan and cause damage under severe load. Please install a cooling solution.";
// Temperature (for some it may be 10xTemp, so limit the upper bound.)
} else if (attr_match(p, "attr_temperature_celsius_x10") && attr.raw_value_int > 500) { // 50C
w = WarningLevel::notice;
reason = "The temperature of the drive is higher than 50 degrees Celsius. This may shorten its lifespan and cause damage under severe load. Please install a cooling solution.";
reason = "The temperature of the drive is higher than 50 degrees Celsius. "
"This may shorten its lifespan and cause damage under severe load. Please install a cooling solution.";
// Reallocation Event Count
} else if (attr_match(p, "attr_reallocation_event_count") && attr.raw_value_int > 0) {
w = WarningLevel::notice;
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors.";
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. "
"This could be an indication of future failures and/or potential data loss in bad sectors.";
// Current Pending Sector Count
} else if ((attr_match(p, "attr_current_pending_sector_count") || attr_match(p, "attr_total_pending_sectors"))
&& attr.raw_value_int > 0) {
w = WarningLevel::notice;
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors.";
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. "
"This could be an indication of future failures and/or potential data loss in bad sectors.";
// Uncorrectable Sector Count
} else if ((attr_match(p, "attr_offline_uncorrectable") || attr_match(p, "attr_total_attr_offline_uncorrectable"))
&& attr.raw_value_int > 0) {
w = WarningLevel::notice;
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors.";
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. "
"This could be an indication of future failures and/or potential data loss in bad sectors.";
// SSD Life Left (%)
} else if ((attr_match(p, "attr_ssd_life_left"))
@@ -1764,7 +1800,8 @@ WarningLevel storage_property_autoset_warning(StorageProperty& p)
// nothing. we don't warn about e.g. temperature increase in the past
} else { // pre-fail
w = WarningLevel::warning; // there was a problem, it got corrected (hopefully)
reason = "The drive had a failing pre-fail attribute, but it has been restored to a normal value. This may be a serious problem, you should consider replacing the drive.";
reason = "The drive had a failing pre-fail attribute, but it has been restored to a normal value. "
"This may be a serious problem, you should consider replacing the drive.";
}
}
}
@@ -1913,7 +1950,8 @@ WarningLevel storage_property_autoset_warning(StorageProperty& p)
// Current temperature
if (name_match(p, "sct_temperature_celsius") && p.get_value<int64_t>() > 50) { // 50C
w = WarningLevel::notice;
reason = "The temperature of the drive is higher than 50 degrees Celsius. This may shorten its lifespan and cause damage under severe load. Please install a cooling solution.";
reason = "The temperature of the drive is higher than 50 degrees Celsius. "
"This may shorten its lifespan and cause damage under severe load. Please install a cooling solution.";
}
break;
+1 -1
View File
@@ -175,7 +175,7 @@ void GscAddDeviceWindow::on_device_name_browse_button_clicked()
if (!entry)
return;
auto path = hz::fs::u8path(entry->get_text());
auto path = hz::fs::u8path(std::string(entry->get_text()));
int result = 0;
+16 -16
View File
@@ -918,7 +918,7 @@ void GscInfoWindow::fill_ui_general(const std::vector<StorageProperty>& props)
name->set_alignment(Gtk::ALIGN_END); // right-align
name->set_selectable(true);
name->set_can_focus(false);
name->set_markup("<b>" + Glib::Markup::escape_text(p.readable_name) + "</b>");
name->set_markup("<b>" + Glib::Markup::escape_text(p.displayable_name) + "</b>");
// If the above is Label, then this has to be Label too, else it will shrink
// and "name" will take most of the horizontal space. If "name" is set to shrink,
@@ -1059,7 +1059,7 @@ void GscInfoWindow::fill_ui_attributes(const std::vector<StorageProperty>& props
// add non-attribute-type properties to label above
if (!p.is_value_type<StorageAttribute>()) {
label_strings.emplace_back(p.readable_name + ": " + p.format_value(), &p);
label_strings.emplace_back(p.displayable_name + ": " + p.format_value(), &p);
if (int(p.warning) > int(max_tab_warning))
max_tab_warning = p.warning;
@@ -1079,7 +1079,7 @@ void GscInfoWindow::fill_ui_attributes(const std::vector<StorageProperty>& props
Gtk::TreeRow row = *(list_store->append());
row[col_id] = attr.id;
row[col_name] = p.readable_name;
row[col_name] = p.displayable_name;
row[col_flag_value] = attr.flag; // it's a string, not int.
row[col_value] = (attr.value.has_value() ? hz::number_to_string_locale(attr.value.value()) : "-");
row[col_worst] = (attr.worst.has_value() ? hz::number_to_string_locale(attr.worst.value()) : "-");
@@ -1168,7 +1168,7 @@ void GscInfoWindow::fill_ui_statistics(const std::vector<StorageProperty>& props
// add non-entry-type properties to label above
if (!p.is_value_type<StorageStatistic>()) {
label_strings.emplace_back(p.readable_name + ": " + p.format_value(), &p);
label_strings.emplace_back(p.displayable_name + ": " + p.format_value(), &p);
if (int(p.warning) > int(max_tab_warning))
max_tab_warning = p.warning;
@@ -1178,7 +1178,7 @@ void GscInfoWindow::fill_ui_statistics(const std::vector<StorageProperty>& props
Gtk::TreeRow row = *(list_store->append());
const auto& st = p.get_value<StorageStatistic>();
row[col_description] = (st.is_header ? p.readable_name : (" " + p.readable_name));
row[col_description] = (st.is_header ? p.displayable_name : (" " + p.displayable_name));
row[col_value] = st.format_value();
row[col_flags] = st.flags; // it's a string, not int.
row[col_page_offset] = (st.is_header ? std::string()
@@ -1228,7 +1228,7 @@ void GscInfoWindow::fill_ui_self_test_info()
auto test_ioffline = std::make_shared<SelfTest>(drive, SelfTest::TestType::immediate_offline);
if (test_ioffline->is_supported()) {
row = *(test_combo_model->append());
row[test_combo_col_name] = SelfTest::get_test_name(SelfTest::TestType::immediate_offline);
row[test_combo_col_name] = SelfTest::get_test_displayable_name(SelfTest::TestType::immediate_offline);
row[test_combo_col_description] =
_("Immediate Offline Test (also known as Immediate Offline Data Collection)"
" is the manual version of Automatic Offline Data Collection, which, if enabled, is automatically run"
@@ -1240,7 +1240,7 @@ void GscInfoWindow::fill_ui_self_test_info()
auto test_short = std::make_shared<SelfTest>(drive, SelfTest::TestType::short_test);
if (test_short->is_supported()) {
row = *(test_combo_model->append());
row[test_combo_col_name] = SelfTest::get_test_name(SelfTest::TestType::short_test);
row[test_combo_col_name] = SelfTest::get_test_displayable_name(SelfTest::TestType::short_test);
row[test_combo_col_description] =
_("Short self-test consists of a collection of test routines that have the highest chance"
" of detecting drive problems. Its result is reported in the Self-Test Log."
@@ -1254,7 +1254,7 @@ void GscInfoWindow::fill_ui_self_test_info()
auto test_long = std::make_shared<SelfTest>(drive, SelfTest::TestType::long_test);
if (test_long->is_supported()) {
row = *(test_combo_model->append());
row[test_combo_col_name] = SelfTest::get_test_name(SelfTest::TestType::long_test);
row[test_combo_col_name] = SelfTest::get_test_displayable_name(SelfTest::TestType::long_test);
row[test_combo_col_description] =
_("Extended self-test examines complete disk surface and performs various test routines"
" built into the drive. Its result is reported in the Self-Test Log.");
@@ -1264,7 +1264,7 @@ void GscInfoWindow::fill_ui_self_test_info()
auto test_conveyance = std::make_shared<SelfTest>(drive, SelfTest::TestType::conveyance);
if (test_conveyance->is_supported()) {
row = *(test_combo_model->append());
row[test_combo_col_name] = SelfTest::get_test_name(SelfTest::TestType::conveyance);
row[test_combo_col_name] = SelfTest::get_test_displayable_name(SelfTest::TestType::conveyance);
row[test_combo_col_description] =
_("Conveyance self-test is intended to identify damage incurred during transporting of the drive.");
row[test_combo_col_self_test] = test_conveyance;
@@ -1360,7 +1360,7 @@ void GscInfoWindow::fill_ui_self_test_log(const std::vector<StorageProperty>& pr
// add non-entry properties to label above
if (!p.is_value_type<StorageSelftestEntry>()) {
label_strings.emplace_back(p.readable_name + ": " + p.format_value(), &p);
label_strings.emplace_back(p.displayable_name + ": " + p.format_value(), &p);
if (int(p.warning) > int(max_tab_warning))
max_tab_warning = p.warning;
@@ -1494,7 +1494,7 @@ void GscInfoWindow::fill_ui_error_log(const std::vector<StorageProperty>& props)
// add non-tree properties to label above
} else if (!p.is_value_type<StorageErrorBlock>()) {
label_strings.emplace_back(p.readable_name + ": " + p.format_value(), &p);
label_strings.emplace_back(p.displayable_name + ": " + p.format_value(), &p);
if (p.generic_name == "error_log_error_count")
label_strings.back().label += " "s + _("(Note: The number of entries may be limited to the newest ones)");
@@ -1507,7 +1507,7 @@ void GscInfoWindow::fill_ui_error_log(const std::vector<StorageProperty>& props)
row[col_num] = eb.error_num;
row[col_hours] = eb.format_lifetime_hours();
row[col_state] = eb.device_state;
row[col_type] = StorageErrorBlock::get_readable_error_types(eb.reported_types);
row[col_type] = StorageErrorBlock::get_displayable_error_types(eb.reported_types);
row[col_details] = (type_details.empty() ? "-" : type_details); // e.g. OBS has no details
// There are no descriptions in self-test log entries, so don't display
// "No description available" for all of them.
@@ -1657,7 +1657,7 @@ WarningLevel GscInfoWindow::fill_ui_capabilities(const std::vector<StorageProper
if (p.section != StorageProperty::Section::data || p.subsection != StorageProperty::SubSection::capabilities)
continue;
Glib::ustring name = p.readable_name;
Glib::ustring name = p.displayable_name;
std::string flag_value;
Glib::ustring str_value;
@@ -1908,7 +1908,7 @@ gboolean GscInfoWindow::test_idle_callback(void* data)
result_msg = "<b>"s + _("Test was manually aborted.") + "</b>"; // it's a StatusSeverity::none message
} else {
result_msg = Glib::ustring::compose(_("<b>Test result:</b> %1."), StorageSelftestEntry::get_status_name(status));
result_msg = Glib::ustring::compose(_("<b>Test result:</b> %1."), StorageSelftestEntry::get_status_displayable_name(status));
// It may not reach 100% somehow, so do it manually.
if (test_completion_progressbar)
@@ -1985,7 +1985,7 @@ void GscInfoWindow::on_test_execute_button_clicked()
std::string error_msg = test->start(ex); // this runs update() too.
if (!error_msg.empty()) {
/// Translators: %1 is test name
gui_show_error_dialog(Glib::ustring::compose(_("Cannot run %1"), SelfTest::get_test_name(test->get_test_type())), error_msg, this);
gui_show_error_dialog(Glib::ustring::compose(_("Cannot run %1"), SelfTest::get_test_displayable_name(test->get_test_type())), error_msg, this);
return;
}
@@ -2047,7 +2047,7 @@ void GscInfoWindow::on_test_stop_button_clicked()
std::string error_msg = current_test->force_stop(ex);
if (!error_msg.empty()) {
/// Translators: %1 is test name
gui_show_error_dialog(Glib::ustring::compose(_("Cannot stop %1"), SelfTest::get_test_name(current_test->get_test_type())), error_msg, this);
gui_show_error_dialog(Glib::ustring::compose(_("Cannot stop %1"), SelfTest::get_test_displayable_name(current_test->get_test_type())), error_msg, this);
return;
}
+7 -5
View File
@@ -283,9 +283,11 @@ class GscMainWindowIconView : public Gtk::IconView {
name += (drive->get_model_name().empty() ? Glib::ustring("Unknown model") : Glib::Markup::escape_text(drive->get_model_name()));
if (rconfig::get_data<bool>("gui/icons_show_device_name")) {
if (!drive->get_is_virtual()) {
name += "\n" + Glib::Markup::escape_text(drive->get_device_with_type());
#ifdef _WIN32
name += " (" + drive_letters + ")";
#ifndef _WIN32
std::string dev = Glib::Markup::escape_text(drive->get_device_with_type());
name += "\n" + dev;
#else
name += "\n" + Glib::ustring::compose(_("%1 (%2)"), dev, drive_letters);
#endif
} else if (!drive->get_virtual_filename().empty()) {
name += "\n" + Glib::Markup::escape_text(drive->get_virtual_filename());
@@ -322,9 +324,9 @@ class GscMainWindowIconView : public Gtk::IconView {
tooltip_strs.push_back(Glib::ustring::compose(_("Serial number: %1"), "<b>" + Glib::Markup::escape_text(drive->get_serial_number()) + "</b>"));
}
tooltip_strs.push_back(Glib::ustring::compose(_("SMART status: %1"),
"<b>" + StorageDevice::get_status_name(drive->get_smart_status()) + "</b>"));
"<b>" + StorageDevice::get_status_displayable_name(drive->get_smart_status()) + "</b>"));
tooltip_strs.push_back(Glib::ustring::compose(_("Automatic Offline Data Collection status: %1"),
"<b>" + StorageDevice::get_status_name(drive->get_aodc_status()) + "</b>"));
"<b>" + StorageDevice::get_status_displayable_name(drive->get_aodc_status()) + "</b>"));
std::string tooltip_str = hz::string_join(tooltip_strs, '\n');
+1 -1
View File
@@ -517,7 +517,7 @@ void GscPreferencesWindow::on_window_reset_all_button_clicked()
void GscPreferencesWindow::on_smartctl_binary_browse_button_clicked()
{
auto* entry = this->lookup_widget<Gtk::Entry*>("smartctl_binary_entry");
auto path = hz::fs::u8path(entry->get_text());
auto path = hz::fs::u8path(std::string(entry->get_text()));
int result = 0;
+63 -49
View File
@@ -20,12 +20,20 @@
#include <cstdint>
#include <sstream>
#include <chrono>
#include <vector>
#if defined __MINGW32__
#include <_mingw.h> // MINGW_HAS_SECURE_API
#endif
#ifdef ENABLE_GLIB
#include <glib/gi18n.h>
#else
#define C_(Str) (Str)
#endif
#include "string_num.h" // hz::number_to_string_locale
#include "string_algo.h"
/// \def HAVE_REENTRANT_LOCALTIME
@@ -64,76 +72,77 @@ inline std::string format_size(uint64_t size, bool use_decimal = false, bool siz
// const uint64_t zb_size = eb_size * multiplier; // zetta
// const uint64_t yb_size = zb_size * multiplier; // yotta
static const char* const names[] = {
" B", // bytes decimal
" B", // bytes binary
" bit", // bits decimal
" bit", // bits binary. note: 2 bit, not 2 bits.
// Note: This won't work with runtime language change.
static const std::vector<std::string> names = {
C_("file_size", "%s B"), // bytes decimal
C_("file_size", "%s B"), // bytes binary
C_("file_size", "%s bit"), // bits decimal
C_("file_size", "%s bit"), // bits binary. note: 2 bit, not 2 bits.
" KB",
" KiB",
" Kbit",
" Kibit",
C_("file_size", "%s KB"),
C_("file_size", "%s KiB"),
C_("file_size", "%s Kbit"),
C_("file_size", "%s Kibit"),
" MB",
" MiB",
" Mbit",
" Mibit",
C_("file_size", "%s MB"),
C_("file_size", "%s MiB"),
C_("file_size", "%s Mbit"),
C_("file_size", "%s Mibit"),
" GB",
" GiB",
" Gbit",
" Gibit",
C_("file_size", "%s GB"),
C_("file_size", "%s GiB"),
C_("file_size", "%s Gbit"),
C_("file_size", "%s Gibit"),
" TB",
" TiB",
" Tbit",
" Tibit",
C_("file_size", "%s TB"),
C_("file_size", "%s TiB"),
C_("file_size", "%s Tbit"),
C_("file_size", "%s Tibit"),
" PB",
" PiB",
" Pbit",
" Pibit",
C_("file_size", "%s PB"),
C_("file_size", "%s PiB"),
C_("file_size", "%s Pbit"),
C_("file_size", "%s Pibit"),
" EB",
" EiB",
" Ebit",
" Eibit"
C_("file_size", "%s EB"),
C_("file_size", "%s EiB"),
C_("file_size", "%s Ebit"),
C_("file_size", "%s Eibit")
};
const int addn = static_cast<int>(!use_decimal) + (static_cast<int>(size_is_bits) * 2);
if (size >= eb_size) { // exa
return hz::number_to_string_locale(static_cast<long double>(size) / static_cast<long double>(eb_size), 2, true)
+ names[(6 * 4) + addn];
return hz::string_replace_copy(names[(6 * 4) + addn], "%s",
hz::number_to_string_locale(static_cast<long double>(size) / static_cast<long double>(eb_size), 2, true), 1);
}
if (size >= pb_size) { // peta
return hz::number_to_string_locale(static_cast<long double>(size) / static_cast<long double>(pb_size), 2, true)
+ names[(5 * 4) + addn];
return hz::string_replace_copy(names[(5 * 4) + addn], "%s",
hz::number_to_string_locale(static_cast<long double>(size) / static_cast<long double>(pb_size), 2, true), 1);
}
if (size >= tb_size) { // tera
return hz::number_to_string_locale(static_cast<long double>(size) / static_cast<long double>(tb_size), 2, true)
+ names[(4 * 4) + addn];
return hz::string_replace_copy(names[(4 * 4) + addn], "%s",
hz::number_to_string_locale(static_cast<long double>(size) / static_cast<long double>(tb_size), 2, true), 1);
}
if (size >= gb_size) { // giga
return hz::number_to_string_locale(static_cast<long double>(size) / static_cast<long double>(gb_size), 2, true)
+ names[(3 * 4) + addn];
return hz::string_replace_copy(names[(3 * 4) + addn], "%s",
hz::number_to_string_locale(static_cast<long double>(size) / static_cast<long double>(gb_size), 2, true), 1);
}
if (size >= mb_size) { // mega
return hz::number_to_string_locale(static_cast<long double>(size) / static_cast<long double>(mb_size), 2, true)
+ names[(2 * 4) + addn];
return hz::string_replace_copy(names[(2 * 4) + addn], "%s",
hz::number_to_string_locale(static_cast<long double>(size) / static_cast<long double>(mb_size), 2, true), 1);
}
if (size >= kb_size) { // kilo
return hz::number_to_string_locale(static_cast<long double>(size) / static_cast<long double>(kb_size), 2, true)
+ names[(1 * 4) + addn];
return hz::string_replace_copy(names[(1 * 4) + addn], "%s",
hz::number_to_string_locale(static_cast<long double>(size) / static_cast<long double>(kb_size), 2, true), 1);
}
return std::to_string(size) + names[(0 * 4) + addn];
return hz::string_replace_copy(names[(0 * 4) + addn], "%s", std::to_string(size));
}
@@ -163,11 +172,13 @@ inline std::string format_time_length(std::chrono::seconds secs)
if (hours > 0 && sec_diff.count() < (-hour_size / 2))
days--;
return std::to_string(days.count()) + " " + "d"
+ " " + std::to_string(hours) + " " + "h";
return hz::string_replace_array_copy(C_("time", "{days} d {hours} h"),
std::vector<std::string>{"{days}", "{hours}"},
std::vector<std::string>{std::to_string(days.count()), std::to_string(hours)});
} else { // display days only
return std::to_string(days.count()) + " " + "d";
return hz::string_replace_copy(C_("time", "{days} d"),
"{days}", std::to_string(days.count()));
}
} else if (secs >= 100min) {
@@ -179,8 +190,9 @@ inline std::string format_time_length(std::chrono::seconds secs)
if (minutes > 0 && sec_diff.count() < (-min_size / 2))
hours--;
return std::to_string(hours.count()) + " " + "h"
+ " " + std::to_string(minutes) + " " + "min";
return hz::string_replace_array_copy(C_("time", "{hours} h {minutes} min"),
std::vector<std::string>{"{hours}", "{minutes}"},
std::vector<std::string>{std::to_string(hours.count()), std::to_string(minutes)});
} else { // display hours only
return std::to_string(hours.count()) + " " + "h";
@@ -188,10 +200,12 @@ inline std::string format_time_length(std::chrono::seconds secs)
} else if (secs >= 100s) {
auto minutes = std::chrono::round<std::chrono::minutes>(secs);
return std::to_string(minutes.count()) + " " + "min";
return hz::string_replace_copy(C_("time", "{minutes} min"),
"{minutes}", std::to_string(minutes.count()));
}
return std::to_string(secs.count()) + " " + "sec";
return hz::string_replace_copy(C_("time", "{seconds} sec"),
"{seconds}", std::to_string(secs.count()));
}
-3
View File
@@ -68,9 +68,6 @@ Filesystem utilities
namespace hz {
// TODO Change with gcc8.
namespace fs = std::experimental::filesystem;
#ifdef _WIN32
// Unlike std::filesystem::path::preferred_separator, this is always char.
+12 -3
View File
@@ -18,9 +18,6 @@
#include <stdexcept> // std::runtime_error
namespace hz {
/**
\file
Locale manipulation facilities
@@ -33,6 +30,18 @@ sets locale on per-thread basis.
*/
// Remove setlocale macro from mingw's libintl.h
// TODO mingw's gettext implementation should use libintl_setlocale():
// extern char* libintl_setlocale(int, const char*);
#ifdef setlocale
#undef setlocale
#endif
namespace hz {
/// Set the C standard library locale, storing the previous one into \c old_locale.
/// \return false on failure
inline bool locale_c_set(const std::string& loc, std::string& old_locale)
+30 -10
View File
@@ -396,7 +396,7 @@ inline std::string string_remove_adjacent_duplicates_copy(const std::string& s,
/// Replace from with to inside s (modifying s). Return number of replacements made.
inline std::string::size_type string_replace(std::string& s,
const std::string& from, const std::string& to, int max_replacements = -1)
const std::string_view& from, const std::string_view& to, int max_replacements = -1)
{
if (from.empty())
return std::string::npos;
@@ -422,7 +422,7 @@ inline std::string::size_type string_replace(std::string& s,
/// Replace from with to inside s, not modifying s, returning the changed string.
inline std::string string_replace_copy(const std::string& s,
const std::string& from, const std::string& to, int max_replacements = -1)
const std::string_view& from, const std::string_view& to, int max_replacements = -1)
{
std::string ret(s);
string_replace(ret, from, to, max_replacements);
@@ -469,7 +469,7 @@ inline std::string string_replace_copy(const std::string& s,
/// from_chars.size() must be equal to to_chars.size().
/// Note: This is a multi-pass algorithm (there are from_chars.size() iterations).
inline std::string::size_type string_replace_chars(std::string& s,
const std::string& from_chars, const std::string& to_chars, int max_replacements = -1)
const std::string_view& from_chars, const std::string_view& to_chars, int max_replacements = -1)
{
const std::string::size_type from_size = from_chars.size();
if (from_size != to_chars.size())
@@ -501,7 +501,7 @@ inline std::string::size_type string_replace_chars(std::string& s,
/// from_chars.size() must be equal to to_chars.size().
/// Note: This is a multi-pass algorithm (there are from_chars.size() iterations).
inline std::string string_replace_chars_copy(const std::string& s,
const std::string& from_chars, const std::string& to_chars, int max_replacements = -1)
const std::string_view& from_chars, const std::string_view& to_chars, int max_replacements = -1)
{
std::string ret(s);
string_replace_chars(ret, from_chars, to_chars, max_replacements);
@@ -514,7 +514,7 @@ inline std::string string_replace_chars_copy(const std::string& s,
/// Replace all chars from from_chars with to_char (modifying s).
inline std::string::size_type string_replace_chars(std::string& s,
const std::string& from_chars, char to_char, int max_replacements = -1)
const std::string_view& from_chars, char to_char, int max_replacements = -1)
{
if (from_chars.empty())
return std::string::npos;
@@ -535,7 +535,7 @@ inline std::string::size_type string_replace_chars(std::string& s,
/// Replace all chars from from_chars with to_char, not modifying s, returning the changed string.
inline std::string string_replace_chars_copy(const std::string& s,
const std::string& from_chars, char to_char, int max_replacements = -1)
const std::string_view& from_chars, char to_char, int max_replacements = -1)
{
std::string ret(s);
string_replace_chars(ret, from_chars, to_char, max_replacements);
@@ -645,7 +645,7 @@ std::string string_replace_array_copy(const std::string& s,
/// Note: This is a one-pass algorithm.
template<class Container> inline
std::string::size_type string_replace_array(std::string& s,
const Container& from_strings, const std::string& to_string, int max_replacements = -1)
const Container& from_strings, const std::string_view& to_string, int max_replacements = -1)
{
const std::string::size_type from_array_size = from_strings.size();
const std::string::size_type to_str_size = to_string.size();
@@ -676,7 +676,7 @@ std::string::size_type string_replace_array(std::string& s,
/// Note: This is a one-pass algorithm.
template<class Container> inline
std::string string_replace_array_copy(const std::string& s,
const Container& from_strings, const std::string& to_string, int max_replacements = -1)
const Container& from_strings, const std::string_view& to_string, int max_replacements = -1)
{
std::string ret(s);
string_replace_array(ret, from_strings, to_string, max_replacements);
@@ -686,12 +686,32 @@ std::string string_replace_array_copy(const std::string& s,
/// Same as the other overloads, but needed to avoid conflict with all-template version
template<class Container> inline
std::string::size_type string_replace_array(std::string& s,
const Container& from_strings, const std::string& to_string, int max_replacements = -1)
{
return string_replace_array<Container>(s, from_strings, std::string_view(to_string), max_replacements);
}
// Same as the other overloads, but needed to avoid conflict with all-template version
template<class Container> inline
std::string string_replace_array_copy(const std::string& s,
const Container& from_strings, const std::string& to_string, int max_replacements = -1)
{
return string_replace_array_copy<Container>(s, from_strings, std::string_view(to_string), max_replacements);
}
/// Same as the other overloads, but needed to avoid conflict with all-template version
template<class Container> inline
std::string::size_type string_replace_array(std::string& s,
const Container& from_strings, const char* to_string, int max_replacements = -1)
{
return string_replace_array<Container>(s, from_strings, std::string(to_string), max_replacements);
return string_replace_array<Container>(s, from_strings, std::string_view(to_string), max_replacements);
}
@@ -700,7 +720,7 @@ template<class Container> inline
std::string string_replace_array_copy(const std::string& s,
const Container& from_strings, const char* to_string, int max_replacements = -1)
{
return string_replace_array_copy<Container>(s, from_strings, std::string(to_string), max_replacements);
return string_replace_array_copy<Container>(s, from_strings, std::string_view(to_string), max_replacements);
}