/**
 * \file rpmio/rpmstring.c
 */

#include "system.h"

#include <stdarg.h>

#include <rpm/rpmstring.h>
#include "debug.h"

#define BUF_CHUNK 1024

struct StringBufRec {
    char *buf;
    char *tail;     /* Points to first "free" char */
    int allocated;
    int free;
};

char * stripTrailingChar(char * s, char c)
{
    char * t;
    for (t = s + strlen(s) - 1; *t == c && t >= s; t--)
	*t = '\0';
    return s;
}

StringBuf newStringBuf(void)
{
    StringBuf sb = xmalloc(sizeof(*sb));

    sb->free = sb->allocated = BUF_CHUNK;
    sb->buf = xcalloc(sb->allocated, sizeof(*sb->buf));
    sb->buf[0] = '\0';
    sb->tail = sb->buf;
    
    return sb;
}

StringBuf freeStringBuf(StringBuf sb)
{
    if (sb) {
	sb->buf = _free(sb->buf);
	sb = _free(sb);
    }
    return sb;
}

void truncStringBuf(StringBuf sb)
{
    sb->buf[0] = '\0';
    sb->tail = sb->buf;
    sb->free = sb->allocated;
}

void stripTrailingBlanksStringBuf(StringBuf sb)
{
    while (sb->free != sb->allocated) {
	if (! risspace(*(sb->tail - 1)))
	    break;
	sb->free++;
	sb->tail--;
    }
    sb->tail[0] = '\0';
}

char * getStringBuf(StringBuf sb)
{
    return sb->buf;
}

void appendStringBufAux(StringBuf sb, const char *s, int nl)
{
    int l;

    l = strlen(s);
    /* If free == l there is no room for NULL terminator! */
    while ((l + nl + 1) > sb->free) {
        sb->allocated += BUF_CHUNK;
	sb->free += BUF_CHUNK;
        sb->buf = xrealloc(sb->buf, sb->allocated);
	sb->tail = sb->buf + (sb->allocated - sb->free);
    }
    
    /* FIX: shrug */
    strcpy(sb->tail, s);
    sb->tail += l;
    sb->free -= l;
    if (nl) {
        sb->tail[0] = '\n';
        sb->tail[1] = '\0';
	sb->tail++;
	sb->free--;
    }
}

int rstrcasecmp(const char * s1, const char * s2)
{
  const char * p1 = s1;
  const char * p2 = s2;
  char c1, c2;

  if (p1 == p2)
    return 0;

  do
    {
      c1 = rtolower (*p1++);
      c2 = rtolower (*p2++);
      if (c1 == '\0')
        break;
    }
  while (c1 == c2);

  return (int)(c1 - c2);
}

int rstrncasecmp(const char *s1, const char *s2, size_t n)
{
  const char * p1 = s1;
  const char * p2 = s2;
  char c1, c2;

  if (p1 == p2 || n == 0)
    return 0;

  do
    {
      c1 = rtolower (*p1++);
      c2 = rtolower (*p2++);
      if (c1 == '\0' || c1 != c2)
	break;
    } while (--n > 0);

  return (int)(c1 - c2);
}

/* 
 * Simple and stupid asprintf() clone.
 * FIXME: write to work with non-C99 vsnprintf or check for one in configure.
 */
int rasprintf(char **strp, const char *fmt, ...)
{
    int n;
    va_list ap;
    char * p = NULL;
  
    if (strp == NULL) 
	return -1;

    va_start(ap, fmt);
    n = vsnprintf(NULL, 0, fmt, ap);
    va_end(ap);

    if (n >= -1) {
	size_t nb = n + 1;
	p = xmalloc(nb);
    	va_start(ap, fmt);
        n = vsnprintf(p, nb, fmt, ap);
    	va_end(ap);
    } 
    *strp = p;
    return n;
}

/*
 * Concatenate two strings with dynamically (re)allocated
 * memory what prevents static buffer overflows by design.
 * *dest is reallocated to the size of strings to concatenate.
 *
 * Note:
 * 1) char *buf = rstrcat(NULL,"string"); is the same like rstrcat(&buf,"string");
 * 2) rstrcat(&buf,NULL) returns buf
 * 3) rstrcat(NULL,NULL) returns NULL
 * 4) *dest and src can overlap
 */
char *rstrcat(char **dest, const char *src)
{
    if ( src == NULL ) {
	return dest != NULL ? *dest : NULL;
    }

    if ( dest == NULL ) {
	return xstrdup(src);
    }

    {
	size_t dest_size = *dest != NULL ? strlen(*dest) : 0;
	size_t src_size = strlen(src);

	*dest = xrealloc(*dest, dest_size+src_size+1);		/* include '\0' */
	memmove(&(*dest)[dest_size], src, src_size+1);
    }

    return *dest;
}

/*
 * Concatenate strings with dynamically (re)allocated
 * memory what prevents static buffer overflows by design.
 * *dest is reallocated to the size of strings to concatenate.
 * List of strings has to be NULL terminated.
 *
 * Note:
 * 1) char *buf = rstrscat(NULL,"string",NULL); is the same like rstrscat(&buf,"string",NULL);
 * 2) rstrscat(&buf,NULL) returns buf
 * 3) rstrscat(NULL,NULL) returns NULL
 * 4) *dest and argument strings can overlap
 */
char *rstrscat(char **dest, const char *arg, ...)
{
    va_list ap;
    size_t arg_size, dst_size;
    const char *s;
    char *dst, *p;

    dst = dest ? *dest : NULL;

    if ( arg == NULL ) {
        return dst;
    }

    va_start(ap, arg);
    for (arg_size=0, s=arg; s; s = va_arg(ap, const char *))
        arg_size += strlen(s);
    va_end(ap);

    dst_size = dst ? strlen(dst) : 0;
    dst = realloc(dst, dst_size+arg_size+1);    /* include '\0' */
    p = &dst[dst_size];

    va_start(ap, arg);
    for (s = arg; s; s = va_arg(ap, const char *)) {
        size_t size = strlen(s);
        memmove(p, s, size);
        p += size;
    }
    *p = '\0';

    if ( dest ) {
        *dest = dst;
    }

    return dst;
}

/*
 * Adapted from OpenBSD, strlcpy() originally developed by
 * Todd C. Miller <Todd.Miller@courtesan.com>
 */
size_t rstrlcpy(char *dest, const char *src, size_t n)
{
    char *d = dest;
    const char *s = src;
    size_t len = n;

    /* Copy as many bytes as will fit */
    if (len != 0) {
	while (--len != 0) {
	    if ((*d++ = *s++) == '\0')
		break;
	}
    }

    /* Not enough room in dst, add NUL and traverse rest of src */
    if (len == 0) {
	if (n != 0)
	    *d = '\0'; /* NUL-terminate dst */
	while (*s++)
	    ;
    }

    return s - src - 1; /* count does not include NUL */
}
