v23i022: The SC Spreadsheet, release 6.8, Part02/06
Rich Salz
rsalz at bbn.com
Wed Sep 5 05:21:20 AEST 1990
Submitted-by: Jeff Buhrt <sawmill!buhrt>
Posting-number: Volume 23, Issue 22
Archive-name: sc6.8/part02
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix at uunet.uu.net if you want that tool.
# Contents: interp.c sc6.8p1.hdr sres.sed
# Wrapped by rsalz at litchi.bbn.com on Fri Jul 13 15:24:18 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 2 (of 6)."'
if test -f 'interp.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'interp.c'\"
else
echo shar: Extracting \"'interp.c'\" \(48096 characters\)
sed "s/^X//" >'interp.c' <<'END_OF_FILE'
X/* SC A Spreadsheet Calculator
X * Expression interpreter and assorted support routines.
X *
X * original by James Gosling, September 1982
X * modified by Mark Weiser and Bruce Israel,
X * University of Maryland
X *
X * More mods Robert Bond, 12/86
X * More mods by Alan Silverstein, 3-4/88, see list of changes.
X * $Revision: 6.8 $
X */
X
X#define DEBUGDTS 1 /* REMOVE ME */
X/* #define EXPRTREE /* expr. dependency tree stuff, not ready yet */
X
X#ifdef aiws
X#undef _C_func /* Fixes for undefined symbols on AIX */
X#endif
X
X#ifdef IEEE_MATH
X#include <ieeefp.h>
X#endif /* IEEE_MATH */
X
X#include <math.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <stdio.h>
X
Xextern int errno; /* set by math functions */
X#ifdef BSD42
X#include <strings.h>
X#include <sys/time.h>
X#ifndef strchr
X#define strchr index
X#endif
X#else
X#include <time.h>
X#ifndef SYSIII
X#include <string.h>
X#endif
X#endif
X
X#include <curses.h>
X#include "sc.h"
X
X#if defined(BSD42) || defined(BSD43)
Xchar *re_comp();
X#endif
X#if defined(SYSV2) || defined(SYSV3)
Xchar *regcmp();
Xchar *regex();
X#endif
X
X#ifdef SIGVOID
X void quit();
X#else
X int quit();
X#endif
X
X/* Use this structure to save the the last 'g' command */
X
Xstruct go_save {
X int g_type;
X double g_n;
X char *g_s;
X int g_row;
X int g_col;
X} gs;
X
X/* g_type can be: */
X
X#define G_NONE 0 /* Starting value - must be 0*/
X#define G_NUM 1
X#define G_STR 2
X#define G_CELL 3
X
X#define ISVALID(r,c) ((r)>=0 && (r)<maxrows && (c)>=0 && (c)<maxcols)
X
Xextern FILE *popen();
X
Xjmp_buf fpe_save;
Xint exprerr; /* Set by eval() and seval() if expression errors */
Xdouble prescale = 1.0; /* Prescale for constants in let() */
Xint extfunc = 0; /* Enable/disable external functions */
Xint loading = 0; /* Set when readfile() is active */
Xdouble fn1_eval();
Xdouble fn2_eval();
Xstruct ent *firstev = (struct ent *)0; /* first expr in the eval list */
X
X#define PI (double)3.14159265358979323846
X#define dtr(x) ((x)*(PI/(double)180.0))
X#define rtd(x) ((x)*(180.0/(double)PI))
X
Xdouble finfunc(fun,v1,v2,v3)
Xint fun;
Xdouble v1,v2,v3;
X{
X double answer,p;
X
X p = fn2_eval(pow, 1 + v2, v3);
X
X switch(fun)
X {
X case PV:
X answer = v1 * (1 - 1/p) / v2;
X break;
X case FV:
X answer = v1 * (p - 1) / v2;
X break;
X case PMT:
X answer = v1 * v2 / (1 - 1/p);
X break;
X default:
X error("Unknown function in finfunc");
X return((double)0);
X }
X return(answer);
X}
X
Xchar *
Xdostindex( val, minr, minc, maxr, maxc)
Xdouble val;
Xint minr, minc, maxr, maxc;
X{
X register r,c;
X register struct ent *p;
X char *pr;
X int x;
X
X x = (int) val;
X r = minr; c = minc;
X p = (struct ent *)0;
X if ( minr == maxr ) { /* look along the row */
X c = minc + x - 1;
X if (c <= maxc && c >=minc)
X p = *ATBL(tbl, r, c);
X } else if ( minc == maxc ) { /* look down the column */
X r = minr + x - 1;
X if (r <= maxr && r >=minr)
X p = *ATBL(tbl, r, c);
X } else {
X error ("range specified to @stindex");
X return((char *)0);
X }
X
X if (p && p->label) {
X pr = xmalloc((unsigned)(strlen(p->label)+1));
X (void)strcpy(pr, p->label);
X return (pr);
X } else
X return((char *)0);
X}
X
Xdouble
Xdoindex( val, minr, minc, maxr, maxc)
Xdouble val;
Xint minr, minc, maxr, maxc;
X{
X double v;
X register r,c;
X register struct ent *p;
X int x;
X
X x = (int) val;
X v = (double)0;
X r = minr; c = minc;
X if ( minr == maxr ) { /* look along the row */
X c = minc + x - 1;
X if (c <= maxc && c >=minc
X && (p = *ATBL(tbl, r, c)) && p->flags&is_valid )
X return p->v;
X }
X else if ( minc == maxc ){ /* look down the column */
X r = minr + x - 1;
X if (r <= maxr && r >=minr
X && (p = *ATBL(tbl, r, c)) && p->flags&is_valid )
X return p->v;
X }
X else error(" range specified to @index");
X return v;
X}
X
Xdouble
Xdolookup( val, minr, minc, maxr, maxc, offr, offc)
Xstruct enode * val;
Xint minr, minc, maxr, maxc, offr, offc;
X{
X double v, ret = (double)0;
X register r,c;
X register struct ent *p = (struct ent *)0;
X int incr,incc,fndr,fndc;
X char *s;
X
X incr = (offc != 0); incc = (offr != 0);
X if (etype(val) == NUM) {
X v = eval(val);
X for (r = minr, c = minc; r <= maxr && c <= maxc; r+=incr, c+=incc) {
X if ( (p = *ATBL(tbl, r, c)) && p->flags&is_valid ) {
X if (p->v <= v) {
X fndr = incc ? (minr + offr) : r;
X fndc = incr ? (minc + offc) : c;
X if (ISVALID(fndr,fndc))
X p = *ATBL(tbl, fndr, fndc);
X else error(" range specified to @[hv]lookup");
X if ( p && p->flags&is_valid)
X ret = p->v;
X } else break;
X }
X }
X } else {
X s = seval(val);
X for (r = minr, c = minc; r <= maxr && c <= maxc; r+=incr, c+=incc) {
X if ( (p = *ATBL(tbl, r, c)) && p->label ) {
X if (strcmp(p->label,s) == 0) {
X fndr = incc ? (minr + offr) : r;
X fndc = incr ? (minc + offc) : c;
X if (ISVALID(fndr,fndc))
X p = *ATBL(tbl, fndr, fndc);
X else error(" range specified to @[hv]lookup");
X break;
X }
X }
X }
X if ( p && p->flags&is_valid)
X ret = p->v;
X xfree(s);
X }
X return ret;
X}
X
Xdouble
Xdocount(minr, minc, maxr, maxc)
Xint minr, minc, maxr, maxc;
X{
X int v;
X register r,c;
X register struct ent *p;
X
X v = 0;
X for (r = minr; r<=maxr; r++)
X for (c = minc; c<=maxc; c++)
X if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid)
X v++;
X return v;
X}
X
Xdouble
Xdosum(minr, minc, maxr, maxc)
Xint minr, minc, maxr, maxc;
X{
X double v;
X register r,c;
X register struct ent *p;
X
X v = (double)0;
X for (r = minr; r<=maxr; r++)
X for (c = minc; c<=maxc; c++)
X if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid)
X v += p->v;
X return v;
X}
X
Xdouble
Xdoprod(minr, minc, maxr, maxc)
Xint minr, minc, maxr, maxc;
X{
X double v;
X register r,c;
X register struct ent *p;
X
X v = 1;
X for (r = minr; r<=maxr; r++)
X for (c = minc; c<=maxc; c++)
X if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid)
X v *= p->v;
X return v;
X}
X
Xdouble
Xdoavg(minr, minc, maxr, maxc)
Xint minr, minc, maxr, maxc;
X{
X double v;
X register r,c,count;
X register struct ent *p;
X
X v = (double)0;
X count = 0;
X for (r = minr; r<=maxr; r++)
X for (c = minc; c<=maxc; c++)
X if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid) {
X v += p->v;
X count++;
X }
X
X if (count == 0)
X return ((double) 0);
X
X return (v / (double)count);
X}
X
Xdouble
Xdostddev(minr, minc, maxr, maxc)
Xint minr, minc, maxr, maxc;
X{
X double lp, rp, v, nd;
X register r,c,n;
X register struct ent *p;
X
X n = 0;
X lp = 0;
X rp = 0;
X for (r = minr; r<=maxr; r++)
X for (c = minc; c<=maxc; c++)
X if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid) {
X v = p->v;
X lp += v*v;
X rp += v;
X n++;
X }
X
X if ((n == 0) || (n == 1))
X return ((double) 0);
X nd = (double)n;
X return (sqrt((nd*lp-rp*rp)/(nd*(nd-1))));
X}
X
Xdouble
Xdomax(minr, minc, maxr, maxc)
Xint minr, minc, maxr, maxc;
X{
X double v = (double)0;
X register r,c,count;
X register struct ent *p;
X
X count = 0;
X for (r = minr; r<=maxr; r++)
X for (c = minc; c<=maxc; c++)
X if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid) {
X if (!count) {
X v = p->v;
X count++;
X } else if (p->v > v)
X v = p->v;
X }
X
X if (count == 0)
X return ((double) 0);
X
X return (v);
X}
X
Xdouble
Xdomin(minr, minc, maxr, maxc)
Xint minr, minc, maxr, maxc;
X{
X double v = (double)0;
X register r,c,count;
X register struct ent *p;
X
X count = 0;
X for (r = minr; r<=maxr; r++)
X for (c = minc; c<=maxc; c++)
X if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid) {
X if (!count) {
X v = p->v;
X count++;
X } else if (p->v < v)
X v = p->v;
X }
X
X if (count == 0)
X return ((double) 0);
X
X return (v);
X}
X
X#define sec_min 60
X#define sec_hr 3600L
X#define sec_day 86400L
X#define sec_yr 31471200L /* 364.25 days/yr */
X#define sec_mo 2622600L /* sec_yr/12: sort of an average */
Xint mdays[12]={ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
X
Xdouble
Xdodts(mo, day, yr)
Xint mo, day, yr;
X{
X long trial;
X register struct tm *tp;
X register int i;
X register long jdate;
X
X mdays[1] = 28 + (yr%4 == 0);
X
X if (mo < 1 || mo > 12 || day < 1 || day > mdays[--mo] ||
X yr > 1999 || yr < 1970) {
X error("@dts: invalid argument");
X return(0.0);
X }
X
X jdate = day-1;
X for (i=0; i<mo; i++)
X jdate += mdays[i];
X for (i = 1970; i < yr; i++)
X jdate += 365 + (i%4 == 0);
X
X trial = jdate * sec_day;
X
X yr -= 1900;
X
X tp = localtime(&trial);
X
X if (tp->tm_year != yr) {
X /*
X * We may fail this test once a year because of time zone
X * and daylight savings time errors. This bounces the
X * trial time past the boundary. The error introduced is
X * corrected below.
X */
X trial += sec_day*(yr - tp->tm_year);
X tp = localtime(&trial);
X }
X if (tp->tm_mon != mo) {
X /* We may fail this test once a month. */
X trial += sec_day*(mo - tp->tm_mon);
X tp = localtime(&trial);
X }
X if (tp->tm_mday + tp->tm_hour + tp->tm_min + tp->tm_sec != day) {
X trial -= (tp->tm_mday - day)*sec_day + tp->tm_hour*sec_hr
X + tp->tm_min*sec_min + tp->tm_sec;
X }
X
X#ifdef DEBUGDTS
X tp = localtime(&trial);
X if (tp->tm_mday + tp->tm_hour + tp->tm_min + tp->tm_sec +
X tp->tm_year + tp->tm_mon != yr+mo+day)
X error("Dts broke down");
X#endif
X
X return ((double)trial);
X}
X
Xdouble
Xdotts(hr, min, sec)
Xint hr, min, sec;
X{
X if (hr < 0 || hr > 23 || min < 0 || min > 59 || sec < 0 || sec > 59) {
X error ("@tts: Invalid argument");
X return ((double)0);
X }
X return ((double)(sec+min*60+hr*3600));
X}
X
Xdouble
Xdotime(which, when)
Xint which;
Xdouble when;
X{
X long time();
X
X static long t_cache;
X static struct tm tm_cache;
X struct tm *tp;
X long tloc;
X
X if (which == NOW)
X return (double)time((long *)0);
X
X tloc = (long)when;
X
X if (tloc != t_cache) {
X tp = localtime(&tloc);
X tm_cache = *tp;
X tm_cache.tm_mon += 1;
X tm_cache.tm_year += 1900;
X t_cache = tloc;
X }
X
X switch (which) {
X case HOUR: return((double)(tm_cache.tm_hour));
X case MINUTE: return((double)(tm_cache.tm_min));
X case SECOND: return((double)(tm_cache.tm_sec));
X case MONTH: return((double)(tm_cache.tm_mon));
X case DAY: return((double)(tm_cache.tm_mday));
X case YEAR: return((double)(tm_cache.tm_year));
X }
X /* Safety net */
X return ((double)0);
X}
X
Xdouble
Xdoston(s)
Xchar *s;
X{
X char *strtof();
X double v;
X
X if (!s)
X return((double)0);
X
X (void)strtof(s, &v);
X xfree(s);
X return(v);
X}
X
Xdouble
Xdoeqs(s1, s2)
Xchar *s1, *s2;
X{
X double v;
X
X if (!s1 && !s2)
X return((double)1.0);
X
X if (!s1 || !s2)
X v = 0.0;
X else if (strcmp(s1, s2) == 0)
X v = 1.0;
X else
X v = 0.0;
X
X if (s1)
X xfree(s1);
X
X if (s2)
X xfree(s2);
X
X return(v);
X}
X
X
X/*
X * Given a string representing a column name and a value which is a column
X * number, return a pointer to the selected cell's entry, if any, else 0. Use
X * only the integer part of the column number. Always free the string.
X */
X
Xstruct ent *
Xgetent (colstr, rowdoub)
X char *colstr;
X double rowdoub;
X{
X int collen; /* length of string */
X int row, col; /* integer values */
X struct ent *ep = (struct ent *)0; /* selected entry */
X
X if (((row = (int) floor (rowdoub)) >= 0)
X && (row < maxrows) /* in range */
X && ((collen = strlen (colstr)) <= 2) /* not too long */
X && ((col = atocol (colstr, collen)) >= 0)
X && (col < maxcols)) /* in range */
X {
X ep = *ATBL(tbl, row, col);
X }
X
X xfree (colstr);
X return (ep);
X}
X
X
X/*
X * Given a string representing a column name and a value which is a column
X * number, return the selected cell's numeric value, if any.
X */
X
Xdouble
Xdonval (colstr, rowdoub)
X char *colstr;
X double rowdoub;
X{
X struct ent *ep;
X
X return (((ep = getent (colstr, rowdoub)) && ((ep -> flags) & is_valid)) ?
X (ep -> v) : (double)0);
X}
X
X
X/*
X * The list routines (e.g. dolmax) are called with an LMAX enode.
X * The left pointer is a chain of ELIST nodes, the right pointer
X * is a value.
X */
Xdouble
Xdolmax(ep)
Xstruct enode *ep;
X{
X register int count = 0;
X register double maxval = 0; /* Assignment to shut up lint */
X register struct enode *p;
X register double v;
X
X for (p = ep; p; p = p->e.o.left) {
X v = eval(p->e.o.right);
X if (!count || v > maxval) {
X maxval = v; count++;
X }
X }
X if (count) return maxval;
X else return (double)0;
X}
X
Xdouble
Xdolmin(ep)
Xstruct enode *ep;
X{
X register int count = 0;
X register double minval = 0; /* Assignment to shut up lint */
X register struct enode *p;
X register double v;
X
X for (p = ep; p; p = p->e.o.left) {
X v = eval(p->e.o.right);
X if (!count || v < minval) {
X minval = v; count++;
X }
X }
X if (count) return minval;
X else return (double)0;
X}
X
Xdouble
Xeval(e)
Xregister struct enode *e;
X{
X if (e == (struct enode *)0) return (double)0;
X switch (e->op) {
X case '+': return (eval(e->e.o.left) + eval(e->e.o.right));
X case '-': return (eval(e->e.o.left) - eval(e->e.o.right));
X case '*': return (eval(e->e.o.left) * eval(e->e.o.right));
X case '/': return (eval(e->e.o.left) / eval(e->e.o.right));
X case '%': { double num, denom;
X num = floor(eval(e->e.o.left));
X denom = floor(eval (e->e.o.right));
X return denom ? num - floor(num/denom)*denom : (double)0; }
X case '^': return (fn2_eval(pow,eval(e->e.o.left),eval(e->e.o.right)));
X case '<': return (eval(e->e.o.left) < eval(e->e.o.right));
X case '=': return (eval(e->e.o.left) == eval(e->e.o.right));
X case '>': return (eval(e->e.o.left) > eval(e->e.o.right));
X case '&': return (eval(e->e.o.left) && eval(e->e.o.right));
X case '|': return (eval(e->e.o.left) || eval(e->e.o.right));
X case IF:
X case '?': return eval(e->e.o.left) ? eval(e->e.o.right->e.o.left)
X : eval(e->e.o.right->e.o.right);
X case 'm': return (-eval(e->e.o.right));
X case 'f': return (eval(e->e.o.right));
X case '~': return (eval(e->e.o.right) == 0.0);
X case 'k': return (e->e.k);
X case 'v': return (e->e.v.vp->v);
X case INDEX:
X case LOOKUP:
X case HLOOKUP:
X case VLOOKUP:
X { register r,c;
X register maxr, maxc;
X register minr, minc;
X maxr = e->e.o.right->e.r.right.vp -> row;
X maxc = e->e.o.right->e.r.right.vp -> col;
X minr = e->e.o.right->e.r.left.vp -> row;
X minc = e->e.o.right->e.r.left.vp -> col;
X if (minr>maxr) r = maxr, maxr = minr, minr = r;
X if (minc>maxc) c = maxc, maxc = minc, minc = c;
X switch(e->op){
X case LOOKUP:
X return dolookup(e->e.o.left, minr, minc, maxr, maxc,
X minr==maxr, minc==maxc);
X case HLOOKUP:
X return dolookup(e->e.o.left->e.o.left, minr,minc,maxr,maxc,
X (int) eval(e->e.o.left->e.o.right), 0);
X case VLOOKUP:
X return dolookup(e->e.o.left->e.o.left, minr,minc,maxr,maxc,
X 0, (int) eval(e->e.o.left->e.o.right));
X case INDEX:
X return doindex(eval(e->e.o.left), minr, minc, maxr, maxc);
X }
X }
X case REDUCE | '+':
X case REDUCE | '*':
X case REDUCE | 'a':
X case REDUCE | 'c':
X case REDUCE | 's':
X case REDUCE | MAX:
X case REDUCE | MIN:
X { register r,c;
X register maxr, maxc;
X register minr, minc;
X maxr = e->e.r.right.vp -> row;
X maxc = e->e.r.right.vp -> col;
X minr = e->e.r.left.vp -> row;
X minc = e->e.r.left.vp -> col;
X if (minr>maxr) r = maxr, maxr = minr, minr = r;
X if (minc>maxc) c = maxc, maxc = minc, minc = c;
X switch (e->op) {
X case REDUCE | '+': return dosum(minr, minc, maxr, maxc);
X case REDUCE | '*': return doprod(minr, minc, maxr, maxc);
X case REDUCE | 'a': return doavg(minr, minc, maxr, maxc);
X case REDUCE | 'c': return docount(minr, minc, maxr, maxc);
X case REDUCE | 's': return dostddev(minr, minc, maxr, maxc);
X case REDUCE | MAX: return domax(minr, minc, maxr, maxc);
X case REDUCE | MIN: return domin(minr, minc, maxr, maxc);
X }
X }
X case ABS: return (fn1_eval( fabs, eval(e->e.o.right)));
X case ACOS: return (fn1_eval( acos, eval(e->e.o.right)));
X case ASIN: return (fn1_eval( asin, eval(e->e.o.right)));
X case ATAN: return (fn1_eval( atan, eval(e->e.o.right)));
X case ATAN2: return (fn2_eval( atan2, eval(e->e.o.left), eval(e->e.o.right)));
X case CEIL: return (fn1_eval( ceil, eval(e->e.o.right)));
X case COS: return (fn1_eval( cos, eval(e->e.o.right)));
X case EXP: return (fn1_eval( exp, eval(e->e.o.right)));
X case FABS: return (fn1_eval( fabs, eval(e->e.o.right)));
X case FLOOR: return (fn1_eval( floor, eval(e->e.o.right)));
X case HYPOT: return (fn2_eval( hypot, eval(e->e.o.left), eval(e->e.o.right)));
X case LOG: return (fn1_eval( log, eval(e->e.o.right)));
X case LOG10: return (fn1_eval( log10, eval(e->e.o.right)));
X case POW: return (fn2_eval( pow, eval(e->e.o.left), eval(e->e.o.right)));
X case SIN: return (fn1_eval( sin, eval(e->e.o.right)));
X case SQRT: return (fn1_eval( sqrt, eval(e->e.o.right)));
X case TAN: return (fn1_eval( tan, eval(e->e.o.right)));
X case DTR: return (dtr(eval(e->e.o.right)));
X case RTD: return (rtd(eval(e->e.o.right)));
X case RND: {
X double temp;
X temp = eval(e->e.o.right);
X return(temp-floor(temp) < 0.5 ?
X floor(temp) : ceil(temp));
X }
X case ROUND: {
X double temp = eval(e->e.o.left);
X int prec = (int) eval(e->e.o.right), scal = 1;
X while (prec-- > 0) scal *= 10;
X temp *= scal;
X temp = ((temp-floor(temp)) < 0.5 ?
X floor(temp) : ceil(temp));
X return(temp / scal);
X }
X case FV:
X case PV:
X case PMT: return(finfunc(e->op,eval(e->e.o.left),
X eval(e->e.o.right->e.o.left),
X eval(e->e.o.right->e.o.right)));
X case HOUR: return (dotime(HOUR, eval(e->e.o.right)));
X case MINUTE: return (dotime(MINUTE, eval(e->e.o.right)));
X case SECOND: return (dotime(SECOND, eval(e->e.o.right)));
X case MONTH: return (dotime(MONTH, eval(e->e.o.right)));
X case DAY: return (dotime(DAY, eval(e->e.o.right)));
X case YEAR: return (dotime(YEAR, eval(e->e.o.right)));
X case NOW: return (dotime(NOW, (double)0.0));
X case DTS: return (dodts((int)eval(e->e.o.left),
X (int)eval(e->e.o.right->e.o.left),
X (int)eval(e->e.o.right->e.o.right)));
X case TTS: return (dotts((int)eval(e->e.o.left),
X (int)eval(e->e.o.right->e.o.left),
X (int)eval(e->e.o.right->e.o.right)));
X case STON: return (doston(seval(e->e.o.right)));
X case EQS: return (doeqs(seval(e->e.o.right),seval(e->e.o.left)));
X case LMAX: return dolmax(e);
X case LMIN: return dolmin(e);
X case NVAL: return (donval(seval(e->e.o.left),eval(e->e.o.right)));
X default: error ("Illegal numeric expression");
X exprerr = 1;
X }
X return((double)0.0);
X}
X
X#ifdef SIGVOID
Xvoid
X#endif
Xeval_fpe(signo) /* Trap for FPE errors in eval */
Xint signo;
X{
X#ifdef IEEE_MATH
X (void)fpsetsticky((fp_except)0); /* Clear exception */
X#endif /* IEEE_MATH */
X longjmp(fpe_save, 1);
X}
X
Xdouble fn1_eval(fn, arg)
Xdouble (*fn)();
Xdouble arg;
X{
X double res;
X errno = 0;
X res = (*fn)(arg);
X if(errno)
X eval_fpe(0);
X
X return res;
X}
X
Xdouble fn2_eval(fn, arg1, arg2)
Xdouble (*fn)();
Xdouble arg1, arg2;
X{
X double res;
X errno = 0;
X res = (*fn)(arg1, arg2);
X if(errno)
X eval_fpe(0);
X
X return res;
X}
X
X/*
X * Rules for string functions:
X * Take string arguments which they xfree.
X * All returned strings are assumed to be xalloced.
X */
X
Xchar *
Xdocat(s1, s2)
Xregister char *s1, *s2;
X{
X register char *p;
X char *arg1, *arg2;
X
X if (!s1 && !s2)
X return((char *)0);
X arg1 = s1 ? s1 : "";
X arg2 = s2 ? s2 : "";
X p = xmalloc((unsigned)(strlen(arg1)+strlen(arg2)+1));
X (void) strcpy(p, arg1);
X (void) strcat(p, arg2);
X if (s1)
X xfree(s1);
X if (s2)
X xfree(s2);
X return(p);
X}
X
Xchar *
Xdodate(tloc)
Xlong tloc;
X{
X char *tp;
X char *p;
X
X tp = ctime(&tloc);
X tp[24] = '\0';
X p = xmalloc((unsigned)25);
X (void) strcpy(p, tp);
X return(p);
X}
X
X
Xchar *
Xdofmt(fmtstr, v)
Xchar *fmtstr;
Xdouble v;
X{
X char buff[FBUFLEN];
X char *p;
X
X if (!fmtstr)
X return((char *)0);
X (void)sprintf(buff, fmtstr, v);
X p = xmalloc((unsigned)(strlen(buff)+1));
X (void) strcpy(p, buff);
X xfree(fmtstr);
X return(p);
X}
X
X
X/*
X * Given a command name and a value, run the command with the given value and
X * read and return its first output line (only) as an allocated string, always
X * a copy of prevstr, which is set appropriately first unless external
X * functions are disabled, in which case the previous value is used. The
X * handling of prevstr and freeing of command is tricky. Returning an
X * allocated string in all cases, even if null, insures cell expressions are
X * written to files, etc.
X */
X
X#ifdef VMS
Xchar *
Xdoext(command, value)
Xchar *command;
Xdouble value;
X{
X error("Warning: External functions unavailable on VMS");
X if (command)
X xfree(command);
X return (strcpy (xmalloc((unsigned) 1), "\0"));
X}
X
X#else /* VMS */
X
Xchar *
Xdoext (command, value)
Xchar *command;
Xdouble value;
X{
X static char *prevstr = (char *)0; /* previous result */
X char buff[FBUFLEN]; /* command line/return, not permanently alloc */
X
X if (!prevstr) {
X prevstr = xmalloc((unsigned)1);
X *prevstr = '\0';
X }
X if (!extfunc) {
X error ("Warning: external functions disabled; using %s value",
X prevstr ? "previous" : "null");
X
X if (command) xfree (command);
X } else {
X if (prevstr) xfree (prevstr); /* no longer needed */
X prevstr = '\0';
X
X if ((! command) || (! *command)) {
X error ("Warning: external function given null command name");
X if (command) xfree (command);
X } else {
X FILE *pp;
X
X (void) sprintf (buff, "%s %g", command, value); /* build cmd line */
X xfree (command);
X
X error ("Running external function...");
X (void) refresh();
X
X if ((pp = popen (buff, "r")) == (FILE *) NULL) /* run it */
X error ("Warning: running \"%s\" failed", buff);
X else {
X if (fgets (buff, sizeof(buff)-1, pp) == NULL) /* one line */
X error ("Warning: external function returned nothing");
X else {
X char *cp;
X
X error (""); /* erase notice */
X buff[sizeof(buff)-1] = '\0';
X
X if (cp = strchr (buff, '\n')) /* contains newline */
X *cp = '\0'; /* end string there */
X
X (void) strcpy (prevstr =
X xmalloc ((unsigned) (strlen (buff) + 1)), buff);
X /* save alloc'd copy */
X }
X (void) pclose (pp);
X
X } /* else */
X } /* else */
X } /* else */
X return (strcpy (xmalloc ((unsigned) (strlen (prevstr) + 1)), prevstr));
X}
X
X#endif /* VMS */
X
X
X/*
X * Given a string representing a column name and a value which is a column
X * number, return the selected cell's string value, if any. Even if none,
X * still allocate and return a null string so the cell has a label value so
X * the expression is saved in a file, etc.
X */
X
Xchar *
Xdosval (colstr, rowdoub)
X char *colstr;
X double rowdoub;
X{
X struct ent *ep;
X char *label;
X
X label = (ep = getent (colstr, rowdoub)) ? (ep -> label) : "";
X return (strcpy (xmalloc ((unsigned) (strlen (label) + 1)), label));
X}
X
X
X/*
X * Substring: Note that v1 and v2 are one-based to users, but zero-based
X * when calling this routine.
X */
X
Xchar *
Xdosubstr(s, v1, v2)
Xchar *s;
Xregister int v1,v2;
X{
X register char *s1, *s2;
X char *p;
X
X if (!s)
X return((char *)0);
X
X if (v2 >= strlen (s)) /* past end */
X v2 = strlen (s) - 1; /* to end */
X
X if (v1 < 0 || v1 > v2) { /* out of range, return null string */
X xfree(s);
X p = xmalloc((unsigned)1);
X p[0] = '\0';
X return(p);
X }
X s2 = p = xmalloc((unsigned)(v2-v1+2));
X s1 = &s[v1];
X for(; v1 <= v2; s1++, s2++, v1++)
X *s2 = *s1;
X *s2 = '\0';
X xfree(s);
X return(p);
X}
X
Xchar *
Xseval(se)
Xregister struct enode *se;
X{
X register char *p;
X
X if (se == (struct enode *)0) return (char *)0;
X switch (se->op) {
X case O_SCONST: p = xmalloc((unsigned)(strlen(se->e.s)+1));
X (void) strcpy(p, se->e.s);
X return(p);
X case O_VAR: {
X struct ent *ep;
X ep = se->e.v.vp;
X
X if (!ep->label)
X return((char *)0);
X p = xmalloc((unsigned)(strlen(ep->label)+1));
X (void) strcpy(p, ep->label);
X return(p);
X }
X case '#': return(docat(seval(se->e.o.left), seval(se->e.o.right)));
X case 'f': return(seval(se->e.o.right));
X case IF:
X case '?': return(eval(se->e.o.left) ? seval(se->e.o.right->e.o.left)
X : seval(se->e.o.right->e.o.right));
X case DATE: return(dodate((long)(eval(se->e.o.right))));
X case FMT: return(dofmt(seval(se->e.o.left), eval(se->e.o.right)));
X case STINDEX:
X { register r,c;
X register maxr, maxc;
X register minr, minc;
X maxr = se->e.o.right->e.r.right.vp -> row;
X maxc = se->e.o.right->e.r.right.vp -> col;
X minr = se->e.o.right->e.r.left.vp -> row;
X minc = se->e.o.right->e.r.left.vp -> col;
X if (minr>maxr) r = maxr, maxr = minr, minr = r;
X if (minc>maxc) c = maxc, maxc = minc, minc = c;
X return dostindex(eval(se->e.o.left), minr, minc, maxr, maxc);
X }
X case EXT: return(doext(seval(se->e.o.left), eval(se->e.o.right)));
X case SVAL: return(dosval(seval(se->e.o.left), eval(se->e.o.right)));
X case SUBSTR: return(dosubstr(seval(se->e.o.left),
X (int)eval(se->e.o.right->e.o.left) - 1,
X (int)eval(se->e.o.right->e.o.right) - 1));
X default:
X error ("Illegal string expression");
X exprerr = 1;
X return((char *)0);
X }
X}
X
X/*
X * The graph formed by cell expressions which use other cells's values is not
X * evaluated "bottom up". The whole table is merely re-evaluated cell by cell,
X * top to bottom, left to right, in RealEvalAll(). Each cell's expression uses
X * constants in other cells. However, RealEvalAll() notices when a cell gets a
X * new numeric or string value, and reports if this happens for any cell.
X * EvalAll() repeats calling RealEvalAll() until there are no changes or the
X * evaluation count expires.
X */
X
Xint propagation = 10; /* max number of times to try calculation */
X
Xvoid
Xsetiterations(i)
Xint i;
X{
X if(i<1) {
X error("iteration count must be at least 1");
X propagation = 1;
X }
X else propagation = i;
X}
X
Xvoid
XEvalAll () {
X int lastcnt, repct = 0;
X
X while ((lastcnt = RealEvalAll()) && (repct++ <= propagation));
X if((propagation>1)&& (lastcnt >0 ))
X error("Still changing after %d iterations",propagation-1);
X}
X
X/*
X * Evaluate all cells which have expressions and alter their numeric or string
X * values. Return the number of cells which changed.
X */
X
Xint
XRealEvalAll () {
X register int i,j;
X int chgct = 0;
X register struct ent *p;
X
X (void) signal(SIGFPE, eval_fpe);
X#ifdef EXPRTREE
X for (p = firstev; p; p = p->evnext)
X RealEvalOne(p, &chgct);
X#else
X if(calc_order == BYROWS ) {
X for (i=0; i<=maxrow; i++)
X for (j=0; j<=maxcol; j++)
X if ((p=tbl[i][j]) && p->expr) RealEvalOne(p,i,j, &chgct);
X }
X else if ( calc_order == BYCOLS ) {
X for (j=0; j<=maxcol; j++)
X { for (i=0; i<=maxrow; i++)
X if ((p=tbl[i][j]) && p->expr) RealEvalOne(p,i,j, &chgct);
X }
X }
X else error("Internal error calc_order");
X#endif
X
X (void) signal(SIGFPE, quit);
X return(chgct);
X}
X
Xvoid
X#ifdef EXPRTREE
XRealEvalOne(p, chgct)
Xregister struct ent *p;
Xint *chgct;
X#else
XRealEvalOne(p, i, j, chgct)
Xregister struct ent *p;
Xint i, j, *chgct;
X#endif
X{
X if (p->flags & is_strexpr) {
X char *v;
X if (setjmp(fpe_save)) {
X#ifdef EXPRTREE
X error("Floating point exception %s", v_name(p->row, p->col));
X#else
X error("Floating point exception %s", v_name(i, j));
X#endif
X v = "";
X } else {
X v = seval(p->expr);
X }
X if (!v && !p->label) /* Everything's fine */
X return;
X if (!p->label || !v || strcmp(v, p->label) != 0) {
X (*chgct)++;
X p->flags |= is_changed;
X changed++;
X }
X if(p->label)
X xfree(p->label);
X p->label = v;
X } else {
X double v;
X if (setjmp(fpe_save)) {
X#ifdef EXPRTREE
X error("Floating point exception %s", v_name(p->row, p->col));
X#else
X error("Floating point exception %s", v_name(i, j));
X#endif
X v = (double)0.0;
X } else {
X v = eval (p->expr);
X }
X if (v != p->v) {
X p->v = v; (*chgct)++;
X p->flags |= is_changed|is_valid;
X changed++;
X }
X }
X}
X
Xstruct enode *
Xnew(op, a1, a2)
Xint op;
Xstruct enode *a1, *a2;
X{
X register struct enode *p;
X p = (struct enode *) xmalloc ((unsigned)sizeof (struct enode));
X p->op = op;
X p->e.o.left = a1;
X p->e.o.right = a2;
X return p;
X}
X
Xstruct enode *
Xnew_var(op, a1)
Xint op;
Xstruct ent_ptr a1;
X{
X register struct enode *p;
X p = (struct enode *) xmalloc ((unsigned)sizeof (struct enode));
X p->op = op;
X p->e.v = a1;
X return p;
X}
X
Xstruct enode *
Xnew_range(op, a1)
Xint op;
Xstruct range_s a1;
X{
X register struct enode *p;
X p = (struct enode *) xmalloc ((unsigned)sizeof (struct enode));
X p->op = op;
X p->e.r = a1;
X return p;
X}
X
Xstruct enode *
Xnew_const(op, a1)
Xint op;
Xdouble a1;
X{
X register struct enode *p;
X p = (struct enode *) xmalloc ((unsigned)sizeof (struct enode));
X p->op = op;
X p->e.k = a1;
X return p;
X}
X
Xstruct enode *
Xnew_str(s)
Xchar *s;
X{
X register struct enode *p;
X
X p = (struct enode *) xmalloc ((unsigned)sizeof(struct enode));
X p->op = O_SCONST;
X p->e.s = s;
X return(p);
X}
X
Xvoid
Xcopy(dv1, dv2, v1, v2)
Xstruct ent *dv1, *dv2, *v1, *v2;
X{
X int minsr, minsc;
X int maxsr, maxsc;
X int mindr, mindc;
X int maxdr, maxdc;
X int vr, vc;
X int r, c;
X
X mindr = dv1->row;
X mindc = dv1->col;
X maxdr = dv2->row;
X maxdc = dv2->col;
X if (mindr>maxdr) r = maxdr, maxdr = mindr, mindr = r;
X if (mindc>maxdc) c = maxdc, maxdc = mindc, mindc = c;
X maxsr = v2->row;
X maxsc = v2->col;
X minsr = v1->row;
X minsc = v1->col;
X if (minsr>maxsr) r = maxsr, maxsr = minsr, minsr = r;
X if (minsc>maxsc) c = maxsc, maxsc = minsc, minsc = c;
X checkbounds(&maxdr, &maxdc);
X
X erase_area(mindr, mindc, maxdr, maxdc);
X if (minsr == maxsr && minsc == maxsc) {
X /* Source is a single cell */
X for(vr = mindr; vr <= maxdr; vr++)
X for (vc = mindc; vc <= maxdc; vc++)
X copyrtv(vr, vc, minsr, minsc, maxsr, maxsc);
X } else if (minsr == maxsr) {
X /* Source is a single row */
X for (vr = mindr; vr <= maxdr; vr++)
X copyrtv(vr, mindc, minsr, minsc, maxsr, maxsc);
X } else if (minsc == maxsc) {
X /* Source is a single column */
X for (vc = mindc; vc <= maxdc; vc++)
X copyrtv(mindr, vc, minsr, minsc, maxsr, maxsc);
X } else {
X /* Everything else */
X copyrtv(mindr, mindc, minsr, minsc, maxsr, maxsc);
X }
X sync_refs();
X}
X
Xvoid
Xcopyrtv(vr, vc, minsr, minsc, maxsr, maxsc)
Xint vr, vc, minsr, minsc, maxsr, maxsc;
X{
X register struct ent *p;
X register struct ent *n;
X register int sr, sc;
X register int dr, dc;
X
X for (dr=vr, sr=minsr; sr<=maxsr; sr++, dr++)
X for (dc=vc, sc=minsc; sc<=maxsc; sc++, dc++) {
X if (p = *ATBL(tbl, sr, sc))
X { n = lookat (dr, dc);
X (void) clearent(n);
X copyent( n, p, dr - sr, dc - sc);
X }
X else
X if (n = *ATBL(tbl, dr, dc))
X (void) clearent(n);
X }
X}
X
Xvoid
Xeraser(v1, v2)
Xstruct ent *v1, *v2;
X{
X FullUpdate++;
X flush_saved();
X erase_area(v1->row, v1->col, v2->row, v2->col);
X sync_refs();
X}
X
X/* Goto subroutines */
X
Xvoid
Xg_free()
X{
X switch (gs.g_type) {
X case G_STR: xfree(gs.g_s); break;
X default: break;
X }
X gs.g_type = G_NONE;
X}
X
Xvoid
Xgo_last()
X{
X switch (gs.g_type) {
X case G_NONE:
X error("Nothing to repeat"); break;
X case G_NUM:
X num_search(gs.g_n);
X break;
X case G_CELL:
X moveto(gs.g_row, gs.g_col);
X break;
X case G_STR:
X gs.g_type = G_NONE; /* Don't free the string */
X str_search(gs.g_s);
X break;
X
X default: error("go_last: internal error");
X }
X}
X
Xvoid
Xmoveto(row, col)
Xint row, col;
X{
X currow = row;
X curcol = col;
X g_free();
X gs.g_type = G_CELL;
X gs.g_row = currow;
X gs.g_col = curcol;
X}
X
Xvoid
Xnum_search(n)
Xdouble n;
X{
X register struct ent *p;
X register int r,c;
X int endr, endc;
X
X g_free();
X gs.g_type = G_NUM;
X gs.g_n = n;
X
X if (currow > maxrow)
X endr = maxrow ? maxrow-1 : 0;
X else
X endr = currow;
X if (curcol > maxcol)
X endc = maxcol ? maxcol-1 : 0;
X else
X endc = curcol;
X r = endr;
X c = endc;
X do {
X if (c < maxcol)
X c++;
X else {
X if (r < maxrow) {
X while(++r < maxrow && row_hidden[r]) /* */;
X c = 0;
X } else {
X r = 0;
X c = 0;
X }
X }
X if (r == endr && c == endc) {
X error("Number not found");
X return;
X }
X p = *ATBL(tbl, r, c);
X } while(col_hidden[c] || !p || p && (!(p->flags & is_valid)
X || (p->flags&is_valid) && p->v != n));
X currow = r;
X curcol = c;
X}
X
Xvoid
Xstr_search(s)
Xchar *s;
X{
X register struct ent *p;
X register int r,c;
X int endr, endc;
X char *tmp;
X
X#if defined(BSD42) || defined(BSD43)
X if ((tmp = re_comp(s)) != (char *)0) {
X xfree(s);
X error(tmp);
X return;
X }
X#endif
X#if defined(SYSV2) || defined(SYSV3)
X if ((tmp = regcmp(s, (char *)0)) == (char *)0) {
X xfree(s);
X error("Invalid search string");
X return;
X }
X#endif
X g_free();
X gs.g_type = G_STR;
X gs.g_s = s;
X if (currow > maxrow)
X endr = maxrow ? maxrow-1 : 0;
X else
X endr = currow;
X if (curcol > maxcol)
X endc = maxcol ? maxcol-1 : 0;
X else
X endc = curcol;
X r = endr;
X c = endc;
X do {
X if (c < maxcol)
X c++;
X else {
X if (r < maxrow) {
X while(++r < maxrow && row_hidden[r]) /* */;
X c = 0;
X } else {
X r = 0;
X c = 0;
X }
X }
X if (r == endr && c == endc) {
X error("String not found");
X#if defined(SYSV2) || defined(SYSV3)
X free(tmp);
X#endif
X return;
X }
X p = *ATBL(tbl, r, c);
X } while(col_hidden[c] || !p || p && (!(p->label)
X#if defined(BSD42) || defined(BSD43)
X || (re_exec(p->label) == 0)));
X#else
X#if defined(SYSV2) || defined(SYSV3)
X || (regex(tmp, p->label) == (char *)0)));
X#else
X || (strcmp(s, p->label) != 0)));
X#endif
X#endif
X currow = r;
X curcol = c;
X#if defined(SYSV2) || defined(SYSV3)
X free(tmp);
X#endif
X}
X
Xvoid
Xfill (v1, v2, start, inc)
Xstruct ent *v1, *v2;
Xdouble start, inc;
X{
X register r,c;
X register struct ent *n;
X int maxr, maxc;
X int minr, minc;
X
X maxr = v2->row;
X maxc = v2->col;
X minr = v1->row;
X minc = v1->col;
X if (minr>maxr) r = maxr, maxr = minr, minr = r;
X if (minc>maxc) c = maxc, maxc = minc, minc = c;
X checkbounds(&maxr, &maxc);
X if (minr < 0) minr = 0;
X if (minr < 0) minr = 0;
X
X FullUpdate++;
X if( calc_order == BYROWS ) {
X for (r = minr; r<=maxr; r++)
X for (c = minc; c<=maxc; c++) {
X n = lookat (r, c);
X (void) clearent(n);
X n->v = start;
X start += inc;
X n->flags |= (is_changed|is_valid);
X }
X }
X else if ( calc_order == BYCOLS ) {
X for (c = minc; c<=maxc; c++)
X for (r = minr; r<=maxr; r++) {
X n = lookat (r, c);
X (void) clearent(n);
X n->v = start;
X start += inc;
X n->flags |= (is_changed|is_valid);
X }
X }
X else error(" Internal error calc_order");
X changed++;
X}
X
Xvoid
Xlet (v, e)
Xstruct ent *v;
Xstruct enode *e;
X{
X double val;
X
X exprerr = 0;
X (void) signal(SIGFPE, eval_fpe);
X if (setjmp(fpe_save)) {
X error ("Floating point exception in cell %s", v_name(v->row, v->col));
X val = (double)0.0;
X } else {
X val = eval(e);
X }
X (void) signal(SIGFPE, quit);
X if (exprerr) {
X efree((struct ent *)0, e);
X return;
X }
X if (constant(e)) {
X if (!loading)
X v->v = val * prescale;
X else
X v->v = val;
X if (!(v->flags & is_strexpr)) {
X efree(v, v->expr);
X v->expr = (struct enode *)0;
X }
X efree((struct ent *)0, e);
X v->flags |= (is_changed|is_valid);
X changed++;
X modflg++;
X return;
X }
X efree (v, v->expr);
X v->expr = e;
X v->flags |= (is_changed|is_valid);
X v->flags &= ~is_strexpr;
X
X#ifdef EXPRTREE
X totoptree(v);
X#endif
X changed++;
X modflg++;
X}
X
Xvoid
Xslet (v, se, flushdir)
Xstruct ent *v;
Xstruct enode *se;
Xint flushdir;
X{
X char *p;
X
X exprerr = 0;
X (void) signal(SIGFPE, eval_fpe);
X if (setjmp(fpe_save)) {
X error ("Floating point exception in cell %s", v_name(v->row, v->col));
X p = "";
X } else {
X p = seval(se);
X }
X (void) signal(SIGFPE, quit);
X if (exprerr) {
X efree((struct ent *)0, se);
X return;
X }
X if (constant(se)) {
X label(v, p, flushdir);
X if (p)
X xfree(p);
X efree((struct ent *)0, se);
X if (v->flags & is_strexpr) {
X efree (v, v->expr);
X v->expr = (struct enode *)0;
X v->flags &= ~is_strexpr;
X }
X return;
X }
X efree (v, v->expr);
X v->expr = se;
X v->flags |= (is_changed|is_strexpr);
X if (flushdir<0) v->flags |= is_leftflush;
X else v->flags &= ~is_leftflush;
X
X#ifdef EXPRTREE
X totoptree();
X#endif
X FullUpdate++;
X changed++;
X modflg++;
X}
X
X#ifdef EXPRTREE
X/*
X * put an expression in the expression tree, only the top of each branch is
X * in the firstev list
X */
Xtotoptree(v)
Xstruct ent *v;
X{
X int right;
X int left;
X if (!v->expr)
X return;
X
X#ifdef notdef
X right = FALSE;
X left = FALSE;
X switch(v->expr->op)
X {
X /* no real expression */
X case 'v':
X if (v->expr->o.v->evnext)
X evdel(v->expr->o.v);
X case 'k':
X case LMAX:
X case LMIN:
X case NOW:
X case O_SCONST:
X case O_VAR:
X default:
X return;
X
X /* left && right */
X case '#':
X case '%':
X case '&':
X case '*':
X case '+':
X case '-':
X case '/':
X case '<':
X case '=':
X case '>':
X case '?':
X case '^':
X case '|':
X case ATAN2:
X case DTS:
X case EQS:
X case EXT:
X case FMT:
X case FV:
X case HYPOT:
X case IF:
X case NVAL:
X case PMT:
X case POW:
X case PV:
X case REDUCE | '*':
X case REDUCE | '+':
X case REDUCE | 'a':
X case REDUCE | 'c':
X case REDUCE | 's':
X case REDUCE | MAX:
X case REDUCE | MIN:
X case ROUND:
X case STINDEX:
X case SUBSTR:
X case SVAL:
X case TTS:
X left = right = TRUE;
X break;
X /* right only */
X case 'f':
X case 'm':
X case '~':
X case ABS:
X case ACOS:
X case ASIN:
X case ATAN:
X case CEIL:
X case COS:
X case DATE:
X case DAY:
X case DTR:
X case EXP:
X case FABS:
X case FLOOR:
X case HLOOKUP:
X case HOUR:
X case IF:
X case INDEX:
X case LOG10:
X case LOG:
X case LOOKUP:
X case MINUTE:
X case MONTH:
X case RND:
X case RTD:
X case SECOND:
X case SIN:
X case SQRT:
X case STON:
X case TAN:
X case VLOOKUP:
X case YEAR:
X right = TRUE;
X break;
X }
X /* for now insert at the beginning of the list */
X v->evnext = firstev;
X v->evprev = (struct ent *)0;
X if (firstev)
X firstev->evprev = v;
X firstev = v;
X#endif
X firstev = v;
X}
X#endif /* EXPRTREE*/
X
Xvoid
Xhide_row(arg)
Xint arg;
X{
X if (arg < 0) {
X error("Invalid Range");
X return;
X }
X if (arg >= maxrows-1)
X {
X if (!growtbl(GROWROW, arg+1, 0))
X { error("You can't hide the last row");
X return;
X }
X }
X FullUpdate++;
X row_hidden[arg] = 1;
X}
X
Xvoid
Xhide_col(arg)
Xint arg;
X{
X if (arg < 0) {
X error("Invalid Range");
X return;
X }
X if (arg >= maxcols-1)
X { if ((arg >= ABSMAXCOLS-1) || !growtbl(GROWCOL, 0, arg+1))
X { error("You can't hide the last col");
X return;
X }
X }
X FullUpdate++;
X col_hidden[arg] = 1;
X}
X
Xvoid
Xclearent (v)
Xstruct ent *v;
X{
X if (!v)
X return;
X label(v,"",-1);
X v->v = (double)0;
X if (v->expr)
X efree(v, v->expr);
X v->expr = (struct enode *)0;
X v->flags |= (is_changed);
X v->flags &= ~(is_valid);
X changed++;
X modflg++;
X}
X
X/*
X * Say if an expression is a constant (return 1) or not.
X */
Xint
Xconstant (e)
X register struct enode *e;
X{
X return ((e == (struct enode *)0)
X || ((e -> op) == O_CONST)
X || ((e -> op) == O_SCONST)
X || (((e -> op) != O_VAR)
X && (((e -> op) & REDUCE) != REDUCE)
X && constant (e -> e.o.left)
X && constant (e -> e.o.right)
X && (e -> op != EXT) /* functions look like constants but aren't */
X && (e -> op != NVAL)
X && (e -> op != SVAL)
X && (e -> op != NOW)));
X}
X
Xvoid
Xefree (v, e)
Xstruct ent *v;
Xstruct enode *e;
X{
X if (e) {
X if (e->op != O_VAR && e->op !=O_CONST && e->op != O_SCONST
X && (e->op & REDUCE) != REDUCE) {
X efree(v, e->e.o.left);
X efree(v, e->e.o.right);
X }
X if (e->op == O_SCONST && e->e.s)
X xfree(e->e.s);
X xfree ((char *)e);
X
X#ifdef EXPRTREE
X /* delete this cell from the eval list */
X if (v)
X { if (v->evprev)
X v->evprev->evnext = v->evnext;
X if (v->evnext)
X v->evnext->evprev = v->evprev;
X }
X#endif /* EXPRTREE */
X }
X}
X
Xvoid
Xlabel (v, s, flushdir)
Xregister struct ent *v;
Xregister char *s;
Xint flushdir;
X{
X if (v) {
X if (flushdir==0 && v->flags&is_valid) {
X register struct ent *tv;
X if (v->col>0 && ((tv=lookat(v->row,v->col-1))->flags&is_valid)==0)
X v = tv, flushdir = 1;
X else if (((tv=lookat (v->row,v->col+1))->flags&is_valid)==0)
X v = tv, flushdir = -1;
X else flushdir = -1;
X }
X if (v->label) xfree((char *)(v->label));
X if (s && s[0]) {
X v->label = xmalloc ((unsigned)(strlen(s)+1));
X (void) strcpy (v->label, s);
X } else
X v->label = (char *)0;
X if (flushdir<0) v->flags |= is_leftflush;
X else v->flags &= ~is_leftflush;
X FullUpdate++;
X modflg++;
X }
X}
X
Xvoid
Xdecodev (v)
Xstruct ent_ptr v;
X{
X register struct range *r;
X
X if (!v.vp) (void)sprintf (line+linelim,"VAR?");
X else if ((r = find_range((char *)0, 0, v.vp, v.vp)) && !r->r_is_range)
X (void)sprintf(line+linelim, "%s", r->r_name);
X else
X (void)sprintf (line+linelim, "%s%s%s%d",
X v.vf & FIX_COL ? "$" : "",
X coltoa(v.vp->col),
X v.vf & FIX_ROW ? "$" : "",
X v.vp->row);
X linelim += strlen (line+linelim);
X}
X
Xchar *
Xcoltoa(col)
Xint col;
X{
X static char rname[3];
X register char *p = rname;
X
X if (col > 25) {
X *p++ = col/26 + 'A' - 1;
X col %= 26;
X }
X *p++ = col+'A';
X *p = '\0';
X return(rname);
X}
X
X/*
X * To make list elements come out in the same order
X * they were entered, we must do a depth-first eval
X * of the ELIST tree
X */
Xstatic void
Xdecompile_list(p)
Xstruct enode *p;
X{
X if (!p) return;
X decompile_list(p->e.o.left); /* depth first */
X decompile(p->e.o.right, 0);
X line[linelim++] = ',';
X}
X
Xvoid
Xdecompile(e, priority)
Xregister struct enode *e;
Xint priority;
X{
X register char *s;
X if (e) {
X int mypriority;
X switch (e->op) {
X default: mypriority = 99; break;
X case '?': mypriority = 1; break;
X case ':': mypriority = 2; break;
X case '|': mypriority = 3; break;
X case '&': mypriority = 4; break;
X case '<': case '=': case '>': mypriority = 6; break;
X case '+': case '-': case '#': mypriority = 8; break;
X case '*': case '/': case '%': mypriority = 10; break;
X case '^': mypriority = 12; break;
X }
X if (mypriority<priority) line[linelim++] = '(';
X switch (e->op) {
X case 'f': for (s="fixed "; line[linelim++] = *s++;);
X linelim--;
X decompile (e->e.o.right, 30);
X break;
X case 'm': line[linelim++] = '-';
X decompile (e->e.o.right, 30);
X break;
X case '~': line[linelim++] = '~';
X decompile (e->e.o.right, 30);
X break;
X case 'v': decodev (e->e.v);
X break;
X case 'k': (void)sprintf (line+linelim,"%.15g",e->e.k);
X linelim += strlen (line+linelim);
X break;
X case '$': (void)sprintf (line+linelim, "\"%s\"", e->e.s);
X linelim += strlen(line+linelim);
X break;
X
X case REDUCE | '+': range_arg( "@sum(", e); break;
X case REDUCE | '*': range_arg( "@prod(", e); break;
X case REDUCE | 'a': range_arg( "@avg(", e); break;
X case REDUCE | 'c': range_arg( "@count(", e); break;
X case REDUCE | 's': range_arg( "@stddev(", e); break;
X case REDUCE | MAX: range_arg( "@max(", e); break;
X case REDUCE | MIN: range_arg( "@min(", e); break;
X
X case ABS: one_arg( "@abs(", e); break;
X case ACOS: one_arg( "@acos(", e); break;
X case ASIN: one_arg( "@asin(", e); break;
X case ATAN: one_arg( "@atan(", e); break;
X case ATAN2: two_arg( "@atan2(", e); break;
X case CEIL: one_arg( "@ceil(", e); break;
X case COS: one_arg( "@cos(", e); break;
X case EXP: one_arg( "@exp(", e); break;
X case FABS: one_arg( "@fabs(", e); break;
X case FLOOR: one_arg( "@floor(", e); break;
X case HYPOT: two_arg( "@hypot(", e); break;
X case LOG: one_arg( "@ln(", e); break;
X case LOG10: one_arg( "@log(", e); break;
X case POW: two_arg( "@pow(", e); break;
X case SIN: one_arg( "@sin(", e); break;
X case SQRT: one_arg( "@sqrt(", e); break;
X case TAN: one_arg( "@tan(", e); break;
X case DTR: one_arg( "@dtr(", e); break;
X case RTD: one_arg( "@rtd(", e); break;
X case RND: one_arg( "@rnd(", e); break;
X case ROUND: two_arg( "@round(", e); break;
X case HOUR: one_arg( "@hour(", e); break;
X case MINUTE: one_arg( "@minute(", e); break;
X case SECOND: one_arg( "@second(", e); break;
X case MONTH: one_arg( "@month(", e); break;
X case DAY: one_arg( "@day(", e); break;
X case YEAR: one_arg( "@year(", e); break;
X case DATE: one_arg( "@date(", e); break;
X case DTS: three_arg( "@dts(", e); break;
X case TTS: three_arg( "@tts(", e); break;
X case STON: one_arg( "@ston(", e); break;
X case FMT: two_arg( "@fmt(", e); break;
X case EQS: two_arg( "@eqs(", e); break;
X case NOW: for ( s = "@now"; line[linelim++] = *s++;);
X linelim--;
X break;
X case LMAX: list_arg("@max(", e); break;
X case LMIN: list_arg("@min(", e); break;
X case FV: three_arg("@fv(", e); break;
X case PV: three_arg("@pv(", e); break;
X case PMT: three_arg("@pmt(", e); break;
X case NVAL: two_arg("@nval(", e); break;
X case SVAL: two_arg("@sval(", e); break;
X case EXT: two_arg("@ext(", e); break;
X case SUBSTR: three_arg("@substr(", e); break;
X case STINDEX: index_arg("@stindex(", e); break;
X case INDEX: index_arg("@index(", e); break;
X case LOOKUP: index_arg("@lookup(", e); break;
X case HLOOKUP: two_arg_index("@hlookup(", e); break;
X case VLOOKUP: two_arg_index("@vlookup(", e); break;
X case IF: three_arg("@if(", e); break;
X default: decompile (e->e.o.left, mypriority);
X line[linelim++] = e->op;
X decompile (e->e.o.right, mypriority+1);
X break;
X }
X if (mypriority<priority) line[linelim++] = ')';
X } else line[linelim++] = '?';
X}
X
Xvoid
Xindex_arg(s, e)
Xchar *s;
Xstruct enode *e;
X{
X for (; line[linelim++] = *s++;);
X linelim--;
X decompile( e-> e.o.left, 0 );
X range_arg(", ", e->e.o.right);
X}
X
Xvoid
Xtwo_arg_index(s, e)
Xchar *s;
Xstruct enode *e;
X{
X for (; line[linelim++] = *s++;);
X linelim--;
X decompile( e->e.o.left->e.o.left, 0 );
X range_arg(",", e->e.o.right);
X linelim--;
X line[linelim++] = ',';
X decompile( e->e.o.left->e.o.right, 0 );
X line[linelim++] = ')';
X}
X
Xvoid
Xlist_arg(s, e)
Xchar *s;
Xstruct enode *e;
X{
X for (; line[linelim++] = *s++;);
X linelim--;
X
X decompile (e->e.o.right, 0);
X line[linelim++] = ',';
X decompile_list(e->e.o.left);
X line[linelim - 1] = ')';
X}
X
Xvoid
Xone_arg(s, e)
Xchar *s;
Xstruct enode *e;
X{
X for (; line[linelim++] = *s++;);
X linelim--;
X decompile (e->e.o.right, 0);
X line[linelim++] = ')';
X}
X
Xvoid
Xtwo_arg(s,e)
Xchar *s;
Xstruct enode *e;
X{
X for (; line[linelim++] = *s++;);
X linelim--;
X decompile (e->e.o.left, 0);
X line[linelim++] = ',';
X decompile (e->e.o.right, 0);
X line[linelim++] = ')';
X}
X
Xvoid
Xthree_arg(s,e)
Xchar *s;
Xstruct enode *e;
X{
X for (; line[linelim++] = *s++;);
X linelim--;
X decompile (e->e.o.left, 0);
X line[linelim++] = ',';
X decompile (e->e.o.right->e.o.left, 0);
X line[linelim++] = ',';
X decompile (e->e.o.right->e.o.right, 0);
X line[linelim++] = ')';
X}
X
Xvoid
Xrange_arg(s,e)
Xchar *s;
Xstruct enode *e;
X{
X struct range *r;
X
X for (; line[linelim++] = *s++;);
X linelim--;
X if ((r = find_range((char *)0, 0, e->e.r.left.vp,
X e->e.r.right.vp)) && r->r_is_range) {
X (void)sprintf(line+linelim, "%s", r->r_name);
X linelim += strlen(line+linelim);
X } else {
X decodev (e->e.r.left);
X line[linelim++] = ':';
X decodev (e->e.r.right);
X }
X line[linelim++] = ')';
X}
X
Xvoid
Xeditv (row, col)
Xint row, col;
X{
X register struct ent *p;
X
X p = lookat (row, col);
X (void)sprintf (line, "let %s = ", v_name(row, col));
X linelim = strlen(line);
X if (p->flags & is_strexpr || p->expr == 0) {
X (void)sprintf (line+linelim, "%.15g", p->v);
X linelim += strlen (line+linelim);
X } else {
X editexp(row,col);
X }
X}
X
Xvoid
Xeditexp(row,col)
Xint row, col;
X{
X register struct ent *p;
X
X p = lookat (row, col);
X decompile (p->expr, 0);
X line[linelim] = '\0';
X}
X
Xvoid
Xedits (row, col)
Xint row, col;
X{
X register struct ent *p;
X
X p = lookat (row, col);
X (void)sprintf (line, "%sstring %s = ",
X ((p->flags&is_leftflush) ? "left" : "right"),
X v_name(row, col));
X linelim = strlen(line);
X if (p->flags & is_strexpr && p->expr) {
X editexp(row, col);
X } else if (p->label) {
X (void)sprintf (line+linelim, "\"%s\"", p->label);
X linelim += strlen (line+linelim);
X } else {
X (void)sprintf (line+linelim, "\"");
X linelim += 1;
X }
X}
END_OF_FILE
if test 48096 -ne `wc -c <'interp.c'`; then
echo shar: \"'interp.c'\" unpacked with wrong size!
fi
# end of 'interp.c'
fi
if test -f 'sc6.8p1.hdr' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'sc6.8p1.hdr'\"
else
echo shar: Extracting \"'sc6.8p1.hdr'\" \(2645 characters\)
sed "s/^X//" >'sc6.8p1.hdr' <<'END_OF_FILE'
XFrom @uunet.uu.net:sawmill!buhrt at newton.physics.purdue.edu Thu Jun 21 22:27:39 1990
XReceived: from BBN.COM by pineapple.bbn.com id <AA17042 at pineapple.bbn.com>; Thu, 21 Jun 90 22:26:50 -0400
XReceived: from uunet.UU.NET by BBN.COM id ab13765; 21 Jun 90 22:25 EDT
XReceived: from newton.physics.purdue.edu by uunet.uu.net (5.61/1.14) with UUCP
X id AA09100; Thu, 21 Jun 90 22:24:58 -0400
XReceived: from pur-ee.UUCP by rutgers.edu (5.59/SMI4.0/RU1.3/3.06) with UUCP
X id AA09408; Thu, 21 Jun 90 20:35:48 EDT
XReceived: from newton.physics.purdue.edu by ee.ecn.purdue.edu (5.61/1.22jrs)
X id AA12057; Thu, 21 Jun 90 16:05:59 -0500
XReceived: from sawmill.UUCP by newton.physics.purdue.edu (5.61/1.34)
X id AA01613; Thu, 21 Jun 90 16:00:19 -0500
XReceived: by sawmill.UUCP (5.61/1.35)
X id AA08015; Thu, 21 Jun 90 15:36:01 -0500
XDate: Thu, 21 Jun 90 15:36:01 -0500
XFrom: Jeffery A Buhrt <sawmill!buhrt at newton.physics.purdue.edu>
XMessage-Id: <9006212036.AA08015 at sawmill.UUCP>
XTo: uunet!sources at uunet.uu.net
XSubject: Sc6.8 (part 1 of 4)
XStatus: R
X
X
XRobert Bond (sequent!rgb) has turned Sc's maintenance over to me.
X
Xsh (unshar) all four parts.
XEdit Makefile
Xmake sc psc
X
XIf you have /etc/magic (for 'file' (or 'att file')) add:
X38 string Spreadsheet sc file
XThank you: edgard at cao.gipsi.fr
X
XTested on a Sequent Symmetry (gcc, cc, atscc), 3b2/400 (cc, fpcc),
X 3b1 (3.51m-cc,gcc), '386 (AT&T3.2.2-cc,gcc), '286 (Microport 2.4-cc),
X
XPlease send any diffs/changes/comments you might make/have
X(make sure to include enough context diffs to help in patching, and please
Xnote the version number the patch is w/r to).
X
XFor all testers: 6.8 is the same as 6.7.1.3
X
X -Jeff Buhrt
X Grauel Enterprises, Inc.
X 317-477-6000
X {newton.physics.purdue.edu (aka: pur-phy), sequent}!sawmill!buhrt
X
XCHANGES BETWEEN 6.1 and 6.8
X
XDave Lewis -
X Found and fixed a null pointer dereference in the 'R' command.
X
XRob McMahon -
X Changed the ctl() macro to work with ANSI style compilers.
X Cleaned up some non-readonly text problems.
X
XRick Linck -
X Fixed a bug in lex.c - Ann Arbor Ambassadors have long ks and ke
X termcap entries.
X
XSam Drake -
X A fix for undefined C_* symbols in AIX.
X
XPeter Brower -
X Cleaned up the INTERNATIONAL ifdefs with more portable code.
X
XGlen Ditchfield
X Cleaned up a problem in crypt.c when the encrypted file shrank.
X
XBob Bond -
X Vi style editing for the command line.
X A bug in range name aliases.
X
XJeff Buhrt -
X -Added "~" filename expansion.
X -702 columns (A-ZZ) and unlimited rows/cells based on max. memory
X -fixed a few bugs
X -slightly decreased CPU usage
X -MAKES backup copies of files (by default: path/#name~)
X -understands ~$HOME stuff
END_OF_FILE
if test 2645 -ne `wc -c <'sc6.8p1.hdr'`; then
echo shar: \"'sc6.8p1.hdr'\" unpacked with wrong size!
fi
# end of 'sc6.8p1.hdr'
fi
if test -f 'sres.sed' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'sres.sed'\"
else
echo shar: Extracting \"'sres.sed'\" \(50 characters\)
sed "s/^X//" >'sres.sed' <<'END_OF_FILE'
X/%token.*S_/!d
X/%token.*S_\(.*\)/s// "\1", S_\1,/
END_OF_FILE
if test 50 -ne `wc -c <'sres.sed'`; then
echo shar: \"'sres.sed'\" unpacked with wrong size!
fi
# end of 'sres.sed'
fi
echo shar: End of archive 2 \(of 6\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 6 archives.
rm -f ark[1-9]isdone
else
echo You still must unpack the following archives:
echo " " ${MISSING}
fi
exit 0
exit 0 # Just in case...
--
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list