/*
 * File: rconv.c
 *  Contents: cvcset, cvint, cvnum, cvpos, cvreal, cvstr, mkint,
 *    makereal, mksubs, strprc
 */

#include <math.h>
#include "..\h\config.h"
#include "..\h\rt.h"
#include "rproto.h"

/*
 * Prototypes.
 */

hidden int	cstos	Params((int *cs,dptr dp,char *s));
hidden int	itos	Params((long num,dptr dp,char *s));
hidden int	ston	Params((char *s,dptr dp));

#ifndef LargeInts
hidden int	radix	Params((int sign,int r,char *s,dptr dp));
#endif					/* LargeInts */

#ifdef StrInvoke
extern struct pstrnm pntab[];
#endif					/* StrInvoke */

#include <ctype.h>

#if !EBCDIC
#define tonum(c)	(isdigit(c) ? (c)-'0' : 10+(((c)|(040))-'a'))
#endif					/* !EBCDIC */

/*
 * cvcset(dp, cs, csbuf) - convert dp to a cset and
 *  make cs point to it, using csbuf as a buffer if necessary.
 */

int cvcset(dp, cs, csbuf)
register dptr dp;
int **cs, *csbuf;
   {
   register char *s;
   register word l;
   char sbuf[MaxCvtLen];

   if (dp->dword == D_Cset) {
      *cs = BlkLoc(*dp)->cset.bits;
      return T_Cset;
      }

   if (cvstr(dp, sbuf) == CvtFail)
      return CvtFail;

   for (l = 0; l < CsetSize; l++)
      csbuf[l] = 0;

   s = StrLoc(*dp);
   l = StrLen(*dp);
   while (l--) {
      Setb(ToAscii(*s), csbuf);
      s++;
      }
   *cs = csbuf;
   return T_Cset;
   }

/*
 * cvint - convert the value represented by dp into an integer and write
 *  the value into the location referenced by i.  cvint returns the type or
 *  CvtFail depending on the outcome of the conversion.
 */

int cvint(dp)
register dptr dp;
   {
   /*
    * Use cvnum to attempt the conversion into "result".
    */
   switch (cvnum(dp)) {

      case T_Integer:
	 return T_Integer;

#ifdef LargeInts
      case T_Bignum:
	 /*
	  * Bignum, not in the range of an integer.  Fail as we do 
	  *  for large reals.
	  */
	 return CvtFail;
#endif					/* LargeInts */

      case T_Real:
	 /*
	  * The value converted into a real number.  If it's not in the
	  *  range of an integer, fail, otherwise convert the real value
	  *  into an integer.
	  */
	 if (BlkLoc(*dp)->realblk.realval > MaxLong || 
	     BlkLoc(*dp)->realblk.realval < MinLong)
	    return CvtFail;
	 dp->dword = D_Integer;
	 IntVal(*dp) = (long)BlkLoc(*dp)->realblk.realval;
	 return T_Integer;

      default:
	 return CvtFail;
      }
   }

/*
 * cvnum - convert the value represented by d into a numeric quantity
 *  in place. The value returned is the type or CvtFail.
 */

int cvnum(dp)
register dptr dp;
   {
   static char sbuf[MaxCvtLen];
   struct descrip cstring;

   cstring = *dp;  /* placed outside "if" to avoid Lattice 3.21 code gen bug */
   if (Qual(*dp)) {
      qtos(&cstring, sbuf);
      return ston(StrLoc(cstring), dp);
      }

   switch (Type(*dp)) {

      case T_Integer:

#ifdef LargeInts
      case T_Bignum:
#endif					/* LargeInts */

      case T_Real:
	 return Type(*dp);

      default:
	 /*
	  * Try to convert the value to a string and
	  *  then try to convert the string to an integer.
	  */
	 if (cvstr(dp, sbuf) == CvtFail)
	    return CvtFail;
	 return ston(StrLoc(*dp), dp);
      }
   }

/*
 * ston - convert a string to a numeric quantity if possible.
 */
