/*
 * Windows Kermit protocol support functions
 *
 * Copyright (c) 1990 by
 * William S. Hall
 * 3665 Benton Street, #66
 * Santa Clara, CA 95051
 *
 */

/* The file is large, so these defines reduce the size of
 * the symbol table generated by windows.h
 */
#define NOGDICAPMASKS     - CC_*, LC_*, PC_*, CP_*, TC_*, RC_
#define NOVIRTUALKEYCODES - VK_*
// #define NOWINMESSAGES     - WM_*, EM_*, LB_*, CB_*
#define NOWINSTYLES       - WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
#define NOSYSMETRICS      - SM_*
// #define NOMENUS	      - MF_*
#define NOICONS	      - IDI_*
#define NOKEYSTATES       - MK_*
// #define NOSYSCOMMANDS     - SC_*
#define NORASTEROPS       - Binary and Tertiary raster ops
// #define NOSHOWWINDOW      - SW_*
#define OEMRESOURCE       - OEM Resource values
#define NOATOM	      - Atom Manager routines
#define NOCLIPBOARD       - Clipboard routines
#define NOCOLOR	      - Screen colors
// #define NOCTLMGR	      - Control and Dialog routines
// #define NODRAWTEXT	      - DrawText() and DT_*
// #define NOGDI	      - All GDI defines and routines
// #define NOKERNEL	      - All KERNEL defines and routines
// #define NOUSER	      - All USER defines and routines
// #define NOMB	      - MB_* and MessageBox()
// #define NOMEMMGR	      - GMEM_*, LMEM_*, GHND, LHND, associated routines
#define NOMETAFILE	      - typedef METAFILEPICT
#define NOMINMAX	      - Macros min(a,b) and max(a,b)
#define NOMSG	      - typedef MSG and associated routines
// #define NOOPENFILE	      - OpenFile(), OemToAnsi, AnsiToOem, and OF_*
#define NOSCROLL	      - SB_* and scrolling routines
#define NOSOUND	      - Sound driver routines
#define NOTEXTMETRIC      - typedef TEXTMETRIC and associated routines
#define NOWH	      - SetWindowsHook and WH_*
// #define NOWINOFFSETS      - GWL_*, GCL_*, associated routines
// #define NOCOMM	      - COMM driver routines
#define NOKANJI	      - Kanji support stuff.
#define NOHELP            - Help engine interface.
#define NOPROFILER	      - Profiler interface.
#define NODEFERWINDOWPOS  - DeferWindowPos routines

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>	
#include <limits.h>
#include <io.h>
#include <sys\types.h>
#include <sys\stat.h>
#ifdef COLUMBIA
#include "wkasci.h"
#include "wkkerm.h"
#include "wkkdlg.h"
#else
#include "ascii.h"
#include "wnkerm.h"
#include "wnkdlg.h"
#endif

/* local function declarations */
static	short	NEAR	krmWriteComm(int cid, BYTE *buf, int len);
static	short	NEAR	krm_spack(char type, int n, int len, BYTE *data);
static	void	NEAR	krm_errpkt(int msgnum);
static	void	NEAR	krmSet8BitQuote(register BYTE sq);
static	void	NEAR	krm_nak(void);
static	void	NEAR	krmSetMenus(BOOL mode);
static	void	NEAR	krmUpdateXferBox(WORD id, char *str);
static	void	NEAR	krm_nxtpkt(void);
static	int	NEAR	krm_decode(BYTE outbuf[], BYTE inbuf[], int *len);
static	int	NEAR	krm_getpacket(register BYTE *str, register int len);
static	WORD	NEAR	krm_chk1(WORD);
static	WORD	NEAR	krm_chksum(BYTE *p, WORD init);
static	DWORD	NEAR	krm_chksum3(BYTE *p, DWORD init);
static	HANDLE	NEAR	krmOpenReceiveFile(BYTE *inbuf);
BOOL	FAR	PASCAL	krmHideChildren(HWND hWnd, LONG lParam);
static	HANDLE	NEAR	krm_endserver(int mode);
static  void	NEAR	InsertLine(HWND hDlg, WORD id, BYTE *buf);

/*
 * krm_getnextfile
 *
 * Parse the string of file names for the next one to send.
 * The names themselves are delimited by a space.
 */
char * NEAR krm_getnextfile(BOOL first)
{
    char *type;

    if (Kermit.abort == KRM_BATCHABORT) {
	Kermit.abort = 0;
        return (char *)NULL;
    }	

    type = (first ? Kermit.pFilelist : NULL);
    return strtok(type, " ");
}

/*
 * krm_err
 *
 * Send an error packet to remote Kermit 
 * and exit protocol with a message to user.
 */
void NEAR krm_err(int ref)
{
    krm_errpkt(ref);
    krm_tend(ref);
}

/*
 * krm_sinit
 *
 * Send an init packet to remote Kermit
 */
int NEAR krm_sinit(char type)
{
    BYTE data[40];
    register int i = krm_rpar(data);
    return (krm_spack(type, Kermit.seq, i, data));
}

/*
 * krm_sfile
 *
 * Send file name packet to remote Kermit
 */
int NEAR krm_sfile(void)
{

    BYTE buf[KRM_MAXDATALEN + 1];
    register int outlen;
    int inlen;
    int result;
    struct stat fdata;

    if ((Kermit.hFile = OpenFile((LPSTR)Kermit.pFile,
			 (OFSTRUCT FAR *)&Kermit.fstruct, OF_READ)) != -1) {
	stat(Kermit.fstruct.szPathName, &fdata);
	Kermit.filesize = fdata.st_size ? fdata.st_size : Kermit.filesize;
        Kermit.bytesmoved = 0;
	if (IsWindow(Kermit.hWndXfer)) {
	    SetDlgItemText(Kermit.hWndXfer, IDD_KRM_FILENAME,
			   (strrchr(Kermit.fstruct.szPathName,'\\') + 1));
	    SetDlgItemText(Kermit.hWndXfer, IDD_KRM_BYTESMOVED,
			   ultoa(Kermit.bytesmoved,buf,10));
	    SetDlgItemText(Kermit.hWndXfer, IDD_KRM_PERCENTAGE,
			   itoa(0, buf, 10));
	}
	Kermit.maxsenddatalen = krm_sndinit.maxpktsize - 2 - Kermit.bctu;
	inlen = strlen(Kermit.pFile);
	outlen = krm_encode(buf, Kermit.pFile, Kermit.maxsenddatalen, &inlen);
        krm_nxtpkt();
        result = krm_spack('F',Kermit.seq, outlen, buf);
	if (result < 0) {
	    krm_rclose(FALSE);
	    return result;
	}
    }
    return (Kermit.hFile);
}

/*
 * krm_sdata
 *
 * Send encoded data packet to remote Kermit
 */
