mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			1299 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1299 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
/* Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
 | 
						|
   Contributed by Andy Vaught
 | 
						|
 | 
						|
This file is part of the GNU Fortran 95 runtime library (libgfortran).
 | 
						|
 | 
						|
Libgfortran 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.
 | 
						|
 | 
						|
Libgfortran 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 Libgfortran; see the file COPYING.  If not, write to
 | 
						|
the Free Software Foundation, 59 Temple Place - Suite 330,
 | 
						|
Boston, MA 02111-1307, USA.  */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
#include <string.h>
 | 
						|
#include <float.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include "libgfortran.h"
 | 
						|
#include "io.h"
 | 
						|
 | 
						|
 | 
						|
#define star_fill(p, n) memset(p, '*', n)
 | 
						|
 | 
						|
 | 
						|
typedef enum
 | 
						|
{ SIGN_NONE, SIGN_MINUS, SIGN_PLUS }
 | 
						|
sign_t;
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
write_a (fnode * f, const char *source, int len)
 | 
						|
{
 | 
						|
  int wlen;
 | 
						|
  char *p;
 | 
						|
 | 
						|
  wlen = f->u.string.length < 0 ? len : f->u.string.length;
 | 
						|
 | 
						|
  p = write_block (wlen);
 | 
						|
  if (p == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  if (wlen < len)
 | 
						|
    memcpy (p, source, wlen);
 | 
						|
  else
 | 
						|
    {
 | 
						|
      memset (p, ' ', wlen - len);
 | 
						|
      memcpy (p + wlen - len, source, len);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int64_t
 | 
						|
extract_int (const void *p, int len)
 | 
						|
{
 | 
						|
  int64_t i = 0;
 | 
						|
 | 
						|
  if (p == NULL)
 | 
						|
    return i;
 | 
						|
 | 
						|
  switch (len)
 | 
						|
    {
 | 
						|
    case 1:
 | 
						|
      i = *((const int8_t *) p);
 | 
						|
      break;
 | 
						|
    case 2:
 | 
						|
      i = *((const int16_t *) p);
 | 
						|
      break;
 | 
						|
    case 4:
 | 
						|
      i = *((const int32_t *) p);
 | 
						|
      break;
 | 
						|
    case 8:
 | 
						|
      i = *((const int64_t *) p);
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      internal_error ("bad integer kind");
 | 
						|
    }
 | 
						|
 | 
						|
  return i;
 | 
						|
}
 | 
						|
 | 
						|
static double
 | 
						|
extract_real (const void *p, int len)
 | 
						|
{
 | 
						|
  double i = 0.0;
 | 
						|
  switch (len)
 | 
						|
    {
 | 
						|
    case 4:
 | 
						|
      i = *((const float *) p);
 | 
						|
      break;
 | 
						|
    case 8:
 | 
						|
      i = *((const double *) p);
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      internal_error ("bad real kind");
 | 
						|
    }
 | 
						|
  return i;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Given a flag that indicate if a value is negative or not, return a
 | 
						|
   sign_t that gives the sign that we need to produce.  */
 | 
						|
 | 
						|
static sign_t
 | 
						|
calculate_sign (int negative_flag)
 | 
						|
{
 | 
						|
  sign_t s = SIGN_NONE;
 | 
						|
 | 
						|
  if (negative_flag)
 | 
						|
    s = SIGN_MINUS;
 | 
						|
  else
 | 
						|
    switch (g.sign_status)
 | 
						|
      {
 | 
						|
      case SIGN_SP:
 | 
						|
	s = SIGN_PLUS;
 | 
						|
	break;
 | 
						|
      case SIGN_SS:
 | 
						|
	s = SIGN_NONE;
 | 
						|
	break;
 | 
						|
      case SIGN_S:
 | 
						|
	s = options.optional_plus ? SIGN_PLUS : SIGN_NONE;
 | 
						|
	break;
 | 
						|
      }
 | 
						|
 | 
						|
  return s;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Returns the value of 10**d.  */
 | 
						|
 | 
						|
static double
 | 
						|
calculate_exp (int d)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  double r = 1.0;
 | 
						|
 | 
						|
  for (i = 0; i< (d >= 0 ? d : -d); i++)
 | 
						|
    r *= 10;
 | 
						|
 | 
						|
  r = (d >= 0) ? r : 1.0 / r;
 | 
						|
 | 
						|
  return r;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Generate corresponding I/O format for FMT_G output.
 | 
						|
   The rules to translate FMT_G to FMT_E or FMT_F from DEC fortran
 | 
						|
   LRM (table 11-2, Chapter 11, "I/O Formatting", P11-25) is:
 | 
						|
 | 
						|
   Data Magnitude                              Equivalent Conversion
 | 
						|
   0< m < 0.1-0.5*10**(-d-1)                   Ew.d[Ee]
 | 
						|
   m = 0                                       F(w-n).(d-1), n' '
 | 
						|
   0.1-0.5*10**(-d-1)<= m < 1-0.5*10**(-d)     F(w-n).d, n' '
 | 
						|
   1-0.5*10**(-d)<= m < 10-0.5*10**(-d+1)      F(w-n).(d-1), n' '
 | 
						|
   10-0.5*10**(-d+1)<= m < 100-0.5*10**(-d+2)  F(w-n).(d-2), n' '
 | 
						|
   ................                           ..........
 | 
						|
   10**(d-1)-0.5*10**(-1)<= m <10**d-0.5       F(w-n).0,n(' ')
 | 
						|
   m >= 10**d-0.5                              Ew.d[Ee]
 | 
						|
 | 
						|
   notes: for Gw.d ,  n' ' means 4 blanks
 | 
						|
          for Gw.dEe, n' ' means e+2 blanks  */
 | 
						|
 | 
						|
static fnode *
 | 
						|
calculate_G_format (fnode *f, double value, int len, int *num_blank)
 | 
						|
{
 | 
						|
  int e = f->u.real.e;
 | 
						|
  int d = f->u.real.d;
 | 
						|
  int w = f->u.real.w;
 | 
						|
  fnode *newf;
 | 
						|
  double m, exp_d;
 | 
						|
  int low, high, mid;
 | 
						|
  int ubound, lbound;
 | 
						|
 | 
						|
  newf = get_mem (sizeof (fnode));
 | 
						|
 | 
						|
  /* Absolute value.  */
 | 
						|
  m = (value > 0.0) ? value : -value;
 | 
						|
 | 
						|
  /* In case of the two data magnitude ranges,
 | 
						|
     generate E editing, Ew.d[Ee].  */
 | 
						|
  exp_d = calculate_exp (d);
 | 
						|
  if ((m > 0.0 && m < 0.1 - 0.05 / (double) exp_d)
 | 
						|
      || (m >= (double) exp_d - 0.5 ))
 | 
						|
    {
 | 
						|
      newf->format = FMT_E;
 | 
						|
      newf->u.real.w = w;
 | 
						|
      newf->u.real.d = d;
 | 
						|
      newf->u.real.e = e;
 | 
						|
      *num_blank = 0;
 | 
						|
      return newf;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Use binary search to find the data magnitude range.  */
 | 
						|
  mid = 0;
 | 
						|
  low = 0;
 | 
						|
  high = d + 1;
 | 
						|
  lbound = 0;
 | 
						|
  ubound = d + 1;
 | 
						|
 | 
						|
  while (low <= high)
 | 
						|
    {
 | 
						|
      double temp;
 | 
						|
      mid = (low + high) / 2;
 | 
						|
 | 
						|
      /* 0.1 * 10**mid - 0.5 * 10**(mid-d-1)  */
 | 
						|
      temp = 0.1 * calculate_exp (mid) - 0.5 * calculate_exp (mid - d - 1);
 | 
						|
 | 
						|
      if (m < temp)
 | 
						|
        {
 | 
						|
          ubound = mid;
 | 
						|
          if (ubound == lbound + 1)
 | 
						|
            break;
 | 
						|
          high = mid - 1;
 | 
						|
        }
 | 
						|
      else if (m > temp)
 | 
						|
        {
 | 
						|
          lbound = mid;
 | 
						|
          if (ubound == lbound + 1)
 | 
						|
            {
 | 
						|
              mid ++;
 | 
						|
              break;
 | 
						|
            }
 | 
						|
          low = mid + 1;
 | 
						|
        }
 | 
						|
      else
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Pad with blanks where the exponent would be.  */
 | 
						|
  if (e < 0)
 | 
						|
    *num_blank = 4;
 | 
						|
  else
 | 
						|
    *num_blank = e + 2;
 | 
						|
 | 
						|
  /* Generate the F editing. F(w-n).(-(mid-d-1)), n' '.  */
 | 
						|
  newf->format = FMT_F;
 | 
						|
  newf->u.real.w = f->u.real.w - *num_blank;
 | 
						|
 | 
						|
  /* Special case.  */
 | 
						|
  if (m == 0.0)
 | 
						|
    newf->u.real.d = d - 1;
 | 
						|
  else
 | 
						|
    newf->u.real.d = - (mid - d - 1);
 | 
						|
 | 
						|
  /* For F editing, the scale factor is ignored.  */
 | 
						|
  g.scale_factor = 0;
 | 
						|
  return newf;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Output a real number according to its format which is FMT_G free.  */
 | 
						|
 | 
						|
static void
 | 
						|
output_float (fnode *f, double value, int len)
 | 
						|
{
 | 
						|
  /* This must be large enough to accurately hold any value.  */ 
 | 
						|
  char buffer[32];
 | 
						|
  char *out;
 | 
						|
  char *digits;
 | 
						|
  int e;
 | 
						|
  char expchar;
 | 
						|
  format_token ft;
 | 
						|
  int w;
 | 
						|
  int d;
 | 
						|
  int edigits;
 | 
						|
  int ndigits;
 | 
						|
  /* Number of digits before the decimal point.  */
 | 
						|
  int nbefore;
 | 
						|
  /* Number of zeros after the decimal point.  */
 | 
						|
  int nzero;
 | 
						|
  /* Number of digits after the decimal point.  */
 | 
						|
  int nafter;
 | 
						|
  int leadzero;
 | 
						|
  int nblanks;
 | 
						|
  int i;
 | 
						|
  sign_t sign;
 | 
						|
 | 
						|
  ft = f->format;
 | 
						|
  w = f->u.real.w;
 | 
						|
  d = f->u.real.d;
 | 
						|
 | 
						|
  /* We should always know the field width and precision.  */
 | 
						|
  if (d < 0)
 | 
						|
    internal_error ("Uspecified precision");
 | 
						|
 | 
						|
  /* Use sprintf to print the number in the format +D.DDDDe+ddd
 | 
						|
     For an N digit exponent, this gives us (32-6)-N digits after the
 | 
						|
     decimal point, plus another one before the decimal point.  */
 | 
						|
  sign = calculate_sign (value < 0.0);
 | 
						|
  if (value < 0)
 | 
						|
    value = -value;
 | 
						|
 | 
						|
  /* Printf always prints at least two exponent digits.  */
 | 
						|
  if (value == 0)
 | 
						|
    edigits = 2;
 | 
						|
  else
 | 
						|
    {
 | 
						|
      edigits = 1 + (int) log10 (fabs(log10 (value)));
 | 
						|
      if (edigits < 2)
 | 
						|
	edigits = 2;
 | 
						|
    }
 | 
						|
  
 | 
						|
  if (ft == FMT_F || ft == FMT_EN
 | 
						|
      || ((ft == FMT_D || ft == FMT_E) && g.scale_factor != 0))
 | 
						|
    {
 | 
						|
      /* Always convert at full precision to avoid double rounding.  */
 | 
						|
      ndigits = 27 - edigits;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* We know the number of digits, so can let printf do the rounding
 | 
						|
	 for us.  */
 | 
						|
      if (ft == FMT_ES)
 | 
						|
	ndigits = d + 1;
 | 
						|
      else
 | 
						|
	ndigits = d;
 | 
						|
      if (ndigits > 27 - edigits)
 | 
						|
	ndigits = 27 - edigits;
 | 
						|
    }
 | 
						|
 | 
						|
  sprintf (buffer, "%+-#31.*e", ndigits - 1, value);
 | 
						|
  
 | 
						|
  /* Check the resulting string has punctuation in the correct places.  */
 | 
						|
  if (buffer[2] != '.' || buffer[ndigits + 2] != 'e')
 | 
						|
      internal_error ("printf is broken");
 | 
						|
 | 
						|
  /* Read the exponent back in.  */
 | 
						|
  e = atoi (&buffer[ndigits + 3]) + 1;
 | 
						|
 | 
						|
  /* Make sure zero comes out as 0.0e0.  */
 | 
						|
  if (value == 0.0)
 | 
						|
    e = 0;
 | 
						|
 | 
						|
  /* Normalize the fractional component.  */
 | 
						|
  buffer[2] = buffer[1];
 | 
						|
  digits = &buffer[2];
 | 
						|
 | 
						|
  /* Figure out where to place the decimal point.  */
 | 
						|
  switch (ft)
 | 
						|
    {
 | 
						|
    case FMT_F:
 | 
						|
      nbefore = e + g.scale_factor;
 | 
						|
      if (nbefore < 0)
 | 
						|
	{
 | 
						|
	  nzero = -nbefore;
 | 
						|
	  if (nzero > d)
 | 
						|
	    nzero = d;
 | 
						|
	  nafter = d - nzero;
 | 
						|
	  nbefore = 0;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  nzero = 0;
 | 
						|
	  nafter = d;
 | 
						|
	}
 | 
						|
      expchar = 0;
 | 
						|
      break;
 | 
						|
 | 
						|
    case FMT_E:
 | 
						|
    case FMT_D:
 | 
						|
      i = g.scale_factor;
 | 
						|
      e -= i;
 | 
						|
      if (i < 0)
 | 
						|
	{
 | 
						|
	  nbefore = 0;
 | 
						|
	  nzero = -i;
 | 
						|
	  nafter = d + i;
 | 
						|
	}
 | 
						|
      else if (i > 0)
 | 
						|
	{
 | 
						|
	  nbefore = i;
 | 
						|
	  nzero = 0;
 | 
						|
	  nafter = (d - i) + 1;
 | 
						|
	}
 | 
						|
      else /* i == 0 */
 | 
						|
	{
 | 
						|
	  nbefore = 0;
 | 
						|
	  nzero = 0;
 | 
						|
	  nafter = d;
 | 
						|
	}
 | 
						|
 | 
						|
      if (ft = FMT_E)
 | 
						|
	expchar = 'E';
 | 
						|
      else
 | 
						|
	expchar = 'D';
 | 
						|
      break;
 | 
						|
 | 
						|
    case FMT_EN:
 | 
						|
      /* The exponent must be a multiple of three, with 1-3 digits before
 | 
						|
	 the decimal point.  */
 | 
						|
      e--;
 | 
						|
      if (e >= 0)
 | 
						|
	nbefore = e % 3;
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  nbefore = (-e) % 3;
 | 
						|
	  if (nbefore != 0)
 | 
						|
	    nbefore = 3 - nbefore;
 | 
						|
	}
 | 
						|
      e -= nbefore;
 | 
						|
      nbefore++;
 | 
						|
      nzero = 0;
 | 
						|
      nafter = d;
 | 
						|
      expchar = 'E';
 | 
						|
      break;
 | 
						|
 | 
						|
    case FMT_ES:
 | 
						|
      e--;
 | 
						|
      nbefore = 1;
 | 
						|
      nzero = 0;
 | 
						|
      nafter = d;
 | 
						|
      expchar = 'E';
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      /* Should never happen.  */
 | 
						|
      internal_error ("Unexpected format token");
 | 
						|
    }
 | 
						|
 | 
						|
  /* Round the value.  */
 | 
						|
  if (nbefore + nafter == 0)
 | 
						|
    ndigits = 0;
 | 
						|
  else if (nbefore + nafter < ndigits)
 | 
						|
    {
 | 
						|
      ndigits = nbefore + nafter;
 | 
						|
      i = ndigits;
 | 
						|
      if (digits[i] >= '5')
 | 
						|
	{
 | 
						|
	  /* Propagate the carry.  */
 | 
						|
	  for (i--; i >= 0; i--)
 | 
						|
	    {
 | 
						|
	      if (digits[i] != '9')
 | 
						|
		{
 | 
						|
		  digits[i]++;
 | 
						|
		  break;
 | 
						|
		}
 | 
						|
	      digits[i] = '0';
 | 
						|
	    }
 | 
						|
 | 
						|
	  if (i < 0)
 | 
						|
	    {
 | 
						|
	      /* The carry overflowed.  Fortunately we have some spare space
 | 
						|
		 at the start of the buffer.  We may discard some digits, but
 | 
						|
		 this is ok because we already know they are zero.  */
 | 
						|
	      digits--;
 | 
						|
	      digits[0] = '1';
 | 
						|
	      if (ft == FMT_F)
 | 
						|
		{
 | 
						|
		  if (nzero > 0)
 | 
						|
		    {
 | 
						|
		      nzero--;
 | 
						|
		      nafter++;
 | 
						|
		    }
 | 
						|
		  else
 | 
						|
		    nbefore++;
 | 
						|
		}
 | 
						|
	      else if (ft == FMT_EN)
 | 
						|
		{
 | 
						|
		  nbefore++;
 | 
						|
		  if (nbefore == 4)
 | 
						|
		    {
 | 
						|
		      nbefore = 1;
 | 
						|
		      e += 3;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		e++;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /* Calculate the format of the exponent field.  */
 | 
						|
  if (expchar)
 | 
						|
    {
 | 
						|
      edigits = 1;
 | 
						|
      for (i = abs (e); i >= 10; i /= 10)
 | 
						|
	edigits++;
 | 
						|
      
 | 
						|
      if (f->u.real.e < 0)
 | 
						|
	{
 | 
						|
	  /* Width not specified.  Must be no more than 3 digits.  */
 | 
						|
	  if (e > 999 || e < -999)
 | 
						|
	    edigits = -1;
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      edigits = 4;
 | 
						|
	      if (e > 99 || e < -99)
 | 
						|
		expchar = ' ';
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* Exponent width specified, check it is wide enough.  */
 | 
						|
	  if (edigits > f->u.real.e)
 | 
						|
	    edigits = -1;
 | 
						|
	  else
 | 
						|
	    edigits = f->u.real.e + 2;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    edigits = 0;
 | 
						|
 | 
						|
  /* Pick a field size if none was specified.  */
 | 
						|
  if (w <= 0)
 | 
						|
    w = nbefore + nzero + nafter + 2;
 | 
						|
 | 
						|
  /* Create the ouput buffer.  */
 | 
						|
  out = write_block (w);
 | 
						|
  if (out == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  /* Zero values always output as positive, even if the value was negative
 | 
						|
     before rounding.  */
 | 
						|
  for (i = 0; i < ndigits; i++)
 | 
						|
    {
 | 
						|
      if (digits[i] != '0')
 | 
						|
	break;
 | 
						|
    }
 | 
						|
  if (i == ndigits)
 | 
						|
    sign = calculate_sign (0);
 | 
						|
 | 
						|
  /* Work out how much padding is needed.  */
 | 
						|
  nblanks = w - (nbefore + nzero + nafter + edigits + 1);
 | 
						|
  if (sign != SIGN_NONE)
 | 
						|
    nblanks--;
 | 
						|
  
 | 
						|
  /* Check the value fits in the specified field width.  */
 | 
						|
  if (nblanks < 0 || edigits == -1)
 | 
						|
    {
 | 
						|
      star_fill (out, w);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  /* See if we have space for a zero before the decimal point.  */
 | 
						|
  if (nbefore == 0 && nblanks > 0)
 | 
						|
    {
 | 
						|
      leadzero = 1;
 | 
						|
      nblanks--;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    leadzero = 0;
 | 
						|
 | 
						|
  /* Padd to full field width.  */
 | 
						|
  if (nblanks > 0)
 | 
						|
    {
 | 
						|
      memset (out, ' ', nblanks);
 | 
						|
      out += nblanks;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Output the initial sign (if any).  */
 | 
						|
  if (sign == SIGN_PLUS)
 | 
						|
    *(out++) = '+';
 | 
						|
  else if (sign == SIGN_MINUS)
 | 
						|
    *(out++) = '-';
 | 
						|
 | 
						|
  /* Output an optional leading zero.  */
 | 
						|
  if (leadzero)
 | 
						|
    *(out++) = '0';
 | 
						|
 | 
						|
  /* Output the part before the decimal point, padding with zeros.  */
 | 
						|
  if (nbefore > 0)
 | 
						|
    {
 | 
						|
      if (nbefore > ndigits)
 | 
						|
	i = ndigits;
 | 
						|
      else
 | 
						|
	i = nbefore;
 | 
						|
 | 
						|
      memcpy (out, digits, i);
 | 
						|
      while (i < nbefore)
 | 
						|
	out[i++] = '0';
 | 
						|
 | 
						|
      digits += i;
 | 
						|
      ndigits -= i;
 | 
						|
      out += nbefore;
 | 
						|
    }
 | 
						|
  /* Output the decimal point.  */
 | 
						|
  *(out++) = '.';
 | 
						|
 | 
						|
  /* Output leading zeros after the decimal point.  */
 | 
						|
  if (nzero > 0)
 | 
						|
    {
 | 
						|
      for (i = 0; i < nzero; i++)
 | 
						|
	*(out++) = '0';
 | 
						|
    }
 | 
						|
 | 
						|
  /* Output digits after the decimal point, padding with zeros.  */
 | 
						|
  if (nafter > 0)
 | 
						|
    {
 | 
						|
      if (nafter > ndigits)
 | 
						|
	i = ndigits;
 | 
						|
      else
 | 
						|
	i = nafter;
 | 
						|
 | 
						|
      memcpy (out, digits, i);
 | 
						|
      while (i < nafter)
 | 
						|
	out[i++] = '0';
 | 
						|
 | 
						|
      digits += i;
 | 
						|
      ndigits -= i;
 | 
						|
      out += nafter;
 | 
						|
    }
 | 
						|
  
 | 
						|
  /* Output the exponent.  */
 | 
						|
  if (expchar)
 | 
						|
    {
 | 
						|
      if (expchar != ' ')
 | 
						|
	{
 | 
						|
	  *(out++) = expchar;
 | 
						|
	  edigits--;
 | 
						|
	}
 | 
						|
#if HAVE_SNPRINTF
 | 
						|
      snprintf (buffer, 32, "%+0*d", edigits, e);
 | 
						|
#else
 | 
						|
      sprintf (buffer, "%+0*d", edigits, e);
 | 
						|
#endif
 | 
						|
      memcpy (out, buffer, edigits);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
write_l (fnode * f, char *source, int len)
 | 
						|
{
 | 
						|
  char *p;
 | 
						|
  int64_t n;
 | 
						|
 | 
						|
  p = write_block (f->u.w);
 | 
						|
  if (p == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  memset (p, ' ', f->u.w - 1);
 | 
						|
  n = extract_int (source, len);
 | 
						|
  p[f->u.w - 1] = (n) ? 'T' : 'F';
 | 
						|
}
 | 
						|
 | 
						|
/* Output a real number according to its format.  */
 | 
						|
 | 
						|
static void
 | 
						|
write_float (fnode *f, const char *source, int len)
 | 
						|
{
 | 
						|
  double n;
 | 
						|
  int nb =0, res;
 | 
						|
  char * p, fin;
 | 
						|
  fnode *f2 = NULL;
 | 
						|
 | 
						|
  n = extract_real (source, len);
 | 
						|
 | 
						|
  if (f->format != FMT_B && f->format != FMT_O && f->format != FMT_Z)
 | 
						|
    {
 | 
						|
      res = isfinite (n);
 | 
						|
      if (res == 0)
 | 
						|
	{
 | 
						|
	  nb =  f->u.real.w;
 | 
						|
	  p = write_block (nb);
 | 
						|
	  if (nb < 3)
 | 
						|
	    {
 | 
						|
	      memset (p, '*',nb);
 | 
						|
	      return;
 | 
						|
	    }
 | 
						|
 | 
						|
	  memset(p, ' ', nb);
 | 
						|
	  res = !isnan (n); 
 | 
						|
	  if (res != 0)
 | 
						|
	    {
 | 
						|
	      if (signbit(n))   
 | 
						|
		fin = '-';
 | 
						|
	      else
 | 
						|
		fin = '+';
 | 
						|
 | 
						|
	      if (nb > 7)
 | 
						|
		memcpy(p + nb - 8, "Infinity", 8); 
 | 
						|
	      else
 | 
						|
		memcpy(p + nb - 3, "Inf", 3);
 | 
						|
	      if (nb < 8 && nb > 3)
 | 
						|
		p[nb - 4] = fin;
 | 
						|
	      else if (nb > 8)
 | 
						|
		p[nb - 9] = fin; 
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    memcpy(p + nb - 3, "NaN", 3);
 | 
						|
	  return;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (f->format != FMT_G)
 | 
						|
    {
 | 
						|
      output_float (f, n, len);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      f2 = calculate_G_format(f, n, len, &nb);
 | 
						|
      output_float (f2, n, len);
 | 
						|
      if (f2 != NULL)
 | 
						|
        free_mem(f2);
 | 
						|
 | 
						|
      if (nb > 0)
 | 
						|
        {
 | 
						|
          p = write_block (nb);
 | 
						|
          memset (p, ' ', nb);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
write_int (fnode *f, const char *source, int len, char *(*conv) (uint64_t))
 | 
						|
{
 | 
						|
  uint32_t ns =0;
 | 
						|
  uint64_t n = 0;
 | 
						|
  int w, m, digits, nzero, nblank;
 | 
						|
  char *p, *q;
 | 
						|
 | 
						|
  w = f->u.integer.w;
 | 
						|
  m = f->u.integer.m;
 | 
						|
 | 
						|
  n = extract_int (source, len);
 | 
						|
 | 
						|
  /* Special case:  */
 | 
						|
 | 
						|
  if (m == 0 && n == 0)
 | 
						|
    {
 | 
						|
      if (w == 0)
 | 
						|
        w = 1;
 | 
						|
 | 
						|
      p = write_block (w);
 | 
						|
      if (p == NULL)
 | 
						|
        return;
 | 
						|
 | 
						|
      memset (p, ' ', w);
 | 
						|
      goto done;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
  if (len < 8)
 | 
						|
     {
 | 
						|
       ns = n;
 | 
						|
       q = conv (ns);
 | 
						|
     }
 | 
						|
  else
 | 
						|
      q = conv (n);
 | 
						|
 | 
						|
  digits = strlen (q);
 | 
						|
 | 
						|
  /* Select a width if none was specified.  The idea here is to always
 | 
						|
     print something.  */
 | 
						|
 | 
						|
  if (w == 0)
 | 
						|
    w = ((digits < m) ? m : digits);
 | 
						|
 | 
						|
  p = write_block (w);
 | 
						|
  if (p == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  nzero = 0;
 | 
						|
  if (digits < m)
 | 
						|
    nzero = m - digits;
 | 
						|
 | 
						|
  /* See if things will work.  */
 | 
						|
 | 
						|
  nblank = w - (nzero + digits);
 | 
						|
 | 
						|
  if (nblank < 0)
 | 
						|
    {
 | 
						|
      star_fill (p, w);
 | 
						|
      goto done;
 | 
						|
    }
 | 
						|
 | 
						|
  memset (p, ' ', nblank);
 | 
						|
  p += nblank;
 | 
						|
 | 
						|
  memset (p, '0', nzero);
 | 
						|
  p += nzero;
 | 
						|
 | 
						|
  memcpy (p, q, digits);
 | 
						|
 | 
						|
 done:
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
write_decimal (fnode *f, const char *source, int len, char *(*conv) (int64_t))
 | 
						|
{
 | 
						|
  int64_t n = 0;
 | 
						|
  int w, m, digits, nsign, nzero, nblank;
 | 
						|
  char *p, *q;
 | 
						|
  sign_t sign;
 | 
						|
 | 
						|
  w = f->u.integer.w;
 | 
						|
  m = f->u.integer.m;
 | 
						|
 | 
						|
  n = extract_int (source, len);
 | 
						|
 | 
						|
  /* Special case:  */
 | 
						|
 | 
						|
  if (m == 0 && n == 0)
 | 
						|
    {
 | 
						|
      if (w == 0)
 | 
						|
        w = 1;
 | 
						|
 | 
						|
      p = write_block (w);
 | 
						|
      if (p == NULL)
 | 
						|
        return;
 | 
						|
 | 
						|
      memset (p, ' ', w);
 | 
						|
      goto done;
 | 
						|
    }
 | 
						|
 | 
						|
  sign = calculate_sign (n < 0);
 | 
						|
  if (n < 0)
 | 
						|
    n = -n;
 | 
						|
 | 
						|
  nsign = sign == SIGN_NONE ? 0 : 1;
 | 
						|
  q = conv (n);
 | 
						|
 | 
						|
  digits = strlen (q);
 | 
						|
 | 
						|
  /* Select a width if none was specified.  The idea here is to always
 | 
						|
     print something.  */
 | 
						|
 | 
						|
  if (w == 0)
 | 
						|
    w = ((digits < m) ? m : digits) + nsign;
 | 
						|
 | 
						|
  p = write_block (w);
 | 
						|
  if (p == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  nzero = 0;
 | 
						|
  if (digits < m)
 | 
						|
    nzero = m - digits;
 | 
						|
 | 
						|
  /* See if things will work.  */
 | 
						|
 | 
						|
  nblank = w - (nsign + nzero + digits);
 | 
						|
 | 
						|
  if (nblank < 0)
 | 
						|
    {
 | 
						|
      star_fill (p, w);
 | 
						|
      goto done;
 | 
						|
    }
 | 
						|
 | 
						|
  memset (p, ' ', nblank);
 | 
						|
  p += nblank;
 | 
						|
 | 
						|
  switch (sign)
 | 
						|
    {
 | 
						|
    case SIGN_PLUS:
 | 
						|
      *p++ = '+';
 | 
						|
      break;
 | 
						|
    case SIGN_MINUS:
 | 
						|
      *p++ = '-';
 | 
						|
      break;
 | 
						|
    case SIGN_NONE:
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  memset (p, '0', nzero);
 | 
						|
  p += nzero;
 | 
						|
 | 
						|
  memcpy (p, q, digits);
 | 
						|
 | 
						|
 done:
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Convert unsigned octal to ascii.  */
 | 
						|
 | 
						|
static char *
 | 
						|
otoa (uint64_t n)
 | 
						|
{
 | 
						|
  char *p;
 | 
						|
 | 
						|
  if (n == 0)
 | 
						|
    {
 | 
						|
      scratch[0] = '0';
 | 
						|
      scratch[1] = '\0';
 | 
						|
      return scratch;
 | 
						|
    }
 | 
						|
 | 
						|
  p = scratch + sizeof (SCRATCH_SIZE) - 1;
 | 
						|
  *p-- = '\0';
 | 
						|
 | 
						|
  while (n != 0)
 | 
						|
    {
 | 
						|
      *p = '0' + (n & 7);
 | 
						|
      p -- ;
 | 
						|
      n >>= 3;
 | 
						|
    }
 | 
						|
 | 
						|
  return ++p;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Convert unsigned binary to ascii.  */
 | 
						|
 | 
						|
static char *
 | 
						|
btoa (uint64_t n)
 | 
						|
{
 | 
						|
  char *p;
 | 
						|
 | 
						|
  if (n == 0)
 | 
						|
    {
 | 
						|
      scratch[0] = '0';
 | 
						|
      scratch[1] = '\0';
 | 
						|
      return scratch;
 | 
						|
    }
 | 
						|
 | 
						|
  p = scratch + sizeof (SCRATCH_SIZE) - 1;
 | 
						|
  *p-- = '\0';
 | 
						|
 | 
						|
  while (n != 0)
 | 
						|
    {
 | 
						|
      *p-- = '0' + (n & 1);
 | 
						|
      n >>= 1;
 | 
						|
    }
 | 
						|
 | 
						|
  return ++p;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
write_i (fnode * f, const char *p, int len)
 | 
						|
{
 | 
						|
  write_decimal (f, p, len, (void *) gfc_itoa);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
write_b (fnode * f, const char *p, int len)
 | 
						|
{
 | 
						|
  write_int (f, p, len, btoa);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
write_o (fnode * f, const char *p, int len)
 | 
						|
{
 | 
						|
  write_int (f, p, len, otoa);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
write_z (fnode * f, const char *p, int len)
 | 
						|
{
 | 
						|
  write_int (f, p, len, xtoa);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
write_d (fnode *f, const char *p, int len)
 | 
						|
{
 | 
						|
  write_float (f, p, len);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
write_e (fnode *f, const char *p, int len)
 | 
						|
{
 | 
						|
  write_float (f, p, len);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
write_f (fnode *f, const char *p, int len)
 | 
						|
{
 | 
						|
  write_float (f, p, len);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
write_en (fnode *f, const char *p, int len)
 | 
						|
{
 | 
						|
  write_float (f, p, len);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
write_es (fnode *f, const char *p, int len)
 | 
						|
{
 | 
						|
  write_float (f, p, len);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Take care of the X/TR descriptor.  */
 | 
						|
 | 
						|
void
 | 
						|
write_x (fnode * f)
 | 
						|
{
 | 
						|
  char *p;
 | 
						|
 | 
						|
  p = write_block (f->u.n);
 | 
						|
  if (p == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  memset (p, ' ', f->u.n);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* List-directed writing.  */
 | 
						|
 | 
						|
 | 
						|
/* Write a single character to the output.  Returns nonzero if
 | 
						|
   something goes wrong.  */
 | 
						|
 | 
						|
static int
 | 
						|
write_char (char c)
 | 
						|
{
 | 
						|
  char *p;
 | 
						|
 | 
						|
  p = write_block (1);
 | 
						|
  if (p == NULL)
 | 
						|
    return 1;
 | 
						|
 | 
						|
  *p = c;
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Write a list-directed logical value.  */
 | 
						|
 | 
						|
static void
 | 
						|
write_logical (const char *source, int length)
 | 
						|
{
 | 
						|
  write_char (extract_int (source, length) ? 'T' : 'F');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Write a list-directed integer value.  */
 | 
						|
 | 
						|
static void
 | 
						|
write_integer (const char *source, int length)
 | 
						|
{
 | 
						|
  char *p;
 | 
						|
  const char *q;
 | 
						|
  int digits;
 | 
						|
  int width;
 | 
						|
 | 
						|
  q = gfc_itoa (extract_int (source, length));
 | 
						|
 | 
						|
  switch (length)
 | 
						|
    {
 | 
						|
    case 1:
 | 
						|
      width = 4;
 | 
						|
      break;
 | 
						|
 | 
						|
    case 2:
 | 
						|
      width = 6;
 | 
						|
      break;
 | 
						|
 | 
						|
    case 4:
 | 
						|
      width = 11;
 | 
						|
      break;
 | 
						|
 | 
						|
    case 8:
 | 
						|
      width = 20;
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      width = 0;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  digits = strlen (q);
 | 
						|
 | 
						|
  if(width < digits )
 | 
						|
    width = digits ;
 | 
						|
  p = write_block (width) ;
 | 
						|
 | 
						|
  memset(p ,' ', width - digits) ;
 | 
						|
  memcpy (p + width - digits, q, digits);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Write a list-directed string.  We have to worry about delimiting
 | 
						|
   the strings if the file has been opened in that mode.  */
 | 
						|
 | 
						|
static void
 | 
						|
write_character (const char *source, int length)
 | 
						|
{
 | 
						|
  int i, extra;
 | 
						|
  char *p, d;
 | 
						|
 | 
						|
  switch (current_unit->flags.delim)
 | 
						|
    {
 | 
						|
    case DELIM_APOSTROPHE:
 | 
						|
      d = '\'';
 | 
						|
      break;
 | 
						|
    case DELIM_QUOTE:
 | 
						|
      d = '"';
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      d = ' ';
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  if (d == ' ')
 | 
						|
    extra = 0;
 | 
						|
  else
 | 
						|
    {
 | 
						|
      extra = 2;
 | 
						|
 | 
						|
      for (i = 0; i < length; i++)
 | 
						|
	if (source[i] == d)
 | 
						|
	  extra++;
 | 
						|
    }
 | 
						|
 | 
						|
  p = write_block (length + extra);
 | 
						|
  if (p == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  if (d == ' ')
 | 
						|
    memcpy (p, source, length);
 | 
						|
  else
 | 
						|
    {
 | 
						|
      *p++ = d;
 | 
						|
 | 
						|
      for (i = 0; i < length; i++)
 | 
						|
	{
 | 
						|
	  *p++ = source[i];
 | 
						|
	  if (source[i] == d)
 | 
						|
	    *p++ = d;
 | 
						|
	}
 | 
						|
 | 
						|
      *p = d;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Output a real number with default format.
 | 
						|
   This is 1PG14.7E2 for REAL(4) and 1PG23.15E3 for REAL(8).  */
 | 
						|
 | 
						|
static void
 | 
						|
write_real (const char *source, int length)
 | 
						|
{
 | 
						|
  fnode f ;
 | 
						|
  int org_scale = g.scale_factor;
 | 
						|
  f.format = FMT_G;
 | 
						|
  g.scale_factor = 1;
 | 
						|
  if (length < 8)
 | 
						|
    {
 | 
						|
      f.u.real.w = 14;
 | 
						|
      f.u.real.d = 7;
 | 
						|
      f.u.real.e = 2;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      f.u.real.w = 23;
 | 
						|
      f.u.real.d = 15;
 | 
						|
      f.u.real.e = 3;
 | 
						|
    }
 | 
						|
  write_float (&f, source , length);
 | 
						|
  g.scale_factor = org_scale;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
write_complex (const char *source, int len)
 | 
						|
{
 | 
						|
  if (write_char ('('))
 | 
						|
    return;
 | 
						|
  write_real (source, len);
 | 
						|
 | 
						|
  if (write_char (','))
 | 
						|
    return;
 | 
						|
  write_real (source + len, len);
 | 
						|
 | 
						|
  write_char (')');
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Write the separator between items.  */
 | 
						|
 | 
						|
static void
 | 
						|
write_separator (void)
 | 
						|
{
 | 
						|
  char *p;
 | 
						|
 | 
						|
  p = write_block (options.separator_len);
 | 
						|
  if (p == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  memcpy (p, options.separator, options.separator_len);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Write an item with list formatting.
 | 
						|
   TODO: handle skipping to the next record correctly, particularly
 | 
						|
   with strings.  */
 | 
						|
 | 
						|
void
 | 
						|
list_formatted_write (bt type, void *p, int len)
 | 
						|
{
 | 
						|
  static int char_flag;
 | 
						|
 | 
						|
  if (current_unit == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  if (g.first_item)
 | 
						|
    {
 | 
						|
      g.first_item = 0;
 | 
						|
      char_flag = 0;
 | 
						|
      write_char (' ');
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if (type != BT_CHARACTER || !char_flag ||
 | 
						|
	  current_unit->flags.delim != DELIM_NONE)
 | 
						|
	write_separator ();
 | 
						|
    }
 | 
						|
 | 
						|
  switch (type)
 | 
						|
    {
 | 
						|
    case BT_INTEGER:
 | 
						|
      write_integer (p, len);
 | 
						|
      break;
 | 
						|
    case BT_LOGICAL:
 | 
						|
      write_logical (p, len);
 | 
						|
      break;
 | 
						|
    case BT_CHARACTER:
 | 
						|
      write_character (p, len);
 | 
						|
      break;
 | 
						|
    case BT_REAL:
 | 
						|
      write_real (p, len);
 | 
						|
      break;
 | 
						|
    case BT_COMPLEX:
 | 
						|
      write_complex (p, len);
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      internal_error ("list_formatted_write(): Bad type");
 | 
						|
    }
 | 
						|
 | 
						|
  char_flag = (type == BT_CHARACTER);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
namelist_write (void)
 | 
						|
{
 | 
						|
  namelist_info * t1, *t2;
 | 
						|
  int len,num;
 | 
						|
  void * p;
 | 
						|
 | 
						|
  num = 0;
 | 
						|
  write_character("&",1);
 | 
						|
  write_character (ioparm.namelist_name, ioparm.namelist_name_len);
 | 
						|
  write_character("\n",1);
 | 
						|
 | 
						|
  if (ionml != NULL)
 | 
						|
    {
 | 
						|
      t1 = ionml;
 | 
						|
      while (t1 != NULL)
 | 
						|
	{
 | 
						|
          num ++;
 | 
						|
          t2 = t1;
 | 
						|
          t1 = t1->next;
 | 
						|
          if (t2->var_name)
 | 
						|
            {
 | 
						|
              write_character(t2->var_name, strlen(t2->var_name));
 | 
						|
              write_character("=",1);
 | 
						|
            }
 | 
						|
          len = t2->len;
 | 
						|
          p = t2->mem_pos;
 | 
						|
          switch (t2->type)
 | 
						|
            {
 | 
						|
            case BT_INTEGER:
 | 
						|
              write_integer (p, len);
 | 
						|
              break;
 | 
						|
            case BT_LOGICAL:
 | 
						|
              write_logical (p, len);
 | 
						|
              break;
 | 
						|
            case BT_CHARACTER:
 | 
						|
              write_character (p, t2->string_length);
 | 
						|
              break;
 | 
						|
            case BT_REAL:
 | 
						|
              write_real (p, len);
 | 
						|
              break;
 | 
						|
            case BT_COMPLEX:
 | 
						|
              write_complex (p, len);
 | 
						|
              break;
 | 
						|
            default:
 | 
						|
              internal_error ("Bad type for namelist write");
 | 
						|
            }
 | 
						|
	  write_character(",",1);
 | 
						|
	  if (num > 5)
 | 
						|
	    {
 | 
						|
	      num = 0;
 | 
						|
	      write_character("\n",1);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  write_character("/",1);
 | 
						|
}
 |