• R/O
  • SSH

YSLib: Commit

The YSLib project - main repository


Commit MetaInfo

Revision3da33dd96b6dca452f72b6f96f32aeb11f05a34b (tree)
Time2020-10-25 12:06:55
AuthorFrankHB <frankhb1989@gmai...>
CommiterFrankHB

Log Message

更新主分支版本: build 901 rev 10 。

Change Summary

    Incremental Difference

    diff -r db41e0c9c6e8 -r 3da33dd96b6d Tools/SHBuild/Main.cpp
    --- a/Tools/SHBuild/Main.cpp Tue Oct 13 02:01:49 2020 +0800
    +++ b/Tools/SHBuild/Main.cpp Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file Main.cpp
    1212 \ingroup MaintenanceTools
    1313 \brief 宿主构建工具:递归查找源文件并编译和静态链接。
    14-\version r3963
    14+\version r3984
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 473
    1717 \par 创建时间:
    1818 2014-02-06 14:33:55 +0800
    1919 \par 修改时间:
    20- 2020-09-06 15:03 +0800
    20+ 2020-10-21 04:45 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -34,7 +34,6 @@
    3434 // std::lock_guard, function, vector, std::initializer_list, to_std_string,
    3535 // string_view, uspawn, ystdex::sfmt, istringstream;
    3636 #include YFM_YCLib_Host // for namespace platform_ex, platform_ex::Terminal,
    37-// platform_ex::EncodeArg, platform_ex::DecodeArg,
    3837 // platform_ex::SetEnvironmentVariable;
    3938 #include YFM_YSLib_Service_YTimer // for namespace std::chrono,
    4039 // YSLib::Timers::FetchElapsed;
    @@ -44,8 +43,12 @@
    4443 #include YFM_NPL_Dependency // for NPL::DepsEventType, NPL, A1, Forms,
    4544 // NPL::DecomposeMakefileDepList, NPL::FilterMakefileDependencies,
    4645 // NPL::Install*;
    46+#include <iostream> // for std::cout;
    4747 #include YFM_YSLib_Core_YConsole // for YSLib::Consoles;
    4848 #include <ystdex/concurrency.h> // for ystdex::task_pool;
    49+#if YCL_Win32
    50+# include YFM_Win32_YCLib_MinGW32 // for platform_ex::ParseCommandArguments;
    51+#endif
    4952 #include <sstream> // for complete istringstream;
    5053
    5154 //! \since build 837
    @@ -373,6 +376,7 @@
    373376 // necessary at least in stage 1.
    374377 context.Root.Trace.FilterLevel = Logger::Level::Informative;
    375378 LoadStandardContext(context);
    379+ context.OutputStreamPtr = NPL::make_observer(&std::cout);
    376380
    377381 auto& rctx(context.Root);
    378382
    @@ -916,20 +920,31 @@
    916920 Terminal::Guard
    917921 guard(term_ref, std::bind(UpdateForeColorByLevel, _1, lv));
    918922
    919- std::fprintf(stream, "%s", &EncodeArg(Nonnull(str))[0]);
    923+ std::fprintf(stream, "%s", &Nonnull(str)[0]);
    920924 }
    921925 std::fputc('\n', stream);
    922926 });
    923- if(argc > 1)
    927+
    928+#if YCL_Win32
    929+ yunused(argc), yunused(argv);
    930+
    931+ auto xargv(platform_ex::ParseCommandArguments());
    932+ const auto xargc(xargv.size());
    933+#else
    934+ const auto xargc(static_cast<size_t>(argc));
    935+ const auto xargv(argv);
    936+#endif
    937+
    938+ if(xargc > 1)
    924939 {
    925940 vector<string> args;
    926941 bool opt_trans(true);
    927942
    928- for(int i(1); i < argc; ++i)
    943+ for(size_t i(1); i < xargc; ++i)
    929944 {
    930- string arg(DecodeArg(argv[i]));
    945+ string arg(xargv[i]);
    931946
    932- if(opt_trans && string(argv[i]) == "--")
    947+ if(opt_trans && string(xargv[i]) == "--")
    933948 opt_trans = {};
    934949 else if(!opt_trans || (!arg.empty()
    935950 && std::none_of(begin(OptionsTable), end(OptionsTable),
    @@ -1036,10 +1051,10 @@
    10361051 bctx.Build();
    10371052 }
    10381053 }
    1039- else if(argc == 1)
    1054+ else if(xargc == 1)
    10401055 {
    10411056 std::printf("%s%s%s", "Usage: [ENV ...] ",
    1042- quote(string(*argv)).c_str(),
    1057+ quote(string(xargv[0])).c_str(),
    10431058 " SRCPATH [OPTIONS ... [-- ARGS...]]\n"
    10441059 "\tThis program is a tool to build the source tree, with some"
    10451060 " additional functionalities. It has two execution mode,"
    diff -r db41e0c9c6e8 -r 3da33dd96b6d Tools/Scripts/SHBuild-BuildApp.txt
    --- a/Tools/Scripts/SHBuild-BuildApp.txt Tue Oct 13 02:01:49 2020 +0800
    +++ b/Tools/Scripts/SHBuild-BuildApp.txt Sun Oct 25 11:06:55 2020 +0800
    @@ -8,7 +8,7 @@
    88 SHBuild_DirectoryOf_ SHBuild_RaiseError_ SHBuild_TrimOptions_;
    99
    1010 $def! (prog .args) () cmd-get-args;
    11-$def! SR_Var_NPLA1_ SHBuild_DirectoryOf_ prog;
    11+$def! SR_Share_NPLA1_ SHBuild_DirectoryOf_ prog;
    1212
    1313 $def! env_SHBuild_Common_ $provide! (
    1414 $assert-nonempty
    @@ -28,7 +28,7 @@
    2828 system-or-puts
    2929 system-check
    3030 win32?
    31-) load (++ SR_Var_NPLA1_ "/SHBuild-YSLib-common.txt");
    31+) load (++ SR_Share_NPLA1_ "/SHBuild-YSLib-common.txt");
    3232
    3333 "TODO", "Merge more with logic in %SHBuild-YSLib-build.txt.";
    3434 $def! (env-os env-arch) () SHBuild_GetPlatformStrings;
    diff -r db41e0c9c6e8 -r 3da33dd96b6d Tools/Scripts/SHBuild-YSLib-build.txt
    --- a/Tools/Scripts/SHBuild-YSLib-build.txt Tue Oct 13 02:01:49 2020 +0800
    +++ b/Tools/Scripts/SHBuild-YSLib-build.txt Sun Oct 25 11:06:55 2020 +0800
    @@ -69,6 +69,7 @@
    6969 $def! SR_Include_ ++ SR_Prefix_ "/include";
    7070 $def! SR_Lib_ ++ SR_Prefix_ "/lib";
    7171 $def! SR_Share_ ++ SR_Prefix_ "/share";
    72+$def! SR_Share_NPLA1_ ++ SR_Prefix_ "/share/NPLA1";
    7273 $def! SR_Var_NPLA1_ ++ SR_Prefix_ "/var/NPLA1";
    7374 $def! SR_SHBuild_ ++ SHBuild_SysRoot "/.shbuild";
    7475
    @@ -224,8 +225,8 @@
    224225 $defl! SHB_InstLibD (build-dir name)
    225226 InstLibD build-dir name SR_Lib_ SR_DSO_Dest SR_DSO_Imp;
    226227
    227- for-each-ltr SHBuild_EnsureDirectory_
    228- (list SR_Bin_ SR_Include_ SR_Lib_ SR_Share_ SR_Var_NPLA1_);
    228+ for-each-ltr SHBuild_EnsureDirectory_ (list SR_Bin_ SR_Include_ SR_Lib_
    229+ SR_Share_ SR_Share_NPLA1_ SR_Var_NPLA1_);
    229230
    230231 $if (safeenv-empty? "SHBuild_No3rd")
    231232 (
    @@ -321,7 +322,7 @@
    321322 $assert-absolute-path SHBuild_ToolDir;
    322323 $import! env_SHB_ InstHardLinkExe InstNPLA1Module_ InstTool InstSHBuild;
    323324 $defl! SHB_InstNPLA1Module_ (name)
    324- InstNPLA1Module_ SHBuild_ToolDir SR_Var_NPLA1_ name;
    325+ InstNPLA1Module_ SHBuild_ToolDir SR_Share_NPLA1_ name;
    325326 $defl! SHB_InstTool (name)
    326327 InstTool SHBuild_ToolDir SR_Bin_ name;
    327328 InstHardLinkExe SR_SHBuild_ SR_Bin_ "SHBuild";
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YBase/include/ystdex/ios.hpp
    --- a/YBase/include/ystdex/ios.hpp Tue Oct 13 02:01:49 2020 +0800
    +++ b/YBase/include/ystdex/ios.hpp Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file ios.hpp
    1212 \ingroup YStandardEx
    1313 \brief ISO C++ 标准库输入/输出流基类扩展。
    14-\version r80
    14+\version r86
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 663
    1717 \par 创建时间:
    1818 2015-12-28 20:00:09 +0800
    1919 \par 修改时间:
    20- 2020-02-25 20:48 +0800
    20+ 2020-10-25 05:27 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -41,7 +41,8 @@
    4141 */
    4242 template<typename _tChar, class _tTraits>
    4343 void
    44-setstate(std::basic_ios<_tChar, _tTraits>& ios, std::ios_base::iostate state) ynothrow
    44+setstate(std::basic_ios<_tChar, _tTraits>& ios, std::ios_base::iostate state)
    45+ ynothrow
    4546 {
    4647 const auto except(ios.exceptions());
    4748
    @@ -51,7 +52,7 @@
    5152 }
    5253
    5354 /*!
    54-\brief 设置流状态并重新抛出当前异常。
    55+\brief 设置流状态,并在必要时重新抛出当前异常。
    5556 \note 一个主要用例为实现标准库要求的格式/非格式输入/输出函数。
    5657 \see WG21 N4567 27.7.2.2.1[istream.formatted.reqmts] 。
    5758 \see WG21 N4567 27.7.2.3[istream.unformatted]/1 。
    @@ -60,12 +61,13 @@
    6061 \see LWG 91 。
    6162 */
    6263 template<typename _tChar, class _tTraits>
    63-YB_NORETURN void
    64+void
    6465 rethrow_badstate(std::basic_ios<_tChar, _tTraits>& ios,
    6566 std::ios_base::iostate state = std::ios_base::badbit)
    6667 {
    6768 setstate(ios, state);
    68- throw;
    69+ if(ios.exceptions() & state)
    70+ throw;
    6971 }
    7072
    7173 } // namespace ystdex;
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YBase/include/ystdex/path.hpp
    --- a/YBase/include/ystdex/path.hpp Tue Oct 13 02:01:49 2020 +0800
    +++ b/YBase/include/ystdex/path.hpp Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file path.hpp
    1212 \ingroup YStandardEx
    1313 \brief 抽象路径模板。
    14-\version r1531
    14+\version r1616
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 408
    1717 \par 创建时间:
    1818 2013-05-27 02:42:19 +0800
    1919 \par 修改时间:
    20- 2020-01-27 03:08 +0800
    20+ 2020-10-16 21:19 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -87,7 +87,14 @@
    8787 struct path_traits<void>
    8888 {
    8989 template<typename _tString>
    90- YB_PURE static yconstfn bool
    90+ YB_ATTR_nodiscard static yconstfn bool
    91+ is_absolute(const _tString& x) ynothrow
    92+ {
    93+ return ystdex::string_empty(x);
    94+ }
    95+
    96+ template<typename _tString>
    97+ YB_ATTR_nodiscard YB_PURE static yconstfn bool
    9198 is_parent(const _tString& str) ynothrow
    9299 {
    93100 using char_value_t = decltype(str[0]);
    @@ -97,14 +104,7 @@
    97104 }
    98105
    99106 template<typename _tString>
    100- static yconstfn bool
    101- is_absolute(const _tString& x) ynothrow
    102- {
    103- return ystdex::string_empty(x);
    104- }
    105-
    106- template<typename _tString>
    107- YB_PURE static yconstfn bool
    107+ YB_ATTR_nodiscard YB_PURE static yconstfn bool
    108108 is_self(const _tString& str) ynothrow
    109109 {
    110110 return
    @@ -112,21 +112,21 @@
    112112 }
    113113
    114114 template<typename _tString>
    115- YB_PURE static yconstfn bool
    115+ YB_ATTR_nodiscard YB_PURE static yconstfn bool
    116116 has_root_name(const _tString& x) ynothrow
    117117 {
    118118 return ystdex::string_empty(x);
    119119 }
    120120
    121121 template<typename _tString>
    122- YB_PURE static yconstfn bool
    122+ YB_ATTR_nodiscard YB_PURE static yconstfn bool
    123123 has_root_path(const _tString& x) ynothrow
    124124 {
    125125 return ystdex::string_empty(x);
    126126 }
    127127
    128128 template<typename _tString1, typename _tString2>
    129- YB_PURE static yconstfn bool
    129+ YB_ATTR_nodiscard YB_PURE static yconstfn bool
    130130 have_same_root_names(const _tString1& x, const _tString2& y) ynothrow
    131131 {
    132132 return x == y;
    @@ -134,59 +134,96 @@
    134134 };
    135135
    136136 /*!
    137+\brief 文件字符串视图路径特征。
    138+\since build 901
    139+*/
    140+template<typename _tChar, class _tTraits>
    141+struct path_traits<basic_string_view<_tChar, _tTraits>>
    142+{
    143+ using char_type = _tChar;
    144+ using value_type = basic_string_view<_tChar, _tTraits>;
    145+ using view_type = value_type;
    146+
    147+ YB_ATTR_nodiscard YB_PURE static yconstfn bool
    148+ is_absolute(view_type sv) ynothrow
    149+ {
    150+ return sv.empty();
    151+ }
    152+
    153+ YB_ATTR_nodiscard YB_PURE static yconstfn bool
    154+ is_parent(view_type sv) ynothrow
    155+ {
    156+ return sv.length() == 2 && sv[0] == '.' && sv[1] == '.';
    157+ }
    158+
    159+ YB_ATTR_nodiscard YB_PURE static yconstfn bool
    160+ is_self(view_type sv) ynothrow
    161+ {
    162+ return sv.length() == 1 && sv.front() == '.';
    163+ }
    164+
    165+ YB_ATTR_nodiscard YB_PURE static yconstfn bool
    166+ has_root_name(view_type sv) ynothrow
    167+ {
    168+ return sv.empty();
    169+ }
    170+
    171+ YB_ATTR_nodiscard YB_PURE static yconstfn bool
    172+ has_root_path(view_type sv) ynothrow
    173+ {
    174+ return sv.empty();
    175+ }
    176+
    177+ YB_ATTR_nodiscard YB_PURE static yconstfn bool
    178+ have_same_root_names(view_type x, view_type y) ynothrow
    179+ {
    180+ return x == y;
    181+ }
    182+};
    183+
    184+/*!
    137185 \brief 文件字符串路径特征。
    138186 \since build 861
    139187 */
    140188 template<typename _tChar, class _tTraits, class _tAlloc>
    141189 struct path_traits<basic_string<_tChar, _tTraits, _tAlloc>>
    190+ : private path_traits<basic_string_view<_tChar, _tTraits>>
    142191 {
    143192 using char_type = _tChar;
    144193 using value_type = basic_string<_tChar, _tTraits, _tAlloc>;
    145194 using view_type = basic_string_view<char_type>;
    146195
    147- static yconstfn bool
    148- is_parent(view_type sv) ynothrow
    149- {
    150- return sv.length() == 2 && sv[0] == '.' && sv[1] == '.';
    151- }
    196+ using path_traits<view_type>::is_parent;
    152197
    153198 //! \since build 836
    154199 //@{
    155- static yconstfn bool
    156- is_absolute(view_type sv) ynothrow
    157- {
    158- return sv.empty();
    159- }
    160-
    161- static yconstfn bool
    162- is_self(view_type sv) ynothrow
    163- {
    164- return sv.length() == 1 && sv.front() == '.';
    165- }
    200+ using path_traits<view_type>::is_absolute;
    166201
    167- static yconstfn bool
    168- has_root_name(view_type sv) ynothrow
    169- {
    170- return sv.empty();
    171- }
    202+ using path_traits<view_type>::is_self;
    172203
    173- static yconstfn bool
    174- has_root_path(view_type sv) ynothrow
    175- {
    176- return sv.empty();
    177- }
    204+ using path_traits<view_type>::has_root_name;
    178205
    179- static yconstfn bool
    180- have_same_root_names(view_type x, view_type y) ynothrow
    181- {
    182- return x == y;
    183- }
    206+ using path_traits<view_type>::has_root_path;
    207+
    208+ using path_traits<view_type>::have_same_root_names;
    184209 //@}
    185210 };
    186211 //@}
    187212
    188213
    189214 /*!
    215+\brief 判断参数是表示当前或上级目录名称的字符串。
    216+\since build 901
    217+*/
    218+template<typename _type>
    219+YB_ATTR_nodiscard yconstfn YB_PURE bool
    220+is_parent_or_self(const _type& s) ynothrow
    221+{
    222+ return path_traits<_type>::is_parent(s) || path_traits<_type>::is_self(s);
    223+}
    224+
    225+
    226+/*!
    190227 \brief 路径类别。
    191228 \since build 543
    192229 */
    @@ -255,7 +292,7 @@
    255292 参见 operator/= 的说明。
    256293 非解析的追加操作使用 push_back 等容器对元素的通用操作替代。
    257294 不单独设置对元素自身的操作如 operator+= ;可使用对元素的直接操作替代。
    258- 首个元素表示根路径(若存在),内部表示不区分根路径中的根名称和根目录。
    295+ 第一个元素表示根路径(若存在),内部表示不区分根路径中的根名称和根目录。
    259296 需要区分时通过特征操作,
    260297 参见具有 const path& 参数类型的 operator/= 重载的说明。
    261298 */
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YBase/include/ystdex/string.hpp
    --- a/YBase/include/ystdex/string.hpp Tue Oct 13 02:01:49 2020 +0800
    +++ b/YBase/include/ystdex/string.hpp Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file string.hpp
    1212 \ingroup YStandardEx
    1313 \brief ISO C++ 标准字符串扩展。
    14-\version r3071
    14+\version r3120
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 304
    1717 \par 创建时间:
    1818 2012-04-26 20:12:19 +0800
    1919 \par 修改时间:
    20- 2020-06-14 12:03 +0800
    20+ 2020-10-25 07:06 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -34,10 +34,11 @@
    3434 #include "string_view.hpp" // for internal "string_view.hpp" (implying
    3535 // "range.hpp" and "<libdefect/string.h>"), basic_string_view,
    3636 // std::char_traits, std::initializer_list, std::to_string, string,
    37-// basic_string, ntctslen;
    37+// basic_string;
    3838 #include "container.hpp" // for "container.hpp", enable_for_input_iterator_t,
    3939 // make_index_sequence, index_sequence, begin, end, size, sort_unique,
    4040 // underlying, std::hash;
    41+#include "cstring.h" // for ystdex::ntctslen;
    4142 #include "cstdio.h" // for vfmtlen, ystdex::is_null;
    4243 #include "array.hpp" // for std::bidirectional_iterator_tag, to_array;
    4344 #include <istream> // for std::basic_istream;
    @@ -1628,7 +1629,7 @@
    16281629 \since build 565
    16291630 */
    16301631 //@{
    1631-//! \note 同 std::getline ,除判断分隔符及附带的副作用由参数的函数对象决定。
    1632+//! \note 同 std::getline ,除判断分隔符及附带的副作用由参数中的函数对象决定。
    16321633 template<typename _tChar, class _tTraits, class _tAlloc, typename _func>
    16331634 std::basic_istream<_tChar, _tTraits>&
    16341635 extract(std::basic_istream<_tChar, _tTraits>& is,
    @@ -1638,8 +1639,8 @@
    16381639 // NOTE: The type shall be explicit.
    16391640 std::ios_base::iostate st(std::ios_base::goodbit);
    16401641
    1641- if(const auto k
    1642- = typename std::basic_istream<_tChar, _tTraits>::sentry(is, true))
    1642+ if(const auto
    1643+ k{typename std::basic_istream<_tChar, _tTraits>::sentry(is, true)})
    16431644 {
    16441645 const auto msize(str.max_size());
    16451646 const auto p_buf(is.rdbuf());
    @@ -1751,16 +1752,38 @@
    17511752 //@}
    17521753
    17531754
    1754-/*!
    1755-\brief 非格式输出。
    1756-\since build 599
    1757-*/
    1755+//! \brief 无格式字符串输出。
    17581756 //@{
    1759-template<typename _tChar, class _tString>
    1757+//! \since build 901
    1758+//@{
    1759+template<typename _tChar, typename _tString>
    17601760 std::basic_ostream<_tChar, typename _tString::traits_type>&
    17611761 write(std::basic_ostream<_tChar, typename _tString::traits_type>& os,
    1762- const _tString& str, typename _tString::size_type pos = 0,
    1763- typename _tString::size_type n = _tString::npos)
    1762+ const _tString& str)
    1763+{
    1764+ const auto len(str.length());
    1765+
    1766+ if(len != 0)
    1767+ // XXX: Here %str is not necessarily of a class type.
    1768+ os.write(&str[0], std::streamsize(len));
    1769+ return os;
    1770+}
    1771+template<typename _tChar, typename _tString>
    1772+std::basic_ostream<_tChar, typename _tString::traits_type>&
    1773+write(std::basic_ostream<_tChar, typename _tString::traits_type>& os,
    1774+ const _tString& str, typename _tString::size_type pos)
    1775+{
    1776+ const auto len(str.length());
    1777+
    1778+ if(pos < len)
    1779+ os.write(&str[pos], std::streamsize(len - pos));
    1780+ return os;
    1781+}
    1782+template<typename _tChar, typename _tString>
    1783+std::basic_ostream<_tChar, typename _tString::traits_type>&
    1784+write(std::basic_ostream<_tChar, typename _tString::traits_type>& os,
    1785+ const _tString& str, typename _tString::size_type pos,
    1786+ typename _tString::size_type n)
    17641787 {
    17651788 const auto len(str.length());
    17661789
    @@ -1768,13 +1791,7 @@
    17681791 os.write(&str[pos], std::streamsize(std::min(n, len - pos)));
    17691792 return os;
    17701793 }
    1771-//! \since build 619
    1772-template<typename _tChar, class _tTraits, size_t _vN>
    1773-std::basic_ostream<_tChar, _tTraits>&
    1774-write(std::basic_ostream<_tChar, _tTraits>& os, const _tChar(&s)[_vN])
    1775-{
    1776- return os.write(std::addressof(s[0]), std::streamsize(size(s)));
    1777-}
    1794+//@}
    17781795
    17791796 /*!
    17801797 \note 参数数组作为字符串字面量。
    @@ -1789,6 +1806,18 @@
    17891806 return
    17901807 os.write(std::addressof(s[0]), std::streamsize(size(s) - 1));
    17911808 }
    1809+
    1810+/*!
    1811+\note 参数作为 NTCTS 输出。
    1812+\pre 间接断言:第二参数非空。
    1813+\since build 901
    1814+*/
    1815+template<typename _tChar, class _tTraits>
    1816+YB_NONNULL(2) std::basic_ostream<_tChar, _tTraits>&
    1817+write_ntcts(std::basic_ostream<_tChar, _tTraits>& os, const _tChar* s)
    1818+{
    1819+ return os.write(s, std::streamsize(ystdex::ntctslen(s)));
    1820+}
    17921821 //@}
    17931822
    17941823 //! \since build 833
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YBase/include/ystdex/type_op.hpp
    --- a/YBase/include/ystdex/type_op.hpp Tue Oct 13 02:01:49 2020 +0800
    +++ b/YBase/include/ystdex/type_op.hpp Sun Oct 25 11:06:55 2020 +0800
    @@ -1,5 +1,5 @@
    11 /*
    2- © 2011-2016, 2018-2019 FrankHB.
    2+ © 2011-2016, 2018-2020 FrankHB.
    33
    44 This file is part of the YSLib project, and may only be used,
    55 modified, and distributed under the terms of the YSLib project
    @@ -11,13 +11,13 @@
    1111 /*! \file type_op.hpp
    1212 \ingroup YStandardEx
    1313 \brief C++ 类型操作。
    14-\version r2893
    14+\version r2898
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 201
    1717 \par 创建时间:
    1818 2011-04-14 08:54:25 +0800
    1919 \par 修改时间:
    20- 2019-09-13 13:22 +0800
    20+ 2020-10-15 03:01 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -113,7 +113,10 @@
    113113
    114114 #ifdef YB_IMPL_GNUCPP
    115115 # pragma GCC diagnostic push
    116-# pragma GCC diagnostic ignored "-Wextra"
    116+# if YB_IMPL_GNUCPP >= 100000
    117+// NOTE: See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90449.
    118+# pragma GCC diagnostic ignored "-Winaccessible-base"
    119+# endif
    117120 #endif
    118121
    119122 struct C : A, B
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/Android/source/YCLib/Android.cpp
    --- a/YFramework/Android/source/YCLib/Android.cpp Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/Android/source/YCLib/Android.cpp Sun Oct 25 11:06:55 2020 +0800
    @@ -1,5 +1,5 @@
    11 /*
    2- © 2014-2016 FrankHB.
    2+ © 2014-2016, 2020 FrankHB.
    33
    44 This file is part of the YSLib project, and may only be used,
    55 modified, and distributed under the terms of the YSLib project
    @@ -12,13 +12,13 @@
    1212 \ingroup YCLib
    1313 \ingroup Android
    1414 \brief YCLib Android 平台公共扩展。
    15-\version r564
    15+\version r567
    1616 \author FrankHB <frankhb1989@gmail.com>
    1717 \since build 492
    1818 \par 创建时间:
    1919 2014-04-09 18:30:24 +0800
    2020 \par 修改时间:
    21- 2016-06-19 19:27 +0800
    21+ 2020-10-14 05:53 +0800
    2222 \par 文本编码:
    2323 UTF-8
    2424 \par 模块名称:
    @@ -45,7 +45,7 @@
    4545
    4646 #if YCL_Android
    4747
    48-namespace Android
    48+inline namespace Android
    4949 {
    5050
    5151 InputQueue::InputQueue(::ALooper& looper, ::AInputQueue& input_queue)
    @@ -116,7 +116,7 @@
    116116 int(::AConfiguration_getUiModeNight(&cfg)));
    117117 }
    118118
    119-} // namespace Android;
    119+} // inline namespace Android;
    120120
    121121 #endif
    122122
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/DS/include/YCLib/DSIO.h
    --- a/YFramework/DS/include/YCLib/DSIO.h Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/DS/include/YCLib/DSIO.h Sun Oct 25 11:06:55 2020 +0800
    @@ -12,13 +12,13 @@
    1212 \ingroup YCLib
    1313 \ingroup DS
    1414 \brief DS 底层输入输出接口。
    15-\version r1607
    15+\version r1609
    1616 \author FrankHB <frankhb1989@gmail.com>
    1717 \since build 604
    1818 \par 创建时间:
    1919 2015-06-06 03:01:27 +0800
    2020 \par 修改时间:
    21- 2020-10-08 18:51 +0800
    21+ 2020-10-16 21:07 +0800
    2222 \par 文本编码:
    2323 UTF-8
    2424 \par 模块名称:
    @@ -43,6 +43,7 @@
    4343 # include <sys/syslimits.h> // for NAME_MAX.
    4444 # include <ystdex/function.hpp> // for ystdex::function;
    4545 # include <ystdex/optional.h> // for ystdex::ref_opt;
    46+# include <ystdex/path.hpp> // for ystdex::is_parent_or_self;
    4647 # include <bitset> // for std::bitset;
    4748 #endif
    4849
    @@ -620,7 +621,7 @@
    620621 DefDeMoveAssignment(DEntry)
    621622 //@}
    622623
    623- DefPred(const ynothrow, Dot, name == "." || name == "..")
    624+ DefPred(const ynothrow, Dot, ystdex::is_parent_or_self(name))
    624625 /*!
    625626 \brief 判断分区类型是 FAT32 且 Data 表示根目录。
    626627 \exception std::system_error 调用失败。
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/DS/source/YCLib/DSIO.cpp
    --- a/YFramework/DS/source/YCLib/DSIO.cpp Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/DS/source/YCLib/DSIO.cpp Sun Oct 25 11:06:55 2020 +0800
    @@ -12,13 +12,13 @@
    1212 \ingroup YCLib
    1313 \ingroup DS
    1414 \brief DS 底层输入输出接口。
    15-\version r4893
    15+\version r4902
    1616 \author FrankHB <frankhb1989@gmail.com>
    1717 \since build 604
    1818 \par 创建时间:
    1919 2015-06-06 06:25:00 +0800
    2020 \par 修改时间:
    21- 2020-10-11 17:41 +0800
    21+ 2020-10-16 21:23 +0800
    2222 \par 文本编码:
    2323 UTF-8
    2424 \par 模块名称:
    @@ -29,10 +29,11 @@
    2929 #include "YCLib/YModules.h"
    3030 #include YFM_DS_YCLib_DSIO // for platform::Descriptions, std::runtime_error,
    3131 // ystdex::throw_error, std::system_error, string, string_view,
    32-// ystdex::retry_on_cond, YAssertNonnull, FetchSeparator, ystdex::id,
    33-// std::bind, std::placeholder::_1, ystdex::exchange, ystdex::invoke, std::ref,
    34-// YTraceDe, std::exception, ystdex::trivially_copy_n, ptrdiff_t,
    35-// DISC_INTERFACE, unique_raw, ystdex::aligned_store_cast;
    32+// ystdex::exchange, ystdex::retry_on_cond, YAssertNonnull, FetchSeparator,
    33+// ystdex::id, std::bind, std::placeholder::_1, ystdex::is_parent_or_self,
    34+// ystdex::invoke, std::ref, YTraceDe, std::exception,
    35+// ystdex::trivially_copy_n, ptrdiff_t, DISC_INTERFACE, unique_raw,
    36+// ystdex::aligned_store_cast;
    3637 #if YCL_DS
    3738 # include "YSLib/Core/YModules.h"
    3839 # include YFM_YSLib_Core_YException // for YSLib::TryInvoke,
    @@ -808,7 +809,7 @@
    808809 ConsLeafWithCallback(part, sv, act, dclus);
    809810 return {};
    810811 }
    811- ConsBranch(part, sv, next_pos, dclus);
    812+ return ConsBranch(part, sv, next_pos, dclus);
    812813 });
    813814 // XXX: For a file system with external link to other file systems, the
    814815 // order is significant: this check cannot be moved earlier. However, this
    @@ -912,7 +913,7 @@
    912913
    913914 if(LFN::MaxMBCSLength < comp.length())
    914915 throw_error(errc::filename_too_long);
    915- if(dclus == part.GetRootDirCluster() && (comp == "." || comp == ".."))
    916+ if(dclus == part.GetRootDirCluster() && ystdex::is_parent_or_self(comp))
    916917 ConsReset(part);
    917918 else
    918919 {
    @@ -956,7 +957,7 @@
    956957
    957958 if(LFN::MaxMBCSLength < comp.length())
    958959 throw_error(errc::filename_too_long);
    959- if(dclus == part.GetRootDirCluster() && (comp == "." || comp == ".."))
    960+ if(dclus == part.GetRootDirCluster() && ystdex::is_parent_or_self(comp))
    960961 ConsReset(part);
    961962 else
    962963 {
    @@ -987,7 +988,7 @@
    987988
    988989 if(LFN::MaxMBCSLength < comp.length())
    989990 throw_error(errc::filename_too_long);
    990- if(dclus == part.GetRootDirCluster() && (comp == "." || comp == ".."))
    991+ if(dclus == part.GetRootDirCluster() && ystdex::is_parent_or_self(comp))
    991992 throw_error(errc::invalid_argument);
    992993 name = string(ystdex::rtrim(comp, ' '));
    993994 // NOTE: The check is not performed here. Let it go into the callback.
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/Win32/include/YCLib/MinGW32.h
    --- a/YFramework/Win32/include/YCLib/MinGW32.h Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/Win32/include/YCLib/MinGW32.h Sun Oct 25 11:06:55 2020 +0800
    @@ -12,13 +12,13 @@
    1212 \ingroup YCLib
    1313 \ingroup Win32
    1414 \brief YCLib MinGW32 平台公共扩展。
    15-\version r2050
    15+\version r2132
    1616 \author FrankHB <frankhb1989@gmail.com>
    1717 \since build 412
    1818 \par 创建时间:
    1919 2012-06-08 17:57:49 +0800
    2020 \par 修改时间:
    21- 2020-01-27 02:40 +0800
    21+ 2020-10-21 05:01 +0800
    2222 \par 文本编码:
    2323 UTF-8
    2424 \par 模块名称:
    @@ -26,8 +26,8 @@
    2626 */
    2727
    2828
    29-#ifndef YCL_MinGW32_INC_MinGW32_h_
    30-#define YCL_MinGW32_INC_MinGW32_h_ 1
    29+#ifndef YCL_Win32_INC_MinGW32_h_
    30+#define YCL_Win32_INC_MinGW32_h_ 1
    3131
    3232 #include "YCLib/YModules.h"
    3333 #include YFM_YCLib_Host // for string, ystdex::remove_reference_t, wstring,
    @@ -62,14 +62,14 @@
    6262 \return 当对应不存在时 EINVAL ,否则参数对应的 errno 。
    6363 \since build 639
    6464 */
    65-YF_API YB_STATELESS int
    65+YB_ATTR_nodiscard YF_API YB_STATELESS int
    6666 ConvertToErrno(ErrorCode) ynothrow;
    6767
    6868 /*!
    6969 \brief 取转换为 errno 的 Win32 错误。
    7070 \since build 622
    7171 */
    72-inline PDefH(int, GetErrnoFromWin32, ) ynothrow
    72+YB_ATTR_nodiscard inline PDefH(int, GetErrnoFromWin32, ) ynothrow
    7373 ImplRet(ConvertToErrno(::GetLastError()))
    7474
    7575
    @@ -125,14 +125,14 @@
    125125 \return std::error_category 派生类的 const 引用。
    126126 \since build 545
    127127 */
    128- static const std::error_category&
    128+ YB_ATTR_nodiscard YB_PURE static const std::error_category&
    129129 GetErrorCategory();
    130130
    131131 /*!
    132132 \brief 格式化错误消息字符串。
    133133 \return 若发生异常则结果为空,否则为区域固定为 en-US 的系统消息字符串。
    134134 */
    135- static std::string
    135+ YB_ATTR_nodiscard YB_PURE static std::string
    136136 FormatMessage(ErrorCode) ynothrow;
    137137 //@}
    138138 };
    @@ -238,18 +238,20 @@
    238238 /*!
    239239 \brief 从模块加载指定过程的指针。
    240240 \pre 参数非空。
    241+\exception Win32Exception 动态加载失败。
    242+\todo 支持缓存加载结果。
    241243 */
    242244 //@{
    243-YF_API YB_ATTR_returns_nonnull YB_NONNULL(2) ModuleProc*
    245+YB_ATTR_nodiscard YF_API YB_ATTR_returns_nonnull YB_NONNULL(2) ModuleProc*
    244246 LoadProc(::HMODULE, const char*);
    245247 template<typename _func>
    246-YB_NONNULL(2) inline _func&
    248+YB_ATTR_nodiscard YB_NONNULL(2) inline _func&
    247249 LoadProc(::HMODULE h_module, const char* proc)
    248250 {
    249251 return platform::Deref(reinterpret_cast<_func*>(LoadProc(h_module, proc)));
    250252 }
    251253 template<typename _func>
    252-YB_NONNULL(1, 2) inline _func&
    254+YB_ATTR_nodiscard YB_NONNULL(1, 2) inline _func&
    253255 LoadProc(const wchar_t* module, const char* proc)
    254256 {
    255257 return LoadProc<_func>(YCL_CallF_Win32(GetModuleHandleW, module), proc);
    @@ -299,7 +301,7 @@
    299301 \brief 取模块映像路径。
    300302 \since build 701
    301303 */
    302-YF_API wstring
    304+YB_ATTR_nodiscard YF_API wstring
    303305 FetchModuleFileName(::HMODULE = {}, YSLib::RecordLevel = YSLib::Err);
    304306
    305307
    @@ -338,7 +340,7 @@
    338340 ~GlobalLocked();
    339341
    340342 template<typename _type = void>
    341- observer_ptr<_type>
    343+ YB_ATTR_nodiscard observer_ptr<_type>
    342344 GetPtr() const ynothrow
    343345 {
    344346 return make_observer(static_cast<_type*>(p_locked));
    @@ -554,14 +556,14 @@
    554556 \brief 按打开的文件句柄归类节点从属的属性类别。
    555557 \return 指定句柄为字符、管道或未知类别。
    556558 */
    557-YF_API platform::NodeCategory
    559+YB_ATTR_nodiscard YF_API platform::NodeCategory
    558560 TryCategorizeNodeAttributes(UniqueHandle::pointer);
    559561
    560562 /*!
    561563 \brief 按打开的文件句柄归类节点从属的设备类别。
    562564 \return 指定句柄为字符、管道或未知类别。
    563565 */
    564-YF_API platform::NodeCategory
    566+YB_ATTR_nodiscard YF_API platform::NodeCategory
    565567 TryCategorizeNodeDevice(UniqueHandle::pointer);
    566568 //@}
    567569
    @@ -573,18 +575,18 @@
    573575 */
    574576 //@{
    575577 //! \brief 按 FileAttributes 和重解析标签归类节点类别。
    576-YF_API YB_STATELESS platform::NodeCategory
    578+YB_ATTR_nodiscard YF_API YB_STATELESS platform::NodeCategory
    577579 CategorizeNode(FileAttributes, unsigned long = 0) ynothrow;
    578580 //! \brief 按 \c ::WIN32_FIND_DATAA 归类节点类别。
    579-YB_STATELESS inline PDefH(platform::NodeCategory, CategorizeNode,
    580- const ::WIN32_FIND_DATAA& d) ynothrow
    581+YB_ATTR_nodiscard YB_STATELESS inline PDefH(platform::NodeCategory,
    582+ CategorizeNode, const ::WIN32_FIND_DATAA& d) ynothrow
    581583 ImplRet(CategorizeNode(FileAttributes(d.dwFileAttributes), d.dwReserved0))
    582584 /*!
    583585 \brief 按 \c ::WIN32_FIND_DATAW 归类节点类别。
    584586 \since build 658
    585587 */
    586-YB_STATELESS inline PDefH(platform::NodeCategory, CategorizeNode,
    587- const ::WIN32_FIND_DATAW& d) ynothrow
    588+YB_ATTR_nodiscard YB_STATELESS inline PDefH(platform::NodeCategory,
    589+ CategorizeNode, const ::WIN32_FIND_DATAW& d) ynothrow
    588590 ImplRet(CategorizeNode(FileAttributes(d.dwFileAttributes), d.dwReserved0))
    589591 //@}
    590592 /*!
    @@ -595,7 +597,7 @@
    595597 \sa TryCategorizeNodeDevice
    596598 \since build 701
    597599 */
    598-YF_API platform::NodeCategory
    600+YB_ATTR_nodiscard YF_API YB_PURE platform::NodeCategory
    599601 CategorizeNode(UniqueHandle::pointer) ynothrow;
    600602
    601603
    @@ -606,50 +608,52 @@
    606608 \since build 632
    607609 */
    608610 //@{
    609-YF_API YB_NONNULL(1) UniqueHandle
    611+YB_ATTR_nodiscard YF_API YB_NONNULL(1) UniqueHandle
    610612 MakeFile(const wchar_t*, FileAccessRights = AccessRights::None,
    611613 FileShareMode = FileShareMode::All, CreationDisposition
    612614 = CreationDisposition::OpenExisting,
    613615 FileAttributesAndFlags = FileAttributesAndFlags::NormalAll) ynothrowv;
    614616 //! \since build 660
    615-YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile, const wchar_t* path,
    616- FileAccessRights desired_access, FileShareMode shared_mode,
    617- FileAttributesAndFlags attributes_and_flags) ynothrowv
    617+YB_ATTR_nodiscard YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile,
    618+ const wchar_t* path, FileAccessRights desired_access, FileShareMode
    619+ shared_mode, FileAttributesAndFlags attributes_and_flags) ynothrowv
    618620 ImplRet(MakeFile(path, desired_access, shared_mode,
    619621 CreationDisposition::OpenExisting, attributes_and_flags))
    620-YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile, const wchar_t* path,
    622+YB_ATTR_nodiscard YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile, const wchar_t* path,
    621623 FileAccessRights desired_access, CreationDisposition creation_disposition,
    622624 FileAttributesAndFlags attributes_and_flags
    623625 = FileAttributesAndFlags::NormalAll) ynothrowv
    624626 ImplRet(MakeFile(path, desired_access, FileShareMode::All,
    625627 creation_disposition, attributes_and_flags))
    626628 //! \since build 637
    627-YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile, const wchar_t* path,
    628- FileAccessRights desired_access,
    629+YB_ATTR_nodiscard YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile,
    630+ const wchar_t* path, FileAccessRights desired_access,
    629631 FileAttributesAndFlags attributes_and_flags) ynothrowv
    630632 ImplRet(MakeFile(path, desired_access, FileShareMode::All,
    631633 CreationDisposition::OpenExisting, attributes_and_flags))
    632634 //! \since build 660
    633635 //@{
    634-YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile, const wchar_t* path,
    635- FileShareMode shared_mode, CreationDisposition creation_disposition
    636- = CreationDisposition::OpenExisting, FileAttributesAndFlags
    637- attributes_and_flags = FileAttributesAndFlags::NormalAll) ynothrowv
    636+YB_ATTR_nodiscard YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile,
    637+ const wchar_t* path, FileShareMode shared_mode, CreationDisposition
    638+ creation_disposition = CreationDisposition::OpenExisting,
    639+ FileAttributesAndFlags attributes_and_flags
    640+ = FileAttributesAndFlags::NormalAll) ynothrowv
    638641 ImplRet(MakeFile(path, AccessRights::None, shared_mode,
    639642 creation_disposition, attributes_and_flags))
    640643 //! \since build 701
    641-YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile, const wchar_t* path,
    642- CreationDisposition creation_disposition, FileAttributesAndFlags
    643- attributes_and_flags = FileAttributesAndFlags::NormalAll) ynothrowv
    644+YB_ATTR_nodiscard YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile,
    645+ const wchar_t* path, CreationDisposition creation_disposition,
    646+ FileAttributesAndFlags attributes_and_flags
    647+ = FileAttributesAndFlags::NormalAll) ynothrowv
    644648 ImplRet(MakeFile(path, AccessRights::None, FileShareMode::All,
    645649 creation_disposition, attributes_and_flags))
    646-YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile, const wchar_t* path,
    647- FileShareMode shared_mode, FileAttributesAndFlags
    650+YB_ATTR_nodiscard YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile,
    651+ const wchar_t* path, FileShareMode shared_mode, FileAttributesAndFlags
    648652 attributes_and_flags = FileAttributesAndFlags::NormalAll) ynothrowv
    649653 ImplRet(MakeFile(path, AccessRights::None, shared_mode,
    650654 CreationDisposition::OpenExisting, attributes_and_flags))
    651-YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile, const wchar_t* path,
    652- FileAttributesAndFlags attributes_and_flags) ynothrowv
    655+YB_ATTR_nodiscard YB_NONNULL(1) inline PDefH(UniqueHandle, MakeFile,
    656+ const wchar_t* path, FileAttributesAndFlags attributes_and_flags) ynothrowv
    653657 ImplRet(MakeFile(path, AccessRights::None, FileShareMode::All,
    654658 CreationDisposition::OpenExisting, attributes_and_flags))
    655659 //@}
    @@ -662,7 +666,7 @@
    662666 下的 <tt>Software\Wine</tt> 键实现。
    663667 \since build 435
    664668 */
    665-YF_API bool
    669+YB_ATTR_nodiscard YF_API bool
    666670 CheckWine();
    667671
    668672
    @@ -753,7 +757,7 @@
    753757 处理文件信息时,首先调用 CategorizeNode 对查找数据归类;
    754758 然后打开名称指定的文件进一步调用 CategorizeNode 对打开的文件判断归类。
    755759 */
    756- platform::NodeCategory
    760+ YB_ATTR_nodiscard platform::NodeCategory
    757761 GetNodeCategory() const ynothrow;
    758762
    759763 /*!
    @@ -815,10 +819,10 @@
    815819 */
    816820 //@{
    817821 //! \since build 660
    818-YF_API YB_NONNULL(1) wstring
    822+YB_ATTR_nodiscard YF_API YB_NONNULL(1) wstring
    819823 ResolveReparsePoint(const wchar_t*);
    820824 //! \since build 705
    821-YF_API YB_NONNULL(1) wstring_view
    825+YB_ATTR_nodiscard YF_API YB_NONNULL(1) wstring_view
    822826 ResolveReparsePoint(const wchar_t*, ReparsePointData::Data&);
    823827 //@}
    824828
    @@ -829,9 +833,30 @@
    829833 \throw Win32Exception 调用失败。
    830834 \since build 658
    831835 */
    832-YF_API YB_NONNULL(1) wstring
    836+YB_ATTR_nodiscard YF_API YB_NONNULL(1) wstring
    833837 ExpandEnvironmentStrings(const wchar_t*);
    834838
    839+/*!
    840+\brief 解析应用程序命令行参数。
    841+\return UTF-8 编码的参数向量。
    842+\since build 901
    843+\see $2020-10 @ %Documentation::Workflow.
    844+
    845+解析 Unicode 编码的应用程序命令行参数字符串。
    846+参数向量同标准 C++ 的 \c ::main 命令行参数的格式,但不依赖实现使用的代码页。
    847+不依赖入口函数是否为 \c ::main 及其具体声明形式。
    848+特别地,使用声明为 \c ::WinMain 和 \c ::wWinMain 入口函数的程序也使用一致的参数。
    849+解析参数字符串不展开通配符。具体行为兼容 UCRT 的实现,不保证和 CRT 历史实现完全一致。
    850+*/
    851+//@{
    852+//! \note 以 \c ::GetCommandLineW 调用取参数。
    853+YB_ATTR_nodiscard YF_API YB_PURE vector<string>
    854+ParseCommandArguments();
    855+//! \pre 断言:参数非空。
    856+YB_ATTR_nodiscard YF_API YB_NONNULL(1) YB_PURE vector<string>
    857+ParseCommandArguments(const wchar_t*);
    858+//@}
    859+
    835860
    836861 /*!
    837862 \see https://msdn.microsoft.com/zh-cn/library/windows/desktop/aa363788(v=vs.85).aspx 。
    @@ -851,13 +876,13 @@
    851876 //! \brief 查询文件链接数。
    852877 //@{
    853878 //! \since build 637
    854-YF_API size_t
    879+YB_ATTR_nodiscard YF_API size_t
    855880 QueryFileLinks(UniqueHandle::pointer);
    856881 /*!
    857882 \pre 间接断言:路径参数非空。
    858883 \note 最后参数表示跟踪重解析点。
    859884 */
    860-YF_API YB_NONNULL(1) size_t
    885+YB_ATTR_nodiscard YF_API YB_NONNULL(1) size_t
    861886 QueryFileLinks(const wchar_t*, bool = {});
    862887 //@}
    863888
    @@ -869,13 +894,13 @@
    869894 */
    870895 //@{
    871896 //! \since build 638
    872-YF_API pair<VolumeID, FileID>
    897+YB_ATTR_nodiscard YF_API pair<VolumeID, FileID>
    873898 QueryFileNodeID(UniqueHandle::pointer);
    874899 /*!
    875900 \pre 间接断言:路径参数非空。
    876901 \note 最后参数表示跟踪重解析点。
    877902 */
    878-YF_API YB_NONNULL(1) pair<VolumeID, FileID>
    903+YB_ATTR_nodiscard YF_API YB_NONNULL(1) pair<VolumeID, FileID>
    879904 QueryFileNodeID(const wchar_t*, bool = {});
    880905 //@}
    881906 //@}
    @@ -886,10 +911,10 @@
    886911 \since build 718
    887912 */
    888913 //@{
    889-YF_API std::uint64_t
    914+YB_ATTR_nodiscard YF_API std::uint64_t
    890915 QueryFileSize(UniqueHandle::pointer);
    891916 //! \pre 间接断言:路径参数非空。
    892-YF_API YB_NONNULL(1) std::uint64_t
    917+YB_ATTR_nodiscard YF_API YB_NONNULL(1) std::uint64_t
    893918 QueryFileSize(const wchar_t*);
    894919 //@}
    895920
    @@ -950,13 +975,13 @@
    950975 \brief 转换文件时间为以 POSIX 历元起始度量的时间间隔。
    951976 \since build 632
    952977 */
    953-YF_API std::chrono::nanoseconds
    978+YB_ATTR_nodiscard YF_API std::chrono::nanoseconds
    954979 ConvertTime(const ::FILETIME&);
    955980 /*!
    956981 \brief 转换以 POSIX 历元起始度量的时间间隔为文件时间。
    957982 \since build 651
    958983 */
    959-YF_API ::FILETIME
    984+YB_ATTR_nodiscard YF_API ::FILETIME
    960985 ConvertTime(std::chrono::nanoseconds);
    961986 //@}
    962987
    @@ -964,7 +989,7 @@
    964989 /*!
    965990 \pre 文件句柄不为 \c INVALID_HANDLE_VALUE ,
    966991 且具有 AccessRights::GenericRead 或 AccessRights::GenericWrite 权限。
    967-\since build 721
    992+\since build 901
    968993 */
    969994 //@{
    970995 /*!
    @@ -975,11 +1000,11 @@
    9751000 */
    9761001 //@{
    9771002 //! \throw Win32Exception 锁定失败。
    978-void
    1003+YF_API void
    9791004 LockFile(UniqueHandle::pointer, std::uint64_t = 0,
    9801005 std::uint64_t = std::uint64_t(-1), bool = true, bool = {});
    9811006
    982-bool
    1007+YB_ATTR_nodiscard YF_API bool
    9831008 TryLockFile(UniqueHandle::pointer, std::uint64_t = 0,
    9841009 std::uint64_t = std::uint64_t(-1), bool = true, bool = true) ynothrow;
    9851010 //@}
    @@ -988,7 +1013,7 @@
    9881013 \brief 解锁文件。
    9891014 \pre 文件已被锁定。
    9901015 */
    991-bool
    1016+YF_API bool
    9921017 UnlockFile(UniqueHandle::pointer, std::uint64_t = 0,
    9931018 std::uint64_t = std::uint64_t(-1)) ynothrow;
    9941019 //@}
    @@ -1005,7 +1030,7 @@
    10051030 WaitUnique(UniqueHandle::pointer, unsigned long = INFINITE);
    10061031
    10071032 //! \return 是否等待成功。
    1008-YF_API bool
    1033+YB_ATTR_nodiscard YF_API bool
    10091034 TryWaitUnique(UniqueHandle::pointer, unsigned long = 0) ynothrow;
    10101035 //@}
    10111036
    @@ -1031,7 +1056,7 @@
    10311056 PDefH(void, lock, )
    10321057 ImplExpr(WaitUnique(native_handle()))
    10331058
    1034- PDefH(bool, try_lock, ) ynothrow
    1059+ YB_ATTR_nodiscard PDefH(bool, try_lock, ) ynothrow
    10351060 ImplRet(TryWaitUnique(native_handle()))
    10361061
    10371062 /*!
    @@ -1056,14 +1081,14 @@
    10561081 \brief 取系统目录路径。
    10571082 \since build 593
    10581083 */
    1059-YF_API wstring
    1084+YB_ATTR_nodiscard YF_API wstring
    10601085 FetchSystemPath(size_t = MAX_PATH);
    10611086
    10621087 /*!
    10631088 \brief 取系统目录路径。
    10641089 \since build 693
    10651090 */
    1066-YF_API wstring
    1091+YB_ATTR_nodiscard YF_API wstring
    10671092 FetchWindowsPath(size_t = MAX_PATH);
    10681093 //@}
    10691094
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/Win32/source/YCLib/MinGW32.cpp
    --- a/YFramework/Win32/source/YCLib/MinGW32.cpp Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/Win32/source/YCLib/MinGW32.cpp Sun Oct 25 11:06:55 2020 +0800
    @@ -12,13 +12,13 @@
    1212 \ingroup YCLib
    1313 \ingroup Win32
    1414 \brief YCLib MinGW32 平台公共扩展。
    15-\version r2200
    15+\version r2286
    1616 \author FrankHB <frankhb1989@gmail.com>
    1717 \since build 427
    1818 \par 创建时间:
    1919 2013-07-10 15:35:19 +0800
    2020 \par 修改时间:
    21- 2020-01-12 18:09 +0800
    21+ 2020-10-21 04:34 +0800
    2222 \par 文本编码:
    2323 UTF-8
    2424 \par 模块名称:
    @@ -173,10 +173,10 @@
    173173 class Win32ErrorCategory : public std::error_category
    174174 {
    175175 public:
    176- PDefH(const char*, name, ) const ynothrow override
    176+ YB_PURE PDefH(const char*, name, ) const ynothrow override
    177177 ImplRet("Win32Error")
    178178 //! \since build 564
    179- PDefH(std::string, message, int ev) const override
    179+ YB_PURE PDefH(std::string, message, int ev) const override
    180180 // NOTE: For Win32 a %::DWORD can be mapped one-to-one for 32-bit %int.
    181181 ImplRet("Error " + std::to_string(ev) + ": "
    182182 + Win32Exception::FormatMessage(ErrorCode(ev)))
    @@ -206,7 +206,7 @@
    206206 YCL_Raise_Win32E("CreateFileW", yfsig);
    207207 }
    208208
    209-yconstfn
    209+YB_ATTR_nodiscard YB_STATELESS yconstfn
    210210 PDefH(FileAttributesAndFlags, FollowToAttr, bool follow_reparse_point) ynothrow
    211211 ImplRet(follow_reparse_point ? FileAttributesAndFlags::NormalWithDirectory
    212212 : FileAttributesAndFlags::NormalAll)
    @@ -220,10 +220,12 @@
    220220 //! \since build 721
    221221 //@{
    222222 // TODO: Extract to %YCLib.NativeAPI?
    223-yconstfn PDefH(unsigned long, High32, std::uint64_t val) ynothrow
    223+YB_ATTR_nodiscard yconstfn YB_STATELESS
    224+ PDefH(unsigned long, High32, std::uint64_t val) ynothrow
    224225 ImplRet(static_cast<unsigned long>(val >> 32UL))
    225226
    226-yconstfn PDefH(unsigned long, Low32, std::uint64_t val) ynothrow
    227+YB_ATTR_nodiscard yconstfn YB_STATELESS
    228+ PDefH(unsigned long, Low32, std::uint64_t val) ynothrow
    227229 ImplRet(static_cast<unsigned long>(val))
    228230
    229231 template<typename _func>
    @@ -246,7 +248,7 @@
    246248 Windows
    247249 };
    248250
    249-wstring
    251+YB_ATTR_nodiscard wstring
    250252 FetchFixedSystemPath(SystemPaths e, size_t s)
    251253 {
    252254 // XXX: Depends right behavior on external API.
    @@ -371,7 +373,7 @@
    371373 void
    372374 LocalDelete::operator()(pointer h) const ynothrow
    373375 {
    374- // FIXME: For some platforms, no %::LocalFree available. See https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms679351(v=vs.85).aspx.
    376+ // FIXME: For some platforms, no %::LocalFree is available. See https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms679351(v=vs.85).aspx.
    375377 // NOTE: %::LocalFree ignores null handle value.
    376378 if(YB_UNLIKELY(::LocalFree(h)))
    377379 YCL_Trace_Win32E(Warning, LocalFree, yfsig);
    @@ -670,6 +672,88 @@
    670672 return wstr;
    671673 }
    672674
    675+vector<string>
    676+ParseCommandArguments()
    677+{
    678+ return ParseCommandArguments(::GetCommandLineW());
    679+}
    680+vector<string>
    681+ParseCommandArguments(const wchar_t* p)
    682+{
    683+ YAssertNonnull(p);
    684+ if(p[0] == '\0')
    685+ {
    686+ wchar_t program_name[MAX_PATH + 1];
    687+
    688+ ::GetModuleFileNameW({}, program_name, MAX_PATH);
    689+ p = program_name;
    690+ if(p[0] == '\0')
    691+ throw std::runtime_error("Failed getting module file name.");
    692+ }
    693+
    694+ vector<string> args;
    695+ wstring cbuf;
    696+ wchar_t c;
    697+ bool quoted = {};
    698+ const auto add([&]{
    699+ args.push_back(WCSToMBCS(std::move(cbuf)));
    700+ });
    701+
    702+ do
    703+ {
    704+ if(*p == '"')
    705+ quoted = !quoted;
    706+ else
    707+ cbuf += *p;
    708+ c = *p++;
    709+ }while(c != '\0' && (quoted || (c != ' ' && c != '\t')));
    710+ add();
    711+ if(c == '\0')
    712+ return args;
    713+ cbuf.clear();
    714+ quoted = {};
    715+ while(true)
    716+ {
    717+ if(*p != '\0')
    718+ while(*p == ' ' || *p == '\t')
    719+ ++p;
    720+ if(*p == '\0')
    721+ break;
    722+ while(true)
    723+ {
    724+ bool copy_char(true);
    725+ unsigned n_slash(0);
    726+
    727+ while(*p == '\\')
    728+ yunseq(++p, ++n_slash);
    729+ if(*p == '"')
    730+ {
    731+ if(n_slash % 2 == 0)
    732+ {
    733+ if(quoted && p[1] == '"')
    734+ ++p;
    735+ else
    736+ {
    737+ copy_char = {};
    738+ quoted = !quoted;
    739+ }
    740+ }
    741+ n_slash /= 2;
    742+ }
    743+ while(n_slash-- != 0)
    744+ cbuf += '\\';
    745+ if(*p == '\0' || (!quoted && (*p == ' ' || *p == '\t')))
    746+ break;
    747+ if(copy_char)
    748+ cbuf += *p;
    749+ ++p;
    750+ }
    751+ add();
    752+ cbuf.clear();
    753+ }
    754+ return args;
    755+}
    756+
    673757
    674758 size_t
    675759 QueryFileLinks(UniqueHandle::pointer h)
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/include/NPL/NPLA1.h
    --- a/YFramework/include/NPL/NPLA1.h Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/include/NPL/NPLA1.h Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file NPLA1.h
    1212 \ingroup NPL
    1313 \brief NPLA1 公共接口。
    14-\version r8439
    14+\version r8452
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 472
    1717 \par 创建时间:
    1818 2014-02-02 17:58:24 +0800
    1919 \par 修改时间:
    20- 2020-10-05 22:33 +0800
    20+ 2020-10-19 06:22 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -47,6 +47,7 @@
    4747 #include <ystdex/algorithm.hpp> // for ystdex::fast_any_of, ystdex::split;
    4848 #include <ystdex/cast.hpp> // for ystdex::polymorphic_downcast;
    4949 #include <ystdex/scope_guard.hpp> // for ystdex::guard;
    50+#include <iosfwd> // for std::ostream;
    5051
    5152 namespace NPL
    5253 {
    @@ -1638,6 +1639,11 @@
    16381639 指定字符串参数作为加载对象描述,结合自身状态加载输入为待求值的项。
    16391640 */
    16401641 Loader Load{DefaultLoad};
    1642+ /*!
    1643+ \brief 默认输出流指针。
    1644+ \since build 901
    1645+ */
    1646+ observer_ptr<std::ostream> OutputStreamPtr{};
    16411647
    16421648 /*!
    16431649 \sa ListTermPreprocess
    @@ -1669,6 +1675,14 @@
    16691675 IsAsynchronous() const ynothrow;
    16701676
    16711677 /*!
    1678+ \brief 取输出流引用。
    1679+ \throw ystdex::unsupported 不支持的输出流:流指针为空。
    1680+ \sa OutputStreamPtr
    1681+ */
    1682+ YB_ATTR_nodiscard YB_PURE std::ostream&
    1683+ GetOutputStreamRef() const;
    1684+
    1685+ /*!
    16721686 \brief 默认加载。
    16731687 \since build 899
    16741688
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/include/YCLib/FileIO.h
    --- a/YFramework/include/YCLib/FileIO.h Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/include/YCLib/FileIO.h Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file FileIO.h
    1212 \ingroup YCLib
    1313 \brief 平台相关的文件访问和输入/输出接口。
    14-\version r2868
    14+\version r3176
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 616
    1717 \par 创建时间:
    1818 2015-07-14 18:50:35 +0800
    1919 \par 修改时间:
    20- 2020-01-27 22:15 +0800
    20+ 2020-10-24 04:29 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -29,14 +29,10 @@
    2929 #define YCL_INC_FileIO_h_ 1
    3030
    3131 #include "YModules.h"
    32-#include YFM_YCLib_Platform
    33-#include YFM_YBaseMacro
    3432 #include YFM_YCLib_Debug // for YB_NONNULL, string, Nonnull, u16string_view,
    3533 // std::uint32_t, std::uint64_t, ynothrow, ystdex::totally_ordered, std::FILE,
    36-// ystdex::enable_for_string_class_t, ystdex::retry_for_vector, errno,
    37-// ystdex::throw_error, u16string, std::system_error, YTraceDe, array, wstring,
    38-// string_view;
    39-#include <chrono> // for std::chrono::nanoseconds;
    34+// ystdex::enable_for_string_class_t, errno, ystdex::throw_error, u16string,
    35+// std::system_error, YTraceDe, array, wstring, string_view;
    4036 #include YFM_YCLib_Reference // for unique_ptr_from;
    4137 #include <ios> // for std::ios_base::sync_with_stdio;
    4238 #include <fstream> // for std::filebuf;
    @@ -47,6 +43,7 @@
    4743 # include <ystdex/cstdio.h> // for ystdex::openmode_conv;
    4844 # include <locale> // for std::use_facet, std::codecvt;
    4945 #endif
    46+#include <iosfwd> // for std::ostream;
    5047
    5148 namespace platform
    5249 {
    @@ -75,36 +72,6 @@
    7572 //@}
    7673
    7774
    78-//! \since build 638
    79-//@{
    80-//! \brief 文件节点标识类型。
    81-//@{
    82-#if YCL_Win32
    83-using FileNodeID = pair<std::uint32_t, std::uint64_t>;
    84-#else
    85-using FileNodeID = pair<std::uint64_t, std::uint64_t>;
    86-#endif
    87-//@}
    88-
    89-/*!
    90-\bug 结构化类型污染。
    91-\relates FileNodeID
    92-*/
    93-//@{
    94-YB_ATTR_nodiscard yconstfn
    95- PDefHOp(bool, ==, const FileNodeID& x, const FileNodeID& y)
    96- ImplRet(x.first == y.first && x.second == y.second)
    97-YB_ATTR_nodiscard yconstfn
    98- PDefHOp(bool, !=, const FileNodeID& x, const FileNodeID& y)
    99- ImplRet(!(x == y))
    100-//@}
    101-//@}
    102-
    103-
    104-//! \since build 628
    105-using FileTime = std::chrono::nanoseconds;
    106-
    107-
    10875 /*!
    10976 \brief 文件模式类型。
    11077 \since build 626
    @@ -212,14 +179,6 @@
    212179 //! \exception std::system_error 参数无效或调用失败。
    213180 //@{
    214181 /*!
    215- \brief 取访问时间。
    216- \return 以 POSIX 时间相同历元的时间间隔。
    217- \note 当前 Windows 使用 \c ::GetFileTime 实现,其它只保证最高精确到秒。
    218- \since build 628
    219- */
    220- YB_ATTR_nodiscard FileTime
    221- GetAccessTime() const;
    222- /*!
    223182 \brief 取节点类别。
    224183 \return 失败时为 NodeCategory::Invalid ,否则为对应类别。
    225184 \since build 638
    @@ -242,31 +201,6 @@
    242201 YB_ATTR_nodiscard mode_t
    243202 GetMode() const;
    244203 //@}
    245- /*!
    246- \return 以 POSIX 时间相同历元的时间间隔。
    247- \note 当前 Windows 使用 \c ::GetFileTime 实现,其它只保证最高精确到秒。
    248- \since build 628
    249- */
    250- //@{
    251- //! \brief 取修改时间。
    252- YB_ATTR_nodiscard FileTime
    253- GetModificationTime() const;
    254- //! \brief 取修改和访问时间。
    255- YB_ATTR_nodiscard array<FileTime, 2>
    256- GetModificationAndAccessTime() const;
    257- //@}
    258- //@}
    259- /*!
    260- \note 若存储分配失败,设置 errno 为 \c ENOMEM 。
    261- \since build 638
    262- */
    263- //@{
    264- //! \brief 取文件系统节点标识。
    265- YB_ATTR_nodiscard FileNodeID
    266- GetNodeID() const ynothrow;
    267- //! \brief 取链接数。
    268- YB_ATTR_nodiscard size_t
    269- GetNumberOfLinks() const ynothrow;
    270204 //@}
    271205 /*!
    272206 \brief 取大小。
    @@ -280,14 +214,6 @@
    280214 GetSize() const;
    281215
    282216 /*!
    283- \brief 设置访问时间。
    284- \throw std::system_error 设置失败。
    285- \note DS 平台:不支持操作。
    286- \since build 651
    287- */
    288- void
    289- SetAccessTime(FileTime) const;
    290- /*!
    291217 \throw std::system_error 调用失败或不支持操作。
    292218 \note 非 POSIX 平台:不支持操作。
    293219 \since build 719
    @@ -298,39 +224,25 @@
    298224 \return 是否需要并改变设置。
    299225 \see http://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html 。
    300226 */
    301- #if !YCL_API_POSIXFileSystem
    227+#if !YCL_API_POSIXFileSystem
    302228 YB_NORETURN
    303- #endif
    229+#endif
    304230 bool
    305231 SetBlocking() const;
    306232 //! \brief 设置旗标。
    307- #if !YCL_API_POSIXFileSystem
    233+#if !YCL_API_POSIXFileSystem
    308234 YB_NORETURN
    309- #endif
    235+#endif
    310236 void
    311237 SetFlags(int) const;
    312238 //! \brief 设置访问模式。
    313- #if !YCL_API_POSIXFileSystem
    239+#if !YCL_API_POSIXFileSystem
    314240 YB_NORETURN
    315- #endif
    241+#endif
    316242 void
    317243 SetMode(mode_t) const;
    318244 //@}
    319245 /*!
    320- \throw std::system_error 设置失败。
    321- \note DS 平台:不支持操作。
    322- \note Win32 平台:要求打开的文件具有写权限。
    323- \since build 651
    324- */
    325- //@{
    326- //! \brief 设置修改时间。
    327- void
    328- SetModificationTime(FileTime) const;
    329- //! \brief 设置修改和访问时间。
    330- void
    331- SetModificationAndAccessTime(array<FileTime, 2>) const;
    332- //@}
    333- /*!
    334246 \brief 设置非阻塞模式。
    335247 \return 是否需要并改变设置。
    336248 \throw std::system_error 调用失败或不支持操作。
    @@ -339,9 +251,9 @@
    339251 \see http://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html 。
    340252 \since build 719
    341253 */
    342- #if !YCL_API_POSIXFileSystem
    254+#if !YCL_API_POSIXFileSystem
    343255 YB_NORETURN
    344- #endif
    256+#endif
    345257 bool
    346258 SetNonblocking() const;
    347259 //! \since build 625
    @@ -535,24 +447,10 @@
    535447
    536448 /*!
    537449 \pre 断言:第一参数非空。
    538-\note 若存储分配失败,设置 errno 为 \c ENOMEM 。
    539-\since build 669
    450+\note 若在发生其它错误前存储分配失败,设置 errno 为 \c ENOMEM 。
    540451 */
    541452 //@{
    542453 /*!
    543-\brief 测试路径可访问性。
    544-\param path 路径,意义同 POSIX <tt>::open</tt> 。
    545-\param amode 模式,基本语义同 POSIX.1 2004 ,具体行为取决于实现。 。
    546-\note errno 在出错时会被设置,具体值由实现定义。
    547-*/
    548-//@{
    549-YB_ATTR_nodiscard YF_API YB_NONNULL(1) int
    550-uaccess(const char* path, int amode) ynothrowv;
    551-YB_ATTR_nodiscard YF_API YB_NONNULL(1) int
    552-uaccess(const char16_t* path, int amode) ynothrowv;
    553-//@}
    554-
    555-/*!
    556454 \param filename 文件名,意义同 POSIX \c ::open 。
    557455 \param oflag 打开旗标,基本语义同 POSIX.1 2004 ,具体行为取决于实现。
    558456 \param pmode 打开模式,基本语义同 POSIX.1 2004 ,具体行为取决于实现。
    @@ -571,7 +469,7 @@
    571469
    572470 /*!
    573471 \param filename 文件名,意义同 std::fopen 。
    574-\note 若存储分配失败,设置 errno 为 \c ENOMEM 。
    472+\since build 669
    575473 */
    576474 //@{
    577475 /*!
    @@ -634,74 +532,6 @@
    634532 */
    635533 YB_ATTR_nodiscard YF_API YB_NONNULL(1) int
    636534 upclose(std::FILE*) ynothrowv;
    637-
    638-/*!
    639-\brief 取当前工作目录复制至指定缓冲区中。
    640-\param size 缓冲区长。
    641-\return 若成功为第一参数,否则为空指针。
    642-\note 基本语义同 POSIX.1 2004 的 \c ::getcwd 。
    643-\note 若存储分配失败,设置 errno 为 \c ENOMEM 。
    644-\note Win32 平台:当且仅当结果为根目录时以分隔符结束。
    645-\note 其它平台:未指定结果是否以分隔符结束。
    646-\since build 699
    647-*/
    648-//@{
    649-//! \param buf UTF-8 缓冲区起始指针。
    650-YB_ATTR_nodiscard YF_API YB_NONNULL(1) char*
    651-ugetcwd(char* buf, size_t size) ynothrowv;
    652-//! \param buf UCS-2 缓冲区起始指针。
    653-YB_ATTR_nodiscard YF_API YB_NONNULL(1) char16_t*
    654-ugetcwd(char16_t* buf, size_t size) ynothrowv;
    655-//@}
    656-
    657-/*!
    658-\pre 断言:参数非空。
    659-\return 操作是否成功。
    660-\note errno 在出错时会被设置,具体值除以上明确的外,由实现定义。
    661-\note 参数表示路径,使用 UTF-8 编码。
    662-\note DS 使用 newlib 实现。 MinGW32 使用 MSVCRT 实现。 Android 使用 bionic 实现。
    663- 其它 Linux 使用 GLibC 实现。
    664-*/
    665-//@{
    666-/*!
    667-\brief 切换当前工作路径至指定路径。
    668-\note POSIX 平台:除路径和返回值外语义同 \c ::chdir 。
    669-*/
    670-YB_ATTR_nodiscard YF_API YB_NONNULL(1) bool
    671-uchdir(const char*) ynothrowv;
    672-
    673-/*!
    674-\brief 按路径以默认权限新建一个目录。
    675-\note 权限由实现定义: DS 使用最大权限; MinGW32 使用 \c ::_wmkdir 指定的默认权限。
    676-*/
    677-YB_ATTR_nodiscard YF_API YB_NONNULL(1) bool
    678-umkdir(const char*) ynothrowv;
    679-
    680-/*!
    681-\brief 按路径删除一个空目录。
    682-\note POSIX 平台:除路径和返回值外语义同 \c ::rmdir 。
    683-*/
    684-YB_ATTR_nodiscard YF_API YB_NONNULL(1) bool
    685-urmdir(const char*) ynothrowv;
    686-
    687-/*!
    688-\brief 按路径删除一个非目录文件。
    689-\note POSIX 平台:除路径和返回值外语义同 \c ::unlink 。
    690-\note Win32 平台:支持移除只读文件,但删除打开的文件总是失败。
    691-*/
    692-YB_ATTR_nodiscard YF_API YB_NONNULL(1) bool
    693-uunlink(const char*) ynothrowv;
    694-
    695-/*!
    696-\brief 按路径移除一个文件。
    697-\note POSIX 平台:除路径和返回值外语义同 \c ::remove 。移除非空目录总是失败。
    698-\note Win32 平台:支持移除空目录和只读文件,但删除打开的文件总是失败。
    699-\see https://msdn.microsoft.com/en-us/library/kc07117k.aspx 。
    700-*/
    701-YB_ATTR_nodiscard YF_API YB_NONNULL(1) bool
    702-uremove(const char*) ynothrowv;
    703-//@}
    704-//@}
    705535 //@}
    706536
    707537
    @@ -1210,124 +1040,18 @@
    12101040 //@}
    12111041
    12121042
    1213-//! \since build 713
    1214-//@{
    12151043 /*!
    1216-\brief 尝试按指定的起始缓冲区大小取当前工作目录的路径。
    1217-\pre 间接断言:参数不等于 0 。
    1218-\note 未指定结果是否以分隔符结束。
    1219-*/
    1220-template<typename _tChar>
    1221-YB_ATTR_nodiscard basic_string<_tChar>
    1222-FetchCurrentWorkingDirectory(size_t init)
    1223-{
    1224- return ystdex::retry_for_vector<basic_string<_tChar>>(init,
    1225- [](basic_string<_tChar>& res, size_t) -> bool{
    1226- const auto r(platform::ugetcwd(&res[0], res.length()));
    1227-
    1228- if(r)
    1229- {
    1230- res = r;
    1231- return {};
    1232- }
    1233-
    1234- const int err(errno);
    1044+\brief 向流中输出字符串。
    1045+\pre 断言:第二参数非空。
    1046+\note Win32 平台:检查流是否使用控制台。若使用控制台,刷新流并使用控制台输出。
    1047+\sa ystdex::write_ntcts
    1048+\since build 901
    12351049
    1236- if(err != ERANGE)
    1237- ystdex::throw_error(err, yfsig);
    1238- return true;
    1239- });
    1240-}
    1241-#if YCL_Win32
    1242-//! \note 参数被忽略。
    1243-//@{
    1244-template<>
    1245-YB_ATTR_nodiscard YF_API string
    1246-FetchCurrentWorkingDirectory(size_t);
    1247-template<>
    1248-YB_ATTR_nodiscard YF_API u16string
    1249-FetchCurrentWorkingDirectory(size_t);
    1250-//@}
    1251-#endif
    1252-//@}
    1253-
    1254-
    1255-//! \exception std::system_error 参数无效或文件时间查询失败。
    1256-//@{
    1257-/*!
    1258-\sa FileDescriptor::GetAccessTime
    1259-\since build 631
    1260-*/
    1261-//@{
    1262-YB_ATTR_nodiscard YB_NONNULL(1) inline
    1263- PDefH(FileTime, GetFileAccessTimeOf, std::FILE* fp)
    1264- ImplRet(FileDescriptor(fp).GetAccessTime())
    1265-/*!
    1266-\pre 断言:第一参数非空。
    1267-\note 最后参数表示跟随连接:若文件系统支持,访问链接的文件而不是链接自身。
    1050+无格式输出字符串。
    1051+默认使用 ystdex::write_ntcts ,但可对特定的流实现检查并使用不同的方式。
    12681052 */
    1269-//@{
    1270-YB_ATTR_nodiscard YF_API YB_NONNULL(1) FileTime
    1271-GetFileAccessTimeOf(const char*, bool = {});
    1272-YB_ATTR_nodiscard YF_API YB_NONNULL(1) FileTime
    1273-GetFileAccessTimeOf(const char16_t*, bool = {});
    1274-//@}
    1275-//@}
    1276-
    1277-/*!
    1278-\sa FileDescriptor::GetModificationTime
    1279-\since build 628
    1280-*/
    1281-//@{
    1282-YB_ATTR_nodiscard YB_NONNULL(1) inline
    1283- PDefH(FileTime, GetFileModificationTimeOf, std::FILE* fp)
    1284- ImplRet(FileDescriptor(fp).GetModificationTime())
    1285-
    1286-/*!
    1287-\pre 断言:第一参数非空。
    1288-\note 最后参数表示跟随连接:若文件系统支持,访问链接的文件而不是链接自身。
    1289-*/
    1290-//@{
    1291-YB_ATTR_nodiscard YF_API YB_NONNULL(1) FileTime
    1292-GetFileModificationTimeOf(const char*, bool = {});
    1293-YB_ATTR_nodiscard YF_API YB_NONNULL(1) FileTime
    1294-GetFileModificationTimeOf(const char16_t*, bool = {});
    1295-//@}
    1296-//@}
    1297-
    1298-/*!
    1299-\sa FileDescriptor::GetModificationAndAccessTime
    1300-\since build 631
    1301-*/
    1302-//@{
    1303-YB_ATTR_nodiscard YB_NONNULL(1) inline PDefH(array<FileTime YPP_Comma 2>,
    1304- GetFileModificationAndAccessTimeOf, std::FILE* fp)
    1305- ImplRet(FileDescriptor(fp).GetModificationAndAccessTime())
    1306-/*!
    1307-\pre 断言:第一参数非空。
    1308-\note 最后参数表示跟随连接:若文件系统支持,访问链接的文件而不是链接自身。
    1309-*/
    1310-//@{
    1311-YB_ATTR_nodiscard YF_API YB_NONNULL(1) array<FileTime, 2>
    1312-GetFileModificationAndAccessTimeOf(const char*, bool = {});
    1313-YB_ATTR_nodiscard YF_API YB_NONNULL(1) array<FileTime, 2>
    1314-GetFileModificationAndAccessTimeOf(const char16_t*, bool = {});
    1315-//@}
    1316-//@}
    1317-//@}
    1318-
    1319-/*!
    1320-\brief 取路径指定的文件链接数。
    1321-\return 若成功为连接数,否则若文件不存在时为 0 。
    1322-\note 最后参数表示跟随连接:若文件系统支持,访问链接的文件而不是链接自身。
    1323-\since build 846
    1324-*/
    1325-//@{
    1326-YB_ATTR_nodiscard YF_API YB_NONNULL(1) size_t
    1327-FetchNumberOfLinks(const char*, bool = {});
    1328-YB_ATTR_nodiscard YF_API YB_NONNULL(1) size_t
    1329-FetchNumberOfLinks(const char16_t*, bool = {});
    1330-//@}
    1053+YF_API YB_NONNULL(2) void
    1054+StreamPut(std::ostream&, const char*);
    13311055
    13321056
    13331057 /*!
    @@ -1343,7 +1067,6 @@
    13431067 */
    13441068 YB_ATTR_nodiscard YF_API YB_NONNULL(1) UniqueFile
    13451069 EnsureUniqueFile(const char*, mode_t = DefaultPMode(), size_t = 1, bool = {});
    1346-//@}
    13471070
    13481071 /*!
    13491072 \brief 比较文件内容相等。
    @@ -1373,35 +1096,6 @@
    13731096 HaveSameContents(UniqueFile, UniqueFile, const char*, const char*);
    13741097 //@}
    13751098
    1376-/*!
    1377-\brief 判断参数是否表示共享的文件节点。
    1378-\note 可能设置 errno 。
    1379-\since build 638
    1380-*/
    1381-//@{
    1382-YB_ATTR_nodiscard YB_PURE yconstfn
    1383- PDefH(bool, IsNodeShared, const FileNodeID& x, const FileNodeID& y) ynothrow
    1384- ImplRet(x != FileNodeID() && x == y)
    1385-/*!
    1386-\pre 间接断言:字符串参数非空。
    1387-\note 最后参数表示跟踪连接。
    1388-\since build 660
    1389-*/
    1390-//@{
    1391-YB_ATTR_nodiscard YF_API YB_NONNULL(1, 2) bool
    1392-IsNodeShared(const char*, const char*, bool = true) ynothrowv;
    1393-YB_ATTR_nodiscard YF_API YB_NONNULL(1, 2) bool
    1394-IsNodeShared(const char16_t*, const char16_t*, bool = true) ynothrowv;
    1395-//@}
    1396-/*!
    1397-\note 取节点失败视为不共享。
    1398-\sa FileDescriptor::GetNodeID
    1399-\since build 846
    1400-*/
    1401-YB_ATTR_nodiscard YF_API bool
    1402-IsNodeShared(FileDescriptor, FileDescriptor) ynothrow;
    1403-//@}
    1404-
    14051099 } // namespace platform;
    14061100
    14071101 namespace platform_ex
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/include/YCLib/FileSystem.h
    --- a/YFramework/include/YCLib/FileSystem.h Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/include/YCLib/FileSystem.h Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file FileSystem.h
    1212 \ingroup YCLib
    1313 \brief 平台相关的文件系统接口。
    14-\version r3889
    14+\version r4195
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 312
    1717 \par 创建时间:
    1818 2012-05-30 22:38:37 +0800
    1919 \par 修改时间:
    20- 2020-10-08 13:19 +0800
    20+ 2020-10-23 15:39 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -29,16 +29,17 @@
    2929 #define YCL_INC_FileSystem_h_ 1
    3030
    3131 #include "YModules.h"
    32-#include YFM_YCLib_Container // for basic_string_view, ystdex::is_null,
    33-// string_view_t, ystdex::exclude_self_t, std::wint_t, ystdex::to_array,
    34-// ystdex::string_traits, ystdex::rtrim, string, u16string, std::uint8_t,
    35-// std::uint32_t, std::memcmp, pair, size, tuple;
    32+#include YFM_YCLib_Debug // for Nonnull, Deref, basic_string_view,
    33+// ystdex::is_null, string_view_t, ystdex::exclude_self_t, std::wint_t,
    34+// ystdex::to_array, ystdex::string_traits, ystdex::rtrim, string, u16string,
    35+// ystdex::retry_for_vector, std::FILE, std::uint8_t, std::uint32_t,
    36+// std::memcmp, pair, size, tuple;
    3637 #include YFM_YCLib_Reference // for unique_ptr_from, tidy_ptr;
    3738 #include <system_error> // for std::system_error;
    3839 #include <ystdex/base.h> // for ystdex::deref_self;
    3940 #include <ystdex/iterator.hpp> // for ystdex::indirect_input_iterator;
    4041 #include <ctime> // for std::time_t;
    41-#include YFM_YCLib_Debug // for Nonnull, Deref;
    42+#include <chrono> // for std::chrono::nanoseconds;
    4243 #include <ystdex/function.hpp> // for ystdex::function;
    4344 #include <ystdex/type_pun.hpp> // for ystdex::is_trivially_replaceable;
    4445 #include <ystdex/cstdint.hpp> // for ystdex::read_uint_le;
    @@ -56,7 +57,6 @@
    5657 {
    5758
    5859 //! \since build 654
    59-//@{
    6060 template<typename _tChar>
    6161 YB_ATTR_nodiscard YB_STATELESS yconstfn bool
    6262 IsColon(_tChar c) ynothrow
    @@ -64,6 +64,7 @@
    6464 return c == _tChar(':');
    6565 }
    6666
    67+//! \since build 654
    6768 template<typename _tChar>
    6869 YB_ATTR_nodiscard YB_PURE YB_NONNULL(1) inline const _tChar*
    6970 FindColon(const _tChar* p) ynothrowv
    @@ -131,6 +132,8 @@
    131132 \note 空路径不是绝对路径。
    132133 */
    133134 //@{
    135+//! \since build 654
    136+//@{
    134137 template<typename _tChar>
    135138 YB_ATTR_nodiscard YB_PURE YB_NONNULL(2) inline bool
    136139 IsAbsolute_P(IDTag<YF_Platform_DS> tag, const _tChar* path) ynothrowv
    @@ -178,11 +181,14 @@
    178181 YAssertNonnull(path.data());
    179182 return !path.empty() && platform::IsSeparator_P(tag, path.front());
    180183 }
    184+//@}
    181185
    182186 //! \since build 652
    183187 YCL_DefPlatformFwdTmpl(IsAbsolute, IsAbsolute_P)
    184188 //@}
    185189
    190+//! \since build 836
    191+//@{
    186192 #define YCL_Impl_DefPlatformStringFwdTmpl(_fn) \
    187193 template<class _tTag, class _tString> \
    188194 inline auto \
    @@ -196,7 +202,6 @@
    196202 /*!
    197203 \brief 取指定路径的文件系统根节点名称的结束位置。
    198204 \note 不计入可能存在的紧随在根名称后的一个或多个文件分隔符。
    199-\since build 836
    200205 */
    201206 //@{
    202207 template<typename _tChar>
    @@ -221,12 +226,15 @@
    221226
    222227 YCL_DefPlatformFwdTmpl(FetchRootNameEnd, FetchRootNameEnd_P)
    223228 //@}
    229+//@}
    224230
    225231 /*!
    226232 \brief 取指定路径的文件系统根节点名称的长度。
    227233 \note 不计入可能存在的紧随在根名称后的一个或多个文件分隔符。
    228234 */
    229235 //@{
    236+//! \since build 654
    237+//@{
    230238 template<typename _tChar>
    231239 YB_ATTR_nodiscard YB_PURE YB_NONNULL(2) inline size_t
    232240 FetchRootNameLength_P(IDTag<YF_Platform_DS> tag, const _tChar* path) ynothrowv
    @@ -325,6 +333,7 @@
    325333 YAssertNonnull(path.data());
    326334 return !path.empty() && platform::IsSeparator_P(tag, path.front()) ? 1 : 0;
    327335 }
    336+//@}
    328337 //! \since build 836
    329338 YCL_Impl_DefPlatformStringFwdTmpl(FetchRootNameLength_P)
    330339
    @@ -539,6 +548,29 @@
    539548 //@}
    540549
    541550
    551+//! \since build 901
    552+class FileDescriptor;
    553+
    554+/*!
    555+\brief 取指定的文件的链接数。
    556+\return 指定的文件在文件系统中共享的实例数。
    557+*/
    558+//@{
    559+//! \since build 901
    560+YB_ATTR_nodiscard YF_API size_t
    561+FetchNumberOfLinks(const FileDescriptor&);
    562+/*!
    563+\note 第二参数表示跟随连接:若文件系统支持,访问链接的文件而不是链接自身。
    564+\since build 846
    565+*/
    566+//@{
    567+YB_ATTR_nodiscard YF_API YB_NONNULL(1) size_t
    568+FetchNumberOfLinks(const char*, bool = {});
    569+YB_ATTR_nodiscard YF_API YB_NONNULL(1) size_t
    570+FetchNumberOfLinks(const char16_t*, bool = {});
    571+//@}
    572+//@}
    573+
    542574 /*!
    543575 \pre 断言:路径指针非空。
    544576 \exception Win32Exception Win32 平台:本机 API 调用失败。
    @@ -856,6 +888,299 @@
    856888
    857889
    858890 /*!
    891+\pre 断言:第一参数非空。
    892+\note 若在发生其它错误前存储分配失败,设置 errno 为 \c ENOMEM 。
    893+*/
    894+//@{
    895+/*!
    896+\brief 测试路径可访问性。
    897+\param path 路径,意义同 POSIX <tt>::open</tt> 。
    898+\param amode 模式,基本语义同 POSIX.1 2004 ,具体行为取决于实现。 。
    899+\note errno 在出错时会被设置,具体值由实现定义。
    900+\since build 669
    901+*/
    902+//@{
    903+YB_ATTR_nodiscard YF_API YB_NONNULL(1) int
    904+uaccess(const char* path, int amode) ynothrowv;
    905+YB_ATTR_nodiscard YF_API YB_NONNULL(1) int
    906+uaccess(const char16_t* path, int amode) ynothrowv;
    907+//@}
    908+
    909+/*!
    910+\brief 取当前工作目录复制至指定缓冲区中。
    911+\param size 缓冲区长。
    912+\return 若成功为第一参数,否则为空指针。
    913+\note 基本语义同 POSIX.1 2004 的 \c ::getcwd 。
    914+\note 若存储分配失败,设置 errno 为 \c ENOMEM 。
    915+\note Win32 平台:当且仅当结果为根目录时以分隔符结束。
    916+\note 其它平台:未指定结果是否以分隔符结束。
    917+\since build 699
    918+*/
    919+//@{
    920+//! \param buf UTF-8 缓冲区起始指针。
    921+YB_ATTR_nodiscard YF_API YB_NONNULL(1) char*
    922+ugetcwd(char* buf, size_t size) ynothrowv;
    923+//! \param buf UCS-2 缓冲区起始指针。
    924+YB_ATTR_nodiscard YF_API YB_NONNULL(1) char16_t*
    925+ugetcwd(char16_t* buf, size_t size) ynothrowv;
    926+//@}
    927+
    928+/*!
    929+\return 操作是否成功。
    930+\note errno 在出错时会被设置,具体值除以上明确的外,由实现定义。
    931+\note 参数表示路径,使用 UTF-8 编码。
    932+\note DS 使用 newlib 实现。MinGW32 使用 MSVCRT 实现。Android 使用 bionic 实现。
    933+ 其它 Linux 使用 GLibC 实现。
    934+*/
    935+//@{
    936+/*!
    937+\brief 切换当前工作路径至指定路径。
    938+\note POSIX 平台:除路径和返回值外语义同 \c ::chdir 。
    939+*/
    940+YB_ATTR_nodiscard YF_API YB_NONNULL(1) bool
    941+uchdir(const char*) ynothrowv;
    942+
    943+/*!
    944+\brief 按路径以默认权限新建一个目录。
    945+\note 权限由实现定义: DS 使用最大权限; MinGW32 使用 \c ::_wmkdir 指定的默认权限。
    946+*/
    947+YB_ATTR_nodiscard YF_API YB_NONNULL(1) bool
    948+umkdir(const char*) ynothrowv;
    949+
    950+/*!
    951+\brief 按路径删除一个空目录。
    952+\note POSIX 平台:除路径和返回值外语义同 \c ::rmdir 。
    953+*/
    954+YB_ATTR_nodiscard YF_API YB_NONNULL(1) bool
    955+urmdir(const char*) ynothrowv;
    956+
    957+/*!
    958+\brief 按路径删除一个非目录文件。
    959+\note POSIX 平台:除路径和返回值外语义同 \c ::unlink 。
    960+\note Win32 平台:支持移除只读文件,但删除打开的文件总是失败。
    961+*/
    962+YB_ATTR_nodiscard YF_API YB_NONNULL(1) bool
    963+uunlink(const char*) ynothrowv;
    964+
    965+/*!
    966+\brief 按路径移除一个文件。
    967+\note POSIX 平台:除路径和返回值外语义同 \c ::remove 。移除非空目录总是失败。
    968+\note Win32 平台:支持移除空目录和只读文件,但删除打开的文件总是失败。
    969+\see https://msdn.microsoft.com/en-us/library/kc07117k.aspx 。
    970+*/
    971+YB_ATTR_nodiscard YF_API YB_NONNULL(1) bool
    972+uremove(const char*) ynothrowv;
    973+//@}
    974+//@}
    975+
    976+
    977+//! \since build 713
    978+//@{
    979+/*!
    980+\brief 尝试按指定的起始缓冲区大小取当前工作目录的路径。
    981+\pre 间接断言:参数不等于 0 。
    982+\note 未指定结果是否以分隔符结束。
    983+*/
    984+template<typename _tChar>
    985+YB_ATTR_nodiscard basic_string<_tChar>
    986+FetchCurrentWorkingDirectory(size_t init)
    987+{
    988+ return ystdex::retry_for_vector<basic_string<_tChar>>(init,
    989+ [](basic_string<_tChar>& res, size_t) -> bool{
    990+ const auto r(platform::ugetcwd(&res[0], res.length()));
    991+
    992+ if(r)
    993+ {
    994+ res = r;
    995+ return {};
    996+ }
    997+
    998+ const int err(errno);
    999+
    1000+ if(err != ERANGE)
    1001+ ystdex::throw_error(err, yfsig);
    1002+ return true;
    1003+ });
    1004+}
    1005+#if YCL_Win32
    1006+//! \note 参数被忽略。
    1007+//@{
    1008+template<>
    1009+YB_ATTR_nodiscard YF_API string
    1010+FetchCurrentWorkingDirectory(size_t);
    1011+template<>
    1012+YB_ATTR_nodiscard YF_API u16string
    1013+FetchCurrentWorkingDirectory(size_t);
    1014+//@}
    1015+#endif
    1016+//@}
    1017+
    1018+
    1019+//! \since build 638
    1020+//@{
    1021+//! \brief 文件节点标识类型。
    1022+//@{
    1023+#if YCL_Win32
    1024+using FileNodeID = pair<std::uint32_t, std::uint64_t>;
    1025+#else
    1026+using FileNodeID = pair<std::uint64_t, std::uint64_t>;
    1027+#endif
    1028+//@}
    1029+
    1030+/*!
    1031+\bug 结构化类型污染。
    1032+\relates FileNodeID
    1033+\since build 901
    1034+*/
    1035+//@{
    1036+YB_ATTR_nodiscard yconstfn
    1037+ PDefHOp(bool, ==, const FileNodeID& x, const FileNodeID& y) ynothrow
    1038+ ImplRet(x.first == y.first && x.second == y.second)
    1039+YB_ATTR_nodiscard yconstfn
    1040+ PDefHOp(bool, !=, const FileNodeID& x, const FileNodeID& y) ynothrow
    1041+ ImplRet(!(x == y))
    1042+//@}
    1043+//@}
    1044+
    1045+/*!
    1046+\brief 取文件系统节点标识。
    1047+\since build 638
    1048+*/
    1049+YB_ATTR_nodiscard FileNodeID
    1050+GetFileNodeIDOf(const FileDescriptor&) ynothrow;
    1051+
    1052+/*!
    1053+\brief 判断参数是否表示共享的文件节点。
    1054+\note 可能设置 errno 。
    1055+\since build 638
    1056+*/
    1057+//@{
    1058+YB_ATTR_nodiscard YB_PURE yconstfn
    1059+ PDefH(bool, IsNodeShared, const FileNodeID& x, const FileNodeID& y) ynothrow
    1060+ ImplRet(x != FileNodeID() && x == y)
    1061+/*!
    1062+\pre 间接断言:字符串参数非空。
    1063+\note 最后参数表示跟踪连接。
    1064+\since build 660
    1065+*/
    1066+//@{
    1067+YB_ATTR_nodiscard YF_API YB_NONNULL(1, 2) bool
    1068+IsNodeShared(const char*, const char*, bool = true) ynothrowv;
    1069+YB_ATTR_nodiscard YF_API YB_NONNULL(1, 2) bool
    1070+IsNodeShared(const char16_t*, const char16_t*, bool = true) ynothrowv;
    1071+//@}
    1072+/*!
    1073+\note 取节点失败视为不共享。
    1074+\sa FileDescriptor::GetNodeID
    1075+\since build 846
    1076+*/
    1077+YB_ATTR_nodiscard YF_API bool
    1078+IsNodeShared(FileDescriptor, FileDescriptor) ynothrow;
    1079+//@}
    1080+
    1081+
    1082+//! \since build 628
    1083+using FileTime = std::chrono::nanoseconds;
    1084+
    1085+/*!
    1086+\return 以 POSIX 时间相同历元的时间间隔。
    1087+\note 当前 Windows 使用 \c ::GetFileTime 实现,其它只保证最高精确到秒。
    1088+\exception std::system_error 参数无效或文件时间查询失败。
    1089+*/
    1090+//@{
    1091+/*!
    1092+\brief 取访问时间。
    1093+\since build 631
    1094+*/
    1095+//@{
    1096+//! \since build 901
    1097+YB_ATTR_nodiscard YF_API FileTime
    1098+GetFileAccessTimeOf(const FileDescriptor&);
    1099+YB_ATTR_nodiscard YF_API YB_NONNULL(1) FileTime
    1100+GetFileAccessTimeOf(std::FILE*);
    1101+/*!
    1102+\pre 断言:第一参数非空。
    1103+\note 最后参数表示跟随连接:若文件系统支持,访问链接的文件而不是链接自身。
    1104+*/
    1105+//@{
    1106+YB_ATTR_nodiscard YF_API YB_NONNULL(1) FileTime
    1107+GetFileAccessTimeOf(const char*, bool = {});
    1108+YB_ATTR_nodiscard YF_API YB_NONNULL(1) FileTime
    1109+GetFileAccessTimeOf(const char16_t*, bool = {});
    1110+//@}
    1111+//@}
    1112+
    1113+/*!
    1114+\brief 取文件修改时间。
    1115+\since build 628
    1116+*/
    1117+//@{
    1118+//! \since build 901
    1119+YB_ATTR_nodiscard YF_API FileTime
    1120+GetFileModificationTimeOf(const FileDescriptor&);
    1121+YB_ATTR_nodiscard YF_API YB_NONNULL(1) FileTime
    1122+GetFileModificationTimeOf(std::FILE*);
    1123+
    1124+/*!
    1125+\pre 断言:第一参数非空。
    1126+\note 最后参数表示跟随连接:若文件系统支持,访问链接的文件而不是链接自身。
    1127+*/
    1128+//@{
    1129+YB_ATTR_nodiscard YF_API YB_NONNULL(1) FileTime
    1130+GetFileModificationTimeOf(const char*, bool = {});
    1131+YB_ATTR_nodiscard YF_API YB_NONNULL(1) FileTime
    1132+GetFileModificationTimeOf(const char16_t*, bool = {});
    1133+//@}
    1134+//@}
    1135+
    1136+/*!
    1137+\brief 取修改和访问时间。
    1138+\since build 631
    1139+*/
    1140+//@{
    1141+//! \since build 901
    1142+YB_ATTR_nodiscard YF_API array<FileTime, 2>
    1143+GetFileModificationAndAccessTimeOf(const FileDescriptor&);
    1144+YB_ATTR_nodiscard YF_API YB_NONNULL(1) array<FileTime, 2>
    1145+GetFileModificationAndAccessTimeOf(std::FILE*);
    1146+/*!
    1147+\pre 断言:第一参数非空。
    1148+\note 最后参数表示跟随连接:若文件系统支持,访问链接的文件而不是链接自身。
    1149+*/
    1150+//@{
    1151+YB_ATTR_nodiscard YF_API YB_NONNULL(1) array<FileTime, 2>
    1152+GetFileModificationAndAccessTimeOf(const char*, bool = {});
    1153+YB_ATTR_nodiscard YF_API YB_NONNULL(1) array<FileTime, 2>
    1154+GetFileModificationAndAccessTimeOf(const char16_t*, bool = {});
    1155+//@}
    1156+//@}
    1157+//@}
    1158+
    1159+/*!
    1160+\throw std::system_error 设置失败。
    1161+\note DS 平台:不支持操作。
    1162+\since build 901
    1163+*/
    1164+//@{
    1165+/*!
    1166+\brief 设置访问时间。
    1167+\throw std::system_error 设置失败。
    1168+*/
    1169+YF_API void
    1170+SetFileAccessTimeOf(const FileDescriptor&, FileTime);
    1171+//! \note Win32 平台:要求打开的文件具有写权限。
    1172+//@{
    1173+//! \brief 设置修改时间。
    1174+YF_API void
    1175+SetFileModificationTimeOf(const FileDescriptor&, FileTime);
    1176+//! \brief 设置修改和访问时间。
    1177+YF_API void
    1178+SetFileModificationAndAccessTimeOf(const FileDescriptor&, array<FileTime, 2>);
    1179+//@}
    1180+//@}
    1181+
    1182+
    1183+/*!
    8591184 \brief 已知文件系统类型。
    8601185 \since build 607
    8611186 */
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/include/YCLib/NativeAPI.h
    --- a/YFramework/include/YCLib/NativeAPI.h Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/include/YCLib/NativeAPI.h Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file NativeAPI.h
    1212 \ingroup YCLib
    1313 \brief 通用平台应用程序接口描述。
    14-\version r1659
    14+\version r1671
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 202
    1717 \par 创建时间:
    1818 2011-04-13 20:26:21 +0800
    1919 \par 修改时间:
    20- 2019-10-10 18:38 +0800
    20+ 2019-10-25 08:38 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -617,7 +617,8 @@
    617617 # endif
    618618
    619619 # include <Windows.h>
    620-# if __has_include(<specstrings_undef.h>)
    620+// XXX: This may have effects on the system headers included later.
    621+# if false && __has_include(<specstrings_undef.h>)
    621622 # include <specstrings_undef.h>
    622623 # else
    623624 // NOTE: Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97362.
    @@ -669,11 +670,21 @@
    669670 {
    670671
    671672 /*!
    673+\brief 取 ::_get_osfhandle 返回值对应的句柄。
    674+\note 检查特殊值 -2 。这个值仅在较新版本的 CRT 文档中被明确。
    675+\since build 901
    676+\see https://docs.microsoft.com/en-us/previous-versions/ks2530z6(v=vs.140) 。
    677+\see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle?view=vs-2019 。
    678+*/
    679+inline PDefH(::HANDLE, IntPtrToHandle, std::intptr_t h) ynothrow
    680+ ImplRet(h != -2 ? ::HANDLE(h) : INVALID_HANDLE_VALUE)
    681+
    682+/*!
    672683 \brief 取文件描述符对应的句柄。
    673684 \since build 704
    674685 */
    675686 inline PDefH(::HANDLE, ToHandle, int fd) ynothrow
    676- ImplRet(::HANDLE(::_get_osfhandle(fd)))
    687+ ImplRet(IntPtrToHandle(::_get_osfhandle(fd)))
    677688
    678689 } // namespace platform_ex;
    679690
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/include/YSLib/Adaptor/YAdaptor.h
    --- a/YFramework/include/YSLib/Adaptor/YAdaptor.h Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/include/YSLib/Adaptor/YAdaptor.h Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file YAdaptor.h
    1212 \ingroup Adaptor
    1313 \brief 外部库关联。
    14-\version r2430
    14+\version r2444
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since 早于 build 132
    1717 \par 创建时间:
    1818 2010-02-22 20:16:21 +0800
    1919 \par 修改时间:
    20- 2020-08-30 19:37 +0800
    20+ 2020-10-23 05:55 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -38,15 +38,16 @@
    3838 // <array>, <deque>, <forward_list>, <iosfwd>, <istream>, <ostream>, <queue>,
    3939 // <set>, <stack>, <unordered_map>, <unordered_set>, <vector>,
    4040 // <ystdex/memory_resource.h>, platform::basic_types, YFM_YCLib_Container,
    41-// platform::basic_utilities, platform::containers, '*string_view', uopen,
    42-// 'uf*', 'up*', etc;
    41+// platform::basic_utilities, platform::containers, '*string_view',
    42+// MakePathString, uopen, 'uf*', 'up*', 'omode_conv*', StreamPut, etc;
    4343 #include YFM_YCLib_Reference // for YFM_YCLib_Reference, platform::references,
    4444 // <memory>, '*_ptr', 'make*_ptr', lref;
    4545 #include YFM_YCLib_Keys // for <bitset>, YCLib key space;
    4646 #include YFM_YCLib_Timer
    4747 #include YFM_YCLib_Mutex // for YFM_YCLib_Mutex, ystdex::noncopyable,
    4848 // ystdex::nonmovable, Threading, Concurrency;
    49-#include YFM_YCLib_FileSystem
    49+#include YFM_YCLib_FileSystem // for 'u*dir', platform::FileTime, 'GetFile*Of',
    50+// 'SetFile*Of', etc;
    5051 #include YFM_YCLib_MemoryMapping // for MappedFile;
    5152 #include YFM_YCLib_Video // for MonoType, AlphaType, Color;
    5253
    @@ -321,12 +322,22 @@
    321322 using platform::DefaultPMode;
    322323 using platform::omode_conv;
    323324 using platform::omode_convb;
    325+//! \since build 901
    326+using platform::StreamPut;
    327+//! \since build 901
    328+using platform::FileTime;
    324329 //! \since build 631
    325330 using platform::GetFileAccessTimeOf;
    326331 //! \since build 547
    327332 using platform::GetFileModificationTimeOf;
    328333 //! \since build 631
    329334 using platform::GetFileModificationAndAccessTimeOf;
    335+//! \since build 901
    336+//@{
    337+using platform::SetFileAccessTimeOf;
    338+using platform::SetFileModificationTimeOf;
    339+using platform::SetFileModificationAndAccessTimeOf;
    340+//@}
    330341 using platform::FetchNumberOfLinks;
    331342 using platform::EnsureUniqueFile;
    332343 //! \since build 659
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/include/YSLib/Service/FileSystem.h
    --- a/YFramework/include/YSLib/Service/FileSystem.h Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/include/YSLib/Service/FileSystem.h Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file FileSystem.h
    1212 \ingroup Service
    1313 \brief 平台中立的文件系统抽象。
    14-\version r3492
    14+\version r3498
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 473
    1717 \par 创建时间:
    1818 2010-03-28 00:09:28 +0800
    1919 \par 修改时间:
    20- 2020-10-10 18:46 +0800
    20+ 2020-10-23 04:46 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -743,12 +743,14 @@
    743743 //@{
    744744 //! \brief 保持修改时间。
    745745 const auto PreserveModificationTime(
    746- ystdex::bind_forward(&FileDescriptor::SetModificationTime,
    747- &FileDescriptor::GetModificationTime));
    746+ ystdex::bind_forward(SetFileModificationTimeOf,
    747+ static_cast<FileTime(&)(const FileDescriptor&)>(
    748+ GetFileModificationTimeOf)));
    748749 //! \brief 保持修改和访问时间。
    749750 const auto PreserveModificationAndAccessTime(
    750- ystdex::bind_forward(&FileDescriptor::SetModificationAndAccessTime,
    751- &FileDescriptor::GetModificationAndAccessTime));
    751+ ystdex::bind_forward(SetFileModificationAndAccessTimeOf,
    752+ static_cast<array<FileTime,
    753+ 2>(&)(const FileDescriptor&)>(GetFileModificationAndAccessTimeOf)));
    752754 //@}
    753755 //@}
    754756
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/source/NPL/Dependency.cpp
    --- a/YFramework/source/NPL/Dependency.cpp Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/source/NPL/Dependency.cpp Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file Dependency.cpp
    1212 \ingroup NPL
    1313 \brief 依赖管理。
    14-\version r3783
    14+\version r3792
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 623
    1717 \par 创建时间:
    1818 2015-08-09 22:14:45 +0800
    1919 \par 修改时间:
    20- 2020-10-11 21:48 +0800
    20+ 2020-10-25 06:11 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -32,7 +32,8 @@
    3232 // NPL::forward_as_tuple, LiftOther, Collapse, LiftOtherOrCopy, NPL::IsMovable,
    3333 // LiftTermOrCopy, ResolveTerm, LiftTermValueOrCopy, MoveResolved,
    3434 // ResolveIdentifier, ystdex::plus, std::placeholders, NPL::ResolveRegular,
    35-// ystdex::tolower, ystdex::swap_dependent, LiftTermRef, LiftTerm, NPL::Deref;
    35+// ystdex::tolower, ystdex::swap_dependent, LiftTermRef, LiftTerm, NPL::Deref,
    36+// YSLib::IO::StreamPut;
    3637 #include YFM_NPL_NPLA1Forms // for NPL::Forms functions;
    3738 #include YFM_YSLib_Service_FileSystem // for YSLib::IO::*;
    3839 #include <ystdex/iterator.hpp> // for std::istreambuf_iterator,
    @@ -40,8 +41,8 @@
    4041 #include YFM_YSLib_Service_TextFile // for IO::SharedInputMappedFileStream,
    4142 // Text::OpenSkippedBOMtream;
    4243 #include <cerrno> // for errno, ERANGE;
    43-#include <cstdio> // for std::puts;
    4444 #include <regex> // for std::regex, std::regex_match;
    45+#include <ostream> // for std::endl;
    4546 #include "NPLA1Internals.h" // for NPL_Impl_NPLA1_Enable_Thunked;
    4647 #include YFM_YSLib_Core_YCoreUtilities // for FetchCommandOutput;
    4748 #include <ystdex/string.hpp> // for ystdex::begins_with;
    @@ -1147,10 +1148,12 @@
    11471148 {
    11481149 auto& renv(context.Root.GetRecordRef());
    11491150
    1150- RegisterUnary<Strict, const string>(renv, "puts", [](const string& str){
    1151- // FIXME: Use %EncodeArg?
    1152- // XXX: Error from 'std::puts' is ignored.
    1153- std::puts(str.c_str());
    1151+ RegisterUnary<Strict, const string>(renv, "puts", [&](const string& str){
    1152+ auto& os(context.GetOutputStreamRef());
    1153+
    1154+ YSLib::IO::StreamPut(os, str.c_str());
    1155+ if(os)
    1156+ os << std::endl;
    11541157 return ValueToken::Unspecified;
    11551158 });
    11561159 #if true
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/source/NPL/NPLA1.cpp
    --- a/YFramework/source/NPL/NPLA1.cpp Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/source/NPL/NPLA1.cpp Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file NPLA1.cpp
    1212 \ingroup NPL
    1313 \brief NPLA1 公共接口。
    14-\version r20086
    14+\version r20096
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 473
    1717 \par 创建时间:
    1818 2014-02-02 18:02:47 +0800
    1919 \par 修改时间:
    20- 2020-10-06 22:04 +0800
    20+ 2020-10-19 05:23 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -44,6 +44,7 @@
    4444 // YSLib::share_move, ystdex::call_value_or, Session;
    4545 #include "NPLA1Internals.h" // for A1::Internals API;
    4646 #include YFM_NPL_Dependency // for A1::OpenUnique;
    47+#include <ystdex/exception.h> // for ystdex::unsupported;
    4748
    4849 using namespace YSLib;
    4950
    @@ -823,8 +824,8 @@
    823824 TermNode&
    824825 ContextState::GetNextTermRef() const
    825826 {
    826- if(const auto p = next_term_ptr)
    827- return *p;
    827+ if(next_term_ptr)
    828+ return *next_term_ptr;
    828829 // NOTE: This should not occur unless there exists some invalid low-level
    829830 // interoperations on the next term pointer in the context.
    830831 throw NPLException("No next term found to evaluation.");
    @@ -1709,6 +1710,14 @@
    17091710 return NPL_Impl_NPLA1_Enable_Thunked;
    17101711 }
    17111712
    1713+std::ostream&
    1714+REPLContext::GetOutputStreamRef() const
    1715+{
    1716+ if(OutputStreamPtr)
    1717+ return *OutputStreamPtr;
    1718+ throw ystdex::unsupported("Unsupported output stream found.");
    1719+}
    1720+
    17121721 TermNode
    17131722 REPLContext::DefaultLoad(REPLContext& context, ContextNode& ctx,
    17141723 string filename)
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/source/NPL/NPLA1Forms.cpp
    --- a/YFramework/source/NPL/NPLA1Forms.cpp Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/source/NPL/NPLA1Forms.cpp Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file NPLA1Forms.cpp
    1212 \ingroup NPL
    1313 \brief NPLA1 语法形式。
    14-\version r19493
    14+\version r19495
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 882
    1717 \par 创建时间:
    1818 2014-02-15 11:19:51 +0800
    1919 \par 修改时间:
    20- 2020-10-11 21:44 +0800
    20+ 2020-10-15 03:01 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -1957,10 +1957,10 @@
    19571957 term.emplace(std::move(sum));
    19581958 sum.Value.Clear();
    19591959 rterm.GetContainerRef().emplace_back();
    1960- RelaySwitched(ctx, [&]{
    1960+ RelaySwitched(ctx, A1::NameTypedReducerHandler([&]{
    19611961 LiftOther(term, rterm);
    19621962 return ctx.LastStatus;
    1963- });
    1963+ }, "eval-lift-sum"));
    19641964 }
    19651965
    19661966 template<typename _func>
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/source/YCLib/FileIO.cpp
    --- a/YFramework/source/YCLib/FileIO.cpp Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/source/YCLib/FileIO.cpp Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file FileIO.cpp
    1212 \ingroup YCLib
    1313 \brief 平台相关的文件访问和输入/输出接口。
    14-\version r3180
    14+\version r3696
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 615
    1717 \par 创建时间:
    1818 2015-07-14 18:53:12 +0800
    1919 \par 修改时间:
    20- 2020-01-12 18:14 +0800
    20+ 2020-10-25 06:07 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -32,12 +32,13 @@
    3232 // std::errc::function_not_supported, YCL_CallF_CAPI, std::is_integral,
    3333 // ystdex::invoke, Nonnull, ystdex::temporary_buffer;
    3434 #include YFM_YCLib_NativeAPI // for Mode, ::HANDLE, struct ::stat,
    35-// platform_ex::cstat, platform_ex::estat, ::futimens, OpenMode,
    35+// platform_ex::cstat, platform_ex::estat ::GetConsoleMode, OpenMode,
    3636 // YCL_CallGlobal, ::close, ::fcntl, F_GETFL, ::setmode, ::fchmod, ::_chsize,
    3737 // ::ftruncate, ::fsync, ::_wgetcwd, ::getcwd, ::chdir, ::rmdir, ::unlink,
    3838 // !defined(__STRICT_ANSI__) API, ::GetCurrentDirectoryW;
    3939 #include YFM_YCLib_FileSystem // for NodeCategory::*, CategorizeNode;
    4040 #include <ystdex/functional.hpp> // for ystdex::compose, ystdex::addrof;
    41+#include <ystdex/string.hpp> // for ystdex::write_ntcts;
    4142 #include <ystdex/streambuf.hpp> // for ystdex::streambuf_equal;
    4243 #if YCL_DS
    4344 # include "CHRLib/YModules.h"
    @@ -51,27 +52,18 @@
    5152 // available if it is really being used.
    5253 # undef _fileno
    5354 # endif
    54-# include YFM_Win32_YCLib_MinGW32 // for platform_ex::FileAttributes,
    55-// platform_ex::GetErrnoFromWin32, platform_ex::QueryFileLinks,
    56-// platform_ex::QueryFileNodeID, platform_ex::QueryFileTime,
    57-// platform_ex::ConvertTime, platform_ex::SetFileTime, platform_ex::UnlockFile,
    58-// platform_ex::LockFile, platform_ex::TryLockFile;
    55+# if __GLIBCXX__
    56+# include <ystdex/ios.hpp> // for ystdex::rethrow_badstate;
    57+# include YFM_Win32_YCLib_Consoles // for platform_ex::WConsole;
    58+# include <ext/stdio_sync_filebuf.h> // for __gnu_cxx::stdio_sync_filebuf;
    59+# endif
    60+# include YFM_Win32_YCLib_MinGW32 // for
    61+// platform_ex::UnlockFile, platform_ex::LockFile, platform_ex::TryLockFile;
    5962 # include YFM_Win32_YCLib_NLS // for platform_ex::UTF8ToWCS,
    6063 // platform_ex::WCSToUTF8;
    6164
    62-//! \since build 639
    63-using platform_ex::FileAttributes;
    64-using platform_ex::GetErrnoFromWin32;
    65-//! \since build 639
    66-using platform_ex::QueryFileLinks;
    67-//! \since build 632
    68-//@{
    69-using platform_ex::QueryFileNodeID;
    70-using platform_ex::QueryFileTime;
    7165 //! \since build 540
    7266 using platform_ex::UTF8ToWCS;
    73-using platform_ex::ConvertTime;
    74-//@}
    7567 //! \since build 706
    7668 using platform_ex::MakePathStringW;
    7769 #elif YCL_API_POSIXFileSystem
    @@ -103,157 +95,7 @@
    10395 #if YCL_Win32
    10496 //! \since build 704
    10597 using platform_ex::ToHandle;
    106-
    107-//! \since build 660
    108-void
    109-QueryFileTime(const char* path, ::FILETIME* p_ctime, ::FILETIME* p_atime,
    110- ::FILETIME* p_mtime, bool follow_reparse_point)
    111-{
    112- QueryFileTime(MakePathStringW(path).c_str(), p_ctime, p_atime, p_mtime,
    113- follow_reparse_point);
    114-}
    115-//! \since build 632
    116-//@{
    117-void
    118-QueryFileTime(int fd, ::FILETIME* p_ctime, ::FILETIME* p_atime,
    119- ::FILETIME* p_mtime)
    120-{
    121- QueryFileTime(ToHandle(fd), p_ctime, p_atime, p_mtime);
    122-}
    123-
    124-// TODO: Blocked. Use C++14 generic lambda expressions.
    125-yconstexpr const struct
    126-{
    127- template<typename _tParam, typename... _tParams>
    128- FileTime
    129- operator()(_tParam arg, _tParams... args) const
    130- {
    131- ::FILETIME atime{};
    132-
    133- QueryFileTime(arg, {}, &atime, {}, args...);
    134- return ConvertTime(atime);
    135- }
    136-} get_st_atime{};
    137-yconstexpr const struct
    138-{
    139- template<typename _tParam, typename... _tParams>
    140- FileTime
    141- operator()(_tParam arg, _tParams... args) const
    142- {
    143- ::FILETIME mtime{};
    144-
    145- QueryFileTime(arg, {}, {}, &mtime, args...);
    146- return ConvertTime(mtime);
    147- }
    148-} get_st_mtime{};
    149-yconstexpr const struct
    150-{
    151- template<typename _tParam, typename... _tParams>
    152- array<FileTime, 2>
    153- operator()(_tParam arg, _tParams... args) const
    154- {
    155- ::FILETIME mtime{}, atime{};
    156-
    157- QueryFileTime(arg, {}, &atime, &mtime, args...);
    158- return array<FileTime, 2>{ConvertTime(mtime), ConvertTime(atime)};
    159- }
    160-} get_st_matime{};
    161-//@}
    162-
    163-//! \since build 639
    164-//@{
    165-YB_NONNULL(1) bool
    166-UnlinkWithAttr(const wchar_t* path, FileAttributes attr) ynothrow
    167-{
    168- return !(attr & FileAttributes::ReadOnly) || ::SetFileAttributesW(path,
    169- attr & ~FileAttributes::ReadOnly) ? ::_wunlink(path) == 0
    170- : (errno = GetErrnoFromWin32(), false);
    171-}
    172-
    173-template<typename _func>
    174-YB_NONNULL(2) bool
    175-CallFuncWithAttr(_func f, const char* path)
    176-{
    177- const auto& wpath(MakePathStringW(path));
    178- const auto& wstr(wpath.c_str());
    179- const auto attr(FileAttributes(::GetFileAttributesW(wstr)));
    180-
    181- return attr != platform_ex::Invalid ? f(wstr, attr)
    182- : (errno = GetErrnoFromWin32(), false);
    183-}
    184-//@}
    185-#else
    186-inline PDefH(::timespec, ToTimeSpec, FileTime ft) ynothrow
    187- ImplRet({std::time_t(ft.count() / 1000000000LL),
    188- long(ft.count() % 1000000000LL)})
    189-
    190-//! \since build 719
    191-void
    192-SetFileTime(int fd, const ::timespec(&times)[2])
    193-{
    194-#if YCL_DS
    195- // XXX: Hack.
    196-#ifndef UTIME_OMIT
    197-# define UTIME_OMIT (-1L)
    19898 #endif
    199- yunused(fd), yunused(times);
    200- ystdex::throw_error(std::errc::function_not_supported, yfsig);
    201-#else
    202- YCL_CallF_CAPI(, ::futimens, fd, times);
    203-#endif
    204-}
    205-
    206-//! \since build 631
    207-//@{
    208-const auto get_st_atime([](struct ::stat& st){
    209- return FileTime(st.st_atime);
    210-});
    211-const auto get_st_mtime([](struct ::stat& st){
    212- return FileTime(st.st_mtime);
    213-});
    214-const auto get_st_matime([](struct ::stat& st){
    215- return array<FileTime, 2>{FileTime(st.st_mtime), FileTime(st.st_atime)};
    216-});
    217-//@}
    218-
    219-//! \since build 638
    220-//@{
    221-static_assert(std::is_integral<::dev_t>(),
    222- "Nonconforming '::dev_t' type found.");
    223-static_assert(std::is_unsigned<::ino_t>(),
    224- "Nonconforming '::ino_t' type found.");
    225-
    226-inline PDefH(FileNodeID, get_file_node_id, struct ::stat& st) ynothrow
    227- ImplRet({std::uint64_t(st.st_dev), std::uint64_t(st.st_ino)})
    228-//@}
    229-//! \since build 719
    230-YB_NONNULL(2, 4) inline PDefH(void, cstat, struct ::stat& st,
    231- const char16_t* path, bool follow_link, const char* sig)
    232- ImplRet(cstat(st, MakePathString(path).c_str(), follow_link, sig))
    233-#endif
    234-
    235-//! \since build 632
    236-template<typename _func, typename... _tParams>
    237-auto
    238-FetchFileTime(_func f, _tParams... args)
    239-#if YCL_Win32
    240- -> ystdex::invoke_result_t<_func, _tParams&...>
    241-#else
    242- -> ystdex::invoke_result_t<_func, struct ::stat&>
    243-#endif
    244-{
    245-#if YCL_Win32
    246- // NOTE: The %::FILETIME has resolution of 100 nanoseconds.
    247- // XXX: Error handling for indirect calls.
    248- return f(args...);
    249-#else
    250- // TODO: Get more precise time count.
    251- struct ::stat st;
    252-
    253- cstat(st, args..., yfsig);
    254- return ystdex::invoke(f, st);
    255-#endif
    256-}
    25799
    258100 //! \since build 765
    259101 template<int _vErr, typename _fCallable, class _tObj, typename _tByteBuf,
    @@ -317,44 +159,46 @@
    317159 }
    318160 #endif
    319161
    320-//! \since build 660
    321-bool
    322-IsNodeShared_Impl(const char* a, const char* b, bool follow_link) ynothrow
    162+#if YCL_Win32 && __GLIBCXX__
    163+YB_NONNULL(3) bool
    164+StreamPutToFileDescriptor(std::ostream& os, int fd, const char* s)
    323165 {
    324-#if YCL_Win32
    325- return CallNothrow({}, [=]{
    326- return QueryFileNodeID(MakePathStringW(a).c_str(), follow_link)
    327- == QueryFileNodeID(MakePathStringW(b).c_str(), follow_link);
    328- });
    329-#else
    330- struct ::stat st;
    331-
    332- if(estat(st, a, follow_link) != 0)
    333- return {};
    334-
    335- const auto id(get_file_node_id(st));
    166+ const auto h(ToHandle(fd));
    336167
    337- if(estat(st, b, follow_link) != 0)
    338- return {};
    168+ if(h != INVALID_HANDLE_VALUE)
    169+ {
    170+ unsigned long mode;
    339171
    340- return IsNodeShared(id, get_file_node_id(st));
    341-#endif
    172+ if(::GetConsoleMode(h, &mode))
    173+ {
    174+ if(os.flush())
    175+ {
    176+ std::ios_base::iostate st(std::ios_base::goodbit);
    177+
    178+ if(const auto k{typename std::ostream::sentry(os)})
    179+ {
    180+ if(*s != '\0')
    181+ try
    182+ {
    183+ const auto wstr(platform_ex::UTF8ToWCS(s));
    184+ const auto n(wstr.length());
    185+
    186+ if(platform_ex::WConsole(h).WriteString(wstr) != n)
    187+ st |= std::ios_base::badbit;
    188+ }
    189+ CatchExpr(...,
    190+ ystdex::rethrow_badstate(os, std::ios_base::badbit))
    191+ }
    192+ else
    193+ st |= std::ios_base::badbit;
    194+ os.setstate(st);
    195+ }
    196+ return true;
    197+ }
    198+ }
    199+ return {};
    342200 }
    343-//! \since build 660
    344-bool
    345-IsNodeShared_Impl(const char16_t* a, const char16_t* b, bool follow_link)
    346- ynothrow
    347-{
    348-#if YCL_Win32
    349- return CallNothrow({}, [=]{
    350- return QueryFileNodeID(wcast(a), follow_link)
    351- == QueryFileNodeID(wcast(b), follow_link);
    352- });
    353-#else
    354- return IsNodeShared_Impl(MakePathString(a).c_str(),
    355- MakePathString(b).c_str(), follow_link);
    356201 #endif
    357-}
    358202
    359203 //! \since build 701
    360204 template<typename _tChar>
    @@ -426,11 +270,6 @@
    426270 : desc(fp ? YCL_CallGlobal(fileno, fp) : -1)
    427271 {}
    428272
    429-FileTime
    430-FileDescriptor::GetAccessTime() const
    431-{
    432- return FetchFileTime(get_st_atime, desc);
    433-}
    434273 NodeCategory
    435274 FileDescriptor::GetCategory() const ynothrow
    436275 {
    @@ -460,44 +299,6 @@
    460299 cstat(st, desc, yfsig);
    461300 return st.st_mode;
    462301 }
    463-FileTime
    464-FileDescriptor::GetModificationTime() const
    465-{
    466- return FetchFileTime(get_st_mtime, desc);
    467-}
    468-array<FileTime, 2>
    469-FileDescriptor::GetModificationAndAccessTime() const
    470-{
    471- return FetchFileTime(get_st_matime, desc);
    472-}
    473-FileNodeID
    474-FileDescriptor::GetNodeID() const ynothrow
    475-{
    476-#if YCL_Win32
    477- return CallNothrow({}, [this]{
    478- return QueryFileNodeID(ToHandle(desc));
    479- });
    480-#else
    481- struct ::stat st;
    482-
    483- return estat(st, desc) == 0 ? get_file_node_id(st) : FileNodeID();
    484-#endif
    485-}
    486-size_t
    487-FileDescriptor::GetNumberOfLinks() const ynothrow
    488-{
    489-#if YCL_Win32
    490- return CallNothrow({}, [this]{
    491- return QueryFileLinks(ToHandle(desc));
    492- });
    493-#else
    494- struct ::stat st;
    495- static_assert(std::is_unsigned<decltype(st.st_nlink)>(),
    496- "Unsupported 'st_nlink' type found.");
    497-
    498- return estat(st, desc) == 0 ? size_t(st.st_nlink) : size_t();
    499-#endif
    500-}
    501302 std::uint64_t
    502303 FileDescriptor::GetSize() const
    503304 {
    @@ -516,19 +317,6 @@
    516317 #endif
    517318 }
    518319
    519-void
    520-FileDescriptor::SetAccessTime(FileTime ft) const
    521-{
    522-#if YCL_Win32
    523- auto atime(ConvertTime(ft));
    524-
    525- platform_ex::SetFileTime(ToHandle(desc), {}, &atime, {});
    526-#else
    527- const ::timespec times[]{ToTimeSpec(ft), {yimpl(0), UTIME_OMIT}};
    528-
    529- SetFileTime(desc, times);
    530-#endif
    531-}
    532320 bool
    533321 FileDescriptor::SetBlocking() const
    534322 {
    @@ -564,32 +352,6 @@
    564352 ystdex::throw_error(std::errc::function_not_supported, yfsig);
    565353 #endif
    566354 }
    567-void
    568-FileDescriptor::SetModificationTime(FileTime ft) const
    569-{
    570-#if YCL_Win32
    571- auto mtime(ConvertTime(ft));
    572-
    573- platform_ex::SetFileTime(ToHandle(desc), {}, {}, &mtime);
    574-#else
    575- const ::timespec times[]{{yimpl(0), UTIME_OMIT}, ToTimeSpec(ft)};
    576-
    577- SetFileTime(desc, times);
    578-#endif
    579-}
    580-void
    581-FileDescriptor::SetModificationAndAccessTime(array<FileTime, 2> fts) const
    582-{
    583-#if YCL_Win32
    584- auto atime(ConvertTime(fts[0])), mtime(ConvertTime(fts[1]));
    585-
    586- platform_ex::SetFileTime(ToHandle(desc), {}, &atime, &mtime);
    587-#else
    588- const ::timespec times[]{ToTimeSpec(fts[0]), ToTimeSpec(fts[1])};
    589-
    590- SetFileTime(desc, times);
    591-#endif
    592-}
    593355 bool
    594356 FileDescriptor::SetNonblocking() const
    595357 {
    @@ -844,31 +606,6 @@
    844606
    845607
    846608 int
    847-uaccess(const char* path, int amode) ynothrowv
    848-{
    849- YAssertNonnull(path);
    850-#if YCL_Win32
    851- return CallNothrow(-1, [=]{
    852- return ::_waccess(MakePathStringW(path).c_str(), amode);
    853- });
    854-#else
    855- return ::access(path, amode);
    856-#endif
    857-}
    858-int
    859-uaccess(const char16_t* path, int amode) ynothrowv
    860-{
    861- YAssertNonnull(path);
    862-#if YCL_Win32
    863- return ::_waccess(wcast(path), amode);
    864-#else
    865- return CallNothrow(-1, [=]{
    866- return ::access(MakePathString(path).c_str(), amode);
    867- });
    868-#endif
    869-}
    870-
    871-int
    872609 uopen(const char* filename, int oflag, mode_t pmode) ynothrowv
    873610 {
    874611 YAssertNonnull(filename);
    @@ -1009,239 +746,31 @@
    1009746 #endif
    1010747 }
    1011748
    1012-char*
    1013-ugetcwd(char* buf, size_t size) ynothrowv
    749+
    750+void
    751+StreamPut(std::ostream& os, const char* s)
    1014752 {
    1015- YAssertNonnull(buf);
    1016- if(size != 0)
    753+ YAssertNonnull(s);
    754+#if YCL_Win32
    755+ if(const auto p_sb = os.rdbuf())
    1017756 {
    1018- using namespace std;
    1019-
    1020-#if YCL_Win32
    1021- try
    757+ // TODO: Implement for other standard library implementations.
    758+# if __GLIBCXX__
    759+ if(const auto p = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(p_sb))
    1022760 {
    1023- const auto p(make_unique<wchar_t[]>(size));
    1024-
    1025- if(const auto cwd = ::_wgetcwd(p.get(), int(size)))
    1026- {
    1027- const auto res(MakePathString(ucast(cwd)));
    1028- const auto len(res.length());
    1029-
    1030- if(size < len + 1)
    1031- errno = ERANGE;
    1032- else
    1033- return ystdex::ntctscpy(buf, res.data(), len);
    1034- }
    761+ if(StreamPutToFileDescriptor(os, p->fd(), s))
    762+ return;
    1035763 }
    1036- CatchExpr(std::bad_alloc&, errno = ENOMEM);
    1037-#else
    1038- // NOTE: POSIX.1 2004 has no guarantee about slashes. POSIX.1 2013
    1039- // mandates there are no redundant slashes. See http://pubs.opengroup.org/onlinepubs/009695399/functions/getcwd.html.
    1040- return ::getcwd(buf, size);
    1041-#endif
    1042- }
    1043- else
    1044- errno = EINVAL;
    1045- return {};
    1046-}
    1047-char16_t*
    1048-ugetcwd(char16_t* buf, size_t len) ynothrowv
    1049-{
    1050- YAssertNonnull(buf);
    1051- if(len != 0)
    1052- {
    1053- using namespace std;
    1054-
    1055-#if YCL_Win32
    1056- // NOTE: Win32 guarantees there will be a separator if and only if when
    1057- // the result is root directory for ::_wgetcwd, and actually it is
    1058- // the same in ::%GetCurrentDirectoryW.
    1059- const auto n(::GetCurrentDirectoryW(len, wcast(buf)));
    1060-
    1061- if(n != 0)
    1062- return buf;
    1063- errno = GetErrnoFromWin32();
    1064- return {};
    1065-#else
    1066- // XXX: Alias by %char array is safe.
    1067- if(const auto cwd
    1068- = ::getcwd(ystdex::aligned_store_cast<char*>(buf), len))
    1069- try
    1070- {
    1071- const auto res(platform_ex::MakePathStringU(cwd));
    1072- const auto rlen(res.length());
    1073-
    1074- if(len < rlen + 1)
    1075- errno = ERANGE;
    1076- else
    1077- return ystdex::ntctscpy(buf, res.data(), rlen);
    1078- }
    1079- CatchExpr(std::bad_alloc&, errno = ENOMEM)
    764+ if(const auto p
    765+ = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(p_sb))
    766+ {
    767+ if(StreamPutToFileDescriptor(os, ::_fileno(p->file()), s))
    768+ return;
    769+ }
    1080770 #endif
    1081771 }
    1082- else
    1083- errno = EINVAL;
    1084- return {};
    1085-}
    1086-
    1087-#define YCL_Impl_FileSystem_ufunc_1(_n) \
    1088-bool \
    1089-_n(const char* path) ynothrowv \
    1090-{ \
    1091- YAssertNonnull(path); \
    1092-
    1093-#if YCL_Win32
    1094-# define YCL_Impl_FileSystem_ufunc_2(_fn, _wfn) \
    1095- return CallNothrow({}, [&]{ \
    1096- return _wfn(MakePathStringW(path).c_str()) == 0; \
    1097- }); \
    1098-}
    1099-#else
    1100-# define YCL_Impl_FileSystem_ufunc_2(_fn, _wfn) \
    1101- return _fn(path) == 0; \
    1102-}
    1103-#endif
    1104-
    1105-#define YCL_Impl_FileSystem_ufunc(_n, _fn, _wfn) \
    1106- YCL_Impl_FileSystem_ufunc_1(_n) \
    1107- YCL_Impl_FileSystem_ufunc_2(_fn, _wfn)
    1108-
    1109-YCL_Impl_FileSystem_ufunc(uchdir, ::chdir, ::_wchdir)
    1110-
    1111-YCL_Impl_FileSystem_ufunc_1(umkdir)
    1112-#if YCL_Win32
    1113- YCL_Impl_FileSystem_ufunc_2(_unused_, ::_wmkdir)
    1114-#else
    1115- return ::mkdir(path, mode_t(Mode::Access)) == 0;
    1116-}
    1117-#endif
    1118-
    1119-YCL_Impl_FileSystem_ufunc(urmdir, ::rmdir, ::_wrmdir)
    1120-
    1121-YCL_Impl_FileSystem_ufunc_1(uunlink)
    1122-#if YCL_Win32
    1123- return CallNothrow({}, [=]{
    1124- return CallFuncWithAttr(UnlinkWithAttr, path);
    1125- });
    1126-}
    1127-#else
    1128-YCL_Impl_FileSystem_ufunc_2(::unlink, )
    1129-#endif
    1130-
    1131-YCL_Impl_FileSystem_ufunc_1(uremove)
    1132-#if YCL_Win32
    1133- // NOTE: %::_wremove is same to %::_wunlink on Win32 which cannot delete
    1134- // empty directories.
    1135- return CallNothrow({}, [=]{
    1136- return CallFuncWithAttr([](const wchar_t* wstr, FileAttributes attr)
    1137- YB_ATTR_LAMBDA_QUAL(ynothrow, YB_NONNULL(1)){
    1138- return attr & FileAttributes::Directory ? ::_wrmdir(wstr) == 0
    1139- : UnlinkWithAttr(wstr, attr);
    1140- }, path);
    1141- });
    1142-}
    1143-#else
    1144-YCL_Impl_FileSystem_ufunc_2(std::remove, )
    1145772 #endif
    1146-
    1147-#undef YCL_Impl_FileSystem_ufunc_1
    1148-#undef YCL_Impl_FileSystem_ufunc_2
    1149-#undef YCL_Impl_FileSystem_ufunc
    1150-
    1151-
    1152-#if YCL_Win32
    1153-template<>
    1154-YF_API string
    1155-FetchCurrentWorkingDirectory(size_t init)
    1156-{
    1157- return MakePathString(FetchCurrentWorkingDirectory<char16_t>(init));
    1158-}
    1159-template<>
    1160-YF_API u16string
    1161-FetchCurrentWorkingDirectory(size_t)
    1162-{
    1163- u16string res;
    1164- unsigned long len, rlen(0);
    1165-
    1166- // NOTE: Retry is necessary to prevent failure due to modification of
    1167- // current directory from other threads.
    1168- ystdex::retry_on_cond([&]() -> bool{
    1169- if(rlen < len)
    1170- {
    1171- res.pop_back();
    1172- return {};
    1173- }
    1174- if(rlen != 0)
    1175- return true;
    1176- YCL_Raise_Win32E("GetCurrentDirectoryW", yfsig);
    1177- }, [&]{
    1178- if((len = ::GetCurrentDirectoryW(0, {})) != 0)
    1179- {
    1180- res.resize(size_t(len + 1));
    1181- rlen = ::GetCurrentDirectoryW(len, wcast(&res[0]));
    1182- }
    1183- });
    1184- res.resize(rlen);
    1185- return res;
    1186-}
    1187-#endif
    1188-
    1189-
    1190-FileTime
    1191-GetFileAccessTimeOf(const char* filename, bool follow_link)
    1192-{
    1193- return FetchFileTime(get_st_atime, filename, follow_link);
    1194-}
    1195-FileTime
    1196-GetFileAccessTimeOf(const char16_t* filename, bool follow_link)
    1197-{
    1198- return FetchFileTime(get_st_atime, wcast(filename), follow_link);
    1199-}
    1200-
    1201-FileTime
    1202-GetFileModificationTimeOf(const char* filename, bool follow_link)
    1203-{
    1204- return FetchFileTime(get_st_mtime, filename, follow_link);
    1205-}
    1206-FileTime
    1207-GetFileModificationTimeOf(const char16_t* filename, bool follow_link)
    1208-{
    1209- return FetchFileTime(get_st_mtime, wcast(filename), follow_link);
    1210-}
    1211-
    1212-array<FileTime, 2>
    1213-GetFileModificationAndAccessTimeOf(const char* filename, bool follow_link)
    1214-{
    1215- return FetchFileTime(get_st_matime, filename, follow_link);
    1216-}
    1217-array<FileTime, 2>
    1218-GetFileModificationAndAccessTimeOf(const char16_t* filename, bool follow_link)
    1219-{
    1220- return FetchFileTime(get_st_matime, wcast(filename), follow_link);
    1221-}
    1222-
    1223-YB_NONNULL(1) size_t
    1224-FetchNumberOfLinks(const char* path, bool follow_link)
    1225-{
    1226-#if YCL_Win32
    1227- return QueryFileLinks(MakePathStringW(path).c_str(), follow_link);
    1228-#else
    1229- struct ::stat st;
    1230- static_assert(std::is_unsigned<decltype(st.st_nlink)>(),
    1231- "Unsupported 'st_nlink' type found.");
    1232-
    1233- cstat(st, path, follow_link, yfsig);
    1234- return st.st_nlink;
    1235-#endif
    1236-}
    1237-YB_NONNULL(1) size_t
    1238-FetchNumberOfLinks(const char16_t* path, bool follow_link)
    1239-{
    1240-#if YCL_Win32
    1241- return QueryFileLinks(wcast(path), follow_link);
    1242-#else
    1243- return FetchNumberOfLinks(MakePathString(path).c_str(), follow_link);
    1244-#endif
    773+ ystdex::write_ntcts(os, s);
    1245774 }
    1246775
    1247776
    @@ -1317,26 +846,6 @@
    1317846 return {};
    1318847 }
    1319848
    1320-bool
    1321-IsNodeShared(const char* a, const char* b, bool follow_link) ynothrowv
    1322-{
    1323- return a == b || ystdex::ntctscmp(Nonnull(a), Nonnull(b)) == 0
    1324- || IsNodeShared_Impl(a, b, follow_link);
    1325-}
    1326-bool
    1327-IsNodeShared(const char16_t* a, const char16_t* b, bool follow_link) ynothrowv
    1328-{
    1329- return a == b || ystdex::ntctscmp(Nonnull(a), Nonnull(b)) == 0
    1330- || IsNodeShared_Impl(a, b, follow_link);
    1331-}
    1332-bool
    1333-IsNodeShared(FileDescriptor x, FileDescriptor y) ynothrow
    1334-{
    1335- const auto id(x.GetNodeID());
    1336-
    1337- return id != FileNodeID() && id == y.GetNodeID();
    1338-}
    1339-
    1340849 } // namespace platform;
    1341850
    1342851 namespace platform_ex
    diff -r db41e0c9c6e8 -r 3da33dd96b6d YFramework/source/YCLib/FileSystem.cpp
    --- a/YFramework/source/YCLib/FileSystem.cpp Tue Oct 13 02:01:49 2020 +0800
    +++ b/YFramework/source/YCLib/FileSystem.cpp Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file FileSystem.cpp
    1212 \ingroup YCLib
    1313 \brief 平台相关的文件系统接口。
    14-\version r4403
    14+\version r4947
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 312
    1717 \par 创建时间:
    1818 2012-05-30 22:41:35 +0800
    1919 \par 修改时间:
    20- 2020-10-08 12:17 +0800
    20+ 2020-10-25 06:11 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -33,17 +33,22 @@
    3333 #include YFM_YCLib_FileIO // for platform_ex::MakePathStringW,
    3434 // platform_Ex::MakePathStringU, MakePathString, Deref, ystdex::throw_error,
    3535 // 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
    36+// CategorizeNode, complete FileDescriptor, ystdex::ntctslen, std::wctob,
    37+// std::towupper, ystdex::restrict_length, std::min, ystdex::ntctsicmp,
    38+// std::errc::invalid_argument, std::strchr;
    3939 #include YFM_YCLib_NativeAPI // for Mode, struct ::stat, ::stat,
    40-// ::GetFileAttributesW, ::linkat, ::symlink, ::lstat, ::readlink;
    40+// ::GetFileAttributesW, platform_ex::cstat, platform_ex::estat, ::futimens,
    41+// ::linkat, ::symlink, ::lstat, ::readlink;
    4142 #include "CHRLib/YModules.h"
    4243 #include YFM_CHRLib_CharacterProcessing // for CHRLib::MakeUCS2LE;
    4344 #include <ystdex/ctime.h> // for ystdex::is_date_range_valid,
    4445 // ystdex::is_time_no_leap_valid;
    4546 #if YCL_Win32
    46-# include YFM_Win32_YCLib_MinGW32 // for platform_ex::Invalid,
    47+# include YFM_Win32_YCLib_MinGW32 // for platform_ex::FileAttributes,
    48+// platform_ex::GetErrnoFromWin32, platform_ex::Invalid,
    49+// platform_ex::ConvertTime, platform_ex::ToHandle,
    50+// platform_ex::QueryFileNodeID, platform_ex::QueryFileLinks,
    51+// platform_ex::QueryFileTime, platform_ex::SetFileTime,
    4752 // platform_ex::ResolveReparsePoint, platform_ex::DirectoryFindData;
    4853 # include <time.h> // for ::localtime_s;
    4954
    @@ -136,6 +141,219 @@
    136141 }
    137142 #endif
    138143
    144+#if YCL_Win32
    145+//! \since build 639
    146+//@{
    147+using platform_ex::FileAttributes;
    148+using platform_ex::GetErrnoFromWin32;
    149+
    150+YB_NONNULL(1) bool
    151+UnlinkWithAttr(const wchar_t* path, FileAttributes attr) ynothrow
    152+{
    153+ return !(attr & FileAttributes::ReadOnly) || ::SetFileAttributesW(path,
    154+ attr & ~FileAttributes::ReadOnly) ? ::_wunlink(path) == 0
    155+ : (errno = GetErrnoFromWin32(), false);
    156+}
    157+
    158+template<typename _func>
    159+YB_NONNULL(2) bool
    160+CallFuncWithAttr(_func f, const char* path)
    161+{
    162+ const auto& wpath(MakePathStringW(path));
    163+ const auto& wstr(wpath.c_str());
    164+ const auto attr(FileAttributes(::GetFileAttributesW(wstr)));
    165+
    166+ return attr != platform_ex::Invalid ? f(wstr, attr)
    167+ : (errno = GetErrnoFromWin32(), false);
    168+}
    169+//@}
    170+#endif
    171+
    172+#if YCL_Win32
    173+//! \since build 632
    174+using platform_ex::ConvertTime;
    175+//! \since build 704
    176+using platform_ex::ToHandle;
    177+//! \since build 632
    178+using platform_ex::QueryFileNodeID;
    179+//! \since build 639
    180+using platform_ex::QueryFileLinks;
    181+
    182+//! \since build 632
    183+using platform_ex::QueryFileTime;
    184+//! \since build 660
    185+void
    186+QueryFileTime(const char* path, ::FILETIME* p_ctime, ::FILETIME* p_atime,
    187+ ::FILETIME* p_mtime, bool follow_reparse_point)
    188+{
    189+ QueryFileTime(MakePathStringW(path).c_str(), p_ctime, p_atime, p_mtime,
    190+ follow_reparse_point);
    191+}
    192+//! \since build 632
    193+//@{
    194+void
    195+QueryFileTime(int fd, ::FILETIME* p_ctime, ::FILETIME* p_atime,
    196+ ::FILETIME* p_mtime)
    197+{
    198+ QueryFileTime(ToHandle(fd), p_ctime, p_atime, p_mtime);
    199+}
    200+
    201+// TODO: Blocked. Use C++14 generic lambda expressions.
    202+yconstexpr const struct
    203+{
    204+ template<typename _tParam, typename... _tParams>
    205+ FileTime
    206+ operator()(_tParam arg, _tParams... args) const
    207+ {
    208+ ::FILETIME atime{};
    209+
    210+ QueryFileTime(arg, {}, &atime, {}, args...);
    211+ return ConvertTime(atime);
    212+ }
    213+} get_st_atime{};
    214+yconstexpr const struct
    215+{
    216+ template<typename _tParam, typename... _tParams>
    217+ FileTime
    218+ operator()(_tParam arg, _tParams... args) const
    219+ {
    220+ ::FILETIME mtime{};
    221+
    222+ QueryFileTime(arg, {}, {}, &mtime, args...);
    223+ return ConvertTime(mtime);
    224+ }
    225+} get_st_mtime{};
    226+yconstexpr const struct
    227+{
    228+ template<typename _tParam, typename... _tParams>
    229+ array<FileTime, 2>
    230+ operator()(_tParam arg, _tParams... args) const
    231+ {
    232+ ::FILETIME mtime{}, atime{};
    233+
    234+ QueryFileTime(arg, {}, &atime, &mtime, args...);
    235+ return array<FileTime, 2>{ConvertTime(mtime), ConvertTime(atime)};
    236+ }
    237+} get_st_matime{};
    238+//@}
    239+#else
    240+//! \since build 901
    241+using platform_ex::cstat;
    242+//! \since build 719
    243+YB_NONNULL(2, 4) inline PDefH(void, cstat, struct ::stat& st,
    244+ const char16_t* path, bool follow_link, const char* sig)
    245+ ImplRet(cstat(st, MakePathString(path).c_str(), follow_link, sig))
    246+
    247+//! \since build 901
    248+using platform_ex::estat;
    249+
    250+//! \since build 638
    251+//@{
    252+static_assert(std::is_integral<::dev_t>(),
    253+ "Nonconforming '::dev_t' type found.");
    254+static_assert(std::is_unsigned<::ino_t>(),
    255+ "Nonconforming '::ino_t' type found.");
    256+
    257+inline PDefH(FileNodeID, get_file_node_id, struct ::stat& st) ynothrow
    258+ ImplRet({std::uint64_t(st.st_dev), std::uint64_t(st.st_ino)})
    259+//@}
    260+
    261+inline PDefH(::timespec, ToTimeSpec, FileTime ft) ynothrow
    262+ ImplRet({std::time_t(ft.count() / 1000000000LL),
    263+ long(ft.count() % 1000000000LL)})
    264+
    265+//! \since build 719
    266+void
    267+SetFileTime(int fd, const ::timespec(&times)[2])
    268+{
    269+#if YCL_DS
    270+ // XXX: Hack.
    271+#ifndef UTIME_OMIT
    272+# define UTIME_OMIT (-1L)
    273+#endif
    274+ yunused(fd), yunused(times);
    275+ ystdex::throw_error(std::errc::function_not_supported, yfsig);
    276+#else
    277+ YCL_CallF_CAPI(, ::futimens, fd, times);
    278+#endif
    279+}
    280+
    281+//! \since build 631
    282+//@{
    283+const auto get_st_atime([](struct ::stat& st){
    284+ return FileTime(st.st_atime);
    285+});
    286+const auto get_st_mtime([](struct ::stat& st){
    287+ return FileTime(st.st_mtime);
    288+});
    289+const auto get_st_matime([](struct ::stat& st){
    290+ return array<FileTime, 2>{FileTime(st.st_mtime), FileTime(st.st_atime)};
    291+});
    292+//@}
    293+#endif
    294+
    295+//! \since build 660
    296+bool
    297+IsNodeShared_Impl(const char* a, const char* b, bool follow_link) ynothrow
    298+{
    299+#if YCL_Win32
    300+ return CallNothrow({}, [=]{
    301+ return QueryFileNodeID(MakePathStringW(a).c_str(), follow_link)
    302+ == QueryFileNodeID(MakePathStringW(b).c_str(), follow_link);
    303+ });
    304+#else
    305+ struct ::stat st;
    306+
    307+ if(estat(st, a, follow_link) != 0)
    308+ return {};
    309+
    310+ const auto id(get_file_node_id(st));
    311+
    312+ if(estat(st, b, follow_link) != 0)
    313+ return {};
    314+
    315+ return IsNodeShared(id, get_file_node_id(st));
    316+#endif
    317+}
    318+//! \since build 660
    319+bool
    320+IsNodeShared_Impl(const char16_t* a, const char16_t* b, bool follow_link)
    321+ ynothrow
    322+{
    323+#if YCL_Win32
    324+ return CallNothrow({}, [=]{
    325+ return QueryFileNodeID(wcast(a), follow_link)
    326+ == QueryFileNodeID(wcast(b), follow_link);
    327+ });
    328+#else
    329+ return IsNodeShared_Impl(MakePathString(a).c_str(),
    330+ MakePathString(b).c_str(), follow_link);
    331+#endif
    332+}
    333+
    334+//! \since build 632
    335+template<typename _func, typename... _tParams>
    336+auto
    337+FetchFileTime(_func f, _tParams... args)
    338+#if YCL_Win32
    339+ -> ystdex::invoke_result_t<_func, _tParams&...>
    340+#else
    341+ -> ystdex::invoke_result_t<_func, struct ::stat&>
    342+#endif
    343+{
    344+#if YCL_Win32
    345+ // NOTE: The %::FILETIME has resolution of 100 nanoseconds.
    346+ // XXX: Error handling for indirect calls.
    347+ return f(args...);
    348+#else
    349+ // TODO: Get more precise time count.
    350+ struct ::stat st;
    351+
    352+ cstat(st, args..., yfsig);
    353+ return ystdex::invoke(f, st);
    354+#endif
    355+}
    356+
    139357 } // unnamed namespace;
    140358
    141359 bool
    @@ -164,6 +382,45 @@
    164382 #endif
    165383 }
    166384
    385+
    386+size_t
    387+FetchNumberOfLinks(const FileDescriptor& fd)
    388+{
    389+#if YCL_Win32
    390+ return QueryFileLinks(ToHandle(*fd));
    391+#else
    392+ struct ::stat st;
    393+ static_assert(std::is_unsigned<decltype(st.st_nlink)>(),
    394+ "Unsupported 'st_nlink' type found.");
    395+
    396+ cstat(st, *fd, yfsig);
    397+ return size_t(st.st_nlink);
    398+#endif
    399+}
    400+size_t
    401+FetchNumberOfLinks(const char* path, bool follow_link)
    402+{
    403+#if YCL_Win32
    404+ return QueryFileLinks(MakePathStringW(path).c_str(), follow_link);
    405+#else
    406+ struct ::stat st;
    407+ static_assert(std::is_unsigned<decltype(st.st_nlink)>(),
    408+ "Unsupported 'st_nlink' type found.");
    409+
    410+ cstat(st, path, follow_link, yfsig);
    411+ return size_t(st.st_nlink);
    412+#endif
    413+}
    414+size_t
    415+FetchNumberOfLinks(const char16_t* path, bool follow_link)
    416+{
    417+#if YCL_Win32
    418+ return QueryFileLinks(wcast(path), follow_link);
    419+#else
    420+ return FetchNumberOfLinks(MakePathString(path).c_str(), follow_link);
    421+#endif
    422+}
    423+
    167424 void
    168425 CreateHardLink(const char* dst, const char* src)
    169426 {
    @@ -506,6 +763,352 @@
    506763 }
    507764
    508765
    766+int
    767+uaccess(const char* path, int amode) ynothrowv
    768+{
    769+ YAssertNonnull(path);
    770+#if YCL_Win32
    771+ return CallNothrow(-1, [=]{
    772+ return ::_waccess(MakePathStringW(path).c_str(), amode);
    773+ });
    774+#else
    775+ return ::access(path, amode);
    776+#endif
    777+}
    778+int
    779+uaccess(const char16_t* path, int amode) ynothrowv
    780+{
    781+ YAssertNonnull(path);
    782+#if YCL_Win32
    783+ return ::_waccess(wcast(path), amode);
    784+#else
    785+ return CallNothrow(-1, [=]{
    786+ return ::access(MakePathString(path).c_str(), amode);
    787+ });
    788+#endif
    789+}
    790+
    791+char*
    792+ugetcwd(char* buf, size_t size) ynothrowv
    793+{
    794+ YAssertNonnull(buf);
    795+ if(size != 0)
    796+ {
    797+ using namespace std;
    798+
    799+#if YCL_Win32
    800+ try
    801+ {
    802+ const auto p(make_unique<wchar_t[]>(size));
    803+
    804+ if(const auto cwd = ::_wgetcwd(p.get(), int(size)))
    805+ {
    806+ const auto res(MakePathString(ucast(cwd)));
    807+ const auto len(res.length());
    808+
    809+ if(size < len + 1)
    810+ errno = ERANGE;
    811+ else
    812+ return ystdex::ntctscpy(buf, res.data(), len);
    813+ }
    814+ }
    815+ CatchExpr(std::bad_alloc&, errno = ENOMEM);
    816+#else
    817+ // NOTE: POSIX.1 2004 has no guarantee about slashes. POSIX.1 2013
    818+ // mandates there are no redundant slashes. See http://pubs.opengroup.org/onlinepubs/009695399/functions/getcwd.html.
    819+ // At least platform %DS (implemented in devkitPro newlib's libsysbase)
    820+ // may have trailing slashes or not, depending on the last successful
    821+ // %::chdir call.
    822+ return ::getcwd(buf, size);
    823+#endif
    824+ }
    825+ else
    826+ errno = EINVAL;
    827+ return {};
    828+}
    829+char16_t*
    830+ugetcwd(char16_t* buf, size_t len) ynothrowv
    831+{
    832+ YAssertNonnull(buf);
    833+ if(len != 0)
    834+ {
    835+ using namespace std;
    836+
    837+#if YCL_Win32
    838+ // NOTE: Win32 guarantees there will be a separator if and only if when
    839+ // the result is root directory for ::_wgetcwd, and actually it is
    840+ // the same in ::%GetCurrentDirectoryW.
    841+ const auto n(::GetCurrentDirectoryW(len, wcast(buf)));
    842+
    843+ if(n != 0)
    844+ return buf;
    845+ errno = GetErrnoFromWin32();
    846+ return {};
    847+#else
    848+ // XXX: Alias by %char array is safe.
    849+ if(const auto cwd
    850+ = ::getcwd(ystdex::aligned_store_cast<char*>(buf), len))
    851+ try
    852+ {
    853+ const auto res(platform_ex::MakePathStringU(cwd));
    854+ const auto rlen(res.length());
    855+
    856+ if(len < rlen + 1)
    857+ errno = ERANGE;
    858+ else
    859+ return ystdex::ntctscpy(buf, res.data(), rlen);
    860+ }
    861+ CatchExpr(std::bad_alloc&, errno = ENOMEM)
    862+#endif
    863+ }
    864+ else
    865+ errno = EINVAL;
    866+ return {};
    867+}
    868+
    869+#define YCL_Impl_FileSystem_ufunc_1(_n) \
    870+bool \
    871+_n(const char* path) ynothrowv \
    872+{ \
    873+ YAssertNonnull(path); \
    874+
    875+#if YCL_Win32
    876+# define YCL_Impl_FileSystem_ufunc_2(_fn, _wfn) \
    877+ return CallNothrow({}, [&]{ \
    878+ return _wfn(MakePathStringW(path).c_str()) == 0; \
    879+ }); \
    880+}
    881+#else
    882+# define YCL_Impl_FileSystem_ufunc_2(_fn, _wfn) \
    883+ return _fn(path) == 0; \
    884+}
    885+#endif
    886+
    887+#define YCL_Impl_FileSystem_ufunc(_n, _fn, _wfn) \
    888+ YCL_Impl_FileSystem_ufunc_1(_n) \
    889+ YCL_Impl_FileSystem_ufunc_2(_fn, _wfn)
    890+
    891+YCL_Impl_FileSystem_ufunc(uchdir, ::chdir, ::_wchdir)
    892+
    893+YCL_Impl_FileSystem_ufunc_1(umkdir)
    894+#if YCL_Win32
    895+ YCL_Impl_FileSystem_ufunc_2(_unused_, ::_wmkdir)
    896+#else
    897+ return ::mkdir(path, mode_t(Mode::Access)) == 0;
    898+}
    899+#endif
    900+
    901+YCL_Impl_FileSystem_ufunc(urmdir, ::rmdir, ::_wrmdir)
    902+
    903+YCL_Impl_FileSystem_ufunc_1(uunlink)
    904+#if YCL_Win32
    905+ return CallNothrow({}, [=]{
    906+ return CallFuncWithAttr(UnlinkWithAttr, path);
    907+ });
    908+}
    909+#else
    910+YCL_Impl_FileSystem_ufunc_2(::unlink, )
    911+#endif
    912+
    913+YCL_Impl_FileSystem_ufunc_1(uremove)
    914+#if YCL_Win32
    915+ // NOTE: %::_wremove is same to %::_wunlink on Win32 which cannot delete
    916+ // empty directories.
    917+ return CallNothrow({}, [=]{
    918+ return CallFuncWithAttr([](const wchar_t* wstr, FileAttributes attr)
    919+ YB_ATTR_LAMBDA_QUAL(ynothrow, YB_NONNULL(1)){
    920+ return attr & FileAttributes::Directory ? ::_wrmdir(wstr) == 0
    921+ : UnlinkWithAttr(wstr, attr);
    922+ }, path);
    923+ });
    924+}
    925+#else
    926+YCL_Impl_FileSystem_ufunc_2(std::remove, )
    927+#endif
    928+
    929+#undef YCL_Impl_FileSystem_ufunc_1
    930+#undef YCL_Impl_FileSystem_ufunc_2
    931+#undef YCL_Impl_FileSystem_ufunc
    932+
    933+
    934+#if YCL_Win32
    935+template<>
    936+YF_API string
    937+FetchCurrentWorkingDirectory(size_t init)
    938+{
    939+ return MakePathString(FetchCurrentWorkingDirectory<char16_t>(init));
    940+}
    941+template<>
    942+YF_API u16string
    943+FetchCurrentWorkingDirectory(size_t)
    944+{
    945+ u16string res;
    946+ unsigned long len, rlen(0);
    947+
    948+ // NOTE: Retry is necessary to prevent failure due to modification of
    949+ // current directory from other threads.
    950+ ystdex::retry_on_cond([&]() -> bool{
    951+ if(rlen < len)
    952+ {
    953+ res.pop_back();
    954+ return {};
    955+ }
    956+ if(rlen != 0)
    957+ return true;
    958+ YCL_Raise_Win32E("GetCurrentDirectoryW", yfsig);
    959+ }, [&]{
    960+ if((len = ::GetCurrentDirectoryW(0, {})) != 0)
    961+ {
    962+ res.resize(size_t(len + 1));
    963+ rlen = ::GetCurrentDirectoryW(len, wcast(&res[0]));
    964+ }
    965+ });
    966+ res.resize(rlen);
    967+ return res;
    968+}
    969+#endif
    970+
    971+
    972+FileNodeID
    973+GetFileNodeIDOf(const FileDescriptor& fd) ynothrow
    974+{
    975+#if YCL_Win32
    976+ return CallNothrow({}, [=]{
    977+ return QueryFileNodeID(ToHandle(*fd));
    978+ });
    979+#else
    980+ struct ::stat st;
    981+
    982+ return estat(st, *fd) == 0 ? get_file_node_id(st) : FileNodeID();
    983+#endif
    984+}
    985+
    986+bool
    987+IsNodeShared(const char* a, const char* b, bool follow_link) ynothrowv
    988+{
    989+ return a == b || ystdex::ntctscmp(Nonnull(a), Nonnull(b)) == 0
    990+ || IsNodeShared_Impl(a, b, follow_link);
    991+}
    992+bool
    993+IsNodeShared(const char16_t* a, const char16_t* b, bool follow_link) ynothrowv
    994+{
    995+ return a == b || ystdex::ntctscmp(Nonnull(a), Nonnull(b)) == 0
    996+ || IsNodeShared_Impl(a, b, follow_link);
    997+}
    998+bool
    999+IsNodeShared(FileDescriptor x, FileDescriptor y) ynothrow
    1000+{
    1001+ const auto id(GetFileNodeIDOf(x));
    1002+
    1003+ return id != FileNodeID() && id == GetFileNodeIDOf(y);
    1004+}
    1005+
    1006+
    1007+FileTime
    1008+GetFileAccessTimeOf(const FileDescriptor& fd)
    1009+{
    1010+ return FetchFileTime(get_st_atime, *fd);
    1011+}
    1012+FileTime
    1013+GetFileAccessTimeOf(std::FILE* fp)
    1014+{
    1015+ return GetFileAccessTimeOf(FileDescriptor(fp));
    1016+}
    1017+FileTime
    1018+GetFileAccessTimeOf(const char* filename, bool follow_link)
    1019+{
    1020+ return FetchFileTime(get_st_atime, filename, follow_link);
    1021+}
    1022+FileTime
    1023+GetFileAccessTimeOf(const char16_t* filename, bool follow_link)
    1024+{
    1025+ return FetchFileTime(get_st_atime, wcast(filename), follow_link);
    1026+}
    1027+
    1028+FileTime
    1029+GetFileModificationTimeOf(const FileDescriptor& fd)
    1030+{
    1031+ return FetchFileTime(get_st_mtime, *fd);
    1032+}
    1033+FileTime
    1034+GetFileModificationTimeOf(std::FILE* fp)
    1035+{
    1036+ return GetFileModificationTimeOf(FileDescriptor(fp));
    1037+}
    1038+FileTime
    1039+GetFileModificationTimeOf(const char* filename, bool follow_link)
    1040+{
    1041+ return FetchFileTime(get_st_mtime, filename, follow_link);
    1042+}
    1043+FileTime
    1044+GetFileModificationTimeOf(const char16_t* filename, bool follow_link)
    1045+{
    1046+ return FetchFileTime(get_st_mtime, wcast(filename), follow_link);
    1047+}
    1048+
    1049+array<FileTime, 2>
    1050+GetFileModificationAndAccessTimeOf(const FileDescriptor& fd)
    1051+{
    1052+ return FetchFileTime(get_st_matime, *fd);
    1053+}
    1054+array<FileTime, 2>
    1055+GetFileModificationAndAccessTimeOf(std::FILE* fp)
    1056+{
    1057+ return GetFileModificationAndAccessTimeOf(FileDescriptor(fp));
    1058+}
    1059+array<FileTime, 2>
    1060+GetFileModificationAndAccessTimeOf(const char* filename, bool follow_link)
    1061+{
    1062+ return FetchFileTime(get_st_matime, filename, follow_link);
    1063+}
    1064+array<FileTime, 2>
    1065+GetFileModificationAndAccessTimeOf(const char16_t* filename, bool follow_link)
    1066+{
    1067+ return FetchFileTime(get_st_matime, wcast(filename), follow_link);
    1068+}
    1069+
    1070+void
    1071+SetFileAccessTimeOf(const FileDescriptor& fd, FileTime ft)
    1072+{
    1073+#if YCL_Win32
    1074+ auto atime(ConvertTime(ft));
    1075+
    1076+ platform_ex::SetFileTime(ToHandle(*fd), {}, &atime, {});
    1077+#else
    1078+ const ::timespec times[]{ToTimeSpec(ft), {yimpl(0), UTIME_OMIT}};
    1079+
    1080+ SetFileTime(*fd, times);
    1081+#endif
    1082+}
    1083+void
    1084+SetFileModificationTimeOf(const FileDescriptor& fd, FileTime ft)
    1085+{
    1086+#if YCL_Win32
    1087+ auto mtime(ConvertTime(ft));
    1088+
    1089+ platform_ex::SetFileTime(ToHandle(*fd), {}, {}, &mtime);
    1090+#else
    1091+ const ::timespec times[]{{yimpl(0), UTIME_OMIT}, ToTimeSpec(ft)};
    1092+
    1093+ SetFileTime(*fd, times);
    1094+#endif
    1095+}
    1096+void
    1097+SetFileModificationAndAccessTimeOf(const FileDescriptor& fd,
    1098+ array<FileTime, 2> fts)
    1099+{
    1100+#if YCL_Win32
    1101+ auto atime(ConvertTime(fts[0])), mtime(ConvertTime(fts[1]));
    1102+
    1103+ platform_ex::SetFileTime(ToHandle(*fd), {}, &atime, &mtime);
    1104+#else
    1105+ const ::timespec times[]{ToTimeSpec(fts[0]), ToTimeSpec(fts[1])};
    1106+
    1107+ SetFileTime(*fd, times);
    1108+#endif
    1109+}
    1110+
    1111+
    5091112 namespace FAT
    5101113 {
    5111114
    diff -r db41e0c9c6e8 -r 3da33dd96b6d doc/ChangeLog.V0.9.txt
    --- a/doc/ChangeLog.V0.9.txt Tue Oct 13 02:01:49 2020 +0800
    +++ b/doc/ChangeLog.V0.9.txt Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file ChangeLog.V0.9.txt
    1212 \ingroup Documentation
    1313 \brief 版本更新历史记录 - V0.9 。
    14-\version r382
    14+\version r592
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 800
    1717 \par 创建时间:
    1818 2020-10-12 17:19:23 +0800
    1919 \par 修改时间:
    20- 2020-10-12 18:26 +0800
    20+ 2020-10-25 09:04 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -32,6 +32,212 @@
    3232
    3333 $now
    3434 (
    35+ / %YBase.YStandardEx $=
    36+ (
    37+ + $re_ex(b178) $dev $lib "#pragma directive"
    38+ @ 'YB_IMPL_GNUCPP >= 100000' @ "metafunction \
    39+ %have_common_nonempty_virtual_base" @ %TypeOperation,
    40+ // To eliminate G++ 10 warning: [-Winaccessible-base].
    41+ / %Path $=
    42+ (
    43+ / @ "class template %path_traits" $=
    44+ (
    45+ (
    46+ + "specialization for %basic_string_view instantiations";
    47+ / "simplified specialization for %basic_string instantiations",
    48+ ),
    49+ + $dev $lib 'YB_ATTR_nodiscard'
    50+ @ "all static memeber functions"
    51+ ),
    52+ + "function template %is_parent_or_self"
    53+ ),
    54+ * "missing check exceptions mask"
    55+ @ "function template %rethrow_badstate" @ %IOS $since b663,
    56+ / %String $=
    57+ (
    58+ / DLI @ "function templates %write" $=
    59+ (
    60+ / "overload %write#1"
    61+ -> "3 function templates without default arguments",
    62+ // This is more efficient.
    63+ - "overload for arrays",
    64+ // This should be already covered by other overloads.
    65+ / $impl "all 'class _tString'" -> 'typename _tString'
    66+ // The string objects of array type are also supported \
    67+ uniformly.
    68+ ),
    69+ (
    70+ * "missing inclusion %CString" @ 'YB_Has_string_view == 1'
    71+ $since b833;
    72+ + "function template %write_ntcts"
    73+ ),
    74+ / DLDI "sentry" @ "function template %extract#1"
    75+ ^ "direct initialization"
    76+ )
    77+ ),
    78+ / %YFramework $=
    79+ (
    80+
    81+ * "missing 'inline'" @ "namespace %Android" @ ".cpp file"
    82+ @ %'YCLib_(Android)'.Android $since b492,
    83+ // Also to eliminate Clang++ warning.
    84+ * "missing return value" @ "branch traversal"
    85+ @ "constructor %DEntry#5" @ %'YCLib(DS)'.DSIO $since b900,
    86+ // Also to eliminate G++ warning: [-Wno-unused-result].
    87+ / %'YCLib(Win32)'.MinGW32 $=
    88+ (
    89+ + "2 functions %ParseCommandArguments",
    90+ // See $2020-10 @ %Documentation::Workflow.
    91+ * "missing 'YF_API'"
    92+ @ "functions %(LockFile, TryLockFile, UnlockFile)",
    93+ + $dev $lib 'YB_ATTR_nodiscard' @ "all functions with non-void \
    94+ return type and without 'YF_ATTR_nodiscard' other than \
    95+ declared by 'Def*' or %(DirectoryFindData::Read, UnlockFile)",
    96+ + $dev $lib 'YB_PURE' @ ("functions %(Win32Exception::( \
    97+ GetErrorCategory, FormatMessage), CategorizeNode#4)",
    98+ $impl "internal error category member functions")
    99+ ),
    100+ / %YCLib $=
    101+ (
    102+ + "class name declaration %FileDescriptor" @ %FileSystem,
    103+ / %FileIO $=
    104+ (
    105+ / ("all functions %(uaccess, ugetcwd, uchdir, umkdir, urmdir, \
    106+ uunlink, uremove)",
    107+ "function template %FetchCurrentWorkingDirectory and its \
    108+ specializations") >> %FileSystem,
    109+ (
    110+ / "member functions %FileDescriptor::(GetAccessTime, \
    111+ GetModificationTime, GetModificationAndAccessTime, \
    112+ SetAccessTime, SetModificationTime, \
    113+ SetModificationAndAccessTime)" -> "functions \
    114+ %(GetFileAccessTimeOf, GetFileModificationTimeOf, \
    115+ GetFileModificationAndAccessTimeOf, SetFileAccessTimeOf, \
    116+ SetFileModificationTimeOf, \
    117+ SetFileModificationAndAccessTimeOf)";
    118+ / "all functions ('SetFile*TimeOf, 'GetFile*TimeOf'))"
    119+ >> %FileSystem $dep_from ("%FileDescriptor" @ %FileSystem)
    120+ ),
    121+ (
    122+ / "member function %FileDescriptor::GetNumberOfLinks"
    123+ -> "function %FetchNumberOfLinks";
    124+ / "all functions %FetchNumberOfLinks"
    125+ >> %FileSystem $dep_from ("%FileDescriptor" @ %FileSystem)
    126+ ),
    127+ (
    128+ + 'ynothrow' @ "%operator(==, !=) for %FileNodeID",
    129+ / "member function %FileDescriptor::GetNodeID"
    130+ -> "function %GetFileNodeIDOf";
    131+ / "type %NodeID"; "functions %operator(==, !=) for %NodeID",
    132+ "functions %(GetFileNodeIDOf, IsNodeShared)"
    133+ >> %FileSystem
    134+ ),
    135+ + "function %StreamPut" $dep_from (%YBase.YStandardEx.IOS,
    136+ "%ystdex::write_ntcts" @ %YBase.YStandardEx.String)
    137+ ),
    138+ / @ "platform %Win32" @ %NativeAPI $=
    139+ (
    140+ * "'__has_include(<specstrings_undef.h>)' broke Visual C++ \
    141+ builds" $since b900
    142+ $= (/ "commented out the check"),
    143+ / @ "namespace %platform_ex" $=
    144+ (
    145+ + "function %IntPtrToHandle";
    146+ + "check for special invalid return value '-2'"
    147+ @ "function %ToHandle"
    148+ // This is not a bug since the special value is \
    149+ documented recently. Note the earliest \
    150+ implementation in this project dates back to b475 \
    151+ in %YCLib.FileSystem.
    152+ )
    153+ )
    154+ ),
    155+ / %YSLib $=
    156+ (
    157+ / @ "namespace %YSLib::IO" @ %Adaptor.YAdaptor $=
    158+ (
    159+ + 'using platform::FileTime;',
    160+ + 'using platform::SetFileAccessTimeOf;'
    161+ $dep_from ("%FileDescriptor::SetAccessTime" @ %FileIO),
    162+ + 'using platform::SetFileModificationTimeOf;' $dep_from
    163+ ("%FileDescriptor::SetModificationTime" @ %FileIO),
    164+ + 'using platform::SetFileModificationAndAccessTimeOf;'
    165+ $dep_from
    166+ ("%FileDescriptor::SetModificationAndAccessTime"
    167+ @ %FileIO),
    168+ + 'using platform::StreamPut;'
    169+ $dep_from ("%StreamPut" @ %FileIO)
    170+ ),
    171+ * $forced DLDI "object definitions %(PreserveModificationTime, \
    172+ PreserveModificationAndAccessTime)" @ %YSLib.Service.FileSystem
    173+ $dep_from ("%FileDescriptor" @ %YCLib.FileIO)
    174+ $= (/ $impl ^ "%(SetFileModificationTimeOf, \
    175+ GetFileModificationTimeOf, \
    176+ SetFileModificationAndAccessTimeOf, \
    177+ GetFileModificationAndAccessTimeOf)"
    178+ $dep_from %YAdaptor.Adaptor)
    179+ ),
    180+ / %NPL $=
    181+ (
    182+ + "common reducer name" @ "functions %(AccR, FoldR1, Map1)"
    183+ ^ "%A1::NameTypedReducerHandler" @ %NPLA1Forms,
    184+ / %NPLA1 $=
    185+ (
    186+ / DLDI
    187+ "simplified member function %Continuation::GetNextTermRef",
    188+ / @ "class %REPLContext" $=
    189+ (
    190+ + "data member %OutputStreamPtr";
    191+ + "function %GetOutputStreamRef"
    192+ )
    193+ ),
    194+ / "applicative %puts" @ "function %LoadModule_std_io" @ %Dependency
    195+ ^ ($dep_from "%REPLContext::GetOutputStreamRef" @ %NPLA1,
    196+ "%YSLib::IO::StreamPut" @ %YSLib.Adaptor.YAdaptor)
    197+ ~ "%std::puts"
    198+ // This hack makes it possible to work for priting Unicode BMP \
    199+ codepoints from a UTF-8 input on the traditional (broken) \
    200+ Windows console in %MinGW32 platforms when libstdc++ is \
    201+ used, though it still chokes from the console input;
    202+ )
    203+ ),
    204+ / %Tools $=
    205+ (
    206+ / %Scripts $=
    207+ (
    208+ / "relocated installed NPLA1 scripts to 'share/NPLA1'" ~ 'var/NPLA1'
    209+ @ %SHBuild-build-YSLib.txt,
    210+ // This should be better read-only.
    211+ / DLDI "renamed internal path prefix variable for loading"
    212+ // Ditto, but actually not sensitive to the actual behavior, \
    213+ as the loaded script %SHBuild-YSLib-common.txt is still in \
    214+ the same directory to %SHBuild-BuildApp.txt.
    215+ ),
    216+ / %SHBuild.Main $=
    217+ (
    218+ / $forced DLI "registered %std::cout as the output stream"
    219+ @ "NPLA1 context initialization"
    220+ $dep_from %YFramework.NPL.Dependency,
    221+ * "wrong ANSI code page assumptions on handling external command \
    222+ arguments" @ "platform %Win32" $since b476
    223+ $= (/ $impl ^ "%platform_ex::ParseCommandArguments"
    224+ ~ "%platform_ex::(EncodeArg, DecodeArg)")
    225+ // See $2020-10 @ %Documentation::Workflow.
    226+ )
    227+ ),
    228+ / DLI "optimized dot strings comparision"
    229+ ^ $dep_from ("%ystdex::is_parent_or_self" @ %YBase.YStandardEx.Path)
    230+ $dep_from ("ystdex::path_traits specialization for %basic_string_view \
    231+ instantiations" @ %YBase.YStandardEx.Path)
    232+ $effective @ ("constructors#(4, 5)", "function %IsDot")
    233+ @ "class %DEntry" @ %'YCLib_(DS).DSIO'
    234+ // This is in general more efficient than comparing to string literals \
    235+ more than 3 by times when dot and dot-dot string are all compared \
    236+ (also having significantly improving in a string string comparison).
    237+),
    238+
    239+b900
    240+(
    35241 / $re_add(b826) DLB "removed targets generated from 'objcopy -O binary'"
    36242 @ "makefile" @ "project %YSTest_ARM9" @ "platform %DS",
    37243 // This is not used since b826. See $2018-05 @ %Documentation::Workflow.
    @@ -321,7 +527,7 @@
    321527 * DLDI "wrong handling of negation %SS_Debg" $since b840,
    322528 // This did not expose wrong behavior because the consumption \
    323529 of the value is also wrong.
    324- * @ "applicative ''ss-verbose-puts''" $since b840 $=
    530+ * @ "applicative 'ss-verbose-puts'" $since b840 $=
    325531 (
    326532 * "wrong handling of negation %SS_Verbose",
    327533 * "missing copying the variable to the static environment"
    @@ -376,7 +582,11 @@
    376582 ),
    377583 / $re_ex(b800) "regenerated Code::Blocks projects"
    378584 ^ ("%GenerateProjects.sh" @ %Tools.Scripts),
    379- / $doc $deploy "updated version" @ "%Doxyfile"
    585+ / $doc "%Doxyfile" $=
    586+ (
    587+ / $deploy "updated version";
    588+ / DLDI "saved with some new default configurations"
    589+ )
    380590 );
    381591
    382592 ////
    diff -r db41e0c9c6e8 -r 3da33dd96b6d doc/NPL.txt
    --- a/doc/NPL.txt Tue Oct 13 02:01:49 2020 +0800
    +++ b/doc/NPL.txt Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file NPL.txt
    1212 \ingroup Documentation
    1313 \brief NPL 规范和实现规格说明。
    14-\version r17149
    14+\version r17309
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 304
    1717 \par 创建时间:
    1818 2012-04-25 10:34:20 +0800
    1919 \par 修改时间:
    20- 2020-10-11 21:52 +0800
    20+ 2020-10-21 10:50 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -89,7 +89,7 @@
    8989 关于 vau 演算的形式模型和其它相关内容,详见 [Shu10] 。
    9090 历史上,vau 演算提供了 fexpr 类似的抽象,参见:
    9191 https://en.wikipedia.org/wiki/Fexpr
    92-另见 @7.8.1 。
    92+另见 @7.8.2 。
    9393 关于一些其它支持 fexpr 特性的语言设计,参见:
    9494 PicoLisp :https://software-lab.de/doc/faq.html#lambda
    9595 newLISP :http://www.newlisp.org/downloads/newlisp_manual.html#define-macro
    @@ -1549,6 +1549,7 @@
    15491549
    15501550 @5.2 NPLA 约定:
    15511551 使用宿主语言(@2.4.1) 为 ISO C++11(及其之后的向前兼容的版本)的简单实现模型 NPL-EMA(@2.7.1) 。
    1552+词法分析可接受多字节文本编码的字符串形式的源代码,但不假设其编码中除 0(空字符 NUL )以外的具体代码点被编码的数值,不转换编码(@2.4.1) 。
    15521553 使用可选的语法预处理和 NPL-GA 语法(@3.4.4) 。
    15531554 默认使用隐式类型而非显式类型(@4.6.2.2) 。
    15541555 默认使用潜在类型(@4.6.2.2) :值(@4.1) 具有类型;不指定动态类型以外的类型。
    @@ -1791,6 +1792,8 @@
    17911792 参见 @3.3.4 和参考实现模块 Lexical 。
    17921793 Lexical 模块在 namespace NPL 通过别名声明引入 YSLib::string 类型和 YSLib::string_view 类型。
    17931794 Lexical 模块的词法分析器支持分析表示 S 表达式的文本流,但源语言中不包含点(dot) 记号,且支持同宿主语言相同的断行连接和转义字符。和宿主语言不同,不支持的转义前的 \ 不需要被重复,且不支持字符串字面量的直接并置连接。
    1795+词法分析使用二进制模式处理文本,字符序列被逐字节输入。CR(carriage return) 和 LF(line feed) 预期以单字节编码。
    1796+词法分析忽略 CR(carriage return) ,而 LF(line feed) 被视为换行。
    17941797 词法分析的结果是记号的序列,以记号列表类型 TokenList 表示。
    17951798 Lexical 模块提供类型别名和以下类型:
    17961799 LexemeList :词素(@3.3.1) 列表。
    @@ -3001,7 +3004,7 @@
    30013004 NPLA1 实现可在实现中提供构建时确定的实现选项,以宿主语言的宏定义等方式启用。
    30023005 这些选项主要用于提供参考实现,不应被直接作为公开接口;通常情形不需要更改。
    30033006 特定的 API 可间接依赖这些特性,以便派生实现提供不同的优化实现,包括:
    3004-A1::REPLContext::IsAsynchronous(@7.8)
    3007+A1::REPLContext::IsAsynchronous(@7.8.1)
    30053008 @6.1 的部分保证依赖特定的实现选项支持(如 NPL_Impl_NPLA1_Enable_ThunkedSeparatorPass(@7.9.2) ),当没有启用时可能不满足。
    30063009
    30073010 @7.1.1 对象语言约定:
    @@ -3519,12 +3522,34 @@
    35193522 若项是具有一个子项的列表节点,则规约为这个子项;
    35203523 若项是具有多个子项的列表节点,依次调用 NPL::ReduceHeadEmptyList(@6.7.1) 、A1::ReduceFirst(@7.4.6) 和 A1::ReduceCombined(@7.6.4.2) 规约列表项;
    35213524 否则调用 A1::ReduceLeafToken(@7.6.4) 规约叶节点。
    3522-成员函数 A1::REPLContext::IsAsynchronous 判断是否启用异步规约实现(@7.9.2) 。
    3523-REPL 对象内部保存求值表达式一次预处理的处理器作为数据成员。这在 @7.8.1 的算法前生效,不处理子表达式。
    3525+A1::REPLContext(@7.8.1)
    35243526 函数模板 A1::TryLoadSource 从上下文尝试加载代码。
    35253527
    3526-@7.8.1 NPLA1 规范(canonical) 求值算法:
    3527-A1::REPLContext 配置的主规约函数具有的求值算法(@7.4.4) 称为 NPLA1 规范求值算法。
    3528+@7.8.1 类 A1::REPLContext :
    3529+A1::REPLContext REPL 是上下文对象类。
    3530+类 A1::REPLContext 还提供以下 API :
    3531+数据成员 Allocator
    3532+数据成员 Root
    3533+数据成员 Preprocess 是内部保存的求值表达式一次预处理的处理器。这在 @7.8.2 的算法前生效,不处理子表达式。
    3534+数据成员 ConvertLeaf
    3535+数据成员 ConvertLeafSourced
    3536+数据成员 CurrentSource
    3537+数据成员 UseSourceLocation
    3538+数据成员 Load 默认初始化为 DefaultLoad 。
    3539+数据成员 OutputStreamPtr
    3540+成员函数 IsAsynchronous 判断是否启用异步规约实现(@7.9.2) 。
    3541+成员函数 GetOutputStreamRef
    3542+静态成员函数 DefaultLoad
    3543+成员函数模板 LoadFrom
    3544+成员函数模板 Perform
    3545+成员函数 Prepare
    3546+成员函数模板 Prepare
    3547+成员函数 ReadFrom
    3548+成员函数模板 ReadFrom
    3549+成员函数模板 ShareCurrentSource
    3550+
    3551+@7.8.2 NPLA1 规范(canonical) 求值算法:
    3552+A1::REPLContext(@7.8.1) 配置的主规约函数具有的求值算法(@7.4.4) 称为 NPLA1 规范求值算法。
    35283553 NPLA1 规范求值算法和 [RnRK] 中定义的 Kernel 求值算法(以及 [Shu10] 中定义的 vau 演算(@1.3) )类似,差异为:
    35293554 规约函数类型签名决定输入的表示被求值项(@5.5) 的数据结构 TermNode 是真列表(@5.4.2.1) 而不是有序对,但作为真列表子集功能实质等价;
    35303555 求值算法不直接约定取得 WHNF(@4.4.3.1) 以外的子项是否被求值,而由被调用的 A1::FormContextHandler::operator() (@7.6.1.2) 决定(另见 [RnRK] 关于 unwrap 的 Rational 的描述。);
    @@ -3810,24 +3835,24 @@
    38103835 这能同时支持同步和异步实现,同时在当前动作以序列表示时,不需要无效化被组合的动作的宿主对象。
    38113836
    38123837 @7.11.3 函数合并(@4.5.3) 的实现:
    3813-NPLA1 规范求值算法(@7.8.1) 相对 Kernel 更简单以支持函数合并的 TCO 实现(参见 @7.11.5)。
    3838+NPLA1 规范求值算法(@7.8.2) 相对 Kernel 更简单以支持函数合并的 TCO 实现(参见 @7.11.5)。
    38143839 对应地,函数合并和 klisp 的 eval 实现略有不同:在处理器调用(@7.6.1.2) 实现参数列表中参数的求值。
    38153840 NPLA1 使用包装数(@7.6.1.1) 处理包装调用,支持多重包装(这在 [RnRK] 被求值算法蕴含);而当前 klisp 的实现没有正确地支持多重包装调用,参见:https://bitbucket.org/AndresNavarro/klisp/issues/11 。
    38163841 和 klisp 类似,参数列表求值前检查不存在实际参数(而无需求值)的情形进行优化。
    38173842
    38183843 @7.11.4 PTC 和尾调用本机实现:
    38193844 符合 PTC 的尾调用通过 TCO 动作(@7.10.3) 实现。
    3820-在规范求值算法(@7.8.1) 的 PTC 通过显式构造 TCO 动作以及在此基础上的操作压缩(@7.10.3.2) 实现。
    3845+在规范求值算法(@7.8.2) 的 PTC 通过显式构造 TCO 动作以及在此基础上的操作压缩(@7.10.3.2) 实现。
    38213846 这对应 klisp 的 ktail_call 实现 Kernel 的默认求值算法(包括 [RnRK] 的 eval 应用子的底层的操作子);但和后者依赖 GC 不同,TCO 动作可对环境有所有权(@7.10.3.3) 。
    38223847 klisp 还使用 ktail_call 提供本机实现。NPLA1 也可使用类似的实现。
    38233848 其它更一般的情况下,在求值上下文(@4.4.7) 中隐含已构造 TCO 动作为前提,通过直接或间接调用 A1::ReduceOnce(@7.4.4) 提供 PTC 保证。
    38243849 这对应 klisp 的 ktail_eval ;但和后者不同,A1::ReduceOnce 调用确切地只对应不显式引入新的环境的操作。
    3825-可能引入新的环境的操作,如 [RnRK] 的 eval(对应 NPLA1 对象语言函数 eval 和 eval% 等,参见 @10.5.5 ),需要另行调用比 A1::ReduceOnce 更复杂的操作压缩。
    3850+可能引入新的环境的操作,如 [RnRK] 的 eval(对应 NPLA1 对象语言函数 eval 和 eval% 等,参见 @10.5.6 ),需要另行调用比 A1::ReduceOnce 更复杂的操作压缩。
    38263851 和 Kernel 要求以外,因为维护资源(@7.10.4) 的需要,TCO 动作在函数合并的底层操作子合并前被构造,因此进入参数求值时在资源管理的意义上也在所在函数合并的同一个尾上下文(@4.4.7) 。
    38273852 另见 @7.11.5 。
    38283853
    38293854 @7.11.5 TCO 动作(@7.10.3) 实现和限制:
    3830-因为维护资源(@7.10.4) 的需要,规范求值算法(@7.8.1) 不处理 WHNF 以外的操作数且允许宿主类型的调用(@7.6.1.2) 处理 WHNF 的第一项。
    3855+因为维护资源(@7.10.4) 的需要,规范求值算法(@7.8.2) 不处理 WHNF 以外的操作数且允许宿主类型的调用(@7.6.1.2) 处理 WHNF 的第一项。
    38313856 当前在 TCO 动作中被分配的临时对象(@5.2.4.2) 的操作都针对作为 WHNF 的第一项的合并子,其中合并子相等性由 @8.4.5.5 或其它宿主实现提供的 == 操作确定。
    38323857 由 @8.4.5.5 ,捕获不同环境的 vau 合并子不相等;不论是否具有相等的外部表示(@4.1) 和来源,其右值不会被直接操作压缩(@7.10.3.2) 避免多次添加而被 TCO ;若其嵌套调用因为捕获不同的存在引用关系的环境,不支持其它形式的 TCO ;因此,不支持 PTC 。
    38333858 注意上述情形在其它语言中因为可达性分析等可能具有实现缺陷也不会释放存储,如 Kernel 程序 ($sequence ($define! f ($lambda (n) ((($vau (x y) e (eval ($sequence x y) e)) n (f n))))) (f 1)) 在 klisp 中实测不支持 PTC 。
    @@ -3854,6 +3879,7 @@
    38543879 eval-accl-accl
    38553880 eval-accl-tail
    38563881 eval-accr-accr
    3882+eval-accr-foldr
    38573883 eval-accr-sum
    38583884 eval-argument-list
    38593885 eval-booleans
    @@ -3889,6 +3915,7 @@
    38893915 eval-forward-list-first :Forms::ForwardListFirst(@8.4.3) 取列表第一个元素。
    38903916 eval-guard :非 TCO 的求值实现重置守卫的附加动作帧(@5.10.6)。
    38913917 eval-lift-result :非 TCO 的求值实现调用 NPL::ReduceForLiftedResult(@6.7.1) 提升返回结果。
    3918+eval-lift-sum :Forms::AccR、Forms::FoldR1 和 Forms::Map1(@8.4.9) 公共的部分和提升调用。
    38923919 eval-map1-appv :Forms::Map1(@8.4.9) 应用子调用。
    38933920 eval-map1-cons :Forms::Map1 列表构造调用。
    38943921 eval-sequence-return :非 TCO 的 A1::ReduceOrdered(@7.4.6) 实现中设置规约合并最后的返回结果。
    @@ -4189,8 +4216,11 @@
    41894216 @9.2 规约文法约定:
    41904217 为描述语言规则,约定适用 @9 的文法(@2.3.2) 形式。注意这不需依赖 NPL 语言的基本文法(@3) 。
    41914218 规约操作中项的约束通过以 <> 中的同类名称表示。
    4192-
    4193-@9.2.1 元文法基本约定如下:
    4219+本节描述的项是被用于求值(参见 @9.4.1 )的项或它们的直接文法组合。前者应能涵盖原子表达式(@3.4.2) 、其求值结果以及预期在对象语言中实现 @9.4.1 所需的 NPLA1 用户程序(@9.1) 构造。
    4220+库可参照本节的方式约定通过库引入的项的文法。
    4221+除非另行指定,对不确定具体项内容的对应要求同时适用于本节中和这些库中引入的项。
    4222+
    4223+@9.2.1 元文法基本约定:
    41944224 ... :Kleene 星号,重复之前修饰的项 0 次或多次。
    41954225 + :重复之前修饰的项 1 次或多次。
    41964226 ? :重复之前修饰的项 0 次或 1 次。
    @@ -4207,16 +4237,16 @@
    42074237 根据是否可作为操作子中指定不被求值的函数参数,本节的操作数及其子项分为未求值的操作数(@9.2.2.1) 和求值得到的操作数(@9.2.2.2) 。
    42084238
    42094239 @9.2.2.1 未求值的操作数:
    4210-未求值的操作数作为数据结构和 AST(@5.3.2) 同构,是符号(@5.6.1) 或未求值操作数构成的真列表(@9.6.3) 。
    4240+未求值的操作数作为数据结构和 AST(@5.3.2) 同构,是符号(@5.6.1) 或未求值操作数构成的真列表(@9.6.3) ,是未求值的 AST 子项或求值规约(@4.4) 未修改输入的项。
    42114241 未求值的操作数的文法约定如下:
    42124242 <symbol> :符号。
    4213-基于 @5.6.1 ,内部使用和 <string>(@9.2.3) 一一对应的表示,不提供符号和外部表示的其它映射关系。
    4243+基于 @5.6.1 ,内部使用和 <string>(@9.2.2.2) 一一对应的表示,不提供符号和外部表示的其它映射关系。
    42144244 <symbols> :元素为 <symbol> 的列表,形式为 (<symbol>...) 。
    42154245 <eformal> :表示可选提供的环境名称或 #ignore 的符号。
    42164246 使用和 <symbol> 相同的表示。通常为动态环境。
    42174247 <expression> :待求值的表达式。
    42184248 <expressions> :形式为 <expression>... 的待求值形式。
    4219-求值时,<expressions> 被作为单一表达式(即视为 (<expression>...) ),代替 <expression> 可避免语法中要求过多的括号及 eval(@10.5.5) 等求值形式中显式构造列表的需要。
    4249+求值时,<expressions> 被作为单一表达式(即视为 (<expression>...) ),代替 <expression> 可避免语法中要求过多的括号及 eval(@10.5.6) 等求值形式中显式构造列表的需要。
    42204250 <binding> :绑定列表,形式为 <symbol> <expressions> ,用于指定被求值的表达式和绑定参数的符号。
    42214251 和 Kernel 不同,<symbol> 后不要求是整个 <expression> 。
    42224252 <bindings> :元素为 <binding> 的列表,形式为 (<binding>...) 。
    @@ -4245,52 +4275,45 @@
    42454275 <applicative> :应用子(@4.5.3.2) 。
    42464276 <predicate> :谓词,是应用操作数的求值结果的值为 <bool> 的 <applicative> 。通常实现为纯求值(@4.4.2) 。
    42474277 <environment> :一等环境(@9.6.2) 。
    4278+<string> :字符串,宿主值类型为 string(@5.6.1) 。
    4279+字符串(包括字符串字面量求值的结果)以 string 类型表示,编码和表示要求同 YFramework 默认约定([Documentation::YFramework @@3.3.1]) 。
    42484280
    42494281 @9.2.2.4 文法形式补充约定:
    42504282 和 [RnRK] 不同,<definiend> 和 <formals> 不要求重复符号检查。另见 @7.7.3 。
    42514283 [RnRK] 和 [Shu10] 都没有显式区分 <definiend> 和 <formals> ,两者在上下文中实质可互换,差别仅在 [RnRS] 中的 define 形式中定义的位置可具有和 <formals> 不兼容的扩展形式。
    4252-本文档补充约定,使用 <formals> 的情形包括合并子基本操作(@10.5.6) 和可通过这些操作派生的操作在对应位置的操作数。
    4284+本文档补充约定,使用 <formals> 的情形包括合并子基本操作(@10.5.7) 和可通过这些操作派生的操作在对应位置的操作数。
    42534285 针对这个假设,派生实现可进行附加的语义检查。在 NPLA1 的当前实现(特别地,使用 @7.7.4 的 API )中,没有这样的检查。
    42544286 这实质等价使用 [Shu09] 中的记法,即 <formals> 用于除和 [Shu09] 的 $define! 类似外的所有操作(包括 $set! 和 $let 等,而不论是否对应 <body> )。这些上下文中总是隐含了上述的可派生实现的要求。
    42554287
    4256-@9.2.3 派生实现类型:
    4257-部分库中使用的特定类型的项如下:
    4258-<box> :箱(@4.2.3.5.3) 。
    4259-<promise> :求值代理:表示可被求值的封装结果。
    4260-<string> :字符串,宿主值类型为 string(@5.6.1) 。
    4261-<regex> :正则表达式。
    4262-字符串以 string 类型表示,编码和表示要求同 YFramework 默认约定([Documentation::YFramework @@3.3.1]) 。
    4263-正则表达式以 std::regex 类型表示,实现保证可通过 string 初始化。
    4264-
    42654288 @9.3 对象语言语法:
    42664289 基于 NPLA 基本语法约定参见 @5.2 。
    42674290 表达式的语法符合 @3.4.2 。
    42684291
    42694292 @9.3.1 字面量(@3.3.3) :
    4270-由 @5.2 、@5.6.1 和 @9.2.3 ,字符串字面量的类型为 <string> 。
    4293+由 @5.2 、@5.6.1 和 @9.2.2.2 ,字符串字面量的类型为 <string> 。
    42714294 NPLA1 支持以下扩展字面量(@3.3.3) :
    42724295 #t :类似 Kernel 的 #t 字面量,类型为 <bool> 。
    42734296 #f :类似 Kernel 的 #f 字面量,类型为 <bool> 。
    42744297 #true :同 #t 。
    42754298 #false :同 #f 。
    42764299 #inert :类似 Kernel 的 #inert 字面量,宿主值为 ValueToken::Unspecified(@7.2.2) 而不具有单独的类型(因此也不需要定义 inert? 谓词)。
    4277-另见 @10.3 。
    4300+另见 @10.5.1 。
    42784301 NPLA1 字面量都是纯右值(@5.5.1) ,但总是允许实质化转换(@5.5.4) 为消亡值并引入可互操作的临时对象(@5.5.5.3) 。
    42794302 这和宿主语言的字符串字面量是左值不同。当前 NPLA1 对象语言不提供能引起互操作差异的接口(字符串字面量不被修改),以后可能改变。
    42804303
    42814304 @9.3.2 函数合并(@4.5.3) :
    4282-和 Scheme 及 Kernel 不同,求值算法决定求值为函数合并表达式的语法表达不需要括号,且具有不同的函数合并形式(@7.8.1) 。
    4305+和 Scheme 及 Kernel 不同,求值算法决定求值为函数合并表达式的语法表达不需要括号,且具有不同的函数合并形式(@7.8.2) 。
    42834306 以下使用 ...(@9.1) 作为函数的操作数时,可支持没有操作数的函数合并。此情形下应用表达式仍需要前缀 () ,但不在以下规约文法中显式地表示。
    42844307
    42854308 @9.4 基本语义规则和约定:
    4286-除非另行指定,约束(@9.2) 名称后的整数序数仅用于区分允许的同类约束项不同,无其它附加含义。
    4309+为区分同类约束(@9.2) 的不同项,约束的名称后(在 > 之前)的可带有以 1 起始的正整数序数。除非另行指定,这些序数仅用于区分不同的同类约束项,无其它附加含义。
    42874310 本节中除循环引用(@9.6.1.1) 的限制外,不支持的特性可能会在之后的实现中扩展并支持。
    42884311 NPLA1 对象语言程序中的未定义行为(@2.3.2) 包括 NPLA 未定义行为(@5.2.2) 和以下扩展 NPLA 未定义行为:
    42894312 特定情形下访问被修改的环境中绑定的对象(@9.6.2) 。
    42904313
    42914314 @9.4.1 求值算法:
    4292-除非另行指定,NPLA1 对象语言的求值总是使用规范求值算法(@7.8.1) 。
    4293-在 REPL(@7.8) 中输入求值算法接受的语法形式之前,求值可使用基于中缀变换(@7.5.2) 识别的 ; 和 , 分隔符,分别表示对被分隔的序列参数进行有序和无序列表求值(替换后合并子功能对应参考实现环境中函数 $sequence(@10.6.1) 和 list%(@10.5.4) 求值后的合并子)。
    4315+除非另行指定,NPLA1 对象语言的求值总是使用规范求值算法(@7.8.2) 。
    4316+在 REPL(@7.8) 中输入求值算法接受的语法形式之前,求值可使用基于中缀变换(@7.5.2) 识别的 ; 和 , 分隔符,分别表示对被分隔的序列参数进行有序和无序列表求值(替换后合并子功能对应参考实现环境中函数 $sequence(@10.6.1) 和 list%(@10.5.5) 求值后的合并子)。
    42944317 其中,分隔符 , 优先组合。
    42954318 对分隔符的处理使用和组合顺序相反的两遍分别对 ; 和 , 遍历替换,实现参见 @8.5 。
    42964319 由此引起的其它语法差异参见 @9.5.2 。
    @@ -4326,7 +4349,7 @@
    43264349
    43274350 @9.4.3.1 错误(@2.5.2) :
    43284351 NPLA1 中的错误是按接口的约定不符合预期的正常条件(如不被正常处理的操作数类型(@9.4.3.4) )引起的诊断。
    4329-求值特定的表达式可引起(@2.5.2) 错误,包括语法错误(@7.8.1) 和其它的语义错误。
    4352+求值特定的表达式可引起(@2.5.2) 错误,包括语法错误(@7.8.2) 和其它的语义错误。
    43304353 其它错误包括求值特定的函数应用(@9.4.3) ,由具体操作指定(参见 @10.4.1 )。
    43314354 程序可通过引发(raise) 错误对象(error object) 指定引起诊断。
    43324355 除非另行指定,NPLA1 的错误对象不需要是 NPLA1 支持的对象,而可以仅在宿主实现中可见。
    @@ -4340,7 +4363,7 @@
    43404363 引发错误对象(@9.4.3.1) 可能通过抛出异常实现。被抛出的宿主语言异常对象是错误对象。被抛出的异常类型可具有被显式指定的 public 基类,这些基类应无歧义以允许宿主语言捕获。
    43414364 若存在依赖错误(@9.4.3.1) 且引发被依赖的错误对象使用抛出异常实现,使用宿主语言标准库的嵌套异常(nested error) 机制实现依赖错误。
    43424365 除非由宿主环境(@2.7.1) (包括 @9.4.3.3 中指定的情形)、对 TermNode(@5.4.2) 的操作内部实现或另行指定的来源,被抛出的异常对象的类型总是 NPL::NPLException(@6.3) 或其 public 派生类。
    4343-若语法错误(@7.8.1) 使用抛出异常实现,则异常对象的类型是 NPL::InvalidSyntax(@6.3) 或其 public 派生类。
    4366+若语法错误(@7.8.2) 使用抛出异常实现,则异常对象的类型是 NPL::InvalidSyntax(@6.3) 或其 public 派生类。
    43444367 除显式指定的情形,抛出的具体异常类型未指定。通常实现可根据接口的含义使用 @6.3 中最合适的异常。
    43454368
    43464369 @9.4.3.3 运行时(@2.4.1) 错误条件(@2.5.2) :
    @@ -4374,6 +4397,14 @@
    43744397 和 Kernel 不同,NPLA1 不要求对象和其它实体存在外部表示,也不要求外部表示唯一。
    43754398 NPLA1 当前不提供可移植的互操作接口(包括 I/O 操作),也不约定其涉及的外部表示形式。用户可自行通过调用 NPLA1 宿主语言中的实现的 API(@7) 扩展。
    43764399
    4400+@9.4.4.1 文本形式:
    4401+除非另行指定,外部表示共享 NPLA 词法分析(@5.3.1) 兼容的字符串格式。另见 @5.2 。
    4402+对和实现环境(@2.3.2) 交互的操作,当前使用外部表示附加约定如下:
    4403+对来自任意外部文件的文本流的输入和输出:
    4404+ 首先忽略 UTF-8 BOM(若存在),不保证检查其余的内容相对使用的编码的有效性。
    4405+ 和 @5.3.1 一致的方式使用二进制模式处理内容(如处理换行)。
    4406+若程序的实现逻辑依赖具体编码,处理相对编码非法的流内容的程序行为未指定。
    4407+
    43774408 @9.4.5 对象同一性(@4.1) :
    43784409 NPLA1 的对象是一等对象(@4.1) 。由定义,NPLA1 的对象默认确保同一性。
    43794410
    @@ -4413,7 +4444,7 @@
    44134444 当前实现不使用对象驻留,以简化存储对象的互操作(@5.2.4) 。
    44144445
    44154446 @9.4.5.3 等价比较:
    4416-和 Kernel 不同,一些类型的对象若可修改(@9.4.5.1)(如环境(@9.6.2) ),不保证 eq? 和其它等价谓词(@10.5.1) 的比较结果等价。
    4447+和 Kernel 不同,一些类型的对象若可修改(@9.4.5.1)(如环境(@9.6.2) ),不保证 eq? 和其它等价谓词(@10.5.2) 的比较结果等价。
    44174448 但是,基于 @6.9.1 已约定宿主值的类型,在对象语言中传递环境引用(@5.4.3) 不改变环境的这些等价性。
    44184449 为了满足不依赖引用的一等对象,区分引用和被引用的对象都作为一等实体,这是必要的。
    44194450 一般地,谓词 eq? 用于判断两个参数对象的同一性。
    @@ -4446,8 +4477,8 @@
    44464477 提供 PTC 保证的尾上下文和 Kernel 类似,当前仅包括 PTC 不改变可观察行为(@4.1.3) 时的:
    44474478 函数调用(@4.5.3.1) 中,(使用 $vau 和 $lambda 等合并子抽象引入的)合并子(@9.6.4) 的调用(注意不包括其它途径引入的外部函数的调用);
    44484479 对 <body>(@9.2.2.1) 的求值;
    4449-由 <test>(@9.2.2.1) 控制或连续求值多个表达式中最后被求值的子表达式求值,包括 $if(@10.5.2) 、$sequence(@10.6.1) 、$and?(@10.6.1) 和 $or?(@10.6.1) ;
    4450-函数 eval(@10.5.5) 、eval%(@10.5.5) 、$quote(@10.6.1) 、id(@10.6.1) 、idv(@10.6.1) 、eval-string(@11.1) 和 eval-string%(@11.1) 对 <expression> 的求值或替换;
    4480+由 <test>(@9.2.2.1) 控制或连续求值多个表达式中最后被求值的子表达式求值,包括 $if(@10.5.3) 、$sequence(@10.6.1) 、$and?(@10.6.1) 和 $or?(@10.6.1) ;
    4481+函数 eval(@10.5.6) 、eval%(@10.5.6) 、$quote(@10.6.1) 、id(@10.6.1) 、idv(@10.6.1) 、eval-string(@11.1) 和 eval-string%(@11.1) 对 <expression> 的求值或替换;
    44514482 函数 apply(@10.6.1) 对 <applicative> 和 <object> 在 <environment> 或默认的动态环境中的应用;
    44524483 函数 accl(@10.6.1) 、map-reverse(@10.6.2) 和 for-each-ltr(@10.6.2) 蕴含的尾上下文的递归调用。
    44534484
    @@ -4460,7 +4491,7 @@
    44604491 实现 PTC 时被规约时替换结果的清理(@5.7.4) 若实现支持 TCO(@7.4) 且没有实现保存未绑定操作数引起这种情形。
    44614492 当前通过参考实现环境(@10) 引入的合并子已经支持了保存操作数,但若互操作没有保存操作数,则可能产生问题。
    44624493 应注意合并子的参数绑定引用值(@7.7.3) 在同一个尾上下文求值引起清理导致悬空引用(@5.6.3) 破坏内存安全(@5.2.4.3) 。
    4463-这里的求值典型地可通过显式求值引入。具体地,这主要包含使用引用值作为函数 eval 或 eval%(@10.5.5) 的参数的进行函数应用表达式求值的方式。
    4494+这里的求值典型地可通过显式求值引入。具体地,这主要包含使用引用值作为函数 eval 或 eval%(@10.5.6) 的参数的进行函数应用表达式求值的方式。
    44644495 对合并子的直接的函数调用求值较不容易引起这种情形,因为 <body> 内使用的参数是被绑定的,其中临时对象即便传递引用也会被转移值(@7.7.3) 。悬空引用通过其它方式(如在合并子的动态环境内显式求值)才被直接引入。
    44654496 考虑在调用前使用总是不保留引用值(@9.6.1.5) 的 list 和 list* 而不是保留引用值(@9.6.1.5) 的 list% 和 list*%(@10.6.1) ,或单独调用 idv(@10.6.1) 提升被传递项中的值避免此类问题。
    44664497
    @@ -4561,6 +4592,8 @@
    45614592
    45624593 @9.6.2 环境:
    45634594 和 Kernel 类似,NPLA1 支持一等环境(first-class environment) 。
    4595+和 Kernel 类似,NPLA1 的一等环境可包含一个或多个父环境(@5.4.3) ,其重定向(@4.3.3) 使用 DFS 形式的默认重定向算法(@5.4.3) 。
    4596+新环境(fresh environment) 是新创建的环境。除非另行指定,新环境是不存在能引起程序行为改变的父环境的空环境(@4.6.1.1) 。
    45644597 和 Kernel 不同,环境绑定(@4.1) 的抽象不依赖对象语言中表达的引用的概念,允许直接关联一个没有引用的值。另见 @4.2.3 和 @5.4.3 。
    45654598 和 Kernel 不同,环境绑定对被绑定的对象具有所有权(@5.4.3) 。除非另行指定(参见 @10.1.2 ),这种直接所有权是独占的。
    45664599 和 Kernel 不同,环境对象符合默认的等价比较规则(@9.4.5.3) ,不提供不同等价谓词的结果一致性。
    @@ -4568,7 +4601,7 @@
    45684601 但环境在特定情形仍保证稳定性(stability) ,即总是可假定绑定维持一定意义的等价性。违反环境稳定性引起未定义行为(@9.4) 。
    45694602 当前约定稳定性包括隐藏环境(hidden environment) 的绑定有效稳定性(@9.6.2.2) 和值稳定性(@9.6.2.3) ,其中隐藏环境指不能被对象语言作为一等对象的环境。
    45704603 一般地,隐藏环境是某一个(非隐藏的)一等环境的直接或间接父环境(而能通过求值等间接操作被访问)。
    4571-和 Kernel 不同,环境(@10.5.5) 和合并子操作(@10.5.6) 及基于这些操作的一些派生操作(@10.6) 的操作数树(@7.7.3) 构造不检查其中的非列表项是否都为符号或 #ignore ;匹配时不检查符号重复;若形式参数中的符号重复,则绑定的目标未指定。
    4604+和 Kernel 不同,环境(@10.5.6) 和合并子操作(@10.5.7) 及基于这些操作的一些派生操作(@10.6) 的操作数树(@7.7.3) 构造不检查其中的非列表项是否都为符号或 #ignore ;匹配时不检查符号重复;若形式参数中的符号重复,则绑定的目标未指定。
    45724605 此外,NPLA1 提供单独的递归绑定符号的机制,且明确支持在操作数中同时递归绑定之前未被绑定的多个符号(@9.5.2.1) 。
    45734606 环境对应多个宿主值类型(@6.9.3) 。
    45744607 关于环境的比较,另见 @9.4.5.3 。
    @@ -4785,7 +4818,7 @@
    47854818 因不预期通过派生实现,基本操作总是通过本机实现提供。
    47864819 要求通过基础环境直接或间接提供的库(@9.1) 总称标准库(standard library) 。
    47874820 标准库的接口随语言规范在参考实现环境(@10) 和参考实现扩展环境(@11) 约定。
    4788-核心库(core library) 是提供直接绑定在基础环境中的派生操作的标准库模块。
    4821+核心库(core library) 是提供直接绑定在基础环境中的保证可派生实现的接口的标准库模块。
    47894822 在参考实现环境中的不同标准库模块的绑定都可在基础环境访问;
    47904823 在参考实现扩展环境中的标准库模块以其它环境(通常作为基础环境的子对象提供)中的绑定和基础环境隔离。
    47914824 派生实现可以库的形式提供语言扩展或其它功能特性,扩充标准库。
    @@ -4794,7 +4827,8 @@
    47944827 若符号和先前出现的函数同名,则指称对应的操作;
    47954828 默认使用基础环境作为求值环境。
    47964829 注意这些约束作用于接口描述。不改变可观察行为(@4.1.3) 时,实现可使用不同的求值(如使用不同于基础环境的根环境)。
    4797-@10.5 提供在基础上下文(@8.5.2) 初始化的基本操作,作为单独的模块。
    4830+本章中的接口在根环境中提供,统称根环境接口。
    4831+@10.5 提供在基础上下文(@8.5.2) 初始化的根环境基本(primitive) 接口,作为单独的模块。
    47984832 @10.6 起的各节提供其余要求 NPLA1 实现直接支持的各个模块的操作。
    47994833
    48004834 @10.1.1 限制:
    @@ -4813,18 +4847,18 @@
    48134847 第一类不安全操作和引用值的使用(特别是保留引用值(@9.6.1.5) )相关,具体规则参见 @10.1.2.1 、@10.4.1 和 @10.4.2 。
    48144848 其中,可引起扩展 NPLA 未定义行为(@9.4) 的操作包括以下可能破坏环境稳定性(@9.6.2) 的第一类不安全操作(@10.1.2.1) :
    48154849 赋值操作(@10.4.1.3) ;
    4816-move!(@10.5.3) 。
    4817-第二类不安全操作可能通过非引用的操作引入循环引用破坏环境的资源所有权,其中的基本操作在 @10.5.5 提供,对应的函数名如下:
    4850+move!(@10.5.4) 。
    4851+第二类不安全操作可能通过非引用的操作引入循环引用破坏环境的资源所有权,其中的基本操作在 @10.5.6 提供,对应的函数名如下:
    48184852 copy-environment
    48194853 lock-environment
    48204854 第二类不安全操作中的派生操作在 @10.6.1 提供,对应的函数名如下:
    48214855 lock-current-environment
    48224856 第三类不安全操作支持强递归绑定(@9.5.2.1) ,对应的函数名如下:
    4823-$defrec!(@10.5.5)
    4857+$defrec!(@10.5.6)
    48244858 $setrec!(@10.6.1)
    48254859 不安全操作中,在参数以外直接引入间接值的操作有:
    4826-ref&(@10.5.3) ;
    4827-第三类不安全操作的强递归绑定过程中引用共享对象(参见 $defrec!(@10.5.5) )的中间值。
    4860+ref&(@10.5.4) ;
    4861+第三类不安全操作的强递归绑定过程中引用共享对象(参见 $defrec!(@10.5.6) )的中间值。
    48284862 NPLA1 参考实现环境中,仅有第三类不安全操作的实现内部可能引入引用持有者;参见 @9.4.2 。
    48294863
    48304864 @10.1.2.1 第一类不安全操作的子类:
    @@ -4849,9 +4883,9 @@
    48494883 @10.2 实体实现(@2.3.1.2) 约定:
    48504884 本节约定对 NPLA1 参考实现内部有效,不作用在用户程序。
    48514885 类似 [RnRK] ,派生操作可通过基本操作及派生操作实现。
    4852-除非另行指定,NPLA1 参考实现环境作为公开接口的提供变量在根环境中绑定。
    4886+除非另行指定,NPLA1 参考实现环境作为公开接口提供的变量在根环境中绑定。
    48534887 除非是语义蕴含的操作结果或另行指定,所有作为函数值的操作(@10.1) 的结果(@4.1) 是未指定值(@7.2.2) 。另见 @9.9 。
    4854-未指定值可等于 #inert(@10.3) 或其它值,但满足忽略值时不引起可观察行为(@4.1.3) 的改变(这排除了引入 volatile 类型或非平凡析构的宿主值)。
    4888+未指定值可等于 #inert(@9.3.1) 或其它值,但满足忽略值时不引起可观察行为(@4.1.3) 的改变(这排除了引入 volatile 类型或非平凡析构的宿主值)。
    48554889 若操作(@10.1) 在 Kernel 或 klisp 中存在结果为 #inert 的对应的操作,且未指定作为函数值的结果,则返回等于 #inert 的右值。
    48564890 除非另行指定,空环境没有父环境。
    48574891 约定的接口通过绑定在根环境中的名称提供,参见以下各节。
    @@ -4865,7 +4899,7 @@
    48654899
    48664900 @10.2.1 函数实现使用约定:
    48674901 名称使用 $def 起始的函数用于在当前环境(@5.4.4) 引入绑定。
    4868-需引入绑定时,优先使用 @10.6.2 中的函数;当使用这些函数并不能简化实现时,使用 $def!(@10.5.5) 。
    4902+需引入绑定时,优先使用 @10.6.2 中的函数;当使用这些函数并不能简化实现时,使用 $def!(@10.5.6) 。
    48694903 其中优先使用函数名中没有 % 的函数,仅当语义要求时,使用 %! 结尾的函数,以减少不安全操作(@10.1.2) 并明确函数值是否可能只是非引用值(此时两者效果应一致)。
    48704904 这对应宿主实现中,使用 auto 而不是 auto&& 作为返回值;但宿主语言中返回非引用类型的表达式两者含义不同。
    48714905 尽管设计时没有参照,使用函数结尾的引用标记字符和其它一些语言的类似特性的使用惯例也一致,如 PHP 的 function & 语法:
    @@ -4874,13 +4908,7 @@
    48744908 与引入绑定不同,实现内部不要求类似的优先使用,在需要明确实现需要符合的要求或减小实现开销时,可使用提供以引用标记字符结尾版本的操作(@9.9.1) 替代其它操作。
    48754909
    48764910 @10.3 预定义对象:
    4877-特定名称的预定义对象以变量的形式在基础上下文(@8.5.2) 的根环境中初始化,提供具有预设目的的可编程特性。
    4878-ignore :类似 Kernel 的 #ignore 字面量,但不作为字面量而是普通对象,关联的值是记号值(@5.6.1) 。
    4879-注意 ignore 是一等对象(@4.1) ,和 Kenerl 为 #ignore 单独提供值域仅有一个单元类型(unit type) 不同。
    4880-原理:按 @1.4.2.3 ,提供单独的类型是不必要的。
    4881-从表达上,#inert(@9.3.1) 和 ignore 仍都可以被视为特定单元类型的值:等价的类型判断谓词可以直接使用值的相等关系确定。
    4882-#inert 的值是 ValueToken 宿主类型唯一直接在对象语言中提供的宿主值。
    4883-ignore 的值是完全作为标签使用的记号,具有记号值类型。
    4911+除操作外,特定名称的对象以变量的形式在库中初始化,直接提供具有预设目的的可编程特性。
    48844912
    48854913 @10.4 函数分类:
    48864914 本节对函数按名称和其它不同性质进行分类。
    @@ -4896,7 +4924,7 @@
    48964924 不带有引用标记字符结尾的操作通过避免保留引用值(@9.6.1.5) 提供一定的内存安全保证,而带有引用标记字符结尾的操作较容易引起注意。这符合易预测性(@1.4.5.2.1) 。
    48974925 带有引用标记字符结尾的不安全操作属于第一类不安全操作(@10.1.2) 。
    48984926 第一类不安全操作实际不满足内存安全的一个主要的必要非充分条件是操作保留引用值,且引用值在被引用对象的生存期外被使用。
    4899-这主要包括直接使用标识符求值为引用值的情形:因为标识符求值(@9.4.1) 后指称左值引用值(@7.8.1) 。
    4927+这主要包括直接使用标识符求值为引用值的情形:因为标识符求值(@9.4.1) 后指称左值引用值(@7.8.2) 。
    49004928 保留引用值操作的内存安全(@9.4.2) 依赖所有被保留的引用值在之后的使用中都满足内存安全。
    49014929 若保留引用值操作不返回引用值,则返回的值等价经过返回值转换(@9.6.1.4) 。
    49024930 一般仅在明确需要引用时,使用以引用标记字符结尾的版本的函数(特别是环境可能作为参数被使用时)。
    @@ -4911,10 +4939,10 @@
    49114939
    49124940 @10.4.1.1 可能使结果包含引用值的容器构造器:
    49134941 以下操作是容器构造器,包括:
    4914-cons(@10.5.4)
    4915-cons%(@10.5.4)
    4916-wrap(@10.5.6)
    4917-wrap%(@10.5.6)
    4942+cons(@10.5.5)
    4943+cons%(@10.5.5)
    4944+wrap(@10.5.7)
    4945+wrap%(@10.5.7)
    49184946 list(@10.6.1)
    49194947 list%(@10.6.1)
    49204948 list*(@10.6.1)
    @@ -4936,18 +4964,18 @@
    49364964 其中,带有引用标记字符结尾的操作是直接保留引用值操作;函数名不带有标记字符结尾的访问器属于参数转发操作(@9.8.2) 和函数值转发操作(@9.8.4) 。
    49374965 因为正规表示(@5.9.4) 的限制,rest 、rest& 和 rest% 重新构造列表,并不直接返回子对象;其它访问器若带有引用标记字符,可直接返回引用值。
    49384966 此外,@10.4.2.4 中部分函数也符合容器元素访问器的要求,但当前不提供带有后缀标记字符的版本。这些函数包括:
    4939-unwrap(@10.5.6)
    4967+unwrap(@10.5.7)
    49404968 unbox(@10.6.2)
    49414969
    49424970 @10.4.1.3 可能使对象中包含引用值的修改操作(@9.4.5.1) :
    49434971 以下操作的内存安全(@9.4.2) 依赖所有参数都满足内存安全:
    49444972 简单赋值(simple assignment) (包含于赋值操作(@9.4.5.1.1) ),包括:
    4945-assign@!(@10.5.3)
    4973+assign@!(@10.5.4)
    49464974 assign%!(@10.6.1)
    49474975 assign!(@10.6.1)
    49484976 列表元素改变器(mutator) ,包括:
    4949-set-rest!(@10.5.4)
    4950-set-rest%!(@10.5.4)
    4977+set-rest!(@10.5.5)
    4978+set-rest%!(@10.5.5)
    49514979 set-first!(@10.6.1)
    49524980 set-first@!(@10.6.1)
    49534981 set-first%!(@10.6.1)
    @@ -4959,8 +4987,8 @@
    49594987 一些操作可涉及不同的环境,参数在这些环境中被求值可能得到引用值。
    49604988 这些操作包括求值为操作子的以下函数:
    49614989 以求值 <body> 作为尾上下文的操作(@9.4.8.1) ,包括结果是合并子或用于在环境中绑定合并子的构造器操作,以及核心库函数(@10.6.2) 中的绑定操作;
    4962-以求值 <expression> 作为尾上下文的函数 eval 和 eval%(@10.5.5) 。
    4963-以上操作中,带有引用标记字符结尾的操作是间接保留引用值操作,表示求值结果不要求按值传递并可返回引用值,参见 @10.5.5 和 @10.6.2 。
    4990+以求值 <expression> 作为尾上下文的函数 eval 和 eval%(@10.5.6) 。
    4991+以上操作中,带有引用标记字符结尾的操作是间接保留引用值操作,表示求值结果不要求按值传递并可返回引用值,参见 @10.5.6 和 @10.6.2 。
    49644992 以上操作中的求值符合词法闭包(@4.6.1.1.2) 规则,可使用 A1::RelayForEval 或 A1::RelayForCall(@7.7.2) 实现。
    49654993 注意和此处直接在参数中给出被求值表达式不同,如 apply(@10.6.1) 等应用子中的求值的操作不属于上述操作(@10.4.2.5) 。
    49664994
    @@ -4980,7 +5008,7 @@
    49805008 被求值的 <test> 进行左值到右值转换,其它被求值的参数不进行左值到右值转换,调用者需负责决定是否求值其它参数。
    49815009 这类似宿主语言中参数传递和返回 auto&& 类型。
    49825010 这些操作当前包括:
    4983-$if(@10.5.2)
    5011+$if(@10.5.3)
    49845012 $sequence(@10.6.1)
    49855013 $cond(@10.6.1)
    49865014 $when(@10.6.1)
    @@ -4990,7 +5018,7 @@
    49905018
    49915019 @10.4.2.2 返回非引用值的操作:
    49925020 部分操作类似容器构造器(@10.4.1.1) 保证返回非引用值(@9.8.3) ,但并非直接以参数实现决定函数值:
    4993-若非构造器的操作总是返回列表、箱(@9.2.3) 或其它的封装类型(@10.5.8) 对象,返回的对象总是按值传递。
    5021+若非构造器的操作总是返回列表、箱(@10.6.2) 或其它的封装类型(@10.5.9) 对象,返回的对象总是按值传递。
    49945022 和作为构造器的操作不同,后者需区分结果中是否可能包含引用值(@10.4.1.1) :
    49955023 没有引用标记字符的操作(@9.9) 表示(假定一次折叠(@9.9.1) 后)不包含引用值的构造器;
    49965024 带有引用标记字符的操作表示可能包含引用值的构造器。
    @@ -5013,7 +5041,7 @@
    50135041 为简化接口及满足其它分类(如 @10.4.2.2 ),不适合提供不保留引用值的操作;
    50145042 和 @10.4.1.2 不同,这些操作并非用于取子对象,返回值不一定是引用值,和具体操作相关,不适合使用引用标记字符区分。
    50155043 本节约定的函数对引用标记字符的使用和 @10.4.1 的函数名的使用不一致,而相当于结尾的 % 。
    5016-本节约定的函数包括合并子基本操作中的合并子访问器 unwrap(@10.5.6) 。
    5044+本节约定的函数包括合并子基本操作中的合并子访问器 unwrap(@10.5.7) 。
    50175045 非构造器函数参数支持转发操作的这些操作还包括核心库函数(@10.6.2) 中的:
    50185046 accl
    50195047 accr
    @@ -5021,7 +5049,7 @@
    50215049 map1
    50225050 核心库函数(@10.6.2) 中的绑定操作的非 <environment> 的形式参数支持转发。
    50235051 注意 map1 同时返回非引用值(@10.4.2.2) 。
    5024-参数形式上被转发但操作的语义并非总是转发到其它操作的操作不使用本节的名称约定,如以下仅有第二参数支持转发的操作是提供结尾 % 或 & 对应名称的函数(@10.4.1) ,有对象基本操作(@10.5.3) 中的:
    5052+参数形式上被转发但操作的语义并非总是转发到其它操作的操作不使用本节的名称约定,如以下仅有第二参数支持转发的操作是提供结尾 % 或 & 对应名称的函数(@10.4.1) ,有对象基本操作(@10.5.4) 中的:
    50255053 assign%!
    50265054 assign@!
    50275055 此外,@10.4.2.4 中的函数值转发操作同时也是直接参数转发操作。
    @@ -5035,12 +5063,12 @@
    50355063 和 @10.4.1.1 引入引用值的情形不同,不带有后缀 % 相对不容易引起误用,因为返回值保留的引用可以继续被返回值转换(@9.6.1.4) 影响。
    50365064 例如,使用保证返回非引用值的涉及环境中求值的操作(@10.4.1.4) ,引用值会在引用的对象生存期结束前被返回值转换而不影响内存安全。
    50375065 NPLA1 参考实现环境的函数值转发操作包括以下访问对象或被引用对象自身或子对象的函数:
    5038-unwrap(@10.5.6)
    5066+unwrap(@10.5.7)
    50395067 id(@10.6.1)
    50405068 forward(@10.6.1)
    50415069 unbox(@10.6.2)
    50425070 基本操作(@10.5) 的不具有名称的相关操作中参数和函数值原生支持的转发操作包括:
    5043-使用 make-encapsulation-type(@10.5.8) 返回的访问器合并子。
    5071+使用 make-encapsulation-type(@10.5.9) 返回的访问器合并子。
    50445072
    50455073 @10.4.2.5 间接保留引用值的操作:
    50465074 部分不带有引用标记字符的操作是可能间接保留引用值的操作(@10.1.2.1) 。
    @@ -5052,14 +5080,14 @@
    50525080
    50535081 @10.4.3 引用值构造:
    50545082 当前构造子对象引用(@5.6.3.5) 的操作有:
    5055-基本操作中的 unwrap(@10.5.6) 。
    5083+基本操作中的 unwrap(@10.5.7) 。
    50565084 子对象引用具有非平凡非正规表示(@5.9.4.1) 。
    50575085
    5058-@10.5 基础基本操作(grounded primitive operations) :
    5059-基础基本操作在基础上下文(@8.5.2) 的根环境中初始化。
    5060-和 [RnRK] 不同,为简化设计,不提供可选(optional) 的合并子,而提供可选的模块(@11) 。
    5086+@10.5 根环境基本接口(@10.1) :
    5087+和 [RnRK] 不同,为简化设计,NPLA1 不提供可选(optional) 的合并子,而通过预定义对象(@10.3) 的形式提供可选的模块(@11) 。
    5088+根环境基本接口是除了这些模块的以变量绑定形式提供的不要求可派生实现的接口,被绑定对象(@5.4.3) 包括预定义对象(@10.3) 和在基础上下文(@8.5.2) 的根环境中初始化的基础基本操作(grounded primitive operations) 的实现。
    50615089 派生实现可以通过提供不公开不安全操作(@10.1.2) 的根环境,但不符合此处的规格要求(@2.3.1.2) 。
    5062-若派生实现不提供第三类不安全操作(@10.1.2) ,可以简化部分 @10.5.3 中与之关联的操作的实现。
    5090+若派生实现不提供第三类不安全操作(@10.1.2) ,可以简化部分 @10.5.4 中与之关联的操作的实现。
    50635091 部分可选的 Kernel 合并子被直接提供。
    50645092 和 Kernel 不同,一些函数显式地操作引用值(@5.6.3) ,包括未折叠的引用值(@5.6.3) 。
    50655093 和 Kernel 不同,求值算法不直接处理对象的引用值(@6.5.3.3) 。
    @@ -5069,10 +5097,20 @@
    50695097 inert?
    50705098 ignore?
    50715099 考虑(可变对象的)一等引用和绑定构造(@9.5.2) 绑定引用的的平摊复杂度,不提供需要同时转换不同层次子项的 copy-es-immutable 操作。
    5072-部分其它原理参见 @10.3 。关于引用值的处理另见 @10.4 。
    5100+部分其它原理参见 @10.5.1 。关于引用值的处理另见 @10.4 。
    50735101 其它没有包含在以下节中的 Kernel 操作可能会在之后的版本中被支持。
    50745102
    5075-@10.5.1 等价谓词(@4.1.4) :
    5103+@10.5.1 根环境对象:
    5104+根环境定义以下对象:
    5105+ignore :类似 Kernel 的 #ignore 字面量,但不作为字面量而是普通对象,关联的值是记号值(@5.6.1) 。
    5106+注意 ignore 是一等对象(@4.1) ,和 Kenerl 为 #ignore 单独提供值域仅有一个单元类型(unit type) 不同。
    5107+原理:按 @1.4.2.3 ,提供单独的类型是不必要的。
    5108+从表达上,#inert(@9.3.1) 和 ignore 仍都可以被视为特定单元类型的值:等价的类型判断谓词可以直接使用值的相等关系确定。
    5109+#inert 的值是 ValueToken 宿主类型唯一直接在对象语言中提供的宿主值。
    5110+ignore 的值是完全作为标签使用的记号,具有记号值类型。
    5111+以下各节引入的变量都表示操作。
    5112+
    5113+@10.5.2 等价谓词(@4.1.4) :
    50765114 等价谓词不应修改参数对象。
    50775115 用户定义的类型提供等价谓词应满足和 NPLA1 提供的等价谓词的语义一致的等价关系,否则若谓词被求值,行为未指定。
    50785116 开放类型映射(@5.2.3) 的类型系统(@4.6.2) 可能属于这种理论。不需要依赖序的等价谓词可以为名义类型(@4.6.2.1) 提供直接的支持。
    @@ -5098,7 +5136,7 @@
    50985136 类似 Kernel 和 Scheme 谓词的 equal? 可通过 eqv? 直接构造。因为列表的性质(@9.6.3) ,不需要支持循环引用(@9.6.1.1) ,可以直接派生。后者被视为基本的抽象而非实现细节。
    50995137 通常,等价谓词比较的求值应保证能终止且对非列表项和 n 个子项的列表分别具有 O(1) 和 O(n) 平摊复杂度。但语言不需要约束这个性质。
    51005138
    5101-@10.5.2 控制:
    5139+@10.5.3 控制:
    51025140 $if <test> <consequent> <alternate> :条件分支,按条件成立与否返回 <consequent> 或 <alternative> 之一,可能是引用值。
    51035141 $if <test> <consequent> :省略第三操作数的条件分支,条件成立时返回 <consequent> 。
    51045142 和 Kernel 不同而和 Scheme 类似,如 <test> 的求值结果非 #f 即选择 <consequent> ,且支持省略第三参数。
    @@ -5119,7 +5157,7 @@
    51195157 一般地,NPLA1 不提供强调只存在副作用的操作。返回未指定(而不要求被使用)的求值结果的情形并不表示只有副作用(@10.2) ,因为副作用是否存在原则上依赖具体操作。这和 Kernel 的 #inert 以及 Racket 的 #<void> 值即便在实现上都一致,但含义不同。
    51205158 另见 $when 的说明。
    51215159
    5122-@10.5.3 对象基本操作:
    5160+@10.5.4 对象基本操作:
    51235161 注意因为真列表(@9.6.3) 的限制,列表左值只能引用完整的列表的对象,而不支持部分列表。
    51245162 这影响 set-rest! 和 set-rest%! 的 <list> 参数。
    51255163 null? <object> :判断操作数是否为空列表。
    @@ -5161,7 +5199,7 @@
    51615199 赋值对象直接修改(@9.4.5.1) 被引用的对象,但不无效化(@9.4.5.4) 参数指定的引用。
    51625200 Scheme 的 set! 在 SRFI-17 提供具有类似作用的支持,但第一操作数限于 set! 且为特定的过程调用;Kernel 没有类似的操作。
    51635201
    5164-@10.5.4 列表基本操作:
    5202+@10.5.5 列表基本操作:
    51655203 cons <object> <list> :构造两个元素的列表。
    51665204 和 Kernel 不同,NPL 不支持列表中存在环,因此第二个操作数应为列表,否则引起错误(@9.4.3.1) 。
    51675205 cons% <object> <list> :构造两个元素的列表,保留引用值。
    @@ -5172,7 +5210,7 @@
    51725210 和 Kernel 的 set-cdr! 类似,但检查列表是左值。
    51735211 不检查修改操作(@9.4.5.1) 导致循环引用(@9.6.1.1) ,用户应自行避免未定义行为(@5.2.2) 。
    51745212
    5175-@10.5.5 环境:
    5213+@10.5.6 环境:
    51765214 和 Kernel 不同,环境可使用环境强引用和环境弱引用(@5.4.3) 蕴含不同的所有权。
    51775215 为避免引入过于容易引入循环引用(@9.6.1.1) ,仅通过个别操作引入环境强引用(另见 @5.4.3 ):
    51785216 make-environment
    @@ -5204,7 +5242,7 @@
    52045242 $def! <definiend> <expressions> :修改当前环境中的绑定。
    52055243 类似 Kernel 的 $define! ,但满足 @9.5.2 的约定。
    52065244 和 Kernel 的 $define! 不同,$def! 和 $defrec! 在求值 <expressions> 后,进行类型检查(@9.6.2.4) ,确保环境没有被冻结后添加绑定。
    5207-和 Kernel 类似,对在 <expressions> 中某些未被直接求值的子表达式(如 $lambda(@10.5.6) 的 <body>),因为其中的求值依赖 $def! 表达式求值后的环境,在之后仍可以实现递归。
    5245+和 Kernel 类似,对在 <expressions> 中某些未被直接求值的子表达式(如 $lambda(@10.5.7) 的 <body>),因为其中的求值依赖 $def! 表达式求值后的环境,在之后仍可以实现递归。
    52085246 由于递归调用依赖环境中的绑定,修改以上定义引入的绑定后可影响被递归函数的调用。
    52095247 对 <definiend> 中已存在的标识符的绑定,保证直接替换对象的值,对象的引用不失效(@9.6.2.2) 。
    52105248 $defrec! <definiend> <expressions> :修改绑定,同 $def! ,但在绑定时针对 <definiend> 指定的操作数树(@7.7.4) 中的绑定名称有附加的处理以支持直接递归。
    @@ -5219,20 +5257,20 @@
    52195257 求值表达式 $def! (a b) list b ($lambda () 1) 因为被求值的 b 未被绑定引起错误(@9.4.3.1) ;
    52205258 求值表达式 $defrec! (a b) list b ($lambda () 1) 不需要 a 或 b 已被绑定(即便 b 并不在 $lambda 的 <body> 中),求值后 a 为默认对象;
    52215259 求值表达式 $defrec! (b &a) list ($lambda () 1) b 绑定要求同上,但求值后 a 可能为默认对象(操作数树中的同级叶节点被未指定的绑定顺序影响)。
    5222-这也允许在 $vau/e(@10.5.6) 等表达式的 <environment> 指定的静态环境使 <body> 不能访问目标环境时,直接定义递归函数。
    5260+这也允许在 $vau/e(@10.5.7) 等表达式的 <environment> 指定的静态环境使 <body> 不能访问目标环境时,直接定义递归函数。
    52235261 递归定义的对象中的值数据成员(@5.4.2) 可能具有共享的持有者。若为合并子,直接调用会利用替换的值重新访问所在的环境。复制和转移这样的值不会改变被访问的环境。若访问的环境失效,则抛出异常,或无限递归调用自身。
    5224-特定情形使用 deshare(@10.5.3) 可去除共享和避免以上可能非预期的行为。
    5262+特定情形使用 deshare(@10.5.4) 可去除共享和避免以上可能非预期的行为。
    52255263 另见 @9.6.2 。
    52265264
    5227-@10.5.6 合并子:
    5265+@10.5.7 合并子:
    52285266 和 Scheme 及 Kernel 不同,<body> 可以是多个项,而不在派生另外的版本支持顺序求值。
    5229-引入合并子的操作子不求值 <body> ,后者在被调用时替换操作数以后被求值。这允许安全地使用 $def! 而不需要 $defrec! 进行递归绑定(@10.5.5) 。
    5267+引入合并子的操作子不求值 <body> ,后者在被调用时替换操作数以后被求值。这允许安全地使用 $def! 而不需要 $defrec! 进行递归绑定(@10.5.6) 。
    52305268 创建合并子时对参数指定的求值环境 <environment> 进行类型检查(@9.4.3.4.1) 外,还进行内部的检查确保宿主值非空。
    5231-检查失败的错误(@9.4.3.1) 是(可能依赖(@9.4.3.1) 类型错误(@9.4.3.4.1) 的)语法错误(@7.8.1) 。
    5269+检查失败的错误(@9.4.3.1) 是(可能依赖(@9.4.3.1) 类型错误(@9.4.3.4.1) 的)语法错误(@7.8.2) 。
    52325270 $vau/e <environment> <formals> <eformal> <body> :创建指定静态环境的 vau 抽象(@4.5.2.3) 。
    52335271 创建的对象是操作子(@7.6.1.1) 。
    52345272 和 Kernel 不同,因为支持保存环境的所有权,$vau/e 被设计为比 $vau 更基本的操作。
    5235-不考虑所有权时,eval(@10.5.5) 和 $vau 可派生 $vau/e 。
    5273+不考虑所有权时,eval(@10.5.6) 和 $vau 可派生 $vau/e 。
    52365274 $vau/e% <environment> <formals> <eformal> <body> :同 $vau/e ,但保留引用值。
    52375275 wrap <combiner> :包装(@4.5.3.2) 合并子为应用子(@7.6.1.1) 。
    52385276 包装应用子可能符合包装数溢出的错误条件(@9.6.4) 。
    @@ -5240,13 +5278,13 @@
    52405278 unwrap <applicative> :解包装(@4.5.3.2) 应用子为底层的合并子(@7.6.1.1) 。
    52415279 和 Kernel 不同,参数是右值时子对象被复制,由这些合并子创建的操作子当前仍不足以取代内置的一等操作子,因为不支持只能转移而不能复制的对象。传递这些对象作为操作数会引起构造失败的异常。
    52425280
    5243-@10.5.7 错误处理和检查:
    5281+@10.5.8 错误处理和检查:
    52445282 raise-invalid-syntax-error <string> :引发包含参数指定的字符串内容的语法错误(@9.4.3.1) 。
    52455283 当前实现中引发的错误对象以抛出异常实现,其中异常对象类型具有 public 基类 NPL::InvalidSyntax 。
    52465284 check-list-reference <object> :检查对象是否是列表引用,若检查通过返回参数,否则引发错误对象(@9.4.3.1) 。
    52475285 当前实现中引发错误对象以抛出异常实现,其中异常对象类型是 NPL::ListTypeError(@6.3) 或 NPL::ValueCategoryMismatch(@6.3) 。
    52485286
    5249-@10.5.8 封装:
    5287+@10.5.9 封装:
    52505288 () make-encapsulation-type :创建封装类型。
    52515289 和 Kernel 类似,结果是三个合并子组成的列表,其元素分别表示用于构造封装类型对象的封装(encapsulate) 构造器、判断封装类型的谓词和用于解封装(decapsulate) 的访问器。
    52525290 构造器直接使用参数,类似(@10.4.1.1) 中带有 % 的容器构造器;
    @@ -5255,9 +5293,8 @@
    52555293 另见 https://small.r7rs.org/wiki/UniqueTypesSnellPym/ 和 SRFI-137 。
    52565294
    52575295 @10.6 基础派生操作(grounded derived operations) :
    5258-剩余基础环境(@10) 操作可以从基本操作组合实现,或依赖其它实现特定的互操作接口的本机实现(@5.2.1) 组合实现。
    5259-这些可组合的实现是派生操作,在基础上下文(@8.5.2) 的根环境中初始化。
    5260-@10.6.1 和 @10.6.2 的少量操作可使用派生实现(可能需要使用 @10.6.2 中的函数或 @10.6.1 中的部分非派生实现),但因为性能等原因在 NPLA1 API 中提供直接支持的本机实现(@10) 。
    5296+根环境接口(@10.1) 中,除 @10.5 及模块外的剩余接口可通过既有对象语言提供的接口实现,或依赖其它实现特定的互操作接口的本机实现(@5.2.1) 组合实现。
    5297+@10.6.1 的操作可使用派生实现(可能需要使用 @10.5 或 @10.6.1 中的部分非派生实现),但因为性能等原因在 NPLA1 API 中提供直接支持的本机实现(@10) 。
    52615298 这些操作的派生实现同时在 NPL::Dependency 模块内被给出作为替代实现。
    52625299 启用本机实现使用以下实现选项(@7.1) 控制:
    52635300 NPL_Impl_NPLA1_Native_Forms
    @@ -5271,16 +5308,16 @@
    52715308 注意和 [RnRK] 合并子的派生完全省略错误处理而允许不同的诊断不同,NPLA1 中的操作的替代实现的没有被以上例外或操作自身的语义指定的其它行为(如诊断(@9.4.3) )仍应和本机实现保持一致。
    52725309
    52735310 @10.6.1 基本派生操作:
    5274-引入合并子的操作子对 <body> 的约定同 @10.5.6 。
    5311+引入合并子的操作子对 <body> 的约定同 @10.5.7 。
    52755312 因为互相依赖,一些操作实现为派生操作时,不能用于直接派生特定一些其它操作。
    5276-和 $vau/e 或 $vau/e%(@10.5.6) 不同,不指定静态环境的合并子构造器隐含总是使用环境弱引用(@5.4.3) 形式的静态环境,以避免过于容易引入循环引用(@9.6.1.1) 。另见 @5.4.3 。
    5313+和 $vau/e 或 $vau/e%(@10.5.7) 不同,不指定静态环境的合并子构造器隐含总是使用环境弱引用(@5.4.3) 形式的静态环境,以避免过于容易引入循环引用(@9.6.1.1) 。另见 @5.4.3 。
    52775314 () get-current-environment :取当前环境(@5.4.4) :取当前环境的环境弱引用。
    52785315 结果具有宿主值类型 NPL::EnvironmentReference 。派生需要非派生实现的 vau/e 。
    52795316 () lock-current-environment :锁定当前环境:取当前环境的环境强引用。
    52805317 结果具有宿主值类型 shared_ptr<Environment> 。
    52815318 $vau <formals> <eformal> <body> :创建 vau 抽象(@4.5.2.3) 。
    5282-类似 $vau/e(@10.5.6) ,但以当前环境代替额外的求值环境作为静态环境。
    5283-和 Kernel 不同,可通过 $vau/e(@10.5.6) 和(非派生的)get-current-environment 派生,不是基本操作(@10.5) 。
    5319+类似 $vau/e(@10.5.7) ,但以当前环境代替额外的求值环境作为静态环境。
    5320+和 Kernel 不同,可通过 $vau/e(@10.5.7) 和(非派生的)get-current-environment 派生,不是基本操作(@10.5) 。
    52845321 $vau% <formals> <eformal> <body> :同 $vau(@10.6.1) ,但保留引用值。
    52855322 $quote <expression> 求值引用操作。结果为返回值转换(@5.7.4.4) 后的未被求值的操作数。
    52865323 考虑通常引用操作对符号类型未被求值的左值操作数使用,保留引用值没有意义,因此不提供对应保留引用值的操作。
    @@ -5291,12 +5328,12 @@
    52915328 list <object>... :创建列表(类型为 <list> )对象。
    52925329 list% <object>... :同 list ,但每个参数都不隐含左值到右值转换,在结果保留引用值。
    52935330 $deflazy! <definiend> <expressions> :修改绑定。
    5294-同 $def! ,但不求值参数;在添加绑定前仍对冻结环境进行检查(@10.5.5) 。
    5331+同 $def! ,但不求值参数;在添加绑定前仍对冻结环境进行检查(@10.5.6) 。
    52955332 $set! <environment> <formals> <expressions> :修改指定环境的变量绑定。
    5296-在当前环境求值 <environment> 和 <expressions> ,再以后者的求值结果修改前者的求值结果指定的环境中的绑定。绑定效果同使用 $def!(@10.5.5) 。
    5333+在当前环境求值 <environment> 和 <expressions> ,再以后者的求值结果修改前者的求值结果指定的环境中的绑定。绑定效果同使用 $def!(@10.5.6) 。
    52975334 类似 Kernel 的 $set! 。注意 <expressions> 的形式不同。允许的递归操作参见 $def! 。
    52985335 和 Kernel 不同而和 NPLA1 的 $def! 等类似,在修改绑定前对冻结环境进行检查。
    5299-$setrec! <environment> <formals> <expressions> :修改指定环境的绑定,绑定效果同使用 $defrec!(@10.5.5) 。
    5336+$setrec! <environment> <formals> <expressions> :修改指定环境的绑定,绑定效果同使用 $defrec!(@10.5.6) 。
    53005337 同 $set! ,但允许不同的递归操作,参见 $defrec! 。
    53015338 $lambda <formals> <body> :创建 λ 抽象(@4.5.2.2) 。
    53025339 和创建 vau 抽象类似,但创建的是调用时对操作数的元素求值一次的应用子(@7.6.1.1) ,且忽略动态环境。
    @@ -5317,11 +5354,11 @@
    53175354 forward! <object> :同 forward ,但除转移右值操作数外,也转移(而不是复制)可修改的临时对象操作数。
    53185355 其中,需转移时,使用使用项的转移(@5.5.3) 。这和对象的转移(@5.5.2.3) 不同,不保证调用宿主实现的转移构造函数。
    53195356 本机实现使用 NPL::MoveRValueToForward(@6.6.3) 可简化操作。
    5320-这个函数类似宿主语言以对象类型参数和推断的函数参数类型作为模板参数调用 std::forward ,但若需转移,直接转移而非如 expire(@10.5.1) 返回指定结果是消亡值(@5.5.1) 的唯一引用(@5.4.2.2) 。
    5357+这个函数类似宿主语言以对象类型参数和推断的函数参数类型作为模板参数调用 std::forward ,但若需转移,直接转移而非如 expire(@10.5.2) 返回指定结果是消亡值(@5.5.1) 的唯一引用(@5.4.2.2) 。
    53215358 和宿主语言不同,直接转移允许区分消亡值和纯右值,同等地作为一等对象(如作为列表的元素)。
    53225359 apply <applicative> <object> <environment> :在指定环境中应用。
    53235360 和 Kernel 的参考派生不同,检查 <environment> 确保为一个对象。
    5324-apply <applicative> <object> :在新建的空环境(@4.6.1.1) 中应用。
    5361+apply <applicative> <object> :在新环境(@9.6.2) 中应用。
    53255362 以上 apply 的函数值保留引用值。
    53265363 list* <object>+ :在列表前附加元素创建列表。
    53275364 类似 cons ,但支持一个和多个操作数。
    @@ -5332,7 +5369,7 @@
    53325369 对参数列表 (&list-appv &appv &l) ,结果定义为和以下求值表达式等价:
    53335370 ($lambda% ((&x .)) (forward! appv) ($move-resolved! x)) ((forward! list-appv) l) ;
    53345371 其中,调用 (forward! list-appv) 和 (forward! appv)(除参数求值外)的动态环境同调用 forward-list-first% 的动态环境。
    5335-assign%! <reference> <object> :同 assign@!(@10.5.3) ,但 <object> 是引用时被折叠。
    5372+assign%! <reference> <object> :同 assign@!(@10.5.4) ,但 <object> 是引用时被折叠。
    53365373 assign! <reference> <object> :同 assign%! ,但 <object> 隐含左值到右值转换。
    53375374 set-first! <list> <object> :修改列表的第一个元素。
    53385375 和 Kernel 的 set-car! 类似,但可派生,检查列表是左值,且不保留引用值。
    @@ -5343,12 +5380,12 @@
    53435380 类似传统 Lisp 及 Kernel 的 car 。命名和 SRFI-1 及 Clojure 等现代变体一致。
    53445381 当 <list> 是左值时结果是折叠的引用值,否则结果是返回值转换(@5.7.4.4) 后的值。
    53455382 first@ <list> :同 first ,但结果总是未折叠的引用值。
    5346-first& <list> :同 first ,但结果总是折叠的引用值,且首先调用 check-list-reference(@10.5.7) 检查参数是列表引用,对右值的抛出异常。
    5383+first& <list> :同 first ,但结果总是折叠的引用值,且首先调用 check-list-reference(@10.5.8) 检查参数是列表引用,对右值的抛出异常。
    53475384 firstv <list> :同 first ,但结果总是返回值转换后的值。
    53485385 check-environment <environment> :检查环境。
    5349-检查环境通过的条件同创建合并子时的检查(@10.5.6) 。
    5386+检查环境通过的条件同创建合并子时的检查(@10.5.7) 。
    53505387 若检查通过,返回转发的参数,否则引发错误(@9.4.3.1)。
    5351-引发的错误对象和创建合并子时环境检查失败引发的错误对象,或其依赖(@9.4.3.1) 的错误对象(后者保证不是语法错误(@7.8.1) )。
    5388+引发的错误对象和创建合并子时环境检查失败引发的错误对象,或其依赖(@9.4.3.1) 的错误对象(后者保证不是语法错误(@7.8.2) )。
    53525389 $cond <clauses> :条件选择。
    53535390 类似 Kernel 的同名操作,但 <test> 的判断条件和 <body> 形式不同。
    53545391 $when <test> <expression-sequence> :条件成立时顺序求值。
    @@ -5381,7 +5418,10 @@
    53815418 任意两个调用之间的相对求值顺序未指定。
    53825419 类似 Kernel 的 map ,但只接受一个真列表(@9.6.3) 。
    53835420
    5384-@10.6.2 核心库(core library) 函数:
    5421+@10.6.2 核心库(core library) :
    5422+本节约定以下求值得到的操作数:
    5423+<box> :箱(@4.2.3.5.3) 。
    5424+核心库提供以下操作,即核心库函数:
    53855425 $defl! <variable> <formals> <body> :绑定 λ 抽象,等价 $def! <variable> $lambda <formals> <body> 。
    53865426 $defl%! <variable> <formals> <body> :绑定 λ 抽象,等价 $def! <variable> $lambda% <formals> <body> 。
    53875427 $defv! <variable> <formals> <eformal> <body> :绑定 vau 抽象,等价 $def! <variable> $vau <formals> <eformal> <body> 。
    @@ -5409,7 +5449,7 @@
    54095449 类型分区(@9.4.7) 使 box? 对 <list> 类型的参数的结果总是 #f 。若没有这个限制,用以下 <list> 的相关操作可整体替换进行功能等价的代替:
    54105450 用 list 、list% 和 first 可代替 box 、box% 和 unbox 。
    54115451 和 http://community.schemewiki.org/?scheme-faq-language 关于装箱的描述不同,这样的代替不一定保证有更好的性能。
    5412-以上这些函数可使用 make-encapsulation-type(@10.5.8) 实现。
    5452+以上这些函数可使用 make-encapsulation-type(@10.5.9) 实现。
    54135453 和 Scheme 等不同,箱具有被装箱对象的所有权,因此使用 box% 和 unbox 时,需注意保存被构造的箱或被箱中引用值引用的对象。
    54145454 first-null? <list> :复合 first 和 null? 操作。可用于实现 nonfoldable? 的检查中的单个项。
    54155455 list-rest% <list> :复合 list% 和 rest% 操作。可用于解析 <bindings> ,去除其中每个元素的符号项构成新的列表。
    @@ -5419,7 +5459,7 @@
    54195459 顺序查找列表中的元素,若列表的元素的第一个值以 eqv? 判断和参数指定的对象相等,则结果为这个元素转发的值;
    54205460 否则,值为空列表。
    54215461 类似 Kernel 的 assoc ,但使用 eqv? 而不是 equal? ,且转发参数。
    5422-和 Scheme 的 assv 类似,但失败的值不返回 #f ,尽管和 Kernel 相同而和 Scheme 不同,<test> 支持非布尔类型的值(@10.5.2) 。
    5462+和 Scheme 的 assv 类似,但失败的值不返回 #f ,尽管和 Kernel 相同而和 Scheme 不同,<test> 支持非布尔类型的值(@10.5.3) 。
    54235463 $let <bindings> <body> :局部绑定求值。
    54245464 类似 Kernel 的同名操作,但返回非引用值(@9.6.1.4) 。
    54255465 $let% <bindings> <body> :同 $let ,但保留引用值。
    @@ -5501,6 +5541,8 @@
    55015541
    55025542 @11.2 代理求值:
    55035543 通过初始化基础上下文后调用 Forms::LoadModule_std_promises(@8.5.2) 初始化,默认加载为根环境下的 std.promises 环境。
    5544+本节约定以下求值得到的操作数:
    5545+<promise> :求值代理:表示可被求值的封装结果。
    55045546 除 $laze/e 外,同 [RnRK] 的 promises 模块,但当前不支持 PTC(@9.4.8) 。
    55055547 promise? <object> :判断是否为 <promise> 类型的对象。
    55065548 memoize <expressions> :记忆化求值。
    @@ -5510,6 +5552,9 @@
    55105552
    55115553 @11.3 字符串:
    55125554 通过初始化基础上下文后调用 Forms::LoadModule_std_strings(@8.5.2) 初始化,默认加载为根环境下的 std.strings 环境。
    5555+本节约定以下求值得到的操作数:
    5556+<regex> :正则表达式。
    5557+正则表达式以 std::regex 类型表示,实现保证可通过 string 初始化。
    55135558 ++ <string>... :字符串串接。
    55145559 string-empty? <string> :判断字符串是否为空。
    55155560 string<- <string1> <string2> :字符串赋值(@9.4.5.1.1) 。
    @@ -5522,7 +5567,9 @@
    55225567
    55235568 @11.4 输入/输出:
    55245569 通过初始化基础上下文后调用 Forms::LoadModule_std_io(@8.5.2) 初始化,默认加载为根环境下的 std.io 环境。
    5525-puts <string> :调用 std::puts 输出字符串。
    5570+puts <string> :输出字符串(视为 NTCTS(@5.11.1) )和换行并刷新缓冲。
    5571+实现使用 REPLContext::GetOutputStreamRef(@7.8.1) 。
    5572+在使用前,一般应初始化 REPLContext::OutputStreamPtr(@7.8.1) 指向特定的 std::ostream 对象;否则,总是失败引发错误。
    55265573 load <string> :加载参数指定的翻译单元。
    55275574 当前实现中的参数为文件系统路径。
    55285575 被加载的翻译单元视为对象的外部表示(@4.1.1) ,经读取翻译为 NPLA1 对象。
    diff -r db41e0c9c6e8 -r 3da33dd96b6d doc/Workflow.txt
    --- a/doc/Workflow.txt Tue Oct 13 02:01:49 2020 +0800
    +++ b/doc/Workflow.txt Sun Oct 25 11:06:55 2020 +0800
    @@ -11,13 +11,13 @@
    1111 /*! \file Workflow.txt
    1212 \ingroup Documentation
    1313 \brief 工作流汇总报告。
    14-\version r3895
    14+\version r3935
    1515 \author FrankHB <frankhb1989@gmail.com>
    1616 \since build 433
    1717 \par 创建时间:
    1818 2013-07-31 01:27:41 +0800
    1919 \par 修改时间:
    20- 2020-10-12 16:36 +0800
    20+ 2020-10-24 07:33 +0800
    2121 \par 文本编码:
    2222 UTF-8
    2323 \par 模块名称:
    @@ -3860,6 +3860,8 @@
    38603860 The pragma GCC optimize("no-var-tracking") easily causes internal compiler errors even used for a whole translation unit.
    38613861
    38623862 $2020-10:
    3863+
    3864+report.impl:
    38633865 3rdparty libraries are massively rebuilt.
    38643866 FreeImage for platform Android is an exception, as it is not actively used.
    38653867 FreeImage now supports different configurations in its makefile, to support different configurations of YFramework builds.
    @@ -3907,6 +3909,45 @@
    39073909 MinGW32 dynamic release build miscompiles someting/fails to link 'YFramework.dll' and the final binary crashes on opening.
    39083910 There is a SIGSEGV in 0x646469d0 in ZN5YSLib2UI7ListBoxC2ERKNS_7Drawing4RectESt10unique_ptrINS0_8TextListESt14default_deleteIS7_EE.constprop.1.
    39093911 This is also worked around by disabling LTO in all compiler options in this configuration.
    3912+NPL I/O library now has different basic abstract backend model.
    3913+ C++ streams are now used to enable the support of Scheme/Kernel style ports.
    3914+ This is basically by using 'std::ios_base' as the polymorphic value type in the object model.
    3915+ The actual value in such an object is usually stored in the polymorphic value holder.
    3916+ The backend for output now directly uses a 'std::ostream' pointer as 'REPLContext' data member.
    3917+ Instead of 'std::ios_base', this avoids the unnecessary dynamic cast. (It can't be 'static_cast' due to the virtual base.)
    3918+ The applicative 'puts' in module 'std.io' is changed to adapt to it, without dependency to <cstdio> any longer.
    3919+ This may have overhead, but usually negligible.
    3920+ Note that as usual (documented by 'doc/YSLib.txt'), the standard header <iostream> should be avoid in the library, esp. for some resource-constrained platforms.
    3921+ Basically this requires an additional initialization in the client code.
    3922+ The additional requirement can be also useful to make sure that the <iostream> interface separates from the one in <cstdio>, even with 'std::ios_base::sync_with_stdio' calls.
    3923+The use of encoding conversion in Windows is revised.
    3924+ Bug of the confusion between the ANSI code page ('CP_ACP') and the console code pages (configurable by 'chcp' in the command line or the Wni32 API '::SetConsoleCP'/'::SetConsoleOutputCP') is fixed.
    3925+ There are some caveats on changing the console code pages.
    3926+ The command 'chcp' set both input and output pages of a console when called with an argument. It displays only the input console without an argument.
    3927+ The '::SetConsoleCP' and '::SetConsoleOutputCP' can have the effect after the program exit.
    3928+ The traditional Windows console (backed by 'conhost.exe' since Windows Vista) still cannot handle the non-DBCS MBCS well.
    3929+ Examples of non-DBCS MBCS encoding include UTF-8 and GB-18030. They are not compatible to 'CP_ACP' used internally.
    3930+ See http://archives.miloush.net/michkap/archive/2006/10/11/816996.html.
    3931+ See also http://archives.miloush.net/michkap/archive/2007/01/03/1392379.html.
    3932+ Windows Terminal should overcome the problem.
    3933+ See https://github.com/microsoft/terminal.
    3934+ However, it requires Windows 10 1903 (build 18362) or later.
    3935+ The code pages of a console can be set separatedly, so they do not necessarily equal to the ANSI code page, hence the bug.
    3936+ The improper calls of 'platform_ex::Encode' and 'platform_ex::Decode' with 'CP_ACP' or a default argument (of 'CP_ACP') can lead the bug.
    3937+ Currently, the bug has effects on the 'SHBuild' tool in the YSLib project.
    3938+ This is effected by calls of 'EncodeArg' and 'DecodeArg' since build 561 which had replaced calls of 'MBCSToMBCS' since build 476.
    3939+ Client code should also be careful.
    3940+ In general, client programs should better leave the code pages untouched (without calls to '::SetConsoleCP' and '::SetConsoleOutputCP') unless some encodings are explicitly depended on by design.
    3941+ Command line vector should be use UTF-16 encoding instead of the deprecated ANSI code page.
    3942+ For the reasons above, handling of the command line for Win32 platforms is reimplemented in YFramework.
    3943+ This is now implemented by getting the platform-specific command line and parsing it according to the initialization of UCRT implementation, exposed by 'platform_ex::ParseCommandArguments' in YFramework module 'YCLib_(Win32).MinGW32'.
    3944+ There can be some subtle behavior differences between the UCRT implementation and the old implementation using '::CommandLineToArgvW' in Shlwapi.
    3945+ See https://github.com/rust-lang/rust/issues/44650.
    3946+ The new behavior is mandated here, despite the CRT implementation being linked to the problem.
    3947+ The globbing is disabled by default as the MinGW-W64 runtime.
    3948+ Since the C++ implementation can be referenced, this is not strictly necessary, in contrast to freestanding environments without aid of CRT (like Rust).
    3949+ However, the reimplementation provides better compatibility by avoiding referencing the internal interface of the CRT implementations.
    3950+ Note it can also serve to 'YSLib::ArgumentsVector' in module YSLib::Core::YCoreUtility by direct accesses to its member.
    39103951
    39113952 ////
    39123953
    Show on old repository browser