commit 66f0de49702fa4a468fd507bc6f5b1b3957eeee0 Author: Bob Polis Date: Mon Mar 16 15:16:35 2020 +0100 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c156b3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +libscstring.hpp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..63ad5d1 --- /dev/null +++ b/Makefile @@ -0,0 +1,80 @@ +LIBNAME := $(shell basename $$(pwd)) +MAJOR := 1 +MINOR := 0.0 + +UNAME_S := $(shell uname -s) + +ifeq ($(UNAME_S),Darwin) + LINKERNAME := $(LIBNAME).dylib + SONAME := $(LIBNAME).$(MAJOR).dylib + REALNAME := $(LINKERNAME) +else + LINKERNAME := $(LIBNAME).so + SONAME := $(LINKERNAME).$(MAJOR) + REALNAME := $(SONAME).$(MINOR) +endif + +PREFIX ?= /usr/local +BINDIR ?= $(PREFIX)/bin +LIBDIR ?= $(PREFIX)/lib +INCLUDEDIR ?= $(PREFIX)/include +DATADIR ?= $(PREFIX)/share +MANDIR ?= $(DATADIR)/man + +SRCS := $(wildcard *.cpp) +OBJS := $(subst .cpp,.o,$(SRCS)) +DEPS := $(subst .cpp,.d,$(SRCS)) +HDRS := $(filter-out $(LIBNAME).hpp,$(wildcard *.hpp)) + +CXX ?= g++ + +CXXFLAGS := $(CXXFLAGS) -Wshadow -Wall -Wpedantic -Wextra -g -fno-strict-aliasing -std=c++17 -fPIC +ifeq ($(DEBUG),1) + CXXFLAGS += -D DEBUG -O0 +else + CXXFLAGS += -D NDEBUG -O3 +endif + +LDLIBS := -lscerror + +RM := /bin/rm -f +INSTALL := /usr/bin/install -c + +all: $(REALNAME) + +$(REALNAME): $(OBJS) $(DEPS) +ifeq ($(UNAME_S),Darwin) + $(CXX) -dynamiclib -o $(REALNAME) -current_version $(MAJOR) -compatibility_version $(MINOR) $(LDFLAGS) $(LDLIBS) $(OBJS) +else + $(CXX) -g -shared -Wl,-soname,$(SONAME) -o $(REALNAME) $(LDFLAGS) $(LDLIBS) $(OBJS) +endif + +%.o: %.cpp %.d Makefile + $(CXX) $(CXXFLAGS) -MMD -MP -MT $@ -MF $*.d -c $< + +-include *.d + +%.d: ; + +$(LIBNAME).hpp: $(HDRS) + @echo updating $(LIBNAME).hpp + @cp /dev/null $(LIBNAME).hpp + @for h in $(HDRS); \ + do \ + cat $$h >> $(LIBNAME).hpp; \ + done + +.PHONY: clean install + +clean: + $(RM) $(OBJS) $(DEPS) $(REALNAME) $(LIBNAME).hpp + +install: $(REALNAME) $(LIBNAME).hpp + $(INSTALL) -m 644 $(REALNAME) $(LIBDIR) + $(INSTALL) -m 644 $(LIBNAME).hpp $(INCLUDEDIR) +ifeq ($(UNAME_S),Darwin) + cd $(LIBDIR) && ln -sf $(REALNAME) $(SONAME) +else + ldconfig + cd $(LIBDIR) && ln -sf $(SONAME) $(LINKERNAME) +endif diff --git a/string_utils.cpp b/string_utils.cpp new file mode 100644 index 0000000..42f04b9 --- /dev/null +++ b/string_utils.cpp @@ -0,0 +1,261 @@ +/* + * string_utils.cpp + * + * Created by Bob Polis on 14-11-2014. + * Copyright 2014 Thalictrum. All rights reserved. + * + */ + +#include "string_utils.hpp" +#include +#include +#include +#include +#include +#include +using namespace std; + +vector sc::split(const string& str, const string& sep) +{ + vector components; + string::size_type start = 0; + string::size_type pos = str.find(sep); + while (pos != string::npos) { // found separator => add substring to vector + components.push_back(str.substr(start, pos - start)); + start = pos + sep.length(); // next search starts just after found separator + pos = str.find(sep, start); + } + // at end of string => add last component + components.push_back(str.substr(start, str.length() - start)); + return components; +} + +vector sc::split(const string& str, const regex& sep) { + vector components; + sregex_token_iterator end {}; + for (sregex_token_iterator p {str.begin(), str.end(), sep, -1}; p != end; ++p) { + components.push_back(*p); + } + return components; +} + +string sc::join(const vector& components, const string& join) +{ + string result; + for (vector::const_iterator i = components.cbegin(); i != components.cend(); ++i) { + if (i != components.cbegin()) { + result += join; + } + result += *i; + } + return result; +} + +string sc::trim(const string& str, const string& del) +{ + string result { str }; + string::size_type pos {0}; + + // erase leading chars that occur in del + while (result.size() && (pos = result.find_first_of(del)) == 0) { + result.erase(pos, 1); + } + + // erase trailing chars that occur in del + while (result.size() && (pos = result.find_last_of(del)) == result.size() - 1) { + result.erase(pos); + } + return result; +} + +bool sc::file_exists(const string& path) +{ + struct stat st; + return !(::stat(path.c_str(), &st) == -1 && errno == ENOENT); +} + +string sc::file_get_contents(const string& path) +{ + ifstream file {path}; + file.exceptions(ios::failbit | ios::badbit); + file.seekg(0, ios::end); + ios::pos_type file_len {file.tellg()}; + file.seekg(0); + vector buf; + buf.resize(file_len); + file.read(buf.data(), file_len); + return {buf.data(), static_cast(file_len)}; +} + +map sc::parse_ini_file(const string& path) +{ + map result; + string line; + ifstream file {path}; + file.exceptions(/*ios::failbit |*/ ios::badbit); // it seems that getline() will set failbit when confronted with eof immediately + while (getline(file, line)) { + if (line[0] == '[') continue; + vector parts {split(line, "=")}; + if (parts.size() > 1) { + string key {trim(parts[0])}; + string value {trim(parts[1], " \"")}; + result[key] = value; + } + } + return result; +} + +string sc::str_replace(const string& what, const string& replacement, const string& target) +{ + string result; + string::size_type pos {0}; + string::size_type from {0}; + while ((pos = target.find(what, from)) != string::npos) { + result += target.substr(from, pos - from); + result += replacement; + from = pos + what.length(); + if (from >= target.length()) break; + } + result += target.substr(from); + return result; +} + +wstring sc::replace_all(const std::wstring& what, + const std::wstring& replacement, + const std::wstring& target) +{ + wstring term {target}; + wstring::size_type pos = wstring::npos; + wstring::size_type from = 0; + do { + pos = term.find(what, from); + if (pos != wstring::npos) { + term.replace(pos, what.length(), replacement); + from = pos + replacement.length(); + } + } while (pos != wstring::npos); + return term; +} + +void sc::create_dir(const std::string &path, int mode) +{ + string dir; + vector path_components {sc::split(path, "/")}; + for (string comp : path_components) { + dir += comp + '/'; + if (!sc::file_exists(dir)) { + throw_if_min1(::mkdir(dir.c_str(), mode)); + } + } +} + +string sc::dirname(const string& path) { + string result {path}; + vector buf; + buf.resize(PATH_MAX); + if (::realpath(path.c_str(), buf.data())) { + result = buf.data(); + } + auto pos = result.rfind("/"); + if (pos != string::npos) { + return result.substr(0, pos); + } + return result; +} + +string sc::basename(const string& path, bool remove_extension) { + string result {path}; + if (remove_extension) { + auto dot = path.rfind("."); + if (dot != string::npos) { + result = result.substr(0, dot); + } + } + auto pos = result.rfind("/"); + if (pos != string::npos) { + return result.substr(pos + 1); + } + return result; +} + +string sc::replace_tilde(const string& path) { + string result {path}; + auto pos = result.find("~"); + if (pos != string::npos) { + string home {::getenv("HOME")}; + result.replace(pos, pos + 1, home); + } + return result; +} + +string sc::filename_extension(const string& path) { + auto pos = path.rfind("."); + if (pos != string::npos) { + return path.substr(pos); + } + return ""; +} + +string sc::tool_path(const string& name) { + if (name.find('/') != string::npos) { // name has (at least one) slash + vector buf; + buf.resize(PATH_MAX); + if (::realpath(name.c_str(), buf.data())) { + return string(buf.data()); + } + } else { // no slash in name => command from PATH? + string envpath {::getenv("PATH")}; + vector paths {sc::split(envpath, ":")}; + for (const string& elem : paths) { + string path {elem + '/' + name}; + if (sc::file_exists(path)) { + return path; + } + } + } + return ""; +} + +string sc::truncate(const string& str, unsigned int maxlen, int /*how*/) { + if (maxlen > str.length()) return str; + return str.substr(0, maxlen - 1) + "\u2026"; // add ellipsis +} + +string sc::lowercase(const string& str, const locale& loc) { + string result; + for (const char c : str) { + result += tolower(c, loc); + } + return result; +} + +string sc::uppercase(const string& str, const locale& loc) { + string result; + for (const char c : str) { + result += toupper(c, loc); + } + return result; +} + +bool sc::is_valid_utf8(const string& str) { + // From: http://www.zedwood.com/article/cpp-is-valid-utf8-string-function + int c,i,ix,n,j; + for (i=0, ix=str.length(); i < ix; i++) + { + c = (unsigned char) str[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)str[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 +#include +#include +#include + +namespace sc { + + std::vector split (const std::string& str, + const std::string& sep); + + std::vector split (const std::string& str, + const std::regex& sep); + + std::string join (const std::vector& components, + const std::string& join); + + std::string trim (const std::string& str, + const std::string& del = " "); + + std::string str_replace (const std::string& what, + const std::string& replacement, + const std::string& target); + + std::wstring replace_all (const std::wstring& what, + const std::wstring& replacement, + const std::wstring& target); + + bool file_exists (const std::string& path); + + std::string file_get_contents (const std::string& path); + + std::map parse_ini_file (const std::string& path); + + void create_dir (const std::string& path, + int mode = 0777); + + std::string dirname (const std::string& path); + + std::string basename (const std::string& path, bool remove_extension = false); + + std::string replace_tilde (const std::string& path); + + std::string filename_extension (const std::string& path); + + std::string tool_path (const std::string& name); + + std::string truncate (const std::string& str, unsigned int maxlen, int how = 0); + + std::string lowercase (const std::string& str, const std::locale& loc); + + std::string uppercase (const std::string& str, const std::locale& loc); + + bool is_valid_utf8 (const std::string& str); + +} + +#endif // _string_utils_