int NEAR krm_sdata(void)
{
    BYTE sbuf[KRM_MAXDATALEN + 1];
    BYTE rbuf[KRM_MAXDATALEN + 1];
    register int len;
    register int numread = 0;
    int numrem = 0;
    int result;
    BYTE *sptr;
    int numcoded = 0;
    int maxlen = Kermit.maxsenddatalen;

    for (sptr = sbuf; (numrem == 0) && (numcoded < maxlen); 
		      sptr += len, maxlen -= len) {
	if ((numread = read(Kermit.hFile, rbuf, sizeof(rbuf) - 1)) > 0) {
	    numrem = numread;
	    numcoded += len = krm_encode(sptr, rbuf, maxlen, &numrem);
	    Kermit.bytesmoved = lseek(Kermit.hFile, -(LONG)numrem, SEEK_CUR);
	}
	else
	    break;
    }
    if ((numread >= 0) && (numcoded > 0)) {
        krm_nxtpkt();
        result = krm_spack('D', Kermit.seq, numcoded, sbuf);
	if (result > 0) {
	    if (IsWindow(Kermit.hWndXfer)) {
		SetDlgItemText(Kermit.hWndXfer, IDD_KRM_BYTESMOVED,
			       ultoa(Kermit.bytesmoved,sbuf,10));
	        SetDlgItemText(Kermit.hWndXfer, IDD_KRM_PERCENTAGE,
		    ultoa(Kermit.bytesmoved * 100 / Kermit.filesize, sbuf, 10));
	    }
	}
	return result;
    }
    return numread;
}

/*
 * krm_seof
 *
 * Send end of file packet to remote Kermit
 */
int NEAR krm_seof(char *s)
{

    if (krm_rclose(FALSE)) {
        krm_nxtpkt();
        return (krm_spack('Z', Kermit.seq, strlen(s), s));
    }
    return (-1);
}

/*
 * krm_seot
 *
 * Send end of transmission (break) packet to remote Kermit
 */
int NEAR krm_seot(void)
{
    krm_nxtpkt();
    return (krm_spack('B', Kermit.seq, 0, ""));
}

/*
 * krm_encode
 *
 * Encode a buffer quoting control characters,
 * adding run length encoding and eight bit
 * quoting if requested, while packing buffer to its maximum.
 */
int NEAR krm_encode(BYTE *outbuf, BYTE *inbuf, int maxlen, int *inlen)
{
    register int i, j;
    BYTE t, t7;
    BYTE b8;
    int rpt = 0;
    int back = 1;
    int extra = 0;

    for (i = 0, j = 0; (i < maxlen) && (j < *inlen); ) {
	t = inbuf[j++];
	t7 = t & (BYTE)0x7f;
	b8 = t & (BYTE)0x80;
	
	if (Kermit.rptflag) {
	    for (rpt = 0; (rpt < 93) && (j < *inlen); rpt++, j++)
	        if (t != inbuf[j])
		    break;
	    if (rpt == 1) {
		j -= 1;
		rpt = 0;
	    }
	    back = 1 + rpt;
	    extra = rpt ? 2 : 0;
	    if (rpt > 1)
		rpt += 1;
	}
	if ((t7 < SP) || (t7 == DEL)) {
	    if (Kermit.ebqflag && b8) {
		if (i < (maxlen - 2 - extra)) {
		    if (rpt) {
			outbuf[i++] = krm_sndinit.rpquote;
			outbuf[i++] = (BYTE)tochar(rpt);
		    }
		    outbuf[i++] = krm_sndinit.ebquote;
		    outbuf[i++] = krm_sndinit.quote;
		    outbuf[i++] = (BYTE)ctl(t7);
		}
		else {
		    j -= back;
		    break;
		}
	    }
	    else {
		if (i < (maxlen - 1 - extra)) {
		    if (rpt) {
			outbuf[i++] = krm_sndinit.rpquote;
			outbuf[i++] = (BYTE)tochar(rpt);
		    }
		    outbuf[i++] = krm_sndinit.quote;
		    outbuf[i++] = (BYTE)ctl(t);
		}
		else {
		    j -= back;
		    break;
		}
	    }
	}
	else if (t7 == krm_sndinit.rpquote) {
	    if (Kermit.rptflag) {
	        if (Kermit.ebqflag) {
		    if (b8) {
		        if (i < (maxlen - 2 - extra)) {
		            if (rpt) {
			        outbuf[i++] = krm_sndinit.rpquote;
			        outbuf[i++] = (BYTE)tochar(rpt);
		            }
		            outbuf[i++] = krm_sndinit.ebquote;
		            outbuf[i++] = krm_sndinit.quote;
		            outbuf[i++] = t7;
		        }
		        else {
			    j -= back;
			    break;
		        }
		    }
		    else {
		        if (i < (maxlen - 1 - extra)) {
		            if (rpt) {
			        outbuf[i++] = krm_sndinit.rpquote;
			        outbuf[i++] = (BYTE)tochar(rpt);
		            }
		            outbuf[i++] = krm_sndinit.quote;
		            outbuf[i++] = t7;
		        }			
		        else {
			    j -= back;
			    break;
		        }
		    }
	        }
	        else {
		    if (i < (maxlen - 1 - extra)) {
		        if (rpt) {
		            outbuf[i++] = krm_sndinit.rpquote;
			    outbuf[i++] = (BYTE)tochar(rpt);
		        }
			outbuf[i++] = krm_sndinit.quote;
	                outbuf[i++] = t;
		    }
		    else {
		        j -= back;
		        break;
		    }
	        }
	    }
	    else {
	        if (Kermit.ebqflag && b8) {
		    if (i < (maxlen - 1 - extra)) {
		        outbuf[i++] = krm_sndinit.ebquote;
		        outbuf[i++] = t7;
		    }
		    else {
		        j -= back;
		        break;
		    }
	        }
	        else {
		    if (i < (maxlen - extra)) {
		        outbuf[i++] = t;
		    }
		    else {
		        j -= back;
		        break;
		    }
	        }
	    }
	}
	else if (t7 == krm_sndinit.ebquote) {
	    if (Kermit.ebqflag) {
		if (b8) {
		    if (i < (maxlen - 2 - extra)) {
		        if (rpt) {
			    outbuf[i++] = krm_sndinit.rpquote;
			    outbuf[i++] = (BYTE)tochar(rpt);
		        }
		        outbuf[i++] = krm_sndinit.ebquote;
		        outbuf[i++] = krm_sndinit.quote;
		        outbuf[i++] = t7;
		    }
		    else {
			j -= back;
			break;
		    }
		}
		else {
		    if (i < (maxlen - 1 - extra)) {
		        if (rpt) {
			    outbuf[i++] = krm_sndinit.rpquote;
			    outbuf[i++] = (BYTE)tochar(rpt);
		        }
		        outbuf[i++] = krm_sndinit.quote;
		        outbuf[i++] = t7;
		    }			
		    else {
			j -= back;
			break;
		    }
		}
	    }
	    else {
		if (i < (maxlen - extra)) {
		    if (rpt) {
		        outbuf[i++] = krm_sndinit.rpquote;
			outbuf[i++] = (BYTE)tochar(rpt);
		    }
	            outbuf[i++] = t;
		}
		else {
		    j -= back;
		    break;
		}
	    }
	}
	else if (t7 == krm_sndinit.quote) {
	    if (Kermit.ebqflag && b8) {
		if (i < (maxlen - 2 - extra)) {
		    if (rpt) {
		        outbuf[i++] = krm_sndinit.rpquote;
			outbuf[i++] = (BYTE)tochar(rpt);
		    }
		    outbuf[i++] = krm_sndinit.ebquote;
		    outbuf[i++] = krm_sndinit.quote;
		    outbuf[i++] = t7;
		}
		else {
		    j -= back;
		    break;
		}
	    }
	    else {
		if (i < (maxlen - 1 - extra)) {
		    if (rpt) {
		        outbuf[i++] = krm_sndinit.rpquote;
			outbuf[i++] = (BYTE)tochar(rpt);
		    }
		    outbuf[i++] = krm_sndinit.quote;
		    outbuf[i++] = t;
		}
		else {
		    j -= back;
		    break;
		}
	    }
	}	
	else {
	    if (Kermit.ebqflag && b8) {
		if (i < (maxlen - 1 - extra)) {
		    if (rpt) {
		        outbuf[i++] = krm_sndinit.rpquote;
			outbuf[i++] = (BYTE)tochar(rpt);
		    }
		    outbuf[i++] = krm_sndinit.ebquote;
		    outbuf[i++] = t7;
		}
		else {
		    j -= back;
		    break;
		}
	    }
	    else {
		if (i < (maxlen - extra)) {
		    if (rpt) {
		        outbuf[i++] = krm_sndinit.rpquote;
			outbuf[i++] = (BYTE)tochar(rpt);
		    }
		    outbuf[i++] = t;
		}
		else {
		    j -= back;
		    break;
		}
	    }
	}
    }
    outbuf[i] = NUL;
    *inlen -= j;
    return i;
}

