1 /*	SC	A Spreadsheet Calculator
2  *		Curses based Screen 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  *		$Revision: 7.16 $
11  *
12  */
13 
14 
15 #include <curses.h>
16 #include <time.h>
17 #include "sc.h"
18 
19 #ifndef MSDOS
20 #include <unistd.h>
21 #endif
22 
23 #ifdef VMS
24 extern int VMS_read_raw;   /*sigh*/
25     VMS_read_raw = 1;
26 #endif
27 
28 #ifdef BROKENCURSES
29 		/* nl/nonl bug fix */
30 #undef nl
31 #undef nonl
32 #define nl()	 (_tty.sg_flags |= CRMOD,_pfast = _rawmode,stty(_tty_ch, &_tty))
33 #define nonl()	 (_tty.sg_flags &= ~CRMOD, _pfast = TRUE, stty(_tty_ch, &_tty))
34 #endif
35 
36 void	repaint(int x, int y, int len, int attron, int attroff);
37 
38 char	under_cursor = ' '; /* Data under the < cursor */
39 char	mode_ind = 'i';
40 char	search_ind = ' ';
41 extern	char    revmsg[];
42 
43 int	lines, cols;
44 int	rows, lcols;
45 int	lastmx, lastmy;	/* Screen address of the cursor */
46 int	lastrow = -1;	/* Spreadsheet Row the cursor was in last */
47 int	lastcol = -1;	/* Spreadsheet Column the cursor was in last */
48 int	lastendrow = -1;	/* Last bottom row of screen */
49 int	lastftoprows = 0;	/* Rows in top of frame cursor was in last */
50 int	lastfbottomrows = 0;	/* Rows in bottom of frame cursor was in last */
51 int	lastfleftcols = 0;	/* Columns in left side of frame cursor was
52 				    in last */
53 int	lastfrightcols = 0;
54 struct	frange *lastfr = 0;	/* Last framed range we were in */
55 bool	frTooLarge = 0;	/* If set, either too many rows or too many columns
56 			    exist in frame to allow room for the scrolling
57 			    portion of the framed range */
58 int	framerows;	/* Rows in current frame */
59 int	framecols;	/* Columns in current frame */
60 int	rescol = 4;	/* Columns reserved for row numbers */
61 extern	int *fwidth;
62 extern	int showneed;	/* Causes cells needing values to be highlighted */
63 extern	int showexpr;	/* Causes cell exprs to be displayed, highlighted */
64 extern	int shownote;	/* Causes cells with attached notes to be highlighted */
65 #ifdef RIGHT_CBUG
66 extern	int wasforw;	/* Causes screen to be redisplay if on lastcol */
67 #endif
68 extern struct go_save gs;
69 
70 /*
71  * update() does general screen update
72  *
73  * standout last time in update()?
74  *	At this point we will let curses do work
75  */
76 int	standlast	= FALSE;
77 
78 void
update(int anychanged)79 update(int anychanged)		/* did any cell really change in value? */
80 {
81     int				row, col;
82     struct ent			**pp;
83     int				mxrow, mxcol;
84     int				minsr = 0, minsc = 0, maxsr = 0, maxsc = 0;
85     int				r, i;
86     struct frange		*fr;
87     struct crange		*cr;
88     int				ftoprows, fbottomrows, fleftcols, frightcols;
89     int				ftrows, fbrows, flcols, frcols;
90     bool			message;
91 
92 #ifndef MSDOS
93     /*
94      * If receiving input from a pipeline, don't display spreadsheet data
95      * on screen.
96      */
97     if (!usecurses) return;
98 #endif
99 
100     getmaxyx(stdscr, lines, cols);
101     fr = lastfr;
102     if (!(fr && fr->or_left->row <= currow &&	/* If we've left the	    */
103 	    fr->or_left->col <= curcol &&	/* previous framed range... */
104 	    fr->or_right->row >= currow &&
105 	    fr->or_right->col >= curcol)) {
106 	fr = find_frange(currow, curcol);
107 	if (fr != lastfr)
108 	    FullUpdate++;
109     }
110     if (fr) {
111 	ftoprows = fr->ir_left->row - fr->or_left->row;
112 	fbottomrows = fr->or_right->row - fr->ir_right->row;
113 	fleftcols = fr->ir_left->col - fr->or_left->col;
114 	frightcols = fr->or_right->col - fr->ir_right->col;
115 	framerows = RESROW + ftoprows + fbottomrows;
116 	framecols = rescol;
117 	for (r = fr->or_left->row - 1, i = ftoprows; i; i--)
118 	    if (row_hidden[r+i])
119 		framerows--;
120 	for (r = fr->or_right->row - 1, i = fbottomrows; i; i--)
121 	    if (row_hidden[r-i])
122 		framerows--;
123 	for (r = fr->or_left->col - 1, i = fleftcols; i; i--) {
124 	    if (col_hidden[r+i])
125 		continue;
126 	    framecols += fwidth[r+i];
127 	}
128 	for (r = fr->or_right->col - 1, i = frightcols; i; i--) {
129 	    if (col_hidden[r-i])
130 		continue;
131 	    framecols += fwidth[r-i];
132 	}
133 	if (framerows >= lines || framecols >= cols) {
134 	    frTooLarge = TRUE;
135 	    if (FullUpdate) {
136 		error("Frame too large for screen size - ignoring");
137 	    }
138 	    ftoprows = fbottomrows = fleftcols = frightcols = 0;
139 	    strow -= lastftoprows;
140 	    stcol -= lastfleftcols;
141 	} else {
142 	    frTooLarge = FALSE;
143 	    if (strow >= fr->or_left->row) {
144 		if (fr == lastfr && strow < fr->or_left->row + ftoprows)
145 		    strow = fr->or_left->row;
146 		else if (strow > fr->ir_right->row) {
147 		    strow = fr->ir_right->row;
148 		    FullUpdate++;
149 		}
150 	    }
151 	    if (stcol >= fr->or_left->col) {
152 		if (stcol < fr->or_left->col + fleftcols)
153 		    stcol = fr->or_left->col;
154 		else if (stcol > fr->ir_right->col) {
155 		    stcol = fr->ir_right->col;
156 		    FullUpdate++;
157 		}
158 	    }
159 	    if (fr == lastfr && currow == lastrow)
160 		fbottomrows = lastfbottomrows;
161 	    else if (currow < fr->ir_right->row)
162 		fbottomrows = fr->or_right->row - fr->ir_right->row;
163 	    else
164 		fbottomrows = fr->or_right->row - currow;
165 	    if (fr == lastfr && curcol == lastcol)
166 		frightcols = lastfrightcols;
167 	    else if (curcol < fr->ir_right->col)
168 		frightcols = fr->or_right->col - fr->ir_right->col;
169 	    else
170 		frightcols = fr->or_right->col - curcol;
171 	}
172     } else {
173 	ftoprows = fbottomrows = fleftcols = frightcols = 0;
174 	framerows = framecols = 0;
175     }
176     if (fr != lastfr && !gs.stflag && lastfr) {
177 	if (strow >= lastfr->ir_left->row)
178 	    strow -= lastftoprows;
179 	if (stcol >= lastfr->ir_left->col)
180 	    stcol -= lastfleftcols;
181     }
182 
183     ftrows = ftoprows;
184     fbrows = fbottomrows;
185     flcols = frcols = 0;
186     if (fr) {
187 	for (r = fr->or_left->row - 1, i = ftrows; i; i--)
188 	    if (row_hidden[r+i])
189 		ftrows--;
190 	for (r = fr->or_right->row + 1, i = fbrows; i; i--)
191 	    if (row_hidden[r-i])
192 		fbrows--;
193 	for (r = fr->or_left->col - 1, i = fleftcols; i; i--) {
194 	    if (col_hidden[r+i])
195 		continue;
196 	    flcols += fwidth[r+i];
197 	}
198 	for (r = fr->or_right->col + 1, i = frightcols; i; i--) {
199 	    if (col_hidden[r-i])
200 		continue;
201 	    frcols += fwidth[r-i];
202 	}
203     }
204 
205     /*
206      * Place the cursor on the screen.  Set col, curcol, stcol, lastcol as
207      * needed.  If strow and stcol are negative, centering is forced.
208      */
209     if ((curcol != lastcol) || FullUpdate) {
210 	while (col_hidden[curcol])   /* You can't hide the last row or col */
211 	    curcol++;
212 	if (fwidth[curcol] > cols - rescol - 2) {
213 	    error("column %s too wide - resizing", coltoa(curcol));
214 	    doformat(curcol, curcol, cols - rescol - 2,
215 		    precision[curcol], realfmt[curcol]);
216 	}
217 
218 	/* First see if the last display still covers curcol */
219 	if (stcol >= 0 && stcol <= curcol) {
220 	    int c = 0;
221 
222 	    if (fr) {
223 		if (fr != lastfr) {
224 		    if (stcol == fr->or_left->col)
225 			stcol += fleftcols;
226 		    else if (stcol >= fr->or_left->col && !gs.stflag) {
227 			stcol += fleftcols;
228 			if (stcol > fr->ir_right->col)
229 			    stcol = fr->ir_right->col + 1;
230 		    }
231 		} else if (stcol == fr->or_left->col)
232 		    stcol += fleftcols;
233 	    }
234 	    i = stcol;
235 	    lcols = 0;
236 	    col = rescol + frcols;
237 	    if (fr && stcol >= fr->or_left->col)
238 		if (stcol < fr->ir_left->col)
239 		    i = fr->or_left->col;
240 		else
241 		    col += flcols;
242 	    for (; (col + fwidth[i] < cols-1 || col_hidden[i] || i < curcol) &&
243 		    i < maxcols; i++) {
244 		lcols++;
245 		if (fr && i == fr->ir_right->col + 1) {
246 		    col -= frcols;
247 		    frcols = frightcols = 0;
248 		}
249 		if (col_hidden[i])
250 		    continue;
251 
252 		/* If there isn't room for more columns, and we haven't yet
253 		 * reached the current column, start removing columns from
254 		 * the left.
255 		 */
256 		while (col + fwidth[i] > cols - 2) {
257 		    lcols--;
258 		    col -= fwidth[stcol];
259 		    while (col_hidden[++stcol]) /**/ ;
260 		    FullUpdate++;
261 		    c++;
262 		}
263 		col += fwidth[i];
264 	    }
265 	    if (!frTooLarge && fr && curcol <= stcol + lcols &&
266 		    fr->ir_left->col >= stcol + lcols) {
267 		while (stcol + lcols < fr->ir_left->col) {
268 		    col -= fwidth[stcol];
269 		    lcols--;
270 		    while (col_hidden[++stcol])
271 			lcols--;
272 		    while (col + fwidth[stcol + lcols] < cols - 1) {
273 			col += fwidth[stcol + lcols];
274 			lcols++;
275 		    }
276 		}
277 	    } else if (c)
278 		stcol = -1;
279 	}
280 
281 	while (stcol < 0 || curcol < stcol || stcol + lcols - 1 < curcol ||
282 		(colsinrange != fwidth[curcol] && stcol != curcol &&
283 		stcol + lcols - 1 < gs.g_lastcol)) {
284 
285 	    FullUpdate++;
286 
287 		/* How about back one? */
288 	    if (stcol - 1 == curcol) {
289 		stcol--;
290 		/* Forward one? */
291 	    } else if (stcol >= 0 && stcol + lcols == curcol) {
292 		stcol++;
293 	    } else if (stcol >= 0 && fr && curcol >= fr->or_left->col &&
294 		    curcol <= fr->ir_left->col && stcol < curcol &&
295 		    curcol <= stcol + lcols + fr->ir_left->col -
296 			    fr->or_left->col) {
297 		while ((stcol + lcols < fr->ir_left->col && !frTooLarge) ||
298 			(colsinrange != fwidth[curcol] && stcol != curcol &&
299 			stcol + lcols - 1 < gs.g_lastcol)) {
300 		    if (col_hidden[++stcol]) lcols--;
301 		}
302 	    } else {
303 		/* Try to put the cursor in the center of the screen.
304 		 * If we've just jumped to a range using the goto command,
305 		 * center the range instead.
306 		 */
307 		colsinrange = (colsinrange > cols - rescol -
308 			flcols - frcols - 2 ?
309 			cols - rescol - flcols - frcols - 2 : colsinrange);
310 		col = (cols - rescol - flcols - frcols - colsinrange)/2;
311 		stcol = curcol;
312 		for (i = curcol - 1;
313 			i >= (fr ? fr->or_left->col + fleftcols : 0) &&
314 			(col - fwidth[i] > 0 || col_hidden[i]);
315 			i--) {
316 		    stcol--;
317 		    if (col_hidden[i])
318 			continue;
319 		    col -= fwidth[i];
320 		}
321 		if (fr && stcol < fr->or_left->col + fleftcols) {
322 		    stcol = fr->or_left->col + fleftcols;
323 		    if (curcol < stcol)
324 			stcol = curcol;
325 		}
326 	    }
327 	    /* Now pick up the counts again */
328 	    i = stcol;
329 	    lcols = 0;
330 	    col = rescol + frcols;
331 	    if (fr && stcol >= fr->or_left->col)
332 		if (stcol < fr->ir_left->col)
333 		    i = fr->or_left->col;
334 		else
335 		    col += flcols;
336 	    for (; (col + fwidth[i] < cols-1 || col_hidden[i] || i < curcol) &&
337 		    i < maxcols; i++) {
338 		lcols++;
339 		if (fr && i == fr->ir_right->col + 1) {
340 		    col -= frcols;
341 		    frcols = frightcols = 0;
342 		}
343 		if (col_hidden[i])
344 		    continue;
345 
346 		col += fwidth[i];
347 	    }
348 	}
349     }
350     if (fleftcols && stcol >= fr->or_left->col &&
351 	    stcol < fr->or_left->col + fleftcols) {
352 	lcols += (fr->or_left->col - stcol);
353 	stcol = fr->or_left->col + fleftcols;
354 	if (curcol < stcol)
355 	    stcol = curcol;
356     }
357 
358     /* Now - same process on the rows as the columns */
359     if ((currow != lastrow) || FullUpdate) {
360 	while (row_hidden[currow])   /* You can't hide the last row or col */
361 	    currow++;
362 	if (strow >= 0 && strow <= currow) {
363 	    int c = 0;
364 
365 	    if (fr) {
366 		if (fr != lastfr) {
367 		    if (strow == fr->or_left->row)
368 			strow += ftoprows;
369 		    else if (strow >= fr->or_left->row && !gs.stflag) {
370 			strow += ftoprows;
371 			if (strow > fr->ir_right->row)
372 			    strow = fr->ir_right->row + 1;
373 		    }
374 		} else if (strow == fr->or_left->row)
375 		    strow += ftoprows;
376 	    }
377 	    i = strow;
378 	    rows = 0;
379 	    row = RESROW + fbrows;
380 	    if (fr && strow >= fr->or_left->row)
381 		if (strow < fr->ir_left->row)
382 		    i = fr->or_left->row;
383 		else
384 		    row += ftrows;
385 	    for (; (row < lines || row_hidden[i] || i < currow) && i < maxrows;
386 		    i++) {
387 		rows++;
388 		if (fr && i == fr->ir_right->row + 1) {
389 		    row -= fbrows;
390 		    fbrows = fbottomrows = 0;
391 		}
392 		if (row_hidden[i])
393 		    continue;
394 
395 		/* If there isn't room for more rows, and we haven't yet
396 		 * reached the current row, start removing rows from the
397 		 * top.
398 		 */
399 		if (row >= lines) {
400 		    rows--;
401 		    row--;
402 		    while (row_hidden[++strow]) /**/;
403 		    FullUpdate++;
404 		    c++;
405 		}
406 		row++;
407 	    }
408 	    if (!frTooLarge && fr && currow <= strow + rows &&
409 		    fr->ir_left->row >= strow + rows) {
410 		while (strow + rows < fr->ir_left->row) {
411 		    while (row_hidden[++strow]) /**/;
412 		}
413 	    } else if (c && currow > lastendrow)
414 		strow = -1;
415 	}
416 
417 	while (strow < 0 || currow < strow || strow + rows - 1 < currow ||
418 		strow + rows < currow + rowsinrange) {
419 
420 	    FullUpdate++;
421 
422 		/* How about up one? */
423 	    if (strow - 1 == currow) {
424 		strow--;
425 		/* Down one? */
426 	    } else if (strow >= 0 && strow + rows == currow) {
427 		strow++;
428 	    } else if (strow >= 0 && fr && currow >= fr->or_left->row &&
429 		    currow <= fr->ir_left->row && strow < currow &&
430 		    currow <= strow + rows + fr->ir_left->row -
431 			    fr->or_left->row) {
432 		while ((strow + rows < fr->ir_left->row && !frTooLarge) ||
433 			(rowsinrange > 1 && strow != currow &&
434 			strow + rows - 1 < gs.g_lastrow)) {
435 		    if (row_hidden[++strow]) rows--;
436 		}
437 	    } else {
438 		/* Try to put the cursor in the center of the screen.
439 		 * If we've just jumped to a range using the goto command,
440 		 * center the range instead.
441 		 */
442 		rowsinrange = (rowsinrange > lines - RESROW - ftrows - fbrows ?
443 			lines - RESROW - ftrows - fbrows : rowsinrange);
444 		row = (lines - RESROW - ftrows - fbrows - rowsinrange)/2;
445 		strow = currow;
446 		for (i = currow - 1;
447 			i >= (fr ? fr->or_left->row + ftoprows : 0) &&
448 			(row > 0 || row_hidden[i]); i--) {
449 		    strow--;
450 		    if (row_hidden[i])
451 			continue;
452 		    row--;
453 		}
454 		if (fr && strow < fr->or_left->row + ftoprows)
455 		    strow = fr->or_left->row + ftoprows;
456 		    if (currow < strow)
457 			strow = currow;
458 	    }
459 	    /* Now pick up the counts again */
460 	    i = strow;
461 	    rows = 0;
462 	    row = RESROW + fbrows;
463 	    if (fr && strow >= fr->or_left->row)
464 		if (strow < fr->ir_left->row)
465 		    i = fr->or_left->row;
466 		else
467 		    row += ftrows;
468 	    for (; (row < lines || row_hidden[i] || i < currow) && i < maxrows;
469 		    i++) {
470 		rows++;
471 		if (fr && i == fr->ir_right->row + 1) {
472 		    row -= fbrows;
473 		    fbrows = fbottomrows = 0;
474 		}
475 		if (row_hidden[i])
476 		    continue;
477 
478 		row++;
479 	    }
480 	}
481     }
482     if (ftoprows && strow >= fr->or_left->row &&
483 	    strow < fr->or_left->row + ftoprows) {
484 	rows += (fr->or_left->row - strow);
485 	strow = fr->or_left->row + ftoprows;
486 	if (currow < strow)
487 	    strow = currow;
488     }
489 
490     mxcol = frightcols ? fr->or_right->col : stcol + lcols - 1;
491     mxrow = fbottomrows ? fr->or_right->row : strow + rows - 1;
492     gs.stflag = 0;
493     lastfr = fr;
494     lastftoprows = ftoprows;
495     lastfbottomrows = fbottomrows;
496     lastfleftcols = fleftcols;
497     lastfrightcols = frightcols;
498 
499     /* Get rid of cursor standout on the cell at previous cursor position */
500     if (!FullUpdate) {
501 	if (showcell) {
502 	    pp = ATBL(tbl, lastrow, lastcol);
503 	    if (color && has_colors()) {
504 		if ((cr = find_crange(lastrow, lastcol)))
505 		    color_set(cr->r_color, NULL);
506 		else
507 		    color_set(1, NULL);
508 		if (*pp) {
509 		    if (colorneg && (*pp)->flags & is_valid && (*pp)->v < 0) {
510 			if (cr)
511 			    color_set(((cr->r_color) % CPAIRS) + 1, NULL);
512 			else
513 			    color_set(2, NULL);
514 		    }
515 		    else if (colorerr && (*pp)->cellerror)
516 			color_set(3, NULL);
517 		}
518 	    }
519 	    repaint(lastmx, lastmy, fwidth[lastcol], 0, A_STANDOUT);
520 	}
521 
522 	(void) move(lastmy, lastmx+fwidth[lastcol]);
523 
524 	if ((inch() & A_CHARTEXT) == '<')
525 	    (void) addch(under_cursor | (inch() & A_ATTRIBUTES));
526 
527 	repaint(lastmx, RESROW - 1, fwidth[lastcol], A_STANDOUT, 0);
528 	repaint(0, lastmy, rescol - 1, A_STANDOUT, 0);
529 	if (color && has_colors())
530 	    color_set(1, NULL);
531     }
532     lastrow = currow;
533     lastcol = curcol;
534     lastendrow = strow + rows;
535 
536     /* where is the the cursor now? */
537     lastmy =  RESROW;
538     if (fr && strow >= fr->or_left->row)
539 	if (strow < fr->ir_left->row)
540 	    row = fr->or_left->row;
541 	else {
542 	    row = strow;
543 	    lastmy += ftrows;
544 	}
545     else
546 	row = strow;
547     for (; row < currow; row++)
548 	if (!row_hidden[row])
549 	    lastmy++;
550 
551     lastmx = rescol;
552     if (fr && stcol >= fr->or_left->col)
553 	if (stcol < fr->ir_left->col)
554 	    col = fr->or_left->col;
555 	else {
556 	    col = stcol;
557 	    lastmx += flcols;
558 	}
559     else
560 	col = stcol;
561     for (; col < curcol; col++)
562 	if (!col_hidden[col])
563 	    lastmx += fwidth[col];
564 
565     if (color && has_colors())
566 	color_set(1, NULL);
567 
568     if (FullUpdate || standlast) {
569 	(void) move(2, 0);
570 	(void) clrtobot();
571 	(void) standout();
572 
573 	for (row = RESROW, i = (ftoprows && strow >= fr->or_left->row ?
574 		fr->or_left->row : strow);
575 		i <= mxrow; i++) {
576 	    if (ftoprows && strow >= fr->or_left->row &&
577 		    row == RESROW + ftrows)
578 		i = (strow < i ? i : strow);
579 	    if (fbottomrows && row == lines - fbrows)
580 		i = fr->or_right->row - fbottomrows + 1;
581 	    if (row_hidden[i])
582 		continue;
583 	    (void) move(row, 0);
584 	    (void) printw("%*d", rescol - 1, i);
585 	    row++;
586 	}
587 #ifdef RIGHT_CBUG
588 	if (wasforw) {
589 	    clearok(stdscr, TRUE);
590 	    wasforw = 0;
591 	}
592 #endif
593 	(void) move(2, 0);
594 	(void) printw("%*s", rescol, " ");
595 
596 	for (col = rescol, i = (fleftcols && stcol >= fr->or_left->col ?
597 		fr->or_left->col : stcol);
598 		i <= mxcol; i++) {
599 	    register int k;
600 	    if (fleftcols && stcol >= fr->or_left->col &&
601 		    col == rescol + flcols)
602 		i = (stcol < i ? i : stcol);
603 	    if (frightcols && col + fwidth[i] >= cols - 1 - frcols &&
604 		    i < fr->or_right->col - frightcols + 1)
605 		i = fr->or_right->col - frightcols + 1;
606 	    if (col_hidden[i])
607 		continue;
608 	    (void) move(2, col);
609 	    k = (fwidth[i] - strlen(coltoa(i)))/2;
610 	    if (fwidth[i] == 1)
611 		(void) printw("%1s", coltoa(i%26));
612 	    else if (braille)
613 	        (void) printw("%-*s", fwidth[i], coltoa(i));
614 	    else
615 	        (void) printw("%*s%-*s", k, "", fwidth[i]-k, coltoa(i));
616 	    col += fwidth[i];
617 	}
618 	(void) standend();
619     }
620 
621     (void) move(1, 0);
622     message = (inch() & A_CHARTEXT) != ' ';
623     if (showrange) {
624 	if (showrange == SHOWROWS) {
625 	    minsr = showsr < currow ? showsr : currow;
626 	    minsc = fr ? fr->or_left->col : 0;
627 	    maxsr = showsr > currow ? showsr : currow;
628 	    maxsc = fr ? fr->or_right->col : maxcols;
629 
630 	    if (showtop && !message) {
631 		(void) clrtoeol();
632 		(void) printw("Default range:  %d:%d", minsr, maxsr);
633 	    }
634 	} else if (showrange == SHOWCOLS) {
635 	    minsr = 0;
636 	    minsc = showsc < curcol ? showsc : curcol;
637 	    maxsr = maxrows;
638 	    maxsc = showsc > curcol ? showsc : curcol;
639 
640 	    if (showtop && !message) {
641 		char r[6];
642 
643 		strcpy(r, coltoa(minsc));
644 		strcat(r, ":");
645 		strcat(r, coltoa(maxsc));
646 		(void) clrtoeol();
647 		(void) printw("Default range:  %s", r);
648 	    }
649 	} else {
650 	    minsr = showsr < currow ? showsr : currow;
651 	    minsc = showsc < curcol ? showsc : curcol;
652 	    maxsr = showsr > currow ? showsr : currow;
653 	    maxsc = showsc > curcol ? showsc : curcol;
654 
655 	    if (showtop && !message) {
656 		(void) clrtoeol();
657 		(void) printw("Default range:  %s",
658 			    r_name(minsr, minsc, maxsr, maxsc));
659 	    }
660 	}
661     } else if (braille && braillealt && !message && mode_ind == 'v') {
662 	(void) clrtoeol();
663 	(void) printw("Current cell:   %s%d ", coltoa(curcol), currow);
664     }
665 
666     /* Repaint the visible screen */
667     if (showrange || anychanged || FullUpdate || standlast) {
668 	/* may be reset in loop, if not next time we will do a FullUpdate */
669       if (standlast) {
670       	FullUpdate = TRUE;
671 	standlast = FALSE;
672       }
673 
674       for (row = (ftoprows && strow >= fr->or_left->row ?
675 		  fr->or_left->row : strow), r = RESROW;
676 	    row <= mxrow; row++) {
677 	int c = rescol;
678 	int do_stand = 0;
679 	int fieldlen;
680 	int nextcol;
681 
682 	if (row_hidden[row])
683 	    continue;
684 	if (ftoprows && strow >= fr->or_left->row && r == RESROW + ftrows)
685 	    row = (strow < row ? row : strow);
686 	if (fbottomrows && r == lines - fbrows)
687 	    row = fr->or_right->row - fbottomrows + 1;
688 	for (pp = ATBL(tbl, row, col = (fleftcols && stcol >= fr->or_left->col ?
689 		fr->or_left->col : stcol));
690 		col <= mxcol;
691 		pp += nextcol - col,  col = nextcol, c += fieldlen) {
692 
693 	    if (fleftcols && stcol >= fr->or_left->col &&
694 		    c == rescol + flcols) {
695 		col = (stcol < col ? col : stcol);
696 		pp = ATBL(tbl, row, col);
697 	    }
698 	    if (frightcols && c + fwidth[col] >= cols - 1 - frcols &&
699 		    col < fr->or_right->col - frightcols + 1) {
700 		col = fr->or_right->col - frightcols + 1;
701 		pp = ATBL(tbl, row, col);
702 	    }
703 	    nextcol = col + 1;
704 	    if (col_hidden[col]) {
705 		fieldlen = 0;
706 		continue;
707 	    }
708 
709 	    fieldlen = fwidth[col];
710 
711 	    /*
712 	     * Set standout if:
713 	     *
714 	     * - showing ranges, and not showing cells which need to be filled
715 	     *	 in, and not showing cell expressions, and in a range, OR
716 	     *
717 	     * - showing cells which need to be filled in and this one is
718 	     *	 of that type (has a value and doesn't have an expression,
719 	     *	 or it is a string expression), OR
720 	     *
721 	     * - showing cells which have expressions and this one does.
722 	     */
723 	    if ((showrange && (!showneed) && (!showexpr)
724 			&& (row >= minsr) && (row <= maxsr)
725 			&& (col >= minsc) && (col <= maxsc))
726 		    || (showneed && (*pp) && ((*pp)->flags & is_valid) &&
727 			(((*pp)->flags & is_strexpr) || !((*pp)->expr)))
728 		    || (showexpr && (*pp) && ((*pp)->expr))
729 		    || (shownote && (*pp) && ((*pp)->nrow >= 0))) {
730 
731 		(void) move(r, c);
732 		(void) standout();
733 		if (color && has_colors() && (cr = find_crange(row, col)))
734 		    color_set(cr->r_color, NULL);
735 		standlast++;
736 		if (!*pp) {	/* no cell, but standing out */
737 		    (void) printw("%*s", fwidth[col], " ");
738 		    (void) standend();
739 		    if (color && has_colors())
740 			color_set(1, NULL);
741 		    continue;
742 		} else
743 		    do_stand = 1;
744 	    } else
745 		do_stand = 0;
746 
747 	    if ((cr = find_crange(row, col)) && color && has_colors())
748 		color_set(cr->r_color, NULL);
749 
750 	    if ((*pp) && (((*pp)->flags & is_changed || FullUpdate) ||
751 		    do_stand)) {
752 		if (do_stand) {
753 		    (*pp)->flags |= is_changed;
754 		} else {
755 		    (void) move(r, c);
756 		    (*pp)->flags &= ~is_changed;
757 		}
758 
759 		/*
760 		 * Show expression; takes priority over other displays:
761 		 */
762 
763 		if ((*pp)->cellerror) {
764 		    if (color && colorerr && has_colors())
765 			color_set(3, NULL);
766 		    (void) printw("%*.*s", fwidth[col], fwidth[col],
767 			(*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID");
768 		} else
769 		if (showexpr && ((*pp)->expr)) {
770 		    linelim = 0;
771 		    editexp(row, col);		/* set line to expr */
772 		    linelim = -1;
773 		    showstring(line, /* leftflush = */ 1, /* hasvalue = */ 0,
774 			    row, col, &nextcol, mxcol, &fieldlen, r, c,
775 			    fr, frightcols, flcols, frcols);
776 		} else {
777 		    /*
778 		     * Show cell's numeric value:
779                      */
780 
781 		    if ((*pp)->flags & is_valid) {
782 			char field[FBUFLEN];
783 			char *cfmt;
784 			int note;
785 
786 			*field = '\0';
787 			note = (*pp)->nrow >= 0 ? 1 : 0;
788 			cfmt = (*pp)->format ? (*pp)->format :
789 			    (realfmt[col] >= 0 && realfmt[col] < COLFORMATS &&
790 			    colformat[realfmt[col]]) ?
791 			    colformat[realfmt[col]] : NULL;
792 			if (color && has_colors() && colorneg && (*pp)->v < 0) {
793 			    if (cr)
794 				color_set(((cr->r_color) % CPAIRS) + 1, NULL);
795 			    else
796 				color_set(2, NULL);
797 			}
798 			if (cfmt) {
799 			    if (*cfmt == ctl('d')) {
800 				time_t v = (time_t) ((*pp)->v);
801 				strftime(field, sizeof(field),
802 					cfmt + 1, localtime(&v));
803 			    } else
804 				(void) format(cfmt, precision[col], (*pp)->v,
805 					field, sizeof(field));
806 			} else {
807 			    (void) engformat(realfmt[col], fwidth[col] - note,
808 				    precision[col], (*pp)->v,
809 				    field, sizeof(field));
810 			}
811 			if (strlen(field) > fwidth[col]) {
812 			    for (i = 0; i < fwidth[col]; i++) {
813 				if (note) {
814 				    attr_t attr;
815 				    short curcolor;
816 				    if (!i && color && has_colors()) {
817 					attr_get(&attr, &curcolor, NULL);
818 					color_set(4, NULL);
819 				    }
820 				    (void)addch('*');
821 				    i++;
822 				    if (!i && color && has_colors())
823 					color_set(curcolor, NULL);
824 				}
825 				(void)addch('*');
826 			    }
827 			} else {
828 			    if (cfmt && *cfmt != ctl('d'))
829 				for (i = 0;
830 					i < fwidth[col] - strlen(field) - note;
831 					i++)
832 				    (void)addch(' ');
833 			    if (note) {
834 				attr_t attr;
835 				short curcolor;
836 				if (color && has_colors()) {
837 				    attr_get(&attr, &curcolor, NULL);
838 				    color_set(4, NULL);
839 				}
840 				(void)addch('*');
841 				if (color && has_colors())
842 				    color_set(curcolor, NULL);
843 			    }
844 			    (void)addstr(field);
845 			    if (cfmt && *cfmt == ctl('d'))
846 				for (i = 0;
847 					i < fwidth[col] - strlen(field) - note;
848 					i++)
849 				    (void)addch(' ');
850 			}
851 		    }
852 
853 		    /*
854 		     * Show cell's label string:
855 		     */
856 
857 		    if ((*pp)->label) {
858 			showstring((*pp)->label,
859 				    (*pp)->flags & (is_leftflush|is_label),
860 				    (*pp)->flags & is_valid,
861 				    row, col, &nextcol, mxcol, &fieldlen,
862 				    r, c, fr, frightcols, flcols, frcols);
863 		    } else	/* repaint a blank cell: */
864 		    if ((((do_stand || !FullUpdate) &&
865 			    ((*pp)->flags & is_changed)) ||
866 			    (color && has_colors() &&
867 			    cr && cr->r_color != 1)) &&
868 			    !((*pp)->flags & is_valid) && !(*pp)->label) {
869 			(void) printw("%*s", fwidth[col], " ");
870 		    }
871 		} /* else */
872 	    } else
873 	    if (!*pp && color && has_colors && cr && cr->r_color != 1) {
874 		move(r, c);
875 		color_set(cr->r_color, NULL);
876 		printw("%*s", fwidth[col], " ");
877 	    }
878 	    if (color && has_colors())
879 		color_set(1, NULL);
880 	    if (do_stand) {
881 		(void) standend();
882 		do_stand = 0;
883 	    }
884 	}
885 	r++;
886       }
887     }
888 
889     /* place 'cursor marker' */
890     if (showcell && (!showneed) && (!showexpr) && (!shownote)) {
891 	(void) move(lastmy, lastmx);
892 	pp = ATBL(tbl, currow, curcol);
893 	if (color && has_colors()) {
894 	    if ((cr = find_crange(currow, curcol)))
895 		color_set(cr->r_color, NULL);
896 	    else
897 		color_set(1, NULL);
898 	    if (*pp) {
899 		if (colorneg && (*pp)->flags & is_valid && (*pp)->v < 0) {
900 		    if (cr)
901 			color_set(((cr->r_color) % CPAIRS) + 1, NULL);
902 		    else
903 			color_set(2, NULL);
904 		} else if (colorerr && (*pp)->cellerror)
905 		    color_set(3, NULL);
906 	    }
907 	}
908         repaint(lastmx, lastmy, fwidth[lastcol], A_STANDOUT, 0);
909 	if (color && has_colors())
910 	    color_set(1, NULL);
911     }
912 
913     repaint(lastmx, RESROW - 1, fwidth[lastcol], 0, A_STANDOUT);
914     repaint(0, lastmy, rescol - 1, 0, A_STANDOUT);
915 
916     (void) move(lastmy, lastmx+fwidth[lastcol]);
917     under_cursor = (inch() & A_CHARTEXT);
918     if (!showcell)
919 	(void) addch('<' | (inch() & A_ATTRIBUTES));
920 
921     (void) move(0, 0);
922     (void) clrtoeol();
923 
924     if (linelim >= 0) {
925 	int ctlchars;
926 
927 	for (i = ctlchars = 0; i < linelim; i++)
928 	    if ((unsigned char) line[i] < ' ')
929 		ctlchars++;
930 	(void) addch(mode_ind);
931 	(void) addch('>');
932 	(void) addch(search_ind);
933 	(void) addstr(line);
934 	if (!braille || (!message && mode_ind != 'v'))
935 	    (void) move((linelim+3+ctlchars)/cols, (linelim+3+ctlchars)%cols);
936 	else if (message)
937 	    move(1, 0);
938 	else if (braillealt)
939 	    move(1, 16);
940 	else
941 	    move(lastmy, lastmx);
942     } else {
943 	if (showtop) {			/* show top line */
944 	    register struct ent *p1;
945 	    int printed = 0;		/* printed something? */
946 
947 	    (void) printw("%s%d ", coltoa(curcol), currow);
948 
949 	    if ((p1 = *ATBL(tbl, currow, curcol)) && p1->nrow > -1)
950 		printw("{*%s} ", r_name(p1->nrow, p1->ncol,
951 			p1->nlastrow, p1->nlastcol));
952 
953 	    /* show the current cell's format */
954 	    if ((p1) && p1->format)
955 		printw("(%s) ", p1->format);
956 	    else
957 		printw("(%d %d %d) ", fwidth[curcol], precision[curcol],
958 				realfmt[curcol]);
959 
960 	    if (p1) {
961 		if (p1->expr) {
962 		    /* has expr of some type */
963 		    linelim = 0;
964 		    editexp(currow, curcol);	/* set line to expr */
965 		    linelim = -1;
966 		}
967 
968 		/*
969 		 * Display string part of cell:
970 		 */
971 
972 		if ((p1->expr) && (p1->flags & is_strexpr)) {
973  		    if (p1->flags & is_label)
974 			(void) addstr("|{");
975 		    else
976 			(void) addstr((p1->flags & is_leftflush) ? "<{" : ">{");
977 		    (void) addstr(line);
978 		    (void) addstr("} ");	/* and this '}' is for vi % */
979 		    printed = 1;
980 
981 		} else if (p1->label) {
982 		    /* has constant label only */
983 		    if (p1->flags & is_label)
984 			(void) addstr("|\"");
985 		    else
986 			(void) addstr((p1->flags & is_leftflush) ? "<\"" : ">\"");
987 		    (void) addstr(p1->label);
988 		    (void) addstr("\" ");
989 		    printed = 1;
990 		}
991 
992 		/*
993 		 * Display value part of cell:
994 		 */
995 
996 		if (p1->flags & is_valid) {
997 		    /* has value or num expr */
998 		    if ((!(p1->expr)) || (p1->flags & is_strexpr))
999 			(void) sprintf(line, "%.15g", p1->v);
1000 
1001 		    (void) addch('[');
1002 		    (void) addstr(line);
1003 		    (void) addch(']');
1004 		    *line = '\0'; /* this is the input buffer ! */
1005 		    printed = 1;
1006 		}
1007 	    }
1008 	    if (!printed)
1009 		(void) addstr("[]");
1010 	    /* Display if cell is locked */
1011 	    if (p1 && p1->flags&is_locked)
1012 		(void) addstr(" locked");
1013 	}
1014 	if (braille)
1015 	    if (message)
1016 		move(1, 0);
1017 	    else if (braillealt)
1018 		move(0, 0);
1019 	    else
1020 		move(lastmy, lastmx);
1021 	else if (showcell)
1022 	    move(lines - 1, cols - 1);
1023 	else
1024 	    (void) move(lastmy, lastmx+fwidth[lastcol]);
1025     }
1026 
1027     if (color && has_colors())
1028 	color_set(1, NULL);
1029 
1030     if (revmsg[0]) {
1031 	(void) move(0, 0);
1032 	(void) clrtoeol();	/* get rid of topline display */
1033 	(void) printw(revmsg);
1034 	*revmsg = '\0';		/* don't show it again */
1035 	if (braille)
1036 	    if (message)
1037 		move(1, 0);
1038 	    else if (braillealt)
1039 		move(0, 0);
1040 	    else
1041 		move(lastmy, lastmx);
1042 	else if (showcell)
1043 	    move(lines - 1, cols - 1);
1044 	else
1045 	    (void) move(lastmy, lastmx+fwidth[lastcol]);
1046     }
1047 
1048     if (color && has_colors())
1049 	color_set(1, NULL);
1050 
1051     if (revmsg[0]) {
1052 	(void) move(0, 0);
1053 	(void) clrtoeol();	/* get rid of topline display */
1054 	(void) printw(revmsg);
1055 	*revmsg = '\0';		/* don't show it again */
1056 	if (braille)
1057 	    if (message)
1058 		move(1, 0);
1059 	    else
1060 		move(lastmy, lastmx);
1061 	else if (showcell)
1062 	    move(lines - 1, cols - 1);
1063 	else
1064 	    (void) move(lastmy, lastmx + fwidth[lastcol]);
1065     }
1066 
1067     FullUpdate = FALSE;
1068 }
1069 
1070 /* redraw what is under the cursor from curses' idea of the screen */
1071 void
repaint(int x,int y,int len,int attron,int attroff)1072 repaint(int x, int y, int len, int attron, int attroff)
1073 {
1074     while (len-- > 0) {
1075 	(void) move(y, x);
1076 	addch((inch() | attron) & ~attroff);
1077 	x++;
1078     }
1079 }
1080 
1081 int seenerr;
1082 
1083 /* error routine for yacc (gram.y) */
1084 void
yyerror(char * err)1085 yyerror(char *err)
1086 {
1087     if (usecurses) {
1088 	if (seenerr) return;
1089 	seenerr++;
1090 	(void) move(1, 0);
1091 	(void) clrtoeol();
1092 	(void) printw("%s: %.*s<=%s", err, linelim, line, line + linelim);
1093     } else
1094 	(void) fprintf(stderr, "%s: %.*s<=%s\n", err, linelim, line,
1095 		       line + linelim);
1096 }
1097 
1098 #ifdef XENIX2_3
1099 struct termio tmio;
1100 #endif
1101 
1102 void
startdisp()1103 startdisp()
1104 {
1105 #if sun
1106     int	 fd;
1107     fd = dup(0);
1108 #endif
1109 #ifndef MSDOS
1110     if (usecurses) {
1111 #endif
1112 	int i;
1113 #ifdef TIOCGSIZE
1114 	{   struct ttysize size;
1115 	    if (ioctl(0, TIOCGSIZE, &size) == 0) {
1116 		lines = size.ts_lines;
1117 		cols = size.ts_cols;
1118 	    }
1119 	}
1120 #endif
1121 
1122 #ifdef XENIX2_3
1123 	(void) ioctl(fileno(stdin), TCGETA, & tmio);
1124 #endif
1125 	(void) initscr();
1126 	start_color();
1127 	for (i = 0; i < 8; i++)
1128 	    if (cpairs[i])
1129 		init_pair(i + 1, cpairs[i]->fg, cpairs[i]->bg);
1130 	if (color && has_colors())
1131 	    bkgdset(COLOR_PAIR(1) | ' ');
1132 #if sun
1133 	close(0);
1134 	dup(fd);
1135 	close(fd);
1136 #endif
1137 	(void) clear();
1138 #ifdef VMS
1139 	VMS_read_raw = 1;
1140 #else
1141 	nonl();
1142 	noecho();
1143 	cbreak();
1144 #endif
1145 	initkbd();
1146 	scrollok(stdscr, 1);
1147 
1148 #if defined(SYSV3) && !defined(NOIDLOK)
1149 # ifndef IDLOKBAD
1150 	/*
1151 	 * turn hardware insert/delete on, if possible.
1152 	 * turn on scrolling for systems with SYSVr3.{1,2} (SYSVr3.0 has
1153 	 * this set as the default)
1154 	 */
1155 	idlok(stdscr,TRUE);
1156 # else	/*
1157 	 * This seems to fix (with an empty spreadsheet):
1158 	 *	a) Redrawing the bottom half of the screen when you
1159 	 *		move between row 9 <-> 10
1160 	 *	b) the highlighted row labels being trash when you
1161 	 *		move between row 9 <-> 10
1162 	 *	c) On an xterm on Esix Rev. D+ from eating lines
1163 	 *	 -goto (or move) a few lines (or more) past the bottom
1164 	 *	 of the screen, goto (or move) to the top line on the
1165 	 *	 screen, move upward and the current line is deleted, the
1166 	 *	 others move up even when they should not, check by
1167 	 *	 noticing the rows become 2, 3, 40, 41, 42... (etc).
1168 	 */
1169 	idlok(stdscr,FALSE);
1170 # endif
1171 #endif
1172 
1173 	FullUpdate++;
1174 #ifndef MSDOS
1175     }
1176 #endif
1177 }
1178 
1179 void
stopdisp()1180 stopdisp()
1181 {
1182 #ifndef MSDOS
1183     if (usecurses) {
1184 #endif
1185 	deraw(1);
1186 	resetkbd();
1187 	endwin();
1188 #ifdef XENIX2_3
1189 	(void) ioctl(fileno(stdin), TCSETAW, & tmio);
1190 #endif
1191 #ifndef MSDOS
1192     }
1193 #endif
1194 }
1195 
1196 /* init curses */
1197 #ifdef VMS
1198 
1199 void
goraw()1200 goraw()
1201 {
1202     if (usecurses) {
1203 	VMS_read_raw = 1;
1204 	if (color && has_colors())
1205 	    bkgdset(COLOR_PAIR(1) | ' ');
1206 	FullUpdate++;
1207     }
1208 }
1209 
1210 void
deraw(int ClearLastLine)1211 deraw(int ClearLastLine)
1212 {
1213     if (usecurses) {
1214 	if (ClearLastLine) {
1215 	    if (color && has_colors())
1216 		bkgdset(COLOR_PAIR(0) | ' ');
1217 	    (void) move(lines - 1, 0);
1218 	    (void) clrtoeol();
1219 	    (void) refresh();
1220 	}
1221 	VMS_read_raw = 0;
1222     }
1223 }
1224 
1225 #else /* VMS */
1226 void
goraw()1227 goraw()
1228 {
1229     if (usecurses) {
1230 #if SYSV2 || SYSV3
1231 	fixterm();
1232 #else /* SYSV2 || SYSV3 */
1233 	cbreak();
1234 	nonl();
1235 	noecho ();
1236 #endif /* SYSV2 || SYSV3 */
1237 	kbd_again();
1238 	if (color && has_colors())
1239 	    bkgdset(COLOR_PAIR(1) | ' ');
1240 	FullUpdate++;
1241     }
1242 }
1243 
1244 /* clean up curses */
1245 void
deraw(int ClearLastLine)1246 deraw(int ClearLastLine)
1247 {
1248     if (usecurses) {
1249 	if (ClearLastLine) {
1250 	    if (color && has_colors())
1251 		bkgdset(COLOR_PAIR(0) | ' ');
1252 	    (void) move(lines - 1, 0);
1253 	    (void) clrtoeol();
1254 	    (void) refresh();
1255 	}
1256 #if SYSV2 || SYSV3
1257 	resetterm();
1258 #else
1259 	nocbreak();
1260 	nl();
1261 	echo();
1262 #endif
1263 	resetkbd();
1264     }
1265 }
1266 
1267 #endif /* VMS */
1268