/*
 *	fhist - file history and comparison tools
 *	Copyright (C) 1998, 2001, 2002 Peter Miller;
 *	All rights reserved.
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 *
 * MANIFEST: functions to manipulate lists of strings
 *
 * This file contains routines for mainpulating words and word lists.
 * Much of the functionality of cook uses these routines.
 */

#include <ac/ctype.h>
#include <ac/stddef.h>
#include <ac/string.h>
#include <ac/stdlib.h>
#include <ac/time.h>

#include <mem.h>
#include <str.h>
#include <str_list.h>


/*
 * NAME
 *	string_list_append - append to a word list
 *
 * SYNOPSIS
 *	void string_list_append(string_list_ty *wlp, string_ty *wp);
 *
 * DESCRIPTION
 *	Wl_append is used to append to a word list.
 *
 * CAVEAT
 *	The word being appended IS copied.
 */

void
string_list_append(string_list_ty *wlp, string_ty *w)
{
    /* assert(wlp); */
    /* assert(w); */
    if (wlp->nstrings >= wlp->nstrings_max)
    {
	size_t		nbytes;

	wlp->nstrings_max = wlp->nstrings_max * 2 + 16;
	nbytes = wlp->nstrings_max * sizeof(string_ty *);
	wlp->string = mem_change_size(wlp->string, nbytes);
    }
    wlp->string[wlp->nstrings++] = str_copy(w);
}


/*
 * NAME
 *	string_list_append_list
 *
 * SYNOPSIS
 *	void string_list_append_list(string_list_ty *to, string_list_ty *from);
 *
 * DESCRIPTION
 *	The string_list_append_list function is used to append one
 *	string list (from) onto the end of another (to).
 */

void
string_list_append_list(string_list_ty *to, const string_list_ty *from)
{
    size_t          j;

    for (j = 0; j < from->nstrings; ++j)
       	string_list_append(to, from->string[j]);
}


/*
 * NAME
 *	string_list_prepend
 *
 * SYNOPSIS
 *	void string_list_prepend(string_list_ty *, string_ty *);
 *
 * DESCRIPTION
 *	The string_list_prepend function is used to insert a string at
 *	the beginning of a string list.
 */

void
string_list_prepend(string_list_ty *wlp, string_ty *w)
{
    ptrdiff_t       j;

    /* assert(wlp); */
    /* assert(w); */
    if (wlp->nstrings >= wlp->nstrings_max)
    {
	size_t		nbytes;

	wlp->nstrings_max = wlp->nstrings_max * 2 + 16;
	nbytes = wlp->nstrings_max * sizeof(string_ty *);
	wlp->string = mem_change_size(wlp->string, nbytes);
    }
    wlp->nstrings++;
    for (j = wlp->nstrings - 1; j > 0; --j)
	wlp->string[j] = wlp->string[j - 1];
    wlp->string[0] = str_copy(w);
}


/*
 * NAME
 *	string_list_destructor - free a word list
 *
 * SYNOPSIS
 *	void string_list_destructor(string_list_ty *wlp);
 *
 * DESCRIPTION
 *	Wl_free is used to free the contents of a word list
 *	when it is finished with.
 *
 * CAVEAT
 *	It is assumed that the contents of the word list were all
 *	created using strdup() or similar, and grown using string_list_append().
 */

void
string_list_destructor(string_list_ty *wlp)
{
    size_t          j;

    for (j = 0; j < wlp->nstrings; j++)
	str_free(wlp->string[j]);
    if (wlp->string)
	mem_free(wlp->string);
    wlp->nstrings = 0;
    wlp->nstrings_max = 0;
    wlp->string = 0;
}


/*
 * NAME
 *	string_list_member - word list membership
 *
 * SYNOPSIS
 *	int string_list_member(string_list_ty *wlp, string_ty *wp);
 *
 * DESCRIPTION
 *	Wl_member is used to determine if the given word is
 *	contained in the given word list.
 *
 * RETURNS
 *	A zero if the word is not in the list,
 *	and a non-zero if it is.
 */

int
string_list_member(const string_list_ty *wlp, string_ty *w)
{
    size_t          j;

    for (j = 0; j < wlp->nstrings; j++)
	if (str_equal(wlp->string[j], w))
    	    return 1;
    return 0;
}


/*
 * NAME
 *	string_list_intersect - word list intersection test
 *
 * SYNOPSIS
 *	int string_list_intersect(string_list_ty *wlp, string_list_ty *wp);
 *
 * DESCRIPTION
 *	Wl_intersect is used to determine if the given word is
 *	contained in the given word list.
 *
 * RETURNS
 *	A zero if the word is not in the list,
 *	and a non-zero if it is.
 */

