dnl 
dnl Copyright 2000, 2001, 2003, 2004, 2005, 2006 The Regents of the University of California 
dnl All Rights Reserved 
dnl 
dnl Permission to use, copy, modify and distribute any part of this
dnl CoralReef software package for educational, research and non-profit
dnl purposes, without fee, and without a written agreement is hereby
dnl granted, provided that the above copyright notice, this paragraph
dnl and the following paragraphs appear in all copies.
dnl 
dnl Those desiring to incorporate this into commercial products or use
dnl for commercial purposes should contact the Technology Transfer
dnl Office, University of California, San Diego, 9500 Gilman Drive, La
dnl Jolla, CA 92093-0910, Ph: (858) 534-5815, FAX: (858) 534-7345.
dnl 
dnl IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY
dnl PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
dnl DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS
dnl SOFTWARE, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF
dnl THE POSSIBILITY OF SUCH DAMAGE.
dnl 
dnl THE SOFTWARE PROVIDED HEREIN IS ON AN "AS IS" BASIS, AND THE
dnl UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
dnl SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. THE UNIVERSITY
dnl OF CALIFORNIA MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES
dnl OF ANY KIND, EITHER IMPLIED OR EXPRESS, INCLUDING, BUT NOT LIMITED
dnl TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
dnl PARTICULAR PURPOSE, OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE
dnl ANY PATENT, TRADEMARK OR OTHER RIGHTS.
dnl 
dnl The CoralReef software package is developed by the CoralReef
dnl development team at the University of California, San Diego under
dnl the Cooperative Association for Internet Data Analysis (CAIDA)
dnl Program. Support for this effort is provided by the CAIDA grant
dnl NCR-9711092, DARPA NGI Contract N66001-98-2-8922, DARPA NMS Grant
dnl N66001-01-1-8909, and by CAIDA members.
dnl 
dnl Report bugs and suggestions to coral-bugs@caida.org.
dnl 

