From c67fb616625b112623d44d716070c523030aa706 Mon Sep 17 00:00:00 2001 From: Vadim Zhukov Date: Tue, 10 Jul 2018 22:20:36 +0300 Subject: [PATCH 1/5] Make cht.sh work under POSIX environment. There are a few corner cases still, but normal desktop users should be satisfied on almost any *nix now. Verified with bash, ksh (OpenBSD) and dash. --- share/cht.sh.txt | 126 ++++++++++++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 46 deletions(-) diff --git a/share/cht.sh.txt b/share/cht.sh.txt index b1b99fe..175dc39 100755 --- a/share/cht.sh.txt +++ b/share/cht.sh.txt @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # # [X] open section # [X] one shot mode @@ -8,7 +8,7 @@ # [X] yank/y/copy/c # [X] Y/C # [X] eof problem -# [X] less +# [X] more # [X] stealth mode # # here are several examples for the stealth mode: @@ -38,30 +38,30 @@ get_query_options() { local query="$*" if [ -n "$CHTSH_QUERY_OPTIONS" ]; then - if [[ $query == *\?* ]]; then - query="$query&${CHTSH_QUERY_OPTIONS}" - else - query="$query?${CHTSH_QUERY_OPTIONS}" - fi + case $query in + *\?*) query="$query&${CHTSH_QUERY_OPTIONS}";; + *) query="$query?${CHTSH_QUERY_OPTIONS}";; + esac fi - echo "$query" + printf "%s" "$query" } do_query() { local query="$*" - local b_opts=() + local b_opts= + local uri="https://cht.sh/\"\$(get_query_options $query)\"" if [ -e "$HOME/.cht.sh/id" ]; then - b_opts=(-b "$HOME/.cht.sh/id") + b_opts="-b \"\$HOME/.cht.sh/id\"" fi - curl "${b_opts[@]}" -s https://cht.sh/"$(get_query_options $query)" > "$TMP1" + eval curl $b_opts -s $uri > "$TMP1" if [ -z "$lines" ] || [ "$(wc -l "$TMP1" | awk '{print $1}')" -lt "$lines" ]; then cat "$TMP1" else - less -R "$TMP1" + ${PAGER:-$defpager} "$TMP1" fi } @@ -69,17 +69,17 @@ prepare_query() { local section="$1"; shift local input="$1"; shift - local arguments="$1"; shift + local arguments="$1" local query - if [ -z "$section" ] || [[ "$input" = /* ]]; then - query=$(echo "$input" | sed 's@ @/@; s@ @+@g') + if [ -z "$section" ] || [ x"${input}" != x"${input#/}" ]; then + query=$(printf %s "$input" | sed 's@ @/@; s@ @+@g') else - query=$(echo "$section/$input" | sed 's@ @+@g') + query=$(printf %s "$section/$input" | sed 's@ @+@g') fi [ -n "$arguments" ] && arguments="?$arguments" - echo "$query$arguments" + printf %s "$query$arguments" } get_list_of_sections() @@ -87,16 +87,41 @@ get_list_of_sections() curl -s https://cht.sh/:list | grep -v '/.*/' | grep '/$' } +gen_random_str() +( + len=$1 + if command -v openssl >/dev/null; then + openssl rand -base64 $(($len*3/4)) | awk -v ORS='' // + else + rdev=/dev/urandom + for d in /dev/{srandom,random,arandom}; do + test -r $d && rdev=$d + done + if command -v hexdump >/dev/null; then + hexdump -vn $(($len/2)) -e '1/1 "%02X" 1 ""' $rdev + elif command -v xxd >/dev/null; then + xxd -l $(($len/2)) -ps $dev | awk -v ORS='' // + else + cd /tmp + s= + while [ $(echo "$s" | wc -c) -lt $len ]; do + s="$s$(mktemp -u XXXXXXXXXX)" + done + printf %.${len}s "$s" + fi + fi +) + if [ -e "$HOME"/.cht.sh/cht.sh.conf ]; then # shellcheck disable=SC1090,SC2002 - source <( cat "$HOME"/.cht.sh/cht.sh.conf | sed 's/#.*//' | grep -xv '' | sed s/^/CHTSH_/ ) >& /dev/null + . "$HOME"/.cht.sh/cht.sh.conf fi -if [ "$1" == --read ]; then +if [ "$1" = --read ]; then read -r a || a=exit - echo $a + printf "%s\n" "$a" exit 0 -elif [ "$1" == --help ] || [ -z "$1" ]; then +elif [ x"$1" = x--help ] || [ -z "$1" ]; then cat <& /dev/null || { echo 'DEPENDENCY: please install "xsel" for "copy"' >&2; } + command -v xsel >/dev/null || echo 'DEPENDENCY: please install "xsel" for "copy"' >&2 fi -which rlwrap >& /dev/null || { echo 'DEPENDENCY: install "rlwrap" to use cht.sh in the shell mode' >&2; exit 1; } -which curl >& /dev/null || { echo 'DEPENDENCY: install "curl" to use cht.sh' >&2; exit 1; } +command -v rlwrap >/dev/null || { echo 'DEPENDENCY: install "rlwrap" to use cht.sh in the shell mode' >&2; exit 1; } +command -v curl >/dev/null || { echo 'DEPENDENCY: install "curl" to use cht.sh' >&2; exit 1; } mkdir -p "$HOME/.cht.sh/" lines=$(tput lines) +if command -v less >/dev/null; then + defpager="less -R" +elif command -v more >/dev/null; then + defpager=more +else + defpager=cat +fi + TMP1=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX) trap 'rm -f $TMP1 $TMP2' EXIT trap 'true' INT @@ -171,7 +204,7 @@ while true; do fi input=$( - rlwrap -H $HOME/.cht.sh/history -pgreen -C cht.sh -S "$full_prompt" bash "$0" --read | sed 's/ *#.*//' + rlwrap -H "$HOME/.cht.sh/history" -pgreen -C cht.sh -S "$full_prompt" sh "$0" --read | sed 's/ *#.*//' ) case "$input" in @@ -204,12 +237,12 @@ EOF continue ;; "cd "*) - new_section=$(echo "$input" | sed 's/cd //; s/ .*//; s@/\+$@@; s@^/\+@@') - if [ "$new_section" = "" ] || [ "$new_section" = ".." ] || [ "$new_section" = / ]; then + new_section=$(echo "$input" | sed 's/cd *//; s@/*$@@; s@^/*@@') + if [ -z "$new_section" ] || [ ".." = "$new_section" ]; then section="" else - valid_sections=($(get_list_of_sections)) - valid=no; for q in "${valid_sections[@]}"; do [[ "$q" == $new_section/ ]] && { valid=yes; break; }; done + valid_sections=$(get_list_of_sections) + valid=no; for q in $valid_sections; do [ "$q" = "$new_section/" ] && { valid=yes; break; }; done if [ "$valid" = no ]; then echo "Invalid section: $new_section" echo "Valid sections:" @@ -265,20 +298,20 @@ EOF id|"id "*) id_file="$HOME/.cht.sh/id" - if [ "$input" = id ]; then + if [ id = "$input" ]; then new_id="" else - new_id=$(echo "$input" | sed 's/id \+//; s/ *$//; s/ /+/g') + new_id=$(echo "$input" | sed 's/id *//; s/ *$//; s/ /+/g') fi if [ "$new_id" = remove ]; then if [ -e "$id_file" ]; then - rm -f "$id_file" && echo "id is removed" + rm -f -- "$id_file" && echo "id is removed" else echo "id was not set, so you can't remove it" fi continue fi - if [ -n "$new_id" ] && [ "$new_id" != reset ] && [ "$(echo $new_id | wc -c)" -lt 16 ]; then + if [ -n "$new_id" ] && [ reset != "$new_id" ] && [ $(/bin/echo -n "$new_id" | wc -c) -lt 16 ]; then echo "ERROR: $new_id: Too short id. Minimal id length is 16. Use 'id reset' for a random id" continue fi @@ -287,19 +320,19 @@ EOF # if yes, just show it # if not, generate a new id if [ -e "$id_file" ]; then - echo $(cat "$id_file" | awk '{if ($6 == "id") print $NF}' | head -1) + echo $(awk '$6 == "id" {print $NF}' <"$id_file" | tail -n 1) continue else new_id=reset fi fi if [ "$new_id" = reset ]; then - new_id="$(cat /dev/urandom 2> /dev/null | env LC_CTYPE=C tr -cd 'a-f0-9' 2> /dev/null | head -c 32)" + new_id=$(gen_random_str 12) else echo WARNING: if someone gueses your id, he can read your cht.sh search history fi - if [ -e "$id_file" ] && grep -q '\tid\t[^\t]\+$' "$id_file" 2> /dev/null; then - sed -i 's/\tid\t[^\t]\+$/ id '"$new_id"'/' "$id_file" + if [ -e "$id_file" ] && grep -q '\tid\t[^\t][^\t]*$' "$id_file" 2> /dev/null; then + sed -i 's/\tid\t[^\t][^\t]*$/ id '"$new_id"'/' "$id_file" else if ! [ -e "$id_file" ]; then printf '#\n\n' > "$id_file" @@ -354,9 +387,9 @@ EOF curl -s https://cht.sh/:cht.sh > "$TMP2" if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then if grep -q ^__CHTSH_VERSION= "$TMP2"; then - args=(--shell) - [ -n "$section" ] && args=("${args[@]}" "$section") - cp "$TMP2" "$0" && echo "Updated. Restarting..." && rm "$TMP2" && CHEATSH_RESTART=1 exec "$0" "${args[@]}" + # section was vaildated by us already + args="--shell $section" + cp "$TMP2" "$0" && echo "Updated. Restarting..." && rm "$TMP2" && CHEATSH_RESTART=1 exec "$0" $args else echo "Something went wrong. Please update manually" fi @@ -367,7 +400,8 @@ EOF continue ;; version) - echo "cht.sh version $__CHTSH_VERSION of $__CHTSH_DATETIME; installed at: $(stat -c %y "$0" | sed 's@\..* @ @')" + insttime=$(ls -lT -- "$0" | sed 's/ / /g' | cut -d ' ' -f 6-9) + echo "cht.sh version $__CHTSH_VERSION of $__CHTSH_DATETIME; installed at: $insttime" TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX) if curl -s https://cht.sh/:cht.sh > "$TMP2"; then if ! cmp "$0" "$TMP2" > /dev/null 2>&1; then From 56b28daf4ac6e41a5fe6b960a79233bd9f42fcf1 Mon Sep 17 00:00:00 2001 From: Vadim Zhukov Date: Wed, 11 Jul 2018 11:36:38 +0300 Subject: [PATCH 2/5] Missed non-POSIX array expansion. --- share/cht.sh.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/cht.sh.txt b/share/cht.sh.txt index 175dc39..e42d899 100755 --- a/share/cht.sh.txt +++ b/share/cht.sh.txt @@ -246,7 +246,7 @@ EOF if [ "$valid" = no ]; then echo "Invalid section: $new_section" echo "Valid sections:" - echo "${valid_sections[@]}" | xargs printf "%-10s\n" | tr ' ' . | xargs -n 10 | sed 's/\./ /g; s/^/ /' + echo $valid_sections | xargs printf "%-10s\n" | tr ' ' . | xargs -n 10 | sed 's/\./ /g; s/^/ /' continue else section="$new_section" From 3f16075d8992f3a84a68fc640dc6e7138412d5d1 Mon Sep 17 00:00:00 2001 From: Vadim Zhukov Date: Wed, 11 Jul 2018 11:37:07 +0300 Subject: [PATCH 3/5] The ksh93 shell doesn't have "local", but "typeset" could be used instead. --- share/cht.sh.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/share/cht.sh.txt b/share/cht.sh.txt index e42d899..7e1fa95 100755 --- a/share/cht.sh.txt +++ b/share/cht.sh.txt @@ -34,6 +34,11 @@ case "$OSTYPE" in *) is_macos=no ;; esac +# for KSH93 +if ! local foo 2>/dev/null; then + alias local=typeset +fi + get_query_options() { local query="$*" From 417f91119082a47d279dd2d8833cfd2ca6df5c6f Mon Sep 17 00:00:00 2001 From: Vadim Zhukov Date: Wed, 11 Jul 2018 11:37:54 +0300 Subject: [PATCH 4/5] Make zsh happy as well (in sh mode). --- share/cht.sh.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/cht.sh.txt b/share/cht.sh.txt index 7e1fa95..a7dcb89 100755 --- a/share/cht.sh.txt +++ b/share/cht.sh.txt @@ -89,7 +89,7 @@ prepare_query() get_list_of_sections() { - curl -s https://cht.sh/:list | grep -v '/.*/' | grep '/$' + curl -s https://cht.sh/:list | grep -v '/.*/' | grep '/$' | xargs } gen_random_str() From ada92527301e63e412c67530ae20822adb4aa561 Mon Sep 17 00:00:00 2001 From: Vadim Zhukov Date: Thu, 19 Jul 2018 16:48:50 +0300 Subject: [PATCH 5/5] ls -T isn't portable as well, as pointed out by pickfire@. Since we don't really use timestamp for anything except direct display, just extract fields 6-8 from "ls -l" output and be done with it. --- share/cht.sh.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/cht.sh.txt b/share/cht.sh.txt index a7dcb89..86afd70 100755 --- a/share/cht.sh.txt +++ b/share/cht.sh.txt @@ -405,7 +405,7 @@ EOF continue ;; version) - insttime=$(ls -lT -- "$0" | sed 's/ / /g' | cut -d ' ' -f 6-9) + insttime=$(ls -l -- "$0" | sed 's/ */ /g' | cut -d ' ' -f 6-8) echo "cht.sh version $__CHTSH_VERSION of $__CHTSH_DATETIME; installed at: $insttime" TMP2=$(mktemp /tmp/cht.sh.XXXXXXXXXXXXX) if curl -s https://cht.sh/:cht.sh > "$TMP2"; then