[alt.sources] SC - PD Unix Spreadsheet, Part 02/02

sword@vu-vlsi.UUCP (Ronin) (01/28/88)

-------------------CUT HERE, PART 02 ---------------------------------
if test -f 'make.log'
then
	echo shar: will not over-write existing file "'make.log'"
else
cat << \SHAR_EOF > 'make.log'
cc -O -DBSD42  -c sc.c
yacc -d gram.y; mv y.tab.c gram.c

conflicts: 14 shift/reduce, 1 reduce/reduce
cc -O -DBSD42 -c gram.c
sed<gram.y >experres.h -f eres.sed;sed < gram.y > statres.h -f sres.sed
cc -O -DBSD42  -c lex.c
cc -O -DBSD42 -c interp.c
cc -O -DBSD42 -c cmds.c
cc -O -DBSD42 -c crypt.c
cc -O -DBSD42 -c xmalloc.c
cc -O -DBSD42 -c range.c
cc -O -DBSD42  sc.o lex.o gram.o interp.o cmds.o crypt.o xmalloc.o range.o -lm -lcurses -ltermcap -o vcalc
SHAR_EOF
if test 456 -ne "`wc -c < 'make.log'`"
then
	echo shar: error transmitting "'make.log'" '(should have been 456 characters)'
fi
fi # end of overwriting check
if test -f 'makefile.dist'
then
	echo shar: will not over-write existing file "'makefile.dist'"
else
cat << \SHAR_EOF > 'makefile.dist'

# Specify the name of the program.
# All documentation and installation keys on this value.
# 
name=sc
NAME=SC

# This is where the install step puts it.
EXDIR=/a/rgb/bin

# This is where the man page goes.
MANDIR=/usr/man/man1

# All of the source files
SRC=sc.h sc.c lex.c gram.y interp.c cmds.c crypt.c xmalloc.c range.c eres.sed\
	sres.sed makefile cvt.sed 

# The documents in the Archive
DOCS=README $(name).man sc.doc

# Set SIMPLE for lex.c if you don't want arrow keys or lex.c blows up
#SIMPLE=-DSIMPLE

# Set QUICK if you want to enter numbers without "=" first
#QUICK=-DQUICK

# Use this for system V.2
#CFLAGS= -O -DSYSV2
#LDFLAGS=
#LIB=-lm -lcurses

# Use this for system V.3
#CFLAGS= -O -DSYSV3
#LDFLAGS=
#LIB=-lm -lcurses

# Use this for BSD 4.2
#CFLAGS= -O -DBSD42
#LDFLAGS=
#LIB=-lm -lcurses -ltermcap

# Use this for BSD 4.3
CFLAGS= -O -DBSD43
LDFLAGS=
LIB=-lm -lcurses -ltermcap

# Use this for system III (XENIX)
#CFLAGS= -O -DSYSIII
#LDFLAGS= -i
#LIB=-lm -lcurses -ltermcap

# Use this for VENIX
#CFLAGS= -DVENIX -DBSD42 -DV7
#LDFLAGS= -z -i 
#LIB=-lm -lcurses -ltermcap

# The objects
OBJS=sc.o lex.o gram.o interp.o cmds.o crypt.o xmalloc.o range.o

$(name):	$(OBJS)
	cc ${CFLAGS} ${LDFLAGS} ${OBJS} ${LIB} -o $(name)

diff_to_sc:	diff_to_sc.c
	cc ${CFLAGS} -o dtv diff_to_sc.c

pvc:	pvc.c
	cc ${CFLAGS} -o pvc pvc.c
	cp pvc $(EXDIR)/pvc

lex.o:	sc.h y.tab.h gram.o
	cc ${CFLAGS} ${SIMPLE} -c lex.c

sc.o:	sc.h sc.c
	cc ${CFLAGS} ${QUICK} -c sc.c

interp.o:	interp.c sc.h

gram.o:	sc.h y.tab.h

cmds.o: cmds.c sc.h

crypt.o: crypt.c sc.h

range.o: range.c sc.h

y.tab.h:	gram.y

gram.o:	sc.h y.tab.h gram.c
	cc ${CFLAGS} -c gram.c
	sed<gram.y >experres.h -f eres.sed;sed < gram.y > statres.h -f sres.sed

gram.c:	gram.y
	yacc -d gram.y; mv y.tab.c gram.c

clean:
	rm -f *.o *res.h y.tab.h $(name) debug core gram.c 

shar: ${SRC} ${DOCS}
	shar -c -m 55000 -f shar ${DOCS} ${SRC}

lint: sc.h sc.c lex.c gram.c interp.c cmds.c crypt.c
	lint ${CFLAGS} ${SIMPLE} ${QUICK} sc.c lex.c gram.c interp.c cmds.c crypt.c range.c -lcurses -lm

print: sc.h gram.y sc.c lex.c interp.c cmds.c crypt.c 
	prc sc.h gram.y sc.c lex.c interp.c cmds.c crypt.c | lpr

$(name).1:	sc.doc
	sed -e s/pname/$(name)/g -e s/PNAME/$(NAME)/g sc.doc >  $(name).1

$(name).man:	$(name).1
	-mv $(name).man $(name).mold
	nroff -man $(name).1 > $(name).man

install: $(EXDIR)/$(name)

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

$(EXDIR)/$(name): $(name)
	-mv $(EXDIR)/$(name) $(EXDIR)/$(name).old
	strip $(name)
	cp $(name) $(EXDIR)

$(MANDIR)/$(name).1: $(name).1
	cp $(name).1 $(MANDIR)
