* Copyright (c) 1984, 1985, 1986 by the Regents of the
* University of California and by Gregory Glenn Minshall.
* Permission to use, copy, modify, and distribute these
* programs and their documentation for any purpose and
* without fee is hereby granted, provided that this
* copyright and permission appear on all copies and
* supporting documentation, the name of the Regents of
* the University of California not be used in advertising
* or publicity pertaining to distribution of the programs
* without specific prior permission, and notice be given in
* supporting documentation that copying and distribution is
* by permission of the Regents of the University of California
* and by Gregory Glenn Minshall. Neither the Regents of the
* University of California nor Gregory Glenn Minshall make
* representations about the suitability of this software
* for any purpose. It is provided "as is" without
* express or implied warranty.
static char sccsid
[] = "@(#)outbound.c 3.1 10/29/86";
#include "../ctlr/hostctlr.h"
#include "../ctlr/inbound.ext"
#include "../ctlr/options.ext"
#include "../ctlr/outbound.ext"
#include "../ctlr/screen.h"
#include "../keyboard/map3270.ext"
#include "../system/globals.h"
extern void EmptyTerminal();
#define CorrectTerminalCursor() ((TransparentClock == OutputClock)? \
terminalCursorAddress:UnLocked? CursorAddress: HighestScreen())
static int terminalCursorAddress
; /* where the cursor is on term */
static int screenInitd
; /* the screen has been initialized */
static int screenStopped
; /* the screen has been stopped */
static int max_changes_before_poll
; /* how many characters before looking */
/* at terminal and net again */
#endif /* defined(SLOWSCREEN) */
static int needToRing
; /* need to ring terinal bell */
static char *bellSequence
= "\07"; /* bell sequence (may be replaced by
* VB during initialization)
static WINDOW
*bellwin
= 0; /* The window the bell message is in */
int bellwinup
= 0; /* Are we up with it or not */
#endif /* defined(unix) */
static int inHighlightMode
= 0;
ScreenImage Terminal
[MAXSCREENSIZE
];
#endif /* defined(SLOWSCREEN) */
/* Variables for transparent mode */
static int tcflag
= -1; /* transparent mode command flag */
static int savefd
[2]; /* for storing fds during transcom */
extern int tin
, tout
; /* file descriptors */
#endif /* defined(unix) */
* Initialize variables used by screen.
#endif /* defined(SLOWSCREEN) */
/* OurExitString - designed to keep us from going through infinite recursion */
OurExitString(file
, string
, value
)
static int recursion
= 0;
ExitString(file
, string
, value
);
OurExitString(stderr
, "ERR from refresh\n", 1);
char *from
; /* routine that gave error */
int where
; /* cursor address */
sprintf(foo
, "ERR from %s at %d (%d, %d)\n",
from
, where
, ScreenLine(where
), ScreenLineOffset(where
));
OurExitString(stderr
, foo
, 1);
/* What is the screen address of the attribute byte for the terminal */
if (TermIsStartField(i
)) {
return(LowestScreen()); /* unformatted screen... */
#endif /* defined(SLOWSCREEN) */
* There are two algorithms for updating the screen.
* The first, SlowScreen() optimizes the line between the
* computer and the screen (say a 9600 baud line). To do
* this, we break out of the loop every so often to look
* at any pending input from the network (so that successive
* screens will only partially print until the final screen,
* the one the user possibly wants to see, is displayed
* The second algorithm tries to optimize CPU time (by
* being simpler) at the cost of the bandwidth to the
* Of course, curses(3X) gets in here also.
#else /* defined(NOT43) */
#endif /* defined(NOT43) */
register int columnsleft
;
# define SetHighlightMode(p) { \
if (!IsStartField(p) && IsHighlightedAttr(fieldattr)) { \
if (!inHighlightMode) { \
# define DoCharacterAt(c,p) { \
if (p != HighestScreen()) { \
c = TerminalCharacterAttr(disp_asc[c&0xff], p, \
if (terminalCursorAddress != p) { \
if (ERR == mvaddch(ScreenLine(p), \
ScreenLineOffset(p), c)) {\
terminalCursorAddress = ScreenInc(p); \
/* run through screen, printing out non-null lines */
/* There are two separate reasons for wanting to terminate this
* loop early. One is to respond to new input (either from
* the terminal or from the network [host]). For this reason,
* we expect to see 'HaveInput' come true when new input comes in.
* The second reason is a bit more difficult (for me) to understand.
* Basically, we don't want to get too far ahead of the characters that
* appear on the screen. Ideally, we would type out a few characters,
* wait until they appeared on the screen, then type out a few more.
* The reason for this is that the user, on seeing some characters
* appear on the screen may then start to type something. We would
* like to look at what the user types at about the same 'time'
* (measured by characters being sent to the terminal) that the
* user types them. For this reason, what we would like to do
* is update a bit, then call curses to do a refresh, flush the
* output to the terminal, then wait until the terminal data
* Note that curses is useful for, among other things, deciding whether
* or not to send :ce: (clear to end of line), so we should call curses
* at end of lines (beginning of next lines).
* The problems here are the following: If we do lots of write(2)s,
* we will be doing lots of context switches, thus lots of overhead
* (which we have already). Second, if we do a select to wait for
* the output to drain, we have to contend with the fact that NOW
* we are scheduled to run, but who knows what the scheduler will
* decide when the output has caught up.
if (Highest
== HighestScreen()) {
Highest
= ScreenDec(Highest
); /* else, while loop will never end */
if (Lowest
< LowestScreen()) {
Lowest
= LowestScreen(); /* could be -1 in some cases with
if (Highest
>= (pointer
= Lowest
)) {
/* if there is anything to do, do it. We won't terminate
* the loop until we've gone at least to Highest.
while ((pointer
<= Highest
) && !HaveInput
) {
/* point at the next place of disagreement */
pointer
+= (bunequal(Host
+pointer
, Terminal
+pointer
,
(Highest
-pointer
+1)*sizeof Host
[0])/sizeof Host
[0]);
/* how many characters to change until the end of the
columnsleft
= NumberColumns
- ScreenLineOffset(pointer
);
* Make sure we are where we think we are.
move(ScreenLine(pointer
), ScreenLineOffset(pointer
));
/* what is the field attribute of the current position */
fieldattr
= FieldAttributes(WhereAttrByte(pointer
));
if ((IsStartField(pointer
) != TermIsStartField(pointer
)) ||
(IsStartField(pointer
) &&
fieldattr
!= TermAttributes(pointer
))) {
oldterm
= TermAttributes(pointer
);
if (IsStartField(pointer
)) {
TermNewField(pointer
, fieldattr
);
TermDeleteField(pointer
);
/* We always do the first character in a divergent
* field, since otherwise the start of a field in
* the Host structure may leave a highlighted blank
* on the screen, and the start of a field in the
* Terminal structure may leave a non-highlighted
* something in the middle of a highlighted field
SetHighlightMode(pointer
);
DoCharacterAt(c
,pointer
); /* MACRO */
if (NotVisuallyCompatibleAttributes
(pointer
, fieldattr
, oldterm
)) {
pointer
= ScreenInc(pointer
);
move(ScreenLine(pointer
), 0);
columnsleft
= NumberColumns
;
SetHighlightMode(pointer
); /* Turn on highlighting */
while (!IsStartField(pointer
) &&
!TermIsStartField(pointer
)) {
DoCharacterAt(c
,pointer
); /* MACRO */
pointer
= ScreenInc(pointer
);
move(ScreenLine(pointer
), 0);
columnsleft
= NumberColumns
;
/* We don't look at HaveInput here, since
* if we leave this loop before the end of
* the 3270 field, we could have pointer
* higher than Highest. This would cause
* us to end the highest "while" loop,
* but we may, in fact, need to go around the
/* The loop needs to be protected
* from the situation where there had been only
* one field on the Terminal, and none on the Host.
* In this case, we have just deleted our last
* field. Hence, the break.
if (IsStartField(pointer
) && !TermIsStartField(pointer
)) {
/* Remember what the terminal looked like */
TermNewField(pointer
, oldterm
);
* The danger here is that the current position may
* be the start of a Host field. If so, and the
* field is highlighted, and our terminal was
* highlighted, then we will leave a highlighted
* blank at this position.
SetHighlightMode(pointer
);
DoCharacterAt(c
,pointer
);
/* We could be in the situation of needing to exit.
* This could happen if the current field wrapped around
/* We always do the first character in a divergent
* field, since otherwise the start of a field in
* the Host structure may leave a highlighted blank
* on the screen, and the start of a field in the
* Terminal structure may leave a non-highlighted
* something in the middle of a highlighted field
SetHighlightMode(pointer
);
DoCharacterAt(c
,pointer
);
SetHighlightMode(pointer
);
* The following will terminate at least when we get back
* to the original 'pointer' location (since we force
while (((c
= GetHost(pointer
)) != GetTerminal(pointer
)) &&
!IsStartField(pointer
) && !TermIsStartField(pointer
)) {
DoCharacterAt(c
, pointer
);
pointer
= ScreenInc(pointer
);
if (HaveInput
) { /* if input came in, take it */
move(ScreenLine(pointer
), 0);
columnsleft
= NumberColumns
;
if (Lowest
> Highest
) { /* if we finished input... */
Lowest
= HighestScreen()+1;
Highest
= LowestScreen()-1;
terminalCursorAddress
= CorrectTerminalCursor();
if (ERR
== move(ScreenLine(terminalCursorAddress
),
ScreenLineOffset(terminalCursorAddress
))) {
GoAway("move", terminalCursorAddress
);
StringToTerminal(bellSequence
);
EmptyTerminal(); /* move data along */
#endif /* defined(SLOWSCREEN) */
#else /* defined(NOT43) */
#endif /* defined(NOT43) */
#else /* defined(MSDOS) */
#endif /* defined(MSDOS) */
#define DoAttribute(a) if (IsHighlightedAttr(a)) { \
if (IsNonDisplayAttr(a)) { \
a = 0; /* zero == don't display */ \
if (!FormattedScreen()) { \
a = 1; /* one ==> do display on unformatted */\
int fieldattr
; /* spends most of its time == 0 or 1 */
/* OK. We want to do this a quickly as possible. So, we assume we
* only need to go from Lowest to Highest. However, if we find a
* field in the middle, we do the whole screen.
* In particular, we separate out the two cases from the beginning.
if ((Highest
!= HighestScreen()) || (Lowest
!= LowestScreen())) {
register int columnsleft
;
move(ScreenLine(Lowest
), ScreenLineOffset(Lowest
));
if (Highest
== HighestScreen()) {
Highest
= ScreenDec(Highest
);
#endif /* !defined(MSDOS) */
fieldattr
= FieldAttributes(Lowest
);
DoAttribute(fieldattr
); /* Set standout, non-display status */
columnsleft
= NumberColumns
-ScreenLineOffset(p
-Host
);
if (IsStartFieldPointer(p
)) { /* New field? */
Highest
= HighestScreen();
FastScreen(); /* Recurse */
} else if (fieldattr
) { /* Should we display? */
addch(disp_asc
[p
->data
]); /* Display translated data */
addch(' '); /* Display a blank */
/* If the physical screen is larger than what we
* are using, we need to make sure that each line
* starts at the beginning of the line. Otherwise,
* we will just string all the lines together.
if (--columnsleft
== 0) {
columnsleft
= NumberColumns
;
} else { /* Going from Lowest to Highest */
unsigned char tmpbuf
[MAXNUMBERCOLUMNS
+1];
ScreenImage
*End
= &Host
[ScreenSize
]-1-SaveCorner
;
register unsigned char *tmp
= tmpbuf
, *tmpend
= tmpbuf
+NumberColumns
;
*tmpend
= 0; /* terminate from the beginning */
fieldattr
= FieldAttributes(LowestScreen());
DoAttribute(fieldattr
); /* Set standout, non-display status */
if (IsStartFieldPointer(p
)) { /* New field? */
*tmp
++ = 0; /* close out */
tmpend
= tmpbuf
+ NumberColumns
- ScreenLineOffset(p
-Host
);
fieldattr
= FieldAttributesPointer(p
); /* Get attributes */
DoAttribute(fieldattr
); /* Set standout, non-display */
if (fieldattr
) { /* Should we display? */
/* Display translated data */
*tmp
++ = disp_asc
[p
->data
];
/* If the physical screen is larger than what we
* are using, we need to make sure that each line
* starts at the beginning of the line. Otherwise,
* we will just string all the lines together.
int i
= p
-Host
; /* Be sure the "p++" happened first! */
tmpend
= tmpbuf
+ NumberColumns
;
Lowest
= HighestScreen()+1;
Highest
= LowestScreen()-1;
terminalCursorAddress
= CorrectTerminalCursor();
if (ERR
== move(ScreenLine(terminalCursorAddress
),
ScreenLineOffset(terminalCursorAddress
))) {
GoAway("move", terminalCursorAddress
);
StringToTerminal(bellSequence
);
EmptyTerminal(); /* move data along */
/* TryToSend - send data out to user's terminal */
#else /* defined(NOT43) */
#endif /* defined(NOT43) */
(*TryToSend
)() = FastScreen
;
/* InitTerminal - called to initialize the screen, etc. */
static int speeds
[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800,
InitMapping(); /* Go do mapping file (MAP3270) first */
if (!screenInitd
) { /* not initialized */
char *lotsofspace
= KSEbuffer
;
#endif /* defined(unix) */
#endif /* defined(SLOWSCREEN) */
terminalCursorAddress
= SetBufferAddress(0,0);
#if defined(unix) && defined(SLOWSCREEN)
ioctl(1, TIOCGETP
, (char *) &ourttyb
);
if ((ourttyb
.sg_ospeed
< 0) || (ourttyb
.sg_ospeed
> B9600
)) {
max_changes_before_poll
= 1920;
max_changes_before_poll
= speeds
[ourttyb
.sg_ospeed
]/10;
if (max_changes_before_poll
< 40) {
max_changes_before_poll
= 40;
HaveInput
= 1; /* get signals going */
#endif /* defined(unix) && defined(SLOWSCREEN) */
* By now, initscr() (in curses) has been called (from telnet.c),
* and the screen has been initialized.
/* the problem is that curses catches SIGTSTP to
* be nice, but it messes us up.
signal(SIGTSTP
, SIG_DFL
);
if ((KS
= tgetstr("ks", &lotsofspace
)) != 0) {
if ((KE
= tgetstr("ke", &lotsofspace
)) != 0) {
if (tgetstr("md", &lotsofspace
) && tgetstr("me", &lotsofspace
)) {
SO
= strsave(tgetstr("md", &lotsofspace
));
SE
= strsave(tgetstr("me", &lotsofspace
));
bellSequence
= VB
; /* use visual bell */
screenStopped
= 0; /* Not stopped */
/* StopScreen - called when we are going away... */
if (screenInitd
&& !screenStopped
) {
#endif /* defined(SLOWSCREEN) */
#endif /* defined(unix) */
StringToTerminal("\r\n");
screenStopped
= 1; /* This is stopped */
/* RefreshScreen - called to cause the screen to be refreshed */
/* ConnectScreen - called to reconnect to the screen */
#endif /* defined(unix) */
/* LocalClearScreen() - clear the whole ball of wax, cheaply */
outputPurge(); /* flush all data to terminal */
clear(); /* clear in curses */
#endif /* defined(SLOWSCREEN) */
Lowest
= HighestScreen()+1; /* everything in sync... */
Highest
= LowestScreen()+1;
Lowest
= MIN(Lowest
, LINES
/2);
Highest
= MAX(Highest
, (LINES
/2)+3);
memset(Terminal
+LINES
/2, 0, (sizeof Terminal
[0])*(3*COLS
));
#endif /* defined(SLOWSCREEN) */
if ((bellwin
= newwin(3, len
+2, LINES
/2, 0)) == NULL
) {
OurExitString(stderr
, "Error from newwin in RingBell", 1);
if (wmove(bellwin
, 1, 1) == ERR
) {
OurExitString(stderr
, "Error from wmove in RingBell", 1);
if (waddch(bellwin
, *s
++) == ERR
) {
OurExitString(stderr
, "Error from waddch in RingBell", 1);
if (wrefresh(bellwin
) == ERR
) {
OurExitString(stderr
, "Error from wrefresh in RingBell", 1);
/* returns a 1 if no more output available (so, go ahead and block),
or a 0 if there is more output available (so, just poll the other
sources/destinations, don't block).
/* called just before a select to conserve IO to terminal */
return 1; /* No output if not initialized */
if ((Lowest
<= Highest
) || needToRing
||
(terminalCursorAddress
!= CorrectTerminalCursor())) {
return 1; /* no more output now */
return 0; /* more output for future */
* The following are defined to handle transparent data.
(void) signal(SIGCHLD
, SIG_DFL
);
(void) signal(SIGCHLD
, SIG_DFL
);
#endif /* defined(unix) */
int inpipefd
[2], outpipefd
[2], savemode
;
#endif /* defined(unix) */
while (DoTerminalOutput() == 0) {
#endif /* defined(unix) */
if (transcom
&& tcflag
== -1) {
while (1) { /* go thru once */
if (pipe(outpipefd
) < 0) {
if (pipe(inpipefd
) < 0) {
if ((tcflag
= fork()) == 0) {
(void) close(outpipefd
[1]);
if (dup(outpipefd
[0]) < 0) {
(void) close(outpipefd
[0]);
(void) close(inpipefd
[0]);
if (dup(inpipefd
[1]) < 0) {
(void) close(inpipefd
[1]);
if (execl("/bin/csh", "csh", "-c", transcom
, (char *) 0)) {
(void) close(inpipefd
[1]);
(void) close(outpipefd
[0]);
(void) signal(SIGCHLD
, aborttc
);
#endif /* defined(unix) */
(void) DataToTerminal(buffer
, count
);
#endif /* defined(unix) */