/*

    File:    wordio.c
    Author:  Graham Toal

    Description:

       Input & output support.  Going via here gives a convenient
    hook for porting to other systems.

*/


#include "splib.h"

#ifndef COMPILER_MICROSOFT
long
#ifndef __STDC__
words_get(data, bytecount, fp)
long *data;
long bytecount;
FILE *fp;
#else
words_get(long *data, long bytecount, FILE *fp)
#endif /* __STDC__ */
{
  return((long)fread(&data[0], 1, (size_t)bytecount, fp));
}

long
#ifndef __STDC__
words_put(data, bytecount, fp)
long *data;
long bytecount;
FILE *fp;
#else
words_put(long *data, long bytecount, FILE *fp)
#endif /* __STDC__ */
{
  return(fwrite(&data[0], 1, (size_t)bytecount, fp));
}

void
#ifndef __STDC__
word_put(w, fp)
long w;
FILE *fp;
#else
word_put(long w, FILE *fp)
#endif /* __STDC__ */
{
long local_long = w;
  if (words_put(&local_long, (long)sizeof(long), fp) != sizeof(long)) {
    /* (using putwords avoids confusion over bytesex) */
    fprintf(stderr, "Data write error - putword\n");
  }
}

long
#ifndef __STDC__
word_get(fp)
FILE *fp;
#else
word_get(FILE *fp)
#endif /* __STDC__ */
{
long local_long;
  if (words_get(&local_long, (long)sizeof(long), fp) != sizeof(long)) {
    fprintf(stderr, "Data read error - getword\n");
  }
  return(local_long);
}

#else /* else if microsoft */

#define MIN_FREAD 256
#define MAX_FREAD (16*1024)
#define MIN_FWRITE 256
#define MAX_FWRITE (16*1024)

/* Read 'count' bytes from f into huge buffer 'dest' */
long
words_get(long *dest, long count, FILE *f)
{
char *a;    /* 'char *' so that pointer arithmetic works as expected */
size_t buffsize = MAX_FREAD;
long total = 0L;
size_t wanted, bytes_got, i;
char *tmp = NULL, *tmpp;

  a = (char *)dest;
  if (count < (long)buffsize) buffsize = (int)count;
  if (count > MIN_FREAD) tmp = Malloc(buffsize, 1);
  if (tmp == NULL) {
    long i;
    /* Read small buffers the old fashioned way ...
       (Also used if not enough heap space for large near buffer) */
    for (i = 0; i < count; i++) {
      int c = fgetc(f);
      if (ferror(f) || feof(f) || c == EOF) return((long)i);
      *a++ = (c & 255);
    }
    return(i);
  }

  for (;;) {
    wanted = buffsize;
    tmpp = tmp;
    if (count < (long)buffsize) wanted = (int)count;
     /* read 'wanted' bytes to tmp */
    bytes_got = fread(tmp, (size_t)1, (size_t)wanted, f);

    /* subtract bytes read from count */
    count = count - (long) bytes_got;
    total = total + (long) bytes_got;

    /* When you hit end of file with fread, it doesn't return an error
       immediately; instead it tells you how many bytes you got (being
       a number less that you expected).  On the *next* request, it
       returns '0 bytes' and an error.  We'll use this fact below. */
       
    /* Note that asking for, say, 1000 bytes is liable to return you
       923 bytes -- if your file was opened in text mode (fopen(file,, "r"))
       it will get 1000 raw bytes, but will remove the carriage-returns */

    /* return if file error */
    if (ferror(f) || feof(f)) {
      Free(tmp); return(total);
    }

    /* copy from NEAR tmp to dest */
    for (i = 0; i < bytes_got; i++) *a++ = *tmpp++;

    /* keep going until count = 0 */
    if (count == 0L) break;
  }
  Free(tmp); return(total);
}

/* Read 'count' bytes from f into buffer 'dest' */
long
words_put(long *source, long count, FILE *f)
{
char *a;    /* 'char *' so that pointer arithmetic works as expected */
int buffsize = MAX_FWRITE;
long total = 0L;
int wanted, bytes_put, i;
char *tmp = NULL, *tmpp;

  a = (char *)source;
  if (count < (long)buffsize) buffsize = (int)count;
  if (count > MIN_FWRITE) tmp = Malloc(buffsize, 1);
  if (tmp == NULL) {
    long i;
    int rc;
    /* Write small buffers the old fashioned way ...
       (Also used if not enough heap space for large near buffer) */
    for (i = 0L; i < count; i++) {
      rc = fputc(*a++, f);
      if (rc == EOF || ferror(f)) return(i);
    }
    return(i);
  }
  for (;;) {
    wanted = buffsize;
    tmpp = tmp;

    if (count < (long)buffsize) wanted = (int)count;
    /* copy from source to NEAR tmp */
    for (i = 0; i < wanted; i++) *tmpp++ = *a++;

    /* write 'wanted' bytes to tmp */
    bytes_put = fwrite(tmp, (size_t)1, (size_t)wanted, f);
    
    /* subtract bytes read from count */
    count = count - (long) bytes_put;
    total = total + (long) bytes_put;
    
    if (bytes_put != wanted) {
      fprintf(stderr, "Warning: asked to write %d bytes; managed %d\n",
        wanted, bytes_put);
      break;
    }

    /* return if file error */
    if (ferror(f)) break;

    /* keep going until count = 0 */
    if (count == 0L) break;
    if (count <= 0L) {
    	/* shouldn't happen */
      fprintf(stderr, "Internal Error: wr_bytes -- count = %ld\n", count);
      exit(0);
    }
  }
  Free(tmp); return(total);
}

static long
w_get(long *data, size_t bytecount, FILE *fp)
{
  return((long)fread(&data[0], 1, bytecount, fp));
}

static long
w_put(long *data, size_t bytecount, FILE *fp)
{
  return(fwrite(&data[0], 1, bytecount, fp));
}

void
word_put(long w, FILE *fp)
{
long local_long = w;
  if (w_put(&local_long, sizeof(long), fp) != sizeof(long)) {
    /* (using putwords avoids confusion over bytesex) */
    fprintf(stderr, "Data write error - putword\n");
  }
}

long
word_get(FILE *fp)
{
long local_long;
  if (w_get(&local_long, sizeof(long), fp) != sizeof(long)) {
    fprintf(stderr, "Data read error - getword\n");
  }
  return(local_long);
}

#endif
