Statistics
| Revision:

root / libsylph / prefs.c @ 2081

History | View | Annotate | Download (12.1 kB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2008 Hiroyuki Yamamoto
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with this library; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
 */
19
20
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23
24
#include <glib.h>
25
#include <glib/gi18n.h>
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <string.h>
29
#include <unistd.h>
30
#include <errno.h>
31
32
#include "prefs.h"
33
#include "codeconv.h"
34
#include "utils.h"
35
36
typedef enum
37
{
38
        DUMMY_PARAM
39
} DummyEnum;
40
41
typedef struct _PrefFilePrivate        PrefFilePrivate;
42
43
struct _PrefFilePrivate {
44
        FILE *fp;
45
        gchar *path;
46
        gint backup_generation;
47
};
48
49
static void prefs_config_parse_one_line        (GHashTable        *param_table,
50
                                         const gchar        *buf);
51
52
GHashTable *prefs_param_table_get(PrefParam *param)
53
{
54
        GHashTable *table;
55
        gint i;
56
57
        g_return_val_if_fail(param != NULL, NULL);
58
59
        table = g_hash_table_new(g_str_hash, g_str_equal);
60
61
        for (i = 0; param[i].name != NULL; i++) {
62
                g_hash_table_insert(table, param[i].name, &param[i]);
63
        }
64
65
        return table;
66
}
67
68
void prefs_param_table_destroy(GHashTable *param_table)
69
{
70
        g_hash_table_destroy(param_table);
71
}
72
73
void prefs_read_config(PrefParam *param, const gchar *label,
74
                       const gchar *rcfile, const gchar *encoding)
75
{
76
        FILE *fp;
77
        gchar buf[PREFSBUFSIZE];
78
        gchar *block_label;
79
        GHashTable *param_table;
80
81
        g_return_if_fail(param != NULL);
82
        g_return_if_fail(label != NULL);
83
        g_return_if_fail(rcfile != NULL);
84
85
        debug_print("Reading configuration...\n");
86
87
        prefs_set_default(param);
88
89
        if ((fp = g_fopen(rcfile, "rb")) == NULL) {
90
                if (ENOENT != errno) FILE_OP_ERROR(rcfile, "fopen");
91
                return;
92
        }
93
94
        block_label = g_strdup_printf("[%s]", label);
95
96
        /* search aiming block */
97
        while (fgets(buf, sizeof(buf), fp) != NULL) {
98
                gint val;
99
100
                if (encoding) {
101
                        gchar *conv_str;
102
103
                        conv_str = conv_codeset_strdup
104
                                (buf, encoding, CS_INTERNAL);
105
                        if (!conv_str)
106
                                conv_str = g_strdup(buf);
107
                        val = strncmp
108
                                (conv_str, block_label, strlen(block_label));
109
                        g_free(conv_str);
110
                } else
111
                        val = strncmp(buf, block_label, strlen(block_label));
112
                if (val == 0) {
113
                        debug_print("Found %s\n", block_label);
114
                        break;
115
                }
116
        }
117
        g_free(block_label);
118
119
        param_table = prefs_param_table_get(param);
120
121
        while (fgets(buf, sizeof(buf), fp) != NULL) {
122
                strretchomp(buf);
123
                if (buf[0] == '\0') continue;
124
                /* reached next block */
125
                if (buf[0] == '[') break;
126
127
                if (encoding) {
128
                        gchar *conv_str;
129
130
                        conv_str = conv_codeset_strdup
131
                                (buf, encoding, CS_INTERNAL);
132
                        if (!conv_str)
133
                                conv_str = g_strdup(buf);
134
                        prefs_config_parse_one_line(param_table, conv_str);
135
                        g_free(conv_str);
136
                } else
137
                        prefs_config_parse_one_line(param_table, buf);
138
        }
139
140
        prefs_param_table_destroy(param_table);
141
142
        debug_print("Finished reading configuration.\n");
143
        fclose(fp);
144
}
145
146
static void prefs_config_parse_one_line(GHashTable *param_table,
147
                                        const gchar *buf)
148
{
149
        PrefParam *param;
150
        const gchar *p = buf;
151
        gchar *name;
152
        const gchar *value;
153
154
        while (*p && *p != '=')
155
                p++;
156
157
        if (*p != '=') {
158
                g_warning("invalid pref line: %s\n", buf);
159
                return;
160
        }
161
162
        name = g_strndup(buf, p - buf);
163
        value = p + 1;
164
165
        /* debug_print("%s = %s\n", name, value); */
166
167
        param = g_hash_table_lookup(param_table, name);
168
169
        if (!param) {
170
                debug_print("pref key '%s' (value '%s') not found\n",
171
                            name, value);
172
                g_free(name);
173
                return;
174
        }
175
176
        switch (param->type) {
177
        case P_STRING:
178
                g_free(*((gchar **)param->data));
179
                *((gchar **)param->data) = *value ? g_strdup(value) : NULL;
180
                break;
181
        case P_INT:
182
                *((gint *)param->data) = (gint)atoi(value);
183
                break;
184
        case P_BOOL:
185
                *((gboolean *)param->data) =
186
                        (*value == '0' || *value == '\0') ? FALSE : TRUE;
187
                break;
188
        case P_ENUM:
189
                *((DummyEnum *)param->data) = (DummyEnum)atoi(value);
190
                break;
191
        case P_USHORT:
192
                *((gushort *)param->data) = (gushort)atoi(value);
193
                break;
194
        default:
195
                break;
196
        }
197
198
        g_free(name);
199
}
200
201
#define TRY(func) \
202
if (!(func)) \
203
{ \
204
        g_warning(_("failed to write configuration to file\n")); \
205
        if (orig_fp) fclose(orig_fp); \
206
        prefs_file_close_revert(pfile); \
207
        g_free(rcpath); \
208
        g_free(block_label); \
209
        return; \
210
} \
211
212
void prefs_write_config(PrefParam *param, const gchar *label,
213
                        const gchar *rcfile)
214
{
215
        FILE *orig_fp;
216
        PrefFile *pfile;
217
        gchar *rcpath;
218
        gchar buf[PREFSBUFSIZE];
219
        gchar *block_label = NULL;
220
        gboolean block_matched = FALSE;
221
222
        g_return_if_fail(param != NULL);
223
        g_return_if_fail(label != NULL);
224
        g_return_if_fail(rcfile != NULL);
225
226
        rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, rcfile, NULL);
227
        if ((orig_fp = g_fopen(rcpath, "rb")) == NULL) {
228
                if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
229
        }
230
231
        if ((pfile = prefs_file_open(rcpath)) == NULL) {
232
                g_warning(_("failed to write configuration to file\n"));
233
                if (orig_fp) fclose(orig_fp);
234
                g_free(rcpath);
235
                return;
236
        }
237
238
        block_label = g_strdup_printf("[%s]", label);
239
240
        /* search aiming block */
241
        if (orig_fp) {
242
                while (fgets(buf, sizeof(buf), orig_fp) != NULL) {
243
                        gint val;
244
245
                        val = strncmp(buf, block_label, strlen(block_label));
246
                        if (val == 0) {
247
                                debug_print(_("Found %s\n"), block_label);
248
                                block_matched = TRUE;
249
                                break;
250
                        } else
251
                                TRY(fputs(buf, pfile->fp) != EOF);
252
                }
253
        }
254
255
        TRY(fprintf(pfile->fp, "%s\n", block_label) > 0);
256
        g_free(block_label);
257
        block_label = NULL;
258
259
        /* write all param data to file */
260
        TRY(prefs_file_write_param(pfile, param) == 0);
261
262
        if (block_matched) {
263
                while (fgets(buf, sizeof(buf), orig_fp) != NULL) {
264
                        /* next block */
265
                        if (buf[0] == '[') {
266
                                TRY(fputc('\n', pfile->fp) != EOF &&
267
                                    fputs(buf, pfile->fp)  != EOF);
268
                                break;
269
                        }
270
                }
271
                while (fgets(buf, sizeof(buf), orig_fp) != NULL)
272
                        TRY(fputs(buf, pfile->fp) != EOF);
273
        }
274
275
        if (orig_fp) fclose(orig_fp);
276
        if (prefs_file_close(pfile) < 0)
277
                g_warning(_("failed to write configuration to file\n"));
278
        g_free(rcpath);
279
280
        debug_print(_("Configuration is saved.\n"));
281
}
282
283
gint prefs_file_write_param(PrefFile *pfile, PrefParam *param)
284
{
285
        gint i;
286
        gchar buf[PREFSBUFSIZE];
287
288
        for (i = 0; param[i].name != NULL; i++) {
289
                switch (param[i].type) {
290
                case P_STRING:
291
                        g_snprintf(buf, sizeof(buf), "%s=%s\n", param[i].name,
292
                                   *((gchar **)param[i].data) ?
293
                                   *((gchar **)param[i].data) : "");
294
                        break;
295
                case P_INT:
296
                        g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name,
297
                                   *((gint *)param[i].data));
298
                        break;
299
                case P_BOOL:
300
                        g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name,
301
                                   *((gboolean *)param[i].data));
302
                        break;
303
                case P_ENUM:
304
                        g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name,
305
                                   *((DummyEnum *)param[i].data));
