ref: 6c63e71880f8bcdf597548e8bbaba165c4fe7d38
parent: 61a400cc2f9b50b4a39537eb9d0981df77347929
author: sirjofri <sirjofri@sirjofri.de>
date: Sun Feb 26 17:34:16 EST 2023
adds freeini command
--- a/README
+++ b/README
@@ -11,6 +11,7 @@
int forcelower)
int iniconfig(char *file, int forcelower)
+ int freeini(int config)
char* getinivalue(int config, char *section, char *key)
char** getiniarray(int config, char *section, char *key, int
@@ -32,7 +33,9 @@
Iniconfig parses the given file into a convenient config
database. It returns the id of the parsed config file
that's needed for future lookups. It is possible to parse
- multiple INI files within one program this way.
+ multiple INI files within one program this way. Freeini
+ frees the whole loaded INI configuration specified by the
+ config parameter.
The functions getinivalue and getiniarray can be used to
fetch the parsed config values. Both functions expect the
@@ -57,4 +60,6 @@
BUGS
Sure.
+
+ Freeini potentially leaks memory.
--- a/ini.c
+++ b/ini.c
@@ -22,6 +22,7 @@
void** Aarr(Array *a);
int Anum(Array *a);
void Ashrink(Array *a);
+int Aset(Array *a, int index, void *item);
typedef struct Property Property;
struct Property {
@@ -65,8 +66,10 @@
return nil;
IniConfig *c = (IniConfig*)Aget(&configs, config);
- if (!c)
- sysfatal("invalid config: %r");
+ if (!c) {
+ werrstr("invalid config: %r");
+ return nil;
+ }
for (int i = 0; i < Anum(&c->properties); i++) {
Property *p = (Property*)Aget(&c->properties, i);
@@ -292,6 +295,8 @@
Ainit(&configs);
IniConfig *ini = malloc(sizeof(IniConfig));
+ if (!ini)
+ sysfatal("error: %r");
Ainit(&ini->sections);
Ainit(&ini->properties);
Ainit(&ini->arrayproperties);
@@ -306,6 +311,44 @@
}
int
+freeini(int config)
+{
+ IniConfig *c = (IniConfig*)Aget(&configs, config);
+ if (!c)
+ sysfatal("invalid config: %r");
+
+ for (int i = 0; i < Anum(&c->properties); i++) {
+ Property *p = (Property*)Aget(&c->properties, i);
+ free(p->key);
+ p->key = nil;
+ free(p->value);
+ p->value = nil;
+ free(p);
+ Aset(&c->properties, i, nil);
+ }
+ for (int i = 0; i < Anum(&c->arrayproperties); i++) {
+ ArrayProperty *p = (ArrayProperty*)Aget(&c->arrayproperties, i);
+ for (int j = 0; j < Anum(&p->values); j++) {
+ free(Aget(&p->values, j));
+ Aset(&p->values, j, nil);
+ }
+ free(p->key);
+ p->key = nil;
+ free(p);
+ Aset(&c->arrayproperties, i, nil);
+ }
+ for (int i = 0; i < Anum(&c->sections); i++) {
+ free(Aget(&c->sections, i));
+ Aset(&c->sections, i, nil);
+ }
+ free(c);
+ if (Aset(&configs, config, nil))
+ return 1;
+ werrstr("cannot free ini config: %r");
+ return 0;
+}
+
+int
Afindstrid(Array *a, char *item)
{
for (int i = 0; i < a->num; i++) {
@@ -397,6 +440,17 @@
if (!a->items)
sysfatal("error: %r");
a->maxnum = a->num;
+}
+
+int
+Aset(Array *a, int index, void *item)
+{
+ if (index >= a->num) {
+ werrstr("bad index");
+ return 0;
+ }
+ a->items[index] = item;
+ return 1;
}
void
--- a/ini.h
+++ b/ini.h
@@ -7,3 +7,4 @@
char* getinivalue(int config, char *section, char *key);
char** getiniarray(int config, char *section, char *key, int *num);
+int freeini(int config);
--- a/ini.man
+++ b/ini.man
@@ -11,6 +11,9 @@
.B
.ta \w'char** 'u
int iniconfig(char *file, int forcelower)
+.br
+.B
+int freeini(int config)
.PP
.B
.ta \w'char** 'u
@@ -38,6 +41,10 @@
.I id
of the parsed config file that's needed for future lookups.
It is possible to parse multiple INI files within one program this way.
+.I Freeini
+frees the whole loaded INI configuration specified by the
+.I config
+parameter.
.PP
The functions
.I getinivalue
@@ -69,3 +76,6 @@
.IR errstr .
.SH BUGS
Sure.
+.PP
+.I Freeini
+potentially leaks memory.
--- a/mkfile
+++ b/mkfile
@@ -12,8 +12,8 @@
test.$O: test.c ini.h
$CC $CFLAGS test.c
-test:QV: $O.out
- cat <<EOF >test.ini
+test.ini:Q:
+ cat <<EOF >$target
nocat=novalue
+nocatarr=valA
+nocatarr=valB
@@ -25,8 +25,7 @@
; Comment
[Category B]
- keyC=valueC
- keyD=valueD
+ keyB=valueB
[Category C]
keyx=nope
@@ -33,7 +32,12 @@
+keyF=abc
+keyF=def
EOF
- $O.out test.ini
+
+test:QV: $O.out test.ini
+ $O.out test.ini >/dev/null &
+ p=$apid
+ sleep 0.5 && leak -s $p | acid $p
+ wait $p
rm test.ini
README: ini.man
--- a/test.c
+++ b/test.c
@@ -2,10 +2,13 @@
#include <libc.h>
#include "ini.h"
+int verbose = 0;
+
void
parseline(char *section, char *key, char *value, int)
{
- print("section: '%s', key: '%s', value: '%s'\n", section, key, value);
+ if (verbose)
+ print("section: '%s', key: '%s', value: '%s'\n", section, key, value);
}
void
@@ -15,43 +18,58 @@
char *status = nil;
char *err = "error";
- print("Testing forcelower=0\n\n");
-
if (!parseini(file, parseline, 0, 0)) {
fprint(2, "error: %r\n");
status = err;
}
- print("\nTesting forcelower=1\n\n");
-
if (!parseini(file, parseline, 1, 0)) {
fprint(2, "error: %r\n");
status = err;
}
- print("\nTesting iniconfig\n\n");
int conf = iniconfig(file, 0);
+ if (conf < 0)
+ sysfatal("error: %r");
char* v = getinivalue(conf, "Category A", "keyB");
- print("Category A, keyB: %s\n", v);
+ if (strcmp(v, "valueB") != 0)
+ fprint(2, "fail: expected 'valueB', got '%s'\n", v);
int num;
char** values = getiniarray(conf, "Category C", "keyF", &num);
+ if (num != 2)
+ fprint(2, "fail: expected 2 entries, got %d\n", num);
if (values) {
for (int i = 0; i < num; i++) {
- print("array: %s\n", values[i]);
+ if (i == 0 && strcmp(values[i], "abc") != 0)
+ fprint(2, "fail: expected 'abc', got '%s'\n", values[i]);
+ if (i == 1 && strcmp(values[i], "def") != 0)
+ fprint(2, "fail: expected 'def', got '%s'\n", values[i]);
}
}
v = getinivalue(conf, nil, "nocat");
- print("no category, nocat: %s\n", v);
+ if (strcmp(v, "novalue") != 0)
+ fprint(2, "fail: expected 'novalue', got '%s'\n", v);
values = getiniarray(conf, nil, "nocatarr", &num);
+ if (num != 2)
+ fprint(2, "fail: expected 2 entries, got %d\n", num);
if (values) {
for (int i = 0; i < num; i++) {
- print("nocatarr: %s\n", values[i]);
+ if (i == 0 && strcmp(values[i], "valA") != 0)
+ fprint(2, "fail: expected 'valA', got '%s'\n", values[i]);
+ if (i == 1 && strcmp(values[i], "valB") != 0)
+ fprint(2, "fail: expected 'valB', got '%s'\n", values[i]);
}
}
+
+ int r = freeini(conf);
+ if (!r)
+ fprint(2, "error freeing: %r\n");
+
+ sleep(1000);
exits(status);
}