/*******************************************************************
  uLan Utilities Library - C library of basic reusable constructions

  ul_dbufmore.c	- more functions for dynamically allocated buffer

  (C) Copyright 2001-2004 by Pavel Pisa - Originator
  (C) Copyright 2003-2004 by Frantisek Vacek - Originator

  The uLan utilities library can be used, copied and modified under
  next licenses
    - GPL - GNU General Public License
    - LGPL - GNU Lesser General Public License
    - MPL - Mozilla Public License
    - and other licenses added by project originators
  Code can be modified and re-distributed under any combination
  of the above listed licenses. If contributor does not agree with
  some of the licenses, he/she can delete appropriate line.
  Warning, if you delete all lines, you are not allowed to
  distribute source code and/or binaries utilizing code.
  
  See files COPYING and README for details.

 *******************************************************************/
#include <string.h>
#include "ul_utmalloc.h"
#include "ul_dbuff.h"

typedef unsigned char byte;

//#define UL_DBUFF_LOGGING
#ifdef UL_DBUFF_LOGGING
    #include "can_vca.h"
    
    #define LOG_FATAL   0
    #define LOG_ERR     1
    #define LOG_MSG     2
    #define LOG_INF     3
    #define LOG_DEB     4
#endif
//#undef DEBUG

//-----------------------------------------------------------------
/**
 * ul_dbuff_set_capacity - change capacity of buffer to at least @new_capacity
 * @buf: buffer structure
 * @new_capacity: new capacity
 *
 * Returns real capacity of reallocated buffer
 */
int ul_dbuff_set_capacity(ul_dbuff_t *buf, int new_capacity)
{
    if(new_capacity < 0) new_capacity = 0;
    #ifdef UL_DBUFF_LOGGING
    int old_capacity = buf->capacity;
    #endif
    if(buf->flags & UL_DBUFF_IS_STATIC) {
        if(!buf->data || !buf->capacity) {
            buf->capacity = sizeof(buf->sbuff);
            buf->data = buf->sbuff;
        }
    }
    else{
        if(new_capacity <= UL_DBUFF_SLEN) {
            if((buf->data) && (buf->data != buf->sbuff)) {
                // dynamic storage -> static
                unsigned long c = buf->capacity;
                if(c > sizeof(buf->sbuff)) c = sizeof(buf->sbuff);
                memcpy(buf->sbuff, buf->data, c);
                free(buf->data);
            }
            buf->capacity = sizeof(buf->sbuff);
            buf->data = buf->sbuff;
        }
        else if(new_capacity != buf->capacity) {
            if(buf->data == buf->sbuff) {
                // static storage -> dynamic
                buf->data = malloc((size_t)new_capacity);
                if(buf->data != NULL) {
                    memcpy(buf->data, buf->sbuff, buf->capacity);
                    buf->capacity = new_capacity;
                }
                else {
                    buf->capacity = 0;
                }
            }
            else {
                // dynamic storage -> dynamic
	        unsigned char *new_data;
                new_data = realloc(buf->data, (size_t)new_capacity);
                if(buf->data != NULL) {
                    buf->capacity = new_capacity;
                    buf->data = new_data;
                }
                else {
                    /* Old data are not changed if realloc fails, capacity remains at old value */
                    #ifdef UL_DBUFF_LOGGING
                    vca_log("dbuff", LOG_DEB, "realloc buffer to %d failed\n", new_capacity);
                    #endif
                }
            }
        }
    }
    if(buf->len > buf->capacity) buf->len = buf->capacity;
    #ifdef UL_DBUFF_LOGGING
    vca_log("dbuff", LOG_DEB, "capacity changed from %d to %ld, required %d\n", old_capacity, buf->capacity, new_capacity);
    #endif
    return buf->capacity;
}                                                                               

//-----------------------------------------------------------------
/**
 * ul_dbuff_set_len - sets a new len of the buffer, change the capacity if neccessary 
 * @buf: buffer structure
 * @new_len: new desired buffer length
 *
 * Returns new buffer length
 */
int ul_dbuff_set_len(ul_dbuff_t *buf, int new_len)
{
    if(new_len < 0) new_len = 0;
    if(new_len > buf->capacity) {
        unsigned long new_cap = UL_DBUFF_SLEN;
        for(; new_cap < new_len; new_cap <<= 1);
        ul_dbuff_set_capacity(buf, new_cap);
    }
    if(new_len > buf->capacity) {
        buf->data = buf->sbuff;
        strncpy(buf->data, "set_len ERROR", buf->capacity);
        new_len = buf->capacity;
    }
    buf->len = new_len;
    return buf->len;
}
 
