TuttleOFX  1
TuttleOFX/libraries/tuttle/src/tuttle/common/utils/applicationPath.cpp
Go to the documentation of this file.
00001 /*
00002  * This code is rewrite from the Trolltech Qt code applicationFilePath / applicationDirPath.
00003  */
00004 
00005 #include "applicationPath.hpp"
00006 
00007 #include <tuttle/common/system/system.hpp>
00008 #include <tuttle/common/system/macos.hpp>
00009 #include <tuttle/common/utils/global.hpp>
00010 
00011 #include <boost/filesystem/operations.hpp>
00012 
00013 #include <boost/algorithm/string/predicate.hpp>
00014 #include <boost/algorithm/string/split.hpp>
00015 #include <boost/algorithm/string/classification.hpp>
00016 
00017 #include <boost/format.hpp>
00018 #include <boost/foreach.hpp>
00019 #include <boost/version.hpp>
00020 
00021 #if defined( __WINDOWS__ )
00022 #include <windows.h>
00023 #elif defined( __MACOS__ )
00024 #elif defined( __LINUX__ )
00025 #endif
00026 
00027 namespace tuttle {
00028 namespace common {
00029 
00030 #ifdef __WINDOWS__
00031 #define DIRSEP "\\"
00032 #define DIRLIST_SEP_CHARS ";"
00033 #else
00034 #define DIRSEP "/"
00035 #define DIRLIST_SEP_CHARS ";:"
00036 #endif
00037 
00038 /*!
00039         Returns the absolute path to the application executable.
00040 
00041         On Mac OS X this will point to the directory actually containing the
00042         executable, which may be inside of an application bundle (if the
00043         application is bundled).
00044 
00045         \warning On Linux, this function will try to get the path from the
00046         \c {/proc} file system. If that fails, it assumes that \c
00047         {argv[0]} contains the absolute file name of the executable. The
00048         function also assumes that the current directory has not been
00049         changed by the application.
00050 
00051         In Symbian this function will return the application private directory,
00052         not the path to executable itself, as those are always in \c {/sys/bin}.
00053         If the application is in a read only drive, i.e. ROM, then the private path
00054         on the system drive will be returned.
00055  */
00056 boost::filesystem::path applicationFilepath( const std::string& argv0, const boost::filesystem::path& currentPath )
00057 {
00058         namespace bfs = boost::filesystem;
00059         namespace bal = boost::algorithm;
00060         bfs::path app( argv0 );
00061         
00062 #if defined( __WINDOWS__ )
00063         {
00064                 // We do MAX_PATH + 2 here, and request with MAX_PATH + 1, so we can handle all paths
00065                 // up to, and including MAX_PATH size perfectly fine with string termination, as well
00066                 // as easily detect if the file path is indeed larger than MAX_PATH, in which case we
00067                 // need to use the heap instead. This is a work-around, since contrary to what the
00068                 // MSDN documentation states, GetModuleFileName sometimes doesn't set the
00069                 // ERROR_INSUFFICIENT_BUFFER error number, and we thus cannot rely on this value if
00070                 // GetModuleFileName(0, buffer, MAX_PATH) == MAX_PATH.
00071                 // GetModuleFileName(0, buffer, MAX_PATH + 1) == MAX_PATH just means we hit the normal
00072                 // file path limit, and we handle it normally, if the result is MAX_PATH + 1, we use
00073                 // heap (even if the result _might_ be exactly MAX_PATH + 1, but that's ok).
00074                 char buffer[MAX_PATH + 2];
00075                 DWORD v = GetModuleFileName(0, buffer, MAX_PATH + 1);
00076                 buffer[MAX_PATH + 1] = 0;
00077 
00078                 if( v > 0 )
00079                 {
00080                         if( v <= MAX_PATH )
00081                                 return std::string( buffer );
00082                                 //return QString::fromWCharArray( buffer );
00083 
00084                         // MAX_PATH sized buffer wasn't large enough to contain the full path, use heap
00085                         char* b = NULL;
00086                         int i = 1;
00087                         size_t size;
00088                         do {
00089                                 ++i;
00090                                 size = MAX_PATH * i;
00091                                 b = reinterpret_cast<char *>( realloc( b, (size + 1) * sizeof(char) ) );
00092                                 if( b )
00093                                         v = GetModuleFileName( NULL, b, size );
00094                         } while( b && v == size );
00095 
00096                         if( b )
00097                                 *(b + size) = 0;
00098 
00099                         std::string res( b );
00100                         //QString res = QString::fromWCharArray(b);
00101                         free(b);
00102 
00103                         return res;
00104                 }
00105         }
00106 #elif defined( __MACOS__ )
00107         {
00108                 CFTypeContainer<CFURLRef> bundleURL( CFBundleCopyExecutableURL( CFBundleGetMainBundle() ) );
00109                 if( bundleURL )
00110                 {
00111                         CFStringContainer cfPath( CFURLCopyFileSystemPath( bundleURL, kCFURLPOSIXPathStyle ) );
00112                         if( cfPath )
00113                         {
00114                                 return cfPath.str();
00115                         }
00116                 }
00117         }
00118 #elif defined( __LINUX__ )
00119 
00120 #if( BOOST_VERSION >= 104800 )
00121         {
00122                 // Try looking for a /proc/<pid>/exe symlink first which points to
00123                 // the absolute path of the executable
00124                 bfs::path exeLinkPath( ( boost::format("/proc/%s/exe") % getpid() ).str() );
00125                 if( bfs::exists( exeLinkPath ) && bfs::is_symlink( exeLinkPath ) )
00126                 {
00127                         return bfs::canonical( exeLinkPath ); // need boost version >= 1.48
00128                 }
00129     }
00130 #endif
00131 
00132 #endif
00133 
00134         /// @todo on windows check which is the first local file or file in PATH...
00135         //      const bool isPath = bal::find_first( argv0, DIRSEP);
00136         const bool isPath = bal::contains( argv0, DIRSEP );
00137         if( isPath )
00138         {
00139                 if( app.is_relative() )
00140                 {
00141                         app = currentPath / app;
00142                 }
00143                 if( bfs::exists( app ) && !bfs::is_directory( app ) )
00144                 {
00145                         return app;
00146                 }
00147         }
00148         else
00149         {
00150                 const char* envPath = std::getenv( "PATH" );
00151                 if( envPath != NULL )
00152                 {
00153                         std::vector<std::string> paths;
00154                         bal::split( paths, envPath, bal::is_any_of( DIRLIST_SEP_CHARS ) );
00155 
00156                         BOOST_FOREACH( const std::string& pathStr, paths )
00157                         {
00158                                 app = bfs::path( pathStr ) / argv0;
00159                                 if( bfs::exists( app ) && !bfs::is_directory( app ) )
00160                                 {
00161                                         return app;
00162                                 }
00163                         }
00164                 }
00165         }
00166 
00167         return argv0; // not found...
00168 }
00169 
00170 }
00171 }