• 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

92.3
/src/data_parser.cc
1
/**
2
 * Copyright (c) 2007-2012, 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 <stack>
31

32
#include "data_parser.hh"
33

34
#include "config.h"
35
#include "spookyhash/SpookyV2.h"
36

37
data_format data_parser::FORMAT_SEMI("semi", DT_COMMA, DT_SEMI);
38
data_format data_parser::FORMAT_COMMA("comma", DT_INVALID, DT_COMMA);
39
data_format data_parser::FORMAT_EMDASH("emdash", DT_INVALID, DT_EMDASH);
40
data_format data_parser::FORMAT_PLAIN("plain", DT_INVALID, DT_INVALID);
41

42
data_parser::data_parser(data_scanner* ds)
596✔
43
    : dp_errors("dp_errors", __FILE__, __LINE__),
596✔
44
      dp_pairs("dp_pairs", __FILE__, __LINE__), dp_msg_format(nullptr),
596✔
45
      dp_msg_format_begin(ds->get_init_offset()), dp_scanner(ds)
1,192✔
46
{
47
    if (TRACE_FILE != nullptr) {
596✔
48
        fprintf(TRACE_FILE, "input %s\n", ds->get_input().to_string().c_str());
68✔
49
    }
50
}
596✔
51

52
void
53
data_parser::pairup(data_parser::schema_id_t* schema,
724✔
54
                    data_parser::element_list_t& pairs_out,
55
                    data_parser::element_list_t& in_list,
56
                    int group_depth)
57
{
58
    element_list_t ELEMENT_LIST_T(el_stack), ELEMENT_LIST_T(free_row),
724✔
59
        ELEMENT_LIST_T(key_comps), ELEMENT_LIST_T(value),
724✔
60
        ELEMENT_LIST_T(prefix);
724✔
61
    SpookyHash context;
62

63
    require(in_list.el_format.df_name != nullptr);
724✔
64

65
    POINT_TRACE("pairup_start");
724✔
66

67
    FORMAT_TRACE(in_list);
724✔
68

69
    for (auto iter = in_list.begin(); iter != in_list.end(); ++iter) {
5,343✔
70
        if (iter->e_token == DNT_GROUP) {
4,619✔
71
            element_list_t ELEMENT_LIST_T(group_pairs);
128✔
72

73
            this->pairup(
256✔
74
                nullptr, group_pairs, *iter->e_sub_elements, group_depth + 1);
128✔
75
            if (!group_pairs.empty()) {
128✔
76
                iter->assign_elements(group_pairs);
123✔
77
            }
78
        }
128✔
79

80
        if (in_list.el_format.df_prefix_terminator != DT_INVALID) {
4,619✔
81
            if (iter->e_token == in_list.el_format.df_prefix_terminator) {
4✔
82
                in_list.el_format.df_prefix_terminator = DT_INVALID;
2✔
83
                in_list.el_format.df_separator = DT_COLON;
2✔
84
            } else {
85
                el_stack.PUSH_BACK(*iter);
2✔
86
            }
87
        } else if (iter->e_token == in_list.el_format.df_terminator) {
4,615✔
88
            this->end_of_value(
118✔
89
                el_stack, key_comps, value, in_list, group_depth, iter);
90

91
            key_comps.PUSH_BACK(*iter);
118✔
92
        } else if (iter->e_token == in_list.el_format.df_qualifier) {
4,497✔
93
            value.SPLICE(
22✔
94
                value.end(), key_comps, key_comps.begin(), key_comps.end());
95
            strip(value, element_is_space{});
22✔
96
            if (!value.empty()) {
22✔
97
                el_stack.PUSH_BACK(element(value, DNT_VALUE));
22✔
98
            }
99
            value.CLEAR();
22✔
100
        } else if (iter->e_token == in_list.el_format.df_separator
4,475✔
101
                   || iter->e_token == DNT_GROUP)
4,475✔
102
        {
103
            auto key_iter = key_comps.end();
436✔
104
            bool found = false, key_is_values = true, mixed_values = false;
436✔
105
            auto last_is_key = !key_comps.empty()
436✔
106
                && (key_comps.back().e_token == DT_WORD
610✔
107
                    || key_comps.back().e_token == DT_SYMBOL);
174✔
108
            element_list_t ELEMENT_LIST_T(mixed_queue),
436✔
109
                ELEMENT_LIST_T(mixed_tail);
436✔
110

111
            if (!key_comps.empty()) {
436✔
112
                do {
113
                    --key_iter;
616✔
114
                    if (key_iter->e_token == in_list.el_format.df_appender) {
616✔
115
                        ++key_iter;
×
UNCOV
116
                        value.SPLICE(value.end(),
×
117
                                     key_comps,
118
                                     key_comps.begin(),
119
                                     key_iter);
120
                        if (!key_comps.empty()) {
×
UNCOV
121
                            key_comps.POP_FRONT();
×
122
                        }
UNCOV
123
                        found = true;
×
124
                    } else if (key_iter->e_token
616✔
125
                               == in_list.el_format.df_terminator)
616✔
126
                    {
127
                        std::vector<element> key_copy;
67✔
128

129
                        value.SPLICE(value.end(),
67✔
130
                                     key_comps,
131
                                     key_comps.begin(),
132
                                     key_iter);
133
                        key_comps.POP_FRONT();
67✔
134
                        strip(key_comps, element_is_space{});
67✔
135
                        if (key_comps.empty()) {
67✔
UNCOV
136
                            key_iter = key_comps.end();
×
137
                        } else {
138
                            key_iter = key_comps.begin();
67✔
139
                        }
140
                        found = true;
67✔
141
                    }
67✔
142
                    if (!found && key_iter != key_comps.end()) {
616✔
143
                        switch (key_iter->e_token) {
549✔
144
                            case DT_WORD:
312✔
145
                            case DT_SYMBOL:
146
                                key_is_values = false;
312✔
147
                                break;
312✔
148
                            case DT_WHITE:
×
UNCOV
149
                                break;
×
150
                            case DT_ID:
224✔
151
                            case DT_ANCHOR:
152
                            case DT_QUOTED_STRING:
153
                            case DT_URL:
154
                            case DT_PATH:
155
                            case DT_MAC_ADDRESS:
156
                            case DT_DATE:
157
                            case DT_TIME:
158
                            case DT_DATE_TIME:
159
                            case DT_IPV4_ADDRESS:
160
                            case DT_IPV6_ADDRESS:
161
                            case DT_HEX_DUMP:
162
                            case DT_UUID:
163
                            case DT_CREDIT_CARD_NUMBER:
164
                            case DT_VERSION_NUMBER:
165
                            case DT_OCTAL_NUMBER:
166
                            case DT_PERCENTAGE:
167
                            case DT_NUMBER:
168
                            case DT_HEX_NUMBER:
169
                            case DT_EMAIL:
170
                            case DT_CONSTANT:
171
                            case DNT_MEASUREMENT: {
172
                                if (((in_list.el_format.df_terminator
448✔
173
                                          != DT_INVALID
174
                                      && !el_stack.empty())
29✔
175
                                     || (key_comps.size() == 1
199✔
176
                                         && mixed_queue.empty()))
29✔
177
                                    && key_iter->e_token == DT_ID)
448✔
178
                                {
179
                                    key_is_values = false;
8✔
180
                                } else if (in_list.el_format.df_terminator
432✔
181
                                               == DT_INVALID
182
                                           || el_stack.empty())
216✔
183
                                {
184
                                    element_list_t ELEMENT_LIST_T(mixed_key);
193✔
185
                                    element_list_t ELEMENT_LIST_T(mixed_value);
193✔
186

187
                                    mixed_values = true;
193✔
188
                                    auto value_iter = key_iter;
193✔
189
                                    if (last_is_key) {
193✔
190
                                        if (mixed_tail.empty()) {
106✔
191
                                            mixed_tail.SPLICE(
120✔
192
                                                mixed_tail.end(),
193
                                                key_comps,
194
                                                std::next(value_iter),
195
                                                key_comps.end());
196
                                        }
197
                                    } else {
198
                                        while (std::prev(key_comps.end())
87✔
199
                                               != value_iter)
87✔
200
                                        {
UNCOV
201
                                            key_comps.POP_BACK();
×
202
                                        }
203
                                    }
204
                                    key_iter = std::next(value_iter);
193✔
205
                                    mixed_value.SPLICE(mixed_value.end(),
193✔
206
                                                       key_comps,
207
                                                       value_iter,
208
                                                       key_iter);
209
                                    if (!el_stack.empty()
193✔
210
                                        && el_stack.back().e_token == DNT_KEY
128✔
211
                                        && key_comps.empty())
321✔
212
                                    {
213
                                        el_stack.PUSH_BACK(
33✔
214
                                            element(mixed_value, DNT_VALUE));
215
                                    } else {
216
                                        mixed_queue.PUSH_FRONT(
160✔
217
                                            element(mixed_value, DNT_VALUE));
218
                                        if (!key_comps.empty()) {
160✔
219
                                            if (key_comps.back().e_token
133✔
220
                                                == DT_WORD)
133✔
221
                                            {
222
                                                key_iter = std::prev(
50✔
223
                                                    key_comps.end());
224
                                                mixed_key.SPLICE(
50✔
225
                                                    mixed_key.end(),
226
                                                    key_comps,
227
                                                    key_iter,
228
                                                    key_comps.end());
229
                                                mixed_queue.PUSH_FRONT(element(
50✔
230
                                                    mixed_key, DNT_KEY));
231
                                            }
232
                                        }
233
                                    }
234
                                    while (!key_comps.empty()
372✔
235
                                           && !key_comps.back().is_value())
372✔
236
                                    {
237
                                        key_comps.POP_BACK();
179✔
238
                                    }
239
                                    key_iter = key_comps.end();
193✔
240
                                }
193✔
241
                                break;
224✔
242
                            }
243
                            default:
13✔
244
                                break;
13✔
245
                        }
246
                    }
247
                } while (key_iter != key_comps.begin() && !found);
616✔
248
            }
249
            if (!mixed_queue.empty()) {
436✔
250
                if (!el_stack.empty() && el_stack.back().e_token == DNT_KEY
103✔
251
                    && mixed_queue.front().e_token == DNT_KEY)
103✔
252
                {
UNCOV
253
                    el_stack.POP_BACK();
×
254
                }
255
                el_stack.SPLICE(el_stack.end(),
68✔
256
                                mixed_queue,
257
                                mixed_queue.begin(),
258
                                mixed_queue.end());
259
            }
260
            if (!mixed_tail.empty()) {
436✔
261
                key_comps.CLEAR();
60✔
262
                key_comps.SPLICE(key_comps.end(),
120✔
263
                                 mixed_tail,
264
                                 std::prev(mixed_tail.end()),
265
                                 mixed_tail.end());
266
            }
267
            if (!found && !mixed_values && !el_stack.empty()
369✔
268
                && !key_comps.empty())
805✔
269
            {
270
                element_list_t::iterator value_iter;
52✔
271

272
                if (el_stack.size() > 1
52✔
273
                    && in_list.el_format.df_appender != DT_INVALID
33✔
274
                    && in_list.el_format.df_terminator != DT_INVALID)
85✔
275
                {
276
                    /* If we're expecting a terminator and haven't found it */
