pax_global_header 0000666 0000000 0000000 00000000064 14401377222 0014514 g ustar 00root root 0000000 0000000 52 comment=ce73587a0f72cf077e55f163e71ea1b5496a133c go-osc52-2.0.1/ 0000775 0000000 0000000 00000000000 14401377222 0013052 5 ustar 00root root 0000000 0000000 go-osc52-2.0.1/.github/ 0000775 0000000 0000000 00000000000 14401377222 0014412 5 ustar 00root root 0000000 0000000 go-osc52-2.0.1/.github/workflows/ 0000775 0000000 0000000 00000000000 14401377222 0016447 5 ustar 00root root 0000000 0000000 go-osc52-2.0.1/.github/workflows/build.yml 0000664 0000000 0000000 00000001453 14401377222 0020274 0 ustar 00root root 0000000 0000000 name: build on: push: pull_request: branches: - master jobs: build: strategy: matrix: go-version: [^1] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} env: GO111MODULE: "on" steps: - name: Checkout code uses: actions/checkout@v3 - name: Install Go uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} - uses: actions/cache@v3 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - name: Download Go modules run: go mod download - name: Build run: go build -v ./... - name: Test run: go test ./... go-osc52-2.0.1/LICENSE 0000664 0000000 0000000 00000002056 14401377222 0014062 0 ustar 00root root 0000000 0000000 MIT License Copyright (c) 2022 Ayman Bagabas Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. go-osc52-2.0.1/README.md 0000664 0000000 0000000 00000004502 14401377222 0014332 0 ustar 00root root 0000000 0000000 # go-osc52
A Go library to work with the [ANSI OSC52](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands) terminal sequence. ## Usage You can use this small library to construct an ANSI OSC52 sequence suitable for your terminal. ### Example ```go import ( "os" "fmt" "github.com/aymanbagabas/go-osc52/v2" ) func main() { s := "Hello World!" // Copy `s` to system clipboard osc52.New(s).WriteTo(os.Stderr) // Copy `s` to primary clipboard (X11) osc52.New(s).Primary().WriteTo(os.Stderr) // Query the clipboard osc52.Query().WriteTo(os.Stderr) // Clear system clipboard osc52.Clear().WriteTo(os.Stderr) // Use the fmt.Stringer interface to copy `s` to system clipboard fmt.Fprint(os.Stderr, osc52.New(s)) // Or to primary clipboard fmt.Fprint(os.Stderr, osc52.New(s).Primary()) } ``` ## SSH Example You can use this over SSH using [gliderlabs/ssh](https://github.com/gliderlabs/ssh) for instance: ```go var sshSession ssh.Session seq := osc52.New("Hello awesome!") // Check if term is screen or tmux pty, _, _ := s.Pty() if pty.Term == "screen" { seq = seq.Screen() } else if isTmux { seq = seq.Tmux() } seq.WriteTo(sshSession.Stderr()) ``` ## Tmux Make sure you have `set-clipboard on` in your config, otherwise, tmux won't allow your application to access the clipboard [^1]. Using the tmux option, `osc52.TmuxMode` or `osc52.New(...).Tmux()`, wraps the OSC52 sequence in a special tmux DCS sequence and pass it to the outer terminal. This requires `allow-passthrough on` in your config. `allow-passthrough` is no longer enabled by default [since tmux 3.3a](https://github.com/tmux/tmux/issues/3218#issuecomment-1153089282) [^2]. [^1]: See [tmux clipboard](https://github.com/tmux/tmux/wiki/Clipboard) [^2]: [What is allow-passthrough](https://github.com/tmux/tmux/wiki/FAQ#what-is-the-passthrough-escape-sequence-and-how-do-i-use-it) ## Credits * [vim-oscyank](https://github.com/ojroques/vim-oscyank) this is heavily inspired by vim-oscyank. go-osc52-2.0.1/_examples/ 0000775 0000000 0000000 00000000000 14401377222 0015027 5 ustar 00root root 0000000 0000000 go-osc52-2.0.1/_examples/local/ 0000775 0000000 0000000 00000000000 14401377222 0016121 5 ustar 00root root 0000000 0000000 go-osc52-2.0.1/_examples/local/main.go 0000664 0000000 0000000 00000000234 14401377222 0017373 0 ustar 00root root 0000000 0000000 package main import ( "fmt" "github.com/aymanbagabas/go-osc52" ) func main() { str := "hello world" osc52.Copy(str) fmt.Printf("Copied %q!", str) } go-osc52-2.0.1/_examples/ssh/ 0000775 0000000 0000000 00000000000 14401377222 0015624 5 ustar 00root root 0000000 0000000 go-osc52-2.0.1/_examples/ssh/main.go 0000664 0000000 0000000 00000001447 14401377222 0017105 0 ustar 00root root 0000000 0000000 package main import ( "fmt" "log" "github.com/aymanbagabas/go-osc52" "github.com/charmbracelet/wish" "github.com/gliderlabs/ssh" ) func main() { s, err := wish.NewServer( wish.WithAddress(":2222"), wish.WithHostKeyPath("ssh_host_key"), wish.WithMiddleware( middleware(), ), ) if err != nil { log.Fatal(err) } fmt.Printf("SSH into %s\n", s.Addr) s.ListenAndServe() } func middleware() wish.Middleware { return func(h ssh.Handler) ssh.Handler { return func(s ssh.Session) { environ := s.Environ() pty, _, _ := s.Pty() // Put TERM environment variable into environ. environ = append(environ, fmt.Sprintf("TERM=%s", pty.Term)) out := osc52.NewOutput(s, environ) str := "hello world" out.Copy(str) s.Write([]byte(fmt.Sprintf("Copied %q!\n", str))) } } } go-osc52-2.0.1/go.mod 0000775 0000000 0000000 00000000064 14401377222 0014163 0 ustar 00root root 0000000 0000000 module github.com/aymanbagabas/go-osc52/v2 go 1.16 go-osc52-2.0.1/go.sum 0000664 0000000 0000000 00000000000 14401377222 0014173 0 ustar 00root root 0000000 0000000 go-osc52-2.0.1/osc52.go 0000775 0000000 0000000 00000017452 14401377222 0014350 0 ustar 00root root 0000000 0000000 // OSC52 is a terminal escape sequence that allows copying text to the clipboard. // // The sequence consists of the following: // // OSC 52 ; Pc ; Pd BEL // // Pc is the clipboard choice: // // c: clipboard // p: primary // q: secondary (not supported) // s: select (not supported) // 0-7: cut-buffers (not supported) // // Pd is the data to copy to the clipboard. This string should be encoded in // base64 (RFC-4648). // // If Pd is "?", the terminal replies to the host with the current contents of // the clipboard. // // If Pd is neither a base64 string nor "?", the terminal clears the clipboard. // // See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands // where Ps = 52 => Manipulate Selection Data. // // Examples: // // // copy "hello world" to the system clipboard // fmt.Fprint(os.Stderr, osc52.New("hello world")) // // // copy "hello world" to the primary Clipboard // fmt.Fprint(os.Stderr, osc52.New("hello world").Primary()) // // // limit the size of the string to copy 10 bytes // fmt.Fprint(os.Stderr, osc52.New("0123456789").Limit(10)) // // // escape the OSC52 sequence for screen using DCS sequences // fmt.Fprint(os.Stderr, osc52.New("hello world").Screen()) // // // escape the OSC52 sequence for Tmux // fmt.Fprint(os.Stderr, osc52.New("hello world").Tmux()) // // // query the system Clipboard // fmt.Fprint(os.Stderr, osc52.Query()) // // // query the primary clipboard // fmt.Fprint(os.Stderr, osc52.Query().Primary()) // // // clear the system Clipboard // fmt.Fprint(os.Stderr, osc52.Clear()) // // // clear the primary Clipboard // fmt.Fprint(os.Stderr, osc52.Clear().Primary()) package osc52 import ( "encoding/base64" "fmt" "io" "strings" ) // Clipboard is the clipboard buffer to use. type Clipboard rune const ( // SystemClipboard is the system clipboard buffer. SystemClipboard Clipboard = 'c' // PrimaryClipboard is the primary clipboard buffer (X11). PrimaryClipboard = 'p' ) // Mode is the mode to use for the OSC52 sequence. type Mode uint const ( // DefaultMode is the default OSC52 sequence mode. DefaultMode Mode = iota // ScreenMode escapes the OSC52 sequence for screen using DCS sequences. ScreenMode // TmuxMode escapes the OSC52 sequence for tmux. Not needed if tmux // clipboard is set to `set-clipboard on` TmuxMode ) // Operation is the OSC52 operation. type Operation uint const ( // SetOperation is the copy operation. SetOperation Operation = iota // QueryOperation is the query operation. QueryOperation // ClearOperation is the clear operation. ClearOperation ) // Sequence is the OSC52 sequence. type Sequence struct { str string limit int op Operation mode Mode clipboard Clipboard } var _ fmt.Stringer = Sequence{} var _ io.WriterTo = Sequence{} // String returns the OSC52 sequence. func (s Sequence) String() string { var seq strings.Builder // mode escape sequences start seq.WriteString(s.seqStart()) // actual OSC52 sequence start seq.WriteString(fmt.Sprintf("\x1b]52;%c;", s.clipboard)) switch s.op { case SetOperation: str := s.str if s.limit > 0 && len(str) > s.limit { return "" } b64 := base64.StdEncoding.EncodeToString([]byte(str)) switch s.mode { case ScreenMode: // Screen doesn't support OSC52 but will pass the contents of a DCS // sequence to the outer terminal unchanged. // // Here, we split the encoded string into 76 bytes chunks and then // join the chunks with