#!/bin/bash # Copyright (C) 2004, 2005 Moreno 'baro' Baricevic # # # Redistribution and use of this script, with or without modification, is # permitted provided that the following conditions are met: # # 1. Redistributions of this script must retain the above copyright # notice, this list of conditions and the following disclaimer. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. # #=====================================# # Author: Moreno 'baro' Baricevic # # Contact: baro AT democritos DOT it # # Path: /bin # # File: pkginfo++ # # Date: 14 Oct 2004 # #-------------------------------------# # Last Modified: 17 Oct 2004 # #=====================================# # # Display info about installed packages. # # Inspired by Slackware pkgtool (some code has # been simply adapted and translated to bash). # # Dependencies: # bash, test/[, ls, sed, awk, printf, less # ADM_DIR=/var/log PKG_DIR=$ADM_DIR/packages test -d "$PKG_DIR" || { echo >&2 "!!! Directory '$PKG_DIR' does not exist !!!" ; exit -1 ; } #============================================================================= # faster basename # function mybasename() { basename="${1##*/}" test "$2x" != "x" && basename="${basename%$2}" echo $basename } # mybasename # #============================================================================= # enabling bash extended pattern matching # shopt -s extglob #============================================================================= # extract short package name # (bash translation of package_name() from pkgtool) # function package_name() { # Check for old style package name with one segment: if [ "${1%%-*}x" = "${1#*-}x" ] then echo ${1%%-*} else # has more than one dash delimited segment # Count number of segments: index=1 next="$1" last="${1##*-}" while [ "${next}x" != "${last}x" ] do next="${next#*-}" let ++index done # If we don't have four segments, return the old-style (or out of spec) package name: if [ "$index" = "2" -o "$index" = "3" ] then echo $1 else # we have four or more segments, so we'll consider this a new-style name: echo ${1%%-+([^-])-+([^-])-+([^-])} fi fi } # package_name # #============================================================================= # extract one-line package description # function package_desc() { name="$1" file="$2" descr="$(sed -n "/$name:/{s/\"//g;p;q;}" $PKG_DIR/$file 2>/dev/null)" echo ${descr#*:} } # package_desc # #============================================================================= # display package(s) info # function package_list() { declare -a args test $# -eq 0 && args=( `ls $PKG_DIR 2>/dev/null` ) || args=( $@ ) for pkg in ${args[@]} do pkg_file="$(mybasename $pkg)" pkg_name="$(package_name $pkg_file)" pkg_desc="$(package_desc $pkg_name $pkg_file)" # Let's have some backward compatibility with the interim beta (for now): test -z "$pkg_desc" && pkg_desc="$(package_desc $pkg_file $pkg_file)" # FIXME printf "%-20.20s %-34.34s %-${w}.${w}s\n" "$pkg_name" "$pkg_file" "$pkg_desc" done } # package_list # #============================================================================= # display package(s) that own given file pattern # function package_owner() { # remove beginning / (not present in "FILE LIST") awk -F: -v REGEX="${1#/*}" ' FNR==1 { I=0 } $1=="FILE LIST" { I=1 } I==1 && match( $0 , REGEX ) { pkg = gensub( ".*/" , "" , "g" , FILENAME ); printf( "%-40.30s %s\n" , pkg ":" , $0 ); } ' $PKG_DIR/* } # package_list # #============================================================================= # print short Usage # function Usage() { test ! -z "$*" && echo >&2 "Usage error: $*" myself="$(mybasename $0)" cat <<_USAGE_ >&2 Usage: $myself [ -l | -p PACKAGE | -f FILE | -h ] -l list installed packages -p PACKAGE view PACKAGE info -f FILE query package owning FILE -h display a short help message _USAGE_ exit 1 } # Usage # #============================================================================= # print the usage and a short help message # function Help() { myself="$(mybasename $0)" cat <<_HELP_ Usage: $myself [ -l | -p PACKAGE | -f FILE | -h ] -l list installed packages -p PACKAGE view PACKAGE info -f FILE query package owning FILE -h display this message NOTES: - when PACKAGE match more packages, a short list is displayed: $myself -p p # list of "p*" packages $myself -p pkgtools # "pkgtools" info $myself -l | grep "tool" # "*tool*" list (!match description too!) $myself -l | grep "^[^ ]*tool" # "*tool*" list (match only package name) - the '$' character match the end of line $myself -f /usr/bin/time # package(s) that owns /usr/bin/time* $myself -f /usr/bin/time$ # package(s) that owns /usr/bin/time $myself -f \`which ls\` # package(s) that owns /bin/ls* $myself -f \`which ls\`$ # package(s) that owns /bin/ls - since unused in packages "FILE LIST" section, the first slash is always removed. Use '//' to force a starting slash (useful for partial paths). $myself -f time # match any file name that contains "time" $myself -f time$ # match any file name that ends with "time" $myself -f //time # match any file name that begins with "time" $myself -f //time$ # match any file named exactly "time" _HELP_ exit 0 } # Help # #============================================================================= # check package name syntax (anything but special chars) # function invalid_pkg() { test "$1x" = "x" -o "${1//[a-zA-Z]*([a-zA-Z0-9_\+\-\.,:])/}x" != "x" } # invalid_pkg # #============================================================================= # check file name syntax (slash, backslash and trailing dollar are allowed) # (note: '\\\\' is '\' escaped twice) # function invalid_file() { f="${1%\$}" # remove trailing $ if any (only for the next check) test "${f}x" = "x" -o "${f//+([a-zA-Z0-9_\+\-\.,:\/\\\\])/}x" != "x" } # invalid_file # #----------------------------------------------------------------------------# # check window size # eval `resize 2>/dev/null` # X? test "$COLUMNS" = "" -o "$COLUMNS" -lt 80 && COLUMNS=80 w=$(($COLUMNS-56)) # '56' comes from printf fmt (package_list()) test $# -eq 0 && Usage list=0 file="" package="" while getopts lp:f:hHuU opt do case $opt in l) test $# -ne 1 && Usage "option -$opt and additional argument(s) given" list=1 ;; f) test $# -ne 2 && Usage "option -$opt require one argument" test ${#OPTARG} -lt 2 && Usage "filename too short" file="${OPTARG//\./\\\\.}" # escape string ".sh" -> regexp "\.sh" invalid_file "$file" && Usage "option -$opt: invalid argument" ;; p) test $# -ne 2 && Usage "option -$opt require one argument" package="$OPTARG" invalid_pkg "$package" && Usage "option -$opt: invalid argument" ;; h|H|u|U) Help ;; *|\?) # getopts already prints an error message exit 1 ;; esac done test "${package}${file}${list}" = "0" && Usage if [ $list -eq 1 ] then # list all the installed packages # echo >&2 "=========================== Scanning system for installed packages ===========================" package_list exit 0 elif [ -n "$file" ] then package_owner "$file" exit 0 elif [ -n "$package" ] then # look for candidates # declare -a candidates candidates=( `ls $PKG_DIR/"$package"* 2>/dev/null` ) num=${#candidates[*]} if [ $num -eq 0 ] then # mmh, no candidates found echo >&2 "Package '$package': no match found in $PKG_DIR/" test ${#package} -gt 1 && echo >&2 "Hint: try '${package%?}'" elif [ $num -gt 1 ] then # we have more packages that match given argument echo >&2 "$num possible candidates:" package_list ${candidates[@]} else # we have just one match trap "exec 2>/dev/null ; exit 0 ;" SIGPIPE less ${candidates[*]} fi exit 0 fi echo >&2 "mmh, seems like a bug!" exit -1 # WTF??? #----------------------------------------------------------------------------# #EOF