/*
 * krm_rcvfile
 *
 * Get a file name from remote Kermit and open
 * it for writing.
 */
BOOL NEAR krm_rcvfil(void)
{
    BYTE buf[KRM_MAXPACKETSIZE + 1];
    int len = krm_rcvpkt.len;
    
    krm_decode(buf, krm_rcvpkt.data, &len);
    if (IsWindow(Kermit.hWndXfer))
        SetDlgItemText(Kermit.hWndXfer, IDD_KRM_FILENAME, buf);

    if ((Kermit.hFile = krmOpenReceiveFile(buf)) != -1) {
        Kermit.bytesmoved = 0;
	Kermit.pFile = strrchr(Kermit.fstruct.szPathName,'\\') + 1;
	if (IsWindow(Kermit.hWndXfer)) {
            SetDlgItemText(Kermit.hWndXfer, IDD_KRM_SAVENAME, Kermit.pFile);
	    SetDlgItemText(Kermit.hWndXfer, IDD_KRM_BYTESMOVED, 
			      ultoa(Kermit.bytesmoved, buf, 10));
	}
	return TRUE;
    }
    return FALSE;
}

/*
 * krm_rcvdata
 *
 * Get a data packet from remote Kermit and save
 * to a file
 */
BOOL NEAR krm_rcvdata(void)
{
    BYTE buf[KRM_MAXPACKETSIZE + 1];
    register int outlen;
    int startlen, inlen;
    register BYTE *ptr;

    for (ptr = krm_rcvpkt.data, startlen = inlen = krm_rcvpkt.len;
	 inlen > 0; ptr += (startlen - inlen), startlen = inlen) {
        outlen = krm_decode(buf, ptr, &inlen);
	if (Kermit.putterm) {
	    if (IsWindow(Kermit.hWndXfer))
		InsertLine(Kermit.hWndXfer, IDD_KRM_REMOTEDATA, buf);
	}
	else if (write(Kermit.hFile, buf, outlen) == -1)
            return FALSE;
        Kermit.bytesmoved += (DWORD)outlen;
    }
    if (IsWindow(Kermit.hWndXfer))
        SetDlgItemText(Kermit.hWndXfer,
		       IDD_KRM_BYTESMOVED,ultoa(Kermit.bytesmoved, buf, 10));
    return TRUE;
}

extern BOOL fOverflow;

static void NEAR InsertLine(HWND hDlg, WORD id, BYTE *buf)
{

    int count;
    int linenum;
    BYTE msgbuf[80];

    count = (int)SendDlgItemMessage(hDlg,id,EM_LINEINDEX, -1, 0L);
    count += (int)SendDlgItemMessage(hDlg,id,EM_LINELENGTH, -1, 0L);
    SendDlgItemMessage(hDlg, id, EM_REPLACESEL, 0, (LONG)(LPSTR)buf);
    if (fOverflow) {
	fOverflow = FALSE;
	SendDlgItemMessage(hDlg, id, WM_SETREDRAW, FALSE, 0L);
	SendDlgItemMessage(hDlg, id, EM_SETSEL, 0, MAKELONG(count, 32767));
	SendDlgItemMessage(hDlg, id, EM_REPLACESEL, 0, (LONG)(LPSTR)"");
	linenum = (int)SendDlgItemMessage(hDlg, id, EM_GETLINECOUNT, 0, 0L);
        count = (int)SendDlgItemMessage(hDlg,id,EM_LINEINDEX,
					3 * (linenum /4), 0L);
	SendDlgItemMessage(hDlg, id, EM_SETSEL, 0, MAKELONG(0, count));
	SendDlgItemMessage(hDlg, id, EM_REPLACESEL, 0, (LONG)(LPSTR)"");
	SendDlgItemMessage(hDlg, id, EM_SETSEL, 0, MAKELONG(32767, 32767));
	SendDlgItemMessage(hDlg, id, WM_SETREDRAW, TRUE, 0L);
        SendDlgItemMessage(hDlg, id, EM_REPLACESEL, 0, (LONG)(LPSTR)buf);
	LoadString(Kermit.hInst, IDS_KRM_WINDOWOVERFLOW, msgbuf, sizeof(msgbuf));
	SetDlgItemText(Kermit.hWndXfer, IDD_KRM_MESSAGE, msgbuf);
    }

/*
    count = (int)SendDlgItemMessage(hDlg, id, EM_LINEINDEX, -1, 0L);
    dbs("count = %d\r\n", count);
    if (count > 20000) {
	linenum = (int)SendDlgItemMessage(hDlg, id, EM_GETLINECOUNT, 0, 0L);
        count = (int)SendDlgItemMessage(hDlg,id,EM_LINEINDEX,linenum /2, 0L);
	SendDlgItemMessage(hDlg, id, EM_SETSEL, 0, MAKELONG(0, count));
	SendDlgItemMessage(hDlg, id, EM_REPLACESEL, 0, (LONG)(LPSTR)"");
	SendDlgItemMessage(hDlg, id, EM_SETSEL, 0, MAKELONG(32767, 32767));
    }
*/
}

/*
 * krm_rclose
 *
 * Close a file under normal or error conditions
 * deleting a partially received file if necessary.
 */
BOOL NEAR krm_rclose(BOOL remove)
{

    if (Kermit.putterm)
	return TRUE;

    if (Kermit.hFile > 0) {
        if (close(Kermit.hFile) == 0) {
	    Kermit.hFile = 0;
	    if (remove && KermParams.DiscardPartialFile && 
		((Kermit.mode == IDM_KRM_RECEIVE) || 
		 (Kermit.mode == IDM_KRM_GET))) {
		    unlink(Kermit.fstruct.szPathName);
	    }
            return TRUE;
	}
     }
     else if (Kermit.hFile == 0)
	return TRUE;

     return FALSE;
}

