#!/usr/bin/perl -w
#$Id: cc1,v 1.13 2005/02/19 05:58:01 hchen Exp $

use strict;
use File::Basename;
use Getopt::Long qw(:config gnu_compat bundling_override pass_through);
use File::Temp qw/ tempfile tempdir /;
use FileHandle;
use lib dirname($0) . "/../lib";
use Util;

sub setExtension($$)
{
  my ($filename, $ext) = @_;

  if ($filename !~ s/\.[^.]+/.$ext/)
  {
    $filename .= ".$ext";
  }

  return $filename;
}

sub isAutoConf($)
{
  my ($filename) = @_;

  return $filename eq "conftest.c" || $filename eq "conftest.cc" ||
    $filename eq "__conftest.c" || $filename eq "_autotst.c" ||
    $filename =~ /^dummy-/;
}

# Concatenate the string $cfgFile with the content of $cfgFile into $outputFile
sub createAssemblyFile($$$$)
{
  my ($inputFile, $cfgFile, $outputFile, $hasCFG) = @_;
  my ($fhIn, $fhOut, $str);

  if ($inputFile eq $outputFile)
  { 
    if (!$hasCFG)
    {
      return;
    }
    else
    {
      (undef, $inputFile) = tempfile($inputFile . "-XXXX", UNLINK => 1);
      rename($outputFile, $inputFile) or do
      { error("rename($outputFile, $inputFile): $!\n"); end(1); };
    }
  }
  if ($outputFile eq "-")
  {
    $fhOut = \*STDOUT;
  }
  else
  {
    $fhOut = new FileHandle ">$outputFile" or do
    { error("Cannot open $outputFile for writing: $!\n"); end(1); };
  }
  $fhIn = new FileHandle $inputFile or do
  { error("Cannot open $inputFile for reading: $!\n"); end(1); };
  print $fhOut "#MOPSCFG: $cfgFile\n" if ($hasCFG);
  while (defined($str = <$fhIn>))
  {
    print $fhOut $str;
  }
  $fhIn->close();
  $fhOut->close();
}

setLogFile($ENV{MOPS_LOG});
begin();

## Make sure we can get everything we need from the environment
my ($MOPS_ENABLE, $MOPS_TEMPDIR, $MOPS_GCC_CPP0, $MOPS_GCC_CC1,
    $MOPS_GCC_CC1PLUS, $MOPS_CC1);
my ($mops_gcc_cc, $str);

foreach my $var (qw(MOPS_ENABLE MOPS_TEMPDIR MOPS_GCC_CPP0 MOPS_GCC_CC1
		    MOPS_GCC_CC1PLUS MOPS_CC1))
{
  if (! defined($ENV{$var}))
  {
    info("Disable MOPS because environment variable $var is not set\n");
    end(1);
  }
  eval "\$$var = \"$ENV{$var}\"";
  debug("$var set to ", eval("\$$var"), "\n");
}
# cc1 or cc1plus
$str = basename($0);
if ($str eq "cc1")
{
  $mops_gcc_cc = $MOPS_GCC_CC1;
}
elsif ($str eq "cc1plus")
{
  $mops_gcc_cc = $MOPS_GCC_CC1PLUS;
}
else
{
  error("$0 is neither cc1 nor cc1plus\n");
  end(1);
}

if (!$MOPS_ENABLE)
{
  end(runSysCmd($mops_gcc_cc, @ARGV));
}

my ($inputFile, $outputFile, $isInputStdin, $isOutputStdout, $fh, $ret);

## Save @ARGV so we can invoke the real cc1 after using getopt my
my @cc1ARGV = @ARGV;

## Pull out the options that do not belong to cpp
my ($cppArg, $cppOnly, $dumpbase, $nocpp, $unused);
GetOptions("dumpbase=s" => \$dumpbase,
	   "ansi|pedantic" => \$unused,
	   "auxbase=s" => \$unused,
	   "E" => \$cppOnly,
	   "f=s" => \$unused,
	   "fpreprocessed" => \$nocpp,
	   "g" => \$unused,
	   "m=s" => \$unused,
	   "quiet" => \$unused,
	   "O:s" => \$unused,
	   "o=s" => \$outputFile,
	   "std=s" => \$unused,
	   "version" => \$unused,
	   "isystem=s" => \$unused,
           "mtune=s" => \$unused,
           "auxbase-strip=s" => \$unused,
	   "p" => \$unused);

## Now remove everything other than source files from ARGV
my @cppARGV = @ARGV;
GetOptions("A|D|I|iprefix=s" => \$cppArg,
	   "lang-c" => \$unused,
	   "M|MG|MM" => \$unused,
	   "MD|MMD=s" => \$unused,
	   "v" => \$unused,
	   "W:s" => \$unused,
	   "w" => \$unused);

