rsalz@uunet.uu.net (Rich Salz) (03/22/89)
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@uunet.uu.net.