int
string_list_intersect(const string_list_ty *wl1, const string_list_ty *wl2)
{
    size_t          j;

    if (wl1->nstrings > wl2->nstrings)
    {
	for (j = 0; j < wl2->nstrings; j++)
    	    if (string_list_member(wl1, wl2->string[j]))
       		return 1;
    }
    else
    {
	for (j = 0; j < wl1->nstrings; j++)
    	    if (string_list_member(wl2, wl1->string[j]))
       		return 1;
    }
    return 0;
}


/*
 * NAME
 *	string_list_copy_constructor - copy a word list
 *
 * SYNOPSIS
 *	void string_list_copy_constructor(string_list_ty *to, string_list_ty *from);
 *
 * DESCRIPTION
 *	Wl_copy is used to copy word lists.
 *
 * RETURNS
 *	A copy of the 'to' word list is placed in 'from'.
 *
 * CAVEAT
 *	It is the responsibility of the caller to ensure that the
 *	new word list is freed when finished with, by a call to string_list_destructor().
 */

void
string_list_copy_constructor(string_list_ty *to, const string_list_ty *from)
{
    size_t          j;

    string_list_constructor(to);
    for (j = 0; j < from->nstrings; j++)
       	string_list_append(to, str_copy(from->string[j]));
}


/*
 * NAME
 *	wl2str - form a string from a word list
 *
 * SYNOPSIS
 *	string_ty *wl2str(string_list_ty *wlp, int start, int stop, char *sep);
 *
 * DESCRIPTION
 *	Wl2str is used to form a string from a word list.
 *
 * RETURNS
 *	A pointer to the newly formed string in dynamic memory.
 *
 * CAVEAT
 *	It is the responsibility of the caller to ensure that the
 *	new string is freed when finished with, by a call to str_free().
 */

string_ty *
wl2str(const string_list_ty *wl, int start, int stop, char *sep)
{
    int             j;
    static char     *tmp;
    static size_t   tmplen;
    size_t          length;
    size_t          seplen;
    char            *pos;
    string_ty       *s;

    if (!sep)
	sep = " ";
    seplen = strlen(sep);
    length = 0;
    for (j = start; j <= stop && j < wl->nstrings; j++)
    {
	s = wl->string[j];
	if (s->str_length)
	{
	    if (length)
	       	length += seplen;
	    length += s->str_length;
	}
    }

    if (tmplen < length)
    {
	tmplen = length;
	tmp = mem_change_size(tmp, tmplen);
    }

    pos = tmp;
    for (j = start; j <= stop && j < wl->nstrings; j++)
    {
	s = wl->string[j];
	if (s->str_length)
	{
    	    if (pos != tmp)
    	    {
		memcpy(pos, sep, seplen);
       		pos += seplen;
    	    }
    	    memcpy(pos, s->str_text, s->str_length);
    	    pos += s->str_length;
	}
    }

    s = str_n_from_c(tmp, length);
    return s;
}


/*
 * NAME
 *	str2wl - string to word list
 *
 * SYNOPSIS
 *	void str2wl(string_list_ty *wlp, string_ty *s, char *sep, int ewhite);
 *
 * DESCRIPTION
 *	Str2wl is used to form a word list from a string.
 *	wlp	- where to put the word list
 *	s	- string to break
 *	sep	- separators, default to " " if 0 given
 *	ewhite	- supress extra white space around separators
 *
 * RETURNS
 *	The string is broken on spaces into words,
 *	using strndup() and string_list_append().
 *
 * CAVEAT
 *	Quoting is not understood.
 */

void
str2wl(string_list_ty *slp, string_ty *s, char *sep, int ewhite)
{
    char            *cp;
    int             more;

    if (!sep)
    {
	sep = " \t\n\f\r";
	ewhite = 1;
    }
    string_list_constructor(slp);
    cp = s->str_text;
    more = 0;
    while (*cp || more)
    {
	string_ty	*w;
	char		*cp1;
	char		*cp2;

	if (ewhite)
	    while (isspace((unsigned char)*cp))
	       	cp++;
	if (!*cp && !more)
	    break;
	more = 0;
	cp1 = cp;
	while (*cp && !strchr(sep, *cp))
	    cp++;
	if (*cp)
	{
	    cp2 = cp + 1;
	    more = 1;
	}
	else
	    cp2 = cp;
	if (ewhite)
	    while (cp > cp1 && isspace((unsigned char)cp[-1]))
	       	cp--;
	w = str_n_from_c(cp1, cp - cp1);
	string_list_append(slp, w);
	str_free(w);
	cp = cp2;
    }
}


/*
 * NAME
 *	wl_insert - a insert a word into a list
 *
 * SYNOPSIS
 *	void wl_insert(string_list_ty *wlp, string_ty *wp);
 *
 * DESCRIPTION
 *	Wl_insert is similar to string_list_append, however it does not
 *	append the word unless it is not already in the list.
 *
 * CAVEAT
 *	If the word is inserted it is copied.
 */

