1 /*
2 * TilEm II
3 *
4 * Copyright (c) 2010-2011 Thibault Duponchelle
5 * Copyright (c) 2011 Benjamin Moody
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <gtk/gtk.h>
30 #include <ticalcs.h>
31 #include <tilem.h>
32
33 #include "gui.h"
34 #include "files.h"
35 #include "msgbox.h"
36
37 #ifndef CONFIG_FILE
38 #define CONFIG_FILE "config.ini"
39 #endif
40
41 #define MAX_RECENT_FILES 10
42
43 /* Store a filename in a GKeyFile. Any control characters or
44 non-UTF-8 filenames are stored in octal. Note that
45 g_key_file_set/get_string() can't be used because they only allow
46 UTF-8 */
key_file_set_filename(GKeyFile * gkf,const char * group,const char * key,const char * value)47 static void key_file_set_filename(GKeyFile *gkf, const char *group,
48 const char *key, const char *value)
49 {
50 char *escaped;
51 const char *p;
52 char *q;
53 gunichar uc;
54 int b;
55
56 q = escaped = g_new(char, strlen(value) * 4 + 1);
57
58 while (*value != 0) {
59 uc = g_utf8_get_char_validated(value, -1);
60 if (uc < 0x20 || uc == 0x7F || !g_unichar_validate(uc)) {
61 b = (unsigned char) *value;
62 q[0] = '\\';
63 q[1] = '0' + (b >> 6);
64 q[2] = '0' + ((b >> 3) & 7);
65 q[3] = '0' + (b & 7);
66 q += 4;
67 value++;
68 }
69 else if (uc == '\\') {
70 q[0] = q[1] = '\\';
71 q += 2;
72 value++;
73 }
74 else {
75 p = g_utf8_next_char(value);
76 while (value != p)
77 *q++ = *value++;
78 }
79 }
80
81 *q = 0;
82
83 g_key_file_set_value(gkf, group, key, escaped);
84 g_free(escaped);
85 }
86
87 /* Retrieve a filename from a GKeyFile. */
key_file_get_filename(GKeyFile * gkf,const char * group,const char * key,GError ** error)88 static char *key_file_get_filename(GKeyFile *gkf, const char *group,
89 const char *key, GError **error)
90 {
91 char *value, *unescaped;
92
93 value = g_key_file_get_value(gkf, group, key, error);
94 if (!value)
95 return NULL;
96
97 unescaped = g_strcompress(value);
98 g_free(value);
99 return unescaped;
100 }
101
102 /* Load and parse the configuration file. */
load_config(gboolean writable)103 static GKeyFile *load_config(gboolean writable)
104 {
105 static gboolean warned;
106 GKeyFile *gkf;
107 GKeyFileFlags flags;
108 char *cfname, *dname;
109 GError *err = NULL;
110
111 gkf = g_key_file_new();
112
113 cfname = get_shared_file_path(CONFIG_FILE, NULL);
114 if (!cfname)
115 return gkf;
116
117 if (writable)
118 flags = (G_KEY_FILE_KEEP_COMMENTS
119 | G_KEY_FILE_KEEP_TRANSLATIONS);
120 else
121 flags = 0;
122
123 if (!g_key_file_load_from_file(gkf, cfname, flags, &err)) {
124 /* don't bother the user more than once */
125 if (!warned) {
126 dname = g_filename_display_name(cfname);
127 messagebox02(NULL, GTK_MESSAGE_ERROR,
128 "Unable to read settings",
129 "An error occurred while reading %s: %s",
130 dname, err->message);
131 g_free(dname);
132 warned = TRUE;
133 }
134 g_error_free(err);
135 }
136
137 g_free(cfname);
138 return gkf;
139 }
140
141 /* Save the configuration file. */
save_config(GKeyFile * gkf)142 static void save_config(GKeyFile *gkf)
143 {
144 static gboolean warned;
145 char *cfname, *dname;
146 char *data;
147 gsize length;
148 GError *err = NULL;
149
150 data = g_key_file_to_data(gkf, &length, NULL);
151
152 cfname = get_config_file_path(CONFIG_FILE, NULL);
153
154 if (!g_file_set_contents(cfname, data, length, &err)) {
155 /* don't bother the user more than once */
156 if (!warned) {
157 dname = g_filename_display_name(cfname);
158 messagebox02(NULL, GTK_MESSAGE_ERROR,
159 "Unable to save settings",
160 "An error occurred while writing %s: %s",
161 dname, err->message);
162 g_free(dname);
163 warned = TRUE;
164 }
165 g_error_free(err);
166 }
167
168 g_free(cfname);
169 g_free(data);
170 }
171
172 /* Retrieve settings from the configuration file. */
tilem_config_get(const char * group,const char * option,...)173 void tilem_config_get(const char *group, const char *option, ...)
174 {
175 va_list ap;
176 GKeyFile *gkf;
177 const char *type, *defvalue;
178 GError *err = NULL;
179 char *key, *p;
180 char **strp;
181 int *intp;
182 double *dblp;
183 GdkColor *colorp;
184
185 g_return_if_fail(group != NULL);
186 g_return_if_fail(option != NULL);
187
188 gkf = load_config(FALSE);
189
190 va_start(ap, option);
191 while (option != NULL) {
192 type = strrchr(option, '/');
193 if (type == NULL || type[1] == 0
194 || (type[2] != 0 && type[2] != '=')) {
195 g_critical("invalid argument\n");
196 break;
197 }
198
199 if (type[2] == '=')
200 defvalue = &type[3];
201 else
202 defvalue = NULL;
203
204 key = g_strndup(option, type - option);
205
206 if (type[1] == 'f') {
207 strp = va_arg(ap, char **);
208 *strp = key_file_get_filename(gkf, group, key, &err);
209 if (err && defvalue)
210 *strp = g_strdup(defvalue);
211 }
212 else if (type[1] == 's') {
213 strp = va_arg(ap, char **);
214 *strp = g_key_file_get_string(gkf, group, key, &err);
215 if (err && defvalue)
216 *strp = g_strdup(defvalue);
217 }
218 else if (type[1] == 'i') {
219 intp = va_arg(ap, int *);
220 *intp = g_key_file_get_integer(gkf, group, key, &err);
221 if (err && defvalue)
222 *intp = g_ascii_strtoll(defvalue, NULL, 10);
223 }
224 else if (type[1] == 'r') {
225 dblp = va_arg(ap, double *);
226 *dblp = g_key_file_get_double(gkf, group, key, &err);
227 if (err && defvalue)
228 *dblp = g_ascii_strtod(defvalue, NULL);
229 }
230 else if (type[1] == 'b') {
231 intp = va_arg(ap, int *);
232 *intp = g_key_file_get_boolean(gkf, group, key, &err);
233 if (err && defvalue)
234 *intp = g_ascii_strtoll(defvalue, NULL, 10);
235 }
236 else if (type[1] == 'c') {
237 colorp = va_arg(ap, GdkColor *);
238 p = g_key_file_get_string(gkf, group, key, &err);
239 if (p == NULL || !gdk_color_parse(p, colorp)) {
240 if (defvalue) {
241 gdk_color_parse(defvalue, colorp);
242 }
243 else {
244 colorp->red = 0;
245 colorp->green = 0;
246 colorp->blue = 0;
247 }
248 }
249 g_free(p);
250 }
251 else {
252 g_critical("invalid argument\n");
253 g_free(key);
254 break;
255 }
256
257 g_clear_error(&err);
258 g_free(key);
259 option = va_arg(ap, const char *);
260 }
261 va_end(ap);
262
263 g_key_file_free(gkf);
264 }
265
266 /* Save settings to the configuration file. */
tilem_config_set(const char * group,const char * option,...)267 void tilem_config_set(const char *group, const char *option, ...)
268 {
269 va_list ap;
270 GKeyFile *gkf;
271 const char *type;
272 char *key;
273 const char *strv;
274 int intv;
275 double dblv;
276 const GdkColor *colorv;
277 char *p;
278
279 g_return_if_fail(group != NULL);
280 g_return_if_fail(option != NULL);
281
282 gkf = load_config(TRUE);
283
284 va_start(ap, option);
285 while (option != NULL) {
286 type = strrchr(option, '/');
287 if (type == NULL || type[1] == 0 || type[2] != 0) {
288 g_critical("invalid argument\n");
289 break;
290 }
291
292 key = g_strndup(option, type - option);
293
294 if (type[1] == 'f') {
295 strv = va_arg(ap, const char *);
296 key_file_set_filename(gkf, group, key, strv);
297 }
298 else if (type[1] == 's') {
299 strv = va_arg(ap, const char *);
300 g_key_file_set_string(gkf, group, key, strv);
301 }
302 else if (type[1] == 'i') {
303 intv = va_arg(ap, int);
304 g_key_file_set_integer(gkf, group, key, intv);
305 }
306 else if (type[1] == 'r') {
307 dblv = va_arg(ap, double);
308 g_key_file_set_double(gkf, group, key, dblv);
309 }
310 else if (type[1] == 'b') {
311 intv = va_arg(ap, int);
312 g_key_file_set_boolean(gkf, group, key, !!intv);
313 }
314 else if (type[1] == 'c') {
315 colorv = va_arg(ap, const GdkColor *);
316 p = g_strdup_printf("#%02x%02x%02x",
317 colorv->red >> 8,
318 colorv->green >> 8,
319 colorv->blue >> 8);
320 g_key_file_set_string(gkf, group, key, p);
321 g_free(p);
322 }
323 else {
324 g_critical("invalid argument\n");
325 g_free(key);
326 break;
327 }
328
329 g_free(key);
330
331 option = va_arg(ap, const char *);
332 }
333 va_end(ap);
334
335 save_config(gkf);
336 g_key_file_free(gkf);
337 }
338
339