# vim: set ft=tcl syntax=tcl ts=2 sw=2 expandtab:

# Make sure we use /usr as a default prefix on Linux; we can't use $host_
# variables because this needs to go before the inclusion of the system module.
if {![catch {exec uname} out] && $out eq {Linux}} {
  options-defaults {
    prefix /usr
  }
}

autosetup-require-version 0.6.9

# Standard autosetup modules and our own modules
use system cc cc-lib mutt-gettext mutt-iconv pkg-config

###############################################################################
# Names and versions
define PACKAGE          "neomutt"
define PACKAGE_VERSION  "20220429"
define BUGS_ADDRESS     "neomutt-devel@neomutt.org"

# Subdirectories that contain additional Makefile.autosetup files
set subdirs {po docs contrib}
###############################################################################

###############################################################################
# Add any user options here
options {
# Curses
  with-ncurses:path         => "Location of ncurses"
# Features w/o 3rd party dependencies
  doc=1                     => "Disable building the documentation"
  full-doc=0                => "Build the full documentation set"
  docdir:path               => "Documentation root"
  with-lock:=fcntl          => "Select fcntl() or flock() to lock files"
  fmemopen=0                => "Use fmemopen() for temporary in-memory files"
  inotify=1                 => "Disable file monitoring support (Linux only)"
  locales-fix=0             => "Enable locales fix"
  pgp=1                     => "Disable PGP support"
  smime=1                   => "Disable SMIME support"
  mixmaster=0               => "Enable Mixmaster support"
  with-mixmaster:=mixmaster => "Location of the mixmaster executable"
  homespool=0               => "Enable new mail spooling in the user's HOME"
  with-homespool:mailbox    => "File in the user's HOME where new mail is spooled"
  with-mailpath:/var/mail   => "Directory where spool mailboxes are located"
  with-domain:domain        => "Specify your DNS domain name"
# Crypto
  # OpenSSL or GnuTLS
  ssl=0                     => "Enable TLS support using OpenSSL"
  with-ssl:path             => "Location of OpenSSL"
  gnutls=0                  => "Enable TLS support using GnuTLS"
  with-gnutls:path          => "Location of GnuTLS"
  # GPGME
  gpgme=0                   => "Enable GPGME support"
  with-gpgme:path           => "Location of GPGME"
  # GSS (IMAP auth)
  gss=0                     => "Use GSSAPI authentication for IMAP"
  with-gss:path             => "Location of GSSAPI library"
  # SASL (IMAP and POP auth)
  sasl=0                    => "Use the SASL network security library"
  with-sasl:path            => "Location of the SASL network security library"
  # AutoCrypt
  autocrypt=0               => "Enable AutoCrypt support (requires GPGME and SQLite)"
# Lua
  lua=0                     => "Enable Lua scripting support"
  with-lua:path             => "Location of Lua"
# Notmuch
  notmuch=0                 => "Enable Notmuch support"
  with-notmuch:path         => "Location of Notmuch"
# NLS
  nls=1                     => "Disable Native Language Support"
  with-nls:path             => "Location of libintl"
# IDN
  idn=1                     => "Disable GNU libidn for internationalized domain names"
  with-idn:path             => "Location of GNU libidn"
  idn2=0                    => "Enable GNU libidn2 for internationalized domain names"
  with-idn2:path            => "Location of GNU libidn2"
# PCRE2
  pcre2=0                   => "Enable PCRE2 regular expressions"
  with-pcre2:path           => "Location of PCRE2"
# Header cache
  bdb=0                     => "Use BerkeleyDB for the header cache"
  with-bdb:path             => "Location of BerkeleyDB"
  with-bdb-version:version  => "Version of BerkeleyDB"
  gdbm=0                    => "Use GNU dbm for the header cache"
  with-gdbm:path            => "Location of GNU dbm"
  kyotocabinet=0            => "Use KyotoCabinet for the header cache"
  with-kyotocabinet:path    => "Location of KyotoCabinet"
  lmdb=0                    => "Use LMDB for the header cache"
  with-lmdb:path            => "Location of LMDB"
  qdbm=0                    => "Use QDBM for the header cache"
  with-qdbm:path            => "Location of QDBM"
  rocksdb=0                 => "Use RocksDB for the header cache"
  with-rocksdb:path         => "Location of RocksDB"
  tdb=0                     => "Use TDB for the header cache"
  with-tdb:path             => "Location of TDB"
  tokyocabinet=0            => "Use TokyoCabinet for the header cache"
  with-tokyocabinet:path    => "Location of TokyoCabinet"
# sqlite
  sqlite=0                  => "Enable SQLite support"
  with-sqlite:path          => "Location of SQLite"
# compression
  lz4=0                     => "Enable LZ4 header cache compression support"
  with-lz4:path             => "Location of LZ4"
  zlib=0                    => "Enable zlib support"
  with-zlib:path            => "Location of zlib"
  zstd=0                    => "Enable Zstandard header cache compression support"
  with-zstd:path            => "Location of Zstandard"
# System
  with-sysroot:path         => "Target system root"
  with-tmpdir:=/tmp         => "location of the tmp directory"
# Misc
  include-path-in-cflags=1  => "Remove include paths from CFLAGS in the output of neomutt -v"
# Testing
  asan=0                    => "Enable the Address Sanitizer"
  coverage=0                => "Enable Coverage Testing"
  testing=0                 => "Enable Unit Testing"
  fuzzing                   => "Enable Fuzz Testing"
# Configure with pkg-config
  pkgconf=0                 => "Use pkg-config during configure"
# Enable all options
  everything=0              => "Enable all options"
# Debug options
  debug-backtrace=0         => "DEBUG: Enable backtrace support with libunwind"
  with-backtrace:path       => "Location of libunwind"
  debug-color=0             => "DEBUG: Enable Color dump"
  debug-email=0             => "DEBUG: Enable Email dump"
  debug-graphviz=0          => "DEBUG: Enable Graphviz dump"
  debug-notify=0            => "DEBUG: Enable Notifications dump"
  debug-parse-test=0        => "DEBUG: Enable 'neomutt -T' for config testing"
  debug-queue=0             => "DEBUG: Enable TAILQ debugging"
  debug-window=0            => "DEBUG: Enable windows dump"
# Deprecated stuff
  with-ui:=deprecated       => "Deprecated"
  with-slang:deprecated     => "Deprecated"
}
###############################################################################

###############################################################################
# Deprecated config options
foreach dep {with-ui with-slang} {
  if {[opt-bool -nodefault $dep] != -1} {
    user-notice "\nThe configure option \"--$dep\" is not used anymore\n"
  }
}
###############################################################################

###############################################################################
# All boolean options are converted to want-* definitions here. Further down,
# their values is checked with [get-define opt].  This facilitates the handling
# of dependencies among options (see "everything").
if {1} {
  # Keep sorted, please.
  foreach opt {
    asan autocrypt bdb coverage debug-backtrace debug-color debug-email
    debug-graphviz debug-notify debug-parse-test debug-queue debug-window doc
    everything fmemopen full-doc fuzzing gdbm gnutls gpgme gss homespool idn
    idn2 include-path-in-cflags inotify kyotocabinet lmdb locales-fix lua lz4
    mixmaster nls notmuch pcre2 pgp pkgconf qdbm rocksdb sasl smime sqlite ssl
    testing tdb tokyocabinet zlib zstd
  } {
    define want-$opt [opt-bool $opt]
  }

  # These options support a --with-opt parameter. If that is set, force the
  # relative --enable-opt to true. This allows "--with-opt=/usr" to be used as
  # a shortcut for "--opt --with-opt=/usr".
  foreach opt {
    bdb gdbm gnutls gpgme gss homespool idn idn2 kyotocabinet lmdb lua lz4 
    mixmaster nls notmuch pcre2 qdbm rocksdb sasl sqlite ssl tdb tokyocabinet 
    zlib zstd
  } {
    if {[opt-val with-$opt] ne {}} {
      define want-$opt 1
    }
  }

  # No more usage of [opt-bool] below this point.
  proc opt-bool {args} {
    user-error "opt-bool should not be called here"
  }
}
###############################################################################

###############################################################################
# Paths
define BINDIR           [get-define bindir]
define MUTTLOCALEDIR    [get-define datadir]/locale
define PKGDATADIR       [get-define datadir]/neomutt
define PKGDOCDIR        [opt-val docdir [get-define datadir]/doc/neomutt]
define SYSCONFDIR       [get-define sysconfdir]
define TMPDIR           [opt-val with-tmpdir /tmp]
set libdir_tail         [file tail [get-define libdir]] ;# used only locally
###############################################################################

###############################################################################
# Helper functions
if {1} {
  # Check for a header file and a function in a library
  proc check-inc-and-lib {name prefix header fun lib} {
    cc-with [list -cflags -I$prefix/include -libs -L$prefix/$::libdir_tail] {
      if {[cc-check-includes $header] && [cc-check-function-in-lib $fun $lib]} {
        define-append CFLAGS  -I$prefix/include
        define-append LDFLAGS -L$prefix/$::libdir_tail
        define-feature $name
      }
    }
    have-feature $name
  }

  proc check-define {incfile macro} {
    set code {}
    if {$incfile ne {}} {
      append code "#include \"$incfile\"\n"
    }
    append code "#ifndef $macro\n#error \"$macro not defined\"\n#endif\n$macro"
    if {[catch {exec {*}[get-define CC] {*}[get-define CFLAGS] -xc - -E | tail -1 << $code} out]} {
      error
    }
    set out
  }

  # Check whether a macro is defined after preprocessing a header file
  proc has-define {incfile macro} {
    expr {[catch {check-define $incfile $macro}] == 0}
  }

  # Get the value of a macro by preprocessing a header file that defines it
  proc get-define-value {incfile macro} {
    if {![catch {check-define $incfile $macro} out]} {
      return $out
    }
  }

  # Convert a string into a representation suitable for a C char[]
  proc text2c {s} {
    set result "\n  "
    set i 0
    foreach c [split $s {}] {
      append result "0x[format %02x [scan $c %c]], "
      if {[expr {[incr i] % 12 == 0}]} {
        append result "\n  "
        set i 0
      }
    }
    append result "0x00\n";
  }

  # Guess what..
  proc yesno val {
    expr {$val ? "yes" : "no"}
  }

  # Wrapper around pkg-config, to optionally fail is a pkg is not found, and to
  # add the compile/link definitions for the module.
  proc pkgconf {required module args} {
    if {[pkg-config $module {*}$args]} {

      # Add only non-duplicate CFLAGS and LDFLAGS
      foreach which {CFLAGS LDFLAGS} {
        foreach flag [pkg-config-get $module $which] {
          if {$flag ni [get-define $which]} {
            define-append $which $flag
          }
        }
      }
      # Order of libraries matter, so we cannot skip duplicates
      define-append LIBS [pkg-config-get $module LIBS]
      return 1
    } elseif {[string is bool $required] && $required} {
      user-error "$module $args"
    } else {
      return 0
    }
  }
}
###############################################################################

###############################################################################
# C compiler definitions and related tools
if {1} {

  # First off, require c99
  if {[cc-check-standards c99] eq {}} {
    user-error "C99 is required"
  }
  define-append CFLAGS_FOR_BUILD -std=c99
  define LDFLAGS_FOR_BUILD {}

  # Check for tools and programs
  cc-check-tools ar ranlib strip
  cc-check-progs install
  if {![cc-path-progs sendmail]} {
    define SENDMAIL /usr/sbin/sendmail
  }

  # GCC-specifc CFLAGS
  if {![catch {exec {*}[get-define CC] --version} res]} {
    if {[regexp -nocase gcc $res]} {
      define-append CFLAGS "-fno-delete-null-pointer-checks"
    }
  }

  # Enable extensions (reverse-engineered from AC_SYSTEM_EXTENSIONS)
  if {1} {
    if {[cc-check-includes minix/config.h]} {
      lappend extensions -D_POSIX_SOURCE=1
      lappend extensions -D_POSIX_1_SOURCE=2
      lappend extensions -D_MINIX=1
    }
    lappend extensions -D_ALL_SOURCE=1
    lappend extensions -D_GNU_SOURCE=1
    lappend extensions -D__EXTENSIONS__
    define-append CFLAGS_FOR_BUILD {*}$extensions
    define-append CFLAGS {*}$extensions
  }
  cc-with [list -cflags [get-define CFLAGS]]

  # Endianness
  cc-check-endian
  if {[have-feature BIG_ENDIAN]} {
    define WORDS_BIGENDIAN
  }

  # Large file support
  if {[cc-check-lfs]} {
    define OFF_T_FMT {"%" PRId64}
  } else {
    define OFF_T_FMT {"%" PRId32}
  }
  define LOFF_T off_t
}
###############################################################################