void
string_list_append_unique(string_list_ty *wlp, string_ty *wp)
{
    size_t          j;

    for (j = 0; j < wlp->nstrings; j++)
       	if (str_equal(wlp->string[j], wp))
	    return;
    string_list_append(wlp, wp);
}


/*
 * NAME
 *	string_list_append_list_unique
 *
 * SYNOPSIS
 *	void string_list_append_list_unique(string_list_ty *to, string_list_ty *from);
 *
 * DESCRIPTION
 *	The string_list_append_list_unique function is used to append
 *	the contents of one string list (from) to the end of another
 *	tring list (to).  Entries which duplicate items already present
 *	will be ignored.
 */

void
string_list_append_list_unique(string_list_ty *to, const string_list_ty *from)
{
    size_t          j;

    for (j = 0; j < from->nstrings; ++j)
       	string_list_append_unique(to, from->string[j]);
}


/*
 * NAME
 *	string_list_remove - remove list member
 *
 * SYNOPSIS
 *	void string_list_remove(string_list_ty *wlp, string_ty *wp);
 *
 * DESCRIPTION
 *	The string_list_remove function is used to delete a member of a word list.
 *
 * RETURNS
 *	void
 */

void
string_list_remove(string_list_ty *wlp, string_ty *wp)
{
    size_t          j;
    size_t          k;

    for (j = 0; j < wlp->nstrings; ++j)
    {
	if (str_equal(wlp->string[j], wp))
	{
	    wlp->nstrings--;
	    for (k = j; k < wlp->nstrings; ++k)
		wlp->string[k] = wlp->string[k + 1];
	    str_free(wp);
	    break;
	}
    }
}


/*
 * NAME
 *	string_list_remove_list - remove list members
 *
 * SYNOPSIS
 *	void string_list_remove_list(string_list_ty *wlp, string_list_ty *wp);
 *
 * DESCRIPTION
 *	The string_list_remove_list function is used to delete all the
 *	members of a word list from another word list.
 *
 * RETURNS
 *	void
 */

void
string_list_remove_list(string_list_ty *wlp, const string_list_ty *nuke)
{
    size_t          j;

    for (j = 0; j < nuke->nstrings; ++j)
       	string_list_remove(wlp, nuke->string[j]);
}


/*
 * NAME
 *	string_list_constructor
 *
 * SYNOPSIS
 *	void string_list_constructor(string_list_ty *);
 *
 * DESCRIPTION
 *	The string_list_constructor function is used to prepare a string
 *	list for use.  It will be empty.
 *
 * CAVEAT
 *	This must be called on the string list before any other action
 *	is taken.  Use string_list_destructor when you are done.
 */

void
string_list_constructor(string_list_ty *wlp)
{
    wlp->nstrings = 0;
    wlp->nstrings_max = 0;
    wlp->string = 0;
}


/*
 * NAME
 *	string_list_new
 *
 * DESCRIPTION
 *	string_list_ty *string_list_new(void);
 *
 * DESCRIPTION
 *	The string_list_new function is used to allocate a new  string
 *	list in dynamic memory.  It will be empty.
 *
 * RETURNS
 *	string_list_ty *
 *
 * CAVEAT
 *	Use string_list_delete when you are done.
 */

string_list_ty *
string_list_new(void)
{
    string_list_ty  *slp;

    slp = mem_alloc(sizeof(string_list_ty));
    string_list_constructor(slp);
    return slp;
}


/*
 * NAME
 *	string_list_new_copy
 *
 * SYNOPSIS
 *	string_list_ty *string_list_new_copy(string_list_ty *);
 *
 * DESCRIPTION
 *	The string_list_new_copy function is used to allocate a new copy
 *	of a string list in dynamic memory.
 *
 * RETURNS
 *	string_list_ty *
 *
 * CAVEAT
 *	Use string_list_delete when you are done.
 */

string_list_ty *
string_list_new_copy(const string_list_ty *from)
{
    string_list_ty  *slp;

    slp = mem_alloc(sizeof(string_list_ty));
    string_list_copy_constructor(slp, from);
    return slp;
}


/*
 * NAME
 *	string_list_delete
 *
 * SYNOPSIS
 *	void string_list_delete(string_list_ty *);
 *
 * DESCRIPTION
 *	The string_list_delete function is used to release the resources
 *	held by a string list in dynamic memory.
 */

void
string_list_delete(string_list_ty *slp)
{
    string_list_destructor(slp);
    mem_free(slp);
}


/*
 * NAME
 *	string_list_bool
 *
 * SYNOPSIS
 *	int string_list_bool(string_list_ty *);
 *
 * DESCRIPTION
 *	The string_list_bool function is used to perform a boolean
 *	evaluation on a list of strings.  If any return str_bool of
 *	true, the list is true.
 */

int
string_list_bool(const string_list_ty *slp)
{
    size_t          j;

    for (j = 0; j < slp->nstrings; ++j)
	if (str_bool(slp->string[j]))
    	    return 1;
    return 0;
}
