root/OpenSceneGraph/trunk/src/osgDB/FileNameUtils.cpp @ 13041

Revision 13041, 16.2 kB (checked in by robert, 3 years ago)

Ran script to remove trailing spaces and tabs

  • 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#include <stdlib.h>
14#include <string.h>
15#include <limits.h>
16
17#include <osgDB/ConvertUTF>
18#include <osgDB/FileNameUtils>
19#include <osgDB/FileUtils>
20
21#ifdef WIN32
22    #define _WIN32_WINNT 0x0500
23    #include <windows.h>
24#endif
25
26#if defined(__sgi)
27    #include <ctype.h>
28#elif defined(__GNUC__) || !defined(WIN32) || defined(__MWERKS__)
29    #include <cctype>
30    using std::tolower;
31#endif
32
33using namespace std;
34
35static const char * const PATH_SEPARATORS = "/\\";
36static unsigned int PATH_SEPARATORS_LEN = 2;
37
38
39std::string osgDB::getFilePath(const std::string& fileName)
40{
41    std::string::size_type slash = fileName.find_last_of(PATH_SEPARATORS);
42    if (slash==std::string::npos) return std::string();
43    else return std::string(fileName, 0, slash);
44}
45
46
47std::string osgDB::getSimpleFileName(const std::string& fileName)
48{
49    std::string::size_type slash = fileName.find_last_of(PATH_SEPARATORS);
50    if (slash==std::string::npos) return fileName;
51    else return std::string(fileName.begin()+slash+1,fileName.end());
52}
53
54
55std::string osgDB::getFileExtension(const std::string& fileName)
56{
57    std::string::size_type dot = fileName.find_last_of('.');
58    std::string::size_type slash = fileName.find_last_of(PATH_SEPARATORS);
59    if (dot==std::string::npos || (slash!=std::string::npos && dot<slash)) return std::string("");
60    return std::string(fileName.begin()+dot+1,fileName.end());
61}
62
63std::string osgDB::getFileExtensionIncludingDot(const std::string& fileName)
64{
65    std::string::size_type dot = fileName.find_last_of('.');
66    std::string::size_type slash = fileName.find_last_of(PATH_SEPARATORS);
67    if (dot==std::string::npos || (slash!=std::string::npos && dot<slash)) return std::string("");
68    return std::string(fileName.begin()+dot,fileName.end());
69}
70
71std::string osgDB::convertFileNameToWindowsStyle(const std::string& fileName)
72{
73    std::string new_fileName(fileName);
74
75    std::string::size_type slash = 0;
76    while( (slash=new_fileName.find_first_of(UNIX_PATH_SEPARATOR,slash)) != std::string::npos)
77    {
78        new_fileName[slash]=WINDOWS_PATH_SEPARATOR;
79    }
80    return new_fileName;
81}
82
83std::string osgDB::convertFileNameToUnixStyle(const std::string& fileName)
84{
85    std::string new_fileName(fileName);
86
87    std::string::size_type slash = 0;
88    while( (slash=new_fileName.find_first_of(WINDOWS_PATH_SEPARATOR,slash)) != std::string::npos)
89    {
90        new_fileName[slash]=UNIX_PATH_SEPARATOR;
91    }
92
93    return new_fileName;
94}
95
96char osgDB::getNativePathSeparator()
97{
98#if defined(WIN32) && !defined(__CYGWIN__)
99    return WINDOWS_PATH_SEPARATOR;
100#else
101    return UNIX_PATH_SEPARATOR;
102#endif
103}
104
105bool osgDB::isFileNameNativeStyle(const std::string& fileName)
106{
107#if defined(WIN32) && !defined(__CYGWIN__)
108    return fileName.find(UNIX_PATH_SEPARATOR) == std::string::npos; // return true if no unix style slash exist
109#else
110    return fileName.find(WINDOWS_PATH_SEPARATOR) == std::string::npos; // return true if no windows style backslash exist
111#endif
112}
113
114std::string osgDB::convertFileNameToNativeStyle(const std::string& fileName)
115{
116#if defined(WIN32) && !defined(__CYGWIN__)
117    return convertFileNameToWindowsStyle(fileName);
118#else
119    return convertFileNameToUnixStyle(fileName);
120#endif
121}
122
123
124
125std::string osgDB::getLowerCaseFileExtension(const std::string& filename)
126{
127    return convertToLowerCase(osgDB::getFileExtension(filename));
128}
129
130std::string osgDB::convertToLowerCase(const std::string& str)
131{
132    std::string lowcase_str(str);
133    for(std::string::iterator itr=lowcase_str.begin();
134        itr!=lowcase_str.end();
135        ++itr)
136    {
137        *itr = tolower(*itr);
138    }
139    return lowcase_str;
140}
141
142// strip one level of extension from the filename.
143std::string osgDB::getNameLessExtension(const std::string& fileName)
144{
145    std::string::size_type dot = fileName.find_last_of('.');
146    std::string::size_type slash = fileName.find_last_of(PATH_SEPARATORS);        // Finds forward slash *or* back slash
147    if (dot==std::string::npos || (slash!=std::string::npos && dot<slash)) return fileName;
148    return std::string(fileName.begin(),fileName.begin()+dot);
149}
150
151
152// strip all extensions from the filename.
153std::string osgDB::getNameLessAllExtensions(const std::string& fileName)
154{
155    // Finds start serach position: from last slash, or the begining of the string if none found
156    std::string::size_type startPos = fileName.find_last_of(PATH_SEPARATORS);            // Finds forward slash *or* back slash
157    if (startPos == std::string::npos) startPos = 0;
158    std::string::size_type dot = fileName.find_first_of('.', startPos);        // Finds *FIRST* dot from start pos
159    if (dot==std::string::npos) return fileName;
160    return std::string(fileName.begin(),fileName.begin()+dot);
161}
162
163std::string osgDB::getStrippedName(const std::string& fileName)
164{
165    std::string simpleName = getSimpleFileName(fileName);
166    return getNameLessExtension( simpleName );
167}
168
169
170bool osgDB::equalCaseInsensitive(const std::string& lhs,const std::string& rhs)
171{
172    if (lhs.size()!=rhs.size()) return false;
173    std::string::const_iterator litr = lhs.begin();
174    std::string::const_iterator ritr = rhs.begin();
175    while (litr!=lhs.end())
176    {
177        if (tolower(*litr)!=tolower(*ritr)) return false;
178        ++litr;
179        ++ritr;
180    }
181    return true;
182}
183
184bool osgDB::equalCaseInsensitive(const std::string& lhs,const char* rhs)
185{
186    if (rhs==NULL || lhs.size()!=strlen(rhs)) return false;
187    std::string::const_iterator litr = lhs.begin();
188    const char* cptr = rhs;
189    while (litr!=lhs.end())
190    {
191        if (tolower(*litr)!=tolower(*cptr)) return false;
192        ++litr;
193        ++cptr;
194    }
195    return true;
196}
197
198
199
200bool osgDB::containsServerAddress(const std::string& filename)
201{
202    // need to check for ://
203    std::string::size_type pos(filename.find("://"));
204    if (pos == std::string::npos)
205        return false;
206    std::string proto(filename.substr(0, pos));
207
208    return Registry::instance()->isProtocolRegistered(proto);
209}
210
211std::string osgDB::getServerProtocol(const std::string& filename)
212{
213    std::string::size_type pos(filename.find("://"));
214    if (pos != std::string::npos)
215        return filename.substr(0,pos);
216
217    return "";
218}
219
220std::string osgDB::getServerAddress(const std::string& filename)
221{
222    std::string::size_type pos(filename.find("://"));
223
224    if (pos != std::string::npos)
225    {
226        std::string::size_type pos_slash = filename.find_first_of('/',pos+3);
227        if (pos_slash!=std::string::npos)
228        {
229            return filename.substr(pos+3,pos_slash-pos-3);
230        }
231        else
232        {
233            return filename.substr(pos+3,std::string::npos);
234        }
235    }
236    return "";
237}
238
239std::string osgDB::getServerFileName(const std::string& filename)
240{
241    std::string::size_type pos(filename.find("://"));
242
243    if (pos != std::string::npos)
244    {
245        std::string::size_type pos_slash = filename.find_first_of('/',pos+3);
246        if (pos_slash!=std::string::npos)
247        {
248            return filename.substr(pos_slash+1,std::string::npos);
249        }
250        else
251        {
252            return "";
253        }
254
255    }
256    return filename;
257}
258
259std::string osgDB::concatPaths(const std::string& left, const std::string& right)
260{
261#if defined(WIN32) && !defined(__CYGWIN__)
262    const char delimiterNative  = WINDOWS_PATH_SEPARATOR;
263    const char delimiterForeign = UNIX_PATH_SEPARATOR;
264#else
265    const char delimiterNative  = UNIX_PATH_SEPARATOR;
266    const char delimiterForeign = WINDOWS_PATH_SEPARATOR;
267#endif
268
269    if(left.empty())
270    {
271        return(right);
272    }
273    char lastChar = left[left.size() - 1];
274
275    if(lastChar == delimiterNative)
276    {
277        return left + right;
278    }
279    else if(lastChar == delimiterForeign)
280    {
281        return left.substr(0, left.size() - 1) + delimiterNative + right;
282    }
283    else // lastChar != a delimiter
284    {
285        return left + delimiterNative + right;
286    }
287}
288
289std::string osgDB::getRealPath(const std::string& path)
290{
291#if defined(WIN32)  && !defined(__CYGWIN__)
292
293#ifdef OSG_USE_UTF8_FILENAME
294
295    std::wstring wpath = convertUTF8toUTF16(path);
296    wchar_t retbuf[MAX_PATH + 1];
297    wchar_t tempbuf1[MAX_PATH + 1];
298    if (GetFullPathNameW(wpath.c_str(), _countof(retbuf), retbuf, NULL)==0) {
299        return path;
300    }
301    // Force drive letter to upper case
302    if ((retbuf[1] == ':') && iswlower(retbuf[0]))
303        retbuf[0] = towupper(retbuf[0]);
304    if (fileExists(convertUTF16toUTF8(retbuf)))
305    {
306        // Canonicalise the full path
307        GetShortPathNameW(retbuf, tempbuf1, _countof(tempbuf1));
308        GetLongPathNameW(tempbuf1, retbuf, _countof(retbuf));
309        return convertUTF16toUTF8(retbuf);
310    }
311    else
312    {
313        std::string retbuf8 = convertUTF16toUTF8(retbuf);
314        // Canonicalise the directories
315        std::string FilePath = getFilePath(retbuf8);
316        wchar_t tempbuf2[MAX_PATH + 1];
317        if (0 == GetShortPathNameW(convertUTF8toUTF16(FilePath).c_str(), tempbuf1, _countof(tempbuf1)))
318            return retbuf8;
319        if (0 == GetLongPathNameW(tempbuf1, tempbuf2, _countof(tempbuf2)))
320            return retbuf8;
321        FilePath = convertUTF16toUTF8(tempbuf2);
322        FilePath += WINDOWS_PATH_SEPARATOR;
323        FilePath.append(getSimpleFileName(retbuf8));
324        return FilePath;
325    }
326
327#else
328
329    // Not unicode compatible should give an error if UNICODE defined
330    char retbuf[MAX_PATH + 1];
331    char tempbuf1[MAX_PATH + 1];
332    GetFullPathName(path.c_str(), sizeof(retbuf), retbuf, NULL);
333    // Force drive letter to upper case
334    if ((retbuf[1] == ':') && islower(retbuf[0]))
335        retbuf[0] = _toupper(retbuf[0]);
336    if (fileExists(std::string(retbuf)))
337    {
338        // Canonicalise the full path
339        GetShortPathName(retbuf, tempbuf1, sizeof(tempbuf1));
340        GetLongPathName(tempbuf1, retbuf, sizeof(retbuf));
341        return std::string(retbuf);
342    }
343    else
344    {
345        // Canonicalise the directories
346        std::string FilePath = getFilePath(retbuf);
347        char tempbuf2[MAX_PATH + 1];
348        if (0 == GetShortPathName(FilePath.c_str(), tempbuf1, sizeof(tempbuf1)))
349            return std::string(retbuf);
350        if (0 == GetLongPathName(tempbuf1, tempbuf2, sizeof(tempbuf2)))
351            return std::string(retbuf);
352        FilePath = std::string(tempbuf2);
353        FilePath += WINDOWS_PATH_SEPARATOR;
354        FilePath.append(getSimpleFileName(std::string(retbuf)));
355        return FilePath;
356    }
357#endif
358
359#else
360    char resolved_path[PATH_MAX];
361    char* result = realpath(path.c_str(), resolved_path);
362
363    if (result) return std::string(resolved_path);
364    else return path;
365#endif
366}
367
368namespace osgDB
369{
370
371/** Helper to iterate over elements of a path (including Windows' root, if any). **/
372class PathIterator {
373public:
374    PathIterator(const std::string & v);
375    bool valid() const { return start!=end; }
376    PathIterator & operator++();
377    std::string operator*();
378
379protected:
380    std::string::const_iterator end;     ///< End of path string
381    std::string::const_iterator start;   ///< Points to the first char of an element, or ==end() if no more
382    std::string::const_iterator stop;    ///< Points to the separator after 'start', or ==end()
383
384    /// Iterate until 'it' points to something different from a separator
385    std::string::const_iterator skipSeparators(std::string::const_iterator it);
386    std::string::const_iterator next(std::string::const_iterator it);
387};
388
389}
390
391osgDB::PathIterator::PathIterator(const std::string & v) : end(v.end()), start(v.begin()), stop(v.begin()) { operator++(); }
392osgDB::PathIterator & osgDB::PathIterator::operator++()
393{
394    if (!valid()) return *this;
395    start = skipSeparators(stop);
396    if (start != end) stop = next(start);
397    return *this;
398}
399std::string osgDB::PathIterator::operator*()
400{
401    if (!valid()) return std::string();
402    return std::string(start, stop);
403}
404
405std::string::const_iterator osgDB::PathIterator::skipSeparators(std::string::const_iterator it)
406{
407    for (; it!=end && std::find_first_of(it, it+1, PATH_SEPARATORS, PATH_SEPARATORS+PATH_SEPARATORS_LEN) != it+1; ++it) {}
408    return it;
409}
410
411std::string::const_iterator osgDB::PathIterator::next(std::string::const_iterator it)
412{
413    return std::find_first_of(it, end, PATH_SEPARATORS, PATH_SEPARATORS+PATH_SEPARATORS_LEN);
414}
415
416void osgDB::getPathElements(const std::string& path, std::vector<std::string> & out_elements)
417{
418    out_elements.clear();
419    for(osgDB::PathIterator it(path); it.valid(); ++it) out_elements.push_back(*it);
420}
421
422
423std::string osgDB::getPathRoot(const std::string& path) {
424    // Test for unix root
425    if (path.empty()) return "";
426    if (path[0] == '/') return "/";
427    // Now test for Windows root
428    if (path.length()<2) return "";
429    if (path[1] == ':') return path.substr(0, 2);        // We should check that path[0] is a letter, but as ':' is invalid in paths in other cases, that's not a problem.
430    return "";
431}
432
433bool osgDB::isAbsolutePath(const std::string& path) {
434    // Test for unix root
435    if (path.empty()) return false;
436    if (path[0] == '/') return true;
437    // Now test for Windows root
438    if (path.length()<2) return false;
439    return path[1] == ':';        // We should check that path[0] is a letter, but as ':' is invalid in paths in other cases, that's not a problem.
440}
441
442std::string osgDB::getPathRelative(const std::string& from, const std::string& to)
443{
444    // This implementation is not 100% robust, and should be replaced with C++0x "std::path" as soon as possible.
445
446    // Definition: an "element" is a part between slashes. Ex: "/a/b" has two elements ("a" and "b").
447    // Algorithm:
448    // 1. If paths are neither both absolute nor both relative, then we cannot do anything (we need to make them absolute, but need additionnal info on how to make it). Return.
449    // 2. If both paths are absolute and root isn't the same (for Windows only, as roots are of the type "C:", "D:"), then the operation is impossible. Return.
450    // 3. Iterate over two paths elements until elements are equal
451    // 4. For each remaining element in "from", add ".." to result
452    // 5. For each remaining element in "to", add this element to result
453
454    // 1 & 2
455    const std::string root = getPathRoot(from);
456    if (root != getPathRoot(to)) {
457        OSG_INFO << "Cannot relativise paths. From=" << from << ", To=" << to << ". Returning 'to' unchanged." << std::endl;
458        //return to;
459        return osgDB::getSimpleFileName(to);
460    }
461
462    // 3
463    PathIterator itFrom(from), itTo(to);
464    // Iterators may point to Windows roots. As we tested they are equal, there is no need to ++itFrom and ++itTo.
465    // However, if we got an Unix root, we must add it to the result.
466    std::string res(root == "/" ? "/" : "");
467    for(; itFrom.valid() && itTo.valid() && *itFrom==*itTo; ++itFrom, ++itTo) {}
468
469    // 4
470    for(; itFrom.valid(); ++itFrom) res += "../";
471
472    // 5
473    for(; itTo.valid(); ++itTo) res += *itTo + "/";
474
475    // Remove trailing slash before returning
476    if (!res.empty() && std::find_first_of(res.rbegin(), res.rbegin()+1, PATH_SEPARATORS, PATH_SEPARATORS+PATH_SEPARATORS_LEN) != res.rbegin()+1)
477    {
478        return res.substr(0, res.length()-1);
479    }
480    return res;
481}
482
483//using namespace osgDB;
484//std::string testA = getPathRelative("C:\\a\\b", "C:\\a/b/d/f");       // d/f
485//std::string testB = getPathRelative("C:\\a\\d", "C:\\a/b/d/f");       // ../b/d/f
486//std::string testC = getPathRelative("C:\\ab", "C:\\a/b/d/f");         // ../a/b/d/f
487//std::string testD = getPathRelative("a/d", "a/d");                    // ""
488//std::string testE = getPathRelative("a", "a/d");                      // ../d
489//std::string testF = getPathRelative("C:/a/b", "a/d");                 // Precondition fail. Returns d.
490//std::string testG = getPathRelative("/a/b", "a/d");                   // Precondition fail. Returns d.
491//std::string testH = getPathRelative("a/b", "/a/d");                   // Precondition fail. Returns d.
Note: See TracBrowser for help on using the browser.