Tue, 14 Jan 2020 22:05:34 +0100
add xml tree iterator
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2016 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)) { alfree(map->allocator, value.ptr); return 1; } } if (parser->error) { return parser->error; } else { return 0; } } // buffer size is documented - change doc, when you change bufsize! #define UCX_PROPLOAD_BUFSIZE 1024 int ucx_properties_load(UcxMap *map, FILE *file) { UcxProperties *parser = ucx_properties_new(); if(!(parser && map && file)) { return 1; } int error = 0; size_t r; char buf[UCX_PROPLOAD_BUFSIZE]; while((r = fread(buf, 1, UCX_PROPLOAD_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); void *v; sstr_t value; size_t written; UCX_MAP_FOREACH(k, v, iter) { value = sstr((char*)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; }