spmenu/scripts/spmenu_run
speedie e22c5f7e75 Simplify caching of desktop entries
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.
2023-06-22 23:27:56 +02:00

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/\&/\&amp;/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 "$@"