[pe.cust.sources] Little Smalltalk Source, *New* Part 15 of 20

earlw@pesnta.UUCP (Earl Wallace) (06/13/85)

#! /bin/sh 
#
# This is an another posting of the Little Smalltalk source, the last posting
# of this source went out in 5 parts and they were too big (>200k) for most
# sites so I redid the whole mess to keep the files around the 50k range.
#
# The complete set is now 20 parts.
#
# P.S. - If you don't receive all 20 parts within 5 days, drop me a line.
#	 Also, I have the Rand sources of May 1984, if someone has a more
#	 updated copy, I'll be happy to post them (or YOU can post them :-))
# 
# -earlw@pesnta
#
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	sources/drive.c
#	sources/file.c
#	sources/file.h
#	sources/interp.c
#	sources/interp.h
#	sources/lex.c
#	sources/lexcmd.c
#	sources/line.c
# This archive created: Thu Jun 13 11:32:56 1985
# By:	Earl Wallace (Perkin-Elmer Data Systems Group / Customer Service)
export PATH; PATH=/bin:$PATH
if test -f 'sources/drive.c'
then
	echo shar: will not over-write existing file "'sources/drive.c'"
else
cat << \SHAR_EOF > 'sources/drive.c'
/*
	Little Smalltalk
		command parser

		timothy a. budd, 12/84

*/
/*
	The source code for the Little Smalltalk System may be freely
	copied provided that the source of all files is acknowledged
	and that this condition is copied with each file.

	The Little Smalltalk System is distributed without responsibility
	for the performance of the program and without any guarantee of
	maintenance.

	All questions concerning Little Smalltalk should be addressed to:

		Professor Tim Budd
		Department of Computer Science
		The University of Arizona
		Tucson, Arizona
		85721
		USA
*/
# include <stdio.h>
# include "object.h"
# define DRIVECODE
# include "drive.h"
# include "cmds.h"
# include "number.h"
# include "symbol.h"
# include "string.h"
# include "byte.h"
# include "interp.h"
# include "primitive.h"

extern enum lextokens token, nextlex();
extern int prntcmd;
extern int inisstd;
extern int started;
extern char toktext[];
extern char *lexptr;
extern int line_grabber();
extern tok_type t;

/* test_driver - see if the driver should be invoked */
int test_driver(block)
int block;	/* indicates wheter to use block or non-blocking input */
{
	switch(line_grabber( block )) {
		default: cant_happen(17);
		case -1:
			/*  return end of file indication */
			return(0);
		case 0:
			/* enqueue driver process again */
			return(1);
		case 1:
			if (*lexptr == ')') {
				dolexcommand(lexptr);
				return(1);
				}
			parse();
			return(1);
		}
}

/* ---- code generation routines  -------------- */
# define CODEMAX 500
static uchar code[CODEMAX];
static int codetop = 0;

static gencode(value)
register int value;
{
	if (value >= 256)
		lexerr("code word too big: %d", value);
	if (codetop > CODEMAX)
		lexerr("too many code words: %d", codetop);
	/*if (started)
	fprintf(stderr,"code %d (%d %d)\n", value, value/16, value%16);*/
	code[codetop++] = itouc(value);
}

static genhighlow(high, low)
register int high;
register int low;
{
	if (high < 0 || high > 16)
		lexerr("genhighlow error: %d", high);
	if (low < 0)
		lexerr("genhighlow low error: %d", low);
	if (low < 16) gencode(high * 16 + low);
	else {
		gencode(TWOBIT * 16 + high);
		gencode(low);
		}
}
/*-------------------------------------------------------*/

static int errflag;

/* parse - main parser */
int parse()
{	register int i;

	errflag = 0;
	reset();

	if (nextlex() == nothing) return(1);
	if (token == NL) return(1);

	i = aprimary();
	if (i >= 0) {
		asign(i);
		if ((prntcmd > 1) && inisstd)
			genhighlow(UNSEND, PRNTCMD);
		}
	else {
		cexpression();
		if (prntcmd && inisstd)
			genhighlow(UNSEND, PRNTCMD);
		}
	genhighlow(POPINSTANCE, 0);	/* assign to ``last'' */
	if (errflag)
		return(1);
	if (token == nothing || token == NL) {
		bld_interpreter();
		return(0);
		}
	expect("end of expression");
	return(1);
}

/* asign - code for an assignment statement - leaves result on stack */
static asign(pos)
int pos;
{	int i;

	i = aprimary();
	if (i >= 0) {
		asign(i);
		}
	else {
		cexpression();
		}
	genhighlow(SPECIAL, DUPSTACK);
	genhighlow(POPINSTANCE, pos);
}

/* expression - read an expression, leaving result on stack */
static expression()
{	int i;

	i = aprimary();
	if (i >= 0) {
		asign(i);
		}
	else {
		cexpression();
		}
}

/* cexpression - code for a (possibly cascaded) expression */
static cexpression()
{
	kcontinuation();
	while (token == SEMI) {
		genhighlow(SPECIAL, DUPSTACK);
		nextlex();
		kcontinuation();
		genhighlow(SPECIAL, POPSTACK);
		}
}

/* kcontinuation - keyword continuation */
static kcontinuation()
{	char kbuf[150];
	int  kcount;

	bcontinuation();
	if (token == KEYWORD) {
		kbuf[0] = '\0';
		kcount = 0;
		while (token == KEYWORD) {
			strcat(kbuf, t.c);
			strcat(kbuf, ":");
			kcount++;
			nextlex();
			primary(1);
			bcontinuation();
			}
		gensend(kbuf, kcount);
		}
}

/* isbinary - see if the current token(s) is a binary */
static int isbinary(bbuf)
char *bbuf;
{
	if (token == BINARY || token == MINUS || 
	    token == BAR || token == PE) {
		strcpy(bbuf, t.c);
		nextlex();
		if (token == BINARY || token == MINUS || 
	    	    token == BAR || token == PE) {
			strcat(bbuf, t.c);
			nextlex();
			}
		return(1);
		}
	return(0);
}

/* bcontinuation - binary continuation */
static bcontinuation()
{	char bbuf[3];

	ucontinuation();
	while (isbinary(bbuf)) {
		primary(1);
		ucontinuation();
		gensend(bbuf, 1);
		}
}

/* ucontinuation - unary continuation */
static ucontinuation()
{
	while (token == LOWERCASEVAR) {
		gensend(t.c, 0);
		nextlex();
		}
}