SHAR_EOF
if test 2566 -ne "`wc -c < 'makefile.dist'`"
then
	echo shar: error transmitting "'makefile.dist'" '(should have been 2566 characters)'
fi
fi # end of overwriting check
if test -f 'range.c'
then
	echo shar: will not over-write existing file "'range.c'"
else
cat << \SHAR_EOF > 'range.c'

/*	SC	A Spreadsheet Calculator
 *		Range Manipulations
 *
 *              Robert Bond, 4/87
 *
 */

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

#ifdef BSD42
#include <strings.h>
#else
#ifndef SYSIII
#include <string.h>
#endif
#endif

char *xmalloc();

static struct range *rng_base;

add_range(name, left, right)
char *name;
struct ent *left, *right;
{
    struct range *r;
    register char *p;
    int len;

    if (find_range(name, strlen(name), (struct ent *)0, (struct ent *)0)) {
	error("Error: range name already defined");
	return;
    }

    if (strlen(name) <=2) {
	error("Invalid range name - too short");
	return;
    }

    for(p=name, len=0; *p; p++, len++)
	if (!((isalpha(*p) && (len<=2)) ||
	    ((isdigit(*p) || isalpha(*p) || (*p == '_')) && (len>2)))) {
	    error("Invalid range name - illegal combination");
	    return;
        }

    r = (struct range *)xmalloc((unsigned)sizeof(struct range));
    r->r_name = name;
    r->r_left = left;
    r->r_right = right;
    r->r_next = rng_base;
    r->r_prev = 0;
    if (rng_base)
        rng_base->r_prev = r;
    rng_base = r;
}

del_range(left, right)
struct ent *left, *right;
{
    register struct range *r;

    if (!(r = find_range((char *)0, 0, left, right))) 
	return;

    if (r->r_next)
        r->r_next->r_prev = r->r_prev;
    if (r->r_prev)
        r->r_prev->r_next = r->r_next;
    else
	rng_base = r->r_next;
    xfree((char *)(r->r_name));
    xfree((char *)r);
}

/* Match on name or lmatch, rmatch */

struct range *
find_range(name, len, lmatch, rmatch)
char *name;
int len;
struct ent *lmatch;
struct ent *rmatch;
{
    struct range *r;
    register char *rp, *np;
    register int c;

    if (name) {
	for (r = rng_base; r; r = r->r_next) {
	    for (np = name, rp = r->r_name, c = len;
		 c && *rp && (*rp == *np);
		 rp++, np++, c--) /* */;
	    if (!c && !*rp)
		return(r);
	}
	return(0);
    }

    for (r = rng_base; r; r= r->r_next) {
	if ((lmatch == r->r_left) && (rmatch == r->r_right)) 
	    return(r);
    }
    return(0);
}

sync_ranges()
{
    register struct range *r;

    r = rng_base;
    while(r) {
	r->r_left = lookat(r->r_left->row, r->r_left->col);
	r->r_right = lookat(r->r_right->row, r->r_right->col);
	r = r->r_next;
    }
}

write_range(f)
FILE *f;
{
    register struct range *r;

    for (r = rng_base; r; r = r->r_next) {
	fprintf(f, "define \"%s\" %s%d", r->r_name, coltoa(r->r_left->col), 
				   r->r_left->row);
	if (r->r_left != r->r_right)
	    fprintf(f, ":%s%d\n", coltoa(r->r_right->col), r->r_right->row);
	else
	    fprintf(f, "\n");
    }
}

list_range(f)
FILE *f;
{
    register struct range *r;

    for (r = rng_base; r; r = r->r_next) {
	fprintf(f, "%-30s %s%d", r->r_name, coltoa(r->r_left->col), 
				   r->r_left->row);
	if (r->r_left != r->r_right)
	    fprintf(f, ":%s%d\n", coltoa(r->r_right->col), r->r_right->row);
	else
	    fprintf(f, "\n");

    }
}

char *
v_name(row, col)
int row, col;
{
    struct ent *v;
    struct range *r;
    static char buf[20];

    v = lookat(row, col);
    if (r = find_range((char *)0, 0, v, v)) {
	return(r->r_name);
    } else {
        sprintf(buf, "%s%d", coltoa(col), row);
	return buf;
    }
}
SHAR_EOF
if test 3211 -ne "`wc -c < 'range.c'`"
then
	echo shar: error transmitting "'range.c'" '(should have been 3211 characters)'
fi
fi # end of overwriting check
if test -f 'sc.c'
then
	echo shar: will not over-write existing file "'sc.c'"
else
cat << \SHAR_EOF > 'sc.c'
/*	SC	A Spreadsheet Calculator
 *		Main driver
 *
 *		original by James Gosling, September 1982
 *		modifications by Mark Weiser and Bruce Israel,
 *			University of Maryland
 *
 *              More mods Robert Bond, 12/86
 *
 */


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

#ifdef BSD42
#include <strings.h>
#else
#ifndef SYSIII
#include <string.h>
#endif
#endif

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

char *xmalloc();

/* default column width */

#define DEFWIDTH 10
#define DEFPREC   2

#define RESCOL 4  /* columns reserved for row numbers */
#define RESROW 3  /* rows reserved for prompt, error, and column numbers */

/* Globals defined in sc.h */

struct ent *tbl[MAXROWS][MAXCOLS];
int strow, stcol;
int currow, curcol;
int savedrow, savedcol;
int FullUpdate;
int maxrow, maxcol;
int fwidth[MAXCOLS];
int precision[MAXCOLS];
char col_hidden[MAXCOLS];
char row_hidden[MAXROWS];
char line[1000];
int changed;
struct ent *to_fix;
int modflg;

