/* Terminal.c

   Autor: E.G. Meyzis, Mai 1992

   Compile Instr. fuer DICE:                   execute cterm
   dcc Terminal.c -c -o t:Terminal.o

   Link Instr. fuer DICE:                      execute lterm
   dlink dlib:c.o t:Terminal.o dlib:c.lib+
   dlib:amigas20.lib dlib:auto.lib dlib:x.o -o Terminal
*/

#include <Devices/Serial.h>
#include <Dos/Dos.h>
#include <Dos/DosExtens.h>
#include <Exec/Exec.h>
#include <Intuition/Intuition.h>
#include <stdlib.h>
#include <stdio.h>

/* +++++++++  Definitionen  ++++++++++++
*/
#define TRUE  1   /* um mit dem Datentyp BOOL zu arbeiten */
#define FALSE 0

      /* zwei Definitionen fuer DOS-Fenster */

#define TxTitle "CON:0/0/500/100/ Terminal-Send "
#define RxTitle "CON:0/101/500/100/ Terminal-Receive "

      /* Texte zur Info bei Text-Senden/Empfangen */

#define Senden   "\nSende Textdatei\n\n"
#define Empfang  "\nwarte auf Textdatei\n\n"
#define Beendet  "\nÜbertragung beendet\n\n"

#define Unit0     0      /* eingebaute ser. Schnittstelle */
#define PLaenge 512      /* Pufferlaenge Datemuebertrag.  */

struct GetBlock             /* nur in GetWinPtr verwendet */
         {
          struct StandardPacket stdPkt;
          struct InfoData       info;
         };

/* +++++++++  Datentypen  ++++++++++++
*/
typedef char                BOOL;
typedef char                *CharPtr;
typedef struct IOExtSer     *IOExtSerPtr;
typedef struct FileHandle   *FileHandleBPtr;      /* BPTR */
typedef struct FileHandle   *FileHandlePtr;    /* Adresse */
typedef struct Window       *WindowPtr;
typedef struct GetBlock     *GetBlockPtr;
typedef struct MessagePort  *MsgPortPtr;
typedef struct IntuiMessage *IntuiMessagePtr;


APTR GetSysBase();    /* Adr ExecBase aus Adresse 4 holen */

void GetWinPointer (FileHandleBPtr *winHdl,
                    WindowPtr      *winPtr);
void MakeWindow(CharPtr        namePtr,
                WindowPtr      *winPtr,
                FileHandleBPtr *fileHdl);
BOOL WindowsAngelegt();

void SetBaudRate(IOExtSerPtr serPtr);
BOOL InitSerialDev(IOExtSerPtr serPtr, int cmnd);
BOOL SerialVorbereitet();
void SetTermChars(IOExtSerPtr serPtr);
void FlushSerialDevice();

void SendeTextDatei(IOExtSerPtr txReqPtr);
void EmpfangeTextDatei(IOExtSerPtr rxReqPtr);

void TerminalLoop();
void Aufraeumen();

/* +++++++++  globale Variablen  ++++++++++++
*/
struct ExecBase *execBasePtr;

/*
               Doppelt ausgelegte Datenstrukturen

                Sendeteil:         Empfangsteil:
*/
                /* Device zweimal oeffnen */
struct IOExtSer txRequest,         rxRequest;

                /* jedem Device seinen Puffer */
char            rxPuffer[PLaenge], txPuffer[PLaenge];

                /* getrennte Fenster fuer Ein-/Ausgaben */
FileHandleBPtr  txWin,             rxWin;
WindowPtr       txWinPtr,          rxWinPtr;



APTR GetSysBase() /* Adr ExecBase aus Adresse 4 holen */
{
 long *systemBasis;

 systemBasis = (APTR)4;
 return (APTR)*systemBasis;
}


