/* Copyright (C) 1988-1991 Apple Computer, Inc.
* All Rights Reserved.
*
* Warranty Information
* Even though Apple has reviewed this software, Apple makes no warranty
* or representation, either express or implied, with respect to this
* software, its quality, accuracy, merchantability, or fitness for a
* particular purpose.  As a result, this software is provided "as is,"
* and you, its user, are assuming the entire risk as to its quality
* and accuracy.
*
* This code may be used and freely distributed as long as it includes
* this copyright notice and the warranty information.
*
*
* Motorola processors (Macintosh, Sun, Sparc, MIPS, etc)
* pack bytes from high to low (they are big-endian).
* Use the HighLow routines to match the native format
* of these machines.
*
* Intel-like machines (PCs, Sequent)
* pack bytes from low to high (the are little-endian).
* Use the LowHigh routines to match the native format
* of these machines.
*
* These routines have been tested on the following machines:
*      Apple Macintosh, MPW 3.1 C compiler
*      Apple Macintosh, THINK C compiler
*      Silicon Graphics IRIS, MIPS compiler
*      Cray X/MP and Y/MP
*      Digital Equipment VAX
*
*
* Implemented by Malcolm Slaney and Ken Turkowski.
*
* Malcolm Slaney contributions during 1988-1990 include big- and little-
* endian file I/O, conversion to and from Motorola's extended 80-bit
* floating-point format, and conversions to and from IEEE single-
* precision floating-point format.
*
* In 1991, Ken Turkowski implemented the conversions to and from
* IEEE double-precision format, added more precision to the extended
* conversions, and accommodated conversions involving +/- infinity,
* NaN's, and denormalized numbers.
*
* $Id: portableio.c,v 1.11 2001/01/07 23:47:38 markt Exp $
*/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include        <stdio.h>
#if defined(__riscos__) && defined(FPA10)
#include        "ymath.h"
#else
#include        <math.h>
#endif
#include        "portableio.h"

#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif

/****************************************************************
* Big/little-endian independent I/O routines.
****************************************************************/

/*
* It is a hoax to call this code portable-IO:
*
*   - It doesn't work on machines with CHAR_BIT != 8
*   - it also don't test this error condition
*   - otherwise it tries to handle CHAR_BIT != 8 by things like
*     masking 'putc(i&0xff,fp)'
*   - It doesn't handle EOF in any way
*   - it only works with ints with 32 or more bits
*   - It is a collection of initial buggy code with patching the known errors
*     instead of CORRECTING them!
*     For that see comments on the old Read16BitsHighLow()
*/

#ifdef KLEMM_36

signed int    ReadByte ( FILE* fp )
{
   int  result = getc (fp);
   return result == EOF  ?  0  :  (signed char) (result & 0xFF);
}

unsigned int  ReadByteUnsigned ( FILE* fp )
{
   int  result = getc (fp);
   return result == EOF  ?  0  :  (unsigned char) (result & 0xFF);
}

#else

int
ReadByte(FILE *fp)
{
       int     result;

       result = getc(fp) & 0xff;
       if (result & 0x80)
               result = result - 0x100;
       return result;
}

#endif

#ifdef KLEMM_36

int  Read16BitsLowHigh ( FILE* fp )
{
   int  low  = ReadByteUnsigned (fp);
   int  high = ReadByte         (fp);

   return (high << 8) | low;
}

#else
int
Read16BitsLowHigh(FILE *fp)
{
       int     first, second, result;

       first = 0xff & getc(fp);
       second = 0xff & getc(fp);

       result = (second << 8) + first;
#ifndef THINK_C42
       if (result & 0x8000)
               result = result - 0x10000;
#endif  /* THINK_C */
       return(result);
}
#endif


#ifdef KLEMM_36

int  Read16BitsHighLow ( FILE* fp )
{
   int  high = ReadByte         (fp);
   int  low  = ReadByteUnsigned (fp);

   return (high << 8) | low;
}