/* aprimary - primary or beginning of assignment */
static int aprimary()
{	char *c;

	if (token == LOWERCASEVAR) {
		c = t.c;
		if (nextlex() == ASSIGN) {
			nextlex();
			return(findvar(c, 1));
			}
		else {
			genvar(c);
			return( -1 );
			}
		}
	primary(1);
	return( - 1 );
}

/* primary - find a primary expression */
static int primary(must)
int must;	/* must we find something ? */
{	int i, count;

	switch(token) {
		case UPPERCASEVAR:
			genhighlow(PUSHCLASS, aliteral(1));
			break;

		case LOWERCASEVAR:
			genvar(t.c);
			nextlex();
			break;

		case LITNUM:
			if (t.i >= 0 && t.i < 10) {
				genhighlow(PUSHSPECIAL, t.i);
				nextlex();
				}
			else {
				genhighlow(PUSHLIT, aliteral(1));
				}
			break;

		case MINUS:
		case LITFNUM:
		case LITCHAR:
		case LITSTR:
		case LITSYM:
		case PS:
			genhighlow(PUSHLIT, aliteral(1));
			break;

		case PSEUDO:
			switch(t.p) {
				case nilvar: i = 13; break;
				case truevar: i = 11; break;
				case falsevar: i = 12; break;
				case smallvar: i  = 14; break;
				default: lexerr("unknown pseudo var %d", t.p);
				}
			genhighlow(PUSHSPECIAL, i);
			nextlex();
			break;

		case PRIMITIVE:
			if (nextlex() != LITNUM) expect("primitive number");
			i = t.i;
			nextlex();
			count = 0;
			while (primary(0)) count++;
			if (token != PE) expect("primitive end");
			nextlex();
			genhighlow(SPECIAL, PRIMCMD);
			gencode(count);
			gencode(i);
			break;

		case LP:
			nextlex();
			expression();
			if (token != RP) expect("right parenthesis");
			nextlex();
			break;

		case LB:
			nextlex();
			block();
			break;

		default:
			if (must) expect("primary expression");
			return(0);
		}
	return(1);
}

static int maxtemps = 1;
static int temptop = 0;
static char *tempnames[20];

/* block - parse a block definition */
static block()
{	int count, i, position;

	count = 0;
	if (token == COLONVAR) {
		while (token == COLONVAR) {
			tempnames[temptop++] = t.c;
			if (temptop > maxtemps) maxtemps = temptop;
			count++;
			nextlex();
			}
		if (token != BAR) 
			expect("bar following arguments in block");
		nextlex();
		}
	genhighlow(BLOCKCREATE, count);
	if (count) 		/* where arguments go in context */
		gencode(1 + (temptop - count));	
	position = codetop;
	gencode(0);

	if (token == RB) {
		genhighlow(PUSHSPECIAL, 13);
		}
	else
		while (1) {
			i = aprimary();
			if (i >= 0) {
				expression();
				if (token != PERIOD)
					genhighlow(SPECIAL, DUPSTACK);
				genhighlow(POPINSTANCE, i);
				}
			else {
				cexpression();
				if (token == PERIOD)
					genhighlow(SPECIAL, POPSTACK);
				}
			if (token != PERIOD)
				break;
			nextlex();
			}
	genhighlow(SPECIAL, RETURN);
	if (token != RB) expect("end of block");
	temptop -= count;
	nextlex();
	i = (codetop - position) - 1;
	if (i > 255)
		lexerr("block too big %d", i);
	code[position] = itouc(i);
}

# define LITMAX 100
static object *lit_array[LITMAX];
static int littop = 0;

static int addliteral(lit)
object *lit;
{
	if (littop >= LITMAX)
		cant_happen(18);
	sassign(lit_array[littop++], lit);
	return(littop - 1);
}

/* aliteral - find a literal that is part of a literal array */
static int aliteral(must)
int must;	/* must we find something ? */
{	char *c;
	object *new;
	int count;
	int bytetop;
	uchar bytes[200];

	switch(token) {
		case MINUS:
			c = t.c;
			nextlex();
			if (token == LITNUM) {
				new = new_int( - t.i );
				nextlex();
				}
			else if (token == LITFNUM) {
				new = new_float( - t.f );
				nextlex();
				}
			else {
				new = new_sym(c);
				}
			break;

		case LITNUM:
			new = new_int(t.i);
			nextlex();
			break;

		case LITFNUM:
			new = new_float(t.f);
			nextlex();
			break;

		case LITCHAR:
			new = new_char(t.i);
			nextlex();
			break;

		case LITSTR:
			new = new_str(t.c);
			nextlex();
			break;

		case LITSYM:
			new = new_sym(t.c);
			nextlex();
			break;

		case PSEUDO:
			switch(t.p) {
				case nilvar: new = o_nil; break;
				case truevar: new = o_true; break;
				case falsevar: new = o_false; break;
				case smallvar: new = o_smalltalk; break;
				default: lexerr("unknown peudo %d", t.p);
				}
			nextlex();
			break;

		case PS:
			nextlex();
			if (token == LP) goto rdarray;
			else if (token == LB) {
				bytetop = 0;
				while (nextlex() == LITNUM)
					bytes[bytetop++] = itouc(t.i);
				if (token != RB)
					expect("right bracket");
				nextlex();
				new = new_bytearray(bytes, bytetop);
				}
			else expect("array or bytearray");
			break;

		case LP: rdarray:
			count = 0;
			nextlex();
			while (aliteral(0) >= 0) {
				count++;
				}
			if (token != RP) expect("right parenthesis");
			nextlex();
			new = new_array(count, 0);
			while (count)
				new->inst_var[--count] = lit_array[--littop];
			break;

		case UPPERCASEVAR:
		case LOWERCASEVAR:
		case KEYWORD:
		case COLONVAR:
		case BINARY:
		case PE:
		case BAR:
		case SEMI:
			new = new_sym(t.c);
			nextlex();
			break;

		default:
			if (must)
				expect("literal");
			else return( - 1 );
		}
	return(addliteral(new));
}

/* gensend - generate a message send */
static gensend(message, numargs)
char *message;
int  numargs;
{	int i;
	char **p, c;
	tok_type e;

	c = *message;
	if (numargs == 0) {
		for (p = unspecial, i = 0; *p; i++, p++)
			if ((**p == c) && (strcmp(*p, message) == 0)) {
				genhighlow(UNSEND, i);
				return;
				}
		}
	else if (numargs == 1) {
		for (p = binspecial, i = 0; *p; i++, p++)
			if ((**p == c) && (strcmp(*p, message) == 0)) {
				genhighlow(BINSEND, i);
				return;
				}
		for (p = arithspecial, i = 0; *p; i++, p++)
			if ((**p == c) && (strcmp(*p, message) == 0)) {
				genhighlow(ARITHSEND, i);
				return;
				}
		}
	else if (numargs == 2) {
		for (p = keyspecial, i = 0; *p; i++, p++)
			if ((**p == c) && (strcmp(*p, message) == 0)) {
				genhighlow(KEYSEND, i);
				return;
				}
		}
	genhighlow(SEND, numargs);
	gencode(addliteral(new_sym(message)));
}