/*
 * krm_decode
 *
 * Decode control, eight-bit, and repeat character quote characters
 * in a received data packet.
 */
static int NEAR krm_decode(BYTE outbuf[], BYTE inbuf[], int *len)
{
    BYTE a, a7;
    register int i,j;
    BYTE bitset;
    int rpt;

    for (i = 0, j = 0; (j < *len) && (i < KRM_MAXPACKETSIZE); ) {
	rpt = 1;
	a = inbuf[j++];
	if (Kermit.rptflag) {
	    if (a == krm_rcvinit.rpquote) {
		rpt = unchar(inbuf[j++]);
		if (rpt <= (KRM_MAXPACKETSIZE - i))
		    a = inbuf[j++];
		else {	
		    j -= 2;
		    break;
		}
	    }
	}
	bitset = 0;
        if (Kermit.ebqflag) {
	    if (a == krm_rcvinit.ebquote) {
		a = inbuf[j++];
		bitset = 0x80;
	    }
	}
	if (a == krm_rcvinit.quote) {
	    a = inbuf[j++];
	    a7 = a & (BYTE)0x7f;
	    if ((a7 >= '?') && ( a7 <= '_'))
	        a = (BYTE)ctl(a);
	}
	a |= bitset;
	for ( ; rpt > 0; rpt--)
	    outbuf[i++] = a;
    }
    *len -= j;
    outbuf[i] = NUL;
    return i;
}

/*
 * krm_rpack
 *
 * Read and call routines to build an incoming packet.
 */
int NEAR krm_rpack(void)
{
    register int type;

    switch(krm_rcvpkt.state) {
	case PS_START:
	    if (KermParams.Timer)
		SetTimer(Kermit.hWnd, KRM_WAITPACKET, 
			 krm_sndinit.timeout * 1000, Kermit.fpTimer);
	    krm_rcvpkt.state += 1;

	default:
	    if (*krmBuflen)
		*krmBuflen = krm_getpacket(krmBufptr, *krmBuflen);
	    if (krm_rcvpkt.state < PS_DONE) {
		type = '$';
		break;
	    }

	case PS_DONE:
	    krm_rcvpkt.state = PS_START;
	    if (KermParams.Timer)
		KillTimer(Kermit.hWnd, KRM_WAITPACKET);
	    type = krm_rcvpkt.type;

    }
    return type;
}

/*
 * krm_getpacket
 *
 * Attempt to build as much as possible of a complete incoming
 * packet.
 */	    
static int NEAR krm_getpacket(register BYTE *str, register int len)
{
    static BYTE buf[5];
    WORD chk;
    DWORD chk3;

    for ( ; len > 0; len--, str++) {

	switch(krm_rcvpkt.state) {

	    case PS_SYNCH:
		if (*str == krm_rcvinit.mark) {
		    krm_rcvpkt.data_count = 0;
		    krm_rcvpkt.chk_count = 0;
		    krm_rcvpkt.data[0] = 0;
		    krm_rcvpkt.state++;
		}
		break;

	    case PS_LEN:
		krm_rcvpkt.len = unchar(*str) - 2 - Kermit.bctu;
		if (krm_rcvpkt.len > KRM_MAXDATALEN)
		    krm_rcvpkt.len = KRM_MAXDATALEN;
		buf[0] = *str;
		krm_rcvpkt.state++;
		break;

	    case PS_NUM:
		krm_rcvpkt.seq = unchar(*str);
		buf[1] = *str;
		krm_rcvpkt.state++;
		break;

	    case PS_TYPE:
		krm_rcvpkt.type = *str;
		buf[2] = *str;
		buf[3] = 0;
		if (krm_rcvpkt.len)
		    krm_rcvpkt.state++;
		else
		    krm_rcvpkt.state = PS_CHK;
		break;

	    case PS_DATA:
		if (krm_rcvpkt.data_count < krm_rcvpkt.len) {
		    krm_rcvpkt.data[krm_rcvpkt.data_count++] = *str;
		    break;
		}
		else {
		    krm_rcvpkt.data[krm_rcvpkt.data_count] = NUL;
		    krm_rcvpkt.state++;
		    /* fall thru */
		}

	    case PS_CHK:
		if (krm_rcvpkt.chk_count < Kermit.bctu) {
		    krm_rcvpkt.rchksum[krm_rcvpkt.chk_count++] = *str;
		    break;
		}
		switch(Kermit.bctu) {
		    case 1:
		    default:
		        chk = krm_chk1(krm_chksum(krm_rcvpkt.data, 
				       krm_chksum(buf, 0)));
		        if (chk != (WORD)unchar(krm_rcvpkt.rchksum[0]))
			    krm_rcvpkt.type = 'Q';
			break;
		    case 2:
			chk = ((WORD)unchar(krm_rcvpkt.rchksum[0]) << 6) | 
			       (WORD)unchar(krm_rcvpkt.rchksum[1]);
			if (chk != krm_chksum(krm_rcvpkt.data, 
				              krm_chksum(buf,0)))
			    krm_rcvpkt.type = 'Q';
			break;
		    case 3:
			chk3 = ((WORD)unchar(krm_rcvpkt.rchksum[0]) << 12) |
				((WORD)unchar(krm_rcvpkt.rchksum[1]) << 6) |
				(WORD)unchar(krm_rcvpkt.rchksum[2]);
			if (chk3 != krm_chksum3(krm_rcvpkt.data, 
						krm_chksum3(buf, 0)))
			    krm_rcvpkt.type = 'Q';
			break;
		}		
		krm_rcvpkt.state++;
		break;

	    case PS_DONE:
		if (*str == krm_rcvinit.eol)
		    len--;
		return len;
		break;
	}
    }
    return len;
}

/*
 * krm_sendcmd
 *
 * send a generic or host command
 */
int NEAR krm_sendcmd(char cmd, BYTE *cmdstr)
{
    int inlen, outlen;
    BYTE buf[KRM_MAXDATALEN + 1];

    inlen = strlen(cmdstr);
    outlen = krm_encode(buf, cmdstr, Kermit.maxsenddatalen, &inlen);

    return (krm_spack(cmd, Kermit.seq, outlen, buf));
}

/*
 * krm_srinit
 * 
 * send an 'R' packet
 */
int NEAR krm_srinit(BYTE *filename)
{

    int inlen, outlen;
    BYTE buf[KRM_MAXDATALEN + 1];

    inlen = strlen(filename);
    outlen = krm_encode(buf, filename, Kermit.maxsenddatalen, &inlen);

    return (krm_spack('R', Kermit.seq, outlen, buf));

}

/*
 * krm_opent
 *
 * prepare a window for host or generic command
 */
int NEAR krm_opent()
{

    HWND hTemp;

    hTemp = CreateDialog(Kermit.hInst, 
			 MAKEINTRESOURCE(DT_KRM_REMOTE),
			 Kermit.hWnd, Kermit.fpXferRemote);
    if (hTemp) {
        if (IsWindow(Kermit.hWndXfer))
	    DestroyWindow(Kermit.hWndXfer);
	Kermit.hWndXfer = hTemp;
	Kermit.putterm = TRUE;
    }
    return Kermit.putterm;
}

