2014-03-14 20:34:19 +00:00
|
|
|
/*
|
|
|
|
|
* synergy -- mouse and keyboard sharing utility
|
2016-09-07 14:24:00 +00:00
|
|
|
* Copyright (C) 2014-2016 Symless Ltd.
|
2024-07-02 19:07:06 +00:00
|
|
|
*
|
2014-03-14 20:34:19 +00:00
|
|
|
* This package is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
2015-05-03 02:33:52 +00:00
|
|
|
* found in the file LICENSE that should have accompanied this file.
|
2024-07-02 19:07:06 +00:00
|
|
|
*
|
2014-03-14 20:34:19 +00:00
|
|
|
* This package is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "base/String.h"
|
2024-07-02 19:07:06 +00:00
|
|
|
#include "arch/Arch.h"
|
2014-03-21 08:32:36 +00:00
|
|
|
#include "common/stdvector.h"
|
2014-03-14 20:34:19 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
#include <algorithm>
|
2014-03-21 08:32:36 +00:00
|
|
|
#include <cctype>
|
2024-07-02 19:07:06 +00:00
|
|
|
#include <cerrno>
|
|
|
|
|
#include <cstdarg>
|
2022-12-08 09:16:44 +00:00
|
|
|
#include <cstdint>
|
2014-03-21 08:32:36 +00:00
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <cstring>
|
2015-04-02 13:01:50 +00:00
|
|
|
#include <iomanip>
|
2024-07-02 19:07:06 +00:00
|
|
|
#include <sstream>
|
|
|
|
|
#include <stdio.h>
|
2014-03-20 10:32:40 +00:00
|
|
|
|
2014-03-21 08:32:36 +00:00
|
|
|
namespace synergy {
|
|
|
|
|
namespace string {
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
String format(const char *fmt, ...) {
|
|
|
|
|
va_list args;
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
|
String result = vformat(fmt, args);
|
|
|
|
|
va_end(args);
|
|
|
|
|
return result;
|
2014-03-21 08:32:36 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
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;
|
2024-07-16 08:30:35 +00:00
|
|
|
size_t fmtLength = 0; // To store the length of fmt
|
2024-07-02 19:07:06 +00:00
|
|
|
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 (i > maxIndex) {
|
|
|
|
|
maxIndex = i;
|
|
|
|
|
}
|
|
|
|
|
scan = end;
|
2016-12-28 11:50:32 +00:00
|
|
|
}
|
2024-07-02 19:07:06 +00:00
|
|
|
} else {
|
|
|
|
|
// improper escape -- ignore
|
|
|
|
|
}
|
2016-12-28 11:50:32 +00:00
|
|
|
}
|
2024-07-02 19:07:06 +00:00
|
|
|
++scan;
|
2024-07-16 08:30:35 +00:00
|
|
|
++fmtLength; // Increment fmtLength for each character processed
|
2024-07-02 19:07:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get args
|
|
|
|
|
std::vector<const char *> value;
|
|
|
|
|
std::vector<size_t> length;
|
|
|
|
|
value.push_back("%");
|
|
|
|
|
length.push_back(1);
|
|
|
|
|
for (int 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
|
2024-07-16 08:30:35 +00:00
|
|
|
size_t resultLength = fmtLength;
|
2024-07-02 19:07:06 +00:00
|
|
|
const int n = static_cast<int>(pos.size());
|
|
|
|
|
for (int i = 0; i < n; ++i) {
|
|
|
|
|
resultLength -= width[i];
|
|
|
|
|
resultLength += length[index[i]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// substitute
|
|
|
|
|
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;
|
|
|
|
|
}
|
2016-12-28 11:50:32 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
String sprintf(const char *fmt, ...) {
|
|
|
|
|
char tmp[1024];
|
|
|
|
|
char *buffer = tmp;
|
|
|
|
|
int len = (int)(sizeof(tmp) / sizeof(tmp[0]));
|
|
|
|
|
String result;
|
|
|
|
|
while (buffer != NULL) {
|
|
|
|
|
// try printing into the buffer
|
|
|
|
|
va_list args;
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
|
int n = ARCH->vsnprintf(buffer, len, fmt, args);
|
|
|
|
|
va_end(args);
|
2016-12-28 11:50:32 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
// 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];
|
2016-12-28 11:50:32 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
// if it was big enough then save the string and don't try again
|
|
|
|
|
else {
|
|
|
|
|
result = buffer;
|
|
|
|
|
if (buffer != tmp) {
|
|
|
|
|
delete[] buffer;
|
|
|
|
|
}
|
|
|
|
|
buffer = NULL;
|
2016-12-28 11:50:32 +00:00
|
|
|
}
|
2024-07-02 19:07:06 +00:00
|
|
|
}
|
2016-12-28 11:50:32 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
return result;
|
2014-03-21 08:32:36 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-16 13:36:44 +00:00
|
|
|
void findReplaceAll(
|
|
|
|
|
String &subject, const String &find, const String &replace) {
|
2024-07-02 19:07:06 +00:00
|
|
|
size_t pos = 0;
|
|
|
|
|
while ((pos = subject.find(find, pos)) != String::npos) {
|
|
|
|
|
subject.replace(pos, find.length(), replace);
|
|
|
|
|
pos += replace.length();
|
|
|
|
|
}
|
2014-03-14 20:34:19 +00:00
|
|
|
}
|
2014-03-20 10:32:40 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
String removeFileExt(String filename) {
|
|
|
|
|
size_t dot = filename.find_last_of('.');
|
2015-01-09 13:46:35 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
if (dot == String::npos) {
|
|
|
|
|
return filename;
|
|
|
|
|
}
|
2015-01-09 13:46:35 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
return filename.substr(0, dot);
|
2015-01-09 13:46:35 +00:00
|
|
|
}
|
2014-03-21 08:32:36 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
void toHex(String &subject, int width, const char fill) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << std::hex;
|
|
|
|
|
for (unsigned int i = 0; i < subject.length(); i++) {
|
|
|
|
|
ss << std::setw(width) << std::setfill(fill)
|
|
|
|
|
<< (int)(unsigned char)subject[i];
|
|
|
|
|
}
|
2015-04-02 13:01:50 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
subject = ss.str();
|
2015-04-02 13:01:50 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
void uppercase(String &subject) {
|
|
|
|
|
std::transform(subject.begin(), subject.end(), subject.begin(), ::toupper);
|
2015-04-02 13:01:50 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
void removeChar(String &subject, const char c) {
|
|
|
|
|
subject.erase(std::remove(subject.begin(), subject.end(), c), subject.end());
|
2015-04-02 13:01:50 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
String sizeTypeToString(size_t n) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << n;
|
|
|
|
|
return ss.str();
|
2015-05-18 18:27:54 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
size_t stringToSizeType(String string) {
|
|
|
|
|
std::istringstream iss(string);
|
|
|
|
|
size_t value;
|
|
|
|
|
iss >> value;
|
|
|
|
|
return value;
|
2015-05-19 21:23:43 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
std::vector<String> splitString(String string, const char c) {
|
|
|
|
|
std::vector<String> results;
|
2016-12-28 11:50:32 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
size_t head = 0;
|
|
|
|
|
size_t separator = string.find(c);
|
|
|
|
|
while (separator != String::npos) {
|
|
|
|
|
if (head != separator) {
|
|
|
|
|
results.push_back(string.substr(head, separator - head));
|
2016-12-28 11:50:32 +00:00
|
|
|
}
|
2024-07-02 19:07:06 +00:00
|
|
|
head = separator + 1;
|
|
|
|
|
separator = string.find(c, head);
|
|
|
|
|
}
|
2016-12-28 11:50:32 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
if (head < string.size()) {
|
|
|
|
|
results.push_back(string.substr(head, string.size() - head));
|
|
|
|
|
}
|
2016-12-28 11:50:32 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
return results;
|
2015-10-29 19:52:55 +00:00
|
|
|
}
|
|
|
|
|
|
2014-03-21 08:32:36 +00:00
|
|
|
//
|
|
|
|
|
// CaselessCmp
|
|
|
|
|
//
|
|
|
|
|
|
2024-07-16 13:36:44 +00:00
|
|
|
bool CaselessCmp::cmpEqual(
|
|
|
|
|
const String::value_type &a, const String::value_type &b) {
|
2024-07-02 19:07:06 +00:00
|
|
|
// should use std::tolower but not in all versions of libstdc++ have it
|
|
|
|
|
return tolower(a) == tolower(b);
|
2014-03-21 08:32:36 +00:00
|
|
|
}
|
2014-03-20 13:34:21 +00:00
|
|
|
|
2024-07-16 13:36:44 +00:00
|
|
|
bool CaselessCmp::cmpLess(
|
|
|
|
|
const String::value_type &a, const String::value_type &b) {
|
2024-07-02 19:07:06 +00:00
|
|
|
// should use std::tolower but not in all versions of libstdc++ have it
|
|
|
|
|
return tolower(a) < tolower(b);
|
2014-03-21 08:32:36 +00:00
|
|
|
}
|
2014-03-20 13:34:21 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
bool CaselessCmp::less(const String &a, const String &b) {
|
2024-07-16 13:36:44 +00:00
|
|
|
return std::lexicographical_compare(
|
|
|
|
|
a.begin(), a.end(), b.begin(), b.end(),
|
|
|
|
|
&synergy::string::CaselessCmp::cmpLess);
|
2014-03-21 08:32:36 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
bool CaselessCmp::equal(const String &a, const String &b) {
|
|
|
|
|
return !(less(a, b) || less(b, a));
|
2014-03-21 08:32:36 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
bool CaselessCmp::operator()(const String &a, const String &b) const {
|
|
|
|
|
return less(a, b);
|
2014-03-21 08:32:36 +00:00
|
|
|
}
|
2014-03-20 13:34:21 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
} // namespace string
|
|
|
|
|
} // namespace synergy
|