xref: /original-bsd/contrib/sc/sc.c (revision e59fb703)
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 
31 char *getenv();
32 
33 #ifdef SYSV3
34 void 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 
43 extern	char	*rev;
44 
45 /* Globals defined in sc.h */
46 
47 struct ent ***tbl;
48 int strow, stcol;
49 int currow, curcol;
50 int savedrow, savedcol;
51 int FullUpdate;
52 int maxrow, maxcol;
53 int maxrows, maxcols;
54 int *fwidth;
55 int *precision;
56 char *col_hidden;
57 char *row_hidden;
58 char line[FBUFLEN];
59 int changed;
60 struct ent *to_fix;
61 int modflg;
62 int numeric;
63 char *mdir;
64 int showsc, showsr;	/* Starting cell for highlighted range */
65 char mode_ind = '.';
66 
67 char curfile[PATHLEN];
68 char    revmsg[80];
69 
70 int  linelim = -1;
71 
72 int  showtop   = 1;	/* Causes current cell value display in top line  */
73 int  showcell  = 1;	/* Causes current cell to be highlighted	  */
74 int  showrange = 0;	/* Causes ranges to be highlighted		  */
75 int  showneed  = 0;	/* Causes cells needing values to be highlighted  */
76 int  showexpr  = 0;	/* Causes cell exprs to be displayed, highlighted */
77 
78 int  autocalc = 1 ;	/* 1 to calculate after each update */
79 int  calc_order = BYROWS;
80 int  tbl_style = 0;	/* headers for T command output */
81 
82 int  lastmx, lastmy;	/* Screen address of the cursor */
83 int  lastcol;		/* Spreadsheet Column the cursor was in last */
84 char under_cursor[] = " "; /* Data under the < cursor */
85 
86 #ifdef VMS
87 int VMS_read_raw = 0;
88 #endif
89 
90 int seenerr;
91 
92 void
93 yyerror(err)
94 char *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 
102 struct ent *
103 lookat(row,col)
104 int	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 
132 void
133 free_ent(p)
134 register struct ent *p;
135 {
136     p->next = to_fix;
137     to_fix = p;
138     p->flags |= is_deleted;
139 }
140 
141 void
142 flush_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  */
162 int	standlast	= FALSE;
163 
164 void
165 update (anychanged)
166 int	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 
526 void
527 repaint(x, y, len)
528 int 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 
540 char    *progname;
541 
542 int
543 main (argc, argv)
544 int argc;
545 char  **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 
1427 void
1428 startshow()
1429 {
1430     showrange = 1;
1431     showsr = currow;
1432     showsc = curcol;
1433 }
1434 
1435 void
1436 showdr()
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 
1447 void
1448 setorder(i)
1449 int i;
1450 {
1451 	if((i == BYROWS)||(i == BYCOLS))
1452 	    calc_order = i;
1453 	else
1454 	    error("Not yet implemented");
1455 }
1456 
1457 void
1458 setauto(i)
1459 int i;
1460 {
1461 	autocalc = i;
1462 }
1463 
1464 
1465 #ifdef VMS
1466 
1467 goraw()
1468 {
1469     VMS_read_raw = 1;
1470     FullUpdate++;
1471 }
1472 
1473 deraw()
1474 {
1475     (void) move (LINES - 1, 0);
1476     (void) clrtoeol();
1477     (void) refresh();
1478     VMS_read_raw = 0;
1479 }
1480 
1481 #else /* VMS */
1482 void
1483 goraw()
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 
1497 void
1498 deraw()
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 
1515 void
1516 signals()
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
1538 void
1539 #endif
1540 quit()
1541 {
1542     diesave();
1543     deraw();
1544     resetkbd();
1545     endwin();
1546     exit(1);
1547 }
1548 
1549 #ifdef SIGVOID
1550 void
1551 #endif
1552 dump_me()
1553 {
1554     diesave();
1555     deraw();
1556     abort();
1557 }
1558 
1559 /* try to save the current spreadsheet if we can */
1560 diesave()
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 
1570 int
1571 modcheck(endstr)
1572 char *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 }
1596