/* * TilEm II * * Copyright (c) 2010-2011 Thibault Duponchelle * Copyright (c) 2011 Benjamin Moody * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include "gui.h" #include "files.h" #include "msgbox.h" #ifndef CONFIG_FILE #define CONFIG_FILE "config.ini" #endif #define MAX_RECENT_FILES 10 /* Store a filename in a GKeyFile. Any control characters or non-UTF-8 filenames are stored in octal. Note that g_key_file_set/get_string() can't be used because they only allow UTF-8 */ static void key_file_set_filename(GKeyFile *gkf, const char *group, const char *key, const char *value) { char *escaped; const char *p; char *q; gunichar uc; int b; q = escaped = g_new(char, strlen(value) * 4 + 1); while (*value != 0) { uc = g_utf8_get_char_validated(value, -1); if (uc < 0x20 || uc == 0x7F || !g_unichar_validate(uc)) { b = (unsigned char) *value; q[0] = '\\'; q[1] = '0' + (b >> 6); q[2] = '0' + ((b >> 3) & 7); q[3] = '0' + (b & 7); q += 4; value++; } else if (uc == '\\') { q[0] = q[1] = '\\'; q += 2; value++; } else { p = g_utf8_next_char(value); while (value != p) *q++ = *value++; } } *q = 0; g_key_file_set_value(gkf, group, key, escaped); g_free(escaped); } /* Retrieve a filename from a GKeyFile. */ static char *key_file_get_filename(GKeyFile *gkf, const char *group, const char *key, GError **error) { char *value, *unescaped; value = g_key_file_get_value(gkf, group, key, error); if (!value) return NULL; unescaped = g_strcompress(value); g_free(value); return unescaped; } /* Load and parse the configuration file. */ static GKeyFile *load_config(gboolean writable) { static gboolean warned; GKeyFile *gkf; GKeyFileFlags flags; char *cfname, *dname; GError *err = NULL; gkf = g_key_file_new(); cfname = get_shared_file_path(CONFIG_FILE, NULL); if (!cfname) return gkf; if (writable) flags = (G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS); else flags = 0; if (!g_key_file_load_from_file(gkf, cfname, flags, &err)) { /* don't bother the user more than once */ if (!warned) { dname = g_filename_display_name(cfname); messagebox02(NULL, GTK_MESSAGE_ERROR, "Unable to read settings", "An error occurred while reading %s: %s", dname, err->message); g_free(dname); warned = TRUE; } g_error_free(err); } g_free(cfname); return gkf; } /* Save the configuration file. */ static void save_config(GKeyFile *gkf) { static gboolean warned; char *cfname, *dname; char *data; gsize length; GError *err = NULL; data = g_key_file_to_data(gkf, &length, NULL); cfname = get_config_file_path(CONFIG_FILE, NULL); if (!g_file_set_contents(cfname, data, length, &err)) { /* don't bother the user more than once */ if (!warned) { dname = g_filename_display_name(cfname); messagebox02(NULL, GTK_MESSAGE_ERROR, "Unable to save settings", "An error occurred while writing %s: %s", dname, err->message); g_free(dname); warned = TRUE; } g_error_free(err); } g_free(cfname); g_free(data); } /* Retrieve settings from the configuration file. */ void tilem_config_get(const char *group, const char *option, ...) { va_list ap; GKeyFile *gkf; const char *type, *defvalue; GError *err = NULL; char *key, *p; char **strp; int *intp; double *dblp; GdkColor *colorp; g_return_if_fail(group != NULL); g_return_if_fail(option != NULL); gkf = load_config(FALSE); va_start(ap, option); while (option != NULL) { type = strrchr(option, '/'); if (type == NULL || type[1] == 0 || (type[2] != 0 && type[2] != '=')) { g_critical("invalid argument\n"); break; } if (type[2] == '=') defvalue = &type[3]; else defvalue = NULL; key = g_strndup(option, type - option); if (type[1] == 'f') { strp = va_arg(ap, char **); *strp = key_file_get_filename(gkf, group, key, &err); if (err && defvalue) *strp = g_strdup(defvalue); } else if (type[1] == 's') { strp = va_arg(ap, char **); *strp = g_key_file_get_string(gkf, group, key, &err); if (err && defvalue) *strp = g_strdup(defvalue); } else if (type[1] == 'i') { intp = va_arg(ap, int *); *intp = g_key_file_get_integer(gkf, group, key, &err); if (err && defvalue) *intp = g_ascii_strtoll(defvalue, NULL, 10); } else if (type[1] == 'r') { dblp = va_arg(ap, double *); *dblp = g_key_file_get_double(gkf, group, key, &err); if (err && defvalue) *dblp = g_ascii_strtod(defvalue, NULL); } else if (type[1] == 'b') { intp = va_arg(ap, int *); *intp = g_key_file_get_boolean(gkf, group, key, &err); if (err && defvalue) *intp = g_ascii_strtoll(defvalue, NULL, 10); } else if (type[1] == 'c') { colorp = va_arg(ap, GdkColor *); p = g_key_file_get_string(gkf, group, key, &err); if (p == NULL || !gdk_color_parse(p, colorp)) { if (defvalue) { gdk_color_parse(defvalue, colorp); } else { colorp->red = 0; colorp->green = 0; colorp->blue = 0; } } g_free(p); } else { g_critical("invalid argument\n"); g_free(key); break; } g_clear_error(&err); g_free(key); option = va_arg(ap, const char *); } va_end(ap); g_key_file_free(gkf); } /* Save settings to the configuration file. */ void tilem_config_set(const char *group, const char *option, ...) { va_list ap; GKeyFile *gkf; const char *type; char *key; const char *strv; int intv; double dblv; const GdkColor *colorv; char *p; g_return_if_fail(group != NULL); g_return_if_fail(option != NULL); gkf = load_config(TRUE); va_start(ap, option); while (option != NULL) { type = strrchr(option, '/'); if (type == NULL || type[1] == 0 || type[2] != 0) { g_critical("invalid argument\n"); break; } key = g_strndup(option, type - option); if (type[1] == 'f') { strv = va_arg(ap, const char *); key_file_set_filename(gkf, group, key, strv); } else if (type[1] == 's') { strv = va_arg(ap, const char *); g_key_file_set_string(gkf, group, key, strv); } else if (type[1] == 'i') { intv = va_arg(ap, int); g_key_file_set_integer(gkf, group, key, intv); } else if (type[1] == 'r') { dblv = va_arg(ap, double); g_key_file_set_double(gkf, group, key, dblv); } else if (type[1] == 'b') { intv = va_arg(ap, int); g_key_file_set_boolean(gkf, group, key, !!intv); } else if (type[1] == 'c') { colorv = va_arg(ap, const GdkColor *); p = g_strdup_printf("#%02x%02x%02x", colorv->red >> 8, colorv->green >> 8, colorv->blue >> 8); g_key_file_set_string(gkf, group, key, p); g_free(p); } else { g_critical("invalid argument\n"); g_free(key); break; } g_free(key); option = va_arg(ap, const char *); } va_end(ap); save_config(gkf); g_key_file_free(gkf); }