configure.ac: Move OggVorbis Encoder to Encoder Plugins.
[mpd-mk.git] / src / conf.c
blobab7be10a7b65b30863b67f064052d65d2c8ef277
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 = CONF_AUTO_UPDATE_DEPTH, false, false },
98 { .name = "filter", true, true },
101 static bool
102 get_bool(const char *value, bool *value_r)
104 static const char *t[] = { "yes", "true", "1", NULL };
105 static const char *f[] = { "no", "false", "0", NULL };
107 if (string_array_contains(t, value)) {
108 *value_r = true;
109 return true;
112 if (string_array_contains(f, value)) {
113 *value_r = false;
114 return true;
117 return false;
120 struct config_param *
121 config_new_param(const char *value, int line)
123 struct config_param *ret = g_new(struct config_param, 1);
125 if (!value)
126 ret->value = NULL;
127 else
128 ret->value = g_strdup(value);
130 ret->line = line;
132 ret->num_block_params = 0;
133 ret->block_params = NULL;
134 ret->used = false;
136 return ret;
139 static void
140 config_param_free(struct config_param *param)
142 g_free(param->value);
144 for (unsigned i = 0; i < param->num_block_params; i++) {
145 g_free(param->block_params[i].name);
146 g_free(param->block_params[i].value);
149 if (param->num_block_params)
150 g_free(param->block_params);
152 g_free(param);
155 static void
156 config_param_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
158 struct config_param *param = data;
160 config_param_free(param);
163 static struct config_entry *
164 config_entry_get(const char *name)
166 for (unsigned i = 0; i < G_N_ELEMENTS(config_entries); ++i) {
167 struct config_entry *entry = &config_entries[i];
168 if (strcmp(entry->name, name) == 0)
169 return entry;
172 return NULL;
175 void config_global_finish(void)
177 for (unsigned i = 0; i < G_N_ELEMENTS(config_entries); ++i) {
178 struct config_entry *entry = &config_entries[i];
180 g_slist_foreach(entry->params,
181 config_param_free_callback, NULL);
182 g_slist_free(entry->params);
186 void config_global_init(void)
190 static void
191 config_param_check(gpointer data, G_GNUC_UNUSED gpointer user_data)
193 struct config_param *param = data;
195 if (!param->used)
196 /* this whole config_param was not queried at all -
197 the feature might be disabled at compile time?
198 Silently ignore it here. */
199 return;
201 for (unsigned i = 0; i < param->num_block_params; i++) {
202 struct block_param *bp = &param->block_params[i];
204 if (!bp->used)
205 g_warning("option '%s' on line %i was not recognized",
206 bp->name, bp->line);
210 void config_global_check(void)
212 for (unsigned i = 0; i < G_N_ELEMENTS(config_entries); ++i) {
213 struct config_entry *entry = &config_entries[i];
215 g_slist_foreach(entry->params, config_param_check, NULL);
219 bool
220 config_add_block_param(struct config_param * param, const char *name,
221 const char *value, int line, GError **error_r)
223 struct block_param *bp;
225 bp = config_get_block_param(param, name);
226 if (bp != NULL) {
227 g_set_error(error_r, config_quark(), 0,
228 "\"%s\" first defined on line %i, and "
229 "redefined on line %i\n", name,
230 bp->line, line);
231 return false;
234 param->num_block_params++;
236 param->block_params = g_realloc(param->block_params,
237 param->num_block_params *
238 sizeof(param->block_params[0]));
240 bp = &param->block_params[param->num_block_params - 1];
242 bp->name = g_strdup(name);
243 bp->value = g_strdup(value);
244 bp->line = line;
245 bp->used = false;
247 return true;
250 static struct config_param *
251 config_read_block(FILE *fp, int *count, char *string, GError **error_r)
253 struct config_param *ret = config_new_param(NULL, *count);
254 GError *error = NULL;
255 bool success;
257 while (true) {
258 char *line;
259 const char *name, *value;
261 line = fgets(string, MAX_STRING_SIZE, fp);
262 if (line == NULL) {
263 config_param_free(ret);
264 g_set_error(error_r, config_quark(), 0,
265 "Expected '}' before end-of-file");
266 return NULL;
269 (*count)++;
270 line = g_strchug(line);
271 if (*line == 0 || *line == CONF_COMMENT)
272 continue;
274 if (*line == '}') {
275 /* end of this block; return from the function
276 (and from this "while" loop) */
278 line = g_strchug(line + 1);
279 if (*line != 0 && *line != CONF_COMMENT) {
280 config_param_free(ret);
281 g_set_error(error_r, config_quark(), 0,
282 "line %i: Unknown tokens after '}'",
283 *count);
284 return false;
287 return ret;
290 /* parse name and value */
292 name = tokenizer_next_word(&line, &error);
293 if (name == NULL) {
294 assert(*line != 0);
295 config_param_free(ret);
296 g_propagate_prefixed_error(error_r, error,
297 "line %i: ", *count);
298 return NULL;
301 value = tokenizer_next_string(&line, &error);
302 if (value == NULL) {
303 config_param_free(ret);
304 if (*line == 0)
305 g_set_error(error_r, config_quark(), 0,
306 "line %i: Value missing", *count);
307 else
308 g_propagate_prefixed_error(error_r, error,
309 "line %i: ",
310 *count);
311 return NULL;
314 if (*line != 0 && *line != CONF_COMMENT) {
315 config_param_free(ret);
316 g_set_error(error_r, config_quark(), 0,
317 "line %i: Unknown tokens after value",
318 *count);
319 return NULL;
322 success = config_add_block_param(ret, name, value, *count,
323 error_r);
324 if (!success) {
325 config_param_free(ret);
326 return false;
331 bool
332 config_read_file(const char *file, GError **error_r)
334 FILE *fp;
335 char string[MAX_STRING_SIZE + 1];
336 int count = 0;
337 struct config_entry *entry;
338 struct config_param *param;
340 g_debug("loading file %s", file);
342 if (!(fp = fopen(file, "r"))) {
343 g_set_error(error_r, config_quark(), errno,
344 "Failed to open %s: %s",
345 file, strerror(errno));
346 return false;
349 while (fgets(string, MAX_STRING_SIZE, fp)) {
350 char *line;
351 const char *name, *value;
352 GError *error = NULL;
354 count++;
356 line = g_strchug(string);
357 if (*line == 0 || *line == CONF_COMMENT)
358 continue;
360 /* the first token in each line is the name, followed
361 by either the value or '{' */
363 name = tokenizer_next_word(&line, &error);
364 if (name == NULL) {
365 assert(*line != 0);
366 g_propagate_prefixed_error(error_r, error,
367 "line %i: ", count);
368 return false;
371 /* get the definition of that option, and check the
372 "repeatable" flag */
374 entry = config_entry_get(name);
375 if (entry == NULL) {
376 g_set_error(error_r, config_quark(), 0,
377 "unrecognized parameter in config file at "
378 "line %i: %s\n", count, name);
379 return false;
382 if (entry->params != NULL && !entry->repeatable) {
383 param = entry->params->data;
384 g_set_error(error_r, config_quark(), 0,
385 "config parameter \"%s\" is first defined "
386 "on line %i and redefined on line %i\n",
387 name, param->line, count);
388 return false;
391 /* now parse the block or the value */
393 if (entry->block) {
394 /* it's a block, call config_read_block() */
396 if (*line != '{') {
397 g_set_error(error_r, config_quark(), 0,
398 "line %i: '{' expected", count);
399 return false;
402 line = g_strchug(line + 1);
403 if (*line != 0 && *line != CONF_COMMENT) {
404 g_set_error(error_r, config_quark(), 0,
405 "line %i: Unknown tokens after '{'",
406 count);
407 return false;
410 param = config_read_block(fp, &count, string, error_r);
411 if (param == NULL)
412 return false;
413 } else {
414 /* a string value */
416 value = tokenizer_next_string(&line, &error);
417 if (value == NULL) {
418 if (*line == 0)
419 g_set_error(error_r, config_quark(), 0,
420 "line %i: Value missing",
421 count);
422 else {
423 g_set_error(error_r, config_quark(), 0,
424 "line %i: %s", count,
425 error->message);
426 g_error_free(error);
429 return false;
432 if (*line != 0 && *line != CONF_COMMENT) {
433 g_set_error(error_r, config_quark(), 0,
434 "line %i: Unknown tokens after value",
435 count);
436 return false;
439 param = config_new_param(value, count);
442 entry->params = g_slist_append(entry->params, param);
444 fclose(fp);
446 return true;
449 struct config_param *
450 config_get_next_param(const char *name, const struct config_param * last)
452 struct config_entry *entry;
453 GSList *node;
454 struct config_param *param;
456 entry = config_entry_get(name);
457 if (entry == NULL)
458 return NULL;
460 node = entry->params;
462 if (last) {
463 node = g_slist_find(node, last);
464 if (node == NULL)
465 return NULL;
467 node = g_slist_next(node);
470 if (node == NULL)
471 return NULL;
473 param = node->data;
474 param->used = true;
475 return param;
478 const char *
479 config_get_string(const char *name, const char *default_value)
481 const struct config_param *param = config_get_param(name);
483 if (param == NULL)
484 return default_value;
486 return param->value;
489 const char *
490 config_get_path(const char *name)
492 struct config_param *param = config_get_param(name);
493 char *path;
495 if (param == NULL)
496 return NULL;
498 path = parsePath(param->value);
499 if (path == NULL)
500 g_error("error parsing \"%s\" at line %i\n",
501 name, param->line);
503 g_free(param->value);
504 return param->value = path;
507 unsigned
508 config_get_unsigned(const char *name, unsigned default_value)
510 const struct config_param *param = config_get_param(name);
511 long value;
512 char *endptr;
514 if (param == NULL)
515 return default_value;
517 value = strtol(param->value, &endptr, 0);
518 if (*endptr != 0 || value < 0)
519 g_error("Not a valid non-negative number in line %i", param->line);
521 return (unsigned)value;
524 unsigned
525 config_get_positive(const char *name, unsigned default_value)
527 const struct config_param *param = config_get_param(name);
528 long value;
529 char *endptr;
531 if (param == NULL)
532 return default_value;
534 value = strtol(param->value, &endptr, 0);
535 if (*endptr != 0)
536 g_error("Not a valid number in line %i", param->line);
538 if (value <= 0)
539 g_error("Not a positive number in line %i", param->line);
541 return (unsigned)value;
544 struct block_param *
545 config_get_block_param(const struct config_param * param, const char *name)
547 if (param == NULL)
548 return NULL;
550 for (unsigned i = 0; i < param->num_block_params; i++) {
551 if (0 == strcmp(name, param->block_params[i].name)) {
552 struct block_param *bp = &param->block_params[i];
553 bp->used = true;
554 return bp;
558 return NULL;
561 bool config_get_bool(const char *name, bool default_value)
563 const struct config_param *param = config_get_param(name);
564 bool success, value;
566 if (param == NULL)
567 return default_value;
569 success = get_bool(param->value, &value);
570 if (!success)
571 g_error("%s is not a boolean value (yes, true, 1) or "
572 "(no, false, 0) on line %i\n",
573 name, param->line);
575 return value;
578 const char *
579 config_get_block_string(const struct config_param *param, const char *name,
580 const char *default_value)
582 struct block_param *bp = config_get_block_param(param, name);
584 if (bp == NULL)
585 return default_value;
587 return bp->value;
590 unsigned
591 config_get_block_unsigned(const struct config_param *param, const char *name,
592 unsigned default_value)
594 struct block_param *bp = config_get_block_param(param, name);
595 long value;
596 char *endptr;
598 if (bp == NULL)
599 return default_value;
601 value = strtol(bp->value, &endptr, 0);
602 if (*endptr != 0)
603 g_error("Not a valid number in line %i", bp->line);
605 if (value < 0)
606 g_error("Not a positive number in line %i", bp->line);
608 return (unsigned)value;
611 bool
612 config_get_block_bool(const struct config_param *param, const char *name,
613 bool default_value)
615 struct block_param *bp = config_get_block_param(param, name);
616 bool success, value;
618 if (bp == NULL)
619 return default_value;
621 success = get_bool(bp->value, &value);
622 if (!success)
623 g_error("%s is not a boolean value (yes, true, 1) or "
624 "(no, false, 0) on line %i\n",
625 name, bp->line);
627 return value;