/*
* barrier -- mouse and keyboard sharing utility
* Copyright (C) 2014-2016 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* 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 .
*/
#include "arch/Arch.h"
#include "base/String.h"
#include "common/common.h"
#include "common/stdvector.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace barrier {
namespace string {
namespace {
// returns negative in case of non-matching character
int hex_to_number(char ch)
{
switch (ch) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'a': return 10;
case 'b': return 11;
case 'c': return 12;
case 'd': return 13;
case 'e': return 14;
case 'f': return 15;
case 'A': return 10;
case 'B': return 11;
case 'C': return 12;
case 'D': return 13;
case 'E': return 14;
case 'F': return 15;
}
return -1;
}
} // namespace
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 pos;
std::vector width;
std::vector index;
size_t maxIndex = 0;
for (const char* scan = fmt; *scan != '\0'; ++scan) {
if (*scan == '%') {
++scan;
if (*scan == '\0') {
break;
}
else if (*scan == '%') {
// literal
index.push_back(0);
pos.push_back(static_cast((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((scan - 1) - fmt));
width.push_back(static_cast((end - scan) + 2));
if (i > maxIndex) {
maxIndex = i;
}
scan = end;
}
}
else {
// improper escape -- ignore
}
}
}
// get args
std::vector value;
std::vector 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 = strlen(arg);
value.push_back(arg);
length.push_back(len);
}
// compute final length
size_t resultLength = strlen(fmt);
const int n = static_cast(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;
int len = (int)(sizeof(tmp) / sizeof(tmp[0]));
std::string result;
while (buffer != NULL) {
// try printing into the buffer
va_list args;
va_start(args, fmt);
int n = std::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 = NULL;
}
}
return result;
}
void
findReplaceAll(
std::string& subject,
const std::string& find,
const std::string& replace)
{
size_t pos = 0;
while ((pos = subject.find(find, pos)) != std::string::npos) {
subject.replace(pos, find.length(), replace);
pos += replace.length();
}
}
std::string
removeFileExt(std::string filename)
{
size_t dot = filename.find_last_of('.');
if (dot == std::string::npos) {
return filename;
}
return filename.substr(0, dot);
}
std::string to_hex(const std::vector& subject, int width, const char fill)
{
std::stringstream ss;
ss << std::hex;
for (unsigned int i = 0; i < subject.size(); i++) {
ss << std::setw(width) << std::setfill(fill) << static_cast(subject[i]);
}
return ss.str();
}
std::vector from_hex(const std::string& data)
{
std::vector result;
result.reserve(data.size() / 2);
std::size_t i = 0;
while (i < data.size()) {
if (data[i] == ':') {
i++;
continue;
}
if (i + 2 > data.size()) {
return {}; // uneven character count follows, it's unclear how to interpret it
}
auto high = hex_to_number(data[i]);
auto low = hex_to_number(data[i + 1]);
if (high < 0 || low < 0) {
return {};
}
result.push_back(high * 16 + low);
i += 2;
}
return result;
}
void
uppercase(std::string& subject)
{
std::transform(subject.begin(), subject.end(), subject.begin(), ::toupper);
}
void
removeChar(std::string& subject, const char c)
{
subject.erase(std::remove(subject.begin(), subject.end(), c), subject.end());
}
std::string
sizeTypeToString(size_t n)
{
std::stringstream ss;
ss << n;
return ss.str();
}
size_t
stringToSizeType(std::string string)
{
std::istringstream iss(string);
size_t value;
iss >> value;
return value;
}
std::vector
splitString(std::string string, const char c)
{
std::vector results;
size_t head = 0;
size_t separator = string.find(c);
while (separator != std::string::npos) {
if (head!=separator) {
results.push_back(string.substr(head, separator - head));
}
head = separator + 1;
separator = string.find(c, head);
}
if (head < string.size()) {
results.push_back(string.substr(head, string.size() - head));
}
return results;
}
//
// CaselessCmp
//
bool CaselessCmp::cmpEqual(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);
}
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);
}
bool
CaselessCmp::less(const std::string& a, const std::string& b)
{
return std::lexicographical_compare(
a.begin(), a.end(),
b.begin(), b.end(),
&barrier::string::CaselessCmp::cmpLess);
}
bool
CaselessCmp::equal(const std::string& a, const std::string& b)
{
return !(less(a, b) || less(b, a));
}
bool
CaselessCmp::operator()(const std::string& a, const std::string& b) const
{
return less(a, b);
}
}
}