/* Copyright (C) 1993,1994 by the author(s).
 
 This software is published in the hope that it will be useful, but
 WITHOUT ANY WARRANTY for any part of this software to work correctly
 or as described in the manuals. See the ShapeTools Public License
 for details.

 Permission is granted to use, copy, modify, or distribute any part of
 this software but only under the conditions described in the ShapeTools 
 Public License. A copy of this license is supposed to have been given
 to you along with ShapeTools in a file named LICENSE. Among other
 things, this copyright notice and the Public License must be
 preserved on all copies.
*/
/*
 * ShapeTools/shape program - strbuf.c
 *
 * by Steve Emerson (steve@unidata.ucar.edu)
 *
 * $Header: strbuf.c[8.0] Thu Jun 10 18:29:55 1993 axel@cs.tu-berlin.de frozen $
 */
#ifndef lint
static char *AtFSid = "$Header: strbuf.c[8.0] Thu Jun 10 18:29:55 1993 axel@cs.tu-berlin.de frozen $";
#endif

#include "shape.h"

typedef struct str_buf {
    char	*buf;	/* Malloc(3)ed string buffer.  NUL terminated. */
    int		len;	/* Current string-length.  Excludes NUL. */
    int		max;	/* Maximum possible string-length.  Excludes NUL. */
} str_buf;


/*
 * Insure that a string-buffer can contain a string of the given size.
 * This is done by increasing the size of a string-buffer
 * via a geometric progression based on the golden ratio (which I happen
 * to like) until it can hold the specified number of characters.
 */

    static str_buf*
sbinsure(sb, len)
    str_buf	*sb;		/* String-buffer */
    int		len;		/* Number of characters to insure */
{
    if (sb != NULL) {
	if (len > sb->max) {
	    if (sb->buf == NULL) {
		sb->buf	= malloc((unsigned)(len+1));
		if(sb->buf == NIL)
		  errexit(10,"malloc");
	    } else {
		int	n	= sb->max + 1;

		while (n < len+1)
		    n	*= 1.618034;		/* Golden ratio (approx.) */

		sb->buf	= realloc(sb->buf, (unsigned)n);
		if(sb->buf == NIL)
		  errexit(10,"realloc");
		len	= n - 1;
	    }
	    if (sb->buf == NULL) {
		sb	= NULL;
	    } else {
		sb->max	= len;
	    }
	}
    }

    return sb;
}


/*
 * Clear a string-buffer.
 */

    static str_buf*
sbclear(sb)
    str_buf	*sb;		/* String-buffer */
{
    if (sb != NULL) {
	sb->buf	= NULL;
	sb->len	= 0;
	sb->max	= 0;
    }

    return sb;
}


/*
 * Copy a string to a string-buffer.  The string buffer will expand as
 * necessary to accomodate the input string.  On failure, it will return
 * NULL.
 */

    str_buf*
sbcpy(sb, string, len)
    str_buf	*sb;		/* String-buffer */
    char	*string;	/* String to be copied */
    int		len;		/* Length of string.  Excludes NUL */
{
    if (sb != NULL) {
	if (sbinsure(sb, len) == NULL)
	    return NULL;
	if (string == NULL) {
	    sb->buf[0]	= 0;
	    sb->len	= 0;
	} else {
	    strncpy(sb->buf, string, len);
	    sb->len	= len;
	}
    }

    return sb;
}


/*
 * Initialize a string-buffer.
 */

    str_buf*
sbinit(sb, string, len)
    str_buf	*sb;		/* String-buffer */
    char	*string;	/* Initial string */
    int		len;		/* String length */
{
    return sbcpy(sbclear(sb), string, len);
}


/*
 * Instantiate a string-buffer.  Returns NULL on failure.  The specified,
 * initial, maximum string length doesn't count the terminating NUL.
 */

    str_buf*
sbnew(max)
    int		max;		/* Initial maximum string length */
{
    str_buf	*sb;
    if((sb = (str_buf*)malloc((unsigned)sizeof(str_buf))) == (str_buf *)NIL)
      errexit(10,"malloc");
    return sbinsure(sbclear(sb), max);
}


/*
 * Free a string-buffer.
 */

    void
sbfree(sb)
    str_buf	*sb;		/* String-buffer */
{
    if (sb != NULL) {
	if (sb->buf != NULL)
	    free(sb->buf);
	free((char*)sb);
    }
}


/*
 * Append a string to a string-buffer.  The string-buffer will expand as
 * necessary to accomodate the catenated string.  The function returns NULL
 * on failure.
 */

    str_buf*
sbcat(sb, string, len)
    str_buf	*sb;		/* String-buffer */
    char	*string;	/* String to be appended */
    int		len;		/* Length of string.  Excludes NUL */
{
    if (sb != NULL) {
	if (sbinsure(sb, sb->len+len) == NULL)
	    return NULL;
	strncpy(sb->buf+sb->len, string, len);
	sb->len	+= len;
    }

    return sb;
}


/*
 * Return a pointer to a string-buffer's string.  The string is guaranteed
 * to be NUL terminated.
 */

    char*
sbstr(sb)
    str_buf	*sb;		/* String-buffer */
{
    if (sb == NULL)
	return NULL;

    if (sb->buf != NULL)
	sb->buf[sb->len]	= 0;

    return sb->buf;
}


/*
 * Return the length of a string-buffer's string.  Excludes the terminating
 * NUL.
 */

    int
sblen(sb)
    str_buf	*sb;		/* String-buffer */
{
    return sb == NULL ? 0 : sb->buf == NULL ? 0 : sb->len;
}
