Commit | Line | Data |
---|---|---|
deda4c62 KB |
1 | /* SC A Spreadsheet Calculator |
2 | * Expression interpreter and assorted support routines. | |
3 | * | |
4 | * original by James Gosling, September 1982 | |
5 | * modified by Mark Weiser and Bruce Israel, | |
6 | * University of Maryland | |
7 | * | |
8 | * More mods Robert Bond, 12/86 | |
9 | * More mods by Alan Silverstein, 3-4/88, see list of changes. | |
10 | * $Revision: 6.8 $ | |
11 | */ | |
12 | ||
13 | #define DEBUGDTS 1 /* REMOVE ME */ | |
14 | /* #define EXPRTREE /* expr. dependency tree stuff, not ready yet */ | |
15 | ||
16 | #ifdef aiws | |
17 | #undef _C_func /* Fixes for undefined symbols on AIX */ | |
18 | #endif | |
19 | ||
20 | #ifdef IEEE_MATH | |
21 | #include <ieeefp.h> | |
22 | #endif /* IEEE_MATH */ | |
23 | ||
24 | #include <math.h> | |
25 | #include <signal.h> | |
26 | #include <setjmp.h> | |
27 | #include <stdio.h> | |
28 | ||
29 | extern int errno; /* set by math functions */ | |
30 | #ifdef BSD42 | |
31 | #include <strings.h> | |
32 | #include <sys/time.h> | |
33 | #ifndef strchr | |
34 | #define strchr index | |
35 | #endif | |
36 | #else | |
37 | #include <time.h> | |
38 | #ifndef SYSIII | |
39 | #include <string.h> | |
40 | #endif | |
41 | #endif | |
42 | ||
43 | #include <curses.h> | |
44 | #include "sc.h" | |
45 | ||
46 | #if defined(BSD42) || defined(BSD43) | |
47 | char *re_comp(); | |
48 | #endif | |
49 | #if defined(SYSV2) || defined(SYSV3) | |
50 | char *regcmp(); | |
51 | char *regex(); | |
52 | #endif | |
53 | ||
54 | #ifdef SIGVOID | |
55 | void quit(); | |
56 | #else | |
57 | int quit(); | |
58 | #endif | |
59 | ||
60 | /* Use this structure to save the the last 'g' command */ | |
61 | ||
62 | struct go_save { | |
63 | int g_type; | |
64 | double g_n; | |
65 | char *g_s; | |
66 | int g_row; | |
67 | int g_col; | |
68 | } gs; | |
69 | ||
70 | /* g_type can be: */ | |
71 | ||
72 | #define G_NONE 0 /* Starting value - must be 0*/ | |
73 | #define G_NUM 1 | |
74 | #define G_STR 2 | |
75 | #define G_CELL 3 | |
76 | ||
77 | #define ISVALID(r,c) ((r)>=0 && (r)<maxrows && (c)>=0 && (c)<maxcols) | |
78 | ||
79 | extern FILE *popen(); | |
80 | ||
81 | jmp_buf fpe_save; | |
82 | int exprerr; /* Set by eval() and seval() if expression errors */ | |
83 | double prescale = 1.0; /* Prescale for constants in let() */ | |
84 | int extfunc = 0; /* Enable/disable external functions */ | |
85 | int loading = 0; /* Set when readfile() is active */ | |
86 | double fn1_eval(); | |
87 | double fn2_eval(); | |
88 | struct ent *firstev = (struct ent *)0; /* first expr in the eval list */ | |
89 | ||
90 | #define PI (double)3.14159265358979323846 | |
91 | #define dtr(x) ((x)*(PI/(double)180.0)) | |
92 | #define rtd(x) ((x)*(180.0/(double)PI)) | |
93 | ||
94 | double finfunc(fun,v1,v2,v3) | |
95 | int fun; | |
96 | double v1,v2,v3; | |
97 | { | |
98 | double answer,p; | |
99 | ||
100 | p = fn2_eval(pow, 1 + v2, v3); | |
101 | ||
102 | switch(fun) | |
103 | { | |
104 | case PV: | |
105 | answer = v1 * (1 - 1/p) / v2; | |
106 | break; | |
107 | case FV: | |
108 | answer = v1 * (p - 1) / v2; | |
109 | break; | |
110 | case PMT: | |
111 | answer = v1 * v2 / (1 - 1/p); | |
112 | break; | |
113 | default: | |
114 | error("Unknown function in finfunc"); | |
115 | return((double)0); | |
116 | } | |
117 | return(answer); | |
118 | } | |
119 | ||
120 | char * | |
121 | dostindex( val, minr, minc, maxr, maxc) | |
122 | double val; | |
123 | int minr, minc, maxr, maxc; | |
124 | { | |
125 | register r,c; | |
126 | register struct ent *p; | |
127 | char *pr; | |
128 | int x; | |
129 | ||
130 | x = (int) val; | |
131 | r = minr; c = minc; | |
132 | p = (struct ent *)0; | |
133 | if ( minr == maxr ) { /* look along the row */ | |
134 | c = minc + x - 1; | |
135 | if (c <= maxc && c >=minc) | |
136 | p = *ATBL(tbl, r, c); | |
137 | } else if ( minc == maxc ) { /* look down the column */ | |
138 | r = minr + x - 1; | |
139 | if (r <= maxr && r >=minr) | |
140 | p = *ATBL(tbl, r, c); | |
141 | } else { | |
142 | error ("range specified to @stindex"); | |
143 | return((char *)0); | |
144 | } | |
145 | ||
146 | if (p && p->label) { | |
147 | pr = xmalloc((unsigned)(strlen(p->label)+1)); | |
148 | (void)strcpy(pr, p->label); | |
149 | return (pr); | |
150 | } else | |
151 | return((char *)0); | |
152 | } | |
153 | ||
154 | double | |
155 | doindex( val, minr, minc, maxr, maxc) | |
156 | double val; | |
157 | int minr, minc, maxr, maxc; | |
158 | { | |
159 | double v; | |
160 | register r,c; | |
161 | register struct ent *p; | |
162 | int x; | |
163 | ||
164 | x = (int) val; | |
165 | v = (double)0; | |
166 | r = minr; c = minc; | |
167 | if ( minr == maxr ) { /* look along the row */ | |
168 | c = minc + x - 1; | |
169 | if (c <= maxc && c >=minc | |
170 | && (p = *ATBL(tbl, r, c)) && p->flags&is_valid ) | |
171 | return p->v; | |
172 | } | |
173 | else if ( minc == maxc ){ /* look down the column */ | |
174 | r = minr + x - 1; | |
175 | if (r <= maxr && r >=minr | |
176 | && (p = *ATBL(tbl, r, c)) && p->flags&is_valid ) | |
177 | return p->v; | |
178 | } | |
179 | else error(" range specified to @index"); | |
180 | return v; | |
181 | } | |
182 | ||
183 | double | |
184 | dolookup( val, minr, minc, maxr, maxc, offr, offc) | |
185 | struct enode * val; | |
186 | int minr, minc, maxr, maxc, offr, offc; | |
187 | { | |
188 | double v, ret = (double)0; | |
189 | register r,c; | |
190 | register struct ent *p = (struct ent *)0; | |
191 | int incr,incc,fndr,fndc; | |
192 | char *s; | |
193 | ||
194 | incr = (offc != 0); incc = (offr != 0); | |
195 | if (etype(val) == NUM) { | |
196 | v = eval(val); | |
197 | for (r = minr, c = minc; r <= maxr && c <= maxc; r+=incr, c+=incc) { | |
198 | if ( (p = *ATBL(tbl, r, c)) && p->flags&is_valid ) { | |
199 | if (p->v <= v) { | |
200 | fndr = incc ? (minr + offr) : r; | |
201 | fndc = incr ? (minc + offc) : c; | |
202 | if (ISVALID(fndr,fndc)) | |
203 | p = *ATBL(tbl, fndr, fndc); | |
204 | else error(" range specified to @[hv]lookup"); | |
205 | if ( p && p->flags&is_valid) | |
206 | ret = p->v; | |
207 | } else break; | |
208 | } | |
209 | } | |
210 | } else { | |
211 | s = seval(val); | |
212 | for (r = minr, c = minc; r <= maxr && c <= maxc; r+=incr, c+=incc) { | |
213 | if ( (p = *ATBL(tbl, r, c)) && p->label ) { | |
214 | if (strcmp(p->label,s) == 0) { | |
215 | fndr = incc ? (minr + offr) : r; | |
216 | fndc = incr ? (minc + offc) : c; | |
217 | if (ISVALID(fndr,fndc)) | |
218 | p = *ATBL(tbl, fndr, fndc); | |
219 | else error(" range specified to @[hv]lookup"); | |
220 | break; | |
221 | } | |
222 | } | |
223 | } | |
224 | if ( p && p->flags&is_valid) | |
225 | ret = p->v; | |
226 | xfree(s); | |
227 | } | |
228 | return ret; | |
229 | } | |
230 | ||
231 | double | |
232 | docount(minr, minc, maxr, maxc) | |
233 | int minr, minc, maxr, maxc; | |
234 | { | |
235 | int v; | |
236 | register r,c; | |
237 | register struct ent *p; | |
238 | ||
239 | v = 0; | |
240 | for (r = minr; r<=maxr; r++) | |
241 | for (c = minc; c<=maxc; c++) | |
242 | if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid) | |
243 | v++; | |
244 | return v; | |
245 | } | |
246 | ||
247 | double | |
248 | dosum(minr, minc, maxr, maxc) | |
249 | int minr, minc, maxr, maxc; | |
250 | { | |
251 | double v; | |
252 | register r,c; | |
253 | register struct ent *p; | |
254 | ||
255 | v = (double)0; | |
256 | for (r = minr; r<=maxr; r++) | |
257 | for (c = minc; c<=maxc; c++) | |
258 | if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid) | |
259 | v += p->v; | |
260 | return v; | |
261 | } | |
262 | ||
263 | double | |
264 | doprod(minr, minc, maxr, maxc) | |
265 | int minr, minc, maxr, maxc; | |
266 | { | |
267 | double v; | |
268 | register r,c; | |
269 | register struct ent *p; | |
270 | ||
271 | v = 1; | |
272 | for (r = minr; r<=maxr; r++) | |
273 | for (c = minc; c<=maxc; c++) | |
274 | if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid) | |
275 | v *= p->v; | |
276 | return v; | |
277 | } | |
278 | ||
279 | double | |
280 | doavg(minr, minc, maxr, maxc) | |
281 | int minr, minc, maxr, maxc; | |
282 | { | |
283 | double v; | |
284 | register r,c,count; | |
285 | register struct ent *p; | |
286 | ||
287 | v = (double)0; | |
288 | count = 0; | |
289 | for (r = minr; r<=maxr; r++) | |
290 | for (c = minc; c<=maxc; c++) | |
291 | if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid) { | |
292 | v += p->v; | |
293 | count++; | |
294 | } | |
295 | ||
296 | if (count == 0) | |
297 | return ((double) 0); | |
298 | ||
299 | return (v / (double)count); | |
300 | } | |
301 | ||
302 | double | |
303 | dostddev(minr, minc, maxr, maxc) | |
304 | int minr, minc, maxr, maxc; | |
305 | { | |
306 | double lp, rp, v, nd; | |
307 | register r,c,n; | |
308 | register struct ent *p; | |
309 | ||
310 | n = 0; | |
311 | lp = 0; | |
312 | rp = 0; | |
313 | for (r = minr; r<=maxr; r++) | |
314 | for (c = minc; c<=maxc; c++) | |
315 | if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid) { | |
316 | v = p->v; | |
317 | lp += v*v; | |
318 | rp += v; | |
319 | n++; | |
320 | } | |
321 | ||
322 | if ((n == 0) || (n == 1)) | |
323 | return ((double) 0); | |
324 | nd = (double)n; | |
325 | return (sqrt((nd*lp-rp*rp)/(nd*(nd-1)))); | |
326 | } | |
327 | ||
328 | double | |
329 | domax(minr, minc, maxr, maxc) | |
330 | int minr, minc, maxr, maxc; | |
331 | { | |
332 | double v = (double)0; | |
333 | register r,c,count; | |
334 | register struct ent *p; | |
335 | ||
336 | count = 0; | |
337 | for (r = minr; r<=maxr; r++) | |
338 | for (c = minc; c<=maxc; c++) | |
339 | if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid) { | |
340 | if (!count) { | |
341 | v = p->v; | |
342 | count++; | |
343 | } else if (p->v > v) | |
344 | v = p->v; | |
345 | } | |
346 | ||
347 | if (count == 0) | |
348 | return ((double) 0); | |
349 | ||
350 | return (v); | |
351 | } | |
352 | ||
353 | double | |
354 | domin(minr, minc, maxr, maxc) | |
355 | int minr, minc, maxr, maxc; | |
356 | { | |
357 | double v = (double)0; | |
358 | register r,c,count; | |
359 | register struct ent *p; | |
360 | ||
361 | count = 0; | |
362 | for (r = minr; r<=maxr; r++) | |
363 | for (c = minc; c<=maxc; c++) | |
364 | if ((p = *ATBL(tbl, r, c)) && p->flags&is_valid) { | |
365 | if (!count) { | |
366 | v = p->v; | |
367 | count++; | |
368 | } else if (p->v < v) | |
369 | v = p->v; | |
370 | } | |
371 | ||
372 | if (count == 0) | |
373 | return ((double) 0); | |
374 | ||
375 | return (v); | |
376 | } | |
377 | ||
378 | #define sec_min 60 | |
379 | #define sec_hr 3600L | |
380 | #define sec_day 86400L | |
381 | #define sec_yr 31471200L /* 364.25 days/yr */ | |
382 | #define sec_mo 2622600L /* sec_yr/12: sort of an average */ | |
383 | int mdays[12]={ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | |
384 | ||
385 | double | |
386 | dodts(mo, day, yr) | |
387 | int mo, day, yr; | |
388 | { | |
389 | long trial; | |
390 | register struct tm *tp; | |
391 | register int i; | |
392 | register long jdate; | |
393 | ||
394 | mdays[1] = 28 + (yr%4 == 0); | |
395 | ||
396 | if (mo < 1 || mo > 12 || day < 1 || day > mdays[--mo] || | |
397 | yr > 1999 || yr < 1970) { | |
398 | error("@dts: invalid argument"); | |
399 | return(0.0); | |
400 | } | |
401 | ||
402 | jdate = day-1; | |
403 | for (i=0; i<mo; i++) | |
404 | jdate += mdays[i]; | |
405 | for (i = 1970; i < yr; i++) | |
406 | jdate += 365 + (i%4 == 0); | |
407 | ||
408 | trial = jdate * sec_day; | |
409 | ||
410 | yr -= 1900; | |
411 | ||
412 | tp = localtime(&trial); | |
413 | ||
414 | if (tp->tm_year != yr) { | |
415 | /* | |
416 | * We may fail this test once a year because of time zone | |
417 | * and daylight savings time errors. This bounces the | |
418 | * trial time past the boundary. The error introduced is | |
419 | * corrected below. | |
420 | */ | |
421 | trial += sec_day*(yr - tp->tm_year); | |
422 | tp = localtime(&trial); | |
423 | } | |
424 | if (tp->tm_mon != mo) { | |
425 | /* We may fail this test once a month. */ | |
426 | trial += sec_day*(mo - tp->tm_mon); | |
427 | tp = localtime(&trial); | |
428 | } | |
429 | if (tp->tm_mday + tp->tm_hour + tp->tm_min + tp->tm_sec != day) { | |
430 | trial -= (tp->tm_mday - day)*sec_day + tp->tm_hour*sec_hr | |
431 | + tp->tm_min*sec_min + tp->tm_sec; | |
432 | } | |
433 | ||
434 | #ifdef DEBUGDTS | |
435 | tp = localtime(&trial); | |
436 | if (tp->tm_mday + tp->tm_hour + tp->tm_min + tp->tm_sec + | |
437 | tp->tm_year + tp->tm_mon != yr+mo+day) | |
438 | error("Dts broke down"); | |
439 | #endif | |
440 | ||
441 | return ((double)trial); | |
442 | } | |
443 | ||
444 | double | |
445 | dotts(hr, min, sec) | |
446 | int hr, min, sec; | |
447 | { | |
448 | if (hr < 0 || hr > 23 || min < 0 || min > 59 || sec < 0 || sec > 59) { | |
449 | error ("@tts: Invalid argument"); | |
450 | return ((double)0); | |
451 | } | |
452 | return ((double)(sec+min*60+hr*3600)); | |
453 | } | |
454 | ||
455 | double | |
456 | dotime(which, when) | |
457 | int which; | |
458 | double when; | |
459 | { | |
460 | long time(); | |
461 | ||
462 | static long t_cache; | |
463 | static struct tm tm_cache; | |
464 | struct tm *tp; | |
465 | long tloc; | |
466 | ||
467 | if (which == NOW) | |
468 | return (double)time((long *)0); | |
469 | ||
470 | tloc = (long)when; | |
471 | ||
472 | if (tloc != t_cache) { | |
473 | tp = localtime(&tloc); | |
474 | tm_cache = *tp; | |
475 | tm_cache.tm_mon += 1; | |
476 | tm_cache.tm_year += 1900; | |
477 | t_cache = tloc; | |
478 | } | |
479 | ||
480 | switch (which) { | |
481 | case HOUR: return((double)(tm_cache.tm_hour)); | |
482 | case MINUTE: return((double)(tm_cache.tm_min)); | |
483 | case SECOND: return((double)(tm_cache.tm_sec)); | |
484 | case MONTH: return((double)(tm_cache.tm_mon)); | |
485 | case DAY: return((double)(tm_cache.tm_mday)); | |
486 | case YEAR: return((double)(tm_cache.tm_year)); | |
487 | } | |
488 | /* Safety net */ | |
489 | return ((double)0); | |
490 | } | |
491 | ||
492 | double | |
493 | doston(s) | |
494 | char *s; | |
495 | { | |
496 | char *strtof(); | |
497 | double v; | |
498 | ||
499 | if (!s) | |
500 | return((double)0); | |
501 | ||
502 | (void)strtof(s, &v); | |
503 | xfree(s); | |
504 | return(v); | |
505 | } | |
506 | ||
507 | double | |
508 | doeqs(s1, s2) | |
509 | char *s1, *s2; | |
510 | { | |
511 | double v; | |
512 | ||
513 | if (!s1 && !s2) | |
514 | return((double)1.0); | |
515 | ||
516 | if (!s1 || !s2) | |
517 | v = 0.0; | |
518 | else if (strcmp(s1, s2) == 0) | |
519 | v = 1.0; | |
520 | else | |
521 | v = 0.0; | |
522 | ||
523 | if (s1) | |
524 | xfree(s1); | |
525 | ||
526 | if (s2) | |
527 | xfree(s2); | |
528 | ||
529 | return(v); | |
530 | } | |
531 | ||
532 | ||
533 | /* | |
534 | * Given a string representing a column name and a value which is a column | |
535 | * number, return a pointer to the selected cell's entry, if any, else 0. Use | |
536 | * only the integer part of the column number. Always free the string. | |
537 | */ | |
538 | ||
539 | struct ent * | |
540 | getent (colstr, rowdoub) | |
541 | char *colstr; | |
542 | double rowdoub; | |
543 | { | |
544 | int collen; /* length of string */ | |
545 | int row, col; /* integer values */ | |
546 | struct ent *ep = (struct ent *)0; /* selected entry */ | |
547 | ||
548 | if (((row = (int) floor (rowdoub)) >= 0) | |
549 | && (row < maxrows) /* in range */ | |
550 | && ((collen = strlen (colstr)) <= 2) /* not too long */ | |
551 | && ((col = atocol (colstr, collen)) >= 0) | |
552 | && (col < maxcols)) /* in range */ | |
553 | { | |
554 | ep = *ATBL(tbl, row, col); | |
555 | } | |
556 | ||
557 | xfree (colstr); | |
558 | return (ep); | |
559 | } | |
560 | ||
561 | ||
562 | /* | |
563 | * Given a string representing a column name and a value which is a column | |
564 | * number, return the selected cell's numeric value, if any. | |
565 | */ | |
566 | ||
567 | double | |
568 | donval (colstr, rowdoub) | |
569 | char *colstr; | |
570 | double rowdoub; | |
571 | { | |
572 | struct ent *ep; | |
573 | ||
574 | return (((ep = getent (colstr, rowdoub)) && ((ep -> flags) & is_valid)) ? | |
575 | (ep -> v) : (double)0); | |
576 | } | |
577 | ||
578 | ||
579 | /* | |
580 | * The list routines (e.g. dolmax) are called with an LMAX enode. | |
581 | * The left pointer is a chain of ELIST nodes, the right pointer | |
582 | * is a value. | |
583 | */ | |
584 | double | |
585 | dolmax(ep) | |
586 | struct enode *ep; | |
587 | { | |
588 | register int count = 0; | |
589 | register double maxval = 0; /* Assignment to shut up lint */ | |
590 | register struct enode *p; | |
591 | register double v; | |
592 | ||
593 | for (p = ep; p; p = p->e.o.left) { | |
594 | v = eval(p->e.o.right); | |
595 | if (!count || v > maxval) { | |
596 | maxval = v; count++; | |
597 | } | |
598 | } | |
599 | if (count) return maxval; | |
600 | else return (double)0; | |
601 | } | |
602 | ||
603 | double | |
604 | dolmin(ep) | |
605 | struct enode *ep; | |
606 | { | |
607 | register int count = 0; | |
608 | register double minval = 0; /* Assignment to shut up lint */ | |
609 | register struct enode *p; | |
610 | register double v; | |
611 | ||
612 | for (p = ep; p; p = p->e.o.left) { | |
613 | v = eval(p->e.o.right); | |
614 | if (!count || v < minval) { | |
615 | minval = v; count++; | |
616 | } | |
617 | } | |
618 | if (count) return minval; | |
619 | else return (double)0; | |
620 | } | |
621 | ||
622 | double | |
623 | eval(e) | |
624 | register struct enode *e; | |
625 | { | |
626 | if (e == (struct enode *)0) return (double)0; | |
627 | switch (e->op) { | |
628 | case '+': return (eval(e->e.o.left) + eval(e->e.o.right)); | |
629 | case '-': return (eval(e->e.o.left) - eval(e->e.o.right)); | |
630 | case '*': return (eval(e->e.o.left) * eval(e->e.o.right)); | |
631 | case '/': return (eval(e->e.o.left) / eval(e->e.o.right)); | |
632 | case '%': { double num, denom; | |
633 | num = floor(eval(e->e.o.left)); | |
634 | denom = floor(eval (e->e.o.right)); | |
635 | return denom ? num - floor(num/denom)*denom : (double)0; } | |
636 | case '^': return (fn2_eval(pow,eval(e->e.o.left),eval(e->e.o.right))); | |
637 | case '<': return (eval(e->e.o.left) < eval(e->e.o.right)); | |
638 | case '=': return (eval(e->e.o.left) == eval(e->e.o.right)); | |
639 | case '>': return (eval(e->e.o.left) > eval(e->e.o.right)); | |
640 | case '&': return (eval(e->e.o.left) && eval(e->e.o.right)); | |
641 | case '|': return (eval(e->e.o.left) || eval(e->e.o.right)); | |
642 | case IF: | |
643 | case '?': return eval(e->e.o.left) ? eval(e->e.o.right->e.o.left) | |
644 | : eval(e->e.o.right->e.o.right); | |
645 | case 'm': return (-eval(e->e.o.right)); | |
646 | case 'f': return (eval(e->e.o.right)); | |
647 | case '~': return (eval(e->e.o.right) == 0.0); | |
648 | case 'k': return (e->e.k); | |
649 | case 'v': return (e->e.v.vp->v); | |
650 | case INDEX: | |
651 | case LOOKUP: | |
652 | case HLOOKUP: | |
653 | case VLOOKUP: | |
654 | { register r,c; | |
655 | register maxr, maxc; | |
656 | register minr, minc; | |
657 | maxr = e->e.o.right->e.r.right.vp -> row; | |
658 | maxc = e->e.o.right->e.r.right.vp -> col; | |
659 | minr = e->e.o.right->e.r.left.vp -> row; | |
660 | minc = e->e.o.right->e.r.left.vp -> col; | |
661 | if (minr>maxr) r = maxr, maxr = minr, minr = r; | |
662 | if (minc>maxc) c = maxc, maxc = minc, minc = c; | |
663 | switch(e->op){ | |
664 | case LOOKUP: | |
665 | return dolookup(e->e.o.left, minr, minc, maxr, maxc, | |
666 | minr==maxr, minc==maxc); | |
667 | case HLOOKUP: | |
668 | return dolookup(e->e.o.left->e.o.left, minr,minc,maxr,maxc, | |
669 | (int) eval(e->e.o.left->e.o.right), 0); | |
670 | case VLOOKUP: | |
671 | return dolookup(e->e.o.left->e.o.left, minr,minc,maxr,maxc, | |
672 | 0, (int) eval(e->e.o.left->e.o.right)); | |
673 | case INDEX: | |
674 | return doindex(eval(e->e.o.left), minr, minc, maxr, maxc); | |
675 | } | |
676 | } | |
677 | case REDUCE | '+': | |
678 | case REDUCE | '*': | |
679 | case REDUCE | 'a': | |
680 | case REDUCE | 'c': | |
681 | case REDUCE | 's': | |
682 | case REDUCE | MAX: | |
683 | case REDUCE | MIN: | |
684 | { register r,c; | |
685 | register maxr, maxc; | |
686 | register minr, minc; | |
687 | maxr = e->e.r.right.vp -> row; | |
688 | maxc = e->e.r.right.vp -> col; | |
689 | minr = e->e.r.left.vp -> row; | |
690 | minc = e->e.r.left.vp -> col; | |
691 | if (minr>maxr) r = maxr, maxr = minr, minr = r; | |
692 | if (minc>maxc) c = maxc, maxc = minc, minc = c; | |
693 | switch (e->op) { | |
694 | case REDUCE | '+': return dosum(minr, minc, maxr, maxc); | |
695 | case REDUCE | '*': return doprod(minr, minc, maxr, maxc); | |
696 | case REDUCE | 'a': return doavg(minr, minc, maxr, maxc); | |
697 | case REDUCE | 'c': return docount(minr, minc, maxr, maxc); | |
698 | case REDUCE | 's': return dostddev(minr, minc, maxr, maxc); | |
699 | case REDUCE | MAX: return domax(minr, minc, maxr, maxc); | |
700 | case REDUCE | MIN: return domin(minr, minc, maxr, maxc); | |
701 | } | |
702 | } | |
703 | case ABS: return (fn1_eval( fabs, eval(e->e.o.right))); | |
704 | case ACOS: return (fn1_eval( acos, eval(e->e.o.right))); | |
705 | case ASIN: return (fn1_eval( asin, eval(e->e.o.right))); | |
706 | case ATAN: return (fn1_eval( atan, eval(e->e.o.right))); | |
707 | case ATAN2: return (fn2_eval( atan2, eval(e->e.o.left), eval(e->e.o.right))); | |
708 | case CEIL: return (fn1_eval( ceil, eval(e->e.o.right))); | |
709 | case COS: return (fn1_eval( cos, eval(e->e.o.right))); | |
710 | case EXP: return (fn1_eval( exp, eval(e->e.o.right))); | |
711 | case FABS: return (fn1_eval( fabs, eval(e->e.o.right))); | |
712 | case FLOOR: return (fn1_eval( floor, eval(e->e.o.right))); | |
713 | case HYPOT: return (fn2_eval( hypot, eval(e->e.o.left), eval(e->e.o.right))); | |
714 | case LOG: return (fn1_eval( log, eval(e->e.o.right))); | |
715 | case LOG10: return (fn1_eval( log10, eval(e->e.o.right))); | |
716 | case POW: return (fn2_eval( pow, eval(e->e.o.left), eval(e->e.o.right))); | |
717 | case SIN: return (fn1_eval( sin, eval(e->e.o.right))); | |
718 | case SQRT: return (fn1_eval( sqrt, eval(e->e.o.right))); | |
719 | case TAN: return (fn1_eval( tan, eval(e->e.o.right))); | |
720 | case DTR: return (dtr(eval(e->e.o.right))); | |
721 | case RTD: return (rtd(eval(e->e.o.right))); | |
722 | case RND: { | |
723 | double temp; | |
724 | temp = eval(e->e.o.right); | |
725 | return(temp-floor(temp) < 0.5 ? | |
726 | floor(temp) : ceil(temp)); | |
727 | } | |
728 | case ROUND: { | |
729 | double temp = eval(e->e.o.left); | |
730 | int prec = (int) eval(e->e.o.right), scal = 1; | |
731 | while (prec-- > 0) scal *= 10; | |
732 | temp *= scal; | |
733 | temp = ((temp-floor(temp)) < 0.5 ? | |
734 | floor(temp) : ceil(temp)); | |
735 | return(temp / scal); | |
736 | } | |
737 | case FV: | |
738 | case PV: | |
739 | case PMT: return(finfunc(e->op,eval(e->e.o.left), | |
740 | eval(e->e.o.right->e.o.left), | |
741 | eval(e->e.o.right->e.o.right))); | |
742 | case HOUR: return (dotime(HOUR, eval(e->e.o.right))); | |
743 | case MINUTE: return (dotime(MINUTE, eval(e->e.o.right))); | |
744 | case SECOND: return (dotime(SECOND, eval(e->e.o.right))); | |
745 | case MONTH: return (dotime(MONTH, eval(e->e.o.right))); | |
746 | case DAY: return (dotime(DAY, eval(e->e.o.right))); | |
747 | case YEAR: return (dotime(YEAR, eval(e->e.o.right))); | |
748 | case NOW: return (dotime(NOW, (double)0.0)); | |
749 | case DTS: return (dodts((int)eval(e->e.o.left), | |
750 | (int)eval(e->e.o.right->e.o.left), | |
751 | (int)eval(e->e.o.right->e.o.right))); | |
752 | case TTS: return (dotts((int)eval(e->e.o.left), | |
753 | (int)eval(e->e.o.right->e.o.left), | |
754 | (int)eval(e->e.o.right->e.o.right))); | |
755 | case STON: return (doston(seval(e->e.o.right))); | |
756 | case EQS: return (doeqs(seval(e->e.o.right),seval(e->e.o.left))); | |
757 | case LMAX: return dolmax(e); | |
758 | case LMIN: return dolmin(e); | |
759 | case NVAL: return (donval(seval(e->e.o.left),eval(e->e.o.right))); | |
760 | default: error ("Illegal numeric expression"); | |
761 | exprerr = 1; | |
762 | } | |
763 | return((double)0.0); | |
764 | } | |
765 | ||
766 | #ifdef SIGVOID | |
767 | void | |
768 | #endif | |
769 | eval_fpe(signo) /* Trap for FPE errors in eval */ | |
770 | int signo; | |
771 | { | |
772 | #ifdef IEEE_MATH | |
773 | (void)fpsetsticky((fp_except)0); /* Clear exception */ | |
774 | #endif /* IEEE_MATH */ | |
775 | longjmp(fpe_save, 1); | |
776 | } | |
777 | ||
778 | double fn1_eval(fn, arg) | |
779 | double (*fn)(); | |
780 | double arg; | |
781 | { | |
782 | double res; | |
783 | errno = 0; | |
784 | res = (*fn)(arg); | |
785 | if(errno) | |
786 | eval_fpe(0); | |
787 | ||
788 | return res; | |
789 | } | |
790 | ||
791 | double fn2_eval(fn, arg1, arg2) | |
792 | double (*fn)(); | |
793 | double arg1, arg2; | |
794 | { | |
795 | double res; | |
796 | errno = 0; | |
797 | res = (*fn)(arg1, arg2); | |
798 | if(errno) | |
799 | eval_fpe(0); | |
800 | ||
801 | return res; | |
802 | } | |
803 | ||
804 | /* | |
805 | * Rules for string functions: | |
806 | * Take string arguments which they xfree. | |
807 | * All returned strings are assumed to be xalloced. | |
808 | */ | |
809 | ||
810 | char * | |
811 | docat(s1, s2) | |
812 | register char *s1, *s2; | |
813 | { | |
814 | register char *p; | |
815 | char *arg1, *arg2; | |
816 | ||
817 | if (!s1 && !s2) | |
818 | return((char *)0); | |
819 | arg1 = s1 ? s1 : ""; | |
820 | arg2 = s2 ? s2 : ""; | |
821 | p = xmalloc((unsigned)(strlen(arg1)+strlen(arg2)+1)); | |
822 | (void) strcpy(p, arg1); | |
823 | (void) strcat(p, arg2); | |
824 | if (s1) | |
825 | xfree(s1); | |
826 | if (s2) | |
827 | xfree(s2); | |
828 | return(p); | |
829 | } | |
830 | ||
831 | char * | |
832 | dodate(tloc) | |
833 | long tloc; | |
834 | { | |
835 | char *tp; | |
836 | char *p; | |
837 | ||
838 | tp = ctime(&tloc); | |
839 | tp[24] = '\0'; | |
840 | p = xmalloc((unsigned)25); | |
841 | (void) strcpy(p, tp); | |
842 | return(p); | |
843 | } | |
844 | ||
845 | ||
846 | char * | |
847 | dofmt(fmtstr, v) | |
848 | char *fmtstr; | |
849 | double v; | |
850 | { | |
851 | char buff[FBUFLEN]; | |
852 | char *p; | |
853 | ||
854 | if (!fmtstr) | |
855 | return((char *)0); | |
856 | (void)sprintf(buff, fmtstr, v); | |
857 | p = xmalloc((unsigned)(strlen(buff)+1)); | |
858 | (void) strcpy(p, buff); | |
859 | xfree(fmtstr); | |
860 | return(p); | |
861 | } | |
862 | ||
863 | ||
864 | /* | |
865 | * Given a command name and a value, run the command with the given value and | |
866 | * read and return its first output line (only) as an allocated string, always | |
867 | * a copy of prevstr, which is set appropriately first unless external | |
868 | * functions are disabled, in which case the previous value is used. The | |
869 | * handling of prevstr and freeing of command is tricky. Returning an | |
870 | * allocated string in all cases, even if null, insures cell expressions are | |
871 | * written to files, etc. | |
872 | */ | |
873 | ||
874 | #ifdef VMS | |
875 | char * | |
876 | doext(command, value) | |
877 | char *command; | |
878 | double value; | |
879 | { | |
880 | error("Warning: External functions unavailable on VMS"); | |
881 | if (command) | |
882 | xfree(command); | |
883 | return (strcpy (xmalloc((unsigned) 1), "\0")); | |
884 | } | |
885 | ||
886 | #else /* VMS */ | |
887 | ||
888 | char * | |
889 | doext (command, value) | |
890 | char *command; | |
891 | double value; | |
892 | { | |
893 | static char *prevstr = (char *)0; /* previous result */ | |
894 | char buff[FBUFLEN]; /* command line/return, not permanently alloc */ | |
895 | ||
896 | if (!prevstr) { | |
897 | prevstr = xmalloc((unsigned)1); | |
898 | *prevstr = '\0'; | |
899 | } | |
900 | if (!extfunc) { | |
901 | error ("Warning: external functions disabled; using %s value", | |
902 | prevstr ? "previous" : "null"); | |
903 | ||
904 | if (command) xfree (command); | |
905 | } else { | |
906 | if (prevstr) xfree (prevstr); /* no longer needed */ | |
907 | prevstr = '\0'; | |
908 | ||
909 | if ((! command) || (! *command)) { | |
910 | error ("Warning: external function given null command name"); | |
911 | if (command) xfree (command); | |
912 | } else { | |
913 | FILE *pp; | |
914 | ||
915 | (void) sprintf (buff, "%s %g", command, value); /* build cmd line */ | |
916 | xfree (command); | |
917 | ||
918 | error ("Running external function..."); | |
919 | (void) refresh(); | |
920 | ||
921 | if ((pp = popen (buff, "r")) == (FILE *) NULL) /* run it */ | |
922 | error ("Warning: running \"%s\" failed", buff); | |
923 | else { | |
924 | if (fgets (buff, sizeof(buff)-1, pp) == NULL) /* one line */ | |
925 | error ("Warning: external function returned nothing"); | |
926 | else { | |
927 | char *cp; | |
928 | ||
929 | error (""); /* erase notice */ | |
930 | buff[sizeof(buff)-1] = '\0'; | |
931 | ||
932 | if (cp = strchr (buff, '\n')) /* contains newline */ | |
933 | *cp = '\0'; /* end string there */ | |
934 | ||
935 | (void) strcpy (prevstr = | |
936 | xmalloc ((unsigned) (strlen (buff) + 1)), buff); | |
937 | /* save alloc'd copy */ | |
938 | } | |
939 | (void) pclose (pp); | |
940 | ||
941 | } /* else */ | |
942 | } /* else */ | |
943 | } /* else */ | |
944 | return (strcpy (xmalloc ((unsigned) (strlen (prevstr) + 1)), prevstr)); | |
945 | } | |
946 | ||
947 | #endif /* VMS */ | |
948 | ||
949 | ||
950 | /* | |
951 | * Given a string representing a column name and a value which is a column | |
952 | * number, return the selected cell's string value, if any. Even if none, | |
953 | * still allocate and return a null string so the cell has a label value so | |
954 | * the expression is saved in a file, etc. | |
955 | */ | |
956 | ||
957 | char * | |
958 | dosval (colstr, rowdoub) | |
959 | char *colstr; | |
960 | double rowdoub; | |
961 | { | |
962 | struct ent *ep; | |
963 | char *label; | |
964 | ||
965 | label = (ep = getent (colstr, rowdoub)) ? (ep -> label) : ""; | |
966 | return (strcpy (xmalloc ((unsigned) (strlen (label) + 1)), label)); | |
967 | } | |
968 | ||
969 | ||
970 | /* | |
971 | * Substring: Note that v1 and v2 are one-based to users, but zero-based | |
972 | * when calling this routine. | |
973 | */ | |
974 | ||
975 | char * | |
976 | dosubstr(s, v1, v2) | |
977 | char *s; | |
978 | register int v1,v2; | |
979 | { | |
980 | register char *s1, *s2; | |
981 | char *p; | |
982 | ||
983 | if (!s) | |
984 | return((char *)0); | |
985 | ||
986 | if (v2 >= strlen (s)) /* past end */ | |
987 | v2 = strlen (s) - 1; /* to end */ | |
988 | ||
989 | if (v1 < 0 || v1 > v2) { /* out of range, return null string */ | |
990 | xfree(s); | |
991 | p = xmalloc((unsigned)1); | |
992 | p[0] = '\0'; | |
993 | return(p); | |
994 | } | |
995 | s2 = p = xmalloc((unsigned)(v2-v1+2)); | |
996 | s1 = &s[v1]; | |
997 | for(; v1 <= v2; s1++, s2++, v1++) | |
998 | *s2 = *s1; | |
999 | *s2 = '\0'; | |
1000 | xfree(s); | |
1001 | return(p); | |
1002 | } | |
1003 | ||
1004 | char * | |
1005 | seval(se) | |
1006 | register struct enode *se; | |
1007 | { | |
1008 | register char *p; | |
1009 | ||
1010 | if (se == (struct enode *)0) return (char *)0; | |
1011 | switch (se->op) { | |
1012 | case O_SCONST: p = xmalloc((unsigned)(strlen(se->e.s)+1)); | |
1013 | (void) strcpy(p, se->e.s); | |
1014 | return(p); | |
1015 | case O_VAR: { | |
1016 | struct ent *ep; | |
1017 | ep = se->e.v.vp; | |
1018 | ||
1019 | if (!ep->label) | |
1020 | return((char *)0); | |
1021 | p = xmalloc((unsigned)(strlen(ep->label)+1)); | |
1022 | (void) strcpy(p, ep->label); | |
1023 | return(p); | |
1024 | } | |
1025 | case '#': return(docat(seval(se->e.o.left), seval(se->e.o.right))); | |
1026 | case 'f': return(seval(se->e.o.right)); | |
1027 | case IF: | |
1028 | case '?': return(eval(se->e.o.left) ? seval(se->e.o.right->e.o.left) | |
1029 | : seval(se->e.o.right->e.o.right)); | |
1030 | case DATE: return(dodate((long)(eval(se->e.o.right)))); | |
1031 | case FMT: return(dofmt(seval(se->e.o.left), eval(se->e.o.right))); | |
1032 | case STINDEX: | |
1033 | { register r,c; | |
1034 | register maxr, maxc; | |
1035 | register minr, minc; | |
1036 | maxr = se->e.o.right->e.r.right.vp -> row; | |
1037 | maxc = se->e.o.right->e.r.right.vp -> col; | |
1038 | minr = se->e.o.right->e.r.left.vp -> row; | |
1039 | minc = se->e.o.right->e.r.left.vp -> col; | |
1040 | if (minr>maxr) r = maxr, maxr = minr, minr = r; | |
1041 | if (minc>maxc) c = maxc, maxc = minc, minc = c; | |
1042 | return dostindex(eval(se->e.o.left), minr, minc, maxr, maxc); | |
1043 | } | |
1044 | case EXT: return(doext(seval(se->e.o.left), eval(se->e.o.right))); | |
1045 | case SVAL: return(dosval(seval(se->e.o.left), eval(se->e.o.right))); | |
1046 | case SUBSTR: return(dosubstr(seval(se->e.o.left), | |
1047 | (int)eval(se->e.o.right->e.o.left) - 1, | |
1048 | (int)eval(se->e.o.right->e.o.right) - 1)); | |
1049 | default: | |
1050 | error ("Illegal string expression"); | |
1051 | exprerr = 1; | |
1052 | return((char *)0); | |
1053 | } | |
1054 | } | |
1055 | ||
1056 | /* | |
1057 | * The graph formed by cell expressions which use other cells's values is not | |
1058 | * evaluated "bottom up". The whole table is merely re-evaluated cell by cell, | |
1059 | * top to bottom, left to right, in RealEvalAll(). Each cell's expression uses | |
1060 | * constants in other cells. However, RealEvalAll() notices when a cell gets a | |
1061 | * new numeric or string value, and reports if this happens for any cell. | |
1062 | * EvalAll() repeats calling RealEvalAll() until there are no changes or the | |
1063 | * evaluation count expires. | |
1064 | */ | |
1065 | ||
1066 | int propagation = 10; /* max number of times to try calculation */ | |
1067 | ||
1068 | void | |
1069 | setiterations(i) | |
1070 | int i; | |
1071 | { | |
1072 | if(i<1) { | |
1073 | error("iteration count must be at least 1"); | |
1074 | propagation = 1; | |
1075 | } | |
1076 | else propagation = i; | |
1077 | } | |
1078 | ||
1079 | void | |
1080 | EvalAll () { | |
1081 | int lastcnt, repct = 0; | |
1082 | ||
1083 | while ((lastcnt = RealEvalAll()) && (repct++ <= propagation)); | |
1084 | if((propagation>1)&& (lastcnt >0 )) | |
1085 | error("Still changing after %d iterations",propagation-1); | |
1086 | } | |
1087 | ||
1088 | /* | |
1089 | * Evaluate all cells which have expressions and alter their numeric or string | |
1090 | * values. Return the number of cells which changed. | |
1091 | */ | |
1092 | ||
1093 | int | |
1094 | RealEvalAll () { | |
1095 | register int i,j; | |
1096 | int chgct = 0; | |
1097 | register struct ent *p; | |
1098 | ||
1099 | (void) signal(SIGFPE, eval_fpe); | |
1100 | #ifdef EXPRTREE | |
1101 | for (p = firstev; p; p = p->evnext) | |
1102 | RealEvalOne(p, &chgct); | |
1103 | #else | |
1104 | if(calc_order == BYROWS ) { | |
1105 | for (i=0; i<=maxrow; i++) | |
1106 | for (j=0; j<=maxcol; j++) | |
1107 | if ((p=tbl[i][j]) && p->expr) RealEvalOne(p,i,j, &chgct); | |
1108 | } | |
1109 | else if ( calc_order == BYCOLS ) { | |
1110 | for (j=0; j<=maxcol; j++) | |
1111 | { for (i=0; i<=maxrow; i++) | |
1112 | if ((p=tbl[i][j]) && p->expr) RealEvalOne(p,i,j, &chgct); | |
1113 | } | |
1114 | } | |
1115 | else error("Internal error calc_order"); | |
1116 | #endif | |
1117 | ||
1118 | (void) signal(SIGFPE, quit); | |
1119 | return(chgct); | |
1120 | } | |
1121 | ||
1122 | void | |
1123 | #ifdef EXPRTREE | |
1124 | RealEvalOne(p, chgct) | |
1125 | register struct ent *p; | |
1126 | int *chgct; | |
1127 | #else | |
1128 | RealEvalOne(p, i, j, chgct) | |
1129 | register struct ent *p; | |
1130 | int i, j, *chgct; | |
1131 | #endif | |
1132 | { | |
1133 | if (p->flags & is_strexpr) { | |
1134 | char *v; | |
1135 | if (setjmp(fpe_save)) { | |
1136 | #ifdef EXPRTREE | |
1137 | error("Floating point exception %s", v_name(p->row, p->col)); | |
1138 | #else | |
1139 | error("Floating point exception %s", v_name(i, j)); | |
1140 | #endif | |
1141 | v = ""; | |
1142 | } else { | |
1143 | v = seval(p->expr); | |
1144 | } | |
1145 | if (!v && !p->label) /* Everything's fine */ | |
1146 | return; | |
1147 | if (!p->label || !v || strcmp(v, p->label) != 0) { | |
1148 | (*chgct)++; | |
1149 | p->flags |= is_changed; | |
1150 | changed++; | |
1151 | } | |
1152 | if(p->label) | |
1153 | xfree(p->label); | |
1154 | p->label = v; | |
1155 | } else { | |
1156 | double v; | |
1157 | if (setjmp(fpe_save)) { | |
1158 | #ifdef EXPRTREE | |
1159 | error("Floating point exception %s", v_name(p->row, p->col)); | |
1160 | #else | |
1161 | error("Floating point exception %s", v_name(i, j)); | |
1162 | #endif | |
1163 | v = (double)0.0; | |
1164 | } else { | |
1165 | v = eval (p->expr); | |
1166 | } | |
1167 | if (v != p->v) { | |
1168 | p->v = v; (*chgct)++; | |
1169 | p->flags |= is_changed|is_valid; | |
1170 | changed++; | |
1171 | } | |
1172 | } | |
1173 | } | |
1174 | ||
1175 | struct enode * | |
1176 | new(op, a1, a2) | |
1177 | int op; | |
1178 | struct enode *a1, *a2; | |
1179 | { | |
1180 | register struct enode *p; | |
1181 | p = (struct enode *) xmalloc ((unsigned)sizeof (struct enode)); | |
1182 | p->op = op; | |
1183 | p->e.o.left = a1; | |
1184 | p->e.o.right = a2; | |
1185 | return p; | |
1186 | } | |
1187 | ||
1188 | struct enode * | |
1189 | new_var(op, a1) | |
1190 | int op; | |
1191 | struct ent_ptr a1; | |
1192 | { | |
1193 | register struct enode *p; | |
1194 | p = (struct enode *) xmalloc ((unsigned)sizeof (struct enode)); | |
1195 | p->op = op; | |
1196 | p->e.v = a1; | |
1197 | return p; | |
1198 | } | |
1199 | ||
1200 | struct enode * | |
1201 | new_range(op, a1) | |
1202 | int op; | |
1203 | struct range_s a1; | |
1204 | { | |
1205 | register struct enode *p; | |
1206 | p = (struct enode *) xmalloc ((unsigned)sizeof (struct enode)); | |
1207 | p->op = op; | |
1208 | p->e.r = a1; | |
1209 | return p; | |
1210 | } | |
1211 | ||
1212 | struct enode * | |
1213 | new_const(op, a1) | |
1214 | int op; | |
1215 | double a1; | |
1216 | { | |
1217 | register struct enode *p; | |
1218 | p = (struct enode *) xmalloc ((unsigned)sizeof (struct enode)); | |
1219 | p->op = op; | |
1220 | p->e.k = a1; | |
1221 | return p; | |
1222 | } | |
1223 | ||
1224 | struct enode * | |
1225 | new_str(s) | |
1226 | char *s; | |
1227 | { | |
1228 | register struct enode *p; | |
1229 | ||
1230 | p = (struct enode *) xmalloc ((unsigned)sizeof(struct enode)); | |
1231 | p->op = O_SCONST; | |
1232 | p->e.s = s; | |
1233 | return(p); | |
1234 | } | |
1235 | ||
1236 | void | |
1237 | copy(dv1, dv2, v1, v2) | |
1238 | struct ent *dv1, *dv2, *v1, *v2; | |
1239 | { | |
1240 | int minsr, minsc; | |
1241 | int maxsr, maxsc; | |
1242 | int mindr, mindc; | |
1243 | int maxdr, maxdc; | |
1244 | int vr, vc; | |
1245 | int r, c; | |
1246 | ||
1247 | mindr = dv1->row; | |
1248 | mindc = dv1->col; | |
1249 | maxdr = dv2->row; | |
1250 | maxdc = dv2->col; | |
1251 | if (mindr>maxdr) r = maxdr, maxdr = mindr, mindr = r; | |
1252 | if (mindc>maxdc) c = maxdc, maxdc = mindc, mindc = c; | |
1253 | maxsr = v2->row; | |
1254 | maxsc = v2->col; | |
1255 | minsr = v1->row; | |
1256 | minsc = v1->col; | |
1257 | if (minsr>maxsr) r = maxsr, maxsr = minsr, minsr = r; | |
1258 | if (minsc>maxsc) c = maxsc, maxsc = minsc, minsc = c; | |
1259 | checkbounds(&maxdr, &maxdc); | |
1260 | ||
1261 | erase_area(mindr, mindc, maxdr, maxdc); | |
1262 | if (minsr == maxsr && minsc == maxsc) { | |
1263 | /* Source is a single cell */ | |
1264 | for(vr = mindr; vr <= maxdr; vr++) | |
1265 | for (vc = mindc; vc <= maxdc; vc++) | |
1266 | copyrtv(vr, vc, minsr, minsc, maxsr, maxsc); | |
1267 | } else if (minsr == maxsr) { | |
1268 | /* Source is a single row */ | |
1269 | for (vr = mindr; vr <= maxdr; vr++) | |
1270 | copyrtv(vr, mindc, minsr, minsc, maxsr, maxsc); | |
1271 | } else if (minsc == maxsc) { | |
1272 | /* Source is a single column */ | |
1273 | for (vc = mindc; vc <= maxdc; vc++) | |
1274 | copyrtv(mindr, vc, minsr, minsc, maxsr, maxsc); | |
1275 | } else { | |
1276 | /* Everything else */ | |
1277 | copyrtv(mindr, mindc, minsr, minsc, maxsr, maxsc); | |
1278 | } | |
1279 | sync_refs(); | |
1280 | } | |
1281 | ||
1282 | void | |
1283 | copyrtv(vr, vc, minsr, minsc, maxsr, maxsc) | |
1284 | int vr, vc, minsr, minsc, maxsr, maxsc; | |
1285 | { | |
1286 | register struct ent *p; | |
1287 | register struct ent *n; | |
1288 | register int sr, sc; | |
1289 | register int dr, dc; | |
1290 | ||
1291 | for (dr=vr, sr=minsr; sr<=maxsr; sr++, dr++) | |
1292 | for (dc=vc, sc=minsc; sc<=maxsc; sc++, dc++) { | |
1293 | if (p = *ATBL(tbl, sr, sc)) | |
1294 | { n = lookat (dr, dc); | |
1295 | (void) clearent(n); | |
1296 | copyent( n, p, dr - sr, dc - sc); | |
1297 | } | |
1298 | else | |
1299 | if (n = *ATBL(tbl, dr, dc)) | |
1300 | (void) clearent(n); | |
1301 | } | |
1302 | } | |
1303 | ||
1304 | void | |
1305 | eraser(v1, v2) | |
1306 | struct ent *v1, *v2; | |
1307 | { | |
1308 | FullUpdate++; | |
1309 | flush_saved(); | |
1310 | erase_area(v1->row, v1->col, v2->row, v2->col); | |
1311 | sync_refs(); | |
1312 | } | |
1313 | ||
1314 | /* Goto subroutines */ | |
1315 | ||
1316 | void | |
1317 | g_free() | |
1318 | { | |
1319 | switch (gs.g_type) { | |
1320 | case G_STR: xfree(gs.g_s); break; | |
1321 | default: break; | |
1322 | } | |
1323 | gs.g_type = G_NONE; | |
1324 | } | |
1325 | ||
1326 | void | |
1327 | go_last() | |
1328 | { | |
1329 | switch (gs.g_type) { | |
1330 | case G_NONE: | |
1331 | error("Nothing to repeat"); break; | |
1332 | case G_NUM: | |
1333 | num_search(gs.g_n); | |
1334 | break; | |
1335 | case G_CELL: | |
1336 | moveto(gs.g_row, gs.g_col); | |
1337 | break; | |
1338 | case G_STR: | |
1339 | gs.g_type = G_NONE; /* Don't free the string */ | |
1340 | str_search(gs.g_s); | |
1341 | break; | |
1342 | ||
1343 | default: error("go_last: internal error"); | |
1344 | } | |
1345 | } | |
1346 | ||
1347 | void | |
1348 | moveto(row, col) | |
1349 | int row, col; | |
1350 | { | |
1351 | currow = row; | |
1352 | curcol = col; | |
1353 | g_free(); | |
1354 | gs.g_type = G_CELL; | |
1355 | gs.g_row = currow; | |
1356 | gs.g_col = curcol; | |
1357 | } | |
1358 | ||
1359 | void | |
1360 | num_search(n) | |
1361 | double n; | |
1362 | { | |
1363 | register struct ent *p; | |
1364 | register int r,c; | |
1365 | int endr, endc; | |
1366 | ||
1367 | g_free(); | |
1368 | gs.g_type = G_NUM; | |
1369 | gs.g_n = n; | |
1370 | ||
1371 | if (currow > maxrow) | |
1372 | endr = maxrow ? maxrow-1 : 0; | |
1373 | else | |
1374 | endr = currow; | |
1375 | if (curcol > maxcol) | |
1376 | endc = maxcol ? maxcol-1 : 0; | |
1377 | else | |
1378 | endc = curcol; | |
1379 | r = endr; | |
1380 | c = endc; | |
1381 | do { | |
1382 | if (c < maxcol) | |
1383 | c++; | |
1384 | else { | |
1385 | if (r < maxrow) { | |
1386 | while(++r < maxrow && row_hidden[r]) /* */; | |
1387 | c = 0; | |
1388 | } else { | |
1389 | r = 0; | |
1390 | c = 0; | |
1391 | } | |
1392 | } | |
1393 | if (r == endr && c == endc) { | |
1394 | error("Number not found"); | |
1395 | return; | |
1396 | } | |
1397 | p = *ATBL(tbl, r, c); | |
1398 | } while(col_hidden[c] || !p || p && (!(p->flags & is_valid) | |
1399 | || (p->flags&is_valid) && p->v != n)); | |
1400 | currow = r; | |
1401 | curcol = c; | |
1402 | } | |
1403 | ||
1404 | void | |
1405 | str_search(s) | |
1406 | char *s; | |
1407 | { | |
1408 | register struct ent *p; | |
1409 | register int r,c; | |
1410 | int endr, endc; | |
1411 | char *tmp; | |
1412 | ||
1413 | #if defined(BSD42) || defined(BSD43) | |
1414 | if ((tmp = re_comp(s)) != (char *)0) { | |
1415 | xfree(s); | |
1416 | error(tmp); | |
1417 | return; | |
1418 | } | |
1419 | #endif | |
1420 | #if defined(SYSV2) || defined(SYSV3) | |
1421 | if ((tmp = regcmp(s, (char *)0)) == (char *)0) { | |
1422 | xfree(s); | |
1423 | error("Invalid search string"); | |
1424 | return; | |
1425 | } | |
1426 | #endif | |
1427 | g_free(); | |
1428 | gs.g_type = G_STR; | |
1429 | gs.g_s = s; | |
1430 | if (currow > maxrow) | |
1431 | endr = maxrow ? maxrow-1 : 0; | |
1432 | else | |
1433 | endr = currow; | |
1434 | if (curcol > maxcol) | |
1435 | endc = maxcol ? maxcol-1 : 0; | |
1436 | else | |
1437 | endc = curcol; | |
1438 | r = endr; | |
1439 | c = endc; | |
1440 | do { | |
1441 | if (c < maxcol) | |
1442 | c++; | |
1443 | else { | |
1444 | if (r < maxrow) { | |
1445 | while(++r < maxrow && row_hidden[r]) /* */; | |
1446 | c = 0; | |
1447 | } else { | |
1448 | r = 0; | |
1449 | c = 0; | |
1450 | } | |
1451 | } | |
1452 | if (r == endr && c == endc) { | |
1453 | error("String not found"); | |
1454 | #if defined(SYSV2) || defined(SYSV3) | |
1455 | free(tmp); | |
1456 | #endif | |
1457 | return; | |
1458 | } | |
1459 | p = *ATBL(tbl, r, c); | |
1460 | } while(col_hidden[c] || !p || p && (!(p->label) | |
1461 | #if defined(BSD42) || defined(BSD43) | |
1462 | || (re_exec(p->label) == 0))); | |
1463 | #else | |
1464 | #if defined(SYSV2) || defined(SYSV3) | |
1465 | || (regex(tmp, p->label) == (char *)0))); | |
1466 | #else | |
1467 | || (strcmp(s, p->label) != 0))); | |
1468 | #endif | |
1469 | #endif | |
1470 | currow = r; | |
1471 | curcol = c; | |
1472 | #if defined(SYSV2) || defined(SYSV3) | |
1473 | free(tmp); | |
1474 | #endif | |
1475 | } | |
1476 | ||
1477 | void | |
1478 | fill (v1, v2, start, inc) | |
1479 | struct ent *v1, *v2; | |
1480 | double start, inc; | |
1481 | { | |
1482 | register r,c; | |
1483 | register struct ent *n; | |
1484 | int maxr, maxc; | |
1485 | int minr, minc; | |
1486 | ||
1487 | maxr = v2->row; | |
1488 | maxc = v2->col; | |
1489 | minr = v1->row; | |
1490 | minc = v1->col; | |
1491 | if (minr>maxr) r = maxr, maxr = minr, minr = r; | |
1492 | if (minc>maxc) c = maxc, maxc = minc, minc = c; | |
1493 | checkbounds(&maxr, &maxc); | |
1494 | if (minr < 0) minr = 0; | |
1495 | if (minr < 0) minr = 0; | |
1496 | ||
1497 | FullUpdate++; | |
1498 | if( calc_order == BYROWS ) { | |
1499 | for (r = minr; r<=maxr; r++) | |
1500 | for (c = minc; c<=maxc; c++) { | |
1501 | n = lookat (r, c); | |
1502 | (void) clearent(n); | |
1503 | n->v = start; | |
1504 | start += inc; | |
1505 | n->flags |= (is_changed|is_valid); | |
1506 | } | |
1507 | } | |
1508 | else if ( calc_order == BYCOLS ) { | |
1509 | for (c = minc; c<=maxc; c++) | |
1510 | for (r = minr; r<=maxr; r++) { | |
1511 | n = lookat (r, c); | |
1512 | (void) clearent(n); | |
1513 | n->v = start; | |
1514 | start += inc; | |
1515 | n->flags |= (is_changed|is_valid); | |
1516 | } | |
1517 | } | |
1518 | else error(" Internal error calc_order"); | |
1519 | changed++; | |
1520 | } | |
1521 | ||
1522 | void | |
1523 | let (v, e) | |
1524 | struct ent *v; | |
1525 | struct enode *e; | |
1526 | { | |
1527 | double val; | |
1528 | ||
1529 | exprerr = 0; | |
1530 | (void) signal(SIGFPE, eval_fpe); | |
1531 | if (setjmp(fpe_save)) { | |
1532 | error ("Floating point exception in cell %s", v_name(v->row, v->col)); | |
1533 | val = (double)0.0; | |
1534 | } else { | |
1535 | val = eval(e); | |
1536 | } | |
1537 | (void) signal(SIGFPE, quit); | |
1538 | if (exprerr) { | |
1539 | efree((struct ent *)0, e); | |
1540 | return; | |
1541 | } | |
1542 | if (constant(e)) { | |
1543 | if (!loading) | |
1544 | v->v = val * prescale; | |
1545 | else | |
1546 | v->v = val; | |
1547 | if (!(v->flags & is_strexpr)) { | |
1548 | efree(v, v->expr); | |
1549 | v->expr = (struct enode *)0; | |
1550 | } | |
1551 | efree((struct ent *)0, e); | |
1552 | v->flags |= (is_changed|is_valid); | |
1553 | changed++; | |
1554 | modflg++; | |
1555 | return; | |
1556 | } | |
1557 | efree (v, v->expr); | |
1558 | v->expr = e; | |
1559 | v->flags |= (is_changed|is_valid); | |
1560 | v->flags &= ~is_strexpr; | |
1561 | ||
1562 | #ifdef EXPRTREE | |
1563 | totoptree(v); | |
1564 | #endif | |
1565 | changed++; | |
1566 | modflg++; | |
1567 | } | |
1568 | ||
1569 | void | |
1570 | slet (v, se, flushdir) | |
1571 | struct ent *v; | |
1572 | struct enode *se; | |
1573 | int flushdir; | |
1574 | { | |
1575 | char *p; | |
1576 | ||
1577 | exprerr = 0; | |
1578 | (void) signal(SIGFPE, eval_fpe); | |
1579 | if (setjmp(fpe_save)) { | |
1580 | error ("Floating point exception in cell %s", v_name(v->row, v->col)); | |
1581 | p = ""; | |
1582 | } else { | |
1583 | p = seval(se); | |
1584 | } | |
1585 | (void) signal(SIGFPE, quit); | |
1586 | if (exprerr) { | |
1587 | efree((struct ent *)0, se); | |
1588 | return; | |
1589 | } | |
1590 | if (constant(se)) { | |
1591 | label(v, p, flushdir); | |
1592 | if (p) | |
1593 | xfree(p); | |
1594 | efree((struct ent *)0, se); | |
1595 | if (v->flags & is_strexpr) { | |
1596 | efree (v, v->expr); | |
1597 | v->expr = (struct enode *)0; | |
1598 | v->flags &= ~is_strexpr; | |
1599 | } | |
1600 | return; | |
1601 | } | |
1602 | efree (v, v->expr); | |
1603 | v->expr = se; | |
1604 | v->flags |= (is_changed|is_strexpr); | |
1605 | if (flushdir<0) v->flags |= is_leftflush; | |
1606 | else v->flags &= ~is_leftflush; | |
1607 | ||
1608 | #ifdef EXPRTREE | |
1609 | totoptree(); | |
1610 | #endif | |
1611 | FullUpdate++; | |
1612 | changed++; | |
1613 | modflg++; | |
1614 | } | |
1615 | ||
1616 | #ifdef EXPRTREE | |
1617 | /* | |
1618 | * put an expression in the expression tree, only the top of each branch is | |
1619 | * in the firstev list | |
1620 | */ | |
1621 | totoptree(v) | |
1622 | struct ent *v; | |
1623 | { | |
1624 | int right; | |
1625 | int left; | |
1626 | if (!v->expr) | |
1627 | return; | |
1628 | ||
1629 | #ifdef notdef | |
1630 | right = FALSE; | |
1631 | left = FALSE; | |
1632 | switch(v->expr->op) | |
1633 | { | |
1634 | /* no real expression */ | |
1635 | case 'v': | |
1636 | if (v->expr->o.v->evnext) | |
1637 | evdel(v->expr->o.v); | |
1638 | case 'k': | |
1639 | case LMAX: | |
1640 | case LMIN: | |
1641 | case NOW: | |
1642 | case O_SCONST: | |
1643 | case O_VAR: | |
1644 | default: | |
1645 | return; | |
1646 | ||
1647 | /* left && right */ | |
1648 | case '#': | |
1649 | case '%': | |
1650 | case '&': | |
1651 | case '*': | |
1652 | case '+': | |
1653 | case '-': | |
1654 | case '/': | |
1655 | case '<': | |
1656 | case '=': | |
1657 | case '>': | |
1658 | case '?': | |
1659 | case '^': | |
1660 | case '|': | |
1661 | case ATAN2: | |
1662 | case DTS: | |
1663 | case EQS: | |
1664 | case EXT: | |
1665 | case FMT: | |
1666 | case FV: | |
1667 | case HYPOT: | |
1668 | case IF: | |
1669 | case NVAL: | |
1670 | case PMT: | |
1671 | case POW: | |
1672 | case PV: | |
1673 | case REDUCE | '*': | |
1674 | case REDUCE | '+': | |
1675 | case REDUCE | 'a': | |
1676 | case REDUCE | 'c': | |
1677 | case REDUCE | 's': | |
1678 | case REDUCE | MAX: | |
1679 | case REDUCE | MIN: | |
1680 | case ROUND: | |
1681 | case STINDEX: | |
1682 | case SUBSTR: | |
1683 | case SVAL: | |
1684 | case TTS: | |
1685 | left = right = TRUE; | |
1686 | break; | |
1687 | /* right only */ | |
1688 | case 'f': | |
1689 | case 'm': | |
1690 | case '~': | |
1691 | case ABS: | |
1692 | case ACOS: | |
1693 | case ASIN: | |
1694 | case ATAN: | |
1695 | case CEIL: | |
1696 | case COS: | |
1697 | case DATE: | |
1698 | case DAY: | |
1699 | case DTR: | |
1700 | case EXP: | |
1701 | case FABS: | |
1702 | case FLOOR: | |
1703 | case HLOOKUP: | |
1704 | case HOUR: | |
1705 | case IF: | |
1706 | case INDEX: | |
1707 | case LOG10: | |
1708 | case LOG: | |
1709 | case LOOKUP: | |
1710 | case MINUTE: | |
1711 | case MONTH: | |
1712 | case RND: | |
1713 | case RTD: | |
1714 | case SECOND: | |
1715 | case SIN: | |
1716 | case SQRT: | |
1717 | case STON: | |
1718 | case TAN: | |
1719 | case VLOOKUP: | |
1720 | case YEAR: | |
1721 | right = TRUE; | |
1722 | break; | |
1723 | } | |
1724 | /* for now insert at the beginning of the list */ | |
1725 | v->evnext = firstev; | |
1726 | v->evprev = (struct ent *)0; | |
1727 | if (firstev) | |
1728 | firstev->evprev = v; | |
1729 | firstev = v; | |
1730 | #endif | |
1731 | firstev = v; | |
1732 | } | |
1733 | #endif /* EXPRTREE*/ | |
1734 | ||
1735 | void | |
1736 | hide_row(arg) | |
1737 | int arg; | |
1738 | { | |
1739 | if (arg < 0) { | |
1740 | error("Invalid Range"); | |
1741 | return; | |
1742 | } | |
1743 | if (arg >= maxrows-1) | |
1744 | { | |
1745 | if (!growtbl(GROWROW, arg+1, 0)) | |
1746 | { error("You can't hide the last row"); | |
1747 | return; | |
1748 | } | |
1749 | } | |
1750 | FullUpdate++; | |
1751 | row_hidden[arg] = 1; | |
1752 | } | |
1753 | ||
1754 | void | |
1755 | hide_col(arg) | |
1756 | int arg; | |
1757 | { | |
1758 | if (arg < 0) { | |
1759 | error("Invalid Range"); | |
1760 | return; | |
1761 | } | |
1762 | if (arg >= maxcols-1) | |
1763 | { if ((arg >= ABSMAXCOLS-1) || !growtbl(GROWCOL, 0, arg+1)) | |
1764 | { error("You can't hide the last col"); | |
1765 | return; | |
1766 | } | |
1767 | } | |
1768 | FullUpdate++; | |
1769 | col_hidden[arg] = 1; | |
1770 | } | |
1771 | ||
1772 | void | |
1773 | clearent (v) | |
1774 | struct ent *v; | |
1775 | { | |
1776 | if (!v) | |
1777 | return; | |
1778 | label(v,"",-1); | |
1779 | v->v = (double)0; | |
1780 | if (v->expr) | |
1781 | efree(v, v->expr); | |
1782 | v->expr = (struct enode *)0; | |
1783 | v->flags |= (is_changed); | |
1784 | v->flags &= ~(is_valid); | |
1785 | changed++; | |
1786 | modflg++; | |
1787 | } | |
1788 | ||
1789 | /* | |
1790 | * Say if an expression is a constant (return 1) or not. | |
1791 | */ | |
1792 | int | |
1793 | constant (e) | |
1794 | register struct enode *e; | |
1795 | { | |
1796 | return ((e == (struct enode *)0) | |
1797 | || ((e -> op) == O_CONST) | |
1798 | || ((e -> op) == O_SCONST) | |
1799 | || (((e -> op) != O_VAR) | |
1800 | && (((e -> op) & REDUCE) != REDUCE) | |
1801 | && constant (e -> e.o.left) | |
1802 | && constant (e -> e.o.right) | |
1803 | && (e -> op != EXT) /* functions look like constants but aren't */ | |
1804 | && (e -> op != NVAL) | |
1805 | && (e -> op != SVAL) | |
1806 | && (e -> op != NOW))); | |
1807 | } | |
1808 | ||
1809 | void | |
1810 | efree (v, e) | |
1811 | struct ent *v; | |
1812 | struct enode *e; | |
1813 | { | |
1814 | if (e) { | |
1815 | if (e->op != O_VAR && e->op !=O_CONST && e->op != O_SCONST | |
1816 | && (e->op & REDUCE) != REDUCE) { | |
1817 | efree(v, e->e.o.left); | |
1818 | efree(v, e->e.o.right); | |
1819 | } | |
1820 | if (e->op == O_SCONST && e->e.s) | |
1821 | xfree(e->e.s); | |
1822 | xfree ((char *)e); | |
1823 | ||
1824 | #ifdef EXPRTREE | |
1825 | /* delete this cell from the eval list */ | |
1826 | if (v) | |
1827 | { if (v->evprev) | |
1828 | v->evprev->evnext = v->evnext; | |
1829 | if (v->evnext) | |
1830 | v->evnext->evprev = v->evprev; | |
1831 | } | |
1832 | #endif /* EXPRTREE */ | |
1833 | } | |
1834 | } | |
1835 | ||
1836 | void | |
1837 | label (v, s, flushdir) | |
1838 | register struct ent *v; | |
1839 | register char *s; | |
1840 | int flushdir; | |
1841 | { | |
1842 | if (v) { | |
1843 | if (flushdir==0 && v->flags&is_valid) { | |
1844 | register struct ent *tv; | |
1845 | if (v->col>0 && ((tv=lookat(v->row,v->col-1))->flags&is_valid)==0) | |
1846 | v = tv, flushdir = 1; | |
1847 | else if (((tv=lookat (v->row,v->col+1))->flags&is_valid)==0) | |
1848 | v = tv, flushdir = -1; | |
1849 | else flushdir = -1; | |
1850 | } | |
1851 | if (v->label) xfree((char *)(v->label)); | |
1852 | if (s && s[0]) { | |
1853 | v->label = xmalloc ((unsigned)(strlen(s)+1)); | |
1854 | (void) strcpy (v->label, s); | |
1855 | } else | |
1856 | v->label = (char *)0; | |
1857 | if (flushdir<0) v->flags |= is_leftflush; | |
1858 | else v->flags &= ~is_leftflush; | |
1859 | FullUpdate++; | |
1860 | modflg++; | |
1861 | } | |
1862 | } | |
1863 | ||
1864 | void | |
1865 | decodev (v) | |
1866 | struct ent_ptr v; | |
1867 | { | |
1868 | register struct range *r; | |
1869 | ||
1870 | if (!v.vp) (void)sprintf (line+linelim,"VAR?"); | |
1871 | else if ((r = find_range((char *)0, 0, v.vp, v.vp)) && !r->r_is_range) | |
1872 | (void)sprintf(line+linelim, "%s", r->r_name); | |
1873 | else | |
1874 | (void)sprintf (line+linelim, "%s%s%s%d", | |
1875 | v.vf & FIX_COL ? "$" : "", | |
1876 | coltoa(v.vp->col), | |
1877 | v.vf & FIX_ROW ? "$" : "", | |
1878 | v.vp->row); | |
1879 | linelim += strlen (line+linelim); | |
1880 | } | |
1881 | ||
1882 | char * | |
1883 | coltoa(col) | |
1884 | int col; | |
1885 | { | |
1886 | static char rname[3]; | |
1887 | register char *p = rname; | |
1888 | ||
1889 | if (col > 25) { | |
1890 | *p++ = col/26 + 'A' - 1; | |
1891 | col %= 26; | |
1892 | } | |
1893 | *p++ = col+'A'; | |
1894 | *p = '\0'; | |
1895 | return(rname); | |
1896 | } | |
1897 | ||
1898 | /* | |
1899 | * To make list elements come out in the same order | |
1900 | * they were entered, we must do a depth-first eval | |
1901 | * of the ELIST tree | |
1902 | */ | |
1903 | static void | |
1904 | decompile_list(p) | |
1905 | struct enode *p; | |
1906 | { | |
1907 | if (!p) return; | |
1908 | decompile_list(p->e.o.left); /* depth first */ | |
1909 | decompile(p->e.o.right, 0); | |
1910 | line[linelim++] = ','; | |
1911 | } | |
1912 | ||
1913 | void | |
1914 | decompile(e, priority) | |
1915 | register struct enode *e; | |
1916 | int priority; | |
1917 | { | |
1918 | register char *s; | |
1919 | if (e) { | |
1920 | int mypriority; | |
1921 | switch (e->op) { | |
1922 | default: mypriority = 99; break; | |
1923 | case '?': mypriority = 1; break; | |
1924 | case ':': mypriority = 2; break; | |
1925 | case '|': mypriority = 3; break; | |
1926 | case '&': mypriority = 4; break; | |
1927 | case '<': case '=': case '>': mypriority = 6; break; | |
1928 | case '+': case '-': case '#': mypriority = 8; break; | |
1929 | case '*': case '/': case '%': mypriority = 10; break; | |
1930 | case '^': mypriority = 12; break; | |
1931 | } | |
1932 | if (mypriority<priority) line[linelim++] = '('; | |
1933 | switch (e->op) { | |
1934 | case 'f': for (s="fixed "; line[linelim++] = *s++;); | |
1935 | linelim--; | |
1936 | decompile (e->e.o.right, 30); | |
1937 | break; | |
1938 | case 'm': line[linelim++] = '-'; | |
1939 | decompile (e->e.o.right, 30); | |
1940 | break; | |
1941 | case '~': line[linelim++] = '~'; | |
1942 | decompile (e->e.o.right, 30); | |
1943 | break; | |
1944 | case 'v': decodev (e->e.v); | |
1945 | break; | |
1946 | case 'k': (void)sprintf (line+linelim,"%.15g",e->e.k); | |
1947 | linelim += strlen (line+linelim); | |
1948 | break; | |
1949 | case '$': (void)sprintf (line+linelim, "\"%s\"", e->e.s); | |
1950 | linelim += strlen(line+linelim); | |
1951 | break; | |
1952 | ||
1953 | case REDUCE | '+': range_arg( "@sum(", e); break; | |
1954 | case REDUCE | '*': range_arg( "@prod(", e); break; | |
1955 | case REDUCE | 'a': range_arg( "@avg(", e); break; | |
1956 | case REDUCE | 'c': range_arg( "@count(", e); break; | |
1957 | case REDUCE | 's': range_arg( "@stddev(", e); break; | |
1958 | case REDUCE | MAX: range_arg( "@max(", e); break; | |
1959 | case REDUCE | MIN: range_arg( "@min(", e); break; | |
1960 | ||
1961 | case ABS: one_arg( "@abs(", e); break; | |
1962 | case ACOS: one_arg( "@acos(", e); break; | |
1963 | case ASIN: one_arg( "@asin(", e); break; | |
1964 | case ATAN: one_arg( "@atan(", e); break; | |
1965 | case ATAN2: two_arg( "@atan2(", e); break; | |
1966 | case CEIL: one_arg( "@ceil(", e); break; | |
1967 | case COS: one_arg( "@cos(", e); break; | |
1968 | case EXP: one_arg( "@exp(", e); break; | |
1969 | case FABS: one_arg( "@fabs(", e); break; | |
1970 | case FLOOR: one_arg( "@floor(", e); break; | |
1971 | case HYPOT: two_arg( "@hypot(", e); break; | |
1972 | case LOG: one_arg( "@ln(", e); break; | |
1973 | case LOG10: one_arg( "@log(", e); break; | |
1974 | case POW: two_arg( "@pow(", e); break; | |
1975 | case SIN: one_arg( "@sin(", e); break; | |
1976 | case SQRT: one_arg( "@sqrt(", e); break; | |
1977 | case TAN: one_arg( "@tan(", e); break; | |
1978 | case DTR: one_arg( "@dtr(", e); break; | |
1979 | case RTD: one_arg( "@rtd(", e); break; | |
1980 | case RND: one_arg( "@rnd(", e); break; | |
1981 | case ROUND: two_arg( "@round(", e); break; | |
1982 | case HOUR: one_arg( "@hour(", e); break; | |
1983 | case MINUTE: one_arg( "@minute(", e); break; | |
1984 | case SECOND: one_arg( "@second(", e); break; | |
1985 | case MONTH: one_arg( "@month(", e); break; | |
1986 | case DAY: one_arg( "@day(", e); break; | |
1987 | case YEAR: one_arg( "@year(", e); break; | |
1988 | case DATE: one_arg( "@date(", e); break; | |
1989 | case DTS: three_arg( "@dts(", e); break; | |
1990 | case TTS: three_arg( "@tts(", e); break; | |
1991 | case STON: one_arg( "@ston(", e); break; | |
1992 | case FMT: two_arg( "@fmt(", e); break; | |
1993 | case EQS: two_arg( "@eqs(", e); break; | |
1994 | case NOW: for ( s = "@now"; line[linelim++] = *s++;); | |
1995 | linelim--; | |
1996 | break; | |
1997 | case LMAX: list_arg("@max(", e); break; | |
1998 | case LMIN: list_arg("@min(", e); break; | |
1999 | case FV: three_arg("@fv(", e); break; | |
2000 | case PV: three_arg("@pv(", e); break; | |
2001 | case PMT: three_arg("@pmt(", e); break; | |
2002 | case NVAL: two_arg("@nval(", e); break; | |
2003 | case SVAL: two_arg("@sval(", e); break; | |
2004 | case EXT: two_arg("@ext(", e); break; | |
2005 | case SUBSTR: three_arg("@substr(", e); break; | |
2006 | case STINDEX: index_arg("@stindex(", e); break; | |
2007 | case INDEX: index_arg("@index(", e); break; | |
2008 | case LOOKUP: index_arg("@lookup(", e); break; | |
2009 | case HLOOKUP: two_arg_index("@hlookup(", e); break; | |
2010 | case VLOOKUP: two_arg_index("@vlookup(", e); break; | |
2011 | case IF: three_arg("@if(", e); break; | |
2012 | default: decompile (e->e.o.left, mypriority); | |
2013 | line[linelim++] = e->op; | |
2014 | decompile (e->e.o.right, mypriority+1); | |
2015 | break; | |
2016 | } | |
2017 | if (mypriority<priority) line[linelim++] = ')'; | |
2018 | } else line[linelim++] = '?'; | |
2019 | } | |
2020 | ||
2021 | void | |
2022 | index_arg(s, e) | |
2023 | char *s; | |
2024 | struct enode *e; | |
2025 | { | |
2026 | for (; line[linelim++] = *s++;); | |
2027 | linelim--; | |
2028 | decompile( e-> e.o.left, 0 ); | |
2029 | range_arg(", ", e->e.o.right); | |
2030 | } | |
2031 | ||
2032 | void | |
2033 | two_arg_index(s, e) | |
2034 | char *s; | |
2035 | struct enode *e; | |
2036 | { | |
2037 | for (; line[linelim++] = *s++;); | |
2038 | linelim--; | |
2039 | decompile( e->e.o.left->e.o.left, 0 ); | |
2040 | range_arg(",", e->e.o.right); | |
2041 | linelim--; | |
2042 | line[linelim++] = ','; | |
2043 | decompile( e->e.o.left->e.o.right, 0 ); | |
2044 | line[linelim++] = ')'; | |
2045 | } | |
2046 | ||
2047 | void | |
2048 | list_arg(s, e) | |
2049 | char *s; | |
2050 | struct enode *e; | |
2051 | { | |
2052 | for (; line[linelim++] = *s++;); | |
2053 | linelim--; | |
2054 | ||
2055 | decompile (e->e.o.right, 0); | |
2056 | line[linelim++] = ','; | |
2057 | decompile_list(e->e.o.left); | |
2058 | line[linelim - 1] = ')'; | |
2059 | } | |
2060 | ||
2061 | void | |
2062 | one_arg(s, e) | |
2063 | char *s; | |
2064 | struct enode *e; | |
2065 | { | |
2066 | for (; line[linelim++] = *s++;); | |
2067 | linelim--; | |
2068 | decompile (e->e.o.right, 0); | |
2069 | line[linelim++] = ')'; | |
2070 | } | |
2071 | ||
2072 | void | |
2073 | two_arg(s,e) | |
2074 | char *s; | |
2075 | struct enode *e; | |
2076 | { | |
2077 | for (; line[linelim++] = *s++;); | |
2078 | linelim--; | |
2079 | decompile (e->e.o.left, 0); | |
2080 | line[linelim++] = ','; | |
2081 | decompile (e->e.o.right, 0); | |
2082 | line[linelim++] = ')'; | |
2083 | } | |
2084 | ||
2085 | void | |
2086 | three_arg(s,e) | |
2087 | char *s; | |
2088 | struct enode *e; | |
2089 | { | |
2090 | for (; line[linelim++] = *s++;); | |
2091 | linelim--; | |
2092 | decompile (e->e.o.left, 0); | |
2093 | line[linelim++] = ','; | |
2094 | decompile (e->e.o.right->e.o.left, 0); | |
2095 | line[linelim++] = ','; | |
2096 | decompile (e->e.o.right->e.o.right, 0); | |
2097 | line[linelim++] = ')'; | |
2098 | } | |
2099 | ||
2100 | void | |
2101 | range_arg(s,e) | |
2102 | char *s; | |
2103 | struct enode *e; | |
2104 | { | |
2105 | struct range *r; | |
2106 | ||
2107 | for (; line[linelim++] = *s++;); | |
2108 | linelim--; | |
2109 | if ((r = find_range((char *)0, 0, e->e.r.left.vp, | |
2110 | e->e.r.right.vp)) && r->r_is_range) { | |
2111 | (void)sprintf(line+linelim, "%s", r->r_name); | |
2112 | linelim += strlen(line+linelim); | |
2113 | } else { | |
2114 | decodev (e->e.r.left); | |
2115 | line[linelim++] = ':'; | |
2116 | decodev (e->e.r.right); | |
2117 | } | |
2118 | line[linelim++] = ')'; | |
2119 | } | |
2120 | ||
2121 | void | |
2122 | editv (row, col) | |
2123 | int row, col; | |
2124 | { | |
2125 | register struct ent *p; | |
2126 | ||
2127 | p = lookat (row, col); | |
2128 | (void)sprintf (line, "let %s = ", v_name(row, col)); | |
2129 | linelim = strlen(line); | |
2130 | if (p->flags & is_strexpr || p->expr == 0) { | |
2131 | (void)sprintf (line+linelim, "%.15g", p->v); | |
2132 | linelim += strlen (line+linelim); | |
2133 | } else { | |
2134 | editexp(row,col); | |
2135 | } | |
2136 | } | |
2137 | ||
2138 | void | |
2139 | editexp(row,col) | |
2140 | int row, col; | |
2141 | { | |
2142 | register struct ent *p; | |
2143 | ||
2144 | p = lookat (row, col); | |
2145 | decompile (p->expr, 0); | |
2146 | line[linelim] = '\0'; | |
2147 | } | |
2148 | ||
2149 | void | |
2150 | edits (row, col) | |
2151 | int row, col; | |
2152 | { | |
2153 | register struct ent *p; | |
2154 | ||
2155 | p = lookat (row, col); | |
2156 | (void)sprintf (line, "%sstring %s = ", | |
2157 | ((p->flags&is_leftflush) ? "left" : "right"), | |
2158 | v_name(row, col)); | |
2159 | linelim = strlen(line); | |
2160 | if (p->flags & is_strexpr && p->expr) { | |
2161 | editexp(row, col); | |
2162 | } else if (p->label) { | |
2163 | (void)sprintf (line+linelim, "\"%s\"", p->label); | |
2164 | linelim += strlen (line+linelim); | |
2165 | } else { | |
2166 | (void)sprintf (line+linelim, "\""); | |
2167 | linelim += 1; | |
2168 | } | |
2169 | } |