// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2016-2020 Red Hat, Inc.

/// @file
///
/// Some specialization for shared pointer utility templates.
///

#include "config.h"

#include <sstream>
#include <ostream>

#include "abg-internal.h"

// <headers defining libabigail's API go under here>
ABG_BEGIN_EXPORT_DECLARATIONS

#include "abg-regex.h"
#include "abg-sptr-utils.h"

ABG_END_EXPORT_DECLARATIONS
// </headers defining libabigail's API>

namespace abigail
{

/// Specialization of sptr_utils::build_sptr for regex_t.
///
/// This is used to wrap a pointer to regex_t into a
/// shared_ptr<regex_t>.
///
/// @param p the bare pointer to regex_t to wrap into a shared_ptr<regex_t>.
///
/// @return the shared_ptr<regex_t> that wraps @p p.
template<>
regex::regex_t_sptr
sptr_utils::build_sptr<regex_t>(regex_t *p)
{return regex::regex_t_sptr(p, regex::regex_t_deleter());}

/// Specialization of sptr_utils::build_sptr for regex_t.
///
/// This creates a pointer to regex_t and wraps it into a shared_ptr<regex_t>.
///
/// @return the shared_ptr<regex_t> wrapping the newly created regex_t*
template<>
regex::regex_t_sptr
sptr_utils::build_sptr<regex_t>()
{return sptr_utils::build_sptr(new regex_t);}

namespace regex
{

/// Escape regex special charaters in input string.
///
/// @param os the output stream being written to.
///
/// @param esc the regex_escape object holding a reference to the string
/// needing to be escaped.
///
/// @return the output stream.
std::ostream&
operator<<(std::ostream& os, const escape& esc)
{
  // ']' and '}' are only conditionally special, so could be removed.
  static const std::string specials = "^.[]$()|*+?{}\\";
  const std::string& str = esc.ref;
  for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
    {
      if (specials.find(*i) != std::string::npos)
	os << '\\';
      os << *i;
    }
  return os;
}

/// Generate a regex pattern equivalent to testing set membership.
///
/// A string will match the resulting pattern regex, if and only if it
/// was present in the vector.
///
/// @param strs a vector of strings
///
/// @return a regex pattern
std::string
generate_from_strings(const std::vector<std::string>& strs)
{
  if (strs.empty())
    // This cute-looking regex does not match any string.
    return "^_^";
  std::ostringstream os;
  std::vector<std::string>::const_iterator i = strs.begin();
  os << "^(" << escape(*i++);
  while (i != strs.end())
    os << "|" << escape(*i++);
  os << ")$";
  return os.str();
}

/// Compile a regex from a string.
///
/// The result is held in a shared pointer. This will be null if regex
/// compilation fails.
///
/// @param str the string representation of the regex.
///
/// @return shared pointer holder of a compiled regex object.
regex_t_sptr
compile(const std::string& str)
{
  regex_t_sptr r = sptr_utils::build_sptr(new regex_t);
  if (regcomp(r.get(), str.c_str(), REG_EXTENDED))
    r.reset();
  return r;
}

/// See if a string matches a regex.
///
/// @param r a shared pointer holder of a compiled regex object.
///
/// @param str a string.
///
/// @return whether there was a match.
bool
match(const regex_t_sptr& r, const std::string& str)
{
  return !regexec(r.get(), str.c_str(), 0, NULL, 0);
}

}//end namespace regex

}//end namespace abigail