/*
 * krm_tinit
 *
 * Initialize for file transfer
 */
void NEAR krm_tinit(void)
{

    DCB mydcb;
//    FARPROC fp;

  // Get data from KermParams
    Kermit.bctr = KermParams.BlockCheckType;
    Kermit.bctu = 1;

  // Get data from sndparams
    krm_sndinit.mark = sndparams.mark;
    krm_sndinit.maxpktsize = sndparams.maxpktsize;
    krm_sndinit.timeout = sndparams.timeout;
    krm_sndinit.padcount = sndparams.padcount;
    krm_sndinit.padchar = sndparams.padchar;
    krm_sndinit.eol = sndparams.eol;
    krm_sndinit.quote = sndparams.quote;

  // Get data from rcvparams;
    krm_rcvinit.mark = rcvparams.mark;
    krm_rcvinit.maxpktsize = rcvparams.maxpktsize;
    krm_rcvinit.timeout = rcvparams.timeout;
    krm_rcvinit.padcount = rcvparams.padcount;
    krm_rcvinit.padchar = rcvparams.padchar;
    krm_rcvinit.eol = rcvparams.eol;
    krm_rcvinit.quote = rcvparams.quote;

  // Set other values
    krm_sndinit.ebquote = 'N';
    krm_sndinit.rpquote = KermParams.rpquote;

 // Check parity and set eight-bit request
    GetCommState(*krmcid, &mydcb);
    if (mydcb.ByteSize == 8)
	krm_rcvinit.ebquote = 'Y';
    else
	krm_rcvinit.ebquote = KermParams.ebquote;
    krm_rcvinit.rpquote = KermParams.rpquote;
/*
    fp = MakeProcInstance((FARPROC)krmHideChildren, Kermit.hInst);
    EnumChildWindows(Kermit.hWnd, fp, (LONG)SW_HIDE);
    FreeProcInstance(fp);
*/
    if (IsWindow(Kermit.hWndXfer))
	DestroyWindow(Kermit.hWndXfer);

    Kermit.hWndXfer = CreateDialog(Kermit.hInst,
				   MAKEINTRESOURCE(DT_KRM_XFER),
				   Kermit.hWnd, Kermit.fpXfer);

  // If delay before first send packet, then set timer
    if (KermParams.SendDelay) {
	SetTimer(Kermit.hWnd, KRM_WAITSEND, KermParams.SendDelay * 1000, 
			   Kermit.fpTimer);
	Kermit.delay = TRUE;
    }

  // Set more parameters
    krm_rcvpkt.state = PS_START;
    krm_rcvpkt.seq = 0;

    krm_sndpkt[0] = NUL;

    Kermit.seq = 0;
    Kermit.retries = 0;
    Kermit.totalretries = 0;
    Kermit.bytesmoved = 0L;
    Kermit.packetcount = 0L;
    Kermit.filesize = ULONG_MAX;
    Kermit.abort = 0;
    Kermit.InTransfer = TRUE;
    Kermit.hFile = 0;
    Kermit.hFilelist = NULL;
    Kermit.pFile = NULL;
    Kermit.ebqflag = FALSE;
    Kermit.rptflag = FALSE;
    Kermit.maxsenddatalen = krm_sndinit.maxpktsize - 5;   /* worst case */
    Kermit.putterm = FALSE;

    krmSetMenus(TRUE);
    krmFlushQue();

}

/*
 * krmSetMenus
 *
 * Toggle menus according to whether Kermit is starting or ending
 */
static void NEAR krmSetMenus(BOOL mode)
{
    HMENU hMenu = GetMenu(Kermit.hWnd);

    EnableMenuItem(hMenu, IDM_KRM_RECEIVE, mode ? MF_GRAYED : MF_ENABLED);
    EnableMenuItem(hMenu, IDM_KRM_SEND, mode ? MF_GRAYED : MF_ENABLED);
    EnableMenuItem(hMenu, IDM_KRM_GET, mode ? MF_GRAYED : MF_ENABLED);

    EnableMenuItem(hMenu, IDM_KRM_REMOTECOMMAND,mode ? MF_GRAYED : MF_ENABLED);

    EnableMenuItem(hMenu, IDM_KRM_REMOTECWD, mode ? MF_GRAYED : MF_ENABLED);
    EnableMenuItem(hMenu, IDM_KRM_REMOTEDIR, mode ? MF_GRAYED : MF_ENABLED);
    EnableMenuItem(hMenu, IDM_KRM_REMOTEHELP, mode ? MF_GRAYED : MF_ENABLED);
    EnableMenuItem(hMenu, IDM_KRM_REMOTETYPE, mode ? MF_GRAYED : MF_ENABLED);
    EnableMenuItem(hMenu, IDM_KRM_REMOTEERASE, mode ? MF_GRAYED : MF_ENABLED);
    EnableMenuItem(hMenu, IDM_KRM_REMOTESPACE, mode ? MF_GRAYED : MF_ENABLED);
    EnableMenuItem(hMenu, IDM_KRM_REMOTEWHO, mode ? MF_GRAYED : MF_ENABLED);
    EnableMenuItem(hMenu, IDM_KRM_REMOTEFINISH, mode ? MF_GRAYED : MF_ENABLED);
    EnableMenuItem(hMenu, IDM_KRM_REMOTEBYE, mode ? MF_GRAYED : MF_ENABLED);
    EnableMenuItem(hMenu, IDM_KRM_REMOTELOGOUT, mode ? MF_GRAYED : MF_ENABLED);

    EnableMenuItem(hMenu, IDM_KRM_LOCALFILES, mode ? MF_GRAYED : MF_ENABLED);
    
    EnableMenuItem(hMenu, IDM_KRM_PROTOCOL, mode ? MF_GRAYED : MF_ENABLED);
    EnableMenuItem(hMenu, IDM_KRM_PACKETS, mode ? MF_GRAYED : MF_ENABLED);

    EnableMenuItem(hMenu, IDM_KRM_CANCEL, mode ? MF_ENABLED : MF_GRAYED);
    EnableMenuItem(hMenu, IDM_KRM_FILEABORT, mode ? MF_ENABLED : MF_GRAYED);
    EnableMenuItem(hMenu, IDM_KRM_BATCHABORT, mode ? MF_ENABLED : MF_GRAYED);
    EnableMenuItem(hMenu, IDM_KRM_ERRORABORT, mode ? MF_ENABLED : MF_GRAYED);

}

/*
 * krm_tend
 *
 * Terminate Kermit file transfer and clean up
 */
