~laudrup/redshift/kde-redshift

« back to all changes in this revision

Viewing changes to src/config-ini.c

  • Committer: Jon Lund Steffensen
  • Date: 2010-10-15 10:43:48 UTC
  • Revision ID: jonlst@gmail.com-20101015104348-gmukpx3do8dy3lyz
Add configuration file support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* config-ini.c -- INI config file parser
 
2
   This file is part of Redshift.
 
3
 
 
4
   Redshift is free software: you can redistribute it and/or modify
 
5
   it under the terms of the GNU General Public License as published by
 
6
   the Free Software Foundation, either version 3 of the License, or
 
7
   (at your option) any later version.
 
8
 
 
9
   Redshift is distributed in the hope that it will be useful,
 
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
   GNU General Public License for more details.
 
13
 
 
14
   You should have received a copy of the GNU General Public License
 
15
   along with Redshift.  If not, see <http://www.gnu.org/licenses/>.
 
16
 
 
17
   Copyright (c) 2010  Jon Lund Steffensen <jonlst@gmail.com>
 
18
*/
 
19
 
 
20
 
 
21
#include <stdio.h>
 
22
#include <stdlib.h>
 
23
#include <string.h>
 
24
#include <errno.h>
 
25
 
 
26
#include "config-ini.h"
 
27
 
 
28
#ifdef ENABLE_NLS
 
29
# include <libintl.h>
 
30
# define _(s) gettext(s)
 
31
#else
 
32
# define _(s) s
 
33
#endif
 
34
 
 
35
#define MAX_CONFIG_PATH  4096
 
36
#define MAX_LINE_LENGTH   512
 
37
 
 
38
 
 
39
static FILE *
 
40
open_config_file(const char *filepath)
 
41
{
 
42
        if (filepath == NULL) {
 
43
                char cp[MAX_CONFIG_PATH];
 
44
                char *env;
 
45
 
 
46
                if ((env = getenv("XDG_CONFIG_HOME")) != NULL &&
 
47
                    env[0] != '\0') {
 
48
                        snprintf(cp, sizeof(cp), "%s/redshift.conf", env);
 
49
                        filepath = cp;
 
50
                } else if ((env = getenv("HOME")) != NULL && env[0] != '\0') {
 
51
                        snprintf(cp, sizeof(cp),
 
52
                                 "%s/.config/redshift.conf", env);
 
53
                        filepath = cp;
 
54
                }
 
55
 
 
56
                if (filepath != NULL) {
 
57
                        FILE *f = fopen(filepath, "r");
 
58
                        if (f != NULL) return f;
 
59
                        else if (f == NULL && errno != ENOENT) return NULL;
 
60
                }
 
61
 
 
62
                /* TODO look in getenv("XDG_CONFIG_DIRS") */
 
63
        } else {
 
64
                FILE *f = fopen(filepath, "r");
 
65
                if (f == NULL) {
 
66
                        perror("fopen");
 
67
                        return NULL;
 
68
                }
 
69
        }
 
70
 
 
71
        return NULL;
 
72
}
 
73
 
 
74
int
 
75
config_ini_init(config_ini_state_t *state, const char *filepath)
 
