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.
23 #include "tokenizer.h"
25 #include "glib_compat.h"
36 #define G_LOG_DOMAIN "config"
38 #define MAX_STRING_SIZE MPD_PATH_MAX+80
40 #define CONF_COMMENT '#'
43 const char *const name
;
44 const bool repeatable
;
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 },
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
)) {
111 if (string_array_contains(f
, value
)) {
119 struct config_param
*
120 config_new_param(const char *value
, int line
)
122 struct config_param
*ret
= g_new(struct config_param
, 1);
127 ret
->value
= g_strdup(value
);
131 ret
->num_block_params
= 0;
132 ret
->block_params
= NULL
;
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
);
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)
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)
190 config_param_check(gpointer data
, G_GNUC_UNUSED gpointer user_data
)
192 struct config_param
*param
= data
;
195 /* this whole config_param was not queried at all -
196 the feature might be disabled at compile time?
197 Silently ignore it here. */
200 for (unsigned i
= 0; i
< param
->num_block_params
; i
++) {
201 struct block_param
*bp
= ¶m
->block_params
[i
];
204 g_warning("option '%s' on line %i was not recognized",
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
);
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
);
226 g_set_error(error_r
, config_quark(), 0,
227 "\"%s\" first defined on line %i, and "
228 "redefined on line %i\n", name
,
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
= ¶m
->block_params
[param
->num_block_params
- 1];
241 bp
->name
= g_strdup(name
);
242 bp
->value
= g_strdup(value
);
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
;
258 const char *name
, *value
;
260 line
= fgets(string
, MAX_STRING_SIZE
, fp
);
262 config_param_free(ret
);
263 g_set_error(error_r
, config_quark(), 0,
264 "Expected '}' before end-of-file");
269 line
= g_strchug(line
);
270 if (*line
== 0 || *line
== CONF_COMMENT
)
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 '}'",
289 /* parse name and value */
291 name
= tokenizer_next_word(&line
, &error
);
294 config_param_free(ret
);
295 g_propagate_prefixed_error(error_r
, error
,
296 "line %i: ", *count
);
300 value
= tokenizer_next_string(&line
, &error
);
302 config_param_free(ret
);
304 g_set_error(error_r
, config_quark(), 0,
305 "line %i: Value missing", *count
);
307 g_propagate_prefixed_error(error_r
, error
,
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",
321 success
= config_add_block_param(ret
, name
, value
, *count
,
324 config_param_free(ret
);
331 config_read_file(const char *file
, GError
**error_r
)
334 char string
[MAX_STRING_SIZE
+ 1];
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
));
348 while (fgets(string
, MAX_STRING_SIZE
, fp
)) {
350 const char *name
, *value
;
351 GError
*error
= NULL
;
355 line
= g_strchug(string
);
356 if (*line
== 0 || *line
== CONF_COMMENT
)
359 /* the first token in each line is the name, followed
360 by either the value or '{' */
362 name
= tokenizer_next_word(&line
, &error
);
365 g_propagate_prefixed_error(error_r
, error
,
370 /* get the definition of that option, and check the
373 entry
= config_entry_get(name
);
375 g_set_error(error_r
, config_quark(), 0,
376 "unrecognized parameter in config file at "
377 "line %i: %s\n", count
, name
);
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
);
390 /* now parse the block or the value */
393 /* it's a block, call config_read_block() */
396 g_set_error(error_r
, config_quark(), 0,
397 "line %i: '{' expected", count
);
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 '{'",
409 param
= config_read_block(fp
, &count
, string
, error_r
);
415 value
= tokenizer_next_string(&line
, &error
);
418 g_set_error(error_r
, config_quark(), 0,
419 "line %i: Value missing",
422 g_set_error(error_r
, config_quark(), 0,
423 "line %i: %s", count
,
431 if (*line
!= 0 && *line
!= CONF_COMMENT
) {
432 g_set_error(error_r
, config_quark(), 0,
433 "line %i: Unknown tokens after value",
438 param
= config_new_param(value
, count
);
441 entry
->params
= g_slist_append(entry
->params
, param
);
448 struct config_param
*
449 config_get_next_param(const char *name
, const struct config_param
* last
)
451 struct config_entry
*entry
;
453 struct config_param
*param
;
455 entry
= config_entry_get(name
);
459 node
= entry
->params
;
462 node
= g_slist_find(node
, last
);
466 node
= g_slist_next(node
);
478 config_get_string(const char *name
, const char *default_value
)
480 const struct config_param
*param
= config_get_param(name
);
483 return default_value
;
489 config_get_path(const char *name
)
491 struct config_param
*param
= config_get_param(name
);
497 path
= parsePath(param
->value
);
499 g_error("error parsing \"%s\" at line %i\n",
502 g_free(param
->value
);
503 return param
->value
= path
;
507 config_get_positive(const char *name
, unsigned default_value
)
509 const struct config_param
*param
= config_get_param(name
);
514 return default_value
;
516 value
= strtol(param
->value
, &endptr
, 0);
518 g_error("Not a valid number in line %i", param
->line
);
521 g_error("Not a positive number in line %i", param
->line
);
523 return (unsigned)value
;
527 config_get_block_param(const struct config_param
* param
, const char *name
)
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
= ¶m
->block_params
[i
];
543 bool config_get_bool(const char *name
, bool default_value
)
545 const struct config_param
*param
= config_get_param(name
);
549 return default_value
;
551 success
= get_bool(param
->value
, &value
);
553 g_error("%s is not a boolean value (yes, true, 1) or "
554 "(no, false, 0) on line %i\n",
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
);
567 return default_value
;
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
);
581 return default_value
;
583 value
= strtol(bp
->value
, &endptr
, 0);
585 g_error("Not a valid number in line %i", bp
->line
);
588 g_error("Not a positive number in line %i", bp
->line
);
590 return (unsigned)value
;
594 config_get_block_bool(const struct config_param
*param
, const char *name
,
597 struct block_param
*bp
= config_get_block_param(param
, name
);
601 return default_value
;
603 success
= get_bool(bp
->value
, &value
);
605 g_error("%s is not a boolean value (yes, true, 1) or "
606 "(no, false, 0) on line %i\n",