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