char *ckxv = "AMIGA tty I/O, 1.0(000), 30 Mar 1986";

/*  C K I T I O  --  Terminal and Interrupt Functions for Commodore AMIGA  */

/*
 * Copyright (C) 1986, Trustees of Columbia University in the City of
 * New York.  Permission is granted to any individual or institution to
 * use, copy, or redistribute this software, so long as it is not sold
 * for profit, provided this copyright notice is retained.
 */

/* Edit History
  27 Feb 1986  - Davide P. Cervone.  Wrote it.  This should be re-written
    to do menu and mouse stuff, but I don't have time for that just now.
    I don't have the ROM Kernal Manuals yet, so I expect that I will have
    done some things "wrong," and that there are better ways to do the
    I/O.  I will try to update this when I get the manuals.  The UnMap
    function will be used, eventually, to allow modifiable keyboard mappings
    (like MacKermit), and should allow for multi-character mappings, as
    well.  Eventually, this should include a VT100 emulator.
*/

/* C-Kermit interrupt, terminal control & i/o functions for AMIGA systems */

char *ckxsys = " Commodore AMIGA";

/*
 Variables available to outside world:

   dftty  -- Pointer to default tty name string, like "/dev/tty".
   dfloc  -- 0 if dftty is console, 1 if external line.
   dfprty -- Default parity
   dfflow -- Default flow control
   ckxech -- Flag for who echoes console typein:
     1 - The program (system echo is turned off)
     0 - The system (or front end, or terminal).
   functions that want to do their own echoing should check this flag
   before doing so.

   backgrd -- Flag indicating program executing in background ( & on
      end of shell command). Used to ignore INT and QUIT signals.

 Functions for assigned communication line (either external or console tty):

   ttopen(ttname,local,mdmtyp) -- Open the named tty for exclusive access.
   ttclos()                -- Close & reset the tty, releasing any access lock.
   ttpkt(speed,flow)       -- Put the tty in packet mode and set the speed.
   ttvt(speed,flow)        -- Put the tty in virtual terminal mode.
            or in DIALING or CONNECTED modem control state.
   ttinl(dest,max,timo)    -- Timed read line from the tty.
   ttinc(timo)             -- Timed read character from tty.
   ttchk()                 -- See how many characters in tty input buffer.
   ttxin(n,buf)            -- Read n characters from tty (untimed).
   ttol(string,length)     -- Write a string to the tty.
   ttoc(c)                 -- Write a character to the tty.
   ttflui()                -- Flush tty input buffer.
*/

/*
Functions for console terminal:

   congm()   -- Get console terminal modes.
   concb(esc) -- Put the console in single-character wakeup mode with no echo.
   conbin(esc) -- Put the console in binary (raw) mode.
   conres()  -- Restore the console to mode obtained by congm().
   conoc(c)  -- Unbuffered output, one character to console.
   conol(s)  -- Unbuffered output, null-terminated string to the console.
   conola(s) -- Unbuffered output, array of strings to the console.
   conxo(n,s) -- Unbuffered output, n characters to the console.
   conchk()  -- Check if characters available at console (bsd 4.2).
      Check if escape char (^\) typed at console (System III/V).
   coninc(timo)  -- Timed get a character from the console.
   conint()  -- Enable terminal interrupts on the console if not background.
   connoi()  -- Disable terminal interrupts on the console if not background.
   contti()  -- Get a character from either console or tty, whichever is first.

Time functions

   msleep(m) -- Millisecond sleep
   ztime(&s) -- Return pointer to date/time string
*/

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <exec/tasks.h>
#include <exec/io.h>
#include <exec/devices.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfxbase.h>
#include <graphics/regions.h>
#include <graphics/copper.h>
#include <graphics/gels.h>
#include <devices/keymap.h>
#include <devices/timer.h>
#include <devices/serial.h>
#include <hardware/blit.h>
#include <libraries/dos.h>
#include <stdio.h>
#include <ctype.h>

#define INTUITION_REV 1
#define GRAPHICS_REV 1

#define SCREENCLR 12    /* character that does clear screen */
#define ERROR_EXIT 10   /* status for exit with error */

