[net.sources] Unix `vc' spreadsheet.

mark@tove.UUCP (Mark Weiser) (01/02/85)

There has been some clamoring for me to post this, so I am.  The original
was called `sc', and was declared public domain by James Gosling
a couple years ago.  Since then we have hacked in some features here,
mainly bug fixes and a way to call arbitrary unix programs from the
spreadsheet, pass them spreadsheet arguments, and place the results back.
We call ours `vc' (mainly because we already had an `sc').
James didn't include the infamous Stallman anti-copyright clause in his
source, but we'll carry on the public domain tradition by sending
this on out to whoever.  Enjoy.
------------------------------------------------------------------------
Spoken: Mark Weiser 	ARPA:	mark@maryland	Phone: (301) 454-7817
CSNet:	mark@umcp-cs 	UUCP:	{seismo,allegra}!umcp-cs!mark
USPS: Computer Science Dept., University of Maryland, College Park, MD 20742
----------------------cut here---------------------------------
: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting sc.h'
sed 's/^X//' <<'//go.sysin dd *' >sc.h
X/*	VC	A Table Calculator
 *		Common definitions
 *
 *		original by James Gosling, September 1982
 *		modified by Mark Weiser and Bruce Israel,
 *			University of Maryland
 *
 */



#define MAXROWS 500
#define MAXCOLS 40

struct ent {
    double v;
    char *label;
    struct enode *expr;
    short flags;
    char row, col;
};


struct enode {
    int op;
    union {
	double k;
	struct ent *v;
	struct {
	    struct enode *left, *right;
	} o;
    } e;
};

#define MAXLPTR 100
struct lnode {
	int num;
	struct enode *lptr[MAXLPTR];
};

X/* op values */
#define O_VAR 'v'
#define O_CONST 'k'
#define O_REDUCE(c) (c+0200)

X/* flag values */
#define is_valid     0001
#define is_changed   0002
#define is_lchanged  0004
#define is_leftflush 0010

struct ent *tbl[MAXROWS][MAXCOLS];

int strow, stcol;
int currow, curcol;
int FullUpdate;
int maxrow, maxcol;
int fwidth[MAXCOLS];
int precision[MAXCOLS];
char line[1000];
int linelim;
int changed;
struct enode *new();
struct ent *lookat();
struct enode *copye();
double executeprogram();
struct lnode *addlist(), *newlist();

int modflg;
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 sc.h
	/bin/echo -n '	'; /bin/ls -ld sc.h
fi
/bin/echo 'Extracting interp.c'
sed 's/^X//' <<'//go.sysin dd *' >interp.c
X/*	SC	A Spreadsheet Calculator
 *		Expression interpreter and assorted support routines.
 *
 *		original by James Gosling, September 1982
 *		modified by Mark Weiser and Bruce Israel, 
 *			University of Maryland
 */

#include "sc.h"
#include <stdio.h>
#define DEFCOLDELIM ':'

char *malloc();

double eval(e)
register struct enode *e; {
    if (e==0) return 0;
    switch (e->op) {
	case '+':	return (eval(e->e.o.left) + eval(e->e.o.right));
	case '-':	return (eval(e->e.o.left) - eval(e->e.o.right));
	case '*':	return (eval(e->e.o.left) * eval(e->e.o.right));
	case '/':     {	double denom = eval (e->e.o.right);
			return denom ? eval(e->e.o.left) / denom : 0; }
	case '<':	return (eval(e->e.o.left) < eval(e->e.o.right));
	case '=':	return (eval(e->e.o.left) == eval(e->e.o.right));
	case '>':	return (eval(e->e.o.left) > eval(e->e.o.right));
	case '&':	return (eval(e->e.o.left) && eval(e->e.o.right));
	case '|':	return (eval(e->e.o.left) || eval(e->e.o.right));
	case '?':	return eval(e->e.o.left) ? eval(e->e.o.right->e.o.left)
						 : eval(e->e.o.right->e.o.right);
	case 'm':	return (-eval(e->e.o.right));
	case 'f':	return (eval(e->e.o.right));
	case '~':	return (!eval(e->e.o.right));
	case 'k':	return (e->e.k);
	case 'v':	return (e->e.v->v);
	case 'p':	return (executeprogram(e));
	case O_REDUCE('+'):
	    {	register r,c;
		register struct ent *p;
		register double v = 0;
		register maxr, maxc;
		register minr, minc;
		maxr = ((struct ent *) e->e.o.right) -> row;
		maxc = ((struct ent *) e->e.o.right) -> col;
		minr = ((struct ent *) e->e.o.left) -> row;
		minc = ((struct ent *) e->e.o.left) -> col;
		if (minr>maxr) r = maxr, maxr = minr, minr = r;
		if (minc>maxc) c = maxc, maxc = minc, minc = c;
		for (r = minr; r<=maxr; r++)
		    for (c = minc; c<=maxc; c++)
			if ((p = tbl[r][c]) && p->flags&is_valid)
			    v += p->v;
		return v;
	    }
    }
}

#define MAXPROP 7

EvalAll () {
    int lastct,repct = 0;

    while ((lastct = RealEvalAll()) && (repct++ <= MAXPROP));

    repct--;
}

int RealEvalAll () {
    register i,j;
    int chgct = 0;
    register struct ent *p;
    for (i=0; i<=maxrow; i++)
	for (j=0; j<=maxcol; j++)
	    if ((p=tbl[i][j]) && p->expr) {
		double v = eval (p->expr);
		if (v != p->v) {
		    p->v = v; chgct++;
		    p->flags |= (is_changed|is_valid);
		}
	    }
    return(chgct);
}

struct enode *new(op,a1,a2)
struct enode *a1, *a2; {
    register struct enode *p = (struct enode *) malloc (sizeof (struct enode));
    p->op = op;
    switch (op) {
    case O_VAR: p->e.v = (struct ent *) a1; break;
    case O_CONST: p->e.k = *(double *)&a1;
    default: p->e.o.left = a1; p->e.o.right = a2;
    }
    return p;
}

let (v, e)
struct ent *v;
struct enode *e; {
    efree (v->expr);
    if (constant(e)) {
	v->v = eval(e);
	v->expr = 0;
	efree(e);
    } else
	v->expr = e;
    v->flags |= (is_changed|is_valid);
    changed++;
    modflg++;
}

clearent (v)
struct ent *v; {
    label(v,"",-1);
    v->v = 0;
    v->expr = 0;
    v->flags |= (is_changed);
    v->flags &= ~(is_valid);
    changed++;
    modflg++;
}

constant(e)
register struct enode *e; {
    return e==0 || e->op == O_CONST 
	|| (e->op != O_VAR
	 && e->op != 'p'
	 && (e->op&~0177) != O_REDUCE(0)
	 && constant (e->e.o.left)
	 && constant(e->e.o.right));
}

efree (e)
register struct enode *e; {
    if (e) {
	if (e->op == 'p')
		{ pfree (e); }
	else {
		if (e->op != O_VAR && e->op !=O_CONST 
		    && (e->op&~0177) != O_REDUCE(0))
		{
		    efree (e->e.o.left);
		    efree (e->e.o.right);
		}
		free (e);
	}
    }
}