void NEAR krm_tend(int code)
{

//    FARPROC fp;

    KillTimer(Kermit.hWnd, KRM_WAITPACKET);
    KillTimer(Kermit.hWnd, KRM_WAITSEND);

    if (Kermit.hFilelist) {
	LocalUnlock(Kermit.hFilelist);
	LocalFree(Kermit.hFilelist);
    }
    krmSetMenus(FALSE);

    if (KermParams.Bell)
	MessageBeep(code);

    if (IsWindow(Kermit.hWndXfer)) {
	HWND hctl = GetDlgItem(Kermit.hWndXfer, IDCANCEL);
	SetWindowText(hctl, "OK");
        krmShowMessage(code);
    }

    if ((Kermit.mode == IDM_KRM_REMOTEBYE) &&
	(code == IDS_KRM_TRANSACTION_DONE))
	PostMessage(Kermit.hWnd, WM_SYSCOMMAND, SC_CLOSE, 0L);

    Kermit.InTransfer = FALSE;

    if (IsWindow(Kermit.hWnd))
	InvalidateRect(Kermit.hWnd, NULL, TRUE);

}

/*
 * krmShutdown
 *
 * Terminate Kermit and release any used resources.
 * If in the middle of a tranfer, ask if user really
 * wants to shut down.  This function should be called
 * at WM_CLOSE and WM_QUERYENDSESSION in the terminal program.  
 * If it returns anything but IDYES, then the terminal
 * program should not terminate.
 */
int FAR krmShutdown()
{
    char buf[80];
    char appname[20];
    int result = IDYES;

    if (Kermit.InTransfer) {
        LoadString(Kermit.hInst, IDS_KRM_KERMIT, appname, sizeof(appname));
        LoadString(Kermit.hInst, IDS_KRM_QUIT, (LPSTR)buf, sizeof(buf));
        result = MessageBox(GetFocus(),buf,appname,MB_ICONQUESTION | MB_YESNO);

        if (result == IDYES) {
            krm_errpkt(IDS_KRM_CANCELLED);
            krm_rclose(TRUE);

            KillTimer(Kermit.hWnd, KRM_WAITPACKET);
            KillTimer(Kermit.hWnd, KRM_WAITSEND);

        }
    }
    if (result == IDYES) {
        if (IsWindow(Kermit.hWndXfer))
            DestroyWindow(Kermit.hWndXfer);
        FreeProcInstance(Kermit.fpTimer);
        FreeProcInstance(Kermit.fpXferRemote);
        FreeProcInstance(Kermit.fpXfer);
    }
    return result;
}

/*
 * krm_spack
 *
 * Assemble a packet for transmission
 */    
short NEAR krm_spack(char type, int n, int len, BYTE *data)
{
    register int i, j;
    WORD chk;
    DWORD chk3;

    i = 0;

    krm_sndpkt[i++] = krm_sndinit.mark;
    krm_sndpkt[i++] = (BYTE)tochar(len + 2 + Kermit.bctu);
    krm_sndpkt[i++] = (BYTE)tochar(n);
    krm_sndpkt[i++] = type;

    for (j = len; j > 0; j--)
	krm_sndpkt[i++] = *data++;

    krm_sndpkt[i] = NUL;

    switch(Kermit.bctu) {
	case 1:
	default:
	    krm_sndpkt[i++] = 
		(BYTE)tochar(krm_chk1(krm_chksum(krm_sndpkt+1,0)));
	    break;
	case 2:
	    chk = krm_chksum(krm_sndpkt+1,0);
	    krm_sndpkt[i++] = (BYTE)tochar((chk >> 6) & 077);
	    krm_sndpkt[i++] = (BYTE)tochar(chk & 077);
	    break;
	case 3:
	    chk3 = krm_chksum3(krm_sndpkt+1,0L);
	    krm_sndpkt[i++] = (BYTE)tochar((chk3 >> 12) & 017);
	    krm_sndpkt[i++] = (BYTE)tochar((chk3 >> 6) & 077);
	    krm_sndpkt[i++] = (BYTE)tochar(chk3 & 077);
	    break;
    }
    krm_sndpkt[i++] = krm_sndinit.eol;
    krm_sndpkt[i] = NUL;

    return (krmWriteComm(*krmcid, krm_sndpkt, i));
    
}

/*
 * krm_chk1
 *
 * Perform a 1-byte check sum calculation on a value
 */
static WORD NEAR krm_chk1(WORD s)
{

    return (((s & 192) >> 6) + s) & 63;

}

/*
 * krm_chksum
 *
 * Perform a 2-byte check sum calculation on a string.  The
 * initial value of the checksum may be specified.
 */
static WORD NEAR krm_chksum(BYTE *p, WORD init)
{
    WORD s;
 
    for (s = init; *p != NUL; *p++)
	s += *p;

    return (s & 07777);
}

/*
 * krm_chksum3
 *
 * Perform a 3-byte CRC checksum on a string.  The initial
 * value of the checksum may be specified.
 */
static DWORD NEAR krm_chksum3(BYTE *p, DWORD init)
{
    DWORD crc = init;
    WORD c, q;

    while (c = *p++) {
	q = (WORD)((crc ^ c) & 017);
	crc = (crc >> 4) ^ (q * 010201);
	q = (WORD)((crc ^ (c >> 4)) & 017);
	crc = (crc >> 4) ^ (q * 010201);
    }
    return crc;
}

/*
 * krm_ack
 *
 * Send an ack packet, possibly containing data
 */
void NEAR krm_ack(short len, BYTE * str)
{

    krm_spack('Y', Kermit.seq, len, str);
    krm_nxtpkt();
}

/*
 * krm_nxtpkt
 *
 * Bump the packet sequence number
 * and display the information if requested.
 */
static void NEAR krm_nxtpkt(void)
{
    char buf[40];

    Kermit.seq = (Kermit.seq + 1) & 63;
    Kermit.packetcount += 1;
    if (IsWindow(Kermit.hWndXfer))
        SetDlgItemText(Kermit.hWndXfer,
		       IDD_KRM_PACKETS,ultoa(Kermit.packetcount, buf, 10));
    if (IsIconic(Kermit.hWnd))
        InvalidateRect(Kermit.hWnd, NULL, FALSE);
}

/*
 * krm_rpar
 *
 * Prepare our parameters for transmission to
 * the remote Kermit
 */
int NEAR krm_rpar(BYTE data[])
{
    register int i = 0;

    data[i++] = (BYTE)tochar(krm_rcvinit.maxpktsize);
    data[i++] = (BYTE)tochar(krm_rcvinit.timeout);
    data[i++] = (BYTE)tochar(krm_rcvinit.padcount);
    data[i++] = (BYTE)ctl(krm_rcvinit.padchar);
    data[i++] = (BYTE)tochar(krm_rcvinit.eol);
    data[i++] = krm_rcvinit.quote;
    data[i++] = krm_rcvinit.ebquote;
    data[i++] = (BYTE)(Kermit.bctr + '0');
    data[i++] = krm_rcvinit.rpquote;
    data[i] = NUL;

    return i;
}

/*
 * krmSet8BitQuote
 * 
 * Negotiate eight-bit quoting with remote Kermit
 */
static void NEAR krmSet8BitQuote(register BYTE sq)
{

    krm_sndinit.ebquote = sq;

    switch(sq) {
	case 'N':
	    Kermit.ebqflag = FALSE;
	    break;

	case 'Y':
	    if (krm_rcvinit.ebquote == KermParams.ebquote) {
		krm_sndinit.ebquote = krm_rcvinit.ebquote;
		Kermit.ebqflag = TRUE;
	    }
	    break;

	default:
	    if (((sq > 32) && (sq < 63)) || ((sq > 95) && (sq < 127))) {
		krm_rcvinit.ebquote = sq;
		Kermit.ebqflag = TRUE;
	    }
	    else
		Kermit.ebqflag = FALSE;
    }
}