static int ston(s, dp)
register char *s;
dptr dp;
   {
   register int c;
   int realflag = 0;	/* indicates a real number */
   char msign = '+';    /* sign of mantissa */
   char esign = '+';    /* sign of exponent */
   double mantissa = 0; /* scaled mantissa with no fractional part */
   long lresult = 0;	/* integer result */
   int scale = 0;	/* number of decimal places to shift mantissa */
   int digits = 0;	/* total number of digits seen */
   int sdigits = 0;	/* number of significant digits seen */
   int exponent = 0;	/* exponent part of real number */
   double fiveto;	/* holds 5^scale */
   double power;	/* holds successive squares of 5 to compute fiveto */
   int err_no;
   char *ssave;         /* holds original ptr for bigradix */

   c = *s++;

   /*
    * Skip leading white space.
    */
   while (isspace(c))
      c = *s++;

   /*
    * Check for sign.
    */
   if (c == '+' || c == '-') {
      msign = c;
      c = *s++;
      }

   ssave = s - 1;   /* set pointer to beginning of digits in case it's needed */

   /*
    * Get integer part of mantissa.
    */
   while (isdigit(c)) {
      digits++;
      if (mantissa < Big) {
	 mantissa = mantissa * 10 + (c - '0');
         lresult = lresult * 10 + (c - '0');
	 if (mantissa > 0.0)
	    sdigits++;
	 }
      else
	 scale++;
      c = *s++;
      }

   /*
    * Check for based integer.
    */
   if (c == 'r' || c == 'R')

#ifdef LargeInts
      return bigradix(msign, (int)mantissa, s, dp);
#else					/* LargeInts */
      return radix(msign, (int)mantissa, s, dp);
#endif					/* LargeInts */

   /*
    * Get fractional part of mantissa.
    */
   if (c == '.') {
      realflag++;
      c = *s++;
      while (isdigit(c)) {
	 digits++;
	 if (mantissa < Big) {
	    mantissa = mantissa * 10 + (c - '0');
	    lresult = lresult * 10 + (c - '0');
	    scale--;
	    if (mantissa > 0.0)
	       sdigits++;
	    }
	 c = *s++;
	 }
      }

   /*
    * Check that at least one digit has been seen so far.
    */
   if (digits == 0)
      return CvtFail;

   /*
    * Get exponent part.
    */
   if (c == 'e' || c == 'E') {
      realflag++;
      c = *s++;
      if (c == '+' || c == '-') {
	 esign = c;
	 c = *s++;
	 }
      if (!isdigit(c))
	 return CvtFail;
      while (isdigit(c)) {
	 exponent = exponent * 10 + (c - '0');
	 c = *s++;
	 }
      scale += (esign == '+') ? exponent : -exponent;
      }

   /*
    * Skip trailing white space.
    */
   while (isspace(c))
      c = *s++;

   /*
    * Check that entire string has been consumed.
    */
   if (c != '\0')
      return CvtFail;

   /*
    * Test for integer.
    */
   if (!realflag && !scale && mantissa >= MinLong && mantissa <= MaxLong) {
      dp->dword = D_Integer;
      IntVal(*dp) = (msign == '+' ? lresult : -lresult);
      return T_Integer;
      }

#ifdef LargeInts
   /*
    * Test for bignum.
    */
   if (!realflag)
      return bigradix(msign, 10, ssave, dp);
#endif					/* LargeInts */

   if (!realflag)
      return CvtFail;		/* don't promote to real if integer format */

   /*
    * Rough tests for overflow and underflow.
    */
   if (sdigits + scale > LogHuge)
      return CvtFail;

   if (sdigits + scale < -LogHuge) {
      makereal(0.0, dp);
      return T_Real;
      }

   /*
    * Put the number together by multiplying the mantissa by 5^scale and
    *  then using ldexp() to multiply by 2^scale.
    */

   exponent = (scale > 0)? scale : -scale;
   fiveto = 1.0;
   power = 5.0;
   for (;;) {
      if (exponent & 01)
	 fiveto *= power;
      exponent >>= 1;
      if (exponent == 0)
	 break;
      power *= power;
      }
   if (scale > 0)
      mantissa *= fiveto;
   else
      mantissa /= fiveto;

   err_no = 0;
   mantissa = ldexp(mantissa, scale);
   if (err_no > 0 && mantissa > 0)
      /*
       * ldexp caused overflow.
       */
      return CvtFail;

   if (msign == '-')
      mantissa = -mantissa;
   makereal(mantissa, dp);
   return T_Real;
   }

#ifndef LargeInts
/*
 * radix - convert string s in radix r into an integer in *dp.  sign
 *  will be either '+' or '-'.
 */
static int radix(sign, r, s, dp)
int sign;
register int r;
register char *s;
dptr dp;
   {
   register int c;
   long num;

   if (r < 2 || r > 36)
      return CvtFail;
   c = *s++;
   num = 0L;
   while (isalnum(c)) {
      c = tonum(c);
      if (c >= r)
	 return CvtFail;
      num = num * r + c;
      c = *s++;
      }

   while (isspace(c))
      c = *s++;

   if (c != '\0')
      return CvtFail;

   dp->dword = D_Integer;
   dp->vword.integr = (sign == '+' ? num : -num);

   return T_Integer;
   }
#endif					/* LargeInts */

/*
 * cvpos - convert position to strictly positive position
 *  given length.
 */

