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

tstack / lnav / 23066365063-2835

13 Mar 2026 06:12PM UTC coverage: 68.989% (+0.01%) from 68.979%
23066365063-2835

push

github

tstack
[cmds] add sticky headers

Related to #1385

114 of 146 new or added lines in 6 files covered. (78.08%)

13 existing lines in 4 files now uncovered.

52488 of 76082 relevant lines covered (68.99%)

520548.33 hits per line

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

79.0
/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; }
35✔
79

80
    bool is_read_only() const { return this->ec_perms == perm_t::READ_ONLY; }
1,670✔
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) const;
89

90
    lnav::console::user_message make_error_from_str(std::string&& str) const;
91

92
    template<typename... Args>
93
    lnav::console::user_message make_error_msg(fmt::string_view format_str,
31✔
94
                                               const Args&... args) const
95
    {
96
        auto str = fmt::vformat(format_str, fmt::make_format_args(args...));
51✔
97

98
        return this->make_error_from_str(std::move(str));
62✔
99
    }
31✔
100

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

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

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

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

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

132
    void set_output_format(text_format_t tf)
108✔
133
    {
134
        if (!this->ec_output_stack.empty()
108✔
135
            && this->ec_output_stack.back().od_format
108✔
136
                == text_format_t::TF_PLAINTEXT)
137
        {
138
            this->ec_output_stack.back().od_format = tf;
106✔
139
        }
140
    }
108✔
141

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

144
    void clear_output();
145

146
    struct mouse_input {};
147
    struct keyboard_input {};
148
    struct format_rewrite {};
149
    struct user {};
150
    struct file_open {
151
        std::string fo_name;
152
    };
153
    struct external_access {
154
        std::string ea_src;
155
    };
156

157
    using provenance_t = mapbox::util::variant<user,
158
                                               format_rewrite,
159
                                               keyboard_input,
160
                                               mouse_input,
161
                                               file_open,
162
                                               external_access>;
163

UNCOV
164
    struct provenance_guard {
×
165
        explicit provenance_guard(exec_context* context, provenance_t prov)
51✔
166
            : pg_context(context)
51✔
UNCOV
167
        {
×
168
            this->pg_context->ec_provenance.push_back(prov);
51✔
169
        }
51✔
170

171
        provenance_guard(const provenance_guard&) = delete;
172
        provenance_guard(provenance_guard&& other)
173
            : pg_context(other.pg_context)
174
        {
175
            other.pg_context = nullptr;
176
        }
UNCOV
177

×
178
        ~provenance_guard()
51✔
UNCOV
179
        {
×
180
            if (this->pg_context != nullptr) {
51✔
181
                this->pg_context->ec_provenance.pop_back();
51✔
182
            }
183
        }
51✔
184

185
        exec_context* operator->() { return this->pg_context; }
×
186

187
        exec_context* pg_context;
188
    };
UNCOV
189

×
190
    provenance_guard with_provenance(provenance_t prov)
46✔
UNCOV
191
    {
×
192
        return provenance_guard{this, prov};
46✔
193
    }
194

195
    struct source_guard {
196
        source_guard(exec_context* context) : sg_context(context) {}
2,555✔
197

198
        source_guard(const source_guard&) = delete;
199

200
        source_guard(source_guard&& other)
201
            : sg_context(std::exchange(other.sg_context, nullptr))
202
        {
203
        }
UNCOV
204

×
205
        ~source_guard()
2,555✔
UNCOV
206
        {
×
207
            if (this->sg_context != nullptr) {
2,555✔
208
                this->sg_context->ec_source.pop_back();
2,555✔
209
            }
210
        }
2,555✔
211

212
        exec_context* sg_context;
213
    };
214

215
    struct output_guard {
216
        explicit output_guard(exec_context& context,
217
                              std::string name = "default",
218
                              const std::optional<output_t>& file
219
                              = std::nullopt,
220
                              text_format_t tf = text_format_t::TF_PLAINTEXT);
221

222
        ~output_guard();
223

224
        exec_context& sg_context;
225
        bool sg_active;
226
    };
