/* Release 3.10 */
/*-------------------------------------------------------------------------*
*                                                                          *
*  STREAM.CPP - 1.2 stream class implementations.      		           *
*                                                                          *
*  COPYRIGHT (C) 1989..1992 Clarion Software Corporation.                  *
*  All Rights Reserved                                                     *
*                                                                          *
*--------------------------------------------------------------------------*/

#define _NO_IO
#include <clib.h>
#include <stream.hpp>

#pragma module(init_prio=>22)

struct whitespace WS;

#define CIRC_BUF_SIZE 1024
#define MAX_STRING 512

#pragma save
#pragma call(near_call=>on, reg_param=>(ax,bx,cx,dx), reg_saved=>(ax,bx,cx,dx,di,si,ds,st1,st2))
static void   outstr(ostream& st, char *s);
static char  *doconv(long val, int width, int radix);
static char  *getbuf(int sz);
#pragma restore

int  streambuf::allocate() {

   base=new char[BUFSIZ];
   if(base) {
     didalloc=1;
     setbuf(base, BUFSIZ, 0);
     return(0);
   }
   return(-1);
}

streambuf * streambuf::setbuf(char *b, int sz, unsigned pos) {

   if(didalloc && base)
     delete base;
   base=b;
   if(base && pos < sz) {
     eptr=b+sz;
     pptr=b+pos;
     gptr=b+pos;
     return(this);
   }
   return(NULL);
}

int streambuf::overflow(int) {

  return EOF;
}

int streambuf::underflow() {

  return EOF;
}

void  streambuf::terminate() {

  if(pptr < eptr)
    *pptr = '\0';
}

streambuf::~streambuf() {

  if(didalloc)
    delete base;
}

int streambuf::snextc() {

  if(pptr == eptr)
    return underflow();
  return *pptr++;
}

int streambuf::sputc(int c) {

  if(gptr == eptr)
    return overflow(c);
  *gptr++ = c;
  return c;
}

void streambuf::sputbackc(char c) {

  if((pptr != base) || (c != EOF))
    *--pptr = c;
}

/****************************************************************************/
//  conversion fiunction implementation
/****************************************************************************/

static char *getbuf(int sz) {

  static char buffer[CIRC_BUF_SIZE];
  static char *curpos=buffer;
  char *ret;

  if(curpos+sz < buffer+CIRC_BUF_SIZE) {
    ret=curpos;
    curpos+=sz;
  }
  else if(sz < CIRC_BUF_SIZE) {
    ret=buffer;
    curpos=buffer;
  }
  else
    ret=NULL;
  return ret;
}


static char *doconv(long val, int width, int radix) {

  char nbuf[32];
  char *obuf;
  int l, pad;

  ltoa(val, nbuf, radix);
  l=strlen(nbuf);
  if(width > 0) {
    if(l > width) {
      l = width;
      nbuf[l]='\0';
    }
    pad = width-l;
    if(pad+l > (MAX_STRING-1))
      pad=(MAX_STRING-1)-l;
  }
  else
    pad=0;
  obuf=getbuf(pad+l+1);
  if(pad)
    memset(obuf, ' ', pad);
  strcpy(&obuf[pad], nbuf);
  return(obuf);
}

char *dec(long val, int width) {

  return(doconv(val, width, 10));
}

char *hex(long val, int width) {

  return(doconv(val, width, 16));
}

char *oct(long val, int width) {

  return(doconv(val, width, 8));
}

char *chr(int c, int width) {

  char *obuf;

  if(--width > 0) {
    if(width > (MAX_STRING-2))
      width = (MAX_STRING-2);
  }
  else
    width=0;
  obuf=getbuf(width+2);
  if(width)
    memset(obuf, ' ', width);
  obuf[width]=c;
  obuf[width+1]='\0';
  return obuf;
}

char *str(const char *s, int width) {

  int l, pad;
  char *obuf;

  l=strlen(s);
  if(width > 0) {
    if(l > width)
      l = width;
    pad = width-l;
    if(pad+l > (MAX_STRING-1))
      pad=(MAX_STRING-1)-l;
  }
  else
    pad=0;
  obuf=getbuf(pad+l+1);
  if(pad)
    memset(obuf, ' ', pad);
  memcpy(&obuf[pad], s, l);
  obuf[pad+l]='\0';
  return(obuf);
}

char *form(char *format, ...) {

    va_list _v_argptr;
    char nbuf[MAX_STRING];
    char *obuf;

    va_start(_v_argptr, format);
    vsprintf(nbuf, format, _v_argptr);
    obuf=getbuf(strlen(nbuf)+1);
    strcpy(obuf, nbuf);
    return obuf;
}


/****************************************************************************/
//  filebuf implementation
/****************************************************************************/

