/****************************************************************************
 *
 * Dynamic strings
 ****************************************************************************/

/*
 * tcl.h --
 *
 *      This header file describes the externally-visible facilities
 *      of the Tcl interpreter.
 *
 * Copyright (c) 1987-1994 The Regents of the University of California.
 * Copyright (c) 1994-1996 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * SCCS: @(#) tcl.h 1.283 96/10/02 17:17:39
 */


#include "dstring.h"
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "str_strsafe.h"

/*
 *----------------------------------------------------------------------
 *
 * DStringInit --
 *
 *      Initializes a dynamic string, discarding any previous contents
 *      of the string (DStringFree should have been called already
 *      if the dynamic string was previously in use).
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      The dynamic string is initialized to be empty.
 *
 *----------------------------------------------------------------------
 */

void
DStringInit(DString* pDs)
{
    pDs->pString = pDs->staticSpace;
    pDs->length = 0;
    pDs->spaceAvl = kDstringStaticSize;
    pDs->staticSpace[0] = 0;
}

/*
 *----------------------------------------------------------------------
 *
 * DStringAppend --
 *
 *      Append more characters to the current value of a dynamic string.
 *
 * Results:
 *      The return value is a pointer to the dynamic string's new value.
 *
 * Side effects:
 *      Length bytes from string (or all of string if length is less
 *      than zero) are added to the current value of the string.  Memory
 *      gets reallocated if needed to accomodate the string's new size.
 *
 *----------------------------------------------------------------------
 */

char *
DStringAppend(DString *pDs,
                 const char* string,
                 int         length)
{
    int   newSize;
    char* newString;
    char* dst;
    const char* end;

    if (length < 0) {
        length = strlen(string);
    }
    newSize = length + pDs->length;

    /*
     * Allocate a larger buffer for the string if the current one isn't
     * large enough.  Allocate extra space in the new buffer so that there
     * will be room to grow before we have to allocate again.
     */

    if (newSize >= pDs->spaceAvl) {
        pDs->spaceAvl = newSize*2;
        newString = (char *) malloc((unsigned) pDs->spaceAvl);
        memcpy((void *)newString, (void *) pDs->pString,
                (size_t) pDs->length);
        if (pDs->pString != pDs->staticSpace) {
            free(pDs->pString);
        }
        pDs->pString = newString;
    }

    /*
     * Copy the new string into the buffer at the end of the old
     * one.
     */

    for (dst = pDs->pString + pDs->length, end = string+length;
            string < end; string++, dst++) {
        *dst = *string;
    }
    *dst = 0;
    pDs->length += length;
    return pDs->pString;
}

/*
 *----------------------------------------------------------------------
 *
 * DStringSetLength --
 *
 *      Change the length of a dynamic string.  This can cause the
 *      string to either grow or shrink, depending on the value of
 *      length.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      The length of pDsis changed to length and a null byte is
 *      stored at that position in the string.  If length is larger
 *      than the space allocated for pDs, then a panic occurs.
 *
 *----------------------------------------------------------------------
 */

void
DStringSetLength(DString* pDs,
                    int         length)
{
    if (length < 0) {
        length = 0;
    }
    if (length >= pDs->spaceAvl) {
        char *newString;

        pDs->spaceAvl = length+1;
        newString = (char *) malloc((unsigned) pDs->spaceAvl);

        /*
         * SPECIAL NOTE: must use memcpy, not strcpy, to copy the string
         * to a larger buffer, since there may be embedded NULLs in the
         * string in some cases.
         */

        memcpy((void *) newString, (void*) pDs->pString,
                (size_t) pDs->length);
        if (pDs->pString != pDs->staticSpace) {
            free(pDs->pString);
        }
        pDs->pString = newString;
    }
    pDs->length = length;
    pDs->pString[length] = 0;
}

