Git init
[framework/uifw/isf.git] / ism / modules / config / scim_simple_config.cpp
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" {
55     void scim_module_init (void)
56     {
57         SCIM_DEBUG_CONFIG(1) << "Initializing Simple Config module...\n";
58     }
59     
60     void scim_module_exit (void)
61     {
62         SCIM_DEBUG_CONFIG(1) << "Exiting Simple Config module...\n";
63     }
64
65     void scim_config_module_init ()
66     {
67         SCIM_DEBUG_CONFIG(1) << "Initializing Simple Config module (more)...\n";
68     }
69
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
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
88 SimpleConfig::~SimpleConfig ()
89 {
90     flush ();
91 }
92
93 bool
94 SimpleConfig::valid () const
95 {
96     return ConfigBase::valid();
97 }
98
99 String
100 SimpleConfig::get_name () const
101 {
102     return "simple";
103 }
104
105 // String
106 bool
107 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
130 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
153 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
176 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
206 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
230 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
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
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
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
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
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
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
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 | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
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
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
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
466 SimpleConfig::get_sysconf_dir ()
467 {
468     return String (SCIM_SYSCONFDIR) +
469            String (SCIM_PATH_DELIM_STRING) +
470            String ("scim");
471 }
472
473 String
474 SimpleConfig::get_userconf_dir ()
475 {
476     return scim_get_user_data_dir ();
477 }
478
479 String
480 SimpleConfig::get_sysconf_filename ()
481 {
482     return get_sysconf_dir () +
483            String (SCIM_PATH_DELIM_STRING) +
484            String ("config");
485 }
486
487 String
488 SimpleConfig::get_userconf_filename ()
489 {
490     return get_userconf_dir () +
491            String (SCIM_PATH_DELIM_STRING) +
492            String ("config");
493 }
494
495 String
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
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
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
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
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
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
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 */