@@ -11,6 +11,59 @@ inline constexpr default_args_t default_args{};
1111
1212namespace details
1313{
14+ template <::std::integral replace_char_type, typename Iter>
15+ inline constexpr void append_win32_quoted_arg_common (
16+ bool is_first,
17+ ::fast_io::containers::basic_string<replace_char_type, ::fast_io::native_global_allocator> &str,
18+ Iter first, Iter last)
19+ {
20+ // Reserve rough upper bound: quotes + worst-case doubling
21+ str.reserve (str.size () + 3 + static_cast <::std::size_t >(last - first) * 2u );
22+ str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \" ' , replace_char_type>);
23+
24+ ::std::size_t backslash_count{};
25+ for (; first != last; ++first)
26+ {
27+ auto const c{*first};
28+ if (c == ::fast_io::char_literal_v<u8 ' \" ' , replace_char_type>)
29+ {
30+ if (is_first) [[unlikely]]
31+ {
32+ // Windows argv[0] does not allow double quotes even if escaped
33+ throw_win32_error (13 );
34+ }
35+ // Output 2*n+1 backslashes before a quote
36+ for (::std::size_t i{}; i != ((backslash_count << 1u ) + 1u ); ++i)
37+ {
38+ str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \\ ' , replace_char_type>);
39+ }
40+ str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \" ' , replace_char_type>);
41+ backslash_count = 0 ;
42+ }
43+ else if (c == ::fast_io::char_literal_v<u8 ' \\ ' , replace_char_type>)
44+ {
45+ ++backslash_count;
46+ }
47+ else
48+ {
49+ // Flush pending backslashes (not before a quote): output as-is
50+ for (::std::size_t i{}; i != backslash_count; ++i)
51+ {
52+ str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \\ ' , replace_char_type>);
53+ }
54+ backslash_count = 0 ;
55+ str.push_back_unchecked (c);
56+ }
57+ }
58+ // Before closing quote, double any trailing backslashes
59+ for (::std::size_t i{}; i != (backslash_count << 1u ); ++i)
60+ {
61+ str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \\ ' , replace_char_type>);
62+ }
63+ str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \" ' , replace_char_type>);
64+ str.push_back_unchecked (::fast_io::char_literal_v<u8 ' ' , replace_char_type>);
65+ }
66+
1467template <::std::integral replace_char_type, typename T>
1568inline constexpr void construct_win32_process_args_decay_singal (bool is_first, ::fast_io::containers::basic_string<replace_char_type, ::fast_io::native_global_allocator> &str, T t)
1669{
@@ -21,61 +74,15 @@ inline constexpr void construct_win32_process_args_decay_singal(bool is_first, :
2174 replace_char_type buf[32767 ];
2275 ::fast_io::basic_obuffer_view<replace_char_type> obf{buf, buf + 32767 };
2376 ::fast_io::operations::decay::print_freestanding_decay<false >(::fast_io::operations::output_stream_ref (obf), t);
24- str.reserve (str.size () + 3 + obf.size () * 2 );
25- str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \" ' , replace_char_type>);
26- for (auto const c : obf)
27- {
28- if (c == ::fast_io::char_literal_v<u8 ' \" ' , replace_char_type>)
29- {
30- if (is_first) [[unlikely]]
31- {
32- // The first argument of windows does not support double quotes
33- throw_win32_error (13 );
34- }
35- else
36- {
37- str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \\ ' , replace_char_type>);
38- str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \" ' , replace_char_type>);
39- }
40- }
41- else
42- {
43- str.push_back_unchecked (c);
44- }
45- }
46- str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \" ' , replace_char_type>);
47- str.push_back_unchecked (::fast_io::char_literal_v<u8 ' ' , replace_char_type>);
77+ append_win32_quoted_arg_common<replace_char_type>(is_first, str, obf.cbegin (), obf.cend ());
4878 }
4979 else if constexpr (requires { ::fast_io::mnp::code_cvt (t); })
5080 {
5181 replace_char_type buf[32767 ];
5282 ::fast_io::basic_obuffer_view<replace_char_type> obf{buf, buf + 32767 };
5383 // need decay
5484 ::fast_io::operations::print_freestanding<false >(obf, ::fast_io::mnp::code_cvt (t));
55- str.reserve (str.size () + 3 + obf.size () * 2 );
56- str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \" ' , replace_char_type>);
57- for (auto const c : obf)
58- {
59- if (c == ::fast_io::char_literal_v<u8 ' \" ' , replace_char_type>)
60- {
61- if (is_first) [[unlikely]]
62- {
63- // The first argument of windows does not support double quotes
64- throw_win32_error (13 );
65- }
66- else
67- {
68- str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \\ ' , replace_char_type>);
69- str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \" ' , replace_char_type>);
70- }
71- }
72- else
73- {
74- str.push_back_unchecked (c);
75- }
76- }
77- str.push_back_unchecked (::fast_io::char_literal_v<u8 ' \" ' , replace_char_type>);
78- str.push_back_unchecked (::fast_io::char_literal_v<u8 ' ' , replace_char_type>);
85+ append_win32_quoted_arg_common<replace_char_type>(is_first, str, obf.cbegin (), obf.cend ());
7986 }
8087 else
8188 {
@@ -133,7 +140,7 @@ inline constexpr void construct_win32_process_envs_decay(
133140
134141} // namespace details
135142
136- template <::fast_io::win32_family family>
143+ template <::fast_io::win32_family family, bool is_first >
137144struct basic_win32_process_args FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE
138145{
139146 inline static constexpr bool is_nt{family == ::fast_io::win32_family::wide_nt};
@@ -164,9 +171,9 @@ struct basic_win32_process_args FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE
164171 requires (!::std::same_as<::std::remove_cvref_t <T>, default_args_t >)
165172 inline constexpr basic_win32_process_args (T &&t, Args &&...as)
166173 {
167- details::construct_win32_process_args_decay<true >(args,
168- ::fast_io::io_print_forward<replace_char_type>(::fast_io::io_print_alias (t)),
169- ::fast_io::io_print_forward<replace_char_type>(::fast_io::io_print_alias (as))...);
174+ details::construct_win32_process_args_decay<is_first >(args,
175+ ::fast_io::io_print_forward<replace_char_type>(::fast_io::io_print_alias (t)),
176+ ::fast_io::io_print_forward<replace_char_type>(::fast_io::io_print_alias (as))...);
170177 }
171178
172179 inline constexpr char_type_may_alias_const_ptr get () const noexcept
@@ -181,7 +188,7 @@ struct basic_win32_process_args FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE
181188 }
182189 }
183190
184- inline constexpr basic_win32_process_args& append (basic_win32_process_args const &others) noexcept
191+ inline constexpr basic_win32_process_args & append (basic_win32_process_args const &others) noexcept
185192 {
186193 args.append (others.args );
187194
@@ -242,24 +249,30 @@ struct basic_win32_process_envs FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE
242249 }
243250 }
244251
245- inline constexpr basic_win32_process_envs& append (basic_win32_process_envs const &others) noexcept
252+ inline constexpr basic_win32_process_envs & append (basic_win32_process_envs const &others) noexcept
246253 {
247254 envs.append (others.envs );
248255
249256 return *this ;
250257 }
251258};
252259
253- using win32_process_args_ntw = ::fast_io::basic_win32_process_args<::fast_io::win32_family::wide_nt>;
260+ // Provide a Windows command line with argv0 version conversion, where argv0 is specially handled.
261+
262+ using win32_process_args_ntw = ::fast_io::basic_win32_process_args<::fast_io::win32_family::wide_nt, false >;
263+ using win32_process_args_ntw_with_argv0 = ::fast_io::basic_win32_process_args<::fast_io::win32_family::wide_nt, true >;
254264using win32_process_envs_ntw = ::fast_io::basic_win32_process_envs<::fast_io::win32_family::wide_nt>;
255265
256- using win32_process_args_9xa = ::fast_io::basic_win32_process_args<::fast_io::win32_family::ansi_9x>;
266+ using win32_process_args_9xa = ::fast_io::basic_win32_process_args<::fast_io::win32_family::ansi_9x, false >;
267+ using win32_process_args_9xa_with_argv0 = ::fast_io::basic_win32_process_args<::fast_io::win32_family::ansi_9x, true >;
257268using win32_process_envs_9xa = ::fast_io::basic_win32_process_envs<::fast_io::win32_family::ansi_9x>;
258269
259- using win32_process_args = ::fast_io::basic_win32_process_args<::fast_io::win32_family::native>;
270+ using win32_process_args = ::fast_io::basic_win32_process_args<::fast_io::win32_family::native, false >;
271+ using win32_process_args_with_argv0 = ::fast_io::basic_win32_process_args<::fast_io::win32_family::native, true >;
260272using win32_process_envs = ::fast_io::basic_win32_process_envs<::fast_io::win32_family::native>;
261273
262- using nt_process_args = ::fast_io::basic_win32_process_args<::fast_io::win32_family::wide_nt>;
274+ using nt_process_args = ::fast_io::basic_win32_process_args<::fast_io::win32_family::wide_nt, false >;
275+ using nt_process_args_with_argv0 = ::fast_io::basic_win32_process_args<::fast_io::win32_family::wide_nt, true >;
263276using nt_process_envs = ::fast_io::basic_win32_process_envs<::fast_io::win32_family::wide_nt>;
264277
265278#else
@@ -273,12 +286,18 @@ struct cstr_guard FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE
273286{
274287 using Alloc = ::fast_io::native_typed_global_allocator<char_type>;
275288
276- char_type *cstr;
289+ char_type *cstr{} ;
277290
278291 inline constexpr cstr_guard () noexcept = default;
279292
280293 inline constexpr cstr_guard (cstr_guard const &others) noexcept
281294 {
295+ if (others.cstr == nullptr )
296+ {
297+ cstr = nullptr ;
298+ return *this ;
299+ }
300+
282301 ::std::size_t str_size{::fast_io::cstr_len (others.cstr )};
283302 cstr = Alloc::allocate (str_size + 1 );
284303 auto const lase_ptr{::fast_io::freestanding::non_overlapped_copy_n (others.cstr , str_size, cstr)};
@@ -294,10 +313,18 @@ struct cstr_guard FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE
294313
295314 Alloc::deallocate (cstr);
296315
316+ if (others.cstr == nullptr )
317+ {
318+ cstr = nullptr ;
319+ return *this ;
320+ }
321+
297322 ::std::size_t str_size{::fast_io::cstr_len (others.cstr )};
298323 cstr = Alloc::allocate (str_size + 1 );
299324 auto const lase_ptr{::fast_io::freestanding::non_overlapped_copy_n (others.cstr , str_size, cstr)};
300325 *lase_ptr = ::fast_io::char_literal_v<u8 ' \0 ' , char_type>;
326+
327+ return *this ;
301328 }
302329
303330 inline constexpr cstr_guard (cstr_guard &&others) noexcept
@@ -317,6 +344,8 @@ struct cstr_guard FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE
317344
318345 cstr = others.cstr ;
319346 others.cstr = nullptr ;
347+
348+ return *this ;
320349 }
321350
322351 inline constexpr ~cstr_guard ()
@@ -433,7 +462,7 @@ struct posix_process_args FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE
433462 arg_envs.emplace_back (); // nullptr
434463 }
435464
436- inline char const *const *get () const noexcept
465+ inline char const *const *get_argv () const noexcept
437466 {
438467 using char_const_p_const_p_may_alias_ptr
439468#if __has_cpp_attribute(__gnu__::__may_alias__)
@@ -444,7 +473,39 @@ struct posix_process_args FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE
444473 return reinterpret_cast <char_const_p_const_p_may_alias_ptr>(arg_envs.data ());
445474 }
446475
447- inline constexpr posix_process_args& append (posix_process_args const &others) noexcept
476+ inline char const *const *get_envs () const noexcept
477+ {
478+ using char_const_p_const_p_may_alias_ptr
479+ #if __has_cpp_attribute(__gnu__::__may_alias__)
480+ [[__gnu__::__may_alias__]]
481+ #endif
482+ = char const *const *;
483+
484+ if (arg_envs.size () < 2u )
485+ {
486+ #if defined(__APPLE__) && defined(__MACH__)
487+ extern char ***_NSGetEnviron () noexcept __asm__ (" __NSGetEnviron" );
488+
489+ return reinterpret_cast <char_const_p_const_p_may_alias_ptr>(*_NSGetEnviron ());
490+ #else
491+ #if defined(__MSDOS__) || defined(__DJGPP__)
492+ // djgpp only provides `char** _environ`. For consistency, a symbolic link is used here.
493+ extern char **environ __asm__ (" __environ" );
494+ #elif !(defined(_WIN32) || defined(__CYGWIN__))
495+ // Reference to the global `environ` variable
496+ extern " C" char **environ;
497+ #endif
498+
499+ return reinterpret_cast <char_const_p_const_p_may_alias_ptr>(environ);
500+ #endif
501+ }
502+ else
503+ {
504+ return reinterpret_cast <char_const_p_const_p_may_alias_ptr>(arg_envs.data ());
505+ }
506+ }
507+
508+ inline constexpr posix_process_args &append (posix_process_args const &others) noexcept
448509 {
449510 if (others.arg_envs .size () > 1 ) [[likely]]
450511 {
@@ -473,8 +534,8 @@ namespace freestanding
473534{
474535#if (defined(_WIN32) && !defined(__WINE__)) || defined(__CYGWIN__)
475536
476- template <::fast_io::win32_family family>
477- struct is_trivially_copyable_or_relocatable <basic_win32_process_args<family>>
537+ template <::fast_io::win32_family family, bool is_first >
538+ struct is_trivially_copyable_or_relocatable <basic_win32_process_args<family, is_first >>
478539{
479540 inline static constexpr bool value = true ;
480541};
0 commit comments