X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/a87ba420698b7f58d8eaa9236bb01bf11558ed35..6d61c7779d1f5dc139cd6f354d9b75ce4b20e37c:/src/pulsecore/conf-parser.c diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c index 4aec45d7..200252bb 100644 --- a/src/pulsecore/conf-parser.c +++ b/src/pulsecore/conf-parser.c @@ -5,7 +5,7 @@ PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, + by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but @@ -40,107 +40,174 @@ #define COMMENTS "#;\n" /* Run the user supplied parser for an assignment */ -static int next_assignment(const char *filename, unsigned line, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) { - pa_assert(filename); - pa_assert(t); - pa_assert(lvalue); - pa_assert(rvalue); +static int normal_assignment(pa_config_parser_state *state) { + const pa_config_item *item; + + pa_assert(state); + + for (item = state->item_table; item->parse; item++) { + + if (item->lvalue && !pa_streq(state->lvalue, item->lvalue)) + continue; - for (; t->parse; t++) - if (!strcmp(lvalue, t->lvalue)) - return t->parse(filename, line, lvalue, rvalue, t->data, userdata); + if (item->section && !state->section) + continue; + + if (item->section && !pa_streq(state->section, item->section)) + continue; + + state->data = item->data; + + return item->parse(state); + } - pa_log("[%s:%u] Unknown lvalue '%s'.", filename, line, lvalue); + pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", state->filename, state->lineno, state->lvalue, pa_strna(state->section)); return -1; } -/* Returns non-zero when c is contained in s */ -static int in_string(char c, const char *s) { - pa_assert(s); +/* Parse a proplist entry. */ +static int proplist_assignment(pa_config_parser_state *state) { + pa_assert(state); + pa_assert(state->proplist); - for (; *s; s++) - if (*s == c) - return 1; + if (pa_proplist_sets(state->proplist, state->lvalue, state->rvalue) < 0) { + pa_log("[%s:%u] Failed to parse a proplist entry: %s = %s", state->filename, state->lineno, state->lvalue, state->rvalue); + return -1; + } return 0; } -/* Remove all whitepsapce from the beginning and the end of *s. *s may - * be modified. */ -static char *strip(char *s) { - char *b = s+strspn(s, WHITESPACE); - char *e, *l = NULL; +/* Parse a variable assignment line */ +static int parse_line(pa_config_parser_state *state) { + char *c; - for (e = b; *e; e++) - if (!in_string(*e, WHITESPACE)) - l = e; + state->lvalue = state->buf + strspn(state->buf, WHITESPACE); - if (l) - *(l+1) = 0; + if ((c = strpbrk(state->lvalue, COMMENTS))) + *c = 0; - return b; -} + if (!*state->lvalue) + return 0; -/* Parse a variable assignment line */ -static int parse_line(const char *filename, unsigned line, const pa_config_item *t, char *l, void *userdata) { - char *e, *c, *b = l+strspn(l, WHITESPACE); + if (pa_startswith(state->lvalue, ".include ")) { + char *path = NULL, *fn; + int r; + + fn = pa_strip(state->lvalue + 9); + if (!pa_is_path_absolute(fn)) { + const char *k; + if ((k = strrchr(state->filename, '/'))) { + char *dir = pa_xstrndup(state->filename, k - state->filename); + fn = path = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", dir, fn); + pa_xfree(dir); + } + } - if ((c = strpbrk(b, COMMENTS))) - *c = 0; + r = pa_config_parse(fn, NULL, state->item_table, state->proplist, state->userdata); + pa_xfree(path); + return r; + } + + if (*state->lvalue == '[') { + size_t k; + + k = strlen(state->lvalue); + pa_assert(k > 0); + + if (state->lvalue[k-1] != ']') { + pa_log("[%s:%u] Invalid section header.", state->filename, state->lineno); + return -1; + } + + pa_xfree(state->section); + state->section = pa_xstrndup(state->lvalue + 1, k-2); + + if (pa_streq(state->section, "Properties")) { + if (!state->proplist) { + pa_log("[%s:%u] \"Properties\" section is not allowed in this file.", state->filename, state->lineno); + return -1; + } + + state->in_proplist = true; + } else + state->in_proplist = false; - if (!*b) return 0; + } - if (!(e = strchr(b, '='))) { - pa_log("[%s:%u] Missing '='.", filename, line); + if (!(state->rvalue = strchr(state->lvalue, '='))) { + pa_log("[%s:%u] Missing '='.", state->filename, state->lineno); return -1; } - *e = 0; - e++; + *state->rvalue = 0; + state->rvalue++; - return next_assignment(filename, line, t, strip(b), strip(e), userdata); + state->lvalue = pa_strip(state->lvalue); + state->rvalue = pa_strip(state->rvalue); + + if (state->in_proplist) + return proplist_assignment(state); + else + return normal_assignment(state); } /* Go through the file and parse each line */ -int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata) { +int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, pa_proplist *proplist, void *userdata) { int r = -1; - unsigned line = 0; - int do_close = !f; + bool do_close = !f; + pa_config_parser_state state; pa_assert(filename); pa_assert(t); - if (!f && !(f = fopen(filename, "r"))) { + pa_zero(state); + + if (!f && !(f = pa_fopen_cloexec(filename, "r"))) { if (errno == ENOENT) { + pa_log_debug("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno)); r = 0; goto finish; } - pa_log_warn("Failed to open configuration file '%s': %s", - filename, pa_cstrerror(errno)); + pa_log_warn("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno)); goto finish; } + state.filename = filename; + state.item_table = t; + state.userdata = userdata; + + if (proplist) + state.proplist = pa_proplist_new(); + while (!feof(f)) { - char l[256]; - if (!fgets(l, sizeof(l), f)) { + if (!fgets(state.buf, sizeof(state.buf), f)) { if (feof(f)) break; - pa_log_warn("Failed to read configuration file '%s': %s", - filename, pa_cstrerror(errno)); + pa_log_warn("Failed to read configuration file '%s': %s", filename, pa_cstrerror(errno)); goto finish; } - if (parse_line(filename, ++line, t, l, userdata) < 0) + state.lineno++; + + if (parse_line(&state) < 0) goto finish; } + if (proplist) + pa_proplist_update(proplist, PA_UPDATE_REPLACE, state.proplist); + r = 0; finish: + if (state.proplist) + pa_proplist_free(state.proplist); + + pa_xfree(state.section); if (do_close && f) fclose(f); @@ -148,17 +215,16 @@ finish: return r; } -int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { - int *i = data; +int pa_config_parse_int(pa_config_parser_state *state) { + int *i; int32_t k; - pa_assert(filename); - pa_assert(lvalue); - pa_assert(rvalue); - pa_assert(data); + pa_assert(state); - if (pa_atoi(rvalue, &k) < 0) { - pa_log("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); + i = state->data; + + if (pa_atoi(state->rvalue, &k) < 0) { + pa_log("[%s:%u] Failed to parse numeric value: %s", state->filename, state->lineno, state->rvalue); return -1; } @@ -166,17 +232,50 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, return 0; } -int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { +int pa_config_parse_unsigned(pa_config_parser_state *state) { + unsigned *u; + uint32_t k; + + pa_assert(state); + + u = state->data; + + if (pa_atou(state->rvalue, &k) < 0) { + pa_log("[%s:%u] Failed to parse numeric value: %s", state->filename, state->lineno, state->rvalue); + return -1; + } + + *u = (unsigned) k; + return 0; +} + +int pa_config_parse_size(pa_config_parser_state *state) { + size_t *i; + uint32_t k; + + pa_assert(state); + + i = state->data; + + if (pa_atou(state->rvalue, &k) < 0) { + pa_log("[%s:%u] Failed to parse numeric value: %s", state->filename, state->lineno, state->rvalue); + return -1; + } + + *i = (size_t) k; + return 0; +} + +int pa_config_parse_bool(pa_config_parser_state *state) { int k; - pa_bool_t *b = data; + bool *b; - pa_assert(filename); - pa_assert(lvalue); - pa_assert(rvalue); - pa_assert(data); + pa_assert(state); - if ((k = pa_parse_boolean(rvalue)) < 0) { - pa_log("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue); + b = state->data; + + if ((k = pa_parse_boolean(state->rvalue)) < 0) { + pa_log("[%s:%u] Failed to parse boolean value: %s", state->filename, state->lineno, state->rvalue); return -1; } @@ -185,15 +284,32 @@ int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue return 0; } -int pa_config_parse_string(const char *filename, PA_GCC_UNUSED unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { - char **s = data; +int pa_config_parse_not_bool(pa_config_parser_state *state) { + int k; + bool *b; - pa_assert(filename); - pa_assert(lvalue); - pa_assert(rvalue); - pa_assert(data); + pa_assert(state); + + b = state->data; + + if ((k = pa_parse_boolean(state->rvalue)) < 0) { + pa_log("[%s:%u] Failed to parse boolean value: %s", state->filename, state->lineno, state->rvalue); + return -1; + } + + *b = !k; + + return 0; +} + +int pa_config_parse_string(pa_config_parser_state *state) { + char **s; + + pa_assert(state); + + s = state->data; pa_xfree(*s); - *s = *rvalue ? pa_xstrdup(rvalue) : NULL; + *s = *state->rvalue ? pa_xstrdup(state->rvalue) : NULL; return 0; }