#define NODEF       -1            /* undefined key */
#define NRMLKEYS     0            /* normal keys */
#define CTRLKEYS     1            /* control keys */
#define UNSHIFT      0            /* unshifted keyboard */
#define SHIFTED      1            /* shifted keyboard */
#define METABIT      0x80         /* 8th bit meta prefixing */

#define Puts(s)         fputs(stdout,s)
#define Printf(s)       fprintf(stdout,s)
#define Printf2(s,x)    fprintf(stdout,s,x)

char templine[500];

#define LONG_DEF
#include "ckcker.h"
#include "ckcdeb.h"         /* Formats for debug() */


/* Declarations */

/* dftty is the device name of the default device for file transfer */
/* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */

    char *dftty = "SERIAL";
    int dfloc = 1;         /* Default location is local */
    int dfprty = 0;         /* Parity (0 = none) */
    int dfflow = 1;         /* Xon/Xoff flow control */
    int batch = 0;         /* Assume interactive */

int ckxech = 0; /* 0 if system normally echoes console characters, else 1 */


/* Declarations of variables global within this module */

static int conif = 0,         /* Console interrupts on/off flag */
    cgmf = 0,                 /* Flag that console modes saved */
    ttychn = 0,               /* TTY i/o channe; */
    conch = NODEF;            /* console input character buffer  */

static char escchr;           /* Escape or attn character */

#define LEFTEDGE     4
#define TOPEDGE     17

int Xpos=3, Ypos=17;  /* current cursor position */
int Xmax, Ymax;       /* current max X and Y values */
int CursorON = -1;    /* cursor on or off */

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

struct timerequest TimerIO;
struct MsgPort TimerMsgPort;

extern struct MsgPort *CreatePort();

struct IOExtSer *ReadRequest;
static char ReadPort[2];
struct IOExtSer *WriteRequest;
static char WritePort[2];

int OpenedRead = 0;
int OpenedWrite = 0;
int OpenedIntui = 0;
int OpenedGfx = 0;
int OpenedWindow = 0;
int OpenedTimer = 0;

struct NewWindow NewWindow = {            /* window definition */
   0,
   0,
   640,
   200,
   0,
   1,
   CLOSEWINDOW | NEWSIZE | RAWKEY,
   WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWDRAG |
   WINDOWDEPTH | WINDOWSIZING,
   NULL,
   NULL,
   "Amiga Kermit",
   NULL,
   NULL,
   100, 35,
   640, 200,
   WBENCHSCREEN
};

struct Window *myWindow;              /* new window */


OpenTimer()
{
   if (OpenDevice(TIMERNAME, UNIT_VBLANK, &TimerIO, 0) != 0)
   {
      Printf("Can't open Timer Device\n");
      doexit(ERROR_EXIT);
   } else {
      OpenedTimer = 1;
   }
   TimerMsgPort.mp_Node.ln_Type = NT_MSGPORT;
   TimerMsgPort.mp_Node.ln_Name = "vt-timer";
   TimerMsgPort.mp_Flags = 0;
   TimerMsgPort.mp_SigBit = AllocSignal(-1);
   TimerMsgPort.mp_SigTask = (struct Task *)FindTask((char *)NULL);
   AddPort(&TimerMsgPort);
   TimerIO.tr_node.io_Message.mn_ReplyPort = &TimerMsgPort;
}

StartTimer(seconds, micros)
int seconds, micros;
{
   TimerIO.tr_node.io_Command = TR_ADDREQUEST;
   TimerIO.tr_node.io_Flags = IOF_QUICK;
   TimerIO.tr_time.tv_secs = (ULONG) seconds;
   TimerIO.tr_time.tv_micro = (ULONG) micros;
   SendIO(&TimerIO);
}

OpenSerial()    /* Open the serial device */
{
   ReadRequest = (struct IOExtSer *) AllocMem(sizeof(*ReadRequest),
                                              MEMF_PUBLIC | MEMF_CLEAR);
   ReadRequest->IOSer.io_Message.mn_ReplyPort = CreatePort("Read_RS",0);
   ReadRequest->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
   OpenedRead = -1;
   if (OpenDevice(SERIALNAME, NULL, ReadRequest, NULL) != 0)
   {
      Printf("Can't open Serial Device for Reading\n");
      doexit(ERROR_EXIT);
   } else {
      OpenedRead = 1;
   }

   WriteRequest = (struct IOExtSer *) AllocMem(sizeof(*WriteRequest),
                                               MEMF_PUBLIC | MEMF_CLEAR);
   WriteRequest->IOSer.io_Message.mn_ReplyPort = CreatePort("Write_RS",0);
   WriteRequest->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
   OpenedWrite = -1;
   if (OpenDevice(SERIALNAME, NULL, WriteRequest, NULL) != 0)
   {
      Printf("Can't open Serial Device for Writing\n");
      doexit(ERROR_EXIT);
   } else {
      OpenedWrite = 1;
   }

   ReadRequest->IOSer.io_Command = SDCMD_SETPARAMS;
   ReadRequest->IOSer.io_Length = 1;
   ReadRequest->IOSer.io_Data = (APTR) &ReadPort[0];
   ReadRequest->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
   ReadRequest->io_Baud = 9600;
   ReadRequest->io_ReadLen = 8;
   ReadRequest->io_WriteLen = 8;

   WriteRequest->IOSer.io_Command = CMD_WRITE;
   WriteRequest->IOSer.io_Length = 1;
   WriteRequest->IOSer.io_Data = (APTR) &WritePort[0];
   WriteRequest->io_SerFlags = SERF_SHARED | SERF_XDISABLED;

   DoIO(ReadRequest);
   ReadRequest->IOSer.io_Command = CMD_READ;
   BeginIO(ReadRequest);
}

OpenLibraries()
{
   if ((IntuitionBase = (struct IntuitionBase *)
       OpenLibrary("intuition.library", INTUITION_REV)) == NULL)
   {
      Printf("Can't open Intuition Library\n");
      doexit(ERROR_EXIT);
   } else {
      OpenedIntui = 1;
   }

   if ((GfxBase = (struct GfxBase *)
       OpenLibrary("graphics.library", GRAPHICS_REV)) == NULL)
   {
      Printf("Can't open Graphics Library\n");
      doexit(ERROR_EXIT);
   } else {
      OpenedGfx = 1;
   }
}

OpenMyWindow()
{
   if ((myWindow = (struct Window *)OpenWindow(&NewWindow)) == NULL)
   {
      Printf("Can't open my window\n");
      doexit(ERROR_EXIT);
   } else {
      OpenedWindow = 1;
   }
}

OpenAll()
{
   OpenLibraries();
   OpenTimer();
   OpenSerial();
   OpenMyWindow();
}

/*  S Y S I N I T  --  System-dependent program initialization.  */

sysinit() {

   OpenAll();
   SetAPen(myWindow->RPort,1);
   SetDrMd(myWindow->RPort,COMPLEMENT);
   Xmax = myWindow->Width;
   Ymax = myWindow->Height;

   putchar(SCREENCLR);
   return(0);
}


CloseLibraries()
{
   if (OpenedIntui) CloseLibrary(IntuitionBase);
   if (OpenedGfx)   CloseLibrary(GfxBase);
   OpenedIntui = 0;
   OpenedGfx = 0;
}

CloseSerial()
{
   if (OpenedRead)
   {
      if (OpenedRead > 0) CloseDevice(ReadRequest);
      DeletePort(ReadRequest->IOSer.io_Message.mn_ReplyPort);
      FreeMem(ReadRequest,sizeof(*ReadRequest));
      OpenedRead = 0;
   }

   if (OpenedWrite)
   {
      if (OpenedWrite > 0) CloseDevice(WriteRequest);
      DeletePort(WriteRequest->IOSer.io_Message.mn_ReplyPort);
      FreeMem(WriteRequest,sizeof(*WriteRequest));
      OpenedWrite = 0;
   }
}

CloseTimer()
{
   if (OpenedTimer)
   {
      CloseDevice(&TimerIO);
      RemPort(&TimerMsgPort);
      OpenedTimer = 0;
   }
}