277
                    /* then this is part of the value. */
278
                    continue;
1✔
279
                }
280

281
                value.SPLICE(
51✔
282
                    value.end(), key_comps, key_comps.begin(), key_comps.end());
283
                value_iter = value.end();
51✔
284
                std::advance(value_iter, -1);
285
                key_comps.SPLICE(
51✔
286
                    key_comps.begin(), value, value_iter, value.end());
287
                key_comps.resize(1);
51✔
288
            }
289

290
            strip(value, element_is_space{});
435✔
291
            value.remove_if(element_if(DT_COMMA));
435✔
292
            if (!value.empty()) {
435✔
293
                el_stack.PUSH_BACK(element(value, DNT_VALUE));
15✔
294
            }
295
            strip(key_comps, element_is_space{});
435✔
296
            if (!key_comps.empty()) {
435✔
297
                if (mixed_values) {
263✔
298
                    key_is_values = false;
60✔
299
                    while (key_comps.size() > 1) {
60✔
UNCOV
300
                        key_comps.POP_FRONT();
×
301
                    }
302
                }
303
                if (!key_comps.empty()) {
263✔
304
                    if (key_is_values) {
263✔
305
                        el_stack.PUSH_BACK(element(key_comps, DNT_VALUE));
21✔
306
                    } else {
307
                        el_stack.PUSH_BACK(element(key_comps, DNT_KEY, false));
242✔
308
                    }
309
                }
310
            }
311
            key_comps.CLEAR();
435✔
312
            value.CLEAR();
435✔
313

314
            if (iter->e_token == DNT_GROUP) {
435✔
315
                value.PUSH_BACK(*iter);
128✔
316
                el_stack.PUSH_BACK(element(value, DNT_VALUE));
128✔
317
                value.CLEAR();
128✔
318
            }
319
        } else if (iter->e_token != DT_WHITE && iter->e_token != DT_CSI
6,843✔
320
                   && iter->e_token != DT_LINE)
6,406✔
321
        {
322
            key_comps.PUSH_BACK(*iter);
2,272✔
323
        }
324

325
        POINT_TRACE("pairup_loop");
4,618✔
326
    }
327

328
    POINT_TRACE("pairup_eol");
724✔
329

330
    CONSUMED_TRACE(in_list);
724✔
331

332
    // Only perform the free-row logic at the top level, if we're in a group
333
    // assume it is a list.
334
    if (group_depth < 1 && el_stack.empty()) {
724✔
335
        free_row.SPLICE(
489✔
336
            free_row.begin(), key_comps, key_comps.begin(), key_comps.end());
337
    } else {
338
        this->end_of_value(
235✔
339
            el_stack, key_comps, value, in_list, group_depth, in_list.end());
340
    }
341

342
    POINT_TRACE("pairup_stack");
724✔
343

344
    context.Init(0, 0);