76
{
 
77
        config_ini_section_t *section = NULL;
 
78
        state->sections = NULL;
 
79
 
 
80
        FILE *f = open_config_file(filepath);
 
81
        if (f == NULL) {
 
82
                /* Only a serious error if a file was explicitly requested. */
 
83
                if (filepath != NULL) return -1;
 
84
                return 0;
 
85
        }
 
86
 
 
87
        char line[MAX_LINE_LENGTH];
 
88
        char *s;
 
89
 
 
90
        while (1) {
 
91
                /* Handle the file input linewise. */
 
92
                char *r = fgets(line, sizeof(line), f);
 
93
                if (r == NULL) break;
 
94
 
 
95
                /* Strip leading blanks and trailing newline. */
 
96
                s = line + strspn(line, " \t");
 
97
                s[strcspn(s, "\r\n")] = '\0';
 
98
 
 
99
                /* Skip comments and empty lines. */
 
100
                if (s[0] == ';' || s[0] == '\0') continue;
 
101
 
 
102
                if (s[0] == '[') {
 
103
                        /* Read name of section. */
 
104
                        const char *name = s+1;
 
105
                        char *end = strchr(s, ']');
 
106
                        if (end == NULL || end[1] != '\0' || end == name) {
 
107
                                fputs(_("Malformed section header in config"
 
108
                                        " file.\n"), stderr);
 
109
                                fclose(f);
 
110
                                config_ini_free(state);
 
111
                                return -1;
 
112
                        }
 
113
 
 
114
                        *end = '\0';
 
115
 
 
116
                        /* Create section. */
 
117
                        section = malloc(sizeof(config_ini_section_t));
 
118
                        if (section == NULL) {
 
119
                                fclose(f);
 
120
                                config_ini_free(state);
 
121
                                return -1;
 
122
                        }
 
123
 
 
124
                        /* Insert into section list. */
 
125
                        section->name = NULL;
 
126
                        section->settings = NULL;
 
127
                        section->next = state->sections;
 
128
                        state->sections = section;
 
129
 
 
130
                        /* Copy section name. */
 
131
                        section->name = malloc(end - name + 1);
 
132
                        if (section->name == NULL) {
 
133
                                fclose(f);
 
134
                                config_ini_free(state);
 
135
                                return -1;
 
136
                        }
 
137
 
 
138
                        memcpy(section->name, name, end - name + 1);
 
139
                } else {
 
140
                        /* Split assignment at equals character. */
 
141
                        char *end = strchr(s, '=');
 
142
                        if (end == NULL || end == s) {
 
143
                                fputs(_("Malformed assignment in config"
 
144
                                        " file.\n"), stderr);
 
145
                                fclose(f);
 
146
                                config_ini_free(state);
 
147
                                return -1;
 
148
                        }
 
149
 
 
150
                        *end = '\0';
 
151
                        char *value = end + 1;
 
152
 
 
153
                        if (section == NULL) {
 
154
                                fputs(_("Assignment outside section in config"
 
155
                                        " file.\n"), stderr);
 
156
                                fclose(f);
 
157
                                config_ini_free(state);
 
158
                                return -1;
 
159
                        }
 
160
 
 
161
                        /* Create section. */
 
162
                        config_ini_setting_t *setting =
 
163
                                malloc(sizeof(config_ini_setting_t));
 
164
                        if (setting == NULL) {
 
165
                                fclose(f);
 
166
                                config_ini_free(state);
 
167
                                return -1;
 
168
                        }
 
169
                        
 
170
                        /* Insert into section list. */
 
171
                        setting->name = NULL;
 
172
                        setting->value = NULL;
 
173
                        setting->next = section->settings;
 
174
                        section->settings = setting;
 
175
 
 
176
                        /* Copy name of setting. */
 
177
                        setting->name = malloc(end - s + 1);
 
178
                        if (setting->name == NULL) {
 
179
                                fclose(f);
 
180
                                config_ini_free(state);
 
181
                                return -1;
 
182
                        }
 
183
 
 
184
                        memcpy(setting->name, s, end - s + 1);
 
185
 
 
186
                        /* Copy setting value. */
 
187
                        size_t value_len = strlen(value) + 1;
 
188
                        setting->value = malloc(value_len);
 
189
                        if (setting->value == NULL) {
 
190
                                fclose(f);
 
191
                                config_ini_free(state);
 
192
                                return -1;
 
193
                        }
 
194
 
 
195
                        memcpy(setting->value, value, value_len);
 
196
                }
 
197
        }
 
198
 
 
199
        fclose(f);
 
200
 
 
201
        return 0;
 
202
}
 
203
 
 
204
void
 
205
config_ini_free(config_ini_state_t *state)
 
206
{
 
207
        config_ini_section_t *section = state->sections;
 
208
 
 
209
        while (section != NULL) {
 
210
                config_ini_setting_t *setting = section->settings;
 
211
 
 
212
                while (setting != NULL) {
 
213
                        free(setting->name);
 
214
                        free(setting->value);
 
215
                        setting = setting->next;
 
216
                }
 
217
 
 
218
                free(section->name);
 
219
                section = section->next;
 
220
        }
 
221
}
 
222
 
 
223
config_ini_section_t *
 
224
config_ini_get_section(config_ini_state_t *state, const char *name)
 
225
{
 
226
        config_ini_section_t *section = state->sections;
 
227
        while (section != NULL) {
 
228
                if (strcasecmp(section->name, name) == 0) {
 
229
                        return section;
 
230
                }
 
231
                section = section->next;
 
232
        }
 
233
 
 
234
        return NULL;
 
235
}