#! /usr/bin/perl
#
#*** Copyright Acute

#***

use strict;

my %defines;

sub parse_opts () {
    my ($infile,$outfile);

    while(my $opt = shift @ARGV) {
        if($opt eq '-o') {
            $outfile = shift @ARGV;
        } elsif ($opt eq '-D') {
            $defines{shift @ARGV} = 1;
        } elsif ($opt =~ /^-D(\w+)$/) {
            $defines{$1} = 1;
        } else {
            $infile = $opt;
        }
    }

    die "no input file\n" if($infile eq undef);

    ($infile,$outfile)
}

my($in,$out) = parse_opts();

open INFILE,"<$in" || die "cannot open $in for reading\n";
if($out) {
    open OUTFILE,">$out" || die "cannot open $out for writing";
} else {
    open OUTFILE,'>-';
}

my $lnum = 0;
my $level = 0;
my @stack; # contains the last test result for each trunk of #if nested level
           # $stack[i] ==  0 means no test was on this level, but the parent one is ok
           # $stack[i] ==  1 this trunk test is sucessfull
           # $stack[i] == -1 an other trunk has been successful on this level, but not ours
           #                 OR we are in a subnested level of a failed test

$stack[0] = 1;

while(<INFILE>) {
    my $line = $_;
    chop($line);
    $lnum ++;

    if($line =~ /^#define\s+(\w+)\s*$/) {                   # #define ==> add it to the hash
        $defines{$1} = 1;
    } elsif ($line =~ /^#undef\s+(\w+)\s*$/) {              # #undef ==> undef it in the hash
        $defines{$1} = undef;
    } elsif ($line =~ /^#ifdef((?:\s+\w+)+)\s*$/) {         # #ifdef ==> one level up in the netsting
        $level ++;                                          #            and if we come from a bad trunk,
        $stack[$level] = 0;                                 #            stay in a bad state ($stack[$level] = -1)
        foreach my $key (split /\s+/,$1) {
            $stack[$level] = 1 if($defines{$key});
        }
        $stack[$level] = -1 if($stack[$level-1] < 1);
    } elsif ($line =~ /^#elseif((?:\s+\w+)+)\s*$/) {        # #elsif ==> stay in our level, handle stack
        die "#elseif without any opened #if on line $lnum : [$line]" if($level == 0);
        if($stack[$level]) {
            $stack[$level] = -1;
        } else {
            foreach my $key (split /\s+/,$1) {
                $stack[$level] = 1 if($defines{$key});
            }
        }
    } elsif ($line =~ /^#else\s*$/) {                       # #else ==> stay in our level, handle stack
        die "#else without any opened #if on line $lnum : [$line]" if($level == 0);
        if($stack[$level]) {
            $stack[$level] = -1;
        } else {
            $stack[$level] = 1;
        }
    } elsif ($line =~ /^#endif\s*$/) {                      # #endif ==> if level is >0, then climb donw in nesting
        die "#endif without any opened #if on line $lnum : [$line]" if($level == 0);
        $level--;
    } elsif ($line =~ /^#\w/) {                             # bouuuuuh, bad syntax somewhere
        die "i don't understand line $lnum : [$line]";
    }

    if(($stack[$level] == 1) and !($line =~ /^#\w/)) {
        print OUTFILE "$line\n";
    } else {
        print OUTFILE "\n";
    }
}

die "$level #if are not closed" if($level != 0);
