diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml new file mode 100644 index 0000000..9d1b148 --- /dev/null +++ b/.github/workflows/goreleaser.yml @@ -0,0 +1,20 @@ +name: goreleaser + +on: + - push + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@master + - name: Set up Go + uses: actions/setup-go@master + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v1 + with: + version: latest + args: release --rm-dist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b1c8b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/dist diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..df71f76 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,20 @@ +builds: + - main: ./cmd/pbcopy + binary: pbcopy + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + goarch: + - amd64 +checksum: + name_template: 'pbcopy-checksums.txt' +archives: + - name_template: "pbcopy-{{ .Os }}-{{ .Arch }}" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' diff --git a/cmd/pbcopy/main.go b/cmd/pbcopy/main.go new file mode 100644 index 0000000..f98e4e6 --- /dev/null +++ b/cmd/pbcopy/main.go @@ -0,0 +1,108 @@ +package main + +import ( + "bytes" + "encoding/base64" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "strings" +) + +func normalEsc(b64 string) string { + return "\x1B]52;;" + b64 + "\x1B\x5C" +} + +func tmuxEsc(b64 string) string { + return "\x1BPtmux;\x1B\x1B]52;;" + b64 + "\x1B\x1B\x5C\x5C\x1B\x5C" +} + +func screenEsc(b64 string) string { + out := []string{} + for i := 0; ; i++ { + begin, end := i*76, (i+1)*76 + if end > len(b64) { + end = len(b64) + } + if begin == 0 { + out = append(out, "\x1BP\x1B]52;;"+b64[begin:end]) + } else { + out = append(out, "\x1B\x5C\x1BP"+b64[begin:end]) + } + if end == len(b64) { + break + } + } + out = append(out, "\x07\x1B\x5C") + return strings.Join(out, "") +} + +func isTmuxCC(pid string) bool { + out, err := exec.Command("ps", "-p", pid, "-o", "command=").Output() + if err != nil { + return false + } + out = bytes.TrimRight(out, "\n\r") + for _, argv := range strings.Split(string(out), " ") { + if argv == "-CC" { + return true + } + } + return false +} + +func chooseEsc() func(string) string { + if env := os.Getenv("TMUX"); env != "" { + envs := strings.Split(env, ",") + if len(envs) > 1 { + pid := envs[1] + if isTmuxCC(pid) { + return normalEsc + } + } + return tmuxEsc + } else if env := os.Getenv("TERM"); strings.HasPrefix(env, "screen") { + return screenEsc + } + return normalEsc +} + +func run() error { + var r io.Reader + switch len(os.Args) { + case 1: + r = os.Stdin + default: + if os.Args[1] == "-h" || os.Args[1] == "--help" { + fmt.Print("Usage:\n pbcopy FILE\n some-command | pbcopy\n") + os.Exit(1) + } + var err error + r, err = os.Open(os.Args[1]) + if err != nil { + return err + } + } + b, err := ioutil.ReadAll(r) + if err != nil { + return err + } + b = bytes.TrimRight(b, "\n\r") + if len(b) == 0 { + return nil + } + + esc := chooseEsc() + b64 := base64.RawStdEncoding.EncodeToString(b) + fmt.Print(esc(b64)) + return nil +} + +func main() { + if err := run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +}