/* ---------------------------------------------------------------------
   (c) ED 2001-2008
   Project      : CLIB
   Function     : string tokenizer
   Module       : TOK
   File         : TOK.C
   Created      : 11-03-2001
   Modified     : 29-05-2008
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   Log

   1.5 - 29-05-2008 *TOK* becomes *tok*. Ascending compatibility is preserved
   1.4 - 01-02-2005 String handling debugging.
   1.3 - 27-01-2005 String handling added.
   .                Refactoring.
   .                - TOK_init() becomes TOK_create()
   .                - TOK_end() becomes TOK_delete()
   1.2 - 01-06-2004 .errno becomes .err
   1.1 - 25-12-2002 sTOK_INFO and TOK_get() added
   1.0 - 11-03-2001 Working version
   0.0 - 11-03-2001 Created

   --------------------------------------------------------------------- */
#ifdef __cplusplus
#error This source file is not C++ but C. Please use a C compiler.
#endif

#include "ed/inc/tok.h"

#include "ed/inc/sys.h"
#include "ed/inc/sysalloc.h"
#include "ed/inc/str.h"

/* macros ============================================================== */

#define MODULE "TOK"
#define VER "1.5"
#define ID "TOK Module \"C\" (c) ED 2001-2005"

/* constants =========================================================== */
/* types =============================================================== */
/* structures ========================================================== */

struct tok
{
   tok_err_e err;
   size_t n;
   char const **pp;
};

/* private data ======================================================== */
/* private functions =================================================== */

/* ---------------------------------------------------------------------
   is_in_sep()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
static int is_in_sep (int c, char const *sep)
{
   int is_in_the_seps = 0;

   if (sep != NULL && c != 0)
   {
      is_in_the_seps = strchr (sep, c) != NULL;
   }
   return is_in_the_seps;
}

/* ---------------------------------------------------------------------
   is_sep()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
static int is_sep (char const *s, char const *sep, size_t n)
{
   int is_a_sep = 0;

   if (sep != NULL)
   {
      is_a_sep = strncmp (s, sep, n) == 0;
   }
   return is_a_sep;
}

/* ---------------------------------------------------------------------
   get_tokens()
   ---------------------------------------------------------------------
   Very subtle code. The test suite must be extremely detailled.
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
static char const **get_tokens (char const *const s
                                ,char const *const sep
                                ,char const *str_delim
                                ,size_t n
                                ,size_t * pn)

{
   char const **pp = malloc (sizeof *pp * n);

   if (pp != NULL)
   {
      {
         size_t i;

         for (i = 0; i < n; i++)
         {
            pp[i] = NULL;
         }
      }

      {
         size_t const lsep = strlen (sep);
         size_t i = 0;
         char const *p_beg = s;
         int in_string = is_in_sep (*p_beg, str_delim);
         char const *p_end = p_beg + in_string;

         while (*p_end != 0)
         {
            if (!in_string)
            {
               if (is_sep (p_end, sep, lsep))
               {
                  ASSERT (i < n);
                  pp[i] = STR_sub_dyn (p_beg, p_end);
                  i++;
                  p_beg = p_end + lsep;
                  p_end = p_beg;
               }
               else
               {
                  p_end++;
               }

               in_string = is_in_sep (*p_end, str_delim);

               if (in_string)
               {
                  p_end++;
               }
            }
            else
            {
               /* in_string */
               if (is_in_sep (*p_end, str_delim))
               {
                  in_string = 0;
               }
               else
               {
                  p_end++;
               }
            }
         }

         /* last token */
         if (i < n)
         {
            if (*p_beg != 0)
            {
               pp[i] = STR_sub_dyn (p_beg, p_end);
               i++;
            }
         }

         if (pn != NULL)
         {
            *pn = i;
         }
      }
   }

   return pp;
}

/* ---------------------------------------------------------------------
   init()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
static tok_err_e init (tok_s * const this
                      ,char const *const s
                      ,char const *const sep
                      ,char const *str_delim)
{
   tok_err_e err = TOK_ERR_UNDEFINED;

   /* large evaluation. The actual value is adjusted by get_tokens() */
   size_t n = 1 + STR_count_sub (s, sep);

   char const **const pp = get_tokens (s, sep, str_delim, n, &n);

   ASSERT (this != NULL);

   if (pp)
   {
      this->pp = pp;
      this->n = n;
      err = TOK_OK;
   }
   else
   {
      err = TOK_ERR_MEMORY;
   }
   return err;
}

