Subject: v23i022: The SC Spreadsheet, release 6.8, Part02/06 Newsgroups: comp.sources.unix Approved: rsalz@uunet.UU.NET X-Checksum-Snefru: ef58239c 06502ec8 315fb6ca e68c7550 Submitted-by: Jeff 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@uunet.uu.net if you want that tool. # Contents: interp.c sc6.8p1.hdr sres.sed # Wrapped by rsalz@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 X#endif /* IEEE_MATH */ X X#include X#include X#include X#include X Xextern int errno; /* set by math functions */ X#ifdef BSD42 X#include X#include X#ifndef strchr X#define strchr index X#endif X#else X#include X#ifndef SYSIII X#include X#endif X#endif X X#include 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)=0 && (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; itm_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 (mypriorityop) { 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 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@newton.physics.purdue.edu Thu Jun 21 22:27:39 1990 XReceived: from BBN.COM by pineapple.bbn.com id ; 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 XMessage-Id: <9006212036.AA08015@sawmill.UUCP> XTo: uunet!sources@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@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...