227

228
    struct sql_callback_guard {
229
        sql_callback_guard(exec_context& context, sql_callback_t cb);
230

231
        sql_callback_guard(const sql_callback_guard&) = delete;
232

233
        sql_callback_guard(sql_callback_guard&& other);
234

235
        ~sql_callback_guard();
236

237
        exec_context& scg_context;
238
        sql_callback_t scg_old_callback;
239
    };
240

241
    sql_callback_guard push_callback(sql_callback_t cb);
242

243
    source_guard enter_source(source_location, const std::string& content);
UNCOV
244

×
245
    source_guard enter_source(intern_string_t path,
2,553✔
246
                              int line_number,
247
                              const std::string& content)
UNCOV
248
    {
×
249
        return this->enter_source(source_location{path, line_number}, content);
2,553✔
250
    }
251

252
    struct db_source_guard {
253
        explicit db_source_guard(exec_context* context) : dsg_context(context)
2✔
254
        {
255
        }
2✔
256

257
        db_source_guard(const db_source_guard&) = delete;
258

259
        db_source_guard(db_source_guard&& other) noexcept
260
            : dsg_context(std::exchange(other.dsg_context, nullptr))
261
        {
262
        }
263

264
        ~db_source_guard()
2✔
265
        {
266
            if (this->dsg_context != nullptr) {
2✔
267
                this->dsg_context->ec_label_source_stack.pop_back();
2✔
268
            }
269
        }
2✔
270

271
        exec_context* dsg_context;
272
    };
273

274
    db_source_guard enter_db_source(db_label_source* dls)
2✔
275
    {
276
        this->ec_label_source_stack.push_back(dls);
2✔
277

278
        return db_source_guard{this};
2✔
279
    }
280

281
    struct msg_cb_guard {
282
        explicit msg_cb_guard(std::vector<msg_callback_t>* cb_stack)
649✔
283
            : sg_cb_stack(cb_stack)
649✔
284
        {
285
        }
649✔
286

287
        msg_cb_guard(const msg_cb_guard&) = delete;
288
        msg_cb_guard(msg_cb_guard&& other) noexcept
289
            : sg_cb_stack(other.sg_cb_stack)
290
        {
291
            other.sg_cb_stack = nullptr;
292
        }
293

294
        ~msg_cb_guard()
649✔
295
        {
296
            if (this->sg_cb_stack != nullptr) {
649✔
297
                this->sg_cb_stack->pop_back();
649✔
298
            }
299
        }
649✔
300

301
        std::vector<msg_callback_t>* sg_cb_stack;
302
    };
303

304
    msg_cb_guard add_msg_callback(msg_callback_t cb)
649✔
305
    {
306
        this->ec_msg_callback_stack.emplace_back(std::move(cb));
649✔
307
        return msg_cb_guard{&this->ec_msg_callback_stack};
649✔
308
    }
309

310
    scoped_resolver create_resolver()
248✔
311
    {
312
        return {
313
            &this->ec_local_vars.top(),
248✔
314
            &this->ec_global_vars,
248✔
315
        };
248✔
316
    }
317

318
    Result<std::string, lnav::console::user_message> execute(
319
        source_location loc, const std::string& cmdline);
320

321
    using kv_pair_t = std::pair<std::string, std::string>;
322

323
    Result<std::string, lnav::console::user_message> execute_with_int(
×
324
        source_location loc, const std::string& cmdline)
325
    {
326
        return this->execute(loc, cmdline);
×
327
    }
328

329
    template<typename... Args>
330
    Result<std::string, lnav::console::user_message> execute_with_int(
×
331
        source_location loc,
332
        const std::string& cmdline,
333
        kv_pair_t pair,
334
        Args... args)
335
    {
336
        this->ec_local_vars.top().emplace(pair);
×
337
        return this->execute_with_int(loc, cmdline, args...);
×
338
    }
339

