#!/bin/sh

##  Synchronize INN files maintained in rra-c-util with upstream.
##
##  Initial version written in November 2013 by Julien ÉLIE.
##
##  Various bug fixes, code and documentation improvements since then
##  in 2014-2018, 2020-2025.
##
##  This script downloads the latest version of the files maintained
##  in the rra-c-util package that INN uses for most of its utility and
##  portability functions.  These files are available at
##  <https://www.eyrie.org/~eagle/software/rra-c-util/>.
##
##  Running this script permits keeping up-to-date most of INN utility and
##  portability functions by automatically fetching the latest version of
##  the upstream files, and putting them in the expected location in the
##  INN source code.
##  The name of the files that have been modified since the last run of the
##  script are written to standard output.  Have a look at the changes and,
##  if all looks right, commit the changes.

##  URL where the files can be downloaded.
URL_START="https://raw.githubusercontent.com/rra/rra-c-util/refs/heads/main/"

##  This function downloads the files and copies them to the expected
##  location in the INN source code, after having performed a few minimal
##  changes to integrate them in the INN build system.
##  If a file is not found in upstream or has been modified since the last
##  run of the script, write it on standard output.
##
##  This function expects the following arguments:
##    $1 - full path of the file to download in the upstream package
##    $2 - directory where the file should be copied in INN source code
##    $3 - name of the file in INN source code
download() {
    TEMP=$3.tmp

    rm -f ${TEMP}
    wget -q "${URL_START}$1" -O ${TEMP}

    if [ ! -s "${TEMP}" ]; then
        echo "File $1 does not exist in upstream package"
    else
        # Update the path of included C header files.
        # Change "RRA_" to "INN_", "rra_" to "inn_", and "UTIL_" to "INN_"
        # so as to keep a homogeneous naming convention in INN source code.
        # Also remove the first 2 lines containing an internal serial number.
        # Changes in shell, m4 and C files are not the same.
        if [ "$2" = "m4" ]; then
            sed -i -e 's/RRA_/INN_/g' \
                -e 's/rra_/inn_/g' \
                -e '1,2d' \
                ${TEMP}

            # Remove useless function for INN.
            if [ "$3" = "cc-flags.m4" ]; then
                sed -i -e '/dnl Provides INN_PROG_CC_WARNINGS_FLAGS/,+6d' \
                    -e '/dnl Determine the full set/,$d' \
                    -e '/test x"$CLANG" = xyes/,+1d' \
                    ${TEMP}
                sed -i -e '$d' ${TEMP}
            elif [ "$3" = "sqlite3.m4" ]; then
                sed -i -e '49 i \
dnl Checks if SQLite v3 is present. The single argument, if "true", says to\
dnl fail if the SQLite library could not be found. We do not use pkg-config\
dnl like the upstream version of this file as we have a specific check to\
dnl ensure that SQLite v3 meets our minimum version requirement.\
AC_DEFUN([_INN_LIB_SQLITE3_INTERNAL],\
[AC_CACHE_CHECK([for a sufficiently recent SQLite],\
    [inn_cv_have_sqlite3],\
    [INN_LIB_HELPER_PATHS([SQLITE3])\
     INN_LIB_SQLITE3_SWITCH\
     LIBS="-lsqlite3 $LIBS"\
     AC_COMPUTE_INT([_INN_LIB_SQLITE3_VERSION], [SQLITE_VERSION_NUMBER],\
        [#include <sqlite3.h>])\
     AS_IF([test "x$_INN_LIB_SQLITE3_VERSION" != x],\
        [AS_IF([test "$_INN_LIB_SQLITE3_VERSION" -ge 3008002],\
            [inn_cv_have_sqlite3=yes],\
            [inn_cv_have_sqlite3=no])],\
        [inn_cv_have_sqlite3=no])\
     INN_LIB_SQLITE3_RESTORE])\
 AS_IF([test x"$inn_cv_have_sqlite3" = xyes],\
    [SQLITE3_LIBS="-lsqlite3"],\
    [AS_IF([test x"$1" = xtrue],\
        [AC_MSG_ERROR([cannot find usable SQLite v3 library])])])])\
' \
                    -e '49,71d' \
                    ${TEMP}
            fi
        elif [ "$2" = "include/inn" ] || [ "$2" = "include/portable" ] \
            || [ "$2" = "include" ] || [ "$2" = "lib" ] || [ "$2" = "tests/lib" ] \
            || [ "$2" = "tests/lib/network" ] || [ "$2" = "tests/tap" ]; then
            if [ "$3" = "xmalloc.t" ]; then
                # Path should be updated in this shell script.
                sed -i -e 's/\$C_TAP_BUILD\/util/\$C_TAP_BUILD\/lib/g' \
                    ${TEMP}
                chmod 755 ${TEMP}
            else
                if [ "$3" = "network.c" ]; then
                    # Apply specific changes to take into account
                    # how sourceaddress and sourceaddress6 work.
                    sed -i -e '61 i \
#include "inn/innconf.h"' \
                        -e '456 s/)$/ \&\& innconf == NULL)/' \
                        -e '457,458d' \
                        -e '463 i \
        if (source == NULL && innconf != NULL)\
            source = innconf->sourceaddress;\
        if (source == NULL || strcmp(source, "all") == 0\
            || strcmp(source, "any") == 0)\
            return true;\
' \
                        -e '476 i \
        if (source == NULL && innconf != NULL)\
            source = innconf->sourceaddress6;\
        if (source == NULL || strcmp(source, "all") == 0\
            || strcmp(source, "any") == 0)\
            return true;\
' \
                        ${TEMP}
                fi

                if [ "$3" = "sd-daemon.h" ]; then
                    sed -i -e '53 i \
/*\
 * See portable/system.h for more information about the following code.\
 *\
 * systemd/sd-daemon.h includes linux/posix_types.h which unconditionally\
 * redefines __FD_SETSIZE to 1024 (even if already set), so we need redefining\
 * it again, if wanted.\
 */\
#if defined(HAVE_SD_NOTIFY) && defined(__linux__) && LARGE_FD_SETSIZE > 1024\
#    undef __FD_SETSIZE\
#    define __FD_SETSIZE LARGE_FD_SETSIZE\
#endif\
' \
                        ${TEMP}
                fi

                if [ "$3" = "socket.h" ]; then
                    sed -i -e 's/  If we.*visibility//g' \
                        -e '190d' \
                        -e 's/__attribute__((__visibility__("hidden")))//g' \
                        ${TEMP}
                fi

                # Remove the part about CONFIG_H_INCLUDED.
                if [ "$3" = "stdbool.h" ]; then
                    sed -i -e '26,30d;32d' \
                        ${TEMP}
                fi

                if [ "$3" = "system.h" ]; then
                    # Add INN-specific stuff.
                    sed -i -e '49 i \
/*\
 * Provide a way to increase FD_SETSIZE at build time for servers needing more\
 * than the usual default of 1024 file descriptors.\
 * It otherwise leads to corruption of the tracking of open file descriptors\
 * in programs like innd because fd_set is not large enough.\
 * Use for instance -DLARGE_FD_SETSIZE=4096 at build time to increase the\
 * limit to 4096.\
 *\
 * FD_SETSIZE cannot be increased on Linux, but __FD_SETSIZE can with\
 * glibc 2.2 and also probably later versions.  We do this by including\
 * bits/types.h which defines __FD_SETSIZE first (before any other include),\
 * then we redefine __FD_SETSIZE.  Of course, a user program may *never*\
 * include bits/whatever.h directly, so this is a dirty hack!\
 * We assume that on systems other than Linux, just defining FD_SETSIZE works.\
 * Idea taken from Diablo which also has running instances needing more than\
 * 1024 files descriptors.\
 * Naturally, the right long-term solution is to switch to libevent which\
 * handles select/poll/epoll/etc. and does not suffer from the hard-coded\
 * fd_set size for select(2) calls.\
 */\
#if LARGE_FD_SETSIZE > 1024\
#    if defined(__linux__)\
#        include <features.h>\
#        if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)\
#            include <bits/types.h>\
#            undef __FD_SETSIZE\
#            define __FD_SETSIZE LARGE_FD_SETSIZE\
#        endif\
#    else\
#        define FD_SETSIZE LARGE_FD_SETSIZE\
#    endif\
#endif\
' \
                        -e "108 i \\
/*\\
 * This almost certainly isn't necessary, but it's not hurting anything.\\
 * gcc assumes that if SEEK_SET isn't defined none of the rest are either,\\
 * so we certainly can as well.\\
 */\\
#ifndef SEEK_SET\\
#    define SEEK_SET 0\\
#    define SEEK_CUR 1\\
#    define SEEK_END 2\\
#endif\\
" \
                        -e "135 i \\
/*\\
 * Handle defining fseeko and ftello.  If HAVE_FSEEKO is defined, the system\\
 * header files take care of this for us.  Otherwise, see if we're building\\
 * with large file support.  If we are, we have to provide some real fseeko\\
 * and ftello implementation; declare them if they're not already declared and\\
 * we'll use replacement versions in libinn.  If we're not using large files,\\
 * we can safely just use fseek and ftell.\\
 *\\
 * We'd rather use fseeko and ftello unconditionally, even when we're not\\
 * building with large file support, since they're a better interface.\\
 * Unfortunately, they're available but not declared on some systems unless\\
 * building with large file support, the AC_FUNC_FSEEKO Autoconf function\\
 * always turns on large file support, and our fake declarations won't work on\\
 * some systems (like HP_UX).  This is the best compromise we've been able to\\
 * come up with.\\
 */\\
#if !HAVE_FSEEKO\\
#    if DO_LARGEFILES\\
#        if !HAVE_DECL_FSEEKO\\
extern int fseeko(FILE *, off_t, int);\\
#        endif\\
#        if !HAVE_DECL_FTELLO\\
extern off_t ftello(FILE *);\\
#        endif\\
#    else\\
#        define fseeko(f, o, w) fseek((f), (long) (o), (w))\\
#        define ftello(f)       ftell(f)\\
#    endif\\
#endif\\
\\
/* Other prototypes. */\\
#if !HAVE_DECL_PREAD\\
extern ssize_t pread(int, void *, size_t, off_t);\\
#endif\\
#if !HAVE_DECL_PWRITE\\
extern ssize_t pwrite(int, const void *, size_t, off_t);\\
#endif\\
#if !HAVE_SYMLINK\\
extern int symlink(const char *, const char *);\\
#endif\\
" \
                        -e '150 i \
#if !HAVE_DECL_SNPRINTF\
extern int snprintf(char *, size_t, const char *, ...)\
    __attribute__((__format__(printf, 3, 4)));\
#endif\
#if !HAVE_DECL_VSNPRINTF\
extern int vsnprintf(char *, size_t, const char *, va_list)\
    __attribute__((__format__(printf, 3, 0)));\
#endif' \
                        -e '167 i \
#if !HAVE_DECL_STRLCAT\
extern size_t strlcat(char *, const char *, size_t);\
#endif\
#if !HAVE_DECL_STRLCPY\
extern size_t strlcpy(char *, const char *, size_t);\
#endif\
#if !HAVE_GETPAGESIZE\
extern int getpagesize(void);\
#endif\
#if !HAVE_STRCASECMP\
extern int strcasecmp(const char *, const char *);\
extern int strncasecmp(const char *, const char *, size_t);\
#endif\
#if !HAVE_STRSPN\
extern size_t strspn(const char *, const char *);\
#endif\
#if !HAVE_STRTOK\
extern char *strtok(char *, const char *);\
#endif' \
                        -e '/#if !HAVE_ISSETUGID/,+2d' \
                        -e '/#if !HAVE_STRNDUP/,+2d' \
                        ${TEMP}
                fi

                # Keep inn/system.h as it uses INN_ macros.
                if [ "$3" = "xmalloc.h" ]; then
                    sed -i -e 's/<config\.h>$/"inn\/system.h"/g' \
                        ${TEMP}
                fi

                sed -i -e 's/UTIL_/INN_/g' \
                    -e '/^\/\* Default to a hidden visibility for all .* functions\. \*\/$/,+2d' \
                    -e '/^\/\* Undo default visibility change\. \*\/$/,+2d' \
                    -e '/^#.*pragma GCC visibility /d' \
                    -e 's/<config\.h>$/"config.h"/g' \
                    -e 's/<util\/\(.*\)>/"inn\/\1"/g' \
                    -e 's/<portable\/\(.*\)>/"portable\/\1"/g' \
                    -e 's/<tests\/tap\/\(.*\)>/"tap\/\1"/g' \
                    -e 's/<tests\/util\/\(.*\)>/"lib\/\1"/g' \
                    ${TEMP}

                # For compatibility with the integration of C TAP Harness
                # in INN.
                if [ "$2" = "tests/lib" ] || [ "$2" = "tests/lib/network" ] \
                    || [ "$3" = "process.c" ]; then
                    case "$3" in
                    fakewrite.c) ;;

                    xmalloc.c) ;;

                    *.c)
                        sed -i -e "0,/^$/{s/^$/\n#define LIBTEST_NEW_FORMAT 1\n/}" \
                            ${TEMP}
                        ;;
                    esac
                fi

                # Rename a few symbols defined by autoconf since they're too
                # likely to conflict with other packages.
                # Please see support/mksystem for more information.
                if [ "$2" = "include/inn" ]; then
                    sed -i -e 's/\([^_]\)HAVE_C99_VAMACROS/\1INN_HAVE_C99_VAMACROS/g' \
                        -e 's/\([^_]\)HAVE_GNU_VAMACROS/\1INN_HAVE_GNU_VAMACROS/g' \
                        -e 's/\([^_]\)HAVE_INET6/\1INN_HAVE_INET6/g' \
                        -e 's/\([^_]\)HAVE_INTTYPES_H/\1INN_HAVE_INTTYPES_H/g' \
                        -e 's/\([^_]\)HAVE_MSYNC_3_ARG/\1INN_HAVE_MSYNC_3_ARG/g' \
                        -e 's/\([^_]\)HAVE_STDBOOL_H/\1INN_HAVE_STDBOOL_H/g' \
                        -e 's/\([^_]\)HAVE_SYS_BITYPES_H/\1INN_HAVE_SYS_BITYPES_H/g' \
                        -e 's/\([^_]\)HAVE__BOOL/\1INN_HAVE__BOOL/g' \
                        -e 's/"portable\/\(.*\)"/"inn\/portable-\1"/g' \
                        -e '/"config\.h"$/d' \
                        ${TEMP}
                fi
            fi
        fi

        mv -f ${TEMP} ../$2/$3

        case "$3" in
        *.c | *.h)
            clang-format --style=file -i ../$2/$3
            ;;
        esac

        # Output modified files.
        GIT_STATUS=$(git status --short ../$2/$3)
        if [ -n "${GIT_STATUS}" ]; then
            echo ${GIT_STATUS}
            if [ "$3" = "network.c" ] || [ "$3" = "network.h" ]; then
                echo "Changes may need backporting in network-innbind.[ch]"
            fi
        fi
    fi
}

