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