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