@ 261105.1
current
/***************************************************************************
                          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;
}

}