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

tstack / lnav / 22507085525-2793

27 Feb 2026 10:49PM UTC coverage: 68.948% (-0.02%) from 68.966%
22507085525-2793

push

github

tstack
fix mixup of O_CLOEXEC with FD_CLOEXEC

52007 of 75429 relevant lines covered (68.95%)

440500.48 hits per line

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

44.99
/src/files_sub_source.cc
1
/**
2
 * Copyright (c) 2020, 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 <vector>
31

32
#include "files_sub_source.hh"
33

34
#include "base/ansi_scrubber.hh"
35
#include "base/attr_line.builder.hh"
36
#include "base/fs_util.hh"
37
#include "base/humanize.hh"
38
#include "base/humanize.network.hh"
39
#include "base/humanize.time.hh"
40
#include "base/itertools.enumerate.hh"
41
#include "base/itertools.hh"
42
#include "base/opt_util.hh"
43
#include "base/string_util.hh"
44
#include "config.h"
45
#include "lnav.hh"
46
#include "mapbox/variant.hpp"
47
#include "md4cpp.hh"
48
#include "readline_highlighters.hh"
49
#include "sql_util.hh"
50

51
using namespace md4cpp::literals;
52
using namespace lnav::roles::literals;
53

54
namespace files_model {
55
files_list_selection
56
from_selection(std::optional<vis_line_t> sel_vis)
25✔
57
{
58
    if (!sel_vis) {
25✔
59
        return no_selection{};
15✔
60
    }
61

62
    auto& fc = lnav_data.ld_active_files;
10✔
63
    int sel = sel_vis.value();
10✔
64

65
    {
66
        safe::ReadAccess<safe_name_to_stubs> errs(*fc.fc_name_to_stubs);
10✔
67

68
        if (sel < (ssize_t) errs->size()) {
10✔
69
            auto iter = errs->begin();
×
70

71
            std::advance(iter, sel);
72
            return stub_selection::build(
×
73
                sel,
74
                std::make_pair(iter->second.fsi_display_name,
×
75
                               iter->second.fsi_description));
×
76
        }
77

78
        sel -= errs->size();
10✔
79
    }
10✔
80

81
    if (sel < (ssize_t) fc.fc_other_files.size()) {
10✔
82
        auto iter = fc.fc_other_files.begin();
×
83

84
        std::advance(iter, sel);
85
        return other_selection::build(sel, iter);
×
86
    }
87

88
    sel -= fc.fc_other_files.size();
10✔
89

90
    if (sel < (ssize_t) fc.fc_files.size()) {
10✔
91
        auto iter = fc.fc_files.begin();
10✔
92

93
        std::advance(iter, sel);
94
        return file_selection::build(sel, iter);
10✔
95
    }
96

97
    return no_selection{};
×
98
}
99
}  // namespace files_model
100

101
bool
102
files_sub_source::list_input_handle_key(listview_curses& lv, const ncinput& ch)
1✔
103
{
104
    switch (ch.eff_text[0]) {
1✔
105
        case NCKEY_ENTER:
×
106
        case '\r': {
107
            auto sel = files_model::from_selection(lv.get_selection());
×
108

109
            sel.match(
×
110
                [](files_model::no_selection) {},
×
111
                [](files_model::stub_selection) {},
×
112
                [](files_model::other_selection) {},
×
113
                [&](files_model::file_selection& fs) {
×
114
                    auto& lss = lnav_data.ld_log_source;
×
115
                    auto lf = *fs.sb_iter;
×
116

117
                    lf->set_indexing(true);
×
118
                    lss.find_data(lf) | [](auto ld) {
×
119
                        ld->set_visibility(true);
×
120
                        lnav_data.ld_log_source.text_filters_changed();
×
121
                    };
122

123
                    if (lf->get_format() != nullptr) {
×
124
                        auto& log_view = lnav_data.ld_views[LNV_LOG];
×
125
                        lss.row_for_time(lf->front().get_timeval()) |
×
126
                            [](auto row) {
×
127
                                lnav_data.ld_views[LNV_LOG].set_selection(row);
×
128
                            };
129
                        ensure_view(&log_view);
×
130
                    } else {
131
                        auto& tv = lnav_data.ld_views[LNV_TEXT];
×
132
                        auto& tss = lnav_data.ld_text_source;
×
133

134
                        tss.to_front(lf);
×
135
                        tv.reload_data();
×
136
                        ensure_view(&tv);
×
137
                    }
138

139
                    lv.reload_data();
×
140
                    set_view_mode(ln_mode_t::PAGING);
×
141
                });
×
142

143
            return true;
×
144
        }
145

146
        case ' ': {
×
147
            auto sel = files_model::from_selection(lv.get_selection());
×
148

149
            sel.match([](files_model::no_selection) {},
×
150
                      [](files_model::stub_selection) {},
×
151
                      [](files_model::other_selection) {},
×
152
                      [&](files_model::file_selection& fs) {
×
153
                          auto& lss = lnav_data.ld_log_source;
×
154
                          auto lf = *fs.sb_iter;
×
155

156
                          log_debug("toggling visibility of file: %s",
×
157
                                    lf->get_filename().c_str());
158
                          lss.find_data(lf) | [](auto ld) {
×
159
                              ld->get_file_ptr()->set_indexing(!ld->ld_visible);
×
160
                              ld->set_visibility(!ld->ld_visible);
×
161
                          };
162

163
                          auto top_view = *lnav_data.ld_view_stack.top();
×
164
                          auto tss = top_view->get_sub_source();
×
165

166
                          if (tss != nullptr) {
×
167
                              if (tss != &lss) {
×
168
                                  lss.text_filters_changed();
×
169
                                  lnav_data.ld_views[LNV_LOG].reload_data();
×
170
                              }
171
                              tss->text_filters_changed();
×
172
                              top_view->reload_data();
×
173
                          }
174

175
                          lv.reload_data();
×
176
                      });
×
177
            return true;
×
178
        }
179
        case '/': {
×
180
            execute_command(lnav_data.ld_exec_context, "prompt search-files");
×
181
            return true;
×
182
        }
183
        case 'X': {
×
184
            auto sel = files_model::from_selection(lv.get_selection());
×
185

186
            sel.match(
×
187
                [](files_model::no_selection) {},
×
188
                [&](files_model::stub_selection& es) {
×
189
                    auto& fc = lnav_data.ld_active_files;
×
190

191
                    fc.fc_file_names.erase(es.sb_iter.first);
×
192

193
                    auto name_iter = fc.fc_file_names.begin();
×
194
                    while (name_iter != fc.fc_file_names.end()) {
×
195
                        if (name_iter->first == es.sb_iter.first) {
×
196
                            fc.fc_file_names.erase(name_iter);
×
197
                            name_iter = fc.fc_file_names.begin();
×
198
                            continue;
×
199
                        }
200

201
                        auto rp_opt = humanize::network::path::from_str(
202
                            name_iter->first);
×
203

204
                        if (rp_opt) {
×
205
                            auto rp = *rp_opt;
×
206

207
                            if (fmt::to_string(rp.home()) == es.sb_iter.first) {
×
208
                                fc.fc_other_files.erase(name_iter->first);
×
209
                                fc.fc_file_names.erase(name_iter);
×
210
                                name_iter = fc.fc_file_names.begin();
×
211
                                continue;
×
212
                            }
213
                        }
214
                        ++name_iter;
×
215
                    }
216

217
                    fc.fc_name_to_stubs->writeAccess()->erase(es.sb_iter.first);
×
218
                    fc.fc_invalidate_merge = true;
×
219
                    lv.reload_data();
×
220
                },
×
221
                [](files_model::other_selection) {},
×
222
                [](files_model::file_selection) {});
×
223
            return true;
×
224
        }
225
    }
226
    return false;
1✔
227
}
228

229
void
230
files_sub_source::list_input_handle_scroll_out(listview_curses& lv)
×
231
{
232
    set_view_mode(ln_mode_t::PAGING);
×
233
    lnav_data.ld_filter_view.reload_data();
×
234
}
235

236
size_t
237
files_sub_source::text_line_count()
3,739✔
238
{
239
    const auto& fc = lnav_data.ld_active_files;
3,739✔
240
    auto retval = fc.fc_name_to_stubs->readAccess()->size()
3,739✔
241
        + fc.fc_other_files.size() + fc.fc_files.size();
3,739✔
242

243
    return retval;
3,739✔
244
}
245

246
size_t
247
files_sub_source::text_line_width(textview_curses& curses)
1✔
248
{
249
    return 512;
1✔
250
}
251

252
line_info
253
files_sub_source::text_value_for_line(textview_curses& tc,
1✔
254
                                      int line,
255
                                      std::string& value_out,
256
                                      text_sub_source::line_flags_t flags)
257
{
258
    bool selected = line == tc.get_selection();
1✔
259
    role_t cursor_role = lnav_data.ld_mode == ln_mode_t::FILES
2✔
260
        ? role_t::VCR_CURSOR_LINE
1✔
261
        : role_t::VCR_DISABLED_CURSOR_LINE;
262
    const auto& fc = lnav_data.ld_active_files;
1✔
263
    auto filename_width = std::min(fc.fc_largest_path_length, (size_t) 30);
1✔
264

265
    this->fss_curr_line.clear();
1✔
266
    auto& al = this->fss_curr_line;
1✔
267
    attr_line_builder alb(al);
1✔
268

269
    if (selected) {
1✔
270
        al.append(" ", VC_GRAPHIC.value(NCACS_RARROW));
1✔
271
    } else {
272
        al.append(" ");
×
273
    }
274
    {
275
        safe::ReadAccess<safe_name_to_stubs> errs(*fc.fc_name_to_stubs);
1✔
276

277
        if (line < (ssize_t) errs->size()) {
1✔
278
            auto iter = std::next(errs->begin(), line);
×
279
            auto path = std::filesystem::path(iter->second.fsi_display_name);
×
280
            auto fn = fmt::to_string(path.filename());
×
281

282
            truncate_to(fn, filename_width);
×
283
            al.append("   ");
×
284
            {
285
                auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_FILE));
×
286

287
                al.appendf(FMT_STRING("{:<{}}"), fn, filename_width);
×
288
            }
289
            al.append("   ");
×
290
            ui_icon_t icon = ui_icon_t::ok;
×
291
            switch (iter->second.fsi_description.um_level) {
×
292
                case lnav::console::user_message::level::raw:
×
293
                    break;
×
294
                case lnav::console::user_message::level::ok:
×
295
                    icon = ui_icon_t::ok;
×
296
                    break;
×
297
                case lnav::console::user_message::level::info:
×
298
                    icon = ui_icon_t::info;
×
299
                    break;
×
300
                case lnav::console::user_message::level::warning:
×
301
                    icon = ui_icon_t::warning;
×
302
                    break;
×
303
                case lnav::console::user_message::level::error:
×
304
                    icon = ui_icon_t::error;
×
305
                    break;
×
306
            }
307
            al.append(" ", VC_ICON.value(icon));
×
308
            if (selected) {
×
309
                al.with_attr_for_all(VC_ROLE.value(cursor_role));
×
310
            }
311

312
            value_out = al.get_string();
×
313
            return {};
×
314
        }
315

316
        line -= errs->size();
1✔
317
    }
1✔
318

319
    if (line < (ssize_t) fc.fc_other_files.size()) {
1✔
320
        auto iter = std::next(fc.fc_other_files.begin(), line);
×
321
        auto path = std::filesystem::path(iter->first);
×
322
        auto fn = fmt::to_string(path);
×
323

324
        truncate_to(fn, filename_width);
×
325
        al.append("   ");
×
326
        {
327
            auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_FILE));
×
328

329
            al.appendf(FMT_STRING("{:<{}}"), fn, filename_width);
×
330
        }
331
        al.append("   ")
×
332
            .appendf(FMT_STRING("{:14}"), iter->second.ofd_format)
×
333
            .append("  ")
×
334
            .append(iter->second.ofd_description);
×
335
        if (selected) {
×
336
            al.with_attr_for_all(VC_ROLE.value(cursor_role));
×
337
        }
338
        if (line == (ssize_t) fc.fc_other_files.size() - 1) {
×
339
            al.with_attr_for_all(VC_STYLE.value(text_attrs::with_underline()));
×
340
        }
341

342
        value_out = al.get_string();
×
343
        return {};
×
344
    }
345

346
    line -= fc.fc_other_files.size();
1✔
347

348
    const auto& lf = fc.fc_files[line];
1✔
349
    const auto& loo = lf->get_open_options();
1✔
350
    auto ld_opt = lnav_data.ld_log_source.find_data(lf);
1✔
351
    auto fn = fmt::to_string(std::filesystem::path(lf->get_unique_path()));
1✔
352

353
    truncate_to(fn, filename_width);
1✔
354
    al.append(" ");
1✔
355
    if (ld_opt) {
1✔
356
        if (ld_opt.value()->ld_visible) {
1✔
357
            al.append("\u25c6"_ok);
1✔
358
        } else {
359
            al.append("\u25c7"_comment);
×
360
        }
361
    } else {
362
        al.append("\u25c6"_comment);
×
363
    }
364
    al.append(" ");
1✔
365
    al.appendf(FMT_STRING("{:<{}}"), fn, filename_width);
4✔
366
    al.append("  ");
1✔
367
    {
368
        auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_NUMBER));
1✔
369

370
        al.appendf(FMT_STRING("{:>8}"),
4✔
371
                   humanize::file_size(lf->get_index_size(),
2✔
372
                                       humanize::alignment::columnar));
373
    }
1✔
374
    al.append(" ");
1✔
375
    auto indexed_size = lf->get_index_size();
1✔
376
    auto total_size = lf->get_content_size();
1✔
377
    if (!lf->get_decompress_error().empty()) {
1✔
378
        al.append(" ", VC_ICON.value(ui_icon_t::error));
×
379
    } else if (!lf->get_notes().empty()) {
1✔
380
        al.append(" ", VC_ICON.value(ui_icon_t::warning));
×
381
    } else if (indexed_size < total_size) {
1✔
382
        al.append(humanize::sparkline(indexed_size, total_size));
×
383
    } else if ((loo.loo_child_poller && loo.loo_child_poller->is_alive())
1✔
384
               || (loo.loo_piper && !loo.loo_piper->is_finished()))
1✔
385
    {
386
        al.append(" ", VC_ICON.value(ui_icon_t::busy));
×
387
    } else {
388
        al.append(" ", VC_ICON.value(ui_icon_t::ok));
1✔
389
    }
390
    if (selected) {
1✔
391
        al.with_attr_for_all(VC_ROLE.value(cursor_role));
1✔
392
    }
393

394
    value_out = al.get_string();
1✔
395
    this->fss_last_line_len = value_out.length();
1✔
396

397
    return {};
1✔
398
}
1✔
399

400
void
401
files_sub_source::text_attrs_for_line(textview_curses& tc,
1✔
402
                                      int line,
403
                                      string_attrs_t& value_out)
404
{
405
    value_out = this->fss_curr_line.get_attrs();
1✔
406
}
1✔
407

408
size_t
409
files_sub_source::text_size_for_line(textview_curses& tc,
×
410
                                     int line,
411
                                     text_sub_source::line_flags_t raw)
412
{
413
    return 0;
×
414
}
415

416
static auto
417
spinner_index()
×
418
{
419
    auto now = ui_clock::now();
×
420

421
    return std::chrono::duration_cast<std::chrono::milliseconds>(
×
422
               now.time_since_epoch())
×
423
               .count()
×
424
        / 100;
×
425
}
426

427
bool
428
files_overlay_source::list_static_overlay(const listview_curses& lv,
15✔
429
                                          media_t media,
430
                                          int y,
431
                                          int bottom,
432
                                          attr_line_t& value_out)
433
{
434
    if (y != 0) {
15✔
435
        return false;
12✔
436
    }
437
    static const char PROG[] = "-\\|/";
438
    constexpr size_t PROG_SIZE = sizeof(PROG) - 1;
3✔
439

440
    auto& fc = lnav_data.ld_active_files;
3✔
441
    auto fc_prog = fc.fc_progress;
3✔
442
    safe::WriteAccess<safe_scan_progress> sp(*fc_prog);
3✔
443

444
    if (!sp->sp_extractions.empty()) {
3✔
445
        const auto& prog = sp->sp_extractions.front();
×
446

447
        value_out.with_ansi_string(fmt::format(
×
448
            "{} Extracting " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM
449
                                                    "... {:>8}/{}",
450
            humanize::sparkline(prog.ep_out_size, prog.ep_total_size),
×
451
            prog.ep_path.filename().string(),
×
452
            humanize::file_size(prog.ep_out_size, humanize::alignment::none),
×
453
            humanize::file_size(prog.ep_total_size,
×
454
                                humanize::alignment::none)));
455
        return true;
×
456
    }
457
    if (!sp->sp_tailers.empty()) {
3✔
458
        auto first_iter = sp->sp_tailers.begin();
×
459

460
        value_out.with_ansi_string(fmt::format(
×
461
            "{} Connecting to " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM ": {}",
462
            PROG[spinner_index() % PROG_SIZE],
×
463
            first_iter->first,
×
464
            first_iter->second.tp_message));
×
465
        return true;
×
466
    }
467

468
    return false;
3✔
469
}
3✔
470

471
bool
472
files_sub_source::text_handle_mouse(
×
473
    textview_curses& tc,
474
    const listview_curses::display_line_content_t&,
475
    mouse_event& me)
476
{
477
    auto nci = ncinput{};
×
478
    if (me.is_click_in(mouse_button_t::BUTTON_LEFT, 1, 3)) {
×
479
        nci.id = ' ';
×
480
        nci.eff_text[0] = ' ';
×
481
        this->list_input_handle_key(tc, nci);
×
482
    }
483
    if (me.is_double_click_in(mouse_button_t::BUTTON_LEFT, line_range{4, -1})) {
×
484
        nci.id = '\r';
×
485
        nci.eff_text[0] = '\r';
×
486
        this->list_input_handle_key(tc, nci);
×
487
    }
488

489
    return false;
×
490
}
491

492
void
493
files_sub_source::text_update_marks(vis_bookmarks& bm)
2,278✔
494
{
495
    auto& bm_errs = bm[&textview_curses::BM_ERRORS];
2,278✔
496
    auto& bm_warn = bm[&textview_curses::BM_WARNINGS];
2,278✔
497

498
    bm_errs.clear();
2,278✔
499
    bm_warn.clear();
2,278✔
500

501
    auto index = 0_vl;
2,278✔
502

503
    auto& fc = lnav_data.ld_active_files;
2,278✔
504
    {
505
        safe::ReadAccess<safe_name_to_stubs> stubs(*fc.fc_name_to_stubs);
2,278✔
506

507
        for (const auto& stub_pair : *stubs) {
2,278✔
508
            switch (stub_pair.second.fsi_description.um_level) {
×
509
                case lnav::console::user_message::level::warning:
×
510
                    bm_warn.insert_once(index);
×
511
                    break;
×
512
                case lnav::console::user_message::level::error:
×
513
                    bm_errs.insert_once(index);
×
514
                    break;
×
515
                default:
×
516
                    break;
×
517
            }
518
            index += 1_vl;
×
519
        }
520
    }
2,278✔
521

522
    index += vis_line_t(fc.fc_other_files.size());
2,278✔
523

524
    for (const auto& lf : fc.fc_files) {
3,182✔
525
        if (!lf->get_decompress_error().empty()) {
904✔
526
            bm_errs.insert_once(index);
×
527
        } else if (!lf->get_notes().empty()) {
904✔
528
            bm_warn.insert_once(index);
1✔
529
        }
530
        index += 1_vl;
904✔
531
    }
532
}
2,278✔
533

534
void
535
files_sub_source::text_selection_changed(textview_curses& tc)
21✔
536
{
537
    auto sel = files_model::from_selection(tc.get_selection());
21✔
538
    std::vector<attr_line_t> details;
21✔
539

540
    this->fss_details_mtime = std::chrono::microseconds::zero();
21✔
541
    sel.match(
21✔
542
        [](files_model::no_selection) {},
×
543

544
        [&details](const files_model::stub_selection& es) {
×
545
            es.sb_iter.second.to_attr_line().split_lines(details);
×
546
        },
×
547
        [&details](const files_model::other_selection& os) {
×
548
            auto path = std::filesystem::path(os.sb_iter->first);
×
549

550
            details.emplace_back(
×
551
                attr_line_t().appendf(FMT_STRING("Full path: {}"), path));
×
552
            details.emplace_back(attr_line_t("  ").append("Match Details"_h3));
×
553
            for (const auto& msg : os.sb_iter->second.ofd_details) {
×
554
                auto msg_al = msg.to_attr_line();
×
555

556
                for (auto& msg_line : msg_al.rtrim().split_lines()) {
×
557
                    msg_line.insert(0, 4, ' ');
×
558
                    details.emplace_back(msg_line);
×
559
                }
560
            }
561
        },
×
562
        [&details, this](const files_model::file_selection& fs) {
21✔
563
            static constexpr auto NAME_WIDTH = 17;
564

565
            auto lf = *fs.sb_iter;
8✔
566
            this->fss_details_mtime = lf->get_modified_time();
8✔
567
            auto path = lf->get_filename();
8✔
568
            auto actual_path = lf->get_actual_path();
8✔
569
            auto format = lf->get_format();
8✔
570
            const auto& open_opts = lf->get_open_options();
8✔
571

572
            details.emplace_back(attr_line_t()
16✔
573
                                     .appendf(FMT_STRING("{}"), path.filename())
40✔
574
                                     .with_attr_for_all(VC_STYLE.value(
16✔
575
                                         text_attrs::with_bold())));
16✔
576
            details.emplace_back(
8✔
577
                attr_line_t("  ")
8✔
578
                    .append(":open_file_folder:"_emoji)
16✔
579
                    .appendf(FMT_STRING(" {}"), path.parent_path()));
32✔
580

581
            const auto& decompress_err = lf->get_decompress_error();
8✔
582
            if (!decompress_err.empty()) {
8✔
583
                details.emplace_back(
×
584
                    attr_line_t("  ")
×
585
                        .append("Error"_h2)
×
586
                        .append(": ")
×
587
                        .append(lnav::roles::error(decompress_err)));
×
588
            }
589

590
            const auto notes = lf->get_notes();
8✔
591
            if (!notes.empty()) {
8✔
592
                details.emplace_back(
×
593
                    attr_line_t("  ").append("Notes"_h2).append(":"));
×
594
                for (const auto& note_um : notes.values()) {
×
595
                    for (const auto& note_line :
×
596
                         note_um.to_attr_line().split_lines())
×
597
                    {
598
                        details.emplace_back(
×
599
                            attr_line_t("    ").append(note_line));
×
600
                    }
601
                }
602
            }
603
            details.emplace_back(attr_line_t("  ").append("General"_h2));
8✔
604
            if (actual_path) {
8✔
605
                if (path != actual_path.value()) {
8✔
606
                    auto line = attr_line_t()
×
607
                                    .append("Actual Path"_h3)
×
608
                                    .right_justify(NAME_WIDTH)
×
609
                                    .append(": ")
×
610
                                    .append(lnav::roles::file(
×
611
                                        fmt::to_string(actual_path.value())))
×
612
                                    .move();
×
613
                    details.emplace_back(line);
×
614
                }
615
            } else {
616
                details.emplace_back(attr_line_t().append("  Piped"));
×
617
            }
618
            if (open_opts.loo_child_poller) {
8✔
619
                auto cmd_al = attr_line_t(
620
                    open_opts.loo_child_poller->get_description());
×
621
                readline_shlex_highlighter(cmd_al, std::nullopt);
×
622
                cmd_al.with_attr_for_all(
×
623
                    VC_ROLE.value(role_t::VCR_QUOTED_CODE));
×
624
                auto child_icon = ui_icon_t::ok;
×
625
                if (open_opts.loo_child_poller->is_alive()) {
×
626
                    child_icon = ui_icon_t::busy;
×
627
                } else if (open_opts.loo_child_poller->get_exit_status()
×
628
                               .value_or(0)
×
629
                           != 0)
×
630
                {
631
                    child_icon = ui_icon_t::error;
×
632
                }
633
                details.emplace_back(
×
634
                    attr_line_t()
×
635
                        .append("Child Command"_h3)
×
636
                        .right_justify(NAME_WIDTH)
×
637
                        .append(": ")
×
638
                        .append("  ", VC_ICON.value(child_icon))
×
639
                        .append(cmd_al));
640
            }
641
            {
642
                auto tf_opt = lf->get_text_format();
8✔
643
                if (tf_opt) {
8✔
644
                    details.emplace_back(
7✔
645
                        attr_line_t()
14✔
646
                            .append("MIME Type"_h3)
7✔
647
                            .right_justify(NAME_WIDTH)
7✔
648
                            .append(": ")
7✔
649
                            .append(fmt::to_string(tf_opt.value())));
14✔
650
                }
651
            }
652
            auto ltime = std::chrono::seconds{
653
                convert_log_time_to_local(lf->get_stat().st_mtime)};
8✔
654
            auto ltime_str = lnav::to_rfc3339_string(ltime, 'T');
8✔
655
            details.emplace_back(attr_line_t()
16✔
656
                                     .append("Last Modified"_h3)
8✔
657
                                     .right_justify(NAME_WIDTH)
8✔
658
                                     .append(": ")
8✔
659
                                     .append(ltime_str));
660
            details.emplace_back(
8✔
661
                attr_line_t()
8✔
662
                    .append("Size"_h3)
8✔
663
                    .right_justify(NAME_WIDTH)
8✔
664
                    .append(": ")
8✔
665
                    .append(humanize::file_size(lf->get_index_size(),
16✔
666
                                                humanize::alignment::none)));
667
            if (lf->is_compressed()) {
8✔
668
                details.emplace_back(attr_line_t()
×
669
                                         .append("Compressed Size"_h3)
×
670
                                         .right_justify(NAME_WIDTH)
×
671
                                         .append(": ")
×
672
                                         .append(humanize::file_size(
×
673
                                             lf->get_stat().st_size,
×
674
                                             humanize::alignment::none)));
675
            }
676

677
            details.emplace_back(attr_line_t()
16✔
678
                                     .append("Lines"_h3)
8✔
679
                                     .right_justify(NAME_WIDTH)
8✔
680
                                     .append(": ")
8✔
681
                                     .append(lnav::roles::number(fmt::format(
16✔
682
                                         FMT_STRING("{:L}"), lf->size()))));
32✔
683
            if (format != nullptr && lf->size() > 0) {
8✔
684
                auto tr = lf->get_content_time_range();
7✔
685
                details.emplace_back(attr_line_t()
14✔
686
                                         .append("Time Range"_h3)
7✔
687
                                         .right_justify(NAME_WIDTH)
7✔
688
                                         .append(": ")
7✔
689
                                         .append(lnav::to_rfc3339_string(
14✔
690
                                             to_timeval(tr.tr_begin), 'T'))
691
                                         .append(" - ")
7✔
692
                                         .append(lnav::to_rfc3339_string(
14✔
693
                                             to_timeval(tr.tr_end), 'T')));
694
                if (open_opts.loo_time_range.has_lower_bound()) {
7✔
695
                    details.emplace_back(
×
696
                        attr_line_t()
×
697
                            .append("Cutoff From"_h3)
×
698
                            .right_justify(NAME_WIDTH)
×
699
                            .append(": ")
×
700
                            .append(lnav::to_rfc3339_string(
×
701
                                to_timeval(open_opts.loo_time_range.tr_begin),
702
                                'T')));
703
                }
704
                if (open_opts.loo_time_range.has_upper_bound()) {
7✔
705
                    details.emplace_back(
×
706
                        attr_line_t()
×
707
                            .append("Cutoff To"_h3)
×
708
                            .right_justify(NAME_WIDTH)
×
709
                            .append(": ")
×
710
                            .append(lnav::to_rfc3339_string(
×
711
                                to_timeval(open_opts.loo_time_range.tr_end),
712
                                'T')));
713
                }
714
                details.emplace_back(
7✔
715
                    attr_line_t()
14✔
716
                        .append("Duration"_h3)
7✔
717
                        .right_justify(NAME_WIDTH)
7✔
718
                        .append(": ")
7✔
719
                        .append(humanize::time::duration::from_tv(
7✔
720
                                    lf->back().get_timeval()
7✔
721
                                    - lf->front().get_timeval())
14✔
722
                                    .to_string()));
14✔
723
            }
724

725
            auto file_options_opt = lf->get_file_options();
8✔
726
            if (file_options_opt) {
8✔
727
                auto& [path, file_options] = file_options_opt.value();
×
728

729
                details.emplace_back(attr_line_t()
×
730
                                         .append("Options Path"_h3)
×
731
                                         .right_justify(NAME_WIDTH)
×
732
                                         .append(": ")
×
733
                                         .append(lnav::roles::file(path)));
×
734
                if (file_options.fo_default_zone.pp_value != nullptr) {
×
735
                    details.emplace_back(attr_line_t()
×
736
                                             .append("Timezone"_h3)
×
737
                                             .right_justify(NAME_WIDTH)
×
738
                                             .append(": ")
×
739
                                             .append(lnav::roles::symbol(
×
740
                                                 file_options.fo_default_zone
741
                                                     .pp_value->name())));
×
742
                }
743
            }
744

745
            {
746
                details.emplace_back(
8✔
747
                    attr_line_t()
8✔
748
                        .append("Provenance"_h3)
8✔
749
                        .right_justify(NAME_WIDTH)
8✔
750
                        .append(": ")
8✔
751
                        .append(fmt::to_string(open_opts.loo_source)));
16✔
752
                details.emplace_back(
8✔
753
                    attr_line_t()
8✔
754
                        .append("Flags"_h3)
8✔
755
                        .right_justify(NAME_WIDTH)
8✔
756
                        .append(": ")
8✔
757
                        .append(lnav::roles::for_flag(
16✔
758
                            "\u2022", open_opts.loo_include_in_session))
8✔
759
                        .append("include-in-session")
8✔
760
                        .append(", ")
8✔
761
                        .append(lnav::roles::for_flag(
16✔
762
                            "\u2022", open_opts.loo_detect_format))
8✔
763
                        .append("detect-format"));
764
                if (open_opts.loo_init_location.valid()) {
8✔
765
                    auto loc = open_opts.loo_init_location.match(
766
                        [](default_for_text_format) {
8✔
767
                            return std::string("default");
16✔
768
                        },
769
                        [](file_location_tail) { return std::string("tail"); },
×
770
                        [](int vl) {
×
771
                            return fmt::format(FMT_STRING("L{:L}"), vl);
×
772
                        },
773
                        [](std::string section) { return section; });
8✔
774
                    details.emplace_back(attr_line_t()
16✔
775
                                             .append("Initial Location"_h3)
8✔
776
                                             .right_justify(NAME_WIDTH)
8✔
777
                                             .append(": ")
8✔
778
                                             .append(loc));
779
                }
8✔
780
            }
781

782
            {
783
                auto line = attr_line_t("  ")
16✔
784
                                .append("Log Format"_h2)
8✔
785
                                .append(": ")
8✔
786
                                .move();
8✔
787

788
                if (format != nullptr) {
8✔
789
                    line.append(lnav::roles::identifier(
7✔
790
                        format->get_name().to_string()));
14✔
791
                } else {
792
                    line.append("(none)"_comment);
1✔
793
                }
794
                details.emplace_back(line);
8✔
795
            }
8✔
796

797
            if (lf->get_format_ptr() != nullptr) {
8✔
798
                const auto um = lnav::console::user_message::info(
799
                    attr_line_t("The file contents matched this log format and "
14✔
800
                                "will be shown in the LOG view"));
7✔
801
                um.to_attr_line().rtrim().split_lines()
14✔
802
                    | lnav::itertools::for_each([&details](const auto& al) {
14✔
803
                          details.emplace_back(attr_line_t("    ").append(al));
7✔
804
                      });
7✔
805
            } else {
7✔
806
                auto cmd
807
                    = attr_line_t("lnav -m format ")
1✔
808
                          .append("format-name",
1✔
809
                                  VC_STYLE.value(text_attrs::with_underline()))
2✔
810
                          .append(" test ")
1✔
811
                          .append(lf->get_filename())
1✔
812
                          .with_attr_for_all(
1✔
813
                              VC_ROLE.value(role_t::VCR_QUOTED_CODE));
2✔
814
                const auto um
815
                    = lnav::console::user_message::info(
2✔
816

817
                          "The file contents did not match any log "
818
                          "formats and can be accessed in the TEXT view")
819
                          .with_help(attr_line_t("If you expected this file to "
2✔
820
                                                 "match a particular "
821
                                                 "format, you can run the "
822
                                                 "following to get more "
823
                                                 "details:\n  ")
824
                                         .append(cmd));
1✔
825
                um.to_attr_line().rtrim().split_lines()
2✔
826
                    | lnav::itertools::for_each([&details](const auto& al) {
2✔
827
                          details.emplace_back(attr_line_t("    ").append(al));
3✔
828
                      });
3✔
829
            }
1✔
830

831
            const auto& match_msgs = lf->get_format_match_messages();
8✔
832
            details.emplace_back(
8✔
833
                attr_line_t("    ").append("Match Details"_h3));
16✔
834
            for (const auto& msg : match_msgs) {
95✔
835
                auto msg_al = msg.to_attr_line();
87✔
836

837
                for (auto& msg_line : msg_al.rtrim().split_lines()) {
400✔
838
                    msg_line.insert(0, 6, ' ');
313✔
839
                    details.emplace_back(msg_line);
313✔
840
                }
87✔
841
            }
87✔
842

843
            const auto& ili = lf->get_invalid_line_info();
8✔
844
            if (ili.ili_total > 0) {
8✔
845
                auto dotdot = ili.ili_total
×
846
                    > logfile::invalid_line_info::MAX_INVALID_LINES;
847
                auto um = lnav::console::user_message::error(
×
848
                              attr_line_t()
×
849
                                  .append(lnav::roles::number(
×
850
                                      fmt::to_string(ili.ili_total)))
×
851
                                  .append(" line(s) are not handled by the "
×
852
                                          "format and considered invalid"))
853
                              .with_note(attr_line_t("Lines: ")
×
854
                                             .join(ili.ili_lines, ", ")
×
855
                                             .append(dotdot ? ", ..." : ""))
×
856
                              .with_help(
×
857
                                  "The format may be need to be adjusted to "
858
                                  "capture these lines");
×
859

860
                details.emplace_back(attr_line_t());
×
861
                um.to_attr_line().rtrim().split_lines()
×
862
                    | lnav::itertools::for_each([&details](const auto& al) {
×
863
                          details.emplace_back(attr_line_t("    ").append(al));
×
864
                      });
×
865
            }
866

867
            {
868
                const auto& meta = lf->get_embedded_metadata();
8✔
869

870
                if (!meta.empty()) {
8✔
871
                    details.emplace_back(
×
872
                        attr_line_t("  ").append("Embedded Metadata:"_h2));
×
873
                    for (const auto& [index, meta_pair] :
×
874
                         lnav::itertools::enumerate(meta))
×
875
                    {
876
                        details.emplace_back(
×
877
                            attr_line_t("  ")
×
878
                                .appendf(FMT_STRING("[{}]"), index)
×
879
                                .append(" ")
×
880
                                .append(lnav::roles::h3(meta_pair.first)));
×
881
                        details.emplace_back(
×
882
                            attr_line_t("      MIME Type: ")
×
883
                                .append(lnav::roles::symbol(fmt::to_string(
×
884
                                    meta_pair.second.m_format))));
×
885
                        line_range lr{6, -1};
×
886
                        details.emplace_back(attr_line_t().with_attr(
×
887
                            string_attr{lr, VC_GRAPHIC.value(NCACS_HLINE)}));
×
888
                        const auto val_sf = string_fragment::from_str(
×
889
                            meta_pair.second.m_value);
×
890
                        for (const auto val_line : val_sf.split_lines()) {
×
891
                            details.emplace_back(
×
892
                                attr_line_t("      ").append(val_line));
×
893
                        }
894
                    }
895
                }
896
            }
897
        });
8✔
898

899
    this->fss_details_source->replace_with(details);
21✔
900
}
21✔
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