// ------------------------------------------------------------------------ // kvu_utils.cpp: Miscellaneous helper routines // Copyright (C) 1999-2004,2009 Kai Vehmanen // // Attributes: // eca-style-version: 2 // // 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 2 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, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // ------------------------------------------------------------------------ #ifdef HAVE_CONFIG_H #include #endif #include /* find() */ #include /* atoi() */ #include #include #include #if HAVE_LOCALE_H #include /* setlocale */ #endif #include /* nanosleep() */ #include /* gettimeofday() */ #include /* usleep() */ #include /* isspace(), toupper() */ #include "kvu_dbc.h" #include "kvu_utils.h" using namespace std; static string::const_iterator kvu_priv_find_next_instance(const string& arg, const string::const_iterator& curpos, const string::value_type value); static void kvu_priv_strip_escapes(string* const input, const string& escaped_char); /** * Returns a string where all regex metachars in 'arg' * have been quoted using a backslash. * * Reference: man regex(7) */ string kvu_string_regex_meta_escape(const string& arg) { string result; /* regex metachar set: ^.[]$()|*+?{}\ */ string::const_iterator p = arg.begin(); while(p != arg.end()) { if (*p == '^') result += "\\^"; else if (*p == '.') result += "\\."; else if (*p == '[') result += "\\["; else if (*p == ']') result += "\\]"; else if (*p == '$') result += "\\$"; else if (*p == '(') result += "\\("; else if (*p == ')') result += "\\)"; else if (*p == '|') result += "\\|"; else if (*p == '*') result += "\\*"; else if (*p == '+') result += "\\+"; else if (*p == '?') result += "\\?"; else if (*p == '{') result += "\\{"; else if (*p == '}') result += "\\}"; else if (*p == '\\') result += "\\\\"; else result += *p; ++p; } return result; } /** * Returns a string where all meta characters in 'arg' * have been quoted using a backslash. * * Reference: man sh(1), man bash(1) */ string kvu_string_shell_meta_escape(const string& arg) { string result; string::const_iterator p = arg.begin(); while(p != arg.end()) { if (*p == '"') result += "\\\""; else if (*p == '\'') result += "\\\'"; else if (*p == ' ') result += "\\ "; else if (*p == '|') result += "\\|"; else if (*p == '&') result += "\\&"; else if (*p == ';') result += "\\;"; else if (*p == '(') result += "\\("; else if (*p == ')') result += "\\)"; else if (*p == '<') result += "\\<"; else if (*p == '>') result += "\\>"; else result += *p; ++p; } return result; } /** * Converts a string to a vector of strings (words). * Whitespace is used as the separator. * * Note! This function is obsolete, use @see string_to_tokens() * instead. */ vector kvu_string_to_words(const string& s) { return kvu_string_to_tokens(s); } /** * Converts a string to a vector of token strings. * Whitespace is used as the separator. */ vector kvu_string_to_tokens(const string& s) { vector vec; string stmp = ""; for(string::const_iterator p = s.begin(); p != s.end(); p++) { if (isspace(*p) == 0) stmp += *p; else { if (stmp == "") continue; vec.push_back(stmp); stmp = ""; } } if (stmp.size() > 0) vec.push_back(stmp); return vec; } /** * Converts a string to a vector of token strings. * Whitespace is used as the token separator. * * Unlike string_to_tokens(), quotes can be used to mark * groups of words as tokens (e.g. "this is one token"). * The tokens are not removed from the string. * Single-quotes (') are not supported. * * It's also possible to add individual whitespace * characted by escaping them with a backclash (e.g. * 'this\ is\ one\ token\ '). Escaped characters are * not considered as possible separators. */ vector kvu_string_to_tokens_quoted(const string& s) { vector vec; string stmp; bool quoteflag = false; for(string::const_iterator p = s.begin(); p != s.end(); p++) { if (*p == '\"') { quoteflag = !quoteflag; stmp += *p; } else if (*p == '\\') { p++; if (p == s.end()) break; stmp += *p; } else if (isspace(*p) == 0 || quoteflag == true) { stmp += *p; } else { /* note: token ready, add to vector if length is non-zero */ if (stmp.size() == 0) continue; vec.push_back(stmp); stmp = ""; DBC_CHECK(stmp.size() == 0); } } if (stmp.size() > 0) vec.push_back(stmp); return vec; } /** * Converts a string to a vector of strings. * * @param str string to be converted * @param separator character to be used for separating items */ vector kvu_string_to_vector(const string& str, const string::value_type separator) { vector vec; string stmp = ""; for(string::const_iterator p = str.begin(); p != str.end(); p++) { if (*p != separator) stmp += *p; else { if (stmp == "") continue; vec.push_back(stmp); stmp = ""; } } if (stmp.size() > 0) vec.push_back(stmp); return vec; } /** * Converts a string to a vector of integers. * * @param str string to be converted * @param separator character to be used for separating items */ vector kvu_string_to_int_vector(const string& str, const string::value_type separator) { vector vec; string stmp = ""; for(string::const_iterator p = str.begin(); p != str.end(); p++) { if (*p != separator) stmp += *p; else { if (stmp == "") continue; vec.push_back(atoi(stmp.c_str())); stmp = ""; } } if (stmp.size() > 0) vec.push_back(atoi(stmp.c_str())); return vec; } /** * Return a modified copy of vector 'str_vector' where 'from' has * been replaced with 'to' in all items. */ vector kvu_vector_search_and_replace(const vector& str_vector, const string& from, const string& to) { vector vstmp; vector::const_iterator p = str_vector.begin(); while(p != str_vector.end()) { vstmp.push_back(kvu_string_search_and_replace(*p, from, to)); ++p; } return vstmp; } /** * Converts a vector of strings to a single string. * * @param str vector of strings to be converted * @param separator string that is inserted between items */ string kvu_vector_to_string(const vector& str, const string& separator) { string stmp; vector::const_iterator p = str.begin(); while(p != str.end()) { stmp += *p; ++p; if (p != str.end()) stmp += separator; } return stmp; } /** * Return a new string, where all 'from' characters are * replaced with 'to' characters. */ string kvu_string_search_and_replace(const string& str, const string::value_type from, const string::value_type to) { string stmp (str); for(vector::size_type p = 0; p < str.size(); p++) { if (str[p] == from) stmp[p] = to; else stmp[p] = str[p]; } return stmp; } /** * Return a new string, where all 'from' characters are * replaced with 'to' characters. */ string kvu_string_search_and_replace(const string& str, const string& from, const string& to) { string tmp (str); size_t pos = 0; while((pos = tmp.find(from, pos)) != string::npos) { tmp.replace(pos, from.size(), to); pos += (to.size() > from.size() ? to.size() : from.size()); } return tmp; } /** * Case-insensitive string compare. Ignores preceding and * trailing white space. */ bool kvu_string_icmp(const string& first, const string& second) { string a = first; string b = second; a = kvu_remove_trailing_spaces(a); a = kvu_remove_preceding_spaces(a); a = kvu_convert_to_uppercase(a); b = kvu_remove_trailing_spaces(b); b = kvu_remove_preceding_spaces(b); b = kvu_convert_to_uppercase(b); return a == b; } /** * Removes all trailing white space */ string kvu_remove_trailing_spaces(const string& a) { string r = ""; string::const_reverse_iterator p; for(p = a.rbegin(); p != a.rend(); p++) { if (*p != ' ') break; } for(; p != a.rend(); p++) { r = *p + r; } return r; } /** * Removes all preciding white space */ string kvu_remove_preceding_spaces(const string& a) { string r = ""; string::const_iterator p; for(p = a.begin(); p != a.end(); p++) { if (*p != ' ') break; } for(; p != a.end(); p++) { r += *p; } return r; } /** * Removes all surrounding white spaces */ string kvu_remove_surrounding_spaces(const string& a) { string::const_iterator p,q; for(p = a.begin(); p != a.end() && *p == ' '; p++); for(q = (a.end() - 1); q != a.begin() && *q == ' '; q--); return string(p,q + 1); } /** * Converts string to uppercase using toupper(int) */ string kvu_convert_to_uppercase(const string& a) { string r = a; for(string::iterator p = r.begin(); p != r.end(); p++) *p = toupper(*p); return r; } /** * Converts string to lowercase using tolower(int) */ string kvu_convert_to_lowercase(const string& a) { string r = a; for(string::iterator p = r.begin(); p != r.end(); p++) *p = tolower(*p); return r; } /** * Converts string to uppercase using toupper(int) * Modifies the parameter object. */ void kvu_to_uppercase(string& a) { string::iterator p = a.begin(); while(p != a.end()) { *p = toupper(*p); ++p; } } /** * Converts string to lowercase using tolower(int) * Modifies the parameter object. */ void kvu_to_lowercase(string& a) { string::iterator p = a.begin(); while(p != a.end()) { *p = tolower(*p); ++p; } } /** * Finds the next instance of character 'value' and returns its * position. * * All backslash escaped instances of 'value' ("\") * are ignored in the search. Also instance of 'value' within * a pair of double quotes are ignored. * * Note that general backlash escaping is not supported, i.e. * "\" is not interpreted as "". * * @return position of next 'value' or arg.end() if not found */ static string::const_iterator kvu_priv_find_next_instance(const string& arg, const string::const_iterator& start, const string::value_type value) { string::const_iterator curpos = start, ret = arg.end(), nextquote = arg.end(); while(curpos != arg.end()) { ret = find(curpos, arg.end(), value); nextquote = find(curpos, arg.end(), '"'); if (nextquote < ret) { /* step: ignore quoted part of the input string */ nextquote = find(nextquote + 1, arg.end(), '"'); if (nextquote != arg.end()) { curpos = nextquote + 1; ret = find(curpos, arg.end(), value); } } if (ret != arg.end() && ret != arg.begin()) { string::const_iterator prev = ret - 1; if ((*prev) == '\\') { curpos = ret + 1; continue; } } break; } return ret; } /** * Returns the nth argument from a formatted string * * @param number the argument number * @param argu a formatted string: "something:arg1,arg2,...,argn" * * require: * number >= 1 */ string kvu_get_argument_number(int number, const string& arg) { string result; vector temp = kvu_get_arguments(arg); if (static_cast(temp.size()) >= number) { result = temp[number - 1]; } return result; } /** * Converts all backslash-commas into commas and returns * the result. * * @pre escaped_char.size() == 1 */ static void kvu_priv_strip_escapes(string* const input, const string& escaped_char) { size_t pos; while((pos = input->find(string("\\") + escaped_char)) != string::npos) { input->replace(pos, 2, escaped_char); } } /** * Strips the outer quotes of type 'quote_char' from the string. * The string is only modified if the the string starts with, * and ends to, a character matching 'quote_char'. * * @pre escaped_char.size() == 1 */ void kvu_string_strip_outer_quotes(string* input, const std::string::value_type quote_char) { if (input->size() >= 2 && *input->begin() == quote_char && *(input->end() - 1) == quote_char) *input = std::string(input->begin() + 1, input->end() - 1); } /** * Returns number of arguments in formatted string 'arg'. */ int kvu_get_number_of_arguments(const string& arg) { return kvu_get_arguments(arg).size(); } /** * Returns a vector of all arguments from a formatted string * * Note: forces locale to "POSIX" as decimal separate must be * be period (.) in order to argument separation to work * * @param argu a formatted string: "something:arg1,arg2,...,argn" */ vector kvu_get_arguments(const string& argu) { vector resvec; char* locale_save; string::const_iterator b = kvu_priv_find_next_instance(argu, argu.begin(), ':'); string::const_iterator e; if (b == argu.end()) { if (argu.size() > 0) b = argu.begin(); else return resvec; } else ++b; #if HAVE_SETLOCALE locale_save = setlocale (LC_NUMERIC, "POSIX"); #else locale_save = NULL; #endif for(; b != argu.end();) { e = kvu_priv_find_next_instance(argu, b, ','); string target = string(b, e); /* strip backslash-commas (and leave the commas in place) */ kvu_priv_strip_escapes(&target, ","); kvu_priv_strip_escapes(&target, ":"); kvu_string_strip_outer_quotes(&target, '"'); resvec.push_back(target); if (e == argu.end()) break; b = e; ++b; /* special case in which last argument is empty */ if (b == argu.end() && *e == ',') resvec.push_back(""); } #if HAVE_SETLOCALE /* restore original locale */ if (locale_save) setlocale (LC_NUMERIC, locale_save); #endif return resvec; } /** * Get the prefix part of a string argument * @param argument format used is -prefix:arg1, arg2, ..., argN * * require: * argu.find('-') != string::npos * * ensure: * result.size() >= 0 */ string kvu_get_argument_prefix(const string& argu) { // -------- DBC_REQUIRE(argu.find('-') != string::npos); // -------- string::const_iterator b = find(argu.begin(), argu.end(), '-'); string::const_iterator e = find(argu.begin(), argu.end(), ':'); if (b != argu.end()) { ++b; if (b != argu.end()) { return string(b,e); } } return ""; } /** * Prints a time stamp to stderr */ void kvu_print_time_stamp(void) { // -- // not thread-safe! // -- static bool first = true; static struct timeval last; struct timeval current; if (first) { ::gettimeofday(&last, 0); first = false; } ::gettimeofday(¤t, 0); cerr << "(timestamp) " << current.tv_sec << "sec, " << current.tv_usec << "msec."; long delta = current.tv_usec; delta -= last.tv_usec; delta += (current.tv_sec - last.tv_sec) * 1000000; cerr << " Delta " << delta << "msec." << endl; last.tv_sec = current.tv_sec; last.tv_usec = current.tv_usec; } /** * Put the calling execution context to sleeps for * 'seconds.nanosecods'. * * Note! If available, implemented using nanosleep(). * * @return 0 on success, non-zero if sleep was * interrupted for some reason */ int kvu_sleep(long int seconds, long int nanoseconds) { int ret = 0; #if defined(HAVE_NANOSLEEP) && !defined(__CYGWIN__) struct timespec len; len.tv_sec = static_cast(seconds); len.tv_nsec = nanoseconds; ret = nanosleep(&len, NULL); #elif HAVE_USLEEP ret = usleep(seconds * 1000000 + nanoseconds / 1000); #else cerr << "(libkvutils) kvutils:: warning! neither nanosleep() or usleep() found!" << endl; #endif return ret; }