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