label (v, s, flushdir)
register struct ent *v;
register char *s; {
    if (v) {
	if (flushdir==0 && v->flags&is_valid) {
	    register struct ent *tv;
	    if (v->col>0 && ((tv=lookat(v->row,v->col-1))->flags&is_valid)==0)
		v = tv, flushdir = 1;
	    else if (((tv=lookat (v->row,v->col+1))->flags&is_valid)==0)
		v = tv, flushdir = -1;
	    else flushdir = -1;
	}
	if (v->label) free(v->label);
	if (s && s[0]) {
	    v->label = (char *) malloc (strlen(s)+1);
	    strcpy (v->label, s);
	} else v->label = 0;
	v->flags |= is_lchanged;
	if (flushdir<0) v->flags |= is_leftflush;
	else v->flags &= ~is_leftflush;
	FullUpdate++;
	modflg++;
    }
}

decodev (v)
register struct ent *v; {
	if (v) sprintf (line+linelim,"r%dc%d",v->row,v->col);
	else sprintf (line+linelim,"VAR?");
	linelim += strlen (line+linelim);
}

decompile(e, priority)
register struct enode *e; {
    if (e) {
	int mypriority;
	switch (e->op) {
	default: mypriority = 99; break;
	case '?': mypriority = 1; break;
	case ':': mypriority = 2; break;
	case '|': mypriority = 3; break;
	case '&': mypriority = 4; break;
	case '<': case '=': case '>': mypriority = 6; break;
	case '+': case '-': mypriority = 8; break;
	case '*': case '/': mypriority = 10; break;
	}
	if (mypriority<priority) line[linelim++] = '(';
	switch (e->op) {
	case 'f':	{   register char *s;
			    for (s="fixed "; line[linelim++] = *s++;);
			    linelim--;
			    decompile (e->e.o.right, 30);
			    break;
			}
	case 'm':	line[linelim++] = '-';
			decompile (e->e.o.right, 30);
			break;
	case '~':	line[linelim++] = '~';
			decompile (e->e.o.right, 30);
			break;
	case 'v':	decodev (e->e.v);
			break;
	case 'k':	sprintf (line+linelim,"%g",e->e.k);
			linelim += strlen (line+linelim);
			break;
	case O_REDUCE('+'):
	case O_REDUCE('*'):
			line[linelim++] = e->op&0177;
			line[linelim++] = '/';
			decodev (e->e.o.left);
			line[linelim++] = ':';
			decodev (e->e.o.right);
			break;
	case 'p':
			{
			int i;
			char *cptr;
			struct lnode *ptr;
			line[linelim++] = 'e';
			line[linelim++] = 'x';
			line[linelim++] = 'e';
			line[linelim++] = 'c';
			line[linelim++] = '(';
			line[linelim++] = '"';
			cptr = (char *)(e->e.o.left);
			while (*cptr)
				line[linelim++] = *cptr++;
			line[linelim++] = '"';
			line[linelim++] = ',';
			ptr = (struct lnode *)(e->e.o.right);
			decompile ( ptr->lptr[0] );
			for( i=1; i<ptr->num; i+=1)
				{
				line[linelim++] = ',';
				decompile ( ptr->lptr[i] );
				}
			line[linelim++] = ')';
			break;
			}
	default:	decompile (e->e.o.left, mypriority);
			line[linelim++] = e->op;
			decompile (e->e.o.right, mypriority+1);
			break;
	}
	if (mypriority<priority) line[linelim++] = ')';
    } else line[linelim++] = '?';
}

editv (row, col) {
    sprintf (line, "let r%dc%d = ", row, col);
    linelim = strlen(line);
    editexp(row,col);
}

editexp(row,col) {
    register struct ent *p;
    p = lookat (row, col);
    if (p->flags&is_valid)
	if (p->expr) {
	    decompile (p->expr);
	    line[linelim] = 0;
	} else {
	    sprintf (line+linelim, "%g", p->v);
	    linelim += strlen (line+linelim);
	}
}

edits (row, col) {
    register struct ent *p = lookat (row, col);
    sprintf (line, "%sstring r%dc%d = \"",
			((p->flags&is_leftflush) ? "left" : "right"),
			row, col);
    linelim = strlen(line);
    sprintf (line+linelim, "%s", p->label);
    linelim += strlen (line+linelim);
}

printfile (fname) {
    FILE *f = fopen(fname, "w");
    char pline[1000];
    int plinelim;
    register row, col;
    register struct ent **p;
    if (f==0) {
	error ("Can't create %s", fname);
	return;
    }
    for (row=0;row<=maxrow; row++) {
	register c = 0;
	plinelim = 0;
	for (p = &tbl[row][col=0]; col<=maxcol; col++, p++) {
	    if (*p) {
		char *s;
		while (plinelim<c) pline[plinelim++] = ' ';
		plinelim = c;
		if ((*p)->flags&is_valid) {
		    sprintf (pline+plinelim,"%*.*f",fwidth[col],precision[col],
				(*p)->v);
		    plinelim += strlen (pline+plinelim);
		}
		if (s = (*p)->label) {
		    register char *d;
		    d = pline+((*p)->flags&is_leftflush
			? c : c-strlen(s)+fwidth[col]);
		    while (d>pline+plinelim) pline[plinelim++] = ' ';
		    if (d<pline) d = pline;
		    while (*s) *d++ = *s++;
		    if (d-pline>plinelim) plinelim = d-pline;
		}
	    }
	    c += fwidth [col];
	}
	fprintf (f,"%.*s\n",plinelim,pline);
    }
    fclose (f);
}

tblprintfile (fname) {
    FILE *f = fopen(fname, "w");
    char pline[1000];
    int plinelim;
    register row, col;
    register struct ent **p;
    char coldelim = DEFCOLDELIM;

    if (f==0) {
	error ("Can't create %s", fname);
	return;
    }
    for (row=0;row<=maxrow; row++) {
	register c = 0;
	plinelim = 0;
	for (p = &tbl[row][col=0]; col<=maxcol; col++, p++) {
	    if (*p) {
		char *s;
		if ((*p)->flags&is_valid) {
		    fprintf (f,"%.*f",precision[col],
				(*p)->v);
		}
		if (s = (*p)->label) {
	            fprintf (f,"%s",s);
		}
	    }
	    fprintf(f,"%c",coldelim);
	}
	fprintf (f,"\n",pline);
    }
    fclose (f);
}

