#!/bin/bash
#
# listet alle Bibliotheken ab einem bestimmten Verzeichnis oder
# mehreren (default: *.a* und *.so* in /usr/lib und /lib), die eines
# oder mehrere der als Argument übergebenen Symbole exportieren.


set -o noglob   # Expansion von wildcards abschalten

suchpfade="/usr/lib /lib"
libmuster="*.a* *.so*"

# zu suchende Typen (siehe man nm); jede der Variablen hat ihren Namen als Wert,
# wenn danach gesucht werden soll, oder ist leer, wenn nicht:
A='A'
B='B'
C='C'
D='D'
G='G'
R='R'
S='S'
T='T'
V='V'
W='W'

# entsprechend nicht zu suchende Typen:
notA=''
notB=''
notC=''
notD=''
notG=''
notR=''
notS=''
notT=''
notV=''
notW=''

# gibt etwas Hilfe aus:
help()
{
        echo "Aufruf:"
        echo "  $0 [option1 [option2...]] [symbol1 [symbol2 ...]]"
        echo ""
        echo "Damit kann in allen Laufzeitbibliotheken oder Objektdateien"
        echo "unterhalb eines oder mehrerer Verzeichnisse nach darin"
        echo "definierten Symbolen gesucht werden "
        echo "('in welcher Bibliothek ist phtread_create definiert?')"
        echo ""
        echo "Optionen:"
        echo "-h, --help  Ausgabe einer kurzen Info (nämlich dieser)"
        echo "-p -        alle bisher definierten Pfade der Suchliste löschen"
        echo "-p pfad     pfad zur Suchliste hinzufügen"
        echo "-l -        Liste der Muster zu suchender Bibliotheken löschen"
        echo "-l muster   muster zur Liste der Muster hinzufügen"
        echo "+A          es soll nach absoluten Symbolen gesucht werden"
        echo "+B          es soll nach BSS-Symbolen gesucht werden"
        echo "+C          es soll nach Common-Symbolen gesucht werden"
        echo "+D          es soll nach DATA-Symbolen (initialized) gesucht werden"
        echo "+G          es soll nach DATA-Symbolen (initialized,small) gesucht werden"
        echo "+R          es soll nach read-only-Symbolen gesucht werden"
        echo "+S          es soll nach uninitialized small data-Symbolen gesucht werden"
        echo "+T          es soll nach Text-Symbolen (Code) gesucht werden"
        echo "+V          es soll nach weak symbols V gesucht werden"
        echo "+W          es soll nach weak symbols W (not tagged) gesucht werden"
        echo "-A          es soll nicht nach absoluten Symbolen gesucht werden"
        echo "-B          es soll nicht nach BSS-Symbolen gesucht werden"
        echo "-C          es soll nicht nach Common-Symbolen gesucht werden"
        echo "-D          es soll nicht nach DATA-Symbolen (initialized) gesucht werden"
        echo "-G          es soll nicht nach DATA-Symbolen (initialized,small) gesucht werden"
        echo "-R          es soll nicht nach read-only-Symbolen gesucht werden"
        echo "-S          es soll nicht nach uninitialized small data-Symbolen gesucht werden"
        echo "-T          es soll nicht nach Text-Symbolen (Code) gesucht werden"
        echo "-V          es soll nicht nach weak symbols V gesucht werden"
        echo "-W          es soll nicht nach weak symbols W (not tagged) gesucht werden"
        echo "-none       schaltet die Suche nach allen Symbolen aus (-A -B ...)"
        echo "-all        schaltet die Suche nach allen Symbolen an (+A +B ...)"
        echo ""
        echo "Jedes der angegebenen Symbole wird als regulärer Ausdruck"
        echo "('extended regular expression' wie bei egrep) behandelt:"
        echo "Ein Punkt ('.') steht für ein beliebiges Zeichen; ein Stern"
        echo "('*') für 0 oder beliebig viele Vorkommen des vorhergehenden"
        echo "Symbols; '^' für den Anfang und '$' für das Ende etc.;"
        echo "siehe man 8 regex."
        echo ""
        echo "Gibt man nur eine Zeichenfolge an, darf sie überall im tatsächlichen"
        echo "Symbolnamen enthalten sein (Suche nach 'sin' findet also"
        echo "auch 'asin', 'sinh' und 'asinh')."
        echo "Will man nur Symbole, die so wie angegeben beginnen, muß man"
        echo "ein Anfangszeichen '^' davor setzen; die Suche nach '^sin' findet"
        echo "Namen wie 'sin', 'sinh' und so weiter, aber nicht 'asin' oder"
        echo "'asinh'."
        echo ""
        echo "Will man nur Symbole, die so wie angegeben enden, muß man"
        echo "ein Endezeichen '$' dahinter setzen; die Suche nach 'sin$' findet"
        echo "Namen wie 'sin', 'asin' und so weiter, aber nicht 'sinh' oder"
        echo "'asinh'."
        echo ""
        echo "'^' davor und '$' dahinter finden nur Namen, die wie angegeben"
        echo "beginnen und enden."
        echo ""
        echo "Beispiele:"
        echo " Ausgabe der Voreinstellungen (zu durchsuchende Verzeichnisse,"
        echo "  Bibliotheksnamen) und Kurzanleitung:"
        echo "     $0 -h"
        echo ""
        echo "Suche nach der Definition von pthread_create:"
        echo "     $0 pthread_create"
        echo ""
        echo "Suche nach der Definition von pthread_create nur in /lib:"
        echo "     $0 -p - -p /lib pthread_create"
        echo ""
        echo "Suche nach der Definition von pthread_create zusätzlich in /etc:"
        echo "     $0 -p /etc pthread_create"
        echo ""
        echo "Suche nach der Definition von pthread_create nur in *.so*-Dateien:"
        echo "     $0 -l - -l '*.so*' pthread_create"
        echo ""
        echo "Suche nach Symbolen, die mit 'pthread' beginnen, und dann beliebig enden:"
        echo "     $0 '^pthread_create'"
        echo ""
        echo "Suche nach Symbolen, die auf '_create'  enden:"
        echo "     $0 '_create$'"
        echo ""
        echo "Suche nur nach Text-Symbolen (Code), die genau 'sin' heißen:"
        echo "     $0 -none +T '^sin$'"
        echo ""

        echo "Suchpfade:"
        for i in $suchpfade
        do
            echo " $i"
        done
        echo ""

        echo "zu durchsuchende Bibliotheken:"
        for i in $libmuster
        do
            echo " $i"
        done
        echo ""

        echo "Suche nach den Symbolen: "\
             $A$B$C$D$G$R$S$T$V$W
        echo "übergehen der Symbole  : "\
             $notA$notB$notC$notD$notG$notR$notS$notT$notV$notW
        echo
}

