e22c5f7e75
This commit simplifies the caching of desktop entries. Previously spmenu_run would cache each desktop entry into a cache directory, of course using it's own format and then parses that yet again to add it to the arrays. This is terribly inefficient and wastes the user's valuable space. With this commit, the entries are parsed and directly written to the six cache files, which spmenu can then grab from in plain text, without any parsing. Significantly speeds up the caching and saves disk space.
740 lines
28 KiB
Bash
Executable file
740 lines
28 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# spmenu_run
|
|
# Feature rich run launcher, file lister and .desktop launcher for spmenu
|
|
#
|
|
# See LICENSE file for copyright and license details.
|
|
|
|
# Set basic variables, in case the config isn't valid, env variables and config file can override these
|
|
CONFDIR="${CONFDIR:-${XDG_CONFIG_HOME:-$HOME/.config}}"
|
|
TERMINAL="${TERMINAL:-st -e}"
|
|
BROWSER="${BROWSER:-xdg-open}"
|
|
TORRENT="${TORRENT:-qbittorrent}"
|
|
PDF_READER="${PDF_READER:-zathura}"
|
|
EDITOR="${EDITOR:-nvim}"
|
|
PLAYER="${PLAYER:-mpv}"
|
|
GENERIC="${GENERIC:-$TERMINAL -e $EDITOR}"
|
|
WEB_GREP="${WEB_GREP:-http:|https:|www[.]}"
|
|
MAGNET_GREP="${MAGNET_GREP:-magnet:?}"
|
|
HISTORY="${HISTORY:-${XDG_CACHE_HOME:-$HOME/.cache/}/spmenu_run.hist}"
|
|
RUNLAUNCHER="${RUNLAUNCHER:-spmenu}"
|
|
PREFIX="${PREFIX:-/usr}"
|
|
DESTDIR="${DESTDIR:-}"
|
|
STDOUT="${STDOUT:-false}"
|
|
SORT_BY_NUMBER="${SORT_BY_NUMBER:-true}"
|
|
SORT_IN_REVERSE="${SORT_IN_REVERSE:-true}"
|
|
SORT_BY_RECENT="${SORT_BY_RECENT:-false}"
|
|
SORT_ARGS="${SORT_ARGS:-}"
|
|
UNIQ_ARGS="${UNIQ_ARGS:-}"
|
|
HIDDEN_KEYWORDS="${HIDDEN_KEYWORDS:-spmenu}"
|
|
KEYWORDS="${KEYWORDS:-}"
|
|
DISPLAY_DUPLICATES="${DISPLAY_DUPLICATES:-false}"
|
|
DISPLAY_DESCRIPTION="${DISPLAY_DESCRIPTION:-true}"
|
|
VALIDATE_ENTRIES="${VALIDATE_ENTRIES:-true}"
|
|
LS_ARGS="${LS_ARGS:- --color=always}"
|
|
USE_FULL_PATH="${USE_FULL_PATH:-false}"
|
|
HELP_COLOR="${HELP_COLOR:-#FFFF00}"
|
|
DESCRIPTION_COLOR="${DESCRIPTION_COLOR:-#999888}"
|
|
DMENU_COMPAT="${DMENU_COMPAT:-false}"
|
|
AUTOREFRESH="${AUTOREFRESH:-false}"
|
|
MULTISELECT="${MULTISELECT:-true}"
|
|
|
|
DESKTOP_DIR="${DESKTOP_DIR:-${DESTDIR}${PREFIX}/share/applications ${HOME}/.local/share/applications}"
|
|
ICON_DIR="${ICON_DIR:-${DESTDIR}${PREFIX}/share/icons/hicolor ${HOME}/.local/share/icons/hicolor ${DESTDIR}${PREFIX}/share/pixmaps}"
|
|
IMAGE="${IMAGE:-true}"
|
|
LOGFILE="${LOGFILE:-/tmp/spmenu_run.log}"
|
|
|
|
TITLEFILE="${TITLEFILE:-${XDG_CACHE_HOME:-$HOME/.cache}/.title}"
|
|
EXECFILE="${EXECFILE:-${XDG_CACHE_HOME:-$HOME/.cache}/.exec}"
|
|
ICONFILE="${ICONFILE:-${XDG_CACHE_HOME:-$HOME/.cache}/.icon}"
|
|
DESCFILE="${DESCFILE:-${XDG_CACHE_HOME:-$HOME/.cache}/.desc}"
|
|
FILEFILE="${FILEFILE:-${XDG_CACHE_HOME:-$HOME/.cache}/.file}"
|
|
TERMFILE="${TERMFILE:-${XDG_CACHE_HOME:-$HOME/.cache}/.term}"
|
|
|
|
# arrays containing entries
|
|
declare -a it_title
|
|
declare -a it_icon
|
|
declare -a it_exec
|
|
declare -a it_file
|
|
declare -a it_desc
|
|
declare -a it_term
|
|
|
|
# arrays containing arguments
|
|
declare -a rl_fm
|
|
declare -a rl_run
|
|
declare -a rl_desktop
|
|
declare -a rl_help
|
|
declare -a rl_ex
|
|
declare -a ls_args
|
|
declare -a sort_args
|
|
declare -a uniq_args
|
|
|
|
# directories
|
|
declare -a desktopdir
|
|
declare -a icondir
|
|
|
|
fail_mac() {
|
|
[ -f "/usr/local/share/spmenu/allowusageonmac" ] && return
|
|
cat << EOF
|
|
spmenu_run does not support macOS due to the outdated Bash version.
|
|
See https://spmenu.speedie.site/Using+spmenu+on+macOS for more information.
|
|
|
|
If you wish to ignore this warning anyway, touch /usr/local/share/spmenu/allowusageonmac.
|
|
EOF
|
|
exit 1
|
|
}
|
|
|
|
check() {
|
|
[ ! -d "$CONFDIR/spmenu/run" ] && mkdir -p "$CONFDIR/spmenu/run"
|
|
if [ ! -f "$CONFDIR/spmenu/run/.first_run" ]; then
|
|
print_help
|
|
touch "$CONFDIR/spmenu/run/.first_run"
|
|
fi
|
|
}
|
|
|
|
check_desktop() {
|
|
[ ! -d "$CONFDIR/spmenu/run" ] && mkdir -p "$CONFDIR/spmenu/run"
|
|
if [ ! -f "$CONFDIR/spmenu/run/.first_run" ]; then
|
|
print_desktop_help
|
|
touch "$CONFDIR/spmenu/run/.first_run"
|
|
fi
|
|
}
|
|
|
|
path() {
|
|
[ "$SORT_BY_NUMBER" != "false" ] && NUMBERSORTARG="-n"
|
|
[ "$SORT_IN_REVERSE" != "false" ] && REVERSESORTARG="-r"
|
|
SORT_ARGS="$NUMBERSORTARG $REVERSESORTARG $SORT_ARGS"
|
|
|
|
read -ra uniq_args <<< "${UNIQ_ARGS}"
|
|
read -ra sort_args <<< "${SORT_ARGS}"
|
|
|
|
HIDDEN_KEYWORDS="${HIDDEN_KEYWORDS:-NULL_ENTRY}"
|
|
|
|
print_menu() {
|
|
print() {
|
|
printf "%s\n" "$PATH" | \
|
|
tr ':' '\n' | \
|
|
sed 's#$#/#' | \
|
|
xargs ls -lu --time-style=+%s 2>&1 | \
|
|
grep -vE "$HIDDEN_KEYWORDS" | \
|
|
grep -E "$KEYWORDS"
|
|
}
|
|
|
|
if [ "$SORT_BY_RECENT" != "false" ]; then
|
|
print | awk '/^(-|l)/ { print $6, $7 }' | sort "${sort_args[@]}" | cut -d' ' -f 2 2>&1
|
|
else
|
|
print | awk '/^(-|l)/ { print $7 }' | sort "${sort_args[@]}" | cut -d' ' -f 2 2>&1
|
|
fi
|
|
}
|
|
|
|
# uniq
|
|
if [ "$DISPLAY_DUPLICATES" != "false" ]; then
|
|
print_menu
|
|
else
|
|
print_menu | uniq "${uniq_args[@]}"
|
|
fi
|
|
|
|
command -v run_pre_func && run_pre_func
|
|
}
|
|
|
|
print_help() {
|
|
if [ "$DMENU_COMPAT" != "true" ]; then
|
|
COL='\033[0;31m'
|
|
RUNLAUNCHER_EX_ARGS="--lines 20 --columns 1 --normal --sgr1 $HELP_COLOR --hide-cursor --no-allow-typing --no-color-items --hide-prompt --hide-powerline --hide-input --hide-right-arrow --hide-left-arrow --hide-mode --hide-match-count --hide-caps"
|
|
read -ra rl_ex <<< "$RUNLAUNCHER_EX_ARGS"
|
|
fi
|
|
cat << EOF | $RUNLAUNCHER "${rl_help[@]}" "${rl_ex[@]}" > /dev/null
|
|
Start typing in keywords to list out entries. Press Enter to select an entry. The selected entry will be run through a shell.
|
|
To set spmenu options, you pass arguments to 'spmenu_run' directly. See 'spmenu --help' for a list of valid arguments.
|
|
To configure spmenu, you may also copy ${DESTDIR}${PREFIX}/share/spmenu/example.Xresources to $CONFDIR/spmenu/spmenurc and edit that.
|
|
|
|
- Type in '?' to show this help screen at any time.
|
|
- If the entry selected starts with 'www', it will instead be treated as a link and spawned in a web browser (\$BROWSER)
|
|
- If the entry selected starts with 'magnet', it will instead be treated as a magnet link and spawned in a torrent client (\$TORRENT)
|
|
- If the entry selected starts with '?' followed by a valid command, it will be opened as a man page in spmenu.
|
|
- If the entry starts with '#' followed by a valid command, it will be opened in the defined terminal emulator.
|
|
|
|
$(printf "%b" "${COL}")Note: This may also be displayed if you deleted your spmenu configuration directory.
|
|
EOF
|
|
}
|
|
|
|
print_cli_help() {
|
|
cat << EOF
|
|
spmenu_run - Run launcher for spmenu
|
|
|
|
spmenu_run -x, --run List entries in \$PATH.
|
|
spmenu_run -fm, --fm <dir> List files and directories in <dir>
|
|
spmenu_run -d, --desktop List .desktop entries.
|
|
spmenu_run -p, --full-path Print the full path to the file selected (-fm)
|
|
spmenu_run -np, --no-full-path Don't print the full path to the file selected (-fm)
|
|
spmenu_run -cc, --clear-cache Clear cache, useful if you just installed/uninstalled a program (-d)
|
|
spmenu_run -dm, --dmenu Run spmenu_run using dmenu instead of spmenu
|
|
spmenu_run -ndm, --no-dmenu Run spmenu_run using spmenu instead of dmenu
|
|
spmenu_run -h, --help Print this help
|
|
spmenu_run -o, --stdout Print to standard input and do not execute the selected item
|
|
spmenu_run -no, --no-stdout Don't print to standard input, execute the selected item
|
|
spmenu_run -a, --args <args> Pass <args> to spmenu
|
|
|
|
See spmenu(1) and spmenu_run(1) for more information.
|
|
EOF
|
|
}
|
|
|
|
print_config() {
|
|
[ -f "$CONFDIR/spmenu/run/config" ] && . "$CONFDIR/spmenu/run/config" && return
|
|
mkdir -p "$CONFDIR/spmenu/run"
|
|
cat << EOF > "$CONFDIR/spmenu/run/config"
|
|
# spmenu_run default configuration file
|
|
#
|
|
# This is the configuration file for the run launcher spmenu comes with.
|
|
# It is not the configuration file for spmenu, see ~/.config/spmenu/spmenu.conf for that.
|
|
#
|
|
# spmenu_run also runs these functions:
|
|
#
|
|
# 'run_pre_func' before spawning spmenu.
|
|
# 'run_post_func' after spawning spmenu, selected item passed as an argument.
|
|
# 'desktop_pre_func' before spawning spmenu.
|
|
# 'desktop_post_func' after spawning spmenu, selected item passed as an argument.
|
|
# 'fm_pre_func' before spawning spmenu.
|
|
# 'fm_post_func' after spawning spmenu, selected item passed as an argument.
|
|
# 'fm_pre_list_func' right before listing out files.
|
|
# 'fm_post_list_func' right after listing out files.
|
|
# 'fm_dir_func' before changing directory to the selected directory.
|
|
# 'fm_line_func' for each line in ls output, the line is passed as an argument, including SGR colors.
|
|
# 'read_man' when reading a man page, selected item passed as an argument.
|
|
#
|
|
# You may create those functions below.
|
|
#
|
|
# For example, to implement a basic history file:
|
|
#
|
|
# run_post_func() {
|
|
# rm -f /tmp/spmenu_entryhist; printf "\$1\n" >> /tmp/spmenu_entryhist
|
|
# }
|
|
#
|
|
# You can use anything POSIX compliant shells and Bash support, as well as programs available on the system.
|
|
|
|
# misc software
|
|
TERMINAL="\${TERMINAL:-st -e}" # Terminal commands are spawned in
|
|
BROWSER="\${BROWSER:-xdg-open}" # Web browser, for URLs
|
|
TORRENT="\${TORRENT:-qbittorrent}" # Torrent client, for magnet links
|
|
PDF_READER="\${PDF_READER:-zathura}" # PDF reader, for file management
|
|
EDITOR="\${EDITOR:-nvim}" # Editor, used to open documents
|
|
PLAYER="\${PLAYER:-mpv}" # Player, used to play audio/video
|
|
GENERIC="\${GENERIC:-\$TERMINAL -e \$EDITOR}" # Generic, used to open unknown files
|
|
WEB_GREP="http:|https:|www[.]" # Needs to be in grep -E syntax
|
|
MAGNET_GREP="magnet:?" # Needs to be in grep -E syntax
|
|
HISTORY="\${XDG_CACHE_HOME:-\$HOME/.cache/}/spmenu_run.hist" # History file, spmenu (meaning your user) must have permission to read and write to it.
|
|
|
|
# run launcher options
|
|
RUNLAUNCHER="\${RUNLAUNCHER:-spmenu}" # Run launcher to use
|
|
RUNLAUNCHER_RUN_ARGS="" # Extra rguments passed to \$RUNLAUNCHER when using the run launcher
|
|
RUNLAUNCHER_DESKTOP_ARGS="" # Extra rguments passed to \$RUNLAUNCHER when using the .desktop launcher
|
|
RUNLAUNCHER_FM_ARGS="--lines 40" # Extra arguments passed to \$RUNLAUNCHER when using the file manager
|
|
RUNLAUNCHER_HELP_ARGS="" # Extra arguments passed to \$RUNLAUNCHER when using the help
|
|
DMENU_COMPAT="false" # Enable dmenu compatibility (true/false)
|
|
|
|
# sorting
|
|
SORT_BY_NUMBER="true" # Sort by numbers
|
|
SORT_IN_REVERSE="true" # Sort in reverse
|
|
SORT_BY_RECENT="false" # Sort by recent
|
|
SORT_ARGS="" # Extra arguments passed to the sort command.
|
|
|
|
# keywords
|
|
HIDDEN_KEYWORDS="spmenu" # Keywords that will be ignored, needs to be in grep -vE syntax.
|
|
KEYWORDS="" # Keywords that will be matched, needs to be in grep -E syntax.
|
|
|
|
# misc
|
|
STDOUT="false" # Print to stdout and exit (true/false)
|
|
DISPLAY_DUPLICATES="false" # Display duplicates or not
|
|
DEFAULT_FEATURE="run" # spmenu_run default feature (run/fm/desktop/help)
|
|
HELP_COLOR="#FFFF00"
|
|
|
|
# .desktop options
|
|
DESKTOP_DIR="\${DESTDIR}\${PREFIX}/share/applications \${HOME}/.local/share/applications" # Directories for .desktop entries
|
|
ICON_DIR="\${DESTDIR}\${PREFIX}/share/icons/hicolor \${HOME}/.local/share/icons/hicolor \${DESTDIR}\${PREFIX}/share/pixmaps" # Directories for icons defined in the entries
|
|
HIDDEN_ENTRY_KEYWORDS="\$HIDDEN_KEYWORDS" # Keywords that will be ignored, needs to be in grep -vE syntax.
|
|
ENTRY_KEYWORDS="\$KEYWORDS" # Keywords that will be matched, needs to be in grep -E syntax.
|
|
AUTOREFRESH="false" # Refresh (clear) cache if there are more entries available than cached. May cause cache to be cleared every time in some cases. (true/false)
|
|
MULTISELECT="true" # Allow handling multiple items, if set to false only the first line/selected item will be used.
|
|
IMAGE="true" # Display images (true/false)
|
|
VALIDATE_ENTRIES="true" # Validate entries using desktop-file-validate (true/false)
|
|
DISPLAY_DESCRIPTION="true" # Display description (true/false)"
|
|
DESCRIPTION_COLOR="#999888" # Description text color
|
|
LOGFILE="/tmp/spmenu_run.log" # Log file
|
|
TITLEFILE="\${TITLEFILE:-\${XDG_CACHE_HOME:-\$HOME/.cache}/.title}" # File containing the different titles to display.
|
|
DESCFILE="\${DESCFILE:-\${XDG_CACHE_HOME:-\$HOME/.cache}/.desc}" # File containing the description to display
|
|
EXECFILE="\${EXECFILE:-\${XDG_CACHE_HOME:-\$HOME/.cache}/.exec}" # File containing the different executables to run.
|
|
ICONFILE="\${ICONFILE:-\${XDG_CACHE_HOME:-\$HOME/.cache}/.icon}" # File containing the paths to the icons to display.
|
|
FILEFILE="\${FILEFILE:-\${XDG_CACHE_HOME:-\$HOME/.cache}/.file}" # File containing the path to the .desktop entries.
|
|
|
|
# file management
|
|
DEFAULT_DIRECTORY="\$(pwd)" # Directory to start -fm if none is specified.
|
|
LS_ARGS="\${LS_ARGS:- --color=always}" # Arguments passed to /bin/ls
|
|
USE_FULL_PATH="true" # Return full path (true/false)
|
|
|
|
# function to read the man page in spmenu
|
|
read_man() {
|
|
man "\$1" | \\
|
|
col -b | \\
|
|
\${RUNLAUNCHER:-spmenu} --lines 40 --columns 1 -p "man \$1"
|
|
}
|
|
EOF
|
|
[ -f "$CONFDIR/spmenu/run/config" ] && . "$CONFDIR/spmenu/run/config" && return
|
|
}
|
|
|
|
parse() {
|
|
path | sed "s/\&/\&/g" | $RUNLAUNCHER "${rl_run[@]}" > /tmp/spmenu_out
|
|
|
|
while read -r sout; do
|
|
# parse
|
|
case "${sout:0:1}" in
|
|
"#") EXEC="term" ;;
|
|
"?") EXEC="man" ;;
|
|
esac
|
|
|
|
printf "Run launcher output: '%s'\n" "$sout" >> "$LOGFILE"
|
|
|
|
case "$(printf "%s" "$sout" | awk '{ print $1 }')" in
|
|
"magnet") EXEC=torrent ;;
|
|
"www") EXEC=web ;;
|
|
"?") ;;
|
|
esac
|
|
|
|
if [ "$sout" = "?" ]; then
|
|
print_help "$@"
|
|
parse
|
|
exec_cmd
|
|
fi
|
|
|
|
printf "Type: '%s'\n" "$EXEC" >> "$LOGFILE"
|
|
|
|
# check for keywords
|
|
printf "%s" "$sout" | grep -qE "$WEB_GREP" && EXEC=web
|
|
printf "%s" "$sout" | grep -qE "$MAGNET_GREP" && EXEC=torrent
|
|
|
|
exec_cmd "$args"
|
|
[ "$MULTISELECT" != "true" ] && break
|
|
done < /tmp/spmenu_out
|
|
}
|
|
|
|
exec_cmd() {
|
|
[ -z "$EXEC" ] && EXEC=shell
|
|
[ "$STDOUT" != "false" ] && printf "%s\n" "$sout" && exit 1
|
|
command -v run_post_func > /dev/null && run_post_func "$sout"
|
|
|
|
# when there's no read_man func because the user is retarded and removed it, this basic function will be called instead
|
|
read_nman() {
|
|
$TERMINAL -e man "$1"
|
|
}
|
|
|
|
# execute it
|
|
case "$EXEC" in
|
|
"shell") printf "%s" "$sout" | sed "s/#//g" | ${SHELL:-"/bin/sh"} & ;;
|
|
"term") $TERMINAL -e "$(printf "%s" "$sout" | sed "s/#//g")" & ;;
|
|
"web") $BROWSER "$(printf "%s" "$sout" | sed "s/www //g")" & ;;
|
|
"torrent") $TORRENT "$(printf "%s" "$sout" | sed "s/magnet //g")" & ;;
|
|
"man") exec="$(printf "%s" "$sout" | sed "s/?//g")"
|
|
[ -x "$(command -v "$exec")" ] || return
|
|
if [ "$(command -v read_man)" ]; then
|
|
read_man "$exec"
|
|
return
|
|
else
|
|
read_nman "$exec"
|
|
return
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
|
|
remove_arg() {
|
|
args="$(printf "%s\n" "$args" | sed "s|$1||g")"
|
|
}
|
|
|
|
read_args() {
|
|
function="${DEFAULT_FEATURE:-run}" # default functionality
|
|
dir="${DEFAULT_DIRECTORY:-$(pwd)}" # default directory
|
|
args="$(printf "%s\n" "$@")"
|
|
argc="$(printf "%s\n" "$@" | wc -l)"
|
|
|
|
while true; do
|
|
i=$((i+1))
|
|
arg="$(printf "%s\n" "$args" | sed "${i}q;d")"
|
|
narg="$(printf "%s\n" "$args" | sed "$((i+1))q;d")"
|
|
|
|
case "$arg" in
|
|
-x|-run|--run) remove_arg "$arg" && function=run ;;
|
|
-o|-stdout|--stdout) remove_arg "$arg" && STDOUT=true ;;
|
|
-no|-no-stdout) remove_arg "$arg" && STDOUT=false ;;
|
|
-f|-fm|--fm) remove_arg "$arg"
|
|
[ -d "$narg" ] && dir="$narg" && remove_arg "$narg"
|
|
function=fm
|
|
;;
|
|
-c|-cc|--clear-cache) remove_arg "$arg"
|
|
clearcache="true"
|
|
;;
|
|
-d|-desktop|--desktop) remove_arg "$arg"
|
|
function=desktop
|
|
;;
|
|
-p|-full-path|--full-path) remove_arg "$arg"
|
|
USE_FULL_PATH="true"
|
|
;;
|
|
-np|-no-full-path|--no-full-path) remove_arg "$arg"
|
|
USE_FULL_PATH="false"
|
|
;;
|
|
-dm|-dmenu|--dmenu) remove_arg "$arg"
|
|
DMENU_COMPAT="true"
|
|
;;
|
|
-ndm|-no-dmenu|--no-dmenu) remove_arg "$arg"
|
|
DMENU_COMPAT="false"
|
|
;;
|
|
-a|-args|--args) remove_arg "$arg"
|
|
[ -z "$narg" ] && printf "You must specify a list of arguments to pass to %s.\n" "$RUNLAUNCHER" && exit 1
|
|
|
|
remove_arg "$narg"
|
|
MARGS="$narg"
|
|
;;
|
|
-h|--help) remove_arg "$arg" && function=help
|
|
;;
|
|
"") :
|
|
;;
|
|
*) printf "spmenu_run: Invalid argument: '%s'. If you meant to pass this to spmenu, use the '%s' option.\n" "$arg" "-a"
|
|
;;
|
|
esac
|
|
|
|
[ "$argc" = "$i" ] && break
|
|
done
|
|
|
|
args="$(printf "%s\n" "$*")"
|
|
}
|
|
|
|
exec_file() {
|
|
[ "$USE_FULL_PATH" != "false" ] && DIR="$(pwd)/"
|
|
[ "$STDOUT" != "false" ] && printf "%s%s\n" "${DIR}" "$1" && return 0
|
|
command -v fm_post_func > /dev/null && fm_post_func "$1"
|
|
|
|
# some default basic parsing
|
|
case "$1" in
|
|
*.html|*.htm) $BROWSER "$1" ;;
|
|
*.pdf) $PDF_READER "$1" ;;
|
|
*.flac|*.mp3|*.wav|*.ogg) $PLAYER "$1" ;;
|
|
*.mp4|*.mov|*.mkv) $PLAYER "$1" ;;
|
|
*.theme) $EDITOR "$1" ;;
|
|
*)
|
|
if [ -x "$1" ]; then
|
|
$TERMINAL -e "$1"
|
|
else
|
|
$GENERIC "$1"
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
|
|
prepare_dirnav() {
|
|
[ ! -d "$dir" ] && return 1
|
|
cd "$dir" || printf "Invalid directory.. somehow\n"
|
|
|
|
# TODO: read line by line so we can append/prepend whatever we want
|
|
listing() {
|
|
command -v fm_pre_list_func > /dev/null && fm_pre_list_func
|
|
ls "${ls_args[@]}" > /tmp/spmenu_ls_list # this allows us SGR colors
|
|
printf "..\n" >> /tmp/spmenu_ls_list
|
|
|
|
while read -r l; do
|
|
command -v fm_line_func > /dev/null && fm_line_func "$(pwd)/$l"
|
|
printf "%s\n" "$l"
|
|
done < "/tmp/spmenu_ls_list"; rm -f /tmp/spmenu_ls_list
|
|
|
|
command -v fm_post_list_func > /dev/null && fm_post_list_func
|
|
}
|
|
|
|
command -v fm_pre_func > /dev/null && fm_pre_func
|
|
|
|
listing | $RUNLAUNCHER "${rl_fm[@]}" | sed -e 's/\x1b\[[0-9;]*m//g' > /tmp/spmenu_out
|
|
|
|
while read -r dir; do
|
|
case "$dir" in
|
|
*)
|
|
if [ -d "$dir" ]; then
|
|
dir="$(pwd)/$dir"
|
|
command -v fm_dir_func > /dev/null && fm_dir_func "$dir"
|
|
prepare_dirnav
|
|
elif [ -f "$dir" ]; then
|
|
exec_file "$dir"
|
|
else
|
|
return 1
|
|
fi
|
|
;;
|
|
esac
|
|
[ "$MULTISELECT" != "true" ] && break
|
|
done < /tmp/spmenu_out; rm -f /tmp/spmenu_out
|
|
}
|
|
|
|
print_desktop_help() {
|
|
if [ "$DMENU_COMPAT" != "true" ]; then
|
|
COL='\033[0;31m'
|
|
RUNLAUNCHER_EX_ARGS="--lines 20 --columns 1 --normal --sgr1 $HELP_COLOR --hide-cursor --hide-caps --no-allow-typing --no-color-items --hide-prompt --hide-powerline --hide-input --hide-right-arrow --hide-left-arrow --hide-mode --hide-match-count"
|
|
read -ra rl_ex <<< "${RUNLAUNCHER_EX_ARGS}"
|
|
fi
|
|
cat << EOF | $RUNLAUNCHER "${rl_help[@]}" "${rl_ex[@]}" > /dev/null
|
|
Start typing in keywords to list out entries. Press Enter to select an entry. The selected entry will be run through a shell.
|
|
To set spmenu options, you modify \$RUNLAUNCHER_ARGS in the config. See 'spmenu --help' for a list of valid arguments to add to the variable.
|
|
To configure spmenu itself, you may copy ${DESTDIR}${PREFIX}/share/spmenu.conf to ~/.config/spmenu/spmenu.conf.
|
|
|
|
By default, spmenu_run will cache entries for speed reasons. You can find these entries in ~/.config/spmenu/run/cache.
|
|
If you make changes to .desktop files (not new entries, modified old entries), you need to clear the cache for the changes to appear. Simply delete the directory to do this.
|
|
|
|
- Type in '?' to show this help screen at any time.
|
|
|
|
$(printf "%b" "$COL")Note: This may also be displayed if you deleted your spmenu configuration directory.
|
|
EOF
|
|
}
|
|
|
|
print_desktop_list() {
|
|
# should we use cached files?
|
|
if [ -f "$TITLEFILE" ] && [ -f "$ICONFILE" ] && [ -f "$EXECFILE" ] && [ -f "$FILEFILE" ] && [ -f "$DESCFILE" ]; then
|
|
cfiles=true
|
|
else
|
|
cfiles=false
|
|
fi
|
|
|
|
read -ra uniq_args <<< "${UNIQ_ARGS}"
|
|
|
|
it_title=()
|
|
it_desc=()
|
|
it_icon=()
|
|
it_exec=()
|
|
it_file=()
|
|
it_term=()
|
|
|
|
# print data from entries
|
|
if [ "$cfiles" = "false" ]; then
|
|
printf "Writing cache files because none exist.\nTitle file: '%s'\nDescription file: '%s'\nIcon file: '%s'\nExec file: '%s'\nFile file: '%s'\nTerm file: '%s'\n" "$TITLEFILE" "$DESCFILE" "$ICONFILE" "$EXECFILE" "$FILEFILE" "$TERMFILE" >> "$LOGFILE" && tail -n 1 "$LOGFILE"
|
|
|
|
icons="$(find "${icondir[@]}" -type f)"
|
|
entry="$(find "${desktopdir[@]}" -type f)"
|
|
entry_c="$(printf "$entry\n" | wc -l)"
|
|
|
|
rm -f "$TITLEFILE" "$ICONFILE" "$DESCFILE" "$EXECFILE" "$FILEFILE" "$TERMFILE"
|
|
|
|
for i in $(seq "$entry_c"); do
|
|
cur_file="$(printf "%s" "$entry" | sed "${i}q;d")"
|
|
icon_name="$(grep "Icon=" "$cur_file" | head -n 1 | sed "s/Icon=//g")"
|
|
|
|
if [ -x "$(command -v desktop-file-validate)" ] && [ "$VALIDATE_ENTRIES" != "false" ]; then
|
|
desktop-file-validate "$cur_file" > /dev/null || continue
|
|
fi
|
|
|
|
# get details to display
|
|
it_title[i]="$(grep "Name=" "$cur_file" | grep -v Generic | head -n 1 | sed "s/Name=//g")"
|
|
it_desc[i]="$(grep "GenericName=" "$cur_file" | sed "s/GenericName=//g")"
|
|
it_icon[i]="$(printf "%s" "$icons" | grep "/${icon_name}[.]" | head -n 1)"
|
|
it_exec[i]="$(grep -v "TryExec" "$cur_file" | grep -m1 "Exec=" | sed "s/Exec=//g; s/%U//g; s/%F//g; s/%u//g; s/%f//g")"
|
|
it_file[i]="$cur_file"
|
|
it_term[i]="false"
|
|
|
|
grep -q "Terminal=true" "$cur_file" && it_term[i]="true"
|
|
|
|
# write files
|
|
printf "%s\n" "${it_title[i]}" >> "$TITLEFILE"
|
|
printf "%s\n" "${it_icon[i]}" >> "$ICONFILE"
|
|
printf "%s\n" "${it_desc[i]}" >> "$DESCFILE"
|
|
printf "%s\n" "${it_exec[i]}" >> "$EXECFILE"
|
|
printf "%s\n" "${it_file[i]}" >> "$FILEFILE"
|
|
printf "%s\n" "${it_term[i]}" >> "$TERMFILE"
|
|
|
|
# log it all
|
|
printf "%d. Title - %s\n" "${i}" "${it_title[i]}" >> "$LOGFILE"
|
|
printf "%d. Description - %s\n" "${i}" "${it_desc[i]}" >> "$LOGFILE"
|
|
printf "%d. Executable - %s\n" "${i}" "${it_exec[i]}" >> "$LOGFILE"
|
|
printf "%d. Icon - %s\n" "${i}" "${it_icon[i]}" >> "$LOGFILE"
|
|
printf "%d. File - %s\n" "${i}" "${it_file[i]}" >> "$LOGFILE"
|
|
printf "%d. Term - %s\n" "${i}" "${it_term[i]}" >> "$LOGFILE"
|
|
done
|
|
|
|
print_desktop_list
|
|
else # we have entries, let's populate the arrays
|
|
command -v desktop_pre_func > /dev/null && desktop_pre_func
|
|
|
|
# read title
|
|
while read -r p; do
|
|
it_title+=("$p")
|
|
done < "$TITLEFILE"
|
|
|
|
# read icon
|
|
while read -r p; do
|
|
[ "$IMAGE" != "true" ] && it_icon+=("") && continue
|
|
it_icon+=("$p")
|
|
done < "$ICONFILE"
|
|
|
|
# read executable
|
|
while read -r p; do
|
|
it_exec+=("$p")
|
|
done < "$EXECFILE"
|
|
|
|
# read file
|
|
while read -r p; do
|
|
it_file+=("$p")
|
|
done < "$FILEFILE"
|
|
|
|
# read description
|
|
while read -r p; do
|
|
it_desc+=("$p")
|
|
done < "$DESCFILE"
|
|
|
|
# finally print all of it
|
|
for i in "${!it_title[@]}"; do
|
|
if [ -f "${it_icon[i]}" ] && [ -n "${it_title[i]}" ] && [ -n "${it_exec[i]}" ] && [ "$IMAGE" != "false" ]; then
|
|
printf "%s\t%s" "img://${it_icon[i]}" "${it_title[i]}"
|
|
elif [ -n "${it_title[i]}" ] && [ -n "${it_exec[i]}" ]; then
|
|
printf "%s" "${it_title[i]}"
|
|
else
|
|
continue
|
|
fi
|
|
|
|
if [ -z "${it_desc[i]}" ] || [ "$DISPLAY_DESCRIPTION" = "false" ]; then
|
|
printf "\n"
|
|
else
|
|
[ "$DMENU_COMPAT" != "true" ] && COL='\033[0;31m'
|
|
printf " - %b%s\n" "${COL}" "${it_desc[i]}"
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
exec_program() {
|
|
# read title
|
|
while read -r p; do
|
|
it_title+=("$p")
|
|
done < "$TITLEFILE"
|
|
|
|
# read executable
|
|
while read -r p; do
|
|
it_exec+=("$p")
|
|
done < "$EXECFILE"
|
|
|
|
# read term
|
|
while read -r p; do
|
|
it_term+=("$p")
|
|
done < "$TERMFILE"
|
|
|
|
# set exec
|
|
[ -z "${it_exec[1]}" ] && printf "Executable array is empty.\n" >> "$LOGFILE"
|
|
for i in "${!it_title[@]}"; do
|
|
if [ "${it_title[i]}" = "$menusel" ]; then
|
|
exec="${it_exec[i]}"
|
|
term="${it_term[i]}"
|
|
|
|
printf "Executable %s is: '%s'\n" "$i" "${it_exec[i]}" >> "$LOGFILE"
|
|
printf "Executable %s term status: '%s'\n" "$i" "${it_term[i]}" >> "$LOGFILE"
|
|
|
|
break;
|
|
else
|
|
printf "Executable %s is: '%s'\n" "$i" "${it_exec[i]}" >> "$LOGFILE"
|
|
printf "Executable %s term status: '%s'\n" "$i" "${it_term[i]}" >> "$LOGFILE"
|
|
fi
|
|
done
|
|
|
|
# finally run the program
|
|
if [ -n "$exec" ] && [ "$term" = "false" ]; then
|
|
${SHELL:-/bin/sh} -c "$exec" &
|
|
elif [ -n "$exec" ] && [ "$term" = "true" ]; then
|
|
${TERMINAL} -e "$exec" &
|
|
else
|
|
printf "No executable found. Try clearing cache." >> "$LOGFILE"
|
|
fi
|
|
}
|
|
|
|
print_desktop_menu() {
|
|
check_desktop
|
|
|
|
HIDDEN_ENTRY_KEYWORDS="${HIDDEN_ENTRY_KEYWORDS:-NULL_ENTRY}"
|
|
|
|
print_desktop_list | uniq "${uniq_args[@]}" | sort "${sort_args[@]}" | grep -vE "$HIDDEN_ENTRY_KEYWORDS" | grep -E "$ENTRY_KEYWORDS" | $RUNLAUNCHER "${rl_desktop[@]}" > /tmp/spmenu_out
|
|
|
|
while read -r menusel; do
|
|
[ "$menusel" = "?" ] && print_desktop_help && print_desktop_menu
|
|
command -v desktop_post_func > /dev/null && desktop_post_func "$menusel"
|
|
[ -z "$menusel" ] && return 1 \
|
|
|| printf "User input: %s\n" "$menusel" >> "$LOGFILE"
|
|
|
|
# this must now be the title only
|
|
menusel="$(printf "%s" "$menusel" | sed "s/ -.*//")"
|
|
|
|
exec_program "$@"
|
|
[ "$MULTISELECT" != "true" ] && break
|
|
done < /tmp/spmenu_out; rm -f /tmp/spmenu_out
|
|
}
|
|
|
|
prep_desktop() {
|
|
printf "spmenu_run log (%s)\nFunction: Desktop\n" "$(date "+%D %T")" > "$LOGFILE"
|
|
}
|
|
|
|
clear_cache() {
|
|
rm -f "${TITLEFILE}" "${FILEFILE}" "${EXECFILE}" "${ICONFILE}" "${DESCFILE}" "${TERMFILE}"
|
|
}
|
|
|
|
main() {
|
|
[ -d "/System/Library/Extensions" ] && fail_mac
|
|
rm -f "$LOGFILE"
|
|
print_config
|
|
read_args "$@"
|
|
check "$args"
|
|
|
|
# some run launcher args
|
|
RUNLAUNCHER_FM_ARGS="--insert --hist-file $HISTORY $RUNLAUNCHER_FM_ARGS $MARGS"
|
|
RUNLAUNCHER_RUN_ARGS="--insert --hist-file $HISTORY $RUNLAUNCHER_RUN_ARGS $MARGS"
|
|
RUNLAUNCHER_DESKTOP_ARGS="-sgr1 $DESCRIPTION_COLOR --lines 20 --columns 1 --image-size 100 --image-gaps 20 $RUNLAUNCHER_DESKTOP_ARGS $MARGS"
|
|
RUNLAUNCHER_HELP_ARGS="--insert --hist-file $HISTORY $RUNLAUNCHER_HELP_ARGS $MARGS"
|
|
|
|
# dmenu compatibility
|
|
DMENU_FM_ARGS="-l 20 $MARGS"
|
|
DMENU_RUN_ARGS="$MARGS"
|
|
DMENU_DESKTOP_ARGS="-l 20 $MARGS"
|
|
DMENU_HELP_ARGS="-l 20 $MARGS"
|
|
COMPAT_LS_ARGS="--color=never"
|
|
|
|
if [ "$DMENU_COMPAT" != "false" ]; then
|
|
IMAGE="false"
|
|
RUNLAUNCHER="dmenu"
|
|
RUNLAUNCHER_FM_ARGS="$DMENU_FM_ARGS"
|
|
RUNLAUNCHER_RUN_ARGS="$DMENU_RUN_ARGS"
|
|
RUNLAUNCHER_DESKTOP_ARGS="$DMENU_DESKTOP_ARGS"
|
|
RUNLAUNCHER_HELP_ARGS="$DMENU_HELP_ARGS"
|
|
LS_ARGS="$COMPAT_LS_ARGS"
|
|
fi
|
|
|
|
# read to arrays
|
|
read -ra rl_fm <<< "${RUNLAUNCHER_FM_ARGS}"
|
|
read -ra rl_run <<< "${RUNLAUNCHER_RUN_ARGS}"
|
|
read -ra rl_desktop <<< "${RUNLAUNCHER_DESKTOP_ARGS}"
|
|
read -ra rl_help <<< "${RUNLAUNCHER_HELP_ARGS}"
|
|
read -ra ls_args <<< "${LS_ARGS}"
|
|
read -ra desktopdir <<< "${DESKTOP_DIR}"
|
|
read -ra icondir <<< "${ICON_DIR}"
|
|
|
|
# clear cache
|
|
[ "$clearcache" = "true" ] && clear_cache
|
|
|
|
# $PATH listing
|
|
case "$function" in
|
|
"run")
|
|
parse "$args"
|
|
;;
|
|
"fm") prepare_dirnav "$args"
|
|
;;
|
|
"desktop")
|
|
prep_desktop
|
|
print_desktop_menu "$args"
|
|
;;
|
|
"help")
|
|
print_cli_help
|
|
exit 0
|
|
;;
|
|
*)
|
|
printf "Undefined function: '%s'\n" "$function"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# shellcheck disable=SC2031
|
|
main "$@"
|