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

Revision 10171, 36.3 kB (checked in by robert, 4 years ago)

Refactored the Registry::ReadFileCallback?, WriteFileCallback? and ReaderWriter::Options to they are now defined in their own header and in the osgDB namespace.

Introduced a new FindFileCallback? to Registry to compliement the existing ReadFileCallback? and WriteFileCallback?.

Added support for assign Find, Read and WriteFileCallbacks? to osdDB::Options to enable plugins/applications to override the callbacks just for that
read/write call and any nested file operations

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