summaryrefslogtreecommitdiff
path: root/pkgtools/pkg_alternatives/files/pkg_alternatives.sh
diff options
context:
space:
mode:
Diffstat (limited to 'pkgtools/pkg_alternatives/files/pkg_alternatives.sh')
-rw-r--r--pkgtools/pkg_alternatives/files/pkg_alternatives.sh614
1 files changed, 614 insertions, 0 deletions
diff --git a/pkgtools/pkg_alternatives/files/pkg_alternatives.sh b/pkgtools/pkg_alternatives/files/pkg_alternatives.sh
new file mode 100644
index 00000000000..86cd6d23e63
--- /dev/null
+++ b/pkgtools/pkg_alternatives/files/pkg_alternatives.sh
@@ -0,0 +1,614 @@
+#!@SH@
+#
+# $NetBSD: pkg_alternatives.sh,v 1.1.1.1 2005/01/25 13:00:46 jmmv Exp $
+#
+# pkg_alternatives - Generic wrappers for programs with similar interfaces
+# Copyright (c) 2005 Julio M. Merino Vidal <jmmv@NetBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. Neither the name of author nor the names of its contributors may
+# be used to endorse or promote products derived from this software
+# without alternative prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+Conf_Dir=@CONFDIR@@PREFIX@
+Data_Dir=@DATADIR@
+Db_Dir=@DBDIR@@PREFIX@
+Prefix=@PREFIX@
+Prog_Name=${0##*/}
+Verbose=yes
+
+: ${PKG_DBDIR:=@PKG_DBDIR@}
+
+# -------------------------------------------------------------------------
+
+# action_auto_package package
+#
+# Configures the given package to be in automatic mode. I.e., it removes
+# local customizations for all related wrappers, so that only the files in
+# the database are taken into account.
+#
+action_auto_package() {
+ validate_args auto ${#} -eq 1
+ validate_package ${1}
+ pkg=${PKG_DBDIR}/${1}*/+ALTERNATIVES
+
+ set -- $(cat ${pkg} | tr ' ' '¬')
+ while [ ${#} -gt 0 ]; do
+ action_auto_wrapper $(echo ${1} | cut -d '¬' -f 1)
+ shift
+ done
+}
+
+# -------------------------------------------------------------------------
+
+# action_auto_wrapper wrapper
+#
+# Configures the given wrapper to be in automatic mode. I.e., it removes
+# local customizations so that only the files in the database are taken
+# into account.
+#
+# wrapper is a path relative to prefix.
+#
+action_auto_wrapper() {
+ validate_args auto ${#} -eq 1
+ validate_wrapper ${1} yes
+
+ conf=$(get_my_config)/${1}
+ if [ -f ${conf} ]; then
+ info "removing configuration from \`${conf}'"
+ rm -f ${conf}
+ rmdir_p ${conf%/*}
+ else
+ info "no existing configuration for \`${1}'; nothing to be done"
+ fi
+}
+
+# -------------------------------------------------------------------------
+
+# action_list_package
+#
+# Lists all available packages that provide alternatives.
+#
+action_list_package() {
+ validate_args list ${#} -eq 0
+
+ info "looking for alternatives in \`${PKG_DBDIR}'"
+ for d in ${PKG_DBDIR}/*; do
+ [ -f ${d}/+ALTERNATIVES ] && echo ${d##${PKG_DBDIR}/}
+ done
+}
+
+# -------------------------------------------------------------------------
+
+# action_list_wrapper
+#
+# Unavailable action.
+#
+action_list_wrapper() {
+ err "the \`list' action cannot be used in wrapper mode"
+}
+
+# -------------------------------------------------------------------------
+
+# action_manual_package package
+#
+# Configures the given package to be the preferred alternative for all the
+# wrappers associated to it.
+#
+action_manual_package() {
+ validate_args manual ${#} -eq 1
+ validate_package ${1}
+ pkg=${PKG_DBDIR}/${1}*/+ALTERNATIVES
+
+ set -- $(cat ${pkg} | tr ' ' '¬')
+ while [ ${#} -gt 0 ]; do
+ action_manual_wrapper $(echo ${1} | tr '¬' ' ')
+ shift
+ done
+}
+
+# -------------------------------------------------------------------------
+
+# action_manual_wrapper wrapper alternative arguments
+#
+# Configures the given wrapper to use the specified alternative.
+# If running as the administrator, the system-wide settings are changed;
+# otherwise, the user-specific settings are affected.
+#
+# wrapper is a path relative to prefix.
+# alternative is an absolute path.
+#
+action_manual_wrapper() {
+ validate_args manual ${#} -ge 2
+ validate_wrapper ${1} yes
+ validate_alternative ${2}
+
+ conf=$(get_my_config)/${1}
+ info "modifying configuration from \`${conf}'"
+ mkdir_p ${conf%/*}
+ shift
+ echo "${*}" >${conf}
+}
+
+# -------------------------------------------------------------------------
+
+# action_rebuild_package
+#
+# Rebuilds the alternatives database from the package database.
+#
+action_rebuild_package() {
+ validate_args list ${#} -eq 0
+
+ info "removing contents of \`@DBDIR@'"
+ rm -rf @DBDIR@/* 2>/dev/null
+ info "looking for alternatives in \`${PKG_DBDIR}'"
+ for d in ${PKG_DBDIR}/*; do
+ if [ -f ${d}/+ALTERNATIVES ]; then
+ action_register_package ${d}/+ALTERNATIVES
+ fi
+ done
+}
+
+# -------------------------------------------------------------------------
+
+# action_rebuild_wrapper
+#
+# Unavailable action.
+#
+action_rebuild_wrapper() {
+ err "the \`rebuild' action cannot be used in wrapper mode"
+}
+
+# -------------------------------------------------------------------------
+
+# action_register_package file
+#
+# Registers all wrapper/alternative pairs listed in the file. This is
+# supposed to be an +ALTERNATIVES file found inside a package in PKG_DBDIR.
+# Each line should follow the semantics expected by action_register_wrapper.
+#
+action_register_package() {
+ validate_args register ${#} -eq 1
+
+ set -- $(cat ${1} | tr ' ' '¬')
+ while [ ${#} -gt 0 ]; do
+ action_register_wrapper $(echo ${1} | tr '¬' ' ')
+ shift
+ done
+}
+
+# -------------------------------------------------------------------------
+
+# action_register_wrapper wrapper alternative arguments
+#
+# Registers a new alternative for the given wrapper in the database.
+# The wrapper and its manual page are created if they do not exist.
+#
+action_register_wrapper() {
+ validate_args register ${#} -ge 2
+ validate_wrapper ${1} no
+ validate_alternative ${2}
+
+ wbase=${1}; shift
+ alt=${1}; shift
+ args=${*}
+
+ wabs=${Prefix}/${wbase}
+ manpage=$(get_manpage ${wbase})
+ dbconf=${Db_Dir}/${wbase}
+ sysconf=${Conf_Dir}/${wbase}
+
+ if [ ! -f ${dbconf} ]; then
+ info "initializing database entry for \`${wbase}'"
+ mkdir_p ${dbconf%/*}
+ touch ${dbconf%/*}
+ fi
+ info "registering alternative \`${alt}' for wrapper \`${wbase}'"
+ if [ -n "${args}" ]; then
+ echo "${alt} ${args}" >>${dbconf}
+ else
+ echo "${alt}" >>${dbconf}
+ fi
+
+ if [ ! -f ${wabs} ]; then
+ info "creating wrapper \`${wbase}'"
+ mkdir_p ${wabs%/*}
+ sed -e "s|__SH__|@SH@|g" \
+ -e "s|__CONF_FILE__|${sysconf}|g" \
+ -e "s|__CREATOR__|${Prog_Name}|g" \
+ -e "s|__DB_FILE__|${dbconf}|g" \
+ -e "s|__ROOT_USER__|@ROOT_USER@|g" \
+ -e "s|__WRAPPER__|${wabs}|g" \
+ <${Data_Dir}/wrapper.sh >${wabs}
+ chmod +x ${wabs}
+ mkdir_p ${manpage%/*}
+ sed -e "s|__CONF_FILE__|${sysconf}|g" \
+ -e "s|__CREATOR__|${Prog_Name}|g" \
+ -e "s|__DB_FILE__|${dbconf}|g" \
+ -e "s|__PREFIX__|${Prefix}|g" \
+ -e "s|__SECTION__|$(get_manpage_sect ${wbase})|g" \
+ -e "s|__TITLE__|$(echo ${wbase##*/} | tr a-z A-Z)|g" \
+ -e "s|__WRAPPER__|${wbase##*/}|g" \
+ -e "s|__WRAPPERBASE__|${wbase}|g" \
+ <${Data_Dir}/wrapper.man >${manpage}
+ fi
+}
+
+# -------------------------------------------------------------------------
+
+# action_status_package package
+#
+# Shows the status of each wrapper associated to the given package.
+#
+action_status_package() {
+ validate_args status ${#} -eq 1
+ validate_package ${1}
+ pkg=${PKG_DBDIR}/${1}*/+ALTERNATIVES
+
+ set -- $(cat ${pkg} | tr ' ' '¬')
+ while [ ${#} -gt 0 ]; do
+ action_status_wrapper $(echo ${1} | cut -d '¬' -f 1)
+ shift
+ done
+}
+
+# -------------------------------------------------------------------------
+
+# action_status_wrapper wrapper
+#
+# Shows the current status for the given wrapper.
+#
+action_status_wrapper() {
+ validate_args status ${#} -eq 1
+ validate_wrapper ${1} yes
+
+ wbase=${1}
+ dbconf=${Db_Dir}/${wbase}
+ sysconf=${Conf_Dir}/${wbase}
+ userconf=~/.pkg_alternatives${Prefix}/${wbase}
+
+ [ $(id -un) = @ROOT_USER@ ] && userconf=
+ alts=$(cat ${userconf} ${sysconf} ${dbconf} 2>/dev/null | grep -v '^#' | \
+ tr ' ' '¬')
+
+ found=
+ for a in ${alts}; do
+ prog=$(echo ${a} | cut -d '¬' -f 1)
+ if [ -x ${prog} ]; then
+ found=$(echo ${a} | tr '¬' ' ')
+ break
+ fi
+ done
+
+ [ -n "${found}" ] ||
+ err "the wrapper \`${wbase}' exists but has no valid alternatives"
+
+ echo "\`${wbase}' points to \`${found}'"
+ for a in $(echo ${alts} | tr ' ' '\n' | sort | uniq); do
+ echo " candidate: $(echo ${a} | tr '¬' ' ')"
+ done
+}
+
+# -------------------------------------------------------------------------
+
+# action_unregister_package file
+#
+# Unregisters the given package (given by its +ALTERNATIVES file) and
+# removes all associated alternatives from their respective wrappers.
+#
+action_unregister_package() {
+ validate_args unregister ${#} -eq 1
+
+ set -- $(cat ${1} | tr ' ' '¬')
+ while [ ${#} -gt 0 ]; do
+ action_unregister_wrapper $(echo ${1} | tr '¬' ' ')
+ shift
+ done
+}
+
+# -------------------------------------------------------------------------
+
+# action_unregister_wrapper wrapper alternative arguments
+#
+# Unregisters an alternative for the given wrapper. The lookup is done
+# based on the alternative name and its arguments. I.e., there must be
+# an exact match. If no alternatives remain after the removal, the wrapper
+# and its manpage are removed.
+#
+action_unregister_wrapper() {
+ validate_args unregister ${#} -ge 2
+ validate_wrapper ${1} yes
+
+ wbase=${1}; shift
+ alt=${1}; shift
+ args=${*}
+
+ manpage=$(get_manpage ${wbase})
+ dbconf=${Db_Dir}/${wbase}
+
+ if [ -n "${args}" ]; then
+ grep -v "^${alt} ${args}$" <${dbconf} >${dbconf}.new
+ else
+ grep -v "^${alt}$" <${dbconf} >${dbconf}.new
+ fi
+ if cmp -s ${dbconf} ${dbconf}.new; then
+ rm -f ${dbconf}.new
+ err "unknown alternative \`${alt}' for wrapper \`${wbase}'"
+ fi
+ mv ${dbconf}.new ${dbconf}
+
+ if ! grep '^/' ${dbconf} >/dev/null; then
+ info "no more alternatives for \`${wbase}'; removing"
+ rm -f ${dbconf} ${Prefix}/${wbase} ${manpage}
+ rmdir_p ${dbconf%/*}
+ rmdir_p ${Prefix}/${wbase%/*}
+ rmdir_p ${manpage%/*}
+ fi
+}
+
+# -------------------------------------------------------------------------
+
+# err message
+#
+# Prints the given error message and exist with an error code.
+#
+err() {
+ echo "${Prog_Name}: ${*}" 1>&2
+ exit 1
+}
+
+# -------------------------------------------------------------------------
+
+# get_my_config
+#
+# Prints the configuration directory for the current user. If root, this
+# is the system-wide configuration directory. Otherwise, it is the user's
+# personal directory.
+#
+get_my_config() {
+ if [ $(id -un) = @ROOT_USER@ ]; then
+ echo ${Conf_Dir}
+ else
+ echo ~/.pkg_alternatives${Prefix}
+ fi
+}
+
+# -------------------------------------------------------------------------
+
+# get_manpage wrapper
+#
+# Prints the absolute path to the manual page associated to wrapper.
+# Does no error checking; validate_wrapper has to be called before this
+# function to ensure wrapper is a valid name.
+#
+get_manpage() {
+ sect=$(get_manpage_sect ${1})
+ echo ${Prefix}/man/man${sect}/${1##*/}.${sect}
+}
+
+# -------------------------------------------------------------------------
+
+# get_manpage_sect wrapper
+#
+# Prints the section number that will be used by the wrapper's manpage.
+# Does no error checking; validate_wrapper has to be called before this
+# function to ensure wrapper is a valid name.
+#
+get_manpage_sect() {
+ case ${1%/*} in
+ bin) echo 1 ;;
+ libexec|sbin) echo 8 ;;
+ esac
+}
+
+# -------------------------------------------------------------------------
+
+# info message
+#
+# Prints the given informative message if running in verbose mode.
+#
+info() {
+ [ ${Verbose} = yes ] && echo "${Prog_Name}: ${*}"
+}
+
+# -------------------------------------------------------------------------
+
+# mkdir_p directory
+#
+mkdir_p() {
+ if [ ! -d ${1} ]; then
+ cnt=2
+ max=$(($(echo ${1} | tr '/' ' ' | wc -w | awk '{ print $1; }') + 1))
+ while [ ${cnt} -le ${max} ]; do
+ mkdir $(echo ${1} | cut -d '/' -f -${cnt}) 2>/dev/null
+ cnt=$((${cnt} + 1))
+ done
+ [ -d ${1} ] || err "cannot create directory \`${1}'"
+ fi
+}
+
+# -------------------------------------------------------------------------
+
+# usage
+#
+# Shows an usage message and exits the program with an error condition.
+#
+usage() {
+ echo "Usage: ${Prog_Name} [-sw] [-p prefix] action [arg1 ... argN]" 1>&2
+ exit 1
+}
+
+# -------------------------------------------------------------------------
+
+# rmdir_p directory
+#
+rmdir_p() {
+ if [ -d ${1} ]; then
+ cnt=$(($(echo ${1} | tr '/' ' ' | wc -w | awk '{ print $1; }') + 1))
+ while [ ${cnt} -gt 1 ]; do
+ rmdir $(echo ${1} | cut -d '/' -f -${cnt}) 2>/dev/null
+ cnt=$((${cnt} - 1))
+ done
+ fi
+}
+
+# -------------------------------------------------------------------------
+
+# validate_alternative name
+#
+# Verifies that the given program name is valid to be used as an
+# alternative.
+#
+validate_alternative() {
+ echo ${1} | grep '^/' >/dev/null || \
+ err "the alternative \`${1}' is not an absolute file name"
+ [ -x ${1} ] || \
+ err "the alternative \`${1}' is not an executable"
+}
+
+# -------------------------------------------------------------------------
+
+# validate_args action argcount operator expected
+#
+# Verifies that the number of arguments passed to the specified action
+# are correct according to its semantics. Prints an error message if
+# incorrect.
+#
+validate_args() {
+ [ ${2} ${3} ${4} ] || \
+ err "incorrect number of arguments for the \`${1}' action"
+}
+
+# -------------------------------------------------------------------------
+
+# validate_package name
+#
+# Verifies that the specified package is valid.
+#
+validate_package() {
+ if [ ! -f ${PKG_DBDIR}/${1}/+ALTERNATIVES ]; then
+ cnt=$(cd ${PKG_DBDIR} && ls -d ${1}-[0-9]* 2>/dev/null | wc -l | \
+ awk '{ print $1; }')
+ if [ "${cnt}" -eq 0 ]; then
+ err "the package \`${1}' is not known"
+ elif [ "${cnt}" -gt 1 ]; then
+ err "multiple matches found for \`${1}'"
+ fi
+
+ # Reached this point, there is a single match for the package,
+ # but it may still be wrong (i.e., no alternatives for it).
+ [ ! -f ${PKG_DBDIR}/${1}*/+ALTERNATIVES ] && \
+ err "no alternatives defined for the \`${1}' package"
+ fi
+}
+
+# -------------------------------------------------------------------------
+
+# validate_wrapper name exists
+#
+# Verifies that the specified wrapper is valid, i.e., if it belongs to a
+# known directory. Also, if the exists parameter is yes, the function
+# ensures that the wrapper already exists (as well as its configuration
+# file in the database).
+#
+validate_wrapper() {
+ case ${1%/*} in
+ bin|libexec|sbin)
+ if [ ${2} = yes ]; then
+ [ -x ${Prefix}/${1} ] || \
+ err "the wrapper \`${1}' does not exist or is not" \
+ "executable"
+ [ -f ${Db_Dir}/${1} ] || \
+ err "the database configuration \`${Db_Dir}/${1}'" \
+ "does not exist"
+ fi
+ ;;
+ *)
+ err "wrapper name \`${1}' is invalid"
+ ;;
+ esac
+}
+
+# -------------------------------------------------------------------------
+
+# warn message
+#
+# Shows a warning message.
+#
+warn() {
+ echo "${Prog_Name}: ${*}" 1>&2
+}
+
+# -------------------------------------------------------------------------
+
+# main
+#
+# Main program code. Does argument parsing and executes the required
+# action.
+#
+main() {
+ args=$(getopt p:sw ${*})
+ [ ${?} -eq 0 ] || usage
+ set -- ${args}
+ what=package
+ while [ ${#} -gt 0 ]; do
+ case ${1} in
+ -p)
+ Prefix=$2; shift
+ Conf_Dir=@CONFDIR@${Prefix}
+ Db_Dir=@DBDIR@${Prefix}
+ ;;
+ -s)
+ Verbose=no
+ ;;
+ -w)
+ what=wrapper
+ ;;
+ --)
+ shift; break
+ ;;
+ esac
+ shift
+ done
+
+ if [ ${#} -eq 0 ]; then
+ usage
+ fi
+
+ action=${1}; shift
+ case ${action} in
+ auto|list|manual|rebuild|register|status|unregister)
+ action_${action}_${what} "${@}"
+ ;;
+ *)
+ err "unknown action \`${action}'"
+ ;;
+ esac
+
+ return 0
+}
+
+main "${@}"
+
+# vim: syntax=sh:expandtab:shiftwidth=4:softtabstop=4