#!/bin/sh

: -*- mode: ksh -*-

## meta-funcs.sh - functions to facilitate analysis/comparison of .meta files
#

if ! type realpath > /dev/null 2>&1 ; then
# trivial realpath for platforms that lack it
realpath() {
    case "$1" in
    /*) echo $1;;
    */*) echo "`cd ${1%/*} && pwd`/${1##*/}";;
    *) echo `pwd`/$1;;
    esac
}
fi

read_link() {
    if [ -h $1 ]; then
        'ls' -l $1 | sed 's,.*-> ,,'
    else
        echo $1
    fi
}

## sb_filter - replace some var values with references
#
# Since every $SB is unique comparison is easier when
# we output ${SB} rather than its value.
# Same goes for $HOST_TARGET, $SB_BSD10 and any other
# var=value pairs we are given.
#
sb_filter() {
    subst=
    while :
    do
        case "$1" in
        *=*) eval "$1"
            case "${1%%=*}" in
            SB|SB_BSDX|HOST_TARGET) ;;
            *) subst="$subst -e s,${1#*=},_{${1%%=*}},g";;
            esac
            shift
            ;;
        -e) subst="$subst -e $2"; shift 2;;
        *) break;;
        esac
    done
    sed $subst -e "s,${SB:-notset},_{SB},g" \
        -e "s,${SB_BSDX:-notset},_{SB_BSDX},g" \
        -e "s,${HOST_TARGET:-notset},_{HOST_TARGET},g" \
        -e "s,${HOST_TARGET32:-notset},_{HOST_TARGET32},g" \
        -e 's,_{,${,g'
}

## meta_realpath_filter - apply realpath(1) to paths referenced in syscalls
#
# Pay attention to C (chdir) syscalls so that relative paths are
# resolved correctly.
#
meta_realpath_filter() {
    cwd=$1
    d=$1
    while read op f
    do
        : op=$op f=$f
        case "$op" in
        C)
            case "$f" in
            /*) d=$f;;
            *)
                for d in $d/$f $cwd/$f
                do
                    test -d $d && break
                done
                ;;
            esac
            continue
            ;;
        esac
        case "$f" in
        .) ;;
        /*) echo "$f";;
        *) realpath $d/$f;;
        esac
    done
}

## meta_flist - extract list of files referenced
#
# By default we are interested in files Read
# If the first arg is upper case it is assumed to be a set of syscall
# tags we want.
# Eg.  meta_flist ER would extract both Read and Execute entries.
# If ``--raw`` flag is present no filtering is done otherwise
# out put is passed through meta_realpath_filter
#
meta_flist() {
    class=R
    filter=meta_realpath_filter
    while :
    do
        case "$1" in
        [A-Z]*[A-Z]|[A-Z]) class="$1"; shift;;
        --raw) filter=cat; shift;;
        *) break;;
        esac
    done
    case "$filter" in
    cat)
        'grep' "^[$class] " /dev/null "$@" | sed 's,.*:[A-Z] [0-9]* ,,'
        return
        ;;
    esac
    for m in "$@"
    do
        d=`'grep' ^CWD $m | sed 's,CWD ,,'`
        'grep' "^[C$class] " /dev/null $m | sed 's,.*:\([A-Z]\) [0-9]* ,\1 ,' |
        meta_realpath_filter $d
    done
}

## meta_src_list
#
# Filter the output of flist to just files from SRCTOP
meta_src_list() {
    meta_flist "$@" | grep "${SRCTOP:-${SB_SRC:-/src}}/"
}

## meta_out_words - command output one word per line
#
# The one per line format makes diff'ing more effective.
#
meta_out_words() {
    sed '1,/-- command output/d;/-- filemon acquired/,$d' "$@" | tr ' ' '\012'
}

## meta_replay - extract command for replay
#
# It is often handy to re-run a command - perhaps with ``sh -x``
# or replace the ``-c`` of a comile command with ``-E`` to get
# pre-processor output::
#
#       meta_replay foo.meta | sed 's, -o .*,,;s, -c , -E ,'
#
meta_replay() {
    for meta in "$@"
    do
        sed -n '/^CWD/s,^CWD \(.*\),cd \1 \&\&,p' $meta
        sed -n '/^CMD/s,^CMD ,,p' $meta
    done | sed 's,^[@+-],,'
}

meta_cpp_out() {
    meta_replay "$@" | egrep 'cd | -c' | sed 's, -c , -E ,;s, -o .*,,'
}

## meta_cmd_words - extract command line one word per line
#
# Again the one per line format makes diff'ing more effective.
#
# See also sb_filter
#
meta_cmd_words() {
    'grep' '^CMD' "$@" | tr ' ' '\012'
}

## meta_Dlist - extract list of ``-I`` one per line
#
meta_Dlist() {
    meta_cmd_words "$@" | 'grep' '^-D'
}

## meta_Ilist - extract list of ``-I`` one per line
#
meta_Ilist() {
    meta_cmd_words "$@" | 'egrep' '^(-I|-isystem/)'
}

## meta_Llist - extract list of ``-L`` one per line
#
meta_Llist() {
    meta_cmd_words "$@" | 'egrep' '^(-L|-sysroot)'
}

## meta_hlist - extract list of headers
#
meta_hlist() {
    meta_flist "$@" | 'grep' '\.hp*$'
}

## meta_srcs_list - extract list of sources even those outside SB
#
meta_srcs_list() {
    meta_flist "$@" | 'egrep' '\.(c[cxp]*|hp*)$'
}

## objdir_dirdep turn an objdir into a dirdep
#
# objdir_dirdep [var=val ...] $objdir
#
# var is one of tsw tso as decribed below for meta_dirdep
#
objdir_dirdep() {
    tsw=${TARGET_SPEC_WORDS:-${TARGET_SPEC_VARS:+${TARGET_SPEC_VARS#}}}
    tsw=${tsw:-1}
    tso=${TARGET_OBJ_SPEC_ORDER}
    
    while :
    do
        case "$1" in
        *=*) eval "$1"; shift;;
        *) break;;
        esac
    done

    case "$1" in
    */obj/*)
        if ${isPOSIX_SHELL:-false}; then
            ddep=${1##*/obj/}
            ddep=${ddep%.meta}
        else
            ddep=`expr "$1" : '.*/obj/\(.*\)'`
            case "$ddep" in
            *.meta) ddep=`expr "$1" : '\(.*\).meta'`;;
            esac
        fi
        ;;
    esac
    ts=
    while [ $tsw -gt 0 ]
    do
        if ${isPOSIX_SHELL:-false}; then
            tsw=$((tsw - 1))
            x=${ddep%%/*}
            ddep=${ddep#$x/}
        else
            tsw=`expr $tsw - 1`
            x=`expr $ddep : '\([^/]*\)/.*'`
            ddep=`expr $ddep : '[^/]*/\(.*\)'`
        fi
        case "$tso" in
        r) ts="$x${ts:+,$ts}";;
        *) ts="${ts:+$ts,}$x";;
        esac
    done
    echo $ddep.$ts
}

