//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//
/*
 *
 * Copyright (c) 1998-9
 * Dr John Maddock
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Dr John Maddock makes no representations
 * about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 *
 */

 /*
  *   FILE     regfmt.h
  *   VERSION  2.12
  *
  *   Provides formatting output routines for search and replace
  *   operations.  Note this is an internal header file included
  *   by regex.h, do not include on its own.
  */


#ifndef REGFMT_H
#define REGFMT_H


JM_NAMESPACE(__JM)

template <class O, class I>
O RE_CALL re_copy_out(O out, I first, I last)
{
   while(first != last)
   {
      *out = *first;
      ++out;
      ++first;
   }
   return out;
}

template <class charT>
void RE_CALL re_skip_format(const charT*& fmt
#ifdef RE_LOCALE_CPP
               , const __JM_STD::locale& l
#endif
               )
{
   #ifdef JM_NO_TEMPLATE_TYPENAME
   typedef char_regex_traits<charT> re_traits_type;
   #else
   typedef typename char_regex_traits<charT> re_traits_type;
   #endif
   unsigned int parens = 0;
   unsigned int c;
   while(*fmt)
   {
      c = re_traits_type::syntax_type(*fmt MAYBE_PASS_LOCALE(l));
      if((c == syntax_colon) && (parens == 0))
      {
         ++fmt;
         return;
      }
      else if(c == syntax_close_bracket)
      {
         if(parens == 0)
         {
            ++fmt;
            return;
         }
         --parens;
      }
      else if(c == syntax_open_bracket)
         ++parens;
      else if(c == syntax_slash)
      {
         ++fmt;
         if(*fmt == 0)
            return;
      }
      ++fmt;
   }
}

#ifdef JM_NO_OI_ASSIGN

//
// ugly hack for buggy output iterators

template <class T>
inline void oi_assign(T* p, T v)
{
   jm_destroy(p);
   jm_construct(p, v);
}

#else

template <class T>
inline void oi_assign(T* p, T v)
{
   //
   // if you get a compile time error in here then you either
   // need to rewrite your output iterator to make it assignable
   // (as is required by the standard), or define JM_NO_OI_ASSIGN
   // to use the ugly hack above
   *p = v;
}

#endif

