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

tstack / lnav / 11872214087-1756

16 Nov 2024 06:12PM UTC coverage: 70.243% (+0.5%) from 69.712%
11872214087-1756

push

github

tstack
[build] disable regex101

46266 of 65866 relevant lines covered (70.24%)

467515.63 hits per line

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

72.57
/src/base/string_util.cc
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
#include <algorithm>
31
#include <iterator>
32
#include <regex>
33
#include <sstream>
34

35
#include "string_util.hh"
36

37
#include "config.h"
38
#include "is_utf8.hh"
39
#include "lnav_log.hh"
40

41
void
42
scrub_to_utf8(char* buffer, size_t length)
12✔
43
{
44
    while (true) {
45
        auto frag = string_fragment::from_bytes(buffer, length);
12✔
46
        auto scan_res = is_utf8(frag);
12✔
47

48
        if (scan_res.is_valid()) {
12✔
49
            break;
6✔
50
        }
51
        for (size_t lpc = 0; lpc < scan_res.usr_faulty_bytes; lpc++) {
12✔
52
            buffer[scan_res.usr_valid_frag.sf_end + lpc] = '?';
6✔
53
        }
54
    }
6✔
55
}
6✔
56

57
void
58
quote_content(auto_buffer& buf, const string_fragment& sf, char quote_char)
×
59
{
60
    for (char ch : sf) {
×
61
        if (ch == quote_char) {
×
62
            buf.push_back('\\').push_back(ch);
×
63
            continue;
×
64
        }
65
        switch (ch) {
×
66
            case '\\':
×
67
                buf.push_back('\\').push_back('\\');
×
68
                break;
×
69
            case '\n':
×
70
                buf.push_back('\\').push_back('n');
×
71
                break;
×
72
            case '\t':
×
73
                buf.push_back('\\').push_back('t');
×
74
                break;
×
75
            case '\r':
×
76
                buf.push_back('\\').push_back('r');
×
77
                break;
×
78
            case '\a':
×
79
                buf.push_back('\\').push_back('a');
×
80
                break;
×
81
            case '\b':
×
82
                buf.push_back('\\').push_back('b');
×
83
                break;
×
84
            default:
×
85
                buf.push_back(ch);
×
86
                break;
×
87
        }
88
    }
89
}
90

91
size_t
92
unquote_content(char* dst, const char* str, size_t len, char quote_char)
176✔
93
{
94
    size_t index = 0;
176✔
95

96
    for (size_t lpc = 0; lpc < len; lpc++, index++) {
7,433✔
97
        dst[index] = str[lpc];
7,257✔
98
        if (str[lpc] == quote_char) {
7,257✔
99
            lpc += 1;
62✔
100
        } else if (str[lpc] == '\\' && (lpc + 1) < len) {
7,195✔
101
            switch (str[lpc + 1]) {
111✔
102
                case 'n':
6✔
103
                    dst[index] = '\n';
6✔
104
                    break;
6✔
105
                case 'r':
×
106
                    dst[index] = '\r';
×
107
                    break;
×
108
                case 't':
×
109
                    dst[index] = '\t';
×
110
                    break;
×
111
                default:
105✔
112
                    dst[index] = str[lpc + 1];
105✔
113
                    break;
105✔
114
            }
115
            lpc += 1;
111✔
116
        }
117
    }
118
    dst[index] = '\0';
176✔
119

120
    return index;
176✔
121
}
122

123
size_t
124
unquote(char* dst, const char* str, size_t len)
173✔
125
{
126
    if (str[0] == 'r' || str[0] == 'u') {
173✔
127
        str += 1;
1✔
128
        len -= 1;
1✔
129
    }
130
    char quote_char = str[0];
173✔
131

132
    require(str[0] == '\'' || str[0] == '"');
173✔
133

134
    return unquote_content(dst, &str[1], len - 2, quote_char);
173✔
135
}
136

137
size_t
138
unquote_w3c(char* dst, const char* str, size_t len)
7✔
139
{
140
    size_t index = 0;
7✔
141

142
    require(str[0] == '\'' || str[0] == '"');
7✔
143

144
    for (size_t lpc = 1; lpc < (len - 1); lpc++, index++) {
514✔
145
        dst[index] = str[lpc];
507✔
146
        if (str[lpc] == '"') {
507✔
147
            lpc += 1;
×
148
        }
149
    }
150
    dst[index] = '\0';
7✔
151

152
    return index;
7✔
153
}
154