static object *var_names;
static object *var_values;

extern object *o_nil, *o_true;

static int findvar(str, make)
char *str;
int make;
{  int i;
   object *comp_obj;

   sassign(comp_obj, new_obj((class *) 0, 2, 0));
   sassign(comp_obj->inst_var[0], o_nil);
   sassign(comp_obj->inst_var[1], new_sym(str));
   for (i = 0; i < var_names->size; i++) {
	assign(comp_obj->inst_var[0], var_names->inst_var[i]);
	if (o_true == primitive(SYMEQTEST, 2, &(comp_obj->inst_var[0]))) {
		obj_dec(comp_obj);
		return(i);
		}
	}
   /* not found, perhaps it's new */
   if (make) {
	assign(comp_obj->inst_var[0], var_names);
	assign(var_names, primitive(GROW, 2, &(comp_obj->inst_var[0])));
	assign(comp_obj->inst_var[0], var_values);
	assign(comp_obj->inst_var[1], o_nil);
	assign(var_values, primitive(GROW, 2, &(comp_obj->inst_var[0])));
	}
   else {
	lexerr("unknown variable %s", str);
	i = 0;
	}
   obj_dec(comp_obj);
   return(i);
}

genvar(name)
char *name;
{	int i;

	for (i = 0; i < temptop; i++)
		if (strcmp(name, tempnames[i]) == 0) {
			genhighlow(PUSHTEMP, i+1);
			return;
			}
	genhighlow(PUSHINSTANCE, findvar(name, 0));
}

/* lexerr - error printing with limited reformatting */
lexerr(s, v)
char *s, *v;
{
	char e1[500], e2[500];
	object *new;

	errflag = 1;
	sprintf(e1, s, v); /* format error message */
	sprintf(e2, "error: %s\n", e1);
	sassign(new, new_str(e2));
	primitive(ERRPRINT, 1, &new);
	obj_dec(new);
}

expect(str)
char *str;
{	char ebuf[150];

	/*fprintf(stderr,"expected %s\n", str);
	fprintf(stderr,"current token type %d\n", token);
	fprintf(stderr,"remainder of line %s\n", lexptr);
	fprintf(stderr,"current text %s\n", toktext);*/
	sprintf(ebuf,"expected %s found %s", str, toktext);
	lexerr(ebuf,"");
}

extern object *o_drive;	/* ``driver'' interpreter */

bld_interpreter()
{  interpreter *interp;
   object *literals, *bytecodes, *context;
   int i;

   if (codetop == 0) {
	return;
	}
   genhighlow(SPECIAL, SELFRETURN);
   gencode(0);			/* mark end of bytecodes */
   sassign(literals, new_array(littop, 0));
   for (i = 0; i < littop; i++)
	literals->inst_var[ i ] = lit_array[i];
   sassign(bytecodes, new_bytearray(code, codetop));
   sassign(context, new_obj((class *) 0, 1 + maxtemps, 1));
   interp = cr_interpreter((interpreter *) o_drive, var_values, 
		literals, bytecodes, context);
   link_to_process(interp);
   obj_dec(context);
   obj_dec(bytecodes);
   obj_dec(literals);
}

reset(){
	codetop = littop = temptop = 0;
	maxtemps = 1;
}

/* drv_init initializes the driver, should be called only once */
drv_init() {
	sassign(var_names, new_obj((class *) 0, 0, 0));
	sassign(var_values, new_obj((class *) 0, 0, 0));
	reset();
	findvar("last", 1); 	/* create variable "last" */
	}

drv_free() {
	int i;

	for (i = 0; i < var_values->size; i++)
		assign(var_values->inst_var[ i ], o_nil);
	obj_dec(var_names);
	obj_dec(var_values);
	}
SHAR_EOF
if test 13242 -ne "`wc -c < 'sources/drive.c'`"
then
	echo shar: error transmitting "'sources/drive.c'" '(should have been 13242 characters)'
fi
fi # end of overwriting check
if test -f 'sources/file.c'
then
	echo shar: will not over-write existing file "'sources/file.c'"
else
cat << \SHAR_EOF > 'sources/file.c'
/*
	Little Smalltalk

		programs used by class File
		timothy a. budd 11/84
*/
/*
	The source code for the Little Smalltalk System may be freely
	copied provided that the source of all files is acknowledged
	and that this condition is copied with each file.

	The Little Smalltalk System is distributed without responsibility
	for the performance of the program and without any guarantee of
	maintenance.

	All questions concerning Little Smalltalk should be addressed to:

		Professor Tim Budd
		Department of Computer Science
		The University of Arizona
		Tucson, Arizona
		85721
		USA
*/
# include <stdio.h>
# include "object.h"
# include "file.h"
# include "string.h"
# include "number.h"
# include "primitive.h"

static mstruct *fr_file = 0;	/* free file list */

object *new_file()
{	struct file_struct *new;

	if (fr_file) {
		new = (struct file_struct *) fr_file;
		fr_file = fr_file->mlink;
		}
	else {
		new = structalloc(struct file_struct);
		}

	new->l_size = FILESIZE;
	new->l_ref_count = 0;
	new->file_mode = STRMODE;
	new->fp = NULL;
	return((object *) new);
}

free_file(phil)
struct file_struct *phil;
{
	if (! is_file(phil))
		cant_happen(8);
	if (phil->fp != NULL)
		fclose(phil->fp);
	((mstruct *) phil)->mlink = fr_file;
	fr_file = (mstruct *) phil;
}

file_err(message)
char *message;
{	object *errp;
	char buffer[150];

	sprintf(buffer,"File: %s", message);
	sassign(errp, new_str(buffer));
	primitive(ERRPRINT, 1, &errp);
	obj_dec(errp);
}

file_open(phil, name, type)
struct file_struct *phil;
char *name, *type;
{	char buffer[100];

	if (phil->fp != NULL)
		fclose(phil->fp);
	phil->fp = fopen(name, type);
	if (phil->fp == NULL) {
		sprintf(buffer,"can't open: %s\n", name);
		file_err(buffer);
		}
}

# define BUFLENGTH 250

object *file_read(phil)
struct file_struct *phil;
{	object *new;
	int c;
	char buffer[BUFLENGTH], *p;

