diff --git a/.clang-tidy b/.clang-tidy index 5ae6ef5..8a9761b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -17,6 +17,7 @@ # misc-no-recursion is too noisy. # modernize-raw-string-literal is very noisy. # modernize-use-trailing-return-type is contrary to our style. +# performance-enum-size is too noisy. # readability-avoid-unconditional-preprocessor-if is triggered on platform-specific code # readability-convert-member-functions-to-static is triggered for many callbacks. # readability-function-cognitive-complexity needed by UI constructors. @@ -63,6 +64,7 @@ Checks: > -objc-*, openmp-*, performance-*, + -performance-enum-size, portability-*, readability-*, -readability-avoid-unconditional-preprocessor-if, diff --git a/src/applib/CMakeLists.txt b/src/applib/CMakeLists.txt index 90cda52..1366bb7 100644 --- a/src/applib/CMakeLists.txt +++ b/src/applib/CMakeLists.txt @@ -34,6 +34,8 @@ target_sources(applib PRIVATE smartctl_parser.h smartctl_json_ata_parser.cpp smartctl_json_ata_parser.h + smartctl_json_basic_parser.cpp + smartctl_json_basic_parser.h smartctl_json_parser_helpers.h smartctl_executor.cpp smartctl_executor_gui.h @@ -41,6 +43,8 @@ target_sources(applib PRIVATE smartctl_parser_types.h smartctl_text_ata_parser.cpp smartctl_text_ata_parser.h + smartctl_text_basic_parser.cpp + smartctl_text_basic_parser.h smartctl_text_parser_helper.cpp smartctl_text_parser_helper.h smartctl_version_parser.cpp diff --git a/src/applib/examples/example_smartctl_parser.cpp b/src/applib/examples/example_smartctl_parser.cpp index 51c6f4c..a3ccf00 100644 --- a/src/applib/examples/example_smartctl_parser.cpp +++ b/src/applib/examples/example_smartctl_parser.cpp @@ -42,8 +42,8 @@ int main(int argc, char* argv[]) return EXIT_FAILURE; } - SmartctlAtaTextParser parser; - if (const auto parse_status = parser.parse_full(contents); !parse_status.has_value()) { + SmartctlTextAtaParser parser; + if (const auto parse_status = parser.parse(contents); !parse_status.has_value()) { debug_out_error("app", "Cannot parse file contents: " << parse_status.error().message() << "\n"); return EXIT_FAILURE; } diff --git a/src/applib/selftest.cpp b/src/applib/selftest.cpp index 3d601cc..e1b98a0 100644 --- a/src/applib/selftest.cpp +++ b/src/applib/selftest.cpp @@ -243,10 +243,10 @@ std::string SelfTest::update(const std::shared_ptr& smartctl_ex return error_msg; const AtaStorageAttribute::DiskType disk_type = drive_->get_is_hdd() ? AtaStorageAttribute::DiskType::Hdd : AtaStorageAttribute::DiskType::Ssd; - auto parser = SmartctlParser::create(SmartctlParserType::Text); + auto parser = SmartctlParser::create(SmartctlParserType::TextAta); DBG_ASSERT_RETURN(parser, "Cannot create parser"); - auto parse_status = parser->parse_full(output); + auto parse_status = parser->parse(output); if (!parse_status) { return Glib::ustring::compose(_("Cannot parse smartctl output: %1"), parse_status.error().message()); } diff --git a/src/applib/smartctl_json_ata_parser.cpp b/src/applib/smartctl_json_ata_parser.cpp index 360d324..3a7a071 100644 --- a/src/applib/smartctl_json_ata_parser.cpp +++ b/src/applib/smartctl_json_ata_parser.cpp @@ -80,20 +80,18 @@ _custom/smart_enabled -hz::ExpectedVoid SmartctlAtaJsonParser::parse_full(const std::string& json_data_full) +hz::ExpectedVoid SmartctlJsonAtaParser::parse(std::string_view smartctl_output) { using namespace SmartctlJsonParserHelpers; - this->set_data_full(json_data_full); - - if (hz::string_trim_copy(json_data_full).empty()) { + if (hz::string_trim_copy(smartctl_output).empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Empty string passed as an argument. Returning.\n"); return hz::Unexpected(SmartctlParserError::EmptyInput, "Smartctl data is empty."); } nlohmann::json json_root_node; try { - json_root_node = nlohmann::json::parse(json_data_full); + json_root_node = nlohmann::json::parse(smartctl_output); } catch (const nlohmann::json::parse_error& e) { debug_out_warn("app", DBG_FUNC_MSG << "Error parsing smartctl output as JSON: " << e.what() << "\n"); return hz::Unexpected(SmartctlParserError::SyntaxError, std::string("Invalid JSON data: ") + e.what()); @@ -112,7 +110,7 @@ hz::ExpectedVoid SmartctlAtaJsonParser::parse_full(const st -hz::ExpectedVoid SmartctlAtaJsonParser::parse_version(const nlohmann::json& json_root_node) +hz::ExpectedVoid SmartctlJsonAtaParser::parse_version(const nlohmann::json& json_root_node) { using namespace SmartctlJsonParserHelpers; @@ -155,7 +153,7 @@ hz::ExpectedVoid SmartctlAtaJsonParser::parse_version(const p.section = AtaStorageProperty::Section::info; // add to info section add_property(p); } - if (!SmartctlVersionParser::check_parsed_version(SmartctlParserType::Json, smartctl_version)) { + if (!SmartctlVersionParser::check_parsed_version(SmartctlParserType::JsonAta, smartctl_version)) { debug_out_warn("app", DBG_FUNC_MSG << "Incompatible smartctl version. Returning.\n"); return hz::Unexpected(SmartctlParserError::IncompatibleVersion, "Incompatible smartctl version."); } @@ -165,7 +163,7 @@ hz::ExpectedVoid SmartctlAtaJsonParser::parse_version(const -hz::ExpectedVoid SmartctlAtaJsonParser::parse_section_info(const nlohmann::json& json_root_node) +hz::ExpectedVoid SmartctlJsonAtaParser::parse_section_info(const nlohmann::json& json_root_node) { using namespace SmartctlJsonParserHelpers; @@ -285,7 +283,7 @@ hz::ExpectedVoid SmartctlAtaJsonParser::parse_section_info( -hz::ExpectedVoid SmartctlAtaJsonParser::parse_section_health(const nlohmann::json& json_root_node) +hz::ExpectedVoid SmartctlJsonAtaParser::parse_section_health(const nlohmann::json& json_root_node) { using namespace SmartctlJsonParserHelpers; diff --git a/src/applib/smartctl_json_ata_parser.h b/src/applib/smartctl_json_ata_parser.h index 8767261..8da99ac 100644 --- a/src/applib/smartctl_json_ata_parser.h +++ b/src/applib/smartctl_json_ata_parser.h @@ -20,14 +20,14 @@ Copyright: /// Smartctl (S)ATA JSON output parser -class SmartctlAtaJsonParser : public SmartctlParser { +class SmartctlJsonAtaParser : public SmartctlParser { public: // Defaulted, used by make_unique. - SmartctlAtaJsonParser() = default; + SmartctlJsonAtaParser() = default; // Overridden - hz::ExpectedVoid parse_full(const std::string& json_data_full) override; + hz::ExpectedVoid parse(std::string_view smartctl_output) override; private: diff --git a/src/applib/smartctl_json_basic_parser.cpp b/src/applib/smartctl_json_basic_parser.cpp new file mode 100644 index 0000000..85c2ba6 --- /dev/null +++ b/src/applib/smartctl_json_basic_parser.cpp @@ -0,0 +1,44 @@ +/****************************************************************************** +License: GNU General Public License v3.0 only +Copyright: + (C) 2024 Alexander Shaduri +******************************************************************************/ +/// \file +/// \author Alexander Shaduri +/// \ingroup applib +/// \weakgroup applib +/// @{ + +// #include "local_glibmm.h" +//#include // localeconv +//#include +//#include + +// #include "hz/locale_tools.h" // ScopedCLocale, locale_c_get(). +//#include "hz/string_algo.h" // string_* +//#include "hz/string_num.h" // string_is_numeric, number_to_string +//#include "hz/debug.h" // debug_* + +//#include "app_pcrecpp.h" +//#include "smartctl_text_ata_parser.h" +//#include "ata_storage_property_descr.h" +// #include "warning_colors.h" +//#include "smartctl_version_parser.h" +#include "smartctl_json_basic_parser.h" + + + + +// Parse full "smartctl -x" output +hz::ExpectedVoid SmartctlJsonBasicParser::parse(std::string_view smartctl_output) +{ + + return {}; +} + + + + + + +/// @} diff --git a/src/applib/smartctl_json_basic_parser.h b/src/applib/smartctl_json_basic_parser.h new file mode 100644 index 0000000..d59ea4d --- /dev/null +++ b/src/applib/smartctl_json_basic_parser.h @@ -0,0 +1,41 @@ +/****************************************************************************** +License: GNU General Public License v3.0 only +Copyright: + (C) 2024 Alexander Shaduri +******************************************************************************/ +/// \file +/// \author Alexander Shaduri +/// \ingroup applib +/// \weakgroup applib +/// @{ + +#ifndef SMARTCTL_JSON_BASIC_PARSER_H +#define SMARTCTL_JSON_BASIC_PARSER_H + +//#include +//#include + +#include "smartctl_parser.h" + + + +/// Parse info output, regardless of device type +class SmartctlJsonBasicParser : public SmartctlParser { + public: + + // Defaulted, used by make_unique. + SmartctlJsonBasicParser() = default; + + // Overridden + hz::ExpectedVoid parse(std::string_view smartctl_output) override; + +}; + + + + + + +#endif + +/// @} diff --git a/src/applib/smartctl_parser.cpp b/src/applib/smartctl_parser.cpp index b567ce6..3eaa502 100644 --- a/src/applib/smartctl_parser.cpp +++ b/src/applib/smartctl_parser.cpp @@ -15,36 +15,45 @@ Copyright: #include "smartctl_parser.h" #include "smartctl_text_ata_parser.h" #include "smartctl_json_ata_parser.h" -#include "ata_storage_property_descr.h" -#include "warning_colors.h" +#include "smartctl_json_basic_parser.h" +#include "smartctl_text_basic_parser.h" +//#include "ata_storage_property_descr.h" std::unique_ptr SmartctlParser::create(SmartctlParserType type) { switch(type) { - case SmartctlParserType::Json: - return std::make_unique(); - case SmartctlParserType::Text: - return std::make_unique(); + case SmartctlParserType::JsonBasic: + return std::make_unique(); + break; + case SmartctlParserType::JsonAta: + return std::make_unique(); + break; + case SmartctlParserType::TextBasic: + return std::make_unique(); + break; + case SmartctlParserType::TextAta: + return std::make_unique(); + break; } return nullptr; } -hz::ExpectedValue SmartctlParser::detect_output_type(const std::string& output) +hz::ExpectedValue SmartctlParser::detect_output_format(std::string_view smartctl_output) { // Look for the first non-whitespace symbol - auto first_symbol = std::find_if(output.begin(), output.end(), [&](char c) { + const auto* first_symbol = std::find_if(smartctl_output.begin(), smartctl_output.end(), [&](char c) { return !std::isspace(c, std::locale::classic()); }); - if (first_symbol != output.end()) { + if (first_symbol != smartctl_output.end()) { if (*first_symbol == '{') { - return SmartctlParserType::Json; + return SmartctlParserFormat::Json; } - if (output.rfind("smartctl", static_cast(first_symbol - output.begin())) == 0) { - return SmartctlParserType::Text; + if (smartctl_output.rfind("smartctl", static_cast(first_symbol - smartctl_output.begin())) == 0) { + return SmartctlParserFormat::Text; } return hz::Unexpected(SmartctlParserError::UnsupportedFormat, "Unsupported format while trying to detect smartctl output format."); } @@ -53,13 +62,6 @@ hz::ExpectedValue SmartctlParser::detec -std::string SmartctlParser::get_data_full() const -{ - return data_full_; -} - - - const std::vector& SmartctlParser::get_properties() const { return properties_; @@ -76,13 +78,6 @@ void SmartctlParser::add_property(AtaStorageProperty p) -void SmartctlParser::set_data_full(const std::string& s) -{ - data_full_ = s; -} - - - /// @} diff --git a/src/applib/smartctl_parser.h b/src/applib/smartctl_parser.h index 2d72fa1..1a16435 100644 --- a/src/applib/smartctl_parser.h +++ b/src/applib/smartctl_parser.h @@ -12,7 +12,7 @@ Copyright: #ifndef SMARTCTL_PARSER_H #define SMARTCTL_PARSER_H -#include +#include #include #include @@ -71,16 +71,13 @@ class SmartctlParser { /// Parse full "smartctl -x" output. /// Note: Once parsed, this function cannot be called again. - virtual hz::ExpectedVoid parse_full(const std::string& full) = 0; + virtual hz::ExpectedVoid parse(std::string_view smartctl_output) = 0; /// Detect smartctl output type (text, json). - [[nodiscard]] static hz::ExpectedValue detect_output_type(const std::string& output); + [[nodiscard]] static hz::ExpectedValue detect_output_format(std::string_view smartctl_output); - /// Get "full" data, as passed to parse_full(). - [[nodiscard]] std::string get_data_full() const; - /// Get parse result properties [[nodiscard]] const std::vector& get_properties() const; @@ -90,14 +87,10 @@ class SmartctlParser { /// Add a property into property list, look up and set its description void add_property(AtaStorageProperty p); - /// Set "full" data ("smartctl -x" output), json or text. - void set_data_full(const std::string& s); - private: std::vector properties_; ///< Parsed data properties - std::string data_full_; ///< full data, filled by parse_full() }; diff --git a/src/applib/smartctl_parser_types.h b/src/applib/smartctl_parser_types.h index 9b3f450..37262aa 100644 --- a/src/applib/smartctl_parser_types.h +++ b/src/applib/smartctl_parser_types.h @@ -19,8 +19,10 @@ Copyright: enum class SmartctlParserType { - Json, - Text, + JsonBasic, ///< Info only + JsonAta, + TextBasic, ///< Info only + TextAta, }; @@ -32,13 +34,15 @@ struct SmartctlParserTypeExt SmartctlParserTypeExt, Glib::ustring> { - static constexpr inline SmartctlParserType default_value = SmartctlParserType::Json; + static constexpr inline SmartctlParserType default_value = SmartctlParserType::JsonAta; static std::unordered_map> build_enum_map() { return { - {SmartctlParserType::Json, {"json", _("JSON")}}, - {SmartctlParserType::Text, {"text", _("Text")}}, + {SmartctlParserType::JsonBasic, {"json_basic", _("JSON Basic")}}, + {SmartctlParserType::JsonAta, {"json_ata", _("JSON ATA")}}, + {SmartctlParserType::TextBasic, {"text_basic", _("Text Basic")}}, + {SmartctlParserType::TextAta, {"text_ata", _("Text ATA")}}, }; } @@ -46,6 +50,12 @@ struct SmartctlParserTypeExt +enum class SmartctlParserFormat { + Json, + Text, +}; + + enum class SmartctlParserSettingType { Auto, diff --git a/src/applib/smartctl_text_ata_parser.cpp b/src/applib/smartctl_text_ata_parser.cpp index a285b42..6112ed6 100644 --- a/src/applib/smartctl_text_ata_parser.cpp +++ b/src/applib/smartctl_text_ata_parser.cpp @@ -69,15 +69,12 @@ namespace { // Parse full "smartctl -x" output -hz::ExpectedVoid SmartctlAtaTextParser::parse_full(const std::string& full) +hz::ExpectedVoid SmartctlTextAtaParser::parse(std::string_view smartctl_output) { - this->set_data_full(full); - - // -------------------- Fix the output, so it doesn't interfere with proper parsing // perform any2unix - std::string s = hz::string_trim_copy(hz::string_any_to_unix_copy(full)); + std::string s = hz::string_trim_copy(hz::string_any_to_unix_copy(smartctl_output)); if (s.empty()) { debug_out_warn("app", DBG_FUNC_MSG << "Empty string passed as an argument. Returning.\n"); @@ -204,7 +201,7 @@ hz::ExpectedVoid SmartctlAtaTextParser::parse_full(const st // version info std::string version, version_full; - if (!SmartctlVersionParser::parse_version(s, version, version_full)) { + if (!SmartctlVersionParser::parse_version_text(s, version, version_full)) { debug_out_warn("app", DBG_FUNC_MSG << "Cannot extract version information. Returning.\n"); return hz::Unexpected(SmartctlParserError::NoVersion, "Cannot extract smartctl version information."); } @@ -226,7 +223,7 @@ hz::ExpectedVoid SmartctlAtaTextParser::parse_full(const st add_property(p); } - if (!SmartctlVersionParser::check_parsed_version(SmartctlParserType::Text, version)) { + if (!SmartctlVersionParser::check_parsed_version(SmartctlParserType::TextAta, version)) { debug_out_warn("app", DBG_FUNC_MSG << "Incompatible smartctl version. Returning.\n"); return hz::Unexpected(SmartctlParserError::IncompatibleVersion, "Incompatible smartctl version."); } @@ -269,7 +266,7 @@ hz::ExpectedVoid SmartctlAtaTextParser::parse_full(const st // Parse the section part (with "=== .... ===" header) - info or data sections. -hz::ExpectedVoid SmartctlAtaTextParser::parse_section(const std::string& header, const std::string& body) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section(const std::string& header, const std::string& body) { if (app_pcre_match("/START OF INFORMATION SECTION/mi", header)) { return parse_section_info(body); @@ -311,7 +308,7 @@ hz::ExpectedVoid SmartctlAtaTextParser::parse_section(const // ------------------------------------------------ INFO SECTION -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_info(const std::string& body) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_info(const std::string& body) { this->set_data_section_info(body); @@ -423,7 +420,7 @@ http://knowledge.seagate.com/articles/en_US/FAQ/213891en // Parse a component (one line) of the info section -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_info_property(AtaStorageProperty& p) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_info_property(AtaStorageProperty& p) { // ---- Info if (p.section != AtaStorageProperty::Section::info) { @@ -613,7 +610,7 @@ hz::ExpectedVoid SmartctlAtaTextParser::parse_section_info_ // Parse the Data section (without "===" header) -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data(const std::string& body) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data(const std::string& body) { this->set_data_section_data(body); @@ -764,7 +761,7 @@ hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data( // -------------------- Health -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data_subsection_health(const std::string& sub) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_health(const std::string& sub) { // Health section data (--info and --get=all): /* @@ -807,7 +804,7 @@ Device is: In smartctl database [for details use: -P show] // -------------------- Capabilities -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data_subsection_capabilities(const std::string& sub_initial) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_capabilities(const std::string& sub_initial) { // Capabilities section data: /* @@ -987,7 +984,7 @@ SCT capabilities: (0x003d) SCT Status supported. // Check the capabilities for internal properties we can use. -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data_internal_capabilities(AtaStorageProperty& cap_prop) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_internal_capabilities(AtaStorageProperty& cap_prop) { // Some special capabilities we're interested in. @@ -1244,7 +1241,7 @@ hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data_ // -------------------- Attributes -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data_subsection_attributes(const std::string& sub) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_attributes(const std::string& sub) { AtaStorageProperty pt; // template for easy copying pt.section = AtaStorageProperty::Section::data; @@ -1465,7 +1462,7 @@ ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data_subsection_directory_log(const std::string& sub) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_directory_log(const std::string& sub) { AtaStorageProperty pt; // template for easy copying pt.section = AtaStorageProperty::Section::data; @@ -1517,7 +1514,7 @@ Address Access R/W Size Description -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data_subsection_error_log(const std::string& sub) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_error_log(const std::string& sub) { AtaStorageProperty pt; // template for easy copying pt.section = AtaStorageProperty::Section::data; @@ -1714,7 +1711,7 @@ Error 1 [0] occurred at disk power-on lifetime: 1 hours (0 days + 1 hours) // -------------------- Selftest Log -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data_subsection_selftest_log(const std::string& sub) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_selftest_log(const std::string& sub) { AtaStorageProperty pt; // template for easy copying pt.section = AtaStorageProperty::Section::data; @@ -1893,7 +1890,7 @@ Num Test_Description Status Remaining LifeTime(hours) LBA // -------------------- Selective Selftest Log -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data_subsection_selective_selftest_log(const std::string& sub) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_selective_selftest_log(const std::string& sub) { AtaStorageProperty pt; // template for easy copying pt.section = AtaStorageProperty::Section::data; @@ -1950,7 +1947,7 @@ If Selective self-test is pending on power-up, resume after 0 minute delay. -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data_subsection_scttemp_log(const std::string& sub) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_scttemp_log(const std::string& sub) { AtaStorageProperty pt; // template for easy copying pt.section = AtaStorageProperty::Section::data; @@ -2040,7 +2037,7 @@ Index Estimated Time Temperature Celsius -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data_subsection_scterc_log(const std::string& sub) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_scterc_log(const std::string& sub) { AtaStorageProperty pt; // template for easy copying pt.section = AtaStorageProperty::Section::data; @@ -2089,7 +2086,7 @@ SCT Error Recovery Control: -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data_subsection_devstat(const std::string& sub) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_devstat(const std::string& sub) { AtaStorageProperty pt; // template for easy copying pt.section = AtaStorageProperty::Section::data; @@ -2260,7 +2257,7 @@ Page Offset Size Value Description -hz::ExpectedVoid SmartctlAtaTextParser::parse_section_data_subsection_sataphy(const std::string& sub) +hz::ExpectedVoid SmartctlTextAtaParser::parse_section_data_subsection_sataphy(const std::string& sub) { AtaStorageProperty pt; // template for easy copying pt.section = AtaStorageProperty::Section::data; @@ -2318,14 +2315,14 @@ ID Size Value Description -void SmartctlAtaTextParser::set_data_section_info(std::string s) +void SmartctlTextAtaParser::set_data_section_info(std::string s) { data_section_info_ = std::move(s); } -void SmartctlAtaTextParser::set_data_section_data(std::string s) +void SmartctlTextAtaParser::set_data_section_data(std::string s) { data_section_data_ = std::move(s); } diff --git a/src/applib/smartctl_text_ata_parser.h b/src/applib/smartctl_text_ata_parser.h index 1ed17aa..a9c33ce 100644 --- a/src/applib/smartctl_text_ata_parser.h +++ b/src/applib/smartctl_text_ata_parser.h @@ -20,16 +20,16 @@ Copyright: /// Smartctl (S)ATA text output parser. -/// Note: ALL parse_* functions (except parse_full() and parse_version()) +/// Note: ALL parse_* functions (except parse()) /// expect data in unix-newline format! -class SmartctlAtaTextParser : public SmartctlParser { +class SmartctlTextAtaParser : public SmartctlParser { public: // Defaulted, used by make_unique. - SmartctlAtaTextParser() = default; + SmartctlTextAtaParser() = default; // Overridden - hz::ExpectedVoid parse_full(const std::string& full) override; + hz::ExpectedVoid parse(std::string_view smartctl_output) override; protected: diff --git a/src/applib/smartctl_text_basic_parser.cpp b/src/applib/smartctl_text_basic_parser.cpp new file mode 100644 index 0000000..84ea22d --- /dev/null +++ b/src/applib/smartctl_text_basic_parser.cpp @@ -0,0 +1,43 @@ +/****************************************************************************** +License: GNU General Public License v3.0 only +Copyright: + (C) 2008 - 2024 Alexander Shaduri +******************************************************************************/ +/// \file +/// \author Alexander Shaduri +/// \ingroup applib +/// \weakgroup applib +/// @{ + +// #include "local_glibmm.h" +//#include // localeconv +//#include +//#include + +// #include "hz/locale_tools.h" // ScopedCLocale, locale_c_get(). +//#include "hz/string_algo.h" // string_* +//#include "hz/string_num.h" // string_is_numeric, number_to_string +//#include "hz/debug.h" // debug_* + +#include "app_pcrecpp.h" +//#include "smartctl_text_ata_parser.h" +//#include "ata_storage_property_descr.h" +// #include "warning_colors.h" +//#include "smartctl_version_parser.h" +#include "smartctl_text_basic_parser.h" + + + + +hz::ExpectedVoid SmartctlTextBasicParser::parse(std::string_view smartctl_output) +{ + + return {}; +} + + + + + + +/// @} diff --git a/src/applib/smartctl_text_basic_parser.h b/src/applib/smartctl_text_basic_parser.h new file mode 100644 index 0000000..4a5b428 --- /dev/null +++ b/src/applib/smartctl_text_basic_parser.h @@ -0,0 +1,41 @@ +/****************************************************************************** +License: GNU General Public License v3.0 only +Copyright: + (C) 2008 - 2024 Alexander Shaduri +******************************************************************************/ +/// \file +/// \author Alexander Shaduri +/// \ingroup applib +/// \weakgroup applib +/// @{ + +#ifndef SMARTCTL_TEXT_BASIC_PARSER_H +#define SMARTCTL_TEXT_BASIC_PARSER_H + +//#include +//#include + +#include "smartctl_parser.h" + + + +/// Parse info output, regardless of device type +class SmartctlTextBasicParser : public SmartctlParser { + public: + + // Defaulted, used by make_unique. + SmartctlTextBasicParser() = default; + + // Overridden + hz::ExpectedVoid parse(std::string_view smartctl_output) override; + +}; + + + + + + +#endif + +/// @} diff --git a/src/applib/smartctl_version_parser.cpp b/src/applib/smartctl_version_parser.cpp index b23ab91..fa4451d 100644 --- a/src/applib/smartctl_version_parser.cpp +++ b/src/applib/smartctl_version_parser.cpp @@ -21,7 +21,7 @@ Copyright: -bool SmartctlVersionParser::parse_version(const std::string& s, std::string& version_only, std::string& version_full) +bool SmartctlVersionParser::parse_version_text(const std::string& s, std::string& version_only, std::string& version_full) { // e.g. // "smartctl version 5.37" @@ -57,9 +57,11 @@ bool SmartctlVersionParser::check_parsed_version(SmartctlParserType parser_type, { if (auto numeric_version = get_numeric_version(version_only); numeric_version.has_value()) { switch(parser_type) { - case SmartctlParserType::Json: + case SmartctlParserType::JsonBasic: + case SmartctlParserType::JsonAta: return numeric_version.value() >= minimum_req_json_version; - case SmartctlParserType::Text: + case SmartctlParserType::TextBasic: + case SmartctlParserType::TextAta: return numeric_version.value() >= minimum_req_text_version; } } @@ -70,11 +72,10 @@ bool SmartctlVersionParser::check_parsed_version(SmartctlParserType parser_type, std::optional SmartctlVersionParser::detect_supported_parser_type(const std::string& version_only) { - if (check_parsed_version(SmartctlParserType::Json, version_only)) { - return SmartctlParserType::Json; - } - if (check_parsed_version(SmartctlParserType::Text, version_only)) { - return SmartctlParserType::Text; + for (auto type : SmartctlParserTypeExt::getAllValues()) { + if (check_parsed_version(type, version_only)) { + return type; + } } return std::nullopt; } diff --git a/src/applib/smartctl_version_parser.h b/src/applib/smartctl_version_parser.h index 14c11f1..8d1c7e0 100644 --- a/src/applib/smartctl_version_parser.h +++ b/src/applib/smartctl_version_parser.h @@ -28,15 +28,15 @@ class SmartctlVersionParser { /// Supply any text (not JSON) output of smartctl here, the smartctl version will be retrieved. /// The text does not have to be in Unix newline format. - /// \param command_output "smartctl -V" command output. + /// \param s "smartctl -V" command output. /// \param[out] version_only A string similar to "7.2" /// \param[out] version_full A string similar to "smartctl 7.2 2020-12-30 r5155" /// \return false if the version could not be parsed. - static bool parse_version(const std::string& command_output, std::string& version_only, std::string& version_full); + static bool parse_version_text(const std::string& s, std::string& version_only, std::string& version_full); /// Get numeric version as a double from a parsed version. - /// \param version_only A string similar to "7.2", as parsed by parse_version(). + /// \param version_only A string similar to "7.2", as parsed by parse_version_text(). /// \return Numeric version as a double, e.g. 7.2. std::nullopt if the version could not be parsed. static std::optional get_numeric_version(const std::string& version_only); diff --git a/src/applib/storage_device.cpp b/src/applib/storage_device.cpp index e333bff..42442dc 100644 --- a/src/applib/storage_device.cpp +++ b/src/applib/storage_device.cpp @@ -146,7 +146,7 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si } std::string version, version_full; - if (!SmartctlVersionParser::parse_version(this->info_output_, version, version_full)) // is this smartctl data at all? + if (!SmartctlVersionParser::parse_version_text(this->info_output_, version, version_full)) // is this smartctl data at all? return _("Cannot get smartctl version information."); // Detect type. note: we can't distinguish between sata and scsi (on linux, for -d ata switch). @@ -174,10 +174,10 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si smart_enabled_ = false; } else { - // Note: We don't use SmartctlAtaTextParser here, because this information + // Note: We don't use SmartctlTextAtaParser here, because this information // may be in some other format. If this information is valid, only then it's - // passed to SmartctlAtaTextParser. - // Compared to SmartctlAtaTextParser, this one is much looser. + // passed to SmartctlTextAtaParser. + // Compared to SmartctlTextAtaParser, this one is much looser. // Don't put complete messages here - they change across smartctl versions. if (app_pcre_match("/^SMART support is:[ \\t]*Unavailable/mi", info_output_) // cdroms output this @@ -241,10 +241,10 @@ std::string StorageDevice::parse_basic_data(bool do_set_properties, bool emit_si disk_type = hdd_.value() ? AtaStorageAttribute::DiskType::Hdd : AtaStorageAttribute::DiskType::Ssd; } - auto parser = SmartctlParser::create(SmartctlParserType::Text); + auto parser = SmartctlParser::create(SmartctlParserType::TextAta); DBG_ASSERT_RETURN(parser, "Cannot create parser"); - if (parser->parse_full(this->info_output_)) { // try to parse it + if (parser->parse(this->info_output_)) { // try to parse it this->set_properties(StoragePropertyProcessor::process_properties(parser->get_properties(), disk_type)); // copy to our drive, overwriting old data } } @@ -317,24 +317,30 @@ std::string StorageDevice::parse_data() disk_type = hdd_.value() ? AtaStorageAttribute::DiskType::Hdd : AtaStorageAttribute::DiskType::Ssd; } - auto parser_type = SmartctlParser::detect_output_type(this->full_output_); + auto parser_format = SmartctlParser::detect_output_format(this->full_output_); - if (!parser_type.has_value()) { - return parser_type.error().message(); + if (!parser_format.has_value()) { + return parser_format.error().message(); } - auto parser = SmartctlParser::create(parser_type.value()); + // TODO Choose format according to device type + SmartctlParserType parser_type = SmartctlParserType::TextAta; + if (parser_format == SmartctlParserFormat::Json) { + parser_type = SmartctlParserType::JsonAta; + } + + auto parser = SmartctlParser::create(parser_type); DBG_ASSERT_RETURN(parser, "Cannot create parser"); // Try to parse it (parse only, set the properties after basic parsing). - const auto parse_status = parser->parse_full(this->full_output_); + const auto parse_status = parser->parse(this->full_output_); if (parse_status.has_value()) { // refresh basic info too - this->info_output_ = parser->get_data_full(); // put data including version information + this->info_output_ = this->full_output_; // put data including version information // note: this will clear the non-basic properties! - // this will parse some info that is already parsed by SmartctlAtaTextParser::parse_full(), + // this will parse some info that is already parsed by SmartctlAtaTextParser::parse(), // but this one sets the StorageDevice class members, not properties. this->parse_basic_data(false, false); // don't emit signal, we're not complete yet. diff --git a/src/applib/tests/test_smartctl_parser.cpp b/src/applib/tests/test_smartctl_parser.cpp index de0f1b2..2b5d226 100644 --- a/src/applib/tests/test_smartctl_parser.cpp +++ b/src/applib/tests/test_smartctl_parser.cpp @@ -21,21 +21,21 @@ Copyright: TEST_CASE("SmartctlFormatDetection", "[app][parser]") { - REQUIRE(SmartctlParser::detect_output_type({}).error().data() == SmartctlParserError::EmptyInput); + REQUIRE(SmartctlParser::detect_output_format({}).error().data() == SmartctlParserError::EmptyInput); - REQUIRE(SmartctlParser::detect_output_type("smart").error().data() == SmartctlParserError::UnsupportedFormat); + REQUIRE(SmartctlParser::detect_output_format("smart").error().data() == SmartctlParserError::UnsupportedFormat); - REQUIRE(SmartctlParser::detect_output_type("{ }").value() == SmartctlParserType::Json); + REQUIRE(SmartctlParser::detect_output_format("{ }").value() == SmartctlParserFormat::Json); - REQUIRE(SmartctlParser::detect_output_type(" \n { } ").value() == SmartctlParserType::Json); + REQUIRE(SmartctlParser::detect_output_format(" \n { } ").value() == SmartctlParserFormat::Json); - REQUIRE(SmartctlParser::detect_output_type("smartctl").value() == SmartctlParserType::Text); + REQUIRE(SmartctlParser::detect_output_format("smartctl").value() == SmartctlParserFormat::Text); - REQUIRE(SmartctlParser::detect_output_type( + REQUIRE(SmartctlParser::detect_output_format( R"(smartctl 7.2 2020-12-30 r5155 [x86_64-linux-5.3.18-lp152.66-default] (SUSE RPM) Copyright (C) 2002-20, Bruce Allen, Christian Franke, www.smartmontools.org -)").value() == SmartctlParserType::Text); +)").value() == SmartctlParserFormat::Text); } diff --git a/src/applib/tests/test_smartctl_version_parser.cpp b/src/applib/tests/test_smartctl_version_parser.cpp index 382129e..2ec3207 100644 --- a/src/applib/tests/test_smartctl_version_parser.cpp +++ b/src/applib/tests/test_smartctl_version_parser.cpp @@ -24,31 +24,31 @@ TEST_CASE("SmartctlVersionParser", "[app][parser]") std::string version_only, version_full; SECTION("Parse with version keyword") { - SmartctlVersionParser::parse_version("smartctl version 5.37", version_only, version_full); + SmartctlVersionParser::parse_version_text("smartctl version 5.37", version_only, version_full); REQUIRE(version_only == "5.37"); REQUIRE(version_full == "5.37"); } SECTION("Parse without version keyword") { - SmartctlVersionParser::parse_version("smartctl 5.39", version_only, version_full); + SmartctlVersionParser::parse_version_text("smartctl 5.39", version_only, version_full); REQUIRE(version_only == "5.39"); REQUIRE(version_full == "5.39"); } SECTION("Parse with date (CVS)") { - SmartctlVersionParser::parse_version("smartctl 5.39 2009-06-03 20:10", version_only, version_full); + SmartctlVersionParser::parse_version_text("smartctl 5.39 2009-06-03 20:10", version_only, version_full); REQUIRE(version_only == "5.39"); REQUIRE(version_full == "5.39 2009-06-03 20:10"); } SECTION("Parse with date (SVN)") { - SmartctlVersionParser::parse_version("smartctl 5.39 2009-08-08 r2873", version_only, version_full); + SmartctlVersionParser::parse_version_text("smartctl 5.39 2009-08-08 r2873", version_only, version_full); REQUIRE(version_only == "5.39"); REQUIRE(version_full == "5.39 2009-08-08 r2873"); } SECTION("Parse old 5.0") { - SmartctlVersionParser::parse_version("smartctl version 5.0-49", version_only, version_full); + SmartctlVersionParser::parse_version_text("smartctl version 5.0-49", version_only, version_full); REQUIRE(version_only == "5.0-49"); REQUIRE(version_full == "5.0-49"); } @@ -70,7 +70,7 @@ smartmontools build host: x86_64-suse-linux-gnu smartmontools build with: C++14, GCC 7.5.0 smartmontools configure arguments: '--host=x86_64-suse-linux-gnu' '--build=x86_64-suse-linux-gnu' '--program-prefix=' '--prefix=/usr' '--exec-prefix=/usr' '--bindir=/usr/bin' '--sbindir=/usr/sbin' '--sysconfdir=/etc' '--datadir=/usr/share' '--includedir=/usr/include' '--libdir=/usr/lib64' '--libexecdir=/usr/lib' '--localstatedir=/var' '--sharedstatedir=/var/lib' '--mandir=/usr/share/man' '--infodir=/usr/share/info' '--disable-dependency-tracking' '--docdir=/usr/share/doc/packages/smartmontools' '--with-selinux' '--with-libsystemd' '--with-systemdsystemunitdir=/usr/lib/systemd/system' '--with-savestates' '--with-attributelog' '--with-nvme-devicescan' 'build_alias=x86_64-suse-linux-gnu' 'host_alias=x86_64-suse-linux-gnu' 'CXXFLAGS=-O2 -g -m64 -fmessage-length=0 -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables -fPIE ' 'LDFLAGS=-pie' 'CFLAGS=-O2 -g -m64 -fmessage-length=0 -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables -fPIE' 'PKG_CONFIG_PATH=:/usr/lib64/pkgconfig:/usr/share/pkgconfig' )"; - SmartctlVersionParser::parse_version(output, version_only, version_full); + SmartctlVersionParser::parse_version_text(output, version_only, version_full); REQUIRE(version_only == "7.2"); REQUIRE(version_full == "7.2 2020-12-30 r5155"); } @@ -93,7 +93,7 @@ smartmontools build with: C++11, GCC 7.5.0 smartmontools configure arguments: [no arguments given] )"; - SmartctlVersionParser::parse_version(output, version_only, version_full); + SmartctlVersionParser::parse_version_text(output, version_only, version_full); REQUIRE(version_only == "7.3"); REQUIRE(version_full == "7.3"); } diff --git a/src/gsc_main_window.cpp b/src/gsc_main_window.cpp index 131bef0..579a771 100644 --- a/src/gsc_main_window.cpp +++ b/src/gsc_main_window.cpp @@ -118,7 +118,7 @@ GscMainWindow::GscMainWindow(BaseObjectType* gtkcobj, Glib::RefPtr } std::string version, version_full; - if (!SmartctlVersionParser::parse_version(output, version, version_full)) { + if (!SmartctlVersionParser::parse_version_text(output, version, version_full)) { error_msg = _("Smartctl returned invalid output."); break; } diff --git a/src/hz/string_algo.h b/src/hz/string_algo.h index 754d78d..b7830ce 100644 --- a/src/hz/string_algo.h +++ b/src/hz/string_algo.h @@ -236,7 +236,7 @@ inline bool string_trim(std::string& s, const std::string& trim_chars = " \t\r\n /// Trim a string s from both sides (not modifying s), returning the changed string. /// Trimming removes all trim_chars that occur on either side of the string s. -inline std::string string_trim_copy(const std::string& s, const std::string& trim_chars = " \t\r\n") +inline std::string string_trim_copy(std::string_view s, const std::string& trim_chars = " \t\r\n") { std::string ret(s); string_trim(ret, trim_chars); @@ -268,7 +268,7 @@ inline bool string_trim_left(std::string& s, const std::string& trim_chars = " \ /// Trim a string s from the left (not modifying s), returning the changed string. /// Trimming removes all trim_chars that occur on the left side of the string s. -inline std::string string_trim_left_copy(const std::string& s, const std::string& trim_chars = " \t\r\n") +inline std::string string_trim_left_copy(std::string_view s, const std::string& trim_chars = " \t\r\n") { std::string ret(s); string_trim_left(ret, trim_chars); @@ -297,7 +297,7 @@ inline bool string_trim_right(std::string& s, const std::string& trim_chars = " /// Trim a string s from the right (not modifying s), returning the changed string. /// Trimming removes all trim_chars that occur on the right side of the string s. -inline std::string string_trim_right_copy(const std::string& s, const std::string& trim_chars = " \t\r\n") +inline std::string string_trim_right_copy(std::string_view s, const std::string& trim_chars = " \t\r\n") { std::string ret(s); string_trim_right(ret, trim_chars); @@ -756,7 +756,7 @@ std::string string_replace_array_copy(const std::string& s, /// Check whether a string begins with another string -inline bool string_begins_with(const std::string& str, const std::string& substr) +inline bool string_begins_with(std::string_view str, const std::string& substr) { if (str.length() >= substr.length()) { return (str.compare(0, substr.length(), substr) == 0); @@ -767,7 +767,7 @@ inline bool string_begins_with(const std::string& str, const std::string& substr /// Check whether a string begins with a character -inline bool string_begins_with(const std::string& str, char ch) +inline bool string_begins_with(std::string_view str, char ch) { return !str.empty() && str[0] == ch; } @@ -775,7 +775,7 @@ inline bool string_begins_with(const std::string& str, char ch) /// Check whether a string ends with another string -inline bool string_ends_with(const std::string& str, const std::string& substr) +inline bool string_ends_with(std::string_view str, const std::string& substr) { if (str.length() >= substr.length()) { return (str.compare(str.length() - substr.length(), substr.length(), substr) == 0); @@ -786,7 +786,7 @@ inline bool string_ends_with(const std::string& str, const std::string& substr) /// Check whether a string ends with a character -inline bool string_ends_with(const std::string& str, char ch) +inline bool string_ends_with(std::string_view str, char ch) { return !str.empty() && str[str.size() - 1] == ch; } @@ -810,7 +810,7 @@ inline bool string_any_to_unix(std::string& s) /// Auto-detect and convert mac/dos/unix newline formats in s to unix format. /// Returns the result string. -inline std::string string_any_to_unix_copy(const std::string& s) +inline std::string string_any_to_unix_copy(std::string_view s) { std::string ret(s); string_any_to_unix(ret); @@ -831,7 +831,7 @@ inline bool string_any_to_dos(std::string& s) /// Auto-detect and convert mac/dos/unix newline formats in s to dos format. /// Returns the result string. -inline std::string string_any_to_dos_copy(const std::string& s) +inline std::string string_any_to_dos_copy(std::string_view s) { std::string ret(s); string_any_to_dos(ret); @@ -853,7 +853,7 @@ inline std::string::size_type string_to_lower(std::string& s) /// Convert s to lowercase, not modifying s, returning the changed string. -inline std::string string_to_lower_copy(const std::string& s) +inline std::string string_to_lower_copy(std::string_view s) { std::string ret(s); string_to_lower(ret); @@ -875,7 +875,7 @@ inline std::string::size_type string_to_upper(std::string& s) /// Convert s to uppercase, not modifying s, returning the changed string. -inline std::string string_to_upper_copy(const std::string& s) +inline std::string string_to_upper_copy(std::string_view s) { std::string ret(s); string_to_upper(ret);