/***************************************************************************
stringtools.cpp - description
-------------------
begin : Mon Dec 10 2001
copyright : (C) 2001-2026 by Andre Simon
email : a.simon@mailbox.org
***************************************************************************/
/*
This file is part of Highlight.
Highlight is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Highlight 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 Highlight. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stringtools.h"
using std::string;
using std::vector;
namespace StringTools
{
string change_case ( const string & s, const KeywordCase kcase ) throw()
{
string r ( s );
switch ( kcase ) {
case CASE_UNCHANGED:
break;
case CASE_CAPITALIZE:
case CASE_LOWER:
for ( unsigned int i = 0; i < r.size(); ++i )
r[i] = tolower ( r[i] );
if ( kcase == CASE_CAPITALIZE && r.size() )
r[0] = toupper ( r[0] );
break;
case CASE_UPPER:
for ( unsigned int i = 0; i < r.size(); ++i )
r[i] = toupper ( r[i] );
break;
}
return r;
}
string trimRight ( const string &value )
{
string::size_type where = value.find_last_not_of ( " \t\r" );
if ( where == string::npos )
// string has nothing but space
return string();
if ( where == ( value.length() - 1 ) )
// string has no trailing space, don't copy its contents
return value;
return value.substr ( 0, where + 1 );
}
string trim(const string& s)
{
string str(s);
string::size_type pos = str.find_last_not_of(' ');
if(pos != string::npos) {
str.erase(pos + 1);
pos = str.find_first_not_of(' ');
if(pos != string::npos) str.erase(0, pos);
} else str.erase(str.begin(), str.end());
return str;
}
vector<string> splitString ( const string& s, unsigned char delim )
{
string::size_type pos=s.find ( delim ), oldPos=0;
vector <string> results;
if ( pos ==string::npos ) {
if ( !s.empty() ) results.push_back ( s );
return results;
}
do {
if ( oldPos-pos ) results.push_back ( s.substr ( oldPos, pos-oldPos ) );
oldPos=pos+1;
pos=s.find ( delim, pos+1 );
} while ( pos!=string::npos );
results.push_back ( s.substr ( oldPos ) );
return results;
}
int calcWeight(const string&s)
{
int sum=0;
for (unsigned int i=0; i<s.size(); i++) {
sum+=(i%2)? int(s[i])*3 : int(s[i]);
}
return sum;
}
string getPathAcronym(const string&path, char delim)
{
string acronym;
size_t pos=0;
while ((pos=path.find(delim, pos))!=string::npos) {
pos++;
if (pos<path.size() && path[pos]!=delim) {
acronym+= path[pos];
}
}
if(!acronym.empty()) acronym.replace(acronym.end()-1, acronym.end(), "_");
return acronym;
}
bool endsWith(std::string const & value, std::string const & ending)
{
if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
static void appendUtf8(string& out, unsigned long cp)
{
if (cp < 0x80) {
out += (char) cp;
} else if (cp < 0x800) {
out += (char) (0xC0 | (cp >> 6));
out += (char) (0x80 | (cp & 0x3F));
} else if (cp < 0x10000) {
out += (char) (0xE0 | (cp >> 12));
out += (char) (0x80 | ((cp >> 6) & 0x3F));
out += (char) (0x80 | (cp & 0x3F));
} else {
out += (char) (0xF0 | (cp >> 18));
out += (char) (0x80 | ((cp >> 12) & 0x3F));
out += (char) (0x80 | ((cp >> 6) & 0x3F));
out += (char) (0x80 | (cp & 0x3F));
}
}
string utfWideToUtf8(const string& bytes, unsigned int unitSize, bool bigEndian)
{
const unsigned long replacement = 0xFFFD;
string result;
result.reserve(bytes.size());
auto readUnit = [&](size_t pos) {
unsigned long unit = 0;
for (unsigned int i=0; i<unitSize; i++) {
unsigned char b = (unsigned char) bytes[pos + (bigEndian ? i : unitSize-1-i)];
unit = (unit << 8) | b;
}
return unit;
};
size_t pos = 0;
while (pos + unitSize <= bytes.size()) {
unsigned long cp = readUnit(pos);
pos += unitSize;
if (unitSize == 2 && cp >= 0xD800 && cp <= 0xDBFF) {
// high surrogate, needs a following low surrogate
if (pos + 2 <= bytes.size()) {
unsigned long low = readUnit(pos);
if (low >= 0xDC00 && low <= 0xDFFF) {
cp = 0x10000 + ((cp - 0xD800) << 10) + (low - 0xDC00);
pos += 2;
} else {
cp = replacement;
}
} else {
cp = replacement;
}
} else if ((cp >= 0xD800 && cp <= 0xDFFF) || cp > 0x10FFFF) {
cp = replacement;
}
appendUtf8(result, cp);
}
// truncated trailing code unit
if (pos < bytes.size())
appendUtf8(result, replacement);
return result;
}
int utf8_strlen(const string& str)
{
int c,i,ix,q;
for (q=0, i=0, ix=str.length(); i < ix; i++, q++)
{
c = (unsigned char) str[i];
if (c>=0 && c<=127) i+=0;
else if ((c & 0xE0) == 0xC0) i+=1;
else if ((c & 0xF0) == 0xE0) i+=2;
else if ((c & 0xF8) == 0xF0) i+=3;
else return 0;
}
return q;
}
std::string sanitizeForTerminal(std::string_view s)
{
string out;
out.reserve(s.size());
for (unsigned char c : s)
out += (c < 0x20 && c != '\t') ? '?' : static_cast<char>(c);
return out;
}
}