v18i047: SC spreadsheet, version 6.1, Part03/04
Rich Salz
rsalz at uunet.uu.net
Wed Mar 22 07:17:41 AEST 1989
Submitted-by: Robert Bond <sequent!rgb>
Posting-number: Volume 18, Issue 47
Archive-name: sc6.1/part03
# 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:
# ./gram.y
# ./interp.c
# ./crypt.c
#
if `test ! -s ./gram.y`
then
echo "Extracting ./gram.y"
cat > ./gram.y << '\SHAR\EOF\'
/* SC A Spreadsheet Calculator
* Command and expression parser
*
* original by James Gosling, September 1982
* modified by Mark Weiser and Bruce Israel,
* University of Maryland
*
* more mods Robert Bond 12/86
*
* More mods by Alan Silverstein, 3/88, see list of changes.
*
* $Revision: 6.1 $
*/
%{
#include <curses.h>
#include "sc.h"
#define ENULL (struct enode *)0
char *strcpy();
%}
%union {
int ival;
double fval;
struct ent_ptr ent;
struct enode *enode;
char *sval;
struct range_s rval;
}
%type <ent> var
%type <fval> num
%type <rval> range
%type <rval> var_or_range
%type <sval> strarg
%type <enode> e term expr_list
%token <sval> STRING
%token <ival> NUMBER
%token <fval> FNUMBER
%token <rval> RANGE
%token <rval> VAR
%token <sval> WORD
%token <ival> COL
%token S_FORMAT
%token S_LABEL
%token S_LEFTSTRING
%token S_RIGHTSTRING
%token S_GET
%token S_PUT
%token S_MERGE
%token S_LET
%token S_WRITE
%token S_TBL
%token S_COPY
%token S_SHOW
%token S_ERASE
%token S_FILL
%token S_GOTO
%token S_DEFINE
%token S_UNDEFINE
%token S_VALUE
%token S_MDIR
%token S_HIDE
%token S_SET
%token K_FIXED
%token K_SUM
%token K_PROD
%token K_AVG
%token K_STDDEV
%token K_ACOS
%token K_ASIN
%token K_ATAN
%token K_ATAN2
%token K_CEIL
%token K_COS
%token K_EXP
%token K_FABS
%token K_FLOOR
%token K_HYPOT
%token K_LN
%token K_LOG
%token K_PI
%token K_POW
%token K_SIN
%token K_SQRT
%token K_TAN
%token K_DTR
%token K_RTD
%token K_MAX
%token K_MIN
%token K_RND
%token K_PV
%token K_FV
%token K_PMT
%token K_HOUR
%token K_MINUTE
%token K_SECOND
%token K_MONTH
%token K_DAY
%token K_YEAR
%token K_NOW
%token K_DATE
%token K_FMT
%token K_SUBSTR
%token K_STON
%token K_EQS
%token K_EXT
%token K_NVAL
%token K_SVAL
%token K_LOOKUP
%token K_INDEX
%token K_STINDEX
%token K_AUTO
%token K_AUTOCALC
%token K_BYROWS
%token K_BYCOLS
%token K_BYGRAPH
%token K_ITERATIONS
%token K_NUMERIC
%token K_PRESCALE
%token K_EXTFUN
%token K_CELLCUR
%token K_TOPROW
%token K_TBLSTYLE
%token K_TBL
%token K_LATEX
%token K_TEX
%left '?' ':'
%left '|'
%left '&'
%nonassoc '<' '=' '>' '!'
%left '+' '-' '#'
%left '*' '/' '%'
%left '^'
%%
command: S_LET var_or_range '=' e
{ let($2.left.vp, $4); }
| S_LABEL var_or_range '=' e
{ slet($2.left.vp, $4, 0); }
| S_LEFTSTRING var_or_range '=' e
{ slet($2.left.vp, $4, -1); }
| S_RIGHTSTRING var_or_range '=' e
{ slet($2.left.vp, $4, 1); }
| S_FORMAT COL ':' COL NUMBER NUMBER
{ doformat($2,$4,$5,$6); }
| S_FORMAT COL NUMBER NUMBER
{ doformat($2,$2,$3,$4); }
| S_GET strarg { /* This tmp hack is because readfile
* recurses back through yyparse. */
char *tmp;
tmp = $2;
readfile (tmp, 1);
xfree(tmp);
}
| S_MERGE strarg {
char *tmp;
tmp = $2;
readfile (tmp, 0);
xfree(tmp);
}
| S_MDIR strarg
{ if (mdir) xfree(mdir); mdir = $2; }
| S_PUT strarg range
{ (void) writefile($2, ($3.left.vp)->row,
($3.left.vp)->col, ($3.right.vp)->row,
($3.right.vp)->col);
xfree($2); }
| S_PUT strarg
{ (void) writefile ($2, 0, 0, maxrow, maxcol);
xfree($2); }
| S_WRITE strarg range { (void) printfile($2, ($3.left.vp)->row,
($3.left.vp)->col, ($3.right.vp)->row,
($3.right.vp)->col);
xfree($2); }
| S_WRITE strarg { (void) printfile ($2, 0, 0, maxrow, maxcol);
xfree($2); }
| S_TBL strarg range { (void) tblprintfile($2, ($3.left.vp)->row,
($3.left.vp)->col, ($3.right.vp)->row,
($3.right.vp)->col);
xfree($2); }
| S_TBL strarg { (void)tblprintfile ($2, 0, 0, maxrow, maxcol);
xfree($2); }
| S_SHOW COL ':' COL
{ showcol( $2, $4); }
| S_SHOW NUMBER ':' NUMBER
{ showrow( $2, $4); }
| S_HIDE COL
{ hide_col( $2 ); }
| S_HIDE NUMBER
{ hide_row( $2 ); }
| S_COPY range var_or_range
{ copy($2.left.vp,$2.right.vp,
$3.left.vp,$3.right.vp); }
| S_ERASE
{ eraser(lookat(showsr, showsc),
lookat(currow, curcol)); }
| S_ERASE var_or_range
{ eraser($2.left.vp, $2.right.vp); }
| S_VALUE { valueize_area(showsr, showsc, currow, curcol);
modflg++; }
| S_VALUE var_or_range { valueize_area(($2.left.vp)->row,
($2.left.vp)->col,
($2.right.vp)->row,
($2.right.vp)->col); modflg++; }
| S_FILL num num { fill(lookat(showsr, showsc),
lookat(currow, curcol), $2, $3); }
| S_FILL var_or_range num num
{ fill($2.left.vp, $2.right.vp, $3, $4); }
| S_GOTO var_or_range {moveto($2.left.vp->row, $2.left.vp->col);}
| S_GOTO num {num_search($2);}
| S_GOTO STRING {str_search($2);}
| S_GOTO {go_last();}
| S_DEFINE strarg { struct ent_ptr arg1, arg2;
arg1.vp = lookat(showsr, showsc);
arg1.vf = 0;
arg2.vp = lookat(currow, curcol);
arg2.vf = 0;
add_range($2, arg1, arg2, 1); }
| S_DEFINE strarg range { add_range($2, $3.left, $3.right, 1); }
| S_DEFINE strarg var { add_range($2, $3, $3, 0); }
| S_UNDEFINE var_or_range { del_range($2.left.vp, $2.right.vp); }
| S_SET setlist
| /* nothing */
| error;
term: var { $$ = new_var('v', $1); }
| K_FIXED term { $$ = new ('f', ENULL, $2); }
| '@' K_SUM '(' var_or_range ')'
{ $$ = new_range(REDUCE | '+', $4); }
| '@' K_PROD '(' var_or_range ')'
{ $$ = new_range (REDUCE | '*', $4); }
| '@' K_AVG '(' var_or_range ')'
{ $$ = new_range (REDUCE | 'a', $4); }
| '@' K_STDDEV '(' var_or_range ')'
{ $$ = new_range (REDUCE | 's', $4); }
| '@' K_MAX '(' var_or_range ')'
{ $$ = new_range (REDUCE | MAX, $4); }
| '@' K_MAX '(' e ',' expr_list ')'
{ $$ = new(LMAX, $6, $4); }
| '@' K_MIN '(' var_or_range ')'
{ $$ = new_range (REDUCE | MIN, $4); }
| '@' K_MIN '(' e ',' expr_list ')'
{ $$ = new(LMIN, $6, $4); }
| '@' K_ACOS '(' e ')'
{ $$ = new(ACOS, ENULL, $4); }
| '@' K_ASIN '(' e ')' { $$ = new(ASIN, ENULL, $4); }
| '@' K_ATAN '(' e ')' { $$ = new(ATAN, ENULL, $4); }
| '@' K_ATAN2 '(' e ',' e ')' { $$ = new(ATAN2, $4, $6); }
| '@' K_CEIL '(' e ')' { $$ = new(CEIL, ENULL, $4); }
| '@' K_COS '(' e ')' { $$ = new(COS, ENULL, $4); }
| '@' K_EXP '(' e ')' { $$ = new(EXP, ENULL, $4); }
| '@' K_FABS '(' e ')' { $$ = new(FABS, ENULL, $4); }
| '@' K_FLOOR '(' e ')' { $$ = new(FLOOR, ENULL, $4); }
| '@' K_HYPOT '(' e ',' e ')' { $$ = new(HYPOT, $4, $6); }
| '@' K_LN '(' e ')' { $$ = new(LOG, ENULL, $4); }
| '@' K_LOG '(' e ')' { $$ = new(LOG10, ENULL, $4); }
| '@' K_POW '(' e ',' e ')' { $$ = new(POW, $4, $6); }
| '@' K_SIN '(' e ')' { $$ = new(SIN, ENULL, $4); }
| '@' K_SQRT '(' e ')' { $$ = new(SQRT, ENULL, $4); }
| '@' K_TAN '(' e ')' { $$ = new(TAN, ENULL, $4); }
| '@' K_DTR '(' e ')' { $$ = new(DTR, ENULL, $4); }
| '@' K_RTD '(' e ')' { $$ = new(RTD, ENULL, $4); }
| '@' K_RND '(' e ')' { $$ = new(RND, ENULL, $4); }
| '@' K_PV '(' e ',' e ',' e ')' { $$ = new(PV, $4,new(':',$6,$8)); }
| '@' K_FV '(' e ',' e ',' e ')' { $$ = new(FV, $4,new(':',$6,$8)); }
| '@' K_PMT '(' e ',' e ',' e ')' { $$ = new(PMT, $4,new(':',$6,$8)); }
| '@' K_HOUR '(' e ')' { $$ = new(HOUR,ENULL, $4); }
| '@' K_MINUTE '(' e ')' { $$ = new(MINUTE,ENULL, $4); }
| '@' K_SECOND '(' e ')' { $$ = new(SECOND,ENULL, $4); }
| '@' K_MONTH '(' e ')' { $$ = new(MONTH,ENULL,$4); }
| '@' K_DAY '(' e ')' { $$ = new(DAY, ENULL, $4); }
| '@' K_YEAR '(' e ')' { $$ = new(YEAR, ENULL, $4); }
| '@' K_NOW { $$ = new(NOW, ENULL, ENULL);}
| '@' K_STON '(' e ')' { $$ = new(STON, ENULL, $4); }
| '@' K_EQS '(' e ',' e ')' { $$ = new (EQS, $4, $6); }
| '@' K_DATE '(' e ')' { $$ = new(DATE, ENULL, $4); }
| '@' K_FMT '(' e ',' e ')' { $$ = new(FMT, $4, $6); }
| '@' K_INDEX '(' e ',' var_or_range ')'
{ $$ = new(INDEX, $4, new_range(REDUCE | INDEX, $6)); }
| '@' K_LOOKUP '(' e ',' var_or_range ')'
{ $$ = new(LOOKUP, $4, new_range(REDUCE | LOOKUP, $6)); }
| '@' K_STINDEX '(' e ',' var_or_range ')'
{ $$ = new(STINDEX, $4, new_range(REDUCE | STINDEX, $6)); }
| '@' K_EXT '(' e ',' e ')' { $$ = new(EXT, $4, $6); }
| '@' K_NVAL '(' e ',' e ')' { $$ = new(NVAL, $4, $6); }
| '@' K_SVAL '(' e ',' e ')' { $$ = new(SVAL, $4, $6); }
| '@' K_SUBSTR '(' e ',' e ',' e ')'
{ $$ = new(SUBSTR, $4, new(',', $6, $8)); }
| '(' e ')' { $$ = $2; }
| '+' term { $$ = $2; }
| '-' term { $$ = new ('m', ENULL, $2); }
| NUMBER { $$ = new_const('k', (double) $1); }
| FNUMBER { $$ = new_const('k', $1); }
| K_PI { $$ = new_const('k', (double)3.14159265358979323846); }
| STRING { $$ = new_str($1); }
| '~' term { $$ = new ('~', ENULL, $2); }
| '!' term { $$ = new ('~', ENULL, $2); }
;
e: e '+' e { $$ = new ('+', $1, $3); }
| e '-' e { $$ = new ('-', $1, $3); }
| e '*' e { $$ = new ('*', $1, $3); }
| e '/' e { $$ = new ('/', $1, $3); }
| e '%' e { $$ = new ('%', $1, $3); }
| e '^' e { $$ = new ('^', $1, $3); }
| term
| e '?' e ':' e { $$ = new ('?', $1, new(':', $3, $5)); }
| e '<' e { $$ = new ('<', $1, $3); }
| e '=' e { $$ = new ('=', $1, $3); }
| e '>' e { $$ = new ('>', $1, $3); }
| e '&' e { $$ = new ('&', $1, $3); }
| e '|' e { $$ = new ('|', $1, $3); }
| e '<' '=' e { $$ = new ('~', ENULL, new ('>', $1, $4)); }
| e '!' '=' e { $$ = new ('~', ENULL, new ('=', $1, $4)); }
| e '>' '=' e { $$ = new ('~', ENULL, new ('<', $1, $4)); }
| e '#' e { $$ = new ('#', $1, $3); }
;
expr_list: e { $$ = new(ELIST, ENULL, $1); }
| expr_list ',' e { $$ = new(ELIST, $1, $3); }
;
range: var ':' var { $$.left = $1; $$.right = $3; }
| RANGE { $$ = $1; }
;
var: COL NUMBER { $$.vp = lookat($2 , $1); $$.vf = 0;}
| '$' COL NUMBER { $$.vp = lookat($3 , $2);
$$.vf = FIX_COL;}
| COL '$' NUMBER { $$.vp = lookat($3 , $1);
$$.vf = FIX_ROW;}
| '$' COL '$' NUMBER { $$.vp = lookat($4 , $2);
$$.vf = FIX_ROW | FIX_COL;}
| VAR { $$ = $1.left; }
;
var_or_range: range { $$ = $1; }
| var { $$.left = $1; $$.right = $1; }
;
num: NUMBER { $$ = (double) $1; }
| FNUMBER { $$ = $1; }
| '-' num { $$ = -$2; }
| '+' num { $$ = $2; }
;
strarg: STRING { $$ = $1; }
| var {
char *s, *s1;
s1 = $1.vp->label;
if (!s1)
s1 = "NULL_STRING";
s = xmalloc((unsigned)strlen(s1)+1);
(void) strcpy(s, s1);
$$ = s;
}
;
setlist :
| setlist setitem
;
setitem : K_AUTO { setauto(1); }
| K_AUTOCALC { setauto(1); }
| '~' K_AUTO { setauto(0); }
| '~' K_AUTOCALC { setauto(0); }
| '!' K_AUTO { setauto(0); }
| '!' K_AUTOCALC { setauto(0); }
| K_BYCOLS { setorder(BYCOLS); }
| K_BYROWS { setorder(BYROWS); }
| K_BYGRAPH { setorder(BYGRAPH); }
| K_NUMERIC { numeric = 1; }
| '!' K_NUMERIC { numeric = 0; }
| K_PRESCALE { prescale = 0.01; }
| '!' K_PRESCALE { prescale = 1.0; }
| K_EXTFUN { extfunc = 1; }
| '!' K_EXTFUN { extfunc = 0; }
| K_CELLCUR { showcell = 1; }
| '!' K_CELLCUR { showcell = 0; }
| K_TOPROW { showtop = 1; }
| '!' K_TOPROW { showtop = 0; }
| K_ITERATIONS '=' NUMBER { setiterations($3); }
| K_TBLSTYLE '=' NUMBER { tbl_style = $3; }
| K_TBLSTYLE '=' K_TBL { tbl_style = TBL; }
| K_TBLSTYLE '=' K_LATEX { tbl_style = LATEX; }
| K_TBLSTYLE '=' K_TEX { tbl_style = TEX; }
;
\SHAR\EOF\
else
echo "will not over write ./gram.y"
fi
if [ `wc -c ./gram.y | awk '{printf $1}'` -ne 11263 ]
then
echo `wc -c ./gram.y | awk '{print "Got " $1 ", Expected " 11263}'`
fi
if `test ! -s ./interp.c`
then
echo "Extracting ./interp.c"
cat > ./interp.c << '\SHAR\EOF\'
/* SC A Spreadsheet Calculator
* Expression interpreter and assorted support routines.
*
* original by James Gosling, September 1982
* modified by Mark Weiser and Bruce Israel,
* University of Maryland
*
* More mods Robert Bond, 12/86
* More mods by Alan Silverstein, 3-4/88, see list of changes.
* $Revision: 6.1 $
*/
#include <math.h>
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>
extern int errno; /* set by math functions */
#ifdef BSD42
#include <strings.h>
#include <sys/time.h>
#ifndef strchr
#define strchr rindex
#endif
#else
#include <time.h>
#ifndef SYSIII
#include <string.h>
#endif
#endif
#include <curses.h>
#include "sc.h"
#if defined(BSD42) || defined(BSD43)
char *re_comp();
#endif
#if defined(SYSV2) || defined(SYSV3)
char *regcmp();
char *regex();
#endif
#ifdef SIGVOID
void quit();
#else
int quit();
#endif
/* Use this structure to save the the last 'g' command */
struct go_save {
int g_type;
double g_n;
char *g_s;
int g_row;
int g_col;
} gs;
/* g_type can be: */
#define G_NONE 0 /* Starting value - must be 0*/
#define G_NUM 1
#define G_STR 2
#define G_CELL 3
extern FILE *popen();
jmp_buf fpe_save;
int exprerr; /* Set by eval() and seval() if expression errors */
double prescale = 1.0; /* Prescale for constants in let() */
int extfunc = 0; /* Enable/disable external functions */
int loading = 0; /* Set when readfile() is active */
double fn1_eval();
double fn2_eval();
#define PI (double)3.14159265358979323846
#define dtr(x) ((x)*(PI/(double)180.0))
#define rtd(x) ((x)*(180.0/(double)PI))
double finfunc(fun,v1,v2,v3)
int fun;
double v1,v2,v3;
{
double answer,p;
p = fn2_eval(pow, 1 + v2, v3);
switch(fun)
{
case PV:
answer = v1 * (1 - 1/p) / v2;
break;
case FV:
answer = v1 * (p - 1) / v2;
break;
case PMT:
answer = v1 * v2 / (1 - 1/p);
break;
}
return(answer);
}
char *
dostindex( val, minr, minc, maxr, maxc)
double val;
int minr, minc, maxr, maxc;
{
register r,c;
register struct ent *p;
char *pr;
int x;
x = (int) val;
r = minr; c = minc;
p = 0;
if ( minr == maxr ) { /* look along the row */
c = minc + x - 1;
if (c <= maxc && c >=minc)
p = tbl[r][c];
} else if ( minc == maxc ) { /* look down the column */
r = minr + x - 1;
if (r <= maxr && r >=minr)
p = tbl[r][c];
} else {
error ("range specified to @stindex");
return(0);
}
if (p && p->label) {
pr = xmalloc((unsigned)(strlen(p->label)+1));
(void)strcpy(pr, p->label);
return (pr);
} else
return(0);
}
double
doindex( val, minr, minc, maxr, maxc)
double val;
int minr, minc, maxr, maxc;
{
double v;
register r,c;
register struct ent *p;
int x;
x = (int) val;
v = 0;
r = minr; c = minc;
if ( minr == maxr ) { /* look along the row */
c = minc + x - 1;
if (c <= maxc && c >=minc
&& (p = tbl[r][c] ) && p->flags&is_valid )
return p->v;
}
else if ( minc == maxc ){ /* look down the column */
r = minr + x - 1;
if (r <= maxr && r >=minr
&& (p = tbl[r][c] ) && p->flags&is_valid )
return p->v;
}
else error(" range specified to @index");
return v;
}
double
dolookupn( val, minr, minc, maxr, maxc)
double val;
int minr, minc, maxr, maxc;
{
double v;
register r,c;
register struct ent *p;
v = 0;
r = minr; c = minc;
if ( minr == maxr ) { /* look along the row */
for ( c = minc; c <= maxc; c++) {
if ( (p = tbl[r][c] ) && p->flags&is_valid ) {
if(p->v <= val) {
p = tbl[r+1][c];
if ( p && p->flags&is_valid)
v = p->v;
}
else return v;
}
}
}
else if ( minc == maxc ){ /* look down the column */
for ( r = minr; r <= maxr; r++) {
if ( (p = tbl[r][c] ) && p->flags&is_valid ) {
if(p->v <= val) {
p = tbl[r][c+1];
if ( p && p->flags&is_valid)
v = p->v;
}
else return v;
}
}
}
else error(" range specified to @lookup");
return v;
}
double
dolookups(s, minr, minc, maxr, maxc)
char *s;
int minr, minc, maxr, maxc;
{
double v;
register r,c;
register struct ent *p;
v = 0;
r = minr; c = minc;
if ( minr == maxr ) { /* look along the row */
for ( c = minc; c <= maxc; c++) {
if ( (p = tbl[r][c] ) && p->label) {
if(strcmp(s,p->label) == 0) {
p = tbl[r+1][c];
xfree(s);
if ( p && p->flags & is_valid)
return(p->v);
}
}
}
} else if ( minc == maxc ) { /* look down the column */
for ( r = minr; r <= maxr; r++) {
if ( (p = tbl[r][c] ) && p->label) {
if(strcmp(s,p->label) == 0) {
p = tbl[r][c+1];
xfree(s);
if ( p && p->flags & is_valid)
return(p->v);
}
}
}
} else error(" range specified to @lookup");
xfree(s);
return v;
}
double
dosum(minr, minc, maxr, maxc)
int minr, minc, maxr, maxc;
{
double v;
register r,c;
register struct ent *p;
v = 0;
for (r = minr; r<=maxr; r++)
for (c = minc; c<=maxc; c++)
if ((p = tbl[r][c]) && p->flags&is_valid)
v += p->v;
return v;
}
double
doprod(minr, minc, maxr, maxc)
int minr, minc, maxr, maxc;
{
double v;
register r,c;
register struct ent *p;
v = 1;
for (r = minr; r<=maxr; r++)
for (c = minc; c<=maxc; c++)
if ((p = tbl[r][c]) && p->flags&is_valid)
v *= p->v;
return v;
}
double
doavg(minr, minc, maxr, maxc)
int minr, minc, maxr, maxc;
{
double v;
register r,c,count;
register struct ent *p;
v = 0;
count = 0;
for (r = minr; r<=maxr; r++)
for (c = minc; c<=maxc; c++)
if ((p = tbl[r][c]) && p->flags&is_valid) {
v += p->v;
count++;
}
if (count == 0)
return ((double) 0);
return (v / (double)count);
}
double
dostddev(minr, minc, maxr, maxc)
int minr, minc, maxr, maxc;
{
double lp, rp, v, nd;
register r,c,n;
register struct ent *p;
n = 0;
lp = 0;
rp = 0;
for (r = minr; r<=maxr; r++)
for (c = minc; c<=maxc; c++)
if ((p = tbl[r][c]) && p->flags&is_valid) {
v = p->v;
lp += v*v;
rp += v;
n++;
}
if ((n == 0) || (n == 1))
return ((double) 0);
nd = (double)n;
return (sqrt((nd*lp-rp*rp)/(nd*(nd-1))));
}
double
domax(minr, minc, maxr, maxc)
int minr, minc, maxr, maxc;
{
double v;
register r,c,count;
register struct ent *p;
count = 0;
for (r = minr; r<=maxr; r++)
for (c = minc; c<=maxc; c++)
if ((p = tbl[r][c]) && p->flags&is_valid) {
if (!count) {
v = p->v;
count++;
} else if (p->v > v)
v = p->v;
}
if (count == 0)
return ((double) 0);
return (v);
}
double
domin(minr, minc, maxr, maxc)
int minr, minc, maxr, maxc;
{
double v;
register r,c,count;
register struct ent *p;
count = 0;
for (r = minr; r<=maxr; r++)
for (c = minc; c<=maxc; c++)
if ((p = tbl[r][c]) && p->flags&is_valid) {
if (!count) {
v = p->v;
count++;
} else if (p->v < v)
v = p->v;
}
if (count == 0)
return ((double) 0);
return (v);
}
double
dotime(which, when)
int which;
double when;
{
long time();
static long t_cache;
static struct tm *tp;
long tloc;
if (which == NOW)
return (double)time((long *)0);
tloc = (long)when;
if (tloc != t_cache) {
tp = localtime(&tloc);
tp->tm_mon += 1;
tp->tm_year += 1900;
t_cache = tloc;
}
switch (which) {
case HOUR: return((double)(tp->tm_hour));
case MINUTE: return((double)(tp->tm_min));
case SECOND: return((double)(tp->tm_sec));
case MONTH: return((double)(tp->tm_mon));
case DAY: return((double)(tp->tm_mday));
case YEAR: return((double)(tp->tm_year));
}
/* Safety net */
return (0.0);
}
double
doston(s)
char *s;
{
char *strtof();
double v;
if (!s)
return((double)0.0);
(void)strtof(s, &v);
xfree(s);
return(v);
}
double
doeqs(s1, s2)
char *s1, *s2;
{
double v;
if (!s1 && !s2)
return(1.0);
if (!s1 || !s2)
v = 0.0;
else if (strcmp(s1, s2) == 0)
v = 1.0;
else
v = 0.0;
if (s1)
xfree(s1);
if (s2)
xfree(s2);
return(v);
}
/*
* Given a string representing a column name and a value which is a column
* number, return a pointer to the selected cell's entry, if any, else 0. Use
* only the integer part of the column number. Always free the string.
*/
struct ent *
getent (colstr, rowdoub)
char *colstr;
double rowdoub;
{
int collen; /* length of string */
int row, col; /* integer values */
struct ent *ep = 0; /* selected entry */
if (((row = (int) floor (rowdoub)) >= 0)
&& (row < MAXROWS) /* in range */
&& ((collen = strlen (colstr)) <= 2) /* not too long */
&& ((col = atocol (colstr, collen)) >= 0)
&& (col < MAXCOLS)) /* in range */
{
ep = tbl [row] [col];
}
xfree (colstr);
return (ep);
}
/*
* Given a string representing a column name and a value which is a column
* number, return the selected cell's numeric value, if any.
*/
double
donval (colstr, rowdoub)
char *colstr;
double rowdoub;
{
struct ent *ep;
return (((ep = getent (colstr, rowdoub)) && ((ep -> flags) & is_valid)) ?
(ep -> v) : 0);
}
/*
* The list routines (e.g. dolmax) are called with an LMAX enode.
* The left pointer is a chain of ELIST nodes, the right pointer
* is a value.
*/
double
dolmax(ep)
struct enode *ep;
{
register int count = 0;
register double maxval = 0; /* Assignment to shut up lint */
register struct enode *p;
register double v;
for (p = ep; p; p = p->e.o.left) {
v = eval(p->e.o.right);
if (!count || v > maxval) {
maxval = v; count++;
}
}
if (count) return maxval;
else return 0.0;
}
double
dolmin(ep)
struct enode *ep;
{
register int count = 0;
register double minval = 0; /* Assignment to shut up lint */
register struct enode *p;
register double v;
for (p = ep; p; p = p->e.o.left) {
v = eval(p->e.o.right);
if (!count || v < minval) {
minval = v; count++;
}
}
if (count) return minval;
else return 0.0;
}
double
eval(e)
register struct enode *e;
{
if (e==0) return 0;
switch (e->op) {
case '+': return (eval(e->e.o.left) + eval(e->e.o.right));
case '-': return (eval(e->e.o.left) - eval(e->e.o.right));
case '*': return (eval(e->e.o.left) * eval(e->e.o.right));
case '/': return (eval(e->e.o.left) / eval(e->e.o.right));
case '%': { double num, denom;
num = floor(eval(e->e.o.left));
denom = floor(eval (e->e.o.right));
return denom ? num - floor(num/denom)*denom : 0; }
case '^': return (fn2_eval(pow,eval(e->e.o.left),eval(e->e.o.right)));
case '<': return (eval(e->e.o.left) < eval(e->e.o.right));
case '=': return (eval(e->e.o.left) == eval(e->e.o.right));
case '>': return (eval(e->e.o.left) > eval(e->e.o.right));
case '&': return (eval(e->e.o.left) && eval(e->e.o.right));
case '|': return (eval(e->e.o.left) || eval(e->e.o.right));
case '?': return eval(e->e.o.left) ? eval(e->e.o.right->e.o.left)
: eval(e->e.o.right->e.o.right);
case 'm': return (-eval(e->e.o.right));
case 'f': return (eval(e->e.o.right));
case '~': return (eval(e->e.o.right) == 0.0);
case 'k': return (e->e.k);
case 'v': return (e->e.v.vp->v);
case INDEX:
case LOOKUP:
{ register r,c;
register maxr, maxc;
register minr, minc;
maxr = e->e.o.right->e.r.right.vp -> row;
maxc = e->e.o.right->e.r.right.vp -> col;
minr = e->e.o.right->e.r.left.vp -> row;
minc = e->e.o.right->e.r.left.vp -> col;
if (minr>maxr) r = maxr, maxr = minr, minr = r;
if (minc>maxc) c = maxc, maxc = minc, minc = c;
switch(e->op){
case LOOKUP:
if (etype(e->e.o.left) == NUM)
return dolookupn(eval(e->e.o.left), minr, minc, maxr, maxc);
else
return dolookups(seval(e->e.o.left),minr, minc, maxr, maxc);
case INDEX:
return doindex(eval(e->e.o.left), minr, minc, maxr, maxc);
}
}
case REDUCE | '+':
case REDUCE | '*':
case REDUCE | 'a':
case REDUCE | 's':
case REDUCE | MAX:
case REDUCE | MIN:
{ register r,c;
register maxr, maxc;
register minr, minc;
maxr = e->e.r.right.vp -> row;
maxc = e->e.r.right.vp -> col;
minr = e->e.r.left.vp -> row;
minc = e->e.r.left.vp -> col;
if (minr>maxr) r = maxr, maxr = minr, minr = r;
if (minc>maxc) c = maxc, maxc = minc, minc = c;
switch (e->op) {
case REDUCE | '+': return dosum(minr, minc, maxr, maxc);
case REDUCE | '*': return doprod(minr, minc, maxr, maxc);
case REDUCE | 'a': return doavg(minr, minc, maxr, maxc);
case REDUCE | 's': return dostddev(minr, minc, maxr, maxc);
case REDUCE | MAX: return domax(minr, minc, maxr, maxc);
case REDUCE | MIN: return domin(minr, minc, maxr, maxc);
}
}
case ACOS: return (fn1_eval( acos, eval(e->e.o.right)));
case ASIN: return (fn1_eval( asin, eval(e->e.o.right)));
case ATAN: return (fn1_eval( atan, eval(e->e.o.right)));
case ATAN2: return (fn2_eval( atan2, eval(e->e.o.left), eval(e->e.o.right)));
case CEIL: return (fn1_eval( ceil, eval(e->e.o.right)));
case COS: return (fn1_eval( cos, eval(e->e.o.right)));
case EXP: return (fn1_eval( exp, eval(e->e.o.right)));
case FABS: return (fn1_eval( fabs, eval(e->e.o.right)));
case FLOOR: return (fn1_eval( floor, eval(e->e.o.right)));
case HYPOT: return (fn2_eval( hypot, eval(e->e.o.left), eval(e->e.o.right)));
case LOG: return (fn1_eval( log, eval(e->e.o.right)));
case LOG10: return (fn1_eval( log10, eval(e->e.o.right)));
case POW: return (fn2_eval( pow, eval(e->e.o.left), eval(e->e.o.right)));
case SIN: return (fn1_eval( sin, eval(e->e.o.right)));
case SQRT: return (fn1_eval( sqrt, eval(e->e.o.right)));
case TAN: return (fn1_eval( tan, eval(e->e.o.right)));
case DTR: return (dtr(eval(e->e.o.right)));
case RTD: return (rtd(eval(e->e.o.right)));
case RND: {
double temp;
temp = eval(e->e.o.right);
return(temp-floor(temp) < 0.5 ?
floor(temp) : ceil(temp));
}
case FV:
case PV:
case PMT: return(finfunc(e->op,eval(e->e.o.left),
eval(e->e.o.right->e.o.left),
eval(e->e.o.right->e.o.right)));
case HOUR: return (dotime(HOUR, eval(e->e.o.right)));
case MINUTE: return (dotime(MINUTE, eval(e->e.o.right)));
case SECOND: return (dotime(SECOND, eval(e->e.o.right)));
case MONTH: return (dotime(MONTH, eval(e->e.o.right)));
case DAY: return (dotime(DAY, eval(e->e.o.right)));
case YEAR: return (dotime(YEAR, eval(e->e.o.right)));
case NOW: return (dotime(NOW, (double)0.0));
case STON: return (doston(seval(e->e.o.right)));
case EQS: return (doeqs(seval(e->e.o.right),seval(e->e.o.left)));
case LMAX: return dolmax(e);
case LMIN: return dolmin(e);
case NVAL: return (donval(seval(e->e.o.left),eval(e->e.o.right)));
default: error ("Illegal numeric expression");
exprerr = 1;
return((double)0.0);
}
#ifdef sequent
return((double)0.0); /* Quiet a questionable compiler complaint */
#endif
}
#ifdef SIGVOID
void
#endif
eval_fpe() /* Trap for FPE errors in eval */
{
longjmp(fpe_save, 1);
}
double fn1_eval(fn, arg)
double (*fn)();
double arg;
{
double res;
errno = 0;
res = (*fn)(arg);
if(errno)
eval_fpe();
return res;
}
double fn2_eval(fn, arg1, arg2)
double (*fn)();
double arg1, arg2;
{
double res;
errno = 0;
res = (*fn)(arg1, arg2);
if(errno)
eval_fpe();
return res;
}
/*
* Rules for string functions:
* Take string arguments which they xfree.
* All returned strings are assumed to be xalloced.
*/
char *
docat(s1, s2)
register char *s1, *s2;
{
register char *p;
char *arg1, *arg2;
if (!s1 && !s2)
return(0);
arg1 = s1 ? s1 : "";
arg2 = s2 ? s2 : "";
p = xmalloc((unsigned)(strlen(arg1)+strlen(arg2)+1));
(void) strcpy(p, arg1);
(void) strcat(p, arg2);
if (s1)
xfree(s1);
if (s2)
xfree(s2);
return(p);
}
char *
dodate(tloc)
long tloc;
{
char *tp;
char *p;
tp = ctime(&tloc);
tp[24] = 0;
p = xmalloc((unsigned)25);
(void) strcpy(p, tp);
return(p);
}
char *
dofmt(fmtstr, v)
char *fmtstr;
double v;
{
char buff[1024];
char *p;
if (!fmtstr)
return(0);
(void)sprintf(buff, fmtstr, v);
p = xmalloc((unsigned)(strlen(buff)+1));
(void) strcpy(p, buff);
xfree(fmtstr);
return(p);
}
/*
* Given a command name and a value, run the command with the given value and
* read and return its first output line (only) as an allocated string, always
* a copy of prevstr, which is set appropriately first unless external
* functions are disabled, in which case the previous value is used. The
* handling of prevstr and freeing of command is tricky. Returning an
* allocated string in all cases, even if null, insures cell expressions are
* written to files, etc.
*/
#ifdef VMS
char *
doext(command, value)
char *command;
double value;
{
error("Warning: External functions unavailable on VMS");
if (command)
xfree(command);
return (strcpy (xmalloc((unsigned) 1), "\0"));
}
#else /* VMS */
char *
doext (command, value)
char *command;
double value;
{
static char *prevstr = 0; /* previous result */
char buff[1024]; /* command line/return, not permanently alloc */
if (!prevstr) {
prevstr = xmalloc((unsigned)1);
*prevstr = 0;
}
if (!extfunc) {
error ("Warning: external functions disabled; using %s value",
prevstr ? "previous" : "null");
if (command) xfree (command);
} else {
if (prevstr) xfree (prevstr); /* no longer needed */
prevstr = 0;
if ((! command) || (! *command)) {
error ("Warning: external function given null command name");
if (command) xfree (command);
} else {
FILE *pp;
(void) sprintf (buff, "%s %g", command, value); /* build cmd line */
xfree (command);
error ("Running external function...");
(void) refresh();
if ((pp = popen (buff, "r")) == (FILE *) NULL) /* run it */
error ("Warning: running \"%s\" failed", buff);
else {
if (fgets (buff, 1024, pp) == NULL) /* one line */
error ("Warning: external function returned nothing");
else {
char *cp;
error (""); /* erase notice */
buff[1023] = 0;
if (cp = strchr (buff, '\n')) /* contains newline */
*cp = 0; /* end string there */
(void) strcpy (prevstr =
xmalloc ((unsigned) (strlen (buff) + 1)), buff);
/* save alloc'd copy */
}
(void) pclose (pp);
} /* else */
} /* else */
} /* else */
return (strcpy (xmalloc ((unsigned) (strlen (prevstr) + 1)), prevstr));
}
#endif /* VMS */
/*
* Given a string representing a column name and a value which is a column
* number, return the selected cell's string value, if any. Even if none,
* still allocate and return a null string so the cell has a label value so
* the expression is saved in a file, etc.
*/
char *
dosval (colstr, rowdoub)
char *colstr;
double rowdoub;
{
struct ent *ep;
char *label;
label = (ep = getent (colstr, rowdoub)) ? (ep -> label) : "";
return (strcpy (xmalloc ((unsigned) (strlen (label) + 1)), label));
}
/*
* Substring: Note that v1 and v2 are one-based to users, but zero-based
* when calling this routine.
*/
char *
dosubstr(s, v1, v2)
char *s;
register int v1,v2;
{
register char *s1, *s2;
char *p;
if (!s)
return(0);
if (v2 >= strlen (s)) /* past end */
v2 = strlen (s) - 1; /* to end */
if (v1 < 0 || v1 > v2) { /* out of range, return null string */
xfree(s);
p = xmalloc((unsigned)1);
p[0] = 0;
return(p);
}
s2 = p = xmalloc((unsigned)(v2-v1+2));
s1 = &s[v1];
for(; v1 <= v2; s1++, s2++, v1++)
*s2 = *s1;
*s2 = 0;
xfree(s);
return(p);
}
char *
seval(se)
register struct enode *se;
{
register char *p;
if (se==0) return 0;
switch (se->op) {
case O_SCONST: p = xmalloc((unsigned)(strlen(se->e.s)+1));
(void) strcpy(p, se->e.s);
return(p);
case O_VAR: {
struct ent *ep;
ep = se->e.v.vp;
if (!ep->label)
return(0);
p = xmalloc((unsigned)(strlen(ep->label)+1));
(void) strcpy(p, ep->label);
return(p);
}
case '#': return(docat(seval(se->e.o.left), seval(se->e.o.right)));
case 'f': return(seval(se->e.o.right));
case '?': return(eval(se->e.o.left) ? seval(se->e.o.right->e.o.left)
: seval(se->e.o.right->e.o.right));
case DATE: return(dodate((long)(eval(se->e.o.right))));
case FMT: return(dofmt(seval(se->e.o.left), eval(se->e.o.right)));
case STINDEX:
{ register r,c;
register maxr, maxc;
register minr, minc;
maxr = se->e.o.right->e.r.right.vp -> row;
maxc = se->e.o.right->e.r.right.vp -> col;
minr = se->e.o.right->e.r.left.vp -> row;
minc = se->e.o.right->e.r.left.vp -> col;
if (minr>maxr) r = maxr, maxr = minr, minr = r;
if (minc>maxc) c = maxc, maxc = minc, minc = c;
return dostindex(eval(se->e.o.left), minr, minc, maxr, maxc);
}
case EXT: return(doext(seval(se->e.o.left), eval(se->e.o.right)));
case SVAL: return(dosval(seval(se->e.o.left), eval(se->e.o.right)));
case SUBSTR: return(dosubstr(seval(se->e.o.left),
(int)eval(se->e.o.right->e.o.left) - 1,
(int)eval(se->e.o.right->e.o.right) - 1));
default:
error ("Illegal string expression");
exprerr = 1;
return(0);
}
}
/*
* The graph formed by cell expressions which use other cells's values is not
* evaluated "bottom up". The whole table is merely re-evaluated cell by cell,
* top to bottom, left to right, in RealEvalAll(). Each cell's expression uses
* constants in other cells. However, RealEvalAll() notices when a cell gets a
* new numeric or string value, and reports if this happens for any cell.
* EvalAll() repeats calling RealEvalAll() until there are no changes or the
* evaluation count expires.
*/
int propagation = 10; /* max number of times to try calculation */
setiterations(i)
int i;
{
if(i<1){
error("iteration count must be at least 1");
propagation = 1;
}
else propagation = i;
}
EvalAll () {
int lastcnt, repct = 0;
while ((lastcnt = RealEvalAll()) && (repct++ <= propagation));
if((propagation>1)&& (lastcnt >0 ))
error("Still changing after %d iterations",propagation-1);
}
/*
* Evaluate all cells which have expressions and alter their numeric or string
* values. Return the number of cells which changed.
*/
int
RealEvalAll () {
register int i,j;
int chgct = 0;
register struct ent *p;
(void) signal(SIGFPE, eval_fpe);
if(calc_order == BYROWS ) {
for (i=0; i<=maxrow; i++)
for (j=0; j<=maxcol; j++)
if ((p=tbl[i][j]) && p->expr) RealEvalOne(p,i,j, &chgct);
}
else if ( calc_order == BYCOLS ) {
for (j=0; j<=maxcol; j++)
for (i=0; i<=maxrow; i++)
if ((p=tbl[i][j]) && p->expr) RealEvalOne(p,i,j, &chgct);
}
else error("Internal error calc_order");
(void) signal(SIGFPE, quit);
return(chgct);
}
RealEvalOne(p, i , j, chgct)
register struct ent *p;
int i, j, *chgct;
{
if (p->flags & is_strexpr) {
char *v;
if (setjmp(fpe_save)) {
error("Floating point exception %s", v_name( i, j));
v = "";
} else {
v = seval(p->expr);
}
if (!v && !p->label) /* Everything's fine */
return;
if (!p->label || !v || strcmp(v, p->label) != 0) {
(*chgct)++;
p->flags |= is_changed;
}
if(p->label)
xfree(p->label);
p->label = v;
} else {
double v;
if (setjmp(fpe_save)) {
error("Floating point exception %s", v_name( i, j));
v = 0.0;
} else {
v = eval (p->expr);
}
if (v != p->v) {
p->v = v; (*chgct)++;
p->flags |= is_changed|is_valid;
}
}
}
struct enode *
new(op, a1, a2)
struct enode *a1, *a2;
{
register struct enode *p;
p = (struct enode *) xmalloc ((unsigned)sizeof (struct enode));
p->op = op;
p->e.o.left = a1;
p->e.o.right = a2;
return p;
}
struct enode *
new_var(op, a1)
struct ent_ptr a1;
{
register struct enode *p;
p = (struct enode *) xmalloc ((unsigned)sizeof (struct enode));
p->op = op;
p->e.v = a1;
return p;
}
struct enode *
new_range(op, a1)
struct range_s a1;
{
register struct enode *p;
p = (struct enode *) xmalloc ((unsigned)sizeof (struct enode));
p->op = op;
p->e.r = a1;
return p;
}
struct enode *
new_const(op, a1)
double a1;
{
register struct enode *p;
p = (struct enode *) xmalloc ((unsigned)sizeof (struct enode));
p->op = op;
p->e.k = a1;
return p;
}
struct enode *
new_str(s)
char *s;
{
register struct enode *p;
p = (struct enode *) xmalloc ((unsigned)sizeof(struct enode));
p->op = O_SCONST;
p->e.s = s;
return(p);
}
copy(dv1, dv2, v1, v2)
struct ent *dv1, *dv2, *v1, *v2;
{
int minsr, minsc;
int maxsr, maxsc;
int mindr, mindc;
int maxdr, maxdc;
int vr, vc;
int r, c;
mindr = dv1->row;
mindc = dv1->col;
maxdr = dv2->row;
maxdc = dv2->col;
if (mindr>maxdr) r = maxdr, maxdr = mindr, mindr = r;
if (mindc>maxdc) c = maxdc, maxdc = mindc, mindc = c;
maxsr = v2->row;
maxsc = v2->col;
minsr = v1->row;
minsc = v1->col;
if (minsr>maxsr) r = maxsr, maxsr = minsr, minsr = r;
if (minsc>maxsc) c = maxsc, maxsc = minsc, minsc = c;
if (maxdr >= MAXROWS ||
maxdc >= MAXCOLS) {
error ("The table can't be any bigger");
return;
}
erase_area(mindr, mindc, maxdr, maxdc);
if (minsr == maxsr && minsc == maxsc) {
/* Source is a single cell */
for(vr = mindr; vr <= maxdr; vr++)
for (vc = mindc; vc <= maxdc; vc++)
copyrtv(vr, vc, minsr, minsc, maxsr, maxsc);
} else if (minsr == maxsr) {
/* Source is a single row */
for (vr = mindr; vr <= maxdr; vr++)
copyrtv(vr, mindc, minsr, minsc, maxsr, maxsc);
} else if (minsc == maxsc) {
/* Source is a single column */
for (vc = mindc; vc <= maxdc; vc++)
copyrtv(mindr, vc, minsr, minsc, maxsr, maxsc);
} else {
/* Everything else */
copyrtv(mindr, mindc, minsr, minsc, maxsr, maxsc);
}
sync_refs();
}
copyrtv(vr, vc, minsr, minsc, maxsr, maxsc)
int vr, vc, minsr, minsc, maxsr, maxsc;
{
register struct ent *p;
register struct ent *n;
register int sr, sc;
register int dr, dc;
for (dr=vr, sr=minsr; sr<=maxsr; sr++, dr++)
for (dc=vc, sc=minsc; sc<=maxsc; sc++, dc++) {
n = lookat (dr, dc);
(void) clearent(n);
if (p = tbl[sr][sc])
copyent( n, p, dr - sr, dc - sc);
}
}
eraser(v1, v2)
struct ent *v1, *v2;
{
FullUpdate++;
flush_saved();
erase_area(v1->row, v1->col, v2->row, v2->col);
sync_refs();
}
/* Goto subroutines */
g_free()
{
switch (gs.g_type) {
case G_STR: xfree(gs.g_s); break;
default: break;
}
gs.g_type = G_NONE;
}
go_last()
{
switch (gs.g_type) {
case G_NONE:
error("Nothing to repeat"); break;
case G_NUM:
num_search(gs.g_n);
break;
case G_CELL:
moveto(gs.g_row, gs.g_col);
break;
case G_STR:
gs.g_type = G_NONE; /* Don't free the string */
str_search(gs.g_s);
break;
default: error("go_last: internal error");
}
}
moveto(row, col)
int row, col;
{
currow = row;
curcol = col;
g_free();
gs.g_type = G_CELL;
gs.g_row = currow;
gs.g_col = curcol;
}
num_search(n)
double n;
{
register struct ent *p;
register int r,c;
g_free();
gs.g_type = G_NUM;
gs.g_n = n;
r = currow;
c = curcol;
do {
if (c < maxcol)
c++;
else {
if (r < maxrow) {
while(++r < maxrow && row_hidden[r]) /* */;
c = 0;
} else {
r = 0;
c = 0;
}
}
if (r == currow && c == curcol) {
error("Number not found");
return;
}
p = tbl[r][c];
} while(col_hidden[c] || !p || p && (!(p->flags & is_valid)
|| (p->flags&is_valid) && p->v != n));
currow = r;
curcol = c;
}
str_search(s)
char *s;
{
register struct ent *p;
register int r,c;
char *tmp;
#if defined(BSD42) || defined(BSD43)
if ((tmp = re_comp(s)) != (char *)0) {
xfree(s);
error(tmp);
return;
}
#endif
#if defined(SYSV2) || defined(SYSV3)
if ((tmp = regcmp(s, (char *)0)) == (char *)0) {
xfree(s);
error("Invalid search string");
return;
}
#endif
g_free();
gs.g_type = G_STR;
gs.g_s = s;
r = currow;
c = curcol;
do {
if (c < maxcol)
c++;
else {
if (r < maxrow) {
while(++r < maxrow && row_hidden[r]) /* */;
c = 0;
} else {
r = 0;
c = 0;
}
}
if (r == currow && c == curcol) {
error("String not found");
#if defined(SYSV2) || defined(SYSV3)
free(tmp);
#endif
return;
}
p = tbl[r][c];
} while(col_hidden[c] || !p || p && (!(p->label)
#if defined(BSD42) || defined(BSD43)
|| (re_exec(p->label) == 0)));
#else
#if defined(SYSV2) || defined(SYSV3)
|| (regex(tmp, p->label) == (char *)0)));
#else
|| (strcmp(s, p->label) != 0)));
#endif
#endif
currow = r;
curcol = c;
#if defined(SYSV2) || defined(SYSV3)
free(tmp);
#endif
}
fill (v1, v2, start, inc)
struct ent *v1, *v2;
double start, inc;
{
register r,c;
register struct ent *n;
int maxr, maxc;
int minr, minc;
maxr = v2->row;
maxc = v2->col;
minr = v1->row;
minc = v1->col;
if (minr>maxr) r = maxr, maxr = minr, minr = r;
if (minc>maxc) c = maxc, maxc = minc, minc = c;
if (maxr >= MAXROWS) maxr = MAXROWS-1;
if (maxc >= MAXCOLS) maxc = MAXCOLS-1;
if (minr < 0) minr = 0;
if (minr < 0) minr = 0;
FullUpdate++;
if( calc_order == BYROWS ) {
for (r = minr; r<=maxr; r++)
for (c = minc; c<=maxc; c++) {
n = lookat (r, c);
(void) clearent(n);
n->v = start;
start += inc;
n->flags |= (is_changed|is_valid);
}
}
else if ( calc_order == BYCOLS ) {
for (c = minc; c<=maxc; c++)
for (r = minr; r<=maxr; r++) {
n = lookat (r, c);
(void) clearent(n);
n->v = start;
start += inc;
n->flags |= (is_changed|is_valid);
}
}
else error(" Internal error calc_order");
}
let (v, e)
struct ent *v;
struct enode *e;
{
double val;
exprerr = 0;
(void) signal(SIGFPE, eval_fpe);
if (setjmp(fpe_save)) {
error ("Floating point exception in cell %s", v_name(v->row, v->col));
val = 0.0;
} else {
val = eval(e);
}
(void) signal(SIGFPE, quit);
if (exprerr) {
efree(e);
return;
}
if (constant(e)) {
if (!loading)
v->v = val * prescale;
else
v->v = val;
if (!(v->flags & is_strexpr)) {
efree (v->expr);
v->expr = 0;
}
efree(e);
v->flags |= (is_changed|is_valid);
changed++;
modflg++;
return;
}
efree (v->expr);
v->expr = e;
v->flags |= (is_changed|is_valid);
v->flags &= ~is_strexpr;
changed++;
modflg++;
}
slet (v, se, flushdir)
struct ent *v;
struct enode *se;
int flushdir;
{
char *p;
exprerr = 0;
(void) signal(SIGFPE, eval_fpe);
if (setjmp(fpe_save)) {
error ("Floating point exception in cell %s", v_name(v->row, v->col));
p = "";
} else {
p = seval(se);
}
(void) signal(SIGFPE, quit);
if (exprerr) {
efree(se);
return;
}
if (constant(se)) {
label(v, p, flushdir);
if (p)
xfree(p);
efree(se);
if (v->flags & is_strexpr) {
efree (v->expr);
v->expr = 0;
v->flags &= ~is_strexpr;
}
return;
}
efree (v->expr);
v->expr = se;
v->flags |= (is_changed|is_strexpr);
if (flushdir<0) v->flags |= is_leftflush;
else v->flags &= ~is_leftflush;
FullUpdate++;
changed++;
modflg++;
}
hide_row(arg)
int arg;
{
if (arg < 0) {
error("Invalid Range");
return;
}
if (arg > MAXROWS-2) {
error("You can't hide the last row");
return;
}
FullUpdate++;
row_hidden[arg] = 1;
}
hide_col(arg)
int arg;
{
if (arg < 0) {
error("Invalid Range");
return;
}
if (arg > MAXCOLS-2) {
error("You can't hide the last col");
return;
}
FullUpdate++;
col_hidden[arg] = 1;
}
clearent (v)
struct ent *v;
{
if (!v)
return;
label(v,"",-1);
v->v = 0;
if (v->expr)
efree(v->expr);
v->expr = 0;
v->flags |= (is_changed);
v->flags &= ~(is_valid);
changed++;
modflg++;
}
/*
* Say if an expression is a constant (return 1) or not.
*/
constant (e)
register struct enode *e;
{
return ((e == 0)
|| ((e -> op) == O_CONST)
|| ((e -> op) == O_SCONST)
|| (((e -> op) != O_VAR)
&& (((e -> op) & REDUCE) != REDUCE)
&& constant (e -> e.o.left)
&& constant (e -> e.o.right)
&& (e -> op != EXT) /* functions look like constants but aren't */
&& (e -> op != NVAL)
&& (e -> op != SVAL)
&& (e -> op != NOW)));
}
efree (e)
register struct enode *e;
{
if (e) {
if (e->op != O_VAR && e->op !=O_CONST && e->op != O_SCONST
&& (e->op & REDUCE) != REDUCE) {
efree(e->e.o.left);
efree(e->e.o.right);
}
if (e->op == O_SCONST && e->e.s)
xfree(e->e.s);
xfree ((char *)e);
}
}
label (v, s, flushdir)
register struct ent *v;
register char *s;
{
if (v) {
if (flushdir==0 && v->flags&is_valid) {
register struct ent *tv;
if (v->col>0 && ((tv=lookat(v->row,v->col-1))->flags&is_valid)==0)
v = tv, flushdir = 1;
else if (((tv=lookat (v->row,v->col+1))->flags&is_valid)==0)
v = tv, flushdir = -1;
else flushdir = -1;
}
if (v->label) xfree((char *)(v->label));
if (s && s[0]) {
v->label = xmalloc ((unsigned)(strlen(s)+1));
(void) strcpy (v->label, s);
} else
v->label = 0;
if (flushdir<0) v->flags |= is_leftflush;
else v->flags &= ~is_leftflush;
FullUpdate++;
modflg++;
}
}
decodev (v)
struct ent_ptr v;
{
register struct range *r;
if (!v.vp) (void)sprintf (line+linelim,"VAR?");
else if (r = find_range((char *)0, 0, v.vp, v.vp))
(void)sprintf(line+linelim, "%s", r->r_name);
else
(void)sprintf (line+linelim, "%s%s%s%d",
v.vf & FIX_COL ? "$" : "",
coltoa(v.vp->col),
v.vf & FIX_ROW ? "$" : "",
v.vp->row);
linelim += strlen (line+linelim);
}
char *
coltoa(col)
int col;
{
static char rname[3];
register char *p = rname;
if (col > 25) {
*p++ = col/26 + 'A' - 1;
col %= 26;
}
*p++ = col+'A';
*p = 0;
return(rname);
}
/*
* To make list elements come out in the same order
* they were entered, we must do a depth-first eval
* of the ELIST tree
*/
static
decompile_list(p)
struct enode *p;
{
if (!p) return;
decompile_list(p->e.o.left); /* depth first */
decompile(p->e.o.right, 0);
line[linelim++] = ',';
}
decompile(e, priority)
register struct enode *e; {
register char *s;
if (e) {
int mypriority;
switch (e->op) {
default: mypriority = 99; break;
case '?': mypriority = 1; break;
case ':': mypriority = 2; break;
case '|': mypriority = 3; break;
case '&': mypriority = 4; break;
case '<': case '=': case '>': mypriority = 6; break;
case '+': case '-': case '#': mypriority = 8; break;
case '*': case '/': case '%': mypriority = 10; break;
case '^': mypriority = 12; break;
}
if (mypriority<priority) line[linelim++] = '(';
switch (e->op) {
case 'f': for (s="fixed "; line[linelim++] = *s++;);
linelim--;
decompile (e->e.o.right, 30);
break;
case 'm': line[linelim++] = '-';
decompile (e->e.o.right, 30);
break;
case '~': line[linelim++] = '~';
decompile (e->e.o.right, 30);
break;
case 'v': decodev (e->e.v);
break;
case 'k': (void)sprintf (line+linelim,"%.15g",e->e.k);
linelim += strlen (line+linelim);
break;
case '$': (void)sprintf (line+linelim, "\"%s\"", e->e.s);
linelim += strlen(line+linelim);
break;
case REDUCE | '+': range_arg( "@sum(", e); break;
case REDUCE | '*': range_arg( "@prod(", e); break;
case REDUCE | 'a': range_arg( "@avg(", e); break;
case REDUCE | 's': range_arg( "@stddev(", e); break;
case REDUCE | MAX: range_arg( "@max(", e); break;
case REDUCE | MIN: range_arg( "@min(", e); break;
case ACOS: one_arg( "@acos(", e); break;
case ASIN: one_arg( "@asin(", e); break;
case ATAN: one_arg( "@atan(", e); break;
case ATAN2: two_arg( "@atan2(", e); break;
case CEIL: one_arg( "@ceil(", e); break;
case COS: one_arg( "@cos(", e); break;
case EXP: one_arg( "@exp(", e); break;
case FABS: one_arg( "@fabs(", e); break;
case FLOOR: one_arg( "@floor(", e); break;
case HYPOT: two_arg( "@hypot(", e); break;
case LOG: one_arg( "@ln(", e); break;
case LOG10: one_arg( "@log(", e); break;
case POW: two_arg( "@pow(", e); break;
case SIN: one_arg( "@sin(", e); break;
case SQRT: one_arg( "@sqrt(", e); break;
case TAN: one_arg( "@tan(", e); break;
case DTR: one_arg( "@dtr(", e); break;
case RTD: one_arg( "@rtd(", e); break;
case RND: one_arg( "@rnd(", e); break;
case HOUR: one_arg( "@hour(", e); break;
case MINUTE: one_arg( "@minute(", e); break;
case SECOND: one_arg( "@second(", e); break;
case MONTH: one_arg( "@month(", e); break;
case DAY: one_arg( "@day(", e); break;
case YEAR: one_arg( "@year(", e); break;
case DATE: one_arg( "@date(", e); break;
case STON: one_arg( "@ston(", e); break;
case FMT: two_arg( "@fmt(", e); break;
case EQS: two_arg( "@eqs(", e); break;
case NOW: for ( s = "@now"; line[linelim++] = *s++;);
linelim--;
break;
case LMAX: list_arg("@max(", e); break;
case LMIN: list_arg("@min(", e); break;
case FV: three_arg("@fv(", e); break;
case PV: three_arg("@pv(", e); break;
case PMT: three_arg("@pmt(", e); break;
case NVAL: two_arg("@nval(", e); break;
case SVAL: two_arg("@sval(", e); break;
case EXT: two_arg("@ext(", e); break;
case SUBSTR: three_arg("@substr(", e); break;
case STINDEX: index_arg("@stindex(", e); break;
case INDEX: index_arg("@index(", e); break;
case LOOKUP: index_arg("@lookup(", e); break;
default: decompile (e->e.o.left, mypriority);
line[linelim++] = e->op;
decompile (e->e.o.right, mypriority+1);
break;
}
if (mypriority<priority) line[linelim++] = ')';
} else line[linelim++] = '?';
}
index_arg(s, e)
char *s;
struct enode *e;
{
for (; line[linelim++] = *s++;);
linelim--;
decompile( e-> e.o.left, 0 );
range_arg(", ", e->e.o.right);
}
list_arg(s, e)
char *s;
struct enode *e;
{
for (; line[linelim++] = *s++;);
linelim--;
decompile (e->e.o.right, 0);
line[linelim++] = ',';
decompile_list(e->e.o.left);
line[linelim - 1] = ')';
}
one_arg(s, e)
char *s;
struct enode *e;
{
for (; line[linelim++] = *s++;);
linelim--;
decompile (e->e.o.right, 0);
line[linelim++] = ')';
}
two_arg(s,e)
char *s;
struct enode *e;
{
for (; line[linelim++] = *s++;);
linelim--;
decompile (e->e.o.left, 0);
line[linelim++] = ',';
decompile (e->e.o.right, 0);
line[linelim++] = ')';
}
three_arg(s,e)
char *s;
struct enode *e;
{
for (; line[linelim++] = *s++;);
linelim--;
decompile (e->e.o.left, 0);
line[linelim++] = ',';
decompile (e->e.o.right->e.o.left, 0);
line[linelim++] = ',';
decompile (e->e.o.right->e.o.right, 0);
line[linelim++] = ')';
}
range_arg(s,e)
char *s;
struct enode *e;
{
struct range *r;
for (; line[linelim++] = *s++;);
linelim--;
if (r = find_range((char *)0, 0, e->e.r.left.vp,
e->e.r.right.vp)) {
(void)sprintf(line+linelim, "%s", r->r_name);
linelim += strlen(line+linelim);
} else {
decodev (e->e.r.left);
line[linelim++] = ':';
decodev (e->e.r.right);
}
line[linelim++] = ')';
}
editv (row, col)
int row, col;
{
register struct ent *p;
p = lookat (row, col);
(void)sprintf (line, "let %s = ", v_name(row, col));
linelim = strlen(line);
if (p->flags & is_strexpr || p->expr == 0) {
(void)sprintf (line+linelim, "%.15g", p->v);
linelim += strlen (line+linelim);
} else {
editexp(row,col);
}
}
editexp(row,col)
int row, col;
{
register struct ent *p;
p = lookat (row, col);
decompile (p->expr, 0);
line[linelim] = 0;
}
edits (row, col)
int row, col;
{
register struct ent *p;
p = lookat (row, col);
(void)sprintf (line, "%sstring %s = ",
((p->flags&is_leftflush) ? "left" : "right"),
v_name(row, col));
linelim = strlen(line);
if (p->flags & is_strexpr && p->expr) {
editexp(row, col);
} else if (p->label) {
(void)sprintf (line+linelim, "\"%s\"", p->label);
linelim += strlen (line+linelim);
} else {
(void)sprintf (line+linelim, "\"");
linelim += 1;
}
}
\SHAR\EOF\
else
echo "will not over write ./interp.c"
fi
if [ `wc -c ./interp.c | awk '{printf $1}'` -ne 40555 ]
then
echo `wc -c ./interp.c | awk '{print "Got " $1 ", Expected " 40555}'`
fi
if `test ! -s ./crypt.c`
then
echo "Extracting ./crypt.c"
cat > ./crypt.c << '\SHAR\EOF\'
/*
* Encryption utilites
* Bradley Williams
* {allegra,ihnp4,uiucdcs,ctvax}!convex!williams
* $Revision: 6.1 $
*/
#include <stdio.h>
#include <curses.h>
#if defined(BSD42) || defined(BSD43)
#include <sys/file.h>
#else
#include <fcntl.h>
#endif
#include "sc.h"
char *strcpy();
#ifdef SYSV3
void exit();
#endif
int Crypt = 0;
creadfile (save, eraseflg)
char *save;
int eraseflg;
{
register FILE *f;
int pipefd[2];
int fildes;
int pid;
if (eraseflg && strcmp(save, curfile) && modcheck(" first")) return;
fildes = open (save, O_RDONLY, 0);
if (fildes < 0)
{
error ("Can't read file \"%s\"", save);
return;
}
if (eraseflg) erasedb ();
if (pipe(pipefd) < 0) {
error("Can't make pipe to child");
return;
}
deraw();
if ((pid=fork()) == 0) /* if child */
{
(void) close (0); /* close stdin */
(void) close (1); /* close stdout */
(void) close (pipefd[0]); /* close pipe input */
(void) dup (fildes); /* standard in from file */
(void) dup (pipefd[1]); /* connect to pipe */
(void) fprintf (stderr, " ");
(void) execl ("/bin/sh", "sh", "-c", "crypt", 0);
exit (-127);
}
else /* else parent */
{
(void) close (fildes);
(void) close (pipefd[1]); /* close pipe output */
f = fdopen (pipefd[0], "r");
if (f == 0)
{
(void) kill (pid, -9);
error ("Can't fdopen file \"%s\"", save);
(void) close (pipefd[0]);
return;
}
}
loading++;
while (fgets(line,sizeof line,f)) {
linelim = 0;
if (line[0] != '#') (void) yyparse ();
}
--loading;
(void) fclose (f);
(void) close (pipefd[0]);
while (pid != wait(&fildes)) /**/;
goraw();
linelim = -1;
modflg++;
if (eraseflg) {
(void) strcpy (curfile, save);
modflg = 0;
}
EvalAll();
}
cwritefile (fname, r0, c0, rn, cn)
char *fname;
int r0, c0, rn, cn;
{
register FILE *f;
int pipefd[2];
int fildes;
int pid;
char save[1024];
char *fn;
if (*fname == 0) fname = &curfile[0];
fn = fname;
while (*fn && (*fn == ' ')) /* Skip leading blanks */
fn++;
if ( *fn == '|' ) {
error ("Can't have encrypted pipe");
return(-1);
}
(void) strcpy(save,fname);
fildes = open (save, O_WRONLY|O_CREAT, 0600);
if (fildes < 0)
{
error ("Can't create file \"%s\"", save);
return(-1);
}
if (pipe (pipefd) < 0) {
error ("Can't make pipe to child\n");
return(-1);
}
deraw();
if ((pid=fork()) == 0) /* if child */
{
(void) close (0); /* close stdin */
(void) close (1); /* close stdout */
(void) close (pipefd[1]); /* close pipe output */
(void) dup (pipefd[0]); /* connect to pipe input */
(void) dup (fildes); /* standard out to file */
(void) fprintf (stderr, " ");
(void) execl ("/bin/sh", "sh", "-c", "crypt", 0);
exit (-127);
}
else /* else parent */
{
(void) close (fildes);
(void) close (pipefd[0]); /* close pipe input */
f = fdopen (pipefd[1], "w");
if (f == 0)
{
(void) kill (pid, -9);
error ("Can't fdopen file \"%s\"", save);
(void) close (pipefd[1]);
return(-1);
}
}
write_fd(f, r0, c0, rn, cn);
(void) fclose (f);
(void) close (pipefd[1]);
while (pid != wait(&fildes)) /**/;
(void) strcpy(curfile,save);
modflg = 0;
error ("File \"%s\" written", curfile);
goraw();
return(0);
}
\SHAR\EOF\
else
echo "will not over write ./crypt.c"
fi
if [ `wc -c ./crypt.c | awk '{printf $1}'` -ne 3437 ]
then
echo `wc -c ./crypt.c | awk '{print "Got " $1 ", Expected " 3437}'`
fi
echo "Finished archive 3 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