# 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  "20260504"
define BUGS_ADDRESS     "neomutt-devel@neomutt.org"

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

###############################################################################
# Add any user options here
set valid_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"
  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"
  # GPGME
  gpgme=0                   => "Enable GPGME support"
  # GSS (IMAP auth)
  gss=0                     => "Use GSSAPI authentication for IMAP"
  with-gss:path             => "Location of GSSAPI library"
  # SASL (IMAP and POP auth)
  gsasl=0                   => "Use the GNU SASL network security library"
  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"
# 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
  idn2=1                    => "Disable GNU libidn2 for internationalized domain names"
# PCRE2
  pcre2=0                   => "Enable PCRE2 regular expressions"
# 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"
  lmdb=0                    => "Use LMDB for the header cache"
  with-lmdb:path            => "Location of LMDB"
  qdbm=0                    => "Use QDBM for the header cache"
  rocksdb=0                 => "Use RocksDB for the header cache"
  with-rocksdb:path         => "Location of RocksDB"
  tdb=0                     => "Use TDB for the header cache"
  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"
  zlib=0                    => "Enable zlib support"
  with-zlib:path            => "Location of zlib"
  zstd=0                    => "Enable Zstandard header cache compression support"
# iconv
  with-iconv:path           => "Location of IConv"
# System
  with-sysroot:path         => "Target system root"
  with-tmpdir:=/tmp         => "location of the tmp directory"
# Misc
  paths-in-cflags=1         => "Remove paths from CFLAGS in the output of neomutt -v"
# Testing
  asan=0                    => "Enable the Address Sanitizer"
  ubsan=0                   => "Enable the Undefined Behaviour Sanitizer"
  coverage=0                => "Enable Coverage Testing"
  fuzzing                   => "Enable Fuzz Testing"
  compile-commands          => "Generate compile_commands.json (requires clang)"
# 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-keymap=0            => "DEBUG: Enable Key mappings dump"
  debug-logging=0           => "DEBUG: Enable Debug logging"
  debug-names=0             => "DEBUG: Enable Name lookup tables"
  debug-notify=0            => "DEBUG: Enable Notifications dump"
  debug-queue=0             => "DEBUG: Enable TAILQ debugging"
  debug-window=0            => "DEBUG: Enable Windows dump"
}

set deprecated_options  {
  idn:
  include-path-in-cflags:=
  mixmaster:=
  pkgconf:=
  testing:=
  with-gnutls:
  with-gpgme:
  with-gsasl:
  with-idn:=
  with-idn2:
  with-kyotocabinet:
  with-lua:
  with-lz4:
  with-mixmaster:
  with-pcre2:
  with-qdbm:
  with-slang:
  with-tdb:
  with-ui:
  with-zstd:
}

foreach dep $deprecated_options {
  append valid_options "${dep}deprecated => Deprecated\n"
}

options $valid_options
###############################################################################

###############################################################################
# Issue a warning for deprecated options on the command line
set notices {}
foreach dep $deprecated_options {
  lassign [split $dep :] name
  if {[opt-bool -nodefault $name] != -1} {
    lappend notices --$name
  }
}
if {[llength $notices]} {
  user-notice "\nThe following options are deprecated: [join $notices {, }]\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 compile-commands coverage debug-backtrace debug-color
    debug-email debug-graphviz debug-keymap debug-logging debug-names
    debug-notify debug-queue debug-window doc everything fmemopen full-doc
    fuzzing gdbm gnutls gpgme gsasl gss homespool idn2 inotify kyotocabinet
    lmdb locales-fix lua lz4 nls notmuch paths-in-cflags pcre2 pgp
    qdbm rocksdb sasl smime sqlite ssl tdb
    tokyocabinet ubsan 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 gsasl gss homespool idn2 kyotocabinet lmdb lua lz4
    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
    }
  }

  # lkill l p is the list of the elements in l that don't match p
  proc lkill {l p} {
    set res [list]
    foreach elem $l {
      if {![apply $p $elem]} {
        lappend res $elem
      }
    }
    set res
  }
}
###############################################################################

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

  # First off, require c11
  if {[cc-check-standards c11] eq {}} {
    user-error "C11 is required"
  }
  define-append CFLAGS_FOR_BUILD -std=c11
  define-append CFLAGS -std=c11
  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
  }

  # Compiler vendor
  if {[has-define {} __clang__]} {
    define COMPILER_IS_CLANG
  } elseif {[has-define {} __GNUC__]} {
    define COMPILER_IS_GCC
  } else {
    define COMPILER_IS_UNKNOWN
  }

  # GCC-specifc CFLAGS
  if {[get-define COMPILER_IS_GCC]} {
    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__
    lappend extensions -D_XOPEN_SOURCE_EXTENDED
    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
cc-check-includes signal.h

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 \
    reallocarray \
    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 sys/stat.h} {
    cc-check-members "struct stat.st_atim.tv_nsec"
  }

  cc-with {-includes time.h} {
    cc-check-members "struct tm.tm_gmtoff"
  }
}
###############################################################################

