/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:outbound.c 12.0$ */
/* $ACIS:outbound.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ucb/tn3270/RCS/outbound.c,v $ */

#ifndef lint
static char *rcsid = "$Header:outbound.c 12.0$";
#endif

/*
 *	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.
 */

#ifndef lint
static	char	sccsid[] = "@(#)outbound.c	3.2  1/11/87";
#endif	/* lint */


#if defined(unix)
#include <signal.h>
#include <sgtty.h>
#endif
#include <stdio.h>
#include <curses.h>

#include "ascebc.h"
#include "3270.h"
#include "screen.h"

#include "globals.h"
#include "map3270.ext"
#include "options.ext"
#include "screen.ext"
#include "telnet.ext"
#include "termin.ext"
#include "inbound.ext"
#include "outbound.ext"

extern void EmptyTerminal();
void OurExitString();

#define CorrectTerminalCursor() ((TransparentClock == OutputClock)? terminalCursorAddress:UnLocked? CursorAddress: HighestScreen())

#define SetHighestLowest(position) { \
					if (position < Lowest) { \
					    Lowest = position; \
					} \
					if (position > Highest) { \
					    Highest = position; \
					} \
				    }

extern char	ebcasc[NEBCASC][NEBC];	/* translate table */
#if	defined(unix)
extern int	tin, tout;		/* file descriptors */
#endif	/* defined(unix) */

static int terminalCursorAddress = 0;	/* where the cursor is on term */
static int screenInitd = 0; 		/* the screen has been initialized */
static int screenStopped = 1;		/* the screen has been stopped */
#if	defined(SLOWSCREEN)
static int max_changes_before_poll;	/* how many characters before looking */
					/* at terminal and net again */
#endif	/* defined(SLOWSCREEN) */

static int needToRing = 0;		/* need to ring terinal bell */
static char *bellSequence = "\07";	/* bell sequence (may be replaced by
					 * VB during initialization)
					 */

#if	defined(unix)
static int tcflag = -1;			/* transparent mode command flag */
static int savefd[2];			/* for storing fds during transcom */
#endif	/* defined(unix) */

#if	defined(SLOWSCREEN)
static int inHighlightMode = 0;
#endif	/* defined(SLOWSCREEN)

/* some globals */

int	OutputClock = 0;		/* what time it is */
int	TransparentClock = 0;		/* time we were last in transparent */


/* DoARefresh */

static void
DoARefresh()
{
    if (ERR == refresh()) {
	OurExitString(stderr, "ERR from refresh\n", 1);
    }
}

static void
GoAway(from, where)
char *from;		/* routine that gave error */
int	where;		/* cursor address */
{
	char foo[100];

	sprintf(foo, "ERR from %s at %d (%d, %d)\n",
		from, where, ScreenLine(where), ScreenLineOffset(where));
	OurExitString(stderr, foo, 1);
	/* NOTREACHED */
}

/*
 *	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
 *  in its entirety).
 *
 *	The second algorithm tries to optimize CPU time (by
 *  being simpler) at the cost of the bandwidth to the
 *  screen.
 *
 *	Of course, curses(3X) gets in here also.
 */


#if defined(SLOWSCREEN)
#if	defined(NOT43)
static int
#else	/* defined(NOT43) */
static void
#endif	/* defined(NOT43) */
SlowScreen()
{
    register int pointer;
    register int c;
    register int fieldattr;
    register int columnsleft;

#   define  SetHighlightMode(p) { \
		if (!IsStartField(p) && IsHighlightedAttr(fieldattr)) { \
		    if (!inHighlightMode) { \
			inHighlightMode = 1; \
			standout(); \
		    } \
		} else { \
		    if (inHighlightMode) { \
			inHighlightMode = 0; \
			standend(); \
		    } \
		} \
	    }

#   define  DoCharacterAt(c,p) { \
		SetTerminal(p, c); \
		if (p != HighestScreen()) { \
		    c = TerminalCharacterAttr(ebcasc[0][c&0xff], p, \
								fieldattr); \
		    if (terminalCursorAddress != p) { \
			if (ERR == mvaddch(ScreenLine(p), \
						ScreenLineOffset(p), c)) {\
			    GoAway("mvaddch", p); \
			} \
		    } else { \
			if (ERR == addch(c)) {\
			    GoAway("addch", p); \
			} \
		    } \
		    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
     * has been sent.
     *
     * 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
					 * unformatted screens.
					 */
    }
    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
		 * current line
		 */
	    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))) {

		int oldterm;

		oldterm = TermAttributes(pointer);
		if (IsStartField(pointer)) {
		    TermNewField(pointer, fieldattr);
		    SetTerminal(pointer, 0);
		} else {
		    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
		     * on the screen.
		     */
		SetHighlightMode(pointer);
		c = GetHost(pointer);
		DoCharacterAt(c,pointer);		/* MACRO */

		if (NotVisuallyCompatibleAttributes
				(pointer, fieldattr, oldterm)) {
		    int j;

		    j = pointer;

		    pointer = ScreenInc(pointer);
		    if (!(--columnsleft)) {
			DoARefresh();
			EmptyTerminal();
			move(ScreenLine(pointer), 0);
			columnsleft = NumberColumns;
		    }
		    SetHighlightMode(pointer);	/* Turn on highlighting */
		    while (!IsStartField(pointer) &&
				!TermIsStartField(pointer)) {
			c = GetHost(pointer);
			DoCharacterAt(c,pointer);	/* MACRO */
			pointer = ScreenInc(pointer);
			if (!(--columnsleft)) {
			    DoARefresh();
			    EmptyTerminal();
			    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
				 * screen once again.
				 */
			}
			/*		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 (j == pointer) {
			    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);
			c = GetHost(pointer);
			DoCharacterAt(c,pointer);
		    }
			/* We could be in the situation of needing to exit.
			 * This could happen if the current field wrapped around
			 * the end of the screen.
			 */
		    if (j > pointer) {
			break;
		    }
		} else {
		    c = GetHost(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
			 * on the screen.
			 */
		    SetHighlightMode(pointer);
		    DoCharacterAt(c,pointer);
		}
	    } else {
		SetHighlightMode(pointer);
		/* The following will terminate at least when we get back
		 * to the original 'pointer' location (since we force
		 * things to be equal).
		 */
		while (((c = GetHost(pointer)) != GetTerminal(pointer)) &&
			!IsStartField(pointer) && !TermIsStartField(pointer)) {
		    DoCharacterAt(c, pointer);
		    pointer = ScreenInc(pointer);
		    if (!(--columnsleft)) {
			DoARefresh();
			EmptyTerminal();
			if (HaveInput) {	/* if input came in, take it */
			    break;
			}
			move(ScreenLine(pointer), 0);
			columnsleft = NumberColumns;
		    }
		}
	    }
	}
    }
    DoARefresh();
    Lowest = pointer;
    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);
	}
	DoARefresh();
	if (needToRing) {
	    StringToTerminal(bellSequence);
	    needToRing = 0;
	}
    }
    EmptyTerminal();			/* move data along */
    return;
}
#endif	/* defined(SLOWSCREEN) */

#if	defined(NOT43)
static int
#else	/* defined(NOT43) */
static void
#endif	/* defined(NOT43) */
FastScreen()
{
#if	defined(msdos)
#define	SaveCorner	0
#else	/* defined(msdos) */
#define	SaveCorner	1
#endif	/* defined(msdos) */

#define	DoAttribute(a) 	    if (IsHighlightedAttr(a)) { \
				standout(); \
			    } else { \
				standend(); \
			    } \
			    if (IsNonDisplayAttr(a)) { \
				a = 0; 	/* zero == don't display */ \
			    } \
			    if (!FormattedScreen()) { \
				a = 1;	/* one ==> do display on unformatted */\
			    }
    ScreenImage *p, *upper;
    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));
	p = &Host[Lowest];
#if	!defined(msdos)
	if (Highest == HighestScreen()) {
	    Highest = ScreenDec(Highest);
	}
