/** @file scim_simple_config.cpp * implementation of SimpleConfig class. */ /* * Smart Common Input Method * * Copyright (c) 2002-2005 James Su * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA * * $Id: scim_simple_config.cpp,v 1.35 2005/07/06 03:57:04 suzhe Exp $ */ #define Uses_SCIM_CONFIG_BASE #define Uses_SCIM_CONFIG_PATH #define Uses_STL_IOSTREAM #define Uses_STL_FSTREAM #define Uses_C_STDIO #include #include #include #include #include "scim_private.h" #include "scim.h" #include "scim_simple_config.h" #ifndef SCIM_SYSCONFDIR #define SCIM_SYSCONFDIR "/etc" #endif #define scim_module_init simple_LTX_scim_module_init #define scim_module_exit simple_LTX_scim_module_exit #define scim_config_module_init simple_LTX_scim_config_module_init #define scim_config_module_create_config simple_LTX_scim_config_module_create_config using namespace scim; extern "C" { void scim_module_init (void) { SCIM_DEBUG_CONFIG(1) << "Initializing Simple Config module...\n"; } void scim_module_exit (void) { SCIM_DEBUG_CONFIG(1) << "Exiting Simple Config module...\n"; } void scim_config_module_init () { SCIM_DEBUG_CONFIG(1) << "Initializing Simple Config module (more)...\n"; } ConfigPointer scim_config_module_create_config () { SCIM_DEBUG_CONFIG(1) << "Creating a Simple Config instance...\n"; return new SimpleConfig (); } } namespace scim { SimpleConfig::SimpleConfig () : m_need_reload (false) { m_update_timestamp.tv_sec = 0; m_update_timestamp.tv_usec = 0; load_all_config (); } SimpleConfig::~SimpleConfig () { flush (); } bool SimpleConfig::valid () const { return ConfigBase::valid(); } String SimpleConfig::get_name () const { return "simple"; } // String bool SimpleConfig::read (const String& key, String *pStr) const { if (!valid () || !pStr || key.empty()) return false; KeyValueRepository::const_iterator i = m_new_config.find (key); KeyValueRepository::const_iterator end = m_new_config.end (); if (i == end) { i = m_config.find (key); end = m_config.end (); } if (i != end) { *pStr = i->second; return true; } *pStr = String (""); return false; } // int bool SimpleConfig::read (const String& key, int *pl) const { if (!valid () || !pl || key.empty()) return false; KeyValueRepository::const_iterator i = m_new_config.find (key); KeyValueRepository::const_iterator end = m_new_config.end (); if (i == end || !i->second.length ()) { i = m_config.find (key); end = m_config.end (); } if (i != end && i->second.length ()) { *pl = strtol (i->second.c_str (), (char**) NULL, 10); return true; } *pl = 0; return false; } // double bool SimpleConfig::read (const String& key, double* val) const { if (!valid () || !val || key.empty()) return false; KeyValueRepository::const_iterator i = m_new_config.find (key); KeyValueRepository::const_iterator end = m_new_config.end (); if (i == end || !i->second.length ()) { i = m_config.find (key); end = m_config.end (); } if (i != end && i->second.length ()) { *val = strtod (i->second.c_str (), (char**) NULL); return true; } *val = 0; return false; } // bool bool SimpleConfig::read (const String& key, bool* val) const { if (!valid () || !val || key.empty()) return false; KeyValueRepository::const_iterator i = m_new_config.find (key); KeyValueRepository::const_iterator end = m_new_config.end (); if (i == end || !i->second.length ()) { i = m_config.find (key); end = m_config.end (); } if (i != end && i->second.length ()) { if (i->second == "true" || i->second == "TRUE" || i->second == "True" || i->second == "1") { *val = true; return true; } else if (i->second == "false" || i->second == "FALSE" || i->second == "False" || i->second == "0") { *val = false; return true; } } *val = false; return false; } //String list bool SimpleConfig::read (const String& key, std::vector * val) const { if (!valid () || !val || key.empty()) return false; KeyValueRepository::const_iterator i = m_new_config.find (key); KeyValueRepository::const_iterator end = m_new_config.end (); if (i == end) { i = m_config.find (key); end = m_config.end (); } val->clear (); if (i != end) { scim_split_string_list (*val, i->second, ','); return true; } return false; } //int list bool SimpleConfig::read (const String& key, std::vector * val) const { if (!valid () || !val || key.empty()) return false; KeyValueRepository::const_iterator i = m_new_config.find (key); KeyValueRepository::const_iterator end = m_new_config.end (); if (i == end) { i = m_config.find (key); end = m_config.end (); } val->clear(); if (i != end) { std::vector vec; scim_split_string_list (vec, i->second, ','); for (std::vector ::iterator j = vec.begin (); j != vec.end (); ++j) { int result = strtol (j->c_str (), (char**)NULL, 10); val->push_back (result); } return true; } return false; } // write the value (return true on success) bool SimpleConfig::write (const String& key, const String& value) { if (!valid () || key.empty()) return false; m_new_config [key] = value; remove_key_from_erased_list (key); m_need_reload = true; return true; } bool SimpleConfig::write (const String& key, int value) { if (!valid () || key.empty()) return false; char buf [256]; snprintf (buf, 255, "%d", value); m_new_config [key] = String (buf); remove_key_from_erased_list (key); m_need_reload = true; return true; } bool SimpleConfig::write (const String& key, double value) { if (!valid () || key.empty()) return false; char buf [256]; snprintf (buf, 255, "%lf", value); m_new_config [key] = String (buf); remove_key_from_erased_list (key); m_need_reload = true; return true; } bool SimpleConfig::write (const String& key, bool value) { if (!valid () || key.empty()) return false; if (value) m_new_config [key] = String ("true"); else m_new_config [key] = String ("false"); remove_key_from_erased_list (key); m_need_reload = true; return true; } bool SimpleConfig::write (const String& key, const std::vector & value) { if (!valid () || key.empty()) return false; m_new_config [key] = scim_combine_string_list (value, ','); remove_key_from_erased_list (key); m_need_reload = true; return true; } bool SimpleConfig::write (const String& key, const std::vector & value) { if (!valid () || key.empty()) return false; std::vector vec; char buf [256]; for (std::vector ::const_iterator i = value.begin (); i != value.end (); ++i) { snprintf (buf, 255, "%d", *i); vec.push_back (String (buf)); } m_new_config [key] = scim_combine_string_list (vec, ','); remove_key_from_erased_list (key); m_need_reload = true; return true; } // permanently writes all changes bool SimpleConfig::flush() { if (!valid ()) return false; // If no config has been modified, then just return. if (!m_new_config.size () && !m_erased_keys.size ()) return true; String userconf = get_userconf_filename (); String userconf_dir = get_userconf_dir (); if (access (userconf_dir.c_str (), R_OK | W_OK) != 0) { mkdir (userconf_dir.c_str (), S_IRUSR | S_IWUSR | S_IXUSR); if (access (userconf_dir.c_str (), R_OK | W_OK) != 0) return false; } if (userconf.length ()) { // Reload config to ensure user made modification won't lost. load_all_config (); std::ofstream os (userconf.c_str ()); if (!os) return false; KeyValueRepository::iterator i; std::vector::iterator j; // Merge new config with old ones. for (i = m_new_config.begin (); i != m_new_config.end (); ++i) m_config [i->first] = i->second; // Remove all erased keys. for (j = m_erased_keys.begin (); j != m_erased_keys.end (); ++j) { if ((i = m_config.find (*j)) != m_config.end ()) m_config.erase (i); } m_new_config.clear (); m_erased_keys.clear (); gettimeofday (&m_update_timestamp, 0); char buf [128]; snprintf (buf, 128, "%lu:%lu", m_update_timestamp.tv_sec, m_update_timestamp.tv_usec); m_config [String (SCIM_CONFIG_UPDATE_TIMESTAMP)] = String (buf); save_config (os); return true; } return false; } // delete entries bool SimpleConfig::erase (const String& key) { if (!valid ()) return false; KeyValueRepository::iterator i = m_new_config.find(key); KeyValueRepository::iterator j = m_config.find(key); bool ok = false; if (i != m_new_config.end ()) { m_new_config.erase (i); ok = true; } if (j != m_config.end ()) { m_config.erase (j); ok = true; } if (ok && std::find (m_erased_keys.begin (), m_erased_keys.end (), key) == m_erased_keys.end ()) m_erased_keys.push_back (key); m_need_reload = true; return ok; } bool SimpleConfig::reload () { if (!valid ()) return false; if (load_all_config ()) { m_new_config.clear (); m_erased_keys.clear (); m_need_reload = true; } if (m_need_reload) { m_need_reload = false; return ConfigBase::reload (); } return false; } String SimpleConfig::get_sysconf_dir () { return String (SCIM_SYSCONFDIR) + String (SCIM_PATH_DELIM_STRING) + String ("scim"); } String SimpleConfig::get_userconf_dir () { return scim_get_user_data_dir (); } String SimpleConfig::get_sysconf_filename () { return get_sysconf_dir () + String (SCIM_PATH_DELIM_STRING) + String ("config"); } String SimpleConfig::get_userconf_filename () { return get_userconf_dir () + String (SCIM_PATH_DELIM_STRING) + String ("config"); } String SimpleConfig::trim_blank (const String &str) { String::size_type begin, len; begin = str.find_first_not_of (" \t\n\v"); if (begin == String::npos) return String (); len = str.find_last_not_of (" \t\n\v") - begin + 1; return str.substr (begin, len); } String SimpleConfig::get_param_portion (const String &str) { String::size_type begin = str.find_first_of (" \t\n\v="); if (begin == String::npos) return str; return str.substr (0, begin); } String SimpleConfig::get_value_portion (const String &str) { String::size_type begin = str.find_first_of ("="); if (begin == String::npos || (begin + 1) == str.length ()) return String (""); return trim_blank (str.substr (begin + 1, String::npos)); } void SimpleConfig::parse_config (std::istream &is, KeyValueRepository &config) { char *conf_line = new char [SCIM_MAX_CONFIG_LINE_LENGTH]; while (!is.eof()) { is.getline(conf_line, SCIM_MAX_CONFIG_LINE_LENGTH); if (!is.eof()) { String normalized_line = trim_blank(conf_line); if ((normalized_line.find_first_of("#") > 0) && (normalized_line.length() != 0)) { if (normalized_line.find_first_of("=") == String::npos) { SCIM_DEBUG_CONFIG(2) << " Invalid config line : " << normalized_line << "\n"; continue; } if (normalized_line[0] == '=') { SCIM_DEBUG_CONFIG(2) << " Invalid config line : " << normalized_line << "\n"; continue; } String param = get_param_portion(normalized_line); KeyValueRepository::iterator i = config.find(param); if (i != config.end()) { SCIM_DEBUG_CONFIG(2) << " Config entry " << normalized_line << " has been read.\n"; } else { String value = get_value_portion (normalized_line); config [param] = value; SCIM_DEBUG_CONFIG(2) << " Config entry " << param << "=" << value << " is successfully read.\n"; } } } } delete [] conf_line; } void SimpleConfig::save_config (std::ostream &os) { KeyValueRepository::iterator i; for (i = m_config.begin (); i != m_config.end (); ++i) { os << i->first << " = " << i->second << "\n"; } } bool SimpleConfig::load_all_config () { String sysconf = get_sysconf_filename (); String userconf = get_userconf_filename (); KeyValueRepository config; if (userconf.length ()) { std::ifstream is (userconf.c_str ()); if (is) { SCIM_DEBUG_CONFIG(1) << "Parsing user config file: " << userconf << "\n"; parse_config (is, config); } } if (sysconf.length ()) { std::ifstream is (sysconf.c_str ()); if (is) { SCIM_DEBUG_CONFIG(1) << "Parsing system config file: " << sysconf << "\n"; parse_config (is, config); } } if (!m_config.size () || (m_update_timestamp.tv_sec == 0 && m_update_timestamp.tv_usec == 0)) { m_config.swap (config); gettimeofday (&m_update_timestamp, 0); return true; } KeyValueRepository::iterator it = config.find (String (SCIM_CONFIG_UPDATE_TIMESTAMP)); if (it != config.end ()) { std::vector strs; if (scim_split_string_list (strs, it->second, ':') == 2) { time_t sec = (time_t) strtol (strs [0].c_str (), 0, 10); suseconds_t usec = (suseconds_t) strtol (strs [1].c_str (), 0, 10); // The config file is newer, so load it. if (m_update_timestamp.tv_sec < sec || (m_update_timestamp.tv_sec == sec && m_update_timestamp.tv_usec < usec)) { m_config.swap (config); m_update_timestamp.tv_sec = (time_t) sec; m_update_timestamp.tv_usec = (suseconds_t) usec; return true; } } } return false; } void SimpleConfig::remove_key_from_erased_list (const String &key) { std::vector ::iterator it = std::find (m_erased_keys.begin (), m_erased_keys.end (), key); if (it != m_erased_keys.end ()) m_erased_keys.erase (it); } } // namespace scim /* vi:ts=4:nowrap:ai:expandtab */