Files
FreeCAD/src/App/ElementNamingUtils.cpp
T
Chris Hennes 07e94dfaae Part: Eliminate use of sscanf
Replace with safer modern C++ `std::from_chars` (plus some additional
validation for expected inputs). Includes unit tests for new function.
2026-02-27 23:29:31 +01:00

144 lines
3.1 KiB
C++

// SPDX-License-Identifier: LGPL-2.1-or-later
#include "ElementNamingUtils.h"
#include <charconv>
#include <boost/algorithm/string/predicate.hpp>
const char* Data::isMappedElement(const char* name)
{
if (name && boost::starts_with(name, ELEMENT_MAP_PREFIX)) {
return name + ELEMENT_MAP_PREFIX_SIZE;
}
return nullptr;
}
std::string Data::newElementName(const char* name)
{
if (!name) {
return {};
}
const char* dot = strrchr(name, '.');
if (!dot || dot == name) {
return name;
}
const char* c = dot - 1;
for (; c != name; --c) {
if (*c == '.') {
++c;
break;
}
}
if (isMappedElement(c)) {
return std::string(name, dot - name);
}
return name;
}
std::string Data::oldElementName(const char* name)
{
if (!name) {
return {};
}
const char* dot = strrchr(name, '.');
if (!dot || dot == name) {
return name;
}
const char* c = dot - 1;
for (; c != name; --c) {
if (*c == '.') {
++c;
break;
}
}
if (isMappedElement(c)) {
return std::string(name, c - name) + (dot + 1);
}
return name;
}
std::string Data::noElementName(const char* name)
{
if (!name) {
return {};
}
auto element = findElementName(name);
if (element) {
return std::string(name, element - name);
}
return name;
}
const char* Data::findElementName(const char* subname)
{
// skip leading dots
while (subname && subname[0] == '.') {
++subname;
}
if (!subname || !subname[0] || isMappedElement(subname)) {
return subname;
}
const char* dot = strrchr(subname, '.');
if (!dot) {
return subname;
}
const char* element = dot + 1;
if (dot == subname || isMappedElement(element)) {
return element;
}
for (--dot; dot != subname; --dot) {
if (*dot == '.') {
++dot;
break;
}
}
if (isMappedElement(dot)) {
return dot;
}
return element;
}
bool Data::hasMissingElement(const char* subname)
{
if (!subname) {
return false;
}
auto dot = strrchr(subname, '.');
if (dot) {
subname = dot + 1;
}
return boost::starts_with(subname, MISSING_PREFIX);
}
const char* Data::hasMappedElementName(const char* subname)
{
return isMappedElement(findElementName(subname));
}
const std::string Data::indexSuffix(int index, const char* label)
{
if (index < 2) { // Don't add a suffix for item #1, begin appending at 2
return {""};
}
std::string name(label);
name += std::to_string(index);
return name;
}
int Data::indexOfElement(std::string_view s, std::string_view prefix)
{
int idx = 0;
if (prefix.empty()) {
return idx;
}
if (s.starts_with(prefix)) {
auto num = s.substr(prefix.size());
auto [ptr, ec] = std::from_chars(num.data(), num.data() + num.size(), idx);
if (ec != std::errc{} || ptr != num.data() + num.size()) {
return 0;
}
}
return idx;
}