From cf29a263c4a08f06552f7e1da5fbb7f322923dfa Mon Sep 17 00:00:00 2001 From: Alexey Gruzdev Date: Thu, 15 Jan 2026 20:54:57 +0300 Subject: [PATCH 1/2] PluginXBM: use regex in Load() as standard-compliant and safe text parser --- Source/Plugins/PluginXBM.cpp | 100 ++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/Source/Plugins/PluginXBM.cpp b/Source/Plugins/PluginXBM.cpp index da89c5d..23ec28a 100644 --- a/Source/Plugins/PluginXBM.cpp +++ b/Source/Plugins/PluginXBM.cpp @@ -25,11 +25,13 @@ #include "FreeImage.h" #include "Utilities.h" +#include + // ========================================================== // Internal functions // ========================================================== -#define MAX_LINE 512 +constexpr int MAX_LINE = 512; static const char *ERR_XBM_SYNTAX = "Syntax error"; static const char *ERR_XBM_LINE = "Line too long"; @@ -87,76 +89,76 @@ Read an XBM file into a buffer */ static const char* readXBMFile(FreeImageIO *io, fi_handle handle, int *widthP, int *heightP, std::unique_ptr &dataP) { - char line[MAX_LINE], name_and_type[MAX_LINE]; - char *ptr{}; - char *t{}; + std::string line(MAX_LINE, '\0'); + char *ptr{}; + char *t{}; int version = 0; size_t bytes, bytes_per_line, raster_length; - int v, padding; int c1, c2, value1, value2; int hex_table[256]; - bool found_declaration{}; // haven't found it yet; haven't even looked + bool found_declaration{}; // haven't found it yet; haven't even looked /* in scanning through the bitmap file, we have found the first - line of the C declaration of the array (the "static char ..." - or whatever line) + line of the C declaration of the array (the "static char ..." or whatever line) */ - bool eof{}; // we've encountered end of file while searching file - *widthP = *heightP = -1; + const std::regex width_regex(R"(\s*#define\s+(\w+)_width\s+(\d+)\s*)"); + const std::regex height_regex(R"(\s*#define\s+(\w+)_height\s+(\d+)\s*)"); + const std::regex decl_v10_regex(R"(\s*static\s+short\s+(\w+)\s*\[\s*\]\s*=\s*\{\s*)"); + const std::regex decl_v11_regex(R"(\s*static\s+(unsigned\s+)?char\s+(\w+)\s*\[\s*\]\s*=\s*\{\s*)"); + std::cmatch match; - while (!found_declaration && !eof) { + *widthP = *heightP = -1; - if (!readLine(line, MAX_LINE, io, handle)) { - eof = true; + for(;;) { + if (!readLine(line.data(), MAX_LINE, io, handle)) { + break; + } + + if (strlen(line.c_str()) >= MAX_LINE - 1) { + return ERR_XBM_LINE; } - else { - if (strlen(line) == MAX_LINE - 1) - return( ERR_XBM_LINE ); - if (sscanf_s(line, "#define %s %d", name_and_type, MAX_LINE, &v) == 2) { - if ((t = strrchr(name_and_type, '_')) == nullptr) - t = name_and_type; - else - t++; - if (!strcmp("width", t)) - *widthP = v; - else if (!strcmp("height", t)) - *heightP = v; - continue; - } - if (sscanf_s(line, "static short %s = {", name_and_type, MAX_LINE) == 1) { - version = 10; - found_declaration = true; - } - else if (sscanf_s(line, "static char %s = {", name_and_type, MAX_LINE) == 1) { - version = 11; - found_declaration = true; - } - else if (sscanf_s(line, "static unsigned char %s = {", name_and_type, MAX_LINE) == 1) { - version = 11; - found_declaration = true; - } + if (std::regex_match(line.c_str(), match, width_regex)) { + *widthP = std::stoi(match.str(2)); + } + else if (std::regex_match(line.c_str(), match, height_regex)) { + *heightP = std::stoi(match.str(2)); + } + else if (std::regex_match(line.c_str(), match, decl_v10_regex)) { + version = 10; + found_declaration = true; + break; + } + else if (std::regex_match(line.c_str(), match, decl_v11_regex)) { + version = 11; + found_declaration = true; + break; } } - if (!found_declaration) - return( ERR_XBM_DECL ); + if (!found_declaration) { + return ERR_XBM_DECL; + } - if (*widthP == -1) - return( ERR_XBM_WIDTH ); - if (*heightP == -1) - return( ERR_XBM_HEIGHT ); + if (*widthP <= 0) { + return ERR_XBM_WIDTH; + } + if (*heightP <= 0) { + return ERR_XBM_HEIGHT; + } - padding = 0; - if ( ((*widthP % 16) >= 1) && ((*widthP % 16) <= 8) && (version == 10) ) + int padding = 0; + if (((*widthP % 16) >= 1) && ((*widthP % 16) <= 8) && (version == 10)) { padding = 1; + } bytes_per_line = (*widthP + 7) / 8 + padding; raster_length = bytes_per_line * *heightP; dataP.reset(malloc(raster_length)); - if (!dataP) - return( ERR_XBM_MEMORY ); + if (!dataP) { + return ERR_XBM_MEMORY; + } // initialize hex_table for ( c1 = 0; c1 < 256; c1++ ) { From a187195d766eaa4d5db5f28f8cb176c28ce15868 Mon Sep 17 00:00:00 2001 From: Alexey Gruzdev Date: Fri, 16 Jan 2026 15:36:41 +0300 Subject: [PATCH 2/2] PluginXBM: use sscanf for simplicity --- Source/Plugins/PluginXBM.cpp | 45 ++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/Source/Plugins/PluginXBM.cpp b/Source/Plugins/PluginXBM.cpp index 23ec28a..79ae105 100644 --- a/Source/Plugins/PluginXBM.cpp +++ b/Source/Plugins/PluginXBM.cpp @@ -25,13 +25,15 @@ #include "FreeImage.h" #include "Utilities.h" -#include - // ========================================================== // Internal functions // ========================================================== -constexpr int MAX_LINE = 512; +#define MAX_LINE 512 +#define LINE_FORMAT_WIDTH_OR_HEIGHT "#define %" FI_QUOTE(MAX_LINE) "s %d" +#define LINE_FORMAT_BITS_V10 "static short %" FI_QUOTE(MAX_LINE) "s = {" +#define LINE_FORMAT_BITS_V11_OPT1 "static char %" FI_QUOTE(MAX_LINE) "s = {" +#define LINE_FORMAT_BITS_V11_OPT2 "static unsigned char %" FI_QUOTE(MAX_LINE) "s = {" static const char *ERR_XBM_SYNTAX = "Syntax error"; static const char *ERR_XBM_LINE = "Line too long"; @@ -89,9 +91,8 @@ Read an XBM file into a buffer */ static const char* readXBMFile(FreeImageIO *io, fi_handle handle, int *widthP, int *heightP, std::unique_ptr &dataP) { - std::string line(MAX_LINE, '\0'); + std::string line(MAX_LINE, '\0'), name(MAX_LINE + 1, '\0'); char *ptr{}; - char *t{}; int version = 0; size_t bytes, bytes_per_line, raster_length; int c1, c2, value1, value2; @@ -101,35 +102,39 @@ readXBMFile(FreeImageIO *io, fi_handle handle, int *widthP, int *heightP, std::u line of the C declaration of the array (the "static char ..." or whatever line) */ - const std::regex width_regex(R"(\s*#define\s+(\w+)_width\s+(\d+)\s*)"); - const std::regex height_regex(R"(\s*#define\s+(\w+)_height\s+(\d+)\s*)"); - const std::regex decl_v10_regex(R"(\s*static\s+short\s+(\w+)\s*\[\s*\]\s*=\s*\{\s*)"); - const std::regex decl_v11_regex(R"(\s*static\s+(unsigned\s+)?char\s+(\w+)\s*\[\s*\]\s*=\s*\{\s*)"); - std::cmatch match; - *widthP = *heightP = -1; - for(;;) { + for (;;) { if (!readLine(line.data(), MAX_LINE, io, handle)) { break; } - + if (strlen(line.c_str()) >= MAX_LINE - 1) { return ERR_XBM_LINE; } - if (std::regex_match(line.c_str(), match, width_regex)) { - *widthP = std::stoi(match.str(2)); - } - else if (std::regex_match(line.c_str(), match, height_regex)) { - *heightP = std::stoi(match.str(2)); + int val{}; + if (2 == std::sscanf(line.c_str(), LINE_FORMAT_WIDTH_OR_HEIGHT, name.data(), &val)) { + if (const auto suffix = std::strrchr(name.c_str(), '_')) { + if (0 == std::strcmp("_width", suffix)) { + *widthP = val; + continue; + } + if (0 == std::strcmp("_height", suffix)) { + *heightP = val; + continue; + } + } } - else if (std::regex_match(line.c_str(), match, decl_v10_regex)) { + + if (1 == std::sscanf(line.c_str(), LINE_FORMAT_BITS_V10, name.data())) { version = 10; found_declaration = true; break; } - else if (std::regex_match(line.c_str(), match, decl_v11_regex)) { + + if (1 == std::sscanf(line.c_str(), LINE_FORMAT_BITS_V11_OPT1, name.data()) || + 1 == std::sscanf(line.c_str(), LINE_FORMAT_BITS_V11_OPT2, name.data())) { version = 11; found_declaration = true; break;