#!/usr/bin/perl
#
# jcpp Copyright (C) 1999 Jochen Hoenicke.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.  If not, write to
# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
#
# $Id: jcpp,v 1.5 1999/06/29 09:22:04 jochen Exp $

# This is a program to allow conditional compiled code in java files.
# The key idea is, not to run the file always through the
# preprocessor, but to modify the java files directly and make use of
# comments.
#
# The comments all have the form /// to distinguish them from normal
# comments. You should not use such comments yourself.
#
# Usage is simple:  jcpp -Ddefine1 -Ddefine2 first.java second.java
# The files should contain comments of the form
#
#  ///#ifdef JDK12
#      jdk1.2 code
#  ///#else
#      jdk1.1 code
#  ///#endif
#
# After running jcpp the false branch is commented out.  If the true
# branch was commented out it will get commented in.

my @files;
my %defs;

for (@ARGV) {
    if ($_ =~ /^-D([^=]*)$/) {
	$defs{$1} = 1;
    } elsif ($_ =~ /^-D([^=]*)=([^=]*)$/) {
	$defs{$1} = $2;
    } else {
	push @files, $_;
    }
}

for (@files) {
    # Number of nested #if directives.  Initially 0, will be increased
    # on every #if directive and decreased on every #endif directive.
    my $level = 0;

    # The number of the outermost level, whose #if directive was
    # false.  This is 0, if there wasn't an false #if directive, yet.
    # As long as it is != 0, we comment every line, and ignore
    # directives except for increasing/decreasing $level.  
    my $falselevel = 0;

    # Tells if an error occured and the transformation shouldn't
    # be done.
    my $error = 0;
    my $changes = 0;

    my $file = $_;
    open OLD, "<$file" or do {
	print STDERR "Can't open file $file\n"; 
	next;
    };
    open NEW, ">$file.tmp" or do {
	print STDERR "Can't open tmp file $file.tmp\n"; 
	next;
    };
    my $linenr = 0;
  LINE:
    while (<OLD>) {
	$linenr++;
	if (m'^///#') {
	    # This is a directive.  First we print it out.
	    print NEW $_;
	    if (m'^///# *if') {
		$level++;
		if (m'^///# *ifdef (\S*)$') {
		    # If there was an outer false #if directive, we ignore the
		    # condition.
		    next LINE if ($falselevel);

		    my $label=$1;

		    # An ifdef directive, look if -D is defined.
		    $falselevel = $level
			unless (defined $defs{$label});
		} elsif (m'^///# *ifndef (\S*)$') {
		    # If there was an outer false #if directive, we ignore the
		    # condition.
		    next LINE if ($falselevel);
		    
		    my $label=$1;
		    # An ifndef directive, look if -D is defined
		    $falselevel = $level 
		        if (defined $defs{$label});
		}
	    } elsif (m'^///# *else') {
		# An else directive.  We switch from true to false and 
		# if level is falselevel we switch from false to true
		if ($level == 0) {
		    # An else outside of any directives; warn.
		    print STDERR "$file: $linenr: unmatched $_";
		    $error = 1;
		} elsif ($falselevel == $level) {
		    $falselevel = 0;
		} elsif ($falselevel == 0) {
		    $falselevel = $level;
		}
	    } elsif (m'^///# *endif') {
		# set $falselevel to 0, if the false branch is over now.
		$falselevel = 0 if ($falselevel == $level);
		# decrease level.
		if ($level == 0) {
		    print STDERR "$file: $linenr: unmatched $_";
		    $error = 1;
		} else {
		    $level--;
		}
	    } else {
		print STDERR "$file: $linenr: ignoring unknown directive $_";
		$error = 1;
	    }
	} elsif (m'^///(.*)') {
	    $line = $1;
	    if ($falselevel == 0 && $level > 0) {
		# remove comments in true branch, but not in outermost level.
		print NEW "$line\n";
		$changes = 1;
	    } else {
		# print out the full line with comments:
		print NEW $_;
	    }
	} else {
	    if ($falselevel != 0) {
		# add comments in false branch
		print NEW "///$_";
		$changes = 1;
	    } else {
		print NEW $_;
	    }
	}
    }

    if ($level != 0 || $falselevel != 0) {
	# something got wrong
	print STDERR "$file: unmatched directives: level $level, ".
	    "falselevel $falselevel\n";
	$error = 1;
    }

    if ($error == 0) {
	if ($changes == 0) {
	    unlink "$file.tmp";
	} else {
	    (rename "$file", "$file.orig" 
	     and rename "$file.tmp", "$file") 
		or print STDERR "$file: Couldn't rename files.\n";
	}
    } else {
	print STDERR "$file: errors occured, file not transformed.\n";
    }
}
