Statistics
| Revision:

root / libsylph / prefs.c @ 2123

History | View | Annotate | Download (12.5 kB)

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