summaryrefslogtreecommitdiff
path: root/main.go
diff options
context:
space:
mode:
authortheimpostor <sahirhoda@gmail.com>2023-08-03 08:28:41 -0500
committertheimpostor <sahirhoda@gmail.com>2023-08-03 08:28:41 -0500
commit2d684e8e11525641c3db594b317b10faa2daa437 (patch)
tree88cfb83e1fa34062ec315b96ba0626915950275d /main.go
parenta51cea32f79859f6cf33bd6a45737a6019aa4ef9 (diff)
Rename module to osc, and use subcommands via spf13/cobra
Diffstat (limited to 'main.go')
-rw-r--r--main.go297
1 files changed, 153 insertions, 144 deletions
diff --git a/main.go b/main.go
index f7161a5..85ae9d0 100644
--- a/main.go
+++ b/main.go
@@ -3,7 +3,6 @@ package main
import (
"bufio"
"encoding/base64"
- "flag"
"fmt"
"github.com/gdamore/tcell/v2"
"github.com/jba/slog/handlers/loghandler"
@@ -13,12 +12,16 @@ import (
"os"
"strings"
"time"
+
+ "github.com/spf13/cobra"
)
var (
- oscOpen string = "\x1b]52;c;"
- oscClose string = "\a"
- isScreen bool
+ oscOpen string = "\x1b]52;c;"
+ oscClose string = "\a"
+ isScreen bool
+ verboseFlag bool
+ logfileFlag string
)
func encode(fname string, encoder io.WriteCloser) {
@@ -54,44 +57,8 @@ func closetty(tty tcell.Tty) {
tty.Close()
}
-func _main() int {
- var fnames []string
+func initLogging() {
var err error
- var exitCode int
-
- flag.Usage = func() {
- template := `Reads or writes the system clipboard using the ANSI OSC52 escape sequence.
-
-Usage:
-
-COPY mode (default):
-
- %s [file1 [...fileN]]
-
-With no arguments, will read from stdin.
-
-PASTE mode:
-
- %s --paste
-
-Outputs clipboard contents to stdout.
-
-Options:
-`
- fmt.Fprintf(flag.CommandLine.Output(), template, os.Args[0], os.Args[0])
- flag.PrintDefaults()
- }
-
- var pasteFlag bool
- var verboseFlag bool
- var logfileFlag string
- flag.BoolVar(&pasteFlag, "paste", pasteFlag, "paste operation")
- flag.BoolVar(&verboseFlag, "verbose", verboseFlag, "verbose logging")
- flag.BoolVar(&verboseFlag, "v", verboseFlag, "verbose logging")
- flag.StringVar(&logfileFlag, "logFile", logfileFlag, "redirect logs to file")
-
- flag.Parse()
-
logLevel := &slog.LevelVar{} // INFO
logOutput := os.Stdout
@@ -113,7 +80,9 @@ Options:
slog.SetDefault(logger)
slog.Debug("logging started")
+}
+func identifyTerm() {
if ti, err := tcell.LookupTerminfo(os.Getenv("TERM")); err != nil {
slog.Error(fmt.Sprintf("Failed to lookup terminfo: %v", err))
} else {
@@ -128,129 +97,169 @@ Options:
oscOpen = "\x1bP" + oscOpen
oscClose = oscClose + "\x1b\\"
}
+}
+
+func copy(fnames []string) error {
+ // copy
+ if len(fnames) == 0 {
+ fnames = []string{"-"}
+ }
- if !pasteFlag {
- // copy
- if len(flag.Args()) > 0 {
- fnames = flag.Args()
- } else {
- fnames = []string{"-"}
+ slog.Debug("Beginning osc52 copy operation")
+ err := func() error {
+ tty, err := opentty()
+ if err != nil {
+ slog.Error(fmt.Sprintf("opentty: %v", err))
+ return err
}
+ defer closetty(tty)
- slog.Debug("Beginning osc52 copy operation")
- func() {
- tty, err := opentty()
- if err != nil {
- slog.Error(fmt.Sprintf("opentty: %v", err))
- exitCode = 1
- return
- }
- defer closetty(tty)
+ // Open buffered output, using default max OSC52 length as buffer size
+ // TODO limit size
+ out := bufio.NewWriterSize(tty, 1000000)
- // Open buffered output, using default max OSC52 length as buffer size
- // TODO limit size
- out := bufio.NewWriterSize(tty, 1000000)
+ // Start OSC52
+ fmt.Fprint(out, oscOpen)
- // Start OSC52
- fmt.Fprint(out, oscOpen)
+ b64 := base64.NewEncoder(base64.StdEncoding, out)
+ for _, fname := range fnames {
+ encode(fname, b64)
+ }
+ b64.Close()
- b64 := base64.NewEncoder(base64.StdEncoding, out)
- for _, fname := range fnames {
- encode(fname, b64)
- }
- b64.Close()
+ // End OSC52
+ fmt.Fprint(out, oscClose)
- // End OSC52
- fmt.Fprint(out, oscClose)
+ out.Flush()
+ return nil
+ }()
+ slog.Debug("Ended osc52")
+ return err
+}
- out.Flush()
- }()
- slog.Debug("Ended osc52")
- } else {
- // paste
-
- slog.Debug("Beginning osc52 paste operation")
- data := func() []byte {
- tty, err := opentty()
- if err != nil {
- slog.Error(fmt.Sprintf("opentty: %v", err))
- exitCode = 1
- return nil
+func paste() error {
+ slog.Debug("Beginning osc52 paste operation")
+ if data, err := func() ([]byte, error) {
+ tty, err := opentty()
+ if err != nil {
+ slog.Error(fmt.Sprintf("opentty: %v", err))
+ return nil, err
+ }
+ defer closetty(tty)
+
+ // Start OSC52
+ fmt.Fprint(tty, oscOpen+"?"+oscClose)
+
+ ttyReader := bufio.NewReader(tty)
+ buf := make([]byte, 0, 1024)
+
+ // time out intial read in 100 milliseconds
+ readChan := make(chan byte, 1)
+ defer close(readChan)
+ go func() {
+ if b, e := ttyReader.ReadByte(); e != nil {
+ slog.Debug(fmt.Sprintf("Initial ReadByte error: %v", e))
+ } else {
+ readChan <- b
}
- defer closetty(tty)
-
- // Start OSC52
- fmt.Fprint(tty, oscOpen+"?"+oscClose)
-
- ttyReader := bufio.NewReader(tty)
- buf := make([]byte, 0, 1024)
-
- // time out intial read in 100 milliseconds
- readChan := make(chan byte, 1)
- defer close(readChan)
- go func() {
- if b, e := ttyReader.ReadByte(); e != nil {
- slog.Debug(fmt.Sprintf("Initial ReadByte error: %v", e))
- } else {
- readChan <- b
+ }()
+ select {
+ case b := <-readChan:
+ buf = append(buf, b)
+ case <-time.After(100 * time.Millisecond):
+ slog.Debug("tty read timeout")
+ return nil, fmt.Errorf("tty read timeout")
+ }
+
+ for {
+ if b, e := ttyReader.ReadByte(); e != nil {
+ slog.Error(fmt.Sprintf("ReadByte: %v", e))
+ return nil, e
+ } else {
+ slog.Debug(fmt.Sprintf("Read: %x '%s'", b, string(b)))
+ // Terminator might be BEL (\a) or ESC-backslash (\x1b\\)
+ if b == '\a' {
+ break
}
- }()
- select {
- case b := <-readChan:
buf = append(buf, b)
- case <-time.After(100 * time.Millisecond):
- slog.Debug("tty read timeout")
- exitCode = 1
- return nil
- }
-
- for {
- if b, e := ttyReader.ReadByte(); e != nil {
- slog.Error(fmt.Sprintf("ReadByte: %v", e))
- exitCode = 1
- return nil
- } else {
- slog.Debug(fmt.Sprintf("Read: %x '%s'", b, string(b)))
- // Terminator might be BEL (\a) or ESC-backslash (\x1b\\)
- if b == '\a' {
- break
- }
- buf = append(buf, b)
- // Skip initial 7 bytes of response
- if len(buf) > 9 && buf[len(buf)-2] == '\x1b' && buf[len(buf)-1] == '\\' {
- buf = buf[:len(buf)-2]
- break
- }
+ // Skip initial 7 bytes of response
+ if len(buf) > 9 && buf[len(buf)-2] == '\x1b' && buf[len(buf)-1] == '\\' {
+ buf = buf[:len(buf)-2]
+ break
}
}
+ }
- slog.Debug(fmt.Sprintf("buf[:7]: %q", buf[:7]))
- buf = buf[7:]
-
- decodedBuf := make([]byte, base64.StdEncoding.DecodedLen(len(buf)))
- n, err := base64.StdEncoding.Decode(decodedBuf, []byte(buf))
- if err != nil {
- slog.Error(fmt.Sprintf("decode error: %v", err))
- exitCode = 1
- return nil
- }
- decodedBuf = decodedBuf[:n]
+ slog.Debug(fmt.Sprintf("buf[:7]: %q", buf[:7]))
+ buf = buf[7:]
- return decodedBuf
- }()
+ decodedBuf := make([]byte, base64.StdEncoding.DecodedLen(len(buf)))
+ n, err := base64.StdEncoding.Decode(decodedBuf, []byte(buf))
+ if err != nil {
+ slog.Error(fmt.Sprintf("decode error: %v", err))
+ return nil, err
+ }
+ decodedBuf = decodedBuf[:n]
- if data != nil {
- if _, err := os.Stdout.Write(data); err != nil {
- slog.Error(fmt.Sprintf("Error writing to stdout: %v", err))
- exitCode = 1
- }
+ return decodedBuf, nil
+ }(); err != nil {
+ return err
+ } else {
+ if _, err = os.Stdout.Write(data); err != nil {
+ slog.Error(fmt.Sprintf("Error writing to stdout: %v", err))
+ return err
}
- slog.Debug("Ended osc52")
}
+ slog.Debug("Ended osc52")
- return exitCode
+ return nil
+}
+
+var copyCmd = &cobra.Command{
+ Use: "copy",
+ Short: "Copies input to the system clipboard",
+ Long: `Copies input to the system clipboard. Usage:
+
+osc copy [file1 [...fileN]]
+
+With no arguments, will read from stdin.`,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ initLogging()
+ identifyTerm()
+ return copy(args)
+ },
+}
+
+var pasteCmd = &cobra.Command{
+ Use: "paste",
+ Short: "Outputs system clipboard contents to stdout",
+ Long: `Outputs system clipboard contents to stdout. Usage:
+
+osc paste`,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ initLogging()
+ identifyTerm()
+ return paste()
+ },
+}
+
+var rootCmd = &cobra.Command{
+ Use: "osc",
+ Short: "Reads or writes the system clipboard using the ANSI OSC52 escape sequence",
+ Long: `Reads or writes the system clipboard using the ANSI OSC52 escape sequence.`,
+}
+
+func init() {
+ rootCmd.PersistentFlags().BoolVarP(&verboseFlag, "verbose", "v", false, "verbose logging")
+ rootCmd.PersistentFlags().StringVarP(&logfileFlag, "log", "l", "", "write logs to file")
+
+ rootCmd.AddCommand(copyCmd)
+ rootCmd.AddCommand(pasteCmd)
}
func main() {
- os.Exit(_main())
+ err := rootCmd.Execute()
+ if err != nil {
+ os.Exit(1)
+ }
}