mirror of
https://github.com/tstack/lnav.git
synced 2024-09-11 13:05:51 +03:00
[keymap] add more keys to the keymap
This commit is contained in:
parent
c3097db7af
commit
00ed12557b
5
NEWS
5
NEWS
@ -22,6 +22,9 @@ lnav v0.8.4:
|
||||
that hide/show lines based on whether they are bookmarked.
|
||||
* Added the "json_contains()" SQL function to check if a JSON value
|
||||
contains a number of a string.
|
||||
* The relative time parser recognizes "next" at the beginning of the
|
||||
input, for example, "next hour" or "next day". Handy for use in the
|
||||
":goto" command.
|
||||
|
||||
Interface Changes:
|
||||
* When typing in a search, instead of moving the view to the first match
|
||||
@ -37,6 +40,8 @@ lnav v0.8.4:
|
||||
to the beginning of the line.
|
||||
* The :delete-filter command should now tab-complete existing filters.
|
||||
* Milliseconds can now be used in relative times (e.g. 10:00:00.123)
|
||||
* The J/K hotkeys were not marking lines correctly when the bottom of
|
||||
the view was reached.
|
||||
|
||||
lnav v0.8.3:
|
||||
Features:
|
||||
|
@ -547,7 +547,7 @@ string execute_from_file(exec_context &ec, const string &path, int line_number,
|
||||
}
|
||||
break;
|
||||
case ';':
|
||||
setup_logline_table();
|
||||
setup_logline_table(ec);
|
||||
retval = execute_sql(ec, cmdline, alt_msg);
|
||||
break;
|
||||
case '|':
|
||||
@ -589,7 +589,7 @@ string execute_any(exec_context &ec, const string &cmdline_with_mode)
|
||||
}
|
||||
break;
|
||||
case ';':
|
||||
setup_logline_table();
|
||||
setup_logline_table(ec);
|
||||
retval = execute_sql(ec, cmdline, alt_msg);
|
||||
break;
|
||||
case '|': {
|
||||
@ -636,7 +636,7 @@ void execute_init_commands(exec_context &ec, vector<pair<string, string> > &msgs
|
||||
}
|
||||
break;
|
||||
case ';':
|
||||
setup_logline_table();
|
||||
setup_logline_table(ec);
|
||||
msg = execute_sql(ec, cmd.substr(1), alt_msg);
|
||||
break;
|
||||
case '|':
|
||||
|
4628
src/doctest.hh
4628
src/doctest.hh
File diff suppressed because it is too large
Load Diff
142
src/hotkeys.cc
142
src/hotkeys.cc
@ -140,25 +140,6 @@ static void copy_to_xclip(void)
|
||||
lnav_data.ld_rl_view->set_value(buffer);
|
||||
}
|
||||
|
||||
static void back_ten(int ten_minute)
|
||||
{
|
||||
textview_curses * tc = lnav_data.ld_view_stack.back();
|
||||
logfile_sub_source *lss;
|
||||
|
||||
lss = dynamic_cast<logfile_sub_source *>(tc->get_sub_source());
|
||||
|
||||
if (!lss)
|
||||
return;
|
||||
|
||||
time_t hour = rounddown_offset(lnav_data.ld_top_time,
|
||||
60 * 60,
|
||||
ten_minute * 10 * 60);
|
||||
vis_line_t line = lss->find_from_time(hour);
|
||||
|
||||
--line;
|
||||
lnav_data.ld_view_stack.back()->set_top(line);
|
||||
}
|
||||
|
||||
void handle_paging_key(int ch)
|
||||
{
|
||||
if (lnav_data.ld_view_stack.empty()) {
|
||||
@ -248,32 +229,6 @@ void handle_paging_key(int ch)
|
||||
lnav_data.ld_rl_view->set_value("Cleared bookmarks");
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
moveto_cluster(&bookmark_vector<vis_line_t>::next,
|
||||
&logfile_sub_source::BM_WARNINGS,
|
||||
tc->get_top());
|
||||
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
|
||||
6, ^,
|
||||
"to move to next/previous hour boundary"));
|
||||
break;
|
||||
|
||||
case 'W':
|
||||
moveto_cluster(&bookmark_vector<vis_line_t>::prev,
|
||||
&logfile_sub_source::BM_WARNINGS,
|
||||
tc->get_top());
|
||||
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
|
||||
6, ^,
|
||||
"to move to next/previous hour boundary"));
|
||||
break;
|
||||
|
||||
case 'y':
|
||||
tc->set_top(bm[&BM_QUERY].next(tc->get_top()));
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
tc->set_top(bm[&BM_QUERY].prev(tc->get_top()));
|
||||
break;
|
||||
|
||||
case '>':
|
||||
{
|
||||
std::pair<int, int> range;
|
||||
@ -422,7 +377,7 @@ void handle_paging_key(int ch)
|
||||
unsigned long width;
|
||||
|
||||
tc->get_dimensions(height, width);
|
||||
if (lnav_data.ld_last_user_mark[tc] > tc->get_bottom() - 2 &&
|
||||
if (lnav_data.ld_last_user_mark[tc] > (tc->get_bottom() - 2) &&
|
||||
tc->get_top() + height < tc->get_inner_height()) {
|
||||
tc->shift_top(vis_line_t(1));
|
||||
}
|
||||
@ -558,49 +513,6 @@ void handle_paging_key(int ch)
|
||||
}
|
||||
break;
|
||||
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
if (lss) {
|
||||
int ten_minute = (ch - '0') * 10 * 60;
|
||||
time_t hour = rounddown(lnav_data.ld_top_time +
|
||||
(60 * 60) -
|
||||
ten_minute +
|
||||
1,
|
||||
60 * 60);
|
||||
vis_line_t line = lss->find_from_time(hour + ten_minute);
|
||||
|
||||
tc->set_top(line);
|
||||
}
|
||||
break;
|
||||
|
||||
case '!':
|
||||
back_ten(1);
|
||||
break;
|
||||
|
||||
case '@':
|
||||
back_ten(2);
|
||||
break;
|
||||
|
||||
case '#':
|
||||
back_ten(3);
|
||||
break;
|
||||
|
||||
case '$':
|
||||
back_ten(4);
|
||||
break;
|
||||
|
||||
case '%':
|
||||
back_ten(5);
|
||||
break;
|
||||
|
||||
case '^':
|
||||
back_ten(6);
|
||||
break;
|
||||
|
||||
case '9':
|
||||
if (lss) {
|
||||
double tenth = ((double)tc->get_inner_height()) / 10.0;
|
||||
@ -866,7 +778,7 @@ void handle_paging_key(int ch)
|
||||
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
|
||||
|
||||
lnav_data.ld_mode = LNM_SQL;
|
||||
setup_logline_table();
|
||||
setup_logline_table(lnav_data.ld_exec_context);
|
||||
lnav_data.ld_rl_view->focus(LNM_SQL, ";");
|
||||
|
||||
lnav_data.ld_bottom_source.update_loading(0, 0);
|
||||
@ -942,16 +854,6 @@ void handle_paging_key(int ch)
|
||||
tc->reload_data();
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
if (toggle_view(&lnav_data.ld_views[LNV_HISTOGRAM])) {
|
||||
lnav_data.ld_rl_view->set_alt_value(
|
||||
HELP_MSG_2(z, Z, "to zoom in/out"));
|
||||
}
|
||||
else {
|
||||
lnav_data.ld_rl_view->set_alt_value("");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'I':
|
||||
{
|
||||
time_t log_top = lnav_data.ld_top_time;
|
||||
@ -1088,10 +990,6 @@ void handle_paging_key(int ch)
|
||||
tc->set_needs_update();
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
lnav_data.ld_rl_view->set_value(execute_command(ec, "close"));
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
{
|
||||
string ex;
|
||||
@ -1116,38 +1014,40 @@ void handle_paging_key(int ch)
|
||||
"Use the 'goto' command to set the relative time to move by");
|
||||
}
|
||||
else {
|
||||
vis_line_t vl = tc->get_top();
|
||||
vis_line_t vl = tc->get_top(), new_vl;
|
||||
relative_time rt = lnav_data.ld_last_relative_time;
|
||||
struct timeval tv;
|
||||
content_line_t cl;
|
||||
struct exttm tm;
|
||||
bool done = false;
|
||||
|
||||
if (ch == 'R') {
|
||||
rt.negate();
|
||||
if (ch == 'r') {
|
||||
if (rt.is_negative()) {
|
||||
rt.negate();
|
||||
}
|
||||
} else if (ch == 'R') {
|
||||
if (!rt.is_negative()) {
|
||||
rt.negate();
|
||||
}
|
||||
}
|
||||
|
||||
cl = lnav_data.ld_log_source.at(vl);
|
||||
logline *ll = lnav_data.ld_log_source.find_line(cl);
|
||||
ll->to_exttm(tm);
|
||||
rt.add(tm);
|
||||
tv.tv_sec = timegm(&tm.et_tm);
|
||||
tv.tv_usec = tm.et_nsec / 1000;
|
||||
vl = lnav_data.ld_log_source.find_from_time(tv);
|
||||
if (rt.is_negative() && (vl > vis_line_t(0))) {
|
||||
--vl;
|
||||
if (vl == tc->get_top()) {
|
||||
vl = vis_line_t(0);
|
||||
do {
|
||||
rt.add(tm);
|
||||
new_vl = lnav_data.ld_log_source.find_from_time(tm);
|
||||
|
||||
if (new_vl == 0_vl || new_vl != vl || !rt.is_relative()) {
|
||||
vl = new_vl;
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
tc->set_top(vl);
|
||||
lnav_data.ld_rl_view->set_value(" " + rt.to_string());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_CTRL_R:
|
||||
reset_session();
|
||||
break;
|
||||
|
||||
case KEY_CTRL_W:
|
||||
execute_command(ec, lnav_data.ld_views[LNV_LOG].get_word_wrap() ?
|
||||
"disable-word-wrap" : "enable-word-wrap");
|
||||
|
@ -1,17 +1,29 @@
|
||||
{
|
||||
"global": {
|
||||
"keymap_def_alt_warning": "Press ${ansi_bold}w${ansi_norm}/${ansi_bold}W${ansi_norm} to move forward/backward through ${ansi_yellow}warning${ansi_norm} messages",
|
||||
"keymap_def_alt_hour_boundary": "Press ${ansi_bold}6${ansi_bold}/${ansi_bold}^${ansi_norm} to move to the next/previous hour boundary",
|
||||
"keymap_def_scroll_horiz": "Press \\'${ansi_bold}>${ansi_norm}\\' or \\'${ansi_bold}<${ansi_norm}\\' to scroll horizontally to a search result",
|
||||
"keymap_def_next_user_mark": "Press ${ansi_bold}u${ansi_norm}/${ansi_bold}U${ansi_norm} to move forward/backward through user bookmarks",
|
||||
"keymap_def_db_view": "Press ${ansi_bold}v${ansi_norm}/${ansi_bold}V${ansi_norm} to switch to the SQL result view",
|
||||
"keymap_def_hist_view": "Press ${ansi_bold}i${ansi_norm}/${ansi_bold}I${ansi_norm} to switch to the histogram view",
|
||||
"keymap_def_text_view": "Press ${ansi_bold}t${ansi_norm} to switch to the text view"
|
||||
"keymap_def_text_view": "Press ${ansi_bold}t${ansi_norm} to switch to the text view",
|
||||
"keymap_def_pop_view": "Press ${ansi_bold}q${ansi_norm} to return to the previous view",
|
||||
"keymap_def_zoom": "Press ${ansi_bold}z${ansi_norm}/${ansi_bold}z${ansi_norm} to zoom in/out"
|
||||
},
|
||||
"keymap_def": {
|
||||
"default": {
|
||||
"x0c": [
|
||||
":write-raw-to -"
|
||||
],
|
||||
|
||||
"x12": [
|
||||
":reset-session"
|
||||
],
|
||||
|
||||
"x58": [
|
||||
":close"
|
||||
],
|
||||
|
||||
"x45": [
|
||||
":prev-mark error",
|
||||
":eval :alt-msg ${keymap_def_alt_warning}"
|
||||
@ -21,6 +33,22 @@
|
||||
":eval :alt-msg ${keymap_def_alt_warning}"
|
||||
],
|
||||
|
||||
"x57": [
|
||||
":prev-mark warning",
|
||||
":eval :alt-msg ${keymap_def_alt_hour_boundary}"
|
||||
],
|
||||
"x77": [
|
||||
":next-mark warning",
|
||||
":eval :alt-msg ${keymap_def_alt_hour_boundary}"
|
||||
],
|
||||
|
||||
"x59": [
|
||||
":prev-mark query"
|
||||
],
|
||||
"x79": [
|
||||
":next-mark query"
|
||||
],
|
||||
|
||||
"x67": [":goto 0"],
|
||||
"x6d": [
|
||||
":mark",
|
||||
@ -36,8 +64,29 @@
|
||||
":eval :alt-msg ${keymap_def_scroll_horiz}"
|
||||
],
|
||||
|
||||
"x31": [":goto next 10 minutes after the hour"],
|
||||
"x32": [":goto next 20 minutes after the hour"],
|
||||
"x33": [":goto next 30 minutes after the hour"],
|
||||
"x34": [":goto next 40 minutes after the hour"],
|
||||
"x35": [":goto next 50 minutes after the hour"],
|
||||
"x36": [":goto next hour"],
|
||||
|
||||
"x21": [":goto last 10 minutes after the hour"],
|
||||
"x40": [":goto last 20 minutes after the hour"],
|
||||
"x23": [":goto last 30 minutes after the hour"],
|
||||
"x24": [":goto last 40 minutes after the hour"],
|
||||
"x25": [":goto last 50 minutes after the hour"],
|
||||
"x5e": [":goto last hour"],
|
||||
|
||||
"x3f": [":toggle-view help"],
|
||||
"x50": [":toggle-view pretty"],
|
||||
"x69": [
|
||||
":toggle-view histogram",
|
||||
":eval :alt-msg ${keymap_def_zoom}"
|
||||
],
|
||||
"x50": [
|
||||
":toggle-view pretty",
|
||||
":eval :alt-msg ${keymap_def_pop_view}"
|
||||
],
|
||||
"x76": [":toggle-view db"],
|
||||
|
||||
"x71": ["|lnav-pop-view ${keyseq}"],
|
||||
|
@ -262,7 +262,7 @@ static void regenerate_unique_file_names()
|
||||
upg.generate();
|
||||
}
|
||||
|
||||
bool setup_logline_table()
|
||||
bool setup_logline_table(exec_context &ec)
|
||||
{
|
||||
// Hidden columns don't show up in the table_info pragma.
|
||||
static const char *hidden_table_columns[] = {
|
||||
@ -284,7 +284,7 @@ bool setup_logline_table()
|
||||
bool retval = false;
|
||||
bool update_possibilities = (
|
||||
lnav_data.ld_rl_view != NULL &&
|
||||
lnav_data.ld_exec_context.ec_local_vars.size() == 1);
|
||||
ec.ec_local_vars.size() == 1);
|
||||
|
||||
if (update_possibilities) {
|
||||
lnav_data.ld_rl_view->clear_possibilities(LNM_SQL, "*");
|
||||
@ -1358,7 +1358,7 @@ static void clear_last_user_mark(void *, listview_curses *lv)
|
||||
{
|
||||
textview_curses *tc = (textview_curses *) lv;
|
||||
if (lnav_data.ld_select_start.find(tc) != lnav_data.ld_select_start.end() &&
|
||||
lnav_data.ld_select_start[tc] != tc->get_top()) {
|
||||
!tc->is_visible(vis_line_t(lnav_data.ld_last_user_mark[tc]))) {
|
||||
lnav_data.ld_select_start.erase(tc);
|
||||
lnav_data.ld_last_user_mark.erase(tc);
|
||||
}
|
||||
|
@ -334,7 +334,7 @@ void rebuild_indexes(bool force);
|
||||
bool ensure_view(textview_curses *expected_tc);
|
||||
bool toggle_view(textview_curses *toggle_tc);
|
||||
|
||||
bool setup_logline_table();
|
||||
bool setup_logline_table(exec_context &ec);
|
||||
|
||||
void execute_search(lnav_view_t view, const std::string ®ex);
|
||||
|
||||
|
@ -270,10 +270,11 @@ static string com_goto(exec_context &ec, string cmdline, vector<string> &args)
|
||||
if (rt.parse(all_args, pe)) {
|
||||
if (tc == &lnav_data.ld_views[LNV_LOG]) {
|
||||
content_line_t cl;
|
||||
vis_line_t vl;
|
||||
vis_line_t vl, new_vl;
|
||||
logline *ll;
|
||||
bool done = false;
|
||||
|
||||
if (!rt.is_absolute()) {
|
||||
if (rt.is_relative()) {
|
||||
lnav_data.ld_last_relative_time = rt;
|
||||
}
|
||||
|
||||
@ -281,17 +282,23 @@ static string com_goto(exec_context &ec, string cmdline, vector<string> &args)
|
||||
cl = lnav_data.ld_log_source.at(vl);
|
||||
ll = lnav_data.ld_log_source.find_line(cl);
|
||||
ll->to_exttm(tm);
|
||||
rt.add(tm);
|
||||
tv.tv_sec = timegm(&tm.et_tm);
|
||||
tv.tv_usec = tm.et_nsec / 1000;
|
||||
|
||||
vl = lnav_data.ld_log_source.find_from_time(tv);
|
||||
do {
|
||||
rt.add(tm);
|
||||
|
||||
new_vl = lnav_data.ld_log_source.find_from_time(tm);
|
||||
|
||||
if (new_vl == 0_vl || new_vl != vl || !rt.is_relative()) {
|
||||
vl = new_vl;
|
||||
done = true;
|
||||
}
|
||||
} while (!done);
|
||||
if (ec.ec_dry_run) {
|
||||
retval = "info: will move to line " + to_string((int) vl);
|
||||
} else {
|
||||
tc->set_top(vl);
|
||||
retval = "";
|
||||
if (!rt.is_absolute() && lnav_data.ld_rl_view != NULL) {
|
||||
if (!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"));
|
||||
@ -301,7 +308,8 @@ static string com_goto(exec_context &ec, string cmdline, vector<string> &args)
|
||||
retval = "error: relative time values only work in the log view";
|
||||
}
|
||||
}
|
||||
else if (dts.scan(args[1].c_str(), args[1].size(), NULL, &tm, tv) != NULL) {
|
||||
else if (dts.scan(args[1].c_str(), args[1].size(), nullptr, &tm, tv) !=
|
||||
nullptr) {
|
||||
if (tc == &lnav_data.ld_views[LNV_LOG]) {
|
||||
vis_line_t vl;
|
||||
|
||||
@ -2174,7 +2182,7 @@ static string com_summarize(exec_context &ec, string cmdline, vector<string> &ar
|
||||
args.emplace_back("colname");
|
||||
return retval;
|
||||
}
|
||||
else if (!setup_logline_table()) {
|
||||
else if (!setup_logline_table(ec)) {
|
||||
retval = "error: no log data available";
|
||||
}
|
||||
else if (args.size() == 1) {
|
||||
|
@ -419,6 +419,15 @@ public:
|
||||
return this->find_from_time(tv);
|
||||
};
|
||||
|
||||
vis_line_t find_from_time(exttm &etm) {
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = timegm(&etm.et_tm);
|
||||
tv.tv_usec = etm.et_nsec / 1000;
|
||||
|
||||
return this->find_from_time(tv);
|
||||
};
|
||||
|
||||
time_t time_for_row(int row) {
|
||||
return this->find_line(this->at(vis_line_t(row)))->get_time();
|
||||
};
|
||||
|
@ -66,11 +66,15 @@ static struct {
|
||||
{ "tomo", pcrepp("\\Atomorrow\\b") },
|
||||
{ "noon", pcrepp("\\Anoon\\b") },
|
||||
{ "and", pcrepp("\\Aand\\b") },
|
||||
{ "the", pcrepp("\\Athe\\b") },
|
||||
{ "ago", pcrepp("\\Aago\\b") },
|
||||
{ "lter", pcrepp("\\Alater\\b") },
|
||||
{ "bfor", pcrepp("\\Abefore\\b") },
|
||||
{ "aft", pcrepp("\\Aafter\\b") },
|
||||
{ "now", pcrepp("\\Anow\\b") },
|
||||
{ "here", pcrepp("\\Ahere\\b") },
|
||||
{ "next", pcrepp("\\Anext\\b") },
|
||||
{ "previous", pcrepp("\\Aprevious|last\\b") },
|
||||
};
|
||||
|
||||
static int64_t TIME_SCALES[] = {
|
||||
@ -80,12 +84,24 @@ static int64_t TIME_SCALES[] = {
|
||||
24,
|
||||
};
|
||||
|
||||
const char relative_time::FIELD_CHARS[] = {
|
||||
'u',
|
||||
's',
|
||||
'm',
|
||||
'h',
|
||||
'd',
|
||||
'M',
|
||||
'y',
|
||||
};
|
||||
|
||||
bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_out)
|
||||
{
|
||||
pcre_input pi(str, 0, len);
|
||||
pcre_context_static<30> pc;
|
||||
int64_t number = 0;
|
||||
bool number_set = false;
|
||||
bool number_set = false, number_was_set = false;
|
||||
bool next_set = false;
|
||||
token_t base_token = RTT_INVALID;
|
||||
|
||||
pe_out.pe_column = -1;
|
||||
pe_out.pe_msg.clear();
|
||||
@ -112,9 +128,17 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
|
||||
found = true;
|
||||
if (RTT_MICROS <= token && token <= RTT_YEARS) {
|
||||
if (!number_set) {
|
||||
pe_out.pe_msg = "Expecting a number before time unit";
|
||||
return false;
|
||||
if (base_token != RTT_INVALID) {
|
||||
base_token = RTT_INVALID;
|
||||
this->rt_is_absolute = true;
|
||||
continue;
|
||||
}
|
||||
if (!this->rt_next && !this->rt_previous) {
|
||||
pe_out.pe_msg = "Expecting a number before time unit";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
number_was_set = number_set;
|
||||
number_set = false;
|
||||
}
|
||||
switch (token) {
|
||||
@ -123,7 +147,6 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
|
||||
case RTT_NOW: {
|
||||
struct timeval tv;
|
||||
struct exttm tm;
|
||||
int abs_start = 0, abs_end = RTF__MAX;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
localtime_r(&tv.tv_sec, &tm.et_tm);
|
||||
@ -141,7 +164,7 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
|
||||
this->rt_field[RTF_MICROSECONDS] = tm.et_nsec / 1000;
|
||||
break;
|
||||
case RTT_YESTERDAY:
|
||||
this->rt_field[RTF_DAYS] -= 1;
|
||||
this->rt_field[RTF_DAYS].value -= 1;
|
||||
case RTT_TODAY:
|
||||
this->rt_field[RTF_HOURS] = 0;
|
||||
this->rt_field[RTF_MINUTES] = 0;
|
||||
@ -151,39 +174,35 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
|
||||
default:
|
||||
break;
|
||||
}
|
||||
for (int lpc = abs_start; lpc <= abs_end; lpc++) {
|
||||
this->rt_is_absolute[lpc] = true;
|
||||
}
|
||||
this->rt_is_absolute = true;
|
||||
break;
|
||||
}
|
||||
case RTT_INVALID:
|
||||
case RTT_WHITE:
|
||||
case RTT_AND:
|
||||
case RTT_THE:
|
||||
break;
|
||||
case RTT_AM:
|
||||
case RTT_PM:
|
||||
if (number_set) {
|
||||
this->rt_field[RTF_HOURS] = number;
|
||||
this->rt_is_absolute[RTF_HOURS] = true;
|
||||
this->rt_field[RTF_MINUTES] = 0;
|
||||
this->rt_is_absolute[RTF_MINUTES] = true;
|
||||
this->rt_field[RTF_SECONDS] = 0;
|
||||
this->rt_is_absolute[RTF_SECONDS] = true;
|
||||
this->rt_field[RTF_MICROSECONDS] = 0;
|
||||
this->rt_is_absolute[RTF_MICROSECONDS] = true;
|
||||
this->rt_is_absolute = true;
|
||||
number_set = false;
|
||||
}
|
||||
if (!this->rt_is_absolute[RTF_HOURS]) {
|
||||
if (!this->rt_is_absolute) {
|
||||
pe_out.pe_msg = "Expecting absolute time with A.M. or P.M.";
|
||||
return false;
|
||||
}
|
||||
if (token == RTT_AM) {
|
||||
if (this->rt_field[RTF_HOURS] == 12) {
|
||||
if (this->rt_field[RTF_HOURS].value == 12) {
|
||||
this->rt_field[RTF_HOURS] = 0;
|
||||
}
|
||||
}
|
||||
else if (this->rt_field[RTF_HOURS] < 12) {
|
||||
this->rt_field[RTF_HOURS] += 12;
|
||||
else if (this->rt_field[RTF_HOURS].value < 12) {
|
||||
this->rt_field[RTF_HOURS].value += 12;
|
||||
}
|
||||
break;
|
||||
case RTT_A:
|
||||
@ -197,9 +216,7 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
|
||||
string hstr = pi.get_substr(pc[0]);
|
||||
string mstr = pi.get_substr(pc[1]);
|
||||
this->rt_field[RTF_HOURS] = atoi(hstr.c_str());
|
||||
this->rt_is_absolute[RTF_HOURS] = true;
|
||||
this->rt_field[RTF_MINUTES] = atoi(mstr.c_str());
|
||||
this->rt_is_absolute[RTF_MINUTES] = true;
|
||||
if (pc[2]->is_valid()) {
|
||||
string sstr = pi.get_substr(pc[2]);
|
||||
this->rt_field[RTF_SECONDS] = atoi(sstr.c_str());
|
||||
@ -222,8 +239,7 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
|
||||
this->rt_field[RTF_SECONDS] = 0;
|
||||
this->rt_field[RTF_MICROSECONDS] = 0;
|
||||
}
|
||||
this->rt_is_absolute[RTF_SECONDS] = true;
|
||||
this->rt_is_absolute[RTF_MICROSECONDS] = true;
|
||||
this->rt_is_absolute = true;
|
||||
break;
|
||||
}
|
||||
case RTT_NUMBER: {
|
||||
@ -248,25 +264,70 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
|
||||
this->rt_field[RTF_MICROSECONDS] = number * 1000;
|
||||
break;
|
||||
case RTT_SECONDS:
|
||||
this->rt_field[RTF_SECONDS] = number;
|
||||
if (number_was_set) {
|
||||
this->rt_field[RTF_SECONDS] = number;
|
||||
} else if (next_set) {
|
||||
this->rt_field[RTF_MICROSECONDS] = 0;
|
||||
this->rt_is_absolute = true;
|
||||
}
|
||||
break;
|
||||
case RTT_MINUTES:
|
||||
this->rt_field[RTF_MINUTES] = number;
|
||||
if (number_was_set) {
|
||||
this->rt_field[RTF_MINUTES] = number;
|
||||
} else if (next_set) {
|
||||
this->rt_field[RTF_MICROSECONDS] = 0;
|
||||
this->rt_field[RTF_SECONDS] = 0;
|
||||
this->rt_is_absolute = true;
|
||||
}
|
||||
break;
|
||||
case RTT_HOURS:
|
||||
this->rt_field[RTF_HOURS] = number;
|
||||
if (number_was_set) {
|
||||
this->rt_field[RTF_HOURS] = number;
|
||||
} else if (next_set) {
|
||||
this->rt_field[RTF_MICROSECONDS] = 0;
|
||||
this->rt_field[RTF_SECONDS] = 0;
|
||||
this->rt_field[RTF_MINUTES] = 0;
|
||||
this->rt_is_absolute = true;
|
||||
}
|
||||
break;
|
||||
case RTT_DAYS:
|
||||
this->rt_field[RTF_DAYS] = number;
|
||||
if (number_was_set) {
|
||||
this->rt_field[RTF_DAYS] = number;
|
||||
} else if (next_set) {
|
||||
this->rt_field[RTF_MICROSECONDS] = 0;
|
||||
this->rt_field[RTF_SECONDS] = 0;
|
||||
this->rt_field[RTF_MINUTES] = 0;
|
||||
this->rt_field[RTF_HOURS] = 0;
|
||||
this->rt_is_absolute = true;
|
||||
}
|
||||
break;
|
||||
case RTT_WEEKS:
|
||||
this->rt_field[RTF_DAYS] = number * 7;
|
||||
break;
|
||||
case RTT_MONTHS:
|
||||
this->rt_field[RTF_MONTHS] = number;
|
||||
if (number_was_set) {
|
||||
this->rt_field[RTF_MONTHS] = number;
|
||||
} else if (next_set) {
|
||||
this->rt_field[RTF_MICROSECONDS] = 0;
|
||||
this->rt_field[RTF_SECONDS] = 0;
|
||||
this->rt_field[RTF_MINUTES] = 0;
|
||||
this->rt_field[RTF_HOURS] = 0;
|
||||
this->rt_field[RTF_DAYS] = 0;
|
||||
this->rt_is_absolute = true;
|
||||
}
|
||||
break;
|
||||
case RTT_YEARS:
|
||||
this->rt_field[RTF_YEARS] = number;
|
||||
if (number_was_set) {
|
||||
this->rt_field[RTF_YEARS] = number;
|
||||
} else if (next_set) {
|
||||
this->rt_field[RTF_MICROSECONDS] = 0;
|
||||
this->rt_field[RTF_SECONDS] = 0;
|
||||
this->rt_field[RTF_MINUTES] = 0;
|
||||
this->rt_field[RTF_HOURS] = 0;
|
||||
this->rt_field[RTF_DAYS] = 0;
|
||||
this->rt_field[RTF_MONTHS] = 0;
|
||||
this->rt_is_absolute = true;
|
||||
}
|
||||
break;
|
||||
case RTT_BEFORE:
|
||||
case RTT_AGO:
|
||||
@ -275,11 +336,14 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
|
||||
return false;
|
||||
}
|
||||
for (int field = 0; field < RTF__MAX; field++) {
|
||||
if (this->rt_field[field] > 0) {
|
||||
this->rt_field[field] = -this->rt_field[field];
|
||||
if (this->rt_field[field].value > 0) {
|
||||
this->rt_field[field] = -this->rt_field[field].value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RTT_AFTER:
|
||||
base_token = token;
|
||||
break;
|
||||
case RTT_LATER:
|
||||
if (this->empty()) {
|
||||
pe_out.pe_msg = "Expecting a time unit before 'later'";
|
||||
@ -288,15 +352,24 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
|
||||
break;
|
||||
case RTT_HERE:
|
||||
break;
|
||||
case RTT_NEXT:
|
||||
this->rt_next = true;
|
||||
next_set = true;
|
||||
break;
|
||||
case RTT_PREVIOUS:
|
||||
this->rt_previous = true;
|
||||
next_set = true;
|
||||
break;
|
||||
case RTT_TOMORROW:
|
||||
this->rt_field[RTF_DAYS] = 1;
|
||||
break;
|
||||
case RTT_NOON:
|
||||
this->rt_field[RTF_HOURS] = 12;
|
||||
this->rt_is_absolute[RTF_HOURS] = true;
|
||||
for (int lpc = RTF_MICROSECONDS; lpc < RTF_HOURS; lpc++) {
|
||||
this->rt_field[lpc] = 0;
|
||||
this->rt_is_absolute[lpc] = true;
|
||||
this->rt_is_absolute = true;
|
||||
for (int lpc2 = RTF_MICROSECONDS;
|
||||
lpc2 < RTF_HOURS;
|
||||
lpc2++) {
|
||||
this->rt_field[lpc2] = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -304,6 +377,14 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (token != RTT_NEXT &&
|
||||
token != RTT_PREVIOUS &&
|
||||
token != RTT_WHITE) {
|
||||
next_set = false;
|
||||
}
|
||||
|
||||
number_was_set = false;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
@ -316,22 +397,96 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
|
||||
void relative_time::rollover()
|
||||
{
|
||||
for (int lpc = 0; lpc < RTF_DAYS; lpc++) {
|
||||
int64_t val = this->rt_field[lpc];
|
||||
if (!this->rt_field[lpc].is_set) {
|
||||
continue;
|
||||
}
|
||||
int64_t val = this->rt_field[lpc].value;
|
||||
this->rt_field[lpc] = val % TIME_SCALES[lpc];
|
||||
this->rt_field[lpc + 1] += val / TIME_SCALES[lpc];
|
||||
this->rt_field[lpc + 1].value += val / TIME_SCALES[lpc];
|
||||
}
|
||||
if (std::abs(this->rt_field[RTF_DAYS]) > 31) {
|
||||
int64_t val = this->rt_field[RTF_DAYS];
|
||||
if (std::abs(this->rt_field[RTF_DAYS].value) > 31) {
|
||||
int64_t val = this->rt_field[RTF_DAYS].value;
|
||||
this->rt_field[RTF_DAYS] = val % 31;
|
||||
this->rt_field[RTF_MONTHS] += val / 31;
|
||||
this->rt_field[RTF_MONTHS].value += val / 31;
|
||||
}
|
||||
if (std::abs(this->rt_field[RTF_MONTHS]) > 12) {
|
||||
int64_t val = this->rt_field[RTF_MONTHS];
|
||||
if (std::abs(this->rt_field[RTF_MONTHS].value) > 12) {
|
||||
int64_t val = this->rt_field[RTF_MONTHS].value;
|
||||
this->rt_field[RTF_MONTHS] = val % 12;
|
||||
this->rt_field[RTF_YEARS] += val / 12;
|
||||
this->rt_field[RTF_YEARS].value += val / 12;
|
||||
}
|
||||
}
|
||||
|
||||
std::string relative_time::to_string()
|
||||
{
|
||||
char dst[128] = "";
|
||||
char *pos = dst;
|
||||
|
||||
if (this->is_absolute()) {
|
||||
pos += snprintf(pos, sizeof(dst) - (pos - dst),
|
||||
"%s",
|
||||
this->rt_next ? "next " :
|
||||
(this->rt_previous ? "last " : ""));
|
||||
if (this->rt_field[RTF_YEARS].is_set &&
|
||||
(this->rt_next || this->rt_previous ||
|
||||
this->rt_field[RTF_YEARS].value != 0)) {
|
||||
pos += snprintf(pos, sizeof(dst) - (pos - dst),
|
||||
"year %" PRId64 " ",
|
||||
this->rt_field[RTF_YEARS].value);
|
||||
} else if ((this->rt_next || this->rt_previous) &&
|
||||
this->rt_field[RTF_MONTHS].is_set) {
|
||||
pos += snprintf(pos, sizeof(dst) - (pos - dst), "year ");
|
||||
}
|
||||
if (this->rt_field[RTF_MONTHS].is_set &&
|
||||
(this->rt_next || this->rt_previous ||
|
||||
this->rt_field[RTF_MONTHS].value != 0)) {
|
||||
pos += snprintf(pos, sizeof(dst) - (pos - dst),
|
||||
"month %" PRId64 " ",
|
||||
this->rt_field[RTF_MONTHS].value);
|
||||
} else if ((this->rt_next || this->rt_previous) &&
|
||||
this->rt_field[RTF_DAYS].is_set) {
|
||||
pos += snprintf(pos, sizeof(dst) - (pos - dst), "month ");
|
||||
}
|
||||
if (this->rt_field[RTF_DAYS].is_set &&
|
||||
(this->rt_next || this->rt_previous ||
|
||||
this->rt_field[RTF_DAYS].value != 0)) {
|
||||
pos += snprintf(pos, sizeof(dst) - (pos - dst),
|
||||
"day %" PRId64 " ",
|
||||
this->rt_field[RTF_DAYS].value);
|
||||
} else if ((this->rt_next || this->rt_previous) &&
|
||||
this->rt_field[RTF_HOURS].is_set) {
|
||||
pos += snprintf(pos, sizeof(dst) - (pos - dst), "day ");
|
||||
}
|
||||
pos += snprintf(pos, sizeof(dst) - (pos - dst),
|
||||
"%" PRId64 ":%02" PRId64,
|
||||
this->rt_field[RTF_HOURS].value,
|
||||
this->rt_field[RTF_MINUTES].value);
|
||||
if (this->rt_field[RTF_SECONDS].is_set &&
|
||||
this->rt_field[RTF_SECONDS].value != 0) {
|
||||
pos += snprintf(pos, sizeof(dst) - (pos - dst),
|
||||
":%.02" PRId64,
|
||||
this->rt_field[RTF_SECONDS].value);
|
||||
if (this->rt_field[RTF_MICROSECONDS].is_set &&
|
||||
this->rt_field[RTF_MICROSECONDS].value != 0) {
|
||||
pos += snprintf(pos, sizeof(dst) - (pos - dst),
|
||||
".%.03" PRId64,
|
||||
this->rt_field[RTF_MICROSECONDS].value / 1000);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int lpc = RTF__MAX - 1; lpc >= 0; lpc--) {
|
||||
if (this->rt_field[lpc].value == 0) {
|
||||
continue;
|
||||
}
|
||||
pos += snprintf(pos, sizeof(dst) - (pos - dst),
|
||||
"%" PRId64 "%c",
|
||||
this->rt_field[lpc].value,
|
||||
FIELD_CHARS[lpc]);
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
size_t str2reltime(int64_t millis, std::string &value_out)
|
||||
{
|
||||
/* 24h22m33s111 */
|
||||
|
@ -68,11 +68,15 @@ public:
|
||||
RTT_TOMORROW,
|
||||
RTT_NOON,
|
||||
RTT_AND,
|
||||
RTT_THE,
|
||||
RTT_AGO,
|
||||
RTT_LATER,
|
||||
RTT_BEFORE,
|
||||
RTT_AFTER,
|
||||
RTT_NOW,
|
||||
RTT_HERE,
|
||||
RTT_NEXT,
|
||||
RTT_PREVIOUS,
|
||||
|
||||
RTT__MAX
|
||||
};
|
||||
@ -83,20 +87,35 @@ public:
|
||||
|
||||
void clear() {
|
||||
memset(this->rt_field, 0, sizeof(this->rt_field));
|
||||
memset(this->rt_is_absolute, 0, sizeof(this->rt_is_absolute));
|
||||
this->rt_next = false;
|
||||
this->rt_previous = false;
|
||||
this->rt_is_absolute = false;
|
||||
};
|
||||
|
||||
void negate() {
|
||||
if (this->rt_is_absolute) {
|
||||
if (this->rt_next) {
|
||||
this->rt_next = false;
|
||||
this->rt_previous = true;
|
||||
} else if (this->rt_previous) {
|
||||
this->rt_next = true;
|
||||
this->rt_previous = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (int lpc = 0; lpc < RTF__MAX; lpc++) {
|
||||
if (!this->rt_is_absolute[lpc] && this->rt_field[lpc] != 0) {
|
||||
this->rt_field[lpc] = -this->rt_field[lpc];
|
||||
if (this->rt_field[lpc].value != 0) {
|
||||
this->rt_field[lpc].value = -this->rt_field[lpc].value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool is_negative() const {
|
||||
for (int lpc = 0; lpc < RTF__MAX; lpc++) {
|
||||
if (this->rt_field[lpc] < 0) {
|
||||
if (this->rt_previous) {
|
||||
return true;
|
||||
}
|
||||
for (auto rtf : this->rt_field) {
|
||||
if (rtf.value < 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -104,17 +123,16 @@ public:
|
||||
};
|
||||
|
||||
bool is_absolute() const {
|
||||
for (int lpc = 0; lpc < RTF__MAX; lpc++) {
|
||||
if (this->rt_is_absolute[lpc]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return this->rt_is_absolute;
|
||||
};
|
||||
|
||||
bool is_relative() const {
|
||||
return !this->rt_is_absolute || this->rt_next || this->rt_previous;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
for (int lpc = 0; lpc < RTF__MAX; lpc++) {
|
||||
if (this->rt_field[lpc]) {
|
||||
for (auto rtf : this->rt_field) {
|
||||
if (rtf.is_set) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -144,59 +162,99 @@ public:
|
||||
};
|
||||
|
||||
void add(struct exttm &tm) {
|
||||
if (this->rt_is_absolute[RTF_MICROSECONDS]) {
|
||||
tm.et_nsec = this->rt_field[RTF_MICROSECONDS] * 1000;
|
||||
if (this->rt_field[RTF_MICROSECONDS].is_set && this->rt_is_absolute) {
|
||||
tm.et_nsec = this->rt_field[RTF_MICROSECONDS].value * 1000;
|
||||
}
|
||||
else {
|
||||
tm.et_nsec += this->rt_field[RTF_MICROSECONDS] * 1000;
|
||||
tm.et_nsec += this->rt_field[RTF_MICROSECONDS].value * 1000;
|
||||
}
|
||||
if (this->rt_is_absolute[RTF_SECONDS]) {
|
||||
tm.et_tm.tm_sec = this->rt_field[RTF_SECONDS];
|
||||
if (this->rt_field[RTF_SECONDS].is_set && this->rt_is_absolute) {
|
||||
if (this->rt_next &&
|
||||
this->rt_field[RTF_SECONDS].value <= tm.et_tm.tm_sec) {
|
||||
tm.et_tm.tm_min += 1;
|
||||
}
|
||||
if (this->rt_previous &&
|
||||
this->rt_field[RTF_SECONDS].value >= tm.et_tm.tm_sec) {
|
||||
tm.et_tm.tm_min -= 1;
|
||||
}
|
||||
tm.et_tm.tm_sec = this->rt_field[RTF_SECONDS].value;
|
||||
}
|
||||
else {
|
||||
tm.et_tm.tm_sec += this->rt_field[RTF_SECONDS];
|
||||
tm.et_tm.tm_sec += this->rt_field[RTF_SECONDS].value;
|
||||
}
|
||||
if (this->rt_is_absolute[RTF_MINUTES]) {
|
||||
tm.et_tm.tm_min = this->rt_field[RTF_MINUTES];
|
||||
if (this->rt_field[RTF_MINUTES].is_set && this->rt_is_absolute) {
|
||||
if (this->rt_next &&
|
||||
this->rt_field[RTF_MINUTES].value <= tm.et_tm.tm_min) {
|
||||
tm.et_tm.tm_hour += 1;
|
||||
}
|
||||
if (this->rt_previous && (this->rt_field[RTF_MINUTES].value == 0 ||
|
||||
(this->rt_field[RTF_MINUTES].value >= tm.et_tm.tm_min))) {
|
||||
tm.et_tm.tm_hour -= 1;
|
||||
}
|
||||
tm.et_tm.tm_min = this->rt_field[RTF_MINUTES].value;
|
||||
}
|
||||
else {
|
||||
tm.et_tm.tm_min += this->rt_field[RTF_MINUTES];
|
||||
tm.et_tm.tm_min += this->rt_field[RTF_MINUTES].value;
|
||||
}
|
||||
if (this->rt_is_absolute[RTF_HOURS]) {
|
||||
tm.et_tm.tm_hour = this->rt_field[RTF_HOURS];
|
||||
if (this->rt_field[RTF_HOURS].is_set && this->rt_is_absolute) {
|
||||
if (this->rt_next &&
|
||||
this->rt_field[RTF_HOURS].value <= tm.et_tm.tm_hour) {
|
||||
tm.et_tm.tm_mday += 1;
|
||||
}
|
||||
if (this->rt_previous &&
|
||||
this->rt_field[RTF_HOURS].value >= tm.et_tm.tm_hour) {
|
||||
tm.et_tm.tm_mday -= 1;
|
||||
}
|
||||
tm.et_tm.tm_hour = this->rt_field[RTF_HOURS].value;
|
||||
}
|
||||
else {
|
||||
tm.et_tm.tm_hour += this->rt_field[RTF_HOURS];
|
||||
tm.et_tm.tm_hour += this->rt_field[RTF_HOURS].value;
|
||||
}
|
||||
if (this->rt_is_absolute[RTF_DAYS]) {
|
||||
tm.et_tm.tm_mday = this->rt_field[RTF_DAYS];
|
||||
if (this->rt_field[RTF_DAYS].is_set && this->rt_is_absolute) {
|
||||
if (this->rt_next &&
|
||||
this->rt_field[RTF_DAYS].value <= tm.et_tm.tm_hour) {
|
||||
tm.et_tm.tm_mon += 1;
|
||||
}
|
||||
if (this->rt_previous &&
|
||||
this->rt_field[RTF_DAYS].value >= tm.et_tm.tm_hour) {
|
||||
tm.et_tm.tm_mon -= 1;
|
||||
}
|
||||
tm.et_tm.tm_mday = this->rt_field[RTF_DAYS].value;
|
||||
}
|
||||
else {
|
||||
tm.et_tm.tm_mday += this->rt_field[RTF_DAYS];
|
||||
tm.et_tm.tm_mday += this->rt_field[RTF_DAYS].value;
|
||||
}
|
||||
if (this->rt_is_absolute[RTF_MONTHS]) {
|
||||
tm.et_tm.tm_mon = this->rt_field[RTF_MONTHS];
|
||||
if (this->rt_field[RTF_MONTHS].is_set && this->rt_is_absolute) {
|
||||
if (this->rt_next &&
|
||||
this->rt_field[RTF_MONTHS].value <= tm.et_tm.tm_mon) {
|
||||
tm.et_tm.tm_year += 1;
|
||||
}
|
||||
if (this->rt_previous &&
|
||||
this->rt_field[RTF_MONTHS].value >= tm.et_tm.tm_mon) {
|
||||
tm.et_tm.tm_year -= 1;
|
||||
}
|
||||
tm.et_tm.tm_mon = this->rt_field[RTF_MONTHS].value;
|
||||
}
|
||||
else {
|
||||
tm.et_tm.tm_mon += this->rt_field[RTF_MONTHS];
|
||||
tm.et_tm.tm_mon += this->rt_field[RTF_MONTHS].value;
|
||||
}
|
||||
if (this->rt_is_absolute[RTF_YEARS]) {
|
||||
tm.et_tm.tm_year = this->rt_field[RTF_YEARS];
|
||||
if (this->rt_field[RTF_YEARS].is_set && this->rt_is_absolute) {
|
||||
tm.et_tm.tm_year = this->rt_field[RTF_YEARS].value;
|
||||
}
|
||||
else {
|
||||
tm.et_tm.tm_year += this->rt_field[RTF_YEARS];
|
||||
tm.et_tm.tm_year += this->rt_field[RTF_YEARS].value;
|
||||
}
|
||||
};
|
||||
|
||||
int64_t to_microseconds() {
|
||||
int64_t retval;
|
||||
|
||||
retval = this->rt_field[RTF_YEARS] * 12;
|
||||
retval = (retval + this->rt_field[RTF_MONTHS]) * 30;
|
||||
retval = (retval + this->rt_field[RTF_DAYS]) * 24;
|
||||
retval = (retval + this->rt_field[RTF_HOURS]) * 60;
|
||||
retval = (retval + this->rt_field[RTF_MINUTES]) * 60;
|
||||
retval = (retval + this->rt_field[RTF_SECONDS]) * 1000 * 1000;
|
||||
retval = this->rt_field[RTF_YEARS].value * 12;
|
||||
retval = (retval + this->rt_field[RTF_MONTHS].value) * 30;
|
||||
retval = (retval + this->rt_field[RTF_DAYS].value) * 24;
|
||||
retval = (retval + this->rt_field[RTF_HOURS].value) * 60;
|
||||
retval = (retval + this->rt_field[RTF_MINUTES].value) * 60;
|
||||
retval = (retval + this->rt_field[RTF_SECONDS].value) * 1000 * 1000;
|
||||
|
||||
return retval;
|
||||
};
|
||||
@ -208,27 +266,7 @@ public:
|
||||
tv_out.tv_usec = us % (1000 * 1000);
|
||||
};
|
||||
|
||||
std::string to_string() {
|
||||
char dst[128];
|
||||
|
||||
snprintf(dst, sizeof(dst),
|
||||
"%" PRId64 "%c%" PRId64 "%c%" PRId64 "%c%" PRId64 "%c%" PRId64 "%c%" PRId64 "%c%" PRId64 "%c",
|
||||
this->rt_field[RTF_YEARS],
|
||||
this->rt_is_absolute[RTF_YEARS] ? 'Y' : 'y',
|
||||
this->rt_field[RTF_MONTHS],
|
||||
this->rt_is_absolute[RTF_MONTHS] ? 'M' : 'm',
|
||||
this->rt_field[RTF_DAYS],
|
||||
this->rt_is_absolute[RTF_DAYS] ? 'D' : 'd',
|
||||
this->rt_field[RTF_HOURS],
|
||||
this->rt_is_absolute[RTF_HOURS] ? 'H' : 'h',
|
||||
this->rt_field[RTF_MINUTES],
|
||||
this->rt_is_absolute[RTF_MINUTES] ? 'M' : 'm',
|
||||
this->rt_field[RTF_SECONDS],
|
||||
this->rt_is_absolute[RTF_SECONDS] ? 'S' : 's',
|
||||
this->rt_field[RTF_MICROSECONDS],
|
||||
this->rt_is_absolute[RTF_MICROSECONDS] ? 'U' : 'u');
|
||||
return dst;
|
||||
};
|
||||
std::string to_string();
|
||||
|
||||
void rollover();
|
||||
|
||||
@ -244,8 +282,22 @@ public:
|
||||
RTF__MAX
|
||||
};
|
||||
|
||||
int64_t rt_field[RTF__MAX];
|
||||
bool rt_is_absolute[RTF__MAX];
|
||||
static const char FIELD_CHARS[RTF__MAX];
|
||||
|
||||
struct _rt_field {
|
||||
_rt_field(int64_t value) : value(value), is_set(true) {
|
||||
};
|
||||
|
||||
_rt_field() : value(0), is_set(false) {
|
||||
};
|
||||
|
||||
int64_t value;
|
||||
bool is_set;
|
||||
} rt_field[RTF__MAX];
|
||||
|
||||
bool rt_next;
|
||||
bool rt_previous;
|
||||
bool rt_is_absolute;
|
||||
};
|
||||
|
||||
size_t str2reltime(int64_t millis, std::string &value_out);
|
||||
|
@ -29,9 +29,9 @@ add_executable(test_abbrev test_abbrev.cc
|
||||
../src/spookyhash/SpookyV2.cpp)
|
||||
add_executable(drive_sql_anno drive_sql_anno.cc ../src/lnav_log.cc ../src/pcrepp.cc)
|
||||
link_directories(/opt/local/lib)
|
||||
target_link_libraries(test_pcrepp /opt/local/lib/libpcre.a)
|
||||
target_link_libraries(test_reltime /opt/local/lib/libpcre.a)
|
||||
target_link_libraries(lnav_doctests /opt/local/lib/libpcre.a)
|
||||
target_link_libraries(test_date_time_scanner /opt/local/lib/libpcre.a)
|
||||
target_link_libraries(test_abbrev /opt/local/lib/libpcre.a)
|
||||
target_link_libraries(drive_sql_anno /opt/local/lib/libpcre.a)
|
||||
target_link_libraries(test_pcrepp /usr/local/lib/libpcre.a)
|
||||
target_link_libraries(test_reltime /usr/local/lib/libpcre.a)
|
||||
target_link_libraries(lnav_doctests /usr/local/lib/libpcre.a)
|
||||
target_link_libraries(test_date_time_scanner /usr/local/lib/libpcre.a)
|
||||
target_link_libraries(test_abbrev /usr/local/lib/libpcre.a)
|
||||
target_link_libraries(drive_sql_anno /usr/local/lib/libpcre.a)
|
||||
|
@ -771,6 +771,16 @@ check_output "rollover is not working with histogram" <<EOF
|
||||
Wed Jan 03 09:20:00 1 normal 0 errors 0 warnings 0 marks
|
||||
EOF
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ":goto 0" \
|
||||
-c ":goto next year" \
|
||||
logfile_rollover.1.live
|
||||
|
||||
check_output ":goto next year is not working" <<EOF
|
||||
Feb 3 09:23:38 veridian automount[7998]: lookup(file): lookup for foobar failed
|
||||
Jan 3 09:23:38 veridian automount[16442]: attempting to mount entry /auto/opt
|
||||
EOF
|
||||
|
||||
touch -t 200711030923 ${srcdir}/logfile_syslog.0
|
||||
run_test ${lnav_test} -n \
|
||||
-c ":switch-to-view histogram" \
|
||||
|
@ -29,10 +29,12 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <intern_string.hh>
|
||||
#include "intern_string.hh"
|
||||
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include "doctest.hh"
|
||||
#include "relative_time.hh"
|
||||
|
||||
using namespace std;
|
||||
@ -41,21 +43,29 @@ static struct {
|
||||
const char *reltime;
|
||||
const char *expected;
|
||||
} TEST_DATA[] = {
|
||||
{ "a minute ago", "0y0m0d0h-1m0s0u" },
|
||||
{ "1m ago", "0y0m0d0h-1m0s0u" },
|
||||
{ "a min ago", "0y0m0d0h-1m0s0u" },
|
||||
{ "a m ago", "0y0m0d0h-1m0s0u" },
|
||||
{ "+1 minute ago", "0y0m0d0h-1m0s0u" },
|
||||
{ "-1 minute ago", "0y0m0d0h-1m0s0u" },
|
||||
{ "-1 minute", "0y0m0d0h-1m0s0u" },
|
||||
{ "1:40", "0y0m0d1H40M0S0U" },
|
||||
{ "01:40", "0y0m0d1H40M0S0U" },
|
||||
{ "1h40m", "0y0m0d1h40m0s0u" },
|
||||
{ "1pm", "0y0m0d13H0M0S0U" },
|
||||
{ "12pm", "0y0m0d12H0M0S0U" },
|
||||
{ "00:27:18.567", "0y0m0d0H27M18S567000U" },
|
||||
// { "10 minutes after the next hour", "next 0:10" },
|
||||
{ "next day", "next day 0:00" },
|
||||
{ "next month", "next month day 0 0:00" },
|
||||
{ "next year", "next year month 0 day 0 0:00" },
|
||||
{ "last hour", "last 0:00" },
|
||||
{ "next 10 minutes after the hour", "next 0:10" },
|
||||
{ "1h50m", "1h50m" },
|
||||
{ "next hour", "next 0:00" },
|
||||
{ "a minute ago", "-1m" },
|
||||
{ "1m ago", "-1m" },
|
||||
{ "a min ago", "-1m" },
|
||||
{ "a m ago", "-1m" },
|
||||
{ "+1 minute ago", "-1m" },
|
||||
{ "-1 minute ago", "-1m" },
|
||||
{ "-1 minute", "-1m" },
|
||||
{ "10 minutes after the hour", "0:10" },
|
||||
{ "1:40", "1:40" },
|
||||
{ "01:30", "1:30" },
|
||||
{ "1pm", "13:00" },
|
||||
{ "12pm", "12:00" },
|
||||
{ "00:27:18.567", "0:27:18.567" },
|
||||
|
||||
{ NULL, NULL }
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static struct {
|
||||
@ -69,7 +79,7 @@ static struct {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
TEST_CASE("reltime")
|
||||
{
|
||||
time_t base_time = 1317913200;
|
||||
struct exttm base_tm;
|
||||
@ -86,67 +96,67 @@ int main(int argc, char *argv[])
|
||||
|
||||
rt.clear();
|
||||
rc = rt.parse(TEST_DATA[lpc].reltime, pe);
|
||||
printf("%s %s %s\n", TEST_DATA[lpc].reltime, TEST_DATA[lpc].expected, rt.to_string().c_str());
|
||||
assert(rc);
|
||||
assert(std::string(TEST_DATA[lpc].expected) == rt.to_string());
|
||||
CHECK_MESSAGE(rc, TEST_DATA[lpc].reltime);
|
||||
CHECK(std::string(TEST_DATA[lpc].expected) == rt.to_string());
|
||||
}
|
||||
|
||||
for (int lpc = 0; BAD_TEST_DATA[lpc].reltime; lpc++) {
|
||||
bool rc;
|
||||
rt.clear();
|
||||
rc = rt.parse(BAD_TEST_DATA[lpc].reltime, pe);
|
||||
printf("%s -- %s\n", BAD_TEST_DATA[lpc].reltime, pe.pe_msg.c_str());
|
||||
assert(!rc);
|
||||
CHECK(!rc);
|
||||
}
|
||||
|
||||
rt.clear();
|
||||
rt.parse("", pe);
|
||||
CHECK(rt.empty());
|
||||
|
||||
rt.clear();
|
||||
rt.parse("a minute ago", pe);
|
||||
assert(rt.rt_field[relative_time::RTF_MINUTES] == -1);
|
||||
CHECK(rt.rt_field[relative_time::RTF_MINUTES].value == -1);
|
||||
|
||||
rt.clear();
|
||||
rt.parse("5 milliseconds", pe);
|
||||
|
||||
assert(rt.rt_field[relative_time::RTF_MICROSECONDS] == 5 * 1000);
|
||||
CHECK(rt.rt_field[relative_time::RTF_MICROSECONDS].value == 5 * 1000);
|
||||
|
||||
rt.clear();
|
||||
rt.parse("5000 ms ago", pe);
|
||||
assert(rt.rt_field[relative_time::RTF_SECONDS] == -5);
|
||||
CHECK(rt.rt_field[relative_time::RTF_SECONDS].value == -5);
|
||||
|
||||
rt.clear();
|
||||
rt.parse("5 hours 20 minutes ago", pe);
|
||||
|
||||
assert(rt.rt_field[relative_time::RTF_HOURS] == -5);
|
||||
assert(rt.rt_field[relative_time::RTF_MINUTES] == -20);
|
||||
CHECK(rt.rt_field[relative_time::RTF_HOURS].value == -5);
|
||||
CHECK(rt.rt_field[relative_time::RTF_MINUTES].value == -20);
|
||||
|
||||
rt.clear();
|
||||
rt.parse("5 hours and 20 minutes ago", pe);
|
||||
|
||||
assert(rt.rt_field[relative_time::RTF_HOURS] == -5);
|
||||
assert(rt.rt_field[relative_time::RTF_MINUTES] == -20);
|
||||
CHECK(rt.rt_field[relative_time::RTF_HOURS].value == -5);
|
||||
CHECK(rt.rt_field[relative_time::RTF_MINUTES].value == -20);
|
||||
|
||||
rt.clear();
|
||||
rt.parse("1:23", pe);
|
||||
|
||||
assert(rt.rt_field[relative_time::RTF_HOURS] == 1);
|
||||
assert(rt.rt_is_absolute[relative_time::RTF_HOURS]);
|
||||
assert(rt.rt_field[relative_time::RTF_MINUTES] == 23);
|
||||
assert(rt.rt_is_absolute[relative_time::RTF_MINUTES]);
|
||||
CHECK(rt.rt_field[relative_time::RTF_HOURS].value == 1);
|
||||
CHECK(rt.rt_field[relative_time::RTF_MINUTES].value == 23);
|
||||
CHECK(rt.rt_is_absolute);
|
||||
|
||||
rt.clear();
|
||||
rt.parse("1:23:45", pe);
|
||||
|
||||
assert(rt.rt_field[relative_time::RTF_HOURS] == 1);
|
||||
assert(rt.rt_is_absolute[relative_time::RTF_HOURS]);
|
||||
assert(rt.rt_field[relative_time::RTF_MINUTES] == 23);
|
||||
assert(rt.rt_is_absolute[relative_time::RTF_MINUTES]);
|
||||
assert(rt.rt_field[relative_time::RTF_SECONDS] == 45);
|
||||
assert(rt.rt_is_absolute[relative_time::RTF_SECONDS]);
|
||||
CHECK(rt.rt_field[relative_time::RTF_HOURS].value == 1);
|
||||
CHECK(rt.rt_field[relative_time::RTF_MINUTES].value == 23);
|
||||
CHECK(rt.rt_field[relative_time::RTF_SECONDS].value == 45);
|
||||
CHECK(rt.rt_is_absolute);
|
||||
|
||||
tm = base_tm;
|
||||
rt.add(tm);
|
||||
|
||||
new_time = timegm(&tm.et_tm);
|
||||
tm.et_tm = *gmtime(&new_time);
|
||||
assert(tm.et_tm.tm_hour == 1);
|
||||
assert(tm.et_tm.tm_min == 23);
|
||||
CHECK(tm.et_tm.tm_hour == 1);
|
||||
CHECK(tm.et_tm.tm_min == 23);
|
||||
|
||||
rt.clear();
|
||||
rt.parse("5 minutes ago", pe);
|
||||
@ -156,7 +166,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
new_time = timegm(&tm.et_tm);
|
||||
|
||||
assert(new_time == (base_time - (5 * 60)));
|
||||
CHECK(new_time == (base_time - (5 * 60)));
|
||||
|
||||
rt.clear();
|
||||
rt.parse("today at 4pm", pe);
|
||||
@ -176,12 +186,12 @@ int main(int argc, char *argv[])
|
||||
tm2.et_tm.tm_gmtoff = 0;
|
||||
tm2.et_tm.tm_zone = NULL;
|
||||
#endif
|
||||
assert(tm.et_tm.tm_year == tm2.et_tm.tm_year);
|
||||
assert(tm.et_tm.tm_mon == tm2.et_tm.tm_mon);
|
||||
assert(tm.et_tm.tm_mday == tm2.et_tm.tm_mday);
|
||||
assert(tm.et_tm.tm_hour == tm2.et_tm.tm_hour);
|
||||
assert(tm.et_tm.tm_min == tm2.et_tm.tm_min);
|
||||
assert(tm.et_tm.tm_sec == tm2.et_tm.tm_sec);
|
||||
CHECK(tm.et_tm.tm_year == tm2.et_tm.tm_year);
|
||||
CHECK(tm.et_tm.tm_mon == tm2.et_tm.tm_mon);
|
||||
CHECK(tm.et_tm.tm_mday == tm2.et_tm.tm_mday);
|
||||
CHECK(tm.et_tm.tm_hour == tm2.et_tm.tm_hour);
|
||||
CHECK(tm.et_tm.tm_min == tm2.et_tm.tm_min);
|
||||
CHECK(tm.et_tm.tm_sec == tm2.et_tm.tm_sec);
|
||||
|
||||
rt.clear();
|
||||
rt.parse("yesterday at 4pm", pe);
|
||||
@ -200,10 +210,10 @@ int main(int argc, char *argv[])
|
||||
tm2.et_tm.tm_gmtoff = 0;
|
||||
tm2.et_tm.tm_zone = NULL;
|
||||
#endif
|
||||
assert(tm.et_tm.tm_year == tm2.et_tm.tm_year);
|
||||
assert(tm.et_tm.tm_mon == tm2.et_tm.tm_mon);
|
||||
assert(tm.et_tm.tm_mday == tm2.et_tm.tm_mday);
|
||||
assert(tm.et_tm.tm_hour == tm2.et_tm.tm_hour);
|
||||
assert(tm.et_tm.tm_min == tm2.et_tm.tm_min);
|
||||
assert(tm.et_tm.tm_sec == tm2.et_tm.tm_sec);
|
||||
CHECK(tm.et_tm.tm_year == tm2.et_tm.tm_year);
|
||||
CHECK(tm.et_tm.tm_mon == tm2.et_tm.tm_mon);
|
||||
CHECK(tm.et_tm.tm_mday == tm2.et_tm.tm_mday);
|
||||
CHECK(tm.et_tm.tm_hour == tm2.et_tm.tm_hour);
|
||||
CHECK(tm.et_tm.tm_min == tm2.et_tm.tm_min);
|
||||
CHECK(tm.et_tm.tm_sec == tm2.et_tm.tm_sec);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user