724✔
345
    while (!el_stack.empty()) {
1,403✔
346
        auto kv_iter = el_stack.begin();
679✔
347
        if (kv_iter->e_token == DNT_VALUE) {
679✔
348
            if (pairs_out.empty()) {
362✔
349
                free_row.PUSH_BACK(el_stack.front());
190✔
350
            } else {
351
                element_list_t ELEMENT_LIST_T(free_pair_subs);
172✔
352
                struct element blank;
172✔
353

354
                blank.e_capture.c_begin = blank.e_capture.c_end
172✔
355
                    = el_stack.front().e_capture.c_begin;
172✔
356
                blank.e_token = DNT_KEY;
172✔
357
                free_pair_subs.PUSH_BACK(blank);
172✔
358
                free_pair_subs.PUSH_BACK(el_stack.front());
172✔
359
                pairs_out.PUSH_BACK(element(free_pair_subs, DNT_PAIR));
172✔
360
            }
172✔
361
        }
362
        if (kv_iter->e_token != DNT_KEY) {
679✔
363
            el_stack.POP_FRONT();
364✔
364
            continue;
375✔
365
        }
366

367
        ++kv_iter;
315✔
368
        if (kv_iter == el_stack.end()) {
315✔
369
            el_stack.POP_FRONT();
11✔
370
            continue;
11✔
371
        }
372

373
        element_list_t ELEMENT_LIST_T(pair_subs);
304✔
374

375
        if (schema != nullptr) {
304✔
376
            size_t key_len;
377
            const char* key_val
378
                = this->get_element_string(el_stack.front(), key_len);
217✔
379
            context.Update(key_val, key_len);
217✔
380
        }
381

382
        while (!free_row.empty()) {
365✔
383
            element_list_t ELEMENT_LIST_T(free_pair_subs);
61✔
384
            struct element blank;
61✔
385

386
            blank.e_capture.c_begin = blank.e_capture.c_end
61✔
387
                = free_row.front().e_capture.c_begin;
61✔
388
            blank.e_token = DNT_KEY;
61✔
389
            free_pair_subs.PUSH_BACK(blank);
61✔
390
            free_pair_subs.PUSH_BACK(free_row.front());
61✔
391
            pairs_out.PUSH_BACK(element(free_pair_subs, DNT_PAIR));
61✔
392
            free_row.POP_FRONT();
61✔
393
        }
61✔
394

395
        bool has_value = false;
304✔
396

397
        if (kv_iter->e_token == DNT_VALUE) {
304✔
398
            ++kv_iter;
295✔
399
            has_value = true;
295✔
400
        }
401

402
        pair_subs.SPLICE(
304✔
403
            pair_subs.begin(), el_stack, el_stack.begin(), kv_iter);
404

405
        if (!has_value) {
304✔
406
            element_list_t ELEMENT_LIST_T(blank_value);
9✔
407
            struct element blank;
9✔
408

409
            blank.e_token = DT_QUOTED_STRING;
9✔
410
            blank.e_capture.c_begin = blank.e_capture.c_end
9✔
411
                = pair_subs.front().e_capture.c_end;
9✔
412
            if (blank.e_capture.c_begin >= 0
18✔
413
                && blank.e_capture.c_begin
18✔
414
                    < this->dp_scanner->get_input().sf_end)
9✔
415
            {
416
                switch (this->dp_scanner->to_string_fragment(blank.e_capture)
9✔
417
                            .front())
9✔
418
                {
419
                    case '=':
9✔
420
                    case ':':
421
                        blank.e_capture.c_begin += 1;
9✔
422
                        blank.e_capture.c_end += 1;
9✔
423
                        break;
9✔
424
                }
425
            }
426
            blank_value.PUSH_BACK(blank);
9✔
427
            pair_subs.PUSH_BACK(element(blank_value, DNT_VALUE));
9✔
428
        }
9✔
429

430
        pairs_out.PUSH_BACK(element(pair_subs, DNT_PAIR));
304✔
431
    }
304✔
432

433
    if (pairs_out.size() == 1) {
724✔
434
        element& pair = pairs_out.front();
40✔
435
        element& evalue = pair.e_sub_elements->back();
40✔
436

437
        if (evalue.e_token == DNT_VALUE && evalue.e_sub_elements != nullptr
40✔
438
            && evalue.e_sub_elements->size() > 1)
80✔
439
        {
440
            element_list_t::iterator next_sub;
1✔
441

442
            next_sub = pair.e_sub_elements->begin();
1✔
443
            ++next_sub;
1✔
444
            prefix.SPLICE(prefix.begin(),
1✔
445
                          *pair.e_sub_elements,
446
                          pair.e_sub_elements->begin(),
447
                          next_sub);
448
            free_row.CLEAR();
1✔
449
            free_row.SPLICE(free_row.begin(),
1✔
450
                            *evalue.e_sub_elements,
451
                            evalue.e_sub_elements->begin(),
452
                            evalue.e_sub_elements->end());
453
            pairs_out.CLEAR();
1✔
454
            context.Init(0, 0);
1✔
455
        }
456
    }
457

458
    if (group_depth >= 1 && pairs_out.empty() && !free_row.empty()) {
724✔
459
        pairs_out.SWAP(free_row);
85✔
460
    }
461

462
    if (pairs_out.empty() && !free_row.empty()) {
724✔
463
        while (!free_row.empty()) {
1,298✔
464
            switch (free_row.front().e_token) {
1,030✔
465
                case DNT_GROUP:
342✔
466
                case DNT_VALUE:
467
                case DT_EMAIL:
468
                case DT_CONSTANT:
469
                case DT_NUMBER:
470
                case DT_SYMBOL:
471
                case DT_ID:
472
                case DT_HEX_NUMBER:
473
                case DT_OCTAL_NUMBER:
474
                case DT_VERSION_NUMBER:
475
                case DT_QUOTED_STRING:
476
                case DT_IPV4_ADDRESS:
477
                case DT_IPV6_ADDRESS:
478
                case DT_MAC_ADDRESS:
479
                case DT_HEX_DUMP:
480
                case DT_XML_DECL_TAG:
481
                case DT_XML_OPEN_TAG:
482
                case DT_XML_CLOSE_TAG:
483
                case DT_XML_EMPTY_TAG:
484
                case DT_UUID:
485
                case DT_URL:
486
                case DT_ANCHOR:
487
                case DT_PATH:
488
                case DT_DATE:
489
                case DT_TIME:
490
                case DT_PERCENTAGE:
491
                case DNT_MEASUREMENT: {
492
                    element_list_t ELEMENT_LIST_T(pair_subs);
342✔
493
                    struct element blank;
342✔
494

495
                    blank.e_capture.c_begin = blank.e_capture.c_end
342✔
496
                        = free_row.front().e_capture.c_begin;
342✔
497
                    blank.e_token = DNT_KEY;
342✔
498
                    pair_subs.PUSH_BACK(blank);
342✔
499
                    pair_subs.PUSH_BACK(free_row.front());
342✔
500
                    pairs_out.PUSH_BACK(element(pair_subs, DNT_PAIR));
342✔
501

502
                    // Throw something into the hash so that the number of
503
                    // columns is significant.  I don't think we want to
504
                    // use the token ID since some columns values might vary
505
                    // between rows.
506
                    context.Update(" ", 1);
342✔
507
                } break;
342✔
508

509
                default: {
688✔
510
                    size_t key_len;
511
                    const char* key_val
512
                        = this->get_element_string(free_row.front(), key_len);
688✔
513

514
                    context.Update(key_val, key_len);
688✔
515
                    break;
688✔
516
                }
517
            }
518

519
            free_row.POP_FRONT();
1,030✔
520
        }
521
    }
522

523
    if (!prefix.empty()) {
724✔
524
        element_list_t ELEMENT_LIST_T(pair_subs);
1✔
525
        struct element blank;
1✔
526

527
        blank.e_capture.c_begin = blank.e_capture.c_end
1✔
528
            = prefix.front().e_capture.c_begin;
1✔
529
        blank.e_token = DNT_KEY;
1✔
530
        pair_subs.PUSH_BACK(blank);
1✔
531
        pair_subs.PUSH_BACK(prefix.front());
1✔
532
        pairs_out.PUSH_FRONT(element(pair_subs, DNT_PAIR));
1✔
533
    }
