bin/Makefile.am | 1 + bin/eselect.in | 150 +++++++++++- libs/Makefile.am | 7 +- libs/alternatives-common.bash.in | 510 +++++++++++++++++++++++++++++++++++++++ libs/alternatives.bash.in | 316 ++++++++++++++++++++++++ modules/Makefile.am | 1 + modules/alternatives.eselect | 178 ++++++++++++++ modules/modules.eselect | 288 ++++++++++++++-------- 8 files changed, 1343 insertions(+), 108 deletions(-) diff --git a/bin/Makefile.am b/bin/Makefile.am index 20902c1..ad08867 100644 --- a/bin/Makefile.am +++ b/bin/Makefile.am @@ -4,6 +4,7 @@ EXTRA_DIST = eselect.in dosed = @SED@ -e 's%\@BASH\@%$(BASH)%g' \ -e 's%\@DATADIR\@%$(datadir)%g' \ -e 's%\@EPREFIX\@%$(EPREFIX)%g' \ + -e 's%\@LIBEXECDIR\@%$(datadir)%g' \ -e 's%\@VERSION\@%$(VERSION)$(EXTRAVERSION)%g' % : %.in diff --git a/bin/eselect.in b/bin/eselect.in index 934a10a..5242ed3 100755 --- a/bin/eselect.in +++ b/bin/eselect.in @@ -22,10 +22,18 @@ ESELECT_DATA_PATH="@DATADIR@/eselect" # Where are modules installed by default? ESELECT_DEFAULT_MODULES_PATH="${ESELECT_DATA_PATH}/modules" +# Where are auto-generated modules placed? (e.g. from alternatives-2.eclass) +ESELECT_AUTO_GENERATED_MODULES_PATH="${ESELECT_DEFAULT_MODULES_PATH}/auto" + +# Where the users store their modules +ESELECT_USER_MODULES_PATH="${ROOT}${HOME}/.eselect/modules" + # Look in these places for modules -ESELECT_MODULES_PATH=( \ - "${HOME}/.eselect/modules" \ - "${ESELECT_DEFAULT_MODULES_PATH}" ) +ESELECT_MODULES_PATH=( + "${ESELECT_USER_MODULES_PATH}}" + "${ESELECT_DEFAULT_MODULES_PATH}" + "${ESELECT_AUTO_GENERATED_MODULES_PATH}" +) # Look in this place for libraries ESELECT_CORE_PATH="${ESELECT_DATA_PATH}/libs" @@ -64,12 +72,27 @@ fi # Load core functions source "${ESELECT_CORE_PATH}/core.bash" || exit 255 # Load necessary functions for the main script -inherit manip output path-manipulation tests +inherit manip output path-manipulation tests config # Sneaky trick to make die in subshells work. If you don't get # it, don't ask... trap 'echo "exiting" >&2; exit 250' 15 +# es_find_module foo +# Find and echo the filename of the foo module. If there's no foo module, +# die. +es_find_module() { + local modname="$1" modpath="" modfile="" + [[ -z ${modname} ]] && die "Usage: ${FUNCNAME} " + for modpath in "${ESELECT_MODULES_PATH[@]}" ; do + [[ -f ${modpath}/${modname}.eselect ]] && break + done + + modfile="${modpath}/${modname}.eselect" + [[ -r ${modfile} ]] || die -q "Can't load module ${modname}" + echo ${modfile} +} + # es_do_usage # Display eselect usage es_do_usage() { @@ -81,14 +104,10 @@ es_do_usage() { es_do_help() { es_do_usage echo - # display all recognized global options - write_list_start "Global options:" - write_kv_list_entry "--brief" "Make output shorter" - write_kv_list_entry "--colour=" \ - "Enable or disable colour output (default 'auto')" + es_do_list-options echo # display all available eselect modules - do_action modules list + es_do_list-modules } # es_do_version @@ -100,6 +119,114 @@ es_do_version() { echo "Distributed under the terms of the GNU GPL version 2 or later." } +# es_do_list-options +# Display all recognized global options +es_do_list-options() { + write_list_start "Global options:" + write_kv_list_entry "--brief" "Make output shorter" + write_kv_list_entry "--colour=" "Enable or disable colour output (default 'auto')" + write_kv_list_entry "--debug" "Debug eselect (enable set -x)" +} + +# es_do_list-modules +# Display all available eselect modules DEPRECATED +es_do_list-modules() { + do_action modules list $@ +} + +### print-* actions, for use with bash_completion and zsh-completion ### + +# es_do_print-modules +# +# Display all availble eselect modules in a way that's useful to bash +# completion / zsh completion +es_do_print-modules() { + local ret=1 path module group groupname want_descriptions + + while [[ -n $@ ]]; do + case "${1}" in + --descriptions) + want_descriptions='yes' ;; + --group) + groupname=${2} + [[ -z "$groupname" ]] && die -q "Required option (group name) missing." + shift ;; + esac + shift + done + + if [[ ${groupname} == Built-in || -z ${groupname} ]]; then + for module in help usage version print-{modules,actions,options}; do + echo "${module}" + done + [[ ${groupname} == Built-in ]] && return 0 + fi + + # TODO: factor this out in modules.eselect's do_list() + for path in "${ESELECT_MODULES_PATH[@]}" ; do + [[ -d "${path}" ]] || continue + for file in "${path}"/*.eselect ; do + [[ -f "${file}" ]] || continue + if [[ -n "${groupname}" ]]; then + group=$(load_config "${file}" ESELECT_MODULE_GROUP) + [[ "${groupname}" == "${group}" || + ( "${groupname}" == Extra && -z "${group}" ) ]] || continue + fi + module="${file##*/}" + module="${module%%.eselect}" + echo "${module}${want_descriptions:+:$(load_config "${file}" DESCRIPTION)}" + ret=0 + done + done + return $ret +} + +# es_do_print-actions +# +# Display all available actions for the given module. +es_do_print-actions() { + local modfile="$(es_find_module "${1}")" actions action want_descriptions + [[ "${2}" == "--descriptions" ]] && want_descriptions='yes' + ( + source "${modfile}" 2>/dev/null \ + || die "Couldn't source ${modfile}" + actions=( $(declare -F \ + | sed -n -e 's/^declare\s\+-f\s\+do_//p' \ + | egrep -v '^(action|help|usage|version)$' \ + | sort ) ) + for action in "${actions[@]}" ; do + echo "${action}${want_descriptions:+:$(describe_${action})}" + done + ) +} + +# es_do_print-options +# +# Display all available options for the given module and action +es_do_print-options() { + local modfile action want_descriptions + + [[ "${1}" == "--descriptions" ]] && want_descriptions='yes' && shift + + if [[ -z ${1} ]]; then + echo "--debug${want_descriptions:+:Debug eselect (enable set -x)}" + echo "--no-color${want_descriptions:+:Disable coloured output}" + echo "--no-colour${want_descriptions:+:Disable coloured output}" + elif [[ -n ${2} ]]; then + modfile="$(es_find_module "${1}")" + action=${2} + shift 2 + ( + source "${modfile}" 2>/dev/null \ + || die "Couldn't source ${modfile}" + is_function options_${action} || return 1 + options_${action} ${want_descriptions:+--descriptions} "$@" || return 2 + ) + else + die "Usage: ${FUNCNAME} [--descriptions] [ ]" + fi +} + ### main code ### # figure out what the action is. we need to know whether we're @@ -141,6 +268,9 @@ while [[ ${1##--} != "$1" ]]; do *) die -q "Invalid argument for ${1%%=*} option" ;; esac ;; + debug) + set -x + ;; help|version) [[ -z ${action} ]] || die -q "Too many parameters" action=${1##--} diff --git a/libs/Makefile.am b/libs/Makefile.am index 027ef73..a5fe373 100644 --- a/libs/Makefile.am +++ b/libs/Makefile.am @@ -1,6 +1,8 @@ eselectlibsdir = $(datadir)/$(PACKAGE_NAME)/libs/ eselectlibs_DATA = \ + alternatives.bash \ + alternatives-common.bash \ config.bash \ core.bash \ default.eselect \ @@ -14,6 +16,8 @@ eselectlibs_DATA = \ tests.bash EXTRA_DIST = \ + alternatives.bash.in \ + alternatives-common.bash.in \ config.bash.in \ core.bash.in \ default.eselect.in \ @@ -30,7 +34,8 @@ dosed = @SED@ \ -e 's%\@SED\@%@SED@%g' \ -e 's%\@PORTAGEQ\@%@PORTAGEQ@%g' \ -e 's%\@ENV_UPDATE\@%@ENV_UPDATE@%g' \ - -e 's%\@CANONICALISE\@%@CANONICALISE@%g' + -e 's%\@CANONICALISE\@%@CANONICALISE@%g' \ + -e 's%\@sysconfdir\@%@sysconfdir@%g' %.bash : %.bash.in @$(dosed) $< > $@ diff --git a/libs/alternatives-common.bash.in b/libs/alternatives-common.bash.in new file mode 100644 index 0000000..8753b89 --- /dev/null +++ b/libs/alternatives-common.bash.in @@ -0,0 +1,510 @@ +# Copyright (c) 2005-2015 Gentoo Foundation +# Copyright (c) 2008 Mike Kelly +# Copyright (c) 2009-2013 David Leverton +# Copyright (c) 2009-2014 Bo Ørsted Andresen +# +# This file is part of the 'eselect' tools framework. +# +# eselect is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation, either version 2 of the License, or (at your option) any later +# version. +# +# eselect is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# eselect. If not, see . + +inherit config output path-manipulation tests + +: "${ALTERNATIVESDIR_ROOTLESS:=@sysconfdir@/env.d/alternatives}" +: "${ALTERNATIVESDIR_ROOT:=${ROOT%/}}" +ALTERNATIVESDIR="${ALTERNATIVESDIR_ROOT}${ALTERNATIVESDIR_ROOTLESS}" + +get_current_provider() { + local dieprefix="Could not determine current provider for ${ALTERNATIVE}" + if [[ -L ${ALTERNATIVESDIR}/${ALTERNATIVE}/_current ]]; then + local provider=$(readlink "${ALTERNATIVESDIR}/${ALTERNATIVE}/_current" || die "${dieprefix}: readlink ${symlink} failed") + [[ ${provider} == */* ]] && die "${dieprefix}: malformed target for ${symlink}" + + if [[ -L ${ALTERNATIVESDIR}/${ALTERNATIVE}/${provider} || + ( -e ${ALTERNATIVESDIR}/${ALTERNATIVE}/${provider} && ! -d ${ALTERNATIVESDIR}/${ALTERNATIVE}/${provider} ) ]]; then + die "${dieprefix}: ${ALTERNATIVESDIR}/${ALTERNATIVE}/${provider} is not a directory" + fi + + echo "${provider}" + + elif [[ -e ${ALTERNATIVESDIR}/${ALTERNATIVE}/_current ]]; then + die "${dieprefix}: ${ALTERNATIVESDIR}/${ALTERNATIVE}/_current is not a symlink" + fi +} + +compare_importance() { + local IFS=. + local a=( ${1} ) b=( ${2} ) + local -i i=0 + while (( i<${#a[@]} && i<${#b[@]} )); do + if (( a[i]b[i] )); then + return 1 + fi + i+=1 + done + (( i<${#b[@]} )) +} + +sort_providers() { + local begin=${1:-0} + local count=${2:-${#providers[@]}} + [[ ${count} -le 1 ]] && return 0 + sort_providers ${begin} $((count/2)) + sort_providers $((begin+count/2)) $((count-count/2)) + local left=( "${providers[@]:begin:count/2}" ) + local right=( "${providers[@]:begin+count/2:count-count/2}" ) + local -i x i=0 j=0 + for (( x=begin; x=${#right[@]} )) || { (( i<${#left[@]} )) && compare_importance "${left[i]%%:*}" "${right[j]%%:*}"; }; then + providers[x]=${left[i++]} + else + providers[x]=${right[j++]} + fi + done +} + +get_providers() { + local p= importance providers=() + for p in "${ALTERNATIVESDIR}/${ALTERNATIVE}"/* ; do + [[ -d ${p} && ! -L ${p} ]] || continue + p=${p##*/} + + [[ -e "${ALTERNATIVESDIR}/${ALTERNATIVE}/${p}/_importance" ]] && \ + importance=$(< "${ALTERNATIVESDIR}/${ALTERNATIVE}/${p}/_importance") + importance=${importance:-0} + [[ "${importance}" =~ ^[0123456789]+(\.[0123456789]+)*$ ]] || die "_importance (${importance}) for ${p} is not a dot-separated list of integers" + + providers+=( "${importance}:${p}" ) + done + + sort_providers + for (( p=${#providers[@]}-1 ; p>=0 ; --p )); do + echo "${providers[p]#*:}" + done +} + +has_provider() { + local provider=${1} item providers=( $(get_providers) ) + for item in ${providers[@]}; do + [[ ${item} == ${provider} ]] && return 0 + done + return 1 +} + +_options_parameters() { + [[ -n ${2} && ${2} != --descriptions ]] && die -q "Unrecognised option ${2}" + local describe_func=describe_${1#options_}_options descriptions=${2} opt options oldifs=$IFS + if is_function ${describe_func}; then + IFS=$'\n' + options=( $(${describe_func}) ) + IFS=$oldifs + for opt in "${options[@]}"; do + [[ ${opt} == --* ]] || continue + if [[ -n ${descriptions} ]]; then + echo "${opt/ : /:}" + else + echo "${opt%% : *}" + fi + done + fi +} + +### set action stub ### + +# not available in "eselect alternatives", but needed by do_update + +alternatives_do_set() { + [[ -z "${ALTERNATIVE}" ]] && die "Need to set ALTERNATIVE in the eselect module" + + local force provider providers + if [[ ${1} == --force ]]; then + force=yes + shift + fi + local idx=${1} + if [[ ${1} == +(-|+|[[:digit:]]) ]]; then + idx=${1#+(-|+)} + providers=( $(get_providers) ) + (( ${idx} <= ${#providers[@]} )) || die -q "The given provider with index (${idx}) does not exist" + provider=${providers[${idx}-1]} + else + provider="${1}" + fi + [[ -z "${provider}" ]] && die -q "Missing required parameter 'provider'" + local dieprefix="Could not set provider ${provider} for alternative ${ALTERNATIVE}" + + if [[ ! -d ${ALTERNATIVESDIR}/${ALTERNATIVE}/${provider} ]] ; then + if is_number ${provider} ; then + providers=( $(get_providers) ) + [[ -n ${providers[${idx}-1]} ]] && \ + die -q "The given provider (${provider}) does not exist" + fi + die -q "The given provider (${provider}) does not exist" + fi + + local symlink newsymlinks=() oldsymlinks=() + + while read -r -d '' symlink; do + local nicesymlink=${symlink#.} + nicesymlink=${nicesymlink//+(\/)/\/} + [[ ${nicesymlink} == /* ]] || die "${dieprefix}: bad symlink ${symlink}?" + [[ ${nicesymlink} == */ ]] && die "${dieprefix}: bad symlink ${symlink}?" + + newsymlinks+=( "${nicesymlink}" ) + done < <( + cd "${ALTERNATIVESDIR}/${ALTERNATIVE}/${provider}" || die "${dieprefix}: cd failed" + find . -type l -print0 | LC_ALL=C sort -r -u -z) + [[ ${#newsymlinks[@]} -gt 0 ]] || die "${dieprefix}: does not provide any symlinks?" + + if [[ -f ${ALTERNATIVESDIR}/${ALTERNATIVE}/_current_list ]]; then + while read -r -d '' symlink; do + local nicesymlink=${symlink//+(\/)/\/} + [[ ${nicesymlink} == /* ]] || die "${dieprefix}: old provider ${oldcur} provides bad symlink ${symlink}?" + [[ ${nicesymlink} == */ ]] && die "${dieprefix}: old provider ${oldcur} provides bad symlink ${symlink}?" + + oldsymlinks+=( "${nicesymlink}" ) + done < <(LC_ALL=C sort -r -u -z "${ALTERNATIVESDIR}/${ALTERNATIVE}/_current_list") + [[ ${#oldsymlinks[@]} -gt 0 ]] || die "${dieprefix}: old provider ${oldcur} does not provide any symlinks?" + + elif [[ -L ${ALTERNATIVESDIR}/${ALTERNATIVE}/_current_list || -e ${ALTERNATIVESDIR}/${ALTERNATIVE}/_current_list ]]; then + die "${dieprefix}: ${ALTERNATIVESDIR}/${ALTERNATIVE}/_current_list is not a file" + fi + + local pass errors= + for pass in check perform; do + local -i new_i=0 old_i=0 + while [[ -n ${newsymlinks[new_i]} || -n ${oldsymlinks[old_i]} ]]; do + + if ( LC_ALL=C; [[ ${newsymlinks[new_i]} < ${oldsymlinks[old_i]} ]] ); then + if [[ ${pass} == check ]]; then + if [[ -L ${EROOT%/}${oldsymlinks[old_i]} ]]; then + : + elif [[ -d ${EROOT%/}${oldsymlinks[old_i]} ]]; then + write_error_msg "Can't remove ${EROOT%/}${oldsymlinks[old_i]}: is a directory${force:+ which is a fatal error that cannot be ignored by --force}" + errors=yes + elif [[ -e ${EROOT%/}${oldsymlinks[old_i]} ]]; then + if [[ -n ${force} ]]; then + write_warning_msg "Removing ${EROOT%/}${oldsymlinks[old_i]} due to --force: is not a symlink" + else + write_error_msg "Refusing to remove ${EROOT%/}${oldsymlinks[old_i]}: is not a symlink (use --force to override)" + errors=yes + fi + fi + + elif [[ ${pass} == perform ]]; then + rm -f "${EROOT%/}${oldsymlinks[old_i]}" || die "${dieprefix}: rm failed" + else + die "${dieprefix}: unknown \${pass} ${pass}???" + fi + + old_i+=1 + + else + local target=${ALTERNATIVESDIR_ROOTLESS#${EPREFIX}/}/${ALTERNATIVE}/_current${newsymlinks[new_i]} dir=${newsymlinks[new_i]%/*} + while [[ -n ${dir} ]]; do + target=../${target} + dir=${dir%/*} + done + + if [[ ${pass} == check ]]; then + if [[ -L ${EROOT%/}${newsymlinks[new_i]} ]]; then + : + elif [[ -d ${EROOT%/}${newsymlinks[new_i]} ]]; then + write_error_msg "Can't overwrite ${EROOT%/}${newsymlinks[new_i]}: is a directory${force:+ which is a fatal error that cannot be ignored by --force}" + errors=yes + elif [[ -e ${EROOT%/}${newsymlinks[new_i]} ]]; then + if [[ -n ${force} ]]; then + write_warning_msg "Overwriting ${EROOT%/}${newsymlinks[new_i]} due to --force: is not a symlink" + else + write_error_msg "Refusing to overwrite ${EROOT%/}${newsymlinks[new_i]}: is not a symlink (use --force to override)" + errors=yes + fi + fi + + elif [[ ${pass} == perform ]]; then + mkdir -p "${EROOT%/}${newsymlinks[new_i]%/*}" || die "${dieprefix}: mkdir -p failed" + ln -snf "${target#/}" "${EROOT%/}${newsymlinks[new_i]}" || die "${dieprefix}: ln -snf failed" + else + die "${dieprefix}: unknown \${pass} ${pass}???" + fi + + [[ ${newsymlinks[new_i]} == ${oldsymlinks[old_i]} ]] && old_i+=1 + new_i+=1 + fi + done + + [[ -n ${errors} ]] && die "${dieprefix}: see previous errors" + done + + local oldcur="$(get_current_provider)" + ln -snf "${provider}" "${ALTERNATIVESDIR}/${ALTERNATIVE}/_current" || die "${dieprefix}: ln -snf failed" + + : >"${ALTERNATIVESDIR}/${ALTERNATIVE}/_current_list" || die "${dieprefix}: emptying/creating _current_list failed" + for symlink in "${newsymlinks[@]}"; do + echo -n -e "${symlink}\\0" >>"${ALTERNATIVESDIR}/${ALTERNATIVE}/_current_list" || die "${dieprefix}: appending ${symlink} to _current_list failed" + done + return 0 +} + +### add action ### +alternatives_describe_add() { + echo "Add provider to selected alternative" +} + +alternatives_describe_add_parameters() { + echo " [ [...]]" +} + +alternatives_describe_add_options() { + echo " : the name of the alternative provider" + echo " : interger value, representing the importance of the provider" + echo " : source of the symlink" + echo " : destination for the symlink" +} + +alternatives_do_add() { + [[ -z "${ALTERNATIVE}" ]] && die "Need to set ALTERNATIVE in the eselect module" + + (( $# >= 4 )) && (( ($#-2)%2 == 0)) || \ + die "exactly 3+N*2 arguments where N>=1 required" + local provider=${1} + local importance=${2} + shift 2 + local index src target ret=0 + local provider_dir="${ALTERNATIVESDIR}/${ALTERNATIVE}/${provider}" + + # Creating base dir + if [[ ! -e "${provider_dir}" ]]; then + mkdir -p "${provider_dir}" || die "Failed to create ${provider_dir}" + elif [[ ! -d "${provider_dir}" ]]; then + die "${provider_dir} exists but is a file" + else + die "${provider_dir} already present. You need to remove it before adding it again" + fi + + # Make sure importance is a signed integer + if [[ -n ${importance} ]] && ! [[ ${importance} =~ ^[0-9]+(\.[0-9]+)*$ ]]; then + die "Invalid importance (${importance}) detected" + else + # Setting importance + echo ${importance} > "${provider_dir}/_importance" + fi + + # Process source-target pairs + while (( $# >= 2 )); do + src=${1//+(\/)/\/}; target=${2//+(\/)/\/} + if [[ ${src} != /* ]]; then + die "Source path must be absolute, but got ${src}" + else + local reltarget= dir=${provider_dir#${ALTERNATIVESDIR_ROOT}${EPREFIX}}${src%/*} + while [[ -n ${dir} ]]; do + reltarget+=../ + dir=${dir%/*} + done + + reltarget=${reltarget%/} + [[ ${target} == /* ]] || reltarget+=${src%/*}/ + reltarget+=${target} + mkdir -p "${provider_dir}${src%/*}" || die "Failed to create ${provider_dir}${src%/*}" + ln -sf "${reltarget}" "${provider_dir}${src}" || die "Failed to create symlink" + # The -e test will fail if existing symlink points to non-existing target, + # so check for -L also. + # Say ${ED}/sbin/init exists and links to /bin/systemd (which doesn't exist yet). +# if [[ -e ${ED}${src} || -L ${ED}${src} ]]; then +# local fulltarget=${target} +# [[ ${fulltarget} != /* ]] && fulltarget=${src%/*}/${fulltarget} +# if [[ -e ${ED}${fulltarget} || -L ${ED}${fulltarget} ]]; then +# die "${src} defined as provider for ${fulltarget}, but both already exist in \${ED}" +# else +# mv "${ED}${src}" "${ED}${fulltarget}" || die +# fi +# fi + fi + shift 2 + done +} + +alternatives_options_add() { + _options_parameters ${FUNCNAME#alternatives_} "$@" +} + +### remove action ### + +alternatives_describe_remove() { + echo "Remove a provider for selected alternative" +} + +alternatives_describe_remove_parameters() { + echo "" +} + +alternatives_describe_remove_options() { + echo " : the name of the alternative provider to be removed" +} + +alternatives_do_remove() { + [[ -z "${ALTERNATIVE}" ]] && die "Need to set ALTERNATIVE in the eselect module" + + if [[ -d "${ALTERNATIVESDIR}/${ALTERNATIVE}/${provider}" ]]; then + rm -r "${ALTERNATIVESDIR}/${ALTERNATIVE}/${provider}" || \ + die "Failed to remove '${provider}' for '${ALTERNATIVE}'" + else + einfo "'${provider}' is not a provider for '${ALTERNATIVE}'" + fi +} + +alternatives_options_remove() { + _options_parameters ${FUNCNAME#alternatives_} "$@" +} + +### update action ### + +# available in both "eselect alternatives" and individual modules + +alternatives_describe_update() { + echo "Set a default provider if no valid one currently exists" +} + +alternatives_describe_update_parameters() { + echo "[--best] [--ignore] " +} + +alternatives_describe_update_options() { + echo "--best : update to the best provider even if one is already selected" + echo "--ignore : update to any valid provider EXCEPT the specified provider" + echo " : the name of the provider to use" +} + +alternatives_do_update() { + [[ -z "${ALTERNATIVE}" ]] && die "Need to set ALTERNATIVE in the eselect module" + + local p cur=$(get_current_provider) providers=( $(get_providers) ) best ignore + if [[ "--best" == ${1} ]] ; then + shift + best=1 + fi + if [[ "--ignore" == ${1} ]] ; then + # Try everything except setting the provider to the given + # one. So, if it isn't the given one, we end up doing + # nothing. Bug #128 + shift + ignore=${1} + fi + [[ -n ${best} && -n ${1} && -z ${ignore} ]] && die -q "Cannot specify both --best and a provider" + + if [[ -n ${best} ]] ; then + # set best provider + : # fall through to "switch to first available" loop below + elif [[ $# == "0" ]] && [[ -z ${cur} ]]; then + # if nothing is specified + : # fall through to "switch to first available" loop below + elif [[ ${cur} == ${1} && -z ${ignore} ]]; then + # if current provider was just updated, reselect it since it could have changed + alternatives_do_set "${cur}" && return 0 + elif [[ -n ${cur} && ${cur} != ${ignore} ]] ; then + # verify existing provider's symlinks + local p= bad=0 + while read -r -d '' p ; do + [[ -L "${EROOT%/}${p}" && -e "${EROOT%/}${p}" ]] || (( bad++ )) + done < "${ALTERNATIVESDIR}/${ALTERNATIVE}/_current_list" + + [[ "${bad}" -eq 0 ]] && return 0 + # fix existing provider if possible + has "${cur}" "${providers[@]}" && alternatives_do_set "${cur}" && return 0 + elif has "${1}" "${providers[@]}" && [[ -z ${ignore} ]] ; then + # switch to new provider if none was set before or it can't be fixed + alternatives_do_set "${1}" && return 0 + fi + + # if no valid provider has been selected switch to first available, valid + # provider, sorted according to importance + for p in "${providers[@]}"; do + [[ ${ignore} != ${p} ]] && alternatives_do_set "${p}" && return 0 + done + + # if a provider is set but no providers are available anymore cleanup + cur=$(get_current_provider) + if [[ -n ${cur} ]]; then + alternatives_do_unset "${cur}" && return 2 + fi + # if no provider is set and none are available that are not ignored, return 2 for cleanup + [[ -z ${providers[@]} || ${providers[@]} == ${ignore} ]] && return 2 + + # we tried everything to select a valid provider, but failed + return 1 +} + +alternatives_options_update() { + _options_parameters ${FUNCNAME#alternatives_} "$@" + if [[ -n ${ALTERNATIVE} ]]; then + get_providers + else + for alt in ${ALTERNATIVESDIR_ROOTLESS}/_*/*/_importance; do + echo ${alt} | cut -d/ -f5 + done | sort -u + fi +} + +### unset action stub ### + +# not available in "eselect alternatives", but needed by do_update + +alternatives_do_unset() { + [[ -z "${ALTERNATIVE}" ]] && die "Need to set ALTERNATIVE in the eselect module" + + local force= + if [[ ${1} == --force ]]; then + force=yes + shift + fi + + local cur="$(get_current_provider)" p= + [[ -n "${cur}" ]] || die -q "Nothing to unset" + local dieprefix="Could not unset provider for ${ALTERNATIVE}" + + local one=false symlink pass errors= + for pass in check perform; do + while read -r -d '' symlink; do + one=true + if [[ ${pass} == check ]]; then + if [[ -L ${EROOT%/}${symlink} ]]; then + : + elif [[ -d ${EROOT%/}${symlink} ]]; then + write_error_msg "Can't remove ${EROOT%/}${symlink}: is a directory${force:+ which is a fatal error that cannot be ignored by --force}" + errors=yes + elif [[ -e ${EROOT%/}${symlink} ]]; then + if [[ -n ${force} ]]; then + write_warning_msg "Removing ${EROOT%/}${symlink} due to --force: is not a symlink" + else + write_error_msg "Refusing to remove ${EROOT%/}${symlink}: is not a symlink (use --force to override)" + errors=yes + fi + fi + + elif [[ ${pass} == perform ]]; then + rm -f "${EROOT%/}${symlink}" || die "${dieprefix}: rm failed" + else + die "${dieprefix}: unknown \${pass} ${pass}???" + fi + done <"${ALTERNATIVESDIR}/${ALTERNATIVE}/_current_list" + + [[ -n ${errors} ]] && die "${dieprefix}: see previous errors" + done + + ${one} || die "${dieprefix}: does not provide any symlinks?" + + rm "${ALTERNATIVESDIR}/${ALTERNATIVE}"/{_current,_current_list} || die "${dieprefix}: rm failed" +} + +# vim: set ft=eselect sw=4 sts=4 ts=4 et tw=80 : diff --git a/libs/alternatives.bash.in b/libs/alternatives.bash.in new file mode 100644 index 0000000..cf4fa36 --- /dev/null +++ b/libs/alternatives.bash.in @@ -0,0 +1,316 @@ +# Copyright (c) 2005-2015 Gentoo Foundation +# Copyright (c) 2008 Mike Kelly +# Copyright (c) 2009-2013 David Leverton +# Copyright (c) 2009-2014 Bo Ørsted Andresen +# +# This file is part of the 'eselect' tools framework. +# +# eselect is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation, either version 2 of the License, or (at your option) any later +# version. +# +# eselect is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# eselect. If not, see . + +inherit alternatives-common + +### show action ### +describe_show() { + echo "Show the current provider in use for ${ALTERNATIVE}" +} + +do_show() { + [[ -z "${ALTERNATIVE}" ]] && die "Need to set ALTERNATIVE in the eselect module" + local current="$(get_current_provider)" + if [[ -z "${current}" ]] ; then + echo "(none)" + return 2 + fi + echo "${current}" +} + +options_show() { + : +} + +### list action ### +describe_list() { + echo "Lists all available providers for ${ALTERNATIVE}" +} + +do_list() { + [[ -z "${ALTERNATIVE}" ]] && die "Need to set ALTERNATIVE in the eselect module" + local n cur= providers=( $(get_providers) ) + write_list_start "Available providers for ${ALTERNATIVE}:" + + cur="$(get_current_provider)" + + if [[ -n "${providers[@]}" ]] ; then + for (( n = 0 ; n < ${#providers[@]} ; ++n )) ; do + [[ ${cur} == "${providers[${n}]}" ]] && \ + providers[${n}]="${providers[${n}]} $(highlight '*')" + done + write_numbered_list "${providers[@]}" + else + write_kv_list_entry "(none found)" "" + fi +} + +options_list() { + : +} + +### files action ### +describe_files() { + echo "Lists symlinks provided by the currently selected provider" +} + +do_files() { + [[ -z "${ALTERNATIVE}" ]] && die "Need to set ALTERNATIVE in the eselect module" + + local cur="$(get_current_provider)" p= + [[ -n "${cur}" ]] || die -q "No selected provider, hence no symlinks provided" + local dieprefix="Could not list symlinks provided for ${ALTERNATIVE}" + + local errors symlink rootsymlink + while read -r -d '' symlink; do + rootsymlink="${EROOT%/}${symlink}" + rootsymlink=${rootsymlink//+(\/)/\/} + echo "${rootsymlink}" + if [[ -L ${rootsymlink} ]]; then + if [[ ! -e ${rootsymlink} ]]; then + write_error_msg "${rootsymlink} is dangling symlink" + errors=yes + fi + elif [[ -d ${rootsymlink} ]]; then + write_error_msg "${rootsymlink} is a directory" + errors=yes + elif [[ -e ${rootsymlink} ]]; then + write_error_msg "${rootsymlink} exists but is not a symlink" + errors=yes + else + write_error_msg "${rootsymlink} does not exist" + errors=yes + fi + done <"${ALTERNATIVESDIR}/${ALTERNATIVE}/_current_list" +} + +options_files() { + : +} + +### set action ### + +describe_set() { + echo "Sets a provider for ${ALTERNATIVE}" +} + +describe_set_parameters() { + echo "[ --force ] " +} + +describe_set_options() { + echo "--force : overwrite or remove existing non-symlink files (but not directories) if necessary" + echo " : the name of the provider to use or the index of the provider preceeded by a dash" +} + +do_set() { + # implementation defined in alternatives-common.bash as needed by do_update + alternatives_do_set "$@" +} + +options_set() { + _options_parameters $FUNCNAME "$@" + get_providers +} + +### add action ### + +describe_add() { + alternatives_describe_add +} + +describe_add_parameters() { + alternatives_describe_add_parameters +} + +describe_add_options() { + alternatives_describe_add_options +} + +do_add() { + alternatives_do_add + + # make sure we have something selected + eselect ${provider} show > /dev/null || eselect ${provider} update --best +} + +### remove action ### + +describe_remove() { + alternatives_describe_remove +} + +describe_remove_parameters() { + alternatives_describe_remove_parameters +} + +describe_remove_options() { + alternatives_describe_remove_options +} + +do_remove() { + local ret + alternatives_do_remove + +# if no provider is present, remove whole alternative + eselect ${provider} update --best > /dev/null; ret=$? + case ret in + 0) + # All good + :;; + 2) + # No provider present anymore + eselect alternatives remove ${ALTERNATIVE};; + *) + write_error_msg "Failed to remove ${ALTERNATIVE}" + errors=yes + esac +} + +### update action ### + +# all functions implemented in alternatives-common.bash as defined for +# both "eselect alternatives" and individual modules + +describe_update() { + alternatives_describe_update +} + +describe_update_parameters() { + alternatives_describe_update_parameters +} + +describe_update_options() { + alternatives_describe_update_options +} + +do_update() { + alternatives_do_update "$@" +} + +options_update() { + alternatives_options_update +} + +### unset action ### + +describe_unset() { + echo "Unset any symlinks created for the current provider for ${ALTERNATIVE}." +} + +describe_unset_parameters() { + echo "[ --force ]" +} + +describe_unset_options() { + echo "--force : remove existing non-symlink files (but not directories) if necessary" +} + +do_unset() { + # implementation defined in alternatives-common.bash as needed by do_update + alternatives_do_unset "$@" +} + +options_unset() { + _options_parameters $FUNCNAME "$@" + get_current_provider +} + +### script action ### + +describe_script() { + echo "Output an evalable script fragment to set PATH, LD_LIBRARY_PATH and MANPATH to use the specified provider" +} + +describe_script_parameters() { + echo "[--sh | --csh] []" +} + +describe_script_options() { + echo "--sh : use Bourne shell syntax (default)" + echo "--csh : use C shell syntax" + echo " : the provider to use or the index of the provider (if not specified, use the system default)" +} + +do_script() { + [[ -z "${ALTERNATIVE}" ]] && die "Need to set ALTERNATIVE in the eselect module" + local syntax=sh provider providers + if [[ ${1} == --sh ]]; then + shift + elif [[ ${1} == --csh ]]; then + syntax=csh + shift + fi + + local idx=${!} + if [[ ${idx} == +(-|+|[[:digit:]]) ]]; then + idx=${1#+(-|+)} + providers=( $(get_providers) ) + (( ${idx} <= ${#providers[@]} )) || die -q "The given provider with index (${idx}) does not exist" + provider=${providers[${idx}-1]} + else + provider="${idx}" + fi + [[ -z "${provider}" ]] && die -q "Missing required parameter 'provider'" + + if [[ ! -d ${ALTERNATIVESDIR}/${ALTERNATIVE}/${provider} ]] ; then + if is_number ${provider} ; then + providers=( $(get_providers) ) + [[ -n ${providers[${idx}-1]} ]] && \ + die -q "The given provider (${provider}) does not exist" + fi + die -q "The given provider (${provider}) does not exist" + fi + + local variables=( PATH LD_LIBRARY_PATH MANPATH ) + [[ -n ${!default_*} ]] && local ${!default_*} + local default_LD_LIBRARY_PATH=$(grep '^[^#]' "${EROOT%/}"/etc/ld.so.conf | tr '\n' ':')/lib:/usr/lib + local default_MANPATH=$(MANPATH= man -C"${EROOT%/}"/etc/man.conf -w) + + local var IFS=: + for var in "${variables[@]}"; do + local defvar=default_${var} path paths=( ) + for path in ${!var}; do + [[ ${path} == ${ALTERNATIVESDIR_ROOTLESS}/${ALTERNATIVE}/* ]] && continue + [[ -n ${provider} && -d ${ALTERNATIVESDIR}/${ALTERNATIVE}/${provider}/${path#/} ]] && paths+=( "${ALTERNATIVESDIR_ROOTLESS}/${ALTERNATIVE}/${provider}/${path#/}" ) + paths+=( "${path}" ) + done + + [[ -n ${provider} ]] && for path in ${!defvar}; do + [[ -d ${ALTERNATIVESDIR}/${ALTERNATIVE}/${provider}/${path#/} ]] && paths+=( "${ALTERNATIVESDIR_ROOTLESS}/${ALTERNATIVE}/${provider}/${path#/}" ) + done + + local newval=${paths[*]} + if [[ ${newval} != ${!var} ]]; then + newval=${newval//\'/\'\\\'\'} + if [[ ${syntax} == sh ]]; then + echo "${var}='${newval}'; export ${var}" + else + echo "setenv ${var} '${newval}'" + fi + fi + done +} + +options_script() { + _options_parameters $FUNCNAME "$@" + get_providers +} + +# vim: set ft=eselect sw=4 sts=4 ts=4 et tw=80 : diff --git a/modules/Makefile.am b/modules/Makefile.am index 75ebe02..92127b6 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -1,6 +1,7 @@ modulesdir=$(datadir)/$(PACKAGE_NAME)/modules/ modules_DATA = \ + alternatives.eselect \ editor.eselect \ env.eselect \ kernel.eselect \ diff --git a/modules/alternatives.eselect b/modules/alternatives.eselect new file mode 100644 index 0000000..840b9c5 --- /dev/null +++ b/modules/alternatives.eselect @@ -0,0 +1,178 @@ +# Copyright (c) 2005-2015 Gentoo Foundation +# Copyright (c) 2008 Mike Kelly +# Copyright (c) 2009-2013 David Leverton +# Copyright (c) 2009-2014 Bo Ørsted Andresen +# +# This file is part of the 'eselect' tools framework. +# +# eselect is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation, either version 2 of the License, or (at your option) any later +# version. +# +# eselect is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# eselect. If not, see . + +inherit alternatives-common + +DESCRIPTION="Maintain Alternatives symlinks" +VERSION="20150521" +MAINTAINER="sci@gentoo.org" + +show_extra_help_text() { + cat <<- ENDOFTEXT + This module is intended for internal use when a package is installed, + updated or removed. For alternatives for which it is likely that + users will need control over which provider is selected, there will be + a separate eselect module installed to do so. + ENDOFTEXT +} + +# all functions implemented in alternatives-common.bash as defined for +# both "eselect alternatives" and individual modules + +### update action ### + +describe_update() { + alternatives_describe_update +} + +describe_update_parameters() { + echo " $(alternatives_describe_update_parameters)" +} + +describe_update_options() { + echo " : the name of the alternative to set a provider for" + alternatives_describe_update_options +} + +do_update() { + local ALTERNATIVE=$1 + shift + [[ -z "${ALTERNATIVE}" ]] && die -q "Missing required parameter 'alternative'" + alternatives_do_update "$@" +} + +options_update() { + alternatives_options_update +} + +### create action ### + +describe_create() { + echo "Create a new alternative" +} + +describe_create_parameters() { + echo "" +} + +describe_create_options() { + echo " : the name of the alternative to create" +} + +do_create() { + [[ $# == 1 ]] || die "Need exactly one alternative name" + + local auto_dir="${ESELECT_AUTO_GENERATED_MODULES_PATH}" + local auto_file="${auto_dir}/${1}.eselect" + + if [[ ! -e "${auto_file}" ]]; then + if [[ ! -e "${auto_dir}" ]]; then + mkdir -p "${auto_dir}" || die "Failed to create ${auto_dir}" + elif [[ ! -d "${auto_dir}" ]]; then + die "${auto_dir} exists but is a file" + fi + cat >> ${auto_file} <<- ENDOFTEXT + DESCRIPTION="${DESCRIPTION} for ${1}" + VERSION="${VERSION}" + MAINTAINER="${MAINTAINER}" + ESELECT_MODULE_GROUP="Alternatives" + + ALTERNATIVE="${1}" + + inherit alternatives + ENDOFTEXT + fi +} + +### delete action ### + +describe_delete() { + echo "Delete an existing alternative" +} + +describe_delete_parameters() { + echo "" +} + +describe_delete_options() { + echo " : the name of the alternative to delete" +} + +do_delete() { + [[ $# == 1 ]] || die "Need exactly one alternative name" + + local auto_dir="${ESELECT_AUTO_GENERATED_MODULES_PATH}" + local auto_file="${auto_dir}/${1}.eselect" + + if [[ -e "${auto_file}" ]]; then + rm "${auto_file}" || die "Failed to remove alternative: ${1}" + fi +} + +### add action ### + +describe_add() { + alternatives_describe_add +} + +describe_add_parameters() { + echo " $(alternatives_describe_add_parameters)" +} + +describe_add_options() { + echo " : the name of the alternative to add a provider for" + alternatives_describe_add_options +} + +do_add() { + local ALTERNATIVE=$1 + shift + [[ -z "${ALTERNATIVE}" ]] && die -q "Missing required parameter 'alternative'" + alternatives_do_add "$@" +} + +options_add() { + alternatives_options_add +} + +### remove action ### + +describe_remove() { + alternatives_describe_remove +} + +describe_remove_parameters() { + echo " $(alternatives_describe_remove_parameters)" +} + +describe_remove_options() { + echo " : the name of the alternative to remove a provider from" + alternatives_describe_remove_options +} + +do_remove() { + local ALTERNATIVE=$1 + shift + [[ -z "${ALTERNATIVE}" ]] && die -q "Missing required parameter 'alternative'" + alternatives_do_remove "$@" +} + +options_remove() { + alternatives_options_remove +} diff --git a/modules/modules.eselect b/modules/modules.eselect index 038f630..78dcbab 100644 --- a/modules/modules.eselect +++ b/modules/modules.eselect @@ -2,7 +2,7 @@ # Copyright 2006-2015 Gentoo Foundation # Distributed under the terms of the GNU GPL version 2 or later -inherit config +inherit config output tests DESCRIPTION="Query eselect modules" MAINTAINER="eselect@gentoo.org" @@ -15,57 +15,94 @@ describe_list() { echo "List all available modules" } -describe_list_options() { - echo "--only-names : Output names of modules only" -} - # List all installed modules do_list() { - local only_names path file module name desc - local -a extra_modules - - if [[ ${1#--} = only-names ]]; then - only_names=1 - shift - fi - [[ $# -gt 0 ]] && die -q "Too many parameters" - - for path in "${ESELECT_MODULES_PATH[@]}" ; do - [[ -d ${path} ]] || continue - for file in "${path}"/*.eselect ; do - [[ -f ${file} ]] || continue - extra_modules=( "${extra_modules[@]}" "${file}" ) - done - done - - if [[ -n ${only_names} ]]; then - # This is mainly intended for bash completion - echo "help" - echo "usage" - echo "version" - for module in "${extra_modules[@]}" ; do - name=${module##*/} - echo "${name%%.eselect}" - done - else - write_list_start "Built-in modules:" - write_kv_list_entry "help" "Display a help message" - write_kv_list_entry "usage" "Display a usage message" - write_kv_list_entry "version" "Display version information" - - if [[ ${#extra_modules[@]} -gt 0 ]] ; then - echo - write_list_start "Extra modules:" - for module in "${extra_modules[@]}" ; do - name=${module##*/} - name=${name%%.eselect} - desc=$(ESELECT_MODULE_NAME=${name} \ - load_config "${module}" DESCRIPTION) - desc=${desc:-No description available} - write_kv_list_entry "${name}" "${desc}" - done - fi - fi + local path file module name desc group groups Extra_modules + + write_list_start "Built-in modules:" + write_kv_list_entry "help" "Display a help message" + write_kv_list_entry "usage" "Display a usage message" + write_kv_list_entry "version" "Display version information" + write_kv_list_entry "print-modules" "Print eselect modules" + write_kv_list_entry "print-actions" "Print actions for a given module" + write_kv_list_entry "print-options" "Print options for a given action" + + for path in "${ESELECT_MODULES_PATH[@]}" ; do + [[ -d ${path} ]] || continue + for file in ${path}/*.eselect ; do + [[ -f ${file} ]] || continue + group=$(load_config "${file}" ESELECT_MODULE_GROUP) + if [[ -n ${group} ]]; then + has ${group} ${groups} || groups+=" ${group}" + declare ${group}_modules+=" ${file}" + else + Extra_modules+=" ${file}" + fi + done + done + + for group in ${groups} Extra; do + local m + m="${group}_modules" + if [[ -n ${!m} ]] ; then + echo + write_list_start "${group} modules:" + for module in ${!m}; do + name=${module##*/} + name=${name%%.eselect} + desc=$(load_config "${module}" DESCRIPTION) + desc=${desc:-No description available} + write_kv_list_entry "${name}" "${desc}" + done + fi + done +} + +### group action + +describe_group() { + echo "Lists all available modules belonging to a specified group." +} + +describe_group_parameters() { + echo "" +} + +do_group() { + local path file groupname="$1" group module modules name desc + [[ -z "$groupname" ]] && die -q "Required option (group name) missing." + + if [[ ${groupname} == Built-in ]]; then + write_list_start "Built-in modules:" + write_kv_list_entry "help" "Display a help message" + write_kv_list_entry "usage" "Display a usage message" + write_kv_list_entry "version" "Display version information" + return 0 + fi + + for path in "${ESELECT_MODULES_PATH[@]}" ; do + [[ -d ${path} ]] || continue + for file in ${path}/*.eselect ; do + [[ -f ${file} ]] || continue + group=$(load_config "${file}" ESELECT_MODULE_GROUP) + [[ ${groupname} == ${group} || + ( ${groupname} == Extra && -z ${group} ) ]] || continue + modules+=" ${file}" + done + done + + if [[ -n ${modules} ]] ; then + write_list_start "${groupname} modules:" + for module in ${modules}; do + name=${module##*/} + name=${name%%.eselect} + desc=$(load_config "${module}" DESCRIPTION) + desc=${desc:-No description available} + write_kv_list_entry "${name}" "${desc}" + done + else + die -q "No modules belonging to ${groupname} was found" + fi } ### has action @@ -75,56 +112,113 @@ describe_has() { } describe_has_parameters() { - echo "" + echo "" } do_has() { - [[ -z $1 ]] && die -q "Required option (module name) missing" - [[ $# -gt 1 ]] && die -q "Too many parameters" - - local modname=$1 modpath - for modpath in "${ESELECT_MODULES_PATH[@]}" ; do - [[ -f ${modpath}/${modname}.eselect ]] && return 0 - done - return 1 + [[ -z $1 ]] && die -q "Required option (module name) missing" + [[ $# -gt 1 ]] && die -q "Too many parameters" + local modname="$1" modpath + [[ -z "$modname" ]] && die -q "Required option (module name) missing." + for modpath in "${ESELECT_MODULES_PATH[@]}" ; do + [[ -f "${modpath}/${modname}.eselect" ]] && return 0 + done + return 1 } ### add action -# *** Commented out. Do we really want to have an eselect module that is -# *** installing other modules in a system directory? Also, this should -# *** go together with a "remove" action. - -# describe_add() { -# echo "Install the given module file somewhere that eselect can find it." -# echo "By default, install to \$HOME/.eselect/modules/, unless running as " -# echo "root. Then, install to ${ESELECT_DATA_PATH}/modules/." -# } - -# describe_add_parameters() { -# echo "" -# } - -# do_add() { -# local local_path="${ROOT}${HOME}/.eselect/modules/" module_file -# local force_default=0 -# -# if [[ $1 = "--force-default-location" ]] ; then -# force_default=1 -# shift -# fi -# module_file=$1 -# -# [[ -z ${module_file} ]] && die -q "Required option (module file) missing" -# -# # TODO: Don't install the module "somewhere", depending on write access. -# # Add an option to control if it goes to the user's or to the system dir. -# if ! cp "${module_file}" "${ESELECT_DEFAULT_MODULES_PATH}" &> /dev/null ; then -# [[ ${force_default} == 1 ]] \ -# && die -q "Failed to install module file to default modules path" -# -# mkdir -p "${local_path}" \ -# || die -q "Failed to create module install directory" -# cp "${module_file}" "${local_path}" \ -# || die -q "Failed to install module file" -# fi -# } + +describe_add() { + echo "Install a module file" +} + +describe_add_options() { + echo "--default : install as global module (default if root)" + echo "--user : install as user's module" +} + +describe_add_parameters() { + echo "" +} + +do_add() { + local module_file module_path + case $1 in + --default) + module_path="${ESELECT_DEFAULT_MODULES_PATH}" + shift + ;; + --user) + module_path="${ESELECT_USER_MODULES_PATH}" + mkdir -p "${module_path}" || die -q "Failed to create directory ${module_path}" + shift + ;; + *) : ;; + esac + module_file=$1 + + [[ -z ${module_file} ]] && die -q "Required module file missing" + + if [[ -n ${module_path} ]]; then + cp "${module_file}" "${module_path}" &> /dev/null || \ + die -q "Failed to install module file to ${module_path}" + else + if ! cp "${module_file}" "${ESELECT_DEFAULT_MODULES_PATH}" &> /dev/null ; then + mkdir -p "${ESELECT_USER_MODULES_PATH}" \ + || die -q "Failed to create directory ${ESELECT_USER_MODULES_PATH}" + cp "${module_file}" "${ESELECT_USER_MODULES_PATH}" \ + || die -q "Failed to install module file ${module_file}" + fi + fi +} + +### remove action + +describe_remove() { + echo "Remove the given module name" +} + +describe_remove_options() { + echo "--default : remove global module (default if root)" + echo "--alternatives : remove generated alternatives module" + echo "--user : remove user module" +} + +describe_remove_parameters() { + echo "" +} + +do_remove() { + local module_name module_path ret + case $1 in + --alternatives) + module_path="${ESELECT_GENERATED_MODULES_PATH}" + shift + ;; + --default) + module_path="${ESELECT_DEFAULT_MODULES_PATH}" + shift + ;; + --user) + module_path="${ESELECT_USER_MODULES_PATH}" + shift + ;; + *) : ;; + esac + module_name=$1 + + [[ -z ${module_name} ]] && die -q "Required module name missing" + + if [[ -n ${module_path} ]]; then + rm "${module_path}/${module_name}.eselect" &> /dev/null + ret=$? + else + for module_path in "${ESELECT_MODULES_PATH[@]}" ; do + rm "${module_path}/${module_name}.eselect" &> /dev/null + ret=$? + done + fi + [[ ${ret} != 0 ]] && die -q "Failed to remove module ${module_name}" +} + +# vim: set ft=eselect sw=4 sts=4 ts=4 et tw=80 :