//
// RegexString.cc for pekwm
// Copyright (C)  2003-2005 Claes Nasten <pekdon{@}pekdon{.}net>
//
// This program is licensed under the GNU GPL.
// See the LICENSE file for more information.
//

#include "../config.h"

#include "RegexString.hh"

#include <iostream>

using std::cerr;
using std::endl;
using std::string;
using std::list;

//! @brief RegexString constructor.
RegexString::RegexString (void) :
  m_reg_ok (false), m_i_ref_max (1)
{
}

//! @brief RegexString destructor.
RegexString::~RegexString (void)
{
  free_regex ();
}

//! @brief Simple ed s command lookalike.
bool
RegexString::ed_s (std::string &or_string)
{
  if (!m_reg_ok)
    return false;

  const char *op_str = or_string.c_str();
  regmatch_t *op_matches = new regmatch_t[m_i_ref_max];

  if (regexec (&m_o_regex, or_string.c_str (), m_i_ref_max, op_matches, 0))
    {
      delete [] op_matches;
      return false;
    }

  string o_result;
  uint i_ref, i_size;

  list<RegexString::Part>::iterator it(m_o_ref_list.begin ());
  for (; it != m_o_ref_list.end(); ++it)
    {
      if (it->get_reference () >= 0)
        {
          i_ref = it->get_reference ();

          if (op_matches[i_ref].rm_so != -1)
            {
              i_size = op_matches[i_ref].rm_eo - op_matches[i_ref].rm_so;
              o_result.append (string (op_str + op_matches[i_ref].rm_so, i_size));
            }
        }
      else
        o_result.append (it->get_string ());
    }

  // Replace area regexp matched.
  i_size = op_matches[0].rm_eo - op_matches[0].rm_so;
  or_string.replace (op_matches[0].rm_so, i_size, o_result);

  return true;
}

//! @brief Parses match part of regular expression.
bool
RegexString::parse_match (const std::string &or_match)
{
  if (or_match.size ())
    {
      if (m_reg_ok)
        free_regex ();
      m_reg_ok = !regcomp (&m_o_regex, or_match.c_str (), REG_EXTENDED);
    }
  else
   m_reg_ok = false;

  return m_reg_ok;
}

//! @brief Parses replace part of ed_s command.
//! Expects input in the style of /replace/me/. / can be any character
//! except \. References to sub expressions are made with \num. \0 Represents
//! the part of the string that matched.
bool
RegexString::parse_replace (const std::string &or_replace)
{
  m_i_ref_max = 0;

  string o_part;
  string::size_type o_begin = 0, o_end = 0, o_last = 0;

  // Go through the string and split at \num points
  while ((o_end = or_replace.find_first_of ('\\', o_begin)) != string::npos)
    {
      // Store string between references.
      if (o_end > o_last)
        {
          o_part = or_replace.substr (o_last, o_end - o_last);
          m_o_ref_list.push_back (RegexString::Part (o_part));
        }

      // Get reference number.
      for (o_begin = ++o_end; isdigit (or_replace[o_end]); o_end++)
        ;

      if (o_end > o_begin)
        {
          // Convert number and add item.
          o_part = or_replace.substr (o_begin, o_end - o_last);
          int i_ref = strtol (o_part.c_str (), NULL, 10);
          if (i_ref >= 0)
            {
              m_o_ref_list.push_back (RegexString::Part ("", i_ref));
              if (i_ref > m_i_ref_max)
                m_i_ref_max = i_ref;
            }
        }

      o_last = o_end;
      o_begin = o_last + 1;
    }

  if (o_begin < or_replace.size ())
    {
      o_part = or_replace.substr (o_begin, or_replace.size () - o_begin);
      m_o_ref_list.push_back (RegexString::Part (o_part));
    }

  m_i_ref_max++;

  return true;
}

//! @brief Parses ed s style command. /from/to/
bool
RegexString::parse_ed_s (const std::string &or_ed_s)
{
  if (or_ed_s.size () < 3)
    return false;

  char c_delimeter = or_ed_s[0];
  string::size_type o_middle, o_end;

  // Middle.
  for (o_middle = 1; o_middle < or_ed_s.size (); o_middle++)
    {
      if ((or_ed_s[o_middle] == c_delimeter) && (or_ed_s[o_middle - 1] != '\\'))
        break;
    }

  // End.
  for (o_end = o_middle + 1; o_end < or_ed_s.size (); o_end++)
    {
      if ((or_ed_s[o_end] == c_delimeter) && (or_ed_s[o_end - 1] != '\\'))
        break;
    }

  string o_match, o_replace;
  o_match = or_ed_s.substr (1, o_middle - 1);
  o_replace = or_ed_s.substr (o_middle + 1, o_end - o_middle - 1);

  parse_match (o_match);
  parse_replace (o_replace);

  return true;
}

//! @brief Matches RegexString against or_rhs, needs successfull parse_match.
bool
RegexString::operator== (const std::string &or_rhs)
{
  if (!m_reg_ok)
   return false;

  return !regexec (&m_o_regex, or_rhs.c_str (), 0, 0, 0);
}

//! @brief Free resources used by RegexString.
void
RegexString::free_regex (void)
{
  if (m_reg_ok)
    {
      regfree (&m_o_regex);
      m_reg_ok = false;
    }
}


syntax highlighted by Code2HTML, v. 0.9.1