1✔
534

535
    if (schema != nullptr && this->dp_msg_format != nullptr) {
724✔
536
        for (auto& fiter : pairs_out) {
437✔
537
            *(this->dp_msg_format) += this->get_string_up_to_value(fiter);
287✔
538
            this->dp_msg_format->append("#");
287✔
539
        }
540
        if (this->dp_msg_format_begin < this->dp_scanner->get_input().sf_end) {
150✔
541
            auto last = this->dp_scanner->get_input();
40✔
542
            last.sf_begin = this->dp_msg_format_begin;
40✔
543

544
            switch (last.front()) {
40✔
545
                case '\'':
7✔
546
                case '"':
547
                    last.sf_begin += 1;
7✔
548
                    break;
7✔
549
            }
550
            *(this->dp_msg_format) += last.to_string();
40✔
551
        }
552
        context.Update(this->dp_msg_format->c_str(),
150✔
553
                       this->dp_msg_format->length());
150✔
554
    }
555

556
    if (schema != nullptr) {
724✔
557
        context.Final(schema->out(0), schema->out(1));
596✔
558
    }
559

560
    if (pairs_out.size() > 1000) {
724✔
561
        pairs_out.resize(1000);
×
562
    }
563
}
724✔
564

565
void
566
data_parser::discover_format()
596✔
567
{
568
    std::stack<discover_format_state> state_stack;
596✔
569
    this->dp_group_token.push_back(DT_INVALID);
596✔
570
    this->dp_group_stack.resize(1);
596✔
571

572
    state_stack.push(discover_format_state());
596✔
573
    while (true) {
574
        auto tok_res = this->dp_scanner->tokenize2();
5,350✔
575
        if (!tok_res) {
5,350✔
576
            break;
596✔
577
        }
578

579
        element elem;
4,754✔
580
        elem.e_token = tok_res->tr_token;
4,754✔
581
        elem.e_capture = tok_res->tr_inner_capture;
4,754✔
582

583
        require(elem.e_capture.c_begin >= 0);
4,754✔
584
        require(elem.e_capture.c_end >= 0);
4,754✔
585
        require(elem.e_capture.c_begin <= elem.e_capture.c_end);
4,754✔
586

587
        state_stack.top().update_for_element(elem);
4,754✔
588
        switch (elem.e_token) {
4,754✔
589
            case DT_LPAREN:
123✔
590
            case DT_LANGLE:
591
            case DT_LCURLY:
592
            case DT_LSQUARE:
593
                this->dp_group_token.push_back(elem.e_token);
123✔
594
                this->dp_group_stack.emplace_back("_anon_", __FILE__, __LINE__);
123✔
595
                state_stack.push(discover_format_state());
123✔
596
                break;
123✔
597

598
            case DT_EMPTY_CONTAINER: {
5✔
599
                auto& curr_group = this->dp_group_stack.back();
5✔
600
                auto empty_list = element_list_t("_anon_", __FILE__, __LINE__);
5✔
601
                discover_format_state dfs;
5✔
602

603
                dfs.finalize();
5✔
604

605
                empty_list.el_format = dfs.dfs_format;
5✔
606
                curr_group.PUSH_BACK(element());
5✔
607

608
                auto& empty = curr_group.back();
5✔
609
                empty.e_capture.c_begin = elem.e_capture.c_begin + 1;
5✔
610
                empty.e_capture.c_end = elem.e_capture.c_begin + 1;
5✔
611
                empty.e_token = DNT_GROUP;
5✔
612
                empty.assign_elements(empty_list);
5✔
613
                break;
5✔
614
            }
5✔
615

616
            case DT_RPAREN:
116✔
617
            case DT_RANGLE:
618
            case DT_RCURLY:
619
            case DT_RSQUARE:
620
                if (this->dp_group_token.back() == (elem.e_token - 1)) {
116✔
621
                    this->dp_group_token.pop_back();
113✔
622

623
                    auto riter = this->dp_group_stack.rbegin();
113✔
624
                    ++riter;
113✔
625
                    state_stack.top().finalize();
113✔
626
                    this->dp_group_stack.back().el_format
113✔
627
                        = state_stack.top().dfs_format;
113✔
628
                    state_stack.pop();
113✔
629
                    if (!this->dp_group_stack.back().empty()) {
113✔
630
                        (*riter).PUSH_BACK(
113✔
631
                            element(this->dp_group_stack.back(), DNT_GROUP));
632
                    } else {
633
                        (*riter).PUSH_BACK(element());
×
634
                        riter->back().e_capture.c_begin
×
635
                            = elem.e_capture.c_begin;
×
636
                        riter->back().e_capture.c_end = elem.e_capture.c_begin;
×
637
                        riter->back().e_token = DNT_GROUP;
×
638
                        riter->back().assign_elements(
×
639
                            this->dp_group_stack.back());
×
640
                    }
641
                    this->dp_group_stack.pop_back();
113✔
642
                } else {
643
                    this->dp_group_stack.back().PUSH_BACK(elem);
3✔
644
                }
645
                break;
116✔
646

647
            case DT_UNIT: {
22✔
648
                element_list_t measurement_list;
22✔
649

650
                measurement_list.SPLICE(
44✔
651
                    measurement_list.end(),
652
                    this->dp_group_stack.back(),
653
                    std::prev(this->dp_group_stack.back().end()),
654
                    this->dp_group_stack.back().end());
655
                measurement_list.PUSH_BACK(elem);
22✔
656
                this->dp_group_stack.back().PUSH_BACK(
22✔
657
                    element(measurement_list, DNT_MEASUREMENT));
658
                break;
22✔
659
            }
22✔
660

661
            default:
4,488✔
662
                this->dp_group_stack.back().PUSH_BACK(elem);
4,488✔
663
                break;
4,488✔
664
        }
665
    }
9,508✔
666

667
    while (this->dp_group_stack.size() > 1) {
606✔
668
        this->dp_group_token.pop_back();
10✔
669

670
        auto riter = this->dp_group_stack.rbegin();
10✔
671
        ++riter;
10✔
672
        if (!this->dp_group_stack.back().empty()) {
10✔
673
            state_stack.top().finalize();
10✔
674
            this->dp_group_stack.back().el_format
10✔
675
                = state_stack.top().dfs_format;
10✔
676
            state_stack.pop();
10✔
677
            (*riter).PUSH_BACK(element(this->dp_group_stack.back(), DNT_GROUP));
10✔
678
        }
679
        this->dp_group_stack.pop_back();
10✔
680
    }
681

682
    state_stack.top().finalize();
596✔
683
    this->dp_group_stack.back().el_format = state_stack.top().dfs_format;
596✔
684
}
596✔
685

686
void
687
data_parser::end_of_value(data_parser::element_list_t& el_stack,
353✔
688
                          data_parser::element_list_t& key_comps,
689
                          data_parser::element_list_t& value,
690
                          const data_parser::element_list_t& in_list,
691
                          int group_depth,
692
                          element_list_t::iterator iter)
