v18i048: SC spreadsheet, version 6.1, Part04/04

Rich Salz rsalz at uunet.uu.net
Wed Mar 22 07:18:30 AEST 1989


Submitted-by: Robert Bond <sequent!rgb>
Posting-number: Volume 18, Issue 48
Archive-name: sc6.1/part04

# This is a shell archive.  Remove anything before this line
# then unpack it by saving it in a file and typing "sh file"
# (Files unpacked will be owned by you and have default permissions).
# This archive contains the following files:
#	./xmalloc.c
#	./cmds.c
#	./range.c
#	./help.c
#	./eres.sed
#	./sres.sed
#	./Makefile
#	./psc.c
#
if `test ! -s ./xmalloc.c`
then
echo "Extracting ./xmalloc.c"
cat > ./xmalloc.c << '\SHAR\EOF\'
/*
 * A safer saner malloc, for careless programmers
 * $Revision: 6.1 $
 */

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

extern char *malloc();

#ifdef SYSV3
extern void free();
extern void exit();
#endif

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

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

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

fatal(str)
char *str;
{
    deraw();
    (void) fprintf(stderr,"%s\n", str);
    exit(1);
}
\SHAR\EOF\
else
  echo "will not over write ./xmalloc.c"
fi
if [ `wc -c ./xmalloc.c | awk '{printf $1}'` -ne 670 ]
then
echo `wc -c ./xmalloc.c | awk '{print "Got " $1 ", Expected " 670}'`
fi
if `test ! -s ./cmds.c`
then
echo "Extracting ./cmds.c"
cat > ./cmds.c << '\SHAR\EOF\'
/*	SC	A Spreadsheet Calculator
 *		Command routines
 *
 *		original by James Gosling, September 1982
 *		modifications by Mark Weiser and Bruce Israel,
 *			University of Maryland
 *
 *              More mods Robert Bond, 12/86
 *
 *		$Revision: 6.1 $
 */

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

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

#ifdef SYSV3
extern void exit();
#else
extern int exit();
#endif

#define DEFCOLDELIM ':'

duprow()
{
    if (currow >= MAXROWS - 1 || maxrow >= MAXROWS - 1) {
	error ("The table can't be any bigger");
	return;
    }
    modflg++;
    currow++;
    openrow (currow);
    for (curcol = 0; curcol <= maxcol; curcol++) {
	register struct ent *p = tbl[currow - 1][curcol];
	if (p) {
	    register struct ent *n;
	    n = lookat (currow, curcol);
	    copyent ( n, p, 1, 0);
	}
    }
    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;
}

dupcol() 
{
    if (curcol >= MAXCOLS - 1 || maxcol >= MAXCOLS - 1) {
	error ("The table can't be any wider");
	return;
    }
    modflg++;
    curcol++;
    opencol (curcol);
    for (currow = 0; currow <= maxrow; currow++) {
	register struct ent *p = tbl[currow][curcol - 1];
	if (p) {
	    register struct ent *n;
	    n = lookat (currow, curcol);
	    copyent ( n, p, 0, 1);
	}
    }
    for (currow = 0; currow <= maxrow; currow++) {
	register struct ent *p = tbl[currow][curcol];
	if (p && (p -> flags & is_valid) && !p -> expr)
	    break;
    }
    if (currow > maxrow)
	currow = 0;
}

insertrow(arg)
register int arg;
{
    while (--arg>=0) openrow (currow);
}

deleterow(arg)
register int arg;
{
    flush_saved();
    erase_area(currow, 0, currow + arg - 1, maxcol);
    currow += arg;
    while (--arg>=0) closerow (--currow);
    sync_refs();
}

insertcol(arg)
register int arg;
{
    while (--arg>=0) opencol(curcol);
}

deletecol(arg)
register int arg;
{
    flush_saved();
    erase_area(0, curcol, maxrow, curcol + arg - 1);
    curcol += arg;
    while (--arg>=0) closecol (--curcol);
    sync_refs();
}

rowvalueize(arg)
register int arg;
{
    valueize_area(currow, 0, currow + arg - 1, maxcol);
}

colvalueize(arg)
register int arg;
{
    valueize_area(0, curcol, maxrow, curcol + arg - 1);
}

erase_area(sr, sc, er, ec)
int sr, sc, er, ec;
{
    register int r, c;
    register struct ent **p;

    if (sr > er) {
	r = sr; sr = er; er= r;	
    }

    if (sc > ec) {
	c = sc; sc = ec; ec= c;	
    }

    if (sr < 0)
	sr = 0; 
    if (sc < 0)
	sc = 0;
    if (er >= MAXROWS)
	er = MAXROWS-1;
    if (ec >= MAXCOLS)
	ec = MAXCOLS-1;

    for (r = sr; r <= er; r++) {
	for (c = sc; c <= ec; c++) {
	    p = &tbl[r][c];
	    if (*p) {
		free_ent(*p);
		*p = 0;
	    }
	}
    }

}

valueize_area(sr, sc, er, ec)
int sr, sc, er, ec;
{
    register int r, c;
    register struct ent *p;

    if (sr > er) {
	r = sr; sr = er; er= r;	
    }

    if (sc > ec) {
	c = sc; sc = ec; ec= c;	
    }

    if (sr < 0)
	sr = 0; 
    if (sc < 0)
	sc = 0;
    if (er >= MAXROWS)
	er = MAXROWS-1;
    if (ec >= MAXCOLS)
	ec = MAXCOLS-1;

    for (r = sr; r <= er; r++) {
	for (c = sc; c <= ec; c++) {
	    p = tbl[r][c];
	    if (p && p->expr) {
		efree(p->expr);
		p->expr = 0;
		p->flags &= ~is_strexpr;
	    }
	}
    }

}

pullcells(to_insert)
int to_insert;
{
    register struct ent *p, *n;
    register int deltar, deltac;
    int minrow, mincol;
    int mxrow, mxcol;
    int numrows, numcols;

    if (! to_fix)
    {
	error ("No data to pull");
	return;
    }

    minrow = MAXROWS; 
    mincol = MAXCOLS;
    mxrow = 0;
    mxcol = 0;

    for (p = to_fix; p; p = p->next) {
	if (p->row < minrow)
	    minrow = p->row;
	if (p->row > mxrow)
	    mxrow = p->row;
	if (p->col < mincol)
	    mincol = p->col;
	if (p->col > mxcol)
	    mxcol = p->col;
    }

    numrows = mxrow - minrow + 1;
    numcols = mxcol - mincol + 1;
    deltar = currow - minrow;
    deltac = curcol - mincol;

    if (to_insert == 'r') {
	insertrow(numrows);
	deltac = 0;
    } else if (to_insert == 'c') {
	insertcol(numcols);
	deltar = 0;
    }

    FullUpdate++;
    modflg++;

    for (p = to_fix; p; p = p->next) {
	n = lookat (p->row + deltar, p->col + deltac);
	(void) clearent(n);
	copyent( n, p, deltar, deltac);
	n -> flags = p -> flags & ~is_deleted;
    }
}

colshow_op()
{
    register int i,j;
    for (i=0; i<MAXCOLS; i++)
	if (col_hidden[i]) 
	    break;
    for(j=i; j<MAXCOLS; j++)
	if (!col_hidden[j])
	    break;
    j--;
    if (i>=MAXCOLS)
	error ("No hidden columns to show");
    else {
	(void) sprintf(line,"show %s:", coltoa(i));
	(void) sprintf(line + strlen(line),"%s",coltoa(j));
	linelim = strlen (line);
    }
}

rowshow_op()
{
    register int i,j;
    for (i=0; i<MAXROWS; i++)
	if (row_hidden[i]) 
	    break;
    for(j=i; j<MAXROWS; j++)
	if (!row_hidden[j]) {
	    break;
	}
    j--;

    if (i>=MAXROWS)
	error ("No hidden rows to show");
    else {
	(void) sprintf(line,"show %d:%d", i, j);
        linelim = strlen (line);
    }
}

/*
 * Given a row/column command letter, emit a small menu, then read a qualifier
 * character for a row/column command and convert it to 'r' (row), 'c'
 * (column), or 0 (unknown).  If ch is 'p', an extra qualifier 'm' is allowed.
 */

