2011-05-14 17:25:05 +02:00
// Copyright (c) 2009-2010 Satoshi Nakamoto
2012-02-07 17:28:30 +01:00
// Copyright (c) 2009-2012 The Bitcoin developers
2011-05-14 17:25:05 +02:00
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
2012-03-31 15:22:45 +02:00
2012-04-15 22:10:54 +02:00
# include "util.h"
2012-05-11 17:00:03 +02:00
# include "sync.h"
2011-06-18 18:46:01 +02:00
# include "strlcpy.h"
2012-04-15 22:10:54 +02:00
# include "version.h"
2012-04-16 14:56:45 +02:00
# include "ui_interface.h"
2011-12-16 22:26:14 +01:00
# include <boost/algorithm/string/join.hpp>
2012-04-15 11:42:40 +02:00
// Work around clang compilation problem in Boost 1.46:
// /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup
// See also: http://stackoverflow.com/questions/10020179/compilation-fail-in-boost-librairies-program-options
// http://clang.debian.net/status.php?version=3.0&key=CANNOT_FIND_FUNCTION
namespace boost {
namespace program_options {
std : : string to_internal ( const std : : string & ) ;
}
}
2011-06-18 18:46:01 +02:00
# include <boost/program_options/detail/config_file.hpp>
# include <boost/program_options/parsers.hpp>
2011-06-26 19:23:24 +02:00
# include <boost/filesystem.hpp>
2011-06-18 18:46:01 +02:00
# include <boost/filesystem/fstream.hpp>
# include <boost/foreach.hpp>
2012-04-15 22:10:54 +02:00
# include <openssl/crypto.h>
# include <openssl/rand.h>
# ifdef WIN32
# ifdef _MSC_VER
# pragma warning(disable:4786)
# pragma warning(disable:4804)
# pragma warning(disable:4805)
# pragma warning(disable:4717)
# endif
# ifdef _WIN32_WINNT
# undef _WIN32_WINNT
# endif
# define _WIN32_WINNT 0x0501
# ifdef _WIN32_IE
# undef _WIN32_IE
# endif
2012-05-03 02:21:43 +02:00
# define _WIN32_IE 0x0501
2012-04-15 22:10:54 +02:00
# define WIN32_LEAN_AND_MEAN 1
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include "shlobj.h"
# endif
2011-05-14 17:25:05 +02:00
using namespace std ;
map < string , string > mapArgs ;
map < string , vector < string > > mapMultiArgs ;
bool fDebug = false ;
bool fPrintToConsole = false ;
bool fPrintToDebugger = false ;
bool fRequestShutdown = false ;
bool fShutdown = false ;
bool fDaemon = false ;
bool fServer = false ;
bool fCommandLine = false ;
string strMiscWarning ;
bool fTestNet = false ;
bool fNoListen = false ;
bool fLogTimestamps = false ;
2011-12-21 22:33:19 +01:00
CMedianFilter < int64 > vTimeOffsets ( 200 , 0 ) ;
2011-05-14 17:25:05 +02:00
// Init openssl library multithreading support
2012-05-11 17:00:03 +02:00
static CCriticalSection * * ppmutexOpenSSL ;
2011-05-14 17:25:05 +02:00
void locking_callback ( int mode , int i , const char * file , int line )
{
2012-05-11 17:00:03 +02:00
if ( mode & CRYPTO_LOCK ) {
ENTER_CRITICAL_SECTION ( * ppmutexOpenSSL [ i ] ) ;
} else {
LEAVE_CRITICAL_SECTION ( * ppmutexOpenSSL [ i ] ) ;
}
2011-05-14 17:25:05 +02:00
}
// Init
class CInit
{
public :
CInit ( )
{
// Init openssl library multithreading support
2012-05-11 17:00:03 +02:00
ppmutexOpenSSL = ( CCriticalSection * * ) OPENSSL_malloc ( CRYPTO_num_locks ( ) * sizeof ( CCriticalSection * ) ) ;
2011-05-14 17:25:05 +02:00
for ( int i = 0 ; i < CRYPTO_num_locks ( ) ; i + + )
2012-05-11 17:00:03 +02:00
ppmutexOpenSSL [ i ] = new CCriticalSection ( ) ;
2011-05-14 17:25:05 +02:00
CRYPTO_set_locking_callback ( locking_callback ) ;
2011-10-07 17:02:21 +02:00
# ifdef WIN32
2011-05-14 17:25:05 +02:00
// Seed random number generator with screen scrape and other hardware sources
RAND_screen ( ) ;
# endif
// Seed random number generator with performance counter
RandAddSeed ( ) ;
}
~ CInit ( )
{
// Shutdown openssl library multithreading support
CRYPTO_set_locking_callback ( NULL ) ;
for ( int i = 0 ; i < CRYPTO_num_locks ( ) ; i + + )
delete ppmutexOpenSSL [ i ] ;
OPENSSL_free ( ppmutexOpenSSL ) ;
}
}
instance_of_cinit ;
void RandAddSeed ( )
{
// Seed with CPU performance counter
2011-12-21 22:33:19 +01:00
int64 nCounter = GetPerformanceCounter ( ) ;
2011-05-14 17:25:05 +02:00
RAND_add ( & nCounter , sizeof ( nCounter ) , 1.5 ) ;
memset ( & nCounter , 0 , sizeof ( nCounter ) ) ;
}
void RandAddSeedPerfmon ( )
{
RandAddSeed ( ) ;
// This can take up to 2 seconds, so only do it every 10 minutes
2011-12-21 22:33:19 +01:00
static int64 nLastPerfmon ;
2011-05-14 17:25:05 +02:00
if ( GetTime ( ) < nLastPerfmon + 10 * 60 )
return ;
nLastPerfmon = GetTime ( ) ;
2011-10-07 17:02:21 +02:00
# ifdef WIN32
2011-05-14 17:25:05 +02:00
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
// Seed with the entire set of perfmon data
unsigned char pdata [ 250000 ] ;
memset ( pdata , 0 , sizeof ( pdata ) ) ;
unsigned long nSize = sizeof ( pdata ) ;
long ret = RegQueryValueExA ( HKEY_PERFORMANCE_DATA , " Global " , NULL , NULL , pdata , & nSize ) ;
RegCloseKey ( HKEY_PERFORMANCE_DATA ) ;
if ( ret = = ERROR_SUCCESS )
{
RAND_add ( pdata , nSize , nSize / 100.0 ) ;
memset ( pdata , 0 , nSize ) ;
printf ( " %s RandAddSeed() %d bytes \n " , DateTimeStrFormat ( " %x %H:%M " , GetTime ( ) ) . c_str ( ) , nSize ) ;
}
# endif
}
2011-12-21 22:33:19 +01:00
uint64 GetRand ( uint64 nMax )
2011-05-14 17:25:05 +02:00
{
if ( nMax = = 0 )
return 0 ;
// The range of the random source must be a multiple of the modulus
// to give every possible output value an equal possibility
2011-12-21 22:33:19 +01:00
uint64 nRange = ( std : : numeric_limits < uint64 > : : max ( ) / nMax ) * nMax ;
uint64 nRand = 0 ;
2011-05-14 17:25:05 +02:00
do
RAND_bytes ( ( unsigned char * ) & nRand , sizeof ( nRand ) ) ;
while ( nRand > = nRange ) ;
return ( nRand % nMax ) ;
}
int GetRandInt ( int nMax )
{
return GetRand ( nMax ) ;
}
inline int OutputDebugStringF ( const char * pszFormat , . . . )
{
int ret = 0 ;
if ( fPrintToConsole )
{
// print to console
va_list arg_ptr ;
va_start ( arg_ptr , pszFormat ) ;
ret = vprintf ( pszFormat , arg_ptr ) ;
va_end ( arg_ptr ) ;
}
else
{
// print to debug.log
static FILE * fileout = NULL ;
if ( ! fileout )
{
2012-04-09 23:50:56 +02:00
boost : : filesystem : : path pathDebug = GetDataDir ( ) / " debug.log " ;
fileout = fopen ( pathDebug . string ( ) . c_str ( ) , " a " ) ;
2011-05-14 17:25:05 +02:00
if ( fileout ) setbuf ( fileout , NULL ) ; // unbuffered
}
if ( fileout )
{
static bool fStartedNewLine = true ;
// Debug print useful for profiling
if ( fLogTimestamps & & fStartedNewLine )
fprintf ( fileout , " %s " , DateTimeStrFormat ( " %x %H:%M:%S " , GetTime ( ) ) . c_str ( ) ) ;
if ( pszFormat [ strlen ( pszFormat ) - 1 ] = = ' \n ' )
fStartedNewLine = true ;
else
fStartedNewLine = false ;
va_list arg_ptr ;
va_start ( arg_ptr , pszFormat ) ;
ret = vfprintf ( fileout , pszFormat , arg_ptr ) ;
va_end ( arg_ptr ) ;
}
}
2011-10-07 17:02:21 +02:00
# ifdef WIN32
2011-05-14 17:25:05 +02:00
if ( fPrintToDebugger )
{
static CCriticalSection cs_OutputDebugStringF ;
// accumulate a line at a time
{
2012-04-06 18:39:12 +02:00
LOCK ( cs_OutputDebugStringF ) ;
2011-05-14 17:25:05 +02:00
static char pszBuffer [ 50000 ] ;
static char * pend ;
if ( pend = = NULL )
pend = pszBuffer ;
va_list arg_ptr ;
va_start ( arg_ptr , pszFormat ) ;
int limit = END ( pszBuffer ) - pend - 2 ;
int ret = _vsnprintf ( pend , limit , pszFormat , arg_ptr ) ;
va_end ( arg_ptr ) ;
if ( ret < 0 | | ret > = limit )
{
pend = END ( pszBuffer ) - 2 ;
* pend + + = ' \n ' ;
}
else
pend + = ret ;
* pend = ' \0 ' ;
char * p1 = pszBuffer ;
char * p2 ;
2012-05-01 01:46:03 +02:00
while ( ( p2 = strchr ( p1 , ' \n ' ) ) )
2011-05-14 17:25:05 +02:00
{
p2 + + ;
char c = * p2 ;
* p2 = ' \0 ' ;
OutputDebugStringA ( p1 ) ;
* p2 = c ;
p1 = p2 ;
}
if ( p1 ! = pszBuffer )
memmove ( pszBuffer , p1 , pend - p1 + 1 ) ;
pend - = ( p1 - pszBuffer ) ;
}
}
# endif
return ret ;
}
// Safer snprintf
// - prints up to limit-1 characters
// - output string is always null terminated even if limit reached
// - return value is the number of characters actually printed
int my_snprintf ( char * buffer , size_t limit , const char * format , . . . )
{
if ( limit = = 0 )
return 0 ;
va_list arg_ptr ;
va_start ( arg_ptr , format ) ;
int ret = _vsnprintf ( buffer , limit , format , arg_ptr ) ;
va_end ( arg_ptr ) ;
2012-04-22 19:51:16 +02:00
if ( ret < 0 | | ret > = ( int ) limit )
2011-05-14 17:25:05 +02:00
{
ret = limit - 1 ;
buffer [ limit - 1 ] = 0 ;
}
return ret ;
}
2012-03-18 23:14:03 +01:00
string real_strprintf ( const std : : string & format , int dummy , . . . )
2011-05-14 17:25:05 +02:00
{
char buffer [ 50000 ] ;
char * p = buffer ;
int limit = sizeof ( buffer ) ;
int ret ;
loop
{
va_list arg_ptr ;
2012-03-18 23:14:03 +01:00
va_start ( arg_ptr , dummy ) ;
2011-06-13 16:56:37 +02:00
ret = _vsnprintf ( p , limit , format . c_str ( ) , arg_ptr ) ;
2011-05-14 17:25:05 +02:00
va_end ( arg_ptr ) ;
if ( ret > = 0 & & ret < limit )
break ;
if ( p ! = buffer )
2011-06-05 19:15:15 +02:00
delete [ ] p ;
2011-05-14 17:25:05 +02:00
limit * = 2 ;
p = new char [ limit ] ;
if ( p = = NULL )
throw std : : bad_alloc ( ) ;
}
string str ( p , p + ret ) ;
if ( p ! = buffer )
2011-06-05 19:15:15 +02:00
delete [ ] p ;
2011-05-14 17:25:05 +02:00
return str ;
}
2012-03-18 23:14:03 +01:00
bool error ( const char * format , . . . )
2011-05-14 17:25:05 +02:00
{
char buffer [ 50000 ] ;
int limit = sizeof ( buffer ) ;
va_list arg_ptr ;
va_start ( arg_ptr , format ) ;
2012-03-18 23:14:03 +01:00
int ret = _vsnprintf ( buffer , limit , format , arg_ptr ) ;
2011-05-14 17:25:05 +02:00
va_end ( arg_ptr ) ;
if ( ret < 0 | | ret > = limit )
{
2012-04-22 16:22:45 +02:00
buffer [ limit - 1 ] = 0 ;
2011-05-14 17:25:05 +02:00
}
printf ( " ERROR: %s \n " , buffer ) ;
return false ;
}
void ParseString ( const string & str , char c , vector < string > & v )
{
if ( str . empty ( ) )
return ;
string : : size_type i1 = 0 ;
string : : size_type i2 ;
loop
{
i2 = str . find ( c , i1 ) ;
if ( i2 = = str . npos )
{
v . push_back ( str . substr ( i1 ) ) ;
return ;
}
v . push_back ( str . substr ( i1 , i2 - i1 ) ) ;
i1 = i2 + 1 ;
}
}
2011-12-21 22:33:19 +01:00
string FormatMoney ( int64 n , bool fPlus )
2011-05-14 17:25:05 +02:00
{
// Note: not using straight sprintf here because we do NOT want
// localized number formatting.
2011-12-21 22:33:19 +01:00
int64 n_abs = ( n > 0 ? n : - n ) ;
int64 quotient = n_abs / COIN ;
int64 remainder = n_abs % COIN ;
2011-05-14 17:25:05 +02:00
string str = strprintf ( " % " PRI64d " .%08 " PRI64d , quotient , remainder ) ;
// Right-trim excess 0's before the decimal point:
int nTrim = 0 ;
for ( int i = str . size ( ) - 1 ; ( str [ i ] = = ' 0 ' & & isdigit ( str [ i - 2 ] ) ) ; - - i )
+ + nTrim ;
if ( nTrim )
str . erase ( str . size ( ) - nTrim , nTrim ) ;
if ( n < 0 )
str . insert ( ( unsigned int ) 0 , 1 , ' - ' ) ;
else if ( fPlus & & n > 0 )
str . insert ( ( unsigned int ) 0 , 1 , ' + ' ) ;
return str ;
}
2011-12-21 22:33:19 +01:00
bool ParseMoney ( const string & str , int64 & nRet )
2011-05-14 17:25:05 +02:00
{
return ParseMoney ( str . c_str ( ) , nRet ) ;
}
2011-12-21 22:33:19 +01:00
bool ParseMoney ( const char * pszIn , int64 & nRet )
2011-05-14 17:25:05 +02:00
{
string strWhole ;
2011-12-21 22:33:19 +01:00
int64 nUnits = 0 ;
2011-05-14 17:25:05 +02:00
const char * p = pszIn ;
while ( isspace ( * p ) )
p + + ;
for ( ; * p ; p + + )
{
if ( * p = = ' . ' )
{
p + + ;
2011-12-21 22:33:19 +01:00
int64 nMult = CENT * 10 ;
2011-05-14 17:25:05 +02:00
while ( isdigit ( * p ) & & ( nMult > 0 ) )
{
nUnits + = nMult * ( * p + + - ' 0 ' ) ;
nMult / = 10 ;
}
break ;
}
if ( isspace ( * p ) )
break ;
if ( ! isdigit ( * p ) )
return false ;
strWhole . insert ( strWhole . end ( ) , * p ) ;
}
for ( ; * p ; p + + )
if ( ! isspace ( * p ) )
return false ;
2011-10-01 02:47:47 +02:00
if ( strWhole . size ( ) > 10 ) // guard against 63 bit overflow
2011-05-14 17:25:05 +02:00
return false ;
if ( nUnits < 0 | | nUnits > COIN )
return false ;
2011-12-21 22:33:19 +01:00
int64 nWhole = atoi64 ( strWhole ) ;
int64 nValue = nWhole * COIN + nUnits ;
2011-05-14 17:25:05 +02:00
nRet = nValue ;
return true ;
}
2012-04-16 07:32:55 +02:00
static signed char phexdigit [ 256 ] =
2012-01-05 03:40:52 +01:00
{ - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , 0xa , 0xb , 0xc , 0xd , 0xe , 0xf , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
2012-04-16 07:31:38 +02:00
- 1 , 0xa , 0xb , 0xc , 0xd , 0xe , 0xf , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
2012-01-05 03:40:52 +01:00
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , } ;
bool IsHex ( const string & str )
2011-05-14 17:25:05 +02:00
{
2012-01-05 03:40:52 +01:00
BOOST_FOREACH ( unsigned char c , str )
{
if ( phexdigit [ c ] < 0 )
return false ;
}
return ( str . size ( ) > 0 ) & & ( str . size ( ) % 2 = = 0 ) ;
}
2011-05-14 17:25:05 +02:00
2012-01-05 03:40:52 +01:00
vector < unsigned char > ParseHex ( const char * psz )
{
2011-05-14 17:25:05 +02:00
// convert hex dump to vector
vector < unsigned char > vch ;
loop
{
while ( isspace ( * psz ) )
psz + + ;
2012-04-16 07:32:55 +02:00
signed char c = phexdigit [ ( unsigned char ) * psz + + ] ;
if ( c = = ( signed char ) - 1 )
2011-05-14 17:25:05 +02:00
break ;
unsigned char n = ( c < < 4 ) ;
c = phexdigit [ ( unsigned char ) * psz + + ] ;
2012-04-16 07:32:55 +02:00
if ( c = = ( signed char ) - 1 )
2011-05-14 17:25:05 +02:00
break ;
n | = c ;
vch . push_back ( n ) ;
}
return vch ;
}
vector < unsigned char > ParseHex ( const string & str )
{
return ParseHex ( str . c_str ( ) ) ;
}
2012-02-16 21:08:32 +01:00
static void InterpretNegativeSetting ( string name , map < string , string > & mapSettingsRet )
{
// interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set
if ( name . find ( " -no " ) = = 0 )
{
std : : string positive ( " - " ) ;
positive . append ( name . begin ( ) + 3 , name . end ( ) ) ;
if ( mapSettingsRet . count ( positive ) = = 0 )
{
bool value = ! GetBoolArg ( name ) ;
mapSettingsRet [ positive ] = ( value ? " 1 " : " 0 " ) ;
}
}
}
2012-04-22 16:22:45 +02:00
void ParseParameters ( int argc , const char * const argv [ ] )
2011-05-14 17:25:05 +02:00
{
mapArgs . clear ( ) ;
mapMultiArgs . clear ( ) ;
for ( int i = 1 ; i < argc ; i + + )
{
char psz [ 10000 ] ;
strlcpy ( psz , argv [ i ] , sizeof ( psz ) ) ;
char * pszValue = ( char * ) " " ;
if ( strchr ( psz , ' = ' ) )
{
pszValue = strchr ( psz , ' = ' ) ;
* pszValue + + = ' \0 ' ;
}
2011-10-07 17:02:21 +02:00
# ifdef WIN32
2011-05-14 17:25:05 +02:00
_strlwr ( psz ) ;
if ( psz [ 0 ] = = ' / ' )
psz [ 0 ] = ' - ' ;
# endif
if ( psz [ 0 ] ! = ' - ' )
break ;
2012-02-06 19:55:11 +01:00
2011-05-14 17:25:05 +02:00
mapArgs [ psz ] = pszValue ;
mapMultiArgs [ psz ] . push_back ( pszValue ) ;
}
2012-02-06 19:55:11 +01:00
// New 0.6 features:
BOOST_FOREACH ( const PAIRTYPE ( string , string ) & entry , mapArgs )
{
string name = entry . first ;
// interpret --foo as -foo (as long as both are not set)
if ( name . find ( " -- " ) = = 0 )
{
std : : string singleDash ( name . begin ( ) + 1 , name . end ( ) ) ;
if ( mapArgs . count ( singleDash ) = = 0 )
mapArgs [ singleDash ] = entry . second ;
name = singleDash ;
}
2012-02-16 21:08:32 +01:00
// interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set
InterpretNegativeSetting ( name , mapArgs ) ;
2012-02-06 19:55:11 +01:00
}
2011-05-14 17:25:05 +02:00
}
2012-02-06 18:37:49 +01:00
std : : string GetArg ( const std : : string & strArg , const std : : string & strDefault )
{
if ( mapArgs . count ( strArg ) )
return mapArgs [ strArg ] ;
return strDefault ;
}
int64 GetArg ( const std : : string & strArg , int64 nDefault )
{
if ( mapArgs . count ( strArg ) )
return atoi64 ( mapArgs [ strArg ] ) ;
return nDefault ;
}
bool GetBoolArg ( const std : : string & strArg , bool fDefault )
{
if ( mapArgs . count ( strArg ) )
{
if ( mapArgs [ strArg ] . empty ( ) )
return true ;
return ( atoi ( mapArgs [ strArg ] ) ! = 0 ) ;
}
return fDefault ;
}
2012-01-03 16:14:22 +01:00
bool SoftSetArg ( const std : : string & strArg , const std : : string & strValue )
{
if ( mapArgs . count ( strArg ) )
return false ;
mapArgs [ strArg ] = strValue ;
return true ;
}
2012-02-06 21:48:00 +01:00
bool SoftSetBoolArg ( const std : : string & strArg , bool fValue )
2012-01-03 16:14:22 +01:00
{
if ( fValue )
return SoftSetArg ( strArg , std : : string ( " 1 " ) ) ;
else
return SoftSetArg ( strArg , std : : string ( " 0 " ) ) ;
}
2011-09-20 15:38:29 +02:00
string EncodeBase64 ( const unsigned char * pch , size_t len )
2011-07-26 00:13:55 +02:00
{
2011-09-20 15:38:29 +02:00
static const char * pbase64 = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ " ;
string strRet = " " ;
strRet . reserve ( ( len + 2 ) / 3 * 4 ) ;
int mode = 0 , left = 0 ;
const unsigned char * pchEnd = pch + len ;
while ( pch < pchEnd )
{
int enc = * ( pch + + ) ;
switch ( mode )
{
case 0 : // we have no bits
strRet + = pbase64 [ enc > > 2 ] ;
left = ( enc & 3 ) < < 4 ;
mode = 1 ;
break ;
case 1 : // we have two bits
strRet + = pbase64 [ left | ( enc > > 4 ) ] ;
left = ( enc & 15 ) < < 2 ;
mode = 2 ;
break ;
case 2 : // we have four bits
strRet + = pbase64 [ left | ( enc > > 6 ) ] ;
strRet + = pbase64 [ enc & 63 ] ;
mode = 0 ;
break ;
}
}
if ( mode )
{
strRet + = pbase64 [ left ] ;
strRet + = ' = ' ;
if ( mode = = 1 )
strRet + = ' = ' ;
}
return strRet ;
}
string EncodeBase64 ( const string & str )
2011-07-26 00:13:55 +02:00
{
2011-09-20 15:38:29 +02:00
return EncodeBase64 ( ( const unsigned char * ) str . c_str ( ) , str . size ( ) ) ;
}
2011-07-26 00:13:55 +02:00
2011-09-20 15:38:29 +02:00
vector < unsigned char > DecodeBase64 ( const char * p , bool * pfInvalid )
{
static const int decode64_table [ 256 ] =
{
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , 62 , - 1 , - 1 , - 1 , 63 , 52 , 53 , 54 , 55 , 56 , 57 , 58 , 59 , 60 , 61 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 ,
15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , 26 , 27 , 28 ,
29 , 30 , 31 , 32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 , 40 , 41 , 42 , 43 , 44 , 45 , 46 , 47 , 48 ,
49 , 50 , 51 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 ,
- 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1
} ;
if ( pfInvalid )
* pfInvalid = false ;
vector < unsigned char > vchRet ;
vchRet . reserve ( strlen ( p ) * 3 / 4 ) ;
int mode = 0 ;
int left = 0 ;
while ( 1 )
2011-07-26 00:13:55 +02:00
{
2012-04-15 12:22:30 +02:00
int dec = decode64_table [ ( unsigned char ) * p ] ;
2011-09-20 15:38:29 +02:00
if ( dec = = - 1 ) break ;
p + + ;
switch ( mode )
2011-07-26 00:13:55 +02:00
{
case 0 : // we have no bits and get 6
left = dec ;
mode = 1 ;
break ;
case 1 : // we have 6 bits and keep 4
2011-09-20 15:38:29 +02:00
vchRet . push_back ( ( left < < 2 ) | ( dec > > 4 ) ) ;
2011-07-26 00:13:55 +02:00
left = dec & 15 ;
mode = 2 ;
break ;
2011-09-20 15:38:29 +02:00
case 2 : // we have 4 bits and get 6, we keep 2
vchRet . push_back ( ( left < < 4 ) | ( dec > > 2 ) ) ;
2011-07-26 00:13:55 +02:00
left = dec & 3 ;
mode = 3 ;
break ;
2011-09-20 15:38:29 +02:00
2011-07-26 00:13:55 +02:00
case 3 : // we have 2 bits and get 6
2011-09-20 15:38:29 +02:00
vchRet . push_back ( ( left < < 6 ) | dec ) ;
mode = 0 ;
2011-07-26 00:13:55 +02:00
break ;
}
}
2011-09-20 15:38:29 +02:00
if ( pfInvalid )
switch ( mode )
{
case 0 : // 4n base64 characters processed: ok
break ;
case 1 : // 4n+1 base64 character processed: impossible
* pfInvalid = true ;
break ;
case 2 : // 4n+2 base64 characters processed: require '=='
2012-04-15 12:22:30 +02:00
if ( left | | p [ 0 ] ! = ' = ' | | p [ 1 ] ! = ' = ' | | decode64_table [ ( unsigned char ) p [ 2 ] ] ! = - 1 )
2011-09-20 15:38:29 +02:00
* pfInvalid = true ;
break ;
case 3 : // 4n+3 base64 characters processed: require '='
2012-04-15 12:22:30 +02:00
if ( left | | p [ 0 ] ! = ' = ' | | decode64_table [ ( unsigned char ) p [ 1 ] ] ! = - 1 )
2011-09-20 15:38:29 +02:00
* pfInvalid = true ;
break ;
}
return vchRet ;
2011-07-26 00:13:55 +02:00
}
2011-09-20 15:38:29 +02:00
string DecodeBase64 ( const string & str )
{
vector < unsigned char > vchRet = DecodeBase64 ( str . c_str ( ) ) ;
return string ( ( const char * ) & vchRet [ 0 ] , vchRet . size ( ) ) ;
}
2011-05-14 17:25:05 +02:00
bool WildcardMatch ( const char * psz , const char * mask )
{
loop
{
switch ( * mask )
{
case ' \0 ' :
return ( * psz = = ' \0 ' ) ;
case ' * ' :
return WildcardMatch ( psz , mask + 1 ) | | ( * psz & & WildcardMatch ( psz + 1 , mask ) ) ;
case ' ? ' :
if ( * psz = = ' \0 ' )
return false ;
break ;
default :
if ( * psz ! = * mask )
return false ;
break ;
}
psz + + ;
mask + + ;
}
}
bool WildcardMatch ( const string & str , const string & mask )
{
return WildcardMatch ( str . c_str ( ) , mask . c_str ( ) ) ;
}
void FormatException ( char * pszMessage , std : : exception * pex , const char * pszThread )
{
2011-10-07 17:02:21 +02:00
# ifdef WIN32
2012-04-22 16:22:45 +02:00
char pszModule [ MAX_PATH ] = " " ;
2011-05-14 17:25:05 +02:00
GetModuleFileNameA ( NULL , pszModule , sizeof ( pszModule ) ) ;
# else
const char * pszModule = " bitcoin " ;
# endif
if ( pex )
snprintf ( pszMessage , 1000 ,
" EXCEPTION: %s \n %s \n %s in %s \n " , typeid ( * pex ) . name ( ) , pex - > what ( ) , pszModule , pszThread ) ;
else
snprintf ( pszMessage , 1000 ,
" UNKNOWN EXCEPTION \n %s in %s \n " , pszModule , pszThread ) ;
}
void LogException ( std : : exception * pex , const char * pszThread )
{
char pszMessage [ 10000 ] ;
FormatException ( pszMessage , pex , pszThread ) ;
printf ( " \n %s " , pszMessage ) ;
}
void PrintException ( std : : exception * pex , const char * pszThread )
{
char pszMessage [ 10000 ] ;
FormatException ( pszMessage , pex , pszThread ) ;
printf ( " \n \n ************************ \n %s \n " , pszMessage ) ;
fprintf ( stderr , " \n \n ************************ \n %s \n " , pszMessage ) ;
strMiscWarning = pszMessage ;
throw ;
}
void PrintExceptionContinue ( std : : exception * pex , const char * pszThread )
{
char pszMessage [ 10000 ] ;
FormatException ( pszMessage , pex , pszThread ) ;
printf ( " \n \n ************************ \n %s \n " , pszMessage ) ;
fprintf ( stderr , " \n \n ************************ \n %s \n " , pszMessage ) ;
strMiscWarning = pszMessage ;
}
2012-04-09 23:50:56 +02:00
boost : : filesystem : : path GetDefaultDataDir ( )
2011-05-14 17:25:05 +02:00
{
2012-04-09 23:50:56 +02:00
namespace fs = boost : : filesystem ;
2012-04-22 16:22:45 +02:00
// Windows < Vista: C:\Documents and Settings\Username\Application Data\Bitcoin
// Windows >= Vista: C:\Users\Username\AppData\Roaming\Bitcoin
2011-05-14 17:25:05 +02:00
// Mac: ~/Library/Application Support/Bitcoin
// Unix: ~/.bitcoin
2011-10-07 17:02:21 +02:00
# ifdef WIN32
2011-05-14 17:25:05 +02:00
// Windows
2012-04-22 16:22:45 +02:00
return GetSpecialFolderPath ( CSIDL_APPDATA ) / " Bitcoin " ;
2011-05-14 17:25:05 +02:00
# else
2012-04-09 23:50:56 +02:00
fs : : path pathRet ;
2011-05-14 17:25:05 +02:00
char * pszHome = getenv ( " HOME " ) ;
if ( pszHome = = NULL | | strlen ( pszHome ) = = 0 )
2012-04-09 23:50:56 +02:00
pathRet = fs : : path ( " / " ) ;
else
pathRet = fs : : path ( pszHome ) ;
2011-10-07 17:02:21 +02:00
# ifdef MAC_OSX
2011-05-14 17:25:05 +02:00
// Mac
2012-04-12 17:14:46 +02:00
pathRet / = " Library/Application Support " ;
2012-05-09 19:56:53 +02:00
fs : : create_directory ( pathRet ) ;
2012-04-09 23:50:56 +02:00
return pathRet / " Bitcoin " ;
2011-05-14 17:25:05 +02:00
# else
// Unix
2012-04-09 23:50:56 +02:00
return pathRet / " .bitcoin " ;
2011-05-14 17:25:05 +02:00
# endif
# endif
}
2012-04-09 23:50:56 +02:00
const boost : : filesystem : : path & GetDataDir ( bool fNetSpecific )
2011-05-14 17:25:05 +02:00
{
2012-04-09 23:50:56 +02:00
namespace fs = boost : : filesystem ;
static fs : : path pathCached [ 2 ] ;
static CCriticalSection csPathCached ;
static bool cachedPath [ 2 ] = { false , false } ;
fs : : path & path = pathCached [ fNetSpecific ] ;
// This can be called during exceptions by printf, so we cache the
// value so we don't have to do memory allocations after that.
if ( cachedPath [ fNetSpecific ] )
return path ;
LOCK ( csPathCached ) ;
if ( mapArgs . count ( " -datadir " ) ) {
2012-04-22 14:35:22 +02:00
path = fs : : system_complete ( mapArgs [ " -datadir " ] ) ;
if ( ! fs : : is_directory ( path ) ) {
path = " " ;
return path ;
}
2012-04-09 23:50:56 +02:00
} else {
path = GetDefaultDataDir ( ) ;
2011-05-14 17:25:05 +02:00
}
2012-04-13 03:11:14 +02:00
if ( fNetSpecific & & GetBoolArg ( " -testnet " , false ) )
path / = " testnet " ;
2011-05-14 17:25:05 +02:00
2012-04-09 23:50:56 +02:00
fs : : create_directory ( path ) ;
cachedPath [ fNetSpecific ] = true ;
return path ;
2011-05-14 17:25:05 +02:00
}
2012-04-09 23:50:56 +02:00
boost : : filesystem : : path GetConfigFile ( )
2011-05-14 17:25:05 +02:00
{
2012-04-22 16:22:45 +02:00
boost : : filesystem : : path pathConfigFile ( GetArg ( " -conf " , " bitcoin.conf " ) ) ;
2012-04-09 23:50:56 +02:00
if ( ! pathConfigFile . is_complete ( ) ) pathConfigFile = GetDataDir ( false ) / pathConfigFile ;
return pathConfigFile ;
2011-05-14 17:25:05 +02:00
}
2012-04-22 14:35:22 +02:00
void ReadConfigFile ( map < string , string > & mapSettingsRet ,
2011-05-14 17:25:05 +02:00
map < string , vector < string > > & mapMultiSettingsRet )
{
2012-04-22 16:22:45 +02:00
boost : : filesystem : : ifstream streamConfig ( GetConfigFile ( ) ) ;
2011-05-14 17:25:05 +02:00
if ( ! streamConfig . good ( ) )
2012-04-22 14:35:22 +02:00
return ; // No bitcoin.conf file is OK
2011-05-14 17:25:05 +02:00
set < string > setOptions ;
setOptions . insert ( " * " ) ;
2012-04-09 23:50:56 +02:00
2012-04-22 16:22:45 +02:00
for ( boost : : program_options : : detail : : config_file_iterator it ( streamConfig , setOptions ) , end ; it ! = end ; + + it )
2011-05-14 17:25:05 +02:00
{
// Don't overwrite existing settings so command line settings override bitcoin.conf
string strKey = string ( " - " ) + it - > string_key ;
if ( mapSettingsRet . count ( strKey ) = = 0 )
2012-02-16 21:08:32 +01:00
{
2011-05-14 17:25:05 +02:00
mapSettingsRet [ strKey ] = it - > value [ 0 ] ;
2012-04-22 16:22:45 +02:00
// interpret nofoo=1 as foo=0 (and nofoo=0 as foo=1) as long as foo not set)
2012-02-16 21:08:32 +01:00
InterpretNegativeSetting ( strKey , mapSettingsRet ) ;
}
2011-05-14 17:25:05 +02:00
mapMultiSettingsRet [ strKey ] . push_back ( it - > value [ 0 ] ) ;
}
}
2012-04-09 23:50:56 +02:00
boost : : filesystem : : path GetPidFile ( )
2011-05-14 17:25:05 +02:00
{
2012-04-22 16:22:45 +02:00
boost : : filesystem : : path pathPidFile ( GetArg ( " -pid " , " bitcoind.pid " ) ) ;
2012-04-09 23:50:56 +02:00
if ( ! pathPidFile . is_complete ( ) ) pathPidFile = GetDataDir ( ) / pathPidFile ;
return pathPidFile ;
2011-05-14 17:25:05 +02:00
}
2012-04-09 23:50:56 +02:00
void CreatePidFile ( const boost : : filesystem : : path & path , pid_t pid )
2011-05-14 17:25:05 +02:00
{
2012-04-09 23:50:56 +02:00
FILE * file = fopen ( path . string ( ) . c_str ( ) , " w " ) ;
2011-06-24 20:03:16 +02:00
if ( file )
2011-05-14 17:25:05 +02:00
{
fprintf ( file , " %d \n " , pid ) ;
fclose ( file ) ;
}
}
int GetFilesize ( FILE * file )
{
int nSavePos = ftell ( file ) ;
int nFilesize = - 1 ;
if ( fseek ( file , 0 , SEEK_END ) = = 0 )
nFilesize = ftell ( file ) ;
fseek ( file , nSavePos , SEEK_SET ) ;
return nFilesize ;
}
void ShrinkDebugFile ( )
{
// Scroll debug.log if it's getting too big
2012-04-09 23:50:56 +02:00
boost : : filesystem : : path pathLog = GetDataDir ( ) / " debug.log " ;
FILE * file = fopen ( pathLog . string ( ) . c_str ( ) , " r " ) ;
2011-05-14 17:25:05 +02:00
if ( file & & GetFilesize ( file ) > 10 * 1000000 )
{
// Restart the file with some of the end
char pch [ 200000 ] ;
fseek ( file , - sizeof ( pch ) , SEEK_END ) ;
int nBytes = fread ( pch , 1 , sizeof ( pch ) , file ) ;
fclose ( file ) ;
2011-06-24 20:03:16 +02:00
2012-04-09 23:50:56 +02:00
file = fopen ( pathLog . string ( ) . c_str ( ) , " w " ) ;
2011-06-24 20:03:16 +02:00
if ( file )
2011-05-14 17:25:05 +02:00
{
fwrite ( pch , 1 , nBytes , file ) ;
fclose ( file ) ;
}
}
}
//
// "Never go to sea with two chronometers; take one or three."
// Our three time sources are:
// - System clock
// - Median of other nodes's clocks
// - The user (asking the user to fix the system clock if the first two disagree)
//
2011-12-21 22:33:19 +01:00
static int64 nMockTime = 0 ; // For unit testing
2011-09-15 14:55:15 +02:00
2011-12-21 22:33:19 +01:00
int64 GetTime ( )
2011-05-14 17:25:05 +02:00
{
2011-09-15 14:55:15 +02:00
if ( nMockTime ) return nMockTime ;
2011-05-14 17:25:05 +02:00
return time ( NULL ) ;
}
2011-12-21 22:33:19 +01:00
void SetMockTime ( int64 nMockTimeIn )
2011-09-15 14:55:15 +02:00
{
nMockTime = nMockTimeIn ;
}
2011-12-21 22:33:19 +01:00
static int64 nTimeOffset = 0 ;
2011-05-14 17:25:05 +02:00
2011-12-21 22:33:19 +01:00
int64 GetAdjustedTime ( )
2011-05-14 17:25:05 +02:00
{
return GetTime ( ) + nTimeOffset ;
}
2012-01-03 23:33:31 +01:00
void AddTimeData ( const CNetAddr & ip , int64 nTime )
2011-05-14 17:25:05 +02:00
{
2011-12-21 22:33:19 +01:00
int64 nOffsetSample = nTime - GetTime ( ) ;
2011-05-14 17:25:05 +02:00
// Ignore duplicates
2012-01-03 23:33:31 +01:00
static set < CNetAddr > setKnown ;
2011-05-14 17:25:05 +02:00
if ( ! setKnown . insert ( ip ) . second )
return ;
// Add data
Retain only the most recent time samples
Remembering all time samples makes nTimeOffset slow to respond to
system clock corrections. For instance, I start my node with a system
clock that's 30 minutes slow and run it for a few days. During that
time, I accumulate 10,000 offset samples with a median of 1800
seconds. Now I correct my system clock. Without this change, my node
must collect another 10,000 samples before nTimeOffset is correct
again. With this change, I must only accumulate 100 samples to
correct the offset.
Storing unlimited time samples also allows an attacker with many IP
addresses (ex, a large botnet) to perform a memory exhaustion attack
against Bitcoin nodes. The attacker sends a version message from each
IP to his target, consuming more of the target's memory each time.
Time samples are small, so this attack might be impractical under the
old code, but it's impossible with the new code.
2011-11-30 04:15:59 +01:00
vTimeOffsets . input ( nOffsetSample ) ;
printf ( " Added time data, samples %d, offset %+ " PRI64d " (%+ " PRI64d " minutes) \n " , vTimeOffsets . size ( ) , nOffsetSample , nOffsetSample / 60 ) ;
2011-05-14 17:25:05 +02:00
if ( vTimeOffsets . size ( ) > = 5 & & vTimeOffsets . size ( ) % 2 = = 1 )
{
2011-12-21 22:33:19 +01:00
int64 nMedian = vTimeOffsets . median ( ) ;
std : : vector < int64 > vSorted = vTimeOffsets . sorted ( ) ;
2011-05-14 17:25:05 +02:00
// Only let other nodes change our time by so much
if ( abs64 ( nMedian ) < 70 * 60 )
{
nTimeOffset = nMedian ;
}
else
{
nTimeOffset = 0 ;
static bool fDone ;
if ( ! fDone )
{
// If nobody has a time different than ours but within 5 minutes of ours, give a warning
bool fMatch = false ;
2011-12-21 22:33:19 +01:00
BOOST_FOREACH ( int64 nOffset , vSorted )
2011-05-14 17:25:05 +02:00
if ( nOffset ! = 0 & & abs64 ( nOffset ) < 5 * 60 )
fMatch = true ;
if ( ! fMatch )
{
fDone = true ;
string strMessage = _ ( " Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly. " ) ;
strMiscWarning = strMessage ;
printf ( " *** %s \n " , strMessage . c_str ( ) ) ;
2012-03-25 20:47:33 +02:00
ThreadSafeMessageBox ( strMessage + " " , string ( " Bitcoin " ) , wxOK | wxICON_EXCLAMATION ) ;
2011-05-14 17:25:05 +02:00
}
}
}
2011-11-30 03:06:10 +01:00
if ( fDebug ) {
2011-12-21 22:33:19 +01:00
BOOST_FOREACH ( int64 n , vSorted )
2011-11-30 03:06:10 +01:00
printf ( " %+ " PRI64d " " , n ) ;
printf ( " | " ) ;
}
printf ( " nTimeOffset = %+ " PRI64d " (%+ " PRI64d " minutes) \n " , nTimeOffset , nTimeOffset / 60 ) ;
2011-05-14 17:25:05 +02:00
}
}
string FormatVersion ( int nVersion )
{
if ( nVersion % 100 = = 0 )
return strprintf ( " %d.%d.%d " , nVersion / 1000000 , ( nVersion / 10000 ) % 100 , ( nVersion / 100 ) % 100 ) ;
else
return strprintf ( " %d.%d.%d.%d " , nVersion / 1000000 , ( nVersion / 10000 ) % 100 , ( nVersion / 100 ) % 100 , nVersion % 100 ) ;
}
string FormatFullVersion ( )
{
2012-04-07 02:06:53 +02:00
return CLIENT_BUILD ;
2011-05-14 17:25:05 +02:00
}
2011-12-16 22:26:14 +01:00
// Format the subversion field according to BIP 14 spec (https://en.bitcoin.it/wiki/BIP_0014)
std : : string FormatSubVersion ( const std : : string & name , int nClientVersion , const std : : vector < std : : string > & comments )
{
std : : ostringstream ss ;
ss < < " / " ;
ss < < name < < " : " < < FormatVersion ( nClientVersion ) ;
if ( ! comments . empty ( ) )
ss < < " ( " < < boost : : algorithm : : join ( comments , " ; " ) < < " ) " ;
ss < < " / " ;
return ss . str ( ) ;
}
2011-05-14 17:25:05 +02:00
2012-04-15 22:10:54 +02:00
# ifdef WIN32
2012-04-22 16:22:45 +02:00
boost : : filesystem : : path GetSpecialFolderPath ( int nFolder , bool fCreate )
{
namespace fs = boost : : filesystem ;
char pszPath [ MAX_PATH ] = " " ;
if ( SHGetSpecialFolderPathA ( NULL , pszPath , nFolder , fCreate ) )
{
return fs : : path ( pszPath ) ;
}
printf ( " SHGetSpecialFolderPathA() failed, could not obtain requested path. \n " ) ;
return fs : : path ( " " ) ;
}
2012-04-15 22:10:54 +02:00
# endif