word cvpos(pos, len)
long pos;
register long len;
   {
   register word p;

   /*
    * Make sure the position is in the range of an int. (?)
    */
   if ((long)(p = pos) != pos)
      return CvtFail;
   /*
    * Make sure the position is within range.
    */
   if (p < -len || p > len + 1)
      return CvtFail;
   /*
    * If the position is greater than zero, just return it.  Otherwise,
    *  convert the zero/negative position.
    */
   if (pos > 0)
      return p;
   return (len + p + 1);
   }

/*
 * cvreal - convert to real in place.
 */

int cvreal(dp)
register dptr dp;
   {
   /*
    * Use cvnum to classify the value.	Cast integers into reals and
    *  fail if the value is non-numeric.
    */
   switch (cvnum(dp)) {

      case T_Integer:
	 makereal((double)IntVal(*dp), dp);
	 return T_Real;

#ifdef LargeInts
      case T_Bignum:
	 makereal(bigtoreal(dp), dp);
	 return T_Real;
#endif					/* LargeInts */

      case T_Real:
	 return T_Real;

      default:
	 return CvtFail;
      }
   }

/*
 * cvstr(dp,s) - convert dp (in place) into a string, using s as buffer
 *  if necessary.  cvstr returns CvtFail if the conversion fails, Cvt if dp
 *  wasn't a string but was converted into one, and NoCvt if dp was already
 *  a string.  When a string conversion takes place, sbuf gets the
 *  resulting string.
 */

int cvstr(dp, sbuf)
register dptr dp;
char *sbuf;
   {
   double rres;

   if (Qual(*dp))
      return NoCvt;			/* It is already a string */

   switch (Type(*dp)) {
      /*
       * For types that can be converted into strings, call the
       *  appropriate conversion routine and return its result.
       *  Note that the conversion routines change the descriptor
       *  pointed to by dp.
       */
      case T_Integer:
	 return itos((long)IntVal(*dp), dp, sbuf);

#ifdef LargeInts
      case T_Bignum:
	 return bigtos(dp, dp);
#endif					/* LargeInts */

      case T_Real:
	 GetReal(dp,rres);
	 return rtos(rres, dp, sbuf);

      case T_Cset:
	 return cstos(BlkLoc(*dp)->cset.bits, dp, sbuf);

      default:
	 /*
	  * The value cannot be converted to a string.
	  */
	 return CvtFail;
      }
   }

/*
 * itos - convert the integer num into a string using s as a buffer and
 *  making q a descriptor for the resulting string.
 */

static int itos(num, dp, s)
long num;
dptr dp;
char *s;
   {
   register char *p;
   long ival;
   static char *maxneg = MaxNegInt;

   p = s + MaxCvtLen - 1;
   ival = num;

   *p = '\0';
   if (num >= 0L)
      do {
	 *--p = ival % 10L + '0';
	 ival /= 10L;
	 } while (ival != 0L);
   else {
      if (ival == -ival) {      /* max negative value */
	 p -= strlen (maxneg);
	 sprintf (p, "%s", maxneg);
         }
      else {
	ival = -ival;
	do {
	   *--p = '0' + (ival % 10L);
	   ival /= 10L;
	   } while (ival != 0L);
	*--p = '-';
	}
      }

   StrLen(*dp) = s + MaxCvtLen - 1 - p;
   StrLoc(*dp) = p;
   return Cvt;
   }

/*
 * rtos - convert the real number n into a string using s as a buffer and
 *  making a descriptor for the resulting string.
 */
int rtos(n, dp, s)
double n;
dptr dp;
char *s;
   {

   s++; 			/* leave room for leading zero */
/*
 * The following code is operating-system dependent [@rconv.01]. Convert real
 *  number to string.
 *
 * If IconGcvt is defined, icon_gcvt() is actually called, due to a #define
 *  in config.h.
 */

#if PORT
   gcvt(n, Precision, s);
Deliberate Syntax Error
#endif					/* PORT */

#if AMIGA || ATARI_ST || MSDOS || UNIX || VMS
   gcvt(n, Precision, s);
#endif                                  /* AMIGA  || ATARI_ST || ... */

#if VM || MVS
#if SASC
   sprintf(s,"%.*g", Precision, n);
   {
     char *ep = strstr(s, "e+");
     if (ep) memmove(ep+1, ep+2, strlen(ep+2)+1);
   }
#else					/* SASC */
   gcvt(n, Precision, s);
#endif					/* SASC */
#endif                                  /* MVS || VM */


#if HIGHC_386
   sprintf(s,"%.*g", Precision, n);
#endif					/* HIGHC_386 */

#if MACINTOSH
   sprintf(s,"%20g",n);
#endif					/* MACINTOSH */

/*
 * End of operating-system specific code.
 */
   
   /*
    * Now clean up possible messes.
    */
   while (*s == ' ')			/* delete leading blanks */
      s++;
   if (*s == '.') {			/* prefix 0 t0 to initial period */
      s--;
      *s = '0';
      }
   else if (strcmp(s, "-0.0") == 0)	/* negative zero */
      s++;
   else if (!index(s, '.') && !index(s,'e') && !index(s,'E'))
         strcat(s, ".0");		/* if no decimal point or exp. */
   if (s[strlen(s) - 1] == '.')		/* if decimal point is at the end ... */
      strcat(s, "0");
   StrLen(*dp) = strlen(s);
   StrLoc(*dp) = s;
   return Cvt;
   }

