#include "ckcsym.h"
#ifdef NOLOCAL
char *connv = "";
#else
char *connv = "OS-9 Connect Command, 7.0.015, 1 Jan 2000";

/*  C K 9 C O N  --  Dumb terminal connection to remote system, for OS-9  */
/*
 Modified from ckucon.c by Bob Larson (blarson@ecla.usc.edu)
 Edition: 5A(01)
    by Chris Hemsing, Aachen, W Germany (chris@lfm.rwth-aachen.de):
    More efficient using sigmask, added character set translation
 Edition: 5A(02)
 07/25/91 Chris Hemsing     minor bug fixes, changes for gnu (ansi) C
                            flow control on both sides
 Edition: 5A(03)
 03/04/92 Chris Hemsing     Kanji bug fix
 Edition: 5A(04)
 08/20/92 Chris Hemsing     flow control bug fix on return from local shell
 Edition: 5A(05)
 10/01/92 Chris Hemsing     added sending xon via escape character
 Edition: 5A(06)
 10/09/92 Chris Hemsing     escape back to local even on receive burst
 Edition: 5A(07)
 12/22/94 Ulli Schlueter    improved overall performance (system load is 
                            reduced about 80%, see ck9tio.c),
                            brought it near to the unix version that
                            means: added APC and ansi escape sequence
                            recognition, text session logging, support of
                            SET TERM CR-DISPLAY, support of SET TERM NEWLINE,
                            debug session, keyboard macros and some escape
                            character features
 Edition: 5A(08)
 01/26/95 Ulli Schlueter    added network support
 Edition: 5A(09)
 03/13/95 Ulli Schlueter    i/o buffers dynamic
 Edition: 5A(10)
 04/19/95 Ulli Schlueter    Changed handling of CR from keyboard. Added
                            handling of telnet binary mode.
 Edition: 5A(11)
 04/21/95 Ulli Schlueter    Incoming telnet CR/NUL is handled. Made debugging
                            display prettier.
 Edition: 5A(12)
 04/25/95 Ulli Schlueter    Keyboard i/o buffered.

 Edition: 7.0.15
 1 Jan 2000 S Rance, F. da Cruz: Adapt to C-Kermit 7.0 and Ultra C.

  Author: Frank da Cruz <fdc@columbia.edu>
  Columbia University Center for Computing Activities, January 1985.

  Copyright (C) 1985, 2000,
    Trustees of Columbia University in the City of New York.
    All rights reserved.  See the C-Kermit COPYING.TXT file or the
    copyright text in the ckcmai.c module for disclaimer and permissions.
*/
#include "ckcdeb.h"
#include "ckucmd.h"
#include "ckcker.h"
#include "ckcasc.h"
#ifndef NOCSETS
#include "ckcxla.h"                     /* Character set translation */
#endif /* NOCSETS */
#include "ckcnet.h"
#include <errno.h>
#include <sgstat.h>			/* Set/Get tty modes */

#ifndef EOS_NOTRDY
#ifdef E_NOTRDY
#define EOS_NOTRDY E_NOTRDY
#endif /* E_NOTRDY */
#endif /* EOS_NOTRDY */

_PROTOTYP( VOID doesc, (char) );
_PROTOTYP( static VOID logchar, (char) );
_PROTOTYP( int hconne, (void) );

extern int local, escape, duplex, parity, flow, seslog, sessft, debses, nopush,
 mdmtyp, ttnproto, cmask, cmdmsk, network, nettype, deblog, sosi, tnlm,
 xitsta, what, ttyfd, quiet, backgrd, pflag, tt_crd/*, ttfdflg*/;
#ifdef TCPSOCKET
extern int tn_nlm, tn_b_nlm, tn_binary;
#endif /* TCPSOCKET */
extern long speed;
extern char ttname[], sesfil[], myhost[], *ccntab[];

/* from ck9tio.c . . . */
/* console/tty signal flags (incremented by catch) */
extern int csigflg, tsigflg;

#ifdef CK_APC
extern int apcactive;                   /* Application Program Command (APC) */
extern int apcstatus;                   /* items ... */
static int apclength = 0;          
#ifdef DCMDBUF
extern char *apcbuf;
#else
extern char apcbuf[];
#endif /* DCMDBUF */
static int apcbuflen = APCBUFLEN - 2;
#endif /* CK_APC */

/* Character-set items */

#ifndef NOCSETS
#ifdef CK_ANSIC /* ANSI C prototypes... */
extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Character set */
extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* translation functions */
static CHAR (*sxo)(CHAR);       /* Local translation functions */
static CHAR (*rxo)(CHAR);       /* for output (sending) terminal chars */
static CHAR (*sxi)(CHAR);       /* and for input (receiving) terminal chars. */
static CHAR (*rxi)(CHAR);
#else /* Not ANSI C... */
extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); /* Character set */
extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(); /* translation functions. */
static CHAR (*sxo)();           /* Local translation functions */
static CHAR (*rxo)();           /* for output (sending) terminal chars */
static CHAR (*sxi)();           /* and for input (receiving) terminal chars. */
static CHAR (*rxi)();
#endif /* CK_ANSIC */
extern int language;            /* Current language. */
extern struct csinfo fcsinfo[]; /* File character set info. */
extern int tcsr, tcsl;          /* Terminal character sets, remote & local. */
#endif /* NOCSETS */

/*
  We do not need to parse and recognize escape sequences if we are being built
  without character-set support AND without APC support.
*/
#ifdef NOCSETS                          /* No character sets */
#ifndef CK_APC                          /* No APC */
#ifndef NOESCSEQ
#define NOESCSEQ                        /* So no escape sequence recognizer */
#endif /* NOESCSEQ */
#endif /* CK_APC */
#endif /* NOCSETS */

#ifndef NOSETKEY                    /* Keyboard mapping */
extern KEY *keymap;                 /* Single-character key map */
extern MACRO *macrotab;             /* Key macro pointer table */
#endif /* NOSETKEY */

#ifdef NOESCSEQ
#define chkaes(x, y)
#else
/*
  As of edit 178, the CONNECT command will skip past ANSI escape sequences
  to avoid translating the characters within them.  This allows the CONNECT
  command to work correctly when connected to a remote host that uses a
  7-bit ISO 646 national character set, in which characters like '[' would
  normally be translated into accented characters, ruining the terminal's
  interpretation (and generation) of escape sequences.

  As of edit 190, the CONNECT command responds to APC escape sequences
  (ESC _ text ESC \) if the user SETs TERMINAL APC ON or UNCHECKED, and the
  program was built with CK_APC defined.

  Non-ANSI/ISO-compliant escape sequences are not handled.
*/

/*
  States for the escape-sequence recognizer.
*/
#define ES_NORMAL 0                     /* Normal, not in an escape sequence */
#define ES_GOTESC 1                     /* Current character is ESC */
#define ES_ESCSEQ 2                     /* Inside an escape sequence */
#define ES_GOTCSI 3                     /* Inside a control sequence */
#define ES_STRING 4                     /* Inside DCS,OSC,PM, or APC string */
#define ES_TERMIN 5                     /* 1st char of string terminator */

static CHAR escseq;                     /* 1 = Recognizer is active */
static struct _escinf {
  char
  inesc,                                /* State of sequence recognizer */
  oldesc;                               /* Previous state of recognizer */
} escinf[2];

/*
  ANSI escape sequence handling.  Only the 7-bit form is treated, because
  translation is not a problem in the 8-bit environment, in which all GL
  characters are ASCII and no translation takes place.  So we don't check
  for the 8-bit single-character versions of CSI, DCS, OSC, APC, or ST.
  Here is the ANSI sequence recognizer state table, followed by the code
  that implements it.

  Definitions:
    CAN = Cancel                       01/08         Ctrl-X
    SUB = Substitute                   01/10         Ctrl-Z
    DCS = Device Control Sequence      01/11 05/00   ESC P
    CSI = Control Sequence Introducer  01/11 05/11   ESC [
    ST  = String Terminator            01/11 05/12   ESC \
    OSC = Operating System Command     01/11 05/13   ESC ]
    PM  = Privacy Message              01/11 05/14   ESC ^
    APC = Application Program Command  01/11 05/15   ESC _

  ANSI escape sequence recognizer:

    State    Input  New State  ; Commentary

    NORMAL   (start)           ; Start in NORMAL state

    (any)    CAN    NORMAL     ; ^X cancels
    (any)    SUB    NORMAL     ; ^Z cancels

    NORMAL   ESC    GOTESC     ; Begin escape sequence
    NORMAL   other             ; NORMAL control or graphic character

    GOTESC   ESC               ; Start again
    GOTESC   [      GOTCSI     ; CSI
    GOTESC   P      STRING     ; DCS introducer, consume through ST
    GOTESC   ]      STRING     ; OSC introducer, consume through ST
    GOTESC   ^      STRING     ; PM  introducer, consume through ST
    GOTESC   _      STRING     ; APC introducer, consume through ST
    GOTESC   0..~   NORMAL     ; 03/00 through 17/14 = Final character
    GOTESC   other  ESCSEQ     ; Intermediate or ignored control character

    ESCSEQ   ESC    GOTESC     ; Start again
    ESCSEQ   0..~   NORMAL     ; 03/00 through 17/14 = Final character
    ESCSEQ   other             ; Intermediate or ignored control character

    GOTCSI   ESC    GOTESC     ; Start again
    GOTCSI   @..~   NORMAL     ; 04/00 through 17/14 = Final character
    GOTCSI   other             ; Intermediate char or ignored control char

    STRING   ESC    TERMIN     ; Maybe have ST
    STRING   other             ; Consume all else

    TERMIN   \      NORMAL     ; End of string
    TERMIN   other  STRING     ; Still in string
*/
/*
  chkaes() -- Check ANSI Escape Sequence.
  Call with EACH character in input stream.
  Sets global inesc variable according to escape sequence state.
  Returns 0 normally, 1 if an APC sequence is to be executed.
*/
int
#ifdef CK_ANSIC
chkaes(char c, struct _escinf *inf)
#else
chkaes(c, inf) char c; struct _escinf *inf;
#endif /* CK_ANSIC */
/* chkaes */ {

    inf->oldesc = inf->inesc;           /* Remember previous state */
    if (c == CAN || c == SUB)           /* CAN and SUB cancel any sequence */
      inf->inesc = ES_NORMAL;
    else                                /* Otherwise */
      switch (inf->inesc) {             /* enter state switcher */

        case ES_NORMAL:                 /* NORMAL state */
          if (c == ESC)                 /* Got an ESC */
            inf->inesc = ES_GOTESC;     /* Change state to GOTESC */
          break;                        /* Otherwise stay in NORMAL state */

        case ES_GOTESC:                 /* GOTESC state */
          if (c == '[')                 /* Left bracket after ESC is CSI */
            inf->inesc = ES_GOTCSI;     /* Change to GOTCSI state */
          else if (c == 'P' || (c > 0134 && c < 0140)) { /* P, [, ^, or _ */
              inf->inesc = ES_STRING;   /* Switch to STRING-absorption state */
#ifdef CK_APC
	      /* APC handled in child only */
              if (c == '_' && inf == &escinf[1] &&
                  apcstatus != APC_OFF) { 
                  debug(F100,"APC begin","",0);
                  apcactive = 1;        /* Set APC-Active flag */
                  apclength = 0;        /* and reset APC buffer pointer */
              }
#endif /* CK_APC */
          } else if (c > 057 && c < 0177) /* Final character '0' thru '~' */
            inf->inesc = ES_NORMAL;     /* Back to normal */
          else if (c != ESC)            /* ESC in an escape sequence... */
            inf->inesc = ES_ESCSEQ;     /* starts a new escape sequence */
          break;                        /* Intermediate or ignored ctrl char */

        case ES_ESCSEQ:                 /* ESCSEQ -- in an escape sequence */
          if (c > 057 && c < 0177)      /* Final character '0' thru '~' */
            inf->inesc = ES_NORMAL;     /* Return to NORMAL state. */
          else if (c == ESC)            /* ESC ... */
            inf->inesc = ES_GOTESC;     /* starts a new escape sequence */
          break;                        /* Intermediate or ignored ctrl char */

        case ES_GOTCSI:                 /* GOTCSI -- In a control sequence */
          if (c > 077 && c < 0177)      /* Final character '@' thru '~' */
            inf->inesc = ES_NORMAL;     /* Return to NORMAL. */
          else if (c == ESC)            /* ESC ... */
            inf->inesc = ES_GOTESC;     /* starts over. */
          break;                        /* Intermediate or ignored ctrl char */

        case ES_STRING:                 /* Inside a string */
          if (c == ESC)                 /* ESC may be 1st char of terminator */
            inf->inesc = ES_TERMIN;     /* Go see. */
#ifdef CK_APC
          else if (apcactive && (apclength < apcbuflen)) /* If in APC, */
            apcbuf[apclength++] = c;    /* deposit this character. */
          else {                        /* Buffer overrun */
              apcactive = 0;            /* Discard what we got */
              apclength = 0;            /* and go back to normal. */
              apcbuf[0] = 0;            /* Not pretty, but what else */
              inf->inesc = ES_NORMAL;   /* can we do? (ST might not come) */
          }
#endif /* CK_APC */
          break;                        /* Absorb all other characters. */

        case ES_TERMIN:                 /* May have a string terminator */
          if (c == '\\') {              /* which must be backslash */
              inf->inesc = ES_NORMAL;   /* If so, back to NORMAL */
#ifdef CK_APC
              if (apcactive) {          /* If it was an APC string, */
                  debug(F101,"APC terminated","",c);
                  apcbuf[apclength] = NUL; /* terminate it and then ... */
                  return(1);
              }
#endif /* CK_APC */
          } else {                      /* Otherwise */
              inf->inesc = ES_STRING;   /* Back to string absorption. */
#ifdef CK_APC
              if (apcactive && (apclength+1 < apcbuflen)) { /* In APC string */
                  apcbuf[apclength++] = ESC; /* deposit the Esc character */
                  apcbuf[apclength++] = c;   /* and this character too */
              }
#endif /* CK_APC */
          }
      }
    return(0);
}
#endif /* NOESCSEQ */

static CHAR active,                     /* Variables global to this module */
    quitnow,
#ifdef NETCONN
    telnet_cnct,                        /* 1 if telnet connection */
#endif /* NETCONN */
#ifdef TNCODE
    c_ttprv,                            /* Previous character from comm line */
#endif /* TNCODE */
    c_slprv,                            /* Previous session log character */
    inshift, outshift,                  /* SO/SI shift state */
    dohangup;   /* 2 = user requested hangup, 1 = hangup due to error */

#define LIBUFL 200                      /* Line buffer */
#define LOBUFL (2*LIBUFL)               /* for cr/lf display */
#ifdef DYNAMIC
static CHAR *libuf;
static CHAR *lobuf;
#else
static CHAR libuf[LIBUFL];
static CHAR lobuf[LOBUFL];
#endif /* DYNAMIC */
static int ibc;
static CHAR *ibp;

#define KIBUFL 80
#define KOBUFL (3*KIBUFL)
#ifdef DYNAMIC
static CHAR *kibuf, *kobuf;
#else
static CHAR kibuf[KIBUFL];
static CHAR kobuf[KOBUFL];
#endif /* DYNAMIC */
static CHAR *kibp, *kobp;
static int kibc, kobc;

static struct sgbuf opt;		/* sgtty info... */

/*  C K C G E T C  --  C-Kermit CONNECT Get Character  */
/*
  Semi buffered read from communication device.
  On error, returns ttinc's return code (see ttinc() description).
  Dummy argument for compatible calling conventions with ttinc().
  NOTE: We don't have a macro for this because we have to pass
  a pointer to this function as an argument to tn_doop().
*/
int
ckcgetc(dummy) int dummy; {
    if (ibc > 0) {
        ibc -= 1;
        return *ibp++;
    }
    else return ttinc(0);
}

/*  K B G E T C  --  Get one character from keyboard (semi buffered). */
static int
kbgetc () {
    if (kibc > 0) {
        kibc -= 1;
        return *kibp++;
    }
    return coninc(0);
}

/*  K B P U T C  --  Send one character from keyboard (buffered) */
/*
  Using write() and not ttol() because less overhead. When an error occurrs,
  ttol() will call ttclos() on network connections where as ttoc(), which was
  used for output in the unbuffered version, will not (ckutio.c: why that?).
  The first arg of ttol() schould (must) be a null terminated string!?
*/
static int
kbputc (c) int c; {
    if (c < 0 || kobc >= KOBUFL) {
        if (write(ttyfd, kobuf, kobc) < 0) {
            debug(F101, "conect kbputc errno", NULL, errno);
            return -1;
        }
        kobp = kobuf;
        kobc = 0;
    }
    if (!(c < 0)) {
        *kobp++ = dopar(c);
        kobc += 1;
    }
    return 0;
}

/*  C O N E C T  --  Perform terminal connection  */

conect() {
    register int c;               /* c is a character, but must be signed
                                   integer to pass thru -1, which is the
                                   modem disconnection signal, and is
                                   different from the character 0377 */
    register int csave;           /* Another copy   of c */
    register int n;
    char temp[80];
    int we_have_data;
#ifndef NOESCSEQ
    int apcrc = 0;                /* APC recognized flag */
#endif /* NOESCSEQ */
#ifndef NOCSETS
    int langsv;                 /* For remembering language setting. */
    int tcs;                    /* Intermediate ("transfer") character set. */
#endif /* NOCSETS */
#ifndef NOSETKEY
    MACRO kmptr = NULL;         /* Pointer to current key macro */
#endif /* NOSETKEY */

    if (!local) {
#ifdef NETCONN
        printf("Sorry, you must SET LINE or SET HOST first\n");
#else
        printf("Sorry, you must SET LINE first\n");
#endif /* !NETCONN */
        return(0);
    }
    if (speed < 0 && network == 0 /*&& ttfdflg == 0*/) {
        printf("Sorry, you must SET SPEED first\n");
        return(0);
    }
#ifdef COMMENT
/* This is handled by SET ESCAPE */
    if ((escape < 0) || (escape > 0177)) {
        printf("Your escape character is not ASCII - %d\n",escape);
        return(0);
    }
#endif /* COMMENT */

#ifdef DYNAMIC
    if (libuf == NULL) {
        /* Allocate input/output buffers */
        n = LIBUFL + LOBUFL + KIBUFL + KOBUFL;
        if ((libuf = (CHAR *)malloc(n)) == NULL) {
            printf("Sorry, CONNECT i/o buffers can't be allocated\n");
            return 0;
        }
        lobuf = libuf + LIBUFL;
        kibuf = lobuf + LOBUFL;
        kobuf = kibuf + KIBUFL;
    }
#endif /* DYNAMIC */

    if (ttopen(ttname,&local,
#ifdef NETCONN
        network ? -nettype : mdmtyp,
#else
        mdmtyp,
#endif /* NETCONN */
        0) < 0) {
        sprintf(temp,"Sorry, can't open %s",ttname);
        perror(temp);
        return(0);
    }

    dohangup = 0;                       /* Hangup not requested yet */

    if (!quiet
#ifdef CK_APC
        && !apcactive
#endif /* CK_APC */
    ) {
#ifdef NETCONN
        printf("Connecting %s %s", network ? "to host" : "through", ttname);
        if (!network && speed >= 0L) printf(", speed %d", speed);
#else
        printf("Connecting thru %s, speed %d",ttname,speed);
#endif /* NETCONN */
        printf(".\nThe escape character is Ctrl-%c (ASCII %d, %s)\n",
        ctl(escape), escape, escape == 127 ? "DEL" : ccntab[escape]);
        printf("Type the escape character followed by C to get back,\n");
        printf("or followed by ? to see other options.\n");
        if (seslog) printf("(Session logged to %s, %s)\n",sesfil,
            sessft ? "binary" : "text");
        if (debses) printf("(Debugging Display...)\n");
    }

/* Condition console terminal and communication line */

    if (conbin(escape) < 0) {
        printf("Sorry, can't condition console terminal\n");
        return(0);
    }
    if (flow==1) { /*if xon/xoff you should have it running on both*/
        if (_gs_opt(0,&opt) <0)  /* Structure for restoring */
        {
            printf("Sorry, can't condition console terminal\n");
            return(0);
        }
        opt.sg_xon = 0x11;
        opt.sg_xoff = 0x13;
        _ss_opt(0,&opt);
        _ss_opt(1,&opt);            /* set new modes . */
    }
    if (ttvt(speed,flow) < 0) {
        conres();
        printf("Sorry, Can't condition communication line\n");
        return(0);
    }

#ifdef NETCONN
    telnet_cnct = network && ttnproto == NP_TELNET;
#endif /* NETCONN */

#ifndef NOCSETS
/* Set up character set translations */

    tcs = gettcs(tcsr, tcsl);           /* Get intermediate set */

    if (tcsr == tcsl) {                 /* Remote and local sets the same? */
        sxo = rxo = NULL;               /* If so, no translation. */
        sxi = rxi = NULL;
    } else {                            /* Otherwise, set up */
        sxo = xls[tcs][tcsl];           /* translation function */
        rxo = xlr[tcs][tcsr];           /* pointers for output functions */
        sxi = xls[tcs][tcsr];           /* and for input functions. */
        rxi = xlr[tcs][tcsl];
    }

/*
  This is to prevent use of zmstuff() and zdstuff() by translation functions.
  They only work with disk i/o, not with communication i/o.  Luckily Russian
  translation functions don't do any stuffing...
*/
    langsv = language;
#ifndef NOCYRIL
    if (language != L_RUSSIAN)
#endif /* NOCYRIL */
      language = L_USASCII;

#ifndef NOESCSEQ
/*
  We need to activate the escape-sequence recognition feature when:
   (a) translation is elected, AND
   (b) the local and/or remote set is a 7-bit set other than US ASCII.
 Or:
   SET TERMINAL APC is not OFF (handled in the next statement).
*/
    escseq = (tcs != TC_TRANSP) &&      /* Not transparent */
      (fcsinfo[tcsl].size == 128 || fcsinfo[tcsr].size == 128) && /* 7 bits */
        (fcsinfo[tcsl].code != FC_USASCII); /* But not ASCII */
#endif /* NOESCSEQ */
#endif /* NOCSETS */

#ifndef NOESCSEQ
#ifdef CK_APC
    escseq = escseq || (apcstatus != APC_OFF);
    apcactive = 0;                      /* An APC command is not active */
    apclength = 0;                      /* ... */
#endif /* CK_APC */
    escinf[0].oldesc =
    escinf[1].oldesc = -1;
    escinf[0].inesc =
    escinf[1].inesc = ES_NORMAL;        /* Initial state of recognizer */
    debug(F101,"escseq","",escseq);
#endif /* NOESCSEQ */

    inshift = outshift = 0;     /* Initial shift state. */
/*
  Main loop. The treatment of the 8th bit of keyboard characters
  is governed by SET COMMAND BYTESIZE (cmdmsk).  The treatment of the 8th bit
  of characters sent to the remote is governed by SET TERMINAL BYTESIZE
  (cmask).   This distinction was introduced in edit C-Kermit 5A(164).
*/
    what = W_CONNECT;           /* Keep track of what we're doing */
    active = 1;
    csigflg = tsigflg = 1;      /* force signal setup */
    while (active) {
/*
  Increment signal mask, signals must be masked between _ss_ssig and sleep
*/
        if (ibc <= 0) {
            sigmask(1);
            if (csigflg) {
                csigflg = 0;
                _ss_ssig(0, SIGARB);
            }
#ifdef NETCONN
            if (!network) {
#endif /* NETCONN */
                if (tsigflg) {
                    tsigflg = 0;
                    _ss_ssig(ttyfd, SIGARB+1);
                }
                sleep(0); /* signal mask is cleared */
#ifdef NETCONN
            } else {
                extern int ttevid;
/*
  Note: We're going to wait for an event with the signal mask set. That's ok,
  because only then the event wait routine recognizes signals that arrived
  between the above call of sigmask and the entry of event wait! The effect is
  that an arriving signal will put us in the active queue, but the intercept
  routine is not executed. That is ok too, because after the execution of the
  below sigmask call the signal mask is clear and the os9call exception handler
  checks for pending signals - then the intercept routine is executed.
*/
                _ev_wait(ttevid, 1, 0x7fff);
                sigmask(0);
            }
#endif /* NETCONN */
        }
/*
  Avoid this expensive system call, cause it's unnecessary: we're calling
  read only if there's data available, then no signal is pending anymore.
*/
#ifdef COMMENT
        _ss_rel(0);
        _ss_rel(ttyfd);
#endif /* COMMENT */
/*
  Loop while data on keybord or remote port. Do check console to get
  characters through even if communication line receives a burst.
*/
        do {
            we_have_data = 0;
            if ((n = _gs_rdy(0)) > 0
/*
  Using read because missing conxin() - see the discussion of ckucon.c:kbget().
*/
                ? (n = read(0, kibp = kibuf, n > KIBUFL ? KIBUFL : n)) > 0
                : (n = errno != EOS_NOTRDY ? -1 : 0, 0))
            {
/*
  Wait some arbitrary time for buffer fill to reduce system load and to avoid
  sending small packets over the network.
*/
              if (n < 10) tsleep(2);
              we_have_data = 1;
              kibc = n;
              /* kobc = 0; */
              kobp = kobuf;
              do { /* while (kibc || kmptr) */
#ifndef NOSETKEY
                if (kmptr) {
                    if ((c = (CHAR)*kmptr++) == NUL) {
                        kmptr = NULL;
                        continue; /* get next character from kb or break */
                    }
                } else
#endif /* NOSETKEY */
                  c = kbgetc();           /* Get character from keyboard */
                c &= cmdmsk;   
#ifndef NOSETKEY
/*
  Note: kmptr is NULL if we got character c from the keyboard, and it is
  not NULL if it came from a macro.  In the latter case, we must avoid
  expanding it again.
*/
                if (!kmptr && macrotab[c]) { /* Macro definition for c? */
                    kmptr = macrotab[c];     /* Yes, set up macro pointer */
                    continue;                /* and restart the loop */
                } else c = keymap[c];        /* else use single-char keymap */
#endif /* NOSETKEY */
                if (
#ifndef NOSETKEY
                    !kmptr &&
#endif /* NOSETKEY */
                    (c & 0177) == escape)
                { /* Look for escape char */
                    if (kobc) kbputc(-1);   /* Flush kb output buffer */
                    c = kbgetc() & 0177;    /* Got esc, get its arg */
                    doesc(c);               /* And process it */
                    if (!active) break;     /* immediately leave we_have_data
                                               loop to avoid burst receive */
                }
                else
                {             /* Ordinary character */
                    csave = c;  /* Save it before translation for local echo */
#ifndef NOCSETS
#ifndef NOESCSEQ
                    if (escinf[0].inesc == ES_NORMAL)
#endif /* NOESCSEQ */
                    {
                        /* Translate character sets */
                        if (sxo) c = (*sxo)(c); /* Local to intermediate. */
                        if (rxo) c = (*rxo)(c); /* Intermediate to remote. */
                    }
                    if (escseq) chkaes((char)c, &escinf[0]);
#endif /* NOCSETS */
/*
 If Shift-In/Shift-Out selected and we have a 7-bit connection,
 handle shifting here.
*/
                    if (sosi) {      /* Shift-In/Out selected? */
                        if (cmask == 0177) { /*  In 7-bit environment? */
                            if (c & 0200) {          /* 8-bit character? */
                                if (outshift == 0) { /* If not shifted, */
                                    kbputc(SO);      /* shift. */
                                    outshift = 1;
                                }
                            } else {
                                if (outshift == 1) { /* 7-bit character */
                                    kbputc(SI);      /* If shifted, */
                                    outshift = 0;    /* unshift. */
                                }
                            }
                        }
                        if (c == SO) outshift = 1; /* User typed SO */
                        if (c == SI) outshift = 0; /* User typed SI */
                    }
                    c &= cmask;         /* Apply Kermit-to-host mask now. */
                    kbputc(c);
                    if (c == CR) {              /* Carriage Return */
#ifdef TNCODE           /* Handle TELNET NEWLINE ON/OFF/RAW */
                        if (telnet_cnct) {
                            switch (!TELOPT_ME(TELOPT_BINARY) ?
				    tn_nlm : tn_b_nlm) {
                            case TNL_CRLF:      /* Send CR/LF */
                                kbputc(LF);
                                break;
                            case TNL_CRNUL:     /* Send CR/NUL */
                                kbputc(NUL);
                                break;
                            }
                        }
#endif /* TNCODE */
                        if (tnlm)               /* TERMINAL NEWLINE ON */
                          kbputc(LF);           /* Send CR/LF */
                    }
#ifdef TNCODE
/* If user types the 0xff character (TELNET IAC), it must be doubled. */
                    else                /* Not CR */
                      if ((dopar((CHAR) c) == IAC) && /* IAC (0xff) */
                          telnet_cnct) {
                          kbputc(IAC); /* double it */
                      }
#endif /* TNCODE */
                    while (1) {                 /* Handle local echo */
                        if (duplex) {           /* Half duplex? */
                            if (debses) {
                                conol(dbchr(csave)); /* the original char */
                                if (csave == LF) conoll("");
                            } else
                              conoc(csave);     /* Yes, also echo it. */
                            if (seslog)         /* And maybe log it. */
                              logchar(
			      /* sessft == 0 && csave == '\l' ? '\n' : */
				      csave
				      );
                            if (csave == CR && tt_crd) { /* TERM CR-DISPLAY */
                               csave = LF;
                               continue; /* Output LF and leave loop */
                            }
                        }
                        break; /* Leave pseudo loop */
                    }
                } /* end if ((c & 0177) == escape) */
              } while (kibc
#ifndef NOSETKEY
                       || kmptr
#endif /* NOSETKEY */
                      );
              if (kobc) {                       /* Flush kb output buffer */
                  if (kbputc(-1) < 0) {
                      perror("\r\012Can't send character\012");
                      active = 0;
                  }
              }
            } /* end if data on console */
            else if (n < 0) {
                debug(F101,"conect console errno", NULL, errno);
                active = 0;
            }
            if (active == 0) break; /* Leave we-have-data loop */
/*
   Get characters from the communication line and put to the
   console.
*/
            if ((n = ibc) > 0 || ((n = ttchk()) > 0
                ? (n = ttxin(n > LIBUFL ? LIBUFL : n, ibp = libuf)) > 0
                : (n = errno != EOS_NOTRDY ? -1 : 0, 0)))
            {
                register CHAR *pi = ibp;
                register CHAR *po = lobuf;

                we_have_data = 1;

                do {
                    c = *pi++;
#ifdef TNCODE
                if (c == NUL && telnet_cnct &&
		    !TELOPT_U(TELOPT_BINARY) && c_ttprv == CR) {
                    c_ttprv = c;
                    continue; /* get next character */
                }
                c_ttprv = c;

                /* Handle TELNET negotiations... */

                if (c == IAC && telnet_cnct) {
                    register int tx;
                    debug(F100,"CONNECT got IAC","",0);
                    if (po - lobuf > 0) { /* Dump screen-output buffer */
                        conxo(po - lobuf, (char *)lobuf);
                        po = lobuf;
                    }
                    ibc = n - 1; /* save for ckcgetc */
                    ibp = pi;
                    tx = tn_doop((CHAR)(c & 0xff),duplex,ckcgetc);
                    n = ibc + 1;
                    pi = ibp;
                    if ((tx) == 0) {
                        continue; /* get next character */
                    } else if (tx == -1) { /* I/O error */
                        if (!quiet)
                          printf("\n\012Communications disconnect ");
#ifdef NOSETBUF
                        fflush(stdout);
#endif /* NOSETBUF */
                        ttclos(0);	/* close network connection */
                        dohangup = 1;
                        active = 0;
                        break; /* Leave comm-data loop */
                        /* NOTREACHED */
                    } else if ((tx == 1) && (!duplex)) { /* ECHO change */
                        duplex = 1;     /* Turn on local echo */
                        debug(F101,"CONNECT TELNET duplex change","",duplex);
                        continue; /* get next character */
                    } else if ((tx == 2) && (duplex)) { /* ECHO change */
                        duplex = 0;
                        debug(F101,"CONNECT TELNET duplex change","",duplex);
                        continue; /* get next character */
                    } else if (tx == 3) { /* Quoted IAC */
                        c = IAC;
                    } else continue;    /* Negotiation OK, get next char. */
                }
#endif /* TNCODE */
                    if (debses) {
                         while (1) {
                             conol(dbchr(c));
                             if (c == LF) conoll("");
                             if (seslog) logchar(c);
                             if (c == CR && tt_crd) { /* TERM CR-DISPLAY */
                                 c = LF;
                                 continue; /* Output LF and leave loop */
                             }
                             break; /* Leave pseudo loop */
                         }
                    } else {
                        c &= cmask;
                        if (sosi) {
                            if (c == SO) {
                                inshift = 1;
                                continue; /* get next character */
                            } else if (c == SI) {
                                inshift = 0;
                                continue; /* get next character */
                            }
                            if (inshift) c |= 0200;
                        }
#ifndef NOCSETS
#ifndef NOESCSEQ
			/* If not in an esc sequ. */
                        if (escinf[1].inesc == ES_NORMAL)
#endif /* NOESCSEQ */
                        {
                            /* Translate character sets */
                            if (sxi) c = (*sxi)(c);
                            if (rxi) c = (*rxi)(c);
                        }
#endif /* NOCSETS */

#ifndef NOESCSEQ
                        if (escseq)     /* If handling escape sequences */
                            /* update our state */
                            apcrc = chkaes((char)c, &escinf[1]);
#ifdef CK_APC
/*
  If we are handling APCs, we have several possibilities at this point:
   1. Ordinary character to be written to the screen.
   2. An Esc; we can't write it because it might be the beginning of an APC.
   3. The character following an Esc, in which case we write Esc, then char,
      but only if we have not just entered an APC sequence.
*/
                        if (escseq && apcstatus != APC_OFF) {
                            if (escinf[1].inesc == ES_GOTESC)
                                /* Don't write ESC yet */
                                continue; /* Handle next character */
                            else if (escinf[1].oldesc == ES_GOTESC
                                && !apcactive) {
                                *po++ = ESC;            /* Write saved ESC */
                                if (seslog) logchar((char)ESC);
                            } else if (apcrc) {         /* We have an APC */
                                debug(F111,"APC complete",apcbuf,apclength);
                                active = 0;
                                n--;	/* uncount character */
                                break;	/* Force screen update */
                            }
                        }
#endif /* CK_APC */
#endif /* NOESCSEQ */

#ifdef CK_APC
                        if (!apcactive) /* Ignore when in APC sequence */
#endif /* CK_APC */
                        {
                            c &= cmdmsk; /* Apply command mask. */
                            if (c == CR && tt_crd) { /* SET TER CR-DISP CRLF */
                                *po++ = c;
                                if (seslog) logchar(c);
                                c = LF;
                            }
                            *po++ = c;
                        }
                        if (seslog) logchar(c); /* Handle session log */
                    }  /* end no debug session */
                } while (--n > 0);
                ibc = n; /* save remaining input count */
                ibp = pi; /* save input buffer position */
                if (po - lobuf > 0)
                conxo(po - lobuf,(char *)lobuf); /* Output whole buffer */
            }  /* end data on communications line */
            else if (n < 0) {
                debug(F101, "conect comm errno", NULL, errno);
                if (!quiet)
                  printf("\n\lCommunications disconnect ");
                if (network) ttclos(0);
                dohangup = 1;
                active = 0;
            }
        } while (we_have_data && active); /* end while(we_have_data) */
    }  /* end while(active) */
    _ss_rel(0);
    if (!network) _ss_rel(ttyfd);
    conres();                           /* Reset the console. */
    if (dohangup > 0) {                 /* If hangup requested, do that. */
#ifndef NODIAL
        if (dohangup > 1)               /* User asked for it */
          if (mdmhup() < 1)             /* Maybe hang up via modem */
#endif /* NODIAL */
            tthang();                   /* And make sure we don't hang up */
        dohangup = 0;                   /* again unless requested again. */
    }
    if (quitnow) doexit(GOOD_EXIT, xitsta); /* Exit now if requested. */
    if (!quiet
#ifdef CK_APC
        && !apcactive
#endif /* CK_APC */
    )
        printf("[Back at %s]", *myhost ? myhost : "local system");
#ifdef CK_APC
    if (!apcactive)
#endif /* CK_APC */
        printf("\n");
    what = W_NOTHING;           /* So console modes are set right. */
#ifndef NOCSETS
    language = langsv;          /* Restore language */
#endif /* NOCSETS */
    return(1);
}


/*  H C O N N E  --  Give help message for connect.  */

int
#ifdef CK_ANSIC
hconne(void)
#else
hconne()
#endif /* CK_ANSIC */
{
    int c;
    static char *hlpmsg[] = {"\
\r\012C to close the connection, or:\r",
"  0 (zero) to send a null\r",
"  B to send a BREAK\r",
#ifdef NETCONN
"  I to send a network interrupt packet\r",
#ifdef TCPSOCKET
"  A to send Are You There?\r",
#endif /* TCPSOCKET */
#endif /* NETCONN */
"  U to hangup and close connection\r",
"  Q to hangup and quit Kermit\r",
"  S for status\r",
"  X to send an XON\r",
#ifndef NOPUSH
"  ! to push to local shell\r",
#endif /* NOPUSH */
"  ? for help\r",
"  \\ backslash code:\r",
"    \\nnn  decimal character code\r",
"    \\Onnn octal character code\r",
"    \\Xhh  hexadecimal character code\r",
"    terminate with carriage return.\r",
" escape character twice to send the escape character.\r\012\r\012\r",
""};

    conola(hlpmsg);                     /* Print the help message. */
    conol("Command>");                  /* Prompt for command. */
    c = kbgetc() & 0177;                /* Get character, strip any parity. */
    conoc(c);                           /* Echo it. */
    if (c != CMDQ)
        conoll("");
    return(c);                          /* Return it. */
}


