// clang-format off
/*
*   $Id: vstring.c 558 2007-06-15 19:17:02Z elliotth $
*
*   Copyright (c) 1998-2002, Darren Hiebert
*
*   This source code is released for free distribution under the terms of the
*   GNU General Public License.
*
*   This module contains functions supporting resizeable strings.
*/

/*
*   INCLUDE FILES
*/
#include "third_party/ctags/general.h"  /* must always come first */

#include "libc/limits.h"
#include "libc/sysv/consts/_posix.h"
#include "libc/sysv/consts/iov.h"
#include "libc/sysv/consts/limits.h"
#include "libc/sysv/consts/xopen.h"
#include "libc/thread/thread.h"  /* to define INT_MAX */
#include "libc/mem/alg.h"
#include "libc/str/str.h"
#include "libc/str/str.h"

#include "third_party/ctags/debug.h"
#include "third_party/ctags/routines.h"
#include "third_party/ctags/vstring.h"

/*
*   DATA DEFINITIONS
*/
static const size_t vStringInitialSize = 32;

/*
*   FUNCTION DEFINITIONS
*/

static void vStringResize (vString *const string, const size_t newSize)
{
	char *const newBuffer = xRealloc (string->buffer, newSize, char);

	string->size = newSize;
	string->buffer = newBuffer;
}

/*
*   External interface
*/

extern boolean vStringAutoResize (vString *const string)
{
	boolean ok = TRUE;

	if (string->size <= INT_MAX / 2)
	{
		const size_t newSize = string->size * 2;

		vStringResize (string, newSize);
	}
	return ok;
}

extern void vStringClear (vString *const string)
{
	string->length = 0;
	string->buffer [0] = '\0';
	DebugStatement ( memset (string->buffer, 0, string->size); )
}

extern void vStringDelete (vString *const string)
{
	if (string != NULL)
	{
		if (string->buffer != NULL)
			eFree (string->buffer);
		eFree (string);
	}
}

extern vString *vStringNew (void)
{
	vString *const string = xMalloc (1, vString);

	string->length = 0;
	string->size   = vStringInitialSize;
	string->buffer = xMalloc (string->size, char);

	vStringClear (string);

	return string;
}

#ifndef VSTRING_PUTC_MACRO
extern void vStringPut (vString *const string, const int c)
{
	if (string->length + 1 == string->size)  /*  check for buffer overflow */
		vStringAutoResize (string);

	string->buffer [string->length] = c;
	if (c != '\0')
		string->buffer [++string->length] = '\0';
}
#endif

extern void vStringCatS (vString *const string, const char *const s)
{
#if 1
	const size_t len = strlen (s);
	while (string->length + len + 1 >= string->size)/*  check for buffer overflow */
		vStringAutoResize (string);
	strcpy (string->buffer + string->length, s);
	string->length += len;
#else
	const char *p = s;
	do
		vStringPut (string, *p);
	while (*p++ != '\0');
#endif
}

extern vString *vStringNewCopy (const vString *const string)
{
	vString *vs = vStringNew ();
	vStringCatS (vs, string->buffer);
	return vs;
}

extern vString *vStringNewInit (const char *const s)
{
	vString *vs = vStringNew ();
	vStringCatS (vs, s);
	return vs;
}

extern void vStringNCatS (
		vString *const string, const char *const s, const size_t length)
{
	const char *p = s;
	size_t remain = length;

	while (*p != '\0'  &&  remain > 0)
	{
		vStringPut (string, *p);
		--remain;
		++p;
	}
	vStringTerminate (string);
}

/*  Strip trailing newline from string.
 */
extern void vStringStripNewline (vString *const string)
{
	const size_t final = string->length - 1;
	if (string->buffer [final] == '\n')
	{
		string->buffer [final] = '\0';
		string->length--;
	}
}

/*  Strip leading white space from string.
 */
extern void vStringStripLeading (vString *const string)
{
	while (isspace ((int) string->buffer [0]) && string->length > 0)
	{
		size_t i;
		for (i = 1  ;  i < string->length  ;  ++i)
			string->buffer [i - 1] = string->buffer [i];
		--string->length;
		string->buffer [string->length] = '\0';
	}
}

/*  Strip trailing white space from string.
 */
extern void vStringStripTrailing (vString *const string)
{
	while (isspace ((int) string->buffer [string->length - 1]) &&
		   string->length > 0)
	{
		string->length--;
		string->buffer [string->length] = '\0';
	}
}

/*  Chop last character from string.
 */
extern void vStringChop (vString *const string)
{
	if (string->length > 0)
	{
		--string->length;
		string->buffer [string->length] = '\0';
	}
}

extern void vStringCopyS (vString *const string, const char *const s)
{
	vStringClear (string);
	vStringCatS (string, s);
}

extern void vStringNCopyS (
		vString *const string, const char *const s, const size_t length)
{
	vStringClear (string);
	vStringNCatS (string, s, length);
}

extern void vStringCopyToLower (vString *const dest, const vString *const src)
{
	const size_t length = src->length;
	const char *s = src->buffer;
	char *d;
	size_t i;

	if (dest->size < src->size)
		vStringResize (dest, src->size);
	d = dest->buffer;
	for (i = 0  ;  i < length  ;  ++i)
	{
		int c = s [i];

		d [i] = tolower (c);
	}
	d [i] = '\0';
}

extern void vStringSetLength (vString *const string)
{
	string->length = strlen (string->buffer);
}

/* vi:set tabstop=4 shiftwidth=4: */