/* Mit Dos.Open geoeffnete Fenster liefern nur einen Handle
   und leider nicht den WindowPtr; deshalb:
*/
void GetWinPointer (FileHandleBPtr *winHdl,
                    WindowPtr      *winPtr)
{
 MsgPortPtr  portPtr;
 GetBlockPtr pktPtr;

 FileHandlePtr wHdlPtr;

 *winPtr = NULL;                   /* negatives Vorurteil */
 if (IsInteractive(*winHdl))        /* Fenster vorhanden? */
   {
    wHdlPtr = (APTR)((long)*winHdl) << 2;  /* BPTR -> Adr */
    if (portPtr = (MsgPortPtr)CreateMsgPort())
      {
       /* Speicher fuer Dos.Packet anfordern*/

       pktPtr=(GetBlockPtr)AllocMem(sizeof(struct GetBlock),
                                    MEMF_CLEAR|MEMF_PUBLIC);
       if (pktPtr)           /* Dos.Packet initialisieren */
         {
          pktPtr->stdPkt.sp_Pkt.dp_Link =
                                     &pktPtr->stdPkt.sp_Msg;
          pktPtr->stdPkt.sp_Pkt.dp_Port = portPtr;
          pktPtr->stdPkt.sp_Pkt.dp_Type = ACTION_DISK_INFO;
          pktPtr->stdPkt.sp_Pkt.dp_Arg1 = &pktPtr->info >>2;
          pktPtr->stdPkt.sp_Msg.mn_Node.ln_Name =
                                     &pktPtr->stdPkt.sp_Pkt;
          pktPtr->stdPkt.sp_Msg.mn_Node.ln_Type= NT_MESSAGE;
          pktPtr->stdPkt.sp_Msg.mn_Length =
                                     sizeof(struct Message);

          PutMsg(wHdlPtr->fh_Type, pktPtr);/*und absenden */
          do
            WaitPort(portPtr);     /* auf Antwort von Dos */
          while (APTR)GetMsg(portPtr) != pktPtr;

          if (pktPtr->stdPkt.sp_Pkt.dp_Res1)/* Antwort ok */
             *winPtr = (APTR)pktPtr->info.id_VolumeNode;

           DeleteMsgPort(portPtr);
           FreeMem(pktPtr, sizeof(struct GetBlock));
         }
       else
         DeleteMsgPort(portPtr);
      }

   } /* Interactive */
}


/* Dos.Fenster anlegen und WindowPtr holen */

void MakeWindow(CharPtr        namePtr,
                WindowPtr      *winPtr,
                FileHandleBPtr *fileHdl)

{
  *fileHdl = (FileHandleBPtr)Open(namePtr, MODE_NEWFILE);
  if (*fileHdl)
    GetWinPointer(&(*fileHdl), &(*winPtr));
}


/* Beide Terminal-Fenster anlegen und bei Fehlschlag
   Ressourcen freigeben.
*/
BOOL WindowsAngelegt()
{
  BOOL angelegt;

  angelegt = FALSE;         /* zunaechst keines vorhanden */

  MakeWindow(TxTitle, &txWinPtr, &txWin);/* Sende-Fenster */
  if (txWinPtr)
    {
     angelegt = TRUE;

     /* um Tastatureingaben durch Intuition abzufangen */

     ModifyIDCMP(txWinPtr, IDCMP_RAWKEY|IDCMP_VANILLAKEY);

     MakeWindow(RxTitle, &rxWinPtr, &rxWin);/*Empf.-Fenst */
     if (rxWinPtr = NULL)
       {
        angelegt = FALSE;        /* Sendefenster aufgeben */
        Close(txWin);
        txWin = NULL;
       }
    }
  else                    /* WindowPtr nicht zu ermitteln */
    if (txWin)
       Close(txWin);                /* deshalb schliessen */
  return angelegt;
}

void SetBaudRate(IOExtSerPtr serPtr)
{
  serPtr->io_Baud = 9600;
  serPtr->IOSer.io_Command = SDCMD_SETPARAMS;
  DoIO(serPtr);
              /* io_Command nach dem Aufruf zuruecksetzen */
}