/* 
 * krm_spar
 *
 * Read remote Kermit's parameters and save them to
 * a KRMSENDINIT structure to be used for forming outbound
 * packets
 */
void NEAR krm_spar(BYTE *data, short len)
{
    register int i;
    register int x;

    for (i = 0; i < len; i++) {
	switch(i) {
	    case 0:
		x = unchar(data[i]);
		if ((x < KRM_MINPACKETSIZE) || (x > KRM_MAXPACKETSIZE))
		    x = KRM_DEFPACKETSIZE;
		krm_sndinit.maxpktsize = x;
		break;

	    case 1:
		x = unchar(data[i]);
		if ((x < KRM_MINTIMEOUT) || (x > KRM_MAXTIMEOUT))
		    x = KRM_DEFTIMEOUT;
		krm_sndinit.timeout = x;
		break;

	    case 2:
		x = unchar(data[i]);
		if (x > KRM_MAXPADCOUNT)
		    x = 0;
		krm_sndinit.padcount = x;
		break;

	    case 3:
		x = ctl(data[i]);
		krm_sndinit.padchar = (BYTE)x;
		break;

	    case 4:
		x = unchar(data[i]);
		if ((x < 1) || (x > 31))
		    x = CR;
		krm_sndinit.eol = (BYTE)x;
		break;

	    case 5:
		x = data[i];
		x = ((x > 32 && x < 63) || (x > 95 && x < 127)) ? x : '#';
		krm_sndinit.quote = (BYTE)x;
		break;

	    case 6:
		krmSet8BitQuote(data[i]);
		break;

	    case 7:
		x = data[i] - '0';
		if ((x < 1) || (x > 3))
		    x = KRM_DEFBLOCKCHECK;
		Kermit.bctr = x;
		break;

	    case 8:
		x = data[i];
		if (Kermit.rptflag = ((x > 32 && x < 63) || (x > 95 && x < 127)))
		    krm_rcvinit.rpquote = krm_sndinit.rpquote = (BYTE)x;
		break;

	    default:
		return;
	}
    }
}

/*
 * krmShowMessage
 *
 * Show a message according to a given string ID in the resource file
 * or from a remote Kermit's error packet.
 */
void FAR krmShowMessage(int msgnum)
{

    BYTE buf[KRM_MAXPACKETSIZE + 1];
//    BYTE appname[20];

    int len = krm_rcvpkt.len;

    if (msgnum == KRM_DATA_PACKET)
	krm_decode(buf, krm_rcvpkt.data, &len);
    else
        LoadString(Kermit.hInst, msgnum, (LPSTR)buf, sizeof(buf));

//    LoadString(Kermit.hInst, IDS_KRM_KERMIT, appname, sizeof(appname));
//    MessageBox(GetFocus(), buf, appname, MB_ICONASTERISK | MB_OK);
    SetDlgItemText(Kermit.hWndXfer, IDD_KRM_MESSAGE, buf);

}

/*
 * krmWndCommand
 *
 * Process WM_COMMAND message, returning FALSE if
 * not handled, and TRUE otherwise.
 * This routine should be placed in the terminal's
 * WM_COMMAND processor before any other menu commands.
 * If krmWndCommand returns FALSE, then the terminal
 * should call its own WM_COMMAND routine.
 */
BOOL FAR krmWndCommand(HWND hWnd, WORD mode)
{

    switch(mode) {

	case IDM_KRM_RECEIVE:
	    Kermit.mode = mode;
	    Kermit.start = 'v';
	    wart();
	    break;

	case IDM_KRM_SEND:
	    Kermit.mode = mode;
	    if (krmOpenDlgBox(hWnd, (FARPROC)krmSendFileProc, DT_KRM_SENDFILE)) {
 	        Kermit.start = 's';
	        wart();
	    }
	    break;

	case IDM_KRM_GET:
	    Kermit.mode = mode;
	    if (krmOpenDlgBox(hWnd,(FARPROC)krmRemote1ParamCmd,DT_KRM_GETFILE)) {
		Kermit.start = 'r';
		wart();
	    }
	    break;

	case IDM_KRM_REMOTECOMMAND:
	    Kermit.mode = mode;
	    if (krmOpenDlgBox(hWnd,(FARPROC)krmRemote1ParamCmd,DT_KRM_GETFILE)) {
		Kermit.start = 'c';
		wart();
	    }
	    break;

	case IDM_KRM_REMOTEDIR:
	case IDM_KRM_REMOTETYPE:
	case IDM_KRM_REMOTEHELP:
	case IDM_KRM_REMOTEERASE:
	case IDM_KRM_REMOTESPACE:
	case IDM_KRM_REMOTEWHO:
	    Kermit.mode = mode;
	    if (krmOpenDlgBox(hWnd,(FARPROC)krmRemote1ParamCmd,DT_KRM_GETFILE)) {
		Kermit.start = 'g';
		wart();
	    }
	    break;

	case IDM_KRM_REMOTECWD:
	    Kermit.mode = mode;
	    if (krmOpenDlgBox(hWnd, (FARPROC)krmCWD, DT_KRM_CWD)) {
		Kermit.start = 'g';
		wart();
	    }
	    break;

	case IDM_KRM_REMOTEFINISH:
	case IDM_KRM_REMOTEBYE:
	case IDM_KRM_REMOTELOGOUT:
            Kermit.mode = mode;
	    if (krm_endserver(mode)) {
	        Kermit.start = 'g';
	        wart();
	    }
	    break;

	case IDM_KRM_LOCALFILES:
	    krmOpenDlgBox(hWnd, (FARPROC)krmLocalFiles, DT_KRM_LOCALFILES);
	    break;
	    
	case IDM_KRM_PROTOCOL:
	    krmOpenDlgBox(hWnd, (FARPROC)krmProtocol, DT_KRM_PROTOCOL);
	    break;

	case IDM_KRM_PACKETS:
	    krmOpenDlgBox(hWnd, (FARPROC)krmPackets, DT_KRM_PACKETS);
	    break;

	case IDM_KRM_FILEABORT:
	    Kermit.abort = KRM_FILEABORT;
	    break;

	case IDM_KRM_BATCHABORT:
	    Kermit.abort = KRM_BATCHABORT;
	    break;

	case IDM_KRM_ERRORABORT:
	     krm_errpkt(IDS_KRM_CANCELLED);
/* fall thru */

	case IDM_KRM_CANCEL:
	     krmCreatePseudoPacket('E', PS_DONE, IDS_KRM_CANCELLED);
	     break;

	default:
	    return FALSE;
    }
    return TRUE;
}

/*
 * krm_errpkt
 *
 * Send an error packet to the remote Kermit according
 * to the resource string ID requested.
 */
static void NEAR krm_errpkt(int msgnum)
{
    char szMessage[80];
    BYTE buf[KRM_MAXDATALEN + 1];
    int inlen, outlen;
    
    inlen = LoadString(Kermit.hInst,msgnum,szMessage,sizeof(szMessage));
    outlen = krm_encode(buf, szMessage, Kermit.maxsenddatalen, &inlen);

    krm_spack('E', Kermit.seq, outlen, szMessage);

}

