deskflow/src/lib/base/String.cpp

182 lines
4.3 KiB
C++
Raw Normal View History

/*
* Deskflow -- mouse and keyboard sharing utility
2025-01-25 13:56:53 +00:00
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "base/String.h"
#include <algorithm>
#include <cstdarg>
#include <sstream>
#include <vector>
2025-06-19 21:47:35 +00:00
namespace deskflow::string {
std::string format(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
std::string result = vformat(fmt, args);
va_end(args);
return result;
}
std::string vformat(const char *fmt, va_list args)
{
// find highest indexed substitution and the locations of substitutions
std::vector<size_t> pos;
std::vector<size_t> width;
std::vector<size_t> index;
size_t maxIndex = 0;
Solve SonarCloud security hotspots and bugs (#7383) * Test: allocLockCursorToScreenInfo_withState_setsState * Merge remote-tracking branch 'origin/master' into S1-1717-sonarcloud-issues * Remove header wrappers for gmock/gtest * Convenience wrapper script for tests * Use `std::copy` instead of `strcpy` * Use `std::ranges::copy` instead of `stdcpy` * Delete dead code * Add guitests VS Code config and test wrapper * Revert "Delete dead code" This reverts commit aa40f5cd35a22b4b69acfd1876aceea053088060. * Build guitests with CMake * Run all tests * Don't use --gtest_filter for Qt tests * Undo skip for GUI tests * Coverage for `IpcClient::sendCommand` * Remove provider and proxy * Use lamda for StreamProvider to reduce boilerplate * Restore version checker tests * Remove activation souces * Tasks for current/all tests * Change command for tasks * Mock QNetworkAccessManager * Create core app to satisfy Qt assertations * Use `std::copy` instead of `std::ranges::copy` * Remove integtests * Merge guitests into unittests * Use std::string::length * Remove include (resolves to root) * Fix memory leaks * Disable sigsegv tests * Fixed formatting * Remove guitests from CI * New MainWindowTests * Passing test for MainWindowTests * Use alternative to strlen in MainWindow::checkSecureSocket * Passing test for Log::print * Fixed dtor call order * Fixed var name typo * Use proxy instead of `#define protected public` * Add args test for log * Add license ctor * Fixed log test for release * Add error log test * Init qt with -platform offscreen * Back-out initQt function * Use QT_QPA_PLATFORM * Try QT_QPA_PLATFORM=offscreen * Use more readable env node * Set QT_QPA_PLATFORM in CI and CodeQL * Remove env not needed * Modernize Log::print * Calculate the length of the format string fmt during the initial scan loop * More direct and efficient alterative to `strlen` * Fixed major maint issues in AppConfig * New clang and cmake rules * Set `max_pargs_hwrap` to 4 * Undo clang format for now * Fix missing `Setting::` * Fixed missing `m_S` on Windows * Re-add accidental dep resolution (fix later) * Fixed missing buffer position increment * Turn on errors * Fixed tests and improve error message * Fixed segfault on log refactor * Extract log time to function * Copyright date * Update ChangeLog
2024-07-16 08:30:35 +00:00
size_t fmtLength = 0; // To store the length of fmt
const char *scan = fmt;
while (*scan) {
if (*scan == '%') {
++scan;
if (*scan == '\0') {
break;
} else if (*scan == '%') {
// literal
index.push_back(0);
pos.push_back(static_cast<size_t>((scan - 1) - fmt));
width.push_back(2);
} else if (*scan == '{') {
// get argument index
char *end;
errno = 0;
long i = strtol(scan + 1, &end, 10);
if (errno || (i < 0) || (*end != '}')) {
// invalid index -- ignore
scan = end - 1; // BUG if there are digits?
} else {
index.push_back(i);
pos.push_back(static_cast<size_t>((scan - 1) - fmt));
width.push_back(static_cast<size_t>((end - scan) + 2));
if (static_cast<size_t>(i) > maxIndex) {
maxIndex = i;
}
scan = end;
}
} else {
// improper escape -- ignore
}
}
++scan;
Solve SonarCloud security hotspots and bugs (#7383) * Test: allocLockCursorToScreenInfo_withState_setsState * Merge remote-tracking branch 'origin/master' into S1-1717-sonarcloud-issues * Remove header wrappers for gmock/gtest * Convenience wrapper script for tests * Use `std::copy` instead of `strcpy` * Use `std::ranges::copy` instead of `stdcpy` * Delete dead code * Add guitests VS Code config and test wrapper * Revert "Delete dead code" This reverts commit aa40f5cd35a22b4b69acfd1876aceea053088060. * Build guitests with CMake * Run all tests * Don't use --gtest_filter for Qt tests * Undo skip for GUI tests * Coverage for `IpcClient::sendCommand` * Remove provider and proxy * Use lamda for StreamProvider to reduce boilerplate * Restore version checker tests * Remove activation souces * Tasks for current/all tests * Change command for tasks * Mock QNetworkAccessManager * Create core app to satisfy Qt assertations * Use `std::copy` instead of `std::ranges::copy` * Remove integtests * Merge guitests into unittests * Use std::string::length * Remove include (resolves to root) * Fix memory leaks * Disable sigsegv tests * Fixed formatting * Remove guitests from CI * New MainWindowTests * Passing test for MainWindowTests * Use alternative to strlen in MainWindow::checkSecureSocket * Passing test for Log::print * Fixed dtor call order * Fixed var name typo * Use proxy instead of `#define protected public` * Add args test for log * Add license ctor * Fixed log test for release * Add error log test * Init qt with -platform offscreen * Back-out initQt function * Use QT_QPA_PLATFORM * Try QT_QPA_PLATFORM=offscreen * Use more readable env node * Set QT_QPA_PLATFORM in CI and CodeQL * Remove env not needed * Modernize Log::print * Calculate the length of the format string fmt during the initial scan loop * More direct and efficient alterative to `strlen` * Fixed major maint issues in AppConfig * New clang and cmake rules * Set `max_pargs_hwrap` to 4 * Undo clang format for now * Fix missing `Setting::` * Fixed missing `m_S` on Windows * Re-add accidental dep resolution (fix later) * Fixed missing buffer position increment * Turn on errors * Fixed tests and improve error message * Fixed segfault on log refactor * Extract log time to function * Copyright date * Update ChangeLog
2024-07-16 08:30:35 +00:00
++fmtLength; // Increment fmtLength for each character processed
}
// get args
std::vector<const char *> value;
std::vector<size_t> length;
value.push_back("%");
length.push_back(1);
for (size_t i = 0; i < maxIndex; ++i) {
const char *arg = va_arg(args, const char *);
size_t len = strnlen(arg, SIZE_MAX);
value.push_back(arg);
length.push_back(len);
}
// compute final length
Solve SonarCloud security hotspots and bugs (#7383) * Test: allocLockCursorToScreenInfo_withState_setsState * Merge remote-tracking branch 'origin/master' into S1-1717-sonarcloud-issues * Remove header wrappers for gmock/gtest * Convenience wrapper script for tests * Use `std::copy` instead of `strcpy` * Use `std::ranges::copy` instead of `stdcpy` * Delete dead code * Add guitests VS Code config and test wrapper * Revert "Delete dead code" This reverts commit aa40f5cd35a22b4b69acfd1876aceea053088060. * Build guitests with CMake * Run all tests * Don't use --gtest_filter for Qt tests * Undo skip for GUI tests * Coverage for `IpcClient::sendCommand` * Remove provider and proxy * Use lamda for StreamProvider to reduce boilerplate * Restore version checker tests * Remove activation souces * Tasks for current/all tests * Change command for tasks * Mock QNetworkAccessManager * Create core app to satisfy Qt assertations * Use `std::copy` instead of `std::ranges::copy` * Remove integtests * Merge guitests into unittests * Use std::string::length * Remove include (resolves to root) * Fix memory leaks * Disable sigsegv tests * Fixed formatting * Remove guitests from CI * New MainWindowTests * Passing test for MainWindowTests * Use alternative to strlen in MainWindow::checkSecureSocket * Passing test for Log::print * Fixed dtor call order * Fixed var name typo * Use proxy instead of `#define protected public` * Add args test for log * Add license ctor * Fixed log test for release * Add error log test * Init qt with -platform offscreen * Back-out initQt function * Use QT_QPA_PLATFORM * Try QT_QPA_PLATFORM=offscreen * Use more readable env node * Set QT_QPA_PLATFORM in CI and CodeQL * Remove env not needed * Modernize Log::print * Calculate the length of the format string fmt during the initial scan loop * More direct and efficient alterative to `strlen` * Fixed major maint issues in AppConfig * New clang and cmake rules * Set `max_pargs_hwrap` to 4 * Undo clang format for now * Fix missing `Setting::` * Fixed missing `m_S` on Windows * Re-add accidental dep resolution (fix later) * Fixed missing buffer position increment * Turn on errors * Fixed tests and improve error message * Fixed segfault on log refactor * Extract log time to function * Copyright date * Update ChangeLog
2024-07-16 08:30:35 +00:00
size_t resultLength = fmtLength;
2025-04-27 16:20:28 +00:00
const auto n = static_cast<int>(pos.size());
for (int i = 0; i < n; ++i) {
resultLength -= width[i];
resultLength += length[index[i]];
}
// substitute
std::string result;
result.reserve(resultLength);
size_t src = 0;
for (int i = 0; i < n; ++i) {
result.append(fmt + src, pos[i] - src);
result.append(value[index[i]]);
src = pos[i] + width[i];
}
result.append(fmt + src);
return result;
}
std::string sprintf(const char *fmt, ...)
{
char tmp[1024];
char *buffer = tmp;
auto len = static_cast<int>(std::size(tmp));
std::string result;
while (buffer != nullptr) {
// try printing into the buffer
va_list args;
va_start(args, fmt);
2025-01-25 12:34:17 +00:00
int n = vsnprintf(buffer, len, fmt, args);
va_end(args);
// if the buffer wasn't big enough then make it bigger and try again
if (n < 0 || n > len) {
if (buffer != tmp) {
delete[] buffer;
}
len *= 2;
buffer = new char[len];
}
// if it was big enough then save the string and don't try again
else {
result = buffer;
if (buffer != tmp) {
delete[] buffer;
}
buffer = nullptr;
}
}
return result;
}
std::string sizeTypeToString(size_t n)
{
std::stringstream ss;
ss << n;
return ss.str();
2015-05-18 18:27:54 +00:00
}
size_t stringToSizeType(std::string string)
{
std::istringstream iss(string);
size_t value;
iss >> value;
return value;
}
//
// CaselessCmp
//
bool CaselessCmp::operator()(const std::string &a, const std::string &b) const
{
return less(a, b);
}
bool CaselessCmp::less(const std::string_view &a, const std::string_view &b)
{
return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(), &deskflow::string::CaselessCmp::cmpLess);
}
bool CaselessCmp::equal(const std::string &a, const std::string &b)
{
return !(less(a, b) || less(b, a));
}
bool CaselessCmp::cmpLess(const std::string::value_type &a, const std::string::value_type &b)
{
// should use std::tolower but not in all versions of libstdc++ have it
return tolower(a) < tolower(b);
}
2025-06-19 21:47:35 +00:00
} // namespace deskflow::string