filebuf::filebuf(FILE *f) {

  file=f;
  if(file) {
    fd=fileno(f);
    isopen=1;
    didalloc=0;
  }
  else
    isopen=0;
}

filebuf::filebuf(int h) {

  file=::fdopen(h, "r+");
  if(file) {
    fd=h;
    isopen=1;
    didalloc=0;
  }
  else
    isopen=0;
}

filebuf::filebuf(int h, char *buf, int sz) {

  file=::fdopen(h, "r+");
  if(file) {
    fd=h;
    isopen=1;
    didalloc=0;
    ::setvbuf(file, buf, _IOFBF, sz);
  }
  else
    isopen=0;
}

static char *modes[3]={"r",
                        "w",
                        "a" };

filebuf* filebuf::open(char *path, int open_mode) {

  if(file)
    close();
  file=::fopen(path, modes[open_mode]);
  if(file) {
    fd=fileno(file);
    isopen=1;
    didalloc=0;
    return(this);
  }
  isopen=0;
  return(NULL);
}

int filebuf::close() {

  isopen=0;
  return ::fclose(file);
}


void filebuf::terminate() {

}


/****************************************************************************/
//  ostream implementation
/****************************************************************************/

static void outstr(ostream& st, char *s) {

  int c, n=0;

  while((st.good()) && ((c=s[n++]) != '\0')) {
    st.put(c);
  }
}

ostream::ostream(streambuf *sb) {

  stream=sb;
  mystream=0;
  state=_good;
}

ostream::ostream(int h) {

  stream = new filebuf(h);
  mystream=1;
  state=_good;

}

ostream::ostream(int len, char *buf) {

  stream = new streambuf(buf, len);
  mystream=1;
  state=_good;
}


ostream& ostream::operator <<(int i) {

  char o[32];

  ltoa((long) i, o, 10);
  outstr(*this, o);
  return *this;
}

ostream& ostream::operator <<(long l) {

  char o[32];

  ltoa(l, o, 10);
  outstr(*this, o);
  return *this;
}

ostream& ostream::operator <<(unsigned long ul) {

  char o[32];

  ultoa(ul, o, 10);
  outstr(*this, o);
  return *this;
}

ostream& ostream::operator <<(const char *s) {

  outstr(*this, (char *)s);
  return *this;
}

ostream& ostream::operator <<(double d) {

  char o[32];

  gcvtl((long double) d, 18, o);
  outstr(*this, o);
  return *this;
}

ostream& ostream::operator <<(long double ld) {

  char o[32];

  gcvtl(ld, 18, o);
  outstr(*this, o);
  return *this;
}


ostream& ostream::flush() {

  if(stream->file)
    fflush(stream->file);
  else {
    stream->pptr=stream->base;
    stream->gptr=stream->base;
  }
  state=_good;
  return *this;
}

/****************************************************************************/
//  istream implementation
/****************************************************************************/

long double istream::getreal() {

  int c, n=0;
  char _fmt_buf[64];

  if(tied_to)
    tied_to->flush();
  if(skipping)
    *this >> WS;
  c=stream->snextc();
  while(c != EOF)
  {
    if((c == '+') || (c == '-')) {	   /* leading sign */
      _fmt_buf[n++]= (char) c;
      c=stream->snextc();
      if(c == EOF)
        break;
    }
    for(int q=0; q<2; q++) {
      while(isdigit(c)) {     /* get digits */
        _fmt_buf[n++]= (char) c;
        c=stream->snextc();
        if(c == EOF)
          break;
      }
      if((c == '.') && (q == 0)) {   /* decimal point */
        _fmt_buf[n++]= (char) c;
        c=stream->snextc();
        if(c == EOF)
          break;
      }
      else if(q == 0)
        break;
    }                  /* go back for more digits if first iteration */
    c= toupper(c);
    if((c == 'E') || (c == 'D')) {
      _fmt_buf[n++]= (char) c;
      c=stream->snextc();
      if(c == EOF)
        break;
    }
    else
      break;
    if((c == '+') || (c == '-')) {
      _fmt_buf[n++]= (char) c;
      c=stream->snextc();
      if(c == EOF)
        break;
    }                   /* exponent digits */
    while(isdigit(c)) {
      _fmt_buf[n++]= (char) c;
      c=stream->snextc();
      if(c == EOF)
        break;
    }
    break;
  }
  _fmt_buf[n]=0;           /* terminate string */
  if(c != EOF)
    stream->sputbackc(c);
  else {
    state = _fail|_eof;
    if((stream->file) && (ferror(stream->file)))
      state = _bad;
  }
  return atofl(_fmt_buf);	 /* convert and assign if not suppressed */
}