/*
 *----------------------------------------------------------------------
 *
 * DStringFree --
 *
 *      Frees up any memory allocated for the dynamic string and
 *      reinitializes the string to an empty state.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      The previous contents of the dynamic string are lost, and
 *      the new value is an empty string.
 *
 *----------------------------------------------------------------------
 */

void
DStringFree(DString* pDs)
{
    if (pDs->pString != pDs->staticSpace) {
        free(pDs->pString);
    }
    pDs->pString = pDs->staticSpace;
    pDs->length = 0;
    pDs->spaceAvl = kDstringStaticSize;
    pDs->staticSpace[0] = 0;
}

/*
 * DStringSprintf --
 *
 * Append a formatted string to a dstring
 */

void
DStringSprintf(DString* pDs,
                   const char* pFormat,
                   ...)
{
#ifdef _WIN32
    va_list     args;
    char        message[256];
    char  *     buf;
    size_t      bufCchSize;

    buf = &(message[0]);
    bufCchSize = sizeof(message);

    va_start(args, pFormat);
# ifndef DISABLE_STRSAFE
    for (;;)
    {
        /* TODO: this only works on windows with recent C library */
        HRESULT hr = StringCchVPrintfA(buf, bufCchSize, pFormat, args);
        if (S_OK == hr)
            break;
        if (STRSAFE_E_INSUFFICIENT_BUFFER != hr)
        {
            /* any error other than buffer not big enough:
               a) should not happen
               b) means we give up */
            goto Error;
        }
        /* we have to make the buffer bigger. The algorithm used to calculate
           the new size is arbitrary (aka. educated guess) */
        if (buf != &(message[0]))
            free(buf);
        if (bufCchSize < 4*1024)
            bufCchSize += bufCchSize;
        else
            bufCchSize += 1024;
        buf = (char *)malloc(bufCchSize*sizeof(char));
        if (NULL == buf)
            goto Error;
    }
# else /* strsafe is disabled */
    {
        int len = vsnprintf(buf, bufCchSize, pFormat, args);
        
        if(len >= bufCchSize)
        {
            bufCchSize = len + 1;
            buf = (char *)malloc(bufCchSize*sizeof(char));
            if (NULL == buf)
                goto Error;
            
            len = vsnprintf(buf, bufCchSize, pFormat, args);
        }
        
        if(len < 0)
            goto Error;
    }
# endif
    va_end(args);

    DStringAppend(pDs, buf, -1);
Error:
    if (buf != &(message[0]))
        free((void*)buf);
    return;
#else
  va_list args;
  char*   pBuffer;
  int     len;

  va_start(args, pFormat);
  len = vasprintf(&pBuffer, pFormat, args);
  DStringAppend(pDs, pBuffer, len);
  free(pBuffer);
  va_end(args);
#endif
}

/****************************************************************************
 * DStringAppendLowerCase --
 *
 * Append text to a dstring lowercased
 */

char*
DStringAppendLowerCase(DString*   pDs,
                         const char*    string,
                         int            length)
{
    int   newSize;
    char* newString;
    char* dst;
    const char* end;

    if (length < 0) {
        length = strlen(string);
    }
    newSize = length + pDs->length;

    /*
     * Allocate a larger buffer for the string if the current one isn't
     * large enough.  Allocate extra space in the new buffer so that there
     * will be room to grow before we have to allocate again.
     */

    if (newSize >= pDs->spaceAvl) {
        pDs->spaceAvl = newSize*2;
        newString = (char *) malloc((unsigned) pDs->spaceAvl);
        memcpy((void *)newString, (void *) pDs->pString,
                (size_t) pDs->length);
        if (pDs->pString != pDs->staticSpace) {
            free(pDs->pString);
        }
        pDs->pString = newString;
    }

    /*
     * Copy the new string into the buffer at the end of the old
     * one.
     */

    for (dst = pDs->pString + pDs->length, end = string+length;
            string < end; string++, dst++) {
        *dst = tolower(*string);
    }
    *dst = 0;
    pDs->length += length;
    return pDs->pString;
}