693
{
694
    auto key_iter = key_comps.end();
353✔
695
    bool found = false, key_is_values = true, mixed_values = false;
353✔
696
    auto last_is_key = !key_comps.empty()
353✔
697
        && (key_comps.back().e_token == DT_WORD
554✔
698
            || key_comps.back().e_token == DT_SYMBOL);
201✔
699
    element_list_t ELEMENT_LIST_T(mixed_queue), ELEMENT_LIST_T(mixed_tail);
353✔
700

701
    if (!key_comps.empty()) {
353✔
702
        do {
703
            --key_iter;
457✔
704
            if (key_iter->e_token == in_list.el_format.df_appender) {
457✔
705
                ++key_iter;
×
706
                value.SPLICE(
×
707
                    value.end(), key_comps, key_comps.begin(), key_iter);
708
                if (!key_comps.empty()) {
×
709
                    key_comps.POP_FRONT();
×
710
                }
711
                found = true;
×
712
            } else if (key_iter->e_token == in_list.el_format.df_terminator) {
457✔
713
                value.SPLICE(
51✔
714
                    value.end(), key_comps, key_comps.begin(), key_iter);
715
                key_comps.POP_FRONT();
51✔
716
                strip(key_comps, element_is_space{});
51✔
717
                if (key_comps.empty()) {
51✔
718
                    key_iter = key_comps.end();
×
719
                } else {
720
                    key_iter = key_comps.begin();
51✔
721
                }
722
                found = true;
51✔
723
            }
724
            if (!found && key_iter != key_comps.end()) {
457✔
725
                switch (key_iter->e_token) {
406✔
726
                    case DT_WORD:
225✔
727
                    case DT_SYMBOL:
728
                        key_is_values = false;
225✔
729
                        break;
225✔
730
                    case DT_WHITE:
×
731
                        break;
×
732
                    case DT_ID:
167✔
733
                    case DT_QUOTED_STRING:
734
                    case DT_URL:
735
                    case DT_PATH:
736
                    case DT_ANCHOR:
737
                    case DT_MAC_ADDRESS:
738
                    case DT_DATE:
739
                    case DT_TIME:
740
                    case DT_DATE_TIME:
741
                    case DT_IPV4_ADDRESS:
742
                    case DT_IPV6_ADDRESS:
743
                    case DT_HEX_DUMP:
744
                    case DT_UUID:
745
                    case DT_CREDIT_CARD_NUMBER:
746
                    case DT_VERSION_NUMBER:
747
                    case DT_OCTAL_NUMBER:
748
                    case DT_PERCENTAGE:
749
                    case DT_NUMBER:
750
                    case DT_HEX_NUMBER:
751
                    case DT_EMAIL:
752
                    case DT_CONSTANT:
753
                    case DNT_MEASUREMENT: {
754
                        if (((in_list.el_format.df_terminator != DT_INVALID
334✔
755
                              && !el_stack.empty())
91✔
756
                             || (key_comps.size() == 1 && mixed_queue.empty()))
84✔
757
                            && key_iter->e_token == DT_ID)
334✔
758
                        {
759
                            key_is_values = false;
25✔
760
                        } else if (in_list.el_format.df_terminator == DT_INVALID
284✔
761
                                   || el_stack.empty())
142✔
762
                        {
763
                            element_list_t ELEMENT_LIST_T(mixed_key);
79✔
764
                            element_list_t ELEMENT_LIST_T(mixed_value);
79✔
765

766
                            mixed_values = true;
79✔
767
                            auto value_iter = key_iter;
79✔
768
                            if (last_is_key) {
79✔
769
                                if (mixed_tail.empty()) {
5✔
770
                                    mixed_tail.SPLICE(mixed_tail.end(),
8✔
771
                                                      key_comps,
772
                                                      std::next(value_iter),
773
                                                      key_comps.end());
774
                                }
775
                            } else {
776
                                while (std::prev(key_comps.end()) != value_iter)
148✔
777
                                {
UNCOV
778
                                    key_comps.POP_BACK();
×
779
                                }
780
                            }
781
                            key_iter = std::next(value_iter);
79✔
782
                            mixed_value.SPLICE(mixed_value.end(),
79✔
783
                                               key_comps,
784
                                               value_iter,
785
                                               key_iter);
786
                            if (!el_stack.empty()
79✔
787
                                && el_stack.back().e_token == DNT_KEY
44✔
788
                                && key_comps.empty())
123✔
789
                            {
790
                                el_stack.PUSH_BACK(
29✔
791
                                    element(mixed_value, DNT_VALUE));
792
                            } else {
793
                                mixed_queue.PUSH_FRONT(
50✔
794
                                    element(mixed_value, DNT_VALUE));
795
                                if (!key_comps.empty()) {
50✔
796
                                    if (key_comps.back().e_token == DT_WORD) {
21✔
797
                                        key_iter = std::prev(key_comps.end());
9✔
798
                                        mixed_key.SPLICE(mixed_key.end(),
9✔
799
                                                         key_comps,
800
                                                         key_iter,
801
                                                         key_comps.end());
802
                                        mixed_queue.PUSH_FRONT(
9✔
803
                                            element(mixed_key, DNT_KEY));
804
                                    }
805
                                }
806
                            }
807
                            while (!key_comps.empty()
109✔
808
                                   && !key_comps.back().is_value())
109✔
809
                            {
810
                                key_comps.POP_BACK();
30✔
811
                            }
812
                            key_iter = key_comps.end();
79✔
813
                        }
79✔
814
                        break;
167✔
815
                    }
816
                    default:
14✔
817
                        break;
14✔
818
                }
819
            }
820
        } while (key_iter != key_comps.begin() && !found);
457✔
821
    }
822
    if (!mixed_queue.empty()) {
353✔
823
        if (!el_stack.empty() && el_stack.back().e_token == DNT_KEY
61✔
824
            && mixed_queue.front().e_token == DNT_KEY)
61✔
825
        {
826
            el_stack.POP_BACK();
2✔
827
        }
828
        el_stack.SPLICE(el_stack.end(),
48✔
829
                        mixed_queue,
830
                        mixed_queue.begin(),
831
                        mixed_queue.end());
832
    }
833
    if (!mixed_tail.empty()) {
353✔
834
        key_comps.CLEAR();
4✔
835
        key_comps.SPLICE(key_comps.end(),
8✔
836
                         mixed_tail,
837
                         std::prev(mixed_tail.end()),
838
                         mixed_tail.end());
839
    }
840
    if (!mixed_values && !el_stack.empty() && !key_comps.empty()) {
353✔
841
        element_list_t::iterator value_iter;
149✔
842

843
        if (el_stack.size() > 1 && in_list.el_format.df_appender != DT_INVALID
267✔
844
            && in_list.el_format.df_terminator != DT_INVALID
33✔
845
            && iter->e_token == in_list.el_format.df_separator)
267✔
846
        {
847
            /* If we're expecting a terminator and haven't found it */
848
            /* then this is part of the value. */
849
            return;
×
850
        }
851

852
        value.SPLICE(
149✔
853
            value.end(), key_comps, key_comps.begin(), key_comps.end());
854

855
        if (value.size() == 2
149✔
856
            && (value.front().e_token == DT_WORD
25✔
857
                || value.front().e_token == DT_SYMBOL
23✔
858
                || value.front().e_token == DT_ID)
7✔
859
            && !el_stack.empty() && el_stack.back().e_token != DNT_KEY)
174✔
860
        {
861
            element_list_t ELEMENT_LIST_T(mixed_key);
16✔
862

863
            mixed_key.SPLICE(mixed_key.end(),
32✔
864
                             value,
865
                             value.begin(),
866
                             std::next(value.begin()));
867
            el_stack.PUSH_BACK(element(mixed_key, DNT_KEY, false));
16✔
868
        }
16✔
869
    }
870

871
    strip(value, element_is_space{});
353✔
872
    value.remove_if(element_if(DT_COMMA));
353✔
873
    if (!value.empty()) {
353✔
874
        el_stack.PUSH_BACK(element(value, DNT_VALUE));
149✔
875
    }
876
    strip(key_comps, element_is_space{});
353✔
877
    if (!key_comps.empty()) {
353✔
878
        if (mixed_values) {
50✔
879
            key_is_values = false;
4✔
880
            while (key_comps.size() > 1) {
4✔
UNCOV
881
                key_comps.POP_FRONT();
×
882
            }
883
        }
884
        if (!key_comps.empty()) {
50✔
885
            if (iter == in_list.end()
50✔
886
                || iter->e_token != in_list.el_format.df_separator)
50✔
887
            {
888
                key_is_values = true;
50✔
889
            }
890
            if (key_is_values) {
50✔
891
                el_stack.PUSH_BACK(element(key_comps, DNT_VALUE));
50✔
892
            } else {
UNCOV
893
                el_stack.PUSH_BACK(element(key_comps, DNT_KEY, false));
×
894
            }
895
        }
896
    }
897
    key_comps.CLEAR();
353✔
898
    value.CLEAR();
353✔
899
}
353✔
900

