From: Joel Rosdahl Date: Mon, 20 Oct 2025 19:04:19 +0000 (+0200) Subject: fix: Pass unsigned char to std::is* and std::tolower X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e707de01b2fca1b54e513270f5ee35e0d9ea6266;p=thirdparty%2Fccache.git fix: Pass unsigned char to std::is* and std::tolower Passing (negative) char is technically undefined behavior. --- diff --git a/src/ccache/argprocessing.cpp b/src/ccache/argprocessing.cpp index 063bac34..76807a8e 100644 --- a/src/ccache/argprocessing.cpp +++ b/src/ccache/argprocessing.cpp @@ -673,7 +673,7 @@ process_option_arg(const Context& ctx, } if (util::starts_with(arg, "-x")) { - if (arg.length() >= 3 && !islower(arg[2])) { + if (arg.length() >= 3 && !util::is_lower(arg[2])) { // -xCODE (where CODE can be e.g. Host or CORE-AVX2, always starting with // an uppercase letter) is an ordinary Intel compiler option, not a // language specification. (GCC's "-x" language argument is always diff --git a/src/ccache/core/common.cpp b/src/ccache/core/common.cpp index 136aaa2f..0a6ac2b8 100644 --- a/src/ccache/core/common.cpp +++ b/src/ccache/core/common.cpp @@ -227,7 +227,7 @@ get_diagnostics_path_length(std::string_view line) do { line.remove_suffix(1); } while (!line.empty() && line.back() != '(' - && (std::isdigit(line.back()) || line.back() == ',')); + && (util::is_digit(line.back()) || line.back() == ',')); if (!line.empty() && line.back() == '(') { path_end = line.size() - 1; diff --git a/src/ccache/core/msvcshowincludesoutput.cpp b/src/ccache/core/msvcshowincludesoutput.cpp index 8bc8798b..66d5518f 100644 --- a/src/ccache/core/msvcshowincludesoutput.cpp +++ b/src/ccache/core/msvcshowincludesoutput.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2022-2024 Joel Rosdahl and other contributors +// Copyright (C) 2022-2025 Joel Rosdahl and other contributors // // See doc/authors.adoc for a complete list of contributors. // @@ -36,7 +36,7 @@ get_includes(std::string_view file_content, std::string_view prefix) for (std::string_view line : util::split_into_views(file_content, "\r\n")) { if (util::starts_with(line, prefix)) { size_t pos = prefix.size(); - while (pos < line.size() && isspace(line[pos])) { + while (pos < line.size() && util::is_space(line[pos])) { ++pos; } std::string_view include = line.substr(pos); diff --git a/src/ccache/depfile.cpp b/src/ccache/depfile.cpp index 03d710e7..9d823a84 100644 --- a/src/ccache/depfile.cpp +++ b/src/ccache/depfile.cpp @@ -150,7 +150,7 @@ tokenize(std::string_view text) while (true) { // Find start of next token. - while (i < length && text[i] != '\n' && isspace(text[i])) { + while (i < length && text[i] != '\n' && util::is_space(text[i])) { ++i; } @@ -182,7 +182,7 @@ tokenize(std::string_view text) // Parse token. std::string token; while (i < length) { - if (text[i] == ':' && token.length() == 1 && !isspace(token[0]) + if (text[i] == ':' && token.length() == 1 && !util::is_space(token[0]) && i + 1 < length && (text[i + 1] == '/' || text[i + 1] == '\\')) { // It's a Windows path, so the colon is not a separator and instead // added to the token. @@ -191,7 +191,7 @@ tokenize(std::string_view text) continue; } - if (text[i] == ':' || isspace(text[i]) + if (text[i] == ':' || util::is_space(text[i]) || (text[i] == '\\' && i + 1 < length && text[i + 1] == '\n')) { // End of token. break; diff --git a/src/ccache/hashutil.cpp b/src/ccache/hashutil.cpp index 38f7893d..bb76ed9f 100644 --- a/src/ccache/hashutil.cpp +++ b/src/ccache/hashutil.cpp @@ -72,9 +72,10 @@ check_for_temporal_macros_helper(std::string_view str, size_t pos) // Check char before and after macro to verify that the found macro isn't part // of another identifier. - if ((pos == 1 || (str[pos - 2] != '_' && !isalnum(str[pos - 2]))) + if ((pos == 1 || (str[pos - 2] != '_' && !util::is_alnum(str[pos - 2]))) && (pos + macro_len == str.length() - || (str[pos + macro_len] != '_' && !isalnum(str[pos + macro_len])))) { + || (str[pos + macro_len] != '_' + && !util::is_alnum(str[pos + macro_len])))) { return found; } diff --git a/src/ccache/util/environment.cpp b/src/ccache/util/environment.cpp index b795001a..10ba23dc 100644 --- a/src/ccache/util/environment.cpp +++ b/src/ccache/util/environment.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023-2024 Joel Rosdahl and other contributors +// Copyright (C) 2023-2025 Joel Rosdahl and other contributors // // See doc/authors.adoc for a complete list of contributors. // @@ -51,7 +51,7 @@ expand_environment_variables(const std::string& str) ++left; } right = left; - while (isalnum(*right) || *right == '_') { + while (util::is_alnum(*right) || *right == '_') { ++right; } if (curly && *right != '}') { diff --git a/src/ccache/util/string.cpp b/src/ccache/util/string.cpp index b82d6995..950ba92b 100644 --- a/src/ccache/util/string.cpp +++ b/src/ccache/util/string.cpp @@ -306,7 +306,7 @@ parse_size(const std::string& value) return tl::unexpected(FMT("invalid size: \"{}\"", value)); } - while (isspace(*p)) { + while (util::is_space(*p)) { ++p; } @@ -392,7 +392,7 @@ percent_decode(std::string_view string) { const auto from_hex = [](const char digit) { return static_cast( - std::isdigit(digit) ? digit - '0' : std::tolower(digit) - 'a' + 10); + util::is_digit(digit) ? digit - '0' : util::to_lower(digit) - 'a' + 10); }; std::string result; @@ -401,8 +401,8 @@ percent_decode(std::string_view string) while (i < string.size()) { if (string[i] != '%') { result += string[i]; - } else if (i + 2 >= string.size() || !std::isxdigit(string[i + 1]) - || !std::isxdigit(string[i + 2])) { + } else if (i + 2 >= string.size() || !util::is_xdigit(string[i + 1]) + || !util::is_xdigit(string[i + 2])) { return tl::unexpected( FMT("invalid percent-encoded string at position {}: {}", i, string)); } else { @@ -537,10 +537,10 @@ split_path_list(std::string_view path_list) std::string strip_whitespace(const std::string_view string) { - const auto is_space = [](const int ch) { return std::isspace(ch); }; - const auto start = std::find_if_not(string.begin(), string.end(), is_space); + const auto start = + std::find_if_not(string.begin(), string.end(), util::is_space); const auto end = - std::find_if_not(string.rbegin(), string.rend(), is_space).base(); + std::find_if_not(string.rbegin(), string.rend(), util::is_space).base(); return start < end ? std::string(start, end) : std::string(); } @@ -549,7 +549,7 @@ to_lowercase(std::string_view string) { std::string result; result.resize(string.length()); - std::transform(string.begin(), string.end(), result.begin(), tolower); + std::transform(string.begin(), string.end(), result.begin(), util::to_lower); return result; } diff --git a/src/ccache/util/string.hpp b/src/ccache/util/string.hpp index 13d7fd40..8b17efc0 100644 --- a/src/ccache/util/string.hpp +++ b/src/ccache/util/string.hpp @@ -88,6 +88,21 @@ std::string format_human_readable_size(uint64_t size, std::string format_iso8601_timestamp(const TimePoint& time, TimeZone time_zone = TimeZone::local); +// Check if `ch` is alphanumeric. +bool is_alnum(char ch); + +// Check if `ch` is a digit. +bool is_digit(char ch); + +// Check if `ch` is a lowercase. +bool is_lower(char ch); + +// Check if `ch` is a whitespace character. +bool is_space(char ch); + +// Check if `ch` is a hexadecimal digit. +bool is_xdigit(char ch); + // Join stringified elements of `container` delimited by `delimiter` into a // string. There must exist an `std::string to_string(T::value_type)` function. template @@ -208,6 +223,9 @@ bool starts_with(std::string_view string, std::string_view prefix); // Strip whitespace from left and right side of a string. [[nodiscard]] std::string strip_whitespace(std::string_view string); +// Return lowercase `ch`. +char to_lower(char ch); + // Convert a string to lowercase. [[nodiscard]] std::string to_lowercase(std::string_view string); @@ -220,6 +238,36 @@ ends_with(const std::string_view string, const std::string_view suffix) && string.substr(string.length() - suffix.length()) == suffix; } +inline bool +is_alnum(char ch) +{ + return std::isalnum(static_cast(ch)); +} + +inline bool +is_digit(char ch) +{ + return std::isdigit(static_cast(ch)); +} + +inline bool +is_lower(char ch) +{ + return std::islower(static_cast(ch)); +} + +inline bool +is_space(char ch) +{ + return std::isspace(static_cast(ch)); +} + +inline bool +is_xdigit(char ch) +{ + return std::isxdigit(static_cast(ch)); +} + template inline std::string join(const T& container, const std::string_view delimiter) @@ -255,4 +303,10 @@ starts_with(const std::string_view string, const std::string_view prefix) return string.substr(0, prefix.size()) == prefix; } +inline char +to_lower(char ch) +{ + return std::tolower(static_cast(ch)); +} + } // namespace util