Browse Source

Avoid signed char-to-int conversions in tolower/toupper calls

These functions document that the arguments must be representable
by `unsigned char`.  In particular, NetBSD is strict about this.

Fixes: #27574
Brad King 1 month ago
parent
commit
05ed2af7da

+ 1 - 1
Source/CPack/cmCPackInnoSetupGenerator.cxx

@@ -116,7 +116,7 @@ int cmCPackInnoSetupGenerator::PackageFiles()
     if (cmSystemTools::LowerCase(i) == "english") {
       params["MessagesFile"] = "\"compiler:Default.isl\"";
     } else {
-      i[0] = static_cast<char>(std::toupper(i[0]));
+      i[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(i[0])));
       params["MessagesFile"] = cmStrCat("\"compiler:Languages\\", i, ".isl\"");
     }
 

+ 9 - 7
Source/CPack/cmCPackRPMGenerator.cxx

@@ -143,7 +143,7 @@ int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup)
            ++compIt) {
         std::string component(compIt->first);
         std::transform(component.begin(), component.end(), component.begin(),
-                       ::toupper);
+                       [](unsigned char c) { return std::toupper(c); });
 
         if (this->IsOn("CPACK_RPM_" + compIt->first + "_DEBUGINFO_PACKAGE") ||
             this->IsOn("CPACK_RPM_" + component + "_DEBUGINFO_PACKAGE")) {
@@ -157,7 +157,7 @@ int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup)
            compGIt != this->ComponentGroups.end(); ++compGIt) {
         std::string component(compGIt->first);
         std::transform(component.begin(), component.end(), component.begin(),
-                       ::toupper);
+                       [](unsigned char c) { return std::toupper(c); });
 
         if (this->IsOn("CPACK_RPM_" + compGIt->first + "_DEBUGINFO_PACKAGE") ||
             this->IsOn("CPACK_RPM_" + component + "_DEBUGINFO_PACKAGE")) {
@@ -174,7 +174,8 @@ int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup)
           if (!compIt->second.Group) {
             std::string component(compIt->first);
             std::transform(component.begin(), component.end(),
-                           component.begin(), ::toupper);
+                           component.begin(),
+                           [](unsigned char c) { return std::toupper(c); });
 
             if (this->IsOn("CPACK_RPM_" + compIt->first +
                            "_DEBUGINFO_PACKAGE") ||
@@ -206,7 +207,8 @@ int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup)
 
     std::string mainComponentUpper(mainComponent);
     std::transform(mainComponentUpper.begin(), mainComponentUpper.end(),
-                   mainComponentUpper.begin(), ::toupper);
+                   mainComponentUpper.begin(),
+                   [](unsigned char c) { return std::toupper(c); });
 
     // The default behavior is to have one package by component group
     // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
