YSTest: FileSystem.cpp 源文件

YSTest PreAlpha_b800_20170810 The YSLib Test Project

- 首页
- 相关页面
- 模块
- 命名空间
- 类
- 文件

- 文件列表
- 文件成员

YCLib/FileSystem.cpp
浏览该文件的文档.
1 /*
2 © 2011-2017 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 YFM_YCLib_FileSystem // for basic_string, ystdex::pun_storage_t,
30 // default_delete, make_observer, std::min, std::accumulate, std::tm,
31 // ystdex::read_uint_le, YAssertNonnull, ystdex::write_uint_le, std::bind,
32 // std::ref, ystdex::retry_on_cond;
33 #include YFM_YCLib_FileIO // for platform_ex::MakePathStringW,
34 // platform_Ex::MakePathStringU, MakePathString, Deref, ystdex::throw_error,
35 // std::invalid_argument, std::errc::function_not_supported, YCL_CallF_CAPI,
36 // CategorizeNode, ystdex::ntctslen, std::wctob, std::towupper,
37 // ystdex::restrict_length, std::min, ystdex::ntctsicmp,
38 // std::errc::invalid_argument, std::strchr; , std::errc::invalid_argument
39 #include YFM_YCLib_NativeAPI // for Mode, struct ::stat, ::stat,
40 // ::GetFileAttributesW, ::linkat, ::symlink, ::lstat, ::readlink;
41 #include "CHRLib/YModules.h"
42 #include YFM_CHRLib_CharacterProcessing // for CHRLib::MakeUCS2LE;
43 #include <ystdex/ctime.h> // for ystdex::is_date_range_valid,
44 // ystdex::is_time_no_leap_valid;
45 #if YCL_Win32
46 # include YFM_Win32_YCLib_MinGW32 // for platform_ex::Invalid,
47 // platform_ex::ResolveReparsePoint, platform_ex::DirectoryFindData;
48 # include <time.h> // for ::localtime_s;
49
51 namespace
52 {
53
54 namespace YCL_Impl_details
55 {
56
57 // NOTE: To avoid hiding of global name, the declarations shall not be under
58 // namespace %platform.
59 YCL_DeclW32Call(CreateSymbolicLinkW, kernel32, unsigned char, const wchar_t*, \
60 const wchar_t*, unsigned long)
61 using platform::wcast;
62
63 // NOTE: As %SYMBOLIC_LINK_FLAG_DIRECTORY, but with correct type.
64 yconstexpr const auto SymbolicLinkFlagDirectory(1UL);
65
66 inline PDefH(void, W32_CreateSymbolicLink, const char16_t* dst,
67 const char16_t* src, unsigned long flags)
68 ImplExpr(YCL_CallF_Win32(CreateSymbolicLinkW, wcast(dst), wcast(src), flags))
69
70 } // namespace YCL_Impl_details;
71
72 } // unnamed namespace;
73
75 using platform_ex::MakePathStringW;
77 using platform_ex::DirectoryFindData;
78 #else
79 # include <dirent.h>
80 # include <ystdex/scope_guard.hpp> // for ystdex::swap_guard;
81 # include <time.h> // for ::localtime_r;
82
84 using platform_ex::MakePathStringU;
85 #endif
86
88 using namespace CHRLib;
89
90 namespace platform
91 {
92
93 namespace
94 {
95
96 #if !YCL_Win32
97 using errno_guard = ystdex::swap_guard<int, void, decltype(errno)&>;
99 #endif
100
102 #if !YCL_DS
103 template<typename _tChar>
104 bool
105 IterateLinkImpl(basic_string<_tChar>& path, size_t& n)
106 {
107 if(!path.empty())
108 try
109 {
110 // TODO: Throw with context information about failed path?
111 // TODO: Use preallocted object to improve performance?
112 path = ReadLink(path.c_str());
113 if(n != 0)
114 --n;
115 else
116 // XXX: Enumerator
117 // %std::errc::too_many_symbolic_link_levels is
118 // commented out in MinGW-w64 configuration of
119 // libstdc++. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71444.
120 ystdex::throw_error(ELOOP, "ReadLink @ platform::IteratorLink");
121 return true;
122 }
123 CatchIgnore(std::invalid_argument&)
124 return {};
125 }
126 #endif
127
128 #if YCL_Win32
129 template<class _type, typename _tParam>
131 _type*
132 CreateDirectoryDataPtr(_tParam&& arg)
133 {
134 return new _type(yforward(arg));
135 }
136 #endif
137
138 } // unnamed namespace;
139
140 bool
141 IsDirectory(const char* path)
142 {
143 #if YCL_Win32
144 return IsDirectory(ucast(MakePathStringW(path).c_str()));
145 #else
146 // TODO: Merge? See %platform_ex::estat in %NativeAPI.
147 struct ::stat st;
148
149 return ::stat(path, &st) == 0 && bool(Mode(st.st_mode) & Mode::Directory);
150 #endif
151 }
152 bool
153 IsDirectory(const char16_t* path)
154 {
155 #if YCL_Win32
156 // TODO: Simplify?
157 const auto attr(::GetFileAttributesW(wcast(path)));
158
159 return attr != platform_ex::Invalid
160 && (platform_ex::FileAttributes(attr) & platform_ex::Directory);
161 #else
162 return IsDirectory(MakePathString(path).c_str());
163 #endif
164 }
165
166 void
167 CreateHardLink(const char* dst, const char* src)
168 {
169 #if YCL_DS
170 YAssertNonnull(dst), YAssertNonnull(src);
171 ystdex::throw_error(std::errc::function_not_supported, yfsig);
172 #elif YCL_Win32
173 CreateHardLink(ucast(MakePathStringW(dst).c_str()),
174 ucast(MakePathStringW(src).c_str()));
175 #else
176 // NOTE: POSIX %::link is implementation-defined to following symbolic
177 // link target.
178 YCL_CallF_CAPI(, ::linkat, AT_FDCWD, Nonnull(src), AT_FDCWD, Nonnull(dst),
179 AT_SYMLINK_FOLLOW);
180 #endif
181 }
182 void
183 CreateHardLink(const char16_t* dst, const char16_t* src)
184 {
185 #if YCL_DS
186 YAssertNonnull(dst), YAssertNonnull(src);
187 ystdex::throw_error(std::errc::function_not_supported, yfsig);
188 #elif YCL_Win32
189 YCL_CallF_Win32(CreateHardLinkW, wcast(dst), wcast(src), {});
190 #else
191 // TODO: To make the behavior specific and same as on platform %Win32, use
192 // %::realpath on platform %Linux, etc.
193 CreateHardLink(MakePathString(dst).c_str(), MakePathString(src).c_str());
194 #endif
195 }
196
197 void
198 CreateSymbolicLink(const char* dst, const char* src, bool is_dir)
199 {
200 #if YCL_DS
201 yunused(dst), yunused(src), yunused(is_dir);
202 ystdex::throw_error(std::errc::function_not_supported, yfsig);
203 #elif YCL_Win32
204 using namespace platform_ex;
205
206 CreateSymbolicLink(ucast(MakePathStringW(dst).c_str()),
207 ucast(MakePathStringW(src).c_str()), is_dir);
208 #else
209 yunused(is_dir);
210 YCL_CallF_CAPI(, ::symlink, Nonnull(src), Nonnull(dst));
211 #endif
212 }
213 void
214 CreateSymbolicLink(const char16_t* dst, const char16_t* src, bool is_dir)
215 {
216 #if YCL_DS
217 yunused(dst), yunused(src), yunused(is_dir);
218 ystdex::throw_error(std::errc::function_not_supported, yfsig);
219 #elif YCL_Win32
220 YCL_Impl_details::W32_CreateSymbolicLink(dst, src,
221 is_dir ? YCL_Impl_details::SymbolicLinkFlagDirectory : 0UL);
222 #else
223 CreateSymbolicLink(MakePathString(dst).c_str(), MakePathString(src).c_str(),
224 is_dir);
225 #endif
226 }
227
228 string
229 ReadLink(const char* path)
230 {
231 #if YCL_DS
232 YAssertNonnull(path);
233 ystdex::throw_error(std::errc::function_not_supported, yfsig);
234 #elif YCL_Win32
235 // TODO: Simplify?
236 return
237 MakePathString(ReadLink(ucast(MakePathStringW(path).c_str())).c_str());
238 #else
239 struct ::stat st;
240
241 YCL_CallF_CAPI(, ::lstat, Nonnull(path), &st);
242 if((Mode(st.st_mode) & Mode::Link) == Mode::Link)
243 {
244 auto n(st.st_size);
245
246 // NOTE: Some file systems like procfs on Linux are not
247 // conforming to POSIX, thus %st.st_size is not always reliable. In
248 // most cases, it is 0. Only 0 is currently supported.
249 if(n >= 0)
250 {
251 // FIXME: Blocked. TOCTTOU access.
252 if(n == 0)
253 // TODO: Use %::pathconf to determine initial length instead
254 // of magic number.
255 n = yimpl(1024);
256 return ystdex::retry_for_vector<string>(size_t(n),
257 [&](string& res, size_t s) -> bool{
258 errno_guard gd(errno, 0);
259 const auto r(::readlink(path, &res[0], size_t(n)));
260
261 if(r < 0)
262 {
263 const int err(errno);
264
265 if(err == EINVAL)
266 throw std::invalid_argument("Failed reading link:"
267 " Specified file is not a link.");
268 YCL_RaiseZ_SysE(, err, "::readlink", yfsig);
269 throw std::runtime_error(
270 "Unknown error @ ::readlink.");
271 }
272 if(size_t(r) <= s)
273 {
274 res.resize(size_t(r));
275 return {};
276 }
277 return true;
278 });
279 }
280 throw std::invalid_argument("Invalid link size found.");
281 }
282 throw std::invalid_argument("Specified file is not a link.");
283 #endif
284 }
285 u16string
286 ReadLink(const char16_t* path)
287 {
288 #if YCL_DS
289 YAssertNonnull(path);
290 ystdex::throw_error(std::errc::function_not_supported, yfsig);
291 #elif YCL_Win32
292 using namespace platform_ex;
293 const auto sv(ResolveReparsePoint(wcast(path), ReparsePointData().Get()));
294
295 return {sv.cbegin(), sv.cend()};
296 #else
297 return MakePathStringU(ReadLink(MakePathString(path).c_str()));
298 #endif
299 }
300
301 #if !YCL_DS
302 bool
303 IterateLink(string& path, size_t& n)
304 {
305 return IterateLinkImpl(path, n);
306 }
307 bool
308 IterateLink(u16string& path, size_t& n)
309 {
310 return IterateLinkImpl(path, n);
311 }
312 #endif
313
314
315 #if YCL_Win32
316 class DirectorySession::Data final : public DirectoryFindData
317 {
318 public:
319 using DirectoryFindData::DirectoryFindData;
320 };
321 #else
322 namespace
324 {
325
326 PDefH(::DIR*, ToDirPtr, DirectorySession::NativeHandle p)
327 ImplRet(static_cast<::DIR*>(p))
328
329 } // unnamed namespace;
330 #endif
331 void
332 DirectorySession::Deleter::operator()(pointer p) const ynothrowv
333 {
334 #if YCL_Win32
335 default_delete<Data>::operator()(p);
336 #else
337 const auto res(::closedir(ToDirPtr(p)));
338
339 YAssert(res == 0, "No valid directory found.");
340 yunused(res);
341 #endif
342 }
343
344
345 DirectorySession::DirectorySession()
346 #if YCL_Win32
347 : DirectorySession(u".")
348 #else
349 : DirectorySession(".")
350 #endif
351 {}
352 DirectorySession::DirectorySession(const char* path)
353 #if YCL_Win32
354 : dir(CreateDirectoryDataPtr<Data>(MakePathStringW(path)))
355 #else
356 : sDirPath([](const char* p) YB_NONNULL(1){
357 const auto res(Deref(p) != char()
358 ? ystdex::rtrim(string(p), FetchSeparator<char>()) : ".");
359
360 YAssert(res.empty() || EndsWithNonSeperator(res),
361 "Invalid directory name state found.");
362 return res + FetchSeparator<char>();
363 }(path)),
364 dir(::opendir(sDirPath.c_str()))
365 #endif
366 {
367 #if !YCL_Win32
368 if(!dir)
369 ystdex::throw_error(errno, yfsig);
370 #endif
371 }
372 DirectorySession::DirectorySession(const char16_t* path)
373 #if YCL_Win32
374 : dir(CreateDirectoryDataPtr<Data>(path))
375 #else
376 : DirectorySession(MakePathString(path).c_str())
377 #endif
378 {}
379
380 void
381 DirectorySession::Rewind() ynothrow
382 {
383 YAssert(dir, "Invalid native handle found.");
384 #if YCL_Win32
385 Deref(dir.get()).Rewind();
386 #else
387 ::rewinddir(ToDirPtr(dir.get()));
388 #endif
389 }
390
391
392 HDirectory&
393 HDirectory::operator++()
394 {
395 #if YCL_Win32
396 auto& find_data(*static_cast<DirectoryFindData*>(GetNativeHandle()));
397
398 if(find_data.Read())
399 {
400 const wstring_view sv(find_data.GetEntryName());
401
402 dirent_str = u16string(sv.cbegin(), sv.cend());
403 YAssert(!dirent_str.empty(), "Invariant violation found.");
404 }
405 else
406 dirent_str.clear();
407 #else
408 YAssert(!p_dirent || bool(GetNativeHandle()), "Invariant violation found.");
409
410 const errno_guard gd(errno, 0);
411
412 if(const auto p = ::readdir(ToDirPtr(GetNativeHandle())))
413 p_dirent = make_observer(p);
414 else
415 {
416 const int err(errno);
417
418 if(err == 0)
419 p_dirent = {};
420 else
421 ystdex::throw_error(errno, yfsig);
422 }
423 #endif
424 return *this;
425 }
426
427 NodeCategory
428 HDirectory::GetNodeCategory() const ynothrow
429 {
430 if(*this)
431 {
432 YAssert(bool(GetNativeHandle()), "Invariant violation found.");
433
434 #if YCL_Win32
435 const auto res(Deref(static_cast<platform_ex::DirectoryFindData*>(
436 GetNativeHandle())).GetNodeCategory());
437 #else
438 using Descriptions::Warning;
439 auto res(NodeCategory::Empty);
440
441 try
442 {
443 auto name(sDirPath + Deref(p_dirent).d_name);
444 struct ::stat st;
445
446 # if YCL_DS
447 # define YCL_Impl_lstatn ::stat
448 # else
449 # define YCL_Impl_lstatn ::lstat
450 # endif
451 // TODO: Simplify.
452 // XXX: Value of %errno might be overwritten.
453 if(YCL_TraceCallF_CAPI(YCL_Impl_lstatn, name.c_str(), &st) == 0)
454 res |= CategorizeNode(st.st_mode);
455 # if !YCL_DS
456 if(bool(res & NodeCategory::Link)
457 && YCL_TraceCallF_CAPI(::stat, name.c_str(), &st) == 0)
458 res |= CategorizeNode(st.st_mode);
459 # endif
460 # undef YCL_Impl_lstatn
461 }
462 CatchExpr(std::exception& e, YTraceDe(Warning, "Failed getting node "
463 "category (errno = %d) @ %s: %s.", errno, yfsig, e.what()))
464 CatchExpr(...,
465 YTraceDe(Warning, "Unknown error @ %s.", yfsig))
466 #endif
467 return res != NodeCategory::Empty ? res : NodeCategory::Invalid;
468 }
469 return NodeCategory::Empty;
470 }
471
472 HDirectory::operator string() const
473 {
474 #if YCL_Win32
475 return MakePathString(GetNativeName());
476 #else
477 return GetNativeName();
478 #endif
479 }
480 HDirectory::operator u16string() const
481 {
482 return
483 #if YCL_Win32
484 GetNativeName();
485 #else
486 MakePathStringU(GetNativeName());
487 #endif
488 }
489
490 const HDirectory::NativeChar*
491 HDirectory::GetNativeName() const ynothrow
492 {
493 return *this ?
494 #if YCL_Win32
495 dirent_str.data() : u".";
496 #else
497 p_dirent->d_name : ".";
498 #endif
499 }
500
501
502 namespace FAT
503 {
504
505 namespace LFN
506 {
507
508 tuple<string, string, bool>
509 ConvertToAlias(const u16string& long_name)
510 {
511 YAssert(ystdex::ntctslen(long_name.c_str()) == long_name.length(),
512 "Invalid long filename found.");
513
514 string alias;
515 // NOTE: Strip leading periods.
516 size_t offset(long_name.find_first_not_of(u'.'));
517 // NOTE: Set when the alias had to be modified to be valid.
518 bool lossy(offset != 0);
519 const auto check_char([&](string& str, char16_t uc){
520 int bc(std::wctob(std::towupper(wint_t(uc))));
521
522 if(!lossy && std::wctob(wint_t(uc)) != bc)
523 lossy = true;
524 if(bc == ' ')
525 {
526 // NOTE: Skip spaces in filename.
527 lossy = true;
528 return;
529 }
530 // TODO: Optimize.
531 if(bc == EOF || string(IllegalAliasCharacters).find(char(bc))
532 != string::npos)
533 // NOTE: Replace unconvertible or illegal characters with
534 // underscores. See Microsoft FAT specification Section 7.4.
535 yunseq(bc = '_', lossy = true);
536 str += char(bc);
537 });
538 const auto len(long_name.length());
539
540 for(; alias.length() < MaxAliasMainPartLength && long_name[offset] != u'.'
541 && offset != len; ++offset)
542 check_char(alias, long_name[offset]);
543 if(!lossy && long_name[offset] != u'.' && long_name[offset] != char16_t())
544 // NOTE: More than 8 characters in name.
545 lossy = true;
546
547 auto ext_pos(long_name.rfind(u'.'));
548 string ext;
549
550 if(!lossy && ext_pos != u16string::npos && long_name.rfind(u'.', ext_pos)
551 != u16string::npos)
552 // NOTE: More than one period in name.
553 lossy = true;
554 if(ext_pos != u16string::npos && ext_pos + 1 != len)
555 {
556 ++ext_pos;
557 for(size_t ext_len(0); ext_len < LFN::MaxAliasExtensionLength
558 && ext_pos != len; yunseq(++ext_len, ++ext_pos))
559 check_char(ext, long_name[ext_pos]);
560 if(ext_pos != len)
561 // NOTE: More than 3 characters in extension.
562 lossy = true;
563 }
564 return make_tuple(std::move(alias), std::move(ext), lossy);
565 }
566
567 string
568 ConvertToMBCS(const char16_t* path)
569 {
570 // TODO: Optimize?
571 ImplRet(ystdex::restrict_length(MakePathString({path,
572 std::min<size_t>(ystdex::ntctslen(path), MaxLength)}), MaxMBCSLength))
573 }
574
575 EntryDataUnit
576 GenerateAliasChecksum(const EntryDataUnit* p) ynothrowv
577 {
578 static_assert(std::is_same<EntryDataUnit, unsigned char>::value,
579 "Only unsigned char as byte is supported by checksum generation.");
580
581 YAssertNonnull(p);
582 // NOTE: The operation is an unsigned char rotate right.
583 return std::accumulate(p, p + AliasEntryLength, 0,
584 [](EntryDataUnit v, EntryDataUnit b){
585 return ((v & 1) != 0 ? 0x80 : 0) + (v >> 1) + b;
586 });
587 }
588
589 bool
590 ValidateName(string_view name) ynothrowv
591 {
592 YAssertNonnull(name.data());
593 return std::all_of(begin(name), end(name), [](char c) ynothrow{
594 // TODO: Use interval arithmetic.
595 return c >= 0x20 && static_cast<unsigned char>(c) < 0xF0;
596 });
597 }
598
599 void
600 WriteNumericTail(string& alias, size_t k) ynothrowv
601 {
602 YAssert(!(MaxAliasMainPartLength < alias.length()), "Invalid alias found.");
603
604 auto p(&alias[MaxAliasMainPartLength - 1]);
605
606 while(k > 0)
607 {
608 YAssert(p != &alias[0], "Value is too large to store in the alias.");
609 *p-- = '0' + k % 10;
610 k /= 10;
611 }
612 *p = '~';
613 }
614
615 } // namespace LFN;
616
617 std::time_t
618 ConvertFileTime(Timestamp d, Timestamp t) ynothrow
619 {
620 struct std::tm time_parts;
621
622 yunseq(
623 time_parts.tm_hour = t >> 11,
624 time_parts.tm_min = (t >> 5) & 0x3F,
625 time_parts.tm_sec = (t & 0x1F) << 1,
626 time_parts.tm_mday = d & 0x1F,
627 time_parts.tm_mon = ((d >> 5) & 0x0F) - 1,
628 time_parts.tm_year = (d >> 9) + 80,
629 time_parts.tm_isdst = 0
630 );
631 return std::mktime(&time_parts);
632 }
633
634 pair<Timestamp, Timestamp>
635 FetchDateTime() ynothrow
636 {
637 struct std::tm tmp;
638 std::time_t epoch;
639
640 if(std::time(&epoch) != std::time_t(-1))
641 {
642 #if YCL_Win32
643 // NOTE: The return type and parameter order differs than ISO C11
644 // library extension.
645 ::localtime_s(&tmp, &epoch);
646 #else
647 ::localtime_r(&epoch, &tmp);
648 // FIXME: For platforms without %::(localtime_r, localtime_s).
649 #endif
650 // NOTE: Microsoft FAT base year is 1980.
651 return {ystdex::is_date_range_valid(tmp) ? ((tmp.tm_year - 80) & 0x7F)
652 << 9 | ((tmp.tm_mon + 1) & 0xF) << 5 | (tmp.tm_mday & 0x1F) : 0,
653 ystdex::is_time_no_leap_valid(tmp) ? (tmp.tm_hour & 0x1F) << 11
654 | (tmp.tm_min & 0x3F) << 5 | ((tmp.tm_sec >> 1) & 0x1F) : 0};
655 }
656 return {0, 0};
657 }
658
659
660 void
661 EntryData::CopyLFN(char16_t* str) const ynothrowv
662 {
663 const auto pos(LFN::FetchLongNameOffset((*this)[LFN::Ordinal]));
664
665 YAssertNonnull(str);
666 for(size_t i(0); i < LFN::EntryLength; ++i)
667 if(pos + i < LFN::MaxLength - 1)
668 str[pos + i]
669 = ystdex::read_uint_le<16>(data() + LFN::OffsetTable[i]);
670 }
671
672 pair<EntryDataUnit, size_t>
673 EntryData::FillNewName(string_view name,
674 std::function<bool(string_view)> verify)
675 {
676 YAssertNonnull(name.data()),
677 YAssertNonnull(verify);
678
679 EntryDataUnit alias_check_sum(0);
680 size_t entry_size;
681
682 ClearAlias();
683 if(name == ".")
684 {
685 SetDot(0),
686 entry_size = 1;
687 }
688 else if(name == "..")
689 {
690 SetDot(0),
691 SetDot(1),
692 entry_size = 1;
693 }
694 else
695 {
696 const auto& long_name(CHRLib::MakeUCS2LE(name));
697 const auto len(long_name.length());
698
699 if(len < LFN::MaxLength)
700 {
701 auto alias_tp(LFN::ConvertToAlias(long_name));
702 auto& pri(get<0>(alias_tp));
703 const auto& ext(get<1>(alias_tp));
704 auto alias(pri);
705 const auto check(std::bind(verify, std::ref(alias)));
706
707 if(!ext.empty())
708 alias += '.' + ext;
709 if((get<2>(alias_tp) ? alias.length() : 0) == 0)
710 entry_size = 1;
711 else
712 {
713 entry_size
714 = (len + LFN::EntryLength - 1) / LFN::EntryLength + 1;
715 if(ystdex::ntctsicmp(alias.c_str(), name.data(),
716 LFN::MaxAliasLength) != 0 || check())
717 {
718 size_t i(1);
719
720 pri.resize(LFN::MaxAliasMainPartLength, '_');
721 alias = pri + '.' + ext;
722 ystdex::retry_on_cond(check, [&]{
723 if(YB_UNLIKELY(LFN::MaxNumericTail < i))
724 ystdex::throw_error(std::errc::invalid_argument,
725 yfsig);
726 LFN::WriteNumericTail(alias, i++);
727 });
728 }
729 }
730 WriteAlias(alias);
731 }
732 else
733 ystdex::throw_error(std::errc::invalid_argument, yfsig);
734 alias_check_sum = LFN::GenerateAliasChecksum(data());
735 }
736 return {alias_check_sum, entry_size};
737 }
738
739 bool
740 EntryData::FindAlias(string_view name) const
741 {
742 const auto alias(GenerateAlias());
743
744 return ystdex::ntctsicmp(Nonnull(name.data()), alias.c_str(),
745 std::min<size_t>(name.length(), alias.length())) == 0;
746 }
747
748 string
749 EntryData::GenerateAlias() const
750 {
751 if((*this)[0] != Free)
752 {
753 if((*this)[0] == '.')
754 return (*this)[1] == '.' ? ".." : ".";
755
756 // NOTE: Copy the base filename.
757 bool case_info(((*this)[CaseInfo] & LFN::CaseLowerBasename) != 0);
758 const auto conv([&](size_t i){
759 const auto c((*this)[i]);
760
761 return char(case_info ? std::tolower(int(c)) : c);
762 });
763 string res;
764
765 res.reserve(LFN::MaxAliasLength - 1);
766 for(size_t i(0); i < LFN::MaxAliasMainPartLength
767 && (*this)[Name + i] != ' '; ++i)
768 res += conv(Name + i);
769 if((*this)[Extension] != ' ')
770 {
771 res += '.';
772 case_info = ((*this)[CaseInfo] & LFN::CaseLowerExtension) != 0;
773 for(size_t i(0); i < LFN::MaxAliasExtensionLength
774 && (*this)[Extension + i] != ' '; ++i)
775 res += conv(Extension + i);
776 }
777 return res;
778 }
779 return {};
780 }
781
782 void
783 EntryData::SetupRoot(ClusterIndex root_cluster) ynothrow
784 {
785 Clear();
786 ClearAlias(),
787 SetDot(Name),
788 SetDirectoryAttribute();
789 WriteCluster(root_cluster);
790 }
791
792 void
793 EntryData::WriteCluster(ClusterIndex c) ynothrow
794 {
795 using ystdex::write_uint_le;
796
797 write_uint_le<16>(data() + Cluster, c),
798 write_uint_le<16>(data() + ClusterHigh, c >> 16);
799 }
800
801 void
802 EntryData::WriteAlias(const string& alias) ynothrow
803 {
804 size_t i(0), j(0);
805
806 for(; j < LFN::MaxAliasMainPartLength && alias[i] != '.'
807 && alias[i] != char(); yunseq(++i, ++j))
808 (*this)[j] = EntryDataUnit(alias[i]);
809 while(j < LFN::MaxAliasMainPartLength)
810 {
811 (*this)[j] = ' ';
812 ++j;
813 }
814 if(alias[i] == '.')
815 for(++i; alias[i] != char() && j < LFN::AliasEntryLength;
816 yunseq(++i, ++j))
817 (*this)[j] = EntryDataUnit(alias[i]);
818 for(; j < LFN::AliasEntryLength; ++j)
819 (*this)[j] = ' ';
820 }
821
822 void
823 EntryData::WriteCDateTime() ynothrow
824 {
825 using ystdex::write_uint_le;
826 const auto& date_time(FetchDateTime());
827
828 write_uint_le<16>(data() + CTime, date_time.second),
829 write_uint_le<16>(data() + CDate, date_time.first);
830 }
831
832 void
833 EntryData::WriteDateTime() ynothrow
834 {
835 using ystdex::write_uint_le;
836 using ystdex::unseq_apply;
837 const auto date_time(FetchDateTime());
838 const auto dst(data());
839
840 unseq_apply([&](size_t offset){
841 write_uint_le<16>(dst + offset, date_time.first);
842 }, CDate, MDate, ADate),
843 unseq_apply([&](size_t offset){
844 write_uint_le<16>(dst + offset, date_time.second);
845 }, CTime, MTime);
846 }
847
848 const char*
849 CheckColons(const char* path)
850 {
851 if(const auto p_col = std::strchr(Nonnull(path), ':'))
852 {
853 path = p_col + 1;
854 if(std::strchr(path, ':'))
855 ystdex::throw_error(std::errc::invalid_argument, yfsig);
856 }
857 return path;
858 }
859
860 } // namespace FAT;
861
862 } // namespace platform;
863
864 namespace platform_ex
865 {
866
867 } // namespace platform_ex;
868
ystdex::is_date_range_valid
bool is_date_range_valid(const std::tm &t)
判断 std::tm 的日期数据成员范围是否有效。
Definition: ctime.h:41
YTraceDe
#define YTraceDe(...)
YCLib 默认调试跟踪。
Definition: Debug.h:313
platform::FAT::LFN::EntryLength
Definition: YCLib/FileSystem.h:945
NPL::dst
YF_API const char src const string const char * dst
Definition: Dependency.h:95
CHRLib::PDefH
PDefH(char16_t, FetchBiCharBE, const char *c_ptr) ImplRet(yconstraint(c_ptr)
取 c_ptr 指向的大端序双字节字符。
platform::FAT::IllegalAliasCharacters
const char IllegalAliasCharacters[]
非法别名字符。
Definition: YCLib/FileSystem.h:866
v
byte v
Definition: CharRenderer.cpp:75
platform::FAT::LFN::CaseLowerExtension
WinNT 小写扩展名。
Definition: YCLib/FileSystem.h:958
platform::FAT::LFN::OffsetTable
yconstexpr const size_t OffsetTable[]
组成长文件名中的 UCS-2 字符在项中的偏移量表。
Definition: YCLib/FileSystem.h:928
platform::FAT::LFN::MaxMBCSLength
UTF-8 项最大长度。
Definition: YCLib/FileSystem.h:943
platform::containers::u16string
basic_string< char16_t > u16string
Definition: Container.h:103
platform::FAT::LFN::ValidateName
YF_API bool ValidateName(string_view) ynothrowv
检查名称是否为合法的可被 UCS-2 表示的非控制字符组成。
Definition: YCLib/FileSystem.cpp:590
ystdex::make_observer
observer_ptr< _type > make_observer(_type *p)
Definition: pointer.hpp:326
ctime.h
ISO C 日期和时间接口扩展。
platform
默认平台命名空间。
Definition: Container.h:47
platform::Deref
yconstfn auto Deref(_type &&p) -> decltype(*p)
断言并解引用非空指针。
Definition: Debug.h:451
platform::ReadLink
u16string ReadLink(const char16_t *path)
Definition: YCLib/FileSystem.cpp:286
ystdex::tolower
_tChar tolower(_tChar c)
转换大小写字符。
Definition: cctype.h:136
yfsig
#define yfsig
展开为表示函数签名的预定义变量的宏。
Definition: ydef.h:728
YSLib::Drawing::r
The boundary is out of the const Graphics const Rect const Point const Point Color c const Rect const Rect & r
Definition: YDraw.h:174
platform::CreateSymbolicLink
void CreateSymbolicLink(const char16_t *dst, const char16_t *src, bool is_dir)
Definition: YCLib/FileSystem.cpp:214
platform::FAT::LFN::MaxAliasMainPartLength
Definition: YCLib/FileSystem.h:947
platform::bool
std::FILE *fp bool
Definition: FileIO.h:1280
platform::MakePathString
MakePathString
Definition: FileIO.h:62
yimpl
#define yimpl(...)
实现标签。
Definition: ydef.h:262
FileAttributes
FileAttributes
文件共享模式。
Definition: MinGW32.h:447
YAssertNonnull
#define YAssertNonnull(_expr)
Definition: cassert.h:120
YB_UNLIKELY
#define YB_UNLIKELY(_expr)
分支预测提示。
Definition: ydef.h:464
NPL::c_str
YF_API const char src c_str())) YF_API void InstallDirectory(const string &
安装目录：复制目录树。
Definition: Dependency.h:131
ystdex::ntctslen
size_t ntctslen(const _tChar *s)
计算简单 NTCTS 长度。
Definition: cstring.h:456
yconstexpr
#define yconstexpr
指定编译时常量表达式。
Definition: ydef.h:679
platform::containers::string
basic_string< char > string
Definition: Container.h:101
CatchIgnore
#define CatchIgnore(_ex)
指定异常处理器忽略指定异常。
Definition: YBaseMacro.h:133
platform::IsDirectory
bool IsDirectory(const char16_t *path)
Definition: YCLib/FileSystem.cpp:153
YCL_CallF_Win32
#define YCL_CallF_Win32(_fn,...)
Definition: MinGW32.h:190
ystdex::data
auto data(_tRange &c) -> decltype(c.data())
Definition: range.hpp:280
YCL_RaiseZ_SysE
#define YCL_RaiseZ_SysE(_t, _expr, _msg, _sig)
Definition: FileIO.h:1188
ystdex::cpp2011::end
_type * end(_type(&&array)[_vN])
Definition: range.hpp:54
yunseq
#define yunseq
无序列依赖表达式组求值。
Definition: ydef.h:1022
ystdex::is_time_no_leap_valid
bool is_time_no_leap_valid(const std::tm &t)
判断 std::tm 的时间数据成员范围是否有效。
Definition: ctime.h:48
platform::FAT::LFN::WriteNumericTail
YF_API void WriteNumericTail(string &, size_t) ynothrowv
在指定字符串写前缀字符 '~' 和整数的后缀以作为短文件名。
Definition: YCLib/FileSystem.cpp:600
ystdex::wstring_view
basic_string_view< wchar_t > wstring_view
Definition: string_view.hpp:418
YCL_Impl_lstatn
#define YCL_Impl_lstatn
ystdex::throw_error
void throw_error(std::error_code ec, _tParams &&...args)
抛出错误： std::system_error 或允许相同构造函数参数的异常。
Definition: ystdex/exception.h:206
platform::DirectorySession::Deleter::pointer
NativeHandle pointer
Definition: YCLib/FileSystem.h:488
YAssert
#define YAssert(_expr, _msg)
Definition: cassert.h:111
platform::FAT::LFN::Ordinal
长文件名序数。
Definition: YCLib/FileSystem.h:903
CatchExpr
#define CatchExpr(_ex,...)
指定表达式作为异常处理器。
Definition: YBaseMacro.h:129
CHRLib::YB_NONNULL
YB_NONNULL(1) PDefH(ConversionResult
转换指定编码的多字节字符串为指定类型的 UCS-2 字符串。
platform::FAT::LFN::CaseLowerBasename
WinNT 小写文件名。
Definition: YCLib/FileSystem.h:956
ynothrow
#define ynothrow
YSLib 无异常抛出保证：若支持 noexcept 关键字， 指定特定的 noexcept 异常规范。
Definition: ydef.h:766
CHRLib::s
_tDst const _tSrc * s
Definition: CharacterProcessing.h:140
YCL_DeclW32Call
#define YCL_DeclW32Call(_fn, _module, _tRet,...)
声明调用 WinAPI 的例程。
Definition: MinGW32.h:254
CHRLib::ImplRet
const char *c_ptr ImplRet(yconstraint(c_ptr), char16_t((c_ptr[1]<< CHAR_BIT)|*c_ptr)) enum class ConversionResult
编码转换结果。
Definition: CharacterMapping.h:84
platform::FAT::LFN::ConvertToMBCS
string ConvertToMBCS(const char16_t *path)
Definition: YCLib/FileSystem.cpp:568
YSLib::i
return i
Definition: YCoreUtilities.h:176
ystdex::restrict_length
_tString && restrict_length(_tString &&str, const typename string_traits< _tString >::size_type n)
限制长度不超过指定参数。
Definition: string.hpp:510
platform_ex::Windows::Invalid
Definition: MinGW32.h:471
platform::FAT::LFN::AliasEntryLength
Definition: YCLib/FileSystem.h:946
YModules.h
CHRLib::MakeUCS2LE
_tDst MakeUCS2LE(basic_string_view< char > sv, _tParams &&...args)
Definition: CharacterProcessing.h:538
YModules.h
platform::DirectorySession
目录会话：表示打开的目录。
Definition: YCLib/FileSystem.h:468
platform::FAT::FetchDateTime
YF_API pair< Timestamp, Timestamp > FetchDateTime() ynothrow
取以实时时钟的文件日期和时间。
Definition: YCLib/FileSystem.cpp:635
yunused
#define yunused(...)
标记未使用的表达式。
Definition: ydef.h:968
platform::FAT::LFN::MaxNumericTail
Definition: YCLib/FileSystem.h:950
ystdex::state_guard
使用临时状态暂存对象的作用域守护。
Definition: scope_guard.hpp:273
ynothrowv
#define ynothrowv
YSLib 无异常抛出保证验证：有条件地使用无异常抛出规范。
Definition: ydef.h:786
ystdex::rtrim
_tString && rtrim(_tString &&str, typename string_traits< _tString >::value_type c)
删除字符串中指定的连续后缀字符。
Definition: string.hpp:676
CHRLib::e
const char const char * e
Definition: CharacterProcessing.h:109
platform::FAT::Timestamp
std::uint16_t Timestamp
时间戳：表示日期和时间的整数类型。
Definition: YCLib/FileSystem.h:880
platform::FAT::CheckColons
const char * CheckColons(const char *path)
Definition: YCLib/FileSystem.cpp:849
CHRLib
Definition: CharacterDefinition.h:38
platform::Nonnull
_type && Nonnull(_type &&p) ynothrowv
断言并返回非空参数。
Definition: Debug.h:422
platform::FAT::LFN::ConvertToAlias
YF_API tuple< string, string, bool > ConvertToAlias(const u16string &)
转换长文件名为别名。
Definition: YCLib/FileSystem.cpp:509
platform::FAT::LFN::MaxLength
UCS-2 项最大长度。
Definition: YCLib/FileSystem.h:937
platform::Descriptions::Warning
Definition: YCommon.h:167
scope_guard.hpp
作用域守护。
platform::HDirectory::NativeChar
char NativeChar
Definition: YCLib/FileSystem.h:576
platform_ex
平台扩展命名空间。
Definition: Android.h:45
YSLib::UI::name
yconstfn const string & name
Definition: Loader.h:130
YSLib::Text::str
const char const char * str
Definition: TextFile.h:81
platform::FAT::ClusterIndex
std::uint32_t ClusterIndex
Definition: YCLib/FileSystem.h:695
platform::KeyCodes::Win32::Empty
Definition: Keys.h:279
CHRLib::c
c
Definition: CharacterProcessing.h:81
yforward
#define yforward(_expr)
根据参数类型使用 std::forward 传递对应参数。
Definition: ydef.h:995
platform::IterateLink
YF_API bool IterateLink(string &, size_t &)
迭代访问链接：替换非空路径并按需减少允许链接计数。
Definition: YCLib/FileSystem.cpp:303
ystdex::retry_on_cond
result_of_t< _fCallable &&(_tParams &&...)> retry_on_cond(_fCond cond, _fCallable &&f, _tParams &&...args)
循环重复调用：代替直接使用 do-while 语句以避免过多引入作用域外的变量。
Definition: functional.hpp:1152
CHRLib::char16_t
char16_t((*c_ptr<< CHAR_BIT)|c_ptr[1])) inline PDefH(char16_t
取 c_ptr 指向的小端序双字节字符。
platform::CreateHardLink
void CreateHardLink(const char16_t *dst, const char16_t *src)
Definition: YCLib/FileSystem.cpp:183
platform::FAT::LFN::MaxAliasLength
Definition: YCLib/FileSystem.h:949
ystdex::write_uint_le
void write_uint_le(byte *buf, typename make_width_int< _vWidth >::unsigned_type val)
向字节缓冲区写入指定宽的小端序无符号整数。
Definition: cstdint.hpp:352
platform::CategorizeNode
enum NodeCategory std::uint_least32_t YF_API NodeCategory CategorizeNode(mode_t) ynothrow
文件节点类别。
Definition: FileIO.cpp:388
ystdex::cpp2011::begin
_type * begin(_type(&&array)[_vN])
Definition: range.hpp:46
ystdex::unseq_apply
void unseq_apply(_func &&f, _tParams &&...args)
无序调用。
Definition: functional.hpp:240
CHRLib::d
_tDst * d
Definition: CharacterProcessing.h:139
ystdex::ntctsicmp
int ntctsicmp(const _tChar *s1, const _tChar *s2)
按字典序比较简单 NTCTS （忽略大小写）。
Definition: cstring.h:213
platform::FAT::ConvertFileTime
YF_API std::time_t ConvertFileTime(Timestamp, Timestamp) ynothrow
转换日期和时间的时间戳为标准库时间类型。
Definition: YCLib/FileSystem.cpp:618
platform::FAT::EntryDataUnit
std::uint8_t EntryDataUnit
Definition: YCLib/FileSystem.h:694
YCL_CallF_CAPI
#define YCL_CallF_CAPI(_t, _fn,...)
Definition: FileIO.h:1237
platform::FAT::LFN::GenerateAliasChecksum
EntryDataUnit GenerateAliasChecksum(const EntryDataUnit *p) ynothrowv
Definition: YCLib/FileSystem.cpp:576
CHRLib::size_t
return size_t(d-p)
platform::FAT::LFN::MaxAliasExtensionLength
Definition: YCLib/FileSystem.h:948
platform_ex::MakePathStringU
MakePathStringU
Definition: FileIO.h:1443
ystdex::ref
lref< _type > ref(_type &t)
构造引用包装。
Definition: ref.hpp:132
YSLib::path
YF_API const char * path
Definition: Initialization.h:77
ystdex::string_view
basic_string_view< char > string_view
Definition: string_view.hpp:415
YCL_TraceCallF_CAPI
#define YCL_TraceCallF_CAPI(_fn,...)
Definition: FileIO.h:1258
CHRLib::p
const auto p(d)
platform_ex::Windows::Directory
Definition: MinGW32.h:452