# -*-eselect-*- vim: ft=eselect # Copyright 2005-2013 Gentoo Foundation # Distributed under the terms of the GNU GPL version 2 or later DESCRIPTION="Manage contributed bash-completion scripts" MAINTAINER="eselect@gentoo.org" COMPLETIONS_DIR=${EROOT%/}/usr/share/bash-completion/completions # blacklist for system-wide disables COMPLETIONS_GLOBAL_BLACKLIST=${EROOT%}/etc/bash/completion.blacklist # whitelist for user re-enables COMPLETIONS_USER_WHITELIST=~/.config/bash_completion.whitelist # blacklist for user disables COMPLETIONS_USER_BLACKLIST=~/.config/bash_completion.blacklist find_targets() { local bc # Note: safe to cd, we're in a subshell cd "${COMPLETIONS_DIR}" || die for bc in *; do [[ -f ${bc} && ${bc} != *~ ]] && echo "${bc}" done | sort -u } is_enabled() { local state=1 [[ -n ${USE_GLOBAL} ]] \ && grep -q -s "^${1}\$" "${COMPLETIONS_GLOBAL_BLACKLIST}" \ && state=0 if [[ -n ${USE_USER} ]]; then grep -q -s "^${1}\$" "${COMPLETIONS_USER_WHITELIST}" \ && state=1 grep -q -s "^${1}\$" "${COMPLETIONS_USER_BLACKLIST}" \ && state=0 fi [[ ${state} == 1 ]] } post_update() { # adjust permissions if necessary if [[ -n ${USE_GLOBAL} ]]; then chmod a+r "${COMPLETIONS_GLOBAL_BLACKLIST}" || die fi echo "For the changes to apply to open shells, you may need to run:" echo " complete -r ${*}" echo "in each of them." } ### list action ### describe_list() { echo "List available completions" } describe_list_options() { echo "--global : Show only global completion state" echo "--user : Show only user completion state" } do_list() { local targets i targets=( $(find_targets) ) # by default, show 'effective' state USE_GLOBAL=1 USE_USER=1 case "${1}" in --global) USE_USER= shift ;; --user) USE_GLOBAL= shift ;; esac [[ ${#} -ne 0 ]] && die "Invalid or conflicting options" for (( i = 0; i < ${#targets[@]}; i++ )); do is_enabled ${opts} "${targets[i]}" \ && targets[i]=$(highlight_marker "${targets[i]}") done write_list_start "Available completions:" write_numbered_list -m "(none found)" "${targets[@]}" } ### enable action ### describe_enable() { echo "Enable specified completion(s)" } describe_enable_parameters() { echo "" } describe_enable_options() { echo "--global : Affect only global completion state (default when root)" echo "--user : Affect only user completion state (default when not root)" echo " : Target name or number (from 'list' action)" } do_enable() { USE_GLOBAL= USE_USER= case "${1}" in --global) USE_GLOBAL=1 shift ;; --user) USE_USER=1 shift ;; *) # defaults [[ ${EUID} == 0 ]] && USE_GLOBAL=1 || USE_USER=1 ;; esac [[ $# -eq 0 ]] && die -q "You didn't specify any completions to enable." local bc target targets=( $(find_targets) ) changed=() need_wl=() for bc; do target=${bc} is_number "${target}" && target=${targets[target-1]} [[ -z ${target} || ${target} == */* ]] \ && die -q "Target \"${bc}\" doesn't appear to be valid!" bc=${target} # with --global, check global state # with --user, check effective state (global+user) if USE_GLOBAL=1 is_enabled "${bc}"; then # does it exist? if ! has "${bc}" "${targets[@]}"; then write_error_msg "${bc} doesn't exist" continue fi write_warning_msg "${bc} is not disabled" # maybe it's masked per-user? if ! USE_USER=1 is_enabled "${bc}"; then need_wl+=( "${bc}" ) fi continue fi # first, handle blacklist requested (global or user) local blacklist [[ -n ${USE_GLOBAL} ]] \ && blacklist=${COMPLETIONS_GLOBAL_BLACKLIST} \ || blacklist=${COMPLETIONS_USER_BLACKLIST} # (user blacklist may not exist if whitelist is necessary) if [[ -f ${blacklist} ]]; then sed -i -e "/^${bc}\$/d" "${blacklist}" || die fi # then, check if it's still blacklisted for user # (need to be whitelisted) if ! USE_GLOBAL=1 USE_USER=1 is_enabled "${bc}"; then if [[ -n ${USE_USER} ]]; then # put it on our whitelist if --user echo "${bc}" >> "${COMPLETIONS_USER_WHITELIST}" || die else # otherwise, warn about it need_wl+=( "${bc}" ) fi fi changed+=( "${bc}" ) done if [[ -n ${need_wl[@]} ]]; then write_warning_msg "Note: some of the listed modules are disabled for your user." write_warning_msg "You may want to enable them using:" write_warning_msg " eselect bashcomp enable --user ${need_wl[*]}" echo fi if [[ -n ${changed[@]} ]]; then post_update "${changed[@]}" fi } ### disable action ### describe_disable() { echo "Disable specified completion(s)" } describe_disable_parameters() { echo "" } describe_disable_options() { echo "--global : Affect only global completion state (default when root)" echo "--user : Affect only user completion state (default when not root)" echo " : Target name or number (from 'list' action)" } do_disable() { USE_GLOBAL= USE_USER= case "${1}" in --global) USE_GLOBAL=1 shift ;; --user) USE_USER=1 shift ;; *) # defaults [[ ${EUID} == 0 ]] && USE_GLOBAL=1 || USE_USER=1 ;; esac [[ $# -eq 0 ]] && die -q "You didn't specify any completions to disable." local bc target targets=( $(find_targets) ) changed=() need_bl=() for bc; do target=${bc} is_number "${target}" && target=${targets[target-1]} [[ -z ${target} || ${target} == */* ]] \ && die -q "Target \"${bc}\" doesn't appear to be valid!" bc=${target} # does it exist? if ! has "${bc}" "${targets[@]}"; then write_error_msg "${bc} doesn't exist" continue fi if ! is_enabled "${bc}"; then write_warning_msg "${bc} is disabled" # maybe it's whitelisted per-user? if USE_USER=1 is_enabled "${bc}"; then need_bl+=( "${bc}" ) fi continue fi # first, handle blacklist requested (global or user) local blacklist [[ -n ${USE_GLOBAL} ]] \ && blacklist=${COMPLETIONS_GLOBAL_BLACKLIST} \ || blacklist=${COMPLETIONS_USER_BLACKLIST} echo "${bc}" >> "${blacklist}" || die # when doing user-wide changes, also clean up the whitelist if [[ -n ${USE_USER} ]]; then sed -i -e "/^${bc}\$/d" "${COMPLETIONS_USER_WHITELIST}" || die else # otherwise, check if the user didn't whitelist it if USE_USER=1 is_enabled "${bc}"; then # and warn about it need_bl+=( "${bc}" ) fi fi changed+=( "${bc}" ) done if [[ -n ${need_bl[@]} ]]; then write_warning_msg "Note: some of the listed modules are enabled for your user." write_warning_msg "You may want to disable them using:" write_warning_msg " eselect bashcomp disable --user ${need_bl[*]}" echo fi if [[ -n ${changed[@]} ]]; then post_update "${changed[@]}" fi } ### prune action ### describe_prune() { echo "Prune stale blacklist and whitelist entries" } describe_prune_options() { echo "--global : Prune non-existent global completions" echo "--user : Prune non-existent and non-effective user completions" } do_prune() { local targets i do_global do_user targets=( $(find_targets) ) # by default, do both do_global=1 do_user=1 case "${1}" in --global) do_user= shift ;; --user) do_global= shift ;; esac [[ ${#} -ne 0 ]] && die "Invalid options" local f files=() [[ -n ${do_global} ]] && files+=( "${COMPLETIONS_GLOBAL_BLACKLIST}" ) [[ -n ${do_user} ]] && files+=( "${COMPLETIONS_USER_WHITELIST}" "${COMPLETIONS_USER_BLACKLIST}" ) for f in "${files[@]}"; do local tmpfile=${f}.new # try to write it touch "${tmpfile}" || die while read; do # allow comments to pass-through if [[ ${REPLY} != '#'* ]]; then # prune non-existing files has "${REPLY}" "${targets[@]}" || continue if [[ ${f} == ${COMPLETIONS_USER_WHITELIST} ]]; then # remove stale whitelist entries when not on global bl USE_GLOBAL=1 is_enabled && continue # or if blacklisted by user USE_USER=1 is_enabled || continue elif [[ ${f} == ${COMPLETIONS_USER_BLACKLIST} ]]; then # remove redundant blacklist entries USE_GLOBAL=1 is_enabled || continue fi fi echo "${REPLY}" > "${tmpfile}" done < "${f}" || die if [[ ! -s ${tmpfile} ]]; then # empty, so remove both rm "${tmpfile}" "${f}" || die else # replace mv "${tmpfile}" "${f}" || die fi done }