/* atof_generic.c - turn a string of digits into a Flonum
  Copyright (C) 1987-2024 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 3, 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, 51 Franklin Street - Fifth Floor, Boston, MA
  02110-1301, USA.  */

#include "as.h"
#include "safe-ctype.h"
#include <limits.h>

#ifdef TRACE
static void flonum_print (const FLONUM_TYPE *);
#endif

#define ASSUME_DECIMAL_MARK_IS_DOT

/***********************************************************************\
*                                                                      *
*      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
atof_generic (/* return pointer to just AFTER number we read.  */
             char **address_of_string_pointer,
             /* At most one per number.  */
             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;         /* 0 means OK.  */
 char *first_digit;
 unsigned int number_of_digits_before_decimal;
 unsigned int number_of_digits_after_decimal;
 unsigned long decimal_exponent;
 unsigned 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;

#ifdef ASSUME_DECIMAL_MARK_IS_DOT
 gas_assert (string_of_decimal_marks[0] == '.'
         && string_of_decimal_marks[1] == 0);
#define IS_DECIMAL_MARK(c)      ((c) == '.')
#else
#define IS_DECIMAL_MARK(c)      (0 != strchr (string_of_decimal_marks, (c)))
#endif

 first_digit = *address_of_string_pointer;
 c = *first_digit;

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

 switch (first_digit[0])
   {
   case 's':
   case 'S':
   case 'q':
   case 'Q':
     if (!strncasecmp ("nan", first_digit + 1, 3))
       {
         address_of_generic_floating_point_number->sign =
           digits_sign_char == '+' ? TOUPPER (first_digit[0])
                                   : TOLOWER (first_digit[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 + 4;
         return 0;
       }
     break;

   case 'n':
   case 'N':
     if (!strncasecmp ("nan", first_digit, 3))
       {
         address_of_generic_floating_point_number->sign =
           digits_sign_char == '+' ? 0 : 'q';
         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;
       }
     break;

   case 'i':
   case 'I':
     if (!strncasecmp ("inf", first_digit, 3))
       {
         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;

         first_digit += 3;
         if (!strncasecmp ("inity", first_digit, 5))
           first_digit += 5;

         *address_of_string_pointer = first_digit;

         return 0;
       }
     break;
   }

 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 || !IS_DECIMAL_MARK (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.  */

 /* The case number_of_digits_before_decimal = 0 is handled for
    deleting zeros after decimal.  In this case the decimal mark and
    the first zero digits after decimal mark are skipped.  */
 seen_significant_digit = 0;
 unsigned long subtract_decimal_exponent = 0;

 if (c && IS_DECIMAL_MARK (c))
   {
     unsigned int zeros = 0;   /* Length of current string of zeros.  */

     if (number_of_digits_before_decimal == 0)
       /* Skip decimal mark.  */
       first_digit++;

     for (p++; (c = *p) && ISDIGIT (c); p++)
       {
         if (c == '0')
           {
             if (number_of_digits_before_decimal == 0
                 && !seen_significant_digit)
               {
                 /* Skip '0' and the decimal mark.  */
                 first_digit++;
                 subtract_decimal_exponent--;
               }
             else
               zeros++;
           }
         else
           {
             seen_significant_digit = 1;
             number_of_digits_after_decimal += 1 + zeros;
             zeros = 0;
           }
       }
   }
#else
 if (c && IS_DECIMAL_MARK (c))
   {
     for (p++;
          (((c = *p) != '\0')
           && (!c || !strchr (string_of_decimal_exponent_marks, c)));
          p++)
       {
         if (ISDIGIT (c))
           {
             /* This may be retracted below.  */
             number_of_digits_after_decimal++;

             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;
#endif

 if (flag_m68k_mri)
   {
     while (c == '_')
       c = *++p;
   }
 if (c && strchr (string_of_decimal_exponent_marks, c))
   {
     char digits_exponent_sign_char;

     c = *++p;
     if (flag_m68k_mri)
       {
         while (c == '_')
           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))
           {
             if (decimal_exponent > LONG_MAX / 10
                 || (decimal_exponent == LONG_MAX / 10
                     && c > '0' + (char) (LONG_MAX - LONG_MAX / 10 * 10)))
               return_value = ERROR_EXPONENT_OVERFLOW;
             decimal_exponent = decimal_exponent * 10 + c - '0';
           }
         else
           {
             break;
           }
       }

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

#ifndef OLD_FLOAT_READS
 /* Subtract_decimal_exponent != 0 when number_of_digits_before_decimal = 0
    and first digit after decimal is '0'.  */
 decimal_exponent += subtract_decimal_exponent;
#endif

 *address_of_string_pointer = p;

 number_of_digits_available =
   number_of_digits_before_decimal + number_of_digits_after_decimal;
 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 *temporary_binary_low = NULL;
     LITTLENUM_TYPE *power_binary_low = NULL;
     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.  */

     /* precision includes two littlenums worth of guard bits,
        so this gives us 10 decimal guard digits here.  */
     maximum_useful_digits = (precision
                              * LITTLENUM_NUMBER_OF_BITS
                              * 1000000 / 3321928
                              + 1);    /* round up.  */

     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;
       }

     /* Cast these to SIGNED LONG first, otherwise, on systems with
        LONG wider than INT (such as Alpha OSF/1), unsignedness may
        cause unexpected results.  */
     decimal_exponent += ((long) number_of_digits_before_decimal
                          - (long) number_of_digits_to_use);

     more_than_enough_bits_for_digits
       = (number_of_digits_to_use * 3321928 / 1000000 + 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 *)
       xmalloc (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"));
               }
           }
         else
           {
             ++count;          /* '.' doesn't alter digits used count.  */
           }
       }

     /*
      * 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 mantissa (& exponent) of the power of 10.
        * If successful, then multiply the power of 10 by the digits
        * giving return_binary_mantissa and return_binary_exponent.
        */

       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;
       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 = (long) decimal_exponent < 0;
       if (decimal_exponent_is_negative)
         {
           decimal_exponent = -decimal_exponent;
         }

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

       size_of_power_in_chars = size_of_power_in_littlenums
         * sizeof (LITTLENUM_TYPE) + 2;

       power_binary_low = (LITTLENUM_TYPE *) xmalloc (size_of_power_in_chars);
       temporary_binary_low = (LITTLENUM_TYPE *) xmalloc (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
#ifdef TRACE
                     printf ("multiplier:\n");
                     flonum_print (multiplicand + place_number);
                     (void) putchar ('\n');
#endif
                     flonum_multip (multiplicand + place_number,
                                    &power_of_10_flonum, &temporary_flonum);
#ifdef TRACE
                     printf ("after multiply:\n");
                     flonum_print (&temporary_flonum);
                     (void) putchar ('\n');
#endif
                     flonum_copy (&temporary_flonum, &power_of_10_flonum);
#ifdef TRACE
                     printf ("after copy:\n");
                     flonum_print (&power_of_10_flonum);
                     (void) putchar ('\n');
#endif
                   } /* 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:\n");
         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;

     free (temporary_binary_low);
     free (power_binary_low);
     free (digits_binary_low);
   }
 return return_value;
}

#ifdef TRACE
static void
flonum_print (f)
    const FLONUM_TYPE *f;
{
 LITTLENUM_TYPE *lp;
 char littlenum_format[10];
 sprintf (littlenum_format, " %%0%dx", sizeof (LITTLENUM_TYPE) * 2);
#define print_littlenum(LP)     (printf (littlenum_format, LP))
 printf ("flonum @%p %c e%ld", f, f->sign, f->exponent);
 if (f->low < f->high)
   for (lp = f->high; lp >= f->low; lp--)
     print_littlenum (*lp);
 else
   for (lp = f->low; lp <= f->high; lp++)
     print_littlenum (*lp);
 printf ("\n");
 fflush (stdout);
}
#endif

/* end of atof_generic.c */