/* atof_generic.c - turn a string of digits into a Flonum
   Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.

   This file is part of GAS, the GNU Assembler.

   GAS is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   GAS is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GAS; see the file COPYING.  If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include <ctype.h>
#include <string.h>

#include "as.h"

#ifdef __GNUC__
#define alloca __builtin_alloca
#else
#ifdef sparc
#include <alloca.h>
#endif
#endif

#ifndef FALSE
#define FALSE (0)
#endif
#ifndef TRUE
#define TRUE  (1)
#endif

/***********************************************************************\
 *									*
 *	Given a string of decimal digits , with optional decimal	*
 *	mark and optional decimal exponent (place value) of the		*
 *	lowest_order decimal digit: produce a floating point		*
 *	number. The number is 'generic' floating point: our		*
 *	caller will encode it for a specific machine architecture.	*
 *									*
 *	Assumptions							*
 *		uses base (radix) 2					*
 *		this machine uses 2's complement binary integers	*
 *		target flonums use "      "         "       "		*
 *		target flonums exponents fit in a long		*
 *									*
 \***********************************************************************/

/*

  Syntax:

  <flonum> ::= <optional-sign> <decimal-number> <optional-exponent>
  <optional-sign> ::= '+' | '-' | {empty}
  <decimal-number> ::= <integer>
  | <integer> <radix-character>
  | <integer> <radix-character> <integer>
  | <radix-character> <integer>

  <optional-exponent> ::= {empty}
  | <exponent-character> <optional-sign> <integer>

  <integer> ::= <digit> | <digit> <integer>
  <digit> ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
  <exponent-character> ::= {one character from "string_of_decimal_exponent_marks"}
  <radix-character> ::= {one character from "string_of_decimal_marks"}

  */