long istream::getlong(int us) {

  int c, negative=0, flag=0;
  long temp=0;
  int _goodscan=0;

  if(tied_to)
    tied_to->flush();
  if(skipping)
    *this >> WS;
  c=stream->snextc();
  if(c == EOF)
    goto error;
  if(c == '+') {
    _goodscan=1;
    c=stream->snextc();
    if(c == EOF)
      goto error;
  }
  else if(c == '-') {                  /* number is negative */
    _goodscan=1;
    negative=1;
    c=stream->snextc();
    if(c == EOF)
      goto error;
    else if(us) {
      state = _fail;
      return(0);
    }
  }
  if(c =='0') {
    _goodscan=1;
    flag=_OCT;
    c=stream->snextc();
    if(c == EOF)
      goto error;
    c=tolower(c);
    if(c == 'x') {
      flag=_HEX;
      c=stream->snextc();
      if(c == EOF)
        goto error;
    }
  }
  if(flag == _HEX) {
    while(isxdigit(c=tolower(c))) {
      temp*=16;
      if(!isdigit(c))
        c-=87;
      else
        c-='0';
      temp+=c;
      c=stream->snextc();
      if(c == EOF)
        goto error;
    }
  }
  else if(flag == _OCT) {
    while((c >= '0') && (c <= '7')) {
      temp*=8;
      temp+=c-'0';
      c=stream->snextc();
      if(c == EOF)
        goto error;
    }
  }
  else {
    while(isdigit(c)) {
      _goodscan=1;
      temp*=10;
      temp+=c-'0';
      c=stream->snextc();
      if(c == EOF)
        goto error;
    }
  }
error:
  if(!_goodscan) {
    state = _fail|_eof;
    if((stream->file) && (ferror(stream->file)))
      state = _bad;
    return(0);
  }
  c=tolower(c);
  if((c != 'u') && (c != 'l'))
    stream->sputbackc(c);
  if(negative)
    return -temp;
  return temp;
}

istream::istream(streambuf *st, int sk, ostream* tie) {

  tied_to=tie;
  stream=st;
  skipping=sk;
  mystream=0;
  if(st)
    state=_good;
}

istream::istream(int sz, char* buf, int sk) {

  tied_to=NULL;
  stream= new streambuf(buf, sz);
  skipping=sk;
  mystream=1;
  if(stream)
    state=_good;
}

istream::istream(int h, int sk, ostream* tie) {

  tied_to=tie;
  stream= new filebuf(h);
  skipping=sk;
  mystream=1;
  if(stream)
    state=_good;
}

istream& istream::operator>>(unsigned char& c) {

  int t;

  if(state == _good) {
    if(tied_to)
      tied_to->flush();
    do {
      t=stream->snextc();
    } while ((skipping) && (isspace(t)));
    if(t == EOF) {
      state = _fail|_eof;
      if((stream->file) && (ferror(stream->file)))
        state = _bad;
    }
    else
      c=t;
  }
  return *this;
}

istream& istream::operator>>(float& f) {

  f=(float) getreal();
  return *this;
}

istream& istream::operator>>(double& d) {

  d=(double) getreal();
  return *this;
}

istream& istream::operator>>(long double& ld) {

  ld= getreal();
  return *this;
}

istream& istream::operator>>(char *s) {

  int c, n=0;

  if(state == _good) {
    if(tied_to)
      tied_to->flush();
    do {
      c=stream->snextc();
    } while ((skipping) && (isspace(c)));
    while(!isspace(c)) {
      if(c == EOF) {
        state = _fail|_eof;
        if((stream->file) && (ferror(stream->file)))
          state = _bad;
        break;

      }
      s[n++]=c;
      c=stream->snextc();
    }
    s[n] = '\0';
  }
  return *this;
}

istream& istream::operator>>(whitespace& w) {

  if(state == _good) {
    if(tied_to)
      tied_to->flush();
    do {
      w.x=stream->snextc();
    } while (isspace(w.x));
    stream->sputbackc(w.x);
  }
  return *this;

}

istream& istream::get(char& c) {

  int t;

  if(state == _good) {
    t=stream->snextc();
    if(t == EOF) {
      state = _fail|_eof;
      if((stream->file) && (ferror(stream->file)))
        state = _bad;
    }
    else
      c=t;
  }
  return *this;
}

istream& istream::get(char *s, int len, int term) {

  int c, n=0;

  if(state == _good) {
    while(n < len) {
      c=stream->snextc();
      if(c == EOF) {
        state = _fail|_eof;
        if((stream->file) && (ferror(stream->file)))
          state = _bad;
        break;
      }
      if(c == term)  {
        stream->sputbackc(c);
        break;
      }
      s[n++]=c;
    }
    s[n] = '\0';
  }
  return *this;
}

static filebuf  inbuf(&_iob[0]);
static filebuf outbuf(&_iob[1]);
static filebuf errbuf(&_iob[2]);

istream  cin(&inbuf, 1, &cout);
ostream cout(&outbuf);
ostream cerr(&errbuf);
