/*
 *      dnsutl - utilities to make DNS easier to configure
 *      Copyright (C) 1990-1994, 1996, 1999, 2006, 2007 Peter Miller
 *
 *      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 3 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, see
 *      <http://www.gnu.org/licenses/>.
 *
 * 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/stdlib.h>
#include <ac/string.h>
#include <ac/time.h>

#include <error.h>
#include <mem.h>
#include <strlist.h>


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

void
strlist_append(strlist_ty *wlp, string_ty *w)
{
        size_t          nbytes;

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


void
strlist_prepend(strlist_ty *wlp, string_ty *w)
{
        long            j;
        size_t          nbytes;

        assert(wlp);
        assert(w);
        if (wlp->nstrings >= wlp->nstrings_max)
        {
                wlp->nstrings_max = wlp->nstrings_max * 2 + 4;
                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
 *      strlist_free - free a word list
 *
 * SYNOPSIS
 *      void strlist_free(strlist_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 strlist_append().
 */

void
strlist_free(strlist_ty *wlp)
{
        size_t          j;

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


/*
 * NAME
 *      strlist_member - word list membership
 *
 * SYNOPSIS
 *      int strlist_member(strlist_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
strlist_member(strlist_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
 *      strlist_copy - copy a word list
 *
 * SYNOPSIS
 *      void strlist_copy(strlist_ty *to, strlist_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 strlist_free().
 */

void
strlist_copy(strlist_ty *to, strlist_ty *from)
{
        size_t          j;

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


/*
 * NAME
 *      wl2str - form a string from a word list
 *
 * SYNOPSIS
 *      string_ty *wl2str(strlist_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 free().
 */

string_ty *
wl2str(strlist_ty *wl, size_t start, size_t stop, const char *sep)
{
        size_t          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(strlist_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 strlist_append().
 *
 * CAVEAT
 *      Quoting is not understood.
 */

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

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

                if (ewhite)
                        while (isspace(*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(cp[-1]))
                                cp--;
                w = str_n_from_c(cp1, cp - cp1);
                strlist_append(slp, w);
                str_free(w);
                cp = cp2;
        }
}


/*
 * NAME
 *      strlist_insert - a insert a word into a list
 *
 * SYNOPSIS
 *      void strlist_insert(strlist_ty *wlp, string_ty *wp);
 *
 * DESCRIPTION
 *      Wl_insert is similar to strlist_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
strlist_append_unique(strlist_ty *wlp, string_ty *wp)
{
        size_t          j;

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


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

void
strlist_delete(strlist_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;
                }
        }
}


void
strlist_zero(strlist_ty *wlp)
{
        wlp->nstrings = 0;
        wlp->nstrings_max = 0;
        wlp->string = 0;
}


static int
cmp(const void *va, const void *vb)
{
        string_ty       *a;
        string_ty       *b;

        a = *(string_ty **)va;
        b = *(string_ty **)vb;
        if (str_equal(a, b))
                return 0;
        return strcmp(a->str_text, b->str_text);
}


void
strlist_sort(strlist_ty *slp)
{
        qsort(slp->string, slp->nstrings, sizeof(slp->string[0]), cmp);
}


syntax highlighted by Code2HTML, v. 0.9.1