OpenVPN 3 Core Library
Loading...
Searching...
No Matches
string.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// General purpose string-manipulation functions.
13
14#ifndef OPENVPN_COMMON_STRING_H
15#define OPENVPN_COMMON_STRING_H
16
17#include <string>
18#include <string_view>
19#include <vector>
20#include <cstring>
21#include <locale>
22#include <algorithm>
23#include <optional>
24
25#include <fmt/core.h>
26
29
30namespace openvpn::string {
31// case insensitive compare functions
32
33inline int strcasecmp(const char *s1, const char *s2)
34{
35#ifdef OPENVPN_PLATFORM_WIN
36 return ::_stricmp(s1, s2);
37#else
38 return ::strcasecmp(s1, s2);
39#endif
40}
41
42inline int strcasecmp(const std::string &s1, const char *s2)
43{
44 return strcasecmp(s1.c_str(), s2);
45}
46
47inline int strcasecmp(const char *s1, const std::string &s2)
48{
49 return strcasecmp(s1, s2.c_str());
50}
51
52inline int strcasecmp(const std::string &s1, const std::string &s2)
53{
54 return strcasecmp(s1.c_str(), s2.c_str());
55}
56
57// Like strncpy but makes sure dest is always null terminated
58inline void strncpynt(char *dest, const char *src, size_t maxlen)
59{
60 strncpy(dest, src, maxlen);
61 if (maxlen > 0)
62 dest[maxlen - 1] = 0;
63}
64
65// Copy string to dest, make sure dest is always null terminated,
66// and fill out trailing chars in dest with '\0' up to dest_size.
67inline void copy_fill(void *dest, const std::string &src, const size_t dest_size)
68{
69 if (dest_size > 0)
70 {
71 const size_t ncopy = std::min(dest_size - 1, src.length());
72 std::memcpy(dest, src.c_str(), ncopy);
73 std::memset(static_cast<unsigned char *>(dest) + ncopy, 0, dest_size - ncopy);
74 }
75}
76
77inline bool is_true(const std::string &str)
78{
79 return str == "1" || !strcasecmp(str.c_str(), "true");
80}
81
82// Return true if str == prefix or if str starts with prefix + delim
83template <typename STRING>
84inline bool starts_with_delim(const STRING &str, const std::string &prefix, const char delim)
85{
86 if (prefix.length() < str.length())
87 return str[prefix.length()] == delim && str.starts_with(prefix);
88 return prefix == str;
89}
90
91// return true if string ends with a newline
92template <typename STRING>
93inline bool ends_with_newline(const STRING &str)
94{
95 return str.ends_with('\n');
96}
97
98// return true if string ends with a CR or LF
99template <typename STRING>
100inline bool ends_with_crlf(const STRING &str)
101{
102 if (str.length())
103 {
104 const char c = str.back();
105 return c == '\n' || c == '\r';
106 }
107 return false;
108}
109
110// Prepend leading characters (c) to str to obtain a minimum string length (min_len).
111// Useful for adding leading zeros to numeric values or formatting tables.
112inline std::string add_leading(const std::string &str, const size_t min_len, const char c)
113{
114 if (min_len <= str.length())
115 return str;
116 size_t len = min_len - str.length();
117 std::string ret;
118 ret.reserve(min_len);
119 while (len--)
120 ret += c;
121 ret += str;
122 return ret;
123}
124
125// make sure that string ends with char c, if not append it
126inline std::string add_trailing_copy(const std::string &str, const char c)
127{
128 if (str.ends_with(c))
129 return str;
130 return str + c;
131}
132
133// make sure that string ends with char c, if not append it
134inline void add_trailing(std::string &str, const char c)
135{
136 if (!str.ends_with(c))
137 str += c;
138}
139
140// make sure that string ends with CRLF, if not append it
141inline void add_trailing_crlf(std::string &str)
142{
143 if (str.ends_with("\r\n"))
144 ;
145 else if (str.ends_with('\r'))
146 str += '\n';
147 else if (str.ends_with('\n'))
148 {
149 str.pop_back();
150 str += "\r\n";
151 }
152 else
153 str += "\r\n";
154}
155
156// make sure that string ends with CRLF, if not append it
157inline std::string add_trailing_crlf_copy(std::string str)
158{
160 return str;
161}
162
163// make sure that string ends with char c, if not append it (unless the string is empty)
164inline std::string add_trailing_unless_empty_copy(const std::string &str, const char c)
165{
166 if (str.empty() || str.ends_with(c))
167 return str;
168 return str + c;
169}
170
171// remove trailing \r or \n chars
172template <typename STRING>
173inline void trim_crlf(STRING &str)
174{
175 while (ends_with_crlf(str))
176 str.pop_back();
177}
178
179// remove trailing \r or \n chars
180inline std::string trim_crlf_copy(std::string str)
181{
182 trim_crlf(str);
183 return str;
184}
185
186// return true if str of size len contains an embedded null
187inline bool embedded_null(const char *str, size_t len)
188{
189 while (len--)
190 if (!*str++)
191 return true;
192 return false;
193}
194
195// return the length of a string, omitting trailing nulls
196inline size_t len_without_trailing_nulls(const char *str, size_t len)
197{
198 while (len > 0 && str[len - 1] == '\0')
199 --len;
200 return len;
201}
202
203// return true if string contains at least one newline
204inline bool is_multiline(const std::string &str)
205{
206 return str.find_first_of('\n') != std::string::npos;
207}
208
209// Return string up to a delimiter (without the delimiter).
210// Returns the entire string if no delimiter is found.
211inline std::string to_delim(const std::string &str, const char delim)
212{
213 const size_t pos = str.find_first_of(delim);
214 if (pos != std::string::npos)
215 return str.substr(0, pos);
216 return str;
217}
218
219// return the first line (without newline) of a multi-line string
220inline std::string first_line(const std::string &str)
221{
222 return to_delim(str, '\n');
223}
224
239inline bool is_space(const char c)
240{
241 return std::isspace(static_cast<unsigned char>(c)) != 0;
242}
243
244inline bool is_digit(const char c)
245{
246 return std::isdigit(static_cast<unsigned char>(c)) != 0;
247}
248
249inline bool is_alpha(const char c)
250{
251 return std::isalpha(static_cast<unsigned char>(c)) != 0;
252}
253
254inline bool is_alphanumeric(const char c)
255{
256 return std::isalnum(static_cast<unsigned char>(c)) != 0;
257}
258
259inline bool is_printable(const char c)
260{
261 return std::isprint(static_cast<unsigned char>(c)) != 0;
262}
263
264inline bool is_printable(const unsigned char c)
265{
266 return std::isprint(c) != 0;
267}
268
269inline bool is_ctrl(const char c)
270{
271 return std::iscntrl(static_cast<unsigned char>(c)) != 0;
272}
273
274inline bool is_ctrl(const unsigned char c)
275{
276 return std::iscntrl(c) != 0;
277}
278
279// return true if string conforms to regex \w*
280inline bool is_word(const std::string &str)
281{
282 for (auto &c : str)
283 if (!(is_alphanumeric(c) || c == '_'))
284 return false;
285 return true;
286}
287
288// return true if all string characters are printable (or if string is empty)
289inline bool is_printable(const std::string &str)
290{
291 for (auto &c : str)
292 if (!is_printable(c))
293 return false;
294 return true;
295}
296
297// return true if str contains at least one non-space control char
298inline bool contains_non_space_ctrl(const std::string &str)
299{
300 for (auto &c : str)
301 if ((!is_space(c) && is_ctrl(c)) || c == 127)
302 return true;
303 return false;
304}
305
306// return true if str contains at least one space char
307inline bool contains_space(const std::string &str)
308{
309 for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
310 if (is_space(*i))
311 return true;
312 return false;
313}
314
315// remove all spaces in string
316inline std::string remove_spaces(const std::string &str)
317{
318 std::string ret;
319 for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
320 {
321 char c = *i;
322 if (!is_space(c))
323 ret += c;
324 }
325 return ret;
326}
327
328// replace all spaces in string with rep
329inline std::string replace_spaces(const std::string &str, const char rep)
330{
331 std::string ret;
332 for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
333 {
334 char c = *i;
335 if (is_space(c))
336 c = rep;
337 ret += c;
338 }
339 return ret;
340}
341
342// replace all spaces in string with rep, reducing instances of multiple
343// consecutive spaces to a single instance of rep and removing leading
344// and trailing spaces
345inline std::string reduce_spaces(const std::string &str, const char rep)
346{
347 std::string ret;
348 bool last_space = true;
349 for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
350 {
351 char c = *i;
352 const bool space = is_space(c);
353 if (is_space(c))
354 c = rep;
355 if (!(space && last_space))
356 ret += c;
357 last_space = space;
358 }
359 if (last_space && !ret.empty())
360 ret.pop_back();
361 return ret;
362}
363
364// generate a string with n instances of char c
365inline std::string repeat(const char c, size_t n)
366{
367 std::string ret;
368 ret.reserve(n);
369 while (n-- > 0)
370 ret += c;
371 return ret;
372}
373
374// generate a string with spaces
375inline std::string spaces(size_t n)
376{
377 return repeat(' ', n);
378}
379
380// indent a multiline string
381inline std::string indent(const std::string &str, const int first, const int remaining)
382{
383 std::string ret;
384 int n_spaces = first;
385 for (auto &c : str)
386 {
387 if (n_spaces)
388 ret += spaces(n_spaces);
389 n_spaces = 0;
390 ret += c;
391 if (c == '\n')
392 n_spaces = remaining;
393 }
394 return ret;
395}
396
397// replace instances of char 'from' in string with char 'to'
398inline std::string replace_copy(const std::string &str, const char from, const char to)
399{
400 std::string ret;
401 ret.reserve(str.length());
402 for (auto &c : str)
403 ret.push_back(c == from ? to : c);
404 return ret;
405}
406
407// return true if str is empty or contains only space chars
408inline bool is_empty(const std::string &str)
409{
410 for (const auto &c : str)
411 if (!is_space(c))
412 return false;
413 return true;
414}
415
416// return true if str is empty or contains only space chars
417inline bool is_empty(const char *str)
418{
419 if (!str)
420 return true;
421 char c;
422 while ((c = *str++) != '\0')
423 if (!is_space(c))
424 return false;
425 return true;
426}
427
428// convert \n to \r\n
429inline std::string unix2dos(const std::string &str, const bool force_eol = false)
430{
431 std::string ret;
432 bool last_char_was_cr = false;
433
434 ret.reserve(str.length() + str.length() / 8);
435 for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
436 {
437 const char c = *i;
438 if (c == '\n' && !last_char_was_cr)
439 ret += '\r';
440 ret += c;
441 last_char_was_cr = (c == '\r');
442 }
443 if (force_eol)
445 return ret;
446}
447
448// Split a string on sep delimiter. The size of the
449// returned string vector will be at least 1 and at
450// most maxsplit + 1 (unless maxsplit is passed as -1).
451template <typename T>
452inline std::vector<T> split(const T &str,
453 const typename T::value_type sep,
454 const int maxsplit = -1)
455{
456 /* ensure we have a string as type */
457 static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, std::wstring>);
458 std::vector<T> ret;
459 int nterms = 0;
460 T term;
461
462 if (maxsplit >= 0)
463 ret.reserve(maxsplit + 1);
464
465 for (const auto c : str)
466 {
467 if (c == sep && (maxsplit < 0 || nterms < maxsplit))
468 {
469 ret.push_back(std::move(term));
470 ++nterms;
471 term.clear();
472 }
473 else
474 term += c;
475 }
476 ret.push_back(std::move(term));
477 return ret;
478}
479
480template <class T>
481inline auto join(const T &strings,
482 const typename T::value_type &delim,
483 const bool tail = false)
484{
485 /* ensure we have a container with strings as values */
486 static_assert(std::is_same_v<typename T::value_type, std::string> || std::is_same_v<typename T::value_type, std::wstring>);
487 typename T::value_type ret;
488 bool first = true;
489 for (const auto &s : strings)
490 {
491 if (!first)
492 ret += delim;
493 ret += s;
494 first = false;
495 }
496 if (tail && !ret.empty())
497 ret += delim;
498 return ret;
499}
500
501inline std::vector<std::string> from_argv(int argc, char *argv[], const bool skip_first)
502{
503 std::vector<std::string> ret;
504 for (int i = (skip_first ? 1 : 0); i < argc; ++i)
505 ret.emplace_back(argv[i]);
506 return ret;
507}
508
509inline std::string trim_left_copy(const std::string &str)
510{
511 for (size_t i = 0; i < str.length(); ++i)
512 {
513 if (!is_space(str[i]))
514 return str.substr(i);
515 }
516 return std::string();
517}
518
519inline std::string trim_copy(const std::string &str)
520{
521 for (size_t i = 0; i < str.length(); ++i)
522 {
523 if (!is_space(str[i]))
524 {
525 size_t last_nonspace = i;
526 for (size_t j = i + 1; j < str.length(); ++j)
527 {
528 if (!is_space(str[j]))
529 last_nonspace = j;
530 }
531 return str.substr(i, last_nonspace - i + 1);
532 }
533 }
534 return std::string();
535}
536
537inline std::string to_upper_copy(const std::string &str)
538{
539 std::string ret;
540 std::locale loc;
541 ret.reserve(str.length());
542 for (const auto &c : str)
543 ret.push_back(std::toupper(c, loc));
544 return ret;
545}
546
547inline std::string to_lower_copy(const std::string &str)
548{
549 std::string ret;
550 std::locale loc;
551 ret.reserve(str.length());
552 for (const auto &c : str)
553 ret.push_back(std::tolower(c, loc));
554 return ret;
555}
556
557inline void trim(std::string &str)
558{
559 str = trim_copy(str);
560}
561
562inline void trim_left(std::string &str)
563{
565}
566
567inline void to_lower(std::string &str)
568{
570}
571
572inline void to_upper(std::string &str)
573{
575}
576
577// Replace any subsequence of consecutive space chars containing
578// at least one newline with a single newline char. If string
579// is non-empty and doesn't include a trailing newline, add one.
580inline std::string remove_blanks(const std::string &str)
581{
582 std::string ret;
583 ret.reserve(str.length() + 1);
584
585 std::string spaces;
586 bool in_space = false;
587 bool has_nl = false;
588
589 for (auto &c : str)
590 {
591 const bool s = is_space(c);
592 reprocess:
593 if (in_space)
594 {
595 if (s)
596 {
597 if (c == '\n')
598 has_nl = true;
599 spaces += c;
600 }
601 else
602 {
603 if (has_nl)
604 ret += '\n';
605 else
606 ret += spaces;
607 in_space = false;
608 has_nl = false;
609 spaces.clear();
610 goto reprocess;
611 }
612 }
613 else
614 {
615 if (s)
616 {
617 in_space = true;
618 goto reprocess;
619 }
620 else
621 ret += c;
622 }
623 }
624 if (!ret.empty() && !ends_with_newline(ret))
625 ret += '\n';
626 return ret;
627}
628
629// copy str to the return value, removing all instances of
630// chars that match remove
631inline std::string remove_char(const std::string &str, const char remove)
632{
633 std::string ret;
634 ret.reserve(str.length());
635 for (const auto c : str)
636 {
637 if (c != remove)
638 ret.push_back(c);
639 }
640 return ret;
641}
642
658template <typename... ArgsT>
659inline auto args_to_string(std::string_view delim, ArgsT &&...args) -> std::string
660{
661 std::string result;
662 ((result += std::string(result.empty() ? "" : delim) + fmt::format("{}", args)), ...);
663 return result;
664}
665
680template <typename... ArgsT>
681inline auto format_safe(std::string format, ArgsT &&...args) noexcept -> std::optional<std::string>
682{
683 try
684 {
685 return fmt::vformat(format, fmt::make_format_args(args...));
686 }
687 catch (...)
688 {
689 return std::nullopt;
690 }
691}
692
693} // namespace openvpn::string
694
695#endif // OPENVPN_COMMON_STRING_H
void strncpynt(char *dest, const char *src, size_t maxlen)
Definition string.hpp:58
std::vector< std::string > from_argv(int argc, char *argv[], const bool skip_first)
Definition string.hpp:501
std::string replace_copy(const std::string &str, const char from, const char to)
Definition string.hpp:398
auto args_to_string(std::string_view delim, ArgsT &&...args) -> std::string
Convert variadic arguments to a string.
Definition string.hpp:659
std::string remove_blanks(const std::string &str)
Definition string.hpp:580
bool ends_with_crlf(const STRING &str)
Definition string.hpp:100
bool is_alpha(const char c)
Definition string.hpp:249
bool is_digit(const char c)
Definition string.hpp:244
int strcasecmp(const char *s1, const char *s2)
Definition string.hpp:33
bool contains_space(const std::string &str)
Definition string.hpp:307
size_t len_without_trailing_nulls(const char *str, size_t len)
Definition string.hpp:196
void add_trailing_crlf(std::string &str)
Definition string.hpp:141
std::string add_trailing_unless_empty_copy(const std::string &str, const char c)
Definition string.hpp:164
bool is_ctrl(const char c)
Definition string.hpp:269
bool is_multiline(const std::string &str)
Definition string.hpp:204
auto join(const T &strings, const typename T::value_type &delim, const bool tail=false)
Definition string.hpp:481
void to_lower(std::string &str)
Definition string.hpp:567
std::string remove_char(const std::string &str, const char remove)
Definition string.hpp:631
bool ends_with_newline(const STRING &str)
Definition string.hpp:93
void trim_crlf(STRING &str)
Definition string.hpp:173
bool is_true(const std::string &str)
Definition string.hpp:77
bool embedded_null(const char *str, size_t len)
Definition string.hpp:187
std::string indent(const std::string &str, const int first, const int remaining)
Definition string.hpp:381
std::string spaces(size_t n)
Definition string.hpp:375
std::vector< T > split(const T &str, const typename T::value_type sep, const int maxsplit=-1)
Definition string.hpp:452
void add_trailing(std::string &str, const char c)
Definition string.hpp:134
std::string trim_copy(const std::string &str)
Definition string.hpp:519
std::string add_trailing_copy(const std::string &str, const char c)
Definition string.hpp:126
bool is_space(const char c)
Definition string.hpp:239
std::string first_line(const std::string &str)
Definition string.hpp:220
auto format_safe(std::string format, ArgsT &&...args) noexcept -> std::optional< std::string >
Format a string with error handling.
Definition string.hpp:681
bool is_alphanumeric(const char c)
Definition string.hpp:254
bool contains_non_space_ctrl(const std::string &str)
Definition string.hpp:298
std::string unix2dos(const std::string &str, const bool force_eol=false)
Definition string.hpp:429
void trim_left(std::string &str)
Definition string.hpp:562
void to_upper(std::string &str)
Definition string.hpp:572
void copy_fill(void *dest, const std::string &src, const size_t dest_size)
Definition string.hpp:67
std::string repeat(const char c, size_t n)
Definition string.hpp:365
std::string to_delim(const std::string &str, const char delim)
Definition string.hpp:211
void trim(std::string &str)
Definition string.hpp:557
std::string add_trailing_crlf_copy(std::string str)
Definition string.hpp:157
std::string add_leading(const std::string &str, const size_t min_len, const char c)
Definition string.hpp:112
bool starts_with_delim(const STRING &str, const std::string &prefix, const char delim)
Definition string.hpp:84
std::string replace_spaces(const std::string &str, const char rep)
Definition string.hpp:329
bool is_printable(const char c)
Definition string.hpp:259
std::string to_lower_copy(const std::string &str)
Definition string.hpp:547
bool is_empty(const std::string &str)
Definition string.hpp:408
std::string reduce_spaces(const std::string &str, const char rep)
Definition string.hpp:345
std::string trim_crlf_copy(std::string str)
Definition string.hpp:180
std::string trim_left_copy(const std::string &str)
Definition string.hpp:509
bool is_word(const std::string &str)
Definition string.hpp:280
std::string to_upper_copy(const std::string &str)
Definition string.hpp:537
std::string remove_spaces(const std::string &str)
Definition string.hpp:316
os<< "Session Name: "<< tbc-> session_name<< '\n';os<< "Layer: "<< tbc-> layer str()<< '\n'
std::string ret