# include <compare>
#endif
+#ifdef __glibcxx_format_path // C++ >= 26 && HOSTED
+# include <bits/formatfwd.h>
+#endif
+
#if defined(_WIN32) && !defined(__CYGWIN__)
# define _GLIBCXX_FILESYSTEM_IS_WINDOWS 1
#endif
{ return filesystem::hash_value(__p); }
};
+#ifdef __glibcxx_format_path // C++ >= 26 && HOSTED
+ template<__format::__char _CharT>
+ struct formatter<filesystem::path, _CharT>
+ {
+ formatter() = default;
+
+ constexpr typename basic_format_parse_context<_CharT>::iterator
+ parse(basic_format_parse_context<_CharT>& __pc)
+ {
+ auto __first = __pc.begin();
+ const auto __last = __pc.end();
+ __format::_Spec<_CharT> __spec{};
+
+ auto __finalize = [this, &__spec] {
+ _M_spec = __spec;
+ };
+
+ auto __finished = [&] {
+ if (__first == __last || *__first == '}')
+ {
+ __finalize();
+ return true;
+ }
+ return false;
+ };
+
+ if (__finished())
+ return __first;
+
+ __first = __spec._M_parse_fill_and_align(__first, __last);
+ if (__finished())
+ return __first;
+
+ __first = __spec._M_parse_width(__first, __last, __pc);
+ if (__finished())
+ return __first;
+
+ if (*__first == '?')
+ {
+ __spec._M_debug = true;
+ ++__first;
+ }
+ if (__finished())
+ return __first;
+
+ if (*__first == 'g')
+ {
+ __spec._M_type = __format::_Pres_g;
+ ++__first;
+ }
+ if (__finished())
+ return __first;
+
+ __format::__failed_to_parse_format_spec();
+ }
+
+ template<typename _Out>
+ typename basic_format_context<_Out, _CharT>::iterator
+ format(const filesystem::path& __p,
+ basic_format_context<_Out, _CharT>& __fc) const
+ {
+ using _ValueT = filesystem::path::value_type;
+ using _ViewT = basic_string_view<_ValueT>;
+ using _FmtStrT = __format::__formatter_str<_CharT>;
+
+ _ViewT __sv;
+ filesystem::path::string_type __s;
+ if (_M_spec._M_type == __format::_Pres_g)
+ __sv = __s = __p.generic_string<_ValueT>();
+ else
+ __sv = __p.native();
+
+ auto __spec = _M_spec;
+ // 'g' should not be passed along.
+ __spec._M_type = __format::_Pres_none;
+
+ if constexpr (is_same_v<_CharT, _ValueT>)
+ return _FmtStrT(__spec).format(__sv, __fc);
+ else
+ {
+ __format::_Str_sink<_ValueT> __sink;
+ if (__spec._M_debug)
+ {
+ using __format::_Term_quote;
+ __format::__write_escaped(__sink.out(), __sv, _Term_quote);
+ __sv = __sink.view();
+ __spec._M_debug = 0;
+ }
+ basic_string<_CharT> __out_str
+ (std::from_range, __unicode::_Utf_view<_CharT, _ViewT>(__sv));
+ return _FmtStrT(__spec).format(__out_str, __fc);
+ }
+ }
+
+ constexpr void
+ set_debug_format() noexcept
+ { _M_spec._M_debug = true; }
+
+ private:
+ __format::_Spec<_CharT> _M_spec{};
+ };
+#endif // __glibcxx_format_path
+
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
--- /dev/null
+// { dg-do run { target c++26 } }
+// { dg-options "-fexec-charset=UTF-8" }
+
+#include <filesystem>
+#include <format>
+#include <testsuite_hooks.h>
+
+using std::filesystem::path;
+
+template<typename... Args>
+bool
+is_format_string_for(const char* str, Args&&... args)
+{
+ try {
+ (void) std::vformat(str, std::make_format_args(args...));
+ return true;
+ } catch (const std::format_error&) {
+ return false;
+ }
+}
+
+template<typename... Args>
+bool
+is_format_string_for(const wchar_t* str, Args&&... args)
+{
+ try {
+ (void) std::vformat(str, std::make_wformat_args(args...));
+ return true;
+ } catch (const std::format_error&) {
+ return false;
+ }
+}
+
+void
+test_format_spec()
+{
+ // [fs.path.fmtr.funcs]
+ // \nontermdef{path-format-spec}\br
+ // \opt{fill-and-align} \opt{width} \opt{\terminal{?}} \opt{\terminal{g}}
+ path p;
+ VERIFY( is_format_string_for("{}", p) );
+ VERIFY( is_format_string_for("{:}", p) );
+ VERIFY( is_format_string_for("{:?}", p) );
+ VERIFY( is_format_string_for("{:g}", p) );
+ VERIFY( is_format_string_for("{:?g}", p) );
+ VERIFY( is_format_string_for("{:?g}", p) );
+ VERIFY( is_format_string_for("{:F^32?g}", p) );
+ VERIFY( is_format_string_for("{:G<{}?g}", p, 32) );
+ VERIFY( is_format_string_for(L"{:G<{}?g}", p, 32) );
+
+ VERIFY( ! is_format_string_for("{:g?}", p) );
+}
+
+#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S)
+#define WFMT(S) WIDEN_(_FCharT, S)
+#define WPATH(S) WIDEN_(_PCharT, S)
+
+template<typename _FCharT, typename _PCharT>
+void
+test_format()
+{
+ std::basic_string<_FCharT> res;
+ res = std::format(WFMT("{}"), path(WPATH("/usr/include")));
+ VERIFY( res == WFMT("/usr/include") );
+ res = std::format(WFMT("{:.<10}"), path(WPATH("foo/bar")));
+ VERIFY( res == WFMT("foo/bar...") );
+ res = std::format(WFMT("{}"), path(WPATH("foo///bar")));
+ VERIFY( res == WFMT("foo///bar") );
+ res = std::format(WFMT("{:g}"), path(WPATH("foo///bar")));
+ VERIFY( res == WFMT("foo/bar") );
+ res = std::format(WFMT("{}"), path(WPATH("/path/with/new\nline")));
+ VERIFY( res == WFMT("/path/with/new\nline") );
+ res = std::format(WFMT("{:?}"), path(WPATH("multi\nline")));
+ VERIFY( res == WFMT("\"multi\\nline\"") );
+ res = std::format(WFMT("{:?g}"), path(WPATH("mu///lti\nli///ne")));
+ VERIFY( res == WFMT("\"mu/lti\\nli/ne\"") );
+ res = std::format(WFMT("{}"),
+ path(WPATH("\u0428\u0447\u0443\u0447\u044B\u043D\u0448\u0447\u044B\u043D\u0430")));
+ VERIFY( res == WFMT("\u0428\u0447\u0443\u0447\u044B\u043D\u0448\u0447\u044B\u043D\u0430"));
+
+ if constexpr (path::preferred_separator == L'\\')
+ {
+ res = std::format(WFMT("{}"), path(WPATH("C:\\foo\\bar")));
+ VERIFY( res == WFMT("C:\\foo\\bar") );
+ res = std::format(WFMT("{:g}"), path(WPATH("C:\\foo\\bar")));
+ VERIFY( res == WFMT("C:/foo/bar") );
+ }
+}
+
+void
+test_format_invalid()
+{
+ if constexpr (std::is_same_v<path::value_type, char>)
+ {
+ std::wstring res;
+ std::string_view seq = "\xf0\x9f\xa6\x84"; // \U0001F984
+
+ path p(seq.substr(1));
+ res = std::format(L"{}", p);
+ VERIFY( res == L"\uFFFD\uFFFD\uFFFD" );
+ res = std::format(L"{:?}", p);
+ VERIFY( res == LR"("\x{9f}\x{a6}\x{84}")" );
+ }
+ else
+ {
+ std::string res;
+
+ path p(L"\xd800");
+ res = std::format("{}", p);
+ VERIFY( res == "\uFFFD" );
+ res = std::format("{:?}", p);
+ VERIFY( res == "\"\\x{d800}\"" );
+
+ path p2(L"///\xd800");
+ res = std::format("{}", p2);
+ VERIFY( res == "///\uFFFD" );
+ res = std::format("{:g}", p2);
+ VERIFY( res == "/\uFFFD" );
+ res = std::format("{:?}", p2);
+ VERIFY( res == "\"///\\x{d800}\"" );
+ res = std::format("{:?g}", p2);
+ VERIFY( res == "\"/\\x{d800}\"" );
+ res = std::format("{:C>14?g}", p2);
+ VERIFY( res == "CCC\"/\\x{d800}\"" );
+ }
+}
+
+int main()
+{
+ test_format_spec();
+ test_format<char, char>();
+ test_format<char, wchar_t>();
+ test_format<wchar_t, char>();
+ test_format<wchar_t, wchar_t>();
+ test_format_invalid();
+}