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