Skip to content

Commit 9eeee8e

Browse files
committed
[ptime] add specializations for certain timestamps
Also... missed checking for selection in filter panel; clearing the last user mark was not working
1 parent ba5ac52 commit 9eeee8e

10 files changed

Lines changed: 182 additions & 47 deletions

src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ add_custom_command(
143143
DEPENDS ptimec
144144
COMMAND ptimec ${TIME_FORMATS} > time_fmts.cc)
145145

146-
add_library(lnavdt STATIC config.h.in ptimec.hh ptimec_rt.cc time_fmts.cc)
146+
add_library(lnavdt STATIC config.h.in ptimec.hh ptimec_spec.hh ptimec_rt.cc time_fmts.cc)
147147
target_include_directories(lnavdt PUBLIC . ${CMAKE_CURRENT_BINARY_DIR} third-party/date/include)
148148

149149
function(bin2c)

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ noinst_HEADERS = \
329329
pretty_printer.hh \
330330
preview_status_source.hh \
331331
ptimec.hh \
332+
ptimec_spec.hh \
332333
readline_callbacks.hh \
333334
readline_context.hh \
334335
readline_highlighters.hh \

src/filter_sub_source.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, const ncinput& ch)
106106
auto* tss = top_view->get_sub_source();
107107
auto& fs = tss->get_filters();
108108

109-
if (fs.empty()) {
109+
if (fs.empty() || !lv.get_selection()) {
110110
return true;
111111
}
112112

@@ -123,7 +123,7 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, const ncinput& ch)
123123
auto* tss = top_view->get_sub_source();
124124
auto& fs = tss->get_filters();
125125

126-
if (fs.empty()) {
126+
if (fs.empty() || !lv.get_selection()) {
127127
return true;
128128
}
129129

@@ -145,7 +145,7 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, const ncinput& ch)
145145
auto* tss = top_view->get_sub_source();
146146
auto& fs = tss->get_filters();
147147

148-
if (fs.empty()) {
148+
if (fs.empty() || !lv.get_selection()) {
149149
return true;
150150
}
151151

@@ -228,7 +228,7 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, const ncinput& ch)
228228
auto* tss = top_view->get_sub_source();
229229
auto& fs = tss->get_filters();
230230

231-
if (fs.empty()) {
231+
if (fs.empty() || !lv.get_selection()) {
232232
return true;
233233
}
234234

src/lnav.indexing.cc

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -364,21 +364,29 @@ rebuild_indexes(std::optional<ui_clock::time_point> deadline)
364364
}
365365

366366
lf.sort([](const auto& left, const auto& right) {
367-
return right->get_stat().st_size < left->get_stat().st_size;
367+
const auto& lst = left->get_stat();
368+
const auto& rst = right->get_stat();
369+
return rst.st_size < lst.st_size
370+
|| (rst.st_size == lst.st_size
371+
&& rst.st_mtime < lst.st_mtime);
368372
});
369373

