Skip to content

Commit 21fd346

Browse files
committed
nt_process_args
1 parent 5c27a0c commit 21fd346

File tree

7 files changed

+347
-96
lines changed

7 files changed

+347
-96
lines changed

include/fast_io_core_impl/buffer_view.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ struct basic_obuffer_view
181181
{
182182
return static_cast<::std::size_t>(end_ptr - begin_ptr);
183183
}
184+
185+
inline constexpr bool empty() const noexcept
186+
{
187+
return begin_ptr == curr_ptr;
188+
}
184189
};
185190

186191
template <::std::integral char_type>

include/fast_io_dsal/impl/string.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,14 @@ class basic_string FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE
631631
{
632632
this->append_impl(other.data(), other.size());
633633
}
634+
inline constexpr void append(char_type const *begin, char_type const *end) noexcept
635+
{
636+
this->append_impl(begin, static_cast<::std::size_t>(end - begin));
637+
}
638+
inline constexpr void append(char_type const *otherptr, size_type othern) noexcept
639+
{
640+
this->append_impl(otherptr, othern);
641+
}
634642

635643
inline constexpr void clear() noexcept
636644
{

include/fast_io_hosted/process/process/arg_env.h

Lines changed: 124 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,59 @@ inline constexpr default_args_t default_args{};
1111

1212
namespace 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+
1467
template <::std::integral replace_char_type, typename T>
1568
inline 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>
137144
struct 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>;
254264
using 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>;
257268
using 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>;
260272
using 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>;
263276
using 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
};

include/fast_io_hosted/process/process/native.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,26 @@
1515

1616
namespace fast_io
1717
{
18-
#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WINE__)
18+
#if (defined(_WIN32) && !defined(__WINE__)) || defined(__CYGWIN__)
1919
#if !defined(_WIN32_WINDOWS) && (!defined(_WIN32_WINNT) || _WIN32_WINNT >= 0x600)
2020
using native_wait_status = ::fast_io::nt_wait_status;
2121
using native_process_args = ::fast_io::nt_process_args;
22+
using native_process_args_with_argv0 = ::fast_io::nt_process_args_with_argv0;
2223
using native_process_envs = ::fast_io::nt_process_envs;
2324
using native_process_observer = ::fast_io::nt_process_observer;
2425
using native_process = ::fast_io::nt_process;
2526
#else
2627
using native_wait_status = ::fast_io::win32_wait_status;
2728
using native_process_args = ::fast_io::win32_process_args;
29+
using native_process_args_with_argv0 = ::fast_io::win32_process_args_with_argv0;
2830
using native_process_envs = ::fast_io::win32_process_envs;
2931
using native_process_observer = ::fast_io::win32_process_observer;
3032
using native_process = ::fast_io::win32_process;
3133
#endif
3234
#else
3335
using native_wait_status = ::fast_io::posix_wait_status;
3436
using native_process_args = ::fast_io::posix_process_args;
37+
using native_process_args_with_argv0 = ::fast_io::posix_process_args;
3538
using native_process_envs = ::fast_io::posix_process_envs;
3639
using native_process_observer = ::fast_io::posix_process_observer;
3740
using native_process = ::fast_io::posix_process;

0 commit comments

Comments
 (0)