YSTest  PreAlpha_b500_20140530
The YSLib Test Project
 全部  命名空间 文件 函数 变量 类型定义 枚举 枚举值 友元 宏定义  
YCLib/FileSystem.cpp
浏览该文件的文档.
1 /*
2  © 2012-2014 FrankHB.
3 
4  This file is part of the YSLib project, and may only be used,
5  modified, and distributed under the terms of the YSLib project
6  license, LICENSE.TXT. By continuing to use, modify, or distribute
7  this file you indicate that you have read the license and
8  understand and accept it fully.
9 */
10 
28 #include "YCLib/YModules.h"
29 #include "CHRLib/YModules.h"
30 #include YFM_YCLib_FileSystem
31 #include YFM_YCLib_NativeAPI
32 #include YFM_CHRLib_CharacterProcessing
33 #include <cstring> // for std::strcpy, std::strchr;
34 #if YCL_DS
35 # include <fcntl.h>
36 
38 extern "C" int _EXFUN(fileno, (FILE *));
39 #elif YCL_Win32
40 # if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
41 // At least one headers of <stdlib.h>, <stdio.h>, <windows.h>, <windef.h>
42 // (and probably more) should have been included to make the MinGW-W64 macro
43 // available if it is really being used.
45 
46 extern "C" _CRTIMP int __cdecl __MINGW_NOTHROW
47 _fileno(::FILE*);
48 
49 extern "C" _CRTIMP ::FILE* __cdecl __MINGW_NOTHROW
50 _wfopen(const wchar_t*, const wchar_t*);
52 # endif
53 # include <Shlwapi.h> // for ::PathIsRelativeW;
54 #elif YCL_Android
55 # include <fcntl.h>
56 # include <dirent.h>
57 # include <sys/stat.h>
58 #endif
59 
61 using namespace CHRLib;
62 
63 namespace platform
64 {
65 
66 static_assert(std::is_same<CHRLib::ucs2_t, char16_t>::value,
67  "Wrong character type found.");
68 static_assert(std::is_same<CHRLib::ucs4_t, char32_t>::value,
69  "Wrong character type found.");
70 #if YCL_Win32
71 static_assert(sizeof(wchar_t) == sizeof(CHRLib::ucs2_t),
72  "Wrong character type found.");
73 static_assert(yalignof(wchar_t) == yalignof(CHRLib::ucs2_t),
74  "Inconsistent alignment between character types found.");
75 #endif
76 
77 namespace
78 {
79 
80 #if YCL_DS || YCL_Android
81 #elif YCL_MinGW32
82 std::wstring
83 u_to_w(const char* str)
84 {
85  return std::wstring(reinterpret_cast<const wchar_t*>(ucsdup(str).c_str()));
86 }
87 
88 
90 
91 class DirEnv
92 {
93 public:
94  std::wstring d_name;
95  WIN32_FIND_DATAW& find_data;
96 
97  DirEnv(std::wstring&, ::WIN32_FIND_DATAW&);
98 
99  DefGetter(const ynothrow, ::DWORD, Attributes, find_data.dwFileAttributes)
100 };
101 
102 DirEnv::DirEnv(std::wstring& dir_name, ::WIN32_FIND_DATAW& win32_fdata)
103  : d_name(), find_data(win32_fdata)
104 {
105  yconstraint(!dir_name.empty() && dir_name.back() != '\\');
106 
107  const auto r(::GetFileAttributesW(dir_name.c_str()));
108 
109  if(r != INVALID_FILE_ATTRIBUTES && r & FILE_ATTRIBUTE_DIRECTORY)
110  dir_name += L"\\*";
111  else
112  throw FileOperationFailure("Opening directory failed.");
113 }
114 
115 
116 class DirectoryData : private ystdex::noncopyable
117 {
118 private:
119  std::wstring dir_name;
120  ::WIN32_FIND_DATAW find_data;
121  ::HANDLE h_node;
122  DirEnv posix_dir;
123 
124 public:
125  DirectoryData(const char*);
126  ~DirectoryData();
127 
128  DefGetterMem(const ynothrow, ::DWORD, Attributes, posix_dir)
129 
130 private:
131  void
132  Close() ynothrow;
133 
134 public:
135  DirEnv*
136  Read();
137 
138  void
139  Rewind() ynothrow;
140 };
141 
142 DirectoryData::DirectoryData(const char* name)
143  : dir_name(ystdex::rtrim(u_to_w(name), L"/\\")),
144  find_data(), h_node(), posix_dir(dir_name, find_data)
145 {}
146 DirectoryData::~DirectoryData()
147 {
148  if(h_node)
149  Close();
150 }
151 
152 void
154 {
155  const auto res(::FindClose(h_node));
156 
157  YAssert(res, "No valid directory found.");
158  yunused(res);
159 }
160 
161 DirEnv*
162 DirectoryData::Read()
163 {
164  if(!h_node)
165  {
166  // NOTE: See MSDN "FindFirstFile function" for details.
167  yassume(!dir_name.empty());
168  yassume(dir_name.back() != L'\\');
169  if((h_node = ::FindFirstFileW(dir_name.c_str(), &find_data))
170  == INVALID_HANDLE_VALUE)
171  h_node = {};
172  }
173  else if(!::FindNextFileW(h_node, &find_data))
174  {
175  Close();
176  h_node = {};
177  }
178  if(h_node && h_node != INVALID_HANDLE_VALUE)
179  {
180  yassume(find_data.cFileName);
181  posix_dir.d_name = find_data.cFileName;
182  }
183  return !h_node ? nullptr : &posix_dir;
184 }
185 
186 void
187 DirectoryData::Rewind() ynothrow
188 {
189  if(h_node)
190  {
191  Close();
192  h_node = {};
193  }
194 }
196 #else
197 # error "Unsupported platform found."
198 #endif
199 
200 } // unnamed namespace;
201 
202 
203 int
204 uopen(const char* filename, int oflag) ynothrow
205 {
206  yconstraint(filename);
207 #if !YCL_Win32
208  return ::open(filename, oflag);
209 #else
210  try
211  {
212  return ::_wopen(u_to_w(filename).c_str(), oflag);
213  }
214  catch(...)
215  {}
216  return -1;
217 #endif
218 }
219 int
220 uopen(const char* filename, int oflag, int pmode) ynothrow
221 {
222  yconstraint(filename);
223 #if !YCL_Win32
224  return ::open(filename, oflag, pmode);
225 #else
226  try
227  {
228  return ::_wopen(u_to_w(filename).c_str(), oflag, pmode);
229  }
230  catch(...)
231  {}
232  return -1;
233 #endif
234 }
235 int
236 uopen(const char16_t* filename, int oflag) ynothrow
237 {
238  yconstraint(filename);
239 #if !YCL_Win32
240  try
241  {
242  return ::open(strdup(filename).c_str(), oflag);
243  }
244  catch(...)
245  {}
246  return -1;
247 #else
248  return ::_wopen(reinterpret_cast<const wchar_t*>(filename), oflag);
249 #endif
250 }
251 int
252 uopen(const char16_t* filename, int oflag, int pmode) ynothrow
253 {
254  yconstraint(filename);
255 #if !YCL_Win32
256  try
257  {
258  return ::open(strdup(filename).c_str(), oflag, pmode);
259  }
260  catch(...)
261  {}
262  return -1;
263 #else
264  return ::_wopen(reinterpret_cast<const wchar_t*>(filename), oflag, pmode);
265 #endif
266 }
267 
268 std::FILE*
269 ufopen(const char* filename, const char* mode) ynothrow
270 {
271  yconstraint(filename),
272  yconstraint(mode);
273  yconstraint(*mode != char());
274 #if !YCL_Win32
275  return std::fopen(filename, mode);
276 #else
277  try
278  {
279  return ::_wfopen(u_to_w(filename).c_str(), u_to_w(mode).c_str());
280  }
281  catch(...)
282  {}
283  return nullptr;
284 #endif
285 }
286 std::FILE*
287 ufopen(const char16_t* filename, const char16_t* mode) ynothrow
288 {
289  yconstraint(filename),
290  yconstraint(mode);
291  yconstraint(*mode != char());
292 #if !YCL_Win32
293  try
294  {
295  return std::fopen(strdup(filename).c_str(), strdup(mode).c_str());
296  }
297  catch(...)
298  {}
299  return nullptr;
300 #else
301  return ::_wfopen(reinterpret_cast<const wchar_t*>(filename),
302  reinterpret_cast<const wchar_t*>(mode));
303 #endif
304 }
305 
306 bool
307 ufexists(const char* filename) ynothrow
308 {
309 #if !YCL_Win32
310  return ystdex::fexists(filename);
311 #else
312  yconstraint(filename);
313  if(const auto file = ufopen(filename, "rb"))
314  {
315  std::fclose(file);
316  return true;
317  }
318  return false;
319 #endif
320 }
321 bool
322 ufexists(const char16_t* filename) ynothrow
323 {
324  yconstraint(filename);
325 
326  if(const auto file = ufopen(filename, u"rb"))
327  {
328  std::fclose(file);
329  return true;
330  }
331  return false;
332 }
333 
334 char16_t*
335 u16getcwd_n(char16_t* buf, std::size_t size) ynothrow
336 {
337  if(size == 0)
338  // last_err = EINVAL;
339  ;
340  else
341  {
342  using namespace std;
343  using namespace CHRLib;
344 
345  if(YB_LIKELY(buf))
346 #if !YCL_Win32
347  {
348  const auto p(static_cast<ucs2_t*>(malloc((size + 1)
349  * sizeof(ucs2_t))));
350 
351  if(YB_LIKELY(p))
352  {
353  auto len(MBCSToUCS2(p,
354  ::getcwd(reinterpret_cast<char*>(buf), size)));
355 
356  if(size < len + 1)
357  {
358  // last_err = ERANGE;
359  return nullptr;
360  }
361  memcpy(buf, p, ++len * sizeof(ucs2_t));
362  return buf;
363  }
364  // else
365  // last_err = ENOMEM;
366  }
367 #else
368  return reinterpret_cast<ucs2_t*>(
369  ::_wgetcwd(reinterpret_cast<wchar_t*>(buf), size));
370 #endif
371  }
372  return nullptr;
373 }
374 
376 #define YCL_FileSystem_ufunc_impl1(_n) \
377 bool \
378 _n(const char* path) ynothrow \
379 { \
380  yconstraint(path); \
381 
382 #if !YCL_Win32
383 # define YCL_FileSystem_ufunc_impl2(_fn, _wfn) \
385  return _fn(path) == 0; \
386 }
387 #else
388 # define YCL_FileSystem_ufunc_impl2(_fn, _wfn) \
390  try \
391  { \
392  return _wfn(u_to_w(path).c_str()) == 0; \
393  } \
394  catch(...) \
395  {} \
396  return false; \
397 }
398 #endif
399 
401 #define YCL_FileSystem_ufunc_impl(_n, _fn, _wfn) \
402  YCL_FileSystem_ufunc_impl1(_n) \
403  YCL_FileSystem_ufunc_impl2(_fn, _wfn)
404 
405 YCL_FileSystem_ufunc_impl(uchdir, ::chdir, ::_wchdir)
406 
408 #if !YCL_Win32
409  return ::mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) == 0;
410 }
411 #else
412  YCL_FileSystem_ufunc_impl2(_unused_, ::_wmkdir)
413 #endif
414 
415 YCL_FileSystem_ufunc_impl(urmdir, ::rmdir, ::_wrmdir)
416 
417 YCL_FileSystem_ufunc_impl(uunlink, ::unlink, ::_wunlink)
418 
419 YCL_FileSystem_ufunc_impl(uremove, std::remove, ::_wremove)
420 
421 #undef YCL_FileSystem_ufunc_impl1
422 #undef YCL_FileSystem_ufunc_impl2
423 #undef YCL_FileSystem_ufunc_impl
424 
425 bool
426 truncate(std::FILE* fp, std::size_t size) ynothrow
427 {
428 #if !YCL_Win32
429  return ::ftruncate(fileno(fp), ::off_t(size)) == 0;
430 #else
431  return ::_chsize(::_fileno(fp), long(size)) == 0;
432 #endif
433 }
434 
435 
436 std::uint64_t
438 {
439 #if !YCL_Win32
440  struct ::stat st;
441 
442  if(::fstat(fd, &st) == 0)
443  return st.st_size;
444 #else
445  ::LARGE_INTEGER sz;
446 
447  // XXX: Error handling for indirect calls.
448  if(::GetFileSizeEx(::HANDLE(::_get_osfhandle(fd)), &sz) != 0
449  && YB_LIKELY(sz.QuadPart >= 0))
450  return sz.QuadPart;
451 #endif
452  throw FileOperationFailure("Failed getting file size.");
453 }
454 std::uint64_t
455 GetFileSizeOf(std::FILE* fp)
456 {
457  yconstraint(fp);
458 #ifdef YCL_API_FILESYSTEM_POSIX
459  return GetFileSizeOf(fileno(fp));
460 #else
461  return GetFileSizeOf(::_fileno(fp));
462 #endif
463 }
464 
465 
467  : dir(
468 #if !YCL_Win32
469  ::opendir
470 #else
471  new DirectoryData
472 #endif
473  (path && *path != '\0' ? path : ".")
474  )
475 {
476  if(!dir)
477  throw FileOperationFailure("Opening directory failed.");
478 }
480 {
481 #if !YCL_Win32
482  const auto res(::closedir(dir));
483 
484  YAssert(res == 0, "No valid directory found.");
485  yunused(res);
486 #else
487  delete static_cast<DirectoryData*>(dir);
488 #endif
489 }
490 
491 void
492 DirectorySession::Rewind() ynothrow
493 {
494  YAssert(dir, "Invalid native handle found.");
495 #if !YCL_Win32
496  ::rewinddir(dir);
497 #else
498  static_cast<DirectoryData*>(dir)->Rewind();
499 #endif
500 }
501 
502 
503 HDirectory&
505 {
506 #if !YCL_Win32
507  p_dirent = ::readdir(GetNativeHandle());
508 #else
509  p_dirent = static_cast<DirectoryData*>(GetNativeHandle())->Read();
510 #endif
511  return *this;
512 }
513 
516 {
517  if(p_dirent)
518  {
519 #if !YCL_Win32
520  return p_dirent->d_type & DT_DIR ? NodeCategory::Directory
522 #else
523  return static_cast<DirectoryData*>(GetNativeHandle())->GetAttributes()
524  & FILE_ATTRIBUTE_DIRECTORY ? NodeCategory::Directory
526 #endif
527  }
528  return NodeCategory::Empty;
529 }
530 
531 const char*
533 {
534  if(p_dirent)
535  {
536 #if !YCL_Win32
537  return p_dirent->d_name;
538 #else
539  try
540  {
541  utf8_name = strdup(reinterpret_cast<const char16_t*>(
542  (static_cast<DirEnv*>(p_dirent))->d_name.c_str()));
543  return &utf8_name[0];
544  }
545  catch(...)
546  {}
547 #endif
548  }
549  return ".";
550 }
551 
552 
553 bool
554 IsAbsolute(const char* path)
555 {
556 #if !YCL_Win32
557  if(path)
558  {
559  if(*path == '/')
560  return true;
561 
562  const auto p(std::strstr(path, ":/"));
563 
564  return p && p != path && !std::strstr(p, ":/");
565  }
566  return false;
567 #else
568  return !::PathIsRelativeW(u_to_w(path).c_str());
569 #endif
570 }
571 
573 GetRootNameLength(const char* path)
574 {
575  const char* p(std::strchr(path, ':'));
576 
577  return !p ? 0 : p - path + 1;
578 }
579 
580 }
581 
582 namespace platform_ex
583 {
584 
585 #if !YCL_Win32
586 char16_t
587 FS_IsRoot(const char16_t* str)
588 {
589  const std::u16string ustr(str);
590 #if YCL_DS
591  return ustr == u"/" || ustr == u"fat:/" || ustr == u"sd:/";
592 #else
593  return ustr == u"/";
594 #endif
595 }
596 #endif
597 
598 }
599 
char16_t * u16getcwd_n(char16_t *buf, std::size_t size) ynothrow
std::FILE ConversionState fp
Definition: chrproc.h:88
YF_API bool IsAbsolute(const char *)
判断指定路径字符串是否表示一个绝对路径。
YF_API std::string strdup(const ucs2_t *, Encoding=CS_Default)
复制 UCS-2 字符串为多字节字符串。
Definition: chrproc.cpp:162
YF_API bool truncate(std::FILE *, std::size_t) ynothrow
截断文件至指定长度。
YF_API std::size_t GetRootNameLength(const char *)
取指定路径的文件系统根节点名称的长度。
DirectorySession(const char *path={})
构造:打开目录路径。
#define yunused(...)
标记未使用的表达式。
Definition: ydef.h:697
GetName()) const char *GetName() const ynothrow
间接操作:取节点名称。
YF_API size_t MBCSToUCS2(ucs2_t *, const char *, Encoding=CS_Default)
按指定编码转换 MBCS 字符串为 UCS-2 字符串,返回转换的串长。
Definition: chrproc.cpp:112
#define YCL_FileSystem_ufunc_impl(_n, _fn, _wfn)
YF_API bool ufexists(const char *) ynothrow
判断指定 UTF-8 文件名的文件是否存在。
#define DefGetter(_q, _t, _n,...)
Definition: YBaseMacro.h:180
YF_API std::basic_string< ucs2_t > ucsdup(const char *, Encoding=CS_Default)
复制多字节字符串为 UCS-2 字符串。
Definition: chrproc.cpp:174
std::size_t size ynothrow
YF_API bool uchdir(const char *) ynothrow
切换当前工作路径至指定的 UTF-8 字符串。
YF_API bool urmdir(const char *) ynothrow
按 UTF-8 路径删除一个空目录。
不可复制对象:禁止派生类调用默认原型的复制构造函数和复制赋值操作符。
Definition: utility.hpp:75
#define yassume
假定:环境语义。
Definition: cassert.h:58
~DirectorySession()
析构:关闭目录路径。
#define ynothrow
YSLib 无异常抛出保证:若支持 noexcept 关键字, 指定特定的 noexcept 异常规范。
Definition: ydef.h:514
yconstfn const string & name
Definition: Loader.h:110
#define yalignof(_type)
指定特定类型的对齐。
Definition: ydef.h:442
::dirent * p_dirent
节点信息。
YF_API std::FILE * ufopen(const char *filename, const char *mode) ynothrow
以 UTF-8 文件名打开文件。
#define yconstraint
约束:接口语义。
Definition: cassert.h:47
#define YCL_FileSystem_ufunc_impl2(_fn, _wfn)
NodeCategory GetNodeCategory() const ynothrow
取节点状态信息确定的文件系统节点类别。
if(YB_LIKELY(!error)) if(YB_LIKELY(!(error
YF_API bool uunlink(const char *) ynothrow
按 UTF-8 路径删除一个非目录文件。
YF_API bool uremove(const char *) ynothrow
按 UTF-8 路径删除一个文件。
YF_API bool umkdir(const char *) ynothrow
按 UTF-8 路径以默认权限新建一个目录。
const ynothrow ImplRet this HDirectory & operator++()
迭代:向后遍历。
char16_t FS_IsRoot(const char16_t *)
#define YCL_FileSystem_ufunc_impl1(_n)
char16_t ucs2_t
UCS-2 字符类型。
Definition: chrdef.h:44
bounds & r
Definition: ydraw.h:220
表示文件操作失败的异常。
#define DefGetterMem(_q, _t, _n, _m)
Definition: YBaseMacro.h:185
YF_API std::uint64_t GetFileSizeOf(int)
取文件的大小。
#define YB_LIKELY(expr)
Definition: ydef.h:297
bool fexists(const char *)
判断指定路径的文件是否存在。
Definition: cstdio.cpp:36
YF_API int uopen(const char *filename, int oflag) ynothrow
以 UTF-8 文件名无缓冲打开文件。
NodeCategory
文件系统节点类别。
void Close(IWidget &wgt)
Definition: ywidget.cpp:95
#define YAssert(_expr, _msg)
Definition: cassert.h:73
_tString && rtrim(_tString &&str, typename string_traits< _tString >::const_pointer t=&to_array< typename string_traits< _tString >::value_type >("\n\r\t\v ")[0])
删除字符串中指定的连续后缀字符。
Definition: string.hpp:231