// C++ .cc file for gplot, CGM-specific I/O  and parsing -*-c++-*-
// copyright Phil Andrews, Pittsburgh Supercomputing Center, 1992
// all rights reserved
#ifdef macintosh
#  include <errors.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <values.h>
#include "cgm.h"
#include "cgmio.h"
#ifndef _toupper
#define _toupper(c) ((c) -'a'+'A')
#endif
////
// error call
////
extern void myError(const char *inMsg, const char *inMsg2=NULL,
		    int severity=1);
#ifdef macintosh
 #pragma segment CIO1
#endif
// inititalization needed  for some compilers
int cgmInput::beginPictureIndex = 0;
int cgmInput::endMetafileIndex = 0;
////
// basic cgm input
////
const int cgmInput::noErrors = 1;
const int cgmInput::noSep = 1<<1;
const int cgmInput::skipParen = 1<<2;
////
//initialize constants
////



void cgmInput::initialize()
{
  // do we need to initialize the name array ?
  if (!cgmNameArray) initNameArray();
  // initialize other pointers
  vdcTypeCmd = NULL;
  intPrecCmd = NULL;
  realPrecCmd = NULL;
  indexPrecCmd = NULL;
  defColrPrec.maxInt = 255;
  defColrPrec.noBytes = 1;
  defColrIndexPrec.maxInt = 255;
  defColrIndexPrec.noBytes = 1;
  colrPrecCmd = NULL;
  colrIndexPrecCmd = NULL;
  vdcIntPrecCmd = NULL;
  defVdcIntPrecCmd = NULL;
  vdcRealPrecCmd = NULL;
  defVdcRealPrecCmd = NULL;
  inputError = 0;
}
////
// index the metafile
////
void cgmInput::makeIndex(cgmMetafile *inMF)
{
  if (!inMF->firstPic) return; // no pictures
  cgmBeginPicture *myBeginPic;
  cgmPicture *lastPic, *currPic = NULL;
  ////
  // mark our spot
  filePos savePos = pos();
  ////
  // go to the beginning
  goTo(inMF->start());
  lastPic = inMF->firstPic;
  ////
  // find all of the begin picture commands (already have at least first one)
  int noPics = 0;
  while (getCmd() && !sameCmd(cgmNameArray[endMetafileIndex])) {
    if (sameCmd(cgmNameArray[beginPictureIndex])) { // begin picture
      if (noPics) { // at second one, at least
	myBeginPic = new cgmBeginPicture(inMF, this);
	if (!currPic) lastPic->next =
	  new cgmPicture(inMF, this, myBeginPic, lastPic);
	lastPic = lastPic->next;
      }
      currPic = lastPic->next;
      ++noPics;
    } // begin picture
  } // got a command
  ////
  // go back to where we began
  goTo(savePos);
}
////
// get a direct colour (R,G,B)
////
void cgmInput::getDColr(dColr &inColr)
{
//  for (int i=0; i<3; ++i) inColr[i] = getCD();
  for (int i=0; i<3; ++i) inColr.setValue(i, getCD());
}
////
// get a general colour
////
genColr *cgmInput::getColr(int mode, const colrValueExtent *inExtent,
			     const colrTable *inTable)
{
  if (mode) { // r, g, b mode
    int red = getCD();
    int green = getCD();
    int blue = getCD();
    return new genColr(red, green, blue, inExtent, inTable);
  } else return new genColr(getCI(), inExtent, inTable); // indexed
}
////
// get a single vdc measure
////
vdc *cgmInput::getVdc()
{
  if (myVdcType()) { // real VDC
    return new vdc(getVRP());
  }  else { // integer VDC
    return new vdc(getVIP());
  }
}
////
// get a set of vdc points
////
vdcPts *cgmInput::getVdcPts(int inNoPts)
{
  int maxPts = inNoPts ? inNoPts : getNoPts(); // how many would we like
  int ptsGot = 0;
  if (myVdcType()) { // real VDC
    float *floatPtr = new float[maxPts * 2]; // real vdc
    while ((ptsGot < maxPts) && getPt(floatPtr + 2 * ptsGot)) ++ptsGot;
    return new vdcPts(floatPtr, ptsGot);
  }  else { // integer VDC
    int *intPtr = new int[maxPts * 2]; // integer vdc
    while ((ptsGot < maxPts) && getPt(intPtr + 2 * ptsGot)) ++ptsGot;
    return new vdcPts(intPtr, ptsGot);
  }
}
////
// get a polygon set
////
void cgmInput::polygonSet(vdcPts* &outPts, int* &outFlags)
{
  int maxPts = polygonSetSize(), ptsGot = 0;
  outFlags = new int[maxPts];
  if (myVdcType()) { // real VDC
    float *floatPtr = new float[maxPts * 2]; // real vdc
    while ((ptsGot < maxPts) && getPt(floatPtr + 2 * ptsGot)) {
      outFlags[ptsGot] = getType(cgmPolygonSet::typeList,
				 cgmPolygonSet::typeListSize);
      ++ptsGot;
    }
    outPts = new vdcPts(floatPtr, ptsGot);
  }  else { // integer VDC
    int *intPtr = new int[maxPts * 2]; // integer vdc
    while ((ptsGot < maxPts) && getPt(intPtr + 2 * ptsGot)) {
      outFlags[ptsGot] = getType(cgmPolygonSet::typeList,
				 cgmPolygonSet::typeListSize);
      ++ptsGot;
    }
    outPts =  new vdcPts(intPtr, ptsGot);
  }
}
////
// get a colour table
////
colrTable *cgmInput::getColrTable(const colrValueExtent *inExtent)
{
  int startIndex = getCI();
  int noEntries = getNoDColrs();
  dColr tmpDColr; // temporary
  colrTable *newTable = new colrTable(noEntries, inExtent, startIndex);
  for (int i=0; i<noEntries; ++i) {
    getDColr(tmpDColr);
    newTable->addColr(tmpDColr);
  }
  return newTable;
}
////
// clear text input
////
// the constant values for the clear info class
////
const char clearInfo::termChar = ';'; // terminating character
const char clearInfo::termChar1 = '/';
const char clearInfo::quoteChar = '\"'; // quotation character
const char clearInfo::quoteChar1 = '\'';
const char clearInfo::sepChar = ' '; // separation character
const char clearInfo::sepChar1 = '\011'; 
const char clearInfo::sepChar2 = '\012'; 
const char clearInfo::sepChar3 = '\013'; 
const char clearInfo::sepChar4 = '\014'; 
const char clearInfo::sepChar5 = '\015'; 
const char clearInfo::sepChar6 = ','; 
const char clearInfo::nullChar = '_'; // null character (ignored inside tokens)
const char clearInfo::nullChar1 = '$';
const char clearInfo::commentChar = '%'; // comment character
////
// the clear input class
////
// get one clear text command into memory
#if __MSDOS__
HugePt clearInput::getCmd()
#else
unsigned char *clearInput::getCmd()
#endif
{
  // state of parsing
  enum {normalS, quoting, spacing, commenting} myState = normalS; 
  int done = 0; // have we finished ?
  int endFile = 0; // ran out of input
  char c; // latest character 
  char lastQuote = 0; // last quotation character used
  myLastPos = pos(); // mark our spot
  bIndex = 0; // initialize buffer index
  // now loop until done and still have input
#ifdef macintosh
  long char_count = 1;
  while (!done && !(endFile = (FSRead(frefHand,&char_count, &c) == eofErr))) {
#elif __MSDOS__
  while ( !done && !(endFile = eof(frefHand))) {
  read(frefHand, &c,1);
#else
  while (!done && !(endFile = ((c = getc(filePtr)) == EOF))) {	
#endif
  switch (myState) {
    case commenting: // in the middle of a comment
      if (isComment(c)) myState = normalS; // end of comment
      break; // else ignore input for now
	 case quoting: // in the middle of a quote
      if (c == lastQuote) myState = normalS; // end of quote
      buffer[bIndex++] = c; // in any case take input
      break;
    case spacing: // getting spaces
      if (isSep(c)) break; // no input
      else myState = normalS; // fall thru to normalS state to process c
    case normalS: // normalS input mode
      if (isQuote(c)) { // begin quoting
	myState = quoting;
	lastQuote = c;
      } else if (isComment(c)) { // begin a comment
	myState = commenting;
	break; // no input
      } else if (isTerm(c)) { // finished this command
	done = 1; 
      } else if (isSep(c)) { // a separator
	myState = spacing;
	if (bIndex) { // had some real input already
	  c = sepChar; // standard separator character
	} else break; // no leading spaces
      } else if (isNull(c)) { // null character, ignore
	break;
      } else if (iscntrl(c)) { // control character, ignore
	break;
      }
      else if (islower(c)) { // lower case
	c = _toupper(c); // map to upper case for convenience
      }
      buffer[bIndex++] = c; // acceptable input
    } // end of switch statement
    if (bIndex >= bufferSize) { // need more memory
      if (!doubleBuffer(bIndex)) {
	myError("couldn't double buffer size", "aborting", 0);
	return NULL;
      }
    }
  } // end of while statement
  if (endFile) return NULL; // no more input, will terminate
  if (!done) { // incomplete command
    bIndex = 0; // ignore this command, but continue processing
  }
  // normal finish
  buffer[bIndex] = 0; // terminate string
  return buffer; // return the parsed string
}
////
// match the incoming command name to the beginning of the buffer
////
int clearInput::same(const char *inCmd)
{
  static const int caseDiff = 'A' - 'a';
  int foundMatch;

  for (int i=0; (i<bIndex) && inCmd[i]; ++i) {
    if (buffer[i] == inCmd[i]) continue;
    if ((((int)buffer[i] - inCmd[i]) == caseDiff) &&
	(islower(inCmd[i]))) continue;
    if (((inCmd[i] - (int) buffer[i]) == caseDiff) &&
	(isupper(inCmd[i]))) continue;
	 return 0; // no match
  }
  // match if got to end of inCmd, nothing useful left in buffer
  foundMatch = (!inCmd[i]) && !isalnum(buffer[i]);
  if (foundMatch) { // skip over the command name
    aIndex = i;
  }
  return foundMatch;
}
// figure out how many points we have coming
// somewhat simple minded, but should give an upper bound
int clearInput::getNoPts()
{
  int noVDC = 0; long tIndex = aIndex;
  enum {between, inNumber} myState = between;
  
  while ((tIndex < bIndex) && (!isTerm(buffer[tIndex]))) { // loop thru input
    if (isSep(buffer[tIndex]) || (buffer[tIndex] == '(')
	|| (buffer[tIndex] == ')')) { // not in a number
	myState = between; // mark state
    } else if (myState == between) { // entering a number
      myState = inNumber;
      ++noVDC;
    }
    ++tIndex;
  } // end of input
  return (noVDC + 1) / 2;
}
// figure out how many points in a polygon set
int clearInput::polygonSetSize()
{
  return (getNoTokens() + 2) / 3;
}
// figure out how many tokens we have coming
int clearInput::getNoTokens()
{
  int noTokens = 0; long tIndex = aIndex;
  enum {between, inToken} myState = between;
  
  while ((tIndex < bIndex) && (!isTerm(buffer[tIndex]))) {
    if (isSep(buffer[tIndex])) {
      if (myState == inToken) {
	myState = between;
      }
    } else if (myState == between) {
      myState = inToken;
      ++noTokens;
    }
    ++tIndex;
  }
  return noTokens;
}
////
// get an integer point
////
int clearInput::getPt(int *outArray)
{
  int useParen;
  while ((aIndex < bIndex) && isSep(buffer[aIndex])) ++aIndex; // skip seps

  if ((useParen = (buffer[aIndex] == '('))) ++aIndex; // using parentheses
  outArray[0] = getInt(); // x
  outArray[1] = getInt(); // y
  while ((aIndex < bIndex) && isSep(buffer[aIndex])) ++aIndex; // skip seps
  if (useParen && (buffer[aIndex] == ')')) ++aIndex; // matching parenthesis
  return inputError ? (inputError = 0), 0 : 1;
}
////
// get a real point
////
int clearInput::getPt(float *outArray)
{
  int useParen;
  while ((aIndex < bIndex) && isSep(buffer[aIndex])) ++aIndex; // skip seps

  if ((useParen = (buffer[aIndex] == '('))) ++aIndex; // using parentheses
  outArray[0] = getVRP(); // x
  outArray[1] = getVRP(); // y
  while ((aIndex < bIndex) && isSep(buffer[aIndex])) ++aIndex; // skip seps
  if (useParen && (buffer[aIndex] == ')')) ++aIndex; // matching parenthesis
  return inputError ? (inputError = 0), 0 : 1;
}
////
// grab a string from the input, make memory for it and return a pointer
////
char *clearInput::getString(int &outSize)
{
  char lastQuote, *newPtr;
  int stringSize;

  // skip over separators
  while (isSep(buffer[aIndex]) && (aIndex < bIndex)) ++aIndex;
  if (aIndex >= bIndex) {
    myError("couldn't find string");
    outSize = 0;
    return NULL;
  }
  if (!isQuote(buffer[aIndex])) {
    myError("couldn't find quotation mark");
    outSize = 0;
    return NULL;
  }
  lastQuote = buffer[aIndex++];
  for (stringSize = 0; ((aIndex + stringSize) < bIndex) &&
       (lastQuote != buffer[aIndex + stringSize]); ++stringSize);
  if (lastQuote != buffer[aIndex + stringSize]) {
    myError("couldn't find end of quote");
    outSize = 0;
    return NULL;
  }
  newPtr = new char[stringSize + 1];
  for (int i=0; i < stringSize; ++i) newPtr[i] = buffer[aIndex + i];
  newPtr[i] = 0;
  aIndex += stringSize + 1; // 1 for the closing quote
  outSize = stringSize;
  return newPtr;     
}
////
// basic cgm output
////
// polygon set
////
int cgmOutput::polygonSet(const vdcPts *inPts, const int *inFlags)
{
  if (!inPts || !inFlags) {
    myError("no input for cgmOutput::polygonSet");
    return 1;
  }
  vdc *myVdc;
  for (int i=0; i<inPts->no(); ++i) {
    myVdc = inPts->newVdc(2*i);
    outVdc(myVdc);
    delete myVdc;
    myVdc = inPts->newVdc(2*i + 1);
    outVdc(myVdc);
    delete myVdc;
    outType(cgmPolygonSet::typeList, inFlags[i]);
  }
  return 1;
}
////
// clear text output
////
int clearOutput::startCmd(baseCGM *inCGM) // start outputting the command
{
  return outS(inCGM->cgmName());
}
int clearOutput::endCmd() // end the command
{
  return outC(termChar) && endLine();
}
int clearOutput::outString(const char *inString) // output a CGM string
{
  return outSep() && outC(quoteChar) && outS(inString) &&
    outC(quoteChar);
}
// output one of a list of types
int clearOutput::outType(const char **inList, int inValue)
{
  return outSep() && outS(inList[inValue]);
}
////
// input one of a list of types, assume both are in upper case
////
int clearInput::getType(const char **inList, int listSize)
{
  // skip over separators
  while (isSep(buffer[aIndex]) && (aIndex < bIndex)) ++aIndex;
  if (aIndex >= bIndex) {
    myError("couldn't find type");
    return 0;
  }
  for (int i=0; i<listSize; ++i) {
    for (int j=0; (aIndex < bIndex) && inList[i][j] &&
	 (inList[i][j] == buffer[aIndex + j]); ++j);
    if (!inList[i][j] && !isalnum(buffer[aIndex + j])) { // found match
      aIndex += j;
      return i;
    }
  }
  myError("couldn't match type ", (char *)buffer + aIndex);
  return 0;
}
////
// get an integer precision
////
void clearInput::getIntPrec(intPrec *inPrec)
{
  inPrec->minInt = getInt();
  inPrec->maxInt = getInt();
  inPrec->noBytes = 0;  
}
////
// get an unsigned integer precision
////
void clearInput::getIntPrec(uIntPrec *inPrec)
{
  inPrec->maxInt = getInt();
  inPrec->noBytes = 0;  
}
////
// get a real precision
////
void clearInput::getRealPrec(realPrec *inPrec)
{
  inPrec->minReal = getReal();
  inPrec->maxReal = getReal();
  inPrec->noDigits = getInt();
  inPrec->format = 0;
  inPrec->expPart = 0;
  inPrec->fractPart = 0;
}
// put an unsigned integer precision
int clearOutput::outIntPrec(uIntPrec *inPrec)
{
  outInt(inPrec->maxInt);
  return 1;
}
// put an integer precision
int clearOutput::outIntPrec(intPrec *inPrec)
{
  outInt(inPrec->minInt);
  outInt(inPrec->maxInt);
  return 1;
}
// put a real precision
int clearOutput::outRealPrec(realPrec *inPrec)
{
  outReal(inPrec->minReal);
  outReal(inPrec->maxReal);
  outInt(inPrec->noDigits);
  return 1;
}
// get a real number
float clearInput::getReal()
{
  const int noPowers = 21; // should be enough
  static float powers[noPowers] = {1., 10., 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
				   1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14,
				   1e15, 1e16, 1e17, 1e18, 1e19, 1e20};

  int i;
  float fractPart = 0;
  int intPart = getInt(noErrors); // get the leading integer, may not exist
  float ret;
  if (buffer[aIndex] == '.') {
    ++aIndex; // skip the period
    for (i=0; isdigit(buffer[aIndex + i]); ++i); // how many digits to come
    ////
    // renormalize the fractional part
	 if (i && (fractPart = getLongDecimal())) { // have a non-zero fraction
      if (i<noPowers) { // easy case
	fractPart /= powers[i];
      } else { // maybe on some machine of the future
	for (; i; --i) fractPart /= 10.0; // must be real, ULTRIX C bug
      }
    }
  }
  ret = (intPart < 0) ? intPart - fractPart : intPart + fractPart;
  ////
  // do we have an exponent ?
  if (buffer[aIndex] == 'E') { // scaled number
    int exponent = getInt();
	 if (exponent < 0) {
      if (exponent < noPowers) ret /= powers[exponent];
      else for (i=0; i>exponent; --i) ret /= 10.0;
    } else if (exponent > 0) {
      if (exponent < noPowers) ret *= powers[exponent];
      else for (i=0; i<exponent; ++i) ret *= 10.0;
	 }
  }
  // all done
  return ret;
}
////
// get a cell array
////
cellArray *clearInput::getCellArray(int inColMode,
				  const colrValueExtent *inExtent,
				  const colrTable *inTable)
{
  const int noPrec = 6;
  static const unsigned int precValues[noPrec] = {1, 2, 4, 8, 16, 24};
  long i;
  vdcPts *myPts = getVdcPts(3);
  int nx = getInt();
  int ny = getInt();
  unsigned int localColrPrec = getE(); // local colour precision
  if (!localColrPrec) localColrPrec = colrPrec().maxInt; // use default
  ////
  // will encode in binary format, find the corresponding precision
  for (i=0; (i<noPrec) && (localColrPrec > ((1<<precValues[i]) - 1)); ++i);
  unsigned int binaryPrec = (i<noPrec) ? precValues[i] : 32; // 32 is the max
  ////
  // create the cellarray
  cellArray *myPtr = new cellArray(inColMode, inExtent, myPts, nx, ny,
					binaryPrec, 1, inTable);
  ////
  // now get the values
  long noValues;
  if (inColMode) { // direct colour mode
	 noValues = (long)3 * nx * ny;
	 for (i=0; i<noValues; ++i) myPtr->addValue(getInt(skipParen));
  } else {
	 noValues = (long)nx * ny;
	 for (i=0; i<noValues; ++i) myPtr->addValue(getInt(skipParen));
  }
  ////
  return myPtr;
}
////
// output a cell array
////
int clearOutput::outCellArray(cellArray *inC)
{
  int ret = 1;
  ret = ret && outVdcPts(inC->corners()) && outInt(inC->nx())
     && outInt(inC->ny());
  // get the binary precision
  unsigned int binaryPrec = inC->prec();
  // find the equivalent clear text precision and output it
  if (binaryPrec > (8 * sizeof(unsigned int))) {
    myError("unsigned int not big enough, tell Phil !");
  }
  unsigned int clearPrec = (unsigned int)
    (((unsigned long) 1 << binaryPrec) - 1);
  ret = ret && outInt(clearPrec);
  // now the values
  int noValues = inC->nx() * inC->ny(); // for indexed
  if (inC->colMode()) noValues *= 3; // for direct
  for (int i=0; i<noValues; ++i) ret = ret && outInt(inC->getValue(i));
  return ret;
}
////
// put a direct colour (R,G,B)
////
int clearOutput::outDColr(dColr &inColr)
{
//  return outInt(inColr[0]) && outInt(inColr[1]) && outInt(inColr[2]);
  return outInt(inColr.getValue(0)) &&
     outInt(inColr.getValue(1)) && outInt(inColr.getValue(2));
}
////
// output a general colour
////
int clearOutput::outColr(genColr *inColr)
{
  int ret = 1;
  if (inColr->type()) { // r, g, b format
    for (int i=0; i<3; ++i) ret = ret && outInt(inColr->i(i));
  } else ret = outInt(inColr->i());
  return ret;
}
////
// output a colour table
////
int clearOutput::outColrTable(colrTable *inTable)
{
  int ret = 1;
  outInt(inTable->start());
  for (int i=0; i<inTable->no(); ++i) ret = ret && outDColr(inTable->val(i));
  return ret;
}
////
// output a set of defaults
////
int clearOutput::outDefaults(cgmDefaults *inDefaults)
{
  int ret = 1;
  endCmd(); // a completely separate command in this encoding
  for (baseCGM *myPtr = inDefaults->contents(); myPtr; myPtr = myPtr->next) {
    myPtr->cgmOut(this);
  }
  outS(cgmMetafileDefaultsReplacement::endName);
  return ret;
}
////
// output a single vdc
////
int clearOutput::outVdc(const vdc *inVDC)
{
  return (inVDC->type()) ? outReal(inVDC->f()) : outInt(inVDC->i());
}
////
// output a set of vdc points
////
int clearOutput::outVdcPts(const vdcPts *inPts)
{
  int i;
  if (inPts->type()) { // real
    for (i = 0; (i<inPts->no()) && outReal(inPts->fx(i)) &&
	 outReal(inPts->fy(i)); ++i);
  } else { // integer
    for (i = 0; (i<inPts->no()) && outInt(inPts->ix(i)) &&
	 outInt(inPts->iy(i)); ++i);
  }
  return i >= inPts->no(); // all successful
}
// functions to read in a clear text integer
// get an integer from the input
int clearInput::getInt(int flag)
{
  int isNeg = 0, firstInt;
  // may skip over separators
  if (flag & skipParen) { // skip over parentheses
    while ((isSep(buffer[aIndex]) || (buffer[aIndex] == '(')
	   || (buffer[aIndex] == ')')) && (aIndex < bIndex)) ++aIndex;
  } else if (!(flag & noSep)) { // mustn't jump over separators
    while (isSep(buffer[aIndex]) && (aIndex < bIndex)) ++aIndex;
  }
  if (aIndex >= bIndex) {
    myError("couldn't find integer");
    inputError = 1;
    return 0;
  }
  // what's our first character ?
  if (buffer[aIndex] == '-') {
    isNeg = 1;
    ++aIndex;
  } else if (buffer[aIndex] == '+') ++aIndex;
  else if (!isdigit(buffer[aIndex])) { // no digit ?, may be OK
    if (!(flag & noErrors)) { // should get an integer
      myError("no digits for getInt in", (char *)buffer);
      myError("getInt read to", (char *)buffer + aIndex);
      inputError = 1;
    }
    return 0; // return 0 in any case
  }
  // now guarranteed some digits
  firstInt = getDecimal();
  if (buffer[aIndex] == '#') {  // do we have a base ?
    if ((firstInt >= 2) && (firstInt <= 16)) {
      return (isNeg) ? - getBased(firstInt) : getBased(firstInt);
    } else {
      myError("illegal base");
      inputError = 1;
      return 0;
    }
  } // bare integer
  return (isNeg) ? - firstInt : firstInt;
}
// read a simple decimal integer, assume already lined up at first digit
int clearInput::getDecimal()
{
  int i;
  long ret = 0;
  while (isdigit(buffer[aIndex])) { // should work anywhere
    for (i=0; (digits[i] != buffer[aIndex]); ++i); // first matching digit
    ret = 10 * ret + i;
    ++aIndex;
  }
  return (int) ret;
}
// read a simple long decimal integer, assume already lined up at first digit
long clearInput::getLongDecimal()
{
  int i;
  long ret = 0;
  while (isdigit(buffer[aIndex])) { // should work anywhere
    for (i=0; (digits[i] != buffer[aIndex]); ++i); // first matching digit
    ret = 10 * ret + i;
    ++aIndex;
  }
  return ret;
}
// read a based integer
int clearInput::getBased(int inBase)
{
  static char extDigits[] =
    {'0', '1', '2', '3', '4', '5', '6', '7',
     '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  static const int maxExtend = sizeof(extDigits) / sizeof(extDigits[0]);
  int ret = 0, i;
  
  // check that we have a legal base 
  if ((inBase < 2) || (inBase > maxExtend)) {
    myError("illegal base");
    return 0;
  }

  // check that we have a legit number
  for (i=0; (i<inBase) && (extDigits[i] != buffer[aIndex]); ++i);
  if (i>=inBase) {
    myError("illegal based integer");
    return 0;
  }
  
  while (1) {
    for (i=0; (i<inBase) && (extDigits[i] != buffer[aIndex]); ++i);
    if (i>=inBase) { // no more digits
      return ret;
    } else { // more to go ?
      ret = inBase * ret + i;
		++aIndex;
    }
  }
}
////
// the binary input class
////
// destructor
////
binaryInput::~binaryInput() {} // keep Cray CC happy
////
// get one binary command into memory
////
#if __MSDOS__
HugePt binaryInput::getCmd()
#else
unsigned char *binaryInput::getCmd()
#endif
{
  int pLen, done, bytesNeeded;
  // get the next 2 non-zero bytes (skip simple, 2-byte NULLS)
  do {
	 if (!getOffBytes(2)) {
      myError("couldn't get command header");
      return NULL;
    }
  } while (!(buffer[0] || buffer[1]));
  myLastPos = pos() - 2; // mark beginning of actual command
  aIndex = bIndex = 2;
  ////
  // get the parameter length
  pLen = buffer[1] & 31;
  if (pLen < 31) { // short form, buffer size is guaranteed larger
    // next command must start on even boundary
    bytesNeeded = (pLen % 2) ? pLen + 1 : pLen;
	 if (bytesNeeded && !getOffBytes(bytesNeeded, bIndex)) {
		myError("couldn't get command");
      return NULL;
    } else {
      bIndex += pLen;
      return buffer;
    }
  }
  // long form
  do { // carry on until we get a terminating partition header
    if (!getOffBytes(2, bIndex)) { // get the next 2 bytes
      myError("couldn't get partition header");
      return NULL;
    }
    pLen = ((buffer[bIndex] & 127) << 8) | buffer[bIndex + 1];
    done = !(buffer[bIndex] & 128); // finished ?
    // next command must start on even boundary
    bytesNeeded = (done && ((bIndex + pLen) % 2)) ? pLen + 1 : pLen;
    // may need more memory
    while ((bytesNeeded + bIndex) > bufferSize) {
      if (!doubleBuffer(bIndex)) return NULL;
    } // get bytes, maybe overwriting partition header
	 if (bytesNeeded && !getOffBytes(bytesNeeded, bIndex)) {
		myError("couldn't get partition");
      return NULL;
    }
    bIndex += pLen;
  } while (!done);
  return buffer;
}
////
// grab a string from the input, make memory for it and return a pointer
////
char *binaryInput::getString(int &outSize)
{
  char *newPtr = NULL;
  int i, pLen, sIndex;
  unsigned int stringSize =  (aIndex < bIndex)?  buffer[aIndex++] : 0; // first byte has initial length
  if (stringSize < 255) { // simple string
    newPtr = new char[stringSize + 1];
    for (i=0; i < stringSize; ++i) newPtr[i] = buffer[aIndex + i];
    newPtr[i] = 0;
    aIndex += stringSize;
    outSize = stringSize;
    return newPtr;     
  }
  // now have complex string, get size first
  int done;
  stringSize = 0; // start fresh
  do {
    done = !(buffer[aIndex + stringSize] & 128); // finished ?
    stringSize += ((buffer[aIndex + stringSize] & 127) << 8)
      | buffer[aIndex + stringSize + 1];
  } while (!done);
  // now make the memory
  newPtr = new char[stringSize + 1];
  // and fill it out
  sIndex = 0;
  do {
    done = !(buffer[aIndex] & 128); // finished ?
    pLen = ((buffer[aIndex] & 127) << 8) | buffer[aIndex + 1];
    aIndex += 2;
    for (i=0; i<pLen; ++i) newPtr[sIndex++] = buffer[aIndex++];
  } while (!done);
  if (sIndex != stringSize) myError("bad string");
  newPtr[sIndex]  = 0;
  outSize = stringSize;
  return newPtr;
}
////
// diagnostic
////
const char *binaryInput::cmdSignature()
{
  static char myString[60];
  sprintf(myString, "(%d, %d) = (%d, %d)", (int) buffer[0], (int) buffer[1],
	  bufferClass(), bufferElement());
  return myString;
}
////
// general signed integer (assume long at least 4 bytes, may truncate)
////
int binaryInput::gInt(int noBytes)
{
  long ret = (buffer[aIndex] & 128) ? (-1 ^ 255) | (int) buffer[aIndex++]
    : buffer[aIndex++];
  switch(noBytes) {
  case 4:
    ret = (ret<<8) | buffer[aIndex++]; // fallthru
  case 3:
    ret = (ret<<8) | buffer[aIndex++]; // fallthru
  case 2:
    ret = (ret<<8) | buffer[aIndex++]; // end
  }
  return (int) ret;
}
////
// general shifted signed integer
////
int binaryInput::gShiftInt(int noBytes, int bitsRead)
{
  // for convenience/speed
  static unsigned char bitMask[8] = {255, 127, 63, 31, 15, 7, 3, 1};
  //
  if ((bitsRead > 7) || (bitsRead < 0)) {
    myError("illegal bitsRead");
    return 0;
  }
  if (bitsRead == 0) return gInt(noBytes); // no shift
  // span parts of noBytes + 1 bytes
  //
  // spans at least 2 bytes, get 'em one at a time
  // first byte
  // get the sign
  long ret = ((buffer[aIndex] & (1 << (7 - bitsRead))) ? -1 : 0)
    << (7 - bitsRead);
  // now the rest of the bits
  ret = ret | (buffer[aIndex++] & bitMask[bitsRead]);
  switch(noBytes) {
  case 4:
    ret = (ret<<8) | buffer[aIndex++]; // fallthru
  case 3:
    ret = (ret<<8) | buffer[aIndex++]; // fallthru
  case 2:
    ret = (ret<<8) | buffer[aIndex++]; // end
  }
  // last byte
  ret = (ret << bitsRead) | (buffer[aIndex] >> (8 - bitsRead));
  return ret;
}
////
// general unsigned integer
////
unsigned int binaryInput::gUInt(int noBytes)
{
  unsigned long ret = buffer[aIndex++];
  switch(noBytes) {
  case 4:
    ret = (ret<<8) | (buffer[aIndex++] & 255); // fallthru
  case 3:
    ret = (ret<<8) | (buffer[aIndex++] & 255); // fallthru
  case 2:
    ret = (ret<<8) | (buffer[aIndex++] & 255); // end
  }
  return (unsigned int) ret;
}
////
// figure out how many points we have coming
////
int binaryInput::getNoPts()
{
  if (aIndex >= bIndex) {
    myError("no Pts to get");
    return 0;
  }
  // each vdc uses an even number of bytes, 2 vdc's per point (8/2 = 4)
  if (myVdcType()) { // real vdc's
    return ((bIndex - aIndex) * 4)  /
      (vdcRealPrec().expPart + vdcRealPrec().fractPart);
  } else return (bIndex - aIndex) / (2 * vdcIntPrec().noBytes);
}
////
// figure out how many points we have in a polygon set
////
int binaryInput::polygonSetSize()
{
  if (aIndex >= bIndex) {
    myError("no polygon set Pts to get");
    return 0;
  }
  // each vdc uses an even number of bytes, 2 vdc's per point (8/2 = 4)
  // the enumerated flag takes 2 bytes
  if (myVdcType()) { // real vdc's
    return ((bIndex - aIndex) * 4)  /
      (vdcRealPrec().expPart + vdcRealPrec().fractPart + 8);
  } else return (bIndex - aIndex) / (2 * vdcIntPrec().noBytes + 2);
}
////
// get one real vdc pt
////
int binaryInput::getPt(float *inF)
{
  if (aIndex >= bIndex) return 0;
  inF[0]=getVRP();
  if (aIndex >= bIndex) return 0;
  inF[1]=getVRP();
  return (aIndex > bIndex) ? 0 : 1;
}
////
// get one integer vdc pt
////
int binaryInput::getPt(int *inI)
{
  if (aIndex >= bIndex) return 0;
  inI[0]=getVIP();
  if (aIndex >= bIndex) return 0;
  inI[1]=getVIP();
  return (aIndex > bIndex) ? 0 : 1;
}
////
// general fixed point real
////
float binaryInput::gFX(realPrec &inPrec)
{
  double expPart = gInt(inPrec.expPart/8);
  double fractPart = gUInt(inPrec.fractPart/8);
  float ret = expPart + fractPart / ((long) 1 << inPrec.fractPart);
  return ret;
}
////
// general floating point real
////
float binaryInput::gFP(realPrec &inPrec)
{
  static double p149 = 1, p23 = 1, p1074, p52;
  double dfract;
  unsigned int exponent;
  unsigned long fract;
  float ret;
  int i;
  if (p149 == 1) for (i=0; i<149; ++i) p149 *= 2.0; // initialize
  if (p23 == 1) for (i=0; i<23; ++i) p23 *= 2.0; // initialize
  if (p1074 == 1) for (i=0; i<1074; ++i) p1074 *= 2.0; // initialize
  if (p52 == 1) for (i=0; i<p52; ++i) p52 *= 2.0; // initialize

  int signBit = (buffer[aIndex] >> 7) & 1; // is this negative ?
  // which precision is this ?
  switch (inPrec.fractPart + inPrec.expPart) {
  case 32: // 32 bit precision
    // get the exponent
	 exponent = ((buffer[aIndex] & 127) << 1) + ((buffer[aIndex + 1] >> 7) & 1);
    // get the fractional part
	 fract = ((unsigned long)(buffer[aIndex + 1] & 127) << 16) +
				((unsigned long)buffer[aIndex + 2] << 8)
      + buffer[aIndex + 3];
	 // step forward the pointer before we forget
    aIndex += 4;
    // check for special cases
    if (exponent == 255) {
      if (fract == 0) { // big number
	return (signBit) ? -MAXFLOAT : MAXFLOAT;
      } else { // screwed up
	myError("undefined IEEE number");
	return 0;
      }
    } else if (exponent == 0) {
      if (fract == 0) return 0;
      else return (signBit) ? -fract / p149 : fract / p149; // fract / 2^149
    } else { // normal number
      ret = 1 + fract / p23;
      if (exponent < 127) for (i=0; i<(127-exponent); ++i) ret /= 2.0;
      else if (exponent > 127) for (i=0; i<(exponent-127); ++i) ret *= 2.0;
      return (signBit) ? -ret : ret;
    }
	 break;
  case 64: // 64 bit precision  !!! not for PC
    exponent = ((buffer[aIndex] & 127) << 4) + ((buffer[aIndex+1] >> 4) & 15);
    // fractional [part might not fit in a long
    dfract = buffer[aIndex + 1] & 15;
    for (i=2; i<8; ++i) {
      dfract *= 256.0;
      dfract += buffer[aIndex + i];
    }
    // step forward the pointer before we forget
    aIndex += 8;
    if (exponent == 2047) {
      if (fract == 0) { // big number
	return (signBit) ? -MAXFLOAT : MAXFLOAT;
      } else { // screwed up
	myError("undefined IEEE number");
	return 0;
      }
    } else if (exponent == 0) {
      if (fract == 0) return 0;
      else return (signBit) ? -fract / p1074 : fract /p1074; // fract / 2^1074
    } else { // normal number
      ret = 1 + dfract / p52;
      if (exponent < 1023) {
	for (i=0; i<(1023 - exponent); ++i) ret /= 2.0;
      } else if (exponent > 1023) {
	for (i=0; i<(exponent - 1023); ++i) ret *= 2.0;
	return (signBit) ? -ret : ret;
      } else return 0; // screwed up
    }
    break;
  }
  return 0; // for safety
}
////
// get a real precision
////
void binaryInput::getRealPrec(realPrec *inPrec)
{
  char myString[60];
  int format = getE();
  int expPart = getInt();
  int fractPart = getInt();
  // check for legality
  if ((((fractPart + expPart) != 32) && ((fractPart + expPart) != 64)) ||
      ((format != 0) && (format != 1)) ||
      ((format == 0) && ((fractPart != 9) && (fractPart != 23))) ||
      ((format == 1) && ((fractPart != 16) && (fractPart != 32)))) {
    sprintf(myString, "(%d, %d, %d)", format, expPart, fractPart);
    myError("illegal binary real precision", myString);
    return;
  }
  ////
  inPrec->format = format;
  inPrec->expPart = expPart;
  inPrec->fractPart = fractPart;
  inPrec->minReal = - 1 << expPart; // guess
  inPrec->maxReal = 1 << expPart; // guess
}
////
// NCAR specific input
////
// initialization
////
void NCARInput::initialize()
{
  if (!fillLocalBuffer()) myError("couldn't get first NCAR block");
}
////
// get some bytes
////
int NCARInput::getOffBytes(unsigned int noBytes, unsigned long offset)
{
  for (int i=0; i<noBytes; ++i) {
    while ((endIndex <= startIndex) && fillLocalBuffer()); // may need bytes
    if (endIndex <= startIndex) {
      myError("couldn't get NCAR data");
      return 0;
    } else buffer[offset + i] = localBuffer[startIndex++];
  }
  return 1;
}
////
// fill the local buffer
////
int NCARInput::fillLocalBuffer()
{
  if (!getBytes(NCARSIZE, localBuffer)) {
    myError("couldn't get NCAR block");
    return 0;
  }
  startIndex = 4; // start of real data
  endIndex = startIndex + ((localBuffer[0] << 8) | (localBuffer[1] & 255));
  if (endIndex % 2) ++endIndex; // sometimes NCAR doesn't include pad byte
  if (endIndex > NCARSIZE) {
    char myString[20];
    sprintf(myString, "%d bytes", endIndex - startIndex);
    myError("too many bytes in NCAR header", myString);
    return 0;
  } else return 1;
}
////
// get a cell array
////
cellArray *binaryInput::getCellArray(int inColMode,
				     const colrValueExtent *inExtent,
				     const colrTable *inTable)
{
  char myString[60];
  long i;
  vdcPts *myPts = getVdcPts(3);
  int nx = getInt();
  int ny = getInt();
  unsigned int localColrPrec = getE(); // local colour precision
  if (!localColrPrec) localColrPrec = colrPrec().noBytes * 8; // use default
  ////
  // create the cellarray
  cellArray *myPtr = new cellArray(inColMode, inExtent, myPts, nx, ny,
					localColrPrec, 1, inTable);
  // what mode is this ?
  int repMode = getE();
  ////
  // now get the values
  int valSize = (inColMode) ? 3 : 1;
  int valBits = valSize * localColrPrec;
  int noValues = valSize * nx;
  int rowSize = 2 * ((noValues * localColrPrec + 15) / 16); // padded
  if (repMode) { // packed list
	 for (i=0; i<ny; ++i) {
		if ((aIndex + rowSize) > bIndex) {
	myError("not enough bytes for cell array");
	break;
		}
		// add a complete row
		myPtr->addValues(&buffer[aIndex], 0, noValues);
		aIndex += rowSize;
	 }
  } else { // run length encoded
	 int runCount, valsGot, j, bitsGot;
	 for (i=0; i<ny; ++i) { // rows start on a 2-byte boundary
		bitsGot = valsGot = 0;
		while (valsGot < nx) {
	runCount = getShiftInt(bitsGot); // increases aIndex
	if (((runCount + valsGot) > nx) || (runCount < 0)) {
	  sprintf(myString, "%d, x = %d, nx = %d, y = %d, ny = %d",
		  runCount, valsGot, nx, i, ny);
	  myError("illegal runcount in cell array", myString);
	  return myPtr;
	} // else OK, replicate value
	for (j=0; j<runCount; ++j)
	  myPtr->addValues(&buffer[aIndex], bitsGot, valSize);
	valsGot += runCount;
	bitsGot += valBits;
	if (bitsGot > 7) { // step forward the byte index
	  aIndex += bitsGot / 8;
	  bitsGot = bitsGot % 8;
	}
		}
		if (aIndex > bIndex) {
	myError("not enough bytes for run length encoding");
	return myPtr;
		}
		if (bitsGot) ++aIndex; // start at a clean byte
		if (aIndex % 2) ++aIndex; // pad at end of rows
	 }
  }
  ////
  return myPtr;
}
#ifdef macintosh
  #pragma segment CIO2
#endif
////
// parsing initialization
////
// set the parser's pointer
////
cgmNameNew *cgmInput::cgmNameArray = NULL;
////
// add new classes to this array later so that the parser knows about them
////
// define an INITMACRO set up for runtime initialization
////
#define INITMACRO(inName) \
cgmNameArray[i].myName = &inName::myName;\
cgmNameArray[i].myClass = inName::myClass;\
cgmNameArray[i].myElement = inName::myElement;\
cgmNameArray[i].fp = inName::getNew; ++i;
////
// now fill out the structure, either at runtime
////
void cgmInput::initNameArray() {
  ////
  // grab some memory
  cgmNameArray = new cgmNameNew[cgmNoClasses];
  if (!cgmNameArray) myError("couldn't create parsing array", "", 0);
  int i = 0;
  ////
  // delimiters
  INITMACRO(cgmBeginMetafile)
  endMetafileIndex = i; // will need this later
  INITMACRO(cgmEndMetafile)
  beginPictureIndex = i; // will need this later
  INITMACRO(cgmBeginPicture)
  INITMACRO(cgmBeginPictureBody)
  INITMACRO(cgmEndPicture)
  // metafile descriptors
  INITMACRO(cgmMetafileVersion)
  INITMACRO(cgmMetafileDescription)
  INITMACRO(cgmVdcType)
  INITMACRO(cgmIntegerPrec)
  INITMACRO(cgmRealPrec)
  INITMACRO(cgmIndexPrec)
  INITMACRO(cgmColrPrec)
  INITMACRO(cgmColrIndexPrec)
  INITMACRO(cgmMaxColrIndex)
  INITMACRO(cgmColrValueExtent)
  INITMACRO(cgmMetafileElementList)
  INITMACRO(cgmMetafileDefaultsReplacement)
  INITMACRO(cgmFontList)
  INITMACRO(cgmCharacterSetList)
  INITMACRO(cgmCharacterCodingAnnouncer)
  // picture descriptors
  INITMACRO(cgmScalingMode)
  INITMACRO(cgmColrMode)
  INITMACRO(cgmLineWidthMode)
  INITMACRO(cgmMarkerSizeMode)
  INITMACRO(cgmEdgeWidthMode)
  INITMACRO(cgmVdcExtent)
  INITMACRO(cgmBackgroundColr)
  // control elements
  INITMACRO(cgmVdcInteger)
  INITMACRO(cgmVdcReal)
  INITMACRO(cgmAuxColr)
  INITMACRO(cgmTransparency)
  INITMACRO(cgmClipRect)
  INITMACRO(cgmClip)
  // graphical primitives
  INITMACRO(cgmPolyline)
  INITMACRO(cgmDisPolyline)
  INITMACRO(cgmPolymarker)
  INITMACRO(cgmText)
  INITMACRO(cgmRestrText)
  INITMACRO(cgmApndText)
  INITMACRO(cgmPolygon)
  INITMACRO(cgmPolygonSet)
  INITMACRO(cgmGDP)
  INITMACRO(cgmCellArray)
  INITMACRO(cgmRectangle)
  INITMACRO(cgmCircle)
  INITMACRO(cgmArc3Pt)
  INITMACRO(cgmArc3PtClose)
  INITMACRO(cgmArcCtr)
  INITMACRO(cgmArcCtrClose)
  INITMACRO(cgmEllipse)
  INITMACRO(cgmEllipArc)
  INITMACRO(cgmEllipArcClose)
  // attributes
  INITMACRO(cgmLineIndex)
  INITMACRO(cgmLineType)
  INITMACRO(cgmLineWidth) 
  INITMACRO(cgmLineColr) 
  INITMACRO(cgmMarkerIndex)
  INITMACRO(cgmMarkerType)
  INITMACRO(cgmMarkerSize)
  INITMACRO(cgmMarkerColr) 
  INITMACRO(cgmTextIndex)
  INITMACRO(cgmFontIndex)
  INITMACRO(cgmTextPrec)
  INITMACRO(cgmCharExpan)
  INITMACRO(cgmCharSpace)
  INITMACRO(cgmTextColr) 
  INITMACRO(cgmCharHeight)
  INITMACRO(cgmCharOri)
  INITMACRO(cgmTextPath)
  INITMACRO(cgmTextAlign)
  INITMACRO(cgmCharSetIndex)
  INITMACRO(cgmAltCharSetIndex)
  INITMACRO(cgmFillIndex)
  INITMACRO(cgmHatchIndex)
  INITMACRO(cgmPatIndex)
  INITMACRO(cgmIntStyle)
  INITMACRO(cgmFillColr)
  INITMACRO(cgmEdgeIndex)
  INITMACRO(cgmEdgeType)
  INITMACRO(cgmEdgeWidth)
  INITMACRO(cgmEdgeColr) 
  INITMACRO(cgmEdgeVis)
  INITMACRO(cgmFillRefPt)
  INITMACRO(cgmPatTable)
  INITMACRO(cgmPatSize)
  INITMACRO(cgmColrTable)
  INITMACRO(cgmASF)
  // escape element
  INITMACRO(cgmEscapeElement)
  // externals
  INITMACRO(cgmMessage)
  INITMACRO(cgmApplData)
};
#undef INITMACRO
////
// binary output
////
int binaryOutput::startCmd(baseCGM *inCGM) // start outputting the command
{
  return 1; // fix later
}
int binaryOutput::endCmd() // end the command
{
  return 1; // fix later
}
int binaryOutput::outString(const char *inString) // output a CGM string
{
  return 1; // fix later
}
// output one of a list of types
int binaryOutput::outType(const char **inList, int inValue)
{
  return 1; // fix later
}
