/* * Copyright (c) 2003-2012, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @defgroup util General utilities */ /** * @file utils.h * @author John Wiegley * * @ingroup util * * @brief General utility facilities used by Ledger */ #ifndef _UTILS_H #define _UTILS_H /** * @name Default values */ /*@{*/ #define TIMERS_ON 1 #if defined(DEBUG_MODE) #define VERIFY_ON 1 #define TRACING_ON 1 #define DEBUG_ON 1 #elif defined(NDEBUG) #define NO_ASSERTS 1 //#define NO_LOGGING 1 #else #define TRACING_ON 1 // use --trace X to enable #endif /*@}*/ /** * @name Forward declarations */ /*@{*/ namespace ledger { using namespace boost; #if defined(VERIFY_ON) || defined(HAVE_BOOST_PYTHON) class string; #else typedef std::string string; #endif typedef std::list strings_list; typedef posix_time::ptime ptime; typedef ptime::time_duration_type time_duration; typedef gregorian::date date; typedef gregorian::date_duration date_duration; typedef posix_time::seconds seconds; typedef boost::filesystem::path path; typedef boost::filesystem::ifstream ifstream; typedef boost::filesystem::ofstream ofstream; typedef boost::filesystem::filesystem_error filesystem_error; } #if BOOST_FILESYSTEM_VERSION == 3 #if defined(VERIFY_ON) || defined(HAVE_BOOST_PYTHON) namespace boost { namespace filesystem3 { namespace path_traits { template<> struct is_pathable { static const bool value = true; }; }}} #endif // defined(VERIFY_ON) || defined(HAVE_BOOST_PYTHON) #endif // BOOST_FILESYSTEM_VERSION == 3 /*@}*/ /** * @name Assertions */ /*@{*/ #ifdef assert #undef assert #endif #if ! defined(NO_ASSERTS) #define ASSERTS_ON 1 #endif #if defined(ASSERTS_ON) namespace ledger { void debug_assert(const string& reason, const string& func, const string& file, std::size_t line); } #define assert(x) \ ((x) ? ((void)0) : debug_assert(#x, BOOST_CURRENT_FUNCTION, \ __FILE__, __LINE__)) #else // ! ASSERTS_ON #define assert(x) #endif // ASSERTS_ON /*@}*/ /** * @name Verification (i.e., heavy asserts) */ /*@{*/ #if defined(VERIFY_ON) namespace ledger { extern bool verify_enabled; #define VERIFY(x) (ledger::verify_enabled ? assert(x) : ((void)0)) #define DO_VERIFY() ledger::verify_enabled void initialize_memory_tracing(); void shutdown_memory_tracing(); std::size_t current_memory_size(); std::size_t current_objects_size(); void trace_ctor_func(void * ptr, const char * cls_name, const char * args, std::size_t cls_size); void trace_dtor_func(void * ptr, const char * cls_name, std::size_t cls_size); #define TRACE_CTOR(cls, args) \ (DO_VERIFY() ? \ ledger::trace_ctor_func(this, #cls, args, sizeof(cls)) : ((void)0)) #define TRACE_DTOR(cls) \ (DO_VERIFY() ? \ ledger::trace_dtor_func(this, #cls, sizeof(cls)) : ((void)0)) void report_memory(std::ostream& out, bool report_all = false); } // namespace ledger #else // ! VERIFY_ON #define VERIFY(x) #define DO_VERIFY() true #define TRACE_CTOR(cls, args) #define TRACE_DTOR(cls) #endif // VERIFY_ON #define IF_VERIFY() if (DO_VERIFY()) /*@}*/ /** * @name String wrapper * * This string type is a wrapper around std::string that allows us to trace * constructor and destructor calls. It also makes ledger's use of strings a * unique type, that the Boost.Python code can use as the basis for * transparent Unicode conversions. */ /*@{*/ namespace ledger { #if defined(VERIFY_ON) || defined(HAVE_BOOST_PYTHON) class string : public std::string { public: string(); string(const string& str); string(const std::string& str); string(size_type len, char x); template string(_InputIterator __beg, _InputIterator __end) : std::string(__beg, __end) { TRACE_CTOR(string, "InputIterator, InputIterator"); } string(const char * str); string(const char * str, const char * end); string(const string& str, size_type x); string(const string& str, size_type x, size_type y); string(const char * str, size_type x); string(const char * str, size_type x, size_type y); ~string() throw(); #if defined(HAVE_BOOST_SERIALIZATION) private: /** Serialization. */ friend class boost::serialization::access; template void serialize(Archive& ar, const unsigned int /* version */) { ar & boost::serialization::base_object(*this); } #endif // HAVE_BOOST_SERIALIZATION }; inline string operator+(const string& __lhs, const string& __rhs) { string __str(__lhs); __str.append(__rhs); return __str; } string operator+(const char* __lhs, const string& __rhs); string operator+(char __lhs, const string& __rhs); inline string operator+(const string& __lhs, const char* __rhs) { string __str(__lhs); __str.append(__rhs); return __str; } inline string operator+(const string& __lhs, char __rhs) { typedef string __string_type; typedef string::size_type __size_type; __string_type __str(__lhs); __str.append(__size_type(1), __rhs); return __str; } inline bool operator==(const string& __lhs, const string& __rhs) { return __lhs.compare(__rhs) == 0; } inline bool operator==(const char* __lhs, const string& __rhs) { return __rhs.compare(__lhs) == 0; } inline bool operator==(const string& __lhs, const char* __rhs) { return __lhs.compare(__rhs) == 0; } inline bool operator!=(const string& __lhs, const string& __rhs) { return __rhs.compare(__lhs) != 0; } inline bool operator!=(const char* __lhs, const string& __rhs) { return __rhs.compare(__lhs) != 0; } inline bool operator!=(const string& __lhs, const char* __rhs) { return __lhs.compare(__rhs) != 0; } #endif // defined(VERIFY_ON) || defined(HAVE_BOOST_PYTHON) extern string empty_string; strings_list split_arguments(const char * line); } // namespace ledger /*@}*/ /** * @name Tracing and logging */ /*@{*/ #if ! defined(NO_LOGGING) #define LOGGING_ON 1 #endif #if defined(LOGGING_ON) namespace ledger { enum log_level_t { LOG_OFF = 0, LOG_CRIT, LOG_FATAL, LOG_ASSERT, LOG_ERROR, LOG_VERIFY, LOG_WARN, LOG_INFO, LOG_EXCEPT, LOG_DEBUG, LOG_TRACE, LOG_ALL }; extern log_level_t _log_level; extern std::ostream * _log_stream; extern std::ostringstream _log_buffer; void logger_func(log_level_t level); #define LOGGER(cat) \ static const char * const _this_category = cat #if defined(TRACING_ON) extern uint8_t _trace_level; #define SHOW_TRACE(lvl) \ (ledger::_log_level >= ledger::LOG_TRACE && lvl <= ledger::_trace_level) #define TRACE(lvl, msg) \ (SHOW_TRACE(lvl) ? \ ((ledger::_log_buffer << msg), \ ledger::logger_func(ledger::LOG_TRACE)) : (void)0) #else // TRACING_ON #define SHOW_TRACE(lvl) false #define TRACE(lvl, msg) #endif // TRACING_ON #if defined(DEBUG_ON) extern optional _log_category; #if defined(HAVE_BOOST_REGEX_UNICODE) extern optional _log_category_re; #else extern optional _log_category_re; #endif inline bool category_matches(const char * cat) { if (_log_category) { if (! _log_category_re) { _log_category_re = #if defined(HAVE_BOOST_REGEX_UNICODE) boost::make_u32regex(_log_category->c_str(), boost::regex::perl | boost::regex::icase); #else boost::regex(_log_category->c_str(), boost::regex::perl | boost::regex::icase); #endif } #if defined(HAVE_BOOST_REGEX_UNICODE) return boost::u32regex_search(cat, *_log_category_re); #else return boost::regex_search(cat, *_log_category_re); #endif } return false; } #define SHOW_DEBUG(cat) \ (ledger::_log_level >= ledger::LOG_DEBUG && ledger::category_matches(cat)) #define SHOW_DEBUG_() SHOW_DEBUG(_this_category) #define DEBUG(cat, msg) \ (SHOW_DEBUG(cat) ? \ ((ledger::_log_buffer << msg), \ ledger::logger_func(ledger::LOG_DEBUG)) : (void)0) #define DEBUG_(msg) DEBUG(_this_category, msg) #else // DEBUG_ON #define SHOW_DEBUG(cat) false #define SHOW_DEBUG_() false #define DEBUG(cat, msg) #define DEBUG_(msg) #endif // DEBUG_ON #define LOG_MACRO(level, msg) \ (ledger::_log_level >= level ? \ ((ledger::_log_buffer << msg), ledger::logger_func(level)) : (void)0) #define SHOW_INFO() (ledger::_log_level >= ledger::LOG_INFO) #define SHOW_WARN() (ledger::_log_level >= ledger::LOG_WARN) #define SHOW_ERROR() (ledger::_log_level >= ledger::LOG_ERROR) #define SHOW_FATAL() (ledger::_log_level >= ledger::LOG_FATAL) #define SHOW_CRITICAL() (ledger::_log_level >= ledger::LOG_CRIT) #define INFO(msg) LOG_MACRO(ledger::LOG_INFO, msg) #define WARN(msg) LOG_MACRO(ledger::LOG_WARN, msg) #define ERROR(msg) LOG_MACRO(ledger::LOG_ERROR, msg) #define FATAL(msg) LOG_MACRO(ledger::LOG_FATAL, msg) #define CRITICAL(msg) LOG_MACRO(ledger::LOG_CRIT, msg) #define EXCEPTION(msg) LOG_MACRO(ledger::LOG_EXCEPT, msg) } // namespace ledger #else // ! LOGGING_ON #define LOGGER(cat) #define SHOW_TRACE(lvl) false #define SHOW_DEBUG(cat) false #define SHOW_DEBUG_() false #define SHOW_INFO() false #define SHOW_WARN() false #define SHOW_ERROR() false #define SHOW_FATAL() false #define SHOW_CRITICAL() false #define TRACE(lvl, msg) #define DEBUG(cat, msg) #define DEBUG_(msg) #define INFO(msg) #define WARN(msg) #define ERROR(msg) #define FATAL(msg) #define CRITICAL(msg) #endif // LOGGING_ON #define IF_TRACE(lvl) if (SHOW_TRACE(lvl)) #define IF_DEBUG(cat) if (SHOW_DEBUG(cat)) #define IF_DEBUG_() if (SHOW_DEBUG_()) #define IF_INFO() if (SHOW_INFO()) #define IF_WARN() if (SHOW_WARN()) #define IF_ERROR() if (SHOW_ERROR()) #define IF_FATAL() if (SHOW_FATAL()) #define IF_CRITICAL() if (SHOW_CRITICAL()) /*@}*/ /** * @name Timers * This allows log xacts to specify cumulative time spent. */ /*@{*/ #if defined(LOGGING_ON) && defined(TIMERS_ON) namespace ledger { void start_timer(const char * name, log_level_t lvl); void stop_timer(const char * name); void finish_timer(const char * name); #if defined(TRACING_ON) #define TRACE_START(name, lvl, msg) \ (SHOW_TRACE(lvl) ? \ ((ledger::_log_buffer << msg), \ ledger::start_timer(#name, ledger::LOG_TRACE)) : ((void)0)) #define TRACE_STOP(name, lvl) \ (SHOW_TRACE(lvl) ? ledger::stop_timer(#name) : ((void)0)) #define TRACE_FINISH(name, lvl) \ (SHOW_TRACE(lvl) ? ledger::finish_timer(#name) : ((void)0)) #else #define TRACE_START(name, lvl, msg) #define TRACE_STOP(name, lvl) #define TRACE_FINISH(name, lvl) #endif #if defined(DEBUG_ON) #define DEBUG_START(name, cat, msg) \ (SHOW_DEBUG(cat) ? \ ((ledger::_log_buffer << msg), \ ledger::start_timer(#name, ledger::LOG_DEBUG)) : ((void)0)) #define DEBUG_START_(name, msg) \ DEBUG_START_(name, _this_category, msg) #define DEBUG_STOP(name, cat) \ (SHOW_DEBUG(cat) ? ledger::stop_timer(#name) : ((void)0)) #define DEBUG_STOP_(name) \ DEBUG_STOP_(name, _this_category) #define DEBUG_FINISH(name, cat) \ (SHOW_DEBUG(cat) ? ledger::finish_timer(#name) : ((void)0)) #define DEBUG_FINISH_(name) \ DEBUG_FINISH_(name, _this_category) #else #define DEBUG_START(name, cat, msg) #define DEBUG_START_(name, msg) #define DEBUG_STOP(name) #define DEBUG_FINISH(name) #endif #define INFO_START(name, msg) \ (SHOW_INFO() ? \ ((ledger::_log_buffer << msg), \ ledger::start_timer(#name, ledger::LOG_INFO)) : ((void)0)) #define INFO_STOP(name) \ (SHOW_INFO() ? stop_timer(#name) : ((void)0)) #define INFO_FINISH(name) \ (SHOW_INFO() ? finish_timer(#name) : ((void)0)) } // namespace ledger #else // ! (LOGGING_ON && TIMERS_ON) #define TRACE_START(lvl, msg, name) #define TRACE_STOP(name, lvl) #define TRACE_FINISH(name, lvl) #define DEBUG_START(name, msg) #define DEBUG_START_(name, cat, msg) #define DEBUG_STOP(name) #define DEBUG_FINISH(name) #define INFO_START(name, msg) #define INFO_STOP(name) #define INFO_FINISH(name) #endif // TIMERS_ON /*@}*/ /* * These files define the other internal facilities. */ #include "error.h" enum caught_signal_t { NONE_CAUGHT, INTERRUPTED, PIPE_CLOSED }; extern caught_signal_t caught_signal; void sigint_handler(int sig); void sigpipe_handler(int sig); inline void check_for_signal() { switch (caught_signal) { case NONE_CAUGHT: break; case INTERRUPTED: throw std::runtime_error(_("Interrupted by user (use Control-D to quit)")); case PIPE_CLOSED: throw std::runtime_error(_("Pipe terminated")); } } /** * @name General utility functions */ /*@{*/ #if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__ #define foreach(x, y) for (x : y) #define unique_ptr std::unique_ptr #else #define foreach BOOST_FOREACH #define unique_ptr std::auto_ptr #endif namespace ledger { template inline T& downcast(U& object) { return *polymorphic_downcast(&object); } path resolve_path(const path& pathname); #ifdef HAVE_REALPATH extern "C" char * realpath(const char *, char resolved_path[]); #endif inline const string& either_or(const string& first, const string& second) { return first.empty() ? second : first; } inline char * skip_ws(char * ptr) { while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') ptr++; return ptr; } inline char * trim_ws(char * ptr) { std::size_t len = std::strlen(ptr); int i = int(len) - 1; while (i >= 0 && (ptr[i] == ' ' || ptr[i] == '\t' || ptr[i] == '\n')) ptr[i--] = '\0'; return skip_ws(ptr); } inline char * next_element(char * buf, bool variable = false) { for (char * p = buf; *p; p++) { if (! (*p == ' ' || *p == '\t')) continue; if (! variable) { *p = '\0'; return skip_ws(p + 1); } else if (*p == '\t') { *p = '\0'; return skip_ws(p + 1); } else if (*(p + 1) == ' ') { *p = '\0'; return skip_ws(p + 2); } } return NULL; } inline char peek_next_nonws(std::istream& in) { char c = static_cast(in.peek()); while (in.good() && ! in.eof() && std::isspace(c)) { in.get(c); c = static_cast(in.peek()); } return c; } #define READ_INTO(str, targ, size, var, cond) { \ char * _p = targ; \ var = static_cast(str.peek()); \ while (str.good() && ! str.eof() && var != '\n' && \ (cond) && _p - targ < size) { \ str.get(var); \ if (str.eof()) \ break; \ if (var == '\\') { \ str.get(var); \ if (in.eof()) \ break; \ switch (var) { \ case 'b': var = '\b'; break; \ case 'f': var = '\f'; break; \ case 'n': var = '\n'; break; \ case 'r': var = '\r'; break; \ case 't': var = '\t'; break; \ case 'v': var = '\v'; break; \ default: break; \ } \ } \ *_p++ = var; \ var = static_cast(str.peek()); \ } \ *_p = '\0'; \ } #define READ_INTO_(str, targ, size, var, idx, cond) { \ char * _p = targ; \ var = static_cast(str.peek()); \ while (str.good() && ! str.eof() && var != '\n' && \ (cond) && _p - targ < size) { \ str.get(var); \ if (str.eof()) \ break; \ idx++; \ if (var == '\\') { \ str.get(var); \ if (in.eof()) \ break; \ switch (var) { \ case 'b': var = '\b'; break; \ case 'f': var = '\f'; break; \ case 'n': var = '\n'; break; \ case 'r': var = '\r'; break; \ case 't': var = '\t'; break; \ case 'v': var = '\v'; break; \ default: break; \ } \ idx++; \ } \ *_p++ = var; \ var = static_cast(str.peek()); \ } \ *_p = '\0'; \ } inline string to_hex(unsigned int * message_digest, const int len = 1) { std::ostringstream buf; for(int i = 0; i < 5 ; i++) { buf.width(8); buf.fill('0'); buf << std::hex << message_digest[i]; if (i + 1 >= len) break; // only output the first LEN dwords } return buf.str(); } inline string sha1sum(const string& str) { SHA1 sha; sha.Reset(); sha << str.c_str(); unsigned int message_digest[5]; sha.Result(message_digest); return to_hex(message_digest, 5); } class push_xml { std::ostream& out; string tag; bool leave_open; public: push_xml(std::ostream& _out, const string& _tag, bool has_attrs = false, bool _leave_open = false) : out(_out), tag(_tag), leave_open(_leave_open) { out << '<' << tag; if (! has_attrs) out << '>'; } ~push_xml() { if (! leave_open) out << "'; } void close_attrs() { out << '>'; } static string guard(const string& str) { std::ostringstream buf; foreach (const char& ch, str) { switch (ch) { case '<': buf << "<"; break; case '>': buf << ">"; break; case '&': buf << "&"; break; default: buf << ch; break; } } return buf.str(); } }; extern const string version; } // namespace ledger /*@}*/ #endif // _UTILS_H