mirror of
https://github.com/eth-p/bat-extras.git
synced 2025-06-20 19:57:46 +02:00
batpipe: Add batpipe script
This commit is contained in:
parent
dd07f98367
commit
b85c7c43e3
18
lib/dirs.sh
Normal file
18
lib/dirs.sh
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# bat-extras | Copyright (C) 2021 eth-p | MIT License
|
||||||
|
#
|
||||||
|
# Repository: https://github.com/eth-p/bat-extras
|
||||||
|
# Issues: https://github.com/eth-p/bat-extras/issues
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Gets a configuration directory for a command-line program.
|
||||||
|
# Arguments:
|
||||||
|
# 1 -- The program name.
|
||||||
|
config_dir() {
|
||||||
|
if [[ -n "${XDG_CONFIG_HOME+x}" ]]; then
|
||||||
|
echo "${XDG_CONFIG_HOME}/$1"
|
||||||
|
else
|
||||||
|
echo "${HOME}/.config/$1"
|
||||||
|
fi
|
||||||
|
}
|
26
lib/path.sh
Normal file
26
lib/path.sh
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# bat-extras | Copyright (C) 2021 eth-p | MIT License
|
||||||
|
#
|
||||||
|
# Repository: https://github.com/eth-p/bat-extras
|
||||||
|
# Issues: https://github.com/eth-p/bat-extras/issues
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Gets the file extension from a file path.
|
||||||
|
# Arguments:
|
||||||
|
# 1 -- The file path.
|
||||||
|
extname() {
|
||||||
|
local file="$1"
|
||||||
|
echo ".${file##*.}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Strips trailing slashes from a file path.
|
||||||
|
# Arguments:
|
||||||
|
# 1 -- The file path.
|
||||||
|
strip_trailing_slashes() {
|
||||||
|
local file="$1"
|
||||||
|
while [[ -n "$file" && "${file: -1}" = "/" ]]; do
|
||||||
|
file="${file:0:$((${#file}-1))}"
|
||||||
|
done
|
||||||
|
echo "$file"
|
||||||
|
}
|
67
lib/proc.sh
Normal file
67
lib/proc.sh
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# bat-extras | Copyright (C) 2021 eth-p | MIT License
|
||||||
|
#
|
||||||
|
# Repository: https://github.com/eth-p/bat-extras
|
||||||
|
# Issues: https://github.com/eth-p/bat-extras/issues
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Gets the path to the parent executable file.
|
||||||
|
# Arguments:
|
||||||
|
# 1 -- The target pid. If not provided, the script's parent is used.
|
||||||
|
parent_executable() {
|
||||||
|
local target_pid="${1:-$PPID}"
|
||||||
|
ps -f -p "$target_pid" | tail -n 1 | awk '{for(i=8;i<=NF;i++) printf $i" "; printf "\n"}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Gets the PID of the parent executable file.
|
||||||
|
# Arguments:
|
||||||
|
# 1 -- The target pid. If not provided, the script's parent is used.
|
||||||
|
parent_executable_pid() {
|
||||||
|
local target_pid="${1:-$PPID}"
|
||||||
|
ps -f -p "$target_pid" | tail -n 1 | awk '{print $3}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Gets the path to the parent login shell.
|
||||||
|
# Arguments:
|
||||||
|
# 1 -- The target pid. If not provided, the script's parent is used.
|
||||||
|
parent_shell() {
|
||||||
|
local target_pid="${1:-$PPID}"
|
||||||
|
local target_name
|
||||||
|
while true; do
|
||||||
|
{
|
||||||
|
read -r target_pid
|
||||||
|
read -r target_name
|
||||||
|
|
||||||
|
# If the parent process starts with a "-", it's a login shell.
|
||||||
|
if [[ "${target_name:0:1}" = "-" ]]; then
|
||||||
|
target_name="$(cut -f1 -d' ' <<< "${target_name:1}")"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If the parent process has "*sh " followed by "-l", it's probably a login shell.
|
||||||
|
if [[ "$target_name" =~ sh\ .*-l ]]; then
|
||||||
|
target_name="$(cut -f1 -d' ' <<< "${target_name}")"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If the parent process is pid 0 (init), then we haven't found a parent shell.
|
||||||
|
# At this point, it's best to assume the shell is whatever is defined in $SHELL.
|
||||||
|
if [[ "$target_pid" -eq 0 ]]; then
|
||||||
|
target_name="$SHELL"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
} < <({
|
||||||
|
ps -f -p "$target_pid" \
|
||||||
|
| tail -n 1 \
|
||||||
|
| awk '{print $3; for(i=8;i<=NF;i++) printf $i" "; printf "\n"}'
|
||||||
|
})
|
||||||
|
done
|
||||||
|
|
||||||
|
# Ensure that the detected shell is an executable path.
|
||||||
|
if [[ -f "$target_name" ]]; then
|
||||||
|
echo "$target_name"
|
||||||
|
elif ! command -v "$target_name" 2>/dev/null; then
|
||||||
|
echo "$target_name" # It's not, but we have nothing else we can do here.
|
||||||
|
fi
|
||||||
|
}
|
231
src/batpipe.sh
Executable file
231
src/batpipe.sh
Executable file
@ -0,0 +1,231 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# bat-extras | Copyright (C) 2021 eth-p | MIT License
|
||||||
|
#
|
||||||
|
# Repository: https://github.com/eth-p/bat-extras
|
||||||
|
# Issues: https://github.com/eth-p/bat-extras/issues
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# EXTERNAL VIEWERS FOR BATPIPE:
|
||||||
|
#
|
||||||
|
# External viewers can be added to batpipe by creating bash scripts
|
||||||
|
# inside the `~/.config/batpipe/viewers.d/` directory.
|
||||||
|
#
|
||||||
|
# CREATING A VIEWER:
|
||||||
|
#
|
||||||
|
# Viewers must define two functions and append the viewer's name to the
|
||||||
|
# `BATPIPE_VIEWERS` array.
|
||||||
|
#
|
||||||
|
# - viewer_${viewer}_supports [file_path] [file_basename]
|
||||||
|
# If this returns 0, the viewer's process function will be used.
|
||||||
|
#
|
||||||
|
# - viewer_${viewer}_process [file_path] [inner_file_path]
|
||||||
|
#
|
||||||
|
# VIEWER API:
|
||||||
|
#
|
||||||
|
# $BATPIPE_VIEWERS -- An array of loaded file viewers.
|
||||||
|
# $BATPIPE_ENABLE_COLOR -- Whether color is supported. (`true`|`false`)
|
||||||
|
# $BATPIPE_INSIDE_LESS -- Whether batpipe is inside less. (`true`|`false`)
|
||||||
|
#
|
||||||
|
# batpipe_header [pattern] [...] -- Print a viewer header line.
|
||||||
|
# batpipe_subheader [pattern] [...] -- Print a viewer subheader line.
|
||||||
|
#
|
||||||
|
# strip_trailing_slashes [path] -- Strips trailing slashes from a path.
|
||||||
|
#
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# shellcheck disable=SC1090 disable=SC2155
|
||||||
|
SELF="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo ".")")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
|
||||||
|
LIB="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo ".")")/../lib" && pwd)"
|
||||||
|
source "${LIB}/constants.sh"
|
||||||
|
source "${LIB}/dirs.sh"
|
||||||
|
source "${LIB}/str.sh"
|
||||||
|
source "${LIB}/print.sh"
|
||||||
|
source "${LIB}/path.sh"
|
||||||
|
source "${LIB}/proc.sh"
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Usage/Install:
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [[ "$#" -eq 0 ]]; then
|
||||||
|
# If writing to a terminal, display instructions and help.
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
printc "%{DIM}# %s, %s.\n# %s\n# %s\n# %s\n# \n# %s%{CLEAR}\n" \
|
||||||
|
"$PROGRAM" \
|
||||||
|
"a bat-based preprocessor for less and bat" \
|
||||||
|
"Version: $PROGRAM_VERSION" \
|
||||||
|
"Homepage: $PROGRAM_HOMEPAGE" \
|
||||||
|
"$PROGRAM_COPYRIGHT" \
|
||||||
|
"To use $PROGRAM, eval the output of this command in your shell init script."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect the shell.
|
||||||
|
#
|
||||||
|
# This will directly check if the parent is fish, since there's a
|
||||||
|
# good chance that `bash` or `sh` will be invoking fish.
|
||||||
|
if [[ "$(basename -- "$(parent_executable | cut -f1 -d' ')")" == "fish" ]]; then
|
||||||
|
detected_shell="fish"
|
||||||
|
else
|
||||||
|
detected_shell="$(parent_shell)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Print the commands required to add `batpipe` to the environment variables.
|
||||||
|
case "$(basename -- "${detected_shell:bash}")" in
|
||||||
|
fish) # Fish
|
||||||
|
printc '%{YELLOW}set -x %{CLEAR}LESSOPEN %{CYAN}"|%q %%s"%{CLEAR};\n' "$SELF"
|
||||||
|
printc '%{YELLOW}set -e %{CLEAR}LESSCLOSE;\n'
|
||||||
|
;;
|
||||||
|
*) # Bash-like
|
||||||
|
printc '%{YELLOW}LESSOPEN=%{CYAN}"|%s %%s"%{CLEAR};\n' "$SELF"
|
||||||
|
printc '%{YELLOW}export%{CLEAR} LESSOPEN\n' "$SELF"
|
||||||
|
printc '%{YELLOW}unset%{CLEAR} LESSCLOSE;\n'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Init:
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [[ "$(basename -- "$(parent_executable "$(parent_executable_pid)"|cut -f1 -d' ')")" == less ]]; then
|
||||||
|
BATPIPE_INSIDE_LESS=true
|
||||||
|
else
|
||||||
|
BATPIPE_INSIDE_LESS=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Viewers:
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
BATPIPE_VIEWERS=("ls" "tar")
|
||||||
|
|
||||||
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
viewer_ls_supports() {
|
||||||
|
[[ -d "$1" ]]
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
viewer_ls_process() {
|
||||||
|
local dir="$(strip_trailing_slashes "$1")"
|
||||||
|
batpipe_header "Viewing contents of directory: %{PATH}%s" "$dir"
|
||||||
|
ls -lA "$1"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
viewer_tar_supports() {
|
||||||
|
command -v "tar" &> /dev/null || return 1
|
||||||
|
|
||||||
|
case "$2" in
|
||||||
|
*.tar | *.tar.*) return 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
viewer_tar_process() {
|
||||||
|
if [[ -n "$2" ]]; then
|
||||||
|
batpipe_header "Archived file: %{SUBPATH}%s%{PATH}/%s" "$1" "$2"
|
||||||
|
tar -xf "$1" -O "$2" 2>&1
|
||||||
|
else
|
||||||
|
batpipe_header "Viewing contents of archive: %{PATH}%s" "$1"
|
||||||
|
batpipe_subheader "To view files within the archive, add the file path after the archive."
|
||||||
|
tar -tvf "$1"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Functions:
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Print a header for batpipe messages.
|
||||||
|
# Arguments:
|
||||||
|
# 1 -- The printc formatting string.
|
||||||
|
# ... -- The printc formatting arguments.
|
||||||
|
batpipe_header() {
|
||||||
|
local pattern="${1//%{C\}/%{C\}%{HEADER\}}"
|
||||||
|
printc "%{HEADER}==> $pattern%{C}\n" "${@:2}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Print a subheader for batpipe messages.
|
||||||
|
# Arguments:
|
||||||
|
# 1 -- The printc formatting string.
|
||||||
|
# ... -- The printc formatting arguments.
|
||||||
|
batpipe_subheader() {
|
||||||
|
local pattern="${1//%{C\}/%{C\}%{SUBHEADER\}}"
|
||||||
|
printc "%{SUBHEADER}==> $pattern%{C}\n" "${@:2}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Colors:
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
printc_init "[DEFINE]" << END
|
||||||
|
C \x1B[0m
|
||||||
|
SUBPATH \x1B[2;35m
|
||||||
|
PATH \x1B[0;35m
|
||||||
|
HEADER \x1B[0;36m
|
||||||
|
SUBHEADER \x1B[2;36m
|
||||||
|
END
|
||||||
|
|
||||||
|
# Enable color output if:
|
||||||
|
# - Parent is not less OR BATPIPE=color; AND
|
||||||
|
# - NO_COLOR is not defined.
|
||||||
|
#
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
if [[ "$BATPIPE_INSIDE_LESS" == "false" || "$BATPIPE" == "color" ]] && [[ -z "${NO_COLOR+x}" ]]; then
|
||||||
|
BATPIPE_ENABLE_COLOR=true
|
||||||
|
printc_init true
|
||||||
|
else
|
||||||
|
BATPIPE_ENABLE_COLOR=false
|
||||||
|
printc_init false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Main:
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
__CONFIG_DIR="$(config_dir batpipe)"
|
||||||
|
__TARGET_INSIDE=""
|
||||||
|
__TARGET_FILE="$(strip_trailing_slashes "$1")"
|
||||||
|
|
||||||
|
# Determine the target file by walking upwards from the specified path.
|
||||||
|
# This allows inner paths of archives to be used.
|
||||||
|
while ! [[ -e "$__TARGET_FILE" ]]; do
|
||||||
|
__TARGET_INSIDE="$(basename -- "${__TARGET_FILE}")/${__TARGET_INSIDE}"
|
||||||
|
__TARGET_FILE="$(dirname "${__TARGET_FILE}")"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove trailing slash of the inner target path.
|
||||||
|
__TARGET_INSIDE="$(strip_trailing_slashes "$__TARGET_INSIDE")"
|
||||||
|
__TARGET_BASENAME="$(basename -- "$__TARGET_FILE")"
|
||||||
|
|
||||||
|
# Stop bat from calling this recursively.
|
||||||
|
unset LESSOPEN
|
||||||
|
unset LESSCLOSE
|
||||||
|
|
||||||
|
# Load external viewers.
|
||||||
|
if [[ -d "${__CONFIG_DIR}/viewers.d" ]]; then
|
||||||
|
unset LIB
|
||||||
|
unset SELF
|
||||||
|
|
||||||
|
shopt -o nullglob
|
||||||
|
for viewer_script in "${__CONFIG_DIR}/viewers.d"/*; do
|
||||||
|
source "${viewer_script}"
|
||||||
|
done
|
||||||
|
shopt -u nullglob
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try opening the file with the first viewer that supports it.
|
||||||
|
for viewer in "${BATPIPE_VIEWERS[@]}"; do
|
||||||
|
if "viewer_${viewer}_supports" "$__TARGET_FILE" "$__TARGET_BASENAME" 1>&2; then
|
||||||
|
"viewer_${viewer}_process" "$__TARGET_FILE" "$__TARGET_INSIDE"
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# No supported viewer. Just pass it through.
|
||||||
|
exit 1
|
Loading…
x
Reference in New Issue
Block a user