/ home / bin / osc52
osc52
  1  #!/bin/sh
  2  # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
  3  # Use of this source code is governed by a BSD-style license that can be
  4  # found in the LICENSE file.
  5  # Max length of the OSC 52 sequence.  Sequences longer than this will not be
  6  # sent to the terminal.
  7  OSC_52_MAX_SEQUENCE="100000"
  8  # Write an error message and exit.
  9  # Usage: <message>
 10  die() {
 11    echo "ERROR: $*"
 12    exit 1
 13  }
 14  # Send a DCS sequence through tmux.
 15  # Usage: <sequence>
 16  tmux_dcs() {
 17    printf '\033Ptmux;\033%s\033\\' "$1"
 18  }
 19  # Send a DCS sequence through screen.
 20  # Usage: <sequence>
 21  screen_dcs() {
 22    # Screen limits the length of string sequences, so we have to break it up.
 23    # Going by the screen history:
 24    #   (v4.2.1) Apr 2014 - today: 768 bytes
 25    #   Aug 2008 - Apr 2014 (v4.2.0): 512 bytes
 26    #   ??? - Aug 2008 (v4.0.3): 256 bytes
 27    # Since v4.2.0 is only ~4 years old, we'll use the 256 limit.
 28    # We can probably switch to the 768 limit in 2022.
 29    local limit=256
 30    # We go 4 bytes under the limit because we're going to insert two bytes
 31    # before (\eP) and 2 bytes after (\e\) each string.
 32    echo "$1" | \
 33      sed -E "s:.{$(( limit - 4 ))}:&\n:g" | \
 34      sed -E -e 's:^:\x1bP:' -e 's:$:\x1b\\:' | \
 35      tr -d '\n'
 36  }
 37  # Send an escape sequence to hterm.
 38  # Usage: <sequence>
 39  print_seq() {
 40    local seq="$1"
 41    case ${TERM-} in
 42    screen*)
 43      # Since tmux defaults to setting TERM=screen (ugh), we need to detect
 44      # it here specially.
 45      if [ -n "${TMUX-}" ]; then
 46        tmux_dcs "${seq}"
 47      else
 48        screen_dcs "${seq}"
 49      fi
 50      ;;
 51    tmux*)
 52      tmux_dcs "${seq}"
 53      ;;
 54    *)
 55      echo "${seq}"
 56      ;;
 57    esac
 58  }
 59  # Base64 encode stdin.
 60  b64enc() {
 61    base64 | tr -d '\n'
 62  }
 63  # Send the OSC 52 sequence to copy the content.
 64  # Usage: [string]
 65  copy() {
 66    local str
 67    if [ $# -eq 0 ]; then
 68      str="$(b64enc)"
 69    else
 70      str="$(echo "$@" | b64enc)"
 71    fi
 72    local len=${#str}
 73    if [ ${len} -lt ${OSC_52_MAX_SEQUENCE} ]; then
 74      print_seq "$(printf '\033]52;c;%s\a' "${str}")"
 75    else
 76      die "selection too long to send to terminal:" \
 77        "${OSC_52_MAX_SEQUENCE} limit, ${len} attempted"
 78    fi
 79  }
 80  # Write tool usage and exit.
 81  # Usage: [error message]
 82  usage() {
 83    if [ $# -gt 0 ]; then
 84      exec 1>&2
 85    fi
 86    cat <<EOF
 87  Usage: osc52 [options] [string]
 88  Send an arbitrary string to the terminal clipboard using the OSC 52 escape
 89  sequence as specified in xterm:
 90    https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
 91    Section "Operating System Controls", Ps => 52.
 92  The data can either be read from stdin:
 93    $ echo "hello world" | osc52.sh
 94  Or specified on the command line:
 95    $ osc52.sh "hello world"
 96  EOF
 97    if [ $# -gt 0 ]; then
 98      echo
 99      die "$@"
100    else
101      exit 0
102    fi
103  }
104  main() {
105    set -e
106    while [ $# -gt 0 ]; do
107      case $1 in
108      -h|--help)
109        usage
110        ;;
111      -*)
112        usage "Unknown option: $1"
113        ;;
114      *)
115        break
116        ;;
117      esac
118    done
119    copy "$@"
120  }
121  main "$@"