--- john.bash_completion.orig 2012-06-01 15:15:05.479095570 +0000 +++ john.bash_completion 2012-06-01 23:28:41.110661714 +0000 @@ -35,10 +35,9 @@ # # # The code is still ugly, many things can probably be done more efficiently. -# Currently, grep and sed are the only external commands used. -# -# +# Currently, grep, tr, and sed are the only external commands used. # Trying to build a perfect completion script will be hard. +# # If possible, I'd like to avoid the need to maintain this script whenever # john gets a new option. # @@ -72,36 +71,42 @@ # but pressing [tab] expands this to something useful # Where to fix this? In john? Some bash config option? # -# --external should use names of [List.External:..-.] sections -# (Just sections without a generate() function, if --wordlist, --incremental or --single -# is present on the command line; with a generate() function, if none of these -# options is used - WHAT IF the user adds a --wordlist option later?) -# --restore= and --status= completion should search for existing .rec files -# in the correct directory, instead of $PWD. -# --mkpc and other options which are not mentioned in the usage output are currently ignored -# -# Should I support -option instead of --option? (currently -option (or -opt) gets replaced -# with --option during completion. -# Should I support --option:val, -option:val, or even -opt:val instead of just --option=val? +# --external should not use all names of [List.External:..-.] sections, but +# just sections without a generate() function, if --wordlist, --incremental or --single +# is present on the command line; and just those with a generate() function, +# if none of these options is used - WHAT IF the user adds a --wordlist option later? +# # Should expanding an abbreviated option to its long form also be done by john itself # (requires new john option)? - -# grep and sed are used to process john's usage info the list of .rec files... - # different implementations for completion logic for these options: # --rules --single --incremental --restore --status # for __john_completion=[2|any other value] # # john ## on my system, have() is a dummy function which always return "yes", so get rid of calling it... -## have grep && have sed && +## have grep && have sed && have tr _john() { - local first cur options valopts compreplya compreplyb encodings formats subformats list hidden dir cmd i ver ver1 ver2 ver3 prev - + local first cur options valopts compreplya compreplyb encodings formats subformats list hidden dir cmd i ver ver1 ver2 ver3 prev words COMPREPLY=() - _get_comp_words_by_ref -n = cur + + if [[ "${COMP_WORDBREAKS}" == *:* ]] ; then + _get_comp_words_by_ref -n := cur prev words + else + _get_comp_words_by_ref -n = cur prev words + # If the colon is not part of COMP_WORDBREAKS, e.g., due to + # including this line into your ~/.bashrc, as mentioned in + # /etc/bash_completion ... + # export COMP_WORDBREAKS="${COMP_WORDBREAKS//:}" + # just replace : with = in -opt:val + if [[ "${cur}" == -*:* ]] ; then + cur="${cur//:/=}" + COMPREPLY=( $(compgen -W "${cur}" -- ${cur}) ) + compopt -o nospace + return 0 + fi + fi # We need to make sure we run the correct program, not some other program # called john which is located somewhere in $PATH @@ -133,29 +138,34 @@ _john() hidden=`${first} --list=hidden-options 2>/dev/null|sed 's#^\(--[a-z-]*=\?\).*$#\1#'` case "${cur}" in - -?(-)fo?(r|rm|rma|rmat)=dynamic*) - if echo "${options}" | grep "^--subformat" > /dev/null ; then + -?(-)fo?(r|rm|rma|rmat)+(=|:)dynamic*) + if [[ "${options}" == *--subformat* ]] ; then subformats=`${first} --subformat=LIST|sed 's#^\(User\)\?Format = \(dynamic_[0-9]*\).*$#\2#'` - cur=${cur#*=} + cur=${cur#*[=:]} COMPREPLY=( $(compgen -W "${subformats}" -- ${cur}) ) fi return 0 ;; - -?(-)fo?(r|rm|rma|rmat)=dy?(n|na|nam|nami)) - if echo "${options}" | grep "^--subformat" > /dev/null ; then - cur=${cur#*=} + -?(-)fo?(r|rm|rma|rmat)+(=|:)dy?(n|na|nam|nami)) + if [[ "${options}" == *--subformat* ]] ; then + if [[ "${cur#*f}" == o[=:]* || "${cur#*f}" == or[=:]* ]] ; then + if [[ `echo "${valopts}"|grep -c "^-*${cur%[=:]*}"` -ne 1 ]] ; then + return 0 + fi + fi + cur=${cur#*[=:]} COMPREPLY=( $(compgen -W "dynamic" -- ${cur}) ) compopt -o nospace fi return 0 ;; - -?(-)fo?(r|rm|rma|rmat)=*) - if [[ "_${cur#*o}" == "_=" || "_${cur#*o}" == "_r=" ]] ; then - if [[ `echo "${valopts}"|grep -c "^-*${cur%=}"` -ne 1 ]] ; then + -?(-)fo?(r|rm|rma|rmat)+(=|:)*) + if [[ "${cur#*f}" == o[=:]* || "${cur#*f}" == or[=:]* ]] ; then + if [[ `echo "${valopts}"|grep -c "^-*${cur%[=:]*}"` -ne 1 ]] ; then return 0 fi fi - cur=${cur#*=} + cur=${cur#*[=:]} formats=`${first} |sed -n '/^--format/,$ { s#^--format=[ A-Za-z]*:##; /^--/ b; s#^ *##; s#\#dynamic#; s#[/ ]#\n#g; p }'` COMPREPLY=( $(compgen -W "${formats}" -- ${cur}) ) return 0 @@ -176,15 +186,15 @@ _john() fi return 0 ;; - -?(-)re?(s|st|sto|stor|store)=*|-?(-)sta?(t|tu|tus)=*) - if [[ "_${cur}" == "_-re=" || "_${cur}" == "_--re=" ]] ; then + -?(-)re?(s|st|sto|stor|store)+(=|:)*|-?(-)sta?(t|tu|tus)+(=|:)*) + if [[ "${cur}" == -re[=:]* || "${cur}" == --re[=:]* ]] ; then if [[ `echo "${valopts}"|grep -c "^--re"` -ne 1 ]] ; then return 0 fi fi # If there is no .rec file in the current directory, the old completion logic will show all files: ##echo _`for f in *.rec; do echo ${f%.rec};done`_ - cur=${cur#*=} + cur=${cur#*[=:]} # cd $JOHN/ or Private home for system-wide builds, if ./john --list=build-info works? # NO, this would be wrong! # .rec files are stored in the current directory (or a subdirectory if the session name contains a slash) @@ -197,14 +207,14 @@ _john() done return 0 ;; - -?(-)w?(o|or|ord|ordl|ordli|ordlis|ordlist)=*) - cur=${cur#*=} + -?(-)w?(o|or|ord|ordl|ordli|ordlis|ordlist)+(=|:)*) + cur=${cur#*[=:]} __expand_tilde_by_ref cur 2>/dev/null _filedir return 0 ;; --rules|--single) - if echo "${valopts}" | grep "^${cur}$" > /dev/null ; then + if [[ "${valopts}" == *${cur}* ]] ; then if [[ "_${__john_completion}" == "_2" ]] ; then COMPREPLY=( $(compgen -W "${cur}=" -- ${cur}) ) compopt -o nospace @@ -226,26 +236,26 @@ _john() fi return 0 ;; - -?(-)ru?(l|le|les)=*|-?(-)si?(n|ng|ngl|ngle)=*) + -?(-)ru?(l|le|les)+(=|:)*|-?(-)si?(n|ng|ngl|ngle)+(=|:)*) # let's assume every john version which supports --single= # also supports --rules=, and vice versa - if echo "${valopts}" | grep "^--rules$" > /dev/null ; then + if [[ "${valopts}" == *--rules* ]] ; then cmd=`echo ${COMP_LINE}|sed "s# ${cur}# --list=rules #"` list=`${cmd} 2>/dev/null` if [[ $? -ne 0 ]] ; then list=`${first} --list=rules 2>/dev/null` fi if [[ $? -eq 0 ]] ; then - cur=`echo ${cur#*=}|LC_ALL=C tr [A-Z] [a-z]` + cur=`echo ${cur#*[=:]}|LC_ALL=C tr A-Z a-z` COMPREPLY=( $(compgen -W "${list}" -- ${cur}) ) else - cur=${cur#*=} + cur=${cur#*[=:]} COMPREPLY=( $(compgen -W "NT single wordlist" -- ${cur}) ) fi fi return 0 ;; - -?(-)ex?(t|te|ter|tern|terna|ternal)=*) + -?(-)ex?(t|te|ter|tern|terna|ternal)+(=|:)*) cmd=`echo ${COMP_LINE}|sed "s# ${cur}# --list=externals #"` list=`${cmd} 2>/dev/null` if [[ $? -ne 0 ]] ; then @@ -278,9 +288,9 @@ _john() list="${list} DumbForce KnownForce DateTime Repeats Subsets AtLeast1-Simple AtLeast1-Generic Policy AppendLuhn AutoAbort AutoStatus" fi fi - cur=${cur#*=} + cur=${cur#*[=:]} else - cur=`echo ${cur#*=}|LC_ALL=C tr [A-Z] [a-z]` + cur=`echo ${cur#*[=:]}|LC_ALL=C tr A-Z a-z` fi COMPREPLY=( $(compgen -W "${list}" -- ${cur}) ) return 0 @@ -304,23 +314,23 @@ _john() fi return 0 ;; - -?(-)i?(n|nc|ncr|ncre|ncrem|ncreme|ncremen|ncrement|ncrementa|ncremental)=*) + -?(-)i?(n|nc|ncr|ncre|ncrem|ncreme|ncremen|ncrement|ncrementa|ncremental)+(=|:)*) cmd=`echo ${COMP_LINE}|sed "s# ${cur}# --list=inc-modes #"` list=`${cmd} 2>/dev/null` if [[ $? -ne 0 ]] ; then list=`${first} --list=inc-modes 2>/dev/null` fi if [[ $? -eq 0 ]] ; then - cur=`echo ${cur#*=}|LC_ALL=C tr [A-Z] [a-z]` + cur=`echo ${cur#*[=:]}|LC_ALL=C tr A-Z a-z` COMPREPLY=( $(compgen -W "${list}" -- ${cur}) ) else - cur=${cur#*=} + cur=${cur#*[=:]} COMPREPLY=( $(compgen -W "All Alpha Digits Alnum LanMan" -- ${cur}) ) fi return 0 ;; - -?(-)mak?(e|e-|e-c|e-ch|e-cha|e-char|e-chars|e-charse|e-charset)=*) - cur=${cur#*=} + -?(-)mak?(e|e-|e-c|e-ch|e-cha|e-char|e-chars|e-charse|e-charset)+(=|:)*) + cur=${cur#*[=:]} #redirect stderr just in case __expand_tilde_by_ref #doesn't exist everywhere #(I'm a bit worried because of the __ at the begin. @@ -331,21 +341,18 @@ _john() _filedir "chr" return 0 ;; - --stdout=*|--markov=*) - return 0 - ;; --stdout) COMPREPLY=( $(compgen -W "--stdout --stdout=LENGTH" -- ${cur}) ) return 0 ;; --markov) - if echo "${options}" | grep "^${cur}$" > /dev/null ; then + if [[ "${valopts}" == *${cur}* ]] ; then COMPREPLY=( $(compgen -W "--markov --markov=LEVEL[:START[:END[:LENGTH]]]" -- ${cur}) ) fi return 0 ;; --test) - if echo "${valopts}" | grep "^${cur}$" > /dev/null ; then + if [[ "${valopts}" == *${cur}* ]] ; then COMPREPLY=( $(compgen -W "--test --test=SECONDS" -- ${cur}) ) else COMPREPLY=( $(compgen -W "${cur}" -- ${cur}) ) @@ -353,31 +360,31 @@ _john() return 0 ;; --show) - if echo "${valopts}" | grep "^--show$" > /dev/null ; then + if [[ "${valopts}" == *${cur}* ]] ; then COMPREPLY=( $(compgen -W "--show --show=LEFT" -- ${cur}) ) else COMPREPLY=( $(compgen -W "--show" -- ${cur}) ) fi return 0 ;; - -?(-)sho?(w)=l*) - if echo "${valopts}" | grep "^--show$" > /dev/null ; then - cur=${cur#*=} + -?(-)sho?(w)+(=|:)l*) + if [[ "${valopts}" == *--show* ]] ; then + cur=${cur#*[=:]} COMPREPLY=( $(compgen -W "left" -- ${cur}) ) fi return 0 ;; - -?(-)sho?(w)=*) - if echo "${valopts}" | grep "^--show$" > /dev/null ; then - cur=${cur#*=} + -?(-)sho?(w)+(=|:)*) + if [[ "${valopts}" == *--show* ]] ; then + cur=${cur#*[=:]} COMPREPLY=( $(compgen -W "LEFT" -- ${cur}) ) fi return 0 ;; - --users=L*|--users=U*|--users=-*|--groups=G*|--groups=-*|--shells=S*|--shells=-*|--salts=C*|--salts=-*) + --users=?(-)+(L|U)*|--groups=+(-|G)*|--shells=+(-|S)*|--salts=+(-|C)*) return 0 ;; - --users=*) + --users=?(-)) cur=${cur#*=} COMPREPLY=( $(compgen -W "LOGIN,... UID,... -LOGIN,... -UID,..." -- ${cur}) ) return 0 @@ -397,11 +404,11 @@ _john() COMPREPLY=( $(compgen -W "COUNT -COUNT" -- ${cur}) ) return 0 ;; - -?(-)en?(c|co|cod|codi|codin|coding)=*) - if echo "${valopts}" | grep "^--encoding=" > /dev/null ; then + -?(-)en?(c|co|cod|codi|codin|coding)+(=|:)*) + if [[ "${valopts}" == *--encoding=* ]] ; then # --encoding=LIST writes to stderr encodings=`${first} --encoding=LIST 2>&1|grep -v 'Supported encodings'|sed 's#[,)]##g'|sed 's#(or ##g'` - cur=${cur#*=} + cur=${cur#*[=:]} if [[ ${COMP_CWORD} -eq 2 || ${COMP_CWORD} -eq 3 && "_${cur}" != "_" ]] ; then encodings="${encodings} LIST" # make sure LIST will be the first option: @@ -411,11 +418,11 @@ _john() fi return 0 ;; - -?(-)po?(t)=*) - if echo "${valopts}" | grep "^--encoding=" > /dev/null ; then + -?(-)po?(t)+(=|:)*) + if [[ "${valopts}" == *--pot=* ]] ; then # if --pot= is used, john always looks for the file $PWD # (tested with system-wide and local build of john) - cur=${cur#*=} + cur=${cur#*[=:]} #redirect stderr just in case __expand_tilde_by_ref #doesn't exist everywhere #(I'm a bit worried because of the __ at the begin. @@ -425,68 +432,65 @@ _john() fi return 0 ;; - -?(-)co?(n|nf|nfi|nfig)=*) - if echo "${valopts}" | grep "^--encoding=" > /dev/null ; then + -?(-)co?(n|nf|nfi|nfig)+(=|:)*) + if [[ "${valopts}" == *--config=* ]] ; then # if --config= is used, john always looks for files in $PWD # (tested for system-wide and local builds) - cur=${cur#*=} + cur=${cur#*[=:]} __expand_tilde_by_ref cur 2>/dev/null _filedir '@(conf|ini)' fi return 0 ;; - -?(-)sav?(e|e-|e-m|e-me|e-mem|e-memo|e-memor|e-memory)=*) - cur=${cur#*=} + -?(-)sav?(e|e-|e-m|e-me|e-mem|e-memo|e-memor|e-memory)+(=|:)*) + cur=${cur#*[=:]} COMPREPLY=( $(compgen -W "1 2 3" -- ${cur}) ) return 0 ;; - -?(-)reg?(e|en|en-|en-l|en-lo|en-los|en-lost|en-lost-|en-lost-s|en-lost-sa|en-lost-sal|en-lost-salt|en-lost-salts)=*) - if echo "${valopts}" | grep "^--regen-lost-salts=" > /dev/null ; then - cur=${cur#*=} + -?(-)reg?(e|en|en-|en-l|en-lo|en-los|en-lost|en-lost-|en-lost-s|en-lost-sa|en-lost-sal|en-lost-salt|en-lost-salts)+(=|:)*) + if [[ "${valopts}" == *--regen-lost-salts=* ]] ; then + cur=${cur#*[=:]} COMPREPLY=( $(compgen -W "1 2 3 4 5" -- ${cur}) ) fi return 0 ;; - --subformat=l*) - if echo "${options}" | grep "^--subformat=" > /dev/null ; then - cur=${cur#*=} + -?(-)su?(b|bf|bfo|bfor|bform|bforma|bformat)+(=|:)l*) + if [[ "${options}" == *--subformat=* ]] ; then + cur=${cur#*[=:]} COMPREPLY=( $(compgen -W "list" -- ${cur}) ) fi return 0 ;; - --subformat=*) - if echo "${valopts}" | grep "^--subformat=" > /dev/null ; then - cur=${cur#*=} + -?(-)su?(b|bf|bfo|bfor|bform|bforma|bformat)+(=|:)*) + if [[ "${options}" == *--subformat=* ]] ; then + cur=${cur#*[=:]} COMPREPLY=( $(compgen -W "LIST" -- ${cur}) ) fi return 0 ;; - -?(-)pla?(t|tf|tfo|tfor|tform)=+(L|l)*|-?(-)dev?(i|ic|ice)=+(L|l)*) + -?(-)pla?(t|tf|tfo|tfor|tform)+(=|:)+(L|l)*|-?(-)dev?(i|ic|ice)+(=|:)+(L|l)*) # CUDA doesn't allow --device=LIST # workaround: check if --platform= is allowed - if echo "${valopts}" | grep "^--platform=$" > /dev/null ; then - cur=${cur#*=} + if [[ "${valopts}" == *--platform=* ]] ; then + cur=${cur#*[=:]} COMPREPLY=( $(compgen -W "LIST list" -- ${cur}) ) fi return 0 ;; - --platform=|--device=) + -?(-)platform+(=|:)|-?(-)device+(=|:)) # --device=LIST isn't supported for CUDA, but for CUDA # --platform= is not a valid option - if echo "${valopts}" | grep "^--platform=$" > /dev/null ; then + if [[ "${valopts}" == *--platform=* ]] ; then # Calling john --platform=LIST just to find possible completions # will take too long - cur=${cur#*=} + cur=${cur#*[=:]} COMPREPLY=( $(compgen -W "LIST N" -- ${cur}) ) fi return 0 ;; - --platform=*|--device=*) - return 0 - ;; - -?(-)l?(i|is|ist)=*) - if echo "${hidden}" | grep "^--list=" > /dev/null ; then - cur=${cur#*=} + -?(-)l?(i|is|ist)+(=|:)*) + if [[ "${hidden}" == *--list=* ]] ; then + cur=${cur#*[=:]} # the --list=? output changed, that's why a more complex regex is used # to cover all cases list=`${first} --list=? 2>/dev/null|sed 's#\(,\)\?\( or\)\?[ ]*[<].*$##; s#,##g'` @@ -498,7 +502,7 @@ _john() fi return 0 ;; - -*=) + -*+(=|:)) return 0; ;; -*)