/********************************************************************
 *  vt100 terminal emulator with xmodem transfer capability
 *
 *	v2.6 870227 DBW - bug fixes for all the stuff in v2.5
 *	v2.5 870214 DBW - more additions (see readme file)
 *	v2.4 861214 DBW - lots of fixes/additions (see readme file)
 *	v2.3 861101 DBW - minor bug fixes
 *	v2.2 861012 DBW - more of the same
 *	v2.1 860915 DBW - new features (see README)
 *	     860901 ACS - Added Parity and Word Length and support code
 *	     860823 DBW - Integrated and rewrote lots of code
 *	v2.0 860809 DBW - Major rewrite
 *	v1.1 860720 DBW	- Switches, 80 cols, colors, bug fixes
 *	v1.0 860712 DBW	- First version released
 *
 *  use <esc> to abort xmodem or kermit transfers
 *
 *  written by Michael Mounier
 *  new version by Dave Wecker
 *******************************************************************/

/*  all includes defines and globals */
#include "vt100.h"

/**************************************************************/
/* here are all the global definitions that appear in vt100.h */
/**************************************************************/

char    bufr[BufSize];
int     fd, timeout = FALSE, ttime;
int	multi = FALSE, server;
long    bytes_xferred;
char	MyDir[60];
struct	FileLock *MyDirLock = NULL;
struct	FileLock *StartLock = NULL;
struct	IntuitionBase *IntuitionBase;
struct	GfxBase *GfxBase;

struct	TextAttr myattr = {
    (STRPTR) "topaz.font",
    8,
    0,
    0};
struct	TextFont *myfont = NULL;
struct NewScreen NewScreen = {
   0L,0L,640L,200L,1L,       /* left, top, width, height, depth */
   0,1,HIRES,    /* DetailPen, BlockPen, ViewModes */
   CUSTOMSCREEN,&myattr,   /* Type, Font */
   (UBYTE *)"VT100", /* Title */
   NULL,NULL };         /* Gadgets, Bitmap */
struct NewWindow NewWindow = {
   0,0L,640L,200L,     /* left, top, width, height */
   0,1,              /* detailpen, blockpen */
   MENUPICK|CLOSEWINDOW|RAWKEY|REQCLEAR|REQSET|ACTIVEWINDOW|INACTIVEWINDOW,
   SMART_REFRESH|ACTIVATE|BORDERLESS|WINDOWCLOSE|WINDOWDEPTH|WINDOWDRAG,
   NULL,NULL,        /* FirstGadget, CheckMark */
   (UBYTE *)NULL,
   NULL,             /* set screen after open screen */
   NULL,             /* bitmap */
   640L, 200L, 640L, 200L,/* minw, minh, maxw, maxh */
   CUSTOMSCREEN      /* Type */
   };
struct IntuiText MyTitle = {
    0,1,JAM2,26,0,	/* front pen, back pen, mode, left, top */
    &myattr,		/* font */
    (UBYTE *)VERSION,	/* title */
    NULL};		/* next text */
struct Screen *myscreen = NULL;      /* ptr to applications screen */
struct Window *mywindow = NULL;     /* ptr to applications window */
struct ViewPort *myviewport;
struct RastPort *myrastport;
struct IntuiMessage *NewMessage;    /* msg structure for GetMsg() */
struct Preferences  *Prefs;	    /* preferences from GetPrefs() */

/**** String requester support ******/

char	InpBuf[80],UndoBuf[80],Prompt[80];
struct IntuiText donetxt = {
    1,0,JAM2,0,0,	/* front pen, back pen, mode, left, top */
    &myattr,		/* font */
    (UBYTE *)"DONE",	/* question to ask */
    NULL};		/* next text */
struct Gadget mydonegad = {
    NULL,290,2,40,10,/* next,left,top,width,height */
    GADGHCOMP|REQGADGET,/* flags */
    RELVERIFY|ENDGADGET,/* activation */
    BOOLGADGET,		/* gadget type */
    NULL,NULL,&donetxt,	/* gad render, sel render, gad text */
    0L,NULL,2,NULL};	/* mutual exclude, special, ID, user data */
struct	StringInfo mystrinfo = {
    (UBYTE *)InpBuf,
    (UBYTE *)UndoBuf,
    0,80,0,0,0,0,	/* initial, max, disp, undo, #chrs, dsp chrs */
    0,0,NULL,0L,NULL};	/* left,top,layer,longint,keymap */
struct Gadget mystrgad = {
    &mydonegad,10,12,320,10,	/* next,left,top,width,height */
    GADGHCOMP|REQGADGET,/* flags */
    ENDGADGET,STRGADGET,/* activation, type */
    NULL,NULL,NULL,	/* gad render, sel render, gad text */
    0L,			/* mutual exclude */
    (APTR)&mystrinfo,	/* special info */
    1,NULL};		/* gadget ID, user data */
struct IntuiText mystrtxt = {
    0,1,JAM2,10,2,	/* front pen, back pen, mode, left, top */
    &myattr,		/* font */
    (UBYTE *)Prompt,	/* question to ask */
    NULL};		/* next text */
struct Requester myrequest = {
    NULL,200,40,340,22,	/* older requester, left, top, width, height */
    0,0,&mystrgad,	/* relleft reltop, gadgets */
    NULL,		/* border */
    &mystrtxt,		/* text */
    NULL,1,NULL,	/* flags, back fill pen, layer */
    {0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0},	/* pad1 */
    NULL,NULL,		/* image bit map, rquest window */
    {0,0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0,0} /* pad2 */
    };
int numreqs = 0;	/* number of outstanding requestors */

/***** menu structures *****/
struct MenuItem FileItem[FILEMAX];
struct IntuiText FileText[FILEMAX];
struct MenuItem CommItem[COMMAX];
struct IntuiText CommText[COMMAX];
struct MenuItem RSItem[RSMAX];
struct IntuiText RSText[RSMAX];
struct MenuItem ParItem[PARMAX];
struct IntuiText ParText[PARMAX];
struct MenuItem XFItem[XFMAX];
struct IntuiText XFText[XFMAX];
struct MenuItem ScriptItem[SCRIPTMAX];
struct IntuiText ScriptText[SCRIPTMAX];
struct MenuItem UtilItem[UTILMAX];
struct IntuiText UtilText[UTILMAX];
struct Menu menu[MAXMENU];
struct IOExtSer *Read_Request;
char *rs_in;
struct IOExtSer *Write_Request;
char rs_out[2];
struct timerequest Timer;
struct MsgPort *Timer_Port = NULL;
struct timerequest Script_Timer;
struct MsgPort *Script_Timer_Port = NULL;
struct IOAudio Audio_Request;
struct MsgPort *Audio_Port = NULL;
UBYTE  *BeepWave;
UBYTE  Audio_AllocMap[4] = { 1, 8, 2, 4 };
int x,y,curmode;
int MINX	= 0;
int MAXX	= 632;
int MINY	= 14;
int MAXY	= 198;
int top		= 14;
int bot		= 198;
int savx	= 0;
int savy	= 14;
int savmode	= 0;
int nlmode	= 0;
int alt		= 0;
int savalt	= 0;
int a[2]	= { 0, 0 };
int sa[2]	= { 0, 0 };
int  inesc	= -1;
int  inctrl	= -1;
int  private	= 0;
int  badseq	= 0;
int  maxcol	= 79;

/*************************** defaults *******************************/
int	p_baud	     = 2400;	    /* baud rate */
int	p_screen     = 1;	    /* 0 = WORKBENCH,	    1 = CUSTOM */
int	p_wbcolors   = 1;	    /* 0 = Custom, 1 = Workbench colors */
int	p_interlace  = 1;	    /* 0 = no interlace,    1 = interlace */
int	p_depth	     = 1;	    /* number of bit planes (1 or 2) */
int	p_foreground = 0x950;	    /* default foreground RGB color */
int	p_background = 0x000;	    /* default background RGB color */
int	p_bold	     = 0x900;	    /* default BOLD       RGB color */
int	p_cursor     = 0x009;	    /* default Cursor	  RGB color */
int	p_lines	     = 48;	    /* number of lines on the screen */
int	p_mode	     = 1;	    /* 0 = image, 1 = CRLF (for kermit) */
int	p_buffer     = 512;	    /* read buffer size (>= 512 bytes) */
int     p_parity     = 0;	    /* 0=none,1=mark,2=space,3=even,4=odd */
long	p_break	     = 750000;	    /* break time (in micro seconds) */
int	p_volume     = 64;	    /* beep volume (0 = DisplayBeep) */
int	p_wrap	     = 0;	    /* 0 = truncate, 1 = wrap long lines */
int	p_keyapp     = 0;	    /* 0 = numeric, 1 = application keypad */
int	p_curapp     = 0;	    /* 0 = cursor, 1 = application cursor */
int	p_echo	     = 0;	    /* 0 = full duplex, 1 = half duplex */
int	p_bs_del     = 0;	    /* 0 = normal, 1 = swap bs and delete */
int	p_convert    = 1;	    /* 1 = convert filenames to lower case */
char	p_keyscript  = 0x7E;	    /* function key script introducer = ~ */
char	*p_f[10]     = {	    /* function key defaults */
    "\033OP","\033OQ","\033OR","\033OS",
    "f5","f6","f7","f8","f9","f10" };

char	*p_F[10]     = {	    /* shifted function key defaults */
    "F1","F2","F3","F4","F5",
    "F6","F7","F8","F9","F10"};

/* for script file */
int script_on;
int script_wait;
int doing_init = 0;

/******************************************************/
/*                   Main Program                     */
/*                                                    */
/*      This is the main body of the program.         */
/******************************************************/

char lookahead[80];
FILE *tranr = NULL;
FILE *trans = NULL;
int capture,send;
char name[80];
struct MsgPort *mySerPort;

main(argc,argv)
int	argc;
char	**argv;
    {
    ULONG class;
    unsigned int code, qual;
    int KeepGoing,i,la,dola,actual;
    char c,*ptr;

    ptr = InitDefaults(argc,argv);
    InitDevs();
    InitFileItems();
    InitCommItems();
    InitScriptItems();
    InitUtilItems();
    InitMenu();
    SetMenuStrip(mywindow,&menu[0]);
    PrintIText(mywindow->RPort,&MyTitle,0L,0L);

    MyDir[0]  =	    '\000';
    StartLock =	(struct FileLock *)((ULONG)((struct Process *)
		    (FindTask(NULL)))->pr_CurrentDir);
    MyDirLock = (struct FileLock *)DupLock(StartLock);
    KeepGoing =	    TRUE;
    capture   =	    FALSE;
    send      =	    FALSE;
    maxcol    =	    MAXX / 8;
    la	      =	    0;
    x	      =	    MINX ; 
    y	      =	    MINY; 
    curmode   =	    FS_NORMAL;
    script_on =     FALSE;
    script_wait=    TRUE;
    SetAPen(mywindow->RPort,1L);
    cursorflip();
    cursorflip();    
    emit(12);
    mySerPort = Read_Request->IOSer.io_Message.mn_ReplyPort;
    SendIO(Read_Request);

    /* see if we had a startup script */
    if (ptr != NULL) script_start(ptr);

    while( KeepGoing )
	    {
	    /* wait for window message or serial port message */
	    cursorflip();
	    if (script_wait)	/* if script ready dont wait here */
		Wait(
		 (1L << mySerPort->mp_SigBit) |
		 (1L << mywindow->UserPort->mp_SigBit) |
		 (1L << Script_Timer_Port->mp_SigBit));
	    cursorflip();
	    
	    /* do ascii file send */
	    if (send)
		{
		if ((c=getc(trans)) != EOF) {
		    if (c == '\n') c = '\r';
		    sendchar(c);
		    }
		else {
		    fclose(trans);
		    req("File Sent","",0);
		    send=FALSE;
		    }
		}

	    /* see if there are any characters from the host */
	    if (CheckIO(Read_Request)) {
		WaitIO(Read_Request);
		c = rs_in[0] & 0x7F;
		doremote(c);
		if (script_on) chk_script(c);
	        if (capture && c != 10) {
	      	    if (c == 13) c = 10;
		    putc(c , tranr);
		    }
		Read_Request->IOSer.io_Command = SDCMD_QUERY;
		DoIO(Read_Request);
		Read_Request->IOSer.io_Command = CMD_READ;
		actual = (int)Read_Request->IOSer.io_Actual;
		if (actual > 0) {
		    if (inesc   <  0 &&
			inctrl  <  0 &&
			a[alt]  == 0 &&
			capture == FALSE) dola = 1;
		    else dola = 0;
		    Read_Request->IOSer.io_Length =
			Read_Request->IOSer.io_Actual;
		    DoIO(Read_Request);
		    Read_Request->IOSer.io_Length = 1;

		    for (i = 0; i < actual; i++) {
			c=rs_in[i] & 0x7f;
			if (script_on) chk_script(c);

			if (dola == 1) {
			    if (c >= ' ' && c <= '~' && la < 80)
				lookahead[la++] = c;
			    else {
				if (la > 0) {
				    emitbatch(la,lookahead);
				    la = 0;
				    }
				doremote(c);
				dola = 0;
				}
			    }
			else {
			    doremote(c);
			    if (inesc   <  0 &&
				inctrl  <  0 &&
				a[alt]  == 0 &&
				capture == FALSE) dola = 1;
			    if (capture && c != 10) {
				if (c == 13) c = 10;
				putc(c , tranr);
				}
			    }
			}

		    /* dump anything left in the lookahead buffer */
		    if (la > 0) {
			emitbatch(la,lookahead);
			la = 0;
			}
		    }
		SendIO(Read_Request);
		}

	    while((NewMessage = 
		    (struct IntuiMessage *)GetMsg(mywindow->UserPort))
			!= FALSE) {
		class = NewMessage->Class;
		code = NewMessage->Code;
		qual = NewMessage->Qualifier;
		ReplyMsg( NewMessage );
		switch( class )
		    {
		    case REQCLEAR:
		    numreqs = 0;
		    break;

		    case CLOSEWINDOW:
		    KeepGoing = FALSE;
		    break;

		    case RAWKEY:
		    c = toasc(code,qual,0);
		    if (p_echo) doremote(c);
		    break;

		    case NEWSIZE:
		    emit(12);
		    break;

		    case MENUPICK:
		    handle_menupick(class,code);
		    break;				    

		    default:
		    PrintIText(mywindow->RPort,&MyTitle,0L,0L);
		    break;
		    }   /* end of switch (class) */
		}   /* end of while ( newmessage )*/

            if (!script_wait || 
                 (CheckIO(&Script_Timer) && 
		    script_wait == WAIT_TIMER))
		do_script_cmd(NEXTCOMMAND);
	    }  /* end while ( keepgoing ) */
		
    /*   It must be time to quit, so we have to clean
    *   up and exit.
    */

    cleanup("",0);

    } /* end of main */

/* cleanup code */

cleanup(reason, fault)
char *reason;
int fault;
    {
    switch(fault) {
	case 0:		/* quitting close everything */
	ClearMenuStrip( mywindow ); 
	CloseDevice(&Audio_Request);
	if (MyDirLock != NULL) UnLock(MyDirLock);

	case 8:		/* error opening audio */
	DeletePort(Audio_Port);
	FreeMem(BeepWave,BEEPSIZE);
	CloseDevice(&Timer);

	case 7:		/* error opening timer */
	DeletePort(Timer_Port);  
	CloseDevice(&Script_Timer);
	DeletePort(Script_Timer_Port);

	case 6:		/* error opening write device */
	DeletePort(Write_Request->IOSer.io_Message.mn_ReplyPort);
	FreeMem(Write_Request,(long)sizeof(*Write_Request));
	CloseDevice(Read_Request);

	case 5:		/* error opening read device */
	DeletePort(Read_Request->IOSer.io_Message.mn_ReplyPort);
	FreeMem(Read_Request,(long)sizeof(*Read_Request));

	case 4:		/* error opening window */
	if (myfont   != NULL) CloseFont( myfont );
	if (mywindow != NULL) CloseWindow( mywindow );
	if (p_screen != 0) CloseScreen( myscreen );

	case 3:		/* error opening screen */
	case 2:		/* error opening graphics library */
	case 1:		/* error opening intuition */
	default:
	if (*reason) puts (reason);
	}
    exit(fault);
    } 

do_capture(file)
char *file;
    {
    if (capture == TRUE)
        {
        capture=FALSE;
        fclose(tranr);
        req("End File Capture","",0);
        }
    else
        {
        if (file == NULL) {
	    name[0] = '\000';
            req("Ascii Capture:",name,1);
	    } 
	else strcpy(name, file);
        if ((tranr=fopen(name,"w")) == 0) {
	    capture=FALSE;
	    req("Error Opening File","",0);
	    return(FALSE);
	    }
	capture=TRUE;
        }
    }

do_send(file)
char *file;
    {
    if (send == TRUE)
	{ 
        send=FALSE;
        fclose(trans);
        req("File Send Cancelled","",0);
        }
    else
        {
        if (file == NULL) {
	    name[0] = '\000';
            req("Ascii Send:",name,1);
            }
	else strcpy(name, file);
        if ((trans=fopen(name,"r")) == 0) {
   	    send=FALSE;
	    req("Error Opening File","",0);
	    return(FALSE);
	    }
	send=TRUE;
	}
    }

void setparams()
    {
    Read_Request->IOSer.io_Command = 
	Write_Request->IOSer.io_Command = 
	    SDCMD_SETPARAMS;
    DoIO(Read_Request); DoIO(Write_Request);
    Read_Request->IOSer.io_Command = CMD_READ;
    SendIO(Read_Request);
    Write_Request->IOSer.io_Command = CMD_WRITE;
    }

void hangup ()
    {
    AbortIO(Read_Request);
    CloseDevice (Read_Request);
    Timer.tr_time.tv_secs=0L;
    Timer.tr_time.tv_micro=750000L;
    DoIO((char *) &Timer.tr_node);
    OpenDevice (SERIALNAME,NULL,Read_Request,NULL);
    setparams();
    }

void redocomm() {
    ClearMenuStrip( mywindow );         /* Remove old menu */
    InitCommItems();                    /* Re-do comm menu   */
    SetMenuStrip(mywindow,&menu[0]);    /* Re-display the menu */	
    }

void setserbaud(baud, redomenu)
int baud;
LONG redomenu;
    {
    AbortIO(Read_Request);
    Write_Request->io_Baud = Read_Request->io_Baud = baud;
    setparams();
    p_baud = baud;
    if (redomenu) redocomm();
    }

void redoutil() {
    ClearMenuStrip(mywindow);
    InitUtilItems();
    SetMenuStrip(mywindow,&menu[0]);
    }

void handle_menupick(class, code)
ULONG class;
unsigned int code;
    {
    unsigned int menunum, itemnum, subnum;

    if (code == MENUNULL) return;

    menunum = MENUNUM( code );
    itemnum = ITEMNUM( code );
    subnum  = SUBNUM( code );
    switch( menunum ) {
	case 0:
	switch( itemnum ) {
	    case 0:
	    do_capture(NULL);
	    break;

	    case 1:
	    do_send(NULL);
	    break;

	    case 2:
	    if (p_parity > 0) {
		req("Parity setting prevents this","",0);
		break;
		}
	    name[0] = '\000';
	    req("Xmodem Receive:",name,1);
	    multi_xfer(name,XMODEM_Read_File,0);
	    break;

	    case 3:
	    if (p_parity > 0) {
		req("Parity setting prevents this","",0);
		break;
		}
	    name[0] = '\000';
	    req("Xmodem Send:",name,1);
	    multi_xfer(name,XMODEM_Send_File,1);
	    break;

	    case 4:
	    server = TRUE;
	    name[0] = '\000';
	    req("Kermit GET remote file(s):",name,1);
	    multi_xfer(name,dokreceive,0);
            break;

	    case 5:
	    multi_xfer("",dokreceive,0);
	    break;

	    case 6:
	    server = TRUE;
	    name[0] = '\000';
	    req("Kermit Send local name:",name,1);
	    multi_xfer(name,doksend,1);
	    break;

	    case 7:
	    saybye();
	    break;
	    }
	break;

	case 1:
	switch( itemnum ) {
	    case 0:
	    switch( subnum ) {
		case 0:
		setserbaud(300, FALSE);
		break;

		case 1:
		setserbaud(1200, FALSE);
		break;

		case 2:
		setserbaud(2400, FALSE);
		break;

		case 3:
		setserbaud(4800, FALSE);
		break;

		case 4:
		setserbaud(9600, FALSE);
		break;
		}
	    break;	    

	    case 1:
	    /* Set  Parity */
	    p_parity = subnum;
	    break;

	    case 2:
	    /* set transfer mode */
	    if (subnum < 2) p_mode = subnum;
	    else {
		if (p_convert)	p_convert = 0;
		else		p_convert = 1;
		redocomm();
		}
	    break;
	    }
	break;

	case 2:
	if (!itemnum && !script_on) {
	    name[0] = '\000';
	    req("Script file name:",name,1);
	    script_start(name);
	    }
	if (itemnum && script_on) exit_script();
	break;

	case 3:
	switch( itemnum ) {
	    case 0:
	    sendbreak();
	    break;

	    case 1:
	    hangup();
	    break;

	    case 2:
	    strcpy(name,MyDir);
	    req("Directory:",name,1);
	    set_dir(name);
	    break;

	    case 3:
	    top = MINY; bot = MAXY; savx = MINX; savy = MINY;
	    curmode = FS_NORMAL; inesc = -1;
	    a[0] = 0; a[1] = 0; sa[0] = 0; sa[1] = 0;
	    redoutil();
	    emit(12);
	    break;

	    case 4:
	    if (p_echo) p_echo = 0;
	    else	p_echo = 1;
	    redoutil();
	    break;

	    case 5:
	    if (p_wrap) p_wrap = 0;
	    else        p_wrap = 1;
	    redoutil();
	    break;

	    case 6:
	    if (p_keyapp) p_keyapp = 0;
	    else          p_keyapp = 1;
	    redoutil();
	    break;

	    case 7:
	    if (p_curapp) p_curapp = 0;
	    else          p_curapp = 1;
	    redoutil();
	    break;	    

	    case 8:
	    swap_bs_del();
	    redoutil();
	    break;
	    }

	break;
	} /* end of switch ( menunum ) */
    }

