LCOV - code coverage report
Current view: top level - src/utils - MooseUtils.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #31706 (f8ed4a) with base bb0a08 Lines: 466 569 81.9 %
Date: 2025-11-03 17:23:24 Functions: 54 62 87.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : // MOOSE includes
      11             : #include "MooseUtils.h"
      12             : #include "MooseError.h"
      13             : #include "MaterialProperty.h"
      14             : #include "MultiMooseEnum.h"
      15             : #include "InputParameters.h"
      16             : #include "ExecFlagEnum.h"
      17             : #include "InfixIterator.h"
      18             : #include "Registry.h"
      19             : #include "MortarConstraintBase.h"
      20             : #include "MortarNodalAuxKernel.h"
      21             : #include "ExecFlagRegistry.h"
      22             : #include "RestartableDataReader.h"
      23             : 
      24             : #include "libmesh/utility.h"
      25             : #include "libmesh/elem.h"
      26             : 
      27             : // External includes
      28             : #include "pcrecpp.h"
      29             : #include "tinydir.h"
      30             : 
      31             : // C++ includes
      32             : #include <iostream>
      33             : #include <fstream>
      34             : #include <istream>
      35             : #include <iterator>
      36             : #include <filesystem>
      37             : #include <ctime>
      38             : #include <cstdlib>
      39             : #include <regex>
      40             : 
      41             : // System includes
      42             : #include <sys/stat.h>
      43             : #include <numeric>
      44             : #include <unistd.h>
      45             : 
      46             : #include "petscsys.h"
      47             : 
      48             : #ifdef __WIN32__
      49             : #include <windows.h>
      50             : #include <winbase.h>
      51             : #include <fileapi.h>
      52             : #else
      53             : #include <sys/ioctl.h>
      54             : #endif
      55             : 
      56             : namespace MooseUtils
      57             : {
      58             : std::filesystem::path
      59      145876 : pathjoin(const std::filesystem::path & p)
      60             : {
      61      145876 :   return p;
      62             : }
      63             : 
      64             : std::string
      65           7 : runTestsExecutable()
      66             : {
      67           7 :   auto build_loc = pathjoin(Moose::getExecutablePath(), "run_tests");
      68           7 :   if (pathExists(build_loc) && checkFileReadable(build_loc))
      69           7 :     return build_loc;
      70             :   // TODO: maybe no path prefix - just moose_test_runner here?
      71           0 :   return pathjoin(Moose::getExecutablePath(), "moose_test_runner");
      72           7 : }
      73             : 
      74             : std::string
      75           7 : findTestRoot()
      76             : {
      77           7 :   std::string path = ".";
      78           7 :   for (int i = 0; i < 5; i++)
      79             :   {
      80           7 :     auto testroot = pathjoin(path, "testroot");
      81           7 :     if (pathExists(testroot) && checkFileReadable(testroot))
      82           7 :       return testroot;
      83           0 :     path += "/..";
      84           7 :   }
      85           0 :   return "";
      86           7 : }
      87             : 
      88             : bool
      89        2674 : parsesToReal(const std::string & input, Real * parsed_real)
      90             : {
      91        2674 :   std::istringstream ss(input);
      92             :   Real real_value;
      93        2674 :   if (ss >> real_value && ss.eof())
      94             :   {
      95        1941 :     if (parsed_real)
      96           0 :       (*parsed_real) = real_value;
      97        1941 :     return true;
      98             :   }
      99         733 :   return false;
     100        2674 : }
     101             : 
     102             : std::string
     103          14 : installedInputsDir(const std::string & app_name,
     104             :                    const std::string & dir_name,
     105             :                    const std::string & extra_error_msg)
     106             : {
     107             :   // See moose.mk for a detailed explanation of the assumed installed application
     108             :   // layout. Installed inputs are expected to be installed in "share/<app_name>/<folder>".
     109             :   // The binary, which has a defined location will be in "bin", a peer directory to "share".
     110             :   std::string installed_path =
     111          14 :       pathjoin(Moose::getExecutablePath(), "..", "share", app_name, dir_name);
     112             : 
     113          14 :   auto test_root = pathjoin(installed_path, "testroot");
     114          14 :   if (!pathExists(installed_path))
     115           0 :     mooseError("Couldn't locate any installed inputs to copy in path: ",
     116             :                installed_path,
     117           0 :                '\n',
     118             :                extra_error_msg);
     119             : 
     120          14 :   checkFileReadable(test_root);
     121          28 :   return installed_path;
     122          14 : }
     123             : 
     124             : std::string
     125           0 : docsDir(const std::string & app_name)
     126             : {
     127             :   // See moose.mk for a detailed explanation of the assumed installed application
     128             :   // layout. Installed docs are expected to be installed in "share/<app_name>/doc".
     129             :   // The binary, which has a defined location will be in "bin", a peer directory to "share".
     130           0 :   std::string installed_path = pathjoin(Moose::getExecutablePath(), "..", "share", app_name, "doc");
     131             : 
     132           0 :   auto docfile = pathjoin(installed_path, "css", "moose.css");
     133           0 :   if (pathExists(docfile) && checkFileReadable(docfile))
     134           0 :     return installed_path;
     135           0 :   return "";
     136           0 : }
     137             : 
     138             : std::string
     139    21699532 : mooseDocsURL(const std::string & path)
     140             : {
     141    21699532 :   return "https://mooseframework.inl.gov/" + path;
     142             : }
     143             : 
     144             : std::string
     145      235292 : replaceAll(std::string str, const std::string & from, const std::string & to)
     146             : {
     147      235292 :   size_t start_pos = 0;
     148      235952 :   while ((start_pos = str.find(from, start_pos)) != std::string::npos)
     149             :   {
     150         660 :     str.replace(start_pos, from.length(), to);
     151         660 :     start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
     152             :   }
     153      235292 :   return str;
     154             : }
     155             : 
     156             : std::string
     157        3655 : convertLatestCheckpoint(std::string orig)
     158             : {
     159        3655 :   auto slash_pos = orig.find_last_of("/");
     160        3655 :   auto path = orig.substr(0, slash_pos);
     161        3655 :   auto file = orig.substr(slash_pos + 1);
     162        3655 :   if (file != "LATEST")
     163        3281 :     return orig;
     164             : 
     165         374 :   auto converted = MooseUtils::getLatestCheckpointFilePrefix(MooseUtils::listDir(path));
     166             : 
     167         374 :   if (converted.empty())
     168           0 :     mooseError("Unable to find suitable recovery file!");
     169             : 
     170         374 :   return converted;
     171        3655 : }
     172             : 
     173             : // this implementation is copied from
     174             : // https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C.2B.2B
     175             : int
     176        4158 : levenshteinDist(const std::string & s1, const std::string & s2)
     177             : {
     178             :   // To change the type this function manipulates and returns, change
     179             :   // the return type and the types of the two variables below.
     180        4158 :   auto s1len = s1.size();
     181        4158 :   auto s2len = s2.size();
     182             : 
     183        4158 :   auto column_start = (decltype(s1len))1;
     184             : 
     185        4158 :   auto column = new decltype(s1len)[s1len + 1];
     186        4158 :   std::iota(column + column_start, column + s1len + 1, column_start);
     187             : 
     188       40930 :   for (auto x = column_start; x <= s2len; x++)
     189             :   {
     190       36772 :     column[0] = x;
     191       36772 :     auto last_diagonal = x - column_start;
     192      469920 :     for (auto y = column_start; y <= s1len; y++)
     193             :     {
     194      433148 :       auto old_diagonal = column[y];
     195      433148 :       auto possibilities = {
     196      433148 :           column[y] + 1, column[y - 1] + 1, last_diagonal + (s1[y - 1] == s2[x - 1] ? 0 : 1)};
     197      433148 :       column[y] = std::min(possibilities);
     198      433148 :       last_diagonal = old_diagonal;
     199             :     }
     200             :   }
     201        4158 :   auto result = column[s1len];
     202        4158 :   delete[] column;
     203        4158 :   return result;
     204             : }
     205             : 
     206             : void
     207     8849866 : escape(std::string & str)
     208             : {
     209     8849866 :   std::map<char, std::string> escapes;
     210     8849866 :   escapes['\a'] = "\\a";
     211     8849866 :   escapes['\b'] = "\\b";
     212     8849866 :   escapes['\f'] = "\\f";
     213     8849866 :   escapes['\n'] = "\\n";
     214     8849866 :   escapes['\t'] = "\\t";
     215     8849866 :   escapes['\v'] = "\\v";
     216     8849866 :   escapes['\r'] = "\\r";
     217             : 
     218    70798928 :   for (const auto & it : escapes)
     219    61949394 :     for (size_t pos = 0; (pos = str.find(it.first, pos)) != std::string::npos;
     220         332 :          pos += it.second.size())
     221         332 :       str.replace(pos, 1, it.second);
     222     8849866 : }
     223             : 
     224             : std::string
     225      509492 : removeExtraWhitespace(const std::string & input)
     226             : {
     227      509492 :   return std::regex_replace(input, std::regex("^\\s+|\\s+$|\\s+(?=\\s)"), "");
     228             : }
     229             : 
     230             : bool
     231           0 : pathContains(const std::string & expression,
     232             :              const std::string & string_to_find,
     233             :              const std::string & delims)
     234             : {
     235           0 :   std::vector<std::string> elements;
     236           0 :   tokenize(expression, elements, 0, delims);
     237             : 
     238             :   std::vector<std::string>::iterator found_it =
     239           0 :       std::find(elements.begin(), elements.end(), string_to_find);
     240           0 :   if (found_it != elements.end())
     241           0 :     return true;
     242             :   else
     243           0 :     return false;
     244           0 : }
     245             : 
     246             : bool
     247       25298 : pathExists(const std::string & path)
     248             : {
     249             :   struct stat buffer;
     250       25298 :   return (stat(path.c_str(), &buffer) == 0);
     251             : }
     252             : 
     253             : bool
     254      371307 : checkFileReadable(const std::string & filename,
     255             :                   bool check_line_endings,
     256             :                   bool throw_on_unreadable,
     257             :                   bool check_for_git_lfs_pointer)
     258             : {
     259      371307 :   std::ifstream in(filename.c_str(), std::ifstream::in);
     260      371307 :   if (in.fail())
     261             :   {
     262       75562 :     if (throw_on_unreadable)
     263          18 :       mooseError(
     264          28 :           (std::string("Unable to open file \"") + filename +
     265          22 :            std::string("\". Check to make sure that it exists and that you have read permission."))
     266          20 :               .c_str());
     267             :     else
     268       75544 :       return false;
     269             :   }
     270             : 
     271      295745 :   if (check_line_endings)
     272             :   {
     273       69569 :     std::istream_iterator<char> iter(in);
     274       69569 :     std::istream_iterator<char> eos;
     275       69569 :     in >> std::noskipws;
     276    85467949 :     while (iter != eos)
     277    85398380 :       if (*iter++ == '\r')
     278           0 :         mooseError(filename + " contains Windows(DOS) line endings which are not supported.");
     279             :   }
     280             : 
     281      295745 :   if (check_for_git_lfs_pointer && checkForGitLFSPointer(in))
     282           8 :     mooseError(filename + " appears to be a Git-LFS pointer. Make sure you have \"git-lfs\" "
     283             :                           "installed so that you may pull this file.");
     284      295737 :   in.close();
     285             : 
     286      295737 :   return true;
     287      371283 : }
     288             : 
     289             : bool
     290      225359 : checkForGitLFSPointer(std::ifstream & file)
     291             : {
     292             :   mooseAssert(file.is_open(), "Passed in file handle is not open");
     293             : 
     294      225359 :   std::string line;
     295             : 
     296             :   // git-lfs pointer files contain several name value pairs. The specification states that the
     297             :   // first name/value pair must be "version {url}". We'll do a simplified check for that.
     298      225359 :   file.seekg(0);
     299      225359 :   std::getline(file, line);
     300      225359 :   if (line.find("version https://") != std::string::npos)
     301           8 :     return true;
     302             :   else
     303      225351 :     return false;
     304      225359 : }
     305             : 
     306             : bool
     307         270 : checkFileWriteable(const std::string & filename, bool throw_on_unwritable)
     308             : {
     309         270 :   std::ofstream out(filename.c_str(), std::ios_base::app);
     310         270 :   if (out.fail())
     311             :   {
     312           0 :     if (throw_on_unwritable)
     313           0 :       mooseError(
     314           0 :           (std::string("Unable to open file \"") + filename +
     315           0 :            std::string("\". Check to make sure that it exists and that you have write permission."))
     316           0 :               .c_str());
     317             :     else
     318           0 :       return false;
     319             :   }
     320             : 
     321         270 :   out.close();
     322             : 
     323         270 :   return true;
     324         270 : }
     325             : 
     326             : void
     327      257261 : parallelBarrierNotify(const Parallel::Communicator & comm, bool messaging)
     328             : {
     329             :   processor_id_type secondary_processor_id;
     330             : 
     331      257261 :   if (messaging)
     332           0 :     Moose::out << "Waiting For Other Processors To Finish" << std::endl;
     333      257261 :   if (comm.rank() == 0)
     334             :   {
     335             :     // The primary process is already through, so report it
     336      192200 :     if (messaging)
     337           0 :       Moose::out << "Jobs complete: 1/" << comm.size() << (1 == comm.size() ? "\n" : "\r")
     338           0 :                  << std::flush;
     339      257261 :     for (unsigned int i = 2; i <= comm.size(); ++i)
     340             :     {
     341       65061 :       comm.receive(MPI_ANY_SOURCE, secondary_processor_id);
     342       65061 :       if (messaging)
     343           0 :         Moose::out << "Jobs complete: " << i << "/" << comm.size()
     344           0 :                    << (i == comm.size() ? "\n" : "\r") << std::flush;
     345             :     }
     346             :   }
     347             :   else
     348             :   {
     349       65061 :     secondary_processor_id = comm.rank();
     350       65061 :     comm.send(0, secondary_processor_id);
     351             :   }
     352             : 
     353      257261 :   comm.barrier();
     354      257261 : }
     355             : 
     356             : void
     357          10 : serialBegin(const libMesh::Parallel::Communicator & comm, bool warn)
     358             : {
     359             :   // unless we are the first processor...
     360          10 :   if (comm.rank() > 0)
     361             :   {
     362             :     // ...wait for the previous processor to finish
     363           3 :     int dummy = 0;
     364           3 :     comm.receive(comm.rank() - 1, dummy);
     365             :   }
     366           7 :   else if (warn)
     367           0 :     mooseWarning("Entering serial execution block (use only for debugging)");
     368          10 : }
     369             : 
     370             : void
     371          10 : serialEnd(const libMesh::Parallel::Communicator & comm, bool warn)
     372             : {
     373             :   // unless we are the last processor...
     374          10 :   if (comm.rank() + 1 < comm.size())
     375             :   {
     376             :     // ...notify the next processor of its turn
     377           3 :     int dummy = 0;
     378           3 :     comm.send(comm.rank() + 1, dummy);
     379             :   }
     380             : 
     381          10 :   comm.barrier();
     382          10 :   if (comm.rank() == 0 && warn)
     383           0 :     mooseWarning("Leaving serial execution block (use only for debugging)");
     384          10 : }
     385             : 
     386             : bool
     387       25915 : hasExtension(const std::string & filename, std::string ext, bool strip_exodus_ext)
     388             : {
     389             :   // Extract the extension, w/o the '.'
     390       25915 :   std::string file_ext;
     391       25915 :   if (strip_exodus_ext)
     392             :   {
     393             :     pcrecpp::RE re(
     394         629 :         ".*\\.([^\\.]*?)(?:-s\\d+)?\\s*$"); // capture the complete extension, ignoring -s*
     395         629 :     re.FullMatch(filename, &file_ext);
     396         629 :   }
     397             :   else
     398             :   {
     399       25286 :     pcrecpp::RE re(".*\\.([^\\.]*?)\\s*$"); // capture the complete extension
     400       25286 :     re.FullMatch(filename, &file_ext);
     401       25286 :   }
     402             : 
     403             :   // Perform the comparision
     404       25915 :   if (file_ext == ext)
     405       13827 :     return true;
     406             :   else
     407       12088 :     return false;
     408       25915 : }
     409             : 
     410             : std::string
     411         688 : getExtension(const std::string & filename, const bool rfind)
     412             : {
     413         688 :   std::string file_ext = "";
     414         688 :   if (filename != "")
     415             :   {
     416             :     // The next line splits filename at the last "/" and gives the file name after "/"
     417         674 :     const std::string stripped_filename = splitFileName<std::string>(filename).second;
     418         674 :     auto pos = rfind ? stripped_filename.rfind(".") : stripped_filename.find(".");
     419         674 :     if (pos != std::string::npos)
     420         208 :       file_ext += stripped_filename.substr(pos + 1, std::string::npos);
     421         674 :   }
     422             : 
     423         688 :   return file_ext;
     424           0 : }
     425             : 
     426             : std::string
     427         276 : stripExtension(const std::string & s, const bool rfind)
     428             : {
     429         276 :   const std::string ext = getExtension(s, rfind);
     430         276 :   const bool offset = (ext.size() != 0);
     431             :   // -1 offset accounts for the extension's leading dot ("."), if there is an extension
     432         552 :   return s.substr(0, s.size() - ext.size() - offset);
     433         276 : }
     434             : 
     435             : std::string
     436           7 : getCurrentWorkingDir()
     437             : {
     438             :   // Note: At the time of creating this method, our minimum compiler still
     439             :   // does not support <filesystem>. Additionally, the inclusion of that header
     440             :   // requires an additional library to be linked so for now, we'll just
     441             :   // use the Unix standard library to get us the cwd().
     442           7 :   constexpr unsigned int BUF_SIZE = 1024;
     443             :   char buffer[BUF_SIZE];
     444             : 
     445          14 :   return getcwd(buffer, BUF_SIZE) != nullptr ? buffer : "";
     446             : }
     447             : 
     448             : void
     449          16 : makedirs(const std::string & dir_name, bool throw_on_failure)
     450             : {
     451             :   // split path into directories with delimiter '/'
     452          16 :   std::vector<std::string> split_dir_names;
     453          16 :   MooseUtils::tokenize(dir_name, split_dir_names);
     454             : 
     455          16 :   auto n = split_dir_names.size();
     456             : 
     457             :   // remove '.' and '..' when possible
     458          16 :   auto i = n;
     459          16 :   i = 0;
     460          72 :   while (i != n)
     461             :   {
     462          56 :     if (split_dir_names[i] == ".")
     463             :     {
     464           6 :       for (auto j = i + 1; j < n; ++j)
     465           4 :         split_dir_names[j - 1] = split_dir_names[j];
     466           2 :       --n;
     467             :     }
     468          54 :     else if (i > 0 && split_dir_names[i] == ".." && split_dir_names[i - 1] != "..")
     469             :     {
     470           8 :       for (auto j = i + 1; j < n; ++j)
     471           6 :         split_dir_names[j - 2] = split_dir_names[j];
     472           2 :       n -= 2;
     473           2 :       --i;
     474             :     }
     475             :     else
     476          52 :       ++i;
     477             :   }
     478          16 :   if (n == 0)
     479           0 :     return;
     480             : 
     481          16 :   split_dir_names.resize(n);
     482             : 
     483             :   // start creating directories recursively
     484          16 :   std::string cur_dir = dir_name[0] == '/' ? "" : ".";
     485          64 :   for (auto & dir : split_dir_names)
     486             :   {
     487          50 :     cur_dir += "/" + dir;
     488             : 
     489          50 :     if (!pathExists(cur_dir))
     490             :     {
     491          34 :       auto code = Utility::mkdir(cur_dir.c_str());
     492          34 :       if (code != 0)
     493             :       {
     494           2 :         std::string msg = "Failed creating directory " + dir_name;
     495           2 :         if (throw_on_failure)
     496           2 :           throw std::invalid_argument(msg);
     497             :         else
     498           0 :           mooseError(msg);
     499           2 :       }
     500             :     }
     501             :   }
     502          18 : }
     503             : 
     504             : void
     505          14 : removedirs(const std::string & dir_name, bool throw_on_failure)
     506             : {
     507             :   // split path into directories with delimiter '/'
     508          14 :   std::vector<std::string> split_dir_names;
     509          14 :   MooseUtils::tokenize(dir_name, split_dir_names);
     510             : 
     511          14 :   auto n = split_dir_names.size();
     512             : 
     513             :   // remove '.' and '..' when possible
     514          14 :   auto i = n;
     515          14 :   i = 0;
     516          68 :   while (i != n)
     517             :   {
     518          54 :     if (split_dir_names[i] == ".")
     519             :     {
     520           6 :       for (auto j = i + 1; j < n; ++j)
     521           4 :         split_dir_names[j - 1] = split_dir_names[j];
     522           2 :       --n;
     523             :     }
     524          52 :     else if (i > 0 && split_dir_names[i] == ".." && split_dir_names[i - 1] != "..")
     525             :     {
     526           8 :       for (auto j = i + 1; j < n; ++j)
     527           6 :         split_dir_names[j - 2] = split_dir_names[j];
     528           2 :       n -= 2;
     529           2 :       --i;
     530             :     }
     531             :     else
     532          50 :       ++i;
     533             :   }
     534          14 :   if (n == 0)
     535           0 :     return;
     536             : 
     537          14 :   split_dir_names.resize(n);
     538             : 
     539             :   // start removing directories recursively
     540          14 :   std::string base_dir = dir_name[0] == '/' ? "" : ".";
     541          46 :   for (i = n; i > 0; --i)
     542             :   {
     543          38 :     std::string cur_dir = base_dir;
     544          38 :     auto j = i;
     545         142 :     for (j = 0; j < i; ++j)
     546         104 :       cur_dir += "/" + split_dir_names[j];
     547             : 
     548             :     // listDir should return at least '.' and '..'
     549          38 :     if (pathExists(cur_dir) && listDir(cur_dir).size() == 2)
     550             :     {
     551          32 :       auto code = rmdir(cur_dir.c_str());
     552          32 :       if (code != 0)
     553             :       {
     554           0 :         std::string msg = "Failed removing directory " + dir_name;
     555           0 :         if (throw_on_failure)
     556           0 :           throw std::invalid_argument(msg);
     557             :         else
     558           0 :           mooseError(msg);
     559           0 :       }
     560             :     }
     561             :     else
     562             :       // stop removing
     563           6 :       break;
     564          38 :   }
     565          14 : }
     566             : 
     567             : std::string
     568          28 : camelCaseToUnderscore(const std::string & camel_case_name)
     569             : {
     570          28 :   std::string replaced = camel_case_name;
     571             :   // Put underscores in front of each contiguous set of capital letters
     572          28 :   pcrecpp::RE("(?!^)(?<![A-Z_])([A-Z]+)").GlobalReplace("_\\1", &replaced);
     573             : 
     574             :   // Convert all capital letters to lower case
     575          28 :   std::transform(replaced.begin(), replaced.end(), replaced.begin(), ::tolower);
     576          28 :   return replaced;
     577           0 : }
     578             : 
     579             : std::string
     580         550 : underscoreToCamelCase(const std::string & underscore_name, bool leading_upper_case)
     581             : {
     582         550 :   pcrecpp::StringPiece input(underscore_name);
     583         550 :   pcrecpp::RE re("([^_]*)(_|$)");
     584             : 
     585         550 :   std::string result;
     586         550 :   std::string us, not_us;
     587         550 :   bool make_upper = leading_upper_case;
     588         574 :   while (re.Consume(&input, &not_us, &us))
     589             :   {
     590         574 :     if (not_us.length() > 0)
     591             :     {
     592         562 :       if (make_upper)
     593             :       {
     594         558 :         result += std::toupper(not_us[0]);
     595         558 :         if (not_us.length() > 1)
     596         558 :           result += not_us.substr(1);
     597             :       }
     598             :       else
     599           4 :         result += not_us;
     600             :     }
     601         574 :     if (us == "")
     602         550 :       break;
     603             : 
     604             :     // Toggle flag so next match is upper cased
     605          24 :     make_upper = true;
     606             :   }
     607             : 
     608        1100 :   return result;
     609         550 : }
     610             : 
     611             : std::string
     612     5070131 : shortName(const std::string & name)
     613             : {
     614     5070131 :   return name.substr(name.find_last_of('/') != std::string::npos ? name.find_last_of('/') + 1 : 0);
     615             : }
     616             : 
     617             : std::string
     618     1551102 : baseName(const std::string & name)
     619             : {
     620     1551102 :   return name.substr(0, name.find_last_of('/') != std::string::npos ? name.find_last_of('/') : 0);
     621             : }
     622             : 
     623             : std::string
     624          12 : hostname()
     625             : {
     626             :   char hostname[1024];
     627          12 :   hostname[1023] = '\0';
     628             : #ifndef __WIN32__
     629          12 :   if (gethostname(hostname, 1023))
     630           0 :     mooseError("Failed to retrieve hostname!");
     631             : #else
     632             :   DWORD dwSize = sizeof(hostname);
     633             :   if (!GetComputerNameEx(ComputerNamePhysicalDnsHostname, hostname, &dwSize))
     634             :     mooseError("Failed to retrieve hostname!");
     635             : #endif
     636          24 :   return hostname;
     637             : }
     638             : 
     639             : unsigned short
     640      137762 : getTermWidth(bool use_environment)
     641             : {
     642             : #ifndef __WIN32__
     643             :   struct winsize w;
     644             : #else
     645             :   struct
     646             :   {
     647             :     unsigned short ws_col;
     648             :   } w;
     649             : #endif
     650             :   /**
     651             :    * Initialize the value we intend to populate just in case
     652             :    * the system call fails
     653             :    */
     654      137762 :   w.ws_col = std::numeric_limits<unsigned short>::max();
     655             : 
     656      137762 :   if (use_environment)
     657             :   {
     658      137762 :     char * pps_width = std::getenv("MOOSE_PPS_WIDTH");
     659      137762 :     if (pps_width != NULL)
     660             :     {
     661           0 :       std::stringstream ss(pps_width);
     662           0 :       ss >> w.ws_col;
     663           0 :     }
     664             :   }
     665             :   // Default to AUTO if no environment variable was set
     666      137762 :   if (w.ws_col == std::numeric_limits<unsigned short>::max())
     667             :   {
     668             : #ifndef __WIN32__
     669             :     try
     670             :     {
     671      137762 :       ioctl(0, TIOCGWINSZ, &w);
     672             :     }
     673             :     catch (...)
     674             : #endif
     675             :     {
     676             :     }
     677             :   }
     678             : 
     679             :   // Something bad happened, make sure we have a sane value
     680             :   // 132 seems good for medium sized screens, and is available as a GNOME preset
     681      137762 :   if (w.ws_col == std::numeric_limits<unsigned short>::max())
     682      137762 :     w.ws_col = 132;
     683             : 
     684      137762 :   return w.ws_col;
     685             : }
     686             : 
     687             : void
     688           0 : MaterialPropertyStorageDump(
     689             :     const HashMap<const libMesh::Elem *, HashMap<unsigned int, MaterialProperties>> & props)
     690             : {
     691             :   // Loop through the elements
     692           0 :   for (const auto & elem_it : props)
     693             :   {
     694           0 :     Moose::out << "Element " << elem_it.first->id() << '\n';
     695             : 
     696             :     // Loop through the sides
     697           0 :     for (const auto & side_it : elem_it.second)
     698             :     {
     699           0 :       Moose::out << "  Side " << side_it.first << '\n';
     700             : 
     701             :       // Loop over properties
     702           0 :       unsigned int cnt = 0;
     703           0 :       for (const auto & mat_prop : side_it.second)
     704             :       {
     705           0 :         if (auto mp = dynamic_cast<const MaterialProperty<Real> *>(&mat_prop))
     706             :         {
     707           0 :           Moose::out << "    Property " << cnt << '\n';
     708           0 :           cnt++;
     709             : 
     710             :           // Loop over quadrature points
     711           0 :           for (unsigned int qp = 0; qp < mp->size(); ++qp)
     712           0 :             Moose::out << "      prop[" << qp << "] = " << (*mp)[qp] << '\n';
     713             :         }
     714             :       }
     715             :     }
     716             :   }
     717             : 
     718           0 :   Moose::out << std::flush;
     719           0 : }
     720             : 
     721             : std::string &
     722       22537 : removeColor(std::string & msg)
     723             : {
     724       22537 :   pcrecpp::RE re("(\\33\\[3[0-7]m))", pcrecpp::DOTALL());
     725       22537 :   re.GlobalReplace(std::string(""), &msg);
     726       22537 :   return msg;
     727       22537 : }
     728             : 
     729             : void
     730           0 : addLineBreaks(std::string & message,
     731             :               unsigned int line_width /*= ConsoleUtils::console_line_length*/)
     732             : {
     733           0 :   for (auto i : make_range(int(message.length() / line_width)))
     734           0 :     message.insert((i + 1) * (line_width + 2) - 2, "\n");
     735           0 : }
     736             : 
     737             : void
     738     1368270 : indentMessage(const std::string & prefix,
     739             :               std::string & message,
     740             :               const char * color /*= COLOR_CYAN*/,
     741             :               bool indent_first_line,
     742             :               const std::string & post_prefix)
     743             : {
     744             :   // First we need to see if the message we need to indent (with color) also contains color codes
     745             :   // that span lines.
     746             :   // The code matches all of the XTERM constants (see XTermConstants.h). If it does, then we'll work
     747             :   // on formatting
     748             :   // each colored multiline chunk one at a time with the right codes.
     749     1368270 :   std::string colored_message;
     750     1368270 :   std::string curr_color = COLOR_DEFAULT; // tracks last color code before newline
     751     1368270 :   std::string line, color_code;
     752             : 
     753     1368270 :   bool ends_in_newline = message.empty() ? true : message.back() == '\n';
     754             : 
     755     1368270 :   bool first = true;
     756             : 
     757     1368270 :   std::istringstream iss(message);
     758     3557868 :   for (std::string line; std::getline(iss, line);) // loop over each line
     759             :   {
     760     2189598 :     const static pcrecpp::RE match_color(".*(\\33\\[3\\dm)((?!\\33\\[3\\d)[^\n])*");
     761     2189598 :     pcrecpp::StringPiece line_piece(line);
     762     2189598 :     match_color.FindAndConsume(&line_piece, &color_code);
     763             : 
     764     2189598 :     if (!first || indent_first_line)
     765     2189598 :       colored_message += color + prefix + post_prefix + curr_color;
     766             : 
     767     2189598 :     colored_message += line;
     768             : 
     769             :     // Only add a newline to the last line if it had one to begin with!
     770     2189598 :     if (!iss.eof() || ends_in_newline)
     771     2126188 :       colored_message += "\n";
     772             : 
     773     2189598 :     if (!color_code.empty())
     774      891294 :       curr_color = color_code; // remember last color of this line
     775             : 
     776     2189598 :     first = false;
     777     1368270 :   }
     778     1368270 :   message = colored_message;
     779     1368270 : }
     780             : 
     781             : std::list<std::string>
     782        3520 : listDir(const std::string path, bool files_only)
     783             : {
     784        3520 :   std::list<std::string> files;
     785             : 
     786        3520 :   tinydir_dir dir;
     787        3520 :   dir.has_next = 0; // Avoid a garbage value in has_next (clang StaticAnalysis)
     788        3520 :   tinydir_open(&dir, path.c_str());
     789             : 
     790       22295 :   while (dir.has_next)
     791             :   {
     792             :     tinydir_file file;
     793       18775 :     file.is_dir = 0; // Avoid a garbage value in is_dir (clang StaticAnalysis)
     794       18775 :     tinydir_readfile(&dir, &file);
     795             : 
     796       18775 :     if (!files_only || !file.is_dir)
     797       18775 :       files.push_back(path + "/" + file.name);
     798             : 
     799       18775 :     tinydir_next(&dir);
     800             :   }
     801             : 
     802        3520 :   tinydir_close(&dir);
     803             : 
     804        7040 :   return files;
     805           0 : }
     806             : 
     807             : std::list<std::string>
     808        3074 : getFilesInDirs(const std::list<std::string> & directory_list, const bool files_only /* = true */)
     809             : {
     810        3074 :   std::list<std::string> files;
     811             : 
     812        6184 :   for (const auto & dir_name : directory_list)
     813        3110 :     files.splice(files.end(), listDir(dir_name, files_only));
     814             : 
     815        3074 :   return files;
     816           0 : }
     817             : 
     818             : std::string
     819        3448 : getLatestCheckpointFilePrefix(const std::list<std::string> & checkpoint_files)
     820             : {
     821             :   // Create storage for newest restart files
     822             :   // Note that these might have the same modification time if the simulation was fast.
     823             :   // In that case we're going to save all of the "newest" files and sort it out momentarily
     824        3448 :   std::time_t newest_time = 0;
     825        3448 :   std::list<std::string> newest_restart_files;
     826             : 
     827             :   // Loop through all possible files and store the newest
     828       22033 :   for (const auto & cp_file : checkpoint_files)
     829             :   {
     830       37170 :     if (MooseUtils::hasExtension(cp_file, "rd"))
     831             :     {
     832             :       struct stat stats;
     833        6859 :       stat(cp_file.c_str(), &stats);
     834             : 
     835        6859 :       std::time_t mod_time = stats.st_mtime;
     836        6859 :       if (mod_time > newest_time)
     837             :       {
     838        3683 :         newest_restart_files.clear(); // If the modification time is greater, clear the list
     839        3683 :         newest_time = mod_time;
     840             :       }
     841             : 
     842        6859 :       if (mod_time == newest_time)
     843        6372 :         newest_restart_files.push_back(cp_file);
     844             :     }
     845             :   }
     846             : 
     847             :   // Loop through all of the newest files according the number in the file name
     848        3448 :   int max_file_num = -1;
     849        3448 :   std::string max_file;
     850        3448 :   std::string max_prefix;
     851             : 
     852             :   // Pull out the path including the number and the number itself
     853             :   // This takes something_blah_out_cp/0024-restart-1.rd
     854             :   // and returns "something_blah_out_cp/0024" as the "prefix"
     855             :   // and then "24" as the number itself
     856        3448 :   pcrecpp::RE re_file_num("(.*?(\\d+))-restart-\\d+.rd$");
     857             : 
     858             :   // Now, out of the newest files find the one with the largest number in it
     859        9508 :   for (const auto & res_file : newest_restart_files)
     860             :   {
     861        6060 :     int file_num = 0;
     862             : 
     863             :     // All of the file up to and including the digits
     864        6060 :     std::string file_prefix;
     865             : 
     866        6060 :     re_file_num.FullMatch(res_file, &file_prefix, &file_num);
     867             : 
     868        6060 :     if (file_num > max_file_num)
     869             :     {
     870             :       // Need both the header and the data
     871        3808 :       if (!RestartableDataReader::isAvailable(res_file))
     872           0 :         continue;
     873             : 
     874        3808 :       max_file_num = file_num;
     875        3808 :       max_file = res_file;
     876        3808 :       max_prefix = file_prefix;
     877             :     }
     878        6060 :   }
     879             : 
     880             :   // Error if nothing was located
     881        3448 :   if (max_file_num == -1)
     882           8 :     mooseError("No checkpoint file found!");
     883             : 
     884        6880 :   return max_prefix;
     885        3440 : }
     886             : 
     887             : bool
     888    24205070 : wildCardMatch(std::string name, std::string search_string)
     889             : {
     890             :   // Assume that an empty string matches anything
     891    24205070 :   if (search_string == "")
     892    22324625 :     return true;
     893             : 
     894             :   // transform to lower for case insenstive matching
     895     1880445 :   std::transform(name.begin(), name.end(), name.begin(), (int (*)(int))std::toupper);
     896     1880445 :   std::transform(search_string.begin(),
     897             :                  search_string.end(),
     898             :                  search_string.begin(),
     899             :                  (int (*)(int))std::toupper);
     900             : 
     901             :   // exact match!
     902     1880445 :   if (search_string.find("*") == std::string::npos)
     903     1880445 :     return search_string == name;
     904             : 
     905             :   // wildcard
     906           0 :   std::vector<std::string> tokens;
     907           0 :   MooseUtils::tokenize(search_string, tokens, 1, "*");
     908             : 
     909           0 :   size_t pos = 0;
     910           0 :   for (unsigned int i = 0; i < tokens.size() && pos != std::string::npos; ++i)
     911             :   {
     912           0 :     pos = name.find(tokens[i], pos);
     913             :     // See if we have a leading wildcard
     914           0 :     if (search_string[0] != '*' && i == 0 && pos != 0)
     915           0 :       return false;
     916             :   }
     917             : 
     918           0 :   if (pos != std::string::npos && tokens.size() > 0)
     919             :   {
     920             :     // Now see if we have a trailing wildcard
     921           0 :     size_t last_token_length = tokens.back().length();
     922           0 :     if (*search_string.rbegin() == '*' || pos == name.size() - last_token_length)
     923           0 :       return true;
     924             :     else
     925           0 :       return false;
     926             :   }
     927             :   else
     928           0 :     return false;
     929           0 : }
     930             : 
     931             : bool
     932       61948 : globCompare(const std::string & candidate,
     933             :             const std::string & pattern,
     934             :             std::size_t c,
     935             :             std::size_t p)
     936             : {
     937       61948 :   if (p == pattern.size())
     938        2146 :     return c == candidate.size();
     939             : 
     940       59802 :   if (pattern[p] == '*')
     941             :   {
     942       30722 :     for (; c < candidate.size(); ++c)
     943       29540 :       if (globCompare(candidate, pattern, c, p + 1))
     944         200 :         return true;
     945        1182 :     return globCompare(candidate, pattern, c, p + 1);
     946             :   }
     947             : 
     948       58420 :   if (pattern[p] != '?' && pattern[p] != candidate[c])
     949       38904 :     return false;
     950             : 
     951       19516 :   return globCompare(candidate, pattern, c + 1, p + 1);
     952             : }
     953             : 
     954             : std::string
     955     5462496 : stringJoin(const std::vector<std::string> & values, const std::string & separator)
     956             : {
     957     5462496 :   std::string combined;
     958    13438776 :   for (const auto & value : values)
     959     7976280 :     combined += value + separator;
     960     5462496 :   if (values.size())
     961     5452930 :     combined = combined.substr(0, combined.size() - separator.size());
     962     5462496 :   return combined;
     963           0 : }
     964             : 
     965             : bool
     966     1293770 : beginsWith(const std::string & value, const std::string & begin_value)
     967             : {
     968     1293770 :   return value.rfind(begin_value, 0) == 0;
     969             : }
     970             : 
     971             : ExecFlagEnum
     972    18301488 : getDefaultExecFlagEnum()
     973             : {
     974    18301488 :   return moose::internal::ExecFlagRegistry::getExecFlagRegistry().getDefaultFlags();
     975             : }
     976             : 
     977             : int
     978         428 : stringToInteger(const std::string & input, bool throw_on_failure)
     979             : {
     980         428 :   return convert<int>(input, throw_on_failure);
     981             : }
     982             : 
     983             : void
     984         900 : linearPartitionItems(dof_id_type num_items,
     985             :                      dof_id_type num_chunks,
     986             :                      dof_id_type chunk_id,
     987             :                      dof_id_type & num_local_items,
     988             :                      dof_id_type & local_items_begin,
     989             :                      dof_id_type & local_items_end)
     990             : {
     991         900 :   auto global_num_local_items = num_items / num_chunks;
     992             : 
     993         900 :   num_local_items = global_num_local_items;
     994             : 
     995         900 :   auto leftovers = num_items % num_chunks;
     996             : 
     997         900 :   if (chunk_id < leftovers)
     998             :   {
     999          45 :     num_local_items++;
    1000          45 :     local_items_begin = num_local_items * chunk_id;
    1001             :   }
    1002             :   else
    1003         855 :     local_items_begin =
    1004         855 :         (global_num_local_items + 1) * leftovers + global_num_local_items * (chunk_id - leftovers);
    1005             : 
    1006         900 :   local_items_end = local_items_begin + num_local_items;
    1007         900 : }
    1008             : 
    1009             : processor_id_type
    1010      396131 : linearPartitionChunk(dof_id_type num_items, dof_id_type num_chunks, dof_id_type item_id)
    1011             : {
    1012      396131 :   auto global_num_local_items = num_items / num_chunks;
    1013             : 
    1014      396131 :   auto leftovers = num_items % num_chunks;
    1015             : 
    1016      396131 :   auto first_item_past_first_part = leftovers * (global_num_local_items + 1);
    1017             : 
    1018             :   // Is it in the first section (that gets an extra item)
    1019      396131 :   if (item_id < first_item_past_first_part)
    1020         494 :     return item_id / (global_num_local_items + 1);
    1021             :   else
    1022             :   {
    1023      395637 :     auto new_item_id = item_id - first_item_past_first_part;
    1024             : 
    1025             :     // First chunk after the first section + the number of chunks after that
    1026      395637 :     return leftovers + (new_item_id / global_num_local_items);
    1027             :   }
    1028             : }
    1029             : 
    1030             : std::vector<std::string>
    1031      275335 : split(const std::string & str, const std::string & delimiter, std::size_t max_count)
    1032             : {
    1033      275335 :   std::vector<std::string> output;
    1034      275335 :   std::size_t count = 0;
    1035      275335 :   size_t prev = 0, pos = 0;
    1036             :   do
    1037             :   {
    1038      289982 :     pos = str.find(delimiter, prev);
    1039      289982 :     output.push_back(str.substr(prev, pos - prev));
    1040      289982 :     prev = pos + delimiter.length();
    1041      289982 :     count += 1;
    1042      289982 :   } while (pos != std::string::npos && count < max_count);
    1043             : 
    1044      275335 :   if (pos != std::string::npos)
    1045           2 :     output.push_back(str.substr(prev));
    1046             : 
    1047      275335 :   return output;
    1048           0 : }
    1049             : 
    1050             : std::vector<std::string>
    1051       10309 : rsplit(const std::string & str, const std::string & delimiter, std::size_t max_count)
    1052             : {
    1053       10309 :   std::vector<std::string> output;
    1054       10309 :   std::size_t count = 0;
    1055       10309 :   size_t prev = str.length(), pos = str.length();
    1056             :   do
    1057             :   {
    1058       17782 :     pos = str.rfind(delimiter, prev);
    1059       17782 :     output.insert(output.begin(), str.substr(pos + delimiter.length(), prev - pos));
    1060       17782 :     prev = pos - delimiter.length();
    1061       17782 :     count += 1;
    1062       17782 :   } while (pos != std::string::npos && pos > 0 && count < max_count);
    1063             : 
    1064       10309 :   if (pos != std::string::npos)
    1065           4 :     output.insert(output.begin(), str.substr(0, pos));
    1066             : 
    1067       10309 :   return output;
    1068           0 : }
    1069             : 
    1070             : void
    1071         164 : createSymlink(const std::string & target, const std::string & link)
    1072             : {
    1073         164 :   clearSymlink(link);
    1074             : #ifndef __WIN32__
    1075         164 :   auto err = symlink(target.c_str(), link.c_str());
    1076             : #else
    1077             :   auto err = CreateSymbolicLink(target.c_str(), link.c_str(), 0);
    1078             : #endif
    1079         164 :   if (err)
    1080           0 :     mooseError("Failed to create symbolic link (via 'symlink') from ", target, " to ", link);
    1081         164 : }
    1082             : 
    1083             : void
    1084        5336 : clearSymlink(const std::string & link)
    1085             : {
    1086             : #ifndef __WIN32__
    1087             :   struct stat sbuf;
    1088        5336 :   if (lstat(link.c_str(), &sbuf) == 0)
    1089             :   {
    1090         131 :     auto err = unlink(link.c_str());
    1091         131 :     if (err != 0)
    1092           0 :       mooseError("Failed to remove symbolic link (via 'unlink') to ", link);
    1093             :   }
    1094             : #else
    1095             :   auto attr = GetFileAttributesA(link.c_str());
    1096             :   if (attr != INVALID_FILE_ATTRIBUTES)
    1097             :   {
    1098             :     auto err = _unlink(link.c_str());
    1099             :     if (err != 0)
    1100             :       mooseError("Failed to remove link/file (via '_unlink') to ", link);
    1101             :   }
    1102             : #endif
    1103        5336 : }
    1104             : 
    1105             : std::size_t
    1106      134016 : fileSize(const std::string & filename)
    1107             : {
    1108             : #ifndef __WIN32__
    1109             :   struct stat buffer;
    1110      134016 :   if (!stat(filename.c_str(), &buffer))
    1111      134016 :     return buffer.st_size;
    1112             : #else
    1113             :   HANDLE hFile = CreateFile(filename.c_str(),
    1114             :                             GENERIC_READ,
    1115             :                             FILE_SHARE_READ | FILE_SHARE_WRITE,
    1116             :                             NULL,
    1117             :                             OPEN_EXISTING,
    1118             :                             FILE_ATTRIBUTE_NORMAL,
    1119             :                             NULL);
    1120             :   if (hFile == INVALID_HANDLE_VALUE)
    1121             :     return 0;
    1122             : 
    1123             :   LARGE_INTEGER size;
    1124             :   if (GetFileSizeEx(hFile, &size))
    1125             :   {
    1126             :     CloseHandle(hFile);
    1127             :     return size.QuadPart;
    1128             :   }
    1129             : 
    1130             :   CloseHandle(hFile);
    1131             : #endif
    1132           0 :   return 0;
    1133             : }
    1134             : 
    1135             : std::string
    1136       69621 : realpath(const std::string & path)
    1137             : {
    1138       69621 :   return std::filesystem::absolute(path);
    1139             : }
    1140             : 
    1141             : BoundingBox
    1142        8299 : buildBoundingBox(const Point & p1, const Point & p2)
    1143             : {
    1144        8299 :   BoundingBox bb;
    1145        8299 :   bb.union_with(p1);
    1146        8299 :   bb.union_with(p2);
    1147        8299 :   return bb;
    1148             : }
    1149             : 
    1150             : std::string
    1151     8437636 : prettyCppType(const std::string & cpp_type)
    1152             : {
    1153             :   // On mac many of the std:: classes are inline namespaced with __1
    1154             :   // On linux std::string can be inline namespaced with __cxx11
    1155     8437636 :   std::string s = cpp_type;
    1156             :   // Remove all spaces surrounding a >
    1157     8437636 :   pcrecpp::RE("\\s(?=>)").GlobalReplace("", &s);
    1158     8437636 :   pcrecpp::RE("std::__\\w+::").GlobalReplace("std::", &s);
    1159             :   // It would be nice if std::string actually looked normal
    1160     8437636 :   pcrecpp::RE("\\s*std::basic_string<char, std::char_traits<char>, std::allocator<char>>\\s*")
    1161     8437636 :       .GlobalReplace("std::string", &s);
    1162             :   // It would be nice if std::vector looked normal
    1163     8437636 :   pcrecpp::RE r("std::vector<([[:print:]]+),\\s?std::allocator<\\s?\\1\\s?>\\s?>");
    1164     8437636 :   r.GlobalReplace("std::vector<\\1>", &s);
    1165             :   // Do it again for nested vectors
    1166     8437636 :   r.GlobalReplace("std::vector<\\1>", &s);
    1167    16875272 :   return s;
    1168     8437636 : }
    1169             : 
    1170             : std::string
    1171       70530 : canonicalPath(const std::string & path)
    1172             : {
    1173       70530 :   return std::filesystem::weakly_canonical(path).c_str();
    1174             : }
    1175             : 
    1176             : bool
    1177           0 : startsWith(const std::string & string1, const std::string & string2)
    1178             : {
    1179           0 :   if (string2.size() > string1.size())
    1180           0 :     return false;
    1181           0 :   return string1.compare(0, string2.size(), string2) == 0;
    1182             : }
    1183             : 
    1184             : void
    1185           0 : replaceStart(std::string & string1, const std::string & string2, const std::string & string3)
    1186             : {
    1187             :   mooseAssert(startsWith(string1, string2),
    1188             :               "Cannot replace the start because it doesn't match the start string");
    1189           0 :   string1.replace(0, string2.size(), string3);
    1190           0 : }
    1191             : 
    1192             : bool
    1193           0 : isAllLowercase(const std::string & str)
    1194             : {
    1195           0 :   return std::all_of(
    1196           0 :       str.begin(), str.end(), [](unsigned char c) { return !std::isalpha(c) || std::islower(c); });
    1197             : }
    1198             : } // MooseUtils namespace
    1199             : 
    1200             : void
    1201       84952 : removeSubstring(std::string & main, const std::string & sub)
    1202             : {
    1203       84952 :   std::string::size_type n = sub.length();
    1204       84952 :   for (std::string::size_type i = main.find(sub); i != std::string::npos; i = main.find(sub))
    1205           0 :     main.erase(i, n);
    1206       84952 : }
    1207             : 
    1208             : std::string
    1209      317324 : removeSubstring(const std::string & main, const std::string & sub)
    1210             : {
    1211      317324 :   std::string copy_main = main;
    1212      317324 :   std::string::size_type n = sub.length();
    1213      634648 :   for (std::string::size_type i = copy_main.find(sub); i != std::string::npos;
    1214      317324 :        i = copy_main.find(sub))
    1215      317324 :     copy_main.erase(i, n);
    1216      317324 :   return copy_main;
    1217           0 : }

Generated by: LCOV version 1.14