ucx/properties.c

Thu, 21 Dec 2017 19:48:27 +0100

author
Mike Becker <universe@uap-core.de>
date
Thu, 21 Dec 2017 19:48:27 +0100
changeset 359
bacb54502b24
parent 335
c1bc13faadaa
permissions
-rw-r--r--

davql: allow ANYWHERE keyword in SELECT statements

This may seem pointless, but users might want to be explicit about this and the grammar is more consistent.

This commit also adds some no-ops to the functions body of the SET parser, because some day the grammar might allow more clauses after the WHERE clause.

/*
 * 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, 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;
}

mercurial