diff options
Diffstat (limited to 'sbosearch')
-rwxr-xr-x | sbosearch | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/sbosearch b/sbosearch new file mode 100755 index 0000000..e4b48d9 --- /dev/null +++ b/sbosearch @@ -0,0 +1,516 @@ +#!/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 <<EOF +$SELF: search local slackbuilds.org repository + +Usage: $SELF [-a] [-v] [-I] [-R] [-B] [-H] [-r required] [-e email] + [-m maintainer] [-k keyword] [-c category] [build] [...] + $SELF --all + $SELF --help + +Find all SlackBuilds in the local repository matching the given +criteria. If multiple search options are given, they are ANDed together +by default. + +Options: + +Options may not be bundled (use "-a -v", not "-av"), and the spacing +shown is required (use "-r foo", not "-rfoo"). + +Search options: +[build] Match builds by name. +-r required Matches builds with <required> 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>. 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 \<bob@" will match bob@example.com, +but not silentbob@example.com). + +Build names are anchored on the left, so searching for 'z' will find all +the build names that begin with 'z'. If you want to find all the build +names containing a 'z', use '.*z' instead. You can search for all build +names ending with 'z' by using '.*z\>' (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 <<EOF 1>&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/<version>/ 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 |