/* SC A Spreadsheet Calculator
* Expression interpreter and assorted support routines.
* original by James Gosling, September 1982
* modified by Mark Weiser and Bruce Israel,
* More mods Robert Bond, 12/86
* More mods by Alan Silverstein, 3-4/88, see list of changes.
#define DEBUGDTS 1 /* REMOVE ME */
/* #define EXPRTREE /* expr. dependency tree stuff, not ready yet */
#undef _C_func /* Fixes for undefined symbols on AIX */
extern int errno
; /* set by math functions */
#if defined(BSD42) || defined(BSD43)
#if defined(SYSV2) || defined(SYSV3)
/* Use this structure to save the the last 'g' command */
#define G_NONE 0 /* Starting value - must be 0*/
#define ISVALID(r,c) ((r)>=0 && (r)<maxrows && (c)>=0 && (c)<maxcols)
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 */
struct ent
*firstev
= (struct ent
*)0; /* first expr in the eval list */
#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
)
p
= fn2_eval(pow
, 1 + v2
, v3
);
answer
= v1
* (1 - 1/p
) / v2
;
answer
= v1
* (p
- 1) / v2
;
answer
= v1
* v2
/ (1 - 1/p
);
error("Unknown function in finfunc");
dostindex( val
, minr
, minc
, maxr
, maxc
)
int minr
, minc
, maxr
, maxc
;
if ( minr
== maxr
) { /* look along the row */
if (c
<= maxc
&& c
>=minc
)
} else if ( minc
== maxc
) { /* look down the column */
if (r
<= maxr
&& r
>=minr
)
error ("range specified to @stindex");
pr
= xmalloc((unsigned)(strlen(p
->label
)+1));
(void)strcpy(pr
, p
->label
);
doindex( val
, minr
, minc
, maxr
, maxc
)
int minr
, minc
, maxr
, maxc
;
if ( minr
== maxr
) { /* look along the row */
if (c
<= maxc
&& c
>=minc
&& (p
= *ATBL(tbl
, r
, c
)) && p
->flags
&is_valid
)
else if ( minc
== maxc
){ /* look down the column */
if (r
<= maxr
&& r
>=minr
&& (p
= *ATBL(tbl
, r
, c
)) && p
->flags
&is_valid
)
else error(" range specified to @index");
dolookup( val
, minr
, minc
, maxr
, maxc
, offr
, offc
)
int minr
, minc
, maxr
, maxc
, offr
, offc
;
double v
, ret
= (double)0;
register struct ent
*p
= (struct ent
*)0;
incr
= (offc
!= 0); incc
= (offr
!= 0);
for (r
= minr
, c
= minc
; r
<= maxr
&& c
<= maxc
; r
+=incr
, c
+=incc
) {
if ( (p
= *ATBL(tbl
, r
, c
)) && p
->flags
&is_valid
) {
fndr
= incc
? (minr
+ offr
) : r
;
fndc
= incr
? (minc
+ offc
) : c
;
p
= *ATBL(tbl
, fndr
, fndc
);
else error(" range specified to @[hv]lookup");
if ( p
&& p
->flags
&is_valid
)
for (r
= minr
, c
= minc
; r
<= maxr
&& c
<= maxc
; r
+=incr
, c
+=incc
) {
if ( (p
= *ATBL(tbl
, r
, c
)) && p
->label
) {
if (strcmp(p
->label
,s
) == 0) {
fndr
= incc
? (minr
+ offr
) : r
;
fndc
= incr
? (minc
+ offc
) : c
;
p
= *ATBL(tbl
, fndr
, fndc
);
else error(" range specified to @[hv]lookup");
if ( p
&& p
->flags
&is_valid
)
docount(minr
, minc
, maxr
, maxc
)
int minr
, minc
, maxr
, maxc
;
for (r
= minr
; r
<=maxr
; r
++)
for (c
= minc
; c
<=maxc
; c
++)
if ((p
= *ATBL(tbl
, r
, c
)) && p
->flags
&is_valid
)
dosum(minr
, minc
, maxr
, maxc
)
int minr
, minc
, maxr
, maxc
;
for (r
= minr
; r
<=maxr
; r
++)
for (c
= minc
; c
<=maxc
; c
++)
if ((p
= *ATBL(tbl
, r
, c
)) && p
->flags
&is_valid
)
doprod(minr
, minc
, maxr
, maxc
)
int minr
, minc
, maxr
, maxc
;
for (r
= minr
; r
<=maxr
; r
++)
for (c
= minc
; c
<=maxc
; c
++)
if ((p
= *ATBL(tbl
, r
, c
)) && p
->flags
&is_valid
)
doavg(minr
, minc
, maxr
, maxc
)
int minr
, minc
, maxr
, maxc
;
for (r
= minr
; r
<=maxr
; r
++)
for (c
= minc
; c
<=maxc
; c
++)
if ((p
= *ATBL(tbl
, r
, c
)) && p
->flags
&is_valid
) {
return (v
/ (double)count
);
dostddev(minr
, minc
, maxr
, maxc
)
int minr
, minc
, maxr
, maxc
;
for (r
= minr
; r
<=maxr
; r
++)
for (c
= minc
; c
<=maxc
; c
++)
if ((p
= *ATBL(tbl
, r
, c
)) && p
->flags
&is_valid
) {
if ((n
== 0) || (n
== 1))
return (sqrt((nd
*lp
-rp
*rp
)/(nd
*(nd
-1))));
domax(minr
, minc
, maxr
, maxc
)
int minr
, minc
, maxr
, maxc
;
for (r
= minr
; r
<=maxr
; r
++)
for (c
= minc
; c
<=maxc
; c
++)
if ((p
= *ATBL(tbl
, r
, c
)) && p
->flags
&is_valid
) {
domin(minr
, minc
, maxr
, maxc
)
int minr
, minc
, maxr
, maxc
;
for (r
= minr
; r
<=maxr
; r
++)
for (c
= minc
; c
<=maxc
; c
++)
if ((p
= *ATBL(tbl
, r
, c
)) && p
->flags
&is_valid
) {
#define sec_yr 31471200L /* 364.25 days/yr */
#define sec_mo 2622600L /* sec_yr/12: sort of an average */
int mdays
[12]={ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
mdays
[1] = 28 + (yr
%4 == 0);
if (mo
< 1 || mo
> 12 || day
< 1 || day
> mdays
[--mo
] ||
yr
> 1999 || yr
< 1970) {
error("@dts: invalid argument");
for (i
= 1970; i
< yr
; i
++)
jdate
+= 365 + (i
%4 == 0);
* We may fail this test once a year because of time zone
* and daylight savings time errors. This bounces the
* trial time past the boundary. The error introduced is
trial
+= sec_day
*(yr
- tp
->tm_year
);
/* We may fail this test once a month. */
trial
+= sec_day
*(mo
- tp
->tm_mon
);
if (tp
->tm_mday
+ tp
->tm_hour
+ tp
->tm_min
+ tp
->tm_sec
!= day
) {
trial
-= (tp
->tm_mday
- day
)*sec_day
+ tp
->tm_hour
*sec_hr
+ tp
->tm_min
*sec_min
+ tp
->tm_sec
;
if (tp
->tm_mday
+ tp
->tm_hour
+ tp
->tm_min
+ tp
->tm_sec
+
tp
->tm_year
+ tp
->tm_mon
!= yr
+mo
+day
)
if (hr
< 0 || hr
> 23 || min
< 0 || min
> 59 || sec
< 0 || sec
> 59) {
error ("@tts: Invalid argument");
return ((double)(sec
+min
*60+hr
*3600));
static struct tm tm_cache
;
return (double)time((long *)0);
tm_cache
.tm_year
+= 1900;
case HOUR
: return((double)(tm_cache
.tm_hour
));
case MINUTE
: return((double)(tm_cache
.tm_min
));
case SECOND
: return((double)(tm_cache
.tm_sec
));
case MONTH
: return((double)(tm_cache
.tm_mon
));
case DAY
: return((double)(tm_cache
.tm_mday
));
case YEAR
: return((double)(tm_cache
.tm_year
));
else if (strcmp(s1
, s2
) == 0)
* 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.
int collen
; /* length of string */
int row
, col
; /* integer values */
struct ent
*ep
= (struct ent
*)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
= *ATBL(tbl
, row
, col
);
* Given a string representing a column name and a value which is a column
* number, return the selected cell's numeric value, if any.
return (((ep
= getent (colstr
, rowdoub
)) && ((ep
-> flags
) & is_valid
)) ?
* The list routines (e.g. dolmax) are called with an LMAX enode.
* The left pointer is a chain of ELIST nodes, the right pointer
register double maxval
= 0; /* Assignment to shut up lint */
register struct enode
*p
;
for (p
= ep
; p
; p
= p
->e
.o
.left
) {
if (!count
|| v
> maxval
) {
if (count
) return maxval
;
register double minval
= 0; /* Assignment to shut up lint */
register struct enode
*p
;
for (p
= ep
; p
; p
= p
->e
.o
.left
) {
if (!count
|| v
< minval
) {
if (count
) return minval
;
register struct enode
*e
;
if (e
== (struct enode
*)0) return (double)0;
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
: (double)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
);
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
;
return dolookup(e
->e
.o
.left
, minr
, minc
, maxr
, maxc
,
return dolookup(e
->e
.o
.left
->e
.o
.left
, minr
,minc
,maxr
,maxc
,
(int) eval(e
->e
.o
.left
->e
.o
.right
), 0);
return dolookup(e
->e
.o
.left
->e
.o
.left
, minr
,minc
,maxr
,maxc
,
0, (int) eval(e
->e
.o
.left
->e
.o
.right
));
return doindex(eval(e
->e
.o
.left
), minr
, minc
, maxr
, maxc
);
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
;
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
| 'c': return docount(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 ABS
: return (fn1_eval( fabs
, eval(e
->e
.o
.right
)));
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
)));
temp
= eval(e
->e
.o
.right
);
return(temp
-floor(temp
) < 0.5 ?
floor(temp
) : ceil(temp
));
double temp
= eval(e
->e
.o
.left
);
int prec
= (int) eval(e
->e
.o
.right
), scal
= 1;
while (prec
-- > 0) scal
*= 10;
temp
= ((temp
-floor(temp
)) < 0.5 ?
floor(temp
) : ceil(temp
));
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 DTS
: return (dodts((int)eval(e
->e
.o
.left
),
(int)eval(e
->e
.o
.right
->e
.o
.left
),
(int)eval(e
->e
.o
.right
->e
.o
.right
)));
case TTS
: return (dotts((int)eval(e
->e
.o
.left
),
(int)eval(e
->e
.o
.right
->e
.o
.left
),
(int)eval(e
->e
.o
.right
->e
.o
.right
)));
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");
eval_fpe(signo
) /* Trap for FPE errors in eval */
(void)fpsetsticky((fp_except
)0); /* Clear exception */
double fn2_eval(fn
, arg1
, arg2
)
* Rules for string functions:
* Take string arguments which they xfree.
* All returned strings are assumed to be xalloced.
p
= xmalloc((unsigned)(strlen(arg1
)+strlen(arg2
)+1));
p
= xmalloc((unsigned)25);
(void)sprintf(buff
, fmtstr
, v
);
p
= xmalloc((unsigned)(strlen(buff
)+1));
* 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
error("Warning: External functions unavailable on VMS");
return (strcpy (xmalloc((unsigned) 1), "\0"));
static char *prevstr
= (char *)0; /* previous result */
char buff
[FBUFLEN
]; /* command line/return, not permanently alloc */
prevstr
= xmalloc((unsigned)1);
error ("Warning: external functions disabled; using %s value",
prevstr
? "previous" : "null");
if (command
) xfree (command
);
if (prevstr
) xfree (prevstr
); /* no longer needed */
if ((! command
) || (! *command
)) {
error ("Warning: external function given null command name");
if (command
) xfree (command
);
(void) sprintf (buff
, "%s %g", command
, value
); /* build cmd line */
error ("Running external function...");
if ((pp
= popen (buff
, "r")) == (FILE *) NULL
) /* run it */
error ("Warning: running \"%s\" failed", buff
);
if (fgets (buff
, sizeof(buff
)-1, pp
) == NULL
) /* one line */
error ("Warning: external function returned nothing");
error (""); /* erase notice */
buff
[sizeof(buff
)-1] = '\0';
if (cp
= strchr (buff
, '\n')) /* contains newline */
*cp
= '\0'; /* end string there */
xmalloc ((unsigned) (strlen (buff
) + 1)), buff
);
return (strcpy (xmalloc ((unsigned) (strlen (prevstr
) + 1)), prevstr
));
* 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.
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.
if (v2
>= strlen (s
)) /* past end */
v2
= strlen (s
) - 1; /* to end */
if (v1
< 0 || v1
> v2
) { /* out of range, return null string */
p
= xmalloc((unsigned)1);
s2
= p
= xmalloc((unsigned)(v2
-v1
+2));
for(; v1
<= v2
; s1
++, s2
++, v1
++)
register struct enode
*se
;
if (se
== (struct enode
*)0) return (char *)0;
case O_SCONST
: p
= xmalloc((unsigned)(strlen(se
->e
.s
)+1));
(void) strcpy(p
, se
->e
.s
);
p
= xmalloc((unsigned)(strlen(ep
->label
)+1));
(void) strcpy(p
, ep
->label
);
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
)));
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));
error ("Illegal string expression");
* 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 */
error("iteration count must be at least 1");
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.
(void) signal(SIGFPE
, eval_fpe
);
for (p
= firstev
; p
; p
= p
->evnext
)
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
);
RealEvalOne(p
, i
, j
, chgct
)
if (p
->flags
& is_strexpr
) {
error("Floating point exception %s", v_name(p
->row
, p
->col
));
error("Floating point exception %s", v_name(i
, j
));
if (!v
&& !p
->label
) /* Everything's fine */
if (!p
->label
|| !v
|| strcmp(v
, p
->label
) != 0) {
error("Floating point exception %s", v_name(p
->row
, p
->col
));
error("Floating point exception %s", v_name(i
, j
));
p
->flags
|= is_changed
|is_valid
;
register struct enode
*p
;
p
= (struct enode
*) xmalloc ((unsigned)sizeof (struct enode
));
register struct enode
*p
;
p
= (struct enode
*) xmalloc ((unsigned)sizeof (struct enode
));
register struct enode
*p
;
p
= (struct enode
*) xmalloc ((unsigned)sizeof (struct enode
));
register struct enode
*p
;
p
= (struct enode
*) xmalloc ((unsigned)sizeof (struct enode
));
register struct enode
*p
;
p
= (struct enode
*) xmalloc ((unsigned)sizeof(struct enode
));
struct ent
*dv1
, *dv2
, *v1
, *v2
;
if (mindr
>maxdr
) r
= maxdr
, maxdr
= mindr
, mindr
= r
;
if (mindc
>maxdc
) c
= maxdc
, maxdc
= mindc
, mindc
= c
;
if (minsr
>maxsr
) r
= maxsr
, maxsr
= minsr
, minsr
= r
;
if (minsc
>maxsc
) c
= maxsc
, maxsc
= minsc
, minsc
= c
;
checkbounds(&maxdr
, &maxdc
);
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
);
copyrtv(mindr
, mindc
, minsr
, minsc
, maxsr
, maxsc
);
copyrtv(vr
, vc
, minsr
, minsc
, maxsr
, maxsc
)
int vr
, vc
, minsr
, minsc
, maxsr
, maxsc
;
for (dr
=vr
, sr
=minsr
; sr
<=maxsr
; sr
++, dr
++)
for (dc
=vc
, sc
=minsc
; sc
<=maxsc
; sc
++, dc
++) {
if (p
= *ATBL(tbl
, sr
, sc
))
copyent( n
, p
, dr
- sr
, dc
- sc
);
if (n
= *ATBL(tbl
, dr
, dc
))
erase_area(v1
->row
, v1
->col
, v2
->row
, v2
->col
);
case G_STR
: xfree(gs
.g_s
); break;
error("Nothing to repeat"); break;
moveto(gs
.g_row
, gs
.g_col
);
gs
.g_type
= G_NONE
; /* Don't free the string */
default: error("go_last: internal error");
endr
= maxrow
? maxrow
-1 : 0;
endc
= maxcol
? maxcol
-1 : 0;
while(++r
< maxrow
&& row_hidden
[r
]) /* */;
if (r
== endr
&& c
== endc
) {
error("Number not found");
} while(col_hidden
[c
] || !p
|| p
&& (!(p
->flags
& is_valid
)
|| (p
->flags
&is_valid
) && p
->v
!= n
));
#if defined(BSD42) || defined(BSD43)
if ((tmp
= re_comp(s
)) != (char *)0) {
#if defined(SYSV2) || defined(SYSV3)
if ((tmp
= regcmp(s
, (char *)0)) == (char *)0) {
error("Invalid search string");
endr
= maxrow
? maxrow
-1 : 0;
endc
= maxcol
? maxcol
-1 : 0;
while(++r
< maxrow
&& row_hidden
[r
]) /* */;
if (r
== endr
&& c
== endc
) {
error("String not found");
#if defined(SYSV2) || defined(SYSV3)
} while(col_hidden
[c
] || !p
|| p
&& (!(p
->label
)
#if defined(BSD42) || defined(BSD43)
|| (re_exec(p
->label
) == 0)));
#if defined(SYSV2) || defined(SYSV3)
|| (regex(tmp
, p
->label
) == (char *)0)));
|| (strcmp(s
, p
->label
) != 0)));
#if defined(SYSV2) || defined(SYSV3)
fill (v1
, v2
, start
, inc
)
if (minr
>maxr
) r
= maxr
, maxr
= minr
, minr
= r
;
if (minc
>maxc
) c
= maxc
, maxc
= minc
, minc
= c
;
checkbounds(&maxr
, &maxc
);
if( calc_order
== BYROWS
) {
for (r
= minr
; r
<=maxr
; r
++)
for (c
= minc
; c
<=maxc
; c
++) {
n
->flags
|= (is_changed
|is_valid
);
else if ( calc_order
== BYCOLS
) {
for (c
= minc
; c
<=maxc
; c
++)
for (r
= minr
; r
<=maxr
; r
++) {
n
->flags
|= (is_changed
|is_valid
);
else error(" Internal error calc_order");
(void) signal(SIGFPE
, eval_fpe
);
error ("Floating point exception in cell %s", v_name(v
->row
, v
->col
));
(void) signal(SIGFPE
, quit
);
efree((struct ent
*)0, e
);
if (!(v
->flags
& is_strexpr
)) {
v
->expr
= (struct enode
*)0;
efree((struct ent
*)0, e
);
v
->flags
|= (is_changed
|is_valid
);
v
->flags
|= (is_changed
|is_valid
);
(void) signal(SIGFPE
, eval_fpe
);
error ("Floating point exception in cell %s", v_name(v
->row
, v
->col
));
(void) signal(SIGFPE
, quit
);
efree((struct ent
*)0, se
);
efree((struct ent
*)0, se
);
if (v
->flags
& is_strexpr
) {
v
->expr
= (struct enode
*)0;
v
->flags
|= (is_changed
|is_strexpr
);
if (flushdir
<0) v
->flags
|= is_leftflush
;
else v
->flags
&= ~is_leftflush
;
* put an expression in the expression tree, only the top of each branch is
if (v
->expr
->o
.v
->evnext
)
/* for now insert at the beginning of the list */
v
->evprev
= (struct ent
*)0;
if (!growtbl(GROWROW
, arg
+1, 0))
{ error("You can't hide the last row");
{ if ((arg
>= ABSMAXCOLS
-1) || !growtbl(GROWCOL
, 0, arg
+1))
{ error("You can't hide the last col");
v
->expr
= (struct enode
*)0;
v
->flags
|= (is_changed
);
* Say if an expression is a constant (return 1) or not.
register struct enode
*e
;
return ((e
== (struct enode
*)0)
|| ((e
-> op
) == O_CONST
)
|| ((e
-> op
) == O_SCONST
)
&& (((e
-> op
) & REDUCE
) != REDUCE
)
&& constant (e
-> e
.o
.left
)
&& constant (e
-> e
.o
.right
)
&& (e
-> op
!= EXT
) /* functions look like constants but aren't */
if (e
->op
!= O_VAR
&& e
->op
!=O_CONST
&& e
->op
!= O_SCONST
&& (e
->op
& REDUCE
) != REDUCE
) {
if (e
->op
== O_SCONST
&& e
->e
.s
)
/* delete this cell from the eval list */
v
->evprev
->evnext
= v
->evnext
;
v
->evnext
->evprev
= v
->evprev
;
if (flushdir
==0 && v
->flags
&is_valid
) {
if (v
->col
>0 && ((tv
=lookat(v
->row
,v
->col
-1))->flags
&is_valid
)==0)
else if (((tv
=lookat (v
->row
,v
->col
+1))->flags
&is_valid
)==0)
if (v
->label
) xfree((char *)(v
->label
));
v
->label
= xmalloc ((unsigned)(strlen(s
)+1));
(void) strcpy (v
->label
, s
);
if (flushdir
<0) v
->flags
|= is_leftflush
;
else v
->flags
&= ~is_leftflush
;
register struct range
*r
;
if (!v
.vp
) (void)sprintf (line
+linelim
,"VAR?");
else if ((r
= find_range((char *)0, 0, v
.vp
, v
.vp
)) && !r
->r_is_range
)
(void)sprintf(line
+linelim
, "%s", r
->r_name
);
(void)sprintf (line
+linelim
, "%s%s%s%d",
v
.vf
& FIX_COL
? "$" : "",
v
.vf
& FIX_ROW
? "$" : "",
linelim
+= strlen (line
+linelim
);
register char *p
= rname
;
* To make list elements come out in the same order
* they were entered, we must do a depth-first eval
decompile_list(p
->e
.o
.left
); /* depth first */
decompile(p
->e
.o
.right
, 0);
register struct enode
*e
;
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
++] = '(';
case 'f': for (s
="fixed "; line
[linelim
++] = *s
++;);
decompile (e
->e
.o
.right
, 30);
case 'm': line
[linelim
++] = '-';
decompile (e
->e
.o
.right
, 30);
case '~': line
[linelim
++] = '~';
decompile (e
->e
.o
.right
, 30);
case 'v': decodev (e
->e
.v
);
case 'k': (void)sprintf (line
+linelim
,"%.15g",e
->e
.k
);
linelim
+= strlen (line
+linelim
);
case '$': (void)sprintf (line
+linelim
, "\"%s\"", e
->e
.s
);
linelim
+= strlen(line
+linelim
);
case REDUCE
| '+': range_arg( "@sum(", e
); break;
case REDUCE
| '*': range_arg( "@prod(", e
); break;
case REDUCE
| 'a': range_arg( "@avg(", e
); break;
case REDUCE
| 'c': range_arg( "@count(", 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 ABS
: one_arg( "@abs(", 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 ROUND
: two_arg( "@round(", 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 DTS
: three_arg( "@dts(", e
); break;
case TTS
: three_arg( "@tts(", 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
++;);
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;
case HLOOKUP
: two_arg_index("@hlookup(", e
); break;
case VLOOKUP
: two_arg_index("@vlookup(", e
); break;
case IF
: three_arg("@if(", e
); break;
default: decompile (e
->e
.o
.left
, mypriority
);
decompile (e
->e
.o
.right
, mypriority
+1);
if (mypriority
<priority
) line
[linelim
++] = ')';
} else line
[linelim
++] = '?';
for (; line
[linelim
++] = *s
++;);
decompile( e
-> e
.o
.left
, 0 );
range_arg(", ", e
->e
.o
.right
);
for (; line
[linelim
++] = *s
++;);
decompile( e
->e
.o
.left
->e
.o
.left
, 0 );
range_arg(",", e
->e
.o
.right
);
decompile( e
->e
.o
.left
->e
.o
.right
, 0 );
for (; line
[linelim
++] = *s
++;);
decompile (e
->e
.o
.right
, 0);
decompile_list(e
->e
.o
.left
);
for (; line
[linelim
++] = *s
++;);
decompile (e
->e
.o
.right
, 0);
for (; line
[linelim
++] = *s
++;);
decompile (e
->e
.o
.left
, 0);
decompile (e
->e
.o
.right
, 0);
for (; line
[linelim
++] = *s
++;);
decompile (e
->e
.o
.left
, 0);
decompile (e
->e
.o
.right
->e
.o
.left
, 0);
decompile (e
->e
.o
.right
->e
.o
.right
, 0);
for (; line
[linelim
++] = *s
++;);
if ((r
= find_range((char *)0, 0, e
->e
.r
.left
.vp
,
e
->e
.r
.right
.vp
)) && r
->r_is_range
) {
(void)sprintf(line
+linelim
, "%s", r
->r_name
);
linelim
+= strlen(line
+linelim
);
(void)sprintf (line
, "let %s = ", v_name(row
, col
));
if (p
->flags
& is_strexpr
|| p
->expr
== 0) {
(void)sprintf (line
+linelim
, "%.15g", p
->v
);
linelim
+= strlen (line
+linelim
);
(void)sprintf (line
, "%sstring %s = ",
((p
->flags
&is_leftflush
) ? "left" : "right"),
if (p
->flags
& is_strexpr
&& p
->expr
) {
(void)sprintf (line
+linelim
, "\"%s\"", p
->label
);
linelim
+= strlen (line
+linelim
);
(void)sprintf (line
+linelim
, "\"");