#include "config.h"
#include <ctype.h>
#include "f2c.h"
#include "fio.h"

extern int f__cursor;
#undef abs
#undef min
#undef max
#include <stdlib.h>

#include "fmt.h"
#include "fp.h"

static int
rd_Z (Uint * n, int w, ftnlen len)
{
  long x[9];
  char *s, *s0, *s1, *se, *t;
  int ch, i, w1, w2;
  static char hex[256];
  static int one = 1;
  int bad = 0;

  if (!hex['0'])
    {
      s = "0123456789";
      while ((ch = *s++))
	hex[ch] = ch - '0' + 1;
      s = "ABCDEF";
      while ((ch = *s++))
	hex[ch] = hex[ch + 'a' - 'A'] = ch - 'A' + 11;
    }
  s = s0 = (char *) x;
  s1 = (char *) &x[4];
  se = (char *) &x[8];
  if (len > 4 * (ftnlen) sizeof (long))
    return errno = 117;
  while (w)
    {
      GET (ch);
      if (ch == ',' || ch == '\n')
	break;
      w--;
      if (ch > ' ')
	{
	  if (!hex[ch & 0xff])
	    bad++;
	  *s++ = ch;
	  if (s == se)
	    {
	      /* discard excess characters */
	      for (t = s0, s = s1; t < s1;)
		*t++ = *s++;
	      s = s1;
	    }
	}
    }
  if (bad)
    return errno = 115;
  w = (int) len;
  w1 = s - s0;
  w2 = (w1 + 1) >> 1;
  t = (char *) n;
  if (*(char *) &one)
    {
      /* little endian */
      t += w - 1;
      i = -1;
    }
  else
    i = 1;
  for (; w > w2; t += i, --w)
    *t = 0;
  if (!w)
    return 0;
  if (w < w2)
    s0 = s - (w << 1);
  else if (w1 & 1)
    {
      *t = hex[*s0++ & 0xff] - 1;
      if (!--w)
	return 0;
      t += i;
    }
  do
    {
      *t = (hex[*s0 & 0xff] - 1) << 4 | (hex[s0[1] & 0xff] - 1);
      t += i;
      s0 += 2;
    }
  while (--w);
  return 0;
}

static int
rd_I (Uint * n, int w, ftnlen len, register int base)
{
  int ch, sign;
  longint x = 0;

  if (w <= 0)
    goto have_x;
  for (;;)
    {
      GET (ch);
      if (ch != ' ')
	break;
      if (!--w)
	goto have_x;
    }
  sign = 0;
  switch (ch)
    {
    case ',':
    case '\n':
      w = 0;
      goto have_x;
    case '-':
      sign = 1;
    case '+':
      break;
    default:
      if (ch >= '0' && ch <= '9')
	{
	  x = ch - '0';
	  break;
	}
      goto have_x;
    }
  while (--w)
    {
      GET (ch);
      if (ch >= '0' && ch <= '9')
	{
	  x = x * base + ch - '0';
	  continue;
	}
      if (ch != ' ')
	{
	  if (ch == '\n' || ch == ',')
	    w = 0;
	  break;
	}
      if (f__cblank)
	x *= base;
    }
  if (sign)
    x = -x;
have_x:
  if (len == sizeof (integer))
    n->il = x;
  else if (len == sizeof (char))
    n->ic = (char) x;
#ifdef Allow_TYQUAD
  else if (len == sizeof (longint))
    n->ili = x;
#endif
  else
    n->is = (short) x;
  if (w)
    {
      while (--w)
	GET (ch);
      return errno = 115;
    }
  return 0;
}

static int
rd_L (ftnint * n, int w, ftnlen len)
{
  int ch, dot, lv;

  if (w <= 0)
    goto bad;
  for (;;)
    {
      GET (ch);
      --w;
      if (ch != ' ')
	break;
      if (!w)
	goto bad;
    }
  dot = 0;
retry:
  switch (ch)
    {
    case '.':
      if (dot++ || !w)
	goto bad;
      GET (ch);
      --w;
      goto retry;
    case 't':
    case 'T':
      lv = 1;
      break;
    case 'f':
    case 'F':
      lv = 0;
      break;
    default:
    bad:
      for (; w > 0; --w)
	GET (ch);
      /* no break */
    case ',':
    case '\n':
      return errno = 116;
    }
  /* The switch statement that was here
     didn't cut it:  It broke down for targets
     where sizeof(char) == sizeof(short). */
  if (len == sizeof (char))
    *(char *) n = (char) lv;
  else if (len == sizeof (short))
    *(short *) n = (short) lv;
  else
    *n = lv;
  while (w-- > 0)
    {
      GET (ch);
      if (ch == ',' || ch == '\n')
	break;
    }
  return 0;
}

