gmrun/src/config_prefs.c

357 lines
7.9 KiB
C

/*
* This is free and unencumbered software released into the public domain.
*
* For more information, please refer to <https://unlicense.org>
*/
/*
* Read config file formatted like this:
*
* key1 = value1
* key2=value2
*
* It's possible to open several files with the same config
* The keys will be updated with thew new values.
* 'key' is not case sensitive
*/
#include "config_prefs.h"
#define APP_CONFIG_FILE "gmrunrc"
// ============================================================
// PRIVATE
// ============================================================
static GList * PrefsGList = NULL;
static GList * ExtensionGList = NULL;
struct _pref_item
{
char * key;
char * value;
};
typedef struct _pref_item pref_item;
static void pref_item_free (pref_item * item)
{
if (item) {
if (item->key) g_free (item->key);
if (item->value) g_free (item->value);
g_free (item);
}
}
static void pref_item_free_cb (gpointer data)
{
pref_item_free ((pref_item *) data);
}
static GList * config_find_key (GList * list, const char * key)
{
GList *i;
pref_item * item;
if (!key || !*key) { /* ignore empty keys (strings) */
return (NULL);
}
for (i = list; i; i = i->next)
{
item = (pref_item *) (i->data);
if (strcasecmp (key, item->key) == 0) {
return (i);
}
}
return (NULL); /* key not found */
}
static void config_replace_key (GList ** list, pref_item * item)
{
GList * found = config_find_key (*list, item->key);
if (found) {
/* only update found item */
pref_item * found_item = (pref_item *) (found->data);
if (strcmp (found_item->value, item->value) == 0) {
pref_item_free (item);
return; /* values are equal, nothing to update */
}
g_free (found_item->value);
found_item->value = g_strdup (item->value);
pref_item_free (item);
} else {
/* append item */
*list = g_list_append (*list, (gpointer) item);
}
}
/** get value, it's always a string **/
static char * config_get_item_value (GList * list, const char * key)
{
GList * ret;
pref_item * item;
ret = config_find_key (list, key);
if (ret) {
item = (pref_item *) (ret->data);
return (item->value);
}
return (NULL); /* key not found */
}
static void config_load_from_file (const char * filename, GList ** out_list)
{
FILE *fp;
char buf[1024];
char * stripped;
char ** keyvalue;
pref_item * item;
fp = fopen (filename, "r");
if (!fp) {
return;
}
/* Read file line by line */
while (fgets (buf, sizeof (buf), fp))
{
stripped = buf;
while (*stripped && *stripped <= 0x20) { // 32 = space
stripped++;
}
if (strlen (stripped) < 3 || *stripped == '#') {
continue;
}
if (!strchr (stripped, '=')) {
continue;
}
item = (pref_item *) g_malloc0 (sizeof (pref_item));
keyvalue = g_strsplit (stripped, "=", 2);
item->key = g_strstrip (keyvalue[0]);
item->value = g_strstrip (keyvalue[1]);
if (!*item->key || !*item->value) {
g_strfreev (keyvalue);
g_free (item);
continue;
}
/// fprintf (stderr, "### %s = %s\n", key, value);
/* Insert or replace item */
config_replace_key (out_list, item);
}
fclose (fp);
return;
}
static void create_extension_handler_list (void)
{
GList * i;
pref_item * item, * item_out;
char ** str_vector;
for (i = PrefsGList; i; i = i->next)
{
item = (pref_item *) (i->data);
if (strncasecmp (item->key, "EXT:", 4) == 0)
{
int w;
str_vector = g_strsplit (item->key + 4, ",", 0);
for (w = 0; str_vector[w]; w++)
{
item_out = (pref_item *) g_malloc0 (sizeof (pref_item));
item_out->key = g_strdup (str_vector[w]);
item_out->value = g_strdup (item->value);
config_replace_key (&ExtensionGList, item_out);
}
g_strfreev (str_vector);
}
}
}
static char * replace_variable (char * txt) /* config_get_string_expanded() */
{
// pre${variable}post : ${Terminal} -e ...
char * pre = NULL, * post = NULL;
char * variable = NULL;
char * variable_value = NULL;
char * new_text = NULL;
char * p, * p2;
if (strlen (txt) < 5) { // at least ${xx}
return (NULL);
}
p = strstr (txt, "${");
if (!p) {
return (NULL); // syntax error
}
if (!strchr (p + 3, '}')) {
return (NULL); // syntax error
}
if (txt[0] != '$' && txt[1] != '$') {
pre = g_strdup (txt);
p2 = strchr (pre, '$');
if (p2) p2 = 0;
}
variable = g_strdup (p + 2); // variable start
p2 = strchr (variable, '}'); // variable end
*p2 = 0; // `Terminal`
post = p2 + 1; // ` -e ...`
variable_value = config_get_item_value (PrefsGList, variable); // xterm
if (variable_value) {
if (pre) {
// pre xterm -e ...
new_text = g_strconcat (pre, variable_value, post, NULL);
} else {
// xterm -e ...
new_text = g_strconcat (variable_value, post, NULL);
}
}
if (pre) g_free (pre);
if (variable) g_free (variable);
return (new_text);
}
// ============================================================
// PUBLIC
// ============================================================
void config_init ()
{
if (PrefsGList) {
return;
}
char config_file[512];
char * HOME;
snprintf (config_file, sizeof (config_file), "/etc/%s", APP_CONFIG_FILE);
config_load_from_file (config_file, &PrefsGList);
HOME = getenv ("HOME");
if (HOME) {
snprintf (config_file, sizeof (config_file), "%s/.%s", HOME, APP_CONFIG_FILE);
config_load_from_file (config_file, &PrefsGList);
}
create_extension_handler_list ();
}
void config_destroy ()
{
if (PrefsGList) {
g_list_free_full (PrefsGList, pref_item_free_cb);
g_list_free_full (ExtensionGList, pref_item_free_cb);
PrefsGList = NULL;
ExtensionGList = NULL;
}
}
void config_reload ()
{
config_destroy ();
config_init ();
}
void config_print ()
{
GList * i;
pref_item * item;
for (i = PrefsGList; i; i = i->next)
{
item = (pref_item *) (i->data);
printf ("%s = %s\n", item->key, item->value);
}
for (i = ExtensionGList; i; i = i->next)
{
item = (pref_item *) (i->data);
printf ("%s = %s\n", item->key, item->value);
}
}
gboolean config_get_int (const char * key, int * out_int)
{
char * value;
value = config_get_item_value (PrefsGList, key);
if (value) {
*out_int = (int) strtoll (value, NULL, 0);
return TRUE;
} else {
*out_int = -1;
return FALSE;
}
}
// returns a string that must be freed with g_free
gboolean config_get_string_expanded (const char * key, char ** out_str)
{
char * value1, * value2, * value = NULL;
value1 = config_get_item_value (PrefsGList, key);
if (value1 && strstr (value1, "${")) {
value2 = replace_variable (value1);
value = value2;
// expand variable up to 2 times
if (value2 && strstr (value2, "${")) {
value = replace_variable (value2);
g_free (value2);
}
} else {
value = g_strdup (value1);
}
if (value) {
*out_str = value;
return TRUE;
} else {
*out_str = NULL;
return FALSE;
}
}
gboolean config_get_string (const char * key, char ** out_str)
{
char * value;
value = config_get_item_value (PrefsGList, key);
if (value) {
*out_str = value;
return TRUE;
} else {
*out_str = NULL;
return FALSE;
}
}
char * config_get_handler_for_extension (const char * extension)
{
char * handler;
if (extension && *extension == '.') {
extension++; // .html -> html
}
handler = config_get_item_value (ExtensionGList, extension);
return (handler);
}