2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
23 #define _WIN32_IE 0x501
25 #define _WIN32_IE 0x400
46 typedef struct ConfigEntry
{
51 typedef struct ConfigBlock
{
53 unsigned int entryCount
;
55 static ConfigBlock cfgBlock
;
58 static char *lstrip(char *line
)
60 while(isspace(line
[0]))
65 static char *rstrip(char *line
)
67 size_t len
= strlen(line
);
68 while(len
> 0 && isspace(line
[len
-1]))
74 static int readline(FILE *f
, char **output
, size_t *maxlen
)
79 while((c
=fgetc(f
)) != EOF
&& (c
== '\r' || c
== '\n'))
90 newmax
= (*maxlen
? (*maxlen
)<<1 : 32);
92 temp
= realloc(*output
, newmax
);
95 ERR("Failed to realloc "SZFMT
" bytes from "SZFMT
"!\n", newmax
, *maxlen
);
102 (*output
)[len
++] = c
;
103 (*output
)[len
] = '\0';
104 } while((c
=fgetc(f
)) != EOF
&& c
!= '\r' && c
!= '\n');
110 static char *expdup(const char *str
)
124 const char *next
= strchr(str
, '$');
126 addstrlen
= next
? (size_t)(next
-str
) : strlen(str
);
135 const char *next
= strchr(str
+1, '$');
137 addstrlen
= next
? (size_t)(next
-str
) : strlen(str
);
147 hasbraces
= (*str
== '{');
150 while((isalnum(*str
) || *str
== '_') && k
< sizeof(envname
)-1)
151 envname
[k
++] = *(str
++);
154 if(hasbraces
&& *str
!= '}')
158 if((addstr
=getenv(envname
)) == NULL
)
160 addstrlen
= strlen(addstr
);
166 if(addstrlen
>= maxlen
-len
)
171 newmax
= len
+addstrlen
+1;
173 temp
= realloc(output
, newmax
);
176 ERR("Failed to realloc "SZFMT
" bytes from "SZFMT
"!\n", newmax
, maxlen
);
184 for(i
= 0;i
< addstrlen
;i
++)
185 output
[len
++] = addstr
[i
];
189 return output
? output
: calloc(1, 1);
193 static void LoadConfigFromFile(FILE *f
)
195 char curSection
[128] = "";
200 while(readline(f
, &buffer
, &maxlen
))
202 char *line
, *comment
;
204 char value
[256] = "";
206 line
= rstrip(lstrip(buffer
));
207 if(!line
[0]) continue;
211 char *section
= line
+1;
214 endsection
= strchr(section
, ']');
215 if(!endsection
|| section
== endsection
)
217 ERR("config parse error: bad line \"%s\"\n", line
);
220 if(endsection
[1] != 0)
222 char *end
= endsection
+1;
225 if(*end
!= 0 && *end
!= '#')
227 ERR("config parse error: bad line \"%s\"\n", line
);
233 if(strcasecmp(section
, "general") == 0)
239 char *nextp
= strchr(section
, '%');
242 strncpy(curSection
+p
, section
, sizeof(curSection
)-1-p
);
246 len
= nextp
- section
;
247 if(len
> sizeof(curSection
)-1-p
)
248 len
= sizeof(curSection
)-1-p
;
249 strncpy(curSection
+p
, section
, len
);
253 if(((section
[1] >= '0' && section
[1] <= '9') ||
254 (section
[1] >= 'a' && section
[1] <= 'f') ||
255 (section
[1] >= 'A' && section
[1] <= 'F')) &&
256 ((section
[2] >= '0' && section
[2] <= '9') ||
257 (section
[2] >= 'a' && section
[2] <= 'f') ||
258 (section
[2] >= 'A' && section
[2] <= 'F')))
261 if(section
[1] >= '0' && section
[1] <= '9')
262 b
= (section
[1]-'0') << 4;
263 else if(section
[1] >= 'a' && section
[1] <= 'f')
264 b
= (section
[1]-'a'+0xa) << 4;
265 else if(section
[1] >= 'A' && section
[1] <= 'F')
266 b
= (section
[1]-'A'+0x0a) << 4;
267 if(section
[2] >= '0' && section
[2] <= '9')
268 b
|= (section
[2]-'0');
269 else if(section
[2] >= 'a' && section
[2] <= 'f')
270 b
|= (section
[2]-'a'+0xa);
271 else if(section
[2] >= 'A' && section
[2] <= 'F')
272 b
|= (section
[2]-'A'+0x0a);
273 if(p
< sizeof(curSection
)-1)
277 else if(section
[1] == '%')
279 if(p
< sizeof(curSection
)-1)
280 curSection
[p
++] = '%';
285 if(p
< sizeof(curSection
)-1)
286 curSection
[p
++] = '%';
289 if(p
< sizeof(curSection
)-1)
291 } while(p
< sizeof(curSection
)-1 && *section
!= 0);
292 curSection
[sizeof(curSection
)-1] = 0;
298 comment
= strchr(line
, '#');
299 if(comment
) *(comment
++) = 0;
300 if(!line
[0]) continue;
302 if(sscanf(line
, "%255[^=] = \"%255[^\"]\"", key
, value
) == 2 ||
303 sscanf(line
, "%255[^=] = '%255[^\']'", key
, value
) == 2 ||
304 sscanf(line
, "%255[^=] = %255[^\n]", key
, value
) == 2)
306 /* sscanf doesn't handle '' or "" as empty values, so clip it
308 if(strcmp(value
, "\"\"") == 0 || strcmp(value
, "''") == 0)
311 else if(sscanf(line
, "%255[^=] %255[=]", key
, value
) == 2)
313 /* Special case for 'key =' */
318 ERR("config parse error: malformed option line: \"%s\"\n\n", line
);
323 if(curSection
[0] != 0)
325 size_t len
= strlen(curSection
);
326 memmove(&key
[len
+1], key
, sizeof(key
)-1-len
);
328 memcpy(key
, curSection
, len
);
331 /* Check if we already have this option set */
332 ent
= cfgBlock
.entries
;
333 while((unsigned int)(ent
-cfgBlock
.entries
) < cfgBlock
.entryCount
)
335 if(strcasecmp(ent
->key
, key
) == 0)
340 if((unsigned int)(ent
-cfgBlock
.entries
) >= cfgBlock
.entryCount
)
342 /* Allocate a new option entry */
343 ent
= realloc(cfgBlock
.entries
, (cfgBlock
.entryCount
+1)*sizeof(ConfigEntry
));
346 ERR("config parse error: error reallocating config entries\n");
349 cfgBlock
.entries
= ent
;
350 ent
= cfgBlock
.entries
+ cfgBlock
.entryCount
;
351 cfgBlock
.entryCount
++;
353 ent
->key
= strdup(key
);
358 ent
->value
= expdup(value
);
360 TRACE("found '%s' = '%s'\n", ent
->key
, ent
->value
);
367 void ReadALConfig(void)
369 al_string ppath
= AL_STRING_INIT_STATIC();
370 WCHAR buffer
[MAX_PATH
];
374 if(SHGetSpecialFolderPathW(NULL
, buffer
, CSIDL_APPDATA
, FALSE
) != FALSE
)
376 al_string filepath
= AL_STRING_INIT_STATIC();
377 alstr_copy_wcstr(&filepath
, buffer
);
378 alstr_append_cstr(&filepath
, "\\alsoft.ini");
380 TRACE("Loading config %s...\n", alstr_get_cstr(filepath
));
381 f
= al_fopen(alstr_get_cstr(filepath
), "rt");
384 LoadConfigFromFile(f
);
387 alstr_reset(&filepath
);
390 GetProcBinary(&ppath
, NULL
);
391 if(!alstr_empty(ppath
))
393 alstr_append_cstr(&ppath
, "\\alsoft.ini");
394 TRACE("Loading config %s...\n", alstr_get_cstr(ppath
));
395 f
= al_fopen(alstr_get_cstr(ppath
), "r");
398 LoadConfigFromFile(f
);
403 if((str
=_wgetenv(L
"ALSOFT_CONF")) != NULL
&& *str
)
405 al_string filepath
= AL_STRING_INIT_STATIC();
406 alstr_copy_wcstr(&filepath
, str
);
408 TRACE("Loading config %s...\n", alstr_get_cstr(filepath
));
409 f
= al_fopen(alstr_get_cstr(filepath
), "rt");
412 LoadConfigFromFile(f
);
415 alstr_reset(&filepath
);
421 void ReadALConfig(void)
423 al_string confpaths
= AL_STRING_INIT_STATIC();
424 al_string fname
= AL_STRING_INIT_STATIC();
428 str
= "/etc/openal/alsoft.conf";
430 TRACE("Loading config %s...\n", str
);
431 f
= al_fopen(str
, "r");
434 LoadConfigFromFile(f
);
438 if(!(str
=getenv("XDG_CONFIG_DIRS")) || str
[0] == 0)
440 alstr_copy_cstr(&confpaths
, str
);
441 /* Go through the list in reverse, since "the order of base directories
442 * denotes their importance; the first directory listed is the most
443 * important". Ergo, we need to load the settings from the later dirs
444 * first so that the settings in the earlier dirs override them.
446 while(!alstr_empty(confpaths
))
448 char *next
= strrchr(alstr_get_cstr(confpaths
), ':');
451 size_t len
= next
- alstr_get_cstr(confpaths
);
452 alstr_copy_cstr(&fname
, next
+1);
453 VECTOR_RESIZE(confpaths
, len
, len
+1);
454 VECTOR_ELEM(confpaths
, len
) = 0;
460 AL_STRING_INIT(confpaths
);
463 if(alstr_empty(fname
) || VECTOR_FRONT(fname
) != '/')
464 WARN("Ignoring XDG config dir: %s\n", alstr_get_cstr(fname
));
467 if(VECTOR_BACK(fname
) != '/') alstr_append_cstr(&fname
, "/alsoft.conf");
468 else alstr_append_cstr(&fname
, "alsoft.conf");
470 TRACE("Loading config %s...\n", alstr_get_cstr(fname
));
471 f
= al_fopen(alstr_get_cstr(fname
), "r");
474 LoadConfigFromFile(f
);
481 if((str
=getenv("HOME")) != NULL
&& *str
)
483 alstr_copy_cstr(&fname
, str
);
484 if(VECTOR_BACK(fname
) != '/') alstr_append_cstr(&fname
, "/.alsoftrc");
485 else alstr_append_cstr(&fname
, ".alsoftrc");
487 TRACE("Loading config %s...\n", alstr_get_cstr(fname
));
488 f
= al_fopen(alstr_get_cstr(fname
), "r");
491 LoadConfigFromFile(f
);
496 if((str
=getenv("XDG_CONFIG_HOME")) != NULL
&& str
[0] != 0)
498 alstr_copy_cstr(&fname
, str
);
499 if(VECTOR_BACK(fname
) != '/') alstr_append_cstr(&fname
, "/alsoft.conf");
500 else alstr_append_cstr(&fname
, "alsoft.conf");
505 if((str
=getenv("HOME")) != NULL
&& str
[0] != 0)
507 alstr_copy_cstr(&fname
, str
);
508 if(VECTOR_BACK(fname
) != '/') alstr_append_cstr(&fname
, "/.config/alsoft.conf");
509 else alstr_append_cstr(&fname
, ".config/alsoft.conf");
512 if(!alstr_empty(fname
))
514 TRACE("Loading config %s...\n", alstr_get_cstr(fname
));
515 f
= al_fopen(alstr_get_cstr(fname
), "r");
518 LoadConfigFromFile(f
);
524 GetProcBinary(&fname
, NULL
);
525 if(!alstr_empty(fname
))
527 if(VECTOR_BACK(fname
) != '/') alstr_append_cstr(&fname
, "/alsoft.conf");
528 else alstr_append_cstr(&fname
, "alsoft.conf");
530 TRACE("Loading config %s...\n", alstr_get_cstr(fname
));
531 f
= al_fopen(alstr_get_cstr(fname
), "r");
534 LoadConfigFromFile(f
);
539 if((str
=getenv("ALSOFT_CONF")) != NULL
&& *str
)
541 TRACE("Loading config %s...\n", str
);
542 f
= al_fopen(str
, "r");
545 LoadConfigFromFile(f
);
551 alstr_reset(&confpaths
);
555 void FreeALConfig(void)
559 for(i
= 0;i
< cfgBlock
.entryCount
;i
++)
561 free(cfgBlock
.entries
[i
].key
);
562 free(cfgBlock
.entries
[i
].value
);
564 free(cfgBlock
.entries
);
567 const char *GetConfigValue(const char *devName
, const char *blockName
, const char *keyName
, const char *def
)
575 if(blockName
&& strcasecmp(blockName
, "general") != 0)
578 snprintf(key
, sizeof(key
), "%s/%s/%s", blockName
, devName
, keyName
);
580 snprintf(key
, sizeof(key
), "%s/%s", blockName
, keyName
);
585 snprintf(key
, sizeof(key
), "%s/%s", devName
, keyName
);
588 strncpy(key
, keyName
, sizeof(key
)-1);
589 key
[sizeof(key
)-1] = 0;
593 for(i
= 0;i
< cfgBlock
.entryCount
;i
++)
595 if(strcmp(cfgBlock
.entries
[i
].key
, key
) == 0)
597 TRACE("Found %s = \"%s\"\n", key
, cfgBlock
.entries
[i
].value
);
598 if(cfgBlock
.entries
[i
].value
[0])
599 return cfgBlock
.entries
[i
].value
;
606 TRACE("Key %s not found\n", key
);
609 return GetConfigValue(NULL
, blockName
, keyName
, def
);
612 int ConfigValueExists(const char *devName
, const char *blockName
, const char *keyName
)
614 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
618 int ConfigValueStr(const char *devName
, const char *blockName
, const char *keyName
, const char **ret
)
620 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
621 if(!val
[0]) return 0;
627 int ConfigValueInt(const char *devName
, const char *blockName
, const char *keyName
, int *ret
)
629 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
630 if(!val
[0]) return 0;
632 *ret
= strtol(val
, NULL
, 0);
636 int ConfigValueUInt(const char *devName
, const char *blockName
, const char *keyName
, unsigned int *ret
)
638 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
639 if(!val
[0]) return 0;
641 *ret
= strtoul(val
, NULL
, 0);
645 int ConfigValueFloat(const char *devName
, const char *blockName
, const char *keyName
, float *ret
)
647 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
648 if(!val
[0]) return 0;
651 *ret
= strtof(val
, NULL
);
653 *ret
= (float)strtod(val
, NULL
);
658 int ConfigValueBool(const char *devName
, const char *blockName
, const char *keyName
, int *ret
)
660 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
661 if(!val
[0]) return 0;
663 *ret
= (strcasecmp(val
, "true") == 0 || strcasecmp(val
, "yes") == 0 ||
664 strcasecmp(val
, "on") == 0 || atoi(val
) != 0);
668 int GetConfigValueBool(const char *devName
, const char *blockName
, const char *keyName
, int def
)
670 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
672 if(!val
[0]) return !!def
;
673 return (strcasecmp(val
, "true") == 0 || strcasecmp(val
, "yes") == 0 ||
674 strcasecmp(val
, "on") == 0 || atoi(val
) != 0);