From db8a3c4d3842fc3f72ff4c787f5c49a8ab8ba15e Mon Sep 17 00:00:00 2001 From: Timothy Stack Date: Mon, 9 Nov 2020 22:17:17 -0800 Subject: [PATCH] [debt] doing some cleanup Defect Number: Reviewed By: Testing Done: --- src/CMakeLists.txt | 7 + src/Makefile.am | 4 + src/all_logs_vtab.cc | 109 +++ src/all_logs_vtab.hh | 96 +-- src/ansi_scrubber.cc | 24 +- src/archive_manager.cc | 5 +- src/archive_manager.hh | 2 +- src/attr_line.hh | 2 +- src/auto_fd.hh | 23 +- src/auto_mem.hh | 8 +- src/auto_pid.hh | 41 +- src/base/future_util.hh | 3 +- src/base/lnav_log.cc | 4 +- src/base/opt_util.hh | 7 + src/base/string_util.hh | 42 ++ src/bookmarks.hh | 6 +- src/collation-functions.cc | 3 - src/column_namer.cc | 2 +- src/command_executor.cc | 35 +- src/config.cmake.h.in | 4 + src/data_parser.cc | 1079 ++++++++++++++++++++++++++--- src/data_parser.hh | 841 +--------------------- src/db_sub_source.cc | 2 +- src/db_sub_source.hh | 20 +- src/environ_vtab.cc | 8 +- src/field_overlay_source.cc | 20 +- src/field_overlay_source.hh | 9 +- src/file_collection.cc | 419 +++++++++++ src/file_collection.hh | 97 +++ src/file_vtab.cc | 36 +- src/file_vtab.hh | 4 +- src/fs-extension-functions.cc | 9 +- src/fstat_vtab.cc | 8 - src/grep_proc.cc | 6 +- src/grep_proc.hh | 2 +- src/help_text_formatter.cc | 12 +- src/highlighter.cc | 28 + src/hist_source.cc | 2 +- src/hotkeys.cc | 9 +- src/line_buffer.cc | 8 +- src/line_buffer.hh | 2 +- src/listview_curses.cc | 2 +- src/listview_curses.hh | 4 +- src/lnav.cc | 449 ++---------- src/lnav.hh | 76 +- src/lnav_commands.cc | 70 +- src/lnav_util.cc | 98 +-- src/lnav_util.hh | 116 +--- src/log_actions.cc | 3 +- src/log_format.hh | 72 +- src/log_format_loader.cc | 33 +- src/log_format_loader.hh | 5 +- src/log_level_re.cc | 308 ++++---- src/log_level_re.re | 2 - src/log_search_table.hh | 2 +- src/log_vtab_impl.cc | 4 +- src/log_vtab_impl.hh | 4 +- src/logfile.cc | 5 +- src/logfile.hh | 2 +- src/pcrepp/pcrepp.cc | 4 +- src/piper_proc.cc | 4 - src/piper_proc.hh | 2 +- src/plain_text_source.hh | 16 +- src/preview_status_source.hh | 4 +- src/readline_curses.cc | 8 +- src/readline_highlighters.cc | 5 +- src/regexp_vtab.cc | 8 - src/relative_time.cc | 9 +- src/session_data.cc | 8 +- src/session_data.hh | 2 +- src/shlex.hh | 8 +- src/sql_util.cc | 5 +- src/sql_util.hh | 4 +- src/sqlite-extension-func.cc | 3 +- src/state-extension-functions.cc | 2 - src/string-extension-functions.cc | 1 - src/textview_curses.hh | 2 + src/top_status_source.hh | 2 +- src/top_sys_status_source.hh | 2 +- src/url_loader.hh | 17 +- src/view_helpers.cc | 3 +- src/view_helpers.hh | 61 ++ src/views_vtab.cc | 29 +- src/vt52_curses.cc | 3 - src/vt52_curses.hh | 1 - src/vtab_module.hh | 94 +-- src/xterm_mouse.hh | 2 +- test/drive_line_buffer.cc | 5 +- test/drive_sql.cc | 2 +- test/drive_sql_anno.cc | 2 +- test/test_auto_fd.cc | 3 +- 91 files changed, 2394 insertions(+), 2230 deletions(-) create mode 100644 src/all_logs_vtab.cc create mode 100644 src/file_collection.cc create mode 100644 src/file_collection.hh create mode 100644 src/view_helpers.hh diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8afa98fb..9155d6e7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -228,6 +228,7 @@ add_library(diag STATIC config.h + all_logs_vtab.cc ansi_scrubber.cc archive_manager.cc bin2c.h @@ -242,6 +243,7 @@ add_library(diag STATIC environ_vtab.cc extension-functions.cc field_overlay_source.cc + file_collection.cc file_vtab.cc files_sub_source.cc filter_observer.cc @@ -352,6 +354,7 @@ add_library(diag STATIC base/enum_util.hh base/future_util.hh field_overlay_source.hh + file_collection.hh file_vtab.hh files_sub_source.hh filter_observer.hh @@ -409,6 +412,7 @@ add_library(diag STATIC timer.hh top_status_source.hh url_loader.hh + view_helpers.hh views_vtab.hh vtab_module.hh yajlpp/yajlpp.hh @@ -470,6 +474,9 @@ add_executable(test_yajlpp yajlpp/test_yajlpp.cc) target_link_libraries(test_yajlpp diag ${lnav_LIBS}) add_test(NAME test_yajlpp COMMAND test_yajlpp) +add_executable(drive_json_op yajlpp/drive_json_op.cc) +target_link_libraries(drive_json_op diag ${lnav_LIBS}) + add_executable(lnav ${lnav_SRCS}) target_link_libraries(lnav diag) diff --git a/src/Makefile.am b/src/Makefile.am index af4b5e3d..f9d1473b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -268,6 +268,7 @@ noinst_HEADERS = \ elem_to_json.hh \ environ_vtab.hh \ field_overlay_source.hh \ + file_collection.hh \ file_vtab.hh \ files_sub_source.hh \ filter_observer.hh \ @@ -352,6 +353,7 @@ noinst_HEADERS = \ unique_path.hh \ url_loader.hh \ view_curses.hh \ + view_helpers.hh \ views_vtab.hh \ vt52_curses.hh \ vtab_module.hh \ @@ -381,6 +383,7 @@ nodist_libdiag_a_SOURCES = \ $(LNAV_BUILT_FILES) libdiag_a_SOURCES = \ + all_logs_vtab.cc \ ansi_scrubber.cc \ archive_manager.cc \ bookmarks.cc \ @@ -394,6 +397,7 @@ libdiag_a_SOURCES = \ environ_vtab.cc \ extension-functions.cc \ field_overlay_source.cc \ + file_collection.cc \ file_vtab.cc \ files_sub_source.cc \ filter_observer.cc \ diff --git a/src/all_logs_vtab.cc b/src/all_logs_vtab.cc new file mode 100644 index 00000000..f289fb1f --- /dev/null +++ b/src/all_logs_vtab.cc @@ -0,0 +1,109 @@ +/** + * 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. + */ + +#include "config.h" + +#include "all_logs_vtab.hh" + +all_logs_vtab::all_logs_vtab() + : log_vtab_impl(intern_string::lookup("all_logs")), + alv_value_name(intern_string::lookup("log_format")), + alv_msg_name(intern_string::lookup("log_msg_format")), + alv_schema_name(intern_string::lookup("log_msg_schema")) { +} + +void all_logs_vtab::get_columns(std::vector &cols) const +{ + cols.emplace_back(this->alv_value_name.get()); + cols.emplace_back(this->alv_msg_name.get()); + cols.emplace_back(this->alv_schema_name.get(), SQLITE3_TEXT, "", true); +} + +void all_logs_vtab::extract(std::shared_ptr lf, uint64_t line_number, + shared_buffer_ref &line, + std::vector &values) +{ + auto *format = lf->get_format(); + values.emplace_back(this->alv_value_name, format->get_name(), 0); + + std::vector sub_values; + + this->vi_attrs.clear(); + format->annotate(line_number, line, this->vi_attrs, sub_values, false); + + auto body = find_string_attr_range(this->vi_attrs, &textview_curses::SA_BODY); + if (body.lr_start == -1) { + body.lr_start = 0; + body.lr_end = line.length(); + } + + data_scanner ds(line, body.lr_start, body.lr_end); + data_parser dp(&ds); + + std::string str; + dp.dp_msg_format = &str; + dp.parse(); + + tmp_shared_buffer tsb(str.c_str()); + + values.emplace_back(this->alv_msg_name, tsb.tsb_ref, 1); + + this->alv_schema_manager.invalidate_refs(); + dp.dp_schema_id.to_string(this->alv_schema_buffer.data()); + shared_buffer_ref schema_ref; + schema_ref.share(this->alv_schema_manager, + this->alv_schema_buffer.data(), + data_parser::schema_id_t::STRING_SIZE - 1); + values.emplace_back(this->alv_schema_name, schema_ref, 2); +} + +bool all_logs_vtab::is_valid(log_cursor &lc, logfile_sub_source &lss) +{ + auto cl = lss.at(lc.lc_curr_line); + auto lf = lss.find(cl); + auto lf_iter = lf->begin() + cl; + + if (lf_iter->is_continued()) { + return false; + } + + return true; +} + +bool all_logs_vtab::next(log_cursor &lc, logfile_sub_source &lss) +{ + lc.lc_curr_line = lc.lc_curr_line + vis_line_t(1); + lc.lc_sub_index = 0; + + if (lc.is_eof()) { + return true; + } + + return this->is_valid(lc, lss); +} diff --git a/src/all_logs_vtab.hh b/src/all_logs_vtab.hh index d675ede2..f72e9b42 100644 --- a/src/all_logs_vtab.hh +++ b/src/all_logs_vtab.hh @@ -27,103 +27,39 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef LNAV_ALL_LOGS_VTAB_HH -#define LNAV_ALL_LOGS_VTAB_HH +#ifndef lnav_all_logs_vtab_hh +#define lnav_all_logs_vtab_hh + +#include #include "log_vtab_impl.hh" #include "data_parser.hh" +/** + * A virtual table that provides access to all log messages from all formats. + */ class all_logs_vtab : public log_vtab_impl { public: - all_logs_vtab() : log_vtab_impl(intern_string::lookup("all_logs")) { - this->alv_value_name = intern_string::lookup("log_format"); - this->alv_msg_name = intern_string::lookup("log_msg_format"); - this->alv_schema_name = intern_string::lookup("log_msg_schema"); - } + all_logs_vtab(); - void get_columns(std::vector &cols) const { - cols.emplace_back(this->alv_value_name.get()); - cols.emplace_back(this->alv_msg_name.get()); - cols.emplace_back(this->alv_schema_name.get(), SQLITE3_TEXT, "", true); - }; + void get_columns(std::vector &cols) const override; void extract(std::shared_ptr lf, uint64_t line_number, shared_buffer_ref &line, - std::vector &values) { - log_format *format = lf->get_format(); - values.emplace_back(this->alv_value_name, format->get_name(), 0); - - std::vector sub_values; - struct line_range body; - - this->vi_attrs.clear(); - format->annotate(line_number, line, this->vi_attrs, sub_values, false); - - body = find_string_attr_range(this->vi_attrs, &textview_curses::SA_BODY); - if (body.lr_start == -1) { - body.lr_start = 0; - body.lr_end = line.length(); - } - - data_scanner ds(line, body.lr_start, body.lr_end); - data_parser dp(&ds); - - std::string str; - dp.dp_msg_format = &str; - dp.parse(); - - tmp_shared_buffer tsb(str.c_str()); - - values.emplace_back(this->alv_msg_name, tsb.tsb_ref, 1); - - this->alv_schema_manager.invalidate_refs(); - dp.dp_schema_id.to_string(this->alv_schema_buffer); - shared_buffer_ref schema_ref; - schema_ref.share(this->alv_schema_manager, - this->alv_schema_buffer, - data_parser::schema_id_t::STRING_SIZE - 1); - values.emplace_back(this->alv_schema_name, schema_ref, 2); - } - - bool is_valid(log_cursor &lc, logfile_sub_source &lss) { - content_line_t cl(lss.at(lc.lc_curr_line)); - std::shared_ptr lf = lss.find(cl); - auto lf_iter = lf->begin() + cl; - - if (lf_iter->is_continued()) { - return false; - } - - return true; - }; - - bool next(log_cursor &lc, logfile_sub_source &lss) { - lc.lc_curr_line = lc.lc_curr_line + vis_line_t(1); - lc.lc_sub_index = 0; - - if (lc.is_eof()) { - return true; - } - - content_line_t cl(lss.at(lc.lc_curr_line)); - std::shared_ptr lf = lss.find(cl); - auto lf_iter = lf->begin() + cl; + std::vector &values) override; - if (lf_iter->is_continued()) { - return false; - } + bool is_valid(log_cursor &lc, logfile_sub_source &lss) override; - return true; - }; + bool next(log_cursor &lc, logfile_sub_source &lss) override; private: - intern_string_t alv_value_name; - intern_string_t alv_msg_name; - intern_string_t alv_schema_name; + const intern_string_t alv_value_name; + const intern_string_t alv_msg_name; + const intern_string_t alv_schema_name; shared_buffer alv_schema_manager; - char alv_schema_buffer[data_parser::schema_id_t::STRING_SIZE]; + std::array alv_schema_buffer{}; }; #endif //LNAV_ALL_LOGS_VTAB_HH diff --git a/src/ansi_scrubber.cc b/src/ansi_scrubber.cc index 6c5fc5dd..b802d88b 100644 --- a/src/ansi_scrubber.cc +++ b/src/ansi_scrubber.cc @@ -173,16 +173,16 @@ void scrub_ansi_string(std::string &str, string_attrs_t &sa) void add_ansi_vars(std::map &vars) { - vars["ansi_csi"] = "\x1b["; - vars["ansi_norm"] = "\x1b[0m"; - vars["ansi_bold"] = "\x1b[1m"; - vars["ansi_underline"] = "\x1b[4m"; - vars["ansi_black"] = "\x1b[30m"; - vars["ansi_red"] = "\x1b[31m"; - vars["ansi_green"] = "\x1b[32m"; - vars["ansi_yellow"] = "\x1b[33m"; - vars["ansi_blue"] = "\x1b[34m"; - vars["ansi_magenta"] = "\x1b[35m"; - vars["ansi_cyan"] = "\x1b[36m"; - vars["ansi_white"] = "\x1b[37m"; + vars["ansi_csi"] = ANSI_CSI; + vars["ansi_norm"] = ANSI_NORM; + vars["ansi_bold"] = ANSI_BOLD_START; + vars["ansi_underline"] = ANSI_UNDERLINE_START; + vars["ansi_black"] = ANSI_COLOR(COLOR_BLACK); + vars["ansi_red"] = ANSI_COLOR(COLOR_RED); + vars["ansi_green"] = ANSI_COLOR(COLOR_GREEN); + vars["ansi_yellow"] = ANSI_COLOR(COLOR_YELLOW); + vars["ansi_blue"] = ANSI_COLOR(COLOR_BLUE); + vars["ansi_magenta"] = ANSI_COLOR(COLOR_MAGENTA); + vars["ansi_cyan"] = ANSI_COLOR(COLOR_CYAN); + vars["ansi_white"] = ANSI_COLOR(COLOR_WHITE); } diff --git a/src/archive_manager.cc b/src/archive_manager.cc index c509561a..668c5c72 100644 --- a/src/archive_manager.cc +++ b/src/archive_manager.cc @@ -43,6 +43,7 @@ #include "auto_mem.hh" #include "fmt/format.h" #include "base/lnav_log.hh" +#include "lnav_util.hh" #include "archive_manager.hh" @@ -81,14 +82,14 @@ public: auto lock_path = archive_path; lock_path += ".lck"; - this->lh_fd = open(lock_path.c_str(), O_CREAT | O_RDWR, 0600); + this->lh_fd = openp(lock_path, O_CREAT | O_RDWR, 0600); log_perror(fcntl(this->lh_fd, F_SETFD, FD_CLOEXEC)); }; auto_fd lh_fd; }; -bool is_archive(const std::string &filename) +bool is_archive(const ghc::filesystem::path& filename) { #if HAVE_ARCHIVE_H auto_mem arc(archive_read_free); diff --git a/src/archive_manager.hh b/src/archive_manager.hh index 524abcba..219bcb8a 100644 --- a/src/archive_manager.hh +++ b/src/archive_manager.hh @@ -56,7 +56,7 @@ struct extract_progress { using extract_cb = std::function; -bool is_archive(const std::string &filename); +bool is_archive(const ghc::filesystem::path& filename); ghc::filesystem::path filename_to_tmp_path(const std::string &filename); diff --git a/src/attr_line.hh b/src/attr_line.hh index d62517b2..e4ec9647 100644 --- a/src/attr_line.hh +++ b/src/attr_line.hh @@ -182,7 +182,7 @@ struct string_attr { require(type); }; - string_attr() : sa_type(NULL) { }; + string_attr() : sa_type(nullptr) { }; bool operator<(const struct string_attr &rhs) const { diff --git a/src/auto_fd.hh b/src/auto_fd.hh index 674bb3b2..4e91bd75 100644 --- a/src/auto_fd.hh +++ b/src/auto_fd.hh @@ -61,7 +61,7 @@ public: { int retval, fd[2]; - require(fd != NULL); + require(af != nullptr); if ((retval = ::pipe(fd)) == 0) { af[0] = fd[0]; @@ -76,7 +76,7 @@ public: * * @param fd The file descriptor to be managed. */ - auto_fd(int fd = -1) + explicit auto_fd(int fd = -1) : af_fd(fd) { require(fd >= -1); @@ -89,7 +89,7 @@ public: * * @param af The source of the file descriptor. */ - auto_fd(auto_fd && af) + auto_fd(auto_fd && af) noexcept : af_fd(af.release()) { }; @@ -116,7 +116,7 @@ public: }; /** @return The file descriptor as a plain integer. */ - operator int() const { return this->af_fd; }; + operator int() const { return this->af_fd; }; /** * Replace the current descriptor with the given one. The current @@ -125,7 +125,7 @@ public: * @param fd The file descriptor to store in this object. * @return *this */ - auto_fd &operator =(int fd) + auto_fd &operator=(int fd) { require(fd >= -1); @@ -139,8 +139,7 @@ public: * @param af The old manager of the file descriptor. * @return *this */ - auto_fd &operator =(auto_fd & af) - { + auto_fd &operator=(auto_fd && af) noexcept { this->reset(af.release()); return *this; }; @@ -209,7 +208,7 @@ private: class auto_pipe { public: - auto_pipe(int child_fd = -1, int child_flags = O_RDONLY) + explicit auto_pipe(int child_fd = -1, int child_flags = O_RDONLY) : ap_child_flags(child_flags), ap_child_fd(child_fd) { switch (child_fd) { @@ -250,17 +249,17 @@ public: case 0: if (this->ap_child_flags == O_RDONLY) { this->write_end().reset(); - if (this->read_end() == -1) { + if (this->read_end().get() == -1) { this->read_end() = ::open("/dev/null", O_RDONLY); } - new_fd = this->read_end(); + new_fd = this->read_end().get(); } else { this->read_end().reset(); - if (this->write_end() == -1) { + if (this->write_end().get() == -1) { this->write_end() = ::open("/dev/null", O_WRONLY); } - new_fd = this->write_end(); + new_fd = this->write_end().get(); } if (this->ap_child_fd != -1) { if (new_fd != this->ap_child_fd) { diff --git a/src/auto_mem.hh b/src/auto_mem.hh index e0bb66e7..8c50800e 100644 --- a/src/auto_mem.hh +++ b/src/auto_mem.hh @@ -70,7 +70,7 @@ public: this->reset(); }; - operator T *(void) const { return this->am_ptr; }; + operator T *() const { return this->am_ptr; }; auto_mem &operator =(T *ptr) { @@ -85,7 +85,7 @@ public: return *this; }; - T *release(void) + T *release() { T *retval = this->am_ptr; @@ -93,12 +93,12 @@ public: return retval; }; - T *in(void) const + T *in() const { return this->am_ptr; }; - T **out(void) + T **out() { this->reset(); return &this->am_ptr; diff --git a/src/auto_pid.hh b/src/auto_pid.hh index 90359f31..656be56a 100644 --- a/src/auto_pid.hh +++ b/src/auto_pid.hh @@ -39,49 +39,59 @@ class auto_pid { public: - auto_pid(pid_t child = -1) : ap_child(child), ap_status(0) {}; + explicit auto_pid(pid_t child = -1) : ap_child(child) + {}; - auto_pid(auto_pid &other) : ap_child(other.release()), ap_status(0) { }; + auto_pid(const auto_pid &other) = delete; - ~auto_pid() { this->reset(); }; + auto_pid(auto_pid &&other) noexcept : ap_child(other.release()) + {}; - auto_pid &operator =(auto_pid &other) { + ~auto_pid() + { this->reset(); }; + + auto_pid &operator=(auto_pid &&other) noexcept { this->reset(other.release()); this->ap_status = other.ap_status; return *this; }; - bool in_child() const { + bool in_child() const + { return this->ap_child == 0; }; - pid_t release() { + pid_t release() + { pid_t retval = this->ap_child; this->ap_child = -1; return retval; }; - int status() const { + int status() const + { return this->ap_status; }; - bool was_normal_exit() const { + bool was_normal_exit() const + { return WIFEXITED(this->ap_status); } - int exit_status() const { + int exit_status() const + { return WEXITSTATUS(this->ap_status); } - bool wait_for_child(int options = 0) { + bool wait_for_child(int options = 0) + { if (this->ap_child != -1) { int rc; while ((rc = waitpid(this->ap_child, &this->ap_status, - options)) < 0 && (errno == EINTR)) { - ; + options)) < 0 && (errno == EINTR)) { ; } if (rc > 0) { this->ap_child = -1; @@ -91,7 +101,8 @@ public: return this->ap_child == -1; }; - void reset(pid_t child = -1) { + void reset(pid_t child = -1) + { if (this->ap_child != child) { this->ap_status = 0; if (this->ap_child != -1) { @@ -103,10 +114,8 @@ public: }; private: - auto_pid(const auto_pid &other) { }; - pid_t ap_child; - int ap_status; + int ap_status{0}; }; #endif diff --git a/src/base/future_util.hh b/src/base/future_util.hh index 13d100f6..1c9a88e1 100644 --- a/src/base/future_util.hh +++ b/src/base/future_util.hh @@ -30,6 +30,7 @@ #ifndef lnav_future_util_hh #define lnav_future_util_hh +#include #include template @@ -43,7 +44,7 @@ std::future> make_ready_future( T&& t ) { template class future_queue { public: - future_queue(std::function processor) + explicit future_queue(std::function processor) : fq_processor(processor) {}; ~future_queue() { diff --git a/src/base/lnav_log.cc b/src/base/lnav_log.cc index 92fdae98..529fef6c 100644 --- a/src/base/lnav_log.cc +++ b/src/base/lnav_log.cc @@ -103,6 +103,8 @@ nonstd::optional lnav_log_file; lnav_log_level_t lnav_log_level = lnav_log_level_t::DEBUG; const char *lnav_log_crash_dir; nonstd::optional lnav_log_orig_termios; +// NOTE: This mutex is leaked so that it is not destroyed during exit. +// Otherwise, any attempts to log will fail. static std::mutex *lnav_log_mutex = new std::mutex(); std::vector log_state_dumper::DUMPER_LIST; @@ -111,7 +113,7 @@ std::vector log_crash_recoverer::CRASH_LIST; struct thid { static uint32_t COUNTER; - thid() : t_id(COUNTER++) {} + thid() noexcept : t_id(COUNTER++) {} uint32_t t_id; }; diff --git a/src/base/opt_util.hh b/src/base/opt_util.hh index e1a5c62c..bdaf52d6 100644 --- a/src/base/opt_util.hh +++ b/src/base/opt_util.hh @@ -30,6 +30,9 @@ #ifndef lnav_opt_util_hh #define lnav_opt_util_hh +#include + +#include "intern_string.hh" #include "optional.hpp" namespace detail { @@ -82,4 +85,8 @@ nonstd::optional cget(const C &container, size_t index) return nonstd::nullopt; } +inline nonstd::optional getenv_opt(const char *name) { + return make_optional_from_nullable(getenv(name)); +} + #endif diff --git a/src/base/string_util.hh b/src/base/string_util.hh index 13976330..e2ca9178 100644 --- a/src/base/string_util.hh +++ b/src/base/string_util.hh @@ -74,4 +74,46 @@ inline bool endswith(const std::string& str, const char (&suffix) [N]) void truncate_to(std::string &str, size_t len); +inline std::string trim(const std::string &str) +{ + std::string::size_type start, end; + + for (start = 0; start < str.size() && isspace(str[start]); start++); + for (end = str.size(); end > 0 && isspace(str[end - 1]); end--); + + return str.substr(start, end - start); +} + +inline std::string tolower(const char *str) +{ + std::string retval; + + for (int lpc = 0; str[lpc]; lpc++) { + retval.push_back(::tolower(str[lpc])); + } + + return retval; +} + +inline std::string tolower(const std::string &str) +{ + return tolower(str.c_str()); +} + +inline std::string toupper(const char *str) +{ + std::string retval; + + for (int lpc = 0; str[lpc]; lpc++) { + retval.push_back(::toupper(str[lpc])); + } + + return retval; +} + +inline std::string toupper(const std::string &str) +{ + return toupper(str.c_str()); +} + #endif diff --git a/src/bookmarks.hh b/src/bookmarks.hh index 75cf06bc..1069c915 100644 --- a/src/bookmarks.hh +++ b/src/bookmarks.hh @@ -68,7 +68,7 @@ struct bookmark_metadata { return retval; }; - bool empty() { + bool empty() const { return this->bm_name.empty() && this->bm_comment.empty() && this->bm_tags.empty(); @@ -189,7 +189,7 @@ public: return all_types; }; - bookmark_type_t(std::string name) : bt_name(std::move(name)) { + explicit bookmark_type_t(std::string name) : bt_name(std::move(name)) { get_all_types().push_back(this); }; @@ -199,7 +199,7 @@ public: private: struct mark_eq { - mark_eq(const std::string &name) : me_name(name) { }; + explicit mark_eq(const std::string &name) : me_name(name) { }; bool operator()(bookmark_type_t *bt) { return bt->bt_name == this->me_name; diff --git a/src/collation-functions.cc b/src/collation-functions.cc index 1caf7dd0..aeaaec85 100644 --- a/src/collation-functions.cc +++ b/src/collation-functions.cc @@ -31,7 +31,6 @@ #include "config.h" -#include #include #include @@ -39,8 +38,6 @@ #include #include -#include - #include "log_level.hh" #include "strnatcmp.h" diff --git a/src/column_namer.cc b/src/column_namer.cc index 44d358b6..d38b229f 100644 --- a/src/column_namer.cc +++ b/src/column_namer.cc @@ -33,8 +33,8 @@ #include +#include "base/string_util.hh" #include "sql_util.hh" -#include "lnav_util.hh" #include "base/lnav_log.hh" #include "column_namer.hh" diff --git a/src/command_executor.cc b/src/command_executor.cc index a39f97e3..dc5a9013 100644 --- a/src/command_executor.cc +++ b/src/command_executor.cc @@ -31,6 +31,7 @@ #include +#include "base/string_util.hh" #include "yajlpp/json_ptr.hh" #include "pcrecpp.h" #include "lnav.hh" @@ -48,9 +49,12 @@ exec_context INIT_EXEC_CONTEXT; bookmark_type_t BM_QUERY("query"); -static const string MSG_FORMAT_STMT = - "SELECT count(*) as total, min(log_line) as log_line, log_msg_format " - "FROM all_logs GROUP BY log_msg_format ORDER BY total desc"; +static const string MSG_FORMAT_STMT = R"( +SELECT count(*) AS total, min(log_line) AS log_line, log_msg_format + FROM all_logs + GROUP BY log_msg_format + ORDER BY total DESC +)"; int sql_progress(const struct log_cursor &lc) { @@ -147,19 +151,19 @@ Result execute_sql(exec_context &ec, const string &sql, string & sql_progress_finished, source.first, source.second); - gettimeofday(&start_tv, NULL); + gettimeofday(&start_tv, nullptr); retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(), stmt_str.c_str(), -1, stmt.out(), - NULL); + nullptr); if (retcode != SQLITE_OK) { const char *errmsg = sqlite3_errmsg(lnav_data.ld_db); alt_msg = ""; return ec.make_error("{}", errmsg); } - else if (stmt == NULL) { + else if (stmt == nullptr) { alt_msg = ""; return ec.make_error("No statement given"); } @@ -215,11 +219,11 @@ Result execute_sql(exec_context &ec, const string &sql, string & global_var->second.c_str(), -1, SQLITE_TRANSIENT); } - else if ((env_value = getenv(&name[1])) != NULL) { + else if ((env_value = getenv(&name[1])) != nullptr) { sqlite3_bind_text(stmt.in(), lpc + 1, env_value, -1, SQLITE_STATIC); } } - else if (name[0] == ':' && ec.ec_line_values != NULL) { + else if (name[0] == ':' && ec.ec_line_values != nullptr) { vector &lvalues = *ec.ec_line_values; vector::iterator iter; @@ -256,7 +260,7 @@ Result execute_sql(exec_context &ec, const string &sql, string & } } - if (lnav_data.ld_rl_view != NULL) { + if (lnav_data.ld_rl_view != nullptr) { lnav_data.ld_rl_view->set_value("Executing query: " + sql + " ..."); } @@ -305,7 +309,7 @@ Result execute_sql(exec_context &ec, const string &sql, string & } } - gettimeofday(&end_tv, NULL); + gettimeofday(&end_tv, nullptr); if (retcode == SQLITE_DONE) { lnav_data.ld_filter_view.reload_data(); lnav_data.ld_files_view.reload_data(); @@ -379,6 +383,8 @@ Result execute_sql(exec_context &ec, const string &sql, string & ensure_view(&lnav_data.ld_views[LNV_DB]); } } + } else { + lnav_data.ld_log_source.text_filters_changed(); } #endif } @@ -777,7 +783,8 @@ future pipe_callback(exec_context &ec, const string &cmdline, auto_fd &f }); } else { auto pp = make_shared( - fd, false, open_temp_file(system_tmpdir() / "lnav.out.XXXXXX") + fd, false, open_temp_file(ghc::filesystem::temp_directory_path() / + "lnav.out.XXXXXX") .then([](auto pair) { ghc::filesystem::remove(pair.first); }) @@ -801,11 +808,7 @@ future pipe_callback(exec_context &ec, const string &cmdline, auto_fd &f HELP_MSG_1(X, "to close the file")); } - packaged_task task([]() { return ""; }); - - task(); - - return task.get_future(); + return make_ready_future(std::string()); } } diff --git a/src/config.cmake.h.in b/src/config.cmake.h.in index 47b341d7..2821aeba 100644 --- a/src/config.cmake.h.in +++ b/src/config.cmake.h.in @@ -2,6 +2,8 @@ #define HAVE_PCRE_H #define HAVE_NCURSESW_CURSES_H +#define HAVE_LIBCURL + #cmakedefine SIZEOF_OFF_T @SIZEOF_OFF_T@ #cmakedefine VCS_PACKAGE_STRING "@VCS_PACKAGE_STRING@" @@ -10,6 +12,8 @@ #cmakedefine HAVE_UTIL_H +#define HAVE_SQLITE3_STMT_READONLY + #define _XOPEN_SOURCE_EXTENDED 1 #define PACKAGE_BUGREPORT "lnav@googlegroups.com" diff --git a/src/data_parser.cc b/src/data_parser.cc index 6e533193..81629a08 100644 --- a/src/data_parser.cc +++ b/src/data_parser.cc @@ -37,6 +37,643 @@ data_format data_parser::FORMAT_SEMI("semi", DT_COMMA, DT_SEMI); data_format data_parser::FORMAT_COMMA("comma", DT_INVALID, DT_COMMA); data_format data_parser::FORMAT_PLAIN("plain", DT_INVALID, DT_INVALID); +data_parser::data_parser(data_scanner *ds) + : dp_errors("dp_errors", __FILE__, __LINE__), + dp_pairs("dp_pairs", __FILE__, __LINE__), + dp_msg_format(nullptr), + dp_msg_format_begin(ds->get_input().pi_offset), + dp_scanner(ds) +{ + if (TRACE_FILE != nullptr) { + fprintf(TRACE_FILE, "input %s\n", ds->get_input().get_string()); + } +} + +void data_parser::pairup(data_parser::schema_id_t *schema, + data_parser::element_list_t &pairs_out, + data_parser::element_list_t &in_list, int group_depth) +{ + element_list_t ELEMENT_LIST_T(el_stack), ELEMENT_LIST_T(free_row), + ELEMENT_LIST_T(key_comps), ELEMENT_LIST_T(value), + ELEMENT_LIST_T(prefix); + SpookyHash context; + + require(in_list.el_format.df_name != nullptr); + + POINT_TRACE("pairup_start"); + + FORMAT_TRACE(in_list); + + for (auto iter = in_list.begin(); + iter != in_list.end(); + ++iter) { + if (iter->e_token == DNT_GROUP) { + element_list_t ELEMENT_LIST_T(group_pairs); + + this->pairup(nullptr, group_pairs, *iter->e_sub_elements, + group_depth + 1); + if (!group_pairs.empty()) { + iter->assign_elements(group_pairs); + } + } + + if (in_list.el_format.df_prefix_terminator != DT_INVALID) { + if (iter->e_token == in_list.el_format.df_prefix_terminator) { + in_list.el_format.df_prefix_terminator = DT_INVALID; + } else { + el_stack.PUSH_BACK(*iter); + } + } else if (iter->e_token == in_list.el_format.df_terminator) { + this->end_of_value(el_stack, key_comps, value, in_list, + group_depth); + + key_comps.PUSH_BACK(*iter); + } else if (iter->e_token == in_list.el_format.df_qualifier) { + value.SPLICE(value.end(), + key_comps, + key_comps.begin(), + key_comps.end()); + strip(value, element_if(DT_WHITE)); + if (!value.empty()) { + el_stack.PUSH_BACK(element(value, DNT_VALUE)); + } + } else if (iter->e_token == in_list.el_format.df_separator) { + auto key_iter = key_comps.end(); + bool found = false, key_is_values = true; + + if (!key_comps.empty()) { + do { + --key_iter; + if (key_iter->e_token == + in_list.el_format.df_appender) { + ++key_iter; + value.SPLICE(value.end(), + key_comps, + key_comps.begin(), + key_iter); + key_comps.POP_FRONT(); + found = true; + } else if (key_iter->e_token == + in_list.el_format.df_terminator) { + std::vector key_copy; + + value.SPLICE(value.end(), + key_comps, + key_comps.begin(), + key_iter); + key_comps.POP_FRONT(); + strip(key_comps, element_if(DT_WHITE)); + if (key_comps.empty()) { + key_iter = key_comps.end(); + } else { + key_iter = key_comps.begin(); + } + found = true; + } + if (key_iter != key_comps.end()) { + switch (key_iter->e_token) { + case DT_WORD: + case DT_SYMBOL: + key_is_values = false; + break; + default: + break; + } + } + } while (key_iter != key_comps.begin() && !found); + } + if (!found && !el_stack.empty() && !key_comps.empty()) { + element_list_t::iterator value_iter; + + if (el_stack.size() > 1 && + in_list.el_format.df_appender != DT_INVALID && + in_list.el_format.df_terminator != DT_INVALID) { + /* If we're expecting a terminator and haven't found it */ + /* then this is part of the value. */ + continue; + } + + value.SPLICE(value.end(), + key_comps, + key_comps.begin(), + key_comps.end()); + value_iter = value.end(); + std::advance(value_iter, -1); + key_comps.SPLICE(key_comps.begin(), + value, + value_iter, + value.end()); + key_comps.resize(1); + } + + strip(value, element_if(DT_WHITE)); + value.remove_if(element_if(DT_COMMA)); + if (!value.empty()) { + el_stack.PUSH_BACK(element(value, DNT_VALUE)); + } + strip(key_comps, element_if(DT_WHITE)); + if (!key_comps.empty()) { + if (key_is_values) { + el_stack.PUSH_BACK(element(key_comps, DNT_VALUE)); + } else { + el_stack.PUSH_BACK(element(key_comps, DNT_KEY, false)); + } + } + key_comps.CLEAR(); + value.CLEAR(); + } else { + key_comps.PUSH_BACK(*iter); + } + + POINT_TRACE("pairup_loop"); + } + + POINT_TRACE("pairup_eol"); + + CONSUMED_TRACE(in_list); + + // Only perform the free-row logic at the top level, if we're in a group + // assume it is a list. + if (group_depth < 1 && el_stack.empty()) { + free_row.SPLICE(free_row.begin(), + key_comps, key_comps.begin(), key_comps.end()); + } else { + this->end_of_value(el_stack, key_comps, value, in_list, group_depth); + } + + POINT_TRACE("pairup_stack"); + + context.Init(0, 0); + while (!el_stack.empty()) { + auto kv_iter = el_stack.begin(); + if (kv_iter->e_token == DNT_VALUE) { + if (pairs_out.empty()) { + free_row.PUSH_BACK(el_stack.front()); + } else { + element_list_t ELEMENT_LIST_T(free_pair_subs); + struct element blank; + + blank.e_capture.c_begin = blank.e_capture.c_end = + el_stack.front().e_capture. + c_begin; + blank.e_token = DNT_KEY; + free_pair_subs.PUSH_BACK(blank); + free_pair_subs.PUSH_BACK(el_stack.front()); + pairs_out.PUSH_BACK(element(free_pair_subs, DNT_PAIR)); + } + } + if (kv_iter->e_token != DNT_KEY) { + el_stack.POP_FRONT(); + continue; + } + + ++kv_iter; + if (kv_iter == el_stack.end()) { + el_stack.POP_FRONT(); + continue; + } + + element_list_t ELEMENT_LIST_T(pair_subs); + + if (schema != nullptr) { + size_t key_len; + const char *key_val = + this->get_element_string(el_stack.front(), key_len); + context.Update(key_val, key_len); + } + + while (!free_row.empty()) { + element_list_t ELEMENT_LIST_T(free_pair_subs); + struct element blank; + + blank.e_capture.c_begin = blank.e_capture.c_end = + free_row.front().e_capture. + c_begin; + blank.e_token = DNT_KEY; + free_pair_subs.PUSH_BACK(blank); + free_pair_subs.PUSH_BACK(free_row.front()); + pairs_out.PUSH_BACK(element(free_pair_subs, DNT_PAIR)); + free_row.POP_FRONT(); + } + + bool has_value = false; + + if (kv_iter->e_token == DNT_VALUE) { + ++kv_iter; + has_value = true; + } + + pair_subs.SPLICE(pair_subs.begin(), + el_stack, + el_stack.begin(), + kv_iter); + + if (!has_value) { + element_list_t ELEMENT_LIST_T(blank_value); + pcre_input &pi = this->dp_scanner->get_input(); + const char *str = pi.get_string(); + struct element blank; + + blank.e_token = DT_QUOTED_STRING; + blank.e_capture.c_begin = blank.e_capture.c_end = + pair_subs.front().e_capture.c_end; + if ((blank.e_capture.c_begin >= 0) && + ((size_t) blank.e_capture.c_begin < pi.pi_length)) { + switch (str[blank.e_capture.c_begin]) { + case '=': + case ':': + blank.e_capture.c_begin += 1; + blank.e_capture.c_end += 1; + break; + } + } + blank_value.PUSH_BACK(blank); + pair_subs.PUSH_BACK(element(blank_value, DNT_VALUE)); + } + + pairs_out.PUSH_BACK(element(pair_subs, DNT_PAIR)); + } + + if (pairs_out.size() == 1) { + element &pair = pairs_out.front(); + element &evalue = pair.e_sub_elements->back(); + + if (evalue.e_token == DNT_VALUE && + evalue.e_sub_elements != nullptr && + evalue.e_sub_elements->size() > 1) { + element_list_t::iterator next_sub; + + next_sub = pair.e_sub_elements->begin(); + ++next_sub; + prefix.SPLICE(prefix.begin(), + *pair.e_sub_elements, + pair.e_sub_elements->begin(), + next_sub); + free_row.CLEAR(); + free_row.SPLICE(free_row.begin(), + *evalue.e_sub_elements, + evalue.e_sub_elements->begin(), + evalue.e_sub_elements->end()); + pairs_out.CLEAR(); + context.Init(0, 0); + } + } + + if (group_depth >= 1 && pairs_out.empty() && !free_row.empty()) { + pairs_out.SWAP(free_row); + } + + if (pairs_out.empty() && !free_row.empty()) { + while (!free_row.empty()) { + switch (free_row.front().e_token) { + case DNT_GROUP: + case DNT_VALUE: + case DT_EMAIL: + case DT_CONSTANT: + case DT_NUMBER: + case DT_SYMBOL: + case DT_HEX_NUMBER: + case DT_OCTAL_NUMBER: + case DT_VERSION_NUMBER: + case DT_QUOTED_STRING: + case DT_IPV4_ADDRESS: + case DT_IPV6_ADDRESS: + case DT_MAC_ADDRESS: + case DT_HEX_DUMP: + case DT_XML_OPEN_TAG: + case DT_XML_CLOSE_TAG: + case DT_XML_EMPTY_TAG: + case DT_UUID: + case DT_URL: + case DT_PATH: + case DT_DATE: + case DT_TIME: + case DT_PERCENTAGE: { + element_list_t ELEMENT_LIST_T(pair_subs); + struct element blank; + + blank.e_capture.c_begin = blank.e_capture.c_end = + free_row.front().e_capture. + c_begin; + blank.e_token = DNT_KEY; + pair_subs.PUSH_BACK(blank); + pair_subs.PUSH_BACK(free_row.front()); + pairs_out.PUSH_BACK(element(pair_subs, DNT_PAIR)); + + // Throw something into the hash so that the number of + // columns is significant. I don't think we want to + // use the token ID since some columns values might vary + // between rows. + context.Update(" ", 1); + } + break; + + default: { + size_t key_len; + const char *key_val = this->get_element_string( + free_row.front(), key_len); + + context.Update(key_val, key_len); + } + break; + } + + free_row.POP_FRONT(); + } + } + + if (!prefix.empty()) { + element_list_t ELEMENT_LIST_T(pair_subs); + struct element blank; + + blank.e_capture.c_begin = blank.e_capture.c_end = + prefix.front().e_capture.c_begin; + blank.e_token = DNT_KEY; + pair_subs.PUSH_BACK(blank); + pair_subs.PUSH_BACK(prefix.front()); + pairs_out.PUSH_FRONT(element(pair_subs, DNT_PAIR)); + } + + if (schema != nullptr) { + context.Final(schema->out(0), schema->out(1)); + } + + if (schema != nullptr && this->dp_msg_format != nullptr) { + pcre_input &pi = this->dp_scanner->get_input(); + for (auto &fiter : pairs_out) { + *(this->dp_msg_format) += this->get_string_up_to_value(fiter); + this->dp_msg_format->append("#"); + } + if ((size_t) this->dp_msg_format_begin < pi.pi_length) { + const char *str = pi.get_string(); + pcre_context::capture_t last(this->dp_msg_format_begin, + pi.pi_length); + + switch (str[last.c_begin]) { + case '\'': + case '"': + last.c_begin += 1; + break; + } + *(this->dp_msg_format) += pi.get_substr(&last); + } + } + + if (pairs_out.size() > 1000) { + pairs_out.resize(1000); + } +} + +void data_parser::discover_format() +{ + pcre_context_static<30> pc; + std::stack state_stack; + struct element elem; + + this->dp_group_token.push_back(DT_INVALID); + this->dp_group_stack.resize(1); + + state_stack.push(discover_format_state()); + while (this->dp_scanner->tokenize2(pc, elem.e_token)) { + pcre_context::iterator pc_iter; + + pc_iter = std::find_if(pc.begin(), pc.end(), capture_if_not(-1)); + require(pc_iter != pc.end()); + + elem.e_capture = *pc_iter; + + require(elem.e_capture.c_begin != -1); + require(elem.e_capture.c_end != -1); + + state_stack.top().update_for_element(elem); + switch (elem.e_token) { + case DT_LPAREN: + case DT_LANGLE: + case DT_LCURLY: + case DT_LSQUARE: + this->dp_group_token.push_back(elem.e_token); + this->dp_group_stack.emplace_back("_anon_", __FILE__, __LINE__); + state_stack.push(discover_format_state()); + break; + + case DT_EMPTY_CONTAINER: { + auto &curr_group = this->dp_group_stack.back(); + auto empty_list = element_list_t("_anon_", __FILE__, __LINE__); + discover_format_state dfs; + + dfs.finalize(); + + empty_list.el_format = dfs.dfs_format; + curr_group.PUSH_BACK(element()); + + auto &empty = curr_group.back(); + empty.e_capture.c_begin = elem.e_capture.c_begin + 1; + empty.e_capture.c_end = elem.e_capture.c_begin + 1; + empty.e_token = DNT_GROUP; + empty.assign_elements(empty_list); + break; + } + + case DT_RPAREN: + case DT_RANGLE: + case DT_RCURLY: + case DT_RSQUARE: + if (this->dp_group_token.back() == (elem.e_token - 1)) { + this->dp_group_token.pop_back(); + + auto riter = this->dp_group_stack.rbegin(); + ++riter; + state_stack.top().finalize(); + this->dp_group_stack.back().el_format = + state_stack.top().dfs_format; + state_stack.pop(); + if (!this->dp_group_stack.back().empty()) { + (*riter).PUSH_BACK(element(this->dp_group_stack.back(), + DNT_GROUP)); + } else { + (*riter).PUSH_BACK(element()); + riter->back().e_capture.c_begin = elem.e_capture.c_begin; + riter->back().e_capture.c_end = elem.e_capture.c_begin; + riter->back().e_token = DNT_GROUP; + riter->back().assign_elements( + this->dp_group_stack.back()); + } + this->dp_group_stack.pop_back(); + } else { + this->dp_group_stack.back().PUSH_BACK(elem); + } + break; + + default: + this->dp_group_stack.back().PUSH_BACK(elem); + break; + } + } + + while (this->dp_group_stack.size() > 1) { + this->dp_group_token.pop_back(); + + auto riter = this->dp_group_stack.rbegin(); + ++riter; + if (!this->dp_group_stack.back().empty()) { + state_stack.top().finalize(); + this->dp_group_stack.back().el_format = state_stack.top().dfs_format; + state_stack.pop(); + (*riter).PUSH_BACK(element(this->dp_group_stack.back(), + DNT_GROUP)); + } + this->dp_group_stack.pop_back(); + } + + state_stack.top().finalize(); + this->dp_group_stack.back().el_format = state_stack.top().dfs_format; +} + +void data_parser::end_of_value(data_parser::element_list_t &el_stack, + data_parser::element_list_t &key_comps, + data_parser::element_list_t &value, + const data_parser::element_list_t &in_list, + int group_depth) +{ + key_comps.remove_if(element_if(in_list.el_format.df_terminator)); + key_comps.remove_if(element_if(DT_COMMA)); + value.remove_if(element_if(in_list.el_format.df_terminator)); + value.remove_if(element_if(DT_COMMA)); + strip(key_comps, element_if(DT_WHITE)); + strip(value, element_if(DT_WHITE)); + if ((el_stack.empty() || el_stack.back().e_token != DNT_KEY) && + value.empty() && key_comps.size() > 1 && + (key_comps.front().e_token == DT_WORD || + key_comps.front().e_token == DT_SYMBOL)) { + element_list_t::iterator key_iter, key_end; + bool found_value = false; + int word_count = 0; + key_iter = key_comps.begin(); + key_end = key_comps.begin(); + for (; key_iter != key_comps.end(); ++key_iter) { + if (key_iter->e_token == DT_WORD || + key_iter->e_token == DT_SYMBOL) { + word_count += 1; + if (found_value) { + key_end = key_comps.begin(); + } + } else if (key_iter->e_token == DT_WHITE) { + + } else { + if (!found_value) { + key_end = key_iter; + } + found_value = true; + } + } + if (word_count != 1) { + key_end = key_comps.begin(); + } + value.SPLICE(value.end(), + key_comps, + key_end, + key_comps.end()); + strip(key_comps, element_if(DT_WHITE)); + if (!key_comps.empty()) { + el_stack.PUSH_BACK(element(key_comps, DNT_KEY, false)); + } + key_comps.CLEAR(); + } else { + value.SPLICE(value.end(), + key_comps, + key_comps.begin(), + key_comps.end()); + } + strip(value, element_if(DT_WHITE)); + strip(value, element_if(DT_COLON)); + strip(value, element_if(DT_WHITE)); + if (!value.empty()) { + if (value.size() == 2 && value.back().e_token == DNT_GROUP) { + element_list_t ELEMENT_LIST_T(group_pair); + + group_pair.PUSH_BACK(element(value, DNT_PAIR)); + el_stack.PUSH_BACK(element(group_pair, DNT_VALUE)); + } else { + el_stack.PUSH_BACK(element(value, DNT_VALUE)); + } + } + value.CLEAR(); +} + +void data_parser::parse() +{ + this->discover_format(); + + this->pairup(&this->dp_schema_id, + this->dp_pairs, + this->dp_group_stack.front()); +} + +std::string +data_parser::get_element_string(const data_parser::element &elem) const +{ + pcre_input &pi = this->dp_scanner->get_input(); + + return pi.get_substr(&elem.e_capture); +} + +std::string +data_parser::get_string_up_to_value(const data_parser::element &elem) +{ + pcre_input &pi = this->dp_scanner->get_input(); + const element &val_elem = elem.e_token == DNT_PAIR ? + elem.e_sub_elements->back() : elem; + + if (this->dp_msg_format_begin <= val_elem.e_capture.c_begin) { + pcre_context::capture_t leading_and_key = pcre_context::capture_t( + this->dp_msg_format_begin, val_elem.e_capture.c_begin); + const char *str = pi.get_string(); + if (leading_and_key.length() >= 2) { + switch (str[leading_and_key.c_end - 1]) { + case '\'': + case '"': + leading_and_key.c_end -= 1; + switch (str[leading_and_key.c_end - 1]) { + case 'r': + case 'u': + leading_and_key.c_end -= 1; + break; + } + break; + } + switch (str[leading_and_key.c_begin]) { + case '\'': + case '"': + leading_and_key.c_begin += 1; + break; + } + } + this->dp_msg_format_begin = val_elem.e_capture.c_end; + return pi.get_substr(&leading_and_key); + } else { + this->dp_msg_format_begin = val_elem.e_capture.c_end; + } + return ""; +} + +const char *data_parser::get_element_string(const data_parser::element &elem, + size_t &len_out) +{ + pcre_input &pi = this->dp_scanner->get_input(); + + len_out = elem.e_capture.length(); + return pi.get_substr_start(&elem.e_capture); +} + +void data_parser::print(FILE *out, data_parser::element_list_t &el) +{ + fprintf(out, " %s\n", + this->dp_scanner->get_input().get_string()); + for (auto &iter : el) { + iter.print(out, this->dp_scanner->get_input()); + } +} + FILE *data_parser::TRACE_FILE; data_format_state_t dfs_prefix_next(data_format_state_t state, @@ -45,39 +682,39 @@ data_format_state_t dfs_prefix_next(data_format_state_t state, data_format_state_t retval = state; switch (state) { - case DFS_INIT: - switch (next_token) { - case DT_PATH: - case DT_COLON: - case DT_EQUALS: - case DT_CONSTANT: - case DT_EMAIL: - case DT_WORD: - case DT_SYMBOL: - case DT_OCTAL_NUMBER: - case DT_HEX_NUMBER: - case DT_NUMBER: - case DT_WHITE: - case DT_LSQUARE: - case DT_RSQUARE: - case DT_LANGLE: - case DT_RANGLE: - case DT_EMPTY_CONTAINER: + case DFS_INIT: + switch (next_token) { + case DT_PATH: + case DT_COLON: + case DT_EQUALS: + case DT_CONSTANT: + case DT_EMAIL: + case DT_WORD: + case DT_SYMBOL: + case DT_OCTAL_NUMBER: + case DT_HEX_NUMBER: + case DT_NUMBER: + case DT_WHITE: + case DT_LSQUARE: + case DT_RSQUARE: + case DT_LANGLE: + case DT_RANGLE: + case DT_EMPTY_CONTAINER: + break; + + default: + retval = DFS_ERROR; + break; + } break; - default: + case DFS_EXPECTING_SEP: + case DFS_ERROR: retval = DFS_ERROR; break; - } - break; - case DFS_EXPECTING_SEP: - case DFS_ERROR: - retval = DFS_ERROR; - break; - - default: - break; + default: + break; } return retval; @@ -89,40 +726,50 @@ data_format_state_t dfs_semi_next(data_format_state_t state, data_format_state_t retval = state; switch (state) { - case DFS_INIT: - switch (next_token) { - case DT_COMMA: - case DT_SEMI: - retval = DFS_ERROR; - break; + case DFS_INIT: + switch (next_token) { + case DT_COMMA: + case DT_SEMI: + retval = DFS_ERROR; + break; - default: retval = DFS_KEY; break; - } - break; - - case DFS_KEY: - switch (next_token) { - case DT_COLON: - case DT_EQUALS: - retval = DFS_VALUE; + default: + retval = DFS_KEY; + break; + } break; - case DT_SEMI: retval = DFS_ERROR; break; + case DFS_KEY: + switch (next_token) { + case DT_COLON: + case DT_EQUALS: + retval = DFS_VALUE; + break; - default: break; - } - break; + case DT_SEMI: + retval = DFS_ERROR; + break; - case DFS_VALUE: - switch (next_token) { - case DT_SEMI: retval = DFS_INIT; break; + default: + break; + } + break; - default: break; - } - break; + case DFS_VALUE: + switch (next_token) { + case DT_SEMI: + retval = DFS_INIT; + break; - case DFS_EXPECTING_SEP: - case DFS_ERROR: retval = DFS_ERROR; break; + default: + break; + } + break; + + case DFS_EXPECTING_SEP: + case DFS_ERROR: + retval = DFS_ERROR; + break; } return retval; @@ -134,87 +781,295 @@ data_format_state_t dfs_comma_next(data_format_state_t state, data_format_state_t retval = state; switch (state) { - case DFS_INIT: - switch (next_token) { - case DT_COMMA: - break; + case DFS_INIT: + switch (next_token) { + case DT_COMMA: + break; - case DT_SEMI: - retval = DFS_ERROR; - break; + case DT_SEMI: + retval = DFS_ERROR; + break; - default: - retval = DFS_KEY; + default: + retval = DFS_KEY; + break; + } break; - } - break; - case DFS_KEY: - switch (next_token) { - case DT_COLON: - case DT_EQUALS: - retval = DFS_VALUE; + case DFS_KEY: + switch (next_token) { + case DT_COLON: + case DT_EQUALS: + retval = DFS_VALUE; + break; + + case DT_COMMA: + retval = DFS_INIT; + break; + + case DT_WORD: + retval = DFS_EXPECTING_SEP; + break; + + case DT_SEMI: + retval = DFS_ERROR; + break; + + default: + break; + } break; - case DT_COMMA: - retval = DFS_INIT; + case DFS_EXPECTING_SEP: + switch (next_token) { + case DT_COLON: + case DT_EQUALS: + case DT_LPAREN: + case DT_LCURLY: + case DT_LSQUARE: + case DT_LANGLE: + retval = DFS_VALUE; + break; + + case DT_EMPTY_CONTAINER: + retval = DFS_INIT; + break; + + case DT_COMMA: + case DT_SEMI: + retval = DFS_ERROR; + break; + + default: + break; + } break; - case DT_WORD: - retval = DFS_EXPECTING_SEP; + case DFS_VALUE: + switch (next_token) { + case DT_COMMA: + retval = DFS_INIT; + break; + + case DT_COLON: + case DT_EQUALS: + retval = DFS_ERROR; + break; + + default: + break; + } break; - case DT_SEMI: + case DFS_ERROR: retval = DFS_ERROR; break; + } + + return retval; +} + +data_parser::element::element() + : e_capture(-1, -1), + e_token(DT_INVALID), + e_sub_elements(nullptr) +{ +} + +data_parser::element::element(data_parser::element_list_t &subs, + data_token_t token, bool assign_subs_elements) + : e_capture(subs.front().e_capture.c_begin, + subs.back().e_capture.c_end), + e_token(token), + e_sub_elements(nullptr) +{ + if (assign_subs_elements) { + this->assign_elements(subs); + } +} + +data_parser::element::element(const data_parser::element &other) +{ + /* require(other.e_sub_elements == nullptr); */ + + this->e_capture = other.e_capture; + this->e_token = other.e_token; + this->e_sub_elements = nullptr; + if (other.e_sub_elements != nullptr) { + this->assign_elements(*other.e_sub_elements); + } +} + +data_parser::element::~element() +{ + delete this->e_sub_elements; + this->e_sub_elements = nullptr; +} + +data_parser::element & +data_parser::element::operator=(const data_parser::element &other) +{ + this->e_capture = other.e_capture; + this->e_token = other.e_token; + this->e_sub_elements = nullptr; + if (other.e_sub_elements != nullptr) { + this->assign_elements(*other.e_sub_elements); + } + return *this; +} + +void data_parser::element::assign_elements(data_parser::element_list_t &subs) +{ + if (this->e_sub_elements == nullptr) { + this->e_sub_elements = new element_list_t("_sub_", __FILE__, + __LINE__); + this->e_sub_elements->el_format = subs.el_format; + } + this->e_sub_elements->SWAP(subs); + this->update_capture(); +} + +void data_parser::element::update_capture() +{ + if (this->e_sub_elements != nullptr && !this->e_sub_elements->empty()) { + this->e_capture.c_begin = + this->e_sub_elements->front().e_capture.c_begin; + this->e_capture.c_end = + this->e_sub_elements->back().e_capture.c_end; + } +} + +const data_parser::element &data_parser::element::get_pair_value() const +{ + require(this->e_token == DNT_PAIR); + + return this->e_sub_elements->back(); +} + +data_token_t data_parser::element::value_token() const +{ + data_token_t retval = DT_INVALID; - default: break; + if (this->e_token == DNT_VALUE) { + if (this->e_sub_elements != nullptr && + this->e_sub_elements->size() == 1) { + retval = this->e_sub_elements->front().e_token; + } else { + retval = DT_SYMBOL; } - break; + } else { + retval = this->e_token; + } + return retval; +} - case DFS_EXPECTING_SEP: - switch (next_token) { - case DT_COLON: - case DT_EQUALS: - case DT_LPAREN: - case DT_LCURLY: - case DT_LSQUARE: - case DT_LANGLE: - retval = DFS_VALUE; - break; +const data_parser::element &data_parser::element::get_value_elem() const +{ + if (this->e_token == DNT_VALUE) { + if (this->e_sub_elements != nullptr && + this->e_sub_elements->size() == 1) { + return this->e_sub_elements->front(); + } + } + return *this; +} - case DT_EMPTY_CONTAINER: - retval = DFS_INIT; - break; +const data_parser::element &data_parser::element::get_pair_elem() const +{ + if (this->e_token == DNT_VALUE) { + return this->e_sub_elements->front(); + } + return *this; +} - case DT_COMMA: - case DT_SEMI: - retval = DFS_ERROR; - break; +void data_parser::element::print(FILE *out, pcre_input &pi, int offset) const +{ + int lpc; - default: break; + if (this->e_sub_elements != nullptr) { + for (auto &e_sub_element : *this->e_sub_elements) { + e_sub_element.print(out, pi, offset + 1); } - break; + } - case DFS_VALUE: - switch (next_token) { - case DT_COMMA: - retval = DFS_INIT; - break; + fprintf(out, "%4s %3d:%-3d ", + data_scanner::token2name(this->e_token), + this->e_capture.c_begin, + this->e_capture.c_end); + for (lpc = 0; lpc < this->e_capture.c_end; lpc++) { + if (lpc == this->e_capture.c_begin) { + fputc('^', out); + } else if (lpc == (this->e_capture.c_end - 1)) { + fputc('^', out); + } else if (lpc > this->e_capture.c_begin) { + fputc('-', out); + } else { + fputc(' ', out); + } + } + for (; lpc < (int) pi.pi_length; lpc++) { + fputc(' ', out); + } - case DT_COLON: - case DT_EQUALS: - retval = DFS_ERROR; - break; + std::string sub = pi.get_substr(&this->e_capture); + fprintf(out, " %s\n", sub.c_str()); +} + +data_parser::discover_format_state::discover_format_state() + : dfs_prefix_state(DFS_INIT), + dfs_semi_state(DFS_INIT), + dfs_comma_state(DFS_INIT) +{ + memset(this->dfs_hist, 0, sizeof(this->dfs_hist)); +} - default: break; +void data_parser::discover_format_state::update_for_element( + const data_parser::element &elem) +{ + this->dfs_prefix_state = dfs_prefix_next(this->dfs_prefix_state, + elem.e_token); + this->dfs_semi_state = dfs_semi_next(this->dfs_semi_state, elem.e_token); + this->dfs_comma_state = dfs_comma_next(this->dfs_comma_state, elem.e_token); + if (this->dfs_prefix_state != DFS_ERROR) { + if (this->dfs_semi_state == DFS_ERROR) { + this->dfs_semi_state = DFS_INIT; + } + if (this->dfs_comma_state == DFS_ERROR) { + this->dfs_comma_state = DFS_INIT; } - break; + } + this->dfs_hist[elem.e_token] += 1; +} + +void data_parser::discover_format_state::finalize() +{ + data_token_t qualifier = this->dfs_format.df_qualifier; + data_token_t separator = this->dfs_format.df_separator; + data_token_t prefix_term = this->dfs_format.df_prefix_terminator; - case DFS_ERROR: - retval = DFS_ERROR; - break; + this->dfs_format = FORMAT_PLAIN; + if (this->dfs_hist[DT_EQUALS]) { + qualifier = DT_COLON; + separator = DT_EQUALS; } - return retval; + if (this->dfs_semi_state != DFS_ERROR && this->dfs_hist[DT_SEMI]) { + this->dfs_format = FORMAT_SEMI; + } else if (this->dfs_comma_state != DFS_ERROR) { + this->dfs_format = FORMAT_COMMA; + if (separator == DT_COLON && this->dfs_hist[DT_COMMA] > 0) { + if (!((this->dfs_hist[DT_COLON] == this->dfs_hist[DT_COMMA]) || + ((this->dfs_hist[DT_COLON] - 1) == + this->dfs_hist[DT_COMMA]))) { + separator = DT_INVALID; + if (this->dfs_hist[DT_COLON] == 1) { + prefix_term = DT_COLON; + } + } + } + } + + this->dfs_format.df_qualifier = qualifier; + this->dfs_format.df_separator = separator; + this->dfs_format.df_prefix_terminator = prefix_term; } diff --git a/src/data_parser.hh b/src/data_parser.hh index f4919a03..58f5d68a 100644 --- a/src/data_parser.hh +++ b/src/data_parser.hh @@ -76,9 +76,9 @@ enum data_format_state_t { }; struct data_format { - data_format(const char *name = NULL, + data_format(const char *name = nullptr, data_token_t appender = DT_INVALID, - data_token_t terminator = DT_INVALID) + data_token_t terminator = DT_INVALID) noexcept : df_name(name), df_appender(appender), df_terminator(terminator), @@ -314,154 +314,31 @@ public: }; struct element { - element() - : e_capture(-1, -1), - e_token(DT_INVALID), - e_sub_elements(nullptr) { - }; + element(); element(element_list_t &subs, data_token_t token, - bool assign_subs_elements = true) - : e_capture(subs.front().e_capture.c_begin, - subs.back().e_capture.c_end), - e_token(token), - e_sub_elements(nullptr) - { - if (assign_subs_elements) { - this->assign_elements(subs); - } - }; + bool assign_subs_elements = true); - element(const element &other) - { - /* require(other.e_sub_elements == nullptr); */ - - this->e_capture = other.e_capture; - this->e_token = other.e_token; - this->e_sub_elements = nullptr; - if (other.e_sub_elements != nullptr) { - this->assign_elements(*other.e_sub_elements); - } - }; + element(const element &other); - ~element() - { - delete this->e_sub_elements; - this->e_sub_elements = nullptr; - }; - - element & operator=(const element &other) - { - this->e_capture = other.e_capture; - this->e_token = other.e_token; - this->e_sub_elements = NULL; - if (other.e_sub_elements != NULL) { - this->assign_elements(*other.e_sub_elements); - } - return *this; - }; + ~element(); - void assign_elements(element_list_t &subs) - { - if (this->e_sub_elements == NULL) { - this->e_sub_elements = new element_list_t("_sub_", __FILE__, - __LINE__); - this->e_sub_elements->el_format = subs.el_format; - } - this->e_sub_elements->SWAP(subs); - this->update_capture(); - }; + element & operator=(const element &other); - void update_capture(void) - { - if (this->e_sub_elements != NULL && !this->e_sub_elements->empty()) { - this->e_capture.c_begin = - this->e_sub_elements->front().e_capture.c_begin; - this->e_capture.c_end = - this->e_sub_elements->back().e_capture.c_end; - } - }; + void assign_elements(element_list_t &subs); - const element &get_pair_value(void) const - { - require(this->e_token == DNT_PAIR); + void update_capture(); - return this->e_sub_elements->back(); - }; + const element &get_pair_value() const; - data_token_t value_token(void) const - { - data_token_t retval = DT_INVALID; - - if (this->e_token == DNT_VALUE) { - if (this->e_sub_elements != NULL && - this->e_sub_elements->size() == 1) { - retval = this->e_sub_elements->front().e_token; - } - else { - retval = DT_SYMBOL; - } - } - else { - retval = this->e_token; - } - return retval; - }; + data_token_t value_token() const; - const element &get_value_elem() const { - if (this->e_token == DNT_VALUE) { - if (this->e_sub_elements != NULL && - this->e_sub_elements->size() == 1) { - return this->e_sub_elements->front(); - } - } - return *this; - }; + const element &get_value_elem() const; - const element &get_pair_elem() const { - if (this->e_token == DNT_VALUE) { - return this->e_sub_elements->front(); - } - return *this; - } + const element &get_pair_elem() const; - void print(FILE *out, pcre_input &pi, int offset = - 0) const - { - int lpc; - - if (this->e_sub_elements != nullptr) { - for (auto & e_sub_element : *this->e_sub_elements) { - e_sub_element.print(out, pi, offset + 1); - } - } - - fprintf(out, "%4s %3d:%-3d ", - data_scanner::token2name(this->e_token), - this->e_capture.c_begin, - this->e_capture.c_end); - for (lpc = 0; lpc < this->e_capture.c_end; lpc++) { - if (lpc == this->e_capture.c_begin) { - fputc('^', out); - } - else if (lpc == (this->e_capture.c_end - 1)) { - fputc('^', out); - } - else if (lpc > this->e_capture.c_begin) { - fputc('-', out); - } - else{ - fputc(' ', out); - } - } - for (; lpc < (int)pi.pi_length; lpc++) { - fputc(' ', out); - } - - std::string sub = pi.get_substr(&this->e_capture); - fprintf(out, " %s\n", sub.c_str()); - }; + void print(FILE *out, pcre_input &pi, int offset = 0) const; pcre_context::capture_t e_capture; data_token_t e_token; @@ -494,59 +371,11 @@ private: }; struct discover_format_state { - discover_format_state() { - memset(this->dfs_hist, 0, sizeof(this->dfs_hist)); - this->dfs_prefix_state = DFS_INIT; - this->dfs_semi_state = DFS_INIT; - this->dfs_comma_state = DFS_INIT; - } + discover_format_state(); - void update_for_element(const element &elem) { - this->dfs_prefix_state = dfs_prefix_next(this->dfs_prefix_state, elem.e_token); - this->dfs_semi_state = dfs_semi_next(this->dfs_semi_state, elem.e_token); - this->dfs_comma_state = dfs_comma_next(this->dfs_comma_state, elem.e_token); - if (this->dfs_prefix_state != DFS_ERROR) { - if (this->dfs_semi_state == DFS_ERROR) { - this->dfs_semi_state = DFS_INIT; - } - if (this->dfs_comma_state == DFS_ERROR) { - this->dfs_comma_state = DFS_INIT; - } - } - this->dfs_hist[elem.e_token] += 1; - } + void update_for_element(const element &elem); - void finalize() { - data_token_t qualifier = this->dfs_format.df_qualifier; - data_token_t separator = this->dfs_format.df_separator; - data_token_t prefix_term = this->dfs_format.df_prefix_terminator; - - this->dfs_format = FORMAT_PLAIN; - if (this->dfs_hist[DT_EQUALS]) { - qualifier = DT_COLON; - separator = DT_EQUALS; - } - - if (this->dfs_semi_state != DFS_ERROR && this->dfs_hist[DT_SEMI]) { - this->dfs_format = FORMAT_SEMI; - } - else if (this->dfs_comma_state != DFS_ERROR) { - this->dfs_format = FORMAT_COMMA; - if (separator == DT_COLON && this->dfs_hist[DT_COMMA] > 0) { - if (!((this->dfs_hist[DT_COLON] == this->dfs_hist[DT_COMMA]) || - ((this->dfs_hist[DT_COLON] - 1) == this->dfs_hist[DT_COMMA]))) { - separator = DT_INVALID; - if (this->dfs_hist[DT_COLON] == 1) { - prefix_term = DT_COLON; - } - } - } - } - - this->dfs_format.df_qualifier = qualifier; - this->dfs_format.df_separator = separator; - this->dfs_format.df_prefix_terminator = prefix_term; - }; + void finalize(); data_format_state_t dfs_prefix_state; data_format_state_t dfs_semi_state; @@ -556,646 +385,28 @@ private: data_format dfs_format; }; - data_parser(data_scanner *ds) - : dp_errors("dp_errors", __FILE__, __LINE__), - dp_pairs("dp_pairs", __FILE__, __LINE__), - dp_msg_format(NULL), - dp_msg_format_begin(ds->get_input().pi_offset), - dp_scanner(ds) - { - if (TRACE_FILE != NULL) { - fprintf(TRACE_FILE, "input %s\n", ds->get_input().get_string()); - } - }; + data_parser(data_scanner *ds); void pairup(schema_id_t *schema, element_list_t &pairs_out, - element_list_t &in_list, int group_depth = 0) - { - element_list_t ELEMENT_LIST_T(el_stack), ELEMENT_LIST_T(free_row), - ELEMENT_LIST_T(key_comps), ELEMENT_LIST_T(value), - ELEMENT_LIST_T(prefix); - SpookyHash context; - - require(in_list.el_format.df_name != NULL); - - POINT_TRACE("pairup_start"); - - FORMAT_TRACE(in_list); - - for (element_list_t::iterator iter = in_list.begin(); - iter != in_list.end(); - ++iter) { - if (iter->e_token == DNT_GROUP) { - element_list_t ELEMENT_LIST_T(group_pairs); - - this->pairup(NULL, group_pairs, *iter->e_sub_elements, group_depth + 1); - if (!group_pairs.empty()) { - iter->assign_elements(group_pairs); - } - } - - if (in_list.el_format.df_prefix_terminator != DT_INVALID) { - if (iter->e_token == in_list.el_format.df_prefix_terminator) { - in_list.el_format.df_prefix_terminator = DT_INVALID; - } - else { - el_stack.PUSH_BACK(*iter); - } - } - else if (iter->e_token == in_list.el_format.df_terminator) { - this->end_of_value(el_stack, key_comps, value, in_list, group_depth); - - key_comps.PUSH_BACK(*iter); - } - else if (iter->e_token == in_list.el_format.df_qualifier) { - value.SPLICE(value.end(), - key_comps, - key_comps.begin(), - key_comps.end()); - strip(value, element_if(DT_WHITE)); - if (!value.empty()) { - el_stack.PUSH_BACK(element(value, DNT_VALUE)); - } - } - else if (iter->e_token == in_list.el_format.df_separator) { - element_list_t::iterator key_iter = key_comps.end(); - bool found = false, key_is_values = true; - - if (!key_comps.empty()) { - do { - --key_iter; - if (key_iter->e_token == - in_list.el_format.df_appender) { - ++key_iter; - value.SPLICE(value.end(), - key_comps, - key_comps.begin(), - key_iter); - key_comps.POP_FRONT(); - found = true; - } - else if (key_iter->e_token == - in_list.el_format.df_terminator) { - std::vector key_copy; - - value.SPLICE(value.end(), - key_comps, - key_comps.begin(), - key_iter); - key_comps.POP_FRONT(); - strip(key_comps, element_if(DT_WHITE)); - if (key_comps.empty()) { - key_iter = key_comps.end(); - } else { - key_iter = key_comps.begin(); - } - found = true; - } - if (key_iter != key_comps.end()) { - switch (key_iter->e_token) { - case DT_WORD: - case DT_SYMBOL: - key_is_values = false; - break; - default: - break; - } - } - } while (key_iter != key_comps.begin() && !found); - } - if (!found && !el_stack.empty() && !key_comps.empty()) { - element_list_t::iterator value_iter; - - if (el_stack.size() > 1 && - in_list.el_format.df_appender != DT_INVALID && - in_list.el_format.df_terminator != DT_INVALID) { - /* If we're expecting a terminator and haven't found it */ - /* then this is part of the value. */ - continue; - } - - value.SPLICE(value.end(), - key_comps, - key_comps.begin(), - key_comps.end()); - value_iter = value.end(); - std::advance(value_iter, -1); - key_comps.SPLICE(key_comps.begin(), - value, - value_iter, - value.end()); - key_comps.resize(1); - } - - strip(value, element_if(DT_WHITE)); - value.remove_if(element_if(DT_COMMA)); - if (!value.empty()) { - el_stack.PUSH_BACK(element(value, DNT_VALUE)); - } - strip(key_comps, element_if(DT_WHITE)); - if (!key_comps.empty()) { - if (key_is_values) { - el_stack.PUSH_BACK(element(key_comps, DNT_VALUE)); - } - else { - el_stack.PUSH_BACK(element(key_comps, DNT_KEY, false)); - } - } - key_comps.CLEAR(); - value.CLEAR(); - } - else { - key_comps.PUSH_BACK(*iter); - } - - POINT_TRACE("pairup_loop"); - } - - POINT_TRACE("pairup_eol"); - - CONSUMED_TRACE(in_list); + element_list_t &in_list, int group_depth = 0); - // Only perform the free-row logic at the top level, if we're in a group - // assume it is a list. - if (group_depth < 1 && el_stack.empty()) { - free_row.SPLICE(free_row.begin(), - key_comps, key_comps.begin(), key_comps.end()); - } - else { - this->end_of_value(el_stack, key_comps, value, in_list, group_depth); - } - - POINT_TRACE("pairup_stack"); - - context.Init(0, 0); - while (!el_stack.empty()) { - element_list_t::iterator kv_iter = el_stack.begin(); - if (kv_iter->e_token == DNT_VALUE) { - if (pairs_out.empty()) { - free_row.PUSH_BACK(el_stack.front()); - } - else { - element_list_t ELEMENT_LIST_T(free_pair_subs); - struct element blank; - - blank.e_capture.c_begin = blank.e_capture.c_end = - el_stack.front().e_capture. - c_begin; - blank.e_token = DNT_KEY; - free_pair_subs.PUSH_BACK(blank); - free_pair_subs.PUSH_BACK(el_stack.front()); - pairs_out.PUSH_BACK(element(free_pair_subs, DNT_PAIR)); - } - } - if (kv_iter->e_token != DNT_KEY) { - el_stack.POP_FRONT(); - continue; - } - - ++kv_iter; - if (kv_iter == el_stack.end()) { - el_stack.POP_FRONT(); - continue; - } - - element_list_t ELEMENT_LIST_T(pair_subs); - - if (schema != NULL) { - size_t key_len; - const char *key_val = - this->get_element_string(el_stack.front(), key_len); - context.Update(key_val, key_len); - } - - while (!free_row.empty()) { - element_list_t ELEMENT_LIST_T(free_pair_subs); - struct element blank; - - blank.e_capture.c_begin = blank.e_capture.c_end = - free_row.front().e_capture. - c_begin; - blank.e_token = DNT_KEY; - free_pair_subs.PUSH_BACK(blank); - free_pair_subs.PUSH_BACK(free_row.front()); - pairs_out.PUSH_BACK(element(free_pair_subs, DNT_PAIR)); - free_row.POP_FRONT(); - } - - bool has_value = false; - - if (kv_iter->e_token == DNT_VALUE) { - ++kv_iter; - has_value = true; - } - - pair_subs.SPLICE(pair_subs.begin(), - el_stack, - el_stack.begin(), - kv_iter); - - if (!has_value) { - element_list_t ELEMENT_LIST_T(blank_value); - pcre_input &pi = this->dp_scanner->get_input(); - const char *str = pi.get_string(); - struct element blank; - - blank.e_token = DT_QUOTED_STRING; - blank.e_capture.c_begin = blank.e_capture.c_end = pair_subs.front().e_capture.c_end; - if ((blank.e_capture.c_begin >= 0) && - ((size_t) blank.e_capture.c_begin < pi.pi_length)) { - switch (str[blank.e_capture.c_begin]) { - case '=': - case ':': - blank.e_capture.c_begin += 1; - blank.e_capture.c_end += 1; - break; - } - } - blank_value.PUSH_BACK(blank); - pair_subs.PUSH_BACK(element(blank_value, DNT_VALUE)); - } - - pairs_out.PUSH_BACK(element(pair_subs, DNT_PAIR)); - } - - if (pairs_out.size() == 1) { - element &pair = pairs_out.front(); - element &evalue = pair.e_sub_elements->back(); - - if (evalue.e_token == DNT_VALUE && - evalue.e_sub_elements != NULL && - evalue.e_sub_elements->size() > 1) { - element_list_t::iterator next_sub; - - next_sub = pair.e_sub_elements->begin(); - ++next_sub; - prefix.SPLICE(prefix.begin(), - *pair.e_sub_elements, - pair.e_sub_elements->begin(), - next_sub); - free_row.CLEAR(); - free_row.SPLICE(free_row.begin(), - *evalue.e_sub_elements, - evalue.e_sub_elements->begin(), - evalue.e_sub_elements->end()); - pairs_out.CLEAR(); - context.Init(0, 0); - } - } - - if (group_depth >= 1 && pairs_out.empty() && !free_row.empty()) { - pairs_out.SWAP(free_row); - } - - if (pairs_out.empty() && !free_row.empty()) { - while (!free_row.empty()) { - switch (free_row.front().e_token) { - case DNT_GROUP: - case DNT_VALUE: - case DT_EMAIL: - case DT_CONSTANT: - case DT_NUMBER: - case DT_SYMBOL: - case DT_HEX_NUMBER: - case DT_OCTAL_NUMBER: - case DT_VERSION_NUMBER: - case DT_QUOTED_STRING: - case DT_IPV4_ADDRESS: - case DT_IPV6_ADDRESS: - case DT_MAC_ADDRESS: - case DT_HEX_DUMP: - case DT_XML_OPEN_TAG: - case DT_XML_CLOSE_TAG: - case DT_XML_EMPTY_TAG: - case DT_UUID: - case DT_URL: - case DT_PATH: - case DT_DATE: - case DT_TIME: - case DT_PERCENTAGE: { - element_list_t ELEMENT_LIST_T(pair_subs); - struct element blank; - - blank.e_capture.c_begin = blank.e_capture.c_end = - free_row.front().e_capture. - c_begin; - blank.e_token = DNT_KEY; - pair_subs.PUSH_BACK(blank); - pair_subs.PUSH_BACK(free_row.front()); - pairs_out.PUSH_BACK(element(pair_subs, DNT_PAIR)); - - // Throw something into the hash so that the number of - // columns is significant. I don't think we want to - // use the token ID since some columns values might vary - // between rows. - context.Update(" ", 1); - } - break; - - default: { - size_t key_len; - const char *key_val = this->get_element_string( - free_row.front(), key_len); - - context.Update(key_val, key_len); - } - break; - } - - free_row.POP_FRONT(); - } - } - - if (!prefix.empty()) { - element_list_t ELEMENT_LIST_T(pair_subs); - struct element blank; - - blank.e_capture.c_begin = blank.e_capture.c_end = - prefix.front().e_capture.c_begin; - blank.e_token = DNT_KEY; - pair_subs.PUSH_BACK(blank); - pair_subs.PUSH_BACK(prefix.front()); - pairs_out.PUSH_FRONT(element(pair_subs, DNT_PAIR)); - } - - if (schema != nullptr) { - context.Final(schema->out(0), schema->out(1)); - } - - if (schema != nullptr && this->dp_msg_format != nullptr) { - pcre_input &pi = this->dp_scanner->get_input(); - for (auto & fiter : pairs_out) { - *(this->dp_msg_format) += this->get_string_up_to_value(fiter); - this->dp_msg_format->append("#"); - } - if ((size_t) this->dp_msg_format_begin < pi.pi_length) { - const char *str = pi.get_string(); - pcre_context::capture_t last(this->dp_msg_format_begin, - pi.pi_length); - - switch (str[last.c_begin]) { - case '\'': - case '"': - last.c_begin += 1; - break; - } - *(this->dp_msg_format) += pi.get_substr(&last); - } - } - - if (pairs_out.size() > 1000) { - pairs_out.resize(1000); - } - }; - - void discover_format() - { - pcre_context_static<30> pc; - std::stack state_stack; - struct element elem; - - this->dp_group_token.push_back(DT_INVALID); - this->dp_group_stack.resize(1); - - state_stack.push(discover_format_state()); - while (this->dp_scanner->tokenize2(pc, elem.e_token)) { - pcre_context::iterator pc_iter; - - pc_iter = std::find_if(pc.begin(), pc.end(), capture_if_not(-1)); - require(pc_iter != pc.end()); - - elem.e_capture = *pc_iter; - - require(elem.e_capture.c_begin != -1); - require(elem.e_capture.c_end != -1); - - state_stack.top().update_for_element(elem); - switch (elem.e_token) { - case DT_LPAREN: - case DT_LANGLE: - case DT_LCURLY: - case DT_LSQUARE: - this->dp_group_token.push_back(elem.e_token); - this->dp_group_stack.emplace_back("_anon_", __FILE__, __LINE__); - state_stack.push(discover_format_state()); - break; - - case DT_EMPTY_CONTAINER: { - auto &curr_group = this->dp_group_stack.back(); - auto empty_list = element_list_t("_anon_", __FILE__, __LINE__); - discover_format_state dfs; - - dfs.finalize(); - - empty_list.el_format = dfs.dfs_format; - curr_group.PUSH_BACK(element()); - - auto &empty = curr_group.back(); - empty.e_capture.c_begin = elem.e_capture.c_begin + 1; - empty.e_capture.c_end = elem.e_capture.c_begin + 1; - empty.e_token = DNT_GROUP; - empty.assign_elements(empty_list); - break; - } - - case DT_RPAREN: - case DT_RANGLE: - case DT_RCURLY: - case DT_RSQUARE: - if (this->dp_group_token.back() == (elem.e_token - 1)) { - this->dp_group_token.pop_back(); - - auto riter = this->dp_group_stack.rbegin(); - ++riter; - state_stack.top().finalize(); - this->dp_group_stack.back().el_format = state_stack.top().dfs_format; - state_stack.pop(); - if (!this->dp_group_stack.back().empty()) { - (*riter).PUSH_BACK(element(this->dp_group_stack.back(), - DNT_GROUP)); - } - else { - (*riter).PUSH_BACK(element()); - riter->back().e_capture.c_begin = elem.e_capture.c_begin; - riter->back().e_capture.c_end = elem.e_capture.c_begin; - riter->back().e_token = DNT_GROUP; - riter->back().assign_elements(this->dp_group_stack.back()); - } - this->dp_group_stack.pop_back(); - } - else { - this->dp_group_stack.back().PUSH_BACK(elem); - } - break; - - default: - this->dp_group_stack.back().PUSH_BACK(elem); - break; - } - } - - while (this->dp_group_stack.size() > 1) { - this->dp_group_token.pop_back(); - - auto riter = this->dp_group_stack.rbegin(); - ++riter; - if (!this->dp_group_stack.back().empty()) { - state_stack.top().finalize(); - this->dp_group_stack.back().el_format = state_stack.top().dfs_format; - state_stack.pop(); - (*riter).PUSH_BACK(element(this->dp_group_stack.back(), - DNT_GROUP)); - } - this->dp_group_stack.pop_back(); - } - - state_stack.top().finalize(); - this->dp_group_stack.back().el_format = state_stack.top().dfs_format; - }; + void discover_format(); void end_of_value(element_list_t &el_stack, element_list_t &key_comps, element_list_t &value, const element_list_t &in_list, - int group_depth) { - key_comps.remove_if(element_if(in_list.el_format.df_terminator)); - key_comps.remove_if(element_if(DT_COMMA)); - value.remove_if(element_if(in_list.el_format.df_terminator)); - value.remove_if(element_if(DT_COMMA)); - strip(key_comps, element_if(DT_WHITE)); - strip(value, element_if(DT_WHITE)); - if ((el_stack.empty() || el_stack.back().e_token != DNT_KEY) && - value.empty() && key_comps.size() > 1 && - (key_comps.front().e_token == DT_WORD || - key_comps.front().e_token == DT_SYMBOL)) { - element_list_t::iterator key_iter, key_end; - bool found_value = false; - int word_count = 0; - key_iter = key_comps.begin(); - key_end = key_comps.begin(); - for (; key_iter != key_comps.end(); ++key_iter) { - if (key_iter->e_token == DT_WORD || - key_iter->e_token == DT_SYMBOL) { - word_count += 1; - if (found_value) { - key_end = key_comps.begin(); - } - } - else if (key_iter->e_token == DT_WHITE) { - - } - else { - if (!found_value) { - key_end = key_iter; - } - found_value = true; - } - } - if (word_count != 1) { - key_end = key_comps.begin(); - } - value.SPLICE(value.end(), - key_comps, - key_end, - key_comps.end()); - strip(key_comps, element_if(DT_WHITE)); - if (!key_comps.empty()) { - el_stack.PUSH_BACK(element(key_comps, DNT_KEY, false)); - } - key_comps.CLEAR(); - } - else { - value.SPLICE(value.end(), - key_comps, - key_comps.begin(), - key_comps.end()); - } - strip(value, element_if(DT_WHITE)); - strip(value, element_if(DT_COLON)); - strip(value, element_if(DT_WHITE)); - if (!value.empty()) { - if (value.size() == 2 && value.back().e_token == DNT_GROUP) { - element_list_t ELEMENT_LIST_T(group_pair); - - group_pair.PUSH_BACK(element(value, DNT_PAIR)); - el_stack.PUSH_BACK(element(group_pair, DNT_VALUE)); - } - else { - el_stack.PUSH_BACK(element(value, DNT_VALUE)); - } - } - value.CLEAR(); - }; + int group_depth); - void parse() - { - this->discover_format(); + void parse(); - this->pairup(&this->dp_schema_id, - this->dp_pairs, - this->dp_group_stack.front()); - }; + std::string get_element_string(const element &elem) const; - std::string get_element_string(const element &elem) const - { - pcre_input &pi = this->dp_scanner->get_input(); + std::string get_string_up_to_value(const element &elem); - return pi.get_substr(&elem.e_capture); - }; + const char *get_element_string(const element &elem, size_t &len_out); - std::string get_string_up_to_value(const element &elem) { - pcre_input &pi = this->dp_scanner->get_input(); - const element &val_elem = elem.e_token == DNT_PAIR ? - elem.e_sub_elements->back() : elem; - - if (this->dp_msg_format_begin <= val_elem.e_capture.c_begin) { - pcre_context::capture_t leading_and_key = pcre_context::capture_t( - this->dp_msg_format_begin, val_elem.e_capture.c_begin); - const char *str = pi.get_string(); - if (leading_and_key.length() >= 2) { - switch (str[leading_and_key.c_end - 1]) { - case '\'': - case '"': - leading_and_key.c_end -= 1; - switch (str[leading_and_key.c_end - 1]) { - case 'r': - case 'u': - leading_and_key.c_end -= 1; - break; - } - break; - } - switch (str[leading_and_key.c_begin]) { - case '\'': - case '"': - leading_and_key.c_begin += 1; - break; - } - } - this->dp_msg_format_begin = val_elem.e_capture.c_end; - return pi.get_substr(&leading_and_key); - } - else { - this->dp_msg_format_begin = val_elem.e_capture.c_end; - } - return ""; - }; - - const char *get_element_string(const element &elem, size_t &len_out) { - pcre_input &pi = this->dp_scanner->get_input(); - - len_out = elem.e_capture.length(); - return pi.get_substr_start(&elem.e_capture); - }; - - void print(FILE *out, element_list_t &el) - { - fprintf(out, " %s\n", - this->dp_scanner->get_input().get_string()); - for (auto & iter : el) { - iter.print(out, this->dp_scanner->get_input()); - } - }; + void print(FILE *out, element_list_t &el); std::vector dp_group_token; std::list dp_group_stack; diff --git a/src/db_sub_source.cc b/src/db_sub_source.cc index 2cf4900b..c6e1fb04 100644 --- a/src/db_sub_source.cc +++ b/src/db_sub_source.cc @@ -262,7 +262,7 @@ size_t db_overlay_source::list_overlay_count(const listview_curses &lv) .with_margins(3, 0); for (auto &jpw_value : jpw.jpw_values) { - this->dos_lines.push_back(" " + jpw_value.wt_ptr + " = " + + this->dos_lines.emplace_back(" " + jpw_value.wt_ptr + " = " + jpw_value.wt_value); string_attrs_t &sa = this->dos_lines.back().get_attrs(); diff --git a/src/db_sub_source.hh b/src/db_sub_source.hh index 1e49701b..05949a97 100644 --- a/src/db_sub_source.hh +++ b/src/db_sub_source.hh @@ -42,12 +42,6 @@ class db_label_source : public text_sub_source, public text_time_translator { public: - db_label_source() : dls_time_column_index(-1) { - - }; - - ~db_label_source() { }; - bool has_log_time_column() const { return !this->dls_time_column.empty(); }; @@ -118,7 +112,7 @@ public: }; struct header_meta { - header_meta(std::string name) + explicit header_meta(std::string name) : hm_name(std::move(name)), hm_column_type(SQLITE3_TEXT), hm_graphable(false), @@ -143,26 +137,22 @@ public: std::vector dls_headers; std::vector > dls_rows; std::vector dls_time_column; - int dls_time_column_index; + int dls_time_column_index{-1}; static const char *NULL_STR; }; class db_overlay_source : public list_overlay_source { public: - db_overlay_source() : dos_active(false), dos_labels(nullptr) { - - }; - size_t list_overlay_count(const listview_curses &lv); bool list_value_for_overlay(const listview_curses &lv, int y, int bottom, vis_line_t row, - attr_line_t &value_out); + attr_line_t &value_out) override; - bool dos_active; - db_label_source *dos_labels; + bool dos_active{false}; + db_label_source *dos_labels{nullptr}; std::vector dos_lines; }; #endif diff --git a/src/environ_vtab.cc b/src/environ_vtab.cc index 7afeb19c..10f96d40 100644 --- a/src/environ_vtab.cc +++ b/src/environ_vtab.cc @@ -210,7 +210,7 @@ static int vt_update(sqlite3_vtab *tab, sqlite_int64 *rowid) { const char *name = ( - argc > 2 ? (const char *)sqlite3_value_text(argv[2]) : NULL); + argc > 2 ? (const char *)sqlite3_value_text(argv[2]) : nullptr); vtab *p_vt = (vtab *)tab; int retval = SQLITE_ERROR; @@ -225,7 +225,7 @@ static int vt_update(sqlite3_vtab *tab, return SQLITE_ERROR; } - if (name != NULL && strchr(name, '=') != NULL) { + if (name != nullptr && strchr(name, '=') != nullptr) { tab->zErrMsg = sqlite3_mprintf( "Environment variable names cannot contain an equals sign (=)"); @@ -244,7 +244,7 @@ static int vt_update(sqlite3_vtab *tab, unsetenv(name); retval = SQLITE_OK; - } else if (name != NULL && getenv(name) != NULL) { + } else if (name != nullptr && getenv(name) != nullptr) { #ifdef SQLITE_FAIL int rc; @@ -266,7 +266,7 @@ static int vt_update(sqlite3_vtab *tab, #endif } - if (name != NULL && argc == 4) { + if (name != nullptr && argc == 4) { const unsigned char *value = sqlite3_value_text(argv[3]); setenv((const char *)name, (const char *)value, 1); diff --git a/src/field_overlay_source.cc b/src/field_overlay_source.cc index 76ec8b9a..72e50eab 100644 --- a/src/field_overlay_source.cc +++ b/src/field_overlay_source.cc @@ -29,7 +29,7 @@ #include "config.h" -#include "lnav.hh" +#include "ansi_scrubber.hh" #include "vtab_module.hh" #include "relative_time.hh" #include "field_overlay_source.hh" @@ -41,7 +41,8 @@ json_string extract(const char *str); void field_overlay_source::build_summary_lines(const listview_curses &lv) { - textfile_sub_source &tss = lnav_data.ld_text_source; + auto& tc = dynamic_cast(lv); + textfile_sub_source &tss = this->fos_tss; logfile_sub_source &lss = this->fos_lss; this->fos_summary_lines.clear(); @@ -55,7 +56,7 @@ void field_overlay_source::build_summary_lines(const listview_curses &lv) lv.get_dimensions(height, width); free_rows = height - filled_rows - vis_line_t(this->fos_lines.size()); - if (free_rows < 2 || lnav_data.ld_flags & LNF_HEADLESS) { + if (free_rows < 2 || !this->fos_show_status) { this->fos_summary_lines.clear(); } else { @@ -68,7 +69,7 @@ void field_overlay_source::build_summary_lines(const listview_curses &lv) } else { logline *first_line, *last_line; - time_t now = time(NULL); + time_t now = time(nullptr); first_line = lss.find_line(lss.at(vis_line_t(0))); last_line = lss.find_line(lss.at(lv.get_bottom())); @@ -84,11 +85,12 @@ void field_overlay_source::build_summary_lines(const listview_curses &lv) 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); - vis_bookmarks &bm = lnav_data.ld_views[LNV_LOG].get_bookmarks(); - bookmark_vector &error_bookmarks = - bm[&logfile_sub_source::BM_ERRORS]; + 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 != -1 && + error_bm_iter != bm.end()) { + auto& error_bookmarks = error_bm_iter->second; auto five_min_lower = lower_bound(error_bookmarks.begin(), error_bookmarks.end(), @@ -369,7 +371,7 @@ void field_overlay_source::build_field_lines(const listview_curses &lv) this->fos_lines.emplace_back(" No known message fields"); } - const log_format *last_format = NULL; + const log_format *last_format = nullptr; for (auto & lv : this->fos_log_helper.ldh_line_values) { string format_name = lv.lv_format->get_name().to_string(); diff --git a/src/field_overlay_source.hh b/src/field_overlay_source.hh index 83b47553..eee06af0 100644 --- a/src/field_overlay_source.hh +++ b/src/field_overlay_source.hh @@ -35,18 +35,19 @@ #include "listview_curses.hh" #include "log_data_helper.hh" #include "logfile_sub_source.hh" +#include "textfile_sub_source.hh" class field_overlay_source : public list_overlay_source { public: - field_overlay_source(logfile_sub_source &lss) - : fos_lss(lss), fos_log_helper(lss) { + explicit field_overlay_source(logfile_sub_source &lss, textfile_sub_source &tss) + : fos_lss(lss), fos_tss(tss), fos_log_helper(lss) { }; void add_key_line_attrs(int key_size, bool last_line = false) { string_attrs_t &sa = this->fos_lines.back().get_attrs(); struct line_range lr(1, 2); - sa.push_back(string_attr(lr, &view_curses::VC_GRAPHIC, last_line ? ACS_LLCORNER : ACS_LTEE)); + sa.emplace_back(lr, &view_curses::VC_GRAPHIC, last_line ? ACS_LLCORNER : ACS_LTEE); lr.lr_start = 3 + key_size + 3; lr.lr_end = -1; @@ -93,9 +94,11 @@ public: std::vector &dst, vis_line_t row); + bool fos_show_status{true}; bool fos_active{false}; bool fos_active_prev{false}; logfile_sub_source &fos_lss; + textfile_sub_source &fos_tss; log_data_helper fos_log_helper; int fos_known_key_size{0}; int fos_unknown_key_size{0}; diff --git a/src/file_collection.cc b/src/file_collection.cc new file mode 100644 index 00000000..9f5464ed --- /dev/null +++ b/src/file_collection.cc @@ -0,0 +1,419 @@ +/** + * 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 file_collection.cc + */ + +#include "config.h" + +#include + +#include "file_collection.hh" + +static std::mutex REALPATH_CACHE_MUTEX; +static std::unordered_map REALPATH_CACHE; + +void file_collection::close_file(const std::shared_ptr &lf) +{ + if (lf->is_valid_filename()) { + std::lock_guard lg(REALPATH_CACHE_MUTEX); + + REALPATH_CACHE.erase(lf->get_filename()); + } else { + this->fc_file_names.erase(lf->get_filename()); + } + auto file_iter = find(this->fc_files.begin(), + this->fc_files.end(), + lf); + if (file_iter != this->fc_files.end()) { + this->fc_files.erase(file_iter); + this->fc_files_generation += 1; + } + + this->regenerate_unique_file_names(); +} + +void file_collection::regenerate_unique_file_names() +{ + unique_path_generator upg; + + for (const auto &lf : this->fc_files) { + upg.add_source(lf); + } + + upg.generate(); + + this->fc_largest_path_length = 0; + for (const auto &lf : this->fc_files) { + const auto &path = lf->get_unique_path(); + + if (path.length() > this->fc_largest_path_length) { + this->fc_largest_path_length = path.length(); + } + } + for (const auto &pair : this->fc_other_files) { + auto bn = ghc::filesystem::path(pair.first).filename().string(); + if (bn.length() > this->fc_largest_path_length) { + this->fc_largest_path_length = bn.length(); + } + } +} + +void file_collection::merge(const file_collection &other) +{ + this->fc_recursive = other.fc_recursive; + this->fc_rotated = other.fc_rotated; + + this->fc_name_to_errors.insert(other.fc_name_to_errors.begin(), + other.fc_name_to_errors.end()); + this->fc_file_names.insert(other.fc_file_names.begin(), + other.fc_file_names.end()); + if (!other.fc_files.empty()) { + this->fc_files.insert(this->fc_files.end(), + other.fc_files.begin(), + other.fc_files.end()); + this->fc_files_generation += 1; + } + for (auto &pair : other.fc_renamed_files) { + pair.first->set_filename(pair.second); + } + this->fc_closed_files.insert(other.fc_closed_files.begin(), + other.fc_closed_files.end()); + this->fc_other_files.insert(other.fc_other_files.begin(), + other.fc_other_files.end()); +} + +/** + * Functor used to compare files based on their device and inode number. + */ +struct same_file { + explicit same_file(const struct stat &stat) : sf_stat(stat) {}; + + /** + * Compare the given log file against the 'stat' given in the constructor. + * @param lf The log file to compare. + * @return True if the dev/inode values in the stat given in the + * constructor matches the stat in the logfile object. + */ + bool operator()(const std::shared_ptr &lf) const + { + return this->sf_stat.st_dev == lf->get_stat().st_dev && + this->sf_stat.st_ino == lf->get_stat().st_ino; + }; + + const struct stat &sf_stat; +}; + +/** + * Try to load the given file as a log file. If the file has not already been + * loaded, it will be loaded. If the file has already been loaded, the file + * name will be updated. + * + * @param filename The file name to check. + * @param fd An already-opened descriptor for 'filename'. + * @param required Specifies whether or not the file must exist and be valid. + */ +std::future +file_collection::watch_logfile(const std::string &filename, + logfile_open_options &loo, bool required) +{ + file_collection retval; + struct stat st; + int rc; + + if (this->fc_closed_files.count(filename)) { + return make_ready_future(retval); + } + + if (loo.loo_fd != -1) { + rc = fstat(loo.loo_fd, &st); + } else { + rc = stat(filename.c_str(), &st); + } + + if (rc == 0) { + if (S_ISDIR(st.st_mode) && this->fc_recursive) { + std::string wilddir = filename + "/*"; + + if (this->fc_file_names.find(wilddir) == + this->fc_file_names.end()) { + retval.fc_file_names.emplace(wilddir, logfile_open_options()); + } + return make_ready_future(retval); + } + if (!S_ISREG(st.st_mode)) { + if (required) { + rc = -1; + errno = EINVAL; + } else { + return make_ready_future(retval); + } + } + } + if (rc == -1) { + if (required) { + retval.fc_name_to_errors[filename] = strerror(errno); + } + return make_ready_future(retval); + } + + auto file_iter = find_if(this->fc_files.begin(), + this->fc_files.end(), + same_file(st)); + + if (file_iter == this->fc_files.end()) { + if (this->fc_other_files.find(filename) != this->fc_other_files.end()) { + return make_ready_future(retval); + } + + auto func = [filename, loo, prog = this->fc_progress, errs = this->fc_name_to_errors]() mutable { + file_collection retval; + + if (errs.find(filename) != errs.end()) { + // The file is broken, no reason to try and reopen + return retval; + } + + auto ff = detect_file_format(filename); + + switch (ff) { + case file_format_t::FF_SQLITE_DB: + retval.fc_other_files[filename] = ff; + break; + + case file_format_t::FF_ARCHIVE: { + nonstd::optional::iterator> + prog_iter_opt; + + auto res = archive_manager::walk_archive_files( + filename, + [prog, &prog_iter_opt]( + const auto &path, + const auto total) { + safe::WriteAccess sp( + *prog); + + prog_iter_opt | + [&sp](auto prog_iter) { + sp->sp_extractions.erase( + prog_iter); + }; + auto prog_iter = sp->sp_extractions.emplace( + sp->sp_extractions.begin(), + path, total); + prog_iter_opt = prog_iter; + + return &(*prog_iter); + }, + [&filename, &retval]( + const auto &tmp_path, + const auto &entry) { + auto ext = entry.path().extension(); + if (ext == ".jar" || + ext == ".war" || + ext == ".zip") { + return; + } + + auto arc_path = ghc::filesystem::relative( + entry.path(), tmp_path); + auto custom_name = + filename / arc_path; + bool is_visible = true; + + if (entry.file_size() == 0) { + log_info( + "hiding empty archive file: %s", + entry.path().c_str()); + is_visible = false; + } + + log_info( + "adding file from archive: %s/%s", + filename.c_str(), + entry.path().c_str()); + retval.fc_file_names[entry.path().string()] + .with_filename( + custom_name.string()) + .with_visibility(is_visible) + .with_non_utf_visibility( + false) + .with_visible_size_limit( + 128 * 1024); + }); + if (res.isErr()) { + log_error( + "archive extraction failed: %s", + res.unwrapErr().c_str()); + retval.clear(); + retval.fc_name_to_errors[filename] = res.unwrapErr(); + } else { + retval.fc_other_files[filename] = ff; + } + { + prog_iter_opt | + [&prog](auto prog_iter) { + prog->writeAccess()->sp_extractions.erase( + prog_iter); + }; + } + break; + } + + default: + log_info("loading new file: filename=%s", + filename.c_str()); + + /* It's a new file, load it in. */ + try { + auto lf = std::make_shared( + filename, loo); + + retval.fc_files.push_back(lf); + } catch (logfile::error &e) { + retval.fc_name_to_errors[filename] = e.what(); + } + break; + } + + return retval; + }; + + return std::async(std::launch::async, func); + } else { + auto lf = *file_iter; + + if (lf->is_valid_filename() && lf->get_filename() != filename) { + /* The file is already loaded, but has been found under a different + * name. We just need to update the stored file name. + */ + retval.fc_renamed_files.emplace_back(lf, filename); + } + } + + return make_ready_future(retval); +} + +/** + * Expand a glob pattern and call watch_logfile with the file names that match + * the pattern. + * @param path The glob pattern to expand. + * @param required Passed to watch_logfile. + */ +void file_collection::expand_filename(future_queue &fq, + const std::string &path, + logfile_open_options &loo, + bool required) +{ + static_root_mem gl; + + { + std::lock_guard lg(REALPATH_CACHE_MUTEX); + + if (REALPATH_CACHE.find(path) != REALPATH_CACHE.end()) { + return; + } + } + + if (is_url(path.c_str())) { + return; + } else if (glob(path.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) { + int lpc; + + if (gl->gl_pathc == 1 /*&& gl.gl_matchc == 0*/) { + /* It's a pattern that doesn't match any files + * yet, allow it through since we'll load it in + * dynamically. + */ + if (access(path.c_str(), F_OK) == -1) { + required = false; + } + } + if (gl->gl_pathc > 1 || + strcmp(path.c_str(), gl->gl_pathv[0]) != 0) { + required = false; + } + + std::lock_guard lg(REALPATH_CACHE_MUTEX); + for (lpc = 0; lpc < (int) gl->gl_pathc; lpc++) { + auto path_str = std::string(gl->gl_pathv[lpc]); + auto iter = REALPATH_CACHE.find(path_str); + + if (iter == REALPATH_CACHE.end()) { + auto_mem abspath; + + if ((abspath = realpath(gl->gl_pathv[lpc], nullptr)) == + nullptr) { + if (required) { + fprintf(stderr, "Cannot find file: %s -- %s", + gl->gl_pathv[lpc], strerror(errno)); + } + continue; + } else { + auto p = REALPATH_CACHE.emplace(path_str, abspath.in()); + + iter = p.first; + } + } + + if (required || access(iter->second.c_str(), R_OK) == 0) { + fq.push_back(watch_logfile(iter->second, loo, required)); + } + } + } +} + +file_collection file_collection::rescan_files(bool required) +{ + file_collection retval; + future_queue fq([&retval](auto &fc) { + retval.merge(fc); + }); + + for (auto &pair : this->fc_file_names) { + if (pair.second.loo_fd == -1) { + this->expand_filename(fq, pair.first, pair.second, required); + if (this->fc_rotated) { + std::string path = pair.first + ".*"; + + this->expand_filename(fq, path, pair.second, false); + } + } else { + fq.push_back(watch_logfile(pair.first, pair.second, required)); + } + + if (retval.fc_files.size() >= 100) { + log_debug("too many new files, breaking..."); + break; + } + } + + fq.pop_to(); + + return retval; +} diff --git a/src/file_collection.hh b/src/file_collection.hh new file mode 100644 index 00000000..4d039540 --- /dev/null +++ b/src/file_collection.hh @@ -0,0 +1,97 @@ +/** + * 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 file_collection.hh + */ + +#ifndef lnav_file_collection_hh +#define lnav_file_collection_hh + +#include +#include + +#include "safe/safe.h" + +#include "base/future_util.hh" +#include "logfile.hh" +#include "archive_manager.hh" +#include "lnav_util.hh" + +struct scan_progress { + std::list sp_extractions; +}; + +using safe_scan_progress = safe::Safe; + +struct file_collection { + bool fc_recursive{false}; + bool fc_rotated{false}; + + std::map fc_name_to_errors; + std::map fc_file_names; + std::vector> fc_files; + int fc_files_generation{0}; + std::vector, std::string>> + fc_renamed_files; + std::set fc_closed_files; + std::map fc_other_files; + std::shared_ptr fc_progress; + size_t fc_largest_path_length{0}; + + file_collection() + : fc_progress(std::make_shared>()) + {} + + void clear() + { + this->fc_name_to_errors.clear(); + this->fc_file_names.clear(); + this->fc_files.clear(); + this->fc_closed_files.clear(); + this->fc_other_files.clear(); + } + + file_collection rescan_files(bool required = false); + + void + expand_filename(future_queue &fq, const std::string &path, + logfile_open_options &loo, bool required); + + std::future + watch_logfile(const std::string &filename, logfile_open_options &loo, + bool required); + + void merge(const file_collection &other); + + void close_file(const std::shared_ptr &lf); + + void regenerate_unique_file_names(); +}; + + +#endif diff --git a/src/file_vtab.cc b/src/file_vtab.cc index ef23b6c6..12125dd9 100644 --- a/src/file_vtab.cc +++ b/src/file_vtab.cc @@ -32,7 +32,6 @@ #include #include -#include "lnav.hh" #include "base/lnav_log.hh" #include "file_vtab.hh" #include "session_data.hh" @@ -56,20 +55,15 @@ CREATE TABLE lnav_file ( ); )"; - struct vtab { - sqlite3_vtab base; - - explicit operator sqlite3_vtab *() { - return &this->base; - }; - }; + lnav_file(file_collection& fc) : lf_collection(fc) { + } iterator begin() { - return lnav_data.ld_active_files.fc_files.begin(); + return this->lf_collection.fc_files.begin(); } iterator end() { - return lnav_data.ld_active_files.fc_files.end(); + return this->lf_collection.fc_files.end(); } int get_column(const cursor &vc, sqlite3_context *ctx, int col) { @@ -135,7 +129,7 @@ CREATE TABLE lnav_file ( int64_t lines, int64_t time_offset, bool visible) { - auto lf = lnav_data.ld_active_files.fc_files[rowid]; + auto lf = this->lf_collection.fc_files[rowid]; struct timeval tv = { (int) (time_offset / 1000LL), (int) (time_offset / (1000LL * 1000LL)), @@ -149,15 +143,15 @@ CREATE TABLE lnav_file ( "real file paths cannot be updated, only symbolic ones"); } - auto iter = lnav_data.ld_active_files.fc_file_names.find(lf->get_filename()); + auto iter = this->lf_collection.fc_file_names.find(lf->get_filename()); - if (iter != lnav_data.ld_active_files.fc_file_names.end()) { - auto loo = iter->second; + if (iter != this->lf_collection.fc_file_names.end()) { + auto loo = std::move(iter->second); - lnav_data.ld_active_files.fc_file_names.erase(iter); + this->lf_collection.fc_file_names.erase(iter); loo.loo_include_in_session = true; - lnav_data.ld_active_files.fc_file_names[path] = loo; + this->lf_collection.fc_file_names[path] = std::move(loo); lf->set_filename(path); init_session(); @@ -167,21 +161,21 @@ CREATE TABLE lnav_file ( if (lf->is_visible() != visible) { lf->set_visibility(visible); - lnav_data.ld_log_source.text_filters_changed(); } return SQLITE_OK; }; + + file_collection &lf_collection; }; -int register_file_vtab(sqlite3 *db) +int register_file_vtab(sqlite3 *db, file_collection &fc) { - static vtab_module LNAV_FILE_MODULE; - + auto mod = new vtab_module(fc); int rc; - rc = LNAV_FILE_MODULE.create(db, "lnav_file"); + rc = mod->create(db, "lnav_file"); ensure(rc == SQLITE_OK); diff --git a/src/file_vtab.hh b/src/file_vtab.hh index ab31d0c9..bf1fdc88 100644 --- a/src/file_vtab.hh +++ b/src/file_vtab.hh @@ -32,6 +32,8 @@ #include -int register_file_vtab(sqlite3 *db); +#include "file_collection.hh" + +int register_file_vtab(sqlite3 *db, file_collection &fc); #endif diff --git a/src/fs-extension-functions.cc b/src/fs-extension-functions.cc index f66927ac..acddfe78 100644 --- a/src/fs-extension-functions.cc +++ b/src/fs-extension-functions.cc @@ -31,11 +31,8 @@ #include "config.h" -#include #include -#include #include -#include #include #include @@ -158,7 +155,7 @@ string sql_realpath(const char *path) { char resolved_path[PATH_MAX]; - if (realpath(path, resolved_path) == NULL) { + if (realpath(path, resolved_path) == nullptr) { throw sqlite_func_error("Could not get real path for {} -- {}", path, strerror(errno)); } @@ -280,11 +277,11 @@ int fs_extension_functions(struct FuncDef **basic_funcs, * TODO: add other functions like normpath, ... */ - { NULL } + { nullptr } }; *basic_funcs = fs_funcs; - *agg_funcs = NULL; + *agg_funcs = nullptr; return SQLITE_OK; } diff --git a/src/fstat_vtab.cc b/src/fstat_vtab.cc index 28b9bc09..6c54aa1c 100644 --- a/src/fstat_vtab.cc +++ b/src/fstat_vtab.cc @@ -89,14 +89,6 @@ CREATE TABLE fstat ( ); )"; - struct vtab { - sqlite3_vtab base; - - operator sqlite3_vtab *() { - return &this->base; - }; - }; - struct cursor { sqlite3_vtab_cursor base; string c_pattern; diff --git a/src/grep_proc.cc b/src/grep_proc.cc index bcf0d624..0a361fdd 100644 --- a/src/grep_proc.cc +++ b/src/grep_proc.cc @@ -34,11 +34,9 @@ #include #include #include -#include #include #include #include -#include #include #include "base/lnav_log.hh" @@ -47,8 +45,6 @@ #include "grep_proc.hh" #include "listview_curses.hh" -#include "time_T.hh" - using namespace std; template @@ -132,7 +128,7 @@ void grep_proc::start() log_perror(fcntl(err_pipe.read_end(), F_SETFL, O_NONBLOCK)); log_perror(fcntl(err_pipe.read_end(), F_SETFD, 1)); require(this->gp_err_pipe.get() == -1); - this->gp_err_pipe = err_pipe.read_end(); + this->gp_err_pipe = std::move(err_pipe.read_end()); this->gp_child_started = true; this->gp_child_queue_size = this->gp_queue.size(); diff --git a/src/grep_proc.hh b/src/grep_proc.hh index cd911b6c..b3c8b073 100644 --- a/src/grep_proc.hh +++ b/src/grep_proc.hh @@ -100,7 +100,7 @@ public: class grep_proc_control { public: - virtual ~grep_proc_control() { }; + virtual ~grep_proc_control() = default; /** @param msg The error encountered while attempting the grep. */ virtual void grep_error(std::string msg) { }; diff --git a/src/help_text_formatter.cc b/src/help_text_formatter.cc index 13d907df..35e73fa2 100644 --- a/src/help_text_formatter.cc +++ b/src/help_text_formatter.cc @@ -30,9 +30,9 @@ #include "config.h" #include +#include -#include - +#include "base/string_util.hh" #include "fmt/format.h" #include "ansi_scrubber.hh" #include "help_text_formatter.hh" @@ -451,7 +451,7 @@ void format_example_text_for_term(const help_text &ht, static std::string link_name(const help_text &ht) { - const static pcrecpp::RE SCRUBBER("[^\\w_]"); + const static std::regex SCRUBBER("[^\\w_]"); auto scrubbed_name = string(ht.ht_name); for (auto ¶m : ht.ht_parameters) { @@ -462,7 +462,7 @@ static std::string link_name(const help_text &ht) scrubbed_name += "_"; scrubbed_name += param.ht_flag_name; } - SCRUBBER.GlobalReplace("_", &scrubbed_name); + scrubbed_name = std::regex_replace(scrubbed_name, SCRUBBER, "_"); return tolower(scrubbed_name); } @@ -600,8 +600,8 @@ void format_help_text_for_rst(const help_text &ht, } stable_sort(related_refs.begin(), related_refs.end()); - fprintf(rst_file, " **See Also:**\n\n %s\n", - join(related_refs.begin(), related_refs.end(), ", ").c_str()); + fmt::print(rst_file, " **See Also:**\n\n {}\n", + fmt::join(related_refs, ", ")); } fprintf(rst_file, "\n----\n\n"); diff --git a/src/highlighter.cc b/src/highlighter.cc index 2f3469b7..a47c06b4 100644 --- a/src/highlighter.cc +++ b/src/highlighter.cc @@ -1,3 +1,31 @@ +/** + * 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. + */ #include "config.h" diff --git a/src/hist_source.cc b/src/hist_source.cc index 995e09ee..41a210d6 100644 --- a/src/hist_source.cc +++ b/src/hist_source.cc @@ -73,7 +73,7 @@ void hist_source2::text_value_for_line(textview_curses &tc, int row, char tm_buffer[128]; char line[256]; - if (gmtime_r(&bucket.b_time, &bucket_tm) != NULL) { + if (gmtime_r(&bucket.b_time, &bucket_tm) != nullptr) { strftime(tm_buffer, sizeof(tm_buffer), " %a %b %d %H:%M:%S ", &bucket_tm); diff --git a/src/hotkeys.cc b/src/hotkeys.cc index 62e67148..202d9f0c 100644 --- a/src/hotkeys.cc +++ b/src/hotkeys.cc @@ -37,15 +37,11 @@ #include "pretty_printer.hh" #include "sysclip.hh" #include "log_data_helper.hh" -#include "session_data.hh" #include "command_executor.hh" #include "termios_guard.hh" -#include "readline_callbacks.hh" -#include "readline_possibilities.hh" #include "readline_highlighters.hh" #include "field_overlay_source.hh" #include "hotkeys.hh" -#include "log_format_loader.hh" #include "base/opt_util.hh" #include "shlex.hh" @@ -408,7 +404,7 @@ bool handle_paging_key(int ch) tc->get_dimensions(height, width); 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)); + tc->shift_top(1_vl); } if (lnav_data.ld_last_user_mark[tc] + 1 >= tc->get_inner_height()) { @@ -441,7 +437,7 @@ bool handle_paging_key(int ch) tc->toggle_user_mark(&textview_curses::BM_USER, vis_line_t(new_mark)); if (new_mark == tc->get_top()) { - tc->shift_top(vis_line_t(-1)); + tc->shift_top(-1_vl); } if (new_mark > 0) { lnav_data.ld_last_user_mark[tc] = new_mark - 1; @@ -872,7 +868,6 @@ bool handle_paging_key(int ch) auto index = distance(fc.fc_files.begin(), iter); auto index_vl = vis_line_t(index); - log_debug("index %d", index); lnav_data.ld_files_view.set_top(index_vl); lnav_data.ld_files_view.set_selection(index_vl); } diff --git a/src/line_buffer.cc b/src/line_buffer.cc index f217bd75..316e96bc 100644 --- a/src/line_buffer.cc +++ b/src/line_buffer.cc @@ -274,7 +274,7 @@ line_buffer::line_buffer() lb_seekable(false), lb_last_line_offset(-1) { - if ((this->lb_buffer = (char *)malloc(this->lb_buffer_max)) == NULL) { + if ((this->lb_buffer = (char *)malloc(this->lb_buffer_max)) == nullptr) { throw bad_alloc(); } @@ -283,11 +283,11 @@ line_buffer::line_buffer() line_buffer::~line_buffer() { - auto_fd fd = -1; + auto empty_fd = auto_fd(); // Make sure any shared refs take ownership of the data. this->lb_share_manager.invalidate_refs(); - this->set_fd(fd); + this->set_fd(empty_fd); } void line_buffer::set_fd(auto_fd &fd) @@ -356,7 +356,7 @@ void line_buffer::set_fd(auto_fd &fd) } this->lb_file_offset = newoff; this->lb_buffer_size = 0; - this->lb_fd = fd; + this->lb_fd = std::move(fd); ensure(this->invariant()); } diff --git a/src/line_buffer.hh b/src/line_buffer.hh index e7c9bccb..ca3f2e76 100644 --- a/src/line_buffer.hh +++ b/src/line_buffer.hh @@ -238,7 +238,7 @@ public: }; /** Check the invariants for this object. */ - bool invariant(void) + bool invariant() { require(this->lb_buffer != NULL); require(this->lb_buffer_size <= this->lb_buffer_max); diff --git a/src/listview_curses.cc b/src/listview_curses.cc index d94f042b..018f1cad 100644 --- a/src/listview_curses.cc +++ b/src/listview_curses.cc @@ -202,7 +202,7 @@ void listview_curses::do_update() while (y < bottom) { lr.lr_start = this->lv_left; lr.lr_end = this->lv_left + wrap_width; - if (this->lv_overlay_source != NULL && + if (this->lv_overlay_source != nullptr && this->lv_overlay_source->list_value_for_overlay( *this, y - this->lv_y, bottom - this->lv_y, diff --git a/src/listview_curses.hh b/src/listview_curses.hh index 7ed5fffa..b9e1a84a 100644 --- a/src/listview_curses.hh +++ b/src/listview_curses.hh @@ -487,12 +487,12 @@ public: /** @return The number of rows of data in this view's source data. */ vis_line_t get_inner_height() const { - return vis_line_t(this->lv_source == NULL ? 0 : + return vis_line_t(this->lv_source == nullptr ? 0 : this->lv_source->listview_rows(*this)); }; size_t get_inner_width() const { - return this->lv_source == NULL ? 0 : + return this->lv_source == nullptr ? 0 : this->lv_source->listview_width(*this); }; diff --git a/src/lnav.cc b/src/lnav.cc index 8e568585..8459c8c3 100644 --- a/src/lnav.cc +++ b/src/lnav.cc @@ -67,9 +67,6 @@ #include #include #include -#include -#include -#include #include #include @@ -147,7 +144,6 @@ #include "readline_possibilities.hh" #include "field_overlay_source.hh" #include "url_loader.hh" -#include "log_search_table.hh" #include "shlex.hh" #include "log_actions.hh" #include "archive_manager.hh" @@ -161,7 +157,7 @@ using namespace std::literals::chrono_literals; static multimap DEFAULT_FILES; -struct _lnav_data lnav_data; +struct lnav_data_t lnav_data; const int ZOOM_LEVELS[] = { 1, @@ -258,7 +254,7 @@ bool setup_logline_table(exec_context &ec) textview_curses &log_view = lnav_data.ld_views[LNV_LOG]; bool retval = false; bool update_possibilities = ( - lnav_data.ld_rl_view != NULL && + lnav_data.ld_rl_view != nullptr && ec.ec_local_vars.size() == 1); if (update_possibilities) { @@ -559,7 +555,7 @@ void rebuild_indexes() if (tss->current_file() != cb.front_file) { tss->to_front(cb.front_file); - old_bottoms[LNV_TEXT] = vis_line_t(-1); + old_bottoms[LNV_TEXT] = -1_vl; } if (cb.front_top < 0) { @@ -659,31 +655,31 @@ static bool append_default_files(lnav_flags_t flag) bool retval = true; if (lnav_data.ld_flags & flag) { + auto cwd = ghc::filesystem::current_path(); + pair::iterator, multimap::iterator> range; for (range = DEFAULT_FILES.equal_range(flag); range.first != range.second; range.first++) { - string path = range.first->second; + string path = range.first->second; struct stat st; if (access(path.c_str(), R_OK) == 0) { auto_mem abspath; - path = get_current_dir() + range.first->second; - if ((abspath = realpath(path.c_str(), NULL)) == NULL) { + path = cwd / range.first->second; + if ((abspath = realpath(path.c_str(), nullptr)) == nullptr) { perror("Unable to resolve path"); } else { - logfile_open_options default_loo; - - lnav_data.ld_active_files.fc_file_names[abspath.in()] = default_loo; + lnav_data.ld_active_files.fc_file_names[abspath.in()]; } } else if (stat(path.c_str(), &st) == 0) { fprintf(stderr, "error: cannot read -- %s%s\n", - get_current_dir().c_str(), + cwd.c_str(), path.c_str()); retval = false; } @@ -751,7 +747,7 @@ vis_line_t next_cluster( return last_top; } - return vis_line_t(-1); + return -1_vl; } bool moveto_cluster(vis_line_t(bookmark_vector::*f) (vis_line_t) const, @@ -806,7 +802,7 @@ void previous_cluster(bookmark_type_t *bt, textview_curses *tc) initial_top < (krh.krh_start_line - (1.5 * height)) && (initial_top - new_top) < height) { bookmark_vector &bv = tc->get_bookmarks()[bt]; - new_top = bv.next(std::max(vis_line_t(0), initial_top - height)); + new_top = bv.next(std::max(0_vl, initial_top - height)); } if (new_top != -1) { @@ -937,380 +933,23 @@ static void clear_last_user_mark(void *, listview_curses *lv) } } -/** - * Functor used to compare files based on their device and inode number. - */ -struct same_file { - same_file(const struct stat &stat) : sf_stat(stat) { }; - - /** - * Compare the given log file against the 'stat' given in the constructor. - * @param lf The log file to compare. - * @return True if the dev/inode values in the stat given in the - * constructor matches the stat in the logfile object. - */ - bool operator()(const shared_ptr &lf) const - { - return this->sf_stat.st_dev == lf->get_stat().st_dev && - this->sf_stat.st_ino == lf->get_stat().st_ino; - }; - - const struct stat &sf_stat; -}; - -static std::mutex REALPATH_CACHE_MUTEX; -static std::unordered_map REALPATH_CACHE; - -void file_collection::close_file(const std::shared_ptr &lf) -{ - if (lf->is_valid_filename()) { - std::lock_guard lg(REALPATH_CACHE_MUTEX); - - REALPATH_CACHE.erase(lf->get_filename()); - } else { - this->fc_file_names.erase(lf->get_filename()); - } - auto file_iter = find(this->fc_files.begin(), - this->fc_files.end(), - lf); - if (file_iter != this->fc_files.end()) { - this->fc_files.erase(file_iter); - this->fc_files_generation += 1; - } - - this->regenerate_unique_file_names(); -} - -void file_collection::regenerate_unique_file_names() -{ - unique_path_generator upg; - - for (const auto& lf : this->fc_files) { - upg.add_source(lf); - } - - upg.generate(); - - this->fc_largest_path_length = 0; - for (const auto& lf : this->fc_files) { - const auto& path = lf->get_unique_path(); - - if (path.length() > this->fc_largest_path_length) { - this->fc_largest_path_length = path.length(); - } - } - for (const auto& pair : this->fc_other_files) { - auto bn = ghc::filesystem::path(pair.first).filename().string(); - if (bn.length() > this->fc_largest_path_length) { - this->fc_largest_path_length = bn.length(); - } - } -} - -void file_collection::merge(const file_collection& other) -{ - this->fc_name_to_errors.insert(other.fc_name_to_errors.begin(), - other.fc_name_to_errors.end()); - this->fc_file_names.insert(other.fc_file_names.begin(), - other.fc_file_names.end()); - if (!other.fc_files.empty()) { - this->fc_files.insert(this->fc_files.end(), - other.fc_files.begin(), - other.fc_files.end()); - this->fc_files_generation += 1; - } - for (auto& pair : other.fc_renamed_files) { - pair.first->set_filename(pair.second); - } - this->fc_closed_files.insert(other.fc_closed_files.begin(), - other.fc_closed_files.end()); - this->fc_other_files.insert(other.fc_other_files.begin(), - other.fc_other_files.end()); -} - -/** - * Try to load the given file as a log file. If the file has not already been - * loaded, it will be loaded. If the file has already been loaded, the file - * name will be updated. - * - * @param filename The file name to check. - * @param fd An already-opened descriptor for 'filename'. - * @param required Specifies whether or not the file must exist and be valid. - */ -std::future -file_collection::watch_logfile(const string& filename, logfile_open_options &loo, bool required) +bool update_active_files(const file_collection& new_files) { static loading_observer obs; - file_collection retval; - struct stat st; - int rc; - - if (this->fc_closed_files.count(filename)) { - return make_ready_future(retval); - } - - if (loo.loo_fd != -1) { - rc = fstat(loo.loo_fd, &st); - } - else { - rc = stat(filename.c_str(), &st); - } - - if (rc == 0) { - if (S_ISDIR(st.st_mode) && lnav_data.ld_flags & LNF_RECURSIVE) { - string wilddir = filename + "/*"; - - if (this->fc_file_names.find(wilddir) == this->fc_file_names.end()) { - logfile_open_options default_loo; - - retval.fc_file_names[wilddir] = default_loo; - } - return make_ready_future(retval); - } - if (!S_ISREG(st.st_mode)) { - if (required) { - rc = -1; - errno = EINVAL; - } - else { - return make_ready_future(retval); - } - } - } - if (rc == -1) { - if (required) { - retval.fc_name_to_errors[filename] = strerror(errno); - } - return make_ready_future(retval); - } - - auto file_iter = find_if(this->fc_files.begin(), - this->fc_files.end(), - same_file(st)); - - if (file_iter == this->fc_files.end()) { - if (this->fc_other_files.find(filename) != this->fc_other_files.end()) { - return make_ready_future(retval); - } - return std::async(std::launch::async, [filename, &loo, prog=this->fc_progress, errs=this->fc_name_to_errors]() { - file_collection retval; - - if (errs.find(filename) != errs.end()) { - // The file is broken, no reason to try and reopen - return retval; - } - - file_format_t ff = detect_file_format(filename); - - switch (ff) { - case file_format_t::FF_SQLITE_DB: - attach_sqlite_db(lnav_data.ld_db.in(), filename); - retval.fc_other_files[filename] = "SQLite Database"; - break; - - case file_format_t::FF_ARCHIVE: { - nonstd::optional::iterator> - prog_iter_opt; - - auto res = archive_manager::walk_archive_files( - filename, - [prog, &prog_iter_opt]( - const auto& path, const auto total) { - safe::WriteAccess sp(*prog); - - prog_iter_opt | [&sp](auto prog_iter) { - sp->sp_extractions.erase(prog_iter); - }; - auto prog_iter = sp->sp_extractions.emplace( - sp->sp_extractions.begin(), path, total); - prog_iter_opt = prog_iter; - - return &(*prog_iter); - }, - [&filename, &retval](const auto& tmp_path, - const auto& entry) { - auto ext = entry.path().extension(); - if (ext == ".jar" || ext == ".war" || ext == ".zip") { - return; - } - - auto arc_path = ghc::filesystem::relative( - entry.path(), tmp_path); - auto custom_name = filename / arc_path; - bool is_visible = true; - - if (entry.file_size() == 0) { - log_info("hiding empty archive file: %s", - entry.path().c_str()); - is_visible = false; - } - - log_info("adding file from archive: %s/%s", - filename.c_str(), - entry.path().c_str()); - retval.fc_file_names[entry.path().string()] = - logfile_open_options() - .with_filename(custom_name.string()) - .with_visibility(is_visible) - .with_non_utf_visibility(false) - .with_visible_size_limit(128 * 1024); - }); - if (res.isErr()) { - log_error("archive extraction failed: %s", - res.unwrapErr().c_str()); - retval.clear(); - retval.fc_name_to_errors[filename] = res.unwrapErr(); - } else { - retval.fc_other_files[filename] = "Archive"; - } - { - prog_iter_opt | [&prog](auto prog_iter) { - prog->writeAccess()->sp_extractions.erase(prog_iter); - }; - } - break; - } - - default: - log_info("loading new file: filename=%s", - filename.c_str()); - - /* It's a new file, load it in. */ - try { - shared_ptr lf = make_shared(filename, - loo); - - lf->set_logfile_observer(&obs); - retval.fc_files.push_back(lf); - } catch (logfile::error& e) { - retval.fc_name_to_errors[filename] = e.what(); - } - break; - } - - return retval; - }); - } - else { - auto lf = *file_iter; - - if (lf->is_valid_filename() && lf->get_filename() != filename) { - /* The file is already loaded, but has been found under a different - * name. We just need to update the stored file name. - */ - retval.fc_renamed_files.emplace_back(lf, filename); - } - } - - return make_ready_future(retval); -} - -/** - * Expand a glob pattern and call watch_logfile with the file names that match - * the pattern. - * @param path The glob pattern to expand. - * @param required Passed to watch_logfile. - */ -void file_collection::expand_filename(future_queue &fq, - const string& path, - logfile_open_options &loo, - bool required) -{ - static_root_mem gl; - - { - std::lock_guard lg(REALPATH_CACHE_MUTEX); - - if (REALPATH_CACHE.find(path) != REALPATH_CACHE.end()) { - return; - } - } - - if (is_url(path.c_str())) { - return; - } - else if (glob(path.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) { - int lpc; - - if (gl->gl_pathc == 1 /*&& gl.gl_matchc == 0*/) { - /* It's a pattern that doesn't match any files - * yet, allow it through since we'll load it in - * dynamically. - */ - if (access(path.c_str(), F_OK) == -1) { - required = false; - } - } - if (gl->gl_pathc > 1 || - strcmp(path.c_str(), gl->gl_pathv[0]) != 0) { - required = false; - } - - std::lock_guard lg(REALPATH_CACHE_MUTEX); - for (lpc = 0; lpc < (int)gl->gl_pathc; lpc++) { - auto path_str = std::string(gl->gl_pathv[lpc]); - auto iter = REALPATH_CACHE.find(path_str); - - if (iter == REALPATH_CACHE.end()) { - auto_mem abspath; - - if ((abspath = realpath(gl->gl_pathv[lpc], nullptr)) == - nullptr) { - if (required) { - fprintf(stderr, "Cannot find file: %s -- %s", - gl->gl_pathv[lpc], strerror(errno)); - } - continue; - } else { - auto p = REALPATH_CACHE.emplace(path_str, abspath.in()); - - iter = p.first; - } - } - - if (required || access(iter->second.c_str(), R_OK) == 0) { - fq.push_back(watch_logfile(iter->second, loo, required)); - } - } - } -} - -file_collection file_collection::rescan_files(bool required) -{ - file_collection retval; - future_queue fq([&retval](auto& fc) { - retval.merge(fc); - }); - - for (auto& pair : this->fc_file_names) { - if (pair.second.loo_fd == -1) { - this->expand_filename(fq, pair.first, pair.second, required); - if (lnav_data.ld_flags & LNF_ROTATED) { - string path = pair.first + ".*"; - - this->expand_filename(fq, path, pair.second, false); - } - } else { - fq.push_back(watch_logfile(pair.first, pair.second, required)); - } - - if (retval.fc_files.size() >= 100) { - log_debug("too many new files, breaking..."); - break; - } - } - - fq.pop_to(); - - return retval; -} - -bool update_active_files(const file_collection& new_files) -{ for (const auto& lf : new_files.fc_files) { + lf->set_logfile_observer(&obs); lnav_data.ld_text_source.push_back(lf); } + for (const auto& other_pair : new_files.fc_other_files) { + switch (other_pair.second) { + case file_format_t::FF_SQLITE_DB: + attach_sqlite_db(lnav_data.ld_db.in(), other_pair.first); + break; + default: + break; + } + } lnav_data.ld_active_files.merge(new_files); if (!new_files.fc_files.empty()) { lnav_data.ld_active_files.regenerate_unique_file_names(); @@ -2055,7 +1694,7 @@ static void looper() lnav_data.ld_log_source.text_line_count() == 0 && lnav_data.ld_text_source.text_line_count() > 0) { ensure_view(&lnav_data.ld_views[LNV_TEXT]); - lnav_data.ld_views[LNV_TEXT].set_top(vis_line_t(0)); + lnav_data.ld_views[LNV_TEXT].set_top(0_vl); lnav_data.ld_rl_view->set_alt_value( HELP_MSG_2(f, F, "to switch to the next/previous file")); @@ -2377,11 +2016,11 @@ int main(int argc, char *argv[]) break; case 'R': - lnav_data.ld_flags |= LNF_ROTATED; + lnav_data.ld_active_files.fc_rotated = true; break; case 'r': - lnav_data.ld_flags |= LNF_RECURSIVE; + lnav_data.ld_active_files.fc_recursive = true; break; case 't': @@ -2555,7 +2194,7 @@ int main(int argc, char *argv[]) register_environ_vtab(lnav_data.ld_db.in()); register_views_vtab(lnav_data.ld_db.in()); - register_file_vtab(lnav_data.ld_db.in()); + register_file_vtab(lnav_data.ld_db.in(), lnav_data.ld_active_files); register_regexp_vtab(lnav_data.ld_db.in()); register_fstat_vtab(lnav_data.ld_db.in()); @@ -2613,12 +2252,17 @@ int main(int argc, char *argv[]) lnav_data.ld_views[LNV_HELP] .set_sub_source(&lnav_data.ld_help_source) .set_word_wrap(true); + auto log_fos = new field_overlay_source(lnav_data.ld_log_source, + lnav_data.ld_text_source); + if (lnav_data.ld_flags & LNF_HEADLESS) { + log_fos->fos_show_status = false; + } lnav_data.ld_views[LNV_LOG] .set_sub_source(&lnav_data.ld_log_source) .set_delegate(new action_delegate(lnav_data.ld_log_source)) .add_input_delegate(lnav_data.ld_log_source) - .set_tail_space(vis_line_t(2)) - .set_overlay_source(new field_overlay_source(lnav_data.ld_log_source)); + .set_tail_space(2_vl) + .set_overlay_source(log_fos); lnav_data.ld_views[LNV_TEXT] .set_sub_source(&lnav_data.ld_text_source); lnav_data.ld_views[LNV_HISTOGRAM] @@ -2632,7 +2276,7 @@ int main(int argc, char *argv[]) .set_sub_source(&lnav_data.ld_spectro_source) .set_overlay_source(&lnav_data.ld_spectro_source) .add_input_delegate(lnav_data.ld_spectro_source) - .set_tail_space(vis_line_t(2)); + .set_tail_space(2_vl); lnav_data.ld_doc_view.set_sub_source(&lnav_data.ld_doc_source); lnav_data.ld_example_view.set_sub_source(&lnav_data.ld_example_source); @@ -2741,7 +2385,6 @@ int main(int argc, char *argv[]) } for (lpc = 0; lpc < argc; lpc++) { - logfile_open_options default_loo; auto_mem abspath; struct stat st; @@ -2763,7 +2406,8 @@ int main(int argc, char *argv[]) } #endif else if (is_glob(argv[lpc])) { - lnav_data.ld_active_files.fc_file_names[argv[lpc]] = default_loo; + lnav_data.ld_active_files.fc_file_names + .emplace(argv[lpc], logfile_open_options()); } else if (stat(argv[lpc], &st) == -1) { fprintf(stderr, @@ -2792,13 +2436,14 @@ int main(int argc, char *argv[]) auto fifo_piper = make_shared( fifo_fd.release(), false, - open_temp_file(system_tmpdir() / "lnav.fifo.XXXXXX") + open_temp_file(ghc::filesystem::temp_directory_path() / + "lnav.fifo.XXXXXX") .then([](auto pair) { ghc::filesystem::remove(pair.first); }) .expect("Cannot create temporary file for FIFO") .second); - int fifo_out_fd = fifo_piper->get_fd(); + auto fifo_out_fd = fifo_piper->get_fd(); char desc[128]; snprintf(desc, sizeof(desc), @@ -2819,10 +2464,12 @@ int main(int argc, char *argv[]) if (dir_wild[dir_wild.size() - 1] == '/') { dir_wild.resize(dir_wild.size() - 1); } - lnav_data.ld_active_files.fc_file_names[dir_wild + "/*"] = default_loo; + lnav_data.ld_active_files.fc_file_names + .emplace(dir_wild + "/*", logfile_open_options()); } else { - lnav_data.ld_active_files.fc_file_names[abspath.in()] = default_loo; + lnav_data.ld_active_files.fc_file_names + .emplace(abspath.in(), logfile_open_options()); } } @@ -2915,7 +2562,7 @@ int main(int argc, char *argv[]) stdin_reader = make_shared( STDIN_FILENO, lnav_data.ld_flags & LNF_TIMESTAMP, stdin_out_fd); lnav_data.ld_active_files.fc_file_names["stdin"] - .with_fd(stdin_out_fd) + .with_fd(auto_fd(stdin_out_fd)) .with_include_in_session(false); lnav_data.ld_pipers.push_back(stdin_reader); } @@ -2993,15 +2640,15 @@ int main(int argc, char *argv[]) alerter::singleton().enabled(false); log_tc = &lnav_data.ld_views[LNV_LOG]; - log_tc->set_height(vis_line_t(24)); + log_tc->set_height(24_vl); lnav_data.ld_view_stack.vs_views.push_back(log_tc); // Read all of stdin wait_for_pipers(); rebuild_indexes(); - log_tc->set_top(vis_line_t(0)); + log_tc->set_top(0_vl); text_tc = &lnav_data.ld_views[LNV_TEXT]; - text_tc->set_top(vis_line_t(0)); + text_tc->set_top(0_vl); text_tc->set_height(vis_line_t(text_tc->get_inner_height())); if (lnav_data.ld_log_source.text_line_count() == 0 && lnav_data.ld_text_source.text_line_count() > 0) { diff --git a/src/lnav.hh b/src/lnav.hh index 420a301d..a57f31fc 100644 --- a/src/lnav.hh +++ b/src/lnav.hh @@ -73,6 +73,8 @@ #include "preview_status_source.hh" #include "sql_util.hh" #include "archive_manager.hh" +#include "file_collection.hh" +#include "view_helpers.hh" /** The command modes that are available while viewing a file. */ typedef enum { @@ -97,8 +99,6 @@ enum { LNB_HELP, LNB_HEADLESS, LNB_QUIET, - LNB_ROTATED, - LNB_RECURSIVE, LNB_CHECK_CONFIG, LNB_INSTALL, LNB_UPDATE_FORMATS, @@ -110,9 +110,6 @@ enum { typedef enum { LNF_SYSLOG = (1L << LNB_SYSLOG), - LNF_ROTATED = (1L << LNB_ROTATED), - LNF_RECURSIVE = (1L << LNB_RECURSIVE), - LNF_TIMESTAMP = (1L << LNB_TIMESTAMP), LNF_HELP = (1L << LNB_HELP), LNF_HEADLESS = (1L << LNB_HEADLESS), @@ -126,22 +123,6 @@ typedef enum { LNF__ALL = (LNF_SYSLOG|LNF_HELP), } lnav_flags_t; -/** The different views available. */ -typedef enum { - LNV_LOG, - LNV_TEXT, - LNV_HELP, - LNV_HISTOGRAM, - LNV_DB, - LNV_SCHEMA, - LNV_PRETTY, - LNV_SPECTRO, - - LNV__MAX -} lnav_view_t; - -extern const char *lnav_view_strings[LNV__MAX + 1]; - extern const char *lnav_zoom_strings[]; /** The status bars. */ @@ -157,7 +138,7 @@ typedef enum { } lnav_status_t; typedef std::pair ppid_time_pair_t; -typedef std::pair session_pair_t; +typedef std::pair session_pair_t; class input_state_tracker : public log_state_dumper { public: @@ -165,7 +146,7 @@ public: memset(this->ist_recent_key_presses, 0, sizeof(this->ist_recent_key_presses)); }; - void log_state() { + void log_state() override { log_info("recent_key_presses: index=%d", this->ist_index); for (int lpc = 0; lpc < COUNT; lpc++) { log_msg_extra(" 0x%x (%c)", this->ist_recent_key_presses[lpc], @@ -195,7 +176,7 @@ struct key_repeat_history { void update(int ch, vis_line_t top) { struct timeval now, diff; - gettimeofday(&now, NULL); + gettimeofday(&now, nullptr); timersub(&now, &this->krh_last_press_time, &diff); if (diff.tv_sec >= 1 || diff.tv_usec > (750 * 1000)) { this->krh_key = 0; @@ -213,44 +194,7 @@ struct key_repeat_history { }; }; -struct scan_progress { - std::list sp_extractions; -}; - -using safe_scan_progress = safe::Safe; - -struct file_collection { - std::map fc_name_to_errors; - std::map fc_file_names; - std::vector> fc_files; - int fc_files_generation{0}; - std::vector, std::string>> - fc_renamed_files; - std::set fc_closed_files; - std::map fc_other_files; - std::shared_ptr fc_progress; - size_t fc_largest_path_length{0}; - - file_collection() - : fc_progress(std::make_shared>()) {} - - void clear() { - this->fc_name_to_errors.clear(); - this->fc_file_names.clear(); - this->fc_files.clear(); - this->fc_closed_files.clear(); - this->fc_other_files.clear(); - } - file_collection rescan_files(bool required = false); - void expand_filename(future_queue &fq, const std::string& path, logfile_open_options &loo, bool required); - std::future - watch_logfile(const std::string& filename, logfile_open_options &loo, bool required); - void merge(const file_collection &other); - void close_file(const std::shared_ptr &lf); - void regenerate_unique_file_names(); -}; - -struct _lnav_data { +struct lnav_data_t { std::map> ld_session_id; time_t ld_session_time; time_t ld_session_load_time; @@ -353,7 +297,7 @@ struct _lnav_data { struct key_repeat_history ld_key_repeat_history; }; -extern struct _lnav_data lnav_data; +extern struct lnav_data_t lnav_data; extern readline_context::command_map_t lnav_commands; extern const int ZOOM_LEVELS[]; @@ -367,12 +311,6 @@ extern const ssize_t ZOOM_COUNT; void rebuild_hist(); void rebuild_indexes(); -void execute_examples(); -attr_line_t eval_example(const help_text &ht, const help_example &ex); - -bool ensure_view(textview_curses *expected_tc); -bool toggle_view(textview_curses *toggle_tc); -void layout_views(); bool setup_logline_table(exec_context &ec); diff --git a/src/lnav_commands.cc b/src/lnav_commands.cc index cc2151e1..ff82aa9f 100644 --- a/src/lnav_commands.cc +++ b/src/lnav_commands.cc @@ -610,10 +610,7 @@ static void write_line_to(FILE *outfile, const attr_line_t &al) fprintf(outfile, " // %s\n", bm->bm_comment.c_str()); } if (!bm->bm_tags.empty()) { - fprintf(outfile, " -- %s\n", - join(bm->bm_tags.begin(), - bm->bm_tags.end(), - " ").c_str()); + fmt::print(outfile, " -- {}\n", fmt::join(bm->bm_tags, " ")); } } } @@ -921,7 +918,7 @@ static Result com_save_to(exec_context &ec, string cmdline, vect lnav_data.ld_preview_source .replace_with(al) - .set_text_format(detect_text_format(buffer, rc)) + .set_text_format(detect_text_format(al.get_string())) .truncate_to(10); lnav_data.ld_preview_status_source.get_description() .set_value("First lines of file: %s", fn.c_str()); @@ -1827,7 +1824,6 @@ static Result com_open(exec_context &ec, string cmdline, vector< } } if (file_iter == lnav_data.ld_active_files.fc_files.end()) { - logfile_open_options default_loo; auto_mem abspath; struct stat st; @@ -1849,7 +1845,7 @@ static Result com_open(exec_context &ec, string cmdline, vector< #endif } else if (is_glob(fn.c_str())) { - file_names[fn] = default_loo; + file_names.emplace(fn, logfile_open_options()); retval = "info: watching -- " + fn; } else if (stat(fn.c_str(), &st) == -1) { @@ -1871,13 +1867,14 @@ static Result com_open(exec_context &ec, string cmdline, vector< auto fifo_piper = make_shared( fifo_fd.release(), false, - open_temp_file(system_tmpdir() / "lnav.fifo.XXXXXX") + open_temp_file(ghc::filesystem::temp_directory_path() / + "lnav.fifo.XXXXXX") .then([](auto pair) { ghc::filesystem::remove(pair.first); }) .expect("Cannot create temporary file for FIFO") .second); - int fifo_out_fd = fifo_piper->get_fd(); + auto fifo_out_fd = fifo_piper->get_fd(); char desc[128]; snprintf(desc, sizeof(desc), @@ -1888,7 +1885,7 @@ static Result com_open(exec_context &ec, string cmdline, vector< lnav_data.ld_pipers.push_back(fifo_piper); } } - else if ((abspath = realpath(fn.c_str(), NULL)) == NULL) { + else if ((abspath = realpath(fn.c_str(), nullptr)) == nullptr) { return ec.make_error("cannot find file -- {}", fn); } else if (S_ISDIR(st.st_mode)) { @@ -1897,7 +1894,7 @@ static Result com_open(exec_context &ec, string cmdline, vector< if (dir_wild[dir_wild.size() - 1] == '/') { dir_wild.resize(dir_wild.size() - 1); } - file_names[dir_wild + "/*"] = default_loo; + file_names.emplace(dir_wild + "/*", logfile_open_options()); retval = "info: watching -- " + dir_wild; } else if (!S_ISREG(st.st_mode)) { @@ -1910,12 +1907,12 @@ static Result com_open(exec_context &ec, string cmdline, vector< } else { fn = abspath.in(); - file_names[fn] = default_loo; + file_names.emplace(fn, logfile_open_options()); retval = "info: opened -- " + fn; files_to_front.emplace_back(fn, top); closed_files.push_back(fn); - if (lnav_data.ld_rl_view != NULL) { + if (lnav_data.ld_rl_view != nullptr) { lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1( X, "to close the file")); } @@ -1933,7 +1930,7 @@ static Result com_open(exec_context &ec, string cmdline, vector< if (is_glob(fn.c_str())) { static_root_mem gl; - if (glob(fn.c_str(), GLOB_NOCHECK, NULL, gl.inout()) == 0) { + if (glob(fn.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) { attr_line_t al; for (size_t lpc = 0; lpc < gl->gl_pathc && lpc < 10; lpc++) { @@ -2547,7 +2544,7 @@ static Result com_pt_time(exec_context &ec, string cmdline, vect new_time.tv_sec = timegm(&tm.et_tm); } else { - dts.scan(args[1].c_str(), args[1].size(), NULL, &tm, new_time); + dts.scan(args[1].c_str(), args[1].size(), nullptr, &tm, new_time); } if (ec.ec_dry_run) { retval = ""; @@ -2607,7 +2604,7 @@ static Result com_summarize(exec_context &ec, string cmdline, ve query.c_str(), -1, stmt.out(), - NULL); + nullptr); if (retcode != SQLITE_OK) { const char *errmsg = sqlite3_errmsg(lnav_data.ld_db); @@ -2729,14 +2726,14 @@ static Result com_summarize(exec_context &ec, string cmdline, ve query.c_str(), -1, stmt.out(), - NULL); + nullptr); if (retcode != SQLITE_OK) { const char *errmsg = sqlite3_errmsg(lnav_data.ld_db); return ec.make_error("{}", errmsg); } - else if (stmt == NULL) { + else if (stmt == nullptr) { retval = ""; } else { @@ -2814,7 +2811,7 @@ static Result com_add_test(exec_context &ec, string cmdline, vec getenv("LNAV_SRC"), hash_string(line).c_str()); - if ((file = fopen(path, "w")) == NULL) { + if ((file = fopen(path, "w")) == nullptr) { perror("fopen failed"); } else { @@ -3041,7 +3038,7 @@ static Result com_toggle_field(exec_context &ec, string cmdline, if (format->hide_field(name, hide)) { found_fields.push_back(args[lpc]); if (hide) { - if (lnav_data.ld_rl_view != NULL) { + if (lnav_data.ld_rl_view != nullptr) { lnav_data.ld_rl_view->set_alt_value( HELP_MSG_1(x, "to quickly show hidden fields")); } @@ -3053,21 +3050,13 @@ static Result com_toggle_field(exec_context &ec, string cmdline, } if (missing_fields.empty()) { - string all_fields = join(found_fields.begin(), - found_fields.end(), - ", "); - - if (hide) { - retval = "info: hiding field(s) -- " + all_fields; - } else { - retval = "info: showing field(s) -- " + all_fields; - } + auto visibility = hide ? "hiding" : "showing"; + retval = fmt::format("info: {} field(s) -- {}", + visibility, + fmt::join(found_fields, ", ")); } else { - string all_fields = join(missing_fields.begin(), - missing_fields.end(), - ", "); - - return ec.make_error("unknown field(s) -- {}", all_fields); + return ec.make_error("unknown field(s) -- {}", + fmt::join(missing_fields, ", ")); } } } @@ -3349,7 +3338,7 @@ static Result com_alt_msg(exec_context &ec, string cmdline, vect retval = ""; } else if (args.size() == 1) { - if (lnav_data.ld_rl_view != NULL) { + if (lnav_data.ld_rl_view != nullptr) { lnav_data.ld_rl_view->set_alt_value(""); } retval = ""; @@ -3357,7 +3346,7 @@ static Result com_alt_msg(exec_context &ec, string cmdline, vect else { string msg = remaining_args(cmdline, args); - if (lnav_data.ld_rl_view != NULL) { + if (lnav_data.ld_rl_view != nullptr) { lnav_data.ld_rl_view->set_alt_value(msg); } @@ -3474,8 +3463,7 @@ static Result com_config(exec_context &ec, string cmdline, vecto lnav_data.ld_preview_source .replace_with(al) - .set_text_format(detect_text_format(old_value.c_str(), - old_value.size())) + .set_text_format(detect_text_format(old_value)) .truncate_to(10); lnav_data.ld_preview_status_source.get_description() .set_value("Value of option: %s", option.c_str()); @@ -3580,7 +3568,7 @@ static Result com_reset_config(exec_context &ec, string cmdline, ypc.ypc_active_paths.insert(option); ypc.update_callbacks(); - if (option == "*" || (ypc.ypc_current_handler != NULL || + if (option == "*" || (ypc.ypc_current_handler != nullptr || !ypc.ypc_handler_stack.empty())) { if (!ec.ec_dry_run) { reset_config(option); @@ -4181,9 +4169,7 @@ static void sql_prompt(vector &args) lnav_data.ld_bottom_source.update_loading(0, 0); lnav_data.ld_status[LNS_BOTTOM].do_update(); - field_overlay_source *fos; - - fos = (field_overlay_source *) log_view.get_overlay_source(); + auto* fos = (field_overlay_source *) log_view.get_overlay_source(); fos->fos_active_prev = fos->fos_active; if (!fos->fos_active) { fos->fos_active = true; diff --git a/src/lnav_util.cc b/src/lnav_util.cc index 0ceaf703..fb2a8ef6 100644 --- a/src/lnav_util.cc +++ b/src/lnav_util.cc @@ -37,18 +37,15 @@ #include #include #include -#include - -#include #include "auto_fd.hh" #include "lnav_util.hh" #include "pcrepp/pcrepp.hh" -#include "lnav_config.hh" #include "base/result.h" #include "ansi_scrubber.hh" #include "view_curses.hh" #include "archive_manager.hh" +#include "fmt/format.h" using namespace std; @@ -83,11 +80,11 @@ std::string hash_bytes(const char *str1, size_t s1len, ...) va_start(args, s1len); context.Init(0, 0); - while (str1 != NULL) { + while (str1 != nullptr) { context.Update(str1, s1len); str1 = va_arg(args, const char *); - if (str1 == NULL) { + if (str1 == nullptr) { break; } s1len = va_arg(args, size_t); @@ -101,7 +98,7 @@ std::string hash_bytes(const char *str1, size_t s1len, ...) std::string time_ago(time_t last_time, bool convert_local) { - time_t delta, current_time = time(NULL); + time_t delta, current_time = time(nullptr); const char *fmt; char buffer[64]; int amount; @@ -155,7 +152,7 @@ std::string precise_time_ago(const struct timeval &tv, bool convert_local) { struct timeval now, diff; - gettimeofday(&now, NULL); + gettimeofday(&now, nullptr); if (convert_local) { now.tv_sec = convert_log_time_to_local(now.tv_sec); } @@ -194,31 +191,12 @@ std::string precise_time_ago(const struct timeval &tv, bool convert_local) } } -std::string get_current_dir(void) -{ - char cwd[FILENAME_MAX]; - std::string retval = "."; - - if (getcwd(cwd, sizeof(cwd)) == NULL) { - perror("getcwd"); - } - else { - retval = std::string(cwd); - } - - if (retval != "/") { - retval += "/"; - } - - return retval; -} - -bool change_to_parent_dir(void) +bool change_to_parent_dir() { bool retval = false; char cwd[3] = ""; - if (getcwd(cwd, sizeof(cwd)) == NULL) { + if (getcwd(cwd, sizeof(cwd)) == nullptr) { /* perror("getcwd"); */ } if (strcmp(cwd, "/") != 0) { @@ -243,28 +221,7 @@ void split_ws(const std::string &str, std::vector &toks_out) } } -std::pair split_path(const char *path, ssize_t len) -{ - ssize_t dir_len = len; - - while (dir_len >= 0 && (path[dir_len] == '/' || path[dir_len] == '\\')) { - dir_len -= 1; - } - - while (dir_len >= 0) { - if (path[dir_len] == '/' || path[dir_len] == '\\') { - return make_pair(string(path, dir_len), - string(&path[dir_len + 1], len - dir_len)); - } - - dir_len -= 1; - } - - return make_pair(path[0] == '/' ? "/" : ".", - path[0] == '/' ? string(&path[1], len - 1) : string(path, len)); -} - -file_format_t detect_file_format(const std::string &filename) +file_format_t detect_file_format(const ghc::filesystem::path &filename) { if (archive_manager::is_archive(filename)) { return file_format_t::FF_ARCHIVE; @@ -273,7 +230,7 @@ file_format_t detect_file_format(const std::string &filename) file_format_t retval = file_format_t::FF_UNKNOWN; auto_fd fd; - if ((fd = open(filename.c_str(), O_RDONLY)) != -1) { + if ((fd = openp(filename, O_RDONLY)) != -1) { char buffer[32]; ssize_t rc; @@ -446,7 +403,7 @@ bool next_format(const char * const fmt[], int &index, int &locked_index) if (locked_index == -1) { index += 1; - if (fmt[index] == NULL) { + if (fmt[index] == nullptr) { retval = false; } } @@ -469,7 +426,7 @@ const char *date_time_scanner::scan(const char *time_dest, { int curr_time_fmt = -1; bool found = false; - const char *retval = NULL; + const char *retval = nullptr; if (!time_fmt) { time_fmt = PTIMEC_FORMAT_STR; @@ -486,7 +443,7 @@ const char *date_time_scanner::scan(const char *time_dest, char time_cp[time_len + 1]; int gmt_int, off; - retval = NULL; + retval = nullptr; memcpy(time_cp, time_dest, time_len); time_cp[time_len] = '\0'; if (sscanf(time_cp, "+%d%n", &gmt_int, &off) == 1) { @@ -495,7 +452,7 @@ const char *date_time_scanner::scan(const char *time_dest, 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 = NULL; + tm_out->et_tm.tm_zone = nullptr; #endif tm_out->et_tm.tm_isdst = 0; gmt = tm2sec(&tm_out->et_tm); @@ -517,7 +474,7 @@ const char *date_time_scanner::scan(const char *time_dest, #ifdef HAVE_STRUCT_TM_TM_ZONE if (!this->dts_keep_base_tz) { - tm_out->et_tm.tm_zone = NULL; + tm_out->et_tm.tm_zone = nullptr; } #endif if (func(tm_out, time_dest, off, time_len)) { @@ -548,7 +505,7 @@ const char *date_time_scanner::scan(const char *time_dest, #ifdef HAVE_STRUCT_TM_TM_ZONE if (!this->dts_keep_base_tz) { - tm_out->et_tm.tm_zone = NULL; + tm_out->et_tm.tm_zone = nullptr; } #endif if (ptime_fmt(time_fmt[curr_time_fmt], tm_out, time_dest, off, time_len) && @@ -563,7 +520,7 @@ const char *date_time_scanner::scan(const char *time_dest, this->to_localtime(gmt, *tm_out); #ifdef HAVE_STRUCT_TM_TM_ZONE - tm_out->et_tm.tm_zone = NULL; + tm_out->et_tm.tm_zone = nullptr; #endif tm_out->et_tm.tm_isdst = 0; } @@ -582,10 +539,10 @@ const char *date_time_scanner::scan(const char *time_dest, } if (!found) { - retval = NULL; + retval = nullptr; } - if (retval != NULL) { + if (retval != nullptr) { /* Try to pull out the milli/micro-second value. */ if (retval[0] == '.' || retval[0] == ',') { off_t off = (retval - time_dest) + 1; @@ -649,7 +606,10 @@ string build_path(const vector &paths) } retval += path.string(); } - retval += ":" + string(getenv("PATH")); + auto env_path = getenv_opt("PATH"); + if (env_path) { + retval += ":" + string(*env_path); + } return retval; } @@ -695,17 +655,6 @@ size_t abbreviate_str(char *str, size_t len, size_t max_len) return len; } -ghc::filesystem::path system_tmpdir() -{ - const char *tmpdir; - - if ((tmpdir = getenv("TMPDIR")) == nullptr) { - tmpdir = _PATH_VARTMP; - } - - return ghc::filesystem::path(tmpdir); -} - Result, std::string> open_temp_file(const ghc::filesystem::path &pattern) { @@ -715,7 +664,8 @@ open_temp_file(const ghc::filesystem::path &pattern) strcpy(pattern_copy, pattern_str.c_str()); if ((fd = mkstemp(pattern_copy)) == -1) { - throw Err(strerror(errno)); + return Err(fmt::format("unable to create temporary file: {} -- {}", + pattern.string(), strerror(errno))); } return Ok(make_pair(ghc::filesystem::path(pattern_copy), fd)); diff --git a/src/lnav_util.hh b/src/lnav_util.hh index ffe10ca3..924e9acc 100644 --- a/src/lnav_util.hh +++ b/src/lnav_util.hh @@ -55,50 +55,9 @@ #include "byte_array.hh" #include "optional.hpp" #include "base/result.h" +#include "fmt/format.h" #include "ghc/filesystem.hpp" -inline std::string trim(const std::string &str) -{ - std::string::size_type start, end; - - for (start = 0; start < str.size() && isspace(str[start]); start++); - for (end = str.size(); end > 0 && isspace(str[end - 1]); end--); - - return str.substr(start, end - start); -} - -inline std::string tolower(const char *str) -{ - std::string retval; - - for (int lpc = 0; str[lpc]; lpc++) { - retval.push_back(::tolower(str[lpc])); - } - - return retval; -} - -inline std::string tolower(const std::string &str) -{ - return tolower(str.c_str()); -} - -inline std::string toupper(const char *str) -{ - std::string retval; - - for (int lpc = 0; str[lpc]; lpc++) { - retval.push_back(::toupper(str[lpc])); - } - - return retval; -} - -inline std::string toupper(const std::string &str) -{ - return toupper(str.c_str()); -} - #undef rounddown /** @@ -205,26 +164,36 @@ object_field_t object_field(UnaryFunction &func, return object_field_t(func, mem); } -std::string get_current_dir(void); - -bool change_to_parent_dir(void); +bool change_to_parent_dir(); void split_ws(const std::string &str, std::vector &toks_out); -std::pair split_path(const char *path, ssize_t len); - -inline -std::pair split_path(const std::string &path) { - return split_path(path.c_str(), path.size()); -}; - enum class file_format_t { FF_UNKNOWN, FF_SQLITE_DB, FF_ARCHIVE, }; -file_format_t detect_file_format(const std::string &filename); +file_format_t detect_file_format(const ghc::filesystem::path& filename); + +template<> +struct fmt::formatter : fmt::formatter { + template + auto format(file_format_t ff, FormatContext& ctx) { + fmt::string_view name = "unknown"; + switch (ff) { + case file_format_t::FF_SQLITE_DB: + name = "SQLite Database"; + break; + case file_format_t::FF_ARCHIVE: + name = "Archive"; + break; + default: + break; + } + return fmt::formatter::format(name, ctx); + } +}; bool next_format(const char * const fmt[], int &index, int &locked_index); @@ -233,23 +202,11 @@ namespace std { inline string to_string(const char *s) { return s; } } -template -inline std::string join(InputIt first, InputIt last, const std::string &delim) -{ - std::string retval; - return std::accumulate(first, last, retval, [&] ( - auto l, auto r) { - std::string lstr = std::to_string(l); - - return lstr + (lstr.empty() ? "" : delim) + std::to_string(r); - }); -} - inline bool is_glob(const char *fn) { - return (strchr(fn, '*') != NULL || - strchr(fn, '?') != NULL || - strchr(fn, '[') != NULL); + return (strchr(fn, '*') != nullptr || + strchr(fn, '?') != nullptr || + strchr(fn, '[') != nullptr); }; bool is_url(const char *fn); @@ -313,14 +270,14 @@ struct date_time_scanner { this->clear(); }; - void clear(void) { + void clear() { this->dts_base_time = 0; memset(&this->dts_base_tm, 0, sizeof(this->dts_base_tm)); this->dts_fmt_lock = -1; this->dts_fmt_len = -1; }; - void unlock(void) { + void unlock() { this->dts_fmt_lock = -1; this->dts_fmt_len = -1; } @@ -423,16 +380,13 @@ struct date_time_scanner { template size_t strtonum(T &num_out, const char *data, size_t len); -inline bool pollfd_ready(const std::vector &pollfds, int fd, short events = POLLIN|POLLHUP) { - for (std::vector::const_iterator iter = pollfds.begin(); - iter != pollfds.end(); - ++iter) { - if (iter->fd == fd && iter->revents & events) { - return true; - } - } - - return false; +inline bool pollfd_ready(const std::vector &pollfds, int fd, + short events = POLLIN | POLLHUP) +{ + return std::any_of(pollfds.begin(), pollfds.end(), + [fd, events](const auto &entry) { + return entry.fd == fd && entry.revents & events; + }); }; inline void rusagesub(const struct rusage &left, const struct rusage &right, struct rusage &diff_out) @@ -475,8 +429,6 @@ inline void rusageadd(const struct rusage &left, const struct rusage &right, str size_t abbreviate_str(char *str, size_t len, size_t max_len); -ghc::filesystem::path system_tmpdir(); - inline int statp(const ghc::filesystem::path &path, struct stat *buf) { return stat(path.c_str(), buf); } diff --git a/src/log_actions.cc b/src/log_actions.cc index 2d785ced..4071d339 100644 --- a/src/log_actions.cc +++ b/src/log_actions.cc @@ -122,7 +122,8 @@ static string execute_action(log_data_helper &ldh, auto pp = make_shared( out_pipe.read_end(), false, - open_temp_file(system_tmpdir() / "lnav.action.XXXXXX") + open_temp_file(ghc::filesystem::temp_directory_path() / + "lnav.action.XXXXXX") .then([](auto pair) { ghc::filesystem::remove(pair.first); }) diff --git a/src/log_format.hh b/src/log_format.hh index ae56b2ce..b70d1747 100644 --- a/src/log_format.hh +++ b/src/log_format.hh @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -168,7 +169,7 @@ public: } }; - bool is_marked(void) const { return this->ll_level & LEVEL_MARK; }; + bool is_marked() const { return this->ll_level & LEVEL_MARK; }; void set_time_skew(bool val) { if (val) { @@ -227,7 +228,7 @@ public: /** * @return True if there is a schema value set for this log line. */ - bool has_schema(void) const + bool has_schema() const { return (this->ll_schema[0] != 0 || this->ll_schema[1] != 0); @@ -508,7 +509,7 @@ public: /** * @return The collection of builtin log formats. */ - static std::vector &get_root_formats(void); + static std::vector &get_root_formats(); /** * Template used to register log formats during initialization. @@ -531,7 +532,7 @@ public: return lf; } } - return NULL; + return nullptr; } struct action_def { @@ -618,20 +619,20 @@ public: }; virtual const logline_value_stats *stats_for_value(const intern_string_t &name) const { - return NULL; + return nullptr; }; virtual std::unique_ptr specialized(int fmt_lock = -1) = 0; - virtual log_vtab_impl *get_vtab_impl(void) const { - return NULL; + virtual log_vtab_impl *get_vtab_impl() const { + return nullptr; }; virtual void get_subline(const logline &ll, shared_buffer_ref &sbr, bool full_message = false) { }; virtual const std::vector *get_actions(const logline_value &lv) const { - return NULL; + return nullptr; }; virtual const std::set get_source_path() const { @@ -648,7 +649,7 @@ public: const char * const *get_timestamp_formats() const { if (this->lf_timestamp_format.empty()) { - return NULL; + return nullptr; } return &this->lf_timestamp_format[0]; @@ -705,7 +706,7 @@ protected: this->pf_timestamp_index = this->pcre.name_index("timestamp"); }; - pcre_format() : name(NULL), pcre("") { }; + pcre_format() : name(nullptr), pcre("") { }; const char *name; pcrepp pcre; @@ -773,36 +774,23 @@ public: }; struct pattern { - pattern() : p_pcre(NULL), - p_timestamp_field_index(-1), - p_level_field_index(-1), - p_module_field_index(-1), - p_opid_field_index(-1), - p_body_field_index(-1), - p_timestamp_end(-1), - p_module_format(false) { - - }; - std::string p_config_path; std::string p_string; - pcrepp *p_pcre; + pcrepp *p_pcre{nullptr}; std::vector p_value_by_index; std::vector p_numeric_value_indexes; - int p_timestamp_field_index; - int p_level_field_index; - int p_module_field_index; - int p_opid_field_index; - int p_body_field_index; - int p_timestamp_end; - bool p_module_format; + int p_timestamp_field_index{-1}; + int p_level_field_index{-1}; + int p_module_field_index{-1}; + int p_opid_field_index{-1}; + int p_body_field_index{-1}; + int p_timestamp_end{-1}; + bool p_module_format{false}; }; struct level_pattern { - level_pattern() : lp_pcre(NULL) { }; - std::string lp_regex; - pcrepp *lp_pcre; + pcrepp *lp_pcre{nullptr}; }; external_log_format(const intern_string_t name) @@ -824,7 +812,7 @@ public: this->jlf_line_offsets.reserve(128); }; - const intern_string_t get_name(void) const { + const intern_string_t get_name() const { return this->elf_name; }; @@ -879,10 +867,10 @@ public: } if (this->elf_type == ELF_TYPE_JSON) { - this->jlf_parse_context.reset(new yajlpp_parse_context(this->elf_name.to_string())); + this->jlf_parse_context = std::make_shared(this->elf_name.to_string()); this->jlf_yajl_handle.reset(yajl_alloc( &this->jlf_parse_context->ypc_callbacks, - NULL, + nullptr, this->jlf_parse_context.get())); yajl_config(this->jlf_yajl_handle.in(), yajl_dont_validate_strings, 1); this->jlf_cached_line.reserve(16 * 1024); @@ -895,7 +883,7 @@ public: }; const logline_value_stats *stats_for_value(const intern_string_t &name) const { - const logline_value_stats *retval = NULL; + const logline_value_stats *retval = nullptr; for (size_t lpc = 0; lpc < this->elf_numeric_value_defs.size(); lpc++) { value_def &vd = *this->elf_numeric_value_defs[lpc]; @@ -911,10 +899,10 @@ public: void get_subline(const logline &ll, shared_buffer_ref &sbr, bool full_message); - log_vtab_impl *get_vtab_impl(void) const; + log_vtab_impl *get_vtab_impl() const; const std::vector *get_actions(const logline_value &lv) const { - const std::vector *retval = NULL; + const std::vector *retval = nullptr; const auto iter = this->elf_value_defs.find(lv.lv_name); if (iter != this->elf_value_defs.end()) { @@ -998,7 +986,7 @@ public: long value_line_count(const intern_string_t ist, bool top_level, - const unsigned char *str = NULL, + const unsigned char *str = nullptr, ssize_t len = -1) const { const auto iter = this->elf_value_defs.find(ist); long line_count = (str != NULL) ? std::count(&str[0], &str[len], '\n') + 1 : 1; @@ -1161,11 +1149,7 @@ private: class module_format { public: - module_format() : mf_mod_format(NULL) { - - }; - - external_log_format *mf_mod_format; + external_log_format *mf_mod_format{nullptr}; }; #endif diff --git a/src/log_format_loader.cc b/src/log_format_loader.cc index 3fb66d2c..8b13a2b0 100644 --- a/src/log_format_loader.cc +++ b/src/log_format_loader.cc @@ -65,7 +65,7 @@ typedef map log_formats_map_t; static log_formats_map_t LOG_FORMATS; struct userdata { - std::string ud_format_path; + ghc::filesystem::path ud_format_path; vector *ud_format_names{nullptr}; std::vector *ud_errors{nullptr}; }; @@ -81,7 +81,7 @@ static external_log_format *ensure_format(const yajlpp_provider_context &ypc, us LOG_FORMATS[name] = retval = new external_log_format(name); log_debug("Loading format -- %s", name.get()); } - retval->elf_source_path.insert(ud->ud_format_path.substr(0, ud->ud_format_path.rfind('/'))); + retval->elf_source_path.insert(ud->ud_format_path.filename().string()); if (find(formats->begin(), formats->end(), name) == formats->end()) { formats->push_back(name); @@ -810,7 +810,9 @@ static void format_error_reporter(const yajlpp_parse_context &ypc, } } -std::vector load_format_file(const string &filename, std::vector &errors) +std::vector +load_format_file(const ghc::filesystem::path &filename, + std::vector &errors) { std::vector retval; struct userdata ud; @@ -823,14 +825,10 @@ std::vector load_format_file(const string &filename, std::vecto yajlpp_parse_context ypc(filename, &root_format_handler); ypc.ypc_userdata = &ud; ypc.with_obj(ud); - if ((fd = open(filename.c_str(), O_RDONLY)) == -1) { - char errmsg[1024]; - - snprintf(errmsg, sizeof(errmsg), - "error:unable to open format file '%s' -- %s", - filename.c_str(), - strerror(errno)); - errors.emplace_back(errmsg); + if ((fd = openp(filename, O_RDONLY)) == -1) { + errors.emplace_back(fmt::format( + "error: unable to open format file '{}' -- {}", + filename.string(), strerror(errno))); } else { auto_mem handle(yajl_free); @@ -838,7 +836,7 @@ std::vector load_format_file(const string &filename, std::vecto off_t offset = 0; ssize_t rc = -1; - handle = yajl_alloc(&ypc.ypc_callbacks, NULL, &ypc); + handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc); ypc.with_handle(handle) .with_error_reporter(format_error_reporter); yajl_config(handle, yajl_allow_comments, 1); @@ -848,11 +846,10 @@ std::vector load_format_file(const string &filename, std::vecto break; } else if (rc == -1) { - errors.push_back( - "error:" + - filename + - ":unable to read file -- " + - string(strerror(errno))); + errors.push_back(fmt::format( + "error:{}:unable to read file -- {}", + filename.string(), + strerror(errno))); break; } if (offset == 0 && (rc > 2) && @@ -1105,7 +1102,7 @@ void extract_metadata_from_file(struct script_metadata &meta_inout) log_warning("unable to open script -- %s", meta_inout.sm_path.c_str()); } else if (!S_ISREG(st.st_mode)) { log_warning("not a regular file -- %s", meta_inout.sm_path.c_str()); - } else if ((fp = fopen(meta_inout.sm_path.c_str(), "r")) != NULL) { + } else if ((fp = fopen(meta_inout.sm_path.c_str(), "r")) != nullptr) { size_t len; len = fread(buffer, 1, sizeof(buffer), fp.in()); diff --git a/src/log_format_loader.hh b/src/log_format_loader.hh index 58d5316c..9511949b 100644 --- a/src/log_format_loader.hh +++ b/src/log_format_loader.hh @@ -37,10 +37,13 @@ #include #include +#include "ghc/filesystem.hpp" + class log_vtab_manager; std::vector load_format_file( - const std::string &filename, std::vector &errors); + const ghc::filesystem::path &filename, + std::vector &errors); void load_formats(const std::vector &extra_paths, std::vector &errors); diff --git a/src/log_level_re.cc b/src/log_level_re.cc index 339c8594..f029b2d5 100644 --- a/src/log_level_re.cc +++ b/src/log_level_re.cc @@ -1,4 +1,4 @@ -/* Generated by re2c 1.1.1 on Thu Apr 18 18:55:08 2019 */ +/* Generated by re2c 2.0.3 on Tue Nov 3 13:27:28 2020 */ #line 1 "../../lnav2/src/log_level_re.re" /** * Copyright (c) 2018, Timothy Stack @@ -31,9 +31,7 @@ #include "config.h" -#include #include -#include #include "log_level.hh" @@ -66,14 +64,14 @@ log_level_t string2level(const char *levelstr, ssize_t len, bool exact) # define YYRESTORE() YYCURSOR = YYMARKER # define YYSTAGP(x) x = YYCURSOR - 1 - const unsigned char *yyt1; + loop: -#line 73 "../../lnav2/src/log_level_re.cc" +#line 71 "../../lnav2/src/log_level_re.cc" { YYCTYPE yych; unsigned int yyaccept = 0; - yych = YYPEEK (); + yych = YYPEEK(); switch (yych) { case 0x00: goto yy2; case 'C': @@ -97,21 +95,21 @@ log_level_t string2level(const char *levelstr, ssize_t len, bool exact) default: goto yy4; } yy2: - YYSKIP (); -#line 75 "../../lnav2/src/log_level_re.re" + YYSKIP(); +#line 73 "../../lnav2/src/log_level_re.re" { RET(LEVEL_UNKNOWN); } -#line 104 "../../lnav2/src/log_level_re.cc" +#line 102 "../../lnav2/src/log_level_re.cc" yy4: - YYSKIP (); + YYSKIP(); yy5: -#line 102 "../../lnav2/src/log_level_re.re" +#line 100 "../../lnav2/src/log_level_re.re" { goto loop; } -#line 110 "../../lnav2/src/log_level_re.cc" +#line 108 "../../lnav2/src/log_level_re.cc" yy6: yyaccept = 0; - YYSKIP (); - YYBACKUP (); - yych = YYPEEK (); + YYSKIP(); + YYBACKUP(); + yych = YYPEEK(); switch (yych) { case 'R': case 'r': goto yy15; @@ -119,9 +117,9 @@ yy6: } yy7: yyaccept = 0; - YYSKIP (); - YYBACKUP (); - yych = YYPEEK (); + YYSKIP(); + YYBACKUP(); + yych = YYPEEK(); switch (yych) { case 'E': case 'e': goto yy17; @@ -129,9 +127,9 @@ yy7: } yy8: yyaccept = 0; - YYSKIP (); - YYBACKUP (); - yych = YYPEEK (); + YYSKIP(); + YYBACKUP(); + yych = YYPEEK(); switch (yych) { case 'R': case 'r': goto yy18; @@ -139,9 +137,9 @@ yy8: } yy9: yyaccept = 0; - YYSKIP (); - YYBACKUP (); - yych = YYPEEK (); + YYSKIP(); + YYBACKUP(); + yych = YYPEEK(); switch (yych) { case 'A': case 'a': goto yy19; @@ -149,9 +147,9 @@ yy9: } yy10: yyaccept = 0; - YYSKIP (); - YYBACKUP (); - yych = YYPEEK (); + YYSKIP(); + YYBACKUP(); + yych = YYPEEK(); switch (yych) { case 'N': case 'n': goto yy20; @@ -159,9 +157,9 @@ yy10: } yy11: yyaccept = 0; - YYSKIP (); - YYBACKUP (); - yych = YYPEEK (); + YYSKIP(); + YYBACKUP(); + yych = YYPEEK(); switch (yych) { case 'O': case 'o': goto yy21; @@ -169,9 +167,9 @@ yy11: } yy12: yyaccept = 0; - YYSKIP (); - YYBACKUP (); - yych = YYPEEK (); + YYSKIP(); + YYBACKUP(); + yych = YYPEEK(); switch (yych) { case 'E': case 'e': goto yy22; @@ -181,9 +179,9 @@ yy12: } yy13: yyaccept = 0; - YYSKIP (); - YYBACKUP (); - yych = YYPEEK (); + YYSKIP(); + YYBACKUP(); + yych = YYPEEK(); switch (yych) { case 'R': case 'r': goto yy24; @@ -191,112 +189,115 @@ yy13: } yy14: yyaccept = 0; - YYSKIP (); - YYBACKUP (); - yych = YYPEEK (); + YYSKIP(); + YYBACKUP(); + yych = YYPEEK(); switch (yych) { case 'A': case 'a': goto yy25; default: goto yy5; } yy15: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'I': case 'i': goto yy26; default: goto yy16; } yy16: - YYRESTORE (); + YYRESTORE(); switch (yyaccept) { - case 0: goto yy5; - case 1: goto yy29; - default: goto yy48; + case 0: + goto yy5; + case 1: + goto yy29; + default: + goto yy48; } yy17: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'B': case 'b': goto yy27; default: goto yy16; } yy18: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'R': case 'r': goto yy28; default: goto yy16; } yy19: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'T': case 't': goto yy30; default: goto yy16; } yy20: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'F': case 'f': goto yy31; default: goto yy16; } yy21: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'T': case 't': goto yy32; default: goto yy16; } yy22: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'V': case 'v': goto yy33; default: goto yy16; } yy23: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'A': case 'a': goto yy34; default: goto yy16; } yy24: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'A': case 'a': goto yy35; default: goto yy16; } yy25: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'R': case 'r': goto yy36; default: goto yy16; } yy26: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'T': case 't': goto yy37; default: goto yy16; } yy27: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'U': case 'u': goto yy38; @@ -304,138 +305,138 @@ yy27: } yy28: yyaccept = 1; - YYSKIP (); - YYBACKUP (); - yych = YYPEEK (); + YYSKIP(); + YYBACKUP(); + yych = YYPEEK(); switch (yych) { case 'O': case 'o': goto yy39; default: goto yy29; } yy29: -#line 98 "../../lnav2/src/log_level_re.re" +#line 96 "../../lnav2/src/log_level_re.re" { RET(LEVEL_ERROR); } -#line 319 "../../lnav2/src/log_level_re.cc" +#line 320 "../../lnav2/src/log_level_re.cc" yy30: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'A': case 'a': goto yy40; default: goto yy16; } yy31: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'O': case 'o': goto yy41; default: goto yy16; } yy32: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'I': case 'i': goto yy43; default: goto yy16; } yy33: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'E': case 'e': goto yy44; default: goto yy16; } yy34: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'T': case 't': goto yy45; default: goto yy16; } yy35: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'C': case 'c': goto yy46; default: goto yy16; } yy36: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'N': case 'n': goto yy47; default: goto yy16; } yy37: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'I': case 'i': goto yy49; default: goto yy16; } yy38: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'G': case 'g': goto yy50; default: goto yy16; } yy39: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'R': case 'r': goto yy52; default: goto yy16; } yy40: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'L': case 'l': goto yy53; default: goto yy16; } yy41: - YYSKIP (); -#line 94 "../../lnav2/src/log_level_re.re" + YYSKIP(); +#line 92 "../../lnav2/src/log_level_re.re" { RET(LEVEL_INFO); } -#line 412 "../../lnav2/src/log_level_re.cc" +#line 413 "../../lnav2/src/log_level_re.cc" yy43: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'C': case 'c': goto yy55; default: goto yy16; } yy44: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'R': case 'r': goto yy56; default: goto yy16; } yy45: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'S': case 's': goto yy57; default: goto yy16; } yy46: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'E': case 'e': goto yy59; @@ -443,41 +444,39 @@ yy46: } yy47: yyaccept = 2; - YYSKIP (); - YYBACKUP (); - yych = YYPEEK (); + YYSKIP(); + YYBACKUP(); + yych = YYPEEK(); switch (yych) { case 'I': case 'i': goto yy61; default: goto yy48; } yy48: -#line 97 "../../lnav2/src/log_level_re.re" +#line 95 "../../lnav2/src/log_level_re.re" { RET(LEVEL_WARNING); } -#line 458 "../../lnav2/src/log_level_re.cc" +#line 459 "../../lnav2/src/log_level_re.cc" yy49: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'C': case 'c': goto yy62; default: goto yy16; } yy50: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case '2': case '3': case '4': case '5': goto yy63; - default: - YYSTAGP (yyt1); - goto yy51; + default: goto yy51; } yy51: - debug_level = yyt1; -#line 77 "../../lnav2/src/log_level_re.re" + YYSTAGP(debug_level); +#line 75 "../../lnav2/src/log_level_re.re" { if (debug_level == nullptr) { RET(LEVEL_DEBUG); @@ -495,96 +494,95 @@ yy51: RET(LEVEL_DEBUG); } } -#line 499 "../../lnav2/src/log_level_re.cc" +#line 498 "../../lnav2/src/log_level_re.cc" yy52: - YYSKIP (); + YYSKIP(); goto yy29; yy53: - YYSKIP (); -#line 101 "../../lnav2/src/log_level_re.re" + YYSKIP(); +#line 99 "../../lnav2/src/log_level_re.re" { RET(LEVEL_FATAL); } -#line 507 "../../lnav2/src/log_level_re.cc" +#line 506 "../../lnav2/src/log_level_re.cc" yy55: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'E': case 'e': goto yy64; default: goto yy16; } yy56: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'E': case 'e': goto yy66; default: goto yy16; } yy57: - YYSKIP (); -#line 96 "../../lnav2/src/log_level_re.re" + YYSKIP(); +#line 94 "../../lnav2/src/log_level_re.re" { RET(LEVEL_STATS); } -#line 528 "../../lnav2/src/log_level_re.cc" +#line 527 "../../lnav2/src/log_level_re.cc" yy59: - YYSKIP (); -#line 76 "../../lnav2/src/log_level_re.re" + YYSKIP(); +#line 74 "../../lnav2/src/log_level_re.re" { RET(LEVEL_TRACE); } -#line 533 "../../lnav2/src/log_level_re.cc" +#line 532 "../../lnav2/src/log_level_re.cc" yy61: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'N': case 'n': goto yy68; default: goto yy16; } yy62: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'A': case 'a': goto yy69; default: goto yy16; } yy63: - YYSKIP (); - YYSTAGP (yyt1); + YYSKIP(); goto yy51; yy64: - YYSKIP (); -#line 95 "../../lnav2/src/log_level_re.re" + YYSKIP(); +#line 93 "../../lnav2/src/log_level_re.re" { RET(LEVEL_NOTICE); } -#line 558 "../../lnav2/src/log_level_re.cc" +#line 556 "../../lnav2/src/log_level_re.cc" yy66: - YYSKIP (); -#line 100 "../../lnav2/src/log_level_re.re" + YYSKIP(); +#line 98 "../../lnav2/src/log_level_re.re" { RET(LEVEL_CRITICAL); } -#line 563 "../../lnav2/src/log_level_re.cc" +#line 561 "../../lnav2/src/log_level_re.cc" yy68: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'G': case 'g': goto yy70; default: goto yy16; } yy69: - YYSKIP (); - yych = YYPEEK (); + YYSKIP(); + yych = YYPEEK(); switch (yych) { case 'L': case 'l': goto yy71; default: goto yy16; } yy70: - YYSKIP (); + YYSKIP(); goto yy48; yy71: - YYSKIP (); -#line 99 "../../lnav2/src/log_level_re.re" + YYSKIP(); +#line 97 "../../lnav2/src/log_level_re.re" { RET(LEVEL_CRITICAL); } -#line 587 "../../lnav2/src/log_level_re.cc" +#line 585 "../../lnav2/src/log_level_re.cc" } -#line 104 "../../lnav2/src/log_level_re.re" +#line 102 "../../lnav2/src/log_level_re.re" } diff --git a/src/log_level_re.re b/src/log_level_re.re index 313ae52e..87c9e181 100644 --- a/src/log_level_re.re +++ b/src/log_level_re.re @@ -29,9 +29,7 @@ #include "config.h" -#include #include -#include #include "log_level.hh" diff --git a/src/log_search_table.hh b/src/log_search_table.hh index 51f3abd1..2052a488 100644 --- a/src/log_search_table.hh +++ b/src/log_search_table.hh @@ -117,7 +117,7 @@ public: content_line_t cl; cl = lss.at(lc.lc_curr_line); - std::shared_ptr lf = lss.find(cl); + auto lf = lss.find(cl); auto lf_iter = lf->begin() + cl; if (lf_iter->is_continued()) { diff --git a/src/log_vtab_impl.cc b/src/log_vtab_impl.cc index 8b1c08c3..0a43f156 100644 --- a/src/log_vtab_impl.cc +++ b/src/log_vtab_impl.cc @@ -856,8 +856,8 @@ static int vt_update(sqlite3_vtab *tab, ypc.parse(log_tags, strlen((const char *) log_tags)); ypc.complete_parse(); if (!errors.empty()) { - tab->zErrMsg = sqlite3_mprintf("%s", - join(errors.begin(), errors.end(), "\n").c_str()); + auto all_errors = fmt::format("{}", fmt::join(errors, "\n")); + tab->zErrMsg = sqlite3_mprintf("%s", all_errors.c_str()); return SQLITE_ERROR; } } diff --git a/src/log_vtab_impl.hh b/src/log_vtab_impl.hh index 500d3ae4..37db811a 100644 --- a/src/log_vtab_impl.hh +++ b/src/log_vtab_impl.hh @@ -103,12 +103,12 @@ public: }; virtual ~log_vtab_impl() { }; - const intern_string_t get_name(void) const + const intern_string_t get_name() const { return this->vi_name; }; - std::string get_table_statement(void); + std::string get_table_statement(); virtual bool is_valid(log_cursor &lc, logfile_sub_source &lss) { content_line_t cl(lss.at(lc.lc_curr_line)); diff --git a/src/logfile.cc b/src/logfile.cc index e04b066b..b5b13adc 100644 --- a/src/logfile.cc +++ b/src/logfile.cc @@ -33,12 +33,9 @@ #include #include -#include #include #include -#include #include -#include #include #include #include @@ -59,7 +56,7 @@ logfile::logfile(const string &filename, logfile_open_options &loo) { require(!filename.empty()); - this->lf_options = loo; + this->lf_options = std::move(loo); memset(&this->lf_stat, 0, sizeof(this->lf_stat)); if (this->lf_options.loo_fd == -1) { char resolved_path[PATH_MAX]; diff --git a/src/logfile.hh b/src/logfile.hh index 66e6610e..a793f7aa 100644 --- a/src/logfile.hh +++ b/src/logfile.hh @@ -84,7 +84,7 @@ struct logfile_open_options { }; logfile_open_options &with_fd(auto_fd fd) { - this->loo_fd = fd; + this->loo_fd = std::move(fd); return *this; }; diff --git a/src/pcrepp/pcrepp.cc b/src/pcrepp/pcrepp.cc index 78650820..ac7137ca 100644 --- a/src/pcrepp/pcrepp.cc +++ b/src/pcrepp/pcrepp.cc @@ -193,7 +193,7 @@ bool pcrepp::match(pcre_context &pc, pcre_input &pi, int options) const return rc > 0; } -void pcrepp::study(void) +void pcrepp::study() { const char *errptr; @@ -237,7 +237,7 @@ void pcrepp::study(void) } #ifdef PCRE_STUDY_JIT_COMPILE -pcre_jit_stack *pcrepp::jit_stack(void) +pcre_jit_stack *pcrepp::jit_stack() { static pcre_jit_stack *retval = NULL; diff --git a/src/piper_proc.cc b/src/piper_proc.cc index da68083a..ef9eec29 100644 --- a/src/piper_proc.cc +++ b/src/piper_proc.cc @@ -34,15 +34,11 @@ #include #include #include -#include #include -#include #include -#include #include #include #include -#include #include #include "base/lnav_log.hh" diff --git a/src/piper_proc.hh b/src/piper_proc.hh index a67adb8b..61e7c649 100644 --- a/src/piper_proc.hh +++ b/src/piper_proc.hh @@ -72,7 +72,7 @@ public: virtual ~piper_proc(); /** @return The file descriptor for the temporary file. */ - int get_fd() { return this->pp_fd.release(); }; + auto_fd get_fd() { return std::move(this->pp_fd); }; pid_t get_child_pid() const { return this->pp_child; }; diff --git a/src/plain_text_source.hh b/src/plain_text_source.hh index 0f53eeeb..a669c1fb 100644 --- a/src/plain_text_source.hh +++ b/src/plain_text_source.hh @@ -39,11 +39,9 @@ class plain_text_source : public text_sub_source, public vis_location_history { public: - plain_text_source() - : tds_text_format(text_format_t::TF_UNKNOWN), tds_longest_line(0) { - }; + plain_text_source() = default; - plain_text_source(const std::string &text) : tds_text_format(text_format_t::TF_UNKNOWN) { + plain_text_source(const std::string &text) { size_t start = 0, end; while ((end = text.find('\n', start)) != std::string::npos) { @@ -57,13 +55,11 @@ public: this->tds_longest_line = this->compute_longest_line(); }; - plain_text_source(const std::vector &text_lines) - : tds_text_format(text_format_t::TF_UNKNOWN) { + plain_text_source(const std::vector &text_lines) { this->replace_with(text_lines); }; - plain_text_source(const std::vector &text_lines) - : tds_text_format(text_format_t::TF_UNKNOWN) { + plain_text_source(const std::vector &text_lines) { this->tds_lines = text_lines; this->tds_longest_line = this->compute_longest_line(); }; @@ -148,8 +144,8 @@ private: }; std::vector tds_lines; - text_format_t tds_text_format; - size_t tds_longest_line; + text_format_t tds_text_format{text_format_t::TF_UNKNOWN}; + size_t tds_longest_line{0}; }; #endif //LNAV_PLAIN_TEXT_SOURCE_HH diff --git a/src/preview_status_source.hh b/src/preview_status_source.hh index b2c17d83..c5408b8e 100644 --- a/src/preview_status_source.hh +++ b/src/preview_status_source.hh @@ -62,9 +62,9 @@ public: this->tss_fields[TSF_TOGGLE].right_justify(true); }; - size_t statusview_fields(void) { return TSF__MAX; }; + size_t statusview_fields() override { return TSF__MAX; }; - status_field &statusview_value_for_field(int field) { + status_field &statusview_value_for_field(int field) override { return this->tss_fields[field]; }; diff --git a/src/readline_curses.cc b/src/readline_curses.cc index 999698aa..271e0e3c 100644 --- a/src/readline_curses.cc +++ b/src/readline_curses.cc @@ -32,12 +32,10 @@ #include "config.h" #include -#include #include #include #include #include -#include #include #include @@ -56,8 +54,8 @@ #include #include "base/string_util.hh" +#include "fmt/format.h" #include "lnav_config.hh" -#include "pcrepp/pcrepp.hh" #include "shlex.hh" #include "auto_mem.hh" #include "base/lnav_log.hh" @@ -354,7 +352,7 @@ char **readline_context::attempted_completion(const char *text, arg_possibilities = nullptr; rl_completion_append_character = 0; if (lexer.split(prefix, scope)) { - string prefix2 = join(prefix.begin(), prefix.end(), "\x1f"); + auto prefix2 = fmt::format("{}", fmt::join(prefix, "\x1f")); auto prefix_iter = loaded_context->rc_prefixes.find(prefix2); if (prefix_iter != loaded_context->rc_prefixes.end()) { @@ -1058,7 +1056,7 @@ void readline_curses::add_prefix(int context, const string &value) { char buffer[1024]; - string prefix_wire = join(prefix.begin(), prefix.end(), "\x1f"); + auto prefix_wire = fmt::format("{}", fmt::join(prefix, "\x1f")); snprintf(buffer, sizeof(buffer), "apre:%d:%s\x1d%s", diff --git a/src/readline_highlighters.cc b/src/readline_highlighters.cc index 6e84dce2..51267f62 100644 --- a/src/readline_highlighters.cc +++ b/src/readline_highlighters.cc @@ -35,7 +35,6 @@ #include "pcrepp/pcrepp.hh" #include "sql_util.hh" #include "shlex.hh" -#include "lnav_util.hh" #include "readline_highlighters.hh" @@ -183,7 +182,7 @@ static void readline_regex_highlighter_int(attr_line_t &al, int x, int skip) "()", "QE", - NULL + nullptr }; string &line = al.get_string(); @@ -449,7 +448,7 @@ void readline_sqlite_highlighter(attr_line_t &al, int x) "[]", "()", - NULL + nullptr }; view_colors &vc = view_colors::singleton(); diff --git a/src/regexp_vtab.cc b/src/regexp_vtab.cc index c3d1cfaf..366541e6 100644 --- a/src/regexp_vtab.cc +++ b/src/regexp_vtab.cc @@ -65,14 +65,6 @@ CREATE TABLE regexp_capture ( ); )"; - struct vtab { - sqlite3_vtab base; - - operator sqlite3_vtab *() { - return &this->base; - }; - }; - struct cursor { sqlite3_vtab_cursor base; unique_ptr c_pattern; diff --git a/src/relative_time.cc b/src/relative_time.cc index abf8c3ca..b19b934d 100644 --- a/src/relative_time.cc +++ b/src/relative_time.cc @@ -31,10 +31,7 @@ #include -#include - #include "pcrepp/pcrepp.hh" -#include "lnav_util.hh" #include "relative_time.hh" using namespace std; @@ -151,7 +148,7 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou struct timeval tv; struct exttm tm; - gettimeofday(&tv, NULL); + gettimeofday(&tv, nullptr); localtime_r(&tv.tv_sec, &tm.et_tm); tm.et_nsec = tv.tv_usec * 1000; this->add(tm); @@ -519,7 +516,7 @@ size_t duration2str(int64_t millis, std::string &value_out) { 60, "%qd%s", "m" }, { 24, "%qd%s", "h" }, { 0, "%qd%s", "d" }, - { 0, NULL, NULL } + { 0, nullptr, nullptr } }; struct rel_interval *curr_interval = intervals; @@ -538,7 +535,7 @@ size_t duration2str(int64_t millis, std::string &value_out) curr_interval += 1; } - for (; curr_interval->symbol != NULL; curr_interval++) { + for (; curr_interval->symbol != nullptr; curr_interval++) { long long amount; char segment[32]; diff --git a/src/session_data.cc b/src/session_data.cc index 5e9d2363..ee235061 100644 --- a/src/session_data.cc +++ b/src/session_data.cc @@ -867,22 +867,22 @@ void load_session() lnav_data.ld_session_load_time = pair.first.second; session_data.sd_save_time = pair.first.second; - const string &view_info_name = pair.second; + const auto& view_info_path = pair.second; - yajlpp_parse_context ypc(view_info_name, &view_info_handlers); + yajlpp_parse_context ypc(view_info_path, &view_info_handlers); ypc.with_obj(session_data); handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc); load_time_bookmarks(); - if ((fd = open(view_info_name.c_str(), O_RDONLY)) < 0) { + if ((fd = openp(view_info_path, O_RDONLY)) < 0) { perror("cannot open session file"); } else { unsigned char buffer[1024]; ssize_t rc; - log_info("loading session file: %s", view_info_name.c_str()); + log_info("loading session file: %s", view_info_path.c_str()); while ((rc = read(fd, buffer, sizeof(buffer))) > 0) { yajl_parse(handle, buffer, rc); } diff --git a/src/session_data.hh b/src/session_data.hh index 85551adf..e7758432 100644 --- a/src/session_data.hh +++ b/src/session_data.hh @@ -35,7 +35,7 @@ #include #include -#include "lnav.hh" +#include "view_helpers.hh" struct file_state { bool fs_is_visible{true}; diff --git a/src/shlex.hh b/src/shlex.hh index e1f47f14..2b6e64d0 100644 --- a/src/shlex.hh +++ b/src/shlex.hh @@ -217,13 +217,7 @@ public: void resolve_home_dir(std::string& result, const pcre_context::capture_t cap) const { if (cap.length() == 1) { - const char *home_dir = getenv("HOME"); - - if (home_dir != nullptr) { - result.append(home_dir); - } else { - result.append("~"); - } + result.append(getenv_opt("HOME").value_or("~")); } else { auto username = (char *) alloca(cap.length()); diff --git a/src/sql_util.cc b/src/sql_util.cc index db2abdda..74f1f9da 100644 --- a/src/sql_util.cc +++ b/src/sql_util.cc @@ -40,9 +40,10 @@ #include "auto_mem.hh" #include "sql_util.hh" +#include "base/string_util.hh" #include "base/lnav_log.hh" -#include "lnav_util.hh" #include "pcrepp/pcrepp.hh" +#include "lnav_util.hh" #include "sqlite-extension-func.hh" using namespace std; @@ -727,7 +728,7 @@ void sql_execute_script(sqlite3 *db, sqlite3_bind_text(stmt, lpc + 1, iter->second.c_str(), -1, SQLITE_TRANSIENT); - } else if ((env_value = getenv(&name[1])) != NULL) { + } else if ((env_value = getenv(&name[1])) != nullptr) { sqlite3_bind_text(stmt, lpc + 1, env_value, -1, SQLITE_TRANSIENT); diff --git a/src/sql_util.hh b/src/sql_util.hh index 7cd7aba8..af353061 100644 --- a/src/sql_util.hh +++ b/src/sql_util.hh @@ -76,7 +76,7 @@ inline ssize_t sql_strftime(char *buffer, size_t buffer_size, return sql_strftime(buffer, buffer_size, tv.tv_sec, tv.tv_usec / 1000, sep); } -void sql_install_logger(void); +void sql_install_logger(); bool sql_ident_needs_quote(const char *ident); @@ -120,7 +120,7 @@ void annotate_sql_statement(attr_line_t &al_inout); extern std::multimap sqlite_function_help; -std::string sql_keyword_re(void); +std::string sql_keyword_re(); std::vector find_sql_help_for_line(const attr_line_t &al, size_t x); #endif diff --git a/src/sqlite-extension-func.cc b/src/sqlite-extension-func.cc index e86e278a..0e71445c 100644 --- a/src/sqlite-extension-func.cc +++ b/src/sqlite-extension-func.cc @@ -31,10 +31,9 @@ #include "config.h" -#include #include -#include "lnav_util.hh" +#include "base/string_util.hh" #include "base/lnav_log.hh" #include "sql_util.hh" diff --git a/src/state-extension-functions.cc b/src/state-extension-functions.cc index dd43ec1a..f3250bc3 100644 --- a/src/state-extension-functions.cc +++ b/src/state-extension-functions.cc @@ -31,8 +31,6 @@ #include "config.h" -#include -#include #include #include diff --git a/src/string-extension-functions.cc b/src/string-extension-functions.cc index 858ba8ed..1c26d133 100644 --- a/src/string-extension-functions.cc +++ b/src/string-extension-functions.cc @@ -9,7 +9,6 @@ #include "config.h" -#include #include #include #include diff --git a/src/textview_curses.hh b/src/textview_curses.hh index 8cfa891a..19a6a657 100644 --- a/src/textview_curses.hh +++ b/src/textview_curses.hh @@ -616,6 +616,8 @@ public: vis_bookmarks &get_bookmarks() { return this->tc_bookmarks; }; + const vis_bookmarks &get_bookmarks() const { return this->tc_bookmarks; }; + void toggle_user_mark(bookmark_type_t *bm, vis_line_t start_line, vis_line_t end_line = vis_line_t(-1)) diff --git a/src/top_status_source.hh b/src/top_status_source.hh index 826a32b6..1919f539 100644 --- a/src/top_status_source.hh +++ b/src/top_status_source.hh @@ -86,7 +86,7 @@ public: lv_functor_t filename_wire; lv_functor_t view_name_wire; - size_t statusview_fields(void) { return TSF__MAX; }; + size_t statusview_fields() { return TSF__MAX; }; status_field &statusview_value_for_field(int field) { diff --git a/src/top_sys_status_source.hh b/src/top_sys_status_source.hh index f9616d4e..1952497b 100644 --- a/src/top_sys_status_source.hh +++ b/src/top_sys_status_source.hh @@ -66,7 +66,7 @@ public: this->tss_fields[TSF_TRAF].set_role(view_colors::VCR_ACTIVE_STATUS); }; - size_t statusview_fields(void) { return TSF__MAX; }; + size_t statusview_fields() { return TSF__MAX; }; status_field &statusview_value_for_field(int field) { diff --git a/src/url_loader.hh b/src/url_loader.hh index 45f5975c..9c4942f2 100644 --- a/src/url_loader.hh +++ b/src/url_loader.hh @@ -37,20 +37,15 @@ class url_loader : public curl_request { public: url_loader(const std::string &url) : curl_request(url), ul_resume_offset(0) { - char piper_tmpname[PATH_MAX]; - const char *tmpdir; - - if ((tmpdir = getenv("TMPDIR")) == NULL) { - tmpdir = _PATH_VARTMP; - } - snprintf(piper_tmpname, sizeof(piper_tmpname), - "%s/lnav.url.XXXXXX", - tmpdir); - if ((this->ul_fd = mkstemp(piper_tmpname)) == -1) { + auto tmp_res = open_temp_file(ghc::filesystem::temp_directory_path() / + "lnav.url.XXXXXX"); + if (tmp_res.isErr()) { return; } - unlink(piper_tmpname); + auto tmp_pair = tmp_res.unwrap(); + ghc::filesystem::remove(tmp_pair.first); + this->ul_fd = tmp_pair.second; curl_easy_setopt(this->cr_handle, CURLOPT_URL, this->cr_name.c_str()); curl_easy_setopt(this->cr_handle, CURLOPT_WRITEFUNCTION, write_cb); diff --git a/src/view_helpers.cc b/src/view_helpers.cc index 75c7dad5..a4a6dcec 100644 --- a/src/view_helpers.cc +++ b/src/view_helpers.cc @@ -36,6 +36,7 @@ #include "vtab_module.hh" #include "shlex.hh" #include "help-txt.h" +#include "view_helpers.hh" using namespace std; @@ -61,7 +62,7 @@ static void open_schema_view() schema_tc->set_sub_source(pts); } -static void open_pretty_view(void) +static void open_pretty_view() { static const char *NOTHING_MSG = "Nothing to pretty-print"; diff --git a/src/view_helpers.hh b/src/view_helpers.hh new file mode 100644 index 00000000..ea5552d9 --- /dev/null +++ b/src/view_helpers.hh @@ -0,0 +1,61 @@ +/** + * 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 view_helpers.hh + */ + +#ifndef lnav_view_helpers_hh +#define lnav_view_helpers_hh + +#include "help_text.hh" +#include "textview_curses.hh" + +/** The different views available. */ +typedef enum { + LNV_LOG, + LNV_TEXT, + LNV_HELP, + LNV_HISTOGRAM, + LNV_DB, + LNV_SCHEMA, + LNV_PRETTY, + LNV_SPECTRO, + + LNV__MAX +} lnav_view_t; + +extern const char *lnav_view_strings[LNV__MAX + 1]; + +bool ensure_view(textview_curses *expected_tc); +bool toggle_view(textview_curses *toggle_tc); +void layout_views(); + +void execute_examples(); +attr_line_t eval_example(const help_text &ht, const help_example &ex); + +#endif diff --git a/src/views_vtab.cc b/src/views_vtab.cc index 89f2be04..d6726aa5 100644 --- a/src/views_vtab.cc +++ b/src/views_vtab.cc @@ -106,14 +106,6 @@ struct from_sqlite> { }; struct lnav_views : public tvt_iterator_cursor { - struct vtab { - sqlite3_vtab base; - - operator sqlite3_vtab *() { - return &this->base; - }; - }; - static constexpr const char *CREATE_STMT = R"( -- Access lnav's views through this table. CREATE TABLE lnav_views ( @@ -272,14 +264,6 @@ CREATE TABLE lnav_view_stack ( ); )"; - struct vtab { - sqlite3_vtab base; - - operator sqlite3_vtab *() { - return &this->base; - }; - }; - iterator begin() { return lnav_data.ld_view_stack.vs_views.begin(); } @@ -290,7 +274,7 @@ CREATE TABLE lnav_view_stack ( int get_column(cursor &vc, sqlite3_context *ctx, int col) { textview_curses *tc = *vc.iter; - lnav_view_t view = lnav_view_t(tc - lnav_data.ld_views); + auto view = lnav_view_t(tc - lnav_data.ld_views); switch (col) { case 0: @@ -341,15 +325,6 @@ CREATE TABLE lnav_view_stack ( }; struct lnav_view_filter_base { - - struct vtab { - sqlite3_vtab base; - - operator sqlite3_vtab *() { - return &this->base; - }; - }; - struct iterator { using difference_type = int; using value_type = text_filter; @@ -528,7 +503,7 @@ CREATE TABLE lnav_view_filters ( bool enabled, text_filter::type_t type, pair pattern) { - lnav_view_t view_index = lnav_view_t(rowid >> 32); + auto view_index = lnav_view_t(rowid >> 32); int filter_index = rowid & 0xffffffffLL; textview_curses &tc = lnav_data.ld_views[view_index]; text_sub_source *tss = tc.get_sub_source(); diff --git a/src/vt52_curses.cc b/src/vt52_curses.cc index f1e7d5c0..2a73bef2 100644 --- a/src/vt52_curses.cc +++ b/src/vt52_curses.cc @@ -162,9 +162,6 @@ vt52_curses::vt52_curses() vc_map_buffer(0) { } -vt52_curses::~vt52_curses() -{ } - const char *vt52_curses::map_input(int ch, int &len_out) { const char *esc, *retval; diff --git a/src/vt52_curses.hh b/src/vt52_curses.hh index 8a5e60eb..cab10fdd 100644 --- a/src/vt52_curses.hh +++ b/src/vt52_curses.hh @@ -55,7 +55,6 @@ class vt52_curses : public view_curses { public: vt52_curses(); - virtual ~vt52_curses(); /** @param win The curses window this view is attached to. */ void set_window(WINDOW *win) { this->vc_window = win; }; diff --git a/src/vtab_module.hh b/src/vtab_module.hh index 44958a6f..dbd3b521 100644 --- a/src/vtab_module.hh +++ b/src/vtab_module.hh @@ -38,6 +38,7 @@ #include "optional.hpp" #include "base/lnav_log.hh" +#include "base/string_util.hh" #include "lnav_util.hh" #include "auto_mem.hh" #include "yajl/api/yajl_gen.h" @@ -58,12 +59,12 @@ struct from_sqlite_conversion_error : std::exception { struct sqlite_func_error : std::exception { template - sqlite_func_error( + explicit sqlite_func_error( fmt::string_view format_str, const Args& ...args) : e_what(fmt::vformat(format_str, fmt::make_format_args(args...))) { } - const char *what() const noexcept { + const char *what() const noexcept override { return this->e_what.c_str(); } @@ -436,11 +437,11 @@ public: }; const_iterator begin() { - return const_iterator(this); + return {this}; }; const_iterator end() { - return const_iterator(this, this->vic_index_info.nConstraint); + return {this, this->vic_index_info.nConstraint}; }; private: @@ -497,14 +498,26 @@ private: template struct vtab_module { + struct vtab { + explicit vtab(T& impl) : v_impl(impl) {}; + + explicit operator sqlite3_vtab *() { + return &this->base; + }; + + sqlite3_vtab v_base{}; + T &v_impl; + }; + static int tvt_create(sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlite3_vtab **pp_vt, char **pzErr) { - static typename T::vtab vt; + auto* mod = static_cast *>(pAux); + auto vt = new vtab(mod->vm_impl); - *pp_vt = (sqlite3_vtab *) vt; + *pp_vt = (sqlite3_vtab *) &vt->v_base; return sqlite3_declare_vtab(db, T::CREATE_STMT); }; @@ -558,7 +571,7 @@ struct vtab_module { auto *p_cur = new (typename T::cursor)(p_svt); - if (p_cur == NULL) { + if (p_cur == nullptr) { return SQLITE_NOMEM; } else { *pp_cursor = (sqlite3_vtab_cursor *) p_cur; @@ -598,10 +611,10 @@ struct vtab_module { }; static int tvt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) { + auto *mod_vt = (typename vtab_module::vtab *) cur->pVtab; auto *p_cur = (typename T::cursor *) cur; - T handler; - return handler.get_column(*p_cur, ctx, col); + return mod_vt->v_impl.get_column(*p_cur, ctx, col); }; static int vt_best_index(sqlite3_vtab *tab, sqlite3_index_info *p_info) { @@ -620,17 +633,17 @@ struct vtab_module { int argc, sqlite3_value **argv, sqlite_int64 *rowid) { - T handler; + auto *mod_vt = (typename vtab_module::vtab *) tab; if (argc <= 1) { sqlite3_int64 rowid = sqlite3_value_int64(argv[0]); - return handler.delete_row(tab, rowid); + return mod_vt->v_impl.delete_row(tab, rowid); } if (sqlite3_value_type(argv[0]) == SQLITE_NULL) { sqlite3_int64 *rowid2 = rowid; - return vtab_module::apply(handler, &T::insert_row, tab, *rowid2, argc - 2, argv + 2); + return vtab_module::apply(mod_vt->v_impl, &T::insert_row, tab, *rowid2, argc - 2, argv + 2); } sqlite3_int64 index = sqlite3_value_int64(argv[0]); @@ -641,7 +654,7 @@ struct vtab_module { return SQLITE_ERROR; } - return vtab_module::apply(handler, &T::update_row, tab, index, argc - 2, argv + 2); + return vtab_module::apply(mod_vt->v_impl, &T::update_row, tab, index, argc - 2, argv + 2); }; template @@ -653,7 +666,8 @@ struct vtab_module { void addUpdate(...) { }; - vtab_module() noexcept { + template + vtab_module(Args& ...args) noexcept : vm_impl(args...) { memset(&this->vm_module, 0, sizeof(this->vm_module)); this->vm_module.iVersion = 0; this->vm_module.xCreate = tvt_create; @@ -668,52 +682,53 @@ struct vtab_module { this->vm_module.xBestIndex = vt_best_index; this->vm_module.xFilter = vt_filter; this->vm_module.xColumn = tvt_column; - this->addUpdate(T()); + this->addUpdate(this->vm_impl); }; - int create(sqlite3 *db, const char *name) { - std::string impl_name = name; + int create(sqlite3 *db, const char *name) + { + auto impl_name = std::string(name); vtab_module_schemas += T::CREATE_STMT; vtab_module_ddls[intern_string::lookup(name)] = trim(T::CREATE_STMT); // XXX Eponymous tables don't seem to work in older sqlite versions impl_name += "_impl"; - int rc = sqlite3_create_module(db, impl_name.c_str(), &this->vm_module, NULL); + int rc = sqlite3_create_module( + db, impl_name.c_str(), &this->vm_module, this); ensure(rc == SQLITE_OK); - std::string create_stmt = std::string("CREATE VIRTUAL TABLE ") + name + " USING " + impl_name + "()"; - return sqlite3_exec(db, create_stmt.c_str(), NULL, NULL, NULL); + auto create_stmt = fmt::format("CREATE VIRTUAL TABLE {} USING {}()", + name, impl_name); + return sqlite3_exec(db, create_stmt.c_str(), nullptr, nullptr, nullptr); }; sqlite3_module vm_module; + T vm_impl; }; template struct tvt_iterator_cursor { struct cursor { - sqlite3_vtab_cursor base; + sqlite3_vtab_cursor base{}; + typename T::iterator iter; - cursor(sqlite3_vtab *vt) + explicit cursor(sqlite3_vtab *vt) { - T handler; + auto* mod_vt = (typename vtab_module::vtab *) vt; this->base.pVtab = vt; - this->iter = handler.begin(); + this->iter = mod_vt->v_impl.begin(); }; int reset() { - T handler; - - this->iter = handler.begin(); + this->iter = get_handler().begin(); return SQLITE_OK; }; int next() { - T handler; - - if (this->iter != handler.end()) { + if (this->iter != get_handler().end()) { ++this->iter; } @@ -722,9 +737,7 @@ struct tvt_iterator_cursor { int eof() { - T handler; - - return this->iter == handler.end(); + return this->iter == get_handler().end(); }; template< bool cond, typename U > @@ -734,9 +747,7 @@ struct tvt_iterator_cursor { resolvedType< std::is_same::iterator_category>::value, U > get_rowid(sqlite_int64 &rowid_out) { - T handler; - - rowid_out = std::distance(handler.begin(), this->iter); + rowid_out = std::distance(get_handler().begin(), this->iter); return SQLITE_OK; } @@ -745,12 +756,17 @@ struct tvt_iterator_cursor { resolvedType< !std::is_same::iterator_category>::value, U > get_rowid(sqlite_int64 &rowid_out) { - T handler; - - rowid_out = handler.get_rowid(this->iter); + rowid_out = get_handler().get_rowid(this->iter); return SQLITE_OK; } + + private: + T &get_handler() { + auto* mod_vt = (typename vtab_module::vtab *) this->base.pVtab; + + return mod_vt->v_impl; + } }; }; diff --git a/src/xterm_mouse.hh b/src/xterm_mouse.hh index c80a9482..4da19311 100644 --- a/src/xterm_mouse.hh +++ b/src/xterm_mouse.hh @@ -59,7 +59,7 @@ */ class mouse_behavior { public: - virtual ~mouse_behavior() { }; + virtual ~mouse_behavior() = default; /** * Callback used to process mouse events. diff --git a/test/drive_line_buffer.cc b/test/drive_line_buffer.cc index d365b4c7..a5265994 100644 --- a/test/drive_line_buffer.cc +++ b/test/drive_line_buffer.cc @@ -36,18 +36,15 @@ #include #include -#include #include #include #include #include -#include #include #include #include "base/string_util.hh" -#include "lnav_util.hh" #include "auto_fd.hh" #include "line_buffer.hh" @@ -57,7 +54,7 @@ int main(int argc, char *argv[]) { int c, rnd_iters = 5, retval = EXIT_SUCCESS; vector > index; - auto_fd fd = STDIN_FILENO, fd_cmp; + auto_fd fd = auto_fd(STDIN_FILENO), fd_cmp; int offseti = 0; off_t offset = 0; int count = 1000; diff --git a/test/drive_sql.cc b/test/drive_sql.cc index e98968e6..984a3a7e 100644 --- a/test/drive_sql.cc +++ b/test/drive_sql.cc @@ -16,7 +16,7 @@ struct callback_state { int cs_row; }; -struct _lnav_data lnav_data; +struct lnav_data_t lnav_data; static int sql_callback(void *ptr, int ncols, diff --git a/test/drive_sql_anno.cc b/test/drive_sql_anno.cc index 66359952..11513f4c 100644 --- a/test/drive_sql_anno.cc +++ b/test/drive_sql_anno.cc @@ -38,7 +38,7 @@ using namespace std; -struct _lnav_data lnav_data; +struct lnav_data_t lnav_data; void rebuild_hist() { diff --git a/test/test_auto_fd.cc b/test/test_auto_fd.cc index 43852353..b0b7f6b2 100644 --- a/test/test_auto_fd.cc +++ b/test/test_auto_fd.cc @@ -30,7 +30,6 @@ #include "config.h" #include -#include #include #include #include @@ -50,7 +49,7 @@ int main(int argc, char *argv[]) fd1 = tmp; fd1 = tmp; assert(fcntl(tmp, F_GETFL) >= 0); - fd1 = fd2; + fd1 = std::move(fd2); assert(fcntl(tmp, F_GETFL) == -1); assert(errno == EBADF); assert(fd1 == -1);