ui/common/ucx_properties.c

Wed, 30 Oct 2024 10:14:47 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 30 Oct 2024 10:14:47 +0100
branch
newapi
changeset 372
4657ec1229f2
parent 325
99a93a9250c4
permissions
-rw-r--r--

fix incorrect UiVar destruction

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "ucx_properties.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.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, cxstring *name, cxstring *value)  {   
    if(parser->tmplen > 0) {
        char *buf = parser->buffer + parser->pos;
        size_t len = parser->buflen - parser->pos;
        cxstring str = cx_strn(buf, len);
        cxstring nl = cx_strchr(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;
        }
        
        cxstring line = has_comment ? cx_strn(buf, comment_index) : cx_strn(buf, i);
        // check line
        if(delimiter_index == 0) {
            line = cx_strtrim(line);
            if(line.length != 0) {
                parser->error = 1;
            }
        } else {
            cxstring n = cx_strn(buf, delimiter_index);
            cxstring v = cx_strn(
                    buf + delimiter_index + 1,
                    line.length - delimiter_index - 1); 
            n = cx_strtrim(n);
            v = cx_strtrim(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, CxMap *map) {
    cxstring name;
    cxstring value;
    while(ucx_properties_next(parser, &name, &value)) {
        cxmutstr mutvalue = cx_strdup_a(map->collection.allocator, value);
        if(!mutvalue.ptr) {
            return 1;
        }
        if(cxMapPut(map, cx_hash_key_cxstr(name), mutvalue.ptr)) {
            cxFree(map->collection.allocator, mutvalue.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(CxMap *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(CxMap *map, FILE *file) {
    CxIterator iter = cxMapIterator(map);
    cxstring value;
    size_t written;

    cx_foreach(CxMapEntry *, v, iter) {
        value = cx_str(v->value);

        written = 0;
        written += fwrite(v->key->data, 1, v->key->len, file);
        written += fwrite(" = ", 1, 3, file);
        written += fwrite(value.ptr, 1, value.length, file);
        written += fwrite("\n", 1, 1, file);

        if (written != v->key->len + value.length + 4) {
            return 1;
        }
    }

    return 0;
}

mercurial