#! /bin/bash
#
# janv10-headerc.sh : détermine les fichiers .h à inclure - correction
#
# Edouard.Thiel@lif.univ-mrs.fr - 17/12/2009
#
# This program is free software under the terms of the 
# GNU Lesser General Public License (LGPL) version 2.1.


# Configuration du script :
dicoH="dicoh.txt"
fichH="tmp2.$$"
action="6"     # Par défaut on fait tout
ccopt="-Wall"  # Options de compilation minimales


# Dictionnaire dicoH :
# - chaque ligne est de la forme: fonction pointh pointh ...
# - on suppose que chaque fct apparait une seule fois.
# Exemple: recopier les lignes suivantes dans un fichier "dicoh.txt"
#          et supprimer le # devant chaque ligne.
#   printf     stdio.h
#   scanf      stdio.h
#   fopen      stdio.h
#   fclose     stdio.h
#   exit       stdlib.h
#   atexit     stdlib.h
#   fork       unistd.h
#   getpid     sys/types.h unistd.h
#   sigaction  signal.h
#   wait       sys/types.h sys/wait.h
#   open       sys/types.h sys/stat.h fcntl.h


# Usage: AffiUsage
AffiUsage ()
{
    cat << EOT
USAGE: 
  $0 -help      : affiche cette aide
  $0 -list      : affiche la liste des fonctions connues

  $0 [-mots|-rec|-sug|-ins|-comp|-min] fichier.c [cc-opts] :
    -mots   : affiche la liste des mots du fichier.c
    -rec    : affiche la liste des fonctions reconnues dans fichier.c
    -sug    : affiche la liste des .h suggérés
    -ins    : insère les .h suggérés dans fichier-aa.c
    -comp   : fait -ins puis compile le .c produit
    -min    : fait -comp puis par essais/erreur, minimise la liste des .h
    cc-opts : les options de compilation, en plus de -Wall
EOT
}


# Usage: ListeMots progc
ListeMots ()
{
    cat "$1" | tr -c 'a-zA-Z0-9_' \\n | tr -s \\n \\n | sort | uniq
}


# On suppose que chaque fonction n'apparait qu'une fois
# Usage: ListeFcts dico
ListeFcts ()
{
    if [ ! -r "$1" ]; then
        echo "Erreur de lecture du dictionnaire \"$1\"" 1>&2
        exit 1
    fi

    while read fonction headers
    do echo "$fonction"
    done < "$1"
}


# Intersection entre dico et mots du programme
# Usage: FctReconnues progc dico
FctReconnues ()
{
    { ListeMots "$1" ; ListeFcts "$2" ;} | sort | uniq -d

    # Autre solution :
    #   tmp="tmp1.$$"
    #   ListeMots "$1" > "$tmp"
    #   ListeFcts "$2" >> "$tmp"
    #   sort "$tmp" | uniq -d
    #   rm -f "$tmp"
}


# Usage: SuggerePointsH dico fichH
SuggerePointsH ()
{
    while read fct
    do
        grep "$fct" "$1"
    done |
    while read fct pointsh
    do
        echo "$pointsh"
    done |
    tr ' ' \\n | tr -s \\n \\n | sort | uniq >| "$2"
}


# Usage: LireTabH fichH
LireTabH ()
{
    local i=0
    while read pointh
    do
      tabH[$i]="$pointh" ; ((i++))
    done < "$fichH"
}


# Usage: InserePointsH progc prog2c
InserePointsH ()
{
    local i
    {
        echo "/* Inclusion des .h générée automatiquement par $0 */"
        echo "/* $(date) */"
        for ((i=0; i < ${#tabH[*]} ; i++)); do
            echo "#include <${tabH[$i]}>"
        done
        echo
        cat "$1"
    } >| "$2"
}


# Usage: CompileProgC progc options
CompileProgC ()
{
    prog="${1%.c}"
    if gcc "$1" -o "$prog" "$2" 2>&1 | grep "implicit declaration" > /dev/null
    then echo "echec"  ; return 1
    else echo "succes" ; return 0
    fi
}


# Usage: MinimisePointsH progc prog2c options
MinimisePointsH ()
{
    local i

    # On sauve la table originale
    tabO=(${tabH[*]})

    for ((i=0; i < ${#tabO[*]} ; i++))
    do
        echo "Essais sans ${tabO[$i]} .."
        tabH=(${tabO[*]})
        unset tabH[$i]     # on supprime
        tabH=(${tabH[*]})  # on tasse

        InserePointsH "$1" "$2"
        if CompileProgC "$2" "$3"; then
            tabO=(${tabH[*]})  # conserve cette suppression
        fi
    done

    echo "Compilation finale ..."
    tabH=(${tabO[*]})
    InserePointsH "$1" "$2"
    CompileProgC "$2" "$3"
}


#
# Programme principal
#

if [ $# -eq 0 ]; then
    AffiUsage 1>&2 ; exit 1
fi

case "$1" in
    -help) AffiUsage ; exit 0 ;;
    -list) ListeFcts "$dicoH" ; exit 0 ;;
    -mots) action=1 ; shift ;;
    -rec)  action=2 ; shift ;;
    -sug)  action=3 ; shift ;;
    -ins)  action=4 ; shift ;;
    -comp) action=5 ; shift ;;
    -min)  action=6 ; shift ;;
esac

if [ $# -eq 0 ]; then
    AffiUsage 1>&2 ; exit 1
fi

prog1c="$1" ; shift
if [ ! -r "$prog1c" ]; then
    echo "Erreur de lecture de \"$prog1c\"" 1>&2 ; exit 1
fi

if [ $# -ne 0 ]; then
    ccopt="$ccopt $*"
fi

if ((action == 1)); then
    ListeMots "$prog1c"
elif ((action == 2)); then
    FctReconnues "$prog1c" "$dicoH"
else
    FctReconnues "$prog1c" "$dicoH" | SuggerePointsH "$dicoH" "$fichH"

    echo "** Inclusions suggérées :"
    cat "$fichH"

    if ((action >= 4)); then
        LireTabH "$fichH"
        prog2c="${prog1c%.c}-aa.c"
        echo "** Insertion dans $prog2c ..."
        InserePointsH "$prog1c" "$prog2c"
    fi

    if ((action >= 5)); then
        echo "** Compilation de $prog2c avec $ccopt ..."
        CompileProgC "$prog2c" "$ccopt"
    fi
    if ((action >= 6)); then
        echo "** Minimisation des inclusions ..."
        MinimisePointsH "$prog1c" "$prog2c" "$ccopt"

        echo "** Inclusions minimales testées :"
        echo "${tabH[*]}"
    fi

    rm -f "$fichH"
fi

exit 0