###############################################################################
# signal-related checks
# Let's always use volatile for sig_atomic_t
cc-check-includes signal.h
define SIG_ATOMIC_VOLATILE_T "volatile sig_atomic_t"

cc-with {-includes "signal.h unistd.h"} {
  cc-check-decls sys_siglist
}
###############################################################################

###############################################################################
# Check for includes and functions that in the code are surrounded by
# '#ifdef HAVE_FOO_H' and '#ifdef HAVE_FUNCTION' and for functions that might
# be in different libraries
if {1} {
  cc-check-includes \
    ioctl.h \
    termios.h \
    sys/ioctl.h \
    syscall.h \
    sys/random.h \
    sys/syscall.h \
    sysexits.h

  cc-check-functions \
    clock_gettime \
    fgetc_unlocked \
    futimens \
    getaddrinfo \
    getrandom \
    getsid \
    iswblank \
    mkdtemp \
    qsort_s \
    strsep \
    strcasestr \
    tcgetwinsize \
    timegm \
    utimesnsat \
    vasprintf \
    wcscasecmp

  # The following systems have known-bad qsort_r with reversed arguments
  if {![has-define sys/param.h __FreeBSD_version] &&
      ![has-define {} __APPLE__]} {
    cc-check-functions qsort_r
  }

  cc-check-function-in-lib gethostent nsl
  cc-check-function-in-lib setsockopt socket
  cc-check-function-in-lib getaddrinfo_a anl
  cc-check-function-in-lib nanosleep rt

  cc-with {-includes time.h} {
    cc-check-types "struct timespec"
  }

  cc-with {-includes sys/stat.h} {
    cc-check-members "struct stat.st_atim.tv_nsec"
  }
}
###############################################################################

###############################################################################
# Various unconditional defines
define USE_COMP_MBOX
define USE_IMAP
define USE_NNTP
define USE_POP
define USE_SIDEBAR
define USE_SOCKET
define USE_SMTP
define SUN_ATTACHMENT
define TEST_CASE_MAXSIZE 1024
###############################################################################

set prefix [opt-val with-sysroot][get-define prefix]

###############################################################################
# Everything
if {[get-define want-everything]} {
  foreach opt {bdb gdbm gpgme kyotocabinet lmdb lua lz4 notmuch pgp rocksdb
               qdbm sasl smime ssl tokyocabinet tdb zlib zstd} {
    define want-$opt
    append conf_options "--$opt "
  }
} else {
  set conf_options "$::argv"
}

###############################################################################
# Locking
switch [opt-val with-lock fcntl] {
  fcntl {
    define USE_FCNTL
  }

  flock {
    define USE_FLOCK
  }

  default {
    user-error "Invalid value for --with-lock=[opt-val with-lock], select fcntl\
                or flock"
  }
}

###############################################################################
# Locales fix
if {[get-define want-locales-fix]} {define LOCALES_HACK}

###############################################################################
# Documentation
if {[get-define want-doc]} {
  if {![cc-check-progs xsltproc]} {
    user-error "Unable to find xsltproc"
  }

  msg-checking "Checking for DocBook DTDs..."
  set res [exec xmlcatalog --shell << {public "-//OASIS//DTD DocBook XML V4.2//EN"}]
  if {[string match {> No entry*} $res]} {
    msg-result "no"
    user-error "Install DocBook DTDs or './configure --disable-doc'"
  }
  msg-result "yes"

  define BUILD_DOC
}
if {[get-define want-full-doc]} {define MAKEDOC_FULL}

###############################################################################
# AutoCrypt
if {[get-define want-autocrypt]} {
  define USE_AUTOCRYPT
  define want-sqlite 1
  define want-gpgme 1
}

###############################################################################
# GPGME
if {[get-define want-gpgme]} {
  if {[is-defined _FILE_OFFSET_BITS]} {
    define-append CFLAGS -D_FILE_OFFSET_BITS=[get-define _FILE_OFFSET_BITS]
  }

  msg-checking "Checking for GPGME..."
  if {1} {
    # Locate gpgme-config
    set gpgme_prefix [opt-val with-gpgme $prefix]
    set gpgme_config_guess [file join $gpgme_prefix bin gpgme-config]
    if {[file-isexec $gpgme_config_guess]} {
      define GPGME-CONFIG $gpgme_config_guess
    } else {
      if {![cc-check-progs gpgme-config]} {
        user-error "Unable to find gpgme-config"
      }
    }
    set gpgme_config [get-define GPGME-CONFIG]

    # Version
    if {[catch {exec-with-stderr $gpgme_config --version} gpgme_version err]} {
      user-error "Could not derive --version from $gpgme_config"
    }
    if {[scan $gpgme_version "%d.%d.%d" gpgme_maj gpgme_min gpgme_patch] != 3} {
      user-error "Could not parse GPGME version $gpgme_version"
    }
    msg-result $gpgme_version
    if {[get-define want-autocrypt]} {
      if {$gpgme_maj < 1 || $gpgme_min < 8} {
        user-error "Found GPGME version $gpgme_version, need 1.8.0 for AutoCrypt"
      }
    } else {
      if {$gpgme_maj < 1 || $gpgme_min < 4} {
        user-error "Found GPGME version $gpgme_version, need 1.4.0"
      }
    }
    define GPGME_VERSION_NUMBER [format "0x%02x%02x%02x" $gpgme_maj $gpgme_min $gpgme_patch]

    # RHEL6 doesn't have this function yet
    cc-check-function-in-lib gpgme_op_export_keys gpgme

    # CFLAGS
    if {[catch {exec-with-stderr $gpgme_config --cflags} res err]} {
      user-error "Could not derive --cflags from $gpgme_config"
    }
    define-append CFLAGS $res

    # LIBS
    if {[catch {exec-with-stderr $gpgme_config --libs} res err]} {
      user-error "Could not derive --libs from $gpgme_config"
    }
    define-append LIBS $res
  }
  define-feature gpgme

  msg-checking "Checking for gpg-error..."
  if {1} {
    # Locate gpg-error-config
    set gpg_error_config_guess [file join $gpgme_prefix bin gpg-error-config]
    if {[file-isexec $gpg_error_config_guess]} {
      define GPG-ERROR-CONFIG $gpg_error_config_guess
    } else {
      if {![cc-check-progs gpg-error-config]} {
        user-error "Unable to find gpg-error-config"
      }
    }
    set gpg_error_config [get-define GPG-ERROR-CONFIG]

    # Version
    if {[catch {exec-with-stderr $gpg_error_config --version} gpg_error_version err]} {
      user-error "Could not derive --version from $gpg_error_config"
    }

    # CFLAGS
    if {[catch {exec-with-stderr $gpg_error_config --cflags} res err]} {
      user-error "Could not derive --cflags from $gpg_error_config"
    }
    define-append CFLAGS $res

    # LIBS
    if {[catch {exec-with-stderr $gpg_error_config --libs} res err]} {
      user-error "Could not derive --libs from $gpg_error_config"
    }
    define-append LIBS $res
  }
  msg-result $gpg_error_version

  define CRYPT_BACKEND_GPGME
}

