]>
git.ipfire.org Git - thirdparty/ccache.git/blob - src/util/string.cpp
1 // Copyright (C) 2021 Joel Rosdahl and other contributors
3 // See doc/AUTHORS.adoc for a complete list of contributors.
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the Free
7 // Software Foundation; either version 3 of the License, or (at your option)
10 // This program is distributed in the hope that it will be useful, but WITHOUT
11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 // You should have received a copy of the GNU General Public License along with
16 // this program; if not, write to the Free Software Foundation, Inc., 51
17 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include <fmtmacros.hpp>
27 nonstd::expected
<int64_t, std::string
>
28 parse_signed(const std::string
& value
,
29 const nonstd::optional
<int64_t> min_value
,
30 const nonstd::optional
<int64_t> max_value
,
31 const nonstd::string_view description
)
33 const std::string stripped_value
= strip_whitespace(value
);
39 // Note: sizeof(long long) is guaranteed to be >= sizeof(int64_t)
40 result
= std::stoll(stripped_value
, &end
, 10);
41 } catch (std::exception
&) {
44 if (failed
|| end
!= stripped_value
.size()) {
45 return nonstd::make_unexpected(
46 FMT("invalid integer: \"{}\"", stripped_value
));
49 const int64_t min
= min_value
? *min_value
: INT64_MIN
;
50 const int64_t max
= max_value
? *max_value
: INT64_MAX
;
51 if (result
< min
|| result
> max
) {
52 return nonstd::make_unexpected(
53 FMT("{} must be between {} and {}", description
, min
, max
));
59 nonstd::expected
<mode_t
, std::string
>
60 parse_umask(const std::string
& value
)
62 return util::parse_unsigned(value
, 0, 0777, "umask", 8);
65 nonstd::expected
<uint64_t, std::string
>
66 parse_unsigned(const std::string
& value
,
67 const nonstd::optional
<uint64_t> min_value
,
68 const nonstd::optional
<uint64_t> max_value
,
69 const nonstd::string_view description
,
72 const std::string stripped_value
= strip_whitespace(value
);
75 unsigned long long result
= 0;
77 if (starts_with(stripped_value
, "-")) {
81 // Note: sizeof(unsigned long long) is guaranteed to be >=
83 result
= std::stoull(stripped_value
, &end
, base
);
84 } catch (std::exception
&) {
88 if (failed
|| end
!= stripped_value
.size()) {
89 const auto base_info
= base
== 8 ? "octal " : "";
90 return nonstd::make_unexpected(
91 FMT("invalid unsigned {}integer: \"{}\"", base_info
, stripped_value
));
94 const uint64_t min
= min_value
? *min_value
: 0;
95 const uint64_t max
= max_value
? *max_value
: UINT64_MAX
;
96 if (result
< min
|| result
> max
) {
97 return nonstd::make_unexpected(
98 FMT("{} must be between {} and {}", description
, min
, max
));
104 nonstd::expected
<std::string
, std::string
>
105 percent_decode(nonstd::string_view string
)
107 const auto from_hex
= [](const char digit
) {
108 return static_cast<uint8_t>(
109 std::isdigit(digit
) ? digit
- '0' : std::tolower(digit
) - 'a' + 10);
113 result
.reserve(string
.size());
114 for (size_t i
= 0; i
< string
.size(); ++i
) {
115 if (string
[i
] != '%') {
117 } else if (i
+ 2 >= string
.size() || !std::isxdigit(string
[i
+ 1])
118 || !std::isxdigit(string
[i
+ 2])) {
119 return nonstd::make_unexpected(
120 FMT("invalid percent-encoded string at position {}: {}", i
, string
));
122 const char ch
= static_cast<char>(from_hex(string
[i
+ 1]) << 4
123 | from_hex(string
[i
+ 2]));
132 std::pair
<nonstd::string_view
, nonstd::optional
<nonstd::string_view
>>
133 split_once(const nonstd::string_view string
, const char split_char
)
135 const size_t sep_pos
= string
.find(split_char
);
136 if (sep_pos
== nonstd::string_view::npos
) {
137 return std::make_pair(string
, nonstd::nullopt
);
139 return std::make_pair(string
.substr(0, sep_pos
),
140 string
.substr(sep_pos
+ 1));
145 strip_whitespace(const nonstd::string_view string
)
147 const auto is_space
= [](const int ch
) { return std::isspace(ch
); };
148 const auto start
= std::find_if_not(string
.begin(), string
.end(), is_space
);
150 std::find_if_not(string
.rbegin(), string
.rend(), is_space
).base();
151 return start
< end
? std::string(start
, end
) : std::string();