Sat, 27 Jan 2024 17:50:19 +0100
update ucx, libidav and add first gui code
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/application.c Sat Jan 27 17:50:19 2024 +0100 @@ -0,0 +1,53 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "application.h" + +#include <string.h> +#include <stdbool.h> + +#include "window.h" + + + +void application_startup(UiEvent* event, void* data) { + application_create_menu(); + + UiObject* win = window_create(); + ui_show(win); +} + +void application_create_menu(void) { + +} + + +DavApp* application_create_app_document(void) { + DavApp* doc = ui_document_new(sizeof(DavApp)); + return doc; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/application.h Sat Jan 27 17:50:19 2024 +0100 @@ -0,0 +1,55 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <ui/ui.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <libidav/webdav.h> +#include <libidav/config.h> + + +typedef struct DavApp { + DavConfig *dav_config; + +} DavApp; + + +/* + * startup callback for the ui framework + */ +void application_startup(UiEvent* event, void* data); + +/* + * create the global menu and toolbar + */ +void application_create_menu(void); + + +DavApp* application_create_app_document(void);
--- a/application/main.c Mon Jan 22 17:27:47 2024 +0100 +++ b/application/main.c Sat Jan 27 17:50:19 2024 +0100 @@ -1,110 +1,58 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <stdlib.h> - -#include <ui/ui.h> -#include <cx/buffer.h> -#include <cx/utils.h> - -typedef struct { - UiText *text; -} MyDocument; - -MyDocument *doc1; -MyDocument *doc2; - - -void action_menu(UiEvent *event, void *userdata) { - printf("action_menu: %s\n", (char*)userdata); -} - -void action_button(UiEvent *event, void *userdata) { - printf("button test\n"); - MyDocument *doc = event->document; - if(!doc) { - printf("no document\n"); - return; - } - - char *text = doc->text->get(doc->text); - printf("text: {\n%s\n}\n", text); -} - -void action_switch(UiEvent *event, void *userdata) { - if(event->document == doc1) { - ui_set_document(event->obj, doc2); - } else { - ui_set_document(event->obj, doc1); - } -} - - -MyDocument* create_doc(void) { - MyDocument *doc = ui_document_new(sizeof(MyDocument)); - UiContext *docctx = ui_document_context(doc); - doc->text = ui_text_new(docctx, "text"); - return doc; -} - -void application_startup(UiEvent *event, void *data) { - - UiObject *obj = ui_window("Test", NULL); - ui_textarea_nv(obj, "text"); - ui_button(obj, "Test", action_button, NULL); - ui_button(obj, "Switch Document", action_switch, NULL); - - doc1 = create_doc(); - doc2 = create_doc(); - - ui_attach_document(obj->ctx, doc1); - - ui_show(obj); -} - -int main(int argc, char** argv) { - ui_init("app1", argc, argv); - ui_onstartup(application_startup, NULL); - - // menu - ui_menu("_File"); - ui_menuitem("_Hello", action_menu, NULL); - ui_submenu("Submenu1"); - ui_submenu("Submenu2"); - ui_menuitem("item2", action_menu, NULL); - ui_submenu_end(); - ui_menuitem("item3", action_menu, NULL); - ui_submenu_end(); - ui_menuitem("item4", action_menu, NULL); - - - ui_main(); - - return (EXIT_SUCCESS); -} +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> + +#ifdef _WIN32 +#include <Windows.h> +#endif + +#include <ui/ui.h> + +#include "application.h" + +int idav_main(int argc, char **argv) { + ui_init("idav", argc, argv); + ui_onstartup(application_startup, NULL); + + ui_main(); + + return 0; +} + +#ifndef _WIN32 +int main(int argc, char** argv) { + return idav_main(argc, argv); +} +#else +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { + return idav_main(__argc, __argv); +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/window.c Sat Jan 27 17:50:19 2024 +0100 @@ -0,0 +1,56 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "window.h" + +#include <ui/stock.h> + + +UiObject* window_create(void) { + UiObject* obj = ui_window("iDAV", NULL); + + // navigation bar + ui_hbox(obj, .fill = UI_OFF) { + // TODO: go back/forward buttons + + ui_path_textfield(obj, .fill = UI_ON); + } + + // main content + UiModel* model = ui_model(obj->ctx, UI_ICON_TEXT, "Name", UI_STRING, "Type", UI_STRING, "Last Modified", UI_STRING, "Size", -1); + ui_table(obj, .fill = UI_ON, .model = model); + + // status bar + ui_hbox(obj, .fill = UI_OFF) { + ui_label(obj, .label = ""); + } + + ui_model_free(obj->ctx, model); + + return obj; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/window.h Sat Jan 27 17:50:19 2024 +0100 @@ -0,0 +1,31 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <ui/ui.h> + +UiObject* window_create(void);
--- a/libidav/Makefile Mon Jan 22 17:27:47 2024 +0100 +++ b/libidav/Makefile Sat Jan 27 17:50:19 2024 +0100 @@ -41,6 +41,7 @@ SRC += crypto.c SRC += xml.c SRC += versioning.c +SRC += config.c OBJ = $(SRC:%.c=../build/libidav/%$(OBJ_EXT)) @@ -52,6 +53,9 @@ ../build/libidav/%$(OBJ_EXT): %.c $(CC) -I../ucx $(CFLAGS) $(DAV_CFLAGS) -c -o $@ $< +../build/ucx: + test -d '$@' + cppcheck: $(SRC) $(CPPCHECK) $(CPPCHECK_CONFIG) -I../ucx $(CPPCHECK_FLAGS) $+ 2>> ../$(CPPCHECK_LOG)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libidav/config.c Sat Jan 27 17:50:19 2024 +0100 @@ -0,0 +1,816 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <cx/hash_map.h> +#include <errno.h> +#include <libxml/tree.h> +#include "utils.h" + +#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) +#define xstrEQ(a,b) !xmlStrcasecmp(BAD_CAST a, BAD_CAST b) + +#define print_error(lineno, ...) \ + do {\ + fprintf(stderr, "Error (config.xml line %u): ", lineno); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "Abort.\n"); \ + } while(0); +#define print_warning(lineno, ...) \ + do {\ + fprintf(stderr, "Warning (config.xml line %u): ", lineno); \ + fprintf(stderr, __VA_ARGS__); \ + } while(0); + +#ifdef _WIN32 +#define ENV_HOME getenv("USERPROFILE") +#else +#define ENV_HOME getenv("HOME") +#endif /* _WIN32 */ + + +static int load_repository( + DavConfig *config, + DavCfgRepository **list_begin, + DavCfgRepository **list_end, + xmlNode *reponode); +static int load_key( + DavConfig *config, + DavCfgKey **list_begin, + DavCfgKey **list_end, + xmlNode *keynode); +static int load_proxy( + DavConfig *config, DavCfgProxy *proxy, xmlNode *proxynode, int type); +static int load_namespace( + DavConfig *config, + DavCfgNamespace **list_begin, + DavCfgNamespace **list_end, + xmlNode *node); +static int load_secretstore(DavConfig *config, xmlNode *node); + + +int dav_cfg_string_set_value(DavConfig *config, CfgString *str, xmlNode *node) { + str->node = node; + char *value = util_xml_get_text(node); + if(value) { + str->value = cx_strdup_a(config->mp->allocator, cx_str(value)); + return 0; + } else { + str->value = (cxmutstr){NULL, 0}; + return 1; + } +} + +void dav_cfg_bool_set_value(DavConfig *config, CfgBool *cbool, xmlNode *node) { + cbool->node = node; + char *value = util_xml_get_text(node); + cbool->value = util_getboolean(value); +} + + +DavConfig* dav_config_load(cxmutstr xmlfilecontent, int *error) { + xmlDoc *doc = xmlReadMemory(xmlfilecontent.ptr, xmlfilecontent.length, NULL, NULL, 0); + if(!doc) { + if(error) { + *error = DAV_CONFIG_ERROR_XML; + } + return NULL; + } + + CxMempool *cfg_mp = cxMempoolCreate(128, NULL); + cxMempoolRegister(cfg_mp, doc, (cx_destructor_func)xmlFreeDoc); + DavConfig *config = cxMalloc(cfg_mp->allocator, sizeof(DavConfig)); + memset(config, 0, sizeof(DavConfig)); + config->mp = cfg_mp; + config->doc = doc; + + DavCfgRepository *repos_begin = NULL; + DavCfgRepository *repos_end = NULL; + DavCfgKey *keys_begin = NULL; + DavCfgKey *keys_end = NULL; + DavCfgNamespace *namespaces_begin = NULL; + DavCfgNamespace *namespaces_end = NULL; + + xmlNode *xml_root = xmlDocGetRootElement(doc); + xmlNode *node = xml_root->children; + int ret = 0; + while(node && !ret) { + if(node->type == XML_ELEMENT_NODE) { + if(xstreq(node->name, "repository")) { + ret = load_repository(config, &repos_begin, &repos_end, node); + } else if(xstreq(node->name, "key")) { + ret = load_key(config, &keys_begin, &keys_end, node); + } else if (xstreq(node->name, "http-proxy")) { + config->http_proxy = cxCalloc(config->mp->allocator, 1, sizeof(DavCfgProxy)); + ret = load_proxy(config, config->http_proxy, node, DAV_HTTP_PROXY); + } else if (xstreq(node->name, "https-proxy")) { + config->https_proxy = cxCalloc(config->mp->allocator, 1, sizeof(DavCfgProxy)); + ret = load_proxy(config, config->https_proxy, node, DAV_HTTPS_PROXY); + } else if (xstreq(node->name, "namespace")) { + ret = load_namespace(config, &namespaces_begin, &namespaces_end, node); + } else if (xstreq(node->name, "secretstore")) { + ret = load_secretstore(config, node); + } else { + fprintf(stderr, "Unknown config element: %s\n", node->name); + ret = 1; + } + } + node = node->next; + } + + config->repositories = repos_begin; + config->keys = keys_begin; + config->namespaces = namespaces_begin; + + if(ret != 0 && error) { + *error = ret; + cxMempoolDestroy(cfg_mp); + } + + return config; +} + +void dav_config_free(DavConfig *config) { + cxMempoolDestroy(config->mp); +} + +CxBuffer* dav_config2buf(DavConfig *config) { + xmlChar* xmlText = NULL; + int textLen = 0; + xmlDocDumpFormatMemory(config->doc, &xmlText, &textLen, 1); + + if(!xmlText) { + return NULL; + } + + CxBuffer *buf = cxBufferCreate(NULL, textLen, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); + cxBufferWrite(xmlText, 1, textLen, buf); + xmlFree(xmlText); + return buf; +} + + +static int repo_add_config( + DavConfig *config, + DavCfgRepository *repo, + xmlNode* node) +{ + unsigned short lineno = node->line; + char *key = (char*)node->name; + char *value = util_xml_get_text(node); + + /* every key needs a value */ + if(!value) { + /* TODO: maybe this should only be reported, if the key is valid + * But this makes the code very ugly. + */ + print_error(lineno, "missing value for config element: %s\n", key); + return 1; + } + + if(xstreq(key, "name")) { + dav_cfg_string_set_value(config, &repo->name, node); + } else if(xstreq(key, "url")) { + dav_cfg_string_set_value(config, &repo->url, node); + } else if(xstreq(key, "user")) { + dav_cfg_string_set_value(config, &repo->user, node); + } else if(xstreq(key, "password")) { + dav_cfg_string_set_value(config, &repo->password, node); + } else if(xstreq(key, "stored-user")) { + dav_cfg_string_set_value(config, &repo->stored_user, node); + } else if(xstreq(key, "default-key")) { + dav_cfg_string_set_value(config, &repo->default_key, node); + } else if(xstreq(key, "full-encryption")) { + dav_cfg_bool_set_value(config, &repo->full_encryption, node); + } else if(xstreq(key, "content-encryption")) { + dav_cfg_bool_set_value(config, &repo->content_encryption, node); + } else if(xstreq(key, "decrypt-content")) { + dav_cfg_bool_set_value(config, &repo->decrypt_content, node); + } else if(xstreq(key, "decrypt-name")) { + dav_cfg_bool_set_value(config, &repo->decrypt_name, node); + } else if(xstreq(key, "cert")) { + dav_cfg_string_set_value(config, &repo->cert, node); + } else if(xstreq(key, "verification")) { + dav_cfg_bool_set_value(config, &repo->verification, node); + } else if(xstreq(key, "ssl-version")) { + repo->ssl_version.node = node; + if(xstrEQ(value, "TLSv1")) { + repo->ssl_version.value = CURL_SSLVERSION_TLSv1; + } else if(xstrEQ(value, "SSLv2")) { + repo->ssl_version.value = CURL_SSLVERSION_SSLv2; + } else if(xstrEQ(value, "SSLv3")) { + repo->ssl_version.value = CURL_SSLVERSION_SSLv3; + } +#if LIBCURL_VERSION_MAJOR * 1000 + LIBCURL_VERSION_MINOR >= 7034 + else if(xstrEQ(value, "TLSv1.0")) { + repo->ssl_version.value = CURL_SSLVERSION_TLSv1_0; + } else if(xstrEQ(value, "TLSv1.1")) { + repo->ssl_version.value = CURL_SSLVERSION_TLSv1_1; + } else if(xstrEQ(value, "TLSv1.2")) { + repo->ssl_version.value = CURL_SSLVERSION_TLSv1_2; + } +#endif +#if LIBCURL_VERSION_MAJOR * 1000 + LIBCURL_VERSION_MINOR >= 7052 + else if(xstrEQ(value, "TLSv1.3")) { + repo->ssl_version.value = CURL_SSLVERSION_TLSv1_3; + } +#endif + else { + print_warning(lineno, "unknown ssl version: %s\n", value); + repo->ssl_version.value = CURL_SSLVERSION_DEFAULT; + } + } else if(xstreq(key, "authmethods")) { + repo->authmethods.node = node; + repo->authmethods.value = CURLAUTH_NONE; + const char *delims = " \t\r\n"; + char *meths = strdup(value); + char *meth = strtok(meths, delims); + while (meth) { + if(xstrEQ(meth, "basic")) { + repo->authmethods.value |= CURLAUTH_BASIC; + } else if(xstrEQ(meth, "digest")) { + repo->authmethods.value |= CURLAUTH_DIGEST; + } else if(xstrEQ(meth, "negotiate")) { + repo->authmethods.value |= CURLAUTH_GSSNEGOTIATE; + } else if(xstrEQ(meth, "ntlm")) { + repo->authmethods.value |= CURLAUTH_NTLM; + } else if(xstrEQ(meth, "any")) { + repo->authmethods.value = CURLAUTH_ANY; + } else if(xstrEQ(meth, "none")) { + /* skip */ + } else { + print_warning(lineno, + "unknown authentication method: %s\n", meth); + } + meth = strtok(NULL, delims); + } + free(meths); + } else { + print_error(lineno, "unkown repository config element: %s\n", key); + return 1; + } + return 0; +} + +static int load_repository( + DavConfig *config, + DavCfgRepository **list_begin, + DavCfgRepository **list_end, + xmlNode *reponode) +{ + DavCfgRepository *repo = dav_repository_new(config); + repo->node = reponode; + + // add repo config from child nodes + xmlNode *node = reponode->children; + int ret = 0; + while(node && !ret) { + if(node->type == XML_ELEMENT_NODE) { + ret = repo_add_config(config, repo, node); + } + node = node->next; + } + + // success: add repo to the configuration, error: free repo + if(ret) { + return 1; + } else { + cx_linked_list_add( + (void**)list_begin, + (void**)list_end, + offsetof(DavCfgRepository, prev), + offsetof(DavCfgRepository, next), + repo); + } + + return 0; +} + +static xmlNode* addXmlNode(xmlNode *node, const char *name, cxmutstr content) { + xmlNode *text1 = xmlNewDocText(node->doc, BAD_CAST "\t\t"); + xmlAddChild(node, text1); + + cxmutstr ctn = cx_strdup(cx_strcast(content)); + xmlNode *newNode = xmlNewChild(node, NULL, BAD_CAST name, BAD_CAST ctn.ptr); + free(ctn.ptr); + + xmlNode *text2 = xmlNewDocText(node->doc, BAD_CAST "\n"); + xmlAddChild(node, text2); + + return newNode; +} + +void dav_config_add_repository(DavConfig *config, DavCfgRepository *repo) { + if(repo->node) { + fprintf(stderr, "Error: dav_config_add_repository: node already exists\n"); + return; + } + + xmlNode *repoNode = xmlNewNode(NULL, BAD_CAST "repository"); + xmlNode *rtext1 = xmlNewDocText(config->doc, BAD_CAST "\n"); + xmlAddChild(repoNode, rtext1); + + if(repo->name.value.ptr) { + repo->name.node = addXmlNode(repoNode, "name", repo->name.value); + } + if(repo->url.value.ptr) { + repo->url.node = addXmlNode(repoNode, "url", repo->url.value); + } + if(repo->user.value.ptr) { + repo->user.node = addXmlNode(repoNode, "user", repo->user.value); + } + if(repo->password.value.ptr) { + repo->password.node = addXmlNode(repoNode, "password", repo->password.value); + } + + if(repo->stored_user.value.ptr) { + repo->stored_user.node = addXmlNode(repoNode, "stored-user", repo->stored_user.value); + } + if(repo->default_key.value.ptr) { + repo->default_key.node = addXmlNode(repoNode, "default-key", repo->default_key.value); + } + if(repo->cert.value.ptr) { + repo->cert.node = addXmlNode(repoNode, "cert", repo->cert.value); + } + + // TODO: implement booleans + + // indent closing tag + xmlNode *rtext2 = xmlNewDocText(config->doc, BAD_CAST "\t"); + xmlAddChild(repoNode, rtext2); + + // add repository to internal list + DavCfgRepository **list_begin = &config->repositories; + cx_linked_list_add( + (void**)list_begin, + NULL, + offsetof(DavCfgRepository, prev), + offsetof(DavCfgRepository, next), + repo); + + // add repository element to the xml document + xmlNode *xml_root = xmlDocGetRootElement(config->doc); + + xmlNode *text1 = xmlNewDocText(config->doc, BAD_CAST "\n\t"); + xmlAddChild(xml_root, text1); + + xmlAddChild(xml_root, repoNode); + + xmlNode *text2 = xmlNewDocText(config->doc, BAD_CAST "\n"); + xmlAddChild(xml_root, text2); +} + +DavCfgRepository* dav_repository_new(DavConfig *config) { + DavCfgRepository *repo = cxMalloc(config->mp->allocator, sizeof(DavCfgRepository)); + repo->decrypt_name.value = false; + repo->decrypt_content.value = true; + repo->decrypt_properties.value = false; + repo->verification.value = true; + repo->ssl_version.value = CURL_SSLVERSION_DEFAULT; + repo->authmethods.value = CURLAUTH_BASIC; + return repo; +} + +void dav_repository_free(DavConfig *config, DavCfgRepository *repo) { + // TODO +} + +void dav_repository_remove_and_free(DavConfig *config, DavCfgRepository *repo) { + if(repo->prev) { + repo->prev->next = repo->next; + } + if(repo->next) { + repo->next->prev = repo->prev; + } + + if(repo->node) { + // TODO: remove newline after repo node + + xmlUnlinkNode(repo->node); + xmlFreeNode(repo->node); + } +} + +int dav_repository_get_flags(DavCfgRepository *repo) { + int flags = 0; + + DavBool encrypt_content = FALSE; + DavBool encrypt_name = FALSE; + DavBool encrypt_properties = FALSE; + DavBool decrypt_content = FALSE; + DavBool decrypt_name = FALSE; + DavBool decrypt_properties = FALSE; + if(repo->full_encryption.value) { + encrypt_content = TRUE; + encrypt_name = TRUE; + encrypt_properties = TRUE; + decrypt_content = TRUE; + decrypt_name = TRUE; + decrypt_properties = TRUE; + } else if(repo->content_encryption.value) { + encrypt_content = TRUE; + decrypt_content = TRUE; + } + + if(decrypt_content) { + flags |= DAV_SESSION_DECRYPT_CONTENT; + } + if(decrypt_name) { + flags |= DAV_SESSION_DECRYPT_NAME; + } + if(decrypt_properties) { + flags |= DAV_SESSION_DECRYPT_PROPERTIES; + } + if(encrypt_content) { + flags |= DAV_SESSION_ENCRYPT_CONTENT; + } + if(encrypt_name) { + flags |= DAV_SESSION_ENCRYPT_NAME; + } + if(encrypt_properties) { + flags |= DAV_SESSION_ENCRYPT_PROPERTIES; + } + return flags; +} + +void dav_repository_set_url(DavConfig *config, DavCfgRepository *repo, cxstring newurl) { + if(repo->url.value.ptr) { + cxFree(config->mp->allocator, repo->url.value.ptr); + } + repo->url.value = cx_strdup_a(config->mp->allocator, newurl); +} + +void dav_repository_set_auth(DavConfig *config, DavCfgRepository *repo, cxstring user, cxstring password) { + const CxAllocator *a = config->mp->allocator; + repo->user.value = cx_strdup_a(a, user); + char *pwenc = util_base64encode(password.ptr, password.length); + repo->password.value = cx_strdup_a(a, cx_str(pwenc)); + free(pwenc); +} + +cxmutstr dav_repository_get_decodedpassword(DavCfgRepository *repo) { + cxmutstr pw = { NULL, 0 }; + if(repo->password.value.ptr) { + pw = cx_mutstr(util_base64decode(repo->password.value.ptr)); + } + return pw; +} + + +static int load_key( + DavConfig *config, + DavCfgKey **list_begin, + DavCfgKey **list_end, + xmlNode *keynode) +{ + xmlNode *node = keynode->children; + DavCfgKey *key = cxMalloc(config->mp->allocator, sizeof(DavCfgKey)); + memset(key, 0, sizeof(DavCfgKey)); + key->type = DAV_KEY_TYPE_AES256; + + int error = 0; + while(node) { + if(node->type == XML_ELEMENT_NODE) { + if(xstreq(node->name, "name")) { + dav_cfg_string_set_value(config, &key->name, node); + } else if(xstreq(node->name, "file")) { + dav_cfg_string_set_value(config, &key->file, node); + } else if(xstreq(node->name, "type")) { + const char *value = util_xml_get_text(node); + key->type_node = node; + if(!strcmp(value, "aes128")) { + key->type = DAV_KEY_TYPE_AES128; + } else if(!strcmp(value, "aes256")) { + key->type = DAV_KEY_TYPE_AES256; + } else { + print_error(node->line, "unknown key type %s\n", value); + error = 1; + } + } else { + key->unknown_elements++; + } + + } + node = node->next; + } + + if(!key->name.value.ptr) { + error = 1; + } + + if(!error) { + error = 0; + size_t expected_length = 0; + if(key->type == DAV_KEY_TYPE_AES128) { + expected_length = 16; + } + if(key->type == DAV_KEY_TYPE_AES256) { + expected_length = 32; + } + /* + if(key->length < expected_length) { + print_error(keynode->line, "key %s is too small (%zu < %zu)\n", + key->name, + key->length, + expected_length); + error = 1; + } + + // add key to context + if(!error) { + cxMapPut(keys, cx_hash_key_str(key->name), key); + dav_context_add_key(context, key); + } + */ + } + + // cleanup + if(error) { + return 1; + } else { + // add key to the configuration + cx_linked_list_add( + (void**)list_begin, + (void**)list_end, + offsetof(DavCfgKey, prev), + offsetof(DavCfgKey, next), + key); + + return 0; + } +} + +static int load_proxy( + DavConfig *config, DavCfgProxy *proxy, xmlNode *proxynode, int type) +{ + const char *stype; + if(type == DAV_HTTPS_PROXY) { + stype = "https"; + } else if(type == DAV_HTTP_PROXY) { + stype = "http"; + } + + if(!proxy) { + // no xml error - so report this directly via fprintf + fprintf(stderr, "no memory reserved for %s proxy.\n", stype); + return 1; + } + + xmlNode *node = proxynode->children; + int ret = 0; + while(node && !ret) { + if(node->type == XML_ELEMENT_NODE) { + int reportmissingvalue = 0; + if(xstreq(node->name, "url")) { + reportmissingvalue = dav_cfg_string_set_value(config, &proxy->url, node); + } else if(xstreq(node->name, "user")) { + reportmissingvalue = dav_cfg_string_set_value(config, &proxy->user, node); + } else if(xstreq(node->name, "password")) { + reportmissingvalue = dav_cfg_string_set_value(config, &proxy->password, node); + } else if(xstreq(node->name, "no")) { + reportmissingvalue = dav_cfg_string_set_value(config, &proxy->noproxy, node); + } else { + proxy->unknown_elements++; + } + + if (reportmissingvalue) { + print_error(node->line, + "missing value for proxy configuration element: %s\n", + node->name); + ret = 1; + break; + } + } + node = node->next; + } + + if(!ret && !proxy->url.value.ptr) { + print_error(proxynode->line, "missing url for %s proxy.\n", stype); + return 1; + } + + return ret; +} + +static char* get_attr_content(xmlNode *node) { + // TODO: remove code duplication (util_xml_get_text) + while(node) { + if(node->type == XML_TEXT_NODE) { + return (char*)node->content; + } + node = node->next; + } + return NULL; +} + +static int load_namespace( + DavConfig *config, + DavCfgNamespace **list_begin, + DavCfgNamespace **list_end, + xmlNode *node) +{ + const char *prefix = NULL; + const char *uri = NULL; + xmlAttr *attr = node->properties; + while(attr) { + if(attr->type == XML_ATTRIBUTE_NODE) { + char *value = get_attr_content(attr->children); + if(!value) { + print_error( + node->line, + "missing value for attribute %s\n", (char*)attr->name); + return 1; + } + if(xstreq(attr->name, "prefix")) { + prefix = value; + } else if(xstreq(attr->name, "uri")) { + uri = value; + } else { + print_error( + node->line, + "unexpected attribute %s\n", (char*)attr->name); + return 1; + } + } + attr = attr->next; + } + + if(!prefix) { + print_error(node->line, "missing prefix attribute\n"); + return 1; + } + if(!uri) { + print_error(node->line, "missing uri attribute\n"); + return 1; + } + + DavCfgNamespace *ns = cxMalloc(config->mp->allocator, sizeof(DavCfgNamespace)); + memset(ns, 0, sizeof(DavCfgNamespace)); + ns->node = node; + ns->prefix = cx_strdup_a(config->mp->allocator, cx_str(prefix)); + ns->uri = cx_strdup_a(config->mp->allocator, cx_str(uri)); + cx_linked_list_add( + (void**)list_begin, + (void**)list_end, + offsetof(DavCfgNamespace, prev), + offsetof(DavCfgNamespace, next), + ns); + + return 0; +} + +static int load_secretstore(DavConfig *config, xmlNode *node) { + // currently only one secretstore is supported + + if(config->secretstore) { + return 1; + } + + config->secretstore = cxCalloc(config->mp->allocator, 1, sizeof(DavCfgSecretStore)); + + node = node->children; + int error = 0; + while(node) { + if(node->type == XML_ELEMENT_NODE) { + if(xstreq(node->name, "unlock-command")) { + dav_cfg_string_set_value(config, &config->secretstore->unlock_cmd, node); + } else if(xstreq(node->name, "lock-command")) { + dav_cfg_string_set_value(config, &config->secretstore->lock_cmd, node); + } + } + node = node->next; + } + + return error; +} + + + + + +DavCfgRepository* dav_config_get_repository(DavConfig *config, cxstring name) { + DavCfgRepository *repo = config->repositories; + while(repo) { + if(!cx_strcmp(cx_strcast(repo->name.value), name)) { + return repo; + } + repo = repo->next; + } + return NULL; +} + +DavCfgRepository* dav_config_url2repo(DavConfig *config, const char *url, char **path) { + cxmutstr p; + DavCfgRepository *repo = dav_config_url2repo_s(config, cx_str(url), &p); + *path = p.ptr; + return repo; +} + +DavCfgRepository* dav_config_url2repo_s(DavConfig *config, cxstring url, cxmutstr *path) { + path->ptr = NULL; + path->length = 0; + + int s; + if(cx_strprefix(url, CX_STR("http://"))) { + s = 7; + } else if(cx_strprefix(url, CX_STR("https://"))) { + s = 8; + } else { + s = 1; + } + + // split URL into repository and path + cxstring r = cx_strsubs(url, s); + cxstring p = cx_strchr(r, '/'); + r = cx_strsubsl(url, 0, url.length-p.length); + if(p.length == 0) { + p = cx_strn("/", 1); + } + + DavCfgRepository *repo = dav_config_get_repository(config, r); + if(repo) { + *path = cx_strdup(p); + } else { + // TODO: who is responsible for freeing this repository? + // how can the callee know, if he has to call free()? + repo = dav_repository_new(config); + repo->name.value = cx_strdup_a(config->mp->allocator, CX_STR("")); + if(url.ptr[url.length-1] == '/') { + repo->url.value = cx_strdup_a(config->mp->allocator, url); + *path = cx_strdup(CX_STR("/")); + } else if (cx_strchr(url, '/').length > 0) { + // TODO: fix the following workaround after + // fixing the inconsistent behavior of util_url_*() + cxstring repo_url = util_url_base_s(url); + repo->url.value = cx_strdup_a(config->mp->allocator, repo_url); + *path = cx_strdup(util_url_path_s(url)); + } else { + repo->url.value = cx_strdup(url); + *path = cx_strdup(CX_STR("/")); + } + } + + return repo; +} + +int dav_config_keytype(DavCfgKeyType type) { + switch(type) { + default: break; + case DAV_KEY_TYPE_AES256: return DAV_KEY_AES256; + case DAV_KEY_TYPE_AES128: return DAV_KEY_AES128; + } + return 0; +} + +int dav_config_register_keys(DavConfig *config, DavContext *ctx, dav_loadkeyfile_func loadkey) { + for(DavCfgKey *key=config->keys;key;key=key->next) { + char *file = cx_strdup_m(key->file.value).ptr; + cxmutstr keycontent = loadkey(file); + free(file); + + // TODO: check key length + + if(!keycontent.ptr) { + return 1; + } + + DavKey *davkey = calloc(1, sizeof(DavKey)); + davkey->name = cx_strdup_m(key->name.value).ptr; + davkey->type = dav_config_keytype(key->type); + davkey->data = keycontent.ptr; + davkey->length = keycontent.length; + + dav_context_add_key(ctx, davkey); + } + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libidav/config.h Sat Jan 27 17:50:19 2024 +0100 @@ -0,0 +1,194 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBIDAV_CONFIG_H +#define LIBIDAV_CONFIG_H + +#include "webdav.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DavConfig DavConfig; +typedef struct DavCfgRepository DavCfgRepository; +typedef struct DavCfgProxy DavCfgProxy; +typedef struct DavCfgKey DavCfgKey; +typedef struct DavCfgNamespace DavCfgNamespace; +typedef struct DavCfgSecretStore DavCfgSecretStore; + +typedef struct CfgString CfgString; +typedef struct CfgInt CfgInt; +typedef struct CfgUInt CfgUInt; +typedef struct CfgBool CfgBool; + +typedef enum dav_cfg_key_type DavCfgKeyType; + +typedef cxmutstr (*dav_loadkeyfile_func)(const char *filename); + +#define DAV_HTTP_PROXY 1 +#define DAV_HTTPS_PROXY 2 + +enum dav_cfg_key_type { + DAV_KEY_TYPE_AES256 = 0, + DAV_KEY_TYPE_AES128, + DAV_KEY_TYPE_UNKNOWN +}; + +struct DavConfig { + CxMempool *mp; + + DavCfgRepository *repositories; + DavCfgKey *keys; + DavCfgNamespace *namespaces; + DavCfgProxy *http_proxy; + DavCfgProxy *https_proxy; + DavCfgSecretStore *secretstore; + + xmlDoc *doc; +}; + +struct CfgString { + cxmutstr value; + xmlNode *node; +}; + +struct CfgInt { + int64_t value; + xmlNode *node; +}; + +struct CfgUInt { + uint64_t value; + xmlNode *node; +}; + +struct CfgBool { + bool value; + xmlNode *node; +}; + + +struct DavCfgRepository { + xmlNode *node; + + CfgString name; + CfgString url; + CfgString user; + CfgString password; + CfgString stored_user; + CfgString default_key; + CfgString cert; + CfgBool verification; + + CfgBool full_encryption; + CfgBool content_encryption; + CfgBool decrypt_content; + CfgBool decrypt_name; + CfgBool decrypt_properties; + + CfgInt ssl_version; + CfgUInt authmethods; + + int unknown_elements; + + DavCfgRepository *prev; + DavCfgRepository *next; +}; + +struct DavCfgProxy { + CfgString url; + CfgString user; + CfgString password; + CfgString noproxy; + + int unknown_elements; +}; + +struct DavCfgKey { + CfgString name; + CfgString file; + DavCfgKeyType type; + xmlNode *type_node; + + DavCfgKey *prev; + DavCfgKey *next; + + int unknown_elements; +}; + +struct DavCfgNamespace { + xmlNode *node; + cxmutstr prefix; + cxmutstr uri; + + DavCfgNamespace *prev; + DavCfgNamespace *next; +}; + +struct DavCfgSecretStore { + CfgString unlock_cmd; + CfgString lock_cmd; +}; + +enum DavConfigError { + DAV_CONFIG_ERROR_XML = 0 +}; + +DavConfig* dav_config_load(cxmutstr xmlfilecontent, int *error); + +void dav_config_free(DavConfig *config); + +CxBuffer* dav_config2buf(DavConfig *config); + +void dav_config_add_repository(DavConfig *config, DavCfgRepository *repo); + +DavCfgRepository* dav_repository_new(DavConfig *config); +void dav_repository_free(DavConfig *config, DavCfgRepository *repo); +void dav_repository_remove_and_free(DavConfig *config, DavCfgRepository *repo); +int dav_repository_get_flags(DavCfgRepository *repo); +void dav_repository_set_url(DavConfig *config, DavCfgRepository *repo, cxstring newurl); +void dav_repository_set_auth(DavConfig *config, DavCfgRepository *repo, cxstring user, cxstring password); +cxmutstr dav_repository_get_decodedpassword(DavCfgRepository *repo); + +int dav_cfg_string_set_value(DavConfig *config, CfgString *str, xmlNode *node); +void dav_cfg_bool_set_value(DavConfig *config, CfgBool *cbool, xmlNode *node); + +DavCfgRepository* dav_config_get_repository(DavConfig *config, cxstring name); +DavCfgRepository* dav_config_url2repo(DavConfig *config, const char *url, char **path); +DavCfgRepository* dav_config_url2repo_s(DavConfig *config, cxstring url, cxmutstr *path); + +int dav_config_keytype(DavCfgKeyType type); +int dav_config_register_keys(DavConfig *config, DavContext *ctx, dav_loadkeyfile_func loadkey); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBIDAV_CONFIG_H */ +
--- a/libidav/session.c Mon Jan 22 17:27:47 2024 +0100 +++ b/libidav/session.c Sat Jan 27 17:50:19 2024 +0100 @@ -114,13 +114,17 @@ return sn; } -void dav_session_set_auth(DavSession *sn, char *user, char *password) { +void dav_session_set_auth(DavSession *sn, const char *user, const char *password) { if(user && password) { - size_t ulen = strlen(user); - size_t plen = strlen(password); - size_t upwdlen = ulen + plen + 2; + dav_session_set_auth_s(sn, cx_str(user), cx_str(password)); + } +} + +void dav_session_set_auth_s(DavSession *sn, cxstring user, cxstring password) { + if(user.length > 0 && password.length > 0) { + size_t upwdlen = user.length + password.length + 2; char *upwdbuf = malloc(upwdlen); - snprintf(upwdbuf, upwdlen, "%s:%s", user, password); + snprintf(upwdbuf, upwdlen, "%.*s:%.*s", (int)user.length, user.ptr, (int)password.length, password.ptr); curl_easy_setopt(sn->handle, CURLOPT_USERPWD, upwdbuf); free(upwdbuf); }
--- a/libidav/utils.c Mon Jan 22 17:27:47 2024 +0100 +++ b/libidav/utils.c Sat Jan 27 17:50:19 2024 +0100 @@ -213,10 +213,11 @@ } int util_strtouint(const char *str, uint64_t *value) { + if (str == NULL || *str == '\0') return 0; char *end; errno = 0; uint64_t val = strtoull(str, &end, 0); - if(errno == 0) { + if(errno == 0 && *end == '\0') { *value = val; return 1; } else { @@ -225,10 +226,11 @@ } int util_strtoint(const char *str, int64_t *value) { + if (str == NULL || *str == '\0') return 0; char *end; errno = 0; int64_t val = strtoll(str, &end, 0); - if(errno == 0) { + if(errno == 0 && *end == '\0') { *value = val; return 1; } else { @@ -237,11 +239,14 @@ } int util_szstrtouint(const char *str, uint64_t *value) { + if (str == NULL || *str == '\0') return 0; char *end; errno = 0; size_t len = strlen(str); uint64_t val = strtoull(str, &end, 0); - if(end == str+len) { + if(errno != 0) { + return 0; + } if(end == str+len) { *value = val; return 1; } else if(end == str+len-1) { @@ -281,7 +286,7 @@ } } -char* util_url_base_s(cxstring url) { +cxstring util_url_base_s(cxstring url) { size_t i = 0; if(url.length > 0) { int slmax; @@ -303,12 +308,11 @@ } } } - cxstring server = cx_strsubsl(url, 0, i); - return cx_strdup(server).ptr; + return cx_strsubsl(url, 0, i); } -char* util_url_base(char *url) { - return util_url_base_s(cx_str(url)); +char* util_url_base(const char *url) { + return cx_strdup(util_url_base_s(cx_str(url))).ptr; } #ifdef _WIN32 @@ -316,30 +320,30 @@ #endif const char* util_url_path(const char *url) { - const char *path = NULL; - size_t len = strlen(url); + return util_url_path_s(cx_str(url)).ptr; +} + +cxstring util_url_path_s(cxstring url) { + cxstring path = { "", 0 }; int slashcount = 0; int slmax; - if(len > 7 && !strncasecmp(url, "http://", 7)) { + if(url.length > 7 && !strncasecmp(url.ptr, "http://", 7)) { slmax = 3; - } else if(len > 8 && !strncasecmp(url, "https://", 8)) { + } else if(url.length > 8 && !strncasecmp(url.ptr, "https://", 8)) { slmax = 3; } else { slmax = 1; } char c; - for(int i=0;i<len;i++) { - c = url[i]; + for(int i=0;i<url.length;i++) { + c = url.ptr[i]; if(c == '/') { slashcount++; if(slashcount == slmax) { - path = url + i; + path = cx_strsubs(url, i); break; } } - } - if(!path) { - path = url + len; // empty string } return path; } @@ -617,7 +621,7 @@ } char* util_concat_path(const char *url_base, const char *p) { - cxstring base = cx_str((char*)url_base); + cxstring base = cx_str(url_base); cxstring path; if(p) { path = cx_str((char*)p); @@ -625,6 +629,14 @@ path = CX_STR(""); } + return util_concat_path_s(base, path).ptr; +} + +cxmutstr util_concat_path_s(cxstring base, cxstring path) { + if(!path.ptr) { + path = CX_STR(""); + } + int add_separator = 0; if(base.length != 0 && base.ptr[base.length-1] == '/') { if(path.ptr[0] == '/') { @@ -643,7 +655,7 @@ url = cx_strcat(2, base, path); } - return url.ptr; + return url; } char* util_get_url(DavSession *sn, const char *href) {
--- a/libidav/utils.h Mon Jan 22 17:27:47 2024 +0100 +++ b/libidav/utils.h Sat Jan 27 17:50:19 2024 +0100 @@ -72,12 +72,14 @@ int util_mkdir(char *path, mode_t mode); -char* util_url_base(char *url); -char* util_url_base_s(cxstring url); +char* util_url_base(const char *url); +cxstring util_url_base_s(cxstring url); const char* util_url_path(const char *url); +cxstring util_url_path_s(cxstring url); char* util_url_decode(DavSession *sn, const char *url); const char* util_resource_name(const char *url); char* util_concat_path(const char *url_base, const char *path); +cxmutstr util_concat_path_s(cxstring url_base, cxstring path); char* util_get_url(DavSession *sn, const char *href); void util_set_url(DavSession *sn, const char *href);
--- a/libidav/webdav.h Mon Jan 22 17:27:47 2024 +0100 +++ b/libidav/webdav.h Sat Jan 27 17:50:19 2024 +0100 @@ -30,6 +30,7 @@ #define WEBDAV_H #include <inttypes.h> +#include <stdbool.h> #include <cx/map.h> #include <cx/mempool.h> #include <cx/linked_list.h> @@ -267,7 +268,8 @@ char *base_url, char *user, char *password); -void dav_session_set_auth(DavSession *sn, char *user, char *password); +void dav_session_set_auth(DavSession *sn, const char *user, const char *password); +void dav_session_set_auth_s(DavSession *sn, cxstring user, cxstring password); void dav_session_set_baseurl(DavSession *sn, char *base_url); void dav_session_enable_encryption(DavSession *sn, DavKey *key, int flags);
--- a/make/vs/idav/idav.vcxproj Mon Jan 22 17:27:47 2024 +0100 +++ b/make/vs/idav/idav.vcxproj Sat Jan 27 17:50:19 2024 +0100 @@ -115,7 +115,8 @@ <SDLCheck>true</SDLCheck> <PreprocessorDefinitions>_DEBUG;_CONSOLE;UI_WINUI;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ConformanceMode>true</ConformanceMode> - <AdditionalIncludeDirectories>..\..\..\ucx;..\vcpkg_installed\x64-windows\x64-windows\include;..\..\..\ui\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>..\..\..\ucx;..\vcpkg_installed\x64-windows\x64-windows\include;..\..\..\ui\;..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <LanguageStandard_C>stdc17</LanguageStandard_C> </ClCompile> <Link> <SubSystem>Windows</SubSystem> @@ -139,9 +140,6 @@ </Link> </ItemDefinitionGroup> <ItemGroup> - <ClCompile Include="main.c" /> - </ItemGroup> - <ItemGroup> <None Include="packages.config" /> </ItemGroup> <ItemGroup> @@ -156,7 +154,13 @@ <Manifest Include="app.manifest" /> </ItemGroup> <ItemGroup> - <ClInclude Include="main.h" /> + <ClCompile Include="..\..\..\application\application.c" /> + <ClCompile Include="..\..\..\application\main.c" /> + <ClCompile Include="..\..\..\application\window.c" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\..\application\application.h" /> + <ClInclude Include="..\..\..\application\window.h" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets">
--- a/make/vs/idav/idav.vcxproj.filters Mon Jan 22 17:27:47 2024 +0100 +++ b/make/vs/idav/idav.vcxproj.filters Sat Jan 27 17:50:19 2024 +0100 @@ -1,18 +1,13 @@ <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> - <Filter Include="Quelldateien"> - <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> - <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions> - </Filter> - <Filter Include="Headerdateien"> - <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> - <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions> - </Filter> <Filter Include="Ressourcendateien"> <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> </Filter> + <Filter Include="src"> + <UniqueIdentifier>{46525c70-7cfc-45fe-ae78-54123ad9bd7e}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" /> @@ -21,16 +16,25 @@ <Manifest Include="app.manifest" /> </ItemGroup> <ItemGroup> - <ClCompile Include="main.c"> - <Filter>Quelldateien</Filter> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\..\application\application.c"> + <Filter>src</Filter> + </ClCompile> + <ClCompile Include="..\..\..\application\main.c"> + <Filter>src</Filter> + </ClCompile> + <ClCompile Include="..\..\..\application\window.c"> + <Filter>src</Filter> </ClCompile> </ItemGroup> <ItemGroup> - <ClInclude Include="main.h"> - <Filter>Headerdateien</Filter> + <ClInclude Include="..\..\..\application\application.h"> + <Filter>src</Filter> + </ClInclude> + <ClInclude Include="..\..\..\application\window.h"> + <Filter>src</Filter> </ClInclude> </ItemGroup> - <ItemGroup> - <None Include="packages.config" /> - </ItemGroup> </Project> \ No newline at end of file
--- a/make/vs/idav/main.c Mon Jan 22 17:27:47 2024 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -#ifdef _WIN32 -#include <Windows.h> -#endif - -#include <ui/ui.h> - -int idav_main(void) { - return 0; -} - -#ifndef _WIN32 -int main(int argc, char** argv) { - return idav_main(); -} -#else -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { - return idav_main(); -} -#endif -
--- a/make/vs/idav/main.h Mon Jan 22 17:27:47 2024 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -#pragma once -
--- a/make/vs/libidav/libidav.vcxproj Mon Jan 22 17:27:47 2024 +0100 +++ b/make/vs/libidav/libidav.vcxproj Sat Jan 27 17:50:19 2024 +0100 @@ -109,7 +109,7 @@ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> <WarningLevel>Level3</WarningLevel> - <SDLCheck>true</SDLCheck> + <SDLCheck>false</SDLCheck> <PreprocessorDefinitions>_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ConformanceMode>true</ConformanceMode> <LanguageStandard_C>stdc11</LanguageStandard_C> @@ -144,6 +144,7 @@ </Link> </ItemDefinitionGroup> <ItemGroup> + <ClCompile Include="..\..\..\libidav\config.c" /> <ClCompile Include="..\..\..\libidav\crypto.c" /> <ClCompile Include="..\..\..\libidav\davqlexec.c" /> <ClCompile Include="..\..\..\libidav\davqlparser.c" /> @@ -156,6 +157,7 @@ <ClCompile Include="..\..\..\libidav\xml.c" /> </ItemGroup> <ItemGroup> + <ClInclude Include="..\..\..\libidav\config.h" /> <ClInclude Include="..\..\..\libidav\crypto.h" /> <ClInclude Include="..\..\..\libidav\davqlexec.h" /> <ClInclude Include="..\..\..\libidav\davqlparser.h" />
--- a/make/vs/libidav/libidav.vcxproj.filters Mon Jan 22 17:27:47 2024 +0100 +++ b/make/vs/libidav/libidav.vcxproj.filters Sat Jan 27 17:50:19 2024 +0100 @@ -45,6 +45,9 @@ <ClCompile Include="..\..\..\libidav\xml.c"> <Filter>Quelldateien</Filter> </ClCompile> + <ClCompile Include="..\..\..\libidav\config.c"> + <Filter>Quelldateien</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\libidav\crypto.h"> @@ -77,5 +80,8 @@ <ClInclude Include="..\..\..\libidav\xml.h"> <Filter>Headerdateien</Filter> </ClInclude> + <ClInclude Include="..\..\..\libidav\config.h"> + <Filter>Quelldateien</Filter> + </ClInclude> </ItemGroup> </Project> \ No newline at end of file
--- a/ucx/array_list.c Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/array_list.c Sat Jan 27 17:50:19 2024 +0100 @@ -27,12 +27,30 @@ */ #include "cx/array_list.h" +#include "cx/compare.h" #include <assert.h> #include <string.h> +// Default array reallocator + +static void *cx_array_default_realloc( + void *array, + size_t capacity, + size_t elem_size, + __attribute__((__unused__)) struct cx_array_reallocator_s *alloc +) { + return realloc(array, capacity * elem_size); +} + +struct cx_array_reallocator_s cx_array_default_reallocator_impl = { + cx_array_default_realloc, NULL, NULL, 0, 0 +}; + +struct cx_array_reallocator_s *cx_array_default_reallocator = &cx_array_default_reallocator_impl; + // LOW LEVEL ARRAY LIST FUNCTIONS -enum cx_array_copy_result cx_array_copy( +enum cx_array_result cx_array_copy( void **target, size_t *size, size_t *capacity, @@ -59,7 +77,7 @@ if (needrealloc) { // a reallocator and a capacity variable must be available if (reallocator == NULL || capacity == NULL) { - return CX_ARRAY_COPY_REALLOC_NOT_SUPPORTED; + return CX_ARRAY_REALLOC_NOT_SUPPORTED; } // check, if we need to repair the src pointer @@ -77,7 +95,7 @@ *target, cap, elem_size, reallocator ); if (newmem == NULL) { - return CX_ARRAY_COPY_REALLOC_FAILED; + return CX_ARRAY_REALLOC_FAILED; } // repair src pointer, if necessary @@ -99,12 +117,13 @@ *size = newsize; // return successfully - return CX_ARRAY_COPY_SUCCESS; + return CX_ARRAY_SUCCESS; } #ifndef CX_ARRAY_SWAP_SBO_SIZE #define CX_ARRAY_SWAP_SBO_SIZE 128 #endif +unsigned cx_array_swap_sbo_size = CX_ARRAY_SWAP_SBO_SIZE; void cx_array_swap( void *arr, @@ -208,7 +227,7 @@ size_t elems_to_move = list->size - index; size_t start_of_moved = index + n; - if (CX_ARRAY_COPY_SUCCESS != cx_array_copy( + if (CX_ARRAY_SUCCESS != cx_array_copy( &arl->data, &list->size, &arl->capacity, @@ -228,7 +247,7 @@ // therefore, it is impossible to leave this function with an invalid array // place the new elements - if (CX_ARRAY_COPY_SUCCESS == cx_array_copy( + if (CX_ARRAY_SUCCESS == cx_array_copy( &arl->data, &list->size, &arl->capacity, @@ -308,11 +327,14 @@ list->size - index - 1, &arl->reallocator ); - if (result == 0) { - // decrease the size - list->size--; - } - return result; + + // cx_array_copy cannot fail, array cannot grow + assert(result == 0); + + // decrease the size + list->size--; + + return 0; } static void cx_arl_clear(struct cx_list_s *list) { @@ -362,9 +384,10 @@ } } -static ssize_t cx_arl_find( - struct cx_list_s const *list, - void const *elem +static ssize_t cx_arl_find_remove( + struct cx_list_s *list, + void const *elem, + bool remove ) { assert(list->cmpfunc != NULL); assert(list->size < SIZE_MAX / 2); @@ -372,7 +395,15 @@ for (ssize_t i = 0; i < (ssize_t) list->size; i++) { if (0 == list->cmpfunc(elem, cur)) { - return i; + if (remove) { + if (0 == cx_arl_remove(list, i)) { + return i; + } else { + return -1; + } + } else { + return i; + } } cur += list->item_size; } @@ -500,7 +531,7 @@ cx_arl_clear, cx_arl_swap, cx_arl_at, - cx_arl_find, + cx_arl_find_remove, cx_arl_sort, cx_arl_compare, cx_arl_reverse, @@ -522,13 +553,14 @@ list->base.cl = &cx_array_list_class; list->base.allocator = allocator; - list->base.cmpfunc = comparator; list->capacity = initial_capacity; if (item_size > 0) { list->base.item_size = item_size; + list->base.cmpfunc = comparator; } else { item_size = sizeof(void *); + list->base.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; cxListStorePointers((CxList *) list); }
--- a/ucx/buffer.c Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/buffer.c Sat Jan 27 17:50:19 2024 +0100 @@ -135,6 +135,11 @@ buffer->pos = 0; } +void cxBufferReset(CxBuffer *buffer) { + buffer->size = 0; + buffer->pos = 0; +} + int cxBufferEof(CxBuffer const *buffer) { return buffer->pos >= buffer->size; }
--- a/ucx/compare.c Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/compare.c Sat Jan 27 17:50:19 2024 +0100 @@ -199,3 +199,15 @@ } } +int cx_cmp_ptr( + void const *ptr1, + void const *ptr2 +) { + uintptr_t p1 = (uintptr_t) ptr1; + uintptr_t p2 = (uintptr_t) ptr2; + if (p1 == p2) { + return 0; + } else { + return p1 < p2 ? -1 : 1; + } +}
--- a/ucx/cx/array_list.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/array_list.h Sat Jan 27 17:50:19 2024 +0100 @@ -31,7 +31,6 @@ * \details Also provides several low-level functions for custom array list implementations. * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */ @@ -46,6 +45,11 @@ #endif /** + * The maximum item size in an array list that fits into stack buffer when swapped. + */ +extern unsigned cx_array_swap_sbo_size; + +/** * Defines a reallocation mechanism for arrays. */ struct cx_array_reallocator_s { @@ -54,8 +58,9 @@ * * Implementations are not required to free the original array. * This allows re-allocation of static memory by allocating heap memory - * and copying the array contents. The information in \p data can keep - * track of the state of the memory or other additional allocator info. + * and copying the array contents. The information in the custom fields of + * the referenced allocator can be used to track the state of the memory + * or to transport other additional data. * * @param array the array to reallocate * @param capacity the new capacity (number of elements) @@ -89,12 +94,17 @@ }; /** - * Return codes for cx_array_copy(). + * A default stdlib-based array reallocator. */ -enum cx_array_copy_result { - CX_ARRAY_COPY_SUCCESS, - CX_ARRAY_COPY_REALLOC_NOT_SUPPORTED, - CX_ARRAY_COPY_REALLOC_FAILED, +extern struct cx_array_reallocator_s *cx_array_default_reallocator; + +/** + * Return codes for array functions. + */ +enum cx_array_result { + CX_ARRAY_SUCCESS, + CX_ARRAY_REALLOC_NOT_SUPPORTED, + CX_ARRAY_REALLOC_FAILED, }; /** @@ -107,7 +117,7 @@ * capacity is used. * * If the capacity is insufficient to hold the new data, a reallocation - * attempt is made, unless the allocator is set to \c NULL, in which case + * attempt is made, unless the \p reallocator is set to \c NULL, in which case * this function ultimately returns a failure. * * @param target the target array @@ -122,7 +132,7 @@ * if re-allocation shall not happen * @return zero on success, non-zero error code on failure */ -enum cx_array_copy_result cx_array_copy( +enum cx_array_result cx_array_copy( void **target, size_t *size, size_t *capacity, @@ -133,6 +143,28 @@ struct cx_array_reallocator_s *reallocator ) __attribute__((__nonnull__(1, 2, 5))); +/** + * Adds an element to an array with the possibility of allocating more space. + * + * The element \p elem is added to the end of the \p target array which containing + * \p size elements, already. The \p capacity must not be \c NULL and point a + * variable holding the current maximum number of elements the array can hold. + * + * If the capacity is insufficient to hold the new element, and the optional + * \p reallocator is not \c NULL, an attempt increase the \p capacity is made + * and the new capacity is written back. + * + * @param target the target array + * @param size a pointer to the size of the target array + * @param capacity a pointer to the target array's capacity - must not be \c NULL + * @param elem_size the size of one element + * @param elem the element to add + * @param reallocator the array re-allocator to use, or \c NULL + * if re-allocation shall not happen + * @return zero on success, non-zero error code on failure + */ +#define cx_array_add(target, size, capacity, elem_size, elem, reallocator) \ + cx_array_copy((void**)(target), size, capacity, *(size), elem, elem_size, 1, reallocator) /** * Swaps two array elements. @@ -153,12 +185,14 @@ * Allocates an array list for storing elements with \p item_size bytes each. * * If \p item_size is CX_STORE_POINTERS, the created list will be created as if - * cxListStorePointers() was called immediately after creation. + * cxListStorePointers() was called immediately after creation and the compare + * function will be automatically set to cx_cmp_ptr(), if none is given. * * @param allocator the allocator for allocating the list memory * (if \c NULL the cxDefaultAllocator will be used) * @param comparator the comparator for the elements - * (if \c NULL sort and find functions will not work) + * (if \c NULL, and the list is not storing pointers, sort and find + * functions will not work) * @param item_size the size of each element in bytes * @param initial_capacity the initial number of elements the array can store * @return the created list @@ -178,7 +212,8 @@ * set it immediately after creation or use cxArrayListCreate(). * * If \p item_size is CX_STORE_POINTERS, the created list will be created as if - * cxListStorePointers() was called immediately after creation. + * cxListStorePointers() was called immediately after creation and the compare + * function will be automatically set to cx_cmp_ptr(). * * @param item_size the size of each element in bytes * @param initial_capacity the initial number of elements the array can store
--- a/ucx/cx/buffer.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/buffer.h Sat Jan 27 17:50:19 2024 +0100 @@ -40,7 +40,6 @@ * * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */ @@ -312,18 +311,32 @@ * Clears the buffer by resetting the position and deleting the data. * * The data is deleted by zeroing it with a call to memset(). + * If you do not need that, you can use the faster cxBufferReset(). * * @param buffer the buffer to be cleared + * @see cxBufferReset() */ __attribute__((__nonnull__)) void cxBufferClear(CxBuffer *buffer); /** - * Tests, if the buffer position has exceeded the buffer capacity. + * Resets the buffer by resetting the position and size to zero. + * + * The data in the buffer is not deleted. If you need a safe + * reset of the buffer, use cxBufferClear(). + * + * @param buffer the buffer to be cleared + * @see cxBufferClear() + */ +__attribute__((__nonnull__)) +void cxBufferReset(CxBuffer *buffer); + +/** + * Tests, if the buffer position has exceeded the buffer size. * * @param buffer the buffer to test * @return non-zero, if the current buffer position has exceeded the last - * available byte of the buffer. + * byte of the buffer's contents. */ __attribute__((__nonnull__)) int cxBufferEof(CxBuffer const *buffer);
--- a/ucx/cx/collection.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/collection.h Sat Jan 27 17:50:19 2024 +0100 @@ -30,7 +30,6 @@ * \brief Common definitions for various collection implementations. * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */ @@ -49,6 +48,8 @@ */ #define CX_STORE_POINTERS 0 +#ifndef CX_COMPARE_FUNC_DEFINED +#define CX_COMPARE_FUNC_DEFINED /** * A comparator function comparing two collection elements. */ @@ -56,6 +57,7 @@ void const *left, void const *right ); +#endif // CX_COMPARE_FUNC_DEFINED /** * Use this macro to declare common members for a collection structure.
--- a/ucx/cx/common.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/common.h Sat Jan 27 17:50:19 2024 +0100 @@ -33,7 +33,6 @@ * * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License * * \mainpage UAP Common Extensions @@ -84,7 +83,7 @@ #define UCX_VERSION_MAJOR 3 /** Minor UCX version as integer constant. */ -#define UCX_VERSION_MINOR 0 +#define UCX_VERSION_MINOR 1 /** Version constant which ensures to increase monotonically. */ #define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR) @@ -97,6 +96,7 @@ #include <stdint.h> #include <sys/types.h> +#ifndef UCX_TEST_H /** * Function pointer compatible with fwrite-like functions. */ @@ -106,6 +106,7 @@ size_t, void * ); +#endif // UCX_TEST_H /** * Function pointer compatible with fread-like functions.
--- a/ucx/cx/compare.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/compare.h Sat Jan 27 17:50:19 2024 +0100 @@ -30,7 +30,6 @@ * \brief A collection of simple compare functions. * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */ @@ -43,6 +42,17 @@ extern "C" { #endif +#ifndef CX_COMPARE_FUNC_DEFINED +#define CX_COMPARE_FUNC_DEFINED +/** + * A comparator function comparing two collection elements. + */ +typedef int(*cx_compare_func)( + void const *left, + void const *right +); +#endif // CX_COMPARE_FUNC_DEFINED + /** * Compares two integers of type int. * @@ -213,6 +223,19 @@ void const *ptr2 ); +/** + * Compares the pointers specified in the arguments without de-referencing. + * + * @param ptr1 pointer one + * @param ptr2 pointer two + * @return -1 if ptr1 is less than ptr2, 0 if both are equal, + * 1 if ptr1 is greater than ptr2 + */ +int cx_cmp_ptr( + void const *ptr1, + void const *ptr2 +); + #ifdef __cplusplus } // extern "C" #endif
--- a/ucx/cx/hash_key.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/hash_key.h Sat Jan 27 17:50:19 2024 +0100 @@ -30,7 +30,6 @@ * \brief Interface for map implementations. * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */
--- a/ucx/cx/hash_map.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/hash_map.h Sat Jan 27 17:50:19 2024 +0100 @@ -30,7 +30,6 @@ * \brief Hash map implementation. * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */
--- a/ucx/cx/iterator.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/iterator.h Sat Jan 27 17:50:19 2024 +0100 @@ -30,7 +30,6 @@ * \brief Interface for iterator implementations. * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */
--- a/ucx/cx/linked_list.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/linked_list.h Sat Jan 27 17:50:19 2024 +0100 @@ -31,7 +31,6 @@ * \details Also provides several low-level functions for custom linked list implementations. * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */ @@ -46,21 +45,22 @@ #endif /** - * Set this flag to true, if you want to disable the use of SBO for - * linked list swap operations. + * The maximum item size that uses SBO swap instead of relinking. */ -extern bool CX_DISABLE_LINKED_LIST_SWAP_SBO; +extern unsigned cx_linked_list_swap_sbo_size; /** * Allocates a linked list for storing elements with \p item_size bytes each. * * If \p item_size is CX_STORE_POINTERS, the created list will be created as if - * cxListStorePointers() was called immediately after creation. + * cxListStorePointers() was called immediately after creation and the compare + * function will be automatically set to cx_cmp_ptr(), if none is given. * * @param allocator the allocator for allocating the list nodes * (if \c NULL the cxDefaultAllocator will be used) * @param comparator the comparator for the elements - * (if \c NULL sort and find functions will not work) + * (if \c NULL, and the list is not storing pointers, sort and find + * functions will not work) * @param item_size the size of each element in bytes * @return the created list */ @@ -78,7 +78,8 @@ * after list creation or use cxLinkedListCreate(). * * If \p item_size is CX_STORE_POINTERS, the created list will be created as if - * cxListStorePointers() was called immediately after creation. + * cxListStorePointers() was called immediately after creation and the compare + * function will be automatically set to cx_cmp_ptr(). * * @param item_size the size of each element in bytes * @return the created list @@ -129,6 +130,27 @@ ) __attribute__((__nonnull__)); /** + * Finds the node containing an element within a linked list. + * + * @param result a pointer to the memory where the node pointer (or \c NULL if the element + * could not be found) shall be stored to + * @param start a pointer to the start node + * @param loc_advance the location of the pointer to advance + * @param loc_data the location of the \c data pointer within your node struct + * @param cmp_func a compare function to compare \p elem against the node data + * @param elem a pointer to the element to find + * @return the index of the element or a negative value if it could not be found + */ +ssize_t cx_linked_list_find_node( + void **result, + void const *start, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func cmp_func, + void const *elem +) __attribute__((__nonnull__)); + +/** * Finds the first node in a linked list. * * The function starts with the pointer denoted by \p node and traverses the list
--- a/ucx/cx/list.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/list.h Sat Jan 27 17:50:19 2024 +0100 @@ -30,7 +30,6 @@ * \brief Interface for list implementations. * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */ @@ -137,11 +136,12 @@ ); /** - * Member function for finding an element. + * Member function for finding and optionally removing an element. */ - ssize_t (*find)( - struct cx_list_s const *list, - void const *elem + ssize_t (*find_remove)( + struct cx_list_s *list, + void const *elem, + bool remove ); /** @@ -580,7 +580,25 @@ CxList const *list, void const *elem ) { - return list->cl->find(list, elem); + return list->cl->find_remove((CxList*)list, elem, false); +} + +/** + * Removes and returns the index of the first element that equals \p elem. + * + * Determining equality is performed by the list's comparator function. + * + * @param list the list + * @param elem the element to find and remove + * @return the index of the now removed element or a negative + * value when the element is not found or could not be removed + */ +__attribute__((__nonnull__)) +static inline ssize_t cxListFindRemove( + CxList *list, + void const *elem +) { + return list->cl->find_remove(list, elem, true); } /**
--- a/ucx/cx/map.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/map.h Sat Jan 27 17:50:19 2024 +0100 @@ -30,7 +30,6 @@ * \brief Interface for map implementations. * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */
--- a/ucx/cx/mempool.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/mempool.h Sat Jan 27 17:50:19 2024 +0100 @@ -30,7 +30,6 @@ * \brief Interface for memory pool implementations. * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */
--- a/ucx/cx/printf.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/printf.h Sat Jan 27 17:50:19 2024 +0100 @@ -30,7 +30,6 @@ * \brief Wrapper for write functions with a printf-like interface. * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */ @@ -45,6 +44,12 @@ extern "C" { #endif + +/** + * The maximum string length that fits into stack memory. + */ +extern unsigned const cx_printf_sbo_size; + /** * A \c fprintf like function which writes the output to a stream by * using a write_func. @@ -159,6 +164,170 @@ #define cx_bprintf(buffer, fmt, ...) cx_fprintf((CxBuffer*)buffer, \ (cx_write_func) cxBufferWrite, fmt, __VA_ARGS__) + +/** + * An \c sprintf like function which reallocates the string when the buffer is not large enough. + * + * \note The resulting string is guaranteed to be zero-terminated. + * That means, when the buffer needed to be reallocated, the new size of the buffer will be + * the length returned by this function plus one. + * + * @param str a pointer to the string buffer + * @param len the current length of the buffer + * @param fmt the format string + * @param ... additional arguments + * @return the length of produced string + */ +#define cx_sprintf(str, len, fmt, ...) cx_sprintf_a(cxDefaultAllocator, str, len, fmt, __VA_ARGS__) + +/** + * An \c sprintf like function which reallocates the string when the buffer is not large enough. + * + * \note The resulting string is guaranteed to be zero-terminated. + * That means, when the buffer needed to be reallocated, the new size of the buffer will be + * the length returned by this function plus one. + * + * \attention The original buffer MUST have been allocated with the same allocator! + * + * @param alloc the allocator to use + * @param str a pointer to the string buffer + * @param len the current length of the buffer + * @param fmt the format string + * @param ... additional arguments + * @return the length of produced string + */ +__attribute__((__nonnull__(1, 2, 4), __format__(printf, 4, 5))) +int cx_sprintf_a(CxAllocator *alloc, char **str, size_t len, const char *fmt, ... ); + + +/** + * An \c sprintf like function which reallocates the string when the buffer is not large enough. + * + * \note The resulting string is guaranteed to be zero-terminated. + * That means, when the buffer needed to be reallocated, the new size of the buffer will be + * the length returned by this function plus one. + * + * @param str a pointer to the string buffer + * @param len the current length of the buffer + * @param fmt the format string + * @param ap argument list + * @return the length of produced string + */ +#define cx_vsprintf(str, len, fmt, ap) cx_vsprintf_a(cxDefaultAllocator, str, len, fmt, ap) + +/** + * An \c sprintf like function which reallocates the string when the buffer is not large enough. + * + * \note The resulting string is guaranteed to be zero-terminated. + * That means, when the buffer needed to be reallocated, the new size of the buffer will be + * the length returned by this function plus one. + * + * \attention The original buffer MUST have been allocated with the same allocator! + * + * @param alloc the allocator to use + * @param str a pointer to the string buffer + * @param len the current length of the buffer + * @param fmt the format string + * @param ap argument list + * @return the length of produced string + */ +__attribute__((__nonnull__)) +int cx_vsprintf_a(CxAllocator *alloc, char **str, size_t len, const char *fmt, va_list ap); + + +/** + * An \c sprintf like function which allocates a new string when the buffer is not large enough. + * + * The location of the resulting string will \em always be stored to \p str. When the buffer + * was sufficiently large, \p buf itself will be stored to the location of \p str. + * + * \note The resulting string is guaranteed to be zero-terminated. + * That means, when the buffer needed to be reallocated, the new size of the buffer will be + * the length returned by this function plus one. + * + * \remark When a new string needed to be allocated, the contents of \p buf will be + * poisoned after the call, because this function tries to produce the string in \p buf, first. + * + * @param buf a pointer to the buffer + * @param len the length of the buffer + * @param str a pointer to the location + * @param fmt the format string + * @param ... additional arguments + * @return the length of produced string + */ +#define cx_sprintf_s(buf, len, str, fmt, ...) cx_sprintf_sa(cxDefaultAllocator, buf, len, str, fmt, __VA_ARGS__) + +/** + * An \c sprintf like function which allocates a new string when the buffer is not large enough. + * + * The location of the resulting string will \em always be stored to \p str. When the buffer + * was sufficiently large, \p buf itself will be stored to the location of \p str. + * + * \note The resulting string is guaranteed to be zero-terminated. + * That means, when the buffer needed to be reallocated, the new size of the buffer will be + * the length returned by this function plus one. + * + * \remark When a new string needed to be allocated, the contents of \p buf will be + * poisoned after the call, because this function tries to produce the string in \p buf, first. + * + * @param alloc the allocator to use + * @param buf a pointer to the buffer + * @param len the length of the buffer + * @param str a pointer to the location + * @param fmt the format string + * @param ... additional arguments + * @return the length of produced string + */ +__attribute__((__nonnull__(1, 2, 4, 5), __format__(printf, 5, 6))) +int cx_sprintf_sa(CxAllocator *alloc, char *buf, size_t len, char **str, const char *fmt, ... ); + +/** + * An \c sprintf like function which allocates a new string when the buffer is not large enough. + * + * The location of the resulting string will \em always be stored to \p str. When the buffer + * was sufficiently large, \p buf itself will be stored to the location of \p str. + * + * \note The resulting string is guaranteed to be zero-terminated. + * That means, when the buffer needed to be reallocated, the new size of the buffer will be + * the length returned by this function plus one. + * + * \remark When a new string needed to be allocated, the contents of \p buf will be + * poisoned after the call, because this function tries to produce the string in \p buf, first. + * + * @param buf a pointer to the buffer + * @param len the length of the buffer + * @param str a pointer to the location + * @param fmt the format string + * @param ap argument list + * @return the length of produced string + */ +#define cx_vsprintf_s(buf, len, str, fmt, ap) cx_vsprintf_sa(cxDefaultAllocator, buf, len, str, fmt, ap) + +/** + * An \c sprintf like function which allocates a new string when the buffer is not large enough. + * + * The location of the resulting string will \em always be stored to \p str. When the buffer + * was sufficiently large, \p buf itself will be stored to the location of \p str. + * + * \note The resulting string is guaranteed to be zero-terminated. + * That means, when the buffer needed to be reallocated, the new size of the buffer will be + * the length returned by this function plus one. + * + * \remark When a new string needed to be allocated, the contents of \p buf will be + * poisoned after the call, because this function tries to produce the string in \p buf, first. + * + * @param alloc the allocator to use + * @param buf a pointer to the buffer + * @param len the length of the buffer + * @param str a pointer to the location + * @param fmt the format string + * @param ap argument list + * @return the length of produced string + */ +__attribute__((__nonnull__)) +int cx_vsprintf_sa(CxAllocator *alloc, char *buf, size_t len, char **str, const char *fmt, va_list ap); + + #ifdef __cplusplus } // extern "C" #endif
--- a/ucx/cx/string.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/string.h Sat Jan 27 17:50:19 2024 +0100 @@ -30,7 +30,6 @@ * \brief Strings that know their length. * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */ @@ -41,6 +40,11 @@ #include "allocator.h" /** + * The maximum length of the "needle" in cx_strstr() that can use SBO. + */ +extern unsigned const cx_strstr_sbo_size; + +/** * The UCX string structure. */ struct cx_mutstr_s {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/test.h Sat Jan 27 17:50:19 2024 +0100 @@ -0,0 +1,330 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file: test.h + * + * UCX Test Framework. + * + * Usage of this test framework: + * + * **** IN HEADER FILE: **** + * + * <pre> + * CX_TEST(function_name); + * CX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional + * </pre> + * + * **** IN SOURCE FILE: **** + * <pre> + * CX_TEST_SUBROUTINE(subroutine_name, paramlist) { + * // tests with CX_TEST_ASSERT() + * } + * + * CX_TEST(function_name) { + * // memory allocation and other stuff here + * #CX_TEST_DO { + * // tests with CX_TEST_ASSERT() and/or + * // calls with CX_TEST_CALL_SUBROUTINE() here + * } + * // cleanup of memory here + * } + * </pre> + * + * @attention Do not call own functions within a test, that use + * CX_TEST_ASSERT() macros and are not defined by using CX_TEST_SUBROUTINE(). + * + * @author Mike Becker + * @author Olaf Wintermann + * + */ + +#ifndef UCX_TEST_H +#define UCX_TEST_H + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <setjmp.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __FUNCTION__ +/** + * Alias for the <code>__func__</code> preprocessor macro. + * Some compilers use <code>__func__</code> and others use __FUNCTION__. + * We use __FUNCTION__ so we define it for those compilers which use + * <code>__func__</code>. + */ +#define __FUNCTION__ __func__ +#endif + +// +#if !defined(__clang__) && __GNUC__ > 3 +#pragma GCC diagnostic ignored "-Wclobbered" +#endif + +#ifndef UCX_COMMON_H +/** + * Function pointer compatible with fwrite-like functions. + */ +typedef size_t (*cx_write_func)( + void const *, + size_t, + size_t, + void * +); +#endif // UCX_COMMON_H + +/** Type for the CxTestSuite. */ +typedef struct CxTestSuite CxTestSuite; + +/** Pointer to a test function. */ +typedef void(*CxTest)(CxTestSuite *, void *, cx_write_func); + +/** Type for the internal list of test cases. */ +typedef struct CxTestSet CxTestSet; + +/** Structure for the internal list of test cases. */ +struct CxTestSet { + + /** Test case. */ + CxTest test; + + /** Pointer to the next list element. */ + CxTestSet *next; +}; + +/** + * A test suite containing multiple test cases. + */ +struct CxTestSuite { + + /** The number of successful tests after the suite has been run. */ + unsigned int success; + + /** The number of failed tests after the suite has been run. */ + unsigned int failure; + + /** The optional name of this test suite. */ + char const *name; + + /** + * Internal list of test cases. + * Use cx_test_register() to add tests to this list. + */ + CxTestSet *tests; +}; + +/** + * Creates a new test suite. + * @param name optional name of the suite + * @return a new test suite + */ +static inline CxTestSuite* cx_test_suite_new(char const *name) { + CxTestSuite* suite = (CxTestSuite*) malloc(sizeof(CxTestSuite)); + if (suite != NULL) { + suite->name = name; + suite->success = 0; + suite->failure = 0; + suite->tests = NULL; + } + + return suite; +} + +/** + * Destroys a test suite. + * @param suite the test suite to destroy + */ +static inline void cx_test_suite_free(CxTestSuite* suite) { + CxTestSet *l = suite->tests; + while (l != NULL) { + CxTestSet *e = l; + l = l->next; + free(e); + } + free(suite); +} + +/** + * Registers a test function with the specified test suite. + * + * @param suite the suite, the test function shall be added to + * @param test the test function to register + * @return zero on success or non-zero on failure + */ +static inline int cx_test_register(CxTestSuite* suite, CxTest test) { + CxTestSet *t = (CxTestSet*) malloc(sizeof(CxTestSet)); + if (t) { + t->test = test; + t->next = NULL; + if (suite->tests == NULL) { + suite->tests = t; + } else { + CxTestSet *last = suite->tests; + while (last->next) { + last = last->next; + } + last->next = t; + } + return 0; + } else { + return 1; + } +} + +/** + * Runs a test suite and writes the test log to the specified stream. + * @param suite the test suite to run + * @param out_target the target buffer or file to write the output to + * @param out_writer the write function writing to \p out_target + */ +static inline void cx_test_run(CxTestSuite *suite, + void *out_target, cx_write_func out_writer) { + if (suite->name == NULL) { + out_writer("*** Test Suite ***\n", 1, 19, out_target); + } else { + out_writer("*** Test Suite : ", 1, 17, out_target); + out_writer(suite->name, 1, strlen(suite->name), out_target); + out_writer(" ***\n", 1, 5, out_target); + } + suite->success = 0; + suite->failure = 0; + for (CxTestSet *elem = suite->tests; elem; elem = elem->next) { + elem->test(suite, out_target, out_writer); + } + out_writer("\nAll test completed.\n", 1, 21, out_target); + char total[80]; + int len = snprintf( + total, 80, + " Total: %u\n Success: %u\n Failure: %u\n\n", + suite->success + suite->failure, suite->success, suite->failure + ); + out_writer(total, 1, len, out_target); +} + +/** + * Runs a test suite and writes the test log to the specified FILE stream. + * @param suite the test suite to run + * @param file the target file to write the output to + */ +#define cx_test_run_f(suite, file) cx_test_run(suite, (void*)file, (cx_write_func)fwrite) + +/** + * Runs a test suite and writes the test log to stdout. + * @param suite the test suite to run + */ +#define cx_test_run_stdout(suite) cx_test_run_f(suite, stdout) + +/** + * Macro for a #CxTest function header. + * + * Use this macro to declare and/or define a #CxTest function. + * + * @param name the name of the test function + */ +#define CX_TEST(name) void name(CxTestSuite* _suite_,void *_output_, cx_write_func _writefnc_) + +/** + * Defines the scope of a test. + * @attention Any CX_TEST_ASSERT() calls must be performed in scope of + * #CX_TEST_DO. + */ +#define CX_TEST_DO _writefnc_("Running ", 1, 8, _output_);\ + _writefnc_(__FUNCTION__, 1, strlen(__FUNCTION__), _output_);\ + _writefnc_("... ", 1, 4, _output_);\ + jmp_buf _env_;\ + for (unsigned int _cx_test_loop_ = 0 ;\ + _cx_test_loop_ == 0 && !setjmp(_env_);\ + _writefnc_("success.\n", 1, 9, _output_),\ + _suite_->success++, _cx_test_loop_++) + +/** + * Checks a test assertion. + * If the assertion is correct, the test carries on. If the assertion is not + * correct, the specified message (terminated by a dot and a line break) is + * written to the test suites output stream. + * @param condition the condition to check + * @param message the message that shall be printed out on failure + */ +#define CX_TEST_ASSERTM(condition,message) if (!(condition)) { \ + char const* _assert_msg_ = message; \ + _writefnc_(_assert_msg_, 1, strlen(_assert_msg_), _output_); \ + _writefnc_(".\n", 1, 2, _output_); \ + _suite_->failure++; \ + longjmp(_env_, 1);\ + } (void) 0 + +/** + * Checks a test assertion. + * If the assertion is correct, the test carries on. If the assertion is not + * correct, the specified message (terminated by a dot and a line break) is + * written to the test suites output stream. + * @param condition the condition to check + */ +#define CX_TEST_ASSERT(condition) CX_TEST_ASSERTM(condition, #condition " failed") + +/** + * Macro for a test subroutine function header. + * + * Use this to declare and/or define a subroutine that can be called by using + * CX_TEST_CALL_SUBROUTINE(). + * + * @param name the name of the subroutine + * @param ... the parameter list + * + * @see CX_TEST_CALL_SUBROUTINE() + */ +#define CX_TEST_SUBROUTINE(name,...) void name(CxTestSuite* _suite_,\ + void *_output_, cx_write_func _writefnc_, jmp_buf _env_, __VA_ARGS__) + +/** + * Macro for calling a test subroutine. + * + * Subroutines declared with CX_TEST_SUBROUTINE() can be called by using this + * macro. + * + * @remark You may <b>only</b> call subroutines within a #CX_TEST_DO block. + * + * @param name the name of the subroutine + * @param ... the argument list + * + * @see CX_TEST_SUBROUTINE() + */ +#define CX_TEST_CALL_SUBROUTINE(name,...) \ + name(_suite_,_output_,_writefnc_,_env_,__VA_ARGS__) + +#ifdef __cplusplus +} +#endif + +#endif /* UCX_TEST_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/tree.h Sat Jan 27 17:50:19 2024 +0100 @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file tree.h + * \brief Interface for tree implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_TREE_H +#define UCX_TREE_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +__attribute__((__nonnull__)) +void cx_tree_link( + void * restrict parent, + void * restrict node, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +); + +__attribute__((__nonnull__)) +void cx_tree_unlink( + void *node, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //UCX_TREE_H
--- a/ucx/cx/utils.h Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/cx/utils.h Sat Jan 27 17:50:19 2024 +0100 @@ -33,7 +33,6 @@ * * \author Mike Becker * \author Olaf Wintermann - * \version 3.0 * \copyright 2-Clause BSD License */
--- a/ucx/linked_list.c Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/linked_list.c Sat Jan 27 17:50:19 2024 +0100 @@ -28,6 +28,7 @@ #include "cx/linked_list.h" #include "cx/utils.h" +#include "cx/compare.h" #include <string.h> #include <assert.h> @@ -63,6 +64,23 @@ cx_compare_func cmp_func, void const *elem ) { + void *dummy; + return cx_linked_list_find_node( + &dummy, start, + loc_advance, loc_data, + cmp_func, elem + ); +} + +ssize_t cx_linked_list_find_node( + void **result, + void const *start, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func cmp_func, + void const *elem +) { + assert(result != NULL); assert(start != NULL); assert(loc_advance >= 0); assert(loc_data >= 0); @@ -73,11 +91,13 @@ do { void *current = ll_data(node); if (cmp_func(current, elem) == 0) { + *result = (void*) node; return index; } node = ll_advance(node); index++; } while (node != NULL); + *result = NULL; return -1; } @@ -460,8 +480,6 @@ // HIGH LEVEL LINKED LIST IMPLEMENTATION -bool CX_DISABLE_LINKED_LIST_SWAP_SBO = false; - typedef struct cx_linked_list_node cx_linked_list_node; struct cx_linked_list_node { cx_linked_list_node *prev; @@ -609,6 +627,7 @@ #ifndef CX_LINKED_LIST_SWAP_SBO_SIZE #define CX_LINKED_LIST_SWAP_SBO_SIZE 128 #endif +unsigned cx_linked_list_swap_sbo_size = CX_LINKED_LIST_SWAP_SBO_SIZE; static int cx_ll_swap( struct cx_list_s *list, @@ -633,6 +652,7 @@ if (left < mid && right < mid) { // case 1: both items left from mid nleft = cx_ll_node_at(ll, left); + assert(nleft != NULL); nright = nleft; for (size_t c = left; c < right; c++) { nright = nright->next; @@ -640,6 +660,7 @@ } else if (left >= mid && right >= mid) { // case 2: both items right from mid nright = cx_ll_node_at(ll, right); + assert(nright != NULL); nleft = nright; for (size_t c = right; c > left; c--) { nleft = nleft->prev; @@ -686,7 +707,7 @@ } } - if (list->item_size > CX_LINKED_LIST_SWAP_SBO_SIZE || CX_DISABLE_LINKED_LIST_SWAP_SBO) { + if (list->item_size > CX_LINKED_LIST_SWAP_SBO_SIZE) { cx_linked_list_node *prev = nleft->prev; cx_linked_list_node *next = nright->next; cx_linked_list_node *midstart = nleft->next; @@ -735,13 +756,35 @@ return node == NULL ? NULL : node->payload; } -static ssize_t cx_ll_find( - struct cx_list_s const *list, - void const *elem +static ssize_t cx_ll_find_remove( + struct cx_list_s *list, + void const *elem, + bool remove ) { - return cx_linked_list_find(((cx_linked_list *) list)->begin, - CX_LL_LOC_NEXT, CX_LL_LOC_DATA, - list->cmpfunc, elem); + if (remove) { + cx_linked_list *ll = ((cx_linked_list *) list); + cx_linked_list_node *node; + ssize_t index = cx_linked_list_find_node( + (void **) &node, + ll->begin, + CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + list->cmpfunc, elem + ); + if (node != NULL) { + cx_invoke_destructor(list, node->payload); + cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, + CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); + list->size--; + cxFree(list->allocator, node); + } + return index; + } else { + return cx_linked_list_find( + ((cx_linked_list *) list)->begin, + CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + list->cmpfunc, elem + ); + } } static void cx_ll_sort(struct cx_list_s *list) { @@ -894,7 +937,7 @@ cx_ll_clear, cx_ll_swap, cx_ll_at, - cx_ll_find, + cx_ll_find_remove, cx_ll_sort, cx_ll_compare, cx_ll_reverse, @@ -915,11 +958,12 @@ list->base.cl = &cx_linked_list_class; list->base.allocator = allocator; - list->base.cmpfunc = comparator; if (item_size > 0) { list->base.item_size = item_size; + list->base.cmpfunc = comparator; } else { + list->base.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; cxListStorePointers((CxList *) list); }
--- a/ucx/list.c Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/list.c Sat Jan 27 17:50:19 2024 +0100 @@ -115,12 +115,13 @@ return ptr == NULL ? NULL : *ptr; } -static ssize_t cx_pl_find( - struct cx_list_s const *list, - void const *elem +static ssize_t cx_pl_find_remove( + struct cx_list_s *list, + void const *elem, + bool remove ) { cx_pl_hack_cmpfunc(list); - ssize_t ret = list->climpl->find(list, &elem); + ssize_t ret = list->climpl->find_remove(list, &elem, remove); cx_pl_unhack_cmpfunc(list); return ret; } @@ -171,7 +172,7 @@ cx_pl_clear, cx_pl_swap, cx_pl_at, - cx_pl_find, + cx_pl_find_remove, cx_pl_sort, cx_pl_compare, cx_pl_reverse, @@ -208,9 +209,10 @@ return NULL; } -static ssize_t cx_emptyl_find( - __attribute__((__unused__)) struct cx_list_s const *list, - __attribute__((__unused__)) void const *elem +static ssize_t cx_emptyl_find_remove( + __attribute__((__unused__)) struct cx_list_s *list, + __attribute__((__unused__)) void const *elem, + __attribute__((__unused__)) bool remove ) { return -1; } @@ -248,7 +250,7 @@ cx_emptyl_noop, NULL, cx_emptyl_at, - cx_emptyl_find, + cx_emptyl_find_remove, cx_emptyl_noop, cx_emptyl_compare, cx_emptyl_noop, @@ -293,8 +295,8 @@ ) { // lists are definitely different - cannot use internal compare function if (list->size == other->size) { - CxIterator left = cxListIterator(list); - CxIterator right = cxListIterator(other); + CxIterator left = list->cl->iterator(list, 0, false); + CxIterator right = other->cl->iterator(other, 0, false); for (size_t i = 0; i < list->size; i++) { void *leftValue = cxIteratorCurrent(left); void *rightValue = cxIteratorCurrent(right);
--- a/ucx/printf.c Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/printf.c Sat Jan 27 17:50:19 2024 +0100 @@ -34,6 +34,7 @@ #ifndef CX_PRINTF_SBO_SIZE #define CX_PRINTF_SBO_SIZE 512 #endif +unsigned const cx_printf_sbo_size = CX_PRINTF_SBO_SIZE; int cx_fprintf( void *stream, @@ -60,17 +61,21 @@ va_copy(ap2, ap); int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap); if (ret < 0) { + va_end(ap2); return ret; } else if (ret < CX_PRINTF_SBO_SIZE) { + va_end(ap2); return (int) wfc(buf, 1, ret, stream); } else { int len = ret + 1; char *newbuf = malloc(len); if (!newbuf) { + va_end(ap2); return -1; } ret = vsnprintf(newbuf, len, fmt, ap2); + va_end(ap2); if (ret > 0) { ret = (int) wfc(newbuf, 1, ret, stream); } @@ -85,9 +90,8 @@ ... ) { va_list ap; - cxmutstr ret; va_start(ap, fmt); - ret = cx_vasprintf_a(allocator, fmt, ap); + cxmutstr ret = cx_vasprintf_a(allocator, fmt, ap); va_end(ap); return ret; } @@ -104,7 +108,7 @@ va_list ap2; va_copy(ap2, ap); int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap); - if (ret > 0 && ret < CX_PRINTF_SBO_SIZE) { + if (ret >= 0 && ret < CX_PRINTF_SBO_SIZE) { s.ptr = cxMalloc(a, ret + 1); if (s.ptr) { s.length = (size_t) ret; @@ -124,6 +128,65 @@ } } } + va_end(ap2); return s; } +int cx_sprintf_a(CxAllocator *alloc, char **str, size_t len, const char *fmt, ... ) { + va_list ap; + va_start(ap, fmt); + int ret = cx_vsprintf_a(alloc, str, len, fmt, ap); + va_end(ap); + return ret; +} + +int cx_vsprintf_a(CxAllocator *alloc, char **str, size_t len, const char *fmt, va_list ap) { + va_list ap2; + va_copy(ap2, ap); + int ret = vsnprintf(*str, len, fmt, ap); + if ((unsigned) ret >= len) { + unsigned newlen = ret + 1; + char *ptr = cxRealloc(alloc, *str, newlen); + if (ptr) { + int newret = vsnprintf(ptr, newlen, fmt, ap2); + if (newret < 0) { + cxFree(alloc, ptr); + } else { + *str = ptr; + ret = newret; + } + } + } + va_end(ap2); + return ret; +} + +int cx_sprintf_sa(CxAllocator *alloc, char *buf, size_t len, char **str, const char *fmt, ... ) { + va_list ap; + va_start(ap, fmt); + int ret = cx_vsprintf_sa(alloc, buf, len, str, fmt, ap); + va_end(ap); + return ret; +} + +int cx_vsprintf_sa(CxAllocator *alloc, char *buf, size_t len, char **str, const char *fmt, va_list ap) { + va_list ap2; + va_copy(ap2, ap); + int ret = vsnprintf(buf, len, fmt, ap); + *str = buf; + if ((unsigned) ret >= len) { + unsigned newlen = ret + 1; + char *ptr = cxMalloc(alloc, newlen); + if (ptr) { + int newret = vsnprintf(ptr, newlen, fmt, ap2); + if (newret < 0) { + cxFree(alloc, ptr); + } else { + *str = ptr; + ret = newret; + } + } + } + va_end(ap2); + return ret; +}
--- a/ucx/string.c Mon Jan 22 17:27:47 2024 +0100 +++ b/ucx/string.c Sat Jan 27 17:50:19 2024 +0100 @@ -236,6 +236,7 @@ #ifndef CX_STRSTR_SBO_SIZE #define CX_STRSTR_SBO_SIZE 512 #endif +unsigned const cx_strstr_sbo_size = CX_STRSTR_SBO_SIZE; cxstring cx_strstr( cxstring haystack,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/tree.c Sat Jan 27 17:50:19 2024 +0100 @@ -0,0 +1,87 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/tree.h" + +#include <assert.h> + +#define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) +#define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) +#define tree_parent(node) CX_TREE_PTR(node, loc_parent) +#define tree_children(node) CX_TREE_PTR(node, loc_children) +#define tree_prev(node) CX_TREE_PTR(node, loc_prev) +#define tree_next(node) CX_TREE_PTR(node, loc_next) + +void cx_tree_link( + void *restrict parent, + void *restrict node, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + void *current_parent = tree_parent(node); + if (current_parent == parent) return; + if (current_parent != NULL) { + cx_tree_unlink(node, loc_parent, loc_children, + loc_prev, loc_next); + } + + if (tree_children(parent) == NULL) { + tree_children(parent) = node; + } else { + void *children = tree_children(parent); + tree_prev(children) = node; + tree_next(node) = children; + tree_children(parent) = node; + } + tree_parent(node) = parent; +} + +void cx_tree_unlink( + void *node, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + if (tree_parent(node) == NULL) return; + + void *left = tree_prev(node); + void *right = tree_next(node); + assert(left == NULL || tree_children(tree_parent(node)) != node); + if (left == NULL) { + tree_children(tree_parent(node)) = right; + } else { + tree_next(left) = right; + } + if (right != NULL) tree_prev(right) = left; + tree_parent(node) = NULL; + tree_prev(node) = NULL; + tree_next(node) = NULL; +}