##  Synchronize Autoconf macros from upstream.
download m4/bdb.m4 m4 bdb.m4
download m4/cc-flags.m4 m4 cc-flags.m4
download m4/inet-ntoa.m4 m4 inet-ntoa.m4
download m4/krb5-config.m4 m4 krb5-config.m4
download m4/krb5.m4 m4 krb5.m4
download m4/lib-depends.m4 m4 lib-depends.m4
download m4/lib-helper.m4 m4 lib-helper.m4
download m4/lib-pathname.m4 m4 lib-pathname.m4
download m4/openssl.m4 m4 openssl.m4
download m4/pam-const.m4 m4 pam-const.m4
download m4/perl.m4 m4 perl.m4
download m4/python.m4 m4 python.m4
download m4/sasl.m4 m4 sasl.m4
download m4/socket-unix.m4 m4 socket-unix.m4
download m4/socket.m4 m4 socket.m4
download m4/sqlite3.m4 m4 sqlite3.m4
download m4/systemd.m4 m4 systemd.m4
download m4/vamacros.m4 m4 vamacros.m4
download m4/zlib.m4 m4 zlib.m4

##  Synchronize portability functions from upstream.
download portable/asprintf.c lib asprintf.c
download portable/daemon.c lib daemon.c
download portable/getaddrinfo.c lib getaddrinfo.c
download portable/getaddrinfo.h include/portable getaddrinfo.h
download portable/getnameinfo.c lib getnameinfo.c
download portable/getnameinfo.h include/portable getnameinfo.h
download portable/inet_aton.c lib inet_aton.c
download portable/inet_ntoa.c lib inet_ntoa.c
download portable/inet_ntop.c lib inet_ntop.c
download portable/macros.h include/portable macros.h
download portable/mkstemp.c lib mkstemp.c
download portable/reallocarray.c lib reallocarray.c
download portable/sd-daemon.c lib sd-daemon.c
download portable/sd-daemon.h include/portable sd-daemon.h
download portable/setenv.c lib setenv.c
download portable/seteuid.c lib seteuid.c
download portable/socket.h include/portable socket.h
download portable/socket-unix.h include/portable socket-unix.h
download portable/stdbool.h include/portable stdbool.h
download portable/system.h include/portable system.h
download portable/uio.h include/portable uio.h