@@ -218,7 +220,7 @@ int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup)
            compGIt != this->ComponentGroups.end(); ++compGIt) {
         std::string component(compGIt->first);
         std::transform(component.begin(), component.end(), component.begin(),
-                       ::toupper);
+                       [](unsigned char c) { return std::toupper(c); });
 
         if (mainComponentUpper == component) {
           // main component will be handled last
@@ -240,7 +242,7 @@ int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup)
         if (!compIt->second.Group) {
           std::string component(compIt->first);
           std::transform(component.begin(), component.end(), component.begin(),
-                         ::toupper);
+                         [](unsigned char c) { return std::toupper(c); });
 
           if (mainComponentUpper == component) {
             // main component will be handled last
@@ -283,7 +285,7 @@ int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup)
            ++compIt) {
         std::string component(compIt->first);
         std::transform(component.begin(), component.end(), component.begin(),
-                       ::toupper);
+                       [](unsigned char c) { return std::toupper(c); });
 
         if (mainComponentUpper == component) {
           // main component will be handled last

+ 1 - 1
Source/CursesDialog/cmCursesColor.cxx

@@ -42,7 +42,7 @@ short cmCursesColor::GetColor(char id, short fallback)
   if (!initialized) {
     if (auto* v = getenv("CCMAKE_COLORS")) {
       while (v[0] && v[1] && v[1] == '=') {
-        auto const n = std::toupper(*v);
+        auto const n = std::toupper(static_cast<unsigned char>(*v));
 
         char buffer[12];
         memset(buffer, 0, sizeof(buffer));

+ 1 - 1
Source/CursesDialog/form/frm_req_name.c

@@ -147,7 +147,7 @@ int form_request_by_name( const char *str )
       strncpy(buf,str,sizeof(buf));
       while( (i<sizeof(buf)) && (buf[i] != '\0') )
 	{
-	  buf[i] = toupper((int)(buf[i]));
+	  buf[i] = toupper((unsigned char)(buf[i]));
 	  i++;
 	}
       

+ 4 - 2
Source/cmCTest.cxx

@@ -3702,7 +3702,8 @@ bool cmCTest::ConvertInstrumentationJSONFileToXML(std::string const& fpath,
   bool generating_test_xml = root["role"] == "test";
   if (!generating_test_xml) {
     std::string element_name = root["role"].asString();
-    element_name[0] = static_cast<char>(std::toupper(element_name[0]));
+    element_name[0] = static_cast<char>(
+      std::toupper(static_cast<unsigned char>(element_name[0])));
     xml.StartElement(element_name);
     std::vector<std::string> keys = root.getMemberNames();
     for (auto const& key : keys) {
@@ -3734,7 +3735,8 @@ bool cmCTest::ConvertInstrumentationJSONFileToXML(std::string const& fpath,
   std::vector<std::string> keys = dynamic_information.getMemberNames();
   for (auto const& key : keys) {
     std::string measurement_name = key;
-    measurement_name[0] = static_cast<char>(std::toupper(measurement_name[0]));
+    measurement_name[0] = static_cast<char>(
+      std::toupper(static_cast<unsigned char>(measurement_name[0])));
 
     xml.StartElement("NamedMeasurement");
     xml.Attribute("type", "numeric/double");

+ 2 - 2
Source/cmComputeLinkInformation.cxx

@@ -1556,8 +1556,8 @@ std::string cmComputeLinkInformation::NoCaseExpression(std::string const& str)
       ret += c;
     } else {
       ret += '[';
-      ret += static_cast<char>(tolower(c));
-      ret += static_cast<char>(toupper(c));
+      ret += static_cast<char>(tolower(static_cast<unsigned char>(c)));
+      ret += static_cast<char>(toupper(static_cast<unsigned char>(c)));
       ret += ']';
     }
   }

+ 1 - 1
Source/cmFindLibraryCommand.cxx

@@ -349,7 +349,7 @@ void cmFindLibraryHelper::RegexFromLiteral(std::string& out,
       out += "\\";
     }
     if (dirCase == cmSystemTools::DirCase::Insensitive) {
-      out += static_cast<char>(tolower(ch));
+      out += static_cast<char>(tolower(static_cast<unsigned char>(ch)));
     } else {
       out += ch;
     }

+ 7 - 4
Source/cmGccDepfileLexerHelper.cxx

@@ -127,10 +127,13 @@ void cmGccDepfileLexerHelper::sanitizeContent()
         // Unescape the colon following the drive letter.
         // Some versions of GNU compilers can escape this character.
         // c\:\path must be transformed to c:\path
-        if (pit->size() >= 3 && std::toupper((*pit)[0]) >= 'A' &&
-            std::toupper((*pit)[0]) <= 'Z' && (*pit)[1] == '\\' &&
-            (*pit)[2] == ':') {
-          pit->erase(1, 1);
+        if (pit->size() >= 3) {
+          auto pit0 = static_cast<char>(
+            std::toupper(static_cast<unsigned char>((*pit)[0])));
+          if (pit0 >= 'A' && pit0 <= 'Z' && (*pit)[1] == '\\' &&
+              (*pit)[2] == ':') {
+            pit->erase(1, 1);
+          }
         }
 #endif
         ++pit;

+ 7 - 3
Source/cmPathResolver.cxx

@@ -170,7 +170,9 @@ std::string ImplBase::GetWorkingDirectoryOnDrive(char letter)
   // Use the drive's working directory, if any.
   std::string d = this->OS.GetWorkingDirectoryOnDrive(letter);
   std::replace(d.begin(), d.end(), '\\', '/');
-  if (d.size() >= 3 && std::toupper(d[0]) == std::toupper(letter) &&
+  if (d.size() >= 3 &&
+      std::toupper(static_cast<unsigned char>(d[0])) ==
+        std::toupper(static_cast<unsigned char>(letter)) &&
       d[1] == ':' && d[2] == '/') {
     d[0] = letter;
     d.push_back('/');
@@ -179,7 +181,9 @@ std::string ImplBase::GetWorkingDirectoryOnDrive(char letter)
 
   // Use the current working directory if the drive matches.
   d = this->OS.GetWorkingDirectory();
-  if (d.size() >= 3 && std::toupper(d[0]) == std::toupper(letter) &&
+  if (d.size() >= 3 &&
+      std::toupper(static_cast<unsigned char>(d[0])) ==
+        std::toupper(static_cast<unsigned char>(letter)) &&
       d[1] == ':' && d[2] == '/') {
     d[0] = letter;
     d.push_back('/');
@@ -280,7 +284,7 @@ Control Impl<Policy>::ResolveRoot(Root root)
 
     if (Policy::ActualCase == Options::ActualCase::Yes) {
       // Normalize the drive letter to upper-case.
-      P[0] = static_cast<char>(std::toupper(P[0]));
+      P[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(P[0])));
     }
 
     // The root is a drive letter.  The root '/' immediately follows.

+ 1 - 1
Source/cmSbomArguments.cxx

@@ -99,6 +99,6 @@ std::string cmSbomArguments::GetPackageFileName() const
   std::string const pkgNameOnDisk = this->GetPackageDirName();
   std::string format = GetSbomFileExtension(this->GetFormat());
   std::transform(format.begin(), format.end(), format.begin(),
-                 [](char const c) { return std::tolower(c); });
+                 [](unsigned char c) { return std::tolower(c); });
   return cmStrCat(pkgNameOnDisk, format);
 }

+ 3 - 2
Source/cmStringAlgorithms.cxx

@@ -14,8 +14,9 @@ bool cmStrCaseEq(cm::string_view s1, cm::string_view s2)
     return false;
   }
 
-  return std::equal(s1.begin(), s1.end(), s2.begin(),
-                    [](char a, char b) { return tolower(a) == tolower(b); });
+  return std::equal(
+    s1.begin(), s1.end(), s2.begin(),
+    [](unsigned char a, unsigned char b) { return tolower(a) == tolower(b); });
 }
 
 std::string cmTrimWhitespace(cm::string_view str)

+ 9 - 5
Source/cmSystemTools.cxx

@@ -463,7 +463,8 @@ std::string cmSystemTools::LowerCase(cm::string_view s)
   std::string n;
   n.resize(s.size());
   for (size_t i = 0; i < s.size(); i++) {
-    n[i] = static_cast<std::string::value_type>(tolower(s[i]));
+    n[i] = static_cast<std::string::value_type>(
+      tolower(static_cast<unsigned char>(s[i])));
   }
   return n;
 }
@@ -474,7 +475,8 @@ std::string cmSystemTools::UpperCase(cm::string_view s)
   std::string n;
   n.resize(s.size());
   for (size_t i = 0; i < s.size(); i++) {
-    n[i] = static_cast<std::string::value_type>(toupper(s[i]));
+    n[i] = static_cast<std::string::value_type>(
+      toupper(static_cast<unsigned char>(s[i])));
   }
   return n;
 }
@@ -1362,7 +1364,7 @@ std::string cmSystemTools::GetRealPathResolvingWindowsSubst(
   }
   // Normalize to upper-case drive letter as cm::PathResolver does.
   if (resolved_path.size() > 1 && resolved_path[1] == ':') {
-    resolved_path[0] = toupper(resolved_path[0]);
+    resolved_path[0] = toupper(static_cast<unsigned char>(resolved_path[0]));
   }
   return resolved_path;
 #else
@@ -1384,10 +1386,12 @@ std::string cmSystemTools::GetRealPath(std::string const& path,
   // limitation to otherwise preserve susbt drives.
   if (resolved_path.size() >= 2 && resolved_path[1] == ':' &&
       path.size() >= 2 && path[1] == ':' &&
-      toupper(resolved_path[0]) != toupper(path[0])) {
+      toupper(static_cast<unsigned char>(resolved_path[0])) !=
+        toupper(static_cast<unsigned char>(path[0]))) {
     // FIXME: Add thread_local or mutex if we use threads.
     static std::map<char, std::string> substMap;
-    char const drive = static_cast<char>(toupper(path[0]));
+    char const drive =
+      static_cast<char>(toupper(static_cast<unsigned char>(path[0])));
     std::string maybe_subst = cmStrCat(drive, ":/");
     auto smi = substMap.find(drive);
     if (smi == substMap.end()) {

+ 2 - 2
Source/cmWindowsRegistry.cxx

@@ -47,8 +47,8 @@ int Strucmp(cm::string_view l, cm::string_view r)
   cm::string_view::size_type ri = 0;
 
   do {
-    lc = std::tolower(l[li++]);
-    rc = std::tolower(r[ri++]);
+    lc = std::tolower(static_cast<unsigned char>(l[li++]));
+    rc = std::tolower(static_cast<unsigned char>(r[ri++]));
   } while (lc == rc && li < l.size() && ri < r.size());
 
   return lc == rc ? static_cast<int>(l.size() - r.size()) : lc - rc;

+ 2 - 2
Source/cmakemain.cxx

@@ -500,8 +500,8 @@ int do_build(int ac, char const* const* av)
   };
   auto resolvePackagesLambda = [&](std::string const& value) -> bool {
     std::string v = value;
-    std::transform(v.begin(), v.end(), v.begin(), ::tolower);
-
+    std::transform(v.begin(), v.end(), v.begin(),
+                   [](unsigned char c) { return std::tolower(c); });
     if (v == "on") {
       resolveMode = PackageResolveMode::Force;
     } else if (v == "only") {

+ 1 - 1
Templates/TestDriver.cxx.in

@@ -66,7 +66,7 @@ CM_LOCAL char* lowercase(const char* string)
   }
   strcpy(new_string, string);  /* NOLINT */
   for (p = new_string; *p != 0; ++p) {
-    *p = CM_CAST(char, tolower(*p));
+    *p = CM_CAST(char, tolower(CM_CAST(unsigned char, *p)));
   }
   return new_string;
 }

+ 2 - 1
Tests/CMakeLib/testPathResolver.cxx

@@ -92,7 +92,8 @@ public:
   std::string GetWorkingDirectoryOnDrive(char letter) override
   {
     std::string result;
-    auto i = this->WorkDirOnDrive.find(std::tolower(letter));
+    auto i = this->WorkDirOnDrive.find(
+      std::tolower(static_cast<unsigned char>(letter)));
     if (i != this->WorkDirOnDrive.end()) {
       result = i->second;
     }

+ 1 - 1
Tests/CMakeLib/testUVProcessChainHelper.cxx

@@ -41,7 +41,7 @@ int main(int argc, char** argv)
     std::this_thread::sleep_for(std::chrono::milliseconds(12000));
     std::string input = getStdin();
     for (auto& c : input) {
-      c = static_cast<char>(std::toupper(c));
+      c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
     }
     std::cout << input << std::flush;
     std::cerr << "2" << std::flush;

+ 1 - 1
Tests/CompileDefinitions/runtest.c

@@ -11,7 +11,7 @@ int main(void)
   char build_config_name[] = BUILD_CONFIG_NAME;
   char* c;
   for (c = build_config_name; *c; ++c) {
-    *c = (char)tolower((int)*c);
+    *c = (char)tolower((unsigned char)*c);
   }
   fprintf(stderr, "build_config_name=\"%s\"\n", build_config_name);
 #ifdef TEST_CONFIG_DEBUG

+ 1 - 1
Tests/TestsWorkingDirectory/main.c

@@ -25,7 +25,7 @@ static char const* Getcwd(char* buf, unsigned int len)
   }
   // make sure the drive letter is capital
   if (strlen(buf) > 1 && buf[1] == ':') {
-    buf[0] = toupper(buf[0]);
+    buf[0] = toupper((unsigned char)buf[0]);
   }
   for (p = buf; *p; ++p) {
     if (*p == '\\') {

+ 3 - 3
Utilities/cmlibarchive/libarchive/archive_windows.c

@@ -712,9 +712,9 @@ __la_stat(const char *path, struct stat *st)
 			char exttype[4];
 
 			++ p;
-			exttype[0] = toupper(*p++);
-			exttype[1] = toupper(*p++);
-			exttype[2] = toupper(*p++);
+			exttype[0] = toupper((unsigned char)*p++);
+			exttype[1] = toupper((unsigned char)*p++);
+			exttype[2] = toupper((unsigned char)*p++);
 			exttype[3] = '\0';
 			if (!strcmp(exttype, "EXE") || !strcmp(exttype, "CMD") ||
 				!strcmp(exttype, "BAT") || !strcmp(exttype, "COM"))

+ 3 - 2
Utilities/std/cm/bits/fs_path.cxx

@@ -426,8 +426,9 @@ private:
 #  if defined(_WIN32)
   bool is_drive_name(pointer ptr)
   {
-    return std::toupper(ptr[0]) >= 'A' && std::toupper(ptr[0]) <= 'Z' &&
-      ptr[1] == ':';
+    auto ptr0 =
+      static_cast<char>(std::toupper(static_cast<unsigned char>(ptr[0])));
+    return ptr0 >= 'A' && ptr0 <= 'Z' && ptr[1] == ':';
   }
 #  endif