/*****************************************************************************\
*                                                                             *
* tstelnet.c -- telnet program for telser.device                              *
*                                                                             *
* Copyright (c) 1994-1995 by Sam Yee.                                         *
* Source freely distributable in unmodified form.                             *
*                                                                             *
* $ Author:       Sam Yee (samy@sfu.ca) $                                     *
* $ Project:      telser $                                                    *
* $ Date Started: October 30, 1994 $                                          *
* $ Version:      1.20 (5.3.95) $                                             *
*                                                                             *
* History:                                                                    *
* Ver. YY/MM/DD Who                Modifications                              *
* --------------------------------------------------------------------------- *
* 1.20 95/03/05 Sam Yee            Prompts user for host if not specified on  *
*                                  command line                               *
*                                  three escapes will kill session            *
* 1.10 95/01/16 Sam Yee            Three breaks will kill session             *
* 1.00 95/01/01 Sam Yee            Original release                           *
*                                                                             *
\*****************************************************************************/

#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <dos/dosasl.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 sprintf(char *buf, char *fmt,...);

/********************************************************************/
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.20 (6.3.95)";

/********************************************************************/
/* 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 = NULL,
                    host_buf[256],
                    ser_char;           /* for serial reads */
    ULONG           wait_mask;
    int             unit = 0,
                    host_arg_num = 1,
                    port = 23,
                    i,
                    read_len,
                    error_code = 0,
                    break_count = 0;
    BOOL            show_usage = FALSE,
                    got_stuff,
                    typed,
                    ser_wrote = FALSE,
                    device_specified = FALSE,
                    typed_ctrl_c,
                    typed_esc,
                    broke = FALSE,
                    ser_opened = FALSE;

    prog_name = FilePart(argv[0]);

    if ((argc == 2) && (!stricmp(argv[1],"-h") || !strcmp(argv[1],"?")))
        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 specified? */
    if (host_arg_num < argc)
    {
        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);
    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 (CheckSignal(SIGBREAKF_CTRL_C))
                {
                    PrintFault(ERROR_BREAK,FilePart(argv[0]));
                    error_code = 10;
                    broke = TRUE;
                    break;
                }

                if (!OpenDevice(device,i,(struct IORequest *)ser_rreq,0L))
                {
                    ser_opened = TRUE;
                    break;
                }
            }

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

            unit = i;
        }

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

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

    if (RawINBase)
    {
        if (!(line = RI_AllocLine(2,2)))
        {
            Printf("%s: no memory for line buffer\n",prog_name);
            error_code = 10;
        }
        else if (!host)
        {
            Printf("Telnet Host: "); Flush(Output());
            i = RI_Gets(line,host_buf,sizeof(host_buf),0,FALSE,FALSE,TRUE);
            Printf("\r\n");

            if (i > 1)
            {
                Printf("Port [%ld]: ",port); Flush(Output());
                i = RI_Gets(line,buf,6,0,FALSE,FALSE,TRUE);
                Printf("\r\n");

                if (i >= 0)
                {
                    Printf("*** Press Esc Esc Esc to exit\r\n\r\n");

                    if (i)
                        port = atoi(buf);

                    host = host_buf;
                }
            }
        }
    }

    if (line && host)
    {
        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),",%ld",port);

        Printf("Trying %s...\r\n",host); Flush(Output());
        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)))
        {
            typed_ctrl_c = typed_esc = got_stuff = FALSE;

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

                if (buf[0] == 0x1b)
                {
                    break_count++;
                    typed_esc = TRUE;
                }
                else if (buf[0] == 0x03)
                    typed_ctrl_c = 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;

                if (break_count != 3)
                    typed = TRUE;
            }

            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)
            {
                if (!typed_ctrl_c && !typed_esc)
                    break_count = 0;

                continue;
            }

            if (Wait(wait_mask) & SIGBREAKF_CTRL_C)
                break_count++;

            if (break_count == 3)
                break;
        }

        AbortReq((struct IORequest *)ser_rreq);
    }

    if (ser_opened)
        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 */