	if (phil->fp == NULL) {
		file_err("attempt to read from unopened file");
		return(o_nil);
		}
	switch(phil->file_mode) {
		case CHARMODE:
			if (EOF == (c = fgetc(phil->fp)))
				new = o_nil;
			else
				new = new_char(c);
			break;
		case STRMODE:
			if (NULL == fgets(buffer, BUFLENGTH, phil->fp))
				new = o_nil;
			else {
				p = &buffer[strlen(buffer) - 1];
				if (*p == '\n') *p = '\0';
				new = new_str(buffer);
				}
			break;
		case INTMODE:
			if (EOF == (c = getw(phil->fp)))
				new = o_nil;
			else
				new = new_int(c);
			break;
		default:
			file_err("unknown mode");
			new = o_nil;
		}
	return(new);
}

file_write(phil, obj)
struct file_struct *phil;
object *obj;
{
	if (phil->fp == NULL) {
		file_err("attempt to write to unopened file");
		return;
		}
	switch(phil->file_mode) {
		case CHARMODE:
			if (! is_character(obj)) goto modeerr;
			fputc(int_value(obj), phil->fp);
			break;
		case STRMODE:
			if (! is_string(obj)) goto modeerr;
			fputs(string_value(obj), phil->fp);
			fputc('\n', phil->fp);
			break;
		case INTMODE:
			if (! is_integer(obj)) goto modeerr;
			putw(int_value(obj), phil->fp);
			break;
		}
	return;
modeerr:
	file_err("attempt to write object of wrong type for mode");
}
SHAR_EOF
if test 3082 -ne "`wc -c < 'sources/file.c'`"
then
	echo shar: error transmitting "'sources/file.c'" '(should have been 3082 characters)'
fi
fi # end of overwriting check
if test -f 'sources/file.h'
then
	echo shar: will not over-write existing file "'sources/file.h'"
else
cat << \SHAR_EOF > 'sources/file.h'
/*
	Little Smalltalk

		class File definitions
		timothy a. budd, 11/84
*/
/*
	files use standard i/o package
*/

struct file_struct {
	int l_ref_count;
	int l_size;
	int file_mode;
	FILE *fp;
	};

typedef struct file_struct file;

extern object *new_file();
extern object *file_read();

/* files can be opened in one of three modes, modes are either
	0 - char mode - each read gets one char
	1 - string mode - each read gets a string
	2 - integer mode - each read gets an integer
*/
# define CHARMODE 0
# define STRMODE  1
# define INTMODE  2
SHAR_EOF
if test 544 -ne "`wc -c < 'sources/file.h'`"
then
	echo shar: error transmitting "'sources/file.h'" '(should have been 544 characters)'
fi
fi # end of overwriting check
if test -f 'sources/interp.c'
then
	echo shar: will not over-write existing file "'sources/interp.c'"
else
cat << \SHAR_EOF > 'sources/interp.c'
/*
	Little Smalltalk
		bytecode interpreter
		timothy a. budd
*/
/*
	The source code for the Little Smalltalk System may be freely
	copied provided that the source of all files is acknowledged
	and that this condition is copied with each file.

	The Little Smalltalk System is distributed without responsibility
	for the performance of the program and without any guarantee of
	maintenance.

	All questions concerning Little Smalltalk should be addressed to:

		Professor Tim Budd
		Department of Computer Science
		The University of Arizona
		Tucson, Arizona
		85721
		USA
*/
# include <stdio.h>
# include "object.h"
# include "drive.h"
# include "cmds.h"
# include "interp.h"
# include "process.h"
# include "number.h"
# include "string.h"
# include "symbol.h"
# include "byte.h"
# include "block.h"
# include "primitive.h"

extern object *o_smalltalk;	/* value of pseudo variable smalltalk */
extern object *fnd_class();	/* used to find classes from names */

static mstruct *fr_interp = 0;	/* interpreter memory free list */
int ca_terp = 0;		/* counter for interpreter allocations */

/* cr_interpreter - create a new interpreter */
interpreter *cr_interpreter(sender, receiver, literals, bitearray, context)
interpreter *sender;
object *literals, *bitearray, *receiver, *context;
{	interpreter *new;
	class *rclass;
	int isize;

	if (fr_interp) {
		new = (interpreter *) fr_interp;
		fr_interp = fr_interp->mlink;
		}
	else {
		new = structalloc(interpreter);
		ca_terp++;
		}

	new->t_ref_count = 0;
	new->t_size = INTERPSIZE;

	new->creator = (interpreter *) 0;
	if (sender)
		sassign(new->sender, sender);
	else
		sassign(new->sender, (interpreter *) o_nil);
	sassign(new->literals, literals);
	sassign(new->bytecodes, bitearray);
	sassign(new->receiver, receiver);
	rclass = (class *) fnd_class(receiver);
	if ((! rclass) || ! is_class(rclass))
		isize = 25;
	else {
		isize = rclass->stack_max;
		}
	sassign(new->context, context);
	sassign(new->stack, new_obj((class *) 0, isize, 1));
	new->stacktop = &(new->stack)->inst_var[0];
	new->currentbyte = byte_value(new->bytecodes);
	return(new);
}

/* free_terpreter - return an unused interpreter to free list */
free_terpreter(anInterpreter)
interpreter *anInterpreter;
{
	if (! is_interpreter(anInterpreter))
		cant_happen(8);

	obj_dec((object *) anInterpreter->sender);
	obj_dec(anInterpreter->receiver);
	obj_dec(anInterpreter->bytecodes);
	obj_dec(anInterpreter->literals);
	obj_dec(anInterpreter->context);
	obj_dec(anInterpreter->stack);

	((mstruct *) anInterpreter)->mlink = fr_interp;
	fr_interp = (mstruct *) anInterpreter;
}

/* copy_arguments - copy an array of arguments into the context */
copy_arguments(anInterpreter, argLocation, argCount, argArray)
interpreter *anInterpreter;
int argLocation, argCount;
object **argArray;
{	object *context = anInterpreter->context;
	int i;

	for (i = 0; i < argCount; argLocation++, i++) {
		assign(context->inst_var[ argLocation ], argArray[i]);
		}
}

# define push(x) {assign(*(anInterpreter->stacktop), x); \
			anInterpreter->stacktop++;}

/* push_object - push a returned value on to an interpreter stack */
push_object(anInterpreter, anObject)
interpreter *anInterpreter;
object *anObject;
{
	push(anObject); /* what? no bounds checking?!? */
}

