Mon, 12 Aug 2013 14:40:19 +0200
new webdav api + repository and key configuration + aes encryption
--- a/Makefile Sat Dec 01 20:34:55 2012 +0100 +++ b/Makefile Mon Aug 12 14:40:19 2013 +0200 @@ -35,16 +35,14 @@ # osx # -#ifndef CONF - CONF=gcc -#endif +CONF=gcc include $(CONF).mk -all: build ucx dav +all: build/tool ucx dav -build: - mkdir build +build/tool: + mkdir -p build/tool ucx: FORCE build cd ucx; $(MAKE) CONF=$(CONF) all @@ -56,7 +54,7 @@ ./build/dav$(APP_EXT) clean: FORCE - $(RM) $(RMFLAGS) build/*.${OBJ_EXT} + $(RM) $(RMFLAGS) build/*.${OBJ_EXT} build/tool/*.${OBJ_EXT} FORCE:
--- a/dav/Makefile Sat Dec 01 20:34:55 2012 +0100 +++ b/dav/Makefile Mon Aug 12 14:40:19 2013 +0200 @@ -28,15 +28,18 @@ include ../$(CONF).mk -CFLAGS += `xml2-config --cflags` -LDFLAGS += `curl-config --libs` `xml2-config --libs` +CFLAGS += `curl-config --cflags` `pkg-config --cflags openssl libxml-2.0` +LDFLAGS += `curl-config --libs` `pkg-config --libs openssl libxml-2.0` SRC = main.c -SRC += propfind.c SRC += utils.c +SRC += webdav.c +SRC += methods.c +SRC += config.c +SRC += crypto.c -OBJ = $(SRC:%.c=../build/%.$(OBJ_EXT)) +OBJ = $(SRC:%.c=../build/tool/%.$(OBJ_EXT)) all: ../build/dav @@ -44,8 +47,6 @@ $(LD) -o ../build/dav$(APP_EXT) $(OBJ) \ ../build/libucx.$(LIB_EXT) $(LDFLAGS) -../build/%.$(OBJ_EXT): %.c ../build +../build/tool/%.$(OBJ_EXT): %.c $(CC) $(CFLAGS) -I../ -o $@ $< -../build: - mkdir -p build
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/config.c Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,197 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 <string.h> +#include <ucx/map.h> +#include <libxml/tree.h> + +#include "config.h" +#include "utils.h" + +#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) + +static UcxMap *repos; +static UcxMap *keys; + +void load_config() { + repos = ucx_map_new(16); + keys = ucx_map_new(16); + + char *file = util_concat_path(getenv("HOME"), ".dav/config.xml"); + xmlDoc *doc = xmlReadFile(file, NULL, 0); + free(file); + + xmlNode *xml_root = xmlDocGetRootElement(doc); + xmlNode *node = xml_root->children; + while(node) { + if(node->type == XML_ELEMENT_NODE) { + if(xstreq(node->name, "repository")) { + load_repository(node); + } else if(xstreq(node->name, "key")) { + load_key(node); + } + } + node = node->next; + } + + // TODO: free doc +} + +void load_repository(xmlNode *reponode) { + xmlNode *node = reponode->children; + Repository *repo = calloc(1, sizeof(Repository)); + repo->store_key_property = true; + repo->decrypt = true; + while(node) { + if(node->type == XML_ELEMENT_NODE) { + char *value = util_xml_get_text(node); + if(!value) { + // next + } else if(xstreq(node->name, "name")) { + repo->name = strdup(value); + } else if(xstreq(node->name, "url")) { + repo->url = strdup(value); + } else if(xstreq(node->name, "user")) { + repo->user = strdup(value); + } else if(xstreq(node->name, "password")) { + // TODO: use base64 + repo->password = strdup(value); + } else if(xstreq(node->name, "default-key")) { + repo->default_key = strdup(value); + } else if(xstreq(node->name, "encrypt")) { + repo->encrypt = util_getboolean(value); + } else if(xstreq(node->name, "decrypt")) { + repo->decrypt = util_getboolean(value); + } else if(xstreq(node->name, "store-key-property")) { + repo->store_key_property = util_getboolean(value); + } + } + node = node->next; + } + + if(repo->name) { + ucx_map_cstr_put(repos, repo->name, repo); + } else { + // TODO: free + } +} + +void load_key(xmlNode *keynode) { + xmlNode *node = keynode->children; + Key *key = calloc(1, sizeof(Key)); + key->type = KEY_AES256; + + while(node) { + if(node->type == XML_ELEMENT_NODE) { + char *value = util_xml_get_text(node); + if(!value) { + // next + } else if(xstreq(node->name, "name")) { + key->name = strdup(value); + } else if(xstreq(node->name, "file")) { + // load key file + sstr_t key_data = load_key_file(value); + if(key_data.length > 0) { + key->data = key_data.ptr; + key->length = key_data.length; + } + } else if(xstreq(node->name, "type")) { + if(!strcmp(value, "aes128")) { + key->type = KEY_AES128; + } else if(!strcmp(value, "aes256")) { + key->type = KEY_AES256; + } + } + + } + node = node->next; + } + + if(key->name) { + if(key->type == KEY_AES128) { + if(key->length < 16) { + return; + } + key->length = 16; + } + if(key->type == KEY_AES256) { + if(key->length < 32) { + return; + } + key->length = 32; + } + ucx_map_cstr_put(keys, key->name, key); + } else { + // TODO: free + } +} + +sstr_t load_key_file(char *filename) { + sstr_t k; + k.ptr = NULL; + k.length = 0; + + FILE *file = NULL; + if(filename[0] == '/') { + file = fopen(filename, "r"); + } else { + char *path = util_concat_path(getenv("HOME"), ".dav/"); + char *p2 = util_concat_path(path, filename); + file = fopen(p2, "r"); + free(path); + free(p2); + } + + if(!file) { + return k; + } + + char *data = malloc(256); + size_t r = fread(data, 1, 256, file); + k.ptr = data; + k.length = r; + + fclose(file); + return k; +} + +Repository* get_repository(char *name) { + if(!name) { + return NULL; + } + return ucx_map_cstr_get(repos, name); +} + +Key* get_key(char *name) { + if(!name) { + return NULL; + } + return ucx_map_cstr_get(keys, name); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/config.h Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,80 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 CONFIG_H +#define CONFIG_H + +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Repository Repository; +typedef struct Key Key; + +enum key_type { + KEY_AES128 = 0, + KEY_AES256, + KEY_PASSWORD +}; + +typedef enum key_type KeyType; + +struct Repository { + char *name; + char *url; + char *user; + char *password; + char *default_key; + bool encrypt; + bool decrypt; + bool store_key_property; +}; + +struct Key { + char *name; + KeyType type; + void *data; + size_t length; +}; + +void load_config(); +void load_repository(xmlNode *reponode); +void load_key(xmlNode *keynode); +sstr_t load_key_file(char *filename); + +Repository* get_repository(char *name); +Key* get_key(char *name); + +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/crypto.c Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,142 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 <string.h> + +#include "crypto.h" + +AESDecrypter* aes_decrypter_new(Key *key, void *stream, dav_write_func write_func) { + AESDecrypter *dec = malloc(sizeof(AESDecrypter)); + dec->stream = stream; + dec->write = write_func; + + EVP_CIPHER_CTX_init(&dec->ctx); + if(key->type == KEY_AES128) { + EVP_DecryptInit_ex(&dec->ctx, EVP_aes_128_cbc(), NULL, key->data, NULL); + } else if(key->type == KEY_AES256) { + EVP_DecryptInit_ex(&dec->ctx, EVP_aes_256_cbc(), NULL, key->data, NULL); + } else { + fprintf(stderr, "unknown key type\n"); + exit(-1); + } + return dec; +} + +size_t aes_write(const void *buf, size_t s, size_t n, AESDecrypter *dec) { + int len = s*n; + int outlen = len + AES_BLOCK_SIZE; + unsigned char *out = malloc(outlen); + EVP_DecryptUpdate(&dec->ctx, out, &len, buf, len); + dec->write(out, 1, len, dec->stream); + free(out); + return (s*n) / s; +} + +void aes_decrypter_close(AESDecrypter *dec) { + void *out = malloc(128); + int len = 0; + EVP_DecryptFinal_ex(&dec->ctx, out, &len); + dec->write(out, 1, len, dec->stream); + free(out); + EVP_CIPHER_CTX_cleanup(&dec->ctx); + free(dec); +} + + +AESEncrypter* aes_encrypter_new(Key *key, void *stream, dav_read_func read_func) { + AESEncrypter *enc = malloc(sizeof(AESEncrypter)); + enc->stream = stream; + enc->read = read_func; + enc->tmp = NULL; + enc->tmplen = 0; + enc->tmpoff = 0; + enc->end = 0; + + EVP_CIPHER_CTX_init(&enc->ctx); + if(key->type == KEY_AES128) { + EVP_EncryptInit_ex(&enc->ctx, EVP_aes_128_cbc(), NULL, key->data, NULL); + } else if(key->type == KEY_AES256) { + EVP_EncryptInit_ex(&enc->ctx, EVP_aes_256_cbc(), NULL, key->data, NULL); + } else { + fprintf(stderr, "unknown key type\n"); + exit(-1); + } + return enc; +} + +size_t aes_read(void *buf, size_t s, size_t n, AESEncrypter *enc) { + size_t len = s*n; + if(enc->tmp) { + size_t tmp_diff = enc->tmplen - enc->tmpoff; + size_t cp_len = tmp_diff > len ? len : tmp_diff; + memcpy(buf, enc->tmp + enc->tmpoff, cp_len); + enc->tmpoff += cp_len; + if(enc->tmpoff >= enc->tmplen) { + free(enc->tmp); + enc->tmp = NULL; + enc->tmplen = 0; + enc->tmpoff = 0; + } + return cp_len / s; + } + + if(enc->end) { + return 0; + } + + void *in = malloc(len); + size_t in_len = enc->read(in, 1, len, enc->stream); + + void *out = NULL; + int outlen = 0; + if(in_len != 0) { + outlen = len + AES_BLOCK_SIZE; + out = malloc(outlen); + EVP_EncryptUpdate(&enc->ctx, out, &outlen, in, in_len); + //out = (char*)aes_encrypt(enc->ctx, (unsigned char*)in, (int*)&in_len); + } else { + out = malloc(AES_BLOCK_SIZE); + EVP_EncryptFinal_ex(&enc->ctx, out, &outlen); + enc->end = 1; + } + enc->tmp = out; + enc->tmplen = outlen; + enc->tmpoff = 0; + + return aes_read(buf, s, n, enc); +} + +void aes_encrypter_close(AESEncrypter *enc) { + if(enc->tmp) { + free(enc->tmp); + } + EVP_CIPHER_CTX_cleanup(&enc->ctx); + free(enc); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/crypto.h Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,70 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 DAV_CRYPTO_H +#define DAV_CRYPTO_H + +#include "webdav.h" +#include "config.h" +#include <openssl/evp.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + EVP_CIPHER_CTX ctx; + void *stream; + dav_write_func write; +} AESDecrypter; + +typedef struct { + EVP_CIPHER_CTX ctx; + void *stream; + dav_read_func read; + char *tmp; + size_t tmplen; + size_t tmpoff; + int end; +} AESEncrypter; + +AESDecrypter* aes_decrypter_new(Key *key, void *stream, dav_write_func write_func); +size_t aes_write(const void *buf, size_t s, size_t n, AESDecrypter *dec); +void aes_decrypter_close(AESDecrypter *dec); +void aes_decrypter_close2(EVP_CIPHER_CTX *ctx); + +AESEncrypter* aes_encrypter_new(Key *key, void *stream, dav_read_func read_func); +size_t aes_read(void *buf, size_t s, size_t n, AESEncrypter *enc); +void aes_encrypter_close(AESEncrypter *enc); + +#ifdef __cplusplus +} +#endif + +#endif /* DAV_CRYPTO_H */ +
--- a/dav/main.c Sat Dec 01 20:34:55 2012 +0100 +++ b/dav/main.c Mon Aug 12 14:40:19 2013 +0200 @@ -29,13 +29,17 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <errno.h> #include <unistd.h> #include <libxml/xmlerror.h> #include <sys/types.h> #include <sys/stat.h> +#include <ucx/string.h> -#include "propfind.h" +#include "webdav.h" #include "utils.h" +#include "config.h" +#include "crypto.h" #include "main.h" void xmlerrorfnc(void * ctx, const char * msg, ... ) { @@ -43,8 +47,9 @@ } int main(int argc, char **argv) { - xmlGenericErrorFunc fnc = xmlerrorfnc; + xmlGenericErrorFunc fnc = xmlerrorfnc; initGenericErrorDefaultFunc(&fnc); + load_config(); if(argc < 2) { print_usage(); @@ -67,94 +72,71 @@ } -void get_file(CURL *curl, char *url, char *path) { - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); - - FILE *out = fopen(path, "w"); +void url_get_parts(char *url, char **root, char **path) { + size_t ulen = strlen(url); + *root = NULL; + *path = NULL; - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, out); - - CURLcode res = curl_easy_perform(curl); + int s; + if(ulen > 7 && !strncmp(url, "http://", 7)) { + s = 7; + } else if(ulen > 8 && !strncmp(url, "https://", 8)) { + s = 8; + } else { + s = 1; + } - fclose(out); - // handle some errors (http://curl.haxx.se/libcurl/c/libcurl-errors.html) - switch(res) { - case CURLE_OK: { + for(int i=s;i<ulen;i++) { + char c = url[i]; + if(c == '/') { + sstr_t r = sstrn(url, i); + sstr_t p = sstrsubs(sstr(url), i); + if(p.length == 0) { + p = sstrn("/", 1); + } + *root = sstrdup(r).ptr; + *path = sstrdup(p).ptr; return; } - case CURLE_REMOTE_ACCESS_DENIED: { - - break; - } - case CURLE_SSL_CONNECT_ERROR: { - - break; - } - case CURLE_LOGIN_DENIED: { - - break; - } - case CURLE_REMOTE_FILE_NOT_FOUND: { - - break; - } - default: { - - break; - } } - unlink(path); + *root = strdup(url); + *path = strdup("/"); } -void put_file(CURL *curl, char *url, char *path) { - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); - - curl_easy_setopt(curl, CURLOPT_PUT, 1L); - - struct stat s; - if(stat(path, &s) != 0) { - perror("stat"); - fprintf(stderr, "file: %s\n", path); - return; - } - - FILE *in = fopen(path, "r"); - - curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); - curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread); - curl_easy_setopt(curl, CURLOPT_READDATA, in); - curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)s.st_size); - - CURLcode res = curl_easy_perform(curl); - fclose(in); - - if(res != CURLE_OK) { - fprintf(stderr, "put_file: %s\n", curl_easy_strerror(res)); - } -} int cmd_list(int argc, char **argv) { if(argc == 0) { return -1; } - char *url = argv[0]; // TODO: use arg as url or alias - CURL *curl = curl_easy_init(); - curl_easy_setopt(curl, CURLOPT_URL, url); - Propfind *propfind = dav_propfind(curl); + DavContext *ctx = dav_context_new(); + DavSession *sn = NULL; + char *url = argv[0]; + char *root = NULL; + char *path = NULL; + url_get_parts(url, &root, &path); - UCX_FOREACH(UcxDlist*, propfind->children, ch) { - DavResource *resource = ch->data; - printf("%s\n", resource->name); + Repository *repo = get_repository(root); + if(repo) { + sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password); + } else { + sn = dav_session_new(ctx, root); } - // TODO: free propfind stuff - curl_easy_cleanup(curl); + //printf("baseurl: %s\n", sn->base_url); + + DavResource *ls = dav_get(sn, path, NULL); + if(!ls) { + fprintf(stderr, "error\n"); + return -1; + } + DavResource *child = ls->children; + while(child) { + printf("%s\n", child->name); + child = child->next; + } return 0; } @@ -164,51 +146,107 @@ return -1; } - char *url = argv[0]; // TODO: use arg as url or alias - - CURL *curl = curl_easy_init(); - curl_easy_setopt(curl, CURLOPT_URL, url); - Propfind *propfind = dav_propfind(curl); + DavContext *ctx = dav_context_new(); + dav_add_namespace(ctx, "U", "http://www.uap-core.de/"); + DavSession *sn = NULL; + char *url = argv[0]; + char *root = NULL; + char *path = NULL; + url_get_parts(url, &root, &path); - if(propfind->resource->collection) { - UCX_FOREACH(UcxDlist*, propfind->children, chd) { - DavResource *resource = chd->data; - if(!resource->collection) { - char *resurl = util_child_url(propfind->url, resource->href); - get_file(curl, resurl, resource->name); - } - } + Repository *repo = get_repository(root); + if(repo) { + sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password); } else { - get_file(curl, propfind->url, propfind->resource->name); + sn = dav_session_new(ctx, root); } - // TODO: free propfind stuff - curl_easy_cleanup(curl); + DavResource *res = dav_get(sn, path, "U:crypto-key"); + if(!res) { + fprintf(stderr, "error\n"); + return -1; + } + FILE *out = fopen(res->name, "w"); + if(!out) { + fprintf(stderr, "cannot open output file\n"); + return -1; + } + + AESDecrypter *dec = NULL; + char *keyprop = dav_get_property_ns(res, "http://www.uap-core.de/", "crypto-key"); + if(repo) { + Key *key = get_key(keyprop); + if(repo->encrypt && key) { + dec = aes_decrypter_new(key, out, (dav_write_func)fwrite); + } + } + + int ret; + if(dec && keyprop) { + ret = dav_get_content(res, dec, (dav_write_func)aes_write); + } else { + ret = dav_get_content(res, out, (dav_write_func)fwrite); + } + if(dec) { + aes_decrypter_close(dec); + } + fclose(out); + if(ret) { + unlink(res->name); + } return 0; } int cmd_put(int argc, char **argv) { - if(argc == 0) { + if(argc < 2) { return -1; } - char *url = argv[0]; // TODO: use arg as url or alias - char *path; - if(argc > 0) { - path = argv[1]; + DavContext *ctx = dav_context_new(); + DavSession *sn = NULL; + char *url = argv[0]; + char *file = argv[1]; + char *root = NULL; + char *path = NULL; + url_get_parts(url, &root, &path); + + Repository *repo = get_repository(root); + if(repo) { + sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password); } else { - fprintf(stderr, "put: missing file argument\n"); - return -1; + sn = dav_session_new(ctx, root); } - char *uploadurl = util_upload_url(url, path); - - CURL *curl = curl_easy_init(); + DavResource *res = dav_resource_new(sn, path); + if(!res) { + fprintf(stderr, "error\n"); + return -1; + } + FILE *in = fopen(file, "r"); + if(!in) { + fprintf(stderr, "cannot open input file\n"); + return -1; + } + AESEncrypter *enc = NULL; + if(repo) { + Key *key = get_key(repo->default_key); + if(repo->encrypt && key) { + enc = aes_encrypter_new(key, in, (dav_read_func)fread); + } + } + if(enc) { + dav_set_content(res, enc, (dav_read_func)aes_read); + dav_set_property_ns(res, "http://www.uap-core.de/", "crypto-key", repo->default_key); + } else { + dav_set_content(res, in, (dav_read_func)fread); + } - // TODO: check possible name conflicts - - put_file(curl, uploadurl, path); - - curl_easy_cleanup(curl); + if(dav_store(res)) { + fprintf(stderr, "cannot upload file\n"); + fclose(in); + return -1; + } + fclose(in); + return 0; }
--- a/dav/main.h Sat Dec 01 20:34:55 2012 +0100 +++ b/dav/main.h Mon Aug 12 14:40:19 2013 +0200 @@ -30,7 +30,6 @@ #define MAIN_H #include <curl/curl.h> -#include "propfind.h" #ifdef __cplusplus extern "C" {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/methods.c Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,458 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 <string.h> + +#include "utils.h" +#include "methods.h" + +#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) + +/* ----------------------------- PROPFIND ----------------------------- */ + +CURLcode do_propfind_request( + CURL *handle, + UcxBuffer *request, + UcxBuffer *response) +{ + curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPFIND"); + + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Content-Type: text/xml"); + headers = curl_slist_append(headers, "Depth: 1"); + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); + + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); + + curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); + curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read); + curl_easy_setopt(handle, CURLOPT_READDATA, request); + curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); + + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); + + ucx_buffer_seek(request, 0, SEEK_SET); + return curl_easy_perform(handle); +} + +UcxBuffer* create_allprop_propfind_request() { + UcxBuffer *buf = ucx_buffer_new(NULL, 512, 0); + sstr_t s; + + s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("<D:propfind xmlns:D=\"DAV:\">\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("<D:allprop/></D:propfind>\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + return buf; +} + +UcxBuffer* create_propfind_request(UcxList *properties) { + UcxBuffer *buf = ucx_buffer_new(NULL, 512, 0); + sstr_t s; + + UcxMap *namespaces = ucx_map_new(8); + UCX_FOREACH(elm, properties) { + DavProperty *p = elm->data; + if(strcmp(p->ns->name, "DAV:")) { + ucx_map_cstr_put(namespaces, p->ns->prefix, p->ns); + } + } + + s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + // write root element and namespaces + s = S("<D:propfind xmlns:D=\"DAV:\""); + ucx_buffer_write(s.ptr, 1, s.length, buf); + UcxMapIterator mapi = ucx_map_iterator(namespaces); + UcxKey key; + DavNamespace *ns; + UCX_MAP_FOREACH(key, ns, mapi) { + s = S(" xmlns:"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(ns->prefix); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S("=\""); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(ns->name); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S("\""); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + s = S(">\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + // default properties + s = S("<D:prop>\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("<D:creationdate />\n<D:getlastmodified />\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("<D:getcontentlength />\n<D:getcontenttype />\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("<D:resourcetype />\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + // extra properties + UCX_FOREACH(elm, properties) { + DavProperty *prop = elm->data; + s = S("<"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(prop->ns->prefix); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(":"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(prop->name); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(" />\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + + // end + s = S("</D:prop>\n</D:propfind>\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + return buf; +} + +DavResource* parse_propfind_response(DavSession *sn, UcxBuffer *response) { + char *url = NULL; + curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &url); + DavResource *root = resource_new_href(sn, util_url_path(url)); + + xmlDoc *doc = xmlReadMemory(response->space, response->size, url, NULL, 0); + if(!doc) { + // TODO: free stuff + return NULL; + } + + xmlNode *xml_root = xmlDocGetRootElement(doc); + xmlNode *node = xml_root->children; + while(node) { + if(node->type == XML_ELEMENT_NODE) { + if(xstreq(node->name, "response")) { + parse_response_tag(root, node); + } + } + + node = node->next; + } + + return root; +} + +int parse_response_tag(DavResource *resource, xmlNode *node) { + DavResource *res = resource; + node = node->children; + while(node) { + if(node->type == XML_ELEMENT_NODE) { + if(xstreq(node->name, "href")) { + xmlNode *href_content = node->children; + if(href_content->type != XML_TEXT_NODE) { + // error + return 1; + } + if(xstreq(resource->href, href_content->content)) { + res = resource; + } else { + res = resource_new_href(resource->session, (char*)href_content->content); + resource_add_child(resource, res); + } + } else if(xstreq(node->name, "propstat")) { + xmlNode *n = node->children; + xmlNode *prop_node = NULL; + int ok = 0; + // get the status code + while(n) { + if(n->type == XML_ELEMENT_NODE) { + if(xstreq(n->name, "prop")) { + prop_node = n; + } else if(xstreq(n->name, "status")) { + xmlNode *status_node = n->children; + if(status_node->type != XML_TEXT_NODE) { + return 1; + } + sstr_t status_str = sstr((char*)status_node->content); + if(status_str.length < 13) { + return 1; + } + status_str = sstrsubsl(status_str, 9, 3); + if(!sstrcmp(status_str, S("200"))) { + ok = 1; + } + } + } + n = n->next; + } + // if status is ok, get all properties + if(ok) { + n = prop_node->children; + while(n) { + if(n->type == XML_ELEMENT_NODE) { + if(xstreq(n->name, "resourcetype")) { + xmlNode *rsnode = n->children; + if(rsnode && rsnode->type == XML_ELEMENT_NODE) { + // TODO: this is a ugly lazy hack + resource_add_property(res, "DAV:", (char*)n->name, "collection"); + res->iscollection = 1; + } + } else { + xmlNode *content = n->children; + if(content) { + resource_add_property( + res, + (char*)n->ns->href, + (char*)n->name, + (char*)content->content); + } + } + } + n = n->next; + } + } + } + } + + node = node->next; + } + + set_davprops(res); + + return 0; +} + +void set_davprops(DavResource *res) { + char *cl = dav_get_property_ns(res, "DAV:", "getcontentlength"); + char *ct = dav_get_property_ns(res, "DAV:", "getcontenttype"); + char *cd = dav_get_property_ns(res, "DAV:", "creationdate"); + char *lm = dav_get_property_ns(res, "DAV:", "getlastmodified"); + + res->contenttype = ct; + if(cl) { + char *end = NULL; + res->contentlength = strtoull(cl, &end, 0); + } + res->creationdate = util_parse_creationdate(cd); + res->lastmodified = util_parse_lastmodified(lm); +} + + +/* ----------------------------- PROPPATCH ----------------------------- */ + +CURLcode do_proppatch_request( + CURL *handle, + UcxBuffer *request, + UcxBuffer *response) +{ + curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPPATCH"); + + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Content-Type: text/xml"); + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); + + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); + + curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); + curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read); + curl_easy_setopt(handle, CURLOPT_READDATA, request); + curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); + + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); + + ucx_buffer_seek(request, 0, SEEK_SET); + return curl_easy_perform(handle); +} + +UcxBuffer* create_proppatch_request(DavNodeData *data) { + UcxBuffer *buf = ucx_buffer_new(NULL, 512, 0); + sstr_t s; + + UcxMap *namespaces = ucx_map_new(8); + char prefix[8]; + int pfxnum = 0; + UCX_FOREACH(elm, data->set) { + DavProperty *p = elm->data; + if(strcmp(p->ns->name, "DAV:")) { + snprintf(prefix, 8, "x%d\0", pfxnum++); + ucx_map_cstr_put(namespaces, p->ns->name, prefix); + } + } + UCX_FOREACH(elm, data->remove) { + DavProperty *p = elm->data; + if(strcmp(p->ns->name, "DAV:")) { + snprintf(prefix, 8, "x%d\0", pfxnum++); + ucx_map_cstr_put(namespaces, p->ns->name, prefix); + } + } + + s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + // write root element and namespaces + s = S("<D:propertyupdate xmlns:D=\"DAV:\""); + ucx_buffer_write(s.ptr, 1, s.length, buf); + UcxMapIterator mapi = ucx_map_iterator(namespaces); + UcxKey key; + char *pfxval; + UCX_MAP_FOREACH(key, pfxval, mapi) { + s = S(" xmlns:"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(pfxval); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S("=\""); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstrn(key.data, key.len); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S("\""); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + s = S(">\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + if(data->set) { + s = S("<D:set>\n<D:prop>\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + UCX_FOREACH(elm, data->set) { + DavProperty *property = elm->data; + char *prefix = ucx_map_cstr_get(namespaces, property->ns->name); + + s = S("<"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(prefix); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(":"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(property->name); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(">"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(property->value); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S("</"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(prefix); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(":"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(property->name); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(">\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + s = S("</D:prop>\n</D:set>\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + if(data->remove) { + s = S("<D:set>\n<D:prop>\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + UCX_FOREACH(elm, data->set) { + DavProperty *property = elm->data; + char *prefix = ucx_map_cstr_get(namespaces, property->ns->name); + + s = S("<"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(prefix); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(":"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(property->name); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(">"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(property->value); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S("</"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(prefix); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(":"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = sstr(property->name); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(">\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + s = S("</D:prop>\n</D:set>\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + + s = S("</D:propertyupdate>\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + return buf; +} + +/* ----------------------------- PUT ----------------------------- */ + +static size_t dummy_write(void *buf, size_t s, size_t n, void *data) { + return s*n; +} + +CURLcode do_put_request(CURL *handle, void *data, dav_read_func read_func, size_t length) { + curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL); + curl_easy_setopt(handle, CURLOPT_PUT, 1L); + curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L); + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); + + UcxBuffer *buf = NULL; + if(!read_func) { + buf = ucx_buffer_new(data, length, 0); + buf->size = length; + data = buf; + read_func = (dav_read_func)ucx_buffer_read; + curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length); + } else if(length == 0) { + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Transfer-Encoding: chunked"); + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); + } + + curl_easy_setopt(handle, CURLOPT_READFUNCTION, read_func); + curl_easy_setopt(handle, CURLOPT_READDATA, data); + + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); + + CURLcode res = curl_easy_perform(handle); + if(buf) { + ucx_buffer_free(buf); + } + return res; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/methods.h Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 METHODS_H +#define METHODS_H + +#include "webdav.h" + +#ifdef __cplusplus +extern "C" { +#endif + +CURLcode do_propfind_request( + CURL *handle, + UcxBuffer *request, + UcxBuffer *response); + +CURLcode do_proppatch_request( + CURL *handle, + UcxBuffer *request, + UcxBuffer *response); + +CURLcode do_put_request( + CURL *handle, + void *data, + dav_read_func read_func, + size_t length); + +UcxBuffer* create_allprop_propfind_request(); +UcxBuffer* create_propfind_request(UcxList *properties); +DavResource* parse_propfind_response(DavSession *sn, UcxBuffer *response); +int parse_response_tag(DavResource *resource, xmlNode *node); +void set_davprops(DavResource *res); + +UcxBuffer* create_proppatch_request(DavNodeData *data); + +#ifdef __cplusplus +} +#endif + +#endif /* METHODS_H */ +
--- a/dav/propfind.c Sat Dec 01 20:34:55 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,229 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2012 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 <string.h> - -#include "utils.h" -#include "propfind.h" - -Propfind* dav_propfind(CURL *handle) { - Propfind *response = malloc(sizeof(Propfind)); - response->resource = NULL; - response->children = NULL; - - curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPFIND"); - - struct curl_slist *headers = NULL; - headers = curl_slist_append(headers, "Content-Type: text/xml"); - headers = curl_slist_append(headers, "Depth: 1"); - curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); - - UcxBuffer *rqbuf = create_propfind_request_body(); - UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); - //printf("request\n%s\n", body->space); - - curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); - - curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); - curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read); - curl_easy_setopt(handle, CURLOPT_READDATA, rqbuf); - curl_easy_setopt(handle, CURLOPT_INFILESIZE, rqbuf->size); - - curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write); - curl_easy_setopt(handle, CURLOPT_WRITEDATA, rpbuf); - - ucx_buffer_seek(rqbuf, 0, SEEK_SET); - CURLcode res = curl_easy_perform(handle); - if(res == CURLE_OK) { - printf("response\n%s\n", rpbuf->space); - - char *url = NULL; - curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); - response->url = util_url_path(url); - - parse_propfind(response, rpbuf); - - response->url = strdup(url); - } - curl_easy_reset(handle); - - return response; -} - -UcxBuffer* create_propfind_request_body() { - UcxBuffer *buf = ucx_buffer_new(NULL, 512, 0); - sstr_t s; - - s = ST("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - - s = ST("<D:propfind xmlns:D=\"DAV:\">\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - - s = ST("<D:prop>\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - - s = ST("<D:creationdate />\n<D:getlastmodified />\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - - s = ST("<D:getcontentlength />\n<D:getcontenttype />\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - - s = ST("<D:resourcetype />\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - - s = ST("</D:prop>\n</D:propfind>\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - - return buf; -} - -int parse_propfind(Propfind *propfind, UcxBuffer *response) { - xmlTextReaderPtr reader = xmlReaderForMemory( - response->space, - response->size, - propfind->url, - NULL, - 0); - - int ret = 0; - if(reader != NULL) { - int r; - while((r = xmlTextReaderRead(reader)) == 1) { - int nodetype = xmlTextReaderNodeType(reader); - if(nodetype == XML_READER_TYPE_ELEMENT) { - const xmlChar *name = xmlTextReaderConstLocalName(reader); - if(!xmlStrcmp(name, BAD_CAST "response")) { - r = parse_response(reader, propfind); - if(r != 0) { - ret = -1; - break; - } - } - } - } - xmlFreeTextReader(reader); - } - - return ret; -} - -int parse_response(xmlTextReaderPtr reader, Propfind *propfind) { - DavResource *res = calloc(sizeof(DavResource), 1); - if(res == NULL) { - return -1; - } - - /* - * 0: href - * 1: creationdate - * 2: getlastmodified - * 3: getcontentlength - * 4: getcontenttype - * 5: resourcetype - */ - int valuetype = -1; - - int r; - while((r = xmlTextReaderRead(reader)) == 1) { - int nodetype = xmlTextReaderNodeType(reader); - - if(nodetype == XML_READER_TYPE_END_ELEMENT) { - const xmlChar *name = xmlTextReaderConstLocalName(reader); - if(!xmlStrcmp(name, BAD_CAST "response")) { - break; - } - } - - switch(nodetype) { - case XML_READER_TYPE_ELEMENT: { - const xmlChar *name = xmlTextReaderConstLocalName(reader); - if(!xmlStrcmp(name, BAD_CAST "href")) { - valuetype = 0; - } else if(!xmlStrcmp(name, BAD_CAST "creationdate")) { - valuetype = 1; - } else if(!xmlStrcmp(name, BAD_CAST "getlastmodified")) { - valuetype = 2; - } else if(!xmlStrcmp(name, BAD_CAST "getcontentlength")) { - valuetype = 3; - } else if(!xmlStrcmp(name, BAD_CAST "getcontenttype")) { - valuetype = 4; - } else if(!xmlStrcmp(name, BAD_CAST "resourcetype")) { - valuetype = 5; - } else if(!xmlStrcmp(name, BAD_CAST "collection") - && valuetype == 5) { - res->collection = 1; - } else { - valuetype = -1; - } - break; - } - case XML_READER_TYPE_TEXT: { - const xmlChar *cvalue = xmlTextReaderConstValue(reader); - if(cvalue == NULL) { - break; - } - char *value = strdup((char*)cvalue); - switch(valuetype) { - case 0: { - res->href = value; - res->name = util_resource_name(value); - break; - } - case 1: { - res->creationdate = 0; // TODO - break; - } - case 2: { - res->lastmodified = 0; // TODO - break; - } - case 3: { - res->contentlength = atoi(value); - break; - } - case 4: { - res->contenttype = value; - //break; - } - } - //break; - } - } - } - - if(!strcmp(res->href, propfind->url)) { - propfind->resource = res; - } else { - propfind->children = ucx_dlist_append(propfind->children, res); - } - - return 0; -}
--- a/dav/propfind.h Sat Dec 01 20:34:55 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2012 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 PROPFIND_H -#define PROPFIND_H - -#include <inttypes.h> -#include <ucx/string.h> -#include <ucx/dlist.h> -#include <ucx/buffer.h> -#include <curl/curl.h> - -#include <libxml/xmlreader.h> - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - char *name; - char *href; - uint64_t contentlength; - char *contenttype; - time_t creationdate; - time_t lastmodified; - int collection; -} DavResource; - -typedef struct { - char *url; - DavResource *resource; - UcxDlist *children; -} Propfind; - -Propfind* dav_propfind(CURL *handle); - -UcxBuffer* create_propfind_request_body(); - -int parse_propfind(Propfind *propfind, UcxBuffer *response); -int parse_response(xmlTextReaderPtr reader, Propfind *propfind); - -#ifdef __cplusplus -} -#endif - -#endif /* PROPFIND_H */ -
--- a/dav/utils.c Sat Dec 01 20:34:55 2012 +0100 +++ b/dav/utils.c Mon Aug 12 14:40:19 2013 +0200 @@ -31,24 +31,36 @@ #include <stdlib.h> #include <string.h> #include <ucx/string.h> +#include <libxml/tree.h> +#include <curl/curl.h> #include "utils.h" -time_t parse_creationdate(char *str) { +time_t util_parse_creationdate(char *str) { // example: 2012-11-29T21:35:35Z - + if(!str) { + return 0; + } // TODO - return 1234; + return 0; } -time_t parse_lastmodified(char *str) { +time_t util_parse_lastmodified(char *str) { // example: Thu, 29 Nov 2012 21:35:35 GMT - - // TODO - return 1234; + if(!str) { + return 0; + } else { + return curl_getdate(str, NULL); + } } +int util_getboolean(char *v) { + if(v[0] == 'T' || v[0] == 't') { + return 1; + } + return 0; +} char* util_url_path(char *url) { char *path = NULL; @@ -92,50 +104,49 @@ return name; } -char* util_child_url(char *base, char *path) { - char *basepath = util_url_path(base); - int baselen = basepath - base; - int pathlen = strlen(path); - - int len = baselen + pathlen + 1; - char *url = malloc(len); - url[len - 1] = 0; - url[len - 2] = 0; - - if(url == NULL) { - return NULL; +char* util_concat_path(char *url_base, char *p) { + sstr_t base = sstr(url_base); + sstr_t path; + if(p) { + path = sstr(p); + } else { + path = sstrn("", 0); } - memcpy(url, base, baselen); - memcpy(url + baselen, path, pathlen); - - return url; -} - -char* util_upload_url(char *url, char *filepath) { - int urllen = strlen(url); - int filepathlen = strlen(filepath); - - for(int i=filepathlen-1;i>=0;i--) { - if(filepath[i] == '/') { - filepath = filepath + i + 1; - break; + int add_separator = 0; + if(base.ptr[base.length-1] == '/') { + if(path.ptr[0] == '/') { + base.length--; + } + } else { + if(path.length == 0 || path.ptr[0] != '/') { + add_separator = 1; } } - int pathlen = urllen + filepathlen; - char *path = malloc(pathlen + 2); + sstr_t url; + url.length = base.length + path.length + add_separator; + url.ptr = malloc(url.length + 1); + url.ptr[url.length] = '\0'; - int j = urllen; - memcpy(path, url, urllen); - if(url[urllen - 1] != '/') { - path[j] = '/'; - j++; + if(add_separator) { + url = sstrncat(url, 3, base, sstr("/"), path); + } else { + url = sstrncat(url, 2, base, path); } - memcpy(path + j, filepath, filepathlen); - j += filepathlen; - path[j] = 0; - return path; + return url.ptr; } + +char* util_xml_get_text(xmlNode *elm) { + xmlNode *node = elm->children; + while(node) { + if(node->type == XML_TEXT_NODE) { + return (char*)node->content; + } + node = node->next; + } + return NULL; +} +
--- a/dav/utils.h Sat Dec 01 20:34:55 2012 +0100 +++ b/dav/utils.h Mon Aug 12 14:40:19 2013 +0200 @@ -30,21 +30,22 @@ #define UTILS_H #include <sys/types.h> +#include <libxml/tree.h> #ifdef __cplusplus extern "C" { #endif -time_t parse_creationdate(char *str); -time_t parse_lastmodified(char *str); +time_t util_parse_creationdate(char *str); +time_t util_parse_lastmodified(char *str); char* util_url_path(char *url); - char* util_resource_name(char *url); +char* util_concat_path(char *url_base, char *path); -char* util_child_url(char *url, char *href); +int util_getboolean(char *v); -char* util_upload_url(char *url, char *filepath); +char* util_xml_get_text(xmlNode *elm); #ifdef __cplusplus }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/webdav.c Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,471 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 <string.h> +#include <libxml/tree.h> + +#include "utils.h" +#include "webdav.h" +#include "methods.h" +#include "ucx/buffer.h" + +#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) + +DavContext* dav_context_new() { + DavContext *context = malloc(sizeof(DavContext)); + if(!context) { + return NULL; + } + context->namespaces = ucx_map_new(16); + if(!context->namespaces) { + free(context); + return NULL; + } + DavNamespace *davns = malloc(sizeof(DavNamespace)); + if(!davns) { + ucx_map_free(context->namespaces); + free(context); + return NULL; + } + davns->prefix = "D"; + davns->name = "DAV:"; + if(ucx_map_cstr_put(context->namespaces, "D", davns)) { + free(davns); + ucx_map_free(context->namespaces); + free(context); + return NULL; + } + + return context; +} + +int dav_add_namespace(DavContext *context, char *prefix, char *name) { + DavNamespace *namespace = malloc(sizeof(DavNamespace)); + if(!namespace) { + return 1; + } + namespace->prefix = strdup(prefix); + namespace->name = strdup(name); + return ucx_map_cstr_put(context->namespaces, prefix, namespace); +} + +DavNamespace* dav_get_namespace(DavContext *context, char *prefix) { + return ucx_map_cstr_get(context->namespaces, prefix); +} + +DavNamespace* dav_get_namespace_s(DavContext *context, sstr_t prefix) { + return ucx_map_sstr_get(context->namespaces, prefix); +} + +DavSession* dav_session_new(DavContext *context, char *base_url) { + if(!base_url) { + return NULL; + } + sstr_t url = sstr(base_url); + if(url.length == 0) { + return NULL; + } + DavSession *sn = malloc(sizeof(DavSession)); + if(url.ptr[url.length - 1] == '/') { + sn->base_url = strdup(base_url); + } else { + char *url_str = malloc(url.length + 2); + memcpy(url_str, base_url, url.length); + url_str[url.length] = '/'; + url_str[url.length + 1] = '\0'; + sn->base_url = url_str; + } + sn->context = context; + sn->handle = curl_easy_init(); + curl_easy_setopt(sn->handle, CURLOPT_URL, base_url); + + sn->mp = ucx_mempool_new(1024); + sn->allocator = ucx_mempool_allocator(sn->mp); + + return sn; +} + +DavSession* dav_session_new_auth(DavContext *context, char *base_url, char *user, char *password) { + DavSession *sn = dav_session_new(context, base_url); + if(!sn) { + return NULL; + } + dav_session_set_auth(sn, user, password); + return sn; +} + +void dav_session_set_auth(DavSession *sn, char *user, char *password) { + if(user && password) { + size_t ulen = strlen(user); + size_t plen = strlen(password); + size_t upwdlen = ulen + plen + 2; + char *upwdbuf = malloc(upwdlen); + snprintf(upwdbuf, upwdlen, "%s:%s\0", user, password); + curl_easy_setopt(sn->handle, CURLOPT_USERPWD, upwdbuf); + free(upwdbuf); + } +} + +DavResource* dav_get(DavSession *sn, char *path, char *properties) { + char *url = util_concat_path(sn->base_url, path); + + CURL *handle = sn->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + + UcxList *proplist = NULL; + if(properties) { + proplist = parse_properties_string(sn->context, sstr(properties)); + } + UcxBuffer *rqbuf = create_propfind_request(proplist); + UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); + + //fwrite(rqbuf->space, 1, rqbuf->size, stdout); + //printf("\n"); + + DavResource *resource = NULL; + CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf); + int status = 0; + curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK && status == 207) { + //printf("response\n%s\n", rpbuf->space); + resource = parse_propfind_response(sn, rpbuf); + } + return resource; +} + +UcxList* parse_properties_string(DavContext *context, sstr_t str) { + UcxList *proplist = NULL; + size_t nprops; + sstr_t *props = sstrsplit(str, S(","), &nprops); + for(int i=0;i<nprops;i++) { + sstr_t s = props[i]; + sstr_t nsname = sstrchr(s, ':'); + if(nsname.length > 0) { + sstr_t nspre = sstrsubsl(s, 0, nsname.ptr - s.ptr); + nsname.ptr++; + nsname.length--; + + DavProperty *dp = malloc(sizeof(DavProperty)); + sstr_t pre = sstrdup(sstrtrim(nspre)); + dp->ns = dav_get_namespace(context, pre.ptr); + free(pre.ptr); + dp->name = sstrdup(nsname).ptr; + if(dp->ns && dp->name) { + proplist = ucx_list_append(proplist, dp); + } else { + free(dp->name); + free(dp); + } + } + free(s.ptr); + } + free(props); + return proplist; +} + + + + +DavResource* dav_resource_new(DavSession *sn, char *path) { + char *url = util_concat_path(sn->base_url, path); + char *href = util_url_path(url); + DavResource *res = resource_new_href(sn, href); + free(url); + return res; +} + +DavResource* resource_new_href(DavSession *sn, char *href) { + UcxMempool *mp = sn->mp; + UcxAllocator *a = sn->allocator; + + DavResource *res = ucx_mempool_calloc(mp, 1, sizeof(DavResource)); + res->session = sn; + + // set name, path and href + resource_set_info(res, href); + + // initialize node data + res->data = node_data_new(sn); + + return res; +} + +void resource_add_property(DavResource *res, char *ns, char *name, char *value) { + if(!value) { + return; + } + UcxMempool *mp = res->session->mp; + UcxAllocator *a = res->session->allocator; + + UcxKey key = dav_property_key(ns, name); + sstr_t v = sstrdup_a(a, sstr(value)); + ucx_map_put(res->data->properties, key, v.ptr); + free(key.data); +} + +char* resource_get_property(DavResource *res, char *ns, char *name) { + UcxKey key = dav_property_key(ns, name); + return ucx_map_get(res->data->properties, key); +} + +UcxKey dav_property_key(char *ns, char *name) { + sstr_t ns_str = sstr(ns); + sstr_t name_str = sstr(name); + + sstr_t key; + key.length = ns_str.length + name_str.length + 1; + key.ptr = malloc(key.length + 1); + key = sstrncat(key, 3, ns_str, S(" "), name_str); + + return ucx_key(key.ptr, key.length); +} + +void resource_add_child(DavResource *parent, DavResource *child) { + child->next = NULL; + if(parent->children) { + DavResource *last = parent->children; + while(last->next) { + last = last->next; + } + last->next = child; + child->prev = last; + } else { + child->prev = NULL; + parent->children = child; + } +} + +void resource_set_info(DavResource *res, char *href_str) { + char *url_str = NULL; + curl_easy_getinfo(res->session->handle, CURLINFO_EFFECTIVE_URL, &url_str); + sstr_t name = sstr(util_resource_name(href_str)); + sstr_t href = sstr(href_str); + + sstr_t base_href = sstr(util_url_path(res->session->base_url)); + sstr_t path = sstrsubs(href, base_href.length - 1); + + UcxAllocator *a = res->session->allocator; + res->name = sstrdup_a(a, name).ptr; + res->href = sstrdup_a(a, href).ptr; + res->path = sstrdup_a(a, path).ptr; +} + +DavNodeData* node_data_new(DavSession *sn) { + DavNodeData *data = ucx_mempool_malloc(sn->mp, sizeof(DavNodeData)); + if(!data) { + return NULL; + } + data->properties = ucx_map_new_a(sn->allocator, 32); + data->set = NULL; + data->remove = NULL; + data->content = NULL; + data->read = NULL; + data->length = 0; + return data; +} + +int dav_load(DavResource *res) { + DavSession *sn = res->session; + // clean map + UcxKey key; + void *value; + UcxMapIterator i = ucx_map_iterator(res->data->properties); + UCX_MAP_FOREACH(key, value, i) { + ucx_map_remove(res->data->properties, key); + } + + char *url = util_concat_path(sn->base_url, res->path); + + CURL *handle = sn->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + + UcxBuffer *rqbuf = create_allprop_propfind_request(); + UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); + + //fwrite(rpbuf->space, 1, rpbuf->size, stdout); + //printf("\n"); + + CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf); + if(ret == CURLE_OK) { + //printf("response\n%s\n", rpbuf->space); + + xmlDoc *doc = xmlReadMemory(rpbuf->space, rpbuf->size, url, NULL, 0); + if(!doc) { + return 1; + } + + xmlNode *xml_root = xmlDocGetRootElement(doc); + xmlNode *node = xml_root->children; + while(node) { + if(node->type == XML_ELEMENT_NODE) { + if(xstreq(node->name, "response")) { + parse_response_tag(res, node); + } + } + node = node->next; + } + } + return 0; +} + +int dav_store(DavResource *res) { + DavSession *sn = res->session; + + char *url = util_concat_path(sn->base_url, res->path); + CURL *handle = res->session->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + + DavNodeData *data = res->data; + + // store content + if(data->content) { + CURLcode ret = do_put_request(handle, data->content, data->read, data->length); + int status = 0; + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK && (status >= 200 && status < 300)) { + res->session->error = 0; + // cleanup node data + if(!data->read) { + ucx_mempool_free(sn->mp, data->content); + } + data->content = NULL; + data->read = NULL; + data->length = 0; + } else { + res->session->error = 1; + return 1; + } + } + + // store properties + if(data->set || data->remove) { + UcxBuffer *request = create_proppatch_request(data); + UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); + + CURLcode ret = do_proppatch_request(handle, request, response); + int status = 0; + curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK && status == 207) { + //printf("%s\n", response->space); + // TODO: parse response + // TODO: cleanup node data correctly + data->set = NULL; + data->remove = NULL; + } else { + res->session->error = 1; + return 1; + } + } + + return 0; +} + +char* dav_get_property_ns(DavResource *res, char *ns, char *name) { + char *property = resource_get_property(res, ns, name); + if(property && res->data->remove) { + // TODO + } else if(!property && res->data->set) { + // TODO + } + return property; +} + +void dav_set_property_ns(DavResource *res, char *ns, char *name, char *value) { + UcxAllocator *a = res->session->allocator; + + DavProperty *property = a->malloc(a->pool, sizeof(DavProperty)); + property->name = sstrdup_a(a, sstr(name)).ptr; + property->value = sstrdup_a(a, sstr(value)).ptr; + DavNamespace *namespace = a->malloc(a->pool, sizeof(DavNamespace)); + namespace->prefix = NULL; + namespace->name = sstrdup_a(a, sstr(ns)).ptr; + property->ns = namespace; + + res->data->set = ucx_list_append_a(a, res->data->set, property); +} + +void dav_remove_property_ns(DavResource *res, char *ns, char *name, char *value) { + UcxAllocator *a = res->session->allocator; + + DavProperty *property = a->malloc(a->pool, sizeof(DavProperty)); + property->name = sstrdup_a(a, sstr(name)).ptr; + property->value = NULL; + DavNamespace *namespace = a->malloc(a->pool, sizeof(DavNamespace)); + namespace->prefix = NULL; + namespace->name = sstrdup_a(a, sstr(ns)).ptr; + property->ns = namespace; + + res->data->remove = ucx_list_append_a(a, res->data->remove, property); +} + + +void dav_set_content(DavResource *res, void *stream, dav_read_func read_func) { + DavNodeData *data = res->data; + data->content = stream; + data->read = read_func; + data->length = 0; +} + +void dav_set_content_data(DavResource *res, char *content, size_t length) { + DavSession *sn = res->session; + DavNodeData *data = res->data; + data->content = content; + data->read = NULL; + data->length = length; +} + +int dav_get_content(DavResource *res, void *stream, dav_write_func write_func) { + char *url = util_concat_path(res->session->base_url, res->path); + CURL *handle = res->session->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL); + curl_easy_setopt(handle, CURLOPT_PUT, 0L); + curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); + + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_func); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, stream); + + CURLcode ret = curl_easy_perform(handle); + + int status = 0; + curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK && (status >= 200 && status < 300)) { + return 0; + } else { + return 1; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/webdav.h Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,166 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 NODE_H +#define NODE_H + +#include <inttypes.h> +#include <ucx/map.h> +#include <ucx/mempool.h> +#include <ucx/list.h> +#include <ucx/buffer.h> +#include <curl/curl.h> +#include <libxml/tree.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DavContext DavContext; +typedef struct DavSession DavSession; +typedef struct DavResource DavResource; +typedef struct DavRequest DavRequest; +typedef struct DavNamespace DavNamespace; +typedef struct DavNodeData DavNodeData; +typedef struct DavProperty DavProperty; + +typedef size_t(*dav_read_func)(void*, size_t, size_t, void*); +typedef size_t(*dav_write_func)(const void*, size_t, size_t, void*); + +struct DavNamespace { + char *prefix; + char *name; +}; + +struct DavResource { + DavSession *session; + DavResource *prev; + DavResource *next; + DavResource *parent; + DavResource *children; + char *name; + char *path; + char *href; + uint64_t contentlength; + char *contenttype; + time_t creationdate; + time_t lastmodified; + DavNodeData *data; + int iscollection; +}; + +struct DavSession { + DavContext *context; + CURL *handle; + char *base_url; + UcxMempool *mp; + UcxAllocator *allocator; + int error; +}; + +struct DavContext { + UcxMap *namespaces; +}; + +struct dav_content_data { + char *data; + size_t length; +}; + +struct dav_content_stream { + void *stream; + read_func read; +}; + +struct DavNodeData { + UcxMap *properties; + UcxList *set; + UcxList *remove; + + /* + * char* or stream + */ + void *content; + /* + * if NULL, content is a char* + */ + read_func read; + /* + * content length + */ + size_t length; +}; + +struct DavProperty { + DavNamespace *ns; + char *name; + char *value; +}; + +DavContext* dav_context_new(); +int dav_add_namespace(DavContext *context, char *prefix, char *ns); +DavNamespace* dav_get_namespace(DavContext *context, char *prefix); + +DavSession* dav_session_new(DavContext *context, char *base_url); +DavSession* dav_session_new_auth(DavContext *context, char *base_url, char *user, char *password); +void dav_session_set_auth(DavSession *sn, char *user, char *password); + +DavResource* dav_get(DavSession *sn, char *path, char *properties); + +UcxList* parse_properties_string(DavContext *context, sstr_t str); + + +DavResource* dav_resource_new(DavSession *sn, char *path); +DavResource* resource_new_href(DavSession *sn, char *href); + +void resource_add_property(DavResource *res, char *ns, char *name, char *value); +void resource_add_child(DavResource *parent, DavResource *child); +UcxKey dav_property_key(char *ns, char *name); +void resource_set_info(DavResource *res, char *href); +DavNodeData* node_data_new(DavSession *sn); + +int dav_load(DavResource *res); +int dav_store(DavResource *res); + +char* dav_get_property_ns(DavResource *res, char *ns, char *name); +void dav_set_property_ns(DavResource *res, char *ns, char *name, char *value); +void dav_remove_property_ns(DavResource *res, char *ns, char *name, char *value); + + + +void dav_set_content(DavResource *res, void *stream, dav_read_func read_func); +void dav_set_content_data(DavResource *res, char *content, size_t length); + +int dav_get_content(DavResource *res, void *stream, dav_write_func write_func); + +#ifdef __cplusplus +} +#endif + +#endif /* NODE_H */ +
--- a/suncc.mk Sat Dec 01 20:34:55 2012 +0100 +++ b/suncc.mk Mon Aug 12 14:40:19 2013 +0200 @@ -31,7 +31,7 @@ AR = ar RM = rm -CFLAGS = -g -c +CFLAGS = -g -c -xc99 COFLAGS = -o LDFLAGS = LOFLAGS = -o
--- a/ucx/Makefile Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/Makefile Mon Aug 12 14:40:19 2013 +0200 @@ -1,7 +1,7 @@ # # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. # -# Copyright 2011 Olaf Wintermann. All rights reserved. +# Copyright 2013 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: @@ -29,9 +29,10 @@ include ../$(CONF).mk # list of source files -SRC = list.c -SRC += dlist.c +SRC = utils.c +SRC += list.c SRC += map.c +SRC += properties.c SRC += mempool.c SRC += string.c SRC += test.c
--- a/ucx/allocator.c Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/allocator.c Mon Aug 12 14:40:19 2013 +0200 @@ -1,6 +1,47 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 <stdlib.h> #include "allocator.h" +UcxAllocator default_allocator = { + NULL, + ucx_default_malloc, + ucx_default_calloc, + ucx_default_realloc, + ucx_default_free +}; + +UcxAllocator *ucx_default_allocator() { + UcxAllocator *allocator = &default_allocator; + return allocator; +} + void *ucx_default_malloc(void *ignore, size_t n) { return malloc(n); } @@ -12,3 +53,7 @@ void *ucx_default_realloc(void *ignore, void *data, size_t n) { return realloc(data, n); } + +void ucx_default_free(void *ignore, void *data) { + free(data); +}
--- a/ucx/allocator.h Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/allocator.h Mon Aug 12 14:40:19 2013 +0200 @@ -1,5 +1,56 @@ -#ifndef ALLOCATOR_H -#define ALLOCATOR_H +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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. + */ +/** + * Allocator for custom memory management. + * + * An UCX allocator consists of a pointer to the memory area / pool and four + * function pointers to memory management functions operating on this memory + * area / pool. These functions shall behave equivalent to the standard libc + * functions <code>malloc(), calloc(), realloc()</code> and <code>free()</code>. + * + * The signature of the memory management functions is based on the signature + * of the respective libc function but each of them takes the pointer to the + * memory area / pool as first argument. + * + * As the pointer to the memory area / pool can be arbitrarily chosen, any data + * can be provided to the memory management functions. An UcxMempool is just + * one example. + * + * @see mempool.h + * @see UcxMap + * + * @file allocator.h + * @author Mike Becker + * @author Olaf Wintermann + */ + +#ifndef UCX_ALLOCATOR_H +#define UCX_ALLOCATOR_H #include "ucx.h" @@ -7,27 +58,111 @@ extern "C" { #endif +/** + * A function pointer to the allocators <code>malloc()</code> function. + * @see UcxAllocator + */ typedef void*(*ucx_allocator_malloc)(void *pool, size_t n); +/** + * A function pointer to the allocators <code>calloc()</code> function. + * @see UcxAllocator + */ typedef void*(*ucx_allocator_calloc)(void *pool, size_t n, size_t size); +/** + * A function pointer to the allocators <code>realloc()</code> function. + * @see UcxAllocator + */ typedef void*(*ucx_allocator_realloc)(void *pool, void *data, size_t n); +/** + * A function pointer to the allocators <code>free()</code> function. + * @see UcxAllocator + */ +typedef void(*ucx_allocator_free)(void *pool, void *data); +/** + * UCX allocator data structure containing memory management functions. + */ typedef struct { + /** Pointer to an area of memory or a complex memory pool. + * This pointer will be passed to any memory management function as first + * argument. + */ void *pool; - ucx_allocator_malloc malloc; - ucx_allocator_calloc calloc; + /** + * The <code>malloc()</code> function for this allocator. + */ + ucx_allocator_malloc malloc; + /** + * The <code>calloc()</code> function for this allocator. + */ + ucx_allocator_calloc calloc; + /** + * The <code>realloc()</code> function for this allocator. + */ ucx_allocator_realloc realloc; + /** + * The <code>free()</code> function for this allocator. + */ + ucx_allocator_free free; } UcxAllocator; +/** + * Returns a pointer to the default allocator. + * + * The default allocator contains wrappers to the standard libc memory + * management functions. Use this function to get a pointer to a globally + * available allocator. You may also define an own UcxAllocator by assigning + * #UCX_ALLOCATOR_DEFAULT to a variable and pass the address of this variable + * to any function that takes an UcxAllocator as argument. Note that using + * this function is the recommended way of passing a default allocator, thus + * it never runs out of scope. + * + * @return a pointer to the default allocator + * + * @see UCX_ALLOCATOR_DEFAULT + */ +UcxAllocator *ucx_default_allocator(); + +/** + * A wrapper for the standard libc <code>malloc()</code> function. + * @param ignore ignored (may be used by allocators for pooled memory) + * @param n argument passed to <code>malloc()</code> + * @return return value of <code>malloc()</code> + */ void *ucx_default_malloc(void *ignore, size_t n); +/** + * A wrapper for the standard libc <code>calloc()</code> function. + * @param ignore ignored (may be used by allocators for pooled memory) + * @param n argument passed to <code>calloc()</code> + * @param size argument passed to <code>calloc()</code> + * @return return value of <code>calloc()</code> + */ void *ucx_default_calloc(void *ignore, size_t n, size_t size); +/** + * A wrapper for the standard libc <code>realloc()</code> function. + * @param ignore ignored (may be used by allocators for pooled memory) + * @param data argumend passed to <code>realloc()</code> + * @param n argument passed to <code>realloc()</code> + * @return return value of <code>realloc()</code> + */ void *ucx_default_realloc(void *ignore, void *data, size_t n); +/** + * A wrapper for the standard libc <code>free()</code> function. + * @param ignore ignored (may be used by allocators for pooled memory) + * @param data argument passed to <code>free()</code> + */ +void ucx_default_free(void *ignore, void *data); +/** + * Convenient macro for a default allocator <code>struct</code> definition. + */ #define UCX_ALLOCATOR_DEFAULT {NULL, \ - ucx_default_malloc, ucx_default_calloc, ucx_default_realloc} + ucx_default_malloc, ucx_default_calloc, ucx_default_realloc, \ + ucx_default_free } #ifdef __cplusplus } #endif -#endif /* ALLOCATOR_H */ +#endif /* UCX_ALLOCATOR_H */
--- a/ucx/buffer.c Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/buffer.c Mon Aug 12 14:40:19 2013 +0200 @@ -1,3 +1,31 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 "buffer.h" #include <stdarg.h> #include <stdlib.h> @@ -8,7 +36,7 @@ if (buffer) { buffer->flags = flags; if (!space) { - buffer->space = malloc(size); + buffer->space = (char*)malloc(size); if (!buffer->space) { free(buffer); return NULL; @@ -16,7 +44,7 @@ memset(buffer->space, 0, size); buffer->flags |= UCX_BUFFER_AUTOFREE; } else { - buffer->space = space; + buffer->space = (char*)space; } buffer->capacity = size; buffer->size = 0; @@ -48,7 +76,7 @@ UcxBuffer *dst = (UcxBuffer*) malloc(sizeof(UcxBuffer)); if (dst) { - dst->space = malloc(length); + dst->space = (char*)malloc(length); if (!dst->space) { free(dst); return NULL; @@ -63,7 +91,7 @@ } int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence) { - off_t npos; + size_t npos = 0; switch (whence) { case SEEK_SET: npos = 0; @@ -78,7 +106,7 @@ npos += offset; - if (npos < 0 || npos > buffer->size) { + if (npos > buffer->size) { return -1; } else { buffer->pos = npos; @@ -95,7 +123,7 @@ size_t newcap = buffer->capacity; while (buffer->pos + len > newcap) newcap <<= 1; - char *newspace = realloc(buffer->space, newcap); + char *newspace = (char*)realloc(buffer->space, newcap); if (newspace) { memset(newspace+buffer->size, 0, newcap-buffer->size); buffer->space = newspace; @@ -185,7 +213,7 @@ size_t ucx_buffer_generic_copy(void *s1, void *s2, read_func readfnc, write_func writefnc, size_t bufsize) { size_t ncp = 0; - char *buf = malloc(bufsize); + char *buf = (char*)malloc(bufsize); if(buf == NULL) { return 0; } @@ -202,3 +230,31 @@ free(buf); return ncp; } + +size_t ucx_buffer_generic_ncopy(void *s1, void *s2, + read_func readfnc, write_func writefnc, size_t bufsize, size_t n) { + if(n == 0) { + return 0; + } + + size_t ncp = 0; + char *buf = (char*)malloc(bufsize); + if(buf == NULL) { + return 0; + } + + size_t r; + size_t rn = bufsize > n ? n : bufsize; + while((r = readfnc(buf, 1, rn, s1)) != 0) { + r = writefnc(buf, 1, r, s2); + ncp += r; + n -= r; + rn = bufsize > n ? n : bufsize; + if(r == 0 || n == 0) { + break; + } + } + + free(buf); + return ncp; +}
--- a/ucx/buffer.h Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/buffer.h Mon Aug 12 14:40:19 2013 +0200 @@ -1,5 +1,33 @@ -#ifndef BUFFER_H -#define BUFFER_H +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 UCX_BUFFER_H +#define UCX_BUFFER_H #include "ucx.h" #include <sys/types.h> @@ -9,7 +37,9 @@ extern "C" { #endif +/* no autoextend or autofree behaviour */ #define UCX_BUFFER_DEFAULT 0x00 +/* the buffer shall free the occupied memory space */ #define UCX_BUFFER_AUTOFREE 0x01 /* the buffer may automatically double its size on write operations */ #define UCX_BUFFER_AUTOEXTEND 0x02 @@ -44,13 +74,16 @@ * SEEK_CUR marks the current position * SEEK_END marks the first 0-byte in the buffer * - * ucx_memseek returns 0 on success and -1 if the new position is beyond the + * ucx_buffer_seek returns 0 on success and -1 if the new position is beyond the * bounds of the allocated buffer. In that case the position of the buffer * remains unchanged. * */ int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence); +#define ucx_buffer_clear(buffer) memset(buffer->space, 0, buffer->size); \ + buffer->size = 0; buffer->pos = 0; + /* * returns non-zero, if the current buffer position has exceeded the last * available byte of the underlying buffer @@ -67,11 +100,6 @@ size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems, UcxBuffer *buffer); -/* when autoextend is enabled, ensure you get the latest pointer to the data */ -//define ucx_buffer_write(data, itemsize, nitems, buffer) \ -// ucx_bufio(data, itemsize, nitems, buffer, 0) -//define ucx_buffer_read(data, itemsize, nitems, buffer) \ -// ucx_bufio(data, itemsize, nitems, buffer, 1) int ucx_buffer_putc(UcxBuffer *b, int c); int ucx_buffer_getc(UcxBuffer *b); @@ -85,16 +113,22 @@ size_t ucx_buffer_generic_copy(void *s1, void *s2, read_func r, write_func w, size_t bufsize); +size_t ucx_buffer_generic_ncopy(void *s1, void *s2, read_func r, write_func w, + size_t bufsize, size_t n); -#define UCX_DEFAULT_BUFFER_SIZE 0x4000000 +#define UCX_DEFAULT_BUFFER_SIZE 0x1000 #define ucx_buffer_copy(s1,s2,r,w) \ ucx_buffer_generic_copy(s1, s2, (read_func)r, (write_func)w, \ UCX_DEFAULT_BUFFER_SIZE) +#define ucx_buffer_ncopy(s1,s2,r,w, n) \ + ucx_buffer_generic_ncopy(s1, s2, (read_func)r, (write_func)w, \ + UCX_DEFAULT_BUFFER_SIZE, n) + #ifdef __cplusplus } #endif -#endif /* BUFFER_H */ +#endif /* UCX_BUFFER_H */
--- a/ucx/dlist.c Sat Dec 01 20:34:55 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,234 +0,0 @@ -#include "dlist.h" - -UcxDlist *restrict ucx_dlist_clone(UcxDlist *restrict l, - copy_func fnc, void *data) { - UcxDlist *ret = NULL; - while (l != NULL) { - if (fnc != NULL) { - ret = ucx_dlist_append(ret, fnc(l->data, data)); - } else { - ret = ucx_dlist_append(ret, l->data); - } - l = l->next; - } - return ret; -} - -int ucx_dlist_equals(const UcxDlist *l1, const UcxDlist *l2, - cmp_func fnc, void* data) { - if (l1 == l2) return 1; - - while (l1 != NULL && l2 != NULL) { - if (fnc == NULL) { - if (l1->data != l2->data) return 0; - } else { - if (fnc(l1->data, l2->data, data) != 0) return 0; - } - l1 = l1->next; - l2 = l2->next; - } - - return (l1 == NULL && l2 == NULL); -} - -void ucx_dlist_free(UcxDlist *l) { - UcxDlist *e = l, *f; - while (e != NULL) { - f = e; - e = e->next; - free(f); - } -} - -UcxDlist *ucx_dlist_append(UcxDlist *l, void *data) { - UcxDlist *nl = (UcxDlist*) malloc(sizeof(UcxDlist)); - if (nl == NULL) return NULL; - - nl->data = data; - nl->next = NULL; - if (l == NULL) { - nl->prev = NULL; - return nl; - } else { - UcxDlist *t = ucx_dlist_last(l); - t->next = nl; - nl->prev = t; - return l; - } -} - -UcxDlist *ucx_dlist_prepend(UcxDlist *l, void *data) { - UcxDlist *nl = ucx_dlist_append(NULL, data); - if (nl == NULL) return NULL; - - if (l != NULL) { - nl->next = l; - l->prev = nl; - } - return nl; -} - -UcxDlist *ucx_dlist_concat(UcxDlist *restrict l1, UcxDlist *restrict l2) { - if (l1 == NULL) { - return l2; - } else { - UcxDlist *last = ucx_dlist_last(l1); - last->next = l2; - l2->prev = last; - return l1; - } -} - -UcxDlist *ucx_dlist_last(const UcxDlist *l) { - if (l == NULL) return NULL; - - const UcxDlist *e = l; - while (e->next != NULL) { - e = e->next; - } - return (UcxDlist*)e; -} - -UcxDlist *ucx_dlist_get(const UcxDlist *l, int index) { - if (l == NULL) return NULL; - - const UcxDlist *e = l; - while (e->next != NULL && index > 0) { - e = e->next; - index--; - } - - return (UcxDlist*)(index == 0 ? e : NULL); -} - -size_t ucx_dlist_size(const UcxDlist *l) { - if (l == NULL) return 0; - - const UcxDlist *e = l; - size_t s = 1; - while (e->next != NULL) { - e = e->next; - s++; - } - - return s; -} - -UcxDlist *ucx_dlist_sort_merge(int length, - UcxDlist* restrict ls, UcxDlist* restrict le, UcxDlist* restrict re, - cmp_func fnc, void* data) { - - UcxDlist** sorted = (UcxDlist**) malloc(sizeof(UcxDlist*)*length); - UcxDlist *rc, *lc; - - lc = ls; rc = le; - int n = 0; - while (lc && lc != le && rc != re) { - if (fnc(lc->data, rc->data, data) <= 0) { - sorted[n] = lc; - lc = lc->next; - } else { - sorted[n] = rc; - rc = rc->next; - } - n++; - } - while (lc && lc != le) { - sorted[n] = lc; - lc = lc->next; - n++; - } - while (rc && rc != re) { - sorted[n] = rc; - rc = rc->next; - n++; - } - - // Update pointer - sorted[0]->prev = NULL; - for (int i = 0 ; i < length-1 ; i++) { - sorted[i]->next = sorted[i+1]; - sorted[i+1]->prev = sorted[i]; - } - sorted[length-1]->next = NULL; - - UcxDlist *ret = sorted[0]; - free(sorted); - return ret; -} - -UcxDlist *ucx_dlist_sort(UcxDlist *l, cmp_func fnc, void *data) { - if (l == NULL) { - return NULL; - } - - UcxDlist *lc; - int ln = 1; - - UcxDlist *restrict ls = l, *restrict le, *restrict re; - lc = ls; - while (lc->next != NULL && fnc(lc->next->data, lc->data, data) > 0) { - lc = lc->next; - ln++; - } - le = lc->next; - - if (le == NULL) { - return l; // this list is already sorted :) - } else { - UcxDlist *rc; - int rn = 1; - rc = le; - while (rc->next != NULL && fnc(rc->next->data, rc->data, data) > 0) { - rc = rc->next; - rn++; - } - re = rc->next; - - // Something left? Sort it! - UcxDlist *remainder = re; - size_t remainder_length = ucx_dlist_size(remainder); - if (remainder != NULL) { - remainder = ucx_dlist_sort(remainder, fnc, data); - } - - // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them - UcxDlist *sorted = ucx_dlist_sort_merge(ln+rn, - ls, le, re, - fnc, data); - - // merge sorted list with (also sorted) remainder - l = ucx_dlist_sort_merge(ln+rn+remainder_length, - sorted, remainder, NULL, fnc, data); - - return l; - } -} - -/* dlist specific functions */ -UcxDlist *ucx_dlist_first(const UcxDlist *l) { - if (l == NULL) return NULL; - - const UcxDlist *e = l; - while (e->prev != NULL) { - e = e->prev; - } - return (UcxDlist *)e; -} - -UcxDlist *ucx_dlist_remove(UcxDlist *l, UcxDlist *e) { - if (e->prev == NULL) { - if(e->next != NULL) { - e->next->prev = NULL; - l = e->next; - } else { - l = NULL; - } - - } else { - e->prev->next = e->next; - e->next->prev = e->prev; - } - free(e); - return l; -}
--- a/ucx/dlist.h Sat Dec 01 20:34:55 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/* - * - */ - -#ifndef DLIST_H -#define DLIST_H - -#include "ucx.h" -#include <stddef.h> - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct UcxDlist UcxDlist; -struct UcxDlist { - void *data; - UcxDlist *restrict next; - UcxDlist *restrict prev; -}; - -UcxDlist *restrict ucx_dlist_clone(UcxDlist *restrict l, - copy_func fnc, void* data); -int ucx_dlist_equals(const UcxDlist *l1, const UcxDlist *l2, - cmp_func fnc, void* data); - -void ucx_dlist_free(UcxDlist *l); -UcxDlist *ucx_dlist_append(UcxDlist *l, void *data); -UcxDlist *ucx_dlist_prepend(UcxDlist *l, void *data); -UcxDlist *ucx_dlist_concat(UcxDlist *restrict l1, UcxDlist *restrict l2); -UcxDlist *ucx_dlist_last(const UcxDlist *l); -UcxDlist *ucx_dlist_get(const UcxDlist *l, int index); -size_t ucx_dlist_size(const UcxDlist *l); - -UcxDlist *ucx_dlist_sort(UcxDlist *l, cmp_func fnc, void *data); - -/* dlist specific functions */ -UcxDlist *ucx_dlist_first(const UcxDlist *l); -UcxDlist *ucx_dlist_remove(UcxDlist *l, UcxDlist *e); - -#ifdef __cplusplus -} -#endif - -#endif /* DLIST_H */ -
--- a/ucx/list.c Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/list.c Mon Aug 12 14:40:19 2013 +0200 @@ -1,13 +1,45 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 "list.h" -UcxList *restrict ucx_list_clone(UcxList *restrict l, +UcxList *ucx_list_clone(UcxList *l, copy_func fnc, void *data) { + return ucx_list_clone_a(ucx_default_allocator(), l, fnc, data); +} + +UcxList *ucx_list_clone_a(UcxAllocator *alloc, UcxList *l, copy_func fnc, void *data) { UcxList *ret = NULL; - while (l != NULL) { - if (fnc != NULL) { - ret = ucx_list_append(ret, fnc(l->data, data)); + while (l) { + if (fnc) { + ret = ucx_list_append_a(alloc, ret, fnc(l->data, data)); } else { - ret = ucx_list_append(ret, l->data); + ret = ucx_list_append_a(alloc, ret, l->data); } l = l->next; } @@ -32,46 +64,67 @@ } void ucx_list_free(UcxList *l) { + ucx_list_free_a(ucx_default_allocator(), l); +} + +void ucx_list_free_a(UcxAllocator *alloc, UcxList *l) { UcxList *e = l, *f; while (e != NULL) { f = e; e = e->next; - free(f); + alloc->free(alloc->pool, f); } } UcxList *ucx_list_append(UcxList *l, void *data) { - UcxList *nl = (UcxList*) malloc(sizeof(UcxList)); - if (nl == NULL) return NULL; + return ucx_list_append_a(ucx_default_allocator(), l, data); +} + +UcxList *ucx_list_append_a(UcxAllocator *alloc, UcxList *l, void *data) { + UcxList *nl = (UcxList*) alloc->malloc(alloc->pool, sizeof(UcxList)); + if (!nl) { + return NULL; + } nl->data = data; nl->next = NULL; - if (l == NULL) { - return nl; - } else { + if (l) { UcxList *t = ucx_list_last(l); t->next = nl; + nl->prev = t; return l; + } else { + nl->prev = NULL; + return nl; } } UcxList *ucx_list_prepend(UcxList *l, void *data) { - UcxList *nl = ucx_list_append(NULL, data); - if (nl == NULL) return NULL; + return ucx_list_prepend_a(ucx_default_allocator(), l, data); +} + +UcxList *ucx_list_prepend_a(UcxAllocator *alloc, UcxList *l, void *data) { + UcxList *nl = ucx_list_append_a(alloc, NULL, data); + if (!nl) { + return NULL; + } + l = ucx_list_first(l); - if (l != NULL) { + if (l) { nl->next = l; + l->prev = nl; } return nl; } -UcxList *ucx_list_concat(UcxList *restrict l1, UcxList *restrict l2) { - if (l1 == NULL) { - return l2; - } else { +UcxList *ucx_list_concat(UcxList *l1, UcxList *l2) { + if (l1) { UcxList *last = ucx_list_last(l1); last->next = l2; + l2->prev = last; return l1; + } else { + return l2; } } @@ -85,11 +138,23 @@ return (UcxList*)e; } +ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem) { + ssize_t index = 0; + while (list) { + if (list == elem) { + return index; + } + list = list->next; + index++; + } + return -1; +} + UcxList *ucx_list_get(const UcxList *l, int index) { if (l == NULL) return NULL; const UcxList *e = l; - while (e->next != NULL && index > 0) { + while (e->next && index > 0) { e = e->next; index--; } @@ -97,6 +162,27 @@ return (UcxList*)(index == 0 ? e : NULL); } +ssize_t ucx_list_find(UcxList *l, void *elem, cmp_func fnc, void *cmpdata) { + ssize_t index = 0; + UCX_FOREACH(e, l) { + if (fnc) { + if (fnc(elem, e->data, cmpdata) == 0) { + return index; + } + } else { + if (elem == e->data) { + return index; + } + } + index++; + } + return -1; +} + +int ucx_list_contains(UcxList *l, void *elem, cmp_func fnc, void *cmpdata) { + return ucx_list_find(l, elem, fnc, cmpdata) > -1; +} + size_t ucx_list_size(const UcxList *l) { if (l == NULL) return 0; @@ -141,8 +227,10 @@ } // Update pointer + sorted[0]->prev = NULL; for (int i = 0 ; i < length-1 ; i++) { sorted[i]->next = sorted[i+1]; + sorted[i+1]->prev = sorted[i]; } sorted[length-1]->next = NULL; @@ -199,21 +287,35 @@ } } -/* list specific functions */ +UcxList *ucx_list_first(const UcxList *l) { + if (!l) { + return NULL; + } + + const UcxList *e = l; + while (e->prev) { + e = e->prev; + } + return (UcxList *)e; +} + UcxList *ucx_list_remove(UcxList *l, UcxList *e) { - if (e == l) { - l = e->next; - free(e); + return ucx_list_remove_a(ucx_default_allocator(), l, e); +} + +UcxList *ucx_list_remove_a(UcxAllocator *alloc, UcxList *l, UcxList *e) { + if (e->prev == NULL) { + if(e->next != NULL) { + e->next->prev = NULL; + l = e->next; + } else { + l = NULL; + } + } else { - UcxList *f = l; - while (f->next != NULL && f->next != e) { - f = f->next; - } - /* perform remove if this element is found in this list */ - if (f->next == e) { - f->next = e->next; - free(e); - } + e->prev->next = e->next; + e->next->prev = e->prev; } + alloc->free(alloc->pool, e); return l; }
--- a/ucx/list.h Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/list.h Mon Aug 12 14:40:19 2013 +0200 @@ -1,44 +1,362 @@ /* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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. + */ +/** + * Doubly linked list implementation. * + * @file list.h + * @author Mike Becker + * @author Olaf Wintermann */ -#ifndef LIST_H -#define LIST_H +#ifndef UCX_LIST_H +#define UCX_LIST_H #include "ucx.h" -#include <stddef.h> +#include "allocator.h" #ifdef __cplusplus extern "C" { #endif - + +/** + * Loop statement for UCX lists. + * + * The first argument is a pointer to the list. In most cases this will be the + * pointer to the first element of the list, but it may also be an arbitrary + * element of the list. The iteration will then start with that element. + * + * The second argument is the name of the iteration variable. The scope of + * this variable is limited to the <code>UCX_FOREACH</code> statement. + * + * @param list The first element of the list + * @param elem The variable name of the element + */ +#define UCX_FOREACH(elem,list) \ + for (UcxList* elem = list ; elem != NULL ; elem = elem->next) + +/** + * UCX list type. + * @see UcxList + */ typedef struct UcxList UcxList; +/** + * UCX list structure. + */ struct UcxList { + /** + * List element payload. + */ void *data; + /** + * Pointer to the next list element or <code>NULL</code>, if this is the + * last element. + */ UcxList *next; + /** + * Pointer to the previous list element or <code>NULL</code>, if this is + * the first element. + */ + UcxList *prev; }; -UcxList *restrict ucx_list_clone(UcxList *restrict l, - copy_func fnc, void *data); -int ucx_list_equals(const UcxList *l1, const UcxList *l2, - cmp_func fnc, void *data); +/** + * Creates an element-wise copy of a list. + * + * This function clones the specified list by creating new list elements and + * copying the data with the specified copy_func(). If no copy_func() is + * specified, a shallow copy is created and the new list will reference the + * same data as the source list. + * + * @param list the list to copy + * @param cpyfnc a pointer to the function that shall copy an element (may be + * <code>NULL</code>) + * @param data additional data for the copy_func() + * @return a pointer to the copy + */ +UcxList *ucx_list_clone(UcxList *list, copy_func cpyfnc, void* data); +/** + * Creates an element-wise copy of a list using an UcxAllocator. + * + * See ucx_list_clone() for details. + * + * Keep in mind, that you might want to pass the allocator as an (part of the) + * argument for the <code>data</code> parameter, if you want the copy_func() to + * make use of the allocator. + * + * @param allocator the allocator to use + * @param list the list to copy + * @param cpyfnc a pointer to the function that shall copy an element (may be + * <code>NULL</code>) + * @param data additional data for the copy_func() + * @return a pointer to the copy + * @see ucx_list_clone() + */ +UcxList *ucx_list_clone_a(UcxAllocator *allocator, UcxList *list, + copy_func cpyfnc, void* data); + +/** + * Compares two UCX lists element-wise by using a compare function. + * + * Each element of the two specified lists are compared by using the specified + * compare function and the additional data. The type and content of this + * additional data depends on the cmp_func() used. + * + * If the list pointers denote elements within a list, the lists are compared + * starting with the denoted elements. Thus any previous elements are not taken + * into account. This might be useful to check, if certain list tails match + * each other. + * + * @param list1 the first list + * @param list2 the second list + * @param cmpfnc the compare function + * @param data additional data for the compare function + * @return 1, if and only if the two lists equal element-wise, 0 otherwise + */ +int ucx_list_equals(const UcxList *list1, const UcxList *list2, + cmp_func cmpfnc, void* data); -void ucx_list_free(UcxList *l); -UcxList *ucx_list_append(UcxList *l, void *data); -UcxList *ucx_list_prepend(UcxList *l, void *data); -UcxList *ucx_list_concat(UcxList *restrict l1, UcxList *restrict l2); -UcxList *ucx_list_last(const UcxList *l); -UcxList *ucx_list_get(const UcxList *l, int index); -size_t ucx_list_size(const UcxList *l); +/** + * Destroys the entire list. + * + * The members of the list are not automatically freed, so ensure they are + * otherwise referenced or a memory leak will occur. + * + * <b>Caution:</b> the argument <b>MUST</b> denote an entire list (i.e. a call + * to ucx_list_first() on the argument must return the argument itself) + * + * @param list the list to free + */ +void ucx_list_free(UcxList *list); +/** + * Destroys the entire list using an UcxAllocator. + * + * See ucx_list_free() for details. + * + * @param allocator the allocator to use + * @param list the list to free + * @see ucx_list_free() + */ +void ucx_list_free_a(UcxAllocator *allocator, UcxList *list); +/** + * Inserts an element at the end of the list. + * + * This is generally an O(n) operation, as the end of the list is seeked with + * ucx_list_last(). + * + * @param list the list where to append the data, or <code>NULL</code> to + * create a new list + * @param data the data to insert + * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to + * the newly created list otherwise + */ +UcxList *ucx_list_append(UcxList *list, void *data); +/** + * Inserts an element at the end of the list using an UcxAllocator. + * + * See ucx_list_append() for details. + * + * @param allocator the allocator to use + * @param list the list where to append the data, or <code>NULL</code> to + * create a new list + * @param data the data to insert + * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to + * the newly created list otherwise + * @see ucx_list_append() + */ +UcxList *ucx_list_append_a(UcxAllocator *allocator, UcxList *list, void *data); +/** + * Inserts an element at the beginning of the list. + * + * You <i>should</i> overwrite the old list pointer by calling + * <code>mylist = ucx_list_prepend(mylist, mydata);</code>. However, you may + * also perform successive calls of ucx_list_prepend() on the same list pointer, + * as this function always searchs for the head of the list with + * ucx_list_first(). + * + * @param list the list where to insert the data or <code>NULL</code> to create + * a new list + * @param data the data to insert + * @return a pointer to the new list head + */ +UcxList *ucx_list_prepend(UcxList *list, void *data); +/** + * Inserts an element at the beginning of the list using an UcxAllocator. + * + * See ucx_list_prepend() for details. + * + * @param allocator the allocator to use + * @param list the list where to insert the data or <code>NULL</code> to create + * a new list + * @param data the data to insert + * @return a pointer to the new list head + * @see ucx_list_prepend() + */ +UcxList *ucx_list_prepend_a(UcxAllocator *allocator, UcxList *list, void *data); +/** + * Concatenates two lists. + * + * Either of the two arguments may be <code>NULL</code>. + * + * This function modifies the references to the next/previous element of + * the last/first element of <code>list1</code>/<code> + * list2</code>. + * + * @param list1 first list + * @param list2 second list + * @return if <code>list1</code> is <code>NULL</code>, <code>list2</code> is + * returned, otherwise <code>list1</code> is returned + */ +UcxList *ucx_list_concat(UcxList *list1, UcxList *list2); +/** + * Returns the first element of a list. + * + * If the argument is the list pointer, it is directly returned. Otherwise + * this function traverses to the first element of the list and returns the + * list pointer. + * + * @param elem one element of the list + * @return the first element of the list, the specified element is a member of + */ +UcxList *ucx_list_first(const UcxList *elem); +/** + * Returns the last element of a list. + * + * If the argument has no successor, it is the last element and therefore + * directly returned. Otherwise this function traverses to the last element of + * the list and returns it. + * + * @param elem one element of the list + * @return the last element of the list, the specified element is a member of + */ +UcxList *ucx_list_last(const UcxList *elem); +/** + * Returns the list element at the specified index. + * + * @param list the list to retrieve the element from + * @param index index of the element to return + * @return the element at the specified index or <code>NULL</code>, if the + * index is greater than the list size + */ +UcxList *ucx_list_get(const UcxList *list, int index); +/** + * Returns the index of an element. + * + * @param list the list where to search for the element + * @param elem the element to find + * @return the index of the element or -1 if the list does not contain the + * element + */ +ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem); +/** + * Returns the element count of the list. + * + * @param list the list whose elements are counted + * @return the element count + */ +size_t ucx_list_size(const UcxList *list); +/** + * Returns the index of an element containing the specified data. + * + * This function uses a cmp_func() to compare the data of each list element + * with the specified data. If no cmp_func is provided, the pointers are + * compared. + * + * If the list contains the data more than once, the index of the first + * occurrence is returned. + * + * @param list the list where to search for the data + * @param elem the element data + * @param cmpfnc the compare function + * @param data additional data for the compare function + * @return the index of the element containing the specified data or -1 if the + * data is not found in this list + */ +ssize_t ucx_list_find(UcxList *list, void *elem, cmp_func cmpfnc, void *data); +/** + * Checks, if a list contains a specific element. + * + * An element is found, if ucx_list_find() returns a value greater than -1. + * + * @param list the list where to search for the data + * @param elem the element data + * @param cmpfnc the compare function + * @param data additional data for the compare function + * @return 1, if and only if the list contains the specified element data + * @see ucx_list_find() + */ +int ucx_list_contains(UcxList *list, void *elem, cmp_func cmpfnc, void *data); -UcxList *ucx_list_sort(UcxList *l, cmp_func fnc, void *data); +/** + * Sorts an UcxList with natural merge sort. + * + * This function uses O(n) additional temporary memory for merge operations + * that is automatically freed after each merge. + * + * As the head of the list might change, you <b>MUST</b> call this function + * as follows: <code>mylist = ucx_list_sort(mylist, mycmpfnc, mydata);</code>. + * + * @param list the list to sort + * @param cmpfnc the function that shall be used to compare the element data + * @param data additional data for the cmp_func() + * @return the sorted list + */ +UcxList *ucx_list_sort(UcxList *list, cmp_func cmpfnc, void *data); -/* list specific functions */ -UcxList *ucx_list_remove(UcxList *l, UcxList *e); +/** + * Removes an element from the list. + * + * If the first element is removed, the list pointer changes. So it is + * <i>highly recommended</i> to <i>always</i> update the pointer by calling + * <code>mylist = ucx_list_remove(mylist, myelem);</code>. + * + * @param list the list from which the element shall be removed + * @param element the element to removed + * @return returns the updated list pointer or <code>NULL</code>, if the list + * is now empty + */ +UcxList *ucx_list_remove(UcxList *list, UcxList *element); +/** + * Removes an element from the list using an UcxAllocator. + * + * See ucx_list_remove() for details. + * + * @param allocator the allocator to use + * @param list the list from which the element shall be removed + * @param element the element to removed + * @return returns the updated list pointer or <code>NULL</code>, if the list + * @see ucx_list_remove() + */ +UcxList *ucx_list_remove_a(UcxAllocator *allocator, UcxList *list, + UcxList *element); #ifdef __cplusplus } #endif -#endif /* LIST_H */ +#endif /* UCX_LIST_H */
--- a/ucx/logging.c Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/logging.c Mon Aug 12 14:40:19 2013 +0200 @@ -1,20 +1,102 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 "logging.h" #include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <time.h> -UcxLogger *ucx_logger_new(FILE *stream, unsigned int level) { +UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask) { UcxLogger *logger = (UcxLogger*) malloc(sizeof(UcxLogger)); if (logger != NULL) { logger->stream = stream; + logger->writer = (write_func)fwrite; + logger->dateformat = (char*) "%F %T %z "; logger->level = level; + logger->mask = mask; + logger->levels = ucx_map_new(8); + + unsigned int l; + l = UCX_LOGGER_ERROR; + ucx_map_int_put(logger->levels, l, (void*) "[ERROR]"); + l = UCX_LOGGER_WARN; + ucx_map_int_put(logger->levels, l, (void*) "[WARNING]"); + l = UCX_LOGGER_INFO; + ucx_map_int_put(logger->levels, l, (void*) "[INFO]"); + l = UCX_LOGGER_TRACE; + ucx_map_int_put(logger->levels, l, (void*) "[TRACE]"); } return logger; } -void ucx_logger_log(UcxLogger *logger, unsigned int level, - const sstr_t message) { +void ucx_logger_free(UcxLogger *logger) { + ucx_map_free(logger->levels); + free(logger); +} + +void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file, + const unsigned int line, const char *format, ...) { if (level <= logger->level) { - fwrite(message.ptr, 1, message.length, logger->stream); - fflush(logger->stream); + const size_t max = 4096; // estimated maximum message length + char msg[max]; + char *text; + size_t k = 0; + size_t n; + + if ((logger->mask & UCX_LOGGER_LEVEL) > 0) { + text = (char*) ucx_map_int_get(logger->levels, level); + n = strlen(text); + memcpy(msg+k, text, n); + k += n; + msg[k++] = ' '; + } + if ((logger->mask & UCX_LOGGER_TIMESTAMP) > 0) { + time_t now = time(NULL); + k += strftime(msg+k, 128, logger->dateformat, localtime(&now)); + } + if ((logger->mask & UCX_LOGGER_SOURCE) > 0) { + n = strlen(file); + memcpy(msg+k, file, n); + k += n; + k += sprintf(msg+k, ":%d ", line); + } + + msg[k++] = '-'; msg[k++] = ' '; + + va_list args; + va_start (args, format); + k += vsnprintf(msg+k, max-k-1, format, args); + va_end (args); + + msg[k++] = '\n'; + + logger->writer(msg, 1, k, logger->stream); } }
--- a/ucx/logging.h Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/logging.h Mon Aug 12 14:40:19 2013 +0200 @@ -1,7 +1,41 @@ -#ifndef LOGGING_H -#define LOGGING_H +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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. + */ +/** + * Logging API. + * + * @file logging.h + * @author Mike Becker, Olaf Wintermann + */ +#ifndef UCX_LOGGING_H +#define UCX_LOGGING_H #include "ucx.h" +#include "map.h" #include "string.h" #include <stdio.h> @@ -10,28 +44,174 @@ #endif /* leave enough space for custom log levels */ -#define UCX_LOGGER_ERROR 0x00 -#define UCX_LOGGER_WARN 0x10 -#define UCX_LOGGER_INFO 0x20 -#define UCX_LOGGER_TRACE 0x30 +/** Log level for error messages. */ +#define UCX_LOGGER_ERROR 0x00 +/** Log level for warning messages. */ +#define UCX_LOGGER_WARN 0x10 +/** Log level for information messages. */ +#define UCX_LOGGER_INFO 0x20 +/** Log level for debug messages. */ +#define UCX_LOGGER_DEBUG 0x30 +/** Log level for trace messages. */ +#define UCX_LOGGER_TRACE 0x40 + +/** + * Output flag for the log level. + * If this flag is set, the log message will contain the log level. + * @see UcxLogger.mask + */ +#define UCX_LOGGER_LEVEL 0x01 +/** + * Output flag for the timestmap. + * If this flag is set, the log message will contain the timestmap. + * @see UcxLogger.mask + */ +#define UCX_LOGGER_TIMESTAMP 0x02 +/** + * Output flag for the source. + * If this flag is set, the log message will contain the source file and line + * number. + * @see UcxLogger.mask + */ +#define UCX_LOGGER_SOURCE 0x04 +/** + * The UCX Logger object. + */ typedef struct { - FILE *stream; + /** The stream this logger writes its messages to.*/ + void *stream; + /** + * The write function that shall be used. + * For standard file or stdout loggers this might be standard fwrite + * (default). + */ + write_func writer; + /** + * The date format for timestamp outputs + * (default: <code>"%F %T %z "</code>). + * @see UCX_LOGGER_TIMESTAMP + */ + char *dateformat; + /** + * The level, this logger operates on. + * If a log command is issued, the message will only be logged, if the log + * level of the message is less or equal than the log level of the logger. + */ unsigned int level; + /** + * A configuration mask for automatic output. + * For each flag that is set, the logger automatically outputs some extra + * information like the timestamp or the source file and line number. + * See the documentation for the flags for details. + */ + unsigned int mask; + /** + * A map of valid log levels for this logger. + * + * The keys represent all valid log levels and the values provide string + * representations, that are used, if the UCX_LOGGER_LEVEL flag is set. + * + * The exact data types are <code>unsigned int</code> for the key and + * <code>const char*</code> for the value. + * + * @see UCX_LOGGER_LEVEL + */ + UcxMap* levels; } UcxLogger; -UcxLogger *ucx_logger_new(FILE *stream, unsigned int level); -/* neither provide a free function nor a parameter for an allocator */ +/** + * Creates a new logger. + * @param stream the stream, which the logger shall write to + * @param level the level on which the logger shall operate + * @param mask configuration mask (cf. UcxLogger.mask) + * @return a new logger object + */ +UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask); +/** + * Destroys the logger. + * + * The map containing the valid log levels is also automatically destroyed. + * + * @param logger the logger to destroy + */ +void ucx_logger_free(UcxLogger* logger); + +/** + * Internal log function - use macros instead. + * + * This function uses the <code>format</code> and variadic arguments for a + * printf()-style output of the log message. + * + * Dependent on the UcxLogger.mask some information is prepended. The complete + * format is: + * + * <code>[LEVEL] [TIMESTAMP] [SOURCEFILE]:[LINENO] message</code> + * + * @param logger the logger to use + * @param level the level to log on + * @param file information about the source file + * @param line information about the source line number + * @param format format string + * @param ... arguments + * @see ucx_logger_log() + */ +void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file, + const unsigned int line, const char* format, ...); -void ucx_logger_log(UcxLogger *logger, unsigned int level, - const sstr_t message); -#define ucx_logger_error(l,m) ucx_logger_log(l, UCX_LOGGER_ERROR, m) -#define ucx_logger_info(l,m) ucx_logger_log(l, UCX_LOGGER_INFO, m) -#define ucx_logger_warn(l,m) ucx_logger_log(l, UCX_LOGGER_WARN, m) -#define ucx_logger_trace(l,m) ucx_logger_log(l, UCX_LOGGER_TRACE, m) +/** + * Logs a message at the specified level. + * @param logger the logger to use + * @param level the level to log the message on + * @param ... format string and arguments + * @see ucx_logger_logf() + */ +#define ucx_logger_log(logger, level, ...) \ + ucx_logger_logf(logger, level, __FILE__, __LINE__, __VA_ARGS__) + +/** + * Shortcut for logging an error message. + * @param logger the logger to use + * @param ... format string and arguments + * @see ucx_logger_logf() + */ +#define ucx_logger_error(logger, ...) \ + ucx_logger_log(logger, UCX_LOGGER_ERROR, __VA_ARGS__) +/** + * Shortcut for logging an information message. + * @param logger the logger to use + * @param ... format string and arguments + * @see ucx_logger_logf() + */ +#define ucx_logger_info(logger, ...) \ + ucx_logger_log(logger, UCX_LOGGER_INFO, __VA_ARGS__) +/** + * Shortcut for logging a warning message. + * @param logger the logger to use + * @param ... format string and arguments + * @see ucx_logger_logf() + */ +#define ucx_logger_warn(logger, ...) \ + ucx_logger_log(logger, UCX_LOGGER_WARN, __VA_ARGS__) +/** + * Shortcut for logging a debug message. + * @param logger the logger to use + * @param ... format string and arguments + * @see ucx_logger_logf() + */ +#define ucx_logger_debug(logger, ...) \ + ucx_logger_log(logger, UCX_LOGGER_DEBUG, __VA_ARGS__) +/** + * Shortcut for logging a trace message. + * @param logger the logger to use + * @param ... format string and arguments + * @see ucx_logger_logf() + */ +#define ucx_logger_trace(logger, ...) \ + ucx_logger_log(logger, UCX_LOGGER_TRACE, __VA_ARGS__) #ifdef __cplusplus } #endif -#endif /* LOGGING_H */ +#endif /* UCX_LOGGING_H */
--- a/ucx/map.c Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/map.c Mon Aug 12 14:40:19 2013 +0200 @@ -1,5 +1,29 @@ /* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * + * Copyright 2013 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 <stdlib.h> @@ -8,18 +32,30 @@ #include "map.h" UcxMap *ucx_map_new(size_t size) { + return ucx_map_new_a(NULL, size); +} + +UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size) { if(size == 0) { size = 16; } + + if(!allocator) { + allocator = ucx_default_allocator(); + } - UcxMap *map = (UcxMap*)malloc(sizeof(UcxMap)); + UcxMap *map = (UcxMap*)allocator->malloc(allocator->pool, sizeof(UcxMap)); if(map == NULL) { return NULL; } - - map->map = (UcxMapElement**)calloc(size, sizeof(UcxMapElement*)); + + map->allocator = allocator; + map->map = (UcxMapElement**)allocator->calloc( + allocator->pool, + size, + sizeof(UcxMapElement*)); if(map->map == NULL) { - free(map); + allocator->free(allocator->pool, map); return NULL; } map->size = size; @@ -28,31 +64,31 @@ return map; } -void ucx_map_free_elmlist(UcxMap *map) { +static void ucx_map_free_elmlist(UcxMap *map) { for (size_t n = 0 ; n < map->size ; n++) { UcxMapElement *elem = map->map[n]; if (elem != NULL) { do { UcxMapElement *next = elem->next; - free(elem->key.data); - free(elem); + map->allocator->free(map->allocator->pool, elem->key.data); + map->allocator->free(map->allocator->pool, elem); elem = next; } while (elem != NULL); } } - free(map->map); + map->allocator->free(map->allocator->pool, map->map); } void ucx_map_free(UcxMap *map) { ucx_map_free_elmlist(map); - free(map); + map->allocator->free(map->allocator->pool, map); } int ucx_map_copy(UcxMap *restrict from, UcxMap *restrict to, copy_func fnc, void *data) { UcxMapIterator i = ucx_map_iterator(from); void *value; - UCX_MAP_FOREACH(value, i) { + UCX_MAP_FOREACH(key, value, i) { int ret = ucx_map_put(to, i.cur->key, fnc ? fnc(value, data) : value); if(ret != 0) { return 1; @@ -78,9 +114,13 @@ oldmap.map = map->map; oldmap.size = map->size; oldmap.count = map->count; + oldmap.allocator = map->allocator; map->size = (map->count * 5) >> 1; - map->map = (UcxMapElement**)calloc(map->size, sizeof(UcxMapElement*)); + map->map = (UcxMapElement**)map->allocator->calloc( + map->allocator->pool, + map->size, + sizeof(UcxMapElement*)); if(map->map == NULL) { *map = oldmap; return 1; @@ -95,6 +135,8 @@ } int ucx_map_put(UcxMap *map, UcxKey key, void *data) { + UcxAllocator *allocator = map->allocator; + if(key.hash == 0) { key.hash = ucx_hash((char*)key.data, key.len); } @@ -109,7 +151,9 @@ } if (elm == NULL || elm->key.hash != key.hash) { - UcxMapElement *e = (UcxMapElement*)malloc(sizeof(UcxMapElement)); + UcxMapElement *e = (UcxMapElement*)allocator->malloc( + allocator->pool, + sizeof(UcxMapElement)); if(e == NULL) { return -1; } @@ -124,7 +168,7 @@ } if(elm->key.data == NULL) { - void *kd = malloc(key.len); + void *kd = allocator->malloc(allocator->pool, key.len); if (kd == NULL) { return -1; } @@ -157,7 +201,7 @@ } else { map->map[slot] = elm->next; } - free(elm); + map->allocator->free(map->allocator->pool, elm); map->count--; } @@ -238,7 +282,7 @@ return i; } -int ucx_map_iter_next(UcxMapIterator *i, void **elm) { +int ucx_map_iter_next(UcxMapIterator *i, UcxKey *key, void **elm) { UcxMapElement *e = i->cur; if(e == NULL) { @@ -252,6 +296,7 @@ if(e->data != NULL) { i->cur = e; *elm = e->data; + *key = e->key; return 0; } @@ -268,123 +313,3 @@ return 1; } -int ucx_map_load_enc(UcxMap *map, FILE *f, UcxAllocator allocator, - ucx_map_coder decoder, void* decdata) { - - int c; int r, n; - - char *key, *value; - - while ((c = fgetc(f)) > 0) { - /* Discard leading spaces and comments */ - if (c < 33) continue; - if (c == '#' || c == '!') { - while ((c = (char) fgetc(f)) > 0) { - if (c == '\n') break; - } - continue; - } - - /* read into key buffer */ - n = 16; - key = (char*) malloc(n); - r = 0; - do { - if (c == '=') break; - if (r > n - 2) { - n *= 2; - key = (char*) realloc(key, n); - } - key[r] = c; - r++; - } while ((c = fgetc(f)) > 0); - if (c <= 0) { - free(key); - return 1; - } - key[r] = 0; - while (key[--r] == ' ') key[r] = 0; - - /* skip whitespaces */ - while ((c = fgetc(f)) > 0) { - if (c > 32) break; - } - if (c <= 0) { - free(key); - return 1; - } - - /* read into value buffer */ - n = 64; - value = (char*) malloc(n); - r = 0; - do { - if (c == '\n') break; - if (r > n - 2) { - n *= 2; - value = (char*) realloc(value, n); - } - value[r] = c; - r++; - } while ((c = fgetc(f)) > 0); - value[r] = 0; - while (value[--r] < 33) value[r] = 0; - - if (decoder) { - size_t decodedSize; - void *decoded = decoder(value, decdata, &decodedSize); - free(value); - value = (char*) decoded; - r = decodedSize; - } else { - r += 2; - value = (char*) realloc(value, r); - } - - if (allocator.pool) { - void *pooledValue = allocator.malloc(allocator.pool, r); - memcpy(pooledValue, value, r); - free(value); - value = (char*) pooledValue; - } - - ucx_map_cstr_put(map, key, value); - free(key); - } - - return 0; -} - -int ucx_map_store_enc(UcxMap *map, FILE *f, - ucx_map_coder encoder, void *encdata) { - UcxMapIterator iter = ucx_map_iterator(map); - char *k, *v; - sstr_t key, value; - int written; - - UCX_MAP_FOREACH(v, iter) { - k = (char*) iter.cur->key.data; - key = sstr(k); - if (encoder) { - size_t encodedSize; - void *encoded = encoder(v, encdata, &encodedSize); - value = sstrn((char*) encoded,encodedSize - 1); - } else { - value = sstr(v); - } - - written = 0; - written += fwrite(key.ptr, 1, key.length, f); - written += fwrite(" = ", 1, 3, f); - written += fwrite(value.ptr, 1, value.length, f); - written += fwrite("\n", 1, 1, f); - - if (encoder) { - free(value.ptr); - } - - if (written != key.length + value.length + 4) return 1; - } - - return 0; -}
--- a/ucx/map.h Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/map.h Mon Aug 12 14:40:19 2013 +0200 @@ -1,36 +1,65 @@ /* - * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 MAP_H -#define MAP_H +/** + * @file map.h + * + * Hash map implementation. + * + * This implementation uses murmur hash 2 and separate chaining with linked + * lists. + * + * @author Mike Becker + * @author Olaf Wintermann + */ + +#ifndef UCX_MAP_H +#define UCX_MAP_H #include "ucx.h" #include "string.h" -#include "mempool.h" +#include "allocator.h" #include <stdio.h> #ifdef __cplusplus extern "C" { #endif -#define UCX_MAP_FOREACH(elm,iter) \ - for(;ucx_map_iter_next(&iter,(void**)&elm)==0;) +#define UCX_MAP_FOREACH(key,elm,iter) \ + for(UcxKey key;ucx_map_iter_next(&iter,&key, (void**)&elm)==0;) typedef struct UcxMap UcxMap; typedef struct UcxKey UcxKey; typedef struct UcxMapElement UcxMapElement; typedef struct UcxMapIterator UcxMapIterator; -/* - * param 1: element - * param 2: additional data - * param 3: size of encoded data will be stored here - * returns encoded / decoded string or NULL on failure - */ -typedef void*(*ucx_map_coder)(void*,void*,size_t*); - struct UcxMap { + UcxAllocator *allocator; UcxMapElement **map; size_t size; size_t count; @@ -51,12 +80,34 @@ struct UcxMapIterator { UcxMap *map; UcxMapElement *cur; - int index; + size_t index; }; - +/** + * Creates a new hash map with the specified size. + * @param size the size of the hash map + * @return a pointer to the new hash map + */ UcxMap *ucx_map_new(size_t size); + +/** + * Creates a new hash map with the specified size using an UcxAllocator. + * @param allocator the allocator to use + * @param size the size of the hash map + * @return a pointer to the new hash map + */ +UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size); + +/** + * Frees a hash map. + * + * <b>Note:</b> the contents are <b>not</b> freed, use an UcxMempool for that + * purpose. + * + * @param map the map to be freed + */ void ucx_map_free(UcxMap *map); + /* you cannot clone maps with more than 390 mio entries */ int ucx_map_copy(UcxMap *restrict from, UcxMap *restrict to, copy_func fnc, void *data); @@ -67,18 +118,81 @@ void* ucx_map_get(UcxMap *map, UcxKey key); void* ucx_map_remove(UcxMap *map, UcxKey key); -#define ucx_map_sstr_put(m, s, d) \ - ucx_map_put(m, ucx_key((void*)s.ptr, s.length), d) -#define ucx_map_cstr_put(m, s, d) \ - ucx_map_put(m, ucx_key((void*)s, 1+strlen(s)), d) -#define ucx_map_sstr_get(m, s) \ - ucx_map_get(m, ucx_key((void*)s.ptr, s.length)) -#define ucx_map_cstr_get(m, s) \ - ucx_map_get(m, ucx_key((void*)s, 1+strlen(s))) -#define ucx_map_sstr_remove(m, s) \ - ucx_map_remove(m, ucx_key((void*)s.ptr, s.length)) -#define ucx_map_cstr_remove(m, s) \ - ucx_map_remove(m, ucx_key((void*)s, 1+strlen(s))) +/** + * Shorthand for putting data with a sstr_t key into the map. + * @param map the map + * @param key the key + * @param value the value + * @see ucx_map_put() + */ +#define ucx_map_sstr_put(map, key, value) \ + ucx_map_put(map, ucx_key(key.ptr, key.length), (void*)value) +/** + * Shorthand for putting data with a C string key into the map. + * @param map the map + * @param key the key + * @param value the value + * @see ucx_map_put() + */ +#define ucx_map_cstr_put(map, key, value) \ + ucx_map_put(map, ucx_key((void*)key, strlen(key)), (void*)value) +/** + * Shorthand for putting data with an integer key into the map. + * @param map the map + * @param key the key + * @param value the value + * @see ucx_map_put() + */ +#define ucx_map_int_put(map, key, value) \ + ucx_map_put(map, ucx_key((void*)&key, sizeof(key)), (void*)value) + + +/** + * Shorthand for getting data from the map with a sstr_t key. + * @param map the map + * @param key the key + * @see ucx_map_get() + */ +#define ucx_map_sstr_get(map, key) \ + ucx_map_get(map, ucx_key(key.ptr, key.length)) +/** + * Shorthand for getting data from the map with a C string key. + * @see ucx_map_get() + */ +#define ucx_map_cstr_get(map, key) \ + ucx_map_get(map, ucx_key((void*)key, strlen(key))) +/** + * Shorthand for getting data from the map with an integer key. + * @param map the map + * @param key the key + * @see ucx_map_get() + */ +#define ucx_map_int_get(map, key) \ + ucx_map_get(map, ucx_key((void*)&key, sizeof(int))) +/** + * Shorthand for removing data from the map with a sstr_t key. + * @param map the map + * @param key the key + * @see ucx_map_remove() + */ +#define ucx_map_sstr_remove(map, key) \ + ucx_map_remove(map, ucx_key(key.ptr, key.length)) +/** + * Shorthand for removing data from the map with a C string key. + * @param map the map + * @param key the key + * @see ucx_map_remove() + */ +#define ucx_map_cstr_remove(map, key) \ + ucx_map_remove(map, ucx_key((void*)key, strlen(key))) +/** + * Shorthand for removing data from the map with an integer key. + * @param map the map + * @param key the key + * @see ucx_map_remove() + */ +#define ucx_map_int_remove(map, key) \ + ucx_map_remove(map, ucx_key((void*)&key, sizeof(key))) UcxKey ucx_key(void *data, size_t len); @@ -86,21 +200,12 @@ UcxMapIterator ucx_map_iterator(UcxMap *map); -int ucx_map_iter_next(UcxMapIterator *i, void **elm); - -/* use macros for string maps only, values are not encoded */ -#define ucx_map_load(map, f, alloc) ucx_map_load_enc(map, f, alloc, NULL, NULL) -#define ucx_map_store(map, f) ucx_map_store_enc(map, f, NULL, NULL) +int ucx_map_iter_next(UcxMapIterator *i, UcxKey *key, void **elm); -int ucx_map_load_enc(UcxMap *map, FILE *f, UcxAllocator allocator, - ucx_map_coder decoder, void* decdata); -/* encoders shall provide null terminated strings*/ -int ucx_map_store_enc(UcxMap *map, FILE *f, - ucx_map_coder encoder, void* encdata); #ifdef __cplusplus } #endif -#endif /* MAP_H */ +#endif /* UCX_MAP_H */
--- a/ucx/mempool.c Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/mempool.c Mon Aug 12 14:40:19 2013 +0200 @@ -1,10 +1,37 @@ /* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * + * Copyright 2013 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 <stdlib.h> #include <string.h> #include <stdio.h> +#ifdef __cplusplus +#define __STDC_FORMAT_MACROS +#endif #include <inttypes.h> #include "mempool.h" @@ -19,14 +46,16 @@ void *ptr; } ucx_regdestr; -void ucx_mempool_shared_destr(void* ptr) { +UCX_EXTERN void ucx_mempool_shared_destr(void* ptr) { ucx_regdestr *rd = (ucx_regdestr*)ptr; rd->destructor(rd->ptr); } UcxMempool *ucx_mempool_new(size_t n) { UcxMempool *pool = (UcxMempool*)malloc(sizeof(UcxMempool)); - if (pool == NULL) return NULL; + if (!pool) { + return NULL; + } pool->data = (void**) malloc(n * sizeof(void*)); if (pool->data == NULL) { @@ -41,20 +70,23 @@ int ucx_mempool_chcap(UcxMempool *pool, size_t newcap) { void **data = (void**) realloc(pool->data, newcap*sizeof(void*)); - if (data == NULL) { - return 1; - } else { + if (data) { pool->data = data; pool->size = newcap; return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; } } void *ucx_mempool_malloc(UcxMempool *pool, size_t n) { ucx_memchunk *mem = (ucx_memchunk*)malloc(sizeof(ucx_destructor) + n); - if (mem == NULL) return NULL; + if (!mem) { + return NULL; + } if (pool->ndata >= pool->size) { + // The hard coded 16 is documented for this function and ucx_mempool_new ucx_mempool_chcap(pool, pool->size + 16); } @@ -62,12 +94,12 @@ pool->data[pool->ndata] = mem; pool->ndata++; - return &mem->c; + return &(mem->c); } void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize) { void *ptr = ucx_mempool_malloc(pool, nelem*elsize); - if(ptr == NULL) { + if (!ptr) { return NULL; } memset(ptr, 0, nelem * elsize); @@ -77,15 +109,17 @@ void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n) { char *mem = ((char*)ptr) - sizeof(ucx_destructor); char *newm = (char*) realloc(mem, n + sizeof(ucx_destructor)); - if (newm == NULL) return NULL; + if (!newm) { + return NULL; + } if (mem != newm) { - for(int i=0;i<pool->ndata;i++) { + for(size_t i=0 ; i < pool->ndata ; i++) { if(pool->data[i] == mem) { pool->data[i] = newm; return newm + sizeof(ucx_destructor); } } - fprintf(stderr, "FATAL: 0x%08"PRIxPTR" not in mpool 0x%08"PRIxPTR"\n", + fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n", (intptr_t)ptr, (intptr_t)pool); exit(1); } else { @@ -93,14 +127,37 @@ } } -void ucx_mempool_free(UcxMempool *pool) { +void ucx_mempool_free(UcxMempool *pool, void *ptr) { + ucx_memchunk *chunk = (ucx_memchunk*)((char*)ptr-sizeof(ucx_destructor)); + for(size_t i=0 ; i<pool->ndata ; i++) { + if(chunk == pool->data[i]) { + if(chunk->destructor != NULL) { + chunk->destructor(&chunk->c); + } + free(chunk); + size_t last_index = pool->ndata - 1; + if(i != last_index) { + pool->data[i] = pool->data[last_index]; + } + pool->ndata--; + return; + } + } + fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n", + (intptr_t)ptr, (intptr_t)pool); + exit(EXIT_FAILURE); +} + +void ucx_mempool_destroy(UcxMempool *pool) { ucx_memchunk *chunk; - for(int i=0;i<pool->ndata;i++) { + for(size_t i=0 ; i<pool->ndata ; i++) { chunk = (ucx_memchunk*) pool->data[i]; - if(chunk->destructor != NULL) { - chunk->destructor(&chunk->c); + if(chunk) { + if(chunk->destructor) { + chunk->destructor(&(chunk->c)); + } + free(chunk); } - free(chunk); } free(pool->data); free(pool); @@ -118,3 +175,17 @@ rd->ptr = ptr; ucx_mempool_set_destr(rd, ucx_mempool_shared_destr); } + +UcxAllocator* ucx_mempool_allocator(UcxMempool *pool) { + UcxAllocator *allocator = (UcxAllocator*)ucx_mempool_malloc( + pool, sizeof(UcxAllocator)); + if(!allocator) { + return NULL; + } + allocator->malloc = (ucx_allocator_malloc)ucx_mempool_malloc; + allocator->calloc = (ucx_allocator_calloc)ucx_mempool_calloc; + allocator->realloc = (ucx_allocator_realloc)ucx_mempool_realloc; + allocator->free = (ucx_allocator_free)ucx_mempool_free; + allocator->pool = pool; + return allocator; +}
--- a/ucx/mempool.h Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/mempool.h Mon Aug 12 14:40:19 2013 +0200 @@ -1,9 +1,42 @@ -/* +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 MPOOL_H -#define MPOOL_H +/** + * @file mempool.h + * + * Memory pool implementation. + * + * @author Mike Becker + * @author Olaf Wintermann + */ + +#ifndef UCX_MEMPOOL_H +#define UCX_MEMPOOL_H #include "ucx.h" #include <stddef.h> @@ -13,36 +46,159 @@ extern "C" { #endif +/** + * A function pointer to a destructor function. + * @see ucx_mempool_setdestr() + * @see ucx_mempool_regdestr() + */ typedef void(*ucx_destructor)(void*); +/** + * UCX mempool structure. + */ typedef struct { + /** List of pointers to pooled memory. */ void **data; + /** Count of pooled memory items. */ size_t ndata; + /** Memory pool size. */ size_t size; } UcxMempool; -#define UCX_ALLOCATOR_MEMPOOL(pool) {pool, \ - (ucx_allocator_malloc) ucx_mempool_malloc, \ - (ucx_allocator_calloc) ucx_mempool_calloc, \ - (ucx_allocator_realloc) ucx_mempool_realloc} +/** Shorthand for a new default memory pool with a capacity of 16 elements. */ +#define ucx_mempool_new_default() ucx_mempool_new(16) + -#define ucx_mempool_new_default() ucx_mempool_new(16) +/** + * Creates a memory pool with the specified initial size. + * + * As the created memory pool automatically grows in size by 16 elements, when + * trying to allocate memory on a full pool, it is recommended that you use + * a multiple of 16 for the initial size. + * + * @param n initial pool size (should be a multiple of 16) + * @return a pointer to the new memory pool + */ UcxMempool *ucx_mempool_new(size_t n); + +/** + * Resizes a memory pool. + * + * @param pool the pool to resize + * @param newcap the new capacity + * @return <code>EXIT_SUCCESS</code> on success or + * <code>EXIT_FAILURE</code> on failure + */ int ucx_mempool_chcap(UcxMempool *pool, size_t newcap); +/** + * Allocates pooled memory. + * + * @param pool the memory pool + * @param n amount of memory to allocate + * @return a pointer to the allocated memory + * @see ucx_allocator_malloc() + */ void *ucx_mempool_malloc(UcxMempool *pool, size_t n); +/** + * Allocates a pooled memory array. + * + * The contents of the allocated memory is set to zero. + * + * @param pool the memory pool + * @param nelem amount of elements to allocate + * @param elsize amount of memory per element + * @return a pointer to the allocated memory + * @see ucx_allocator_calloc() + */ void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize); +/** + * Reallocates pooled memory. + * + * @param pool the memory pool + * @param ptr a pointer to the memory that shall be reallocated + * @param n the new size of the memory + * @return a pointer to the the location of the memory + * @see ucx_allocator_realloc() + */ void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n); +/** + * Frees pooled memory. + * + * Before freeing the memory, the specified destructor function (if any) + * is called. + * + * If you specify memory, that is not pooled by the specified memory pool, this + * is considered as a severe error and an error message is written to + * <code>stderr</code> and the application is shut down with code + * <code>EXIT_FAILURE</code>. + * + * @param pool the memory pool + * @param ptr a pointer to the memory that shall be freed + * @see ucx_mempool_set_destr() + */ +void ucx_mempool_free(UcxMempool *pool, void *ptr); -void ucx_mempool_free(UcxMempool *pool); +/** + * Destroys a memory pool. + * + * For each element the destructor function (if any) is called and the element + * is freed. + * + * Each of the registered destructor function that has no corresponding element + * within the pool (namely those registered by ucx_mempool_reg_destr) is + * called interleaving with the element destruction, but with guarantee to the + * order in which they were registered (FIFO order). + * + * + * @param pool the mempool to destroy + */ +void ucx_mempool_destroy(UcxMempool *pool); +/** + * Sets a destructor function for the specified memory. + * + * The destructor is automatically called when the memory is freed or the + * pool is destroyed. + * + * The only requirement for the specified memory is, that it <b>MUST</b> be + * pooled memory by an UcxMempool or an element-compatible mempool. The pointer + * to the destructor function is saved in a reserved area before the actual + * memory. + * + * @param ptr pooled memory + * @param func a pointer to the destructor function + * @see ucx_mempool_free() + * @see ucx_mempool_destroy() + */ void ucx_mempool_set_destr(void *ptr, ucx_destructor func); + +/** + * Registers a destructor function for the specified (non-pooled) memory. + * + * This is useful, if you have memory that has not been allocated by a mempool, + * but shall be managed by a mempool. + * + * This function creates an entry in the specified mempool and the memory will + * therefore (logically) convert to pooled memory. + * + * @param pool the memory pool + * @param ptr data the destructor is registered for + * @param destr a pointer to the destructor function + */ void ucx_mempool_reg_destr(UcxMempool *pool, void *ptr, ucx_destructor destr); +/** + * Creates an UcxAllocator based on an UcxMempool. + * + * @param pool the mempool to create the UcxAllocator for + * @return a new UcxAllocator based on the specified pool + */ +UcxAllocator* ucx_mempool_allocator(UcxMempool *pool); #ifdef __cplusplus } #endif -#endif /* MPOOL_H */ +#endif /* UCX_MEMPOOL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/properties.c Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,266 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 <string.h> + +#include "properties.h" + +UcxProperties *ucx_properties_new() { + UcxProperties *parser = (UcxProperties*)malloc( + sizeof(UcxProperties)); + if(!parser) { + return NULL; + } + + parser->buffer = NULL; + parser->buflen = 0; + parser->pos = 0; + parser->tmp = NULL; + parser->tmplen = 0; + parser->tmpcap = 0; + parser->error = 0; + parser->delimiter = '='; + parser->comment1 = '#'; + parser->comment2 = 0; + parser->comment3 = 0; + + return parser; +} + +void ucx_properties_free(UcxProperties *parser) { + if(parser->tmp) { + free(parser->tmp); + } + free(parser); +} + +void ucx_properties_fill(UcxProperties *parser, char *buf, size_t len) { + parser->buffer = buf; + parser->buflen = len; + parser->pos = 0; +} + +static void parser_tmp_append(UcxProperties *parser, char *buf, size_t len) { + if(parser->tmpcap - parser->tmplen < len) { + size_t newcap = parser->tmpcap + len + 64; + parser->tmp = (char*)realloc(parser->tmp, newcap); + parser->tmpcap = newcap; + } + memcpy(parser->tmp + parser->tmplen, buf, len); + parser->tmplen += len; +} + +int ucx_properties_next(UcxProperties *parser, sstr_t *name, sstr_t *value) { + if(parser->tmplen > 0) { + char *buf = parser->buffer + parser->pos; + size_t len = parser->buflen - parser->pos; + sstr_t str = sstrn(buf, len); + sstr_t nl = sstrchr(str, '\n'); + if(nl.ptr) { + size_t newlen = (size_t)(nl.ptr - buf) + 1; + parser_tmp_append(parser, buf, newlen); + // the tmp buffer contains exactly one line now + + char *orig_buf = parser->buffer; + size_t orig_len = parser->buflen; + + parser->buffer = parser->tmp; + parser->buflen = parser->tmplen; + parser->pos = 0; + parser->tmp = NULL; + parser->tmpcap = 0; + parser->tmplen = 0; + // run ucx_properties_next with the tmp buffer as main buffer + int ret = ucx_properties_next(parser, name, value); + + // restore original buffer + parser->tmp = parser->buffer; + parser->buffer = orig_buf; + parser->buflen = orig_len; + parser->pos = newlen; + + /* + * if ret == 0 the tmp buffer contained just space or a comment + * we parse again with the original buffer to get a name/value + * or a new tmp buffer + */ + return ret ? ret : ucx_properties_next(parser, name, value); + } else { + parser_tmp_append(parser, buf, len); + return 0; + } + } else if(parser->tmp) { + free(parser->tmp); + parser->tmp = NULL; + } + + char comment1 = parser->comment1; + char comment2 = parser->comment2; + char comment3 = parser->comment3; + char delimiter = parser->delimiter; + + // get one line and parse it + while(parser->pos < parser->buflen) { + char *buf = parser->buffer + parser->pos; + size_t len = parser->buflen - parser->pos; + + /* + * First we check if we have at least one line. We also get indices of + * delimiter and comment chars + */ + size_t delimiter_index = 0; + size_t comment_index = 0; + int has_comment = 0; + + size_t i = 0; + char c = 0; + for(;i<len;i++) { + c = buf[i]; + if(c == comment1 || c == comment2 || c == comment3) { + if(comment_index == 0) { + comment_index = i; + has_comment = 1; + } + } else if(c == delimiter) { + if(delimiter_index == 0 && !has_comment) { + delimiter_index = i; + } + } else if(c == '\n') { + break; + } + } + + if(c != '\n') { + // we don't have enough data for a line + // store remaining bytes in temporary buffer for next round + parser->tmpcap = len + 128; + parser->tmp = (char*)malloc(parser->tmpcap); + parser->tmplen = len; + memcpy(parser->tmp, buf, len); + return 0; + } + + sstr_t line = has_comment ? sstrn(buf, comment_index) : sstrn(buf, i); + // check line + if(delimiter_index == 0) { + line = sstrtrim(line); + if(line.length != 0) { + parser->error = 1; + } + } else { + sstr_t n = sstrn(buf, delimiter_index); + sstr_t v = sstrn( + buf + delimiter_index + 1, + line.length - delimiter_index - 1); + n = sstrtrim(n); + v = sstrtrim(v); + if(n.length != 0 || v.length != 0) { + *name = n; + *value = v; + parser->pos += i + 1; + return 1; + } else { + parser->error = 1; + } + } + + parser->pos += i + 1; + } + + return 0; +} + +int ucx_properties2map(UcxProperties *parser, UcxMap *map) { + sstr_t name; + sstr_t value; + while(ucx_properties_next(parser, &name, &value)) { + value = sstrdup_a(map->allocator, value); + if(!value.ptr) { + return 1; + } + if(ucx_map_sstr_put(map, name, value.ptr)) { + map->allocator->free(map->allocator->pool, value.ptr); + return 1; + } + } + if (parser->error) { + return parser->error; + } else { + return 0; + } +} + +int ucx_properties_load(UcxMap *map, FILE *file) { + UcxProperties *parser = ucx_properties_new(); + if(!(parser && map && file)) { + return 1; + } + + // buffer size is documented - change doc, when you change bufsize! + const size_t bufsize = 1024; + + int error = 0; + size_t r; + char buf[bufsize]; + while((r = fread(buf, 1, bufsize, file)) != 0) { + ucx_properties_fill(parser, buf, r); + error = ucx_properties2map(parser, map); + if (error) { + break; + } + } + + ucx_properties_free(parser); + return error; +} + +int ucx_properties_store(UcxMap *map, FILE *file) { + UcxMapIterator iter = ucx_map_iterator(map); + char *v; + sstr_t value; + size_t written; + + UCX_MAP_FOREACH(k, v, iter) { + value = sstr(v); + + written = 0; + written += fwrite(k.data, 1, k.len, file); + written += fwrite(" = ", 1, 3, file); + written += fwrite(value.ptr, 1, value.length, file); + written += fwrite("\n", 1, 1, file); + + if (written != k.len + value.length + 4) { + return 1; + } + } + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/properties.h Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,205 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 properties.h + * + * Load / store utilities for properties files. + * + * @author Mike Becker + * @author Olaf Wintermann + */ + +#ifndef UCX_PROPERTIES_H +#define UCX_PROPERTIES_H + +#include "ucx.h" +#include "map.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * UcxProperties object for parsing properties data. + * Most of the fields are for internal use only. You may configure the + * properties parser, e.g. by changing the used delimiter or specifying + * up to three different characters that shall introduce comments. + */ +typedef struct { + /** + * Input buffer (don't set manually). + * Automatically set by calls to ucx_properties_fill(). + */ + char *buffer; + /** + * Length of the input buffer (don't set manually). + * Automatically set by calls to ucx_properties_fill(). + */ + size_t buflen; + /** + * Current buffer position (don't set manually). + * Used by ucx_properties_next(). + */ + size_t pos; + /** + * Internal temporary buffer (don't set manually). + * Used by ucx_properties_next(). + */ + char *tmp; + /** + * Internal temporary buffer length (don't set manually). + * Used by ucx_properties_next(). + */ + size_t tmplen; + /** + * Internal temporary buffer capacity (don't set manually). + * Used by ucx_properties_next(). + */ + size_t tmpcap; + /** + * Parser error code. + * This is always 0 on success and a nonzero value on syntax errors. + * The value is set by ucx_properties_next(). + */ + int error; + /** + * The delimiter that shall be used. + * This is '=' by default. + */ + char delimiter; + /** + * The first comment character. + * This is '#' by default. + */ + char comment1; + /** + * The second comment character. + * This is not set by default. + */ + char comment2; + /** + * The third comment character. + * This is not set by default. + */ + char comment3; +} UcxProperties; + + +/** + * Constructs a new UcxProperties object. + * @return a pointer to the new UcxProperties object + */ +UcxProperties *ucx_properties_new(); +/** + * Destroys an UcxProperties object. + * @param prop the UcxProperties object to destroy + */ +void ucx_properties_free(UcxProperties *prop); +/** + * Sets the input buffer for the properties parser. + * + * After calling this function, you may parse the data by calling + * ucx_properties_next() until it returns 0. The function ucx_properties2map() + * is a convenience function that performs these successive calls of + * ucx_properties_next() within a while loop and puts the properties to a map. + * + * + * @param prop the UcxProperties object + * @param buf a pointer to the new buffer + * @param len the payload length of the buffer + * @see ucx_properties_next() + * @see ucx_properties2map() + */ +void ucx_properties_fill(UcxProperties *prop, char *buf, size_t len); +/** + * Retrieves the next key/value-pair. + * + * This function returns a nonzero value as long as there are key/value-pairs + * found. If no more key/value-pairs are found, you may refill the input buffer + * with ucx_properties_fill(). + * + * <b>Attention:</b> the sstr_t.ptr pointers of the output parameters point to + * memory within the input buffer of the parser and will get invalid some time. + * If you want long term copies of the key/value-pairs, use sstrdup() after + * calling this function. + * + * @param prop the UcxProperties object + * @param name a pointer to the sstr_t that shall contain the property name + * @param value a pointer to the sstr_t that shall contain the property value + * @return Nonzero, if a key/value-pair was successfully retrieved + * @see ucx_properties_fill() + */ +int ucx_properties_next(UcxProperties *prop, sstr_t *name, sstr_t *value); +/** + * Retrieves all available key/value-pairs and puts them into an UcxMap. + * + * This is done by successive calls to ucx_properties_next() until no more + * key/value-pairs can be retrieved. + * + * @param prop the UcxProperties object + * @param map the target map + * @return The UcxProperties.error code (i.e. 0 on success). + * @see ucx_properties_fill() + */ +int ucx_properties2map(UcxProperties *prop, UcxMap *map); + +/** + * Loads a properties file to an UcxMap. + * + * This is a convenience function that reads chunks of 1 KB from an input + * stream until the end of the stream is reached. + * + * An UcxProperties object is implicitly created and destroyed. + * + * @param map the map object to write the key/value-pairs to + * @param file the <code>FILE*</code> stream to read from + * @return 0 on success, or a non-zero value on error + * + * @see ucx_properties_fill() + * @see ucx_properties2map() + */ +int ucx_properties_load(UcxMap *map, FILE *file); +/** + * Stores an UcxMap to a file. + * + * The key/value-pairs are written by using the following format: + * + * <code>[key] = [value]\\n</code> + * + * @param map the map to store + * @param file the <code>FILE*</code> stream to write to + * @return 0 on success, or a non-zero value on error + */ +int ucx_properties_store(UcxMap *map, FILE *file); + +#ifdef __cplusplus +} +#endif + +#endif /* UCX_PROPERTIES_H */ +
--- a/ucx/string.c Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/string.c Mon Aug 12 14:40:19 2013 +0200 @@ -1,8 +1,29 @@ /* - * File: sstring.c - * Author: olaf + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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. * - * Created on 17. Juni 2010, 13:27 + * 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 <stdlib.h> @@ -10,18 +31,19 @@ #include <stdarg.h> #include "string.h" +#include "allocator.h" -sstr_t sstr(char *s) { +sstr_t sstr(char *cstring) { sstr_t string; - string.ptr = s; - string.length = strlen(s); + string.ptr = cstring; + string.length = strlen(cstring); return string; } -sstr_t sstrn(char *s, size_t n) { +sstr_t sstrn(char *cstring, size_t length) { sstr_t string; - string.ptr = s; - string.length = n; + string.ptr = cstring; + string.length = length; return string; } @@ -30,7 +52,7 @@ size_t size = s.length; va_start(ap, s); - for (int i=0;i<n-1;i++) { + for (size_t i = 1 ; i < n ; i++) { sstr_t str = va_arg(ap, sstr_t); size += str.length; } @@ -39,22 +61,7 @@ return size; } -sstr_t sstrcat(sstr_t s, ...) { - va_list ap; - va_start(ap, s); - s.ptr[0] = 0; - - sstr_t str = va_arg (ap, sstr_t); - while (str.ptr != NULL) { - s.ptr = strncat (s.ptr, str.ptr, s.length); - str = va_arg (ap, sstr_t); - } - va_end(ap); - - return s; -} - -sstr_t sstrncat(size_t n, sstr_t s, sstr_t c1, ...) { +sstr_t sstrncat(sstr_t s, size_t n, sstr_t c1, ...) { va_list ap; va_start(ap, c1); s.ptr[0] = 0; @@ -66,7 +73,7 @@ memcpy(ptr, c1.ptr, cplen); len -= cplen; ptr += cplen; - for (int i=0;i<n-1;i++) { + for (size_t i = 1 ; i < n ; i++) { sstr_t str = va_arg (ap, sstr_t); cplen = str.length > len ? len : str.length; if(cplen <= 0) { @@ -78,6 +85,7 @@ ptr += cplen; } va_end(ap); + s.length = ptr - s.ptr; return s; } @@ -88,7 +96,7 @@ sstr_t sstrsubsl(sstr_t s, size_t start, size_t length) { sstr_t new_sstr; - if (start < 0 || start >= s.length || length < 0) { + if (start >= s.length) { return s; } if (length > s.length-start) { @@ -99,8 +107,25 @@ return new_sstr; } +sstr_t sstrchr(sstr_t s, int c) { + for(size_t i=0;i<s.length;i++) { + if(s.ptr[i] == c) { + return sstrsubs(s, i); + } + } + sstr_t n; + n.ptr = NULL; + n.length = 0; + return n; +} + sstr_t* sstrsplit(sstr_t s, sstr_t d, size_t *n) { - if (d.length == 0) { + return sstrsplit_a(ucx_default_allocator(), s, d, n); +} + +sstr_t* sstrsplit_a(UcxAllocator *allocator, sstr_t s, sstr_t d, size_t *n) { + if (s.length == 0 || d.length == 0) { + *n = -1; return NULL; } @@ -109,16 +134,20 @@ *n = 1; /* special case: exact match - no processing needed */ - if (s.length == d.length && strncmp(s.ptr, d.ptr, s.length) == 0) { + if (sstrcmp(s, d) == 0) { *n = 0; return NULL; } sstr_t sv = sstrdup(s); + if (sv.length == 0) { + *n = -2; + return NULL; + } - for (int i = 0 ; i < s.length ; i++) { + for (size_t i = 0 ; i < s.length ; i++) { if (sv.ptr[i] == d.ptr[0]) { _Bool match = 1; - for (int j = 1 ; j < d.length ; j++) { + for (size_t j = 1 ; j < d.length ; j++) { if (j+i < s.length) { match &= (sv.ptr[i+j] == d.ptr[j]); } else { @@ -128,7 +157,7 @@ } if (match) { (*n)++; - for (int j = 0 ; j < d.length ; j++) { + for (size_t j = 0 ; j < d.length ; j++) { sv.ptr[i+j] = 0; } i += d.length; @@ -136,37 +165,90 @@ } if ((*n) == nmax) break; } - result = (sstr_t*) malloc(sizeof(sstr_t) * (*n)); + result = (sstr_t*) allocator->malloc(allocator->pool, sizeof(sstr_t)*(*n)); - char *pptr = sv.ptr; - for (int i = 0 ; i < *n ; i++) { - size_t l = strlen(pptr); - char* ptr = (char*) malloc(l + 1); - memcpy(ptr, pptr, l); - ptr[l] = 0; + if (result) { + char *pptr = sv.ptr; + for (size_t i = 0 ; i < *n ; i++) { + size_t l = strlen(pptr); + char* ptr = (char*) allocator->malloc(allocator->pool, l + 1); + memcpy(ptr, pptr, l); + ptr[l] = 0; - result[i] = sstrn(ptr, l); - pptr += l + d.length; + result[i] = sstrn(ptr, l); + pptr += l + d.length; + } + } else { + *n = -2; } - + free(sv.ptr); return result; } int sstrcmp(sstr_t s1, sstr_t s2) { - return strncmp(s1.ptr, s2.ptr, s1.length>s2.length ? s2.length: s1.length); + if (s1.length == s2.length) { + return memcmp(s1.ptr, s2.ptr, s1.length); + } else if (s1.length > s2.length) { + return 1; + } else { + return -1; + } } sstr_t sstrdup(sstr_t s) { + return sstrdup_a(ucx_default_allocator(), s); +} + +sstr_t sstrdup_a(UcxAllocator *allocator, sstr_t s) { sstr_t newstring; - newstring.ptr = (char*) malloc(s.length + 1); - if (newstring.ptr != NULL) { + newstring.ptr = (char*)allocator->malloc(allocator->pool, s.length + 1); + if (newstring.ptr) { newstring.length = s.length; newstring.ptr[newstring.length] = 0; - + memcpy(newstring.ptr, s.ptr, s.length); + } else { + newstring.length = 0; } - + return newstring; } + +sstr_t sstrtrim(sstr_t string) { + sstr_t newstr = string; + if (string.length == 0) { + return newstr; + } + + size_t i; + for(i=0;i<string.length;i++) { + char c = string.ptr[i]; + if(c > 32) { + break; + } + } + newstr.ptr = &string.ptr[i]; + newstr.length = string.length - i; + + if(newstr.length == 0) { + return newstr; + } + + i = newstr.length - 1; + for(;;) { + char c = newstr.ptr[i]; + if(c > 32) { + break; + } + if(i > 0) { + i--; + } else { + break; + } + } + newstr.length = i + 1; + + return newstr; +}
--- a/ucx/string.h Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/string.h Mon Aug 12 14:40:19 2013 +0200 @@ -1,99 +1,345 @@ /* - * File: sstring.h - * Author: olaf + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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. * - * Created on 17. Juni 2010, 13:26 + * 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. + */ +/** + * Bounded string implementation. + * + * The UCX strings (<code>sstr_t</code>) provide an alternative to C strings. + * The main difference to C strings is, that <code>sstr_t</code> does <b>not + * need to be <code>NULL</code>-terminated</b>. Instead the length is stored + * within the structure. + * + * When using <code>sstr_t</code>, developers must be full aware of what type + * of string (<code>NULL</code>-terminated) or not) they are using, when + * accessing the <code>char* ptr</code> directly. + * + * The UCX string module provides some common string functions, known from + * standard libc, working with <code>sstr_t</code>. + * + * @file string.h + * @author Mike Becker + * @author Olaf Wintermann */ -#ifndef _SSTRING_H -#define _SSTRING_H +#ifndef UCX_STRING_H +#define UCX_STRING_H #include "ucx.h" +#include "allocator.h" #include <stddef.h> -/* use macros for literals only */ -#define S(s) { (char*)s, sizeof(s)-1 } -#define ST(s) sstrn((char*)s, sizeof(s)-1) +/** Shortcut for a <code>sstr_t struct</code> literal. */ +#define ST(s) { (char*)s, sizeof(s)-1 } +/** Shortcut for the conversion of a C string to a <code>sstr_t</code>. */ +#define S(s) sstrn((char*)s, sizeof(s)-1) #ifdef __cplusplus extern "C" { #endif -typedef struct sstring { +/** + * The UCX string structure. + */ +typedef struct { + /** A reference to the string (<b>not necessarily <code>NULL</code> + * -terminated</b>) */ char *ptr; + /** The length of the string */ size_t length; } sstr_t; -/* - * creates a new sstr_t from a null terminated string +/** + * Creates a new sstr_t based on a C string. + * + * The length is implicitly inferred by using a call to <code>strlen()</code>. * - * s null terminated string + * <b>Note:</b> the sstr_t will hold a <i>reference</i> to the C string. If you + * do want a copy, use sstrdup() on the return value of this function. + * + * @param cstring the C string to wrap + * @return a new sstr_t containing the C string + * + * @see sstrn() */ -sstr_t sstr(char *s); +sstr_t sstr(char *cstring); -/* - * creates a new sstr_t from a string and length +/** + * Creates a new sstr_t of the specified length based on a C string. * - * s string - * n length of string + * <b>Note:</b> the sstr_t will hold a <i>reference</i> to the C string. If you + * do want a copy, use sstrdup() on the return value of this function. + * + * @param cstring the C string to wrap + * @param length the length of the string + * @return a new sstr_t containing the C string + * + * @see sstr() + * @see S() */ -sstr_t sstrn(char *s, size_t n); +sstr_t sstrn(char *cstring, size_t length); -/* - * gets the length of n sstr_t strings +/** + * Returns the cumulated length of all specified strings. + * + * At least one string must be specified. + * + * <b>Attention:</b> if the count argument does not match the count of the + * specified strings, the behavior is undefined. * - * n number of strings - * s string - * ... strings + * @param count the total number of specified strings (so at least 1) + * @param string the first string + * @param ... all other strings + * @return the cumulated length of all strings */ -size_t sstrnlen(size_t n, sstr_t s, ...); +size_t sstrnlen(size_t count, sstr_t string, ...); + + +/** + * Concatenates strings. + * + * At least one string must be specified and there must be enough memory + * available referenced by the destination sstr_t.ptr for this function to + * successfully concatenate all specified strings. + * + * The sstr_t.length of the destination string specifies the capacity and + * should match the total memory available referenced by the destination + * sstr_t.ptr. This function <i>never</i> copies data beyond the capacity and + * does not modify any of the source strings. + * + * <b>Attention:</b> + * <ul> + * <li>Any content in the destination string will be overwritten</li> + * <li>The destination sstr_t.ptr is <b>NOT</b> + * <code>NULL</code>-terminated</li> + * <li>The destination sstr_t.length is set to the total length of the + * concatenated strings</li> + * <li><i>Hint:</i> get a <code>NULL</code>-terminated string by performing + * <code>mystring.ptr[mystring.length]='\0'</code> after calling this + * function</li> + * </ul> + * + * @param dest new sstr_t with capacity information and allocated memory + * @param count the total number of strings to concatenate + * @param src the first string + * @param ... all other strings + * @return the argument for <code>dest</code> is returned + */ +sstr_t sstrncat(sstr_t dest, size_t count, sstr_t src, ...); -/* - * concatenates n strings - * - * n number of strings - * s new string with enough memory allocated - * ... strings +/** + * Returns a substring starting at the specified location. + * + * <b>Attention:</b> the new string references the same memory area as the + * input string and will <b>NOT</b> be <code>NULL</code>-terminated. + * Use sstrdup() to get a copy. + * + * @param string input string + * @param start start location of the substring + * @return a substring of <code>string</code> starting at <code>start</code> + * + * @see sstrsubsl() + * @see sstrchr() */ -sstr_t sstrncat(size_t n, sstr_t s, sstr_t c1, ...); +sstr_t sstrsubs(sstr_t string, size_t start); - -/* - * +/** + * Returns a substring with a maximum length starting at the specified location. + * + * <b>Attention:</b> the new string references the same memory area as the + * input string and will <b>NOT</b> be <code>NULL</code>-terminated. + * Use sstrdup() to get a copy. + * + * @param string input string + * @param start start location of the substring + * @param length the maximum length of the substring + * @return a substring of <code>string</code> starting at <code>start</code> + * with a maximum length of <code>length</code> + * + * @see sstrsubs() + * @see sstrchr() */ -sstr_t sstrsubs(sstr_t s, size_t start); +sstr_t sstrsubsl(sstr_t string, size_t start, size_t length); -/* - * +/** + * Returns a substring starting at the location of the first occurrence of the + * specified character. + * + * If the string does not contain the character, an empty string is returned. + * + * @param string the string where to locate the character + * @param chr the character to locate + * @return a substring starting at the least location of <code>chr</code> + * + * @see sstrsubs() */ -sstr_t sstrsubsl(sstr_t s, size_t start, size_t length); +sstr_t sstrchr(sstr_t string, int chr); -/* - * splits s into n parts +/** + * Splits a string into parts by using a delimiter string. + * + * This function will return <code>NULL</code>, if one of the following happens: + * <ul> + * <li>the string length is zero</li> + * <li>the delimeter length is zero</li> + * <li>the string equals the delimeter</li> + * <li>memory allocation fails</li> + * </ul> + * + * The integer referenced by <code>count</code> is used as input and determines + * the maximum size of the resulting list, i.e. the maximum count of splits to + * perform + 1. + * + * The integer referenced by <code>count</code> is also used as output and is + * set to + * <ul> + * <li>-2, on memory allocation errors</li> + * <li>-1, if either the string or the delimiter is an empty string</li> + * <li>0, if the string equals the delimiter</li> + * <li>1, if the string does not contain the delimiter</li> + * <li>the count of list items, otherwise</li> + * </ul> + * + * If the string starts with the delimiter, the first item of the resulting + * list will be an empty string. + * + * If the string ends with the delimiter and the maximum list size is not + * exceeded, the last list item will be an empty string. + * + * <b>Attention:</b> All list items <b>AND</b> all sstr_t.ptr of the list + * items must be manually passed to <code>free()</code>. Use sstrsplit_a() with + * an allocator to managed memory, to avoid this. * - * s the string to split - * d the delimiter string - * n the maximum size of the resulting list - * a size of 0 indicates an unbounded list size - * the actual size of the list will be stored here - * - * Hint: use this value to avoid dynamic reallocation of the result list - * - * Returns a list of the split strings - * NOTE: this list needs to be freed manually after usage - * - * Returns NULL on error + * @param string the string to split + * @param delim the delimiter string + * @param count IN: the maximum size of the resulting list (0 for an + * unbounded list), OUT: the actual size of the list + * @return a list of the split strings as sstr_t array or + * <code>NULL</code> on error + * + * @see sstrsplit_a() */ -sstr_t* sstrsplit(sstr_t s, sstr_t d, size_t *n); +sstr_t* sstrsplit(sstr_t string, sstr_t delim, size_t *count); +/** + * Performing sstrsplit() using an UcxAllocator. + * + * <i>Read the description of sstrsplit() for details.</i> + * + * The memory for the sstr_t.ptr pointers of the list items and the memory for + * the sstr_t array itself are allocated by using the UcxAllocator.malloc() + * function. + * + * <b>Note:</b> the allocator is not used for memory that is freed within the + * same call of this function (locally scoped variables). + * + * @param allocator the UcxAllocator used for allocating memory + * @param string the string to split + * @param delim the delimiter string + * @param count IN: the maximum size of the resulting list (0 for an + * unbounded list), OUT: the actual size of the list + * @return a list of the split strings as sstr_t array or + * <code>NULL</code> on error + * + * @see sstrsplit() + */ +sstr_t* sstrsplit_a(UcxAllocator *allocator, sstr_t string, sstr_t delim, + size_t *count); + +/** + * Compares two UCX strings with standard <code>memcmp()</code>. + * + * At first it compares the sstr_t.length attribute of the two strings. The + * <code>memcmp()</code> function is called, if and only if the lengths match. + * + * @param s1 the first string + * @param s2 the second string + * @return -1, if the length of s1 is less than the length of s2 or 1, if the + * length of s1 is greater than the length of s2 or the result of + * <code>memcmp()</code> otherwise (i.e. 0 if the strings match) + */ int sstrcmp(sstr_t s1, sstr_t s2); -sstr_t sstrdup(sstr_t s); +/** + * Creates a duplicate of the specified string. + * + * The new sstr_t will contain a copy allocated by standard + * <code>malloc()</code>. So developers <b>MUST</b> pass the sstr_t.ptr to + * <code>free()</code>. + * + * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- + * terminated. + * + * @param string the string to duplicate + * @return a duplicate of the string + * @see sstrdup_a() + */ +sstr_t sstrdup(sstr_t string); + +/** + * Creates a duplicate of the specified string using an UcxAllocator. + * + * The new sstr_t will contain a copy allocated by the allocators + * ucx_allocator_malloc function. So it is implementation depended, whether the + * returned sstr_t.ptr pointer must be passed to the allocators + * ucx_allocator_free function manually. + * + * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- + * terminated. + * + * @param allocator a valid instance of an UcxAllocator + * @param string the string to duplicate + * @return a duplicate of the string + * @see sstrdup() + */ +sstr_t sstrdup_a(UcxAllocator *allocator, sstr_t string); + +/** + * Omits leading and trailing spaces. + * + * This function returns a new sstr_t containing a trimmed version of the + * specified string. + * + * <b>Note:</b> the new sstr_t references the same memory, thus you + * <b>MUST NOT</b> pass the sstr_t.ptr of the return value to + * <code>free()</code>. It is also highly recommended to avoid assignments like + * <code>mystr = sstrtrim(mystr);</code> as you lose the reference to the + * source string. Assignments of this type are only permitted, if the + * sstr_t.ptr of the source string does not need to be freed or if another + * reference to the source string exists. + * + * @param string the string that shall be trimmed + * @return a new sstr_t containing the trimmed string + */ +sstr_t sstrtrim(sstr_t string); #ifdef __cplusplus } #endif -#endif /* _SSTRING_H */ +#endif /* UCX_STRING_H */
--- a/ucx/test.c Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/test.c Mon Aug 12 14:40:19 2013 +0200 @@ -1,12 +1,39 @@ -/* - * File: test.c - * Author: Mike +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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. * - * Created on 18. Februar 2012, 14:15 + * 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 "test.h" + +struct UcxTestList{ + UcxTest test; + UcxTestList *next; +}; + UcxTestSuite* ucx_test_suite_new() { UcxTestSuite* suite = (UcxTestSuite*) malloc(sizeof(UcxTestSuite)); if (suite != NULL) { @@ -14,24 +41,55 @@ suite->failure = 0; suite->tests = NULL; } + return suite; } void ucx_test_suite_free(UcxTestSuite* suite) { - ucx_list_free(suite->tests); + UcxTestList *l = suite->tests; + while (l != NULL) { + UcxTestList *e = l; + l = l->next; + free(e); + } free(suite); } -void ucx_test_register(UcxTestSuite* suite, UcxTest test) { - suite->tests = ucx_list_append(suite->tests, (void*) test); +int ucx_test_register(UcxTestSuite* suite, UcxTest test) { + if (suite->tests) { + UcxTestList *newelem = (UcxTestList*) malloc(sizeof(UcxTestList)); + if (newelem) { + newelem->test = test; + newelem->next = NULL; + + UcxTestList *last = suite->tests; + while (last->next) { + last = last->next; + } + last->next = newelem; + + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } + } else { + suite->tests = (UcxTestList*) malloc(sizeof(UcxTestList)); + if (suite->tests) { + suite->tests->test = test; + suite->tests->next = NULL; + + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } + } } void ucx_test_run(UcxTestSuite* suite, FILE* output) { suite->success = 0; suite->failure = 0; - UCX_FOREACH (UcxList*, suite->tests, e) { - UcxTest test = (UcxTest) (e->data); - test(suite, output); + for (UcxTestList* elem = suite->tests ; elem ; elem = elem->next) { + elem->test(suite, output); } fwrite("\nAll test completed.\n", 1, 21, output); fprintf(output, " Total: %d\n Success: %d\n Failure: %d\n",
--- a/ucx/test.h Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/test.h Mon Aug 12 14:40:19 2013 +0200 @@ -1,90 +1,223 @@ -/* - * File: test.h - * Author: Mike +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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. * - * Created on 18. Februar 2012, 14:15 - * - * - * + * 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: **** * - * UCX_TEST_DECLARE(function_name) + * <pre> + * UCX_TEST(function_name) + * UCX_TEST_SUBROUTINE(subroutine_name, paramlist) // optional + * </pre> * * **** IN SOURCE FILE: **** + * <pre> + * UCX_TEST_SUBROUTINE(subroutine_name, paramlist) { + * // tests with UCX_TEST_ASSERT() + * } + * + * UCX_TEST(function_name) { + * // memory allocation and other stuff here + * #UCX_TEST_BEGIN + * // tests with UCX_TEST_ASSERT() and/or + * // calls with UCX_TEST_CALL_SUBROUTINE() here + * #UCX_TEST_END + * // cleanup of memory here + * } + * </pre> * - * UCX_TEST_IMPLEMENT(function_name) { - * <memory allocation and other stuff here> - * UCX_TEST_BEGIN - * <tests with UCX_TEST_ASSERT here> - * UCX_TEST_END - * <cleanup of memory here> - * } + * <b>Note:</b> if a test fails, a longjump is performed + * back to the #UCX_TEST_BEGIN macro! + * + * <b>Attention:</b> Do not call own functions within a test, that use + * UCX_TEST_ASSERT() macros and are not defined by using UCX_TEST_SUBROUTINE(). + * * - * PLEASE NOTE: if a test fails, a longjump is performed - * back to the UCX_TEST_BEGIN macro! - * - * You may use multiple BEGIN-END blocks if you are aware of the - * longjmp behaviour. + * @author Mike Becker + * @author Olaf Wintermann * */ -#ifndef TEST_H -#define TEST_H +#ifndef UCX_TEST_H +#define UCX_TEST_H #include "ucx.h" #include <stdio.h> #include <string.h> #include <setjmp.h> -#include "list.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 __FUNC__. + * We use __FUNC__ so we define it for those compilers which use + * <code>__func__</code>. + */ #define __FUNCTION__ __func__ #endif -typedef struct { - unsigned int success; - unsigned int failure; - UcxList *tests; -} UcxTestSuite; - +/** Type for the internal list of test cases. */ +typedef struct UcxTestList UcxTestList; +/** Type for the UcxTestSuite. */ +typedef struct UcxTestSuite UcxTestSuite; +/** Pointer to a test function. */ typedef void(*UcxTest)(UcxTestSuite*,FILE*); +/** + * A test suite containing multiple test cases. + */ +struct UcxTestSuite { + /** 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; + /** + * Internal list of test cases. + * Use ucx_test_register() to add tests to this list. + */ + UcxTestList *tests; +}; + +/** + * Creates a new test suite. + * @return a new test suite + */ UcxTestSuite* ucx_test_suite_new(); -void ucx_test_suite_free(UcxTestSuite*); +/** + * Destroys a test suite. + * @param the test suite to destroy + */ +void ucx_test_suite_free(UcxTestSuite* suite); -void ucx_test_register(UcxTestSuite*, UcxTest); -void ucx_test_run(UcxTestSuite*, FILE*); +/** + * 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 <code>EXIT_SUCCESS</code> on success or + * <code>EXIT_FAILURE</code> on failure + */ +int ucx_test_register(UcxTestSuite* suite, UcxTest test); +/** + * Runs a test suite and writes the test log to the specified stream. + * @param suite the test suite to run + * @param outstream the stream the log shall be written to + */ +void ucx_test_run(UcxTestSuite* suite, FILE* outstream); -#define UCX_TEST_DECLARE(name) void name(UcxTestSuite*,FILE *) -#define UCX_TEST_IMPLEMENT(name) void name(UcxTestSuite* _suite_,FILE *_output_) +/** + * Macro for a #UcxTest function header. + * + * Use this macro to declare and/or define an #UcxTest function. + * + * @param name the name of the test function + */ +#define UCX_TEST(name) void name(UcxTestSuite* _suite_,FILE *_output_) +/** + * Marks the begin of a test. + * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>after</b> + * #UCX_TEST_BEGIN. + * + * @see #UCX_TEST_END + */ #define UCX_TEST_BEGIN fwrite("Running ", 1, 8, _output_);\ fwrite(__FUNCTION__, 1, strlen(__FUNCTION__), _output_);\ fwrite("... ", 1, 4, _output_);\ jmp_buf _env_; \ if (!setjmp(_env_)) { +/** + * 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 UCX_TEST_ASSERT(condition,message) if (!(condition)) { \ fwrite(message".\n", 1, 2+strlen(message), _output_); \ _suite_->failure++; \ longjmp(_env_, 1);\ } -#define UCX_TEST_SUBROUTINE(name,data) void name(UcxTestSuite* _suite_,\ - FILE *_output_, jmp_buf _env_, void* data) -#define UCX_TEST_CALL_SUBROUTINE(name,data) name(_suite_,_output_,_env_,data); +/** + * Macro for a test subroutine function header. + * + * Use this to declare and/or define an subroutine that can be called by using + * UCX_TEST_CALL_SUBROUTINE(). + * + * @param name the name of the subroutine + * @param ... the parameter list + * + * @see UCX_TEST_CALL_SUBROUTINE() + */ +#define UCX_TEST_SUBROUTINE(name,...) void name(UcxTestSuite* _suite_,\ + FILE *_output_, jmp_buf _env_, __VA_ARGS__) +/** + * Macro for calling a test subroutine. + * + * Subroutines declared with UCX_TEST_SUBROUTINE() can be called by using this + * macro. + * + * <b>Note:</b> You may <b>only</b> call subroutines within a #UCX_TEST_BEGIN- + * #UCX_TEST_END-block. + * + * @param name the name of the subroutine + * @param ... the argument list + * + * @see UCX_TEST_SUBROUTINE() + */ +#define UCX_TEST_CALL_SUBROUTINE(name,...) \ + name(_suite_,_output_,_env_,__VA_ARGS__); + +/** + * Marks the end of a test. + * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>before</b> + * #UCX_TEST_END. + * + * @see #UCX_TEST_BEGIN + */ #define UCX_TEST_END fwrite("success.\n", 1, 9, _output_); _suite_->success++;} #ifdef __cplusplus } #endif -#endif /* TEST_H */ +#endif /* UCX_TEST_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/ucx.c Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,37 @@ +/** + * @mainpage UAP Common Extensions + * Library with common and useful functions, macros and data structures. + * <p> + * Latest available source:<br/> + * <a href="https://develop.uap-core.de/hg/ucx"> + * https://develop.uap-core.de/hg/ucx</a> + * </p> + * + * <h2>LICENCE</h2> + * + * Copyright 2013 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 "ucx.h"
--- a/ucx/ucx.h Sat Dec 01 20:34:55 2012 +0100 +++ b/ucx/ucx.h Mon Aug 12 14:40:19 2013 +0200 @@ -1,8 +1,36 @@ -/* - * File: ucx.h - * Author: olaf +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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. * - * Created on 31. Dezember 2011, 17:17 + * 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. + */ +/** + * Main UCX Header providing most common definitions. + * + * @file ucx.h + * @author Mike Becker + * @author Olaf Wintermann */ #ifndef UCX_H @@ -10,37 +38,77 @@ #include <stdlib.h> +#ifdef _WIN32 +#if !(defined __ssize_t_defined || defined _SSIZE_T_) +#include <BaseTsd.h> +typedef SSIZE_T ssize_t; +#define __ssize_t_defined +#define _SSIZE_T_ +#endif /* __ssize_t_defined and _SSIZE_T */ +#else /* !_WIN32 */ +#include <sys/types.h> +#endif /* _WIN32 */ + #ifdef __cplusplus #ifndef _Bool #define _Bool bool #define restrict #endif +#define UCX_EXTERN extern "C" extern "C" { +#else +#define UCX_EXTERN #endif -#define UCX_FOREACH(type,list,elem) \ - for (type elem = list ; elem != NULL ; elem = elem->next) - -#ifdef __cplusplus -#define ucx_dynarray_new(type,identifier,length)\ - type* identifier; identifier = new type[length] -#define ucx_dynarray_free(identifier) delete [] identifier -#else -#define ucx_dynarray_new(type,identifier,length)\ - type identifier[length] -#define ucx_dynarray_free(identifier) -#endif - -/* element1,element2,custom data -> {-1,0,1} */ +/** + * Function pointer to a compare function. + * + * The compare function shall take three arguments: the two values that shall be + * compared and optional additional data. + * The function shall then return -1 if the first argument is less than the + * second argument, 1 if the first argument is greater than the second argument + * and 0 if both arguments are equal. If the third argument is + * <code>NULL</code>, it shall be ignored. + */ typedef int(*cmp_func)(void*,void*,void*); -/* element,custom data -> copy of element */ +/** + * Function pointer to a copy function. + * + * The copy function shall create a copy of the first argument and may use + * additional data provided by the second argument. If the second argument is + * <code>NULL</code>, it shall be ignored. + + * <b>Attention:</b> if pointers returned by functions of this type may be + * passed to <code>free()</code> depends on the implementation of the + * respective <code>copy_func</code>. + */ typedef void*(*copy_func)(void*,void*); -/* buffer, element size, element count, stream */ +/** + * Function pointer to a write function. + * + * The signature of the write function shall be compatible to the signature + * of standard <code>fwrite</code>, though it may use arbitrary data types for + * source and destination. + * + * The arguments shall contain (in ascending order): a pointer to the source, + * the length of one element, the element count and a pointer to the + * destination. + */ typedef size_t(*write_func)(const void*, size_t, size_t, void*); -/* buffer, element size, element count, stream */ +/** + * Function pointer to a read function. + * + * The signature of the read function shall be compatible to the signature + * of standard <code>fread</code>, though it may use arbitrary data types for + * source and destination. + * + * The arguments shall contain (in ascending order): a pointer to the + * destination, the length of one element, the element count and a pointer to + * the source. + */ typedef size_t(*read_func)(void*, size_t, size_t, void*); #ifdef __cplusplus
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/utils.c Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,100 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 "utils.h" +#include "math.h" + +/* COPY FUCNTIONS */ +void* ucx_strcpy(void* s, void* data) { + char *str = (char*) s; + size_t n = 1+strlen(str); + char *cpy = (char*) malloc(n); + memcpy(cpy, str, n); + return cpy; +} + +void* ucx_memcpy(void* m, void* n) { + size_t k = *((size_t*)n); + void *cpy = malloc(k); + memcpy(cpy, m, k); + return cpy; +} + +/* COMPARE FUNCTION */ + +int ucx_strcmp(void *s1, void *s2, void *data) { + return strcmp((char*)s1, (char*)s2); +} + +int ucx_strncmp(void *s1, void *s2, void *n) { + return strncmp((char*)s1, (char*)s2, *((size_t*) n)); +} + +int ucx_intcmp(void *i1, void *i2, void *data) { + int a = *((int*) i1); + int b = *((int*) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int ucx_floatcmp(void *f1, void *f2, void *epsilon) { + float a = *((float*) f1); + float b = *((float*) f2); + float e = !epsilon ? 1e-6f : *((float*)epsilon); + if (fabsf(a - b) < e) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int ucx_doublecmp(void *d1, void *d2, void *epsilon) { + double a = *((float*) d1); + double b = *((float*) d2); + double e = !epsilon ? 1e-14 : *((double*)epsilon); + if (fabs(a - b) < e) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int ucx_ptrcmp(void *ptr1, void *ptr2, void *data) { + if (ptr1 == ptr2) { + return 0; + } else { + return ptr1 < ptr2 ? -1 : 1; + } +} + +int ucx_memcmp(void *ptr1, void *ptr2, void *n) { + return memcmp(ptr1, ptr2, *((size_t*)n)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/utils.h Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,140 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 utils.h + * + * Common utilities like compare and copy functions. + * + * @author Mike Becker + * @author Olaf Wintermann + */ + +#ifndef UCX_UTILS_H +#define UCX_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ucx.h" +#include <string.h> + +/** + * Copies a string. + * @param s the string to copy + * @param data omitted + * @return a pointer to a copy of s1 that can be passed to free(void*) + */ +void *ucx_strcpy(void *s, void *data); + +/** + * Copies a memory area. + * @param m a pointer to the memory area + * @param n a pointer to the size_t containing the size of the memory area + * @return a pointer to a copy of the specified memory area that can + * be passed to free(void*) + */ +void *ucx_memcpy(void *m, void *n); + +/** + * Wraps the strcmp function. + * @param s1 string one + * @param s2 string two + * @param data omitted + * @return the result of strcmp(s1, s2) + */ +int ucx_strcmp(void *s1, void *s2, void *data); + +/** + * Wraps the strncmp function. + * @param s1 string one + * @param s2 string two + * @param n a pointer to the size_t containing the third strncmp parameter + * @return the result of strncmp(s1, s2, *n) + */ +int ucx_strncmp(void *s1, void *s2, void *n); + +/** + * Compares two integers of type int. + * @param i1 pointer to integer one + * @param i2 pointer to integer two + * @param data omitted + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ + +int ucx_intcmp(void *i1, void *i2, void *data); + +/** + * Compares two real numbers of type float. + * @param f1 pointer to float one + * @param f2 pointer to float two + * @param if provided: a pointer to precision (default: 1e-6f) + * @return -1, if *f1 is less than *f2, 0 if both are equal, + * 1 if *f1 is greater than *f2 + */ + +int ucx_floatcmp(void *f1, void *f2, void *data); + +/** + * Compares two real numbers of type double. + * @param f1 pointer to double one + * @param f2 pointer to double two +* @param if provided: a pointer to precision (default: 1e-14) + * @return -1, if *d1 is less than *d2, 0 if both are equal, + * 1 if *d1 is greater than *d2 + */ + +int ucx_doublecmp(void *d1, void *d2, void *data); + +/** + * Compares two pointers. + * @param ptr1 pointer one + * @param ptr2 pointer two + * @param data omitted + * @return -1 if ptr1 is less than ptr2, 0 if both are equal, + * 1 if ptr1 is greater than ptr2 + */ +int ucx_ptrcmp(void *ptr1, void *ptr2, void *data); + +/** + * Compares two memory areas. + * @param ptr1 pointer one + * @param ptr2 pointer two + * @param n a pointer to the size_t containing the third parameter for memcmp + * @return the result of memcmp(ptr1, ptr2, *n) + */ +int ucx_memcmp(void *ptr1, void *ptr2, void *n); + +#ifdef __cplusplus +} +#endif + +#endif /* UCX_UTILS_H */ +