2013-06-02 21:20:15 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2013, 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 lnav_util.cc
|
|
|
|
*
|
|
|
|
* Dumping ground for useful functions with no other home.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2013-06-03 04:11:17 +00:00
|
|
|
#include <stdio.h>
|
2013-06-06 14:01:32 +00:00
|
|
|
#include <fcntl.h>
|
2013-07-02 13:41:28 +00:00
|
|
|
#include <ctype.h>
|
2017-12-29 15:02:19 +00:00
|
|
|
#include <stdarg.h>
|
2015-11-27 06:19:53 +00:00
|
|
|
|
2013-06-20 13:08:53 +00:00
|
|
|
#include "auto_fd.hh"
|
2013-06-02 21:20:15 +00:00
|
|
|
#include "lnav_util.hh"
|
2019-05-08 12:30:59 +00:00
|
|
|
#include "pcrepp/pcrepp.hh"
|
2019-07-30 05:18:32 +00:00
|
|
|
#include "base/result.h"
|
2020-09-05 21:41:05 +00:00
|
|
|
#include "ansi_scrubber.hh"
|
|
|
|
#include "view_curses.hh"
|
2020-10-29 04:17:57 +00:00
|
|
|
#include "archive_manager.hh"
|
2020-11-10 06:17:17 +00:00
|
|
|
#include "fmt/format.h"
|
2013-06-02 21:20:15 +00:00
|
|
|
|
2015-03-16 16:16:49 +00:00
|
|
|
using namespace std;
|
|
|
|
|
2015-08-02 13:43:57 +00:00
|
|
|
bool is_url(const char *fn)
|
|
|
|
{
|
|
|
|
static pcrepp url_re("^(file|https?|ftps?||scp|sftp):");
|
|
|
|
|
|
|
|
pcre_context_static<30> pc;
|
|
|
|
pcre_input pi(fn);
|
|
|
|
|
|
|
|
return url_re.match(pc, pi);
|
|
|
|
}
|
|
|
|
|
2013-06-03 14:45:19 +00:00
|
|
|
std::string hash_string(const std::string &str)
|
|
|
|
{
|
2014-02-04 17:26:25 +00:00
|
|
|
byte_array<2, uint64> hash;
|
2014-02-04 06:29:59 +00:00
|
|
|
SpookyHash context;
|
2013-06-03 14:45:19 +00:00
|
|
|
|
2014-02-04 06:29:59 +00:00
|
|
|
context.Init(0, 0);
|
|
|
|
context.Update(str.c_str(), str.length());
|
2014-02-04 17:26:25 +00:00
|
|
|
context.Final(hash.out(0), hash.out(1));
|
2013-06-03 14:45:19 +00:00
|
|
|
|
|
|
|
return hash.to_string();
|
|
|
|
}
|
|
|
|
|
2015-12-22 13:27:36 +00:00
|
|
|
std::string hash_bytes(const char *str1, size_t s1len, ...)
|
|
|
|
{
|
|
|
|
byte_array<2, uint64> hash;
|
|
|
|
SpookyHash context;
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, s1len);
|
|
|
|
|
|
|
|
context.Init(0, 0);
|
2020-11-10 06:17:17 +00:00
|
|
|
while (str1 != nullptr) {
|
2015-12-22 13:27:36 +00:00
|
|
|
context.Update(str1, s1len);
|
|
|
|
|
|
|
|
str1 = va_arg(args, const char *);
|
2020-11-10 06:17:17 +00:00
|
|
|
if (str1 == nullptr) {
|
2015-12-22 13:27:36 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
s1len = va_arg(args, size_t);
|
|
|
|
}
|
|
|
|
context.Final(hash.out(0), hash.out(1));
|
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return hash.to_string();
|
|
|
|
}
|
|
|
|
|
2016-03-14 05:22:27 +00:00
|
|
|
std::string time_ago(time_t last_time, bool convert_local)
|
2013-06-02 21:20:15 +00:00
|
|
|
{
|
2020-11-10 06:17:17 +00:00
|
|
|
time_t delta, current_time = time(nullptr);
|
2013-06-02 21:20:15 +00:00
|
|
|
const char *fmt;
|
2013-06-16 01:07:50 +00:00
|
|
|
char buffer[64];
|
|
|
|
int amount;
|
2016-03-14 05:22:27 +00:00
|
|
|
|
|
|
|
if (convert_local) {
|
2016-03-25 06:58:25 +00:00
|
|
|
current_time = convert_log_time_to_local(current_time);
|
2016-03-14 05:22:27 +00:00
|
|
|
}
|
2013-06-02 21:20:15 +00:00
|
|
|
|
|
|
|
delta = current_time - last_time;
|
|
|
|
if (delta < 0) {
|
|
|
|
return "in the future";
|
|
|
|
}
|
|
|
|
else if (delta < 60) {
|
|
|
|
return "just now";
|
|
|
|
}
|
|
|
|
else if (delta < (60 * 2)) {
|
|
|
|
return "one minute ago";
|
|
|
|
}
|
|
|
|
else if (delta < (60 * 60)) {
|
2013-06-16 01:07:50 +00:00
|
|
|
fmt = "%d minutes ago";
|
2013-06-02 21:20:15 +00:00
|
|
|
amount = delta / 60;
|
|
|
|
}
|
|
|
|
else if (delta < (2 * 60 * 60)) {
|
|
|
|
return "one hour ago";
|
|
|
|
}
|
|
|
|
else if (delta < (24 * 60 * 60)) {
|
2013-06-16 01:07:50 +00:00
|
|
|
fmt = "%d hours ago";
|
2013-06-02 21:20:15 +00:00
|
|
|
amount = delta / (60 * 60);
|
|
|
|
}
|
|
|
|
else if (delta < (2 * 24 * 60 * 60)) {
|
|
|
|
return "one day ago";
|
|
|
|
}
|
|
|
|
else if (delta < (365 * 24 * 60 * 60)) {
|
2013-06-16 01:07:50 +00:00
|
|
|
fmt = "%d days ago";
|
2013-06-02 21:20:15 +00:00
|
|
|
amount = delta / (24 * 60 * 60);
|
|
|
|
}
|
2017-04-23 14:11:21 +00:00
|
|
|
else if (delta < (2 * 365 * 24 * 60 * 60)) {
|
2013-06-02 21:20:15 +00:00
|
|
|
return "over a year ago";
|
|
|
|
}
|
2017-04-23 14:11:21 +00:00
|
|
|
else {
|
|
|
|
fmt = "over %d years ago";
|
|
|
|
amount = delta / (365 * 24 * 60 * 60);
|
|
|
|
}
|
2013-06-02 21:20:15 +00:00
|
|
|
|
|
|
|
snprintf(buffer, sizeof(buffer), fmt, amount);
|
|
|
|
|
|
|
|
return std::string(buffer);
|
|
|
|
}
|
2013-06-06 14:01:32 +00:00
|
|
|
|
2016-03-14 05:22:27 +00:00
|
|
|
std::string precise_time_ago(const struct timeval &tv, bool convert_local)
|
|
|
|
{
|
|
|
|
struct timeval now, diff;
|
|
|
|
|
2020-11-10 06:17:17 +00:00
|
|
|
gettimeofday(&now, nullptr);
|
2016-03-14 05:22:27 +00:00
|
|
|
if (convert_local) {
|
2016-03-25 06:58:25 +00:00
|
|
|
now.tv_sec = convert_log_time_to_local(now.tv_sec);
|
2016-03-14 05:22:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
timersub(&now, &tv, &diff);
|
|
|
|
if (diff.tv_sec < 0) {
|
|
|
|
return time_ago(tv.tv_sec);
|
|
|
|
}
|
2016-03-14 13:25:38 +00:00
|
|
|
else if (diff.tv_sec <= 1) {
|
2016-03-14 05:22:27 +00:00
|
|
|
return "a second ago";
|
|
|
|
}
|
|
|
|
else if (diff.tv_sec < (10 * 60)) {
|
|
|
|
char buf[64];
|
|
|
|
|
|
|
|
if (diff.tv_sec < 60) {
|
|
|
|
snprintf(buf, sizeof(buf),
|
2016-03-15 16:53:04 +00:00
|
|
|
"%2ld seconds ago",
|
2016-03-14 05:22:27 +00:00
|
|
|
diff.tv_sec);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
time_t seconds = diff.tv_sec % 60;
|
|
|
|
time_t minutes = diff.tv_sec / 60;
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf),
|
2016-03-15 16:53:04 +00:00
|
|
|
"%2ld minute%s and %2ld second%s ago",
|
2016-03-14 05:22:27 +00:00
|
|
|
minutes,
|
|
|
|
minutes > 1 ? "s" : "",
|
|
|
|
seconds,
|
|
|
|
seconds == 1 ? "" : "s");
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(buf);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return time_ago(tv.tv_sec, convert_local);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-10 06:17:17 +00:00
|
|
|
bool change_to_parent_dir()
|
2013-06-06 14:01:32 +00:00
|
|
|
{
|
|
|
|
bool retval = false;
|
|
|
|
char cwd[3] = "";
|
|
|
|
|
2020-11-10 06:17:17 +00:00
|
|
|
if (getcwd(cwd, sizeof(cwd)) == nullptr) {
|
2013-06-06 14:01:32 +00:00
|
|
|
/* perror("getcwd"); */
|
|
|
|
}
|
|
|
|
if (strcmp(cwd, "/") != 0) {
|
|
|
|
if (chdir("..") == -1) {
|
|
|
|
perror("chdir('..')");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
retval = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2016-01-05 14:18:58 +00:00
|
|
|
void split_ws(const std::string &str, std::vector<std::string> &toks_out)
|
|
|
|
{
|
|
|
|
std::stringstream ss(str);
|
|
|
|
std::string buf;
|
|
|
|
|
|
|
|
while (ss >> buf) {
|
|
|
|
toks_out.push_back(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-10 06:17:17 +00:00
|
|
|
file_format_t detect_file_format(const ghc::filesystem::path &filename)
|
2013-06-06 14:01:32 +00:00
|
|
|
{
|
2020-10-29 04:17:57 +00:00
|
|
|
if (archive_manager::is_archive(filename)) {
|
|
|
|
return file_format_t::FF_ARCHIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
file_format_t retval = file_format_t::FF_UNKNOWN;
|
2013-06-20 13:08:53 +00:00
|
|
|
auto_fd fd;
|
2013-06-06 14:01:32 +00:00
|
|
|
|
2020-11-10 06:17:17 +00:00
|
|
|
if ((fd = openp(filename, O_RDONLY)) != -1) {
|
2013-06-06 14:01:32 +00:00
|
|
|
char buffer[32];
|
2020-10-29 04:17:57 +00:00
|
|
|
ssize_t rc;
|
2013-06-06 14:01:32 +00:00
|
|
|
|
|
|
|
if ((rc = read(fd, buffer, sizeof(buffer))) > 0) {
|
2020-10-29 04:17:57 +00:00
|
|
|
static auto SQLITE3_HEADER = "SQLite format 3";
|
|
|
|
auto header_frag = string_fragment(buffer, 0, rc);
|
|
|
|
|
|
|
|
if (header_frag.startswith(SQLITE3_HEADER)) {
|
|
|
|
retval = file_format_t::FF_SQLITE_DB;
|
2013-06-06 14:01:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
2013-06-30 23:43:08 +00:00
|
|
|
|
|
|
|
static time_t BAD_DATE = -1;
|
|
|
|
|
|
|
|
time_t tm2sec(const struct tm *t)
|
|
|
|
{
|
|
|
|
int year;
|
2016-11-20 12:22:10 +00:00
|
|
|
time_t days, secs;
|
2013-06-30 23:43:08 +00:00
|
|
|
const int dayoffset[12] =
|
|
|
|
{ 306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275 };
|
|
|
|
|
|
|
|
year = t->tm_year;
|
|
|
|
|
|
|
|
if (year < 70 || ((sizeof(time_t) <= 4) && (year >= 138))) {
|
|
|
|
return BAD_DATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* shift new year to 1st March in order to make leap year calc easy */
|
|
|
|
|
|
|
|
if (t->tm_mon < 2) {
|
|
|
|
year--;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find number of days since 1st March 1900 (in the Gregorian calendar). */
|
|
|
|
|
|
|
|
days = year * 365 + year / 4 - year / 100 + (year / 100 + 3) / 4;
|
|
|
|
days += dayoffset[t->tm_mon] + t->tm_mday - 1;
|
|
|
|
days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */
|
|
|
|
|
2016-11-20 12:22:10 +00:00
|
|
|
secs = ((days * 24 + t->tm_hour) * 60 + t->tm_min) * 60 + t->tm_sec;
|
2013-06-30 23:43:08 +00:00
|
|
|
|
2016-11-20 12:22:10 +00:00
|
|
|
if (secs < 0) {
|
2013-06-30 23:43:08 +00:00
|
|
|
return BAD_DATE;
|
|
|
|
} /* must have overflowed */
|
|
|
|
else {
|
2014-01-07 15:35:52 +00:00
|
|
|
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
2013-06-30 23:43:08 +00:00
|
|
|
if (t->tm_zone) {
|
2016-11-20 12:22:10 +00:00
|
|
|
secs -= t->tm_gmtoff;
|
2013-06-30 23:43:08 +00:00
|
|
|
}
|
2014-01-07 15:35:52 +00:00
|
|
|
#endif
|
2016-11-20 12:22:10 +00:00
|
|
|
return secs;
|
2013-06-30 23:43:08 +00:00
|
|
|
} /* must be a valid time */
|
|
|
|
}
|
|
|
|
|
2014-11-03 03:35:15 +00:00
|
|
|
static const int SECSPERMIN = 60;
|
|
|
|
static const int SECSPERHOUR = 60 * SECSPERMIN;
|
|
|
|
static const int SECSPERDAY = 24 * SECSPERHOUR;
|
|
|
|
static const int YEAR_BASE = 1900;
|
|
|
|
static const int EPOCH_WDAY = 4;
|
|
|
|
static const int DAYSPERWEEK = 7;
|
|
|
|
static const int EPOCH_YEAR = 1970;
|
|
|
|
|
|
|
|
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
|
|
|
|
|
|
|
|
static const int year_lengths[2] = {
|
|
|
|
365,
|
|
|
|
366
|
2020-04-23 13:42:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const unsigned short int mon_yday[2][13] = {
|
|
|
|
/* Normal years. */
|
|
|
|
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
|
|
|
|
/* Leap years. */
|
|
|
|
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
|
|
|
|
};
|
2014-11-03 03:35:15 +00:00
|
|
|
|
2017-12-07 14:36:12 +00:00
|
|
|
static void secs2wday(const struct timeval &tv, struct tm *res)
|
|
|
|
{
|
|
|
|
long days, rem;
|
|
|
|
time_t lcltime;
|
|
|
|
|
|
|
|
/* base decision about std/dst time on current time */
|
|
|
|
lcltime = tv.tv_sec;
|
|
|
|
|
|
|
|
days = ((long) lcltime) / SECSPERDAY;
|
|
|
|
rem = ((long) lcltime) % SECSPERDAY;
|
|
|
|
while (rem < 0) {
|
|
|
|
rem += SECSPERDAY;
|
|
|
|
--days;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* compute day of week */
|
|
|
|
if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
|
|
|
|
res->tm_wday += DAYSPERWEEK;
|
|
|
|
}
|
|
|
|
|
2014-11-03 03:35:15 +00:00
|
|
|
struct tm *secs2tm(time_t *tim_p, struct tm *res)
|
|
|
|
{
|
|
|
|
long days, rem;
|
|
|
|
time_t lcltime;
|
|
|
|
int y;
|
|
|
|
int yleap;
|
2020-04-23 13:42:48 +00:00
|
|
|
const unsigned short int *ip;
|
2014-11-03 03:35:15 +00:00
|
|
|
|
|
|
|
/* base decision about std/dst time on current time */
|
|
|
|
lcltime = *tim_p;
|
|
|
|
|
|
|
|
days = ((long)lcltime) / SECSPERDAY;
|
|
|
|
rem = ((long)lcltime) % SECSPERDAY;
|
|
|
|
while (rem < 0)
|
|
|
|
{
|
|
|
|
rem += SECSPERDAY;
|
|
|
|
--days;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* compute hour, min, and sec */
|
|
|
|
res->tm_hour = (int) (rem / SECSPERHOUR);
|
|
|
|
rem %= SECSPERHOUR;
|
|
|
|
res->tm_min = (int) (rem / SECSPERMIN);
|
|
|
|
res->tm_sec = (int) (rem % SECSPERMIN);
|
|
|
|
|
|
|
|
/* compute day of week */
|
|
|
|
if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
|
|
|
|
res->tm_wday += DAYSPERWEEK;
|
|
|
|
|
|
|
|
/* compute year & day of year */
|
|
|
|
y = EPOCH_YEAR;
|
|
|
|
if (days >= 0)
|
|
|
|
{
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
yleap = isleap(y);
|
|
|
|
if (days < year_lengths[yleap])
|
|
|
|
break;
|
|
|
|
y++;
|
|
|
|
days -= year_lengths[yleap];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
--y;
|
|
|
|
yleap = isleap(y);
|
|
|
|
days += year_lengths[yleap];
|
|
|
|
} while (days < 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
res->tm_year = y - YEAR_BASE;
|
|
|
|
res->tm_yday = days;
|
2020-04-23 13:42:48 +00:00
|
|
|
ip = mon_yday[isleap(y)];
|
|
|
|
for (y = 11; days < (long int) ip[y]; --y)
|
|
|
|
continue;
|
|
|
|
days -= ip[y];
|
|
|
|
res->tm_mon = y;
|
2014-11-03 03:35:15 +00:00
|
|
|
res->tm_mday = days + 1;
|
|
|
|
|
|
|
|
res->tm_isdst = 0;
|
|
|
|
|
|
|
|
return (res);
|
|
|
|
}
|
|
|
|
|
2015-04-11 05:55:57 +00:00
|
|
|
bool next_format(const char * const fmt[], int &index, int &locked_index)
|
2013-06-30 23:43:08 +00:00
|
|
|
{
|
|
|
|
bool retval = true;
|
|
|
|
|
|
|
|
if (locked_index == -1) {
|
|
|
|
index += 1;
|
2020-11-10 06:17:17 +00:00
|
|
|
if (fmt[index] == nullptr) {
|
2013-06-30 23:43:08 +00:00
|
|
|
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,
|
2014-10-20 05:16:40 +00:00
|
|
|
size_t time_len,
|
2015-04-11 05:55:57 +00:00
|
|
|
const char * const time_fmt[],
|
2014-06-18 04:29:42 +00:00
|
|
|
struct exttm *tm_out,
|
2016-03-08 05:17:57 +00:00
|
|
|
struct timeval &tv_out,
|
|
|
|
bool convert_local)
|
2013-06-30 23:43:08 +00:00
|
|
|
{
|
|
|
|
int curr_time_fmt = -1;
|
|
|
|
bool found = false;
|
2020-11-10 06:17:17 +00:00
|
|
|
const char *retval = nullptr;
|
2013-06-30 23:43:08 +00:00
|
|
|
|
|
|
|
if (!time_fmt) {
|
2014-03-22 15:02:48 +00:00
|
|
|
time_fmt = PTIMEC_FORMAT_STR;
|
2013-06-30 23:43:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
while (next_format(time_fmt,
|
|
|
|
curr_time_fmt,
|
|
|
|
this->dts_fmt_lock)) {
|
|
|
|
*tm_out = this->dts_base_tm;
|
2014-11-03 14:07:36 +00:00
|
|
|
tm_out->et_flags = 0;
|
2017-05-13 13:53:58 +00:00
|
|
|
if (time_len > 1 &&
|
|
|
|
time_dest[0] == '+' &&
|
|
|
|
isdigit(time_dest[1])) {
|
2014-10-31 11:49:16 +00:00
|
|
|
char time_cp[time_len + 1];
|
2013-07-28 18:03:31 +00:00
|
|
|
int gmt_int, off;
|
|
|
|
|
2020-11-10 06:17:17 +00:00
|
|
|
retval = nullptr;
|
2014-10-31 11:49:16 +00:00
|
|
|
memcpy(time_cp, time_dest, time_len);
|
|
|
|
time_cp[time_len] = '\0';
|
|
|
|
if (sscanf(time_cp, "+%d%n", &gmt_int, &off) == 1) {
|
2013-07-28 18:03:31 +00:00
|
|
|
time_t gmt = gmt_int;
|
|
|
|
|
2016-03-08 05:17:57 +00:00
|
|
|
if (convert_local && this->dts_local_time) {
|
2014-06-18 04:29:42 +00:00
|
|
|
localtime_r(&gmt, &tm_out->et_tm);
|
2014-01-07 15:35:52 +00:00
|
|
|
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
2020-11-10 06:17:17 +00:00
|
|
|
tm_out->et_tm.tm_zone = nullptr;
|
2014-01-07 15:35:52 +00:00
|
|
|
#endif
|
2014-06-18 04:29:42 +00:00
|
|
|
tm_out->et_tm.tm_isdst = 0;
|
|
|
|
gmt = tm2sec(&tm_out->et_tm);
|
2013-07-28 18:03:31 +00:00
|
|
|
}
|
|
|
|
tv_out.tv_sec = gmt;
|
2014-03-15 11:40:58 +00:00
|
|
|
tv_out.tv_usec = 0;
|
2016-04-05 06:55:43 +00:00
|
|
|
tm_out->et_flags = ETF_DAY_SET|ETF_MONTH_SET|ETF_YEAR_SET|ETF_MACHINE_ORIENTED|ETF_EPOCH_TIME;
|
2013-07-28 18:03:31 +00:00
|
|
|
|
|
|
|
this->dts_fmt_lock = curr_time_fmt;
|
|
|
|
this->dts_fmt_len = off;
|
|
|
|
retval = time_dest + off;
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-03-22 15:02:48 +00:00
|
|
|
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
|
2015-08-04 15:39:32 +00:00
|
|
|
if (!this->dts_keep_base_tz) {
|
2020-11-10 06:17:17 +00:00
|
|
|
tm_out->et_tm.tm_zone = nullptr;
|
2015-08-04 15:39:32 +00:00
|
|
|
}
|
2014-03-22 15:02:48 +00:00
|
|
|
#endif
|
2018-02-05 06:21:18 +00:00
|
|
|
if (func(tm_out, time_dest, off, time_len)) {
|
2014-03-22 15:02:48 +00:00
|
|
|
retval = &time_dest[off];
|
|
|
|
|
2014-06-18 04:29:42 +00:00
|
|
|
if (tm_out->et_tm.tm_year < 70) {
|
|
|
|
tm_out->et_tm.tm_year = 80;
|
2014-03-22 15:02:48 +00:00
|
|
|
}
|
2016-04-03 03:58:20 +00:00
|
|
|
if (convert_local &&
|
|
|
|
(this->dts_local_time || tm_out->et_flags & ETF_EPOCH_TIME)) {
|
2014-06-18 04:29:42 +00:00
|
|
|
time_t gmt = tm2sec(&tm_out->et_tm);
|
2014-03-22 15:02:48 +00:00
|
|
|
|
2014-10-28 14:02:27 +00:00
|
|
|
this->to_localtime(gmt, *tm_out);
|
2014-03-22 15:02:48 +00:00
|
|
|
}
|
2014-06-18 04:29:42 +00:00
|
|
|
tv_out.tv_sec = tm2sec(&tm_out->et_tm);
|
|
|
|
tv_out.tv_usec = tm_out->et_nsec / 1000;
|
2017-12-07 14:36:12 +00:00
|
|
|
secs2wday(tv_out, &tm_out->et_tm);
|
2014-03-22 15:02:48 +00:00
|
|
|
|
|
|
|
this->dts_fmt_lock = curr_time_fmt;
|
|
|
|
this->dts_fmt_len = retval - time_dest;
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-04-11 05:55:57 +00:00
|
|
|
else {
|
|
|
|
off_t off = 0;
|
2013-07-02 13:34:48 +00:00
|
|
|
|
2016-11-04 18:00:51 +00:00
|
|
|
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
|
|
|
if (!this->dts_keep_base_tz) {
|
2020-11-10 06:17:17 +00:00
|
|
|
tm_out->et_tm.tm_zone = nullptr;
|
2016-11-04 18:00:51 +00:00
|
|
|
}
|
|
|
|
#endif
|
2016-11-22 16:38:45 +00:00
|
|
|
if (ptime_fmt(time_fmt[curr_time_fmt], tm_out, time_dest, off, time_len) &&
|
2018-03-28 01:54:50 +00:00
|
|
|
(time_dest[off] == '.' || time_dest[off] == ',' || off == (off_t)time_len)) {
|
2015-04-11 05:55:57 +00:00
|
|
|
retval = &time_dest[off];
|
|
|
|
if (tm_out->et_tm.tm_year < 70) {
|
|
|
|
tm_out->et_tm.tm_year = 80;
|
2013-07-02 13:34:48 +00:00
|
|
|
}
|
2016-04-03 03:58:20 +00:00
|
|
|
if (convert_local &&
|
|
|
|
(this->dts_local_time || tm_out->et_flags & ETF_EPOCH_TIME)) {
|
2015-04-11 05:55:57 +00:00
|
|
|
time_t gmt = tm2sec(&tm_out->et_tm);
|
2013-07-28 18:03:31 +00:00
|
|
|
|
2015-04-11 05:55:57 +00:00
|
|
|
this->to_localtime(gmt, *tm_out);
|
2014-01-07 15:35:52 +00:00
|
|
|
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
2020-11-10 06:17:17 +00:00
|
|
|
tm_out->et_tm.tm_zone = nullptr;
|
2014-01-07 15:35:52 +00:00
|
|
|
#endif
|
2015-04-11 05:55:57 +00:00
|
|
|
tm_out->et_tm.tm_isdst = 0;
|
|
|
|
}
|
2014-03-22 15:02:48 +00:00
|
|
|
|
2015-04-11 05:55:57 +00:00
|
|
|
tv_out.tv_sec = tm2sec(&tm_out->et_tm);
|
|
|
|
tv_out.tv_usec = tm_out->et_nsec / 1000;
|
2017-12-07 14:36:12 +00:00
|
|
|
secs2wday(tv_out, &tm_out->et_tm);
|
2013-06-30 23:43:08 +00:00
|
|
|
|
2015-04-11 05:55:57 +00:00
|
|
|
this->dts_fmt_lock = curr_time_fmt;
|
|
|
|
this->dts_fmt_len = retval - time_dest;
|
2013-06-30 23:43:08 +00:00
|
|
|
|
2015-04-11 05:55:57 +00:00
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
2013-06-30 23:43:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
2020-11-10 06:17:17 +00:00
|
|
|
retval = nullptr;
|
2013-06-30 23:43:08 +00:00
|
|
|
}
|
|
|
|
|
2020-11-10 06:17:17 +00:00
|
|
|
if (retval != nullptr) {
|
2013-07-28 18:03:31 +00:00
|
|
|
/* Try to pull out the milli/micro-second value. */
|
|
|
|
if (retval[0] == '.' || retval[0] == ',') {
|
2014-10-20 05:16:40 +00:00
|
|
|
off_t off = (retval - time_dest) + 1;
|
|
|
|
|
2015-05-17 12:15:41 +00:00
|
|
|
if (ptime_f(tm_out, time_dest, off, time_len)) {
|
|
|
|
tv_out.tv_usec = tm_out->et_nsec / 1000;
|
2014-10-20 05:16:40 +00:00
|
|
|
this->dts_fmt_len += 7;
|
2015-12-11 04:36:25 +00:00
|
|
|
retval += 7;
|
2014-10-20 05:16:40 +00:00
|
|
|
}
|
2016-08-19 05:15:35 +00:00
|
|
|
else if (ptime_L(tm_out, time_dest, off, time_len)) {
|
2015-05-17 12:15:41 +00:00
|
|
|
tv_out.tv_usec = tm_out->et_nsec / 1000;
|
2014-10-20 05:16:40 +00:00
|
|
|
this->dts_fmt_len += 4;
|
2015-12-11 04:36:25 +00:00
|
|
|
retval += 4;
|
2013-07-28 18:03:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-30 23:43:08 +00:00
|
|
|
return retval;
|
|
|
|
}
|
2014-02-01 14:41:11 +00:00
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
size_t strtonum(T &num_out, const char *string, size_t len)
|
|
|
|
{
|
|
|
|
size_t retval = 0;
|
|
|
|
T sign = 1;
|
|
|
|
|
|
|
|
num_out = 0;
|
|
|
|
|
|
|
|
for (; retval < len && isspace(string[retval]); retval++);
|
|
|
|
for (; retval < len && string[retval] == '-'; retval++) {
|
|
|
|
sign *= -1;
|
|
|
|
}
|
|
|
|
for (; retval < len && string[retval] == '+'; retval++);
|
|
|
|
for (; retval < len && isdigit(string[retval]); retval++) {
|
|
|
|
num_out *= 10;
|
|
|
|
num_out += string[retval] - '0';
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2014-02-01 17:53:29 +00:00
|
|
|
template
|
2014-02-01 14:41:11 +00:00
|
|
|
size_t strtonum<long long>(long long &num_out, const char *string, size_t len);
|
2014-02-01 17:53:29 +00:00
|
|
|
|
|
|
|
template
|
|
|
|
size_t strtonum<long>(long &num_out, const char *string, size_t len);
|
|
|
|
|
|
|
|
template
|
|
|
|
size_t strtonum<int>(int &num_out, const char *string, size_t len);
|
2015-03-16 16:16:49 +00:00
|
|
|
|
2020-10-21 05:55:46 +00:00
|
|
|
string build_path(const vector<ghc::filesystem::path> &paths)
|
2015-03-16 16:16:49 +00:00
|
|
|
{
|
|
|
|
string retval;
|
|
|
|
|
2019-07-30 05:18:32 +00:00
|
|
|
for (const auto &path : paths) {
|
|
|
|
if (path.empty()) {
|
2015-03-16 16:16:49 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!retval.empty()) {
|
|
|
|
retval += ":";
|
|
|
|
}
|
2020-10-21 05:55:46 +00:00
|
|
|
retval += path.string();
|
2015-03-16 16:16:49 +00:00
|
|
|
}
|
2020-11-10 06:17:17 +00:00
|
|
|
auto env_path = getenv_opt("PATH");
|
|
|
|
if (env_path) {
|
|
|
|
retval += ":" + string(*env_path);
|
|
|
|
}
|
2015-03-16 16:16:49 +00:00
|
|
|
return retval;
|
2015-03-28 20:46:42 +00:00
|
|
|
}
|
2015-11-27 06:19:53 +00:00
|
|
|
|
2020-10-21 05:55:46 +00:00
|
|
|
bool read_file(const ghc::filesystem::path &filename, string &out)
|
2015-11-27 06:19:53 +00:00
|
|
|
{
|
2020-10-21 05:55:46 +00:00
|
|
|
std::ifstream sql_file(filename.string());
|
2015-11-27 06:19:53 +00:00
|
|
|
|
|
|
|
if (sql_file) {
|
|
|
|
out.assign((std::istreambuf_iterator<char>(sql_file)),
|
|
|
|
std::istreambuf_iterator<char>());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2015-12-19 06:39:27 +00:00
|
|
|
|
2016-08-22 13:45:27 +00:00
|
|
|
size_t abbreviate_str(char *str, size_t len, size_t max_len)
|
|
|
|
{
|
|
|
|
size_t last_start = 1;
|
|
|
|
|
|
|
|
if (len < max_len) {
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t index = 0; index < len; index++) {
|
2016-08-23 06:28:14 +00:00
|
|
|
switch (str[index]) {
|
|
|
|
case '.':
|
|
|
|
case '-':
|
|
|
|
case '/':
|
|
|
|
case ':':
|
|
|
|
memmove(&str[last_start], &str[index], len - index);
|
|
|
|
len -= (index - last_start);
|
|
|
|
index = last_start + 1;
|
|
|
|
last_start = index + 1;
|
|
|
|
|
|
|
|
if (len < max_len) {
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
break;
|
2016-08-22 13:45:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
2019-07-30 05:18:32 +00:00
|
|
|
|
2020-10-21 05:55:46 +00:00
|
|
|
Result<std::pair<ghc::filesystem::path, int>, std::string>
|
|
|
|
open_temp_file(const ghc::filesystem::path &pattern)
|
2019-07-30 05:18:32 +00:00
|
|
|
{
|
2020-10-21 05:55:46 +00:00
|
|
|
auto pattern_str = pattern.string();
|
2019-07-30 05:18:32 +00:00
|
|
|
char pattern_copy[pattern_str.size() + 1];
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
strcpy(pattern_copy, pattern_str.c_str());
|
|
|
|
if ((fd = mkstemp(pattern_copy)) == -1) {
|
2020-11-10 06:17:17 +00:00
|
|
|
return Err(fmt::format("unable to create temporary file: {} -- {}",
|
|
|
|
pattern.string(), strerror(errno)));
|
2019-07-30 05:18:32 +00:00
|
|
|
}
|
|
|
|
|
2020-10-21 05:55:46 +00:00
|
|
|
return Ok(make_pair(ghc::filesystem::path(pattern_copy), fd));
|
2019-07-30 05:18:32 +00:00
|
|
|
}
|
2020-04-25 20:19:41 +00:00
|
|
|
|
|
|
|
bool is_dev_null(const struct stat &st)
|
|
|
|
{
|
|
|
|
struct stat null_stat;
|
|
|
|
|
|
|
|
stat("/dev/null", &null_stat);
|
|
|
|
|
|
|
|
return st.st_dev == null_stat.st_dev &&
|
|
|
|
st.st_ino == null_stat.st_ino;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_dev_null(int fd)
|
|
|
|
{
|
|
|
|
struct stat fd_stat;
|
|
|
|
|
|
|
|
fstat(fd, &fd_stat);
|
|
|
|
return is_dev_null(fd_stat);
|
|
|
|
}
|
2020-09-05 21:41:05 +00:00
|
|
|
|
|
|
|
std::string ok_prefix(std::string msg)
|
|
|
|
{
|
|
|
|
if (msg.empty()) {
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::string(ANSI_COLOR(COLOR_GREEN) "\u2714" ANSI_NORM " ") + msg;
|
|
|
|
}
|
|
|
|
|
2020-09-14 06:03:23 +00:00
|
|
|
std::string err_prefix(const std::string msg)
|
|
|
|
{
|
|
|
|
if (msg.empty()) {
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::string(ANSI_COLOR(COLOR_RED) "\u2718" ANSI_NORM " ") + msg;
|
|
|
|
}
|
|
|
|
|
2020-09-05 21:41:05 +00:00
|
|
|
Result<std::string, std::string> err_to_ok(const std::string msg)
|
|
|
|
{
|
2020-09-14 06:03:23 +00:00
|
|
|
return Ok(err_prefix(msg));
|
2020-09-05 21:41:05 +00:00
|
|
|
}
|