Skip to content

Windows: choose the child argument encoder per child — MSYS/Cygwin children lose one backslash of each pair inside quoted arguments #4326

Description

@KuSh

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions