Aurore
A Switch-like interface for your app, on every screen
Loading...
Searching...
No Matches
text.hpp
Go to the documentation of this file.
1// text — the small string helpers the parsers share. The two trims differ on
2// purpose, and each says what it removes: Markdown keeps a line end that
3// matters to it, the other readers drop every kind of space. Header-only,
4// plain C++ (ASCII only: no locale).
5// Copyright (C) 2026 JigSawFr. GPLv3-or-later (see LICENSE.GPL).
6#pragma once
7
8#include <cstring>
9#include <string>
10#include <vector>
11
12namespace text
13{
14
17inline std::string trim_space(const std::string& s)
18{
19 const char* ws = " \t\n\v\f\r";
20 const size_t a = s.find_first_not_of(ws);
21 if (a == std::string::npos)
22 return "";
23 return s.substr(a, s.find_last_not_of(ws) - a + 1);
24}
25
27inline std::string trim_space_end(std::string s)
28{
29 const size_t last = s.find_last_not_of(" \t\n\v\f\r");
30 s.erase(last == std::string::npos ? 0 : last + 1);
31 return s;
32}
33
36inline std::string trim_blank(const std::string& s)
37{
38 const size_t a = s.find_first_not_of(" \t");
39 if (a == std::string::npos)
40 return "";
41 return s.substr(a, s.find_last_not_of(" \t") - a + 1);
42}
43
45inline std::string lower_ascii(std::string s)
46{
47 for (char& c : s)
48 if (c >= 'A' && c <= 'Z')
49 c = (char)(c - 'A' + 'a');
50 return s;
51}
52
55inline std::string capitalise_ascii(std::string s)
56{
57 if (!s.empty() && s[0] >= 'a' && s[0] <= 'z')
58 s[0] = (char)(s[0] - 'a' + 'A');
59 return s;
60}
61
62inline bool starts_with(const std::string& s, const char* prefix)
63{
64 return s.compare(0, std::strlen(prefix), prefix) == 0;
65}
66
67inline bool starts_with(const std::string& s, const std::string& prefix)
68{
69 return s.compare(0, prefix.size(), prefix) == 0;
70}
71
72inline bool ends_with(const std::string& s, const std::string& suffix)
73{
74 return s.size() >= suffix.size() && s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
75}
76
80inline std::vector<std::string> lines(const std::string& s)
81{
82 std::vector<std::string> out;
83 for (size_t start = 0; start <= s.size();)
84 {
85 size_t end = s.find('\n', start);
86 if (end == std::string::npos)
87 end = s.size();
88 out.push_back(s.substr(start, end - start));
89 if (!out.back().empty() && out.back().back() == '\r')
90 out.back().pop_back();
91 start = end + 1;
92 }
93 return out;
94}
95
98inline std::string short_url(const std::string& url)
99{
100 const size_t scheme = url.find("://");
101 return scheme == std::string::npos ? url : url.substr(scheme + 3);
102}
103
107inline std::string url_encode(const std::string& s)
108{
109 static const char HEX[] = "0123456789ABCDEF";
110 std::string out;
111 for (unsigned char c : s)
112 {
113 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~')
114 {
115 out += (char)c;
116 }
117 else
118 {
119 out += '%';
120 out += HEX[c >> 4];
121 out += HEX[c & 0xF];
122 }
123 }
124 return out;
125}
126
129inline std::string join(const std::vector<std::string>& parts, const std::string& sep)
130{
131 std::string out;
132 for (size_t i = 0; i < parts.size(); i++)
133 {
134 if (i)
135 out += sep;
136 out += parts[i];
137 }
138 return out;
139}
140
148{
149 public:
157
158 Step next(const std::string& t, std::string& inner)
159 {
160 if (block_ == Comment)
161 {
162 if (t.find("-->") != std::string::npos)
163 block_ = Plain;
164 return Skip;
165 }
166 if (block_ == Hide)
167 {
168 if (t == "<!-- /console -->")
169 block_ = Plain;
170 return Skip;
171 }
172 if (block_ == Only && t == "-->")
173 {
174 block_ = Plain;
175 return Break;
176 }
177 if (block_ == Plain && starts_with(t, "<!--"))
178 {
179 const bool closed = t.size() >= 7 && t.compare(t.size() - 3, 3, "-->") == 0;
180 if (t == "<!-- console:hide -->")
181 {
182 block_ = Hide;
183 }
184 else if (t == "<!-- console:only")
185 {
186 block_ = Only;
187 }
188 else if (closed && starts_with(t, "<!-- console:"))
189 {
190 inner = trim_space(t.substr(13, t.size() - 16));
191 return Directive;
192 }
193 else if (!closed)
194 {
195 block_ = Comment;
196 }
197 return Break;
198 }
199 return Show;
200 }
201
202 private:
203 enum
204 {
205 Plain,
206 Comment,
207 Hide,
208 Only
209 } block_
210 = Plain;
211};
212
213} // namespace text
Definition text.hpp:148
Step
Definition text.hpp:151
@ Directive
<!-- console: … -->: inner is its line
Definition text.hpp:155
@ Skip
inside a comment or a console:hide block
Definition text.hpp:153
@ Break
a comment or a block starts or a console:only ends: no line
Definition text.hpp:154
@ Show
an ordinary line (one inside console:only too)
Definition text.hpp:152
Step next(const std::string &t, std::string &inner)
Definition text.hpp:158
Definition text.hpp:13
bool ends_with(const std::string &s, const std::string &suffix)
Definition text.hpp:72
std::string join(const std::vector< std::string > &parts, const std::string &sep)
Definition text.hpp:129
std::string lower_ascii(std::string s)
A..Z as a..z, every other byte as it is.
Definition text.hpp:45
std::string trim_blank(const std::string &s)
Definition text.hpp:36
std::string capitalise_ascii(std::string s)
Definition text.hpp:55
std::vector< std::string > lines(const std::string &s)
Definition text.hpp:80
bool starts_with(const std::string &s, const char *prefix)
Definition text.hpp:62
std::string trim_space_end(std::string s)
s without the white space (as trim_space) at its end; its start as it is.
Definition text.hpp:27
std::string url_encode(const std::string &s)
Definition text.hpp:107
std::string trim_space(const std::string &s)
Definition text.hpp:17
std::string short_url(const std::string &url)
Definition text.hpp:98