if (defined($cppOnly))
{
  info("-E flag specified.  Disabling MOPS\n");
  end(runSysCmd($mops_gcc_cc, @cc1ARGV));
}

if (scalar(@ARGV) > 1)
{
  warn("Unprocessed arguments: @ARGV\n");
}

# find the input file
$inputFile = getInputFileName(\@ARGV);
if ($inputFile eq "-")
{
  $isInputStdin = 1;
  (undef, $inputFile) = tempfile($MOPS_TEMPDIR . "/stdin-XXXX",
				 SUFFIX => ".c", UNLINK => 1);
  $fh = new FileHandle ">$inputFile" or do
  {
    error("Cannot open $inputFile for writing\n");
    end(1);
  };
  while (defined($str = <STDIN>))
  {
    print $fh $str;
  }
  $fh->close();
}
else
{
  $isInputStdin = 0;
}

# find the output file
if (!defined($outputFile))
{
  if ($isInputStdin)
  {
    $outputFile = "-.s";
  }
  else
  {
    $outputFile = setExtension($inputFile, ".s");
  }
}
if ($outputFile eq "-")
{
  $isOutputStdout = 1;
  (undef, $outputFile) = tempfile($MOPS_TEMPDIR . "/stdout-XXXX",
				  SUFFIX => ".s", UNLINK => 1);
}
else
{
  $isOutputStdout = 0;
}

## Invoke cpp0 manually if we have to, then run rc on the resulting .i file
my $useCpp = (!$nocpp && $cppArg);
if ($useCpp)
{
  my $cppOutputFile;
  (undef, $cppOutputFile) = tempfile($MOPS_TEMPDIR . "/" .
				     myBaseName($inputFile) . "-XXXX",
				     SUFFIX => ".i", UNLINK => 1);
  # Have to use a patchlevel below 7 to properly process u_int8_t and the like
  # This is only a work around for RC limitations.
  push(@cppARGV, "-U__GNUC__", "-D__GNUC__=2",
       "-U__GNUC_MINOR__", "-D__GNUC_MINOR__=5",
       "-U__GNUC_PATCHLEVEL__", "-D__GNUC_PATCHLEVEL__=0",
       "-o", $cppOutputFile);
  #push(@cppARGV, "-o", $cppOutputFile);
  if (basename($0) eq "cc1plus")
  {
    push(@cppARGV, "-lang-c++");
  }
  if ($isInputStdin)
  {
    $ret = runSysCmd($MOPS_GCC_CPP0, @cppARGV, { STDIN => $inputFile });
  }
  else
  {
    $ret = runSysCmd($MOPS_GCC_CPP0, @cppARGV);
  }
  if ($ret != 0)
  {
    end($ret);
  }
  $inputFile = $cppOutputFile;
}

## Finally, we're ready to run rc
my ($cfgFile, @rcARGV, $hasCFG);

(undef, $cfgFile) = tempfile($MOPS_TEMPDIR . "/" . myBaseName($inputFile) .
			     "-XXXX", SUFFIX => ".cfg");
debug("Generating CFG $cfgFile from $inputFile\n");
@rcARGV = ("-fpreprocessed", "-o", $cfgFile);
$hasCFG = 1;
if (defined($dumpbase))
{
  push @rcARGV, "-dumpbase", $dumpbase;
  if (isAutoConf($dumpbase))
  {
    push @rcARGV, "-fparse-only";
    $hasCFG = 0;
  }
}
push @rcARGV, $inputFile;
$ret = runSysCmd($MOPS_CC1, @rcARGV);
if ($ret != 0)
{
  if (isAutoConf($dumpbase))
  {
    debug("rc fails to parse autoconf file\n");
  }
  else
  {
    error("rc returns $ret, MOPS giving up\n");
  }
  unlink($cfgFile);
  end(1);
}

# Run the real cc1
my (%ioRedirect);

if (!$isInputStdin && !$isOutputStdout)
{
  $ret = runSysCmd($mops_gcc_cc, @cc1ARGV);
}
else
{
  if ($isInputStdin)
  {
    $ioRedirect{STDIN} = $inputFile;
  }
  if ($isOutputStdout)
  {
    $ioRedirect{STDOUT} = $outputFile;
  }
  $ret = runSysCmd($mops_gcc_cc, @cc1ARGV, \%ioRedirect);
}
if ($ret != 0)
{
  end($ret);
}

# Insert the name of the CFG file into the .s file
if ($isOutputStdout)
{
  createAssemblyFile($outputFile, $cfgFile, "-", $hasCFG);
}
else
{
  createAssemblyFile($outputFile, $cfgFile, $outputFile, $hasCFG)
}

# testing only!
#link($inputFile, "$inputFile.tmp");
#info("saving $inputFile.tmp\n");
end(0);
