// clang-format off
/*
*   $Id: strlist.c 443 2006-05-30 04:37:13Z darren $
*
*   Copyright (c) 1999-2002, Darren Hiebert
*
*   This source code is released for free distribution under the terms of the
*   GNU General Public License.
*
*   This module contains functions managing resizable string lists.
*/

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

#include "libc/mem/alg.h"
#include "libc/str/str.h"
#ifdef HAVE_FNMATCH_H
#include "third_party/musl/fnmatch.h"
#endif

#include "third_party/ctags/debug.h"
#include "third_party/ctags/read.h"
#include "third_party/ctags/routines.h"
#include "third_party/ctags/strlist.h"

/*
*   FUNCTION DEFINITIONS
*/

extern stringList *stringListNew (void)
{
	stringList* const result = xMalloc (1, stringList);
	result->max   = 0;
	result->count = 0;
	result->list  = NULL;
	return result;
}

extern void stringListAdd (stringList *const current, vString *string)
{
	enum { incrementalIncrease = 10 };
	Assert (current != NULL);
	if (current->list == NULL)
	{
		Assert (current->max == 0);
		current->count = 0;
		current->max   = incrementalIncrease;
		current->list  = xMalloc (current->max, vString*);
	}
	else if (current->count == current->max)
	{
		current->max += incrementalIncrease;
		current->list = xRealloc (current->list, current->max, vString*);
	}
	current->list [current->count++] = string;
}

extern void stringListRemoveLast (stringList *const current)
{
	Assert (current != NULL);
	Assert (current->count > 0);
	--current->count;
	current->list [current->count] = NULL;
}

/* Combine list `from' into `current', deleting `from' */
extern void stringListCombine (
		stringList *const current, stringList *const from)
{
	unsigned int i;
	Assert (current != NULL);
	Assert (from != NULL);
	for (i = 0  ;  i < from->count  ;  ++i)
	{
		stringListAdd (current, from->list [i]);
		from->list [i] = NULL;
	}
	stringListDelete (from);
}

extern stringList* stringListNewFromArgv (const char* const* const argv)
{
	stringList* const result = stringListNew ();
	const char *const *p;
	Assert (argv != NULL);
	for (p = argv  ;  *p != NULL  ;  ++p)
		stringListAdd (result, vStringNewInit (*p));
	return result;
}

extern stringList* stringListNewFromFile (const char* const fileName)
{
	stringList* result = NULL;
	FILE* const fp = fopen (fileName, "r");
	if (fp != NULL)
	{
		result = stringListNew ();
		while (! feof (fp))
		{
			vString* const str = vStringNew ();
			readLine (str, fp);
			vStringStripTrailing (str);
			if (vStringLength (str) > 0)
				stringListAdd (result, str);
			else
				vStringDelete (str);
		}
	}
	return result;
}

extern unsigned int stringListCount (const stringList *const current)
{
	Assert (current != NULL);
	return current->count;
}

extern vString* stringListItem (
		const stringList *const current, const unsigned int indx)
{
	Assert (current != NULL);
	return current->list [indx];
}

extern vString* stringListLast (const stringList *const current)
{
	Assert (current != NULL);
	Assert (current->count > 0);
	return current->list [current->count - 1];
}

extern void stringListClear (stringList *const current)
{
	unsigned int i;
	Assert (current != NULL);
	for (i = 0  ;  i < current->count  ;  ++i)
	{
		vStringDelete (current->list [i]);
		current->list [i] = NULL;
	}
	current->count = 0;
}

extern void stringListDelete (stringList *const current)
{
	if (current != NULL)
	{
		if (current->list != NULL)
		{
			stringListClear (current);
			eFree (current->list);
			current->list = NULL;
		}
		current->max   = 0;
		current->count = 0;
		eFree (current);
	}
}

static boolean compareString (
		const char *const string, vString *const itm)
{
	return (boolean) (strcmp (string, vStringValue (itm)) == 0);
}

static boolean compareStringInsensitive (
		const char *const string, vString *const itm)
{
	return (boolean) (strcasecmp (string, vStringValue (itm)) == 0);
}

static int stringListIndex (
		const stringList *const current,
		const char *const string,
		boolean (*test)(const char *s, vString *const vs))
{
	int result = -1;
	unsigned int i;
	Assert (current != NULL);
	Assert (string != NULL);
	Assert (test != NULL);
	for (i = 0  ;  result == -1  &&  i < current->count  ;  ++i)
		if ((*test)(string, current->list [i]))
			result = i;
	return result;
}

extern boolean stringListHas (
		const stringList *const current, const char *const string)
{
	boolean result = FALSE;
	Assert (current != NULL);
	result = stringListIndex (current, string, compareString) != -1;
	return result;
}

extern boolean stringListHasInsensitive (
		const stringList *const current, const char *const string)
{
	boolean result = FALSE;
	Assert (current != NULL);
	Assert (string != NULL);
	result = stringListIndex (current, string, compareStringInsensitive) != -1;
	return result;
}

extern boolean stringListHasTest (
		const stringList *const current, boolean (*test)(const char *s))
{
	boolean result = FALSE;
	unsigned int i;
	Assert (current != NULL);
	for (i = 0  ;  ! result  &&  i < current->count  ;  ++i)
		result = (*test)(vStringValue (current->list [i]));
	return result;
}

extern boolean stringListRemoveExtension (
		stringList* const current, const char* const extension)
{
	boolean result = FALSE;
	int where;
#ifdef CASE_INSENSITIVE_FILENAMES
	where = stringListIndex (current, extension, compareStringInsensitive);
#else
	where = stringListIndex (current, extension, compareString);
#endif
	if (where != -1)
	{
		memmove (current->list + where, current->list + where + 1,
				(current->count - where) * sizeof (*current->list));
		current->list [current->count - 1] = NULL;
		--current->count;
		result = TRUE;
	}
	return result;
}

extern boolean stringListExtensionMatched (
		const stringList* const current, const char* const extension)
{
#ifdef CASE_INSENSITIVE_FILENAMES
	return stringListHasInsensitive (current, extension);
#else
	return stringListHas (current, extension);
#endif
}

static boolean fileNameMatched (
		const vString* const vpattern, const char* const fileName)
{
	const char* const pattern = vStringValue (vpattern);
#if defined (HAVE_FNMATCH)
	return (boolean) (fnmatch (pattern, fileName, 0) == 0);
#elif defined (CASE_INSENSITIVE_FILENAMES)
	return (boolean) (strcasecmp (pattern, fileName) == 0);
#else
	return (boolean) (strcmp (pattern, fileName) == 0);
#endif
}

extern boolean stringListFileMatched (
		const stringList* const current, const char* const fileName)
{
	boolean result = FALSE;
	unsigned int i;
	for (i = 0  ;  ! result  &&  i < stringListCount (current)  ;  ++i)
		result = fileNameMatched (stringListItem (current, i), fileName);
	return result;
}

extern void stringListPrint (const stringList *const current)
{
	unsigned int i;
	Assert (current != NULL);
	for (i = 0  ;  i < current->count  ;  ++i)
		printf ("%s%s", (i > 0) ? ", " : "", vStringValue (current->list [i]));
}

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