TuttleOFX
1
|
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 }