/*
 * $Id: strbuf.c,v 1.1 2000/08/07 23:15:04 emmerson Exp $
 *
 * Copyright (C) 1992 UCAR/Unidata
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose without fee is hereby granted, provided
 * that the above copyright notice appear in all copies, that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of UCAR/Unidata not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  UCAR makes no
 * representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.  It is
 * provided with no support and without obligation on the part of UCAR or
 * Unidata, to assist in its use, correction, modification, or enhancement.
 *
 * This file contains an implementation of the arbitrarily-extensible
 * `string-buffer' abstraction.
 */

/*LINTLIBRARY*/

#include "udposix.h"		/* for UDPOSIX environment */
#include <stdio.h>		/* needed by <assert.h> */
#include <string.h>		/* for str*() functions */
#include <assert.h>		/* for assert() */
#include "udalloc.h"		/* for UD_ALLOC() & UD_REALLOC() */
#include "strbuf.h"		/* for myself */

/*
 * String-buffer data-type definition:
 */
struct Strbuf {
    char	*buf;	/* Malloc(3)ed string buffer.  0-terminated. */
    size_t	max;	/* Maximum possible string-length.  Excludes 0. */
};


/*
 * Insure that a string-buffer can contain a string of the given size.
 * The new size is the old size multiplied by an approximation to the golden
 * ratio (1.618034...) or the required size -- whichever is larger.  The 
 * string buffer is unchanged if the reallocation fails.
 */
    Strbuf*
sbensure(sb, request)
    Strbuf	*sb;			/* String-buffer */
    size_t	request;		/* Number of characters to insure. 
					 * Excludes 0 */
{
    assert(sb != NULL);

    if (request > sb->max || sb->buf == NULL) {
	char	*cp;
	size_t	newsize	= (13*sb->max)/8;

	if (newsize < request)
	    newsize	= request;

	/*
	 * lint(1) might warn about a possible pointer alignment problem in 
	 * the following statement.  This may safely be ignored.
	 */
	if ((cp = UD_REALLOC(sb->buf, newsize+1, char)) == NULL)
	    return NULL;

	sb->buf	= cp;
	sb->max	= newsize;
    }

    return sb;
}


/*
 * Free a string-buffer.
 */
    Strbuf*
sbfree(sb)
    Strbuf	*sb;		/* String-buffer */
{
    if (sb != NULL) {
	if (sb->buf != NULL)
	    /*
	     * lint(1) might warn about a possible pointer alignment problem in 
	     * the following statement.  This may safely be ignored.
	     */
	    FREE(sb->buf);
	FREE(sb);
    }
    return NULL;
}


/*
 * Instantiate a string-buffer.
 */
    Strbuf*
sbnew(max)
    size_t	max;			/* Initial maximum string length. 
					 * Excludes 0 */
{
    Strbuf	*sb	= UD_ALLOC(1, Strbuf);

    if (sb == NULL)
	return NULL;

    sb->buf	= NULL;
    sb->max	= 0;

    if (sbensure(sb, max) == NULL) {
	sbfree(sb);
	sb	= NULL;
    }

    return sb;
}


/*
 * Copy a string of specified length to a string-buffer.
 */
    Strbuf*
sbncpy(sb, string, len)
    Strbuf	*sb;			/* String-buffer */
    const char	*string;		/* String to be copied */
    size_t	len;			/* Length of string.  Excludes 0 */
{
    assert(sb != NULL);

    if (sbensure(sb, len) == NULL)
	return NULL;

    if (string == NULL) {
	sb->buf[0]	= 0;
    } else {
	(void)strncpy(sb->buf, string, len);
	sb->buf[len]	= 0;
    }

    return sb;
}


/*
 * Copy a string of unknown length to a string-buffer.
 */
    Strbuf*
sbcpy(sb, string)
    Strbuf	*sb;			/* String-buffer */
    const char	*string;		/* String to be copied */
{
    return sbncpy(sb, string, strlen(string));
}


/*
 * Append a string of specified length to a string-buffer.
 */
    Strbuf*
sbncat(sb, string, len)
    Strbuf	*sb;			/* String-buffer */
    const char	*string;		/* String to be appended */
    size_t	len;			/* Length of string.  Excludes 0 */
{
    size_t	nchr;
    assert(sb != NULL);

    nchr	= strlen(sb->buf);

    if (sbensure(sb, nchr+len) == NULL)
	return NULL;

    (void)strncpy(sb->buf+nchr, string, len);
    sb->buf[nchr+len]	= 0;

    return sb;
}


/*
 * Append a string of unknown length to a string-buffer.
 */
    Strbuf*
sbcat(sb, string)
    Strbuf	*sb;			/* String-buffer */
    const char	*string;		/* String to be appended */
{
    assert(sb != NULL);

    return sbncat(sb, string, strlen(string));
}


/*
 * Return a pointer to a string-buffer's string.
 */
    char*
sbstr(sb)
    Strbuf	*sb;		/* String-buffer */
{
    assert(sb != NULL);
    assert(sb->buf != NULL);

    return sb->buf;
}


/*
 * Return the length of a string-buffer's string.
 */
    size_t
sblen(sb)
    Strbuf	*sb;		/* String-buffer */
{
    assert(sb != NULL);

    return strlen(sb->buf);
}


/*
 * Return the maximum size.
 */
    size_t
sbmax(sb)
    Strbuf	*sb;
{
    assert(sb != NULL);

    return sb->max;
}


/*
 * Grow a string-buffer.
 */
    Strbuf*
sbgrow(sb)
    Strbuf	*sb;
{
    assert(sb != NULL);

    return sbensure(sb, (size_t)((13*sb->max)/8));
}
