filepath.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /*
  2. * Copyright 2010-2020 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause
  4. */
  5. #include "bx_p.h"
  6. #include <bx/file.h>
  7. #include <bx/os.h>
  8. #include <bx/readerwriter.h>
  9. #if !BX_CRT_NONE
  10. # if BX_CRT_MSVC
  11. # include <direct.h> // _getcwd
  12. # else
  13. # include <unistd.h> // getcwd
  14. # endif // BX_CRT_MSVC
  15. #endif // !BX_CRT_NONE
  16. #if BX_PLATFORM_WINDOWS
  17. extern "C" __declspec(dllimport) unsigned long __stdcall GetTempPathA(unsigned long _max, char* _ptr);
  18. #endif // BX_PLATFORM_WINDOWS
  19. namespace bx
  20. {
  21. static bool isPathSeparator(char _ch)
  22. {
  23. return false
  24. || '/' == _ch
  25. || '\\' == _ch
  26. ;
  27. }
  28. static int32_t normalizeFilePath(char* _dst, int32_t _dstSize, const char* _src, int32_t _num)
  29. {
  30. // Reference(s):
  31. // - Lexical File Names in Plan 9 or Getting Dot-Dot Right
  32. // https://web.archive.org/web/20180629044444/https://9p.io/sys/doc/lexnames.html
  33. const int32_t num = strLen(_src, _num);
  34. if (0 == num)
  35. {
  36. return strCopy(_dst, _dstSize, ".");
  37. }
  38. int32_t size = 0;
  39. StaticMemoryBlockWriter writer(_dst, _dstSize);
  40. Error err;
  41. int32_t idx = 0;
  42. int32_t dotdot = 0;
  43. if (2 <= num
  44. && ':' == _src[1])
  45. {
  46. size += write(&writer, toUpper(_src[idx]), &err);
  47. size += write(&writer, ':', &err);
  48. idx += 2;
  49. dotdot = size;
  50. }
  51. const int32_t slashIdx = idx;
  52. bool rooted = isPathSeparator(_src[idx]);
  53. if (rooted)
  54. {
  55. size += write(&writer, '/', &err);
  56. ++idx;
  57. dotdot = size;
  58. }
  59. bool trailingSlash = false;
  60. while (idx < num && err.isOk() )
  61. {
  62. switch (_src[idx])
  63. {
  64. case '/':
  65. case '\\':
  66. ++idx;
  67. trailingSlash = idx == num;
  68. break;
  69. case '.':
  70. if (idx+1 == num
  71. || isPathSeparator(_src[idx+1]) )
  72. {
  73. ++idx;
  74. break;
  75. }
  76. if ('.' == _src[idx+1]
  77. && (idx+2 == num || isPathSeparator(_src[idx+2]) ) )
  78. {
  79. idx += 2;
  80. if (dotdot < size)
  81. {
  82. for (--size
  83. ; dotdot < size && !isPathSeparator(_dst[size])
  84. ; --size)
  85. {
  86. }
  87. seek(&writer, size, Whence::Begin);
  88. }
  89. else if (!rooted)
  90. {
  91. if (0 < size)
  92. {
  93. size += write(&writer, '/', &err);
  94. }
  95. size += write(&writer, "..", &err);
  96. dotdot = size;
  97. }
  98. break;
  99. }
  100. BX_FALLTHROUGH;
  101. default:
  102. if ( ( rooted && slashIdx+1 != size)
  103. || (!rooted && 0 != size) )
  104. {
  105. size += write(&writer, '/', &err);
  106. }
  107. for (; idx < num && !isPathSeparator(_src[idx]); ++idx)
  108. {
  109. size += write(&writer, _src[idx], &err);
  110. }
  111. break;
  112. }
  113. }
  114. if (0 == size)
  115. {
  116. size += write(&writer, '.', &err);
  117. }
  118. if (trailingSlash)
  119. {
  120. size += write(&writer, '/', &err);
  121. }
  122. write(&writer, '\0', &err);
  123. return size;
  124. }
  125. static bool getEnv(char* _out, uint32_t* _inOutSize, const StringView& _name, FileType::Enum _type)
  126. {
  127. uint32_t len = *_inOutSize;
  128. *_out = '\0';
  129. if (getEnv(_out, &len, _name) )
  130. {
  131. FileInfo fi;
  132. if (stat(fi, _out)
  133. && _type == fi.type)
  134. {
  135. *_inOutSize = len;
  136. return true;
  137. }
  138. }
  139. return false;
  140. }
  141. static char* pwd(char* _buffer, uint32_t _size)
  142. {
  143. #if BX_PLATFORM_PS4 \
  144. || BX_PLATFORM_XBOXONE \
  145. || BX_PLATFORM_WINRT \
  146. || BX_CRT_NONE
  147. BX_UNUSED(_buffer, _size);
  148. return NULL;
  149. #elif BX_CRT_MSVC
  150. return ::_getcwd(_buffer, (int32_t)_size);
  151. #else
  152. return ::getcwd(_buffer, _size);
  153. #endif // BX_COMPILER_
  154. }
  155. static bool getCurrentPath(char* _out, uint32_t* _inOutSize)
  156. {
  157. uint32_t len = *_inOutSize;
  158. if (NULL != pwd(_out, len))
  159. {
  160. *_inOutSize = strLen(_out);
  161. return true;
  162. }
  163. return false;
  164. }
  165. static bool getHomePath(char* _out, uint32_t* _inOutSize)
  166. {
  167. return false
  168. #if BX_PLATFORM_WINDOWS
  169. || getEnv(_out, _inOutSize, "USERPROFILE", FileType::Dir)
  170. #endif // BX_PLATFORM_WINDOWS
  171. || getEnv(_out, _inOutSize, "HOME", FileType::Dir)
  172. ;
  173. }
  174. static bool getTempPath(char* _out, uint32_t* _inOutSize)
  175. {
  176. #if BX_PLATFORM_WINDOWS
  177. uint32_t len = ::GetTempPathA(*_inOutSize, _out);
  178. bool result = len != 0 && len < *_inOutSize;
  179. *_inOutSize = len;
  180. return result;
  181. #else
  182. static const StringView s_tmp[] =
  183. {
  184. "TMPDIR",
  185. "TMP",
  186. "TEMP",
  187. "TEMPDIR",
  188. ""
  189. };
  190. for (const StringView* tmp = s_tmp; !tmp->isEmpty(); ++tmp)
  191. {
  192. uint32_t len = *_inOutSize;
  193. *_out = '\0';
  194. bool ok = getEnv(_out, &len, *tmp, FileType::Dir);
  195. if (ok
  196. && len != 0
  197. && len < *_inOutSize)
  198. {
  199. *_inOutSize = len;
  200. return ok;
  201. }
  202. }
  203. FileInfo fi;
  204. if (stat(fi, "/tmp")
  205. && FileType::Dir == fi.type)
  206. {
  207. strCopy(_out, *_inOutSize, "/tmp");
  208. *_inOutSize = 4;
  209. return true;
  210. }
  211. return false;
  212. #endif // BX_PLATFORM_*
  213. }
  214. FilePath::FilePath()
  215. {
  216. set("");
  217. }
  218. FilePath::FilePath(Dir::Enum _dir)
  219. {
  220. set(_dir);
  221. }
  222. FilePath::FilePath(const char* _rhs)
  223. {
  224. set(_rhs);
  225. }
  226. FilePath::FilePath(const StringView& _filePath)
  227. {
  228. set(_filePath);
  229. }
  230. FilePath& FilePath::operator=(const StringView& _rhs)
  231. {
  232. set(_rhs);
  233. return *this;
  234. }
  235. void FilePath::clear()
  236. {
  237. if (!isEmpty() )
  238. {
  239. set("");
  240. }
  241. }
  242. void FilePath::set(Dir::Enum _dir)
  243. {
  244. char tmp[kMaxFilePath];
  245. uint32_t len = BX_COUNTOF(tmp);
  246. switch (_dir)
  247. {
  248. case Dir::Current:
  249. getCurrentPath(tmp, &len);
  250. break;
  251. case Dir::Temp:
  252. getTempPath(tmp, &len);
  253. break;
  254. case Dir::Home:
  255. getHomePath(tmp, &len);
  256. break;
  257. default:
  258. len = 0;
  259. break;
  260. }
  261. set(StringView(tmp, len) );
  262. }
  263. void FilePath::set(const StringView& _filePath)
  264. {
  265. normalizeFilePath(
  266. m_filePath
  267. , BX_COUNTOF(m_filePath)
  268. , _filePath.getPtr()
  269. , _filePath.getLength()
  270. );
  271. }
  272. void FilePath::join(const StringView& _str)
  273. {
  274. char tmp[kMaxFilePath];
  275. strCopy(tmp, BX_COUNTOF(tmp), m_filePath);
  276. strCat(tmp, BX_COUNTOF(tmp), "/");
  277. strCat(tmp, BX_COUNTOF(tmp), _str);
  278. set(tmp);
  279. }
  280. FilePath::operator StringView() const
  281. {
  282. return StringView(m_filePath, strLen(m_filePath) );
  283. }
  284. const char* FilePath::getCPtr() const
  285. {
  286. return m_filePath;
  287. }
  288. StringView FilePath::getPath() const
  289. {
  290. StringView end = strRFind(m_filePath, '/');
  291. if (!end.isEmpty() )
  292. {
  293. return StringView(m_filePath, end.getPtr()+1);
  294. }
  295. return StringView();
  296. }
  297. StringView FilePath::getFileName() const
  298. {
  299. StringView fileName = strRFind(m_filePath, '/');
  300. if (!fileName.isEmpty() )
  301. {
  302. return StringView(fileName.getPtr()+1);
  303. }
  304. return getCPtr();
  305. }
  306. StringView FilePath::getBaseName() const
  307. {
  308. const StringView fileName = getFileName();
  309. if (!fileName.isEmpty() )
  310. {
  311. StringView ext = strFind(fileName, '.');
  312. if (!ext.isEmpty() )
  313. {
  314. return StringView(fileName.getPtr(), ext.getPtr() );
  315. }
  316. return fileName;
  317. }
  318. return StringView();
  319. }
  320. StringView FilePath::getExt() const
  321. {
  322. const StringView fileName = getFileName();
  323. if (!fileName.isEmpty() )
  324. {
  325. const StringView dot = strFind(fileName, '.');
  326. return StringView(dot.getPtr(), fileName.getTerm() );
  327. }
  328. return StringView();
  329. }
  330. bool FilePath::isAbsolute() const
  331. {
  332. return '/' == m_filePath[0] // no drive letter
  333. || (':' == m_filePath[1] && '/' == m_filePath[2]) // with drive letter
  334. ;
  335. }
  336. bool FilePath::isEmpty() const
  337. {
  338. return 0 == strCmp(m_filePath, ".");
  339. }
  340. } // namespace bx