/*
 *
 * Copyright (c) 1998-2002
 * Dr John Maddock
 *
 * Use, modification and distribution are subject to the 
 * Boost Software License, Version 1.0. (See accompanying file 
 * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 *
 */
 
 /*
  *
  *   FILE     regress.cpp
  *   VERSION  see <boost/version.hpp>
  *
  * main() and associated code for regress.
  *
  */

#include <cstdlib>
#include <boost/regex.hpp>
#ifdef BOOST_RE_OLD_IOSTREAM
#include <fstream.h>
#else
#include <fstream>
using std::ifstream;
using std::ofstream;
//using std::cin;
//using std::cout;
using std::endl;
#endif

#ifdef __BORLANDC__
#pragma hrdstop
#endif

#include <boost/test/execution_monitor.hpp>

#include "regress.h"

#ifndef BOOST_REGEX_NO_TEST

#if defined(BOOST_MSVC) && defined(_DEBUG)
#include <CRTDBG.H>
#endif
#ifdef TIME_TEST
#include <boost/timer.hpp>
#endif


//
// declare all our globals here:
//

string_type expression;
string_type search_text;
string_type format_string;
string_type merge_string;
int matches[MAX_MATCHES];

char file[MAX_PATH];
int line;
bool do_test = false;
unsigned int flags[5];
unsigned error_count = 0;

void usage()
{
   cout <<

"Usage: regress filename [filename2...]\n"
"see tests.txt for an example test script\n"
"and the script format.\n";

}

int cpp_main(int argc, char * argv[])
{
#if defined(BOOST_MSVC) && defined(_DEBUG)
   // turn on heap reporting at program exit:
   //int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
   //tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
   //tmpFlag &= ~_CRTDBG_CHECK_CRT_DF;
   //_CrtSetDbgFlag( tmpFlag );
#endif
#ifdef TIME_TEST
   boost::timer tim;
#endif
   if(argc < 2)
       usage();
   int i;
   for(i = 1; i < argc; ++i)
   {
      reset_error();
      ifstream is(argv[i]);
      if(is.good() == false)
      {
         cout << "Error unable to open file: " << argv[i] << endl << endl;
         return -1;
      }
      std::strcpy(file, argv[i]);
      line = 0;
      unsigned int tests = 0;
      while(is.good())
      {
         string_type s;
         get_line(is, s);
         ++line;
         jm_trace("Reading test script line " << line << " " << make_narrow(s).c_str());
         parse_input_line(s);
         if(do_test)
         {
            run_tests();
            ++tests;
         }
      }
      cout << line << " lines, " << tests << " tests completed in file " << argv[i] << endl;
   }
#ifdef TIME_TEST
   double elapsed_time = tim.elapsed();
   cout << "Elapsed time = " << elapsed_time << "s" << endl;
#endif

   return error_count;
}

#ifdef TEST_UNICODE

std::string make_narrow(const wchar_t* ptr)
{
   std::string result;
   while(*ptr)
   {
      if(*ptr & ~0x7F)
      {
         char buf[10];
         std::sprintf(buf, "\\x%.4x", (int)*ptr);
         result.append(buf);
         ++ptr;
      }
      else
      {
         result.append(1, (char)*ptr);
         ++ptr;
      }
   }
   return result;
}

istream& get_line(istream& is, nstring_type& s, char delim)
{
   char c = (char)is.get();
   s.erase(s.begin(), s.end());
   while((c != delim) && is.good())
   {
      s.append(1, c);
      c = (char)is.get();
   }
   return is;
}

#ifdef BOOST_NO_STDC_NAMESPACE
namespace std{ using ::mbtowc; }
#endif

istream& get_line(istream& is, string_type& s, char delim)
{
   nstring_type t;
   get_line(is, t, delim);
   s.erase(s.begin(), s.end());
   const char* i, *j;
   i = t.c_str();
   j = t.c_str() + t.size();
   wchar_t* buf = new wchar_t[MB_CUR_MAX + 1];
   int cchars;
   while(i != j)
   {
      cchars = std::mbtowc(buf, i, j - i);
      if(cchars == -1)
         break;
      if(cchars == 0)
         break;
      s.insert(s.end(), buf, buf + cchars);
      i += cchars;
   }
   delete[] buf;
   return is;
}


#else

istream& get_line(istream& is, string_type& s, char delim)
{
   char c = (char)is.get();
   s.erase(s.begin(), s.end());
   while((c != delim) && is.good())
   {
      s.append(1, c);
      c = (char)is.get();
   }
   return is;
}

#endif

jm_debug_alloc::jm_debug_alloc()
{
   blocks = new int;
   count = new int;
   *blocks = 0;
   *count = 1;
   guard = this;
}

jm_debug_alloc::jm_debug_alloc(const jm_debug_alloc& d)
{
   if(&d != this)
   {
      blocks = d.blocks;
      count = d.count;
      ++(*count);
      guard = this;
   }
}
jm_debug_alloc& jm_debug_alloc::operator=(const jm_debug_alloc& d)
{
   if(&d != this)
   {
      free_();
      blocks = d.blocks;
      count = d.count;
      ++(*count);
   }
   return *this;
}

jm_debug_alloc::~jm_debug_alloc()
{
   if(guard != this)
   {
      cout << "Error: attempt to destroy object already destroyed" << endl;
   }
   else
   {
      free_();
      guard = 0;
   }
}
void jm_debug_alloc::free_()
{
    if(--(*count) == 0)
    {
       if(*blocks)
       {
          begin_error();
          cout << "Error: " << *blocks << " blocks not freed by allocator" << endl;
       }
       delete count;
       delete blocks;
    }
}

void* jm_debug_alloc::allocate(size_type n, void*)
{
   pointer p = new char[n + maxi(sizeof(size_type), boost::re_detail::padding_size)];
   *(size_type*)p = n;
   ++(*blocks);
   return p + maxi(sizeof(size_type), boost::re_detail::padding_size);
}
void jm_debug_alloc::deallocate(void* pv, size_type n)
{
   char* p = (char*)pv;
   p -= maxi(sizeof(size_type), boost::re_detail::padding_size);
   if(*(size_type*)p != n)
   {
      begin_error();
      cout << "Error: size mismatch in allocate/deallocate calls" << endl;
   }
   --(*blocks);
   delete[] p;
}

#else

#include <iostream>

int cpp_main(int argc, char * argv[])
{
   std::cout <<
   "\n<note>\n"
#if defined(BOOST_NO_WREGEX) && defined(TEST_UNICODE)
   "This platform does not provide the needed wide character support for this test.\n"
#elif defined(BOOST_REGEX_DYN_LINK)
   "Dynamic linking with this compiler is known not to work in this case - please complain to your compiler vendor.\n"
#else
   "This test has been disabled due to a compiler bug - please complain to your compiler vendor.\n"
#endif
   "</note>\n";
   return 0;
}

#endif


