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

Module: Main Module 

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

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

#include <fstream>

#include <config.h>
#include <expr_util.h>

#include <goto-programs/goto_convert_functions.h>
#include <goto-programs/goto_check.h>
#include <goto-programs/show_claims.h>
#include <goto-programs/set_claims.h>
#include <goto-programs/read_goto_binary.h>

#include <pointer-analysis/value_propagation.h>
#include <pointer-analysis/goto_program_dereference.h>
#include <pointer-analysis/add_failed_symbols.h>
#include <pointer-analysis/show_value_sets.h>

#include "parseoptions.h"
#include "bmc.h"
#include "loop_ids.h"
#include "version.h"

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

Function: cbmc_parseoptionst::set_verbosity

  Inputs:

 Outputs:

 Purpose:

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

void cbmc_parseoptionst::set_verbosity(messaget &message)
{
  int v=8;
  
  if(cmdline.isset("verbosity"))
  {
    v=atoi(cmdline.getval("verbosity"));
    if(v<0)
      v=0;
    else if(v>9)
      v=9;
  }
  
  message.set_verbosity(v);
}

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

Function: cbmc_parseoptionst::get_command_line_options

  Inputs:

 Outputs:

 Purpose:

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

void cbmc_parseoptionst::get_command_line_options(optionst &options)
{
  if(cmdline.isset("16"))
    config.ansi_c.set_16();

  if(cmdline.isset("32"))
    config.ansi_c.set_32();
    
  if(cmdline.isset("64"))
    config.ansi_c.set_64();
    
  if(cmdline.isset("function"))
    config.main=cmdline.getval("function");
    
  if(cmdline.isset('D'))
    config.ansi_c.defines=cmdline.get_values('D');

  if(cmdline.isset('I'))
    config.ansi_c.include_paths=cmdline.get_values('I');

  if(cmdline.isset("floatbv"))
    config.ansi_c.use_fixed_for_float=false;

  if(cmdline.isset("little-endian"))
    config.ansi_c.endianess=configt::ansi_ct::IS_LITTLE_ENDIAN;

  if(cmdline.isset("big-endian"))
    config.ansi_c.endianess=configt::ansi_ct::IS_BIG_ENDIAN;

  if(cmdline.isset("little-endian") &&
     cmdline.isset("big-endian"))
  {
    usage_error();
    exit(1);
  }

  if(cmdline.isset("program-only"))
    options.set_option("program-only", true);

  if(cmdline.isset("show-vcc"))
    options.set_option("show-vcc", true);

  if(cmdline.isset("no-simplify"))
    options.set_option("simplify", false);
  else
    options.set_option("simplify", true);

  if(cmdline.isset("all-claims"))
    options.set_option("all-claims", true);
  else
    options.set_option("all-claims", false);

  if(cmdline.isset("unwind"))
    options.set_option("unwind", cmdline.getval("unwind"));

  if(cmdline.isset("debug-level"))
    options.set_option("debug-level", cmdline.getval("debug-level"));

  if(cmdline.isset("slice-by-trace"))
    options.set_option("slice-by-trace", cmdline.getval("slice-by-trace"));

  if(cmdline.isset("unwindset"))
    options.set_option("unwindset", cmdline.getval("unwindset"));

  // substitution previous expressions
  if(cmdline.isset("no-substitution"))
    options.set_option("substitution", false);
  else
    options.set_option("substitution", true);

  // check array bounds
  if(cmdline.isset("no-bounds-check"))
    options.set_option("bounds-check", false);
  else
    options.set_option("bounds-check", true);

  // check division by zero
  if(cmdline.isset("no-div-by-zero-check"))
    options.set_option("div-by-zero-check", false);
  else
    options.set_option("div-by-zero-check", true);

  // check overflow
  if(cmdline.isset("overflow-check"))
    options.set_option("overflow-check", true);
  else
    options.set_option("overflow-check", false);

  // check pointers
  if(cmdline.isset("no-pointer-check"))
    options.set_option("pointer-check", false);
  else
    options.set_option("pointer-check", true);

  // check assertions
  if(cmdline.isset("no-assertions"))
    options.set_option("assertions", false);
  else
    options.set_option("assertions", true);

  // magic error label
  if(cmdline.isset("error-label"))
    options.set_option("error-label", cmdline.getval("error-label"));

  // generate unwinding assertions
  options.set_option("unwinding-assertions",
   !cmdline.isset("no-unwinding-assertions"));

  // remove unused equations
  options.set_option("slice",
       cmdline.isset("slice"));

  // simplify if conditions and branches
  if(cmdline.isset("no-simplify-if"))
    options.set_option("simplify-if", false);
  else
    options.set_option("simplify-if", true);

  if(cmdline.isset("dimacs"))
    options.set_option("dimacs", true);

  if(cmdline.isset("bv-refine"))
    options.set_option("bv-refine", true);

  if(cmdline.isset("beautify-pbs"))
    options.set_option("beautify-pbs", true);

  if(cmdline.isset("beautify-greedy"))
    options.set_option("beautify-greedy", true);

  if(cmdline.isset("document-subgoals"))
    options.set_option("document-subgoals", true);

  options.set_option("pretty-names", 
                     !cmdline.isset("no-pretty-names"));

  if(cmdline.isset("cvc"))
    options.set_option("cvc", true);

  if(cmdline.isset("smt"))
    options.set_option("smt", true);

  if(cmdline.isset("outfile"))
    options.set_option("outfile", cmdline.getval("outfile"));
}

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