dnl project-independent autoconf macros
dnl  @(#) $Id: caida.m4,v 1.48 2006/02/28 19:14:28 kkeys Exp $


dnl Helper macro for CAIDA_PROG_MODERN_CXX()
define(CAIDA_GET_CXX_VERSION,
    # Extract first word of "$1" in case it is a program with arguments.
    set X $1
    prog=${2}
    if echo $prog | egrep '^/' >/dev/null; then
	{ $prog -E test$$.cc 2>/dev/null | sed -ne "s;COMPILER;$prog;p" >> versions$$; } \
	    2>/dev/null
    else
	oifs="$IFS"
	IFS=":$IFS"
	for dir in $PATH; do
	    { $dir/$prog -E test$$.cc 2>/dev/null | sed -ne "s;COMPILER;$dir/$prog;p" >> versions$$; } \
		2>/dev/null
	done
	IFS="$oifs"
    fi
)

dnl CAIDA_PROG_MODERN_CXX()
dnl Like AC_PROG_CXX, except it also searches for a _modern_ C++ compiler.
dnl If CXX or CCC was set, it will try only that program, instead of searching.
dnl If any C++ compiler is found, CXX will be set to its name.  If it is
dnl modern, MODCXX will be set to "MODCXX", otherwise MODCXX will be set to
dnl "NO".
AC_DEFUN(CAIDA_PROG_MODERN_CXX, [
    CXX="${CXX:-${CCC}}"

    AC_MSG_CHECKING([for modern GNU C++ compiler])

    dnl # Look for GNU compilers with versions >= 2.8.
    dnl # If more than one is found, use the one with the highest version.
    dnl # If none are found, use the first non-GNU compiler that was found.

    cat >test$$.cc <<EOF
#if __GNUC__ - 0
GNU __GNUC__ __GNUC_MINOR__ Z : COMPILER
#else
OTHER X Y Z : COMPILER
#endif
EOF
    echo "GNU 2 7 32767 : none" >versions$$
    if test -n "${CXX}"; then
        CAIDA_GET_CXX_VERSION([$CXX])
    else
        CAIDA_GET_CXX_VERSION([c++])
        CAIDA_GET_CXX_VERSION([g++])
        CAIDA_GET_CXX_VERSION([eg++])
        CAIDA_GET_CXX_VERSION([CC])
        CAIDA_GET_CXX_VERSION([cxx])
        CAIDA_GET_CXX_VERSION([cc++])
    fi

    dnl # Space is required between -k and argument on some versions of sort.
    dnl # The -s (stable) option is not portable.
    MODCXX=`egrep "^GNU" versions$$ | sort -nr -k 2 -k 3 -k 4 | sed -ne '1s/.*: //p'`

    if test "$MODCXX" = "none"; then
	AC_MSG_RESULT([$MODCXX])
    else
	version=`$MODCXX -v 2>&1 | egrep version`
	AC_MSG_RESULT([$MODCXX ($version)])
    fi

    if test "$MODCXX" = "none"; then
        AC_MSG_CHECKING([for non-GNU C++ compiler])
	MODCXX=`egrep "^OTHER" versions$$ | sort -nr -k 2 -k 3 -k 4 | sed -ne '1s/.*: //p'`
        if test -z "$MODCXX"; then
            MODCXX="none"
        fi
	AC_MSG_RESULT($MODCXX)
    fi

    if test -z "$CXX" && test "$MODCXX" != "none"; then
        CXX="$MODCXX"
    fi

    rm test$$.cc versions$$

    AC_PROG_CXX

    if test "$MODCXX" = "none"; then
	MODCXX="NO";
    else
	MODCXX="MODCXX";
    fi
    AC_SUBST(MODCXX)
])

dnl CAIDA_CXX_HAS_BOOL()
dnl Tests that the C++ compiler defines the type "bool".  If so, redefines
dnl CXX with -DHAS_BOOL.  This is useful for perl modules, because they
dnl depend on HAS_BOOL but perl.h doesn't always define it properly.
AC_DEFUN(CAIDA_CXX_HAS_BOOL, [
    AC_REQUIRE([AC_PROG_CXX])
    AC_LANG_SAVE()
    AC_LANG_CPLUSPLUS()
    AC_MSG_CHECKING([whether $CXX defines the bool type])
    AC_TRY_COMPILE(, [bool foo;], caida_has_bool="yes", caida_has_bool="no")
    if test "$caida_has_bool" = "yes"; then
	CXX="$CXX -DHAS_BOOL"
        AC_SUBST(CXX)
        AC_DEFINE(HAVE_BOOL)
    fi
    AC_MSG_RESULT([$caida_has_bool])
    AC_LANG_RESTORE()
])

dnl CAIDA_PROG_SWIG(cmp, major, minor, rev [,DO-IF-FOUND [,DO-IF-NOT-FOUND]])
dnl Tests that swig exists and has version $1 $2.$3.$4.  If so, defines SWIG
dnl as the full path of swig; otherwise, defines SWIG as a command that prints
dnl an error message and has non-zero exit status.
AC_DEFUN(CAIDA_PROG_SWIG, [
    AC_PATH_PROGS(SWIG, swig, "")
    if test -n "$SWIG"; then
        AC_MSG_CHECKING([whether swig version is $1 $2.$3.$4])
	if ${PERL-perl} -e ['open SWIG, "$ARGV[0] -version 2>&1|" or exit 1; $text = join "", <SWIG>; $text =~ s/.*version +(\d+\.\d+(\.\d+)?)\D.*/\1/si; @ver = split /\./, $text; $ver = $ver[0] * 1000000 + $ver[1] * 1000 + $ver[2]; $min = $ARGV[1] * 1000000 + @ARGV[2] * 1000 + $ARGV[3]; exit !($ver $1 $min);'] $SWIG $2 $3 $4; then
	    AC_MSG_RESULT([yes])
	    $5
	else
	    AC_MSG_RESULT([no])
	    SWIG=""
	    $6
	fi
    else
	: "some shells do not allow empty else-body"
	$6
    fi

    AC_SUBST(SWIG)
])

dnl CAIDA_FIND_TYPE(TYPE, HEADERS [, DO-IF-FOUND [,DO-IF-NOT-FOUND]])
dnl Search for a header in the set of HEADERS that defines TYPE.  Defines
dnl HAVE_type if found, and HAVE_header for the header required to define
dnl the type.
AC_DEFUN(CAIDA_FIND_TYPE, [
    AC_CACHE_CHECK([for a header that defines $1],
	coralreef_cv_header_with_$1, [
	    AC_REQUIRE([AC_HEADER_STDC])
	    ac_text="$1 var_$1;"
	    ac_found=0
	    for ac_hdr in "none required" $2; do
		test "$ac_hdr" = "none required" && ac_inc="" ||
		    ac_inc="#include <$ac_hdr>"
		AC_TRY_COMPILE([
		    #ifdef STDC_HEADERS
		    # include <stdlib.h>
		    #endif
		    $ac_inc
		], [$ac_text], [ac_found=1])
		if test "$ac_found" = "1"; then break; fi
	    done
	    if test "$ac_found" = "0"; then
		ac_hdr="none"
	    fi
	    eval coralreef_cv_header_with_$1="'$ac_hdr'"
	])
    ac_hdr="$coralreef_cv_header_with_$1"
    if test "$ac_hdr" != "none"; then
	ac_tr_type=HAVE_`echo $1 | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
	AC_DEFINE_UNQUOTED($ac_tr_type)
	if test "$ac_hdr" != "none required"; then
	    # diff versions of tr handle '-' differently.  sed is consistent.
	    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
	    AC_DEFINE_UNQUOTED($ac_tr_hdr)
	fi
	$3
    else
	: "some shells do not allow empty else-body"
	$4
    fi
])

dnl CAIDA_TYPE_FMTS_HELPER(bits, hexnumber, suffix, conv)
AC_DEFUN(CAIDA_TYPE_FMTS_HELPER, [
    define([BITS], $1)dnl
    define([CONV], $4)dnl
    define([SIGNED], ifelse(CONV, d, , u))dnl
    bits=BITS
    str=$2
    num=$2$3
    conv=CONV
    type=SIGNED[]int[]BITS[]_t
    AC_MSG_CHECKING([for ${type} printf and scanf modifiers])

    AC_CACHE_VAL([coralreef_cv_intmod_]CONV[]BITS, [

    AC_TRY_RUN([
#ifdef STDC_HEADERS
# include <stdlib.h>
#endif
#include "caida-config/caida_t.2.h"
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    char buf[80], expected[80];
    char str[] = "${str}";
    ${type} a = ${num};
    /* sentinels s1 and s2 are declared on either side of b, so a scanf that
     * overflows b will clobber one of them. */
    char s1;
    ${type} b = 0;
    char s2;
    char c1, c2;
    int i;
    FILE *out;
    char printf_fmt[16];
    char scanf_fmt[16];
    /* 1999 C defines these modifiers:
	"hh"	char
	"h"	short
	"l"	long
	"ll"	long long
	"j"	intmax_t
	"z"	size_t
	"t"	ptrdiff_t
	"L"	long double
     */
    const char *mod[] = { "hh", "h", "", "l", "ll", "L", "q" };
#define NMOD (sizeof(mod)/sizeof(*mod))

    out = fopen("conftestdata", "w");
    if (!out) exit(-1);

#ifdef PRI${conv}${bits}
    /* use the system's defintion (in case by some twist they're not
     * defined at compile time) */
    strcpy(printf_fmt, "%" PRI${conv}${bits});
    fprintf(out, "\"%.*s\" ", sizeof(PRI${conv}${bits}) - 2, PRI${conv}${bits});
#else
    /* figure out a defintion */
    for (i = 0; i < NMOD; i++) {
        sprintf(printf_fmt, "%%c%%#%s${conv}%%c", mod[i]);
        sprintf(buf, printf_fmt, '(', a, ')');
        sprintf(expected, "%c%s%c", '(', str, ')');
#if 0 /* debug */
        fprintf(stdout, "\nformat #%s${conv}, expected %s, got %s\n",
	    mod[i], expected, buf);
#endif
        if (strcmp(buf, expected) == 0) {
            fprintf(out, "\"%s\" ", mod[i]);
            break;
        }
    }
    if (i == NMOD) exit(1);
# endif

    if (${bits} == 8) {
	/* 1989 C does not have any way to scanf 8 bit ints, so to avoid
	 * writing programs that depend on it, we NEVER define SCNx8.
	 */
	fprintf(out, "none");
    } else {
#ifdef SCN${conv}${bits}
	/* use the system's defintion (in case by some twist they're not
	 * defined at compile time) */
	fprintf(out, "\"%.*s\"\n",
	    sizeof(SCN${conv}${bits}) - 2, SCN${conv}${bits});
#else
	/* figure out a defintion */
	for (i = 0; i < NMOD; i++) {
	    s1 = 'X';
	    s2 = 'Y';
	    c1 = 'A';
	    c1 = 'Z';
	    b = -1;
	    sprintf(scanf_fmt, "%%c%%%s${conv}%%c", mod[i]);
	    sscanf(expected, scanf_fmt, &c1, &b, &c2);
	    sprintf(buf, printf_fmt, c1, b, c2);
#if 0 /* debug */
	    fprintf(stdout, "\nmod %s, expected %s, got %s, %c%c",
		mod[i], expected, buf, s1, s2);
#endif
	    if (strcmp(buf, expected) == 0 && s1 == 'X' && s2 == 'Y') {
		fprintf(out, "\"%s\"\n", mod[i]);
		break;
	    }
	}
	if (i == NMOD) fprintf(out, "none");
#endif /* SCN${conv}${bits} */
    }

    exit(0);
}
],
	    [coralreef_cv_intmod_]CONV[]BITS[=`cat conftestdata`],
	    [coralreef_cv_intmod_]CONV[]BITS[="none none"],
	    [AC_MSG_ERROR([don't know how to test when cross-compiling])]
	)
    ])

    AC_MSG_RESULT($coralreef_cv_intmod_[]CONV[]BITS)
    set $coralreef_cv_intmod_[]CONV[]BITS
    if test "[$]1" != "none" && test "[$]1" != "standard"; then
	if test "CONV" = "d"; then
	    AC_DEFINE_UNQUOTED([PRId]BITS, [$]{1} "d")
	    AC_DEFINE_UNQUOTED([PRIi]BITS, [$]{1} "i")
	else
	    AC_DEFINE_UNQUOTED([PRIo]BITS, [$]{1} "o")
	    AC_DEFINE_UNQUOTED([PRIu]BITS, [$]{1} "u")
	    AC_DEFINE_UNQUOTED([PRIx]BITS, [$]{1} "x")
	    AC_DEFINE_UNQUOTED([PRIX]BITS, [$]{1} "X")
	fi
    fi
    if test "[$]2" != "none" && test "[$]2" != "standard"; then
	if test "CONV" = "d"; then
	    AC_DEFINE_UNQUOTED([SCNd]BITS, [$]{2} "d")
	    AC_DEFINE_UNQUOTED([SCNi]BITS, [$]{2} "i")
	else
	    AC_DEFINE_UNQUOTED([SCNo]BITS, [$]{2} "o")
	    AC_DEFINE_UNQUOTED([SCNu]BITS, [$]{2} "u")
	    AC_DEFINE_UNQUOTED([SCNx]BITS, [$]{2} "x")
	fi
    fi
])

dnl CAIDA_TYPE_FMTS()
AC_DEFUN(CAIDA_TYPE_FMTS, [
    CAIDA_TYPE_FMTS_HELPER(8,  0xf0, U, x)
    CAIDA_TYPE_FMTS_HELPER(16, 0xf0e1, U, x)
    CAIDA_TYPE_FMTS_HELPER(32, 0xf0e1d2c3, UL, x)
    CAIDA_TYPE_FMTS_HELPER(64, 0xf0e1d2c3b4a59687, ULL, x)
    CAIDA_TYPE_FMTS_HELPER(8,  -128, , d)
    CAIDA_TYPE_FMTS_HELPER(16, -32768, , d)
    CAIDA_TYPE_FMTS_HELPER(32, -2147483648, L, d)
    CAIDA_TYPE_FMTS_HELPER(64, -9223372036854775808, LL, d)
])

dnl CAIDA_FIND_INTTYPES(BITS, HEADERS, DO-IF-NOT-FOUND)
dnl Searches HEADERS for signed and unsigned inttypes of size BITS.
AC_DEFUN(CAIDA_FIND_INTTYPES, [
    CAIDA_FIND_TYPE(int$1_t, $2, , $3)
    CAIDA_FIND_TYPE(uint$1_t, $2, , CAIDA_FIND_TYPE(u_int$1_t, $2, , $3))
])

dnl CAIDA_CXX_TYPE_IO(TYPE [, DO-IF-FOUND [, DO-IF-NOT-FOUND]])
AC_DEFUN(CAIDA_CXX_TYPE_IO, [
    type='$1'
    ac_tr_type="HAVE_CXX_`echo $1 | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`_IO"
    AC_MSG_CHECKING([for C++ ${type} i/o support])

    AC_CACHE_VAL([coralreef_cv_cxx_${type}_io], [

    AC_LANG_SAVE
    AC_LANG_CPLUSPLUS
    AC_TRY_COMPILE([
#include "caida-config/caida_t.2.h"
#include <iostream>
        ],
        [
            ${type} n;
            std::cerr << n;
            return 0;
        ],
        [eval coralreef_cv_cxx_${type}_io=yes],
        [eval coralreef_cv_cxx_${type}_io=no]
        )
        AC_LANG_RESTORE
    ])

    eval result=\$coralreef_cv_cxx_${type}_io
    AC_MSG_RESULT($result)
    if test "$result" = "yes"; then
        AC_DEFINE_UNQUOTED(${ac_tr_type})
	$2
    else
	: "some shells do not allow empty else-body"
	$3
    fi
])

dnl CAIDA_TYPES()
dnl Does all the work needed for caida_t.h.
AC_DEFUN(CAIDA_TYPES, [
    headers="inttypes.h stdint.h sys/types.h machine/types.h"

    ac_need_sizes=0
    CAIDA_FIND_INTTYPES(8, $headers, ac_need_sizes=1)
    CAIDA_FIND_INTTYPES(16, $headers, ac_need_sizes=1)
    CAIDA_FIND_INTTYPES(32, $headers, ac_need_sizes=1)
    CAIDA_FIND_INTTYPES(64, $headers, ac_need_sizes=1)

    if test $ac_need_sizes = 1; then
	AC_CHECK_SIZEOF(short,2)
	AC_CHECK_SIZEOF(int,4)
	AC_CHECK_SIZEOF(long,4)
	AC_CHECK_SIZEOF(long long,8)
    fi
    CAIDA_TYPE_FMTS()
])

dnl findall prog...
dnl Prints a list of every full path to prog in $PATH
findall() {
    for prog in "$@"; do
	case "$prog" in
	*/*)
	    echo $prog
	    ;;
	*)
	    oifs="$IFS"
	    IFS=':'
	    for dir in $PATH; do
		if test -x $dir/$prog && test -f $dir/$prog; then
		    echo $dir/$prog
		fi
	    done
	    IFS="$oifs"
	    ;;
	esac
    done
}

dnl CAIDA_PROG_PERL(LIST [, MINVERSION])
dnl Finds the perl interpreter in LIST with the greatest version number
dnl greater than MINVERSION, and sets PERL.
AC_DEFUN(CAIDA_PROG_PERL, [
    if test "x$2" = "x"; then
	version=0.0
	AC_MSG_CHECKING([for perl])
    else
	version=$2
	AC_MSG_CHECKING([for perl version >= $version])
    fi
    PERL=""
    cmp=">="
    for perl in `findall $1`; do
	if $perl -e "use English; exit !(\$PERL_VERSION $cmp $version);" 2>/dev/null
	then
	    PERL=$perl
	    version=`$perl -e 'use English; printf "%f\n", $PERL_VERSION;'`
	    cmp=">"; # Got one; don't pick one later in path unless it is NEWER
	fi
    done
    if test -z "$PERL"; then
	AC_MSG_RESULT([none])
    else
	AC_MSG_RESULT([$PERL ($version)])
    fi
    AC_SUBST(PERL)
])


dnl CAIDA_PERLMOD_CONFIG()
dnl Uses $PERL to create perl.inc, which defines the following variables
dnl needed by a Makefile to compile perl modules:
dnl   PERL_INC, PERL_LD, PERL_LDDLFLAGS, PERL_CCCDLFLAGS.
AC_DEFUN(CAIDA_PERLMOD_CONFIG, [
    AC_CHECKING([how to compile perl modules])
    ${PERL:-false perl} caida-config/configure.pl > perl.inc ||
	AC_MSG_ERROR([Error while testing perl])
])

dnl CAIDA_CHECK_PERL_MODULE(MODNAME, VERSION, VARNAME, ALT_LOCATION)
dnl Checks for specific perl modules of a sepcific version (version can be
dnl specified as ANY.  Sets a variable have_VARNAME in configure.in to be 1
dnl if available, 0 otherwise.  If ALT_LOCATION is not "", adds it to @INC
dnl before checking.
AC_DEFUN(AC_CAIDA_CHECK_PERL_MODULE,[dnl
if test -z "$4"; then
    AC_MSG_CHECKING([for perl module $1 ($2) in system-wide directory ])
    if !$perl -e 'require $1; if($2 ne 'ANY'){ $1->require_version($2); }' 2>/dev/null; then
        have_$3=0
        AC_MSG_RESULT([not found])
    else
        have_$3=1
        AC_MSG_RESULT([found])
    fi
else  
    AC_MSG_CHECKING([for perl module $1 ($2) in $4])
    if !$perl -e 'use lib $4; require $1; if($2 ne 'ANY'){ $1->require_version($2); }' 2>/dev/null; then
        have_$3=0
        AC_MSG_RESULT([not found])
    else
        have_$3=1
        AC_MSG_RESULT([found])
    fi
fi 
AC_SUBST(have_$3)
])dnl
