#!/usr/bin/perl

package PPP;

use strict 'vars';
my $DEBUG = 0;
our %FLAGS;

unless (caller) {
  local $FLAGS{PPP} = 1;
  while ($ARGV[0] =~ /^[-+]/) {
    my $flag = shift;
    last if $flag eq '--';
    my ($s, $o) = $flag =~ /^([+-])(\w+)$/
      or die "Bizarre flag $flag; aborting\n";
    if ($s eq '+') {
      $FLAGS{$o} = 1;
    } else {
      $FLAGS{$o} = 0;
    }
  }

  my $error = preprocess(\*ARGV, \*STDOUT, \%FLAGS);
  if (defined $error && $error !~ / PPP$/) {
    die "malformed input: $error; aborting\n";
  }
  exit 0;
}

################################################################

#
# preprocess(inhandle, outhandle, flaghash)
sub preprocess {
  my ($in, $out, $flags) = @_;
  local *IN = $in;
  local *OUT = $out;
  local *FLAGS = $flags;
  local $FLAGS{PPP} = 1;
  my $err = process_block('PPP', 0, 1);
  return $err =~ / PPP$/ ? () : $err;
}

# We just read an 'ifdef' line.
# Process everything up to the matching 'endif'
# '$flag' is the name of the flag in which the 'ifdef' is conditioned
# '$ifndef' is true if the line was 'ifndef' instead of 'ifdef'
# '$printing' is true unless we are inside some *other* block that
# is being suppressed
# This function reads from *IN and writes to *OUT.
# It assumes that the symbols it should honor are listed in %FLAGS
sub process_block {
  my $flag = shift;
  my $ifndef = shift;
  my $printing = shift;
  my $correct_half = ($FLAGS{$flag} xor $ifndef);
  my $start = $.;
  while (<IN>) {
    if (/^#\s*if(n)?def\s+(\w+)/i) {
      my $n = $1 ? 1 : 0;
      my $tag = $2;
      print STDERR "Starting call for $tag (!=$n)\n" if $DEBUG;
      my $error = process_block($tag, $n, $printing && $correct_half);
      print STDERR "Returned from call for $tag\n" if $DEBUG;
      return $error if $error;
    } elsif (/#\s*endif/i) {
      return;
    } elsif (/#\s*else/i) {
      $correct_half = ! $correct_half;
      print STDERR "Saw 'else' for $flag\n" if $DEBUG;
    } else {
      print OUT $_ if $printing && $correct_half;
    }
  }
  return "No #endif found for line $start $flag";
}
