root/OpenSceneGraph/trunk/src/osgDB/FileUtils.cpp @ 10818

Revision 10818, 37.4 kB (checked in by robert, 5 years ago)

From Chris Hanson, " Add support for requesting and setting the current directory (a la getcwd/chdir) via a
C++-friendly string-class API.

Prevent osgDB::concatPaths from faulting if the supplied "left" path string is empty."

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
RevLine 
[5328]1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
[1529]2 *
3 * This library is open source and may be redistributed and/or modified under 
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
[773]13
[10818]14// handle TCHAR type on various platforms
15// #ifndef is inspired by https://svn.apache.org/repos/asf/logging/log4cxx/tags/v0_9_4/include/log4cxx/helpers/tchar.h
16// defining type as plain char is from unzip.h, line 64
17
18#ifndef TCHAR
19typedef char TCHAR;
20#endif
21
22
[9124]23// currently this impl is for _all_ platforms, except as defined.
[909]24// the mac version will change soon to reflect the path scheme under osx, but
25// for now, the above include is commented out, and the below code takes precedence.
[773]26
27#if defined(WIN32) && !defined(__CYGWIN__)
[8076]28    #include <io.h>
[9991]29    #define WINBASE_DECLARE_GET_MODULE_HANDLE_EX
[8076]30    #include <windows.h>
31    #include <winbase.h>
[2991]32    #include <sys/types.h>
33    #include <sys/stat.h>
[3367]34    #include <direct.h> // for _mkdir
[3465]35   
[3367]36    #define mkdir(x,y) _mkdir((x))
[3467]37    #define stat64 _stati64
[3367]38
[773]39    // set up for windows so acts just like unix access().
[9201]40#ifndef F_OK
[773]41    #define F_OK 4
[9201]42#endif
[3367]43
[909]44#else // unix
[3483]45
[5456]46#if defined( __APPLE__ )
47    // I'm not sure how we would handle this in raw Darwin
48    // without the AvailablilityMacros.
49    #include <AvailabilityMacros.h>
50    // 10.5 defines stat64 so we can't use this #define
51    // By default, MAC_OS_X_VERSION_MAX_ALLOWED is set to the latest
52    // system the headers know about. So I will use this as the control
53    // variable. (MIN_ALLOWED is set low by default so it is
54    // unhelpful in this case.)
55    // Unfortunately, we can't use the label MAC_OS_X_VERSION_10_4
56    // for older OS's like Jaguar, Panther since they are not defined,
57    // so I am going to hardcode the number.
58    #if (MAC_OS_X_VERSION_MAX_ALLOWED <= 1040)
59        #define stat64 stat
60    #endif
[7496]61#elif defined(__CYGWIN__) || defined(__FreeBSD__) || (defined(__hpux) && !defined(_LARGEFILE64_SOURCE))
[3483]62    #define stat64 stat
63#endif
64
[9475]65    #include <stdlib.h>
[773]66    #include <unistd.h>
[2971]67    #include <sys/types.h>
68    #include <sys/stat.h>
[8]69#endif
[773]70
[3367]71    // set up _S_ISDIR()
72#if !defined(S_ISDIR)
73if defined( _S_IFDIR) && !defined( __S_IFDIR)
74#    define __S_IFDIR _S_IFDIR
75endif
76define S_ISDIR(mode)    (mode&__S_IFDIR)
77#endif
78
[9124]79#include <osg/Config>
80#include <osgDB/ConvertUTF>
[775]81#include <osg/Notify>
82
83#include <osgDB/FileUtils>
84#include <osgDB/FileNameUtils>
85#include <osgDB/Registry>
86
[3367]87#include <errno.h>
[9343]88#include <string.h>
89
[3367]90#include <stack>
[775]91
[9124]92namespace osgDB
93{
94#ifdef OSG_USE_UTF8_FILENAME
95#define OSGDB_STRING_TO_FILENAME(s) osgDB::convertUTF8toUTF16(s)
96#define OSGDB_FILENAME_TO_STRING(s) osgDB::convertUTF16toUTF8(s)
97#define OSGDB_FILENAME_TEXT(x) L ## x
98#define OSGDB_WINDOWS_FUNCT(x) x ## W
[10129]99#define OSGDB_WINDOWS_FUNCT_STRING(x) #x "W"
[9124]100typedef wchar_t filenamechar;
101typedef std::wstring filenamestring;
102#else
103#define OSGDB_STRING_TO_FILENAME(s) s
104#define OSGDB_FILENAME_TO_STRING(s) s
105#define OSGDB_FILENAME_TEXT(x) x
106#define OSGDB_WINDOWS_FUNCT(x) x ## A
[9991]107#define OSGDB_WINDOWS_FUNCT_STRING(x) #x "A"
[9124]108typedef char filenamechar;
109typedef std::string filenamestring;
110#endif
111}
[6705]112
[9124]113FILE* osgDB::fopen(const char* filename, const char* mode)
114{
115#ifdef OSG_USE_UTF8_FILENAME
116    return ::_wfopen(convertUTF8toUTF16(filename).c_str(), convertUTF8toUTF16(mode).c_str());
117#else
118    return ::fopen(filename, mode);
119#endif
120}
[6705]121
[3367]122bool osgDB::makeDirectory( const std::string &path )
123{
124    if (path.empty())
125    {
126        osg::notify(osg::DEBUG_INFO) << "osgDB::makeDirectory(): cannot create an empty directory" << std::endl;
127        return false;
128    }
129   
[3464]130    struct stat64 stbuf;
[9124]131#ifdef OSG_USE_UTF8_FILENAME
132    if( _wstat64( OSGDB_STRING_TO_FILENAME(path).c_str(), &stbuf ) == 0 )
133#else
[3464]134    if( stat64( path.c_str(), &stbuf ) == 0 )
[9124]135#endif
[3367]136    {
137        if( S_ISDIR(stbuf.st_mode))
138            return true;
139        else
140        {
141            osg::notify(osg::DEBUG_INFO) << "osgDB::makeDirectory(): "  <<
142                    path << " already exists and is not a directory!" << std::endl;
143            return false;
144        }
145    }
146
147    std::string dir = path;
148    std::stack<std::string> paths;
149    while( true )
150    {
151        if( dir.empty() )
152            break;
153 
[9124]154#ifdef OSG_USE_UTF8_FILENAME
155        if( _wstat64( OSGDB_STRING_TO_FILENAME(dir).c_str(), &stbuf ) < 0 )
156#else
[3464]157        if( stat64( dir.c_str(), &stbuf ) < 0 )
[9124]158#endif
[3367]159        {
160            switch( errno )
161            {
162                case ENOENT:
163                case ENOTDIR:
164                    paths.push( dir );
165                    break;
166 
167                default:
[3659]168                    osg::notify(osg::DEBUG_INFO) << "osgDB::makeDirectory(): "  << strerror(errno) << std::endl;
[3367]169                    return false;
170            }
171        }
172        dir = getFilePath(std::string(dir));
173    }
174
175    while( !paths.empty() )
176    {
177        std::string dir = paths.top();
178 
[5161]179        #if defined(WIN32)
180            //catch drive name
181            if (dir.size() == 2 && dir.c_str()[1] == ':') {
182                paths.pop();
183                continue;
184            }
185        #endif
186
[9124]187#ifdef OSG_USE_UTF8_FILENAME
188        if ( _wmkdir(OSGDB_STRING_TO_FILENAME(dir).c_str())< 0 )
189#else
[3367]190        if( mkdir( dir.c_str(), 0755 )< 0 )
[9124]191#endif
[3367]192        {
[3659]193            osg::notify(osg::DEBUG_INFO) << "osgDB::makeDirectory(): "  << strerror(errno) << std::endl;
[3367]194            return false;
195        }
196        paths.pop();
197    }
198    return true;
199}
200
201bool osgDB::makeDirectoryForFile( const std::string &path )
202{
203    return makeDirectory( getFilePath( path ));
204}
205
[10818]206
207std::string osgDB::getCurrentWorkingDirectory( void )
208{
209    // MAX_PATH/cwd inspired by unzip.cpp
210#ifndef MAX_PATH
211    #define MAX_PATH 1024
212#endif
213    TCHAR rootdir[MAX_PATH];
214    if(getcwd(rootdir,MAX_PATH-1))
215    {
216        return(rootdir);
217    }
218    return("");
219}// osgDB::getCurrentWorkingDirectory
220
221
222
223bool osgDB::setCurrentWorkingDirectory( const std::string &newCurrentWorkingDirectory )
224{
225    if (newCurrentWorkingDirectory.empty())
226    {
227        osg::notify(osg::DEBUG_INFO) << "osgDB::setCurrentWorkingDirectory(): called with empty string." << std::endl;
228        return false;
229    }
230   
231#ifdef OSG_USE_UTF8_FILENAME
232    return _wchdir( OSGDB_STRING_TO_FILENAME(newCurrentWorkingDirectory).c_str()) == 0;
233#else
234    return chdir( newCurrentWorkingDirectory.c_str()) == 0;
235#endif
236
237    return true;
238} // osgDB::setCurrentWorkingDirectory
239
240
241
[3330]242void osgDB::convertStringPathIntoFilePathList(const std::string& paths,FilePathList& filepath)
243{
244#if defined(WIN32) && !defined(__CYGWIN__)
245    char delimitor = ';';
246#else
247    char delimitor = ':';
248#endif
249
250    if (!paths.empty())
251    {
252        std::string::size_type start = 0;
253        std::string::size_type end;
254        while ((end = paths.find_first_of(delimitor,start))!=std::string::npos)
255        {
256            filepath.push_back(std::string(paths,start,end-start));
257            start = end+1;
258        }
259
[7276]260        std::string lastPath(paths,start,std::string::npos);
261        if (!lastPath.empty())
262            filepath.push_back(lastPath);
[3330]263    }
264 
265}
266
[773]267bool osgDB::fileExists(const std::string& filename)
268{
[9124]269#ifdef OSG_USE_UTF8_FILENAME
270    return _waccess( OSGDB_STRING_TO_FILENAME(filename).c_str(), F_OK ) == 0;
271#else
[773]272    return access( filename.c_str(), F_OK ) == 0;
[9124]273#endif
[773]274}
275
[2971]276osgDB::FileType osgDB::fileType(const std::string& filename)
277{
[3464]278    struct stat64 fileStat;
[9124]279#ifdef OSG_USE_UTF8_FILENAME
280    if ( _wstat64(OSGDB_STRING_TO_FILENAME(filename).c_str(), &fileStat) != 0 )
281#else
282    if ( stat64(filename.c_str(), &fileStat) != 0 )
283#endif
[2971]284    {
285        return FILE_NOT_FOUND;
[2991]286    } // end if
[2992]287
288    if ( fileStat.st_mode & S_IFDIR )
[2991]289        return DIRECTORY;
[2992]290    else if ( fileStat.st_mode & S_IFREG )
[2971]291        return REGULAR_FILE;
[2992]292
[2991]293    return FILE_NOT_FOUND;
[2971]294}
295
[2444]296std::string osgDB::findFileInPath(const std::string& filename, const FilePathList& filepath,CaseSensitivity caseSensitivity)
[773]297{
[2420]298    if (filename.empty())
299        return filename;
[773]300
[3833]301    if (!isFileNameNativeStyle(filename))
302        return findFileInPath(convertFileNameToNativeStyle(filename), filepath, caseSensitivity);
[773]303
[3833]304
[773]305    for(FilePathList::const_iterator itr=filepath.begin();
306        itr!=filepath.end();
307        ++itr)
308    {
[3691]309        osg::notify(osg::DEBUG_INFO) << "itr='" <<*itr<< "'\n";
[6005]310        std::string path = itr->empty() ? filename : concatPaths(*itr, filename);
311       
312        path = getRealPath(path);
313
[773]314        osg::notify(osg::DEBUG_INFO) << "FindFileInPath() : trying " << path << " ...\n";
[2420]315        if(fileExists(path))
316        {
317            osg::notify(osg::DEBUG_INFO) << "FindFileInPath() : USING " << path << "\n";
318            return path;
319        }
[2444]320#ifndef WIN32 
321// windows already case insensitive so no need to retry..
322        else if (caseSensitivity==CASE_INSENSITIVE)
323        {
324            std::string foundfile = findFileInDirectory(filename,*itr,CASE_INSENSITIVE);
325            if (!foundfile.empty()) return foundfile;
326        }
327#endif
328           
[773]329    }
330
331    return std::string();
332}
333
[3691]334
[2444]335std::string osgDB::findDataFile(const std::string& filename,CaseSensitivity caseSensitivity)
[773]336{
[10171]337    return findDataFile(filename,static_cast<Options*>(0),caseSensitivity);
[3689]338}
339
[10171]340OSGDB_EXPORT std::string osgDB::findDataFile(const std::string& filename,const Options* options, CaseSensitivity caseSensitivity)
[3689]341{
[10171]342    return Registry::instance()->findDataFile(filename, options, caseSensitivity);
[773]343}
344
[2444]345std::string osgDB::findLibraryFile(const std::string& filename,CaseSensitivity caseSensitivity)
[773]346{
[10171]347    return Registry::instance()->findLibraryFile(filename, osgDB::Registry::instance()->getOptions(), caseSensitivity);
[773]348}
349
[2444]350std::string osgDB::findFileInDirectory(const std::string& fileName,const std::string& dirName,CaseSensitivity caseSensitivity)
[773]351{
352    bool needFollowingBackslash = false;
353    bool needDirectoryName = true;
354    osgDB::DirectoryContents dc;
355
[9425]356    std::string realDirName = dirName;
357    std::string realFileName = fileName;
358
359    // Skip case-insensitive recursion if on Windows
360    #ifdef WIN32
361        bool win32 = true;
362    #else
363        bool win32 = false;
364    #endif
365   
366    // If the fileName contains extra path information, make that part of the
367    // directory name instead
368    if (fileName != getSimpleFileName(fileName))
[773]369    {
[9425]370        // See if we need to add a slash between the directory and file
371        if (realDirName.empty())
372        {
373            realDirName = getFilePath(fileName);
374        }
375        else if (realDirName=="." || realDirName=="./" || realDirName==".\\")
376        {
377            realDirName = "./" + getFilePath(fileName);
378        }
379        else
380        {
381            char lastChar = dirName[dirName.size()-1];
382            if ((lastChar == '/') || (lastChar == '\\'))
383                realDirName = dirName + getFilePath(fileName);
384            else
385                realDirName = dirName + "/" + getFilePath(fileName);
386        }
387
388        // Simplify the file name
389        realFileName = getSimpleFileName(fileName);
390    }
391
392    osg::notify(osg::DEBUG_INFO) << "findFileInDirectory() : looking for " << realFileName << " in " << realDirName << "...\n";
393
394    if (realDirName.empty())
395    {
[773]396        dc = osgDB::getDirectoryContents(".");
397        needFollowingBackslash = false;
398        needDirectoryName = false;
399    }
[9425]400    else if (realDirName=="." || realDirName=="./" || realDirName==".\\")
[773]401    {
402        dc = osgDB::getDirectoryContents(".");
403        needFollowingBackslash = false;
404        needDirectoryName = false;
405    }
[9425]406    else if (realDirName=="/")
[773]407    {
[9425]408        dc = osgDB::getDirectoryContents("/");
409        needFollowingBackslash = false;
[773]410        needDirectoryName = true;
411    }
[9425]412    else
413    {
414        // See if we're working in case insensitive mode, and that we're not
415        // using Windows (the recursive search is not needed in these
416        // cases)
417        if ((caseSensitivity == CASE_INSENSITIVE) && (!win32))
418        {
419            // Split the last path element from the directory name
420            std::string parentPath = getFilePath(realDirName);
421            std::string lastElement = getSimpleFileName(realDirName);
[773]422
[9425]423            // See if we're already at the top level of the filesystem
424            if ((parentPath.empty()) && (!lastElement.empty()))
425            {
426                // Search for the first path element (ignoring case) in
427                // the top-level directory
428                realDirName = findFileInDirectory(lastElement, "/",
429                                                  CASE_INSENSITIVE);
430     
431                dc = osgDB::getDirectoryContents(realDirName);
432                needFollowingBackslash = true;
433                needDirectoryName = true;
434            }
435            else
436            {
437                // Recursively search for the last path element (ignoring case)
438                // in the parent path
439                realDirName = findFileInDirectory(lastElement, parentPath,
440                                                  CASE_INSENSITIVE);
441     
442                dc = osgDB::getDirectoryContents(realDirName);
443                char lastChar = realDirName[realDirName.size()-1];
444                if (lastChar=='/') needFollowingBackslash = false;
445                else if (lastChar=='\\') needFollowingBackslash = false;
446                else needFollowingBackslash = true;
447                needDirectoryName = true;
448            }
449        }
450        else
451        {
452            // No need for recursive search if we're doing an exact comparison
453            dc = osgDB::getDirectoryContents(realDirName);
454            char lastChar = realDirName[realDirName.size()-1];
455            if (lastChar=='/') needFollowingBackslash = false;
456            else if (lastChar=='\\') needFollowingBackslash = false;
457            else needFollowingBackslash = true;
458            needDirectoryName = true;
459        }
460    }
461
[773]462    for(osgDB::DirectoryContents::iterator itr=dc.begin();
463        itr!=dc.end();
464        ++itr)
465    {
[9425]466        if ((caseSensitivity==CASE_INSENSITIVE && osgDB::equalCaseInsensitive(realFileName,*itr)) ||
467            (realFileName==*itr))
[773]468        {
469            if (!needDirectoryName) return *itr;
[9425]470            else if (needFollowingBackslash) return realDirName+'/'+*itr;
471            else return realDirName+*itr;
[773]472        }
473    }
474    return "";
475}
476
[8901]477static void appendInstallationLibraryFilePaths(osgDB::FilePathList& filepath)
478{
479#ifdef OSG_DEFAULT_LIBRARY_PATH
[8911]480
[8901]481    // Append the install prefix path to the library search path if configured
[8911]482    filepath.push_back(ADDQUOTES(OSG_DEFAULT_LIBRARY_PATH));
[8901]483#endif
484}
485
[796]486#if defined(WIN32) && !defined(__CYGWIN__)
[773]487    #include <io.h>
488    #include <direct.h>
489
490    osgDB::DirectoryContents osgDB::getDirectoryContents(const std::string& dirName)
491    {
492        osgDB::DirectoryContents contents;
493
[9124]494        OSGDB_WINDOWS_FUNCT(WIN32_FIND_DATA) data;
495        HANDLE handle = OSGDB_WINDOWS_FUNCT(FindFirstFile)((OSGDB_STRING_TO_FILENAME(dirName) + OSGDB_FILENAME_TEXT("\\*")).c_str(), &data);
[773]496        if (handle != INVALID_HANDLE_VALUE)
497        {
498            do
499            {
[9124]500                contents.push_back(OSGDB_FILENAME_TO_STRING(data.cFileName));
[773]501            }
[9124]502            while (OSGDB_WINDOWS_FUNCT(FindNextFile)(handle, &data) != 0);
[773]503
504            FindClose(handle);
505        }
506        return contents;
507    }
508
509#else
510
511    #include <dirent.h>
512    osgDB::DirectoryContents osgDB::getDirectoryContents(const std::string& dirName)
513    {
514        osgDB::DirectoryContents contents;
515
516        DIR *handle = opendir(dirName.c_str());
517        if (handle)
518        {
519            dirent *rc;
520            while((rc = readdir(handle))!=NULL)
521            {
522                contents.push_back(rc->d_name);
523            }
524            closedir(handle);
525        }
526
527        return contents;
528    }
529
530#endif // unix getDirectoryContexts
531
[3330]532
533/////////////////////////////////////////////////////////////////////////////////////////////////
534//
535// Implementation of appendPlatformSpecificLibraryFilePaths(..)
536//
537#ifdef __sgi
538
539    void osgDB::appendPlatformSpecificLibraryFilePaths(FilePathList& filepath)
540    {
541        convertStringPathIntoFilePathList("/usr/lib32/:/usr/local/lib32/",filepath);
542
543        // bloody mess see rld(1) man page
[3336]544        char* ptr;
545
[3330]546        #if (_MIPS_SIM == _MIPS_SIM_ABI32)
547        if( (ptr = getenv( "LD_LIBRARY_PATH" )))
548        {
549            convertStringPathIntoFilePathList(ptr,filepath);
550        }
551
552        #elif (_MIPS_SIM == _MIPS_SIM_NABI32)
553
554        if( !(ptr = getenv( "LD_LIBRARYN32_PATH" )))
555            ptr = getenv( "LD_LIBRARY_PATH" );
556
557        if( ptr )
558        {
559            convertStringPathIntoFilePathList(ptr,filepath);
560        }
561
562        #elif (_MIPS_SIM == _MIPS_SIM_ABI64)
563
564        if( !(ptr = getenv( "LD_LIBRARY64_PATH" )))
565            ptr = getenv( "LD_LIBRARY_PATH" );
566
567        if( ptr )
568        {
569            convertStringPathIntoFilePathList(ptr,filepath);
570        }
571        #endif
[8901]572
573        appendInstallationLibraryFilePaths(filepath);
[3330]574    }
575
576
577#elif defined(__CYGWIN__)
578
579    void osgDB::appendPlatformSpecificLibraryFilePaths(FilePathList& filepath)
580    {
581        char* ptr;
582        if ((ptr = getenv( "PATH" )))
583        {
584            convertStringPathIntoFilePathList(ptr,filepath);
585        }
586
[8901]587        appendInstallationLibraryFilePaths(filepath);
588
[3330]589        convertStringPathIntoFilePathList("/usr/bin/:/usr/local/bin/",filepath);
590    }
591   
592#elif defined(WIN32)
593
594    void osgDB::appendPlatformSpecificLibraryFilePaths(FilePathList& filepath)
595    {
[7276]596        // See http://msdn2.microsoft.com/en-us/library/ms682586.aspx
597
598        // Safe DLL search mode changes the DLL search order to search for
[7298]599        // DLLs in the current directory after the system directories, instead
600        // of right after the application's directory. According to the article
601        // linked above, on Windows XP and Windows 2000, Safe DLL search mode
602        // is disabled by default. However, it is a good idea to enable it. We
603        // will search as if it was enabled.
[7276]604
605        // So if SafeDllSearchMode is enabled, the search order is as follows:
606
607        //   1. The directory from which the application loaded.
608        DWORD retval = 0;
609        const DWORD size = MAX_PATH;
[9124]610        filenamechar path[size];
611        retval = OSGDB_WINDOWS_FUNCT(GetModuleFileName)(NULL, path, size);
[7276]612        if (retval != 0 && retval < size)
613        {
[9124]614            filenamestring pathstr(path);
615            filenamestring executableDir(pathstr, 0,
616                                      pathstr.find_last_of(OSGDB_FILENAME_TEXT("\\/")));
617            convertStringPathIntoFilePathList(OSGDB_FILENAME_TO_STRING(executableDir), filepath);
[7276]618        }
619        else
620        {
621            osg::notify(osg::WARN) << "Could not get application directory "
622                "using Win32 API. It will not be searched." << std::endl;
623        }
624
[9991]625        //   2. The directory that the dll that contains this function is in.
626        // For static builds, this will be the executable directory.
627
[10024]628        #if defined(_MSC_VER)
629            // Requires use of the GetModuleHandleEx() function which is available only on Windows XP or higher.
630            // In order to allow execution on older versions, we load the function dynamically from the library and
631            // use it only if it's available.
632            OSGDB_WINDOWS_FUNCT(PGET_MODULE_HANDLE_EX) pGetModuleHandleEx = reinterpret_cast<OSGDB_WINDOWS_FUNCT(PGET_MODULE_HANDLE_EX)>
633                (GetProcAddress( GetModuleHandleA("kernel32.dll"), OSGDB_WINDOWS_FUNCT_STRING(GetModuleHandleEx)));
634            if( pGetModuleHandleEx )
635            {
636                HMODULE thisModule = 0;
[10129]637                static filenamechar static_variable = 0;    // Variable that is located in DLL address space.
[9991]638
[10024]639                if( pGetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, &static_variable, &thisModule) )
[9991]640                {
[10024]641                    retval = OSGDB_WINDOWS_FUNCT(GetModuleFileName)(thisModule, path, size);
642                    if (retval != 0 && retval < size)
643                    {
644                        filenamestring pathstr(path);
645                        filenamestring dllDir(pathstr, 0,
646                                                pathstr.find_last_of(OSGDB_FILENAME_TEXT("\\/")));
647                        convertStringPathIntoFilePathList(OSGDB_FILENAME_TO_STRING(dllDir), filepath);
648                    }
649                    else
650                    {
651                        osg::notify(osg::WARN) << "Could not get dll directory "
652                            "using Win32 API. It will not be searched." << std::endl;
653                    }
[9991]654                }
655                else
656                {
[10024]657                    osg::notify(osg::WARN) << "Could not get dll module handle "
658                        "using Win32 API. Dll directory will not be searched." << std::endl;
[9991]659                }
660            }
[10024]661        #endif
[9991]662
663        //   3. The system directory. Use the GetSystemDirectory function to
[7276]664        //      get the path of this directory.
[9124]665        filenamechar systemDir[(UINT)size];
666        retval = OSGDB_WINDOWS_FUNCT(GetSystemDirectory)(systemDir, (UINT)size);
667
[7276]668        if (retval != 0 && retval < size)
669        {
[9124]670            convertStringPathIntoFilePathList(OSGDB_FILENAME_TO_STRING(systemDir),
[7276]671                                              filepath);
672        }
673        else
674        {
675            osg::notify(osg::WARN) << "Could not get system directory using "
676                "Win32 API, using default directory." << std::endl;
677            convertStringPathIntoFilePathList("C:\\Windows\\System32",
678                                              filepath);
679        }
680
[9991]681        //   4. The 16-bit system directory. There is no function that obtains
[7276]682        //      the path of this directory, but it is searched.
[9991]683        //   5. The Windows directory. Use the GetWindowsDirectory function to
[7276]684        //      get the path of this directory.
[9124]685        filenamechar windowsDir[(UINT)size];
686        retval = OSGDB_WINDOWS_FUNCT(GetWindowsDirectory)(windowsDir, (UINT)size);
[7276]687        if (retval != 0 && retval < size)
688        {
[9124]689            convertStringPathIntoFilePathList(std::string(OSGDB_FILENAME_TO_STRING(windowsDir)) +
[7276]690                                              "\\System", filepath);
[9124]691            convertStringPathIntoFilePathList(OSGDB_FILENAME_TO_STRING(windowsDir),
[7276]692                                              filepath);
693        }
694        else
695        {
696            osg::notify(osg::WARN) << "Could not get Windows directory using "
697                "Win32 API, using default directory." << std::endl;
698            convertStringPathIntoFilePathList("C:\\Windows", filepath);
699            convertStringPathIntoFilePathList("C:\\Windows\\System", filepath);
700        }
701
702
[9991]703        //   6. The current directory.
[7276]704        convertStringPathIntoFilePathList(".", filepath);
705
[9991]706        //   7. The directories that are listed in the PATH environment
[7276]707        //      variable. Note that this does not include the per-application
708        //      path specified by the App Paths registry key.
[9124]709        filenamechar* ptr;
710#ifdef OSG_USE_UTF8_FILENAME
[9459]711        if ((ptr = _wgetenv(OSGDB_FILENAME_TEXT("PATH"))))
[9124]712#else
[9459]713        if ((ptr = getenv("PATH")))
[9124]714#endif
[3330]715        {
[7276]716            // Note that on any sane Windows system, some of the paths above
717            // will also be on the PATH (the values gotten in systemDir and
718            // windowsDir), but the DLL search goes sequentially and stops
719            // when a DLL is found, so I see no point in removing duplicates.
[9124]720            convertStringPathIntoFilePathList(OSGDB_FILENAME_TO_STRING(ptr), filepath);
[3330]721        }
[8901]722
723        appendInstallationLibraryFilePaths(filepath);
[3330]724    }
725   
726#elif defined(__APPLE__)
727
[3333]728    // #define COMPILE_COCOA_VERSION
[3372]729    #define COMPILE_CARBON_VERSION
[4420]730    // WARNING: Cocoa version is currently untested.
731    #ifdef COMPILE_COCOA_VERSION
732        #include <Foundation/Foundation.h>
733    #endif
734    #ifdef COMPILE_CARBON_VERSION
735        #include <CoreServices/CoreServices.h>
736        #include <CoreFoundation/CoreFoundation.h>
737        #include <Carbon/Carbon.h>
738    #endif
739    #include <iostream>
[3333]740   
[4420]741    // These functions are local to FileUtils.cpp and not exposed to the API
742    // returns the path string except for numToShorten directories stripped off the end
743    std::string GetShortenedPath(std::string path, int numToShorten)
744    {
745        unsigned int i = path.length() - 1;
746        if(path[i] == '/') i--;
747        while(i > 1 && numToShorten)
748        {
749            if(path[i] == '/')
750                numToShorten--;
751            i--;
752        }
753        return path.substr(0,i + 1);
754    }
[3333]755
[4420]756    // returns an absolute (POSIX on MacOS X) path from a CFURLRef
757    std::string GetPathFromCFURLRef(CFURLRef urlRef)
758    {
759        char buffer[1024];
760        std::string path;
761        if(CFURLGetFileSystemRepresentation(urlRef, true, (UInt8*)buffer, 1024))
762            path = std::string(buffer);
763        return path;
764    }
765
766    // returns the absolute path to the main bundle
767    std::string GetApplicationBundlePath(CFBundleRef mainBundle)
768    {
769        std::string path;
770        CFURLRef urlRef = CFBundleCopyBundleURL(mainBundle);
771        if(urlRef)
[4990]772        {
[4420]773            path = GetPathFromCFURLRef(urlRef);
[4990]774            CFRelease(urlRef); // docs say we are responsible for releasing CFURLRef
775        }
[4420]776        return path;
777       
778    }
779
780    std::string GetApplicationParentPath(CFBundleRef mainBundle)
781    {
782        return GetShortenedPath(GetApplicationBundlePath(mainBundle), 1);
783    }
784
785    std::string GetApplicationPluginsPath(CFBundleRef mainBundle)
786    {
787        std::string path;
788        CFURLRef urlRef = CFBundleCopyBuiltInPlugInsURL(mainBundle);
789        if(urlRef)
[4990]790        {
[4420]791            path = GetPathFromCFURLRef(urlRef);
[4990]792            CFRelease(urlRef);
793        }
[4420]794        return path;
795       
796    }
797
798    std::string GetApplicationResourcesPath(CFBundleRef mainBundle)
799    {
800        std::string path;
801        CFURLRef urlRef = CFBundleCopyResourcesDirectoryURL(mainBundle);
802        if(urlRef)
[4990]803        {
[4420]804            path = GetPathFromCFURLRef(urlRef);
[4990]805            CFRelease(urlRef);
806        }
[4420]807        return path;
808    }
809
[3330]810    // The Cocoa version is about 10 lines of code.
811    // The Carbon version is noticably longer.
812    // Unfortunately, the Cocoa version requires -lobjc to be
813    // linked in when creating an executable.
814    // Rumor is that this will be done autmatically in gcc 3.5/Tiger,
815    // but for now, this will cause a lot of headaches for people
816    // who aren't familiar with this concept, so the Carbon version
817    // is preferable.
818    // But for the curious, both implementations are here.
819    // Note that if the Cocoa version is used, the file should be
820    // renamed to use the .mm extension to denote Objective-C++.
821    // And of course, you will need to link against Cocoa
[5456]822    // Update: There is a bug in the Cocoa version. Advanced users can remap
823    // their systems so these paths go somewhere else. The Carbon calls
824    // will catch this, but the hardcoded Cocoa code below will not.
[3330]825
[3333]826    #ifdef COMPILE_COCOA_VERSION
[3330]827    // OS X has preferred locations for where PlugIns should be located.
828    // This function will set this as the order to search:
829    // YourProgram.app/Contents/PlugIns
830    // ~/Library/Application Support/OpenSceneGraph/PlugIns
831    // /Library/Application Support/OpenSceneGraph/PlugIns
832    // /Network/Library/Application Support/OpenSceneGraph/PlugIns
833    //
834    // As a side effect of this function, if the application is not a
835    // bundle, the first place searched becomes
836    // YourProgram/PlugIns
837    //
838    // In principle, these other directories should be searched:
839    // ~/Library/Application Support/YourProgram/PlugIns
840    // /Library/Application Support/YourProgram/PlugIns
841    // /Network/Library/Application Support/TheProgram/PlugIns
842    // But I'm not going to worry about it for now because the
843    // bundle's PlugIns directory is supposed to be the preferred
844    // place for this anyway.
845    //
846    // Another directory that might be worth considering is
847    // the directory the program resides in,
848    // but I'm worried about multiplatform distribution.
849    // Because .so is used by other platforms like Linux, we
850    // could end up loading the wrong binary.
851    // I'm not sure how robust the current code is for this case.
852    // Assuming the program doesn't crash, will OSG move on to the
853    // next search directory, or just give up?
854    void osgDB::appendPlatformSpecificLibraryFilePaths(FilePathList& filepath)
855    {
856        char* ptr;
857        if ((ptr = getenv( "DYLD_LIBRARY_PATH" )) )
858        {
859            convertStringPathIntoFilePathList(ptr, filepath);
860        }
861
[8901]862        appendInstallationLibraryFilePaths(filepath);
863
[3330]864        // Since this is currently the only Objective-C code in the
865        // library, we need an autoreleasepool for obj-c memory management.
866        // If more Obj-C is added, we might move this pool to another
867        // location so it can be shared. Pools seem to be stackable,
868        // so I don't think there will be a problem if multiple pools
869        // exist at a time.
870        NSAutoreleasePool* mypool = [[NSAutoreleasePool alloc] init];
871
872        NSString* myBundlePlugInPath;
873        NSString* userSupportDir;
874
875        // This will grab the "official" bundle plug in path.
876        // It will be YourProgram.app/Contents/PlugIns (for App bundles)
877        // or YourProgram/PlugIns (for Unix executables)
878        myBundlePlugInPath = [[NSBundle mainBundle] builtInPlugInsPath];
879
880        // Now setup the other search paths
881        // Cocoa has a nice method for tilde expansion.
882        // There's probably a better way of getting this directory, but I
883        // can't find the call.
884        userSupportDir = [@"~/Library/Application Support/OpenSceneGraph/PlugIns" stringByExpandingTildeInPath];
885
886        // Can setup the remaining directories directly in C++
887
888        // Since Obj-C and C++ objects don't understand each other,
889        // the Obj-C strings must be converted down to C strings so
890        // C++ can make them into C++ strings.
891        filepath.push_back( [myBundlePlugInPath UTF8String] );
892        filepath.push_back( [userSupportDir UTF8String] );
893
894        filepath.push_back( "/Library/Application Support/OpenSceneGraph/PlugIns" );
895        filepath.push_back( "/Network/Library/Application Support/OpenSceneGraph/PlugIns" );
896
897        // Clean up the autorelease pool
898        [mypool release];
899    }
900
[3333]901    #elif defined(COMPILE_CARBON_VERSION)
[3330]902
903    // OS X has preferred locations for where PlugIns should be located.
904    // This function will set this as the order to search:
905    // YourProgram.app/Contents/PlugIns
906    // ~/Library/Application Support/OpenSceneGraph/PlugIns
907    // /Library/Application Support/OpenSceneGraph/PlugIns
908    // /Network/Library/Application Support/OpenSceneGraph/PlugIns
909    //
910    // In principle, these other directories should be searched:
911    // ~/Library/Application Support/YourProgram/PlugIns
912    // /Library/Application Support/YourProgram/PlugIns
913    // /Network/Library/Application Support/TheProgram/PlugIns
914    // But I'm not going to worry about it for now because the
915    // bundle's PlugIns directory is supposed to be the preferred
916    // place for this anyway.
917    //
918    // Another directory that might be worth considering is
919    // the directory the program resides in,
920    // but I'm worried about multiplatform distribution.
921    // Because .so is used by other platforms like Linux, we
922    // could end up loading the wrong binary.
923    // I'm not sure how robust the current code is for this case.
924    // Assuming the program doesn't crash, will OSG move on to the
925    // next search directory, or just give up?
926    void osgDB::appendPlatformSpecificLibraryFilePaths(FilePathList& filepath)
927    {
928        char* ptr;
929        if ((ptr = getenv( "DYLD_LIBRARY_PATH" )) )
930        {
931            convertStringPathIntoFilePathList(ptr, filepath);
932        }
933
[8901]934        appendInstallationLibraryFilePaths(filepath);
935
[3330]936        const std::string OSG_PLUGIN_PATH("/OpenSceneGraph/PlugIns");
937        CFURLRef  url;
938        CFBundleRef myBundle;
939        FSRef     f;
[4327]940        OSErr    errCode;
[3330]941
942        // Start with the the Bundle PlugIns directory.
943
[4420]944        // Get the main bundle first. No need to retain or release it since
945        //  we are not keeping a reference
[3330]946        myBundle = CFBundleGetMainBundle();
[4420]947       
[3330]948        if(myBundle != NULL)
949        {
[4420]950            // CFBundleGetMainBundle will return a bundle ref even if
951            //  the application isn't part of a bundle, so we need to check
952            //  if the path to the bundle ends in ".app" to see if it is a
953            //  proper application bundle. If it is, the plugins path is added
954            std::string bundlePath = GetApplicationBundlePath(myBundle);
955            if( bundlePath.substr(bundlePath.length() - 4, 4) == std::string(".app") )
956                filepath.push_back(GetApplicationPluginsPath(myBundle));
[3330]957        }
958        else
959        {
[3331]960            osg::notify( osg::DEBUG_INFO ) << "Couldn't find the Application Bundle" << std::endl;
[3330]961        }
962
963        // Next, check the User's Application Support folder
964        errCode = FSFindFolder( kUserDomain, kApplicationSupportFolderType, kDontCreateFolder, &f );
965        if(noErr == errCode)
966        {
967            // Get the URL
968            url = CFURLCreateFromFSRef( 0, &f );
[4420]969            if(url)
[4990]970            {
[4420]971                filepath.push_back(GetPathFromCFURLRef(url) + OSG_PLUGIN_PATH);
[4990]972                CFRelease( url );
973            }
[4419]974            else
[4420]975                osg::notify( osg::DEBUG_INFO ) << "Couldn't create CFURLRef for User's application support Path" << std::endl;
976
[4419]977            url = NULL;
[3330]978        }
979        else
980        {
[3331]981            osg::notify( osg::DEBUG_INFO ) << "Couldn't find the User's Application Support Path" << std::endl;
[3330]982        }
983
984        // Next, check the Local System's Application Support Folder
985        errCode = FSFindFolder( kLocalDomain, kApplicationSupportFolderType, kDontCreateFolder, &f );
986        if(noErr == errCode)
987        {
988            // Get the URL
989            url = CFURLCreateFromFSRef( 0, &f );
[4420]990           
991            if(url)
[4990]992            {
[4420]993                filepath.push_back(GetPathFromCFURLRef(url) + OSG_PLUGIN_PATH);
[4990]994                CFRelease( url );
995            }
[4419]996            else
[4420]997                osg::notify( osg::DEBUG_INFO ) << "Couldn't create CFURLRef for local System's ApplicationSupport Path" << std::endl;
998
[4419]999            url = NULL;
[3330]1000        }
1001        else
1002        {
[3331]1003            osg::notify( osg::DEBUG_INFO ) << "Couldn't find the Local System's Application Support Path" << std::endl;
[3330]1004        }
1005
1006        // Finally, check the Network Application Support Folder
1007        // This one has a likely chance of not existing so an error
1008        // may be returned. Don't panic.
1009        errCode = FSFindFolder( kNetworkDomain, kApplicationSupportFolderType, kDontCreateFolder, &f );
1010        if(noErr == errCode)
1011        {
1012            // Get the URL
1013            url = CFURLCreateFromFSRef( 0, &f );
[4420]1014           
1015            if(url)
[4990]1016            {
[4420]1017                filepath.push_back(GetPathFromCFURLRef(url) + OSG_PLUGIN_PATH);
[4990]1018                CFRelease( url );
1019            }
[4419]1020            else
[4420]1021                osg::notify( osg::DEBUG_INFO ) << "Couldn't create CFURLRef for network Application Support Path" << std::endl;
1022
1023            url = NULL;
[3330]1024        }
1025        else
1026        {
[4327]1027        // had to comment out as it segfauls the OSX app otherwise
[3410]1028            // osg::notify( osg::DEBUG_INFO ) << "Couldn't find the Network Application Support Path" << std::endl;
[3330]1029        }
1030    }
[3333]1031    #else
1032    void osgDB::appendPlatformSpecificLibraryFilePaths(FilePathList& filepath)
1033    {
1034        char* ptr;
1035        if ((ptr = getenv( "DYLD_LIBRARY_PATH" )) )
1036        {
1037            convertStringPathIntoFilePathList(ptr, filepath);
1038        }
[8901]1039
1040        appendInstallationLibraryFilePaths(filepath);
[3333]1041    }
[3330]1042    #endif
1043   
1044#else   
1045
1046    void osgDB::appendPlatformSpecificLibraryFilePaths(FilePathList& filepath)
1047    {
1048
1049       char* ptr;
1050       if( (ptr = getenv( "LD_LIBRARY_PATH" )) )
1051        {
1052            convertStringPathIntoFilePathList(ptr,filepath);
1053        }
1054
[8901]1055        appendInstallationLibraryFilePaths(filepath);
[4327]1056
1057#if defined(__ia64__) || defined(__x86_64__)
1058        convertStringPathIntoFilePathList("/usr/lib/:/usr/lib64/:/usr/local/lib/:/usr/local/lib64/",filepath);
1059#else
[3330]1060        convertStringPathIntoFilePathList("/usr/lib/:/usr/local/lib/",filepath);
[4327]1061#endif
[3330]1062
1063    }
1064
1065#endif
1066
1067
1068
1069
[4420]1070#ifdef __APPLE__
1071    void osgDB::appendPlatformSpecificResourceFilePaths(FilePathList& filepath)
1072    {
1073        // Get the main application bundle
1074        CFBundleRef mainBundle = CFBundleGetMainBundle();
1075       
1076        if (mainBundle != NULL) {
1077            // Get the parent directory and the resources directory
1078            std::string bundlePath = GetApplicationBundlePath(mainBundle);
1079            std::string resourcesPath = GetApplicationResourcesPath(mainBundle);
1080           
1081            // check if application is really part of a .app bundle
1082            if(bundlePath.substr(bundlePath.length() - 4, 4) == std::string(".app"))
1083            {
1084                if(resourcesPath != std::string(""))
1085                    filepath.push_back( resourcesPath );
1086               
1087                std::string parentPath = GetShortenedPath(bundlePath, 1);
1088                if(parentPath != std::string(""))
1089                    filepath.push_back( parentPath );
1090            }
1091        }
1092        else
1093        {
1094            osg::notify( osg::DEBUG_INFO ) << "Couldn't find the Application Bundle." << std::endl;
1095        }
1096    }
1097#else
[4600]1098    void osgDB::appendPlatformSpecificResourceFilePaths(FilePathList& /*filepath*/)
[4420]1099    {
1100    }
1101#endif
[3330]1102
1103
1104
1105
1106
Note: See TracBrowser for help on using the browser.