#!/bin/sh

# Copyright (c) 2009, Aleksey Cheusov <vle@gmx.net>
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

set -e

LC_ALL=C
export LC_ALL

##################################################
# options
usage (){
    cat <<EOF
mkc_check_decl detects presense of define, variable, function or type
in system header files by compiling a test program.

Usage:
 mkc_check_decl <define|variable|func[0-9]|type|member> <what> [includes...]

Examples:
   mkc_check_decl define __GNUC__
   mkc_check_decl define RTLD_LAZY dlfcn.h
   mkc_check_decl variable sys_errlist errno.h
   mkc_check_decl variable __malloc_hook malloc.h
   mkc_check_decl func3 poll poll.h
   mkc_check_decl func2 fgetln stdio.h
   mkc_check_decl type mbstate_t wchar.h
   mkc_check_decl type long-long
   mkc_check_decl member tm.tm_isdst time.h
   mkc_check_decl member ifreq.ifr_addr.sa_len net/if.h
EOF
}

if test $# -eq 0; then
    usage
    exit 1
fi
if test "$1" = '-h' -o "$1" = '--help'; then
    usage
    exit 0
fi

##################################################
# initializing

pathpart=`echo $* | tr '/. ' ___`

decltype=`echo $1 | sed -e 's/[0-9]//g'`
argscnt=`echo $1 | sed 's/[^0-9]//g'`
shift

declwhat=`echo $1 | sed 's/-/ /'`
shift

. mkc_check_common.sh

##################################################
# functions

get_includes (){
    for i in $MKC_COMMON_HEADERS "$@"; do
	echo "#include <$i>"
    done
}

##############################
compile (){
    if $CC -c -o "$tmpo" $CPPFLAGS $CFLAGS "$tmpc" 2>"$tmperr"
    then
	return 0
    else
	return 1
    fi
}

##############################
is_define (){
    get_includes "$@" > "$tmpc"

    cat >> "$tmpc" <<EOF
#if defined($declwhat)
int main ()
{
   return 0;
}
#else
.error "$declwhat is not a define"
#endif
EOF

    #
    compile
}

##############################
is_variable (){
    get_includes "$@" > "$tmpc"

    cat >> "$tmpc" <<EOF
int main ()
{
   return sizeof (($declwhat)) && (&$declwhat != 0);
}
EOF
    #
    compile
}

##############################
try_type (){
    get_includes "$@" > "$tmpc"

    cat >> "$tmpc" <<EOF
int main ()
{
   return sizeof ($declwhat);
}
EOF

    #
    compile
}

##############################
is_type (){
    try_type "$@" || return 1
    is_variable "$@" && return 1
    return 0
}

##############################
is_func (){
    get_includes "$@" > "$tmpc"

    cat >> "$tmpc" <<EOF
void func (void)
{
   if (${declwhat}) return 0;
   ${declwhat} (
EOF

    awk -v N="$argscnt" '
BEGIN {
   for (i=0; i < N; ++i){
      if (i)
         printf ","
      printf "0"
   }
}
' >> "$tmpc"

    printf ');\n}\n' >> "$tmpc"

    #
    compile
}

##############################
is_member (){
    get_includes "$@" > "$tmpc"

    type_t=`echo $declwhat | sed 's/[.].*$//'`
    member=`echo $declwhat | sed 's/^[^.]*[.]//'`
    cat >> "$tmpc" <<EOF
int main ()
{
   struct $type_t var;
   return sizeof (var.$member);
}
EOF

    #
    compile
}

##################################################
# test

for i in "$@"; do
    incs_msg="$incs_msg $i"
done

if test -n "$incs_msg"; then
    incs_msg=" ($incs_msg )"
fi

if test "$MKC_NOCACHE" != 1 && test -f "$cache"; then
    cached=1
    printme '%s' "checking for $decltype ${declwhat}${incs_msg}... (cached) " 1>&2
    ret=`cat "$cache"`
else
    printme '%s' "checking for $decltype ${declwhat}${incs_msg}... " 1>&2

    # test itself
    set +e # workaround for buggy FreeBSD /bin/sh and ksh
    if eval is_${decltype} "$@"
    then
	ret=1
    else
	ret=0
    fi
    set -e # workaround for buggy FreeBSD /bin/sh and ksh
    echo $ret > "$cache"
fi

##################################################
# clean-ups

cleanup

##################################################
# finishing

if test "$ret" -eq 1; then
    printme 'yes\n' 1>&2
else
    printme 'no\n' 1>&2
fi

echo $ret
