Skip to content

Commit 4a16c7c

Browse files
committed
fix: gate cmd switch escaping to MSYS
1 parent 7cec714 commit 4a16c7c

3 files changed

Lines changed: 115 additions & 71 deletions

File tree

src/index.ts

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -446,18 +446,9 @@ function generateShShim (src: string, to: string, opts: InternalOptions): string
446446
shTarget = shTarget.split('\\').join('/')
447447
const quotedPathToTarget = path.isAbsolute(shTarget) ? `"${shTarget}"` : `"$basedir/${shTarget}"`
448448
const quotedPathToTarget_win = path.isAbsolute(shTarget) ? `"${shTarget}"` : `"$basedir_win/${shTarget}"`
449-
let shTarget_win
450-
// For `.cmd`/`.bat` targets the runtime is `cmd` and args is `/C`. When
451-
// Git Bash / MSYS launches a native Win32 process, arguments that look
452-
// like POSIX paths are translated — a bare `/C` becomes `C:\`, which
453-
// drops the switch and leaves cmd.exe running interactively. Prefixing
454-
// with `//` is the MSYS escape: it survives the translation and reaches
455-
// cmd.exe as `/C`. Scoped to the cmd runtime so shebang-derived `/C`
456-
// args on other shims are passed through unchanged.
449+
let shTarget_win = ''
457450
let args = opts.args || ''
458-
if (opts.prog === 'cmd' || opts.prog === 'cmd.exe') {
459-
args = escapeMsysCmdSwitches(args)
460-
}
451+
const isCmdRuntime = opts.prog === 'cmd' || opts.prog === 'cmd.exe'
461452
const shNodePath = normalizePathEnvVar(opts.nodePath).posix
462453
if (!shProg) {
463454
shProg = quotedPathToTarget
@@ -481,14 +472,15 @@ function generateShShim (src: string, to: string, opts: InternalOptions): string
481472
// basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
482473
// basedir_win="$basedir"
483474
// exe=""
475+
// msys=""
484476
//
485477
// case `uname -a` in
486478
// *CYGWIN*|*MINGW*|*MSYS*)
487479
// if command -v cygpath > /dev/null 2>&1; then
488-
// basedir=`cygpath -w "$basedir"`
489-
// basedir_win="$basedir"
480+
// basedir_win=`cygpath -w "$basedir"`
490481
// fi
491482
// exe=".exe"
483+
// msys="true"
492484
// ;;
493485
// *WSL2*)
494486
// if command -v wslpath > /dev/null 2>&1; then
@@ -521,14 +513,15 @@ function generateShShim (src: string, to: string, opts: InternalOptions): string
521513
basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')")
522514
basedir_win="$basedir"
523515
exe=""
516+
msys=""
524517
525518
case \`uname -a\` in
526519
*CYGWIN*|*MINGW*|*MSYS*)
527520
if command -v cygpath > /dev/null 2>&1; then
528-
basedir=\`cygpath -w "$basedir"\`
529-
basedir_win="$basedir"
521+
basedir_win=\`cygpath -w "$basedir"\`
530522
fi
531523
exe=".exe"
524+
msys="true"
532525
;;
533526
*WSL2*)
534527
if command -v wslpath > /dev/null 2>&1; then
@@ -558,35 +551,50 @@ fi
558551
`
559552
}
560553

561-
if (shLongProg) {
562-
if (shProgHasExe) {
563-
sh += `\
554+
const generateExecBlock = (execArgs: string) => {
555+
if (shLongProg) {
556+
if (shProgHasExe) {
557+
return `\
564558
if [ -x ${shLongProgExe} ]; then
565-
exec ${shLongProgExe} ${args} ${shTarget_win} ${progArgs}"$@"
559+
exec ${shLongProgExe} ${execArgs} ${shTarget_win} ${progArgs}"$@"
566560
else
567-
exec ${shProgExe} ${args} ${shTarget_win} ${progArgs}"$@"
561+
exec ${shProgExe} ${execArgs} ${shTarget_win} ${progArgs}"$@"
568562
fi
569563
`
570-
} else {
571-
sh += `\
564+
} else {
565+
return `\
572566
if [ -n "$exe" ] && [ -x ${shLongProgExe} ]; then
573-
exec ${shLongProgExe} ${args} ${shTarget_win} ${progArgs}"$@"
567+
exec ${shLongProgExe} ${execArgs} ${shTarget_win} ${progArgs}"$@"
574568
elif [ -x ${shLongProg} ]; then
575-
exec ${shLongProg} ${args} ${shTarget} ${progArgs}"$@"
569+
exec ${shLongProg} ${execArgs} ${shTarget} ${progArgs}"$@"
576570
elif command -v ${shProg} >/dev/null 2>&1; then
577-
exec ${shProg} ${args} ${shTarget} ${progArgs}"$@"
571+
exec ${shProg} ${execArgs} ${shTarget} ${progArgs}"$@"
578572
elif [ -n "$exe" ] && command -v ${shProgExe} >/dev/null 2>&1; then
579-
exec ${shProgExe} ${args} ${shTarget_win} ${progArgs}"$@"
573+
exec ${shProgExe} ${execArgs} ${shTarget_win} ${progArgs}"$@"
580574
else
581-
exec ${shProg} ${args} ${shTarget} ${progArgs}"$@"
575+
exec ${shProg} ${execArgs} ${shTarget} ${progArgs}"$@"
582576
fi
577+
`
578+
}
579+
} else {
580+
return `\
581+
exec ${shProg} ${execArgs} ${shTarget} ${progArgs}"$@"
582+
exit $?
583583
`
584584
}
585-
} else {
585+
}
586+
587+
const msysArgs = isCmdRuntime ? escapeMsysCmdSwitches(args) : args
588+
if (msysArgs !== args) {
586589
sh += `\
587-
exec ${shProg} ${args} ${shTarget} ${progArgs}"$@"
588-
exit $?
590+
if [ -n "$msys" ]; then
591+
${indentShellBlock(generateExecBlock(msysArgs))}
592+
else
593+
${indentShellBlock(generateExecBlock(args))}
594+
fi
589595
`
596+
} else {
597+
sh += generateExecBlock(args)
590598
}
591599

592600
// Marker used by consumers to detect whether the shim is up-to-date
@@ -596,6 +604,10 @@ exit $?
596604
return sh
597605
}
598606

607+
function indentShellBlock (script: string): string {
608+
return script.split('\n').map(line => line ? ` ${line}` : line).join('\n')
609+
}
610+
599611
/**
600612
* Generate the content of a shim for PowerShell.
601613
*

test/e2e.test.js.snapshot

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ exports[`create a command shim for a .exe file > shim files 1`] = `
44
basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')")
55
basedir_win="$basedir"
66
exe=""
7+
msys=""
78

89
case \`uname -a\` in
910
*CYGWIN*|*MINGW*|*MSYS*)
1011
if command -v cygpath > /dev/null 2>&1; then
11-
basedir=\`cygpath -w "$basedir"\`
12-
basedir_win="$basedir"
12+
basedir_win=\`cygpath -w "$basedir"\`
1313
fi
1414
exe=".exe"
15+
msys="true"
1516
;;
1617
*WSL2*)
1718
if command -v wslpath > /dev/null 2>&1; then

0 commit comments

Comments
 (0)