openModeller  Version 1.4.0
os_specific.cpp
Go to the documentation of this file.
00001 
00028 #include <os_specific.hh>
00029 #include <openmodeller/Log.hh>
00030 #include <openmodeller/AlgorithmFactory.hh>
00031 #include <openmodeller/Settings.hh>
00032 
00033 #include <iostream>
00034 #include <stdlib.h>
00035 #include <sys/stat.h>
00036 #include <sys/param.h>
00037 #include <errno.h>
00038 #include <unistd.h>
00039 
00040 using std::vector;
00041 using std::string;
00042 
00043 #if defined(__APPLE__)
00044 //for getting app bundle path
00045     
00046 #include <CoreFoundation/CoreFoundation.h>
00047 #include <ApplicationServices/ApplicationServices.h>
00048 #endif
00049 
00050 #include <fstream>
00051 
00052 /****************************************************************/
00053 /********************* Dynamic Linking Loader *******************/
00054 
00055 #include <dlfcn.h>
00056 
00057 
00058 /****************/
00059 /*** dll Open ***/
00060 DLLHandle
00061 dllOpen( char const *dll_file_name )
00062 {
00063   return dlopen( dll_file_name, RTLD_NOW );
00064 }
00065 
00066 
00067 /********************/
00068 /*** dll Function ***/
00069 void *
00070 dllFunction( DLLHandle handle, char const *function_name )
00071 {
00072   return dlsym( handle, function_name );
00073 }
00074 
00075 
00076 /*****************/
00077 /*** dll Close ***/
00078 int
00079 dllClose( DLLHandle handle )
00080 {
00081   return dlclose( handle );
00082 }
00083 
00084 
00085 /*****************/
00086 /*** dll Error ***/
00087 const char *
00088 dllError( DLLHandle )
00089 {
00090   return dlerror();
00091 }
00092 
00093 /*********************************/
00094 /*** set up External Resources ***/
00095 void setupExternalResources()
00096 {
00097   // nothing here now. See os_specific_win.cpp
00098 }
00099 
00100 /********************/
00101 /*** om Data Path ***/
00102 std::string omDataPath( std::string dir )
00103 {
00104   static string data_path;
00105 
00106   // Set default directory, if specified through parameter
00107   if ( ! dir.empty() ) {
00108 
00109     data_path = dir;
00110 
00111     return data_path;
00112   }
00113 
00114   // Check configuration
00115   if ( Settings::count( "DATA_DIRECTORY" ) == 1 ) {
00116 
00117     return Settings::get( "DATA_DIRECTORY" );
00118   }
00119 
00120   // Check env variable
00121   char *env = getenv( "OM_DATA_DIR" );
00122 
00123   if ( env != 0 ) {
00124 
00125     string om_data_path = (char const *)env;
00126 
00127     if ( ! om_data_path.empty() ) {
00128 
00129       return om_data_path;
00130     }
00131   }
00132 
00133   // Finally compiler constant
00134   return OM_DATA_DIR;
00135 }
00136 
00137 /***************************/
00138 /*** initial Plugin Path ***/
00139 vector<string>
00140 initialPluginPath()
00141 {
00142   Log::instance()->debug( "Determining algorithm paths\n" );
00143 
00144   // Order of initialization:
00145   //
00146   // 1) Programatic setting: AlgorithmFactory::_default_alg_dir
00147   // 2) Settings file (by default om.cfg or set programatically).
00148   // 3) environment variable: OM_ALGS_DIR
00149   // 4) OM_ALGS_DIR compiled constant.
00150   // 5) on mac <application bundle>.app/Contents/MacOS/algs
00151 
00152   vector<string> entries;
00153 
00154   // Default location that can be set programatically
00155   std::string default_dir = AlgorithmFactory::getDefaultAlgDir();
00156 
00157   if ( ! default_dir.empty() ) {
00158 
00159     Log::instance()->debug( "Using programatic setting for algorithms location\n" );
00160 
00161     entries.push_back( default_dir );
00162     return entries;
00163   }
00164 
00165   // Otherwise check configuration
00166   if ( Settings::count( "ALGS_DIRECTORY" ) == 1 ) {
00167 
00168     Log::instance()->debug( "Using configuration setting for algorithms location\n" );
00169     entries.push_back( Settings::get( "ALGS_DIRECTORY" ) );
00170     return entries;
00171   }
00172 
00173   // Or check environment variable
00174   char *env = getenv( "OM_ALGS_DIR" );
00175 
00176   if ( env != 0 ) {
00177 
00178     string envpath( (char const *)env );
00179 
00180     // Ignore empty string
00181     if ( ! envpath.empty() ) {
00182 
00183       Log::instance()->debug( "Using environment setting for algorithms location\n" );
00184 
00185       // Parse the OM_ALGS_DIR with colon (':') delimiters just like all other 
00186       // unix path structures.
00187 
00188       // string::size_type start marks the beginning of the substring.
00189       // initial value is beginning of string, iterate value is one past the ':'
00190       for ( string::size_type start = 0; start < envpath.length() ; ) {
00191       
00192         // Find the next ':' after start
00193         string::size_type it = envpath.find( ':', start );
00194 
00195         // If no ':' is found..
00196         if ( it == string::npos ) {
00197 
00198           // the substring is (start, end-of-string)
00199           entries.push_back( envpath.substr( start ) );
00200           break;
00201         }
00202         // Else, test that the substring is non empty.
00203         else if ( it > start ) {
00204         
00205           string::size_type len = it - start;
00206           entries.push_back( envpath.substr( start, len ) );
00207         }
00208 
00209         // move the start of the next substring to one after the ':'
00210         start = it+1;
00211       }
00212 
00213       return entries;
00214     }
00215   }
00216 
00217   // Default location
00218 #if defined(__APPLE__)
00219   CFURLRef myPluginRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
00220   CFStringRef myMacPath = CFURLCopyFileSystemPath(myPluginRef, kCFURLPOSIXPathStyle);
00221   const char *mypPathPtr = CFStringGetCStringPtr(myMacPath,CFStringGetSystemEncoding());
00222   CFRelease(myPluginRef);
00223   CFRelease(myMacPath);
00224   std::string myFullPath(mypPathPtr);
00225   string::size_type loc = myFullPath.find( ".app", 0 );
00226   if( loc != string::npos ) //found so we are in a mac application bundle
00227   {
00228     myFullPath += "/Contents/MacOS/lib/openmodeller";
00229     entries.push_back( myFullPath );
00230     Log::instance()->debug( "Using Mac bundle for algorithms location\n" );
00231   } 
00232   else //not in a bundle! 
00233   {
00234     //otherwise use the normal search path
00235     Log::instance()->debug( "Using default algorithms location\n" );
00236     entries.push_back( OM_ALGS_DIR );
00237   }
00238 #else
00239   Log::instance()->debug( "Using default algorithms location\n" );
00240   entries.push_back( OM_ALGS_DIR );
00241 #endif
00242 
00243   return entries;
00244 }
00245 
00246 /****************************************************************/
00247 /********************* Scan Directory Entries *******************/
00248 
00249 #include <dirent.h>
00250 #include <string.h>
00251 #include <stdlib.h>
00252 
00253 typedef struct dirent TDirent;
00254 
00255 
00256 /**************/
00257 /*** filter ***/
00262 #ifdef __APPLE__
00263 // NOTE: The following condition may not necessarily be related with clang,
00264 //       but with the OS version. Anyway, it worked for clang 3.4 under Mac Mavericks
00265 #ifdef __clang__
00266 int filter( const struct dirent *dir )
00267 #else
00268 int filter( TDirent *dir )
00269 #endif
00270 #else
00271 int filter( const TDirent *dir )
00272 #endif
00273 {
00274 #if defined(__APPLE__)
00275   // constant version number should not be in filter but
00276   // symlinks ending in .0.dylib and .dylib also exist
00277   // and each library should be found only once
00278   std::string ext = ".so";
00279   //std::string ext = ".0.0.0.dylib";
00280 #elif defined(__CYGWIN__)
00281   // under cygwin, libraries can be loaded using dlopen
00282   // but their extension is .dll
00283   std::string ext = ".dll";
00284 #else
00285   std::string ext = ".so";
00286 #endif
00287 
00288   const char *found = strstr( dir->d_name, (const char *)ext.c_str() );
00289 
00290   return found ? ! strcmp( found, ext.c_str() ) : 0;
00291 }
00292 
00293 
00294 /****************/
00295 /*** scan Dir ***/
00296 vector<string>
00297 scanDirectory( string dir )
00298 {
00299   vector<string> entries;
00300 
00301   if ( dir.length() == 0 ) {
00302     return entries;
00303   }
00304 
00305   if ( dir[ dir.length() ] != '/' ) {
00306     dir += "/";
00307   }
00308 
00309   // Unix scandir call.
00310   TDirent **namelist;
00311 
00312   int nent = scandir( dir.c_str(), &namelist, filter, alphasort );
00313 
00314   if ( nent < 0 )
00315     return entries;
00316 
00317   // Copy from unix structure to the return structure.
00318   for ( int i = 0; i < nent; i++ ) {
00319 
00320     char *found = namelist[i]->d_name;
00321 
00322     string name = dir + found;
00323     entries.push_back( name );
00324 
00325     free( namelist[i] );
00326   }
00327 
00328   // Free unix structure.
00329   free( namelist );
00330 
00331   return entries;
00332 }
00333 
00334 
00335 
00336 
00337 /****************************************************************/
00338 /*********************** Random Generation **********************/
00339 
00340 #ifndef WIN32
00341 #include <sys/time.h>
00342 #include <sys/resource.h>
00343 
00344 #else
00345 #include <time.h>
00346 
00347 #endif
00348 
00349 
00350 /*******************/
00351 /*** init Random ***/
00352 int
00353 initRandom( unsigned int new_seed )
00354 {
00355   static unsigned int seed = 0;
00356 
00357   if ( seed && !new_seed ) {
00358 
00359     // reseeding rand can decrease the randomness, so avoid doing it
00360     return 1;
00361   }
00362 
00363   if ( new_seed ) {
00364 
00365     seed = new_seed;
00366   }
00367   else {
00368 
00369 #ifndef WIN32
00370     struct timeval time;
00371     gettimeofday( &time, (struct timezone *)NULL );
00372     seed = time.tv_usec;
00373 #else
00374     seed = (unsigned int) time( NULL );
00375 #endif
00376   }
00377 
00378   Log::instance()->debug( "Setting random seed %u\n", seed );
00379 
00380   srand( seed );
00381 
00382 // This is an workaround to get some things working with GCC. Unfortunately we don't
00383 // know if the implementation of functions like random_shuffle use rand or lrand48
00384 // internally so that we can seed it accordingly. Apparently, the test below is the
00385 // same one used by the function. A more portable solution would be to develop our
00386 // custom random number generator and pass it to random_shuffle or use it directly 
00387 // in other parts of the code.
00388 #ifdef _GLIBCPP_HAVE_DRAND48
00389   srand48( seed );
00390 #endif
00391 
00392   return 1;
00393 }
00394 
00395 /************************/
00396 /*** get Working path ***/
00397 std::string
00398 getWorkingPath()
00399 {
00400    char temp[MAXPATHLEN];
00401    return ( getcwd(temp, MAXPATHLEN) ? std::string( temp ) : std::string("") );
00402 }
00403 
00404 /*******************/
00405 /*** path Exists ***/
00406 bool
00407 pathExists( const std::string path )
00408 {
00409   struct stat status;
00410   if ( stat( path.c_str(), &status ) == 0 && S_ISDIR(status.st_mode) ) {
00411 
00412     return true;
00413   }
00414 
00415   return false;
00416 }
00417 
00418 /*******************/
00419 /*** create Path ***/
00420 bool
00421 createPath( const std::string path )
00422 {
00423   bool ok = false;
00424   int nRC = ::mkdir( path.c_str(), 0775 );
00425 
00426   if ( nRC == -1 ) {
00427 
00428     switch( errno ) {
00429 
00430       case ENOENT:
00431         // parent didn't exist, try to create it
00432         if( createPath( path.substr(0, path.find_last_of('/')) ) )
00433           // try to create again.
00434           ok = 0 == ::mkdir( path.c_str(), 0775 );
00435         else
00436           ok = false;
00437           break;
00438       case EEXIST:
00439         ok = true;
00440         break;
00441       default:
00442         ok = false;
00443         break;
00444     }
00445   }
00446   else {
00447 
00448     ok = true;
00449   }
00450 
00451   return ok;
00452 }