#if defined(JM_NO_TEMPLATE_SWITCH_MERGE) && !defined(JM_NO_NAMESPACES)
//
// Ugly ugly hack,
// template don't merge if they contain switch statements so declare these
// templates in unnamed namespace (ie with internal linkage), each translation
// unit then gets its own local copy, it works seemlessly but bloats the app.
namespace{
#endif

//
// algorithm reg_format:
// takes the result of a match and a format string
// and merges them to produce a new string which
// is sent to an OutputIterator,
// __reg_format_aux does the actual work:
//
template <class OutputIterator, class iterator, class Allocator, class charT>
OutputIterator RE_CALL __reg_format_aux(OutputIterator out, 
                          const reg_match<iterator, Allocator>& m, 
                          const charT*& fmt,
                          bool isif
#ifdef RE_LOCALE_CPP
                           , const __JM_STD::locale& l
#endif
                           )
{
   #ifdef JM_NO_TEMPLATE_TYPENAME
   typedef char_regex_traits<charT> re_traits_type;
   #else
   typedef typename char_regex_traits<charT> re_traits_type;
   #endif

   const charT* fmt_end = fmt;
   while(*fmt_end) ++ fmt_end;

   while(*fmt)
   {
      switch(re_traits_type::syntax_type(*fmt MAYBE_PASS_LOCALE(l)))
      {
      case syntax_dollar:
         ++fmt;
         if(*fmt == 0) // oops trailing $
         {
            --fmt;
            *out = *fmt;
            ++out;
            return out;
         }
         switch(re_traits_type::syntax_type(*fmt MAYBE_PASS_LOCALE(l)))
         {
         case syntax_start_buffer:
            oi_assign(&out, re_copy_out(out, iterator(m[-1].first), iterator(m[-1].second)));
            ++fmt;
            continue;
         case syntax_end_buffer:
            oi_assign(&out, re_copy_out(out, iterator(m[-2].first), iterator(m[-2].second)));
            ++fmt;
            continue;
         case syntax_digit:
         {
            unsigned int index = re_traits_type::toi(fmt, fmt_end, 10 MAYBE_PASS_LOCALE(l));
            oi_assign(&out, re_copy_out(out, iterator(m[index].first), iterator(m[index].second)));
            continue;
         }
         }
         // anything else:
         if(*fmt == '&')
         {
            oi_assign(&out, re_copy_out(out, iterator(m[0].first), iterator(m[0].second)));
            ++fmt;
         }
         else
         {
            // probably an error, treat as a literal '$'
            --fmt;
            *out = *fmt;
            ++out;
            ++fmt;
         }
         continue;
      case syntax_slash:
      {
         // escape sequence:
         charT c;
         ++fmt;
         if(*fmt == 0)
         {
            --fmt;
            *out = *fmt;
            ++out;
            ++fmt;
            return out;
         }
         switch(re_traits_type::syntax_type(*fmt MAYBE_PASS_LOCALE(l)))
         {
         case syntax_a:
            c = '\a';
            ++fmt;
            break;
         case syntax_f:
            c = '\f';
            ++fmt;
            break;
         case syntax_n:
            c = '\n';
            ++fmt;
            break;
         case syntax_r:
            c = '\r';
            ++fmt;
            break;
         case syntax_t:
            c = '\t';
            ++fmt;
            break;
         case syntax_v:
            c = '\v';
            ++fmt;
            break;
         case syntax_x:
            ++fmt;
            if(fmt == fmt_end)
            {
               *out = *--fmt;
               ++out;
               return out;
            }
            // maybe have \x{ddd}
            if(re_traits_type::syntax_type(*fmt MAYBE_PASS_LOCALE(l)) == syntax_open_brace)
            {
               ++fmt;
               if(fmt == fmt_end)
               {
                  fmt -= 2;
                  *out = *fmt;
                  ++out;
                  ++fmt;
                  continue;
               }
               if(re_traits_type::is_class(*fmt, char_class_xdigit MAYBE_PASS_LOCALE(l)) == false)
               {
                  fmt -= 2;
                  *out = *fmt;
                  ++out;
                  ++fmt;
                  continue;
               }
               c = (charT)re_traits_type::toi(fmt, fmt_end, -16 MAYBE_PASS_LOCALE(l));
               if(re_traits_type::syntax_type(*fmt MAYBE_PASS_LOCALE(l)) != syntax_close_brace)
               {
                  while(re_traits_type::syntax_type(*fmt MAYBE_PASS_LOCALE(l)) != syntax_slash)
                     --fmt;
                  ++fmt;
                  *out = *fmt;
                  ++out;
                  ++fmt;
                  continue;
               }
               ++fmt;
               break;
            }
            else
            {
               if(re_traits_type::is_class(*fmt, char_class_xdigit MAYBE_PASS_LOCALE(l)) == false)
               {
                  --fmt;
                  *out = *fmt;
                  ++out;
                  ++fmt;
                  continue;
               }
               c = (charT)re_traits_type::toi(fmt, fmt_end, -16 MAYBE_PASS_LOCALE(l));
            }
            break;
         case syntax_c:
            ++fmt;
            if(fmt == fmt_end)
            {
               --fmt;
               *out = *fmt;
               ++out;
               return out;
            }
            if(((typename re_traits_type::uchar_type)(*fmt) < (typename re_traits_type::uchar_type)'@')
                  || ((typename re_traits_type::uchar_type)(*fmt) > (typename re_traits_type::uchar_type)127) )
            {
               --fmt;
               *out = *fmt;
               ++out;
               ++fmt;
               break;
            }
            c = (charT)((typename re_traits_type::uchar_type)(*fmt) - (typename re_traits_type::uchar_type)'@');
            ++fmt;
            break;
         case syntax_e:
            c = (charT)27;
            ++fmt;
            break;
         case syntax_digit:
            c = (charT)re_traits_type::toi(fmt, fmt_end, -8 MAYBE_PASS_LOCALE(l));
            break;
         default:
            c = *fmt;
            ++fmt;
         }
         *out = c;
         continue;
      }
      case syntax_open_bracket:
         ++fmt;  // recurse
         oi_assign(&out, __reg_format_aux(out, m, fmt, false MAYBE_PASS_LOCALE(l)));
         continue;
      case syntax_close_bracket:
         ++fmt;  // return from recursion
         return out;
      case syntax_colon:
         if(isif)
         {
            ++fmt;
            return out;
         }
         *out = *fmt;
         ++out;
         ++fmt;
         continue;
      case syntax_question:
      {
         ++fmt;
         if(*fmt == 0)
         {
            --fmt;
            *out = *fmt;
            ++out;
            ++fmt;
            return out;
         }
         unsigned int id = re_traits_type::toi(fmt, fmt_end, 10 MAYBE_PASS_LOCALE(l));
         if(m[id].matched)
         {
            oi_assign(&out, __reg_format_aux(out, m, fmt, true MAYBE_PASS_LOCALE(l)));
            if(re_traits_type::syntax_type(*(fmt-1) MAYBE_PASS_LOCALE(l)) == syntax_colon)
               re_skip_format(fmt MAYBE_PASS_LOCALE(l));
         }
         else
         {
            re_skip_format(fmt MAYBE_PASS_LOCALE(l));
            if(re_traits_type::syntax_type(*(fmt-1) MAYBE_PASS_LOCALE(l)) == syntax_colon)
               oi_assign(&out, __reg_format_aux(out, m, fmt, true MAYBE_PASS_LOCALE(l)));
         }
         return out;
      }
      default:
         *out = *fmt;
         ++out;
         ++fmt;
      }
   }

   return out;
}

#if defined(JM_NO_TEMPLATE_SWITCH_MERGE) && !defined(JM_NO_NAMESPACES)
} // namespace
#endif


