File: | core/settings.c |
Location: | line 169, column 2 |
Description: | Uninitialized or undefined return value returned to caller. |
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 | |
36 | CONFIG_REC *mainconfig; |
37 | |
38 | static GString *last_errors; |
39 | static GSList *last_invalid_modules; |
40 | static int fe_initialized; |
41 | static int config_changed; /* FIXME: remove after .98 (unless needed again) */ |
42 | |
43 | static GHashTable *settings; |
44 | static int timeout_tag; |
45 | |
46 | static int config_last_modifycounter; |
47 | static time_t config_last_mtime; |
48 | static long config_last_size; |
49 | static unsigned int config_last_checksum; |
50 | |
51 | static 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 | |
70 | static const char * |
71 | settings_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 | |
86 | const char *settings_get_str(const char *key) |
87 | { |
88 | return settings_get_str_type(key, -1); |
89 | } |
90 | |
91 | int 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 | |
106 | int 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 | |
121 | int 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 | |
132 | int 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 | |
140 | int 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 | |
151 | char *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 | |
172 | static 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 | |
203 | void 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 | |
213 | void 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 | |
223 | void 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 | |
234 | void 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 | |
244 | void 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 | |
254 | void 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 | |
264 | static 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 | |
275 | static 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 | |
284 | void 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 | |
295 | static 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 | |
306 | void settings_remove_module(const char *module) |
307 | { |
308 | g_hash_table_foreach_remove(settings, |
309 | (GHRFunc) settings_remove_hash, |
310 | (void *) module); |
311 | } |
312 | |
313 | static 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 | |
330 | void 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 | |
335 | void 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 | |
340 | void 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 | |
345 | int 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 | |
356 | int 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 | |
368 | int 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 | |
379 | SettingType 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 */ |
390 | SETTINGS_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 | |
397 | static 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 | |
414 | static 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() */ |
438 | void 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 | |
451 | static 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 */ |
494 | void 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 | |
549 | static 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 | |
557 | static 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 | |
564 | GSList *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 | |
573 | void 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 */ |
587 | static 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 | |
605 | static 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 | |
620 | int 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 | |
635 | static 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 | |
678 | static 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 | |
708 | int 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 | |
741 | int 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 | |
762 | static 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 | |
789 | void 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 | |
810 | static void settings_hash_free(const char *key, SETTINGS_REC *rec) |
811 | { |
812 | settings_destroy(rec); |
813 | } |
814 | |
815 | void 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 | } |