1 /** @file scim_simple_config.cpp
2  * implementation of SimpleConfig class.
3  */
4 
5 /*
6  * Smart Common Input Method
7  *
8  * Copyright (c) 2002-2005 James Su <suzhe@tsinghua.org.cn>
9  *
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this program; if not, write to the
23  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24  * Boston, MA  02111-1307  USA
25  *
26  * $Id: scim_simple_config.cpp,v 1.35 2005/07/06 03:57:04 suzhe Exp $
27  */
28 
29 #define Uses_SCIM_CONFIG_BASE
30 #define Uses_SCIM_CONFIG_PATH
31 #define Uses_STL_IOSTREAM
32 #define Uses_STL_FSTREAM
33 #define Uses_C_STDIO
34 
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <sys/time.h>
38 #include <unistd.h>
39 #include "scim_private.h"
40 #include "scim.h"
41 #include "scim_simple_config.h"
42 
43 #ifndef SCIM_SYSCONFDIR
44   #define SCIM_SYSCONFDIR "/etc"
45 #endif
46 
47 #define scim_module_init simple_LTX_scim_module_init
48 #define scim_module_exit simple_LTX_scim_module_exit
49 #define scim_config_module_init simple_LTX_scim_config_module_init
50 #define scim_config_module_create_config simple_LTX_scim_config_module_create_config
51 
52 using namespace scim;
53 
54 extern "C" {
scim_module_init(void)55     void scim_module_init (void)
56     {
57         SCIM_DEBUG_CONFIG(1) << "Initializing Simple Config module...\n";
58     }
59 
scim_module_exit(void)60     void scim_module_exit (void)
61     {
62         SCIM_DEBUG_CONFIG(1) << "Exiting Simple Config module...\n";
63     }
64 
scim_config_module_init()65     void scim_config_module_init ()
66     {
67         SCIM_DEBUG_CONFIG(1) << "Initializing Simple Config module (more)...\n";
68     }
69 
scim_config_module_create_config()70     ConfigPointer scim_config_module_create_config ()
71     {
72         SCIM_DEBUG_CONFIG(1) << "Creating a Simple Config instance...\n";
73         return new SimpleConfig ();
74     }
75 }
76 
77 namespace scim {
78 
SimpleConfig()79 SimpleConfig::SimpleConfig ()
80     : m_need_reload (false)
81 {
82     m_update_timestamp.tv_sec = 0;
83     m_update_timestamp.tv_usec = 0;
84 
85     load_all_config ();
86 }
87 
~SimpleConfig()88 SimpleConfig::~SimpleConfig ()
89 {
90     flush ();
91 }
92 
93 bool
valid() const94 SimpleConfig::valid () const
95 {
96     return ConfigBase::valid();
97 }
98 
99 String
get_name() const100 SimpleConfig::get_name () const
101 {
102     return "simple";
103 }
104 
105 // String
106 bool
read(const String & key,String * pStr) const107 SimpleConfig::read (const String& key, String *pStr) const
108 {
109     if (!valid () || !pStr || key.empty()) return false;
110 
111     KeyValueRepository::const_iterator i = m_new_config.find (key);
112     KeyValueRepository::const_iterator end = m_new_config.end ();
113 
114     if (i == end) {
115         i = m_config.find (key);
116         end = m_config.end ();
117     }
118 
119     if (i != end) {
120         *pStr = i->second;
121         return true;
122     }
123 
124     *pStr = String ("");
125     return false;
126 }
127 
128 // int
129 bool
read(const String & key,int * pl) const130 SimpleConfig::read (const String& key, int *pl) const
131 {
132     if (!valid () || !pl || key.empty()) return false;
133 
134     KeyValueRepository::const_iterator i = m_new_config.find (key);
135     KeyValueRepository::const_iterator end = m_new_config.end ();
136 
137     if (i == end || !i->second.length ()) {
138         i = m_config.find (key);
139         end = m_config.end ();
140     }
141 
142     if (i != end && i->second.length ()) {
143         *pl = strtol (i->second.c_str (), (char**) NULL, 10);
144         return true;
145     }
146 
147     *pl = 0;
148     return false;
149 }
150 
151 // double
152 bool
read(const String & key,double * val) const153 SimpleConfig::read (const String& key, double* val) const
154 {
155     if (!valid () || !val || key.empty()) return false;
156 
157     KeyValueRepository::const_iterator i = m_new_config.find (key);
158     KeyValueRepository::const_iterator end = m_new_config.end ();
159 
160     if (i == end || !i->second.length ()) {
161         i = m_config.find (key);
162         end = m_config.end ();
163     }
164 
165     if (i != end && i->second.length ()) {
166         *val = strtod (i->second.c_str (), (char**) NULL);
167         return true;
168     }
169 
170     *val = 0;
171     return false;
172 }
173 
174 // bool
175 bool
read(const String & key,bool * val) const176 SimpleConfig::read (const String& key, bool* val) const
177 {
178     if (!valid () || !val || key.empty()) return false;
179 
180     KeyValueRepository::const_iterator i = m_new_config.find (key);
181     KeyValueRepository::const_iterator end = m_new_config.end ();
182 
183     if (i == end || !i->second.length ()) {
184         i = m_config.find (key);
185         end = m_config.end ();
186     }
187 
188     if (i != end && i->second.length ()) {
189         if (i->second == "true" || i->second == "TRUE" || i->second == "True" ||
190             i->second == "1") {
191             *val = true;
192             return true;
193         } else if (i->second == "false" || i->second == "FALSE" || i->second == "False" ||
194             i->second == "0") {
195             *val = false;
196             return true;
197         }
198     }
199 
200     *val = false;
201     return false;
202 }
203 
204 //String list
205 bool
read(const String & key,std::vector<String> * val) const206 SimpleConfig::read (const String& key, std::vector <String>* val) const
207 {
208     if (!valid () || !val || key.empty()) return false;
209 
210     KeyValueRepository::const_iterator i = m_new_config.find (key);
211     KeyValueRepository::const_iterator end = m_new_config.end ();
212 
213     if (i == end) {
214         i = m_config.find (key);
215         end = m_config.end ();
216     }
217 
218     val->clear ();
219 
220     if (i != end) {
221         scim_split_string_list (*val, i->second, ',');
222         return true;
223     }
224 
225     return false;
226 }
227 
228 //int list
229 bool
read(const String & key,std::vector<int> * val) const230 SimpleConfig::read (const String& key, std::vector <int>* val) const
231 {
232     if (!valid () || !val || key.empty()) return false;
233 
234     KeyValueRepository::const_iterator i = m_new_config.find (key);
235     KeyValueRepository::const_iterator end = m_new_config.end ();
236 
237     if (i == end) {
238         i = m_config.find (key);
239         end = m_config.end ();
240     }
241 
242     val->clear();
243 
244     if (i != end) {
245         std::vector <String> vec;
246         scim_split_string_list (vec, i->second, ',');
247 
248         for (std::vector <String>::iterator j = vec.begin (); j != vec.end (); ++j) {
249             int result = strtol (j->c_str (), (char**)NULL, 10);
250             val->push_back (result);
251         }
252         return true;
253     }
254 
255     return false;
256 }
257 
258 // write the value (return true on success)
259 bool
write(const String & key,const String & value)260 SimpleConfig::write (const String& key, const String& value)
261 {
262     if (!valid () || key.empty()) return false;
263 
264     m_new_config [key] = value;
265 
266     remove_key_from_erased_list (key);
267 
268     m_need_reload = true;
269 
270     return true;
271 }
272 
273 bool
write(const String & key,int value)274 SimpleConfig::write (const String& key, int value)
275 {
276     if (!valid () || key.empty()) return false;
277 
278     char buf [256];
279 
280     snprintf (buf, 255, "%d", value);
281 
282     m_new_config [key] = String (buf);
283 
284     remove_key_from_erased_list (key);
285 
286     m_need_reload = true;
287 
288     return true;
289 }
290 
291 bool
write(const String & key,double value)292 SimpleConfig::write (const String& key, double value)
293 {
294     if (!valid () || key.empty()) return false;
295 
296     char buf [256];
297 
298     snprintf (buf, 255, "%lf", value);
299 
300     m_new_config [key] = String (buf);
301 
302     remove_key_from_erased_list (key);
303 
304     m_need_reload = true;
305 
306     return true;
307 }
308 
309 bool
write(const String & key,bool value)310 SimpleConfig::write (const String& key, bool value)
311 {
312     if (!valid () || key.empty()) return false;
313 
314     if (value)
315         m_new_config [key] = String ("true");
316     else
317         m_new_config [key] = String ("false");
318 
319     remove_key_from_erased_list (key);
320 
321     m_need_reload = true;
322 
323     return true;
324 }
325 
326 bool
write(const String & key,const std::vector<String> & value)327 SimpleConfig::write (const String& key, const std::vector <String>& value)
328 {
329     if (!valid () || key.empty()) return false;
330 
331     m_new_config [key] = scim_combine_string_list (value, ',');
332 
333     remove_key_from_erased_list (key);
334 
335     m_need_reload = true;
336 
337     return true;
338 }
339 
340 bool
write(const String & key,const std::vector<int> & value)341 SimpleConfig::write (const String& key, const std::vector <int>& value)
342 {
343     if (!valid () || key.empty()) return false;
344 
345     std::vector <String> vec;
346     char buf [256];
347 
348     for (std::vector <int>::const_iterator i = value.begin (); i != value.end (); ++i) {
349         snprintf (buf, 255, "%d", *i);
350         vec.push_back (String (buf));
351     }
352 
353     m_new_config [key] = scim_combine_string_list (vec, ',');
354 
355     remove_key_from_erased_list (key);
356 
357     m_need_reload = true;
358 
359     return true;
360 }
361 
362 // permanently writes all changes
363 bool
flush()364 SimpleConfig::flush()
365 {
366     if (!valid ()) return false;
367 
368     // If no config has been modified, then just return.
369     if (!m_new_config.size () && !m_erased_keys.size ())
370         return true;
371 
372     String userconf     = get_userconf_filename ();
373     String userconf_dir = get_userconf_dir ();
374 
375     if (access (userconf_dir.c_str (), R_OK | W_OK) != 0) {
376         mkdir (userconf_dir.c_str (), S_IRUSR | S_IWUSR | S_IXUSR);
377         if (access (userconf_dir.c_str (), R_OK | W_OK) != 0)
378             return false;
379     }
380 
381     if (userconf.length ()) {
382         // Reload config to ensure user made modification won't lost.
383         load_all_config ();
384 
385         std::ofstream os (userconf.c_str ());
386         if (!os) return false;
387 
388         KeyValueRepository::iterator i;
389         std::vector<String>::iterator j;
390 
391         // Merge new config with old ones.
392         for (i = m_new_config.begin (); i != m_new_config.end (); ++i)
393             m_config [i->first] = i->second;
394 
395         // Remove all erased keys.
396         for (j = m_erased_keys.begin (); j != m_erased_keys.end (); ++j) {
397             if ((i = m_config.find (*j)) != m_config.end ())
398                 m_config.erase (i);
399         }
400 
401         m_new_config.clear ();
402         m_erased_keys.clear ();
403 
404         gettimeofday (&m_update_timestamp, 0);
405 
406         char buf [128];
407         snprintf (buf, 128, "%lu:%lu", m_update_timestamp.tv_sec, m_update_timestamp.tv_usec);
408 
409         m_config [String (SCIM_CONFIG_UPDATE_TIMESTAMP)] = String (buf);
410 
411         save_config (os);
412         return true;
413     }
414 
415     return false;
416 }
417 
418 // delete entries
419 bool
erase(const String & key)420 SimpleConfig::erase (const String& key)
421 {
422     if (!valid ()) return false;
423 
424     KeyValueRepository::iterator i = m_new_config.find(key);
425     KeyValueRepository::iterator j = m_config.find(key);
426     bool ok = false;
427 
428     if (i != m_new_config.end ()) {
429         m_new_config.erase (i);
430         ok = true;
431     }
432 
433     if (j != m_config.end ()) {
434         m_config.erase (j);
435         ok = true;
436     }
437 
438     if (ok && std::find (m_erased_keys.begin (), m_erased_keys.end (), key) == m_erased_keys.end ())
439         m_erased_keys.push_back (key);
440 
441     m_need_reload = true;
442 
443     return ok;
444 }
445 
446 bool
reload()447 SimpleConfig::reload ()
448 {
449     if (!valid ()) return false;
450 
451     if (load_all_config ()) {
452         m_new_config.clear ();
453         m_erased_keys.clear ();
454         m_need_reload = true;
455     }
456 
457     if (m_need_reload) {
458         m_need_reload = false;
459         return ConfigBase::reload ();
460     }
461 
462     return false;
463 }
464 
465 String
get_sysconf_dir()466 SimpleConfig::get_sysconf_dir ()
467 {
468     return String (SCIM_SYSCONFDIR) +
469            String (SCIM_PATH_DELIM_STRING) +
470            String ("scim");
471 }
472 
473 String
get_userconf_dir()474 SimpleConfig::get_userconf_dir ()
475 {
476     return scim_get_user_data_dir ();
477 }
478 
479 String
get_sysconf_filename()480 SimpleConfig::get_sysconf_filename ()
481 {
482     return get_sysconf_dir () +
483            String (SCIM_PATH_DELIM_STRING) +
484            String ("config");
485 }
486 
487 String
get_userconf_filename()488 SimpleConfig::get_userconf_filename ()
489 {
490     return get_userconf_dir () +
491            String (SCIM_PATH_DELIM_STRING) +
492            String ("config");
493 }
494 
495 String
trim_blank(const String & str)496 SimpleConfig::trim_blank (const String &str)
497 {
498     String::size_type begin, len;
499 
500     begin = str.find_first_not_of (" \t\n\v");
501 
502     if (begin == String::npos)
503         return String ();
504 
505     len = str.find_last_not_of (" \t\n\v") - begin + 1;
506 
507     return str.substr (begin, len);
508 }
509 
510 String
get_param_portion(const String & str)511 SimpleConfig::get_param_portion (const String &str)
512 {
513     String::size_type begin = str.find_first_of (" \t\n\v=");
514 
515     if (begin == String::npos) return str;
516 
517     return str.substr (0, begin);
518 }
519 
520 String
get_value_portion(const String & str)521 SimpleConfig::get_value_portion (const String &str)
522 {
523     String::size_type begin = str.find_first_of ("=");
524 
525     if (begin == String::npos || (begin + 1) == str.length ()) return String ("");
526 
527     return trim_blank (str.substr (begin + 1, String::npos));
528 }
529 
530 void
parse_config(std::istream & is,KeyValueRepository & config)531 SimpleConfig::parse_config (std::istream &is, KeyValueRepository &config)
532 {
533     char *conf_line = new char [SCIM_MAX_CONFIG_LINE_LENGTH];
534 
535     while (!is.eof()) {
536         is.getline(conf_line, SCIM_MAX_CONFIG_LINE_LENGTH);
537         if (!is.eof()) {
538             String normalized_line = trim_blank(conf_line);
539 
540             if ((normalized_line.find_first_of("#") > 0) && (normalized_line.length() != 0)) {
541                 if (normalized_line.find_first_of("=") == String::npos) {
542                     SCIM_DEBUG_CONFIG(2) << " Invalid config line : " << normalized_line << "\n";
543                     continue;
544                 }
545 
546                 if (normalized_line[0] == '=') {
547                     SCIM_DEBUG_CONFIG(2) << " Invalid config line : " << normalized_line << "\n";
548                     continue;
549                 }
550 
551                 String param = get_param_portion(normalized_line);
552                 KeyValueRepository::iterator i = config.find(param);
553 
554                 if (i != config.end()) {
555                     SCIM_DEBUG_CONFIG(2) << " Config entry " << normalized_line << " has been read.\n";
556                 } else {
557                     String value = get_value_portion (normalized_line);
558                     config [param] = value;
559                     SCIM_DEBUG_CONFIG(2) << " Config entry " << param << "=" << value << " is successfully read.\n";
560                 }
561             }
562         }
563     }
564 
565     delete [] conf_line;
566 }
567 
568 void
save_config(std::ostream & os)569 SimpleConfig::save_config (std::ostream &os)
570 {
571     KeyValueRepository::iterator i;
572     for (i = m_config.begin (); i != m_config.end (); ++i) {
573         os << i->first << " = " << i->second << "\n";
574     }
575 }
576 
577 bool
load_all_config()578 SimpleConfig::load_all_config ()
579 {
580     String sysconf = get_sysconf_filename ();
581     String userconf = get_userconf_filename ();
582 
583     KeyValueRepository config;
584 
585     if (userconf.length ()) {
586         std::ifstream is (userconf.c_str ());
587         if (is) {
588             SCIM_DEBUG_CONFIG(1) << "Parsing user config file: "
589                                  << userconf << "\n";
590             parse_config (is, config);
591         }
592     }
593 
594     if (sysconf.length ()) {
595         std::ifstream is (sysconf.c_str ());
596         if (is) {
597             SCIM_DEBUG_CONFIG(1) << "Parsing system config file: "
598                                  << sysconf << "\n";
599             parse_config (is, config);
600         }
601     }
602 
603     if (!m_config.size () || (m_update_timestamp.tv_sec == 0 && m_update_timestamp.tv_usec == 0)) {
604         m_config.swap (config);
605         gettimeofday (&m_update_timestamp, 0);
606         return true;
607     }
608 
609     KeyValueRepository::iterator it = config.find (String (SCIM_CONFIG_UPDATE_TIMESTAMP));
610 
611     if (it != config.end ()) {
612         std::vector <String> strs;
613         if (scim_split_string_list (strs, it->second, ':') == 2) {
614             time_t sec = (time_t) strtol (strs [0].c_str (), 0, 10);
615             suseconds_t usec = (suseconds_t) strtol (strs [1].c_str (), 0, 10);
616 
617             // The config file is newer, so load it.
618             if (m_update_timestamp.tv_sec < sec || (m_update_timestamp.tv_sec == sec && m_update_timestamp.tv_usec < usec)) {
619                 m_config.swap (config);
620                 m_update_timestamp.tv_sec = (time_t) sec;
621                 m_update_timestamp.tv_usec = (suseconds_t) usec;
622                 return true;
623             }
624         }
625     }
626     return false;
627 }
628 
629 void
remove_key_from_erased_list(const String & key)630 SimpleConfig::remove_key_from_erased_list (const String &key)
631 {
632     std::vector <String>::iterator it = std::find (m_erased_keys.begin (), m_erased_keys.end (), key);
633 
634     if (it != m_erased_keys.end ())
635         m_erased_keys.erase (it);
636 }
637 
638 } // namespace scim
639 
640 /*
641 vi:ts=4:nowrap:ai:expandtab
642 */
643