2015-01-05 12:30:17 -07:00
/// @file
/// @author rfree (current maintainer in monero.cc project)
/// @brief various general utils taken from (and relate to) otshell project, including loggiang/debug
/* See other files here for the LICENCE that applies here. */
/* See header file .hpp for info */
# include <algorithm>
# include <functional>
# include <cctype>
# include <locale>
# include <fstream>
# include <iostream>
# include <iomanip>
2016-03-11 05:25:28 -07:00
# include <chrono>
2015-01-05 12:30:17 -07:00
# include "utils.hpp"
# include "ccolor.hpp"
# include "lib_common1.hpp"
# include "runoptions.hpp"
# if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined (WIN64)
# define OS_TYPE_WINDOWS
# elif defined(__unix__) || defined(__posix) || defined(__linux) || defined(__darwin) || defined(__APPLE__) || defined(__clang__)
# define OS_TYPE_POSIX
# else
2015-02-12 12:59:39 -07:00
# warning "Compiler / OS platform is not recognized. Just assuming it will work as POSIX then"
2015-01-05 12:30:17 -07:00
# define OS_TYPE_POSIX
# endif
# if defined(OS_TYPE_WINDOWS)
# include <windows.h>
# elif defined(OS_TYPE_POSIX)
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# else
# error "Compiler / OS platform detection failed - not supported"
# endif
2015-04-01 11:00:45 -06:00
2015-01-05 12:30:17 -07:00
namespace nOT {
namespace nUtils {
2015-02-12 12:59:39 -07:00
INJECT_OT_COMMON_USING_NAMESPACE_COMMON_1 // <=== namespaces
2015-01-05 12:30:17 -07:00
2015-04-02 05:58:38 -06:00
// ====================================================================
// Numerical values of the debug levels - see hpp
const int _debug_level_nr_dbg3 = 20 ;
const int _debug_level_nr_dbg2 = 30 ;
const int _debug_level_nr_dbg1 = 40 ;
const int _debug_level_nr_info = 50 ;
const int _debug_level_nr_note = 60 ;
const int _debug_level_nr_fact = 75 ;
const int _debug_level_nr_mark = 80 ;
const int _debug_level_nr_warn = 90 ;
const int _debug_level_nr_erro = 100 ;
// ====================================================================
2015-01-05 12:30:17 -07:00
myexception : : myexception ( const char * what )
: std : : runtime_error ( what )
{ }
myexception : : myexception ( const std : : string & what )
: std : : runtime_error ( what )
{ }
void myexception : : Report ( ) const {
_erro ( " Error: " < < what ( ) ) ;
}
//myexception::~myexception() { }
// ====================================================================
// text trimming
// http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
std : : string & ltrim ( std : : string & s ) {
s . erase ( s . begin ( ) , std : : find_if ( s . begin ( ) , s . end ( ) , std : : not1 ( std : : ptr_fun < int , int > ( std : : isspace ) ) ) ) ;
return s ;
}
std : : string & rtrim ( std : : string & s ) {
s . erase ( std : : find_if ( s . rbegin ( ) , s . rend ( ) , std : : not1 ( std : : ptr_fun < int , int > ( std : : isspace ) ) ) . base ( ) , s . end ( ) ) ;
return s ;
}
std : : string & trim ( std : : string & s ) {
return ltrim ( rtrim ( s ) ) ;
}
2015-02-12 12:59:39 -07:00
std : : string get_current_time ( ) {
std : : chrono : : system_clock : : time_point now = std : : chrono : : system_clock : : now ( ) ;
time_t time_now = std : : chrono : : system_clock : : to_time_t ( now ) ;
std : : chrono : : high_resolution_clock : : duration duration = now . time_since_epoch ( ) ;
int64_t micro = std : : chrono : : duration_cast < std : : chrono : : microseconds > ( duration ) . count ( ) ;
2015-01-05 12:30:17 -07:00
2015-02-12 12:59:39 -07:00
// std::localtime() - This function may not be thread-safe.
# ifdef OS_TYPE_WINDOWS
struct tm * tm_pointer = std : : localtime ( & time_now ) ; // thread-safe on mingw-w64 (thread local variable) and on MSVC btw
// http://stackoverflow.com/questions/18551409/localtime-r-support-on-mingw
// tm_pointer points to thread-local data, memory is owned/managed by the system/library
# else
// linux, freebsd, have this
struct tm tm_object ; // automatic storage duration http://en.cppreference.com/w/cpp/language/storage_duration
struct tm * tm_pointer = & tm_object ; // just point to our data
auto x = localtime_r ( & time_now , tm_pointer ) ; // modifies our own (this thread) data in tm_object, this is safe http://linux.die.net/man/3/localtime_r
if ( x ! = tm_pointer ) return " (internal error in get_current_time) " ; // redundant check in case of broken implementation of localtime_r
# endif
// tm_pointer now points to proper time data, and that memory is automatically managed
if ( ! tm_pointer ) return " (internal error in get_current_time - NULL) " ; // redundant check in case of broken implementation of used library methods
2015-01-05 12:30:17 -07:00
2015-02-12 12:59:39 -07:00
std : : stringstream stream ;
stream < < std : : setfill ( ' 0 ' )
< < std : : setw ( 2 ) < < tm_pointer - > tm_year + 1900
< < ' - ' < < std : : setw ( 2 ) < < tm_pointer - > tm_mon + 1
< < ' - ' < < std : : setw ( 2 ) < < tm_pointer - > tm_mday
< < ' ' < < std : : setw ( 2 ) < < tm_pointer - > tm_hour
< < ' : ' < < std : : setw ( 2 ) < < tm_pointer - > tm_min
< < ' : ' < < std : : setw ( 2 ) < < tm_pointer - > tm_sec
< < ' . ' < < std : : setw ( 6 ) < < ( micro % 1000000 ) ; // 6 because microseconds
return stream . str ( ) ;
2015-01-05 12:30:17 -07:00
}
cNullstream g_nullstream ; // extern a stream that does nothing (eats/discards data)
2016-03-11 05:25:28 -07:00
boost : : recursive_mutex gLoggerGuard ; // extern
2015-02-20 14:28:03 -07:00
std : : atomic < int > gLoggerGuardDepth ; // extern
std : : atomic < int > & gLoggerGuardDepth_Get ( ) {
// TODO std::once would be nicer here
static bool once = 0 ;
if ( ! once ) { // initialize it once
once = 1 ;
gLoggerGuardDepth = 0 ;
}
return gLoggerGuardDepth ; // global, atomic counter
}
2015-01-05 12:30:17 -07:00
// ====================================================================
namespace nDetail {
const char * DbgShortenCodeFileName ( const char * s ) {
const char * p = s ;
const char * a = s ;
bool inc = 1 ;
while ( * p ) {
+ + p ;
if ( inc & & ( ' \0 ' ! = * p ) ) { a = p ; inc = false ; } // point to the current character (if valid) becasue previous one was slash
if ( ( * p ) = = ' / ' ) { a = p ; inc = true ; } // point at current slash (but set inc to try to point to next character)
}
return a ;
}
}
// a workaround for MSVC compiler; e.g. see https://bugs.webkit.org/show_bug.cgi?format=multiple&id=125795
# ifndef _MSC_VER
template < typename T , typename . . . Args >
std : : unique_ptr < T > make_unique ( Args & & . . . args )
{
return std : : unique_ptr < T > ( new T ( std : : forward < Args > ( args ) . . . ) ) ;
}
# else
using std : : make_unique ;
# endif
// ====================================================================
2015-02-24 12:12:56 -07:00
char cFilesystemUtils : : GetDirSeparatorSys ( ) {
2015-01-05 12:30:17 -07:00
// TODO nicer os detection?
# if defined(OS_TYPE_POSIX)
return ' / ' ;
# elif defined(OS_TYPE_WINDOWS)
return ' \\ ' ;
# else
# error "Do not know how to compile this for your platform."
# endif
}
2015-02-24 12:12:56 -07:00
char cFilesystemUtils : : GetDirSeparatorInter ( ) {
return ' / ' ;
}
string cFilesystemUtils : : FileInternalToSystem ( const std : : string & name ) {
string ret ;
ret . resize ( name . size ( ) ) ;
std : : replace_copy ( name . begin ( ) , name . end ( ) , ret . begin ( ) ,
GetDirSeparatorInter ( ) , GetDirSeparatorSys ( ) ) ;
return ret ;
}
string cFilesystemUtils : : FileSystemToInternal ( const std : : string & name ) {
string ret ;
ret . reserve ( name . size ( ) ) ;
std : : replace_copy ( name . begin ( ) , name . end ( ) , ret . begin ( ) ,
GetDirSeparatorSys ( ) , GetDirSeparatorInter ( ) ) ;
return ret ;
}
2015-01-05 12:30:17 -07:00
bool cFilesystemUtils : : CreateDirTree ( const std : : string & dir , bool only_below ) {
const bool dbg = false ;
//struct stat st;
2015-02-24 12:12:56 -07:00
const char dirchS = cFilesystemUtils : : GetDirSeparatorSys ( ) ;
const char dirchI = cFilesystemUtils : : GetDirSeparatorInter ( ) ;
2015-01-05 12:30:17 -07:00
std : : istringstream iss ( dir ) ;
2015-02-24 12:12:56 -07:00
string partI ; // current par is in internal format (though it should not matter since it doesn't contain any slashes). eg "bar"
string sofarS = " " ; // sofarS - the so far created dir part is in SYSTEM format. eg "foo/bar"
2015-01-05 12:30:17 -07:00
if ( dir . size ( ) < 1 ) return false ; // illegal name
// dir[0] is valid from here
2015-02-24 12:12:56 -07:00
if ( only_below & & ( ( dir [ 0 ] = = dirchS ) | | ( dir [ 0 ] = = dirchI ) ) ) return false ; // no jumping to top (on any os)
while ( getline ( iss , partI , dirchI ) ) { // get new component eg "bar" into part
if ( dbg ) cout < < ' [ ' < < partI < < ' ] ' < < endl ;
sofarS + = partI ;
if ( partI . size ( ) < 1 ) return false ; // bad format?
if ( ( only_below ) & & ( partI = = " .. " ) ) return false ; // trying to go up
if ( dbg ) cout < < " test [ " < < sofarS < < " ] " < < endl ;
2015-01-05 12:30:17 -07:00
// TODO nicer os detection?
# if defined(OS_TYPE_POSIX)
struct stat st ;
2015-02-24 12:12:56 -07:00
bool exists = stat ( sofarS . c_str ( ) , & st ) = = 0 ; // *
2015-01-05 12:30:17 -07:00
if ( exists ) {
if ( ! S_ISDIR ( st . st_mode ) ) {
// std::cerr << "This exists, but as a file: [" << sofar << "]" << (size_t)st.st_ino << endl;
return false ; // exists but is a file nor dir
}
}
# elif defined(OS_TYPE_WINDOWS)
2015-02-24 12:12:56 -07:00
DWORD dwAttrib = GetFileAttributesA ( sofarS . c_str ( ) ) ;
2015-01-05 12:30:17 -07:00
bool exists = ( dwAttrib ! = INVALID_FILE_ATTRIBUTES & & ( dwAttrib & FILE_ATTRIBUTE_DIRECTORY ) ) ;
# else
# error "Do not know how to compile this for your platform."
# endif
if ( ! exists ) {
2015-02-24 12:12:56 -07:00
if ( dbg ) cout < < " mkdir [ " < < sofarS < < " ] " < < endl ;
2015-01-05 12:30:17 -07:00
# if defined(OS_TYPE_POSIX)
2015-02-24 12:12:56 -07:00
bool ok = 0 = = mkdir ( sofarS . c_str ( ) , 0700 ) ; // ***
2015-01-05 12:30:17 -07:00
# elif defined(OS_TYPE_WINDOWS)
2015-02-24 12:12:56 -07:00
bool ok = ( bool ) CreateDirectoryA ( sofarS . c_str ( ) , NULL ) ; // TODO use -W() after conversion to unicode UTF16
2015-01-05 12:30:17 -07:00
# else
# error "Do not know how to compile this for your platform."
# endif
if ( ! ok ) return false ;
}
2015-02-24 12:12:56 -07:00
sofarS + = dirchS ;
2015-01-05 12:30:17 -07:00
}
return true ;
}
// ====================================================================
namespace nDetail {
2015-04-01 11:00:45 -06:00
struct channel_use_info { ///< feedback information about using (e.g. opening) given debug channel - used internally by logging system
/// TODO not yet used in code
/// e.g. used to write into channel net/in/all that given message was a first logged message from never-before-logged thread or PID etc
bool m_was_interesting ; ///< anything interesting happened when using the channel?
std : : vector < std : : string > m_extra_msg ; ///< any additional messages about this channel use
} ;
2015-01-05 12:30:17 -07:00
cDebugScopeGuard : : cDebugScopeGuard ( ) : mLevel ( - 1 ) {
}
2015-02-24 12:12:56 -07:00
2015-01-05 12:30:17 -07:00
cDebugScopeGuard : : ~ cDebugScopeGuard ( ) {
if ( mLevel ! = - 1 ) {
gCurrentLogger . write_stream ( mLevel , mChan ) < < mMsg < < " ... end " < < gCurrentLogger . endline ( ) < < std : : flush ;
}
}
void cDebugScopeGuard : : Assign ( const string & chan , const int level , const string & msg ) {
mChan = chan ;
mLevel = level ;
mMsg = msg ;
}
2015-02-12 12:59:39 -07:00
} // namespace nDetail
2015-01-05 12:30:17 -07:00
// ====================================================================
cLogger : : cLogger ( ) :
mStream ( NULL ) ,
2015-02-24 12:12:56 -07:00
mStreamBrokenDebug ( NULL ) ,
mIsBroken ( true ) , // before constructor finishes
2015-04-02 05:58:38 -06:00
mLevel ( _debug_level_nr_warn ) ,
2015-04-01 11:00:45 -06:00
mThread2Number_Biggest ( 0 ) , // the CURRENT biggest value (no thread yet in map)
mPid2Number_Biggest ( 0 )
2015-01-05 12:30:17 -07:00
{
mStream = & std : : cout ;
2015-04-01 11:00:45 -06:00
mStreamBrokenDebug = & std : : cerr ; // the backup stream
* mStreamBrokenDebug < < " Creating the logger system " < < endl ;
2015-02-24 12:12:56 -07:00
mIsBroken = false ; // ok, constr. succeeded, so string is not broken now
2015-04-01 11:00:45 -06:00
// this is here, because it could be using logging itself to log creation of first thread/PID etc
2016-03-11 05:25:28 -07:00
Thread2Number ( boost : : this_thread : : get_id ( ) ) ; // convert current id to short number, useful to reserve a number so that main thread is usually called 1
2015-04-01 11:00:45 -06:00
Pid2Number ( getpid ( ) ) ; // add this proces ID as first one
2015-01-05 12:30:17 -07:00
}
cLogger : : ~ cLogger ( ) {
for ( auto pair : mChannels ) {
std : : ofstream * ptr = pair . second ;
delete ptr ;
pair . second = NULL ;
}
}
2015-02-24 12:12:56 -07:00
void cLogger : : SetStreamBroken ( ) {
SetStreamBroken ( " (no additional details about this problem) " ) ;
}
void cLogger : : SetStreamBroken ( const std : : string & msg ) {
2015-04-01 11:00:45 -06:00
_dbg_dbg ( " Stream is broken (msg: " < < msg < < " ) " ) ;
2015-02-24 12:12:56 -07:00
if ( ! mIsBroken ) { // if not already marked as broken
2015-04-01 11:00:45 -06:00
_dbg_dbg ( " (It was not broken before) " ) ;
2015-04-02 08:27:19 -06:00
std : : cerr < < OT_CODE_STAMP < < " WARNING: due to a problem in the debug/logging system itself ( " < < msg < < " ) - we are switching back to fallback stream (e.g. cerr) " < < std : : endl ;
2015-02-24 12:12:56 -07:00
if ( mStreamBrokenDebug = = nullptr ) {
2015-04-02 08:27:19 -06:00
std : : cerr < < OT_CODE_STAMP < < " ERROR: in addition, while reporting this problem, mStreamBrokenDebug stream is NULL: " < < mStreamBrokenDebug < < std : : endl ;
2015-02-24 12:12:56 -07:00
} else {
( * mStreamBrokenDebug ) < < OT_CODE_STAMP < < " WARNING: due to debug stream problem ( " < < msg < < " ) - switching back to fallback stream (e.g. cerr) " < < std : : endl ;
}
mIsBroken = true ;
}
}
2015-01-05 12:30:17 -07:00
std : : ostream & cLogger : : write_stream ( int level ) {
return write_stream ( level , " " ) ;
}
std : : ostream & cLogger : : write_stream ( int level , const std : : string & channel ) {
2015-04-01 11:00:45 -06:00
_dbg_dbg ( " level= " < < level < < " channel= " < < channel ) ;
if ( level > = mLevel ) {
if ( mStream ) { // TODO now disabling mStream also disables writting to any channel
_dbg_dbg ( " Selecting output... " ) ;
ostream & output = SelectOutput ( level , channel ) ;
_dbg_dbg ( " Selecting output... done, output= " < < ( void * ) ( & output ) ) ;
# if defined(OS_TYPE_WINDOWS)
output < < windows_stream ( level ) ;
# endif
output < < icon ( level ) < < ' ' ;
2016-03-11 05:25:28 -07:00
boost : : thread : : id this_id = boost : : this_thread : : get_id ( ) ;
2015-04-01 11:00:45 -06:00
output < < " { " < < Thread2Number ( this_id ) < < " } " ;
auto nicePid = Pid2Number ( getpid ( ) ) ;
if ( nicePid > 0 ) output < < " {p " < < nicePid < < " } " ;
output < < ' ' ;
return output ; // <--- return
} else _dbg_dbg ( " Not writting: No mStream " ) ;
} else _dbg_dbg ( " Not writting: Too low level level= " < < level < < " not >= mLevel= " < < mLevel ) ;
2015-01-05 12:30:17 -07:00
return g_nullstream ;
}
std : : string cLogger : : GetLogBaseDir ( ) const {
return " log " ;
}
2015-02-24 12:12:56 -07:00
void cLogger : : OpenNewChannel ( const std : : string & channel ) noexcept {
try {
2015-04-01 11:00:45 -06:00
_dbg_dbg ( " Openning channel for channel= " < < channel ) ;
2015-02-24 12:12:56 -07:00
OpenNewChannel_ ( channel ) ;
}
catch ( const std : : exception & except ) {
SetStreamBroken ( OT_CODE_STAMP + " Got exception when opening debug channel: " + ToStr ( except . what ( ) ) ) ;
}
catch ( . . . ) {
SetStreamBroken ( OT_CODE_STAMP + " Got not-standard exception when opening debug channel. " ) ;
}
2015-01-05 12:30:17 -07:00
}
2015-02-24 12:12:56 -07:00
void cLogger : : OpenNewChannel_ ( const std : : string & channel ) { // channel=="net/sleep"
2015-04-01 11:00:45 -06:00
_dbg_dbg ( " Openning channel for channel= " < < channel ) ;
2015-02-24 12:12:56 -07:00
size_t last_split = channel . find_last_of ( cFilesystemUtils : : GetDirSeparatorInter ( ) ) ;
string fname_system ; // the full file name in system format
if ( last_split = = string : : npos ) { // The channel name has no directory, eg channel=="test"
string dir = GetLogBaseDir ( ) ;
string basefile = channel + " .log " ;
string fname = dir + cFilesystemUtils : : GetDirSeparatorInter ( ) + basefile ;
fname_system = cFilesystemUtils : : FileInternalToSystem ( fname ) ; // <-
2015-01-05 12:30:17 -07:00
}
2015-02-24 12:12:56 -07:00
else { // there is a directory eg channel=="net/sleep"
// net/sleep
// ^----- last_split
string dir = GetLogBaseDir ( ) + cFilesystemUtils : : GetDirSeparatorInter ( ) + channel . substr ( 0 , last_split ) ;
string basefile = channel . substr ( last_split + 1 ) + " .log " ;
string fname = dir + cFilesystemUtils : : GetDirSeparatorInter ( ) + basefile ;
fname_system = cFilesystemUtils : : FileInternalToSystem ( fname ) ; // <-
bool dirok = cFilesystemUtils : : CreateDirTree ( dir ) ;
if ( ! dirok ) { string err = " In logger failed to open directory ( " + dir + " ) for channel ( " + channel + " ) " ; throw std : : runtime_error ( err ) ; }
2015-01-05 12:30:17 -07:00
}
2015-02-24 12:12:56 -07:00
2015-04-01 11:00:45 -06:00
_dbg_dbg ( " Openning fname_system= " < < fname_system ) ;
2015-02-24 12:12:56 -07:00
std : : ofstream * thefile = new std : : ofstream ( fname_system . c_str ( ) ) ; // file system
* thefile < < " ====== Log opened: " < < fname_system < < " (in " < < ( ( void * ) thefile ) < < " ) ====== " < < endl ;
2015-04-02 08:27:19 -06:00
// cerr << "====== Log opened: " << fname_system << " (in " << ((void*)thefile) << ") ======" << endl;
2015-04-01 11:00:45 -06:00
_dbg_dbg ( " ====== Log opened: " < < fname_system < < " (in " < < ( ( void * ) thefile ) < < " ) ====== " ) ;
2015-02-24 12:12:56 -07:00
mChannels . insert ( std : : pair < string , std : : ofstream * > ( channel , thefile ) ) ; // <- created the channel mapping
2015-01-05 12:30:17 -07:00
}
2015-02-24 12:12:56 -07:00
std : : ostream & cLogger : : SelectOutput ( int level , const std : : string & channel ) noexcept {
try {
2015-04-01 11:00:45 -06:00
if ( mIsBroken ) {
_dbg_dbg ( " The stream is broken mIsBroken= " < < mIsBroken < < " so will return backup stream " ) ;
return * mStreamBrokenDebug ;
}
if ( channel = = " " ) {
_dbg_dbg ( " No channel given (channel= " < < channel < < " ) so will return main stream " ) ;
return * mStream ;
}
2015-02-24 12:12:56 -07:00
auto obj = mChannels . find ( channel ) ;
if ( obj = = mChannels . end ( ) ) { // not found - need to make new channel
2015-04-01 11:00:45 -06:00
_dbg_dbg ( " No stream openened for channel= " < < channel < < " so will create it now " ) ;
2015-02-24 12:12:56 -07:00
OpenNewChannel ( channel ) ; // <- create channel
obj = mChannels . find ( channel ) ; // find again
if ( obj = = mChannels . end ( ) ) { // still not found! something is wrong
SetStreamBroken ( OT_CODE_STAMP + " WARNING: can not get stream for channel= " + ToStr ( channel ) + " level= " + ToStr ( channel ) ) ;
return * mStreamBrokenDebug ;
}
}
auto the_stream_ptr = obj - > second ;
2015-04-01 11:00:45 -06:00
_dbg_dbg ( " Found the stream file for channel= " < < channel < < " as the_stream_ptr= " < < the_stream_ptr ) ;
2015-02-24 12:12:56 -07:00
ASRT ( the_stream_ptr ) ;
return * the_stream_ptr ; // <--- RETURN
}
catch ( std : : exception & except ) {
SetStreamBroken ( OT_CODE_STAMP + " Got exception: " + ToStr ( except . what ( ) ) ) ;
2015-04-01 11:00:45 -06:00
_dbg_dbg ( " Exception! Returning broken stream " ) ;
2015-02-24 12:12:56 -07:00
return * mStreamBrokenDebug ;
}
catch ( . . . ) {
SetStreamBroken ( OT_CODE_STAMP + " Got not-standard exception. " ) ;
2015-04-01 11:00:45 -06:00
_dbg_dbg ( " Exception! Returning broken stream " ) ;
2015-02-24 12:12:56 -07:00
return * mStreamBrokenDebug ;
}
// dead code
}
2015-01-05 12:30:17 -07:00
void cLogger : : setOutStreamFile ( const string & fname ) { // switch to using this file
_mark ( " WILL SWITCH DEBUG NOW to file: " < < fname ) ;
mOutfile = make_unique < std : : ofstream > ( fname ) ;
mStream = & ( * mOutfile ) ;
_mark ( " Started new debug, to file: " < < fname ) ;
}
void cLogger : : setOutStreamFromGlobalOptions ( ) {
if ( gRunOptions . getDebug ( ) ) {
if ( gRunOptions . getDebugSendToFile ( ) ) {
mOutfile = make_unique < std : : ofstream > ( " debuglog.txt " ) ;
mStream = & ( * mOutfile ) ;
}
else if ( gRunOptions . getDebugSendToCerr ( ) ) {
mStream = & std : : cerr ;
}
else {
mStream = & g_nullstream ;
}
}
else {
mStream = & g_nullstream ;
}
}
void cLogger : : setDebugLevel ( int level ) {
bool note_before = ( mLevel > level ) ; // report the level change before or after the change? (on higher level)
if ( note_before ) _note ( " Setting debug level to " < < level ) ;
mLevel = level ;
if ( ! note_before ) _note ( " Setting debug level to " < < level ) ;
}
std : : string cLogger : : icon ( int level ) const {
// TODO replan to avoid needles converting back and forth char*, string etc
using namespace zkr ;
2015-04-01 11:00:45 -06:00
# if defined(OS_TYPE_POSIX)
if ( level > = 100 ) return cc : : back : : lightred + ToStr ( cc : : fore : : lightyellow ) + ToStr ( " ERROR " ) + ToStr ( cc : : fore : : lightyellow ) + " " ;
2015-01-05 12:30:17 -07:00
if ( level > = 90 ) return cc : : back : : lightyellow + ToStr ( cc : : fore : : black ) + ToStr ( " Warn " ) + ToStr ( cc : : fore : : red ) + " " ;
if ( level > = 80 ) return cc : : back : : lightmagenta + ToStr ( cc : : fore : : black ) + ToStr ( " MARK " ) ; //+ zkr::cc::console + ToStr(cc::fore::lightmagenta)+ " ";
2015-04-01 11:00:45 -06:00
if ( level > = 75 ) return cc : : back : : lightyellow + ToStr ( cc : : fore : : black ) + ToStr ( " FACT " ) + zkr : : cc : : console + ToStr ( cc : : fore : : lightyellow ) + " " ;
2015-01-05 12:30:17 -07:00
if ( level > = 70 ) return cc : : fore : : green + ToStr ( " Note " ) ;
if ( level > = 50 ) return cc : : fore : : cyan + ToStr ( " info " ) ;
if ( level > = 40 ) return cc : : fore : : lightwhite + ToStr ( " dbg " ) ;
if ( level > = 30 ) return cc : : fore : : lightblue + ToStr ( " dbg " ) ;
if ( level > = 20 ) return cc : : fore : : blue + ToStr ( " dbg " ) ;
2015-04-01 11:00:45 -06:00
# elif defined(OS_TYPE_WINDOWS)
if ( level > = 100 ) return ToStr ( " ERROR " ) ;
if ( level > = 90 ) return ToStr ( " Warn " ) ;
if ( level > = 80 ) return ToStr ( " MARK " ) ;
if ( level > = 75 ) return ToStr ( " FACT " ) ;
if ( level > = 70 ) return ToStr ( " Note " ) ;
if ( level > = 50 ) return ToStr ( " info " ) ;
if ( level > = 40 ) return ToStr ( " dbg " ) ;
if ( level > = 30 ) return ToStr ( " dbg " ) ;
if ( level > = 20 ) return ToStr ( " dbg " ) ;
# endif
2015-01-05 12:30:17 -07:00
return " " ;
}
std : : string cLogger : : endline ( ) const {
2015-04-01 11:00:45 -06:00
# if defined(OS_TYPE_POSIX)
2015-01-05 12:30:17 -07:00
return ToStr ( " " ) + zkr : : cc : : console + ToStr ( " \n " ) ; // TODO replan to avoid needles converting back and forth char*, string etc
2015-04-01 11:00:45 -06:00
# elif defined(OS_TYPE_WINDOWS)
return ToStr ( " \n " ) ;
# endif
2015-01-05 12:30:17 -07:00
}
2016-03-11 05:25:28 -07:00
int cLogger : : Thread2Number ( const boost : : thread : : id id ) {
2015-01-05 12:30:17 -07:00
auto found = mThread2Number . find ( id ) ;
if ( found = = mThread2Number . end ( ) ) { // new one
mThread2Number_Biggest + + ;
mThread2Number [ id ] = mThread2Number_Biggest ;
2015-04-02 08:27:19 -06:00
_info_c ( " dbg/main " , " This is a new thread (used in debug), thread id= " < < id ) ; // can cause some recursion
2015-01-05 12:30:17 -07:00
return mThread2Number_Biggest ;
} else {
return mThread2Number [ id ] ;
}
}
2015-04-01 11:00:45 -06:00
int cLogger : : Pid2Number ( const t_anypid id ) {
auto found = mPid2Number . find ( id ) ;
if ( found = = mPid2Number . end ( ) ) { // new one
mPid2Number_Biggest + + ;
mPid2Number [ id ] = mPid2Number_Biggest ;
2015-04-02 08:27:19 -06:00
_info_c ( " dbg/main " , " This is a new process (used in debug), process pid= " < < id ) ; // can cause some recursion
2015-04-01 11:00:45 -06:00
return mPid2Number_Biggest ;
} else {
return mPid2Number [ id ] ;
}
}
2015-01-05 12:30:17 -07:00
// ====================================================================
// object gCurrentLogger is defined later - in global namespace below
// ====================================================================
// vector debug
void DisplayStringEndl ( std : : ostream & out , const std : : string text ) {
out < < text ;
out < < std : : endl ;
}
std : : string SpaceFromEscape ( const std : : string & s ) {
std : : ostringstream newStr ;
for ( size_t i = 0 ; i < s . length ( ) ; i + + ) {
if ( s [ i ] = = ' \\ ' & & s [ i + 1 ] = = 32 )
newStr < < " " ;
else
newStr < < s [ i ] ;
}
return newStr . str ( ) ;
}
std : : string EscapeFromSpace ( const std : : string & s ) {
std : : ostringstream newStr ;
for ( size_t i = 0 ; i < s . length ( ) ; i + + ) {
if ( s [ i ] = = 32 )
newStr < < " \\ " < < " " ;
else
newStr < < s [ i ] ;
}
return newStr . str ( ) ;
}
std : : string EscapeString ( const std : : string & s ) {
std : : ostringstream newStr ;
for ( size_t i = 0 ; i < s . length ( ) ; i + + ) {
if ( s [ i ] > = 32 & & s [ i ] < = 126 )
newStr < < s [ i ] ;
else
newStr < < " \\ " < < ( int ) s [ i ] ;
}
return newStr . str ( ) ;
}
bool CheckIfBegins ( const std : : string & beggining , const std : : string & all ) {
if ( all . compare ( 0 , beggining . length ( ) , beggining ) = = 0 ) {
return 1 ;
}
else {
return 0 ;
}
}
bool CheckIfEnds ( std : : string const & ending , std : : string const & all ) {
if ( all . length ( ) > = ending . length ( ) ) {
return ( 0 = = all . compare ( all . length ( ) - ending . length ( ) , ending . length ( ) , ending ) ) ;
} else {
return false ;
}
}
vector < string > WordsThatMatch ( const std : : string & sofar , const vector < string > & possib ) {
vector < string > ret ;
for ( auto rec : possib ) { // check of possibilities
if ( CheckIfBegins ( sofar , rec ) ) {
rec = EscapeFromSpace ( rec ) ;
ret . push_back ( rec ) ; // this record matches
}
}
return ret ;
}
char GetLastChar ( const std : : string & str ) { // TODO unicode?
auto s = str . length ( ) ;
if ( s = = 0 ) throw std : : runtime_error ( " Getting last character of empty string ( " + ToStr ( s ) + " ) " + OT_CODE_STAMP ) ;
return str . at ( s - 1 ) ;
}
std : : string GetLastCharIf ( const std : : string & str ) { // TODO unicode?
auto s = str . length ( ) ;
if ( s = = 0 ) return " " ; // empty string signalizes ther is nothing to be returned
return std : : string ( 1 , str . at ( s - 1 ) ) ;
}
// ====================================================================
// ASRT - assert. Name like ASSERT() was too long, and ASS() was just... no.
// Use it like this: ASRT( x>y ); with the semicolon at end, a clever trick forces this syntax :)
void Assert ( bool result , const std : : string & stamp , const std : : string & condition ) {
if ( ! result ) {
_erro ( " Assert failed at " + stamp + " : ASSERT( " < < condition < < " ) " ) ;
throw std : : runtime_error ( " Assert failed at " + stamp + " : ASSERT( " + condition + " ) " ) ;
}
}
// ====================================================================
// advanced string
const std : : string GetMultiline ( string endLine ) {
std : : string result ( " " ) ; // Taken from OT_CLI_ReadUntilEOF
while ( true ) {
std : : string input_line ( " " ) ;
if ( std : : getline ( std : : cin , input_line , ' \n ' ) )
{
input_line + = " \n " ;
if ( input_line [ 0 ] = = ' ~ ' )
break ;
result + = input_line ;
}
if ( std : : cin . eof ( ) )
{
std : : cin . clear ( ) ;
break ;
}
if ( std : : cin . fail ( ) )
{
std : : cin . clear ( ) ;
break ;
}
if ( std : : cin . bad ( ) )
{
std : : cin . clear ( ) ;
break ;
}
}
return result ;
}
vector < string > SplitString ( const string & str ) {
std : : istringstream iss ( str ) ;
vector < string > vec { std : : istream_iterator < string > { iss } , std : : istream_iterator < string > { } } ;
return vec ;
}
bool checkPrefix ( const string & str , char prefix ) {
if ( str . at ( 0 ) = = prefix )
return true ;
return false ;
}
// ====================================================================
// operation on files
# ifdef __unix
void cEnvUtils : : GetTmpTextFile ( ) {
// TODO make this name configurable (depending on project)
char filename [ ] = " /tmp/otshellutils_text.XXXXXX " ;
fd = mkstemp ( filename ) ;
if ( fd = = - 1 ) {
_erro ( " Can't create the file: " < < filename ) ;
return ;
}
mFilename = filename ;
}
void cEnvUtils : : CloseFile ( ) {
close ( fd ) ;
unlink ( mFilename . c_str ( ) ) ;
}
void cEnvUtils : : OpenEditor ( ) {
char * editor = std : : getenv ( " OT_EDITOR " ) ; //TODO Read editor from configuration file
if ( editor = = NULL )
editor = std : : getenv ( " VISUAL " ) ;
if ( editor = = NULL )
editor = std : : getenv ( " EDITOR " ) ;
string command ;
if ( editor ! = NULL )
command = ToStr ( editor ) + " " + mFilename ;
else
command = " /usr/bin/editor " + mFilename ;
_dbg3 ( " Opening editor with command: " < < command ) ;
if ( system ( command . c_str ( ) ) = = - 1 )
_erro ( " Cannot execute system command: " < < command ) ;
}
const string cEnvUtils : : ReadFromTmpFile ( ) {
std : : ifstream ifs ( mFilename ) ;
string msg ( ( std : : istreambuf_iterator < char > ( ifs ) ) , std : : istreambuf_iterator < char > ( ) ) ;
return msg ;
}
const string cEnvUtils : : Compose ( ) {
GetTmpTextFile ( ) ;
OpenEditor ( ) ;
string input = ReadFromTmpFile ( ) ;
CloseFile ( ) ;
return input ;
}
# endif
const string cEnvUtils : : ReadFromFile ( const string path ) {
std : : ifstream ifs ( path ) ;
string msg ( ( std : : istreambuf_iterator < char > ( ifs ) ) , std : : istreambuf_iterator < char > ( ) ) ;
return msg ;
}
void hintingToTxt ( std : : fstream & file , string command , vector < string > & commands ) {
if ( file . good ( ) ) {
file < < command < < " ~ " < < endl ;
for ( auto a : commands ) {
file < < a < < " " ;
file . flush ( ) ;
}
file < < endl ;
}
}
string stringToColor ( const string & hash ) {
// Generete vector with all possible light colors
vector < string > lightColors ;
using namespace zkr ;
lightColors . push_back ( cc : : fore : : lightblue ) ;
lightColors . push_back ( cc : : fore : : lightred ) ;
lightColors . push_back ( cc : : fore : : lightmagenta ) ;
lightColors . push_back ( cc : : fore : : lightgreen ) ;
lightColors . push_back ( cc : : fore : : lightcyan ) ;
lightColors . push_back ( cc : : fore : : lightyellow ) ;
lightColors . push_back ( cc : : fore : : lightwhite ) ;
int sum = 0 ;
for ( auto ch : hash ) sum + = ch ;
auto color = sum % ( lightColors . size ( ) - 1 ) ;
return lightColors . at ( color ) ;
}
// ====================================================================
// algorthms
2015-02-12 12:59:39 -07:00
} // namespace nUtil
2015-01-05 12:30:17 -07:00
2015-02-12 12:59:39 -07:00
} // namespace OT
2015-01-05 12:30:17 -07:00
// global namespace
const extern int _dbg_ignore = 0 ; // see description in .hpp
std : : string GetObjectName ( ) {
//static std::string * name=nullptr;
//if (!name) name = new std::string("(global)");
return " " ;
}
// ====================================================================
nOT : : nUtils : : cLogger gCurrentLogger ;