#else
int
Read16BitsHighLow(FILE *fp)
{
       int     first, second, result;

       /* Reads the High bits, the value is -128...127
        * (which gave after upscaling the -32768...+32512
        * Why this value is not converted to signed char?
        */
       first = 0xff & getc(fp);
       /* Reads the Lows bits, the value is 0...255
        * This is correct. This value gives an additional offset
        * for the High bits
        */
       second = 0xff & getc(fp);

       /* This is right */
       result = (first << 8) + second;

       /* Now we are starting to correct the nasty bug of the first instruction
        * The value of the high bits is wrong. Always. So we must correct this
        * value. This seems to be not necessary for THINK_C42. This is either
        * a 16 bit compiler with 16 bit ints (where this bug is hidden and 0x10000
        * is not in the scope of an int) or it is not a C compiler, but only a
        * C like compiler. In the first case the '#ifndef THINK_C42' is wrong
        * because it's not a property of the THINK_C42 compiler, but of all compilers
        * with sizeof(int)*CHAR_BIT < 18.
        * Another nasty thing is that the rest of the code doesn't work for 16 bit ints,
        * so this patch don't solve the 16 bit problem.
        */
#ifndef THINK_C42
       if (result & 0x8000)
               result = result - 0x10000;
#endif  /* THINK_C */
       return(result);
}
#endif

void
Write8Bits(FILE *fp, int i)
{
       putc(i&0xff,fp);
}


void
Write16BitsLowHigh(FILE *fp, int i)
{
       putc(i&0xff,fp);
       putc((i>>8)&0xff,fp);
}


void
Write16BitsHighLow(FILE *fp, int i)
{
       putc((i>>8)&0xff,fp);
       putc(i&0xff,fp);
}

#ifdef KLEMM_36

int  Read24BitsHighLow ( FILE* fp )
{
   int  high = ReadByte         (fp);
   int  med  = ReadByteUnsigned (fp);
   int  low  = ReadByteUnsigned (fp);

   return (high << 16) | (med << 8) | low;
}

#else
int
Read24BitsHighLow(FILE *fp)
{
       int     first, second, third;
       int     result;

       first = 0xff & getc(fp);
       second = 0xff & getc(fp);
       third = 0xff & getc(fp);

       result = (first << 16) + (second << 8) + third;
       if (result & 0x800000)
               result = result - 0x1000000;
       return(result);
}
#endif

#define Read32BitsLowHigh(f)    Read32Bits(f)

#ifdef KLEMM_36

int  Read32Bits ( FILE* fp )
{
   int  low  = ReadByteUnsigned (fp);
   int  medl = ReadByteUnsigned (fp);
   int  medh = ReadByteUnsigned (fp);
   int  high = ReadByte         (fp);

   return (high << 24) | (medh << 16) | (medl << 8) | low;
}

#else

int
Read32Bits(FILE *fp)
{
       int     first, second, result;

       first = 0xffff & Read16BitsLowHigh(fp);
       second = 0xffff & Read16BitsLowHigh(fp);

       result = (second << 16) + first;
#ifdef  CRAY
       if (result & 0x80000000)
               result = result - 0x100000000;
#endif  /* CRAY */
       return(result);
}
#endif


#ifdef KLEMM_36

int  Read32BitsHighLow ( FILE* fp )
{
   int  high = ReadByte         (fp);
   int  medh = ReadByteUnsigned (fp);
   int  medl = ReadByteUnsigned (fp);
   int  low  = ReadByteUnsigned (fp);

   return (high << 24) | (medh << 16) | (medl << 8) | low;
}

#else

int
Read32BitsHighLow(FILE *fp)
{
       int     first, second, result;

       first = 0xffff & Read16BitsHighLow(fp);
       second = 0xffff & Read16BitsHighLow(fp);

       result = (first << 16) + second;
#ifdef  CRAY
       if (result & 0x80000000)
               result = result - 0x100000000;
#endif
       return(result);
}

#endif

void
Write32Bits(FILE *fp, int i)
{
       Write16BitsLowHigh(fp,(int)(i&0xffffL));
       Write16BitsLowHigh(fp,(int)((i>>16)&0xffffL));
}


void
Write32BitsLowHigh(FILE *fp, int i)
{
       Write16BitsLowHigh(fp,(int)(i&0xffffL));
       Write16BitsLowHigh(fp,(int)((i>>16)&0xffffL));
}


void
Write32BitsHighLow(FILE *fp, int i)
{
       Write16BitsHighLow(fp,(int)((i>>16)&0xffffL));
       Write16BitsHighLow(fp,(int)(i&0xffffL));
}

