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

Revision 3330, 21.8 kB (checked in by robert, 10 years ago)

Added a new osgDB::appendPlatformSpecificLibraryFilePaths() method to FileUtils?.cpp
Includes a new OSX code from Eric Wing

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 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#if 0 // defined(__APPLE__)
14#include "FileUtils_Mac.cpp" // this is not functional yet -- fix!
15#else
16
17// currently this impl is for _all_ platforms, execpt as defined.
18// the mac version will change soon to reflect the path scheme under osx, but
19// for now, the above include is commented out, and the below code takes precedence.
20
21#if defined(WIN32) && !defined(__CYGWIN__)
22    #include <Io.h>
23    #include <Windows.h>
24    #include <Winbase.h>
25    #include <sys/types.h>
26    #include <sys/stat.h>
27    // set up for windows so acts just like unix access().
28    #define F_OK 4
29#else // unix
30    #include <unistd.h>
31    #include <sys/types.h>
32    #include <sys/stat.h>
33#endif
34
35#include <osg/Notify>
36
37#include <osgDB/FileUtils>
38#include <osgDB/FileNameUtils>
39#include <osgDB/Registry>
40
41
42void osgDB::convertStringPathIntoFilePathList(const std::string& paths,FilePathList& filepath)
43{
44#if defined(WIN32) && !defined(__CYGWIN__)
45    char delimitor = ';';
46#else
47    char delimitor = ':';
48#endif
49
50    if (!paths.empty())
51    {
52        std::string::size_type start = 0;
53        std::string::size_type end;
54        while ((end = paths.find_first_of(delimitor,start))!=std::string::npos)
55        {
56            filepath.push_back(std::string(paths,start,end-start));
57            start = end+1;
58        }
59
60        filepath.push_back(std::string(paths,start,std::string::npos));
61    }
62 
63}
64
65bool osgDB::fileExists(const std::string& filename)
66{
67    return access( filename.c_str(), F_OK ) == 0;
68}
69
70osgDB::FileType osgDB::fileType(const std::string& filename)
71{
72#if 1
73
74    // proposed single code path, from Bruce Clay.
75    struct stat fileStat;
76    if ( stat(filename.c_str(), &fileStat) != 0 )
77    {
78        return FILE_NOT_FOUND;
79    } // end if
80
81    if ( fileStat.st_mode & S_IFDIR )
82        return DIRECTORY;
83    else if ( fileStat.st_mode & S_IFREG )
84        return REGULAR_FILE;
85
86    return FILE_NOT_FOUND;
87
88#else
89    #if defined(WIN32) && !defined(__CYGWIN__)
90        struct _stat fileStat;
91        if ( _stat(filename.c_str(), &fileStat) != 0 )
92        {
93            return FILE_NOT_FOUND;
94        } // end if
95
96        if ( fileStat.st_mode & _S_IFDIR )
97            return DIRECTORY;
98        else if ( fileStat.st_mode & _S_IFREG )
99            return REGULAR_FILE;
100
101        return FILE_NOT_FOUND;
102
103    #else
104        struct stat fileStat;
105
106        if(stat(filename.c_str(), &fileStat) == -1)
107        {
108            return FILE_NOT_FOUND;
109        }
110
111        if(S_ISREG(fileStat.st_mode))
112        {
113            return REGULAR_FILE;
114        }
115
116        if(S_ISDIR(fileStat.st_mode))
117        {
118            return DIRECTORY;
119        }
120
121        return FILE_NOT_FOUND;
122    #endif
123#endif
124}
125
126std::string osgDB::findFileInPath(const std::string& filename, const FilePathList& filepath,CaseSensitivity caseSensitivity)
127{
128    if (filename.empty())
129        return filename;
130
131    if(fileExists(filename))
132    {
133        osg::notify(osg::DEBUG_INFO) << "FindFileInPath(" << filename << "): returning " << filename << std::endl;
134        return filename;
135    }
136
137    for(FilePathList::const_iterator itr=filepath.begin();
138        itr!=filepath.end();
139        ++itr)
140    {
141        std::string path = *itr + '/'+ filename;
142        osg::notify(osg::DEBUG_INFO) << "FindFileInPath() : trying " << path << " ...\n";
143        if(fileExists(path))
144        {
145            osg::notify(osg::DEBUG_INFO) << "FindFileInPath() : USING " << path << "\n";
146            return path;
147        }
148#ifndef WIN32 
149// windows already case insensitive so no need to retry..
150        else if (caseSensitivity==CASE_INSENSITIVE)
151        {
152            std::string foundfile = findFileInDirectory(filename,*itr,CASE_INSENSITIVE);
153            if (!foundfile.empty()) return foundfile;
154        }
155#endif
156           
157    }
158
159    return std::string();
160}
161
162std::string osgDB::findDataFile(const std::string& filename,CaseSensitivity caseSensitivity)
163{
164    if (filename.empty()) return filename;
165
166    const FilePathList& filepath = Registry::instance()->getDataFilePathList();
167
168    std::string fileFound = findFileInPath(filename, filepath,caseSensitivity);
169    if (!fileFound.empty()) return fileFound;
170
171    // if a directory is included in the filename, get just the (simple) filename itself and try that
172    std::string simpleFileName = getSimpleFileName(filename);
173    if (simpleFileName!=filename)
174    {
175        std::string fileFound = findFileInPath(simpleFileName, filepath,caseSensitivity);
176        if (!fileFound.empty()) return fileFound;
177    }
178
179    // return empty string.
180    return std::string();
181}
182
183std::string osgDB::findLibraryFile(const std::string& filename,CaseSensitivity caseSensitivity)
184{
185    if (filename.empty())
186        return filename;
187
188    const FilePathList& filepath = Registry::instance()->getLibraryFilePathList();
189
190    std::string fileFound = findFileInPath(filename, filepath,caseSensitivity);
191    if (!fileFound.empty())
192        return fileFound;
193
194    // if a directory is included in the filename, get just the (simple) filename itself and try that
195    std::string simpleFileName = getSimpleFileName(filename);
196    if (simpleFileName!=filename)
197    {
198        std::string fileFound = findFileInPath(simpleFileName, filepath,caseSensitivity);
199        if (!fileFound.empty()) return fileFound;
200    }
201
202    // failed with direct paths,
203    // now try prepending the filename with "osgPlugins/"
204    return findFileInPath("osgPlugins/"+simpleFileName,filepath,caseSensitivity);
205}
206
207std::string osgDB::findFileInDirectory(const std::string& fileName,const std::string& dirName,CaseSensitivity caseSensitivity)
208{
209    bool needFollowingBackslash = false;
210    bool needDirectoryName = true;
211    osgDB::DirectoryContents dc;
212
213    if (dirName.empty())
214    {
215        dc = osgDB::getDirectoryContents(".");
216        needFollowingBackslash = false;
217        needDirectoryName = false;
218    }
219    else if (dirName=="." || dirName=="./" || dirName==".\\")
220    {
221        dc = osgDB::getDirectoryContents(".");
222        needFollowingBackslash = false;
223        needDirectoryName = false;
224    }
225    else
226    {
227        dc = osgDB::getDirectoryContents(dirName);
228        char lastChar = dirName[dirName.size()-1];
229        if (lastChar=='/') needFollowingBackslash = false;
230        else if (lastChar=='\\') needFollowingBackslash = false;
231        else needFollowingBackslash = true;
232        needDirectoryName = true;
233    }
234
235    for(osgDB::DirectoryContents::iterator itr=dc.begin();
236        itr!=dc.end();
237        ++itr)
238    {
239        if ((caseSensitivity==CASE_INSENSITIVE && osgDB::equalCaseInsensitive(fileName,*itr)) ||
240            (fileName==*itr))
241        {
242            if (!needDirectoryName) return *itr;
243            else if (needFollowingBackslash) return dirName+'/'+*itr;
244            else return dirName+*itr;
245        }
246    }
247    return "";
248}
249
250#if defined(WIN32) && !defined(__CYGWIN__)
251    #include <io.h>
252    #include <direct.h>
253
254    osgDB::DirectoryContents osgDB::getDirectoryContents(const std::string& dirName)
255    {
256        osgDB::DirectoryContents contents;
257
258        WIN32_FIND_DATA data;
259        HANDLE handle = FindFirstFile((dirName + "\\*").c_str(), &data);
260        if (handle != INVALID_HANDLE_VALUE)
261        {
262            do
263            {
264                contents.push_back(data.cFileName);
265            }
266            while (FindNextFile(handle, &data) != 0);
267
268            FindClose(handle);
269        }
270        return contents;
271    }
272
273#else
274
275    #include <dirent.h>
276    osgDB::DirectoryContents osgDB::getDirectoryContents(const std::string& dirName)
277    {
278        osgDB::DirectoryContents contents;
279
280        DIR *handle = opendir(dirName.c_str());
281        if (handle)
282        {
283            dirent *rc;
284            while((rc = readdir(handle))!=NULL)
285            {
286                contents.push_back(rc->d_name);
287            }
288            closedir(handle);
289        }
290
291        return contents;
292    }
293
294#endif // unix getDirectoryContexts
295
296#endif // ! target mac carbon
297
298
299
300/////////////////////////////////////////////////////////////////////////////////////////////////
301//
302// Implementation of appendPlatformSpecificLibraryFilePaths(..)
303//
304#ifdef __sgi
305
306    void osgDB::appendPlatformSpecificLibraryFilePaths(FilePathList& filepath)
307    {
308        convertStringPathIntoFilePathList("/usr/lib32/:/usr/local/lib32/",filepath);
309
310        // bloody mess see rld(1) man page
311        #if (_MIPS_SIM == _MIPS_SIM_ABI32)
312
313        char* ptr;
314        if( (ptr = getenv( "LD_LIBRARY_PATH" )))
315        {
316            convertStringPathIntoFilePathList(ptr,filepath);
317        }
318
319        #elif (_MIPS_SIM == _MIPS_SIM_NABI32)
320
321        if( !(ptr = getenv( "LD_LIBRARYN32_PATH" )))
322            ptr = getenv( "LD_LIBRARY_PATH" );
323
324        if( ptr )
325        {
326            convertStringPathIntoFilePathList(ptr,filepath);
327        }
328
329        #elif (_MIPS_SIM == _MIPS_SIM_ABI64)
330
331        if( !(ptr = getenv( "LD_LIBRARY64_PATH" )))
332            ptr = getenv( "LD_LIBRARY_PATH" );
333
334        if( ptr )
335        {
336            convertStringPathIntoFilePathList(ptr,filepath);
337        }
338        #endif
339    }
340
341
342#elif defined(__CYGWIN__)
343
344    void osgDB::appendPlatformSpecificLibraryFilePaths(FilePathList& filepath)
345    {
346        char* ptr;
347        if ((ptr = getenv( "PATH" )))
348        {
349            convertStringPathIntoFilePathList(ptr,filepath);
350        }
351
352        convertStringPathIntoFilePathList("/usr/bin/:/usr/local/bin/",filepath);
353
354    }
355   
356#elif defined(WIN32)
357
358    void osgDB::appendPlatformSpecificLibraryFilePaths(FilePathList& filepath)
359    {
360        char* ptr;
361        if ((ptr = getenv( "PATH" )))
362        {
363            convertStringPathIntoFilePathList(ptr,filepath);
364        }
365
366        convertStringPathIntoFilePathList("C:/Windows/System/",filepath);
367    }
368   
369#elif defined(__APPLE__)
370
371    // The Cocoa version is about 10 lines of code.
372    // The Carbon version is noticably longer.
373    // Unfortunately, the Cocoa version requires -lobjc to be
374    // linked in when creating an executable.
375    // Rumor is that this will be done autmatically in gcc 3.5/Tiger,
376    // but for now, this will cause a lot of headaches for people
377    // who aren't familiar with this concept, so the Carbon version
378    // is preferable.
379    // But for the curious, both implementations are here.
380    // Note that if the Cocoa version is used, the file should be
381    // renamed to use the .mm extension to denote Objective-C++.
382    // And of course, you will need to link against Cocoa
383
384    // #define COMPILE_COCOA_VERSION_WITH_OBJECT-C++
385    #ifdef COMPILE_COCOA_VERSION_WITH_OBJECT-C++
386    #include <Foundation/Foundation.h>
387    // OS X has preferred locations for where PlugIns should be located.
388    // This function will set this as the order to search:
389    // YourProgram.app/Contents/PlugIns
390    // ~/Library/Application Support/OpenSceneGraph/PlugIns
391    // /Library/Application Support/OpenSceneGraph/PlugIns
392    // /Network/Library/Application Support/OpenSceneGraph/PlugIns
393    //
394    // As a side effect of this function, if the application is not a
395    // bundle, the first place searched becomes
396    // YourProgram/PlugIns
397    //
398    // In principle, these other directories should be searched:
399    // ~/Library/Application Support/YourProgram/PlugIns
400    // /Library/Application Support/YourProgram/PlugIns
401    // /Network/Library/Application Support/TheProgram/PlugIns
402    // But I'm not going to worry about it for now because the
403    // bundle's PlugIns directory is supposed to be the preferred
404    // place for this anyway.
405    //
406    // Another directory that might be worth considering is
407    // the directory the program resides in,
408    // but I'm worried about multiplatform distribution.
409    // Because .so is used by other platforms like Linux, we
410    // could end up loading the wrong binary.
411    // I'm not sure how robust the current code is for this case.
412    // Assuming the program doesn't crash, will OSG move on to the
413    // next search directory, or just give up?
414    void osgDB::appendPlatformSpecificLibraryFilePaths(FilePathList& filepath)
415    {
416        char* ptr;
417        if ((ptr = getenv( "DYLD_LIBRARY_PATH" )) )
418        {
419            convertStringPathIntoFilePathList(ptr, filepath);
420        }
421
422        // Since this is currently the only Objective-C code in the
423        // library, we need an autoreleasepool for obj-c memory management.
424        // If more Obj-C is added, we might move this pool to another
425        // location so it can be shared. Pools seem to be stackable,
426        // so I don't think there will be a problem if multiple pools
427        // exist at a time.
428        NSAutoreleasePool* mypool = [[NSAutoreleasePool alloc] init];
429
430        NSString* myBundlePlugInPath;
431        NSString* userSupportDir;
432
433        // This will grab the "official" bundle plug in path.
434        // It will be YourProgram.app/Contents/PlugIns (for App bundles)
435        // or YourProgram/PlugIns (for Unix executables)
436        myBundlePlugInPath = [[NSBundle mainBundle] builtInPlugInsPath];
437
438        // Now setup the other search paths
439        // Cocoa has a nice method for tilde expansion.
440        // There's probably a better way of getting this directory, but I
441        // can't find the call.
442        userSupportDir = [@"~/Library/Application Support/OpenSceneGraph/PlugIns" stringByExpandingTildeInPath];
443
444        // Can setup the remaining directories directly in C++
445
446        // Since Obj-C and C++ objects don't understand each other,
447        // the Obj-C strings must be converted down to C strings so
448        // C++ can make them into C++ strings.
449        filepath.push_back( [myBundlePlugInPath UTF8String] );
450        filepath.push_back( [userSupportDir UTF8String] );
451
452        filepath.push_back( "/Library/Application Support/OpenSceneGraph/PlugIns" );
453        filepath.push_back( "/Network/Library/Application Support/OpenSceneGraph/PlugIns" );
454
455        // Clean up the autorelease pool
456        [mypool release];
457    }
458
459    #else
460
461    #include <CoreServices/CoreServices.h>
462    // OS X has preferred locations for where PlugIns should be located.
463    // This function will set this as the order to search:
464    // YourProgram.app/Contents/PlugIns
465    // ~/Library/Application Support/OpenSceneGraph/PlugIns
466    // /Library/Application Support/OpenSceneGraph/PlugIns
467    // /Network/Library/Application Support/OpenSceneGraph/PlugIns
468    //
469    // As a side effect of this function, if the application is not a
470    // bundle, the first place searched becomes
471    // YourProgram/PlugIns
472    //
473    // In principle, these other directories should be searched:
474    // ~/Library/Application Support/YourProgram/PlugIns
475    // /Library/Application Support/YourProgram/PlugIns
476    // /Network/Library/Application Support/TheProgram/PlugIns
477    // But I'm not going to worry about it for now because the
478    // bundle's PlugIns directory is supposed to be the preferred
479    // place for this anyway.
480    //
481    // Another directory that might be worth considering is
482    // the directory the program resides in,
483    // but I'm worried about multiplatform distribution.
484    // Because .so is used by other platforms like Linux, we
485    // could end up loading the wrong binary.
486    // I'm not sure how robust the current code is for this case.
487    // Assuming the program doesn't crash, will OSG move on to the
488    // next search directory, or just give up?
489    void osgDB::appendPlatformSpecificLibraryFilePaths(FilePathList& filepath)
490    {
491        char* ptr;
492        if ((ptr = getenv( "DYLD_LIBRARY_PATH" )) )
493        {
494            convertStringPathIntoFilePathList(ptr, filepath);
495        }
496
497        const int MAX_OSX_PATH_SIZE = 1024;
498        const std::string OSG_PLUGIN_PATH("/OpenSceneGraph/PlugIns");
499        char buffer[MAX_OSX_PATH_SIZE];
500        char bundlePathBuffer[MAX_OSX_PATH_SIZE];
501        CFURLRef  url;
502        CFStringRef pathString;
503        CFBundleRef myBundle;
504        CFStringRef bundlePathString;
505        FSRef     f;
506        OSErr   errCode;
507
508        // Start with the the Bundle PlugIns directory.
509        // Unlike the Cocoa API, it seems that the PlugIn path is relative
510        // and not absolute. So I will need to fetch both the bundle path
511        // (which is absolute) and the PlugIn path (which is relative),
512        // and combine the two to form a full path.
513
514        // Get the bundle first
515        myBundle = CFBundleGetMainBundle();
516        if(myBundle != NULL)
517        {
518            // Get the URL to the bundle
519            url = CFBundleCopyBundleURL( myBundle );
520
521            // Convert the URL to a CFString that looks like a Unix file path
522            bundlePathString = CFURLCopyFileSystemPath( url, kCFURLPOSIXPathStyle );
523            // Convert the CFString to a C string
524            CFStringGetCString( bundlePathString, bundlePathBuffer, MAX_OSX_PATH_SIZE, kCFStringEncodingUTF8 );
525
526            CFRelease( url );
527
528            // Now find the PlugIns directory
529            // Get the URL to the bundle
530            url = CFBundleCopyBuiltInPlugInsURL( myBundle );
531            //pathString = CFURLCopyPath( url );
532            // Convert the URL to a CFString that looks like a Unix file path
533            pathString = CFURLCopyFileSystemPath( url, kCFURLPOSIXPathStyle );
534            // Convert the CFString to a C string
535            CFStringGetCString( pathString, buffer, MAX_OSX_PATH_SIZE, kCFStringEncodingUTF8 );
536
537            // Combine the string and copy it into the FilePath list
538            filepath.push_back(
539                std::string(bundlePathBuffer)
540                + std::string("/")
541                + std::string(buffer)
542            );
543
544            CFRelease( pathString );
545            CFRelease( bundlePathString );
546            CFRelease( url );
547
548            // I was getting random crashes which I believe were caused by
549            // releasing the bundle. The documentation says I'm responsible
550            // for retaining and releasing which didn't immediately register
551            // in my head. I never retain the bundle, so I don't release it.
552            // CFRelease( myBundle );
553
554            pathString = NULL;
555            bundlePathString = NULL;
556            url = NULL;
557            // myBundle = NULL;
558        }
559        else
560        {
561            notify( DEBUG_INFO ) << "Couldn't find the Application Bundle" << std::endl;
562        }
563
564        // Next, check the User's Application Support folder
565        errCode = FSFindFolder( kUserDomain, kApplicationSupportFolderType, kDontCreateFolder, &f );
566        if(noErr == errCode)
567        {
568            // Get the URL
569            url = CFURLCreateFromFSRef( 0, &f );
570            // Convert the URL to a CFString that looks like a Unix file path
571            pathString = CFURLCopyFileSystemPath( url, kCFURLPOSIXPathStyle );
572            // Convert the CFString to a C string
573            CFStringGetCString( pathString, buffer, MAX_OSX_PATH_SIZE, kCFStringEncodingUTF8 );
574
575            // Add the directory to the FilePathList
576            filepath.push_back(
577                std::string(buffer)
578                + OSG_PLUGIN_PATH
579            );
580
581            CFRelease( url );
582            CFRelease( pathString );
583        }
584        else
585        {
586            notify( DEBUG_INFO ) << "Couldn't find the User's Application Support Path" << std::endl;
587        }
588
589        // Next, check the Local System's Application Support Folder
590        errCode = FSFindFolder( kLocalDomain, kApplicationSupportFolderType, kDontCreateFolder, &f );
591        if(noErr == errCode)
592        {
593            // Get the URL
594            url = CFURLCreateFromFSRef( 0, &f );
595            // Convert the URL to a CFString that looks like a Unix file path
596            pathString = CFURLCopyFileSystemPath( url, kCFURLPOSIXPathStyle );
597            // Convert the CFString to a C string
598            CFStringGetCString( pathString, buffer, MAX_OSX_PATH_SIZE, kCFStringEncodingUTF8 );
599
600            // Add the directory to the FilePathList
601            filepath.push_back(
602                std::string(buffer)
603                + OSG_PLUGIN_PATH
604            );
605
606            CFRelease( url );
607            CFRelease( pathString );
608        }
609        else
610        {
611            notify( DEBUG_INFO ) << "Couldn't find the Local System's Application Support Path" << std::endl;
612        }
613
614        // Finally, check the Network Application Support Folder
615        // This one has a likely chance of not existing so an error
616        // may be returned. Don't panic.
617        errCode = FSFindFolder( kNetworkDomain, kApplicationSupportFolderType, kDontCreateFolder, &f );
618        if(noErr == errCode)
619        {
620            // Get the URL
621            url = CFURLCreateFromFSRef( 0, &f );
622            // Convert the URL to a CFString that looks like a Unix file path
623            pathString = CFURLCopyFileSystemPath( url, kCFURLPOSIXPathStyle );
624            // Convert the CFString to a C string
625            CFStringGetCString( pathString, buffer, MAX_OSX_PATH_SIZE, kCFStringEncodingUTF8 );
626
627            // Add the directory to the FilePathList
628            filepath.push_back(
629                std::string(buffer)
630                + OSG_PLUGIN_PATH
631            );
632
633            CFRelease( url );
634            CFRelease( pathString );
635        }
636        else
637        {
638            notify( DEBUG_INFO ) << "Couldn't find the Network Application Support Path" << std::endl;
639        }
640    }
641
642    #endif
643   
644#else   
645
646    void osgDB::appendPlatformSpecificLibraryFilePaths(FilePathList& filepath)
647    {
648
649       char* ptr;
650       if( (ptr = getenv( "LD_LIBRARY_PATH" )) )
651        {
652            convertStringPathIntoFilePathList(ptr,filepath);
653        }
654
655        convertStringPathIntoFilePathList("/usr/lib/:/usr/local/lib/",filepath);
656
657    }
658
659#endif
660
661
662
663
664
665
666
667
668
Note: See TracBrowser for help on using the browser.