2 settings.c : Irssi settings
4 Copyright (C) 1999 Timo Sirainen
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "lib-config/iconfig.h"
28 #include "default-config.h"
32 CONFIG_REC *mainconfig;
34 static GString *last_errors;
35 static char *last_config_error_msg;
36 static GSList *last_invalid_modules;
37 static int fe_initialized;
38 static int config_changed; /* FIXME: remove after .98 (unless needed again) */
40 static GHashTable *settings;
41 static int timeout_tag;
43 static int config_last_modifycounter;
44 static time_t config_last_mtime;
45 static long config_last_size;
46 static unsigned int config_last_checksum;
48 static SETTINGS_REC *settings_find(const char *key)
52 g_return_val_if_fail(key != NULL, NULL);
54 rec = g_hash_table_lookup(settings, key);
56 g_warning("settings_get_default_str(%s) : "
57 "unknown setting", key);
64 const char *settings_get_str(const char *key)
67 CONFIG_NODE *setnode, *node;
69 rec = settings_find(key);
70 g_return_val_if_fail(rec != NULL, NULL);
72 setnode = iconfig_node_traverse("settings", FALSE);
76 node = config_node_section(setnode, rec->module, -1);
77 return node == NULL ? rec->def :
78 config_node_get_str(node, key, rec->def);
81 int settings_get_int(const char *key)
84 CONFIG_NODE *setnode, *node;
87 rec = settings_find(key);
88 g_return_val_if_fail(rec != NULL, 0);
89 def = GPOINTER_TO_INT(rec->def);
91 setnode = iconfig_node_traverse("settings", FALSE);
95 node = config_node_section(setnode, rec->module, -1);
96 return node == NULL ? def :
97 config_node_get_int(node, key, def);
100 int settings_get_bool(const char *key)
103 CONFIG_NODE *setnode, *node;
106 rec = settings_find(key);
107 g_return_val_if_fail(rec != NULL, 0);
108 def = GPOINTER_TO_INT(rec->def);
110 setnode = iconfig_node_traverse("settings", FALSE);
114 node = config_node_section(setnode, rec->module, -1);
115 return node == NULL ? def :
116 config_node_get_bool(node, key, def);
119 void settings_add_str_module(const char *module, const char *section,
120 const char *key, const char *def)
124 g_return_if_fail(key != NULL);
125 g_return_if_fail(section != NULL);
127 rec = g_hash_table_lookup(settings, key);
128 g_return_if_fail(rec == NULL);
130 rec = g_new0(SETTINGS_REC, 1);
131 rec->module = g_strdup(module);
132 rec->key = g_strdup(key);
133 rec->section = g_strdup(section);
134 rec->def = def == NULL ? NULL : g_strdup(def);
136 g_hash_table_insert(settings, rec->key, rec);
139 void settings_add_int_module(const char *module, const char *section,
140 const char *key, int def)
144 g_return_if_fail(key != NULL);
145 g_return_if_fail(section != NULL);
147 rec = g_hash_table_lookup(settings, key);
148 g_return_if_fail(rec == NULL);
150 rec = g_new0(SETTINGS_REC, 1);
151 rec->module = g_strdup(module);
152 rec->type = SETTING_TYPE_INT;
153 rec->key = g_strdup(key);
154 rec->section = g_strdup(section);
155 rec->def = GINT_TO_POINTER(def);
157 g_hash_table_insert(settings, rec->key, rec);
160 void settings_add_bool_module(const char *module, const char *section,
161 const char *key, int def)
165 g_return_if_fail(key != NULL);
166 g_return_if_fail(section != NULL);
168 rec = g_hash_table_lookup(settings, key);
169 g_return_if_fail(rec == NULL);
171 rec = g_new0(SETTINGS_REC, 1);
172 rec->module = g_strdup(module);
173 rec->type = SETTING_TYPE_BOOLEAN;
174 rec->key = g_strdup(key);
175 rec->section = g_strdup(section);
176 rec->def = GINT_TO_POINTER(def);
178 g_hash_table_insert(settings, rec->key, rec);
181 static void settings_destroy(SETTINGS_REC *rec)
183 if (rec->type == SETTING_TYPE_STRING)
184 g_free_not_null(rec->def);
186 g_free(rec->section);
191 void settings_remove(const char *key)
195 g_return_if_fail(key != NULL);
197 rec = g_hash_table_lookup(settings, key);
198 if (rec == NULL) return;
200 g_hash_table_remove(settings, key);
201 settings_destroy(rec);
204 static int settings_remove_hash(const char *key, SETTINGS_REC *rec,
207 if (strcmp(rec->module, module) == 0) {
208 settings_destroy(rec);
215 void settings_remove_module(const char *module)
217 g_hash_table_foreach_remove(settings,
218 (GHRFunc) settings_remove_hash,
222 static CONFIG_NODE *settings_get_node(const char *key)
227 g_return_val_if_fail(key != NULL, NULL);
229 rec = g_hash_table_lookup(settings, key);
230 g_return_val_if_fail(rec != NULL, NULL);
232 node = iconfig_node_traverse("settings", TRUE);
233 return config_node_section(node, rec->module, NODE_TYPE_BLOCK);
236 void settings_set_str(const char *key, const char *value)
238 iconfig_node_set_str(settings_get_node(key), key, value);
241 void settings_set_int(const char *key, int value)
243 iconfig_node_set_int(settings_get_node(key), key, value);
246 void settings_set_bool(const char *key, int value)
248 iconfig_node_set_bool(settings_get_node(key), key, value);
251 int settings_get_type(const char *key)
255 g_return_val_if_fail(key != NULL, -1);
257 rec = g_hash_table_lookup(settings, key);
258 return rec == NULL ? -1 : rec->type;
261 /* Get the record of the setting */
262 SETTINGS_REC *settings_get_record(const char *key)
264 g_return_val_if_fail(key != NULL, NULL);
266 return g_hash_table_lookup(settings, key);
269 static void sig_init_finished(void)
271 fe_initialized = TRUE;
272 if (last_errors != NULL) {
273 signal_emit("settings errors", 1, last_errors->str);
274 g_string_free(last_errors, TRUE);
277 if (last_config_error_msg != NULL) {
278 signal_emit("gui dialog", 2, "error", last_config_error_msg);
279 g_free_and_null(last_config_error_msg);
282 if (config_changed) {
283 /* some backwards compatibility changes were made to
284 config file, reload it */
285 signal_emit("setup changed", 0);
289 /* FIXME: remove after 0.7.98 - only for backward compatibility */
290 static void settings_move(SETTINGS_REC *rec, char *value)
292 CONFIG_NODE *setnode, *node;
294 setnode = iconfig_node_traverse("settings", TRUE);
295 node = config_node_section(setnode, rec->module, NODE_TYPE_BLOCK);
297 iconfig_node_set_str(node, rec->key, value);
298 iconfig_node_set_str(setnode, rec->key, NULL);
300 config_changed = TRUE;
303 static void settings_clean_invalid_module(const char *module)
309 node = iconfig_node_traverse("settings", FALSE);
310 if (node == NULL) return;
312 node = config_node_section(node, module, -1);
313 if (node == NULL) return;
315 for (tmp = node->value; tmp != NULL; tmp = next) {
316 CONFIG_NODE *subnode = tmp->data;
319 set = g_hash_table_lookup(settings, subnode->key);
320 if (set == NULL || strcmp(set->module, module) != 0)
321 iconfig_node_remove(node, subnode);
325 /* remove all invalid settings from config file. works only with the
326 modules that have already called settings_check() */
327 void settings_clean_invalid(void)
329 while (last_invalid_modules != NULL) {
330 char *module = last_invalid_modules->data;
332 settings_clean_invalid_module(module);
335 last_invalid_modules =
336 g_slist_remove(last_invalid_modules, module);
340 /* verify that all settings in config file for `module' are actually found
342 void settings_check_module(const char *module)
350 g_return_if_fail(module != NULL);
352 node = iconfig_node_traverse("settings", FALSE);
354 /* FIXME: remove after 0.7.98 */
355 for (tmp = node->value; tmp != NULL; tmp = next) {
356 CONFIG_NODE *node = tmp->data;
359 if (node->type != NODE_TYPE_KEY)
361 set = g_hash_table_lookup(settings, node->key);
363 settings_move(set, node->value);
366 node = node == NULL ? NULL : config_node_section(node, module, -1);
367 if (node == NULL) return;
369 errors = g_string_new(NULL);
370 g_string_sprintf(errors, "Unknown settings in configuration "
371 "file for module %s:", module);
374 for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
377 set = g_hash_table_lookup(settings, node->key);
378 if (set == NULL || strcmp(set->module, module) != 0) {
379 g_string_sprintfa(errors, " %s", node->key);
384 if (gslist_find_icase_string(last_invalid_modules,
386 /* mark this module having invalid settings */
387 last_invalid_modules =
388 g_slist_append(last_invalid_modules,
392 signal_emit("settings errors", 1, errors->str);
394 if (last_errors == NULL)
395 last_errors = g_string_new(NULL);
397 g_string_append_c(last_errors, '\n');
398 g_string_append(last_errors, errors->str);
401 g_string_free(errors, TRUE);
404 static int settings_compare(SETTINGS_REC *v1, SETTINGS_REC *v2)
406 return strcmp(v1->section, v2->section);
409 static void settings_hash_get(const char *key, SETTINGS_REC *rec,
412 *list = g_slist_insert_sorted(*list, rec,
413 (GCompareFunc) settings_compare);
416 GSList *settings_get_sorted(void)
421 g_hash_table_foreach(settings, (GHFunc) settings_hash_get, &list);
427 /* if we get SIGTERM after this, just die instead of coming back here. */
428 signal(SIGTERM, SIG_DFL);
430 /* quit from all servers too.. */
431 signal_emit("command quit", 1, "");
437 /* Yes, this is my own stupid checksum generator, some "real" algorithm
438 would be nice but would just take more space without much real benefit */
439 static unsigned int file_checksum(const char *fname)
443 unsigned int checksum = 0;
445 f = open(fname, O_RDONLY);
446 if (f == -1) return 0;
449 while ((ret = read(f, buf, sizeof(buf))) > 0) {
451 checksum += buf[ret] << ((n++ & 3)*8);
457 static void irssi_config_save_state(const char *fname)
461 g_return_if_fail(fname != NULL);
463 if (stat(fname, &statbuf) != 0)
466 /* save modify time, file size and checksum */
467 config_last_mtime = statbuf.st_mtime;
468 config_last_size = statbuf.st_size;
469 config_last_checksum = file_checksum(fname);
472 int irssi_config_is_changed(const char *fname)
477 fname = mainconfig->fname;
479 if (stat(fname, &statbuf) != 0)
482 return config_last_mtime != statbuf.st_mtime &&
483 (config_last_size != statbuf.st_size ||
484 config_last_checksum != file_checksum(fname));
487 static CONFIG_REC *parse_configfile(const char *fname)
494 real_fname = fname != NULL ? g_strdup(fname) :
495 g_strdup_printf("%s"G_DIR_SEPARATOR_S".silc"
496 G_DIR_SEPARATOR_S"config", g_get_home_dir());
498 if (stat(real_fname, &statbuf) == 0)
501 /* user configuration file not found, use the default one
503 path = SYSCONFDIR"/irssi/config";
504 if (stat(path, &statbuf) != 0) {
505 /* no configuration file in sysconfdir ..
506 use the build-in configuration */
511 config = config_open(path, -1);
512 if (config == NULL) {
513 last_config_error_msg =
514 g_strdup_printf("Error opening configuration file %s: %s",
515 path, g_strerror(errno));
516 config = config_open(NULL, -1);
520 config_parse(config);
522 config_parse_data(config, default_config, "internal");
524 config_change_file_name(config, real_fname, 0660);
525 irssi_config_save_state(real_fname);
530 static void init_configfile(void)
535 str = g_strdup_printf("%s"G_DIR_SEPARATOR_S".silc", g_get_home_dir());
536 if (stat(str, &statbuf) != 0) {
537 /* ~/.irssi not found, create it. */
538 if (mkpath(str, 0700) != 0) {
539 g_error("Couldn't create %s directory", str);
541 } else if (!S_ISDIR(statbuf.st_mode)) {
542 g_error("%s is not a directory.\n"
543 "You should remove it with command: rm ~/.irssi", str);
547 mainconfig = parse_configfile(NULL);
548 config_last_modifycounter = mainconfig->modifycounter;
551 if (config_last_error(mainconfig) != NULL) {
552 last_config_error_msg =
553 g_strdup_printf("Ignored errors in configuration "
555 config_last_error(mainconfig));
558 signal(SIGTERM, sig_term);
561 int settings_reread(const char *fname)
563 CONFIG_REC *tempconfig;
566 if (fname == NULL) fname = "~/.silc/config";
568 str = convert_home(fname);
569 tempconfig = parse_configfile(str);
572 if (tempconfig == NULL) {
573 signal_emit("gui dialog", 2, "error", g_strerror(errno));
577 if (config_last_error(tempconfig) != NULL) {
578 str = g_strdup_printf("Errors in configuration file:\n%s",
579 config_last_error(tempconfig));
580 signal_emit("gui dialog", 2, "error", str);
583 config_close(tempconfig);
587 config_close(mainconfig);
588 mainconfig = tempconfig;
589 config_last_modifycounter = mainconfig->modifycounter;
591 signal_emit("setup changed", 0);
592 signal_emit("setup reread", 0);
596 int settings_save(const char *fname)
602 fname = mainconfig->fname;
604 error = config_write(mainconfig, fname, 0660) != 0;
605 irssi_config_save_state(fname);
606 config_last_modifycounter = mainconfig->modifycounter;
608 str = g_strdup_printf("Couldn't save configuration file: %s",
609 config_last_error(mainconfig));
610 signal_emit("gui dialog", 2, "error", str);
616 static void sig_autosave(void)
620 if (!settings_get_bool("settings_autosave") ||
621 config_last_modifycounter == mainconfig->modifycounter)
624 if (!irssi_config_is_changed(NULL))
627 fname = g_strconcat(mainconfig->fname, ".autosave", NULL);
628 str = g_strdup_printf("Configuration file was modified "
629 "while irssi was running. Saving "
630 "configuration to file '%s' instead. "
631 "Use /SAVE or /RELOAD to get rid of "
632 "this message.", fname);
633 signal_emit("gui dialog", 2, "warning", str);
636 settings_save(fname);
641 void settings_init(void)
643 settings = g_hash_table_new((GHashFunc) g_str_hash,
644 (GCompareFunc) g_str_equal);
647 last_config_error_msg = NULL;
648 last_invalid_modules = NULL;
649 fe_initialized = FALSE;
650 config_changed = FALSE;
652 config_last_mtime = 0;
653 config_last_modifycounter = 0;
656 settings_add_bool("misc", "settings_autosave", TRUE);
657 timeout_tag = g_timeout_add(1000*60*60, (GSourceFunc) sig_autosave, NULL);
658 signal_add("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
659 signal_add("gui exit", (SIGNAL_FUNC) sig_autosave);
662 static void settings_hash_free(const char *key, SETTINGS_REC *rec)
664 settings_destroy(rec);
667 void settings_deinit(void)
669 g_source_remove(timeout_tag);
670 signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
671 signal_remove("gui exit", (SIGNAL_FUNC) sig_autosave);
673 g_slist_foreach(last_invalid_modules, (GFunc) g_free, NULL);
674 g_slist_free(last_invalid_modules);
676 g_hash_table_foreach(settings, (GHFunc) settings_hash_free, NULL);
677 g_hash_table_destroy(settings);
679 if (mainconfig != NULL) config_close(mainconfig);