#!/usr/bin/perl
#
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
#
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.

# Configure
#
# This script builds nmake and visual studio build files
#

require 5.18.0;
no warnings 'experimental::smartmatch';

use strict;
use File::Spec;

# files to configure

my $configfile = "../config.h";
my $platformfile = "../lib/isc/win32/include/isc/platform.h";
my @confshlist = ("../bin/tests/system/conf.sh",
		  "../bin/tests/system/run.sh",
		  "../bin/tests/system/start.sh",
		  "../bin/tests/system/stop.sh");

my @filelist = ("../lib/dns/win32/libdns.def",
                "../lib/isc/win32/libisc.def");

my @projectlist = ("../bin/check/win32/checkconf.vcxproj",
                   "../bin/check/win32/checkconf.vcxproj.filters",
                   "../bin/check/win32/checktool.vcxproj",
                   "../bin/check/win32/checktool.vcxproj.filters",
                   "../bin/check/win32/checkzone.vcxproj",
                   "../bin/check/win32/checkzone.vcxproj.filters",
                   "../bin/confgen/win32/confgentool.vcxproj",
                   "../bin/confgen/win32/confgentool.vcxproj.filters",
                   "../bin/confgen/win32/tsigkeygen.vcxproj",
                   "../bin/confgen/win32/tsigkeygen.vcxproj.filters",
                   "../bin/confgen/win32/rndcconfgen.vcxproj",
                   "../bin/confgen/win32/rndcconfgen.vcxproj.filters",
                   "../bin/delv/win32/delv.vcxproj",
                   "../bin/delv/win32/delv.vcxproj.filters",
                   "../bin/dig/win32/dig.vcxproj",
                   "../bin/dig/win32/dig.vcxproj.filters",
                   "../bin/dig/win32/dighost.vcxproj",
                   "../bin/dig/win32/dighost.vcxproj.filters",
                   "../bin/dig/win32/host.vcxproj",
                   "../bin/dig/win32/host.vcxproj.filters",
                   "../bin/dig/win32/nslookup.vcxproj",
                   "../bin/dig/win32/nslookup.vcxproj.filters",
                   "../bin/dnssec/win32/cds.vcxproj",
                   "../bin/dnssec/win32/cds.vcxproj.filters",
                   "../bin/dnssec/win32/dnssectool.vcxproj",
                   "../bin/dnssec/win32/dnssectool.vcxproj.filters",
                   "../bin/dnssec/win32/dsfromkey.vcxproj",
                   "../bin/dnssec/win32/dsfromkey.vcxproj.filters",
                   "../bin/dnssec/win32/importkey.vcxproj",
                   "../bin/dnssec/win32/importkey.vcxproj.filters",
                   "../bin/dnssec/win32/keyfromlabel.vcxproj",
                   "../bin/dnssec/win32/keyfromlabel.vcxproj.filters",
                   "../bin/dnssec/win32/keygen.vcxproj",
                   "../bin/dnssec/win32/keygen.vcxproj.filters",
                   "../bin/dnssec/win32/revoke.vcxproj",
                   "../bin/dnssec/win32/revoke.vcxproj.filters",
                   "../bin/dnssec/win32/settime.vcxproj",
                   "../bin/dnssec/win32/settime.vcxproj.filters",
                   "../bin/dnssec/win32/signzone.vcxproj",
                   "../bin/dnssec/win32/signzone.vcxproj.filters",
                   "../bin/dnssec/win32/verify.vcxproj",
                   "../bin/dnssec/win32/verify.vcxproj.filters",
                   "../bin/named/win32/named.vcxproj",
                   "../bin/named/win32/named.vcxproj.filters",
                   "../bin/nsupdate/win32/nsupdate.vcxproj",
                   "../bin/nsupdate/win32/nsupdate.vcxproj.filters",
                   "../bin/pkcs11/win32/pk11destroy.vcxproj",
                   "../bin/pkcs11/win32/pk11destroy.vcxproj.filters",
                   "../bin/pkcs11/win32/pk11keygen.vcxproj",
                   "../bin/pkcs11/win32/pk11keygen.vcxproj.filters",
                   "../bin/pkcs11/win32/pk11list.vcxproj",
                   "../bin/pkcs11/win32/pk11list.vcxproj.filters",
                   "../bin/pkcs11/win32/pk11tokens.vcxproj",
                   "../bin/pkcs11/win32/pk11tokens.vcxproj.filters",
                   "../bin/rndc/win32/rndc.vcxproj",
                   "../bin/rndc/win32/rndc.vcxproj.filters",
                   "../bin/tools/win32/arpaname.vcxproj",
                   "../bin/tools/win32/arpaname.vcxproj.filters",
                   "../bin/tools/win32/journalprint.vcxproj",
                   "../bin/tools/win32/journalprint.vcxproj.filters",
                   "../bin/tools/win32/mdig.vcxproj",
                   "../bin/tools/win32/mdig.vcxproj.filters",
                   "../bin/tools/win32/nsec3hash.vcxproj",
                   "../bin/tools/win32/nsec3hash.vcxproj.filters",
                   "../bin/tools/win32/rrchecker.vcxproj",
                   "../bin/tools/win32/rrchecker.vcxproj.filters",
                   "../bin/tests/system/win32/bigkey.vcxproj",
                   "../bin/tests/system/win32/bigkey.vcxproj.filters",
                   "../bin/tests/system/win32/feature-test.vcxproj",
                   "../bin/tests/system/win32/feature-test.vcxproj.filters",
                   "../bin/tests/system/win32/gencheck.vcxproj",
                   "../bin/tests/system/win32/gencheck.vcxproj.filters",
                   "../bin/tests/system/win32/keycreate.vcxproj",
                   "../bin/tests/system/win32/keycreate.vcxproj.filters",
                   "../bin/tests/system/win32/keydelete.vcxproj",
                   "../bin/tests/system/win32/keydelete.vcxproj.filters",
                   "../bin/tests/system/win32/pipequeries.vcxproj",
                   "../bin/tests/system/win32/pipequeries.vcxproj.filters",
                   "../bin/tests/system/win32/makejournal.vcxproj",
                   "../bin/tests/system/win32/makejournal.vcxproj.filters",
                   "../bin/tests/system/win32/resolve.vcxproj",
                   "../bin/tests/system/win32/resolve.vcxproj.filters",
                   "../bin/win32/BINDInstall/BINDInstall.vcxproj",
                   "../bin/win32/BINDInstall/BINDInstall.vcxproj.filters",
                   "../lib/bind9/win32/libbind9.vcxproj",
                   "../lib/bind9/win32/libbind9.vcxproj.filters",
                   "../lib/dns/win32/gen.vcxproj",
                   "../lib/dns/win32/gen.vcxproj.filters",
                   "../lib/dns/win32/libdns.vcxproj",
                   "../lib/dns/win32/libdns.vcxproj.filters",
                   "../lib/irs/win32/libirs.vcxproj",
                   "../lib/irs/win32/libirs.vcxproj.filters",
                   "../lib/isc/win32/libisc.vcxproj",
                   "../lib/isc/win32/libisc.vcxproj.filters",
                   "../lib/isccc/win32/libisccc.vcxproj",
                   "../lib/isccc/win32/libisccc.vcxproj.filters",
                   "../lib/isccfg/win32/libisccfg.vcxproj",
                   "../lib/isccfg/win32/libisccfg.vcxproj.filters",
                   "../lib/ns/win32/libns.vcxproj",
                   "../lib/win32/bindevt/bindevt.vcxproj",
                   "../lib/win32/bindevt/bindevt.vcxproj.filters",
                   "bind9.sln");

# for config.h

my %configdefh;

my @substdefh = ("PACKAGE_VERSION_MAJOR",
                 "PACKAGE_VERSION_MINOR",
                 "PACKAGE_VERSION_PATCH",
                 "PACKAGE_VERSION_EXTRA",
                 "PACKAGE_VERSION",
                 "PACKAGE_NAME",
                 "PACKAGE_DESCRIPTION",
                 "PACKAGE_SRCID",
                 "PACKAGE_CONFIGARGS",
                 "PACKAGE_BUILDER",
                 "PACKAGE_STRING",
                 "MAPAPI",
                 "DNS_RDATASET_FIXED",
                 "HAVE_GEOIP2",
                 "HAVE_GSSAPI",
                 "HAVE_GSSAPI_H",
                 "HAVE_GSSAPI_KRB5_H",
                 "HAVE_KRB5_H",
                 "HAVE_GSSAPI_GSSAPI_H",
                 "HAVE_GSSAPI_GSSAPI_KRB5_H",
                 "HAVE_KRB5_KRB5_H",
                 "HAVE_LIBXML2",
                 "USE_BACKTRACE",
                 "USE_OPENSSL",
                 "USE_PKCS11",
                 "HAVE_READLINE",
                 "HAVE_ZLIB",
                 "ISC_LIST_CHECKINIT",
                 "TUNE_LARGE",
                 "WANT_QUERYTRACE",
                 "WITH_IDN",
                 "CPU_RELAX",
                 "VALIDATION_DEFAULT",
                 "HAVE_UV_HANDLE_GET_DATA",
                 "HAVE_UV_HANDLE_SET_DATA",
                 "HAVE_UV_IMPORT",
    );

# for platform.h

my %configdefp;

my @substdefp = ();

# for conf.sh

my %configtest;

my @substtest = ("CRYPTO",
                 "DEVELOPER_MODE",
                 "DNSTAP",
                 "FSTRM_CAPTURE",
                 "JSONSTATS",
                 "NZD_TOOLS",
                 "XMLSTATS",
                 "ZLIB",
		 "builddir",
		 "srcdir",
		 "abs_builddir",
		 "abs_srcdir",
		 "top_builddir",
		 "top_srcdir",
		 "abs_top_builddir",
		 "abs_top_srcdir");

# includes

my %configinc;

my @substinc = ("GSSAPI_INC",
                "GEOIP_INC",
                "IDN_INC",
                "LIBXML2_INC",
                "LIBUV_INC",
                "NGHTTP2_INC",
                "OPENSSL_INC",
                "READLINE_INC",
                "ZLIB_INC");

# libraries

my %configlib;

my @substlib = ("GSSAPI_LIB",
                "GEOIP_LIB",
                "IDN_LIB",
                "KRB5_LIB",
                "LIBXML2_LIB",
                "LIBUV_LIB",
                "NGHTTP2_LIB",
                "OPENSSL_LIBCRYPTO",
                "OPENSSL_LIBSSL",
                "READLINE_LIB",
                "READLINE_LIBD",
                "ZLIB_LIB");

# DLLs

my %configdll;

my @substdll = ("COMERR_DLL",
                "GSSAPI_DLL",
                "ICONV_DLL",
                "IDN_DLL",
                "KRB5_DLL",
                "K5SPRT_DLL",
                "LIBXML2_DLL",
                "LIBUV_DLL",
                "NGHTTP2_DLL",
                "OPENSSL_DLLCRYPTO",
                "OPENSSL_DLLSSL",
                "WSHELP_DLL",
                "ZLIB_DLL");

# variables

my %configvar = (
        "TOOLS_VERSION" => "4.0",
);

my @substvar = ("BIND9_VERSION",
                "BUILD_MACHINE",
                "BUILD_PLATFORM",
                "COPTI",
                "COPTML",
                "COPTMLD",
                "COPTX",
                "COPTY",
                "DLZ_SYSTEM_TEST",
                "EXEEXT",
                "INTRINSIC",
                "MACHINE",
                "OPENSSL_PATH",
                "PLATFORM",
                "PLATFORM_TOOLSET",
                "prefix",
                "PSSUSPEND",
                "PYTHON",
                "TOOLS_VERSION",
                "VCREDIST_PATH",
                "WINDOWS_TARGET_PLATFORM_VERSION");

# defines

my %configdefd;

my @substdefd = ("PK11_LIB_LOCATION",
                 "USE_PYTHON");

