Skip to content

Commit bd8d5b6

Browse files
committed
fix: set UTF-8 code page in safeWindowsExec batch files (#14267)
safeWindowsExec writes temp .bat files as UTF-8 (Deno default), but cmd.exe reads them using the OEM code page (e.g., CP850). Multi-byte UTF-8 characters like é (0xC3 0xA9) get misinterpreted, breaking paths with accented characters (e.g., C:\Users\Sébastien\). Add `chcp 65001 >nul` to switch cmd.exe to UTF-8 before the command line is parsed. Use CRLF line endings for correct .bat parsing.
1 parent d577ef4 commit bd8d5b6

2 files changed

Lines changed: 56 additions & 1 deletion

File tree

src/core/windows.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export async function safeWindowsExec(
156156
try {
157157
Deno.writeTextFileSync(
158158
tempFile,
159-
["@echo off", [program, ...args].join(" ")].join("\n"),
159+
["@echo off", "chcp 65001 >nul", [program, ...args].join(" ")].join("\r\n"),
160160
);
161161
return await fnExec(["cmd", "/c", tempFile]);
162162
} finally {

tests/unit/windows-exec.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,58 @@ echo ARG: %~1
155155
},
156156
{ ignore: !isWindows },
157157
);
158+
159+
// Test that safeWindowsExec handles accented/Unicode characters in paths (issue #14267)
160+
// The bug: safeWindowsExec writes .bat as UTF-8 but cmd.exe reads using OEM code page
161+
// (e.g., CP850), garbling multi-byte chars like é (0xC3 0xA9 → two CP850 chars).
162+
unitTest(
163+
"safeWindowsExec - handles accented characters in paths (issue #14267)",
164+
async () => {
165+
const tempDir = Deno.makeTempDirSync({ prefix: "quarto-test" });
166+
167+
try {
168+
// Create directory with accented characters (simulates C:\Users\Sébastien\)
169+
const accentedDir = join(tempDir, "Sébastien", "project");
170+
Deno.mkdirSync(accentedDir, { recursive: true });
171+
172+
// Create a test file at the accented path
173+
const testFile = join(accentedDir, "test.txt");
174+
Deno.writeTextFileSync(testFile, "accented path works");
175+
176+
// Create batch file that reads the accented-path file
177+
const batFile = join(tempDir, "read-file.bat");
178+
Deno.writeTextFileSync(
179+
batFile,
180+
`@echo off
181+
type %1
182+
`,
183+
);
184+
185+
const quoted = requireQuoting([batFile, testFile]);
186+
187+
const result = await safeWindowsExec(
188+
quoted.args[0],
189+
quoted.args.slice(1),
190+
(cmd) =>
191+
execProcess({
192+
cmd: cmd[0],
193+
args: cmd.slice(1),
194+
stdout: "piped",
195+
stderr: "piped",
196+
}),
197+
);
198+
199+
assert(
200+
result.success,
201+
`Should execute successfully with accented path. stderr: ${result.stderr}`,
202+
);
203+
assert(
204+
result.stdout?.includes("accented path works"),
205+
`Should read file at accented path. Got: ${result.stdout}`,
206+
);
207+
} finally {
208+
Deno.removeSync(tempDir, { recursive: true });
209+
}
210+
},
211+
{ ignore: !isWindows },
212+
);

0 commit comments

Comments
 (0)