/*
 * krmCreatePseudoPacket
 *
 * Handle an internal error as if an error packet were
 * received from a remote Kermit
 */
void NEAR krmCreatePseudoPacket(char type, short state, int msgnum)
{
    krm_rcvpkt.type = type;
    krm_rcvpkt.state = state;
    krm_rcvpkt.len = LoadString(Kermit.hInst, msgnum,
				krm_rcvpkt.data, sizeof(krm_rcvpkt.data));
}

/*
 * krm_resend
 *
 * If krm_sndpkt is not empty, send it again.
 * Otherwise, send a NAK
 */
void NEAR krm_resend(void)
{
    char buf[40];
    register int len = strlen(krm_sndpkt);

    if (len)
        krmWriteComm(*krmcid, krm_sndpkt, len);
    else
	krm_nak();

    if (IsWindow(Kermit.hWndXfer))
	SetDlgItemText(Kermit.hWndXfer, 
		       IDD_KRM_RETRIES,itoa(Kermit.totalretries, buf, 10));

}

/*
 * krmUpdateXferBox
 *
 * Update a field in the tranfer dialog box
 */
static void NEAR krmUpdateXferBox(WORD id, char *str)
{

    SetDlgItemText(Kermit.hWndXfer, id, str);	

}

/*
 * krm_nak
 *
 * Send a negative acknowledgment packet
 */
static void NEAR krm_nak()
{

    krm_spack('N', Kermit.seq, 0, "");

}

/*
 * krmOpenReceiveFile
 *
 * Open a file for writing and return its handle.
 * If the file exists and file warning is on, then
 * create a new name for it.
 */
static HANDLE NEAR krmOpenReceiveFile(BYTE *inbuf)
{

    register int i, j;
    char filename[13];
    char ext[5];
    int numresult;
    char *ptrresult;
    BOOL gotextension;
    HANDLE hfile;
    char tempname[9], genstring[20];
    unsigned filecount;

    memset(filename, NUL, 13);
    ext[0] = NUL;

    ptrresult = strtok(inbuf, ".");		/* look for any extension */
    numresult = strlen(ptrresult);		/* find out how long */
    numresult = numresult < 8 ? numresult : 8;
    strncpy(filename,ptrresult,numresult);	/* load up name */

    gotextension = FALSE;
    ptrresult = strtok(NULL, ".");		/* get extension */
    if (ptrresult != NULL) {
	strcpy(ext,".");
	strncat(ext, ptrresult,3);
	ext[4] = NUL;
	strcat(filename, ext);
	gotextension = TRUE;
    }

    if (KermParams.FileWarning) {
	for (filecount = 0; filecount < UINT_MAX; filecount++) {
            if ((hfile = OpenFile((LPSTR)filename,
			 (OFSTRUCT FAR *)&Kermit.fstruct,OF_EXIST)) == -1)
	        break;
	    ptrresult = strtok(filename, ".");
	    strcpy(tempname, ptrresult);		
	    numresult = strlen(ptrresult);		
	    for (i = numresult; i < 8; i++)		
	        tempname[i] = '0';
	    numresult = strlen(itoa(filecount + 1, genstring,10));		

	    for (i = 8 - numresult, j = 0; i < 8; i++,j++)
	        tempname[i] = genstring[j];		
	    tempname[8] = NUL;
	    strcpy(filename, tempname);
	    if (gotextension)
	        strcat(filename,ext);
	}
    }
    hfile = OpenFile(filename,(OFSTRUCT FAR *)&Kermit.fstruct,OF_CREATE);
    return (hfile);
}

/*
 * krmWriteComm
 *
 * Write data to comm port.  Add padding if required
 */
static short NEAR krmWriteComm(int cid, BYTE *buf, int len)
{

    if (krm_sndinit.padcount) {
	BYTE padbuf[KRM_MAXPADCOUNT];
	memset(padbuf, krm_sndinit.padchar, krm_sndinit.padcount);
	WriteComm(cid, padbuf, krm_sndinit.padcount);
    }
    return WriteComm(cid, buf, len);
}

/*
 * krmHideChildren
 *
 * Child Windows enumeration call back function
 */
BOOL FAR PASCAL krmHideChildren(HWND hWnd, LONG lParam)
{
    ShowWindow(hWnd, LOWORD(lParam));
    return TRUE;
}

/*
 * krmDoTimeout
 *
 * Timer call back function
 */
void FAR PASCAL krmDoTimeout(HWND hWnd,unsigned message,short event,DWORD time)
{

    switch(event) {
	case KRM_WAITPACKET:
	    if (KermParams.Bell)
		MessageBeep(0);
	    krm_rcvpkt.state = PS_DONE;
	    krm_rcvpkt.type = 'T';
	    break;

	case KRM_WAITSEND:
	    Kermit.delay = FALSE;
	    KillTimer(hWnd, event);
	    break;

	default:
	    KillTimer(hWnd, event);
	    break;
    }
}

/*
 * krm_checkcnx
 *
 * Examine ack data packet for remote cancel command
 */
void NEAR krm_checkcnx()
{

    if (krm_rcvpkt.len > 0) {
	switch(krm_rcvpkt.data[0]) {
	    case 'X':
		Kermit.abort = KRM_FILEABORT;
		break;
	    case 'Z':
		Kermit.abort = KRM_BATCHABORT;
		break;
	    default:
		Kermit.abort = 0;
	}
    }
}

/*
 * krmPaint
 *
 * Draw the lower four bits of the packet count
 * in the icon window if iconic
 */
void FAR krmPaint(HWND hWnd, HDC hDC)
{
    char buf[20];
    RECT rect;

    GetClientRect(hWnd, &rect);
    itoa(LOWORD(Kermit.packetcount), buf, 10),
    DrawText(hDC, buf, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}

/*
 * krm_savename
 *
 * Decode and display the name under which the file
 * is saved on the remote Kermit
 */
void NEAR krm_savename()
{
    BYTE buf[KRM_MAXPACKETSIZE + 1];
    int len = krm_rcvpkt.len;

    if (len > 0) {
	krm_decode(buf, krm_rcvpkt.data, &len);
        if (IsWindow(Kermit.hWndXfer))
            SetDlgItemText(Kermit.hWndXfer, IDD_KRM_SAVENAME, buf);
    }
}

/*
 * krmFlushQue
 *
 * Flush the communications port and the internal buffer
 */
void NEAR krmFlushQue()
{
    FlushComm(*krmcid, 0); /* flush send and rececive comm driver queues */
    FlushComm(*krmcid, 1);
    *krmBuflen = 0;	   /* flush local buffer */
}

static HANDLE NEAR krm_endserver(int mode)
{

    Kermit.hFilelist = LocalAlloc(LPTR, 2);
    if (Kermit.hFilelist) {
	Kermit.pFilelist = LocalLock(Kermit.hFilelist);
	*Kermit.pFilelist = (BYTE)(mode == IDM_KRM_REMOTEFINISH ? 'F' : 'L');
    }
    return Kermit.hFilelist;
}