char curfile[1024];

int linelim = -1;
int showme = 1;  /* 1 to display the current cell in the top line */
char *rev = "$Revision: 4.1 $";

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;
    if (row < 0)
	row = 0;
    else if (row > MAXROWS-1) 
	row = MAXROWS-1;
    if (col < 0) 
	col = 0;
    else if (col > MAXCOLS-1)
	col = MAXCOLS-1;
    p = &tbl[row][col];
    if (*p==0) {
	*p = (struct ent *) xmalloc ((unsigned)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;
	(*p)->v = (double) 0.0;
    }
    return *p;
}

/*
 * This structure is used to keep ent structs around before they
 * are deleted to allow the sync_refs routine a chance to fix the
 * variable references.
 * We also use it as a last-deleted buffer for the 'p' command.
 */

free_ent(p)
register struct ent *p;
{
    p->next = to_fix;
    to_fix = p;
    p->flags |= is_deleted;
}

flush_saved()
{
    register struct ent *p;
    register struct ent *q;

    if (!(p = to_fix))
	return;
    while (p) {
	clearent(p);
	q = p->next;
	xfree((char *)p);
	p = q;
    }
    to_fix = 0;
}

update () {
    register    row,
                col;
    register struct ent **p;
    static  lastmx,
            lastmy;
    static  char *under_cursor = " ";
    int     maxcol;
    int     maxrow;
    int     rows;
    int     cols;
    register r;

    while (row_hidden[currow])   /* You can't hide the last row or col */
	currow++;
    while (col_hidden[curcol])
	curcol++;
    if (curcol < stcol)
	stcol = curcol, FullUpdate++;
    if (currow < strow)
	strow = currow, FullUpdate++;
    while (1) {
	register    i;
	for (i = stcol, cols = 0, col = RESCOL;
	     (col + fwidth[i]) < COLS-1 && i < MAXCOLS; i++) {
	    cols++;
	    if (col_hidden[i])
		continue;
	    col += fwidth[i];
	}
	if (curcol >= stcol + cols)
	    stcol++, FullUpdate++;
	else
	    break;
    }
    while (1) {
	register    i;
	for (i = strow, rows = 0, row = RESROW;
	     row < LINES && i < MAXROWS; i++) {
	    rows++;
	    if (row_hidden[i])
		continue;
	    row++;
	}
	if (currow >= strow + rows)
	    strow++, FullUpdate++;
	else
	    break;
    }
    maxcol = stcol + cols - 1;
    maxrow = strow + rows - 1;
    if (FullUpdate) {
	register int i;
	move (2, 0);
	clrtobot ();
	standout();
	for (row=RESROW, i=strow; i <= maxrow; i++) {
	    if (row_hidden[i]) 
		continue;
	    move(row,0);
	    printw("%-*d", RESCOL, i);
	    row++;
	}
	move (2,0);
	printw("%*s", RESCOL, " ");
	for (col=RESCOL, i = stcol; i <= maxcol; i++) {
	    if (col_hidden[i])
		continue;
	    move(2, col);
	    printw("%*s", fwidth[i], coltoa(i));
	    col += fwidth[i];
	}
	standend();
    }
    for (row = strow, r = RESROW; row <= maxrow; row++) {
	register    c = RESCOL;
	if (row_hidden[row])
	    continue;
	for (p = &tbl[row][col = stcol]; col <= maxcol; col++, p++) {
	    if (col_hidden[col])
		continue;
	    if (*p && ((*p) -> flags & is_changed || FullUpdate)) {
		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];
	}
	r++;
    }
    
    move(lastmy, lastmx);
    if (inch() == '<')
        addstr (under_cursor);
    lastmy =  RESROW;
    for (row = strow; row < currow; row++)
	if (!row_hidden[row])
		lastmy += 1;
    lastmx = RESCOL;
    for (col = stcol; col <= curcol; col++)
	if (!col_hidden[col])
		lastmx += fwidth[col];
    move(lastmy, lastmx);
    *under_cursor = inch();
    addstr ("<");
    move (0, 0);
    clrtoeol ();
    if (linelim >= 0) {
	addstr (">> ");
	addstr (line);
    } else {
	if (showme) {
	    register struct ent *p;
	    p = tbl[currow][curcol];
	    if (p && ((p->flags & is_valid) || p->label)) {
		if (p->expr || !p->label) {
		    linelim = 0;
		    editexp(currow, curcol);
		} else {
		    sprintf(line, "%s", p->label);
		}
		addstr("[");
		addstr (line);
		addstr("]");
		linelim = -1;
	    } else {
		addstr("[]");
	    }
	}
	move (lastmy, lastmx);
    }
    FullUpdate = 0;
}