155
void
156
truncate_to(std::string& str, size_t max_char_len)
5,036✔
157
{
158
    static const std::string ELLIPSIS = "\u22ef";
5,036✔
159

160
    if (str.length() < max_char_len) {
5,036✔
161
        return;
4,917✔
162
    }
163

164
    auto str_char_len_res = utf8_string_length(str);
182✔
165

166
    if (str_char_len_res.isErr()) {
182✔
167
        // XXX
168
        return;
×
169
    }
170

171
    auto str_char_len = str_char_len_res.unwrap();
182✔
172
    if (str_char_len <= max_char_len) {
182✔
173
        return;
61✔
174
    }
175

176
    if (max_char_len < 3) {
121✔
177
        str = ELLIPSIS;
2✔
178
        return;
2✔
179
    }
180

181
    auto chars_to_remove = (str_char_len - max_char_len) + 1;
119✔
182
    auto midpoint = str_char_len / 2;
119✔
183
    auto chars_to_keep_at_front = midpoint - (chars_to_remove / 2);
119✔
184
    auto bytes_to_keep_at_front
185
        = utf8_char_to_byte_index(str, chars_to_keep_at_front);
119✔
186
    auto remove_up_to_bytes = utf8_char_to_byte_index(
238✔
187
        str, chars_to_keep_at_front + chars_to_remove);
119✔
188
    auto bytes_to_remove = remove_up_to_bytes - bytes_to_keep_at_front;
119✔
189
    str.erase(bytes_to_keep_at_front, bytes_to_remove);
119✔
190
    str.insert(bytes_to_keep_at_front, ELLIPSIS);
119✔
191
}
182✔
192

193
bool
194
is_url(const std::string& fn)
1,143✔
195
{
196
    static const auto url_re = std::regex("^(file|https?|ftps?|scp|sftp):.*");
1,143✔
197

198
    return std::regex_match(fn, url_re);
1,143✔
199
}
200

201
size_t
202
last_word_str(char* str, size_t len, size_t max_len)
2✔
203
{
204
    if (len < max_len) {
2✔
205
        return len;
×
206
    }
207

208
    size_t last_start = 0;
2✔
209

210
    for (size_t index = 0; index < len; index++) {
27✔
211
        switch (str[index]) {
25✔
212
            case '.':
2✔
213
            case '-':
214
            case '/':
215
            case ':':
216
                last_start = index + 1;
2✔
217
                break;
2✔
218
        }
219
    }
220

221
    if (last_start == 0) {
2✔
222
        return len;
1✔
223
    }
224

225
    memmove(&str[0], &str[last_start], len - last_start);
1✔
226
    return len - last_start;
1✔
227
}
228

229
size_t
230
abbreviate_str(char* str, size_t len, size_t max_len)
131✔
231
{
232
    size_t last_start = 1;
131✔
233

234
    if (len < max_len) {
131✔
235
        return len;
1✔
236
    }
237

238
    for (size_t index = 0; index < len; index++) {
1,522✔
239
        switch (str[index]) {
1,493✔
240
            case '.':
259✔
241
            case '-':
242
            case '/':
243
            case ':':
244
                memmove(&str[last_start], &str[index], len - index);
259✔
245
                len -= (index - last_start);
259✔
246
                index = last_start + 1;
259✔
247
                last_start = index + 1;
259✔
248

249
                if (len < max_len) {
259✔
250
                    return len;
101✔
251
                }
252
                break;
158✔
253
        }
254
    }
255

256
    return len;
29✔
257
}
258

259
void
260
split_ws(const std::string& str, std::vector<std::string>& toks_out)
645✔
261
{
262
    std::stringstream ss(str);
645✔
263
    std::string buf;
645✔
264

265
    while (ss >> buf) {
1,957✔
266
        toks_out.push_back(buf);
1,312✔
267
    }
268
}
645✔
269

270
std::string
271
repeat(const std::string& input, size_t num)
3,732✔
272
{
273
    std::ostringstream os;
3,732✔
274
    std::fill_n(std::ostream_iterator<std::string>(os), num, input);
3,732✔
275
    return os.str();
7,464✔
276
}
3,732✔
277

278
std::string
279
center_str(const std::string& subject, size_t width)
2✔
280
{
281
    std::string retval = subject;
2✔
282

283
    truncate_to(retval, width);
2✔
284

285
    auto retval_length = utf8_string_length(retval).unwrapOr(retval.length());
2✔
286
    auto total_fill = width - retval_length;
2✔
287
    auto before = total_fill / 2;
2✔
288
    auto after = total_fill - before;
2✔
289

290
    retval.insert(0, before, ' ');
2✔
291
    retval.append(after, ' ');
2✔
292

293
    return retval;
2✔
294
}
×
295

296
bool
297
is_blank(const std::string& str)
2,675✔
298
{
299
    return std::all_of(
2,675✔
300
        str.begin(), str.end(), [](const auto ch) { return isspace(ch); });
5,359✔
301
}
302

