/*****************************************************************************\
*                                                                             *
* tstelnet.c -- telnet program for telser.device                              *
*                                                                             *
* Copyright (c) 1994 by Sam Yee.                                              *
* Source freely distributable in unmodified form.                             *
*                                                                             *
* $ Author:       Sam Yee (samy@sfu.ca) $                                     *
* $ Project:      telser $                                                    *
* $ Date Started: October 30, 1994 $                                          *
* $ Version:      1.0 (30.10.94) $                                            *
*                                                                             *
* History:                                                                    *
* YY/MM/DD Who                Modifications                                   *
* --------------------------------------------------------------------------- *
*                                                                             *
\*****************************************************************************/

#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <devices/serial.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/exec_pragmas.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define RAWIN_RUNTIME
#include "RawIN.h"

/********************************************************************/
/* imports */
extern struct DOSBase *DOSBase;
extern struct SysBase *SysBase;

/********************************************************************/
/* prototypes */
ULONG ScanSerial(struct IOExtSer *ser_rreq, struct IOExtSer  *ser_oreq,
                 char *buf, ULONG buf_len);
void SendSerReadRequest(struct IOExtSer *req, char *c);
void SendSerWriteRequest(struct IOExtSer *req, char *buf, ULONG buf_len);
void AbortReq(struct IORequest *req);

/********************************************************************/
void __regargs __chkabort(void);    /* disable SAS/C ^C checking */
void __regargs __chkabort(){}
void __regargs _CXBRK(void);
void __regargs _CXBRK(){}

const char version[] = "\0$VER: tstelnet 1.00 (28.12.94)";

/********************************************************************/
/* where the action starts */
int
main(int  argc,
     char *argv[])
{
    struct Library  *RawINBase = NULL;  /* std i/o handling library */
    struct Line     *line = NULL;       /* line buffer */
    struct IOExtSer *ser_rreq = NULL,   /* serial stuff */
                    *ser_wreq = NULL,
                    *ser_oreq = NULL;
    struct MsgPort  *ser_rport = NULL,
                    *ser_wport = NULL,
                    *ser_oport = NULL;
    char            device[64] = "telser.device",
                    *s,
                    *prog_name,
                    buf[256],       /* these three buffers must be the same size! */
                    ser_wbuf[256],
                    raw_buf[256],
                    *host,
                    ser_char;           /* for serial reads */
    ULONG           wait_mask;
    int             unit = 0,
                    host_arg_num = 1,
                    port = 0,
                    i,
                    read_len,
                    error_code = 0;
    BOOL            show_usage = FALSE,
                    got_stuff,
                    typed,
                    ser_wrote = FALSE,
                    device_specified = FALSE;

    prog_name = FilePart(argv[0]);

    if (argc == 1)  /* no arguments specified? */
        show_usage = TRUE;
    else if (!strnicmp(argv[host_arg_num],"-d",2))
    {
        device_specified = TRUE;
        s = argv[host_arg_num] + 2;
        i = 0;

        while (*s && (i < sizeof(device)-1) && (*s != ','))
            device[i++] = *s++;

        device[i] = '\0';

        if (*s)
            unit = atol(++s);

        host_arg_num++;
    }

    /* host not specified */
    if (host_arg_num == argc)
        show_usage = TRUE;
    else
    {
        host = argv[host_arg_num];

        if (host_arg_num < (argc-1))
            port = atol(argv[host_arg_num+1]);
    }

    if (show_usage)
    {
        Printf("Usage: %s [-ddevice,unit] host-name [port]\n",prog_name);
        error_code = 1;
    }
    else if (!(ser_rport = CreateMsgPort()) ||
             !(ser_wport = CreateMsgPort()) ||
             !(ser_oport = CreateMsgPort()))
    {
        Printf("%s: no memory for message ports\n",prog_name);
        error_code = 10;
    }
    else if (!(ser_rreq = CreateIORequest(ser_rport,sizeof(struct IOExtSer))) ||
             !(ser_wreq = CreateIORequest(ser_wport,sizeof(struct IOExtSer))) ||
             !(ser_oreq = CreateIORequest(ser_oport,sizeof(struct IOExtSer))))
    {
        Printf("%s: no memory for io requests\n",prog_name);
        error_code = 10;
    }
    else
    {
        if (device_specified)
        {
            if (OpenDevice(device,unit,(struct IORequest *)ser_rreq,0L))
                error_code = 10;
        }
        else
        {
            ser_rreq->io_SerFlags = 0;

            for (i = unit; i < (unit+20);i++)
            {
                if (!OpenDevice(device,i,(struct IORequest *)ser_rreq,0L))
                    break;
            }

            if (i == (unit+20))
                error_code = 10;

            unit = i;
        }

        if (error_code)
            Printf("%s: cannot open device %s, unit %ld\n",prog_name,device,unit);
    }

    if (!error_code)
    {
        if (!(RawINBase = OpenLibrary(RAWIN_NAME,0)))
        {
            Printf("%s: " RAWIN_NAME " not found\n",prog_name);
            error_code = 10;
        }
    }

    if (!RawINBase)
    {
        /* do nothing */
    }
    else if (!(line = RI_AllocLine(2,2)))
    {
        Printf("%s: no memory for line buffer\n",prog_name);
        error_code = 10;
    }
    else
    {
        CopyMem(ser_rreq,ser_wreq,sizeof(struct IOExtSer));
        CopyMem(ser_rreq,ser_oreq,sizeof(struct IOExtSer));

        ser_wreq->IOSer.io_Message.mn_ReplyPort = ser_rport;
        ser_oreq->IOSer.io_Message.mn_ReplyPort = ser_oport;

        ser_oreq->io_Baud = 19200;  /* set the baud rate */
        ser_oreq->io_RBufLen = 4096L;
        ser_oreq->IOSer.io_Command = SDCMD_SETPARAMS;
        DoIO((struct IORequest *)ser_oreq);
        SendSerReadRequest(ser_rreq,&ser_char);

        sprintf(ser_wbuf,"ATDT %s",host);

        if (port)
            sprintf(ser_wbuf+strlen(ser_wbuf),",%d",port);

        strcat(ser_wbuf,"\r");
        SendSerWriteRequest(ser_wreq,ser_wbuf,strlen(ser_wbuf));
        ser_wrote = TRUE;

        typed = TRUE;
        RI_SendReadRequest(line);
        wait_mask = SIGBREAKF_CTRL_C |
                    (1L << ser_rport->mp_SigBit) |
                    (1L << ser_wport->mp_SigBit) |
                    (1L << line->ReadReplyPort->mp_SigBit);

        while (!(line->flags & (LNF_EOF|LNF_BREAKC|LNF_READERROR|LNF_WRITEERROR)))
        {
            got_stuff = FALSE;

            /* get user input */
            if ((read_len = RI_GetBlock(line,FALSE,FALSE,buf,sizeof(buf))) > 0L)
            {
                got_stuff = TRUE;
                typed = TRUE;

                if (ser_wrote)  /* already wrote something? if so, wait! */
                    WaitIO((struct IORequest *)ser_wreq);

                memcpy(ser_wbuf,buf,read_len);
                SendSerWriteRequest(ser_wreq,ser_wbuf,read_len);
                ser_wrote = TRUE;
                RI_SendReadRequest(line);
            }

            if (GetMsg(ser_wport))  /* a write reply? */
                ser_wrote = FALSE;

            /* read from serial port as much as possible */
            if (read_len = ScanSerial(ser_rreq,ser_oreq,buf,sizeof(buf)))
            {
                got_stuff = TRUE;
                RI_WaitWrite(line); /* wait for a write to finish if needed */
                memcpy(raw_buf,buf,read_len);
                RI_SendWriteRequest(line,raw_buf,read_len);
            }

            /* if user typed key(s), send next read request */
            if (typed)
            {
                RI_SendReadRequest(line);
                typed = FALSE;
            }

            if (got_stuff)
                continue;

            if (Wait(wait_mask) & SIGBREAKF_CTRL_C)
                break;
        }

        AbortReq((struct IORequest *)ser_rreq);
        CloseDevice((struct IORequest *)ser_rreq);
    }

    /* cleanup */
    if (ser_rreq)
        DeleteIORequest((struct IORequest *)ser_rreq);

    if (ser_wreq)
        DeleteIORequest((struct IORequest *)ser_wreq);

    if (ser_oreq)
        DeleteIORequest((struct IORequest *)ser_oreq);

    if (ser_rport)
        DeleteMsgPort(ser_rport);

    if (ser_wport)
        DeleteMsgPort(ser_wport);

    if (ser_oport)
        DeleteMsgPort(ser_oport);

    if (line)
        RI_FreeLine(line);

    if (RawINBase)
        CloseLibrary(RawINBase);

    return(error_code);
}

/****************************************************************************/
/* grab bytes from serial port if any */
ULONG
ScanSerial(struct IOExtSer  *ser_rreq,
           struct IOExtSer  *ser_oreq,
           char             *buf,
           ULONG            buf_len)
{
    char    *s;
    ULONG   bytesRead = 0L,
            bytesInQ = 0L;

    if (!buf_len)
        return(0L);

    if (CheckIO((struct IORequest *)ser_rreq))
    {
        WaitIO((struct IORequest *)ser_rreq);
        s = (char *)ser_rreq->IOSer.io_Data;
        buf[0] = *s;
        bytesRead = 1L;
        ser_oreq->IOSer.io_Command = SDCMD_QUERY;

        if (!DoIO((struct IORequest *)ser_oreq))
        {
            if (bytesInQ = ser_oreq->IOSer.io_Actual)
            {
                if (bytesInQ >= buf_len)
                    bytesInQ = buf_len - 1L;

                ser_rreq->IOSer.io_Data = buf+1;
                ser_rreq->IOSer.io_Length = bytesInQ;
                ser_rreq->IOSer.io_Command = CMD_READ;
                DoIO((struct IORequest *)ser_rreq);
                bytesRead += bytesInQ;
            }
        }

        SendSerReadRequest(ser_rreq,s);
    }

    return(bytesRead);
}

/****************************************************************************/
/* send an asynchronous serial read */
void
SendSerReadRequest(struct IOExtSer  *req,
                   char             *c)
{
    req->IOSer.io_Command = CMD_READ;
    req->IOSer.io_Data    = c;
    req->IOSer.io_Length  = 1L;
    SendIO((struct IORequest *)req);
}

/****************************************************************************/
/* send an asynchronous serial write */
void
SendSerWriteRequest(struct IOExtSer *req,
                    char            *buf,
                    ULONG           buf_len)
{
    req->IOSer.io_Command = CMD_WRITE;
    req->IOSer.io_Data    = buf;
    req->IOSer.io_Length  = buf_len;
    SendIO((struct IORequest *)req);
}

/****************************************************************************/
void
AbortReq(struct IORequest   *req)
{
    if (!CheckIO(req))
        AbortIO(req);

    WaitIO(req);
}

/****************************************************************************/
/* END */
