Skip to content

Commit 3fa1149

Browse files
wesmclaude
andauthored
Strip surrounding quotes from CLI paths for Windows CMD (#91)
## Summary Fixes #86 - Windows CMD does not strip single quotes from arguments, so `--home 'C:\Users\foo'` passes the literal value `'C:\Users\foo'` (quotes included), producing broken paths like `'C:\Users\foo'\config.toml` - `expandPath()` now strips matching surrounding single or double quotes before processing, covering `--home`, `--config`, and `MSGVAULT_HOME` paths ## Test plan - [x] `TestExpandPath` — new cases for single-quoted, double-quoted, mismatched, and single-char inputs - [x] `make lint` — clean - [x] `make test` — all pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 598b386 commit 3fa1149

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed

internal/config/config.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"log/slog"
77
"os"
88
"path/filepath"
9+
"runtime"
910
"strings"
1011

1112
"github.com/BurntSushi/toml"
@@ -237,10 +238,20 @@ func resolveRelative(path, base string) string {
237238

238239
// expandPath expands ~ to the user's home directory.
239240
// Only expands paths that are exactly "~" or start with "~/".
241+
// It also strips surrounding single or double quotes, which Windows CMD
242+
// passes through literally (unlike Unix shells which strip them).
240243
func expandPath(path string) string {
241244
if path == "" {
242245
return path
243246
}
247+
// Strip surrounding quotes left by Windows CMD (e.g. --home 'C:\Users\foo').
248+
// Only on Windows — Unix shells strip quotes before the process sees them,
249+
// and literal quote characters in Unix paths are valid (if unusual).
250+
if runtime.GOOS == "windows" && len(path) >= 2 &&
251+
((path[0] == '\'' && path[len(path)-1] == '\'') ||
252+
(path[0] == '"' && path[len(path)-1] == '"')) {
253+
path = path[1 : len(path)-1]
254+
}
244255
if path == "~" || strings.HasPrefix(path, "~"+string(os.PathSeparator)) || strings.HasPrefix(path, "~/") {
245256
home, err := os.UserHomeDir()
246257
if err != nil {

internal/config/config_test.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ func TestExpandPath(t *testing.T) {
1515
}
1616

1717
tests := []struct {
18-
name string
19-
input string
20-
expected string
21-
unixOnly bool // skip on Windows (uses Unix-style absolute paths)
18+
name string
19+
input string
20+
expected string
21+
unixOnly bool // skip on Windows (uses Unix-style absolute paths)
22+
windowsOnly bool // skip on non-Windows (quote stripping is Windows-only)
2223
}{
2324
{
2425
name: "empty string",
@@ -50,6 +51,34 @@ func TestExpandPath(t *testing.T) {
5051
input: "~//foo",
5152
expected: filepath.Join(home, "foo"),
5253
},
54+
{
55+
name: "single-quoted path (Windows CMD)",
56+
input: `'C:\Users\wesmc\testing'`,
57+
expected: `C:\Users\wesmc\testing`,
58+
windowsOnly: true,
59+
},
60+
{
61+
name: "double-quoted path (Windows CMD)",
62+
input: `"C:\Users\wesmc\testing"`,
63+
expected: `C:\Users\wesmc\testing`,
64+
windowsOnly: true,
65+
},
66+
{
67+
name: "single-quoted tilde path",
68+
input: "'~/custom-data'",
69+
expected: filepath.Join(home, "custom-data"),
70+
windowsOnly: true,
71+
},
72+
{
73+
name: "mismatched quotes not stripped",
74+
input: `'C:\Users\wesmc"`,
75+
expected: `'C:\Users\wesmc"`,
76+
},
77+
{
78+
name: "single char not stripped",
79+
input: "'",
80+
expected: "'",
81+
},
5382
{
5483
name: "absolute path unchanged",
5584
input: "/var/log/test",
@@ -79,6 +108,9 @@ func TestExpandPath(t *testing.T) {
79108
if tt.unixOnly && runtime.GOOS == "windows" {
80109
t.Skip("skipping Unix-specific path test on Windows")
81110
}
111+
if tt.windowsOnly && runtime.GOOS != "windows" {
112+
t.Skip("skipping Windows-specific path test on non-Windows")
113+
}
82114
got := expandPath(tt.input)
83115
if got != tt.expected {
84116
t.Errorf("expandPath(%q) = %q, want %q", tt.input, got, tt.expected)

0 commit comments

Comments
 (0)