# sucht nach einem übergebenen Namen ($1) in allen in Frage kommenden Dateien:
suchelibs()
{
    echo "Suche nach Symbol "$1

    # aus den lib-Dateimustern zu jedem Suchpfad ein find-Kommando basteln,
    # z.B.:
    #     find /usr/lib -name "lib*.so" -o -name "lib*.a"
    #     find /lib -name "lib*.so" -o -name "lib*.a"

    # Dazu in $namelist alle Namen mit -o getrennt aneinanderhängen:
    namelist=""
    index=0
    for i in $libmuster
    do
        # nicht beim ersten, aber sonst immer " -o " einfügen:
        if [ $((index++)) -ne 0 ]
        then
            namelist="$namelist -o "
        fi
        # in jedem Fall "-name muster" einsetzen:
        namelist="$namelist -name $i"
    done

    for i in $suchpfade
    do
        # Schleife über alle betreffenden Dateinamen darin:
        for lib in `find $i $namelist 2>/dev/null`
        do


            # überhaupt ein passendes Symbol enthalten?

            # $1 ist der vom Aufrufer angegebene RE (nach welchem
            # Symbol er suchen will).
            # Den kann man hier nicht direkt als RE verwenden, weil
            # die jeweilige nm-Ausgabe nicht nur aus dem Symbol besteht,
            # sondern aus einer hexadezimalen Adresse, einem Kennbuchstaben
            # für die Art des Symbols ('T'=globales Symbol im Textsegment etc.),
            # einem Leerzeichen und dann dem gesuchten Namen;
            # Beispiel:
            #    00008f80 T pthread_create
            #
            # Wenn der Benutzer am Anfang seines RE ein '^' stehen hat, dann
            # soll der Name so beginnen; also muß man an einen RE für
            # die Adresse und den Kennbuchstaben das Benutzer-RE ohne '^'
            # anhängen.
            #
            # Hat der Benutzer dagegen kein '^' am Anfang seines RE,
            # dann darf der Symbolname irgendwie beginnen und man muß
            # für das gesamte RE ein RE für Adresse und Kennbuchstaben
            # nehmen, dann '.*' für einen beliebigen Anfang des
            # Symbols, dann das Benutzer-RE.
            #
            if echo $1 | grep '^\^.*' >/dev/null
            then
                # Benutzer-RE beginnt mit '^', also kein '.*', und
                # '^' abgeschnitten:
                muster='[0-9a-fA-F]*[ ]*['$A$B$C$D$G$R$S$T$V$W'] '`echo "$1"|sed -e 's/^\^//'`
            else
                # Benutzer-RE beginnt nicht mit '^', also '.*', und
                # $1 komplett verwendet:
                muster='[0-9a-fA-F]*[ ]*['$A$B$C$D$G$R$S$T$V$W'] .*'"$remod""$1"
            fi

            if ( nm -C $lib 2>/dev/null | grep "$muster" ) >/dev/null
            then
                # mindestens einmal enthalten, also Bibliothek ausgeben:
                echo
                echo $1 gefunden in $lib":"
                # und dann nochmal die einzelnen Zeilen
                # (Symboladresse am Zeilenanfang entfernt,
                # 'T' etc. durch 'Textsegment' etc. ersetzt):
                nm -C $lib 2>/dev/null                                  \
                | grep "$muster"                                        \
                | sed -e 's/^[0-9a-fA-F]* //'                           \
                | sed -e 's/^A/Absolut:                      /'         \
                | sed -e 's/^B/BSS (uninitialized):          /'         \
                | sed -e 's/^C/COMMON:                       /'         \
                | sed -e 's/^D/Data (initialized):           /'         \
                | sed -e 's/^G/Data (initialized,small):     /'         \
                | sed -e 's/^R/Read only:                    /'         \
                | sed -e 's/^S/Data (uninitialized,small):   /'         \
                | sed -e 's/^T/Textsegment (Code):           /'         \
                | sed -e 's/^V/Weak Symbol:                  /'         \
                | sed -e 's/^W/Weak Symbol:                  /'
                echo
            fi
        done
    done
}


