Skip to content

Commit

Permalink
Implement econf_*freep functions for automatic cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
thkukuk committed Dec 2, 2024
1 parent 2bb75e2 commit d05626a
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 3 deletions.
14 changes: 13 additions & 1 deletion include/libeconf.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ enum econf_err {
ECONF_SUCCESS = 0,
/** Generic Error */
ECONF_ERROR = 1,
/** Out of memory */
/** Out of memory */
ECONF_NOMEM = 2,
/** Config file not found */
ECONF_NOFILE = 3,
Expand Down Expand Up @@ -1088,6 +1088,12 @@ extern void econf_errLocation (char **filename, uint64_t *line_nr);
*/
extern char **econf_freeArray(char **array);

/* helper so that __attribute__((cleanup(econf_freeArrayp)) may be used */
static __inline__ void econf_freeArrayp(char ***array) {
if (*array)
*array = econf_freeArray(*array);
}

/** @brief Free memory allocated by e.g. econf_readFile(), econf_readDirs(),...
*
* @param key_file allocated data
Expand All @@ -1096,6 +1102,12 @@ extern char **econf_freeArray(char **array);
*/
extern econf_file *econf_freeFile(econf_file *key_file);

/* helper so that __attribute__((cleanup(econf_freeFilep)) may be used */
static __inline__ void econf_freeFilep(econf_file **key_file) {
if (*key_file)
*key_file = econf_freeFile(*key_file);
}

/** @brief All parsed files require this user permission.
* DEPRECATED: Use the callback in econf_readFileWithCallback or
* econf_readConfigWithCallback instead.
Expand Down
5 changes: 3 additions & 2 deletions tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -192,16 +192,17 @@ test('tst-groups6', tst_groups6_exe)
tst_parseconfig1_exe = executable('tst-parseconfig1', 'tst-parseconfig1.c', c_args: test_args, dependencies : libeconf_dep)
test('tst-parseconfig1', tst_parseconfig1_exe)


tst_quote1_exe = executable('tst-quote1', 'tst-quote1.c', c_args: test_args, dependencies : libeconf_dep)
test('tst-quote1', tst_quote1_exe)


tst_shells1_exe = executable('tst-shells1', 'tst-shells1.c', c_args: test_args, dependencies : libeconf_dep)
test('tst-shells1', tst_shells1_exe)
tst_shells2_exe = executable('tst-shells2', 'tst-shells2.c', c_args: test_args, dependencies : libeconf_dep)
test('tst-shells2', tst_shells2_exe)

tst_freep1_exe = executable('tst-freep1', 'tst-freep1.c', c_args: test_args, dependencies : libeconf_dep)
test('tst-freep1', tst_freep1_exe)


test('tst_econftool1', find_program('tst-econftool1.sh'))
test('tst_econftool_show1', find_program('tst-econftool_show1.sh'))
Expand Down
99 changes: 99 additions & 0 deletions tests/tst-freep1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <string.h>

#include "libeconf.h"

/* Test case:
Use __attribute__((cleanup)) and don't free return values
yourself.
*/

static int
freeFilep(void)
{
__attribute__((cleanup(econf_freeFilep))) econf_file *key_file = NULL;
econf_err error;

error = econf_readConfig (&key_file,
"foo",
"/usr/etc",
"bar",
"conf", "=", "#");
if (error != ECONF_NOFILE)
{
fprintf (stderr, "ERROR: econf_readConfig: %s\n",
econf_errString(error));
return 1;
}

return 0;
}

static int
setValue(econf_file *key_file, const char *group, const char *key, const char *value)
{
econf_err error;

if ((error = econf_setStringValue(key_file, group, key, value)))
{
fprintf (stderr, "ERROR: couldn't set string value: %s\n",
econf_errString(error));
return 1;
}
return 0;
}

static int
freeArrayp(void)
{
__attribute__((cleanup(econf_freeFilep))) econf_file *key_file = NULL;
econf_err error;

if ((error = econf_newKeyFile(&key_file, '=', '#')))
{
fprintf (stderr, "ERROR: couldn't create new file: %s\n",
econf_errString(error));
return 1;
}

if (setValue(key_file, "Group1", "Key1", "value1")) return 1;
if (setValue(key_file, "Group1", "Key2", "value2")) return 1;
if (setValue(key_file, "Group1", "Key3", "value3")) return 1;
if (setValue(key_file, "Group2", "Key1", "value1")) return 1;
if (setValue(key_file, "Group3", "Key1", "value1")) return 1;
if (setValue(key_file, "Group4", "Key4", "value4")) return 1;

__attribute__((cleanup(econf_freeArrayp))) char **groups;
size_t group_number;
if ((error = econf_getGroups(key_file, &group_number, &groups)))
{
fprintf (stderr, "Error getting all groups: %s\n", econf_errString(error));
return 1;
}

__attribute__((cleanup(econf_freeArrayp))) char **keys;
size_t key_number;
error = econf_getKeys (key_file, "Group1", &key_number, &keys);
if (error)
{
fprintf (stderr, "Error getting all keys for [%s]: %s\n",
"Group1", econf_errString(error));
return 1;
}

return 0;
}


int
main(void)
{
if (freeFilep() != 0) return 1;
if (freeArrayp() != 0) return 1;

return 0;
}

0 comments on commit d05626a

Please sign in to comment.