#! /bin/bash

LC_ALL=POSIX

usage() {
    cat <<EOF
Usage: ${0##*/} [-i module] [-x module] [-Nv] {module|rpm} ...

-i	Include the specified modules (glob expression).

-x	Exclude the specified modules (glob expression).

-N	Don't try to combine modalias tags that only differ in one character.

-v	Verbose: report which modules define which aliases.
EOF
    exit $1
}

ARGS=("$@")

options=`getopt -o i:x:vNh -- "$@"`

if [ $? -ne 0 ]
then
    usage 2
fi

eval set -- "$options"

opt_combine=1
while :; do
    case "$1" in
    -i)
	include="$include ${2%.ko}"
	shift 2
	;;
    -x)
	exclude="$exclude ${2%.ko}"
	shift 2
	;;
    -v)
	opt_verbose=1
	shift ;;
    -N)
	opt_combine=
	shift ;;
    -h)
	usage 0
	;;
    --)
	shift
	break
	;;
    esac
done

process_module() {
    declare module=$1 base=${1##*/} aliases

    if [ -n "$include" ]; then
	included=
	for x in $include; do
	    eval "case \"\${base%.ko}\" in (${x%.ko}) included=1;; esac"
	done
    else
	included=1
    fi
    for x in $exclude; do
	eval "case \"\${base%.ko}\" in (${x%.ko}) included=;; esac"
    done
    [ -n "$included" ] || return

    aliases="$(/sbin/modinfo -F alias "$module" \
	       | sed -nre 's@^.+:.+$@Supplements: modalias(&)@p')"
    if [ -n "$aliases" ]; then
	eval "processed_module_${base//[^a-zA-Z0-9_]/_}=$base"
	echo "$aliases" \
	| if [ -n "$opt_verbose" ]; then
	    sed -e "s@\$@ # $base@"
	else
	    cat
	fi
    fi
}

print_modaliases() {
    declare class=$1 variants=$2 pos=$3
    if [ -n "$variants" ]; then
	echo "${class:0:pos}[$variants]${class:pos+1}"
    else
	[ -z "$class" ] || echo "$class"
    fi
}

combine_modaliases() {
    declare tag class variants pos n
    read class
    while read tag; do
	for ((n=0; n<${#class}; n++)); do
	    if [ "*" != "${class:n:1}" -a \
		 "${class:0:n}" = "${tag:0:n}" -a \
		 "${class:n+1}" = "${tag:n+1}" ] &&
	       ( [ -z "$pos" ] || [ $n = $pos ] ); then
		variants="${variants:-${class:n:1}}${tag:n:1}"
		pos=$n
		break
	    fi
	done
	if [ $n -eq ${#class} ]; then
	    print_modaliases "$class" "$variants" "$pos"
	    variants=
	    pos=
	    class=$tag
	fi
    done
    print_modaliases "$class" "$variants" "$pos"
}

tmp=$(mktemp -t ${0##*/}.XXXXXX)
trap "rm -f $tmp" EXIT

for file in "$@"; do
    case "$(file -b "$file")" in
    RPM*)
	tmpdir=$(mktemp -td ${0##*/}.XXXXXX)
	rpm2cpio "$file" | ( cd $tmpdir && cpio -dim --quiet )
	for module in $(find $tmpdir -type f -name '*.ko'); do
	    process_module "$module" >> $tmp
	done
	rm -rf $tmpdir
	;;
    ELF*)
	process_module "$file" >> $tmp
	;;
    *)
	if [ -e "$file" ]; then
	    echo "File type of $file not supported" >&2
	    exit 1
	fi
	file2=$(/sbin/modinfo -F filename "${file%.ko}")
	if ! [ -e "$file2" ]; then
	    echo "No module $file found" >&2
	    exit 1
	fi
	process_module "$file2" >> $tmp
	;;
    esac
done

if [ -n "${!processed_module_*}" ]; then
    echo "# Modules:" $(for m in ${!processed_module_*}; do echo ${!m}; done \
			| sort -u)

    sort -u $tmp \
    | if [ -n "$opt_combine" ]; then
	combine_modaliases
    else
	cat
    fi

    echo "# Generated with: ${0##*/} ${ARGS[*]}"
fi

# vim:softtabstop=4 shiftwidth=4