# conditions

my %configcond;

my @substcond = ("ATOMIC",
                 "GSSAPI",
                 "GEOIP",
                 "IDNKIT",
                 "LIBXML2",
                 "OPENSSL",
                 "PKCS11",
                 "PYTHON",
                 "STESTS",
                 "TESTS",
                 "ZLIB");

my @allcond = (@substcond, "NOTYET", "NOLONGER");

# paths

my $top_builddir = File::Spec->updir();
my $top_srcdir = File::Spec->updir();
my $abs_top_builddir = File::Spec->rel2abs($top_builddir);
my $abs_top_srcdir = File::Spec->rel2abs($top_srcdir);

# arguments

# enable-xxx/disable-xxx

my @enablelist = ("developer",
                  "fixed-rrset",
                  "intrinsics",
                  "native-pkcs11",
                  "openssl-hash",
                  "querytrace");

# with-xxx/without-xxx

my @withlist = ("aes",
                "cross-compile",
                "extra-tests",
                "gssapi",
                "geoip2",
                "iconv",
                "idn",
                "openssl",
                "libxml2",
                "nghttp2",
                "pkcs11",
                "pssuspend",
                "python",
                "readline",
                "system-tests",
                "tests",
                "tuning",
                "libuv",
                "vcredist",
                "zlib");

# general arguments

my @optionlist = ("help", "verbose", "legacy", "win32", "x64", "clean");

# usage

my @usage = ("Usage: perl Configure help\n",
             "       perl Configure options* win32|x64\n",
             "       perl Configure clean\n");

# help

my @help = (
"'Configure' configures BIND9 build files.\n\n",
@usage,
"\nGeneral Options and Commands:\n",
"  verbose               (options) print messages\n",
"  help                  (command) print this help\n",
"  win32                 (command) configure for Win32 platform\n",
"  x64                   (command) configure for x64 platform\n",
"  clean                 (command) clean up generated files\n",
"  <none>                (command) print a summary of the configuration\n",
"\nOptional Features:\n",
"  enable-intrinsics     enable intrinsic/atomic functions [default=yes]\n",
"  enable-native-pkcs11  use native PKCS#11 for all crypto [default=no]\n",
"  enable-openssl-hash   use OpenSSL for hash functions [default=yes]\n",
"  enable-fixed-rrset    enable fixed rrset ordering [default=no]\n",
"  enable-developer      enable developer build settings [default=no]\n",
"  enable-querytrace     enable very verbose query trace [default=no]\n",
"\nOptional Packages:\n",
"  with-tests            build with test suite\n",
"  with-extra-tests      build with extra test suite\n",
"  with-system-tests     build with system test suite\n",
"  with-openssl[=PATH]   build with OpenSSL yes|path (mandatory)\n",
"  with-libuv[=PATH]     build with libuv yes|path (mandatory)\n",
"  with-nghttp2[=PATH]   build with nghttp2 yes|path (mandatory)\n",
"  with-pkcs11[=PATH]    build with PKCS#11 support yes|no|provider-path\n",
"  with-gssapi[=PATH]    build with MIT KfW GSSAPI yes|no|path\n",
"  with-libxml2[=PATH]   build with libxml2 library yes|no|path\n",
"  with-geoip2[=PATH]    build with GeoIP2 support yes|no|path\n",
"  with-pssuspend[=COMMAND] specify pssuspend command\n",
"  with-python[=COMMAND] specify python interpreter python|command\n",
"  with-readline[=PATH]  build with readline library support yes|no|path\n",
"  with-idn[=PATH]       build with IDN kit support yes|no|path\n",
"  with-iconv[=PATH]     path of the iconv DLL [default=same than idn]\n",
"  with-zlib[=PATH]      build with zlib library yes|no|path\n",
"  with-vcredist[=PATH]  visual C++ redistributable package yes|path\n",
"  with-tuning=OPTION    tune for platform size (small|default)\n",
"  with-cross-compile    32 / 64 bit build / host platforms\n",
"\nOptional Visual Studio project parameters:\n",
"  with-tools-version=VERSION     set the ToolsVersion attribute to VERSION\n",
"  with-platform-toolset=VERSION  use supplied toolset VERSION for building\n",
"  with-platform-version=VERSION  use supplied Windows SDK VERSION for building\n");

# Parse arguments

my $verbose = 0;
my $legacy_only = 0;
my $want_help = "no";
my $want_win32 = "no";
my $want_x64 = "no";
my $want_clean = "no";
my $want_unknown = "no";
my $unknown_value;
my $enable_intrinsics = "yes";
my $cryptolib = "";
my $enable_native_pkcs11 = "no";
my $enable_crypto_rand = "yes";
my $enable_fixed_rrset = "no";
my $enable_developer = "no";
my $enable_querytrace = "no";
my $enable_rpz_nsip = "yes";
my $enable_rpz_nsdname = "yes";
my $use_tests = "no";
my $use_stests = "no";
my $use_libuv = "auto";
my $libuv_path = "../../";
my $nghttp2_path = "../../";
my $use_nghttp2 = "auto";
my $use_openssl = "auto";
my $openssl_path = "../../";
my $use_pkcs11 = "no";
my $pkcs11_path = "unknown";
my $use_aes = "auto";
my $use_gssapi = "no";
my $validation_default = "auto";
my $gssapi_path = "C:/Program\ Files/MIT/Kerberos/";
my $use_geoip2 = "no";
my $geoip2_path = "";
my $use_libxml2 = "auto";
my $libxml2_path = "../../";
my $with_pssuspend = "no";
my $pssuspend_command = "pssuspend.exe";
my $use_python = "auto";
my $python_command = "python.exe";
my $use_readline = "no";
my $readline_path = "../../";
my $use_idn = "no";
my $idn_path = "../../";
my $iconv_path = " --idn-- ";
my $use_zlib = "no";
my $zlib_path = "../../";
my $use_vcredist = "yes";
my $vcredist_path = " --infer-- ";
my $cross_compile = "no";
my $tuning = "default";
my $want_checkfiles = "no";

# no arguments -> usage

if ($#ARGV < 0) {
    foreach (@usage) {
        print $_;
    }
    exit 1;
}

# parse arguments

foreach (@ARGV) {
    if (/^verbose$/i) {
        $verbose = 1;
    } elsif (/^help$/i) {
        $want_help = "yes";
    } elsif (/^disable-(.*)$/i) {
        appargs($_);
        myenable($1, "no");
    } elsif (/^enable-(.*)$/i) {
        appargs($_);
        myenable($1, "yes");
    } elsif (/^without-(.*)$/i) {
        appargs($_);
        mywith($1, "no");
    } elsif (/^with-(.*)=(.*)$/i) {
        appargs($_);
        mywith($1, $2);
    } elsif (/^with-(.*)$/i) {
        appargs($_);
        mywith($1, "yes");
    } elsif (/^legacy$/i) {
        $legacy_only = 1;
    } elsif (/^win32$/i) {
        $want_win32 = "yes";
    } elsif (/^x64$/i) {
        appargs($_);
        $want_x64 = "yes";
    } elsif (/^clean$/i) {
        $want_clean = "yes";
    } elsif (/^checkfiles$/i) {
        $want_checkfiles = "yes";
    } else {
        $want_unknown = "yes";
        $unknown_value = $_;
    }
}

# old legacy mode

if ($legacy_only) {
    print "legacy mode is obsolete (so ignored)\n";
}

if ($want_checkfiles eq "yes") {
        my $status=0;
        foreach (@filelist) {
                my $name = $_;
                $name =~ s/\\/\//g;
                next if -r $_ . ".in";
                s/\\/\//g;
                next if -r $_ . ".in";
                print "remove '$name' from filelist in win32utils/Configure or add to repository\n";
                $status = 1;
        }
        foreach (@projectlist) {
                my $name = $_;
                $name =~ s/\\/\//g;
                next if -r $_ . ".in";
                s/\\/\//g;
                next if -r $_ . ".in";
                print "remove '$name' from projectlist in win32utils/Configure or add to repository\n";
                $status = 1;
        }
        exit($status);
}

# configure the platform

if (($want_win32 eq "yes") && ($want_x64 eq "yes")) {
    die "can't ask for both Win32 and x64 platforms\n";
} elsif ($want_win32 eq "yes") {
    $configvar{"PLATFORM"} = "Win32";
    $configvar{"BUILD_PLATFORM"} = "Win32";
    $configvar{"MACHINE"} = "/machine:X86";
    $configvar{"BUILD_MACHINE"} = "/machine:X86";
} elsif ($want_x64 eq "yes") {
    $configvar{"PLATFORM"} = "x64";
    $configvar{"BUILD_PLATFORM"} = "x64";
    $configvar{"MACHINE"} = "/machine:X64";
    $configvar{"BUILD_MACHINE"} = "/machine:X64";
}
# Standard configure variable
$configvar{"EXEEXT"} = ".exe";

$configtest{"builddir"} = kw(".");
$configtest{"srcdir"} = kw(".");
$configtest{"abs_top_builddir"} = kw($abs_top_builddir);
$configtest{"abs_top_builddir"} =~ s/\\/\//g;
$configtest{"abs_top_srcdir"} = kw($abs_top_srcdir);
$configtest{"abs_top_srcdir"} =~ s/\\/\//g;

# Mimic AC_INIT() from autoconf by loading values from configure.ac

sub ac_init {
    my $package_name;
    my $package_bugreport;
    my $package_url;
    my $package_version;
    my $package_version_major;
    my $package_version_minor;
    my $package_version_patch;
    my $package_version_extra;
    my $package_description;
    my $package_srcid;
    my $mapapi;

    open V, "../configure.ac" || die $!;
    while (<V>) {
        chomp;
        if (/^AC_INIT\(\[(.*?)\],\s*bind_PKG_VERSION,\s*\[(.*?)\],\s*\[(.*?)\],\s*\[(.*?)\]\)$/) {
	    $package_name = $1;
	    $package_bugreport = $2;
	    $package_url = $4;
        } elsif (/m4_define\(\[bind_VERSION_MAJOR\],\s*(.*?)\)/) {
	    $package_version_major = $1;
        } elsif (/m4_define\(\[bind_VERSION_MINOR\],\s*(.*?)\)/) {
	    $package_version_minor = $1;
        } elsif (/m4_define\(\[bind_VERSION_PATCH\],\s*(.*?)\)/) {
	    $package_version_patch = $1;
        } elsif (/m4_define\(\[bind_VERSION_EXTRA\],\s*(.*?)\)/) {
	    $package_version_extra = $1;
        } elsif (/m4_define\(\[bind_DESCRIPTION\],\s*\[(.*?)\]\)/) {
	    $package_description = $1;
        } elsif (/AC_DEFINE\(\[MAPAPI\],\s*\[(.*?)\],\s*\[.*?\]\)/) {
	    $mapapi = $1;
        }
    }
    close V;

    $package_version = "${package_version_major}.${package_version_minor}.${package_version_patch}${package_version_extra}";

    my $srcid_fn = "../srcid";

    if (-f $srcid_fn) {
        open(my $fh, '<', $srcid_fn) or die "cannot open file $srcid_fn";
        {
            local $/;
            $package_srcid = <$fh>;
        }
	close($fh);
    } elsif (-d "../.git") {
        $package_srcid = substr(`git rev-list --max-count=1 HEAD`, 0, 7);
    } else {
        $package_srcid = "";
    }
    chomp($package_srcid);

    if (length($package_srcid) == 0) {
        $package_srcid = "unset_id";
    }
    if ($verbose) {
        print "BIND SRCID: $package_srcid\n";
    }

    # Now define those in config.h

    $configdefh{'PACKAGE_NAME'} = "\"$package_name\"";
    $configdefh{'PACKAGE_BUGREPORT'} = "\"$package_bugreport\"";
    $configdefh{'PACKAGE_URL'} = "\"$package_url\"";

    $configdefh{'PACKAGE_VERSION_MAJOR'} = "\"$package_version_major\"";
    $configdefh{'PACKAGE_VERSION_MINOR'} = "\"$package_version_minor\"";
    $configdefh{'PACKAGE_VERSION_PATCH'} = "\"$package_version_patch\"";
    $configdefh{'PACKAGE_VERSION_EXTRA'} = "\"$package_version_extra\"";
    $configdefh{'PACKAGE_DESCRIPTION'} = "\" $package_description\"";
    $configdefh{'PACKAGE_SRCID'} = "\"$package_srcid\"";

    $configdefh{'PACKAGE_VERSION'} = "\"$package_version\"";
    $configdefh{'PACKAGE_STRING'} = "\"$package_name $package_version\"";
    $configdefh{'MAPAPI'} = $mapapi;
}

