/*
	Little Smalltalk, version 2

	Unix specific input and output routines
	written by tim budd, January 1988
*/
# include <stdio.h>
# include "env.h"
# include "memory.h"
# include "names.h"

struct {
	int di;
	object cl;
	short ds;
	} dummyObject;

/*
	imageRead - read in an object image
		we toss out the free lists built initially,
		reconstruct the linkages, then rebuild the free
		lists around the new objects.
		The only objects with nonzero reference counts
		will be those reachable from either symbols
*/
static int fr(fp, p, s)
FILE *fp;
char *p;
int s;
{	int r;

	r = fread(p, s, 1, fp);
	if (r && (r != 1))
		sysError("imageRead count error","");
	return r;
}

noreturn imageRead(fp)
FILE *fp;
{	short i, size;
	object *mBlockAlloc();

	ignore fr(fp, (char *) &symbols, sizeof(object));
	i = 0;

	while(fr(fp, (char *) &dummyObject, sizeof(dummyObject))) {
		i = dummyObject.di;
		
		if ((i < 0) || (i > ObjectTableMax))
			sysError("reading index out of range","");
		objectTable[i].class = dummyObject.cl;
		if ((objectTable[i].class < 0) || 
			((objectTable[i].class>>1) > ObjectTableMax)) {
			fprintf(stderr,"index %d\n", dummyObject.cl);
			sysError("class out of range","imageRead");
			}
		objectTable[i].size = size = dummyObject.ds;
		if (size < 0) size = ((- size) + 1) / 2;
		if (size != 0) {
			objectTable[i].memory = mBlockAlloc((int) size);
			ignore fr(fp, (char *) objectTable[i].memory,
				sizeof(object) * (int) size);
				}
		else
			objectTable[i].memory = (object *) 0;
		}

	/* now restore ref counts, getting rid of unneeded junk */
	visit(symbols);
	/* toss out the old free lists, build new ones */
	setFreeLists();
	
}

/*
	imageWrite - write out an object image
*/

static fw(fp, p, s)
FILE *fp;
char *p;
int  s;
{
	if (fwrite(p, s, 1, fp) != 1) {
		sysError("imageWrite size error","");
		}
}

noreturn imageWrite(fp)
FILE *fp;
{	short i, size;

	fw(fp, (char *) &symbols, sizeof(object));

	for (i = 0; i < ObjectTableMax; i++) {
		if (objectTable[i].referenceCount > 0) {
			dummyObject.di = i;
			dummyObject.cl = objectTable[i].class;
			dummyObject.ds = size = objectTable[i].size;
			fw(fp, (char *) &dummyObject, sizeof(dummyObject));
			if (size < 0) size = ((- size) + 1) / 2;
			if (size != 0)
				fw(fp, (char *) objectTable[i].memory,
					sizeof(object) * size);
			}
		}
}

/* i/o primitives - necessarily rather UNIX dependent;
	basically, files are all kept in a large array.
	File operations then just give an index into this array 
*/
# define MAXFILES 20
/* we assume this is initialized to NULL */
static FILE *fp[MAXFILES];

object ioPrimitive(number, arguments)
int number;
object *arguments;
{	int i, j;
	char *p, buffer[1024];
	object returnedObject;

	returnedObject = nilobj;

	i = intValue(arguments[0]);

	switch(number) {
		case 0:		/* file open */
			i = intValue(arguments[0]);
			p = charPtr(arguments[1]);
			if (streq(p, "stdin")) 
				fp[i] = stdin;
			else if (streq(p, "stdout"))
				fp[i] = stdout;
			else if (streq(p, "stderr"))
				fp[i] = stderr;
			else {
				fp[i] = fopen(p, charPtr(arguments[2]));
				}
			if (fp[i] == NULL)
				returnedObject = nilobj;
			else
				returnedObject = newInteger(i);
			break;

		case 1:		/* file close - recover slot */
			if (fp[i]) ignore fclose(fp[i]);
			fp[i] = NULL;
			break;

		case 2:		/* file size */
		case 3:		/* file in */
			if (fp[i]) fileIn(fp[i], true);
			break;

		case 4:		/* get character */
			sysError("file operation not implemented yet","");

		case 5:		/* get string */
			if (! fp[i]) break;
			j = 0; buffer[j] = '\0';
			while (1) {
			   if (fgets(&buffer[j], 512, fp[i]) == NULL)
			      return(nilobj); /* end of file */
			   if (fp[i] == stdin) {
				/* delete the newline */
				j = strlen(buffer);
				if (buffer[j-1] == '\n')
					buffer[j-1] = '\0';
				}
			   j = strlen(buffer)-1;
			   if (buffer[j] != '\\')
			      break;
			   /* else we loop again */
			}
			returnedObject = newStString(buffer);
			break;

		case 7:		/* write an object image */
			if (fp[i]) imageWrite(fp[i]);
			returnedObject = trueobj;
			break;

		case 8:		/* print no return */
		case 9:		/* print string */
			if (! fp[i]) break; 
			ignore fputs(charPtr(arguments[1]), fp[i]);
			if (number == 8)
				ignore fflush(fp[i]);
			else
				ignore fputc('\n', fp[i]);
			break;

		default:
			sysError("unknown primitive","filePrimitive");
		}

	return(returnedObject);
}