main (argc, argv)
char  **argv; {
    int     inloop = 1;
    register int   c;
    int     edistate = -1;
    int     arg = 1;
    int     narg;
    int     nedistate;
    int	    running;
    char    revmsg[80];
    char    *revi;
    char    *pname;

    pname = argv[0];

    {
	register    i;
	for (i = 0; i < MAXCOLS; i++) {
	    fwidth[i] = DEFWIDTH;
	    precision[i] = DEFPREC;
	}
    }
    curfile[0]=0;
    running = 1;

    signals();
    initscr ();
    goraw();
    initkbd();
    if (argc > 1 && !strcmp(argv[1], "-x")) { 
	    Crypt++;
	    argv++;
	    argc--;
    }
    if (argc > 1) {
	strcpy(curfile,argv[1]);
	readfile (argv[1],0);
    }
    modflg = 0;
    strcpy(revmsg, pname);
    for (revi=rev; *revi++ != ':';);
    strcat(revmsg, revi);
    revi = revmsg+strlen(revmsg);
    *--revi = 0;
    strcat(revmsg,"Type '?' for help.");
    error (revmsg);
#ifdef VENIX
    setbuf (stdin, NULL);
#endif
    FullUpdate++;
    while (inloop) { running = 1;
    while (running) {
	nedistate = -1;
	narg = 1;
	if (edistate < 0 && linelim < 0 && (changed || FullUpdate))
	    EvalAll (), changed = 0;
	update();
#ifndef SYSV3
	refresh(); /* 5.3 does a refresh in getch */ 
#endif
	c = nmgetch();
	move (1, 0);
	clrtoeol ();
	fflush (stdout);
	seenerr = 0;
	if ((c < ' ') || ( c == 0177 ))
	    switch (c) {
#if defined(BSD42) || defined (BSD43)
		case ctl (z):
		    deraw();
#ifndef V7
		    kill(getpid(),SIGTSTP);
#endif

		    /* the pc stops here */

		    goraw();
		    break;
#endif
		case ctl (r):
		case ctl (l):
		    FullUpdate++;
		    clearok(stdscr,1);
		    break;
		default:
		    error ("No such command  (^%c)", c + 0100);
		    break;
		case ctl (b):
		    while (--arg>=0) {
			if (curcol)
			    curcol--;
			else
			    error ("At column A");
			while(col_hidden[curcol] && curcol)
			    curcol--;
		    }
		    break;
		case ctl (c):
		    running = 0;
		    break;
		case ctl (f):
		    while (--arg>=0) {
			if (curcol < MAXCOLS - 1)
			    curcol++;
			else
			    error ("The table can't be any wider");
			while(col_hidden[curcol]&&(curcol<MAXCOLS-1))
			    curcol++;
		    }
		    break;
		case ctl (g):
		case ctl ([):
		    linelim = -1;
		    move (1, 0);
		    clrtoeol ();
		    break;
		case 0177:
		case ctl (h):
		    while (--arg>=0) if (linelim > 0)
			line[--linelim] = 0;
		    break;
		case ctl (m):
		case ctl (j):
		    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++;
			else
			    error ("The table can't be any longer");
			while (row_hidden[currow] && (currow < MAXROWS - 1))
			    currow++;
		    }
		    break;
		case ctl (p):
		    while (--arg>=0) {
			if (currow)
			    currow--;
			else
			    error ("At row zero");
			while (row_hidden[currow] && currow)
			    currow--;
		    }
		    break;
		case ctl (q):
		    break;	/* ignore flow control */
		case ctl (s):
		    break;	/* ignore flow control */
		case ctl (t):
		    showme ^= 1;
		    break;
		case ctl (u):
		    narg = arg * 4;
		    nedistate = 1;
		    break;
		case ctl (v):	/* insert variable name */
		    if (linelim > 0) {
			sprintf (line+linelim,"%s", v_name(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
#ifdef QUICK
	    if ('0' <= c && c <= '9' &&  edistate >= 0) {
#else
	    if ('0' <= c && c <= '9' && (linelim < 0 || edistate >= 0)) {
#endif /* QUICK */
		if (edistate != 0) {
		    if (c == '0')      /* just a '0' goes to left col */
			curcol = 0;
		    else {
		        nedistate = 0;
		        narg = c - '0';
		    }
		} else {
		    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 ':':
			    break;	/* Be nice to vi users */
#ifdef QUICK
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
			case '-':
			    sprintf(line,"let %s = %c",
					v_name(currow, curcol), c);
			    linelim = strlen (line);
			    break;
#endif /* QUICK */
			case '=':
			    sprintf(line,"let %s = ",v_name(currow, curcol));
			    linelim = strlen (line);
			    break;
			case '/':
			    error("c:copy  x:erase  f:fill  d:define  u:undefine  s:show");
			    refresh();
			    switch (nmgetch()) {
			    case 'c':
				sprintf(line,"copy [to] %s [from range] ",
					       v_name(currow, curcol));
				linelim = strlen(line);
				break;
			    case 'x':
				sprintf(line,"erase [range] ");
				linelim = strlen(line);
				break;
			    case 'f':
				sprintf(line,"fill [range start inc] ");
				linelim = strlen(line);
				break;
			    case 'd':
				sprintf(line,"define [string range] \"");
				linelim = strlen(line);
				modflg++;
				break;
			    case 'u':
				sprintf(line,"undefine [range] ");
				linelim = strlen(line);
				modflg++;
				break;
			    case 's':
				{
				FILE *f;
				int pid;
				f = openout("| sort | less", &pid);
				if (!f) {
				    error("Cant open pipe to sort");
				    break;
				}
				list_range(f);
				closeout(f, pid);
				break;
				}
			   default:
				error("Invalid region operation");
			    }
			    break;
			case '$':
			    curcol = MAXCOLS - 1;
			    while (!tbl[currow][curcol] && curcol > 0)
				curcol--;
			    break;
			case '#':
			    currow = MAXROWS - 1;
			    while (!tbl[currow][curcol] && currow > 0)
				currow--;
			    break;
			case '^':
			    currow = 0;
			    break;
			case '?':
			    help ();
			    break;
			case '"':
			    sprintf (line, "label %s = \"",
						v_name(currow, curcol));
			    linelim = strlen (line);
			    break;
			case '<':
			    sprintf (line, "leftstring %s = \"",
				    v_name(currow, curcol));
			    linelim = strlen (line);
			    break;
			case '>':
			    sprintf (line, "rightstring %s = \"",
				   v_name(currow, curcol));
			    linelim = strlen (line);
			    break;
			case 'e':
			    editv (currow, curcol);
			    break;
			case 'E':
			    edits (currow, curcol);
			    break;
			case 'f':
			    if (arg == 1)
			        sprintf (line, "format [for column] %s ",
					coltoa(curcol));
			    else {
				sprintf(line, "format [for columns] %s:",
					coltoa(curcol));
				sprintf(line+strlen(line), "%s ",
					coltoa(curcol+arg-1));
			    }
			    error("Current format is %d %d",
					fwidth[curcol],precision[curcol]);
			    linelim = strlen (line);
			    break;
			case 'g':
			    sprintf (line, "goto [v] ");
			    linelim = strlen (line);
			    break;
			case 'P':
			    sprintf (line, "put [database into] \"");
			    if (*curfile)
			        error("Default path 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 'i':
			    switch (get_qual()) {
			    case 'r':
				insertrow(arg);
				break;
			    case 'c':
				insertcol(arg);
				break;
			    default:
				error("Invalid insert command");
				break;
			    }
			    break;
			case 'd':
			    switch (get_qual()) {
			    case 'r':
				deleterow(arg);
				break;
			    case 'c':
				deletecol(arg);
				break;
			    default:
				error("Invalid delete command");
				break;
			    }
			    break;
			case 'v':
			    switch (get_qual()) {
			    case 'r':
				rowvalueize(arg);
				modflg++;
				break;
			    case 'c':
				colvalueize(arg);
				modflg++;
				break;
			    default:
				error("Invalid value command");
				break;
			    }
			    break;
			case 'p':
			    {
			    register qual;
			    qual = get_qual();
			    while (arg--)
			        pullcells(qual);
			    break;
			    }
			case 'x':
			    {
			    register struct ent **p;
			    register int c;
			    flush_saved();
			    for (c = curcol; arg-- && c < MAXCOLS; c++) {
				p = &tbl[currow][c];
				if (*p) {
			            free_ent(*p);
			            *p = 0;
				}
			    }
			    sync_refs();
			    FullUpdate++;
			    }
			    break;
			case 'Q':
			case 'q':
			    running = 0;
			    break;
			case 'h':
			    while (--arg>=0) {
				if (curcol)
				    curcol--;
				else
				    error ("At column A");
				while(col_hidden[curcol] && curcol)
				    curcol--;
			    }
			    break;
			case 'j':
			    while (--arg>=0) {
				if (currow < MAXROWS - 1)
				    currow++;
				else
				    error ("The table can't be any longer");
				while (row_hidden[currow]&&(currow<MAXROWS-1))
				    currow++;
			    }
			    break;
			case 'k':
			    while (--arg>=0) {
				if (currow)
				    currow--;
				else
				    error ("At row zero");
				while (row_hidden[currow] && currow)
				    currow--;
			    }
			    break;
			case 'l':
			    while (--arg>=0) {
				if (curcol < MAXCOLS - 1)
				    curcol++;
				else
				    error ("The table can't be any wider");
				while(col_hidden[curcol]&&(curcol<MAXCOLS-1))
				    curcol++;
			    }
			    break;
			case 'm':
			    savedrow = currow;
			    savedcol = curcol;
			    break;
			case 'c': {
			    register struct ent *p = tbl[savedrow][savedcol];
			    register c;
			    register struct ent *n;
			    if (!p)
				break;
			    FullUpdate++;
			    modflg++;
			    for (c = curcol; arg-- && c < MAXCOLS; c++) {
				n = lookat (currow, c);
				clearent(n);
				n -> flags = p -> flags;
				n -> v = p -> v;
				n -> expr = copye(p->expr,
					    currow - savedrow,
					    c - savedcol);
				n -> label = 0;
				if (p -> label) {
				    n -> label = (char *)
						 xmalloc((unsigned)strlen(p->label)+1);
				strcpy (n -> label, p -> label);
				}
			    }
			    break;
			}
			case 'z':
			    switch (get_qual()) {
			    case 'r':
				hiderow(arg);
				break;
			    case 'c':
				hidecol(arg);
				break;
			    default:
				error("Invalid zap command");
				break;
			    }
			    break;
			case 's':
			    switch (get_qual()) {
			    case 'r':
				rowshow_op();
				break;
			    case 'c':
				colshow_op();
				break;
			    default:
				error("Invalid show command");
				break;
			    }
			    break;
			case 'a':
			    switch (get_qual()) {
			    case 'r':
				while (arg--)
				    duprow();
				break;
			    case 'c':
				while (arg--)
				    dupcol();
				break;
			    default:
				error("Invalid add row/col command");
				break;
			    }
			    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) */
    deraw();
    resetkbd();
    endwin ();
}

goraw()
{
    clear();
    cbreak();
    nonl();
    noecho ();
    FullUpdate++;
}

deraw()
{
    move (LINES - 1, 0);
    clrtoeol();
    refresh();
    nocbreak();
    nl();
    echo();
}

signals()
{
    int quit();
    int timeout();

    signal(SIGINT, SIG_IGN);
    signal(SIGQUIT, quit);
    signal(SIGPIPE, quit);
    signal(SIGTERM, quit);
    signal(SIGALRM, timeout);
    signal(SIGFPE, quit);
    signal(SIGBUS, quit);
}

quit()
{
    deraw();
    resetkbd();
    endwin();
    exit(1);
}

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 != 'n' && ch != 'N')
 	    if (writefile(curfile) < 0)
 		return (1);
	else if (ch == ctl (g) || ch == ctl([)) return(1);
    } else if (modflg) {
	char ch, lin[100];

	move (0, 0);
	clrtoeol ();
	sprintf (lin,"Do you want a chance to save the data? ");
	addstr (lin);
	refresh();
	ch = nmgetch();
	if (ch == 'n' || ch == 'N') return(0);
	else return(1);
      }
    return(0);
}

    
writefile (fname)
char *fname; {
    register FILE *f;
    register struct ent **p;
    register r, c;
    char save[1024];
    int pid;

    if (Crypt) {
	return (cwritefile(fname));
    }

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

    strcpy(save,fname);

    f = openout(fname, &pid);
    if (f==0) {
	error ("Can't create %s", fname);
	return (-1);
    }

    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 %s %d %d\n",coltoa(c),fwidth[c],precision[c]);
    write_range(f);
    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 %s%d = \"%s\"\n",
				(*p)->flags&is_leftflush ? "left" : "right",
				coltoa(c),r,(*p)->label);
		if ((*p)->flags&is_valid) {
		    editv (r, c);
		    fprintf (f, "%s\n",line);
		}
	    }
    }

    closeout(f, pid);

    if (!pid) {
        strcpy(curfile, save);
        modflg = 0;
        error("File '%s' written.",curfile);
    }

    return (0);
}

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

    if (Crypt)  {
	creadfile(fname, eraseflg);
	return;
    }

    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) xfree ((char *)((*p) -> label));
		xfree ((char *)(*p));
		*p = 0;
	    }
    }
    maxrow = 0;
    maxcol = 0;
    FullUpdate++;
}

