addressed expansion rules, no negation, no buffers, ascii and one pattern

This commit is contained in:
Nikola Jokic 2022-07-27 10:01:44 +02:00
parent 37f7999e5f
commit ebae55c43b
3 changed files with 193 additions and 60 deletions

View File

@ -8,6 +8,7 @@
50:example/test:*.t4 50:example/test:*.t4
50:text/plain:*,v 50:text/plain:*,v
50:application/x-trash:*~ 50:application/x-trash:*~
50:application/x-test-man:*.[1-3]
30:example/do-not-use:*.t4 30:example/do-not-use:*.t4
10:example/glob-question-mark:*.foo?ar 10:example/glob-question-mark:*.foo?ar
10:example/glob-asterisk:*.foo*r 10:example/glob-asterisk:*.foo*r

View File

@ -8,9 +8,9 @@ package mime
import ( import (
"bufio" "bufio"
"bytes"
"os" "os"
"strings" "strings"
"unicode"
) )
func init() { func init() {
@ -136,76 +136,102 @@ func initMimeForTests() map[string]string {
} }
func expand(glob string) ([]string, bool) { func expand(glob string) ([]string, bool) {
runes := []rune(glob) openingBracketIndex := -1
resultSize := 1 closingBracketIndex := -1
stringSize := 0
countLoop: var prefix []byte
for i := 0; i < len(runes); i++ { var suffix []byte
switch runes[i] { var mux *[]byte = &prefix
for i, c := range glob {
if c > unicode.MaxASCII {
return nil, false
}
switch c {
case '[': case '[':
for j := i + 1; j < len(runes); j++ { if len(*mux) > 0 && (*mux)[len(*mux)-1] == '\\' {
if runes[j] == ']' { (*mux)[len(*mux)-1] = glob[i]
i = j continue
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 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: default:
stringSize++ if openingBracketIndex > -1 && closingBracketIndex == -1 {
continue
}
*mux = append(*mux, glob[i])
} }
} }
buffers := make([]bytes.Buffer, resultSize, resultSize) switch {
for i := range buffers { case openingBracketIndex == -1 && closingBracketIndex == -1:
buffers[i].Grow(stringSize) 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++ { expansion := expandRangeWithoutNegation(glob[openingBracketIndex+1 : closingBracketIndex])
switch runes[i] { if expansion == nil {
case '[': return nil, false
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])
}
}
} }
result := make([]string, 0, resultSize) results := make([]string, len(expansion))
for i := 0; i < resultSize; i++ { for i := 0; i < len(expansion); i++ {
result = append(result, buffers[i].String()) 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
} }

View File

@ -54,6 +54,7 @@ func TestMimeExtension(t *testing.T) {
want []string want []string
}{ }{
{typ: "example/glob-range", want: []string{".foo1", ".foo2", ".foo3"}}, {typ: "example/glob-range", want: []string{".foo1", ".foo2", ".foo3"}},
{typ: "application/x-test-man", want: []string{".1", ".2", ".3"}},
} }
for _, tt := range tests { 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)
}
}
}