/* ---------------------------------------------------------------------
   list()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
static tok_err_e list (tok_s * const this, tok_list_f * pf, void *puser)
{
   tok_err_e err = TOK_ERR_UNDEFINED;

   ASSERT (this != NULL);

   if (pf != NULL)
   {
      size_t i;

      err = TOK_OK;

      for (i = 0; i < this->n; i++)
      {
         int cr = pf (this->pp[i], i, puser);

         if (cr != 0)
         {
            err = TOK_ERR_USER_STOP;
            break;
         }
      }
   }
   else
   {
      err = TOK_ERR_NO_CALLBACK;
   }

   this->err = err;

   return err;
}

/* internal public data ================================================ */
/* internal public functions =========================================== */
/* entry points ======================================================== */

/* ---------------------------------------------------------------------
   tok_sver()
   ---------------------------------------------------------------------
   Returns the "Version" string
   ---------------------------------------------------------------------
   I:
   O: ASCIIZ string pointer
   --------------------------------------------------------------------- */
char const *tok_sver (void)
{
   return VER;
}

/* ---------------------------------------------------------------------
   tok_sid()
   ---------------------------------------------------------------------
   Returns the "Identification" string
   ---------------------------------------------------------------------
   I:
   O: ASCIIZ string pointer
   --------------------------------------------------------------------- */
char const *tok_sid (void)
{
   return ID;
}

/* ---------------------------------------------------------------------
   tok_serr()
   ---------------------------------------------------------------------
   Returns an error string
   ---------------------------------------------------------------------
   I: Context
   O:
   --------------------------------------------------------------------- */
char const *tok_serr (tok_err_e err)
{
   char const *s = NULL;

   if (err >= 0 && err < TOK_ERR_NB)
   {
      static char const *as[] =
      {
         "OK",
#define ITEM(a,b) #b,
#include "ed/inc/tok_err.itm"
#undef ITEM
      };
      s = as[err];
   }

   return s;
}

/* ---------------------------------------------------------------------
   tok_create()
   ---------------------------------------------------------------------
   Creates and initializes the C-object
   ---------------------------------------------------------------------
   I: string to be analysed
   I: separator string
   I: list of string delimitors (in a string) or NULL
   O: Context
   --------------------------------------------------------------------- */
tok_s *tok_create (char const *s, char const *sep, char const *str_delim)
{
   tok_s *this = NULL;

   if (s != NULL && strlen (s))
   {
      this = malloc (sizeof *this);

      if (this != NULL)
      {
         CLR (this, tok_s);

         this->err = init (this, s, sep, str_delim);
      }
   }
   return this;
}

/* ---------------------------------------------------------------------
   tok_delete()
   ---------------------------------------------------------------------
   Suppresses the C-object
   ---------------------------------------------------------------------
   I: Context
   O:
   --------------------------------------------------------------------- */
void tok_delete (tok_s * const this)
{
   if (this != NULL)
   {
      if (this->pp)
      {
         size_t i;

         for (i = 0; i < this->n; i++)
         {
            if (this->pp[i] != NULL)
            {
               FREE (this->pp[i]);
            }
         }
         FREE (this->pp);
      }
   }
   free (this);
}

/* ---------------------------------------------------------------------
   tok_list()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I: Context
   O:
   --------------------------------------------------------------------- */
tok_err_e tok_list (tok_s * this, tok_list_f * pf, void *puser)
{
   tok_err_e err = TOK_ERR_UNDEFINED;

   if (this != NULL)
   {
      /*  function code */

      err = list (this, pf, puser);

   }
   else
   {
      err = TOK_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   tok_get()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I: Context
   O:
   --------------------------------------------------------------------- */
tok_err_e tok_get (tok_s * this, tok_info_s * p_info)
{
   tok_err_e err = TOK_ERR_UNDEFINED;

   if (this != NULL)
   {
      /*  function code */
      if (p_info)
      {
         p_info->argc = this->n;
         p_info->argv = this->pp;
         err = TOK_OK;
      }
   }
   else
   {
      err = TOK_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   tok_errno()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I: Context
   O:
   --------------------------------------------------------------------- */
tok_err_e tok_errno (tok_s * this)
{
   tok_err_e err = TOK_ERR_UNDEFINED;

   if (this != NULL)
   {
      err = this->err;
   }
   else
   {
      err = TOK_ERR_CONTEXT;
   }
   return err;
}

/* public data ========================================================= */

/* File generated by 'NEW.EXE' Ver 1.28 (c) ED 1998-2001 */
