#!/usr/bin/perl
# Clive Darke 2006
# Additional modifications August 2008

use warnings;
use strict;
use Getopt::Std;

use App::sh2p::Parser;
use App::sh2p::Handlers;
use App::sh2p::Builtins;
use App::sh2p::Operators;
use App::sh2p::Compound;
use App::sh2p::Here;
use App::sh2p::Utils;
use constant (BREAK => '@');

sub process_script (\@);
sub convert (\@\@);           

my %g_block_commands = (while  => 'done',
                        until  => 'done',
                        for    => 'done',
                        select => 'done',
                        if     => 'fi',
                        case   => 'esac',
                        );

my $g_integer = 1;
my $g_runtime = 1;

our $VERSION = 0.01;
our $DEBUG   = 0;

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

sub outer
{
   my $num_of_files = 0;

   for my $script_file (@_)
   {
      open (my $script_h, '<', $script_file) || die "$script_file: $!\n";
      $num_of_files++;

      if ( $DEBUG ) {
         print STDERR "Processing $script_file\n";
      }
      
      my @the_script = <$script_h>;
      close $script_h;
      
      reset_globals();
      process_script (@the_script);
   }
   
   return $num_of_files;

}  # outer

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

sub process_script (\@)
{
   my ($ref) = @_;
   my $index = 0;
   my $limit = @$ref;
   my $line;
   my $delimiter = ';';
   my $here_label;
   my $here;
   my @statement_tokens;
  
   # Maybe make this optional?
   if ( $ref->[0] =~ /^#!/ ) {
      if ($ref->[0] =~ /^#!.*\/(t?csh)/) {
         die "Sorry, I cannot convert $1 scripts\n";
      }
      $index = 1;
   }
   
   # Maybe make these optional?
   out "#!/usr/bin/perl\n\n";
   out "# Generated by $0 on ".localtime()."\n\n";
   out "use warnings;\n";
   out "use strict;\n";
   
   out "use integer;\n"            if $g_integer;
   out "use App::sh2p::Runtime;\n" if $g_runtime;
   out "\n";
   
   # A foreach loop would be too simplistic
   OUTER:
   while ($index < $limit) {
      
      my @tokens;         

      $line .= $ref->[$index];
      $index++;

      # shortcut for blank lines
      if ($line =~ /^\s+$/) { 
          out $line;
          next 
      };

      # Remove leading whitespace
      # Remove leading & trailing whitespace
      # Also allows for Windows line endings (Cygwin)
      $line =~ s/^\s+//;
      $line =~ s/\s+$//;
      
      if ( substr($line,-1) eq '\\' ) {
         # Continuation character
         substr($line,-1) = "\n";
         next;
      }
      
      if ($line) {
         
         App::sh2p::Utils::mark_new_line();
         
         if ( $DEBUG ) {
             print STDERR "\nProcessing <$line>\n";
         }
         
         # Hack for here-docs
         if ( defined $here_label ) {

            if ($here_label eq $line) {
               $here_label = undef;
               $here->close();
            }
            else {
               # push line into here doc
               $here->write($line);            
            }
            $line = undef;
            next
         }
         
         # Hack for Bourne shell function syntax
         # Change it to ksh syntax (cheat)
         if ($line =~ /^(\w+)\s*\(\)(.*)/) {
             my $name = $1;
             my $rest = $2;
             $line = "function $name $rest"
         }
         
         @tokens = App::sh2p::Parser::tokenise ($line);
         
         # Look for statement delimiters
         for (my $i = 0; $i < @tokens; $i++) {
         
            my $tok = $tokens[$i];
            
            # This check is to 'read-ahead' looking for redirection
            if (exists $g_block_commands{$tok}) {
               $delimiter = $g_block_commands{$tok};
            }
            
            if ($tok eq '<<') {
                $i += 1;
                if ( !defined $tokens[$i] ) {
                    die "*** Malformed here document (no label) line ",$index + 1,"\n";
                }
                $here_label = $tokens[$i];
                $here_label =~ s/^\s+//;
                $here = App::sh2p::Here->open($here_label, '>');
            }
            
            if ($tok eq $delimiter) {
               # Look ahead to check for redirection
               # Currently only 'here' documents
               
               if ( defined $tokens[$i+1] && $tokens[$i+1] eq '<<' ) {   
                  push @statement_tokens, $tok;
               
                  $i += 2;
                  if ( !defined $tokens[$i] ) {
                     die "*** Malformed here document (no label) line ",$index + 1,"\n";
                  }
                  $here_label = $tokens[$i];
                  $here_label =~ s/^\s+//;
                  $here = App::sh2p::Here->open($here_label, '>');
               }
               elsif ($tok ne ';' && $tok ne BREAK) {
                  push @statement_tokens, $tok;
               }
               
               # Process statements 
               if (@statement_tokens) {
                  my @types  = App::sh2p::Parser::identify (0, @statement_tokens);
                  App::sh2p::Parser::convert (@statement_tokens, @types);
                  @statement_tokens = ();
               }
               $delimiter = ';';
            } 
            else {
               # Inside a while, until, for, or if
               push @statement_tokens, $tok;
            }
         }
         
         if (@statement_tokens && 
               ($delimiter eq ';'    || 
                $delimiter eq 'fi'   || 
                $delimiter eq 'done' ||
                $delimiter eq 'esac' )
             ) {
            my @types  = App::sh2p::Parser::identify (0, @statement_tokens);
            App::sh2p::Parser::convert (@statement_tokens, @types);
            @statement_tokens = ();
         }
         else {
            push @statement_tokens, BREAK
         }

      }
      
      # At end
      #out "\n" if ( !defined $here_label );
      $line = undef;
   }
 
}  # process_script
   
###########################################################
# main
my %args;

getopts ('ir', \%args);
$g_integer = 0 if exists $args{'i'};
$g_runtime = 0 if exists $args{'r'};

unless ( @ARGV ) {
   if (-t STDIN) {
      print 'Enter the name of a shell script: ';
   }
   chomp($ARGV[0] = <STDIN>);
   exit 0 if (!@ARGV || !$ARGV[0]);
}
   
outer(@ARGV);

__END__

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