apphandler: Use std::from_chars to convert string to char instead

Using std::from_chars in C++ instead of stoi. This allow us to have more
control of how to handle the errors with more detailed message.

https://en.cppreference.com/w/cpp/utility/from_chars

Change-Id: Iaa0ecb1598428908a588b9357f277a38688e2a27
Signed-off-by: Willy Tu <wltu@google.com>
diff --git a/apphandler.cpp b/apphandler.cpp
index 254697b..aba4abe 100644
--- a/apphandler.cpp
+++ b/apphandler.cpp
@@ -15,6 +15,7 @@
 #include <app/watchdog.hpp>
 #include <apphandler.hpp>
 #include <array>
+#include <charconv>
 #include <cstddef>
 #include <cstdint>
 #include <filesystem>
@@ -30,6 +31,7 @@
 #include <phosphor-logging/log.hpp>
 #include <sdbusplus/message/types.hpp>
 #include <string>
+#include <string_view>
 #include <sys_info_param.hpp>
 #include <tuple>
 #include <vector>
@@ -481,9 +483,9 @@
 /* Additional details : If the option group exists it will force Auxiliary  */
 /* Firmware Revision Information 4th byte to 1 indicating the build was     */
 /* derived with additional edits                                            */
-int convertVersion(std::string s, Revision& rev)
+int convertVersion(std::string_view s, Revision& rev)
 {
-    std::string token;
+    std::string_view token;
     uint16_t commits;
 
     auto location = s.find_first_of('v');
@@ -497,8 +499,20 @@
         location = s.find_first_of(".");
         if (location != std::string::npos)
         {
-            rev.major =
-                static_cast<char>(std::stoi(s.substr(0, location), 0, 10));
+            std::string_view majorView = s.substr(0, location);
+            auto [ptr, ec]{
+                std::from_chars(majorView.begin(), majorView.end(), rev.major)};
+            if (ec != std::errc())
+            {
+                throw std::runtime_error(
+                    "failed to convert major string to uint8_t: " +
+                    std::make_error_code(ec).message());
+            }
+            if (ptr != majorView.begin() + majorView.size())
+            {
+                throw std::runtime_error(
+                    "converted invalid characters in major string");
+            }
             token = s.substr(location + 1);
         }
 
@@ -507,8 +521,20 @@
             location = token.find_first_of(".-");
             if (location != std::string::npos)
             {
-                rev.minor = static_cast<char>(
-                    std::stoi(token.substr(0, location), 0, 10));
+                std::string_view minorView = token.substr(0, location);
+                auto [ptr, ec]{std::from_chars(minorView.begin(),
+                                               minorView.end(), rev.minor)};
+                if (ec != std::errc())
+                {
+                    throw std::runtime_error(
+                        "failed to convert minor string to uint8_t: " +
+                        std::make_error_code(ec).message());
+                }
+                if (ptr != minorView.begin() + minorView.size())
+                {
+                    throw std::runtime_error(
+                        "converted invalid characters in minor string");
+                }
                 token = token.substr(location + 1);
             }
         }
@@ -518,7 +544,20 @@
         location = token.find_first_of(".-");
         if (!token.empty())
         {
-            commits = std::stoi(token.substr(0, location), 0, 16);
+            std::string_view commitView = token.substr(0, location);
+            auto [ptr, ec]{std::from_chars(commitView.begin(), commitView.end(),
+                                           commits, 16)};
+            if (ec != std::errc())
+            {
+                throw std::runtime_error(
+                    "failed to convert commit string to uint16_t: " +
+                    std::make_error_code(ec).message());
+            }
+            if (ptr != commitView.begin() + commitView.size())
+            {
+                throw std::runtime_error(
+                    "converted invalid characters in commit string");
+            }
             rev.d[0] = (commits >> 8) | (commits << 8);
 
             // commit number we skip