#! /usr/bin/perl
# Written by Zack Weinberg <zackw at panix.com> in 2017 and 2020.
# To the extent possible under law, Zack Weinberg has waived all
# copyright and related or neighboring rights to this work.
#
# See https://creativecommons.org/publicdomain/zero/1.0/ for further
# details.

# Process libcrypt.minver and determine the minumum symbol version to
# use for compatibility symbols.  Called from configure.ac.

use v5.14;    # implicit use strict, use feature ':5.14'
use warnings FATAL => 'all';
use utf8;
use open qw(:std :utf8);
no  if $] >= 5.022, warnings => 'experimental::re_strict';
use if $] >= 5.022, re       => 'strict';

use FindBin ();
use lib ${FindBin::Bin};
use BuildCommon qw(
    ensure_C_locale
    sh_split
    popen
    subprocess_error
    which
);

sub preprocessor_check {
    my ($expr) = @_;

    state @CC;
    state @CFLAGS;
    if (!@CC) {
        @CC = which($ENV{CC} // 'cc');
        die "C compiler not available\n" unless @CC;

        @CFLAGS = sh_split($ENV{CFLAGS} // q{});

        # Remove empty elements, particularly leading ones which
        # cause issues with popen below.
        @CFLAGS = grep { $_ } @CFLAGS;

        # We call ensure_C_locale here, not from the main section,
        # because this sub might not get called at all, in which
        # case it would be unnecessary work.
        ensure_C_locale();
    }

    # FIXME: We assume the compiler understands "-fsyntax-only -xc -"
    # to mean "read standard input, interpret as C, check for errors,
    # produce no output."  This is true for all commonly used C
    # compilers on operating systems where this feature is necessary.
    print {*STDERR} "${FindBin::Bin}: testing $expr\n";

    my $ccpipe = popen('|-', @CC, @CFLAGS, '-fsyntax-only', '-xc', '-');
    print {$ccpipe} <<"EOF";
#include <limits.h>
#if !($expr)
#error nope
#endif
int avoid_empty_translation_unit;
EOF
    if (close $ccpipe) {
        return 1;    # compilation successful
    } elsif ($! == 0 && ($? & 0x7F) == 0) {
        return 0;    # compilation failed
    } else {
        subprocess_error(@CC);
    }
}

sub parse_minver_file {
    my ($minver_file, $system, $cpu) = @_;
    $system = lc $system;
    $cpu    = lc $cpu;

    local $_;
    open my $fh, '<', $minver_file
        or die "$minver_file: $!\n";
    while (<$fh>) {
        next if /^#/;
        chomp;
        s/\s+$//;
        next if $_ eq q{};

        my ($vers, $vsys, $vcpu, $ppck) = split q{ }, $_, 4;
        $vsys = lc $vsys;
        $vcpu = lc $vcpu;

        return $vers
            if $system =~ /\A$vsys/
            && $cpu =~ /\A$vcpu/
            && (!defined $ppck || preprocessor_check($ppck));
    }
    print {*STDERR} "$0: no match for ${cpu}-${system}\n";
    return 'ERROR';
}

#
# Main
#
if (scalar(@ARGV) != 3) {
    print {*STDERR} "usage: $0 libcrypt.minver host-os host-cpu\n";
    print {*STDERR} "Environment variables \$CC and \$CFLAGS are honored.\n";
    exit 2;
}

exit 0 if eval {
    print parse_minver_file(@ARGV), "\n";
    close STDOUT or die "write error: $!\n";
    1;
};

print {*STDERR} "${FindBin::Script}: $@";
exit 1;
