# Spreadsheet Calculator release of Sun Oct 10 15:20:24 EDT 1982
# "This file contains the sources for the Unix Spreadsheet Calculator.  Just"
# "execute it as a shell command file in an empty directory and the individual"
# "files will be built.  Compile it with "make".  The manual is in sc.1."
# ""
# "	This program is in the public domain."
# "	Feel free to pass it on or to modify it."
echo Extracting sc.1
cat > sc.1 <<'//gosysin dd *'
.TH SC 1
.SH NAME
sc \- spreadsheet calculator
.SH SYNOPSIS
.B sc
[
.I file
]

.SH DESCRIPTION
.I Sc
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 \fIsc\fR is running, the screen is divided into three regions: the top
line is for entering commands, the second line is for messages from
\fIsc\fR, and the rest form a window looking at the table.  \fIsc\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.

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

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

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

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

.IP ^C
Exit from \fIsc\fR.

.IP ^G
Abort the current long command.

.IP ^H
Backspace one character.

.IP ^L
Redraw the screen.

.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.

.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.

.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.

.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.

.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.

.IP ?
Types a brief helpful message.

.IP "
Enter a label for the current entry.

.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.

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

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

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

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

.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.

.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.

.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.

.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.

.IP d
Delete this row.

.IP D
Delete this column.



.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: *,/; +,-;
<,=,>,<=,>=; &; |; ?.

.TP 15
e+e
Addition.

.TP 15
e-e
Subtraction.

.TP 15
e*e
Multtplication.

.TP 15
e/e
Division.

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

.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.

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

.TP 15
&,|
Boolean connectives.

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

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

.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.
//gosysin dd *
echo Extracting sc.h
cat > sc.h <<'//gosysin dd *'
/*	TC	A Table Calculator
 *		Common definitions
 *
 *		James Gosling, September 1982
 *
 */



#define MAXROWS 50
#define MAXCOLS 20

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;
};

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

/* 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();
//gosysin dd *
echo Extracting gram.y
cat > gram.y <<'//gosysin dd *'
/*	SC	A Spreadsheet Calculator
 *		Command and expression parser
 *
 *		James Gosling, September 1982
 *
 */



%{
#include "sc.h"
%}

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

%type <enode> e term
%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_LET
%token S_WRITE
%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++;
				  precision[$2] = $4; }
	|	S_GET STRING	{ readfile ($2); }
	|	S_PUT STRING	{ writefile ($2); }
	|	S_WRITE STRING	{ printfile ($2); }
	|	/* nothing */
	|	error;

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

reduce_op:	'+'		{ $$ = '+'; }
	|	'*'		{ $$ = '*'; }
	;

term:		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)); }
	;
//gosysin dd *
echo Extracting interp.c
cat > interp.c <<'//gosysin dd *'
/*	SC	A Spreadsheet Calculator
 *		Expression interpreter and assorted support routines.
 *
 *		James Gosling, September 1982
 *
 */



#include "sc.h"
#include <stdio.h>

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 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;
	    }
    }
}

EvalAll () {
    register i,j;
    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;
		    p->flags |= (is_changed|is_valid);
		}
	    }
}

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++;
}

constant (e)
register struct enode *e; {
    return e==0 || e->op == O_CONST
	|| (e->op != O_VAR
	 && (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 != O_VAR && e->op !=O_CONST) {
	    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++;
    }
}

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;
	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) {
    register struct ent *p;
    sprintf (line, "let r%dc%d = ", row, col);
    linelim = strlen(line);
    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);
	}
}

printfile (fname) {
    FILE *f = fopen(fname, "w");
    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;
	linelim = 0;
	for (p = &tbl[row][col=0]; col<=maxcol; col++, p++) {
	    if (*p) {
		char *s;
		while (linelim<c) line[linelim++] = ' ';
		linelim = c;
		if ((*p)->flags&is_valid) {
		    sprintf (line+linelim,"%*.*f",fwidth[col],precision[col],
				(*p)->v);
		    linelim += strlen (line+linelim);
		}
		if (s = (*p)->label) {
		    register char *d;
		    d = line+((*p)->flags&is_leftflush
			? c : c-strlen(s)+fwidth[col]);
		    while (d>line+linelim) line[linelim++] = ' ';
		    if (d<line) d = line;
		    while (*s) *d++ = *s++;
		    if (d-line>linelim) linelim = d-line;
		}
	    }
	    c += fwidth [col];
	}
	fprintf (f,"%.*s\n",linelim,line);
    }
    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;
}
//gosysin dd *
echo Extracting lex.c
cat > lex.c <<'//gosysin dd *'
/*	SC	A Spreadsheet Calculator
 *		Lexical analyser
 *
 *		James Gosling, September 1982
 *
 */



#include <curses.h>
#include "sc.h"
#include <ctype.h>
#include "ytab.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 (isdigit(*p)) {
	register long v = 0;
	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=='"') {
	yylval.sval = ++p;
	while (*p && *p!='"') p++;
	*p = 0;
	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++%20),40);
	clrtoeol();
	printw (fmt,a,b,c);
}