#ifdef KLEMM_36
void ReadBytes (FILE     *fp, char *p, int n)
{
   memset ( p, 0, n );
   fread  ( p, 1, n, fp );
}
#else
void ReadBytes(FILE     *fp, char *p, int n)
{
       /* What about fread? */

       while (!feof(fp) & (n-- > 0))
               *p++ = getc(fp);
}
#endif

void ReadBytesSwapped(FILE *fp, char *p, int n)
{
       register char   *q = p;

       /* What about fread? */

       while (!feof(fp) & (n-- > 0))
               *q++ = getc(fp);

       /* If not all bytes could be read, the resorting is different
        * from the normal resorting. Is this intention or another bug?
        */
       for (q--; p < q; p++, q--){
               n = *p;
               *p = *q;
               *q = n;
       }
}

#ifdef KLEMM_36
void WriteBytes(FILE *fp, char *p, int n)
{
   /* return n == */
   fwrite ( p, 1, n, fp );
}
#else
void WriteBytes(FILE *fp, char *p, int n)
{
       /* No error condition checking */
       while (n-- > 0)
               putc(*p++, fp);
}
#endif
#ifdef KLEMM_36
void WriteBytesSwapped(FILE *fp, char *p, int n)
{
   p += n;
   while ( n-- > 0 )
       putc ( *--p, fp );
}
#else
void WriteBytesSwapped(FILE *fp, char *p, int n)
{
       p += n-1;
       while (n-- > 0)
               putc(*p--, fp);
}
#endif



/****************************************************************
* The following two routines make up for deficiencies in many
* compilers to convert properly between unsigned integers and
* floating-point.  Some compilers which have this bug are the
* THINK_C compiler for the Macintosh and the C compiler for the
* Silicon Graphics MIPS-based Iris.
****************************************************************/

#ifdef applec   /* The Apple C compiler works */
# define FloatToUnsigned(f)     ((unsigned long)(f))
# define UnsignedToFloat(u)     ((double)(u))
#else /* applec */
# define FloatToUnsigned(f)     ((unsigned long)(((long)((f) - 2147483648.0)) + 2147483647L + 1))
# define UnsignedToFloat(u)     (((double)((long)((u) - 2147483647L - 1))) + 2147483648.0)
#endif /* applec */
/****************************************************************
* Extended precision IEEE floating-point conversion routines
****************************************************************/

double
ConvertFromIeeeExtended(char* bytes)
{
       double  f;
       long    expon;
       unsigned long hiMant, loMant;

#ifdef  TEST
printf("ConvertFromIEEEExtended(%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx\r",
       (long)bytes[0], (long)bytes[1], (long)bytes[2], (long)bytes[3],
       (long)bytes[4], (long)bytes[5], (long)bytes[6],
       (long)bytes[7], (long)bytes[8], (long)bytes[9]);
#endif

       expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF);
       hiMant  =       ((unsigned long)(bytes[2] & 0xFF) << 24)
                       |       ((unsigned long)(bytes[3] & 0xFF) << 16)
                       |       ((unsigned long)(bytes[4] & 0xFF) << 8)
                       |       ((unsigned long)(bytes[5] & 0xFF));
       loMant  =       ((unsigned long)(bytes[6] & 0xFF) << 24)
                       |       ((unsigned long)(bytes[7] & 0xFF) << 16)
                       |       ((unsigned long)(bytes[8] & 0xFF) << 8)
                       |       ((unsigned long)(bytes[9] & 0xFF));

       /* This case should also be called if the number is below the smallest
        * positive double variable */
       if (expon == 0 && hiMant == 0 && loMant == 0) {
               f = 0;
       }
       else {
               /* This case should also be called if the number is too large to fit into
                * a double variable */

               if (expon == 0x7FFF) {  /* Infinity or NaN */
                       f = HUGE_VAL;
               }
               else {
                       expon -= 16383;
                       f  = ldexp(UnsignedToFloat(hiMant), (int) (expon -= 31));
                       f += ldexp(UnsignedToFloat(loMant), (int) (expon -= 32));
               }
       }

       if (bytes[0] & 0x80)
               return -f;
       else
               return f;
}





double
ReadIeeeExtendedHighLow(FILE *fp)
{
       char    bytes [10];

       ReadBytes ( fp, bytes, 10 );
       return ConvertFromIeeeExtended ( bytes );
}