Function: cbmc_parseoptionst::doit

  Inputs:

 Outputs:

 Purpose: invoke main modules

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

int cbmc_parseoptionst::doit()
{
  if(cmdline.isset("version"))
  {
    std::cout << CBMC_VERSION << std::endl;
    return 0;
  }

  //
  // unwinding of transition systems
  //

  if(cmdline.isset("module") ||
    cmdline.isset("gen-interface"))

  {
    error("This version of CBMC has no support for "
          " hardware modules. Please use hw-cbmc.");
    return 1;
  }

  config.init();
  
  bmct bmc(context, ui_message_handler);

  //
  // command line options
  //

  get_command_line_options(bmc.options);
  set_verbosity(bmc);
  set_verbosity(*this);

  goto_functionst goto_functions;

  if(get_goto_program(bmc, goto_functions))
    return 6;

  if(cmdline.isset("show-claims"))
  {
    const namespacet ns(context);
    show_claims(ns, get_ui(), goto_functions);
    return 0;
  }

  if(set_claims(goto_functions))
    return 7;

  // do actual BMC
  return do_bmc(bmc, goto_functions);
}

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

Function: cbmc_parseoptionst::set_claims

  Inputs:

 Outputs:

 Purpose:

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

bool cbmc_parseoptionst::set_claims(goto_functionst &goto_functions)
{
  try
  {
    if(cmdline.isset("claim"))
      ::set_claims(goto_functions, cmdline.get_values("claim"));  
  }

  catch(const char *e)
  {
    error(e);
    return true;
  }

  catch(const std::string e)
  {
    error(e);
    return true;
  }
  
  catch(int)
  {
    return true;
  }
  
  return false;
}

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

Function: cbmc_parseoptionst::get_goto_program

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/
  
bool cbmc_parseoptionst::get_goto_program(
  bmc_baset &bmc,
  goto_functionst &goto_functions)
{
  try
  {
    if(cmdline.isset("xml-binary"))
    {
      status("Reading GOTO program from file");

      read_goto_binary(goto_functions);

      if(cmdline.isset("show-symbol-table"))
      {
        show_symbol_table();
        return true;
      }
    }
    else
    {
      if(cmdline.args.size()==0)
      {
        error("Please provide a program to verify");
        return true;
      }
    
      if(parse()) return true;
      if(typecheck()) return true;
      if(get_modules(bmc)) return true;    
      if(final()) return true;

      if(cmdline.isset("show-symbol-table"))
      {
        show_symbol_table();
        return true;
      }

      // we no longer need any parse trees or language files
      clear_parse();

      status("Generating GOTO Program");

      goto_convert_all(
        context, bmc.options, goto_functions,
        ui_message_handler);
    }

    remove_function_pointers(
      context, bmc.options, goto_functions,
      ui_message_handler);        

    if(process_goto_program(bmc, goto_functions))
      return true;
  }

  catch(const char *e)
  {
    error(e);
    return true;
  }

  catch(const std::string e)
  {
    error(e);
    return true;
  }
  
  catch(int)
  {
    return true;
  }
  
  return false;
}

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

Function: cbmc_parseoptionst::read_goto_binary

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/
  
bool cbmc_parseoptionst::read_goto_binary(
  goto_functionst &goto_functions)
{
  std::ifstream in(cmdline.getval("xml-binary"));
  
  if(!in)
  {
    error(
      std::string("Failed to open `")+
      cmdline.getval("xml-binary")+
      "'");
    return true;
  }
  
  ::read_goto_binary(
    in, context, goto_functions, *get_message_handler());
        
  return false;
}

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

