cht.shon commit improve webp-convert.sh (b8ae487)
   1#!/bin/sh
   2#
   3# [X] open section
   4# [X] one shot mode
   5# [X] usage info
   6# [X] dependencies check
   7# [X] help
   8# [X] yank/y/copy/c
   9# [X] Y/C
  10# [X] eof problem
  11# [X] more
  12# [X] stealth mode
  13#
  14# here are several examples for the stealth mode:
  15#
  16# zip lists
  17# list permutation
  18# random list element
  19# reverse a list
  20# read json from file
  21# append string to a file
  22# run process in background
  23# count words in text counter
  24# group elements list
  25
  26__CHTSH_VERSION=4
  27__CHTSH_DATETIME="2018-07-08 22:26:46 +0200"
  28
  29export LESSSECURE=1
  30STEALTH_MAX_SELECTION_LENGTH=5
  31
  32case `uname -s` in
  33  Darwin) is_macos=yes ;;
  34  *) is_macos=no ;;
  35esac
  36
  37# for KSH93
  38if echo $KSH_VERSION | grep -q ' 93' && ! local foo 2>/dev/null; then
  39  alias local=typeset
  40fi
  41
  42get_query_options()
  43{
  44  local query="$*"
  45  if [ -n "$CHTSH_QUERY_OPTIONS" ]; then
  46    case $query in
  47      *\?*)   query="$query&${CHTSH_QUERY_OPTIONS}";;
  48      *)      query="$query?${CHTSH_QUERY_OPTIONS}";;
  49    esac
  50  fi
  51  printf "%s" "$query"
  52}
  53
  54do_query()
  55{
  56  local query="$*"
  57  local b_opts=
  58  local uri="${CHTSH_URL}/\"\$(get_query_options $query)\""
  59
  60  if [ -e "$HOME/.cht.sh/id" ]; then
  61    b_opts="-b \"\$HOME/.cht.sh/id\""
  62  fi
  63
  64  eval curl $b_opts -s $uri > "$TMP1"
  65
  66  if [ -z "$lines" ] || [ "$(wc -l "$TMP1" | awk '{print $1}')" -lt "$lines" ]; then
  67    cat "$TMP1"
  68  else
  69    ${PAGER:-$defpager} "$TMP1"
  70  fi
  71}
  72
  73prepare_query()
  74{
  75  local section="$1"; shift
  76  local input="$1"; shift
  77  local arguments="$1"
  78
  79  local query
  80  if [ -z "$section" ] || [ x"${input}" != x"${input#/}" ]; then
  81    query=$(printf %s "$input" | sed 's@ @/@; s@ @+@g')
  82  else
  83    query=$(printf %s "$section/$input" | sed 's@ @+@g')
  84  fi
  85
  86  [ -n "$arguments" ] && arguments="?$arguments"
  87  printf %s "$query$arguments"
  88}
  89
  90get_list_of_sections()
  91{
  92  curl -s "${CHTSH_URL}"/:list | grep -v '/.*/' | grep '/$' | xargs
  93}
  94
  95gen_random_str()
  96(
  97  len=$1
  98  if command -v openssl >/dev/null; then
  99    openssl rand -base64 $(($len*3/4)) | awk -v ORS='' //
 100  else
 101    rdev=/dev/urandom
 102    for d in /dev/{srandom,random,arandom}; do
 103      test -r $d && rdev=$d
 104    done
 105    if command -v hexdump >/dev/null; then
 106      hexdump -vn $(($len/2)) -e '1/1 "%02X" 1 ""' $rdev
 107    elif command -v xxd >/dev/null; then
 108      xxd -l $(($len/2)) -ps $dev | awk -v ORS='' //
 109    else
 110      cd /tmp
 111      s=
 112      while [ $(echo "$s" | wc -c) -lt $len ]; do
 113        s="$s$(mktemp -u XXXXXXXXXX)"
 114      done
 115      printf %.${len}s "$s"
 116    fi
 117  fi
 118)
 119
 120if [ -z "$CHTSH_CONF" ]; then
 121  CHTSH_CONF="$HOME"/.cht.sh/cht.sh.conf
 122fi
 123
 124if [ -e "$CHTSH_CONF" ]; then
 125  # shellcheck disable=SC1090,SC2002
 126  . "$CHTSH_CONF"
 127fi
 128
 129[ -z "$CHTSH_URL" ] && CHTSH_URL=https://cht.sh
 130
 131# any better test not involving either OS matching or actual query?
 132if [ `uname -s` = OpenBSD ] && [ -x /usr/bin/ftp ]; then
 133  curl() {
 134    local opt args="-o -"
 135    while getopts "b:s" opt; do
 136      case $opt in
 137        b) args="$args -c $OPTARG";;
 138        s) args="$args -M -V";;
 139        *) echo "internal error: unsupported cURL option '$opt'" >&2; exit 1;;
 140      esac
 141    done
 142    shift $(($OPTIND - 1))
 143    /usr/bin/ftp $args "$@"
 144  }
 145else
 146  command -v curl   >/dev/null || { echo 'DEPENDENCY: install "curl" to use cht.sh' >&2; exit 1; }
 147  _CURL=$(command -v curl)
 148  if [ x"$CHTSH_CURL_OPTIONS" != x ]; then
 149    curl() {
 150      $_CURL ${CHTSH_CURL_OPTIONS} "$@"
 151    }
 152  fi
 153fi
 154
 155
 156if [ "$1" = --read ]; then
 157  read -r a || a=exit
 158  printf "%s\n" "$a"
 159  exit 0
 160elif [ x"$1" = x--help ] || [ -z "$1" ]; then
 161  cat <<EOF
 162Usage:
 163
 164    ${0##*/} --help           show this help
 165    ${0##*/} --shell [LANG]   shell mode (open LANG if specified)
 166    ${0##*/} QUERY            process QUERY and exit
 167EOF
 168  exit 0
 169elif [ x"$1" = x--shell ]; then
 170  shell_mode=yes
 171  shift
 172fi
 173
 174prompt="cht.sh"
 175opts=""
 176input=""
 177for o; do
 178  if [ x"$o" != x"${o#-}" ]; then
 179    opts="${opts}${o#-}"
 180  else
 181    input="$input $o"
 182  fi
 183done
 184query=$(echo "$input" | sed 's@ *$@@; s@^ *@@; s@ @/@; s@ @+@g')
 185
 186if [ "$shell_mode" != yes ]; then
 187  curl -s "${CHTSH_URL}"/"$(get_query_options "$query")"
 188  exit 0
 189else
 190  new_section="$1"
 191  valid_sections=$(get_list_of_sections)
 192  valid=no; for q in $valid_sections; do [ "$q" = "$new_section/" ] && { valid=yes; break; }; done
 193
 194  if [ "$valid" = yes ]; then
 195    section="$new_section"
 196    this_query="$(echo "$input" | sed 's@ *[^ ]* *@@')"
 197    this_prompt="\033[0;32mcht.sh/$section>\033[0m "
 198  else
 199    this_query="$input"
 200    this_prompt="\033[0;32mcht.sh>\033[0m "
 201  fi
 202  if [ -n "$this_query" ] && [ -z "$CHEATSH_RESTART" ]; then
 203    printf "$this_prompt$this_query\n"
 204    curl -s "${CHTSH_URL}"/"$(get_query_options "$query")"
 205  fi
 206fi
 207
 208if [ "$is_macos" != yes ]; then
 209  command -v xsel >/dev/null ||   echo 'DEPENDENCY: please install "xsel" for "copy"' >&2
 210fi
 211command -v rlwrap >/dev/null || { echo 'DEPENDENCY: install "rlwrap" to use cht.sh in the shell mode' >&2; exit 1; }
 212
 213mkdir -p "$HOME/.cht.sh/"
 214lines=$(tput lines)
 215
 216if command -v less >/dev/null; then
 217  defpager="less -R"
 218elif command -v more >/dev/null; then 
 219  defpager=more
 220else
 221  defpager=cat
 222fi
 223
 224cmd_cd() {
 225  if [ $# -eq 0 ]; then
 226    section=""
 227  else
 228    new_section=$(echo "$input" | sed 's/cd  *//; s@/*$@@; s@^/*@@')
 229    if [ -z "$new_section" ] || [ ".." = "$new_section" ]; then
 230      section=""
 231    else
 232      valid_sections=$(get_list_of_sections)
 233      valid=no; for q in $valid_sections; do [ "$q" = "$new_section/" ] && { valid=yes; break; }; done
 234      if [ "$valid" = no ]; then
 235        echo "Invalid section: $new_section"
 236        echo "Valid sections:"
 237        echo $valid_sections | xargs printf "%-10s\n" | tr ' ' .  | xargs -n 10 | sed 's/\./ /g; s/^/  /'
 238      else
 239        section="$new_section"
 240      fi
 241    fi
 242  fi
 243}
 244
 245cmd_copy() {
 246  if [ -z "$DISPLAY" ]; then
 247    echo copy: supported only in the Desktop version
 248  elif [ -z "$input" ]; then
 249    echo copy: Make at least one query first.
 250  else
 251    curl -s "${CHTSH_URL}"/"$(get_query_options "$query"?T)" > "$TMP1"
 252    if [ "$is_macos" != yes ]; then
 253      xsel -bi < "$TMP1"
 254    else
 255      cat "$TMP1" | pbcopy
 256    fi
 257    echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection"
 258  fi
 259}
 260
 261cmd_ccopy() {
 262  if [ -z "$DISPLAY" ]; then
 263    echo copy: supported only in the Desktop version
 264  elif [ -z "$input" ]; then
 265    echo copy: Make at least one query first.
 266  else
 267    curl -s "${CHTSH_URL}"/"$(get_query_options "$query"?TQ)" > "$TMP1"
 268    if [ "$is_macos" != yes ]; then
 269      xsel -bi < "$TMP1"
 270    else
 271      cat "$TMP1" | pbcopy
 272    fi
 273    echo "copy: $(wc -l "$TMP1" | awk '{print $1}') lines copied to the selection"
 274  fi
 275}
 276
 277cmd_exit() {
 278  exit 0
 279}
 280
 281cmd_help() {
 282  cat <<EOF
 283help    - show this help
 284hush    - do not show the 'help' string at start anymore
 285cd LANG - change the language context
 286copy    - copy the last answer in the clipboard (aliases: yank, y, c)
 287ccopy   - copy the last answer w/o comments (cut comments; aliases: cc, Y, C)
 288exit    - exit the cheat shell (aliases: quit, ^D)
 289id [ID] - set/show an unique session id ("reset" to reset, "remove" to remove)
 290stealth - stealth mode (automatic queries for selected text)
 291update  - self update (only if the scriptfile is writeable)
 292version - show current cht.sh version
 293/:help  - service help
 294QUERY   - space ceparated query staring (examples are below)
 295              cht.sh> python zip list
 296              cht.sh/python> zip list
 297              cht.sh/go> /python zip list
 298EOF
 299}
 300
 301cmd_hush() {
 302  mkdir -p $HOME/.cht.sh/ && touch $HOME/.cht.sh/.hushlogin && echo "Initial 'use help' message was disabled"
 303}
 304
 305cmd_id() {
 306  id_file="$HOME/.cht.sh/id"
 307
 308  if [ id = "$input" ]; then
 309    new_id=""
 310  else
 311    new_id=$(echo "$input" | sed 's/id  *//; s/ *$//; s/ /+/g')
 312  fi
 313  if [ "$new_id" = remove ]; then
 314    if [ -e "$id_file" ]; then
 315      rm -f -- "$id_file" && echo "id is removed"
 316    else
 317      echo "id was not set, so you can't remove it"
 318    fi
 319    return
 320  fi
 321  if [ -n "$new_id" ] && [ reset != "$new_id" ] && [ $(/bin/echo -n "$new_id" | wc -c) -lt 16 ]; then
 322    echo "ERROR: $new_id: Too short id. Minimal id length is 16. Use 'id reset' for a random id"
 323    return
 324  fi
 325  if [ -z "$new_id" ]; then
 326    # if new_id is not specified check if we have some id already
 327    # if yes, just show it
 328    # if not, generate a new id
 329    if [ -e "$id_file" ]; then
 330      echo $(awk '$6 == "id" {print $NF}' <"$id_file" | tail -n 1)
 331      return
 332    else
 333      new_id=reset
 334    fi
 335  fi
 336  if [ "$new_id" = reset ]; then
 337    new_id=$(gen_random_str 12)
 338  else
 339    echo WARNING: if someone gueses your id, he can read your cht.sh search history
 340  fi
 341  if [ -e "$id_file" ] && grep -q '\tid\t[^\t][^\t]*$' "$id_file" 2> /dev/null; then
 342    sed -i 's/\tid\t[^\t][^\t]*$/ id '"$new_id"'/' "$id_file"
 343  else
 344    if ! [ -e "$id_file" ]; then
 345      printf '#\n\n' > "$id_file"
 346    fi
 347    printf ".cht.sh\tTRUE\t/\tTRUE\t0\tid\t$new_id\n" >> "$id_file"
 348  fi
 349  echo "$new_id"
 350}
 351
 352cmd_query() {
 353  query=$(prepare_query "$section" "$input")
 354  do_query "$query"
 355}
 356
 357cmd_stealth() {
 358  if [ "$input" != stealth ]; then
 359    arguments=$(echo "$input" | sed 's/stealth //; s/ /\&/')
 360  fi
 361  trap break INT
 362  if [ "$is_macos" = yes ]; then
 363    past=$(pbpaste)
 364  else
 365    past=$(xsel -o)
 366  fi
 367  printf "\033[0;31mstealth:\033[0m you are in the stealth mode; select any text in any window for a query\n"
 368  printf "\033[0;31mstealth:\033[0m selections longer than $STEALTH_MAX_SELECTION_LENGTH words are ignored\n"
 369  if [ -n "$arguments" ]; then
 370    printf "\033[0;31mstealth:\033[0m query arguments: ?$arguments\n"
 371  fi
 372  printf "\033[0;31mstealth:\033[0m use ^C to leave this mode\n"
 373  while true; do
 374    if [ "$is_macos" = yes ]; then
 375      current=$(pbpaste)
 376    else
 377      current=$(xsel -o)
 378    fi
 379    if [ "$past" != "$current" ]; then
 380      past=$current
 381      current_text="$(echo $current | tr -c '[a-zA-Z0-9]' ' ')"
 382      if [ $(echo $current_text | wc -w) -gt "$STEALTH_MAX_SELECTION_LENGTH" ]; then
 383        echo "\033[0;31mstealth:\033[0m selection length is longer than $STEALTH_MAX_SELECTION_LENGTH words; ignoring"
 384        continue
 385      else
 386        printf "\n\033[0;31mstealth: \033[7m $current_text\033[0m\n"
 387        query=$(prepare_query "$section" "$current_text" "$arguments")
 388        do_query "$query"
 389      fi
 390    fi
 391    sleep 1;
 392  done
 393  trap - INT
 394}
 395
 396cmd_update() {
 397  [ -w "$0" ] || { echo "The script is readonly; please update manually: curl -s "${CHTSH_URL}"/:bash | sudo tee $0"; return; }
 398  TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX)
 399  curl -s "${CHTSH_URL}"/:cht.sh > "$TMP2"
 400  if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then
 401    if grep -q ^__CHTSH_VERSION= "$TMP2"; then
 402      # section was vaildated by us already
 403      args="--shell $section"
 404      cp "$TMP2" "$0" && echo "Updated. Restarting..." && rm "$TMP2" && CHEATSH_RESTART=1 exec "$0" $args
 405    else
 406      echo "Something went wrong. Please update manually"
 407    fi
 408  else
 409    echo "cht.sh is up to date. No update needed"
 410  fi
 411  rm -f "$TMP2" > /dev/null 2>&1
 412}
 413
 414cmd_version() {
 415  insttime=$(ls -l -- "$0" | sed 's/  */ /g' | cut -d ' ' -f 6-8)
 416  echo "cht.sh version $__CHTSH_VERSION of $__CHTSH_DATETIME; installed at: $insttime"
 417  TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX)
 418  if curl -s "${CHTSH_URL}"/:cht.sh > "$TMP2"; then
 419    if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then
 420      echo "Update needed (type 'update' for that)".
 421    else
 422      echo "Up to date. No update needed"
 423    fi
 424  fi
 425  rm -f "$TMP2" > /dev/null 2>&1
 426}
 427
 428TMP1=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX)
 429trap 'rm -f $TMP1 $TMP2' EXIT
 430trap 'true' INT
 431
 432if ! [ -e "$HOME/.cht.sh/.hushlogin" ] && [ -z "$this_query" ]; then
 433  echo "type 'help' for the cht.sh shell help"
 434fi
 435
 436while true; do
 437  if [ "$section" != "" ]; then
 438    full_prompt="$prompt/$section> "
 439  else
 440    full_prompt="$prompt> "
 441  fi
 442
 443  input=$(
 444    rlwrap -H "$HOME/.cht.sh/history" -pgreen -C cht.sh -S "$full_prompt" sh "$0" --read | sed 's/ *#.*//'
 445  )
 446
 447  cmd_name=${input%% *}
 448  cmd_args=${input#* }
 449  case $cmd_name in
 450    "")             continue;;   # skip empty input lines
 451    '?'|h|help)     cmd_name=help;;
 452    hush)           cmd_name=hush;;
 453    cd)             cmd_name=cd;;
 454    exit|quit)      cmd_name=exit;;
 455    copy|yank|c|y)  cmd_name=copy;;
 456    ccopy|cc|C|Y)   cmd_name=ccopy;;
 457    id)             cmd_name=id;;
 458    stealth)        cmd_name=stealth;;
 459    update)         cmd_name=update;;
 460    version)        cmd_name=version;;
 461    *)              cmd_name="query $cmd_name";;
 462  esac
 463  cmd_$cmd_name $cmd_args 
 464done