# Argumente abklappern:

while [ "$*" ]
do
    case $1 in

    '-h'|'--help') # Hilfe ausgeben, dann beenden:
        help
        true &&  exit
        ;;

    '-p') # Liste der Suchpfade löschen oder ergänzen:
        shift
        if [ "$1" = '-' ]
        then
            # Liste löschen:
            suchpfade=""
        else
            # Liste ergänzen:
            suchpfade="$suchpfade $1"
        fi
        ;;

    '-l') # Liste der Endungen löschen oder ergänzen:
        shift
        if [ "$1" = '-' ]
        then
            # Liste löschen:
            libmuster=""
        else
            # Liste ergänzen:
            libmuster="$libmuster $1"
        fi
        ;;
    '+A')   # es soll nach A-Symbolen gesucht werden
        A='A'
        notA=''
        ;;
    '-A') # es soll nicht nach A-Symbolen gesucht werden
        A=""
        notA='A'
        ;;
    '+B')   # es soll nach B-Symbolen gesucht werden
        B='B'
        notB=''
        ;;
    '-B') # es soll nicht nach B-Symbolen gesucht werden
        B=""
        notB='B'
        ;;
    '+C')   # es soll nach C-Symbolen gesucht werden
        C='C'
        notC=''
        ;;
    '-C') # es soll nicht nach C-Symbolen gesucht werden
        C=""
        notC='C'
        ;;
    '+D')   # es soll nach D-Symbolen gesucht werden
        D='D'
        notD=''
        ;;
    '-D') # es soll nicht nach D-Symbolen gesucht werden
        D=""
        notD='D'
        ;;
    '+G')   # es soll nach G-Symbolen gesucht werden
        G='G'
        notG=''
        ;;
    '-G') # es soll nicht nach G-Symbolen gesucht werden
        G=""
        notG='G'
        ;;
    '+R')   # es soll nach R-Symbolen gesucht werden
        R='R'
        notR=''
        ;;
    '-R') # es soll nicht nach R-Symbolen gesucht werden
        R=""
        notR='R'
        ;;
    '+S')   # es soll nach S-Symbolen gesucht werden
        S='S'
        notS=''
        ;;
    '-S') # es soll nicht nach S-Symbolen gesucht werden
        S=""
        notS='S'
        ;;
    '+T')   # es soll nach T-Symbolen gesucht werden
        T='T'
        notT=''
        ;;
    '-T') # es soll nicht nach T-Symbolen gesucht werden
        T=""
        notT='T'
        ;;
    '+V')   # es soll nach V-Symbolen gesucht werden
        V='V'
        notV=''
        ;;
    '-V') # es soll nicht nach V-Symbolen gesucht werden
        V=""
        notV='V'
        ;;
    '+W')   # es soll nach W-Symbolen gesucht werden
        W='W'
        notW=''
        ;;
    '-W') # es soll nicht nach W-Symbolen gesucht werden
        W=""
        notW='W'
        ;;
     '-none') # Suche nach allen Smybolen abschalten
        A=""
        B=""
        C=""
        D=""
        G=""
        R=""
        S=""
        T=""
        V=""
        W=""
        notA='A'
        notB='B'
        notC='C'
        notD='D'
        notG='G'
        notR='R'
        notS='S'
        notT='T'
        notV='V'
        notW='W'
        ;;
     '-all') # Suche nach allen Smybolen einschalten
        A='A'
        B='B'
        C='C'
        D='D'
        G='G'
        R='R'
        S='S'
        T='T'
        V='V'
        W='W'
        notA=""
        notB=""
        notC=""
        notD=""
        notG=""
        notR=""
        notS=""
        notT=""
        notV=""
        notW=""
        ;;
    *) # keine Option, also normales Argument
        if test "$A$B$C$D$G$R$S$T$V$W" != ""
        then
            suchelibs $1
        else
            echo "(keine zu suchenden Symboltypen, wie soll ich '$1' finden?)"
        fi
        ;;
    esac
    shift
done

true && exit
