2013-06-06 14:01:32 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2013, Timothy Stack
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* * Redistributions of source code must retain the above copyright notice, this
|
|
|
|
* list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
* * Neither the name of Timothy Stack nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
|
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* @file lnav_config.cc
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2013-06-06 14:10:13 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2014-03-02 00:35:30 +00:00
|
|
|
#include <glob.h>
|
2013-06-06 14:01:32 +00:00
|
|
|
#include <sys/stat.h>
|
2015-08-10 04:03:23 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
2018-11-09 17:45:19 +00:00
|
|
|
#include <libgen.h>
|
|
|
|
|
|
|
|
#include <iostream>
|
2019-05-03 20:50:19 +00:00
|
|
|
#include <stdexcept>
|
2015-08-10 04:03:23 +00:00
|
|
|
|
|
|
|
#include "pcrecpp.h"
|
2013-06-06 14:01:32 +00:00
|
|
|
|
2015-09-15 07:24:56 +00:00
|
|
|
#include "auto_fd.hh"
|
2014-03-02 00:35:30 +00:00
|
|
|
#include "lnav_log.hh"
|
2019-05-03 20:50:19 +00:00
|
|
|
#include "lnav_util.hh"
|
2014-03-02 00:35:30 +00:00
|
|
|
#include "auto_mem.hh"
|
2015-08-10 04:03:23 +00:00
|
|
|
#include "auto_pid.hh"
|
2013-06-06 14:01:32 +00:00
|
|
|
#include "lnav_config.hh"
|
2015-08-10 04:03:23 +00:00
|
|
|
#include "yajlpp.hh"
|
2018-05-25 13:32:01 +00:00
|
|
|
#include "yajlpp_def.hh"
|
2018-05-10 13:44:03 +00:00
|
|
|
#include "shlex.hh"
|
2019-05-03 20:50:19 +00:00
|
|
|
#include "styling.hh"
|
2013-06-06 14:01:32 +00:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
2014-03-02 00:35:30 +00:00
|
|
|
static const int MAX_CRASH_LOG_COUNT = 16;
|
|
|
|
|
2016-01-05 14:18:58 +00:00
|
|
|
extern "C" {
|
|
|
|
extern const char default_config_json[];
|
2017-01-21 15:41:28 +00:00
|
|
|
extern const char keymap_default_json[];
|
2016-01-05 14:18:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct _lnav_config lnav_config;
|
2019-05-03 20:50:19 +00:00
|
|
|
struct _lnav_config rollback_lnav_config;
|
2016-01-05 14:18:58 +00:00
|
|
|
static struct _lnav_config lnav_default_config;
|
|
|
|
|
2019-05-03 20:50:19 +00:00
|
|
|
std::map<intern_string_t, source_location> lnav_config_locations;
|
|
|
|
|
2016-05-02 03:35:37 +00:00
|
|
|
lnav_config_listener *lnav_config_listener::LISTENER_LIST;
|
|
|
|
|
2013-06-06 14:01:32 +00:00
|
|
|
string dotlnav_path(const char *sub)
|
|
|
|
{
|
|
|
|
string retval;
|
|
|
|
char * home;
|
|
|
|
|
|
|
|
home = getenv("HOME");
|
2015-09-20 04:13:46 +00:00
|
|
|
if (home && access(home, W_OK|X_OK) == 0) {
|
2013-06-06 14:13:00 +00:00
|
|
|
char hpath[2048];
|
2013-06-06 14:01:32 +00:00
|
|
|
|
|
|
|
snprintf(hpath, sizeof(hpath), "%s/.lnav/%s", home, sub);
|
|
|
|
retval = hpath;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
retval = sub;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool check_experimental(const char *feature_name)
|
|
|
|
{
|
|
|
|
const char *env_value = getenv("LNAV_EXP");
|
|
|
|
|
2014-03-06 14:58:49 +00:00
|
|
|
require(feature_name != NULL);
|
2013-06-06 14:01:32 +00:00
|
|
|
|
|
|
|
if (env_value && strcasestr(env_value, feature_name)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ensure_dotlnav(void)
|
|
|
|
{
|
|
|
|
string path = dotlnav_path("");
|
|
|
|
|
|
|
|
if (!path.empty()) {
|
2015-09-18 03:55:31 +00:00
|
|
|
log_perror(mkdir(path.c_str(), 0755));
|
2013-06-06 14:01:32 +00:00
|
|
|
}
|
2013-07-23 12:55:08 +00:00
|
|
|
|
|
|
|
path = dotlnav_path("formats");
|
|
|
|
if (!path.empty()) {
|
2015-09-18 03:55:31 +00:00
|
|
|
log_perror(mkdir(path.c_str(), 0755));
|
2013-07-23 12:55:08 +00:00
|
|
|
}
|
2014-11-05 17:01:09 +00:00
|
|
|
|
|
|
|
path = dotlnav_path("formats/installed");
|
|
|
|
if (!path.empty()) {
|
2015-09-18 03:55:31 +00:00
|
|
|
log_perror(mkdir(path.c_str(), 0755));
|
2014-11-05 17:01:09 +00:00
|
|
|
}
|
|
|
|
|
2014-03-02 00:35:30 +00:00
|
|
|
path = dotlnav_path("crash");
|
|
|
|
if (!path.empty()) {
|
2015-09-18 03:55:31 +00:00
|
|
|
log_perror(mkdir(path.c_str(), 0755));
|
2014-03-02 00:35:30 +00:00
|
|
|
}
|
|
|
|
lnav_log_crash_dir = strdup(path.c_str());
|
|
|
|
|
|
|
|
{
|
|
|
|
static_root_mem<glob_t, globfree> gl;
|
|
|
|
|
|
|
|
path += "/*";
|
|
|
|
if (glob(path.c_str(), GLOB_NOCHECK, NULL, gl.inout()) == 0) {
|
|
|
|
for (int lpc = 0;
|
2014-03-02 07:40:12 +00:00
|
|
|
lpc < ((int)gl->gl_pathc - MAX_CRASH_LOG_COUNT);
|
2014-03-02 00:35:30 +00:00
|
|
|
lpc++) {
|
2015-09-18 03:55:31 +00:00
|
|
|
log_perror(remove(gl->gl_pathv[lpc]));
|
2014-03-02 00:35:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-29 04:22:04 +00:00
|
|
|
path = dotlnav_path("formats/default");
|
|
|
|
if (!path.empty()) {
|
2015-09-18 03:55:31 +00:00
|
|
|
log_perror(mkdir(path.c_str(), 0755));
|
2013-08-29 04:22:04 +00:00
|
|
|
}
|
2013-06-06 14:01:32 +00:00
|
|
|
}
|
2015-08-10 04:03:23 +00:00
|
|
|
|
|
|
|
void install_git_format(const char *repo)
|
|
|
|
{
|
|
|
|
static pcrecpp::RE repo_name_converter("[^\\w]");
|
|
|
|
|
|
|
|
auto_pid git_cmd(fork());
|
|
|
|
|
|
|
|
if (git_cmd.in_child()) {
|
|
|
|
string formats_path = dotlnav_path("formats/");
|
|
|
|
string local_name = repo;
|
|
|
|
string local_path;
|
|
|
|
|
|
|
|
repo_name_converter.GlobalReplace("_", &local_name);
|
|
|
|
local_path = formats_path + local_name;
|
|
|
|
if (access(local_path.c_str(), R_OK) == 0) {
|
|
|
|
printf("Updating format repo: %s\n", repo);
|
2016-05-04 04:01:39 +00:00
|
|
|
log_perror(chdir(local_path.c_str()));
|
2015-08-10 04:03:23 +00:00
|
|
|
execlp("git", "git", "pull", NULL);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
execlp("git", "git", "clone", repo, local_path.c_str(), NULL);
|
|
|
|
}
|
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
git_cmd.wait_for_child();
|
|
|
|
}
|
|
|
|
|
2018-11-09 17:45:19 +00:00
|
|
|
bool update_git_formats()
|
|
|
|
{
|
|
|
|
static_root_mem<glob_t, globfree> gl;
|
|
|
|
string formats_path = dotlnav_path("formats/");
|
|
|
|
string git_formats = formats_path + "*/.git";
|
|
|
|
bool found = false, retval = true;
|
|
|
|
|
|
|
|
if (glob(git_formats.c_str(), GLOB_NOCHECK, NULL, gl.inout()) == 0) {
|
|
|
|
for (int lpc = 0; lpc < (int) gl->gl_pathc; lpc++) {
|
|
|
|
char *git_dir = dirname(gl->gl_pathv[lpc]);
|
|
|
|
char pull_cmd[1024];
|
|
|
|
|
|
|
|
printf("Updating formats in %s\n", git_dir);
|
|
|
|
snprintf(pull_cmd, sizeof(pull_cmd),
|
|
|
|
"cd %s && git pull",
|
|
|
|
git_dir);
|
|
|
|
int ret = system(pull_cmd);
|
|
|
|
if (ret == -1) {
|
|
|
|
std::cerr << "Failed to spawn command "
|
|
|
|
<< "\"" << pull_cmd << "\": "
|
|
|
|
<< strerror(errno) << std::endl;
|
|
|
|
retval = false;
|
|
|
|
}
|
|
|
|
else if (ret > 0) {
|
|
|
|
std::cerr << "Command "
|
|
|
|
<< "\"" << pull_cmd << "\" failed: "
|
|
|
|
<< strerror(errno) << std::endl;
|
|
|
|
retval = false;
|
|
|
|
}
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
printf("No formats from git repositories found, "
|
|
|
|
"use 'lnav -i extra' to install third-party foramts\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2015-08-10 04:03:23 +00:00
|
|
|
static int read_repo_path(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
|
|
|
|
{
|
|
|
|
string path = string((const char *)str, len);
|
|
|
|
|
|
|
|
install_git_format(path.c_str());
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct json_path_handler format_handlers[] = {
|
2016-01-05 14:18:58 +00:00
|
|
|
json_path_handler("/format-repos#", read_repo_path),
|
2015-08-10 04:03:23 +00:00
|
|
|
|
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
|
|
|
void install_extra_formats()
|
|
|
|
{
|
|
|
|
string config_root = dotlnav_path("remote-config");
|
2015-09-15 07:24:56 +00:00
|
|
|
auto_fd fd;
|
2015-08-10 04:03:23 +00:00
|
|
|
|
|
|
|
if (access(config_root.c_str(), R_OK) == 0) {
|
|
|
|
char pull_cmd[1024];
|
|
|
|
|
|
|
|
printf("Updating lnav remote config repo...\n");
|
|
|
|
snprintf(pull_cmd, sizeof(pull_cmd),
|
|
|
|
"cd '%s' && git pull",
|
|
|
|
config_root.c_str());
|
2016-05-04 04:01:39 +00:00
|
|
|
log_perror(system(pull_cmd));
|
2015-08-10 04:03:23 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
char clone_cmd[1024];
|
|
|
|
|
|
|
|
printf("Cloning lnav remote config repo...\n");
|
|
|
|
snprintf(clone_cmd, sizeof(clone_cmd),
|
|
|
|
"git clone https://github.com/tstack/lnav-config.git %s",
|
|
|
|
config_root.c_str());
|
2016-05-04 04:01:39 +00:00
|
|
|
log_perror(system(clone_cmd));
|
2015-08-10 04:03:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
string config_json = config_root + "/remote-config.json";
|
|
|
|
if ((fd = open(config_json.c_str(), O_RDONLY)) == -1) {
|
|
|
|
perror("Unable to open remote-config.json");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
yajlpp_parse_context ypc_config(config_root, format_handlers);
|
|
|
|
auto_mem<yajl_handle_t> jhandle(yajl_free);
|
|
|
|
unsigned char buffer[4096];
|
|
|
|
ssize_t rc;
|
|
|
|
|
|
|
|
jhandle = yajl_alloc(&ypc_config.ypc_callbacks, NULL, &ypc_config);
|
|
|
|
yajl_config(jhandle, yajl_allow_comments, 1);
|
|
|
|
while ((rc = read(fd, buffer, sizeof(buffer))) > 0) {
|
|
|
|
if (yajl_parse(jhandle,
|
|
|
|
buffer,
|
|
|
|
rc) != yajl_status_ok) {
|
|
|
|
fprintf(stderr, "Unable to parse remote-config.json -- %s",
|
|
|
|
yajl_get_error(jhandle, 1, buffer, rc));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2016-01-05 14:18:58 +00:00
|
|
|
if (yajl_complete_parse(jhandle) != yajl_status_ok) {
|
|
|
|
fprintf(stderr, "Unable to parse remote-config.json -- %s",
|
|
|
|
yajl_get_error(jhandle, 1, buffer, rc));
|
|
|
|
}
|
2015-08-10 04:03:23 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-05 14:18:58 +00:00
|
|
|
|
|
|
|
struct userdata {
|
|
|
|
userdata(vector<string> &errors) : ud_errors(errors) {};
|
|
|
|
|
|
|
|
vector<string> &ud_errors;
|
|
|
|
};
|
|
|
|
|
2019-05-03 20:50:19 +00:00
|
|
|
static void config_error_reporter(const yajlpp_parse_context &ypc,
|
|
|
|
lnav_log_level_t level,
|
|
|
|
const char *msg)
|
|
|
|
{
|
|
|
|
if (level >= LOG_LEVEL_ERROR) {
|
|
|
|
struct userdata *ud = (userdata *) ypc.ypc_userdata;
|
|
|
|
|
|
|
|
ud->ud_errors.emplace_back(msg);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "warning:%s\n", msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-21 15:41:28 +00:00
|
|
|
static struct json_path_handler keymap_def_handlers[] = {
|
2017-03-01 16:44:16 +00:00
|
|
|
json_path_handler("(?<key_seq>(x[0-9a-f]{2})+)#")
|
2017-01-21 15:41:28 +00:00
|
|
|
.with_synopsis("<command>")
|
|
|
|
.with_description("The command to execute for the given key sequence")
|
|
|
|
.with_pattern("[:|;].*")
|
|
|
|
.with_path_provider<key_map>([](key_map *km, vector<string> &paths_out) {
|
|
|
|
for (const auto &iter : km->km_seq_to_cmd) {
|
2019-05-03 20:50:19 +00:00
|
|
|
paths_out.emplace_back(iter.first);
|
2017-01-21 15:41:28 +00:00
|
|
|
}
|
|
|
|
})
|
2018-05-25 13:32:01 +00:00
|
|
|
.FOR_FIELD(key_map, km_seq_to_cmd),
|
2017-01-21 15:41:28 +00:00
|
|
|
|
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct json_path_handler keymap_defs_handlers[] = {
|
|
|
|
json_path_handler("(?<key_map_name>[^/]+)/")
|
|
|
|
.with_synopsis("<name>")
|
|
|
|
.with_description("The command to execute for the given key sequence")
|
|
|
|
.with_obj_provider<key_map, _lnav_config>([](const yajlpp_provider_context &ypc, _lnav_config *root) {
|
|
|
|
key_map &retval = root->lc_ui_keymaps[ypc.ypc_extractor.get_substr("key_map_name")];
|
|
|
|
return &retval;
|
|
|
|
})
|
|
|
|
.with_path_provider<_lnav_config>([](struct _lnav_config *cfg, vector<string> &paths_out) {
|
|
|
|
for (const auto &iter : cfg->lc_ui_keymaps) {
|
2019-05-03 20:50:19 +00:00
|
|
|
paths_out.emplace_back(iter.first);
|
2017-01-21 15:41:28 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.with_children(keymap_def_handlers),
|
|
|
|
|
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
2017-03-01 16:44:16 +00:00
|
|
|
static struct json_path_handler global_var_handlers[] = {
|
|
|
|
json_path_handler("(?<var_name>\\w+)")
|
|
|
|
.with_synopsis("<name>")
|
|
|
|
.with_description("A global variable definition")
|
|
|
|
.with_path_provider<_lnav_config>([](struct _lnav_config *cfg, vector<string> &paths_out) {
|
|
|
|
for (const auto &iter : cfg->lc_global_vars) {
|
2019-05-03 20:50:19 +00:00
|
|
|
paths_out.emplace_back(iter.first);
|
2017-03-01 16:44:16 +00:00
|
|
|
}
|
|
|
|
})
|
2018-05-25 13:32:01 +00:00
|
|
|
.FOR_FIELD(_lnav_config, lc_global_vars),
|
2017-03-01 18:46:26 +00:00
|
|
|
|
|
|
|
json_path_handler()
|
2017-03-01 16:44:16 +00:00
|
|
|
};
|
|
|
|
|
2017-01-21 15:41:28 +00:00
|
|
|
static struct json_path_handler root_config_handlers[] = {
|
|
|
|
json_path_handler("/keymap_def/")
|
|
|
|
.with_children(keymap_defs_handlers),
|
|
|
|
|
2017-03-01 16:44:16 +00:00
|
|
|
json_path_handler("/global/")
|
|
|
|
.with_children(global_var_handlers),
|
|
|
|
|
2017-01-21 15:41:28 +00:00
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
2019-05-03 20:50:19 +00:00
|
|
|
static struct json_path_handler style_config_handlers[] = {
|
|
|
|
json_path_handler("color")
|
|
|
|
.with_synopsis("#hex|color_name")
|
|
|
|
.with_description("Foreground color")
|
|
|
|
.FOR_FIELD(style_config, sc_color),
|
|
|
|
json_path_handler("background-color")
|
|
|
|
.with_synopsis("#hex|color_name")
|
|
|
|
.with_description("Background color")
|
|
|
|
.FOR_FIELD(style_config, sc_background_color),
|
|
|
|
json_path_handler("selected-color")
|
|
|
|
.with_synopsis("#hex|color_name")
|
|
|
|
.with_description("Background color when selected")
|
|
|
|
.FOR_FIELD(style_config, sc_selected_color),
|
|
|
|
json_path_handler("underline")
|
|
|
|
.with_description("Underline")
|
|
|
|
.FOR_FIELD(style_config, sc_underline),
|
|
|
|
json_path_handler("bold")
|
|
|
|
.with_description("Bold")
|
|
|
|
.FOR_FIELD(style_config, sc_bold),
|
|
|
|
|
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct json_path_handler theme_styles_handlers[] = {
|
|
|
|
json_path_handler("identifier/")
|
|
|
|
.with_description("Styling for identifiers in logs")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_identifier;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("text/")
|
|
|
|
.with_description("Styling for plain text")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_text;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("alt-text/")
|
|
|
|
.with_description("Styling for plain text when alternating")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_alt_text;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("error/")
|
|
|
|
.with_description("Styling for error messages")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_error;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("ok/")
|
|
|
|
.with_description("Styling for success messages")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_ok;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("warning/")
|
|
|
|
.with_description("Styling for warning messages")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_warning;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("hidden/")
|
|
|
|
.with_description("Styling for hidden fields in logs")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_hidden;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("adjusted-time/")
|
|
|
|
.with_description("Styling for timestamps that have been adjusted")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_adjusted_time;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("skewed-time/")
|
|
|
|
.with_description("Styling for timestamps ")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_skewed_time;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("offset-time/")
|
|
|
|
.with_description("Styling for hidden fields")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_offset_time;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("popup/")
|
|
|
|
.with_description("Styling for popup windows")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_popup;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
|
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct json_path_handler theme_syntax_styles_handlers[] = {
|
|
|
|
json_path_handler("keyword/")
|
|
|
|
.with_description("Styling for keywords in source files")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_keyword;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("string/")
|
|
|
|
.with_description("Styling for single/double-quoted strings in text")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_string;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("comment/")
|
|
|
|
.with_description("Styling for comments in source files")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_comment;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("variable/")
|
|
|
|
.with_description("Styling for variables in text")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_variable;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("symbol/")
|
|
|
|
.with_description("Styling for symbols in source files")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_symbol;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("number/")
|
|
|
|
.with_description("Styling for numbers in source files")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_number;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("re-special/")
|
|
|
|
.with_description("Styling for special characters in regular expressions")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_re_special;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("re-repeat/")
|
|
|
|
.with_description("Styling for repeats in regular expressions")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_re_repeat;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
|
|
|
|
json_path_handler("diff-delete/")
|
|
|
|
.with_description("Styling for deleted lines in diffs")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_diff_delete;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("diff-add/")
|
|
|
|
.with_description("Styling for added lines in diffs")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_diff_add;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("diff-section/")
|
|
|
|
.with_description("Styling for diffs")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_diff_section;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("file/")
|
|
|
|
.with_description("Styling for file names in source files")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_file;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
|
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct json_path_handler theme_status_styles_handlers[] = {
|
|
|
|
json_path_handler("text/")
|
|
|
|
.with_description("Styling for status bars")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_status;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("warn/")
|
|
|
|
.with_description("Styling for warnings in status bars")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_warn_status;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("alert/")
|
|
|
|
.with_description("Styling for alerts in status bars")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_alert_status;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("active/")
|
|
|
|
.with_description("Styling for activity in status bars")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_active_status;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("inactive/")
|
|
|
|
.with_description("Styling for inactive status bars")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_inactive_status;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("title/")
|
|
|
|
.with_description("Styling for title sections of status bars")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_status_title;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
json_path_handler("subtitle/")
|
|
|
|
.with_description("Styling for subtitle sections of status bars")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
return &root->lt_style_status_subtitle;
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
|
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct json_path_handler theme_log_level_styles_handlers[] = {
|
|
|
|
json_path_handler("(?<level>trace|debug5|debug4|debug3|debug2|debug|info|stats|notice|warning|error|critical|fatal)"
|
|
|
|
"/")
|
|
|
|
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
|
|
|
|
style_config &sc = root->lt_level_styles[
|
|
|
|
string2level(ypc.ypc_extractor.get_substr_i("level").get())];
|
|
|
|
|
|
|
|
return ≻
|
|
|
|
})
|
|
|
|
.with_path_provider<lnav_theme>([](struct lnav_theme *cfg, vector<string> &paths_out) {
|
|
|
|
for (int lpc = LEVEL_TRACE; lpc < LEVEL__MAX; lpc++) {
|
|
|
|
paths_out.emplace_back(level_names[lpc]);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.with_children(style_config_handlers),
|
|
|
|
|
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct json_path_handler theme_vars_handlers[] = {
|
|
|
|
json_path_handler("(?<var_name>\\w+)")
|
|
|
|
.with_synopsis("name")
|
|
|
|
.with_description("A theme variable definition")
|
|
|
|
.with_path_provider<lnav_theme>([](struct lnav_theme *lt, vector<string> &paths_out) {
|
|
|
|
for (const auto &iter : lt->lt_vars) {
|
|
|
|
paths_out.emplace_back(iter.first);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.FOR_FIELD(lnav_theme, lt_vars),
|
|
|
|
|
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct json_path_handler theme_def_handlers[] = {
|
|
|
|
json_path_handler("vars/")
|
|
|
|
.with_description("Variables definitions that are used in this theme")
|
|
|
|
.with_children(theme_vars_handlers),
|
|
|
|
|
|
|
|
json_path_handler("styles/")
|
|
|
|
.with_children(theme_styles_handlers),
|
|
|
|
|
|
|
|
json_path_handler("syntax-styles/")
|
|
|
|
.with_children(theme_syntax_styles_handlers),
|
|
|
|
|
|
|
|
json_path_handler("status-styles/")
|
|
|
|
.with_children(theme_status_styles_handlers),
|
|
|
|
|
|
|
|
json_path_handler("log-level-styles/")
|
|
|
|
.with_children(theme_log_level_styles_handlers),
|
|
|
|
|
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct json_path_handler theme_defs_handlers[] = {
|
|
|
|
json_path_handler("(?<theme_name>[^/]+)/")
|
|
|
|
.with_obj_provider<lnav_theme, _lnav_config>([](const yajlpp_provider_context &ypc, _lnav_config *root) {
|
|
|
|
lnav_theme < = root->lc_ui_theme_defs[ypc.ypc_extractor.get_substr("theme_name")];
|
|
|
|
|
|
|
|
return <
|
|
|
|
})
|
|
|
|
.with_path_provider<_lnav_config>([](struct _lnav_config *cfg, vector<string> &paths_out) {
|
|
|
|
for (const auto &iter : cfg->lc_ui_theme_defs) {
|
|
|
|
paths_out.emplace_back(iter.first);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.with_children(theme_def_handlers),
|
|
|
|
|
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
2016-01-05 14:18:58 +00:00
|
|
|
static struct json_path_handler ui_handlers[] = {
|
|
|
|
json_path_handler("clock-format")
|
2019-05-03 20:50:19 +00:00
|
|
|
.with_synopsis("format")
|
2018-05-25 13:32:01 +00:00
|
|
|
.with_description(
|
|
|
|
"The format for the clock displayed in "
|
|
|
|
"the top-left corner using strftime(3) conversions")
|
|
|
|
.FOR_FIELD(_lnav_config, lc_ui_clock_format),
|
2016-05-02 03:35:37 +00:00
|
|
|
json_path_handler("dim-text")
|
2019-05-03 20:50:19 +00:00
|
|
|
.with_synopsis("bool")
|
2016-05-02 03:35:37 +00:00
|
|
|
.with_description("Reduce the brightness of text (useful for xterms)")
|
2018-05-25 13:32:01 +00:00
|
|
|
.FOR_FIELD(_lnav_config, lc_ui_dim_text),
|
2017-09-15 01:52:29 +00:00
|
|
|
json_path_handler("default-colors")
|
2019-05-03 20:50:19 +00:00
|
|
|
.with_synopsis("bool")
|
2017-09-15 01:52:29 +00:00
|
|
|
.with_description("Use default terminal fg/bg colors")
|
2018-05-25 13:32:01 +00:00
|
|
|
.FOR_FIELD(_lnav_config, lc_ui_default_colors),
|
2017-01-21 15:41:28 +00:00
|
|
|
json_path_handler("keymap")
|
2019-05-03 20:50:19 +00:00
|
|
|
.with_synopsis("name")
|
2017-01-21 15:41:28 +00:00
|
|
|
.with_description("The name of the keymap to use")
|
2018-05-25 13:32:01 +00:00
|
|
|
.FOR_FIELD(_lnav_config, lc_ui_keymap),
|
2019-05-03 20:50:19 +00:00
|
|
|
json_path_handler("theme")
|
|
|
|
.with_synopsis("theme_name")
|
|
|
|
.with_description("The name of the theme to use")
|
|
|
|
.FOR_FIELD(_lnav_config, lc_ui_theme),
|
|
|
|
json_path_handler("theme-defs/")
|
|
|
|
.with_description("Theme definitions")
|
|
|
|
.with_children(theme_defs_handlers),
|
2016-01-05 14:18:58 +00:00
|
|
|
|
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
|
|
|
struct json_path_handler lnav_config_handlers[] = {
|
|
|
|
json_path_handler("/ui/")
|
2019-05-03 20:50:19 +00:00
|
|
|
.with_description("User-interface settings")
|
|
|
|
.with_children(ui_handlers),
|
2016-01-05 14:18:58 +00:00
|
|
|
|
|
|
|
json_path_handler()
|
|
|
|
};
|
|
|
|
|
|
|
|
static void load_config_from(const string &path, vector<string> &errors)
|
|
|
|
{
|
|
|
|
yajlpp_parse_context ypc(path, lnav_config_handlers);
|
|
|
|
struct userdata ud(errors);
|
|
|
|
auto_fd fd;
|
|
|
|
|
2019-05-03 20:50:19 +00:00
|
|
|
ypc.ypc_locations = &lnav_config_locations;
|
2016-01-05 14:18:58 +00:00
|
|
|
ypc.with_obj(lnav_config);
|
|
|
|
ypc.ypc_userdata = &ud;
|
|
|
|
if ((fd = open(path.c_str(), O_RDONLY)) == -1) {
|
|
|
|
if (errno != ENOENT) {
|
|
|
|
char errmsg[1024];
|
|
|
|
|
|
|
|
snprintf(errmsg, sizeof(errmsg),
|
|
|
|
"error: unable to open format file -- %s",
|
|
|
|
path.c_str());
|
2018-05-25 13:32:01 +00:00
|
|
|
errors.emplace_back(errmsg);
|
2016-01-05 14:18:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
auto_mem<yajl_handle_t> handle(yajl_free);
|
|
|
|
char buffer[2048];
|
|
|
|
off_t offset = 0;
|
|
|
|
ssize_t rc = -1;
|
|
|
|
|
|
|
|
handle = yajl_alloc(&ypc.ypc_callbacks, NULL, &ypc);
|
|
|
|
yajl_config(handle, yajl_allow_comments, 1);
|
2019-05-03 20:50:19 +00:00
|
|
|
yajl_config(handle, yajl_allow_multiple_values, 1);
|
|
|
|
ypc.ypc_handle = handle;
|
2016-01-05 14:18:58 +00:00
|
|
|
while (true) {
|
|
|
|
rc = read(fd, buffer, sizeof(buffer));
|
|
|
|
if (rc == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (rc == -1) {
|
|
|
|
errors.push_back(path +
|
|
|
|
":unable to read file -- " +
|
|
|
|
string(strerror(errno)));
|
|
|
|
break;
|
|
|
|
}
|
2019-05-03 20:50:19 +00:00
|
|
|
if (ypc.parse((const unsigned char *)buffer, rc) != yajl_status_ok) {
|
2016-01-05 14:18:58 +00:00
|
|
|
errors.push_back(path +
|
|
|
|
": invalid json -- " +
|
|
|
|
string((char *)yajl_get_error(handle, 1, (unsigned char *)buffer, rc)));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
offset += rc;
|
|
|
|
}
|
|
|
|
if (rc == 0) {
|
2019-05-03 20:50:19 +00:00
|
|
|
if (ypc.complete_parse() != yajl_status_ok) {
|
2016-01-05 14:18:58 +00:00
|
|
|
errors.push_back(path +
|
|
|
|
": invalid json -- " +
|
|
|
|
string((char *)yajl_get_error(handle, 0, NULL, 0)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void load_default_config(yajlpp_parse_context &ypc_builtin,
|
|
|
|
struct _lnav_config &config_obj,
|
2017-01-21 15:41:28 +00:00
|
|
|
const char *config_json,
|
2016-01-05 14:18:58 +00:00
|
|
|
vector<string> &errors)
|
|
|
|
{
|
|
|
|
auto_mem<yajl_handle_t> handle(yajl_free);
|
|
|
|
struct userdata ud(errors);
|
|
|
|
|
|
|
|
handle = yajl_alloc(&ypc_builtin.ypc_callbacks, NULL, &ypc_builtin);
|
2019-05-03 20:50:19 +00:00
|
|
|
ypc_builtin.with_handle(handle);
|
2016-01-05 14:18:58 +00:00
|
|
|
ypc_builtin.with_obj(config_obj);
|
2019-05-03 20:50:19 +00:00
|
|
|
ypc_builtin.with_error_reporter(config_error_reporter);
|
2016-01-05 14:18:58 +00:00
|
|
|
ypc_builtin.ypc_userdata = &ud;
|
|
|
|
yajl_config(handle, yajl_allow_comments, 1);
|
2019-05-03 20:50:19 +00:00
|
|
|
yajl_config(handle, yajl_allow_multiple_values, 1);
|
|
|
|
if (ypc_builtin.parse((const unsigned char *) config_json,
|
|
|
|
strlen(config_json)) != yajl_status_ok ||
|
|
|
|
ypc_builtin.complete_parse() != yajl_status_ok) {
|
2016-01-05 14:18:58 +00:00
|
|
|
errors.push_back("builtin: invalid json -- " +
|
2017-01-21 15:41:28 +00:00
|
|
|
string((char *)yajl_get_error(handle, 1, (unsigned char *) config_json, strlen(config_json))));
|
2016-01-05 14:18:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void load_config(const vector<string> &extra_paths, vector<string> &errors)
|
|
|
|
{
|
|
|
|
string user_config = dotlnav_path("config.json");
|
|
|
|
|
2017-01-21 15:41:28 +00:00
|
|
|
{
|
|
|
|
yajlpp_parse_context ypc_builtin("keymap", root_config_handlers);
|
2019-05-03 20:50:19 +00:00
|
|
|
ypc_builtin.ypc_locations = &lnav_config_locations;
|
2017-01-21 15:41:28 +00:00
|
|
|
load_default_config(ypc_builtin, lnav_config, keymap_default_json,
|
|
|
|
errors);
|
|
|
|
}
|
|
|
|
|
2019-02-01 21:31:28 +00:00
|
|
|
for (const auto &pair : lnav_config.lc_ui_keymaps) {
|
|
|
|
for (const auto &pair2 : pair.second.km_seq_to_cmd) {
|
2018-05-25 13:32:01 +00:00
|
|
|
log_debug("foo %s %d", pair2.first.c_str(), pair2.second.size());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-21 15:41:28 +00:00
|
|
|
{
|
|
|
|
yajlpp_parse_context ypc_builtin("builtin", lnav_config_handlers);
|
2019-05-03 20:50:19 +00:00
|
|
|
ypc_builtin.ypc_locations = &lnav_config_locations;
|
2017-01-21 15:41:28 +00:00
|
|
|
ypc_builtin.reset(lnav_config_handlers);
|
|
|
|
load_default_config(ypc_builtin, lnav_default_config,
|
|
|
|
default_config_json, errors);
|
|
|
|
ypc_builtin.reset(lnav_config_handlers);
|
|
|
|
load_default_config(ypc_builtin, lnav_config, default_config_json,
|
|
|
|
errors);
|
2019-05-03 20:50:19 +00:00
|
|
|
|
|
|
|
for (const auto &extra_path : extra_paths) {
|
|
|
|
string format_path = extra_path + "/formats/*/*.json";
|
|
|
|
static_root_mem<glob_t, globfree> gl;
|
|
|
|
|
|
|
|
if (glob(format_path.c_str(), 0, NULL, gl.inout()) == 0) {
|
|
|
|
for (int lpc = 0; lpc < (int)gl->gl_pathc; lpc++) {
|
|
|
|
const char *base = basename(gl->gl_pathv[lpc]);
|
|
|
|
|
|
|
|
if (!startswith(base, "config.")) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
string filename(gl->gl_pathv[lpc]);
|
|
|
|
|
|
|
|
load_config_from(filename, errors);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-21 15:41:28 +00:00
|
|
|
load_config_from(user_config, errors);
|
|
|
|
}
|
2016-05-02 03:35:37 +00:00
|
|
|
|
2019-05-03 20:50:19 +00:00
|
|
|
reload_config(errors);
|
|
|
|
|
|
|
|
rollback_lnav_config = lnav_config;
|
2016-01-05 14:18:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void reset_config(const std::string &path)
|
|
|
|
{
|
|
|
|
yajlpp_parse_context ypc_builtin("builtin", lnav_config_handlers);
|
|
|
|
vector<string> errors;
|
|
|
|
|
|
|
|
if (path != "*") {
|
2017-01-21 15:41:28 +00:00
|
|
|
ypc_builtin.ypc_ignore_unused = true;
|
2016-01-05 14:18:58 +00:00
|
|
|
ypc_builtin.ypc_active_paths.insert(path);
|
|
|
|
}
|
|
|
|
|
2017-01-21 15:41:28 +00:00
|
|
|
load_default_config(ypc_builtin, lnav_config, default_config_json, errors);
|
2019-05-03 20:50:19 +00:00
|
|
|
|
|
|
|
reload_config(errors);
|
2016-01-05 14:18:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
string save_config()
|
|
|
|
{
|
|
|
|
auto_mem<yajl_gen_t> handle(yajl_gen_free);
|
|
|
|
|
|
|
|
if ((handle = yajl_gen_alloc(NULL)) == NULL) {
|
|
|
|
return "error: Unable to create yajl_gen_object";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
char filename[128];
|
|
|
|
|
|
|
|
snprintf(filename, sizeof(filename), "config.json.%d.tmp", getpid());
|
|
|
|
|
|
|
|
string user_config_tmp = dotlnav_path(filename);
|
|
|
|
string user_config = dotlnav_path("config.json");
|
|
|
|
|
|
|
|
yajl_gen_config(handle, yajl_gen_beautify, true);
|
|
|
|
|
|
|
|
yajlpp_gen_context ygc(handle, lnav_config_handlers);
|
|
|
|
vector<string> errors;
|
|
|
|
|
|
|
|
ygc.with_default_obj(lnav_default_config)
|
2017-01-21 15:41:28 +00:00
|
|
|
.with_obj(lnav_config);
|
2016-01-05 14:18:58 +00:00
|
|
|
ygc.gen();
|
|
|
|
|
|
|
|
const unsigned char *buffer;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
yajl_gen_get_buf(handle, &buffer, &len);
|
|
|
|
|
|
|
|
{
|
|
|
|
auto_fd fd;
|
|
|
|
|
|
|
|
if ((fd = open(user_config_tmp.c_str(),
|
|
|
|
O_WRONLY | O_CREAT | O_TRUNC, 0600)) == -1) {
|
|
|
|
return "error: unable to save configuration -- " +
|
|
|
|
string(strerror(errno));
|
|
|
|
}
|
|
|
|
else {
|
2016-05-04 04:01:39 +00:00
|
|
|
log_perror(write(fd, buffer, len));
|
2016-01-05 14:18:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rename(user_config_tmp.c_str(), user_config.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
return "info: configuration saved";
|
|
|
|
}
|
2016-05-02 03:35:37 +00:00
|
|
|
|
2019-05-03 20:50:19 +00:00
|
|
|
void reload_config(vector<string> &errors)
|
2016-05-02 03:35:37 +00:00
|
|
|
{
|
|
|
|
lnav_config_listener *curr = lnav_config_listener::LISTENER_LIST;
|
|
|
|
|
|
|
|
while (curr != NULL) {
|
2019-05-03 20:50:19 +00:00
|
|
|
auto reporter = [&errors](const void *cfg_value, const std::string &errmsg) {
|
|
|
|
auto cb = [&cfg_value, &errors, &errmsg](
|
|
|
|
const json_path_handler_base &jph,
|
|
|
|
const string &path,
|
|
|
|
void *mem) {
|
|
|
|
if (mem != cfg_value) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto loc_iter = lnav_config_locations.find(intern_string::lookup(path));
|
|
|
|
if (loc_iter == lnav_config_locations.end()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char msg[1024];
|
|
|
|
|
|
|
|
snprintf(msg, sizeof(msg),
|
|
|
|
"%s:%d:%s",
|
|
|
|
loc_iter->second.sl_source.get(),
|
|
|
|
loc_iter->second.sl_line_number,
|
|
|
|
errmsg.c_str());
|
|
|
|
|
|
|
|
errors.emplace_back(msg);
|
|
|
|
};
|
|
|
|
|
|
|
|
for (int lpc = 0; lnav_config_handlers[lpc].jph_path[0]; lpc++) {
|
|
|
|
lnav_config_handlers[lpc].walk(cb, &lnav_config);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
curr->reload_config(reporter);
|
2016-05-02 03:35:37 +00:00
|
|
|
curr = curr->lcl_next;
|
|
|
|
}
|
2018-05-10 13:44:03 +00:00
|
|
|
}
|