Update copyright notices.
[mpd-mk.git] / src / conf.c
blobcf78bb29cc717dd3a5fef82233fada97d1a17dc5
1 /*
2 * Copyright (C) 2003-2010 The Music Player Daemon Project
3 * http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program 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
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "config.h"
21 #include "conf.h"
22 #include "utils.h"
23 #include "tokenizer.h"
24 #include "path.h"
25 #include "glib_compat.h"
27 #include <glib.h>
29 #include <assert.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <errno.h>
35 #undef G_LOG_DOMAIN
36 #define G_LOG_DOMAIN "config"
38 #define MAX_STRING_SIZE MPD_PATH_MAX+80
40 #define CONF_COMMENT '#'
42 struct config_entry {
43 const char *const name;
44 const bool repeatable;
45 const bool block;
47 GSList *params;
50 static struct config_entry config_entries[] = {
51 { .name = CONF_MUSIC_DIR, false, false },
52 { .name = CONF_PLAYLIST_DIR, false, false },
53 { .name = CONF_FOLLOW_INSIDE_SYMLINKS, false, false },
54 { .name = CONF_FOLLOW_OUTSIDE_SYMLINKS, false, false },
55 { .name = CONF_DB_FILE, false, false },
56 { .name = CONF_STICKER_FILE, false, false },
57 { .name = CONF_LOG_FILE, false, false },
58 { .name = CONF_PID_FILE, false, false },
59 { .name = CONF_STATE_FILE, false, false },
60 { .name = CONF_USER, false, false },
61 { .name = CONF_GROUP, false, false },
62 { .name = CONF_BIND_TO_ADDRESS, true, false },
63 { .name = CONF_PORT, false, false },
64 { .name = CONF_LOG_LEVEL, false, false },
65 { .name = CONF_ZEROCONF_NAME, false, false },
66 { .name = CONF_ZEROCONF_ENABLED, false, false },
67 { .name = CONF_PASSWORD, true, false },
68 { .name = CONF_DEFAULT_PERMS, false, false },
69 { .name = CONF_AUDIO_OUTPUT, true, true },
70 { .name = CONF_AUDIO_OUTPUT_FORMAT, false, false },
71 { .name = CONF_MIXER_TYPE, false, false },
72 { .name = CONF_REPLAYGAIN, false, false },
73 { .name = CONF_REPLAYGAIN_PREAMP, false, false },
74 { .name = CONF_REPLAYGAIN_MISSING_PREAMP, false, false },
75 { .name = CONF_VOLUME_NORMALIZATION, false, false },
76 { .name = CONF_SAMPLERATE_CONVERTER, false, false },
77 { .name = CONF_AUDIO_BUFFER_SIZE, false, false },
78 { .name = CONF_BUFFER_BEFORE_PLAY, false, false },
79 { .name = CONF_HTTP_PROXY_HOST, false, false },
80 { .name = CONF_HTTP_PROXY_PORT, false, false },
81 { .name = CONF_HTTP_PROXY_USER, false, false },
82 { .name = CONF_HTTP_PROXY_PASSWORD, false, false },
83 { .name = CONF_CONN_TIMEOUT, false, false },
84 { .name = CONF_MAX_CONN, false, false },
85 { .name = CONF_MAX_PLAYLIST_LENGTH, false, false },
86 { .name = CONF_MAX_COMMAND_LIST_SIZE, false, false },
87 { .name = CONF_MAX_OUTPUT_BUFFER_SIZE, false, false },
88 { .name = CONF_FS_CHARSET, false, false },
89 { .name = CONF_ID3V1_ENCODING, false, false },
90 { .name = CONF_METADATA_TO_USE, false, false },
91 { .name = CONF_SAVE_ABSOLUTE_PATHS, false, false },
92 { .name = CONF_DECODER, true, true },
93 { .name = CONF_INPUT, true, true },
94 { .name = CONF_GAPLESS_MP3_PLAYBACK, false, false },
95 { .name = CONF_PLAYLIST_PLUGIN, true, true },
96 { .name = CONF_AUTO_UPDATE, false, false },
97 { .name = "filter", true, true },
100 static bool
101 get_bool(const char *value, bool *value_r)
103 static const char *t[] = { "yes", "true", "1", NULL };
104 static const char *f[] = { "no", "false", "0", NULL };
106 if (string_array_contains(t, value)) {
107 *value_r = true;
108 return true;
111 if (string_array_contains(f, value)) {
112 *value_r = false;
113 return true;
116 return false;
119 struct config_param *
120 config_new_param(const char *value, int line)
122 struct config_param *ret = g_new(struct config_param, 1);
124 if (!value)
125 ret->value = NULL;
126 else
127 ret->value = g_strdup(value);
129 ret->line = line;
131 ret->num_block_params = 0;
132 ret->block_params = NULL;
133 ret->used = false;
135 return ret;
138 static void
139 config_param_free(struct config_param *param)
141 g_free(param->value);
143 for (unsigned i = 0; i < param->num_block_params; i++) {
144 g_free(param->block_params[i].name);
145 g_free(param->block_params[i].value);
148 if (param->num_block_params)
149 g_free(param->block_params);
151 g_free(param);
154 static void
155 config_param_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
157 struct config_param *param = data;
159 config_param_free(param);
162 static struct config_entry *
163 config_entry_get(const char *name)
165 for (unsigned i = 0; i < G_N_ELEMENTS(config_entries); ++i) {
166 struct config_entry *entry = &config_entries[i];
167 if (strcmp(entry->name, name) == 0)
168 return entry;
171 return NULL;
174 void config_global_finish(void)
176 for (unsigned i = 0; i < G_N_ELEMENTS(config_entries); ++i) {
177 struct config_entry *entry = &config_entries[i];
179 g_slist_foreach(entry->params,
180 config_param_free_callback, NULL);
181 g_slist_free(entry->params);
185 void config_global_init(void)
189 static void
190 config_param_check(gpointer data, G_GNUC_UNUSED gpointer user_data)
192 struct config_param *param = data;
194 if (!param->used)
195 /* this whole config_param was not queried at all -
196 the feature might be disabled at compile time?
197 Silently ignore it here. */
198 return;
200 for (unsigned i = 0; i < param->num_block_params; i++) {
201 struct block_param *bp = &param->block_params[i];
203 if (!bp->used)
204 g_warning("option '%s' on line %i was not recognized",
205 bp->name, bp->line);
209 void config_global_check(void)
211 for (unsigned i = 0; i < G_N_ELEMENTS(config_entries); ++i) {
212 struct config_entry *entry = &config_entries[i];
214 g_slist_foreach(entry->params, config_param_check, NULL);
218 bool
219 config_add_block_param(struct config_param * param, const char *name,
220 const char *value, int line, GError **error_r)
222 struct block_param *bp;
224 bp = config_get_block_param(param, name);
225 if (bp != NULL) {
226 g_set_error(error_r, config_quark(), 0,
227 "\"%s\" first defined on line %i, and "
228 "redefined on line %i\n", name,
229 bp->line, line);
230 return false;
233 param->num_block_params++;
235 param->block_params = g_realloc(param->block_params,
236 param->num_block_params *
237 sizeof(param->block_params[0]));
239 bp = &param->block_params[param->num_block_params - 1];
241 bp->name = g_strdup(name);
242 bp->value = g_strdup(value);
243 bp->line = line;
244 bp->used = false;
246 return true;
249 static struct config_param *
250 config_read_block(FILE *fp, int *count, char *string, GError **error_r)
252 struct config_param *ret = config_new_param(NULL, *count);
253 GError *error = NULL;
254 bool success;
256 while (true) {
257 char *line;
258 const char *name, *value;
260 line = fgets(string, MAX_STRING_SIZE, fp);
261 if (line == NULL) {
262 config_param_free(ret);
263 g_set_error(error_r, config_quark(), 0,
264 "Expected '}' before end-of-file");
265 return NULL;
268 (*count)++;
269 line = g_strchug(line);
270 if (*line == 0 || *line == CONF_COMMENT)
271 continue;
273 if (*line == '}') {
274 /* end of this block; return from the function
275 (and from this "while" loop) */
277 line = g_strchug(line + 1);
278 if (*line != 0 && *line != CONF_COMMENT) {
279 config_param_free(ret);
280 g_set_error(error_r, config_quark(), 0,
281 "line %i: Unknown tokens after '}'",
282 *count);
283 return false;
286 return ret;
289 /* parse name and value */
291 name = tokenizer_next_word(&line, &error);
292 if (name == NULL) {
293 assert(*line != 0);
294 config_param_free(ret);
295 g_propagate_prefixed_error(error_r, error,
296 "line %i: ", *count);
297 return NULL;
300 value = tokenizer_next_string(&line, &error);
301 if (value == NULL) {
302 config_param_free(ret);
303 if (*line == 0)
304 g_set_error(error_r, config_quark(), 0,
305 "line %i: Value missing", *count);
306 else
307 g_propagate_prefixed_error(error_r, error,
308 "line %i: ",
309 *count);
310 return NULL;
313 if (*line != 0 && *line != CONF_COMMENT) {
314 config_param_free(ret);
315 g_set_error(error_r, config_quark(), 0,
316 "line %i: Unknown tokens after value",
317 *count);
318 return NULL;
321 success = config_add_block_param(ret, name, value, *count,
322 error_r);
323 if (!success) {
324 config_param_free(ret);
325 return false;
330 bool
331 config_read_file(const char *file, GError **error_r)
333 FILE *fp;
334 char string[MAX_STRING_SIZE + 1];
335 int count = 0;
336 struct config_entry *entry;
337 struct config_param *param;
339 g_debug("loading file %s", file);
341 if (!(fp = fopen(file, "r"))) {
342 g_set_error(error_r, config_quark(), errno,
343 "Failed to open %s: %s",
344 file, strerror(errno));
345 return false;
348 while (fgets(string, MAX_STRING_SIZE, fp)) {
349 char *line;
350 const char *name, *value;
351 GError *error = NULL;
353 count++;
355 line = g_strchug(string);
356 if (*line == 0 || *line == CONF_COMMENT)
357 continue;
359 /* the first token in each line is the name, followed
360 by either the value or '{' */
362 name = tokenizer_next_word(&line, &error);
363 if (name == NULL) {
364 assert(*line != 0);
365 g_propagate_prefixed_error(error_r, error,
366 "line %i: ", count);
367 return false;
370 /* get the definition of that option, and check the
371 "repeatable" flag */
373 entry = config_entry_get(name);
374 if (entry == NULL) {
375 g_set_error(error_r, config_quark(), 0,
376 "unrecognized parameter in config file at "
377 "line %i: %s\n", count, name);
378 return false;
381 if (entry->params != NULL && !entry->repeatable) {
382 param = entry->params->data;
383 g_set_error(error_r, config_quark(), 0,
384 "config parameter \"%s\" is first defined "
385 "on line %i and redefined on line %i\n",
386 name, param->line, count);
387 return false;
390 /* now parse the block or the value */
392 if (entry->block) {
393 /* it's a block, call config_read_block() */
395 if (*line != '{') {
396 g_set_error(error_r, config_quark(), 0,
397 "line %i: '{' expected", count);
398 return false;
401 line = g_strchug(line + 1);
402 if (*line != 0 && *line != CONF_COMMENT) {
403 g_set_error(error_r, config_quark(), 0,
404 "line %i: Unknown tokens after '{'",
405 count);
406 return false;
409 param = config_read_block(fp, &count, string, error_r);
410 if (param == NULL)
411 return false;
412 } else {
413 /* a string value */
415 value = tokenizer_next_string(&line, &error);
416 if (value == NULL) {
417 if (*line == 0)
418 g_set_error(error_r, config_quark(), 0,
419 "line %i: Value missing",
420 count);
421 else {
422 g_set_error(error_r, config_quark(), 0,
423 "line %i: %s", count,
424 error->message);
425 g_error_free(error);
428 return false;
431 if (*line != 0 && *line != CONF_COMMENT) {
432 g_set_error(error_r, config_quark(), 0,
433 "line %i: Unknown tokens after value",
434 count);
435 return false;
438 param = config_new_param(value, count);
441 entry->params = g_slist_append(entry->params, param);
443 fclose(fp);
445 return true;
448 struct config_param *
449 config_get_next_param(const char *name, const struct config_param * last)
451 struct config_entry *entry;
452 GSList *node;
453 struct config_param *param;
455 entry = config_entry_get(name);
456 if (entry == NULL)
457 return NULL;
459 node = entry->params;
461 if (last) {
462 node = g_slist_find(node, last);
463 if (node == NULL)
464 return NULL;
466 node = g_slist_next(node);
469 if (node == NULL)
470 return NULL;
472 param = node->data;
473 param->used = true;
474 return param;
477 const char *
478 config_get_string(const char *name, const char *default_value)
480 const struct config_param *param = config_get_param(name);
482 if (param == NULL)
483 return default_value;
485 return param->value;
488 const char *
489 config_get_path(const char *name)
491 struct config_param *param = config_get_param(name);
492 char *path;
494 if (param == NULL)
495 return NULL;
497 path = parsePath(param->value);
498 if (path == NULL)
499 g_error("error parsing \"%s\" at line %i\n",
500 name, param->line);
502 g_free(param->value);
503 return param->value = path;
506 unsigned
507 config_get_positive(const char *name, unsigned default_value)
509 const struct config_param *param = config_get_param(name);
510 long value;
511 char *endptr;
513 if (param == NULL)
514 return default_value;
516 value = strtol(param->value, &endptr, 0);
517 if (*endptr != 0)
518 g_error("Not a valid number in line %i", param->line);
520 if (value <= 0)
521 g_error("Not a positive number in line %i", param->line);
523 return (unsigned)value;
526 struct block_param *
527 config_get_block_param(const struct config_param * param, const char *name)
529 if (param == NULL)
530 return NULL;
532 for (unsigned i = 0; i < param->num_block_params; i++) {
533 if (0 == strcmp(name, param->block_params[i].name)) {
534 struct block_param *bp = &param->block_params[i];
535 bp->used = true;
536 return bp;
540 return NULL;
543 bool config_get_bool(const char *name, bool default_value)
545 const struct config_param *param = config_get_param(name);
546 bool success, value;
548 if (param == NULL)
549 return default_value;
551 success = get_bool(param->value, &value);
552 if (!success)
553 g_error("%s is not a boolean value (yes, true, 1) or "
554 "(no, false, 0) on line %i\n",
555 name, param->line);
557 return value;
560 const char *
561 config_get_block_string(const struct config_param *param, const char *name,
562 const char *default_value)
564 struct block_param *bp = config_get_block_param(param, name);
566 if (bp == NULL)
567 return default_value;
569 return bp->value;
572 unsigned
573 config_get_block_unsigned(const struct config_param *param, const char *name,
574 unsigned default_value)
576 struct block_param *bp = config_get_block_param(param, name);
577 long value;
578 char *endptr;
580 if (bp == NULL)
581 return default_value;
583 value = strtol(bp->value, &endptr, 0);
584 if (*endptr != 0)
585 g_error("Not a valid number in line %i", bp->line);
587 if (value < 0)
588 g_error("Not a positive number in line %i", bp->line);
590 return (unsigned)value;
593 bool
594 config_get_block_bool(const struct config_param *param, const char *name,
595 bool default_value)
597 struct block_param *bp = config_get_block_param(param, name);
598 bool success, value;
600 if (bp == NULL)
601 return default_value;
603 success = get_bool(bp->value, &value);
604 if (!success)
605 g_error("%s is not a boolean value (yes, true, 1) or "
606 "(no, false, 0) on line %i\n",
607 name, bp->line);
609 return value;