//-----------------------------------------------------------------
/**
 * ul_dbuff_cpy - copies bytes to buffer and change its capacity if neccessary like memset
 * @buf: buffer structure
 * @b: appended bytes
 * @n: number of apended bytes
 *
 * Returns: length of buffer
 */
int ul_dbuff_set(ul_dbuff_t *buf, byte b, int n)
{
    ul_dbuff_set_len(buf, n);
    memset(buf->data, b, buf->len);
    return buf->len;
}

//-----------------------------------------------------------------
/**
 * ul_dbuff_cpy - copies bytes to buffer and change its capacity if neccessary 
 * @buf: buffer structure
 * @b: appended bytes
 * @n: number of apended bytes
 *
 * Returns: length of buffer
 */
int ul_dbuff_cpy(ul_dbuff_t *buf, const void *b, int n)
{
    if(b == NULL) return 0;
    ul_dbuff_set_len(buf, n);
    memcpy(buf->data, b, buf->len);
    return buf->len;
}

//-----------------------------------------------------------------
/**
 * ul_dbuff_cat - appends bytes at end of buffer and change its capacity if neccessary 
 * @buf: buffer structure
 * @b: appended bytes
 * @n: number of apended bytes
 *
 * Returns: length of buffer
 */
int ul_dbuff_cat(ul_dbuff_t *buf, const void *b, int n)
{
    unsigned long old_len = buf->len;
    unsigned long new_len = old_len + n;
    if(b == NULL) return 0;
    if(new_len == ul_dbuff_set_len(buf, new_len)) {
        memcpy(buf->data + old_len, b, n);
    }
    #ifdef UL_DBUFF_LOGGING
    else {
        vca_log("dbuff", LOG_DEB, "ul_dbuff_cat: set_len(%lu) error, old_len == %lu\n, act len == %lu\n", new_len, old_len, buf->len);
    }
    #endif
    return buf->len;
}

//-----------------------------------------------------------------
/**
 * ul_dbuff_strcat - appends str at dhe end of buffer and change its capacity if neccessary 
 * @buf: buffer structure
 * @str: string to append
 *
 * Returns number length of buffer (including terminating '\0')
 */
inline int ul_dbuff_strcat(ul_dbuff_t *buf, const char *str)
{
    /*
    #ifdef UL_DBUFF_LOGGING
    if(buf->len > 0) vca_log("dbuff", LOG_DEB, "ul_dbuff_strcat: '%s' + '%s'\n", buf->data, str);
    else vca_log("dbuff", LOG_DEB, "ul_dbuff_strcat: '' + %s\n", str);
    #endif
    */
    if(str == NULL) return 0;
    if(buf->len > 0 && buf->data[buf->len-1] == '\0') { 
        /* #ifdef UL_DBUFF_LOGGING
        vca_log("dbuff", LOG_DEB, "ul_dbuff_strcat: terminating zero found at %ld, after '%c'\n", buf->len-1, buf->data[buf->len - 2]);
        #endif
        */
        ul_dbuff_set_len(buf, buf->len - 1);
    }
    #ifdef UL_DBUFF_LOGGING
    if(buf->len > 0 && buf->data[buf->len-1] != '\0') {
        vca_log("dbuff", LOG_ERR, "ul_dbuff_strcat: terminating zero not found at %ld, found '%c'\n", buf->len-1, buf->data[buf->len-1]);
    }
    #endif
    /* #ifdef UL_DBUFF_LOGGING
    ul_dbuff_cat(buf, str, strlen(str) + 1);
    vca_log("dbuff", LOG_DEB, "ul_dbuff_strcat: returning '%s'\n", buf->data);
    return buf->len;
    #else
    return ul_dbuff_cat(buf, str, strlen(str) + 1);
    #endif */
    return ul_dbuff_cat(buf, str, strlen(str) + 1);
}
 
//-----------------------------------------------------------------
/**
 * ul_dbuff_strcpy - copy str to the buffer and change its capacity if neccessary 
 * @buf: buffer structure
 * @str: string to copy
 *
 * Returns number length of buffer (including terminating '\0')
 */