template <class OutputIterator, class iterator, class Allocator, class charT>
OutputIterator RE_CALL reg_format(OutputIterator out,
                          const reg_match<iterator, Allocator>& m,
                          const charT* fmt
#ifdef RE_LOCALE_CPP
                         , __JM_STD::locale locale_inst = __JM_STD::locale()
#endif
                         )
{
   //
   // start by updating the locale:
   //
#if defined(RE_LOCALE_C) || defined(RE_LOCALE_W32)
   static re_initialiser<charT> locale_initialiser;
   locale_initialiser.update();
#else
   if(JM_HAS_FACET(locale_inst, regfacet<charT>) == false)
   {
#ifdef _MSC_VER
      locale_inst = __JM_STD::_ADDFAC(locale_inst, new regfacet<charT>());
#else
      locale_inst = __JM_STD::locale(locale_inst, new regfacet<charT>());
#endif
   }
   JM_USE_FACET(locale_inst, regfacet<charT>).update(locale_inst);
#endif
   return __reg_format_aux(out, m, fmt, false MAYBE_PASS_LOCALE(locale_inst));
}

template <class S>
class string_out_iterator
{
   S* out;
public:
   string_out_iterator(S& s) : out(&s) {}
   string_out_iterator& operator++() { return *this; }
   string_out_iterator& operator++(int) { return *this; }
   string_out_iterator& operator*() { return *this; }
   string_out_iterator& operator=(typename S::value_type v) 
   { 
      out->append(1, v); 
      return *this; 
   }
};

#ifndef JM_NO_STRING_DEF_ARGS
template <class iterator, class Allocator, class charT>
__JM_STD::basic_string<charT> RE_CALL reg_format(const reg_match<iterator, Allocator>& m, const charT* fmt
#ifdef RE_LOCALE_CPP
                                  , __JM_STD::locale locale_inst = __JM_STD::locale()
#endif
                                  )
{
   __JM_STD::basic_string<charT> result;
   string_out_iterator<__JM_STD::basic_string<charT> > i(result);
   reg_format(i, m, fmt MAYBE_PASS_LOCALE(locale_inst));
   return result;
}
#elif !defined(JM_NO_STRING_H)
template <class iterator, class Allocator>
__JM_STD::string RE_CALL reg_format(const reg_match<iterator, Allocator>& m, const char* fmt
#ifdef RE_LOCALE_CPP
                                  , __JM_STD::locale locale_inst = __JM_STD::locale()
#endif
                                  )
{
   __JM_STD::string result;
   string_out_iterator<__JM_STD::string> i(result);
   reg_format(i, m, fmt MAYBE_PASS_LOCALE(locale_inst));
   return result;
}
#endif


