| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569 |
- /*
- * Copyright 2010-2018 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>
- #if !BX_CRT_NONE
- # 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
- #endif // 0
- #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://web.archive.org/web/20180629044444/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_CRT_NONE
- 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::clear()
- {
- if (!isEmpty() )
- {
- set("");
- }
- }
- 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 FilePath::isEmpty() const
- {
- return 0 == strCmp(m_filePath, ".");
- }
- 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());
- #elif BX_CRT_NONE
- BX_UNUSED(_filePath);
- int32_t result = -1;
- #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() );
- }
- }
- #elif BX_CRT_NONE
- BX_UNUSED(_filePath);
- int32_t result = -1;
- #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_CRT_NONE
- BX_UNUSED(_filePath);
- return false;
- #elif 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_CRT_NONE
- return remove(_filePath, _err);
- }
- } // namespace bx
|