/*******************************************************************\

Module:

Author: Daniel Kroening, kroening@cs.cmu.edu

\*******************************************************************/

#include <stdio.h>
#include <unistd.h>

#include <fstream>

#include <config.h>
#include <i2string.h>
#include <error_handler.h>

#include "c_preprocess.h"

/*******************************************************************\

Function: c_preprocess

  Inputs:

 Outputs:

 Purpose: ANSI-C preprocessing

\*******************************************************************/

bool c_preprocess(
  std::istream &instream,
  const std::string &path,
  std::ostream &outstream,
  message_handlert &message_handler)
{
  // preprocessing
  error_handlert error_handler(message_handler);

  std::string file=path,
              stderr_file="tmp.stderr.txt";

  if(path=="") // stdin
  {
    char ch;

    file="tmp.stdin.c";
    FILE *tmp=fopen(file.c_str(), "wt");

    while(instream.read(&ch, 1)!=NULL)
      fputc(ch, tmp);

    fclose(tmp);
  }

  std::string command;
  
  // use VC98 CL in case of MINGW
  
  #ifdef __MINGW32__
  command="CL /nologo /E /D__CPROVER__";
  command+=" /D__WORDSIZE="+i2string(config.ansi_c.word_size);
  command+=" /D_WCHAR_T_DEFINED";
  command+=" /D__PTRDIFF_TYPE__=int";
  #else
  command="cpp -undef -D__CPROVER__ -D__null=0";
  command+=" -D__WORDSIZE="+i2string(config.ansi_c.word_size);
  command+=" -D__PTRDIFF_TYPE__=int";
  #endif  
  
  for(std::list<std::string>::const_iterator
      it=config.ansi_c.defines.begin();
      it!=config.ansi_c.defines.end();
      it++)
    #ifdef __MINGW32__
    command+=" /D \""+*it+"\"";
    #else
    command+=" -D'"+*it+"'";
    #endif

  for(std::list<std::string>::const_iterator
      it=config.ansi_c.include_paths.begin();
      it!=config.ansi_c.include_paths.end();
      it++)
    #ifdef __MINGW32__
    command+=" /I \""+*it+"\"";
    #else
    command+=" -I'"+*it+"'";
    #endif

  #ifdef __MINGW32__
  command+=" \""+file+"\"";
  command+=" 2> \""+stderr_file+"\"";
  #else
  command+=" \""+file+"\"";
  command+=" 2> \""+stderr_file+"\"";
  #endif

  FILE *stream=popen(command.c_str(), "r");

  if(stream!=NULL)
  {
    char ch;
    while((ch=fgetc(stream))!=EOF)
      outstream << ch;

    int result=pclose(stream);
    if(path=="") unlink(file.c_str());

    // errors/warnings
    std::ifstream stderr_stream(stderr_file.c_str());
    while((stderr_stream.read(&ch, 1))!=NULL)
      error_handler.err << ch;

    unlink(stderr_file.c_str());

    if(result!=0)
    {
      error_handler.error_parse(1);
      error_handler.error("Preprocessing failed");
      return true;
    }
    else
      error_handler.error_parse(2);
  }
  else
  {
    if(path=="") unlink(file.c_str());
    unlink(stderr_file.c_str());
    error_handler.error("Preprocessing failed (popen failed)");
    return true;
  }

  return false;
}
