diff options
Diffstat (limited to '3rdparty/bx/src/filepath.cpp')
-rw-r--r-- | 3rdparty/bx/src/filepath.cpp | 543 |
1 files changed, 543 insertions, 0 deletions
diff --git a/3rdparty/bx/src/filepath.cpp b/3rdparty/bx/src/filepath.cpp new file mode 100644 index 00000000000..b8bbe279a59 --- /dev/null +++ b/3rdparty/bx/src/filepath.cpp @@ -0,0 +1,543 @@ +/* + * Copyright 2010-2017 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bx#license-bsd-2-clause + */ + +#include "bx_p.h" +#include <bx/file.h> +#include <bx/os.h> +#include <bx/readerwriter.h> + +#include <stdio.h> // remove +#include <dirent.h> // opendir + +#if BX_CRT_MSVC +# include <direct.h> // _getcwd +#else +# include <sys/stat.h> // mkdir +# include <unistd.h> // getcwd +#endif // BX_CRT_MSVC + +#if BX_PLATFORM_WINDOWS +extern "C" __declspec(dllimport) unsigned long __stdcall GetTempPathA(unsigned long _max, char* _ptr); +#endif // BX_PLATFORM_WINDOWS + +namespace bx +{ + static bool isPathSeparator(char _ch) + { + return false + || '/' == _ch + || '\\' == _ch + ; + } + + static int32_t normalizeFilePath(char* _dst, int32_t _dstSize, const char* _src, int32_t _num) + { + // Reference: Lexical File Names in Plan 9 or Getting Dot-Dot Right + // https://9p.io/sys/doc/lexnames.html + + const int32_t num = strLen(_src, _num); + + if (0 == num) + { + return strCopy(_dst, _dstSize, "."); + } + + int32_t size = 0; + + StaticMemoryBlockWriter writer(_dst, _dstSize); + Error err; + + int32_t idx = 0; + int32_t dotdot = 0; + + if (2 <= num + && ':' == _src[1]) + { + size += write(&writer, toUpper(_src[idx]), &err); + size += write(&writer, ':', &err); + idx += 2; + dotdot = size; + } + + const int32_t slashIdx = idx; + + bool rooted = isPathSeparator(_src[idx]); + if (rooted) + { + size += write(&writer, '/', &err); + ++idx; + dotdot = size; + } + + bool trailingSlash = false; + + while (idx < num && err.isOk() ) + { + switch (_src[idx]) + { + case '/': + case '\\': + ++idx; + trailingSlash = idx == num; + break; + + case '.': + if (idx+1 == num + || isPathSeparator(_src[idx+1]) ) + { + ++idx; + break; + } + + if ('.' == _src[idx+1] + && (idx+2 == num || isPathSeparator(_src[idx+2]) ) ) + { + idx += 2; + + if (dotdot < size) + { + for (--size + ; dotdot < size && !isPathSeparator(_dst[size]) + ; --size) + { + } + seek(&writer, size, Whence::Begin); + } + else if (!rooted) + { + if (0 < size) + { + size += write(&writer, '/', &err); + } + + size += write(&writer, "..", &err); + dotdot = size; + } + + break; + } + + BX_FALLTHROUGH; + + default: + if ( ( rooted && slashIdx+1 != size) + || (!rooted && 0 != size) ) + { + size += write(&writer, '/', &err); + } + + for (; idx < num && !isPathSeparator(_src[idx]); ++idx) + { + size += write(&writer, _src[idx], &err); + } + + break; + } + } + + if (0 == size) + { + size += write(&writer, '.', &err); + } + + if (trailingSlash) + { + size += write(&writer, '/', &err); + } + + write(&writer, '\0', &err); + + return size; + } + + static bool getEnv(const char* _name, FileInfo::Enum _type, char* _out, uint32_t* _inOutSize) + { + uint32_t len = *_inOutSize; + *_out = '\0'; + + if (getenv(_name, _out, &len) ) + { + FileInfo fi; + if (stat(_out, fi) + && _type == fi.m_type) + { + *_inOutSize = len; + return true; + } + } + + return false; + } + + static char* pwd(char* _buffer, uint32_t _size) + { +#if BX_PLATFORM_PS4 \ + || BX_PLATFORM_XBOXONE \ + || BX_PLATFORM_WINRT + BX_UNUSED(_buffer, _size); + return NULL; +#elif BX_CRT_MSVC + return ::_getcwd(_buffer, (int32_t)_size); +#else + return ::getcwd(_buffer, _size); +#endif // BX_COMPILER_ + } + + static bool getCurrentPath(char* _out, uint32_t* _inOutSize) + { + uint32_t len = *_inOutSize; + pwd(_out, len); + *_inOutSize = strLen(_out); + return true; + } + + static bool getHomePath(char* _out, uint32_t* _inOutSize) + { + return false +#if BX_PLATFORM_WINDOWS + || getEnv("USERPROFILE", FileInfo::Directory, _out, _inOutSize) +#endif // BX_PLATFORM_WINDOWS + || getEnv("HOME", FileInfo::Directory, _out, _inOutSize) + ; + } + + static bool getTempPath(char* _out, uint32_t* _inOutSize) + { +#if BX_PLATFORM_WINDOWS + uint32_t len = ::GetTempPathA(*_inOutSize, _out); + bool result = len != 0 && len < *_inOutSize; + *_inOutSize = len; + return result; +#else + static const char* s_tmp[] = + { + "TMPDIR", + "TMP", + "TEMP", + "TEMPDIR", + + NULL + }; + + for (const char** tmp = s_tmp; *tmp != NULL; ++tmp) + { + uint32_t len = *_inOutSize; + *_out = '\0'; + bool ok = getEnv(*tmp, FileInfo::Directory, _out, &len); + + if (ok + && len != 0 + && len < *_inOutSize) + { + *_inOutSize = len; + return ok; + } + } + + FileInfo fi; + if (stat("/tmp", fi) + && FileInfo::Directory == fi.m_type) + { + strCopy(_out, *_inOutSize, "/tmp"); + *_inOutSize = 4; + return true; + } + + return false; +#endif // BX_PLATFORM_* + } + + FilePath::FilePath() + { + set(""); + } + + FilePath::FilePath(Dir::Enum _dir) + { + set(_dir); + } + + FilePath::FilePath(const char* _rhs) + { + set(_rhs); + } + + FilePath::FilePath(const StringView& _filePath) + { + set(_filePath); + } + + FilePath& FilePath::operator=(const StringView& _rhs) + { + set(_rhs); + return *this; + } + + void FilePath::set(Dir::Enum _dir) + { + char tmp[kMaxFilePath]; + uint32_t len = BX_COUNTOF(tmp); + + switch (_dir) + { + case Dir::Current: + getCurrentPath(tmp, &len); + break; + + case Dir::Temp: + getTempPath(tmp, &len); + break; + + case Dir::Home: + getHomePath(tmp, &len); + break; + + default: + len = 0; + break; + } + + set(StringView(tmp, len) ); + } + + void FilePath::set(const StringView& _filePath) + { + normalizeFilePath( + m_filePath + , BX_COUNTOF(m_filePath) + , _filePath.getPtr() + , _filePath.getLength() + ); + } + + void FilePath::join(const StringView& _str) + { + char tmp[kMaxFilePath]; + strCopy(tmp, BX_COUNTOF(tmp), m_filePath); + strCat(tmp, BX_COUNTOF(tmp), "/"); + strCat(tmp, BX_COUNTOF(tmp), _str); + set(tmp); + } + + const char* FilePath::get() const + { + return m_filePath; + } + + const StringView FilePath::getPath() const + { + const char* end = strRFind(m_filePath, '/'); + if (NULL != end) + { + return StringView(m_filePath, end+1); + } + + return StringView(); + } + + const StringView FilePath::getFileName() const + { + const char* fileName = strRFind(m_filePath, '/'); + if (NULL != fileName) + { + return StringView(fileName+1); + } + + return get(); + } + + const StringView FilePath::getBaseName() const + { + const StringView fileName = getFileName(); + if (!fileName.isEmpty() ) + { + const char* ext = strFind(fileName, '.'); + if (ext != NULL) + { + return StringView(fileName.getPtr(), ext); + } + + return fileName; + } + + return StringView(); + } + + const StringView FilePath::getExt() const + { + const StringView fileName = getFileName(); + if (!fileName.isEmpty() ) + { + const char* ext = strFind(fileName, '.'); + return StringView(ext); + } + + return StringView(); + } + + bool FilePath::isAbsolute() const + { + return '/' == m_filePath[0] // no drive letter + || (':' == m_filePath[1] && '/' == m_filePath[2]) // with drive letter + ; + } + + bool make(const FilePath& _filePath, Error* _err) + { + BX_ERROR_SCOPE(_err); + + if (!_err->isOk() ) + { + return false; + } + +#if BX_CRT_MSVC + int32_t result = ::_mkdir(_filePath.get() ); +#elif BX_CRT_MINGW + int32_t result = ::mkdir(_filePath.get()); +#else + int32_t result = ::mkdir(_filePath.get(), 0700); +#endif // BX_CRT_MSVC + + if (0 != result) + { + BX_ERROR_SET(_err, BX_ERROR_ACCESS, "The parent directory does not allow write permission to the process."); + return false; + } + + return true; + } + + bool makeAll(const FilePath& _filePath, Error* _err) + { + BX_ERROR_SCOPE(_err); + + if (!_err->isOk() ) + { + return false; + } + + FileInfo fi; + + if (stat(_filePath, fi) ) + { + if (FileInfo::Directory == fi.m_type) + { + return true; + } + + BX_ERROR_SET(_err, BX_ERROR_NOT_DIRECTORY, "File already exist, and is not directory."); + return false; + } + + const StringView dir = strRTrim(_filePath.get(), "/"); + const char* slash = strRFind(dir, '/'); + + if (NULL != slash + && slash - dir.getPtr() > 1) + { + if (!makeAll(StringView(dir.getPtr(), slash), _err) ) + { + return false; + } + } + + FilePath path(dir); + return make(path, _err); + } + + bool remove(const FilePath& _filePath, Error* _err) + { + BX_ERROR_SCOPE(_err); + + if (!_err->isOk() ) + { + return false; + } + +#if BX_CRT_MSVC + int32_t result = -1; + FileInfo fi; + if (stat(_filePath, fi) ) + { + if (FileInfo::Directory == fi.m_type) + { + result = ::_rmdir(_filePath.get() ); + } + else + { + result = ::remove(_filePath.get() ); + } + } +#else + int32_t result = ::remove(_filePath.get() ); +#endif // BX_CRT_MSVC + + if (0 != result) + { + BX_ERROR_SET(_err, BX_ERROR_ACCESS, "The parent directory does not allow write permission to the process."); + return false; + } + + return true; + } + + bool removeAll(const FilePath& _filePath, Error* _err) + { + BX_ERROR_SCOPE(_err); + + if (remove(_filePath, _err) ) + { + return true; + } + + _err->reset(); + + FileInfo fi; + + if (!stat(_filePath, fi) ) + { + BX_ERROR_SET(_err, BX_ERROR_ACCESS, "The parent directory does not allow write permission to the process."); + return false; + } + + if (FileInfo::Directory != fi.m_type) + { + BX_ERROR_SET(_err, BX_ERROR_NOT_DIRECTORY, "File already exist, and is not directory."); + return false; + } + +#if BX_PLATFORM_WINDOWS || BX_PLATFORM_LINUX || BX_PLATFORM_OSX + DIR* dir = opendir(_filePath.get() ); + if (NULL == dir) + { + BX_ERROR_SET(_err, BX_ERROR_NOT_DIRECTORY, "File already exist, and is not directory."); + return false; + } + + for (dirent* item = readdir(dir); NULL != item; item = readdir(dir) ) + { + if (0 == strCmp(item->d_name, ".") + || 0 == strCmp(item->d_name, "..") ) + { + continue; + } + + FilePath path(_filePath); + path.join(item->d_name); + if (!removeAll(path, _err) ) + { + _err->reset(); + break; + } + } + + closedir(dir); +#endif // BX_PLATFORM_WINDOWS || BX_PLATFORM_LINUX || BX_PLATFORM_OSX + + return remove(_filePath, _err); + } + +} // namespace bx |