static int
rd_F (ufloat * p, int w, int d, ftnlen len)
{
  char s[FMAX + EXPMAXDIGS + 4];
  register int ch;
  register char *sp, *spe, *sp1;
  double x;
  int scale1, se;
  long e, exp;

  sp1 = sp = s;
  spe = sp + FMAX;
  exp = -d;
  x = 0.;

  do
    {
      GET (ch);
      w--;
    }
  while (ch == ' ' && w);
  switch (ch)
    {
    case '-':
      *sp++ = ch;
      sp1++;
      spe++;
    case '+':
      if (!w)
	goto zero;
      --w;
      GET (ch);
    }
  while (ch == ' ')
    {
    blankdrop:
      if (!w--)
	goto zero;
      GET (ch);
    }
  while (ch == '0')
    {
      if (!w--)
	goto zero;
      GET (ch);
    }
  if (ch == ' ' && f__cblank)
    goto blankdrop;
  scale1 = f__scale;
  while (isdigit (ch))
    {
    digloop1:
      if (sp < spe)
	*sp++ = ch;
      else
	++exp;
    digloop1e:
      if (!w--)
	goto done;
      GET (ch);
    }
  if (ch == ' ')
    {
      if (f__cblank)
	{
	  ch = '0';
	  goto digloop1;
	}
      goto digloop1e;
    }
  if (ch == '.')
    {
      exp += d;
      if (!w--)
	goto done;
      GET (ch);
      if (sp == sp1)
	{			/* no digits yet */
	  while (ch == '0')
	    {
	    skip01:
	      --exp;
	    skip0:
	      if (!w--)
		goto done;
	      GET (ch);
	    }
	  if (ch == ' ')
	    {
	      if (f__cblank)
		goto skip01;
	      goto skip0;
	    }
	}
      while (isdigit (ch))
	{
	digloop2:
	  if (sp < spe)
	    {
	      *sp++ = ch;
	      --exp;
	    }
	digloop2e:
	  if (!w--)
	    goto done;
	  GET (ch);
	}
      if (ch == ' ')
	{
	  if (f__cblank)
	    {
	      ch = '0';
	      goto digloop2;
	    }
	  goto digloop2e;
	}
    }
  switch (ch)
    {
    default:
      break;
    case '-':
      se = 1;
      goto signonly;
    case '+':
      se = 0;
      goto signonly;
    case 'e':
    case 'E':
    case 'd':
    case 'D':
      if (!w--)
	goto bad;
      GET (ch);
      while (ch == ' ')
	{
	  if (!w--)
	    goto bad;
	  GET (ch);
	}
      se = 0;
      switch (ch)
	{
	case '-':
	  se = 1;
	case '+':
	signonly:
	  if (!w--)
	    goto bad;
	  GET (ch);
	}
      while (ch == ' ')
	{
	  if (!w--)
	    goto bad;
	  GET (ch);
	}
      if (!isdigit (ch))
	goto bad;

      e = ch - '0';
      for (;;)
	{
	  if (!w--)
	    {
	      ch = '\n';
	      break;
	    }
	  GET (ch);
	  if (!isdigit (ch))
	    {
	      if (ch == ' ')
		{
		  if (f__cblank)
		    ch = '0';
		  else
		    continue;
		}
	      else
		break;
	    }
	  e = 10 * e + ch - '0';
	  if (e > EXPMAX && sp > sp1)
	    goto bad;
	}
      if (se)
	exp -= e;
      else
	exp += e;
      scale1 = 0;
    }
  switch (ch)
    {
    case '\n':
    case ',':
      break;
    default:
    bad:
      return (errno = 115);
    }
done:
  if (sp > sp1)
    {
      while (*--sp == '0')
	++exp;
      if (exp -= scale1)
	sprintf (sp + 1, "e%ld", exp);
      else
	sp[1] = 0;
      x = atof (s);
    }
zero:
  if (len == sizeof (real))
    p->pf = x;
  else
    p->pd = x;
  return (0);
}