inline int ul_dbuff_strcpy(ul_dbuff_t *buf, const char *str)
{
    ul_dbuff_set_len(buf, 0);
    return ul_dbuff_strcat(buf, str);
}
 
//-----------------------------------------------------------------
/**
 * ul_dbuff_append_byte - appends byte at dhe end of buffer and change its capacity if neccessary 
 * @buf: buffer structure
 * @b: appended byte
 *
 * Returns number length of buffer (including terminating '\0')
 */
inline int ul_dbuff_append_byte(ul_dbuff_t *buf, unsigned char b)
{
    return ul_dbuff_cat(buf, &b, 1);
}
 
//-----------------------------------------------------------------
/**
 * ul_dbuff_ltrim - remove all white space characters from the left 
 * @buf: buffer structure
 *
 * Return: new length of buffer
 */
int ul_dbuff_ltrim(ul_dbuff_t *buf)
{
	byte *pb = buf->data;
    int i, j;
	for(i=0; pb[i]<=' ' && i<buf->len; i++);
	if(i > 0) {
		for(j=i, i=0; j<buf->len; j++, i++) pb[i] = pb[j];
        buf->len = i;
	}
	return buf->len;
}

//-----------------------------------------------------------------
/**
 * ul_dbuff_rtrim - remove all white space characters from the right
 * @buf: buffer structure
 *
 * if buffer is terminated by '\0', than is also terminated after rtrim
 *
 * Return: new length of buffer
 */
int ul_dbuff_rtrim(ul_dbuff_t *buf)
{
    int i = buf->len - 1;
    unsigned char *pb = buf->data;
    int terminated = 0;
    if(i < 0) return buf->len;
    if(pb[i] == '\0') terminated = 1;
    for(; i>=0; i--) {
		if(pb[i]>' ') {
            i++;
            if(terminated) pb[i++] = '\0';
            buf->len = i;
            break;
        }
	}
	return buf->len;
}

//-----------------------------------------------------------------
/**
 * ul_dbuff_trim - remove all white space characters from the right and from the left 
 * @buf: buffer structure
 *
 * Returns number length of buffer (including terminating '\0')
 */
int ul_dbuff_trim(ul_dbuff_t *buf)
{
    ul_dbuff_rtrim(buf);
    return ul_dbuff_ltrim(buf);
}
//-----------------------------------------------------------------
/**
 * ul_dbuff_cpos - searches string for char 
 * @buf:   searched dbuff
 * @what:  char to find
 * @quote: skip str areas quoted in quote chars<br>
 *         If you want to ignore quotes assign '\0' to quote in function call 
 *
 * Return: position of what char or negative value
 */
int ul_dbuff_cpos(const ul_dbuff_t *buf, unsigned char what, unsigned char quote)
{
    int in_quotes = 0;
    int ret;
    unsigned char *bytes = buf->data;
    for(ret = 0; ret < buf->len; ret++) {
        if(bytes[ret] == quote) {
            in_quotes = !in_quotes;
            continue;
        }
        if(bytes[ret] == what && !in_quotes) {
            break;
        }
    }
    if(ret >= buf->len) ret = -1;
    return ret;
}
//-----------------------------------------------------------------
/**
 * ul_str_cpos - searches string for char 
 * @str:   zero terminated string
 * @what:  char to find
 * @quote: skip str areas quoted in quote chars
 *         If you want to ignore quotes assign '\0' to quote in function call 
 *
 * Return: position of what char or negative value
 */
int ul_str_cpos(const unsigned char *str, unsigned char what, unsigned char quote)
{
    int in_quotes = 0;
    int ret;
    for(ret = 0; str[ret]; ret++) {
        if(str[ret] == quote) {
            in_quotes = !in_quotes;
            continue;
        }
        if(str[ret] == what && !in_quotes) {
            break;
        }
    }
    if(!str[ret]) ret = -1;
    return ret;
}
//-----------------------------------------------------------------
/**
 * ul_str_pos - searches string for substring
 * @str:   zero terminated string
 * @what:  string to find
 * @quote: skip str areas quoted in quote chars
 *         If you want to ignore quotes assign '\0' to quote in function call 
 *
 * Return: position of what string or negative value
 */
