#!/usr/bin/perl
# mods_required v1.0  (c) 27.11.2015 by Andreas Ley  (u) 8.8.2019
# Extract required module identifiers from IfModule statements

@mods_available = ('/etc/apache2/mods-available');

use locale;
use Getopt::Std;
use File::Spec::Functions;
#use Data::Dumper;
#warn Data::Dumper->Dump([\%hash],['*hash']) if ($opt_D>0);

$modules = '/etc/apache2/myapacheconf.modules';

sub usage
{
	my $image = $0;
	$image =~ s!.*/!!;
	print STDERR "Usage: $image [-v] module-config filename [...]\n";
	exit(1);
}

getopts('DL:hxv') or &usage;

&usage if (defined($opt_h) || scalar(@ARGV)<2);

$opt_D = $opt_L if (defined($opt_L));
if (defined($opt_v) || defined($opt_D)) {
	$|=1;
	select((select(STDERR),$|=1)[0]);
}

open(MODULES,$modules) or die "Can't read $modules: $!\n";
while (<MODULES>) {
	chomp;
	my ($directive,$module_identifier,$source_file) = split;
	$source_to_identifier{$source_file} = $module_identifier;
	$identifier_to_source{$module_identifier} = $source_file;
}

for my $dir (@mods_available) {
	opendir(DIR,$dir) or die "Can't read directory $dir: $!\n";
	while ($entry = readdir(DIR)) {
		next if ($entry =~ /^\.\.?/);
		my $file = catfile($dir,$entry);
		open(FILE,'<',$file) or die "Can't read $file: $!\n";
		while (<FILE>) {
			chomp;
			warn "$.: \"$_\"" if ($opt_D>1);
			if (/^LoadModule\s+(\S+)/i) {
				warn "$.: \"$_\"" if ($opt_D>0);
				warn "$1 => \"$file\"" if ($opt_D>0);
				$module{$1} = $file;
			}
		}
		close(FILE);
	}
	closedir(DIR);
}

my $mod_config = shift;
&include(0,@ARGV);

open(CONFIG,'>',$mod_config) or die "Can't write to $mod_config: $!\n";
for my $module (sort keys %require) {
	unless($loaded{$module}) {
		print CONFIG "# $module";
		print CONFIG " / ",$identifier_to_source{$module} if (defined($identifier_to_source{$module}));
		print CONFIG " NOT FOUND!" unless (defined($module{$module}));
		print CONFIG "\n",map("# Used in $_\n",@{$require{$module}});
		print CONFIG "Include\t",$module{$module},"\n" if (defined($module{$module}));
	}
}
close(CONFIG);

sub include
{
	my $ignore = shift @_;
	for my $file (@_) {
		# Ignore earlier instance of config to be written
		next if ($file eq $mod_config);
		print $file,"\n" if (defined($opt_v));
		open(my $fd,'<',$file) or $ignore or die "Can't read $file: $!\n";
		while (<$fd>) {
			# FIXME: Handle continuation lines
			my $line = $_;
			chomp;
			warn "$ARGV:$.: \"$_\"" if ($opt_D>2);
			s/\s+$//;
			if (/^ServerRoot\s+(?:"(.+)"|([^"].*))$/i) {
				chdir($1.$2) or die "Can't chdir to ServerRoot $1$2: $!\n";
			}
			elsif (/^Include\s+(?:"(.+)"|([^"].*))$/i) {
				&include(0,glob($1.$2));
			}
			elsif (/^IncludeOptional\s+(?:"(.+)"|([^"].*))$/i) {
				&include(1,glob($1.$2));
			}
			elsif (/^LoadModule\s+(\S+)/i) {
				$loaded{$1}++;
			}
			elsif (/^<IfModule\s+!?([^>]+)>/i) {
				my $module = defined($source_to_identifier{$1}) ?  $source_to_identifier{$1} : $1;
				push(@{$require{$module}},$file.' line '.$.);
			}
		}
		close($fd);
	}
}