###############################################################################
# INOTIFY
if {[get-define want-inotify]} {
  if {[cc-check-functions inotify_add_watch inotify_init inotify_rm_watch]} {
    define USE_INOTIFY
    cc-check-functions inotify_init1
    cc-check-includes sys/inotify.h
  }
}

###############################################################################
# PGP
if {[get-define want-pgp]} {
  define-feature PGP
  define CRYPT_BACKEND_CLASSIC_PGP
}

###############################################################################
# SMIME
if {[get-define want-smime]} {
  define-feature SMIME
  define CRYPT_BACKEND_CLASSIC_SMIME
}

###############################################################################
# SASL
if {[get-define want-sasl]} {
  if {[get-define want-pkgconf]} {
    pkgconf true libsasl2
    # RHEL6 doesn't have this function yet
    cc-check-functions sasl_client_done
    define USE_SASL
    define-feature SASL
  } else {
    foreach sasl_lib {sasl2 sasl} {
      if {[check-inc-and-lib sasl [opt-val with-sasl $prefix] \
                             sasl/sasl.h sasl_encode64 $sasl_lib]} {
        # RHEL6 doesn't have this function yet
        cc-check-functions sasl_client_done
        define USE_SASL
        break
      }
    }
    if {![get-define USE_SASL]} {
      user-error "Unable to find SASL"
    }
  }
}

###############################################################################
# Unit Testing
if {[get-define want-testing]} {
  define ENABLE_UNIT_TESTS
  lappend subdirs test
}

###############################################################################
# Lua
if {[get-define want-lua]} {

  if {[get-define want-pkgconf]} {
    if {![pkgconf false lua] && ![pkgconf false lua-5.4] && ![pkgconf false lua-5.3] && ![pkgconf false lua-5.2]} {
      user-error "Unable to find LUA"
    }
    define USE_LUA
  } else {
    set lua_versions { 5.4 5.3 5.2 } ;# Will be checked in order

    apply {{lua_prefix lua_versions} {
      foreach ver $lua_versions {
        lassign [split $ver .] maj min

        foreach lua_suffix [list /lua${maj}${min} /lua${maj}.${min} /lua-${maj}${min} /lua-${maj}.${min} /lua {}] {
          msg-checking "Checking for include$lua_suffix/lua.h..."
          if {[file exists $lua_prefix/include$lua_suffix/lua.h]} {
            msg-result "yes"
            set libs [list lua-${maj}.${min} lua${maj}.${min} lua]
            cc-with [list -libs "-L$lua_prefix/$::libdir_tail"] {
              if {![cc-check-function-in-lib luaL_openlibs $libs]} {
                continue
              }
            }
            define-append CFLAGS -I$lua_prefix/include$lua_suffix
            define-append LDFLAGS -L$lua_prefix/$::libdir_tail
            define USE_LUA
            return
          }
          msg-result "no"
        }
      }
      user-error "Unable to find Lua"
    }} [opt-val with-lua $prefix] $lua_versions
  }
}

###############################################################################
# Notmuch
if {[get-define want-notmuch]} {
  if {![check-inc-and-lib notmuch [opt-val with-notmuch $prefix] \
                          notmuch.h notmuch_database_open notmuch]} {
    user-error "Unable to find Notmuch"
  }
  define USE_NOTMUCH
  msg-checking "Checking for Notmuch API version 3..."
  if {[cctest -includes {notmuch.h} -libs {-lnotmuch} -link 1 \
      -code { notmuch_database_open(NULL, 0, NULL); }]} {
    define NOTMUCH_API_3
    msg-result "yes"
  } else {
    msg-result "no"
  }
  cc-check-function-in-lib notmuch_database_index_file notmuch
  cc-check-function-in-lib notmuch_database_open_with_config notmuch
}

###############################################################################
# Native Language Support (NLS)
if {[get-define want-nls]} {
  if {![check-gettext [opt-val with-nls $prefix]]} {
    user-error "Unable to find gettext. Consider --disable-nls"
  }
  if {![cc-check-progs msgfmt msgmerge xgettext]} {
    user-error "Unable to find gettext tools (msgfmt, msgmerge, xgettext).\
                Consider --disable-nls"
  }
}

###############################################################################
# SQLite Support
if {[get-define want-sqlite]} {
  if {![check-inc-and-lib sqlite [opt-val with-sqlite $prefix] \
                          sqlite3.h sqlite3_open sqlite3]} {
    user-error "Unable to find SQLite"
  }
  define USE_SQLITE
}

