From b0079afd80bd4f0106327c0b360f14d4ba169201 Mon Sep 17 00:00:00 2001 From: theimpostor Date: Sun, 16 Jul 2023 08:50:50 -0500 Subject: Fix paste for terminals ending with ESC-backslash instead of BEL, and add debug logging --- go.mod | 1 + go.sum | 2 + main.go | 155 ++++++++++++++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 120 insertions(+), 38 deletions(-) diff --git a/go.mod b/go.mod index 3e15e7b..340b5d1 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/rivo/uniseg v0.4.3 // indirect + golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect golang.org/x/term v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect ) diff --git a/go.sum b/go.sum index 3ef6b84..9a8e104 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= diff --git a/main.go b/main.go index 2afae80..8a0b1c1 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "flag" "fmt" "github.com/gdamore/tcell/v2" + "golang.org/x/exp/slog" "io" "log" "os" @@ -47,28 +48,63 @@ func closetty(tty tcell.Tty) { func main() { var fnames []string + var err error flag.Usage = func() { - template := `Usage: -%s [file1 [...fileN]] -Copies file contents to system clipboard using the ANSI OSC52 escape sequence. + 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]) + fmt.Fprintf(flag.CommandLine.Output(), template, os.Args[0], os.Args[0]) + flag.PrintDefaults() } - pasteFlag := flag.Bool("paste", false, "paste operation") + 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() - tty, err := opentty() - if err != nil { - fmt.Fprintln(os.Stderr, "ERROR: opentty:", err) - return + logLevel := &slog.LevelVar{} // INFO + logOutput := os.Stdout + + if logfileFlag != "" { + if logOutput, err = os.OpenFile(logfileFlag, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0644); err != nil { + log.Fatalf("Failed to open file %v: %v", logfileFlag, err) + } else { + defer logOutput.Close() + } } - defer closetty(tty) - if !*pasteFlag { + if verboseFlag { + logLevel.Set(slog.LevelDebug) + } + + logger := slog.New(slog.NewTextHandler(logOutput, &slog.HandlerOptions{ + Level: logLevel, + })) + slog.SetDefault(logger) + slog.Debug("logging started") + + if !pasteFlag { // copy if len(flag.Args()) > 0 { fnames = flag.Args() @@ -76,11 +112,19 @@ With no arguments, will read from stdin. fnames = []string{"-"} } + tty, err := opentty() + if err != nil { + slog.Error("ERROR: opentty:", err) + return + } + defer closetty(tty) + // Open buffered output, using default max OSC52 length as buffer size // TODO limit size out := bufio.NewWriterSize(tty, 1000000) // Start OSC52 + slog.Debug("Beginning osc52 copy operation") fmt.Fprintf(out, "\033]52;c;") b64 := base64.NewEncoder(base64.StdEncoding, out) @@ -93,36 +137,71 @@ With no arguments, will read from stdin. fmt.Fprintf(out, "\a") out.Flush() + slog.Debug("Ended osc52") } else { // paste - // Start OSC52 - fmt.Fprintf(tty, "\033]52;c;?\a") - - ttyReader := bufio.NewReader(tty) - - buf, err := ttyReader.ReadBytes('\a') - if err != nil { - fmt.Fprintln(os.Stderr, "Read error:", err) - return - } - - // fmt.Fprintf(os.Stderr, "Read %d bytes, %x\n", len(buf), buf) - // fmt.Fprintf(os.Stderr, "buf[:7]: %q\n", buf[:7]) - // fmt.Fprintf(os.Stderr, "buf[len(buf)-1]: %q\n", buf[len(buf)-1]) - // fmt.Fprintf(os.Stderr, "%x\n", buf) - buf = buf[7 : len(buf)-1] - // fmt.Fprintf(os.Stderr, "%x\n", buf) - - dst := make([]byte, base64.StdEncoding.DecodedLen(len(buf))) - n, err := base64.StdEncoding.Decode(dst, []byte(buf)) - if err != nil { - fmt.Fprintln(os.Stderr, "decode error:", err) - return - } - dst = dst[:n] - if _, err := os.Stdout.Write(dst); err != nil { - fmt.Fprintln(os.Stderr, "Error writing to stdout:", err) + data := func() []byte { + + tty, err := opentty() + if err != nil { + slog.Error("ERROR: opentty:", err) + return nil + } + defer closetty(tty) + + // Start OSC52 + slog.Debug("Beginning osc52 paste operation") + fmt.Fprintf(tty, "\033]52;c;?\a") + + ttyReader := bufio.NewReader(tty) + buf := make([]byte, 0, 1024) + for { + if b, err := ttyReader.ReadByte(); err != nil { + slog.Error("ReadByte error:", err) + return nil + } else { + slog.Debug(fmt.Sprintf("Read: %x '%s'", b, string(b))) + if b == '\a' { + break + } + buf = append(buf, b) + if len(buf) > 2 && buf[len(buf)-2] == '\033' && buf[len(buf)-1] == '\\' { + buf = buf[:len(buf)-2] + break + } + } + } + + // buf, err := ttyReader.ReadBytes('\a') + // if err != nil { + // slog.Error("Read error:", err) + // return nil + // } + + // slog.Debug("Read %d bytes, %x\n", len(buf), buf) + // fmt.Fprintf(os.Stderr, "buf[:7]: %q\n", buf[:7]) + // fmt.Fprintf(os.Stderr, "buf[len(buf)-1]: %q\n", buf[len(buf)-1]) + // fmt.Fprintf(os.Stderr, "%x\n", buf) + buf = buf[7:] + // fmt.Fprintf(os.Stderr, "%x\n", buf) + + dst := make([]byte, base64.StdEncoding.DecodedLen(len(buf))) + n, err := base64.StdEncoding.Decode(dst, []byte(buf)) + if err != nil { + slog.Error("decode error:", err) + return nil + } + dst = dst[:n] + + return dst + }() + + if data != nil { + if _, err := os.Stdout.Write(data); err != nil { + slog.Error("Error writing to stdout:", err) + } } + slog.Debug("Ended osc52") } } -- cgit v1.2.3