int				/* 0 if OK */
atof_generic (
	       address_of_string_pointer,	/* return pointer to just
						AFTER number we read. */
	       string_of_decimal_marks,	/* At most one per number. */
	       string_of_decimal_exponent_marks,
	       address_of_generic_floating_point_number)
     char **address_of_string_pointer;
     const char *string_of_decimal_marks;
     const char *string_of_decimal_exponent_marks;
     FLONUM_TYPE *address_of_generic_floating_point_number;
{
  int return_value;		/* 0 means OK. */
  char *first_digit;
  /* char *last_digit; JF unused */
  int number_of_digits_before_decimal;
  int number_of_digits_after_decimal;
  long decimal_exponent;
  int number_of_digits_available;
  char digits_sign_char;

  /*
   * Scan the input string, abstracting (1)digits (2)decimal mark (3) exponent.
   * It would be simpler to modify the string, but we don't; just to be nice
   * to caller.
   * We need to know how many digits we have, so we can allocate space for
   * the digits' value.
   */

  char *p;
  char c;
  int seen_significant_digit;

  first_digit = *address_of_string_pointer;
  c = *first_digit;

  if (c == '-' || c == '+')
    {
      digits_sign_char = c;
      first_digit++;
    }
  else
    digits_sign_char = '+';

  if ((first_digit[0] == 'n' || first_digit[0] == 'N')
      && (first_digit[1] == 'a' || first_digit[1] == 'A')
      && (first_digit[2] == 'n' || first_digit[2] == 'N'))
    {
      address_of_generic_floating_point_number->sign = 0;
      address_of_generic_floating_point_number->exponent = 0;
      address_of_generic_floating_point_number->leader =
	address_of_generic_floating_point_number->low;
      *address_of_string_pointer = first_digit + 3;
      return (0);
    }

  if ((first_digit[0] == 'i' || first_digit[0] == 'I')
      && (first_digit[1] == 'n' || first_digit[1] == 'N')
      && (first_digit[2] == 'f' || first_digit[2] == 'F'))
    {
      address_of_generic_floating_point_number->sign =
	digits_sign_char == '+' ? 'P' : 'N';
      address_of_generic_floating_point_number->exponent = 0;
      address_of_generic_floating_point_number->leader =
	address_of_generic_floating_point_number->low;

      if ((first_digit[3] == 'i'
	   || first_digit[3] == 'I')
	  && (first_digit[4] == 'n'
	      || first_digit[4] == 'N')
	  && (first_digit[5] == 'i'
	      || first_digit[5] == 'I')
	  && (first_digit[6] == 't'
	      || first_digit[6] == 'T')
	  && (first_digit[7] == 'y'
	      || first_digit[7] == 'Y'))
	{
	  *address_of_string_pointer = first_digit + 8;
	}
      else
	{
	  *address_of_string_pointer = first_digit + 3;
	}
      return (0);
    }

  number_of_digits_before_decimal = 0;
  number_of_digits_after_decimal = 0;
  decimal_exponent = 0;
  seen_significant_digit = 0;
  for (p = first_digit; (((c = *p) != '\0')
			 && (!c || !strchr (string_of_decimal_marks, c))
		  && (!c || !strchr (string_of_decimal_exponent_marks, c)));
       p++)
    {
      if (isdigit (c))
	{
	  if (seen_significant_digit || c > '0')
	    {
	      ++number_of_digits_before_decimal;
	      seen_significant_digit = 1;
	    }
	  else
	    {
	      first_digit++;
	    }
	}
      else
	{
	  break;		/* p -> char after pre-decimal digits. */
	}
    }				/* For each digit before decimal mark. */

#ifndef OLD_FLOAT_READS
  /* Ignore trailing 0's after the decimal point.  The original code here
	 * (ifdef'd out) does not do this, and numbers like
	 *	4.29496729600000000000e+09	(2**31)
	 * come out inexact for some reason related to length of the digit
	 * string.
	 */
  if (c && strchr (string_of_decimal_marks, c))
    {
      int zeros = 0;		/* Length of current string of zeros */

      for (p++; (c = *p) && isdigit (c); p++)
	{
	  if (c == '0')
	    {
	      zeros++;
	    }
	  else
	    {
	      number_of_digits_after_decimal += 1 + zeros;
	      zeros = 0;
	    }
	}
    }
#else
  if (c && strchr (string_of_decimal_marks, c))
    {
      for (p++; (((c = *p) != '\0')
		 && (!c || !strchr (string_of_decimal_exponent_marks, c)));
	   p++)
	{
	  if (isdigit (c))
	    {
	      number_of_digits_after_decimal++;	/* This may be retracted below. */
	      if ( /* seen_significant_digit || */ c > '0')
		{
		  seen_significant_digit = TRUE;
		}
	    }
	  else
	    {
	      if (!seen_significant_digit)
		{
		  number_of_digits_after_decimal = 0;
		}
	      break;
	    }
	}			/* For each digit after decimal mark. */
    }

  while (number_of_digits_after_decimal && first_digit[number_of_digits_before_decimal
				   + number_of_digits_after_decimal] == '0')
    --number_of_digits_after_decimal;
  /* last_digit = p; JF unused */
#endif

  if (c && strchr (string_of_decimal_exponent_marks, c))
    {
      char digits_exponent_sign_char;

      c = *++p;
      if (c && strchr ("+-", c))
	{
	  digits_exponent_sign_char = c;
	  c = *++p;
	}
      else
	{
	  digits_exponent_sign_char = '+';
	}

      for (; (c); c = *++p)
	{
	  if (isdigit (c))
	    {
	      decimal_exponent = decimal_exponent * 10 + c - '0';
	      /*
				 * BUG! If we overflow here, we lose!
				 */
	    }
	  else
	    {
	      break;
	    }
	}

      if (digits_exponent_sign_char == '-')
	{
	  decimal_exponent = -decimal_exponent;
	}
    }

  *address_of_string_pointer = p;



  number_of_digits_available =
    number_of_digits_before_decimal + number_of_digits_after_decimal;
  return_value = 0;
  if (number_of_digits_available == 0)
    {
      address_of_generic_floating_point_number->exponent = 0;	/* Not strictly necessary */
      address_of_generic_floating_point_number->leader
	= -1 + address_of_generic_floating_point_number->low;
      address_of_generic_floating_point_number->sign = digits_sign_char;
      /* We have just concocted (+/-)0.0E0 */

    }
  else
    {
      int count;		/* Number of useful digits left to scan. */

      LITTLENUM_TYPE *digits_binary_low;
      unsigned int precision;
      unsigned int maximum_useful_digits;
      unsigned int number_of_digits_to_use;
      unsigned int more_than_enough_bits_for_digits;
      unsigned int more_than_enough_littlenums_for_digits;
      unsigned int size_of_digits_in_littlenums;
      unsigned int size_of_digits_in_chars;
      FLONUM_TYPE power_of_10_flonum;
      FLONUM_TYPE digits_flonum;

      precision = (address_of_generic_floating_point_number->high
		   - address_of_generic_floating_point_number->low
		   + 1);	/* Number of destination littlenums. */

      /* Includes guard bits (two littlenums worth) */
      maximum_useful_digits = (((double) (precision - 2))
			       * ((double) (LITTLENUM_NUMBER_OF_BITS))
			       / (LOG_TO_BASE_2_OF_10))
	+ 2;			/* 2 :: guard digits. */

      if (number_of_digits_available > maximum_useful_digits)
	{
	  number_of_digits_to_use = maximum_useful_digits;
	}
      else
	{
	  number_of_digits_to_use = number_of_digits_available;
	}

      decimal_exponent += number_of_digits_before_decimal - number_of_digits_to_use;

      more_than_enough_bits_for_digits
	= ((((double) number_of_digits_to_use) * LOG_TO_BASE_2_OF_10) + 1);

      more_than_enough_littlenums_for_digits
	= (more_than_enough_bits_for_digits
	   / LITTLENUM_NUMBER_OF_BITS)
	+ 2;

      /*
		 * Compute (digits) part. In "12.34E56" this is the "1234" part.
		 * Arithmetic is exact here. If no digits are supplied then
		 * this part is a 0 valued binary integer.
		 * Allocate room to build up the binary number as littlenums.
		 * We want this memory to disappear when we leave this function.
		 * Assume no alignment problems => (room for n objects) ==
		 * n * (room for 1 object).
		 */

      size_of_digits_in_littlenums = more_than_enough_littlenums_for_digits;
      size_of_digits_in_chars = size_of_digits_in_littlenums
	* sizeof (LITTLENUM_TYPE);

      digits_binary_low = (LITTLENUM_TYPE *)
	alloca (size_of_digits_in_chars);

      memset ((char *) digits_binary_low, '\0', size_of_digits_in_chars);

      /* Digits_binary_low[] is allocated and zeroed. */

      /*
		 * Parse the decimal digits as if * digits_low was in the units position.
		 * Emit a binary number into digits_binary_low[].
		 *
		 * Use a large-precision version of:
		 * (((1st-digit) * 10 + 2nd-digit) * 10 + 3rd-digit ...) * 10 + last-digit
		 */

      for (p = first_digit, count = number_of_digits_to_use; count; p++, --count)
	{
	  c = *p;
	  if (isdigit (c))
	    {
	      /*
				 * Multiply by 10. Assume can never overflow.
				 * Add this digit to digits_binary_low[].
				 */

	      long carry;
	      LITTLENUM_TYPE *littlenum_pointer;
	      LITTLENUM_TYPE *littlenum_limit;

	      littlenum_limit = digits_binary_low
		+ more_than_enough_littlenums_for_digits
		- 1;

	      carry = c - '0';	/* char -> binary */

	      for (littlenum_pointer = digits_binary_low;
		   littlenum_pointer <= littlenum_limit;
		   littlenum_pointer++)
		{
		  long work;

		  work = carry + 10 * (long) (*littlenum_pointer);
		  *littlenum_pointer = work & LITTLENUM_MASK;
		  carry = work >> LITTLENUM_NUMBER_OF_BITS;
		}

	      if (carry != 0)
		{
		  /*
					 * We have a GROSS internal error.
					 * This should never happen.
					 */
		  as_fatal ("failed sanity check.");	/* RMS prefers abort() to any message. */
		}
	    }
	  else
	    {
	      ++count;		/* '.' doesn't alter digits used count. */
	    }			/* if valid digit */
	}			/* for each digit */


      /*
		 * Digits_binary_low[] properly encodes the value of the digits.
		 * Forget about any high-order littlenums that are 0.
		 */
      while (digits_binary_low[size_of_digits_in_littlenums - 1] == 0
	     && size_of_digits_in_littlenums >= 2)
	size_of_digits_in_littlenums--;

      digits_flonum.low = digits_binary_low;
      digits_flonum.high = digits_binary_low + size_of_digits_in_littlenums - 1;
      digits_flonum.leader = digits_flonum.high;
      digits_flonum.exponent = 0;
      /*
		 * The value of digits_flonum . sign should not be important.
		 * We have already decided the output's sign.
		 * We trust that the sign won't influence the other parts of the number!
		 * So we give it a value for these reasons:
		 * (1) courtesy to humans reading/debugging
		 *     these numbers so they don't get excited about strange values
		 * (2) in future there may be more meaning attached to sign,
		 *     and what was
		 *     harmless noise may become disruptive, ill-conditioned (or worse)
		 *     input.
		 */
      digits_flonum.sign = '+';

      {
	/*
			 * Compute the mantssa (& exponent) of the power of 10.
			 * If sucessful, then multiply the power of 10 by the digits
			 * giving return_binary_mantissa and return_binary_exponent.
			 */

	LITTLENUM_TYPE *power_binary_low;
	int decimal_exponent_is_negative;
	/* This refers to the "-56" in "12.34E-56". */
	/* FALSE: decimal_exponent is positive (or 0) */
	/* TRUE:  decimal_exponent is negative */
	FLONUM_TYPE temporary_flonum;
	LITTLENUM_TYPE *temporary_binary_low;
	unsigned int size_of_power_in_littlenums;
	unsigned int size_of_power_in_chars;

	size_of_power_in_littlenums = precision;
	/* Precision has a built-in fudge factor so we get a few guard bits. */

	decimal_exponent_is_negative = decimal_exponent < 0;
	if (decimal_exponent_is_negative)
	  {
	    decimal_exponent = -decimal_exponent;
	  }

	/* From now on: the decimal exponent is > 0. Its sign is seperate. */

	size_of_power_in_chars = size_of_power_in_littlenums
	  * sizeof (LITTLENUM_TYPE) + 2;

	power_binary_low = (LITTLENUM_TYPE *) alloca (size_of_power_in_chars);
	temporary_binary_low = (LITTLENUM_TYPE *) alloca (size_of_power_in_chars);
	memset ((char *) power_binary_low, '\0', size_of_power_in_chars);
	*power_binary_low = 1;
	power_of_10_flonum.exponent = 0;
	power_of_10_flonum.low = power_binary_low;
	power_of_10_flonum.leader = power_binary_low;
	power_of_10_flonum.high = power_binary_low + size_of_power_in_littlenums - 1;
	power_of_10_flonum.sign = '+';
	temporary_flonum.low = temporary_binary_low;
	temporary_flonum.high = temporary_binary_low + size_of_power_in_littlenums - 1;
	/*
			 * (power) == 1.
			 * Space for temporary_flonum allocated.
			 */

	/*
			 * ...
			 *
			 * WHILE	more bits
			 * DO	find next bit (with place value)
			 *	multiply into power mantissa
			 * OD
			 */
	{
	  int place_number_limit;
	  /* Any 10^(2^n) whose "n" exceeds this */
	  /* value will fall off the end of */
	  /* flonum_XXXX_powers_of_ten[]. */
	  int place_number;
	  const FLONUM_TYPE *multiplicand;	/* -> 10^(2^n) */

	  place_number_limit = table_size_of_flonum_powers_of_ten;

	  multiplicand = (decimal_exponent_is_negative
			  ? flonum_negative_powers_of_ten
			  : flonum_positive_powers_of_ten);

	  for (place_number = 1;/* Place value of this bit of exponent. */
	       decimal_exponent;/* Quit when no more 1 bits in exponent. */
	       decimal_exponent >>= 1, place_number++)
	    {
	      if (decimal_exponent & 1)
		{
		  if (place_number > place_number_limit)
		    {
		      /*
							 * The decimal exponent has a magnitude so great that
							 * our tables can't help us fragment it.  Although this
							 * routine is in error because it can't imagine a
							 * number that big, signal an error as if it is the
							 * user's fault for presenting such a big number.
							 */
		      return_value = ERROR_EXPONENT_OVERFLOW;
		      /*
							 * quit out of loop gracefully
							 */
		      decimal_exponent = 0;
		    }
		  else
		    {
#ifdef TRACE
		      printf ("before multiply, place_number = %d., power_of_10_flonum:\n",
			      place_number);

		      flonum_print (&power_of_10_flonum);
		      (void) putchar ('\n');
#endif
		      flonum_multip (multiplicand + place_number,
				     &power_of_10_flonum, &temporary_flonum);
		      flonum_copy (&temporary_flonum, &power_of_10_flonum);
		    }		/* If this bit of decimal_exponent was computable.*/
		}		/* If this bit of decimal_exponent was set. */
	    }			/* For each bit of binary representation of exponent */
#ifdef TRACE
	  printf (" after computing power_of_10_flonum: ");
	  flonum_print (&power_of_10_flonum);
	  (void) putchar ('\n');
#endif
	}

      }

      /*
		 * power_of_10_flonum is power of ten in binary (mantissa) , (exponent).
		 * It may be the number 1, in which case we don't NEED to multiply.
		 *
		 * Multiply (decimal digits) by power_of_10_flonum.
		 */

      flonum_multip (&power_of_10_flonum, &digits_flonum, address_of_generic_floating_point_number);
      /* Assert sign of the number we made is '+'. */
      address_of_generic_floating_point_number->sign = digits_sign_char;

    }				/* If we had any significant digits. */
  return (return_value);
}				/* atof_generic () */

/* end of atof_generic.c */
