/** * Copyright (c) 2015, 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. */ #ifndef LNAV_COMMAND_EXECUTOR_H #define LNAV_COMMAND_EXECUTOR_H #include #include #include #include #include #include "base/auto_fd.hh" #include "base/lnav.console.hh" #include "db_sub_source.hh" #include "fmt/format.h" #include #include "help_text.hh" #include "shlex.resolver.hh" #include "vis_line.hh" struct exec_context; class attr_line_t; class logline_value; struct logline_value_vector; using sql_callback_t = int (*)(exec_context&, sqlite3_stmt*); int sql_callback(exec_context& ec, sqlite3_stmt* stmt); using pipe_callback_t = std::future (*)(exec_context&, const std::string&, auto_fd&); using error_callback_t = std::function; struct exec_context { enum class perm_t { READ_WRITE, READ_ONLY, }; using output_t = std::pair; exec_context(logline_value_vector* line_values = nullptr, sql_callback_t sql_callback = ::sql_callback, pipe_callback_t pipe_callback = nullptr); bool is_read_write() const { return this->ec_perms == perm_t::READ_WRITE; } bool is_read_only() const { return this->ec_perms == perm_t::READ_ONLY; } exec_context& with_perms(perm_t perms) { this->ec_perms = perms; return *this; } void add_error_context(lnav::console::user_message& um); template lnav::console::user_message make_error_msg(fmt::string_view format_str, const Args&... args) { auto retval = lnav::console::user_message::error( fmt::vformat(format_str, fmt::make_format_args(args...))); this->add_error_context(retval); return retval; } template Result make_error( fmt::string_view format_str, const Args&... args) { return Err(this->make_error_msg(format_str, args...)); } std::optional get_output() { for (auto iter = this->ec_output_stack.rbegin(); iter != this->ec_output_stack.rend(); ++iter) { if (iter->second && (*iter->second).first) { return (*iter->second).first; } } return std::nullopt; } void set_output(const std::string& name, FILE* file, int (*closer)(FILE*)); void clear_output(); struct mouse_input {}; struct user {}; struct file_open { std::string fo_name; }; using provenance_t = mapbox::util::variant; struct provenance_guard { explicit provenance_guard(exec_context* context, provenance_t prov) : pg_context(context) { this->pg_context->ec_provenance.push_back(prov); } provenance_guard(const provenance_guard&) = delete; provenance_guard(provenance_guard&& other) : pg_context(other.pg_context) { other.pg_context = nullptr; } ~provenance_guard() { if (this->pg_context != nullptr) { this->pg_context->ec_provenance.pop_back(); } } exec_context* operator->() { return this->pg_context; } exec_context* pg_context; }; provenance_guard with_provenance(provenance_t prov) { return provenance_guard{this, prov}; } struct source_guard { source_guard(exec_context* context) : sg_context(context) {} source_guard(const source_guard&) = delete; source_guard(source_guard&& other) : sg_context(other.sg_context) { other.sg_context = nullptr; } ~source_guard() { if (this->sg_context != nullptr) { this->sg_context->ec_source.pop_back(); } } exec_context* sg_context; }; struct output_guard { explicit output_guard(exec_context& context, std::string name = "default", const std::optional& file = std::nullopt); ~output_guard(); exec_context& sg_context; }; source_guard enter_source(intern_string_t path, int line_number, const std::string& content); struct db_source_guard { db_source_guard(exec_context* context) : dsg_context(context) {} db_source_guard(const source_guard&) = delete; db_source_guard(source_guard&& other) : dsg_context(other.sg_context) { other.sg_context = nullptr; } ~db_source_guard() { if (this->dsg_context != nullptr) { this->dsg_context->ec_label_source_stack.pop_back(); } } exec_context* dsg_context; }; db_source_guard enter_db_source(db_label_source* dls) { this->ec_label_source_stack.push_back(dls); return db_source_guard{this}; } struct error_cb_guard { error_cb_guard(exec_context* context) : sg_context(context) {} error_cb_guard(const error_cb_guard&) = delete; error_cb_guard(error_cb_guard&& other) : sg_context(other.sg_context) { other.sg_context = nullptr; } ~error_cb_guard() { if (this->sg_context != nullptr) { this->sg_context->ec_error_callback_stack.pop_back(); } } exec_context* sg_context; }; error_cb_guard add_error_callback(error_callback_t cb) { this->ec_error_callback_stack.emplace_back(std::move(cb)); return {this}; } scoped_resolver create_resolver() { return { &this->ec_local_vars.top(), &this->ec_global_vars, }; } void execute(const std::string& cmdline); using kv_pair_t = std::pair; void execute_with_int(const std::string& cmdline) { this->execute(cmdline); } template void execute_with_int(const std::string& cmdline, kv_pair_t pair, Args... args) { this->ec_local_vars.top().template emplace(pair); this->execute(cmdline, args...); } template void execute_with(const std::string& cmdline, Args... args) { this->ec_local_vars.push({}); this->execute_with_int(cmdline, args...); this->ec_local_vars.pop(); } template std::optional get_provenance() const { for (const auto& elem : this->ec_provenance) { if (elem.is()) { return elem.get(); } } return std::nullopt; } vis_line_t ec_top_line{0_vl}; bool ec_dry_run{false}; perm_t ec_perms{perm_t::READ_WRITE}; logline_value_vector* ec_line_values; std::stack> ec_local_vars; std::vector ec_provenance; std::map ec_global_vars; std::vector ec_path_stack; std::vector ec_source; help_text* ec_current_help{nullptr}; std::vector>> ec_output_stack; std::unique_ptr ec_accumulator; sql_callback_t ec_sql_callback; pipe_callback_t ec_pipe_callback; std::vector ec_error_callback_stack; std::vector ec_label_source_stack; }; Result execute_command( exec_context& ec, const std::string& cmdline); Result execute_sql( exec_context& ec, const std::string& sql, std::string& alt_msg); class multiline_executor { public: exec_context& me_exec_context; std::string me_source; std::optional me_cmdline; int me_line_number{0}; int me_starting_line_number{0}; std::string me_last_result; multiline_executor(exec_context& ec, std::string src) : me_exec_context(ec), me_source(src) { } Result push_back(string_fragment line); Result final(); }; Result execute_file( exec_context& ec, const std::string& path_and_args); Result execute_any( exec_context& ec, const std::string& cmdline); void execute_init_commands( exec_context& ec, std::vector, std::string>>& msgs); std::future pipe_callback(exec_context& ec, const std::string& cmdline, auto_fd& fd); int sql_progress(const struct log_cursor& lc); void sql_progress_finished(); void add_global_vars(exec_context& ec); #endif // LNAV_COMMAND_EXECUTOR_H