#endif	/* !defined(msdos) */
	upper = &Host[Highest];
	fieldattr = FieldAttributes(Lowest);
	DoAttribute(fieldattr);	/* Set standout, non-display status */
	columnsleft = NumberColumns-ScreenLineOffset(p-Host);

	while (p <= upper) {
	    if (p->field & ATTR_MASK) {	/* New field? */
		Highest = HighestScreen();
		Lowest = LowestScreen();
		FastScreen();		/* Recurse */
		return;
	    } else if (fieldattr) {	/* Should we display? */
		addch(ebcasc[0][p->data]);	/* Display translated data */
	    } else {
		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.
			 */
	    p++;
	    if (--columnsleft == 0) {
		int i = p-Host;

		move(ScreenLine(i), 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 */
	move(0,0);
	p = Host;
	fieldattr = FieldAttributes(LowestScreen());
	DoAttribute(fieldattr);	/* Set standout, non-display status */

	while (p <= End) {
	    if (p->field & ATTR_MASK) {	/* New field? */
		if (tmp != tmpbuf) {
		    *tmp++ = 0;			/* close out */
		    addstr(tmpbuf);
		    tmp = tmpbuf;
		    tmpend = tmpbuf + NumberColumns - ScreenLineOffset(p-Host);
		}
		fieldattr = p->field;	/* Get attributes */
		DoAttribute(fieldattr);	/* Set standout, non-display */
		*tmp++ = ' ';
	    } else {
		if (fieldattr) {	/* Should we display? */
				/* Display translated data */
		    *tmp++ = ebcasc[0][p->data];
		} else {
		    *tmp++ = ' ';
		}
	    }
			/* 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.
			 */
	    p++;
	    if (tmp == tmpend) {
		int i = p-Host;		/* Be sure the "p++" happened first! */

		*tmp++ = 0;
		addstr(tmpbuf);
		tmp = tmpbuf;
		move(ScreenLine(i), 0);
		tmpend = tmpbuf + NumberColumns;
	    }
	}
	if (tmp != tmpbuf) {
	    *tmp++ = 0;
	    addstr(tmpbuf);
	    tmp = tmpbuf;
	}
    }
    Lowest = HighestScreen()+1;
    Highest = LowestScreen()-1;
    terminalCursorAddress = CorrectTerminalCursor();
    if (ERR == move(ScreenLine(terminalCursorAddress),
		    ScreenLineOffset(terminalCursorAddress))) {
	GoAway("move", terminalCursorAddress);
    }
    DoARefresh();
    if (needToRing) {
	StringToTerminal(bellSequence);
	needToRing = 0;
    }
    EmptyTerminal();			/* move data along */
    return;
}


/* TryToSend - send data out to user's terminal */

#if	defined(NOT43)
int
#else	/* defined(NOT43) */
void
#endif	/* defined(NOT43) */
	(*TryToSend)() = FastScreen;

/* StartScreen - called to initialize the screen, etc. */

void
StartScreen()
{
#if defined(unix)
    struct sgttyb ourttyb;
    static int speeds[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800,
		2400, 4800, 9600 };
#endif
    
    if (!screenInitd) { 	/* not initialized */
#if	defined(unix)
	char KSEbuffer[2050];
	char *lotsofspace = KSEbuffer;
	extern int abort();
	extern char *tgetstr();
#endif	/* defined(unix) */

	bzero((char *)Host, sizeof Host);

	bzero(Orders, sizeof Orders);
	Orders[ORDER_SF] = Orders[ORDER_SBA] = Orders[ORDER_IC]
		= Orders[ORDER_PT] = Orders[ORDER_RA] = Orders[ORDER_EUA]
		= Orders[ORDER_YALE] = 1;

	DeleteAllFields();
#if	defined(SLOWSCREEN)
	bzero((char *)Terminal, sizeof Terminal);
#endif	/* defined(SLOWSCREEN) */
	Lowest = HighestScreen()+1;
	Highest = LowestScreen()-1;
	terminalCursorAddress =
		CursorAddress =
		BufferAddress = SetBufferAddress(0,0);
	UnLocked = 1;
	Initialized = 1;
	OutputClock = 1;
	TransparentClock = -1;
#if defined(unix)
	signal(SIGHUP, abort);
#endif

	TryToSend = FastScreen;
#if defined(unix) && defined(SLOWSCREEN)
	ioctl(1, TIOCGETP, (char *) &ourttyb);
	if ((ourttyb.sg_ospeed < 0) || (ourttyb.sg_ospeed > B9600)) {
	    max_changes_before_poll = 1920;
	} else {
	    max_changes_before_poll = speeds[ourttyb.sg_ospeed]/10;
	    if (max_changes_before_poll < 40) {
		max_changes_before_poll = 40;
	    }
	    TryToSend = SlowScreen;
	    HaveInput = 1;		/* get signals going */
	}
#endif	/* defined(unix) && defined(SLOWSCREEN) */
	setcommandmode();
	/*
	 * By now, initscr() (in curses) has been called (from telnet.c),
	 * and the screen has been initialized.
	 */
#if defined(unix)
	nonl();
			/* 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) {
	    KS = strsave(KS);
	    StringToTerminal(KS);
	}
	if ((KE = tgetstr("ke", &lotsofspace)) != 0) {
	    KE = strsave(KE);
	}
	if (tgetstr("md", &lotsofspace) && tgetstr("me", &lotsofspace)) {
	   SO = strsave(tgetstr("md", &lotsofspace));
	   SE = strsave(tgetstr("me", &lotsofspace));
	}
#endif
	DoARefresh();
	setconnmode();
	if (VB && *VB) {
	    bellSequence = VB;		/* use visual bell */
	}
	screenInitd = 1;
	screenStopped = 0;		/* Not stopped */
    }
}


/* Stop3270 - called when we are going away... */

void
Stop3270(doNewLine)
int doNewLine;
{
    if (screenInitd && !screenStopped) {
	move(NumberLines-1, 1);
	standend();
#if	defined(SLOWSCREEN)
	inHighlightMode = 0;
#endif	/* defined(SLOWSCREEN) */
	DoARefresh();
	setcommandmode();
	endwin();
	setconnmode();
#if	defined(unix)
	if (KE) {
	    StringToTerminal(KE);
	}
#endif	/* defined(unix) */
	if (doNewLine) {
	    StringToTerminal("\r\n");
	}
	EmptyTerminal();
	screenStopped = 1;		/* This is stopped */
    }
}


/* RefreshScreen - called to cause the screen to be refreshed */

void
RefreshScreen()
{
    clearok(curscr, TRUE);
    (*TryToSend)();
}


/* ConnectScreen - called to reconnect to the screen */

void
ConnectScreen()
{
    if (screenInitd) {
#if	defined(unix)
	if (KS) {
	    StringToTerminal(KS);
	}
#endif	/* defined(unix) */
	RefreshScreen();
	(*TryToSend)();
	screenStopped = 0;
    }
}

/* Clear3270 - called to clear the screen */

void
Clear3270()
{
    bzero((char *)Host, sizeof(Host));
    DeleteAllFields();		/* get rid of all fields */
    BufferAddress = SetBufferAddress(0,0);
    CursorAddress = SetBufferAddress(0,0);
    Lowest = LowestScreen();
    Highest = HighestScreen();
}

/* LocalClear3270() - clear the whole ball of wax, cheaply */

void
LocalClear3270()
{
    outputPurge();		/* flush all data to terminal */
    clear();			/* clear in curses */
#if	defined(SLOWSCREEN)
    bzero((char *)Terminal, sizeof Terminal);
#endif	/* defined(SLOWSCREEN) */
    Clear3270();		/* clear host part */
    Lowest = HighestScreen()+1; /* everything in sync... */
    Highest = LowestScreen()+1;
}

/* OurExitString - designed to keep us from going through infinite recursion */

void
OurExitString(file, string, value)
FILE	*file;
char	*string;
int	value;
{
    static int recursion = 0;

    if (!recursion) {
	recursion = 1;
	ExitString(file, string, value);
    }
}


void
RingBell()
{
    needToRing = 1;
}

/* AddHost - called to add a character to the buffer.
 *	We use a macro in this module, since we call it so
 *	often from loops.
 *
 *	NOTE: It is a macro, so don't go around using AddHost(p, *c++), or
 *	anything similar.  (I don't define any temporary variables, again
 *	just for the speed.)
 */
void
AddHost(position, character)
int	position;
char	character;
{
#if	defined(SLOWSCREEN)
#   define	AddHostA(p,c)					\
    {								\
	if (IsStartField(p)) {					\
	    DeleteField(p);					\
	    Highest = HighestScreen();				\
	    Lowest = LowestScreen();				\
	    SetHighestLowest(p);				\
	}							\
	SetHost(p, c);						\
    }
#   define	AddHost(p,c)					\
    {								\
	AddHostA(p,c);						\
	if ((c != GetTerminal(p)) || TermIsStartField(p)	\
		|| (TryToSend == FastScreen)) {			\
	    SetHighestLowest(p);				\
	}							\
    }	/* end of macro of AddHost */
#else	/* defined(SLOWSCREEN) */
#   define	AddHost(p,c)					\
    {								\
	if (IsStartField(p)) {					\
	    DeleteField(p);					\
	    Highest = HighestScreen();				\
	    Lowest = LowestScreen();				\
	    SetHost(p, c);					\
	} else {						\
	    SetHost(p, c);					\
	    SetHighestLowest(p);				\
	}							\
    }	/* end of macro of AddHost */
#endif	/* defined(SLOWSCREEN) */

    AddHost(position, character);
}


/* 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).
 */

int
DoTerminalOutput()
{
	/* called just before a select to conserve IO to terminal */
    if (!Initialized) {
	return 1;		/* No output if not initialized */
    }
    if ((Lowest <= Highest) || needToRing ||
			(terminalCursorAddress != CorrectTerminalCursor())) {
	(*TryToSend)();
    }
    if (Lowest > Highest) {
	return 1;		/* no more output now */
    } else {
	return 0;		/* more output for future */
    }
}

/* returns the number of characters consumed */
int
DataFromNetwork(buffer, count, control)
register unsigned char	*buffer;		/* what the data is */
register int	count;				/* and how much there is */
int	control;				/* this buffer ended block? */
{
    int origCount;
    register int c;
    register int i;
    static int Command;
    static int Wcc;
    static int	LastWasTerminated = 1;	/* was "control" = 1 last time? */
#if	defined(unix)
    extern char *transcom;
    int inpipefd[2], outpipefd[2], savemode, aborttc();
#endif	/* defined(unix) */

    origCount = count;

    if (LastWasTerminated) {

	if (count < 2) {
	    if (count == 0) {
		StringToTerminal("Short count received from host!\n");
		return(count);
	    }
	    Command = buffer[0];
	    switch (Command) {		/* This had better be a read command */
	    case CMD_READ_MODIFIED:
	    case CMD_SNA_READ_MODIFIED:
	    case CMD_SNA_READ_MODIFIED_ALL:
		DoReadModified(Command);
		break;
	    case CMD_READ_BUFFER:
	    case CMD_SNA_READ_BUFFER:
		DoReadBuffer();
		break;
	    default:
		break;
	    }
	    return(1);			/* We consumed everything */
	}
	Command = buffer[0];
	Wcc = buffer[1];
	if (Wcc & WCC_RESET_MDT) {
	    i = c = WhereAttrByte(LowestScreen());
	    do {
		if (HasMdt(i)) {
		    TurnOffMdt(i);
		}
		i = FieldInc(i);
	    } while (i != c);
	}

	switch (Command) {
	case CMD_ERASE_WRITE:
	case CMD_ERASE_WRITE_ALTERNATE:
	case CMD_SNA_ERASE_WRITE:
	case CMD_SNA_ERASE_WRITE_ALTERNATE:
	    {
		int newlines, newcolumns;

#if	defined(unix)
		if (tcflag == 0) {
		   tcflag = -1;
		   (void) signal(SIGCHLD, SIG_DFL);
		} else if (tcflag > 0) {
		   setcommandmode();
		   (void) close(tin);
		   (void) close(tout);
		   tin = savefd[0];
		   tout = savefd[1];
		   setconnmode();
		   tcflag = -1;
		   (void) signal(SIGCHLD, SIG_DFL);
		}
#endif	/* defined(unix) */
		if ((Command == CMD_ERASE_WRITE)
				|| (Command == CMD_SNA_ERASE_WRITE)) {
		    newlines = 24;
		    newcolumns = 80;
		} else {
		    newlines = MaxNumberLines;
		    newcolumns = MaxNumberColumns;
		}
		if ((newlines != NumberLines)
				|| (newcolumns != NumberColumns)) {
			/*
			 * The LocalClear3270() is really for when we
			 * are going from a larger screen to a smaller
			 * screen, and we need to clear off the stuff
			 * at the end of the lines, or the lines at
			 * the end of the screen.
			 */
		    LocalClear3270();
		    NumberLines = newlines;
		    NumberColumns = newcolumns;
		    ScreenSize = NumberLines * NumberColumns;
		}
		Clear3270();
		if (TransparentClock == OutputClock) {
		    clearok(curscr, TRUE);
		}
		break;
	    }

	case CMD_ERASE_ALL_UNPROTECTED:
	case CMD_SNA_ERASE_ALL_UNPROTECTED:
	    CursorAddress = HighestScreen()+1;
	    for (i = LowestScreen(); i <= HighestScreen(); i = ScreenInc(i)) {
		if (IsUnProtected(i)) {
		    if (CursorAddress > i) {
			CursorAddress = i;
		    }
		    AddHost(i, '\0');
		}
		if (HasMdt(i)) {
		    TurnOffMdt(i);
		}
	    }
	    if (CursorAddress == HighestScreen()+1) {
		CursorAddress = SetBufferAddress(0,0);
	    }
	    UnLocked = 1;
	    AidByte = 0;
	    break;
	case CMD_WRITE:
	case CMD_SNA_WRITE:
	    break;
	default:
	    {
		char buffer[100];

		sprintf(buffer, "Unexpected command code 0x%x received.\n",
								Command);
		ExitString(stderr, buffer, 1);
		break;
	    }
	}

	count -= 2;			/* strip off command and wcc */
	buffer += 2;

    }
    LastWasTerminated = 0;		/* then, reset at end... */

    while (count) {
	count--;
	c = *buffer++;
	if (IsOrder(c)) {
	    /* handle an order */
	    switch (c) {
#		define Ensure(x)	if (count < x) { \
					    if (!control) { \
						return(origCount-(count+1)); \
					    } else { \
						/* XXX - should not occur */ \
						count = 0; \
						break; \
					    } \
					}
	    case ORDER_SF:
		Ensure(1);
		c = *buffer++;
		count--;
#if	!defined(SLOWSCREEN)
		Highest = HighestScreen();
		Lowest = LowestScreen();
#endif	/* !defined(SLOWSCREEN) */
		if ( ! (IsStartField(BufferAddress) &&
					FieldAttributes(BufferAddress) == c)) {
#if	defined(SLOWSCREEN)
		    if (NotVisuallyCompatibleAttributes(BufferAddress, c,
					FieldAttributes(BufferAddress))) {
			SetHighestLowest(BufferAddress);
		    }
		    if (GetTerminal(BufferAddress)) {
			SetHighestLowest(BufferAddress);
		    }
#endif	/* defined(SLOWSCREEN) */
		    NewField(BufferAddress,c);
		}
		SetHost(BufferAddress, 0);
		BufferAddress = ScreenInc(BufferAddress);
		break;
	    case ORDER_SBA:
		Ensure(2);
		i = buffer[0];
		c = buffer[1];
		if (!i && !c) { /* transparent write */
		    if (!control) {
			return(origCount-(count+1));
		    } else {
			while (Initialized &&
				((Lowest <= Highest) || needToRing ||
				(terminalCursorAddress !=
						CorrectTerminalCursor()))) {
#if defined(unix)
			    HaveInput = 0;
#endif /* defined(unix) */
			    (*TryToSend)();
			}
			if (TransparentClock != OutputClock) {
			    if (!DoTerminalOutput()) {
				return(origCount-(count+1));
			    }
			    move(ScreenLine(CursorAddress),
					ScreenLineOffset(CursorAddress));
			    DoARefresh();
			}
			TransparentClock = OutputClock; 	/* this clock */
#if	defined(unix)
			if (transcom && tcflag == -1) {
			   while (1) {			  /* go thru once */
				 if (pipe(outpipefd) < 0) {
				    break;
				 }
				 if (pipe(inpipefd) < 0) {
				    break;
				 }
			         if ((tcflag = fork()) == 0) {
				    (void) close(outpipefd[1]);
				    (void) close(0);
				    if (dup(outpipefd[0]) < 0) {
				       exit(1);
				    }
				    (void) close(outpipefd[0]);
				    (void) close(inpipefd[0]);
				    (void) close(1);
				    if (dup(inpipefd[1]) < 0) {
				       exit(1);
				    }
				    (void) close(inpipefd[1]);
				    if (execl("/bin/csh", "csh", "-c", transcom, (char *) 0)) {
					exit(1);
				    }
				 }
				 (void) close(inpipefd[1]);
				 (void) close(outpipefd[0]);
				 savefd[0] = tin;
				 savefd[1] = tout;
				 setcommandmode();
				 tin = inpipefd[0];
				 tout = outpipefd[1];
				 (void) signal(SIGCHLD, aborttc);
				 setconnmode();
				 tcflag = 1;
				 break;
			   }
			   if (tcflag < 1) {
			      tcflag = 0;
			   }
			}
#endif	/* defined(unix) */
			(void) DataToTerminal(buffer+2, count-2);
			SendToIBM();
			TransparentClock = OutputClock+1;	/* clock next */
			buffer += count;
			count -= count;
		    }
		} else {
		    BufferAddress = Addr3270(i, c);
		    buffer += 2;
		    count -= 2;
		}
		break;
	    case ORDER_IC:
		CursorAddress = BufferAddress;
		break;
	    case ORDER_PT:
		for (i = ScreenInc(BufferAddress); (i != HighestScreen());
				i = ScreenInc(i)) {
		    if (IsStartField(i)) {
			i = ScreenInc(i);
			if (!IsProtected(ScreenInc(i))) {
			    break;
			}
			if (i == HighestScreen()) {
			    break;
			}
		    }
		}
		CursorAddress = i;
		break;
	    case ORDER_RA:
		Ensure(3);
		i = Addr3270(buffer[0], buffer[1]);
		c = buffer[2];
		do {
		    AddHost(BufferAddress, c);
		    BufferAddress = ScreenInc(BufferAddress);
		} while (BufferAddress != i);
		buffer += 3;
		count -= 3;
		break;
	    case ORDER_EUA:    /* (from [here,there), ie: half open interval] */
		Ensure(2);
		c = FieldAttributes(WhereAttrByte(BufferAddress));
		for (i = Addr3270(buffer[0], buffer[1]); i != BufferAddress;
				BufferAddress = ScreenInc(BufferAddress)) {
		    if (!IsProtectedAttr(BufferAddress, c)) {
			AddHost(BufferAddress, 0);
		    }
		}
		buffer += 2;
		count -= 2;
		break;
	    case ORDER_YALE:		/* special YALE defined order */
		Ensure(2);	/* need at least two characters */
		if (*buffer == 0x5b) {
		    i = OptOrder(buffer+1, count-1, control);
		    if (i == 0) {
			return(origCount-(count+1));	/* come here again */
		    } else {
			buffer += 1 + i;
			count  -= (1 + i);
		    }
		}
		break;
	    default:
		break;				/* XXX ? */
	    }
	    if (count < 0) {
		count = 0;
	    }
	} else {
	    /* Data comes in large clumps - take it all */
	    i = BufferAddress;
#if	!defined(SLOWSCREEN)
	    AddHost(i, c);
#else	/* !defined(SLOWSCREEN) */
	    AddHostA(i, c);
	    SetHighestLowest(i);
#endif	/* !defined(SLOWSCREEN) */
	    i = ScreenInc(i);
	    c = *buffer;
	    while (count && !IsOrder(c)) {
#if	!defined(SLOWSCREEN)
		AddHost(i, c);
#else	/* !defined(SLOWSCREEN) */
		AddHostA(i, c);
#endif	/* !defined(SLOWSCREEN) */
		i = ScreenInc(i);
#if	defined(SLOWSCREEN)
		if (i == LowestScreen()) {
		    SetHighestLowest(HighestScreen());
		}
#endif	/* defined(SLOWSCREEN) */
		count--;
		buffer++;
		c = *buffer;
	    }
#if	defined(SLOWSCREEN)
	    SetHighestLowest(i);
#endif	/* defined(SLOWSCREEN) */
	    BufferAddress = i;
	}
    }
    if (count == 0) {
	OutputClock++;		/* time rolls on */
	if (control) {
	    if (Wcc & WCC_RESTORE) {
		if (TransparentClock != OutputClock) {
		    AidByte = 0;
		}
		UnLocked = 1;
		(void) TerminalIn();	/* move along user's input */
	    }
	    if (Wcc & WCC_ALARM) {
		RingBell();
	    }
	}
	LastWasTerminated = control;	/* state for next time */
	return(origCount);
    } else {
	return(origCount-count);
    }
}


#if	defined(unix)
aborttc()
{
	int savemode;

	setcommandmode();
	(void) close(tin);
	(void) close(tout);
	tin = savefd[0];
	tout = savefd[1];
	setconnmode();
	tcflag = 0;
}
#endif	/* defined(unix) */
