/* regexp.icc - inline functions for regexp.hh  -*- C++ -*-
 * Copyright 2003 Bas Wijnen <wijnen@debian.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "regexp.hh"
#include "iostring.hh"

namespace shevek
{
  void regexp::l_setup (std::string const &pattern, bool destroy)
  {
    startfunc;
    m_pattern = pattern;
    m_data.clear ();
    int flags = REG_EXTENDED;
    if (!m_case_sensitive)
      flags |= REG_ICASE;
    int r = regcomp (&m_buffer, pattern.c_str (), flags);
    if (r)
      {
	size_t s = regerror (r, &m_buffer, 0, 0);
	char *err = new char[s];
	std::string str_err (err, s - 1);
	delete[] err;
	if (!destroy)
	  {
	    // this will not fail, but if it does, it shouldn't loop.
	    l_setup ("", true);
	  }
	throw std::string (rostring ("error compiling regular expression: %s",
				str_err));
      }
    m_match = new regmatch_t[m_buffer.re_nsub + 1];
    m_size = m_buffer.re_nsub + 1;
  }

  regexp::regexp (std::string const &pattern, bool case_sensitive)
    : m_case_sensitive (case_sensitive)
  {
    startfunc;
    l_setup (pattern, true);
  }

  regexp &regexp::operator= (std::string const &pattern)
  {
    startfunc;
    regfree (&m_buffer);
    delete[] m_match;
    l_setup (pattern, false);
    return *this;
  }

  regexp::regexp (regexp const &that)
    : m_case_sensitive (that.m_case_sensitive)
  {
    startfunc;
    l_setup (that.m_pattern, true);
  }

  regexp &regexp::operator= (regexp const &that)
  {
    startfunc;
    regfree (&m_buffer);
    delete[] m_match;
    l_setup (that.m_pattern, false);
    return *this;
  }

  void regexp::case_sensitive (bool value)
  {
    startfunc;
    m_case_sensitive = value;
  }

  regexp::~regexp ()
  {
    startfunc;
    regfree (&m_buffer);
    delete[] m_match;
  }

  bool regexp::operator() (std::string const &data)
  {
    startfunc;
    m_data = data;
    // An empty pattern never matches
    // (this is an exception, because actually it always matches).
    if (m_pattern.empty () )
      return false;
    return regexec (&m_buffer, data.c_str (), m_size, m_match, 0) == 0;
  }

  std::string regexp::operator[] (unsigned idx) const
  {
    startfunc;
    if (!valid (idx) )
      throw "invalid index";
    return m_data.substr (m_match[idx].rm_so,
			  m_match[idx].rm_eo - m_match[idx].rm_so);
  }

  bool regexp::valid (unsigned idx) const
  {
    startfunc;
    if (idx >= m_size)
      throw "index out of range";
    return m_match[idx].rm_so != -1;
  }

  unsigned regexp::size () const
  {
    startfunc;
    return m_size;
  }

  std::string regexp::transform (std::string const &data) const
  {
    startfunc;
    std::string retval;
    std::string::size_type p, last = 0;
    while ( (p = data.find ('\\', last) ) != std::string::npos)
      {
	retval += data.substr (last, p - last);
	last = p + 1;
	if (data.size () == p + 1)
	  throw "unexpected \\ at end of transform data";
	if (data[p + 1] == '\\')
	  {
	    retval += '\\';
	    ++last;
	  }
	else
	  {
	    p = data.find_first_not_of ("0123456789", last);
	    std::string number;
	    if (p == std::string::npos)
	      {
		number = data.substr (last);
		last = data.size ();
	      }
	    else
	      {
		number = data.substr (last, p - last);
		if (data[p] == '\\')
		  last = p + 1;
		else
		  last = p;
	      }
	    unsigned idx = atoi (number.c_str () );
	    retval += (*this)[idx];
	  }
      }
    retval += data.substr (last);
    return retval;
  }

  std::string const &regexp::pattern () const
  {
    startfunc;
    return m_pattern;
  }
}