# define nextbyte(x) {x = uctoi(*anInterpreter->currentbyte);\
anInterpreter->currentbyte++;}
# define instvar(x) (anInterpreter->receiver)->inst_var[ x ]
# define tempvar(x) (anInterpreter->context)->inst_var[ x ]
# define lit(x)     (anInterpreter->literals)->inst_var[ x ]
# define popstack() (*(--anInterpreter->stacktop))
# define decstack(x) (anInterpreter->stacktop -= x)
# define skip(x)    (anInterpreter->currentbyte += x )

/* resume - resume executing bytecodes associated with an interpreter */
resume(anInterpreter)
register interpreter *anInterpreter;
{
	int highBits;
	register int lowBits;
	object *tempobj, *receiver, *fnd_super();
	interpreter *sender;
	int i, j, numargs, arglocation;
	char *message;

	while(1) {
		nextbyte(highBits);
		lowBits = highBits % 16;
		highBits /= 16;

		switchtop:
		switch(highBits) {
			default: cant_happen(9);
				break;

			case 0:	/* two bit form */
				highBits = lowBits;
				nextbyte(lowBits);
				goto switchtop;

			case 1: /* push instance variable */
				push(instvar(lowBits));
				break;

			case 2: /* push context value */
				push(tempvar(lowBits));
				break;

			case 3: /* literals */
				push(lit(lowBits));
				break;

			case 4: /* push class */
				tempobj = lit(lowBits);
				if (! is_symbol(tempobj)) cant_happen(9);
				tempobj = primitive(FINDCLASS, 1, &tempobj);
				push(tempobj);
				break;

			case 5: /* special literals */
				if (lowBits < 10) 
					tempobj = new_int(lowBits);
				else if (lowBits == 10) 
					tempobj = new_int(-1);
				else if (lowBits == 11)
					tempobj = o_true;
				else if (lowBits == 12)
					tempobj = o_false;
				else if (lowBits == 13)
					tempobj = o_nil;
				else if (lowBits == 14)
					tempobj = o_smalltalk;
				else if (lowBits == 15)
					tempobj = (object *) runningProcess;
				else if ((lowBits >= 30) && (lowBits < 60)) {
					/* get class */
					tempobj =
					    new_sym(classpecial[lowBits-30]);
					tempobj = primitive(FINDCLASS, 1,
						&tempobj);
					}
				else tempobj = new_int(lowBits);
				push(tempobj);
				break;

			case 6: /* pop and store instance variable */
				assign(instvar(lowBits), popstack());
				break;

			case 7: /* pop and store in context */
				assign(tempvar(lowBits), popstack());
				break;

			case 8: /* send a message */
				numargs = lowBits;
				nextbyte(i);
				tempobj = lit(i);
				if (! is_symbol(tempobj)) cant_happen(9);
				message = symbol_value(tempobj);
				goto do_send;

			case 9: /* send a superclass message */
				numargs = lowBits;
				nextbyte(i);
				tempobj = lit(i);
				if (! is_symbol(tempobj)) cant_happen(9);
				message = symbol_value(tempobj);
				receiver =
					fnd_super(anInterpreter->receiver);
				goto do_send2;

			case 10: /* send a special unary message */
				numargs = 0;
				message = unspecial[lowBits];
				goto do_send;

			case 11: /* send a special binary message */
				numargs = 1;
				message = binspecial[lowBits];
				goto do_send;

			case 12: /* send a special arithmetic message */
				tempobj = *(anInterpreter->stacktop - 2);
				if (! is_integer(tempobj)) goto ohwell;
				i = int_value(tempobj);
				tempobj = *(anInterpreter->stacktop - 1);
				if (! is_integer(tempobj)) goto ohwell;
				j = int_value(tempobj);
				decstack(2);
				switch(lowBits) {
					case 0: i += j; break;
					case 1: i -= j; break;
					case 2: i *= j; break;
					case 3: if (i < 0) i = -i;
						i %= j; break;
					case 4: if (j < 0) i >>= (-j);
						else i <<= j; break;
					case 5: i &= j; break;
					case 6: i |= j; break;
					case 7: i = (i < j); break;
					case 8: i = (i <= j); break;
					case 9: i = (i == j); break;
					case 10: i = (i != j); break;
					case 11: i = (i >= j); break;
					case 12: i = (i > j); break;
					case 13: i %= j; break;
					case 14: i /= j; break;
					case 15: i = (i < j) ? i : j;
						break;
					case 16: i = (i < j) ? j : i;
						break;
					default: cant_happen(9);
					}
				if ((lowBits < 7) || (lowBits > 12))
					tempobj = new_int(i); 
				else tempobj = (i ? o_true : o_false);
				push(tempobj);
				break;

				ohwell: /* oh well, send message */
				numargs = 1;
				message = arithspecial[lowBits];
				goto do_send;

			case 13: /* send a special ternary keyword messae */
				numargs = 2;
				message = keyspecial[lowBits];
				goto do_send;

			case 14: /* block creation */
				numargs = lowBits;
				if (numargs)
					nextbyte(arglocation);
				nextbyte(i);    /* size of block */
				push(new_block(anInterpreter, numargs,
					arglocation));
				skip(i);
				break;

			case 15: /* special bytecodes */
				switch(lowBits) {
				case 0: /* no - op */
					break;
				case 1: /* duplicate top of stack */
					push(*(anInterpreter->stacktop - 1));
					break;
				case 2: /* pop top of stack */
					anInterpreter->stacktop--;
					break;
				case 3: /* return top of stack */
					tempobj = popstack();
					goto do_return;
				case 4: /* block return */
					block_return(anInterpreter, popstack());
					return;
				case 5: /* self return */
					tempobj = tempvar(0);
					goto do_return;
				case 6: /* skip on true */
					nextbyte(i);
					tempobj = popstack();
					if (tempobj == o_true) {
						skip(i);
						push(o_nil);
						}
					break;
				case 7: /* skip on false */
					nextbyte(i);
					tempobj = popstack();
					if (tempobj == o_false) {
						skip(i);
						push(o_nil);
						}
					break;
				case 8: /* just skip */
					nextbyte(i);
					skip(i);
					break;
				case 9: /* skip backward */
					nextbyte(i);
					skip( - i );
					break;
				case 10: /* execute a primitive */
					nextbyte(numargs);
					nextbyte(i); /* primitive number */
					if (i == BLOCKEXECUTE)
						goto blk_execute;
					else if (i == DOPERFORM)
						goto do_perform;
					else {
						decstack(numargs);
						tempobj = primitive(i, numargs,
						anInterpreter->stacktop);
						push(tempobj);
						}
					break;
				case 11: /* skip true, push true */
					nextbyte(i);
					tempobj = popstack();
					if (tempobj == o_true) {
						skip(i);
						anInterpreter->stacktop++;
						}
					break;
				case 12: /* skip on false, push false */
					nextbyte(i);
					tempobj = popstack();
					if (tempobj == o_false) {
						skip(i);
						anInterpreter->stacktop++;
						}
					break;
				default: 
					cant_happen(9);
				}
				break;
			}
		}
	/* sorry for the unstructured gotos.
		the sins of unstructuredness seemed less bothersome than
		the problems of not doing the same thing in all places
						-tab
		*/
	do_perform:	/* process perform:withArguments: */
		tempobj = popstack();
		message = symbol_value(tempobj);
		tempobj = popstack();
		numargs = tempobj->size - 1;
		for (i = 0; i <= numargs; i++)
			push(tempobj->inst_var[i]);
		/* fall through into do_send */

		/* do_send - call courier to send a message */
	do_send:
		receiver = *(anInterpreter->stacktop - (numargs + 1));
	do_send2:
		decstack(numargs + 1);
		send_mess(anInterpreter, receiver, message,
			anInterpreter->stacktop , numargs);
		return;

		/* do_return - return from a message */
	do_return:
		sender = anInterpreter->sender;
		if (is_interpreter(sender)) {
			if (! is_driver(sender))
				push_object(sender, tempobj);
			link_to_process(sender);
			}
		else {
			terminate_process(runningProcess);
			}
		return;

		/* blk_execute - perform the block execute primitive */
	blk_execute:
		tempobj = popstack();
		if (! is_integer(tempobj)) cant_happen(9);
		numargs = int_value(tempobj);
		sender = block_execute(anInterpreter->sender, 
			(block *) tempvar(0), numargs, &tempvar(1));
		link_to_process(sender);
		return;
}
SHAR_EOF
if test 10870 -ne "`wc -c < 'sources/interp.c'`"
then
	echo shar: error transmitting "'sources/interp.c'" '(should have been 10870 characters)'
