• 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

76.42
/src/md4cpp.cc
1
/**
2
 * Copyright (c) 2022, 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 "md4cpp.hh"
31

32
#include "base/is_utf8.hh"
33
#include "base/lnav_log.hh"
34
#include "emojis-json.h"
35
#include "pcrepp/pcre2pp.hh"
36
#include "xml-entities-json.h"
37
#include "yajlpp/yajlpp_def.hh"
38

39
namespace md4cpp {
40

41
static const typed_json_path_container<xml_entity_map>&
42
get_xml_entity_map_handlers()
30✔
43
{
44
    static const typed_json_path_container<xml_entity> xml_entity_handlers = {
45
        yajlpp::property_handler("characters").for_field(&xml_entity::xe_chars),
60✔
46
    };
120✔
47

48
    static const typed_json_path_container<xml_entity_map> retval = {
49
        yajlpp::pattern_property_handler("(?<var_name>\\&\\w+;?)")
×
50
            .with_synopsis("<name>")
30✔
51
            .with_path_provider<xml_entity_map>(
30✔
52
                [](struct xml_entity_map* xem,
×
53
                   std::vector<std::string>& paths_out) {
54
                    for (const auto& iter : xem->xem_entities) {
×
UNCOV
55
                        paths_out.emplace_back(iter.first);
×
56
                    }
UNCOV
57
                })
×
58
            .with_obj_provider<xml_entity, xml_entity_map>(
30✔
59
                [](const yajlpp_provider_context& ypc, xml_entity_map* xem) {
30✔
60
                    auto entity_name = ypc.get_substr(0);
401,580✔
61
                    return &xem->xem_entities[entity_name];
803,160✔
62
                })
401,580✔
63
            .with_children(xml_entity_handlers),
30✔
64
    };
120✔
65

66
    return retval;
30✔
67
}
120✔
68

69
static const typed_json_path_container<emoji_map>&
70
get_emoji_map_handlers()
33✔
71
{
72
    static const typed_json_path_container<emoji> emoji_handlers = {
73
        yajlpp::property_handler("emoji").for_field(&emoji::e_value),
66✔
74
        yajlpp::property_handler("shortname").for_field(&emoji::e_shortname),
66✔
75
    };
165✔
76

77
    static const typed_json_path_container<emoji_map> retval = {
78
        yajlpp::property_handler("emojis#")
66✔
79
            .for_field(&emoji_map::em_emojis)
33✔
80
            .with_children(emoji_handlers),
33✔
81
    };
132✔
82

83
    return retval;
33✔
84
}
165✔
85

86
static xml_entity_map
87
load_xml_entity_map()
30✔
88
{
89
    static const intern_string_t name
90
        = intern_string::lookup(xml_entities_json.get_name());
90✔
91
    auto sfp = xml_entities_json.to_string_fragment_producer();
30✔
92
    auto parse_res = get_xml_entity_map_handlers()
30✔
93
                         .parser_for(name)
60✔
94
                         .with_ignore_unused(true)
30✔
95
                         .of(*sfp);
30✔
96

97
    assert(parse_res.isOk());
98

99
    return parse_res.unwrap();
60✔
100
}
30✔
101

102
const xml_entity_map&
103
get_xml_entity_map()
30✔
104
{
105
    static const auto retval = load_xml_entity_map();
30✔
106

107
    return retval;
30✔
108
}
109

110
static emoji_map
111
load_emoji_map()
33✔
112
{
113
    static const intern_string_t name
114
        = intern_string::lookup(emojis_json.get_name());
99✔
115
    auto sfp = emojis_json.to_string_fragment_producer();
33✔
116
    auto parse_res
117
        = get_emoji_map_handlers().parser_for(name).with_ignore_unused(true).of(
66✔
118
            *sfp);
33✔
119

120
    assert(parse_res.isOk());
121

122
    auto retval = parse_res.unwrap();
33✔
123
    for (auto& em : retval.em_emojis) {
133,089✔
124
        retval.em_shortname2emoji.emplace(em.e_shortname, em);
133,056✔
125
    }
126

127
    return retval;
66✔
128
}
33✔
129

130
const emoji_map&
131
get_emoji_map()
116✔
132
{
133
    static const auto retval = load_emoji_map();
116✔
134

135
    return retval;
116✔
136
}
137

138
std::string
139
escape_html(string_fragment content)
×
140
{
141
    std::string retval;
×
142

143
    retval.reserve(content.length());
×
144
    for (const auto ch : content) {
×
145
        switch (ch) {
×
146
            case '"':
×
147
                retval.append("&quot;");
×
148
                break;
×
UNCOV
149
            case '\'':
×
UNCOV
150
                retval.append("&apos;");
×
UNCOV
151
                break;
×
152
            case '<':
×
153
                retval.append("&lt;");
×
UNCOV
154
                break;
×
UNCOV
155
            case '>':
×
UNCOV
156
                retval.append("&gt;");
×
UNCOV
157
                break;
×
UNCOV
158
            case '&':
×
UNCOV
159
                retval.append("&amp;");
×
UNCOV
160
                break;
×
UNCOV
161
            default:
×
UNCOV
162
                retval.push_back(ch);
×
UNCOV
163
                break;
×
164
        }
165
    }
166

UNCOV
167
    return retval;
×
UNCOV
168
}
×
169

170
struct parse_userdata {
171
    event_handler& pu_handler;
172
    std::string pu_error_msg;
173
};
174

175
void
176
event_handler::set_line_number_from(const char* text)
260✔
177
{
178
    if (this->eh_fragment.begin() <= text && text < this->eh_fragment.end()) {
260✔
179
        auto off = text - this->eh_fragment.begin();
164✔
180

181
        this->eh_tree->visit_overlapping(off, off + 1, [this](const auto& cintv) {
164✔
182
            this->eh_line_number = cintv.value;
180✔
183
        });
180✔
184
    }
185
}
260✔
186

187
event_handler::block
188
event_handler::build_block(MD_BLOCKTYPE type, void* detail)
5,644✔
189
{
190
    switch (type) {
5,644✔
191
        case MD_BLOCK_DOC:
182✔
192
            return block_doc{};
182✔
193
        case MD_BLOCK_QUOTE:
72✔
194
            return block_quote{};
72✔
195
        case MD_BLOCK_UL:
170✔
196
            return static_cast<MD_BLOCK_UL_DETAIL*>(detail);
170✔
197
        case MD_BLOCK_OL:
6✔
198
            return static_cast<MD_BLOCK_OL_DETAIL*>(detail);
6✔
199
        case MD_BLOCK_LI:
656✔
200
            return static_cast<MD_BLOCK_LI_DETAIL*>(detail);
656✔
UNCOV
201
        case MD_BLOCK_HR:
×
UNCOV
202
            return block_hr{};
×
203
        case MD_BLOCK_H:
392✔
204
            return static_cast<MD_BLOCK_H_DETAIL*>(detail);
392✔
205
        case MD_BLOCK_CODE: {
260✔
206
            auto retval = static_cast<MD_BLOCK_CODE_DETAIL*>(detail);
260✔
207

208
            this->set_line_number_from(retval->lang.text);
260✔
209
            return retval;
260✔
210
        }
211
        case MD_BLOCK_HTML:
62✔
212
            return block_html{};
62✔
213
        case MD_BLOCK_P:
1,130✔
214
            return block_p{};
1,130✔
215
        case MD_BLOCK_TABLE:
100✔
216
            return static_cast<MD_BLOCK_TABLE_DETAIL*>(detail);
100✔
217
        case MD_BLOCK_THEAD:
100✔
218
            return block_thead{};
100✔
219
        case MD_BLOCK_TBODY:
100✔
220
            return block_tbody{};
100✔
221
        case MD_BLOCK_TR:
786✔
222
            return block_tr{};
786✔
223
        case MD_BLOCK_TH:
208✔
224
            return block_th{};
208✔
225
        case MD_BLOCK_TD:
1,420✔
226
            return static_cast<MD_BLOCK_TD_DETAIL*>(detail);
1,420✔
227
    }
228

UNCOV
229
    return {};
×
230
}
231

232
event_handler::span
233
event_handler::build_span(MD_SPANTYPE type, void* detail)
1,602✔
234
{
235
    switch (type) {
1,602✔
236
        case MD_SPAN_EM:
8✔
237
            return span_em{};
8✔
238
        case MD_SPAN_STRONG:
168✔
239
            return span_strong{};
168✔
240
        case MD_SPAN_A:
484✔
241
            return static_cast<MD_SPAN_A_DETAIL*>(detail);
484✔
242
        case MD_SPAN_IMG:
84✔
243
            return static_cast<MD_SPAN_IMG_DETAIL*>(detail);
84✔
244
        case MD_SPAN_CODE:
816✔
245
            return span_code{};
816✔
246
        case MD_SPAN_DEL:
8✔
247
            return span_del{};
8✔
248
        case MD_SPAN_U:
34✔
249
            return span_u{};
34✔
UNCOV
250
        default:
×
UNCOV
251
            break;
×
252
    }
253

UNCOV
254
    return {};
×
255
}
256

257
static int
258
md4cpp_enter_block(MD_BLOCKTYPE type, void* detail, void* userdata)
2,822✔
259
{
260
    auto* pu = static_cast<parse_userdata*>(userdata);
2,822✔
261

262
    auto enter_res
263
        = pu->pu_handler.enter_block(pu->pu_handler.build_block(type, detail));
2,822✔
264
    if (enter_res.isErr()) {
2,822✔
UNCOV
265
        pu->pu_error_msg = enter_res.unwrapErr();
×
UNCOV
266
        return 1;
×
267
    }
268

269
    return 0;
2,822✔
270
}
2,822✔
271

272
static int
273
md4cpp_leave_block(MD_BLOCKTYPE type, void* detail, void* userdata)
2,822✔
274
{
275
    auto* pu = static_cast<parse_userdata*>(userdata);
2,822✔
276
    auto leave_res
277
        = pu->pu_handler.leave_block(pu->pu_handler.build_block(type, detail));
2,822✔
278
    if (leave_res.isErr()) {
2,822✔
UNCOV
279
        pu->pu_error_msg = leave_res.unwrapErr();
×
UNCOV
280
        return 1;
×
281
    }
282

283
    return 0;
2,822✔
284
}
2,822✔
285

286
static int
287
md4cpp_enter_span(MD_SPANTYPE type, void* detail, void* userdata)
801✔
288
{
289
    auto* pu = static_cast<parse_userdata*>(userdata);
801✔
290

291
    auto enter_res
292
        = pu->pu_handler.enter_span(pu->pu_handler.build_span(type, detail));
801✔
293
    if (enter_res.isErr()) {
801✔
UNCOV
294
        pu->pu_error_msg = enter_res.unwrapErr();
×
UNCOV
295
        return 1;
×
296
    }
297

298
    return 0;
801✔
299
}
801✔
300

301
static int
302
md4cpp_leave_span(MD_SPANTYPE type, void* detail, void* userdata)
801✔
303
{
304
    auto* pu = static_cast<parse_userdata*>(userdata);
801✔
305

306
    auto leave_res
307
        = pu->pu_handler.leave_span(pu->pu_handler.build_span(type, detail));
801✔
308
    if (leave_res.isErr()) {
801✔
UNCOV
309
        pu->pu_error_msg = leave_res.unwrapErr();
×
UNCOV
310
        return 1;
×
311
    }
312

313
    return 0;
801✔
314
}
801✔
315

316
static int
317
md4cpp_text(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata)
5,488✔
318
{
319
    auto* pu = static_cast<parse_userdata*>(userdata);
5,488✔
320

321
    auto text_res = pu->pu_handler.text(type, string_fragment(text, 0, size));
5,488✔
322
    if (text_res.isErr()) {
5,488✔
UNCOV
323
        pu->pu_error_msg = text_res.unwrapErr();
×
UNCOV
324
        return 1;
×
325
    }
326

327
    return 0;
5,488✔
328
}
5,488✔
329

330
namespace details {
331
Result<void, std::string>
332
parse(const string_fragment& sf, event_handler& eh)
91✔
333
{
334
    auto scan_res = is_utf8(sf);
91✔
335
    if (!scan_res.is_valid()) {
91✔
UNCOV
336
        return Err(
×
UNCOV
337
            fmt::format(FMT_STRING("file has invalid UTF-8 at offset {}: {}"),
×
338
                        scan_res.usr_valid_frag.sf_end,
UNCOV
339
                        scan_res.usr_message));
×
340
    }
341

342
    auto eols = std::vector<event_handler::line_type_t>{};
91✔
343
    int lineno = 1;
91✔
344

345
    auto rest_sf = sf;
91✔
346
    while (!rest_sf.empty()) {
3,995✔
347
        auto split_pair = rest_sf.split_when(string_fragment::tag1{'\n'});
3,904✔
348
        auto line_sf = split_pair.first;
3,904✔
349

350
        eols.emplace_back(line_sf.sf_begin, line_sf.sf_end, lineno++);
3,904✔
351
        rest_sf = split_pair.second;
3,904✔
352
    }
353

354
    MD_PARSER parser = {0};
91✔
355
    eh.eh_fragment = sf;
91✔
356
    eh.eh_tree = std::make_unique<event_handler::lines_tree_t>(std::move(eols));
91✔
357
    auto pu = parse_userdata{eh};
91✔
358

359
    parser.abi_version = 0;
91✔
360
    parser.flags
361
        = (MD_DIALECT_GITHUB | MD_FLAG_UNDERLINE | MD_FLAG_STRIKETHROUGH)
91✔
362
        & ~(MD_FLAG_PERMISSIVEAUTOLINKS);
363
    parser.enter_block = md4cpp_enter_block;
91✔
364
    parser.leave_block = md4cpp_leave_block;
91✔
365
    parser.enter_span = md4cpp_enter_span;
91✔
366
    parser.leave_span = md4cpp_leave_span;
91✔
367
    parser.text = md4cpp_text;
91✔
368

369
    auto rc = md_parse(sf.data(), sf.length(), &parser, &pu);
91✔
370

371
    if (rc == 0) {
91✔
372
        return Ok();
91✔
373
    }
374

UNCOV
375
    return Err(pu.pu_error_msg);
×
376
}
91✔
377
}  // namespace details
378

379
}  // namespace md4cpp
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