{
noCase: true,
in: []string{"k1=v1", "k2=v2", "K1=v3"},
- want: []string{"K1=v3", "k2=v2"},
+ want: []string{"k2=v2", "K1=v3"},
},
{
noCase: false,
in: []string{"k1=v1", "K1=V2", "k1=v3"},
- want: []string{"k1=v3", "K1=V2"},
+ want: []string{"K1=V2", "k1=v3"},
},
{
in: []string{"=a", "=b", "foo", "bar"},
want: []string{"=b", "foo", "bar"},
},
+ {
+ // #49886: preserve weird Windows keys with leading "=" signs.
+ noCase: true,
+ in: []string{`=C:=C:\golang`, `=D:=D:\tmp`, `=D:=D:\`},
+ want: []string{`=C:=C:\golang`, `=D:=D:\`},
+ },
+ {
+ // #52436: preserve invalid key-value entries (for now).
+ // (Maybe filter them out or error out on them at some point.)
+ in: []string{"dodgy", "entries"},
+ want: []string{"dodgy", "entries"},
+ },
}
for _, tt := range tests {
got := dedupEnvCase(tt.noCase, tt.in)
// dedupEnvCase is dedupEnv with a case option for testing.
// If caseInsensitive is true, the case of keys is ignored.
func dedupEnvCase(caseInsensitive bool, env []string) []string {
+ // Construct the output in reverse order, to preserve the
+ // last occurrence of each key.
out := make([]string, 0, len(env))
- saw := make(map[string]int, len(env)) // key => index into out
- for _, kv := range env {
- k, _, ok := strings.Cut(kv, "=")
- if !ok {
- out = append(out, kv)
+ saw := make(map[string]bool, len(env))
+ for n := len(env); n > 0; n-- {
+ kv := env[n-1]
+
+ i := strings.Index(kv, "=")
+ if i == 0 {
+ // We observe in practice keys with a single leading "=" on Windows.
+ // TODO(#49886): Should we consume only the first leading "=" as part
+ // of the key, or parse through arbitrarily many of them until a non-"="?
+ i = strings.Index(kv[1:], "=") + 1
+ }
+ if i < 0 {
+ if kv != "" {
+ // The entry is not of the form "key=value" (as it is required to be).
+ // Leave it as-is for now.
+ // TODO(#52436): should we strip or reject these bogus entries?
+ out = append(out, kv)
+ }
continue
}
+ k := kv[:i]
if caseInsensitive {
k = strings.ToLower(k)
}
- if dupIdx, isDup := saw[k]; isDup {
- out[dupIdx] = kv
+ if saw[k] {
continue
}
- saw[k] = len(out)
+
+ saw[k] = true
out = append(out, kv)
}
+
+ // Now reverse the slice to restore the original order.
+ for i := 0; i < len(out)/2; i++ {
+ j := len(out) - i - 1
+ out[i], out[j] = out[j], out[i]
+ }
+
return out
}