901
void
902
data_parser::parse()
596✔
903
{
904
    this->discover_format();
596✔
905

906
    this->pairup(
596✔
907
        &this->dp_schema_id, this->dp_pairs, this->dp_group_stack.front());
596✔
908
}
596✔
909

910
std::string
911
data_parser::get_element_string(const data_parser::element& elem) const
1,042✔
912
{
913
    return this->dp_scanner->to_string_fragment(elem.e_capture).to_string();
1,042✔
914
}
915

916
std::string
917
data_parser::get_string_up_to_value(const data_parser::element& elem)
287✔
918
{
919
    const element& val_elem
920
        = elem.e_token == DNT_PAIR ? elem.e_sub_elements->back() : elem;
287✔
921

922
    if (this->dp_msg_format_begin <= val_elem.e_capture.c_begin) {
287✔
923
        auto leading_and_key_cap = data_scanner::capture_t(
924
            this->dp_msg_format_begin, val_elem.e_capture.c_begin);
287✔
925
        auto leading_and_key_sf
926
            = this->dp_scanner->to_string_fragment(leading_and_key_cap);
287✔
927
        if (leading_and_key_cap.length() >= 2) {
287✔
928
            switch (leading_and_key_sf.back()) {
241✔
929
                case '\'':
15✔
930
                case '"':
931
                    leading_and_key_sf.pop_back();
15✔
932
                    switch (leading_and_key_sf.back()) {
15✔
933
                        case 'r':
1✔
934
                        case 'u':
935
                            leading_and_key_sf.pop_back();
1✔
936
                            break;
1✔
937
                    }
938
                    break;
15✔
939
            }
940
            switch (leading_and_key_sf.front()) {
241✔
941
                case '\'':
7✔
942
                case '"':
943
                    leading_and_key_sf.sf_begin += 1;
7✔
944
                    break;
7✔
945
            }
946
        }
947
        this->dp_msg_format_begin = val_elem.e_capture.c_end;
287✔
948
        return leading_and_key_sf.to_string();
287✔
949
    } else {
UNCOV
950
        this->dp_msg_format_begin = val_elem.e_capture.c_end;
×
951
    }
UNCOV
952
    return "";
×
953
}
954

955
const char*
956
data_parser::get_element_string(const data_parser::element& elem,
1,472✔
957
                                size_t& len_out)
958
{
959
    len_out = elem.e_capture.length();
1,472✔
960
    return this->dp_scanner->to_string_fragment(elem.e_capture).data();
1,472✔
961
}
962

963
void
964
data_parser::print(FILE* out, data_parser::element_list_t& el)
68✔
965
{
966
    fprintf(out,
68✔
967
            "             %s\n",
968
            this->dp_scanner->get_input().to_string().c_str());
136✔
969
    for (auto& iter : el) {
288✔
970
        iter.print(out, *this->dp_scanner);
220✔
971
    }
972
}
68✔
973

974
FILE* data_parser::TRACE_FILE;
975

976
data_format_state_t
977
dfs_prefix_next(data_format_state_t state, data_token_t next_token)
4,754✔
978
{
979
    data_format_state_t retval = state;
4,754✔
980

981
    switch (state) {
4,754✔
982
        case DFS_INIT:
2,496✔
983
            switch (next_token) {
2,496✔
984
                case DT_PATH:
2,162✔
985
                case DT_COLON:
986
                case DT_EQUALS:
987
                case DT_CONSTANT:
988
                case DT_EMAIL:
989
                case DT_WORD:
990
                case DT_SYMBOL:
991
                case DT_ID:
992
                case DT_OCTAL_NUMBER:
993
                case DT_HEX_NUMBER:
994
                case DT_NUMBER:
995
                case DT_WHITE:
996
                case DT_CSI:
997
                case DT_LSQUARE:
998
                case DT_RSQUARE:
999
                case DT_LANGLE:
1000
                case DT_RANGLE:
1001
                case DT_EMPTY_CONTAINER:
1002
                case DT_ANCHOR:
1003
                    break;
2,162✔
1004

1005
                default:
334✔
1006
                    retval = DFS_ERROR;
334✔
1007
                    break;
334✔
1008
            }
1009
            break;
2,496✔
1010

1011
        case DFS_EXPECTING_SEP:
2,258✔
1012
        case DFS_ERROR:
1013
            retval = DFS_ERROR;
2,258✔
1014
            break;
2,258✔
1015

1016
        default:
×
1017
            break;
×
1018
    }
1019

1020
    return retval;
4,754✔
1021
}
1022

1023
data_format_state_t
1024
dfs_semi_next(data_format_state_t state, data_token_t next_token)
4,754✔
1025
{
1026
    data_format_state_t retval = state;
4,754✔
1027

1028
    switch (state) {
4,754✔
1029
        case DFS_INIT:
521✔
1030
            switch (next_token) {
521✔
1031
                case DT_COMMA:
×
1032
                case DT_SEMI:
1033
                    retval = DFS_ERROR;
×
1034
                    break;
×
1035

1036
                default:
521✔
1037
                    retval = DFS_KEY;
521✔
1038
                    break;
521✔
1039
            }
1040
            break;
521✔
1041

1042
        case DFS_KEY:
2,787✔
1043
            switch (next_token) {
2,787✔
1044
                case DT_COLON:
147✔
1045
                case DT_EQUALS:
1046
                    retval = DFS_VALUE;
147✔
1047
                    break;
147✔
1048

1049
                case DT_SEMI:
1✔
1050
                    retval = DFS_ERROR;
1✔
1051
                    break;
1✔
1052

1053
                default:
2,639✔
1054
                    break;
2,639✔
1055
            }
1056
            break;
2,787✔
1057

1058
        case DFS_VALUE:
1,446✔
1059
            switch (next_token) {
1,446✔
1060
                case DT_SEMI:
26✔
1061
                    retval = DFS_INIT;
26✔
1062
                    break;
26✔
1063

1064
                default:
1,420✔
1065
                    break;
1,420✔
1066
            }
1067
            break;
1,446✔
1068

1069
        case DFS_EXPECTING_SEP:
×
1070
        case DFS_ERROR:
1071
            retval = DFS_ERROR;
×
1072
            break;
×
1073
    }
1074

1075
    return retval;
4,754✔
1076
}
1077