306
                        break;
307
                case P_USHORT:
308
                        g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name,
309
                                   *((gushort *)param[i].data));
310
                        break;
311
                default:
312
                        buf[0] = '\0';
313
                }
314
315
                if (buf[0] != '\0') {
316
                        if (fputs(buf, pfile->fp) == EOF) {
317
                                perror("fputs");
318
                                return -1;
319
                        }
320
                }
321
        }
322
323
        return 0;
324
}
325
326
PrefFile *prefs_file_open(const gchar *path)
327
{
328
        PrefFilePrivate *pfile;
329
        gchar *tmppath;
330
        FILE *fp;
331
332
        g_return_val_if_fail(path != NULL, NULL);
333
334
        tmppath = g_strconcat(path, ".tmp", NULL);
335
        if ((fp = g_fopen(tmppath, "wb")) == NULL) {
336
                FILE_OP_ERROR(tmppath, "fopen");
337
                g_free(tmppath);
338
                return NULL;
339
        }
340
341
        if (change_file_mode_rw(fp, tmppath) < 0)
342
                FILE_OP_ERROR(tmppath, "chmod");
343
344
        g_free(tmppath);
345
346
        pfile = g_new(PrefFilePrivate, 1);
347
        pfile->fp = fp;
348
        pfile->path = g_strdup(path);
349
        pfile->backup_generation = 4;
350
351
        return (PrefFile *)pfile;
352
}
353
354
void prefs_file_set_backup_generation(PrefFile *pfile, gint generation)
355
{
356
        PrefFilePrivate *priv = (PrefFilePrivate *)pfile;
357
358
        g_return_if_fail(pfile != NULL);
359
360
        priv->backup_generation = generation;
361
}
362
363
gint prefs_file_get_backup_generation(PrefFile *pfile)
364
{
365
        PrefFilePrivate *priv = (PrefFilePrivate *)pfile;
366
367
        g_return_val_if_fail(pfile != NULL, -1);
368
369
        return priv->backup_generation;
370
}
371
372
gint prefs_file_close(PrefFile *pfile)
373
{
374
        PrefFilePrivate *priv = (PrefFilePrivate *)pfile;
375
        FILE *fp;
376
        gchar *path;
377
        gchar *tmppath;
378
        gchar *bakpath = NULL;
379
        gint generation;
380
        gint ret = 0;
381
382
        g_return_val_if_fail(pfile != NULL, -1);
383
384
        fp = pfile->fp;
385
        path = pfile->path;
386
        generation = priv->backup_generation;
387
        g_free(pfile);
388
389
        tmppath = g_strconcat(path, ".tmp", NULL);
390
        if (fclose(fp) == EOF) {
391
                FILE_OP_ERROR(tmppath, "fclose");
392
                ret = -1;
393
                goto finish;
394
        }
395
396
        if (is_file_exist(path)) {
397
                bakpath = g_strconcat(path, ".bak", NULL);
398
                if (is_file_exist(bakpath)) {
399
                        gint i;
400
                        gchar *bakpath_n, *bakpath_p;
401
402
                        for (i = generation; i > 0; i--) {
403
                                bakpath_n = g_strdup_printf("%s.%d", bakpath,
404
                                                            i);
405
                                if (i == 1)
406
                                        bakpath_p = g_strdup(bakpath);
407
                                else
408
                                        bakpath_p = g_strdup_printf
409
                                                ("%s.%d", bakpath, i - 1);
410
                                if (is_file_exist(bakpath_p)) {
411
                                        if (rename_force(bakpath_p, bakpath_n) < 0) {
412
                                                FILE_OP_ERROR(bakpath_p,
413
                                                              "rename");
414
                                        }
415
                                }
416
                                g_free(bakpath_p);
417
                                g_free(bakpath_n);
418
                        }
419
                }
420
                if (rename_force(path, bakpath) < 0) {
421
                        FILE_OP_ERROR(path, "rename");
422
                        ret = -1;
423
                        goto finish;
424
                }
425
        }
426
427
        if (rename_force(tmppath, path) < 0) {
428
                FILE_OP_ERROR(tmppath, "rename");
429
                ret = -1;
430
                goto finish;
431
        }
432
433
finish:
434
        if (ret < 0)
435
                g_unlink(tmppath);
436
        g_free(path);
437
        g_free(tmppath);
438
        g_free(bakpath);
439
        return ret;
440
}
441
442
gint prefs_file_close_revert(PrefFile *pfile)
443
{
444
        gchar *tmppath;
445
446
        g_return_val_if_fail(pfile != NULL, -1);
447
448
        tmppath = g_strconcat(pfile->path, ".tmp", NULL);
449
        fclose(pfile->fp);
450
        if (g_unlink(tmppath) < 0)
451
                FILE_OP_ERROR(tmppath, "unlink");
452
        g_free(tmppath);
453
        g_free(pfile->path);
454
        g_free(pfile);
455
456
        return 0;
457
}
458
459
void prefs_set_default(PrefParam *param)
460
{
461
        gint i;
462
463
        g_return_if_fail(param != NULL);
464
465
        for (i = 0; param[i].name != NULL; i++) {
466
                if (!param[i].data) continue;
467
468
                switch (param[i].type) {
469
                case P_STRING:
470
                        if (param[i].defval != NULL) {
471
                                if (!g_ascii_strncasecmp(param[i].defval, "ENV_", 4)) {
472
                                        const gchar *envstr;
473
                                        gchar *tmp = NULL;
474
475
                                        envstr = g_getenv(param[i].defval + 4);
476
#ifdef G_OS_WIN32
477
                                        tmp = g_strdup(envstr);
478
#else
479
                                        if (envstr) {
480
                                                tmp = conv_codeset_strdup
481
                                                        (envstr,
482
                                                         conv_get_locale_charset_str(),
483
                                                         CS_UTF_8);
484
                                                if (!tmp) {
485
                                                        g_warning("failed to convert character set.");
486
                                                        tmp = g_strdup(envstr);
487
                                                }
488
                                        }
489
#endif
490
                                        *((gchar **)param[i].data) = tmp;
491
                                } else if (param[i].defval[0] == '~')
492
                                        *((gchar **)param[i].data) =
493
#ifdef G_OS_WIN32
494
                                                g_strconcat(get_rc_dir(),
495
#else
496
                                                g_strconcat(get_home_dir(),
497
#endif
498
                                                            param[i].defval + 1,
499
                                                            NULL);
500
                                else if (param[i].defval[0] != '\0')
501
                                        *((gchar **)param[i].data) =
502
                                                g_strdup(param[i].defval);
503
                                else
504
                                        *((gchar **)param[i].data) = NULL;
505
                        } else
506
                                *((gchar **)param[i].data) = NULL;
507
                        break;
508
                case P_INT:
509
                        if (param[i].defval != NULL)
510
                                *((gint *)param[i].data) =
511
                                        (gint)atoi(param[i].defval);
512
                        else
513
                                *((gint *)param[i].data) = 0;
514
                        break;
515
                case P_BOOL:
516
                        if (param[i].defval != NULL) {
517
                                if (!g_ascii_strcasecmp(param[i].defval, "TRUE"))
518
                                        *((gboolean *)param[i].data) = TRUE;
519
                                else
520
                                        *((gboolean *)param[i].data) =
521
                                                atoi(param[i].defval) ? TRUE : FALSE;
522
                        } else
523
                                *((gboolean *)param[i].data) = FALSE;
524
                        break;
525
                case P_ENUM:
526
                        if (param[i].defval != NULL)
527
                                *((DummyEnum*)param[i].data) =
528
                                        (DummyEnum)atoi(param[i].defval);
529
                        else
530
                                *((DummyEnum *)param[i].data) = 0;
531
                        break;
532
                case P_USHORT:
533
                        if (param[i].defval != NULL)
534
                                *((gushort *)param[i].data) =
535
                                        (gushort)atoi(param[i].defval);
536
                        else
537
                                *((gushort *)param[i].data) = 0;
538
                        break;
539
                default:
540
                        break;
541
                }
542
        }
543
}
544
545
void prefs_free(PrefParam *param)
546
{
547
        gint i;
548
549
        g_return_if_fail(param != NULL);
550
551
        for (i = 0; param[i].name != NULL; i++) {
552
                if (!param[i].data) continue;
553
554
                switch (param[i].type) {
555
                case P_STRING:
556
                        g_free(*((gchar **)param[i].data));
557
                        break;
558
                default:
559
                        break;
560
                }
561
        }
562
}