fi
fi # end of overwriting check
if test -f 'sources/interp.h'
then
	echo shar: will not over-write existing file "'sources/interp.h'"
else
cat << \SHAR_EOF > 'sources/interp.h'
/*
        Little Smalltalk interpeter definitions
*/
/*
	for interpreters
		t_size = INTERPSIZE
	 	
		creator is a pointer to the interpreter which created
		the current interpreter.  it is zero except in the case 
		of blocks, in which case it points to the creating
		interpreter for a block.  it is NOT a reference, ie,
		the ref_count field of the creator is not incremented when
		this field is set - this avoids memory reference loops.

		stacktop is a pointer to a pointer to an object, however it
		is not considered a reference.   ie, changing stacktop does
		not alter reference counts.
*/

struct interp_struct {
        int	t_ref_count;
	int	t_size;	/* should always be INTERPSIZE */
	struct interp_struct *creator;
	struct interp_struct *sender;
	object 	*bytecodes;
	object	*receiver;
	object  *literals;
	object	*context;
	object  *stack;
	object	**stacktop;
	uchar   *currentbyte;
        };

typedef struct interp_struct interpreter;

extern interpreter *cr_interpreter();

extern object *o_drive;

# define is_driver(x) (o_drive == (object *) x)
SHAR_EOF
if test 1065 -ne "`wc -c < 'sources/interp.h'`"
then
	echo shar: error transmitting "'sources/interp.h'" '(should have been 1065 characters)'
fi
fi # end of overwriting check
if test -f 'sources/lex.c'
then
	echo shar: will not over-write existing file "'sources/lex.c'"
else
cat << \SHAR_EOF > 'sources/lex.c'
/*
	Little Smalltalk lexical analyzer for driver 
		timothy a. budd 12/84
*/
/*
	The source code for the Little Smalltalk System may be freely
	copied provided that the source of all files is acknowledged
	and that this condition is copied with each file.

	The Little Smalltalk System is distributed without responsibility
	for the performance of the program and without any guarantee of
	maintenance.

	All questions concerning Little Smalltalk should be addressed to:

		Professor Tim Budd
		Department of Computer Science
		The University of Arizona
		Tucson, Arizona
		85721
		USA
*/
# include <stdio.h>
# include <ctype.h>
# include <math.h>
# define DRIVECODE
# include "drive.h"

# define MAXTOKEN 100
char toktext[MAXTOKEN];
tok_type t;
enum lextokens token;

extern char *lexptr;
extern double atof();

static char ocbuf = 0;
static int pbbuf[20];

# define input() (ocbuf ? pbbuf[--ocbuf] : *lexptr++ )
# define putbak(c) (pbbuf[ocbuf++] = c)

static char *psuvars[] = {"nil", "true", "false", "smalltalk", 0};
static enum pseuvars psuval[] = {nilvar, truevar, falsevar, smallvar};
static char symbols[] = "\n-()[]!|.;>" ;
static enum lextokens symval[] = {NL, MINUS, LP, RP, LB, RB, BAR, BAR, 
	PERIOD, SEMI, PE};

static enum lextokens lexsave(type)
enum lextokens type;
{	char *w_search();

	if (! (t.c = w_search(toktext, 1)))
		lexerr("cannot create symbol %s", toktext);
	/* assign token, and return value */
	return(token = type);
}

enum lextokens nextlex() {
	register char c;
	register char *p;
	char *q;
	int  i, n, base;
	double d, denom;

	do {			/* read whitespace (including comments) */
		c = input();
		if (c == '\"') {
			while ((c = input()) && c != '\"') ;
			if (c == '\"') c = input();
			else lexerr("unterminated comment", "");
			}
		} while (c == ' ' || c == '\t') ;

	if (!c) return(token = nothing);

	p = toktext;
	*p = c;
	toktext[1] = '\0';

