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