#include #include #include #include #include #include "log.h" #include "misc.h" #include "config.h" static FILE* fopen_or_raise(const char* __restrict path, const char* __restrict mode, bool ignore_enoent) { FILE* file = fopen(path, mode); if (!file && !(ignore_enoent && errno == ENOENT)) { throw_system_error(std::string("fopen(") + quote(path) + ')'); } return file; } static void fclose_and_log(FILE* file) { if (!fclose(file)) { return; } log(std::string("Failed to close a file: fclose(): ") + strerror(errno)); } std::string get_config_folder() { const char* path; path = getenv("XDG_CONFIG_HOME"); if (path) { return std::string(path) + "/logmeow"; } path = getenv("HOME"); if (path) { return std::string(path) + "/.config/logmeow"; } throw std::runtime_error("cannot find suitable config folder, please set XDG_CONFIG_HOME or HOME"); } std::string get_config_file_path() { return get_config_folder() + "/config.json"; } // tries to create a directory with an empty string if it's just "/" but who cares void create_config_folders_if_necessary() { std::string full_path = get_config_folder(); std::string path; size_t pos = 0; while (pos != std::string::npos) { pos = full_path.find('/', pos); if (pos != std::string::npos) { pos++; } path = full_path.substr(0, pos); if (!mkdir(path.c_str(), 0600)) { continue; } if (errno == EEXIST) { continue; } throw_system_error(std::string("mkdir(") + quote(path) + ')'); } } void from_json(const nlohmann::json& j, Config& config) { j.at("logcat_command").get_to(config.logcat_command); j.at("normal_font_size").get_to(config.normal_font_size); j.at("monospace_font_size").get_to(config.monospace_font_size); j.at("filters").get_to(config.filters); j.at("exclusions").get_to(config.exclusions); } Config load_config() { std::string config_file_path = get_config_file_path(); std::unique_ptr config_file(fopen_or_raise(config_file_path.c_str(), "r", true), fclose_and_log); if (!config_file) { return Config(); } return nlohmann::json::parse(config_file.get()).get(); } void to_json(nlohmann::json& j, const Config& config) { j["logcat_command"] = config.logcat_command; j["normal_font_size"] = config.normal_font_size; j["monospace_font_size"] = config.monospace_font_size; j["filters"] = config.filters; j["exclusions"] = config.exclusions; } void write_config(const Config& config) { std::string config_file_path = get_config_file_path(); std::string tmp_config_file_path = config_file_path + ".tmp"; std::unique_ptr config_file(fopen_or_raise(tmp_config_file_path.c_str(), "w", false), fclose_and_log); std::string str_config = nlohmann::json(config).dump(4); fwrite(str_config.data(), sizeof(char), str_config.size(), config_file.get()); config_file.reset(); if (!rename(tmp_config_file_path.c_str(), config_file_path.c_str())) { return; } throw_system_error(std::string("rename(") + quote(tmp_config_file_path) + ", " + quote(config_file_path) + ')'); }