						/* identifiers and keywords */
	if (( c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
		for (*p++ = c; (c = input()) && isalnum(c) ; *p++ = c) ;
		*p = '\0';
		lexsave(0);
		if (c == ':') {
			return(token = KEYWORD);
			}
		else {
			putbak(c);
			if (islower(toktext[0])) {
				for (i = 0; psuvars[i]; i++)
					if (strcmp(toktext, psuvars[i]) == 0) {
						t.p = psuval[i];
						return(token = PSEUDO);
						}
				return(token = LOWERCASEVAR);
				}
			else {
				return(token = UPPERCASEVAR);
				}
			}
		}
	
# define scandigits(x) for(*p++ = c; (c = input()) && isdigit(c) ; *p++ = c) x

	if (c >= '0' && c <= '9') {		/* numbers */
		i = c - '0';
		scandigits( i = 10 * i + (c - '0') );
		if (c == '.' || c == 'e') {
			if (c == '.')
				scandigits();
			if (c == 'e') {
				*p++ = c;
				c = input();
				if (c == '+' || c == '-') {
					*p++ = c; c = input(); }
				scandigits();
				}
			putbak(c);
			*p = '\0';
			t.f = atof(toktext);
			return(token = LITFNUM);
			}
		else if ((c == 'r') && ((i >= 2) && (i <= 36))) {
			base = i;
			i = 0;
			for (*p++ = c; c = input(); *p++ = c) {
				if (isdigit(c)) n = c - '0';
				else if (isupper(c)) n = (c - 'A') + 10;
				else break;
				if (n >= base) break;
				i = base * i + n;
				}
			if (c == '.' || c == 'e') {
				d = (double) i;
				if (c == '.') {
					denom = 1.0 / (double) base;
					for (*p++ = c; c = input(); *p++ = c) {
						if (isdigit(c))
							n = c - '0';
						else if (isupper(c))
							n = (c - 'A') + 10;
						else break;
						if (n >= base) break;
						d += n * denom;
						denom /= base;
						}
					}
				if (c == 'e') {
					*p++ = c;
					c = input();
					if (c == '+' || c == '-') {
						n = c;
						*p++ = c;
						c = input();
						}
					else n = 0;
					i = c - '0';
					scandigits(i = 10 * i + (c - '0'));
					if (n == '-') i = - i;
					d *= pow((double) base, (double) i);
					}
				putbak(c);
				*p = '\0';
				t.f = d;
				return(token = LITFNUM);
				}
			}
		putbak(c);
		*p = '\0';
		t.i = i;
		return(token = LITNUM);
		}

	if (c == '#') {				/* symbol */
		i = 1;
		while (i)
			switch(c = input()) {
				case '\0': case ' ': case '\t': case '\n':
				case '(': case '[': case ')':
					putbak(c);
					i = 0;
					break;
				default:
					*p++ = c;
				}
		if (p == toktext)
			return(token = PS);
		else {
			*p = '\0';
			if ((p - toktext) >= MAXTOKEN) cant_happen(18);
			return(lexsave(LITSYM));
			}
		}

	if (c == '\'') {			/* quoted string */
		do {
			for ( ; (c = input()) && c != '\'' ; *p++ = c) ;
			c = input();
			if (c == '\'') *p++ = '\'';
			} while (c == '\'');
		putbak(c);
		*p = '\0';
		if ((p - toktext) >= MAXTOKEN) cant_happen(18);
		t.c = toktext;
		return(token = LITSTR);
		}

	if (c == ':') {				/* colon or argument name */
		c = input();
		if (c == '=') 
			return(token = ASSIGN);
		else if (isalnum(c)) {
			for (*p++ = c; isalnum(c = input()); *p++ = c );
			putbak(c);
			*p = '\0';
			return(lexsave(COLONVAR));
			}
		putbak(c);
		return(lexsave(BINARY));
		}

	if (c == '<') {			/* assign, less than or primitive */
		*p++ = c; *p = '\0';
		c = input();
		if (c == '-')
			return(token = ASSIGN);
		for (p = q = "primitive"; *p && *p == c; p++)
			c = input();
		putbak(c);
		if (*p) {
			for (p--; p >= q; p--) putbak(*p);
			return(lexsave(BINARY));
			}
		else 
			return(token = PRIMITIVE);
		}

	if (c == '.') {			/* number or period */
		c = input();
		if (c >= '0' && c <= '9') {
			putbak(c);		/* reparse with digit */
			putbak('.');		/* inserted on front */
			putbak('0');		/* so it looks like */
			return(nextlex());	/* a number */
			}
		putbak(c);
		return(token = PERIOD);
		}

	if (c == '\\') {		/* binary or hidden newline */
		c = input();
		if (c == '\n')
			return(nextlex());
		putbak(c);
		return(lexsave(BINARY));
		}

	if (c == '$') {			/* literal character or binary */
		c = input();
		if (c) {
			t.i = c;
			return(token = LITCHAR);
			}
		return(lexsave(BINARY));
		}

	for (i = 0; symbols[i]; i++)
		if (c == symbols[i])
			return(lexsave(symval[i]));

	return(lexsave(BINARY));
}
SHAR_EOF
if test 5964 -ne "`wc -c < 'sources/lex.c'`"
then
	echo shar: error transmitting "'sources/lex.c'" '(should have been 5964 characters)'
fi
fi # end of overwriting check
if test -f 'sources/lexcmd.c'
then
	echo shar: will not over-write existing file "'sources/lexcmd.c'"
else
cat << \SHAR_EOF > 'sources/lexcmd.c'
/*
	Little Smalltalk
		misc lexer related routines
		timothy a. budd 12/84
*/
/*
	The source code for the Little Smalltalk System may be freely
	copied provided that the source of all files is acknowledged
	and that this condition is copied with each file.

	The Little Smalltalk System is distributed without responsibility
	for the performance of the program and without any guarantee of
	maintenance.

	All questions concerning Little Smalltalk should be addressed to:

		Professor Tim Budd
		Department of Computer Science
		The University of Arizona
		Tucson, Arizona
		85721
		USA
*/
# include <stdio.h>
# include "env.h"
# include <ctype.h>
# ifdef	OPEN3ARG
# include <fcntl.h>
# endif	OPEN3ARG

extern char toktext[];

/* dolexcommand - read a ) type directive, and process it */
dolexcommand(p)
char *p;
{       char *q;

	/* replace trailing newline with end of string */
	for (q = p; *q && *q != '\n'; q++);
	if (*q == '\n') *q = '\0';

        switch( *++p) {
           case '!': 
# ifndef NOSYSTEM
		system(++p); 
# endif
		break;

           case 'e': for (++p; isspace(*p); p++);
		     if (! lexedit(p)) lexinclude(p);
                     break;

           case 'i': for (++p; isspace(*p); p++);
                     lexinclude(p);
                     break;

           case 'r': for (++p; isspace(*p); p++);
                     lexread(p);
                     break;

	   case 's': for(++p; isspace(*p); p++);
		     dosave(p);
		     break;

	   case 'l': for(++p; isspace(*p); p++);
		     doload(p);
		     break;

           default:  lexerr("unknown command %s", toktext);
           }
}

/* doload/dosave routines written by nick buchholz */
/*
	doload and dosave routines make the following assumptions
	1. version is the first global variable declared in main.
	2. main is the first procedure seen by the loader
	3. the loader allocates memory in the order it sees the procedures
	4. memory is laid out as on the vax 780 under 4.2

	on other machines any or all of these might be false and the
	doload/dosave routines will not work
*/
extern int version;

dosave(p) char *p;{
    int fd; 
    char *start, *end, *sbrk(); 
    unsigned int length, len;
    int dlen;

# ifdef OPEN3ARG
    if ((fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
# endif
# ifndef OPEN3ARG
    if ((fd = creat(p, 0666)) == -1)
# endif
   	fprintf(stderr,"can't open: %s\n",p);

    start = (char *) &version;
    end = sbrk(0);
    length = end - start;

    write(fd, &version, sizeof(int));
    write(fd, &start, sizeof(char *));
    write(fd, &length, sizeof(unsigned int));

    for (len = 0; len < length; len += dlen) {
	dlen = ((length - len) > 512) ? 512 : (length - len);
	if (dlen != write(fd, start + len, dlen)) {
		cant_happen(23);
		}
	}

    fprintf(stderr,"%u bytes written\n",len);

    close(fd);
}

# ifdef ENVSAVE
extern char **environ;
# endif

doload(p) char *p;{
    int fd; 
    char *start, *end, *brk(); 
    unsigned int length, len; 
    int dlen;
    int test;
# ifdef ENVSAVE
    char **evsave;
# endif

# ifdef OPEN3ARG
    if ((fd = open(p, O_RDONLY, 0)) == -1)
# endif
# ifndef OPEN3ARG
    if ((fd = open(p, 0 )) == -1)
# endif
	fprintf(stderr,"no such context as: %s\n", p);

    else {
	read(fd, &test, sizeof(int));
	read(fd, &start, sizeof(char *));
	read(fd, &length, sizeof(unsigned int));

	if ((test != version) || (start != (char *) &version))
	    fprintf(stderr,"%s: not a valid context file for version %d\n", 
				p, version);
	else {
	    start = (char *) &version;
	    end = brk(start + length + 1);
# ifdef ENVSAVE
	    evsave = environ;
# endif

    	    for (len = 0; len < length; len += dlen) {
		dlen = ((length - len) > 512) ? 512 : (length - len);
		if (dlen != read(fd, start + len, dlen)) {
			cant_happen(23);
			}
		}
# ifdef ENVSAVE
	   environ = evsave;
# endif
	    fprintf(stderr,"%u bytes read\n",len);
	}
	close(fd);
    }
}

/* lexread - read commands from a file */
lexread(name)
char *name;
{	FILE *fd;

	fd = fopen(name, "r");
	if (fd == NULL) {
		fprintf(stderr,"can't open %s\n", name);
		}
	else {
		set_file(fd);
		}
}

/* lexinclude - parse a class and include the class description */
lexinclude(name)
char *name;
{  char template[60], cmdbuf[120];
   int  i;

# ifndef NOSYSTEM
   gettemp(template);
   sprintf(cmdbuf,"%s %s >%s", PARSER, name, template);
   i = system(cmdbuf);
   if (i == 0)
   	lexread(template);
# endif
# ifdef NOSYSTEM
   fprintf(stderr,")i does not work on this system\n");
# endif
}

/* lexedit - edit a class description */
int lexedit(name)
char *name;
{	char *e, buffer[100], *getenv();

# ifndef NOSYSTEM
	e = getenv("EDITOR");
	if (!e) e = "ed";
	sprintf(buffer,"%s %s", e, name);
	return(system(buffer));
# endif
# ifdef NOSYSTEM
	fprintf(stderr,")e does not work on this system\n");
	return(1);
# endif
}
SHAR_EOF
if test 4830 -ne "`wc -c < 'sources/lexcmd.c'`"
then
	echo shar: error transmitting "'sources/lexcmd.c'" '(should have been 4830 characters)'
fi
fi # end of overwriting check
if test -f 'sources/line.c'
then
	echo shar: will not over-write existing file "'sources/line.c'"
else
cat << \SHAR_EOF > 'sources/line.c'
/*
	Little Smalltalk

		line grabber - does lowest level input for command lines.
*/
/*
	The source code for the Little Smalltalk System may be freely
	copied provided that the source of all files is acknowledged
	and that this condition is copied with each file.

	The Little Smalltalk System is distributed without responsibility
	for the performance of the program and without any guarantee of
	maintenance.

	All questions concerning Little Smalltalk should be addressed to:

		Professor Tim Budd
		Department of Computer Science
		The University of Arizona
		Tucson, Arizona
		85721
		USA
*/
# include <stdio.h>
# include "object.h"
# include "primitive.h"

# define MAXINCLUDE  10
# define MAXBUFFER  1200		/* text buffer */

static FILE *fdstack[MAXINCLUDE];
static int fdtop = -1;

static char buffer[MAXBUFFER];
static char *buftop = buffer;
char *lexptr = buffer;
static enum {empty, half, filled} bufstate = empty;
int inisstd = 0;
extern object *o_tab;

/* set file - set a file on the file descriptor stack */
set_file(fd)
FILE *fd;
{
	if ((++fdtop) > MAXINCLUDE)
		cant_happen(18);
	fdstack[fdtop] = fd;
	if (fd == stdin) inisstd = 1;
	else inisstd = 0;
}

/* line-grabber - read a line of text 
	do blocked i/o if blocked is nonzero,
	otherwise do non-blocking i/o */

int line_grabber(block)
int block;
{
	/* if it was filled last time, it is now empty */
	if (bufstate == filled) {
		bufstate = empty;
		buftop = buffer;
		lexptr = buffer;
		}

	if ( ! block)
		return(0); /* for now, only respond to blocked requests*/
	else while (bufstate != filled) {
		if (fdtop < 0) {
			fprintf(stderr,"no files to read\n");
			return(-1);
			}
		if (inisstd && o_tab)
			primitive(RAWPRINT, 1, &o_tab);
		if (fgets(buftop, MAXBUFFER, fdstack[fdtop]) == NULL) {
			bufstate = empty;
			if (fdstack[fdtop] != stdin)
				fclose(fdstack[fdtop]);
			if (--fdtop < 0) return(-1);
			inisstd = (fdstack[fdtop] == stdin);
			}
		else {
			bufstate = half;
			while (*buftop) buftop++;
			if (*(buftop-1) == '\n') {
				if (*(buftop-2) == '\\') {
					buftop -= 2;
					}
				else {
					if ((buftop - buffer) > MAXBUFFER)
						cant_happen(18);
					*buftop = '\0';
					bufstate = filled;
					}
				}
			}	
		}
	return(bufstate == filled);
}
SHAR_EOF
if test 2244 -ne "`wc -c < 'sources/line.c'`"
then
	echo shar: error transmitting "'sources/line.c'" '(should have been 2244 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0