mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-24 06:05:56 -05:00
Finish out the config file stuff. Only missing comment preservation now
This commit is contained in:
parent
1d0a3c4149
commit
4402588e2d
201
src/conf.c
201
src/conf.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id$
|
||||
* $idx: conf.c,v 1.4 2006/02/21 03:08:14 rpedde Exp $
|
||||
* Functions for reading and writing the config file
|
||||
*
|
||||
* Copyright (C) 2006 Ron Pedde (ron@pedde.com)
|
||||
@ -42,6 +42,7 @@
|
||||
/** Globals */
|
||||
static int ecode;
|
||||
static LL_HANDLE conf_main=NULL;
|
||||
static char *conf_main_file = NULL;
|
||||
static pthread_mutex_t conf_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
#define conf_LINEBUFFER 128
|
||||
@ -55,7 +56,7 @@ static LL_ITEM *_conf_fetch_item(LL_HANDLE pll, char *section, char *term);
|
||||
static int _conf_exists(LL_HANDLE pll, char *section, char *term);
|
||||
static void _conf_lock(void);
|
||||
static void _conf_unlock(void);
|
||||
|
||||
static int _conf_write(FILE *fp, LL *pll, int sublevel);
|
||||
|
||||
typedef struct _CONF_ELEMENTS {
|
||||
int required;
|
||||
@ -98,7 +99,7 @@ static CONF_ELEMENTS conf_elements[] = {
|
||||
*/
|
||||
void _conf_lock() {
|
||||
int err;
|
||||
|
||||
|
||||
if((err=pthread_mutex_lock(&conf_mutex))) {
|
||||
DPRINTF(E_FATAL,L_CONF,"Cannot lock configuration mutex: %s\n",
|
||||
strerror(err));
|
||||
@ -110,7 +111,7 @@ void _conf_lock() {
|
||||
*/
|
||||
void _conf_unlock() {
|
||||
int err;
|
||||
|
||||
|
||||
if((err = pthread_mutex_unlock(&conf_mutex))) {
|
||||
DPRINTF(E_FATAL,L_CONF,"Cannot unlock configuration mutex %s\n",
|
||||
strerror(err));
|
||||
@ -120,7 +121,7 @@ void _conf_unlock() {
|
||||
/**
|
||||
* fetch item based on section/term basis, rather than just a single
|
||||
* level deep, like ll_fetch_item does
|
||||
*
|
||||
*
|
||||
* @param pll top level linked list to test (config tree)
|
||||
* @param section section to term (key) is in
|
||||
* @param term term/key to look for
|
||||
@ -170,7 +171,7 @@ int _conf_verify(LL_HANDLE pll) {
|
||||
LL_ITEM *pi = NULL;
|
||||
CONF_ELEMENTS *pce;
|
||||
int is_valid=TRUE;
|
||||
|
||||
|
||||
/* first, walk through the elements and make sure
|
||||
* all required elements are there */
|
||||
pce = &conf_elements[0];
|
||||
@ -190,7 +191,7 @@ int _conf_verify(LL_HANDLE pll) {
|
||||
}
|
||||
pce++;
|
||||
}
|
||||
|
||||
|
||||
/* here we would walk through derived sections, if there
|
||||
* were any */
|
||||
|
||||
@ -213,6 +214,12 @@ int conf_read(char *file) {
|
||||
int compat_mode=1;
|
||||
int line=0;
|
||||
|
||||
if(conf_main_file) {
|
||||
conf_close();
|
||||
}
|
||||
|
||||
conf_main_file = strdup(file);
|
||||
|
||||
fin=fopen(file,"r");
|
||||
if(!fin) {
|
||||
return CONF_E_FOPEN;
|
||||
@ -326,8 +333,15 @@ int conf_read(char *file) {
|
||||
* do final config file shutdown
|
||||
*/
|
||||
int conf_close(void) {
|
||||
if(conf_main)
|
||||
if(conf_main) {
|
||||
ll_destroy(conf_main);
|
||||
conf_main = NULL;
|
||||
}
|
||||
|
||||
if(conf_main_file) {
|
||||
free(conf_main_file);
|
||||
conf_main_file = NULL;
|
||||
}
|
||||
|
||||
return CONF_E_SUCCESS;
|
||||
}
|
||||
@ -353,7 +367,7 @@ int conf_get_int(char *section, char *key, int dflt) {
|
||||
retval = atoi(pitem->value.as_string);
|
||||
}
|
||||
_conf_unlock();
|
||||
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -372,7 +386,7 @@ int conf_get_string(char *section, char *key, char *dflt, char *out, int *size)
|
||||
LL_ITEM *pitem;
|
||||
char *result;
|
||||
int len;
|
||||
|
||||
|
||||
_conf_lock();
|
||||
pitem = _conf_fetch_item(conf_main,section,key);
|
||||
if((!pitem) || (pitem->type != LL_TYPE_STRING)) {
|
||||
@ -380,9 +394,9 @@ int conf_get_string(char *section, char *key, char *dflt, char *out, int *size)
|
||||
} else {
|
||||
result = pitem->value.as_string;
|
||||
}
|
||||
|
||||
|
||||
len = strlen(result) + 1;
|
||||
|
||||
|
||||
if(len <= *size) {
|
||||
*size = len;
|
||||
strcpy(out,result);
|
||||
@ -391,7 +405,166 @@ int conf_get_string(char *section, char *key, char *dflt, char *out, int *size)
|
||||
*size = len;
|
||||
return CONF_E_OVERFLOW;
|
||||
}
|
||||
|
||||
|
||||
_conf_unlock();
|
||||
return CONF_E_SUCCESS;
|
||||
return CONF_E_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* set (update) the config tree with a particular value.
|
||||
* this accepts an int, but it actually adds it as a string.
|
||||
* in that sense, it's really just a wrapper for conf_set_string
|
||||
*
|
||||
* @param section section that the key is in
|
||||
* @param key key to update
|
||||
* @param value value to set it to
|
||||
* @returns E_CONF_SUCCESS on success, error code otherwise
|
||||
*/
|
||||
int conf_set_int(char *section, char *key, int value) {
|
||||
char buffer[40]; /* ?? */
|
||||
snprintf(buffer,sizeof(buffer),"%d",value);
|
||||
|
||||
return conf_set_string(section, key, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* set (update) the config tree with a particular string value
|
||||
*
|
||||
* @param section section that the key is in
|
||||
* @param key key to update
|
||||
* @param value value to set it to
|
||||
* @returns E_CONF_SUCCESS on success, error code otherwise
|
||||
*/
|
||||
int conf_set_string(char *section, char *key, char *value) {
|
||||
LL_ITEM *pitem;
|
||||
LL_ITEM *psection;
|
||||
LL *section_ll;
|
||||
int err;
|
||||
|
||||
_conf_lock();
|
||||
pitem = _conf_fetch_item(conf_main,section,key);
|
||||
if(!pitem) {
|
||||
/* fetch the section and add it to that list */
|
||||
if(!(psection = ll_fetch_item(conf_main,section))) {
|
||||
/* that subkey doesn't exist yet... */
|
||||
if((err = ll_create(§ion_ll)) != LL_E_SUCCESS) {
|
||||
DPRINTF(E_LOG,L_CONF,"Could not create linked list: %d\n",err);
|
||||
_conf_unlock();
|
||||
return CONF_E_UNKNOWN;
|
||||
}
|
||||
if((err=ll_add_ll(conf_main,section,section_ll)) != LL_E_SUCCESS) {
|
||||
DPRINTF(E_LOG,L_CONF,"Error inserting new subkey: %d\n",err);
|
||||
_conf_unlock();
|
||||
return CONF_E_UNKNOWN;
|
||||
}
|
||||
} else {
|
||||
section_ll = psection->value.as_ll;
|
||||
}
|
||||
/* have the section, now add it */
|
||||
if((err = ll_add_string(section_ll,key,value)) != LL_E_SUCCESS) {
|
||||
DPRINTF(E_LOG,L_CONF,"Error in conf_set_string: "
|
||||
"(%s/%s)\n",section,key);
|
||||
_conf_unlock();
|
||||
return CONF_E_UNKNOWN;
|
||||
}
|
||||
} else {
|
||||
/* we have the item, let's update it */
|
||||
ll_update_string(pitem,value);
|
||||
}
|
||||
|
||||
_conf_unlock();
|
||||
return CONF_E_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* determine if the configuration file is writable
|
||||
*
|
||||
* @returns TRUE if writable, FALSE otherwise
|
||||
*/
|
||||
int conf_iswritable(void) {
|
||||
FILE *fp;
|
||||
int retval = FALSE;
|
||||
|
||||
/* don't want configfile reopened under us */
|
||||
_conf_lock();
|
||||
|
||||
if(!conf_main_file)
|
||||
return FALSE;
|
||||
|
||||
if((fp = fopen(conf_main_file,"r+")) != NULL) {
|
||||
fclose(fp);
|
||||
retval = TRUE;
|
||||
}
|
||||
|
||||
_conf_unlock();
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* write the current config tree back to the config file
|
||||
*
|
||||
*/
|
||||
int conf_write(void) {
|
||||
int retval = FALSE;
|
||||
FILE *fp;
|
||||
|
||||
if(!conf_main_file) {
|
||||
return CONF_E_NOCONF;
|
||||
}
|
||||
|
||||
_conf_lock();
|
||||
if((fp = fopen(conf_main_file,"w+")) != NULL) {
|
||||
retval = _conf_write(fp,conf_main,0);
|
||||
fclose(fp);
|
||||
}
|
||||
_conf_unlock();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* do the actual work of writing the config file
|
||||
*
|
||||
* @param fp file we are writing the config file to
|
||||
* @param pll list we are dumping k/v pairs for
|
||||
* @param sublevel whether this is the root, or a subkey
|
||||
* @returns TRUE on success, FALSE otherwise
|
||||
*/
|
||||
int _conf_write(FILE *fp, LL *pll, int sublevel) {
|
||||
LL_ITEM *pli;
|
||||
int retval;
|
||||
|
||||
if(!pll)
|
||||
return TRUE;
|
||||
|
||||
/* write all the solo keys, first! */
|
||||
pli = pll->itemlist.next;
|
||||
while(pli) {
|
||||
switch(pli->type) {
|
||||
case LL_TYPE_LL:
|
||||
if(sublevel) {
|
||||
/* something wrong! */
|
||||
DPRINTF(E_LOG,L_CONF,"LL in sublevel: %s\n",pli->key);
|
||||
} else {
|
||||
fprintf(fp,"[%s]\n",pli->key);
|
||||
if(!_conf_write(fp, pli->value.as_ll, 1))
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case LL_TYPE_INT:
|
||||
fprintf(fp,"%s=%d\n",pli->key,pli->value.as_int);
|
||||
break;
|
||||
|
||||
case LL_TYPE_STRING:
|
||||
fprintf(fp,"%s=%s\n",pli->key,pli->value.as_string);
|
||||
break;
|
||||
}
|
||||
|
||||
pli = pli->next;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
#define CONF_E_BADHEADER 3
|
||||
#define CONF_E_PARSE 4
|
||||
#define CONF_E_OVERFLOW 5 /** <Buffer passed too small */
|
||||
|
||||
#define CONF_E_NOCONF 6 /** <No open config file */
|
||||
|
||||
|
||||
extern int conf_read(char *file);
|
||||
@ -36,5 +36,10 @@ extern int conf_close(void);
|
||||
extern int conf_get_int(char *section, char *key, int dflt);
|
||||
extern int conf_get_string(char *section, char *key, char *dflt,
|
||||
char *out, int *size);
|
||||
extern int conf_set_int(char *section, char *key, int value);
|
||||
extern int conf_set_string(char *section, char *key, char *value);
|
||||
|
||||
extern int conf_iswritable(void);
|
||||
extern int conf_write(void);
|
||||
|
||||
#endif /* _CONFIG_H_ */
|
||||
|
@ -16,8 +16,15 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
if((err=conf_read(argv[1])) != CONF_E_SUCCESS) {
|
||||
printf("Error reading config: %d\n",err);
|
||||
conf_close();
|
||||
} else {
|
||||
printf("Read config!\n");
|
||||
conf_set_string("general","stupid","lalala");
|
||||
conf_set_int("potato","yummy",0);
|
||||
if(conf_iswritable()) {
|
||||
printf("writing config\n");
|
||||
conf_write();
|
||||
}
|
||||
}
|
||||
conf_close();
|
||||
}
|
||||
|
74
src/ll.c
74
src/ll.c
@ -35,6 +35,7 @@
|
||||
/** Internal functions */
|
||||
|
||||
int _ll_add_item(LL *pl, char *key, void *vpval, int ival, int type);
|
||||
int _ll_update_item(LL_ITEM *pli, void *vpval, int ival, int type);
|
||||
void _ll_dump(LL *pl, int depth);
|
||||
|
||||
/**
|
||||
@ -167,6 +168,73 @@ int _ll_add_item(LL *pl, char *key, void* vpval, int ival, int type) {
|
||||
return LL_E_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* thin wrapper for _ll_update_item
|
||||
*/
|
||||
int ll_update_string(LL_ITEM *pli, char *cval) {
|
||||
return _ll_update_item(pli,cval,0,LL_TYPE_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* thin wrapper for _ll_update_item
|
||||
*/
|
||||
int ll_update_int(LL_ITEM *pli, int ival) {
|
||||
return _ll_update_item(pli,NULL,ival,LL_TYPE_INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* thin wrapper for _ll_update_item.
|
||||
*
|
||||
* NOTE: There is a reasonable case to be made about
|
||||
* what should happen to flags on an update. We'll just
|
||||
* leave that to the caller, though.
|
||||
*/
|
||||
int ll_update_ll(LL_ITEM *pli, LL *pnew) {
|
||||
int result;
|
||||
|
||||
result = _ll_update_item(pli,(void*)pnew,0,LL_TYPE_LL);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* update an item, given an item pointer
|
||||
*/
|
||||
int _ll_update_item(LL_ITEM *pli, void* vpval, int ival, int type) {
|
||||
|
||||
/* dispose of what used to be there*/
|
||||
switch(pli->type) {
|
||||
case LL_TYPE_LL:
|
||||
ll_destroy(pli->value.as_ll);
|
||||
break;
|
||||
case LL_TYPE_STRING:
|
||||
free(pli->value.as_string);
|
||||
break;
|
||||
case LL_TYPE_INT: /* fallthrough */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pli->type = type;
|
||||
|
||||
switch(pli->type) {
|
||||
case LL_TYPE_INT:
|
||||
pli->value.as_int = ival;
|
||||
break;
|
||||
case LL_TYPE_LL:
|
||||
pli->value.as_ll = (LL *)vpval;
|
||||
break;
|
||||
case LL_TYPE_STRING:
|
||||
pli->value.as_string = strdup((char*)vpval);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return LL_E_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* internal function to get the ll item associated with
|
||||
* a specific key, using the case sensitivity specified
|
||||
@ -258,16 +326,16 @@ void _ll_dump(LL *pl, int depth) {
|
||||
/**
|
||||
* Given an item (or NULL for first item), fetch
|
||||
* the next item
|
||||
*
|
||||
*
|
||||
* @param pl ll to fetch from
|
||||
* @param prev last item we fetched
|
||||
*/
|
||||
LL_ITEM *ll_get_next(LL *pl, LL_ITEM *prev) {
|
||||
if(!pl)
|
||||
return NULL;
|
||||
|
||||
|
||||
if(!prev)
|
||||
return pl->itemlist.next;
|
||||
|
||||
|
||||
return prev->next;
|
||||
}
|
||||
|
4
src/ll.h
4
src/ll.h
@ -63,6 +63,10 @@ extern int ll_add_string(LL *pl, char *key, char *cval);
|
||||
extern int ll_add_int(LL *pl, char *key, int ival);
|
||||
extern int ll_add_ll(LL *pl, char *key, LL *pnew);
|
||||
|
||||
extern int ll_udpate_string(LL_ITEM *pli, char *cval);
|
||||
extern int ll_update_int(LL_ITEM *pli, int ival);
|
||||
extern int ll_update_ll(LL_ITEM *pli, LL *pnew);
|
||||
|
||||
extern int ll_set_flags(LL *pl, unsigned int flags);
|
||||
extern int ll_get_flags(LL *pl, unsigned int *flags);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user