#if DEBUG
debugout(g,fmt,args) FILE *g; char *fmt; {
    int op;

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

    _doprnt(fmt, &args, g);

    fflush(g);
    if (op) fclose(g);
}
#endif
SHAR_EOF
if test 20711 -ne "`wc -c < 'sc.c'`"
then
	echo shar: error transmitting "'sc.c'" '(should have been 20711 characters)'
fi
fi # end of overwriting check
if test -f 'sc.doc'
then
	echo shar: will not over-write existing file "'sc.doc'"
else
cat << \SHAR_EOF > 'sc.doc'
.TH PNAME 1
.SH NAME
pname \- spread sheet calculator
.SH SYNOPSIS
.B pname
[
.I -x
]
[
.I file
]

.SH DESCRIPTION
The spread sheet calculator
.I pname
is based on rectangular tables much like a financial spread sheet.
When it is invoked it presents you with an empty
table organized as rows and columns of cells.  Each cell 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.

The single option 
.I -x
causes the 
.I put
and
.I get
commands to encrypt and decrypt the data files.  

When \fIpname\fR is running, the screen is divided into four regions. The top
line is for entering commands. The second line is for messages from
\fIpname\fR.
The third line and the first four columns show the row and column numbers.
The rest of the screen forms a window looking at the table.
The screen has two
cursors: a cell cursor (indicated by a '<' on the screen) and a character
cursor (indicated by the terminal's hardware cursor).  The cell and
character cursors are often the same.  They will differ when a command
is being typed on the top line.

Commands which use the terminal's control key
such as ^N will work both when a
command is being typed and when in normal mode.

The cursor control commands and the row, column commands can be 
prefixed by a numeric argument indicating how many times the command
is to be executed.  "^U" can be used before the number if
the number is to be entered while a command is being typed
into the command line.

Cursor control commands:

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

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

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

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

.IP ^H
Backspace one character.

.IP "h, j, k, l"
Alternate cursor controls (left, down, up, right).

.IP "Arrow Keys"
The terminal's arrow keys provide another alternate set of cell
cursor controls if they exist and are supported in the
.I termcap
entry.
Some terminals have arrow keys which conflict
with other control key codes.  For example, a terminal could send ^H when the
back arrow key is depressed.  In these cases, the conflicting arrow key
performs the same function as the key combination it mimics.

.IP 0
Move the cell cursor to column 0 of the current row. 

.IP $
Move the cell cursor to the last valid column in the current row.

.IP ^
Move the cell cursor to row 0 of the current column.

.IP #
Move the cell cursor to the last valid row in the current column.

.IP g
Go to a cell.  The program will prompt for the name of a cell.
Enter a cell number such as "a0" or "ae122".

.PP
Cell entry and editing commands:

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

.IP """
Enter a label for the current cell.

.IP <
Enter a label that will be flushed left against the
left edge of the cell.

.IP >
Enter a label that will be flushed right against the
right edge of the cell.

.IP x
Clears the current cell.  You may prefix this command with a count of
the number of cells on the current row to clear.  Cells cleared with
this command may be recalled with any of the variations of the pull
command.

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

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

.IP m 
Mark a cell to be used as the source for the copy command.

.IP c
Copy the last marked cell to the current cell, updating the row and
column references.

.IP ^T
Toggle cell display.  The current cell's contents are displayed in line
one when no command being entered or edited.  ^T turns the
display on or off.

.PP
File operations

.IP G
Get a new database from a file.  If the encryption option
.I -x
was specified, the file is decrypted before it is loaded into the
spread sheet.

.IP P
Put the current database into a file.  If the encryption option
.I -x
was specified, the file is encrypted before it is saved.

.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 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
.I tbl
preprocessor of
.I nroff.

.IP M
Merges the database from the named file into the current database.  Values,
expressions and names defined in the named file are written into the current
file, overwriting the existing entries at those locations.

.PP 
The three output operators, 
.I put, write
and
.I tbl
can pipe their output to a program.  To use this option, enter "| program" to
the prompt asking for a file name.  For example, to redirect the output
of the write command to the printer, you could enter "| lpr -p".

.PP
Row and Column operations.  Members of this class of commands can be used
on either rows or columns.  The second letter of the command is either
a column designator (one of the characters c, j, k, ^N, ^p) or a 
row designator (one of r, l, h, ^B, ^F).
Commands which move or copy cells also modify the variable references 
in affected cell expressions.
Variable references may be frozen by using the "fixed" operator.

.IP "ar, ac"
Creates a new row (column) immediately following the current row (column).
It is initialized
to be a copy of the current one.

.IP "dr, dc"
Delete this row (column).

.IP "pr, pc, pm"
Pull deleted rows (columns) back into the spread sheet.  The last deleted
set of cells is put back into the spread sheet at the current location.
.I Pr
inserts enough rows to hold the data.
.I Pc
inserts enough columns to hold the data.
.I Pm
(merge) does not insert rows or columns. It overwrites the cells
beginning at the current cursor location.

.IP "ir, ic"
Insert a new row (column) by moving the row (column) containing the cell
cursor, and all
following, down (right) one.  The new position will be empty.

.IP "zr, zc"
Hide ("zap") the current row (column).  This keeps a row or column from being
displayed but keeps it in the data base.

.IP "vr, vc"
Removes expressions from the affected rows (columns), leaving only
the values which were in the cells before the command
was executed.

.IP "sr, sc"
Show hidden rows (columns).  Type in a range of rows or columns
to be revealed.  The command default is the first range of rows or
columns currently hidden.

.IP f
Sets the output format to be used for printing the numbers in each cell 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.  Note that this command has only a column version and
does not have a second letter.  A preceeding count can be used to
specify that more than one column be formatted.

.PP
Range Operations:
Range operations
affect a rectangular region on the screen.  
All of the commands in this class start with a slash; the second
letter of the command indicates which command.
The program will prompt for needed parameters.  Phrases surrounded by
square brackets in the prompt are informational only and may be erased with
the backspace key.

Prompts requesting variable names
may be satisfied with either an explicit variable name, such as "A10"
or with a variable name previously defined in a 
.I /d
command.  Range name prompts require either an explicit range such
as "A10:B20" or a range name previously defined with a 
.I /d
command.

.IP "/x"
Clear a region.  Cells cleared with this command may be recalled
via any of the pull row or column commands.

.IP "/c"
Copy a region to the area starting at the current cell.

.IP "/f"
Fill a region with constant values.  The start and increment numbers
may be positive or negative.

.IP "/d"
This command is used to assign a symbolic name to a single cell or 
a rectangular range of cells on the screen.  The parameters are the
name, surrounded by quotation marks, and either a single cell name
such as "A10" or a range such as "A10:B20".  Names defined in this
fashion will be used by the program in future prompts, may be
entered in response to prompts requesting a cell or range name,
and will be saved when the spread sheet is saved with a
.I Put
command.  Names defined must be more than two alpha
characters long to differentiate them from a column names, and must not have
embedded special characters.  Names may include the character "_" or numerals
as long as they occur after the first three alpha characters.

.IP "/s"
This command will list the defined range names.

.IP "/u"
This command is used to undefine a range name.  The range must have
been previously defined.

.PP
Miscellaneous commands:

.IP q
Exit from \fIpname\fR.  If you were editing a file, and you modified
it, then
\fIpname\fR will ask about saving before exiting. 
If you aren't editing a file and haven't saved the data you
entered, you will get a chance to save the data
before you exit.

.IP ^C
Alternate exit command.

.IP ?
Types a brief helpful message.

.IP "^G or ESC"
Abort entry of the current command.

.IP "^R or ^L"
Redraw the screen.

.IP ^V
Types, in the command line, the name of the cell referenced by
the cell cursor.  This is used when typing in expressions which refer to
entries in the table.

.IP ^E
Types, in the command line, the expression of the cell referenced
by the cell cursor.

.IP ^A
Types, in the command line, the value of the cell referenced
by the cell cursor.

.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. 
Rectangular regions of the screen may be operated upon with '@' functions
such as sum (@sum), average (@avg) and product (@prod).
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
Multiplication.

.TP 15
e/e
Division.

.TP 15
e^e
Exponentiation.

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

.TP 15
@avg(v:v)
Average all valid (nonblank) entries in the region whose two corners are defined
by the two variable (cell) names given.

.TP 15
@prod(v:v)
Multiply together all valid (nonblank) entries in the region whose two
corners are defined by the two variable (cell) names given.

.TP 15
@max(v:v)
Return the maximum value in specified region.

.TP 15
@min(v:v)
Return the minimum value in the specified region.

.TP 15
@stddev(v:v)
Return the sample standard deviation of the specified region.

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

.TP 15
fixed
To make a variable not change automatically when a cell moves,
put the word \*(lqfixed\*(rq in front of the reference.  I.e.
B1*fixed C3

.PP
Assorted math functions.  Most of these are standard system functions
more fully described in
.I math(3).
All of them operate on floating point numbers (doubles);
the trig functions operate with angles in radians.

.TP 15
@exp(expr)
Returns exponential function of <expr>.

.TP 15
@ln(expr)
Returns the natural logarithm of <expr>.

.TP 15
@log(expr)
Returns the base 10 logarithm of <expr>.

.TP 15
@pow(expr1,expr2)
Returns <expr1> raised to the power of <expr2>.

.TP 15
@floor(expr)
Returns returns the largest integer not greater than <expr>.

.TP 15
@ceil(expr)
Returns the smallest integer not less than <expr>.

.TP 15
@rnd(expr)
Rounds <expr> to the nearest integer.

.TP 15
@hypot(x,y)
Returns SQRT(x*x+y*y), taking precautions against unwarranted overflows.

.TP 15
@fabs(expr)
Returns the absolute value |expr|.

.TP 15
@sin(expr), @cos(expr), @tan(expr)
Return trigonometric functions of radian arguments. The magnitude of the
arguments are not checked to assure meaningful results.

.TP 15
@asin(expr)
Returns the arc sin in the range -pi/2 to pi/2

.TP 15
@acos(expr)
Returns the arc cosine in the range 0 to pi.
}i(<|J
.TP 15
@atan(expr)
Returns the arc tangent of <expr> in the range -pi/2 to pi/2.

.TP 15
@dtr(expr)
Converts <expr> in degrees to radians.

.TP 15
@rtd(expr)
Converts <expr> in radians to degrees.

.TP 15
pi
A constant quite close to pi.

.TP 15
@gamma(expr1)
Returns the natural log of the gamma function.

.SH SEE ALSO
bc(1), dc(1), crypt(1)

.SH BUGS

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

At most 200 rows and 40 columns.
SHAR_EOF
if test 13353 -ne "`wc -c < 'sc.doc'`"
then
	echo shar: error transmitting "'sc.doc'" '(should have been 13353 characters)'
fi
fi # end of overwriting check
if test -f 'sc.h'
then
	echo shar: will not over-write existing file "'sc.h'"
else
cat << \SHAR_EOF > 'sc.h'
/*	VC	A Table Calculator
 *		Common definitions
 *
 *		original by James Gosling, September 1982
 *		modified by Mark Weiser and Bruce Israel,
 *			University of Maryland
 *
 */



#define MAXROWS 200
#define MAXCOLS 40
#define error move(1,0), clrtoeol(), printw

typedef struct range_s {
	struct ent *left, *right;
} RANGE_S;

/*
 * If you want to save room, make row and col below into unsigned
 * chars and make sure MAXROWS and MAXCOLS above are both less
 * than 256.  (128 if your compiler doesn't support unsigned char).
 */

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

struct range {
    struct ent *r_left, *r_right;
    char *r_name;
    struct range *r_next, *r_prev;
};

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)

#define ACOS 0
#define ASIN 1
#define ATAN 2
#define CEIL 3
#define COS 4
#define EXP 5 
#define FABS 6 
#define FLOOR 7
#define HYPOT 8
#define LOG 9
#define LOG10 10
#define POW 11
#define SIN 12
#define SQRT 13
#define TAN 14
#define DTR 15
#define RTD 16
#define MIN 17
#define MAX 18
#define RND 19

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

#define ctl(c) ('c'&037)

extern struct ent *tbl[MAXROWS][MAXCOLS];

extern int strow, stcol;
extern int currow, curcol;
extern int savedrow, savedcol;
extern int FullUpdate;
extern int maxrow, maxcol;
extern int fwidth[MAXCOLS];
extern int precision[MAXCOLS];
extern char col_hidden[MAXCOLS];
extern char row_hidden[MAXROWS];
extern char line[1000];
extern int linelim;
extern int changed;
extern struct ent *to_fix;
extern struct enode *new();
extern struct enode *new_const();
extern struct enode *new_var();
extern struct ent *lookat();
extern struct enode *copye();
extern char *coltoa();
extern FILE *openout();
extern struct range *find_range();
extern char *v_name();
extern int modflg;
extern int Crypt;

#if BSD42 || SYSIII
#define	cbreak		crmode
#define	nocbreak	nocrmode
#endif

SHAR_EOF
if test 2228 -ne "`wc -c < 'sc.h'`"
then
	echo shar: error transmitting "'sc.h'" '(should have been 2228 characters)'
fi
fi # end of overwriting check
if test -f 'sres.sed'
then
	echo shar: will not over-write existing file "'sres.sed'"
else
cat << \SHAR_EOF > 'sres.sed'
/%token.*S_/!d
/%token.*S_\(.*\)/s//	"\1",	S_\1,/
SHAR_EOF
if test 50 -ne "`wc -c < 'sres.sed'`"
then
	echo shar: error transmitting "'sres.sed'" '(should have been 50 characters)'
fi
fi # end of overwriting check
if test -f 'xmalloc.c'
then
	echo shar: will not over-write existing file "'xmalloc.c'"
else
cat << \SHAR_EOF > 'xmalloc.c'
/*
 * A safer saner malloc, for careless programmers
 * (I guess I qualify! - rgb)
 */

#include <stdio.h>
#include <curses.h>

extern char *malloc();

char *
xmalloc(n)
unsigned n;
{
register char *ptr;

if ((ptr = malloc(n + sizeof(int))) == NULL)
    fatal("xmalloc: no memory");
*((int *) ptr) = 12345;		/* magic number */
return(ptr + sizeof(int));
}

xfree(p)
char *p;
{
if (p == NULL)
    fatal("xfree: NULL");
p -= sizeof(int);
if (*((int *) p) != 12345)
    fatal("xfree: storage not malloc'ed");
free(p);
}

fatal(str)
char *str;
{
    deraw();
    fprintf(stderr,"%s\n", str);
    exit(1);
}
SHAR_EOF
if test 603 -ne "`wc -c < 'xmalloc.c'`"
then
	echo shar: error transmitting "'xmalloc.c'" '(should have been 603 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0