1 /**
2 * Copyright (c) 2013, 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 ON
25 * 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 * @file lnav_config.cc
30 */
31
32 #include "config.h"
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <glob.h>
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <libgen.h>
42
43 #include <regex>
44 #include <chrono>
45 #include <iostream>
46 #include <stdexcept>
47 #include <fmt/format.h>
48
49 #include "auto_fd.hh"
50 #include "base/injector.hh"
51 #include "base/injector.bind.hh"
52 #include "base/paths.hh"
53 #include "base/string_util.hh"
54 #include "base/lnav_log.hh"
55 #include "lnav_util.hh"
56 #include "auto_mem.hh"
57 #include "base/auto_pid.hh"
58 #include "lnav_config.hh"
59 #include "yajlpp/yajlpp.hh"
60 #include "yajlpp/yajlpp_def.hh"
61 #include "styling.hh"
62 #include "bin2c.hh"
63 #include "default-config.h"
64
65 using namespace std;
66
67 static const int MAX_CRASH_LOG_COUNT = 16;
68 static const auto STDIN_CAPTURE_RETENTION = 24h;
69
70 static auto intern_lifetime = intern_string::get_table_lifetime();
71
72 struct _lnav_config lnav_config;
73 struct _lnav_config rollback_lnav_config;
74 static struct _lnav_config lnav_default_config;
75
76 std::map<intern_string_t, source_location> lnav_config_locations;
77
78 lnav_config_listener *lnav_config_listener::LISTENER_LIST;
79
__anon470316650102() 80 static auto a = injector::bind<archive_manager::config>::to_instance(+[]() {
81 return &lnav_config.lc_archive_manager;
82 });
83
__anon470316650202() 84 static auto fvc = injector::bind<file_vtab::config>::to_instance(+[]() {
85 return &lnav_config.lc_file_vtab;
86 });
87
__anon470316650302() 88 static auto lc = injector::bind<lnav::logfile::config>::to_instance(+[]() {
89 return &lnav_config.lc_logfile;
90 });
91
__anon470316650402() 92 static auto tc = injector::bind<tailer::config>::to_instance(+[]() {
93 return &lnav_config.lc_tailer;
94 });
95
__anon470316650502() 96 static auto scc = injector::bind<sysclip::config>::to_instance(+[]() {
97 return &lnav_config.lc_sysclip;
98 });
99
check_experimental(const char * feature_name)100 bool check_experimental(const char *feature_name)
101 {
102 const char *env_value = getenv("LNAV_EXP");
103
104 require(feature_name != nullptr);
105
106 if (env_value && strcasestr(env_value, feature_name)) {
107 return true;
108 }
109
110 return false;
111 }
112
ensure_dotlnav()113 void ensure_dotlnav()
114 {
115 static const char *subdirs[] = {
116 "",
117 "configs",
118 "configs/default",
119 "configs/installed",
120 "formats",
121 "formats/default",
122 "formats/installed",
123 "staging",
124 "stdin-captures",
125 "crash",
126 };
127
128 auto path = lnav::paths::dotlnav();
129
130 for (auto sub_path : subdirs) {
131 auto full_path = path / sub_path;
132
133 log_perror(mkdir(full_path.c_str(), 0755));
134 }
135
136 lnav_log_crash_dir = strdup(path.c_str());
137
138 {
139 static_root_mem<glob_t, globfree> gl;
140 auto crash_glob = path / "crash/*";
141
142 if (glob(crash_glob.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) {
143 for (int lpc = 0;
144 lpc < ((int)gl->gl_pathc - MAX_CRASH_LOG_COUNT);
145 lpc++) {
146 log_perror(remove(gl->gl_pathv[lpc]));
147 }
148 }
149 }
150
151 {
152 static_root_mem<glob_t, globfree> gl;
153 auto cap_glob = path / "stdin-captures/*";
154
155 if (glob(cap_glob.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) {
156 auto old_time = std::chrono::system_clock::now() -
157 STDIN_CAPTURE_RETENTION;
158
159 for (size_t lpc = 0; lpc < gl->gl_pathc; lpc++) {
160 struct stat st;
161
162 if (stat(gl->gl_pathv[lpc], &st) == -1) {
163 continue;
164 }
165
166 if (chrono::system_clock::from_time_t(st.st_mtime) > old_time) {
167 continue;
168 }
169
170 log_debug("Removing old stdin capture: %s", gl->gl_pathv[lpc]);
171 log_perror(remove(gl->gl_pathv[lpc]));
172 }
173 }
174 }
175 }
176
install_from_git(const char * repo)177 bool install_from_git(const char *repo)
178 {
179 static const std::regex repo_name_converter("[^\\w]");
180
181 auto formats_path = lnav::paths::dotlnav() / "formats";
182 auto configs_path = lnav::paths::dotlnav() / "configs";
183 auto staging_path = lnav::paths::dotlnav() / "staging";
184 string local_name = std::regex_replace(repo, repo_name_converter, "_");
185
186 auto local_formats_path = formats_path / local_name;
187 auto local_configs_path = configs_path / local_name;
188 auto local_staging_path = staging_path / local_name;
189
190 auto fork_res = lnav::pid::from_fork();
191 if (fork_res.isErr()) {
192 fprintf(stderr, "error: cannot fork() to run git: %s\n",
193 fork_res.unwrapErr().c_str());
194 _exit(1);
195 }
196
197 auto git_cmd = fork_res.unwrap();
198 if (git_cmd.in_child()) {
199 if (ghc::filesystem::is_directory(local_formats_path)) {
200 printf("Updating format repo: %s\n", repo);
201 log_perror(chdir(local_formats_path.c_str()));
202 execlp("git", "git", "pull", nullptr);
203 }
204 else if (ghc::filesystem::is_directory(local_configs_path)) {
205 printf("Updating config repo: %s\n", repo);
206 log_perror(chdir(local_configs_path.c_str()));
207 execlp("git", "git", "pull", nullptr);
208 }
209 else {
210 execlp("git", "git", "clone", repo, local_staging_path.c_str(), nullptr);
211 }
212 _exit(1);
213 }
214
215 auto finished_child = std::move(git_cmd).wait_for_child();
216
217 if (!finished_child.was_normal_exit() ||
218 finished_child.exit_status() != 0) {
219 return false;
220 }
221
222 if (ghc::filesystem::is_directory(local_staging_path)) {
223 auto config_path = local_staging_path / "*.json";
224 static_root_mem<glob_t, globfree> gl;
225 bool found_config_file = false, found_format_file = false;
226
227 if (glob(config_path.c_str(), 0, nullptr, gl.inout()) == 0) {
228 for (size_t lpc = 0; lpc < gl->gl_pathc; lpc++) {
229 auto json_file_path = gl->gl_pathv[lpc];
230 auto file_type_result = detect_config_file_type(json_file_path);
231
232 if (file_type_result.isErr()) {
233 fprintf(stderr, "error: %s\n",
234 file_type_result.unwrapErr().c_str());
235 return false;
236 }
237 if (file_type_result.unwrap() == config_file_type::CONFIG) {
238 found_config_file = true;
239 } else {
240 found_format_file = true;
241 }
242 }
243 }
244
245 if (found_config_file) {
246 rename(local_staging_path.c_str(),
247 local_configs_path.c_str());
248 fprintf(stderr, "info: installed configuration repo -- %s\n",
249 local_configs_path.c_str());
250 } else if (found_format_file) {
251 rename(local_staging_path.c_str(),
252 local_formats_path.c_str());
253 fprintf(stderr, "info: installed format repo -- %s\n",
254 local_formats_path.c_str());
255 } else {
256 fprintf(stderr, "error: cannot find a valid lnav configuration or format file\n");
257 return false;
258 }
259 }
260
261 return true;
262 }
263
update_installs_from_git()264 bool update_installs_from_git()
265 {
266 static_root_mem<glob_t, globfree> gl;
267 auto git_formats = lnav::paths::dotlnav() / "formats/*/.git";
268 bool found = false, retval = true;
269
270 if (glob(git_formats.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) {
271 for (int lpc = 0; lpc < (int) gl->gl_pathc; lpc++) {
272 char *git_dir = dirname(gl->gl_pathv[lpc]);
273 char pull_cmd[1024];
274
275 printf("Updating formats in %s\n", git_dir);
276 snprintf(pull_cmd, sizeof(pull_cmd),
277 "cd %s && git pull",
278 git_dir);
279 int ret = system(pull_cmd);
280 if (ret == -1) {
281 std::cerr << "Failed to spawn command "
282 << "\"" << pull_cmd << "\": "
283 << strerror(errno) << std::endl;
284 retval = false;
285 }
286 else if (ret > 0) {
287 std::cerr << "Command "
288 << "\"" << pull_cmd << "\" failed: "
289 << strerror(errno) << std::endl;
290 retval = false;
291 }
292 found = true;
293 }
294 }
295
296 if (!found) {
297 printf("No formats from git repositories found, "
298 "use 'lnav -i extra' to install third-party foramts\n");
299 }
300
301 return retval;
302 }
303
read_repo_path(yajlpp_parse_context * ypc,const unsigned char * str,size_t len)304 static int read_repo_path(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
305 {
306 string path = string((const char *)str, len);
307
308 install_from_git(path.c_str());
309
310 return 1;
311 }
312
313 static struct json_path_container format_handlers = {
314 json_path_handler("format-repos#", read_repo_path)
315 };
316
install_extra_formats()317 void install_extra_formats()
318 {
319 auto config_root = lnav::paths::dotlnav() / "remote-config";
320 auto_fd fd;
321
322 if (access(config_root.c_str(), R_OK) == 0) {
323 char pull_cmd[1024];
324
325 printf("Updating lnav remote config repo...\n");
326 snprintf(pull_cmd, sizeof(pull_cmd),
327 "cd '%s' && git pull",
328 config_root.c_str());
329 log_perror(system(pull_cmd));
330 }
331 else {
332 char clone_cmd[1024];
333
334 printf("Cloning lnav remote config repo...\n");
335 snprintf(clone_cmd, sizeof(clone_cmd),
336 "git clone https://github.com/tstack/lnav-config.git %s",
337 config_root.c_str());
338 log_perror(system(clone_cmd));
339 }
340
341 auto config_json = config_root / "remote-config.json";
342 if ((fd = openp(config_json, O_RDONLY)) == -1) {
343 perror("Unable to open remote-config.json");
344 }
345 else {
346 yajlpp_parse_context ypc_config(config_root.string(), &format_handlers);
347 auto_mem<yajl_handle_t> jhandle(yajl_free);
348 unsigned char buffer[4096];
349 ssize_t rc;
350
351 jhandle = yajl_alloc(&ypc_config.ypc_callbacks, nullptr, &ypc_config);
352 yajl_config(jhandle, yajl_allow_comments, 1);
353 while ((rc = read(fd, buffer, sizeof(buffer))) > 0) {
354 if (yajl_parse(jhandle,
355 buffer,
356 rc) != yajl_status_ok) {
357 auto msg = yajl_get_error(jhandle, 1, buffer, rc);
358 fprintf(stderr,
359 "Unable to parse remote-config.json -- %s",
360 msg);
361 yajl_free_error(jhandle, msg);
362 return;
363 }
364 }
365 if (yajl_complete_parse(jhandle) != yajl_status_ok) {
366 auto msg = yajl_get_error(jhandle, 1, buffer, rc);
367
368 fprintf(stderr,
369 "Unable to parse remote-config.json -- %s",
370 msg);
371 yajl_free_error(jhandle, msg);
372 }
373 }
374 }
375
376 struct userdata {
userdatauserdata377 userdata(vector<string> &errors) : ud_errors(errors) {};
378
379 vector<string> &ud_errors;
380 };
381
config_error_reporter(const yajlpp_parse_context & ypc,lnav_log_level_t level,const char * msg)382 static void config_error_reporter(const yajlpp_parse_context &ypc,
383 lnav_log_level_t level,
384 const char *msg)
385 {
386 if (level >= lnav_log_level_t::ERROR) {
387 struct userdata *ud = (userdata *) ypc.ypc_userdata;
388
389 ud->ud_errors.emplace_back(msg);
390 } else {
391 fprintf(stderr, "warning:%s\n", msg);
392 }
393 }
394
395 static struct json_path_container key_command_handlers = {
396 yajlpp::property_handler("command")
397 .with_synopsis("<command>")
398 .with_description(
399 "The command to execute for the given key sequence. Use a script "
400 "to execute more complicated operations.")
401 .with_pattern("[:|;].*")
402 .with_example(":goto next hour")
403 .FOR_FIELD(key_command, kc_cmd),
404 yajlpp::property_handler("alt-msg")
405 .with_synopsis("<msg>")
406 .with_description("The help message to display after the key is pressed.")
407 .FOR_FIELD(key_command, kc_alt_msg)
408 };
409
410 static struct json_path_container keymap_def_handlers = {
411 yajlpp::pattern_property_handler("(?<key_seq>(?:x[0-9a-f]{2})+)")
412 .with_synopsis("<utf8-key-code-in-hex>")
413 .with_description(
414 "Map of key codes to commands to execute. The field names are "
415 "the keys to be mapped using as a hexadecimal representation of "
416 "the UTF-8 encoding. Each byte of the UTF-8 should start with "
417 "an 'x' followed by the hexadecimal representation of the byte.")
__anon470316650602(const yajlpp_provider_context &ypc, key_map *km) 418 .with_obj_provider<key_command, key_map>([](const yajlpp_provider_context &ypc, key_map *km) {
419 key_command &retval = km->km_seq_to_cmd[ypc.ypc_extractor.get_substr("key_seq")];
420
421 return &retval;
422 })
__anon470316650702(key_map *km, vector<string> &paths_out) 423 .with_path_provider<key_map>([](key_map *km, vector<string> &paths_out) {
424 for (const auto &iter : km->km_seq_to_cmd) {
425 paths_out.emplace_back(iter.first);
426 }
427 })
428 .with_children(key_command_handlers)
429 };
430
431 static struct json_path_container keymap_defs_handlers = {
432 yajlpp::pattern_property_handler("(?<keymap_name>[\\w\\-]+)")
433 .with_description("The keymap definitions")
__anon470316650802(const yajlpp_provider_context &ypc, _lnav_config *root) 434 .with_obj_provider<key_map, _lnav_config>([](const yajlpp_provider_context &ypc, _lnav_config *root) {
435 key_map &retval = root->lc_ui_keymaps[ypc.ypc_extractor.get_substr("keymap_name")];
436 return &retval;
437 })
__anon470316650902(struct _lnav_config *cfg, vector<string> &paths_out) 438 .with_path_provider<_lnav_config>([](struct _lnav_config *cfg, vector<string> &paths_out) {
439 for (const auto &iter : cfg->lc_ui_keymaps) {
440 paths_out.emplace_back(iter.first);
441 }
442 })
443 .with_children(keymap_def_handlers)
444 };
445
446 static struct json_path_container global_var_handlers = {
447 yajlpp::pattern_property_handler("(?<var_name>\\w+)")
448 .with_synopsis("<name>")
449 .with_description(
450 "A global variable definition. Global variables can be referenced "
451 "in scripts, SQL statements, or commands.")
452 .with_path_provider<_lnav_config>(
__anon470316650a02(struct _lnav_config *cfg, vector<string> &paths_out) 453 [](struct _lnav_config *cfg, vector<string> &paths_out) {
454 for (const auto &iter : cfg->lc_global_vars) {
455 paths_out.emplace_back(iter.first);
456 }
457 })
458 .FOR_FIELD(_lnav_config, lc_global_vars)
459 };
460
461 static struct json_path_container style_config_handlers =
462 json_path_container{
463 yajlpp::property_handler("color")
464 .with_synopsis("#hex|color_name")
465 .with_description(
466 "The foreground color value for this style. The value can be "
467 "the name of an xterm color, the hexadecimal value, or a theme "
468 "variable reference.")
469 .with_example("#fff")
470 .with_example("Green")
471 .with_example("$black")
472 .FOR_FIELD(style_config, sc_color),
473 yajlpp::property_handler("background-color")
474 .with_synopsis("#hex|color_name")
475 .with_description(
476 "The background color value for this style. The value can be "
477 "the name of an xterm color, the hexadecimal value, or a theme "
478 "variable reference.")
479 .with_example("#2d2a2e")
480 .with_example("Green")
481 .FOR_FIELD(style_config, sc_background_color),
482 yajlpp::property_handler("underline")
483 .with_description("Indicates that the text should be underlined.")
484 .FOR_FIELD(style_config, sc_underline),
485 yajlpp::property_handler("bold")
486 .with_description("Indicates that the text should be bolded.")
487 .FOR_FIELD(style_config, sc_bold),
488 }
489 .with_definition_id("style");
490
491 static struct json_path_container theme_styles_handlers = {
492 yajlpp::property_handler("identifier")
493 .with_description("Styling for identifiers in logs")
__anon470316650b02(const yajlpp_provider_context &ypc, lnav_theme *root) 494 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
495 return &root->lt_style_identifier;
496 })
497 .with_children(style_config_handlers),
498 yajlpp::property_handler("text")
499 .with_description("Styling for plain text")
__anon470316650c02(const yajlpp_provider_context &ypc, lnav_theme *root) 500 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
501 return &root->lt_style_text;
502 })
503 .with_children(style_config_handlers),
504 yajlpp::property_handler("alt-text")
505 .with_description("Styling for plain text when alternating")
__anon470316650d02(const yajlpp_provider_context &ypc, lnav_theme *root) 506 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
507 return &root->lt_style_alt_text;
508 })
509 .with_children(style_config_handlers),
510 yajlpp::property_handler("error")
511 .with_description("Styling for error messages")
__anon470316650e02(const yajlpp_provider_context &ypc, lnav_theme *root) 512 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
513 return &root->lt_style_error;
514 })
515 .with_children(style_config_handlers),
516 yajlpp::property_handler("ok")
517 .with_description("Styling for success messages")
__anon470316650f02(const yajlpp_provider_context &ypc, lnav_theme *root) 518 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
519 return &root->lt_style_ok;
520 })
521 .with_children(style_config_handlers),
522 yajlpp::property_handler("warning")
523 .with_description("Styling for warning messages")
__anon470316651002(const yajlpp_provider_context &ypc, lnav_theme *root) 524 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
525 return &root->lt_style_warning;
526 })
527 .with_children(style_config_handlers),
528 yajlpp::property_handler("hidden")
529 .with_description("Styling for hidden fields in logs")
__anon470316651102(const yajlpp_provider_context &ypc, lnav_theme *root) 530 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
531 return &root->lt_style_hidden;
532 })
533 .with_children(style_config_handlers),
534 yajlpp::property_handler("adjusted-time")
535 .with_description("Styling for timestamps that have been adjusted")
__anon470316651202(const yajlpp_provider_context &ypc, lnav_theme *root) 536 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
537 return &root->lt_style_adjusted_time;
538 })
539 .with_children(style_config_handlers),
540 yajlpp::property_handler("skewed-time")
541 .with_description("Styling for timestamps that are different from the received time")
__anon470316651302(const yajlpp_provider_context &ypc, lnav_theme *root) 542 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
543 return &root->lt_style_skewed_time;
544 })
545 .with_children(style_config_handlers),
546 yajlpp::property_handler("offset-time")
547 .with_description("Styling for hidden fields")
__anon470316651402(const yajlpp_provider_context &ypc, lnav_theme *root) 548 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
549 return &root->lt_style_offset_time;
550 })
551 .with_children(style_config_handlers),
552 yajlpp::property_handler("invalid-msg")
553 .with_description("Styling for invalid log messages")
__anon470316651502(const yajlpp_provider_context &ypc, lnav_theme *root) 554 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
555 return &root->lt_style_invalid_msg;
556 })
557 .with_children(style_config_handlers),
558 yajlpp::property_handler("popup")
559 .with_description("Styling for popup windows")
__anon470316651602(const yajlpp_provider_context &ypc, lnav_theme *root) 560 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
561 return &root->lt_style_popup;
562 })
563 .with_children(style_config_handlers),
564 yajlpp::property_handler("focused")
565 .with_description("Styling for a focused row in a list view")
__anon470316651702(const yajlpp_provider_context &ypc, lnav_theme *root) 566 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
567 return &root->lt_style_focused;
568 })
569 .with_children(style_config_handlers),
570 yajlpp::property_handler("disabled-focused")
571 .with_description("Styling for a disabled focused row in a list view")
__anon470316651802(const yajlpp_provider_context &ypc, lnav_theme *root) 572 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
573 return &root->lt_style_disabled_focused;
574 })
575 .with_children(style_config_handlers),
576 yajlpp::property_handler("scrollbar")
577 .with_description("Styling for scrollbars")
__anon470316651902(const yajlpp_provider_context &ypc, lnav_theme *root) 578 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
579 return &root->lt_style_scrollbar;
580 })
581 .with_children(style_config_handlers)
582 };
583
584 static struct json_path_container theme_syntax_styles_handlers = {
585 yajlpp::property_handler("keyword")
586 .with_description("Styling for keywords in source files")
__anon470316651a02(const yajlpp_provider_context &ypc, lnav_theme *root) 587 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
588 return &root->lt_style_keyword;
589 })
590 .with_children(style_config_handlers),
591 yajlpp::property_handler("string")
592 .with_description("Styling for single/double-quoted strings in text")
__anon470316651b02(const yajlpp_provider_context &ypc, lnav_theme *root) 593 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
594 return &root->lt_style_string;
595 })
596 .with_children(style_config_handlers),
597 yajlpp::property_handler("comment")
598 .with_description("Styling for comments in source files")
__anon470316651c02(const yajlpp_provider_context &ypc, lnav_theme *root) 599 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
600 return &root->lt_style_comment;
601 })
602 .with_children(style_config_handlers),
603 yajlpp::property_handler("doc-directive")
604 .with_description("Styling for documentation directives in source files")
__anon470316651d02(const yajlpp_provider_context &ypc, lnav_theme *root) 605 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
606 return &root->lt_style_doc_directive;
607 })
608 .with_children(style_config_handlers),
609 yajlpp::property_handler("variable")
610 .with_description("Styling for variables in text")
__anon470316651e02(const yajlpp_provider_context &ypc, lnav_theme *root) 611 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
612 return &root->lt_style_variable;
613 })
614 .with_children(style_config_handlers),
615 yajlpp::property_handler("symbol")
616 .with_description("Styling for symbols in source files")
__anon470316651f02(const yajlpp_provider_context &ypc, lnav_theme *root) 617 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
618 return &root->lt_style_symbol;
619 })
620 .with_children(style_config_handlers),
621 yajlpp::property_handler("number")
622 .with_description("Styling for numbers in source files")
__anon470316652002(const yajlpp_provider_context &ypc, lnav_theme *root) 623 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
624 return &root->lt_style_number;
625 })
626 .with_children(style_config_handlers),
627 yajlpp::property_handler("re-special")
628 .with_description("Styling for special characters in regular expressions")
__anon470316652102(const yajlpp_provider_context &ypc, lnav_theme *root) 629 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
630 return &root->lt_style_re_special;
631 })
632 .with_children(style_config_handlers),
633 yajlpp::property_handler("re-repeat")
634 .with_description("Styling for repeats in regular expressions")
__anon470316652202(const yajlpp_provider_context &ypc, lnav_theme *root) 635 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
636 return &root->lt_style_re_repeat;
637 })
638 .with_children(style_config_handlers),
639
640 yajlpp::property_handler("diff-delete")
641 .with_description("Styling for deleted lines in diffs")
__anon470316652302(const yajlpp_provider_context &ypc, lnav_theme *root) 642 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
643 return &root->lt_style_diff_delete;
644 })
645 .with_children(style_config_handlers),
646 yajlpp::property_handler("diff-add")
647 .with_description("Styling for added lines in diffs")
__anon470316652402(const yajlpp_provider_context &ypc, lnav_theme *root) 648 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
649 return &root->lt_style_diff_add;
650 })
651 .with_children(style_config_handlers),
652 yajlpp::property_handler("diff-section")
653 .with_description("Styling for diffs")
__anon470316652502(const yajlpp_provider_context &ypc, lnav_theme *root) 654 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
655 return &root->lt_style_diff_section;
656 })
657 .with_children(style_config_handlers),
658 yajlpp::property_handler("file")
659 .with_description("Styling for file names in source files")
__anon470316652602(const yajlpp_provider_context &ypc, lnav_theme *root) 660 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
661 return &root->lt_style_file;
662 })
663 .with_children(style_config_handlers)
664 };
665
666 static struct json_path_container theme_status_styles_handlers = {
667 yajlpp::property_handler("text")
668 .with_description("Styling for status bars")
__anon470316652702(const yajlpp_provider_context &ypc, lnav_theme *root) 669 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
670 return &root->lt_style_status;
671 })
672 .with_children(style_config_handlers),
673 yajlpp::property_handler("warn")
674 .with_description("Styling for warnings in status bars")
__anon470316652802(const yajlpp_provider_context &ypc, lnav_theme *root) 675 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
676 return &root->lt_style_warn_status;
677 })
678 .with_children(style_config_handlers),
679 yajlpp::property_handler("alert")
680 .with_description("Styling for alerts in status bars")
__anon470316652902(const yajlpp_provider_context &ypc, lnav_theme *root) 681 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
682 return &root->lt_style_alert_status;
683 })
684 .with_children(style_config_handlers),
685 yajlpp::property_handler("active")
686 .with_description("Styling for activity in status bars")
__anon470316652a02(const yajlpp_provider_context &ypc, lnav_theme *root) 687 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
688 return &root->lt_style_active_status;
689 })
690 .with_children(style_config_handlers),
691 yajlpp::property_handler("inactive-alert")
692 .with_description("Styling for inactive alert status bars")
__anon470316652b02(const yajlpp_provider_context &ypc, lnav_theme *root) 693 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
694 return &root->lt_style_inactive_alert_status;
695 })
696 .with_children(style_config_handlers),
697 yajlpp::property_handler("inactive")
698 .with_description("Styling for inactive status bars")
__anon470316652c02(const yajlpp_provider_context &ypc, lnav_theme *root) 699 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
700 return &root->lt_style_inactive_status;
701 })
702 .with_children(style_config_handlers),
703 yajlpp::property_handler("title-hotkey")
704 .with_description("Styling for hotkey highlights in titles")
__anon470316652d02(const yajlpp_provider_context &ypc, lnav_theme *root) 705 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
706 return &root->lt_style_status_title_hotkey;
707 })
708 .with_children(style_config_handlers),
709 yajlpp::property_handler("title")
710 .with_description("Styling for title sections of status bars")
__anon470316652e02(const yajlpp_provider_context &ypc, lnav_theme *root) 711 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
712 return &root->lt_style_status_title;
713 })
714 .with_children(style_config_handlers),
715 yajlpp::property_handler("disabled-title")
716 .with_description("Styling for title sections of status bars")
__anon470316652f02(const yajlpp_provider_context &ypc, lnav_theme *root) 717 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
718 return &root->lt_style_status_disabled_title;
719 })
720 .with_children(style_config_handlers),
721 yajlpp::property_handler("subtitle")
722 .with_description("Styling for subtitle sections of status bars")
__anon470316653002(const yajlpp_provider_context &ypc, lnav_theme *root) 723 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
724 return &root->lt_style_status_subtitle;
725 })
726 .with_children(style_config_handlers),
727 yajlpp::property_handler("hotkey")
728 .with_description("Styling for hotkey highlights of status bars")
__anon470316653102(const yajlpp_provider_context &ypc, lnav_theme *root) 729 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
730 return &root->lt_style_status_hotkey;
731 })
732 .with_children(style_config_handlers),
733 };
734
735 static struct json_path_container theme_log_level_styles_handlers = {
736 yajlpp::pattern_property_handler("(?<level>trace|debug5|debug4|debug3|debug2|debug|info|stats|notice|warning|error|critical|fatal|invalid)")
__anon470316653202(const yajlpp_provider_context &ypc, lnav_theme *root) 737 .with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
738 style_config &sc = root->lt_level_styles[
739 string2level(ypc.ypc_extractor.get_substr_i("level").get())];
740
741 return ≻
742 })
__anon470316653302(struct lnav_theme *cfg, vector<string> &paths_out) 743 .with_path_provider<lnav_theme>([](struct lnav_theme *cfg, vector<string> &paths_out) {
744 for (int lpc = LEVEL_TRACE; lpc < LEVEL__MAX; lpc++) {
745 paths_out.emplace_back(level_names[lpc]);
746 }
747 })
748 .with_children(style_config_handlers)
749 };
750
751 static struct json_path_container highlighter_handlers = {
752 yajlpp::property_handler("pattern")
753 .with_synopsis("regular expression")
754 .with_description("The regular expression to highlight")
755 .FOR_FIELD(highlighter_config, hc_regex),
756
757 yajlpp::property_handler("style")
758 .with_description("The styling for the text that matches the associated pattern")
__anon470316653402(const yajlpp_provider_context &ypc, highlighter_config *root) 759 .with_obj_provider<style_config, highlighter_config>([](const yajlpp_provider_context &ypc, highlighter_config *root) {
760 return &root->hc_style;
761 })
762 .with_children(style_config_handlers),
763 };
764
765 static struct json_path_container theme_highlights_handlers = {
766 yajlpp::pattern_property_handler("(?<highlight_name>\\w+)")
__anon470316653502(const yajlpp_provider_context &ypc, lnav_theme *root) 767 .with_obj_provider<highlighter_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
768 highlighter_config &hc = root->lt_highlights[
769 ypc.ypc_extractor.get_substr_i("highlight_name").get()];
770
771 return &hc;
772 })
__anon470316653602(struct lnav_theme *cfg, vector<string> &paths_out) 773 .with_path_provider<lnav_theme>([](struct lnav_theme *cfg, vector<string> &paths_out) {
774 for (const auto& pair : cfg->lt_highlights) {
775 paths_out.emplace_back(pair.first);
776 }
777 })
778 .with_children(highlighter_handlers)
779 };
780
781 static struct json_path_container theme_vars_handlers = {
782 yajlpp::pattern_property_handler("(?<var_name>\\w+)")
783 .with_synopsis("name")
784 .with_description("A theme variable definition")
__anon470316653702(struct lnav_theme *lt, vector<string> &paths_out) 785 .with_path_provider<lnav_theme>([](struct lnav_theme *lt, vector<string> &paths_out) {
786 for (const auto &iter : lt->lt_vars) {
787 paths_out.emplace_back(iter.first);
788 }
789 })
790 .FOR_FIELD(lnav_theme, lt_vars)
791 };
792
793 static struct json_path_container theme_def_handlers = {
794 yajlpp::property_handler("vars")
795 .with_description("Variables definitions that are used in this theme.")
796 .with_children(theme_vars_handlers),
797
798 yajlpp::property_handler("styles")
799 .with_description("Styles for log messages.")
800 .with_children(theme_styles_handlers),
801
802 yajlpp::property_handler("syntax-styles")
803 .with_description("Styles for syntax highlighting in text files.")
804 .with_children(theme_syntax_styles_handlers),
805
806 yajlpp::property_handler("status-styles")
807 .with_description("Styles for the user-interface components.")
808 .with_children(theme_status_styles_handlers),
809
810 yajlpp::property_handler("log-level-styles")
811 .with_description("Styles for each log message level.")
812 .with_children(theme_log_level_styles_handlers),
813
814 yajlpp::property_handler("highlights")
815 .with_description("Styles for text highlights.")
816 .with_children(theme_highlights_handlers),
817 };
818
819 static struct json_path_container theme_defs_handlers = {
820 yajlpp::pattern_property_handler("(?<theme_name>[\\w\\-]+)")
821 .with_description("Theme definitions")
__anon470316653802(const yajlpp_provider_context &ypc, _lnav_config *root) 822 .with_obj_provider<lnav_theme, _lnav_config>([](const yajlpp_provider_context &ypc, _lnav_config *root) {
823 lnav_theme < = root->lc_ui_theme_defs[ypc.ypc_extractor.get_substr("theme_name")];
824
825 return <
826 })
__anon470316653902(struct _lnav_config *cfg, vector<string> &paths_out) 827 .with_path_provider<_lnav_config>([](struct _lnav_config *cfg, vector<string> &paths_out) {
828 for (const auto &iter : cfg->lc_ui_theme_defs) {
829 paths_out.emplace_back(iter.first);
830 }
831 })
832 .with_children(theme_def_handlers)
833 };
834
835 static struct json_path_container ui_handlers = {
836 yajlpp::property_handler("clock-format")
837 .with_synopsis("format")
838 .with_description("The format for the clock displayed in "
839 "the top-left corner using strftime(3) conversions")
840 .with_example("%a %b %d %H:%M:%S %Z")
841 .FOR_FIELD(_lnav_config, lc_ui_clock_format),
842 yajlpp::property_handler("dim-text")
843 .with_synopsis("bool")
844 .with_description("Reduce the brightness of text (useful for xterms). "
845 "This setting can be useful when running in an xterm "
846 "where the white color is very bright.")
847 .FOR_FIELD(_lnav_config, lc_ui_dim_text),
848 yajlpp::property_handler("default-colors")
849 .with_synopsis("bool")
850 .with_description(
851 "Use default terminal background and foreground colors "
852 "instead of black and white for all text coloring. This setting "
853 "can be useful when transparent background or alternate color "
854 "theme terminal is used.")
855 .FOR_FIELD(_lnav_config, lc_ui_default_colors),
856 yajlpp::property_handler("keymap")
857 .with_synopsis("keymap_name")
858 .with_description("The name of the keymap to use.")
859 .FOR_FIELD(_lnav_config, lc_ui_keymap),
860 yajlpp::property_handler("theme")
861 .with_synopsis("theme_name")
862 .with_description("The name of the theme to use.")
863 .FOR_FIELD(_lnav_config, lc_ui_theme),
864 yajlpp::property_handler("theme-defs")
865 .with_description("Theme definitions.")
866 .with_children(theme_defs_handlers),
867 yajlpp::property_handler("keymap-defs")
868 .with_description("Keymap definitions.")
869 .with_children(keymap_defs_handlers),
870 };
871
872 static struct json_path_container archive_handlers = {
873 yajlpp::property_handler("min-free-space")
874 .with_synopsis("<bytes>")
875 .with_description(
876 "The minimum free space, in bytes, to maintain when unpacking "
877 "archives")
878 .with_min_value(0)
879 .for_field(&_lnav_config::lc_archive_manager,
880 &archive_manager::config::amc_min_free_space),
881 yajlpp::property_handler("cache-ttl")
882 .with_synopsis("<duration>")
883 .with_description(
884 "The time-to-live for unpacked archives, expressed as a duration "
885 "(e.g. '3d' for three days)")
886 .with_example("3d")
887 .with_example("12h")
888 .for_field(&_lnav_config::lc_archive_manager,
889 &archive_manager::config::amc_cache_ttl),
890 };
891
892 static struct json_path_container file_vtab_handlers = {
893 yajlpp::property_handler("max-content-size")
894 .with_synopsis("<bytes>")
895 .with_description(
896 "The maximum allowed file size for the content column")
897 .with_min_value(0)
898 .for_field(&_lnav_config::lc_file_vtab,
899 &file_vtab::config::fvc_max_content_size),
900 };
901
902 static struct json_path_container logfile_handlers = {
903 yajlpp::property_handler("max-unrecognized-lines")
904 .with_synopsis("<lines>")
905 .with_description(
906 "The maximum number of lines in a file to use when detecting the format")
907 .with_min_value(1)
908 .for_field(&_lnav_config::lc_logfile,
909 &lnav::logfile::config::lc_max_unrecognized_lines),
910 };
911
912 static struct json_path_container ssh_config_handlers = {
913 yajlpp::pattern_property_handler("(?<config_name>\\w+)")
914 .with_synopsis("name")
915 .with_description("Set an SSH configuration value")
916 .with_path_provider<_lnav_config>([](
__anon470316653a02( auto *m, std::vector<std::string> &paths_out) 917 auto *m, std::vector<std::string> &paths_out) {
918 for (const auto& pair : m->lc_tailer.c_ssh_config) {
919 paths_out.emplace_back(pair.first);
920 }
921 })
922 .for_field(&_lnav_config::lc_tailer,
923 &tailer::config::c_ssh_config),
924 };
925
926 static struct json_path_container ssh_option_handlers = {
927 yajlpp::pattern_property_handler("(?<option_name>\\w+)")
928 .with_synopsis("name")
929 .with_description("Set an option to be passed to the SSH command")
930 .for_field(&_lnav_config::lc_tailer,
931 &tailer::config::c_ssh_options),
932 };
933
934 static struct json_path_container ssh_handlers = {
935 yajlpp::property_handler("command")
936 .with_synopsis("ssh-command")
937 .with_description("The SSH command to execute")
938 .for_field(&_lnav_config::lc_tailer,
939 &tailer::config::c_ssh_cmd),
940 yajlpp::property_handler("transfer-command")
941 .with_synopsis("command")
942 .with_description(
943 "Command executed on the remote host when transferring the file")
944 .for_field(&_lnav_config::lc_tailer,
945 &tailer::config::c_transfer_cmd),
946 yajlpp::property_handler("start-command")
947 .with_synopsis("command")
948 .with_description(
949 "Command executed on the remote host to start the tailer")
950 .for_field(&_lnav_config::lc_tailer,
951 &tailer::config::c_start_cmd),
952 yajlpp::property_handler("flags")
953 .with_description("The flags to pass to the SSH command")
954 .for_field(&_lnav_config::lc_tailer,
955 &tailer::config::c_ssh_flags),
956 yajlpp::property_handler("options")
957 .with_description("The options to pass to the SSH command")
958 .with_children(ssh_option_handlers),
959 yajlpp::property_handler("config")
960 .with_description(
961 "The ssh_config options to pass to SSH with the -o option")
962 .with_children(ssh_config_handlers),
963 };
964
965 static struct json_path_container remote_handlers = {
966 yajlpp::property_handler("cache-ttl")
967 .with_synopsis("<duration>")
968 .with_description(
969 "The time-to-live for files copied from remote hosts, expressed as a duration "
970 "(e.g. '3d' for three days)")
971 .with_example("3d")
972 .with_example("12h")
973 .for_field(&_lnav_config::lc_tailer,
974 &tailer::config::c_cache_ttl),
975 yajlpp::property_handler("ssh")
976 .with_description(
977 "Settings related to the ssh command used to contact remote "
978 "machines")
979 .with_children(ssh_handlers),
980 };
981
982 static struct json_path_container sysclip_impl_cmd_handlers = json_path_container{
983 yajlpp::property_handler("write")
984 .with_synopsis("<command>")
985 .with_description("The command used to write to the clipboard")
986 .with_example("pbcopy")
987 .for_field(&sysclip::clip_commands::cc_write),
988 yajlpp::property_handler("read")
989 .with_synopsis("<command>")
990 .with_description("The command used to read from the clipboard")
991 .with_example("pbpaste")
992 .for_field(&sysclip::clip_commands::cc_read),
993 }
994 .with_description("Container for the commands used to read from and write to the system clipboard")
995 .with_definition_id("clip-commands");
996
997 static struct json_path_container sysclip_impl_handlers = {
998 yajlpp::property_handler("test")
999 .with_synopsis("<command>")
1000 .with_description("The command that checks")
1001 .with_example("command -v pbcopy")
1002 .for_field(&sysclip::clipboard::c_test_command),
1003 yajlpp::property_handler("general")
1004 .with_description("Commands to work with the general clipboard")
__anon470316653b02(const yajlpp_provider_context &ypc, sysclip::clipboard *root) 1005 .with_obj_provider<sysclip::clip_commands, sysclip::clipboard>([](const yajlpp_provider_context &ypc, sysclip::clipboard *root) {
1006 return &root->c_general;
1007 })
1008 .with_children(sysclip_impl_cmd_handlers),
1009 yajlpp::property_handler("find")
1010 .with_description("Commands to work with the find clipboard")
__anon470316653c02(const yajlpp_provider_context &ypc, sysclip::clipboard *root) 1011 .with_obj_provider<sysclip::clip_commands, sysclip::clipboard>([](const yajlpp_provider_context &ypc, sysclip::clipboard *root) {
1012 return &root->c_find;
1013 })
1014 .with_children(sysclip_impl_cmd_handlers),
1015 };
1016
1017 static struct json_path_container sysclip_impls_handlers = {
1018 yajlpp::pattern_property_handler("(?<clipboard_impl_name>[\\w\\-]+)")
1019 .with_synopsis("<name>")
1020 .with_description("Clipboard implementation")
__anon470316653d02(const yajlpp_provider_context &ypc, _lnav_config *root) 1021 .with_obj_provider<sysclip::clipboard, _lnav_config>([](const yajlpp_provider_context &ypc, _lnav_config *root) {
1022 auto &retval = root->lc_sysclip.c_clipboard_impls[ypc.ypc_extractor.get_substr("clipboard_impl_name")];
1023 return &retval;
1024 })
__anon470316653e02(struct _lnav_config *cfg, vector<string> &paths_out) 1025 .with_path_provider<_lnav_config>([](struct _lnav_config *cfg, vector<string> &paths_out) {
1026 for (const auto &iter : cfg->lc_sysclip.c_clipboard_impls) {
1027 paths_out.emplace_back(iter.first);
1028 }
1029 })
1030 .with_children(sysclip_impl_handlers),
1031 };
1032
1033 static struct json_path_container sysclip_handlers = {
1034 yajlpp::property_handler("impls")
1035 .with_description("Clipboard implementations")
1036 .with_children(sysclip_impls_handlers),
1037 };
1038
1039 static struct json_path_container tuning_handlers = {
1040 yajlpp::property_handler("archive-manager")
1041 .with_description("Settings related to opening archive files")
1042 .with_children(archive_handlers),
1043 yajlpp::property_handler("file-vtab")
1044 .with_description("Settings related to the lnav_file virtual-table")
1045 .with_children(file_vtab_handlers),
1046 yajlpp::property_handler("logfile")
1047 .with_description("Settings related to log files")
1048 .with_children(logfile_handlers),
1049 yajlpp::property_handler("remote")
1050 .with_description("Settings related to remote file support")
1051 .with_children(remote_handlers),
1052 yajlpp::property_handler("clipboard")
1053 .with_description("Settings related to the clipboard")
1054 .with_children(sysclip_handlers),
1055 };
1056
1057 static set<string> SUPPORTED_CONFIG_SCHEMAS = {
1058 "https://lnav.org/schemas/config-v1.schema.json",
1059 };
1060
1061 const char *DEFAULT_FORMAT_SCHEMA =
1062 "https://lnav.org/schemas/format-v1.schema.json";
1063
1064 set<string> SUPPORTED_FORMAT_SCHEMAS = {
1065 DEFAULT_FORMAT_SCHEMA,
1066 };
1067
read_id(yajlpp_parse_context * ypc,const unsigned char * str,size_t len)1068 static int read_id(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
1069 {
1070 auto file_id = string((const char *) str, len);
1071
1072 if (SUPPORTED_CONFIG_SCHEMAS.count(file_id) == 0) {
1073 ypc->report_error(
1074 lnav_log_level_t::ERROR,
1075 "%s:%d: error: unsupported configuration $schema -- %s\n",
1076 ypc->ypc_source.c_str(),
1077 ypc->get_line_number(),
1078 file_id.c_str());
1079 return 0;
1080 }
1081
1082 return 1;
1083 }
1084
1085 struct json_path_container lnav_config_handlers = json_path_container {
1086 json_path_handler("$schema", read_id)
1087 .with_synopsis("The URI of the schema for this file")
1088 .with_description("Specifies the type of this file"),
1089
1090 yajlpp::property_handler("tuning")
1091 .with_description("Internal settings")
1092 .with_children(tuning_handlers),
1093
1094 yajlpp::property_handler("ui")
1095 .with_description("User-interface settings")
1096 .with_children(ui_handlers),
1097
1098 yajlpp::property_handler("global")
1099 .with_description("Global variable definitions")
1100 .with_children(global_var_handlers)
1101 }
1102 .with_schema_id(*SUPPORTED_CONFIG_SCHEMAS.cbegin());
1103
1104 class active_key_map_listener : public lnav_config_listener {
1105 public:
reload_config(error_reporter & reporter)1106 void reload_config(error_reporter &reporter) override
1107 {
1108 lnav_config.lc_active_keymap = lnav_config.lc_ui_keymaps["default"];
1109 for (const auto &pair :
1110 lnav_config.lc_ui_keymaps[lnav_config.lc_ui_keymap].km_seq_to_cmd) {
1111 lnav_config.lc_active_keymap.km_seq_to_cmd[pair.first] = pair.second;
1112 }
1113 }
1114 };
1115
1116 static active_key_map_listener KEYMAP_LISTENER;
1117
1118 Result<config_file_type, std::string>
detect_config_file_type(const ghc::filesystem::path & path)1119 detect_config_file_type(const ghc::filesystem::path &path)
1120 {
1121 static const char *id_path[] = {"$schema", nullptr};
1122
1123 auto read_res = read_file(path);
1124
1125 if (read_res.isErr()) {
1126 return Err(fmt::format("unable to open file: {} -- {}",
1127 path.string(), read_res.unwrapErr()));
1128 }
1129
1130 auto content = read_res.unwrap();
1131 if (startswith(content, "#")) {
1132 content.insert(0, "//");
1133 }
1134
1135 char error_buffer[1024];
1136 auto content_tree = unique_ptr<yajl_val_s, decltype(&yajl_tree_free)>(
1137 yajl_tree_parse(content.c_str(), error_buffer, sizeof(error_buffer)),
1138 yajl_tree_free);
1139 if (content_tree == nullptr) {
1140 return Err(fmt::format("unable to parse file: {} -- {}",
1141 path.string(), error_buffer));
1142 }
1143
1144 auto id_val = yajl_tree_get(content_tree.get(), id_path, yajl_t_string);
1145 if (id_val != nullptr) {
1146 if (SUPPORTED_CONFIG_SCHEMAS.count(id_val->u.string)) {
1147 return Ok(config_file_type::CONFIG);
1148 } else if (SUPPORTED_FORMAT_SCHEMAS.count(id_val->u.string)) {
1149 return Ok(config_file_type::FORMAT);
1150 } else {
1151 return Err(fmt::format("unsupported configuration version in file: {} -- {}",
1152 path.string(), id_val->u.string));
1153 }
1154 } else {
1155 return Ok(config_file_type::FORMAT);
1156 }
1157 }
1158
load_config_from(_lnav_config & lconfig,const ghc::filesystem::path & path,vector<string> & errors)1159 static void load_config_from(_lnav_config& lconfig, const ghc::filesystem::path &path, vector<string> &errors)
1160 {
1161 yajlpp_parse_context ypc(path.string(), &lnav_config_handlers);
1162 struct userdata ud(errors);
1163 auto_fd fd;
1164
1165 ypc.ypc_locations = &lnav_config_locations;
1166 ypc.with_obj(lconfig);
1167 ypc.ypc_userdata = &ud;
1168 ypc.with_error_reporter(config_error_reporter);
1169 if ((fd = openp(path, O_RDONLY)) == -1) {
1170 if (errno != ENOENT) {
1171 char errmsg[1024];
1172
1173 snprintf(errmsg, sizeof(errmsg),
1174 "error: unable to open format file -- %s",
1175 path.c_str());
1176 errors.emplace_back(errmsg);
1177 }
1178 }
1179 else {
1180 auto_mem<yajl_handle_t> handle(yajl_free);
1181 char buffer[2048];
1182 off_t offset = 0;
1183 ssize_t rc = -1;
1184
1185 handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
1186 yajl_config(handle, yajl_allow_comments, 1);
1187 yajl_config(handle, yajl_allow_multiple_values, 1);
1188 ypc.ypc_handle = handle;
1189 while (true) {
1190 rc = read(fd, buffer, sizeof(buffer));
1191 if (rc == 0) {
1192 break;
1193 }
1194 else if (rc == -1) {
1195 errors.push_back(path.string() +
1196 ":unable to read file -- " +
1197 string(strerror(errno)));
1198 break;
1199 }
1200 if (ypc.parse((const unsigned char *)buffer, rc) != yajl_status_ok) {
1201 break;
1202 }
1203 offset += rc;
1204 }
1205 if (rc == 0) {
1206 ypc.complete_parse();
1207 }
1208 }
1209 }
1210
load_default_config(struct _lnav_config & config_obj,const std::string & path,const bin_src_file & bsf,vector<string> & errors)1211 static void load_default_config(struct _lnav_config &config_obj,
1212 const std::string &path,
1213 const bin_src_file &bsf,
1214 vector<string> &errors)
1215 {
1216 yajlpp_parse_context ypc_builtin(bsf.get_name(), &lnav_config_handlers);
1217 auto_mem<yajl_handle_t> handle(yajl_free);
1218 struct userdata ud(errors);
1219
1220 handle = yajl_alloc(&ypc_builtin.ypc_callbacks, nullptr, &ypc_builtin);
1221 ypc_builtin.ypc_locations = &lnav_config_locations;
1222 ypc_builtin.with_handle(handle);
1223 ypc_builtin.with_obj(config_obj);
1224 ypc_builtin.with_error_reporter(config_error_reporter);
1225 ypc_builtin.ypc_userdata = &ud;
1226
1227 if (path != "*") {
1228 ypc_builtin.ypc_ignore_unused = true;
1229 ypc_builtin.ypc_active_paths.insert(path);
1230 }
1231
1232 yajl_config(handle, yajl_allow_comments, 1);
1233 yajl_config(handle, yajl_allow_multiple_values, 1);
1234 if (ypc_builtin.parse(bsf.to_string_fragment()) == yajl_status_ok) {
1235 ypc_builtin.complete_parse();
1236 }
1237 }
1238
load_default_configs(struct _lnav_config & config_obj,const std::string & path,vector<string> & errors)1239 static void load_default_configs(struct _lnav_config &config_obj,
1240 const std::string &path,
1241 vector<string> &errors)
1242 {
1243 for (auto& bsf : lnav_config_json) {
1244 load_default_config(config_obj, path, bsf, errors);
1245 }
1246 }
1247
load_config(const vector<ghc::filesystem::path> & extra_paths,vector<string> & errors)1248 void load_config(const vector<ghc::filesystem::path> &extra_paths, vector<string> &errors)
1249 {
1250 auto user_config = lnav::paths::dotlnav() / "config.json";
1251
1252 for (auto& bsf : lnav_config_json) {
1253 auto sample_path = lnav::paths::dotlnav() /
1254 "configs" /
1255 "default" /
1256 fmt::format("{}.sample", bsf.get_name());
1257
1258 auto fd = auto_fd(openp(sample_path, O_WRONLY|O_TRUNC|O_CREAT, 0644));
1259 auto sf = bsf.to_string_fragment();
1260 if (fd == -1 || write(fd.get(), sf.data(), sf.length()) == -1) {
1261 fprintf(stderr,
1262 "error:unable to write default config file: %s -- %s\n",
1263 sample_path.c_str(),
1264 strerror(errno));
1265 }
1266 }
1267
1268 {
1269 load_default_configs(lnav_default_config, "*", errors);
1270 load_default_configs(lnav_config, "*", errors);
1271
1272 for (const auto &extra_path : extra_paths) {
1273 auto config_path = extra_path / "configs/*/*.json";
1274 static_root_mem<glob_t, globfree> gl;
1275
1276 if (glob(config_path.c_str(), 0, nullptr, gl.inout()) == 0) {
1277 for (size_t lpc = 0; lpc < gl->gl_pathc; lpc++) {
1278 load_config_from(lnav_config, gl->gl_pathv[lpc], errors);
1279 if (errors.empty()) {
1280 load_config_from(lnav_default_config, gl->gl_pathv[lpc], errors);
1281 }
1282 }
1283 }
1284 }
1285 for (const auto &extra_path : extra_paths) {
1286 auto config_path = extra_path / "formats/*/config.*.json";
1287 static_root_mem<glob_t, globfree> gl;
1288
1289 if (glob(config_path.c_str(), 0, nullptr, gl.inout()) == 0) {
1290 for (size_t lpc = 0; lpc < gl->gl_pathc; lpc++) {
1291 load_config_from(lnav_config, gl->gl_pathv[lpc], errors);
1292 if (errors.empty()) {
1293 load_config_from(lnav_default_config, gl->gl_pathv[lpc], errors);
1294 }
1295 }
1296 }
1297 }
1298
1299 load_config_from(lnav_config, user_config, errors);
1300 }
1301
1302 reload_config(errors);
1303
1304 rollback_lnav_config = lnav_config;
1305 }
1306
reset_config(const std::string & path)1307 void reset_config(const std::string &path)
1308 {
1309 vector<string> errors;
1310
1311 load_default_configs(lnav_config, path, errors);
1312
1313 reload_config(errors);
1314
1315 for (auto &err: errors) {
1316 log_debug("reset %s", err.c_str());
1317 }
1318 }
1319
save_config()1320 string save_config()
1321 {
1322 yajlpp_gen gen;
1323 auto filename = fmt::format("config.json.{}.tmp", getpid());
1324 auto user_config_tmp = lnav::paths::dotlnav() / filename;
1325 auto user_config = lnav::paths::dotlnav() / "config.json";
1326
1327 yajl_gen_config(gen, yajl_gen_beautify, true);
1328 yajlpp_gen_context ygc(gen, lnav_config_handlers);
1329 vector<string> errors;
1330
1331 ygc.with_default_obj(lnav_default_config)
1332 .with_obj(lnav_config);
1333 ygc.gen();
1334
1335 {
1336 auto_fd fd;
1337
1338 if ((fd = openp(user_config_tmp,
1339 O_WRONLY | O_CREAT | O_TRUNC, 0600)) == -1) {
1340 return "error: unable to save configuration -- " +
1341 string(strerror(errno));
1342 } else {
1343 string_fragment bits = gen.to_string_fragment();
1344
1345 log_perror(write(fd, bits.data(), bits.length()));
1346 }
1347 }
1348
1349 rename(user_config_tmp.c_str(), user_config.c_str());
1350
1351 return "info: configuration saved";
1352 }
1353
reload_config(vector<string> & errors)1354 void reload_config(vector<string> &errors)
1355 {
1356 lnav_config_listener *curr = lnav_config_listener::LISTENER_LIST;
1357
1358 while (curr != nullptr) {
1359 auto reporter = [&errors](const void *cfg_value, const std::string &errmsg) {
1360 auto cb = [&cfg_value, &errors, &errmsg](
1361 const json_path_handler_base &jph,
1362 const string &path,
1363 void *mem) {
1364 if (mem != cfg_value) {
1365 return;
1366 }
1367
1368 auto loc_iter = lnav_config_locations.find(intern_string::lookup(path));
1369 if (loc_iter == lnav_config_locations.end()) {
1370 return;
1371 }
1372
1373 char msg[1024];
1374
1375 snprintf(msg, sizeof(msg),
1376 "%s:%d:%s",
1377 loc_iter->second.sl_source.get(),
1378 loc_iter->second.sl_line_number,
1379 errmsg.c_str());
1380
1381 errors.emplace_back(msg);
1382 };
1383
1384 for (auto &jph : lnav_config_handlers.jpc_children) {
1385 jph.walk(cb, &lnav_config);
1386 }
1387 };
1388
1389 curr->reload_config(reporter);
1390 curr = curr->lcl_next;
1391 }
1392 }
1393