use strict;
use warnings;
use Getopt::Long;

# Fuer Debug-Meldungen auf 1 setzen
my $DEBUG = 0;

# Ausgabepuffer
my $OUTPUT = "";

# Symboltabelle
my %SYMTAB = ();

# Text ueberspringen (bei IFDEF...)
my $skip = 0;

# Kommandozeilenoptionen
my @INCLUDE_DIRS = ();
my @DEFINES = ();

# Kommandozeilenoptionen lesen
# --define <symbol>=<value>
#   definieren eines Makrosymbols
#
# --include <dir>
#   Angabe von Include-Verzeichnissen
#   Default: aktuelles Verzeichnis

GetOptions (
	"include=s" => \@INCLUDE_DIRS,
	"define=s" => \@DEFINES,
  );

# das aktuelle Verzeichnis ist immer Include-Verzeichnis
push(@INCLUDE_DIRS, ".");

# Ein- und Ausgabedatei von der Kommandozeile
die "Usage: $0 [<Optionen>] <Eingabedatei> <Ausgabedatei>\n"
    unless ($ARGV[0] && $ARGV[1]);
my $INFILE = $ARGV[0];
my $OUTFILE = $ARGV[1];

# Makrodefinitionen der Kommandozeile eintragen
for my $def (@DEFINES)
  {
	if ($def =~ /(.*)=(.*)/)
	  {
		print "Define: $1 -> $2\n" if $DEBUG;
		$SYMTAB{$1} = $2;
    }
  }


# Eingabedatei bearbeiten
include($INFILE);

# Symboltabellen-Dump
if ($DEBUG)
  {
	print "\nSymboltabellendump\n";
	for (keys(%SYMTAB))
	  { print "$_: $SYMTAB{$_}\n"; }
  }

# Ausgabepuffer wegschreiben
open OUTFILE, '>', $OUTFILE or die "Oops: $OUTFILE $!\n";
print OUTFILE $OUTPUT;
close OUTFILE;


sub parse # ($line)
  {
  my $new = '';
  my $i = 0;
  my ($wort, $rest, $name, $value);
  # Zeile analysieren
	my $line = shift;
	chomp($line);
	# include
  if((! $skip) && ($line =~ /^\#include/))
	  {
	  $line =~ s/^\#include[\s]+//;  # Dateinamen ausschneiden
	  $line =~ s/[\s]*$//;           # Leerzeicehn am Ende weg
		print "Include-Datei $line\n" if $DEBUG;
		include($line);
		return;
		}
	# define - nur Makroname
	elsif((! $skip) && ($line =~ /^\#define/))
		{
		$line =~ s/\#define[\s]+//;
		($name, $value) = split(/\s*=\s*/, $line);
		$value = 1 unless(defined($value));
		print "Define $name --> $value\n" if $DEBUG;
		$SYMTAB{$name} = $value;
		return;
		}
	# ifdef
	elsif ($line =~ /^\#ifdef[\s]+([^\s]+)[\s]*/)
		{
    $skip = (defined $SYMTAB{$1})? 0 : 1;
		print "IFDEF $1 --> $skip\n" if $DEBUG;
		return;
	  }
  # endif
	elsif ($line =~ /^\#endif/)
		{
		$skip = 0;
		print "ENDIF --> 0\n" if $DEBUG;
		return;
		}
	# else
	elsif ($line =~ /^\#else/)
		{
		$skip = ($skip == 0)? 1 : 0;
		print "ELSE --> $skip\n" if $DEBUG;
		return;
	  }
	# Makroersetzung bzw. Zeile uebernehmen
	else
	  {
	  return if $skip;           # bedingte Ersetzung - nichts zu tun
    if($line =~ s/^(\s+)//)
      { $new .= $1; }          # fuehrende Leerzeichen beibehalten
    for (split(/(\s+)/, $line, -1))
      # trennen an den Leerzeichen, immer nur ein Ergebnis
      {
      if ($i++ & 1)            # $i ungerade --> Leerraum, fertig
        { $new .= $_; next; }
      $_ =~ /(\w+)(.*)/;       # $i gerade, Satzzeichen abtrennen
      ($wort, $rest) = ($1,$2);
      next unless($wort);
      # Falls Wort in Makroliste --> ersetzen
      $new .= (defined $SYMTAB{$wort})? $SYMTAB{$wort} : $wort;
      $new .= $rest;           # Rest wieder anhaengen
      print "Ersetzung: $wort --> $SYMTAB{$wort}\n"
         if ($DEBUG && defined $SYMTAB{$wort});
      }
    $line = $new;
    }
  $OUTPUT .= $line . "\n";
  }

sub include # ($file)
  {
	my $incfile = shift;
	local *INCFILE;

	# Include-Datei suchen
	my $found = 0;
  for (@INCLUDE_DIRS)
	  {
		print "Suche $incfile in $_\n" if ($DEBUG);
		if (open INCFILE, "$_/$incfile")
		  {
			$found = 1;
		  last;
		  }
		}

  # nichts gefunden?
	die "Oops: $incfile $!\n" unless $found;

  # Datei zeilenweise baearbeiten
	while (<INCFILE>)
	  {	parse($_);}
	close INCFILE;
  }