ac_init();

# append seen args to CONFIGARGS define

sub appargs {
    my $arg = $_[0];
    # escape backslashes and double quotes
    $arg =~ s/([\\"])/\\$1/g;
    $arg =~ s/([\s])/\\\\$1/g;
    if (defined($configdefh{"PACKAGE_CONFIGARGS"})) {
        $configdefh{"PACKAGE_CONFIGARGS"} .= " " . $arg;
    } else {
        $configdefh{"PACKAGE_CONFIGARGS"} = $arg;
    }
}

if (!$configdefh{"PACKAGE_CONFIGARGS"}) {
    # CONFIGARGS default is "default"
    $configdefh{"PACKAGE_CONFIGARGS"} = "\"default\"";
} else {
    my $val = $configdefh{"PACKAGE_CONFIGARGS"};
    $configdefh{"PACKAGE_CONFIGARGS"} = "\"'$val'\"";
}

# parse enable/disable

sub myenable {
    my $key = $_[0];
    my $val = $_[1];

    if ($key =~ /^intrinsics$/i) {
        if ($val =~ /^no$/i) {
            $enable_intrinsics = "no";
        }
    } elsif ($key =~ /^native-pkcs11$/i) {
        if ($val =~ /^yes$/i) {
            $enable_native_pkcs11 = "yes";
        }
    } elsif ($key =~ /^fixed-rrset$/i) {
        if ($val =~ /^yes$/i) {
            $enable_fixed_rrset = "yes";
        }
    } elsif ($key =~ /^developer$/i) {
        if ($val =~ /^yes$/i) {
            $enable_developer = "yes";
        }
    } elsif ($key =~ /^querytrace$/i) {
        if ($val =~ /^yes$/i) {
            $enable_querytrace = "yes";
        }
    } elsif ($key =~ /^auto-validation$/i) {
        if ($val =~ /^no$/i) {
            $validation_default = "yes";
        }
    } else {
        $want_unknown = "yes";
        if ($val eq "no") {
            $unknown_value = "disable-" . $key;
        } else {
            $unknown_value = "enable-". $key;
        }
    }
}

# enable-developer expansion now

if ($enable_developer eq "yes") {
    $configtest{"DEVELOPER_MODE"} = "yes";
    $configdefh{"ISC_LIST_CHECKINIT"} = 1;
    $enable_querytrace = "yes";
    # no atf on WIN32
    $enable_fixed_rrset = "yes";
    # TODO: dlz filesystem
    $use_tests = "yes";
    $use_stests = "yes";
}

# parse with/without

sub mywith {
    my $key = $_[0];
    my $val = $_[1];

    if ($key =~ /^tests$/i) {
        if ($val =~ /^yes$/i) {
            $use_tests = "yes";
        }
    } elsif ($key =~ /^extra-tests$/i) {
        if ($val =~ /^yes$/i) {
            $use_tests = "yes";
        }
    } elsif ($key =~ /^system-tests$/i) {
        if ($val =~ /^yes$/i) {
            $use_tests = "yes";
            $use_stests = "yes";
        }
    } elsif ($key =~ /^openssl$/i) {
        if ($val =~ /^no$/i) {
            die "OpenSSL support is now mandatory\n";
        } elsif ($val !~ /^yes$/i) {
            $use_openssl = "yes";
            $openssl_path = $val;
        }
    } elsif ($key =~ /^libuv$/i) {
        if ($val =~ /^no$/i) {
            die "libuv is required\n";
        } elsif ($val !~ /^yes$/i) {
            $use_libuv = "yes";
            $libuv_path = $val;
        }
    } elsif ($key =~ /^nghttp2$/i) {
        if ($val =~ /^no$/i) {
            die "nghttp2 is required\n";
        } elsif ($val !~ /^yes$/i) {
            $use_nghttp2 = "yes";
            $nghttp2_path = $val;
        }
    } elsif ($key =~ /^pkcs11$/i) {
        if ($val =~ /^yes$/i) {
            $use_pkcs11 = "yes";
        } elsif ($val !~ /^no$/i) {
            $use_pkcs11= "yes";
            $pkcs11_path = $val;
            $pkcs11_path =~ s/\.dll$//i;
        }
    } elsif ($key =~ /^aes$/i) {
        if ($val =~ /^no$/i) {
            $use_aes = "no";
        } elsif ($val =~ /^yes$/i) {
            $use_aes = "yes";
        }
    } elsif ($key =~ /^gssapi$/i) {
        if ($val !~ /^no$/i) {
            $use_gssapi = "yes";
            if ($val !~ /^yes$/i) {
                $gssapi_path = $val;
            }
        }
    } elsif ($key =~ /^libxml2$/i) {
        if ($val =~ /^no$/i) {
            $use_libxml2 = "no";
        } elsif ($val !~ /^yes$/i) {
            $use_libxml2 = "yes";
            $libxml2_path = $val;
        }
    } elsif ($key =~ /^geoip2$/i) {
        if ($val !~ /^no$/i) {
            $use_geoip2 = "yes";
            if ($val !~ /^yes$/i) {
                $geoip2_path = $val;
            } else {
                $geoip2_path = "../../GeoIP2";
            }
        }
    } elsif ($key =~ /^readline$/i) {
        if ($val !~ /^no$/i) {
            $use_readline = "yes";
            if ($val !~ /^yes$/i) {
                $readline_path = $val;
            }
        }
    } elsif ($key =~ /^idn$/i) {
        if ($val !~ /^no$/i) {
            $use_idn = "yes";
            if ($val !~ /^yes$/i) {
                $idn_path = $val;
            }
        }
    } elsif ($key =~ /^iconv$/i) {
        if ($val =~ /^no$/i) {
            $want_unknown = "yes";
            $unknown_value = "without-iconv doesn't make sense)";
        } elsif ($val !~ /^yes$/i) {
            $iconv_path = $val;
        }
    } elsif ($key =~ /^zlib$/i) {
        if ($val !~ /^no$/i) {
            $use_zlib = "yes";
            if ($val !~ /^yes$/i) {
                $zlib_path = $val;
            }
        }
    } elsif ($key =~ /^pssuspend$/i) {
        if ($val =~ /^no$/i) {
            $with_pssuspend = "no";
        } else {
            if ($val !~ /^yes$/i) {
                $pssuspend_command = $val;
            }
        }
    } elsif ($key =~ /^python$/i) {
        if ($val =~ /^no$/i) {
            $use_python = "no";
        } else {
            $use_python = "yes";
            if ($val !~ /^yes$/i) {
                $python_command = $val;
            }
        }
    } elsif ($key =~ /^vcredist$/i) {
        if ($val =~ /^no$/i) {
            $want_unknown = "yes";
            $unknown_value = "without-vcredist (vcredist is required)";
        } elsif ($val !~ /^yes$/i) {
            $vcredist_path = $val;
        }
    } elsif ($key =~ /^cross-compile$/i) {
        if ($val =~ /^yes$/i) {
            $cross_compile = "yes";
        }
    } elsif ($key =~ /^tuning$/i) {
        if ($val =~ /^small$/i) {
            $tuning = "small";
        }
    } elsif ($key =~ /^tools-version$/i) {
        $configvar{"TOOLS_VERSION"} = $val;
    } elsif ($key =~ /^platform-version$/i) {
        $configvar{"WINDOWS_TARGET_PLATFORM_VERSION"} = "<WindowsTargetPlatformVersion>$val</WindowsTargetPlatformVersion>";
    } elsif ($key =~ /^platform-toolset$/i) {
        $configvar{"PLATFORM_TOOLSET"} = "<PlatformToolset>$val</PlatformToolset>";
    } else {
        $want_unknown = "yes";
        if ($val eq "no") {
            $unknown_value = "without-" . $key;
        } else {
            $unknown_value = "with-" . $key;
        }
    }
}

if ($want_help ne "no") {
    foreach (@help) {
        print $_;
    }
    exit 1;
}

# clean up and exit if requested
if ($want_clean eq "yes") {
    my $file;

    unlink($configfile);
    unlink($platformfile);

    foreach $file (@confshlist) {
	unlink($file);
    }

    foreach $file (@filelist) {
        unlink($file);
    }

    foreach $file (@projectlist) {
        unlink($file);
    }

    exit 0;
}

if ($want_unknown ne "no") {
    print STDERR "can't parse $unknown_value\n";
    exit 1;
}

if ($verbose) {
    if ($want_win32 eq "yes") {
        print "configure for win32\n";
    }
    if ($want_x64 eq "yes") {
        print "configure for x64\n";
    }
    if ($cross_compile eq "yes") {
        print "cross compiling";
        if ($want_x64 eq "yes") {
            print ": build on win32 for x64 host\n";
        } elsif ($want_win32 eq "yes") {
            print ": build on x64 for win32 host\n";
        } else {
            print "\n";
        }
    }
    if ($enable_intrinsics eq "yes") {
        print "intrinsics: enabled\n";
    } else {
        print "intrinsics: disabled\n";
    }
    if ($enable_native_pkcs11 eq "yes") {
        print "native-pkcs11: enabled\n";
    } else {
        print "native-pkcs11: disabled\n";
    }
    print "openssl-hash: enabled\n";
    if ($enable_fixed_rrset eq "yes") {
        print "fixed-rrset: enabled\n";
    } else {
        print "fixed-rrset: disabled\n";
    }
    if ($enable_developer eq "yes") {
        print "developer: enabled\n";
    } else {
        print "developer: disabled\n";
    }
    if ($enable_querytrace eq "yes") {
        print "querytrace: enabled\n";
    } else {
        print "querytrace: disabled\n";
    }
    print "libuv-path: $libuv_path\n";
    print "nghttp2-path: $nghttp2_path\n";
    print "openssl-path: $openssl_path\n";
    if ($use_tests eq "yes") {
        print "tests: enabled\n";
    }
    if ($use_stests eq "yes") {
        print "system tests: enabled\n";
    }
    if ($use_pkcs11 eq "no") {
        print "pkcs11: disabled\n";
    } else {
        print "pkcs11-provider-path: $pkcs11_path\n";
    }
    if ($use_aes eq "no") {
        print "aes: disabled\n";
    } else {
        print "aes: enabled\n";
    }
    if ($use_gssapi eq "no") {
        print "gssapi: disabled\n";
    } else {
        print "gssapi-path: $gssapi_path\n";
    }
    if ($use_libxml2 eq "no") {
        print "libxml2: disabled\n";
    } else {
        print "libxml2-path: $libxml2_path\n";
    }
    if ($use_geoip2 eq "no") {
        print "geoip2: disabled\n";
    } else {
        print "geoip2-path: $geoip2_path\n";
    }
    if ($use_readline eq "no") {
        print "readline: disabled\n";
    } else {
        print "readline-path: $readline_path\n";
    }
    if ($use_idn eq "no") {
        print "idn: disabled\n";
    } else {
        print "idn-path: $idn_path\n";
        if ($iconv_path ne " --idn-- ") {
            print "iconv-path: $iconv_path\n";
        }
    }
    if ($use_zlib eq "no") {
        print "zlib: disabled\n";
    } else {
        print "zlib-path: $zlib_path\n";
    }
    if ($with_pssuspend eq "no") {
        print "pssuspend: disabled\n";
    } else {
        print "pssuspend-command: $pssuspend_command\n";
    }
    if ($use_python eq "no") {
        print "python: disabled\n";
    } else {
        print "python-command: $python_command\n";
    }
    print "vcredist-path: $vcredist_path\n";
}

# Check environment

# infer vcredist when not given
if ($vcredist_path eq " --infer-- ") {
    if ($verbose) {
        print "trying to infer vcredist path from build environment\n";
    }

    my @vcpaths = {};
    push(@vcpaths, $ENV{"VCRedistPath"}) if ($ENV{"VCRedistPath"} ne "");
    push(@vcpaths, File::Spec->catdir( File::Spec->curdir(), "..", ".." ));

    if ($ENV{"FrameworkSDKDir"} ne "" && $want_win32 eq "yes") {
        push(@vcpaths, File::Spec->catdir($ENV{"FrameworkSDKDir"},
                                           "BootStrapper", "Packages",
                                           "vcredist_x86"));
    } elsif ($ENV{"FrameworkSDKDir"} ne "" && $want_x64 eq "yes") {
        push(@vcpaths, File::Spec->catdir($ENV{"FrameworkSDKDir"},
                                           "BootStrapper", "Packages",
                                           "vcredist_x64"));
    }

    if ($ENV{"WindowsSDKDir"} ne "" && $want_win32 eq "yes") {
        push(@vcpaths, File::Spec->catdir($ENV{"WindowsSDKDir"},
                                           "BootStrapper", "Packages",
                                           "vcredist_x86"));
    } elsif ($ENV{"WindowsSDKDir"} ne "" && $want_x64 eq "yes") {
        push(@vcpaths, File::Spec->catdir($ENV{"WindowsSDKDir"},
                                           "BootStrapper", "Packages",
                                           "vcredist_x64"));
    }

    if ($ENV{"WindowsSDKDir_old"} ne "" && $want_win32 eq "yes") {
        push(@vcpaths, File::Spec->catdir($ENV{"WindowsSDKDir_old"},
                                           "BootStrapper", "Packages",
                                           "vcredist_x86"));
    } elsif ($ENV{"WindowsSDKDir_old"} ne "" && $want_x64 eq "yes") {
        push(@vcpaths, File::Spec->catdir($ENV{"WindowsSDKDir_old"},
                                           "BootStrapper", "Packages",
                                           "vcredist_x64"));
    }

    if ($ENV{"VCINSTALLDIR"}) {
        push(@vcpaths, File::Spec->catdir($ENV{"VCINSTALLDIR"},
                                           "redist", "1033"));
    }

    # 'VCToolsRedistDir' is available since Visual Studio 2017.
    if ($ENV{"VCToolsRedistDir"}) {
        push(@vcpaths, $ENV{"VCToolsRedistDir"});
    }

    my $rfile;
    if ($want_win32 eq "yes") {
        $rfile = "vcredist_x86.exe";
    } else {
        $rfile = "vcredist_x64.exe";
    }

    foreach (@vcpaths) {
        my $vp = File::Spec->catfile($_, $rfile);
        if (-f $vp) {
            $vcredist_path = $vp;
            last;
        }
    }

    if ($vcredist_path eq " --infer-- ") {
        die "with-vcredist is REQUIRED\n";
    }

    if ($verbose) {
        print "found vcredist at " . $vcredist_path . "\n";
    }
}

my $msc_ver = 0;

open F, ">mscver.c" || die $!;
print F << 'EOF';
#include <windows.h>
#include <stdio.h>

int
main(void)
{
        printf("%d", _MSC_VER);
        return(0);
}
EOF
close F;
my $compret = `cl /nologo /MD mscver.c`;
if (grep { -f and -x } "./mscver.exe") {
    $msc_ver = `./mscver.exe`;
} else {
    die "can't get _MSC_VER value: $compret\n";
}
if ($verbose) {
    print "_MSC_VER == $msc_ver\n";
}
if ($msc_ver < 1910) {
    print STDERR "too old version of C++ compiler/Visual Studio\n";
    exit 1;
}

$configdefh{'PACKAGE_BUILDER'} = "\"MSVC $msc_ver\"";

# gen single threaded for < VS 2005

if ($msc_ver < 1400) {
    $configvar{"COPTML"} = "/ML";
    $configvar{"COPTMLD"} = "/MLD";
}

# /GX deprecated in VS 2005

if ($msc_ver < 1400) {
    $configvar{"COPTX"} = "/GX";
} else {
    $configvar{"COPTX"} = "/EHsc";
}

# /YX for < VS 2005

if ($msc_ver < 1400) {
    $configvar{"COPTY"} = "/YX";
}

# backtrace for >= VS 2012

if ($msc_ver >= 1700) {
    $configdefh{"USE_BACKTRACE"} = 1;
}

# warn when cross compiling

if ($cross_compile eq "yes") {
    if ($want_x64 eq "yes") {
        $configvar{"BUILD_PLATFORM"} = "Win32";
        $configvar{"BUILD_MACHINE"} = "/machine:X86";
    }
    if ($want_win32 eq "yes") {
        $configvar{"BUILD_PLATFORM"} = "x64";
        $configvar{"BUILD_MACHINE"} = "/machine:X64";
    }
} elsif ($want_win32 eq "yes") {
    open F, ">cross.c" || die $!;
    print F << 'EOF';
#include <windows.h>
#include <stdio.h>

int
main(void)
{
#ifdef _WIN64
        fprintf(stderr, "compiling for x64 when win32 was asked?!\n");
#endif
        return(0);
}
EOF
    close F;
    my $compret = `cl /nologo /MD cross.c`;
    if (grep { -f and -x } "./cross.exe") {
        my $cross = `./cross.exe`;
        if ($cross) {
            print STDERR $cross;
        }
    } else {
        print STDERR "can't check cross compile: $compret\n";
    }
} else {
    open F, ">cross.c" || die $!;
    print F << 'EOF';
#include <windows.h>
#include <stdio.h>

int
main(void)
{
#ifndef _WIN64
        fprintf(stderr, "compiling in 32 bits when x64 was asked?!\n");
#endif
        return(0);
}
EOF
    close F;
    my $compret = `cl /nologo /MD cross.c`;
    if (grep { -f and -x } "./cross.exe") {
        my $cross = `./cross.exe`;
        if ($cross) {
            print STDERR $cross;
        }
    } else {
        print STDERR "can't check cross compile: $compret\n";
    }
}

# Process arguments

# enable-native-pkcs11
if ($enable_native_pkcs11 eq "yes") {
    $cryptolib = "pkcs11";
    if ($use_pkcs11 ne "yes") {
        if ($verbose) {
            print "native PKCS#11 support: force with-pkcs11\n";
        }
        $use_pkcs11 = "yes";
    }
    if ($pkcs11_path eq "unknown") {
        if ($verbose) {
            print "native PKCS#11 support: no PKCS#11 provider defined?\n";
        }
    }
}

# enable-fixed-rrset
if ($enable_fixed_rrset eq "yes") {
    $configdefh{"DNS_RDATASET_FIXED"} = 1;
}

# enable-querytrace
if ($enable_querytrace eq "yes") {
    $configdefh{"WANT_QUERYTRACE"} = 1;
}

# with-tests
if ($use_tests eq "yes") {
    $configcond{"TESTS"} = 1;
}

# with-system-tests
if ($use_stests eq "yes") {
    $configcond{"STESTS"} = 1;
}

# with-libuv
if ($use_libuv eq "auto") {
    if ($verbose) {
        print "checking for an libuv built directory at sibling root\n";
    }
    opendir DIR, $libuv_path || die "No Directory: $!\n";
    my @dirlist = grep (/^libuv-v[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+){0,1}$/i,
                        readdir(DIR));
    closedir(DIR);

    # Make sure we have something
    if (scalar(@dirlist) == 0) {
        die "can't find an libuv at sibling root\n";
    }
    # Now see if we have a directory or just a file.
    # Make sure we are case insensitive
    my $file;
    foreach $file (sort {uc($b) cmp uc($a)} @dirlist) {
        if (-f File::Spec->catfile($libuv_path,
                                   $file,
                                   "include", "uv.h")) {
            $libuv_path = File::Spec->catdir($libuv_path, $file);
            $use_libuv = "yes";
            last;
        }
    }

    # If we have one use it otherwise report the error
    if ($use_libuv eq "auto") {
        die "can't find an libuv built directory at sibling root\n";
    }

    # When a libuv version exposing uv_import() and uv_export() is released, the
    # following three config.h macros will need to be conditionally defined for
    # that libuv version and all later ones.
    # $configdefh{"HAVE_UV_HANDLE_SET_DATA"} = 1;
    # $configdefh{"HAVE_UV_HANDLE_GET_DATA"} = 1;
    # $configdefh{"HAVE_UV_IMPORT"} = 1;
}
# falls into (so no else)
if ($use_libuv eq "yes") {
    $libuv_path = File::Spec->rel2abs($libuv_path);
    if ($verbose) {
        print "checking for libuv built directory at \"$libuv_path\"\n";
    }
    my $libuv_new = 0;
    if (!-f File::Spec->catfile($libuv_path,
                                "include", "uv.h")) {
        die "can't find libuv uv.h include\n";
    }
    my $libuv_inc = File::Spec->catdir($libuv_path, "include");
    my $libuv_libdir = File::Spec->catdir($libuv_path, "build\\Release");
    my $libuv_lib = File::Spec->catfile($libuv_libdir, "uv.lib");
    my $libuv_dll = File::Spec->catfile($libuv_libdir, "uv.dll");
    if (!-f $libuv_lib) {
       die "can't find uv.lib library\n";
    }
    if (!-f $libuv_dll) {
       die "can't find uv.dll library\n";
    }
    $configvar{"LIBUV_PATH"} = "$libuv_path";
    $configinc{"LIBUV_INC"} = "$libuv_inc";
    $configlib{"LIBUV_LIB"} = "$libuv_lib";
    $configdll{"LIBUV_DLL"} = "$libuv_dll";
    # When a libuv version exposing uv_import() and uv_export() is released, the
    # following three config.h macros will need to be conditionally defined for
    # that libuv version and all later ones.
    # $configdefh{"HAVE_UV_HANDLE_SET_DATA"} = 1;
    # $configdefh{"HAVE_UV_HANDLE_GET_DATA"} = 1;
    # $configdefh{"HAVE_UV_IMPORT"} = 1;
}

# with-nghttp2
if ($use_nghttp2 eq "auto") {
    if ($verbose) {
        print "checking for an nghttp2 built directory at sibling root\n";
    }
    opendir DIR, $nghttp2_path || die "No Directory: $!\n";
    my @dirlist = grep (/^nghttp2-[0-9]+\.[0-9]+\.[0-9]+$/i, readdir(DIR));
    closedir(DIR);

    # Make sure we have something
    if (scalar(@dirlist) == 0) {
        die "can't find an nghttp2 at sibling root\n";
    }
    # Now see if we have a directory or just a file.
    # Make sure we are case insensitive
    my $file;
    foreach $file (sort {uc($b) cmp uc($a)} @dirlist) {
        if (-f File::Spec->catfile($nghttp2_path,
                                   $file,
                                   "include", "nghttp2", "nghttp2.h")) {
            $nghttp2_path = File::Spec->catdir($nghttp2_path, $file);
            $use_nghttp2 = "yes";
            last;
        }
    }

    # If we have one use it otherwise report the error
    if ($use_nghttp2 eq "auto") {
        die "can't find an nghttp2 built directory at sibling root\n";
    }
}

if ($use_nghttp2 eq "yes") {
    $nghttp2_path = File::Spec->rel2abs($nghttp2_path);
    if ($verbose) {
        print "checking for nghttp2 directory at \"$nghttp2_path\"\n";
    }
    if (!-f File::Spec->catfile($nghttp2_path,
                                "include", "nghttp2", "nghttp2.h")) {
        die "can't find nghttp2 nghttp2.h include\n";
    }
    my $nghttp2_inc = File::Spec->catdir($nghttp2_path, "include");
    my $nghttp2_bindir = File::Spec->catdir($nghttp2_path, "bin");
    my $nghttp2_libdir = File::Spec->catdir($nghttp2_path, "lib");
    my $nghttp2_dll = File::Spec->catfile($nghttp2_bindir, "nghttp2.dll");
    my $nghttp2_lib = File::Spec->catfile($nghttp2_libdir, "nghttp2.lib");
    if (!-f $nghttp2_lib) {
       die "can't find nghttp2.lib library\n";
    }
    if (!-f $nghttp2_dll) {
       die "can't find nghttp2.dll library\n";
    }
    $configinc{"NGHTTP2_INC"} = "$nghttp2_inc";
    $configlib{"NGHTTP2_LIB"} = "$nghttp2_lib";
    $configdll{"NGHTTP2_DLL"} = "$nghttp2_dll";
}

# with-openssl
if ($use_openssl eq "auto") {
    if ($verbose) {
        print "checking for an OpenSSL built directory at sibling root\n";
    }
    opendir DIR, $openssl_path || die "No Directory: $!\n";
    my @dirlist = grep (/^openssl-[0-9]+\.[0-9]+\.[0-9]+[a-z]{0,1}$/i,
                        readdir(DIR));
    closedir(DIR);

    # Make sure we have something
    if (scalar(@dirlist) == 0) {
        die "can't find an OpenSSL at sibling root\n";
    }
    # Now see if we have a directory or just a file.
    # Make sure we are case insensitive
    my $file;
    foreach $file (sort {uc($b) cmp uc($a)} @dirlist) {
        if (-f File::Spec->catfile($openssl_path,
                                   $file,
                                   "inc32/openssl/opensslv.h")) {
            $openssl_path = File::Spec->catdir($openssl_path, $file);
            $use_openssl = "yes";
            last;
        }
        if (-f File::Spec->catfile($openssl_path,
                                   $file,
                                   "include/openssl/opensslv.h")) {
            $openssl_path = File::Spec->catdir($openssl_path, $file);
            $use_openssl = "yes";
            last;
        }
    }

    # If we have one use it otherwise report the error
    if ($use_openssl eq "auto") {
        die "can't find an OpenSSL built directory at sibling root\n";
    }
}
# falls into (so no else)
if ($use_openssl eq "yes") {
    my @dirlist;
    $openssl_path = File::Spec->rel2abs($openssl_path);
    if ($verbose) {
        print "checking for OpenSSL built directory at \"$openssl_path\"\n";
    }
    if (!-f File::Spec->catfile($openssl_path,
                                "include/openssl/opensslv.h")) {
	die "can't find OpenSSL 1.1 opensslv.h include\n";
    }
    my $openssl_inc = File::Spec->catdir($openssl_path, "include");
    my $openssl_libdir = $openssl_path;
    my $openssl_libcrypto = File::Spec->catfile($openssl_path, "libcrypto.lib");
    my $openssl_libssl = File::Spec->catfile($openssl_libdir, "libssl.lib");
    my $openssl_dllcrypto = File::Spec->catfile($openssl_libdir, "libcrypto.dll");
    my $openssl_dllssl = File::Spec->catfile($openssl_libdir, "libssl.dll");

    if (!-f $openssl_libcrypto) {
	die "can't find OpenSSL libcrypto.lib library\n";
    }
    opendir DIR, $openssl_path || die "No Directory: $!\n";
    @dirlist = grep (/^libcrypto-[^.]+\.dll$/i, readdir(DIR));
    closedir(DIR);
    # We must get one file only
    if (scalar(@dirlist) == 0) {
	die "can't find OpenSSL libcrypto-*.dll DLL\n";
    }
    if (scalar(@dirlist) != 1) {
	die "found more than one OpenSSL libcrypto-*.dll DLL candidate\n";
    }
    $openssl_dllcrypto = File::Spec->catdir($openssl_path, "$dirlist[0]");

    if (!-f $openssl_libssl) {
	die "can't find OpenSSL libssl.lib library\n";
    }
    opendir DIR, $openssl_path || die "No Directory: $!\n";
    @dirlist = grep (/^libssl-[^.]+\.dll$/i, readdir(DIR));
    closedir(DIR);
    # We must get one file only
    if (scalar(@dirlist) == 0) {
	die "can't find OpenSSL libssl-*.dll DLL\n";
    }
    if (scalar(@dirlist) != 1) {
	die "found more than one OpenSSL libssl-*.dll DLL candidate\n";
    }
    $openssl_dllssl = File::Spec->catdir($openssl_path, "$dirlist[0]");

    $cryptolib = "openssl";
    $configvar{"OPENSSL_PATH"} = "$openssl_path";
    $configinc{"OPENSSL_INC"} = "$openssl_inc";
    $configlib{"OPENSSL_LIBCRYPTO"} = "$openssl_libcrypto";
    $configdll{"OPENSSL_DLLCRYPTO"} = "$openssl_dllcrypto";
    $configlib{"OPENSSL_LIBSSL"} = "$openssl_libssl";
    $configdll{"OPENSSL_DLLSSL"} = "$openssl_dllssl";
}

if ($cryptolib eq "openssl") {
    $configdefh{"USE_OPENSSL"} = 1;
    $configtest{"CRYPTO"} = "OpenSSL";
} else {
    $configdefh{"USE_PKCS11"} = 1;
    $configtest{"CRYPTO"} = "pkcs11";
}

# check OpenSSL
if ($use_openssl eq "yes") {
#prepare the environment
    my $dll = $configdll{"OPENSSL_DLLCRYPTO"};
    my $ret = `copy "$dll" .`;
    if ($? != 0) {
        die "Can't copy OpenSSL DLL to working directory: $ret\n";
    }

    $dll = $configdll{"OPENSSL_DLLSSL"};
    $ret = `copy "$dll" .`;
    if ($? != 0) {
        die "Can't copy OpenSSL DLL to working directory: $ret\n";
    }

    my $include = $configinc{"OPENSSL_INC"};
    my $libcrypto = $configlib{"OPENSSL_LIBCRYPTO"};
    my $libssl = $configlib{"OPENSSL_LIBSSL"};

# check libcrypto
    if ($verbose) {
        print "checking whether linking with OpenSSL libcrypto works\n";
    }

    open F, ">testossl.c" || die $!;
    print F << 'EOF';
#include <openssl/err.h>

int
main(void)
{
        ERR_clear_error();
        return(0);
}
EOF
    close F;

    $compret = `cl /nologo /MD /I "$include" testossl.c "$libcrypto"`;
    if (grep { -f and -x } "./testossl.exe") {
        `./testossl.exe`;
        if ($? != 0) {
            die "OpenSSL libcrypto test failed\n";
        }
    } else {
        die "can't compile OpenSSL libcrypto test: $compret\n";
    }

    if ($verbose) {
	print "checking whether linking with OpenSSL libssl works\n";
    }

    open F, ">testossl.c" || die $!;
    print F << 'EOF';
#include <openssl/ssl.h>

int
main(void)
{
        SSL_CTX *ctx = SSL_CTX_new(SSLv23_method());
        SSL_CTX_free(ctx);
        return(0);
}
EOF
    close F;
    $compret = `cl /nologo /MD /I "$include" testossl.c "$libcrypto" "$libssl"`;
    if (grep { -f and -x } "./testossl.exe") {
        `./testossl.exe`;
        if ($? != 0) {
            die "OpenSSL libssl test failed\n";
        }
    } else {
        die "can't compile OpenSSL libssl test: $compret\n";
    }

# check OpenSSL version
    if ($verbose) {
        printf "checking OpenSSL library version\n";
    }
    open F, ">testosslv.c" || die $!;
    print F << 'EOF';
#include <stdio.h>
#include <openssl/opensslv.h>

int main() {
        if (OPENSSL_VERSION_NUMBER >= 0x10101000L) {
                return (0);
        }
        printf("\n\nFound   OPENSSL_VERSION_NUMBER %#010x\n",
               OPENSSL_VERSION_NUMBER);
        printf("Require OPENSSL_VERSION_NUMBER 0x10101000L or greater (1.1.1)\n\n");
        return (1);
}
EOF
    close F;

    $compret = `cl /nologo /MD /I "$include" testosslv.c "$libcrypto"`;
    if (grep { -f and -x } "./testosslv.exe") {
        `./testosslv.exe`;
        if ($? != 0) {
            die "OpenSSL version test failed\n";
        }
    } else {
        die "can't compile OpenSSL version test: $compret\n";
    }

    if ($verbose) {
        print "checking for OpenSSL Ed25519 support\n";
    }
    open F, ">tested25519.c" || die $!;
    print F << 'EOF';
#include <openssl/evp.h>
#include <openssl/objects.h>

int
main(void)
{
        EVP_PKEY_CTX *ctx;

        ctx = EVP_PKEY_CTX_new_id(NID_ED25519, NULL);
        if (ctx == NULL)
                return (2);
        return (0);
}
EOF
    close F;

    $compret = `cl /nologo /MD /I "$include" tested25519.c "$libcrypto"`;
    if (grep { -f and -x } "./tested25519.exe") {
        `./tested25519.exe`;
        if ($? == 0) {
            $configdefh{"HAVE_OPENSSL_ED25519"} = 1;
        } else {
            if ($verbose) {
                print "Ed25519 test failed: disabling Ed25519\n";
            }
        }
    } else {
        if ($verbose) {
            print "can't compile Ed25519 test: $compret\n";
            print "disabling Ed25519\n";
        }
    }

    if ($verbose) {
        print "checking for OpenSSL Ed448 support\n";
    }
    open F, ">tested448.c" || die $!;
    print F << 'EOF';
#include <openssl/evp.h>
#include <openssl/objects.h>

int
main(void)
{
        EVP_PKEY_CTX *ctx;

        ctx = EVP_PKEY_CTX_new_id(NID_ED448, NULL);
        if (ctx == NULL)
                return (2);
        return (0);
}
EOF
    close F;

    $compret = `cl /nologo /MD /I "$include" tested448.c "$libcrypto"`;
    if (grep { -f and -x } "./tested448.exe") {
        `./tested448.exe`;
        if ($? == 0) {
            $configdefh{"HAVE_OPENSSL_ED448"} = 1;
        } else {
            if ($verbose) {
                print "Ed448 test failed: disabling Ed448\n";
            }
        }
    } else {
        if ($verbose) {
            print "can't compile Ed448 test: $compret\n";
            print "disabling Ed448\n";
        }
    }
}

# with-aes
if ($use_openssl eq "no") {
    if ($use_aes ne "pkcs11") {
        $use_aes = "no";
    }
}

if ($cryptolib ne "") {
    print "Cryptographic library for DNSSEC: $cryptolib\n";
} else {
    die "No cryptography library has been found or provided."
}

# with-pkcs11
if ($use_pkcs11 ne "no") {
    $configcond{"PKCS11"} = 1;
    $configdefd{"PK11_LIB_LOCATION"} = "PK11_LIB_LOCATION=\"$pkcs11_path\"";
}

# with-gssapi
if ($use_gssapi eq "no") {
    if ($verbose) {
        print "gssapi library is disabled\n";
    }
} else {
    $gssapi_path = File::Spec->rel2abs($gssapi_path);
    if ($verbose) {
        print "checking for gssapi directory at \"$gssapi_path\"\n";
    }
    $configcond{"GSSAPI"} = 1;
    $configdefh{"HAVE_GSSAPI"} = 1;
    if (-f File::Spec->catfile($gssapi_path, "include",
                                "gssapi", "gssapi.h")) {
        $configdefh{"HAVE_GSSAPI_GSSAPI_H"} = 1;
    } elsif (-f File::Spec->catfile($gssapi_path, "include",
                                "gssapi", "gssapi.h")) {
        $configdefh{"HAVE_GSSAPI_H"} = 1;
    } else {
        die "can't find gssapi.h include\n";
    }
    if (-f File::Spec->catfile($gssapi_path, "include",
                                "gssapi", "gssapi_krb5.h")) {
        $configdefh{"HAVE_GSSAPI_GSSAPI_KRB5_H"} = 1;
    } elsif (-f File::Spec->catfile($gssapi_path, "include",
                                      "gssapi_krb5.h")) {
        $configdefh{"HAVE_GSSAPI_KRB5_H"} = 1;
    } else {
        die "can't find gssapi_krb5.h include\n";
    }
    if (-f File::Spec->catfile($gssapi_path, "include",
                                "krb5", "krb5.h")) {
        $configdefh{"HAVE_KRB5_KRB5_H"} = 1;
    } elsif (-f File::Spec->catfile($gssapi_path, "include",
                                "krb5", "krb5.h")) {
        $configdefh{"HAVE_KRB5_H"} = 1;
    } else {
        die "can't find krb5.h include\n";
    }
    $configinc{"GSSAPI_INC"} = File::Spec->catdir($gssapi_path, "include");
    my $bits = "32";
    my $gssapi_lib;
    my $krb5_lib;
    if ($want_win32 eq "yes") {
        $bits = "32";
        if (!-f File::Spec->catfile($gssapi_path, "lib", "i386",
                                    "gssapi${bits}.lib")) {
            die "can't find gssapi${bits}.lib library\n";
        }
        $gssapi_lib = File::Spec->catfile($gssapi_path, "lib", "i386",
                                          "gssapi${bits}.lib");
        if (!-f File::Spec->catfile($gssapi_path, "lib", "i386",
                                    "krb5_${bits}.lib")) {
            die "can't find krb5_${bits}.lib library\n";
        }
        $krb5_lib = File::Spec->catfile($gssapi_path, "lib", "i386",
                                             "krb5_${bits}.lib");
    } elsif ($want_x64 eq "yes") {
        $bits = "64";
        if (!-f File::Spec->catfile($gssapi_path, "lib", "amd64",
                                    "gssapi${bits}.lib")) {
            die "can't find gssapi${bits}.lib library\n";
        }
        $gssapi_lib = File::Spec->catfile($gssapi_path, "lib", "amd64",
                                             "gssapi${bits}.lib");
        if (!-f File::Spec->catfile($gssapi_path, "lib", "amd64",
                                    "krb5_${bits}.lib")) {
            die "can't find krb5_${bits}.lib library\n";
        }
        $krb5_lib = File::Spec->catfile($gssapi_path, "lib", "amd64",
                                        "krb5_${bits}.lib");
    } else {
        die "can't happen: no choice between Win32 and x64\n";
    }
    if (!-f File::Spec->catfile($gssapi_path, "bin", "gssapi${bits}.dll")) {
        die "can't find gssapi${bits}.dll DLL\n";
    }
    if (!-f File::Spec->catfile($gssapi_path, "bin", "krb5_${bits}.dll")) {
        die "can't find krb5_${bits}.dll DLL\n";
    }
    if (!-f File::Spec->catfile($gssapi_path, "bin", "comerr${bits}.dll")) {
        die "can't find comerr${bits}.dll DLL\n";
    }
    if (!-f File::Spec->catfile($gssapi_path, "bin", "k5sprt${bits}.dll")) {
        die "can't find k5sprt${bits}.dll DLL\n";
    }
    if (!-f File::Spec->catfile($gssapi_path, "bin", "wshelp${bits}.dll")) {
        die "can't find wshelp${bits}.dll DLL\n";
    }
    $configlib{"GSSAPI_LIB"} = "$gssapi_lib";
    $configlib{"KRB5_LIB"} = "$krb5_lib";
    my $gssapi_dll = File::Spec->catfile($gssapi_path, "bin",
                                         "gssapi${bits}.dll");
    $configdll{"GSSAPI_DLL"} = "$gssapi_dll";
    my $krb5_dll = File::Spec->catfile($gssapi_path, "bin",
                                         "krb5_${bits}.dll");
    $configdll{"KRB5_DLL"} = "$krb5_dll";
    my $comerr_dll = File::Spec->catfile($gssapi_path, "bin",
                                         "comerr${bits}.dll");
    $configdll{"COMERR_DLL"} = "$comerr_dll";
    my $k5sprt_dll = File::Spec->catfile($gssapi_path, "bin",
                                         "k5sprt${bits}.dll");
    $configdll{"K5SPRT_DLL"} = "$k5sprt_dll";
    my $wshelp_dll = File::Spec->catfile($gssapi_path, "bin",
                                         "wshelp${bits}.dll");
    $configdll{"WSHELP_DLL"} = "$wshelp_dll";
}

# disable-auto-validation
$configdefh{"VALIDATION_DEFAULT"} = "\"$validation_default\"";

# with-geoip2
if ($use_geoip2 eq "no") {
    if ($verbose) {
        print "geoip2 library is disabled\n";
    }
} else {
    $configcond{"GEOIP"} = 1;
    $geoip2_path = File::Spec->rel2abs($geoip2_path);
    if ($verbose) {
        print "checking for geoip2 directory at \"$geoip2_path\"\n";
    }
    if (!-f File::Spec->catfile($geoip2_path, "maxminddb.h")) {
        die "can't find maxminddb.h include\n";
    }
    if (!-f File::Spec->catfile($geoip2_path, "maxminddb_config.h")) {
        die "can't find maxminddb_config.h include\n";
    }
    if (!-f File::Spec->catfile($geoip2_path, "libmaxminddb.lib")) {
        die "can't find libmaxminddb.lib library\n";
    }
    $configinc{"GEOIP_INC"} = "$geoip2_path";
    my $geoip2_lib = File::Spec->catfile($geoip2_path, "libmaxminddb.lib");
    $configlib{"GEOIP_LIB"} = "$geoip2_lib";
    my $geoip_inc = qq(/I "$geoip2_path");
    my $geoip2_libs = qq("$geoip2_lib" Ws2_32.Lib);

    if ($verbose) {
        print "checking for GeoIP2 support\n";
    }
    open F, ">testgeoip2.c" || die $!;
    print F << 'EOF';
#include <maxminddb.h>
int main(void) {
       return MMDB_lib_version() != 0;
}
EOF
    close F;
    $compret = `cl /nologo $geoip_inc /MD testgeoip2.c $geoip2_libs`;
    if (grep { -f and -x } "./testgeoip2.exe") {
        `./testgeoip2.exe`;
        if ($? == 0) {
            die "GeoIP2 test failed\n";
        }
    } else {
        die "can't compile GeoIP2 test: $compret\n";
    }
    $configdefh{"HAVE_GEOIP2"} = 1;
}

# with-readline
if ($use_readline eq "no") {
    if ($verbose) {
        print "readline library is disabled\n";
    }
} else {
    $readline_path = File::Spec->rel2abs($readline_path);
    if ($verbose) {
        print "checking for readline directory at \"$readline_path\"\n";
    }
    if (!-f File::Spec->catfile($readline_path, "readline", "readline.h")) {
        die "can't find readline.h include\n";
    }
    if (!-f File::Spec->catfile($readline_path, "readline", "readline.lib")) {
        die "can't find readline.lib library\n";
    }
    $configdefh{"HAVE_READLINE"} = 1;
    $configinc{"READLINE_INC"} = "$readline_path";
    my $readline_lib = File::Spec->catfile($readline_path,
                                           "readline", "readline.lib");
    $configlib{"READLINE_LIB"} = "$readline_lib";
    if (-f File::Spec->catfile($readline_path, "readline", "readlineD.lib")) {
        my $readline_libd = File::Spec->catfile($readline_path,
                                                "readline", "readlineD.lib");
        $configlib{"READLINE_LIBD"} = "$readline_libd";
    } else {
        $configlib{"READLINE_LIBD"} = "$readline_lib";
    }
}

# with-idn (including with-iconv)
if ($use_idn eq "no") {
    if ($verbose) {
        print "IDN kit is disabled\n";
    }
} else {
    $idn_path = File::Spec->rel2abs($idn_path);
    if ($verbose) {
        print "checking for IDN kit directory at \"$idn_path\"\n";
    }
    if (!-f File::Spec->catfile($idn_path, "idn", "api.h")) {
        die "can't find idn/api.h include\n";
    }
    if (!-f File::Spec->catfile($idn_path, "idn", "idnkit.lib")) {
        die "can't find idnkit.lib library\n";
    }
    if (!-f File::Spec->catfile($idn_path, "idn", "idnkit.dll")) {
        die "can't find idnkit.dll DLL\n";
    }
    $configcond{"IDNKIT"} = 1;
    $configdefh{"WITH_IDN"} = 1;
    $configinc{"IDN_INC"} = "$idn_path";
    my $idn_lib = File::Spec->catfile($idn_path, "idn", "idnkit.lib");
    $configlib{"IDN_LIB"} = "$idn_lib";
    my $idn_dll = File::Spec->catfile($idn_path, "idn", "idnkit.dll");
    $configdll{"IDN_DLL"} = "$idn_dll";
    if ($iconv_path eq " --idn-- ") {
        my $iconv_dll = File::Spec->catfile($idn_path, "idn", "iconv.dll");
        $configdll{"ICONV_DLL"} = "$iconv_dll";
    } else {
        my $iconv_dll = File::Spec->catfile($iconv_path, "iconv.dll");
        $configdll{"ICONV_DLL"} = "$iconv_dll";
    }
}

# with-libxml2
if ($use_libxml2 eq "no") {
    if ($verbose) {
        print "libxml2 library is disabled\n";
    }
} elsif ($use_libxml2 eq "auto") {
    if ($verbose) {
        print "checking for a libxml2 built directory at sibling root\n";
    }
    opendir DIR, $libxml2_path || die "No Directory: $!\n";
    my @dirlist = grep (/^libxml2-[0-9]+\.[0-9]+\.[0-9]+[a-z]*/i,
                        readdir(DIR));
    closedir(DIR);

    # Make sure we have something
    if (scalar(@dirlist) == 0) {
        die "can't find a libxml2 at sibling root\n";
    }
    # Now see if we have a directory or just a file.
    # Make sure we are case insensitive
    my $file;
    foreach $file (sort {uc($b) cmp uc($a)} @dirlist) {
        if (-f File::Spec->catfile($libxml2_path,
                                   $file,
                                   "include", "libxml",
                                   "xmlversion.h")) {
            $libxml2_path = File::Spec->catdir($libxml2_path, $file);
            $use_libxml2 = "yes";
            last;
        }
    }

    # If we have one use it otherwise report the error
    if ($use_libxml2 eq "auto") {
        die "can't find a libxml2 built directory at sibling root\n";
    }
}
# falls into (so no else)
if ($use_libxml2 eq "yes") {
    $libxml2_path = File::Spec->rel2abs($libxml2_path);
    if ($verbose) {
        print "checking for libxml2 built directory at \"$libxml2_path\"\n";
    }
    if (!-f File::Spec->catfile($libxml2_path,
                                "include" , "libxml",
                                "xmlversion.h")) {
        die "can't find libxml2 xmlversion.h include\n";
    }
    if (!-f File::Spec->catfile($libxml2_path,
                                "win32" , "bin.msvc",
                                "libxml2.lib")) {
        die "can't find Libxml2 libxml2.lib library\n";
    }
    if (!-f File::Spec->catfile($libxml2_path,
                                "win32", "bin.msvc",
                                "libxml2.dll")) {
        die "can't find Libxml2 DLL\n";
    }
    $configcond{"LIBXML2"} = 1;
    $configdefh{"HAVE_LIBXML2"} = 1;
    $configtest{"XMLSTATS"} = 1;
    my $libxml2_inc = File::Spec->catdir($libxml2_path, "include");
    $configinc{"LIBXML2_INC"} = "$libxml2_inc";
    my $libxml2_libdir = File::Spec->catdir($libxml2_path, "win32", "bin.msvc");
    my $libxml2_lib = File::Spec->catfile($libxml2_libdir, "libxml2.lib");
    $configlib{"LIBXML2_LIB"} = "$libxml2_lib";
    my $libxml2_dll = File::Spec->catfile($libxml2_libdir, "libxml2.dll");
    $configdll{"LIBXML2_DLL"} = "$libxml2_dll";
}

# with-zlib
if ($use_zlib eq "no") {
    if ($verbose) {
        print "zlib library is disabled\n";
    }
} else {
    $configcond{"ZLIB"} = 1;
    $configtest{"ZLIB"} = 1;
    $zlib_path = File::Spec->rel2abs($zlib_path);
    if ($verbose) {
        print "checking for zlib directory at \"$zlib_path\"\n";
    }
    if (!-f File::Spec->catfile($zlib_path, "zlib.h")) {
        die "can't find zlib.h include\n";
    }
    if (!-f File::Spec->catfile($zlib_path, "zdll.lib")) {
        die "can't find zdll.lib library\n";
    }
    if (!-f File::Spec->catfile($zlib_path, "zlib1.dll")) {
        die "can't find zlib1.dll DLL\n";
    }
    $configdefh{"HAVE_ZLIB"} = 1;
    $configinc{"ZLIB_INC"} = "$zlib_path";
    my $zlib_lib = File::Spec->catfile($zlib_path, "zdll.lib");
    $configlib{"ZLIB_LIB"} = "$zlib_lib";
    my $zlib_dll = File::Spec->catfile($zlib_path, "zlib1.dll");
    $configdll{"ZLIB_DLL"} = "$zlib_dll";
}

if ($with_pssuspend ne "no") {
    $configvar{"PSSUSPEND"} = "$pssuspend_command";
}

# with-python
if ($use_python eq "no") {
    if ($verbose) {
        print "python is disabled\n";
    }
} elsif ($use_python eq "auto") {
    if ($verbose) {
        print "checking for python in path\n";
    }
    my $pythonret = `python.exe -c "quit()" 2>&1`;
    if ($? != 0) {
        print STDERR "can't launch the python interpreter: $pythonret\n";
        $use_python = "no";
    }
}
if ($use_python ne "no") {
    if ($use_python ne "auto") {
        if ($verbose) {
           print "checking for $python_command\n";
        }
        my $pythonret = `"$python_command" -c "quit()" 2>&1`;
        if ($? != 0) {
            die "can't launch $python_command: $pythonret\n";
        }
    }
    if ($python_command !~ /\.exe$/i) {
        $python_command = $python_command . ".exe";
    }
    if ($use_python ne "no") {
        $configcond{"PYTHON"} = 1;
        $configdefd{"USE_PYTHON"} = "USE_PYTHON";
        $configvar{"PYTHON"} = "$python_command";
        # Doesn't matter
        $configvar{"prefix"} = "__prefix__";
    }
}

# with-vcredist
$vcredist_path = File::Spec->rel2abs($vcredist_path);
if (!grep { -f and -x } $vcredist_path) {
    die "$vcredist_path is not correct\n";
} else {
    $configvar{"VCREDIST_PATH"} = "$vcredist_path";
}

# tuning
if ($tuning ne "small") {
    $configdefh{"TUNE_LARGE"} = 1;
}

# escape spaces

sub kw {
    if ($_[0] =~ / /) {
        return "\"$_[0]\"";
    } else {
        return "$_[0]";
    }
}

# setup config.h with %configdefh

sub setupconfigh {
    my $line;
    my @Lines;

    open F, $configfile . ".win32" || die $!;
    @Lines = <F>;
    close F;

    foreach $line (@Lines) {
        chomp $line;
        if ($line =~ /^@([^@]+)\@$/) {
            if (defined($configdefh{$1})) {
                $line = "#define $1 $configdefh{$1}";
            } else {
                $line = "/* #undef $1 */";
            }
        }
    }

    open F, ">" . $configfile || die $!;
    if ($verbose) {
        print "Setting up $configfile\n";
    }
    foreach $line (@Lines) {
        print F $line . "\n";
    }
    close F;
}

# setup platform.h with %configdefp

sub setupplatformh {
    my $line;
    my @Lines;

    open F, $platformfile . ".in" || die $!;
    @Lines = <F>;
    close F;

    foreach $line (@Lines) {
        chomp $line;
        if ($line =~ /^@([^@]+)\@$/) {
            if (defined($configdefp{$1})) {
                $line = "#define $1 $configdefp{$1}";
            } else {
                $line = "/* #undef $1 */";
            }
        }
    }

    open F, ">" . $platformfile || die $!;
    if ($verbose) {
        print "Setting up $platformfile\n";
    }
    foreach $line (@Lines) {
        print F $line . "\n";
    }
    close F;
}

# setup conf.sh with %configtest and %configvar

sub setupconfsh {
    my $line;
    my @lines;
    my $val;
    my $confshvolume;
    my $confshdirectory;
    my $confshbasefile;

    foreach my $confshfile (@confshlist) {
	my $confshfilein;
	if (-f $confshfile . ".win32") {
	    $confshfilein = $confshfile . ".win32";
	} else {
	    $confshfilein = $confshfile . ".in";
	}
	open(my $fin, "<" . $confshfilein) || die $!;

	($confshvolume, $confshdirectory, $confshbasefile) = File::Spec->splitpath($confshfile);

	@lines = <$fin>;
	close($fin);

	foreach $line (@lines) {
	    chomp $line;
	    while ($line =~ /@([^@]+)\@/) {
		if ($1 ~~ @substtest) {
		    if (defined($configtest{$1})) {
			$val = kw($configtest{$1});
		    } elsif ($1 eq "abs_builddir") {
			$val = kw(File::Spec->rel2abs($confshdirectory, File::Spec->curdir()));
			$val =~ s/\\/\//g;
		    } elsif ($1 eq "abs_srcdir") {
			$val = kw(File::Spec->rel2abs($confshdirectory, File::Spec->curdir()));
			$val =~ s/\\/\//g;
		    } elsif ($1 eq "top_builddir") {
			$val = kw(File::Spec->abs2rel($top_builddir, $confshdirectory));
			$val =~ s/\\/\//g;
		    } elsif ($1 eq "top_srcdir") {
			$val = kw(File::Spec->abs2rel($top_srcdir, $confshdirectory));
			$val =~ s/\\/\//g;
		    } else {
			$val = "";
		    }
		} elsif ($1 ~~ @substvar) {
		    if (defined($configvar{$1})) {
			$val = kw($configvar{$1});
		    } else {
			$val = "";
		    }
		}  else {
		    die "unknown control $& in $confshfilein\n";
		}
		$line = "$`$val$'";
	    }
	}

	open(my $fout, ">" . $confshfile) || die $!;
	if ($verbose) {
	    print "Setting up $confshfile\n";
	}
	binmode($fout);
	foreach $line (@lines) {
	    print $fout $line . "\n";
	}
	close($fout);
    }
}

# setup a file with %configcond stack and %config{var,defd,inc,lib,dll,test}

sub setupfile {
    my $line;
    my @Linesin;
    my @Linesout;
    my $filename = $_[0];
    my $cond;
    my @conds;
    my $pass = 1;
    my @passes;
    my $val;

    open F, $filename . ".in" || die $!;
    @Linesin = <F>;
    close F;

    foreach $line (@Linesin) {
        chomp $line;
        if ($line =~ /^\@IF (.*)$/) {
            if (defined($cond)) {
                unshift(@conds, $cond);
                unshift(@passes, $pass);
            }
            if ($1 ~~ @allcond) {
                $cond = $1;
                if (defined($configcond{$cond})) {
                    # do nothing
                } else {
                    $pass = 0;
                }
            } else {
                die "unknown condition \@IF $1 in $filename\n";
            }
            next;
        } elsif ($line =~ /^\@ELSE (.*)$/) {
            if ($cond ne $1) {
                die "\@ELSE $1 mismatch in $filename\n";
            }
            if (defined($configcond{$cond})) {
                $pass = 0;
            } else {
                if (scalar(@conds) > 0) {
                    $pass = $passes[0];
                } else {
                    $pass = 1;
                }
            }
            next;
        } elsif ($line =~ /^\@END (.*)$/) {
            if ($cond ne $1) {
                die "\@END $1 mismatch in $filename\n";
            }
            $cond = shift(@conds);
            if (scalar(@passes) > 0) {
                $pass = shift(@passes);
            } else {
                $pass = 1;
            }
            next;
        }
        if ($pass == 0) {
            next;
        }
        while ($line =~ /@([^@ ]*)@/) {
            if ($1 ~~ @substvar) {
                if (defined($configvar{$1})) {
                    $val = kw($configvar{$1});
                    $line = "$`$val$'";
                } else {
                    $line = "$`$'";
                }
            } elsif ($1 ~~ @substdefd) {
                if (defined($configdefd{$1})) {
                    my $def = $configdefd{$1};
                    my $pre = "$`";
                    my $post = "$'";
                    $def =~ s/([\\ "])/\\$1/g;
                    $line = qq($pre/D "$def"$post);
                } else {
                    $line = "$`$'";
                }
            } elsif ($1 ~~ @substinc) {
                if (defined($configinc{$1})) {
                    $line = qq($`/I "$configinc{$1}"$');
                } else {
                    $line = "$`$'";
                }
            } elsif ($1 ~~ @substlib) {
                if (defined($configlib{$1})) {
                    $val = kw($configlib{$1});
                    $line = "$`$val$'";
                } else {
                    $line = "$`$'";
                }
            } elsif ($1 ~~ @substdll) {
                if (defined($configdll{$1})) {
                    $val = kw($configdll{$1});
                    $line = "$`$val$'";
                } else {
                    $line = "$`$'";
                }
            } else {
                die "unknown control $& in $filename\n";
            }
        }
        push @Linesout, $line;
    }

    open F, ">" . $filename || die $!;
    if ($verbose) {
        print "Setting up $filename\n";
    }
    binmode(F);
    foreach $line (@Linesout) {
        print F $line . "\n";
    }
    close F;
}

# setup a project with %configcond stack and %config{var,defd,inc,lib,dll}

sub setupproject {
    my $line;
    my @Linesin;
    my @Linesout;
    my $projectname = $_[0];
    my $cond;
    my @conds;
    my $pass = 1;
    my @passes;
    my $val;

    open F, $projectname . ".in" || die $!;
    @Linesin = <F>;
    close F;

    foreach $line (@Linesin) {
        chomp $line;
        if ($line =~ /^\@IF (.*)$/) {
            if (defined($cond)) {
                unshift(@conds, $cond);
                unshift(@passes, $pass);
            }
            if ($1 ~~ @allcond) {
                $cond = $1;
                if (defined($configcond{$cond})) {
                    # do nothing
                } else {
                    $pass = 0;
                }
            } else {
                die "unknown condition \@IF $1 in $projectname\n";
            }
            next;
        } elsif ($line =~ /^\@ELSE (.*)$/) {
            if ($cond ne $1) {
                die "\@ELSE $1 mismatch in $projectname\n";
            }
            if (defined($configcond{$cond})) {
                $pass = 0;
            } else {
                if (scalar(@conds) > 0) {
                    $pass = $passes[0];
                } else {
                    $pass = 1;
                }
            }
            next;
        } elsif ($line =~ /^\@END (.*)$/) {
            if ($cond ne $1) {
                die "\@END $1 mismatch in $projectname\n";
            }
            $cond = shift(@conds);
            if (scalar(@passes) > 0) {
                $pass = shift(@passes);
            } else {
                $pass = 1;
            }
            next;
        }
        if ($pass == 0) {
            next;
        }
        while ($line =~ /@([^@ ]*)@/) {
            if ($1 ~~ @substvar) {
                if (defined($configvar{$1})) {
                    $val = kw($configvar{$1});
                    $line = "$`$val$'";
                } else {
                    $line = "$`$'";
                }
            } elsif ($1 ~~ @substdefd) {
                if (defined($configdefd{$1})) {
                    $val = kw($configdefd{$1});
                    $line = "$`$val;$'";
                } else {
                    $line = "$`$'";
                }
            } elsif ($1 ~~ @substinc) {
                if (defined($configinc{$1})) {
                    $val = kw($configinc{$1});
                    $line = "$`$val;$'";
                } else {
                    $line = "$`$'";
                }
            } elsif ($1 ~~ @substlib) {
                if (defined($configlib{$1})) {
                    $val = kw($configlib{$1});
                    $line = "$`$val;$'";
                } else {
                    $line = "$`$'";
                }
            } elsif ($1 ~~ @substdll) {
                if (defined($configdll{$1})) {
                    $val = kw($configdll{$1});
                    $line = "$`$val$'";
                } else {
                    $line = "$`$'";
                }
            } else {
                die "unknown control $& in $projectname\n";
            }
        }
        push @Linesout, $line;
    }

    open F, ">" . $projectname || die $!;
    if ($verbose) {
        print "Setting up $projectname\n";
    }
    foreach $line (@Linesout) {
        print F $line . "\n";
    }
    close F;
}

# Make bind.keys.h
# Keep this in sync with util/bindkeys.pl

sub makebindkeysh {
    my $bindkeysh = `perl.exe ../util/bindkeys.pl ../bind.keys`;
    die "Failed to generate bind.keys.h!\n" unless (length($bindkeysh));

    open(my $fout, ">" . "../bind.keys.h") || die $!;
    print $fout $bindkeysh;
    close($fout);
}

# Build install files

sub makeinstallfile {
    open FOUT, ">InstallFlags" || die "cannot open InstallFlags: $!\n";
    print FOUT "# Flags for BINDInstall\n";
    if ($msc_ver >= 1400) {
        print FOUT "runvcredist\n";
    }
    if ($want_x64 eq "yes") {
        print FOUT "forwin64\n";
    }
    close FOUT;

    open LOUT, ">InstallFiles" || die "cannot open InstallFiles: $!\n";
    print LOUT "# File list for BINDInstall\n";
    if ($msc_ver < 1400) {
        if ($msc_ver >= 1310) {
            print LOUT "mfc71.dll-WCTT\n";
            print LOUT "msvcr71.dll-WCTT\n";
        } elsif (($msc_ver > 1200) && ($msc_ver < 1310)) {
            print LOUT "mfc70.dll-WCTT\n";
            print LOUT "msvcr70.dll-WCTT\n";
        }
    }
    print LOUT "bindevt.dll-BNFT\n";
    print LOUT "libbind9.dll-BCFT\n";
    print LOUT "libisc.dll-BCFT\n";
    print LOUT "libisccfg.dll-BCFT\n";
    print LOUT "libisccc.dll-BCFT\n";
    print LOUT "libdns.dll-BCFT\n";
    print LOUT "libirs.dll-BCFT\n";
    print LOUT "libns.dll-BCFT\n";
    print LOUT "nghttp2.dll-BCFT\n";
    print LOUT "uv.dll-BCFT\n";
    if ($use_openssl eq "yes") {
        my $v;
        my $d;
        my $name;
        ($v, $d, $name) =File::Spec->splitpath($configdll{"OPENSSL_DLLCRYPTO"});
        print LOUT "${name}-BCFT\n";
        ($v, $d, $name) =File::Spec->splitpath($configdll{"OPENSSL_DLLSSL"});
        print LOUT "${name}-BCFT\n";
    }
    if ($use_libxml2 eq "yes") {
        print LOUT "libxml2.dll-BCFT\n";
    }
    if ($use_gssapi eq "yes") {
        if ($want_x64 eq "yes") {
            print LOUT "gssapi64.dll-BCFT\n";
            print LOUT "krb5_64.dll-BCFT\n";
        } else {
            print LOUT "gssapi32.dll-BCFT\n";
            print LOUT "krb5_32.dll-BCFT\n";
        }
    }
    if ($use_idn eq "yes") {
        print LOUT "idnkit.dll-BCFT\n";
        print LOUT "iconv.dll-BCFT\n";
    }
    print LOUT "named.exe-BCFF\n";
    print LOUT "nsupdate.exe-BNFT\n";
    print LOUT "BINDInstall.exe-BNFT\n";
    print LOUT "InstallFlags-BNFT\n";
    print LOUT "InstallFiles-BNFT\n";
    print LOUT "rndc.exe-BNFF\n";
    print LOUT "dig.exe-BNFT\n";
    print LOUT "host.exe-BNFT\n";
    print LOUT "mdig.exe-BNFT\n";
    print LOUT "nslookup.exe-BNFT\n";
    print LOUT "delv.exe-BNFT\n";
    print LOUT "arpaname.exe-BNFT\n";
    print LOUT "nsec3hash.exe-BNFF\n";
    print LOUT "rndc-confgen.exe-BNFF\n";
    print LOUT "ddns-confgen.exe-BNFF\n";
    print LOUT "tsig-keygen.exe-BNFF\n";
    print LOUT "dnssec-keygen.exe-BNFF\n";
    print LOUT "dnssec-signzone.exe-BNFF\n";
    print LOUT "dnssec-dsfromkey.exe-BNFF\n";
    print LOUT "dnssec-importkey.exe-BNFF\n";
    print LOUT "dnssec-keyfromlabel.exe-BNFF\n";
    print LOUT "dnssec-revoke.exe-BNFF\n";
    print LOUT "dnssec-settime.exe-BNFF\n";
    print LOUT "dnssec-verify.exe-BNFF\n";
    print LOUT "named-checkconf.exe-BNFF\n";
    print LOUT "named-checkzone.exe-BNFF\n";
    print LOUT "named-compilezone.exe-BNFF\n";
    print LOUT "named-journalprint.exe-BNFF\n";
    print LOUT "named-rrchecker.exe-BNFF\n";
    if ($use_pkcs11 eq "yes") {
        print LOUT "pkcs11-destroy.exe-BNFF\n";
        print LOUT "pkcs11-keygen.exe-BNFF\n";
        print LOUT "pkcs11-list.exe-BNFF\n";
        print LOUT "pkcs11-tokens.exe-BNFF\n";
    }
    print LOUT "readme1st.txt-BTFT\n";
    close LOUT;
}

# Adjust system tests

# dnstap not supported
#$configtest{"DNSTAP"} = "dnstap";
#$configtest{"FSTRM_CAPTURE"} = "fstrm_capture";

# no json-c library for WIN32
#$configtest{"JSONSTATS"} = 1;

# lmdb not supported
#$configtest{"NZD_TOOLS"} = "nzd";

# TODO check for pkcs11ssl and set PKCS11_TEST

# Status

if ($verbose) {
    my $name;

    print "Configuration Status\n";

    print "\tconfig.h:\n";
    foreach $name (@substdefh) {
        if (defined($configdefh{$name})) {
            print qq(\t\t$name defined to "$configdefh{$name}"\n);
        } else {
            printf qq(\t\t$name undefined\n);
        }
    }

    print "\tplatform.h:\n";
    foreach $name (@substdefp) {
        if (defined($configdefp{$name})) {
            print qq(\t\t$name defined to "$configdefp{$name}"\n);
        } else {
            printf qq(\t\t$name undefined\n);
        }
    }

    print "\tconf.sh:\n";
    foreach $name (@substtest) {
        if (defined($configtest{$name})) {
            print qq(\t\t$name defined to "$configtest{$name}"\n);
        } else {
            printf qq(\t\t$name undefined\n);
        }
    }

    print "\tconditions:\n";
    foreach $name (@substcond) {
        if (defined($configcond{$name})) {
            print "\t\t$name is true\n";
        } else {
            print "\t\t$name is false\n";
        }
    }

    print "\tsubstitutions:\n";
    foreach $name (@substvar) {
        if (defined($configvar{$name})) {
            print qq(\t\t$name -> "$configvar{$name}"\n);
        }
    }

    print "\tdefines:\n";
    foreach $name (@substdefd) {
        if (defined($configdefd{$name})) {
            print qq(\t\t/D "$configdefd{$name}"\n);
        }
    }

    print "\tincludes:\n";
    foreach $name (@substinc) {
        if (defined($configinc{$name})) {
            print qq(\t\t/I "$configinc{$name}"\n);
        }
    }

    print "\tlibraries:\n";
    foreach $name (@substlib) {
        if (defined($configlib{$name})) {
            print "\t\t$configlib{$name}\n";
        }
    }

    print "\tDLLs:\n";
    foreach $name (@substdll) {
        if (defined($configdll{$name})) {
            print "\t\t$configdll{$name}\n";
        }
    }

    print "\n";
}

# Setup
if (($want_win32 eq "yes") || ($want_x64 eq "yes")) {
    setupconfigh();

    setupplatformh();

    setupconfsh();

    my $file;
    foreach $file (@filelist) {
        setupfile($file);
    }

    foreach $file (@projectlist) {
        setupproject($file);
    }

    makeinstallfile();

    makebindkeysh();

    print "Configured.\n";
} else {
    print "add win32 or x64 to commit configuration to build files\n";
}

exit 0;

# Notes: Unix configure.in options
#  --enable-developer partially supported
#  --enable-native-pkcs11 supported
#  --enable-openssl-version-check included without a way to disable it
#  --enable-openssl-hash supported
#  --enable-threads included without a way to disable it
#  --enable-backtrace backtrace included without a way to disable it
#  --enable-symtable incompatible with DLLs
#  --enable-ipv6 included without a way to disable it
#  --enable-atomic supported (renamed to intrinsic)
#  --enable-fixed-rrset supported
#  --enable-querytrace supported
#  --enable-full-report supported by verbose
#  --enable-dnstap not supported (requires libfstrm support on WIN32)
#  --enable-afl not supported (not yet available on Visual Studio C++)
#  --disable-auto-validation supported
#  --with-python supported
#  --with-openssl supported
#  --with-pkcs11 supported
#  --with-aes supported
#  --with-randomdev not supported on WIN32 (makes no sense)
#  --with-geoip2 supported
#  --with-gssapi supported with MIT (K)erberos (f)or (W)indows
#  --with-lmdb no supported on WIN32 (port is not reliable)
#  --with-libxml2 supported
#  --with-json-c not supported on WIN32 (package not available on WIN32)
#  --with-zlib supported
#  --with-purify not supported (package available on WIN32 but for free?)
#  --with-locktype not supported on WIN32 (not yet available on WIN32)
#  --with-readline supported
#  --with-protobuf-c not supported (no reason to until libfstrm is ready)
#  --with-libfrtrm not supported (not yet available on WIN32)
#  --with-docbook-xsl not supported (?)
#  --with-idn[lib] supported
#  --with-[lib]iconv supported (part of IDN)
#  --with-atf not supported on WIN32 (package not available on WIN32)
#  --with-tuning supported
#  --with-dlopen included without a way to disable it
#  --with-dlz-* ?
#
# Notes: MSVC versions
# MSVC 15.x _MSC_VER == 191y (VS 2017)
# MSVC 14.0 _MSC_VER == 1900 (VS 2015)
# MSVC 12.0 _MSC_VER == 1800 (VS 2013)
# MSVC 11.0 _MSC_VER == 1700 (VS 2012)
# MSVC 10.0 _MSC_VER == 1600 (VS 2010)
# MSVC 9.0  _MSC_VER == 1500 (VS 2008)
# MSVC 8.0  _MSC_VER == 1400 (VS 2005)
# MSVC 7.1  _MSC_VER == 1310 (VS .NET 2003)
# MSVC 7.0  _MSC_VER == 1300 (VS .NET (2002))
# MSVC 6.0  _MSC_VER == 1200 (VS 6.0 (1998))
# MSVC 5.0  _MSC_VER == 1100 (VS 97)