370374
const auto& dupe_name = lf.front()->get_unique_path();
371-
log_debug("Keeping duplicated file: %s; size=%lld; path=%s",
372-
lf.front()->get_content_id().c_str(),
373-
lf.front()->get_stat().st_size,
374-
lf.front()->get_filename_as_string().c_str());
375+
log_info(
376+
"Keeping duplicated file: %s; size=%lld; mtime=%d; path=%s",
377+
lf.front()->get_content_id().c_str(),
378+
lf.front()->get_stat().st_size,
379+
lf.front()->get_stat().st_mtime,
380+
lf.front()->get_filename_as_string().c_str());
375381
lf.pop_front();
376382
std::for_each(
377383
lf.begin(), lf.end(), [&dupe_name, &reload](auto& lf) {
378384
if (lf->mark_as_duplicate(dupe_name)) {
379-
log_info(" Hiding copy: size=%lld; path=%s",
380-
lf->get_stat().st_size,
381-
lf->get_filename_as_string().c_str());
385+
log_info(
386+
" Hiding copy: size=%lld; mtime=%d; path=%s",
387+
lf->get_stat().st_size,
388+
lf->get_stat().st_mtime,
389+
lf->get_filename_as_string().c_str());
382390
lnav_data.ld_log_source.find_data(lf) |
383391
[](auto ld) { ld->set_visibility(false); };
384392
reload = true;

src/logfile_sub_source.cc

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,10 +1379,16 @@ logfile_sub_source::text_update_marks(vis_bookmarks& bm)
13791379
bm_files.clear();
13801380

13811381
std::vector<const bookmark_type_t*> used_marks;
1382-
for (const auto& bmt : bookmark_type_t::get_all_types()) {
1383-
if (!this->lss_user_marks[&bmt].empty()) {
1384-
bm[&bmt].clear();
1385-
used_marks.emplace_back(&bmt);
1382+
for (const auto* bmt : {
1383+
&textview_curses::BM_USER,
1384+
&textview_curses::BM_USER_EXPR,
1385+
&textview_curses::BM_PARTITION,
1386+
&textview_curses::BM_META,
1387+
})
1388+
{
1389+
bm[bmt].clear();
1390+
if (!this->lss_user_marks[bmt].empty()) {
1391+
used_marks.emplace_back(bmt);
13861392
}
13871393
}
13881394

@@ -2124,7 +2130,7 @@ logfile_sub_source::text_mark(const bookmark_type_t* bm,
21242130
return;
21252131
}
21262132

2127-
content_line_t cl = this->at(line);
2133+
auto cl = this->at(line);
21282134

21292135
if (bm == &textview_curses::BM_USER) {
21302136
auto* ll = this->find_line(cl);
@@ -2137,8 +2143,6 @@ logfile_sub_source::text_mark(const bookmark_type_t* bm,
21372143
this->lss_user_marks[bm].bv_tree.insert(cl);
21382144
}
21392145
} else if (lb != this->lss_user_marks[bm].bv_tree.end() && *lb == cl) {
2140-
require(lb != this->lss_user_marks[bm].bv_tree.end());
2141-
21422146
this->lss_user_marks[bm].bv_tree.erase(lb);
21432147
}
21442148
if (bm == &textview_curses::BM_META

src/ptimec.cc

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,20 @@
3636
#include <stdlib.h>
3737
#include <string.h>
3838

39-
const char* PRELUDE
40-
= "\
41-
#include <time.h>\n\
42-
#include <sys/types.h>\n\
43-
#include \"ptimec.hh\"\n\
44-
\n\
45-
";
39+
const char* PRELUDE = R"(
40+
#include <time.h>
41+
#include <sys/types.h>
42+
#include "ptimec.hh"
43+
#include "ptimec_spec.hh"
44+
45+
)";
46+
47+
template<std::size_t N>
48+
static bool
49+
startswith(const char* fmt, const char (&pat)[N])
50+
{
51+
return strncmp(fmt, pat, N - 1) == 0;
52+
}
4653

4754
char*
4855
escape_char(char ch)
@@ -121,8 +128,14 @@ main(int argc, char* argv[])
121128
}
122129

123130
auto checked_pos = std::optional<size_t>(0);
124-
for (int index = 0; arg[index]; arg++) {
125-
if (arg[index] == '%') {
131+
for (int index = 0; arg[index]; index++) {
132+
if (startswith(&arg[index], "%Y-%m-%dT%H:%M")) {
133+
printf(
134+
" if (!ptime_YmdTHM(dst, str, off_inout, len)) return "
135+
"false;\n");
136+
index += 13;
137+
checked_pos = std::nullopt;
138+
} else if (arg[index] == '%') {
126139
std::optional<size_t> fixed_width_opt;
127140

128141
if (checked_pos.has_value()) {

src/ptimec_spec.hh

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Copyright (c) 2025, Timothy Stack
3+
*
4+
* All rights reserved.
5+
*
6+
* Redistribution and use in source and binary forms, with or without
7+
* modification, are permitted provided that the following conditions are met:
8+
*
9+
* * Redistributions of source code must retain the above copyright notice, this
10+
* list of conditions and the following disclaimer.
11+
* * Redistributions in binary form must reproduce the above copyright notice,
12+
* this list of conditions and the following disclaimer in the documentation
13+
* and/or other materials provided with the distribution.
14+
* * Neither the name of Timothy Stack nor the names of its contributors
15+
* may be used to endorse or promote products derived from this software
16+
* without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19+
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21+
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
*/
29+
30+
#ifndef lnav_ptime_spec_hh
31+
#define lnav_ptime_spec_hh
32+
33+
#include <sys/types.h>
34+
35+
#include "base/time_util.hh"
36+
37+
inline bool
38+
ptime_YmdTHM(struct exttm* dst, const char* str, off_t& off_inout, ssize_t len)
39+
{
40+
static constexpr auto WIDTH = 16;
41+
42+
if (off_inout + WIDTH > len) {
43+
return false;
44+
}
45+
46+
auto sep_count = size_t{0};
47+
48+
auto Y_hundreds
49+
= (str[off_inout + 0] - '0') * 10 + (str[off_inout + 1] - '0') * 1;
50+
auto Y_ones
51+
= (str[off_inout + 2] - '0') * 10 + (str[off_inout + 3] - '0') * 1;
52+
53+
sep_count += str[off_inout + 4] == '-';
54+
55+
auto m = (str[off_inout + 5] - '0') * 10 + (str[off_inout + 6] - '0') * 1;
56+
57+
sep_count += str[off_inout + 7] == '-';
58+
59+
auto d = (str[off_inout + 8] - '0') * 10 + (str[off_inout + 9] - '0') * 1;
60+
61+
sep_count += str[off_inout + 10] == 'T';
62+
63+
auto H = (str[off_inout + 11] - '0') * 10 + (str[off_inout + 12] - '0') * 1;
64+
65+
sep_count += str[off_inout + 13] == ':';
66+
67+
auto M = (str[off_inout + 14] - '0') * 10 + (str[off_inout + 15] - '0') * 1;
68+
69+
auto Y = (Y_hundreds * 100 + Y_ones) - 1900;
70+
if (sep_count != 4 || Y < 0 || Y > 1100 || m < 1 || m > 12 || d < 0
71+
|| d > 31 || H < 0 || H > 23 || M < 0 || M > 59)
72+
{
73+
return false;
74+
}
75+
76+
dst->et_tm.tm_year = Y;
77+
dst->et_tm.tm_mon = m - 1;
78+
dst->et_tm.tm_mday = d;
79+
dst->et_tm.tm_hour = H;
80+
dst->et_tm.tm_min = M;
81+
82+
dst->et_flags |= ETF_YEAR_SET | ETF_MONTH_SET | ETF_DAY_SET | ETF_HOUR_SET
83+
| ETF_MINUTE_SET;
84+
85+
off_inout += WIDTH;
86+
87+
return true;
88+
}
89+
90+
#endif

src/textview_curses.cc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,15 @@ text_accel_source::get_time_offset_for_line(textview_curses& tc, vis_line_t vl)
182182
}
183183

184184
const DIST_SLICE(bm_types) bookmark_type_t textview_curses::BM_ERRORS("error");
185-
const DIST_SLICE(bm_types) bookmark_type_t textview_curses::BM_WARNINGS("warning");
185+
const DIST_SLICE(bm_types)
186+
bookmark_type_t textview_curses::BM_WARNINGS("warning");
186187
const DIST_SLICE(bm_types) bookmark_type_t textview_curses::BM_USER("user");
187-
const DIST_SLICE(bm_types) bookmark_type_t textview_curses::BM_USER_EXPR("user-expr");
188+
const DIST_SLICE(bm_types)
189+
bookmark_type_t textview_curses::BM_USER_EXPR("user-expr");
188190
const DIST_SLICE(bm_types) bookmark_type_t textview_curses::BM_SEARCH("search");
189191
const DIST_SLICE(bm_types) bookmark_type_t textview_curses::BM_META("meta");
190-
const DIST_SLICE(bm_types) bookmark_type_t textview_curses::BM_PARTITION("partition");
192+
const DIST_SLICE(bm_types)
193+
bookmark_type_t textview_curses::BM_PARTITION("partition");
191194

192195
textview_curses::textview_curses()
193196
: lnav_config_listener(__FILE__), tc_search_action(noop_func{})

src/view_helpers.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,9 @@ set_view_mode(ln_mode_t mode)
16431643
case ln_mode_t::FILES:
16441644
case ln_mode_t::FILTER:
16451645
case ln_mode_t::SPECTRO_DETAILS: {
1646+
if (!lnav_data.ld_filter_view.get_selection()) {
1647+
lnav_data.ld_filter_view.set_selection(0_vl);
1648+
}
16461649
lnav_data.ld_files_source.text_selection_changed(
16471650
lnav_data.ld_files_view);
16481651
breadcrumb_view->set_enabled(false);

test/test_date_time_scanner.cc

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,34 @@ TEST_CASE("date_time_scanner")
7373
setenv("TZ", "UTC", 1);
7474

7575
lnav_config.lc_log_date_time.c_zoned_to_local = false;
76+
77+
{
78+
const auto sf = string_fragment::from_const("2022-03-02T10:20:30+");
79+
timeval tv;
80+
exttm tm;
81+
date_time_scanner dts;
82+
const auto* rc = dts.scan(sf.data(), sf.length(), nullptr, &tm, tv);
83+
auto matched_size = rc - sf.data();
84+
auto rem = sf.substr(matched_size);
85+
CHECK(rem == "+");
86+
}
87+
88+
{
89+
const auto sf = string_fragment::from_const("2025-04-24T19:51:48.55604564Z");
90+
timeval tv;
91+
exttm tm;
92+
date_time_scanner dts;
93+
const auto* rc = dts.scan(sf.data(), sf.length(), nullptr, &tm, tv);
94+
CHECK(rc != nullptr);
95+
printf("fmt %s\n", PTIMEC_FORMAT_STR[dts.dts_fmt_lock]);
96+
CHECK((tm.et_flags & ETF_NANOS_SET));
97+
98+
char ts[64];
99+
dts.ftime(ts, sizeof(ts), nullptr, tm);
100+
101+
CHECK(std::string(ts) == std::string("2025-04-24T19:51:48.556045640Z"));
102+
}
103+
76104
for (const auto* good_time : GOOD_TIMES) {
77105
date_time_scanner dts;
78106
timeval tv;
@@ -109,21 +137,6 @@ TEST_CASE("date_time_scanner")
109137
CHECK(std::string(ts) == std::string("2014-02-11 16:12:34.123"));
110138
}
111139

112-
{
113-
const auto sf = string_fragment::from_const("2025-04-24T19:51:48.55604564Z");
114-
timeval tv;
115-
exttm tm;
116-
date_time_scanner dts;
117-
const auto* rc = dts.scan(sf.data(), sf.length(), nullptr, &tm, tv);
118-
printf("fmt %s\n", PTIMEC_FORMAT_STR[dts.dts_fmt_lock]);
119-
CHECK((tm.et_flags & ETF_NANOS_SET));
120-
121-
char ts[64];
122-
dts.ftime(ts, sizeof(ts), nullptr, tm);
123-
124-
CHECK(std::string(ts) == std::string("2025-04-24T19:51:48.556045640Z"));
125-
}
126-
127140
{
128141
const auto sf
129142
= string_fragment::from_const("2014-02-11 16:12:34.12345Z");

0 commit comments

Comments
 (0)