CloseMyWindow()
{
   if (OpenedWindow)
   {
      CloseWindow(myWindow);
      OpenedWindow = 0;
   }
}


/*  S Y S C L N U P  --  Clean up for final exeit from program */

sysclnup()
{
   CloseSerial();
   CloseTimer();
   CloseMyWindow();
   CloseLibraries();
}



/*  T T O P E N  --  Open a tty for exclusive access.  */

/*  Returns 0 on success, -1 on failure.  */

ttopen(ttname,lcl,modem) char *ttname; int *lcl, modem; {

   if (strcmp(ttname,"SERIAL")!=0) return(-1);
   ttychn = 1;
   return(0);
}



/*  T T C L O S  --  Close the TTY, releasing any lock.  */

ttclos() {
    if (ttychn == 0) return(0);      /* Wasn't open. */
    ttychn = 0;            /* Mark it as closed. */
    return(0);
}


/*  T T R E S  --  Restore terminal to "normal" mode.  */

ttres() {            /* Restore the tty to normal. */
    if (ttychn == 0) return(-1);      /* Not open. */
    debug(F101,"ttres, ttychn","",ttychn);
    return(0);
}



/*  T T P K T  --  Condition the communication line for packets. */
/*      or for modem dialing */

#define DIALING   4      /* flags (via flow) for modem handling */
#define CONNECT   5

/*  If called with speed > -1, also set the speed.  */

/*  Returns 0 on success, -1 on failure.  */

ttpkt(speed,flow) int speed, flow; {
    extern char ttname[];
    int s;
    if (ttychn == 0) return(-1);      /* Not open. */
    if ((s=ttsspd(speed)) <= 0)         /* Check the speed */
       return(-1);
    AbortIO(ReadRequest);
    ReadRequest->io_Baud = s;
    ReadRequest->IOSer.io_Command = SDCMD_SETPARAMS;
    DoIO(ReadRequest);
    ReadRequest->IOSer.io_Command = CMD_READ;
    BeginIO(ReadRequest);
    return(0);
}



/*  T T V T -- Condition communication line for use as virtual terminal  */

ttvt(speed,flow) int speed, flow; {
    ttpkt(speed,flow);
    return(0);
}



/*  T T S S P D  --  Return the internal baud rate code for 'speed'.  */

int speeds[] = {
   300, 1200, 2400, 4800, 9600, 19200, 0 } ;
/* check that all of these are legal */

int
ttsspd(speed) int speed; {
    int s;

    if (speed < 0)         /* Unknown speed fails   */
       return (0);
    for (s = 0;  speeds[s] != 0 && speeds[s] != speed;  s++) ;
    if (speeds[s] != 0)
       return(speeds[s]);
    else {
       printf2("Unsupported line speed - %d\n",speed);
       printf("Current speed not changed\n");
       return(-1);
    }
}



/*  T T F L U I  --  Flush tty input buffer */


ttflui()
{
   AbortIO(ReadRequest);
   BeginIO(ReadRequest);
   return(0);
}


/* Interrupt Functions */


/*  C O N I N T  --  Console Interrupt setter  */

conint(f) int (*f)(); {

    if (conif) return(0);         /* Nothing to do if already on. */
    conif = 1;            /* Flag console interrupts on. */
    return(0);
}


/*  C O N N O I  --  Reset console terminal interrupts */

connoi() {            /* Console-no-interrupts */

    conif = 0;
    return(0);
}



/*  T T C H K  --  Tell how many characters are waiting in tty input buffer  */


ttchk()
{
   if (CheckIO(ReadRequest)) return(1); else return(0);
}


/*  T T X I N  --  Get n characters from tty input buffer  */


ttxin(n, buff)
int n;
char *buff;
{
   int i;

   for (i=n; i > 0; i--) *buff++ = ttinc(0);
   *buff = '\0';
   return(n);
}


/*  T T O L  --  Similar to "ttinl", but for writing.  */

ttol(s,len)
char s[];
int len;
{
   int i;

   for (i=0; i<len; i++) ttoc(s[i]);
   return(0);
}


/*  T T O C  --  Output a character to the communication line  */

ttoc(c)
int c;
{
   WritePort[0] = c;
   DoIO(WriteRequest);
   return(0);
}



/*  T T I N L  --  Read a record (up to break character) from comm line.  */
/*
  If no break character encountered within "cmax", return "cmax" characters,
  with disposition of any remaining characters undefined.  Otherwise, return
  the characters that were read, including the break character, in "dest" and
  the number of characters read as the value of function, or 0 upon end of
  file, or -1 if an error occurred.  Times out & returns error if not completed
  within "timo" seconds.
*/

ttinl(dest,cmax,timo,eol)
int cmax, timo;
char dest[];
char eol;
{
   int time = 0;
   int i, c;

   if (timo>0)
   {
      StartTimer(timo,0);
      time = -1;  /* timed, but timer already set */
   } else {
      time = 0;
   }

   i = 0;
   c = NODEF;
   while (i<cmax && c!=eol)
   {
      if ((c = ttinc(time)) == NODEF) return(-1);
      dest[i++] = c;
   }
   if (timo>0) AbortIO(&TimerIO);
   if (i<cmax) dest[i] = '\0';
   return(i-1);
}



/*  T T I N C --  Read a character from the communication line  */

ttinc(timo)
int timo;
{
   int class,code,qualifier;
   struct IntuiMessage *NewMessage;
   int NoChar = -1;
   int c;

   if (timo>0) StartTimer(timo,0);

   while (NoChar)
   {
      if (CheckIO(ReadRequest))
      {
         WaitIO(ReadRequest);
         c = ReadPort[0] & 0x7F;
         BeginIO(ReadRequest);
         NoChar = 0;
      } else {
         if (timo)
         {
            Wait ((1 << myWindow->UserPort->mp_SigBit) |
                  (1 << TimerMsgPort.mp_SigBit) |
                  (1 << ReadRequest->IOSer.io_Message.mn_ReplyPort->mp_SigBit));
            if (CheckIO(&TimerIO)) return(-1);
         } else {
            Wait ((1 << myWindow->UserPort->mp_SigBit) |
                  (1 << ReadRequest->IOSer.io_Message.mn_ReplyPort->mp_SigBit));
         }
         while (NewMessage = (struct IntuiMessage *)GetMsg(myWindow->UserPort))
         {
            class = NewMessage->Class;
            code = NewMessage->Code;
            qualifier = NewMessage->Qualifier;
            ReplyMsg(NewMessage);
            switch (class)
            {
               case CLOSEWINDOW:
                  doexit(0);
                  break;

               case NEWSIZE:
                  Xmax = myWindow->Width;
                  Ymax = myWindow->Height;
                  conoc(SCREENCLR);
                  break;

               case RAWKEY:
                  if (UnMap(&c,code,qualifier)==0) conch = c;
                  break;

            }
         }
      }
   }
   if (timo>0) AbortIO(&TimerIO);
   return(c);
}


/*  T T S N D B  --  Send a BREAK signal  */

ttsndb() {

    if (ttychn == 0) return(-1);      /* Not open. */

/* send a break */
    return(0);
}



/*  T T H A N G  --  Hang up the communications line  */

tthang() {
    if (ttychn == 0) return(-1);      /* Not open. */

/* hang up */
    return(0);
}



/*  M S L E E P  --  Millisecond version of sleep().  */

msleep(m) int m; {

    StartTimer(0,m);
    return(0);
}

/*  S L E E P  --  Pause for a few seconds */

sleep(time)
int time;
{
/*
   StartTimer(time,0);
   Wait (1 << TimerMsgPort.mp_SigBit);
*/
}


/*  Z T I M E  --  Return date/time string  */

ztime(s) char **s; {
    *s = "Unknown date/time";
}



/*  C O N G M  --  Get console terminal modes.  */

/*
 Saves current console mode, and establishes variables for switching between
 current (presumably normal) mode and other modes.
*/

congm() {
    cgmf = 1;            /* Flag that we got them. */
    return(0);
}


/*  C O N C B --  Put console in cbreak mode.  */

/*  Returns 0 if ok, -1 if not  */

concb(esc) char esc; {
    if (cgmf == 0) congm();      /* Get modes if necessary. */
    escchr = esc;         /* Make this available to other fns */
    ckxech = 1;            /* Program can echo characters */
    return(0);
}



/*  C O N B I N  --  Put console in binary mode  */


/*  Returns 0 if ok, -1 if not  */

conbin(esc) char esc; {
    if (cgmf == 0) congm();      /* Get modes if necessary. */
    escchr = esc;         /* Make this available to other fns */
    ckxech = 1;            /* Program can echo characters */
    return(0);
}


/*  C O N R E S  --  Restore the console terminal  */

conres() {
    if (cgmf == 0) return(0);      /* Don't do anything if modes */
    ckxech = 0;            /* System should echo chars */
    return(0);
}



/*  C O N O C  --  Output a character to the console terminal  */


conoc(c)
int c;
{
   int CursorChanged;

   CursorChanged = 0;
   if (CursorON) {
      XOR_Cursor();
      CursorChanged = 1;
   }

   SetDrMd(myWindow->RPort,JAM2);
   switch(c)
   {
      case 0:
         break;
      case '\t':
         Xpos += 64;
         break;
      case '\n':
         Line_Feed();
         break;
      case '\r':
         Xpos = LEFTEDGE;
         break;
      case '\b':
         Xpos = max(Xpos-8,LEFTEDGE);
         break;
      case SCREENCLR:     /* page */
         Xpos = LEFTEDGE;
         Ypos = TOPEDGE;
         SetAPen(myWindow->RPort,0);
         RectFill(myWindow->RPort,2,10,Xmax-19,Ymax-2);
         SetAPen(myWindow->RPort,1);
         break;
      case 7:     /* bell */
         ClipBlit(myWindow->RPort,0,0,myWindow->RPort,0,0,Xmax,Ymax,0x50);
         ClipBlit(myWindow->RPort,0,0,myWindow->RPort,0,0,Xmax,Ymax,0x50);
         break;
      default:
         if (c>31)        /* only print non-control characters */
         {
            char c1;      /* temporary storage for character */

            if (Xpos > Xmax-31)
            {
               Xpos = LEFTEDGE;
               Line_Feed();
            }
            c1 = c;
            Move(myWindow->RPort,Xpos,Ypos);
            Text(myWindow->RPort,&c1,1);
            Xpos += 8;
         }
   } /* end of switch */
   SetDrMd(myWindow->RPort,COMPLEMENT);

   if (CursorChanged) XOR_Cursor();
}

Line_Feed()
{
   if (Ypos > Ymax-12)
      ScrollRaster(myWindow->RPort,0,8,2,11,Xmax-20,Ymax-2);
    else
      Ypos += 8;
}

XOR_Cursor()
{
   int cx, cy;

   CursorON = !CursorON;

   cy = Ypos;
   cx = Xpos+8;
   while (cx > (Xmax-23)) cx -= 8;

   SetAPen(myWindow->RPort,3);
   RectFill(myWindow->RPort,cx-8,cy-7,cx,cy+1);
   SetAPen(myWindow->RPort,1);
}

conocNL(c)            /*   really a putc command */
int c;
{
   if (c == '\n') conoc('\r');
   conoc(c);
}


/*  C O N X O  --  Write x characters to the console terminal  */

conxo(x,s) char *s; int x;
{
   XOR_Cursor();
   while (x-- > 0)
   {
      if (*s == '\n') conoc('\r');
      conoc(*s++);
   }
   XOR_Cursor();
}

/*  C O N O L  --  Write a line to the console terminal  */

conol(s)
char *s;
{
   XOR_Cursor();
   while (*s != '\0')
   {
      if (*s == '\n') conoc('\r');
      conoc(*s++);
   }
   XOR_Cursor();
}

conolNL(s) char *s;      /* really a puts */
{
   conol(s); conoc('\n'); conoc('\r');
}


/*  C O N O L A  --  Write an array of lines to the console terminal */

conola(s) char *s[]; {
    int i;
    for (i=0 ; *s[i] ; i++) conol(s[i]);
}

/*  C O N O L L  --  Output a string followed by CRLF  */

conoll(s) char *s; {
    conol(s);
    conol("\r\n");
}


/*  C O N C H K  --  Check if characters available at console  */

conchk() {
   return((conch == NODEF)? 0 : 1);
}



/*  C O N I N C  --  Get a character from the console  */


coninc(timo)
int timo;
{
   int class,code,qualifier;
   struct IntuiMessage *NewMessage;
   int NoChar = -1;
   int c;

/* Get buffered character, if any */
   if (conch != NODEF)
   {
      c = conch;
      conch = NODEF;
      return(c);
   }

   if (timo>0) StartTimer(timo,0);

   while (NoChar)
   {
      if (timo)
      {
         Wait ((1 << myWindow->UserPort->mp_SigBit) |
               (1 << TimerMsgPort.mp_SigBit));
         if (CheckIO(&TimerIO)) return(-1);
      } else {
         Wait(1 << myWindow->UserPort->mp_SigBit);
      }
      while (NewMessage = (struct IntuiMessage *)GetMsg(myWindow->UserPort))
      {
         class = NewMessage->Class;
         code = NewMessage->Code;
         qualifier = NewMessage->Qualifier;
         ReplyMsg(NewMessage);
         switch (class)
         {
            case CLOSEWINDOW:
               doexit(0);
               break;

            case NEWSIZE:
               Xmax = myWindow->Width;
               Ymax = myWindow->Height;
               conoc(SCREENCLR);
               cmini();
               prompt();
               break;

            case RAWKEY:
               NoChar = UnMap(&c,code,qualifier);
               if (c == '\r') c = '\n';
               if (c == 127)  c = '\b';
               break;

         }
      }
   }
   if (timo>0) AbortIO(&TimerIO);
   return(c);
}

UnMap(c,code,qualifier)
int *c;
int code, qualifier;
{
   int CapsLock, ShiftType, MapType, MetaChar;

   static char KeyCode [] [2] [96] =
   {
      {      /* normal keys */
         {      /* unshifted */
'`','1','2','3','4','5','6','7','8','9','0','-','=','\\',NODEF,'0',
 'q','w','e','r','t','y','u','i','o','p','[',']',       NODEF,'1','2','3',
  'a','s','d','f','g','h','j','k','l',';','\'',  NODEF, NODEF,'4','5','6',NODEF,
   'z','x','c','v','b','n','m',',','.','/',      NODEF,  '.', '7','8','9',

/*   SP  BS  TAB  RETURN ENTER ESC DEL  */
     ' ', 8, '\t', '\r', '\r', 27, 127, NODEF, NODEF, NODEF, '-', NODEF,
/*   UP     DOWN   LEFT,  RIGHT, */
     NODEF, NODEF, NODEF, NODEF,
/*  Function keys 1 to 10 */
     NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF,
/*                                      help */
     NODEF, NODEF, NODEF, NODEF, NODEF, '?'
         },
         {      /* shifted */
'~','!','@','#','$','%','^','&','*','(',')','_','+','|',NODEF,'0',
 'Q','W','E','R','T','Y','U','I','O','P','{','}',       NODEF,'1','2','3',
  'A','S','D','F','G','H','J','K','L',':','"',   NODEF, NODEF,'4','5','6',NODEF,
   'Z','X','C','V','B','N','M','<','>','?',      NODEF,   '.','7','8','9',

/*   SP  BS  TAB  RETURN ENTER ESC DEL  */
     ' ', 8, '\t', '\r', '\r', 27, 127, NODEF, NODEF, NODEF, '-', NODEF,
/*   UP     DOWN   LEFT,  RIGHT, */
     NODEF, NODEF, NODEF, NODEF,
/*  Function keys 1 to 10 */
     NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF,
/*                                      help */
     NODEF, NODEF, NODEF, NODEF, NODEF, '?'
         }
      },

      {      /*  Control Keys */
         {      /* unshifted */
'`','1', 0 ,'3','4','5', 30,'7','8','9','0', 31,'=', 28,NODEF,'0',
  17, 23, 5 , 18, 20, 25, 21, 9 , 15, 16, 27, 29,       NODEF,'1','2','3',
   1 , 19, 4 , 6 , 7 , 8 , 10, 11, 12,';','\'',   NODEF,NODEF,'4','5','6',NODEF,
    26, 24, 3 , 22, 2 , 14, 13,',','.','/',       NODEF,  '.','7','8','9',

/*   SP  BS  TAB  RETURN ENTER ESC DEL  */
     ' ', 8, '\t', '\r', '\r', 27, 127, NODEF, NODEF, NODEF, '-', NODEF,
/*   UP     DOWN   LEFT,  RIGHT, */
     NODEF, NODEF, NODEF, NODEF,
/*  Function keys 1 to 10 */
     NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF,
/*                                      help */
     NODEF, NODEF, NODEF, NODEF, NODEF, '?'
         },
         {      /* shifted */
'~','!', 0 ,'#','$','%', 30,'&','*','(',')', 31,'+', 28,NODEF,'0',
  17, 23, 5 , 18, 20, 25, 21, 9 , 15, 16, 27, 29,       NODEF,'1','2','3',
   1 , 19, 4 , 6 , 7 , 8 , 10, 11, 12,':','"',    NODEF,NODEF,'4','5','6',NODEF,
    26, 24, 3 , 22, 2 , 14, 13,'<','>','?',       NODEF,  '.','7','8','9',

/*   SP  BS  TAB  RETURN ENTER ESC DEL  */
     ' ', 8, '\t', '\r', '\r', 27, 127, NODEF, NODEF, NODEF, '-', NODEF,
/*   UP     DOWN   LEFT,  RIGHT, */
     NODEF, NODEF, NODEF, NODEF,
/*  Function keys 1 to 10 */
     NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF, NODEF,
/*                                      help */
     NODEF, NODEF, NODEF, NODEF, NODEF, '?'
         }
      }
   };


   if (code > 95)
   {
      *c = NODEF;
      return(-1);
   } else {
      ShiftType = (qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) ?
                                     SHIFTED : UNSHIFT;
      CapsLock  = (qualifier & IEQUALIFIER_CAPSLOCK) ? 1 : 0;
      MapType   = (qualifier & IEQUALIFIER_CONTROL)  ? CTRLKEYS : NRMLKEYS;
      MetaChar  = (qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT)) ?
                                     METABIT : 0;
      if ((*c = KeyCode [MapType] [ShiftType] [code]) == NODEF) {
         return(-1);
      } else {
         if (CapsLock && *c >= 'a' && *c <= 'z') *c -= 32;
         *c |= MetaChar;
         return(0);
      }
   }
}


/*  C O N T T I  --  Get character from console or tty, whichever comes  */
/*   first.  This is used in conect() when NO_FORK is defined.  */
/*   src is returned with 1 if the character came from the comm. line, */
/*   0 if it was from the console, and with -1 if there was any error.  */


contti(c,src)
int *c;
int *src;
{
   int class,code,qualifier;
   struct IntuiMessage *NewMessage;
   int NoChar = -1;

   while (NoChar)
   {
      if (CheckIO(ReadRequest))
      {
         *src = 1;
         WaitIO(ReadRequest);
         *c = ReadPort[0] & 0x7F;
         BeginIO(ReadRequest);
         NoChar = 0;
      } else {
         Wait ((1 << myWindow->UserPort->mp_SigBit) |
               (1 << ReadRequest->IOSer.io_Message.mn_ReplyPort->mp_SigBit));
         while (NewMessage = (struct IntuiMessage *)GetMsg(myWindow->UserPort))
         {
            class = NewMessage->Class;
            code = NewMessage->Code;
            qualifier = NewMessage->Qualifier;
            ReplyMsg(NewMessage);
            switch (class)
            {
               case CLOSEWINDOW:
                  doexit(0);
                  break;

               case NEWSIZE:
                  Xmax = myWindow->Width;
                  Ymax = myWindow->Height;
                  conoc(SCREENCLR);
                  break;

               case RAWKEY:
                  NoChar = UnMap(c,code,qualifier);
                  *src = 0;
                  break;

            }
         }
      }
   }
   return(0);
}


/*  C A N C I O  --  Cancel any pending i/o requests on the console or the
        comm. line.
*/

cancio()  {
   ttflui();
}
