diff options
| author | Martin Fischer <martin@push-f.com> | 2026-01-17 12:04:32 +0100 |
|---|---|---|
| committer | Martin Fischer <martin@push-f.com> | 2026-01-17 12:15:22 +0100 |
| commit | a35a553cbdab04a50b949b0c27c7e287de205fcf (patch) | |
| tree | cc12aad2f652db4d7be44055bcc4de15b4e4f163 | |
| parent | 4eae890b11b743cbdcf68ee185d67bce873076dc (diff) | |
fix(open): always try specific MIME type patterns before patterns ending in *
| -rw-r--r-- | programs/open/main.go | 20 | ||||
| -rw-r--r-- | programs/open/main_test.go | 28 |
2 files changed, 46 insertions, 2 deletions
diff --git a/programs/open/main.go b/programs/open/main.go index f193f3f..f5a9204 100644 --- a/programs/open/main.go +++ b/programs/open/main.go @@ -3,12 +3,14 @@ package main import ( "fmt" "log" + "maps" "mime" "net/url" "os" "os/exec" "path" "path/filepath" + "slices" "strings" "github.com/BurntSushi/toml" @@ -88,13 +90,27 @@ func (c Config) findCommand(arg string) ([]string, error) { mimeType, _, _ = strings.Cut(mimeType, ";") } - for mimeTypePat, cmd := range c.MimeTypes { + sortedMimeTypes := slices.SortedFunc(maps.Keys(c.MimeTypes), func(a, b string) int { + aStar := strings.HasSuffix(a, "*") + bStar := strings.HasSuffix(b, "*") + + // put patterns ending with * last + if aStar && !bStar { + return 1 + } else if !aStar && bStar { + return -1 + } + + return strings.Compare(a, b) + }) + + for _, mimeTypePat := range sortedMimeTypes { matched, err := path.Match(mimeTypePat, mimeType) if err != nil { return nil, err } if matched { - return cmd, nil + return c.MimeTypes[mimeTypePat], nil } } diff --git a/programs/open/main_test.go b/programs/open/main_test.go index 57b501f..8f01766 100644 --- a/programs/open/main_test.go +++ b/programs/open/main_test.go @@ -105,6 +105,34 @@ func TestFindCommand_FileMissing(t *testing.T) { } } +func TestFindCommand_MimeSpecificFirst(t *testing.T) { + // More specific patterns should be tried first irrespective of their configured order. + filePath := path.Join(t.TempDir(), "test.svg") + _, err := os.Create(filePath) + if err != nil { + t.Fatal(err) + } + + cfg := Config{ + MimeTypes: map[string][]string{ + "image/*": {"general-image"}, + "image/svg+xml": {"specific-image"}, + }, + UriSchemes: map[string][]string{}, + } + // Running several times because if the priorization of specific patterns isn't + // implemented then it could succeed depending on the random map iteration order. + for i := 0; i < 10; i++ { + cmd, err := cfg.findCommand(filePath) + if err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(cmd, cfg.MimeTypes["image/svg+xml"]); diff != "" { + t.Fatal("unexpected command", diff) + } + } +} + func TestFindCommand_UriCheckedFirst(t *testing.T) { cases := []struct { uriHandlerExists bool |
