Ctrl K
ComunidadGuías…

pkg/desktop/desktop_windows.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
//go:build windows

package desktop

import (
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"

	"github.com/cockroachdb/errors"
	"golang.org/x/sys/windows"
)

// open launches the path with its registered handler. For a directory this opens
// an Explorer window on the directory itself.
func open(path string) error {
	target, err := shellPath(path)
	if err != nil {
		return err
	}

	file, err := windows.UTF16PtrFromString(target)
	if err != nil {
		return err
	}

	// A nil verb means the file type's default verb, falling back to "open" and
	// then to whatever the registry lists first.
	err = windows.ShellExecute(0, nil, file, nil, nil, windows.SW_SHOWNORMAL)
	runtime.KeepAlive(file)
	if err != nil {
		return errors.Wrapf(err, "open %s", target)
	}
	return nil
}

// reveal opens Explorer on the directory holding the path, with the item selected.
func reveal(path string) error {
	target, err := shellPath(path)
	if err != nil {
		return err
	}

	// The command line is built by hand below, because Explorer doesn't accept the
	// quoting Go applies. A double quote is the one character that could break out
	// of it, and an existing file can't contain one, so this check is dead code kept
	// as a backstop for a path that never passed Reveal's stat.
	if strings.Contains(target, `"`) {
		return errors.Newf("cannot reveal a path containing a quote: %s", target)
	}

	// Resolve explorer.exe absolutely rather than off PATH.
	windowsDir, err := windows.GetWindowsDirectory()
	if err != nil {
		return errors.Wrap(err, "locate the Windows directory")
	}
	explorer := filepath.Join(windowsDir, "explorer.exe")

	// nosemgrep
	cmd := exec.Command(explorer)
	cmd.SysProcAttr = &syscall.SysProcAttr{
		CmdLine: `"` + explorer + `" /select,"` + target + `"`,
	}
	if err := cmd.Start(); err != nil {
		return errors.Wrapf(err, "reveal %s", target)
	}
	// Explorer's exit status is not meaningful, so it is only reaped.
	go func() { _ = cmd.Wait() }()
	return nil
}

func supported() bool {
	return true
}

// shellPath prepares a path for Explorer, which is not the Win32 file API. The \\?\
// verbatim prefix has to go, because shell entry points reject such a path even
// when it is well under MAX_PATH, so the path must never come from a canonicalizing
// step. And a bare drive designator needs its trailing separator: "C:\" resolves
// where "C:" doesn't.
func shellPath(path string) (string, error) {
	if path == "" {
		return "", errors.New("empty path")
	}

	switch {
	case strings.HasPrefix(path, `\\?\UNC\`), strings.HasPrefix(path, `\\.\UNC\`):
		path = `\\` + path[len(`\\?\UNC\`):] // \\?\UNC\srv\share -> \\srv\share
	case strings.HasPrefix(path, `\\?\`), strings.HasPrefix(path, `\\.\`):
		path = path[len(`\\?\`):] // \\?\C:\dir -> C:\dir
	}

	abs, err := filepath.Abs(path)
	if err != nil {
		return "", errors.Wrapf(err, "resolve %s", path)
	}
	abs = filepath.Clean(abs)
	if len(abs) == 2 && abs[1] == ':' {
		// Clean("C:\\") keeps the separator, but Clean("C:") drops it.
		abs += `\`
	}

	// The shell namespace is bound by MAX_PATH with no \\?\ escape hatch, and deep
	// object keys reach that in practice. Say so rather than opening a window on
	// the wrong thing.
	if len(abs) >= 260 {
		return "", errors.Newf("path is %d characters, which exceeds the Windows shell's limit of 260: %s", len(abs), abs)
	}
	return abs, nil
}