date and time created 91/03/03 00:38:20 by donn
[unix-history] / usr / src / contrib / sc / sc.c
CommitLineData
8c2a3498
KB
1/* SC A Spreadsheet Calculator
2 * Main driver
3 *
4 * original by James Gosling, September 1982
5 * modifications 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 * Currently supported by pur-phy!sawmill!buhrt (Jeff Buhrt)
11 * $Revision: 6.8 $
12 *
13 */
14
15
16#include <signal.h>
17#include <curses.h>
18#include <ctype.h>
19
20#ifdef BSD42
21#include <strings.h>
22#else
23#ifndef SYSIII
24#include <string.h>
25#endif
26#endif
27
28#include <stdio.h>
29#include "sc.h"
30
31char *getenv();
32
33#ifdef SYSV3
34void exit();
35#endif
36
37#ifndef DFLT_PAGER
38#define DFLT_PAGER "more" /* more is probably more widespread than less */
39#endif /* DFLT_PAGER */
40
41#define MAXCMD 160 /* for ! command below */
42
43extern char *rev;
44
45/* Globals defined in sc.h */
46
47struct ent ***tbl;
48int strow, stcol;
49int currow, curcol;
50int savedrow, savedcol;
51int FullUpdate;
52int maxrow, maxcol;
53int maxrows, maxcols;
54int *fwidth;
55int *precision;
56char *col_hidden;
57char *row_hidden;
58char line[FBUFLEN];
59int changed;
60struct ent *to_fix;
61int modflg;
62int numeric;
63char *mdir;
64int showsc, showsr; /* Starting cell for highlighted range */
65char mode_ind = '.';
66
67char curfile[PATHLEN];
68char revmsg[80];
69
70int linelim = -1;
71
72int showtop = 1; /* Causes current cell value display in top line */
73int showcell = 1; /* Causes current cell to be highlighted */
74int showrange = 0; /* Causes ranges to be highlighted */
75int showneed = 0; /* Causes cells needing values to be highlighted */
76int showexpr = 0; /* Causes cell exprs to be displayed, highlighted */
77
78int autocalc = 1 ; /* 1 to calculate after each update */
79int calc_order = BYROWS;
80int tbl_style = 0; /* headers for T command output */
81
82int lastmx, lastmy; /* Screen address of the cursor */
83int lastcol; /* Spreadsheet Column the cursor was in last */
84char under_cursor[] = " "; /* Data under the < cursor */
85
86#ifdef VMS
87int VMS_read_raw = 0;
88#endif
89
90int seenerr;
91
92void
93yyerror(err)
94char *err; {
95 if (seenerr) return;
96 seenerr++;
97 (void) move(1,0);
98 (void) clrtoeol();
99 (void) printw("%s: %.*s<=%s",err,linelim,line,line+linelim);
100}
101
102struct ent *
103lookat(row,col)
104int row, col;
105{
106 register struct ent **pp;
107
108 checkbounds(&row, &col);
109 pp = ATBL(tbl, row, col);
110 if (*pp == (struct ent *)0) {
111 *pp = (struct ent *) xmalloc((unsigned)sizeof(struct ent));
112 if (row>maxrow) maxrow = row;
113 if (col>maxcol) maxcol = col;
114 (*pp)->label = (char *)0;
115 (*pp)->row = row;
116 (*pp)->col = col;
117 (*pp)->flags = 0;
118 (*pp)->expr = (struct enode *)0;
119 (*pp)->v = (double) 0.0;
120 (*pp)->evnext = (struct ent *)0;
121 }
122 return *pp;
123}
124
125/*
126 * This structure is used to keep ent structs around before they
127 * are deleted to allow the sync_refs routine a chance to fix the
128 * variable references.
129 * We also use it as a last-deleted buffer for the 'p' command.
130 */
131
132void
133free_ent(p)
134register struct ent *p;
135{
136 p->next = to_fix;
137 to_fix = p;
138 p->flags |= is_deleted;
139}
140
141void
142flush_saved()
143{
144 register struct ent *p;
145 register struct ent *q;
146
147 if (!(p = to_fix))
148 return;
149 while (p) {
150 (void) clearent(p);
151 q = p->next;
152 xfree((char *)p);
153 p = q;
154 }
155 to_fix = 0;
156}
157
158/*
159 * standout last time in update()?
160 * At this point we will let curses do work
161 */
162int standlast = FALSE;
163
164void
165update (anychanged)
166int anychanged; /* did any cell really change in value? */
167{
168 register row,
169 col;
170 register struct ent **pp;
171 int mxcol;
172 int mxrow;
173 int rows;
174 int cols;
175 int minsr, minsc, maxsr, maxsc;
176 register r;
177 register i;
178
179 while (row_hidden[currow]) /* You can't hide the last row or col */
180 currow++;
181 while (col_hidden[curcol])
182 curcol++;
183 /* First see if the last display still covers curcol */
184 if (stcol <= curcol) {
185 for (i = stcol, cols = 0, col = RESCOL;
186 (col + fwidth[i]) < COLS-1 && i < maxcols; i++) {
187 cols++;
188 if (col_hidden[i])
189 continue;
190 col += fwidth[i];
191 }
192 }
193 while (stcol + cols - 1 < curcol || curcol < stcol) {
194 FullUpdate++;
195 if (stcol - 1 == curcol) { /* How about back one? */
196 stcol--;
197 } else if (stcol + cols == curcol) { /* Forward one? */
198 stcol++;
199 } else {
200 /* Try to put the cursor in the center of the screen */
201 col = (COLS - RESCOL - fwidth[curcol]) / 2 + RESCOL;
202 stcol = curcol;
203 for (i=curcol-1; i >= 0 && col-fwidth[i] > RESCOL; i--) {
204 stcol--;
205 if (col_hidden[i])
206 continue;
207 col -= fwidth[i];
208 }
209 }
210 /* Now pick up the counts again */
211 for (i = stcol, cols = 0, col = RESCOL;
212 (col + fwidth[i]) < COLS-1 && i < maxcols; i++) {
213 cols++;
214 if (col_hidden[i])
215 continue;
216 col += fwidth[i];
217 }
218 }
219 /* Now - same process on the rows */
220 if (strow <= currow) {
221 for (i = strow, rows = 0, row=RESROW; row<LINES && i<maxrows; i++) {
222 rows++;
223 if (row_hidden[i])
224 continue;
225 row++;
226 }
227 }
228 while (strow + rows - 1 < currow || currow < strow) {
229 FullUpdate++;
230 if (strow - 1 == currow) { /* How about up one? */
231 strow--;
232 } else if (strow + rows == currow) { /* Down one? */
233 strow++;
234 } else {
235 /* Try to put the cursor in the center of the screen */
236 row = (LINES - RESROW) / 2 + RESROW;
237 strow = currow;
238 for (i=currow-1; i >= 0 && row-1 > RESROW; i--) {
239 strow--;
240 if (row_hidden[i])
241 continue;
242 row--;
243 }
244 }
245 /* Now pick up the counts again */
246 for (i = strow, rows = 0, row=RESROW; row<LINES && i<maxrows; i++) {
247 rows++;
248 if (row_hidden[i])
249 continue;
250 row++;
251 }
252 }
253 mxcol = stcol + cols - 1;
254 mxrow = strow + rows - 1;
255 if (FullUpdate || standlast) {
256 (void) move(2, 0);
257 (void) clrtobot();
258 (void) standout();
259 for (row=RESROW, i=strow; i <= mxrow; i++) {
260 if (row_hidden[i])
261 continue;
262 (void) move(row,0);
263 if (maxrows < 1000)
264 (void) printw("%-*d", RESCOL-1, i);
265 else
266 (void) printw("%-*d", RESCOL, i);
267 row++;
268 }
269 (void) move(2,0);
270 (void) printw("%*s", RESCOL, " ");
271
272 for (col=RESCOL, i = stcol; i <= mxcol; i++) {
273 register int k;
274 if (col_hidden[i])
275 continue;
276 (void) move(2, col);
277 k = fwidth[i]/2;
278 if (k == 0)
279 (void) printw("%1s", coltoa(i));
280 else
281 (void) printw("%*s%-*s", k, " ", fwidth[i]-k, coltoa(i));
282 col += fwidth[i];
283 }
284 (void) standend();
285 }
286
287 /* Get rid of cursor standout on the cell at previous cursor position */
288 if (showcell)
289 { (void) move(lastmx, lastmy);
290 repaint(lastmx, lastmy, fwidth[lastcol]);
291 }
292
293 if (showrange) {
294 minsr = showsr < currow ? showsr : currow;
295 minsc = showsc < curcol ? showsc : curcol;
296 maxsr = showsr > currow ? showsr : currow;
297 maxsc = showsc > curcol ? showsc : curcol;
298
299 if (showtop) {
300 (void) move(1,0);
301 (void) clrtoeol();
302 (void) printw("Default range: %s",
303 r_name(minsr, minsc, maxsr, maxsc));
304 }
305 }
306
307 /* Repaint the visible screen */
308 if (showrange || anychanged || FullUpdate || standlast)
309 {
310 /* may be reset in loop, if not next time we will do a FullUpdate */
311 if (standlast)
312 { FullUpdate = TRUE;
313 standlast = FALSE;
314 }
315 for (row = strow, r = RESROW; row <= mxrow; row++) {
316 register c = RESCOL;
317 int do_stand = 0;
318 int fieldlen;
319 int nextcol;
320
321 if (row_hidden[row])
322 continue;
323 for (pp = ATBL(tbl, row, col = stcol); col <= mxcol;
324 pp += nextcol - col, col = nextcol, c += fieldlen) {
325
326 nextcol = col+1;
327 if (col_hidden[col]) {
328 fieldlen = 0;
329 continue;
330 }
331
332 fieldlen = fwidth[col];
333
334 /*
335 * Set standout if:
336 *
337 * - showing ranges, and not showing cells which need to be filled
338 * in, and not showing cell expressions, and in a range, OR
339 *
340 * - if showing cells which need to be filled in and this one is
341 * of that type (has a value and doesn't have an expression,
342 * or it is a string expression), OR
343 *
344 * - if showing cells which have expressions and this one does.
345 */
346
347 if ((showrange && (! showneed) && (! showexpr)
348 && (row >= minsr) && (row <= maxsr)
349 && (col >= minsc) && (col <= maxsc))
350 || (showneed && (*pp) && ((*pp) -> flags & is_valid)
351 && (((*pp) -> flags & is_strexpr) || !((*pp) -> expr)))
352 || (showexpr && (*pp) && ((*pp) -> expr)))
353 {
354 (void) move(r, c);
355 (void) standout();
356 standlast++;
357 if (!*pp) /* no cell, but standing out */
358 { (void) printw("%*s", fwidth[col], " ");
359 (void) standend();
360 continue;
361 }
362 else
363 do_stand = 1;
364 }
365 else
366 do_stand = 0;
367
368 if ((*pp) && ((*pp) -> flags & is_changed || FullUpdate) || do_stand) {
369 if (do_stand) {
370 (*pp) -> flags |= is_changed;
371 } else {
372 (void) move(r, c);
373 (*pp) -> flags &= ~is_changed;
374 }
375
376 /*
377 * Show expression; takes priority over other displays:
378 */
379
380 if (showexpr && ((*pp) -> expr)) {
381 linelim = 0;
382 editexp(row, col); /* set line to expr */
383 linelim = -1;
384 showstring(line, /* leftflush = */ 1, /* hasvalue = */ 0,
385 row, col, & nextcol, mxcol, & fieldlen, r, c);
386 }
387 else {
388
389 /*
390 * Show cell's numeric value:
391 */
392
393 if ((*pp) -> flags & is_valid) {
394 char field[FBUFLEN];
395 (void)sprintf(field,"%*.*f", fwidth[col], precision[col], (*pp)->v);
396 if(strlen(field) > fwidth[col]) {
397 for(i = 0; i<fwidth[col]; i++)
398 (void)addch('*');
399 } else {
400 (void)addstr(field);
401 }
402 }
403
404 /*
405 * Show cell's label string:
406 */
407
408 if ((*pp) -> label) {
409 showstring((*pp) -> label,
410 (*pp) -> flags & is_leftflush,
411 (*pp) -> flags & is_valid,
412 row, col, & nextcol, mxcol,
413 & fieldlen, r, c);
414 }
415 else /* repaint a blank cell: */
416 if ((do_stand || !FullUpdate) &&
417 ((*pp)->flags & is_changed) &&
418 !((*pp)->flags & is_valid) && !(*pp)->label) {
419 (void) printw("%*s", fwidth[col], " ");
420 }
421 } /* else */
422
423 if (do_stand) {
424 (void) standend();
425 do_stand = 0;
426 }
427 }
428 }
429 r++;
430 }
431 }
432
433 (void) move(lastmy, lastmx+fwidth[lastcol]);
434 if((inch() & A_CHARTEXT ) == '<')
435 (void) addstr(under_cursor);
436
437 lastmy = RESROW;
438 for (row = strow; row < currow; row++)
439 if (!row_hidden[row])
440 lastmy += 1;
441 lastmx = RESCOL;
442 for (col = stcol; col < curcol; col++)
443 if (!col_hidden[col])
444 lastmx += fwidth[col];
445 lastcol = curcol;
446 if (showcell && (! showneed) && (! showexpr)) {
447 (void) move(lastmy, lastmx);
448 (void) standout();
449 repaint(lastmx, lastmy, fwidth[lastcol]);
450 (void) standend();
451 }
452 (void) move(lastmy, lastmx+fwidth[lastcol]);
453 *under_cursor = (inch() & A_CHARTEXT );
454 (void) addstr("<");
455
456 (void) move(0, 0);
457 (void) clrtoeol();
458 if (linelim >= 0) {
459 (void) addch(mode_ind);
460 (void) addstr("> ");
461 (void) addstr(line);
462 (void) move(0, linelim+3);
463 } else {
464 if (showtop) { /* show top line */
465 register struct ent *p1;
466 int printed = 0; /* printed something? */
467
468 (void) printw("%s%d ", coltoa(curcol), currow);
469
470 if (p1 = *ATBL(tbl, currow, curcol)) {
471 if (p1 -> expr) {
472 /* has expr of some type */
473 linelim = 0;
474 editexp(currow, curcol); /* set line to expr */
475 linelim = -1;
476 }
477
478 /*
479 * Display string part of cell:
480 */
481
482 if ((p1 -> expr) && (p1 -> flags & is_strexpr)) {
483 (void) addstr((p1 -> flags & is_leftflush) ? "<{" : ">{");
484 (void) addstr(line);
485 (void) addstr("} "); /* and this '}' is for vi % */
486 printed = 1;
487
488 } else if (p1 -> label) {
489 /* has constant label only */
490 (void) addstr ((p1 -> flags & is_leftflush) ? "<\"" : ">\"");
491 (void) addstr (p1 -> label);
492 (void) addstr ("\" ");
493 printed = 1;
494 }
495
496 /*
497 * Display value part of cell:
498 */
499
500 if (p1 -> flags & is_valid) {
501 /* has value or num expr */
502 if ((! (p1 -> expr)) || (p1 -> flags & is_strexpr))
503 (void) sprintf (line, "%.15g", p1 -> v);
504
505 (void) addstr ("[");
506 (void) addstr (line);
507 (void) addstr ("]");
508 printed = 1;
509 }
510 }
511 if (! printed)
512 (void) addstr ("[]");
513 }
514 (void) move (lastmy, lastmx + fwidth[lastcol]);
515 }
516 if (revmsg[0]) {
517 (void) move(0, 0);
518 (void) clrtoeol (); /* get rid of topline display */
519 (void) printw(revmsg);
520 revmsg[0] = '\0'; /* don't show it again */
521 (void) move (lastmy, lastmx + fwidth[lastcol]);
522 }
523 FullUpdate = FALSE;
524}
525
526void
527repaint(x, y, len)
528int x, y, len;
529{
530 int c;
531
532 while(len-- > 0) {
533 (void) move(y,x);
534 c = inch() & A_CHARTEXT;
535 (void) addch(c);
536 x++;
537 }
538}
539
540char *progname;
541
542int
543main (argc, argv)
544int argc;
545char **argv;
546{
547 int inloop = 1;
548 register int c;
549 int edistate = -1;
550 int arg = 1;
551 int narg;
552 int nedistate;
553 int running;
554 char *revi;
555 int anychanged = FALSE;
556
557 /*
558 * Keep command line options around until the file is read so the
559 * command line overrides file options
560 */
561
562 int Mopt = 0;
563 int Nopt = 0;
564 int Copt = 0;
565 int Ropt = 0;
566
567 int tempx, tempy; /* Temp versions of curx, cury */
568
569 if ((revi = strrchr(argv[0], '/')) != NULL)
570 progname = revi+1;
571 else
572 progname = argv[0];
573
574 while (argc > 1 && argv[1][0] == '-') {
575 argv++;
576 argc--;
577 switch (argv[0][1]) {
578 case 'x':
579#ifdef VMS
580 (void) fprintf(stderr, "Crypt not available for VMS\n");
581 exit(1);
582#else
583 Crypt = 1;
584#endif
585 break;
586 case 'm':
587 Mopt = 1;
588 break;
589 case 'n':
590 Nopt = 1;
591 break;
592 case 'c':
593 Copt = 1;
594 break;
595 case 'r':
596 Ropt = 1;
597 break;
598 default:
599 (void) fprintf(stderr,"%s: unrecognized option: \"%c\"\n",
600 progname,argv[0][1]);
601 exit(1);
602 }
603 }
604
605 *curfile ='\0';
606
607 signals();
608 (void) initscr();
609
610 /* setup the spreadsheet arrays, initscr() will get the screen size */
611 if (!growtbl(GROWNEW, 0, 0))
612 { endwin();
613 exit(1);
614 }
615
616 (void) clear();
617#ifdef VMS
618 VMS_read_raw = 1;
619#else
620 nonl();
621 noecho ();
622 cbreak();
623#endif
624 initkbd();
625 scrollok(stdscr, 1);
626
627 /*
628 * Build revision message for later use:
629 */
630
631 (void) strcpy (revmsg, progname);
632 for (revi = rev; (*revi++) != ':'; ); /* copy after colon */
633 (void) strcat (revmsg, revi);
634 revmsg [strlen (revmsg) - 2] = 0; /* erase last character */
635 (void) strcat (revmsg, ": Type '?' for help.");
636
637 if (argc > 1) {
638 (void) strcpy(curfile,argv[1]);
639 readfile (argv[1], 0);
640 }
641
642 if (Mopt)
643 autocalc = 0;
644 if (Nopt)
645 numeric = 1;
646 if (Copt)
647 calc_order = BYCOLS;
648 if (Ropt)
649 calc_order = BYROWS;
650
651 modflg = 0;
652#ifdef VENIX
653 setbuf (stdin, NULL);
654#endif
655 FullUpdate++;
656 while (inloop) { running = 1;
657 while (running) {
658 nedistate = -1;
659 narg = 1;
660 if (edistate < 0 && linelim < 0 && autocalc && (changed || FullUpdate))
661 { EvalAll ();
662 if (changed) /* if EvalAll changed or was before */
663 anychanged = TRUE;
664 changed = 0;
665 }
666 else /* any cells change? */
667 if (changed)
668 anychanged = TRUE;
669
670 update(anychanged);
671 anychanged = FALSE;
672#ifndef SYSV3
673 (void) refresh(); /* 5.3 does a refresh in getch */
674#endif
675 c = nmgetch();
676 getyx(stdscr, tempy, tempx);
677 (void) move (1, 0);
678 (void) clrtoeol ();
679 (void) move(tempy, tempx);
680 (void) fflush (stdout);
681 seenerr = 0;
682 showneed = 0; /* reset after each update */
683 showexpr = 0;
684
685 /* if ((c < ' ') || ( c == DEL )) how about international here ? PB */
686 if ( iscntrl(c) )
687 switch (c) {
688#ifdef SIGTSTP
689 case ctl('z'):
690 (void) deraw();
691 (void) kill(0, SIGTSTP); /* Nail process group */
692
693 /* the pc stops here */
694
695 (void) goraw();
696 break;
697#endif
698 case ctl('r'):
699 showneed = 1;
700 case ctl('l'):
701 FullUpdate++;
702 (void) clearok(stdscr,1);
703 break;
704 case ctl('x'):
705 FullUpdate++;
706 showexpr = 1;
707 (void) clearok(stdscr,1);
708 break;
709 default:
710 error ("No such command (^%c)", c + 0100);
711 break;
712 case ctl('b'):
713 backcol(arg);
714 break;
715 case ctl('c'):
716 running = 0;
717 break;
718
719 case ctl('e'):
720
721 switch (nmgetch()) {
722 case ctl('p'): case 'k': doend (-1, 0); break;
723 case ctl('n'): case 'j': doend ( 1, 0); break;
724 case ctl('b'): case 'h':
725 case ctl('h'): doend ( 0,-1); break;
726 case ctl('f'): case 'l':
727 case ctl('i'): case ' ': doend ( 0, 1); break;
728
729 case ESC:
730 case ctl('g'):
731 break;
732
733 default:
734 error("Invalid ^E command");
735 break;
736 }
737
738 break;
739
740 case ctl('f'):
741 forwcol(arg);
742 break;
743
744 case ctl('g'):
745 showrange = 0;
746 linelim = -1;
747 (void) move (1, 0);
748 (void) clrtoeol ();
749 break;
750
751 case ESC: /* ctl('[') */
752 write_line(ESC);
753 break;
754
755 case ctl('d'):
756 write_line(ctl('d'));
757 break;
758
759 case DEL:
760 case ctl('h'):
761 if (linelim < 0) { /* not editing line */
762 backcol(arg); /* treat like ^B */
763 break;
764 }
765 write_line(ctl('h'));
766 break;
767
768 case ctl('i'): /* tab */
769 if (linelim < 0) { /* not editing line */
770 forwcol(arg);
771 break;
772 }
773 if (!showrange) {
774 startshow();
775 } else {
776 showdr();
777 linelim = strlen(line);
778 line[linelim++] = ' ';
779 line[linelim] = 0;
780 showrange = 0;
781 }
782 linelim = strlen (line);
783 break;
784
785 case ctl('m'):
786 case ctl('j'):
787 write_line(ctl('m'));
788 break;
789
790 case ctl('n'):
791 forwrow(arg);
792 break;
793
794 case ctl('p'):
795 backrow(arg);
796 break;
797
798 case ctl('q'):
799 break; /* ignore flow control */
800
801 case ctl('s'):
802 break; /* ignore flow control */
803
804 case ctl('t'):
805 error(
806"Toggle: a:auto c:cell e:ext funcs n:numeric t:top x:encrypt $:pre-scale");
807 (void) refresh();
808
809 switch (nmgetch()) {
810 case 'a': case 'A':
811 case 'm': case 'M':
812 autocalc ^= 1;
813 error("Automatic recalculation %sabled.",
814 autocalc ? "en":"dis");
815 break;
816 case 'n': case 'N':
817 numeric = (! numeric);
818 error ("Numeric input %sabled.",
819 numeric ? "en" : "dis");
820 break;
821 case 't': case 'T':
822 showtop = (! showtop);
823 repaint(lastmx, lastmy, fwidth[lastcol]);
824 error ("Top line %sabled.", showtop ? "en" : "dis");
825 break;
826 case 'c': case 'C':
827 showcell = (! showcell);
828 repaint(lastmx, lastmy, fwidth[lastcol]);
829 error ("Cell highlighting %sabled.",
830 showcell ? "en" : "dis");
831 break;
832 case 'x': case 'X':
833 Crypt = (! Crypt);
834 error ("Encryption %sabled.", Crypt? "en" : "dis");
835 break;
836 case '$':
837 if (prescale == 1.0) {
838 error ("Prescale enabled.");
839 prescale = 0.01;
840 } else {
841 prescale = 1.0;
842 error ("Prescale disabled.");
843 }
844 break;
845 case 'e': case 'E':
846 extfunc = (! extfunc);
847 error ("External functions %sabled.",
848 extfunc? "en" : "dis");
849 break;
850 case ESC:
851 case ctl('g'):
852 --modflg; /* negate the modflg++ */
853 break;
854 default:
855 error ("Invalid toggle command");
856 --modflg; /* negate the modflg++ */
857 }
858 FullUpdate++;
859 modflg++;
860 break;
861
862 case ctl('u'):
863 narg = arg * 4;
864 nedistate = 1;
865 break;
866
867 case ctl('v'): /* insert variable name */
868 if (linelim > 0)
869 ins_string(v_name(currow, curcol));
870 break;
871
872 case ctl('w'): /* insert variable expression */
873 if (linelim > 0) {
874 char *temp, *temp1;
875 int templim;
876
877 temp = strcpy(xmalloc((unsigned)(strlen(line)+1)),line);
878 templim = linelim;
879 editexp(currow,curcol);
880 temp1= strcpy(xmalloc((unsigned)(strlen(line)+1)),line);
881 strcpy(line, temp);
882 linelim = templim;
883 ins_string(temp1);
884 xfree(temp);
885 xfree(temp1);
886 }
887 break;
888
889 case ctl('a'): /* insert variable value */
890 if (linelim > 0) {
891 struct ent *p = *ATBL(tbl, currow, curcol);
892 char temp[100];
893
894 if (p && p -> flags & is_valid) {
895 (void) sprintf (temp, "%.*f",
896 precision[curcol],p -> v);
897 ins_string(temp);
898 }
899 }
900 break;
901
902 } /* End of the control char switch stmt */
903 else if (isdigit(c) && ((numeric && edistate >= 0) ||
904 (!numeric && (linelim < 0 || edistate >= 0)))) {
905 /* we got a leading number */
906 if (edistate != 0) {
907 /* First char of the count */
908 if (c == '0') /* just a '0' goes to left col */
909 curcol = 0;
910 else {
911 nedistate = 0;
912 narg = c - '0';
913 }
914 } else {
915 /* Succeeding count chars */
916 nedistate = 0;
917 narg = arg * 10 + (c - '0');
918 }
919 } else if (linelim >= 0) {
920 /* Editing line */
921 switch(c) {
922 case ')':
923 if (showrange) {
924 showdr();
925 showrange = 0;
926 linelim = strlen (line);
927 }
928 break;
929 default:
930 break;
931 }
932 write_line(c);
933
934 } else if (!numeric && ( c == '+' || c == '-' ) ) {
935 /* increment/decrement ops */
936 register struct ent *p = *ATBL(tbl, currow, curcol);
937 if (!p)
938 continue;
939 if (p->expr && !(p->flags & is_strexpr)) {
940 error("Can't increment/decrement a formula\n");
941 continue;
942 }
943 FullUpdate++;
944 modflg++;
945 if( c == '+' )
946 p -> v += (double) arg;
947 else
948 p -> v -= (double) arg;
949 } else
950 /* switch on a normal command character */
951 switch (c) {
952 case ':':
953 break; /* Be nice to vi users */
954
955 case '@':
956 EvalAll ();
957 changed = 0;
958 anychanged = TRUE;
959 break;
960
961 case '0': case '1': case '2': case '3': case '4':
962 case '5': case '6': case '7': case '8': case '9':
963 case '-': case '.': case '+':
964 (void) sprintf(line,"let %s = %c",
965 v_name(currow, curcol), c);
966 linelim = strlen (line);
967 insert_mode();
968 break;
969
970 case '=':
971 (void) sprintf(line,"let %s = ",
972 v_name(currow, curcol));
973 linelim = strlen (line);
974 insert_mode();
975 break;
976
977 case '!':
978 {
979 /*
980 * "! command" executes command
981 * "!" forks a shell
982 * "!!" repeats last command
983 */
984#ifdef VMS
985 error("Not implemented on VMS");
986#else /* VMS */
987 char *shl;
988 int pid, temp;
989 char cmd[MAXCMD];
990 static char lastcmd[MAXCMD];
991
992 if (!(shl = getenv("SHELL")))
993 shl = "/bin/sh";
994
995 deraw();
996 (void) fputs("! ", stdout);
997 (void) fflush(stdout);
998 (void) fgets(cmd, MAXCMD, stdin);
999 cmd[strlen(cmd) - 1] = '\0'; /* clobber \n */
1000 if(strcmp(cmd,"!") == 0) /* repeat? */
1001 (void) strcpy(cmd, lastcmd);
1002 else
1003 (void) strcpy(lastcmd, cmd);
1004
1005 if (modflg)
1006 {
1007 (void) puts ("[No write since last change]");
1008 (void) fflush (stdout);
1009 }
1010
1011 if (!(pid = fork()))
1012 {
1013 (void) signal (SIGINT, SIG_DFL); /* reset */
1014 if(strlen(cmd))
1015 (void)execl(shl,shl,"-c",cmd,(char *)0);
1016 else
1017 (void) execl(shl, shl, (char *)0);
1018 exit(-127);
1019 }
1020
1021 while (pid != wait(&temp));
1022
1023 (void) printf("Press RETURN to continue ");
1024 (void)nmgetch();
1025 goraw();
1026#endif /* VMS */
1027 break;
1028 }
1029
1030 /*
1031 * Range commands:
1032 */
1033
1034 case '/':
1035 error (
1036"Range: x:erase v:value c:copy f:fill d:define s:show u:undefine");
1037 (void) refresh();
1038
1039 switch (nmgetch()) {
1040 case 'c':
1041 (void) sprintf(line,"copy [dest_range src_range] ");
1042 linelim = strlen(line);
1043 startshow();
1044 insert_mode();
1045 break;
1046 case 'x':
1047 (void) sprintf(line,"erase [range] ");
1048 linelim = strlen(line);
1049 startshow();
1050 insert_mode();
1051 break;
1052 case 'v':
1053 (void) sprintf(line, "value [range] ");
1054 linelim = strlen(line);
1055 startshow();
1056 insert_mode();
1057 break;
1058 case 'f':
1059 (void) sprintf(line,"fill [range start inc] ");
1060 linelim = strlen(line);
1061 startshow();
1062 insert_mode();
1063 break;
1064 case 'd':
1065 (void) sprintf(line,"define [string range] \"");
1066 linelim = strlen(line);
1067 startshow();
1068 insert_mode();
1069 modflg++;
1070 break;
1071 case 'u':
1072 (void) sprintf(line,"undefine [range] ");
1073 linelim = strlen(line);
1074 insert_mode();
1075 modflg++;
1076 break;
1077 case 's':
1078 if(are_ranges())
1079 {
1080 FILE *f;
1081 int pid;
1082 char px[MAXCMD] ;
1083 char *pager;
1084
1085 (void) strcpy(px, "| sort | ");
1086 if(!(pager = getenv("PAGER")))
1087 pager = DFLT_PAGER;
1088 (void) strcat(px,pager);
1089 f = openout(px, &pid);
1090 if (!f) {
1091 error("Can't open pipe to sort");
1092 break;
1093 }
1094 list_range(f);
1095 closeout(f, pid);
1096 }
1097 else error("No ranges defined");
1098 break;
1099
1100 case ESC:
1101 case ctl('g'):
1102 break;
1103 default:
1104 error("Invalid region command");
1105 break;
1106 }
1107 break;
1108
1109 /*
1110 * Row/column commands:
1111 */
1112
1113 case 'i':
1114 case 'a':
1115 case 'd':
1116 case 'p':
1117 case 'v':
1118 case 'z':
1119 case 's':
1120 {
1121 register rcqual;
1122
1123 if (! (rcqual = get_rcqual (c))) {
1124 error ("Invalid row/column command");
1125 break;
1126 }
1127
1128 error (""); /* clear line */
1129
1130 if ( rcqual == ESC || rcqual == ctl('g'))
1131 break;
1132
1133 switch (c) {
1134
1135 case 'i':
1136 if (rcqual == 'r') insertrow(arg);
1137 else opencol(curcol, arg);
1138 break;
1139
1140 case 'a':
1141 if (rcqual == 'r') while (arg--) duprow();
1142 else while (arg--) dupcol();
1143 break;
1144
1145 case 'd':
1146 if (rcqual == 'r') deleterow(arg);
1147 else closecol(curcol, arg);
1148 break;
1149
1150 case 'p':
1151 while (arg--) pullcells(rcqual);
1152 break;
1153
1154 case 'v':
1155 if (rcqual == 'r') rowvalueize(arg);
1156 else colvalueize(arg);
1157 modflg = 1;
1158 break;
1159
1160 case 'z':
1161 if (rcqual == 'r') hiderow(arg);
1162 else hidecol(arg);
1163 break;
1164
1165 case 's':
1166 /* special case; no repeat count */
1167
1168 if (rcqual == 'r') rowshow_op();
1169 else colshow_op();
1170 break;
1171 }
1172 break;
1173 }
1174
1175 case '$':
1176 {
1177 register struct ent *p;
1178
1179 curcol = maxcols - 1;
1180 while (!VALID_CELL(p, currow, curcol) && curcol > 0)
1181 curcol--;
1182 break;
1183 }
1184 case '#':
1185 {
1186 register struct ent *p;
1187
1188 currow = maxrows - 1;
1189 while (!VALID_CELL(p, currow, curcol) && currow > 0)
1190 currow--;
1191 break;
1192 }
1193 case 'w':
1194 {
1195 register struct ent *p;
1196
1197 while (--arg>=0) {
1198 do {
1199 if (curcol < maxcols - 1)
1200 curcol++;
1201 else {
1202 if (currow < maxrows - 1) {
1203 while(++currow < maxrows - 1 &&
1204 row_hidden[currow]) /* */;
1205 curcol = 0;
1206 } else {
1207 error("At end of table");
1208 break;
1209 }
1210 }
1211 } while(col_hidden[curcol] ||
1212 !VALID_CELL(p, currow, curcol));
1213 }
1214 break;
1215 }
1216 case 'b':
1217 {
1218 register struct ent *p;
1219
1220 while (--arg>=0) {
1221 do {
1222 if (curcol)
1223 curcol--;
1224 else {
1225 if (currow) {
1226 while(--currow &&
1227 row_hidden[currow]) /* */;
1228 curcol = maxcols - 1;
1229 } else {
1230 error ("At start of table");
1231 break;
1232 }
1233 }
1234 } while(col_hidden[curcol] ||
1235 !VALID_CELL(p, currow, curcol));
1236 }
1237 break;
1238 }
1239 case '^':
1240 currow = 0;
1241 break;
1242 case '?':
1243 help();
1244 break;
1245 case '"':
1246 (void) sprintf (line, "label %s = \"",
1247 v_name(currow, curcol));
1248 linelim = strlen (line);
1249 insert_mode();
1250 break;
1251 case '<':
1252 (void) sprintf (line, "leftstring %s = \"",
1253 v_name(currow, curcol));
1254 linelim = strlen (line);
1255 insert_mode();
1256 break;
1257 case '>':
1258 (void) sprintf (line, "rightstring %s = \"",
1259 v_name(currow, curcol));
1260 linelim = strlen (line);
1261 insert_mode();
1262 break;
1263 case 'e':
1264 editv (currow, curcol);
1265 edit_mode();
1266 break;
1267 case 'E':
1268 edits (currow, curcol);
1269 edit_mode();
1270 break;
1271 case 'f':
1272 if (arg == 1)
1273 (void) sprintf (line, "format [for column] %s ",
1274 coltoa(curcol));
1275 else {
1276 (void) sprintf(line, "format [for columns] %s:",
1277 coltoa(curcol));
1278 (void) sprintf(line+strlen(line), "%s ",
1279 coltoa(curcol+arg-1));
1280 }
1281 error("Current format is %d %d",
1282 fwidth[curcol],precision[curcol]);
1283 linelim = strlen (line);
1284 insert_mode();
1285 break;
1286 case 'g':
1287 (void) sprintf (line, "goto [v] ");
1288 linelim = strlen (line);
1289 insert_mode();
1290 break;
1291 case 'P':
1292 (void) sprintf (line, "put [\"dest\" range] \"");
1293 if (*curfile)
1294 error ("Default path is \"%s\"",curfile);
1295 linelim = strlen (line);
1296 insert_mode();
1297 break;
1298 case 'M':
1299 (void) sprintf (line, "merge [\"source\"] \"");
1300 linelim = strlen (line);
1301 insert_mode();
1302 break;
1303 case 'R':
1304 if (mdir)
1305 (void) sprintf (line,"merge [\"macro_file\"] \"%s/", mdir);
1306 else
1307 (void) sprintf (line,"merge [\"macro_file\"] \"");
1308 linelim = strlen (line);
1309 insert_mode();
1310 break;
1311 case 'D':
1312 (void) sprintf (line, "mdir [\"macro_directory\"] \"");
1313 linelim = strlen (line);
1314 insert_mode();
1315 break;
1316 case 'G':
1317 (void) sprintf (line, "get [\"source\"] \"");
1318 if (*curfile)
1319 error ("Default file is \"%s\"",curfile);
1320 linelim = strlen (line);
1321 insert_mode();
1322 break;
1323 case 'W':
1324 (void) sprintf (line, "write [\"dest\" range] \"");
1325 linelim = strlen (line);
1326 insert_mode();
1327 break;
1328 case 'S': /* set options */
1329 (void) sprintf (line, "set ");
1330 error("Options: byrows, bycols, iterations=n, tblstyle=(0|tbl|latex|tex)");
1331 linelim = strlen (line);
1332 insert_mode();
1333 break;
1334 case 'T': /* tbl output */
1335 (void) sprintf (line, "tbl [\"dest\" range] \"");
1336 linelim = strlen (line);
1337 insert_mode();
1338 break;
1339 case 'x':
1340 {
1341 register struct ent **pp;
1342 register int c1;
1343
1344 flush_saved();
1345 if(calc_order == BYROWS) {
1346 for (c1 = curcol; arg-- && c1 < maxcols; c1++) {
1347 pp = ATBL(tbl, currow, c1);
1348 if (*pp) {
1349 free_ent(*pp);
1350 *pp = (struct ent *)0;
1351 }
1352 }
1353 }
1354 else {
1355 for (c1 = currow; arg-- && c1 < maxrows; c1++) {
1356 pp = ATBL(tbl, c1, curcol);
1357 if (*pp) {
1358 free_ent(*pp);
1359 *pp = (struct ent *)0;
1360 }
1361 }
1362 }
1363 sync_refs();
1364 modflg++;
1365 FullUpdate++;
1366 }
1367 break;
1368 case 'Q':
1369 case 'q':
1370 running = 0;
1371 break;
1372 case 'h':
1373 backcol(arg);
1374 break;
1375 case 'j':
1376 forwrow(arg);
1377 break;
1378 case 'k':
1379 backrow(arg);
1380 break;
1381 case ' ':
1382 case 'l':
1383 forwcol(arg);
1384 break;
1385 case 'm':
1386 savedrow = currow;
1387 savedcol = curcol;
1388 break;
1389 case 'c': {
1390 register struct ent *p = *ATBL(tbl, savedrow, savedcol);
1391 register c1;
1392 register struct ent *n;
1393 if (!p)
1394 break;
1395 FullUpdate++;
1396 modflg++;
1397 for (c1 = curcol; arg-- && c1 < maxcols; c1++) {
1398 n = lookat (currow, c1);
1399 (void) clearent(n);
1400 copyent( n, p, currow - savedrow, c1 - savedcol);
1401 }
1402 break;
1403 }
1404 default:
1405 if ((toascii(c)) != c)
1406 error ("Weird character, decimal %d\n",
1407 (int) c);
1408 else
1409 error ("No such command (%c)", c);
1410 break;
1411 }
1412 edistate = nedistate;
1413 arg = narg;
1414 } /* while (running) */
1415 inloop = modcheck(" before exiting");
1416 } /* while (inloop) */
1417 deraw();
1418 endwin();
1419#ifdef VMS /* Unit VMS "fixes" exit we should say 1 here */
1420 exit(1);
1421#else
1422 exit(0);
1423#endif
1424 /*NOTREACHED*/
1425}
1426
1427void
1428startshow()
1429{
1430 showrange = 1;
1431 showsr = currow;
1432 showsc = curcol;
1433}
1434
1435void
1436showdr()
1437{
1438 int minsr, minsc, maxsr, maxsc;
1439
1440 minsr = showsr < currow ? showsr : currow;
1441 minsc = showsc < curcol ? showsc : curcol;
1442 maxsr = showsr > currow ? showsr : currow;
1443 maxsc = showsc > curcol ? showsc : curcol;
1444 (void) sprintf (line+linelim,"%s", r_name(minsr, minsc, maxsr, maxsc));
1445}
1446
1447void
1448setorder(i)
1449int i;
1450{
1451 if((i == BYROWS)||(i == BYCOLS))
1452 calc_order = i;
1453 else
1454 error("Not yet implemented");
1455}
1456
1457void
1458setauto(i)
1459int i;
1460{
1461 autocalc = i;
1462}
1463
1464
1465#ifdef VMS
1466
1467goraw()
1468{
1469 VMS_read_raw = 1;
1470 FullUpdate++;
1471}
1472
1473deraw()
1474{
1475 (void) move (LINES - 1, 0);
1476 (void) clrtoeol();
1477 (void) refresh();
1478 VMS_read_raw = 0;
1479}
1480
1481#else /* VMS */
1482void
1483goraw()
1484{
1485#if SYSV2 || SYSV3
1486 fixterm();
1487#else /* SYSV2 || SYSV3 */
1488 cbreak();
1489 nonl();
1490 noecho ();
1491#endif /* SYSV2 || SYSV3 */
1492 kbd_again();
1493 (void) clear();
1494 FullUpdate++;
1495}
1496
1497void
1498deraw()
1499{
1500 (void) move (LINES - 1, 0);
1501 (void) clrtoeol();
1502 (void) refresh();
1503#if SYSV2 || SYSV3
1504 resetterm();
1505#else
1506 nocbreak();
1507 nl();
1508 echo();
1509#endif
1510 resetkbd();
1511}
1512
1513#endif /* VMS */
1514
1515void
1516signals()
1517{
1518#ifdef SIGVOID
1519 void quit();
1520 void time_out();
1521 void dump_me();
1522#else
1523 int quit();
1524 int time_out();
1525 int dump_me();
1526#endif
1527
1528 (void) signal(SIGINT, SIG_IGN);
1529 (void) signal(SIGQUIT, dump_me);
1530 (void) signal(SIGPIPE, quit);
1531 (void) signal(SIGTERM, quit);
1532 (void) signal(SIGALRM, time_out);
1533 (void) signal(SIGFPE, quit);
1534 (void) signal(SIGBUS, quit);
1535}
1536
1537#ifdef SIGVOID
1538void
1539#endif
1540quit()
1541{
1542 diesave();
1543 deraw();
1544 resetkbd();
1545 endwin();
1546 exit(1);
1547}
1548
1549#ifdef SIGVOID
1550void
1551#endif
1552dump_me()
1553{
1554 diesave();
1555 deraw();
1556 abort();
1557}
1558
1559/* try to save the current spreadsheet if we can */
1560diesave()
1561{ char path[PATHLEN];
1562 if (modcheck(" before Spreadsheet dies") == 1)
1563 { sprintf(path, "~/SC.SAVE");
1564 if (writefile(path, 0, 0, maxrow, maxcol) < 0)
1565 if (writefile("/tmp/SC.SAVE", 0, 0, maxrow, maxcol) < 0)
1566 error("Couldn't save current spreadsheet, Sorry");
1567 }
1568}
1569
1570int
1571modcheck(endstr)
1572char *endstr;
1573{
1574 if (modflg && curfile[0]) {
1575 int yn_ans;
1576 char lin[100];
1577
1578 (void) sprintf (lin,"File \"%s\" is modified, save%s? ",curfile,endstr);
1579 if ((yn_ans = yn_ask(lin)) < 0)
1580 return(1);
1581 else
1582 if (yn_ans == 1)
1583 { if (writefile(curfile, 0, 0, maxrow, maxcol) < 0)
1584 return (1);
1585 }
1586 } else if (modflg) {
1587 int yn_ans;
1588
1589 if ((yn_ans = yn_ask("Do you want a chance to save the data? ")) < 0)
1590 return(1);
1591 else
1592 return(yn_ans);
1593 }
1594 return(0);
1595}