1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2004-2006 Lennart Poettering 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published 8 by the Free Software Foundation; either version 2.1 of the License, 9 or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <dirent.h> 25#include <string.h> 26#include <stdio.h> 27#include <errno.h> 28 29#include <pulse/xmalloc.h> 30 31#include <pulsecore/core-error.h> 32#include <pulsecore/log.h> 33#include <pulsecore/core-util.h> 34#include <pulsecore/macro.h> 35 36#include "conf-parser.h" 37 38#define WHITESPACE " \t\n" 39#define COMMENTS "#;\n" 40 41/* Run the user supplied parser for an assignment */ 42static int normal_assignment(pa_config_parser_state *state) { 43 const pa_config_item *item; 44 45 pa_assert(state); 46 47 for (item = state->item_table; item->parse; item++) { 48 49 if (item->lvalue && !pa_streq(state->lvalue, item->lvalue)) 50 continue; 51 52 if (item->section && !state->section) 53 continue; 54 55 if (item->section && !pa_streq(state->section, item->section)) 56 continue; 57 58 state->data = item->data; 59 60 return item->parse(state); 61 } 62 63 pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", state->filename, state->lineno, state->lvalue, pa_strna(state->section)); 64 65 return -1; 66} 67 68/* Parse a proplist entry. */ 69static int proplist_assignment(pa_config_parser_state *state) { 70 pa_assert(state); 71 pa_assert(state->proplist); 72 73 if (pa_proplist_sets(state->proplist, state->lvalue, state->rvalue) < 0) { 74 pa_log("[%s:%u] Failed to parse a proplist entry: %s = %s", state->filename, state->lineno, state->lvalue, state->rvalue); 75 return -1; 76 } 77 78 return 0; 79} 80 81/* Parse a variable assignment line */ 82static int parse_line(pa_config_parser_state *state) { 83 char *c; 84 85 state->lvalue = state->buf + strspn(state->buf, WHITESPACE); 86 87 if ((c = strpbrk(state->lvalue, COMMENTS))) 88 *c = 0; 89 90 if (!*state->lvalue) 91 return 0; 92 93 if (pa_startswith(state->lvalue, ".include ")) { 94 char *path = NULL, *fn; 95 int r; 96 97 fn = pa_strip(state->lvalue + 9); 98 if (!pa_is_path_absolute(fn)) { 99 const char *k; 100 if ((k = strrchr(state->filename, '/'))) { 101 char *dir = pa_xstrndup(state->filename, k - state->filename); 102 fn = path = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", dir, fn); 103 pa_xfree(dir); 104 } 105 } 106 107 r = pa_config_parse(fn, NULL, state->item_table, state->proplist, false, state->userdata); 108 pa_xfree(path); 109 return r; 110 } 111 112 if (*state->lvalue == '[') { 113 size_t k; 114 115 k = strlen(state->lvalue); 116 pa_assert(k > 0); 117 118 if (state->lvalue[k-1] != ']') { 119 pa_log("[%s:%u] Invalid section header.", state->filename, state->lineno); 120 return -1; 121 } 122 123 pa_xfree(state->section); 124 state->section = pa_xstrndup(state->lvalue + 1, k-2); 125 126 if (pa_streq(state->section, "Properties")) { 127 if (!state->proplist) { 128 pa_log("[%s:%u] \"Properties\" section is not allowed in this file.", state->filename, state->lineno); 129 return -1; 130 } 131 132 state->in_proplist = true; 133 } else 134 state->in_proplist = false; 135 136 return 0; 137 } 138 139 if (!(state->rvalue = strchr(state->lvalue, '='))) { 140 pa_log("[%s:%u] Missing '='.", state->filename, state->lineno); 141 return -1; 142 } 143 144 *state->rvalue = 0; 145 state->rvalue++; 146 147 state->lvalue = pa_strip(state->lvalue); 148 state->rvalue = pa_strip(state->rvalue); 149 150 if (state->in_proplist) 151 return proplist_assignment(state); 152 else 153 return normal_assignment(state); 154} 155 156#ifndef OS_IS_WIN32 157static int conf_filter(const struct dirent *entry) { 158 return pa_endswith(entry->d_name, ".conf"); 159} 160#endif 161 162/* Go through the file and parse each line */ 163int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, pa_proplist *proplist, bool use_dot_d, 164 void *userdata) { 165 int r = -1; 166 bool do_close = !f; 167 pa_config_parser_state state; 168 169 pa_assert(filename); 170 pa_assert(t); 171 172 pa_zero(state); 173 174 if (!f && !(f = pa_fopen_cloexec(filename, "r"))) { 175 if (errno == ENOENT) { 176 pa_log_debug("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno)); 177 r = 0; 178 goto finish; 179 } 180 181 pa_log_warn("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno)); 182 goto finish; 183 } 184 pa_log_debug("Parsing configuration file '%s'", filename); 185 186 state.filename = filename; 187 state.item_table = t; 188 state.userdata = userdata; 189 190 if (proplist) 191 state.proplist = pa_proplist_new(); 192 193 while (!feof(f)) { 194 if (!fgets(state.buf, sizeof(state.buf), f)) { 195 if (feof(f)) 196 break; 197 198 pa_log_warn("Failed to read configuration file '%s': %s", filename, pa_cstrerror(errno)); 199 goto finish; 200 } 201 202 state.lineno++; 203 204 if (parse_line(&state) < 0) 205 goto finish; 206 } 207 208 if (proplist) 209 pa_proplist_update(proplist, PA_UPDATE_REPLACE, state.proplist); 210 211 r = 0; 212 213finish: 214 if (state.proplist) 215 pa_proplist_free(state.proplist); 216 217 pa_xfree(state.section); 218 219 if (do_close && f) 220 fclose(f); 221 222 if (use_dot_d) { 223#ifdef OS_IS_WIN32 224 char *dir_name = pa_sprintf_malloc("%s.d", filename); 225 char *pattern = pa_sprintf_malloc("%s\\*.conf", dir_name); 226 HANDLE fh; 227 WIN32_FIND_DATA wfd; 228 229 fh = FindFirstFile(pattern, &wfd); 230 if (fh != INVALID_HANDLE_VALUE) { 231 do { 232 if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { 233 char *filename2 = pa_sprintf_malloc("%s\\%s", dir_name, wfd.cFileName); 234 pa_config_parse(filename2, NULL, t, proplist, false, userdata); 235 pa_xfree(filename2); 236 } 237 } while (FindNextFile(fh, &wfd)); 238 FindClose(fh); 239 } else { 240 DWORD err = GetLastError(); 241 242 if (err == ERROR_PATH_NOT_FOUND) { 243 pa_log_debug("Pattern %s did not match any files, ignoring.", pattern); 244 } else { 245 LPVOID msgbuf; 246 DWORD fret = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 247 NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&msgbuf, 0, NULL); 248 249 if (fret != 0) { 250 pa_log_warn("FindFirstFile(%s) failed with error %ld (%s), ignoring.", pattern, err, (char*)msgbuf); 251 LocalFree(msgbuf); 252 } else { 253 pa_log_warn("FindFirstFile(%s) failed with error %ld, ignoring.", pattern, err); 254 pa_log_warn("FormatMessage failed with error %lu", GetLastError()); 255 } 256 } 257 } 258 259 pa_xfree(pattern); 260 pa_xfree(dir_name); 261#else 262 char *dir_name; 263 int n; 264 struct dirent **entries = NULL; 265 266 dir_name = pa_sprintf_malloc("%s.d", filename); 267 268 n = scandir(dir_name, &entries, conf_filter, alphasort); 269 if (n >= 0) { 270 int i; 271 272 for (i = 0; i < n; i++) { 273 char *filename2; 274 275 filename2 = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", dir_name, entries[i]->d_name); 276 pa_config_parse(filename2, NULL, t, proplist, false, userdata); 277 pa_xfree(filename2); 278 279 free(entries[i]); 280 } 281 282 free(entries); 283 } else { 284 if (errno == ENOENT) 285 pa_log_debug("%s does not exist, ignoring.", dir_name); 286 else 287 pa_log_warn("scandir(\"%s\") failed: %s", dir_name, pa_cstrerror(errno)); 288 } 289 290 pa_xfree(dir_name); 291#endif 292 } 293 294 return r; 295} 296 297int pa_config_parse_int(pa_config_parser_state *state) { 298 int *i; 299 int32_t k; 300 301 pa_assert(state); 302 303 i = state->data; 304 305 if (pa_atoi(state->rvalue, &k) < 0) { 306 pa_log("[%s:%u] Failed to parse numeric value: %s", state->filename, state->lineno, state->rvalue); 307 return -1; 308 } 309 310 *i = (int) k; 311 return 0; 312} 313 314int pa_config_parse_unsigned(pa_config_parser_state *state) { 315 unsigned *u; 316 uint32_t k; 317 318 pa_assert(state); 319 320 u = state->data; 321 322 if (pa_atou(state->rvalue, &k) < 0) { 323 pa_log("[%s:%u] Failed to parse numeric value: %s", state->filename, state->lineno, state->rvalue); 324 return -1; 325 } 326 327 *u = (unsigned) k; 328 return 0; 329} 330 331int pa_config_parse_size(pa_config_parser_state *state) { 332 size_t *i; 333 uint32_t k; 334 335 pa_assert(state); 336 337 i = state->data; 338 339 if (pa_atou(state->rvalue, &k) < 0) { 340 pa_log("[%s:%u] Failed to parse numeric value: %s", state->filename, state->lineno, state->rvalue); 341 return -1; 342 } 343 344 *i = (size_t) k; 345 return 0; 346} 347 348int pa_config_parse_bool(pa_config_parser_state *state) { 349 int k; 350 bool *b; 351 352 pa_assert(state); 353 354 b = state->data; 355 356 if ((k = pa_parse_boolean(state->rvalue)) < 0) { 357 pa_log("[%s:%u] Failed to parse boolean value: %s", state->filename, state->lineno, state->rvalue); 358 return -1; 359 } 360 361 *b = !!k; 362 363 return 0; 364} 365 366int pa_config_parse_not_bool(pa_config_parser_state *state) { 367 int k; 368 bool *b; 369 370 pa_assert(state); 371 372 b = state->data; 373 374 if ((k = pa_parse_boolean(state->rvalue)) < 0) { 375 pa_log("[%s:%u] Failed to parse boolean value: %s", state->filename, state->lineno, state->rvalue); 376 return -1; 377 } 378 379 *b = !k; 380 381 return 0; 382} 383 384int pa_config_parse_string(pa_config_parser_state *state) { 385 char **s; 386 387 pa_assert(state); 388 389 s = state->data; 390 391 pa_xfree(*s); 392 *s = *state->rvalue ? pa_xstrdup(state->rvalue) : NULL; 393 return 0; 394} 395