###############################################################################
# zlib Support
if {[get-define want-zlib]} {
  if {![check-inc-and-lib zlib [opt-val with-zlib $prefix] \
                          zlib.h deflate z]} {
    user-error "Unable to find zlib"
  }
  define USE_ZLIB
}

###############################################################################
# fmemopen(3)
if {[get-define want-fmemopen]} {
  if {![cc-check-functions fmemopen]} {
    user-error "Unable to find fmemopen"
  }
  if {![cc-check-functions open_memstream]} {
    user-error "Unable to find open_memstream"
  }
  define USE_FMEMOPEN 1
} else {
  define USE_FMEMOPEN 0
}

###############################################################################
# Ncurses
define-append CFLAGS -DNCURSES_WIDECHAR
set ncurses_prefix [opt-val with-ncurses $prefix]

cc-with [list -libs -L$ncurses_prefix/$::libdir_tail] {

  set tinfo_libs   {tinfow tinfo}
  set ncurses_libs {ncursesw ncurses curses}

  # Locate the library defining tgetent()
  # This must be done *before* checking for ncurses functions, see
  # https://github.com/neomutt/neomutt/issues/1118
  foreach tinfo_lib [concat $tinfo_libs $ncurses_libs] {
    if {[cc-check-function-in-lib tgetent $tinfo_lib]} {
      break
    }
  }

  # Locate the library defining waddnwstr()
  foreach ncurses_lib $ncurses_libs {
    if {[cc-check-function-in-lib waddnwstr $ncurses_lib]} {
      break
    }
  }

  if {![have-feature waddnwstr] || ![have-feature tgetent]} {
    user-error "Unable to find ncursesw library"
  }

  foreach f {bkgrndset setcchar} {
    cc-check-function-in-lib $f $ncurses_lib
  }
}

# Locate the directory containing ncurses.h
# See https://github.com/neomutt/neomutt/pull/679
apply {{ncurses_prefix} {
  cc-with [list -cflags -I$ncurses_prefix/include] {
    foreach ncurses_inc {ncursesw/ ncurses/ curses/ {}} {
      if {[cc-check-includes ${ncurses_inc}ncurses.h] ||
          [cc-check-includes ${ncurses_inc}curses.h]} {
        return
      }
    }
    user-error "Unable to find ncurses headers"
  }
}} $ncurses_prefix

###############################################################################
# Iconv - try to mimic AM_ICONV by preferring an installed libiconv
if {![check-iconv $prefix]} {
  user-error "Unable to find iconv()"
}

###############################################################################
# Mailpath and homespool
if {[get-define want-homespool]} {
  define MAILPATH [opt-val with-homespool mailbox]
  define HOMESPOOL 1
} else {
  define MAILPATH [opt-val with-mailpath /var/mail]
}

###############################################################################
# Mixmaster
if {[get-define want-mixmaster]} {
  define MIXMASTER [opt-val with-mixmaster mixmaster]
}

###############################################################################
# Domain
if {[opt-val with-domain] ne {}} {
  define DOMAIN [opt-val with-domain]
}

###############################################################################
# TLS support
if {[get-define want-ssl] && ![get-define want-gnutls]} {
  # OpenSSL
  if {[get-define want-pkgconf] && [pkgconf false openssl]} {
    # cool - we do not make pkg-config mandatory for OpenSSL because BSDs still
    # ship OpenSSL in the base system. Those aren't available through
    # pkg-config.
  } else {
    set ssl_prefix [opt-val with-ssl $prefix]
    set ssl_cflags -I$ssl_prefix/include
    set ssl_ldflags -L$ssl_prefix/$::libdir_tail
    cc-with [list -libs $ssl_ldflags -cflags $ssl_cflags] {
      if {![cc-check-includes openssl/bio.h openssl/err.h openssl/ssl.h] ||
          ![cc-check-function-in-lib X509_STORE_CTX_new crypto] ||
          ![cc-check-function-in-lib SSL_new ssl] ||
          ![cc-with {-includes openssl/ssl.h} {cc-check-decls SSL_set_mode}]} {
        user-error "Unable to find OpenSSL"
      }
      define-append CFLAGS $ssl_cflags
      define-append LDFLAGS $ssl_ldflags
    }
  }

  cc-check-functions RAND_egd
  cc-check-function-in-lib deflate z
  define USE_SSL
  define USE_SSL_OPENSSL
  if {[cc-with {-includes openssl/ssl.h} {
      cc-check-decls X509_V_FLAG_PARTIAL_CHAIN}]} {
    define-feature SSL_PARTIAL_CHAIN
  }
} elseif {[get-define want-gnutls]} {
  # GnuTLS
  if {[get-define want-pkgconf]} {
    pkgconf true gnutls
  } else {
    set gnutls_prefix [opt-val with-gnutls $prefix]
    cc-with [list -cflags -I$gnutls_prefix/include \
                   -libs -L$gnutls_prefix/$::libdir_tail] {
      if {![cc-check-function-in-lib gnutls_check_version gnutls]} {
        user-error "Unable to find GnuTLS"
      }
      define-append CFLAGS -I$gnutls_prefix/include
      define-append LDFLAGS -L$gnutls_prefix/$::libdir_tail
    }
  }
  cc-check-function-in-lib gnutls_priority_set_direct gnutls
  cc-with {-includes {gnutls/x509.h gnutls/gnutls.h}} {
    cc-check-decls GNUTLS_VERIFY_DISABLE_TIME_CHECKS
    cc-check-types gnutls_certificate_credentials_t \
      gnutls_certificate_status_t \
      gnutls_datum_t \
      gnutls_digest_algorithm_t \
      gnutls_session_t \
      gnutls_transport_ptr_t \
      gnutls_x509_crt_t
  }
  define USE_SSL
  define USE_SSL_GNUTLS
}

