// -*- C++ -*- #ifndef WIBBLE_STRING_H #define WIBBLE_STRING_H /* * Various string functions * * Copyright (C) 2007,2008 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // modifications by DIVINE team: // (c) 2014 Vladimír Štill #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #include #endif namespace wibble { namespace str { using namespace wibble::operators; #ifdef _WIN32 static int vasprintf (char **, const char *, va_list); #endif std::string fmtf( const char* f, ... ); template< typename T > inline std::string fmt(const T& val); // Formatting lists -- actually, we need to move list handling into wibble, // really. template< typename X > inline typename TPair< std::ostream, typename X::Type >::First &operator<<( std::ostream &o, X list ) { if ( list.empty() ) return o << "[]"; o << "[ "; while( !list.empty() ) { o << fmt( list.head() ); if ( !list.tail().empty() ) o << ", "; list = list.tail(); } return o << " ]"; } /// Format any value into a string using a std::stringstream template< typename T > inline std::string fmt(const T& val) { std::stringstream str; str << val; return str.str(); } template<> inline std::string fmt(const std::string& val) { return val; } template<> inline std::string fmt(char * const & val) { return val; } template< typename C > inline std::string fmt_container( const C &c, char f, char l ) { std::string s; s += f; if ( c.empty() ) return s + l; s += ' '; for ( typename C::const_iterator i = c.begin(); i != c.end(); ++i ) { s += fmt( *i ); if ( i != c.end() && i + 1 != c.end() ) s += ", "; } s += ' '; s += l; return s; } // formatting sets using { ... } notation template< typename X > inline std::string fmt(const std::set< X >& val) { return fmt_container( val, '{', '}' ); } // formatting vectors using [ ... ] notation template< typename X > inline std::string fmt(const std::vector< X > &val) { return fmt_container( val, '[', ']' ); } // formatting vectors using [ ... ] notation template< typename X > inline std::string fmt(const std::deque< X > &val) { return fmt_container( val, '[', ']' ); } /// Given a pathname, return the file name without its path inline std::string basename(const std::string& pathname) { size_t pos = pathname.rfind("/"); if (pos == std::string::npos) return pathname; else return pathname.substr(pos+1); } /// Given a pathname, return the directory name without the file name inline std::string dirname(const std::string& pathname) { size_t pos = pathname.rfind("/"); if (pos == std::string::npos) return std::string(); else if (pos == 0) // Handle the case of '/foo' return std::string("/"); else return pathname.substr(0, pos); } /** * Normalise a pathname. * * For example, A//B, A/./B and A/foo/../B all become A/B. */ std::string normpath( std::string pathname ); /// Check if a string starts with the given substring inline bool startsWith(const std::string& str, const std::string& part) { if (str.size() < part.size()) return false; return str.substr(0, part.size()) == part; } /// Check if a string ends with the given substring inline bool endsWith(const std::string& str, const std::string& part) { if (str.size() < part.size()) return false; return str.substr(str.size() - part.size()) == part; } inline std::string replace(const std::string& str, char from, char to) { std::string res; res.reserve(str.size()); for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) if (*i == from) res.append(1, to); else res.append(1, *i); return res; } #if !__xlC__ && (! __GNUC__ || __GNUC__ >= 4) /** * Return the substring of 'str' without all leading and trailing characters * for which 'classifier' returns true. */ template inline std::string trim(const std::string& str, const FUN& classifier) { if (str.empty()) return str; size_t beg = 0; size_t end = str.size() - 1; while (beg < end && classifier(str[beg])) ++beg; while (end >= beg && classifier(str[end])) --end; return str.substr(beg, end-beg+1); } /** * Return the substring of 'str' without all leading and trailing spaces. */ inline std::string trim(const std::string& str) { return trim(str, ::isspace); } #else /// Workaround version for older gcc inline std::string trim(const std::string& str) { if (str.empty()) return str; size_t beg = 0; size_t end = str.size() - 1; while (beg < end && ::isspace(str[beg])) ++beg; while (end >= beg && ::isspace(str[end])) --end; return str.substr(beg, end-beg+1); } #endif /// Convert a string to uppercase inline std::string toupper(const std::string& str) { std::string res; res.reserve(str.size()); for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) res += ::toupper(*i); return res; } /// Convert a string to lowercase inline std::string tolower(const std::string& str) { std::string res; res.reserve(str.size()); for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) res += ::tolower(*i); return res; } /// Return the same string, with the first character uppercased inline std::string ucfirst(const std::string& str) { if (str.empty()) return str; std::string res; res += ::toupper(str[0]); return res + tolower(str.substr(1)); } // sorted by priority #ifdef POSIX const char pathSeparators[] = { '/' }; #endif #ifdef WIN32 const char pathSeparators[] = { '\\', '/' }; #endif inline bool isPathSeparator( char ch ) { for ( int i = 0; i < int( sizeof( pathSeparators ) ); ++i ) if ( ch == pathSeparators[ i ] ) return true; return false; } /// Join two paths, adding slashes when appropriate inline std::string joinpath(const std::string& path1, const std::string& path2) { if (path1.empty()) return path2; if (path2.empty()) return path1; if ( isPathSeparator( path1[path1.size() - 1] ) ) if ( isPathSeparator( path2[0] ) ) return path1 + path2.substr(1); else return path1 + path2; else if ( isPathSeparator( path2[0] ) ) return path1 + path2; else return path1 + pathSeparators[ 0 ] + path2; } inline std::pair< std::string, std::string > absolutePrefix( const std::string &path ) { #ifdef WIN32 /* this must go before general case, because \ is prefix of \\ */ if ( path.size() >= 3 && path[ 1 ] == ':' && isPathSeparator( path[ 2 ] ) ) return std::make_pair( path.substr( 0, 3 ), path.substr( 3 ) ); if ( path.size() >= 2 && isPathSeparator( path[ 0 ] ) && isPathSeparator( path[ 1 ] ) ) return std::make_pair( path.substr( 0, 2 ), path.substr( 2 ) ); #endif // this is absolute path in both windows and unix if ( path.size() >= 1 && isPathSeparator( path[ 0 ] ) ) return std::make_pair( path.substr( 0, 1 ), path.substr( 1 ) ); return std::make_pair( std::string(), path ); } inline bool isAbsolutePath( const std::string &path ) { return absolutePrefix( path ).first.size() != 0; } // append path2 to path1 if path2 is not absolute, otherwise return path2 inline std::string appendpath( const std::string &path1, const std::string &path2 ) { if ( isAbsolutePath( path2 ) ) return path2; return joinpath( path1, path2 ); } /// Urlencode a string std::string urlencode(const std::string& str); /// Decode an urlencoded string std::string urldecode(const std::string& str); /// Encode a string in Base64 std::string encodeBase64(const std::string& str); /// Decode a string encoded in Base64 std::string decodeBase64(const std::string& str); /** * Split a string where a given substring is found * * This does a similar work to the split functions of perl, python and ruby. * * Example code: * \code * str::Split splitter("/", myString); * vector split; * std::copy(splitter.begin(), splitter.end(), back_inserter(split)); * \endcode */ class Split { std::string sep; std::string str; public: class const_iterator : public std::iterator { const std::string& sep; const std::string& str; std::string cur; size_t pos; public: const_iterator(const std::string& sep, const std::string& str) : sep(sep), str(str), pos(0) { ++*this; } const_iterator(const std::string& sep, const std::string& str, bool) : sep(sep), str(str), pos(std::string::npos) {} const_iterator& operator++() { if (pos == str.size()) pos = std::string::npos; else { size_t end; if (sep.empty()) if (pos + 1 == str.size()) end = std::string::npos; else end = pos + 1; else end = str.find(sep, pos); if (end == std::string::npos) { cur = str.substr(pos); pos = str.size(); } else { cur = str.substr(pos, end-pos); pos = end + sep.size(); } } return *this; } std::string remainder() const { if (pos == std::string::npos) return std::string(); else return str.substr(pos); } const std::string& operator*() const { return cur; } const std::string* operator->() const { return &cur; } bool operator==(const const_iterator& ti) const { // Comparing iterators on different strings is not supported for // performance reasons return pos == ti.pos; } bool operator!=(const const_iterator& ti) const { // Comparing iterators on different strings is not supported for // performance reasons return pos != ti.pos; } }; /** * Create a splitter that uses the given regular expression to find tokens. */ Split(const std::string& sep, const std::string& str) : sep(sep), str(str) {} /** * Split the string and iterate the resulting tokens */ const_iterator begin() const { return const_iterator(sep, str); } const_iterator end() const { return const_iterator(sep, str, false); } }; template std::string join(const ITER& begin, const ITER& end, const std::string& sep = ", ") { std::stringstream res; bool first = true; for (ITER i = begin; i != end; ++i) { if (first) first = false; else res << sep; res << *i; } return res.str(); } /** * Parse a record of Yaml-style field: value couples. * * Parsing stops either at end of record (one empty line) or at end of file. * * The value is deindented properly. * * Example code: * \code * utils::YamlStream stream; * map record; * std::copy(stream.begin(inputstream), stream.end(), inserter(record)); * \endcode */ class YamlStream { public: // TODO: add iterator_traits class const_iterator { std::istream* in; std::pair value; std::string line; public: const_iterator(std::istream& in); const_iterator() : in(0) {} const_iterator& operator++(); const std::pair& operator*() const { return value; } const std::pair* operator->() const { return &value; } bool operator==(const const_iterator& ti) const { return in == ti.in; } bool operator!=(const const_iterator& ti) const { return in != ti.in; } }; const_iterator begin(std::istream& in) { return const_iterator(in); } const_iterator end() { return const_iterator(); } }; /** * Escape the string so it can safely used as a C string inside double quotes */ std::string c_escape(const std::string& str); /** * Unescape a C string, stopping at the first double quotes or at the end of * the string. * * lenParsed is set to the number of characters that were pased (which can be * greather than the size of the resulting string in case escapes were found) */ std::string c_unescape(const std::string& str, size_t& lenParsed); } } // _WIN32 std::to_string hack #if defined( _WIN32 ) \ && __GNUC__ == 4 && __GNUC_MINOR__ == 7 \ && __cplusplus >= 201103L #warning WIBBLE string defines std::to_string (which is missing on GCC < 4.8 on win) #include namespace std { static inline std::string to_string( int value ) { return wibble::str::fmt( value ); } static inline std::string to_string( long value ) { return wibble::str::fmt( value ); } static inline std::string to_string( long long value ) { return wibble::str::fmt( value ); } static inline std::string to_string( unsigned value ) { return wibble::str::fmt( value ); } static inline std::string to_string( unsigned long value ) { return wibble::str::fmt( value ); } static inline std::string to_string( unsigned long long value ) { return wibble::str::fmt( value ); } static inline std::string to_string( float value ) { return wibble::str::fmt( value ); } static inline std::string to_string( double value ) { return wibble::str::fmt( value ); } static inline std::string to_string( long double value ) { return wibble::str::fmt( value ); } static inline int stoi( const std::string& str, size_t *pos = 0, int base = 10 ) { char *end; long ret = std::strtol( str.c_str(), &end, base ); if ( end == str.c_str() ) throw std::invalid_argument( "stoi" ); if ( errno == ERANGE || long( int( ret ) ) != ret ) throw std::out_of_range( "stoi" ); if ( pos ) *pos = end - str.c_str(); return ret; }/* static inline long stol( const std::string& str, size_t *pos = 0, int base = 10 ); static inline long long stoll( const std::string& str, size_t *pos = 0, int base = 10 ); */ } #endif // _WIN32 std::to_string hack // vim:set ts=4 sw=4: #endif