1 /************************************************************************
2  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
3  * provided to you without charge, and with no warranty.  You may give  *
4  * away copies of JOVE, including sources, provided that this notice is *
5  * included in all the files.                                           *
6  ************************************************************************/
7 
8 #include "jove.h"
9 #include "jctype.h"
10 #include "chars.h"
11 #include "fp.h"
12 #include "disp.h"
13 #include "ask.h"
14 #include "extend.h"
15 #include "fmt.h"
16 #include "insert.h"
17 /* #include "io.h" */	/* for pwd() */
18 #ifdef IPROCS
19 # include "sysprocs.h"
20 # include "iproc.h"
21 #endif
22 #include "move.h"
23 #include "macros.h"
24 #include "screen.h"
25 #include "term.h"
26 #include "wind.h"
27 
28 #ifdef MAC
29 # include "mac.h"
30 #else
31 # include <sys/stat.h>
32 #endif
33 
34 /* define a couple of unique daddrs */
35 #define	NOWHERE_DADDR	(~NULL_DADDR | DDIRTY)	/* not in tmp file */
36 #define	UNSAVED_CURLINE_DADDR	((NOWHERE_DADDR - 1) | DDIRTY)	/* not yet in tmp file */
37 
38 struct screenline
39 	*Screen = NULL,	/* the screen (a bunch of screenline) */
40 	*Curline = NULL;	/* current line */
41 
42 private void
43 	DeTab proto((char *, int, char *, char *, bool)),
44 	DoIDline proto((int)),
45 	do_cl_eol proto((int)),
46 	ModeLine proto((Window *, char *, int)),
47 	GotoDot proto((void)),
48 	UpdLine proto((int)),
49 	UpdWindow proto((Window *, int));
50 
51 #ifdef ID_CHAR
52 private void
53 	DelChar proto((int, int, int)),
54 	InsChar proto((int, int, int, char *));
55 
56 private bool
57 	IDchar proto ((char *, int)),
58 	OkayDelete proto ((int, int, bool)),
59 	OkayInsert proto ((int, int));
60 
61 private int
62 	NumSimilar proto ((char *, char *, int)),
63 	IDcomp proto ((char *, char *, int));
64 #endif /* ID_CHAR */
65 
66 private bool
67 	AddLines proto((int, int)),
68 	DelLines proto((int, int));
69 
70 bool	DisabledRedisplay = NO;
71 
72 /* Kludge windows gets called by the routines that delete lines from the
73    buffer.  If the w->w_line or w->w_top are deleted and this procedure
74    is not called, the redisplay routine will barf. */
75 
76 void
ChkWindows(line1,line2)77 ChkWindows(line1, line2)
78 LinePtr	line1,
79 	line2;
80 {
81 	register Window	*w = fwind;
82 	register LinePtr	lp,
83 			lend = line2->l_next;
84 
85 	do {
86 		if (w->w_bufp == curbuf) {
87 			for (lp = line1->l_next; lp != lend; lp = lp->l_next) {
88 				if (lp == w->w_top)
89 					w->w_flags |= W_TOPGONE;
90 				if (lp == w->w_line)
91 					w->w_flags |= W_CURGONE;
92 			}
93 		}
94 		w = w->w_next;
95 	} while (w != fwind);
96 }
97 
98 
99 #ifdef WINRESIZE
100 
101 volatile bool
102 	ResizePending = NO;	/* asynch request for screen resize */
103 
104 private void
resize()105 resize()
106 {
107 	bool	oldDisabledRedisplay = DisabledRedisplay;
108 	int
109 		oldILI = ILI,
110 		oldCO = CO;
111 
112 	DisabledRedisplay = YES;	/* prevent tragedy */
113 	ResizePending = NO;	/* early & safe */
114 	ttsize();	/* update line (LI and ILI) and col (CO) info. */
115 
116 	if (oldILI != ILI || oldCO != CO) {
117 		register int	total;
118 		register Window	*wp;
119 
120 		/* Go through the window list, changing each window size in
121 		 * proportion to the resize.  If the window would become too
122 		 * small, we delete it.
123 		 *
124 		 * Actually, we must do the deletion in
125 		 * a separate pass because del_wind donates the space to either
126 		 * neighbouring window.  We test the windows in a funny order:
127 		 * top-most, then bottom-up.  Although it isn't necessary
128 		 * for correctness, it means that we consider any donated
129 		 * space, cutting down the number of windows we decide to delete.
130 		 * Loop termination is tricky: fwind may have changed due to a
131 		 * del_wind.  As a simple fix, we start over whenever we
132 		 * delete a window.  Although this is O(n**2), it can't
133 		 * really be expensive.
134 		 *
135 		 * After scaling all the windows, we
136 		 * give any space remaining to the current window (which would
137 		 * have changed if the old current window had been deleted).
138 		 *
139 		 * This seems fairer than just resizing the current window.
140 		 */
141 		wp = fwind;
142 		for (;;) {
143 			int	newsize = ILI * wp->w_height / oldILI;
144 
145 			if (newsize < 2) {
146 				del_wind(wp);
147 				wp = fwind;
148 			} else {
149 				wp = wp->w_prev;
150 				if (wp == fwind)
151 					break;
152 			}
153 		}
154 
155 		total = 0;
156 		do {
157 			int	newsize = ILI * wp->w_height / oldILI;
158 
159 			wp->w_height = newsize;
160 			total += newsize;
161 			wp = wp->w_next;
162 		} while (wp != fwind);
163 
164 		curwind->w_height += ILI - total;
165 
166 		/* Make a new screen structure */
167 		make_scr();
168 
169 		/* Do a 'hard' update on the screen - clear and redraw */
170 		ClAndRedraw();
171 #ifdef WIN32
172 		ResizeWindow();
173 #endif
174 	}
175 	DisabledRedisplay = oldDisabledRedisplay;
176 }
177 
178 #endif /* WINRESIZE */
179 
180 private bool	RingBell;	/* So if we have a lot of errors ...
181 				  ring the bell only ONCE */
182 
183 void
redisplay()184 redisplay()
185 {
186 	if (DisabledRedisplay)
187 		return;
188 #ifdef WINRESIZE
189 	do
190 #endif
191 	{
192 		register Window	*w;
193 		int
194 			lineno,
195 			i;
196 		bool
197 			done_ID = NO,
198 			old_UpdModLine;
199 		register struct scrimage
200 			*des_p,
201 			*phys_p;
202 
203 #ifdef WINRESIZE
204 		if (ResizePending)
205 			resize();
206 #endif
207 		curwind->w_line = curwind->w_bufp->b_dot;
208 		curwind->w_char = curwind->w_bufp->b_char;
209 #ifdef MAC
210 		/* To avoid calling redisplay() recursively,
211 		 * we must avoid calling CheckEvent(),
212 		 * so we must avoid calling charp().
213 		 */
214 		InputPending = NO;
215 #else
216 		if (PreEmptOutput())
217 			return;
218 #endif
219 		if (RingBell) {
220 			dobell(1);
221 			RingBell = NO;
222 		}
223 		AbortCnt = ScrBufSize;		/* initialize this now */
224 		if (UpdMesg)
225 			DrawMesg(YES);
226 
227 		for (lineno = 0, w = fwind; lineno < ILI; w = w->w_next) {
228 			UpdWindow(w, lineno);
229 			lineno += w->w_height;
230 		}
231 
232 		/* Now that we've called update window, we can
233 		   assume that the modeline will be updated.  But
234 		   if while redrawing the modeline the user types
235 		   a character, ModeLine() is free to set this on
236 		   again so that the modeline will be fully drawn
237 		   at the next redisplay.  Furthermore, if output
238 		   is preempted, we'll restore the old value because
239 		   we can't be sure that the updating has happened. */
240 
241 		old_UpdModLine = UpdModLine;
242 		UpdModLine = NO;
243 
244 		des_p = DesiredScreen;
245 		phys_p = PhysScreen;
246 		for (i = 0; i < ILI; i++, des_p++, phys_p++) {
247 			if (!done_ID && (des_p->s_id != phys_p->s_id)) {
248 				DoIDline(i);
249 				done_ID = YES;
250 			}
251 			if ((des_p->s_flags & (s_DIRTY | s_L_MOD))
252 			|| des_p->s_id != phys_p->s_id
253 			|| des_p->s_vln != phys_p->s_vln
254 			|| des_p->s_offset != phys_p->s_offset)
255 				UpdLine(i);
256 			if (CheapPreEmptOutput()) {
257 				if (old_UpdModLine)
258 					UpdModLine = YES;
259 				goto suppress;
260 			}
261 		}
262 
263 		if (Asking) {
264 			Placur(ILI, min(CO - 2, calc_pos(mesgbuf, AskingWidth)));
265 				/* Nice kludge */
266 			flushscreen();
267 		} else {
268 			GotoDot();
269 		}
270 suppress: ;
271 	}
272 #ifdef WINRESIZE
273 	/**/ while (ResizePending);
274 #endif
275 #ifdef MAC
276 	if (Windchange)
277 		docontrols();
278 #endif
279 }
280 
281 /* find_pos() returns the position on the line, that C_CHAR represents
282    in LINE */
283 
284 private int
find_pos(line,c_char)285 find_pos(line, c_char)
286 LinePtr	line;
287 int	c_char;
288 {
289 	return calc_pos(lcontents(line), c_char);
290 }
291 
292 /* calc_pos calculates the screen column of character c_char.
293  *
294  * Note: the calc_pos, how_far, and DeTab must be in synch --
295  * each thinks it knows how characters are displayed.
296  */
297 
298 int
calc_pos(lp,c_char)299 calc_pos(lp, c_char)
300 register char	*lp;
301 register int	c_char;
302 {
303 	register int	pos = 0;
304 	register ZXchar	c;
305 
306 	while ((--c_char >= 0) && (c = ZXC(*lp++)) != 0) {
307 		if (c == '\t' && tabstop != 0) {
308 			pos += TABDIST(pos);
309 		} else if (jisprint(c)) {
310 			pos += 1;
311 		} else {
312 			if (c <= DEL)
313 				pos += 2;
314 			else
315 				pos += 4;
316 		}
317 	}
318 	return pos;
319 }
320 
321 volatile bool	UpdModLine = NO;
322 bool	UpdMesg = NO;
323 
324 private void
DoIDline(start)325 DoIDline(start)
326 int	start;
327 {
328 	register struct scrimage	*des_p = &DesiredScreen[start];
329 	struct scrimage	*phys_p = &PhysScreen[start];
330 	register int	i,
331 			j;
332 
333 	/* Some changes have been made.  Try for insert or delete lines.
334 	   If either case has happened, Addlines and/or DelLines will do
335 	   necessary scrolling, also CONVERTING PhysScreen to account for the
336 	   physical changes.  The comparison continues from where the
337 	   insertion/deletion takes place; this doesn't happen very often,
338 	   usually it happens with more than one window with the same
339 	   buffer. */
340 
341 #ifdef TERMCAP
342 	if (!CanScroll)
343 		return;		/* We should never have been called! */
344 #endif
345 
346 	for (i = start; i < ILI; i++, des_p++, phys_p++)
347 		if (des_p->s_id != phys_p->s_id)
348 			break;
349 
350 	for (; i < ILI; i++) {
351 		for (j = i + 1; j < ILI; j++) {
352 			des_p = &DesiredScreen[j];
353 			phys_p = &PhysScreen[j];
354 			if (des_p->s_id != NULL_DADDR && des_p->s_id == phys_p->s_id)
355 				break;
356 			if (des_p->s_id == PhysScreen[i].s_id) {
357 				if (des_p->s_id == NULL_DADDR)
358 					continue;
359 				if (AddLines(i, j - i)) {
360 					DoIDline(j);
361 					return;
362 				}
363 				break;
364 			}
365 			if ((des_p = &DesiredScreen[i])->s_id == phys_p->s_id) {
366 				if (des_p->s_id == NULL_DADDR)
367 					continue;
368 				if (DelLines(i, j - i)) {
369 					DoIDline(i);
370 					return;
371 				}
372 				break;
373 			}
374 		}
375 	}
376 }
377 
378 /* Make DesiredScreen reflect what the screen should look like when we are done
379    with the redisplay.  This deals with horizontal scrolling.  Also makes
380    sure the current line of the Window is in the window. */
381 
382 bool	ScrollAll = NO;	/* VAR: when current line scrolls, scroll whole window? */
383 int	ScrollWidth = 10;	/* VAR: unit of horizontal scrolling */
384 
385 private void
UpdWindow(w,start)386 UpdWindow(w, start)
387 register Window	*w;
388 int	start;
389 {
390 	LinePtr	lp;
391 	int	i,
392 		upper,		/* top of window */
393 		lower,		/* bottom of window */
394 		strt_col,	/* starting print column of current line */
395 		ntries = 0;	/* # of tries at updating window */
396 	register struct scrimage	*des_p,
397 					*phys_p;
398 	Buffer	*bp = w->w_bufp;
399 
400 	do {
401 		if (w->w_flags & W_CURGONE) {
402 			w->w_line = bp->b_dot;
403 			w->w_char = bp->b_char;
404 		}
405 		if (w->w_flags & W_TOPGONE)
406 			CentWind(w);	/* reset topline of screen */
407 		w->w_flags &= ~(W_CURGONE | W_TOPGONE);
408 
409 		/* make sure that the current line is in the window */
410 		upper = start;
411 		lower = upper + WSIZE(w);
412 		for (i = upper, lp = w->w_top; ; lp = lp->l_next, i++) {
413 			if (i == lower || lp == NULL) {
414 				/* we've run out of window without finding dot */
415 				ntries += 1;
416 				if (ntries == 1) {
417 					CalcWind(w);
418 				} else if (ntries == 2) {
419 					w->w_top = w->w_line = w->w_bufp->b_first;
420 					writef("\rERROR in redisplay: I got hopelessly lost!");
421 					dobell(2);
422 				} else {
423 					writef("\n\rOops, still lost, quitting ...\r\n");
424 					finish(-1);	/* die! */
425 					/*NOTREACHED*/
426 				}
427 				break;
428 			}
429 			if (lp == w->w_line) {
430 				ntries = 0;	/* happiness: dot is in window */
431 				break;
432 			}
433 		}
434 	} while (ntries != 0);
435 
436 	/* first do some calculations for the current line */
437 	{
438 		int
439 			nw = W_NUMWIDTH(w),
440 			dot_col,
441 			end_col;
442 
443 		strt_col = ScrollAll? w->w_LRscroll : PhysScreen[i].s_offset;
444 		end_col = strt_col + (CO - 1) - (nw + SIWIDTH(strt_col));
445 		/* Right now we are displaying from strt_col to
446 		 * end_col of the buffer line.  These are PRINT
447 		 * columns, not actual characters.
448 		 */
449 		dot_col = w->w_dotcol = find_pos(w->w_line, w->w_char);
450 		/* if the new dotcol is out of range, reselect
451 		 * a horizontal window
452 		 */
453 		if (PhysScreen[i].s_offset == -1
454 	    || !(strt_col <= dot_col && dot_col < end_col))
455 		{
456 			/* If dot_col is within first step left of screen, step left.
457 			 * Otherwise, if ditto for right.
458 			 * Otherwise, if it is in first screenwidth, start from begining.
459 			 * Otherwise, center dot_col.
460 			 * Fudge: if a scroll left would work except for the necessary
461 			 * appearance of an ! on the left, we scroll an extra column.
462 			 */
463 			int
464 				step = min(ScrollWidth, end_col - strt_col);
465 
466 			strt_col =
467 				strt_col > dot_col && strt_col - step <= dot_col
468 					? max(strt_col - step, 0)
469 				: dot_col >= end_col && dot_col < end_col + step
470 					? min(strt_col + step
471 					  + (strt_col == 0 && dot_col == end_col + step - 1? 1 : 0)
472 					  , dot_col)
473 				: dot_col < ((CO - 1) - nw)
474 					? 0
475 				: dot_col - ((CO - nw) / 2);
476 
477 			if (ScrollAll) {
478 				if (w->w_LRscroll != strt_col)
479 					UpdModLine = YES;
480 				w->w_LRscroll = strt_col;
481 			}
482 		}
483 		w->w_dotline = i;
484 		w->w_dotcol = dot_col + nw + SIWIDTH(strt_col);
485 	}
486 
487 	lp = w->w_top;
488 	des_p = &DesiredScreen[upper];
489 	phys_p = &PhysScreen[upper];
490 	for (i = upper; i < lower; i++, des_p++, phys_p++) {
491 		if (lp != NULL) {
492 			des_p->s_offset = (lp == w->w_line)? strt_col : w->w_LRscroll;
493 			des_p->s_flags = isdirty(lp) ? s_L_MOD : 0;
494 			des_p->s_vln = (w->w_flags & W_NUMLINES)?
495 				w->w_topnum + (i - upper) : 0;
496 			des_p->s_id = (lp == curline && DOLsave)?
497 				UNSAVED_CURLINE_DADDR : lp->l_dline & ~DDIRTY;
498 			des_p->s_lp = lp;
499 			lp = lp->l_next;
500 		} else {
501 			/* display line beyond end of buffer */
502 			static const struct scrimage
503 				clean_plate = { 0, 0, 0, NULL_DADDR, NULL, NULL };
504 
505 			*des_p = clean_plate;
506 			if (phys_p->s_id != NULL_DADDR)
507 				des_p->s_flags = s_DIRTY;
508 		}
509 		des_p->s_window = w;
510 	}
511 
512 	/* mode line: */
513 
514 	/* ??? The following assignment to des_p->s_id is very questionable:
515 	 * it stores a pointer in a daddr variable!
516 	 *
517 	 * We count on the cast pointer value being distinct from
518 	 * any other daddr, but equal to itself.  Turning
519 	 * the "DDIRTY" bit on should ensure that it is distinct
520 	 * from legitimate daddr values (except for NOWHERE_DADDR
521 	 * and UNSAVED_CURLINE_DADDR).
522 	 * If sizeof(Buffer *)>sizeof(daddr), nothing ensures that
523 	 * these pointers are even distinct from each other.
524 	 *
525 	 * There also seems to be an assumption that every modeline
526 	 * for a particular buffer will be the same.  This is not
527 	 * always the case: the last modeline on the screen is usually
528 	 * different from any other modeline, even for the same buffer.
529 	 * Currently, I think that only very contrived cases could cause
530 	 * problems (probably involving window resizing).
531 	 * Further problems will arise if JOVE is changed so that there are
532 	 * other ways in which a modeline can reflect the window state
533 	 * (instead of just the buffer state).
534 	 *
535 	 * -- DHR
536 	 */
537 	des_p->s_window = w;
538 	des_p->s_id = (daddr) w->w_bufp | DDIRTY;
539 	des_p->s_flags = (des_p->s_id != phys_p->s_id || UpdModLine)?
540 		s_MODELINE | s_DIRTY : 0;
541 	des_p->s_offset = 0;
542 	des_p->s_vln = 0;
543 	des_p->s_lp = NULL;
544 
545 #ifdef MAC
546 	if (UpdModLine)
547 		Modechange = YES;
548 	if (w == curwind && w->w_control != NULL)
549 		SetScrollBar(w);
550 #endif
551 }
552 
553 /* Write whatever is in mesgbuf (maybe we are Asking, or just printed
554    a message).  Turns off the UpdateMesg line flag. */
555 
556 void
DrawMesg(abortable)557 DrawMesg(abortable)
558 bool	abortable;
559 {
560 	char	outbuf[MAXCOLS + PPWIDTH];	/* assert(CO <= MAXCOLS); */
561 
562 #ifndef MAC		/* same reason as in redisplay() */
563 	if (PreEmptOutput())
564 		return;
565 #endif
566 	i_set(ILI, 0);
567 	DeTab(mesgbuf, 0, outbuf, outbuf + CO, NO);
568 	if (swrite(outbuf, NOEFFECT, abortable)) {
569 		cl_eol();
570 		UpdMesg = NO;
571 	}
572 	flushscreen();
573 }
574 
575 /* Goto the current position in the current window.  Presumably redisplay()
576    has already been called, and curwind->{w_dotline,w_dotcol} have been set
577    correctly. */
578 
579 private void
GotoDot()580 GotoDot()
581 {
582 	if (!CheapPreEmptOutput()) {
583 		Placur(curwind->w_dotline,
584 			curwind->w_dotcol - PhysScreen[curwind->w_dotline].s_offset);
585 		flushscreen();
586 	}
587 }
588 
589 private int
UntilEqual(start)590 UntilEqual(start)
591 register int	start;
592 {
593 	register struct scrimage	*des_p = &DesiredScreen[start],
594 					*phys_p = &PhysScreen[start];
595 
596 	while ((start < ILI) && (des_p->s_id != phys_p->s_id)) {
597 		des_p += 1;
598 		phys_p += 1;
599 		start += 1;
600 	}
601 
602 	return start;
603 }
604 
605 /* Calls the routine to do the physical changes, and changes PhysScreen to
606    reflect those changes. */
607 
608 private bool
AddLines(at,num)609 AddLines(at, num)
610 register int	at,
611 		num;
612 {
613 	register int	i;
614 	int	bottom = UntilEqual(at + num);
615 
616 	if (num == 0 || num >= ((bottom - 1) - at))
617 		return NO;				/* we did nothing */
618 	v_ins_line(num, at, bottom - 1);
619 
620 	/* Now change PhysScreen to account for the physical change. */
621 
622 	for (i = bottom - 1; i - num >= at; i--)
623 		PhysScreen[i] = PhysScreen[i - num];
624 	for (i = 0; i < num; i++)
625 		PhysScreen[at + i].s_id = NULL_DADDR;
626 	return YES;					/* we did something */
627 }
628 
629 private bool
DelLines(at,num)630 DelLines(at, num)
631 register int	at,
632 		num;
633 {
634 	register int	i;
635 	int	bottom = UntilEqual(at + num);
636 
637 	if (num == 0 || num >= ((bottom - 1) - at))
638 		return NO;
639 	v_del_line(num, at, bottom - 1);
640 
641 	for (i = at; num + i < bottom; i++)
642 		PhysScreen[i] = PhysScreen[num + i];
643 	for (i = bottom - num; i < bottom; i++)
644 		PhysScreen[i].s_id = NULL_DADDR;
645 	return YES;
646 }
647 
648 bool	MarkHighlighting = YES;	/* VAR: highlight mark when visible */
649 
650 /* Update line linenum in window w.  Only set PhysScreen to DesiredScreen
651    if the swrite or cl_eol works, that is nothing is interrupted by
652    characters typed. */
653 
654 private void
UpdLine(linenum)655 UpdLine(linenum)
656 register int	linenum;
657 {
658 	register struct scrimage	*des_p = &DesiredScreen[linenum];
659 	register Window	*w = des_p->s_window;
660 	char	outbuf[MAXCOLS + PPWIDTH];	/* assert(CO <= MAXCOLS); */
661 
662 	i_set(linenum, 0);
663 	if (des_p->s_flags & s_MODELINE) {
664 		ModeLine(w, outbuf, linenum);
665 	} else if (des_p->s_id != NULL_DADDR) {
666 		char	*lptr;
667 		int	fromcol = W_NUMWIDTH(w);
668 #ifdef HIGHLIGHTING
669 		static struct LErange lr = {0, 0, NULL, US_effect};
670 		Mark	*mark = b_curmark(w->w_bufp);
671 		bool	marked_line = (MarkHighlighting
672 # ifdef TERMCAP
673 			       && US != NULL
674 # endif
675 			       && mark != NULL
676 			       && mark->m_line == des_p->s_lp);
677 #endif /* HIGHLIGHTING */
678 
679 		des_p->s_lp->l_dline &= ~DDIRTY;
680 		des_p->s_flags &= ~(s_DIRTY | s_L_MOD);
681 
682 		if (w->w_flags & W_NUMLINES)
683 			swritef(outbuf, sizeof(outbuf), "%6d  ", des_p->s_vln);
684 		if (des_p->s_offset != 0) {
685 			outbuf[fromcol++] = '!';
686 			outbuf[fromcol] = '\0';
687 		}
688 		lptr = lcontents(des_p->s_lp);
689 		DeTab(lptr, des_p->s_offset, outbuf + fromcol,
690 		      outbuf + CO, (w->w_flags & W_VISSPACE) != 0);
691 #ifdef HIGHLIGHTING
692 		if (marked_line) {
693 		    lr.start = calc_pos(lptr, mark->m_char)
694 				    - des_p->s_offset + fromcol;
695 		    lr.width = 1;
696 			if (lr.start < sizeof(outbuf) - 1 && outbuf[lr.start] == '\0') {
697 				outbuf[lr.start] = ' ';
698 				outbuf[lr.start + 1] = '\0';
699 			}
700 		}
701 #endif /* HIGHLIGHTING */
702 #ifdef ID_CHAR
703 		/* REMIND: This code, along with the rest of the
704 		   ID_CHAR, belongs in the screen driver for
705 		   termcap based systems.  mac and pc's and other
706 		   window-based drivers don't give a hoot about
707 		   ID_CHAR. */
708 
709 		/* attempt to exploit insert or delete character capability
710 		 * but only if not highlighting some part of the line
711 		 */
712 		if (UseIC && Curline->s_effects == NOEFFECT
713 # ifdef HIGHLIGHTING
714 		&& !marked_line
715 # endif /* HIGHLIGHTING */
716 		) {
717 			if (IDchar(outbuf, linenum)) {
718 				/* success: clean up and go home */
719 				PhysScreen[linenum] = *des_p;
720 				return;
721 			}
722 			/* failure: re-initialize various cursors */
723 			i_set(linenum, 0);
724 		}
725 #endif /* ID_CHAR */
726 
727 		if (swrite(outbuf,
728 #ifdef HIGHLIGHTING
729 			marked_line ? &lr : NOEFFECT,
730 #else
731 			NOEFFECT,
732 #endif
733 			YES))
734 		{
735 			do_cl_eol(linenum);
736 		} else {
737 			/* interrupted: mark indeterminate state */
738 			PhysScreen[linenum].s_id = NOWHERE_DADDR;
739 		}
740 	} else if (PhysScreen[linenum].s_id != NULL_DADDR) {
741 		/* not the same ... make sure */
742 		do_cl_eol(linenum);
743 	}
744 }
745 
746 private void
do_cl_eol(linenum)747 do_cl_eol(linenum)
748 register int	linenum;
749 {
750 	cl_eol();
751 	PhysScreen[linenum] = DesiredScreen[linenum];
752 }
753 
754 /* Expand tabs (and other funny characters) of a section of "buf"
755  * into "outbuf".
756  *
757  * Note: outbuf must allow for at least PPWIDTH extra characters.
758  * This is sufficient room for one extra character to be displayed,
759  * streamlining the code.
760  *
761  * Note: the calc_pos, how_far, and DeTab must be in synch --
762  * each thinks it knows how characters are displayed.
763  */
764 
765 private void
DeTab(src,start_offset,dst,dst_limit,visspace)766 DeTab(src, start_offset, dst, dst_limit, visspace)
767 char	*src;
768 int	start_offset;
769 char	*dst;
770 char	*dst_limit;
771 bool	visspace;
772 {
773 	ZXchar	c;
774 	int	offset = start_offset;
775 
776 	/* At any time, the number of characters we've output is
777 	   start_offset - offset.  This is needed to correctly
778 	   calculate TABDIST() without having to add another
779 	   variable (pos) to be incremented for each call to addc. */
780 
781 #define addc(ch) { if (--offset < 0) *dst++ = (ch); }
782 
783 	while ((c = ZXC(*src++)) != '\0') {
784 		if (c == '\t' && tabstop != 0) {
785 			int	nchars = TABDIST(start_offset - offset);
786 
787 			c = visspace? '>' : ' ';
788 			while (--nchars > 0 && dst < dst_limit) {
789 				addc(c);
790 				c = ' ';
791 			}
792 		} else if (jisprint(c)) {
793 			if (visspace && c == ' ')
794 				c = '_';
795 		} else {
796 			char	buf[PPWIDTH];
797 			char	*p;
798 
799 			PPchar(c, buf);
800 			/* assert(buf[0] != '\0'); */
801 			for (p = buf; (c = *p++), *p != '\0'; )
802 				addc(c);
803 		}
804 		if (--offset < 0) {
805 			*dst++ = c;
806 			if (dst >= dst_limit) {
807 				/* we've run out of real estate: truncate and flag it */
808 				dst = dst_limit-1;
809 				*dst++ = '!';
810 				break;
811 			}
812 		}
813 	}
814 #undef	addc
815 	*dst = '\0';
816 }
817 
818 
819 #ifdef ID_CHAR
820 
821 /* From here to the end of the file is code that tries to utilize the
822    insert/delete character feature on some terminals.  It is very confusing
823    and not so well written code, AND there is a lot of it.  You may want
824    to use the space for something else. */
825 
826 bool	IN_INSmode = NO;
827 
828 void
INSmode(on)829 INSmode(on)
830 bool	on;
831 {
832 	if (on != IN_INSmode) {
833 		putpad(on? IM : EI, 1);
834 		IN_INSmode = on;
835 	}
836 }
837 
838 /* ID character routines full of special cases and other fun stuff like that.
839    It actually works though ...
840 
841 	Returns Non-Zero if you are finished (no differences left). */
842 
843 private bool
IDchar(new,lineno)844 IDchar(new, lineno)
845 register char	*new;
846 int	lineno;
847 {
848 	register int	col = 0;
849 	struct screenline	*sline = &Screen[lineno];
850 	register char	*old = sline->s_line;
851 	int	newlen = strlen(new);
852 
853 	for (;;) {
854 		int	oldlen = sline->s_roof - old;
855 		int	i;
856 
857 		for (; ; col++) {
858 			if (col == oldlen || col == newlen)
859 				return oldlen == newlen;	/* one ended; happy if both ended */
860 
861 			if (old[col] != new[col])
862 				break;
863 		}
864 
865 		/* col now is first difference, and not the end of either */
866 
867 		/* see if an insertion will help */
868 
869 		for (i = col + 1; i < newlen; i++) {
870 			if (new[i] == old[col]) {
871 				/* The number of saved characters is (roughly)
872 				 * the number of characters we can retain after
873 				 * the insertion, minus the number that we
874 				 * could have salvaged without moving them.
875 				 */
876 				int	NumSaved = IDcomp(new + i, old + col, oldlen-col)
877 						- NumSimilar(new + col, old + col, min(i, oldlen)-col);
878 
879 				if (OkayInsert(NumSaved, i - col)) {
880 					InsChar(lineno, col, i - col, new);
881 					col = i;
882 					break;
883 				}
884 			}
885 		}
886 		if (i != newlen)
887 			continue;
888 
889 		/* see if a deletion will help */
890 
891 		for (i = col + 1; i < oldlen; i++) {
892 			if (new[col] == old[i]) {
893 				int	NumSaved = IDcomp(new + col, old + i, oldlen - i);
894 
895 				if (OkayDelete(NumSaved, i - col, newlen == oldlen)) {
896 					DelChar(lineno, col, i - col);
897 					break;
898 				}
899 			}
900 		}
901 		if (i != oldlen)
902 			continue;
903 		return NO;
904 	}
905 }
906 
907 private int
NumSimilar(s,t,n)908 NumSimilar(s, t, n)
909 register char	*s,
910 		*t;
911 int	n;
912 {
913 	register int	num = 0;
914 
915 	while (n--)
916 		if (*s++ == *t++)
917 			num += 1;
918 	return num;
919 }
920 
921 private int
IDcomp(s,t,len)922 IDcomp(s, t, len)
923 register char	*s,	/* NUL terminated */
924 		*t;	/* len chars */
925 int	len;
926 {
927 	register int	i;
928 	int	num = 0,
929 		nonspace = 0;
930 
931 	for (i = 0; i < len; i++) {
932 		char	c = *s++;
933 
934 		if (c == '\0' || c != *t++)
935 			break;
936 		if (c != ' ')
937 			nonspace = 1;
938 		num += nonspace;
939 	}
940 
941 	return num;
942 }
943 
944 private bool
OkayDelete(Saved,num,samelength)945 OkayDelete(Saved, num, samelength)
946 int	Saved,
947 	num;
948 bool	samelength;
949 {
950 	/* If the old and the new have different lengths, then the competition
951 	 * will have to clear to end of line.  We take that into consideration.
952 	 */
953 	return Saved + (samelength ? 0 : CElen) > min(MDClen, DClen * num);
954 }
955 
956 private bool
OkayInsert(Saved,num)957 OkayInsert(Saved, num)
958 int	Saved,
959 	num;
960 {
961 	register int	n = 0;
962 
963 	/* Note: the way termcap/terminfo is defined, we must use *both*
964 	 * IC and IM to insert, but normally only one will be defined.
965 	 * See terminfo(5), under the heading "Insert/Delete Character".
966 	 */
967 	if (IC != NULL)		/* Per character prefixes */
968 		n = min(num * IClen, MIClen);
969 
970 	if (!IN_INSmode)
971 		n += IMlen;
972 
973 	n += num;	/* The characters themselves */
974 
975 	return Saved > n;
976 }
977 
978 private void
DelChar(lineno,col,num)979 DelChar(lineno, col, num)
980 int	lineno,
981 	col,
982 	num;
983 {
984 	register char	*from,
985 			*to;
986 	struct screenline *sp = (&Screen[lineno]);
987 
988 	Placur(lineno, col);
989 	putmulti(DC, M_DC, num, 1);
990 
991 	to = sp->s_line + col;
992 	from = to + num;
993 
994 	byte_copy(from, to, (size_t) (sp->s_roof - from));
995 	clrline(sp->s_roof - num, sp->s_roof);
996 	sp->s_roof -= num;
997 }
998 
999 private void
InsChar(lineno,col,num,new)1000 InsChar(lineno, col, num, new)
1001 int	lineno,
1002 	col,
1003 	num;
1004 char	*new;
1005 {
1006 	register char	*sp1,
1007 			*sp2,	/* To push over the array. */
1008 			*sp3;	/* Last character to push over. */
1009 	int	i;
1010 
1011 	i_set(lineno, 0);
1012 	sp2 = Curline->s_roof + num;
1013 
1014 	if (sp2 > cursend) {
1015 		i_set(lineno, CO - num - 1);
1016 		cl_eol();
1017 		sp2 = cursend;
1018 	}
1019 	Curline->s_roof = sp2;
1020 	sp1 = sp2 - num;
1021 	sp3 = Curline->s_line + col;
1022 
1023 	while (sp1 > sp3)
1024 		*--sp2 = *--sp1;
1025 
1026 	new += col;
1027 	byte_copy(new, sp3, (size_t) num);
1028 
1029 	/* The internal screen is correct, and now we have to do
1030 	   the physical stuff. */
1031 
1032 	Placur(lineno, col);
1033 
1034 	/* Note: the way termcap/terminfo is defined, we must use *both*
1035 	 * IC and IM, but normally only one will be defined.
1036 	 * See terminfo(5), under the heading "Insert/Delete Character".
1037 	 */
1038 	if (IC != NULL)
1039 		putmulti(IC, M_IC, num, 1);
1040 	if (IM != NULL)
1041 		INSmode(YES);
1042 
1043 	for (i = 0; i < num; i++) {
1044 		scr_putchar(new[i]);
1045 		if (IN_INSmode)
1046 			putpad(IP, 1);
1047 	}
1048 	CapCol += num;
1049 }
1050 
1051 #endif /* ID_CHAR */
1052 
1053 #ifdef UNIX		/* obviously ... no mail today if not Unix*/
1054 
1055 /* chkmail() returns YES if there is new mail since the
1056    last time we checked. */
1057 
1058 char	Mailbox[FILESIZE];	/* VAR: mailbox name */
1059 int	MailInt = 60;		/* VAR: mail check interval (seconds) */
1060 
1061 bool
chkmail(force)1062 chkmail(force)
1063 bool	force;
1064 {
1065 	time_t	now;
1066 	static bool	state = NO;	/* assume unknown */
1067 	static time_t	last_chk = 0,
1068 			mbox_time = 0;
1069 	struct stat	stbuf;
1070 
1071 	if (MailInt == 0 || Mailbox[0] == '\0')
1072 		return NO;
1073 	time(&now);
1074 	if ((force == NO) && (now < last_chk + MailInt))
1075 		return state;
1076 	last_chk = now;
1077 	if (stat(Mailbox, &stbuf) < 0) {
1078 		state = NO;		/* no mail */
1079 		return NO;
1080 	}
1081 	if ((stbuf.st_atime > stbuf.st_mtime && stbuf.st_atime > mbox_time)
1082 	|| stbuf.st_size == 0)
1083 	{
1084 		mbox_time = stbuf.st_atime;
1085 		state = NO;
1086 	} else if (stbuf.st_mtime > mbox_time) {
1087 		if (mbox_time > 0)
1088 			dobell(2);		/* announce the change */
1089 		mbox_time = stbuf.st_mtime;
1090 		state = YES;
1091 	}
1092 	return state;
1093 }
1094 
1095 #endif /* UNIX */
1096 
1097 /* Print the mode line. */
1098 
1099 private char	*mode_p,
1100 		*mend_p;
1101 bool	BriteMode = YES;		/* VAR: make the mode line inverse? */
1102 
1103 private void
mode_app(str)1104 mode_app(str)
1105 register const char	*str;
1106 {
1107 	ZXchar	c;
1108 
1109 	while (mode_p < mend_p && (c = ZXC(*str++)) != '\0') {
1110 		/* don't expand tabs: treat them as suspects */
1111 		if (jisprint(c)) {
1112 			*mode_p++ = c;
1113 		} else {
1114 			char	buf[PPWIDTH];
1115 
1116 			PPchar(c, buf);
1117 			mode_app(buf);
1118 		}
1119 	}
1120 
1121 }
1122 
1123 /* VAR: mode line format string */
1124 char	ModeFmt[120] = "%3c %w %[%sJOVE (%M)   Buffer: %b  \"%f\" %]%s%i#-%m*- %((%t)%s%)%e";
1125 
1126 private void
ModeLine(w,line,linenum)1127 ModeLine(w, line, linenum)
1128 register Window	*w;
1129 char	*line;	/* scratch space of at least CO chars */
1130 int	linenum;
1131 {
1132 	int	n,
1133 		glue = 0;
1134 	bool	ign_some = NO;
1135 	bool	td = NO;	/* is time (kludge: or mail status) displayed? */
1136 	char
1137 		*fmt = ModeFmt,
1138 		fillc,
1139 		c;
1140 	register Buffer	*thisbuf = w->w_bufp;
1141 	register Buffer *bp;
1142 	LineEffects highlighting;
1143 
1144 	mode_p = line;
1145 	mend_p = &line[CO - 1];
1146 
1147 #ifdef TERMCAP
1148 	if (SO == NULL)
1149 		BriteMode = NO;	/* we can't do it */
1150 #endif
1151 	/* ??? On Mac, perhaps '_' looks better than '-' */
1152 	fillc = BriteMode? ' ' : '-';
1153 
1154 	while ((c = *fmt++)!='\0' && mode_p<mend_p) {
1155 		if (c != '%') {
1156 			if (c == '\\')
1157 				if ((c = *fmt++) == '\0')
1158 					break;
1159 			if (!ign_some) {
1160 				static char	x[] = "x";
1161 
1162 				x[0] = c;
1163 				mode_app(x);
1164 			}
1165 			continue;
1166 		}
1167 		if ((c = *fmt++) == '\0')	/* char after the '%' */
1168 			break;
1169 		if (ign_some && c != ')')
1170 			continue;
1171 		n = 1;
1172 		if (c >= '0' && c <= '9') {
1173 			n = 0;
1174 			while (c >= '0' && c <= '9') {
1175 				n = n * 10 + (c - '0');
1176 				c = *fmt++;
1177 			}
1178 			if (c == '\0')
1179 				break;
1180 		}
1181 		switch (c) {
1182 		case '%':
1183 			mode_app("%");
1184 			break;
1185 
1186 		case '(':
1187 			if (w->w_next != fwind)	/* Not bottom window. */
1188 				ign_some = YES;
1189 			break;
1190 
1191 		case ')':
1192 			ign_some = NO;
1193 			break;
1194 
1195 		case '[':
1196 		case ']':
1197 			for (n=RecDepth; n>0 && mode_p<mend_p; n--)
1198 				*mode_p++ = c;
1199 			break;
1200 
1201 #ifdef UNIX
1202 		case 'C':	/* check mail here */
1203 			td = YES;	/* kludge: reflect old behaviour where alarm could trigger mail check */
1204 			if (chkmail(NO))
1205 				mode_app("[New mail]");
1206 			break;
1207 #endif /* UNIX */
1208 
1209 		case 'M':
1210 		    {
1211 			static const char	*const mmodes[] = {
1212 				"Fundamental ",
1213 				"Text ",
1214 				"C ",
1215 #ifdef LISP
1216 				"Lisp ",
1217 #endif
1218 				NULL
1219 			};
1220 
1221 			mode_app(mmodes[thisbuf->b_major]);
1222 
1223 			if (BufMinorMode(thisbuf, Fill))
1224 				mode_app("Fill ");
1225 			if (BufMinorMode(thisbuf, Abbrev))
1226 				mode_app("Abbrev ");
1227 			if (BufMinorMode(thisbuf, OverWrite))
1228 				mode_app("OvrWt ");
1229 			if (BufMinorMode(thisbuf, Indent))
1230 				mode_app("Indent ");
1231 			if (BufMinorMode(thisbuf, ReadOnly))
1232 				mode_app("RO ");
1233 			if (InMacDefine)
1234 				mode_app("Def ");
1235 			mode_p -= 1;	/* Back over the extra space. */
1236 			break;
1237 		    }
1238 
1239 		case 'c':
1240 			while (--n>=0 && mode_p<mend_p)
1241 				*mode_p++ = fillc;
1242 			break;
1243 
1244 		case 'd':	/* print working directory */
1245 			mode_app(pr_name(pwd(), YES));
1246 			break;
1247 
1248 		case 'e':	/* stretchable glue */
1249 			*mode_p++ = '\0';	/* glue marker */
1250 			glue++;
1251 			break;
1252 
1253 		case 'b':
1254 			mode_app(thisbuf->b_name);
1255 			break;
1256 
1257 		case 'f':
1258 		case 'F':
1259 			if (thisbuf->b_fname == NULL)
1260 				mode_app("[No file]");
1261 			else {
1262 				if (c == 'f')
1263 					mode_app(pr_name(thisbuf->b_fname, YES));
1264 				else
1265 					mode_app(basename(thisbuf->b_fname));
1266 			}
1267 			break;
1268 
1269 		case 'i':
1270 		    {
1271 			char	yea = (*fmt == '\0') ? '#' : *fmt++;
1272 			char	nay = (*fmt == '\0') ? ' ' : *fmt++;
1273 
1274 			*mode_p++ = w->w_bufp->b_diverged ? yea : nay;
1275 			break;
1276 		    }
1277 
1278 		case 'm':
1279 		    {
1280 			char	yea = (*fmt == '\0') ? '*' : *fmt++;
1281 			char	nay = (*fmt == '\0') ? ' ' : *fmt++;
1282 
1283 			*mode_p++ = IsModified(w->w_bufp) ? yea : nay;
1284 			break;
1285 		    }
1286 
1287 		case 'n':
1288 		    {
1289 			char	tmp[16];
1290 
1291 			for (bp = world, n = 1; bp != NULL; bp = bp->b_next, n++)
1292 				if (bp == thisbuf)
1293 					break;
1294 
1295 			swritef(tmp, sizeof(tmp), "%d", n);
1296 			mode_app(tmp);
1297 			break;
1298 		    }
1299 
1300 #ifdef IPROCS
1301 		case 'p':
1302 			if (thisbuf->b_type == B_PROCESS) {
1303 				char	tmp[40];
1304 				Process	p = thisbuf->b_process;
1305 
1306 				swritef(tmp, sizeof(tmp), "(%s%s)",
1307 					dbxness(p), pstate(p));
1308 				mode_app(tmp);
1309 			}
1310 			break;
1311 #endif
1312 
1313 		case 's':
1314 			if (mode_p[-1] != ' ')
1315 				*mode_p++ = ' ';
1316 			break;
1317 
1318 		case 't':
1319 		    {
1320 			char	timestr[12];
1321 
1322 			td = YES;
1323 			mode_app(get_time((time_t *)NULL, timestr, 11, 16));
1324 			break;
1325 		    }
1326 
1327 		case 'w':
1328 			if (w->w_LRscroll > 0)
1329 				mode_app(">");
1330 			break;
1331 
1332 		default:
1333 			mode_app("?");
1334 			break;
1335 		}
1336 	}
1337 
1338 	/* Glue (Knuth's term) is a field that expands to fill
1339 	 * any leftover space.  Multiple glue fields compete
1340 	 * on an equal basis.  This is a generalization of a
1341 	 * mechanism to allow centring and right-justification.
1342 	 * The original meaning of %e (fill the rest of the
1343 	 * line) has also been generalized.  %e can now
1344 	 * meaningfully be used 0 or more times.
1345 	 */
1346 
1347 	if  (glue) {
1348 		/* 1 space unused, plus padding for magic cookies */
1349 		register char	*to = &line[CO - 1 - (4 * SG)],
1350 				*from = mode_p;
1351 
1352 		if (to < from)
1353 			to = from;
1354 		mode_p = to;
1355 		while (from != line) {
1356 			if ((*--to = *--from) == '\0') {
1357 				register int	portion = (to-from) / glue;
1358 
1359 				glue--;
1360 				*to = fillc;
1361 				while (--portion >= 0)
1362 					*--to = fillc;
1363 			}
1364 		}
1365 	} else {
1366 		while (mode_p < &line[CO - 1 - (4 * SG)])
1367 			*mode_p++ = fillc;
1368 	}
1369 
1370 	*mode_p = '\0';
1371 
1372 	/* Highlight mode line. */
1373 	highlighting = NOEFFECT;
1374 	if (BriteMode) {
1375 		highlighting = WindowRange(w);
1376 #ifdef HIGHLIGHTING
1377 		{
1378 			char
1379 				*p = &line[highlighting->start],
1380 				*e = p + highlighting->width;
1381 
1382 			for (; p != e; p++)
1383 				if (*p == ' ')
1384 					*p = '-';
1385 		}
1386 #endif
1387 	}
1388 	if (w->w_next == fwind && TimeDisplayed != td) {
1389 		TimeDisplayed = td;
1390 #ifdef UNIX
1391 		SetClockAlarm(YES);
1392 #endif
1393 	}
1394 #ifdef ID_CHAR
1395 	INSmode(NO);
1396 #endif
1397 	if (swrite(line, highlighting, YES))
1398 		do_cl_eol(linenum);
1399 	else
1400 		UpdModLine = YES;
1401 }
1402 
1403 /* This tries to place the current line of the current window in the
1404    center of the window, OR to place it at the arg'th line of the window.
1405    This also causes the horizontal position of the line to be centered,
1406    if the line needs scrolling, or moved all the way back to the left,
1407    if that's possible. */
1408 void
RedrawDisplay()1409 RedrawDisplay()
1410 {
1411 	int	line;
1412 	LinePtr	newtop = prev_line((curwind->w_line = curline),
1413 				arg_or_default(WSIZE(curwind)/2));
1414 
1415 	if ((line = in_window(curwind, curwind->w_line)) != -1)
1416 		PhysScreen[line].s_offset = -1;
1417 	if (newtop == curwind->w_top)
1418 		ClAndRedraw();
1419 	else
1420 		SetTop(curwind, newtop);
1421 }
1422 
1423 void
ClAndRedraw()1424 ClAndRedraw()
1425 {
1426 	cl_scr(YES);
1427 }
1428 
1429 void
NextPage()1430 NextPage()
1431 {
1432 	LinePtr	newline;
1433 
1434 	if (Asking) {
1435 		/* don't do it */
1436 	} else if (arg_value() < 0) {
1437 		negate_arg();
1438 		PrevPage();
1439 	} else if (is_non_minus_arg()) {
1440 		UpScroll();
1441 	} else {
1442 		if (in_window(curwind, curwind->w_bufp->b_last) != -1) {
1443 			rbell();
1444 			return;
1445 		}
1446 		newline = next_line(curwind->w_top, max(1, WSIZE(curwind) - 1));
1447 		SetTop(curwind, curwind->w_line = newline);
1448 		if (curwind->w_bufp == curbuf)
1449 			SetLine(newline);
1450 	}
1451 }
1452 
1453 void
PrevPage()1454 PrevPage()
1455 {
1456 	LinePtr	newline;
1457 
1458 	if (Asking) {
1459 		/* don't do it */
1460 	} else if (arg_value() < 0) {
1461 		negate_arg();
1462 		NextPage();
1463 	} else if (is_non_minus_arg()) {
1464 		DownScroll();
1465 	} else {
1466 		newline = prev_line(curwind->w_top, max(1, WSIZE(curwind) - 1));
1467 		SetTop(curwind, curwind->w_line = newline);
1468 		if (curwind->w_bufp == curbuf)
1469 			SetLine(newline);
1470 	}
1471 }
1472 
1473 void
UpScroll()1474 UpScroll()
1475 {
1476 	SetTop(curwind, next_line(curwind->w_top, arg_value()));
1477 	if (curwind->w_bufp == curbuf
1478 	&& in_window(curwind, curline) == -1)
1479 		SetLine(curwind->w_top);
1480 }
1481 
1482 void
DownScroll()1483 DownScroll()
1484 {
1485 	SetTop(curwind, prev_line(curwind->w_top, arg_value()));
1486 	if (curwind->w_bufp == curbuf
1487 	&& in_window(curwind, curline) == -1)
1488 		SetLine(curwind->w_top);
1489 }
1490 
1491 bool	VisBell = NO;	/* VAR: use visible bell (if possible) */
1492 
1493 void
rbell()1494 rbell()
1495 {
1496 	RingBell = YES;
1497 }
1498 
1499 /* Message prints the null terminated string onto the bottom line of the
1500    terminal. */
1501 
1502 void
message(str)1503 message(str)
1504 char	*str;
1505 {
1506 	if (InJoverc)
1507 		return;
1508 	UpdMesg = YES;
1509 	stickymsg = NO;
1510 	if (str != mesgbuf)
1511 		null_ncpy(mesgbuf, str, (sizeof mesgbuf) - 1);
1512 }
1513 
1514 /* End of Window */
1515 
1516 void
Eow()1517 Eow()
1518 {
1519 	if (Asking)
1520 		return;
1521 	SetLine(next_line(curwind->w_top, WSIZE(curwind) - 1 -
1522 			min(WSIZE(curwind) - 1, arg_value() - 1)));
1523 	if (!is_an_arg())
1524 		Eol();
1525 }
1526 
1527 /* Beginning of Window */
1528 
1529 void
Bow()1530 Bow()
1531 {
1532 	if (Asking)
1533 		return;
1534 	SetLine(next_line(curwind->w_top, min(WSIZE(curwind) - 1, arg_value() - 1)));
1535 }
1536 
1537 /* Typeout Mechanism */
1538 
1539 bool	UseBuffers = NO,	/* VAR: use buffers with Typeout() */
1540 	TOabort = NO;
1541 
1542 private int	LineNo;	/* screen line for Typeout (if not UseBuffers) */
1543 
1544 private Window	*old_wind;	/* curwind before preempted by typeout to buffer */
1545 
1546 /* This initializes the typeout.  If send-typeout-to-buffers is set
1547    the buffer NAME is created (emptied if it already exists) and output
1548    goes to the buffer.  Otherwise output is drawn on the screen and
1549    erased by TOstop() */
1550 
1551 void
TOstart(name)1552 TOstart(name)
1553 char	*name;
1554 {
1555 	if (UseBuffers) {
1556 		old_wind = curwind;
1557 		pop_wind(name, YES, B_SCRATCH);
1558 	} else
1559 		DisabledRedisplay = YES;
1560 	TOabort = NO;
1561 	LineNo = 0;
1562 }
1563 
1564 private void
TOlineFits(s)1565 TOlineFits(s)
1566 char	*s;
1567 {
1568 	i_set(LineNo, 0);
1569 	(void) swrite(s, NOEFFECT, NO);
1570 	PhysScreen[LineNo].s_id = NOWHERE_DADDR;
1571 	cl_eol();
1572 	flushscreen();
1573 }
1574 
1575 private void
TOprompt(s)1576 TOprompt(s)
1577 char	*s;
1578 {
1579 	if (!TOabort) {
1580 		register ZXchar	c;
1581 
1582 		TOlineFits(s);
1583 		c = kbd_getch();
1584 		TOlineFits("");
1585 		if (c != ' ') {
1586 			TOabort = YES;
1587 			if (c != AbortChar)
1588 				kbd_ungetch(c);
1589 		}
1590 	}
1591 }
1592 
1593 #ifdef STDARGS
1594 void
Typeout(char * fmt,...)1595 Typeout(char *fmt, ...)
1596 #else
1597 /*VARARGS1*/ void
1598 Typeout(fmt, va_alist)
1599 	char	*fmt;
1600 	va_dcl
1601 #endif
1602 {
1603 	char	string[MAX_TYPEOUT+1];
1604 	va_list	ap;
1605 
1606 	va_init(ap, fmt);
1607 	format(string, sizeof string, fmt, ap);
1608 	va_end(ap);
1609 	if (UseBuffers) {
1610 		ins_str(string);
1611 		ins_str("\n");
1612 	} else {
1613 		char	outbuf[MAXCOLS + PPWIDTH];	/* assert(CO <= MAXCOLS); */
1614 
1615 		if (LineNo == ILI - 2) {
1616 			TOprompt("--more--");
1617 			LineNo = 0;
1618 		}
1619 		if (!TOabort) {
1620 			DeTab(string, 0, outbuf, outbuf + CO, NO);
1621 			TOlineFits(outbuf);
1622 			LineNo += 1;
1623 		}
1624 	}
1625 }
1626 
1627 void
TOstop()1628 TOstop()
1629 {
1630 	if (UseBuffers) {
1631 		ToFirst();
1632 		SetWind(old_wind);
1633 	} else {
1634 		TOprompt("--end--");
1635 		DisabledRedisplay = NO;
1636 	}
1637 }
1638