###############################################################################
# Various unconditional defines
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. Install package: libxslt / xsltproc."
  }
  if {![cc-check-progs xmlcatalog]} {
    user-error "Unable to find xmlcatalog. Install package: libxml2 / libxml2-utils"
  }

  msg-checking "Checking for DocBook DTDs and Stylesheets..."
  foreach catalog { "-//OASIS//DTD DocBook XML V4.2//EN" "http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl" } {
    if {[catch {exec xmlcatalog "" $catalog}]} {
      user-error "Install DocBook DTDs and Stylesheets 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]
  }
  pkgconf true gpgme
  pkgconf true gpg-error
  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
}

###############################################################################
# GNU SASL
if {[get-define want-gsasl] && [get-define want-sasl]} {
  user-error "Cannot specify both --gsasl and --sasl"
}
if {[get-define want-gsasl]} {
  pkgconf true libgsasl
  define USE_SASL_GNU
  define-feature SASL
}

###############################################################################
# SASL
if {[get-define want-sasl]} {
  if {[pkgconf false libsasl2]} {
    define-feature SASL
  } elseif {![check-inc-and-lib sasl [opt-val with-sasl $prefix] \
                                sasl/sasl.h sasl_encode64 sasl2]} {
    user-error "Unable to find SASL"
  }
  define USE_SASL_CYRUS
}

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

  if {![pkgconf false lua] && ![pkgconf false lua-5.4] && ![pkgconf false lua-5.3] && ![pkgconf false lua-5.2] \
                           && ![pkgconf false lua5.4]  && ![pkgconf false lua5.3]  && ![pkgconf false lua5.2]} {
    user-error "Unable to find LUA"
  }
  define USE_LUA
}

###############################################################################
# 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 terminfo}
  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 use_extended_names} {
    cc-check-function-in-lib $f $ncurses_lib
  }

  define-append CFLAGS -I$ncurses_prefix/include
  define-append LDFLAGS -L$ncurses_prefix/$::libdir_tail
}

# 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 [opt-val with-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]
}

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

###############################################################################
# TLS support
if {[get-define want-gnutls] && [get-define want-ssl]} {
  user-error "Configuration error: options '--ssl' (OpenSSL) and '--gnutls' (GnuTLS) are mutually exclusive"
}

if {[get-define want-ssl]} {
  # OpenSSL
  if {[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
  pkgconf true gnutls
  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-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"
    }
  }
  pkgconf true libidn2
  find-idn2-includes
  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"
  }
  define-feature libidn
}

###############################################################################
# PCRE2
if {[get-define want-pcre2]} {
  pkgconf true libpcre2-8
  define-feature pcre2
} elseif {[cc-check-functions 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]} {
  proc find_bdb {bdb_prefix maj min path_tail lib_names} {
    set ver_inc_dir $bdb_prefix/include/$path_tail
    set ver_lib_dir $bdb_prefix/$::libdir_tail/$path_tail
    set ver_inc_file $ver_inc_dir/db.h

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

    # 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)"
      return 0
    }
    msg-result "yes"

    # Can link?
    foreach lib_name $lib_names {
      cc-with [list -libs -L$ver_lib_dir -cflags -I$ver_inc_dir] {
        check-inc-and-lib bdb {} db.h db_env_create $lib_name
      }
      if {[have-feature bdb]} {
        define-append CFLAGS  -I$ver_inc_dir
        define-append LDFLAGS -L$ver_lib_dir
        define-append LIBS    -l$lib_name
        define-append HCACHE_BACKENDS "bdb"
        define USE_HCACHE
        return 1
      }
    }
    return 0
  }

  set bdb_versions [opt-val with-bdb-version { 18.1 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
    foreach path_tail [list "" db-$maj-$min db${maj}${min} db$maj.$min db-$maj db$maj] {
      set lib_names [list db$maj-$maj.$min db$maj-$maj db-$maj.$min db-$maj]
      if {[find_bdb $bdb_prefix $maj $min $path_tail $lib_names]} {
        break
      }
    }
    if {[have-feature bdb]} {
      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]} {
  pkgconf true qdbm
  define-feature 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 {[pkgconf false lmdb]} {
    define-feature lmdb
  } elseif {![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]} {
  pkgconf true kyotocabinet
  define-feature kc
  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]} {
  pkgconf true tdb
  define-feature tdb
  define-append HCACHE_BACKENDS "tdb"
  define USE_HCACHE
}

###############################################################################
# Header Cache - TokyoCabinet
if {[get-define want-tokyocabinet]} {
  pkgconf true tokyocabinet
  define-feature tc
  define-append HCACHE_BACKENDS "tokyocabinet"
  define USE_HCACHE
}

###############################################################################
# Header Cache Compression with LZ4
if {[get-define want-lz4]} {
  pkgconf true liblz4
  define USE_LZ4
  define-feature 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]} {
  pkgconf true libzstd
  define USE_ZSTD
  define-feature 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]"
  define USE_DEBUG_BACKTRACE 1
  define debug_build 1
}

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

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

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

