OpenVPN 3 Core Library
Loading...
Searching...
No Matches
format.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12#ifndef OPENVPN_COMMON_FORMAT_H
13#define OPENVPN_COMMON_FORMAT_H
14
15#include <cstddef> // for std::nullptr_t
16#include <string>
17#include <sstream>
18#include <ostream>
19#include <type_traits>
20#include <utility>
21
24
25namespace openvpn {
26
27// Concatenate arguments into a string:
28// print(args...) -- concatenate
29// prints(args...) -- concatenate but delimit args with space
30// printd(char delim, args...) -- concatenate but delimit args with delim
31
32namespace print_detail {
33template <typename T>
34inline void print(std::ostream &os, char delim, const T &first)
35{
36 os << first;
37}
38
39template <typename T, typename... Args>
40inline void print(std::ostream &os, char delim, const T &first, Args... args)
41{
42 os << first;
43 if (delim)
44 os << delim;
45 print(os, delim, args...);
46}
47} // namespace print_detail
48
49template <typename... Args>
50inline std::string printd(char delim, Args... args)
51{
52 std::ostringstream os;
53 print_detail::print(os, delim, args...);
54 return os.str();
55}
56
57template <typename... Args>
58inline std::string print(Args... args)
59{
60 return printd(0, args...);
61}
62
63template <typename... Args>
64inline std::string prints(Args... args)
65{
66 return printd(' ', args...);
67}
68
69// String formatting similar to sprintf.
70// %s formats any argument regardless of type.
71// %r formats any argument regardless of type and single-quotes it.
72// %R formats any argument regardless of type and double-quotes it.
73// %% formats '%'
74// printfmt(<format_string>, args...)
75
76namespace print_formatted_detail {
77template <typename T>
78class Output
79{
80};
81
82template <>
83class Output<std::string>
84{
85 public:
86 Output(const size_t reserve)
87 {
88 if (reserve)
89 str_.reserve(reserve);
90 }
91
92 // numeric types
93 template <typename T,
94 typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
95 void append(T value)
96 {
97 str_ += openvpn::to_string(value);
98 }
99
100 // non-numeric types not specialized below
101 template <typename T,
102 typename std::enable_if<!std::is_arithmetic<T>::value, int>::type = 0>
103 void append(const T &value)
104 {
105 std::ostringstream os;
106 os << value;
107 str_ += os.str();
108 }
109
110 // specialization for std::string
111 void append(const std::string &value)
112 {
113 str_ += value;
114 }
115
116 // specialization for const char *
117 void append(const char *value)
118 {
119 if (value)
120 str_ += value;
121 }
122
123 // specialization for char *
124 void append(char *value)
125 {
126 if (value)
127 str_ += value;
128 }
129
130 // specialization for char
131 void append(const char c)
132 {
133 str_ += c;
134 }
135
136 // specialization for bool
137 void append(const bool value)
138 {
139 str_ += value ? "true" : "false";
140 }
141
142 // specialization for nullptr
143 void append(std::nullptr_t)
144 {
145 str_ += "nullptr";
146 }
147
148 std::string str()
149 {
150 return std::move(str_);
151 }
152
153 private:
154 std::string str_;
155};
156
157template <>
158class Output<std::ostringstream>
159{
160 public:
161 Output(const size_t reserve)
162 {
163 // fixme -- figure out how to reserve space in std::ostringstream
164 }
165
166 // general types
167 template <typename T>
168 void append(const T &value)
169 {
170 os_ << value;
171 }
172
173 // specialization for const char *
174 void append(const char *value)
175 {
176 if (value)
177 os_ << value;
178 }
179
180 // specialization for char *
181 void append(char *value)
182 {
183 if (value)
184 os_ << value;
185 }
186
187 // specialization for bool
188 void append(const bool value)
189 {
190 if (value)
191 os_ << "true";
192 else
193 os_ << "false";
194 }
195
196 // specialization for nullptr
197 void append(std::nullptr_t)
198 {
199 os_ << "nullptr";
200 }
201
202 std::string str()
203 {
204 return os_.str();
205 }
206
207 private:
208 std::ostringstream os_;
209};
210} // namespace print_formatted_detail
211
212template <typename OUTPUT>
214{
215 public:
216 PrintFormatted(const std::string &fmt_arg, const size_t reserve)
217 : fmt(fmt_arg),
218 fi(fmt.begin()),
219 out(reserve),
220 pct(false)
221 {
222 }
223
224 void process()
225 {
227 }
228
229 template <typename T>
230 void process(const T &last)
231 {
232 process_arg(last);
234 }
235
236 template <typename T, typename... Args>
237 void process(const T &first, Args... args)
238 {
239 process_arg(first);
240 process(args...);
241 }
242
243 std::string str()
244 {
245 return out.str();
246 }
247
248 private:
251
252 template <typename T>
253 bool process_arg(const T &arg)
254 {
255 while (fi != fmt.end())
256 {
257 const char c = *fi++;
258 if (pct)
259 {
260 pct = false;
261 const int quote = quote_delim(c);
262 if (quote >= 0)
263 {
264 if (quote)
265 out.append((char)quote);
266 out.append(arg);
267 if (quote)
268 out.append((char)quote);
269 return true;
270 }
271 else
272 out.append(c);
273 }
274 else
275 {
276 if (c == '%')
277 pct = true;
278 else
279 out.append(c);
280 }
281 }
282 return false;
283 }
284
286 {
287 // '?' printed for %s operators that don't match an argument
288 while (process_arg("?"))
289 ;
290 }
291
292 static int quote_delim(const char fmt)
293 {
294 switch (fmt)
295 {
296 case 's':
297 return 0;
298 case 'r':
299 return '\'';
300 case 'R':
301 return '\"';
302 default:
303 return -1;
304 }
305 }
306
307 const std::string &fmt;
308 std::string::const_iterator fi;
310 bool pct;
311};
312
313template <typename... Args>
314inline std::string printfmt(const std::string &fmt, Args... args)
315{
316#ifdef OPENVPN_PLATFORM_ANDROID
318#else
319 PrintFormatted<std::string> pf(fmt, 256);
320#endif
321 pf.process(args...);
322 return pf.str();
323}
324
325// log with formatting
326#define OPENVPN_FMT(...) OPENVPN_LOG_STRING(printfmt(__VA_ARGS__))
327
328// throw a formatted exception
329#define OPENVPN_THROW_FMT(EXC, ...) throw EXC(printfmt(__VA_ARGS__))
330
331} // namespace openvpn
332
333#endif
static int quote_delim(const char fmt)
Definition format.hpp:292
void process(const T &first, Args... args)
Definition format.hpp:237
PrintFormatted(const PrintFormatted &)=delete
std::string::const_iterator fi
Definition format.hpp:308
std::string str()
Definition format.hpp:243
PrintFormatted & operator=(const PrintFormatted &)=delete
void process(const T &last)
Definition format.hpp:230
const std::string & fmt
Definition format.hpp:307
PrintFormatted(const std::string &fmt_arg, const size_t reserve)
Definition format.hpp:216
bool process_arg(const T &arg)
Definition format.hpp:253
print_formatted_detail::Output< OUTPUT > out
Definition format.hpp:309
void print(std::ostream &os, char delim, const T &first)
Definition format.hpp:34
Support deferred server-side state creation when client connects.
Definition ovpncli.cpp:95
std::string print(Args... args)
Definition format.hpp:58
std::string to_string(T value)
Definition to_string.hpp:33
std::string prints(Args... args)
Definition format.hpp:64
std::string printd(char delim, Args... args)
Definition format.hpp:50
std::string printfmt(const std::string &fmt, Args... args)
Definition format.hpp:314