/*  D O E S C  --  Process an escape character argument  */
VOID
#ifdef CK_ANSIC
doesc(char c)
#else
doesc(c) char c;
#endif /* CK_ANSIC */
{
    CHAR d;
    char temp[80];

    while (1) {
        if (c == escape) {              /* Send escape character */
            d = dopar(c); ttoc(d); return;
        } else                          /* Or else look it up below. */
            if (isupper(c)) c = tolower(c);

        switch (c) {

#ifdef NETCONN
        case 'i':                       /* Send Interrupt */
        case '\011':
#ifdef TCPSOCKET
#ifndef IP
#define IP 244
#endif /* IP */
            if (telnet_cnct) { /* TELNET */
                temp[0] = (CHAR) IAC;   /* I Am a Command */
                temp[1] = (CHAR) IP;    /* Interrupt Process */
                temp[2] = NUL;          /* For ttol debug call */
                ttol((CHAR *)temp,2);
            } else 
#endif /* TCPSOCKET */
              conoc(BEL);
            return;

#ifdef TCPSOCKET
        case 'a':                       /* "Are You There?" */
        case '\01':
#ifndef AYT
#define AYT 246
#endif /* AYT */
            if (telnet_cnct) {
                temp[0] = (CHAR) IAC;   /* I Am a Command */
                temp[1] = (CHAR) AYT;   /* Are You There? */
                temp[2] = NUL;          /* For ttol debug call */
                ttol((CHAR *)temp,2);
            } else conoc(BEL);
            return;
#endif /* TCPSOCKET */
#endif /* NETCONN */

        case 'c':                       /* Close connection */
        case '\03':
            active = 0; conol("\r\012"); return;

        case 'b':                       /* Send a BREAK signal */
        case '\02':
            if (ttsndb()<0) conol("\r\nCan't send break\r\012");
            return;

        case 'u':                       /* Hangup */
        case '\010':
            dohangup = 2; active = 0; conol("\r\012"); return;

        case 'q':
            dohangup = 2; quitnow = 1; active = 0; conol("\r\012"); return;

        case '!':
#ifndef NOPUSH
	    if (!nopush) {
		conres();
		zshcmd("");
		if (conbin(escape) < 0) {
		    printf("Error returning to remote session\n");
		    active = 0;
		}
		/*if xon/xoff you should have it running on both*/
		if (flow == 1) {
		    if ((_ss_opt(0,&opt) < 0)||(_ss_opt(1,&opt) < 0)) {
			printf("Error returning to remote session\n");
			active = 0;
		    }
		}
	    } else
	      conoc(BEL);
#else
	    conoc(BEL);
#endif /* NOPUSH */
	    return;

        case 's':                       /* Status */
            sprintf(temp,
		    "\015\012Connected %s %s",
		    network ? "to" : "through", ttname);
            conol(temp);
            if (speed >= 0L) {
                sprintf(temp,", speed %ld", speed);
                conoll(temp);
            } else conoll("");
            sprintf(temp,
                    "Terminal bytesize: %d, Command bytesize: %d, Parity: ",
                    (cmask  == 0177) ? 7 : 8,
                    (cmdmsk == 0177) ? 7 : 8 );
            conol(temp);

            switch (parity) {
              case  0:  conoll("none");  break;
              case 'e': conoll("even");  break;
              case 'o': conoll("odd");   break;
              case 's': conoll("space"); break;
              case 'm': conoll("mark");  break;
            }
            sprintf(temp,"Terminal echo: %s", duplex ? "local" : "remote");
            conoll(temp);
            if (seslog) {
                conol("Logging to: "); conoll(sesfil);
            }
            if (!network) shomdm();
#ifdef OSK
            conoll("");
#endif /* OSK */
            return;

        case 'h':                       /* Help */
        case '?':                       /* Help */
            c = hconne(); continue;

		case 'x': /* send xon */
			ttoc(dopar(XON)); return;
        case '0':                       /* Send a null */
            c = '\0'; d = dopar(c); ttoc(d); return;

        case SP:                        /* Space, ignore */
            return;

        default:                        /* Other */
            if (c == CMDQ) {            /* Backslash escape */
                int x = 1;
                char *ecbp = temp;
                *ecbp++ = c;
                while (((c = (kbgetc() & cmdmsk)) != '\012') && (c != '\n')
                    && ++x < sizeof temp)
                    *ecbp++ = c;
                *ecbp = NUL; ecbp = temp;
                do {
                    x = xxesc(&ecbp);   /* Interpret it */
                    if (x >= 0) {       /* No key mapping here */
                        ttoc(dopar(x));
                    } else {            /* Invalid backslash code. */
                        conoc(BEL);
                        break;
                    }
                } while (*ecbp != NUL);
                return;
            }
            conoc(BEL); return;         /* Invalid esc arg, beep */
        }
    }
}

static
VOID
#ifdef CK_ANSIC
logchar(char c)
#else
logchar(c) char c;
#endif /* CK_ANSIC */
/* logchar */ {                 /* Log character c to session log */
    /*if (seslog) */
      if ((sessft != 0) ||
          ((c == '\012' || c == '\n') ? c_slprv != '\n' :
           c != '\0' &&
           c != XON &&
           c != XOFF))
        if (zchout(ZSFILE,c) < 0) {
            conoll("");
            conoll("ERROR WRITING SESSION LOG, LOG CLOSED!");
            seslog = 0;
        }
    c_slprv = c;
}
#endif /* !NOLOCAL */
