/* ---------------------------------------------------------------------
   io.c

   1.0 : 12-11-2003 : intial version
   1.1 : 13-11-2004 : [f]get_d() added
   1.2 : 12-02-2005 : [f]get_line() added (dynamic)
   --------------------------------------------------------------------- */

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

#include <string.h>

/* macros ============================================================== */
#define VER "1.2"
#define MOD "IO"
#define CPY "(c) ED 2003-2005"

/* constants =========================================================== */
/* types =============================================================== */
/* structures ========================================================== */
/* private data ======================================================== */
/* private functions =================================================== */
/* internal public data ================================================ */
/* internal public functions =========================================== */
/* entry points ======================================================== */

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
char const *io_sver (void)
{
   return VER;
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
char const *io_sid (void)
{
   return MOD " " CPY;
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
int fget_s (char *s, size_t size, FILE * fp)
{
   int err = IO_OK;

   if (s != NULL)
   {
      if (size > 0)
      {
         if (fp != NULL)
         {
            size_t i = 0;
            int end = 0;

            while (i < size - 1 && !end)
            {
               int c = fgetc (fp);

               switch (c)
               {
               case EOF:
                  err = IO_ERR_READ;
                  end = 1;
                  break;

               case '\n':
                  end = 1;
                  s[i] = 0;
                  break;

               default:
                  s[i] = (uchar) c;
                  i++;
               }
            }

            if (err != IO_ERR_READ)
            {
               s[i] = 0;

               if (i == size - 1)
               {
                  int c;
                  int n = 0;

                  while ((c = fgetc (fp)) != '\n' && c != EOF)
                  {
                     n++;
                  }

                  if (n > 0)
                  {
                     err = IO_ERR_LENGTH;
                  }
               }
            }
         }
         else
         {
            err = IO_ERR_FILE;
         }
      }
      else
      {
         err = IO_ERR_BUFFER_SIZE;
      }
   }
   else
   {
      err = IO_ERR_BUFFER;
   }

   return err;
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
int fget_c (FILE * fp)
{
   int c = EOF;
   char s[3];
   int err = fget_s (s, sizeof s, fp);

   if (err == IO_OK)
   {
      c = *s;
   }
   return c;
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
int fget_l (long *p, FILE * fp)
{
   int err;
   char s[32];

   err = fget_s (s, sizeof s, fp);

   if (!err)
   {
      if (*s)
      {
         char *pend;
         long n = strtol (s, &pend, 0);
         if (pend && *pend == 0)
         {
            if (p)
            {
               *p = n;
            }
            else
            {
               err = IO_ERR_OUTPUT_ADDRESS;
            }
         }
         else
         {
            err = IO_ERR_CONVERSION;
         }
      }
      else
      {
         err = IO_ERR_EMPTY;
      }
   }

   return err;
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
int fget_d (double *p, FILE * fp)
{
   int err;
   char s[64];

   err = fget_s (s, sizeof s, fp);

   if (!err)
   {
      if (*s)
      {
         char *pend;
         double n = strtod (s, &pend);
         if (pend && *pend == 0)
         {
            if (p)
            {
               *p = n;
            }
            else
            {
               err = IO_ERR_OUTPUT_ADDRESS;
            }
         }
         else
         {
            err = IO_ERR_CONVERSION;
         }
      }
      else
      {
         err = IO_ERR_EMPTY;
      }
   }

   return err;
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
int fget_ul (ulong * p, FILE * fp)
{
   int err;
   char s[32];

   err = fget_s (s, sizeof s, fp);

   if (!err)
   {
      if (*s)
      {
         char *pend;
         ulong n = strtoul (s, &pend, 0);
         if (pend && *pend == 0)
         {
            if (p)
            {
               *p = n;
            }
            else
            {
               err = IO_ERR_OUTPUT_ADDRESS;
            }
         }
         else
         {
            err = IO_ERR_CONVERSION;
         }
      }
      else
      {
         err = IO_ERR_EMPTY;
      }
   }

   return err;
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
int get_s (char *s, size_t size)
{
   return fget_s (s, size, stdin);
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
int get_c (void)
{
   return fget_c (stdin);
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
int get_l (long *p)
{
   return fget_l (p, stdin);
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
int get_ul (ulong * p)
{
   return fget_ul (p, stdin);
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
int get_d (double *p)
{
   return fget_d (p, stdin);
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
char *fget_line (FILE * fp, int *p_end)
{
   char *sline = NULL;

   fstr_s *p_fs = fstr_create (32);

   if (p_fs != NULL)
   {
      int c;
      fstr_err_e fs_err = FSTR_OK;

      while ((c = fgetc (fp)) != '\n' && c != EOF && fs_err == FSTR_OK)
      {
         fs_err = fstr_add (p_fs, c);
      }

      if (c == EOF)
      {
         if (p_end != NULL)
         {
            *p_end = 1;
         }
      }
      else
      {
         if (fs_err == FSTR_OK)
         {
            sline = fstr_remove (p_fs);
         }
      }
      fstr_delete (p_fs), p_fs = NULL;
   }
   return sline;
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
char *get_line (void)
{
   return fget_line (stdin, NULL);
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
void io_perror (int err)
{
   static char const *const as[] =
   {
      "OK",
      "ERR_READ",
      "ERR_LENGTH",
      "ERR_BUFFER",
      "ERR_CONVERSION",
      "ERR_EMPTY",
      "ERR_OUTPUT_ADDRESS",
      "ERR_FILE",
      "ERR_BUFFER_SIZE",
   };
   char const *s = "unknown error";

   if (err < IO_ERR_NB)
   {
      s = as[err];
   }

   fprintf (stderr, "input error #%u: '%s'\n"
            ,(uint) err
            ,s);
}

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