###############################################################################
# GNU libidn
if {[get-define want-idn] && [get-define want-idn2]} {
  user-error "Cannot specify both --idn and --idn2"
}
if {[get-define want-idn]} {
  proc find-idn1-includes {} {
    # These are used to figure which header to include
    if {!([cc-check-includes stringprep.h] || [cc-check-includes idn/stringprep.h]) ||
        !([cc-check-includes idna.h] || [cc-check-includes idn/idna.h])} {
      user-error "Unable to find GNU libidn"
    }
  }
  if {[get-define want-pkgconf]} {
    pkgconf true libidn
    find-idn1-includes
  } else {
    set idn_prefix [opt-val with-idn $prefix]
    cc-with [list -cflags -I$idn_prefix/include \
                   -libs -L$idn_prefix/$::libdir_tail] {
      find-idn1-includes
      define-append CFLAGS -I$idn_prefix/include
      define-append LDFLAGS -L$idn_prefix/$::libdir_tail
    }
  }
  if {![cc-check-function-in-lib stringprep_check_version idn]} {
    user-error "Unable to find stringprep_check_version in libidn"
  }
  cc-check-functions idna_to_unicode_utf8_from_utf8 idna_to_unicode_8z8z
  cc-check-functions idna_to_ascii_from_utf8 idna_to_ascii_8z
  cc-check-functions idna_to_ascii_lz idna_to_ascii_from_locale
  define-feature libidn
} elseif {[get-define want-idn2]} {
  proc find-idn2-includes {} {
    # These are used to figure which header to include
    if {!([cc-check-includes idn2.h] || [cc-check-includes idn/idn2.h])} {
      user-error "Unable to find GNU libidn2"
    }
  }
  if {[get-define want-pkgconf]} {
    pkgconf true libidn2
    find-idn2-includes
  } else {
    set idn_prefix [opt-val with-idn2 $prefix]
    cc-with [list -cflags -I$idn_prefix/include \
                  -libs -L$idn_prefix/$::libdir_tail] {
      find-idn2-includes
      define-append CFLAGS -I$idn_prefix/include
      define-append LDFLAGS -L$idn_prefix/$::libdir_tail
    }
  }
  if {![cc-check-function-in-lib idn2_to_ascii_8z     idn2] ||
      ![cc-check-function-in-lib idn2_to_unicode_8z8z idn2] ||
      ![cc-check-function-in-lib idn2_check_version   idn2]} {
    user-error "Unable to find required functions in GNU libidn2.\
                Please consider using idn1 with './configure --idn'."
  }
  define-feature libidn
}

###############################################################################
# PCRE2
if {[get-define want-pcre2]} {
  if {[get-define want-pkgconf]} {
    pkgconf true libpcre2-8
  } else {
    cc-with {-cflags -DPCRE2_CODE_UNIT_WIDTH=8} {
      if {![check-inc-and-lib pcre2 [opt-val with-pcre2 $prefix] \
                              pcre2.h pcre2_compile_8 pcre2-8]} {
        user-error "Unable to find PCRE2"
      }
    }
  }
  define-feature pcre2
} elseif {[check-inc-and-lib pcre2_unwarranted {} {} pcre2_regcomp {}]} {
  # See https://bugs.exim.org/show_bug.cgi?id=2707 and
  # https://github.com/neomutt/neomutt/issues/2865
  user-error "Found libpcre2-posix: please reconfigure with --pcre2"
}

###############################################################################
# Header cache - bdb
if {[get-define want-bdb]} {
  set bdb_versions [opt-val with-bdb-version { 5.3 6.2 4.8 }] ;# Will be checked in order
  set bdb_prefix [opt-val with-bdb $prefix]

  foreach ver $bdb_versions {
    lassign [split $ver .] maj min
    # This is ugly, but it allows us to not have an inner loop
    lappend bdb_majors   $maj $maj         $maj           $maj        $maj    $maj
    lappend bdb_minors   $min $min         $min           $min        $min    $min
    lappend bdb_exploded ""   db-$maj-$min db${maj}${min} db$maj.$min db-$maj db$maj
  }

  foreach maj $bdb_majors min $bdb_minors ver $bdb_exploded {
    set ver_inc_dir $bdb_prefix/include/$ver
    set ver_lib_dir $bdb_prefix/$::libdir_tail/$ver
    set ver_inc_file $ver_inc_dir/db.h
    set ver_lib_file  db-$maj.$min

    # File exists?
    msg-checking "Checking for BerkeleyDB in $ver_inc_dir..."
    if {![file exists $ver_inc_file]} {
      msg-result "no"
      continue
    }

    # Version is coherent?
    set inc_maj [get-define-value $ver_inc_file DB_VERSION_MAJOR]
    set inc_min [get-define-value $ver_inc_file DB_VERSION_MINOR]
    if {$inc_maj eq {}   || $inc_min eq {} ||
        $inc_maj != $maj || $inc_min != $min} {
      msg-result "no (expecting $maj.$min, got $inc_maj.$inc_min)"
      continue
    }
    msg-result "yes"

    # Can link?
    cc-with [list -libs -L$ver_lib_dir -cflags -I$ver_inc_dir] {
      if {![check-inc-and-lib bdb {} db.h db_env_create db-$maj.$min]} {
        msg-result "no"
        continue
      }
    }
    define-append CFLAGS  -I$ver_inc_dir
    define-append LDFLAGS -L$ver_lib_dir
    define-append LIBS    -ldb-$maj.$min
    define-append HCACHE_BACKENDS "bdb"
    define USE_HCACHE
    break
  }

  if {![have-feature bdb]} {
    user-error "Unable to find BerkeleyDB"
  }
}

###############################################################################
# Header cache - QDBM
# Note: qdbm must come before gdbm because they share library symbols.
if {[get-define want-qdbm]} {
  if {[get-define want-pkgconf]} {
    pkgconf true qdbm
    define-feature qdbm
  } else {
    # On Linux, headers are in a dedicated subdirectory
    set qdbm_prefix [opt-val with-qdbm $prefix]
    if {[file isdirectory $qdbm_prefix/include/qdbm]} {
      set qdbm_inc_subdir qdbm/
    } else {
      set qdbm_inc_subdir ""
    }
    if {[check-inc-and-lib qdbm [opt-val with-qdbm $qdbm_prefix] \
                            ${qdbm_inc_subdir}villa.h vlopen qdbm]} {
      define-append CFLAGS -I$qdbm_prefix/include/$qdbm_inc_subdir
    } else {
      user-error "Unable to find QDBM"
    }
  }
  define-append HCACHE_BACKENDS "qdbm"
  define USE_HCACHE
}


