1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)termout.c	4.1 (Berkeley) 12/04/88";
20 #endif /* not lint */
21 
22 #if defined(unix)
23 #include <signal.h>
24 #include <sgtty.h>
25 #endif
26 #include <stdio.h>
27 #include <curses.h>
28 #if	defined(ultrix)
29 /* Some version of this OS has a bad definition for nonl() */
30 #undef	nl
31 #undef	nonl
32 
33 #define nl()	 (_tty.sg_flags |= CRMOD,_pfast = _rawmode,stty(_tty_ch, &_tty))
34 #define nonl()	 (_tty.sg_flags &= ~CRMOD, _pfast = TRUE, stty(_tty_ch, &_tty))
35 #endif	/* defined(ultrix) */
36 
37 #include "../general/general.h"
38 
39 #include "terminal.h"
40 
41 #include "../api/disp_asc.h"
42 
43 #include "../ctlr/hostctlr.h"
44 #include "../ctlr/externs.h"
45 #include "../ctlr/declare.h"
46 #include "../ctlr/oia.h"
47 #include "../ctlr/screen.h"
48 #include "../ctlr/scrnctlr.h"
49 
50 #include "../general/globals.h"
51 
52 #include "../telextrn.h"
53 
54 #define CorrectTerminalCursor() ((TransparentClock == OutputClock)? \
55 		CursorAddress:UnLocked? CursorAddress: HighestScreen())
56 
57 
58 static int terminalCursorAddress;	/* where the cursor is on term */
59 static int screenInitd; 		/* the screen has been initialized */
60 static int screenStopped;		/* the screen has been stopped */
61 static int max_changes_before_poll;	/* how many characters before looking */
62 					/* at terminal and net again */
63 
64 static int needToRing;			/* need to ring terinal bell */
65 static char *bellSequence = "\07";	/* bell sequence (may be replaced by
66 					 * VB during initialization)
67 					 */
68 static WINDOW *bellwin = 0;		/* The window the bell message is in */
69 int	bellwinup = 0;			/* Are we up with it or not */
70 
71 #if	defined(unix)
72 static char *myKS, *myKE;
73 #endif	/* defined(unix) */
74 
75 
76 static int inHighlightMode = 0;
77 ScreenImage Terminal[MAXSCREENSIZE];
78 
79 /* Variables for transparent mode */
80 #if	defined(unix)
81 static int tcflag = -1;			/* transparent mode command flag */
82 static int savefd[2];			/* for storing fds during transcom */
83 extern int	tin, tout;		/* file descriptors */
84 #endif	/* defined(unix) */
85 
86 
87 /*
88  * init_screen()
89  *
90  * Initialize variables used by screen.
91  */
92 
93 void
94 init_screen()
95 {
96     bellwinup = 0;
97     inHighlightMode = 0;
98     ClearArray(Terminal);
99 }
100 
101 
102 /* OurExitString - designed to keep us from going through infinite recursion */
103 
104 static void
105 OurExitString(string, value)
106 char	*string;
107 int	value;
108 {
109     static int recursion = 0;
110 
111     if (!recursion) {
112 	recursion = 1;
113 	ExitString(string, value);
114     }
115 }
116 
117 
118 /* DoARefresh */
119 
120 static void
121 DoARefresh()
122 {
123     if (ERR == refresh()) {
124 	OurExitString("ERR from refresh\n", 1);
125     }
126 }
127 
128 static void
129 GoAway(from, where)
130 char *from;		/* routine that gave error */
131 int	where;		/* cursor address */
132 {
133 	char foo[100];
134 
135 	sprintf(foo, "ERR from %s at %d (%d, %d)\n",
136 		from, where, ScreenLine(where), ScreenLineOffset(where));
137 	OurExitString(foo, 1);
138 	/* NOTREACHED */
139 }
140 
141 /* What is the screen address of the attribute byte for the terminal */
142 
143 static int
144 WhereTermAttrByte(p)
145 register int	p;
146 {
147     register int i;
148 
149     i = p;
150 
151     do {
152 	if (TermIsStartField(i)) {
153 	    return(i);
154 	}
155 	i = ScreenDec(i);
156     } while (i != p);
157 
158     return(LowestScreen());	/* unformatted screen... */
159 }
160 
161 /*
162  *	There are two algorithms for updating the screen.
163  *  The first, SlowScreen() optimizes the line between the
164  *  computer and the screen (say a 9600 baud line).  To do
165  *  this, we break out of the loop every so often to look
166  *  at any pending input from the network (so that successive
167  *  screens will only partially print until the final screen,
168  *  the one the user possibly wants to see, is displayed
169  *  in its entirety).
170  *
171  *	The second algorithm tries to optimize CPU time (by
172  *  being simpler) at the cost of the bandwidth to the
173  *  screen.
174  *
175  *	Of course, curses(3X) gets in here also.
176  */
177 
178 
179 #if	defined(NOT43)
180 static int
181 #else	/* defined(NOT43) */
182 static void
183 #endif	/* defined(NOT43) */
184 SlowScreen()
185 {
186     register int is, shouldbe, isattr, shouldattr;
187     register int pointer;
188     register int fieldattr, termattr;
189     register int columnsleft;
190 
191 #define	HIGHLIGHT	1		/* Mask bits */
192 #define	NONDISPLAY	4		/* Mask bits */
193 
194 #define	DoAttributes(x) \
195 	    switch (x&ATTR_DSPD_MASK) { \
196 	    case ATTR_DSPD_NONDISPLAY: \
197 		x = NONDISPLAY; \
198 		break; \
199 	    case ATTR_DSPD_HIGH: \
200 		x = HIGHLIGHT; \
201 		break; \
202 	    default: \
203 		x = 0; \
204 		break; \
205 	    }
206 
207 #   define  SetHighlightMode(x) \
208 	    { \
209 		if ((x)&HIGHLIGHT) { \
210 		    if (!inHighlightMode) { \
211 			inHighlightMode = HIGHLIGHT; \
212 			standout(); \
213 		    } \
214 		} else { \
215 		    if (inHighlightMode) { \
216 			inHighlightMode = 0; \
217 			standend(); \
218 		    } \
219 		} \
220 	    }
221 
222 #   define  DoCharacterAt(c,p) { \
223 		if (p != HighestScreen()) { \
224 		    c = disp_asc[c&0xff]; \
225 		    if (terminalCursorAddress != p) { \
226 			if (ERR == mvaddch(ScreenLine(p), \
227 						ScreenLineOffset(p), c)) {\
228 			    GoAway("mvaddch", p); \
229 			} \
230 		    } else { \
231 			if (ERR == addch(c)) {\
232 			    GoAway("addch", p); \
233 			} \
234 		    } \
235 		    terminalCursorAddress = ScreenInc(p); \
236 		} \
237 	    }
238 
239 
240     /* run through screen, printing out non-null lines */
241 
242     /* There are two separate reasons for wanting to terminate this
243      * loop early.  One is to respond to new input (either from
244      * the terminal or from the network [host]).  For this reason,
245      * we expect to see 'HaveInput' come true when new input comes in.
246      *
247      * The second reason is a bit more difficult (for me) to understand.
248      * Basically, we don't want to get too far ahead of the characters that
249      * appear on the screen.  Ideally, we would type out a few characters,
250      * wait until they appeared on the screen, then type out a few more.
251      * The reason for this is that the user, on seeing some characters
252      * appear on the screen may then start to type something.  We would
253      * like to look at what the user types at about the same 'time'
254      * (measured by characters being sent to the terminal) that the
255      * user types them.  For this reason, what we would like to do
256      * is update a bit, then call curses to do a refresh, flush the
257      * output to the terminal, then wait until the terminal data
258      * has been sent.
259      *
260      * Note that curses is useful for, among other things, deciding whether
261      * or not to send :ce: (clear to end of line), so we should call curses
262      * at end of lines (beginning of next lines).
263      *
264      * The problems here are the following:  If we do lots of write(2)s,
265      * we will be doing lots of context switches, thus lots of overhead
266      * (which we have already).  Second, if we do a select to wait for
267      * the output to drain, we have to contend with the fact that NOW
268      * we are scheduled to run, but who knows what the scheduler will
269      * decide when the output has caught up.
270      */
271 
272     if (Highest >= HighestScreen()) {	/* Could be > if screen shrunk... */
273 	Highest = ScreenDec(Highest);	/* else, while loop will never end */
274     }
275     if (Lowest < LowestScreen()) {
276 	Lowest = LowestScreen();	/* could be -1 in some cases with
277 					 * unformatted screens.
278 					 */
279     }
280     if (Highest >= (pointer = Lowest)) {
281 		/* if there is anything to do, do it.  We won't terminate
282 		 * the loop until we've gone at least to Highest.
283 		 */
284 	while ((pointer <= Highest) && !HaveInput) {
285 
286 		/* point at the next place of disagreement */
287 	    pointer += (bunequal(Host+pointer, Terminal+pointer,
288 			(Highest-pointer+1)*sizeof Host[0])/sizeof Host[0]);
289 
290 		/*
291 		 * How many characters to change until the end of the
292 		 * current line
293 		 */
294 	    columnsleft = NumberColumns - ScreenLineOffset(pointer);
295 		/*
296 		 * Make sure we are where we think we are.
297 		 */
298 	    move(ScreenLine(pointer), ScreenLineOffset(pointer));
299 
300 		/* what is the field attribute of the current position */
301 	    fieldattr = FieldAttributes(pointer);
302 	    DoAttributes(fieldattr);
303 	    termattr = TermAttributes(pointer);
304 	    DoAttributes(termattr);
305 
306 	    SetHighlightMode(fieldattr);
307 	    /*
308 	     * The following will terminate at least when we get back
309 	     * to the original 'pointer' location (since we force
310 	     * things to be equal).
311 	     */
312 	    for (;;) {
313 		if (IsStartField(pointer)) {
314 		    shouldbe = DISP_BLANK;
315 		    shouldattr = 0;
316 		    fieldattr = GetHost(pointer);
317 		    DoAttributes(fieldattr);
318 		} else {
319 		    if (fieldattr&NONDISPLAY) {
320 			shouldbe = DISP_BLANK;
321 		    } else {
322 			shouldbe = GetHost(pointer);
323 		    }
324 		    shouldattr = fieldattr;
325 		}
326 		if (TermIsStartField(pointer)) {
327 		    is = DISP_BLANK;
328 		    isattr = 0;
329 		    termattr = GetTerminal(pointer);
330 		    DoAttributes(termattr);
331 		} else {
332 		    if (termattr&NONDISPLAY) {
333 			is = DISP_BLANK;
334 		    } else {
335 			is = GetTerminal(pointer);
336 		    }
337 		    isattr = termattr;
338 		}
339 		if ((shouldbe == is) && (shouldattr == isattr)
340 			&& (GetHost(pointer) == GetTerminal(pointer))
341 			&& (GetHost(ScreenInc(pointer))
342 					== GetTerminal(ScreenInc(pointer)))) {
343 		    break;
344 		}
345 
346 		if (shouldattr^inHighlightMode) {
347 		    SetHighlightMode(shouldattr);
348 		}
349 
350 		DoCharacterAt(shouldbe, pointer);
351 		if (IsStartField(pointer)) {
352 		    TermNewField(pointer, FieldAttributes(pointer));
353 		} else {
354 		    SetTerminal(pointer, GetHost(pointer));
355 		}
356 		pointer = ScreenInc(pointer);
357 		if (!(--columnsleft)) {
358 		    DoARefresh();
359 		    EmptyTerminal();
360 		    if (HaveInput) {	/* if input came in, take it */
361 			int c, j;
362 
363 			/*
364 			 * We need to start a new terminal field
365 			 * at this location iff the terminal attributes
366 			 * of this location are not what we have had
367 			 * them as (ie: we've overwritten the terminal
368 			 * start field, a the previous field had different
369 			 * display characteristics).
370 			 */
371 
372 			isattr = TermAttributes(pointer);
373 			DoAttributes(isattr);
374 			if ((!TermIsStartField(pointer)) &&
375 					(isattr != termattr)) {
376 			    /*
377 			     * Since we are going to leave a new field
378 			     * at this terminal position, we
379 			     * need to make sure that we get an actual
380 			     * non-highlighted blank on the screen.
381 			     */
382 			    if ((is != DISP_BLANK) || (termattr&HIGHLIGHT)) {
383 				SetHighlightMode(0);	/* Turn off highlight */
384 				c = ScreenInc(pointer);
385 				j = DISP_BLANK;
386 				DoCharacterAt(j, c);
387 			    }
388 			    if (termattr&HIGHLIGHT) {
389 				termattr = ATTR_DSPD_HIGH;
390 			    } else if (termattr&NONDISPLAY) {
391 				termattr = ATTR_DSPD_NONDISPLAY;
392 			    } else {
393 				termattr = 0;
394 			    }
395 			    TermNewField(pointer, termattr);
396 			}
397 			break;
398 		    }
399 		    move(ScreenLine(pointer), 0);
400 		    columnsleft = NumberColumns;
401 		}
402 	    }	/* end of for (;;) */
403 	} /* end of while (...) */
404     }
405     DoARefresh();
406     Lowest = pointer;
407     if (Lowest > Highest) {		/* if we finished input... */
408 	Lowest = HighestScreen()+1;
409 	Highest = LowestScreen()-1;
410 	terminalCursorAddress = CorrectTerminalCursor();
411 	if (ERR == move(ScreenLine(terminalCursorAddress),
412 			ScreenLineOffset(terminalCursorAddress))) {
413 	    GoAway("move", terminalCursorAddress);
414 	}
415 	DoARefresh();
416 	if (needToRing) {
417 	    StringToTerminal(bellSequence);
418 	    needToRing = 0;
419 	}
420     }
421     EmptyTerminal();			/* move data along */
422     return;
423 }
424 
425 #if	defined(NOT43)
426 static int
427 #else	/* defined(NOT43) */
428 static void
429 #endif	/* defined(NOT43) */
430 FastScreen()
431 {
432 #if	defined(MSDOS)
433 #define	SaveCorner	0
434 #else	/* defined(MSDOS) */
435 #define	SaveCorner	1
436 #endif	/* defined(MSDOS) */
437 
438 #define	DoAttribute(a) 	    if (IsHighlightedAttr(a)) { \
439 				standout(); \
440 			    } else { \
441 				standend(); \
442 			    } \
443 			    if (IsNonDisplayAttr(a)) { \
444 				a = 0; 	/* zero == don't display */ \
445 			    } \
446 			    if (!FormattedScreen()) { \
447 				a = 1;	/* one ==> do display on unformatted */\
448 			    }
449     ScreenImage *p, *upper;
450     int fieldattr;		/* spends most of its time == 0 or 1 */
451 
452 /* OK.  We want to do this a quickly as possible.  So, we assume we
453  * only need to go from Lowest to Highest.  However, if we find a
454  * field in the middle, we do the whole screen.
455  *
456  * In particular, we separate out the two cases from the beginning.
457  */
458     if ((Highest != HighestScreen()) || (Lowest != LowestScreen())) {
459 	register int columnsleft;
460 
461 	move(ScreenLine(Lowest), ScreenLineOffset(Lowest));
462 	p = &Host[Lowest];
463 #if	!defined(MSDOS)
464 	if (Highest == HighestScreen()) {
465 	    Highest = ScreenDec(Highest);
466 	}
467 #endif	/* !defined(MSDOS) */
468 	upper = &Host[Highest];
469 	fieldattr = FieldAttributes(Lowest);
470 	DoAttribute(fieldattr);	/* Set standout, non-display status */
471 	columnsleft = NumberColumns-ScreenLineOffset(p-Host);
472 
473 	while (p <= upper) {
474 	    if (IsStartFieldPointer(p)) {	/* New field? */
475 		Highest = HighestScreen();
476 		Lowest = LowestScreen();
477 		FastScreen();		/* Recurse */
478 		return;
479 	    } else if (fieldattr) {	/* Should we display? */
480 			    /* Display translated data */
481 		addch((char)disp_asc[GetTerminalPointer(p)]);
482 	    } else {
483 		addch(' ');			/* Display a blank */
484 	    }
485 			/* If the physical screen is larger than what we
486 			 * are using, we need to make sure that each line
487 			 * starts at the beginning of the line.  Otherwise,
488 			 * we will just string all the lines together.
489 			 */
490 	    p++;
491 	    if (--columnsleft == 0) {
492 		int i = p-Host;
493 
494 		move(ScreenLine(i), 0);
495 		columnsleft = NumberColumns;
496 	    }
497 	}
498     } else {		/* Going from Lowest to Highest */
499 	unsigned char tmpbuf[MAXNUMBERCOLUMNS+1];
500 	ScreenImage *End = &Host[ScreenSize]-1-SaveCorner;
501 	register unsigned char *tmp = tmpbuf, *tmpend = tmpbuf+NumberColumns;
502 
503 	*tmpend = 0;		/* terminate from the beginning */
504 	move(0,0);
505 	p = Host;
506 	fieldattr = FieldAttributes(LowestScreen());
507 	DoAttribute(fieldattr);	/* Set standout, non-display status */
508 
509 	while (p <= End) {
510 	    if (IsStartFieldPointer(p)) {	/* New field? */
511 		if (tmp != tmpbuf) {
512 		    *tmp++ = 0;			/* close out */
513 		    addstr((char *)tmpbuf);
514 		    tmp = tmpbuf;
515 		    tmpend = tmpbuf+NumberColumns-ScreenLineOffset(p-Host)-1;
516 		}
517 		standend();
518 		addch(' ');
519 		fieldattr = FieldAttributesPointer(p);	/* Get attributes */
520 		DoAttribute(fieldattr);	/* Set standout, non-display */
521 	    } else {
522 		if (fieldattr) {	/* Should we display? */
523 				/* Display translated data */
524 		    *tmp++ = disp_asc[GetTerminalPointer(p)];
525 		} else {
526 		    *tmp++ = ' ';
527 		}
528 	    }
529 			/* If the physical screen is larger than what we
530 			 * are using, we need to make sure that each line
531 			 * starts at the beginning of the line.  Otherwise,
532 			 * we will just string all the lines together.
533 			 */
534 	    p++;
535 	    if (tmp == tmpend) {
536 		int i = p-Host;		/* Be sure the "p++" happened first! */
537 
538 		*tmp++ = 0;
539 		addstr((char *)tmpbuf);
540 		tmp = tmpbuf;
541 		move(ScreenLine(i), 0);
542 		tmpend = tmpbuf + NumberColumns;
543 	    }
544 	}
545 	if (tmp != tmpbuf) {
546 	    *tmp++ = 0;
547 	    addstr((char *)tmpbuf);
548 	    tmp = tmpbuf;
549 	}
550     }
551     Lowest = HighestScreen()+1;
552     Highest = LowestScreen()-1;
553     terminalCursorAddress = CorrectTerminalCursor();
554     if (ERR == move(ScreenLine(terminalCursorAddress),
555 		    ScreenLineOffset(terminalCursorAddress))) {
556 	GoAway("move", terminalCursorAddress);
557     }
558     DoARefresh();
559     if (needToRing) {
560 	StringToTerminal(bellSequence);
561 	needToRing = 0;
562     }
563     EmptyTerminal();			/* move data along */
564     return;
565 }
566 
567 
568 /* TryToSend - send data out to user's terminal */
569 
570 #if	defined(NOT43)
571 int
572 #else	/* defined(NOT43) */
573 void
574 #endif	/* defined(NOT43) */
575 	(*TryToSend)() = FastScreen;
576 
577 /*ARGSUSED*/
578 void
579 ScreenOIA(oia)
580 OIA *oia;
581 {
582 }
583 
584 
585 /* InitTerminal - called to initialize the screen, etc. */
586 
587 void
588 InitTerminal()
589 {
590 #if defined(unix)
591     struct sgttyb ourttyb;
592     static int speeds[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800,
593 		2400, 4800, 9600 };
594 #endif
595     extern void InitMapping();
596 
597     InitMapping();		/* Go do mapping file (MAP3270) first */
598     if (!screenInitd) { 	/* not initialized */
599 #if	defined(unix)
600 	char KSEbuffer[2050];
601 	char *lotsofspace = KSEbuffer;
602 	extern int abort();
603 	extern char *tgetstr();
604 #endif	/* defined(unix) */
605 
606 	if (initscr() == ERR) {	/* Initialize curses to get line size */
607 	    ExitString("InitTerminal:  Error initializing curses", 1);
608 	    /*NOTREACHED*/
609 	}
610 	MaxNumberLines = LINES;
611 	MaxNumberColumns = COLS;
612 	ClearArray(Terminal);
613 	terminalCursorAddress = SetBufferAddress(0,0);
614 #if defined(unix)
615 	signal(SIGHUP, abort);
616 #endif
617 
618 	TryToSend = FastScreen;
619 #if defined(unix)
620 	ioctl(1, TIOCGETP, (char *) &ourttyb);
621 	if ((ourttyb.sg_ospeed < 0) || (ourttyb.sg_ospeed > B9600)) {
622 	    max_changes_before_poll = 1920;
623 	} else {
624 	    max_changes_before_poll = speeds[ourttyb.sg_ospeed]/10;
625 	    if (max_changes_before_poll < 40) {
626 		max_changes_before_poll = 40;
627 	    }
628 	    TryToSend = SlowScreen;
629 	    HaveInput = 1;		/* get signals going */
630 	}
631 #endif	/* defined(unix) */
632 	setcommandmode();
633 	/*
634 	 * By now, initscr() (in curses) has been called (from telnet.c),
635 	 * and the screen has been initialized.
636 	 */
637 #if defined(unix)
638 	nonl();
639 			/* the problem is that curses catches SIGTSTP to
640 			 * be nice, but it messes us up.
641 			 */
642 	signal(SIGTSTP, SIG_DFL);
643 	if ((myKS = tgetstr("ks", &lotsofspace)) != 0) {
644 	    myKS = strsave(myKS);
645 	    StringToTerminal(myKS);
646 	}
647 	if ((myKE = tgetstr("ke", &lotsofspace)) != 0) {
648 	    myKE = strsave(myKE);
649 	}
650 	if (tgetstr("md", &lotsofspace) && tgetstr("me", &lotsofspace)) {
651 	   SO = strsave(tgetstr("md", &lotsofspace));
652 	   SE = strsave(tgetstr("me", &lotsofspace));
653 	}
654 #endif
655 	DoARefresh();
656 	setconnmode();
657 	if (VB && *VB) {
658 	    bellSequence = VB;		/* use visual bell */
659 	}
660 	screenInitd = 1;
661 	screenStopped = 0;		/* Not stopped */
662     }
663 }
664 
665 
666 /* StopScreen - called when we are going away... */
667 
668 void
669 StopScreen(doNewLine)
670 int doNewLine;
671 {
672     if (screenInitd && !screenStopped) {
673 	move(NumberLines-1, 1);
674 	standend();
675 	inHighlightMode = 0;
676 	DoARefresh();
677 	setcommandmode();
678 	endwin();
679 	setconnmode();
680 #if	defined(unix)
681 	if (myKE) {
682 	    StringToTerminal(myKE);
683 	}
684 #endif	/* defined(unix) */
685 	if (doNewLine) {
686 	    StringToTerminal("\r\n");
687 	}
688 	EmptyTerminal();
689 	screenStopped = 1;		/* This is stopped */
690     }
691 }
692 
693 
694 /* RefreshScreen - called to cause the screen to be refreshed */
695 
696 void
697 RefreshScreen()
698 {
699     clearok(curscr, TRUE);
700     (*TryToSend)();
701 }
702 
703 
704 /* ConnectScreen - called to reconnect to the screen */
705 
706 void
707 ConnectScreen()
708 {
709     if (screenInitd) {
710 #if	defined(unix)
711 	if (myKS) {
712 	    StringToTerminal(myKS);
713 	}
714 #endif	/* defined(unix) */
715 	RefreshScreen();
716 	(*TryToSend)();
717 	screenStopped = 0;
718     }
719 }
720 
721 /* LocalClearScreen() - clear the whole ball of wax, cheaply */
722 
723 void
724 LocalClearScreen()
725 {
726     extern void Clear3270();
727 
728     outputPurge();		/* flush all data to terminal */
729     clear();			/* clear in curses */
730     ClearArray(Terminal);
731     Clear3270();
732     Lowest = HighestScreen()+1; /* everything in sync... */
733     Highest = LowestScreen()+1;
734 }
735 
736 
737 void
738 BellOff()
739 {
740     if (bellwinup) {
741 	delwin(bellwin);
742 	bellwin = 0;
743 	bellwinup = 0;
744 	touchwin(stdscr);
745 	DoARefresh();
746     }
747 }
748 
749 
750 void
751 RingBell(s)
752 char *s;
753 {
754     needToRing = 1;
755     if (s) {
756 	int len = strlen(s);
757 
758 	if (len > COLS-2) {
759 	    len = COLS-2;
760 	}
761 	if ((bellwin = newwin(3, len+2, LINES/2, 0)) == NULL) {
762 	    OurExitString("Error from newwin in RingBell", 1);
763 	}
764 	werase(bellwin);
765 	wstandout(bellwin);
766 	box(bellwin, '|', '-');
767 	if (wmove(bellwin, 1, 1) == ERR) {
768 	    OurExitString("Error from wmove in RingBell", 1);
769 	}
770 	while (len--) {
771 	    if (waddch(bellwin, *s++) == ERR) {
772 		OurExitString("Error from waddch in RingBell", 1);
773 	    }
774 	}
775 	wstandend(bellwin);
776 	if (wrefresh(bellwin) == ERR) {
777 	    OurExitString("Error from wrefresh in RingBell", 1);
778 	}
779 	bellwinup = 1;
780     }
781 }
782 
783 
784 /* returns a 1 if no more output available (so, go ahead and block),
785     or a 0 if there is more output available (so, just poll the other
786     sources/destinations, don't block).
787  */
788 
789 int
790 DoTerminalOutput()
791 {
792 	/* called just before a select to conserve IO to terminal */
793     if (!(screenInitd||screenStopped)) {
794 	return 1;		/* No output if not initialized */
795     }
796     if ((Lowest <= Highest) || needToRing ||
797 			(terminalCursorAddress != CorrectTerminalCursor())) {
798 	(*TryToSend)();
799     }
800     if (Lowest > Highest) {
801 	return 1;		/* no more output now */
802     } else {
803 	return 0;		/* more output for future */
804     }
805 }
806 
807 /*
808  * The following are defined to handle transparent data.
809  */
810 
811 void
812 TransStop()
813 {
814 #if	defined(unix)
815     if (tcflag == 0) {
816        tcflag = -1;
817        (void) signal(SIGCHLD, SIG_DFL);
818     } else if (tcflag > 0) {
819        setcommandmode();
820        (void) close(tin);
821        (void) close(tout);
822        tin = savefd[0];
823        tout = savefd[1];
824        setconnmode();
825        tcflag = -1;
826        (void) signal(SIGCHLD, SIG_DFL);
827     }
828 #endif	/* defined(unix) */
829     RefreshScreen();
830 }
831 
832 void
833 TransOut(buffer, count, kind, control)
834 unsigned char	*buffer;
835 int		count;
836 int		kind;		/* 0 or 5 */
837 int		control;	/* To see if we are done */
838 {
839 #if	defined(unix)
840     extern char *transcom;
841     int inpipefd[2], outpipefd[2];
842     void aborttc();
843 #endif	/* defined(unix) */
844 
845     while (DoTerminalOutput() == 0) {
846 #if defined(unix)
847 	HaveInput = 0;
848 #endif /* defined(unix) */
849     }
850 #if	defined(unix)
851     if (transcom && tcflag == -1) {
852        while (1) {			  /* go thru once */
853 	     if (pipe(outpipefd) < 0) {
854 		break;
855 	     }
856 	     if (pipe(inpipefd) < 0) {
857 		break;
858 	     }
859 	     if ((tcflag = fork()) == 0) {
860 		(void) close(outpipefd[1]);
861 		(void) close(0);
862 		if (dup(outpipefd[0]) < 0) {
863 		   exit(1);
864 		}
865 		(void) close(outpipefd[0]);
866 		(void) close(inpipefd[0]);
867 		(void) close(1);
868 		if (dup(inpipefd[1]) < 0) {
869 		   exit(1);
870 		}
871 		(void) close(inpipefd[1]);
872 		if (execl("/bin/csh", "csh", "-c", transcom, (char *) 0)) {
873 		    exit(1);
874 		}
875 	     }
876 	     (void) close(inpipefd[1]);
877 	     (void) close(outpipefd[0]);
878 	     savefd[0] = tin;
879 	     savefd[1] = tout;
880 	     setcommandmode();
881 	     tin = inpipefd[0];
882 	     tout = outpipefd[1];
883 	     (void) signal(SIGCHLD, (int (*)())aborttc);
884 	     setconnmode();
885 	     tcflag = 1;
886 	     break;
887        }
888        if (tcflag < 1) {
889 	  tcflag = 0;
890        }
891     }
892 #endif	/* defined(unix) */
893     (void) DataToTerminal((char *)buffer, count);
894     if (control && (kind == 0)) {		/* Send in AID byte */
895 	SendToIBM();
896     } else {
897 	extern void TransInput();
898 
899 	TransInput(1, kind);			/* Go get some data */
900     }
901 }
902 
903 
904 #if	defined(unix)
905 static void
906 aborttc()
907 {
908 	setcommandmode();
909 	(void) close(tin);
910 	(void) close(tout);
911 	tin = savefd[0];
912 	tout = savefd[1];
913 	setconnmode();
914 	tcflag = 0;
915 }
916 #endif	/* defined(unix) */
917