#include #include #include #include #include #include #include #include #include #include #include #include namespace newsbeuter { configdata::configdata(const std::string& v, ...) : value(v), default_value(v), type(ENUM) { va_list ap; va_start(ap, v); const char * arg; do { arg = va_arg(ap, const char *); if (arg) { enum_values.insert(arg); } } while (arg != NULL); va_end(ap); } configcontainer::configcontainer() { // create the config options and set their resp. default value and type config_data["show-read-feeds"] = configdata("yes", configdata::BOOL); config_data["browser"] = configdata("lynx", configdata::PATH); config_data["use-proxy"] = configdata("no", configdata::BOOL); config_data["auto-reload"] = configdata("no", configdata::BOOL); config_data["reload-time"] = configdata("60", configdata::INT); config_data["max-items"] = configdata("0", configdata::INT); config_data["save-path"] = configdata("~/", configdata::PATH); config_data["download-path"] = configdata("~/", configdata::PATH); config_data["max-downloads"] = configdata("1", configdata::INT); config_data["podcast-auto-enqueue"] = configdata("no", configdata::BOOL); config_data["player"] = configdata("", configdata::PATH); config_data["cleanup-on-quit"] = configdata("yes", configdata::BOOL); config_data["user-agent"] = configdata("", configdata::STR); config_data["refresh-on-startup"] = configdata("no", configdata::BOOL); config_data["suppress-first-reload"] = configdata("no", configdata::BOOL); config_data["cache-file"] = configdata("", configdata::PATH); config_data["proxy"] = configdata("", configdata::STR); config_data["proxy-auth"] = configdata("", configdata::STR); config_data["proxy-auth-method"] = configdata("any", "any", "basic", "digest", "digest_ie", "gssnegotiate", "ntlm", "anysafe", NULL); config_data["confirm-exit"] = configdata("no", configdata::BOOL); config_data["error-log"] = configdata("", configdata::PATH); config_data["notify-screen"] = configdata("no", configdata::BOOL); config_data["notify-always"] = configdata("no", configdata::BOOL); config_data["notify-xterm"] = configdata("no", configdata::BOOL); config_data["notify-beep"] = configdata("no", configdata::BOOL); config_data["notify-program"] = configdata("", configdata::PATH); config_data["notify-format"] = configdata(_("newsbeuter: finished reload, %f unread feeds (%n unread articles total)"), configdata::STR); config_data["datetime-format"] = configdata("%b %d", configdata::STR); config_data["urls-source"] = configdata("local", "local", "opml", "googlereader", NULL); // enum config_data["bookmark-cmd"] = configdata("", configdata::STR); config_data["opml-url"] = configdata("", configdata::STR, true); config_data["html-renderer"] = configdata("internal", configdata::PATH); config_data["feedlist-format"] = configdata("%4i %n %11u %t", configdata::STR); config_data["articlelist-format"] = configdata("%4i %f %D %6L %?T?|%-17T| &?%t", configdata::STR); config_data["text-width"] = configdata("0", configdata::INT); config_data["always-display-description"] = configdata("false", configdata::BOOL); config_data["reload-only-visible-feeds"] = configdata("false", configdata::BOOL); config_data["article-sort-order"] = configdata("date-asc", configdata::STR); config_data["show-read-articles"] = configdata("yes", configdata::BOOL); config_data["goto-next-feed"] = configdata("yes", configdata::BOOL); config_data["display-article-progress"] = configdata("yes", configdata::BOOL); config_data["show-keymap-hint"] = configdata("yes", configdata::BOOL); config_data["download-timeout"] = configdata("30", configdata::INT); config_data["download-retries"] = configdata("1", configdata::INT); config_data["feed-sort-order"] = configdata("none-desc", configdata::STR); config_data["reload-threads"] = configdata("1", configdata::INT); config_data["keep-articles-days"] = configdata("0", configdata::INT); config_data["bookmark-interactive"] = configdata("false", configdata::BOOL); config_data["mark-as-read-on-hover"] = configdata("false", configdata::BOOL); config_data["search-highlight-colors"] = configdata("black yellow bold", configdata::STR, true); config_data["pager"] = configdata("internal", configdata::PATH); config_data["history-limit"] = configdata("100", configdata::INT); config_data["prepopulate-query-feeds"] = configdata("false", configdata::BOOL); config_data["goto-first-unread"] = configdata("true", configdata::BOOL); config_data["proxy-type"] = configdata("http", "http", "socks4", "socks4a", "socks5", NULL); // enum config_data["googlereader-login"] = configdata("", configdata::STR); config_data["googlereader-password"] = configdata("", configdata::STR); config_data["googlereader-flag-share"] = configdata("", configdata::STR); config_data["googlereader-flag-star"] = configdata("", configdata::STR); config_data["googlereader-show-special-feeds"] = configdata("true", configdata::BOOL); config_data["googlereader-min-items"] = configdata("20", configdata::INT); config_data["ignore-mode"] = configdata("download", "download", "display", NULL); // enum config_data["max-download-speed"] = configdata("0", configdata::INT); config_data["cookie-cache"] = configdata("", configdata::STR); /* title formats: */ config_data["feedlist-title-format"] = configdata(_("%N %V - Your feeds (%u unread, %t total)%?T? - tag `%T'&?"), configdata::STR); config_data["articlelist-title-format"] = configdata(_("%N %V - Articles in feed '%T' (%u unread, %t total) - %U"), configdata::STR); config_data["searchresult-title-format"] = configdata(_("%N %V - Search result (%u unread, %t total)"), configdata::STR); config_data["filebrowser-title-format"] = configdata(_("%N %V - %?O?Open File&Save File? - %f"), configdata::STR); config_data["help-title-format"] = configdata(_("%N %V - Help"), configdata::STR); config_data["selecttag-title-format"] = configdata(_("%N %V - Select Tag"), configdata::STR); config_data["selectfilter-title-format"] = configdata(_("%N %V - Select Filter"), configdata::STR); config_data["itemview-title-format"] = configdata(_("%N %V - Article '%T'"), configdata::STR); config_data["urlview-title-format"] = configdata(_("%N %V - URLs"), configdata::STR); config_data["dialogs-title-format"] = configdata(_("%N %V - Dialogs"), configdata::STR); } configcontainer::~configcontainer() { } void configcontainer::register_commands(configparser& cfgparser) { // this registers the config options defined above in the configuration parser // -> if the resp. config option is encountered, it is passed to the configcontainer for (std::map::iterator it=config_data.begin();it!=config_data.end();++it) { cfgparser.register_handler(it->first, this); } } void configcontainer::handle_action(const std::string& action, const std::vector& params) { std::string resolved_action = lookup_alias(action); configdata& cfgdata = config_data[resolved_action]; // configdata::INVALID indicates that the action didn't exist, and that the returned object was created ad-hoc. if (cfgdata.type == configdata::INVALID) { LOG(LOG_WARN, "configcontainer::handler_action: unknown action %s", action.c_str()); throw confighandlerexception(AHS_INVALID_COMMAND); } LOG(LOG_DEBUG, "configcontainer::handle_action: action = %s, type = %u", action.c_str(), cfgdata.type); if (params.size() < 1) { throw confighandlerexception(AHS_TOO_FEW_PARAMS); } switch (cfgdata.type) { case configdata::BOOL: if (!is_bool(params[0])) throw confighandlerexception(utils::strprintf(_("expected boolean value, found `%s' instead"), params[0].c_str())); cfgdata.value = params[0]; break; case configdata::INT: if (!is_int(params[0])) throw confighandlerexception(utils::strprintf(_("expected integer value, found `%s' instead"), params[0].c_str())); cfgdata.value = params[0]; break; case configdata::ENUM: if (cfgdata.enum_values.find(params[0]) == cfgdata.enum_values.end()) throw confighandlerexception(utils::strprintf(_("invalid configuration value `%s'"), params[0].c_str())); // fall-through case configdata::STR: case configdata::PATH: if (cfgdata.multi_option) cfgdata.value = utils::join(params, " "); else cfgdata.value = params[0]; break; default: // should not happen throw confighandlerexception(AHS_INVALID_COMMAND); } } bool configcontainer::is_bool(const std::string& s) { const char * bool_values[] = { "yes", "no", "true", "false", 0 }; for (int i=0; bool_values[i] ; ++i) { if (s == bool_values[i]) return true; } return false; } std::string configcontainer::lookup_alias(const std::string& s) { // this assumes that the config_data table is consistent. std::string alias = s; while (alias != "" && config_data[alias].type == configdata::ALIAS) { alias = config_data[alias].default_value; } return alias; } bool configcontainer::is_int(const std::string& s) { const char * s1 = s.c_str(); for (;*s1;s1++) { if (!isdigit(*s1)) return false; } return true; } std::string configcontainer::get_configvalue(const std::string& key) { std::string retval = config_data[lookup_alias(key)].value; if (config_data[key].type == configdata::PATH) { retval = utils::resolve_tilde(retval); } return retval; } int configcontainer::get_configvalue_as_int(const std::string& key) { std::istringstream is(config_data[lookup_alias(key)].value); int i; is >> i; return i; } bool configcontainer::get_configvalue_as_bool(const std::string& key) { std::string value = config_data[lookup_alias(key)].value; if (value == "true" || value == "yes") return true; return false; } void configcontainer::set_configvalue(const std::string& key, const std::string& value) { LOG(LOG_DEBUG,"configcontainer::set_configvalue(%s [resolved: %s],%s) called", key.c_str(), lookup_alias(key).c_str(), value.c_str()); config_data[lookup_alias(key)].value = value; } void configcontainer::reset_to_default(const std::string& key) { std::string resolved_key = lookup_alias(key); config_data[resolved_key].value = config_data[resolved_key].default_value; } void configcontainer::toggle(const std::string& key) { std::string resolved_key = lookup_alias(key); if (config_data[resolved_key].type == configdata::BOOL) { set_configvalue(resolved_key, std::string(get_configvalue_as_bool(resolved_key) ? "false" : "true")); } } void configcontainer::dump_config(std::vector& config_output) { for (std::map::iterator it=config_data.begin();it!=config_data.end();it++) { std::string configline = it->first + " "; assert(it->second.type != configdata::INVALID); switch (it->second.type) { case configdata::BOOL: case configdata::INT: configline.append(it->second.value); if (it->second.value != it->second.default_value) configline.append(utils::strprintf(" # default: %s", it->second.default_value.c_str())); break; case configdata::ENUM: case configdata::STR: case configdata::PATH: if (it->second.multi_option) { std::vector tokens = utils::tokenize(it->second.value, " "); for (std::vector::iterator it=tokens.begin();it!=tokens.end();it++) { configline.append(utils::quote(*it) + " "); } } else { configline.append(utils::quote(it->second.value)); if (it->second.value != it->second.default_value) { configline.append(utils::strprintf(" # default: %s", it->second.default_value.c_str())); } } break; case configdata::ALIAS: // skip entry, generate no output continue; case configdata::INVALID: assert(0); break; } config_output.push_back(configline); } } std::vector configcontainer::get_suggestions(const std::string& fragment) { std::vector result; for (std::map::iterator it=config_data.begin();it!=config_data.end();it++) { if (it->first.substr(0, fragment.length()) == fragment) result.push_back(it->first); } std::sort(result.begin(), result.end()); return result; } }