/*
 * encoding.c
 *
 * Copyright (c) 1989-1991 Adobe Systems Incorporated.
 * All rights reserved.
 *
 * This file may be freely copied and redistributed as long as:
 *   1) This entire notice continues to be included in the file, 
 *   2) If the file has been modified in any way, a notice of such
 *      modification is conspicuously indicated.
 *
 * PostScript, Display PostScript, and Adobe are registered trademarks of
 * Adobe Systems Incorporated.
 * 
 * ************************************************************************
 * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT
 * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS
 * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR 
 * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY 
 * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, 
 * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR PARTICULAR PURPOSES AND NONINFINGEMENT OF THIRD PARTY RIGHTS.
 * ************************************************************************
 */

#include <stdio.h>
#include <assert.h>
#include "tokens.h"
#include "encoding.h"

#ifndef	FP_IS_IEEE
#define	FP_IS_IEEE	1
#endif	FP_IS_IEEE

#ifndef	HIGH_BYTE_FIRST
#define	HIGH_BYTE_FIRST	1
#endif	HIGH_BYTE_FIRST

typedef struct _t_LoopHole {
  union {
    float   real;
    unsigned long rawBits;
  } value;
} LoopHole;

int	 ieeeFormat = true;
int	 highByteFirst = HIGH_BYTE_FIRST;

char *stringStorage = NULL;
char *stringPointer = NULL;
long stringLength = 0;

void EncodeInteger (integer)
  long integer;
 /*
  */
{
  if ((-128 <= integer) && (integer < 128)) {
    putchar (136);
    putchar (integer & 0377);
  } else if ((-32768 <= integer) && (integer < 32768)) {
    if (highByteFirst) {
      putchar (134);
      putchar ((integer >> 8) & 0377);
      putchar (integer & 0377);
    } else {
      putchar (135);
      putchar (integer & 0377);
      putchar ((integer >> 8) & 0377);
    }
  } else {
    char bytes[5];
    int i;
    if (highByteFirst) {
      bytes[0] = 132;

      for (i = 4; i > 0; i--) {
	bytes[i] = integer & 0377;
	integer >>= 8;
      }
    } else {
      bytes[0] = 133;

      for (i = 1; i < 5; i++) {
	bytes[i] = integer & 0377;
	integer >>= 8;
      }
    }

    fwrite (bytes, 1, 5, stdout);
  }
}

void EncodeReal (real)
  float real;
 /*
   Encoding of reals depends on the native floating point for the machine
   executing this program.  If it is an IEEE machine, no real conversion is
   required, regardless of whether IEEE or native format tokens are requested.
   If the machine is not an IEEE machine, conversion of reals to IEEE format
   is required if IEEE format tokens are requested.
  */
{
  unsigned long value;
  int     i;
  char    bytes[5];
  int highOrderFirst = highByteFirst;

  if (ieeeFormat) {
#if	FP_IS_IEEE
    LoopHole l;

    l.value.real = real;
    value = l.value.rawBits;
    bytes [0] = (highByteFirst ? 138 : 139);
#else	FP_IS_IEEE
    /* Need to convert from native to IEEE here.  Send ASCII for now. */
    printf (" %g", real);
    return;
#endif	FP_IS_IEEE
  } else {
    LoopHole l;

    l.value.real = real;
    value = l.value.rawBits;
    bytes [0] = 140;
    highOrderFirst = HIGH_BYTE_FIRST;
  }

  if (highOrderFirst) {
    for (i = 4; i > 0; i--) {
      bytes[i] = value & 0377;
      value >>= 8;
    }
  } else {
    for (i = 1; i < 5; i++) {
      bytes[i] = value & 0377;
      value >>= 8;
    }
  }

  fwrite (bytes, 1, 5, stdout);
}

void EncodeName (type, name)
  int type;
  char *name;
 /*
   Look for name in the systemname table.  If present, send the encoded form
   unless an immediately evaluated name is required.  Unencoded executable
   names are preceded by a space.  This avoids bookkeeping to record whether
   the name needs to be explicitly separated from the preceding token.
  */
{
  extern int MapName ();
  int     index = MapName (name);

  switch (type) {
   case LITERALNAME:
    if (index >= 0) {
      putchar (145);
      putchar (index);
    } else
      printf ("/%s", name);
    break;

   case EXECUTABLENAME:
    if (index >= 0) {
      putchar (146);
      putchar (index);
    } else
      printf (" %s", name);
    break;

   case IMMEDIATENAME:
    printf ("//%s", name);
    break;

   default:
    FatalError ("Unknown name type %d", type);
    break;
  }
}

void BeginStringEncoding()
 /*
   Initalise string pointer and count, checking first to ensure that no
   previous string is unterminated.  This would be a failure in the parsing
   code.
  */
{
  assert (stringPointer == NULL);

  stringPointer = stringStorage;
  stringLength = 0;
}

void EndStringEncoding ()
 /*
   Write encoded string to output.  Check first that a string is actually
   being built and that its length remains within bounds.
  */
{
  assert ((stringPointer != NULL) && (stringLength <= MAXSTRING));

  if (stringLength < 256) {
    putchar (142);
    putchar (stringLength);
  } else if (highByteFirst) {
    putchar (143);
    putchar ((stringLength >> 8) & 0377);
    putchar (stringLength & 0377);
  } else {
    putchar (144);
    putchar (stringLength & 0377);
    putchar ((stringLength >> 8) & 0377);
  }

  fwrite (stringStorage, 1, stringLength, stdout);

  stringPointer = NULL;		/* Indicate that no string is being built */
}

void InitEncoding ()
 /*
   Storage management for PostScript strings is simplified by pre-allocating
   enough room for the largest allowable string.
  */
{
  stringStorage = (char *) malloc (MAXSTRING);

  if (stringStorage == NULL)
    FatalError ("Failed to allocate string storage (%d bytes).", MAXSTRING);
}