###############################################################################
# Header Cache - GNU dbm
if {[get-define want-gdbm]} {
  if {![check-inc-and-lib gdbm [opt-val with-gdbm $prefix] \
                          gdbm.h gdbm_count gdbm]} {
    user-error "Unable to find GNU dbm"
  }
  define-append HCACHE_BACKENDS "gdbm"
  define USE_HCACHE
}

###############################################################################
# Header cache - LMDB
if {[get-define want-lmdb]} {
  if {![check-inc-and-lib lmdb [opt-val with-lmdb $prefix] \
                          lmdb.h mdb_env_create lmdb]} {
    user-error "Unable to find LMDB"
  }
  define-append HCACHE_BACKENDS "lmdb"
  define USE_HCACHE
}

###############################################################################
# Header cache - KyotoCabinet
if {[get-define want-kyotocabinet]} {
  if {[get-define want-pkgconf]} {
    pkgconf true kyotocabinet
    define-feature kc
  } else {
    if {![check-inc-and-lib kc [opt-val with-kyotocabinet $prefix] \
                            kclangc.h kcdbopen kyotocabinet]} {
      user-error "Unable to find KyotoCabinet"
    }
  }
  define-append HCACHE_BACKENDS "kyotocabinet"
  define USE_HCACHE
}

###############################################################################
# Header cache - RocksDB
if {[get-define want-rocksdb]} {
  if {![check-inc-and-lib rocksdb [opt-val with-rocksdb $prefix] \
                          rocksdb/c.h rocksdb_open rocksdb]} {
    user-error "Unable to find RocksDB"
  }
  define-append HCACHE_BACKENDS "rocksdb"
  define USE_HCACHE
}

###############################################################################
# Header Cache - TDB
if {[get-define want-tdb]} {
  if {[get-define want-pkgconf]} {
    pkgconf true tdb
    define-feature tdb
  } else {
    cc-with {-includes {sys/stat.h stddef.h} } {
      if {![check-inc-and-lib tdb [opt-val with-tdb $prefix] \
                              tdb.h tdb_close tdb]} {
        user-error "Unable to find TDB"
      }
    }
  }
  define-append HCACHE_BACKENDS "tdb"
  define USE_HCACHE
}

###############################################################################
# Header Cache - TokyoCabinet
if {[get-define want-tokyocabinet]} {
  if {[get-define want-pkgconf]} {
    pkgconf true tokyocabinet
    define-feature tc
  } else {
    if {![check-inc-and-lib tc [opt-val with-tokyocabinet $prefix] \
                            tcbdb.h tcbdbopen tokyocabinet]} {
      user-error "Unable to find TokyoCabinet"
    }
  }
  define-append HCACHE_BACKENDS "tokyocabinet"
  define USE_HCACHE
}

###############################################################################
# Header Cache Compression with LZ4
if {[get-define want-lz4]} {
  if {![check-inc-and-lib lz4 [opt-val with-lz4 $prefix] \
                          lz4.h LZ4_compress_default lz4]} {
    user-error "Unable to find LZ4"
  }
  define-append LIBS -llz4
  define USE_LZ4
  define USE_HCACHE_COMPRESSION
  define-append COMPRESS_BACKENDS "lz4"
}

###############################################################################
# Header Cache Compression with zlib
if {[get-define want-zlib]} {
  if {![check-inc-and-lib zlib [opt-val with-zlib $prefix] \
                          zlib.h deflate z]} {
    user-error "Unable to find zlib"
  }
  define-append LIBS -lz
  define USE_ZLIB
  define USE_HCACHE_COMPRESSION
  define-append COMPRESS_BACKENDS "zlib"
}

###############################################################################
# Header Cache Compression with Zstandard
if {[get-define want-zstd]} {
  if {![check-inc-and-lib zstd [opt-val with-zstd $prefix] \
                          zstd.h ZSTD_compress zstd]} {
    user-error "Unable to find Zstandard"
  }
  define-append LIBS -lzstd
  define USE_ZSTD
  define USE_HCACHE_COMPRESSION
  define-append COMPRESS_BACKENDS "zstd"
}

###############################################################################
# GSS
if {[get-define want-gss]} {

  # TODO - Use krb5-config only, which should be enough in any moderately
  # modern OS. If people report breakage, I'll implement the manual logic
  # later.
  set gss_prefix [opt-val with-gss $prefix]
  set krb5_config_guess [file join $gss_prefix bin krb5-config]
  if {[file-isexec $krb5_config_guess]} {
    define KRB5-CONFIG $krb5_config_guess
  } else {
    if {![cc-check-progs krb5-config]} {
      user-error "Unable to find krb5-config"
    }
  }

  msg-checking "Checking for a GSSAPI implementation..."

  # Cflags
  set krb5_config [get-define KRB5-CONFIG]
  if {[catch {exec-with-stderr $krb5_config --cflags gssapi} res err]} {
    user-error "Could not derive --cflags from $krb5_config"
  }
  define-append CFLAGS $res

  # Libs
  if {[catch {exec-with-stderr $krb5_config --libs gssapi} res err]} {
    user-error "Could not derive --libs from $krb5_config"
  }
  define-append LDFLAGS $res

  # Implementation
  if {[catch {exec-with-stderr $krb5_config --version} res err]} {
    user-error "Could not derive --version from $krb5_config"
  }
  switch -glob $res {
    "Kerberos 5 *" { set GSSAPI_IMPL "MIT" }
    "*eimdal*"     { set GSSAPI_IMPL "Heimdal" }
    "Solaris*"     { set GSSAPI_IMPL "Solaris" }
    default        { set GSSAPI_IMPL "Unknown" }
  }
  msg-result $GSSAPI_IMPL

  if {$GSSAPI_IMPL in {Heimdal Solaris}} {
    define HAVE_HEIMDAL
  }
  define USE_GSS
}

###############################################################################
# DEBUG options

# Backtrace support with libunwind
if {[get-define want-debug-backtrace]} {
  if {![check-inc-and-lib libunwind [opt-val with-backtrace $prefix] \
                          libunwind.h unw_backtrace unwind]} {
    user-error "Unable to find libunwind"
  }
  define LIBS "-lunwind-generic [get-define LIBS]"
}