##  Synchronize utility functions from upstream.
download util/buffer.c lib buffer.c
download util/buffer.h include/inn buffer.h
download util/fdflag.c lib fdflag.c
download util/fdflag.h include/inn fdflag.h
download util/macros.h include/inn macros.h
download util/messages.c lib messages.c
download util/messages.h include/inn messages.h
download util/network.c lib network.c
download util/network.h include/inn network.h
download util/vector.c lib vector.c
download util/vector.h include/inn vector.h
download util/xmalloc.c lib xmalloc.c
download util/xmalloc.h include/inn xmalloc.h
download util/xwrite.c lib xwrite.c
download util/xwrite.h include/inn xwrite.h

##  Synchronize test suite files for portability functions from upstream.
download tests/portable/asprintf-t.c tests/lib asprintf-t.c
download tests/portable/daemon-t.c tests/lib daemon-t.c
download tests/portable/getaddrinfo-t.c tests/lib getaddrinfo-t.c
download tests/portable/getnameinfo-t.c tests/lib getnameinfo-t.c
download tests/portable/inet_aton-t.c tests/lib inet_aton-t.c
download tests/portable/inet_ntoa-t.c tests/lib inet_ntoa-t.c
download tests/portable/inet_ntop-t.c tests/lib inet_ntop-t.c
download tests/portable/mkstemp-t.c tests/lib mkstemp-t.c
download tests/portable/reallocarray-t.c tests/lib reallocarray-t.c
download tests/portable/setenv-t.c tests/lib setenv-t.c

