#ifndef AHSTRINGUTILINC #define AHSTRINGUTILINC #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __GNUG__ #include // needed for abi::__cxa_demangle #endif /*! exception to be thrown by fatal() function */ struct AAFatalException : public std::exception { std::string s; AAFatalException(std::string ss) : s(ss) {} ~AAFatalException() throw () {} // Updated const char* what() const throw() { return s.c_str(); } }; extern char **environ; // must be in global namespace namespace stringutil { using std::cout; using std::cerr; using std::endl; using std::flush; using std::ios; using std::ostream; using std::istream; using std::fstream; using std::ifstream; using std::ofstream; using std::stringstream; using std::istringstream; using std::ostringstream; using std::map; using std::set; using std::vector; using std::string; // ================================= // Converting to and from anything // ================================= /*! \brief return true if c is a whitespace. */ inline bool is_whitespace(char c) { return isspace( c ); } /*! \brief remove whitespace from the ends. */ inline string trim(const string& s) { if ( s=="" ) return s; string::size_type i1; string::size_type i2; for (i1=0; i1< s.length(); i1++) { if ( !is_whitespace (s[i1]) ) break; } for (i2 = s.length()-1 ; i2 >i1 ; i2--) { if ( !is_whitespace (s[i2]) ) break; } return s.substr( i1, i2-i1+1 ); } /*! \brief sandwitch s in quotes */ inline string quote(string s, string quote_start ="\"", string quote_stop ="\"" ) { return quote_start+s+quote_stop; } /*! \brief return true if a starts with b */ inline bool startswith( const string a, const string b ) { if ( a.find( b ) == 0 ) return true; return false; } /*! \brief return true if a ends with b */ inline bool endswith( const string a, const string b ) { if (b.length() == 0 ) return true; auto i = a.find( b ); if ( i == string::npos ) return false; if ( i == a.length()-b.length() ) return true; return false; } /*! \brief return true if a contains b */ inline bool contains( string a, string b ) { if ( a.find( b ) != string::npos ) return true; return false; } /*! \brief simple hashing. */ inline unsigned int simple_hash( const char* s ) { // code googled somewhere unsigned hash = 0; while (*s) { hash = hash * 101 + *s++; } return hash; } /*! \brief simple hashing. */ inline unsigned int simple_hash( const string s ) { return simple_hash( s.c_str() ); } /*! \bfief split string on each occurence of delim. delim may not be an emtpy string (leads to exception). Subsequent deliminators lead to empty strings in the output.*/ inline vector split(const string &s, string delim = " ", int nsplits = -1) { if (delim == "") throw std::invalid_argument("empty separator."); // as in python if (nsplits==0) return {s}; int n = 0; string::size_type p1 = 0; string::size_type p2 = 0; vector r; if (s.length() == 0) return r; while (1) { p2 = s.find(delim, p1); if (p2 == string::npos) // not found, push back last bit { if (p1 != p2) { string ss; try { ss = s.substr(p1); } catch (const std::exception &e) { std::cout << " error in split (1)" << endl; std::cout << s << endl; cout << p1 << endl; cout << delim << endl; exit(1); } r.push_back(ss); } return r; } else // delim at p2 { string ss; try { ss = s.substr(p1, p2 - p1); } catch (const std::exception &e) { std::cout << " error in split (2)" << endl; std::cout << s << " " << p1 << " " << p2 << endl; } n++; r.push_back(ss); p1 = p2 + delim.length(); if (n == nsplits) { try { r.push_back(s.substr(p1)); } catch (const std::exception &e) { std::cout << " error in split (3)" << endl; std::cout << s << " " << p1 << " " << p2 << " " << delim << endl; } return r; } } } } /*! \brief split s on any character in delims */ inline vector split2(const string& s, const string delims = " ", int nsplits =-1) { // partly stolen from // http://www.linuxselfhelp.com/HOWTO/C++Programming-HOWTO-7.html vector r; // Skip delims at beginning. string::size_type lastPos = s.find_first_not_of(delims, 0); // Find first "non-delimiter". string::size_type pos = s.find_first_of(delims, lastPos); while (string::npos != pos || string::npos != lastPos) { // Found a token, add it to the vector. r.push_back(s.substr(lastPos, pos - lastPos)); // Skip delims. lastPos = s.find_first_not_of(delims, pos); // Find next "non-delimiter" pos = s.find_first_of(delims, lastPos); if ( int(r.size()) == nsplits ) // reached max number of splits { pos = string::npos; // pretend there are no more delimiters } } return r; } /*! \brief Replace all occurances of src by target in s. The function keeps starting over from left to right untill all occurences of src are gone. eg: replace('aaaaart','aa','a') = art */ inline string replace_all( string s, string src, string target) { while(1) { string::size_type ia = s.find( src ); if (ia == string::npos ) break; s.replace( ia, src.length(), target ); } return s; } // /*! \brief slice ala python */ // inline string slice( const string& in, int start = 0, int stop = -1 ) // { // if ( in == "" ) return ""; // string r; // int len = in.size(); // while (start < 0 ) start += len; // while (stop < 0 ) stop += len; // if (start > len ) start = len; // if (stop > len ) stop = len; // for (int i = start ; i <= stop ; i++) r.push_back( in[(unsigned) i] ) ; // return r; // } /*! \brief return the string between the left/right_end's. First occurunce of the left/right_end as found from the left/right is used. */ inline string get_substring(const string& in, const string& left_end, const string& right_end, bool include_ends = false ) { string::size_type i1; if (left_end=="") i1 =0; else i1 = in.find( left_end ); if (i1 == string::npos ) return ""; string::size_type i2; if (right_end=="") i2 = in.length(); else i2 = in.rfind( right_end ); if (i2 == string::npos) return ""; string::size_type l = i2-i1; if (left_end=="") return in.substr( 0, l ); if (include_ends) return in.substr( i1, l+right_end.length() ); else return in.substr( i1+left_end.length(), l-right_end.length() ); } /*! \brief convert string to lower case */ inline string lower(const string& s) { string r = s; std::transform(s.begin(), s.end(), r.begin(), ::tolower); return r; } /*! \brief convert string to upper case */ inline string upper(const string& s) { string r = s; std::transform(s.begin(), s.end(), r.begin(), ::toupper); return r; } /* \brief Return a string that is the input file, but all parts that follow comment_char are removed, until the end of each line */ inline std::string strip_comments( std::string filename, char comment_char = '#' ) { string s; ifstream in(filename.c_str()); bool write = true; while (in) { char c = in.get(); if ( c == comment_char ) write = false; if ( c == '\n' ) write = true; if ( write && in ) s += c; } return s; } inline std::string strip_comment_line ( string input , string comment_delim ="#") { if (!contains(input, comment_delim)) return input; auto v = split(input,comment_delim,1); if (v.size()>1) return v[0]; return ""; } /*! \brief Like python join. The elements of 'sequence' are streamed, so sequence may contain something else than strings. You can pass a convertor function to transform the vector elements to something else (like a string) before they are streamed.*/ template inline string join( const string sep , const T& sequence , F converter ) { std::ostringstream s; if ( sequence.empty() ) return ""; for ( typename T::const_iterator i = sequence.begin();; ) { s << converter( *i ); i++; if ( i == sequence.end() ) break; s << sep; } return s.str(); } /*! \brief Like python join */ template inline string join( const string sep, const T& sequence ) { auto identity = []( const typename T::value_type& v) {return v;}; return join( sep, sequence, identity ); } /*! format a string printf-style */ inline string form (const char *fmt, ...) { va_list ap; va_start(ap, fmt ); int len=512; char* buffer = 0; again: if (buffer) delete buffer; buffer = new char[len]; int n = vsnprintf( buffer, len, fmt, ap); if (n == -1 || n >=len) { len*=2; goto again; } va_end(ap); string s(buffer); delete[] buffer; return s; } inline string read_file( string filename ) { ifstream f( filename.c_str() ); std::ostringstream sstr; sstr << f.rdbuf(); return sstr.str(); } /*! \brief Demangle a typename */ #ifdef __GNUG__ inline string demangle(const char *abiName) { int status; char *ret = abi::__cxa_demangle(abiName, 0, 0, &status); string r(ret); free( ret ); return r; } #else inline string demangle(const char *abiName) { return abiName; } #endif // ================================ // Environment and command line // ================================ /*! returns the environment as a map (read-only) */ inline std::map env() { std::map r; char **p = ::environ; while (*p) { std::string x = *p; vector v = split(x, "="); r[v[0]]=v[1]; p++; } return r; } /*! convert argc,argv to vector */ inline std::vector to_vector(int argc, char* argv[] ) { vector r; for (int i = 0; i< argc ; i++) r.push_back( string(argv[i]) ); return r; } // ================================= // Converting to and from anything // ================================= /*! \brief Can s be streamed to type T? */ template inline bool is_a(const string& s) { istringstream ss( trim(s) ); T d; ss >> d; if (ss.fail()) return false; // complete failure if (ss.eof()) return true; // complete succes - nothing left. string sdum; ss >> sdum; if (sdum=="" && ss.eof() ) return true; // only whitespace left: fine return false; } /*! \brief Convert anything (with streaming) to a T [e.g. int x = to(s);] */ template T to(const U& v) { stringstream ss; ss << v; T t; ss >> t; if (ss.eof()) return t; string sdum; ss >> sdum; if (sdum=="" && ss.eof() ) return t; // still here? -> report error string type1 = typeid(T).name(); string type2 = typeid(U).name(); cout << "to<>: Failed to convert "; cout << "from type " << type2 << " to " << type1 << " for value " << v << endl; return t; } template<> inline bool to(const string& v) { string s = lower(v); if ( s == "true" || s == "1" || s == "yes" ) return true; if ( s == "false" || s == "0" || s == "no" ) return false; cout << "string " << v << " does no look like a bool" << endl; return false; } /* does string respresent a valid number (double) ? */ inline bool is_num(const string& s) { return is_a(s); } /*! \brief Generic convertor to string. Specialized for several types */ template string str(const T& v); namespace str_impl { template inline string to_str( const std::vector& v , int, long ) // long = experimental { return "["+ join( ", ", v , str ) + "]"; } template inline string to_str( const std::list& v , int, int ) { return "["+ join( ", ", v , str ) + "]"; } template inline string to_str( const std::set& v , int, int ) { return "{"+ join( ", ", v , str ) + "}"; } //! print the map python-style. see to_literal for c++ style template inline string to_str( const std::map& v , int, int ) { auto conv = []( const std::pair& p ) { return str(p.first)+": "+str(p.second); }; return "{"+ join( ", ", v , conv ) + "}"; } // define 3 to_str functions. 2nd and 3rg argument are used for controling // priority. When called with (x, 0,0), best match is provided by int (not long). template inline auto to_str( const T& t, int, int ) -> decltype( t.print(), string() ) { ostringstream ss; t.print(ss); return ss.str(); } inline string to_str(const string& s , int, long ) // need the s,int,long, otherwise it's ambigous with T,int,long { return s; } template inline auto to_str( const T& t, int, long ) -> decltype ( cout << t , string() ) { ostringstream ss; ss << t; return ss.str(); } template inline auto to_str(const T& t, long, long ) -> string { return demangle( typeid(T).name() ) + " object at " + str( &t ); } } //! turn a map into a c++11 map literal template inline string to_literal( const std::map& v ) { auto conv = []( const std::pair& p ) { return "{"+str(p.first)+", "+str(p.second)+"}"; }; return "{"+ join( ", ", v , conv ) + "}"; } /*! \brief Try really hard to convert v to string. returns v.print() if it exists. Otherwise returns result of streaming (operator<<) if that is defined, otherwise return " object at ". */ template string str(const T& v) { return str_impl::to_str( v, 0, 0 ); } // ================================ // Progres indicator // ================================ /*! \brief Print a simple progress indicator */ inline void progress( int item, int total_items, string style = "short" ) { static clock_t start; if (item==0) { start = clock(); return; } clock_t now = clock(); double time_elapsed = ( now - start ) / CLOCKS_PER_SEC; double time_per_item = time_elapsed / item; double items_to_go = total_items - item - 1; double eta = items_to_go * time_per_item; if ( style == "short" ) { string u = "s"; double n = eta; if ( n > 60 ) { n/=60.0; u = "min"; } if ( n > 60 ) { n/=60.0; u = "hour"; } cout << item << " / " << total_items ; cout << " ( " << form("%3.2f",n) << " " << u << " remaining )" << endl; } else { cout << " time elapsed " << time_elapsed << endl; cout << " time_per_item " << time_per_item << endl; cout << " items_to_go " << items_to_go << endl; cout << " eta " << eta << endl; } } //---------------------------------------- // Helper functions for reading istream //---------------------------------------- /*! returns true if the file exists */ inline bool file_exists(string filename) { ifstream f(filename.c_str()); if (!f) return false; return true; } /*! split filename in directory, filename and extension. */ struct Filedesc { string dir; string basename; string ext; Filedesc(string s) { size_t a = s.rfind("/"); size_t b = s.rfind("."); if ( a != string::npos ) dir = s.substr(0, a); basename = s.substr(a+1,b-a-1); if (b != string::npos) ext = s.substr(b+1); else ext=""; } string fullname() {return dir+"/"+basename+"."+ext;} string justfile() {return basename+"."+ext;} string chopext() {return dir+"/"+basename;} }; /*! read fom istream until delim (delim is not included) in the result */ inline string nextline( istream& in, char delim = '\n') { string s; while(in) { char c = in.get(); if (c==delim || !in) break; s+=c; } return s; } inline string peekline( istream& in, char delim) { std::streampos p = in.tellg(); string r = nextline( in, delim ); in.seekg( p ); return r; } inline string peekword( istream& in ) { std::streampos p = in.tellg(); string r; in >> r; in.seekg( p ); return r; } #ifndef __CLING___ // problems with streampos = 0, seekg etc // A temporary file. File is deleted in destructor. struct Tempfile: public fstream { string name; // in case you care Tempfile(bool open_it = true) { char b[][30] = {"/tmp/_Tempfile_XXXXXX", "_Tempfile_XXXXXX", "~/_Tempfile_XXXXXX", "give_up" }; int r=0; for (int i=0;;i++) { if ( strcmp(b[i],"give_up") ==0 ) { cout << "Failed to create a temporary file."; } r = mkstemp(b[i]); if (r!=-1) // succes { name = b[i]; break; } } ::close(r); // from unistd.h if (open_it) { fstream::open( name.c_str(), ios::in| ios::out ); if (!good()) cout << "failed to open temp file " << endl; } } ~Tempfile() { fstream::close(); string cmd = "rm -f "+name; if (-1== system(cmd.c_str()) ) cout << "error closing tempfile" << endl; } string content() // get current content; { bool was_open = false; std::streampos oldpos = 0; if (is_open() ) { flush(); was_open = true; oldpos = tellg(); } else { open(name.c_str(), ios::in); if (!good() ) cout << ("Failed open temporary file " + name ) << endl; } seekg (0, ios::end); std::streampos size = tellg(); seekg (0); char* buffer = new char [ int(size) + 1]; read( buffer , size ); buffer[size]=0; // terminate string s (buffer); delete[] buffer; if (was_open) // get back to where you once belonged { seekg( oldpos ); } return s; } }; // execute command using a system() call and // return stdout in a string. inline string exec(const string& command, int whatstream=0) { Tempfile f(false); string c; if (whatstream == 0 ) c = command + " > "+f.name; // stdout if (whatstream == 1 ) c = command + " 2> "+f.name; // sterrr if (whatstream == 2 ) c = command + " 1>&2 > "+f.name; // sterrr + stdout system(c.c_str()); string r = trim ( f.content() ); return r; } //! run command taking 'input' as stdin. inline string pipe(const string& input, const string& command) { Tempfile f; f << input << flush; return exec( "cat "+f.name+" | "+command ); } //! try irods to get a file. inline string stage_file( string filename, string stagedir, bool verbose = true, bool overwrite = false) { string newfile = ""; if ( startswith( filename, "root:")) return filename; // it's an xrootd url if ( file_exists( filename ) ) return filename; // nothing to do if ( startswith( filename, "/in2p3/" ) ) // looks like irods { newfile = stagedir +"/" + Filedesc( filename ).justfile(); string flags = overwrite ? "-fV -N 0" : "-V -N0 "; string cmd ="iget "+flags+" "+filename+" "+stagedir; if (verbose) std::cout << "staging : " << cmd << endl; string out = exec( cmd , 2 ); if (verbose) std::cout << "output=" << out << endl; } if ( file_exists( newfile ) ) // success { return newfile; } else { return ""; } } /*! Unzip a file, returns the name of the unziped file. */ inline string unzip_file( string filename, bool verbose = true, bool overwrite = false ) { if ( !endswith( filename, ".gz" ) ) return filename; string newfile = Filedesc( filename ).chopext(); string cmd = "gunzip -q "+filename; if ( file_exists( newfile ) && overwrite) cmd = "gunzip -f "+filename; string out = exec( cmd , 2 ); if (verbose) std::cout << "output=" << out << endl; if ( file_exists( newfile ) ) // success { return newfile; } return ""; } #endif // some minimal html stuff inline string html_head() { return "\n";} inline string html_foot() { return " \n";} inline string html_link(string txt, string href) { return " "+txt+""; } //------------------------------------------------------------------------------------------------------------------- namespace coolprinter { struct printer { ostream* stream ; string spacer ; string endline; printer( ostream& out = cout , string spacer = " ", string endline = "\n" ) : stream(&out), spacer(spacer), endline(endline) {} void _prnt () { *stream << endline; } template void _prnt( First first, Rest ... rest ) { *stream << str( first ) << spacer; _prnt(rest...); } template inline printer& operator() (Ts ...args ) { _prnt(args...); return *this; } }; template inline printer& print (Ts ...args ) { static printer p; p._prnt(args...); return p; } template inline string sprint (Ts ...args ) { ostringstream ss; printer p( ss ); p._prnt(args...); return ss.str(); } } namespace fallbackprinter { struct FB { template FB(T&) {} }; inline std::ostream& operator<<( std::ostream& out, const FB& ) { return out << std::string( "(can not print)" ); } }; using coolprinter::print; using coolprinter::sprint; #define dbg(args...) stringutil::print( form("%s:%d (%s) %s = ", __FILE__,__LINE__,__FUNCTION__, #args), args ) #define fatal(args... ) { \ std::string s = stringutil::sprint( form("%s:%d (%s) : fatal error ", __FILE__,__LINE__,__FUNCTION__), args ); \ std::cout << s << std::endl; \ throw AAFatalException( s ) ; } //#endif } #endif