diff --git a/dfm b/dfm index 64e3797..7c871b7 100755 --- a/dfm +++ b/dfm @@ -1,104 +1,161 @@ #!/bin/sh -main() { parse_opts "$@" - [ -z $mode ] && mode=open - if [ -n "$copy" -a "$copy" != false ] && [ "$cat" = true ]; then +PROGRAM_NAME=$(basename $0) # This will be used in a few messages + +main() { + parse_opts "$@" + [ -z "$mode" ] && mode=open # The default mode + + if [ -n "$copy" -a "$copy" != false -a "$cat" = true ]; then prompt_copy_contents "$@" - elif [ "$open" = true ]; then prompt_open "$@" - elif [ "$cat" = true ]; then prompt_print_contents "$@" - elif [ "$print" = true ]; then prompt_print "$@" - elif [ -n "$copy" -a "$copy" != false ]; then prompt_copy "$@" - else prompt_$mode "$@"; fi + elif [ "$open" = true ]; then + prompt_open "$@" + elif [ "$cat" = true ]; then + prompt_print_contents "$@" + elif [ "$print" = true ]; then + prompt_print "$@" + elif [ -n "$copy" -a "$copy" != false ]; then + prompt_copy "$@" + else + prompt_$mode "$@" + fi } prompt_base() { - [ -z $length ] && length=10 - [ "$copy" = true ] && copy="clipboard" + [ -z "$length" ] && length=10 - if [ "$case_sensitivity" = "sensitive" ]; then - backtrack() { sed 's|\(.*/'$sel'[^/]*\).*|\1|'; } ; s=+i - else backtrack() { perl -pe 's|(.*/'$sel'[^/]*).*|$1|i'; } ; i=-i; fi + if [ "$case_sensitivity" = sensitive ]; then + backtrack() { sed 's|\(.*/'$sel'[^/]*\).*|\1|'; } + s=+i + else + backtrack() { perl -pe 's|(.*/'$sel'[^/]*).*|$1|i'; } + i=-i + fi [ -z "$menu" ] && menu="dmenu" if [ "$menu" = dmenu ]; then menu() { $menu $i -l $length -p "$@"; } - elif [ "$menu" = fzf ]; then - menu() { - $menu $s $i --header="$@" --tabstop=4 --multi --no-sort --exact - } + elif [ "$menu" = fzf ]; then menu() { $menu $s $i --header="$@"; } else menu() { $menu; }; fi if [ "$path" = "full" ]; then prompt() { p="$target"; } else prompt() { p="$(printf '%s' "$target" | sed 's|^'"$HOME"'|~|')"; }; fi + # Only GNU `ls` supports `--group-directories-first` if [ "$(ls --version | head -1 | cut -d " " -f 2-3)" = "(GNU coreutils)" ] - then list() { ls --group-directories-first "$@"; } + then + list() { ls --group-directories-first "$@"; } else list() { - (ls -l "$@" | grep "^d" ; ls -l "$@" | grep -vE "^d|^total") | - tr -s " " | cut -d " " -f 9- + (ls -l "$@" | grep "^d" + ls -l "$@" | grep -vE "^d|^total") | tr -s " " | cut -d " " -f 9- } fi + # Commonly used functions in DFM truepath() { sh -c "realpath -s "$sel""; } slash() { printf '%s' "$target/$sel" | rev | cut -b 1-2; } check() { file -E "$@" | grep "(No such file or directory)$"; } fullcmd() { - printf '%s' "$target" | sed -e "s/'/'\\\\''/g;s/\(.*\)/'\1'/" | - cmd + printf '%s' "$target" | sed -e "s/'/'\\\\''/g;s/\(.*\)/'\1'/" | cmd } while true; do - p="$prompt" ; [ -z "$p" ] && prompt + p="$prompt" # Reset the prompt to have it update + [ -z "$p" ] && prompt # Make the prompt if it does not exist + + # This is where the file manager actually first opens. sel="$(printf '%s' "$(list "$target"; list -A "$target" | grep '^\.')" | menu "$p")" - ec=$? ; [ "$ec" -ne 0 ] && exit $ec + + # Exit if the user presses Escape, Control-C, etc. + exit_code=$? + [ "$exit_code" -ne 0 ] && exit $exit_code if [ $(printf '%s' "$sel" | wc -l) -eq 0 ]; then - if [ "$sel" = "" ]; then newt="$(realpath -s "$target/..")" - elif [ -e "$target/$sel" -a "$(slash)" != "//" ]; then + # If the input box is empty, go to the parent directory + if [ "$sel" = "" ]; then + newt="$(realpath -s "$target/..")" + # Relative directories + elif [ -e "$target/$sel" -a "$(slash)" != // ]; then newt="$(realpath -s "$target/$sel")" elif [ ! -e "$target/$sel" -a $(printf '%s' "$target" | grep $i "$(sh -c "printf '%s' "$sel"")" | wc -l) -eq 1 ] - then - if [ ! -e "$(truepath)" ]; then - newt="$(printf '%s' "$target" | backtrack)" - else newt="$(truepath)"; fi + then + # Go to a lower directory using the input box + if [ ! -e "$(truepath)" ]; then + newt="$(printf '%s' "$target" | backtrack)" + # Go to certain directories like `~` `$HOME`, etc. + else + newt="$(truepath)" + fi + # Go to a directory when the input box begins with `/` elif [ -e "$(truepath)" ] && [ ! -e "$target/$sel" -o "$(slash)" = "//" ] - then newt="$(truepath)" - else newt="$(realpath -s "$target/$sel")"; fi - else newt="$sel"; fi + then + newt="$(truepath)" + else + # This applies to wildcards + newt="$(realpath -s "$target/$sel")" + fi + else + newt="$sel" + fi + # If the current working directory is not empty if [ $(ls | wc -l) -ge 1 ]; then target="$newt" if [ ! -d "$target" ]; then + # Check if the user used a wildcard if [ $(printf '%s' "$target" | grep "*" | wc -l) -ge 1 -a\ $(check "$target" | wc -l) -eq 1 ] - then - IFS= - ls "$PWD"/$sel 1> /dev/null 2>& 1 - if [ $? -ne 0 ]; then target="$PWD" - else target=$(ls -d "$PWD"/$sel) fullcmd ; exit 0; fi + then + IFS= # Needed to make wildcards work + ls "$PWD"/$sel 1> /dev/null 2>& 1 + # Target is a file or directory + if [ $? -ne 0 ]; then + target="$PWD" + # Target is a wildcard + else + echo lol + target=$(ls -d "$PWD"/$sel) fullcmd + exit 0 + fi + # No such file or directory elif [ $(printf '%s' "$target" | wc -l) -eq 0 -a\ $(check "$target" | wc -l) -eq 1 ] - then target="$PWD" + then + target="$PWD" + # More than one selection elif [ $(printf '%s' "$target" | wc -l) -gt 0 ]; then target=$(printf '%s' "$target" | sed 's|^|'"$PWD"/'|') - fullcmd ; exit 0 - else fullcmd ; exit 0; fi - else PWD="$target"; fi + fullcmd + exit + # Exactly one selection + else + fullcmd + exit + fi + # Target is a directory + else + PWD="$target" + fi fi done } -prompt_print() { cmd () { xargs ls -d; } ; prompt_base "$@"; } -prompt_print_contents() { cmd() { xargs cat; } ; prompt_base "$@"; } +prompt_print() { + cmd () { xargs ls -d; } + prompt_base "$@" +} + +prompt_print_contents() { + cmd() { xargs cat; } + prompt_base "$@" +} prompt_open() { - cmd() { - if [ -x "$(command -v sesame)" ]; then xargs sesame - else xargs xdg-open; fi - } + if [ -x "$(command -v sesame)" ]; then cmd() { xargs sesame; } + else cmd() { xargs xdg-open; }; fi prompt_base "$@" } @@ -108,16 +165,16 @@ prompt_copy() { } prompt_copy_contents() { - cmd() { - if [ "$(file -b "$target" | cut -d " " -f2)" = "image" ]; then - xargs xclip -i -selection $copy -t image/png - else xargs xclip -r -i -selection $copy; fi - } + if [ "$(file -b "$target" | cut -d " " -f2)" = "image" ]; then + cmd() { xargs xclip -i -selection $copy -t image/png; } + else + cmd() { xargs xclip -r -i -selection $copy; } + fi prompt_base "$@" } - -help() { printf 'Usage:\t%s' "$(basename $0) [options] [target] [prompt] +help() { + printf "Usage:\t$0 [options] [target] [prompt] Options: @@ -141,8 +198,8 @@ Modes: "; } parse_opts() { - : "${config_dir:=${XDG_CONFIG_HOME:-$HOME/.config}/$(basename $0)}" - : "${config_file:=$config_dir/$(basename $0).conf}" + : "${config_dir:=${XDG_CONFIG_HOME:-$HOME/.config}/$PROGRAM_NAME}" + : "${config_file:=$config_dir/$PROGRAM_NAME.conf}" [ -f "$config_file" ] && . "$config_file" needs_arg() { @@ -160,48 +217,82 @@ parse_opts() { OPTARG="${OPTARG#=}" fi case "$OPT" in - h | help) help ; exit 0 ;; - p | print) print=true ;; - c | copy) + h|help) + help + exit 0 + ;; + p|print) + print=true + ;; + c|copy) shift [ $(printf '%s' "$OPT" | wc -c) -eq 1 ] && OPTARG="$1" case "$OPTARG" in - primary | secondary | clipboard | buffer-cut) + primary|secondary|clipboard|buffer-cut) copy="$OPTARG" ;; - *) copy=true ;; + *) + copy=clipboard + ;; esac [ -n "$1" -a "$OPTARG" = "$1" -a "$copy" = "$OPTARG" ] && shift ;; - no-copy) copy=false ;; - cat) cat=true ;; - o | open) open=true ;; - s | sensitive) case_sensitivity="sensitive" ;; - i | insensitive) case_sensitivity="insensitive" ;; - m | menu) needs_arg ; menu="$OPTARG" ;; - l | length) needs_arg ; length=$OPTARG ;; - f | full) path="full" ;; - a | abbreviated) path="abbreviated" ;; + no-copy) + copy=false + ;; + cat) + cat=true + ;; + o|open) + open=true + ;; + s|sensitive) + case_sensitivity="sensitive" + ;; + i|insensitive) + case_sensitivity="insensitive" + ;; + m|menu) + needs_arg + menu="$OPTARG" + ;; + l|length) + needs_arg + length=$OPTARG + ;; + f|full) + path="full" + ;; + a|abbreviated) + path="abbreviated" + ;; ??*) printf '%s\n' "Illegal option --$OPT" >&2 exit 2 ;; - ?) exit 2 ;; + ?) # Error reported via `getopts` + exit 2 + ;; esac done - shift $((OPTIND-1)) + shift $((OPTIND-1)) # Remove option arguments from the argument list - [ -n "$1" ] && target="$1" ; [ -z "$target" ] && target="$PWD" + if [ -n "$1" ]; then target="$1" + elif [ -z "$target" ]; then target="$PWD" + fi if [ -d "$target" ]; then target="$(realpath -s "$target")" PWD="$target" - elif [ ! -d "$target" ]; then - printf '%s\n' "$(basename $0): \`$target\` is not a directory." >&2 + else + printf '%s\n' "$PROGRAM_NAME: \`$target\` is not a directory." >&2 exit 2 fi [ -n "$2" ] && prompt="$2" + # If the prompt is the same as the target, uset the prompt so that it can + # update. This is useful if you set a prompt in your configuration file but + # want to use the default prompt if [ -n "$prompt" ] && [ "$(realpath -s "$prompt")" = "$target" ]; then unset prompt fi