## log_dirdep - extract _OBJDIR from "log" and pass to objdir_dirdep
#
# This is the build log equivalent of meta_dirdep below
#
log_dirdep() {
    tsw=${TARGET_SPEC_WORDS:-${TARGET_SPEC_VARS:+${TARGET_SPEC_VARS#}}}
    tsw=${tsw:-1}
    tso=${TARGET_OBJ_SPEC_ORDER}
    
    while :
    do
        case "$1" in
        -r) tso=r; shift;;
        -w) tsw=$2; shift 2;;
        *) break;;
        esac
    done
    for f in "$@"
    do
        objdir=`grep _OBJDIR= $f |
        sed -n "/_OBJDIR=/{s,.*='\(.*\)',\1,p;q;}"`
        objdir_dirdep tsw=$tsw tso=$tso $objdir
    done
}

## meta_dirdep - output the fully qualified dirdep
#
# The first line of a .meta file provides enough information
# to derrive the dirdep to pass to dirdeps.mk
#
# Options:
#	-r	Order of words in TARGET_OBJ_SPEC is reverse of
#		TARGET_SPEC_VARS (default $TARGET_OBJ_SPEC_ORDER)
#
#	-w "tsw"
#		the number of words in TARGET_SPEC_VARS
#		(default $TARGET_SPEC_WORDS or ${TARGET_SPEC_VARS#})
#
#
meta_dirdep() {
    tsw=${TARGET_SPEC_WORDS:-${TARGET_SPEC_VARS:+${TARGET_SPEC_VARS#}}}
    tsw=${tsw:-1}
    tso=${TARGET_OBJ_SPEC_ORDER}
    
    while :
    do
        case "$1" in
        -r) tso=r; shift;;
        -w) tsw=$2; shift 2;;
        *) break;;
        esac
    done

    ts=
    for f in "$@"
    do
        target=`sed -n '1{s,^.* \(/.*\)\.meta,\1,p;}' $f`
        if [ -d $target ]; then
            objdir=$target
        else
            objdir=`dirname $target`
        fi
        objdir_dirdep tsw=$tsw tso=$tso $objdir
    done
}

# csh uses can run meta-funcs.sh with 1st arg to indicate the
# function to call.
case "/$0" in
*funcs*) op=$1; shift
    do_op() {
        case "$op" in
        *log*dir*) log_dirdep "$@";;
        *objdir*) objdir_dirdep "$@";;
        *dirdep*) meta_dirdep "$@";;
        *filter*) sb_filter "$@";;
        *flist) meta_flist "$@";;
        Dlist) meta_Dlist "$@";;
        Ilist) meta_Ilist "$@";;
        *hlist) meta_hlist "$@";;
        *srcs*) meta_srcs_list "$@";;
        *src*) meta_src_list "$@";;
        *words|*cmd*) meta_cmd_words "$@";;
        *replay) meta_replay "$@";;
        *cpp*) meta_cpp_out "$@";;
        esac
    }
    case "$1" in
    --filter) shift; do_op "$@" | sb_filter;;
    *) do_op "$@";;
    esac
    ;;
*/meta*)
    op=${0##*/}
    $op "$@"
    ;;
esac