help () {
    dbline = 0;
/*	   "====================!===================" */
    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 redraw screen");
    debug ("|^J open new row      ^V type var name");
    debug ("|");
    debug ("|=  enter new value   ?  help (this msg)");
    debug ("|\"  label             e  edit value");
    debug ("|<  left flush str    >  right flush str");
    debug ("|g  get database      p  put database");
    debug ("|w  write listing     f  set format");
    debug ("|r  open row here     c  open col here");
    debug ("|d  delete this row   D  delete this col");
    debug ("|");
    debug ("| ***operators***");
    debug ("|e+e   add            e-e   subtract");
    debug ("|e*e   multiply       e/e   divide");
    debug ("|+/v:v sum region     e?e:e conditional");
    debug ("|<,=,>,<=,>= relations  &,| booleans");
}
//gosysin dd *
echo Extracting sc.c
cat > sc.c <<'//gosysin dd *'
/*	SC	A Spreadsheet Calculator
 *		Main driver
 *
 *		James Gosling, September 1982
 *
 */



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

int linelim = -1;

error (fmt,a,b,c,d,e) {
    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 < COLS - 4 && 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)
		    mvaddstr (r,
			    (*p) -> flags & is_leftflush
			    ? c : c - strlen (s) + fwidth[col],
			    s);
	    }
	    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)

main (argc, argv)
char  **argv; {
    int     running = 1;
    register char   c;
    int     edistate = -1;
    int     arg = 1;
    int     narg;
    int     nedistate = -1;
    {
	register    i;
	for (i = 0; i < MAXCOLS; i++) {
	    fwidth[i] = 10;
	    precision[i] = 2;
	}
    }
    initscr ();
    clear ();
    raw ();
    noecho ();
    error ("Welcome to the Spreadsheet Calculator, type '?' for help.");
    if (argc > 1)
	readfile (argv[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 = getchar ()) < ' ')
	    switch (c) {
		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;
		    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;
		    }
		    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): 
		    if (linelim > 0) {
			sprintf (line + linelim, "r%dc%d", currow, curcol);
			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 'f': 
			    sprintf (line, "format [for column] %d [is] ",
					curcol);
			    linelim = strlen (line);
			    break;
			case 'p': 
			    sprintf (line, "put [database into] \"");
			    linelim = strlen (line);
			    break;
			case 'g': 
			    sprintf (line, "get [database from] \"");
			    linelim = strlen (line);
			    break;
			case 'w': 
			    sprintf (line, "write [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;
		    }
	edistate = nedistate;
	arg = narg;
    }
    move (LINES - 1, 0);
    refresh ();
    noraw ();
    echo ();
    endwin ();
}

writefile (fname)
char *fname; {
    register FILE *f = fopen (fname, "w");
    register struct ent **p;
    register r, c;
    if (f==0) {
	error ("Can't create %s", fname);
	return;
    }
    fprintf (f, "# This data file was generated by the Spreadsheet Calculator.\
\n# You almost certainly shouldn't edit it.\n\n");
    for (c=0; c<MAXCOLS; c++)
	if (fwidth[c] != 10 || precision[c] != 2)
	    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);
}

readfile (fname)
char *fname; {
    register FILE *f = fopen (fname, "r");
    if (f==0) {
	error ("Can't read %s", fname);
	return;
    }
    erasedb ();
    while (fgets(line,sizeof line,f)) {
	linelim = 0;
	if (line[0] != '#') yyparse ();
    }
    fclose (f);
    linelim = -1;
}

erasedb () {
    register r, c;
    for (c = 0; c<maxcol; c++) {
	fwidth[c] = 10;
	precision[c] = 2;
    }
    for (r = 0; r<maxrow; r++) {
	register struct ent **p = &tbl[r][0];
	for (c=maxcol; --c>=0; 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++;
}

closerow (r)
register r; {
    register struct ent **p;
    register c;
    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++;
}

opencol (cs) {
    register r;
    register struct ent **p;
    register c;
    register lim = maxcol-cs+1;
    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++;
}

closecol (cs) {
    register r;
    register struct ent **p;
    register c;
    register lim = maxcol-cs;
    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;
    }
    maxcol--;
    FullUpdate++;
}

//gosysin dd *
echo Extracting eres.sed
cat > eres.sed <<'//gosysin dd *'
/%token.*K_/!d
/%token.*K_\(.*\)/s//	"\1",	K_\1,/
//gosysin dd *
echo Extracting sres.sed
cat > sres.sed <<'//gosysin dd *'
/%token.*S_/!d
/%token.*S_\(.*\)/s//	"\1",	S_\1,/
//gosysin dd *
echo Extracting makefile
cat > makefile <<'//gosysin dd *'
CFLAGS=-g
sc: sc.o lex.o gram.o interp.o
	cc -g sc.o lex.o gram.o interp.o -lcurses -ltermcap -o sc
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 -c gram.c;rm -f gram.c
	sed<gram.y>experres.h -f eres.sed;sed<gram.y>statres.h -f sres.sed
//gosysin dd *
echo Extracting apartments.sc
cat > apartment.sc <<'//gosysin dd *'
# 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
//gosysin dd *
echo Extracting expense.sc
cat > expense.sc <<'//gosysin dd *'
# 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
//gosysin dd *