static int
rd_A (char *p, ftnlen len)
{
  int i, ch;
  for (i = 0; i < len; i++)
    {
      GET (ch);
      *p++ = VAL (ch);
    }
  return (0);
}
static int
rd_AW (char *p, int w, ftnlen len)
{
  int i, ch;
  if (w >= len)
    {
      for (i = 0; i < w - len; i++)
	GET (ch);
      for (i = 0; i < len; i++)
	{
	  GET (ch);
	  *p++ = VAL (ch);
	}
      return (0);
    }
  for (i = 0; i < w; i++)
    {
      GET (ch);
      *p++ = VAL (ch);
    }
  for (i = 0; i < len - w; i++)
    *p++ = ' ';
  return (0);
}
static int
rd_H (int n, char *s)
{
  int i, ch;
  for (i = 0; i < n; i++)
    if ((ch = (*f__getn) ()) < 0)
      return (ch);
    else
      *s++ = ch == '\n' ? ' ' : ch;
  return (1);
}
static int
rd_POS (char *s)
{
  char quote;
  int ch;
  quote = *s++;
  for (; *s; s++)
    if (*s == quote && *(s + 1) != quote)
      break;
    else if ((ch = (*f__getn) ()) < 0)
      return (ch);
    else
      *s = ch == '\n' ? ' ' : ch;
  return (1);
}

int
rd_ed (struct syl * p, char *ptr, ftnlen len)
{
  int ch;
  for (; f__cursor > 0; f__cursor--)
    if ((ch = (*f__getn) ()) < 0)
      return (ch);
  if (f__cursor < 0)
    {
      if (f__recpos + f__cursor < 0)	/*err(elist->cierr,110,"fmt") */
	f__cursor = -f__recpos;	/* is this in the standard? */
      if (f__external == 0)
	{
	  extern char *f__icptr;
	  f__icptr += f__cursor;
	}
      else if (f__curunit && f__curunit->useek)
	FSEEK (f__cf, (off_t) f__cursor, SEEK_CUR);
      else
	err (f__elist->cierr, 106, "fmt");
      f__recpos += f__cursor;
      f__cursor = 0;
    }
  switch (p->op)
    {
    default:
      fprintf (stderr, "rd_ed, unexpected code: %d\n", p->op);
      sig_die (f__fmtbuf, 1);
    case IM:
    case I:
      ch = rd_I ((Uint *) ptr, p->p1, len, 10);
      break;

      /* O and OM don't work right for character, double, complex, */
      /* or doublecomplex, and they differ from Fortran 90 in */
      /* showing a minus sign for negative values. */

    case OM:
    case O:
      ch = rd_I ((Uint *) ptr, p->p1, len, 8);
      break;
    case L:
      ch = rd_L ((ftnint *) ptr, p->p1, len);
      break;
    case A:
      ch = rd_A (ptr, len);
      break;
    case AW:
      ch = rd_AW (ptr, p->p1, len);
      break;
    case E:
    case EE:
    case D:
    case G:
    case GE:
    case F:
      ch = rd_F ((ufloat *) ptr, p->p1, p->p2.i[0], len);
      break;

      /* Z and ZM assume 8-bit bytes. */

    case ZM:
    case Z:
      ch = rd_Z ((Uint *) ptr, p->p1, len);
      break;
    }
  if (ch == 0)
    return (ch);
  else if (ch == EOF)
    return (EOF);
  if (f__cf)
    clearerr (f__cf);
  return (errno);
}

int
rd_ned (struct syl * p)
{
  switch (p->op)
    {
    default:
      fprintf (stderr, "rd_ned, unexpected code: %d\n", p->op);
      sig_die (f__fmtbuf, 1);
    case APOS:
      return (rd_POS (p->p2.s));
    case H:
      return (rd_H (p->p1, p->p2.s));
    case SLASH:
      return ((*f__donewrec) ());
    case TR:
    case X:
      f__cursor += p->p1;
      return (1);
    case T:
      f__cursor = p->p1 - f__recpos - 1;
      return (1);
    case TL:
      f__cursor -= p->p1;
      if (f__cursor < -f__recpos)	/* TL1000, 1X */
	f__cursor = -f__recpos;
      return (1);
    }
}