1078
data_format_state_t
1079
dfs_comma_next(data_format_state_t state, data_token_t next_token)
4,754✔
1080
{
1081
    data_format_state_t retval = state;
4,754✔
1082

1083
    switch (state) {
4,754✔
1084
        case DFS_INIT:
579✔
1085
            switch (next_token) {
579✔
1086
                case DT_COMMA:
×
1087
                    break;
×
1088

1089
                case DT_SEMI:
×
1090
                    retval = DFS_ERROR;
×
1091
                    break;
×
1092

1093
                default:
579✔
1094
                    retval = DFS_KEY;
579✔
1095
                    break;
579✔
1096
            }
1097
            break;
579✔
1098

1099
        case DFS_KEY:
1,100✔
1100
            switch (next_token) {
1,100✔
1101
                case DT_COLON:
112✔
1102
                case DT_EQUALS:
1103
                    retval = DFS_VALUE;
112✔
1104
                    break;
112✔
1105

1106
                case DT_COMMA:
26✔
1107
                    retval = DFS_INIT;
26✔
1108
                    break;
26✔
1109

1110
                case DT_WORD:
225✔
1111
                    retval = DFS_EXPECTING_SEP;
225✔
1112
                    break;
225✔
1113

1114
                case DT_SEMI:
8✔
1115
                    retval = DFS_ERROR;
8✔
1116
                    break;
8✔
1117

1118
                default:
729✔
1119
                    break;
729✔
1120
            }
1121
            break;
1,100✔
1122

1123
        case DFS_EXPECTING_SEP:
1,273✔
1124
            switch (next_token) {
1,273✔
1125
                case DT_COLON:
61✔
1126
                case DT_EQUALS:
1127
                case DT_LPAREN:
1128
                case DT_LCURLY:
1129
                case DT_LSQUARE:
1130
                case DT_LANGLE:
1131
                    retval = DFS_VALUE;
61✔
1132
                    break;
61✔
1133

1134
                case DT_EMPTY_CONTAINER:
×
1135
                    retval = DFS_INIT;
×
1136
                    break;
×
1137

1138
                case DT_COMMA:
23✔
1139
                case DT_SEMI:
1140
                    retval = DFS_ERROR;
23✔
1141
                    break;
23✔
1142

1143
                default:
1,189✔
1144
                    break;
1,189✔
1145
            }
1146
            break;
1,273✔
1147

1148
        case DFS_VALUE:
864✔
1149
            switch (next_token) {
864✔
1150
                case DT_COMMA:
35✔
1151
                    retval = DFS_INIT;
35✔
1152
                    break;
35✔
1153

1154
                case DT_COLON:
42✔
1155
                case DT_EQUALS:
1156
                    retval = DFS_ERROR;
42✔
1157
                    break;
42✔
1158

1159
                default:
787✔
1160
                    break;
787✔
1161
            }
1162
            break;
864✔
1163

1164
        case DFS_ERROR:
938✔
1165
            retval = DFS_ERROR;
938✔
1166
            break;
938✔
1167
    }
1168

1169
    return retval;
4,754✔
1170
}
1171

1172
data_parser::element::element()
5,344✔
1173
    : e_capture(-1, -1), e_token(DT_INVALID), e_sub_elements(nullptr)
5,344✔
1174
{
1175
}
5,344✔
1176

1177
data_parser::element::element(data_parser::element_list_t& subs,
2,008✔
1178
                              data_token_t token,
1179
                              bool assign_subs_elements)
2,008✔
1180
    : e_capture(subs.front().e_capture.c_begin, subs.back().e_capture.c_end),
2,008✔
1181
      e_token(token), e_sub_elements(nullptr)
2,008✔
1182
{
1183
    if (assign_subs_elements) {
2,008✔
1184
        this->assign_elements(subs);
1,750✔
1185
    }
1186
}
2,008✔
1187

1188
data_parser::element::element(const data_parser::element& other)
10,397✔
1189
{
1190
    /* require(other.e_sub_elements == nullptr); */
1191

1192
    this->e_capture = other.e_capture;
10,397✔
1193
    this->e_token = other.e_token;
10,397✔
1194
    this->e_sub_elements = nullptr;
10,397✔
1195
    if (other.e_sub_elements != nullptr) {
10,397✔
1196
        this->assign_elements(*other.e_sub_elements);
2,331✔
1197
    }
1198
}
10,397✔
1199

1200
data_parser::element::~element()
17,738✔
1201
{
1202
    delete this->e_sub_elements;
17,738✔
1203
    this->e_sub_elements = nullptr;
17,738✔
1204
}
17,738✔
1205

1206
data_parser::element&
UNCOV
1207
data_parser::element::operator=(const data_parser::element& other)
×
1208
{
UNCOV
1209
    this->e_capture = other.e_capture;
×
UNCOV
1210
    this->e_token = other.e_token;
×
1211
    this->e_sub_elements = nullptr;
×
UNCOV
1212
    if (other.e_sub_elements != nullptr) {
×
1213
        this->assign_elements(*other.e_sub_elements);
×
1214
    }
1215
    return *this;
×
1216
}
1217

1218
void
1219
data_parser::element::assign_elements(data_parser::element_list_t& subs)
4,209✔
1220
{
1221
    if (this->e_sub_elements == nullptr) {
4,209✔
1222
        this->e_sub_elements = new element_list_t("_sub_", __FILE__, __LINE__);
4,086✔
1223
        this->e_sub_elements->el_format = subs.el_format;
4,086✔
1224
    }
1225
    this->e_sub_elements->SWAP(subs);
4,209✔
1226
    this->update_capture();
4,209✔
1227
}
4,209✔
1228

1229
void
1230
data_parser::element::update_capture()
4,209✔
1231
{
1232
    if (this->e_sub_elements != nullptr && !this->e_sub_elements->empty()) {
4,209✔
1233
        this->e_capture.c_begin
1234
            = this->e_sub_elements->front().e_capture.c_begin;
4,199✔
1235
        this->e_capture.c_end = this->e_sub_elements->back().e_capture.c_end;
4,199✔
1236
    }
1237
}
4,209✔
1238

1239
const data_parser::element&
1240
data_parser::element::get_pair_value() const
800✔
1241
{
1242
    require(this->e_token == DNT_PAIR);
800✔
1243

1244
    return this->e_sub_elements->back();
800✔
1245
}
1246

1247
data_token_t
1248
data_parser::element::value_token() const
1,578✔
1249
{
1250
    data_token_t retval = DT_INVALID;
1,578✔
1251

1252
    if (this->e_token == DNT_VALUE) {
1,578✔
1253
        if (this->e_sub_elements != nullptr
2,010✔
1254
            && this->e_sub_elements->size() == 1)
1,005✔
1255
        {
1256
            retval = this->e_sub_elements->front().e_token;
954✔
1257
        } else {
1258
            retval = DT_SYMBOL;
51✔
1259
        }
1260
    } else {
1261
        retval = this->e_token;
573✔
1262
    }
1263
    return retval;
1,578✔
1264
}
1265

1266
const data_parser::element&
1267
data_parser::element::get_value_elem() const
97✔
1268
{
1269
    if (this->e_token == DNT_VALUE) {
97✔
1270
        if (this->e_sub_elements != nullptr
194✔
1271
            && this->e_sub_elements->size() == 1)
97✔
1272
        {
1273
            return this->e_sub_elements->front();
97✔
1274
        }
1275
    }
UNCOV
1276
    return *this;
×
1277
}
1278

