diff --git a/src/db_sub_source.cc b/src/db_sub_source.cc index 99392347..a4f37c27 100644 --- a/src/db_sub_source.cc +++ b/src/db_sub_source.cc @@ -261,7 +261,7 @@ long db_label_source::column_name_to_index(const std::string &name) const return std::distance(this->dls_headers.begin(), iter); } -int db_label_source::row_for_time(struct timeval time_bucket) +nonstd::optional db_label_source::row_for_time(struct timeval time_bucket) { std::vector::iterator iter; @@ -269,9 +269,9 @@ int db_label_source::row_for_time(struct timeval time_bucket) this->dls_time_column.end(), time_bucket); if (iter != this->dls_time_column.end()) { - return std::distance(this->dls_time_column.begin(), iter); + return vis_line_t(std::distance(this->dls_time_column.begin(), iter)); } - return -1; + return nonstd::nullopt; } size_t db_overlay_source::list_overlay_count(const listview_curses &lv) diff --git a/src/db_sub_source.hh b/src/db_sub_source.hh index 3351aba1..df5a8336 100644 --- a/src/db_sub_source.hh +++ b/src/db_sub_source.hh @@ -81,11 +81,11 @@ public: long column_name_to_index(const std::string &name) const; - int row_for_time(struct timeval time_bucket); + nonstd::optional row_for_time(struct timeval time_bucket); - struct timeval time_for_row(int row) { - if ((row < 0) || (((size_t) row) >= this->dls_time_column.size())) { - return { -1, 0 }; + nonstd::optional time_for_row(vis_line_t row) { + if ((row < 0_vl) || (((size_t) row) >= this->dls_time_column.size())) { + return nonstd::nullopt; } return this->dls_time_column[row]; diff --git a/src/field_overlay_source.cc b/src/field_overlay_source.cc index ef303099..d1960c3f 100644 --- a/src/field_overlay_source.cc +++ b/src/field_overlay_source.cc @@ -85,18 +85,18 @@ void field_overlay_source::build_summary_lines(const listview_curses &lv) time_t five_minutes_ago = local_now - (5 * 60 * 60); time_t ten_secs_ago = local_now - 10; - vis_line_t from_five_min_ago = lss.find_from_time(five_minutes_ago); - vis_line_t from_ten_secs_ago = lss.find_from_time(ten_secs_ago); + auto from_five_min_ago_opt = lss.find_from_time(five_minutes_ago); + auto from_ten_secs_ago_opt = lss.find_from_time(ten_secs_ago); auto &bm = tc.get_bookmarks(); auto error_bm_iter = bm.find(&logfile_sub_source::BM_ERRORS); - if (now > last_line->get_time() && from_five_min_ago != -1 && + if (now > last_line->get_time() && from_five_min_ago_opt && error_bm_iter != bm.end()) { auto& error_bookmarks = error_bm_iter->second; auto five_min_lower = lower_bound(error_bookmarks.begin(), error_bookmarks.end(), - from_five_min_ago); + from_five_min_ago_opt.value()); if (five_min_lower != error_bookmarks.end()) { double error_count = distance( five_min_lower, error_bookmarks.end()); @@ -108,11 +108,11 @@ void field_overlay_source::build_summary_lines(const listview_curses &lv) } error_rate = error_count / time_diff; - if (from_ten_secs_ago != -1) { + if (from_ten_secs_ago_opt) { auto ten_sec_lower = lower_bound(error_bookmarks.begin(), error_bookmarks.end(), - from_ten_secs_ago); + from_ten_secs_ago_opt.value()); if (ten_sec_lower != error_bookmarks.end()) { double recent_error_count = distance( ten_sec_lower, error_bookmarks.end()); diff --git a/src/files_sub_source.cc b/src/files_sub_source.cc index 1cd1dddc..939f0bf4 100644 --- a/src/files_sub_source.cc +++ b/src/files_sub_source.cc @@ -99,9 +99,9 @@ bool files_sub_source::list_input_handle_key(listview_curses &lv, int ch) if (lf->get_format() != nullptr) { auto& log_view = lnav_data.ld_views[LNV_LOG]; - auto row = lss.row_for_time(lf->front().get_timeval()); - - log_view.set_top(vis_line_t(row)); + lss.row_for_time(lf->front().get_timeval()) | [](auto row) { + log_view.set_top(row); + }; ensure_view(&log_view); } else { auto& tv = lnav_data.ld_views[LNV_TEXT]; diff --git a/src/hist_source.cc b/src/hist_source.cc index 73ceb6e5..231fa301 100644 --- a/src/hist_source.cc +++ b/src/hist_source.cc @@ -36,7 +36,7 @@ using namespace std; const char *hist_source2::LINE_FORMAT = " %8d normal %8d errors %8d warnings %8d marks"; -int hist_source2::row_for_time(struct timeval tv_bucket) +nonstd::optional hist_source2::row_for_time(struct timeval tv_bucket) { std::map::iterator iter; int retval = 0; @@ -57,11 +57,11 @@ int hist_source2::row_for_time(struct timeval tv_bucket) for (unsigned int lpc = 0; lpc <= bb.bb_used; lpc++, retval++) { if (time_bucket <= bb.bb_buckets[lpc].b_time) { - return retval; + return vis_line_t(retval); } } } - return retval; + return vis_line_t(retval); } void hist_source2::text_value_for_line(textview_curses &tc, int row, diff --git a/src/hist_source.hh b/src/hist_source.hh index fc6079ab..cbcb573b 100644 --- a/src/hist_source.hh +++ b/src/hist_source.hh @@ -396,16 +396,17 @@ public: return 0; }; - struct timeval time_for_row(int row) { - require(row >= 0); - require(row < this->hs_line_count); + nonstd::optional time_for_row(vis_line_t row) { + if (row < 0 || row > this->hs_line_count) { + return nonstd::nullopt; + } bucket_t &bucket = this->find_bucket(row); - return { bucket.b_time, 0 }; + return timeval{ bucket.b_time, 0 }; }; - int row_for_time(struct timeval tv_bucket); + nonstd::optional row_for_time(struct timeval tv_bucket); private: static const char *LINE_FORMAT; diff --git a/src/hotkeys.cc b/src/hotkeys.cc index 2aeb4302..0f7cc710 100644 --- a/src/hotkeys.cc +++ b/src/hotkeys.cc @@ -228,9 +228,11 @@ bool handle_paging_key(int ch) lnav_data.ld_last_view = nullptr; if (src_view != nullptr && dst_view != nullptr) { - struct timeval top_time = src_view->time_for_row(top_tc->get_top()); - - tc->set_top(vis_line_t(dst_view->row_for_time(top_time))); + src_view->time_for_row(top_tc->get_top()) | [dst_view, tc](auto top_time) { + dst_view->row_for_time(top_time) | [tc](auto row) { + tc->set_top(row); + }; + }; } ensure_view(tc); } @@ -516,23 +518,26 @@ bool handle_paging_key(int ch) case '0': if (lss) { - struct timeval first_time = lss->time_for_row(tc->get_top()); - int step = 24 * 60 * 60; - vis_line_t line = - lss->find_from_time(roundup_size(first_time.tv_sec, step)); - - tc->set_top(line); + const int step = 24 * 60 * 60; + lss->time_for_row(tc->get_top()) | [lss, tc](auto first_time) { + lss->find_from_time(roundup_size(first_time.tv_sec, step)) | [tc](auto line) { + tc->set_top(line); + }; + }; } break; case ')': if (lss) { - struct timeval first_time = lss->time_for_row(tc->get_top()); - time_t day = rounddown(first_time.tv_sec, 24 * 60 * 60); - vis_line_t line = lss->find_from_time(day); - - --line; - tc->set_top(line); + lss->time_for_row(tc->get_top()) | [lss, tc](auto first_time) { + time_t day = rounddown(first_time.tv_sec, 24 * 60 * 60); + lss->find_from_time(day) | [tc](auto line) { + if (line != 0_vl) { + --line; + } + tc->set_top(line); + }; + }; } break; @@ -541,15 +546,16 @@ bool handle_paging_key(int ch) alerter::singleton().chime(); } else if (lss) { - struct timeval first_time = lss->time_for_row(tc->get_top()); - int step = ch == 'D' ? (24 * 60 * 60) : (60 * 60); - time_t top_time = first_time.tv_sec; - vis_line_t line = lss->find_from_time(top_time - step); - - if (line != 0) { - --line; - } - tc->set_top(line); + lss->time_for_row(tc->get_top()) | [lss, ch, tc](auto first_time) { + int step = ch == 'D' ? (24 * 60 * 60) : (60 * 60); + time_t top_time = first_time.tv_sec; + lss->find_from_time(top_time - step) | [tc](auto line) { + if (line != 0_vl) { + --line; + } + tc->set_top(line); + }; + }; lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(/, "to search")); } @@ -557,12 +563,12 @@ bool handle_paging_key(int ch) case 'd': if (lss) { - struct timeval first_time = lss->time_for_row(tc->get_top()); - int step = ch == 'd' ? (24 * 60 * 60) : (60 * 60); - vis_line_t line = - lss->find_from_time(first_time.tv_sec + step); - - tc->set_top(line); + lss->time_for_row(tc->get_top()) | [ch, lss, tc](auto first_time) { + int step = ch == 'd' ? (24 * 60 * 60) : (60 * 60); + lss->find_from_time(first_time.tv_sec + step) | [tc](auto line) { + tc->set_top(line); + }; + }; lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(/, "to search")); } @@ -682,9 +688,11 @@ bool handle_paging_key(int ch) auto *src_view = dynamic_cast(tc->get_sub_source()); if (src_view != nullptr) { - struct timeval log_top = src_view->time_for_row(tc->get_top()); - - hist_tc.set_top(vis_line_t(hs.row_for_time(log_top))); + src_view->time_for_row(tc->get_top()) | [&hs, &hist_tc](auto log_top) { + hs.row_for_time(log_top) | [&hist_tc](auto row) { + hist_tc.set_top(row); + }; + }; } } else { @@ -692,12 +700,15 @@ bool handle_paging_key(int ch) auto *dst_view = dynamic_cast(top_tc->get_sub_source()); if (dst_view != nullptr) { - struct timeval hist_top_time = hs.time_for_row(hist_tc.get_top()); - struct timeval curr_top_time = dst_view->time_for_row(top_tc->get_top()); - if (hs.row_for_time(hist_top_time) != hs.row_for_time(curr_top_time)) { - vis_line_t new_top = vis_line_t(dst_view->row_for_time(hist_top_time)); - top_tc->set_top(new_top); - top_tc->set_needs_update(); + auto hist_top_time_opt = hs.time_for_row(hist_tc.get_top()); + auto curr_top_time_opt = dst_view->time_for_row(top_tc->get_top()); + if (hist_top_time_opt && curr_top_time_opt && + hs.row_for_time(hist_top_time_opt.value()) != + hs.row_for_time(curr_top_time_opt.value())) { + dst_view->row_for_time(hist_top_time_opt.value()) | [top_tc](auto new_top) { + top_tc->set_top(new_top); + top_tc->set_needs_update(); + }; } } }; @@ -764,11 +775,10 @@ bool handle_paging_key(int ch) size_t col_len = strlen(col_value); if (dts.scan(col_value, col_len, nullptr, &tm, tv) != nullptr) { - vis_line_t vl; - - vl = lnav_data.ld_log_source.find_from_time(tv); - tc->set_top(vl); - tc->set_needs_update(); + lnav_data.ld_log_source.find_from_time(tv) | [tc](auto vl) { + tc->set_top(vl); + tc->set_needs_update(); + }; break; } } @@ -879,7 +889,11 @@ bool handle_paging_key(int ch) ll->to_exttm(tm); do { tm = rt.adjust(tm); - new_vl = lnav_data.ld_log_source.find_from_time(tm); + auto new_vl_opt = lnav_data.ld_log_source.find_from_time(tm); + if (!new_vl_opt) { + break; + } + new_vl = new_vl_opt.value(); if (new_vl == 0_vl || new_vl != vl || !rt.is_relative()) { vl = new_vl; diff --git a/src/listview_curses.hh b/src/listview_curses.hh index 5266ac11..4e70f774 100644 --- a/src/listview_curses.hh +++ b/src/listview_curses.hh @@ -326,6 +326,14 @@ public: /** @return The line number that is displayed at the top. */ vis_line_t get_top() const { return this->lv_top; }; + nonstd::optional get_top_opt() const { + if (this->get_inner_height() == 0_vl) { + return nonstd::nullopt; + } + + return this->lv_top; + } + /** @return The line number that is displayed at the bottom. */ vis_line_t get_bottom() const { diff --git a/src/lnav_commands.cc b/src/lnav_commands.cc index d85c8b9b..efe5a374 100644 --- a/src/lnav_commands.cc +++ b/src/lnav_commands.cc @@ -348,47 +348,58 @@ static Result com_goto(exec_context &ec, string cmdline, vector< auto parse_res = relative_time::from_str(all_args); if (parse_res.isOk()) { - if (ttt != nullptr) { - struct timeval tv = ttt->time_for_row(tc->get_top()); - vis_line_t vl = tc->get_top(), new_vl; - bool done = false; - auto rt = parse_res.unwrap(); - - if (rt.is_relative()) { - injector::get() = rt; - } + if (ttt == nullptr) { + return ec.make_error("relative time values only work in a time-indexed view"); + } + if (tc->get_inner_height() == 0_vl) { + return ec.make_error("view is empty"); + } + auto tv_opt = ttt->time_for_row(tc->get_top()); + if (!tv_opt) { + return ec.make_error("cannot get time for the top row"); + } + tv = tv_opt.value(); - do { - auto tm = rt.adjust(tv); + vis_line_t vl = tc->get_top(), new_vl; + bool done = false; + auto rt = parse_res.unwrap(); - tv = tm.to_timeval(); - new_vl = vis_line_t(ttt->row_for_time(tv)); + if (rt.is_relative()) { + injector::get() = rt; + } - if (new_vl == 0_vl || new_vl != vl || !rt.is_relative()) { - vl = new_vl; - done = true; - } - } while (!done); + do { + auto tm = rt.adjust(tv); - dst_vl = vl; + tv = tm.to_timeval(); + auto new_vl_opt = ttt->row_for_time(tv); + if (!new_vl_opt) { + break; + } - if (!ec.ec_dry_run && !rt.is_absolute() && lnav_data.ld_rl_view != nullptr) { - lnav_data.ld_rl_view->set_alt_value( - HELP_MSG_2(r, R, - "to move forward/backward the same amount of time")); + new_vl = new_vl_opt.value(); + if (new_vl == 0_vl || new_vl != vl || !rt.is_relative()) { + vl = new_vl; + done = true; } - } else { - return ec.make_error("relative time values only work in a time-indexed view"); + } while (!done); + + dst_vl = vl; + + if (!ec.ec_dry_run && !rt.is_absolute() && lnav_data.ld_rl_view != nullptr) { + lnav_data.ld_rl_view->set_alt_value( + HELP_MSG_2(r, R, + "to move forward/backward the same amount of time")); } } else if (dts.scan(args[1].c_str(), args[1].size(), nullptr, &tm, tv) != nullptr) { - if (ttt != nullptr) { - dst_vl = vis_line_t(ttt->row_for_time(tv)); - } - else { - return ec.make_error("time values only work in a time-indexed view"); + if (ttt == nullptr) { + return ec.make_error( + "time values only work in a time-indexed view"); } + + dst_vl = ttt->row_for_time(tv); } else if (sscanf(args[1].c_str(), "%f%n", &value, &consumed) == 1) { if (args[1][consumed] == '%') { @@ -3342,24 +3353,30 @@ static Result com_zoom_to(exec_context &ec, string cmdline, vect textview_curses &hist_view = lnav_data.ld_views[LNV_HISTOGRAM]; if (hist_view.get_inner_height() > 0) { - old_time = lnav_data.ld_hist_source2.time_for_row( + auto old_time_opt = lnav_data.ld_hist_source2.time_for_row( lnav_data.ld_views[LNV_HISTOGRAM].get_top()); - rebuild_hist(); - lnav_data.ld_views[LNV_HISTOGRAM].set_top( - vis_line_t( - lnav_data.ld_hist_source2.row_for_time(old_time))); + if (old_time_opt) { + old_time = old_time_opt.value(); + rebuild_hist(); + lnav_data.ld_hist_source2.row_for_time(old_time) | [](auto new_top) { + lnav_data.ld_views[LNV_HISTOGRAM].set_top(new_top); + }; + } } textview_curses &spectro_view = lnav_data.ld_views[LNV_SPECTRO]; if (spectro_view.get_inner_height() > 0) { - old_time = lnav_data.ld_spectro_source.time_for_row( + auto old_time_opt = lnav_data.ld_spectro_source.time_for_row( lnav_data.ld_views[LNV_SPECTRO].get_top()); ss.ss_granularity = ZOOM_LEVELS[lnav_data.ld_zoom_level]; ss.invalidate(); - lnav_data.ld_views[LNV_SPECTRO].set_top( - vis_line_t(lnav_data.ld_spectro_source.row_for_time( - old_time))); + if (old_time_opt) { + lnav_data.ld_spectro_source.row_for_time(old_time_opt.value()) | + [](auto new_top) { + lnav_data.ld_views[LNV_SPECTRO].set_top(new_top); + }; + } } lnav_data.ld_view_stack.set_needs_update(); @@ -4135,17 +4152,11 @@ public: void spectro_row(spectrogram_request &sr, spectrogram_row &row_out) { logfile_sub_source &lss = lnav_data.ld_log_source; - vis_line_t begin_line = lss.find_from_time(sr.sr_begin_time); - vis_line_t end_line = lss.find_from_time(sr.sr_end_time); + vis_line_t begin_line = lss.find_from_time(sr.sr_begin_time).value_or(0_vl); + vis_line_t end_line = lss.find_from_time(sr.sr_end_time).value_or(lss.text_line_count()); vector values; string_attrs_t sa; - if (begin_line == -1) { - begin_line = 0_vl; - } - if (end_line == -1) { - end_line = vis_line_t(lss.text_line_count()); - } for (vis_line_t curr_line = begin_line; curr_line < end_line; ++curr_line) { content_line_t cl = lss.at(curr_line); std::shared_ptr lf = lss.find(cl); @@ -4188,17 +4199,11 @@ public: // XXX need to refactor this and the above method textview_curses &log_tc = lnav_data.ld_views[LNV_LOG]; logfile_sub_source &lss = lnav_data.ld_log_source; - vis_line_t begin_line = lss.find_from_time(begin_time); - vis_line_t end_line = lss.find_from_time(end_time); + vis_line_t begin_line = lss.find_from_time(begin_time).value_or(0_vl); + vis_line_t end_line = lss.find_from_time(end_time).value_or(lss.text_line_count()); vector values; string_attrs_t sa; - if (begin_line == -1) { - begin_line = 0_vl; - } - if (end_line == -1) { - end_line = vis_line_t(lss.text_line_count()); - } for (vis_line_t curr_line = begin_line; curr_line < end_line; ++curr_line) { content_line_t cl = lss.at(curr_line); std::shared_ptr lf = lss.find(cl); @@ -4317,17 +4322,10 @@ public: void spectro_row(spectrogram_request &sr, spectrogram_row &row_out) { db_label_source &dls = lnav_data.ld_db_row_source; - int begin_row = dls.row_for_time({ sr.sr_begin_time, 0 }); - int end_row = dls.row_for_time({ sr.sr_end_time, 0 }); - - if (begin_row == -1) { - begin_row = 0; - } - if (end_row == -1) { - end_row = dls.dls_rows.size(); - } + auto begin_row = dls.row_for_time({ sr.sr_begin_time, 0 }).value_or(0_vl); + auto end_row = dls.row_for_time({ sr.sr_end_time, 0 }).value_or(dls.dls_rows.size()); - for (int lpc = begin_row; lpc < end_row; lpc++) { + for (auto lpc = begin_row; lpc < end_row; ++lpc) { double value = 0.0; sscanf(dls.dls_rows[lpc][this->dsvs_column_index], "%lf", &value); diff --git a/src/log_vtab_impl.cc b/src/log_vtab_impl.cc index 641a29fa..ca02228a 100644 --- a/src/log_vtab_impl.cc +++ b/src/log_vtab_impl.cc @@ -792,14 +792,14 @@ static int vt_filter(sqlite3_vtab_cursor *p_vtc, date_time_scanner dts; struct timeval tv; struct exttm mytm; - vis_line_t vl; dts.scan((const char *)datestr, strlen((const char *)datestr), NULL, &mytm, tv); - if ((vl = vt->lss->find_from_time(tv)) == -1) { + auto vl_opt = vt->lss->find_from_time(tv); + if (!vl_opt) { p_cur->log_cursor.lc_curr_line = p_cur->log_cursor.lc_end_line; } else { - p_cur->log_cursor.update(index[lpc].op, vl, false); + p_cur->log_cursor.update(index[lpc].op, vl_opt.value(), false); } } break; diff --git a/src/logfile_sub_source.cc b/src/logfile_sub_source.cc index 75bb2f5f..cdc80e5d 100644 --- a/src/logfile_sub_source.cc +++ b/src/logfile_sub_source.cc @@ -137,19 +137,17 @@ shared_ptr logfile_sub_source::find(const char *fn, return retval; } -vis_line_t logfile_sub_source::find_from_time(const struct timeval &start) const +nonstd::optional logfile_sub_source::find_from_time(const struct timeval &start) const { - vis_line_t retval(-1); - auto lb = lower_bound(this->lss_filtered_index.begin(), this->lss_filtered_index.end(), start, filtered_logline_cmp(*this)); if (lb != this->lss_filtered_index.end()) { - retval = vis_line_t(lb - this->lss_filtered_index.begin()); + return vis_line_t(lb - this->lss_filtered_index.begin()); } - return retval; + return nonstd::nullopt; } void logfile_sub_source::text_value_for_line(textview_curses &tc, diff --git a/src/logfile_sub_source.hh b/src/logfile_sub_source.hh index 29783446..c2ce16bc 100644 --- a/src/logfile_sub_source.hh +++ b/src/logfile_sub_source.hh @@ -528,15 +528,15 @@ public: return retval; }; - vis_line_t find_from_time(const struct timeval &start) const; + nonstd::optional find_from_time(const struct timeval &start) const; - vis_line_t find_from_time(time_t start) { + nonstd::optional find_from_time(time_t start) const { struct timeval tv = { start, 0 }; return this->find_from_time(tv); }; - vis_line_t find_from_time(const exttm &etm) const { + nonstd::optional find_from_time(const exttm &etm) const { return this->find_from_time(etm.to_timeval()); }; @@ -547,7 +547,13 @@ public: if (lf != nullptr) { auto ll_iter = lf->begin() + line; auto &ll = *ll_iter; - vis_line_t vis_start = this->find_from_time(ll.get_timeval()); + auto vis_start_opt = this->find_from_time(ll.get_timeval()); + + if (!vis_start_opt) { + return nonstd::nullopt; + } + + auto vis_start = *vis_start_opt; while (vis_start < vis_line_t(this->text_line_count())) { content_line_t guess_cl = this->at(vis_start); @@ -569,11 +575,14 @@ public: return nonstd::nullopt; } - struct timeval time_for_row(int row) { - return this->find_line(this->at(vis_line_t(row)))->get_timeval(); + nonstd::optional time_for_row(vis_line_t row) { + if (row < this->text_line_count()) { + return this->find_line(this->at(row))->get_timeval(); + } + return nonstd::nullopt; }; - int row_for_time(struct timeval time_bucket) { + nonstd::optional row_for_time(struct timeval time_bucket) { return this->find_from_time(time_bucket); }; diff --git a/src/spectro_source.cc b/src/spectro_source.cc index 084f3bb7..00a634cd 100644 --- a/src/spectro_source.cc +++ b/src/spectro_source.cc @@ -52,7 +52,11 @@ bool spectrogram_source::list_input_handle_key(listview_curses &lv, int ch) lv.get_dimensions(height, width); spectrogram_bounds &sb = this->ss_cached_bounds; - struct timeval begin_time = this->time_for_row(this->ss_cursor_top); + auto begin_time_opt = this->time_for_row(this->ss_cursor_top); + if (!begin_time_opt) { + return true; + } + auto begin_time = begin_time_opt.value(); struct timeval end_time = begin_time; end_time.tv_sec += this->ss_granularity; @@ -207,7 +211,7 @@ size_t spectrogram_source::text_line_width(textview_curses &tc) return width; } -struct timeval spectrogram_source::time_for_row(int row) +nonstd::optional spectrogram_source::time_for_row(vis_line_t row) { struct timeval retval { 0, 0 }; @@ -219,10 +223,10 @@ struct timeval spectrogram_source::time_for_row(int row) return retval; } -int spectrogram_source::row_for_time(struct timeval time_bucket) +nonstd::optional spectrogram_source::row_for_time(struct timeval time_bucket) { if (this->ss_value_source == nullptr) { - return 0; + return nonstd::nullopt; } time_t diff; @@ -230,13 +234,13 @@ int spectrogram_source::row_for_time(struct timeval time_bucket) this->cache_bounds(); if (time_bucket.tv_sec < this->ss_cached_bounds.sb_begin_time) { - return 0; + return 0_vl; } diff = time_bucket.tv_sec - this->ss_cached_bounds.sb_begin_time; retval = diff / this->ss_granularity; - return retval; + return vis_line_t(retval); } void spectrogram_source::text_value_for_line(textview_curses &tc, int row, @@ -245,11 +249,15 @@ void spectrogram_source::text_value_for_line(textview_curses &tc, int row, { spectrogram_row &s_row = this->load_row(tc, row); - struct timeval row_time; char tm_buffer[128]; struct tm tm; - row_time = this->time_for_row(row); + auto row_time_opt = this->time_for_row(vis_line_t(row)); + if (!row_time_opt) { + value_out.clear(); + return; + } + auto row_time = row_time_opt.value(); gmtime_r(&row_time.tv_sec, &tm); strftime(tm_buffer, sizeof(tm_buffer), " %a %b %d %H:%M:%S", &tm); diff --git a/src/spectro_source.hh b/src/spectro_source.hh index 5df2330f..e403e805 100644 --- a/src/spectro_source.hh +++ b/src/spectro_source.hh @@ -133,9 +133,9 @@ public: return 0; }; - struct timeval time_for_row(int row) override; + nonstd::optional time_for_row(vis_line_t row) override; - int row_for_time(struct timeval time_bucket) override; + nonstd::optional row_for_time(struct timeval time_bucket) override; void text_value_for_line(textview_curses &tc, int row, diff --git a/src/state-extension-functions.cc b/src/state-extension-functions.cc index e7cb6272..70c9d59b 100644 --- a/src/state-extension-functions.cc +++ b/src/state-extension-functions.cc @@ -59,9 +59,14 @@ static nonstd::optional sql_log_top_datetime() return nonstd::nullopt; } + auto top_time = lnav_data.ld_log_source.time_for_row(lnav_data.ld_views[LNV_LOG].get_top()); + if (!top_time) { + return nonstd::nullopt; + } + char buffer[64]; - sql_strftime(buffer, sizeof(buffer), lnav_data.ld_log_source.time_for_row(lnav_data.ld_views[LNV_LOG].get_top())); + sql_strftime(buffer, sizeof(buffer), top_time.value()); return buffer; } diff --git a/src/textview_curses.cc b/src/textview_curses.cc index 5dba9218..8b2c34e8 100644 --- a/src/textview_curses.cc +++ b/src/textview_curses.cc @@ -730,25 +730,27 @@ textview_curses::toggle_user_mark(bookmark_type_t *bm, vis_line_t start_line, void text_time_translator::scroll_invoked(textview_curses *tc) { if (tc->get_inner_height() > 0) { - this->ttt_top_time = this->time_for_row((int) tc->get_top()); + this->time_for_row(tc->get_top()) | [this](auto new_top_time) { + this->ttt_top_time = new_top_time; + }; } } void text_time_translator::data_reloaded(textview_curses *tc) { if (tc->get_inner_height() > 0) { - struct timeval top_time = this->time_for_row((int) tc->get_top()); - - if (top_time != this->ttt_top_time) { - if (this->ttt_top_time.tv_sec != 0) { - vis_line_t new_top(this->row_for_time(this->ttt_top_time)); - - if (new_top >= 0) { - tc->set_top(new_top); + this->time_for_row(tc->get_top()) | [this, tc](auto top_time) { + if (top_time != this->ttt_top_time) { + if (this->ttt_top_time.tv_sec != 0) { + this->row_for_time(this->ttt_top_time) | [tc](auto new_top) { + tc->set_top(new_top); + }; } + this->time_for_row(tc->get_top()) | [this](auto new_top_time) { + this->ttt_top_time = new_top_time; + }; } - this->ttt_top_time = this->time_for_row((int) tc->get_top()); - } + }; } } diff --git a/src/textview_curses.hh b/src/textview_curses.hh index 4ae5205b..39c5adb0 100644 --- a/src/textview_curses.hh +++ b/src/textview_curses.hh @@ -360,9 +360,9 @@ class text_time_translator { public: virtual ~text_time_translator() = default; - virtual int row_for_time(struct timeval time_bucket) = 0; + virtual nonstd::optional row_for_time(struct timeval time_bucket) = 0; - virtual struct timeval time_for_row(int row) = 0; + virtual nonstd::optional time_for_row(vis_line_t row) = 0; void scroll_invoked(textview_curses *tc); diff --git a/src/views_vtab.cc b/src/views_vtab.cc index a343a4dd..3701fa73 100644 --- a/src/views_vtab.cc +++ b/src/views_vtab.cc @@ -156,11 +156,16 @@ CREATE TABLE lnav_views ( auto *time_source = dynamic_cast(tc.get_sub_source()); if (time_source != nullptr && tc.get_inner_height() > 0) { - char timestamp[64]; + auto top_time_opt = time_source->time_for_row(tc.get_top()); - sql_strftime(timestamp, sizeof(timestamp), time_source->time_for_row(tc.get_top()), 'T'); + if (top_time_opt) { + char timestamp[64]; - sqlite3_result_text(ctx, timestamp, -1, SQLITE_TRANSIENT); + sql_strftime(timestamp, sizeof(timestamp), top_time_opt.value(), 'T'); + sqlite3_result_text(ctx, timestamp, -1, SQLITE_TRANSIENT); + } else { + sqlite3_result_null(ctx); + } } else { sqlite3_result_null(ctx); } @@ -231,12 +236,15 @@ CREATE TABLE lnav_views ( struct timeval tv; if (dts.convert_to_timeval(top_time, -1, nullptr, tv)) { - struct timeval last_time = time_source->time_for_row(tc.get_top()); - - if (tv != last_time) { - int row = time_source->row_for_time(tv); - - tc.set_top(vis_line_t(row)); + auto last_time_opt = time_source->time_for_row(tc.get_top()); + + if (last_time_opt) { + auto last_time = last_time_opt.value(); + if (tv != last_time) { + time_source->row_for_time(tv) | [&tc](auto row) { + tc.set_top(row); + }; + } } } else { tab->zErrMsg = sqlite3_mprintf("Invalid time: %s", top_time); diff --git a/test/Makefile.am b/test/Makefile.am index 5a52f613..32707edf 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -367,7 +367,6 @@ TESTS = \ test_log_accel \ test_logfile.sh \ test_reltime \ - test_remote.sh \ test_scripts.sh \ test_sessions.sh \ test_shlexer.sh \ @@ -386,6 +385,7 @@ TESTS = \ test_vt52_curses.sh DISABLED_TESTS = \ + test_remote.sh \ test_top_status \ test_line_buffer2 \ test_line_buffer.sh