• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tstack / lnav / 17589970077-2502

09 Sep 2025 05:00PM UTC coverage: 65.196% (-5.0%) from 70.225%
17589970077-2502

push

github

tstack
[format] add fields for source file/line

Knowing the source file/line context in a log
message can help find log messages when using
log2src.

56 of 70 new or added lines in 2 files covered. (80.0%)

13954 existing lines in 210 files now uncovered.

45516 of 69814 relevant lines covered (65.2%)

404154.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.41
/src/base/string_util.hh
1
/**
2
 * Copyright (c) 2019, Timothy Stack
3
 *
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *
9
 * * Redistributions of source code must retain the above copyright notice, this
10
 * list of conditions and the following disclaimer.
11
 * * Redistributions in binary form must reproduce the above copyright notice,
12
 * this list of conditions and the following disclaimer in the documentation
13
 * and/or other materials provided with the distribution.
14
 * * Neither the name of Timothy Stack nor the names of its contributors
15
 * may be used to endorse or promote products derived from this software
16
 * without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29

30
#ifndef lnav_string_util_hh
31
#define lnav_string_util_hh
32

33
#include <string>
34
#include <vector>
35

36
#include <string.h>
37

38
#include "auto_mem.hh"
39
#include "intern_string.hh"
40
#include "strnatcmp.h"
41
#include "ww898/cp_utf8.hpp"
42

43
void scrub_to_utf8(char* buffer, size_t length);
44

45
inline bool
46
is_line_ending(char ch)
55,873✔
47
{
48
    return ch == '\r' || ch == '\n';
55,873✔
49
}
50

51
void quote_content(auto_buffer& buf,
52
                   const string_fragment& sf,
53
                   char quote_char);
54

55
size_t unquote_content(char* dst, const char* str, size_t len, char quote_char);
56

57
size_t unquote(char* dst, const char* str, size_t len);
58

59
size_t unquote_w3c(char* dst, const char* str, size_t len);
60

61
inline bool
62
startswith(const char* str, const char* prefix)
157,780✔
63
{
64
    return strncmp(str, prefix, strlen(prefix)) == 0;
157,780✔
65
}
66

67
inline bool
68
startswith(const std::string& str, const char* prefix)
18,284✔
69
{
70
    return startswith(str.c_str(), prefix);
18,284✔
71
}
72

73
inline bool
74
startswith(const std::string& str, const std::string& prefix)
683✔
75
{
76
    return startswith(str.c_str(), prefix.c_str());
683✔
77
}
78

79
inline bool
80
endswith(const char* str, const char* suffix)
897✔
81
{
82
    size_t len = strlen(str), suffix_len = strlen(suffix);
897✔
83

84
    if (suffix_len > len) {
897✔
85
        return false;
102✔
86
    }
87

88
    return strcmp(&str[len - suffix_len], suffix) == 0;
795✔
89
}
90

91
template<int N>
92
bool
93
endswith(const std::string& str, const char (&suffix)[N])
10,182✔
94
{
95
    if (N - 1 > str.length()) {
10,182✔
96
        return false;
534✔
97
    }
98

99
    return strcmp(&str[str.size() - (N - 1)], suffix) == 0;
9,648✔
100
}
101

102
void truncate_to(std::string& str, size_t max_char_len);
103

104
std::string scrub_ws(const char* in, ssize_t len = -1);
105
inline std::string
106
scrub_ws(const string_fragment& sf)
8✔
107
{
108
    return scrub_ws(sf.data(), sf.length());
8✔
109
}
110

111
inline std::string
112
trim(const std::string& str)
10,187✔
113
{
114
    std::string::size_type start, end;
115

116
    for (start = 0; start < str.size() && isspace(str[start]); start++)
18,377✔
117
        ;
118
    for (end = str.size(); end > 0 && isspace(str[end - 1]); end--)
18,651✔
119
        ;
120

121
    return str.substr(start, end - start);
10,187✔
122
}
123

124
inline const char*
125
ltrim(const char* str)
126
{
127
    while (isspace(*str)) {
128
        str += 1;
129
    }
130

131
    return str;
132
}
133

134
inline std::string
UNCOV
135
rtrim(const std::string& str)
×
136
{
137
    std::string::size_type end;
138

UNCOV
139
    for (end = str.size(); end > 0 && isspace(str[end - 1]); end--)
×
140
        ;
141

UNCOV
142
    return str.substr(0, end);
×
143
}
144

145
inline std::string
146
tolower(const char* str)
25,815✔
147
{
148
    std::string retval;
25,815✔
149

150
    for (int lpc = 0; str[lpc]; lpc++) {
819,109✔
151
        retval.push_back(::tolower(str[lpc]));
793,294✔
152
    }
153

154
    return retval;
25,815✔
UNCOV
155
}
×
156

157
inline std::string
158
tolower(const std::string& str)
25,815✔
159
{
160
    return tolower(str.c_str());
25,815✔
161
}
162

163
inline std::string
164
toupper(const char* str)
126,575✔
165
{
166
    std::string retval;
126,575✔
167

168
    for (int lpc = 0; str[lpc]; lpc++) {
927,763✔
169
        retval.push_back(::toupper(str[lpc]));
801,188✔
170
    }
171

172
    return retval;
126,575✔
UNCOV
173
}
×
174

175
inline std::string
176
toupper(const std::string& str)
49,100✔
177
{
178
    return toupper(str.c_str());
49,100✔
179
}
180

181
ssize_t utf8_char_to_byte_index(const std::string& str, ssize_t ch_index);
182

183
inline Result<size_t, const char*>
184
utf8_string_length(const char* str, ssize_t len = -1)
12,685✔
185
{
186
    size_t retval = 0;
12,685✔
187

188
    if (len == -1) {
12,685✔
UNCOV
189
        len = strlen(str);
×
190
    }
191

192
    for (ssize_t byte_index = 0; byte_index < len;) {
8,571,734✔
193
        auto ch_size
194
            = TRY(ww898::utf::utf8::char_size([str, len, byte_index]() {
17,118,100✔
195
                  return std::make_pair(str[byte_index], len - byte_index);
196
              }));
197
        byte_index += ch_size;
8,559,049✔
198
        retval += 1;
8,559,049✔
199
    }
200

201
    return Ok(retval);
12,684✔
202
}
203

204
inline Result<size_t, const char*>
205
utf8_string_length(const std::string& str)
8,772✔
206
{
207
    return utf8_string_length(str.c_str(), str.length());
8,772✔
208
}
209

210
bool is_blank(const std::string& str);
211

212
size_t abbreviate_str(char* str, size_t len, size_t max_len);
213

214
size_t last_word_str(char* str, size_t len, size_t max_len);
215

216
void split_ws(const std::string& str, std::vector<std::string>& toks_out);
217

218
std::string repeat(const std::string& input, size_t num);
219

220
std::string center_str(const std::string& subject, size_t width);
221

222
inline std::string
UNCOV
223
on_blank(const std::string& str, const std::string& def)
×
224
{
UNCOV
225
    if (is_blank(str)) {
×
UNCOV
226
        return def;
×
227
    }
228

UNCOV
229
    return str;
×
230
}
231

232
std::string to_superscript(const std::string& in);
233

234
template<typename T, std::enable_if_t<std::is_integral_v<T>, bool> = true>
235
std::string
236
to_superscript(T in)
283✔
237
{
238
    return to_superscript(fmt::to_string(in));
283✔
239
}
240

241
struct strnatless {
242
    bool operator()(const std::string& lhs, const std::string& rhs) const
153✔
243
    {
244
        return strnatcmp(lhs.size(), lhs.data(), rhs.size(), rhs.data()) < 0;
153✔
245
    }
246

247
    bool operator()(const string_fragment& lhs,
248
                    const string_fragment& rhs) const
249
    {
250
        return strnatcmp(lhs.length(), lhs.data(), rhs.length(), rhs.data())
251
            < 0;
252
    }
253
};
254

255
struct strnatcaseless {
UNCOV
256
    bool operator()(const std::string& lhs, const std::string& rhs) const
×
257
    {
UNCOV
258
        return strnatcasecmp(lhs.size(), lhs.data(), rhs.size(), rhs.data())
×
UNCOV
259
            < 0;
×
260
    }
261

262
    bool operator()(const string_fragment& lhs,
263
                    const string_fragment& rhs) const
264
    {
265
        return strnatcasecmp(lhs.length(), lhs.data(), rhs.length(), rhs.data())
266
            < 0;
267
    }
268
};
269

270
namespace lnav {
271
class tainted_string {
272
public:
273
    explicit tainted_string(std::string s) : ts_str(std::move(s)) {}
274

275
    bool operator==(const tainted_string& other) const
276
    {
277
        return this->ts_str == other.ts_str;
278
    }
279

280
    bool operator!=(const tainted_string& other) const
281
    {
282
        return this->ts_str != other.ts_str;
283
    }
284

285
    bool operator<(const tainted_string& other) const
286
    {
287
        return this->ts_str < other.ts_str;
288
    }
289

290
    bool empty() const { return this->ts_str.empty(); }
291

292
    size_t length() const { return this->ts_str.length(); }
293

294
    size_t size() const { return this->ts_str.size(); }
295

296
    friend fmt::formatter<lnav::tainted_string>;
297

298
private:
299
    const std::string ts_str;
300
};
301
}  // namespace lnav
302

303
namespace fmt {
304
template<>
305
struct formatter<lnav::tainted_string> : formatter<string_view> {
306
    auto format(const lnav::tainted_string& ts, format_context& ctx)
307
        -> decltype(ctx.out()) const;
308
};
309
}  // namespace fmt
310

311
namespace lnav::pcre2pp {
312

313
std::string quote(string_fragment sf);
314

315
}
316

317
enum class text_align_t {
318
    start,
319
    center,
320
    end,
321
};
322

323
inline string_fragment
324
to_string_fragment(const auto_buffer& buf)
325
{
326
    return string_fragment::from_bytes(buf.begin(), buf.size());
327
}
328

329
inline bool
330
operator==(const auto_buffer& buf, const string_fragment& sf)
3✔
331
{
332
    if ((int) buf.size() != sf.length()) {
3✔
UNCOV
333
        return false;
×
334
    }
335

336
    return memcmp(buf.begin(), sf.data(), sf.length()) == 0;
3✔
337
}
338

339
inline bool
340
operator==(const string_fragment& sf, const auto_buffer& buf)
341
{
342
    if ((int) buf.size() != sf.length()) {
343
        return false;
344
    }
345

346
    return memcmp(buf.begin(), sf.data(), sf.length()) == 0;
347
}
348

349
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc