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
45 typedef struct ConfigEntry
{
50 typedef struct ConfigBlock
{
52 unsigned int entryCount
;
54 static ConfigBlock cfgBlock
;
57 static char *lstrip(char *line
)
59 while(isspace(line
[0]))
64 static char *rstrip(char *line
)
66 size_t len
= strlen(line
);
67 while(len
> 0 && isspace(line
[len
-1]))
73 static int readline(FILE *f
, char **output
, size_t *maxlen
)
78 while((c
=fgetc(f
)) != EOF
&& (c
== '\r' || c
== '\n'))
89 newmax
= (*maxlen
? (*maxlen
)<<1 : 32);
91 temp
= realloc(*output
, newmax
);
94 ERR("Failed to realloc "SZFMT
" bytes from "SZFMT
"!\n", newmax
, *maxlen
);
101 (*output
)[len
++] = c
;
102 (*output
)[len
] = '\0';
103 } while((c
=fgetc(f
)) != EOF
&& c
!= '\r' && c
!= '\n');
109 static char *expdup(const char *str
)
123 const char *next
= strchr(str
, '$');
125 addstrlen
= next
? (size_t)(next
-str
) : strlen(str
);
134 const char *next
= strchr(str
+1, '$');
136 addstrlen
= next
? (size_t)(next
-str
) : strlen(str
);
146 hasbraces
= (*str
== '{');
149 while((isalnum(*str
) || *str
== '_') && k
< sizeof(envname
)-1)
150 envname
[k
++] = *(str
++);
153 if(hasbraces
&& *str
!= '}')
157 if((addstr
=getenv(envname
)) == NULL
)
159 addstrlen
= strlen(addstr
);
165 if(addstrlen
>= maxlen
-len
)
170 newmax
= len
+addstrlen
+1;
172 temp
= realloc(output
, newmax
);
175 ERR("Failed to realloc "SZFMT
" bytes from "SZFMT
"!\n", newmax
, maxlen
);
183 for(i
= 0;i
< addstrlen
;i
++)
184 output
[len
++] = addstr
[i
];
188 return output
? output
: calloc(1, 1);
192 static void LoadConfigFromFile(FILE *f
)
194 char curSection
[128] = "";
199 while(readline(f
, &buffer
, &maxlen
))
201 char *line
, *comment
;
203 char value
[256] = "";
205 line
= rstrip(lstrip(buffer
));
206 if(!line
[0]) continue;
210 char *section
= line
+1;
213 endsection
= strchr(section
, ']');
214 if(!endsection
|| section
== endsection
)
216 ERR("config parse error: bad line \"%s\"\n", line
);
219 if(endsection
[1] != 0)
221 char *end
= endsection
+1;
224 if(*end
!= 0 && *end
!= '#')
226 ERR("config parse error: bad line \"%s\"\n", line
);
232 if(strcasecmp(section
, "general") == 0)
236 strncpy(curSection
, section
, sizeof(curSection
)-1);
237 curSection
[sizeof(curSection
)-1] = 0;
243 comment
= strchr(line
, '#');
244 if(comment
) *(comment
++) = 0;
245 if(!line
[0]) continue;
247 if(sscanf(line
, "%255[^=] = \"%255[^\"]\"", key
, value
) == 2 ||
248 sscanf(line
, "%255[^=] = '%255[^\']'", key
, value
) == 2 ||
249 sscanf(line
, "%255[^=] = %255[^\n]", key
, value
) == 2)
251 /* sscanf doesn't handle '' or "" as empty values, so clip it
253 if(strcmp(value
, "\"\"") == 0 || strcmp(value
, "''") == 0)
256 else if(sscanf(line
, "%255[^=] %255[=]", key
, value
) == 2)
258 /* Special case for 'key =' */
263 ERR("config parse error: malformed option line: \"%s\"\n\n", line
);
268 if(curSection
[0] != 0)
270 size_t len
= strlen(curSection
);
271 memmove(&key
[len
+1], key
, sizeof(key
)-1-len
);
273 memcpy(key
, curSection
, len
);
276 /* Check if we already have this option set */
277 ent
= cfgBlock
.entries
;
278 while((unsigned int)(ent
-cfgBlock
.entries
) < cfgBlock
.entryCount
)
280 if(strcasecmp(ent
->key
, key
) == 0)
285 if((unsigned int)(ent
-cfgBlock
.entries
) >= cfgBlock
.entryCount
)
287 /* Allocate a new option entry */
288 ent
= realloc(cfgBlock
.entries
, (cfgBlock
.entryCount
+1)*sizeof(ConfigEntry
));
291 ERR("config parse error: error reallocating config entries\n");
294 cfgBlock
.entries
= ent
;
295 ent
= cfgBlock
.entries
+ cfgBlock
.entryCount
;
296 cfgBlock
.entryCount
++;
298 ent
->key
= strdup(key
);
303 ent
->value
= expdup(value
);
305 TRACE("found '%s' = '%s'\n", ent
->key
, ent
->value
);
312 void ReadALConfig(void)
314 WCHAR buffer
[PATH_MAX
];
319 if(SHGetSpecialFolderPathW(NULL
, buffer
, CSIDL_APPDATA
, FALSE
) != FALSE
)
321 al_string filepath
= AL_STRING_INIT_STATIC();
322 al_string_copy_wcstr(&filepath
, buffer
);
323 al_string_append_cstr(&filepath
, "\\alsoft.ini");
325 TRACE("Loading config %s...\n", al_string_get_cstr(filepath
));
326 f
= al_fopen(al_string_get_cstr(filepath
), "rt");
329 LoadConfigFromFile(f
);
332 al_string_deinit(&filepath
);
335 ppath
= GetProcPath();
336 if(!al_string_empty(ppath
))
338 al_string_append_cstr(&ppath
, "\\alsoft.ini");
339 TRACE("Loading config %s...\n", al_string_get_cstr(ppath
));
340 f
= al_fopen(al_string_get_cstr(ppath
), "r");
343 LoadConfigFromFile(f
);
348 if((str
=_wgetenv(L
"ALSOFT_CONF")) != NULL
&& *str
)
350 al_string filepath
= AL_STRING_INIT_STATIC();
351 al_string_copy_wcstr(&filepath
, str
);
353 TRACE("Loading config %s...\n", al_string_get_cstr(filepath
));
354 f
= al_fopen(al_string_get_cstr(filepath
), "rt");
357 LoadConfigFromFile(f
);
360 al_string_deinit(&filepath
);
363 al_string_deinit(&ppath
);
366 void ReadALConfig(void)
368 char buffer
[PATH_MAX
];
373 str
= "/etc/openal/alsoft.conf";
375 TRACE("Loading config %s...\n", str
);
376 f
= al_fopen(str
, "r");
379 LoadConfigFromFile(f
);
383 if(!(str
=getenv("XDG_CONFIG_DIRS")) || str
[0] == 0)
385 strncpy(buffer
, str
, sizeof(buffer
)-1);
386 buffer
[sizeof(buffer
)-1] = 0;
387 /* Go through the list in reverse, since "the order of base directories
388 * denotes their importance; the first directory listed is the most
389 * important". Ergo, we need to load the settings from the later dirs
390 * first so that the settings in the earlier dirs override them.
394 char *next
= strrchr(buffer
, ':');
395 if(next
) *(next
++) = 0;
399 WARN("Ignoring XDG config dir: %s\n", next
);
402 size_t len
= strlen(next
);
403 strncpy(next
+len
, "/alsoft.conf", buffer
+sizeof(buffer
)-next
-len
);
404 buffer
[sizeof(buffer
)-1] = 0;
406 TRACE("Loading config %s...\n", next
);
407 f
= al_fopen(next
, "r");
410 LoadConfigFromFile(f
);
418 if((str
=getenv("HOME")) != NULL
&& *str
)
420 snprintf(buffer
, sizeof(buffer
), "%s/.alsoftrc", str
);
422 TRACE("Loading config %s...\n", buffer
);
423 f
= al_fopen(buffer
, "r");
426 LoadConfigFromFile(f
);
431 if((str
=getenv("XDG_CONFIG_HOME")) != NULL
&& str
[0] != 0)
432 snprintf(buffer
, sizeof(buffer
), "%s/%s", str
, "alsoft.conf");
436 if((str
=getenv("HOME")) != NULL
&& str
[0] != 0)
437 snprintf(buffer
, sizeof(buffer
), "%s/.config/%s", str
, "alsoft.conf");
441 TRACE("Loading config %s...\n", buffer
);
442 f
= al_fopen(buffer
, "r");
445 LoadConfigFromFile(f
);
450 ppath
= GetProcPath();
451 if(!al_string_empty(ppath
))
453 al_string_append_cstr(&ppath
, "/alsoft.conf");
454 TRACE("Loading config %s...\n", al_string_get_cstr(ppath
));
455 f
= al_fopen(al_string_get_cstr(ppath
), "r");
458 LoadConfigFromFile(f
);
463 if((str
=getenv("ALSOFT_CONF")) != NULL
&& *str
)
465 TRACE("Loading config %s...\n", str
);
466 f
= al_fopen(str
, "r");
469 LoadConfigFromFile(f
);
474 al_string_deinit(&ppath
);
478 void FreeALConfig(void)
482 for(i
= 0;i
< cfgBlock
.entryCount
;i
++)
484 free(cfgBlock
.entries
[i
].key
);
485 free(cfgBlock
.entries
[i
].value
);
487 free(cfgBlock
.entries
);
490 const char *GetConfigValue(const char *devName
, const char *blockName
, const char *keyName
, const char *def
)
498 if(blockName
&& strcasecmp(blockName
, "general") != 0)
501 snprintf(key
, sizeof(key
), "%s/%s/%s", blockName
, devName
, keyName
);
503 snprintf(key
, sizeof(key
), "%s/%s", blockName
, keyName
);
508 snprintf(key
, sizeof(key
), "%s/%s", devName
, keyName
);
511 strncpy(key
, keyName
, sizeof(key
)-1);
512 key
[sizeof(key
)-1] = 0;
516 for(i
= 0;i
< cfgBlock
.entryCount
;i
++)
518 if(strcmp(cfgBlock
.entries
[i
].key
, key
) == 0)
520 TRACE("Found %s = \"%s\"\n", key
, cfgBlock
.entries
[i
].value
);
521 if(cfgBlock
.entries
[i
].value
[0])
522 return cfgBlock
.entries
[i
].value
;
529 TRACE("Key %s not found\n", key
);
532 return GetConfigValue(NULL
, blockName
, keyName
, def
);
535 int ConfigValueExists(const char *devName
, const char *blockName
, const char *keyName
)
537 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
541 int ConfigValueStr(const char *devName
, const char *blockName
, const char *keyName
, const char **ret
)
543 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
544 if(!val
[0]) return 0;
550 int ConfigValueInt(const char *devName
, const char *blockName
, const char *keyName
, int *ret
)
552 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
553 if(!val
[0]) return 0;
555 *ret
= strtol(val
, NULL
, 0);
559 int ConfigValueUInt(const char *devName
, const char *blockName
, const char *keyName
, unsigned int *ret
)
561 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
562 if(!val
[0]) return 0;
564 *ret
= strtoul(val
, NULL
, 0);
568 int ConfigValueFloat(const char *devName
, const char *blockName
, const char *keyName
, float *ret
)
570 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
571 if(!val
[0]) return 0;
574 *ret
= strtof(val
, NULL
);
576 *ret
= (float)strtod(val
, NULL
);
581 int ConfigValueBool(const char *devName
, const char *blockName
, const char *keyName
, int *ret
)
583 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
584 if(!val
[0]) return 0;
586 *ret
= (strcasecmp(val
, "true") == 0 || strcasecmp(val
, "yes") == 0 ||
587 strcasecmp(val
, "on") == 0 || atoi(val
) != 0);
591 int GetConfigValueBool(const char *devName
, const char *blockName
, const char *keyName
, int def
)
593 const char *val
= GetConfigValue(devName
, blockName
, keyName
, "");
595 if(!val
[0]) return !!def
;
596 return (strcasecmp(val
, "true") == 0 || strcasecmp(val
, "yes") == 0 ||
597 strcasecmp(val
, "on") == 0 || atoi(val
) != 0);