/*
 * cstos - convert the cset bit array pointed at by cs into a string using
 *  s as a buffer and making a descriptor for the resulting string.
 */

static int cstos(cs, dp, s)
int *cs;
dptr dp;
char *s;
   {
   register unsigned int w;
   register int j, i;
   register char *p;

   p = s;
   for (i = 0; i < CsetSize; i++) {
      if (cs[i])
	 for (j=i*IntBits, w=cs[i]; w; j++, w >>= 1)
	    if (w & 01)
	       *p++ = FromAscii((char)j);
      }
   *p = '\0';

   StrLen(*dp) = p - s;
   StrLoc(*dp) = s;
   return Cvt;
   }

/*
 * makereal(r, dp) - make a real number descriptor and associated block
 *  for r and place it in *dp.
 */

int makereal(r, dp)
double r;
register dptr dp;
   {

   if (blkreq((uword)sizeof(struct b_real)) == Error)
      return Error;
   dp->dword = D_Real;
   BlkLoc(*dp) = (union block *)alcreal(r);
   return Success;
   }

/*
 * mksubs - form a substring.  var is a descriptor for the string from
 *  which the substring is to be formed.  var may be a variable.  val
 *  is a dereferenced version of var.  The descriptor for the resulting
 *  substring is placed in *result.  The substring starts at position
 *  i and extends for j characters.
 */

novalue mksubs(var, val, i, j, result)
register dptr var, val, result;
word i, j;
   {

   if (!Var(*var)) {
      /*
       * var isn't a variable, just form a descriptor that points into
       *  the string named by val.
       */
      StrLen(*result) = j;
      StrLoc(*result) = StrLoc(*val) + i - 1;
      return;
      }

   if ((var)->dword == D_Tvsubs) {
      /*
       * If var is a substring trapped variable,
       *  adjust the position and make var the substrung string.
       */
	 i += BlkLoc(*var)->tvsubs.sspos - 1;
	 var = &BlkLoc(*var)->tvsubs.ssvar;
	 }

   /*
    * Make a substring trapped variable by passing the buck to alcsubs.
    */
   result->dword = D_Tvsubs;
   BlkLoc(*result) = (union block *) alcsubs(j, i, var);
   return;
   }

/*
 * strprc - Convert the qualified string named by *dp into a procedure
 *  descriptor if possible.  n is the number of arguments that the desired
 *  procedure has.  n is only used when the name of the procedure is
 *  non-alphabetic (hence, an operator).
 *
 */
int strprc(dp, n)
dptr dp;
word n;
   {

#ifndef StrInvoke
   return CvtFail;
#else					/* StrInvoke */

   dptr np, gp;
   struct pstrnm *p;
   char *s;
   int i;
   word ns;

   /*
    * Look in global name list first.
    */
   np = gnames; gp = globals;
   while (gp < eglobals) {
      if (!lexcmp(np++,dp))
         if (BlkLoc(*gp)->proc.title == T_Proc) {
   	 StrLen(*dp) = D_Proc; /* really type field */
   	 BlkLoc(*dp) = BlkLoc(*gp);
   	 return T_Proc;
   	 }
      gp++;
   }

/*
 * The name is not a global, see if it is a function or an operator.
 */
   s = StrLoc(*dp);
   if (StrLen(*dp) > MaxCvtLen)		/* can't be that big */
      return CvtFail;
   i = (int)StrLen(*dp);
   for (p = pntab; p->pstrep; p++)
      /*
       * Compare the desired name with each standard procedure/operator
       *  name.
       */
      if (strlen(p->pstrep) == i && strncmp(s,p->pstrep,i) == 0) {
	 if (isalpha(*s)) {
	    /*
	     * The names are the same and s starts with an alphabetic,
	     *  so it's the one being looked for; return it.
	     */
	     StrLen(*dp) = D_Proc;
	     BlkLoc(*dp) = (union block *) p->pblock;
	     return T_Proc;
	     }
	  if ((ns = p->pblock->nstatic) < 0)
	     ns = -ns;
	  else
	     ns = abs((int)p->pblock->nparam);
	  if (n == ns) {
	     StrLen(*dp) = D_Proc;	/* really type field */
	     BlkLoc(*dp) = (union block *)p->pblock;
	     return T_Proc;
	     }
	 }
   return CvtFail;
#endif					/* StrInvoke */

   }