1279
const data_parser::element&
1280
data_parser::element::get_pair_elem() const
80✔
1281
{
1282
    if (this->e_token == DNT_VALUE) {
80✔
UNCOV
1283
        return this->e_sub_elements->front();
×
1284
    }
1285
    return *this;
80✔
1286
}
1287

1288
void
1289
data_parser::element::print(FILE* out, data_scanner& ds, int offset) const
1,175✔
1290
{
1291
    int lpc;
1292

1293
    if (this->e_sub_elements != nullptr) {
1,175✔
1294
        for (auto& e_sub_element : *this->e_sub_elements) {
1,578✔
1295
            e_sub_element.print(out, ds, offset + 1);
955✔
1296
        }
1297
    }
1298

1299
    fprintf(out,
1,175✔
1300
            "%4s %3d:%-3d ",
1301
            data_scanner::token2name(this->e_token),
1,175✔
1302
            this->e_capture.c_begin,
1,175✔
1303
            this->e_capture.c_end);
1,175✔
1304
    for (lpc = 0; lpc < this->e_capture.c_end; lpc++) {
130,723✔
1305
        if (lpc == this->e_capture.c_begin) {
129,548✔
1306
            fputc('^', out);
1,057✔
1307
        } else if (lpc == (this->e_capture.c_end - 1)) {
128,491✔
1308
            fputc('^', out);
1,061✔
1309
        } else if (lpc > this->e_capture.c_begin) {
127,430✔
1310
            fputc('-', out);
11,279✔
1311
        } else {
1312
            fputc(' ', out);
116,151✔
1313
        }
1314
    }
1315
    for (; lpc < (int) ds.get_input().length(); lpc++) {
58,498✔
1316
        fputc(' ', out);
57,323✔
1317
    }
1318

1319
    std::string sub = ds.to_string_fragment(this->e_capture).to_string();
1,175✔
1320
    fprintf(out, "  %s\n", sub.c_str());
1,175✔
1321
}
1,175✔
1322

1323
bool
1324
data_parser::element::is_value() const
307✔
1325
{
1326
    switch (this->e_token) {
307✔
1327
        case DNT_MEASUREMENT:
98✔
1328
        case DT_ID:
1329
        case DT_QUOTED_STRING:
1330
        case DT_URL:
1331
        case DT_PATH:
1332
        case DT_MAC_ADDRESS:
1333
        case DT_DATE:
1334
        case DT_TIME:
1335
        case DT_DATE_TIME:
1336
        case DT_IPV4_ADDRESS:
1337
        case DT_IPV6_ADDRESS:
1338
        case DT_HEX_DUMP:
1339
        case DT_UUID:
1340
        case DT_CREDIT_CARD_NUMBER:
1341
        case DT_VERSION_NUMBER:
1342
        case DT_OCTAL_NUMBER:
1343
        case DT_PERCENTAGE:
1344
        case DT_NUMBER:
1345
        case DT_HEX_NUMBER:
1346
        case DT_EMAIL:
1347
        case DT_CONSTANT:
1348
        case DT_ANCHOR:
1349
            return true;
98✔
1350
        default:
209✔
1351
            return false;
209✔
1352
    }
1353
}
1354

1355
data_parser::discover_format_state::discover_format_state()
724✔
1356
    : dfs_prefix_state(DFS_INIT), dfs_semi_state(DFS_INIT),
724✔
1357
      dfs_comma_state(DFS_INIT)
724✔
1358
{
1359
    memset(this->dfs_hist, 0, sizeof(this->dfs_hist));
724✔
1360
}
724✔
1361

1362
void
1363
data_parser::discover_format_state::update_for_element(
4,754✔
1364
    const data_parser::element& elem)
1365
{
1366
    this->dfs_prefix_state
1367
        = dfs_prefix_next(this->dfs_prefix_state, elem.e_token);
4,754✔
1368
    this->dfs_semi_state = dfs_semi_next(this->dfs_semi_state, elem.e_token);
4,754✔
1369
    this->dfs_comma_state = dfs_comma_next(this->dfs_comma_state, elem.e_token);
4,754✔
1370
    if (this->dfs_prefix_state != DFS_ERROR) {
4,754✔
1371
        if (this->dfs_semi_state == DFS_ERROR) {
2,162✔
UNCOV
1372
            this->dfs_semi_state = DFS_INIT;
×
1373
        }
1374
        if (this->dfs_comma_state == DFS_ERROR) {
2,162✔
1375
            this->dfs_comma_state = DFS_INIT;
23✔
1376
        }
1377
    }
1378
    this->dfs_hist[elem.e_token] += 1;
4,754✔
1379
}
4,754✔
1380

1381
void
1382
data_parser::discover_format_state::finalize()
724✔
1383
{
1384
    data_token_t qualifier = this->dfs_format.df_qualifier;
724✔
1385
    data_token_t separator = this->dfs_format.df_separator;
724✔
1386
    data_token_t prefix_term = this->dfs_format.df_prefix_terminator;
724✔
1387

1388
    this->dfs_format = FORMAT_PLAIN;
724✔
1389
    if (this->dfs_hist[DT_EQUALS]) {
724✔
1390
        qualifier = DT_COLON;
46✔
1391
        separator = DT_EQUALS;
46✔
1392
    }
1393

1394
    if (this->dfs_semi_state != DFS_ERROR && this->dfs_hist[DT_SEMI]) {
724✔
1395
        this->dfs_format = FORMAT_SEMI;
10✔
1396
    } else if (this->dfs_comma_state != DFS_ERROR) {
714✔
1397
        if (this->dfs_hist[DT_COMMA] > 0) {
674✔
1398
            this->dfs_format = FORMAT_COMMA;
23✔
1399
        } else if (this->dfs_hist[DT_EMDASH] > 0) {
651✔
1400
            this->dfs_format = FORMAT_EMDASH;
10✔
1401
        }
1402
        if (separator == DT_COLON && this->dfs_hist[DT_COMMA] > 0) {
674✔
1403
            if (!((this->dfs_hist[DT_COLON] == this->dfs_hist[DT_COMMA])
18✔
1404
                  || ((this->dfs_hist[DT_COLON] - 1)
17✔
1405
                      == this->dfs_hist[DT_COMMA])))
17✔
1406
            {
1407
                separator = DT_INVALID;
11✔
1408
                if (this->dfs_hist[DT_COLON] > 0) {
11✔
1409
                    prefix_term = DT_COLON;
2✔
1410
                }
1411
            }
1412
        }
1413
    }
1414

1415
    this->dfs_format.df_qualifier = qualifier;
724✔
1416
    this->dfs_format.df_separator = separator;
724✔
1417
    this->dfs_format.df_prefix_terminator = prefix_term;
724✔
1418
}
724✔
1419

1420
void
1421
data_parser::element_list_t::push_back(const data_parser::element& elem,
10,127✔
1422
                                       const char* fn,
1423
                                       int line)
1424
{
1425
    ELEMENT_TRACE;
10,127✔
1426

1427
    require(elem.e_capture.c_end >= -1);
10,127✔
1428
    require(this->empty()
10,127✔
1429
            || (elem.e_capture.c_begin == -1 && elem.e_capture.c_end == -1)
1430
            || this->back().e_capture.c_end <= elem.e_capture.c_begin);
1431
    this->std::list<element>::push_back(elem);
10,127✔
1432
}
10,127✔
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