Function: cbmc_parseoptionst::process_goto_program

  Inputs:

 Outputs:

 Purpose:

\*******************************************************************/
  
bool cbmc_parseoptionst::process_goto_program(
  bmc_baset &bmc,
  goto_functionst &goto_functions)
{
  try
  {
    // add loop ids
    set_loop_ids(goto_functions);

    // show it?
    if(cmdline.isset("show-loops"))
    {
      show_loop_ids(get_ui(), goto_functions);
      return true;
    }

    namespacet ns(context);

    // add generic checks
    goto_check(ns, bmc.options, goto_functions);
    
    status("Pointer Analysis");
    value_propagationt value_propagation(ns);
    value_propagation(goto_functions);

    // show it?
    if(cmdline.isset("show-value-sets"))
    {
      show_value_sets(get_ui(), goto_functions, value_propagation);
      return true;
    }

    status("Adding Pointer Checks");

    // add pointer checks
    pointer_checks(
      goto_functions, ns, bmc.options, value_propagation);

    // show it?
    if(cmdline.isset("show-goto-functions"))
    {
      goto_functions.output(ns, std::cout);
      return true;
    }
    
    // add failed symbols
    add_failed_symbols(context);
  }

  catch(const char *e)
  {
    error(e);
    return true;
  }

  catch(const std::string e)
  {
    error(e);
    return true;
  }
  
  catch(int)
  {
    return true;
  }
  
  return false;
}

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

Function: cbmc_parseoptionst::do_bmc

  Inputs:

 Outputs:

 Purpose: invoke main modules

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

int cbmc_parseoptionst::do_bmc(
  bmc_baset &bmc,
  const goto_functionst &goto_functions)
{
  bmc.set_ui(get_ui());

  // do actual BMC
  if(bmc.run(goto_functions))
    return 10;

  return 0;
}

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

Function: cbmc_parseoptionst::help

  Inputs:

 Outputs:

 Purpose: display command line help

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

void cbmc_parseoptionst::help()
{
  std::cout <<
    "\n"
    "* *            CBMC " CBMC_VERSION " - Copyright (C) 2001-2006           * *\n"
    "* *              Daniel Kroening, Edmund Clarke             * *\n"
    "* * Carnegie Mellon University, Computer Science Department * *\n"
    "* *                  kroening@cs.cmu.edu                    * *\n"
    "* *                U.S. Patent applied for                  * *\n"
    "\n"
    "Usage:                       Purpose:\n"
    "\n"
    " cbmc [-?] [-h] [--help]      show help\n"
    " cbmc file.c ...              source file names\n"
    "\n"
    "Additonal options:\n"
    " -I path                      set include path (C/C++)\n"
    " -D macro                     define preprocessor macro (C/C++)\n"
    " --program-only               only show program expression\n"
    " --function name              set main function name\n"
    " --no-simplify                do not simplify\n"
    " --all-claims                 keep all claims\n"
    " --unwind nr                  unwind nr times\n"
    " --unwindset nr               unwind given loop nr times\n"
    " --show-claims                only show claims\n"
    " --decide                     run decision procedure\n"
    " --dimacs                     generate CNF in DIMACS format\n"
    " --document-subgoals          generate subgoals documentation\n"
    " --slice                      remove assignments unrelated to property\n"
    " --no-substitution            do not perform substitution\n"
    " --no-simplify-if             do not simplify ?:\n"
    " --no-assertions              ignore assertions\n"
    " --no-unwinding-assertions    do not generate unwinding assertions\n"
    " --no-bounds-check            do not do array bounds check\n"
    " --no-div-by-zero-check       do not do division by zero check\n"
    " --no-pointer-check           do not do pointer check\n"
    " --no-pretty-names            do not simplify identifiers\n"
    " --overflow-check             enable arithmetic overflow check\n"
    " --bound nr                   number of transitions\n"
    " --module name                module to unwind\n"
    " --counterexample file        counterexample file to write\n"
    " --beautify-greedy            beautify the counterexample (greedy heuristic)\n"
    " --beautify-pbs               beautify the counterexample (PBS)\n"
    " --cvc                        Output subgoals in CVC syntax\n"
    " --smt                        Output subgoals in SMT syntax\n"
    " --outfile Filename           Output subgoals to given file\n"
    " --16, --32, --64             Set width of machine word\n"
    " --little-endian              Allow little-endian word-byte conversions\n"
    " --big-endian                 Allow big-endian word-byte conversions\n"
    " --show-symbol-table          Show symbol table\n"
    " --show-goto-functions        Show goto program\n"
    "\n";
}