int ul_str_pos(const unsigned char *str, const unsigned char *what, unsigned char quote)
{
    int in_quotes = 0;
    int ret;
    for(ret = 0; str[ret]; ret++) {
        if(str[ret] == quote) {
            in_quotes = !in_quotes;
            continue;
        }
        if(!in_quotes) {
            int i;
            for(i=0; str[ret + i]==what[i] && what[i]; i++);
            if(!what[i]) return ret;
        }
    }
    return -1;
}
//-----------------------------------------------------------------
/**
 * ul_str_ncpy - copies string to the buffer
 * @to:         buffer where to copy str
 * @from:       zero terminated string
 * @buff_size:  size of the @to buffer (including terminating zero)
 *
 * Standard strncpy function have some disadvatages 
 * (ie. do not append term. zero if copied string doesn't fit in to buffer, fills whole rest of buffer with zeros)
 *
 * Returns strlen(to) or negative value in case of error
 */
int ul_str_ncpy(unsigned char *to, const unsigned char *from, int buff_size)
{
    int i;
    if(!to || !from) return -1;
    if(buff_size <= 0) return -2;
    for(i=0; i<buff_size-1 && from[i]; i++) to[i] = from[i];
    to[i] = '\0';
    return i;
}
//-----------------------------------------------------------------
/**
 * ul_dbuff_log_hex - writes content of dbuff to log
 * @buf: buffer structure
 * @log_level: logging level
 */
void ul_dbuff_log_hex(ul_dbuff_t *buf, int log_level)
{
#ifdef UL_DBUFF_LOGGING
    vca_log("dbuff", log_level, "[");
    int i;
    log_level |= VCA_LOGL_CONT;
    for(i=0; i<buf->len; i++) {
        char c = buf->data[i];
        if(i > 0) vca_log("dbuff", log_level, " ");
        if(c > ' ') vca_log("dbuff", log_level, " %c", c);
        else        vca_log("dbuff", log_level, "%02x", c);
    }
    vca_log("dbuff", log_level, "]\n");
#endif
}
//-----------------------------------------------------------------
/**
 * ul_dbuff_cut_pos - cut first @n bytes from @fromdb and copies it to @todb.
 * If @n is greater than fromdb.len whole @fromdb is copied to @todb.
 * If @n is negative position to cut is counted from the end of @fromdb.
 * If @n is zero @fromdb stays unchanged and todb is resized to len equal zero.
 * @fromdb: buffer to cut from
 * @todb:   buffer to copy to
 * @n:      position where to cut
 */
void ul_dbuff_cut_pos(ul_dbuff_t *fromdb, ul_dbuff_t *todb, int n)
{
    int newlen;
    if(n < 0) n = fromdb->len + n;
    if(n < 0) n = 0;
    if(n > fromdb->len) n = fromdb->len;
    newlen = fromdb->len - n;
    ul_dbuff_cpy(todb, fromdb->data, n);
    memmove(fromdb->data, fromdb->data + n, fromdb->len - n);
    ul_dbuff_set_len(fromdb, newlen);
}
//-----------------------------------------------------------------
/**
 * ul_dbuff_cut - cuts bytes before delimiter + delimiter char from @fromdb and copies tham to the @todb
 * If @fromdb doesn't contain delimiter @todb is trimmed to zero length.
 * @fromdb: buffer to cut from
 * @todb:   buffer to copy to
 * @delimiter: delimiter char
 * @quote:   quoted delimiters are ignored, @quote can be '\0', than it is ignored.
 */
void ul_dbuff_cut_delimited(ul_dbuff_t *fromdb, ul_dbuff_t *todb, char delimiter, char quote)
{
    int pos = ul_dbuff_cpos(fromdb, delimiter, quote);
    if(pos < 0) pos = -1;
    ul_dbuff_cut_pos(fromdb, todb, pos+1);
}
//-----------------------------------------------------------------
/**
 * ul_dbuff_cut_token - cuts not whitespaces from %fromdb to %todb.
 *                      Leading whitespaces are ignored. Cut string is trimmed.
 * @fromdb: buffer to cut from
 * @todb:   buffer to copy to
 */
void ul_dbuff_cut_token(ul_dbuff_t *fromdb, ul_dbuff_t *todb)
{
    const unsigned char *pc = (unsigned char*)fromdb->data;
    int pos;
    // skip leading white spaces
    for(pos=0; pc[pos]>0 && pc[pos]<=' ' && pos<fromdb->len; pos++);
    // skip token
    for(; pc[pos]>' ' && pos<fromdb->len; pos++);
    ul_dbuff_cut_pos(fromdb, todb, pos);
    ul_dbuff_trim(todb);
}
//-----------------------------------------------------------------