void SetTermChars(IOExtSerPtr serPtr)
{
  serPtr->IOSer.io_Command = SDCMD_SETPARAMS;
  serPtr->io_SerFlags |= SERF_EOFMODE;
  serPtr->io_TermArray.TermArray0 = 0x1C040303;
                                   /* EOF-EOT-ETX-ETX */
  serPtr->io_TermArray.TermArray1 = 0x03030303;
                                   /* RTX-... */
  DoIO(serPtr);
              /* io_Command nach dem Aufruf zuruecksetzen */
}


/* Device-Struktur initialisieren und Device oeffnen */

BOOL InitSerialDev(IOExtSerPtr serPtr, int cmnd)
{
  MsgPortPtr  portPtr;
  BOOL        erfolg;

  erfolg = FALSE;
  portPtr = (MsgPortPtr)CreateMsgPort();   /* fuer Device */
  if (portPtr)
    {
     serPtr->io_SerFlags = SERF_SHARED; /* Mehrfachutzung */
     serPtr->IOSer.io_Length = 1;/* 1 Byte lesen/schreib. */
     serPtr->IOSer.io_Message.mn_ReplyPort = portPtr;

     OpenDevice(SERIALNAME, Unit0, serPtr, 0);
     if (serPtr->IOSer.io_Error != IOERR_OPENFAIL)
       {
        erfolg = TRUE;
        SetBaudRate(serPtr);
        serPtr->IOSer.io_Command = cmnd;/* CMD_WRITE/READ */
       }
      else                     /* Device nicht zu oeffnen */
       {
        DeleteMsgPort
                    (serPtr->IOSer.io_Message.mn_ReplyPort);
        serPtr->IOSer.io_Message.mn_ReplyPort = NULL;
       }
    }
  return erfolg;
}


BOOL SerialVorbereitet() /* getrennte Devices als  Sende- */
{                        /* Empfangskanal oeffnen */
  BOOL vorbereitet;

  if (InitSerialDev(&txRequest, CMD_WRITE)) /* Sendekanal */
    {
     vorbereitet = TRUE;
     txRequest.IOSer.io_Data=txPuffer;/*f. Dateiuebertrag.*/

     if (InitSerialDev(&rxRequest, CMD_READ))/*Empf.-Kanal*/
       {
        rxRequest.IOSer.io_Data = rxPuffer; /* ext. Puffer*/
       }
     else                /* kein Empfangskanal zu oeffnen */
       {
        vorbereitet = FALSE;
        DeleteMsgPort
                  (txRequest.IOSer.io_Message.mn_ReplyPort);
        txRequest.IOSer.io_Message.mn_ReplyPort = NULL;
       }
    }
  else/*kein Kanal zu oeffnen; Device evtl exklusiv belegt*/
    vorbereitet = FALSE;

  return vorbereitet;
}

void SendeTextDatei(IOExtSerPtr txReqPtr)
{
  FileHandleBPtr txDateiHdl;
  long           gelesen,
                 geschrieben;

  geschrieben = FPuts(txWin, Senden);        /* Info-Text */
  SetTermChars(txReqPtr);     /* um Dateiende zu erkennen */
  txReqPtr->IOSer.io_Command = CMD_WRITE;
  txDateiHdl = (FileHandleBPtr       ) /* exemplar. Datei */
                Open("S:StartUp-Sequence", MODE_OLDFILE);
  if (txDateiHdl)            /* Datei zum Lesen geoeffnet */
    {
     for(;;)                            /* Endlosschleife */
       {
        gelesen =                     /* Datenblock lesen */
           FRead(txDateiHdl, txPuffer, 1, sizeof(txPuffer));
        if (gelesen)                       /* Byte-Anzahl */
          {
           txReqPtr->IOSer.io_Length = gelesen;
           DoIO(txReqPtr);                  /* abschicken */
           geschrieben = Write(txWin, ".", 1);/* nur Info */
          }
        else               /* nichts (mehr) gelesen, also */
          break;                 /* raus aus der Schleife */

        if (txReqPtr->IOSer.io_Error)  /* bei Sendefehler */
           break;                   /* Schleife verlassen */
       }
     Close(txDateiHdl);
    }
  geschrieben = FPuts(txWin, Beendet);        /* nur Info */
}

void EmpfangeTextDatei(IOExtSerPtr rxReqPtr)
{
  FileHandleBPtr  rxDatei;
  long            geschrieben;

  AbortIO(rxReqPtr); /* den einzigen Request aus dem Emp- */
  WaitIO(rxReqPtr);            /* fangskanal zurueckholen */

  geschrieben = FPuts(rxWin, Empfang);        /* zur Info */

  SetTermChars(rxReqPtr);  /* Dateiende automat. erkennen */

  rxReqPtr->IOSer.io_Length = 1;/*zunaechst nur 1 Zeichen */

  rxDatei = (FileHandleBPtr)   /* dorthin Daten schreiben */
            Open("RAM:Ser.tst", MODE_NEWFILE);
  if (rxDatei)                         /* Datei geoeffnet */
    {
     for(;;)         /* schoen-gefaehrliche Dauerschleife */
       {
        rxReqPtr->IOSer.io_Command = CMD_READ;
        DoIO(rxReqPtr);                /* Zeichen abholen */
        if (rxReqPtr->IOSer.io_Actual)      /* Zeichen da */
          {
           geschrieben =            /* in Datei schreiben */
                         FWrite(rxDatei, rxPuffer, 1,
                         rxReqPtr->IOSer.io_Actual);
           geschrieben = Write(rxWin, ".", 1);    /* Info */
          }
        else                            /* nichts gelesen */
          break;                     /* deshalb abbrechen */

        rxReqPtr->IOSer.io_Command = SDCMD_QUERY;
        DoIO(rxReqPtr); /* Anz. Zeichen im dev-int. Puff.?*/
        if (rxReqPtr->IOSer.io_Actual == 0)   /* keine da */
          break;                     /* raus aus Schleife */
        else /* Leselaenge auf Anzahl vorhandener Zeichen */
          rxReqPtr->IOSer.io_Length =
                               rxReqPtr->IOSer.io_Actual;
        } /* for */
      Close(rxDatei);
    }
  geschrieben = FPuts(rxWin, Beendet);
}

void TerminalLoop()           /* Hauptschleife  des Prog. */
{
  IntuiMessagePtr inMsgPtr;  /* fuer IDCMP-Nachr. aus dem */
  long            klasse,                 /* Sendefenster */
                  done,
                  txSig, /*aus UserPort des Sendefensters */
                  rxSig,  /* aus Empfangs-Device-Struktur */
                  erwartSig, /* Kombination txSig | rxSig */
                  erhaltSig;       /* angekommene Signale */
  int             raw;             /* fuer Tastatur-Codes */

  txSig = 1 << txWinPtr->UserPort->mp_SigBit;
  rxSig = 1 <<
         rxRequest.IOSer.io_Message.mn_ReplyPort->mp_SigBit;
  erwartSig = txSig | rxSig;

  SendIO(&(rxRequest));  /* async. aufs 1. Zeichen warten */
  for(;;)
    {
     erhaltSig = Wait(erwartSig);  /* Tastendruck/Empfang */
     if (txSig & erhaltSig) /*Taste im Sendefenster gedr. */
       {
        inMsgPtr = (IntuiMessagePtr)      /* Msg. abholen */
                   GetMsg(txWinPtr->UserPort);

        klasse = inMsgPtr->Class;   /* copy Msg teilweise */
        raw    = inMsgPtr->Code;

        ReplyMsg(inMsgPtr);            /* Msg beantworten */

        if (IDCMP_VANILLAKEY & klasse)   /* ASCII-Zeichen */
          {
           txPuffer[0] = raw;  /* Zeichen in Sende-Puffer */
           if (txPuffer[0] != 033) /* nicht ESC gedrueckt */
             {
              done = Write(txWin, txPuffer, 1);   /* Echo */
              DoIO(&txRequest);         /* Zeichen senden */
             }
           else                          /* ESC gedrueckt */
             break;                 /* Programm abbrechen */
          }
       else
         if (IDCMP_RAWKEY & klasse) /* z.B. F1 - F10 gedr.*/
           {
            if ((raw >= 80) && (raw <= 89))
              {
              /* Funktionstasten auswerten F1 - F10 */
               if (raw == 80)                       /* F1 */
                 {
                /* 5. Erweiterung
                   Aufruf SendeTextDatei(txRequest);
                   aus Listing 3-5 hier einfuegen
                */

                SendeTextDatei(&txRequest);
                 }
                else
                  if (raw == 81)                    /* F2 */

                  /* 6. Erweiterung
                     Aufruf EmpfangeTextDatei(rxRequest);
                     aus Listing 3-6 hier einfuegen
                  */

                    EmpfangeTextDatei(&rxRequest);
              } /* raw >= 80*/
           } /* IDCMP_RAWKEY */
       } /* txSig */
     if (rxSig & erhaltSig)/* mind. 1 Zeich. im Empf-kanal*/
       {
        WaitIO(&rxRequest);             /* dieses abholen */
        if (rxRequest.IOSer.io_Error == 0)
           done = Write(rxWin, rxPuffer, 1);/*u. ausgeben */

        rxRequest.IOSer.io_Command = SDCMD_QUERY;
        DoIO(&rxRequest);   /* noch mehr Zeichen im Kanal */
        rxRequest.IOSer.io_Command = CMD_READ;
        if (rxRequest.IOSer.io_Actual > 0)          /* ja */
          {
           rxRequest.IOSer.io_Length = /* auf Zeichenanz. */
                                  rxRequest.IOSer.io_Actual;
           DoIO(&(rxRequest));             /* alles lesen */
           done =        /* und im Empf.-Fenst. darstell. */
           Write(rxWin, rxPuffer,rxRequest.IOSer.io_Actual);
           rxRequest.IOSer.io_Length = 1;
          }

        SendIO(&rxRequest); /* aufs naechste Zeich. wart. */
       }
    } /* for */
    AbortIO(&rxRequest);   /* Auftrag an Device abbrechen */
    WaitIO(&rxRequest);
}

void FlushSerialDevice()   /* extern geladenes Device aus */
{                                    /* aus RAM entfernen */
APTR serDev;

  serDev = (APTR)                  /* Serial-Dev. suchen */
           FindName(&execBasePtr->DeviceList, SERIALNAME);
  if (serDev)                                /* gefunden */
    {
     Forbid();               /* damit uns niemand stoert */
     RemDevice(serDev);  /* belegte Ressourcen freigeben */
     Permit();               /* wieder offen fuer andere */
    }
}

void Aufraeumen()
{
  DeleteMsgPort(rxRequest.IOSer.io_Message.mn_ReplyPort);
  CloseDevice(&rxRequest);
  DeleteMsgPort(txRequest.IOSer.io_Message.mn_ReplyPort);
  CloseDevice(&txRequest);
  Close(rxWin);
  Close(txWin);
  FlushSerialDevice();
}

int main()
{
 BOOL done;

 execBasePtr = GetSysBase();   /* Adr ExecBase beschaffen */

 if (execBasePtr->LibNode.lib_Version >= 36) /* ja, OS 2  */
  {
   if (WindowsAngelegt() && SerialVorbereitet())
     {
      TerminalLoop();     /* Steuerschleife des Programms */
      Aufraeumen();
     }
  }
  return 0;
}
