diff options
Diffstat (limited to 'doc')
-rw-r--r-- | doc/examples/package.cpp | 61 | ||||
-rw-r--r-- | doc/log.txt | 13 | ||||
-rwxr-xr-x | doc/scripts/configure.pl | 2338 |
3 files changed, 2412 insertions, 0 deletions
diff --git a/doc/examples/package.cpp b/doc/examples/package.cpp new file mode 100644 index 000000000..981abaa31 --- /dev/null +++ b/doc/examples/package.cpp @@ -0,0 +1,61 @@ + +#include <botan/botan.h> +#include <botan/serpent.h> +#include <botan/package.h> + +#include <iostream> +#include <fstream> +#include <vector> + +using namespace Botan; + +std::vector<byte> slurp_file(const std::string& filename) + { + std::ifstream in(filename.c_str()); + + std::vector<byte> out; + byte buf[4096] = { 0 }; + + while(in.good()) + { + in.read((char*)buf, sizeof(buf)); + ssize_t got = in.gcount(); + + out.insert(out.end(), buf, buf+got); + } + + return out; + } + +int main(int argc, char* argv[]) + { + if(argc != 2) + { + std::cout << "Usage: " << argv[0] << " filename\n"; + return 1; + } + + LibraryInitializer init; + + AutoSeeded_RNG rng; + + BlockCipher* cipher = new Serpent; + + std::vector<byte> input = slurp_file(argv[1]); + std::vector<byte> output(input.size() + cipher->BLOCK_SIZE); + + AllOrNothingTransform::package(rng, new Serpent, + &input[0], input.size(), + &output[0]); + + std::vector<byte> unpackage_output(output.size() - cipher->BLOCK_SIZE); + + AllOrNothingTransform::unpackage(new Serpent, + &output[0], output.size(), + &unpackage_output[0]); + + if(unpackage_output == input) + std::cout << Package/unpackage worked\n"; + else + std::cout << "Something went wrong :(\n"; + } diff --git a/doc/log.txt b/doc/log.txt index 7183bd8b6..4fa9da368 100644 --- a/doc/log.txt +++ b/doc/log.txt @@ -1,4 +1,17 @@ +* 1.9.1-pre, 2009-??-?? + - Enable SSE2 optimizations under Visual C++ + - Alter Skein-512 to match the tweaked 1.2 specification + +* 1.9.0, 2009-09-09 + - Add support for parallel invocation of block ciphers where possible + - Add SSE2 implementation of Serpent + - Add Rivest's package transform (an all or nothing transform) + - Minor speedups to the Turing key schedule + - Fix processing multiple messages in XTS mode + - Add --no-autoload option to configure.py, for minimized builds + - The previously used configure.pl script is no longer supported + * 1.8.8-dev, 2009-??-?? - Alter Skein-512 to match the tweaked 1.2 specification diff --git a/doc/scripts/configure.pl b/doc/scripts/configure.pl new file mode 100755 index 000000000..bd7e41a3e --- /dev/null +++ b/doc/scripts/configure.pl @@ -0,0 +1,2338 @@ +#!/usr/bin/perl -w + +require 5.006; + +use strict; + +use Config; +use Getopt::Long; +use File::Spec; +use File::Copy; +use File::Find; +use Sys::Hostname; + +my $MAJOR_VERSION = 1; +my $MINOR_VERSION = 8; +my $PATCH_VERSION = 7; + +my $VERSION_SUFFIX = ''; + +my $SO_PATCH_VERSION = 2; + +my $VERSION_STRING = "$MAJOR_VERSION.$MINOR_VERSION.$PATCH_VERSION$VERSION_SUFFIX"; +my $SO_VERSION_STRING = "$MAJOR_VERSION.$MINOR_VERSION.$SO_PATCH_VERSION$VERSION_SUFFIX"; + +################################################## +# Data # +################################################## +my (%CPU, %OPERATING_SYSTEM, %COMPILER, %MODULES); + +my @DOCS = ( + 'api.pdf', 'tutorial.pdf', 'fips140.pdf', + 'api.tex', 'tutorial.tex', 'fips140.tex', + 'credits.txt', 'license.txt', 'log.txt', + 'thanks.txt', 'todo.txt', 'pgpkeys.asc'); + +my $TRACING = 0; + +################################################## +# Run main() and Quit # +################################################## +my $config = {}; + +main(); +exit; + +sub exec_uname { + # Only exec it if we think it might actually work + if(-f '/bin/uname' || -f '/usr/bin/uname' || -f '/bin/sh') { + my $uname = `uname -a`; + if($uname) { + chomp $uname; + return $uname; + } + } + + return ''; +} + +sub deprecation_warning { + warning("$0 is deprecated; migration to ./configure.py strongly recommended"); +} + +################################################## +# Main Driver # +################################################## +sub main { + my $base_dir = where_am_i(); + + deprecation_warning(); + + $$config{'uname'} = exec_uname(); + + $$config{'base-dir'} = $base_dir; + $$config{'src-dir'} = File::Spec->catdir($base_dir, 'src'); + $$config{'checks-dir'} = File::Spec->catdir($base_dir, 'checks'); + $$config{'doc_src_dir'} = File::Spec->catdir($base_dir, 'doc'); + + $$config{'config-dir'} = + File::Spec->catdir($$config{'src-dir'}, 'build-data'); + + $$config{'command_line'} = $0 . ' ' . join(' ', @ARGV); + $$config{'timestamp'} = gmtime; + $$config{'user'} = getlogin || getpwuid($<) || ''; + $$config{'hostname'} = hostname; + + %CPU = read_info_files($config, 'arch', \&get_arch_info); + %OPERATING_SYSTEM = read_info_files($config, 'os', \&get_os_info); + %COMPILER = read_info_files($config, 'cc', \&get_cc_info); + %MODULES = read_module_files($config); + + add_to($config, { + 'version_major' => $MAJOR_VERSION, + 'version_minor' => $MINOR_VERSION, + 'version_patch' => $PATCH_VERSION, + 'version' => $VERSION_STRING, + 'so_version' => $SO_VERSION_STRING, + }); + + get_options($config); + + my $default_value_is = sub { + my ($var, $val) = @_; + $$config{$var} = $val if not defined($$config{$var}); + }; + + &$default_value_is('gcc_bug', 0); + &$default_value_is('autoconfig', 1); + &$default_value_is('debug', 0); + &$default_value_is('shared', 'yes'); + &$default_value_is('local_config', ''); + + # Goes into build-specific dirs (maybe) + + $$config{'build_dir'} = 'build'; + $$config{'botan_config'} = File::Spec->catfile( + $$config{'build_dir'}, 'botan-config'); + + $$config{'botan_pkgconfig'} = File::Spec->catfile( + $$config{'build_dir'}, + 'botan-' . $MAJOR_VERSION . '.' . $MINOR_VERSION . '.pc'); + + $$config{'makefile'} = 'Makefile'; + $$config{'check_prefix'} = ''; + $$config{'lib_prefix'} = ''; + + if(defined($$config{'with_build_dir'})) { + for my $var ('build_dir', + 'botan_config', + 'botan_pkgconfig', + 'makefile', + 'check_prefix', + 'lib_prefix') + { + $$config{$var} = File::Spec->catfile($$config{'with_build_dir'}, + $$config{$var}); + } + } + else { + } + + choose_target($config); + + my $os = $$config{'os'}; + my $cc = $$config{'compiler'}; + + &$default_value_is('prefix', os_info_for($os, 'install_root')); + &$default_value_is('libdir', os_info_for($os, 'lib_dir')); + &$default_value_is('docdir', os_info_for($os, 'doc_dir')); + &$default_value_is('make_style', $COMPILER{$cc}{'makefile_style'}); + + scan_modules($config); + + print_enabled_modules($config); + + add_to($config, { + 'includedir' => os_info_for($os, 'header_dir'), + + 'build_lib' => File::Spec->catdir($$config{'build_dir'}, 'lib'), + 'build_check' => File::Spec->catdir($$config{'build_dir'}, 'checks'), + 'build_include' => + File::Spec->catdir($$config{'build_dir'}, 'include'), + 'build_include_botan' => + File::Spec->catdir($$config{'build_dir'}, 'include', 'botan'), + + 'mp_bits' => find_mp_bits($config), + 'mod_libs' => [ using_libs($config) ], + + 'sources' => { }, + 'includes' => { }, + + 'check_src' => { + map_to($$config{'checks-dir'}, + grep { $_ ne 'keys' and !m@\.(dat|h)$@ } + dir_list($$config{'checks-dir'})) + } + }); + + load_modules($config); + + my @dirs = mkdirs($$config{'build_dir'}, + $$config{'build_include'}, + $$config{'build_include_botan'}, + $$config{'build_lib'}, + $$config{'build_check'}); + + #autoconfig('Created ' . join(' ', @dirs)) if @dirs; + + write_pkg_config($config); + + determine_config($config); + + process_template(File::Spec->catfile($$config{'config-dir'}, 'buildh.in'), + File::Spec->catfile($$config{'build_dir'}, 'build.h'), + $config); + + process_template(File::Spec->catfile( + $$config{'config-dir'}, 'botan.doxy.in'), + File::Spec->catfile($$config{'doc_src_dir'}, 'botan.doxy'), + $config); + + $$config{'includes'}{'build.h'} = $$config{'build_dir'}; + + generate_makefile($config); + + copy_include_files($config); + + deprecation_warning(); +} + +sub where_am_i { + my ($volume,$dir,$file) = File::Spec->splitpath($0); + my $src_dir = File::Spec->catpath($volume, $dir, ''); + return $src_dir if $src_dir; + return File::Spec->curdir(); +} + +################################################## +# Diagnostics # +################################################## +sub with_diagnostic { + my ($type, @args) = @_; + + my $args = join('', @args); + my $str = "($type): "; + while(length($str) < 14) { $str = ' ' . $str; } + + $str .= $args . "\n"; + return $str; +} + +sub croak { + die with_diagnostic('error', @_); +} + +sub warning { + warn with_diagnostic('warning', @_); +} + +sub autoconfig { + print with_diagnostic('autoconfig', @_); +} + +sub emit_help { + print join('', @_); + exit; +} + +sub trace { + return unless $TRACING; + + my (undef, undef, $line) = caller(0); + my (undef, undef, undef, $func) = caller(1); + + $func =~ s/main:://; + + print with_diagnostic('trace', "at $func:$line - ", @_); +} + +################################################## +# Display Help and Quit # +################################################## +sub display_help { + sub module_sets { + my %modsets; + for my $name (sort keys %MODULES) { + my %info = %{$MODULES{$name}}; + next unless (defined($info{'modset'})); + + for my $s (split(/,/, $info{'modset'})) { + $modsets{$s} = undef; + } + } + + return sort keys %modsets; + } + + my $sets = join(' ', module_sets()); + + my $listing = sub { + my (@list) = @_; + + return '' if (@list == 0); + + my ($output, $len) = ('', 0); + + my $append = sub { + my ($to_append) = @_; + $output .= $to_append; + $len += length $to_append; + }; + + foreach my $name (sort @list) { + next if $name eq 'defaults'; + if($len > 58) { + $output .= "\n "; + $len = 8; + } + &$append($name . ' '); + } + chop $output; + return $output; + }; + + #my $modules = &$listing(keys %MODULES); + my $compilers = &$listing(keys %COMPILER); + my $oses = &$listing(keys %OPERATING_SYSTEM); + my $cpus = &$listing(keys %CPU); + + my $helptxt = <<ENDOFHELP; +This is $0 from Botan $VERSION_STRING + +To select the compiler, use + + --cc=[$compilers] + +To select the OS and processor to target, use these options. By +default, autodetection will be attempted. + + --os=[generic $oses] + --cpu=[generic $cpus] + + --with-endian=[little big none] + --with-unaligned-mem=[yes no] + +To change build options: + + --with-tr1={none,system,boost} enable (or disable) using a TR1 implementation + --with-build-dir=DIR setup the build in DIR + --with-local-config=FILE include the contents of FILE into build.h + + --disable-debug don't worry about debugging + --enable-debug set compiler flags for debugging + + --enable-shared enable shared libraries + --disable-shared don't build shared libararies + +To change where the library is installed: + + --prefix=PATH set the base installation directory + --libdir=PATH install library files in \${prefix}/\${libdir} + --docdir=PATH install documentation in \${prefix}/\${docdir} + +To change what modules to use: + + --enable-modules=[module,[module[,...]]] + --disable-modules=[module,[module[,...]]] + +To get diagnostic and debug output: + + --module-info display more information about modules + + --show-arch-info=CPU show more information about a particular CPU + [$cpus] + + --help display this help + --version display the version of Botan + --quiet display only warnings and errors + --trace enable runtime tracing of this program + +See doc/building.pdf for more information about this program. + +ENDOFHELP + + emit_help($helptxt); +} + +################################################## +# Display Further Information about Modules # +################################################## +sub module_info { + + my $info = ''; + foreach my $mod (sort keys %MODULES) { + my $modinfo = $MODULES{$mod}; + my $fullname = $$modinfo{'realname'}; + + while(length($mod) < 10) { $mod .= ' '; } + $info .= "$mod - $fullname\n"; + } + + return $info; +} + +################################################## +# +################################################## +sub choose_target { + my ($config) = @_; + + my $cc = $$config{'compiler'}; + my $os = $$config{'os'}; + my $cpu = $$config{'cpu'}; + + autoconfig("Setting up build for Botan $VERSION_STRING"); + + $cpu = guess_cpu() if not defined($cpu); + $cc = guess_compiler() if not defined($cc); + $os = guess_os() if not defined($os); + + display_help() + unless(defined($cc) and defined($os) and defined($cpu)); + + croak("Compiler $cc isn't known (try --help)") + unless defined($COMPILER{$cc}); + + my %ccinfo = %{$COMPILER{$cc}}; + + if(defined($ccinfo{'compiler_has_tr1'})) { + unless(defined($$config{'tr1'})) { + autoconfig('Assuming compiler ', $cc, ' has TR1 headers. ', + 'Use --with-tr1=none to disable'); + $$config{'tr1'} = 'system'; + } + } + + $os = os_alias($os); + croak("OS $os isn't known (try --help)") unless + ($os eq 'generic' or defined($OPERATING_SYSTEM{$os})); + + my ($arch, $submodel) = figure_out_arch($cpu); + + # hacks + if($cc eq 'gcc') { + $ccinfo{'binary_name'} = 'c++' if($os eq 'darwin'); + + if($$config{'gcc_bug'} != 1) { + my $binary = $ccinfo{'binary_name'}; + + my $gcc_version = `$binary -v 2>&1`; + + $gcc_version = '' if not defined $gcc_version; + + my $has_ll_bug = 0; + $has_ll_bug = 1 if($gcc_version =~ /4\.[01234]/); + $has_ll_bug = 1 if($gcc_version =~ /3\.[34]/); + $has_ll_bug = 1 if($gcc_version =~ /2\.95\.[0-4]/); + $has_ll_bug = 1 if($gcc_version eq ''); + + $has_ll_bug = 0 if($arch eq 'alpha' or $arch =~ /.*64$/); + + if($has_ll_bug) + { + warning('Enabling -fpermissive to work around ', + 'possible GCC bug'); + + $$config{'gcc_bug'} = 1; + } + + warning('GCC 2.95.x issues many spurious warnings') + if($gcc_version =~ /2\.95\.[0-4]/); + } + } + + trace("using $cc $os $arch $submodel"); + + add_to($config, { + 'compiler' => $cc, + 'os' => $os, + 'arch' => $arch, + 'submodel' => $submodel, + }); +} + +sub module_runs_on { + my ($config, $modinfo, $mod, $noisy) = @_; + + my $cc = $$config{'compiler'}; + my $os = $$config{'os'}; + my $submodel = $$config{'submodel'}; + my $arch = $$config{'arch'}; + + my %modinfo = %{$modinfo}; + + my $realname = $modinfo{'realname'}; + + my @arch_list = @{ $modinfo{'arch'} }; + if(scalar @arch_list > 0 && !in_array($arch, \@arch_list) && + !in_array($submodel, \@arch_list)) + { + autoconfig("$mod ($realname): skipping, " . + "not compatible with " . realname($arch) . + "/" . $submodel) if $noisy; + return 0; + } + + my @os_list = @{ $modinfo{'os'} }; + if(scalar @os_list > 0 && !in_array($os, \@os_list)) + { + autoconfig("$mod ($realname): " . + "skipping, not compatible with " . realname($os)) if $noisy; + return 0; + } + + my @cc_list = @{ $modinfo{'cc'} }; + if(scalar @cc_list > 0 && !in_array($cc, \@cc_list)) { + autoconfig("$mod ($realname): " . + "skipping, not compatible with " . realname($cc)) if $noisy; + return 0; + } + + + if($modinfo{'uses_tr1'} eq 'yes') { + return 0 unless defined($$config{'tr1'}); + + my $tr1 = $$config{'tr1'}; + return 0 unless($tr1 eq 'system' or $tr1 eq 'boost'); + } + + return 1; +} + +sub scan_modules { + my ($config) = @_; + + foreach my $mod (sort keys %MODULES) { + my %modinfo = %{ $MODULES{$mod} }; + + next if(defined($$config{'modules'}{$mod}) && $$config{'modules'}{$mod} < 0); + + next unless(module_runs_on($config, \%modinfo, $mod, 0)); + + if($modinfo{'load_on'} eq 'auto' or + $modinfo{'load_on'} eq 'always' or + ($modinfo{'load_on'} eq 'asm_ok' and $$config{'asm_ok'})) { + + my %maybe_load = (); + my $all_deps_found = 1; + + LINE: foreach (@{$modinfo{'requires'}}) { + for my $req_mod (split(/\|/, $_)) { + next unless defined $MODULES{$req_mod}; + + next if(defined($$config{'modules'}{$req_mod}) && $$config{'modules'}{$req_mod} < 0); + next unless(module_runs_on($config, $MODULES{$req_mod}, $req_mod, 0)); + + $maybe_load{$req_mod} = 1; + next LINE; + } + $all_deps_found = 0; + } + + if($all_deps_found) { + foreach my $depmod (keys %maybe_load) + { $$config{'modules'}{$depmod} = 1; } + $$config{'modules'}{$mod} = 1; + } + } + } +} + +sub print_enabled_modules { + my ($config) = @_; + + return unless($$config{'verbose'}); + + my %by_type; + + foreach my $mod (sort keys %MODULES) { + my $type = $MODULES{$mod}{'type'}; + + my $n = 0; + $n = 1 if($$config{'modules'}{$mod} && $$config{'modules'}{$mod} > 0); + + $by_type{$type}{$mod} = $n; + } + + for my $type (sort keys %by_type) { + my %mods = %{$by_type{$type}}; + + my @load_lines; + + if(keys %mods == 1) { + my $on = $mods{$type}; + + if($on > 0) { + print with_diagnostic('loading', $type); + } + else { + print with_diagnostic('loading', '[', $type , ']'); + } + } + else { + my $s = $type . ': '; + + for my $mod (sort keys %mods) { + my $on = $mods{$mod}; + + if($s eq '') { + $s = ' ' x (length($type) + 16); + } + + if($on > 0) { + $s .= $mod . ' '; + } + else { + $s .= '[' . $mod . '] '; + } + + if(length($s) > 60) { + push @load_lines, $s; + $s = ''; + } + } + + #print "Last1 '$s'\n"; + + $s =~ s/\s*$//m; # strip trailing whitespace + + push @load_lines, $s if($s ne ''); + + + print with_diagnostic('loading', join("\n", @load_lines)); + } + } +} + +sub get_options { + my ($config) = @_; + + my $save_option = sub { + my ($opt, $val) = @_; + $opt =~ s/-/_/g; + $$config{$opt} = $val; + }; + + $$config{'verbose'} = 1; + $$config{'asm_ok'} = 1; + $$config{'tr1'} = undef; # not enabled by default + $$config{'modules'} = {}; + + sub arch_info { + my $arg = $_[0]; + + my $arch = find_arch($arg); + + unless(defined($arch) and defined($CPU{$arch})) { + warning("Unknown arch '$arg' passed to --arch-info (try --help)"); + return ''; + } + + my %info = %{ $CPU{$arch} }; + + my $out = "Information for $arg ($arch)\n--------\n"; + + if(@{$info{'aliases'}}) { + $out .= 'Aliases: ' . join(' ', @{$info{'aliases'}}) . "\n"; + } + + if(@{$info{'submodels'}}) { + $out .= 'Submodels: ' . join(' ', @{$info{'submodels'}}) . "\n"; + } + + foreach my $k (keys %{$info{'submodel_aliases'}}) { + $out .= "Alias '$k' -> '" . $info{'submodel_aliases'}{$k} . "'\n"; + } + + if(defined($info{'endian'})) { + $out .= 'Default endian: ' . $info{'endian'} . "\n"; + } + + if(defined($info{'unaligned'})) { + $out .= 'Unaligned memory access: ' . $info{'unaligned'} . "\n"; + } + + return $out; + } + + sub add_modules { + my ($config,$mods) = @_; + + foreach my $mod (split(/,/, $mods)) { + # -1 means disabled by user, do not load + $$config{'modules'}{$mod} = 1 unless( + defined($$config{'modules'}{$mod}) && + $$config{'modules'}{$mod} == -1); + } + } + + sub disable_modules { + my ($config,$mods) = @_; + + foreach my $mod (split(/,/, $mods)) { + # -1 means disabled by user, do not load + $$config{'modules'}{$mod} = -1; + } + } + + sub add_module_sets { + my ($config,$sets) = @_; + + foreach my $set (split(/,/, $sets)) { + for my $mod (sort keys %MODULES) { + my %info = %{$MODULES{$mod}}; + + next unless (defined($info{'modset'})); + + for my $s (split(/,/, $info{'modset'})) { + if($s eq $set) { + $$config{'modules'}{$mod} = 1 + unless($$config{'modules'}{$mod} == -1); + } + } + } + } + } + + exit 1 unless GetOptions( + 'prefix=s' => sub { &$save_option(@_); }, + 'exec-prefix=s' => sub { &$save_option(@_); }, + + 'bindir=s' => sub { &$save_option(@_); }, + 'datadir' => sub { &$save_option(@_); }, + 'datarootdir' => sub { &$save_option(@_); }, + 'docdir=s' => sub { &$save_option(@_); }, + 'dvidir' => sub { &$save_option(@_); }, + 'htmldir' => sub { &$save_option(@_); }, + 'includedir' => sub { &$save_option(@_); }, + 'infodir' => sub { &$save_option(@_); }, + 'libdir=s' => sub { &$save_option(@_); }, + 'libexecdir' => sub { &$save_option(@_); }, + 'localedir' => sub { &$save_option(@_); }, + 'localstatedir' => sub { &$save_option(@_); }, + 'mandir' => sub { &$save_option(@_); }, + 'oldincludedir' => sub { &$save_option(@_); }, + 'pdfdir' => sub { &$save_option(@_); }, + 'psdir' => sub { &$save_option(@_); }, + 'sbindir=s' => sub { &$save_option(@_); }, + 'sharedstatedir' => sub { &$save_option(@_); }, + 'sysconfdir' => sub { &$save_option(@_); }, + + 'cc=s' => sub { &$save_option('compiler', $_[1]) }, + 'os=s' => sub { &$save_option(@_) }, + 'cpu=s' => sub { &$save_option(@_) }, + + 'help' => sub { display_help(); }, + 'module-info' => sub { emit_help(module_info()); }, + 'version' => sub { emit_help("$VERSION_STRING\n") }, + 'so-version' => sub { emit_help("$SO_VERSION_STRING\n") }, + + 'with-tr1-implementation=s' => sub { $$config{'tr1'} = $_[1]; }, + + 'quiet' => sub { $$config{'verbose'} = 0; }, + 'trace' => sub { $TRACING = 1; }, + + 'enable-asm' => sub { $$config{'asm_ok'} = 1; }, + 'disable-asm' => sub { $$config{'asm_ok'} = 0; }, + + 'enable-autoconfig' => sub { $$config{'autoconfig'} = 1; }, + 'disable-autoconfig' => sub { $$config{'autoconfig'} = 0; }, + + 'enable-shared' => sub { $$config{'shared'} = 'yes'; }, + 'disable-shared' => sub { $$config{'shared'} = 'no'; }, + + 'enable-debug' => sub { &$save_option('debug', 1); }, + 'disable-debug' => sub { &$save_option('debug', 0); }, + + 'enable-modules:s' => sub { add_modules($config, $_[1]); }, + 'disable-modules:s' => sub { disable_modules($config, $_[1]); }, + + 'with-openssl' => sub { add_modules($config, 'openssl'); }, + 'without-openssl' => sub { disable_modules($config, 'openssl'); }, + 'with-gnump' => sub { add_modules($config, 'gnump'); }, + 'without-gnump' => sub { disable_modules($config, 'gnump'); }, + 'with-bzip2' => sub { add_modules($config, 'bzip2'); }, + 'without-bzip2' => sub { disable_modules($config, 'bzip2'); }, + 'with-zlib' => sub { add_modules($config, 'zlib'); }, + 'without-zlib' => sub { disable_modules($config, 'zlib'); }, + + 'use-module-set=s' => sub { add_module_sets($config, $_[1]); }, + + 'with-build-dir=s' => sub { &$save_option(@_); }, + 'with-endian=s' => sub { &$save_option(@_); }, + 'with-unaligned-mem=s' => sub { &$save_option(@_); }, + 'with-local-config=s' => + sub { &$save_option('local_config', slurp_file($_[1])); }, + + 'modules=s' => sub { add_modules($config, $_[1]); }, + 'show-arch-info=s' => sub { emit_help(arch_info($_[1])); }, + 'make-style=s' => sub { &$save_option(@_); }, + 'dumb-gcc|gcc295x' => sub { $$config{'gcc_bug'} = 1; } + ); + + # All arguments should now be consumed + croak("Unknown option $ARGV[0] (try --help)") unless($#ARGV == -1); +} + +################################################## +# Functions to search the info tables # +################################################## +sub find_arch { + my $name = $_[0]; + + foreach my $arch (keys %CPU) { + my %info = %{$CPU{$arch}}; + + return $arch if($name eq $arch); + + foreach my $alias (@{$info{'aliases'}}) { + return $arch if($name eq $alias); + } + + foreach my $submodel (@{$info{'submodels'}}) { + return $arch if($name eq $submodel); + } + + foreach my $submodel (keys %{$info{'submodel_aliases'}}) { + return $arch if($name eq $submodel); + } + } + return undef; +}; + +sub figure_out_arch { + my ($name) = @_; + + return ('generic', 'generic') if($name eq 'generic'); + + my $submodel_alias = sub { + my ($name,$info) = @_; + + my %info = %{$info}; + + foreach my $submodel (@{$info{'submodels'}}) { + return $submodel if($name eq $submodel); + } + + return '' unless defined $info{'submodel_aliases'}; + my %sm_aliases = %{$info{'submodel_aliases'}}; + + foreach my $alias (keys %sm_aliases) { + my $official = $sm_aliases{$alias}; + return $official if($alias eq $name); + } + return ''; + }; + + my $arch = find_arch($name); + croak("Arch type $name isn't known (try --help)") unless defined $arch; + trace("mapped name '$name' to arch '$arch'"); + + my %archinfo = %{ $CPU{$arch} }; + + my $submodel = &$submodel_alias($name, \%archinfo); + + if($submodel eq '') { + $submodel = $archinfo{'default_submodel'}; + + autoconfig("Using $submodel as default type for family ", + realname($arch)) if($submodel ne $arch); + } + + trace("mapped name '$name' to submodel '$submodel'"); + + croak("Couldn't figure out arch type of $name") + unless defined($arch) and defined($submodel); + + return ($arch,$submodel); +} + +sub os_alias { + my $name = $_[0]; + + foreach my $os (keys %OPERATING_SYSTEM) { + foreach my $alias (@{$OPERATING_SYSTEM{$os}{'aliases'}}) { + if($alias eq $name) { + trace("os_alias($name) -> $os"); + return $os; + } + } + } + + return $name; +} + +sub os_info_for { + my ($os,$what) = @_; + + die unless defined($os); + + croak('os_info_for called with an os of defaults (internal problem)') + if($os eq 'defaults'); + + my $result = ''; + + if(defined($OPERATING_SYSTEM{$os})) { + my %osinfo = %{$OPERATING_SYSTEM{$os}}; + $result = $osinfo{$what}; + } + + if(!defined($result) or $result eq '') { + $result = $OPERATING_SYSTEM{'defaults'}{$what}; + } + + croak("os_info_for: No info for $what on $os") unless defined $result; + + return $result; +} + +sub my_compiler { + my ($config) = @_; + my $cc = $$config{'compiler'}; + + croak('my_compiler called, but no compiler set in config') + unless defined $cc and $cc ne ''; + + croak("unknown compiler $cc") unless defined $COMPILER{$cc}; + + return %{$COMPILER{$cc}}; +} + +sub mach_opt { + my ($config) = @_; + + my %ccinfo = my_compiler($config); + + # Nothing we can do in that case + return '' unless $ccinfo{'mach_opt_flags'}; + + my $submodel = $$config{'submodel'}; + my $arch = $$config{'arch'}; + if(defined($ccinfo{'mach_opt_flags'}{$submodel})) + { + return $ccinfo{'mach_opt_flags'}{$submodel}; + } + elsif(defined($ccinfo{'mach_opt_flags'}{$arch})) { + my $mach_opt_flags = $ccinfo{'mach_opt_flags'}{$arch}; + my $processed_modelname = $submodel; + + my $remove = ''; + if(defined($ccinfo{'mach_opt_re'}) and + defined($ccinfo{'mach_opt_re'}{$arch})) { + $remove = $ccinfo{'mach_opt_re'}{$arch}; + } + + $processed_modelname =~ s/$remove//; + $mach_opt_flags =~ s/SUBMODEL/$processed_modelname/g; + return $mach_opt_flags; + } + return ''; +} + +################################################## +# # +################################################## +sub using_libs { + my ($config) = @_; + + my $os = $$config{'os'}; + my %libs; + + foreach my $mod (sort keys %{$$config{'modules'}}) { + next if ${$$config{'modules'}}{$mod} < 0; + + my %MOD_LIBS = %{ $MODULES{$mod}{'libs'} }; + + foreach my $mod_os (keys %MOD_LIBS) + { + next if($mod_os =~ /^all!$os$/); + next if($mod_os =~ /^all!$os,/); + #next if($mod_os =~ /^all!.*,${os}$/); + next if($mod_os =~ /^all!.*,$os,.*/); + next unless($mod_os eq $os or ($mod_os =~ /^all.*/)); + my @liblist = split(/,/, $MOD_LIBS{$mod_os}); + foreach my $lib (@liblist) { $libs{$lib} = 1; } + } + } + + return sort keys %libs; +} + +sub libs { + my ($prefix,$suffix,@libs) = @_; + my $output = ''; + foreach my $lib (@libs) { + $output .= ' ' if($output ne ''); + $output .= $prefix . $lib . $suffix; + } + return $output; +} + +################################################## +# Path and file manipulation utilities # +################################################## +sub portable_symlink { + my ($from, $to_dir, $to_fname) = @_; + + #trace("portable_symlink($from, $to_dir, $to_fname)"); + + my $can_symlink = 0; + my $can_link = 0; + + unless($^O eq 'MSWin32' or $^O eq 'dos' or $^O eq 'cygwin') { + $can_symlink = eval { symlink("",""); 1 }; + $can_link = eval { link("",""); 1 }; + } + + chdir $to_dir or croak("Can't chdir to $to_dir ($!)"); + + if($can_symlink) { + symlink $from, $to_fname or + croak("Can't symlink $from to $to_fname ($!)"); + } + elsif($can_link) { + link $from, $to_fname or + croak("Can't link $from to $to_fname ($!)"); + } + else { + copy ($from, $to_fname) or + croak("Can't copy $from to $to_fname ($!)"); + } + + my $go_up = File::Spec->splitdir($to_dir); + for(my $j = 0; $j != $go_up; $j++) # return to where we were + { + chdir File::Spec->updir(); + } +} + +sub copy_include_files { + my ($config) = @_; + + my $include_dir = $$config{'build_include_botan'}; + + trace('Copying to ', $include_dir); + + foreach my $file (dir_list($include_dir)) { + my $path = File::Spec->catfile($include_dir, $file); + unlink $path or croak("Could not unlink $path ($!)"); + } + + my $link_up = sub { + my ($dir, $file) = @_; + my $updir = File::Spec->updir(); + portable_symlink(File::Spec->catfile($updir, $updir, $updir, + $dir, $file), + $include_dir, $file); + }; + + my $files = $$config{'includes'}; + + foreach my $file (keys %$files) { + &$link_up($$files{$file}, $file); + } +} + +sub dir_list { + my ($dir) = @_; + opendir(DIR, $dir) or croak("Couldn't read directory '$dir' ($!)"); + + my @listing = grep { !/#/ and -f File::Spec->catfile($dir, $_) and + $_ ne File::Spec->curdir() and + $_ ne File::Spec->updir() } readdir DIR; + + closedir DIR; + return @listing; +} + +sub mkdirs { + my (@dirs) = @_; + + my @created; + foreach my $dir (@dirs) { + next if( -e $dir and -d $dir ); # skip it if it's already there + mkdir($dir, 0777) or + croak("Could not create directory $dir ($!)"); + push @created, $dir; + } + return @created; +} + +sub slurp_file { + my $file = $_[0]; + + return '' if(!defined($file) or $file eq ''); + + croak("'$file': No such file") unless(-e $file); + croak("'$file': Not a regular file") unless(-f $file); + + open FILE, "<$file" or croak("Couldn't read $file ($!)"); + + my $output = ''; + while(<FILE>) { $output .= $_; } + close FILE; + + return $output; +} + +sub which +{ + my $file = $_[0]; + my @paths = split(/:/, $ENV{PATH}); + foreach my $path (@paths) + { + my $file_path = File::Spec->catfile($path, $file); + return $file_path if(-e $file_path and -r $file_path); + } + return ''; +} + +# Return a hash mapping every var in a list to a constant value +sub map_to { + my $var = shift; + return map { $_ => $var } @_; +} + +sub in_array { + my($target, $array) = @_; + return 0 unless defined($array); + foreach (@$array) { return 1 if($_ eq $target); } + return 0; +} + +sub add_to { + my ($to,$from) = @_; + + foreach my $key (keys %$from) { + $$to{$key} = $$from{$key}; + } +} + +################################################## +# # +################################################## +sub find_mp_bits { + my(@modules_list) = @_; + my $mp_bits = 32; # default, good for most systems + + my $seen_mp_module = undef; + + foreach my $modname (sort keys %{$$config{'modules'}}) { + croak("Unknown module $modname") unless defined $MODULES{$modname}; + + next if $$config{'modules'}{$modname} < 0; + + my %modinfo = %{ $MODULES{$modname} }; + if($modinfo{'mp_bits'}) { + if(defined($seen_mp_module) and $modinfo{'mp_bits'} != $mp_bits) { + croak('Inconsistent mp_bits requests from modules ', + $seen_mp_module, ' and ', $modname); + } + + $seen_mp_module = $modname; + $mp_bits = $modinfo{'mp_bits'}; + } + } + return $mp_bits; +} + +################################################## +# # +################################################## +sub realname { + my $arg = $_[0]; + + return $COMPILER{$arg}{'realname'} + if defined $COMPILER{$arg}; + + return $OPERATING_SYSTEM{$arg}{'realname'} + if defined $OPERATING_SYSTEM{$arg}; + + return $CPU{$arg}{'realname'} + if defined $CPU{$arg}; + + return $arg; +} + +################################################## +# # +################################################## + +sub load_module { + my ($config, $modname) = @_; + + #trace("load_module($modname)"); + + croak("Unknown module $modname") unless defined($MODULES{$modname}); + + my %module = %{$MODULES{$modname}}; + + my $works_on = sub { + my ($what, $lst_ref) = @_; + my @lst = @{$lst_ref}; + return 1 if not @lst; # empty list -> no restrictions + return 1 if $what eq 'generic'; # trust the user + return in_array($what, \@lst); + }; + + # Check to see if everything is OK WRT system requirements + my $os = $$config{'os'}; + + croak("Module '$modname' does not run on $os") + unless(&$works_on($os, $module{'os'})); + + my $arch = $$config{'arch'}; + my $sub = $$config{'submodel'}; + + croak("Module '$modname' does not run on $arch/$sub") + unless(&$works_on($arch, $module{'arch'}) or + &$works_on($sub, $module{'arch'})); + + my $cc = $$config{'compiler'}; + + croak("Module '$modname' does not work with $cc") + unless(&$works_on($cc, $module{'cc'})); + + my $handle_files = sub { + my($lst, $func) = @_; + return unless defined($lst); + + foreach (sort @$lst) { + &$func($module{'moddirs'}, $config, $_); + } + }; + + &$handle_files($module{'ignore'}, \&ignore_file); + &$handle_files($module{'add'}, \&add_file); + &$handle_files($module{'replace'}, + sub { ignore_file(@_); add_file(@_); }); + + warning($modname, ': ', $module{'note'}) + if(defined($module{'note'})); +} + +sub load_modules { + my ($config) = @_; + + my @mod_names; + + foreach my $mod (sort keys %{$$config{'modules'}}) { + next unless($$config{'modules'}{$mod} > 0); + + load_module($config, $mod); + + push @mod_names, $mod; + } + + $$config{'mod_list'} = join("\n", @mod_names); + + my $unaligned_ok = 0; + + my $target_os_defines = sub { + my @macro_list; + + my $os = $$config{'os'}; + if($os ne 'generic') { + push @macro_list, '#define BOTAN_TARGET_OS_IS_' . uc $os; + + my @features = @{$OPERATING_SYSTEM{$os}{'target_features'}}; + + for my $feature (@features) { + push @macro_list, '#define BOTAN_TARGET_OS_HAS_' . uc $feature; + } + + } + return join("\n", @macro_list); + }; + + $$config{'target_os_defines'} = &$target_os_defines(); + + my $target_cpu_defines = sub { + my @macro_list; + + my $arch = $$config{'arch'}; + if($arch ne 'generic') { + my %cpu_info = %{$CPU{$arch}}; + my $endian = $cpu_info{'endian'}; + + if(defined($$config{'with_endian'})) { + $endian = $$config{'with_endian'}; + $endian = undef unless($endian eq 'little' || + $endian eq 'big'); + } + elsif(defined($endian)) { + autoconfig("Since arch is $arch, assuming $endian endian mode"); + } + + push @macro_list, "#define BOTAN_TARGET_ARCH_IS_" . (uc $arch); + + my $submodel = $$config{'submodel'}; + if($arch ne $submodel) { + $submodel = uc $submodel; + $submodel =~ tr/-/_/; + $submodel =~ tr/.//; + + push @macro_list, "#define BOTAN_TARGET_CPU_IS_$submodel"; + } + + if(defined($endian)) { + $endian = uc $endian; + push @macro_list, + "#define BOTAN_TARGET_CPU_IS_${endian}_ENDIAN"; + + # See if the user set --with-unaligned-mem + if(defined($$config{'with_unaligned_mem'})) { + my $spec = $$config{'with_unaligned_mem'}; + + if($spec eq 'yes') { + $unaligned_ok = 1; + } + elsif($spec eq 'no') { + $unaligned_ok = 0; + } + else { + warning('Unknown arg to --with-unaligned-mem (' . + $spec . ') will ignore'); + $unaligned_ok = 0; + } + } + # Otherwise, see if the CPU has a default setting + elsif(defined($cpu_info{'unaligned'}) and + $cpu_info{'unaligned'} eq 'ok') + { + autoconfig("Since arch is $arch, " . + 'assuming unaligned memory access is OK'); + $unaligned_ok = 1; + } + } + } + + # variable is always set (one or zero) + push @macro_list, + "#define BOTAN_TARGET_UNALIGNED_LOADSTOR_OK $unaligned_ok"; + return join("\n", @macro_list); + }; + + $$config{'target_cpu_defines'} = &$target_cpu_defines(); + + my $target_compiler_defines = sub { + my @macro_list; + + if(defined($$config{'tr1'})) { + my $tr1 = $$config{'tr1'}; + + if($tr1 eq 'system') { + push @macro_list, '#define BOTAN_USE_STD_TR1'; + } + elsif($tr1 eq 'boost') { + push @macro_list, '#define BOTAN_USE_BOOST_TR1'; + } + elsif($tr1 ne 'none') { + croak("Unknown --with-tr1= option value '$tr1' (try --help)"); + } + } + + return join("\n", @macro_list); + }; + + $$config{'target_compiler_defines'} = &$target_compiler_defines(); + + my $gen_defines = sub { + my @macro_list; + + my %defines; + + foreach my $mod (sort keys %{$$config{'modules'}}) { + next unless $$config{'modules'}{$mod} > 0; + + my $defs = $MODULES{$mod}{'define'}; + next unless $defs; + + push @{$defines{$MODULES{$mod}{'type'}}}, split(/,/, $defs); + } + + foreach my $type (sort keys %defines) { + push @macro_list, "\n/* $type */"; + + for my $macro (@{$defines{$type}}) { + die unless(defined $macro and $macro ne ''); + push @macro_list, "#define BOTAN_HAS_$macro"; + } + } + + return join("\n", @macro_list); + }; + + $$config{'module_defines'} = &$gen_defines(); +} + +################################################## +# # +################################################## +sub file_type { + my ($file) = @_; + + return 'sources' + if($file =~ /\.cpp$/ or $file =~ /\.c$/ or $file =~ /\.S$/); + return 'includes' if($file =~ /\.h$/); + + croak('file_type() - don\'t know what sort of file ', $file, ' is'); +} + +sub add_file { + my ($mod_dir, $config, $file) = @_; + + check_for_file($config, $file, $mod_dir, $mod_dir); + + my $do_add_file = sub { + my ($type) = @_; + + croak("File $file already added from ", $$config{$type}{$file}) + if(defined($$config{$type}{$file})); + + if($file =~ /(.*):(.*)/) { + my @dirs = File::Spec->splitdir($mod_dir); + + $dirs[$#dirs-1] = $1; + + $$config{$type}{$2} = File::Spec->catdir(@dirs); + } + else { + $$config{$type}{$file} = $mod_dir; + } + }; + + &$do_add_file(file_type($file)); +} + +sub ignore_file { + my ($mod_dir, $config, $file) = @_; + check_for_file($config, $file, undef, $mod_dir); + + my $do_ignore_file = sub { + my ($type, $ok_if_from) = @_; + + if(defined ($$config{$type}{$file})) { + + croak("$mod_dir - File $file modified from ", + $$config{$type}{$file}) + if($$config{$type}{$file} ne $ok_if_from); + + delete $$config{$type}{$file}; + } + }; + + &$do_ignore_file(file_type($file)); +} + +sub check_for_file { + my ($config, $file, $added_from, $mod_dir) = @_; + + #trace("check_for_file($file, $added_from, $mod_dir)"); + + my $full_path = sub { + my ($file,$mod_dir) = @_; + + if($file =~ /(.*):(.*)/) { + return File::Spec->catfile($mod_dir, '..', $1, $2); + } else { + return File::Spec->catfile($mod_dir, $file) if(defined($mod_dir)); + + my @typeinfo = file_type($config, $file); + return File::Spec->catfile($typeinfo[1], $file); + } + }; + + $file = &$full_path($file, $added_from); + + croak("Module $mod_dir requires that file $file exist. This error\n ", + 'should never occur; please contact the maintainers with details.') + unless(-e $file); +} + +################################################## +# # +################################################## +sub process_template { + my ($in, $out, $config) = @_; + + trace("process_template: $in -> $out"); + + my $contents = slurp_file($in); + + foreach my $name (keys %$config) { + my $val = $$config{$name}; + + unless(defined $val) { + trace("Undefined variable $name in $in"); + next; + } + + $contents =~ s/\%\{$name\}/$val/g; + } + + if($contents =~ /\%\{([a-z_]*)\}/) { + + sub summarize { + my ($n, $s) = @_; + + $s =~ s/\n/\\n/; # escape newlines + + return $s if(length($s) <= $n); + + return substr($s, 0, 57) . '...'; + } + + foreach my $key (sort keys %$config) { + print with_diagnostic("debug", + "In %config:", $key, " -> ", + summarize(60, $$config{$key})); + } + + croak("Unbound variable '$1' in $in"); + } + + open OUT, ">$out" or croak("Couldn't write $out ($!)"); + print OUT $contents; + close OUT; +} + +################################################## +# # +################################################## +sub read_list { + my ($line, $reader, $marker, $func) = @_; + + if($line =~ m@^<$marker>$@) { + while(1) { + $line = &$reader(); + + die "EOF while searching for $marker" unless $line; + last if($line =~ m@^</$marker>$@); + &$func($line); + } + } +} + +sub list_push { + my ($listref) = @_; + return sub { push @$listref, $_[0]; } +} + +sub match_any_of { + my ($line, $hash, $quoted, @any_of) = @_; + + $quoted = ($quoted eq 'quoted') ? 1 : 0; + + foreach my $what (@any_of) { + $$hash{$what} = $1 if(not $quoted and $line =~ /^$what (.*)/); + $$hash{$what} = $1 if($quoted and $line =~ /^$what \"(.*)\"/); + } +} + +################################################## +# # +################################################## +sub make_reader { + my $filename = $_[0]; + + croak("make_reader(): Arg was undef") if not defined $filename; + + open FILE, "<$filename" or + croak("Couldn't read $filename ($!)"); + + return sub { + my $line = ''; + while(1) { + my $line = <FILE>; + last unless defined($line); + + chomp($line); + $line =~ s/#.*//; + $line =~ s/^\s*//; + $line =~ s/\s*$//; + $line =~ s/\s\s*/ /; + $line =~ s/\t/ /; + return $line if $line ne ''; + } + close FILE; + return undef; + } +} + +################################################## +# # +################################################## +sub read_info_files { + my ($config, $dir, $func) = @_; + + $dir = File::Spec->catdir($$config{'config-dir'}, $dir); + + my %allinfo; + foreach my $file (dir_list($dir)) { + my $fullpath = File::Spec->catfile($dir, $file); + + trace("reading $fullpath"); + %{$allinfo{$file}} = &$func($file, $fullpath); + } + + return %allinfo; +} + +sub read_module_files { + my ($config) = @_; + + my %allinfo; + + my @modinfos; + + File::Find::find( + { wanted => sub + { if(-f $_ && /^info\.txt\z/s) { + my $name = $File::Find::name; + push @modinfos, $name; + } + } + }, + $$config{'src-dir'}); + + foreach my $modfile (@modinfos) { + trace("reading $modfile"); + + my ($volume,$dirs,$file) = File::Spec->splitpath($modfile); + + my @dirs = File::Spec->splitdir($dirs); + my $moddir = $dirs[$#dirs-1]; + + trace("module $moddir in $dirs $modfile"); + + %{$allinfo{$moddir}} = get_module_info($dirs, $moddir, $modfile); + } + + return %allinfo; +} + +################################################## +# # +################################################## + +sub get_module_info { + my ($dirs, $name, $modfile) = @_; + my $reader = make_reader($modfile); + + my %info; + + $info{'name'} = $name; + $info{'modinfo'} = $modfile; + $info{'moddirs'} = $dirs; + + # Default module settings + $info{'load_on'} = 'request'; # default unless specified + $info{'uses_tr1'} = 'no'; + $info{'libs'} = {}; + $info{'use'} = 'no'; + + my @dir_arr = File::Spec->splitdir($dirs); + $info{'type'} = $dir_arr[$#dir_arr-2]; # cipher, hash, ... + if($info{'type'} eq 'src') { $info{'type'} = $dir_arr[$#dir_arr-1]; } + + while($_ = &$reader()) { + match_any_of($_, \%info, 'quoted', 'realname', 'note', 'type'); + match_any_of($_, \%info, 'unquoted', 'define', 'mp_bits', + 'modset', 'load_on', 'uses_tr1'); + + read_list($_, $reader, 'arch', list_push(\@{$info{'arch'}})); + read_list($_, $reader, 'cc', list_push(\@{$info{'cc'}})); + read_list($_, $reader, 'os', list_push(\@{$info{'os'}})); + read_list($_, $reader, 'add', list_push(\@{$info{'add'}})); + read_list($_, $reader, 'replace', list_push(\@{$info{'replace'}})); + read_list($_, $reader, 'ignore', list_push(\@{$info{'ignore'}})); + read_list($_, $reader, 'requires', list_push(\@{$info{'requires'}})); + + read_list($_, $reader, 'libs', + sub { + my $line = $_[0]; + $line =~ m/^([\w!,]*) -> ([\w.,-]*)$/; + $info{'libs'}{$1} = $2; + }); + + if(/^require_version /) { + if(/^require_version (\d+)\.(\d+)\.(\d+)$/) { + my $version = "$1.$2.$3"; + my $needed_version = 100*$1 + 10*$2 + $3; + + my $have_version = + 100*$MAJOR_VERSION + 10*$MINOR_VERSION + $PATCH_VERSION; + + if($needed_version > $have_version) { + warning("Module $name needs v$version; disabling"); + return (); + } + } + else { + croak("In module $name, bad version requirement '$_'"); + } + } + } + + return %info; +} + +################################################## +# # +################################################## +sub get_arch_info { + my ($name,$file) = @_; + my $reader = make_reader($file); + + my %info; + $info{'name'} = $name; + + while($_ = &$reader()) { + match_any_of($_, \%info, 'quoted', 'realname'); + match_any_of($_, \%info, 'unquoted', + 'default_submodel', 'endian', 'unaligned'); + + read_list($_, $reader, 'aliases', list_push(\@{$info{'aliases'}})); + read_list($_, $reader, 'submodels', list_push(\@{$info{'submodels'}})); + + read_list($_, $reader, 'submodel_aliases', + sub { + my $line = $_[0]; + $line =~ m/^(\S*) -> (\S*)$/; + $info{'submodel_aliases'}{$1} = $2; + }); + } + return %info; +} + +################################################## +# # +################################################## +sub get_os_info { + my ($name,$file) = @_; + my $reader = make_reader($file); + + my %info; + $info{'name'} = $name; + + while($_ = &$reader()) { + match_any_of($_, \%info, + 'quoted', 'realname', 'ar_command', + 'install_cmd_data', 'install_cmd_exec'); + + match_any_of($_, \%info, 'unquoted', + 'os_type', + 'obj_suffix', + 'so_suffix', + 'static_suffix', + 'install_root', + 'header_dir', + 'lib_dir', 'doc_dir', + 'ar_needs_ranlib'); + + read_list($_, $reader, 'aliases', list_push(\@{$info{'aliases'}})); + + read_list($_, $reader, 'target_features', + list_push(\@{$info{'target_features'}})); + + read_list($_, $reader, 'supports_shared', + list_push(\@{$info{'supports_shared'}})); + } + return %info; +} + +################################################## +# Read a file from misc/config/cc and set the values from +# there into a hash for later reference +################################################## +sub get_cc_info { + my ($name,$file) = @_; + my $reader = make_reader($file); + + my %info; + $info{'name'} = $name; + + while($_ = &$reader()) { + match_any_of($_, \%info, 'quoted', + 'realname', + 'binary_name', + 'compile_option', + 'output_to_option', + 'add_include_dir_option', + 'add_lib_dir_option', + 'add_lib_option', + 'lib_opt_flags', + 'check_opt_flags', + 'dll_import_flags', + 'dll_export_flags', + 'lang_flags', + 'warning_flags', + 'shared_flags', + 'ar_command', + 'debug_flags', + 'no_debug_flags'); + + match_any_of($_, \%info, 'unquoted', + 'makefile_style', + 'compiler_has_tr1'); + + sub quoted_mapping { + my $hashref = $_[0]; + return sub { + my $line = $_[0]; + $line =~ m/^(\S*) -> \"(.*)\"$/; + $$hashref{$1} = $2; + } + } + + read_list($_, $reader, 'mach_abi_linking', + quoted_mapping(\%{$info{'mach_abi_linking'}})); + read_list($_, $reader, 'so_link_flags', + quoted_mapping(\%{$info{'so_link_flags'}})); + + read_list($_, $reader, 'mach_opt', + sub { + my $line = $_[0]; + $line =~ m/^(\S*) -> \"(.*)\" ?(.*)?$/; + $info{'mach_opt_flags'}{$1} = $2; + $info{'mach_opt_re'}{$1} = $3; + }); + + } + return %info; +} + +################################################## +# # +################################################## +sub write_pkg_config { + my ($config) = @_; + + return if($$config{'os'} eq 'generic' or + $$config{'os'} eq 'windows'); + + $$config{'link_to'} = libs('-l', '', 'm', @{$$config{'mod_libs'}}); + + my $botan_config = $$config{'botan_config'}; + + process_template( + File::Spec->catfile($$config{'config-dir'}, 'botan-config.in'), + $botan_config, $config); + chmod 0755, $botan_config; + + process_template( + File::Spec->catfile($$config{'config-dir'}, 'botan.pc.in'), + $$config{'botan_pkgconfig'}, $config); + + delete $$config{'link_to'}; +} + +################################################## +# # +################################################## +sub file_list { + my ($put_in, $from, $to, %files) = @_; + + my $list = ''; + + my $spaces = 16; + + foreach (sort keys %files) { + my $file = $_; + + $file =~ s/$from/$to/ if(defined($from) and defined($to)); + + my $dir = $files{$_}; + $dir = $put_in if defined $put_in; + + if(defined($dir)) { + $list .= File::Spec->catfile ($dir, $file); + } + else { + $list .= $file; + } + + $list .= " \\\n "; + } + + $list =~ s/\\\n +$//; # remove trailing escape + + return $list; +} + +sub build_cmds { + my ($config, $dir, $flags, $files) = @_; + + my $obj_suffix = $$config{'obj_suffix'}; + + my %ccinfo = my_compiler($config); + + my $inc = $ccinfo{'add_include_dir_option'}; + my $from = $ccinfo{'compile_option'}; + my $to = $ccinfo{'output_to_option'}; + + my $inc_dir = $$config{'build_include'}; + + # Probably replace by defaults to -I -c -o + croak('undef value found in build_cmds') + unless defined($inc) and defined($from) and defined($to); + + my $bld_line = "\t\$(CXX) $inc$inc_dir $flags $from\$? $to\$@"; + + my @output_lines; + + foreach (sort keys %$files) { + my $src_file = File::Spec->catfile($$files{$_}, $_); + my $obj_file = File::Spec->catfile($dir, $_); + + $obj_file =~ s/\.cpp$/.$obj_suffix/; + $obj_file =~ s/\.c$/.$obj_suffix/; + $obj_file =~ s/\.S$/.$obj_suffix/; + + push @output_lines, "$obj_file: $src_file\n$bld_line"; + } + + return join("\n\n", @output_lines); +} + +sub determine_config { + my ($config) = @_; + + sub os_ar_command { + return os_info_for(shift, 'ar_command'); + } + + sub append_if { + my($var,$addme,$cond) = @_; + + croak('append_if: reference was undef') unless defined $var; + + if($cond and $addme ne '') { + $$var .= ' ' unless($$var eq '' or $$var =~ / $/); + $$var .= $addme; + } + } + + sub append_ifdef { + my($var,$addme) = @_; + append_if($var, $addme, defined($addme)); + } + + my $empty_if_nil = sub { + my $val = $_[0]; + return $val if defined($val); + return ''; + }; + + my %ccinfo = my_compiler($config); + + my $lang_flags = ''; + append_ifdef(\$lang_flags, $ccinfo{'lang_flags'}); + append_if(\$lang_flags, "-fpermissive", $$config{'gcc_bug'}); + + my $debug = $$config{'debug'}; + + my $lib_opt_flags = ''; + append_ifdef(\$lib_opt_flags, $ccinfo{'lib_opt_flags'}); + append_ifdef(\$lib_opt_flags, $ccinfo{'debug_flags'}) if($debug); + append_ifdef(\$lib_opt_flags, $ccinfo{'no_debug_flags'}) if(!$debug); + + # This is a default that works on most Unix and Unix-like systems + my $ar_command = 'ar crs'; + my $ranlib_command = 'true'; # almost no systems need it anymore + + # See if there are any over-riding methods. We presume if CC is creating + # the static libs, it knows how to create the index itself. + + my $os = $$config{'os'}; + + if($ccinfo{'ar_command'}) { + $ar_command = $ccinfo{'ar_command'}; + } + elsif(os_ar_command($os)) + { + $ar_command = os_ar_command($os); + $ranlib_command = 'ranlib' + if(os_info_for($os, 'ar_needs_ranlib') eq 'yes'); + } + + my $arch = $$config{'arch'}; + + my $abi_opts = ''; + append_ifdef(\$abi_opts, $ccinfo{'mach_abi_linking'}{$arch}); + append_ifdef(\$abi_opts, $ccinfo{'mach_abi_linking'}{$os}); + append_ifdef(\$abi_opts, $ccinfo{'mach_abi_linking'}{'all'}); + $abi_opts = ' ' . $abi_opts if($abi_opts ne ''); + + if($$config{'shared'} eq 'yes' and + (in_array('all', $OPERATING_SYSTEM{$os}{'supports_shared'}) or + in_array($$config{'compiler'}, + $OPERATING_SYSTEM{$os}{'supports_shared'}))) { + + $$config{'shared_flags'} = &$empty_if_nil($ccinfo{'shared_flags'}); + $$config{'so_link'} = &$empty_if_nil($ccinfo{'so_link_flags'}{$os}); + + if($$config{'so_link'} eq '') { + $$config{'so_link'} = + &$empty_if_nil($ccinfo{'so_link_flags'}{'default'}) + } + + if($$config{'shared_flags'} eq '' and $$config{'so_link'} eq '') { + $$config{'shared'} = 'no'; + + warning($$config{'compiler'}, ' has no shared object flags set ', + "for $os; disabling shared"); + } + } + else { + autoconfig("No shared library generated with " . + $$config{'compiler'} . " on " . $$config{'os'}); + + $$config{'shared'} = 'no'; + $$config{'shared_flags'} = ''; + $$config{'so_link'} = ''; + } + + add_to($config, { + 'cc' => $ccinfo{'binary_name'} . $abi_opts, + 'lib_opt' => $lib_opt_flags, + 'check_opt' => &$empty_if_nil($ccinfo{'check_opt_flags'}), + 'mach_opt' => mach_opt($config), + 'lang_flags' => $lang_flags, + 'warn_flags' => &$empty_if_nil($ccinfo{'warning_flags'}), + + 'ar_command' => $ar_command, + 'ranlib_command' => $ranlib_command, + 'static_suffix' => os_info_for($os, 'static_suffix'), + 'so_suffix' => os_info_for($os, 'so_suffix'), + 'obj_suffix' => os_info_for($os, 'obj_suffix'), + + 'dll_export_flags' => $ccinfo{'dll_export_flags'}, + 'dll_import_flags' => $ccinfo{'dll_import_flags'}, + + 'install_cmd_exec' => os_info_for($os, 'install_cmd_exec'), + 'install_cmd_data' => os_info_for($os, 'install_cmd_data'), + }); +} + +sub generate_makefile { + my ($config) = @_; + + my $is_in_doc_dir = + sub { -e File::Spec->catfile($$config{'doc_src_dir'}, $_[0]) }; + + my $docs = file_list(undef, undef, undef, + map_to($$config{'doc_src_dir'}, + grep { &$is_in_doc_dir($_); } @DOCS)); + + $docs .= File::Spec->catfile($$config{'base-dir'}, 'readme.txt'); + + my $includes = file_list(undef, undef, undef, + map_to($$config{'build_include_botan'}, + keys %{$$config{'includes'}})); + + my $lib_objs = file_list($$config{'build_lib'}, '(\.cpp$|\.c$|\.S$)', + '.' . $$config{'obj_suffix'}, + %{$$config{'sources'}}); + + my $check_objs = file_list($$config{'build_check'}, '.cpp', + '.' . $$config{'obj_suffix'}, + %{$$config{'check_src'}}), + + my $lib_build_cmds = build_cmds($config, $$config{'build_lib'}, + '$(LIB_FLAGS)', $$config{'sources'}); + + my $check_build_cmds = build_cmds($config, $$config{'build_check'}, + '$(CHECK_FLAGS)', $$config{'check_src'}); + + add_to($config, { + 'lib_objs' => $lib_objs, + 'check_objs' => $check_objs, + 'lib_build_cmds' => $lib_build_cmds, + 'check_build_cmds' => $check_build_cmds, + + 'doc_files' => $docs, + 'include_files' => $includes + }); + + my $template_dir = File::Spec->catdir($$config{'config-dir'}, 'makefile'); + my $template = undef; + + my $make_style = $$config{'make_style'}; + + if($make_style eq 'unix') { + $template = File::Spec->catfile($template_dir, 'unix.in'); + + $template = File::Spec->catfile($template_dir, 'unix_shr.in') + if($$config{'shared'} eq 'yes'); + + add_to($config, { + 'link_to' => libs('-l', '', 'm', @{$$config{'mod_libs'}}), + }); + } + elsif($make_style eq 'nmake') { + $template = File::Spec->catfile($template_dir, 'nmake.in'); + + add_to($config, { + 'shared' => 'no', + 'link_to' => libs('', '', '', @{$$config{'mod_libs'}}), + }); + } + + croak("Don't know about makefile format '$make_style'") + unless defined $template; + + trace("'$make_style' -> '$template'"); + + process_template($template, $$config{'makefile'}, $config); + + autoconfig("Wrote ${make_style}-style makefile in $$config{'makefile'}"); +} + +################################################## +# Configuration Guessing # +################################################## +sub guess_cpu_from_this +{ + my $cpuinfo = lc $_[0]; + + $cpuinfo =~ s/\(r\)//g; + $cpuinfo =~ s/\(tm\)//g; + $cpuinfo =~ s/ //g; + + trace("guess_cpu_from_this($cpuinfo)"); + + # The 32-bit SPARC stuff is impossible to match to arch type easily, and + # anyway the uname stuff will pick up that it's a SPARC so it doesn't + # matter. If it's an Ultra, assume a 32-bit userspace, no 64-bit code + # possible; that's the most common setup right now anyway + return 'sparc32-v9' if($cpuinfo =~ /ultrasparc/); + + # Should probably do this once and cache it + my @names; + my %all_alias; + + foreach my $arch (keys %CPU) { + my %info = %{$CPU{$arch}}; + + foreach my $submodel (@{$info{'submodels'}}) { + push @names, $submodel; + } + + if(defined($info{'submodel_aliases'})) { + my %submodel_aliases = %{$info{'submodel_aliases'}}; + foreach my $sm_alias (keys %submodel_aliases) { + push @names, $sm_alias; + $all_alias{$sm_alias} = $submodel_aliases{$sm_alias}; + } + } + } + + @names = sort { length($b) <=> length($a) } @names; + + foreach my $name (@names) { + if($cpuinfo =~ $name) { + trace("Matched '$cpuinfo' against '$name'"); + + return $all_alias{$name} if defined($all_alias{$name}); + + return $name; + } + } + + trace("Couldn't match $cpuinfo against any submodels"); + + # No match? Try arch names. Reset @names + @names = (); + + foreach my $arch (keys %CPU) { + my %info = %{$CPU{$arch}}; + + push @names, $info{'name'}; + + foreach my $alias (@{$info{'aliases'}}) { + push @names, $alias; + } + } + + @names = sort { length($b) <=> length($a) } @names; + + foreach my $name (@names) { + if($cpuinfo =~ $name) { + trace("Matched '$cpuinfo' against '$name'"); + return $name; + } + } + + return ''; +} + +# Do some WAGing and see if we can figure out what system we are. Think about +# this as a really moronic config.guess +sub guess_compiler +{ + my @CCS = ('gcc', 'msvc', 'icc', 'compaq', 'kai'); + + # First try the CC enviornmental variable, if it's set + if(defined($ENV{CC})) + { + my @new_CCS = ($ENV{CC}); + foreach my $cc (@CCS) { push @new_CCS, $cc; } + @CCS = @new_CCS; + } + + foreach (@CCS) + { + my $bin_name = $COMPILER{$_}{'binary_name'}; + if(which($bin_name) ne '') { + autoconfig("Guessing to use $_ as the compiler " . + "(use --cc to set)"); + return $_; + } + } + + croak( + "Can't find a usable C++ compiler, is PATH right?\n" . + "You might need to run with the --cc option (try $0 --help)\n"); +} + +sub guess_os +{ + sub recognize_os + { + my $os = os_alias($_[0]); + if(defined($OPERATING_SYSTEM{$os})) { + autoconfig("Guessing operating system is $os (use --os to set)"); + return $os; + } + return undef; + } + + my $guess = recognize_os($^O); + return $guess if $guess; + + trace("Can't guess os from $^O"); + + my $uname = $$config{'uname'}; + + if($uname ne '') { + $guess = recognize_os($uname); + return $guess if $guess; + trace("Can't guess os from $uname"); + } + + warning("Unknown OS ('$^O', '$uname'), falling back to generic code"); + return 'generic'; +} + +sub guess_cpu +{ + # If we have /proc/cpuinfo, try to get nice specific information about + # what kind of CPU we're running on. + my $cpuinfo = '/proc/cpuinfo'; + + if(defined($ENV{'CPUINFO'})) { + my $cpuinfo_env = $ENV{'CPUINFO'}; + + if(-e $cpuinfo_env and -r $cpuinfo_env) { + autoconfig("Will use $cpuinfo_env as /proc/cpuinfo"); + $cpuinfo = $cpuinfo_env; + } else { + warn("Could not read from ENV /proc/cpuinfo ($cpuinfo_env)"); + } + } + + if(-e $cpuinfo and -r $cpuinfo) + { + open CPUINFO, $cpuinfo or die "Could not read $cpuinfo\n"; + + while(<CPUINFO>) { + + chomp; + $_ =~ s/\t/ /g; + $_ =~ s/ +/ /g; + + if($_ =~ /^cpu +: (.*)/ or + $_ =~ /^model name +: (.*)/) + { + my $cpu = guess_cpu_from_this($1); + if($cpu ne '') { + autoconfig("Guessing CPU using $cpuinfo line '$_'"); + autoconfig("Guessing CPU is a $cpu (use --cpu to set)"); + return $cpu; + } + } + } + + autoconfig("*** Could not figure out CPU based on $cpuinfo"); + autoconfig("*** Please mail contents to lloyd\@randombit.net"); + } + + sub known_arch { + my ($name) = @_; + + foreach my $arch (keys %CPU) { + my %info = %{$CPU{$arch}}; + + return 1 if $name eq $info{'name'}; + foreach my $submodel (@{$info{'submodels'}}) { + return 1 if $name eq $submodel; + } + + foreach my $alias (@{$info{'aliases'}}) { + return 1 if $name eq $alias; + } + + if(defined($info{'submodel_aliases'})) { + my %submodel_aliases = %{$info{'submodel_aliases'}}; + foreach my $sm_alias (keys %submodel_aliases) { + return 1 if $name eq $sm_alias; + } + } + } + + my $guess = guess_cpu_from_this($name); + + return 0 if($guess eq $name or $guess eq ''); + + return known_arch($guess); + } + + my $uname = $$config{'uname'}; + if($uname ne '') { + my $cpu = guess_cpu_from_this($uname); + + if($cpu ne '') + { + autoconfig("Guessing CPU using uname output '$uname'"); + autoconfig("Guessing CPU is a $cpu (use --cpu to set)"); + + return $cpu if known_arch($cpu); + } + } + + my $config_archname = $Config{'archname'}; + my $cpu = guess_cpu_from_this($config_archname); + + if($cpu ne '') + { + autoconfig("Guessing CPU using Config{archname} '$config_archname'"); + autoconfig("Guessing CPU is a $cpu (use --cpu to set)"); + + return $cpu if known_arch($cpu); + } + + warning("Could not determine CPU type (try --cpu option)"); + return 'generic'; +} |