From 8499b75fb011d72eeb5acbd85bcf41e4ee51e9a7 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Sun, 17 Sep 2023 00:40:34 +0200 Subject: XML --- source/kit/_static.c | 1 + source/kit/allocator.h | 2 +- source/kit/input_buffer.c | 87 ++++++++++++++--- source/kit/input_buffer.h | 24 +++-- source/kit/shared_mutex.h | 12 ++- source/kit/status.h | 41 ++++---- source/kit/xml.c | 203 +++++++++++++++++++-------------------- source/kit/xml.h | 10 +- source/tests/_static.c | 1 + source/tests/input_buffer.test.c | 28 +++--- source/tests/xml.test.c | 61 ++++++++++++ 11 files changed, 309 insertions(+), 161 deletions(-) create mode 100644 source/tests/xml.test.c (limited to 'source') diff --git a/source/kit/_static.c b/source/kit/_static.c index eb66879..03a72e8 100644 --- a/source/kit/_static.c +++ b/source/kit/_static.c @@ -12,3 +12,4 @@ #include "threads.win32.c" #include "shared_memory.posix.c" #include "shared_memory.win32.c" +#include "xml.c" diff --git a/source/kit/allocator.h b/source/kit/allocator.h index 3fcfe9b..9bd6e0c 100644 --- a/source/kit/allocator.h +++ b/source/kit/allocator.h @@ -37,7 +37,7 @@ typedef struct { // See KIT_ENABLE_CUSTOM_ALLOC_DISPATCH macro. // void *kit_alloc_dispatch(kit_allocator_t *alloc, i32 request, - i64 size, i64 previous_size, void *poi32er); + i64 size, i64 previous_size, void *pointer); kit_allocator_t kit_alloc_default(void); diff --git a/source/kit/input_buffer.c b/source/kit/input_buffer.c index c1ae748..5081cc4 100644 --- a/source/kit/input_buffer.c +++ b/source/kit/input_buffer.c @@ -39,7 +39,8 @@ static void kit_buf_acquire_(void *p) { } static void kit_buf_release_(void *p) { - assert(p != NULL); + if (p == NULL) + return; internal_buffer_t *buf = (internal_buffer_t *) p; @@ -74,9 +75,9 @@ static i64 kit_buf_read_(void *p, i64 offset, kit_str_t destination) { return n; } -kit_ib_handle_t kit_ib_wrap(kit_is_handle_t upstream, - kit_allocator_t *alloc) { - kit_ib_handle_t buf; +kit_ib_t kit_ib_wrap(kit_is_handle_t upstream, + kit_allocator_t *alloc) { + kit_ib_t buf; memset(&buf, 0, sizeof buf); buf.status = KIT_OK; DA_INIT(buf.data, 0, alloc); @@ -86,8 +87,8 @@ kit_ib_handle_t kit_ib_wrap(kit_is_handle_t upstream, return buf; } -kit_ib_handle_t kit_ib_read(kit_ib_handle_t buf, i64 size) { - kit_ib_handle_t next; +kit_ib_t kit_ib_read(kit_ib_t buf, i64 size) { + kit_ib_t next; memset(&next, 0, sizeof next); if (buf.status != KIT_OK) { @@ -115,9 +116,69 @@ kit_ib_handle_t kit_ib_read(kit_ib_handle_t buf, i64 size) { return next; } -kit_ib_handle_t kit_ib_read_while( - kit_ib_handle_t buf, kit_ib_read_condition_fn condition) { - kit_ib_handle_t next; +kit_ib_t kit_ib_exact(kit_ib_t buf, kit_str_t data) { + kit_ib_t res = kit_ib_read(buf, data.size); + if (!AR_EQUAL(res.data, data)) + res.status = KIT_ERROR_INTERNAL; + return res; +} + +kit_ib_t kit_ib_until(kit_ib_t buf, kit_str_t data) { + kit_ib_t next; + memset(&next, 0, sizeof next); + + if (buf.status != KIT_OK) { + next.status = buf.status; + return next; + } + + kit_buf_acquire_(buf.internal); + + DA_INIT(next.data, 0, kit_buf_alloc_(buf.internal)); + + i64 size = 0; + + for (;; ++size) { + kit_buf_adjust_(buf.internal, buf.offset + size + 1); + + DA_RESIZE(next.data, size + 1); + + assert(next.data.size == size + 1); + if (next.data.size != size + 1) { + next.status = KIT_ERROR_BAD_ALLOC; + return next; + } + + kit_str_t destination = { .size = 1, + .values = next.data.values + size }; + i64 n = kit_buf_read_(buf.internal, buf.offset + size, + destination); + + if (n != 1) + break; + + if (size + 1 >= data.size && + AR_EQUAL(kit_str(data.size, + next.data.values + (size + 1 - data.size)), + data)) { + size -= data.size - 1; + break; + } + } + + next.offset = buf.offset + size; + next.internal = buf.internal; + + DA_RESIZE(next.data, size); + if (next.data.size != size) + next.status = KIT_ERROR_BAD_ALLOC; + + return next; +} + +kit_ib_t kit_ib_while(kit_ib_t buf, + kit_ib_read_condition_fn condition) { + kit_ib_t next; memset(&next, 0, sizeof next); if (buf.status != KIT_OK) { @@ -135,8 +196,12 @@ kit_ib_handle_t kit_ib_read_while( kit_buf_adjust_(buf.internal, buf.offset + size + 1); DA_RESIZE(next.data, size + 1); - if (next.data.size != size + 1) + + assert(next.data.size == size + 1); + if (next.data.size != size + 1) { next.status = KIT_ERROR_BAD_ALLOC; + return next; + } kit_str_t destination = { .size = 1, .values = next.data.values + size }; @@ -158,7 +223,7 @@ kit_ib_handle_t kit_ib_read_while( return next; } -void kit_ib_destroy(kit_ib_handle_t buf) { +void kit_ib_destroy(kit_ib_t buf) { kit_buf_release_(buf.internal); DA_DESTROY(buf.data); } diff --git a/source/kit/input_buffer.h b/source/kit/input_buffer.h index 5de85c9..e34b512 100644 --- a/source/kit/input_buffer.h +++ b/source/kit/input_buffer.h @@ -14,19 +14,23 @@ typedef struct { i64 offset; void *internal; kit_str_builder_t data; -} kit_ib_handle_t; +} kit_ib_t; typedef i8 (*kit_ib_read_condition_fn)(kit_str_t data); -kit_ib_handle_t kit_ib_wrap(kit_is_handle_t upstream, - kit_allocator_t *alloc); +kit_ib_t kit_ib_wrap(kit_is_handle_t upstream, + kit_allocator_t *alloc); -kit_ib_handle_t kit_ib_read(kit_ib_handle_t buf, i64 size); +kit_ib_t kit_ib_read(kit_ib_t buf, i64 size); -kit_ib_handle_t kit_ib_read_while(kit_ib_handle_t buf, - kit_ib_read_condition_fn condition); +kit_ib_t kit_ib_exact(kit_ib_t buf, kit_str_t data); -void kit_ib_destroy(kit_ib_handle_t buf); +kit_ib_t kit_ib_until(kit_ib_t buf, kit_str_t data); + +kit_ib_t kit_ib_while(kit_ib_t buf, + kit_ib_read_condition_fn condition); + +void kit_ib_destroy(kit_ib_t buf); #define KIT_IB_WRAP(upstream) kit_ib_wrap(upstream, NULL) @@ -35,11 +39,13 @@ void kit_ib_destroy(kit_ib_handle_t buf); #endif #ifndef KIT_DISABLE_SHORT_NAMES -# define ib_handle_t kit_ib_handle_t +# define ib_t kit_ib_t # define ib_read_condition_fn kit_ib_read_condition_fn # define ib_wrap kit_ib_wrap # define ib_read kit_ib_read -# define ib_read_while kit_ib_read_while +# define ib_exact kit_ib_exact +# define ib_until kit_ib_until +# define ib_while kit_ib_while # define ib_destroy kit_ib_destroy # define IB_WRAP KIT_IB_WRAP diff --git a/source/kit/shared_mutex.h b/source/kit/shared_mutex.h index 0896b86..de5958d 100644 --- a/source/kit/shared_mutex.h +++ b/source/kit/shared_mutex.h @@ -123,7 +123,7 @@ static i8 kit_unique_try_lock(kit_shared_mutex_t *m) { return is_locked; } -static void kit_lock(kit_shared_mutex_t *m) { +static void kit_unique_lock(kit_shared_mutex_t *m) { assert(m != NULL); while (!kit_unique_try_lock(m)) @@ -151,4 +151,14 @@ static void kit_unique_unlock(kit_shared_mutex_t *m) { } #endif +#ifndef KIT_DISABLE_SHORT_NAMES +# define shared_mutex_init kit_shared_mutex_init +# define shared_try_lock kit_shared_try_lock +# define shared_lock kit_shared_lock +# define shared_unlock kit_shared_unlock +# define unique_try_lock kit_unique_try_lock +# define unique_lock kit_unique_lock +# define unique_unlock kit_unique_unlock +#endif + #endif diff --git a/source/kit/status.h b/source/kit/status.h index 35139e3..86b34b1 100644 --- a/source/kit/status.h +++ b/source/kit/status.h @@ -5,26 +5,27 @@ enum { KIT_OK = 0, - KIT_ERROR_BAD_ALLOC = 1, - KIT_ERROR_INVALID_ARGUMENT = (1 << 1), - KIT_ERROR_MKDIR_FAILED = (1 << 2), - KIT_ERROR_RMDIR_FAILED = (1 << 3), - KIT_ERROR_UNLINK_FAILED = (1 << 4), - KIT_ERROR_FILE_ALREADY_EXISTS = (1 << 5), - KIT_ERROR_FILE_DOES_NOT_EXIST = (1 << 6), - KIT_ERROR_PATH_TOO_LONG = (1 << 7), - KIT_ERROR_SOCKETS_STARTUP_FAILED = (1 << 8), - KIT_ERROR_SOCKET_CONTROL_FAILED = (1 << 9), - KIT_ERROR_NAME_TOO_LONG = (1 << 10), - KIT_ERROR_INVALID_SIZE = (1 << 11), - KIT_ERROR_INVALID_NAME = (1 << 12), - KIT_ERROR_INVALID_PATH = (1 << 13), - KIT_ERROR_OPEN_FAILED = (1 << 14), - KIT_ERROR_TRUNCATE_FAILED = (1 << 15), - KIT_ERROR_MAP_FAILED = (1 << 16), - KIT_ERROR_UNMAP_FAILED = (1 << 17), - KIT_ERROR_SYNC_FAILED = (1 << 18), - KIT_ERROR_CLOSE_FAILED = (1 << 19), + KIT_ERROR_INTERNAL = 1, + KIT_ERROR_BAD_ALLOC = (1 << 1), + KIT_ERROR_INVALID_ARGUMENT = (1 << 2), + KIT_ERROR_MKDIR_FAILED = (1 << 3), + KIT_ERROR_RMDIR_FAILED = (1 << 4), + KIT_ERROR_UNLINK_FAILED = (1 << 5), + KIT_ERROR_FILE_ALREADY_EXISTS = (1 << 6), + KIT_ERROR_FILE_DOES_NOT_EXIST = (1 << 7), + KIT_ERROR_PATH_TOO_LONG = (1 << 8), + KIT_ERROR_SOCKETS_STARTUP_FAILED = (1 << 9), + KIT_ERROR_SOCKET_CONTROL_FAILED = (1 << 10), + KIT_ERROR_NAME_TOO_LONG = (1 << 11), + KIT_ERROR_INVALID_SIZE = (1 << 12), + KIT_ERROR_INVALID_NAME = (1 << 13), + KIT_ERROR_INVALID_PATH = (1 << 14), + KIT_ERROR_OPEN_FAILED = (1 << 15), + KIT_ERROR_TRUNCATE_FAILED = (1 << 16), + KIT_ERROR_MAP_FAILED = (1 << 17), + KIT_ERROR_UNMAP_FAILED = (1 << 18), + KIT_ERROR_SYNC_FAILED = (1 << 19), + KIT_ERROR_CLOSE_FAILED = (1 << 20), KIT_ERROR_NOT_IMPLEMENTED = (1 << 30), }; diff --git a/source/kit/xml.c b/source/kit/xml.c index 22a850a..bb16192 100644 --- a/source/kit/xml.c +++ b/source/kit/xml.c @@ -1,112 +1,93 @@ #include "xml.h" +#include "input_buffer.h" #include -static i8 kit_xml_is_spaces(str_t data) { - assert(data.size >= 0); - assert(data.values != NULL); - - if (data.size <= 0) - return 1; - char c = data.values[data.size - 1]; - return c == ' ' || c == '\t' || c == '\r' || c == '\n'; -} - -static i8 kit_xml_is_idword(str_t data) { - assert(data.size >= 0); - assert(data.values != NULL); - - if (data.size <= 0) - return 1; - if (data.values[0] >= '0' || data.values[0] <= '9') - return 0; - char c = data.values[data.size - 1]; - return c == '_' || (c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); -} - -static i8 kit_xml_not_tag_open(str_t data) { - assert(data.size >= 0); - assert(data.values != NULL); - - return data.size <= 0 || data.values[data.size - 1] != '<'; -} - -static i8 kit_xml_not_tag_close(str_t data) { - assert(data.size >= 0); - assert(data.values != NULL); - - return data.size <= 0 || data.values[data.size - 1] != '>'; -} - -static i8 kit_xml_is_tag_open(str_t data) { - assert(data.size >= 0); - assert(data.values != NULL); - - return data.size == 1 && data.values[0] == '<'; -} - -static i8 kit_xml_is_tag_close(str_t data) { - assert(data.size >= 0); - assert(data.values != NULL); - - return data.size == 1 && data.values[0] == '>'; -} - -static i8 kit_xml_is_tag_final_open(str_t data) { - assert(data.size >= 0); - assert(data.values != NULL); - - return data.size == 2 && data.values[0] == '<' && - data.values[1] == '/'; -} - -static i8 kit_xml_is_tag_final_close(str_t data) { - assert(data.size >= 0); - assert(data.values != NULL); - - return data.size == 2 && data.values[0] == '/' && - data.values[0] == '>'; -} - -static i8 kit_xml_is_quotes(str_t data) { - assert(data.size >= 0); - assert(data.values != NULL); - - return data.size == 1 && data.values[0] == '"'; -} - -static i8 kit_xml_is_inside_quotes(str_t data) { - assert(data.size >= 0); - assert(data.values != NULL); - - if (data.size <= 0) - return 1; - for (i64 i = 0; i < data.size; i++) - if (data.values[i] == '"') - return 0; - else if (data.values[i] == '\\') - i++; - return 1; -} - -static i8 kit_xml_is_number(str_t data) { - assert(data.size >= 0); - assert(data.values != NULL); - - if (data.size <= 0) - return 1; - char c = data.values[data.size - 1]; - return c >= '0' && c <= '9'; -} - kit_xml_parse_result_t kit_xml_parse(kit_is_handle_t is, kit_allocator_t *alloc) { - kit_xml_parse_result_t result; - memset(&result, 0, sizeof result); - - result.status = KIT_ERROR_NOT_IMPLEMENTED; - return result; + xml_parse_result_t res; + memset(&res, 0, sizeof res); + + ib_t begin = ib_wrap(is, alloc); + ib_t tag_before = ib_until(begin, SZ("<")); + ib_t tag_open = ib_exact(tag_before, SZ("<")); + ib_t tag_name = ib_until(tag_open, SZ(">")); + ib_t tag_name_empty = ib_until(tag_open, SZ("/")); + +#define return_ \ + ib_destroy(begin); \ + ib_destroy(tag_before); \ + ib_destroy(tag_open); \ + ib_destroy(tag_name); \ + ib_destroy(tag_name_empty); \ + ib_destroy(tag_close); \ + return + + if (tag_name_empty.offset < tag_name.offset) { + ib_t tag_close = ib_exact(tag_name_empty, SZ("/>")); + + if (tag_close.status != KIT_OK) { + res.status = KIT_ERROR_INTERNAL; + return_ res; + } + + // move + res.xml.tag = tag_name_empty.data; + memset(&tag_name_empty.data, 0, sizeof tag_name_empty.data); + + while (res.xml.tag.size > 0 && + res.xml.tag.values[res.xml.tag.size - 1] == ' ') + --res.xml.tag.size; + + DA_INIT(res.xml.text, 0, alloc); + DA_INIT(res.xml.tail, 0, alloc); + DA_INIT(res.xml.properties, 0, alloc); + DA_INIT(res.xml.children, 0, alloc); + + res.status = KIT_OK; + return_ res; + } + + ib_t tag_close = ib_exact(tag_name, SZ(">")); + ib_t tag_text = ib_until(tag_close, SZ("<")); + ib_t tagend_open = ib_exact(tag_text, SZ("")); + +#undef return_ +#define return_ \ + ib_destroy(begin); \ + ib_destroy(tag_before); \ + ib_destroy(tag_open); \ + ib_destroy(tag_name); \ + ib_destroy(tag_name_empty); \ + ib_destroy(tag_close); \ + ib_destroy(tag_text); \ + ib_destroy(tagend_open); \ + ib_destroy(tagend_name); \ + ib_destroy(tagend_close); \ + return + + if (tagend_close.status != KIT_OK) { + res.status = KIT_ERROR_INTERNAL; + return_ res; + } + + // move + res.xml.tag = tag_name.data; + memset(&tag_name.data, 0, sizeof tag_name.data); + + // move + res.xml.text = tag_text.data; + memset(&tag_text.data, 0, sizeof tag_text.data); + + DA_INIT(res.xml.tail, 0, alloc); + DA_INIT(res.xml.properties, 0, alloc); + DA_INIT(res.xml.children, 0, alloc); + + res.status = KIT_OK; + return_ res; +#undef return_ } kit_xml_print_result_t kit_xml_print(kit_xml_t *xml, @@ -122,7 +103,21 @@ kit_xml_print_result_t kit_xml_print(kit_xml_t *xml, void kit_xml_destroy(kit_xml_t *xml) { assert(xml != NULL); - if (xml == NULL) return; + + for (i64 i = 0; i < xml->properties.size; i++) { + DA_DESTROY(xml->properties.values[i].name); + DA_DESTROY(xml->properties.values[i].value); + } + + for (i64 i = 0; i < xml->children.size; i++) + kit_xml_destroy(xml->children.values + i); + + DA_DESTROY(xml->tag); + DA_DESTROY(xml->text); + DA_DESTROY(xml->tail); + + DA_DESTROY(xml->properties); + DA_DESTROY(xml->children); } diff --git a/source/kit/xml.h b/source/kit/xml.h index 409c835..c33392b 100644 --- a/source/kit/xml.h +++ b/source/kit/xml.h @@ -29,7 +29,7 @@ struct kit_xml_ { typedef struct { kit_status_t status; - kit_xml_t *xml; + kit_xml_t xml; } kit_xml_parse_result_t; typedef struct { @@ -47,4 +47,12 @@ void kit_xml_destroy(kit_xml_t *xml); } #endif +#ifndef KIT_DISABLE_SHORT_NAMES +# define xml_parse kit_xml_parse +# define xml_print kit_xml_print +# define xml_destroy kit_xml_destroy +# define xml_t kit_xml_t +# define xml_parse_result_t kit_xml_parse_result_t +#endif + #endif diff --git a/source/tests/_static.c b/source/tests/_static.c index 9e4451d..b406f48 100644 --- a/source/tests/_static.c +++ b/source/tests/_static.c @@ -18,4 +18,5 @@ #include "string_ref.test.c" #include "duration.test.c" #include "thread.test.c" +#include "xml.test.c" #include "bench.test.c" diff --git a/source/tests/input_buffer.test.c b/source/tests/input_buffer.test.c index 4712da9..237d60d 100644 --- a/source/tests/input_buffer.test.c +++ b/source/tests/input_buffer.test.c @@ -6,9 +6,9 @@ TEST("input buffer read once") { str_t text = { .size = 3, .values = "foo" }; is_handle_t in = IS_WRAP_STRING(text); - ib_handle_t first = IB_WRAP(in); + ib_t first = IB_WRAP(in); - ib_handle_t second = ib_read(first, 3); + ib_t second = ib_read(first, 3); REQUIRE(second.status == KIT_OK); REQUIRE(second.data.size == 3); @@ -23,10 +23,10 @@ TEST("input buffer read again") { str_t text = { .size = 6, .values = "foobar" }; str_t foo = { .size = 3, .values = "foo" }; is_handle_t in = IS_WRAP_STRING(text); - ib_handle_t first = IB_WRAP(in); + ib_t first = IB_WRAP(in); - ib_handle_t second = ib_read(first, 3); - ib_handle_t third = ib_read(first, 3); + ib_t second = ib_read(first, 3); + ib_t third = ib_read(first, 3); REQUIRE(AR_EQUAL(foo, second.data)); REQUIRE(AR_EQUAL(foo, third.data)); @@ -42,10 +42,10 @@ TEST("input buffer read twice") { str_t foo = { .size = 3, .values = "foo" }; str_t bar = { .size = 3, .values = "bar" }; is_handle_t in = IS_WRAP_STRING(text); - ib_handle_t first = IB_WRAP(in); + ib_t first = IB_WRAP(in); - ib_handle_t second = ib_read(first, 3); - ib_handle_t third = ib_read(second, 3); + ib_t second = ib_read(first, 3); + ib_t third = ib_read(second, 3); REQUIRE(AR_EQUAL(foo, second.data)); REQUIRE(AR_EQUAL(bar, third.data)); @@ -67,9 +67,9 @@ TEST("input buffer read integer once") { str_t text = { .size = 9, .values = "31415 foo" }; str_t num = { .size = 5, .values = "31415" }; is_handle_t in = IS_WRAP_STRING(text); - ib_handle_t first = IB_WRAP(in); + ib_t first = IB_WRAP(in); - ib_handle_t second = ib_read_while(first, is_integer_); + ib_t second = ib_while(first, is_integer_); REQUIRE(second.status == KIT_OK); REQUIRE(second.data.size == 5); @@ -85,11 +85,11 @@ TEST("input buffer read integer twice") { str_t num_0 = { .size = 3, .values = "314" }; str_t num_1 = { .size = 2, .values = "15" }; is_handle_t in = IS_WRAP_STRING(text); - ib_handle_t first = IB_WRAP(in); + ib_t first = IB_WRAP(in); - ib_handle_t second = ib_read_while(first, is_integer_); - ib_handle_t third = ib_read(second, 1); - ib_handle_t fourth = ib_read_while(third, is_integer_); + ib_t second = ib_while(first, is_integer_); + ib_t third = ib_read(second, 1); + ib_t fourth = ib_while(third, is_integer_); REQUIRE(fourth.status == KIT_OK); REQUIRE(second.data.size == 3); diff --git a/source/tests/xml.test.c b/source/tests/xml.test.c new file mode 100644 index 0000000..7d2151c --- /dev/null +++ b/source/tests/xml.test.c @@ -0,0 +1,61 @@ +#include "../kit/xml.h" + +#define KIT_TEST_FILE xml +#include "../kit/kit_test.h" + +TEST("xml parse tag") { + is_handle_t is = IS_WRAP_STRING(SZ(" ")); + xml_parse_result_t res = xml_parse(is, NULL); + + REQUIRE_EQ(res.status, KIT_OK); + + if (res.status == KIT_OK) { + REQUIRE(AR_EQUAL(res.xml.tag, SZ("foo"))); + xml_destroy(&res.xml); + } + + is_destroy(is); +} + +TEST("xml parse tag not closed") { + is_handle_t is = IS_WRAP_STRING(SZ("")); + xml_parse_result_t res = xml_parse(is, NULL); + + REQUIRE_EQ(res.status, KIT_ERROR_INTERNAL); + + if (res.status == KIT_OK) + xml_destroy(&res.xml); + + is_destroy(is); +} + +TEST("xml parse tag text") { + is_handle_t is = IS_WRAP_STRING(SZ(" bar ")); + xml_parse_result_t res = xml_parse(is, NULL); + + REQUIRE_EQ(res.status, KIT_OK); + + if (res.status == KIT_OK) { + REQUIRE(AR_EQUAL(res.xml.tag, SZ("foo"))); + REQUIRE(AR_EQUAL(res.xml.text, SZ(" bar "))); + xml_destroy(&res.xml); + } + + is_destroy(is); +} + +TEST("xml parse empty tag") { + is_handle_t is = IS_WRAP_STRING(SZ("")); + xml_parse_result_t res = xml_parse(is, NULL); + + REQUIRE_EQ(res.status, KIT_OK); + + if (res.status == KIT_OK) { + REQUIRE(AR_EQUAL(res.xml.tag, SZ("foo"))); + xml_destroy(&res.xml); + } + + is_destroy(is); +} + +#undef KIT_TEST_FILE -- cgit v1.2.3