##  Synchronize test suite files for utility functions from upstream.
download tests/util/buffer-t.c tests/lib buffer-t.c
download tests/util/fakewrite.c tests/lib fakewrite.c
download tests/util/fakewrite.h tests/lib fakewrite.h
download tests/util/fdflag-t.c tests/lib fdflag-t.c
download tests/util/messages-t.c tests/lib messages-t.c
download tests/util/vector-t.c tests/lib vector-t.c
download tests/util/xmalloc-t tests/lib xmalloc.t
download tests/util/xmalloc.c tests/lib xmalloc.c
download tests/util/xwrite-t.c tests/lib xwrite-t.c
download tests/util/network/addr-ipv4-t.c tests/lib/network addr-ipv4-t.c
download tests/util/network/addr-ipv6-t.c tests/lib/network addr-ipv6-t.c
download tests/util/network/client-t.c tests/lib/network client-t.c
download tests/util/network/server-t.c tests/lib/network server-t.c

##  Synchronize useful utility functions used for the test suite from upstream.
download tests/tap/messages.c tests/tap messages.c
download tests/tap/messages.h tests/tap messages.h
download tests/tap/process.c tests/tap process.c
download tests/tap/process.h tests/tap process.h
download tests/tap/string.c tests/tap string.c
download tests/tap/string.h tests/tap string.h
