1
/* config-ini.c -- INI config file parser
2
This file is part of Redshift.
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.
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.
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/>.
17
Copyright (c) 2010 Jon Lund Steffensen <jonlst@gmail.com>
26
#include "config-ini.h"
30
# define _(s) gettext(s)
35
#define MAX_CONFIG_PATH 4096
36
#define MAX_LINE_LENGTH 512
40
open_config_file(const char *filepath)
42
if (filepath == NULL) {
43
char cp[MAX_CONFIG_PATH];
46
if ((env = getenv("XDG_CONFIG_HOME")) != NULL &&
48
snprintf(cp, sizeof(cp), "%s/redshift.conf", env);
50
} else if ((env = getenv("HOME")) != NULL && env[0] != '\0') {
51
snprintf(cp, sizeof(cp),
52
"%s/.config/redshift.conf", env);
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;
62
/* TODO look in getenv("XDG_CONFIG_DIRS") */
64
FILE *f = fopen(filepath, "r");
75
config_ini_init(config_ini_state_t *state, const char *filepath)
77
config_ini_section_t *section = NULL;
78
state->sections = NULL;
80
FILE *f = open_config_file(filepath);
82
/* Only a serious error if a file was explicitly requested. */
83
if (filepath != NULL) return -1;
87
char line[MAX_LINE_LENGTH];
91
/* Handle the file input linewise. */
92
char *r = fgets(line, sizeof(line), f);
95
/* Strip leading blanks and trailing newline. */
96
s = line + strspn(line, " \t");
97
s[strcspn(s, "\r\n")] = '\0';
99
/* Skip comments and empty lines. */
100
if (s[0] == ';' || s[0] == '\0') continue;
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);
110
config_ini_free(state);
116
/* Create section. */
117
section = malloc(sizeof(config_ini_section_t));
118
if (section == NULL) {
120
config_ini_free(state);
124
/* Insert into section list. */
125
section->name = NULL;
126
section->settings = NULL;
127
section->next = state->sections;
128
state->sections = section;
130
/* Copy section name. */
131
section->name = malloc(end - name + 1);
132
if (section->name == NULL) {
134
config_ini_free(state);
138
memcpy(section->name, name, end - name + 1);
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);
146
config_ini_free(state);
151
char *value = end + 1;
153
if (section == NULL) {
154
fputs(_("Assignment outside section in config"
155
" file.\n"), stderr);
157
config_ini_free(state);
161
/* Create section. */
162
config_ini_setting_t *setting =
163
malloc(sizeof(config_ini_setting_t));
164
if (setting == NULL) {
166
config_ini_free(state);
170
/* Insert into section list. */
171
setting->name = NULL;
172
setting->value = NULL;
173
setting->next = section->settings;
174
section->settings = setting;
176
/* Copy name of setting. */
177
setting->name = malloc(end - s + 1);
178
if (setting->name == NULL) {
180
config_ini_free(state);
184
memcpy(setting->name, s, end - s + 1);
186
/* Copy setting value. */
187
size_t value_len = strlen(value) + 1;
188
setting->value = malloc(value_len);
189
if (setting->value == NULL) {
191
config_ini_free(state);
195
memcpy(setting->value, value, value_len);
205
config_ini_free(config_ini_state_t *state)
207
config_ini_section_t *section = state->sections;
209
while (section != NULL) {
210
config_ini_setting_t *setting = section->settings;
212
while (setting != NULL) {
214
free(setting->value);
215
setting = setting->next;
219
section = section->next;
223
config_ini_section_t *
224
config_ini_get_section(config_ini_state_t *state, const char *name)
226
config_ini_section_t *section = state->sections;
227
while (section != NULL) {
228
if (strcasecmp(section->name, name) == 0) {
231
section = section->next;