aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2026-01-17 12:04:32 +0100
committerMartin Fischer <martin@push-f.com>2026-01-17 12:15:22 +0100
commita35a553cbdab04a50b949b0c27c7e287de205fcf (patch)
treecc12aad2f652db4d7be44055bcc4de15b4e4f163
parent4eae890b11b743cbdcf68ee185d67bce873076dc (diff)
fix(open): always try specific MIME type patterns before patterns ending in *
-rw-r--r--programs/open/main.go20
-rw-r--r--programs/open/main_test.go28
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