340
    template<typename... Args>
341
    Result<std::string, lnav::console::user_message> execute_with(
×
342
        source_location loc, const std::string& cmdline, Args... args)
343
    {
344
        this->ec_local_vars.push({});
×
345
        auto retval = this->execute_with_int(loc, cmdline, args...);
×
346
        this->ec_local_vars.pop();
×
347

348
        return retval;
×
349
    }
350

351
    template<typename T>
352
    std::optional<T> get_provenance() const
1,836✔
353
    {
354
        for (const auto& elem : this->ec_provenance) {
1,852✔
355
            if (elem.is<T>()) {
53✔
356
                return elem.get<T>();
37✔
357
            }
358
        }
359

360
        return std::nullopt;
1,799✔
361
    }
362

363
    vis_line_t ec_top_line{0_vl};
364
    bool ec_dry_run{false};
365
    perm_t ec_perms{perm_t::READ_WRITE};
366

367
    logline_value_vector* ec_line_values;
368
    std::stack<std::map<std::string, scoped_value_t>> ec_local_vars;
369
    std::vector<provenance_t> ec_provenance;
370
    std::map<std::string, scoped_value_t> ec_global_vars;
371
    std::vector<std::filesystem::path> ec_path_stack;
372
    std::vector<lnav::console::snippet> ec_source;
373
    help_text* ec_current_help{nullptr};
374

375
    struct output_desc {
376
        output_desc(std::string name,
4,347✔
377
                    std::optional<output_t> out,
378
                    text_format_t tf = text_format_t::TF_PLAINTEXT)
379
            : od_name(std::move(name)), od_output(std::move(out)), od_format(tf)
4,347✔
380
        {
381
        }
4,347✔
382

383
        std::string od_name;
384
        std::optional<output_t> od_output;
385
        text_format_t od_format{text_format_t::TF_PLAINTEXT};
386
    };
387

388
    std::vector<output_desc> ec_output_stack;
389
    std::unique_ptr<attr_line_t> ec_accumulator;
390

391
    sql_callback_t ec_sql_callback;
392
    pipe_callback_t ec_pipe_callback;
393
    std::vector<msg_callback_t> ec_msg_callback_stack;
394
    std::vector<db_label_source*> ec_label_source_stack;
395

396
    struct ui_callbacks {
397
        std::function<void()> uc_pre_stdout_write;
398
        std::function<void()> uc_post_stdout_write;
399
        std::function<void()> uc_redraw;
400
    };
401
    ui_callbacks ec_ui_callbacks;
402
};
403

404
Result<std::string, lnav::console::user_message> execute_command(
405
    exec_context& ec, const std::string& cmdline);
406

407
Result<std::string, lnav::console::user_message> execute_sql(
408
    exec_context& ec, const std::string& sql, std::string& alt_msg);
409

410
class multiline_executor : public lnav::script::parser {
411
public:
412
    exec_context& me_exec_context;
413
    std::string me_last_result;
414

415
    multiline_executor(exec_context& ec, const std::string& src)
60✔
416
        : parser(src), me_exec_context(ec)
60✔
417
    {
418
    }
60✔
419

420
    Result<void, lnav::console::user_message> handle_command(
421
        const std::string& cmd) override;
422
};
423

424
Result<std::string, lnav::console::user_message> execute_file(
425
    exec_context& ec, const std::string& path_and_args);
426
Result<std::string, lnav::console::user_message> execute_any(
427
    exec_context& ec, const std::string& cmdline);
428
void execute_init_commands(
429
    exec_context& ec,
430
    std::vector<std::pair<Result<std::string, lnav::console::user_message>,
431
                          std::string>>& msgs);
432

433
std::future<std::string> pipe_callback(exec_context& ec,
434
                                       const std::string& cmdline,
435
                                       auto_fd& fd);
436

437
int sql_progress(const struct log_cursor& lc);
438
void sql_progress_finished();
439

440
void add_global_vars(exec_context& ec);
441

442
#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