get_rcqual (ch)
    int ch;
{
    error ("%sow/column:  r: row  c: column%s",

	    (ch == 'i') ? "Insert r" :
	    (ch == 'a') ? "Append r" :
	    (ch == 'd') ? "Delete r" :
	    (ch == 'p') ? "Pull r" :
	    (ch == 'v') ? "Values r" :
	    (ch == 'z') ? "Zap r" :
	    (ch == 's') ? "Show r" : "R",

	    (ch == 'p') ? "  m: merge" : "");

    (void) refresh();

    switch (nmgetch())
    {
	case 'r':
	case 'l':
	case 'h':
	case ctl(f):
	case ctl(b):	return ('r');

	case 'c':
	case 'j':
	case 'k':
	case ctl(p):
	case ctl(n):	return ('c');

	case 'm':	return ((ch == 'p') ? 'm' : 0);

	case ESC:
	case ctl (g):	return (ESC);

	default:	return (0);
    }
    /*NOTREACHED*/
}

openrow (rs)
int rs;
{
    register    r;
    register struct ent **p;
    register    c;
    register	i;

    if (rs > maxrow) maxrow = rs;
    if (maxrow >= MAXROWS - 1 || rs > MAXROWS - 1) {
	error ("The table can't be any longer");
	return;
    }
    for (i = maxrow+1; i > rs; i--) {
	row_hidden[i] = row_hidden[i-1];
    }
    for (r = ++maxrow; r > rs; r--)
	for (c = maxcol + 1, p = &tbl[r][0]; --c >= 0; p++)
	    if (p[0] = p[-MAXCOLS])
		p[0] -> row++;
    p = &tbl[rs][0];
    for (c = maxcol + 1; --c >= 0;)
	*p++ = 0;
    FullUpdate++;
    modflg++;
}

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

    if (r > maxrow) return;

    p = &tbl[r][0];
    for (c=maxcol+1; --c>=0; ) {
	if (*p)
	    free_ent(*p);
	*p++ = 0;
    }

    for (i = r; i < MAXROWS - 1; i++) {
	row_hidden[i] = row_hidden[i+1];
    }

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

    p = &tbl[maxrow][0];
    for (c=maxcol+1; --c>=0; ) *p++ = 0;
    maxrow--;
    FullUpdate++;
    modflg++;
}

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

    if (cs > maxcol) maxcol = cs;
    if (maxcol >= MAXCOLS - 1 || cs > MAXCOLS - 1) {
	error ("The table can't be any wider");
	return;
    }
    for (i = maxcol+1; i > cs; i--) {
	fwidth[i] = fwidth[i-1];
	precision[i] = precision[i-1];
	col_hidden[i] = col_hidden[i-1];
    }
    /* fwidth[cs] = DEFWIDTH;
    precision[i] =  DEFPREC;  */

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

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

    if (lim < 0) return;

    for (r=0; r<=maxrow; r++)
	if (q = tbl[r][cs]) {
	    free_ent(q);
	}

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

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

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

doend(rowinc, colinc)
int rowinc, colinc;
{
    register struct ent *p;
    int r, c;

    if (VALID_CELL(p, currow, curcol)) {
	r = currow + rowinc;
	c = curcol + colinc;
	if (r >= 0 && r < MAXROWS && 
	    c >= 0 && c < MAXCOLS &&
	    !VALID_CELL(p, r, c)) {
		currow = r;
		curcol = c;
	}
    }

    if (!VALID_CELL(p, currow, curcol)) {
        switch (rowinc) {
        case -1:
	    while (!VALID_CELL(p, currow, curcol) && currow > 0)
		currow--;
	    break;
        case  1:
	    while (!VALID_CELL(p, currow, curcol) && currow < MAXROWS-1)
		currow++;
	    break;
        case  0:
            switch (colinc) {
 	    case -1:
	        while (!VALID_CELL(p, currow, curcol) && curcol > 0)
		    curcol--;
	        break;
 	    case  1:
	        while (!VALID_CELL(p, currow, curcol) && curcol < MAXCOLS-1)
		    curcol++;
	        break;
	    }
            break;
        }

	error ("");	/* clear line */
	return;
    }

    switch (rowinc) {
    case -1:
	while (VALID_CELL(p, currow, curcol) && currow > 0)
	    currow--;
	break;
    case  1:
	while (VALID_CELL(p, currow, curcol) && currow < MAXROWS-1)
	    currow++;
	break;
    case  0:
	switch (colinc) {
	case -1:
	    while (VALID_CELL(p, currow, curcol) && curcol > 0)
		curcol--;
	    break;
	case  1:
	    while (VALID_CELL(p, currow, curcol) && curcol < MAXCOLS-1)
		curcol++;
	    break;
	}
	break;
    }
    if (!VALID_CELL(p, currow, curcol)) {
        currow -= rowinc;
        curcol -= colinc;
    }
}

doformat(c1,c2,w,p)
int c1,c2,w,p;
{
    register int i;

    if (w > COLS - RESCOL - 2) {
	error("Format too large - Maximum = %d", COLS - RESCOL - 2);
	w = COLS-RESCOL-2;
    }

    if (p > w) {
	error("Precision too large");
	p = w;
    }

    for(i = c1; i<=c2; i++)
	fwidth[i] = w, precision[i] = p;

    FullUpdate++;
    modflg++;
}

print_options(f)
FILE *f;
{
    if(
       autocalc &&
       propagation == 10 &&
       calc_order == BYROWS &&
       !numeric &&
       prescale == 1.0 &&
       !extfunc &&
       showcell &&
       showtop &&
       tbl_style == 0
      )
		return;		/* No reason to do this */

    (void) fprintf(f, "set");
    if(!autocalc) 
	(void) fprintf(f," !autocalc");
    if(propagation != 10)
	(void) fprintf(f, " iterations = %d", propagation);
    if(calc_order != BYROWS )
	(void) fprintf(f, " bycols");
    if (numeric)
	(void) fprintf(f, " numeric");
    if (prescale != 1.0)
	(void) fprintf(f, " prescale");
    if (extfunc)
	(void) fprintf(f, " extfun");
    if (!showcell)
	(void) fprintf(f, " !cellcur");
    if (!showtop)
	(void) fprintf(f, " !toprow");
    if (tbl_style)
	(void) fprintf(f, " tblstyle = %s", tbl_style == TBL ? "tbl" :
					tbl_style == LATEX ? "latex" :
					tbl_style == TEX ? "tex" : "0" );
    (void) fprintf(f, "\n");
}

printfile (fname, r0, c0, rn, cn)
char *fname;
int r0, c0, rn, cn;
{
    FILE *f;
    char pline[1000];
    int plinelim;
    int pid;
    int fieldlen, nextcol;
    register row, col;
    register struct ent **p;
    char ch, lin[100];

    if (strcmp(fname, curfile) == 0) {
	(void) move (0, 0);
	(void) clrtoeol ();
	(void) sprintf (lin,
		"Confirm that you want to destroy the data base: (y,n)");
	(void) addstr (lin);
	(void) refresh();
	ch = nmgetch();
	if (ch != 'y' && ch != 'Y') 
	    return;
    }

    f = openout(fname, &pid);

    if (f==0) {
	error ("Can't create file \"%s\"", fname);
	return;
    }
    for (row=r0;row<=rn; row++) {
	register c = 0;

	if (row_hidden[row])
	    continue;

	pline[plinelim=0] = '\0';
	for (p = &tbl[row][col=c0]; col<=cn;
	        p += nextcol-col, col = nextcol, c += fieldlen) {

	    nextcol = col+1;
	    if (col_hidden[col]) {
		fieldlen = 0;
		continue;
	    }

	    fieldlen = fwidth[col];
	    if (*p) {
		char *s;

		while (plinelim<c) pline[plinelim++] = ' ';
		plinelim = c;
		if ((*p)->flags&is_valid) {
		    (void)sprintf (pline+plinelim,"%*.*f",fwidth[col],
		                                precision[col], (*p)->v);
		    plinelim += strlen (pline+plinelim);
		}
		if (s = (*p)->label) {
		    int slen;
		    char *start, *last;
		    register char *fp;
		    struct ent *nc;

		    /* Figure out if the label slops over to a blank field */
		    slen = strlen(s);
		    while (slen > fieldlen && nextcol <= cn &&
			    !((nc = lookat(row,nextcol))->flags & is_valid) &&
			    !(nc->label)) {
			
	                if (!col_hidden[nextcol])
		 	    fieldlen += fwidth[nextcol];

			nextcol++;
		    }
		    if (slen > fieldlen)
			slen = fieldlen;
		    
		    /* Now justify and print */
		    start = (*p)->flags & is_leftflush ? pline + c
					: pline + c + fieldlen - slen;
		    last = pline + c + fieldlen;
		    fp = plinelim < c ? pline + plinelim : pline + c;
		    while (fp < start)
			*fp++ = ' ';
		    while (slen--)
			*fp++ = *s++;
		    if (!((*p)->flags & is_valid) || fieldlen != fwidth[col])
			while(fp < last)
			    *fp++ = ' ';
		    if (plinelim < fp - pline)
			plinelim = fp - pline;
		}
	    }
	}
	pline[plinelim++] = '\n';
	pline[plinelim] = 0;
	(void) fputs (pline, f);
    }

    closeout(f, pid);
}

tblprintfile (fname, r0, c0, rn, cn)
char *fname;
int r0, c0, rn, cn;
{
    FILE *f;
    int pid;
    register row, col;
    register struct ent **p;
    char coldelim = DEFCOLDELIM;
    char ch, lin[100];

    if (strcmp(fname, curfile) == 0) {
	(void) move (0, 0);
	(void) clrtoeol ();
	(void) sprintf (lin,
		"Confirm that you want to destroy the data base: (y,n)");
	(void) addstr (lin);
	(void) refresh();
	ch = nmgetch();
	if (ch != 'y' && ch != 'Y') 
	    return;
    }

    f = openout(fname, &pid);

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

    if ( tbl_style == TBL ) {
	fprintf(f,".\\\" ** %s spreadsheet output \n.TS\n",progname);
	fprintf(f,"tab(%c);\n",coldelim);
	for (col=c0;col<=cn; col++) fprintf(f," n");
	fprintf(f, ".\n");
	}
    else if ( tbl_style == LATEX ) {
	fprintf(f,"%% ** %s spreadsheet output\n\\begin{tabular}{",progname);
	for (col=c0;col<=cn; col++) fprintf(f,"c");
	fprintf(f, "}\n");
	coldelim = '&';
	}
    else if ( tbl_style == TEX ) {
	fprintf(f,"{\t%% ** %s spreadsheet output\n\\settabs %d \\columns\n",
		progname, cn-c0+1);
	coldelim = '&';
	}

    for (row=r0; row<=rn; row++) {
	if ( tbl_style == TEX )
	    (void) fprintf (f, "\\+");
	
	for (p = &tbl[row][col=c0]; col<=cn; col++, p++) {
	    if (*p) {
		char *s;
		if ((*p)->flags&is_valid) {
		    (void) fprintf (f,"%.*f",precision[col],
				(*p)->v);
		}
		if (s = (*p)->label) {
	            (void) fprintf (f,"%s",s);
		}
	    }
	    if ( col < cn )
		(void) fprintf(f,"%c",coldelim);
	}
	if ( tbl_style == LATEX ) {
	    if ( row < rn ) (void) fprintf (f, "\\\\");
	    }
	else if ( tbl_style == TEX ) {
	    (void) fprintf (f, "\\cr");
	    }
	(void) fprintf (f,"\n");
    }

    if ( tbl_style == TBL )
    (void) fprintf (f,".TE\n.\\\" ** end of %s spreadsheet output\n", progname);
    else if ( tbl_style == LATEX )
    (void) fprintf (f,"\\end{tabular}\n%% ** end of %s spreadsheet output\n", progname);
    else if ( tbl_style == TEX )
    (void) fprintf (f,"}\n%% ** end of %s spreadsheet output\n", progname);

    closeout(f, pid);
}

struct enode *
copye (e, Rdelta, Cdelta)
register struct enode *e;
int Rdelta, Cdelta;
{
    register struct enode *ret;
    if (e==0) {
        ret = 0;
    } else if (e->op & REDUCE) {
	int newrow, newcol;
	ret = (struct enode *) xmalloc ((unsigned) sizeof (struct enode));
	ret->op = e->op;
	newrow=e->e.r.left.vf & FIX_ROW ? e->e.r.left.vp->row :
					  e->e.r.left.vp->row+Rdelta;
	newcol=e->e.r.left.vf & FIX_COL ? e->e.r.left.vp->col :
					  e->e.r.left.vp->col+Cdelta;
	ret->e.r.left.vp = lookat (newrow, newcol);
	ret->e.r.left.vf = e->e.r.left.vf;
	newrow=e->e.r.right.vf & FIX_ROW ? e->e.r.right.vp->row :
					   e->e.r.right.vp->row+Rdelta;
	newcol=e->e.r.right.vf & FIX_COL ? e->e.r.right.vp->col :
					   e->e.r.right.vp->col+Cdelta;
	ret->e.r.right.vp = lookat (newrow, newcol);
	ret->e.r.right.vf = e->e.r.right.vf;
    } else {
	ret = (struct enode *) xmalloc ((unsigned) sizeof (struct enode));
	ret->op = e->op;
	switch (ret->op) {
	case 'v':
		{
		    int newrow, newcol;
		    newrow=e->e.v.vf & FIX_ROW ? e->e.v.vp->row :
						 e->e.v.vp->row+Rdelta;
		    newcol=e->e.v.vf & FIX_COL ? e->e.v.vp->col :
						 e->e.v.vp->col+Cdelta;
		    ret->e.v.vp = lookat (newrow, newcol);
		    ret->e.v.vf = e->e.v.vf;
		    break;
		}
	case 'k':
		ret->e.k = e->e.k;
		break;
	case 'f':
		ret->e.o.right = copye (e->e.o.right,0,0);
		ret->e.o.left = 0;
 		break;
	case '$':
		ret->e.s = xmalloc((unsigned) strlen(e->e.s)+1);
		(void) strcpy(ret->e.s, e->e.s);
		break;
	default:
		ret->e.o.right = copye (e->e.o.right,Rdelta,Cdelta);
		ret->e.o.left = copye (e->e.o.left,Rdelta,Cdelta);
		break;
	}
    }
    return ret;
}

/*
 * sync_refs and syncref are used to remove references to
 * deleted struct ents.  Note that the deleted structure must still
 * be hanging around before the call, but not referenced by an entry
 * in tbl.  Thus the free_ent, fix_ent calls in sc.c
 */

sync_refs ()
{
    register i,j;
    register struct ent *p;
    sync_ranges();
    for (i=0; i<=maxrow; i++)
	for (j=0; j<=maxcol; j++)
	    if ((p=tbl[i][j]) && p->expr)
		syncref(p->expr);
}


syncref(e)
register struct enode *e;
{
    if (e==0)
	return;
    else if (e->op & REDUCE) {
 	e->e.r.right.vp = lookat(e->e.r.right.vp->row, e->e.r.right.vp->col);
 	e->e.r.left.vp = lookat(e->e.r.left.vp->row, e->e.r.left.vp->col);
    } else {
	switch (e->op) {
	case 'v':
		e->e.v.vp = lookat(e->e.v.vp->row, e->e.v.vp->col);
		break;
	case 'k':
		break;
	case '$':
		break;
	default:
		syncref(e->e.o.right);
		syncref(e->e.o.left);
		break;
	}
    }
}

hiderow(arg)
int arg;
{
    register int r1;
    register int r2;

    r1 = currow;
    r2 = r1 + arg - 1;
    if (r1 < 0 || r1 > r2) {
	error ("Invalid range");
	return;
    }
    if (r2 > MAXROWS-2) {
	error ("You can't hide the last row");
	return;
    }
    FullUpdate++;
    while (r1 <= r2)
	row_hidden[r1++] = 1;
}

hidecol(arg)
int arg;
{
    register int c1;
    register int c2;

    c1 = curcol;
    c2 = c1 + arg - 1;
    if (c1 < 0 || c1 > c2) {
	error ("Invalid range");
	return;
    }
    if (c2 > MAXCOLS-2) {
	error ("You can't hide the last column");
	return;
    }
    FullUpdate++;
    while (c1 <= c2)
	col_hidden[c1++] = 1;
}

showrow(r1, r2)
int r1, r2;
{
    if (r1 < 0 || r1 > r2) {
	error ("Invalid range");
	return;
    }
    if (r2 > MAXROWS-1) {
	r2 = MAXROWS-1;
    }
    FullUpdate++;
    while (r1 <= r2)
	row_hidden[r1++] = 0;
}

showcol(c1, c2)
int c1, c2;
{
    if (c1 < 0 || c1 > c2) {
	error ("Invalid range");
	return;
    }
    if (c2 > MAXCOLS-1) {
	c2 = MAXCOLS-1;
    }
    FullUpdate++;
    while (c1 <= c2)
	col_hidden[c1++] = 0;
}

/* Open the output file, setting up a pipe if needed */

FILE *
openout(fname, rpid)
char *fname;
int *rpid;
{
    int pipefd[2];
    int pid;
    FILE *f;

    while (*fname && (*fname == ' '))  /* Skip leading blanks */
	fname++;

    if (*fname != '|') {		/* Open file if not pipe */
	*rpid = 0;
	return(fopen(fname, "w"));
    }

    fname++;				/* Skip | */
    if ( pipe (pipefd) < 0) {
	error("Can't make pipe to child");
	*rpid = 0;
	return(0);
    }

    deraw();
#ifdef VMS
    fprintf(stderr, "No son tasks available yet under VMS--sorry\n");
#else /* VMS */

    if ((pid=fork()) == 0)			  /* if child  */
    {
	(void) close (0);			  /* close stdin */
	(void) close (pipefd[1]);
	(void) dup (pipefd[0]);		  /* connect to pipe input */
	(void) signal (SIGINT, SIG_DFL);	  /* reset */
	(void) execl ("/bin/sh", "sh", "-c", fname, 0);
	exit (-127);
    }
    else				  /* else parent */
    {
	*rpid = pid;
	f = fdopen (pipefd[1], "w");
	if (f == 0)
	{
	    (void) kill (pid, -9);
	    error ("Can't fdopen output");
	    (void) close (pipefd[1]);
	    *rpid = 0;
	    return(0);
	}
    }
#endif /* VMS */
    return(f);
}

closeout(f, pid)
FILE *f;
int pid;
{
    int temp;

    (void) fclose (f);
    if (pid) {
         while (pid != wait(&temp)) /**/;
	 (void) printf("Press RETURN to continue ");
	 (void) fflush(stdout);
	 (void) nmgetch();
	 goraw();
    }
}

copyent(n,p,dr,dc)
	    register struct ent *n, *p;
	    int dr, dc;
{
    if(!n||!p){error("internal error");return;}
    n -> v = p -> v;
    n -> flags = p -> flags;
    n -> expr = copye (p -> expr, dr, dc);
    n -> label = 0;
    if (p -> label) {
	n -> label = (char *)
		xmalloc  ((unsigned) (strlen (p -> label) + 1));
	(void) strcpy (n -> label, p -> label);
    }
}

write_fd (f, r0, c0, rn, cn)
register FILE *f;
int r0, c0, rn, cn;
{
    register struct ent **p;
    register r, c;

    (void) fprintf (f, "# This data file was generated by the Spreadsheet ");
    (void) fprintf (f, "Calculator.\n");
    (void) fprintf (f, "# You almost certainly shouldn't edit it.\n\n");
    print_options(f);
    for (c=0; c<MAXCOLS; c++)
	if (fwidth[c] != DEFWIDTH || precision[c] != DEFPREC)
	    (void) fprintf (f, "format %s %d %d\n",coltoa(c),fwidth[c],precision[c]);
    for (c=c0; c<cn; c++) {
        if (col_hidden[c]) {
            (void) fprintf(f, "hide %s\n", coltoa(c));
        }
    }
    for (r=r0; r<=rn; r++) {
	if (row_hidden[r]) {
	    (void) fprintf(f, "hide %d\n", r);
	}
    }

    write_range(f);

    if (mdir) 
	    (void) fprintf(f, "mdir \"%s\"\n", mdir);
    for (r=r0; r<=rn; r++) {
	p = &tbl[r][c0];
	for (c=c0; c<=cn; c++, p++)
	    if (*p) {
		if ((*p)->label) {
		    edits(r,c);
		    (void) fprintf(f, "%s\n",line);
		}
		if ((*p)->flags&is_valid) {
		    editv (r, c);
		    (void) fprintf (f, "%s\n",line);
		}
	    }
    }
}

