From ece099b6a49c1c7c75830ec4edf382fb6ec02658 Mon Sep 17 00:00:00 2001 From: Eragos Date: Fri, 18 Oct 2024 22:19:09 +0200 Subject: [PATCH] update --- .iterm2/imgcat | 209 ++++++++++--- .iterm2/imgls | 678 +++++++++++++++++++++++++++++++++++++---- .iterm2/it2attention | 17 +- .iterm2/it2check | 34 ++- .iterm2/it2copy | 60 +++- .iterm2/it2dl | 68 ++++- .iterm2/it2getvar | 180 ++++++----- .iterm2/it2setcolor | 6 +- .iterm2/it2setkeylabel | 10 +- .iterm2/it2ul | 17 +- .iterm2/it2universion | 6 +- 11 files changed, 1056 insertions(+), 229 deletions(-) diff --git a/.iterm2/imgcat b/.iterm2/imgcat index 001d2b8..39dd229 100755 --- a/.iterm2/imgcat +++ b/.iterm2/imgcat @@ -1,10 +1,13 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -o pipefail # tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; # ST, and for all ESCs in to be replaced with ESC ESC. It -# only accepts ESC backslash for ST. +# only accepts ESC backslash for ST. We use TERM instead of TMUX because TERM +# gets passed through ssh. function print_osc() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]]; then printf "\033Ptmux;\033\033]" else printf "\033]" @@ -13,53 +16,131 @@ function print_osc() { # More of the tmux workaround described above. function print_st() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]]; then printf "\a\033\\" else printf "\a" fi } -# print_image filename inline base64contents print_filename +function load_version() { + if [ -z ${IMGCAT_BASE64_VERSION+x} ]; then + IMGCAT_BASE64_VERSION=$(base64 --version 2>&1) + export IMGCAT_BASE64_VERSION + fi +} + +function b64_encode() { + load_version + if [[ $IMGCAT_BASE64_VERSION =~ GNU ]]; then + # Disable line wrap + base64 -w0 + else + base64 + fi +} + +function b64_decode() { + load_version + if [[ $IMGCAT_BASE64_VERSION =~ fourmilab ]]; then + BASE64ARG=-d + elif [[ $IMGCAT_BASE64_VERSION =~ GNU ]]; then + BASE64ARG=-di + else + BASE64ARG=-D + fi + base64 $BASE64ARG +} + +# print_image filename inline base64contents print_filename width height preserve_aspect_ratio # filename: Filename to convey to client -# inline: 0 or 1 +# inline: 0 or 1, if set to 1, the file will be displayed inline, otherwise, it will be downloaded # base64contents: Base64-encoded contents -# print_filename: If non-empty, print the filename -# before outputting the image +# print_filename: 0 or 1, if set to 1, print the filename after outputting the image +# width: set output width of the image in character cells, pixels or percent +# height: set output height of the image in character cells, pixels or percent +# preserve_aspect_ratio: 0 or 1, if set to 1, fill the specified width and height as much as possible without stretching the image +# file: Empty string or file type like "application/json" or ".js". function print_image() { print_osc - printf '1337;File=' - if [[ -n "$1" ]]; then - printf 'name='`printf "%s" "$1" | base64`";" - fi - - VERSION=$(base64 --version 2>&1) - if [[ "$VERSION" =~ fourmilab ]]; then - BASE64ARG=-d - elif [[ "$VERSION" =~ GNU ]]; then - BASE64ARG=-di - else - BASE64ARG=-D - fi - - printf "%s" "$3" | base64 $BASE64ARG | wc -c | awk '{printf "size=%d",$1}' - printf ";inline=$2" - printf ":" - printf "%s" "$3" + printf "1337;File=inline=%s" "$2" + printf ";size=%d" $(printf "%s" "$3" | b64_decode | wc -c) + [ -n "$1" ] && printf ";name=%s" "$(printf "%s" "$1" | b64_encode)" + [ -n "$5" ] && printf ";width=%s" "$5" + [ -n "$6" ] && printf ";height=%s" "$6" + [ -n "$7" ] && printf ";preserveAspectRatio=%s" "$7" + [ -n "$8" ] && printf ";type=%s" "$8" + printf ":%s" "$3" print_st printf '\n' - if [[ -n "$4" ]]; then - echo $1 - fi + [ "$4" == "1" ] && echo "$1" + has_image_displayed=t } function error() { - echo "ERROR: $*" 1>&2 + errcho "ERROR: $*" +} + +function errcho() { + echo "$@" >&2 } function show_help() { - echo "Usage: imgcat [-p] filename ..." 1>& 2 - echo " or: cat filename | imgcat" 1>& 2 + errcho + errcho "Usage: imgcat [-p] [-n] [-W width] [-H height] [-r] [-s] [-u] [-t file-type] [-f] filename ..." + errcho " cat filename | imgcat [-W width] [-H height] [-r] [-s]" + errcho + errcho "Display images inline in the iTerm2 using Inline Images Protocol" + errcho + errcho "Options:" + errcho + errcho " -h, --help Display help message" + errcho " -p, --print Enable printing of filename or URL after each image" + errcho " -n, --no-print Disable printing of filename or URL after each image" + errcho " -u, --url Interpret following filename arguments as remote URLs" + errcho " -f, --file Interpret following filename arguments as regular Files" + errcho " -t, --type file-type Provides a type hint" + errcho " -r, --preserve-aspect-ratio When scaling image preserve its original aspect ratio" + errcho " -s, --stretch Stretch image to specified width and height (this option is opposite to -r)" + errcho " -W, --width N Set image width to N character cells, pixels or percent (see below)" + errcho " -H, --height N Set image height to N character cells, pixels or percent (see below)" + errcho + errcho " If you don't specify width or height an appropriate value will be chosen automatically." + errcho " The width and height are given as word 'auto' or number N followed by a unit:" + errcho " N character cells" + errcho " Npx pixels" + errcho " N% percent of the session's width or height" + errcho " auto the image's inherent size will be used to determine an appropriate dimension" + errcho + errcho " If a type is provided, it is used as a hint to disambiguate." + errcho " The file type can be a mime type like text/markdown, a language name like Java, or a file extension like .c" + errcho " The file type can usually be inferred from the extension or its contents. -t is most useful when" + errcho " a filename is not available, such as whe input comes from a pipe." + errcho + errcho "Examples:" + errcho + errcho " $ imgcat -W 250px -H 250px -s avatar.png" + errcho " $ cat graph.png | imgcat -W 100%" + errcho " $ imgcat -p -W 500px -u http://host.tld/path/to/image.jpg -W 80 -f image.png" + errcho " $ cat url_list.txt | xargs imgcat -p -W 40 -u" + errcho " $ imgcat -t application/json config.json" + errcho +} + +function check_dependency() { + if ! (builtin command -V "$1" >/dev/null 2>&1); then + error "missing dependency: can't find $1" + exit 1 + fi +} + +# verify that value is in the image sizing unit format: N / Npx / N% / auto +function validate_size_unit() { + if [[ ! "$1" =~ ^(:?[0-9]+(:?px|%)?|auto)$ ]]; then + error "Invalid image sizing unit - '$1'" + show_help + exit 1 + fi } ## Main @@ -71,34 +152,76 @@ else fi # Show help if no arguments and no stdin. -if [ $has_stdin = f -a $# -eq 0 ]; then +if [ $has_stdin = f ] && [ $# -eq 0 ]; then show_help exit fi +check_dependency base64 +check_dependency wc +file_type="" + # Look for command line flags. while [ $# -gt 0 ]; do case "$1" in - -h|--h|--help) + -h | --h | --help) show_help exit ;; - -p|--p|--print) + -p | --p | --print) print_filename=1 ;; + -n | --n | --no-print) + print_filename=0 + ;; + -W | --W | --width) + validate_size_unit "$2" + width="$2" + shift + ;; + -H | --H | --height) + validate_size_unit "$2" + height="$2" + shift + ;; + -r | --r | --preserve-aspect-ratio) + preserve_aspect_ratio=1 + ;; + -s | --s | --stretch) + preserve_aspect_ratio=0 + ;; + -f | --f | --file) + has_stdin=f + is_url=f + ;; + -u | --u | --url) + check_dependency curl + has_stdin=f + is_url=t + ;; + -t | --t | --type) + file_type="$2" + shift + ;; -*) error "Unknown option flag: $1" show_help exit 1 - ;; + ;; *) - if [ -r "$1" ] ; then - has_stdin=f - print_image "$1" 1 "$(base64 < "$1")" "$print_filename" + if [ "$is_url" == "t" ]; then + encoded_image=$(curl -fs "$1" | b64_encode) || { + error "Could not retrieve image from URL $1, error_code: $?" + exit 2 + } + elif [ -r "$1" ]; then + encoded_image=$(cat "$1" | b64_encode) else error "imgcat: $1: No such file or directory" exit 2 fi + has_stdin=f + print_image "$1" 1 "$encoded_image" "$print_filename" "$width" "$height" "$preserve_aspect_ratio" "$file_type" ;; esac shift @@ -106,7 +229,13 @@ done # Read and print stdin if [ $has_stdin = t ]; then - print_image "" 1 "$(cat | base64)" "" + print_image "" 1 "$(cat | b64_encode)" 0 "$width" "$height" "$preserve_aspect_ratio" "$file_type" +fi + +if [ "$has_image_displayed" != "t" ]; then + error "No image provided. Check command line options." + show_help + exit 1 fi exit 0 diff --git a/.iterm2/imgls b/.iterm2/imgls index e116606..49a0b9b 100755 --- a/.iterm2/imgls +++ b/.iterm2/imgls @@ -1,68 +1,624 @@ -#!/bin/bash -# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; -# ST, and for all ESCs in to be replaced with ESC ESC. It -# only accepts ESC backslash for ST. -function print_osc() { - if [ x"$TERM" = "xscreen" ] ; then - printf "\033Ptmux;\033\033]" - else - printf "\033]" - fi +#!/usr/bin/perl + +# imgls: ls(1) listing supplemented with image thumbnails and dimensions +# +# Usage: imgls [--width #] [--height #] [--[no]preserve_ratio] +# [--[no]dimensions] [--[no]unknown] [ls options] [file ...] +# +# Writing an image to an iTerm window is simple. See the official documentation +# at https://iterm2.com/documentation-images.html and the write_image() function below. +# +# Many of ls' options are supported, but not all. The program does not support ls' +# default -C columnar output mode - output is always one entry per line. Writing +# images across the page in columns appears to be problematic. +# +# In addition, options are available to specify setting image properties (width, height, +# preserve aspect ratio), include inline image dimensions, and disable output of +# generic icons for unsupported image types. Finally, a table-based image dimensions +# lookup mechanism is employed to obtain image dimensions. It can call on Perl +# modules such as Image::Size, or call on external programs such as sips, mdls or php. +# It is easy to add additional entries to the table. You can use the --method option +# to select any of the currently supported methods ('sips', 'mdls', 'php', and +# 'image::size'). These are tried, in that order; the first that appears to work +# is used for all. + +use v5.14; +use strict; +use utf8; +use warnings; + +use File::stat; +use IO::Select; +use IPC::Open3; +use File::Spec; +use MIME::Base64; +use File::Basename; +use Symbol 'gensym'; +use Encode qw(decode); +use List::Util qw(max); +use POSIX qw(strftime floor modf); +use Getopt::Long qw(:config no_permute pass_through require_order); + +STDERR->autoflush(1); +STDOUT->autoflush(1); +binmode STDOUT, ':encoding(UTF-8)'; +binmode STDERR, ':encoding(UTF-8)'; + +my $prog = basename $0; +sub usage { + my $leader = "usage: $prog"; + say STDERR + "usage: $prog", + " [--width #] [--height #] [--[no]preserve_ratio] [--[no]dimensions]", + "\n", + ' ' x length($leader), + " [--[no]unknown] [ls options] [file ...]"; + + exit shift; } -# More of the tmux workaround described above. -function print_st() { - if [ x"$TERM" = "xscreen" ] ; then - printf "\a\033\\" - else - printf "\a" - fi +# Some defaults +my $def_image_width = 3; # height (in character cells) +my $def_image_height = 1; # width (in character cells) +my $def_preserve_ratio = 'true'; # preserve aspect ratio + +my %imgparams = ( # default image option parameters + inline => 1, # image appears inline with text + height => $def_image_height, # character cells tall + width => $def_image_width, # character cells wide + preserveAspectRatio => $def_preserve_ratio, # no stretchmarks please +); + +my %failed_types; # cache of file extensions that have failed +my %stat_cache; # cache of file/directory lstat() calls +my $curtime = time(); +my $sixmonths = (365 / 2) * 86400; + +my %opts = ( + height => \$imgparams{'height'}, + width => \$imgparams{'width'}, +); +get_options(\%opts); + +# find a method to obtain image dimensions +my $dims_methods = init_dims_methods(); +my $dims_method = find_dims_methods($dims_methods); + +# single pixel image for non-renderable files +my ($one_pixel_black, $one_pixel_black_len) = get_black_pixel_image(); + +my $do_newline; +my $dot_filter = $opts{'A'} ? qr/^\.{1,2}$/ : qr/^\./; +$dot_filter = undef if $opts{'a'}; + +# special: empty @ARGV, or contains only '.' +my $do_header = @ARGV > 1 ? 1 : 0; +if (@ARGV <= 1) { + push @ARGV, '.' if @ARGV == 0; } -function list_file() { - fn=$1 - dims=$(php -r 'if (!is_file($argv[1])) exit(1); $a = getimagesize($argv[1]); if ($a==FALSE) exit(1); else { echo $a[0] . "x" .$a[1]; exit(0); }' "$fn") - rc=$? - if [[ $rc == 0 ]] ; then - print_osc - printf '1337;File=name='`echo -n "$fn" | base64`";" - wc -c "$fn" | awk '{printf "size=%d",$1}' - printf ";inline=1;height=3;width=3;preserveAspectRatio=true" - printf ":" - base64 < "$fn" - print_st - if [ x"$TERM" == "xscreen" ] ; then - # This works in plain-old tmux but does the wrong thing in iTerm2's tmux - # integration mode. tmux doesn't know that the cursor moves when the - # image code is sent, while iTerm2 does. I had to pick one, since - # integration mode is undetectable, so I picked the failure mode that at - # least produces useful output (there is just too much whitespace in - # integration mode). This could be fixed by not moving the cursor while - # in integration mode. A better fix would be for tmux to interpret the - # image sequence, though. - # - # tl;dr: If you use tmux in integration mode, replace this with the printf - # from the else clause. - printf '\033[4C\033[Bx' - else - printf '\033[A' - fi - echo -n "$dims " - ls -ld "$fn" - else - ls -ld "$fn" - fi +my (@files, @dirs); +for (@ARGV) { + if (! -e _lstat($_)) { + say STDERR "$prog: $_: No such file or directory"; + } + elsif (-f _lstat($_)) { + push @files, $_; + } + else { + push @dirs, $_; + } +} +@files = ls_sort(@files); +@dirs = ls_sort(@dirs); + +if ($opts{'d'}) { + push @files, @dirs; + @dirs = (); +} +do_ls(undef, @files) if @files; + +while (@dirs) { + my $path = shift @dirs; + + if (! -e $path) { + say STDERR "$prog: $path: No such file or directory"; + next; + } + my (@f, @d); + get_dir_content($path, $dot_filter, \@f, \@d) or + next; + do_ls($path, @f, @d); + if ($opts{'R'}) { + push @dirs, grep { ! /\.\.?$/ } @d; + } + $do_newline++; } -if [ $# -eq 0 ]; then - for fn in * - do - list_file "$fn" - done < <(ls -ls) -else - for fn in "$@" - do - list_file "$fn" - done -fi +# Encodes and outputs an image file to the window +# arg 1: path to an image file +# arg 2: size, in bytes, of the image +sub write_image { + my ($file, $size) = @_; + my $encoded; + $imgparams{'name'} = encode_base64($file, ''); # file name is base64 encoded + $imgparams{'size'} = $size; # file size in bytes + + if (ref $file eq '') { + my $bytes = get_image_bytes($file); + $encoded = encode_base64($bytes) if defined $bytes; + } + if (! $encoded or ref $file eq 'SCALAR') { + $encoded = $one_pixel_black; + $imgparams{'name'} = encode_base64('one_pixel_black', ''); + $imgparams{'size'} = $one_pixel_black_len; + } + + printf "%s%s%s;File=%s:%s%s", + "\033", "]", "1337", # image leadin sequence (OSC + 1337) + join(';', map { $_ . '=' . $imgparams{$_} } keys %imgparams), # semicolon separated pairs of param=value pairs + $encoded, # base64 encoded image bytes + "\007"; # end-of-encoding char (BEL = ^G) +} + +sub get_options { + local $SIG{__WARN__} = sub { say "$prog: ", $_[0]; usage(1) }; + Getopt::Long::Configure(qw/no_ignore_case bundling no_passthrough/); + my $result = GetOptions(\%opts, + 'dimensions!' => sub { $opts{'unknown'}++; $opts{$_[0]} = $_[1] }, + 'height=s', + 'method=s', # use to force dimensions method + "preserve_ratio!" => sub { $imgparams{'preserveAspectRatio'} = $_[1] ? 'true' : 'false' }, + 'unknown!' => sub { $opts{'dimensions'}++; $opts{$_[0]} = $_[1] }, + 'width=s', + # supported ls options + 'D=s', + 't' => sub { delete $opts{'S'}; $opts{'t'}++ }, + 'S' => sub { delete $opts{'t'}; $opts{'S'}++ }, + qw/ A F R T a d h i k l n o p r s u y /, 'c|U', + ); + + $opts{'d'} and delete $opts{'R'}; + $opts{'D'} and delete $opts{'T'}; + $opts{'n'} and $opts{'l'}++; + $opts{'o'} and $opts{'l'}++; + $opts{'s'} and $opts{'show_blocks'}++; +} + +sub get_dir_content { + my ($path, $filter, $filesref, $dirsref) = @_; + my $dh; + + unless (opendir($dh, $path)) { + say STDERR "Unable to open directory $path: $!"; + return undef; + } + + while (readdir($dh)) { + next if defined $filter and $_ =~ /$filter/; + my $p = "$path/$_"; + if (-d _lstat($p)) { + push @$dirsref, $p; + } + else { + push @$filesref, $p; + } + } + closedir $dh; + return 1; +} + +sub do_ls { + my $path = shift; + + my $blocks_total = 0; + my (@hfiles, %widths, $st); + for my $file (ls_sort(@_)) { + #say "FILE: $file"; + + my %h; + $h{'file'} = $file; + $h{'filename'} = defined $path ? (split /\//, $file)[-1] : $file; + $h{'st'} = $st = _lstat($file); + $h{'ino'} = $st->ino if $opts{'i'}; + $h{'bytes'} = $st->size; + $h{'bytesh'} = format_human($h{'bytes'}) if $opts{'h'}; + $h{'dims'} = get_dimensions($file) if $opts{'dimensions'} and -f $st && -r $st && $h{'bytes'}; + $h{'nlink'} = $st->nlink if $opts{'s'} or $opts{'l'}; + + if ($opts{'show_blocks'} or $opts{'l'}) { + $h{'blocks'} = $st->blocks; + if ( ! -d $st and ($opts{'a'} or $h{'filename'} !~ /^\.[^.]+/)) { + $blocks_total += $st->blocks; + } + } + + if ($opts{'l'}) { + $h{'lsmodes'} = Stat::lsMode::format_mode($st->mode); + $h{'owner'} = ($opts{'n'} ? $st->uid : getpwuid $st->uid) // $st->uid; + $h{'group'} = ($opts{'n'} ? $st->gid : getgrgid $st->gid) // $st->gid if not $opts{'o'}; + $h{'time'} = format_time($opts{'c'} ? $st->ctime : $st->mtime); + } + push @hfiles, \%h; + + $widths{'dim_w'} = max(defined $h{'dims'} ? length($h{'dims'}{'width'}) : 0, $widths{'dim_w'} // 0); + $widths{'dim_h'} = max(defined $h{'dims'} ? length($h{'dims'}{'height'}) : 0, $widths{'dim_h'} // 0); + for my $key (qw/blocks ino bytes bytesh owner group nlink/) { + $widths{$key} = max(length($h{$key}), $widths{$key} // 0) if exists $h{$key}; + } + } + + # Header output when @ARGV was > 1, or after second dir + print "\n" if $path and $do_newline; + print "$path:\n" if $path and $do_header++; + + # total blocks inline when -d, as header when -l or -s + say "total ", $blocks_total / ($opts{'k'} ? 2 : 1) if $path and ! $opts{'d'} and ($opts{'show_blocks'} or $opts{'l'}); + + for my $h (@hfiles) { + if (! -f $h->{'st'} or ! $h->{'bytes'} or ($opts{'dimensions'} and ! $h->{'dims'} and ! $opts{'unknown'})) { + # pass a ref to indicate the data is already base64 encoded + write_image \$one_pixel_black, $one_pixel_black_len; + } + else { + write_image $h->{'file'}, $h->{'bytes'}; + } + + if ($opts{'dimensions'}) { + if ($widths{'dim_w'} or $widths{'dim_h'}) { + my $min_w = $widths{'dim_w'} // 1; + my $min_h = $widths{'dim_h'} // 1; + if ($h->{'dims'}{'width'} or $h->{'dims'}{'height'}) { + printf " [%*d x %*d] ", $min_w, $h->{'dims'}{'width'} // 0, $min_h, $h->{'dims'}{'height'} // 0; + } + else { + printf " %*s %*s ", $min_w, ' ', $min_h, ' '; + } + } + } + + printf " %*d", $widths{'ino'}, $h->{'ino'} if $opts{'i'}; + printf " %*d", $widths{'blocks'}, $h->{'blocks'} if $opts{'s'}; + printf " %s", $h->{'lsmodes'} if exists $h->{'lsmodes'}; + printf " %*s", $widths{'nlink'}, $h->{'nlink'} if exists $h->{'nlink'}; + printf " %*s", $widths{'owner'}, $h->{'owner'} if exists $h->{'owner'}; + printf " %*s", $widths{'group'}, $h->{'group'} if exists $h->{'group'}; + if ($opts{'l'}) { + printf " %*d", $widths{'bytes'}, $h->{'bytes'} if ! $opts{'h'}; + printf " %4s", $h->{'bytesh'} if $opts{'h'}; + } + printf " %s", $h->{'time'} if exists $h->{'time'}; + print " ", Encode::decode('UTF-8', defined $path ? (split /\//, $h->{'file'})[-1] : $h->{'file'}); + printf "%s", get_F_type($h->{'st'}) if $opts{'F'} or $opts{'p'}; + print "\n"; + } +} + +# Get the image's dimensions to supplement the image and ls output. +sub get_dimensions { + my $file = shift; + + my ($ret, $ext); + $file =~ /\.([^.]+)$/ and $ext = $1; + + if ($dims_method and (!$ext or ($ext and ! exists $failed_types{$ext}))) { + if (ref $dims_method->{'prog'} eq 'CODE') { + $ret = $dims_method->{'format'}->($file); + } + else { + my ($stdout, $stderr, $exit) = runcmd($dims_method->{'prog'}, @{$dims_method->{'args'}}, $file); + if ($stdout) { + $ret = $dims_method->{'format'}->($stdout); + } + } + } + + $failed_types{$ext}++ if ! $ret and $ext; + return $ret; +} + +sub runcmd { + my $prog = shift; + + my $pid = open3(my $in, my $out, my $err = gensym, $prog, @_); + + my ($out_buf, $err_buf) = ('', ''); + my $select = new IO::Select; + $select->add($out, $err); + while (my @ready = $select->can_read(5)) { + foreach my $fh (@ready) { + my $data; + my $bytes = sysread($fh, $data, 1024); + if (! defined( $bytes) && ! $!{ECONNRESET}) { + die "error running cmd: $prog: $!"; + } + elsif (! defined $bytes or $bytes == 0) { + $select->remove($fh); + next; + } + else { + if ($fh == $out) { $out_buf .= $data; } + elsif ($fh == $err) { $err_buf .= $data; } + else { + die 'unexpected filehandle in runcmd'; + } + } + } + } + + waitpid($pid, 0); + return ($out_buf, $err_buf, $? >> 8); +} + +# List of methods to obtain image dimensions, tried in prioritized order. +# Can be external programs or perl module. Expected to return undef when no +# dimensions can be found, or a hash ref with 'width' and 'height' elements. +sub init_dims_methods { + return [ + { + prog => 'sips', + args => [ '-g', 'pixelWidth', '-g', 'pixelHeight' ], + format => sub { + my $out = shift; + return ($out =~ /pixelWidth: (\d+)\s+pixelHeight: (\d+)/s) ? { width => $1, height => $2 } : undef; + } + }, + + { + prog => 'mdls', + args => [ '-name', 'kMDItemPixelWidth', '-name', 'kMDItemPixelHeight' ], + format => sub { + my $out = shift; + my %dim; + for my $d (qw /Width Height/) { + $dim{$d} = $1 if $out =~ /kMDItemPixel$d\s*=\s*(\d+)$/m; + } + return ($dim{'Width'} and $dim{'Height'}) ? { width => $dim{'Width'}, height => $dim{'Height'} } : undef; + } + }, + + { + prog => 'php', + args => [ '-r', q/$a = getimagesize("$argv[1]"); if ($a==FALSE) exit(1); else { echo $a[0] . "x" .$a[1]; exit(0); }/ ], + format => sub { + my $out = shift; + return undef unless $out; + my @d = split /x/, $out; + return { width => $d[0], height => $d[1] }; + } + }, + + { + prog => 'exiftool', + args => [ '-s', '-ImageSize' ], + format => sub { + my $out = shift; + return ($out =~ /ImageSize\s+:\s+(\d+)x(\d+)/) ? { width => $1, height => $2 } : undef; + } + }, + + # Use Image::Size last, due to limitations mentioned elsewhere + { + prog => \&have_Image_Size, + format => \&call_Image_Size, + name => 'Image::Size', + } + ] +}; + +# Look for a dims_methods program to determine image dimensions +sub find_dims_methods { + my $methods = shift; + + if ($opts{'method'}) { + @$methods = grep { + (exists $_->{'name'} and (lc($_->{'name'}) eq lc($opts{'method'}))) or + ($_->{'prog'} eq $opts{'method'}) } @$methods; + } + + for (@$methods) { + if (ref $_->{'prog'} eq 'CODE' and $_->{'prog'}->()) { + return $_; + } + elsif (my $choice = which($_->{'prog'})) { + $_->{'prog'} = $choice; + return $_; + } + } + + say STDERR "$prog: no methods found to obtain image dimensions. Tried: ", + map { "\n " . (exists $_->{'name'} ? $_->{'name'} : $_->{'prog'}) } @$methods; + + exit 1; +} + +# Allow Image::Size to be used if available to calculate an image's size. It +# does not support dimensions of PDF files, so it will be tried last. +sub have_Image_Size { + eval "require Image::Size"; + return Image::Size->can('imgsize'); +} + +sub call_Image_Size { + my $file = shift; + + my ($w, $h, $type) = Image::Size::imgsize($file); + if (defined $w and defined $h) { + # Bug: Workaround negative BMP size values, discovered with export to BMP via + # SnagIt and Pixelmator (classic). + if ($type eq 'BMP') { + $w = (2**32) - $w if $w > 2**31; + $h = (2**32) - $h if $h > 2**31; + } + return { width => $w, height => $h }; + } + + return undef; +} + +# grab the specified image file's contents +sub get_image_bytes { + my $file = shift; + + $/ = undef; + open (my $fh, "<", $file) + or return undef; + my $filebytes = <$fh>; + chomp $filebytes; + close $fh; + + return $filebytes; +} + +sub ls_sort { + return if ! (@_ or scalar @_ > 1); + + if ($opts{'t'}) { + # descending + @_ = ($opts{'y'} or $ENV{'LS_SAMESORT'}) ? + sort { _lstat($b)->mtime <=> _lstat($a)->mtime || $b cmp $a } @_ : + sort { _lstat($b)->mtime <=> _lstat($a)->mtime || $a cmp $b } @_; + } + # macOS seems to sort lexically with -c/-U, but shows ctime timestamps + elsif ($opts{'c'}) { + @_ = sort { $a cmp $b } @_; + } + elsif ($opts{'u'}) { + @_ = sort { _lstat($a)->atime <=> _lstat($b)->atime } @_; + } + elsif ($opts{'S'}) { + @_ = sort { _lstat($b)->size <=> _lstat($a)->size || $a cmp $b } @_; + } + else { + @_ = sort @_; + } + + return $opts{'r'} ? reverse @_ : @_; +} + +sub get_F_type { + my $st = shift; + + return '/' if -d $st and ($opts{'p'} or $opts{'F'}); + return '' unless $opts{'F'}; + return '@' if -l $st; + return '|' if -p $st; + return '=' if -S $st; + return '*' if -x $st; # must come after other tests + return ''; +} + +sub format_time { + my $time = shift; + + my $fmt; + if ($opts{'D'}) { + $fmt = $opts{'D'}; + } + elsif ($opts{'T'}) { + # mmm dd hh:mm:ss yyyy + $fmt = '%b %e %T %Y'; + } + else { + if ($time + $sixmonths > $curtime and $time < $curtime + $sixmonths) { + # mmm dd hh:mm + $fmt = '%b %e %R'; + } + else { + # mmm dd yyyy + $fmt = '%b %e %Y'; + } + } + return strftime $fmt, localtime($time); +} + +sub format_human { + my $bytes = shift; + + my @units = ('B', 'K', 'M', 'G', 'T', 'P'); + my $scale = floor((length($bytes) - 1) / 3); + my $float = $bytes / (1024 ** $scale); + + my ($frac, $int) = modf($float); + if (length($bytes) < 3 or length($int) >= 2) { + sprintf "%d%s", $frac <.5 ? $float : $float + 1, $units[$scale]; + } + else { + sprintf "%.1f%s", $float, $units[$scale]; + } +} + +sub _lstat { + my $file = shift; + return $stat_cache{$file} if exists $stat_cache{$file}; + + if (my $s = lstat($file)) { + return $stat_cache{$file} = $s; + } + + return $file; +} + +sub which { + my ($exec) = @_; + + $exec or + return undef; + + if ($exec =~ m#/# && -f $exec && -x _) { + return $exec + } + + foreach my $file ( map { File::Spec->catfile($_, $exec) } File::Spec->path) { + -d $file and + next; + -x _ and + return $file; + } + + return undef; +} + + +# Generate 1 pixel black PNG as placeholding for non-image-able files. +sub get_black_pixel_image { + # base 64 encoded single black pixel png + my $one_pixel_black = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2NgYGD4DwABBAEAwS2OUAAAAABJRU5ErkJggg=='; + return ($one_pixel_black, length $one_pixel_black); +} + +# Based on Stat::lsMode, Copyright 1998 M-J. Dominus, (mjd-perl-lsmode@plover.com) +# You may distribute this module under the same terms as Perl itself. + +package Stat::lsMode; + +sub format_mode { + my $mode = shift; + + return undef unless defined $mode; + + my @permchars = qw(--- --x -w- -wx r-- r-x rw- rwx); + my @ftypechars = qw(. p c ? d ? b ? - ? l ? s ? ? ?); + $ftypechars[0] = ''; + + my $setids = ($mode & 07000) >> 9; + my @permstrs = @permchars[($mode & 0700) >> 6, ($mode & 0070) >> 3, $mode & 0007]; + my $ftype = $ftypechars[($mode & 0170000) >> 12]; + + if ($setids) { + if ($setids & 01) { # sticky + $permstrs[2] =~ s/([-x])$/$1 eq 'x' ? 't' : 'T'/e; + } + if ($setids & 04) { # setuid + $permstrs[0] =~ s/([-x])$/$1 eq 'x' ? 's' : 'S'/e; + } + if ($setids & 02) { # setgid + $permstrs[1] =~ s/([-x])$/$1 eq 'x' ? 's' : 'S'/e; + } + } + + join '', $ftype, @permstrs; +} + +# vim: expandtab diff --git a/.iterm2/it2attention b/.iterm2/it2attention index 9509296..fc842ab 100755 --- a/.iterm2/it2attention +++ b/.iterm2/it2attention @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash # tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; # ST, and for all ESCs in to be replaced with ESC ESC. It # only accepts ESC backslash for ST. function print_osc() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]] ; then printf "\033Ptmux;\033\033]" else printf "\033]" @@ -13,7 +13,7 @@ function print_osc() { # More of the tmux workaround described above. function print_st() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]] ; then printf "\a\033\\" else printf "\a" @@ -26,6 +26,8 @@ function show_help() { echo " Begin bouncing the dock icon if another app is active" 1>& 2 echo " $(basename $0) stop" 1>& 2 echo " Stop bouncing the dock icon if another app is active" 1>& 2 + echo " $(basename $0) once" 1>& 2 + echo " Bounce the dock icon once if another app is active" 1>& 2 echo " $(basename $0) fireworks" 1>& 2 echo " Show an explosion animation at the cursor" 1>& 2 } @@ -42,6 +44,12 @@ function stop_bounce() { print_st } +function bounce_once() { + print_osc + printf "1337;RequestAttention=once" + print_st +} + function fireworks() { print_osc printf "1337;RequestAttention=fireworks" @@ -61,6 +69,9 @@ then elif [[ $1 == stop ]] then stop_bounce +elif [[ $1 == once ]] +then + bounce_once elif [[ $1 == fireworks ]] then fireworks diff --git a/.iterm2/it2check b/.iterm2/it2check index 6a693ee..34ac095 100755 --- a/.iterm2/it2check +++ b/.iterm2/it2check @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Make sure stdin and stdout are a tty. if [ ! -t 0 ] ; then exit 1 @@ -7,17 +7,6 @@ if [ ! -t 1 ] ; then exit 1 fi -# Save the tty's state. -saved_stty=$(stty -g) - -# Trap ^C to fix the tty. -trap ctrl_c INT - -function ctrl_c() { - stty "$saved_stty" - exit 1 -} - # Read some bytes from stdin. Pass the number of bytes to read as the first argument. function read_bytes() { @@ -47,6 +36,22 @@ function version { echo -n "$1" | sed -e 's/.* //' } +trap clean_up EXIT +_STTY=$(stty -g) ## Save current terminal setup + +function clean_up() { + stty "$_STTY" ## Restore terminal settings +} + +# Prepare to silently read any (>=0) characters with no timeout. +stty -echo -icanon raw min 0 time 0 + +# Consume all pending input. +while read none; do :; done + +# Reset the TTY, so it behaves as expected for the rest of the it2check script. +clean_up + # Enter raw mode and turn off echo so the terminal and I can chat quietly. stty -echo -icanon raw @@ -69,7 +74,7 @@ echo -n '' echo -n '' version_string=$(read_dsr) -if [ "${version_string}" != "0" -a "${version_string}" != "3" ]; then +if [ -n "${version_string}" -a "${version_string}" != "0" -a "${version_string}" != "3" ]; then # Already read DSR 1337. Read DSR 5 and throw it away. dsr=$(read_dsr) else @@ -77,9 +82,6 @@ else version_string="" fi -# Restore the terminal to cooked mode. -stty "$saved_stty" - # Extract the terminal name and version number from the response. version=$(version "${version_string}") term=$(terminal "${version_string}") diff --git a/.iterm2/it2copy b/.iterm2/it2copy index e045952..577949b 100755 --- a/.iterm2/it2copy +++ b/.iterm2/it2copy @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash trap clean_up EXIT trap clean_up INT @@ -23,7 +23,7 @@ function show_help() { # ST, and for all ESCs in to be replaced with ESC ESC. It # only accepts ESC backslash for ST. function print_osc() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]] ; then printf "\033Ptmux;\033\033]" else printf "\033]" @@ -32,13 +32,52 @@ function print_osc() { # More of the tmux workaround described above. function print_st() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]] ; then printf "\a\033\\" else printf "\a" fi } +send_tmux() { + uid=$RANDOM$RANDOM + print_osc + inosc=1 + printf '1337;Copy=2;%s' "$uid" + print_st + inosc=0 + fold | while read line + do + print_osc + inosc=1 + printf '1337;Copy=3;%s:%s' "$uid" "$line" + print_st + inosc=0 + done + + print_osc + inosc=1 + printf '1337;Copy=4;%s' "$uid" + print_st + inosc=0 +} + +send_regular() { + print_osc + inosc=1 + printf '1337;Copy=:%s' "$data" + print_st + inosc=0 +} + +send() { + if [[ $TERM == tmux* ]]; then + send_tmux + else + send_regular + fi +} + # Look for command line flags. while [ $# -gt 0 ]; do case "$1" in @@ -53,12 +92,7 @@ while [ $# -gt 0 ]; do ;; *) if [ -r "$1" ] ; then - data=$(base64 < "$1") - print_osc - inosc=1 - printf '1337;Copy=:%s' "$data" - print_st - inosc=0 + base64 < $1 | send exit 0 else error "it2copy: $1: No such file or directory" @@ -69,10 +103,4 @@ while [ $# -gt 0 ]; do shift done -data=$(base64) -print_osc -inosc=1 -printf '1337;Copy=:%s' "$data" -print_st -inosc=0 - +base64 | send diff --git a/.iterm2/it2dl b/.iterm2/it2dl index dcd3b7d..e0bb9e6 100755 --- a/.iterm2/it2dl +++ b/.iterm2/it2dl @@ -1,17 +1,73 @@ -#!/bin/bash +#!/usr/bin/env bash if [ $# -lt 1 ]; then echo "Usage: $(basename $0) file ..." exit 1 fi + +# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; +# ST, and for all ESCs in to be replaced with ESC ESC. It +# only accepts ESC backslash for ST. We use TERM instead of TMUX because TERM +# gets passed through ssh. +function print_osc() { + if [[ $TERM == screen* || $TERM == tmux* ]]; then + printf "\033Ptmux;\033\033]" + else + printf "\033]" + fi +} + +# More of the tmux workaround described above. +function print_st() { + if [[ $TERM == screen* || $TERM == tmux* ]]; then + printf "\a\033\\" + else + printf "\a" + fi +} + +function load_version() { + if [ -z ${IT2DL_BASE64_VERSION+x} ]; then + export IT2DL_BASE64_VERSION=$(base64 --version 2>&1) + fi +} + +function b64_encode() { + load_version + if [[ "$IT2DL_BASE64_VERSION" =~ GNU ]]; then + # Disable line wrap + base64 -w0 + else + base64 + fi +} + + for fn in "$@" do if [ -r "$fn" ] ; then [ -d "$fn" ] && { echo "$fn is a directory"; continue; } - printf '\033]1337;File=name='`echo -n "$fn" | base64`";" - wc -c "$fn" | awk '{printf "size=%d",$1}' - printf ":" - base64 < "$fn" - printf '\a' + if [[ $TERM == screen* || $TERM == tmux* ]]; then + print_osc + printf '1337;MultipartFile=name=%s;' $(echo -n "$fn" | b64_encode) + wc -c "$fn" | awk '{printf "size=%d",$1}' + print_st + + parts=$(b64_encode < "$fn" | fold -w 256) + for part in $parts; do + print_osc + printf '1337;FilePart=%s' "$part" + print_st + done + print_osc + printf '1337;FileEnd' + print_st + else + printf '\033]1337;File=name=%s;' $(echo -n "$fn" | b64_encode) + wc -c "$fn" | awk '{printf "size=%d",$1}' + printf ":" + base64 < "$fn" + printf '\a' + fi else echo File $fn does not exist or is not readable. fi diff --git a/.iterm2/it2getvar b/.iterm2/it2getvar index 579018c..6cbbf4f 100755 --- a/.iterm2/it2getvar +++ b/.iterm2/it2getvar @@ -1,99 +1,133 @@ -#!/bin/bash +#!/usr/bin/env bash -function ctrl_c() { - stty "$saved_stty" - exit 1 -} +set -o pipefail # tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; # ST, and for all ESCs in to be replaced with ESC ESC. It # only accepts ESC backslash for ST. function print_osc() { - if [[ $TERM == screen* ]] ; then printf "\033Ptmux;\033\033]" + if [[ $TERM == screen* || $TERM == tmux* ]] ; then + printf "\033Ptmux;\033\033]" else - printf "\033]" >& 2 + printf "\033]" fi } # More of the tmux workaround described above. function print_st() { - if [[ $TERM == screen* ]] ; then - printf "\a\033\\" >& 2 + if [[ $TERM == screen* || $TERM == tmux* ]] ; then + printf "\a\033\\" else - printf "\a" >& 2 + printf "\a" fi } +function get_b64_version() { + if [[ -z "${BASE64_VERSION+x}" ]]; then + BASE64_VERSION=$(base64 --version 2>&1) + export BASE64_VERSION + fi +} + +function b64_encode() { + get_b64_version + if [[ $BASE64_VERSION =~ GNU ]]; then + # Disable line wrap + base64 -w0 + else + base64 + fi +} + +function b64_decode() { + get_b64_version + if [[ $BASE64_VERSION =~ fourmilab ]]; then + BASE64_ARG=-d + elif [[ $BASE64_VERSION =~ GNU ]]; then + BASE64_ARG=-di + else + BASE64_ARG=-D + fi + base64 $BASE64_ARG +} + +function error() { + errcho "ERROR: $*" +} + +function errcho() { + echo "$@" >&2 +} + function show_help() { - echo "Usage:" 1>& 2 - echo " $(basename $0) name" 1>& 2 + errcho + errcho "Usage: it2getvar variable_name" + errcho + errcho "Output value of the iTerm2 variable" + errcho + errcho "See the Variables Reference for information about built-in iTerm2 variables:" + errcho " -> https://iterm2.com/documentation-variables.html" + errcho } -# Read some bytes from stdin. Pass the number of bytes to read as the first argument. -function read_bytes() { - numbytes=$1 - dd bs=1 count=$numbytes 2>/dev/null -} - -# read_until c -# Returns bytes read from stdin up to but not including the fist one equal to c -function read_until() { - result="" - while : - do - b=$(read_bytes 1) - if [[ $b == $1 ]] - then - echo "$result" - return +function check_dependency() { + if ! (builtin command -V "$1" >/dev/null 2>&1); then + error "missing dependency: can't find $1" + exit 1 fi - result="$result$b" - done } -## Main -if [[ $# != 1 ]] -then - show_help - exit 1 +# get_variable variable_name +# +# This function uses POSIX standard synonym for the controlling terminal +# associated with the current process group - /dev/tty. It is useful for programs +# that wish to be sure of writing or reading data from the terminal +# no matter how STDIN/STDOUT/STDERR has been redirected. +function get_variable() { + trap 'cleanup' EXIT + stty -echo < /dev/tty + exec 9<> /dev/tty + print_osc >&9 + printf "1337;ReportVariable=%s" "$(echo -n "$1" | b64_encode)" >&9 + print_st >&9 + read -r -t 5 -d $'\a' iterm_response <&9 + exec 9>&- + stty echo < /dev/tty + [[ "$iterm_response" =~ ReportVariable= ]] || { + error "Failed to read response from iTerm2" + exit 2 + } + echo "$(b64_decode <<< ${iterm_response#*=})" +} + +function cleanup() { + stty echo < /dev/tty +} + +# Show help if no arguments +if [ $# -eq 0 ]; then + show_help + exit fi -if ! test -t 1 -then - echo "Standard error not a terminal" - exit 1 -fi +check_dependency stty +check_dependency base64 -# Save the tty's state. -saved_stty=$(stty -g) - -# Trap ^C to fix the tty. -trap ctrl_c INT - -# Enter raw mode and turn off echo so the terminal and I can chat quietly. -stty -echo -icanon raw - -print_osc -printf "1337;ReportVariable=%s" "$(printf "%s" "$1" | base64)" >& 2 -print_st - -VERSION=$(base64 --version 2>&1) -if [[ "$VERSION" =~ fourmilab ]]; then - BASE64ARG=-d -elif [[ "$VERSION" =~ GNU ]]; then - BASE64ARG=-di -else - BASE64ARG=-D -fi - -ignore=$(read_bytes 1) -name=$(read_until ) -re='^]1337;ReportVariable=(.*)' -if [[ $name =~ $re ]] -then - printf "%s" $(base64 $BASE64ARG <<< ${BASH_REMATCH[1]}) - exit 0 -else - exit 1 -fi +# Process command line arguments +case "$1" in + -h|--h|--help) + show_help + exit + ;; + -*) + error "Unknown option: $1" + show_help + exit 1 + ;; + *) + [[ -z "$1" ]] && error "Variable name can't be empty" && exit 1 + get_variable "$1" + ;; +esac +exit 0 diff --git a/.iterm2/it2setcolor b/.iterm2/it2setcolor index 7871d6f..b4614b0 100755 --- a/.iterm2/it2setcolor +++ b/.iterm2/it2setcolor @@ -1,11 +1,11 @@ -#!/bin/bash +#!/usr/bin/env bash open=0 # tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; # ST, and for all ESCs in to be replaced with ESC ESC. It # only accepts ESC backslash for ST. function print_osc() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]] ; then printf "\033Ptmux;\033\033]" else printf "\033]" @@ -14,7 +14,7 @@ function print_osc() { # More of the tmux workaround described above. function print_st() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]] ; then printf "\a\033\\" else printf "\a" diff --git a/.iterm2/it2setkeylabel b/.iterm2/it2setkeylabel index 6996337..7fcc1df 100755 --- a/.iterm2/it2setkeylabel +++ b/.iterm2/it2setkeylabel @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash # tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; # ST, and for all ESCs in to be replaced with ESC ESC. It # only accepts ESC backslash for ST. function print_osc() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]] ; then printf "\033Ptmux;\033\033]" else printf "\033]" @@ -13,7 +13,7 @@ function print_osc() { # More of the tmux workaround described above. function print_st() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]] ; then printf "\a\033\\" else printf "\a" @@ -24,13 +24,15 @@ function show_help() { echo "Usage:" 1>& 2 echo " $(basename $0) set Fn Label" 1>& 2 echo " Where n is a value from 1 to 20" 1>& 2 + echo " $(basename $0) set status Label" 1>& 2 + echo " Sets the touch bar status button's label" 1>& 2 echo " $(basename $0) push [name]" 1>& 2 echo " Saves the current labels with an optional name. Resets labels to their default value, unless name begins with a "." character." 1>& 2 echo " $(basename $0) pop [name]" 1>& 2 echo " If name is given, all key labels up to and including the one with the matching name are popped." 1>& 2 echo "" 1>& 2 echo "Example:" 1>& 2 - echo "#!/bin/bash" 1>& 2 + echo "#!/usr/bin/env bash" 1>& 2 echo "# Wrapper script for mc that sets function key labels" 1>& 2 echo "NAME=mc_\$RANDOM" 1>& 2 echo "# Save existing labels" 1>& 2 diff --git a/.iterm2/it2ul b/.iterm2/it2ul index 5aae8df..9934e0b 100755 --- a/.iterm2/it2ul +++ b/.iterm2/it2ul @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash trap clean_up EXIT _STTY=$(stty -g) ## Save current terminal setup @@ -52,7 +52,7 @@ function decode() { # ST, and for all ESCs in to be replaced with ESC ESC. It # only accepts ESC backslash for ST. function print_osc() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]] ; then printf "\033Ptmux;\033\033]" else printf "\033]" @@ -61,16 +61,25 @@ function print_osc() { # More of the tmux workaround described above. function print_st() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]] ; then printf "\a\033\\" else printf "\a" fi } +function b64_encode() { + if [[ "$IT2DL_BASE64_VERSION" =~ GNU ]]; then + # Disable line wrap + base64 -w0 + else + base64 + fi +} + function send_request_for_upload() { print_osc - printf '1337;RequestUpload=format=tgz' "" + printf '1337;RequestUpload=format=tgz;version=%s' "$(tar --version | head -1 | b64_encode)" print_st } diff --git a/.iterm2/it2universion b/.iterm2/it2universion index 771c067..d974688 100755 --- a/.iterm2/it2universion +++ b/.iterm2/it2universion @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash # tmux requires unrecognized OSC sequences to be wrapped with DCS tmux; # ST, and for all ESCs in to be replaced with ESC ESC. It # only accepts ESC backslash for ST. function print_osc() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]] ; then printf "\033Ptmux;\033\033]" else printf "\033]" @@ -13,7 +13,7 @@ function print_osc() { # More of the tmux workaround described above. function print_st() { - if [[ $TERM == screen* ]] ; then + if [[ $TERM == screen* || $TERM == tmux* ]] ; then printf "\a\033\\" else printf "\a"