# Key mappings dump
if {[get-define want-debug-keymap]} {
  define USE_DEBUG_KEYMAP 1
  define debug_build 1
}

# Logging
if {[get-define want-debug-logging]} {
  define USE_DEBUG_LOGGING 1
  define debug_build 1
}

# Name lookup tables
if {[get-define want-debug-names]} {
  define USE_DEBUG_NAMES 1
  define debug_build 1
}

# Notifications dump
if {[get-define want-debug-notify]} {
  define USE_DEBUG_NOTIFY 1
  define debug_build 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
  define debug_build 1
}

# Windows dump
if {[get-define want-debug-window]} {
  define USE_DEBUG_WINDOW 1
  define debug_build 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 -fno-omit-frame-pointer -fno-common
  define-append LDFLAGS -fsanitize=address
  define USE_ASAN
  define debug_build 1
}

###############################################################################
# Undefined Behaviour Sanitizer
if {[get-define want-ubsan]} {
  msg-checking "Checking for UBSAN..."
  if {![cctest -link 1 -cflags -fsanitize=undefined]} {
    user-error "Unable to compile with -fsanitize=undefined"
  }
  msg-result "yes"
  define-append CFLAGS -fsanitize=undefined -fno-omit-frame-pointer -fno-common
  define-append LDFLAGS -fsanitize=undefined
  define USE_UBSAN
  define debug_build 1
}

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

###############################################################################
# Generate compile_commands.json
if {[get-define want-compile-commands]} {
  if {![get-define COMPILER_IS_CLANG]} {
    user-error "The clang compiler is required to generate compile_commands.json"
  }
  define COMPILE_COMMANDS
  define-append ALL_TARGETS compile_commands.json
  define-append CLEAN_TARGETS clean-compile_commands.json
  define-append CFLAGS {-MJ$(@:.o=.o.json)}
}

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

###############################################################################
# Debug or Release build?
if {[get-define debug_build]} {
  define-append CFLAGS -g -O0
} else {
  define-append CFLAGS -O2
}

###############################################################################
# Generate conststrings.c
proc remove-paths-from-cflags {} {
  lkill [get-define CFLAGS] {{x} {
    foreach cflag {{-I} {-ffile-prefix-map=}} {
      if {[string equal -length [string length $cflag] $x $cflag]} {
        return 1
      }
    }
    return 0
  }}
}

set conststrings "\
  unsigned char cc_cflags\[\] = {[text2c [expr {
    [get-define want-paths-in-cflags]
    ? [get-define CFLAGS]
    : [remove-paths-from-cflags]
  }]]};\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 config.h
set auto_rep {
  _*
  *_TARGETS
  BINDIR
  BUILD_DOC
  COMPILE*
  CRYPT_*
  DOMAIN
  ENABLE_*
  HAVE_*
  HOMESPOOL
  LOCALES_HACK
  MAILPATH
  MAKEDOC_FULL
  MUTTLOCALEDIR
  NOTMUCH_API_3
  PACKAGE
  PKGDATADIR
  PKGDOCDIR
  QUEUE_*
  SENDMAIL
  SYSCONFDIR
  TEST_CASE_MAXSIZE
  TMPDIR
  USE_*
  VPATH
  WORDS_BIGENDIAN
}
set bare_rep {
  ICONV_CONST
  LOFF_T
  OFF_T_FMT
}
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
define VPATH "\$(SRCDIR)"
foreach dir $subdirs {
  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 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"} \
  [lkill [get-define CFLAGS] {{x} {string equal -length 3 $x "-MJ"}}]]
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]]
"
