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