writefile (fname, r0, c0, rn, cn)
char *fname;
int r0, c0, rn, cn;
{
    register FILE *f;
    char save[1024];
    int pid;

#ifndef VMS
    if (Crypt) {
	return (cwritefile(fname, r0, c0, rn, cn));
    }
#endif /* VMS */

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

    (void) strcpy(save,fname);

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

    write_fd(f, r0, c0, rn, cn);
    
    closeout(f, pid);

    if (!pid) {
        (void) 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 (*fname == '*' && mdir) { 
       (void) strcpy(save, mdir);
       *fname = '/';
       (void) strcat(save, fname);
    } else {
        if (*fname == 0)
	    fname = &curfile[0];
        (void) strcpy(save,fname);
    }

#ifndef VMS
    if (Crypt)  {
	creadfile(save, eraseflg);
	return;
    }
#endif /* VMS */

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

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

    if (eraseflg) erasedb ();

    loading++;
    while (fgets(line,sizeof line,f)) {
	linelim = 0;
	if (line[0] != '#') (void) yyparse ();
    }
    --loading;
    (void) fclose (f);
    linelim = -1;
    modflg++;
    if (eraseflg) {
	(void) 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;
    clean_range();
    FullUpdate++;
}

backcol(arg)
	int arg;
{
    while (--arg>=0) {
	if (curcol)
	    curcol--;
	else
	    {error ("At column A"); break;}
	while(col_hidden[curcol] && curcol)
	    curcol--;
    }
}

forwcol(arg)
	int arg;
{
    while (--arg>=0) {
	if (curcol < MAXCOLS - 1)
	    curcol++;
	else
	    {error ("The table can't be any wider"); break;}
	while(col_hidden[curcol]&&(curcol<MAXCOLS-1))
	    curcol++;
    }
}

forwrow(arg)
	int arg;
{
    while (--arg>=0) {
	if (currow < MAXROWS - 1)
	    currow++;
	else
	    {error ("The table can't be any longer"); break;}
	while (row_hidden[currow]&&(currow<MAXROWS-1))
	    currow++;
    }
}

backrow(arg)
	int arg;
{
    while (--arg>=0) {
	if (currow)
	    currow--;
	else
	    {error ("At row zero"); break;}
	while (row_hidden[currow] && currow)
	    currow--;
    }
}


/*
 * Show a cell's label string or expression value.  May overwrite value if
 * there is one already displayed in the cell.  Created from old code in
 * update(), copied with minimal changes.
 */

showstring (string, leftflush, hasvalue, row, col, nextcolp, mxcol, fieldlenp, r, c)
    char *string;	/* to display */
    int leftflush;	/* or rightflush */
    int hasvalue;	/* is there a numeric value? */
    int row, col;	/* spreadsheet location */
    int *nextcolp;	/* value returned through it */
    int mxcol;		/* last column displayed? */
    int *fieldlenp;	/* value returned through it */
    int r, c;		/* screen row and column */
{
    register int nextcol  = *nextcolp;
    register int fieldlen = *fieldlenp;

    char field[1024];
    int  slen;
    char *start, *last;
    register char *fp;
    struct ent *nc;

    /* This figures out if the label is allowed to
       slop over into the next blank field */

    slen = strlen (string);
    while ((slen > fieldlen) && (nextcol <= mxcol) &&
	   !((nc = lookat (row, nextcol)) -> flags & is_valid) &&
	   !(nc->label)) {

	if (! col_hidden [nextcol])
	    fieldlen += fwidth [nextcol];

	nextcol++;
    }
    if (slen > fieldlen)
	slen = fieldlen;

    /* Now justify and print */
    start = leftflush ? field : field + fieldlen - slen;
    last = field+fieldlen;
    fp = field;
    while (fp < start)
	*fp++ = ' ';
    while (slen--)
	*fp++ = *string++;
    if ((! hasvalue) || fieldlen != fwidth[col]) 
	while (fp < last)
	    *fp++ = ' ';
    *fp = 0;
#ifdef VMS
    mvaddstr(r, c, field);	/* this is a macro */
#else
    (void) mvaddstr(r, c, field);
#endif

    *nextcolp  = nextcol;
    *fieldlenp = fieldlen;
}
etype(e)
register struct enode *e;
{

    if (e==0) return 0;
    switch (e->op) {
    case '+': case '-': case '*': case '/': case '%': case '^':
    case '<': case '=': case '>': case '&': case '|': case 'm':
    case '~': case 'k': case INDEX:
    case REDUCE | '+': case REDUCE | '*': case REDUCE | 'a':
    case REDUCE | 's': case REDUCE | MAX: case REDUCE | MIN:
    case ACOS: case ASIN: case ATAN: case ATAN2: case CEIL:
    case COS: case EXP: case FABS: case FLOOR: case HYPOT:
    case LOG: case LOG10: case POW: case SIN: case SQRT:
    case TAN: case DTR: case RTD: case RND: case FV: case PV:
    case PMT: case HOUR: case MINUTE: case SECOND: case MONTH:
    case DAY: case YEAR: case NOW: case STON: case EQS:
    case LMAX: case LMIN: case NVAL: case LOOKUP:
        return (NUM);

    case O_SCONST: case '#': case DATE: case FMT: case STINDEX:
    case EXT: case SVAL: case SUBSTR:
        return (STR);

    case 'f':  case '?':
        return(etype(e->e.o.left));

    case O_VAR: {
	register struct ent *p;
	p = e->e.v.vp;
	if (p->expr) 
	    return(p->flags & is_strexpr ? STR : NUM);
	else if (p->label)
	    return(STR);
	else
	    return(NUM);
	}

    default:
	return(NUM);
    }
}
\SHAR\EOF\
else
  echo "will not over write ./cmds.c"
fi
if [ `wc -c ./cmds.c | awk '{printf $1}'` -ne 27705 ]
then
echo `wc -c ./cmds.c | awk '{print "Got " $1 ", Expected " 27705}'`
fi
if `test ! -s ./range.c`
then
echo "Extracting ./range.c"
cat > ./range.c << '\SHAR\EOF\'

/*	SC	A Spreadsheet Calculator
 *		Range Manipulations
 *
 *              Robert Bond, 4/87
 *
 *		$Revision: 6.1 $
 */

#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

static struct range *rng_base;

add_range(name, left, right, is_range)
char *name;
struct ent_ptr left, right;
int is_range;
{
    struct range *r;
    register char *p;
    int len;
    int minr,minc,maxr,maxc;
    int minrf, mincf, maxrf, maxcf;

    if (left.vp->row < right.vp->row) {
	minr = left.vp->row; minrf = left.vf & FIX_ROW;
	maxr = right.vp->row; maxrf = right.vf & FIX_ROW;
    } else {
	minr = right.vp->row; minrf = right.vf & FIX_ROW;
	maxr = left.vp->row; maxrf = right.vf & FIX_ROW;
    } 

    if (left.vp->col < right.vp->col) {
	minc = left.vp->col; mincf = left.vf & FIX_COL;
	maxc = right.vp->col; maxcf = right.vf & FIX_COL;
    } else {
	minc = right.vp->col; mincf = right.vf & FIX_COL;
	maxc = left.vp->col; maxcf = left.vf & FIX_COL;
    } 

    left.vp = lookat(minr, minc);
    left.vf = minrf | mincf;
    right.vp = lookat(maxr, maxc);
    right.vf = maxrf | maxcf;

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

    if (strlen(name) <= 2) {
	error("Invalid range name - too short");
	xfree(name);
	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");
	    xfree(name);
	    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;
    r->r_is_range = is_range;
    if (rng_base)
        rng_base->r_prev = r;
    rng_base = r;
}

del_range(left, right)
struct ent *left, *right;
{
    register struct range *r;
    int minr,minc,maxr,maxc;

    minr = left->row < right->row ? left->row : right->row;
    minc = left->col < right->col ? left->col : right->col;
    maxr = left->row > right->row ? left->row : right->row;
    maxc = left->col > right->col ? left->col : right->col;

    left = lookat(minr, minc);
    right = lookat(maxr, maxc);

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

clean_range()
{
    register struct range *r;
    register struct range *nextr;

    r = rng_base;
    rng_base = 0;

    while (r) {
	nextr = r->r_next;
	xfree((char *)(r->r_name));
	xfree((char *)r);
	r = nextr;
    }
}

/* 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.vp) && (rmatch == r->r_right.vp)) 
	    return(r);
    }
    return(0);
}

sync_ranges()
{
    register struct range *r;

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

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

    for (r = rng_base; r; r = r->r_next) {
	(void) fprintf(f, "define \"%s\" %s%s%s%d",
			r->r_name,
			r->r_left.vf & FIX_COL ? "$":"",
			coltoa(r->r_left.vp->col), 
			r->r_left.vf & FIX_ROW ? "$":"",
			r->r_left.vp->row);
	if (r->r_is_range)
	    (void) fprintf(f, ":%s%s%s%d\n",
			    r->r_right.vf & FIX_COL ? "$":"",
			    coltoa(r->r_right.vp->col), 
			    r->r_right.vf & FIX_ROW ? "$":"",
			    r->r_right.vp->row);
	else
	    (void) fprintf(f, "\n");
    }
}

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

    (void) fprintf(f, "%-30s %s\n\n","Name","Definition");

    for (r = rng_base; r; r = r->r_next) {
	(void) fprintf(f, "%-30s %s%s%s%d",
			    r->r_name,
			    r->r_left.vf & FIX_COL ? "$":"",
			    coltoa(r->r_left.vp->col), 
			    r->r_left.vf & FIX_ROW ? "$":"",
			    r->r_left.vp->row);
	if (r->r_is_range)
	    (void) fprintf(f, ":%s%s%s%d\n",
			    r->r_right.vf & FIX_COL ? "$":"",
			    coltoa(r->r_right.vp->col), 
			    r->r_right.vf & FIX_ROW ? "$":"",
			    r->r_right.vp->row);
	else
	    (void) 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 {
        (void) sprintf(buf, "%s%d", coltoa(col), row);
	return(buf);
    }
}

char *
r_name(r1, c1, r2, c2)
int r1, c1, r2, c2;
{
    struct ent *v1, *v2;
    struct range *r;
    static char buf[100];

    v1 = lookat(r1, c1);
    v2 = lookat(r2, c2);
    if (r = find_range((char *)0, 0, v1, v2)) {
	return(r->r_name);
    } else {
        (void) sprintf(buf, "%s", v_name(r1, c1));
	(void) sprintf(buf+strlen(buf), ":%s", v_name(r2, c2));
	return(buf);
    }
}

are_ranges()
{
return (rng_base != 0);
}
\SHAR\EOF\
else
  echo "will not over write ./range.c"
fi
if [ `wc -c ./range.c | awk '{printf $1}'` -ne 5535 ]
then
echo `wc -c ./range.c | awk '{print "Got " $1 ", Expected " 5535}'`
fi
if `test ! -s ./help.c`
then
echo "Extracting ./help.c"
cat > ./help.c << '\SHAR\EOF\'
/*
 * Help functions for sc 
 * R. Bond, 1988
 * $Revision: 6.2 $
 */

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

char *intro[] = {
" ",
" Overview:",
" ",
" A:   This overview",
" B:   Options",
" C:   Cursor movement commands",
" D:   Cell entry and editing commands",
" E:   File commands",
" F:   Row and column commands",
" G:   Range commands",
" H:   Miscellaneous commands",
" I:   Variable names/Expressions",
" J:   Range functions",
" K:   Numeric functions",
" L:   String functions",
" M:   Financial functions",
" N:   Time and date functions",
" ",
" Q:   Return to main spreadsheet",
0
};

char *options[] = {
" ",
" B: Options",
" ",
"     ^To  Toggle options. Toggle one option selected by o:",
" ",
"          a    Recalculate automatically or on ``@'' commands.",
"          c    Current cell highlighting enable/disable.",  
"          e    External function execution enable/disable.",
"          n    If enabled, a digit starts a numeric value.",
"          t    Top line display enable/disable.",
"          x    Encrypt/decrypt database and listing files.",
"          $    Dollar prescale.  If enabled, all numeric constants.",
"               (not expressions) entered are multipled by 0.01.",
" ",
"     S    Set options.  Options include:",
" ",
"          byrows        Recalculate in row order. (default)",
"          bycols        Recalculate in column order.",
"          iterations=n  Set the number of iterations allowed. (10)",
"          tblstyle=xx   Set ``T'' output style to:",
"                        0 (none), tex, latex, or tbl.",
0
};

char *cursor[] = {
" ",
" C: Cell cursor movement (always OK):",
" ",
"     ^N ^P ^B ^F Down, up, back, forward",
"     ^Ed         Go to end of range.  Follow ^E by a direction indicator",
"                 such as ^P or j.",
"     Arrow keys (if the terminal and termcap support them.)",
" ",
" Cell cursor movement if no prompt active:",
"     j,k,l,h    Down, up, right, left",
"     SPACE      Forward",
"     ^H         Back",
"     TAB        Forward, otherwise starts/ends a range",
"     ^          Up to row 0 of the current column.",
"     #          Down to the last valid row of the current column.",
"     0          Back to column A.  Preface with ^U if numeric mode.",
"     $          Forward to the last valid column of the current row.",
"     b          Back then up to the previous valid cell.",
"     w          Forward then down to the next valid cell.",
"     g          Go to a cell.  Cell name, range name, quoted string,",
"                or a number specify which cell.",
0
};


char *cell[] = {
" ",
" D: Cell entry and editing commands:",
" ",
"     =    Enter a numeric constant or expression.",
"     <    Enter a left justified string or string expression.",
"     \",>  Enter a right justified string or string expression.",
"     e    Edit the current cell's numeric value.",
"     E    Edit the current cell's string part.",
"     x    Clear the current cell.",
"     c    Copy the last marked cell to the current cell.",
"     m    Mark a cell to be used as the source for ``c''",
"     +    Increment numeric part",
"     -    Decrement numeric part",
" ",
"     In numeric mode, a decimal digit, ``+'', ``-'', and ``.'' all start",
"     a new numeric constant or expression.",
0
};


char *file[] = {
" ",
" E: File commands:",
" ",
"     G    Get a new database from a file. ",
"     M    Merge a new file into the current database.",
"     P    Put the current database into a file.",
"     W    Write a listing of the current database into a file in",
"          a form that matches its appearance on the screen.",
"     T    Write a listing of the current database to a file, but",
"          put delimiters between each pair of fields.",
"          Optionally brackets output with control lines for ``tbl'',",
"          ``LaTeX'', or ``TeX''.",
" ",
"     If encryption mode is set, file I/O will be encrypted/decrypted.",
"     ``\"| program\"'' for a file name will pipe (unencrypted) output to",
"     a program for Put, Write and Table.  If a cell name is used",
"     as the file name, the cell's string part will be used as the",
"     file name.",
0
};


char *row[] = {
" ",
" F: Row and column commands:",
" ",
"     ir, ic      Insert a new, empty row (column)",
"     ar, ac      Append a new copy of the current row (column)",
"     dr, dc      Delete the current row (column)",
"     pr, pc, pm  Pull deleted cells back into the spreadsheet",
"                 Insert rows, columns or merge the cells.",
"     vr, vc      Remove expressions from the affected rows (columns),",
"                 leaving only the values.",
"     zr, zc      Hide (``zap'') the current row (column)",
"     sr, sc      Show hidden rows (columns)",
"     f           Set the output format to be used with the values of",
"                 each cell in this column.  Enter field width and",
"                 number of fractional digits.  A preceding count can be",
"                 used to change more than one column.",
" ",
"     Commands which move or copy cells also modify the row and column ",
"     references in the new cell expressions.  Use ``fixed'' or the",
"     ``$'' style cell reference to supress the change.",
0
};


char *range[] = {
" ",
" G: Range commands:",
" ",
"     /x   Clear a range. ",
"     /v   Remove the expressions from a range of cells, leaving ",
"          just the values.",
"     /c   Copy a source range to a destination range.",
"     /f   Fill a range with constant values starting with a given",
"          value and increasing by a given increment.",
"     /d   Assign a name to a cell or a range of cells.  Give the",
"          the name, surrounded by quotes, and either a cell name such",
"          as ``A10'' or a range such as ``a1:b20''.",
"     /s   Shows the currently defined range names.  Pipe output to",
"          sort, then to less.",
"     /u   Use this command to undefine a previously defined range",
"          name.",
" ",
"     Range operations affect a rectangular region on the screen",
"     defined by the upper left and lower right cells in the region.",
"     A range is specified by giving the cell names separated by ``:'',",
"     such as ``a20:k52''.  Another way to refer to a range is to use",
"     a name previously defined using ``/d''.",
0
};


char *misc[] = {
" ",
" H: Miscellaneous commands:",
" ",
"     Q q ^C   Exit from the program.",
"     ^G ESC   Abort entry of the current command.",
"     ?        Help",
"     !        Shell escape.  Enter a command to run.  ``!!'' repeats",
"              the last command.  Just ``!'' starts an interactive shell.",
"     ^L       Redraw the screen.",
"     ^R       Redraw the screen.  Highlight cells with values but no",
"              expressions.",
"     ^X       Redraw the screen.  Show formulas, not values.",
"     @        Recalculate the spreadsheet.",
"     ^V       Type, in the command line, the name of the current cell.",
"     ^W       Type, in the command line, the current cell's expression.",
"     ^A       Type, in the command line, the current cell's numeric value.",
"     TAB      When the character cursor is on the top line TAB can be used",
"              to start or stop the display of the default range.",
0
};

char *var[] = {
" ",
" I: Variable names:",
" ",
"     K20    Row and column can vary on copies.",
"     $K$20  Row and column stay fixed on copies.",
"     $K20   Row can vary; column stays fixed on copies.",
"     K$20   Row stays fixed; column can vary on copies.",
"     fixed  holds following expession fixed on copies.",
"     Cells and ranges can also be assigned a symbolic name via the",
"     range command ``/d''.",
" ",
" Expressions:",
"     -e      Negation                e<=e  Less than or equal",
"     e+e     Addition                e=e   Equal",
"     e-e     Subtraction             e!=e  Not Equal",
"     e*e     Multiplication          e>=e  Greater than or equal",
"     e/e     Division                e>e  Greater than",
"     e%e     Modulo                  e<e  Less than",
"     e^e     Exponentiation          e&e  Boolean operator AND.",
"     ~e      Boolean operator NOT    e|e     Boolean operator OR",
"     e?e1:e2 Conditional: If the e is non zero then then e1, otherwise e2.",
"     Terms may be constants, variable names, and parenthesized expressions.",
0
};

char *rangef[] = {
" ",
" J: Range functions:",
" ",
"     @sum(r)           Sum all valid cells in the range.",
"     @prod(r)          Multiply together all valid cells in the range.",
"     @avg(r)           Average all valid cells in range.",
"     @max(r)           Return the maximum value in the range.",
"     @min(r)           Return the minimum value in the range.",
"                       See also the numeric versions of max and min.",
"     @stddev(r)        Return the sample standard deviation of ",
"                       the cells in the range.",
"     @index(e,r)       Return the numeric value of the cell at index e",
"                       into range r.",
"     @stindex(e,r)     Return the string value of the cell at index e",
"                       into range r.",
"     @lookup(e,r)      Search through the range r for a value that",
"                       matches e.  The value returned is that from the",
"                       next row and the same column as the match, if",
"                       the range was a single row, or the value from",
"                       the next column and the same row as the match if",
"                       the range was a single column.",
0
};

char *numericf[] = {
" ",
" K: Numeric functions:",
" ",
"     @atan2(e1,e2)     Arc tangent of e1/e2.",
"     @ceil(e)          Smallest integer not less than e.",
"     @eqs(se1,se2)     1 if string expr se1 has the same value as se2.",
"     @exp(e)           Exponential function of e.",
"     @fabs(e)          Absolute value of e.",
"     @floor(e)         The largest integer not greater than e.",
"     @hypot(x,y)       Sqrt(x*x+y*y).",
"     @max(e1,e2,...)   The maximum of the values of the e's.",
"     @min(e1,e2,...)   The minimum of the values of the e's",
"     @nval(se,e)       The numeric value of a named cell.",
"     pi                A constant quite close to pi.",
"     @pow(e1,e2)       e1 raised to the power of e2.",
"     @rnd(e)           Round e to the nearest integer.",
"     @sqrt(e)          Square root of e.",
"     @ston(se)         Convert string expr se to a numeric",
"     @ln(e)   @log(e)           Natural/base 10 logarithm of e.",
"     @dtr(e)  @rtd(e)           Convert degrees to/from radians.",
"     @cos(e)  @sin(e)  @tan(e)  Trig functions of radian arguments.",
"     @asin(e) @acos(e) @atan(e) Inverse trig function.",
0
};

char *stringf[] = {
" ",
" L: String functions:",
" ",
"     #                 Concatenate strings.  For example, the",
"                       string expression ``A0 # \"zy dog\"'' yields",
"                       ``the lazy dog'' if A0 is ``the la''.",
"     @substr(se,e1,e2) Extract characters e1 through e2 from the",
"                       string expression se.  For example,",
"                       ``@substr(\"Nice jacket\" 4, 7)'' yields ",
"                       ``e jac''.",
"     @fmt(se,e)        Convert a number to a string using sprintf(3).",
"                       For example,  ``@fmt(\"*%6.3f*\",10.5)'' yields",
"                       ``*10.500*''.  Use formats are e, E, f, g, and G.", 
"     @sval(se,e)       Return the string value of a cell selected by name.",
"     @ext(se,e)        Call an external function (program or",
"                       script).  Convert e to a string and append it",
"                       to the command line as an argument.  @ext yields",
"                       a string: the first line printed to standard",
"                       output by the command.",
"     String expressions are made up of constant strings (characters",
"     surrounded by quotes), variables, and string functions.",
0
};


char *finf[] = {
" ",
" M: Financial functions:",
" ",
"     @pmt(e1,e2,e3)    @pmt(60000,.01,360) computes the monthly",
"                       payments for a $60000 mortgage at 12%",
"                       annual interest (.01 per month) for 30",
"                       years (360 months).",
" ",
"     @fv(e1,e2,e3)     @fv(100,.005,36) computes the future value",
"                       for of 36 monthly payments of $100 at 6%",
"                       interest (.005 per month).  It answers the",
"                       question:  ``How much will I have in 2",
"                       years if I deposit $100 per month in a",
"                       savings account paying 6% interest com-",
"                       pounded monthly?''",
" ",
"     @pv(e1,e2,e3)     @pv(1000,.015,36) computes the present",
"                       value of an a ordinary annuity of 36",
"                       monthly payments of $1000 at 18% annual",
"                       interest.  It answers the question: ``How",
"                       much can I borrow at 18% for 30 years if I",
"                       pay $1000 per month?''",
0
};


char *timef[] = {
" ",
" N: Time and date functions:",
" ",
"     @now              Return the current time encoded as the",
"                       number of seconds since December 31, 1969,",
"                       midnight, GMT.",
" ",
"     All of the following take an argument expressed in seconds:",
" ",
"     @date(e)          Convert the time in seconds to a date",
"                       string 24 characters long in the following",
"                       form: ``Sun Sep 16 01:03:52 1973''.  Note",
"                       that you can extract pieces of this fixed format",
"                       string with @substr.",
"     @year(e)          Return the year.  Valid years begin with 1970.",
"     @month(e)         Return the month: 1 (Jan) to 12 (Dec).",
"     @day(e)           Return the day of the month: 1 to 31.",
"     @hour(e)          Return the number of hours since midnight: 0 to 23.",
"     @minute(e)        Return the number of minutes since the",
"                       last full hour: 0 to 59.",
"     @second(e)        Return the number of seconds since the",
"                       last full minute: 0 to 59.",
0
};

help()
{
    int option;
    char **ns = intro;

    while((option = pscreen(ns)) != 'q' && option != 'Q') {
    	switch (option) {
	case 'a': case 'A': ns = intro; break;
	case 'b': case 'B': ns = options; break;
	case 'c': case 'C': ns = cursor; break;
	case 'd': case 'D': ns = cell; break;
	case 'e': case 'E': ns = file; break;
	case 'f': case 'F': ns = row; break;
	case 'g': case 'G': ns = range; break;
	case 'h': case 'H': ns = misc; break;
	case 'i': case 'I': ns = var; break;
	case 'j': case 'J': ns = rangef; break;
	case 'k': case 'K': ns = numericf; break;
	case 'l': case 'L': ns = stringf; break;
	case 'm': case 'M': ns = finf; break;
	case 'n': case 'N': ns = timef; break;
	default: ns = intro; break;
	}
    }
    FullUpdate++;
    (void) move(1,0);
    (void) clrtobot();
}

pscreen(screen)
char *screen[];
{
    int line;
    int dbline;

    (void) move(1,0);
    (void) clrtobot();
    dbline = 1;
    for (line = 0; screen[line]; line++) {
	(void) move(dbline++, 4);
	(void) addstr (screen[line]);
	(void) clrtoeol();
    }
    (void) move(0,0);
    (void) printw("Which Screen? [a-n, q]");
    (void) clrtoeol();
    (void) refresh();
    return(nmgetch());
}
\SHAR\EOF\
else
  echo "will not over write ./help.c"
fi
if [ `wc -c ./help.c | awk '{printf $1}'` -ne 15538 ]
then
echo `wc -c ./help.c | awk '{print "Got " $1 ", Expected " 15538}'`
fi
if `test ! -s ./eres.sed`
then
echo "Extracting ./eres.sed"
cat > ./eres.sed << '\SHAR\EOF\'
/%token.*K_/!d
/%token.*K_\(.*\)/s//	"\1",	K_\1,/
\SHAR\EOF\
else
  echo "will not over write ./eres.sed"
fi
if [ `wc -c ./eres.sed | awk '{printf $1}'` -ne 50 ]
then
echo `wc -c ./eres.sed | awk '{print "Got " $1 ", Expected " 50}'`
fi
if `test ! -s ./sres.sed`
then
echo "Extracting ./sres.sed"
cat > ./sres.sed << '\SHAR\EOF\'
/%token.*S_/!d
/%token.*S_\(.*\)/s//	"\1",	S_\1,/
\SHAR\EOF\
else
  echo "will not over write ./sres.sed"
fi
if [ `wc -c ./sres.sed | awk '{printf $1}'` -ne 50 ]
then
echo `wc -c ./sres.sed | awk '{print "Got " $1 ", Expected " 50}'`
fi
if `test ! -s ./Makefile`
then
echo "Extracting ./Makefile"
cat > ./Makefile << '\SHAR\EOF\'
# 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=/h/rgb/bin/sym

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

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

# Set INTERNATIONAL if you need 8 bit characters.  You should
# not set this if you are running 5.3.0.  I think it is OK in 5.3.1.
#INTERNATIONAL=-DINTERNATIONAL

# Set SIGVOID if signal routines are type void.  System 5.3, VMS and ANSI C
# Compliant systems use this.  Most BSD systems do not.
#SIGVOID=-DSIGVOID

# This is the name of a pager like "more" If the line is commented out
# then "more" will be used. "pg" may be appropriate for SYSV
PAGER=-DDFLT_PAGER=\"less\"

# Use this for system V.2
# CFLAGS= -O -DSYSV2 $(SIGVOID)
# LDFLAGS=
# LIB=-lm -lcurses -lPW

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

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

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

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

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

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

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

# The documents in the Archive
DOCS=README CHANGES sc.doc psc.doc tutorial.sc VMS_NOTES BSD_BUGS

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

diff_to_sc:	diff_to_sc.c
	$(CC) ${CFLAGS} -o dtv diff_to_sc.c

p$(name):	psc.c
	$(CC) ${CFLAGS} -o p$(name) psc.c
	cp p$(name) $(EXDIR)/p$(name)

lex.o:	sc.h y.tab.h gram.o
	$(CC) ${CFLAGS} ${SIMPLE} ${INTERNATIONAL} -c lex.c

sc.o:	sc.h sc.c
	$(CC) ${CFLAGS} ${INTERNATIONAL} $(PAGER) -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

help.o: help.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) p$(name) debug core gram.c $(name).1 \
	$(name).man p$(name).man p$(name).1 y.output

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

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

inspect: sc.h sc.c lex.c gram.c interp.c cmds.c crypt.c
	/bruces/ianj/bin/i386/inspect -abv -t 8 sc.c lex.c gram.c interp.c \
	cmds.c crypt.c range.c xmalloc.c help.c

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
	nroff -man $(name).1 > $(name).man

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

p$(name).man:	p$(name).1
	nroff -man p$(name).1 > p$(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)

diffs: ${SRC}
	for i in ${SRC} ${DOCS} ;\
		do \
		rcsdiff -c -r5.1 $$i ;\
		done
\SHAR\EOF\
else
  echo "will not over write ./Makefile"
fi
if [ `wc -c ./Makefile | awk '{printf $1}'` -ne 3645 ]
then
echo `wc -c ./Makefile | awk '{print "Got " $1 ", Expected " 3645}'`
fi
if `test ! -s ./psc.c`
then
echo "Extracting ./psc.c"
cat > ./psc.c << '\SHAR\EOF\'
/* Sc parse routine
 *
 * usage psc options
 * options:
 *   -L		Left justify strings.  Default is right justify.
 *   -r		Assemble data into rows first, not columns.
 *   -R	n	Increment by n between rows 
 *   -C n	Increment by n between columns
 *   -n n	Length of the row (column) should be n.
 *   -s v	Top left location in the spreadsheet should be v; eg, k5
 *   -d c       Use c as the delimiter between the fields.
 *   -k         Keep all delimiters - Default is strip multiple delimiters to 1.
 *   
 *  Author: Robert Bond
 *		$Revision: 6.1 $
 */

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

#define END 0
#define NUM 1
#define ALPHA 2
#define SPACE 3
#define EOL 4

extern char *optarg;
extern int   optind;
char *coltoa();
char *progname;

#ifdef SYSV3
extern void exit();
#else
extern int exit();
#endif

int colfirst = 0;
int r0 = 0;
int c0 = 0;
int rinc = 1;
int cinc = 1;
int leftadj = 0;
int len = 20000;
char delim1 = ' ';
char delim2 = '\t';
int strip_delim = 1;
int fwidth[MAXCOLS];
int precision[MAXCOLS];

char token[1000];

main(argc, argv)
int argc;
char **argv;
{
    int curlen;
    int curcol, coff;
    int currow, roff;
    int first;
    int c;
    register effr, effc;
    int i,j;
    register char *p;

    progname = argv[0];
    while ((c = getopt(argc, argv, "rLks:R:C:n:d:")) != EOF) {
	switch(c) {
	case 'r':
	    colfirst = 1;
	    break;
	case 'L':
	    leftadj = 1;
	    break;
	case 's':
	    c0 = getcol(optarg);
	    r0 = getrow(optarg);
	    break;
	case 'R':
	    rinc = atoi(optarg);
	    break;
	case 'C':
	    cinc = atoi(optarg);
	    break;
	case 'n':
	    len = atoi(optarg);
	    break;
	case 'd':
	    delim1 = optarg[0];
	    delim2 = 0;
	    break;
	case 'k':
	    strip_delim = 0;
	    break;
	default:
	    (void) fprintf(stderr,"Usage: %s [-rkL] [-s v] [-R i] [-C i] [-n i] [-d c]\n", progname);
	    exit(1);
        }
    }

    if (optind < argc) {
	    (void) fprintf(stderr,"Usage: %s [-rL] [-s v] [-R i] [-C i] [-n i] [-d c]\n", progname);
	    exit(1);
    }

    curlen = 0;
    curcol = c0; coff = 0;
    currow = r0; roff = 0;
    first = 1;

    while(1) {

	effr = currow+roff;
	effc = curcol+coff;

	switch(scan()) {
	case END:
	    for (i = 0; i<MAXCOLS; i++) {
		if (precision[i])
		    (void) printf("format %s %d %d\n", coltoa(i), precision[i]+1,
			fwidth[i]);
	    }
	    exit(0);
	case NUM:
	    first = 0;
	    (void) printf("let %s%d = %s\n", coltoa(effc), effr, token);
	    if (effc > MAXCOLS-1)
		(void) fprintf(stderr, "Invalid column used: %s\n", coltoa(effc));
	    else {
		i = 0;
		j = 0;
		p = token;
		while (*p && *p != '.') {
		    p++; i++;
		}
		if (*p) {
		    p++; i++;
		}
		while (*p) {
		    p++; i++; j++;
		}
		if (precision[effc] < i)
		    precision[effc] = i;
		if (fwidth[effc] < j)
		    fwidth[effc] = j;
	    }
	    break;
	case ALPHA:
	    first = 0;
	    if (leftadj)
		(void) printf("leftstring %s%d = \"%s\"\n", coltoa(effc),effr,token); 
	    else
		(void) printf("rightstring %s%d = \"%s\"\n",coltoa(effc),effr,token); 
	    if (effc > MAXCOLS-1)
		(void) fprintf(stderr, "Invalid column used: %s\n", coltoa(effc));
	    else {
		i = strlen(token);
		if (i > precision[effc])
		    precision[effc] = i;
	    }
	    break;
	case SPACE:
	    if (first && strip_delim)
		break;
	    if (colfirst)
		roff++;
	    else
		coff++;
	    break;
	case EOL:
	    curlen++;
	    roff = 0;
	    coff = 0;
	    first = 1;
	    if (colfirst) {
		if (curlen >= len) {
		    curcol = c0;
		    currow += rinc;
		    curlen = 0;
		} else {
		    curcol += cinc;
		}
	    } else {
		if (curlen >= len) {
		    currow = r0;
		    curcol += cinc;
		    curlen = 0;
		} else {
		    currow += rinc;
		}
	    }
	    break;
	}
    }
}

scan()
{
    register int c;
    register char *p;

    p = token;
    c = getchar();

    if (c == EOF)
	return(END);

    if (c == '\n')
	return(EOL);

    if (c == delim1 || c == delim2) {
        if (strip_delim) {
	    while ((c = getchar()) && (c == delim1 || c == delim2))
	        ;
	    (void)ungetc(c, stdin);
	} 
	return(SPACE);
    }

    if (c == '\"') {
	while ((c = getchar()) && c != '\"' && c != '\n' && c != EOF)
	    *p++ = c;
	if (c != '\"')
	    (void)ungetc(c, stdin);
	*p = 0;
	return(ALPHA);
    }

    while (c != delim1 && c != delim2 && c!= '\n' && c != EOF) {
	*p++ = c;
	c = getchar();
    }
    *p = 0;
    (void)ungetc(c, stdin);

    p = token;
    c = *p;
    if (isdigit(c) || c == '.' || c == '-' || c == '+') {
	while(isdigit(c) || c == '.' || c == '-' || c == '+' || c == 'e'
	    || c == 'E') {
		c = *p++;
	}
	if (c == 0)
	    return(NUM);
	else
	    return(ALPHA);
    }

    return(ALPHA);
}
    
getcol(p)
char *p;
{
    register  col;

    if (!p)
	return(0);
    while(*p && !isalpha(*p)) 
	p++; 
    if (!*p)
	return(0);
    col = ((*p & 0137) - 'A');
    if (isalpha(*++p)) 
	col = (col + 1)*26 + ((*p & 0137) - 'A');
    return(col);
}

getrow(p)
char *p;
{
    int row;

    if (!p)
	return(0);
    while(*p && !isdigit(*p))
	p++; 
    if (!*p)
	return(0);
    if (sscanf(p, "%d", &row) != 1)
	return(0);
    return(row);
}

char *
coltoa(col)
int col;
{
    static char rname[3];
    register char *p = rname;

    if (col < 0 || col > 25*26) 
	(void) fprintf(stderr,"coltoa: invalid col: %d", col);

    if (col > 25) {
	*p++ = col/26 + 'A' - 1;
	col %= 26;
    }
    *p++ = col+'A';
    *p = 0;
    return(rname);
}

\SHAR\EOF\
else
  echo "will not over write ./psc.c"
fi
if [ `wc -c ./psc.c | awk '{printf $1}'` -ne 5408 ]
then
echo `wc -c ./psc.c | awk '{print "Got " $1 ", Expected " 5408}'`
fi
echo "Finished archive 4 of 4"
# if you want to concatenate archives, remove anything after this line
exit

-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.



More information about the Comp.sources.unix mailing list