struct enode *copye (e, Rdelta, Cdelta)
register struct enode *e; {
    register struct enode *ret;
    if (e==0) ret = 0;
    else {
	ret = (struct enode *) malloc (sizeof (struct enode));
	*ret = *e;
	switch (ret->op) {
	case 'v':
		ret->e.v = lookat (ret->e.v->row+Rdelta, ret->e.v->col+Cdelta);
		break;
	case 'k':
		break;
	case 'f':
		ret->e.o.right = copye (ret->e.o.right,0,0);
		break;
	default:
		ret->e.o.right = copye (ret->e.o.right,Rdelta,Cdelta);
		ret->e.o.left = copye (ret->e.o.left,Rdelta,Cdelta);
		break;
	}
    }
    return ret;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 interp.c
	/bin/echo -n '	'; /bin/ls -ld interp.c
fi
/bin/echo 'Extracting prog.c'
sed 's/^X//' <<'//go.sysin dd *' >prog.c
X/* Mark Weiser, humble programmer. */
X/* $Log:	prog.c,v $
 * Revision 1.1  84/12/10  23:17:20  mark
 * Initial revision
 *  */
static char rcsid[] = "$Header: /g/mark/vc/prog.c,v 1.1 84/12/10 23:17:20 mark Exp $";
#include <stdio.h>
#include "sc.h"

struct lnode *
newlist(e)
struct enode *e;
	{
	struct lnode *r;
	r = (struct lnode *)malloc(sizeof(struct lnode));
	r->num = 1;
	r->lptr[0] = e;
	return r;
	}

struct lnode *
addlist(l,e)
struct lnode *l;
struct enode *e;
	{
	if (l->num >= MAXLPTR)
		error("Too many arguments");
	l->lptr[l->num] = e;
	l->num += 1;
	return l;
	}

double
executeprogram(e)
struct enode *e;
	{
	char *name;
	struct lnode *l;
	double d,eval();
	FILE *stream,*popen();
	struct enode *p;
	int i;
	char s[10240],ts[128];
	name = (char *)(e->e.o.left) ;
	l = (struct lnode *)(e->e.o.right) ;
	strcpy(s,name);
	strcat(s," ");
	if (l)
		{
		for (i=0; i < l->num; i += 1)
			{
			p = l->lptr[i];
			if (p -> op == 's')	/* new type string */
				strcat(s,p->e.o.left);
			else    {
				sprintf(ts,"%g",eval(p));
				strcat(s,ts);
				}
			strcat(s," ");
			}
		}
	stream = popen(s,"r");
	d = 0.0;
	fscanf(stream,"%lf",&d);
	pclose(stream);
	return d;
	}

saveprogram(v,s,l)
struct ent *v;
char *s;
struct lnode *l;
	{
	v->expr = new('p', s, l);
	v->flags |= (is_changed|is_valid);
	changed++;
	modflg++;
	}

pfree(e)
struct enode *e;
{
struct enode *p;
struct lnode *l;
int i;
free(e->e.o.left);
l = (struct lnode *)(e->e.o.right);
for(i = l->num; i>0 ; i -= 1)
	{
	p = (struct enode *)l->lptr[i];
	if (p->op == 's')
		{
		free(p->e.o.left);
		free(p);
		}
	else	efree(l->lptr[i]);
	}
free(e->e.o.right);
free(e);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 prog.c
	/bin/echo -n '	'; /bin/ls -ld prog.c
fi
/bin/echo 'Extracting sc.c'
sed 's/^X//' <<'//go.sysin dd *' >sc.c
X/*	SC	A Spreadsheet Calculator
 *		Main driver
 *
 *		original by James Gosling, September 1982
 *		modifications by Mark Weiser and Bruce Israel,
 *			University of Maryland
 *
 */


#include <signal.h>
#include <curses.h>
#include "sc.h"

X/* default column width */

#define DEFWIDTH 10
#define DEFPREC   2

char curfile[1024];

int linelim = -1;
int batch = 0;

error (fmt,a,b,c,d,e) {
    if (batch) fprintf(stderr,fmt,a,b,c,d,e);
    else {
	move (1,0);
	clrtoeol ();
	printw (fmt,a,b,c,d,e);
    }
}

int seenerr;

yyerror (err)
char *err; {
    if (seenerr) return;
    seenerr++;
    move (1,0);
    clrtoeol ();
    printw ("%s: %.*s<=%s",err,linelim,line,line+linelim);
}

struct ent *lookat(row,col){
    register struct ent **p = &tbl[row][col];
    if (*p==0) {
	*p = (struct ent *) malloc (sizeof (struct ent));
	if (row>maxrow) maxrow = row;
	if (col>maxcol) maxcol = col;
	(*p)->label = 0;
	(*p)->flags = 0;
	(*p)->row = row;
	(*p)->col = col;
	(*p)->expr = 0;
    }
    return *p;
}

update () {
    register    row,
                col;
    register struct ent **p;
    static  lastmx,
            lastmy;
    int     maxcol;
    int     rows = LINES - 2;
    int     cols;

    if (curcol < stcol)
	stcol = curcol, FullUpdate++;
    if (currow < strow)
	strow = currow, FullUpdate++;
    while (1) {
	register    i;
	for (i = stcol, cols = 0, col = 0;
	     (col + fwidth[i]) < COLS && i < MAXCOLS;
	     i++)
	    col += fwidth[i], cols++;
	if (curcol >= stcol + cols)
	    stcol++, FullUpdate++;
	else
	    break;
    }
    if (currow >= strow + rows)
	strow = currow - rows + 1, FullUpdate++;
    if (FullUpdate) {
	move (2, 0);
	clrtobot ();
    }
    maxcol = stcol + cols - 1;
    for (row = strow + rows - 1; row >= strow; row--) {
	register    c = 0;
	for (p = &tbl[row][col = stcol]; col <= maxcol; col++, p++) {
	    if (*p && ((*p) -> flags & is_changed || FullUpdate)) {
		register    r = row - strow + 2;
		char   *s;
		move (r, c);
		(*p) -> flags &= ~is_changed;
		if ((*p) -> flags & is_valid)
		    printw ("%*.*f", fwidth[col], precision[col], (*p) -> v);
		if (s = (*p) -> label) {
		    char field[1024];

		    strncpy(field,s,fwidth[col]);
		    field[fwidth[col]] = 0;
		    mvaddstr (r,
			    (*p) -> flags & is_leftflush
			    ? c : c - strlen (field) + fwidth[col],
			    field);
		}
	    }
	    c += fwidth[col];
	}
    }
    mvaddstr (lastmy, lastmx, " ");
    lastmy = currow - strow + 2;
    lastmx = 0;
    for (col = stcol; col <= curcol;)
	lastmx += fwidth[col++];
    mvaddstr (lastmy, lastmx, "<");
    move (0, 0);
    clrtoeol ();
    if (linelim >= 0) {
	addstr (">> ");
	addstr (line);
    }
    else
	move (lastmy, lastmx);
    FullUpdate = 0;
}


#define ctl(c) ('c'&037)
			/* non-meta getchar */
#define nmgetch() (getchar() & 0177)

main (argc, argv)
char  **argv; {
    int     inloop = 1;
    register int   c;
    int     edistate = -1;
    int     arg = 1;
    int     narg;
    int     nedistate = -1;
    int	    running = 1;
    {
	register    i;
	for (i = 0; i < MAXCOLS; i++) {
	    fwidth[i] = DEFWIDTH;
	    precision[i] = DEFPREC;
	}
    }
    curfile[0]=0;
    running = 1;

    if (argc > 1 && (! strcmp(argv[1],"-b"))) {
	argc--, argv++;
	batch = 1;
    }

    if (! batch) {
	initscr ();
	clear ();
	raw ();
	noecho ();
    }
    
    if (argc > 1) {
	strcpy(curfile,argv[1]);
	readfile (argv[1],0);
    }
    modflg = 0;
    if (batch) exit(0);
    error ("Welcome to the Spreadsheet Calculator, type '?' for help.");
    while (inloop) { running = 1;
    while (running) {
	nedistate = -1;
	narg = 1;
	if (edistate < 0 && linelim < 0 && (changed || FullUpdate))
	    EvalAll (), changed = 0;
	update ();
	refresh ();
	move (1, 0);
	clrtoeol ();
	fflush (stdout);
	seenerr = 0;
	if ((c = nmgetch ()) < ' ')
	    switch (c) {
		case ctl (z):
		    noraw ();
		    echo ();
		    kill(getpid(),SIGTSTP);

		    /* the pc stops here */

		    raw ();
		    noecho ();
		    break;
		case ctl (r):
		    FullUpdate++;
		    clearok(stdscr,1);
		    break;
		default:
		    error ("No such command  (^%c)", c + 0100);
		    break;
		case ctl (b):
		    while (--arg>=0) if (curcol)
			curcol--;
		    break;
		case ctl (c):
		    running = 0;
		    break;
		case ctl (f):
		    while (--arg>=0) if (curcol < MAXCOLS - 1)
			curcol++;
		    break;
		case ctl (g):
		    linelim = -1;
		    move (1, 0);
		    clrtoeol ();
		    break;
		case ctl (h):
		    while (--arg>=0) if (linelim > 0)
			line[--linelim] = 0;
		    break;
		case ctl (j):
		    if (currow >= MAXCOLS - 1 || maxcol >= MAXCOLS - 1) {
			error ("The table can't be any bigger");
			break;
		    }
		    modflg++;
		    currow++;
		    openrow (currow);
		    for (curcol = 0; curcol <= maxcol; curcol++) {
			register struct ent *p = tbl[currow - 1][curcol];
			if (p) {
			    register struct ent *n;
			    n = lookat (currow, curcol);
			    n -> v = p -> v;
			    n -> flags = p -> flags;
			    n -> expr = copye (p -> expr, 1, 0);
			    n -> label = 0;
			    if (p -> label) {
				n -> label = (char *)
					     malloc (strlen (p -> label) + 1);
				strcpy (n -> label, p -> label);
			    }
			}
		    }
		    for (curcol = 0; curcol <= maxcol; curcol++) {
			register struct ent *p = tbl[currow][curcol];
			if (p && (p -> flags & is_valid) && !p -> expr)
			    break;
		    }
		    if (curcol > maxcol)
			curcol = 0;
		    break;
		case ctl (l):
		    FullUpdate++;
		    break;
		case ctl (m):
		    if (linelim < 0)
			line[linelim = 0] = 0;
		    else {
			linelim = 0;
			yyparse ();
			linelim = -1;
		    }
		    break;
		case ctl (n):
		    while (--arg>=0) if (currow < MAXROWS - 1)
			currow++;
		    break;
		case ctl (p):
		    while (--arg>=0) if (currow)
			currow--;
		    break;
		case ctl (q):
		    break;	/* ignore flow control */
		case ctl (s):
		    break;	/* ignore flow control */
		case ctl (u):
		    narg = arg * 4;
		    nedistate = 1;
		    break;
		case ctl (v):	/* insert variable name */
		    if (linelim > 0) {
			sprintf (line + linelim, "r%dc%d", currow, curcol);
			linelim = strlen (line);
		    }
		    break;
		case ctl (e):	/* insert variable expression */
		    if (linelim > 0) editexp(currow,curcol);
		    break;
		case ctl (a):	/* insert variable value */
		    if (linelim > 0) {
			struct ent *p = tbl[currow][curcol];

			if (p && p -> flags & is_valid) {
			    sprintf (line + linelim, "%.*f",
					precision[curcol],p -> v);
			    linelim = strlen (line);
			}
		    }
		    break;
		}
	else
	    if ('0' <= c && c <= '9' && (linelim < 0 || edistate >= 0)) {
		if (edistate != 0)
		    arg = 0;
		nedistate = 0;
		narg = arg * 10 + (c - '0');
	    }
	    else
		if (linelim >= 0) {
		    line[linelim++] = c;
		    line[linelim] = 0;
		}
		else
		    switch (c) {
			case '.':
			    nedistate = 1;
			    break;
			case '=':
			    sprintf (line, "let r%dc%d = ", currow, curcol);
			    linelim = strlen (line);
			    break;
			case '?':
			    help ();
			    break;
			case '"':
			    sprintf (line, "label r%dc%d = \"",
						currow, curcol);
			    linelim = strlen (line);
			    break;
			case '<':
			    sprintf (line, "leftstring r%dc%d = \"",
				    currow, curcol);
			    linelim = strlen (line);
			    break;
			case '>':
			    sprintf (line, "rightstring r%dc%d = \"",
				    currow, curcol);
			    linelim = strlen (line);
			    break;
			case 'e':
			    editv (currow, curcol);
			    break;
			case 'E':
			    edits (currow, curcol);
			    break;
			case 'f':
			    sprintf (line, "format [for column] %d [is] ",
					curcol);
			    error("Current format is %d %d",
					fwidth[curcol],precision[curcol]);
			    linelim = strlen (line);
			    break;
			case 'p':
			    sprintf (line, "put [database into] \"");
			    if (*curfile)
			    error("default file is '%s'",curfile);
			    linelim = strlen (line);
			    break;
			case 'M':
			    sprintf (line, "merge [database from] \"");
			    linelim = strlen (line);
			    break;
			case 'g':
			    sprintf (line, "get [database from] \"");
			    if (*curfile)
			    error("default file is '%s'",curfile);
			    linelim = strlen (line);
			    break;
			case 'w':
			    sprintf (line, "write [listing to] \"");
			    linelim = strlen (line);
			    break;
			case 'T':	/* tbl output */
			    sprintf (line, "tbl [listing to] \"");
			    linelim = strlen (line);
			    break;
			case 'r':
			    while (--arg>=0) openrow (currow);
			    break;
			case 'd':
			    while (--arg>=0) closerow (currow);
			    break;
			case 'c':
			    while (--arg>=0) opencol (curcol);
			    break;
			case 'D':
			    while (--arg>=0) closecol (curcol);
			    break;
			case 'C':
			    clearent(lookat(currow,curcol));
			    FullUpdate++;
			    break;
			case 'Q':
			    running = 0;
			    break;
			default:
			    if ((c & 0177) != c)
				error("Weird character, decimal '%d'.\n",
					(int) c);
			    else error ("No such command  (%c)", c);
			    break;
		    }
	edistate = nedistate;
	arg = narg;
    }				/* while (running) */
    inloop = modcheck(" before exiting");
    }				/*  while (inloop) */
    move (LINES - 1, 0);
    refresh ();
    noraw ();
    echo ();
    endwin ();
}

modcheck(endstr) char *endstr; {
    if (modflg && curfile[0]) {
	char ch, lin[100];

	move (0, 0);
	clrtoeol ();
	sprintf (lin,"File '%s' is modified, save%s? ",curfile,endstr);
	addstr (lin);
	refresh();
	ch = nmgetch();
	if (ch == 'y' || ch == 'Y') writefile(curfile);
	else if (ch == ctl (g)) return(1);
    }
    return(0);
}
    
writefile (fname)
char *fname; {
    register FILE *f;
    register struct ent **p;
    register r, c;
    char save[1024];

    if (*fname == 0) fname = &curfile[0];

    strcpy(save,fname);

    f = fopen (fname, "w");
    if (f==0) {
	error ("Can't create %s", fname);
	return;
    }

    fprintf (f, "# This data file was generated by the Spreadsheet ");
    fprintf (f, "Calculator.\n");
    fprintf (f, "# You almost certainly shouldn't edit it.\n\n");
    for (c=0; c<MAXCOLS; c++)
	if (fwidth[c] != DEFWIDTH || precision[c] != DEFPREC)
	    fprintf (f, "format %d %d %d\n",c,fwidth[c],precision[c]);
    for (r=0; r<=maxrow; r++) {
	p = &tbl[r][0];
	for (c=0; c<=maxcol; c++, p++)
	    if (*p) {
		if ((*p)->label)
		    fprintf (f, "%sstring r%dc%d = \"%s\"\n",
				(*p)->flags&is_leftflush ? "left" : "right",
				r,c,(*p)->label);
		if ((*p)->flags&is_valid) {
		    editv (r, c);
		    fprintf (f, "%s\n",line);
		}
	    }
    }
    fclose (f);
    strcpy(curfile,save);

    modflg = 0;
    error("File '%s' written.",curfile);
}

readfile (fname,eraseflg)
char *fname; int eraseflg; {
    register FILE *f;
    char save[1024];

    if (*fname == 0) fname = &curfile[0];
    strcpy(save,fname);

    if (eraseflg && strcmp(fname,curfile) && modcheck(" first")) return;

    f = fopen (save, "r");
    if (f==0) {
	error ("Can't read %s", save);
	return;
    }

    if (eraseflg) erasedb ();

    while (fgets(line,sizeof line,f)) {
	linelim = 0;
	if (line[0] != '#') yyparse ();
    }
    fclose (f);
    linelim = -1;
    modflg++;
    if (eraseflg) {
	strcpy(curfile,save);
	modflg = 0;
    }
    EvalAll();
}

erasedb () {
    register r, c;
    for (c = 0; c<maxcol; c++) {
	fwidth[c] = DEFWIDTH;
	precision[c] = DEFPREC;
    }

    for (r = 0; r<=maxrow; r++) {
	register struct ent **p = &tbl[r][0];
	for (c=0; c++<=maxcol; p++)
	    if (*p) {
		if ((*p)->expr) efree ((*p) -> expr);
		if ((*p)->label) free ((*p) -> label);
		free (*p);
		*p = 0;
	    }
    }
    maxrow = 0;
    maxcol = 0;
    FullUpdate++;
}

openrow (rs) {
    register    r;
    register struct ent **p;
    register    c;
    if (maxcol >= MAXCOLS - 1) {
	error ("The table can't be any bigger");
	return;
    }
    for (r = ++maxrow; r > rs; r--)
	for (c = maxcol + 1, p = &tbl[r][0]; --c >= 0; p++)
	    if (p[0] = p[-MAXCOLS])
		p[0] -> row++;
    p = &tbl[rs][0];
    for (c = maxcol + 1; --c >= 0;)
	*p++ = 0;
    FullUpdate++;
    modflg++;
}

closerow (r)
register r; {
    register struct ent **p;
    register c;

    if (r > maxrow) return;
    while (r<maxrow) {
	for (c = maxcol+1, p = &tbl[r][0]; --c>=0; p++)
	    if (p[0] = p[MAXCOLS])
		p[0]->row--;
	r++;
    }
    p = &tbl[maxrow][0];
    for (c=maxcol+1; --c>=0; ) *p++ = 0;
    maxrow--;
    FullUpdate++;
    modflg++;
}

opencol (cs) {
    register r;
    register struct ent **p;
    register c;
    register lim = maxcol-cs+1;
    int i;

    for (i = MAXCOLS - 1; i > cs; i--) {
	fwidth[i] = fwidth[i-1];
	precision[i] = precision[i-1];
    }
    /* fwidth[cs] = DEFWIDTH;
    precision[i] =  DEFPREC;  */

    for (r=0; r<=maxrow; r++) {
	p = &tbl[r][maxcol+1];
	for (c=lim; --c>=0; p--)
	    if (p[0] = p[-1])
		p[0]->col++;
	p[0] = 0;
    }
    maxcol++;
    FullUpdate++;
    modflg++;
}

closecol (cs) {
    register r;
    register struct ent **p;
    register c;
    register lim = maxcol-cs;
    int i;

    if (lim < 0) return;

    for (r=0; r<=maxrow; r++) {
	p = &tbl[r][cs];
	for (c=lim; --c>=0; p++)
	    if (p[0] = p[1])
		p[0]->col++;
	p[0] = 0;
    }

    for (i = cs; i < MAXCOLS - 1; i++) {
	fwidth[i] = fwidth[i+1];
	precision[i] = precision[i+1];
    }

    maxcol--;
    FullUpdate++;
    modflg++;
}

debugout(g,fmt,args) FILE *g; char *fmt; {
    int i,op;

    if (g == 0) g = fopen("debug","a"),op = 1;
    if (g == 0) return;

    _doprnt(fmt, &args, g);

    fflush(g);
    if (op) fclose(g);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 sc.c
	/bin/echo -n '	'; /bin/ls -ld sc.c
fi
/bin/echo 'Extracting lex.c'
sed 's/^X//' <<'//go.sysin dd *' >lex.c
X/*	SC	A Spreadsheet Calculator
 *		Lexical analyser
 *
 *		original by James Gosling, September 1982
 *		modifications by Mark Weiser and Bruce Israel,
 *			University of Maryland
 *
 */



#include <curses.h>
#include "sc.h"
#include <ctype.h>
#include "y.tab.h"

struct key {
    char *key;
    int val;
};

struct key experres[] = {
#include "experres.h"
    0, 0};

struct key statres[] = {
#include "statres.h"
    0, 0};

yylex () {
    register char *p = line+linelim;
    int ret = -1;
    while (isspace(*p)) p++;
    if (*p==0) ret = -1;
    else if (isalpha(*p)) {
	char *tokenst = p;
	register tokenl;
	register struct key *tbl;
	while (isalpha(*p)) p++;
	ret = WORD;
	tokenl = p-tokenst;
	for (tbl = linelim ? experres : statres; tbl->key; tbl++)
		if (((tbl->key[0]^tokenst[0])&0137)==0
		 && tbl->key[tokenl]==0) {
		    register i = 1;
		    while (i<tokenl && ((tokenst[i]^tbl->key[i])&0137)==0)
		        i++;
		    if (i>=tokenl) {
			ret = tbl->val;
			break;
		    }
		}
	if (ret==WORD) {
	    linelim = p-line;
	    yyerror ("Unintelligible word");
	}
    } else if ((*p == '.') || isdigit(*p)) {
	register long v = 0;
	if (*p != '.') {
	    do v = v*10 + (*p-'0');
	    while (isdigit(*++p));
	}
	if (*p=='.') {
	    double fval = v;
	    double digit = 1.0/10.0;
	    while (isdigit (*++p)) fval += (*p-'0')*digit, digit *= 1.0/10.0;
	    ret = FNUMBER;
	    yylval.fval = fval;
	} else {
	    ret = NUMBER;
	    yylval.ival = v;
	}
    } else if (*p=='"') {
	/* This storage is never freed.  Oh well.  -MDW */
	char *ptr;
	ptr = (char *)malloc(256);
	yylval.sval = ptr;
	p += 1;
	while (*p && *p!='"') *ptr++ = *p++;
	*ptr = 0;
	if (*p) p += 1;
	ret = STRING;
    } else if (*p=='[') {
	while (*p && *p!=']') p++;
	if (*p) p++;
	linelim = p-line;
	return yylex();
    } else ret = *p++;
    linelim = p-line;
    return ret;
}


int dbline;

debug (fmt, a, b, c) {
	move (3+(dbline++%22),40);
	clrtoeol();
	printw (fmt,a,b,c);
}

help () {
    dbline = 0;
X/*	   "====================!===================" */
    debug ("|^N next row          ^P previous row");
    debug ("|^F next column       ^B previous column");
    debug ("|^C exit              ^G erase command");
    debug ("|^H erase char        ^L fix screen");
    debug ("|^J open new row      ^V type var name");
    debug ("|^A type var value    ^E type var expr");
    debug ("|^R redraw screen     ?  help (this msg)");
    debug ("|");
    debug ("|=  enter new value   C  clear entry");
    debug ("|\"  label             f  set format");
    debug ("|<  left flush str    >  right flush str");
    debug ("|g  get database      p  put database");
    debug ("|w  write listing     T  write tbl fmt");
    debug ("|r  open row here     c  open col here");
    debug ("|d  delete this row   D  delete this col");
    debug ("|E  edit string       e  edit value");
    debug ("|M  merge in database");
    debug ("|");
    debug ("| ***operators***");
    debug ("|+,-,*,/   add,subtract,multiply,divide");
    debug ("|+/v:v sum region     e?e:e conditional");
    debug ("|<,=,>,<=,>= relations  &,| booleans");
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 lex.c
	/bin/echo -n '	'; /bin/ls -ld lex.c
fi
/bin/echo 'Extracting gram.y'
sed 's/^X//' <<'//go.sysin dd *' >gram.y
X/*	SC	A Spreadsheet Calculator
 *		Command and expression parser
 *
 *		original by James Gosling, September 1982
 *		modified by Mark Weiser and Bruce Israel,
 *			University of Maryland
 *
 */



%{
#include "sc.h"
%}

%union {
    long ival;
    double fval;
    struct ent *ent;
    struct enode *enode;
    char *sval;
    struct lnode *lnode;
}

%type <lnode> elist 
%type <enode> e term elistterm
%type <ent> var
%token <sval> STRING
%token <ival> NUMBER
%type <ival> row, col
%token <fval> FNUMBER
%token <sval> WORD
%type <ival> reduce_op
%token S_FORMAT
%token S_LABEL
%token S_LEFTSTRING
%token S_RIGHTSTRING
%token S_GET
%token S_PUT
%token S_MERGE
%token S_LET
%token S_WRITE
%token S_TBL
%token S_PROGLET
%token K_EXEC
%token K_FIXED
%token K_R
%token K_C

%left '?' ':'
%left '|'
%left '&'
%nonassoc '<' '=' '>'
%left '+' '-'
%left '*' '/'

%%
command:	S_LET var '=' e	{ let ($2, $4); }
	|	S_LABEL var '=' STRING
				{ label ($2, $4, 0); }
	|	S_LEFTSTRING var '=' STRING
				{ label ($2, $4, -1); }
	|	S_RIGHTSTRING var '=' STRING
				{ label ($2, $4, 1); }
	|	S_FORMAT NUMBER NUMBER NUMBER
				{ fwidth[$2] = $3;
				  FullUpdate++;
				  modflg++;
				  precision[$2] = $4; }
	|	S_GET STRING	{ readfile ($2,1); }
	|	S_MERGE STRING	{ readfile ($2,0); }
	|	S_PUT STRING	{ writefile ($2); }
	|	S_WRITE STRING	{ printfile ($2); }
	|	S_TBL STRING	{ tblprintfile ($2); }
	|	/* nothing */
	|	error;

elist:		elist ',' elistterm	{ $$ = addlist( $1 , $3 ) ; }
	|	elistterm	{ $$ = newlist( $1 ); }
	;

elistterm: 	e		{ $$ = $1; }
	|	STRING		{ $$ = new ('s' , $1 , 0 ); }
	;

term:	K_EXEC '(' STRING ',' elist ')' { $$ = new('p', $3, $5); }
	|	var		{ $$ = new ('v', $1); }
	|	K_FIXED term	{ $$ = new ('f', 0, $2); }
	|	reduce_op '/' var ':' var
				{ $$ = new (O_REDUCE($1), $3, $5); }
	|	'(' e ')'	{ $$ = $2; }
	|	'+' term	{ $$ = $2; }
	|	'-' term	{ $$ = new ('m', 0, $2); }
	|	NUMBER		{ $$ = new ('k', (double) $1); }
	|	FNUMBER		{ $$ = new ('k', $1); }
	|	'~' term	{ $$ = new ('~', 0, $2); }
	|	'!' term	{ $$ = new ('~', 0, $2); }
	;

e:		e '+' e		{ $$ = new ('+', $1, $3); }
	|	e '-' e		{ $$ = new ('-', $1, $3); }
	|	e '*' e		{ $$ = new ('*', $1, $3); }
	|	e '/' e		{ $$ = new ('/', $1, $3); }
	|	term
	|	e '?' e ':' e	{ $$ = new ('?', $1, new(':', $3, $5)); }
	|	e '<' e		{ $$ = new ('<', $1, $3); }
	|	e '=' e		{ $$ = new ('=', $1, $3); }
	|	e '>' e		{ $$ = new ('>', $1, $3); }
	|	e '&' e		{ $$ = new ('&', $1, $3); }
	|	e '|' e		{ $$ = new ('|', $1, $3); }
	|	e '<' '=' e	{ $$ = new ('~', 0, new ('>', $1, $4)); }
	|	e '!' '=' e	{ $$ = new ('~', 0, new ('=', $1, $4)); }
	|	e '>' '=' e	{ $$ = new ('~', 0, new ('<', $1, $4)); }
	;

row:		K_R NUMBER	{ $$ = $2; };
col:		K_C NUMBER	{ $$ = $2; };
var:		row col		{ $$ = lookat($1, $2); }
	|	col row		{ $$ = lookat($2, $1); };

reduce_op:	'+'		{ $$ = '+'; }
	|	'*'		{ $$ = '*'; }
	;
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 gram.y
	/bin/echo -n '	'; /bin/ls -ld gram.y
fi
/bin/echo 'Extracting eres.sed'
sed 's/^X//' <<'//go.sysin dd *' >eres.sed
X/%token.*K_/!d
X/%token.*K_\(.*\)/s//	"\1",	K_\1,/
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 eres.sed
	/bin/echo -n '	'; /bin/ls -ld eres.sed
fi
/bin/echo 'Extracting sres.sed'
sed 's/^X//' <<'//go.sysin dd *' >sres.sed
X/%token.*S_/!d
X/%token.*S_\(.*\)/s//	"\1",	S_\1,/
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 sres.sed
	/bin/echo -n '	'; /bin/ls -ld sres.sed
fi
/bin/echo 'Extracting vc.1'
sed 's/^X//' <<'//go.sysin dd *' >vc.1
X.TH VC 1
X.SH NAME
vc \- spreadsheet calculator ("visicalc-like")
X.SH SYNOPSIS
X.B vc
[
X.I file
]

X.SH DESCRIPTION
X.I Vc
is a calculator that is based on rectangular tables, in much the same style
as VisiCalc or T/Maker.  When it is invoked it presents you with an empty
table organised as rows and columns of entries.  Each entry may have a label
string associated with it and an expression.  The expression may be a
constant or it may compute something based on other entries.

When \fIvc\fR is running, the screen is divided into three regions: the top
line is for entering commands, the second line is for messages from
\fIvc\fR, and the rest form a window looking at the table.  \fIvc\fR has two
cursors: an entry cursor (indicated by a '<' on the screen) an a character
cursor (indicated by the terminals hardware cursor).  The entry and
character cursors are often the same, they will differ when a long command
is being typed in the top line.

The following single control character commands are recognized no matter
where the character cursor is.

X.IP ^N
Move the entry cursor to the next row.

X.IP ^P
Move the entry cursor to the previous row.

X.IP ^F
Move the entry cursor forward one column.

X.IP ^B
Move the entry cursor backward one column.

X.IP ^C
Exit from \fIvc\fR.  If you were editing a named file, and you modified
it, then it will ask about saving before exiting.

X.IP ^G
Abort the current long command.

X.IP ^H
Backspace one character.

X.IP ^L
Propogate values around the screen.  A complicated set of rows
and columns may require more than one ^L to stabilize.

X.IP ^R
Redraw the screen.

X.IP ^J
Creates a new row immediatly following the current row.  It is initialized
to be a copy of the current row, with all variable references moved down one
row.  If an expression is to be duplicated with ^J the moving down of a
variable reference may be avoided by using the "fixed" operator.

X.IP ^V
Types, in the long command line, the name of the entry being pointed at by
the entry cursor.  This is used when typing in expressions to refer to
entries in the table.

X.IP ^U\fIn\fR
Sets the numeric argument to the following command to \fIn\fR.  Commands
like ^F and ^B use the numeric argument as the number of times to perform
the operation.  If you aren't entering a long command then the ^U is
unnecessary: repetition count arguments may be entered just as a string of
digits.

X.PP
The following commands are only valid when the character and entry cursors
are the same.  That is, when no long command is being entered.  Most of them
introduce a new long command.

X.IP =
Prompts for an expression which will be evaluated dynamically to produce a
value for the entry pointed at by the entry cursor.  This may be used in
conjunction with ^V to make one entries value be dependent on anothers.

X.IP C
Clears the current entry as if there were none.

X.IP ?
Types a brief helpful message.

X.IP """
Enter a label for the current entry.

X.IP e
Edit the value associated with the current entry.  This is identical to '='
except that the command line starts out containing the old value or
expression associated with the entry.

X.IP E
Edit the string associated with the current entry.  This is the same as
either "leftstring", "rightstring", or "label", with the additional
fact that the command line starts out with the old string.

X.IP <
Associate a string with this entry that will be flushed left against the
left edge of the entry.

X.IP >
Associates a string with this entry that will be flushed right against the
right edge of the entry.

X.IP g
Get a new database from a named file.

X.IP p
Put the current database onto a named file.

X.IP w
Write a listing of the current database in a form that matches its
appearance on the screen.  This differs from the "put" command in that
"put"s files are intended to be reloaded with "get", while "write" produces
a file for people to look at.

X.IP T
Write a listing of the current database to a file, but put ":"s between
each field.  This  is useful for tables that will be further formatted
by the
X.I tbl
preprocessor of
X.I nroff.

X.IP f
Sets the output format to be used for printing the numbers in each entry in
the current column.  Type in two numbers which will be the width in
characters of a column and the number of digits which will follow the
decimal point.

X.IP r
Create a new row by moving the row containing the entry cursor, and all
following, down one.  The new row will be empty.

X.IP c
Create a new column by moving the column containing the entry cursor, and
all following, right one.  The new column will be empty.

X.IP d
Delete this row.

X.IP D
Delete this column.


X.IP C
Clear this entry.

X.PP
Expressions that are used with the '=' and 'e' commands have a fairly
conventional syntax.  Terms may be variable names (from the ^V command),
parenthesised expressions, negated terms, and constants.  The +/ term sums
values in rectangular regions of the table (the notation +/ is reminiscent
of apl's additive reduction.)  Terms may be combined using many binary
operators.  Their precedences (from highest to lowest) are: *,/; +,-;
<,=,>,<=,>=; &; |; ?.

X.TP 15
exec("progname",arg1,...,argn)
Start a Bourne shell with "progname" and arg1 through argn as arguments.
The args can be arbitrary vc expressions.  The output of progname is
converted to a floating point number and the result is returned as the
value of exec.

X.TP 15
e+e
Addition.

X.TP 15
e-e
Subtraction.

X.TP 15
e*e
Multiplication.

X.TP 15
e/e
Division.

X.TP 15
+/v:v
Sum all valid (nonblank) entries in the region whose two corners are defined
by the two variable (entry) names given.

X.TP 15
e?e:e
Conditional: If the first expression is true then the value of the second is
returned, otherwise the value of the third is.

X.TP 15
<,=,>,<=,>=
Relationals: true iff the indicated relation holds.

X.TP 15
&,|
Boolean connectives.

X.TP 15
To make a variable not change automatically when a row is duplicated with
^J, put the word \*(lqfixed\*(rq in front of it.  I.e.
r2c1*fixed r3c1 

X.SH FILES
expense.sc \- a sample expense report.

X.SH SEE ALSO
bc(1), dc(1), the VisiCalc or T/Maker manuals.

X.SH BUGS
There should be a */ operator.

Expression reevaluation is done in the same top-to-bottom, left-to-right
manner as is done in other spreadsheet calculators.  This is silly.  A
proper following of the dependency graph with (perhaps) recourse to
relaxation should be implemented.

At most 100 rows and 40 columns.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 vc.1
	/bin/echo -n '	'; /bin/ls -ld vc.1
fi
/bin/echo 'Extracting makefile'
sed 's/^X//' <<'//go.sysin dd *' >makefile
EXDIR=/g/local
MANDIR=/usr/man/man1

CFLAGS=-g

vc: sc.o lex.o gram.o interp.o prog.o
	-mv vc vc.old
	cc ${CFLAGS} sc.o lex.o gram.o interp.o prog.o -lcurses -ltermcap -o vc
lex.o: sc.h y.tab.h
interp.o: sc.h
sc.o: sc.h
gram.o: sc.h y.tab.h
y.tab.h: gram.o
gram.o: gram.y
	yacc -d gram.y;mv y.tab.c gram.c;cc ${CFLAGS} -c gram.c; rm gram.c
	sed<gram.y>experres.h -f eres.sed;sed<gram.y>statres.h -f sres.sed
print:
	(pr gram.y sc.h;cpr interp.c lex.c sc.c) | lpr
clean:
	rm -f *.o *res.h y.tab.h vc vc.old debug core
co: co-all
coall: co-all
co-all:
	co RCS/*

makescript:
	makescript -o vc.shar sc.h interp.c prog.c sc.c lex.c gram.y eres.sed sres.sed vc.1 makefile BUGS

inst-all: inst-vc inst-man

inst-vc: $(EXDIR)/vc

inst-man: $(MANDIR)/vc.1

$(EXDIR)/vc: vc
	cp vc $(EXDIR)

$(MANDIR)/vc.1: vc.1
	cp vc.1 $(MANDIR)
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 makefile
	/bin/echo -n '	'; /bin/ls -ld makefile
fi
/bin/echo 'Extracting BUGS'
sed 's/^X//' <<'//go.sysin dd *' >BUGS
core dump on linefeed (copye). (copye should check for reduce operators (but
	that might not be the core dump bug)).
should malloc larger table when needed.
exec sometimes fails mysteriously for complicated pipes.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 BUGS
	/bin/echo -n '	'; /bin/ls -ld BUGS
fi

-- 
Spoken: Mark Weiser 	ARPA:	mark@maryland	Phone: (301) 454-7817
CSNet:	mark@umcp-cs 	UUCP:	{seismo,allegra}!umcp-cs!mark
USPS: Computer Science Dept., University of Maryland, College Park, MD 20742

mark@tove.UUCP (Mark Weiser) (01/02/85)

I forgot to include any examples in my posting of the spreadsheet.
Here they are:
: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting apartments.sc'
sed 's/^X//' <<'//go.sysin dd *' >apartments.sc
# This data file was generated by the Spreadsheet Calculator.
# You almost certainly shouldn't edit it.

format 0 5 0
format 6 12 2
rightstring r1c1 = "Rent increase"
let r1c2 = 7
leftstring r1c4 = "Apartment block purchase evaluation"
rightstring r2c1 = "Maintenance"
let r2c2 = 20
rightstring r4c1 = "Rent"
rightstring r5c0 = "Year"
rightstring r5c1 = "Income"
rightstring r5c2 = "Maint"
rightstring r5c3 = "Mortgage"
rightstring r5c4 = "Net mnth"
rightstring r5c5 = "Net year"
rightstring r5c6 = "total"
let r6c0 = 1982
let r6c1 = 30000
let r6c2 = r6c1*fixed r2c2/100
let r6c3 = 31000
let r6c4 = r6c1-r6c2-r6c3
let r6c5 = r6c4*12
let r6c6 = r6c5
let r7c0 = r6c0+1
let r7c1 = r6c1*(1+fixed r1c2/100)
let r7c2 = r7c1*fixed r2c2/100
let r7c3 = r6c3
let r7c4 = r7c1-r7c2-r7c3
let r7c5 = r7c4*12
let r7c6 = r7c5+r6c6
let r8c0 = r7c0+1
let r8c1 = r7c1*(1+fixed r1c2/100)
let r8c2 = r8c1*fixed r2c2/100
let r8c3 = r7c3
let r8c4 = r8c1-r8c2-r8c3
let r8c5 = r8c4*12
let r8c6 = r8c5+r7c6
let r9c0 = r8c0+1
let r9c1 = r8c1*(1+fixed r1c2/100)
let r9c2 = r9c1*fixed r2c2/100
let r9c3 = r8c3
let r9c4 = r9c1-r9c2-r9c3
let r9c5 = r9c4*12
let r9c6 = r9c5+r8c6
let r10c0 = r9c0+1
let r10c1 = r9c1*(1+fixed r1c2/100)
let r10c2 = r10c1*fixed r2c2/100
let r10c3 = r9c3
let r10c4 = r10c1-r10c2-r10c3
let r10c5 = r10c4*12
let r10c6 = r10c5+r9c6
let r11c0 = r10c0+1
let r11c1 = r10c1*(1+fixed r1c2/100)
let r11c2 = r11c1*fixed r2c2/100
let r11c3 = r10c3
let r11c4 = r11c1-r11c2-r11c3
let r11c5 = r11c4*12
let r11c6 = r11c5+r10c6
let r12c0 = r11c0+1
let r12c1 = r11c1*(1+fixed r1c2/100)
let r12c2 = r12c1*fixed r2c2/100
let r12c3 = r11c3
let r12c4 = r12c1-r12c2-r12c3
let r12c5 = r12c4*12
let r12c6 = r12c5+r11c6
let r13c0 = r12c0+1
let r13c1 = r12c1*(1+fixed r1c2/100)
let r13c2 = r13c1*fixed r2c2/100
let r13c3 = r12c3
let r13c4 = r13c1-r13c2-r13c3
let r13c5 = r13c4*12
let r13c6 = r13c5+r12c6
let r14c0 = r13c0+1
let r14c1 = r13c1*(1+fixed r1c2/100)
let r14c2 = r14c1*fixed r2c2/100
let r14c3 = r13c3
let r14c4 = r14c1-r14c2-r14c3
let r14c5 = r14c4*12
let r14c6 = r14c5+r13c6
let r15c0 = r14c0+1
let r15c1 = r14c1*(1+fixed r1c2/100)
let r15c2 = r15c1*fixed r2c2/100
let r15c3 = r14c3
let r15c4 = r15c1-r15c2-r15c3
let r15c5 = r15c4*12
let r15c6 = r15c5+r14c6
let r16c0 = r15c0+1
let r16c1 = r15c1*(1+fixed r1c2/100)
let r16c2 = r16c1*fixed r2c2/100
let r16c3 = r15c3
let r16c4 = r16c1-r16c2-r16c3
let r16c5 = r16c4*12
let r16c6 = r16c5+r15c6
let r17c0 = r16c0+1
let r17c1 = r16c1*(1+fixed r1c2/100)
let r17c2 = r17c1*fixed r2c2/100
let r17c3 = r16c3
let r17c4 = r17c1-r17c2-r17c3
let r17c5 = r17c4*12
let r17c6 = r17c5+r16c6
let r18c0 = r17c0+1
let r18c1 = r17c1*(1+fixed r1c2/100)
let r18c2 = r18c1*fixed r2c2/100
let r18c3 = r17c3
let r18c4 = r18c1-r18c2-r18c3
let r18c5 = r18c4*12
let r18c6 = r18c5+r17c6
let r19c0 = r18c0+1
let r19c1 = r18c1*(1+fixed r1c2/100)
let r19c2 = r19c1*fixed r2c2/100
let r19c3 = r18c3
let r19c4 = r19c1-r19c2-r19c3
let r19c5 = r19c4*12
let r19c6 = r19c5+r18c6
rightstring r20c5 = "----------"
let r21c5 = +/r20c5:r5c5
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 apartments.sc
	/bin/echo -n '	'; /bin/ls -ld apartments.sc
fi
/bin/echo 'Extracting expense.sc'
sed 's/^X//' <<'//go.sysin dd *' >expense.sc
# This data file was generated by the Spreadsheet Calculator.
# You almost certainly shouldn't edit it.

leftstring r0c0 = "Sample expense account form."
rightstring r1c1 = "Plane tickets:"
let r1c2 = 0
rightstring r2c1 = "Car rental:"
let r2c2 = 0
rightstring r3c0 = "Miles driven:"
let r3c1 = 0
leftstring r3c2 = "=>"
let r3c2 = r3c1*0.2
leftstring r3c4 = "(person's own car)"
rightstring r4c2 = "Subtotal:"
let r4c3 = +/r4c2:r0c2
leftstring r5c0 = "Hotels"
let r6c2 = 0
rightstring r7c2 = "Subtotal:"
let r7c3 = +/r7c2:r5c2
leftstring r8c0 = "Meals"
let r9c2 = 0
rightstring r10c2 = "Subtotal:"
let r10c3 = +/r10c2:r8c2
leftstring r11c0 = "Miscellaneous expenses"
let r12c2 = 0
rightstring r13c2 = "Subtotal:"
let r13c3 = +/r13c2:r11c2
rightstring r14c3 = "-------------------"
rightstring r15c2 = "Total expenses:"
let r15c3 = +/r14c3:r0c3
leftstring r15c4 = "=>"
let r15c4 = r15c3
rightstring r16c3 = "Cash advanced:"
let r16c4 = 0
rightstring r17c4 = "----------"
rightstring r18c3 = "Amount due person:"
let r18c4 = r15c4>r16c4?r15c4-r16c4:0
rightstring r19c3 = "Amount due company:"
let r19c4 = r16c4>r15c4?r16c4-r15c4:0
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 expense.sc
	/bin/echo -n '	'; /bin/ls -ld expense.sc
fi
-- 
Spoken: Mark Weiser 	ARPA:	mark@maryland	Phone: (301) 454-7817
CSNet:	mark@umcp-cs 	UUCP:	{seismo,allegra}!umcp-cs!mark
USPS: Computer Science Dept., University of Maryland, College Park, MD 20742