home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GEMini Atari
/
GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso
/
files
/
language
/
mjc2
/
mjcdoc
/
ss.c
< prev
Wrap
C/C++ Source or Header
|
1988-03-31
|
21KB
|
1,061 lines
/*
* spread sheet program
* commands are copy, read, write, print, blank, format, quit
* labels begin with ' and are limited in length
* numbers are stored fixed point (2 decimal places) in longs
*/
#include "stdio.h"
/* sizes */
#define MAXROW 100
#define MAXCOL 25
#define MAXLINE 100
#define MAXNODE 5000
#define MAXSTR 16
/* screen locations */
#define FRAMEW 5
#define FRAMEH 1
#define CURCELL 22
#define MSG 23
/* default values for width, format, and justify */
#define DEFWID 10
#define DEFFMT 2
#define DEFJST 'l'
/* screen and keyboard defines */
#define ESC 27
#define DEL 127
#define HELP 0x6200
#define UNDO 0x6100
#define INS 0x5200
#define CLR 0x4700
#define UP 0x4800
#define DOWN 0x5000
#define LEFT 0x4B00
#define RIGHT 0x4D00
#define BACKSP 8
#define LJUST 'l'
#define RJUST 'r'
/* cell types */
#define FREE 0
#define STRING 1
#define VALUE 2
#define CELL 3
#define ERR 4
#define ADD '+'
#define SUB '-'
#define MUL '*'
#define DIV '/'
#define NEG '_'
#define SUM '@'
/* cell structures */
typedef struct { int type; long a, b, c, d; } Node;
typedef struct { int type; char s[MAXSTR]; } String;
typedef union { Node n; String s; } Cell;
Cell *cell[MAXROW][MAXCOL]; /* cell pointers */
Cell space[MAXNODE]; /* Cell space */
Cell *nextfree; /* free list of nodes */
Cell extra; /* an extra one when empty */
int freecnt; /* count of free nodes */
char linebuf[MAXLINE]; /* keyboard input buffer */
char showbuf[MAXLINE]; /* buffer for show routine */
char filename[MAXLINE]; /* load/save file name */
char prname[MAXLINE]; /* print file name */
char prwin[MAXLINE]; /* print window */
int crow, ccol; /* current cursor row and col */
int frow, lrow; /* first and last displayed row */
int fcol, lcol; /* first and last displayed col */
char width[MAXCOL]; /* width of the columns */
char format[MAXCOL]; /* format of the columns */
char justify[MAXCOL]; /* justification of the cols */
int loc[MAXCOL]; /* screen location of the cols */
char *parstr; /* string for expr parser */
char tokstr[MAXLINE]; /* string for next token */
int reframe; /* need to reframe */
int redisp; /* need to recalc the display */
main(argc, argv) char *argv[]; {
int c;
init();
if (argc > 1)
loadss(argv[1]);
while (1) {
display();
switch (c = get()) {
case 'b': blank(); break;
case 'c': copy(); break;
case 'd': delete(); break;
case 'e': edit(); break;
case 'f': setformat(); break;
case 'g': go(); break;
case 'i': insert(); break;
case 'l': load(); break;
case 'p': print(); break;
case 'q': quit(); break;
case 's': save(); break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '\'': case '(': case '=':
enter(c);
break;
case UP: case DOWN: case LEFT: case RIGHT:
movecur(c);
break;
default:
help();
break;
}
}
}
/* initialize the spreadsheet */
init() {
int r, c;
for (r = 0; r < MAXROW; r++)
for (c = 0; c < MAXCOL; c++)
cell[r][c] = NULL;
crow = ccol = 0;
frow = fcol = 0;
reframe = redisp = 1;
for (c = 0; c < MAXCOL; c++) {
width[c] = DEFWID;
format[c] = DEFFMT;
justify[c] = DEFJST;
}
nextfree = NULL;
freecnt = 0;
for (r = 0; r < MAXNODE; r++)
free(&space[r]);
extra.n.type = ERR;
erase();
}
/* draw a frame on the screen displaying current window rows and cols */
frame() {
int sz, i;
lrow = frow + 20;
if (lrow > MAXROW) lrow = MAXROW;
sz = FRAMEW;
for (lcol = fcol; lcol < MAXCOL && sz + width[lcol] < 80; lcol++) {
loc[lcol] = sz;
sz += width[lcol];
}
move(0, FRAMEW);
reverse(1);
for (i = fcol; i < lcol; i++) {
sprintf(linebuf, "%c..............................", 'a'+i);
outstr(stdout, width[i], LJUST, linebuf);
}
for (i = frow; i < lrow; i++) {
move(FRAMEH + i - frow, 0);
clrline();
sprintf(linebuf, "%d.....", i);
outstr(stdout, FRAMEW, LJUST, linebuf);
}
reverse(0);
clrbelow();
redisp = 1;
}
/* refresh the contents of the current display window */
display() {
int r, c, n;
Cell *p;
char *content();
if (reframe) frame();
if (redisp) {
for (r = frow; r < lrow; r++) {
for (c = fcol; c < lcol; c++) {
if (p = cell[r][c]) {
move(FRAMEH+r-frow, loc[c]);
value(stdout, c, p);
}
}
}
}
move(0, 0); /* how much free space left */
n = ((long)freecnt * 100L) / (long)MAXNODE;
sprintf(linebuf, "%d%%", n);
outstr(stdout, FRAMEW, LJUST, linebuf);
move(CURCELL, 0); /* what is the content of current cell */
clrbelow();
printf("%c%d: %s", ccol+'a', crow, content(cell[crow][ccol], 0));
move(FRAMEH+crow-frow, loc[ccol]); /* place cursor */
reframe = redisp = 0;
}
/* output the value of the cell */
value(fp, c, p) FILE *fp; Cell *p; {
int i, wid, fmt, jst;
char *s, *ntoa();
long n, eval();
wid = width[c];
fmt = format[c];
jst = justify[c];
if (p == NULL)
outstr(fp, wid, jst, "");
else if (p->s.type == STRING)
outstr(fp, wid, jst, p->s.s);
else {
n = eval(p);
outstr(fp, wid, jst, ntoa(n, fmt));
}
}
/* get a line from a file */
rdline(fp, bp) FILE *fp; char *bp; {
int i, c;
for (i = 0; (c = getc(fp)) != '\n' && c != EOF; i++)
if (c >= ' ')
*bp++ = c;
*bp = 0;
return i;
}
/* get a line from the keyboard */
char *
getline(prompt, start) char *prompt, *start; {
int i, j, n, c, plen, change;
move(MSG, 0);
clrline();
for (plen = 2; *prompt; plen++)
put(*prompt++);
ps("? ");
i = 0;
if (start) {
while (linebuf[i] = start[i])
put(linebuf[i++]);
}
n = i;
while ((c = get()) != '\r') {
change = 0;
switch (c) {
case LEFT:
if (i) i--;
break;
case RIGHT:
if (i < n) i++;
break;
case BACKSP:
if (i == 0) break;
i--;
case DEL:
if (i == n) break;
n--;
for (j = i; j < n; j++)
linebuf[j] = linebuf[j+1];
change++;
break;
case ESC:
return NULL;
break;
case INS:
c = ' ';
default:
if (c < ' ' || c > DEL)
break;
for (j = n; j > i; j--)
linebuf[j] = linebuf[j-1];
n++;
linebuf[i++] = c;
change++;
break;
}
if (change) {
j = (i ? i-1 : 0);
move(MSG, plen+j);
while (j < n)
put(linebuf[j++]);
put(' ');
}
move(MSG, plen+i);
}
linebuf[n] = 0;
return (n ? linebuf : NULL);
}
/* move the cursor, if move goes outside the window, adjust frame */
movecur(c) {
switch (c) {
case UP:
if (crow) crow--;
if (crow < frow) {
frow--;
reframe++;
}
break;
case DOWN:
if (crow < MAXROW-1) crow++;
if (crow >= lrow) {
frow++;
reframe++;
}
break;
case LEFT:
if (ccol) ccol--;
if (ccol < fcol) {
fcol--;
reframe++;
}
break;
case RIGHT:
if (ccol < MAXCOL-1) ccol++;
if (ccol >= lcol) {
fcol++;
reframe++;
}
break;
}
}
/* time to go */
quit() {
char *s;
if (s = getline("quit", "yes")) {
if (*s == 'y') {
move(24, 0);
exit(0);
}
}
}
/* expression entry parsing */
char *
next() {
int c;
char *s;
while ((c = *parstr) && c <= ' ')
parstr++;
s = tokstr;
if (alphanum(c)) {
while (alphanum(*parstr))
*s++ = *parstr++;
}
else *s++ = *parstr++;
*s = 0;
return tokstr;
}
/* create a new node */
Cell *
mkcell(t, a, b, c, d) long a, b, c, d; {
Node *p;
if ((p = nextfree) == NULL) {
move(MSG, 0);
printf("out of space");
return &extra;
}
else {
freecnt--;
nextfree = p->a;
p->type = t;
p->a = a;
p->b = b;
p->c = c;
p->d = d;
return p;
}
}
/* parse the string (pointed at by parstr) into an expression tree */
Cell *
parse() {
char *s;
Cell *x, *factor(), *tail();
x = factor();
s = next();
x = tail(x, *s);
}
/* more parsing, here we look for operators */
Cell *
tail(x, op) Cell *x; {
int p;
char *s;
Cell *y, *factor();
while (1) {
if (!(p = prec(op))) return x;
y = factor();
s = next();
if (prec(*s) > p)
y = tail(y, *s);
x = mkcell(op, x, y);
op = *s;
}
}
/* return the precedence of the given operator */
prec(op) {
switch (op) {
case '+': case '-': return 1;
case '*': case '/': return 2;
case '@': return 3;
default: return 0;
}
}
/* parse a factor of an expression */
Cell *
factor() {
char *s;
int i, c, cr, cc;
long aton();
Cell *x, *y;
if (!(s = next()))
return NULL;
switch (c = *s) {
case '\'':
x = mkcell(STRING, 0L, 0L);
s = x->s.s;
for (i = 0; i < MAXSTR && (*s++ = *parstr++); i++)
;
return x;
case '-':
x = factor();
return mkcell(NEG, x, 0L);
case '(':
return parse();
default:
if (c >= 'a' && c <= 'z') {
y = cc = c - 'a';
x = cr = atoi(s+1);
return mkcell(
check(cr, cc) ? CELL : ERR, x, y);
}
else if (c >= '0' && c <= '9') {
x = aton(s);
return mkcell(VALUE, x, 0);
}
else {
move(MSG, 0);
printf("bad factor: %s", s);
return NULL;
}
}
}
/* set the value of a cell */
setvalue(r, c, x) long x; {
Cell *p;
if (p = cell[r][c])
free(p);
cell[r][c] = x;
}
/* free up all space associated the given pointer */
free(p) Node *p; {
switch (p->type) {
case ADD: case SUB: case MUL: case DIV: case SUM:
free(p->b);
case NEG:
free(p->a);
}
p->type = FREE;
p->a = nextfree;
nextfree = p;
freecnt++;
}
/* get a filename from the user and load a spreadsheet */
load() {
char *s;
if (s = getline("load file", filename)) {
loadss(s);
}
}
/* load a spreadsheet file */
loadss(name) char *name; {
FILE *fp;
int c;
char *s;
strcpy(filename, name);
if (fp = fopen(name, "r")) {
init();
while (rdline(fp, parstr = linebuf)) {
s = next();
switch (c = *s) {
case '=':
s = next();
ccol = *s - 'a';
crow = atoi(s+1);
if (check(crow, ccol))
setvalue(crow, ccol, parse());
break;
case 'f':
if (s = next()) {
c = atoi(s);
if (s = next())
width[c] = atoi(s);
if (s = next())
format[c] = atoi(s);
if (s = next())
justify[c] = *s;
}
break;
}
}
reframe = redisp = 1;
chkframe();
fclose(fp);
}
else {
printf(" can't open ", name);
get();
}
}
/* save a spreadsheet on a file */
save() {
int r, c;
FILE *fp;
char *name, *content();
if (name = getline("save file", filename)) {
strcpy(filename, name);
fp = fopen(name, "w");
for (r = 0; r < MAXROW; r++)
for (c = 0; c < MAXCOL; c++)
if (cell[r][c]) {
fprintf(fp, "= %c%d %s\n",
c+'a', r,
content(cell[r][c], 0));
}
for (c = 0; c < MAXCOL; c++)
if (width[c] != DEFWID ||
format[c] != DEFFMT ||
justify[c] != DEFJST)
fprintf(fp, "f %d %d %d %c\n",
c, width[c], format[c], justify[c]);
fclose(fp);
}
}
/* print a report onto a file */
print() {
char *s;
FILE *fp;
int r, c, trow, tcol, brow, bcol;
if (!(s = getline("print window", prwin))) return;
strcpy(prwin, s);
if (!window(s, &trow, &tcol, &brow, &bcol)) return;
if (s = getline("file name", NULL)) {
strcpy(prname, s);
fp = fopen(s, "w");
for (r = trow ; r <= brow; r++) {
for (c = tcol; c <= bcol; c++)
value(fp, c, cell[r][c]);
putc('\n', fp);
}
fclose(fp);
}
}
/* prompt for a cell name and move the cursor to that cell */
go() {
char *p;
if (p = getline("go to cell", NULL)) {
if (*p >= 'a' && *p <= 'z') {
ccol = *p - 'a';
crow = atoi(p+1);
if (crow < 0) crow = 0;
if (crow >= MAXROW) crow = MAXROW-1;
if (ccol < 0) ccol = 0;
if (ccol >= MAXCOL) ccol = MAXCOL-1;
chkframe();
}
}
}
chkframe() {
if (crow < frow || crow >= lrow ||
ccol < fcol || ccol >= lcol) {
frow = crow;
fcol = ccol;
reframe = 1;
}
}
/* make a copy of a cell */
Cell *
copyx(p, sr, sc, dr, dc) Node *p; {
int r, c;
Cell *x, *y;
if (p == NULL) return NULL;
switch (p->type) {
case ADD: case SUB: case MUL: case DIV: case SUM:
x = copyx(p->a, sr, sc, dr, dc);
y = copyx(p->b, sr, sc, dr, dc);
return mkcell(p->type, x, y);
case CELL:
r = p->a - sr + dr;
c = p->b - sc + dc;
return mkcell(
check(r, c) ? CELL : ERR, (long)r, (long)c);
default:
return mkcell(p->type,
p->a, p->b, p->c, p->d);
}
}
/* prompt for where to copy to, and then copy it */
copy() {
char *s;
Cell *src;
int r, c, trow, tcol, brow, bcol;
if (!(s = getline("destination", NULL)))
return;
if (!window(s, &trow, &tcol, &brow, &bcol))
return;
src = cell[crow][ccol];
for (r = trow; r <= brow; r++)
for (c = tcol; c <= bcol; c++)
setvalue(r, c, copyx(src, crow, ccol, r, c));
redisp = 1;
}
/* insert a row or a column */
insert() {
char *s;
if (s = getline("insert (row or col)", NULL)) {
if (*s == 'r')
insrow();
else if (*s == 'c')
inscol();
else return;
reframe = 1;
}
}
insrow() {
int r, c;
Cell *p;
for (r = MAXROW-2; r >= crow; r--) {
for (c = 0; c < MAXCOL; c++) {
p = copyx(cell[r][c], r, c, r+1, c);
setvalue(r+1, c, p);
}
}
for (c = 0; c < MAXCOL; c++)
setvalue(crow, c, NULL);
}
inscol() {
int r, c;
Cell *p;
for (c = MAXCOL-2; c >= ccol; c--) {
width[c+1] = width[c];
format[c+1] = format[c];
justify[c+1] = justify[c];
for (r = 0; r < MAXROW; r++) {
p = copyx(cell[r][c], r, c, r, c+1);
setvalue(r, c+1, p);
}
}
width[ccol] = DEFWID;
format[ccol] = DEFFMT;
justify[ccol] = DEFJST;
for (r = 0; r < MAXROW; r++)
setvalue(r, ccol, NULL);
}
/* delete a row or a column */
delete() {
char *s;
if (s = getline("delete (row or col)", NULL)) {
if (*s == 'r')
delrow();
else if (*s == 'c')
delcol();
else return;
reframe = 1;
}
}
delrow() {
int r, c;
Cell *p;
for (r = crow; r < MAXROW-2; r++) {
for (c = 0; c < MAXCOL; c++) {
p = copyx(cell[r+1][c], r+1, c, r, c);
setvalue(r, c, p);
}
}
}
delcol() {
int r, c;
Cell *p;
for (c = ccol; c < MAXCOL-2; c++) {
width[c] = width[c+1];
format[c] = format[c+1];
justify[c] = justify[c+1];
for (r = 0; r < MAXROW; r++) {
p = copyx(cell[r][c+1], r, c+1, r, c);
setvalue(r, c, p);
}
}
}
/* enter a new expression into the cell */
enter(c) {
char str[2];
if (c != '=')
sprintf(str, "%c", c);
else *str = 0;
if (parstr = getline("enter", str)) {
setvalue(crow, ccol, parse());
redisp = 1;
}
}
/* fill the line buffer with the (unevaluated) contents of the cell */
char *
content(p, cp) Cell *p; {
*linebuf = 0;
showx(p, cp);
return linebuf;
}
/* recursive support routine for content() */
showx(p, cp) Node *p; {
int op, np;
char *ntoa();
if (p == NULL) return;
switch (op = p->type) {
case ERR:
sprintf(showbuf, "ERR");
break;
case FREE:
sprintf(showbuf, "FREE");
break;
case STRING:
sprintf(showbuf, "'%s", ((String *)p)->s);
break;
case CELL:
sprintf(showbuf, "%c%d", (short) (p->b + 'a'), (short) p->a);
break;
case VALUE:
sprintf(showbuf, "%s", ntoa(p->a, 2));
break;
case ADD: case SUB: case MUL: case DIV: case SUM:
np = prec(op);
if (np < cp)
strcat(linebuf, "(");
showx(p->a, np);
sprintf(linebuf, "%s%c", linebuf, p->type);
showx(p->b, np);
if (np < cp)
strcat(linebuf, ")");
*showbuf = 0;
break;
case NEG:
strcat(linebuf, "-");
showx(p->a, cp);
*showbuf = 0;
break;
default:
sprintf(showbuf, "ERR%d", p->type);
break;
}
strcat(linebuf, showbuf);
}
/* erase the current cell */
blank() {
char *s;
if (s = getline("blank this cell", "yes")) {
if (*s == 'y') {
setvalue(crow, ccol, NULL);
move(FRAMEH+crow-frow, loc[ccol]);
outstr(stdout, width[ccol], LJUST, "");
}
}
}
/* edit the contents of the current cell */
edit() {
char *s;
s = content(cell[crow][ccol], 0);
if (parstr = getline("edit", s)) {
setvalue(crow, ccol, parse());
redisp = 1;
}
}
/* prompt for new column formats and change them */
setformat() {
char *s;
int w;
sprintf(linebuf, "%d", format[ccol]);
if (s = getline("fixed decimal", linebuf)) {
w = atoi(s);
format[ccol] = (w > 2 ? 2 : w);
redisp = 1;
}
sprintf(linebuf, "%d", width[ccol]);
if (s = getline("column width", linebuf)) {
w = atoi(s);
width[ccol] = (w < 2 ? 2 : w);
reframe = 1;
}
sprintf(linebuf, "%c", justify[ccol]);
if (s = getline("left or right justify", linebuf)) {
if (*s == 'l' || *s == 'r') {
justify[ccol] = *s;
reframe = 1;
}
}
}
/* display a help message */
help() {
move(MSG, 0);
printf("cell entry: type \"= expr\" or \"'label\" ");
move(MSG+1, 0);
printf(
"commands: blank copy delete edit format goto insert load print quit save");
get();
}
/* evaluate a cell entry */
long
eval(p) Node *p; {
int x, y;
long a, b, sum();
if (p == NULL) return 0L;
switch (p->type) {
case FREE: case STRING: case ERR:
return 0L;
case VALUE:
return p->a;
case CELL:
x = p->a;
y = p->b;
return eval(cell[x][y]);
case SUM:
return sum(p->a, p->b);
break;
case ADD:
a = eval(p->a);
b = eval(p->b);
return a+b;
case SUB:
a = eval(p->a);
b = eval(p->b);
return a-b;
case MUL:
a = eval(p->a);
b = eval(p->b);
return (a*b)/100;
case DIV:
a = eval(p->a);
b = eval(p->b);
return (a*100)/b;
case NEG:
a = eval(p->a);
return -a;
}
}
long
sum(lp, rp) Node *lp, *rp; {
long n;
int br, bc, er, ec, r, c;
n = 0L;
if (lp->type == CELL && rp->type == CELL) {
br = lp->a;
bc = lp->b;
er = rp->a;
ec = rp->b;
for (r = br; r <= er; r++)
for (c = bc; c <= ec; c++)
n += eval(cell[r][c]);
}
return n;
}
/* parse a window definition, e.g. "a0.z99" */
window(s, tr, tc, br, bc) char *s; int *tr, *tc, *br, *bc; {
*tc = *s - 'a';
*tr = atoi(s+1);
while (*s && *s++ != '.')
;
if (*s) {
*bc = *s - 'a';
*br = atoi(s+1);
}
else {
*bc = *tc;
*br = *tr;
}
return check(*tr, *tc) && check(*br, *bc);
}
/* range check on a row and column */
check(r, c) { return r >= 0 && r < MAXROW && c >= 0 && c < MAXCOL; }
/* convert a string to an integer */
atoi(s) char *s; {
int n, c;
if (s == NULL) return 0;
for (n = 0; isdig(c = *s++); )
n = n * 10 + c - '0';
return n;
}
/* convert a string to a "fixed point" number (i.e. "123.45") */
long
aton(s) char *s; {
int c, i;
long n;
n = 0L;
if (s == NULL) return 0L;
for (n = 0; isdig(c = *s++); )
n = n * 10 + (c - '0');
if (c == '.') c = *s++;
for (i = 0; i < 2; i++) {
n = n * 10;
if (isdig(c)) {
n = n + (c - '0');
c = *s++;
}
}
return n;
}
/* check the types of the characters */
isdig(c) { return c >= '0' && c <= '9'; }
alphanum(c) {
if (c >= 'a' && c <= 'z') return 1;
if (c >= 'A' && c <= 'Z') return 1;
if (c == '.') return 1;
return isdig(c);
}
/* convert the fixed point number back to a string */
char *
ntoa(n, fmt) long n; {
int i, neg;
if (neg = n < 0)
n = -n;
i = 16;
linebuf[--i] = 0;
do {
linebuf[--i] = n % 10 + '0';
n = n / 10;
if (i == 13) linebuf[--i] = '.';
} while (n);
while (i > 11) {
if (i == 13) linebuf[--i] = '.';
else linebuf[--i] = '0';
}
if (fmt == 0)
linebuf[12] = 0;
else if (fmt == 1)
linebuf[14] = 0;
if (neg) linebuf[--i] = '-';
return &linebuf[i];
}
/* output a string of width n to the stream fp */
outstr(fp, wid, jst, s) FILE *fp; char *s; {
int i;
if (jst == RJUST) {
for (i = 0; s[i]; i++)
;
for ( ; i < wid; i++)
putc(' ', fp);
}
for (i = 0; i < wid && *s; i++)
putc(*s++, fp);
if (jst == LJUST) {
for ( ; i < wid; i++)
putc(' ', fp);
}
}
/* keyboard input and screen output/control */
get() {
long c;
c = trap(1, 7);
if (c & 0xFF)
return (short)c; /* ascii character */
else return (short)(c >> 8); /* scan code */
}
move(row, col) { put(ESC); put('Y'); put(row+' '); put(col+' '); }
erase() { put(ESC); put('E'); }
clrline() { put(ESC); put('K'); }
clrbelow() { put(ESC); put('J'); }
reverse(on) { put(ESC); put(on ? 'p' : 'q'); }
put(c) { trap(1, 2, c); }
ps(s) char *s; { while (*s) put(*s++); }