// ----------------------------------------------------------------------
// File: StringConversion.cc
// Author: Andreas-Joachim Peters - CERN
// ----------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2011 CERN/Switzerland *
* *
* This program 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. *
* *
* This program 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 "StringConversion.hh"
#include "common/Logging.hh"
#include "common/Timing.hh"
#include
#include "curl/curl.h"
#include
#include
EOSCOMMONNAMESPACE_BEGIN
static std::atomic sCounter {0};
//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
CurlGlobalInitializer::CurlGlobalInitializer()
{
if (sCounter++ == 0) {
curl_global_init(CURL_GLOBAL_DEFAULT);
}
}
//------------------------------------------------------------------------------
// Destructor
//------------------------------------------------------------------------------
CurlGlobalInitializer::~CurlGlobalInitializer()
{
if (--sCounter == 0) {
curl_global_cleanup();
}
}
char StringConversion::pAscii2HexLkup[256];
char StringConversion::pHex2AsciiLkup[16];
//------------------------------------------------------------------------------
// Tokenize a string
//------------------------------------------------------------------------------
void
StringConversion::Tokenize(const std::string& str,
std::vector& tokens,
const std::string& delimiters)
{
// Skip delimiters at the beginning
std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
// Find first "non-delimiter"
std::string::size_type pos = str.find_first_of(delimiters, lastPos);
while (std::string::npos != pos || std::string::npos != lastPos) {
// Found a token, add it to the vector.
tokens.push_back(str.substr(lastPos, pos - lastPos));
// Skip delimiters. Note the "not_of"
lastPos = str.find_first_not_of(delimiters, pos);
// Find next "non-delimiter"
pos = str.find_first_of(delimiters, lastPos);
}
}
//------------------------------------------------------------------------------
// Tokenize a string accepting also empty members e.g. a||b returns 3 fields
//------------------------------------------------------------------------------
void
StringConversion::EmptyTokenize(const std::string& str,
std::vector& tokens,
const std::string& delimiters)
{
// Skip delimiters at beginning.
std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
// Find first "non-delimiter".
std::string::size_type pos = str.find_first_of(delimiters, lastPos);
while (std::string::npos != pos || std::string::npos != lastPos) {
// Found a token, add it to the vector.
tokens.push_back(str.substr(lastPos, pos - lastPos));
// Skip delimiters. Note the "not_of"
lastPos = str.find_first_of(delimiters, pos);
if (lastPos != std::string::npos) {
lastPos++;
}
// Find next "non-delimiter"
pos = str.find_first_of(delimiters, lastPos);
}
}
//------------------------------------------------------------------------------
// Convert a long long value into time s,m,h,d,y scale
//------------------------------------------------------------------------------
const char*
StringConversion::GetReadableAgeString(XrdOucString& sizestring,
unsigned long long age)
{
char formsize[1024];
if (age > 31536000) {
sprintf(formsize, "%lluy", age / 31536000);
} else if (age > 86400) {
sprintf(formsize, "%llud", age / 86400);
} else if (age > 3600) {
sprintf(formsize, "%lluh", age / 3600);
} else if (age > 60) {
sprintf(formsize, "%llum", age / 60);
} else {
sprintf(formsize, "%llus", age);
}
sizestring = formsize;
return sizestring.c_str();
}
std::string
StringConversion::GetFixedDouble(double value, size_t width, size_t precision)
{
char dline[1024];
char format[16];
if (precision == 1) {
snprintf(dline, sizeof(dline), "%.01f", value);
} else if (precision == 2) {
snprintf(dline, sizeof(dline), "%.02f", value);
} else if (precision == 3) {
snprintf(dline, sizeof(dline), "%.03f", value);
} else if (precision == 4) {
snprintf(dline, sizeof(dline), "%.04f", value);
} else {
snprintf(dline, sizeof(dline), "%f", value);
}
char wline[1024];
snprintf(format, sizeof(format), "%%-%lus", width);
snprintf(wline, sizeof(wline), format, dline);
return std::string(wline);
}
//----------------------------------------------------------------------------
//! Convert a long long value into K,M,G,T,P,E byte scale
//----------------------------------------------------------------------------
std::string
StringConversion::GetReadableSizeString(unsigned long long insize,
const char* unit, size_t si)
{
char formsize[1024];
if (insize >= 10 * si) {
if (insize >= (si * si)) {
if (insize >= (si * si * si)) {
if (insize >= (si * si * si * si)) {
if (insize >= (si * si * si * si * si)) {
if (insize >= (si * si * si * si * si * si)) {
// EB
sprintf(formsize, "%.02f E%s",
insize * 1.0 / (si * si * si * si * si * si), unit);
} else {
// PB
sprintf(formsize, "%.02f P%s",
insize * 1.0 / (si * si * si * si * si), unit);
}
} else {
// TB
sprintf(formsize, "%.02f T%s",
insize * 1.0 / (si * si * si * si), unit);
}
} else {
// GB
sprintf(formsize, "%.02f G%s", insize * 1.0 / (si * si * si), unit);
}
} else {
// MB
sprintf(formsize, "%.02f M%s", insize * 1.0 / (si * si), unit);
}
} else {
sprintf(formsize, "%.02f k%s", insize * 1.0 / (si), unit);
}
} else {
if (strlen(unit)) {
sprintf(formsize, "%llu %s", insize, unit);
} else {
sprintf(formsize, "%llu", insize);
}
}
return std::string(formsize);
}
//------------------------------------------------------------------------------
// Convert a long long value into K,M,G,T,P,E byte scale
//------------------------------------------------------------------------------
const char*
StringConversion::GetReadableSizeString(XrdOucString& sizestring,
unsigned long long insize,
const char* unit)
{
sizestring = GetReadableSizeString(insize, unit).c_str();
return sizestring.c_str();
}
//------------------------------------------------------------------------------
// Convert a long long value into K,M,G,T,P,E byte scale
//------------------------------------------------------------------------------
const char*
StringConversion::GetReadableSizeString(std::string& sizestring,
unsigned long long insize,
const char* unit)
{
sizestring = GetReadableSizeString(insize, unit);
return sizestring.c_str();
}
//------------------------------------------------------------------------------
// Convert a string containing binary characters to its hex representation
//------------------------------------------------------------------------------
std::string
StringConversion::string_to_hex(const std::string& input)
{
static const char* const lut = "0123456789ABCDEF";
size_t len = input.length();
std::string output;
output.reserve(2 * len);
for (size_t i = 0; i < len; ++i) {
const unsigned char c = input[i];
output.push_back(lut[c >> 4]);
output.push_back(lut[c & 15]);
}
return output;
}
//------------------------------------------------------------------------------
// Convert a char to its hex representation
//------------------------------------------------------------------------------
std::string
StringConversion::char_to_hex(const char input)
{
static const char* const lut = "0123456789abcdef";
std::string output;
output.resize(2);
const unsigned char c = input;
output[0] = lut[c >> 4];
output[1] = lut[c & 15];
return output;
}
//------------------------------------------------------------------------------
// Convert binary string given as a char* and length to hex string representation
//------------------------------------------------------------------------------
std::string
StringConversion::BinData2HexString(const char* buf, const size_t buf_len,
const size_t nominal_len,
const char separator)
{
std::string out;
if (buf_len == 0) {
return out;
}
char hb[4];
for (size_t i = 0; i < nominal_len; ++i) {
unsigned char target = 0x00;
if (i < buf_len) {
target = buf[i];
}
if ((separator != 0x00) && (i != (nominal_len - 1))) {
sprintf(hb, "%02x%c", target, separator);
} else {
sprintf(hb, "%02x", target);
}
out += hb;
}
return out;
}
//------------------------------------------------------------------------------
// Convert checksum hex representation to binary string
//------------------------------------------------------------------------------
std::unique_ptr
StringConversion::Hex2BinDataChar(const std::string& shex, size_t& out_size,
const size_t nominal_len)
{
out_size = 0;
std::unique_ptr buf {new char[nominal_len]};
if ((buf == nullptr) || shex.empty()) {
return nullptr;
}
memset(buf.get(), 0, nominal_len);
char hex[3];
for (size_t i = 0;
((i < shex.length() - 1) && (i / 2 < nominal_len)); i += 2) {
hex[0] = shex.at(i);
hex[1] = shex.at(i + 1);
hex[2] = '\0';
buf.get()[i / 2] = std::stol(hex, 0, 16);
++out_size;
}
return buf;
}
//----------------------------------------------------------------------------
//! Get size from the given string, return true if parsing was successful,
//! false otherwise
//----------------------------------------------------------------------------
bool
StringConversion::GetSizeFromString(const std::string& sizestring,
uint64_t& out)
{
out = GetSizeFromString(sizestring.c_str());
return (errno == 0);
}
//------------------------------------------------------------------------------
// Convert a readable string into a number
//------------------------------------------------------------------------------
unsigned long long
StringConversion::GetSizeFromString(const char* instring)
{
if (!instring) {
errno = EINVAL;
return 0;
}
XrdOucString sizestring = instring;
unsigned long long convfactor = 1ll;
errno = 0;
if (!sizestring.length()) {
errno = EINVAL;
return 0;
}
if (sizestring.endswith("B") || sizestring.endswith("b")) {
sizestring.erase(sizestring.length() - 1);
}
if (sizestring.endswith("E") || sizestring.endswith("e")) {
convfactor = 1000ll * 1000ll * 1000ll * 1000ll * 1000ll * 1000ll;
}
if (sizestring.endswith("P") || sizestring.endswith("p")) {
convfactor = 1000ll * 1000ll * 1000ll * 1000ll * 1000ll;
}
if (sizestring.endswith("T") || sizestring.endswith("t")) {
convfactor = 1000ll * 1000ll * 1000ll * 1000ll;
}
if (sizestring.endswith("G") || sizestring.endswith("g")) {
convfactor = 1000ll * 1000ll * 1000ll;
}
if (sizestring.endswith("M") || sizestring.endswith("m")) {
convfactor = 1000ll * 1000ll;
}
if (sizestring.endswith("K") || sizestring.endswith("k")) {
convfactor = 1000ll;
}
if (sizestring.endswith("S") || sizestring.endswith("s")) {
convfactor = 1ll;
}
if ((sizestring.length() > 3) && (sizestring.endswith("MIN")
|| sizestring.endswith("min"))) {
convfactor = 60ll;
}
if (sizestring.endswith("H") || sizestring.endswith("h")) {
convfactor = 3600ll;
}
if (sizestring.endswith("D") || sizestring.endswith("d")) {
convfactor = 86400ll;
}
if (sizestring.endswith("W") || sizestring.endswith("w")) {
convfactor = 7 * 86400ll;
}
if ((sizestring.length() > 2) && (sizestring.endswith("MO")
|| sizestring.endswith("mo"))) {
convfactor = 31 * 86400ll;
}
if (sizestring.endswith("Y") || sizestring.endswith("y")) {
convfactor = 365 * 86400ll;
}
if (convfactor > 1) {
sizestring.erase(sizestring.length() - 1);
}
if ((sizestring.find(".")) != STR_NPOS) {
return ((unsigned long long)(strtod(sizestring.c_str(), NULL) * convfactor));
} else {
return (strtoll(sizestring.c_str(), 0, 10) * convfactor);
}
}
//------------------------------------------------------------------------------
// Convert a readable string into a number
//------------------------------------------------------------------------------
unsigned long long
StringConversion::GetDataSizeFromString(const char* instring)
{
if (!instring) {
errno = EINVAL;
return 0ll;
}
XrdOucString sizestring = instring;
unsigned long long convfactor = 1ll;
errno = 0;
if (!sizestring.length()) {
errno = EINVAL;
return 0ll;
} else {
if (sizestring.endswith("B") || sizestring.endswith("b")) {
sizestring.erase(sizestring.length() - 1);
}
if (sizestring.endswith("E") || sizestring.endswith("e")) {
convfactor = 1000ll * 1000ll * 1000ll * 1000ll * 1000ll * 1000ll;
} else if (sizestring.endswith("P") || sizestring.endswith("p")) {
convfactor = 1000ll * 1000ll * 1000ll * 1000ll * 1000ll;
} else if (sizestring.endswith("T") || sizestring.endswith("t")) {
convfactor = 1000ll * 1000ll * 1000ll * 1000ll;
} else if (sizestring.endswith("G") || sizestring.endswith("g")) {
convfactor = 1000ll * 1000ll * 1000ll;
} else if (sizestring.endswith("M") || sizestring.endswith("m")) {
convfactor = 1000ll * 1000ll;
} else if (sizestring.endswith("K") || sizestring.endswith("k")) {
convfactor = 1000ll;
}
}
if (convfactor > 1ll) {
sizestring.erase(sizestring.length() - 1);
}
if ((sizestring.find(".")) != STR_NPOS) {
return ((unsigned long long)(strtod(sizestring.c_str(), NULL) * convfactor));
} else {
return (strtoll(sizestring.c_str(), 0, 10) * convfactor);
}
}
//------------------------------------------------------------------------------
// Split a 'key:value' definition into key + value
//------------------------------------------------------------------------------
bool
StringConversion::SplitKeyValue(std::string keyval, std::string& key,
std::string& value, std::string split)
{
auto equalpos = keyval.find(split);
if (equalpos != std::string::npos) {
key.assign(keyval, 0, equalpos);
value.assign(keyval, equalpos + 1, keyval.length() - (equalpos + 1));
return true;
} else {
key = value = "";
return false;
}
}
//------------------------------------------------------------------------------
// Split a 'key:value' definition into key + value
//------------------------------------------------------------------------------
bool
StringConversion::SplitKeyValue(XrdOucString keyval, XrdOucString& key,
XrdOucString& value, XrdOucString split)
{
int equalpos = keyval.find(split.c_str());
if (equalpos != STR_NPOS) {
key.assign(keyval, 0, equalpos - 1);
value.assign(keyval, equalpos + 1);
return true;
} else {
key = value = "";
return false;
}
}
//------------------------------------------------------------------------------
// Split a comma separated key:val list and fill it into a map
//------------------------------------------------------------------------------
bool
StringConversion::GetKeyValueMap(const char* mapstring,
std::map& map,
const char* split,
const char* sdelimiter,
std::vector* keyvector)
{
if (!mapstring) {
return false;
}
std::string is = mapstring;
std::string delimiter = sdelimiter;
std::vector slist;
Tokenize(is, slist, delimiter);
if (!slist.size()) {
return false;
}
size_t keyvectorindex = 0;
for (auto it = slist.begin(); it != slist.end(); it++) {
std::string key;
std::string val;
if (SplitKeyValue(*it, key, val, split)) {
if (keyvector && !map.count(key)) {
if (std::find(keyvector->begin(), keyvector->end(), key) == keyvector->end()) {
std::vector::iterator it = keyvector->begin();
std::advance(it, keyvectorindex);
keyvector->insert(it, key);
}
}
keyvectorindex++;
map[key] = val;
} else {
return false;
}
}
return true;
}
//------------------------------------------------------------------------------
// Specialized splitting function returning the host part out of a queue name
//------------------------------------------------------------------------------
XrdOucString
StringConversion::GetHostPortFromQueue(const char* queue)
{
XrdOucString hostport = queue;
int pos = hostport.find("/", 2);
if (pos != STR_NPOS) {
hostport.erase(0, pos + 1);
pos = hostport.find("/");
if (pos != STR_NPOS) {
hostport.erase(pos);
}
}
return hostport;
}
//------------------------------------------------------------------------------
// Specialized splitting function returning the host:port part out of a queue name
//------------------------------------------------------------------------------
std::string
StringConversion::GetStringHostPortFromQueue(const char* queue)
{
std::string hostport = queue;
int pos = hostport.find("/", 2);
if (pos != STR_NPOS) {
hostport.erase(0, pos + 1);
pos = hostport.find("/");
if (pos != STR_NPOS) {
hostport.erase(pos);
}
}
return hostport;
}
//------------------------------------------------------------------------------
// Split 'a.b' into a and b
//------------------------------------------------------------------------------
void
StringConversion::SplitByPoint(std::string in, std::string& pre,
std::string& post)
{
std::string::size_type dpos = 0;
pre = in;
post = in;
if ((dpos = in.find(".")) != std::string::npos) {
std::string s = in;
post.erase(0, dpos + 1);
pre.erase(dpos);
} else {
post = "";
}
}
//------------------------------------------------------------------------------
// Load a text file into a string
//------------------------------------------------------------------------------
const char*
StringConversion::LoadFileIntoString(const char* filename, std::string& out)
{
std::ifstream load(filename);
std::stringstream buffer;
buffer << load.rdbuf();
out = buffer.str();
return out.c_str();
}
// ---------------------------------------------------------------------------
// Save a string into a text file
// ---------------------------------------------------------------------------
bool
StringConversion::SaveStringIntoFile(const char* filename,
const std::string& in)
{
std::ofstream save(filename);
save.write(in.c_str(), in.size());
return true;
}
//------------------------------------------------------------------------------
// Read a long long number as output of a shell command - this is not usefull
// in multi-threaded environments.
//------------------------------------------------------------------------------
long long
StringConversion::LongLongFromShellCmd(const char* shellcommand)
{
FILE* fd = popen(shellcommand, "r");
if (fd) {
char buffer[1025];
buffer[0] = 0;
int nread = fread((void*) buffer, 1, 1024, fd);
pclose(fd);
if ((nread > 0) && (nread < 1024)) {
buffer[nread] = 0;
return strtoll(buffer, 0, 10);
}
}
return LLONG_MAX;
}
//------------------------------------------------------------------------------
// Read a string as output of a shell command - this is not usefull in
// multi-threaded environments.
//------------------------------------------------------------------------------
std::string
StringConversion::StringFromShellCmd(const char* shellcommand)
{
FILE* fd = popen(shellcommand, "r");
std::string shellstring;
if (fd) {
char buffer[1025];
buffer[0] = 0;
int nread = 0;
while ((nread = fread((void*) buffer, 1, 1024, fd)) > 0) {
buffer[nread] = 0;
shellstring += buffer;
if (nread != 1024) {
break;
}
}
pclose(fd);
return shellstring;
}
return "";
}
//------------------------------------------------------------------------------
// Return the time as . in a string
//------------------------------------------------------------------------------
const char*
StringConversion::TimeNowAsString(XrdOucString& stime)
{
struct timespec ts;
eos::common::Timing::GetTimeSpec(ts);
char tb[128];
snprintf(tb, sizeof(tb) - 1, "%lu.%lu", ts.tv_sec, ts.tv_nsec);
stime = tb;
return stime.c_str();
}
//------------------------------------------------------------------------------
// Mask a tag 'key=val' as 'key=<...>' in an opaque string
//------------------------------------------------------------------------------
const char*
StringConversion::MaskTag(XrdOucString& line, const char* tag)
{
XrdOucString smask = tag;
smask += "=";
int spos = line.find(smask.c_str());
int epos = line.find("&", spos + 1);
if (spos != STR_NPOS) {
if (epos != STR_NPOS) {
line.erase(spos, epos - spos);
} else {
line.erase(spos);
}
smask += "<...>";
line.insert(smask.c_str(), spos);
}
return line.c_str();
}
//------------------------------------------------------------------------------
// Parse a string as an URL (does not deal with opaque information)
//------------------------------------------------------------------------------
const char*
StringConversion::ParseUrl(const char* url, XrdOucString& protocol,
XrdOucString& hostport)
{
protocol = url;
hostport = url;
int ppos = protocol.find(":/");
if (ppos != STR_NPOS) {
protocol.erase(ppos);
} else {
if (protocol.beginswith("as3:")) {
protocol = "as3";
} else {
protocol = "file";
}
}
if (protocol == "file") {
if (hostport.beginswith("file:")) {
hostport = "";
return (url + 5);
} else {
hostport = "";
return url;
}
}
if (protocol == "root") {
int spos = hostport.find("//", ppos + 2);
if (spos == STR_NPOS) {
return 0;
}
hostport.erase(spos);
hostport.erase(0, 7);
return (url + spos + 1);
}
if (protocol == "as3") {
if (hostport.beginswith("as3://")) {
// as3://// like in ROOT
int spos = hostport.find("/", 6);
if (spos != STR_NPOS) {
hostport.erase(spos);
hostport.erase(0, 6);
return (url + spos + 1);
} else {
return 0;
}
} else {
// as3:/
hostport = "";
return (url + 4);
}
}
if (protocol == "http") {
// http://
int spos = hostport.find("/", 7);
if (spos == STR_NPOS) {
return 0;
}
hostport.erase(spos);
hostport.erase(0, 7);
return (url + spos);
}
if (protocol == "https") {
// https://
int spos = hostport.find("/", 8);
if (spos == STR_NPOS) {
return 0;
}
hostport.erase(spos);
hostport.erase(0, 8);
return (url + spos);
}
if (protocol == "gsiftp") {
// gsiftp://
int spos = hostport.find("/", 9);
if (spos == STR_NPOS) {
return 0;
}
hostport.erase(spos);
hostport.erase(0, 9);
return (url + spos);
}
return 0;
}
//------------------------------------------------------------------------------
// Create an Url
//------------------------------------------------------------------------------
const char*
StringConversion::CreateUrl(const char* protocol, const char* hostport,
const char* path, XrdOucString& url)
{
if (!strcmp(protocol, "file")) {
url = path;
return url.c_str();
}
if (!strcmp(protocol, "root")) {
url = "root://";
url += hostport;
url += "/";
url += path;
return url.c_str();
}
if (!strcmp(protocol, "as3")) {
if (hostport && strlen(hostport)) {
url = "as3://";
url += hostport;
url += path;
return url.c_str();
} else {
url = "as3:";
url += path;
return url.c_str();
}
}
if (!strcmp(protocol, "http")) {
url = "http://";
url += hostport;
url += path;
return url.c_str();
}
if (!strcmp(protocol, "gsiftp")) {
url = "gsiftp://";
url += hostport;
url += path;
return url.c_str();
}
url = "";
return 0;
}
//------------------------------------------------------------------------------
// Check if string is hex number
//------------------------------------------------------------------------------
bool
StringConversion::IsHexNumber(const char* hexstring, const char* format)
{
if (!hexstring) {
return false;
}
unsigned long long number = strtoull(hexstring, 0, 16);
char controlstring[256];
snprintf(controlstring, sizeof(controlstring) - 1, format, number);
return !strcmp(hexstring, controlstring);
}
//------------------------------------------------------------------------------
// Check if string is a decimal number
//------------------------------------------------------------------------------
bool
StringConversion::IsDecimalNumber(const std::string& str)
{
return !str.empty() && std::all_of(str.begin(), str.end(), ::isdigit);
}
//------------------------------------------------------------------------------
// Convert numeric value to string in a pretty way using KB, MB etc. symbols
//------------------------------------------------------------------------------
std::string
StringConversion::GetPrettySize(float size)
{
float fsize = 0;
std::string ret_str;
std::string size_unit;
if ((fsize = size / EB) >= 1) {
size_unit = "EB";
} else if ((fsize = size / PB) >= 1) {
size_unit = "PB";
} else if ((fsize = size / TB) >= 1) {
size_unit = "TB";
} else if ((fsize = size / MB) >= 1) {
size_unit = "MB";
} else {
fsize = size / KB;
size_unit = "KB";
}
char msg[30];
sprintf(msg, "%.1f %s", fsize, size_unit.c_str());
ret_str = msg;
return ret_str;
}
//------------------------------------------------------------------------------
// CURL init/cleanup using local storage
//------------------------------------------------------------------------------
void StringConversion::tlCurlFree(void* arg)
{
eos_static_debug("destroying thread specific CURL session");
// delete the buffer
curl_easy_cleanup((CURL*)arg);
}
CURL* StringConversion::tlCurlInit()
{
eos_static_debug("allocating thread specific CURL session");
CURL* buf = curl_easy_init();
if (!buf) {
eos_static_crit("error initialising CURL easy session");
}
if (buf && pthread_setspecific(sPthreadKey, buf)) {
eos_static_crit("error registering thread-local buffer located at %p for "
"cleaning up : memory will be leaked when thread is "
"terminated", buf);
}
return buf;
}
void StringConversion::tlInitThreadKey()
{
pthread_key_create(&sPthreadKey, StringConversion::tlCurlFree);
}
thread_local CURL* StringConversion::curl = NULL;
pthread_key_t StringConversion::sPthreadKey;
pthread_once_t StringConversion::sInit = PTHREAD_ONCE_INIT;
//------------------------------------------------------------------------------
// Escape string using CURL
//------------------------------------------------------------------------------
std::string
StringConversion::curl_default_escaped(const std::string& str)
{
pthread_once(&sInit, tlInitThreadKey);
std::string ret_str;
// encode the key
if (!curl) {
curl = tlCurlInit();
}
if (curl) {
char* output = curl_easy_escape(curl, str.c_str(), str.length());
if (output) {
ret_str = output;
curl_free(output);
}
}
return ret_str;
}
//------------------------------------------------------------------------------
// Escape path using CURL
//------------------------------------------------------------------------------
std::string
StringConversion::curl_path_escaped(const std::string& str)
{
XrdOucString escaped = curl_default_escaped(str).c_str();
while(escaped.replace("%2F","/")){}
return std::string(escaped.c_str());
}
//------------------------------------------------------------------------------
// Unescape string using CURL
//------------------------------------------------------------------------------
std::string
StringConversion::curl_default_unescaped(const std::string& str)
{
pthread_once(&sInit, tlInitThreadKey);
std::string ret_str;
// encode the key
if (!curl) {
curl = tlCurlInit();
}
if (curl) {
char* output = curl_easy_unescape(curl, str.c_str(), str.length(), 0);
if (output) {
ret_str = output;
curl_free(output);
}
}
return ret_str;
}
//------------------------------------------------------------------------------
// Escape string using CURL and add speical /#curl# prefix to the string
//------------------------------------------------------------------------------
std::string
StringConversion::curl_escaped(const std::string& str)
{
pthread_once(&sInit, tlInitThreadKey);
std::string ret_str = "";
// encode the key
if (!curl) {
curl = tlCurlInit();
}
if (curl) {
char* output = curl_easy_escape(curl, str.c_str(), str.length());
if (output) {
ret_str = output;
curl_free(output);
// don't escape '/'
XrdOucString no_slash = ret_str.c_str();
while (no_slash.replace("%2F", "/")) {}
// this is a hack to avoid decoding a pathname twice
no_slash.insert("/#curl#", 0);
ret_str = no_slash.c_str();
}
}
return ret_str;
}
//------------------------------------------------------------------------------
// Unescape string using CURL that contains special /#curl# prefix
//------------------------------------------------------------------------------
std::string
StringConversion::curl_unescaped(const std::string& str)
{
pthread_once(&sInit, tlInitThreadKey);
std::string ret_str = "";
// encode the key
if (!curl) {
curl = tlCurlInit();
}
if (curl) {
if (strncmp(str.c_str(), "/#curl#", 7)) {
// the string has already been decoded
return str;
}
char* output = curl_easy_unescape(curl, str.c_str() + 7, str.length() - 7, 0);
if (output) {
ret_str = output;
curl_free(output);
}
}
return ret_str;
}
// ---------------------------------------------------------------------------
// Escape JSON string
// ---------------------------------------------------------------------------
std::string
StringConversion::json_encode(const std::string& s)
{
std::string output;
output.reserve(s.length());
for (size_t i = 0; i != s.length(); i++) {
char c = s.at(i);
switch (c) {
case '"':
output += "\\\"";
break;
case '/':
output += "\\/";
break;
case '\b':
output += "\\b";
break;
case '\f':
output += "\\f";
break;
case '\n':
output += "\\n";
break;
case '\r':
output += "\\r";
break;
case '\t':
output += "\\t";
break;
case '\\':
output += "\\\\";
break;
default:
output += c;
break;
}
}
return output;
}
//------------------------------------------------------------------------------
// Create random uuid string
//------------------------------------------------------------------------------
std::string
StringConversion::random_uuidstring()
{
char id[40];
uuid_t uuid;
uuid_generate_time(uuid);
uuid_unparse(uuid, id);
return id;
}
//------------------------------------------------------------------------------
// Create time-based uuid string
//------------------------------------------------------------------------------
std::string StringConversion::timebased_uuidstring()
{
uuid_t uuid;
//The uuid_unparse function converts the supplied UUID from the binary representation into a 36-byte string + '\0' trailing
char uuid_str[37];
uuid_generate_time(uuid);
uuid_unparse(uuid, uuid_str);
return std::string(uuid_str);
}
//------------------------------------------------------------------------------
// Sort lines alphabetically in-place
//------------------------------------------------------------------------------
void
StringConversion::SortLines(XrdOucString& data)
{
XrdOucString sorts = "";
std::vector vec;
XrdOucTokenizer linizer((char*)data.c_str());
char* val = 0;
while ((val = linizer.GetLine())) {
vec.push_back(val);
}
std::sort(vec.begin(), vec.end());
for (unsigned int i = 0; i < vec.size(); ++i) {
sorts += vec[i].c_str();
sorts += "\n";
}
data = sorts;
}
//------------------------------------------------------------------------------
// Check if a string is valid UTF
//------------------------------------------------------------------------------
bool
StringConversion::Valid_UTF8(const string& string)
{
int c, i, ix, n, j;
for (i = 0, ix = string.length(); i < ix; i++) {
c = (unsigned char) string[i];
//if (c==0x09 || c==0x0a || c==0x0d || (0x20 <= c && c <= 0x7e) ) n = 0; // is_printable_ascii
if (0x00 <= c && c <= 0x7f) {
n = 0; // 0bbbbbbb
} else if ((c & 0xE0) == 0xC0) {
n = 1; // 110bbbbb
} else if (c == 0xed && i < (ix - 1) &&
((unsigned char)string[i + 1] & 0xa0) == 0xa0) {
return false; //U+d800 to U+dfff
} else if ((c & 0xF0) == 0xE0) {
n = 2; // 1110bbbb
} else if ((c & 0xF8) == 0xF0) {
n = 3; // 11110bbb
}
//else if (($c & 0xFC) == 0xF8) n=4; // 111110bb //byte 5, unnecessary in 4 byte UTF-8
//else if (($c & 0xFE) == 0xFC) n=5; // 1111110b //byte 6, unnecessary in 4 byte UTF-8
else {
return false;
}
for (j = 0; j < n &&
i < ix; j++) { // n bytes matching 10bbbbbb follow ?
if ((++i == ix) || (((unsigned char)string[i] & 0xC0) != 0x80)) {
return false;
}
}
}
return true;
}
//------------------------------------------------------------------------------
// CURL encode invalid UTF strings with SHA256
//------------------------------------------------------------------------------
std::string
StringConversion::EncodeInvalidUTF8(const string& key)
{
if (!Valid_UTF8(key)) {
return curl_escaped(key);
} else {
return key;
}
}
//------------------------------------------------------------------------------
// Seal xrootd path i.e. replace any & with #AND#
//------------------------------------------------------------------------------
std::string
StringConversion::SealXrdPath(const std::string& input)
{
std::string sealed(input);
ReplaceStringInPlace(sealed, "&", "#AND#");
return sealed;
}
//------------------------------------------------------------------------------
// Unseal xrootd path i.e. replace any #AND# with &
//------------------------------------------------------------------------------
std::string
StringConversion::UnsealXrdPath(const std::string& input)
{
std::string unsealed(input);
ReplaceStringInPlace(unsealed, "#AND#", "&");
return unsealed;
}
//------------------------------------------------------------------------------
// Seal xrootd path in place i.e. replace any & with #AND#
//------------------------------------------------------------------------------
const char*
StringConversion::SealXrdPath(XrdOucString& input)
{
const std::string seal = "#AND#";
while (input.replace("&", seal.c_str())) {};
return input.c_str();
}
//------------------------------------------------------------------------------
// Unseal xrootd path in place i.e. replace any #AND# with &
//------------------------------------------------------------------------------
const char*
StringConversion::UnsealXrdPath(XrdOucString& input)
{
const std::string seal = "#AND#";
while (input.replace(seal.c_str(), "&")) {};
return input.c_str();
}
EOSCOMMONNAMESPACE_END