0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 20:13:05 +02:00
openvpn3/openvpn/common/format.hpp
James Yonan b2769db93a In format.hpp, work around a Windows symbol conflict
by renaming template var OUT to OUTPUT.
2015-10-08 21:52:59 -06:00

351 lines
7.3 KiB
C++

// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2015 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_COMMON_FORMAT_H
#define OPENVPN_COMMON_FORMAT_H
#include <cstddef> // for std::nullptr_t
#include <string>
#include <sstream>
#include <ostream>
#include <type_traits>
#include <utility>
namespace openvpn {
// Convert an arbitrary argument to a string.
// numeric types
template <typename T,
typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
inline std::string to_string(T value)
{
return std::to_string(value);
}
// non-numeric types not specialized below
template <typename T,
typename std::enable_if<!std::is_arithmetic<T>::value, int>::type = 0>
inline std::string to_string(const T& value)
{
std::ostringstream os;
os << value;
return os.str();
}
// Concatenate arguments into a string:
// print(args...) -- concatenate
// prints(args...) -- concatenate but delimit args with space
// printd(char delim, args...) -- concatenate but delimit args with delim
namespace print_detail {
template<typename T>
inline void print(std::ostream& os, char delim, const T& first)
{
os << first;
}
template<typename T, typename... Args>
inline void print(std::ostream& os, char delim, const T& first, Args... args)
{
os << first;
if (delim)
os << delim;
print(os, delim, args...);
}
}
template<typename... Args>
inline std::string printd(char delim, Args... args)
{
std::ostringstream os;
print_detail::print(os, delim, args...);
return os.str();
}
template<typename... Args>
inline std::string print(Args... args)
{
return printd(0, args...);
}
template<typename... Args>
inline std::string prints(Args... args)
{
return printd(' ', args...);
}
// String formatting similar to sprintf.
// %s formats any argument regardless of type.
// %r formats any argument regardless of type and single-quotes it.
// %R formats any argument regardless of type and double-quotes it.
// %% formats '%'
// printfmt(<format_string>, args...)
namespace print_formatted_detail {
template<typename T>
class Output {};
template<>
class Output<std::string>
{
public:
Output(const size_t reserve)
{
if (reserve)
str_.reserve(reserve);
}
// numeric types
template <typename T,
typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
void append(T value)
{
str_ += std::to_string(value);
}
// non-numeric types not specialized below
template <typename T,
typename std::enable_if<!std::is_arithmetic<T>::value, int>::type = 0>
void append(const T& value)
{
std::ostringstream os;
os << value;
str_ += os.str();
}
// specialization for std::string
void append(const std::string& value)
{
str_ += value;
}
// specialization for const char *
void append(const char *value)
{
if (value)
str_ += value;
}
// specialization for char *
void append(char *value)
{
if (value)
str_ += value;
}
// specialization for char
void append(const char c)
{
str_ += c;
}
// specialization for bool
void append(const bool value)
{
str_ += value ? "true" : "false";
}
// specialization for nullptr
void append(std::nullptr_t)
{
str_ += "nullptr";
}
std::string str()
{
return std::move(str_);
}
private:
std::string str_;
};
template<>
class Output<std::ostringstream>
{
public:
Output(const size_t reserve)
{
// fixme -- figure out how to reserve space in std::ostringstream
}
// general types
template <typename T>
void append(const T& value)
{
os_ << value;
}
// specialization for const char *
void append(const char *value)
{
if (value)
os_ << value;
}
// specialization for char *
void append(char *value)
{
if (value)
os_ << value;
}
// specialization for bool
void append(const bool value)
{
if (value)
os_ << "true";
else
os_ << "false";
}
// specialization for nullptr
void append(std::nullptr_t)
{
os_ << "nullptr";
}
std::string str()
{
return os_.str();
}
private:
std::ostringstream os_;
};
}
template <typename OUTPUT>
class PrintFormatted
{
public:
PrintFormatted(const std::string& fmt_arg, const size_t reserve)
: fmt(fmt_arg),
fi(fmt.begin()),
out(reserve),
pct(false)
{
}
void process()
{
process_finish();
}
template<typename T>
void process(const T& last)
{
process_arg(last);
process_finish();
}
template<typename T, typename... Args>
void process(const T& first, Args... args)
{
process_arg(first);
process(args...);
}
std::string str()
{
return out.str();
}
private:
PrintFormatted(const PrintFormatted&) = delete;
PrintFormatted& operator=(const PrintFormatted&) = delete;
template<typename T>
bool process_arg(const T& arg)
{
while (fi != fmt.end())
{
const char c = *fi++;
if (pct)
{
pct = false;
const int quote = quote_delim(c);
if (quote >= 0)
{
if (quote)
out.append((char)quote);
out.append(arg);
if (quote)
out.append((char)quote);
return true;
}
else
out.append(c);
}
else
{
if (c == '%')
pct = true;
else
out.append(c);
}
}
return false;
}
void process_finish()
{
// '?' printed for %s operators that don't match an argument
while (process_arg("?"))
;
}
static int quote_delim(const char fmt)
{
switch (fmt)
{
case 's':
return 0;
case 'r':
return '\'';
case 'R':
return '\"';
default:
return -1;
}
}
const std::string& fmt;
std::string::const_iterator fi;
print_formatted_detail::Output<OUTPUT> out;
bool pct;
};
template<typename... Args>
inline std::string printfmt(const std::string& fmt, Args... args)
{
PrintFormatted<std::string> pf(fmt, 256);
pf.process(args...);
return pf.str();
}
# define OPENVPN_FMT(...) OPENVPN_LOG_STRING(printfmt(__VA_ARGS__))
} // namespace openvpn
#endif