From ebae55c43b1f917b4f698f9b243dc08c7f8a6648 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Wed, 27 Jul 2022 10:01:44 +0200 Subject: [PATCH] addressed expansion rules, no negation, no buffers, ascii and one pattern --- src/mime/testdata/test.types.globs2 | 1 + src/mime/type_unix.go | 146 ++++++++++++++++------------ src/mime/type_unix_test.go | 106 ++++++++++++++++++++ 3 files changed, 193 insertions(+), 60 deletions(-) diff --git a/src/mime/testdata/test.types.globs2 b/src/mime/testdata/test.types.globs2 index 4606d98f13a..02f206b87de 100644 --- a/src/mime/testdata/test.types.globs2 +++ b/src/mime/testdata/test.types.globs2 @@ -8,6 +8,7 @@ 50:example/test:*.t4 50:text/plain:*,v 50:application/x-trash:*~ +50:application/x-test-man:*.[1-3] 30:example/do-not-use:*.t4 10:example/glob-question-mark:*.foo?ar 10:example/glob-asterisk:*.foo*r diff --git a/src/mime/type_unix.go b/src/mime/type_unix.go index ff778e544f2..b5aeb2870eb 100644 --- a/src/mime/type_unix.go +++ b/src/mime/type_unix.go @@ -8,9 +8,9 @@ package mime import ( "bufio" - "bytes" "os" "strings" + "unicode" ) func init() { @@ -136,76 +136,102 @@ func initMimeForTests() map[string]string { } func expand(glob string) ([]string, bool) { - runes := []rune(glob) - resultSize := 1 - stringSize := 0 + openingBracketIndex := -1 + closingBracketIndex := -1 -countLoop: - for i := 0; i < len(runes); i++ { - switch runes[i] { + var prefix []byte + var suffix []byte + var mux *[]byte = &prefix + + for i, c := range glob { + if c > unicode.MaxASCII { + return nil, false + } + switch c { case '[': - for j := i + 1; j < len(runes); j++ { - if runes[j] == ']' { - i = j - continue countLoop - } - if runes[j+1] == '-' { - if j+2 >= len(runes) { - return nil, false - } - resultSize *= int(runes[j+2]-runes[j]) + 1 - stringSize++ - j += 2 - continue - } - resultSize++ - stringSize++ + if len(*mux) > 0 && (*mux)[len(*mux)-1] == '\\' { + (*mux)[len(*mux)-1] = glob[i] + continue } + if openingBracketIndex != -1 { + if closingBracketIndex != -1 { + return nil, false + } + continue + } + openingBracketIndex = i + mux = &suffix + case ']': + if openingBracketIndex == -1 { + *mux = append(*mux, ']') + continue + } + if i == openingBracketIndex+1 { + continue + } + closingBracketIndex = i default: - stringSize++ + if openingBracketIndex > -1 && closingBracketIndex == -1 { + continue + } + *mux = append(*mux, glob[i]) } } - buffers := make([]bytes.Buffer, resultSize, resultSize) - for i := range buffers { - buffers[i].Grow(stringSize) + switch { + case openingBracketIndex == -1 && closingBracketIndex == -1: + return []string{string(prefix)}, true + + case openingBracketIndex != -1 && closingBracketIndex == -1: + return []string{string(prefix) + glob[openingBracketIndex:]}, true + + case openingBracketIndex != -1 && openingBracketIndex+1 == '!': + return nil, false } - for i := 0; i < len(runes); i++ { - switch runes[i] { - case '[': - var expanded []rune - for j := i + 1; j < len(runes); j++ { - if runes[j] == ']' { - i = j - break - } - if runes[j+1] == '-' { - for k := runes[j]; k <= runes[j+2]; k++ { - expanded = append(expanded, k) - } - j += 2 - continue - } - expanded = append(expanded, runes[j]) - } - - for j, k := 0, 0; j < resultSize; j, k = j+1, (k+1)%len(expanded) { - buffers[j].WriteRune(expanded[k]) - } - - default: - for j := 0; j < resultSize; j++ { - buffers[j].WriteRune(runes[i]) - } - } + expansion := expandRangeWithoutNegation(glob[openingBracketIndex+1 : closingBracketIndex]) + if expansion == nil { + return nil, false } - result := make([]string, 0, resultSize) - for i := 0; i < resultSize; i++ { - result = append(result, buffers[i].String()) + results := make([]string, len(expansion)) + for i := 0; i < len(expansion); i++ { + results[i] = string(prefix) + string(expansion[i]) + string(suffix) } - return result, true - + return results, true +} + +func expandRangeWithoutNegation(r string) []byte { + var expansion []byte + for i := 0; i < len(r); i++ { + if r[i] == '!' && i == 0 { + // no negations of range expression + return nil + } + + if r[i] != '-' { + expansion = append(expansion, r[i]) + continue + } + + if i == 0 || i == len(r)-1 { + expansion = append(expansion, '-') + continue + } + if r[i+1] < r[i-1] { + // invalid character range + return nil + } + + for c := r[i-1] + 1; c <= r[i+1]; c++ { + if c == '/' { + // '/' cannot be matched: https://man7.org/linux/man-pages/man7/glob.7.html + continue + } + expansion = append(expansion, c) + } + i++ + } + return expansion } diff --git a/src/mime/type_unix_test.go b/src/mime/type_unix_test.go index ee5c1c2822a..d323e62cf09 100644 --- a/src/mime/type_unix_test.go +++ b/src/mime/type_unix_test.go @@ -54,6 +54,7 @@ func TestMimeExtension(t *testing.T) { want []string }{ {typ: "example/glob-range", want: []string{".foo1", ".foo2", ".foo3"}}, + {typ: "application/x-test-man", want: []string{".1", ".2", ".3"}}, } for _, tt := range tests { @@ -67,3 +68,108 @@ func TestMimeExtension(t *testing.T) { } } } + +func Test_expansion(t *testing.T) { + tests := []struct { + glob string + ok bool + want []string + }{ + { + glob: "foo", + ok: true, + want: []string{"foo"}, + }, + { + glob: ".foo[1-3da-c]", + ok: true, + want: []string{".foo1", ".foo2", ".foo3", ".food", ".fooa", ".foob", ".fooc"}, + }, + { + glob: ".foo[1-3][1-4]", + ok: false, + }, + { + glob: `.foo\[1-3`, + ok: true, + want: []string{".foo[1-3"}, + }, + { + glob: `.foo[1-3`, + ok: true, + want: []string{".foo[1-3"}, + }, + { + glob: ".foo1-3]", + ok: true, + want: []string{".foo1-3]"}, + }, + { + glob: ".foo[12-]", + ok: true, + want: []string{".foo1", ".foo2", ".foo-"}, + }, + { + glob: ".foo[-12]", + ok: true, + want: []string{".foo-", ".foo1", ".foo2"}, + }, + { + glob: ".foo[3-1]", + ok: false, + }, + { + glob: "foo[1-3].bar", + ok: true, + want: []string{"foo1.bar", "foo2.bar", "foo3.bar"}, + }, + { + glob: ".foo[!1-3]", + ok: false, + }, + { + glob: ".foo[!12]", + ok: false, + }, + { + glob: ".foo[1-3!a]", + ok: true, + want: []string{".foo1", ".foo2", ".foo3", ".foo!", ".fooa"}, + }, + { + glob: "[0-12-5]", + ok: true, + want: []string{"0", "1", "2", "3", "4", "5"}, + }, + { + glob: "[][!]", + ok: true, + want: []string{"]", "[", "!"}, + }, + { + glob: "[--0*?]", + ok: true, + want: []string{"-", ".", "0", "*", "?"}, + }, + { + glob: ".foo[]", + ok: true, + want: []string{".foo[]"}, + }, + { + glob: ".foo[1-3][4-5]", + ok: false, + }, + } + + for _, tt := range tests { + got, ok := expand(tt.glob) + if ok != tt.ok { + t.Errorf("expansion(%q) status = %v; want %v", tt.glob, ok, tt.ok) + } + + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("expansion(%q) result = %q; want %q", tt.glob, got, tt.want) + } + } +}