#!/bin/bash # This really is a bash script, uses bashisms, don't change the shebang. DFLTBROWSER=xdg-open SLACKVER=${SLACKVER:-$( cut -d' ' -f2 /etc/slackware-version )} SLACKVER=${SLACKVER:-14.1} SBOROOT=${SBOROOT:-/var/lib/sbopkg/SBo/$SLACKVER} [ ! -d "$SBOROOT" ] && SBOROOT=. SELF=$( echo $0 | sed 's,.*/,,' ) usage() { cat < in the REQUIRES= field (only exists in repo versions 14.0 and up). -e email Match email address. -m maintainer Match MAINTAINER= (real name, not email). -h homepage Match HOMEPAGE. -d readmetxt Match text in documentation (the README). -c category Match builds in . Category names may be abbreviated, e.g. "sys" for system, "ga" for games. -k keyword Keyword search (TAGS.txt, includes build names, only exists in repo versions 14.1 and up). Search modifier options: --all List all builds. Any search options will be ignored. -a AND all the search options together (default is OR). -v Invert the search (find builds not matching the criteria). -i Search for installed packages only. -u Search for uninstalled packages only. -x Exact match (for build names only, not -r -e -m -h -d) Output options: -S Print short names (no category, "zdoom" not "games/zdoom"). -P Print full paths to tarballs. -X Extract tarballs into current dir. -I cat .info files for all builds found. -R cat README files for all builds found. -B Open SBo repo page in browser, for each build found. -H Open HOMEPAGE in browser, for each build found. -C Check installed status (print "installed" or "NOT installed" for each build found). The local repo must be in the current directory, or in the directory given by the SBOROOT environment variable, which defaults to /var/lib/sbopkg/SBo/\$SLACKVER. If SLACKVER is not set in the environment, its value will be extracted from /etc/slackware-version (or set to 14.1, if this file doesn't exist). All search arguments are treated as case-insensitive regular expressions. Required, email, maintainer, and homepage arguments are unanchored, and can't be anchored with ^ or $. However, you can use \< and \> to anchor on word boundaries (e.g. "-e \' (actually this will also find things like compiz-bcop, which have a z at a word boundary). Search options can be given more than once, e.g. "-r foo -r bar" means find builds that require either foo or bar (or, with -a, builds that require both foo and bar). The -B and -H options use the environment variable BROWSER to set which browser to use. If BROWSER is not set, "$DFLTBROWSER" is used. Be careful with these: trying to open several hundred (or several thousand) browser instances will likely eat your machine for breakfast, especially if firefox is the default browser. Best to run the search without -B or -H first, to see how many matches you get. EOF } die() { if [ -n "$TMPDIR" -a -d "$TMPDIR" ]; then rm -rf $TMPDIR fi echo "$SELF: $@ (try '$SELF --help')" 1>&2 exit 1 } info() { echo "$SELF: info: $@" 1>&2 } warn() { echo "$SELF: warning: $@" 1>&2 } set_input() { if [ "$mode" = "or" ]; then INPUT=$ALL else INPUT=$RESULTS fi : > $OUTPUT } set_output() { if [ "$mode" = "or" ]; then cat $OUTPUT >> $RESULTS else mv $OUTPUT $RESULTS fi } info_search() { set_input egrep -i -l "^$1=.*$2" $( cat $INPUT ) > $OUTPUT set_output } # keyword search is complicated by the fact that TAGS.txt doesn't # store the category. Also, we don't differentiate between the keywords # and the build name at the start of the line. keyword_search() { local pkg if [ ! -e $SBOROOT/TAGS.txt ]; then cat <&2 $SELF: $SBOROOT/TAGS.txt not found. Note that this file only exists for Slackware versions 14.1 and up. We seem to be searching version $SLACKVER. EOF die "can't do keyword search" fi set_input egrep -i "$1" $SBOROOT/TAGS.txt | while read line; do pkg=$( echo "$line" | cut -d: -f1 ) egrep "/$pkg\.info\$" $INPUT >> $OUTPUT done set_output } category_search() { local categ set_input categ=$( cut -d/ -f1 $ALL | sort -u | egrep -i "$1" | head -1 ) [ -z "$categ" ] && die "fatal: no such category '$1'" egrep -i "^$categ/" $INPUT > $OUTPUT set_output } build_search() { set_input if [ "$exact" == "1" ]; then fgrep "$1/$1.info" $INPUT > $OUTPUT else egrep -i "/$1[^/]*\.info\$" $INPUT > $OUTPUT fi set_output } readme_search() { local info readme set_input for info in $( cat $INPUT ); do readme=$( echo $info | sed 's,/[^/]*$,/README,' ) egrep -q -i "$1" $readme && echo $info >> $OUTPUT done set_output } is_installed() { [ ! -e /var/log/packages/ ] && die "no /var/log/packages, are you sure this is Slackware?" local pkg="$( echo $1 | cut -d/ -f2 )" [ ! -e $TMPDIR/installed_pkgs ] && \ ls /var/log/packages/ | rev | cut -d- -f4- | rev > $TMPDIR/installed_pkgs fgrep -q -x $pkg $TMPDIR/installed_pkgs return $? } # works, but slow: ## is_installed() { ## local pkg candidate olddir found=1 ## ## found=1 ## pkg="$( echo $1 | cut -d/ -f2 )" ## olddir="$( pwd )" ## cd /var/log/packages || die "no /var/log/packages, are you sure this is Slackware?" ## ## for candidate in "$( ls $pkg* 2>/dev/null )"; do ## if [ "$pkg" = "$( echo $candidate | rev | cut -d- -f4- | rev )" ]; then ## found=0 ## fi ## done ## ## cd $olddir ## return $found ## } # also slow ## is_installed() { ## local pkg ## ## if [ ! -e $TMPDIR/installed_pkgs ]; then ## ls /var/log/packages/ > $TMPDIR/installed_pkgs || \ ## die "no /var/log/packages, are you sure this is Slackware?" ## fi ## ## pkg="$( echo $1 | cut -d/ -f2 )" ## egrep -q "^$pkg"'-[^-]+-[^-]+-[^-]+$' $TMPDIR/installed_pkgs ## return $? ## } ## ## installed_search_backend() { ## local pkg ## set_input ## for pkg in $( cat $INPUT ); do ## eval is_installed $pkg $2 echo $pkg >> $OUTPUT ## done ## set_output ## } ## ## installed_search() { ## installed_search_backend "$1" "&&" ## } ## ## uninstalled_search() { ## installed_search_backend "$1" "||" ## } # almost works, fast, might try to fix someday ## installed_search() { ## set_input ## ls /var/log/packages/ | rev | cut -d- -f4- | rev > $TMPDIR/installed_pkgs || \ ## die "no /var/log/packages, are you sure this is Slackware?" ## sort -t / -k 2 $INPUT > $INPUT.tmp ## join -t / -1 2 -o 1.1,1.2 $INPUT.tmp $TMPDIR/installed_pkgs > $OUTPUT ## exit 0 ## set_output ## } # works, slow, but not as slow as the 1st try ## installed_search() { ## local -A packages ## local pkg shortpkg ## ## [ ! -e /var/log/packages/ ] && die "no /var/log/packages, are you sure this is Slackware?" ## ## set_input ## echo "got here 1" ## for pkg in $( cat $INPUT ); do ## echo -n "." 1>&2 ## shortpkg=$( expr "$pkg" : '.*/\([^/]*\)/' ) ## #shortpkg=$( echo $pkg | cut -d/ -f2 ) ## packages[$shortpkg]=$pkg ## done ## echo "got here 2" ## ls /var/log/packages/ | rev | cut -d- -f4- | rev | while read pkg; do ## [ -n "${packages[$pkg]}" ] && echo ${packages[$pkg]} >> $OUTPUT ## done ## echo "got here 3" ## set_output ## } # lightning fast and works correctly, compared to the commented-out # attempts above. installed_search_backend() { local pkg grepopt="$1" [ ! -e /var/log/packages/ ] && die "no /var/log/packages, are you sure this is Slackware?" set_input cut -d/ -f2 $INPUT > $INPUT.shortnames # $INPUT.shortnames.found is foo => /foo.info # fgrep -x means "match entire line only", used to avoid matching e.g. # zathura when looking for zathura-cb. It's *much* faster than using # egrep with ^ and $. ls /var/log/packages/ | rev | cut -d- -f4- | rev | \ fgrep -x -f $INPUT.shortnames | \ sed 's,.*,/&.info,' \ > $INPUT.shortnames.found fgrep $grepopt -f $INPUT.shortnames.found $INPUT > $OUTPUT set_output } installed_search() { installed_search_backend "" } uninstalled_search() { installed_search_backend "-v" } invert_results() { fgrep -v -f $RESULTS $ALL > $OUTPUT mv $OUTPUT $RESULTS } open_in_browser() { BROWSER="${BROWSER:-$DFLTBROWSER}" info "opening URL '$1' with browser '$BROWSER'" $BROWSER "$1" } print_results() { if [ "$simpleoutput" = "1" ]; then cat $OUTPUT return fi # N.B. don't use 'cat $OUTPUT | while read line' here, console browsers # don't like having their stdin redirected (especially not links) for line in $( cat $OUTPUT ) ; do # -X option if [ "$extract" = "1" ]; then tar -C $OLD_PWD -xvf $SBOROOT/$line.tar.gz fi # -P option if [ "$printpaths" = "1" ]; then echo -n $SBOROOT/$line.tar.gz else # -S option if [ "$shortoutput" = "1" ]; then echo -n $( echo $line | cut -d/ -f2 ) else echo -n $line fi fi # -C option if [ "$checkinstalled" = "1" ]; then is_installed $line && echo ": installed" || echo ": NOT installed" else echo fi # -R option if [ -n "$readmefiles" ]; then echo '==>' $line/README cat $line/README echo fi # -I option if [ -n "$infofiles" ]; then echo '==>' $line/*.info cat $line/*.info echo fi # -H option if [ -n "$hpbrowser" ]; then ( source $line/*.info ; open_in_browser "$HOMEPAGE" ) fi # -B option if [ -n "$browser" ]; then open_in_browser "http://slackbuilds.org/repository/$SLACKVER/$line/" fi done } # main() mode="or" simpleoutput=1 if [ "$*" == "" ]; then set -- --help fi while [ -n "$1" ]; do arg="$1" shift case "$arg" in "--help") usage ; exit 0 ;; "--all") showall=1 ;; "-a") mode="and" ;; "-r") required="$required $1" ; shift ;; "-e") email="$email $1" ; shift ;; "-m") maintainer="$maintainer $1" ; shift ;; "-h") homepage="$homepage $1" ; shift ;; "-k") keyword="$keyword $1" ; shift ;; "-c") category="$category $1" ; shift ;; "-d") readme="$readme $1" ; shift ;; "-v") invert=1 ;; "-R") readmefiles=1 ; simpleoutput=0 ;; "-I") infofiles=1 ; simpleoutput=0 ;; "-B") browser=1 ; simpleoutput=0 ;; "-H") hpbrowser=1 ; simpleoutput=0 ;; "-S") shortoutput=1 ; simpleoutput=0 ;; "-P") printpaths=1 ; simpleoutput=0 ;; "-X") extract=1 ; simpleoutput=0 ;; "-C") checkinstalled=1 ; simpleoutput=0 ;; "-i") installedonly=1 ;; "-u") uninstalledonly=1 ;; "-x") exact=1 ;; -*) die "unknown option '$arg'" ;; *) build="$build $arg" ;; esac done if [ "$installedonly" = "1" -a "$uninstalledonly" = "1" ]; then die "-i and -u don't make sense to use together" fi TMPDIR=${TMP:-/tmp}/sbosearch.$$.$RANDOM rm -rf $TMPDIR mkdir -p $TMPDIR ALL=$TMPDIR/allinfos OUTPUT=$TMPDIR/output RESULTS=$TMPDIR/results OLD_PWD=$( pwd ) cd $SBOROOT || die "set SBOROOT or cd to the SBo// directory." # Unfortunately ChangeLog.txt is the only file that's present in the SBo # tree for every Slack version. if [ ! -e ChangeLog.txt ]; then warn "can't find ChangeLog.txt in $SBOROOT, are you sure this is a valid repo?" fi /bin/ls */*/*.info > $ALL [ "$showall" = "1" ] && mode="and" # Init results. # in 'and' mode, each search is done in the results of the previous # search. The first search starts with the full list, so the results # of the 'previous' (nonexistent) search must be the full list. # in 'or' mode, every search is done against the full list, and the # results are built up by appending (so the result starts out empty). if [ "$mode" = "and" ]; then cp $ALL $RESULTS else touch $RESULTS fi # Do the searches. # Thought about parametrizing these, so there could be a loop such as # for i in REQUIRES EMAIL MAINTAINER HOMEPAGE; do info_search $i; done # ...but it would lead to fugly bash code that I wouldn't care to debug # a year from now. if [ "$showall" != "1" ]; then [ -n "$required" ] && for term in $required; do info_search REQUIRES $term done [ -n "$email" ] && for term in $email; do info_search EMAIL $term done [ -n "$maintainer" ] && for term in $maintainer; do info_search MAINTAINER $term done [ -n "$homepage" ] && for term in $homepage; do info_search HOMEPAGE $term done [ -n "$keyword" ] && for term in $keyword; do keyword_search $term done [ -n "$category" ] && for term in $category; do category_search $term done [ -n "$build" ] && for term in $build; do build_search $term done [ -n "$readme" ] && for term in $readme; do readme_search $term done [ -n "$installedonly" ] && installed_search [ -n "$uninstalledonly" ] && uninstalled_search [ -n "$invert" ] && invert_results fi # done with all the searches, pretty up the output. if [ "$shortoutput" = "1" ]; then sortopts="-t / -k 2" fi sort -u $sortopts $RESULTS | cut -d/ -f1-2 > $OUTPUT print_results rm -rf $TMPDIR exit 0