frudevice: increment value for duplicate dbus paths

In the event of duplicate dbus paths the code would previously count the
instances and append that value to the new dbus path:

busctl tree --no-pager xyz.openbmc_project.FruDevice
`-/xyz
  `-/xyz/openbmc_project
    `-/xyz/openbmc_project/FruDevice
      |-/xyz/openbmc_project/FruDevice/Proving
      |-/xyz/openbmc_project/FruDevice/Altruism
      |-/xyz/openbmc_project/FruDevice/SuperDuper
      |-/xyz/openbmc_project/FruDevice/SuperDuper_0
      |-/xyz/openbmc_project/FruDevice/SuperDuper_01
      |-/xyz/openbmc_project/FruDevice/Power_Now
      |-/xyz/openbmc_project/FruDevice/Granite_HalfDome
      |-/xyz/openbmc_project/FruDevice/Super_Great_V200
      |-/xyz/openbmc_project/FruDevice/Super_Great_V200_0
      |-/xyz/openbmc_project/FruDevice/Super_Great_V200_01
      |-/xyz/openbmc_project/FruDevice/Super_Great_V200_012
      |-/xyz/openbmc_project/FruDevice/Super_Great_V200_0123
      `-/xyz/openbmc_project/FruDevice/Fun_Times

Now, the code detects if there is a matching name that optionally has a
value added to the end and then will find the highest number (if any
found) and increment that.

busctl tree --no-pager xyz.openbmc_project.FruDevice
`-/xyz
  `-/xyz/openbmc_project
    `-/xyz/openbmc_project/FruDevice
      |-/xyz/openbmc_project/FruDevice/Proving
      |-/xyz/openbmc_project/FruDevice/Altruism
      |-/xyz/openbmc_project/FruDevice/SuperDuper
      |-/xyz/openbmc_project/FruDevice/SuperDuper_0
      |-/xyz/openbmc_project/FruDevice/SuperDuper_1
      |-/xyz/openbmc_project/FruDevice/Power_Now
      |-/xyz/openbmc_project/FruDevice/Granite_HalfDome
      |-/xyz/openbmc_project/FruDevice/Super_Great_V200
      |-/xyz/openbmc_project/FruDevice/Super_Great_V200_0
      |-/xyz/openbmc_project/FruDevice/Super_Great_V200_1
      |-/xyz/openbmc_project/FruDevice/Super_Great_V200_2
      |-/xyz/openbmc_project/FruDevice/Super_Great_V200_3
      `-/xyz/openbmc_project/FruDevice/Fun_Times

In the event that a product name is something like "Yes And 2" =>
"Yes_And_2" for the productName variable.  The second one you find will
be "Yes_And_2_0" because the productName is treated as the base
for the regex searches.

Tested: Ran on my platform above with sanitized values.  Also wrote a
small program to exercise edge cases.

Signed-off-by: Patrick Venture <venture@google.com>
Change-Id: I106ff2fe278939185cf4b3215784f85bb87523b0
diff --git a/src/FruDevice.cpp b/src/FruDevice.cpp
index 10750cf..b20b0d3 100644
--- a/src/FruDevice.cpp
+++ b/src/FruDevice.cpp
@@ -874,10 +874,13 @@
     // avoid duplicates by checking to see if on a mux
     if (bus > 0)
     {
-        size_t index = 0;
+        int highest = -1;
+        bool found = false;
+
         for (auto const& busIface : dbusInterfaceMap)
         {
-            if ((busIface.second->get_object_path() == productName))
+            std::string path = busIface.second->get_object_path();
+            if (std::regex_match(path, std::regex(productName + "(_\\d+|)$")))
             {
                 if (isMuxBus(bus) && address == busIface.first.second &&
                     (getFruInfo(static_cast<uint8_t>(busIface.first.first),
@@ -889,18 +892,33 @@
                     // do not replicate it.
                     return;
                 }
-                // add underscore _index for the same object path on dbus
-                std::string strIndex = std::to_string(index);
-                if (index > 0)
+
+                // Check if the match named has extra information.
+                found = true;
+                std::smatch base_match;
+
+                bool match = std::regex_match(
+                    path, base_match, std::regex(productName + "_(\\d+)$"));
+                if (match)
                 {
-                    productName.substr(0, productName.size() - strIndex.size());
+                    if (base_match.size() == 2)
+                    {
+                        std::ssub_match base_sub_match = base_match[1];
+                        std::string base = base_sub_match.str();
+
+                        int value = std::stoi(base);
+                        highest = (value > highest) ? value : highest;
+                    }
                 }
-                else
-                {
-                    productName += "_";
-                }
-                productName += std::to_string(index++);
             }
+        } // end searching objects
+
+        if (found)
+        {
+            // We found something with the same name.  If highest was still -1,
+            // it means this new entry will be _0.
+            productName += "_";
+            productName += std::to_string(++highest);
         }
     }