303
std::string
304
scrub_ws(const char* in, ssize_t len)
3,626✔
305
{
306
    static const std::string TAB_SYMBOL = "\u21e5";
3,626✔
307
    static const std::string LF_SYMBOL = "\u240a";
3,626✔
308
    static const std::string CR_SYMBOL = "\u240d";
3,626✔
309

310
    std::string retval;
3,626✔
311

312
    for (size_t lpc = 0; (len == -1 && in[lpc]) || (len >= 0 && lpc < len);
32,251✔
313
         lpc++)
314
    {
315
        auto ch = in[lpc];
28,625✔
316

317
        switch (ch) {
28,625✔
318
            case '\t':
×
319
                retval.append(TAB_SYMBOL);
×
320
                break;
×
321
            case '\n':
54✔
322
                retval.append(LF_SYMBOL);
54✔
323
                break;
54✔
324
            case '\r':
×
325
                retval.append(CR_SYMBOL);
×
326
                break;
×
327
            default:
28,571✔
328
                retval.append(1, ch);
28,571✔
329
                break;
28,571✔
330
        }
331
    }
332

333
    return retval;
3,626✔
334
}
×
335

336
static constexpr const char* const SUPERSCRIPT_NUMS[] = {
337
    "⁰",
338
    "¹",
339
    "²",
340
    "³",
341
    "⁴",
342
    "⁵",
343
    "⁶",
344
    "⁷",
345
    "⁸",
346
    "⁹",
347
};
348

349
std::string
350
to_superscript(const std::string& in)
257✔
351
{
352
    std::string retval;
257✔
353
    for (const auto ch : in) {
514✔
354
        if (isdigit(ch)) {
257✔
355
            auto index = ch - '0';
257✔
356

357
            retval.append(SUPERSCRIPT_NUMS[index]);
257✔
358
        } else {
359
            retval.push_back(ch);
×
360
        }
361
    }
362

363
    return retval;
257✔
364
}
×
365

366
namespace fmt {
367
auto
368
formatter<lnav::tainted_string>::format(const lnav::tainted_string& ts,
×
369
                                        format_context& ctx)
370
    -> decltype(ctx.out()) const
371
{
372
    auto esc_res = fmt::v10::detail::find_escape(&(*ts.ts_str.begin()),
×
373
                                                 &(*ts.ts_str.end()));
×
374
    if (esc_res.end == nullptr) {
×
375
        return formatter<string_view>::format(ts.ts_str, ctx);
×
376
    }
377

378
    return format_to(ctx.out(), FMT_STRING("{:?}"), ts.ts_str);
×
379
}
×
380
}  // namespace fmt
×
381

382
namespace lnav {
383
namespace pcre2pp {
384

385
static bool
386
is_meta(char ch)
387
{
388
    switch (ch) {
2,971,852✔
389
        case '\\':
390
        case '^':
2,971,852✔
391
        case '$':
6,120✔
392
        case '.':
393
        case '[':
394
        case ']':
395
        case '(':
396
        case ')':
397
        case '*':
398
        case '+':
399
        case '?':
400
        case '{':
401
        case '}':
402
            return true;
403
        default:
404
            return false;
6,120✔
405
    }
2,965,732✔
406
}
2,965,732✔
407

408
static std::optional<const char*>
409
char_escape_seq(char ch)
410
{
411
    switch (ch) {
2,965,732✔
412
        case '\t':
413
            return "\\t";
2,965,732✔
414
        case '\n':
×
415
            return "\\n";
×
416
    }
×
417

×
418
    return std::nullopt;
419
}
420

2,965,732✔
421
std::string
422
quote(string_fragment str)
423
{
424
    std::string retval;
359,402✔
425

426
    while (true) {
359,402✔
427
        auto cp_pair_opt = str.consume_codepoint();
428
        if (!cp_pair_opt) {
429
            break;
3,331,254✔
430
        }
3,331,254✔
431

359,402✔
432
        auto cp_pair = cp_pair_opt.value();
433
        if ((cp_pair.first & ~0xff) == 0) {
434
            if (is_meta(cp_pair.first)) {
2,971,852✔
435
                retval.push_back('\\');
2,971,852✔
436
            } else {
2,971,852✔
437
                auto esc_seq = char_escape_seq(cp_pair.first);
6,120✔
438
                if (esc_seq) {
439
                    retval.append(esc_seq.value());
2,965,732✔
440
                    str = cp_pair_opt->second;
2,965,732✔
441
                    continue;
×
442
                }
×
443
            }
×
444
        }
445
        ww898::utf::utf8::write(cp_pair.first,
446
                                [&retval](char ch) { retval.push_back(ch); });
447
        str = cp_pair_opt->second;
2,971,852✔
448
    }
2,971,852✔
449

2,971,852✔
450
    return retval;
2,971,852✔
451
}
452

359,402✔
453
}  // namespace pcre2pp
×
454
}  // namespace lnav
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