[v3-linux] fileexplorer fix opening dir on Linux (#4521)

* convert file to use lf line-endings

* fileexplorer fix opening dir on Linux

* include update in unreleased changelog

---------

Co-authored-by: Angus Dippenaar <angusdippenaar@gmail.com>
This commit is contained in:
Atterpac 2025-11-03 14:14:27 -07:00 committed by GitHub
commit a3a5d25d90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 108 additions and 97 deletions

View file

@ -54,6 +54,7 @@ After processing, the content will be moved to the main changelog and this file
**Fixed:**
- Fix memory leak in event system during window close operations (#5678)
- Fix crash when using context menus on Linux with Wayland
- Fix on Linux, trying to open a file with the `fileexplorer` will open parent directory (#4521)
**Security:**
- Update dependencies to address CVE-2024-12345 in third-party library

View file

@ -1,97 +1,107 @@
//go:build linux
package fileexplorer
import (
"bytes"
"fmt"
"net/url"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
ini "gopkg.in/ini.v1"
)
func explorerBinArgs(path string, selectFile bool) (string, []string, error) {
// Map of field codes to their replacements
var fieldCodes = map[string]string{
"%d": "",
"%D": "",
"%n": "",
"%N": "",
"%v": "",
"%m": "",
"%f": path,
"%F": path,
"%u": pathToURI(path),
"%U": pathToURI(path),
}
fileManagerQuery := exec.Command("xdg-mime", "query", "default", "inode/directory")
buf := new(bytes.Buffer)
fileManagerQuery.Stdout = buf
fileManagerQuery.Stderr = nil
if err := fileManagerQuery.Run(); err != nil {
return fallbackExplorerBinArgs(path, selectFile)
}
desktopFile, err := findDesktopFile(strings.TrimSpace((buf.String())))
if err != nil {
return fallbackExplorerBinArgs(path, selectFile)
}
cfg, err := ini.Load(desktopFile)
if err != nil {
// Opting to fallback rather than fail
return fallbackExplorerBinArgs(path, selectFile)
}
exec := cfg.Section("Desktop Entry").Key("Exec").String()
for fieldCode, replacement := range fieldCodes {
exec = strings.ReplaceAll(exec, fieldCode, replacement)
}
args := strings.Fields(exec)
if !strings.Contains(strings.Join(args, " "), path) {
args = append(args, path)
}
return args[0], args[1:], nil
}
func sysProcAttr(path string, selectFile bool) *syscall.SysProcAttr {
return &syscall.SysProcAttr{}
}
func fallbackExplorerBinArgs(path string, selectFile bool) (string, []string, error) {
// NOTE: The linux fallback explorer opening is not supporting file selection
path = filepath.Dir(path)
return "xdg-open", []string{path}, nil
}
func pathToURI(path string) string {
absPath, err := filepath.Abs(path)
if err != nil {
return path
}
return "file://" + url.PathEscape(absPath)
}
func findDesktopFile(xdgFileName string) (string, error) {
paths := []string{
filepath.Join(os.Getenv("XDG_DATA_HOME"), "applications"),
filepath.Join(os.Getenv("HOME"), ".local", "share", "applications"),
"/usr/share/applications",
}
for _, path := range paths {
desktopFile := filepath.Join(path, xdgFileName)
if _, err := os.Stat(desktopFile); err == nil {
return desktopFile, nil
}
}
err := fmt.Errorf("desktop file not found: %s", xdgFileName)
return "", err
}
//go:build linux
package fileexplorer
import (
"bytes"
"fmt"
"net/url"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
ini "gopkg.in/ini.v1"
)
func explorerBinArgs(path string, selectFile bool) (string, []string, error) {
// Map of field codes to their replacements
var fieldCodes = map[string]string{
"%d": "",
"%D": "",
"%n": "",
"%N": "",
"%v": "",
"%m": "",
"%f": path,
"%F": path,
"%u": pathToURI(path),
"%U": pathToURI(path),
}
fileManagerQuery := exec.Command("xdg-mime", "query", "default", "inode/directory")
buf := new(bytes.Buffer)
fileManagerQuery.Stdout = buf
fileManagerQuery.Stderr = nil
if err := fileManagerQuery.Run(); err != nil {
return fallbackExplorerBinArgs(path, selectFile)
}
desktopFile, err := findDesktopFile(strings.TrimSpace((buf.String())))
if err != nil {
return fallbackExplorerBinArgs(path, selectFile)
}
cfg, err := ini.Load(desktopFile)
if err != nil {
// Opting to fallback rather than fail
return fallbackExplorerBinArgs(path, selectFile)
}
exec := cfg.Section("Desktop Entry").Key("Exec").String()
for fieldCode, replacement := range fieldCodes {
exec = strings.ReplaceAll(exec, fieldCode, replacement)
}
args := strings.Fields(exec)
if !strings.Contains(strings.Join(args, " "), path) {
args = append(args, path)
}
return args[0], args[1:], nil
}
func sysProcAttr(path string, selectFile bool) *syscall.SysProcAttr {
return &syscall.SysProcAttr{}
}
func fallbackExplorerBinArgs(path string, selectFile bool) (string, []string, error) {
// NOTE: The linux fallback explorer opening does not support file selection
stat, err := os.Stat(path)
if err != nil {
return "", []string{}, fmt.Errorf("stat path: %w", err)
}
// If the path is a file, we want to open the directory containing the file
if !stat.IsDir() {
path = filepath.Dir(path)
}
return "xdg-open", []string{path}, nil
}
func pathToURI(path string) string {
absPath, err := filepath.Abs(path)
if err != nil {
return path
}
return "file://" + url.PathEscape(absPath)
}
func findDesktopFile(xdgFileName string) (string, error) {
paths := []string{
filepath.Join(os.Getenv("XDG_DATA_HOME"), "applications"),
filepath.Join(os.Getenv("HOME"), ".local", "share", "applications"),
"/usr/share/applications",
}
for _, path := range paths {
desktopFile := filepath.Join(path, xdgFileName)
if _, err := os.Stat(desktopFile); err == nil {
return desktopFile, nil
}
}
err := fmt.Errorf("desktop file not found: %s", xdgFileName)
return "", err
}