* Support CMake 4
CMake 4 has removed compatibility with CMake < 3.5
Bumping minimum required version to 3.5 enables
CMake 4 to build this code.
* Move header into subdir
* Improve CMake setup
This commit configures and installs CMake metadata files. This also
provides the namespaced ALIAS target `cpp-subprocess::subprocess`.
* Update README with CMake instructions
* Update include paths in tests
* refactor: Remove unused `util::is_ready()` function
Commit ffd4c731a2c7c09f34e7b81a16e04bdf91fa973d introduced the
`util::is_ready()` function, which has never been used internally and is
not part of the public API.
This change removes the function.
* refactor: Guard `util::quote_argument()` with `#ifdef __USING_WINDOWS__`
The `util::quote_argument()` function is specific to Windows and is used
in code already guarded by `#ifdef __USING_WINDOWS__`.
* Do not escape double quotes for command line arguments on Windows
This change fixes the handling of double quotes and aligns the behavior
with Python's `Popen` class. For example:
```
>py -3
>>> import subprocess
>>> p = subprocess.Popen("cmd.exe /c dir \"C:\\Program Files\"", stdout=subprocess.PIPE, text=True)
>>> print(f"Captured stdout:\n{stdout}")
```
Currently, the same command line processed by the `quote_argument()`
function looks like `cmd.exe /c dir "\"C:\Program" "Files\""`, which is
broken.
With this change, it looks correct: `cmd.exe /c dir "C:\Program Files"`.
* Add `test_double_quotes` test
The new test ensures that no regressions are introduced in the future.
* Fix exception when `CreateProcessW` fails
This change makes the behavior on Windows consistent with the behavior
on Linux.
* test: Add `test_exception`
It's useful to be able to know the exit code of the process when it
fails with a non-zero exit code.
To get this to work, I had to fix a bug in the implementation of
check_output. Previously, check_output would call both `p.communicate()`
and `p.poll()`. The former has the effect of waiting for EOF on the
input and then waiting for the child to exit, reaping it with
`waitpid(2)`. Unfortunately, `p.poll()` was hoping to be able to also
use `waitpid(2)` to retrieve the exit code of the process. But since the
child had already been reaped, the given pid no longer existed, and thus
`waitpid(2)` would return `ECHILD`.
Luckily the call to `p.poll()` is unnecessary, as the process already
provides `p.retcode()` for retrieving the exit code.
1. When more than 2 streams are piped, we use threads to read/write data
parallely. In this scenariod read_atmost_n API was being used instead of
read_all.
2. Another issue with check_output argument validation fixed.