/* ---------------------------------------------------------------------
   (c) ED 2004-2005
   Project      : CLIB - Flexible STRing
   Function     : Flexible string manager
   Module       : FSTR
   File         : fstr.c
   Created      : 03-12-2004
   Modified     : 30-08-2005
   --------------------------------------------------------------------- */

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

   1.3 30-08-2005 Added fstr_serr()
   1.2 15-12-2004 Debug enlarge()
   1.1 14-12-2004 Added fstr_add(), fstr_del() and fstr_len()
   1.0 03-12-2004 Initial version
   0.0 03-12-2004 Created

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

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

#ifdef FSTR_DBG
#include <stdio.h>
#define MODULE ID"."
#endif

/* private macro definitions =========================================== */

#define ID "FSTR"
#define VER "1.3"

/* private constants =================================================== */

/* private types ======================================================= */

/* private structures ================================================== */

struct fstr
{
   size_t size; /* size of the array of char */
   size_t w;    /* write index (the postion of the current final 0) */
   char *s;     /* pointer to the dynamic array of char */
};

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

/* public internal functions =========================================== */

/* ---------------------------------------------------------------------
   enlarge ()
   ---------------------------------------------------------------------
   enlarge the string
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
static fstr_err_e enlarge (fstr_s * this, unsigned const len)
{
   fstr_err_e err = FSTR_OK;
   /* enlarge the string ... (x2) */
   size_t app_size = this->size - 1;

   /* double as necessary */
   while (app_size < len)
   {
      app_size *= 2;
   }

   {
      char *tmp = realloc (this->s, this->size + app_size);

      if (tmp != NULL)
      {
         this->s = tmp;
         memset (this->s + this->size - 1, 0, app_size + 1);
         this->size += app_size;
      }
      else
      {
         FREE (this->s);
         this->w = 0;
         this->size = 0;
         err = FSTR_ERR_MEMORY;
      }
   }
   return err;
}

/* entry points ======================================================== */

/* ---------------------------------------------------------------------
   fstr_sid ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char const *fstr_sid (void)
{
   return ID;
}

/* ---------------------------------------------------------------------
   fstr_sver ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char const *fstr_sver (void)
{
   return VER;
}

/* ---------------------------------------------------------------------
   fstr_serr ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char const *fstr_serr (fstr_err_e err)
{
   char const *s = "FSTR_ERR_???";

   if (err < FSTR_ERR_NB)
   {
      static char const *as[] =
      {
         "FSTR_OK",
#define ITEM(n_, s_)\
         #s_,

#include "ed/inc/fstr_err.itm"

#undef ITEM

      };
      s = as[err];
   }
   return s;
}

/* ---------------------------------------------------------------------
   fstr_create ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
fstr_s *fstr_create (size_t len)
{
   fstr_s *this = malloc (sizeof *this);

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

      {
         size_t const size = len + 1;
         char *s = malloc (size);

         if (s != NULL)
         {
            this->size = size;
            this->s = s;

            memset (s, 0, this->size);
         }
      }
   }
   return this;
}

/* ---------------------------------------------------------------------
   fstr_delete ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
void fstr_delete (fstr_s * this)
{
   if (this != NULL)
   {

      FREE (this->s);

      FREE (this);
   }
}

/* ---------------------------------------------------------------------
   fstr_clr ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
fstr_err_e fstr_clr (fstr_s * this)
{
   fstr_err_e err = FSTR_OK;

   if (this != NULL)
   {
      if (this->s != NULL)
      {
         memset (this->s, 0, this->size);
         this->w = 0;
      }
      else
      {
         err = FSTR_ERR_STRING;
      }
   }
   else
   {
      err = FSTR_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   fstr_cat ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
fstr_err_e fstr_cat (fstr_s * this, char const *s)
{
   fstr_err_e err = FSTR_OK;

   if (this != NULL)
   {
      if (this->s != NULL)
      {
         size_t const len_to_add = strlen (s);

         /* is the string big enough ? */
         if (this->w + len_to_add >= this->size)
         {
            err = enlarge (this, len_to_add);
         }

         if (this->s != NULL)
         {
            /* strcat... */
            memcpy (this->s + this->w, s, len_to_add);

            ASSERT (this->s[this->size - 1] == 0);
            this->w += len_to_add;
         }
      }
      else
      {
         err = FSTR_ERR_STRING;
      }
   }
   else
   {
      err = FSTR_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   fstr_str ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char *fstr_str (fstr_s * this)
{
   char *s = NULL;

   if (this != NULL)
   {
      s = this->s;
   }
   return s;
}

/* ---------------------------------------------------------------------
   fstr_str ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
size_t fstr_len (fstr_s * this)
{
   size_t len = 0;

   if (this != NULL)
   {
      len = this->w;
   }
   return len;
}

/* ---------------------------------------------------------------------
   fstr_remove ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char *fstr_remove (fstr_s * this)
{
   char *s = NULL;

   if (this != NULL)
   {
      s = this->s;

      this->size = 0;
      this->w = 0;
      this->s = NULL;
   }
   return s;
}

/* ---------------------------------------------------------------------
   fstr_add ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
fstr_err_e fstr_add (fstr_s * this, int c)
{
   fstr_err_e err = FSTR_OK;

   if (this != NULL)
   {
      if (this->s != NULL)
      {
         size_t const len_to_add = 1;

         /* is the string big enough ? */
         if (this->w + len_to_add >= this->size)
         {
            err = enlarge (this, len_to_add);
         }

         if (this->s != NULL)
         {
            /* add */
            this->s[this->w] = c;
            this->w += len_to_add;
         }
      }
      else
      {
         err = FSTR_ERR_STRING;
      }
   }
   else
   {
      err = FSTR_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   fstr_del ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
fstr_err_e fstr_del (fstr_s * this)
{
   fstr_err_e err = FSTR_OK;

   if (this != NULL)
   {
      if (this->w > 0)
      {
         this->w--;
         this->s[this->w] = 0;
      }
      else
      {
         err = FSTR_ERR_EMPTY_STRING;
      }
   }
   return err;
}

#ifdef FSTR_DBG
/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
void fstr_dbg (fstr_s * this)
{
   if (this != NULL)
   {
      printf (MODULE"size=%3u+1"
              ,(unsigned) this->w
         );

      if (this->s != NULL)
      {
         printf (" w=%3u s='%s' (%u)"
                 ,(unsigned) this->size - 1
                 ,this->s
                 ,(unsigned) strlen (this->s)
            );
      }
   }
   printf (EOL);
}
#endif

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

/* ---------------------------------------------------------------------
   Generated by NEW (c) ED 2.4
   Powered by C-code generator (c) ED 2003 1.0
   --------------------------------------------------------------------- */
