zdharma-continuum.zconvey/zc
2021-11-12 18:57:51 +01:00

199 lines
6.5 KiB
Bash

setopt localoptions extendedglob clobber
function __zconvey_usage_zc() {
__zconvey_pinfo2 "Sends specified commands to given (via ID or NAME) Zsh session"
__zconvey_pinfo "Usage: zc {-i ID}|{-n NAME} [-q|--quiet] [-v|--verbose] [-h|--help] [-a|--ask] [-r|--raw] COMMAND ARGUMENT ..."
print -- "-h/--help - this message"
print -- "-i ID / --id ID - ID (number) of Zsh session"
print -- "-n NAME / --name NAME - NAME of Zsh session"
print -- "-q/--quiet - don't output status messages"
print -- "-v/--verbose - output more status messages"
print -- "-a/--ask - ask for command (if not provided) and session (etc.)"
print -r -- "-r/--raw - raw input (for scripts) - a quoted string, e.g. ls \\'a\\ dir\\'"
}
local -A opthash
zparseopts -D -A opthash h -help q -quiet v -verbose i: -id: n: -name: a -ask r -raw || { __zconvey_usage_zc; return 1; }
integer have_id=0 have_name=0 verbose=0 quiet=0 ask=0 raw=0
local id name
# Help
(( ${+opthash[-h]} + ${+opthash[--help]} )) && { __zconvey_usage_zc; return 0; }
# ID
have_id=$(( ${+opthash[-i]} + ${+opthash[--id]} ))
(( ${+opthash[-i]} )) && id="${opthash[-i]}"
(( ${+opthash[--id]} )) && id="${opthash[--id]}"
# NAME
have_name=$(( ${+opthash[-n]} + ${+opthash[--name]} ))
(( ${+opthash[-n]} )) && name="${opthash[-n]}"
(( ${+opthash[--name]} )) && name="${opthash[--name]}"
# ASK (requests command and/or ID)
(( ask = ${+opthash[-a]} + ${+opthash[--ask]} ))
if [ "$ask" = "0" ]; then
local ask_setting
zstyle -b ":plugin:zconvey" ask ask_setting || ask_setting="no"
[ "$ask_setting" = "yes" ] && ask=1 || ask=0
fi
# VERBOSE, QUIET, RAW
(( verbose = ${+opthash[-v]} + ${+opthash[--verbose]} ))
(( quiet = ${+opthash[-q]} + ${+opthash[--quiet]} ))
(( raw = ${+opthash[-r]} + ${+opthash[--raw]} ))
if [[ "$have_id" != "0" && "$have_name" != "0" ]]; then
__zconvey_pinfo "Please supply only one of ID (-i) and NAME (-n)"
return 1
fi
if [[ "$have_id" != "0" && "$id" != <-> ]]; then
__zconvey_pinfo "ID must be numeric, 1..100"
return 1
fi
local cmd
if (( raw )); then
cmd="${*}"
else
local -a args
args=( "${(q)@}" )
cmd="${args[*]}"
cmd="${cmd//\\;/;}"
cmd="${cmd//\\|/|}"
cmd="${cmd//\\&/&}"
cmd="${cmd##[[:space:]]#}"
cmd="${cmd%%[[:space:]]#}"
fi
if [[ -z "$cmd" && "$ask" = "0" ]]; then
__zconvey_usage_zc
return 1
fi
if [[ "$have_id" = "0" && "$have_name" = "0" && "$ask" = "0" ]]; then
__zconvey_pinfo "Either supply target ID/NAME or request Zsh-Select (-a/--ask)"
return 1
fi
# Resolve name
if (( $have_name )); then
__zconvey_resolve_name_to_id "$name"
local resolved="$REPLY"
if [ -z "$resolved" ]; then
echo "Could not find session named: \`$name'"
return 1
fi
# Store the resolved ID and continue normally,
# with ID as the main specifier of session
id="$resolved"
fi
# Resupply missing input if requested
if (( $ask )); then
# Supply command?
if [[ -z "$cmd" ]]; then
vared -cp "Enter command to send: " cmd
cmd="${cmd##[[:space:]]#}"
cmd="${cmd%%[[:space:]]#}"
if [ -z "$cmd" ]; then
__zconvey_pinfo "No command enterred, exiting"
return 0
fi
fi
# Supply session?
if [[ "$have_id" = "0" && "$have_name" = "0" ]]; then
if ! type zsh-select 2>/dev/null 1>&2; then
__zconvey_pinfo "Zsh-Select not installed, please install it before using -a/--ask, aborting"
__zconvey_pinfo "Example installation: zinit load psprint/zsh-select"
return 1
else
export ZSHSELECT_START_IN_SEARCH_MODE=0
id=`zc-ls | zsh-select`
if [ -z "$id" ]; then
__zconvey_pinfo "No selection, exiting"
return 0
else
id="${id//(#b)*ID: ([[:digit:]]#)[^[:digit:]]#*(#e)/$match[1]}"
fi
fi
fi
fi
if [ -z "$cmd" ]; then
__zconvey_pinfo "No command provided, aborting"
return 1
fi
if [[ "$id" != <-> || "$id" = "0" || "$id" -gt "100" ]]; then
__zconvey_pinfo "Incorrect sesion ID occured: \`$idx'. ID should be in range 1..100"
return 1
fi
# Obtain current time stamp
local ts
if [ "$ZCONVEY_CONFIG[timestamp_from]" = "datetime" ]; then
[[ "${+modules}" = 1 && "${modules[zsh/datetime]}" != "loaded" && "${modules[zsh/datetime]}" != "autoloaded" ]] && zmodload zsh/datetime
[ "${+modules}" = 0 ] && zmodload zsh/datetime
ts="$EPOCHSECONDS"
fi
# Also a fallback
if [[ "$ZCONVEY_CONFIG[timestamp_from]" = "date" || -z "$ts" || "$ts" = "0" ]]; then
ts="$( date +%s )"
fi
local fd datafile="${ZCONVEY_IO_DIR}/${id}.io"
local lockfile="${datafile}.lock"
{ echo "PID $$ ID $ZCONVEY_ID is sending command" >! "$lockfile"; } 2>/dev/null
# 1. Zsh lock with timeout (2 seconds)
if (( ${ZCONVEY_CONFIG[use_zsystem_flock]} > 0 )); then
(( ${verbose} )) && print "Will use zsystem flock..."
if ! zsystem flock -t 2 -f fd "$lockfile"; then
__zconvey_pinfo2 "Communication channel of session $id is busy, could not send"
return 1
fi
# 2. Provided flock binary (two calls)
else
(( ${verbose} )) && print "Will use provided flock..."
exec {fd}>"$lockfile"
"${ZCONVEY_REPO_DIR}/myflock/flock" -nx "$fd"
if [ "$?" = "101" ]; then
(( ${verbose} )) && print "First attempt failed, will retry..."
LANG=C sleep 1
"${ZCONVEY_REPO_DIR}/myflock/flock" -nx "$fd"
if [ "$?" = "101" ]; then
__zconvey_pinfo2 "Communication channel of session $id is busy, could not send"
return 1
fi
fi
fi
# >> - multiple commands can be accumulated
print -r -- "$ts $cmd" >>! "$datafile"
# Release the lock by closing the lock file
exec {fd}>&-
if (( ${quiet} == 0 )); then
__zconvey_get_name_of_id "$id"
if [[ -n "$REPLY" ]]; then
print "Zconvey successfully sent command to \033[1;32msession $id\033[0m (name: \033[1;32m$REPLY\033[0m)"
else
print "Zconvey successfully sent command to \033[1;32msession $id\033[0m"
fi
local busyfile="$ZCONVEY_OTHER_DIR/${id}.busy"
[ -e "$busyfile" ] && print "The session is busy \033[1;33m($(<$busyfile))\033[0m (command will time out after ${ZCONVEY_CONFIG[expire_seconds]}s)"
integer is_locked
__zconvey_is_session_active "$id" && is_locked=1 || is_locked=0
(( is_locked == 0 )) && print "The session is ABSENT, the command will probably time out (after ${ZCONVEY_CONFIG[expire_seconds]}s)"
fi
# vim:ft=zsh