What happens
On Windows rtk builds every child's command line with one encoder, the MSVCRT/libuv convention that native programs (git.exe, rg.exe, node, dotnet) parse with. MSYS2/Cygwin programs (Git for Windows' grep, find, ls, wc, sed, …) parse the command line with Cygwin's own rules (build_argv / quoted / globify in msys2-runtime winsup/cygwin/dcrt0.cc). The two disagree in one case: inside a quoted argument, Cygwin collapses every \\ to \, while MSVCRT keeps the backslashes that do not precede a quote.
So an argument that has to be quoted (it holds a space or a character MSYS would reinterpret) and contains a run of two or more backslashes not followed by a quote reaches an MSYS child with one backslash of each pair missing. No single encoding is exact for both kinds of child:
- MSYS needs the run doubled.
- MSVCRT needs it as written.
- Moving the run outside the quotes depends on Cygwin's drive-letter heuristic, and fails for one parser or the other.
Evidence
Checked with a Linux harness built from msys2-runtime's real build_argv/quoted/globify and glob.cc (commit c770e1b9), next to an MSVCRT-rule parser. The harness was fed the exact command lines rtk's encoder produces:
| argument |
sent |
MSYS child receives |
native child receives |
\\( |
"\\(" |
\( |
\\( |
C:\\Temp (x86) |
"C:\\Temp (x86)" |
C:\Temp (x86) |
C:\\Temp (x86) |
a\\b( |
"a\\b(" |
a\b( |
a\\b( |
\\srv\share\(x) |
"\\srv\share\(x)" |
\srv\share\(x) |
\\srv\share\(x) |
C:\a\b "x" |
"C:\a\b \"x\"" |
C:\a\b "x" |
C:\a\b "x" |
Single backslashes (ordinary Windows paths) are exact for both. Only runs of two or more are affected: a UNC path or a regex \\ in an argument that is quoted anyway. For example, rtk grep '\\(' f from PowerShell searches for \( in MSYS grep.
Switching everything to the Cygwin encoding would break native children instead: rtk git commit -m "fix: a\\b" would reach git.exe as a\\\b. That is worse, since git is the most common child.
Proposed
Choose the encoder per child. An MSYS/Cygwin program can be recognised from its executable: its PE import table names msys-2.0.dll or cygwin1.dll.
- MSYS children get the Cygwin-exact encoding.
- Native children keep today's encoding, which is exact for them.
- The choice stays a pure function tested on every platform. Only the PE import scan is Windows-only I/O.
- Cost: reading the import table of the resolved executable, about 1 ms per spawn uncached, less with a per-path cache.
Related
What happens
On Windows rtk builds every child's command line with one encoder, the MSVCRT/libuv convention that native programs (
git.exe,rg.exe,node,dotnet) parse with. MSYS2/Cygwin programs (Git for Windows'grep,find,ls,wc,sed, …) parse the command line with Cygwin's own rules (build_argv/quoted/globifyin msys2-runtimewinsup/cygwin/dcrt0.cc). The two disagree in one case: inside a quoted argument, Cygwin collapses every\\to\, while MSVCRT keeps the backslashes that do not precede a quote.So an argument that has to be quoted (it holds a space or a character MSYS would reinterpret) and contains a run of two or more backslashes not followed by a quote reaches an MSYS child with one backslash of each pair missing. No single encoding is exact for both kinds of child:
Evidence
Checked with a Linux harness built from msys2-runtime's real
build_argv/quoted/globifyandglob.cc(commitc770e1b9), next to an MSVCRT-rule parser. The harness was fed the exact command lines rtk's encoder produces:\\("\\("\(\\(C:\\Temp (x86)"C:\\Temp (x86)"C:\Temp (x86)C:\\Temp (x86)a\\b("a\\b("a\b(a\\b(\\srv\share\(x)"\\srv\share\(x)"\srv\share\(x)\\srv\share\(x)C:\a\b "x""C:\a\b \"x\""C:\a\b "x"C:\a\b "x"Single backslashes (ordinary Windows paths) are exact for both. Only runs of two or more are affected: a UNC path or a regex
\\in an argument that is quoted anyway. For example,rtk grep '\\(' ffrom PowerShell searches for\(in MSYS grep.Switching everything to the Cygwin encoding would break native children instead:
rtk git commit -m "fix: a\\b"would reachgit.exeasa\\\b. That is worse, since git is the most common child.Proposed
Choose the encoder per child. An MSYS/Cygwin program can be recognised from its executable: its PE import table names
msys-2.0.dllorcygwin1.dll.Related
ChildCommandencoder refactor. It keeps today's encoding and documents this limit.