1
0
mirror of https://github.com/golang/go synced 2024-11-17 03:24:49 -07:00

os/exec: preserve original order of entries in dedupEnv

Once #50599 is implemented, the entries will be observable via the
Environ method. I find it confusing for later entries in the list to
jump arbitrarily far forward based on entries for the same key that no
longer exist.

This also fixes the deduplication logic for the degenerate Windows
keys observed in #49886, which were previously deduplicated as empty
keys.

(It does not do anything about the even-more-degenerate keys observed
in #52436.)

For #50599.

Change-Id: Ia7cd2200ec34ccc4b9d18631cb513194dc420c25
Reviewed-on: https://go-review.googlesource.com/c/go/+/401339
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Bryan C. Mills 2022-04-19 15:54:50 -04:00 committed by Gopher Robot
parent ab9d31da9e
commit aa24255541
2 changed files with 45 additions and 10 deletions

View File

@ -18,17 +18,29 @@ func TestDedupEnv(t *testing.T) {
{ {
noCase: true, noCase: true,
in: []string{"k1=v1", "k2=v2", "K1=v3"}, in: []string{"k1=v1", "k2=v2", "K1=v3"},
want: []string{"K1=v3", "k2=v2"}, want: []string{"k2=v2", "K1=v3"},
}, },
{ {
noCase: false, noCase: false,
in: []string{"k1=v1", "K1=V2", "k1=v3"}, 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"}, in: []string{"=a", "=b", "foo", "bar"},
want: []string{"=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 { for _, tt := range tests {
got := dedupEnvCase(tt.noCase, tt.in) got := dedupEnvCase(tt.noCase, tt.in)

View File

@ -745,24 +745,47 @@ func dedupEnv(env []string) []string {
// dedupEnvCase is dedupEnv with a case option for testing. // dedupEnvCase is dedupEnv with a case option for testing.
// If caseInsensitive is true, the case of keys is ignored. // If caseInsensitive is true, the case of keys is ignored.
func dedupEnvCase(caseInsensitive bool, env []string) []string { 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)) out := make([]string, 0, len(env))
saw := make(map[string]int, len(env)) // key => index into out saw := make(map[string]bool, len(env))
for _, kv := range env { for n := len(env); n > 0; n-- {
k, _, ok := strings.Cut(kv, "=") kv := env[n-1]
if !ok {
out = append(out, kv) 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 continue
} }
k := kv[:i]
if caseInsensitive { if caseInsensitive {
k = strings.ToLower(k) k = strings.ToLower(k)
} }
if dupIdx, isDup := saw[k]; isDup { if saw[k] {
out[dupIdx] = kv
continue continue
} }
saw[k] = len(out)
saw[k] = true
out = append(out, kv) 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 return out
} }