/*
* dstring
* Dynamic string buffers, modeled after the Tcl_DString routines.
*/

#include <stdlib.h>  /* malloc() */
#include <string.h>  /* strncpy() */
#include <stdio.h>   /* vsprintf() */
#include <ctype.h>   /* isspace() */
#include <stdarg.h>
#include <assert.h>
#include <time.h>    /* strftime() */
#include "dstring.h"
#define PRINTF_MAX 500

/*
* Add a byte array to the dynamically growing string buffer 'd'.
* If 'string' is zero, just make sure string can hold at least 'length'
* more characters. If 'length' is < 0, compute strlen(string).
*/
char *DStringAppend(DString *d, char *string, int length)
{
  if (length < 0) {
    if (string) length = strlen(string);
    else return d->string;
  }
  if (d->length + length + 1 >= d->spaceAvl) {
    if (!d->spaceAvl) {
      d->spaceAvl = length + 1;
      d->string = malloc(d->spaceAvl);
    }
    else {
      d->spaceAvl *= 2;
      if (d->spaceAvl < d->length + length + 1) {
        d->spaceAvl += length + 1;
      }
      d->string = realloc(d->string, d->spaceAvl);
    }
  }
  if (string) {
    memcpy(&d->string[d->length], string, length);
    d->length += length;
  }
  d->string[d->length] = '\0';
  return d->string;
} /* DStringAppend */


/*
* Append, but quote characters in 'quote' with \.
*/
char *DStringAppendQuoted(DString *d, char *string, char *quote)
{
  char *s;

  for (s = string; *s; s++) {
    if (strchr(quote, *s)) {
      DStringAppend(d, "\\", 1);
    }
    DStringAppend(d, s, 1);
  }
  return d->string;
} /* DStringAppendQuoted */


/*
* Append 'src' to 'dst'.
*/
char *DStringAppendD(DString *dst, DString *src)
{
  if (dst) {
    if (src && src->string) {
      DStringAppend(dst, src->string, src->length);
    }
    return dst->string;
  }
  return NULL;
} /* DStringAppendD */


/*
* DStringPrintf()
* Print formatted string to end of dynamic buffer.
* Limited to 500 characters.
*/
char *DStringPrintf(DString *d, char *format, ...)
{
  int n;
  va_list ap;
  va_start(ap, format);

  DStringAppend(d, 0, PRINTF_MAX);
  n = vsprintf(&d->string[d->length], format, ap);
  assert(n < PRINTF_MAX);
  d->length += n;

  va_end(ap);
  return d->string;
} /* DStringPrintf */


/*
* DStringStrftime()
* Convert time value into dynamic string using the format in 'format'.
*/
char *DStringStrftime(DString *d, char *format, struct tm *tm)
{
  DStringAppend(d, 0, PRINTF_MAX);
  d->length += strftime(&d->string[d->length], PRINTF_MAX, format, tm);
  return d->string;
} /* DStringStrftime */


/*
* DStringCftime()
* Convert time value into dynamic string using the format in 'format'.
* If 't' is zero, use current time. If flag 'localtime' is zero, use
* GMT.
*/
char *DStringCftime(DString *d, char *format, time_t t, int ltime)
{
  struct tm tm;

  if (t == 0) t = time(0);
#ifdef WIN32
  tm = *(ltime ? localtime(&t) : gmtime(&t));
#else
  ltime ? localtime_r(&t, &tm) : gmtime_r(&t, &tm);
#endif
  DStringAppend(d, 0, PRINTF_MAX);
  d->length += strftime(&d->string[d->length], PRINTF_MAX, format, &tm);
  return d->string;
} /* DStringCftime */


/*
* Initialize a new dynamic string object.
*/
void DStringInit(DString *d)
{
  d->length   = 0;
  d->spaceAvl = 0;
  d->string   = 0;
} /* DStringInit */


/*
* Reset and free a dynamic string 'd'.
*/
void DStringFree(DString *d)
{
  if (d->string) {
    free(d->string);
    d->string   = NULL;
  }
  d->length   = 0;
  d->spaceAvl = 0;
} /* DStringFree */


/*
* DStringTrim()
* Remove leading and trailing white space.
*/
char *DStringTrim(DString *d)
{
  char *s = d->string;
  int n = 0;

  if (s) {
    /* trailing white space */
    while (isspace((int)d->string[d->length - 1])) {
      d->length--;
      d->string[d->length] = '\0';
    }

    /* leading white space */
    if (isspace((int)*s)) {
      for (n = 0; s[n] && isspace((int)s[n]); n++) ;
      d->spaceAvl = d->length - n + 1;
      s = malloc(d->spaceAvl);
      strcpy(s, &d->string[n]);
      free(d->string);
      d->string = s;
      d->length -= n;
    }
  }
  return d->string;
} /* DStringTrim */


/*
* DStringTrunc()
* Truncate string, but don't free memory.
*/
void DStringTrunc(DString *d)
{
  d->length = 0;
  if (d->string) d->string[d->length] = '\0';
} /* DStringTrunc */


/*
* Return the value of the dynamic string.
*/
char *DStringValue(DString *d)
{
  return d->string;
} /* DStringValue */


/*
* Return length of string.
*/
int DStringLength(DString *d)
{
  return d->length;
} /* DStringLength */
