• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tstack / lnav / 17589970077-2502

09 Sep 2025 05:00PM UTC coverage: 65.196% (-5.0%) from 70.225%
17589970077-2502

push

github

tstack
[format] add fields for source file/line

Knowing the source file/line context in a log
message can help find log messages when using
log2src.

56 of 70 new or added lines in 2 files covered. (80.0%)

13954 existing lines in 210 files now uncovered.

45516 of 69814 relevant lines covered (65.2%)

404154.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.91
/src/command_executor.hh
1
/**
2
 * Copyright (c) 2015, Timothy Stack
3
 *
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *
9
 * * Redistributions of source code must retain the above copyright notice, this
10
 * list of conditions and the following disclaimer.
11
 * * Redistributions in binary form must reproduce the above copyright notice,
12
 * this list of conditions and the following disclaimer in the documentation
13
 * and/or other materials provided with the distribution.
14
 * * Neither the name of Timothy Stack nor the names of its contributors
15
 * may be used to endorse or promote products derived from this software
16
 * without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29

30
#ifndef LNAV_COMMAND_EXECUTOR_H
31
#define LNAV_COMMAND_EXECUTOR_H
32

33
#include <filesystem>
34
#include <functional>
35
#include <future>
36
#include <optional>
37
#include <stack>
38
#include <string>
39
#include <vector>
40

41
#include <sqlite3.h>
42

43
#include "base/auto_fd.hh"
44
#include "base/lnav.console.hh"
45
#include "base/lnav.resolver.hh"
46
#include "db_sub_source.hh"
47
#include "fmt/format.h"
48
#include "help_text.hh"
49
#include "lnav.script.parser.hh"
50
#include "vis_line.hh"
51

52
struct exec_context;
53
class attr_line_t;
54
class logline_value;
55
struct logline_value_vector;
56

57
using sql_callback_t = int (*)(exec_context&, sqlite3_stmt*);
58
int sql_callback(exec_context& ec, sqlite3_stmt* stmt);
59
int internal_sql_callback(exec_context& ec, sqlite3_stmt* stmt);
60

61
using pipe_callback_t
62
    = std::future<std::string> (*)(exec_context&, const std::string&, auto_fd&);
63

64
using msg_callback_t = std::function<void(const lnav::console::user_message&)>;
65

66
struct exec_context {
67
    enum class perm_t {
68
        READ_WRITE,
69
        READ_ONLY,
70
    };
71

72
    using output_t = std::pair<FILE*, int (*)(FILE*)>;
73

74
    exec_context(logline_value_vector* line_values = nullptr,
75
                 sql_callback_t sql_callback = ::sql_callback,
76
                 pipe_callback_t pipe_callback = nullptr);
77

78
    bool is_read_write() const { return this->ec_perms == perm_t::READ_WRITE; }
34✔
79

80
    bool is_read_only() const { return this->ec_perms == perm_t::READ_ONLY; }
1,619✔
81

82
    exec_context& with_perms(perm_t perms)
50✔
83
    {
84
        this->ec_perms = perms;
50✔
85
        return *this;
50✔
86
    }
87

88
    void add_error_context(lnav::console::user_message& um);
89

90
    template<typename... Args>
91
    lnav::console::user_message make_error_msg(fmt::string_view format_str,
24✔
92
                                               const Args&... args)
93
    {
94
        auto retval = lnav::console::user_message::error(
24✔
95
            fmt::vformat(format_str, fmt::make_format_args(args...)));
24✔
96

97
        this->add_error_context(retval);
24✔
98

99
        return retval;
24✔
UNCOV
100
    }
×
101

102
    template<typename... Args>
103
    Result<std::string, lnav::console::user_message> make_error(
23✔
104
        fmt::string_view format_str, const Args&... args)
105
    {
106
        return Err(this->make_error_msg(format_str, args...));
23✔
107
    }
108

109
    std::optional<FILE*> get_output() const
178✔
110
    {
111
        for (auto iter = this->ec_output_stack.rbegin();
178✔
112
             iter != this->ec_output_stack.rend();
224✔
113
             ++iter)
46✔
114
        {
115
            if (iter->od_output && iter->od_output->first) {
222✔
116
                return iter->od_output->first;
176✔
117
            }
118
        }
119

120
        return std::nullopt;
2✔
121
    }
122

123
    text_format_t get_output_format() const
7✔
124
    {
125
        auto retval = text_format_t::TF_UNKNOWN;
7✔
126

127
        if (!this->ec_output_stack.empty()) {
7✔
128
            retval = this->ec_output_stack.back().od_format;
7✔
129
        }
130
        return retval;
7✔
131
    }
132

133
    void set_output(const std::string& name, FILE* file, int (*closer)(FILE*));
134

135
    void clear_output();
136

137
    struct mouse_input {};
138
    struct user {};
139
    struct file_open {
140
        std::string fo_name;
141
    };
142

143
    using provenance_t = mapbox::util::variant<user, mouse_input, file_open>;
144

145
    struct provenance_guard {
146
        explicit provenance_guard(exec_context* context, provenance_t prov)
5✔
147
            : pg_context(context)
5✔
148
        {
149
            this->pg_context->ec_provenance.push_back(prov);
5✔
150
        }
5✔
151

152
        provenance_guard(const provenance_guard&) = delete;
153
        provenance_guard(provenance_guard&& other)
154
            : pg_context(other.pg_context)
155
        {
156
            other.pg_context = nullptr;
157
        }
158

159
        ~provenance_guard()
5✔
160
        {
161
            if (this->pg_context != nullptr) {
5✔
162
                this->pg_context->ec_provenance.pop_back();
5✔
163
            }
164
        }
5✔
165

166
        exec_context* operator->() { return this->pg_context; }
×
167

168
        exec_context* pg_context;
169
    };
170

171
    provenance_guard with_provenance(provenance_t prov)
×
172
    {
173
        return provenance_guard{this, prov};
×
174
    }
175

176
    struct source_guard {
177
        source_guard(exec_context* context) : sg_context(context) {}
2,344✔
178

179
        source_guard(const source_guard&) = delete;
180

181
        source_guard(source_guard&& other)
182
            : sg_context(std::exchange(other.sg_context, nullptr))
183
        {
184
        }
185

186
        ~source_guard()
2,344✔
187
        {
188
            if (this->sg_context != nullptr) {
2,344✔
189
                this->sg_context->ec_source.pop_back();
2,344✔
190
            }
191
        }
2,344✔
192

193
        exec_context* sg_context;
194
    };
195

196
    struct output_guard {
197
        explicit output_guard(exec_context& context,
198
                              std::string name = "default",
199
                              const std::optional<output_t>& file
200
                              = std::nullopt,
201
                              text_format_t tf = text_format_t::TF_UNKNOWN);
202

203
        ~output_guard();
204

205
        exec_context& sg_context;
206
        bool sg_active;
207
    };
208

209
    struct sql_callback_guard {
210
        sql_callback_guard(exec_context& context, sql_callback_t cb);
211

212
        sql_callback_guard(const sql_callback_guard&) = delete;
213

214
        sql_callback_guard(sql_callback_guard&& other);
215

216
        ~sql_callback_guard();
217

218
        exec_context& scg_context;
219
        sql_callback_t scg_old_callback;
220
    };
221

222
    sql_callback_guard push_callback(sql_callback_t cb);
223

224
    source_guard enter_source(source_location, const std::string& content);
225

226
    source_guard enter_source(intern_string_t path,
2,342✔
227
                              int line_number,
228
                              const std::string& content)
229
    {
230
        return this->enter_source(source_location{path, line_number}, content);
2,342✔
231
    }
232

233
    struct db_source_guard {
234
        explicit db_source_guard(exec_context* context) : dsg_context(context)
2✔
235
        {
236
        }
2✔
237

238
        db_source_guard(const db_source_guard&) = delete;
239

240
        db_source_guard(db_source_guard&& other) noexcept
241
            : dsg_context(std::exchange(other.dsg_context, nullptr))
242
        {
243
        }
244

245
        ~db_source_guard()
2✔
246
        {
247
            if (this->dsg_context != nullptr) {
2✔
248
                this->dsg_context->ec_label_source_stack.pop_back();
2✔
249
            }
250
        }
2✔
251

252
        exec_context* dsg_context;
253
    };
254

255
    db_source_guard enter_db_source(db_label_source* dls)
2✔
256
    {
257
        this->ec_label_source_stack.push_back(dls);
2✔
258

259
        return db_source_guard{this};
2✔
260
    }
261

262
    struct msg_cb_guard {
263
        explicit msg_cb_guard(std::vector<msg_callback_t>* cb_stack)
563✔
264
            : sg_cb_stack(cb_stack)
563✔
265
        {
266
        }
563✔
267

268
        msg_cb_guard(const msg_cb_guard&) = delete;
269
        msg_cb_guard(msg_cb_guard&& other) noexcept
270
            : sg_cb_stack(other.sg_cb_stack)
271
        {
272
            other.sg_cb_stack = nullptr;
273
        }
274

275
        ~msg_cb_guard()
563✔
276
        {
277
            if (this->sg_cb_stack != nullptr) {
563✔
278
                this->sg_cb_stack->pop_back();
563✔
279
            }
280
        }
563✔
281

282
        std::vector<msg_callback_t>* sg_cb_stack;
283
    };
284

285
    msg_cb_guard add_msg_callback(msg_callback_t cb)
563✔
286
    {
287
        this->ec_msg_callback_stack.emplace_back(std::move(cb));
563✔
288
        return msg_cb_guard{&this->ec_msg_callback_stack};
563✔
289
    }
290

291
    scoped_resolver create_resolver()
225✔
292
    {
293
        return {
294
            &this->ec_local_vars.top(),
225✔
295
            &this->ec_global_vars,
225✔
296
        };
225✔
297
    }
298

299
    Result<std::string, lnav::console::user_message> execute(
300
        source_location loc, const std::string& cmdline);
301

302
    using kv_pair_t = std::pair<std::string, std::string>;
303

UNCOV
304
    Result<std::string, lnav::console::user_message> execute_with_int(
×
305
        source_location loc, const std::string& cmdline)
306
    {
UNCOV
307
        return this->execute(loc, cmdline);
×
308
    }
309

310
    template<typename... Args>
UNCOV
311
    Result<std::string, lnav::console::user_message> execute_with_int(
×
312
        source_location loc,
313
        const std::string& cmdline,
314
        kv_pair_t pair,
315
        Args... args)
316
    {
UNCOV
317
        this->ec_local_vars.top().emplace(pair);
×
UNCOV
318
        return this->execute_with_int(loc, cmdline, args...);
×
319
    }
320

321
    template<typename... Args>
UNCOV
322
    Result<std::string, lnav::console::user_message> execute_with(
×
323
        source_location loc, const std::string& cmdline, Args... args)
324
    {
UNCOV
325
        this->ec_local_vars.push({});
×
326
        auto retval = this->execute_with_int(loc, cmdline, args...);
×
327
        this->ec_local_vars.pop();
×
328

UNCOV
329
        return retval;
×
330
    }
331

332
    template<typename T>
333
    std::optional<T> get_provenance() const
20✔
334
    {
335
        for (const auto& elem : this->ec_provenance) {
20✔
336
            if (elem.is<T>()) {
5✔
337
                return elem.get<T>();
5✔
338
            }
339
        }
340

341
        return std::nullopt;
15✔
342
    }
343

344
    vis_line_t ec_top_line{0_vl};
345
    bool ec_dry_run{false};
346
    perm_t ec_perms{perm_t::READ_WRITE};
347

348
    logline_value_vector* ec_line_values;
349
    std::stack<std::map<std::string, scoped_value_t>> ec_local_vars;
350
    std::vector<provenance_t> ec_provenance;
351
    std::map<std::string, scoped_value_t> ec_global_vars;
352
    std::vector<std::filesystem::path> ec_path_stack;
353
    std::vector<lnav::console::snippet> ec_source;
354
    help_text* ec_current_help{nullptr};
355

356
    struct output_desc {
357
        output_desc(std::string name,
4,023✔
358
                    std::optional<output_t> out,
359
                    text_format_t tf = text_format_t::TF_UNKNOWN)
360
            : od_name(std::move(name)), od_output(std::move(out)), od_format(tf)
4,023✔
361
        {
362
        }
4,023✔
363

364
        std::string od_name;
365
        std::optional<output_t> od_output;
366
        text_format_t od_format{text_format_t::TF_UNKNOWN};
367
    };
368

369
    std::vector<output_desc> ec_output_stack;
370
    std::unique_ptr<attr_line_t> ec_accumulator;
371

372
    sql_callback_t ec_sql_callback;
373
    pipe_callback_t ec_pipe_callback;
374
    std::vector<msg_callback_t> ec_msg_callback_stack;
375
    std::vector<db_label_source*> ec_label_source_stack;
376

377
    struct ui_callbacks {
378
        std::function<void()> uc_pre_stdout_write;
379
        std::function<void()> uc_post_stdout_write;
380
        std::function<void()> uc_redraw;
381
    };
382
    ui_callbacks ec_ui_callbacks;
383
};
384

385
Result<std::string, lnav::console::user_message> execute_command(
386
    exec_context& ec, const std::string& cmdline);
387

388
Result<std::string, lnav::console::user_message> execute_sql(
389
    exec_context& ec, const std::string& sql, std::string& alt_msg);
390

391
class multiline_executor : public lnav::script::parser {
392
public:
393
    exec_context& me_exec_context;
394
    std::string me_last_result;
395

396
    multiline_executor(exec_context& ec, const std::string& src)
55✔
397
        : parser(src), me_exec_context(ec)
55✔
398
    {
399
    }
55✔
400

401
    Result<void, lnav::console::user_message> handle_command(
402
        const std::string& cmd) override;
403
};
404

405
Result<std::string, lnav::console::user_message> execute_file(
406
    exec_context& ec, const std::string& path_and_args);
407
Result<std::string, lnav::console::user_message> execute_any(
408
    exec_context& ec, const std::string& cmdline);
409
void execute_init_commands(
410
    exec_context& ec,
411
    std::vector<std::pair<Result<std::string, lnav::console::user_message>,
412
                          std::string>>& msgs);
413

414
std::future<std::string> pipe_callback(exec_context& ec,
415
                                       const std::string& cmdline,
416
                                       auto_fd& fd);
417

418
int sql_progress(const struct log_cursor& lc);
419
void sql_progress_finished();
420

421
void add_global_vars(exec_context& ec);
422

423
#endif  // LNAV_COMMAND_EXECUTOR_H
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc