Bug Summary

File:core/settings.c
Location:line 169, column 2
Description:Uninitialized or undefined return value returned to caller.

Annotated Source Code

1/*
2 settings.c : Irssi settings
3
4 Copyright (C) 1999 Timo Sirainen
5
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.
10
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.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20
21#include "module.h"
22#include "signals.h"
23#include "commands.h"
24#include "levels.h"
25#include "misc.h"
26
27#include "lib-config/iconfig.h"
28#include "recode.h"
29#include "settings.h"
30#include "default-config.h"
31
32#include <signal.h>
33
34#define SETTINGS_AUTOSAVE_TIMEOUT(1000*60*60) (1000*60*60) /* 1 hour */
35
36CONFIG_REC *mainconfig;
37
38static GString *last_errors;
39static GSList *last_invalid_modules;
40static int fe_initialized;
41static int config_changed; /* FIXME: remove after .98 (unless needed again) */
42
43static GHashTable *settings;
44static int timeout_tag;
45
46static int config_last_modifycounter;
47static time_t config_last_mtime;
48static long config_last_size;
49static unsigned int config_last_checksum;
50
51static SETTINGS_REC *settings_get(const char *key, SettingType type)
52{
53 SETTINGS_REC *rec;
54
55 g_return_val_if_fail(key != NULL, NULL)do{ if (key != ((void *)0)) { } else { g_return_if_fail_warning
(((gchar*) 0), __PRETTY_FUNCTION__, "key != NULL"); return (
((void *)0)); }; }while (0)
;
56
57 rec = g_hash_table_lookup(settings, key);
58 if (rec == NULL((void *)0)) {
59 g_warning("settings_get(%s) : not found", key)g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "settings_get(%s) : not found"
, key)
;
60 return NULL((void *)0);
61 }
62 if (type != -1 && rec->type != type) {
63 g_warning("settings_get(%s) : invalid type", key)g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "settings_get(%s) : invalid type"
, key)
;
64 return NULL((void *)0);
65 }
66
67 return rec;
68}
69
70static const char *
71settings_get_str_type(const char *key, SettingType type)
72{
73 SETTINGS_REC *rec;
74 CONFIG_NODE *node;
75
76 rec = settings_get(key, type);
77 if (rec == NULL((void *)0)) return NULL((void *)0);
78
79 node = iconfig_node_traverse("settings", FALSE)config_node_traverse(mainconfig, "settings", (0));
80 node = node == NULL((void *)0) ? NULL((void *)0) : config_node_section(node, rec->module, -1);
81
82 return node == NULL((void *)0) ? rec->default_value.v_string :
83 config_node_get_str(node, key, rec->default_value.v_string);
84}
85
86const char *settings_get_str(const char *key)
87{
88 return settings_get_str_type(key, -1);
89}
90
91int settings_get_int(const char *key)
92{
93 SETTINGS_REC *rec;
94 CONFIG_NODE *node;
95
96 rec = settings_get(key, SETTING_TYPE_INT);
97 if (rec == NULL((void *)0)) return 0;
98
99 node = iconfig_node_traverse("settings", FALSE)config_node_traverse(mainconfig, "settings", (0));
100 node = node == NULL((void *)0) ? NULL((void *)0) : config_node_section(node, rec->module, -1);
101
102 return node == NULL((void *)0) ? rec->default_value.v_int :
103 config_node_get_int(node, key, rec->default_value.v_int);
104}
105
106int settings_get_bool(const char *key)
107{
108 SETTINGS_REC *rec;
109 CONFIG_NODE *node;
110
111 rec = settings_get(key, SETTING_TYPE_BOOLEAN);
112 if (rec == NULL((void *)0)) return FALSE(0);
113
114 node = iconfig_node_traverse("settings", FALSE)config_node_traverse(mainconfig, "settings", (0));
115 node = node == NULL((void *)0) ? NULL((void *)0) : config_node_section(node, rec->module, -1);
116
117 return node == NULL((void *)0) ? rec->default_value.v_bool :
118 config_node_get_bool(node, key, rec->default_value.v_bool);
119}
120
121int settings_get_time(const char *key)
122{
123 const char *str;
124 int msecs;
125
126 str = settings_get_str_type(key, SETTING_TYPE_TIME);
127 if (str != NULL((void *)0) && !parse_time_interval(str, &msecs))
128 g_warning("settings_get_time(%s) : Invalid time '%s'", key, str)g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "settings_get_time(%s) : Invalid time '%s'"
, key, str)
;
129 return str == NULL((void *)0) ? 0 : msecs;
130}
131
132int settings_get_level(const char *key)
133{
134 const char *str;
135
136 str = settings_get_str_type(key, SETTING_TYPE_LEVEL);
137 return str == NULL((void *)0) ? 0 : level2bits(str, NULL((void *)0));
138}
139
140int settings_get_size(const char *key)
141{
142 const char *str;
143 int bytes;
144
145 str = settings_get_str_type(key, SETTING_TYPE_SIZE);
146 if (str != NULL((void *)0) && !parse_size(str, &bytes))
147 g_warning("settings_get_size(%s) : Invalid size '%s'", key, str)g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "settings_get_size(%s) : Invalid size '%s'"
, key, str)
;
148 return str == NULL((void *)0) ? 0 : bytes;
149}
150
151char *settings_get_print(SETTINGS_REC *rec)
152{
153 char *value;
154
155 switch(rec->type) {
[1] 'Default' branch taken. Execution continues on line 169
156 case SETTING_TYPE_BOOLEAN:
157 value = g_strdup(settings_get_bool(rec->key) ? "ON" : "OFF");
158 break;
159 case SETTING_TYPE_INT:
160 value = g_strdup_printf("%d", settings_get_int(rec->key));
161 break;
162 case SETTING_TYPE_STRING:
163 case SETTING_TYPE_TIME:
164 case SETTING_TYPE_LEVEL:
165 case SETTING_TYPE_SIZE:
166 value = g_strdup(settings_get_str(rec->key));
167 break;
168 }
169 return value;
[2] Uninitialized or undefined return value returned to caller
170}
171
172static void settings_add(const char *module, const char *section,
173 const char *key, SettingType type,
174 const SettingValue *default_value)
175{
176 SETTINGS_REC *rec;
177
178 g_return_if_fail(key != NULL)do{ if (key != ((void *)0)) { } else { g_return_if_fail_warning
(((gchar*) 0), __PRETTY_FUNCTION__, "key != NULL"); return; }
; }while (0)
;
179 g_return_if_fail(section != NULL)do{ if (section != ((void *)0)) { } else { g_return_if_fail_warning
(((gchar*) 0), __PRETTY_FUNCTION__, "section != NULL"); return
; }; }while (0)
;
180
181 rec = g_hash_table_lookup(settings, key);
182 if (rec != NULL((void *)0)) {
183 /* Already exists, make sure it's correct type */
184 if (rec->type != type) {
185 g_warning("Trying to add already existing "g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "Trying to add already existing "
"setting '%s' with different type.", key)
186 "setting '%s' with different type.", key)g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "Trying to add already existing "
"setting '%s' with different type.", key)
;
187 return;
188 }
189 rec->refcount++;
190 } else {
191 rec = g_new(SETTINGS_REC, 1)((SETTINGS_REC *) g_malloc (((gsize) sizeof (SETTINGS_REC)) *
((gsize) (1))))
;
192 rec->refcount = 1;
193 rec->module = g_strdup(module);
194 rec->key = g_strdup(key);
195 rec->section = g_strdup(section);
196 rec->type = type;
197
198 rec->default_value = *default_value;
199 g_hash_table_insert(settings, rec->key, rec);
200 }
201}
202
203void settings_add_str_module(const char *module, const char *section,
204 const char *key, const char *def)
205{
206 SettingValue default_value;
207
208 memset(&default_value, 0, sizeof(default_value));
209 default_value.v_string = g_strdup(def);
210 settings_add(module, section, key, SETTING_TYPE_STRING, &default_value);
211}
212
213void settings_add_int_module(const char *module, const char *section,
214 const char *key, int def)
215{
216 SettingValue default_value;
217
218 memset(&default_value, 0, sizeof(default_value));
219 default_value.v_int = def;
220 settings_add(module, section, key, SETTING_TYPE_INT, &default_value);
221}
222
223void settings_add_bool_module(const char *module, const char *section,
224 const char *key, int def)
225{
226 SettingValue default_value;
227
228 memset(&default_value, 0, sizeof(default_value));
229 default_value.v_bool = def;
230 settings_add(module, section, key, SETTING_TYPE_BOOLEAN,
231 &default_value);
232}
233
234void settings_add_time_module(const char *module, const char *section,
235 const char *key, const char *def)
236{
237 SettingValue default_value;
238
239 memset(&default_value, 0, sizeof(default_value));
240 default_value.v_string = g_strdup(def);
241 settings_add(module, section, key, SETTING_TYPE_TIME, &default_value);
242}
243
244void settings_add_level_module(const char *module, const char *section,
245 const char *key, const char *def)
246{
247 SettingValue default_value;
248
249 memset(&default_value, 0, sizeof(default_value));
250 default_value.v_string = g_strdup(def);
251 settings_add(module, section, key, SETTING_TYPE_LEVEL, &default_value);
252}
253
254void settings_add_size_module(const char *module, const char *section,
255 const char *key, const char *def)
256{
257 SettingValue default_value;
258
259 memset(&default_value, 0, sizeof(default_value));
260 default_value.v_string = g_strdup(def);
261 settings_add(module, section, key, SETTING_TYPE_SIZE, &default_value);
262}
263
264static void settings_destroy(SETTINGS_REC *rec)
265{
266 if (rec->type != SETTING_TYPE_INT &&
267 rec->type != SETTING_TYPE_BOOLEAN)
268 g_free(rec->default_value.v_string);
269 g_free(rec->module);
270 g_free(rec->section);
271 g_free(rec->key);
272 g_free(rec);
273}
274
275static void settings_unref(SETTINGS_REC *rec, int remove_hash)
276{
277 if (--rec->refcount == 0) {
278 if (remove_hash)
279 g_hash_table_remove(settings, rec->key);
280 settings_destroy(rec);
281 }
282}
283
284void settings_remove(const char *key)
285{
286 SETTINGS_REC *rec;
287
288 g_return_if_fail(key != NULL)do{ if (key != ((void *)0)) { } else { g_return_if_fail_warning
(((gchar*) 0), __PRETTY_FUNCTION__, "key != NULL"); return; }
; }while (0)
;
289
290 rec = g_hash_table_lookup(settings, key);
291 if (rec != NULL((void *)0))
292 settings_unref(rec, TRUE(!(0)));
293}
294
295static int settings_remove_hash(const char *key, SETTINGS_REC *rec,
296 const char *module)
297{
298 if (strcmp(rec->module, module) == 0) {
299 settings_unref(rec, FALSE(0));
300 return TRUE(!(0));
301 }
302
303 return FALSE(0);
304}
305
306void settings_remove_module(const char *module)
307{
308 g_hash_table_foreach_remove(settings,
309 (GHRFunc) settings_remove_hash,
310 (void *) module);
311}
312
313static CONFIG_NODE *settings_get_node(const char *key)
314{
315 SETTINGS_REC *rec;
316 CONFIG_NODE *node;
317
318 g_return_val_if_fail(key != NULL, NULL)do{ if (key != ((void *)0)) { } else { g_return_if_fail_warning
(((gchar*) 0), __PRETTY_FUNCTION__, "key != NULL"); return (
((void *)0)); }; }while (0)
;
319
320 rec = g_hash_table_lookup(settings, key);
321 if (rec == NULL((void *)0)) {
322 g_warning("Changing unknown setting '%s'", key)g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "Changing unknown setting '%s'"
, key)
;
323 return NULL((void *)0);
324 }
325
326 node = iconfig_node_traverse("settings", TRUE)config_node_traverse(mainconfig, "settings", (!(0)));
327 return config_node_section(node, rec->module, NODE_TYPE_BLOCK);
328}
329
330void settings_set_str(const char *key, const char *value)
331{
332 iconfig_node_set_str(settings_get_node(key), key, value)config_node_set_str(mainconfig, settings_get_node(key), key, value
)
;
333}
334
335void settings_set_int(const char *key, int value)
336{
337 iconfig_node_set_int(settings_get_node(key), key, value)config_node_set_int(mainconfig, settings_get_node(key), key, value
)
;
338}
339
340void settings_set_bool(const char *key, int value)
341{
342 iconfig_node_set_bool(settings_get_node(key), key, value)config_node_set_bool(mainconfig, settings_get_node(key), key,
value)
;
343}
344
345int settings_set_time(const char *key, const char *value)
346{
347 int msecs;
348
349 if (!parse_time_interval(value, &msecs))
350 return FALSE(0);
351
352 iconfig_node_set_str(settings_get_node(key), key, value)config_node_set_str(mainconfig, settings_get_node(key), key, value
)
;
353 return TRUE(!(0));
354}
355
356int settings_set_level(const char *key, const char *value)
357{
358 int iserror;
359
360 (void)level2bits(value, &iserror);
361 if (iserror)
362 return FALSE(0);
363
364 iconfig_node_set_str(settings_get_node(key), key, value)config_node_set_str(mainconfig, settings_get_node(key), key, value
)
;
365 return TRUE(!(0));
366}
367
368int settings_set_size(const char *key, const char *value)
369{
370 int size;
371
372 if (!parse_size(value, &size))
373 return FALSE(0);
374
375 iconfig_node_set_str(settings_get_node(key), key, value)config_node_set_str(mainconfig, settings_get_node(key), key, value
)
;
376 return TRUE(!(0));
377}
378
379SettingType settings_get_type(const char *key)
380{
381 SETTINGS_REC *rec;
382
383 g_return_val_if_fail(key != NULL, -1)do{ if (key != ((void *)0)) { } else { g_return_if_fail_warning
(((gchar*) 0), __PRETTY_FUNCTION__, "key != NULL"); return (
-1); }; }while (0)
;
384
385 rec = g_hash_table_lookup(settings, key);
386 return rec == NULL((void *)0) ? -1 : rec->type;
387}
388
389/* Get the record of the setting */
390SETTINGS_REC *settings_get_record(const char *key)
391{
392 g_return_val_if_fail(key != NULL, NULL)do{ if (key != ((void *)0)) { } else { g_return_if_fail_warning
(((gchar*) 0), __PRETTY_FUNCTION__, "key != NULL"); return (
((void *)0)); }; }while (0)
;
393
394 return g_hash_table_lookup(settings, key);
395}
396
397static void sig_init_finished(void)
398{
399 fe_initialized = TRUE(!(0));
400 if (last_errors != NULL((void *)0)) {
401 signal_emit("settings errors", 1, last_errors->str);
402 g_string_free(last_errors, TRUE(!(0)));
403 }
404
405 if (config_changed) {
406 /* some backwards compatibility changes were made to
407 config file, reload it */
408 g_warning("Some settings were automatically "g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "Some settings were automatically "
"updated, please /SAVE")
409 "updated, please /SAVE")g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "Some settings were automatically "
"updated, please /SAVE")
;
410 signal_emit("setup changed", 0);
411 }
412}
413
414static void settings_clean_invalid_module(const char *module)
415{
416 CONFIG_NODE *node;
417 SETTINGS_REC *set;
418 GSList *tmp, *next;
419
420 node = iconfig_node_traverse("settings", FALSE)config_node_traverse(mainconfig, "settings", (0));
421 if (node == NULL((void *)0)) return;
422
423 node = config_node_section(node, module, -1);
424 if (node == NULL((void *)0)) return;
425
426 for (tmp = config_node_first(node->value); tmp != NULL((void *)0); tmp = next) {
427 CONFIG_NODE *subnode = tmp->data;
428 next = config_node_next(tmp);
429
430 set = g_hash_table_lookup(settings, subnode->key);
431 if (set == NULL((void *)0) || strcmp(set->module, module) != 0)
432 iconfig_node_remove(node, subnode)config_node_remove(mainconfig, node, subnode);
433 }
434}
435
436/* remove all invalid settings from config file. works only with the
437 modules that have already called settings_check() */
438void settings_clean_invalid(void)
439{
440 while (last_invalid_modules != NULL((void *)0)) {
441 char *module = last_invalid_modules->data;
442
443 settings_clean_invalid_module(module);
444
445 last_invalid_modules =
446 g_slist_remove(last_invalid_modules, module);
447 g_free(module);
448 }
449}
450
451static int backwards_compatibility(const char *module, CONFIG_NODE *node,
452 CONFIG_NODE *parent)
453{
454 const char *new_key, *new_module;
455 CONFIG_NODE *new_node;
456 char *new_value;
457
458 new_value = NULL((void *)0); new_key = NULL((void *)0); new_module = NULL((void *)0);
459
460 /* fe-text term_type -> fe-common/core term_charset - for 0.8.10-> */
461 if (strcmp(module, "fe-text") == 0) {
462 if (g_ascii_strcasecmp(node->key, "term_type") == 0 ||
463 /* kludge for cvs-version where term_charset was in fe-text */
464 g_ascii_strcasecmp(node->key, "term_charset") == 0) {
465 new_module = "fe-common/core";
466 new_key = "term_charset";
467 new_value = !is_valid_charset(node->value) ? NULL((void *)0) :
468 g_strdup(node->value);
469 new_node = iconfig_node_traverse("settings", FALSE)config_node_traverse(mainconfig, "settings", (0));
470 new_node = new_node == NULL((void *)0) ? NULL((void *)0) :
471 config_node_section(new_node, new_module, -1);
472
473 config_node_set_str(mainconfig, new_node,
474 new_key, new_value);
475 /* remove old */
476 config_node_set_str(mainconfig, parent,
477 node->key, NULL((void *)0));
478 g_free(new_value);
479 config_changed = TRUE(!(0));
480 return new_key != NULL((void *)0);
481 } else if (g_ascii_strcasecmp(node->key, "actlist_moves") == 0 &&
482 node->value != NULL((void *)0) && g_ascii_strcasecmp(node->value, "yes") == 0) {
483 config_node_set_str(mainconfig, parent, "actlist_sort", "recent");
484 config_node_set_str(mainconfig, parent, node->key, NULL((void *)0));
485 config_changed = TRUE(!(0));
486 return TRUE(!(0));
487 }
488 }
489 return new_key != NULL((void *)0);
490}
491
492/* verify that all settings in config file for `module' are actually found
493 from /SET list */
494void settings_check_module(const char *module)
495{
496 SETTINGS_REC *set;
497 CONFIG_NODE *node, *parent;
498 GString *errors;
499 GSList *tmp, *next;
500 int count;
501
502 g_return_if_fail(module != NULL)do{ if (module != ((void *)0)) { } else { g_return_if_fail_warning
(((gchar*) 0), __PRETTY_FUNCTION__, "module != NULL"); return
; }; }while (0)
;
503
504 node = iconfig_node_traverse("settings", FALSE)config_node_traverse(mainconfig, "settings", (0));
505 node = node == NULL((void *)0) ? NULL((void *)0) : config_node_section(node, module, -1);
506 if (node == NULL((void *)0)) return;
507
508 errors = g_string_new(NULL((void *)0));
509 g_string_printf(errors, "Unknown settings in configuration "
510 "file for module %s:", module);
511
512 count = 0;
513 parent = node;
514 tmp = config_node_first(node->value);
515 for (; tmp != NULL((void *)0); tmp = next) {
516 node = tmp->data;
517 next = config_node_next(tmp);
518
519 set = g_hash_table_lookup(settings, node->key);
520 if (backwards_compatibility(module, node, parent))
521 continue;
522
523 if (set == NULL((void *)0) || strcmp(set->module, module) != 0) {
524 g_string_append_printf(errors, " %s", node->key);
525 count++;
526 }
527 }
528 if (count > 0) {
529 if (gslist_find_icase_string(last_invalid_modules,
530 module) == NULL((void *)0)) {
531 /* mark this module having invalid settings */
532 last_invalid_modules =
533 g_slist_append(last_invalid_modules,
534 g_strdup(module));
535 }
536 if (fe_initialized)
537 signal_emit("settings errors", 1, errors->str);
538 else {
539 if (last_errors == NULL((void *)0))
540 last_errors = g_string_new(NULL((void *)0));
541 else
542 g_string_append_c(last_errors, '\n')g_string_append_c_inline (last_errors, '\n');
543 g_string_append(last_errors, errors->str);
544 }
545 }
546 g_string_free(errors, TRUE(!(0)));
547}
548
549static int settings_compare(SETTINGS_REC *v1, SETTINGS_REC *v2)
550{
551 int cmp = strcmp(v1->section, v2->section);
552 if (!cmp)
553 cmp = strcmp(v1->key, v2->key);
554 return cmp;
555}
556
557static void settings_hash_get(const char *key, SETTINGS_REC *rec,
558 GSList **list)
559{
560 *list = g_slist_insert_sorted(*list, rec,
561 (GCompareFunc) settings_compare);
562}
563
564GSList *settings_get_sorted(void)
565{
566 GSList *list;
567
568 list = NULL((void *)0);
569 g_hash_table_foreach(settings, (GHFunc) settings_hash_get, &list);
570 return list;
571}
572
573void sig_term(int n)
574{
575 /* if we get SIGTERM after this, just die instead of coming back here. */
576 signal(SIGTERM15, SIG_DFL((__sighandler_t *)0));
577
578 /* quit from all servers too.. */
579 signal_emit("command quit", 1, "");
580
581 /* and die */
582 raise(SIGTERM15);
583}
584
585/* Yes, this is my own stupid checksum generator, some "real" algorithm
586 would be nice but would just take more space without much real benefit */
587static unsigned int file_checksum(const char *fname)
588{
589 char buf[512];
590 int f, ret, n;
591 unsigned int checksum = 0;
592
593 f = open(fname, O_RDONLY0x0000);
594 if (f == -1) return 0;
595
596 n = 0;
597 while ((ret = read(f, buf, sizeof(buf))) > 0) {
598 while (ret-- > 0)
599 checksum += buf[ret] << ((n++ & 3)*8);
600 }
601 close(f);
602 return checksum;
603}
604
605static void irssi_config_save_state(const char *fname)
606{
607 struct stat statbuf;
608
609 g_return_if_fail(fname != NULL)do{ if (fname != ((void *)0)) { } else { g_return_if_fail_warning
(((gchar*) 0), __PRETTY_FUNCTION__, "fname != NULL"); return
; }; }while (0)
;
610
611 if (stat(fname, &statbuf) != 0)
612 return;
613
614 /* save modify time, file size and checksum */
615 config_last_mtime = statbuf.st_mtimest_mtimespec.tv_sec;
616 config_last_size = statbuf.st_size;
617 config_last_checksum = file_checksum(fname);
618}
619
620int irssi_config_is_changed(const char *fname)
621{
622 struct stat statbuf;
623
624 if (fname == NULL((void *)0))
625 fname = mainconfig->fname;
626
627 if (stat(fname, &statbuf) != 0)
628 return FALSE(0);
629
630 return config_last_mtime != statbuf.st_mtimest_mtimespec.tv_sec &&
631 (config_last_size != statbuf.st_size ||
632 config_last_checksum != file_checksum(fname));
633}
634
635static CONFIG_REC *parse_configfile(const char *fname)
636{
637 CONFIG_REC *config;
638 struct stat statbuf;
639 const char *path;
640 char *str;
641
642 if (fname == NULL((void *)0))
643 fname = get_irssi_config();
644
645 if (stat(fname, &statbuf) == 0)
646 path = fname;
647 else {
648 /* user configuration file not found, use the default one
649 from sysconfdir */
650 path = SYSCONFDIR"/home/jilles/irssi/etc""/"IRSSI_GLOBAL_CONFIG"irssi.conf";
651 if (stat(path, &statbuf) != 0) {
652 /* no configuration file in sysconfdir ..
653 use the build-in configuration */
654 path = NULL((void *)0);
655 }
656 }
657
658 config = config_open(path, -1);
659 if (config == NULL((void *)0)) {
660 str = g_strdup_printf("Error opening configuration file %s: %s",
661 path, g_strerror(errno(* __error())));
662 signal_emit("gui dialog", 2, "error", str);
663 g_free(str);
664
665 config = config_open(NULL((void *)0), -1);
666 }
667
668 if (config->fname != NULL((void *)0))
669 config_parse(config);
670 else
671 config_parse_data(config, default_config, "internal");
672
673 config_change_file_name(config, fname, 0660);
674 irssi_config_save_state(fname);
675 return config;
676}
677
678static void init_configfile(void)
679{
680 struct stat statbuf;
681 char *str;
682
683 if (stat(get_irssi_dir(), &statbuf) != 0) {
684 /* ~/.irssi not found, create it. */
685 if (mkpath(get_irssi_dir(), 0700) != 0) {
686 g_error("Couldn't create %s directory", get_irssi_dir())do { g_log (((gchar*) 0), G_LOG_LEVEL_ERROR, "Couldn't create %s directory"
, get_irssi_dir()); for (;;); } while (0)
;
687 }
688 } else if (!S_ISDIR(statbuf.st_mode)(((statbuf.st_mode) & 0170000) == 0040000)) {
689 g_error("%s is not a directory.\n"do { g_log (((gchar*) 0), G_LOG_LEVEL_ERROR, "%s is not a directory.\n"
"You should remove it with command: rm %s", get_irssi_dir(),
get_irssi_dir()); for (;;); } while (0)
690 "You should remove it with command: rm %s",do { g_log (((gchar*) 0), G_LOG_LEVEL_ERROR, "%s is not a directory.\n"
"You should remove it with command: rm %s", get_irssi_dir(),
get_irssi_dir()); for (;;); } while (0)
691 get_irssi_dir(), get_irssi_dir())do { g_log (((gchar*) 0), G_LOG_LEVEL_ERROR, "%s is not a directory.\n"
"You should remove it with command: rm %s", get_irssi_dir(),
get_irssi_dir()); for (;;); } while (0)
;
692 }
693
694 mainconfig = parse_configfile(NULL((void *)0));
695 config_last_modifycounter = mainconfig->modifycounter;
696
697 /* any errors? */
698 if (config_last_error(mainconfig)(mainconfig)->last_error != NULL((void *)0)) {
699 str = g_strdup_printf("Ignored errors in configuration file:\n%s",
700 config_last_error(mainconfig)(mainconfig)->last_error);
701 signal_emit("gui dialog", 2, "error", str);
702 g_free(str);
703 }
704
705 signal(SIGTERM15, sig_term);
706}
707
708int settings_reread(const char *fname)
709{
710 CONFIG_REC *tempconfig;
711 char *str;
712
713 str = fname == NULL((void *)0) ? NULL((void *)0) : convert_home(fname);
714 tempconfig = parse_configfile(str);
715 g_free_not_null(str)g_free(str);
716
717 if (tempconfig == NULL((void *)0)) {
718 signal_emit("gui dialog", 2, "error", g_strerror(errno(* __error())));
719 return FALSE(0);
720 }
721
722 if (config_last_error(tempconfig)(tempconfig)->last_error != NULL((void *)0)) {
723 str = g_strdup_printf("Errors in configuration file:\n%s",
724 config_last_error(tempconfig)(tempconfig)->last_error);
725 signal_emit("gui dialog", 2, "error", str);
726 g_free(str);
727
728 config_close(tempconfig);
729 return FALSE(0);
730 }
731
732 config_close(mainconfig);
733 mainconfig = tempconfig;
734 config_last_modifycounter = mainconfig->modifycounter;
735
736 signal_emit("setup changed", 0);
737 signal_emit("setup reread", 1, mainconfig->fname);
738 return TRUE(!(0));
739}
740
741int settings_save(const char *fname, int autosave)
742{
743 char *str;
744 int error;
745
746 if (fname == NULL((void *)0))
747 fname = mainconfig->fname;
748
749 error = config_write(mainconfig, fname, 0660) != 0;
750 irssi_config_save_state(fname);
751 config_last_modifycounter = mainconfig->modifycounter;
752 if (error) {
753 str = g_strdup_printf("Couldn't save configuration file: %s",
754 config_last_error(mainconfig)(mainconfig)->last_error);
755 signal_emit("gui dialog", 2, "error", str);
756 g_free(str);
757 }
758 signal_emit("setup saved", 2, fname, GINT_TO_POINTER(autosave)((gpointer) (autosave)));
759 return !error;
760}
761
762static int sig_autosave(void)
763{
764 char *fname, *str;
765
766 if (!settings_get_bool("settings_autosave") ||
767 config_last_modifycounter == mainconfig->modifycounter)
768 return 1;
769
770 if (!irssi_config_is_changed(NULL((void *)0)))
771 settings_save(NULL((void *)0), TRUE(!(0)));
772 else {
773 fname = g_strconcat(mainconfig->fname, ".autosave", NULL((void *)0));
774 str = g_strdup_printf("Configuration file was modified "
775 "while irssi was running. Saving "
776 "configuration to file '%s' instead. "
777 "Use /SAVE or /RELOAD to get rid of "
778 "this message.", fname);
779 signal_emit("gui dialog", 2, "warning", str);
780 g_free(str);
781
782 settings_save(fname, TRUE(!(0)));
783 g_free(fname);
784 }
785
786 return 1;
787}
788
789void settings_init(void)
790{
791 settings = g_hash_table_new((GHashFunc) g_istr_hash,
792 (GCompareFunc) g_istr_equal);
793
794 last_errors = NULL((void *)0);
795 last_invalid_modules = NULL((void *)0);
796 fe_initialized = FALSE(0);
797 config_changed = FALSE(0);
798
799 config_last_mtime = 0;
800 config_last_modifycounter = 0;
801 init_configfile();
802
803 settings_add_bool("misc", "settings_autosave", TRUE)settings_add_bool_module("core", "misc", "settings_autosave",
(!(0)))
;
804 timeout_tag = g_timeout_add(SETTINGS_AUTOSAVE_TIMEOUT(1000*60*60),
805 (GSourceFunc) sig_autosave, NULL((void *)0));
806 signal_add("irssi init finished", (SIGNAL_FUNC) sig_init_finished)signal_add_full("core", 0, ("irssi init finished"), (SIGNAL_FUNC
) ((SIGNAL_FUNC) sig_init_finished), ((void *)0))
;
807 signal_add("gui exit", (SIGNAL_FUNC) sig_autosave)signal_add_full("core", 0, ("gui exit"), (SIGNAL_FUNC) ((SIGNAL_FUNC
) sig_autosave), ((void *)0))
;
808}
809
810static void settings_hash_free(const char *key, SETTINGS_REC *rec)
811{
812 settings_destroy(rec);
813}
814
815void settings_deinit(void)
816{
817 g_source_remove(timeout_tag);
818 signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished)signal_remove_full(("irssi init finished"), (SIGNAL_FUNC) ((SIGNAL_FUNC
) sig_init_finished), ((void *)0))
;
819 signal_remove("gui exit", (SIGNAL_FUNC) sig_autosave)signal_remove_full(("gui exit"), (SIGNAL_FUNC) ((SIGNAL_FUNC)
sig_autosave), ((void *)0))
;
820
821 g_slist_foreach(last_invalid_modules, (GFunc) g_free, NULL((void *)0));
822 g_slist_free(last_invalid_modules);
823
824 g_hash_table_foreach(settings, (GHFunc) settings_hash_free, NULL((void *)0));
825 g_hash_table_destroy(settings);
826
827 if (mainconfig != NULL((void *)0)) config_close(mainconfig);
828}