diff --git a/docs/changelog.txt b/docs/changelog.txt index 2e1fb98..7125900 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -1,8 +1,10 @@ +ver 0.7.1 - Added support for C-style comments and trailing commas in JSON files. - Added support for .jsonc files. - Added "codepage" option to support UTF-8 outputs on Windows. - Added support for colorized outputs on Linux. - Fixed a bug that there is no limit on the buffer size of the console window on Linux. +- Optimized some functions to reduce the binary size. ver 0.7.0 - Added options for string validation. diff --git a/examples/all_keys/gui_definition.json b/examples/all_keys/gui_definition.json index b91808d..15cbac9 100644 --- a/examples/all_keys/gui_definition.json +++ b/examples/all_keys/gui_definition.json @@ -1,6 +1,6 @@ { - "recommended": "0.7.0", - "minimum_required": "0.7.0", + "recommended": "0.7.1", + "minimum_required": "0.7.1", "gui": [ { "label": "Components Minimal", @@ -249,6 +249,7 @@ "show_last_line": true, "check_exit_code": true, "exit_success": 0, + "codepage": "default", "components": [ { "type": "static_text", @@ -260,8 +261,17 @@ "help": [ { "type": "url", - "label": "test", - "url":"example.com" + "label": "open example.com", + "url": "example.com" + }, + { + "type": "file", + "label": "open gui_definition.json", + "path": "gui_definition.json" } - ] + ], // Trailing comma } +// Single-line comment +/* + * Multi-line comments + */ diff --git a/examples/other_features/codepage/README.md b/examples/other_features/codepage/README.md index c691b66..0181d59 100644 --- a/examples/other_features/codepage/README.md +++ b/examples/other_features/codepage/README.md @@ -1,8 +1,5 @@ # UTF-8 Outputs on Windows -> [!WARNING] -> Released builds do not support this feature yet. - Tuw expects user's default locale for stdout on Windows. If you want to use UTF-8 outputs on Windows, you should use `"codepage": "utf8"` (or `"codepage": "utf-8"`) as an option. ```json diff --git a/examples/tips/comments/README.md b/examples/tips/comments/README.md index 4f67550..639d7e5 100644 --- a/examples/tips/comments/README.md +++ b/examples/tips/comments/README.md @@ -1,8 +1,5 @@ # JSON with Comments -> [!WARNING] -> Released builds do not support this feature yet. - Tuw supports the [JSON with Comments](https://code.visualstudio.com/docs/languages/json#_json-with-comments) format. It allows C-style comments (`//` and `/**/`) and trailing commas in addition to the standard JSON format. You can also use `.jsonc` as a file extension to let code editors know that it uses the extended JSON format. ```jsonc diff --git a/include/component.h b/include/component.h index a1250e0..e81a13b 100644 --- a/include/component.h +++ b/include/component.h @@ -26,16 +26,16 @@ class Component { virtual ~Component(); virtual std::string GetRawString() { return "";} std::string GetString(); - std::string const GetID(); + const std::string& GetID() const { return m_id; } virtual void SetConfig(const rapidjson::Value& config) { UNUSED(config); } virtual void GetConfig(rapidjson::Document& config) { UNUSED(config); } - bool HasString() { return m_has_string; } - bool IsWide() { return m_is_wide; } + bool HasString() const { return m_has_string; } + bool IsWide() const { return m_is_wide; } bool Validate(bool* redraw_flag); - std::string GetValidationError(); + const std::string& GetValidationError() const; void PutErrorWidget(uiBox* box); static Component* PutComponent(uiBox* box, const rapidjson::Value& j); diff --git a/include/json_utils.h b/include/json_utils.h index 7f91cf9..c3eb56c 100644 --- a/include/json_utils.h +++ b/include/json_utils.h @@ -23,6 +23,9 @@ struct JsonResult { #define JSON_RESULT_OK { true, "" } +// Max binary size for JSON files. +#define JSON_SIZE_MAX 128 * 1024 + JsonResult LoadJson(const std::string& file, rapidjson::Document& json); JsonResult SaveJson(rapidjson::Document& json, const std::string& file); std::string JsonToString(rapidjson::Document& json); diff --git a/include/tuw_constants.h b/include/tuw_constants.h index cadf972..658ea84 100644 --- a/include/tuw_constants.h +++ b/include/tuw_constants.h @@ -10,11 +10,11 @@ namespace tuw_constants { " CLI tools\n"; constexpr char TOOL_NAME[] = "Tuw"; constexpr char AUTHOR[] = "matyalatte"; - constexpr char VERSION[] = "0.7.0"; - constexpr int VERSION_INT = 700; + constexpr char VERSION[] = "0.7.1"; + constexpr int VERSION_INT = 701; #ifdef _WIN32 - constexpr char OS[] = "win"; + #define TUW_CONSTANTS_OS "win" const int GRID_COMP_XSPACE = 2; const int GRID_MAIN_SPACE = 7; const int BOX_MAIN_SPACE = 6; @@ -23,7 +23,7 @@ namespace tuw_constants { const int BTN_WIDTH = 90; const int BTN_HEIGHT = 24; #elif defined(__TUW_UNIX__) - constexpr char OS[] = "linux"; + #define TUW_CONSTANTS_OS "linux" const int GRID_COMP_XSPACE = 4; const int GRID_MAIN_SPACE = 12; const int BOX_MAIN_SPACE = 12; @@ -32,7 +32,7 @@ namespace tuw_constants { const int BTN_WIDTH = 84; const int BTN_HEIGHT = -1; #else - constexpr char OS[] = "mac"; + #define TUW_CONSTANTS_OS "mac" const int GRID_COMP_XSPACE = 4; const int GRID_MAIN_SPACE = 12; const int BOX_MAIN_SPACE = 12; diff --git a/include/validator.h b/include/validator.h index 3ae51bc..62bf501 100644 --- a/include/validator.h +++ b/include/validator.h @@ -22,5 +22,5 @@ class Validator { ~Validator() {} void Initialize(const rapidjson::Value& j); bool Validate(const std::string& str); - std::string GetError() { return m_error_msg; } + const std::string& GetError() const { return m_error_msg; } }; diff --git a/src/component.cpp b/src/component.cpp index 4b26781..e79db34 100644 --- a/src/component.cpp +++ b/src/component.cpp @@ -27,7 +27,7 @@ Component::Component(const rapidjson::Value& j) { m_is_wide = false; m_label = j["label"].GetString(); m_id = json_utils::GetString(j, "id", ""); - if (m_id == "") { + if (m_id.empty()) { uint32_t hash = Fnv1Hash32(j["label"].GetString()); m_id = "_" + std::to_string(hash); } @@ -46,10 +46,6 @@ std::string Component::GetString() { return str; } -std::string const Component::GetID() { - return m_id; -} - bool Component::Validate(bool* redraw_flag) { // Main frame should run Fit() after this function. bool validate = m_validator.Validate(GetRawString()); @@ -75,7 +71,7 @@ bool Component::Validate(bool* redraw_flag) { return validate; } -std::string Component::GetValidationError() { +const std::string& Component::GetValidationError() const { return m_validator.GetError(); } @@ -239,7 +235,7 @@ class Filter { name = n; } void AddPattern(const char* pattern) { - patterns.push_back(pattern); + patterns.emplace_back(pattern); } uiFileDialogParamsFilter ToLibuiFilter() { return { @@ -267,7 +263,7 @@ class FilterList { bool is_reading_pattern = false; Filter* filter = new Filter(); for (const char c : ext) { - if (c == "|"[0]) { + if (c == '|') { filter_buf[i] = 0; if (is_reading_pattern) { filter->AddPattern(&filter_buf[start]); @@ -278,7 +274,7 @@ class FilterList { } is_reading_pattern = !is_reading_pattern; start = i + 1; - } else if (is_reading_pattern && (c == ";"[0])) { + } else if (is_reading_pattern && (c == ';')) { filter_buf[i] = 0; filter->AddPattern(&filter_buf[start]); start = i + 1; @@ -311,7 +307,7 @@ class FilterList { } void AddFilter(Filter* f) { - filters.push_back(f); + filters.emplace_back(f); } size_t GetSize() { @@ -398,7 +394,7 @@ ComboBox::ComboBox(uiBox* box, const rapidjson::Value& j) const char* label = i["label"].GetString(); uiComboboxAppend(combo, label); const char* value = json_utils::GetString(i, "value", label); - values.push_back(value); + values.emplace_back(value); } uiBox* hbox = uiNewHorizontalBox(); uiBoxAppend(hbox, uiControl(combo), 0); @@ -441,7 +437,7 @@ RadioButtons::RadioButtons(uiBox* box, const rapidjson::Value& j) const char* label = i["label"].GetString(); uiRadioButtonsAppend(radio, label); const char* value = json_utils::GetString(i, "value", label); - values.push_back(value); + values.emplace_back(value); } uiBox* hbox = uiNewHorizontalBox(); uiBoxAppend(hbox, uiControl(radio), 0); @@ -526,9 +522,9 @@ CheckArray::CheckArray(uiBox* box, const rapidjson::Value& j) uiControlSetTooltip(uiControl(check), json_utils::GetString(i, "tooltip", "")); } - checks->push_back(check); + checks->emplace_back(check); const char* value = json_utils::GetString(i, "value", label); - values.push_back(value); + values.emplace_back(value); id++; } uiBoxAppend(box, uiControl(check_array_box), 0); @@ -537,7 +533,7 @@ CheckArray::CheckArray(uiBox* box, const rapidjson::Value& j) } std::string CheckArray::GetRawString() { - std::string str = ""; + std::string str; std::vector checks; checks = *(std::vector*)m_widget; for (size_t i = 0; i < checks.size(); i++) { diff --git a/src/exe_container.cpp b/src/exe_container.cpp index 92bd7ad..1b7b728 100644 --- a/src/exe_container.cpp +++ b/src/exe_container.cpp @@ -9,19 +9,19 @@ static uint32_t ReadUint32(FILE* io) { unsigned char int_as_bin[4]; - size_t ret = fread(int_as_bin, 1, 4, io); - if (ret != 4) + if (fread(int_as_bin, 1, 4, io) != 4) return 0; - uint32_t num = 0; - for (size_t i = 0; i < 4; i++) - num += int_as_bin[i] << (i * 8); - return num; + return static_cast(int_as_bin[0]) | static_cast(int_as_bin[1] << 8) | + static_cast(int_as_bin[2] << 16) | static_cast(int_as_bin[3] << 24); } static void WriteUint32(FILE* io, const uint32_t& num) { - unsigned char int_as_bin[4]; - for (size_t i = 0; i < 4; i++) - int_as_bin[i] = (unsigned char)(num >> (i * 8)); + unsigned char int_as_bin[4] = { + static_cast(num & 0xFF), + static_cast((num >> 8) & 0xFF), + static_cast((num >> 16) & 0xFF), + static_cast((num >> 24) & 0xFF) + }; fwrite(int_as_bin, 1, 4, io); } @@ -29,68 +29,39 @@ static void WriteUint32(FILE* io, const uint32_t& num) { #define BUF_SIZE 1024 static std::string ReadStr(FILE* io, const uint32_t& size) { - std::string str = ""; - char buff[BUF_SIZE + 1]; - buff[BUF_SIZE] = 0; - uint32_t pos = 0; - while (pos < size) { - size_t ret; - uint32_t rest = size - pos; - size_t copy_size = MIN(BUF_SIZE, rest); - ret = fread(buff, 1, copy_size, io); - if (ret != copy_size) - return ""; - buff[copy_size] = 0; - str += buff; - - if (rest < BUF_SIZE) - break; - pos += BUF_SIZE; - } + std::string str(size, '\0'); + if (fread(&str[0], 1, size, io) != size) + return ""; return str; } static void WriteStr(FILE* io, const std::string& str) { - uint32_t size = (uint32_t)str.length(); - uint32_t pos = 0; - while (pos < size) { - if (size - pos <= BUF_SIZE) { - fwrite(str.substr(pos, size).c_str(), 1, size - pos, io); - break; - } - fwrite(str.substr(pos, pos + BUF_SIZE).c_str(), 1, BUF_SIZE, io); - pos += BUF_SIZE; - } + fwrite(str.data(), 1, str.size(), io); + + // Zero padding size_t padding = (8 - ftell(io) % 8) % 8; - char buff[8]; - fwrite(buff, 1, padding, io); + char padding_bytes[8] = { 0 }; + fwrite(padding_bytes, 1, padding, io); } -static bool CopyBinary(FILE* reader, FILE* writer, const uint32_t& size) { - uint32_t pos = 0; +static bool CopyBinary(FILE* reader, FILE* writer, uint32_t size) { char buff[BUF_SIZE]; - while (pos < size) { - uint32_t rest = size - pos; - size_t copy_size = MIN(BUF_SIZE, rest); - size_t ret = fread(buff, 1, copy_size, reader); - if (ret != copy_size) + while (size > 0) { + size_t copy_size = MIN(BUF_SIZE, size); + size_t read_size = fread(buff, 1, copy_size, reader); + if (read_size != copy_size) return false; - fwrite(buff, 1, copy_size, writer); - - if (rest < BUF_SIZE) - break; - pos += BUF_SIZE; + fwrite(buff, 1, read_size, writer); + size -= static_cast(read_size); } return true; } static std::string ReadMagic(FILE* io) { - char magic[5]; - magic[4] = 0; - size_t ret = fread(magic, 1, 4, io); - if (ret != 4) + char magic[4]; + if (fread(magic, 1, 4, io) != 4) return ""; - return std::string(magic); + return std::string(magic, 4); } static uint32_t Length(FILE* io) { @@ -102,7 +73,6 @@ static uint32_t Length(FILE* io) { } static const uint32_t EXE_SIZE_MAX = 20000000; // Allowed size of exe -static const uint32_t JSON_SIZE_MAX = 1000000; // Allowed size of json json_utils::JsonResult ExeContainer::Read(const std::string& exe_path) { m_exe_path = exe_path; @@ -127,9 +97,8 @@ json_utils::JsonResult ExeContainer::Read(const std::string& exe_path) { fseek(file_io, -8, SEEK_CUR); m_exe_size = end_off + ReadUint32(file_io); if (EXE_SIZE_MAX <= m_exe_size || end_off < m_exe_size) { - std::string msg = "Unexpected exe size. (" + std::to_string(m_exe_size) + ")"; fclose(file_io); - return { false, msg }; + return { false, "Unexpected exe size. (" + std::to_string(m_exe_size) + ")" }; } fseek(file_io, m_exe_size, SEEK_SET); @@ -143,9 +112,8 @@ json_utils::JsonResult ExeContainer::Read(const std::string& exe_path) { uint32_t json_size = ReadUint32(file_io); uint32_t stored_hash = ReadUint32(file_io); if (JSON_SIZE_MAX <= json_size || end_off < m_exe_size + json_size + 20) { - std::string msg = "Unexpected json size. (" + std::to_string(json_size) + ")"; fclose(file_io); - return { false, msg }; + return { false, "Unexpected json size. (" + std::to_string(json_size) + ")" }; } // Read json data @@ -155,44 +123,40 @@ json_utils::JsonResult ExeContainer::Read(const std::string& exe_path) { if (json_str.length() != json_size) return { false, "Unexpected char detected." }; - if (stored_hash != Fnv1Hash32(json_str)) { - std::string msg = "Invalid JSON hash. (" + std::to_string(stored_hash) + ")"; - return { false, msg }; - } + if (stored_hash != Fnv1Hash32(json_str)) + return { false, "Invalid JSON hash. (" + std::to_string(stored_hash) + ")" }; rapidjson::ParseResult ok = m_json.Parse(json_str.c_str()); if (!ok) { - std::string msg = std::string("Failed to parse JSON: ") - + rapidjson::GetParseError_En(ok.Code()) - + " (offset: " + std::to_string(ok.Offset()) + ")"; - return { false, msg }; + return { + false, + std::string("Failed to parse JSON: ") + + rapidjson::GetParseError_En(ok.Code()) + + " (offset: " + std::to_string(ok.Offset()) + ")" + }; } return JSON_RESULT_OK; } json_utils::JsonResult ExeContainer::Write(const std::string& exe_path) { - assert(m_exe_path != ""); - std::string json_str = ""; + assert(!m_exe_path.empty()); + std::string json_str; if (HasJson()) json_str = json_utils::JsonToString(m_json); - uint32_t json_size = (uint32_t)json_str.length(); - if (JSON_SIZE_MAX <= json_size) { - std::string msg = "Unexpected json size. (" + std::to_string(json_size) + ")"; - return { false, msg }; - } + uint32_t json_size = static_cast(json_str.length()); + if (JSON_SIZE_MAX <= json_size) + return { false, "Unexpected json size. (" + std::to_string(json_size) + ")" }; FILE* old_io = fopen(m_exe_path.c_str(), "rb"); - if (!old_io) { - std::string msg = "Failed to open a file. (" + m_exe_path + ")"; - return { false, msg }; - } + if (!old_io) + return { false, "Failed to open a file. (" + m_exe_path + ")" }; + FILE* new_io = fopen(exe_path.c_str(), "wb"); if (!new_io) { - std::string msg = "Failed to open a file. (" + exe_path + ")"; fclose(old_io); - return { false, msg }; + return { false, "Failed to open a file. (" + exe_path + ")" }; } m_exe_path = exe_path; diff --git a/src/exec.cpp b/src/exec.cpp index 6b7c248..79dcf72 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -6,25 +6,21 @@ #include "windows.h" #endif +inline bool IsNewline(char ch) { + return ch == '\n' || ch == '\r'; +} + static std::string GetLastLine(const std::string& input) { - if (input.length() == 0) return ""; - size_t end = input.length() - 1; - if (input[end] == '\n') { - if (end == 0) return ""; - end--; - } -#ifdef _WIN32 - if (input[end] == '\r') { - if (end == 0) return ""; - end--; - } -#endif + if (input.empty()) return ""; + size_t end = input.length(); + + // Trim trailing newlines + while (end > 0 && IsNewline(input[end - 1])) end--; if (end == 0) return ""; - size_t position = end; - while ((input[position] != '\n') && position > 0) position--; - if (input[position] == '\n') position++; - if (end <= position) return ""; - return input.substr(position, end - position + 1); + + size_t start = end - 1; + while (start > 0 && input[start - 1] != '\n') start--; + return input.substr(start, end - start); } enum READ_IO_TYPE : int { @@ -53,18 +49,9 @@ unsigned ReadIO(subprocess_s &process, } void DestroyProcess(subprocess_s &process, int *return_code, std::string &err_msg) { - int result = subprocess_join(&process, return_code); - if (0 != result) { - *return_code = -1; - err_msg = "Failed to join a subprocess.\n"; - return; - } - - result = subprocess_destroy(&process); - if (0 != result) { + if (subprocess_join(&process, return_code) || subprocess_destroy(&process)) { *return_code = -1; - err_msg = "Failed to destroy a subprocess.\n"; - return; + err_msg = "Failed to manage subprocess.\n"; } } @@ -156,7 +143,7 @@ ExecuteResult LaunchDefaultApp(const std::string& url) { return { -1, "Failed to create a subprocess.\n", ""}; int return_code; - std::string err_msg = ""; + std::string err_msg; DestroyProcess(process, &return_code, err_msg); return { return_code, err_msg, "" }; diff --git a/src/json_utils.cpp b/src/json_utils.cpp index 35afaf8..7f45cc5 100644 --- a/src/json_utils.cpp +++ b/src/json_utils.cpp @@ -38,7 +38,7 @@ namespace json_utils { if (!fp) return { false, "Failed to open " + file }; - char readBuffer[65536]; + char readBuffer[JSON_SIZE_MAX]; rapidjson::FileReadStream is(fp, readBuffer, sizeof(readBuffer)); rapidjson::ParseResult ok = json.ParseStream(is); @@ -61,7 +61,7 @@ namespace json_utils { if (!fp) return { false, "Failed to open " + file + "." }; - char writeBuffer[65536]; + char writeBuffer[JSON_SIZE_MAX]; rapidjson::FileWriteStream os(fp, writeBuffer, sizeof(writeBuffer)); rapidjson::PrettyWriter writer(os); json.Accept(writer); @@ -100,10 +100,10 @@ namespace json_utils { return def; } - static std::string GetLabel(const std::string& label, const char* key) { + static std::string GetLabel(const char* label, const char* key) { std::string msg = "['"; - if (label != "") { - msg += label + "']['"; + if (*label != '\0') { + msg += std::string(label) + "']['"; } msg += key; msg += "']"; @@ -139,18 +139,18 @@ namespace json_utils { return true; } - static const bool CAN_SKIP = true; + static const bool OPTIONAL = true; static void CheckJsonType(JsonResult& result, const rapidjson::Value& j, const char* key, - const JsonType& type, const std::string& label = "", const bool& canSkip = false) { + const JsonType& type, const char* label = "", const bool& optional = false) { if (!j.HasMember(key)) { - if (canSkip) return; + if (optional) return; result.ok = false; result.msg = GetLabel(label, key) + " not found."; return; } bool valid = false; - std::string type_name; + const char* type_name = nullptr; switch (type) { case JsonType::BOOLEAN: valid = j[key].IsBool(); @@ -222,25 +222,22 @@ namespace json_utils { } } - static std::vector SplitString(const std::string& s, - const std::string& delimiter) { - std::vector tokens = std::vector(0); - std::string token; - size_t delim_length = delimiter.length(); - if (delim_length == 0) { - tokens.push_back(s); - return tokens; - } + static std::vector SplitString(const char* str, + const char delimiter) { + if (!str) + return {}; + + std::vector tokens; - size_t offset = std::string::size_type(0); - while (s.length() > offset) { - size_t pos = s.find(delimiter, offset); - if (pos == std::string::npos) { - tokens.push_back(s.substr(offset)); + const char* start = str; + while (*start != '\0') { + const char* pos = strchr(start, delimiter); + if (!pos) { + tokens.emplace_back(start); break; } - tokens.push_back(s.substr(offset, pos - offset)); - offset = pos + delim_length; + tokens.emplace_back(start, pos - start); + start = pos + 1; } return tokens; } @@ -251,13 +248,13 @@ namespace json_utils { if (size == 0) return; for (size_t i = 0; i < size - 1; i++) { - std::string str = component_ids[i]; - if (str == "") { continue; } + const std::string& str = component_ids[i]; + if (str.empty()) { continue; } for (size_t j = i + 1; j < size; j++) { if (str == component_ids[j]) { result.ok = false; - result.msg = GetLabel("components", "id") - + " should not be duplicated in a gui definition. (" + result.msg = "[components][id]" + " should not be duplicated in a gui definition. (" + str + ")"; return; } @@ -270,8 +267,7 @@ namespace json_utils { rapidjson::Value& sub_definition, const std::vector& comp_ids, rapidjson::Document::AllocatorType& alloc) { - std::vector cmd = SplitString(sub_definition["command"].GetString(), - { '%' }); + std::vector cmd = SplitString(sub_definition["command"].GetString(), '%'); std::vector cmd_ids = std::vector(0); std::vector splitted_cmd = std::vector(0); if (sub_definition.HasMember("command_splitted")) @@ -281,9 +277,9 @@ namespace json_utils { bool store_ids = false; for (const std::string& token : cmd) { if (store_ids) { - cmd_ids.push_back(token); + cmd_ids.emplace_back(token); } else { - splitted_cmd.push_back(token); + splitted_cmd.emplace_back(token); rapidjson::Value n(token.c_str(), alloc); splitted_cmd_json.PushBack(n, alloc); } @@ -293,12 +289,12 @@ namespace json_utils { rapidjson::Value& components = sub_definition["components"];; rapidjson::Value cmd_int_ids(rapidjson::kArrayType); - std::string cmd_str = ""; + std::string cmd_str; int comp_size = static_cast(comp_ids.size()); int non_id_comp = 0; for (int i = 0; i < static_cast(cmd_ids.size()); i++) { cmd_str += splitted_cmd[i]; - std::string id = cmd_ids[i]; + const std::string& id = cmd_ids[i]; int j; if (id == CMD_TOKEN_PERCENT) { j = CMD_ID_PERCENT; @@ -315,7 +311,7 @@ namespace json_utils { if (j == comp_size) { while (non_id_comp < comp_size && (components[non_id_comp]["type_int"] == COMP_STATIC_TEXT - || comp_ids[non_id_comp] != "" + || !comp_ids[non_id_comp].empty() || components[non_id_comp]["type_int"] == COMP_EMPTY)) { non_id_comp++; } @@ -345,7 +341,7 @@ namespace json_utils { result.ok = false; result.msg = "[\"components\"][" + std::to_string(j) + "] is unused in the command; " + cmd_str; - if (comp_ids[j] != "") + if (!comp_ids[j].empty()) result.msg = "The ID of " + result.msg; return; } @@ -401,15 +397,15 @@ namespace json_utils { } void CheckValidator(JsonResult& result, rapidjson::Value& validator, - const std::string& label) { - CheckJsonType(result, validator, "regex", JsonType::STRING, label, CAN_SKIP); - CheckJsonType(result, validator, "regex_error", JsonType::STRING, label, CAN_SKIP); - CheckJsonType(result, validator, "wildcard", JsonType::STRING, label, CAN_SKIP); - CheckJsonType(result, validator, "wildcard_error", JsonType::STRING, label, CAN_SKIP); - CheckJsonType(result, validator, "exist", JsonType::BOOLEAN, label, CAN_SKIP); - CheckJsonType(result, validator, "exist_error", JsonType::STRING, label, CAN_SKIP); - CheckJsonType(result, validator, "not_empty", JsonType::BOOLEAN, label, CAN_SKIP); - CheckJsonType(result, validator, "not_empty_error", JsonType::STRING, label, CAN_SKIP); + const char* label) { + CheckJsonType(result, validator, "regex", JsonType::STRING, label, OPTIONAL); + CheckJsonType(result, validator, "regex_error", JsonType::STRING, label, OPTIONAL); + CheckJsonType(result, validator, "wildcard", JsonType::STRING, label, OPTIONAL); + CheckJsonType(result, validator, "wildcard_error", JsonType::STRING, label, OPTIONAL); + CheckJsonType(result, validator, "exist", JsonType::BOOLEAN, label, OPTIONAL); + CheckJsonType(result, validator, "exist_error", JsonType::STRING, label, OPTIONAL); + CheckJsonType(result, validator, "not_empty", JsonType::BOOLEAN, label, OPTIONAL); + CheckJsonType(result, validator, "not_empty_error", JsonType::STRING, label, OPTIONAL); } // validate one of definitions (["gui"][i]) and store parsed info @@ -417,22 +413,23 @@ namespace json_utils { rapidjson::Document::AllocatorType& alloc) { // check is_string CheckJsonType(result, sub_definition, "label", JsonType::STRING); - CheckJsonType(result, sub_definition, "button", JsonType::STRING, "", CAN_SKIP); + CheckJsonType(result, sub_definition, "button", JsonType::STRING, "", OPTIONAL); CorrectKey(sub_definition, "window_title", "window_name", alloc); CorrectKey(sub_definition, "title", "window_name", alloc); - CheckJsonType(result, sub_definition, "window_name", JsonType::STRING, "", CAN_SKIP); + CheckJsonType(result, sub_definition, "window_name", JsonType::STRING, "", OPTIONAL); - CheckJsonType(result, sub_definition, "check_exit_code", JsonType::BOOLEAN, "", CAN_SKIP); - CheckJsonType(result, sub_definition, "exit_success", JsonType::INTEGER, "", CAN_SKIP); - CheckJsonType(result, sub_definition, "show_last_line", JsonType::BOOLEAN, "", CAN_SKIP); + CheckJsonType(result, sub_definition, "check_exit_code", JsonType::BOOLEAN, "", OPTIONAL); + CheckJsonType(result, sub_definition, "exit_success", JsonType::INTEGER, "", OPTIONAL); + CheckJsonType(result, sub_definition, "show_last_line", JsonType::BOOLEAN, "", OPTIONAL); CheckJsonType(result, sub_definition, - "show_success_dialog", JsonType::BOOLEAN, "", CAN_SKIP); - CheckJsonType(result, sub_definition, "codepage", JsonType::STRING, "", CAN_SKIP); + "show_success_dialog", JsonType::BOOLEAN, "", OPTIONAL); + CheckJsonType(result, sub_definition, "codepage", JsonType::STRING, "", OPTIONAL); if (sub_definition.HasMember("codepage")) { - std::string codepage = sub_definition["codepage"].GetString(); - if (codepage != "utf8" && codepage != "utf-8" && codepage != "default") { + const char* codepage = sub_definition["codepage"].GetString(); + if (strcmp(codepage, "utf8") != 0 && strcmp(codepage, "utf-8") != 0 && + strcmp(codepage, "default") != 0) { result.ok = false; - result.msg = "Unknown codepage: " + codepage; + result.msg = std::string("Unknown codepage: ") + codepage; return; } } @@ -449,13 +446,13 @@ namespace json_utils { // check if type and label exist CheckJsonType(result, c, "label", JsonType::STRING, "components"); if (!result.ok) return; - std::string label = c["label"].GetString(); + const char* label = c["label"].GetString(); // convert ["type"] from string to enum. CheckJsonType(result, c, "type", JsonType::STRING, label); if (!result.ok) return; - std::string type_str = c["type"].GetString(); - int type = ComptypeToInt(type_str.c_str()); + const char* type_str = c["type"].GetString(); + int type = ComptypeToInt(type_str); if (c.HasMember("type_int")) c.RemoveMember("type_int"); c.AddMember("type_int", type, alloc); @@ -463,13 +460,13 @@ namespace json_utils { CorrectKey(c, "item_array", "items", alloc); switch (type) { case COMP_FILE: - CheckJsonType(result, c, "extension", JsonType::STRING, label, CAN_SKIP); + CheckJsonType(result, c, "extension", JsonType::STRING, label, OPTIONAL); /* Falls through. */ case COMP_FOLDER: - CheckJsonType(result, c, "button", JsonType::STRING, label, CAN_SKIP); + CheckJsonType(result, c, "button", JsonType::STRING, label, OPTIONAL); /* Falls through. */ case COMP_TEXT: - CheckJsonType(result, c, "default", JsonType::STRING, label, CAN_SKIP); + CheckJsonType(result, c, "default", JsonType::STRING, label, OPTIONAL); break; case COMP_COMBO: case COMP_RADIO: @@ -477,22 +474,22 @@ namespace json_utils { if (!result.ok) return; for (rapidjson::Value& i : c["items"].GetArray()) { CheckJsonType(result, i, "label", JsonType::STRING, "items"); - CheckJsonType(result, i, "value", JsonType::STRING, "items", CAN_SKIP); + CheckJsonType(result, i, "value", JsonType::STRING, "items", OPTIONAL); } - CheckJsonType(result, c, "default", JsonType::INTEGER, label, CAN_SKIP); + CheckJsonType(result, c, "default", JsonType::INTEGER, label, OPTIONAL); break; case COMP_CHECK: - CheckJsonType(result, c, "value", JsonType::STRING, label, CAN_SKIP); - CheckJsonType(result, c, "default", JsonType::BOOLEAN, label, CAN_SKIP); + CheckJsonType(result, c, "value", JsonType::STRING, label, OPTIONAL); + CheckJsonType(result, c, "default", JsonType::BOOLEAN, label, OPTIONAL); break; case COMP_CHECK_ARRAY: CheckJsonType(result, c, "items", JsonType::JSON_ARRAY, label); if (!result.ok) return; for (rapidjson::Value& i : c["items"].GetArray()) { CheckJsonType(result, i, "label", JsonType::STRING, "items"); - CheckJsonType(result, i, "value", JsonType::STRING, "items", CAN_SKIP); - CheckJsonType(result, i, "default", JsonType::BOOLEAN, "items", CAN_SKIP); - CheckJsonType(result, i, "tooltip", JsonType::STRING, "items", CAN_SKIP); + CheckJsonType(result, i, "value", JsonType::STRING, "items", OPTIONAL); + CheckJsonType(result, i, "default", JsonType::BOOLEAN, "items", OPTIONAL); + CheckJsonType(result, i, "tooltip", JsonType::STRING, "items", OPTIONAL); } break; case COMP_INT: @@ -502,7 +499,7 @@ namespace json_utils { jtype = JsonType::INTEGER; } else { jtype = JsonType::FLOAT; - CheckJsonType(result, c, "digits", JsonType::INTEGER, label, CAN_SKIP); + CheckJsonType(result, c, "digits", JsonType::INTEGER, label, OPTIONAL); if (!result.ok) return; if (c.HasMember("digits") && c["digits"].GetInt() < 0) { result.ok = false; @@ -510,15 +507,15 @@ namespace json_utils { + " should be a non-negative integer."; } } - CheckJsonType(result, c, "default", jtype, label, CAN_SKIP); - CheckJsonType(result, c, "min", jtype, label, CAN_SKIP); - CheckJsonType(result, c, "max", jtype, label, CAN_SKIP); - CheckJsonType(result, c, "inc", jtype, label, CAN_SKIP); - CheckJsonType(result, c, "wrap", JsonType::BOOLEAN, label, CAN_SKIP); + CheckJsonType(result, c, "default", jtype, label, OPTIONAL); + CheckJsonType(result, c, "min", jtype, label, OPTIONAL); + CheckJsonType(result, c, "max", jtype, label, OPTIONAL); + CheckJsonType(result, c, "inc", jtype, label, OPTIONAL); + CheckJsonType(result, c, "wrap", JsonType::BOOLEAN, label, OPTIONAL); break; case COMP_UNKNOWN: result.ok = false; - result.msg = "Unknown component type: " + type_str; + result.msg = std::string("Unknown component type: ") + type_str; break; } if (!result.ok) return; @@ -535,35 +532,34 @@ namespace json_utils { } CorrectKey(c, "add_quote", "add_quotes", alloc); - CheckJsonType(result, c, "add_quotes", JsonType::BOOLEAN, label, CAN_SKIP); + CheckJsonType(result, c, "add_quotes", JsonType::BOOLEAN, label, OPTIONAL); CorrectKey(c, "empty_message", "placeholder", alloc); - CheckJsonType(result, c, "placeholder", JsonType::STRING, label, CAN_SKIP); - CheckJsonType(result, c, "id", JsonType::STRING, label, CAN_SKIP); - CheckJsonType(result, c, "tooltip", JsonType::STRING, label, CAN_SKIP); + CheckJsonType(result, c, "placeholder", JsonType::STRING, label, OPTIONAL); + CheckJsonType(result, c, "id", JsonType::STRING, label, OPTIONAL); + CheckJsonType(result, c, "tooltip", JsonType::STRING, label, OPTIONAL); bool ignore = false; CorrectKey(c, "platform", "platforms", alloc); CorrectKey(c, "platform_array", "platforms", alloc); - CheckJsonType(result, c, "platforms", JsonType::STRING_ARRAY, label, CAN_SKIP); + CheckJsonType(result, c, "platforms", JsonType::STRING_ARRAY, label, OPTIONAL); if (!result.ok) return; if (c.HasMember("platforms")) { ignore = true; for (rapidjson::Value& v : c["platforms"].GetArray()) { - if (v.GetString() == std::string(tuw_constants::OS)) { + if (strcmp(v.GetString(), TUW_CONSTANTS_OS) == 0) { ignore = false; break; } } } - std::string id = ""; + const char* id = GetString(c, "id", ""); if (c.HasMember("id")) { - id = c["id"].GetString(); - if (id == "") { + if (id[0] == '\0') { result.ok = false; result.msg = GetLabel(label, "id") + " should NOT be an empty string."; - } else if (id[0] == "_"[0]) { + } else if (id[0] == '_') { result.ok = false; result.msg = GetLabel(label, "id") + " should NOT start with '_'."; @@ -572,24 +568,24 @@ namespace json_utils { if (!result.ok) return; if (ignore) { - comp_ids.push_back(""); + comp_ids.emplace_back(""); c["type_int"].SetInt(COMP_EMPTY); } else { - comp_ids.push_back(id); + comp_ids.emplace_back(id); } } CheckIndexDuplication(result, comp_ids); if (!result.ok) return; // Overwrite ["command"] with ["command_'os'"] if exists. - std::string command_os_key = std::string("command_") + tuw_constants::OS; - if (sub_definition.HasMember(command_os_key.c_str())) { - CheckJsonType(result, sub_definition, command_os_key.c_str(), JsonType::STRING); + const char* command_os_key = "command_" TUW_CONSTANTS_OS; + if (sub_definition.HasMember(command_os_key)) { + CheckJsonType(result, sub_definition, command_os_key, JsonType::STRING); if (!result.ok) return; - std::string command_os = sub_definition[command_os_key.c_str()].GetString(); + const char* command_os = sub_definition[command_os_key].GetString(); if (sub_definition.HasMember("command")) sub_definition.RemoveMember("command"); - rapidjson::Value v(command_os.c_str(), alloc); + rapidjson::Value v(command_os, alloc); sub_definition.AddMember("command", v, alloc); } @@ -600,15 +596,15 @@ namespace json_utils { } // vX.Y.Z -> 10000*X + 100 * Y + Z - static int VersionStringToInt(JsonResult& result, const std::string& string) { + static int VersionStringToInt(JsonResult& result, const char* string) { std::vector version_strings = - SplitString(string, { '.' }); + SplitString(string, '.'); int digit = 10000; int version_int = 0; for (const std::string& str : version_strings) { if (str.length() == 0 || str.length() > 2) { result.ok = false; - result.msg = "Can NOT convert '" + string + "' to int."; + result.msg = std::string("Can NOT convert '") + string + "' to int."; return 0; } if (str.length() == 1) { @@ -639,12 +635,11 @@ namespace json_utils { if (definition.HasMember("minimum_required")) { CheckJsonType(result, definition, "minimum_required", JsonType::STRING); if (!result.ok) return; - std::string required = definition["minimum_required"].GetString(); - if (!result.ok) return; + const char* required = definition["minimum_required"].GetString(); int required_int = VersionStringToInt(result, required); if (tuw_constants::VERSION_INT < required_int) { result.ok = false; - result.msg = "Version " + required + " is required."; + result.msg = std::string("Version ") + required + " is required."; } } } @@ -671,14 +666,14 @@ namespace json_utils { CheckJsonType(result, h, "type", JsonType::STRING); CheckJsonType(result, h, "label", JsonType::STRING); if (!result.ok) return; - std::string type = h["type"].GetString(); - if (type == "url") { + const char* type = h["type"].GetString(); + if (strcmp(type, "url") == 0) { CheckJsonType(result, h, "url", JsonType::STRING); - } else if (type == "file") { + } else if (strcmp(type, "file") == 0) { CheckJsonType(result, h, "path", JsonType::STRING); } else { result.ok = false; - result.msg = "Unsupported help type: " + type; + result.msg = std::string("Unsupported help type: ") + type; return; } } diff --git a/src/main.cpp b/src/main.cpp index 2aa04c2..2af174c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -210,9 +210,9 @@ int main(int argc, char* argv[]) { std::vector args; for (int i = 0; i < argc; i++) { #ifdef _WIN32 - args.push_back(UTF16toUTF8(argv[i])); + args.emplace_back(UTF16toUTF8(argv[i])); #else - args.push_back(argv[i]); + args.emplace_back(argv[i]); #endif } char *exe_path_cstr = envuGetExecutablePath(); @@ -232,8 +232,8 @@ int main(int argc, char* argv[]) { return 1; } - std::string json_path = ""; - std::string new_exe_path = ""; + std::string json_path; + std::string new_exe_path; bool force = false; for (int i = 2; i < argc; i++) { @@ -268,14 +268,14 @@ int main(int argc, char* argv[]) { } } - if (json_path == "") { + if (json_path.empty()) { if (cmd_int == CMD_MERGE) json_path = "gui_definition.json"; else json_path = "new.json"; } - if (new_exe_path == "") + if (new_exe_path.empty()) new_exe_path = exe_path + ".new"; json_path = envuStr(envuGetFullPath(json_path.c_str())); diff --git a/src/main_frame.cpp b/src/main_frame.cpp index abbaeb8..3eb1ed9 100644 --- a/src/main_frame.cpp +++ b/src/main_frame.cpp @@ -225,11 +225,11 @@ static bool IsValidURL(const std::string &url) { void MainFrame::OpenURL(int id) { rapidjson::Value& help = m_definition["help"].GetArray()[id]; - std::string type = help["type"].GetString(); - std::string url = ""; - std::string tag; + const char* type = help["type"].GetString(); + std::string url; + const char* tag = ""; - if (type == "url") { + if (strcmp(type, "url") == 0) { url = help["url"].GetString(); tag = "[OpenURL] "; @@ -239,13 +239,13 @@ void MainFrame::OpenURL(int id) { // scheme should be http or https if (scheme == "file") { std::string msg = "Use 'file' type for a path, not 'url' type. (" + url + ")"; - PrintFmt("%sError: %s\n", tag.c_str(), msg.c_str()); + PrintFmt("%sError: %s\n", tag, msg.c_str()); ShowErrorDialog(msg); return; } else if (scheme != "https" && scheme != "http") { std::string msg = "Unsupported scheme detected. " "It should be http or https. (" + scheme + ")"; - PrintFmt("%sError: %s\n", tag.c_str(), msg.c_str()); + PrintFmt("%sError: %s\n", tag, msg.c_str()); ShowErrorDialog(msg); return; } @@ -253,7 +253,7 @@ void MainFrame::OpenURL(int id) { url = "https://" + url; } - } else if (type == "file") { + } else if (strcmp(type, "file") == 0) { char *url_cstr = envuGetRealPath(help["path"].GetString()); int exists = envuFileExists(url_cstr); url = envuStr(url_cstr); @@ -261,22 +261,22 @@ void MainFrame::OpenURL(int id) { if (!exists) { std::string msg = "File does not exist. (" + url + ")"; - PrintFmt("%sError: %s\n", tag.c_str(), msg.c_str()); + PrintFmt("%sError: %s\n", tag, msg.c_str()); ShowErrorDialog(msg); return; } } - PrintFmt("%s%s\n", tag.c_str(), url.c_str()); + PrintFmt("%s%s\n", tag, url.c_str()); - if (type == "file") { + if (strcmp(type, "file") == 0) { url = "file:" + url; } if (!IsValidURL(url)) { std::string msg = "URL should NOT contains ' ', ';', '|', '&', '\\r', nor '\\n'.\n" "URL: " + url; - PrintFmt("%sError: %s\n", tag.c_str(), msg.c_str()); + PrintFmt("%sError: %s\n", tag, msg.c_str()); ShowErrorDialog(msg.c_str()); return; } @@ -290,8 +290,8 @@ void MainFrame::OpenURL(int id) { } else { ExecuteResult result = LaunchDefaultApp(url); if (result.exit_code != 0) { - std::string msg = "Failed to open a " + type + " by an unexpected error."; - PrintFmt("%sError: %s\n", tag.c_str(), msg.c_str()); + std::string msg = std::string("Failed to open a ") + type + " by an unexpected error."; + PrintFmt("%sError: %s\n", tag, msg.c_str()); ShowErrorDialog(msg.c_str()); } } @@ -339,7 +339,7 @@ void MainFrame::UpdatePanel(unsigned definition_id) { uiBoxSetSpacing(priv_box, tuw_constants::BOX_SUB_SPACE); new_comp = Component::PutComponent(priv_box, c); new_comp->SetConfig(m_config); - m_components.push_back(new_comp); + m_components.emplace_back(new_comp); uiBoxAppend(main_box, uiControl(priv_box), 0); } } @@ -387,7 +387,7 @@ bool MainFrame::Validate() { std::string val_first_err; for (Component* comp : m_components) { if (!comp->Validate(&redraw_flag)) { - std::string val_err = comp->GetValidationError(); + const std::string& val_err = comp->GetValidationError(); if (validate) val_first_err = val_err; validate = false; @@ -413,10 +413,10 @@ std::string MainFrame::GetCommand() { std::vector cmd_ary; rapidjson::Value& sub_definition = m_definition["gui"][m_definition_id]; for (rapidjson::Value& c : sub_definition["command_splitted"].GetArray()) - cmd_ary.push_back(c.GetString()); + cmd_ary.emplace_back(c.GetString()); std::vector cmd_ids; for (rapidjson::Value& c : sub_definition["command_ids"].GetArray()) - cmd_ids.push_back(c.GetInt()); + cmd_ids.emplace_back(c.GetInt()); std::vector comp_strings = std::vector(m_components.size()); for (size_t i = 0; i < m_components.size(); i++) { @@ -464,8 +464,8 @@ void MainFrame::RunCommand() { #endif rapidjson::Value& sub_definition = m_definition["gui"][m_definition_id]; - std::string codepage = json_utils::GetString(sub_definition, "codepage", ""); - bool use_utf8_on_windows = codepage == "utf8" || codepage == "utf-8"; + const char* codepage = json_utils::GetString(sub_definition, "codepage", ""); + bool use_utf8_on_windows = strcmp(codepage, "utf8") == 0 || strcmp(codepage, "utf-8") == 0; ExecuteResult result = Execute(cmd, use_utf8_on_windows); uiButtonSetText(m_run_button, text); @@ -475,7 +475,7 @@ void MainFrame::RunCommand() { bool show_last_line = json_utils::GetBool(sub_definition, "show_last_line", false); bool show_success_dialog = json_utils::GetBool(sub_definition, "show_success_dialog", true); - if (result.err_msg != "") { + if (!result.err_msg.empty()) { PrintFmt("[RunCommand] Error: %s\n", result.err_msg.c_str()); ShowErrorDialog(result.err_msg); return; @@ -497,7 +497,7 @@ void MainFrame::RunCommand() { return; } - if (show_last_line && result.last_line != "") { + if (show_last_line && !result.last_line.empty()) { ShowSuccessDialog(result.last_line); return; } diff --git a/src/string_utils.cpp b/src/string_utils.cpp index 5acce5a..2675867 100644 --- a/src/string_utils.cpp +++ b/src/string_utils.cpp @@ -358,7 +358,7 @@ class Logger { void SetLogEntry(void* log_entry) { m_log_entry = static_cast(log_entry); - if (m_log_buffer != "") { + if (!m_log_buffer.empty()) { Log(m_log_buffer.c_str()); m_log_buffer = ""; } diff --git a/src/validator.cpp b/src/validator.cpp index fe1fa24..1bc3d69 100644 --- a/src/validator.cpp +++ b/src/validator.cpp @@ -33,29 +33,29 @@ static int IsUnsupportedPattern(const char *pattern) { } bool Validator::Validate(const std::string& str) { - if (m_not_empty && str == "") { - if (m_not_empty_error == "") + if (m_not_empty && str.empty()) { + if (m_not_empty_error.empty()) m_error_msg = "Empty string is NOT allowed."; else m_error_msg = m_not_empty_error; return false; } - if (m_wildcard != "") { + if (!m_wildcard.empty()) { if (tsm_wildcard_match(m_wildcard.c_str(), str.c_str()) != TSM_OK) { - if (m_wildcard_error == "") + if (m_wildcard_error.empty()) m_error_msg = "Wildcard match failed for pattern: " + m_wildcard; else m_error_msg = m_wildcard_error; return false; } } - if (m_regex != "") { + if (!m_regex.empty()) { if (IsUnsupportedPattern(m_regex.c_str())) { m_error_msg = "Regex compile error: () operators are not supported."; return false; } int res = tsm_regex_match(m_regex.c_str(), str.c_str()); - if (res != TSM_OK && m_regex_error != "") { + if (res != TSM_OK && !m_regex_error.empty()) { m_error_msg = m_regex_error; return false; } @@ -68,7 +68,7 @@ bool Validator::Validate(const std::string& str) { } } if (m_exist && !envuPathExists(str.c_str())) { - if (m_exist_error == "") + if (m_exist_error.empty()) m_error_msg = "Path does NOT exist."; else m_error_msg = m_exist_error;