]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/conf-parser.c
module: Fix crash in pa_module_unload_all()
[pulseaudio] / src / pulsecore / conf-parser.c
index ef6d6bb653311dd4e75df18fa40e6cba726beb06..200252bb7ad567dd2d180395b5f893d36d385cdc 100644 (file)
@@ -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
 
   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
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
 #define COMMENTS "#;\n"
 
 /* Run the user supplied parser for an assignment */
 #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;
+
+        if (item->section && !state->section)
+            continue;
+
+        if (item->section && !pa_streq(state->section, item->section))
+            continue;
 
 
-    for (; t->parse; t++)
-        if (!strcmp(lvalue, t->lvalue))
-            return t->parse(filename, line, lvalue, rvalue, t->data, userdata);
+        state->data = item->data;
 
 
-    pa_log("[%s:%u] Unknown lvalue '%s'.", filename, line, lvalue);
+        return item->parse(state);
+    }
+
+    pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", state->filename, state->lineno, state->lvalue, pa_strna(state->section));
 
     return -1;
 }
 
 
     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;
 }
 
 
     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;
         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;
     }
 
         return -1;
     }
 
-    *e = 0;
-    e++;
+    *state->rvalue = 0;
+    state->rvalue++;
+
+    state->lvalue = pa_strip(state->lvalue);
+    state->rvalue = pa_strip(state->rvalue);
 
 
-    return next_assignment(filename, line, t, strip(b), strip(e), userdata);
+    if (state->in_proplist)
+        return proplist_assignment(state);
+    else
+        return normal_assignment(state);
 }
 
 /* Go through the file and parse each line */
 }
 
 /* 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;
     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);
 
 
     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) {
         if (errno == ENOENT) {
+            pa_log_debug("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno));
             r = 0;
             goto finish;
         }
 
             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;
     }
 
         goto finish;
     }
 
+    state.filename = filename;
+    state.item_table = t;
+    state.userdata = userdata;
+
+    if (proplist)
+        state.proplist = pa_proplist_new();
+
     while (!feof(f)) {
     while (!feof(f)) {
-        char l[256];
-        if (!fgets(l, sizeof(l), f)) {
+        if (!fgets(state.buf, sizeof(state.buf), f)) {
             if (feof(f))
                 break;
 
             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;
         }
 
             goto finish;
         }
 
-        if (parse_line(filename, ++line, t,  l, userdata) < 0)
+        state.lineno++;
+
+        if (parse_line(&state) < 0)
             goto finish;
     }
 
             goto finish;
     }
 
+    if (proplist)
+        pa_proplist_update(proplist, PA_UPDATE_REPLACE, state.proplist);
+
     r = 0;
 
 finish:
     r = 0;
 
 finish:
+    if (state.proplist)
+        pa_proplist_free(state.proplist);
+
+    pa_xfree(state.section);
 
     if (do_close && f)
         fclose(f);
 
     if (do_close && f)
         fclose(f);
@@ -148,17 +215,16 @@ finish:
     return r;
 }
 
     return r;
 }
 
-int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
-    int *i = data;
+int pa_config_parse_int(pa_config_parser_state *state) {
+    int *i;
     int32_t k;
 
     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;
     }
 
         return -1;
     }
 
@@ -166,17 +232,16 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue,
     return 0;
 }
 
     return 0;
 }
 
-int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
-    unsigned *u = data;
+int pa_config_parse_unsigned(pa_config_parser_state *state) {
+    unsigned *u;
     uint32_t k;
 
     uint32_t k;
 
-    pa_assert(filename);
-    pa_assert(lvalue);
-    pa_assert(rvalue);
-    pa_assert(data);
+    pa_assert(state);
 
 
-    if (pa_atou(rvalue, &k) < 0) {
-        pa_log("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
+    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;
     }
 
         return -1;
     }
 
@@ -184,17 +249,16 @@ int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lv
     return 0;
 }
 
     return 0;
 }
 
-int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
-    size_t *i = data;
+int pa_config_parse_size(pa_config_parser_state *state) {
+    size_t *i;
     uint32_t k;
 
     uint32_t k;
 
-    pa_assert(filename);
-    pa_assert(lvalue);
-    pa_assert(rvalue);
-    pa_assert(data);
+    pa_assert(state);
 
 
-    if (pa_atou(rvalue, &k) < 0) {
-        pa_log("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
+    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;
     }
 
         return -1;
     }
 
@@ -202,17 +266,16 @@ int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue
     return 0;
 }
 
     return 0;
 }
 
-int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
+int pa_config_parse_bool(pa_config_parser_state *state) {
     int k;
     int k;
-    pa_bool_t *b = data;
+    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(rvalue)) < 0) {
-        pa_log("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
+    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;
     }
 
         return -1;
     }
 
@@ -221,15 +284,32 @@ int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue
     return 0;
 }
 
     return 0;
 }
 
-int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, 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);
 
     pa_xfree(*s);
-    *s = *rvalue ? pa_xstrdup(rvalue) : NULL;
+    *s = *state->rvalue ? pa_xstrdup(state->rvalue) : NULL;
     return 0;
 }
     return 0;
 }