Files

110 lines
2.7 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package clipboard
import (
"encoding/json"
"io"
"net/url"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"syscall"
)
// Snapshot contains only the clipboard facts needed for Cthulhu's announcement.
type Snapshot struct {
Text string `json:"text"`
HasText bool `json:"hasText"`
Image bool `json:"image"`
URIs []string `json:"uris"`
}
// WriteMessage is called only by the short lived clipboard helper process.
// URI filesystem checks stay here so the parent can enforce a read deadline.
func WriteMessage(output io.Writer) error {
snapshot, err := read_native()
if err != nil {
return err
}
return json.NewEncoder(output).Encode(struct {
Message string `json:"message"`
}{Message: Format(snapshot)})
}
func Format(snapshot Snapshot) string {
if snapshot.Image {
return "The clipboard contains a image"
}
if len(snapshot.URIs) != 0 {
return format_uris(snapshot.URIs)
}
if snapshot.HasText {
return "Text in clipboard " + snapshot.Text
}
return "The clipboard is empty"
}
func format_uris(uris []string) string {
objects := make([]string, 0, len(uris))
for _, raw := range uris {
if raw == "" {
continue
}
parsed, err := url.Parse(raw)
if err != nil {
objects = append(objects, raw)
continue
}
name := path.Base(parsed.Path)
if name == "." || name == "/" {
name = raw
}
parts := []string{name}
if parsed.Scheme == "file" && (parsed.Host == "" || parsed.Host == "localhost") {
localPath := parsed.Path
link := false
if info, err := os.Lstat(localPath); err == nil && info.Mode()&os.ModeSymlink != 0 {
link = true
}
if info, err := os.Stat(localPath); err == nil {
if info.IsDir() {
parts = append(parts, "Folder")
} else if info.Mode().IsRegular() {
parts = append(parts, "File")
}
if !link && is_mount(localPath, info) {
parts = append(parts, "Disk")
}
}
if link {
parts = append(parts, "Link")
}
}
objects = append(objects, strings.Join(parts, " "))
}
if len(objects) == 0 {
return "The clipboard is empty"
}
label := " Object in clipboard "
if len(objects) > 1 {
label = " Objects in clipboard "
}
return strconv.Itoa(len(objects)) + label + strings.Join(objects, " ")
}
func is_mount(localPath string, info os.FileInfo) bool {
parentPath := filepath.Dir(filepath.Clean(localPath))
parent, err := os.Stat(parentPath)
if err != nil {
return false
}
currentStat, currentOK := info.Sys().(*syscall.Stat_t)
parentStat, parentOK := parent.Sys().(*syscall.Stat_t)
return currentOK && parentOK && (currentStat.Dev != parentStat.Dev || currentStat.Ino == parentStat.Ino)
}