You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lnav/src/base/date_time_scanner.cc

243 lines
7.8 KiB
C++

/**
* Copyright (c) 2020, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @file date_time_scanner.cc
*/
#include "date_time_scanner.hh"
#include "config.h"
#include "ptimec.hh"
size_t
date_time_scanner::ftime(char* dst, size_t len, const exttm& tm) const
{
off_t off = 0;
PTIMEC_FORMATS[this->dts_fmt_lock].pf_ffunc(dst, off, len, tm);
return (size_t) off;
}
bool
next_format(const char* const fmt[], int& index, int& locked_index)
{
bool retval = true;
if (locked_index == -1) {
index += 1;
if (fmt[index] == nullptr) {
retval = false;
}
} else if (index == locked_index) {
retval = false;
} else {
index = locked_index;
}
return retval;
}
const char*
date_time_scanner::scan(const char* time_dest,
size_t time_len,
const char* const time_fmt[],
struct exttm* tm_out,
struct timeval& tv_out,
bool convert_local)
{
int curr_time_fmt = -1;
bool found = false;
const char* retval = nullptr;
if (!time_fmt) {
time_fmt = PTIMEC_FORMAT_STR;
}
while (next_format(time_fmt, curr_time_fmt, this->dts_fmt_lock)) {
*tm_out = this->dts_base_tm;
tm_out->et_flags = 0;
if (time_len > 1 && time_dest[0] == '+' && isdigit(time_dest[1])) {
char time_cp[time_len + 1];
int gmt_int, off;
retval = nullptr;
memcpy(time_cp, time_dest, time_len);
time_cp[time_len] = '\0';
if (sscanf(time_cp, "+%d%n", &gmt_int, &off) == 1) {
time_t gmt = gmt_int;
if (convert_local && this->dts_local_time) {
localtime_r(&gmt, &tm_out->et_tm);
#ifdef HAVE_STRUCT_TM_TM_ZONE
tm_out->et_tm.tm_zone = nullptr;
#endif
tm_out->et_tm.tm_isdst = 0;
gmt = tm2sec(&tm_out->et_tm);
}
tv_out.tv_sec = gmt;
tv_out.tv_usec = 0;
tm_out->et_flags = ETF_DAY_SET | ETF_MONTH_SET | ETF_YEAR_SET
| ETF_MACHINE_ORIENTED | ETF_EPOCH_TIME;
this->dts_fmt_lock = curr_time_fmt;
this->dts_fmt_len = off;
retval = time_dest + off;
found = true;
break;
}
} else if (time_fmt == PTIMEC_FORMAT_STR) {
ptime_func func = PTIMEC_FORMATS[curr_time_fmt].pf_func;
off_t off = 0;
#ifdef HAVE_STRUCT_TM_TM_ZONE
if (!this->dts_keep_base_tz) {
tm_out->et_tm.tm_zone = nullptr;
}
#endif
if (func(tm_out, time_dest, off, time_len)) {
retval = &time_dest[off];
if (tm_out->et_tm.tm_year < 70) {
tm_out->et_tm.tm_year = 80;
}
if (convert_local
&& (this->dts_local_time
|| tm_out->et_flags & ETF_EPOCH_TIME))
{
time_t gmt = tm2sec(&tm_out->et_tm);
this->to_localtime(gmt, *tm_out);
}
tv_out.tv_sec = tm2sec(&tm_out->et_tm);
tv_out.tv_usec = tm_out->et_nsec / 1000;
secs2wday(tv_out, &tm_out->et_tm);
this->dts_fmt_lock = curr_time_fmt;
this->dts_fmt_len = retval - time_dest;
found = true;
break;
}
} else {
off_t off = 0;
#ifdef HAVE_STRUCT_TM_TM_ZONE
if (!this->dts_keep_base_tz) {
tm_out->et_tm.tm_zone = nullptr;
}
#endif
if (ptime_fmt(
time_fmt[curr_time_fmt], tm_out, time_dest, off, time_len)
&& (time_dest[off] == '.' || time_dest[off] == ','
|| off == (off_t) time_len))
{
retval = &time_dest[off];
if (tm_out->et_tm.tm_year < 70) {
tm_out->et_tm.tm_year = 80;
}
if (convert_local
&& (this->dts_local_time
|| tm_out->et_flags & ETF_EPOCH_TIME))
{
time_t gmt = tm2sec(&tm_out->et_tm);
this->to_localtime(gmt, *tm_out);
#ifdef HAVE_STRUCT_TM_TM_ZONE
tm_out->et_tm.tm_zone = nullptr;
#endif
tm_out->et_tm.tm_isdst = 0;
}
tv_out.tv_sec = tm2sec(&tm_out->et_tm);
tv_out.tv_usec = tm_out->et_nsec / 1000;
secs2wday(tv_out, &tm_out->et_tm);
this->dts_fmt_lock = curr_time_fmt;
this->dts_fmt_len = retval - time_dest;
found = true;
break;
}
}
}
if (!found) {
retval = nullptr;
}
if (retval != nullptr) {
/* Try to pull out the milli/micro-second value. */
if (retval[0] == '.' || retval[0] == ',') {
off_t off = (retval - time_dest) + 1;
if (ptime_f(tm_out, time_dest, off, time_len)) {
tv_out.tv_usec = tm_out->et_nsec / 1000;
this->dts_fmt_len += 7;
retval += 7;
} else if (ptime_L(tm_out, time_dest, off, time_len)) {
tv_out.tv_usec = tm_out->et_nsec / 1000;
this->dts_fmt_len += 4;
retval += 4;
}
}
}
return retval;
}
void
date_time_scanner::to_localtime(time_t t, exttm& tm_out)
{
if (t < (24 * 60 * 60)) {
// Don't convert and risk going past the epoch.
return;
}
if (t < this->dts_local_offset_valid || t >= this->dts_local_offset_expiry)
{
time_t new_gmt;
localtime_r(&t, &tm_out.et_tm);
#ifdef HAVE_STRUCT_TM_TM_ZONE
tm_out.et_tm.tm_zone = nullptr;
#endif
tm_out.et_tm.tm_isdst = 0;
new_gmt = tm2sec(&tm_out.et_tm);
this->dts_local_offset_cache = t - new_gmt;
this->dts_local_offset_valid = t;
this->dts_local_offset_expiry = t + (EXPIRE_TIME - 1);
this->dts_local_offset_expiry
-= this->dts_local_offset_expiry % EXPIRE_TIME;
} else {
time_t adjust_gmt = t - this->dts_local_offset_cache;
gmtime_r(&adjust_gmt, &tm_out.et_tm);
}
}