/*
 * sbuf.c
 *
 * A string buffer library.
 *
 * Author: Dario Rodriguez dario@softhome.net
 * This library is licensed on the terms of the GNU LGPL v2+
 */

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "sbuf.h"

/* sbuf, high level api */
sbuf *
sbuf_init(long bufsize)
{
        sbuf *buf;
        if(bufsize<=0 || (buf=malloc(sizeof(sbuf)))==NULL)
                return(NULL);
        memset(buf,0,sizeof(sbuf));
        if((buf->buf=malloc(bufsize))==NULL) {
                free(buf),buf=NULL;
                return(NULL);
        }
        buf->size=bufsize;
        return(buf);
}

sbuf *
sbuf_staticinit(sbuf *uninitsbuf, long bufsize, char *buf)
{
        if(uninitsbuf==NULL || buf==NULL || bufsize<=0)
                return(NULL);
        memset(uninitsbuf,0,sizeof(sbuf));
        uninitsbuf->buf=buf;
        uninitsbuf->size=bufsize;
        return(uninitsbuf);
}

void
sbuf_free(sbuf *buf)
{
        if(buf==NULL)
                return;
        if(buf->buf!=NULL)
                free(buf->buf),buf->buf=NULL;
        buf->size=0;
        free(buf);
}

void
sbuf_staticfree(sbuf *buf)
{
        if(buf==NULL)
                return;
        memset(buf,0,sizeof(sbuf));
}

int
sbuf_fill(sbuf *buf, int fd, long numbytes)
{
        long n;
        int nread;
        if(buf==NULL || fd==-1 || numbytes<0)
                return(-1);
        n=(numbytes>(buf->size-buf->used))?buf->size-buf->used:numbytes;
        nread=read(fd,buf->buf+buf->used,n);
        if(nread>=0)
                buf->used+=nread;
        return(nread);
}

char *
sbuf_getline(sbuf *buf)
{
        char *start,*end;
        if(buf==NULL)
                return(NULL);
        start=buf->buf+buf->got;
        if((end=memchr(start,'\n',buf->used-buf->got))==NULL)
                return(NULL);
        *end='\0';
        /* we accept lines both with "\n" and "\r\n"; strip the leading \r if necessary */
        if(end>start && end[-1]=='\r')
                end[-1]='\0';
        buf->got+=(end-start+1);
        return(start);
}

void
sbuf_discard(sbuf *buf)
{
        if(buf==NULL || buf->got==0)
                return;
        memmove(buf->buf,buf->buf+buf->got,buf->used-buf->got);
        buf->used-=buf->got;
        buf->got=0;
}

void
sbuf_wipe(sbuf *buf)
{
        buf->used=buf->got=0;
}

/* sbuf, addutional functionality */
char *
sbuf_getbytes(sbuf *buf, long numbytes)
{
        char *res;
        if(buf==NULL || numbytes>(buf->used-buf->got))
                return(NULL);
        res=buf->buf+buf->got;
        buf->got+=numbytes;
        return(res);
}

long
sbuf_count(sbuf *buf)
{
        if(buf==NULL)
                return(0);
        return(buf->used-buf->got);
}

char *
sbuf_ptr(sbuf *buf)
{
        return(buf->buf+buf->got);
}

long
sbuf_add(sbuf *buf, const char *data, long datasize)
{
        long added;
        if(buf==NULL || data==NULL || buf->used==buf->size)
                return(0);
        added=(datasize>(buf->size-buf->used))?(buf->size-buf->used):datasize;
        memcpy(buf->buf+buf->used,data,added);
        buf->used+=added;
        return(added);
}

long
sbuf_addstr(sbuf *buf, const char *str)
{
        if(buf==NULL || str==NULL)
                return(0);
        return(sbuf_add(buf,str,strlen(str)));
}

long
sbuf_unused(sbuf *buf)
{
        if(buf==NULL)
                return(0);
        return(buf->size-buf->used);
}

char *
sbuf_ptrunused(sbuf *buf)
{
        if(buf==NULL)
                return(NULL);
        return(buf->buf+buf->used);
}

long
sbuf_addfromunused(sbuf *buf, long numbytes)
{
        int added;
        if(buf==NULL || numbytes<=0)
                return(0);
        added=(numbytes>(buf->size-buf->used))?(buf->size-buf->used):numbytes;
        buf->used+=added;
        return(added);
}

long
sbuf_send(sbuf *buf, int fd, long numbytes)
{
        int n;
        int written;
        if(buf==NULL || fd==-1 || numbytes<=0)
                return(0);
        n=(numbytes>(buf->used-buf->got))?(buf->used-buf->got):numbytes;
        written=write(fd,buf->buf+buf->got,n);
        if(written>0)
                buf->got+=written;
        return(written);
}