template <class OutputIterator, class iterator, class charT, class Allocator>
class merge_out_predicate
{
   OutputIterator* out;
   iterator* last;
   const charT* fmt;
   bool copy_none;

#ifdef RE_LOCALE_CPP
   const __JM_STD::locale& l;
#endif

public:
   merge_out_predicate(OutputIterator& o, iterator& pi, const charT* f, bool c
#ifdef RE_LOCALE_CPP
      , const __JM_STD::locale& loc
#endif
      ) : out(&o), last(&pi), fmt(f), copy_none(c)
#ifdef RE_LOCALE_CPP
      , l(loc)
#endif
   {}

   ~merge_out_predicate() {}
   bool RE_CALL operator()(const __JM::reg_match<iterator, Allocator>& m)
   {
      const charT* f = fmt;
      if(copy_none)
         oi_assign(out, re_copy_out(*out, iterator(m[-1].first), iterator(m[-1].second)));
      oi_assign(out, __reg_format_aux(*out, m, f, false MAYBE_PASS_LOCALE(l)));
      *last = m[-2].first;
      return true;
   }
};


template <class OutputIterator, class iterator, class traits, class Allocator, class charT>
OutputIterator RE_CALL reg_merge(OutputIterator out, 
                         iterator first,
                         iterator last,
                         const reg_expression<charT, traits, Allocator>& e, 
                         const charT* fmt, 
                         bool copy = true, 
                         unsigned int flags = match_default)
{
   //
   // start by updating the locale:
   //
#if defined(RE_LOCALE_C) || defined(RE_LOCALE_W32)
   static re_initialiser<charT> locale_initialiser;
   locale_initialiser.update();
#else
   __JM_STD::locale locale_inst(e.locale());
   if(JM_HAS_FACET(locale_inst, regfacet<charT>) == false)
   {
#ifdef _MSC_VER
      locale_inst = __JM_STD::_ADDFAC(locale_inst, new regfacet<charT>());
#else
      locale_inst = __JM_STD::locale(locale_inst, new regfacet<charT>());
#endif
   }
   JM_USE_FACET(locale_inst, regfacet<charT>).update(locale_inst);
#endif
   iterator l = first;
   merge_out_predicate<OutputIterator, iterator, charT, Allocator> oi(out, l, fmt, copy MAYBE_PASS_LOCALE(locale_inst));
   reg_grep(oi, first, last, e, flags);
   return copy ? re_copy_out(out, l, last) : out;
}

#ifndef JM_NO_STRING_DEF_ARGS
template <class traits, class Allocator, class charT>
__JM_STD::basic_string<charT> RE_CALL reg_merge(const __JM_STD::basic_string<charT>& s,
                         const reg_expression<charT, traits, Allocator>& e, 
                         const charT* fmt, 
                         bool copy = true, 
                         unsigned int flags = match_default)
{
   __JM_STD::basic_string<charT> result;
   string_out_iterator<__JM_STD::basic_string<charT> > i(result);
   reg_merge(i, s.begin(), s.end(), e, fmt, copy, flags);
   return result;
}
#elif !defined(JM_NO_STRING_H)
template <class traits, class Allocator>
__JM_STD::string RE_CALL reg_merge(const __JM_STD::string& s,
                         const reg_expression<char, traits, Allocator>& e, 
                         const char* fmt, 
                         bool copy = true, 
                         unsigned int flags = match_default)
{
   __JM_STD::string result;
   string_out_iterator<__JM_STD::string> i(result);
   reg_merge(i, s.begin(), s.end(), e, fmt, copy, flags);
   return result;
}
#endif


JM_END_NAMESPACE

#endif