# Color dump
if {[get-define want-debug-color]} {
  define USE_DEBUG_COLOR 1
}

# Email dump
if {[get-define want-debug-email]} {
  define USE_DEBUG_EMAIL 1
}

# Graphviz dump
if {[get-define want-debug-graphviz]} {
  define USE_DEBUG_GRAPHVIZ 1
}

# Notifications dump
if {[get-define want-debug-notify]} {
  define USE_DEBUG_NOTIFY 1
}

# Parse Test
if {[get-define want-debug-parse-test]} {
  define USE_DEBUG_PARSE_TEST 1
}

# TAILQ debugging
if {[get-define want-debug-queue]} {
  # Store the last 2 places the queue element or head was altered
  define QUEUE_MACRO_DEBUG_TRACE
  # Invalidated pointers will be set to 0xffffffffffffffff
  define QUEUE_MACRO_DEBUG_TRASH
}

# Windows dump
if {[get-define want-debug-window]} {
  define USE_DEBUG_WINDOW 1
}

###############################################################################
# Address Sanitizer
if {[get-define want-asan]} {
  msg-checking "Checking for ASAN..."
  if {![cctest -link 1 -cflags -fsanitize=address]} {
    user-error "Unable to compile with -fsanitize=address"
  }
  msg-result "yes"
  define-append CFLAGS -fsanitize=address -O0 -fno-omit-frame-pointer -fno-common
  define-append LDFLAGS -fsanitize=address
  define USE_ASAN
}

###############################################################################
# Fuzz Testing
if {[get-define want-fuzzing]} {
  define ENABLE_FUZZ_TESTS
  lappend subdirs fuzz
  define-append CFLAGS -fsanitize=fuzzer
  define-append LDFLAGS -fsanitize=fuzzer
}

###############################################################################
# Coverage Testing
if {[get-define want-coverage]} {
  define ENABLE_COVERAGE
  define-append CFLAGS -fprofile-arcs -ftest-coverage
  define-append LDFLAGS -fprofile-arcs -ftest-coverage
}

###############################################################################
# Generate conststrings.c
set conststrings "\
  unsigned char cc_cflags\[\] = {[text2c [expr {
    [get-define want-include-path-in-cflags]
    ? [get-define CFLAGS]
    : [lmap x [get-define CFLAGS] {
        expr {[string equal -length 2 $x {-I}] ? [continue] : $x}
      }]
  }]]};\n\
  unsigned char configure_options\[\] = {[text2c $conf_options]};\n"
if {[catch {set fd [open conststrings.c w]
            puts $fd $conststrings
            close $fd} msg]} {
  user-error "Cannot write conststrings.c: $msg"
}

###############################################################################
# Definitions that are going to be substituted in Makefiles and config.h
set auto_rep {
  _*
  *_TARGETS
  BINDIR
  BUILD_DOC
  CRYPT_*
  DOMAIN
  ENABLE_*
  HAVE_*
  HOMESPOOL
  LOCALES_HACK
  MAILPATH
  MAKEDOC_FULL
  MIXMASTER
  MUTTLOCALEDIR
  NOTMUCH_API_3
  PACKAGE
  PKGDATADIR
  PKGDOCDIR
  QUEUE_*
  SENDMAIL
  SUN_ATTACHMENT
  SYSCONFDIR
  TEST_CASE_MAXSIZE
  TMPDIR
  USE_*
  VPATH
  WORDS_BIGENDIAN
}
set bare_rep {
  ICONV_CONST
  LOFF_T
  OFF_T_FMT
  SIG_ATOMIC_VOLATILE_T
}
set str_rep {
  PACKAGE_VERSION
}

###############################################################################
# Use ccache - don't do it earlier than here
if {[get-define CCACHE] ne {none}} {
  define CC "[get-define CCACHE] [get-define CC]"
  define CC_FOR_BUILD "[get-define CCACHE] [get-define CC_FOR_BUILD]"
}

###############################################################################
# Generate targets and Makefile variables for subdirectories
set vpath "\$(SRCDIR)"
foreach dir $subdirs {
  append vpath ":\$(SRCDIR)/$dir"
  define-append ALL_TARGETS all-$dir
  define-append CLEAN_TARGETS clean-$dir
  define-append INSTALL_TARGETS install-$dir
  define-append UNINSTALL_TARGETS uninstall-$dir
  define-append VPATH
}
define VPATH $vpath

###############################################################################
# Define package timestamp (UTC) based on PACKAGE_VERSION for:
# docs/neomuttrc.5, docs/neomutt.1
define PACKAGE_DATE \
  [regsub {(....)(..)(..)} [get-define PACKAGE_VERSION] {\1-\2-\3}]

###############################################################################
# Generate Makefile and config.h
define PWD [pwd]
make-template Makefile.autosetup Makefile
make-config-header config.h -auto $auto_rep -bare $bare_rep -str $str_rep

###############################################################################
# Generate .clang_complete
define cflags-one-per-line [string map {" " "\n"} [get-define CFLAGS]]
make-template .clang_complete.in

###############################################################################
# Print a summary
user-notice "Summary of build options:

  Version:               [get-define PACKAGE_VERSION]
  Host OS:               [get-define host_os]
  Install prefix:        [get-define prefix]
  Compiler:              [get-define CC]
  CFlags:                [get-define CFLAGS]
  LDFlags:               [get-define LDFLAGS]
  Libs:                  [get-define LIBS]

  GPGME:                 [yesno [get-define CRYPT_BACKEND_GPGME]]
  PGP:                   [yesno [get-define CRYPT_BACKEND_CLASSIC_PGP]]
  SMIME:                 [yesno [get-define CRYPT_BACKEND_CLASSIC_SMIME]]
  Notmuch:               [yesno [get-define USE_NOTMUCH]]
  Header Cache(s):       [get-define HCACHE_BACKENDS {}]
  Header Compression(s): [get-define COMPRESS_BACKENDS {}]
  Lua:                   [yesno [get-define USE_LUA]]
"
