//
// Automated Testing Framework (atf)
//
// Copyright (c) 2007 The NetBSD Foundation, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

extern "C" {
#include <regex.h>
}

#include <cctype>
#include <cerrno>
#include <cstring>

#include "exceptions.hpp"
#include "text.hpp"

namespace impl = tools::text;
#define IMPL_NAME "tools::text"

char*
impl::duplicate(const char* str)
{
   char* copy = new char[std::strlen(str) + 1];
   std::strcpy(copy, str);
   return copy;
}

bool
impl::match(const std::string& str, const std::string& regex)
{
   bool found;

   // Special case: regcomp does not like empty regular expressions.
   if (regex.empty()) {
       found = str.empty();
   } else {
       ::regex_t preg;

       if (::regcomp(&preg, regex.c_str(), REG_EXTENDED) != 0)
           throw std::runtime_error("Invalid regular expression '" + regex +
                                    "'");

       const int res = ::regexec(&preg, str.c_str(), 0, NULL, 0);
       regfree(&preg);
       if (res != 0 && res != REG_NOMATCH)
           throw std::runtime_error("Invalid regular expression " + regex);

       found = res == 0;
   }

   return found;
}

std::string
impl::to_lower(const std::string& str)
{
   std::string lc;
   for (std::string::const_iterator iter = str.begin(); iter != str.end();
        iter++)
       lc += std::tolower(*iter);
   return lc;
}

std::vector< std::string >
impl::split(const std::string& str, const std::string& delim)
{
   std::vector< std::string > words;

   std::string::size_type pos = 0, newpos = 0;
   while (pos < str.length() && newpos != std::string::npos) {
       newpos = str.find(delim, pos);
       if (newpos != pos)
           words.push_back(str.substr(pos, newpos - pos));
       pos = newpos + delim.length();
   }

   return words;
}

std::string
impl::trim(const std::string& str)
{
   std::string::size_type pos1 = str.find_first_not_of(" \t");
   std::string::size_type pos2 = str.find_last_not_of(" \t");

   if (pos1 == std::string::npos && pos2 == std::string::npos)
       return "";
   else if (pos1 == std::string::npos)
       return str.substr(0, str.length() - pos2);
   else if (pos2 == std::string::npos)
       return str.substr(pos1);
   else
       return str.substr(pos1, pos2 - pos1 + 1);
}

bool
impl::to_bool(const std::string& str)
{
   const std::string lower = to_lower(str);
   if (lower == "yes" || lower == "true")
       return true;
   else if (lower == "no" || lower == "false")
       return false;
   else {
       // XXX Not really a libc error.
       throw system_error(IMPL_NAME "::to_bool", "Cannot convert string "
                          "'" + str + "' to boolean", EINVAL);
   }
}

int64_t
impl::to_bytes(std::string str)
{
   if (str.empty())
       throw std::runtime_error("Empty value");

   const char unit = str[str.length() - 1];
   int64_t multiplier;
   switch (unit) {
   case 'k': case 'K': multiplier = 1 << 10; break;
   case 'm': case 'M': multiplier = 1 << 20; break;
   case 'g': case 'G': multiplier = 1 << 30; break;
   case 't': case 'T': multiplier = int64_t(1) << 40; break;
   default:
       if (!std::isdigit(unit))
           throw std::runtime_error(std::string("Unknown size unit '") + unit
                                    + "'");
       multiplier = 1;
   }
   if (multiplier != 1)
       str.erase(str.length() - 1);

   return to_type< int64_t >(str) * multiplier;
}