home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
TELECOM
/
xyz.lzh
/
ftk.c
< prev
next >
Wrap
Text File
|
1995-08-18
|
79KB
|
2,517 lines
/*
Printed form of this source is Copyright (C) 1995 Coriolis
Group, Inc. All rights reserved. Individual users may
make printed copies for their own personal use.
All other forms are Copyright (C) 1995 Tim Kientzle. All
rights reserved.
Redistribution in source or binary form is permitted only under
the following conditions:
1. If you own a copy of `The Working Programmer's Guide To Serial
Protocols,' then you may redistribute this code as part of
a complete application program under the conditions
described in that book. (See pages xiv, xv.) In any case,
you must abide by terms 4-7 below.
2. Otherwise, if you have received this code as a part of an
application program, it may only be redistributed with the
complete source of that program, under whatever conditions
apply to redistribution of that program as a whole.
3. If you have received this source code by some other means,
you may not redistribute it without explicit written
permission from Tim Kientzle.
4. All advertising materials mentioning features or use of this
software must prominently display the following acknowledgement:
This product is partially based on source code appearing in
`The Working Programmer's Guide to Serial Protocols,'
Copyright (C) 1995 Coriolis Group, Inc. and Tim Kientzle.
5. All programs using this source code must display the above
acknowledgement prominently in the program documentation
and user interface.
6. Neither the name of the Tim Kientzle nor the Coriolis Group, Inc.,
may be used to endorse or promote products derived from this
software without specific prior written permission.
7. Any redistribution in source form must retain the above copyright
notice, this list of conditions, and the disclaimer below.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL TIM KIENTZLE OR THE CORIOLIS GROUP BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define StsRet(expr)do{int tmpErrorVal= (expr); \
if(tmpErrorVal!=kOK)return StsWarn(tmpErrorVal); \
}while(FALSE)
#define StsWarn(s)((pK->debug)?KDebugWarn(pK,(s),__FILE__,__LINE__):(s))
#define debugWarn (1)
#define debugPacket (2)
#define debugPacketErr (4)
#define debugPacketLowLevel (8)
#define debugCache (16)
#define debugAttr (32)
#define debugInit (64)
#define debugEncoding (256)
#define FILE_BUFFER_SIZE (8000)
#define SetControl(b)(pK->classify[b]|= 1)
#define NeedsControl(b)(pK->classify[b]&1)
#define SwapSlots(p1,p2){ \
EXCHANGE e= pK->exchange[(p1)&63]; \
pK->exchange[(p1)&63]= pK->exchange[(p2)&63]; \
pK->exchange[(p2)&63]= e; \
}
#define MALLOC_LIMIT (300000)
#include "ftdisk.h"
#include "ftserial.h"
#include "ftdebug.h"
#include "ftprog.h"
#include "ftk.h"
#include <stddef.h>
size_t strlen (const char *s1);
void *memset (void *, int, size_t);
void *memcpy (void *, const void *, size_t);
void *malloc (size_t);
void free (void *);
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#define NULL ((void *)0)
#endif
#define STATIC
typedef unsigned char BYTE;
typedef struct {
BYTE *myPacket;
BYTE *yourPacket;
unsigned long myPacketLength;
unsigned long yourPacketLength;
long sequence;
char packetCheck;
BYTE myPacketType;
BYTE yourPacketType;
} EXCHANGE;
typedef struct {
int sending;
int userCancel;
DEBUG debug;
SERIAL_PORT port;
DISKFILE f;
long fileSize;
const char **filenames;
int currentFileName;
int numFileNames;
int fileType;
PROGRESS progress;
unsigned long filePosition;
BYTE *pFileBuffer;
int fileBufferSize;
BYTE *pFileThisChar;
BYTE *pFileBufferLimit;
int eofFlag;
int lockingShift;
char classify[256];
unsigned char encode[256];
unsigned char decode[256];
int repeatStart;
int repeatEnd;
int num8bit;
BYTE repeatByte[6];
int repeatCount[6];
int lockingFlip;
int quoteNext;
char packetCheck;
EXCHANGE exchange[64];
BYTE *txPacket;
unsigned long txPacketLength;
BYTE *rxPacket;
unsigned long rxPacketLength;
int rxPacketSequence;
BYTE rxPacketType;
int currentWindowSize;
int windowSize;
int minCache;
int maxUsed;
int minUsed;
int sequence;
int retries;
BYTE *rawBuffer;
long rawBufferLength;
long rawBufferPacketSize;
EXCHANGE spareExchange;
int serverMode;
struct {
BYTE prefixControl;
BYTE prefix8bit;
BYTE prefixRepeat;
char preferredPacketCheck;
BYTE sopCharacter;
long maxPacketSize;
int timeout;
int padCount;
BYTE padByte;
BYTE packetTerminator;
BYTE capabilities[5];
} my , your;
} KERMIT_PRIVATE;
enum {
kOK = 0,
kFail,
kFailed,
kBadPacket,
kEndOfSession,
kEOF,
kTimeout
};
STATIC int KDebugWarn
(KERMIT_PRIVATE *pK, const int s, const char *file, const int line) {
const char *msg = NULL;
if (s != kOK) {
DebugBeginInternal (pK->debug, debugWarn, file, line);
DebugString (pK->debug, "?!?!?!:");
}
switch (s) {
case kOK:
return kOK;
case kFail:
msg = "kFail";
break;
case kFailed:
msg = "kFailed";
break;
case kBadPacket:
msg = "kBadPacket";
break;
case kEndOfSession:
msg = "kEndOfSession";
break;
case kEOF:
msg = "kEOF";
break;
case kTimeout:
msg = "kTimeout";
break;
}
if (msg != NULL)
DebugString (pK->debug, msg);
else {
DebugString (pK->debug, "Error ");
DebugInt (pK->debug, s);
}
DebugEnd (pK->debug);
return s;
}
STATIC unsigned short int kCrc16Table[256];
STATIC void KInitCrc
(void) {
static int crcDone = 0;
unsigned long i, j, crc;
if (crcDone)
return;
for (i = 0; i < 256; i++) {
crc = i;
for (j = 0; j < 8; j++)
crc = (crc >> 1) ^ ((crc & 1) ? 0x8408 : 0);
kCrc16Table[i] = crc & 0xffff;
}
crcDone = 1;
}
STATIC unsigned short KCrc16
(const BYTE *buff, unsigned int length,
unsigned short crc) {
const BYTE *p = buff;
while (length-- > 0)
crc = kCrc16Table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
return crc & 0xffff;
}
STATIC int KReadBytesWithTimeout
(KERMIT_PRIVATE *pK, int timeout,
BYTE *pBuffer, unsigned long *pLength) {
int s, returnVal = kOK;
s = SerialReadWithTimeout (pK->port, timeout, pBuffer, pLength);
switch (s) {
case serialOK:
returnVal = kOK;
break;
case serialTimeout:
returnVal = kTimeout;
break;
case serialUserCancel:
pK->userCancel = TRUE;
returnVal = kFail;
break;
case serialFrame:
returnVal = kBadPacket;
break;
default:
return StsWarn (kFailed);
}
if (pK->userCancel)
return StsWarn (kFail);
return returnVal;
}
STATIC int KSendBytes
(KERMIT_PRIVATE *pK, const BYTE *pBuffer,
unsigned int length) {
int s, returnVal;
if (length == 0)
return kOK;
s = SerialSend (pK->port, pBuffer, length);
switch (s) {
case serialOK:
returnVal = kOK;
break;
case serialUserCancel:
pK->userCancel = TRUE;
returnVal = kFail;
break;
default:
return StsWarn (kFailed);
}
if (pK->userCancel)
return StsWarn (kFail);
return StsWarn (returnVal);
}
STATIC int KWaitForSentBytes
(KERMIT_PRIVATE *pK) {
int s, returnVal;
if (pK->userCancel)
return StsWarn (kFail);
s = SerialWaitForSentBytes (pK->port);
switch (s) {
case serialOK:
returnVal = kOK;
break;
case serialUserCancel:
pK->userCancel = TRUE;
returnVal = StsWarn (kFail);
break;
default:
return StsWarn (kFailed);
}
if (pK->userCancel)
return StsWarn (kFail);
return StsWarn (returnVal);
}
STATIC int KSendByte
(KERMIT_PRIVATE *pK, BYTE b) {
return StsWarn (KSendBytes (pK, &b, 1));
}
STATIC int KReadByteWithTimeout
(KERMIT_PRIVATE *pK, int timeout, BYTE *pByte) {
unsigned long count = 1;
return KReadBytesWithTimeout (pK, timeout, pByte, &count);
}
STATIC int KFileReadOpenNext
(KERMIT_PRIVATE *pK, int fileType) {
while (1) {
if (pK->currentFileName == pK->numFileNames)
return kEndOfSession;
switch (DiskReadOpen (&pK->f, pK->filenames[pK->currentFileName++],
fileType)) {
case diskOK:
DiskFileSize (pK->f, &(pK->fileSize));
return kOK;
case diskCantRead:
case diskNoSuchFile:
break;
default:
return StsWarn (kFail);
}
}
}
STATIC int KFileRead
(KERMIT_PRIVATE *pK, BYTE *pBuffer,
unsigned long *pLength) {
int returnVal;
switch (DiskRead (pK->f, pBuffer, *pLength, pLength)) {
case diskOK:
returnVal = kOK;
break;
case diskEOF:
returnVal = kEOF;
break;
default:
returnVal = StsWarn (kFail);
break;
}
return returnVal;
}
STATIC int KFileReadClose
(KERMIT_PRIVATE *pK) {
int returnVal;
switch (DiskReadClose (pK->f)) {
case diskOK:
returnVal = kOK;
break;
default:
returnVal = StsWarn (kFail);
break;
}
pK->f = NULL;
return returnVal;
}
STATIC int KFileWriteOpen
(KERMIT_PRIVATE *pK,
const char *fileName, int length, int fileType) {
((void) length);
pK->fileSize = -1;
if (DiskWriteInit (&pK->f, pK->debug))
return StsWarn (kFail);
if (DiskWriteName (pK->f, fileName))
return StsWarn (kFail);
if (DiskWriteType (pK->f, fileType))
return StsWarn (kFail);
if (DiskWriteOpen (pK->f))
return StsWarn (kFail);
return kOK;
}
STATIC int KFileWrite
(KERMIT_PRIVATE *pK, const BYTE *pBuffer,
unsigned long length) {
switch (DiskWrite (pK->f, pBuffer, length)) {
case diskOK:
return kOK;
default:
return StsWarn (kFail);
}
}
STATIC int KFileWriteClose
(KERMIT_PRIVATE *pK) {
int returnVal;
switch (DiskWriteClose (pK->f)) {
case diskOK:
returnVal = kOK;
break;
default:
returnVal = StsWarn (kFail);
break;
}
pK->f = NULL;
return returnVal;
}
enum {
stsNegotiating = progNegotiating,
stsNewFile = progNewFile,
stsSending = progSending,
stsReceiving = progReceiving,
stsEnding = progEnding,
stsEOF = progEOF,
stsDone = progDone,
stsSkipped = progSkipped,
stsFailed = progFailed,
stsCancelled = progCancelled
};
STATIC void KProgress
(KERMIT_PRIVATE *pK, int status) {
if (pK->f) {
const char *fileName = NULL;
int fileType = diskFileUnknown;
DiskFileName (pK->f, &fileName);
ProgressFileName (pK->progress, fileName);
DiskFileType (pK->f, &fileType);
ProgressFileType (pK->progress, fileType);
} else {
ProgressFileName (pK->progress, NULL);
}
ProgressFileSize (pK->progress, pK->fileSize);
ProgressFilePosition (pK->progress, pK->filePosition);
ProgressReport (pK->progress, status);
}
STATIC int KFileReadEncode
(KERMIT_PRIVATE *pK, BYTE *pBuffer,
unsigned long *pLength, unsigned long *pRawLength) {
int err;
long length = (*pLength) -= 4;
BYTE b;
int repeatCount = 1;
BYTE *pDiskBuffer = pK->pFileThisChar;
int eofFlag = pK->eofFlag;
int lockingFlip = pK->lockingFlip;
int quoteNext = pK->quoteNext;
if ((eofFlag > 1) && (pK->repeatStart == pK->repeatEnd))
return StsWarn (kEOF);
*pRawLength = 0;
DebugBegin (pK->debug, debugEncoding);
DebugString (pK->debug, "Beginning to encode for packet length ");
DebugInt (pK->debug, length);
DebugEnd (pK->debug);
while (length > 0) {
while ((pK->repeatEnd != pK->repeatStart) && (eofFlag < 2)) {
if (pDiskBuffer >= pK->pFileBufferLimit) {
if (eofFlag == 0) {
unsigned long bufferSize = pK->fileBufferSize - 1;
err = KFileRead (pK, pK->pFileBuffer, &bufferSize);
pDiskBuffer = pK->pFileBuffer;
if (bufferSize > 0)
pDiskBuffer[bufferSize] = ~pDiskBuffer[bufferSize - 1];
pK->pFileBufferLimit = pDiskBuffer + bufferSize;
pK->pFileThisChar = pDiskBuffer;
if (err == kEOF)
eofFlag = 1;
else
StsRet (err);
} else
eofFlag++;
if (eofFlag)
break;
}
b = *pDiskBuffer;
pK->repeatByte[pK->repeatEnd] = b;
if (b ^ lockingFlip)
pK->num8bit++;
repeatCount = 1;
if ((pDiskBuffer[1] == b) && (pK->my.prefixRepeat)) {
while (pDiskBuffer[repeatCount] == b)
repeatCount++;
if (repeatCount > 94)
repeatCount = 94;
if ((repeatCount == 2) && (!NeedsControl (b)))
repeatCount = 1;
}
pDiskBuffer += repeatCount;
pK->repeatCount[pK->repeatEnd] = repeatCount;
pK->repeatEnd++;
if (pK->repeatEnd >= (sizeof (pK->repeatByte) / sizeof (pK->repeatByte[0])))
pK->repeatEnd = 0;
}
if ((eofFlag > 1) && (pK->repeatStart == pK->repeatEnd))
break;
b = pK->repeatByte[pK->repeatStart];
repeatCount = pK->repeatCount[pK->repeatStart];
if (pK->lockingShift) {
b ^= lockingFlip;
if (quoteNext) {
quoteNext = FALSE;
pK->repeatStart++;
if (b ^ lockingFlip)
pK->num8bit--;
(*pRawLength) += repeatCount;
} else {
switch (b) {
case 14:
case 15:
case 16:
quoteNext = TRUE;
b = 16;
repeatCount = 1;
break;
default:
if ((pK->num8bit > 4) && (b & 0x80)) {
pK->num8bit = 6 - pK->num8bit;
lockingFlip ^= 0x80;
b = (lockingFlip) ? 14 : 15;
repeatCount = 1;
} else {
pK->repeatStart++;
if (b ^ lockingFlip)
pK->num8bit--;
(*pRawLength) += repeatCount;
}
break;
}
}
} else {
pK->repeatStart++;
if (b ^ lockingFlip)
pK->num8bit--;
(*pRawLength) += repeatCount;
}
if (pK->repeatStart >= (sizeof (pK->repeatByte) / sizeof (pK->repeatByte[0])))
pK->repeatStart = 0;
if (repeatCount > 0) {
if (repeatCount > 1) {
*pBuffer++ = pK->my.prefixRepeat;
*pBuffer++ = repeatCount + 32;
length -= 2;
}
if ((b & 0x80) && (pK->my.prefix8bit)) {
*pBuffer++ = pK->my.prefix8bit;
length--;
b &= 0x7f;
}
if (NeedsControl (b)) {
*pBuffer++ = pK->my.prefixControl;
length--;
b = pK->encode[b];
}
*pBuffer++ = b;
length--;
}
}
DebugBegin (pK->debug, debugEncoding);
DebugString (pK->debug, "Done encoding packet");
DebugInt (pK->debug, length);
DebugEnd (pK->debug);
pK->pFileThisChar = pDiskBuffer;
*pLength -= length;
pK->lockingFlip = lockingFlip;
pK->quoteNext = quoteNext;
pK->eofFlag = eofFlag;
return kOK;
}
STATIC int KFileWriteDecode
(KERMIT_PRIVATE *pK, const BYTE *pBuffer,
unsigned long length) {
BYTE buff[500];
unsigned buffLength = 0;
int mask = 0xff;
int flip = 0;
int repeatCount = 0;
int lockingFlip = pK->lockingFlip & 0x80;
int quoteNext = pK->quoteNext;
BYTE b = 0;
if (pK->your.prefix8bit)
mask = 0x7f;
while (length > 0) {
b = ((unsigned) (*pBuffer++)) & mask;
length--;
repeatCount = 1;
flip = 0;
if ((pK->your.prefixRepeat) && (b == pK->your.prefixRepeat)) {
repeatCount = (((unsigned) (*pBuffer++)) & 0x7f) - 32;
b = ((unsigned) (*pBuffer++)) & mask;
length -= 2;
}
if ((pK->your.prefix8bit) && (b == pK->your.prefix8bit)) {
flip ^= 0x80;
b = ((unsigned) (*pBuffer++)) & mask;
length--;
}
if (b == pK->your.prefixControl) {
b = pK->decode[((unsigned) (*pBuffer++)) & mask];
length--;
}
b ^= flip;
if (pK->lockingShift) {
if (!quoteNext) {
switch (b) {
case 14:
lockingFlip = 0x80;
repeatCount = 0;
break;
case 15:
lockingFlip = 0;
repeatCount = 0;
break;
case 16:
quoteNext = TRUE;
repeatCount = 0;
break;
}
} else
quoteNext = FALSE;
}
b ^= lockingFlip;
while (repeatCount > 0) {
buff[buffLength++] = b;
repeatCount--;
pK->filePosition++;
if (buffLength >= sizeof (buff)) {
StsRet (KFileWrite (pK, buff, buffLength));
buffLength = 0;
}
}
}
StsRet (KFileWrite (pK, buff, buffLength));
pK->lockingFlip = lockingFlip;
pK->quoteNext = quoteNext;
return kOK;
}
STATIC void KInitEncoding
(KERMIT_PRIVATE *pK) {
int i;
for (i = 0; i < 256; i++)
pK->classify[i] = 0;
for (i = 0; i < 32; i++) {
SetControl (i);
SetControl (i | 0x80);
}
SetControl (0x7f);
SetControl (0xff);
SetControl (pK->my.prefixControl);
if (pK->my.prefix8bit)
SetControl (pK->my.prefix8bit);
if (pK->my.prefixRepeat)
SetControl (pK->my.prefixRepeat);
for (i = 0; i < 256; i++)
pK->encode[i] = pK->decode[i] = i;
for (i = 0; i < 32; i++)
pK->encode[i] = i ^ 0x40;
pK->encode[0x7f] = 0x7f ^ 0x40;
for (i = 128; i < 160; i++)
pK->encode[i] = i ^ 0x40;
pK->encode[0xff] = 0xff ^ 0x40;
for (i = 0; i < 32; i++)
pK->decode[((unsigned) (pK->encode[i])) & 0xff] = i;
pK->decode[((unsigned) (pK->encode[0x7f])) & 0xff] = 0x7f;
for (i = 128; i < 160; i++)
pK->decode[((unsigned) (pK->encode[i])) & 0xff] = i;
pK->decode[((unsigned) (pK->encode[0xff])) & 0xff] = 0xff;
}
STATIC unsigned KCheckValue
(KERMIT_PRIVATE *pK,
char packetCheck, const BYTE *pData, unsigned int length,
int *pCheckValue) {
int checkAccumulator;
int finalCheck = 0;
int i;
if (pCheckValue)
checkAccumulator = *pCheckValue;
else
checkAccumulator = 0;
switch (packetCheck) {
case '1':
for (i = 0; i < length; i++)
checkAccumulator += pData[i];
finalCheck = checkAccumulator;
finalCheck += (finalCheck >> 6) & 3;
finalCheck &= 63;
break;
case 'B':
case '2':
for (i = 0; i < length; i++)
checkAccumulator += pData[i];
finalCheck = checkAccumulator & 0xfff;
break;
case '3':
checkAccumulator = KCrc16 (pData, length, checkAccumulator);
finalCheck = checkAccumulator;
break;
default:
if (pK->debug) {
DebugBegin (pK->debug, debugWarn);
DebugString (pK->debug, "Internal error: illegal packet check ");
DebugChar (pK->debug, packetCheck);
DebugInt (pK->debug, packetCheck);
DebugEnd (pK->debug);
}
break;
}
if (pCheckValue)
*pCheckValue = checkAccumulator;
return finalCheck;
}
STATIC int KSendPacket
(KERMIT_PRIVATE *pK, int sequence, BYTE type,
const BYTE *pData, unsigned long dataLength) {
int checkAccumulator = 0;
int checkValue;
int packetCheckLength = pK->packetCheck - '0';
if (pK->packetCheck == 'B')
packetCheckLength = 2;
{
int i;
for (i = 0; i < pK->my.padCount; i++) {
StsRet (KSendByte (pK, pK->my.padByte));
}
}
if (dataLength + packetCheckLength > 9024) {
BYTE header[8];
header[0] = pK->my.sopCharacter;
if (header[0] == 0xff)
header[0] = 0x01;
header[1] = 1 + 32;
header[2] = (sequence % 64) + 32;
header[3] = type;
header[4] = (dataLength + packetCheckLength) / 9025 + 32;
header[5] = ((dataLength + packetCheckLength) / 95) % 95 + 32;
header[6] = (dataLength + packetCheckLength) % 95 + 32;
header[7] = KCheckValue (pK, '1', header + 1, 6, NULL) + 32;
StsRet (KSendBytes (pK, header, 8));
KCheckValue (pK, pK->packetCheck, header + 1, 6, &checkAccumulator);
} else if (dataLength + packetCheckLength + 2 > 95) {
BYTE header[7];
header[0] = pK->my.sopCharacter;
if (header[0] == 0xff)
header[0] = 0x01;
header[1] = ' ';
header[2] = (sequence % 64) + 32;
header[3] = type;
header[4] = (dataLength + packetCheckLength) / 95 + 32;
header[5] = (dataLength + packetCheckLength) % 95 + 32;
header[6] = KCheckValue (pK, '1', header + 1, 5, NULL) + 32;
StsRet (KSendBytes (pK, header, 7));
KCheckValue (pK, pK->packetCheck, header + 1, 6, &checkAccumulator);
} else {
BYTE header[4];
header[0] = pK->my.sopCharacter;
if (header[0] == 0xff)
header[0] = 0x01;
header[1] = dataLength + 2 + packetCheckLength + 32;
header[2] = (sequence % 64) + 32;
header[3] = type;
StsRet (KSendBytes (pK, header, 4));
KCheckValue (pK, pK->packetCheck, header + 1, 3, &checkAccumulator);
}
StsRet (KSendBytes (pK, pData, dataLength));
checkValue = KCheckValue (pK, pK->packetCheck, pData, dataLength, &checkAccumulator);
switch (pK->packetCheck) {
case '3':
StsRet (KSendByte (pK, ((checkValue >> 12) & 15) + 32));
case '2':
StsRet (KSendByte (pK, ((checkValue >> 6) & 63) + 32));
case '1':
StsRet (KSendByte (pK, (checkValue & 63) + 32));
break;
case 'B':
StsRet (KSendByte (pK, ((checkValue >> 6) & 63) + 33));
StsRet (KSendByte (pK, (checkValue & 63) + 33));
}
StsRet (KSendByte (pK, pK->my.packetTerminator));
if (pK->debug) {
DebugBegin (pK->debug, debugPacket);
DebugString (pK->debug, "Sent #");
DebugInt (pK->debug, sequence);
DebugString (pK->debug, " type:");
DebugChar (pK->debug, type);
DebugString (pK->debug, " length:");
DebugUInt (pK->debug, dataLength);
DebugString (pK->debug, " check:");
DebugInt (pK->debug, checkValue);
DebugString (pK->debug, " ``");
if (dataLength < 40) {
DebugStringCount (pK->debug, (const char *) pData, dataLength);
DebugString (pK->debug, "''");
} else {
DebugStringCount (pK->debug, (const char *) pData, 37);
DebugString (pK->debug, "...");
}
DebugEnd (pK->debug);
}
StsRet (KWaitForSentBytes (pK));
return kOK;
}
STATIC unsigned KFindCheckValue
(KERMIT_PRIVATE *pK, BYTE *buffer,
long *pCheckValue, char packetCheck) {
BYTE *check;
int i;
long checkValue = 0;
int checkLength = packetCheck - '0';
if (packetCheck == 'B')
checkLength = 2;
check = buffer - checkLength;
for (i = 0; i < checkLength; i++) {
int thisValue = (check[i] & 0x7f) - 32;
if (packetCheck == 'B')
thisValue -= 1;
if ((thisValue > 63) || (thisValue < 0)) {
if (pK->debug) {
DebugBegin (pK->debug, debugPacketErr);
DebugString (pK->debug, "Received illegal check character ");
DebugInt (pK->debug, check[i]);
DebugChar (pK->debug, check[i]);
DebugEnd (pK->debug);
}
return StsWarn (kBadPacket);
}
checkValue = (checkValue << 6) + thisValue;
}
*pCheckValue = checkValue;
return kOK;
}
STATIC int KReceivePacket
(KERMIT_PRIVATE *pK, int timeout,
BYTE *pBuffer, unsigned long *pDataLength,
BYTE *pType, int *pSequence) {
BYTE header[10];
unsigned long headerLength = 3;
int length, checkAccumulator = 0;
unsigned long rxDataLength;
{
BYTE mark;
BYTE sopCharacter = pK->your.sopCharacter;
int err;
int i = 0;
for (i = 0; i < 4; i++)
header[i] = 0xff;
do {
err = KReadByteWithTimeout (pK, timeout, &mark);
switch (err) {
case kOK:
mark &= 0x7f;
header[0] = header[1];
header[1] = header[2];
header[2] = header[3];
header[3] = mark;
if ((header[0] < 32) && (pK->your.sopCharacter == 0xff))
sopCharacter = header[0];
break;
case kBadPacket:
header[3] = 0x80;
break;
default:
return StsWarn (err);
}
} while ((header[0] != sopCharacter)
|| (header[1] < 32) || (header[1] > 126)
|| (header[2] < 32) || (header[2] > 95)
|| (header[3] < 'A') || (header[3] > 'Z')
|| (err != kOK));
}
length = header[1] - 32;
*pSequence = header[2] - 32;
*pType = header[3];
switch (length) {
case 0:
case 1:
{
headerLength = 3 + length;
StsRet (KReadBytesWithTimeout (pK, 3, header + 4, &headerLength));
headerLength += 4;
{
int i = 4;
for (; i < headerLength; i++)
header[i] &= 0x7f;
}
{
int headerCheck = KCheckValue (pK, '1', header + 1, headerLength - 2, NULL);
if (headerCheck != header[headerLength - 1] - 32) {
if (pK->debug) {
DebugBegin (pK->debug, debugPacketErr);
DebugString (pK->debug, "Wrong header check: received: ");
DebugInt (pK->debug, header[headerLength - 1] - 32);
DebugString (pK->debug, " computed: ");
DebugInt (pK->debug, headerCheck);
DebugEnd (pK->debug);
}
return kBadPacket;
}
}
checkAccumulator = 0;
KCheckValue (pK, pK->packetCheck, header + 1, headerLength - 1, &checkAccumulator);
rxDataLength = (header[4] - 32) * 95 + (header[5] - 32);
if (length == 1)
rxDataLength = rxDataLength * 95 + (header[6] - 32);
if (pK->debug) {
DebugBegin (pK->debug, debugPacketLowLevel);
DebugString (pK->debug, "Receiving extended packet, length: ");
DebugUInt (pK->debug, rxDataLength);
DebugEnd (pK->debug);
}
}
break;
case 2:
return StsWarn (kBadPacket);
default:
{
if (length < 2)
return StsWarn (kBadPacket);
rxDataLength = length - 2;
if (pK->debug) {
DebugBegin (pK->debug, debugPacketLowLevel);
DebugString (pK->debug, "Receiving normal packet #");
DebugInt (pK->debug, *pSequence);
DebugString (pK->debug, " data length: ");
DebugUInt (pK->debug, rxDataLength);
DebugString (pK->debug, " type: ");
DebugChar (pK->debug, *pType);
DebugEnd (pK->debug);
}
checkAccumulator = 0;
KCheckValue (pK, pK->packetCheck, header + 1, 3, &checkAccumulator);
}
break;
}
{
long myCheck, rxCheck;
unsigned checkLength = pK->packetCheck - '0';
if (pK->packetCheck == 'B')
checkLength = 2;
StsRet (KReadBytesWithTimeout (pK, 3, pBuffer, &rxDataLength));
if (rxDataLength < checkLength)
return StsWarn (kBadPacket);
myCheck = KCheckValue (pK, pK->packetCheck, pBuffer,
rxDataLength - checkLength, &checkAccumulator);
StsRet (KFindCheckValue (pK, pBuffer + rxDataLength, &rxCheck,
pK->packetCheck));
if (myCheck != rxCheck) {
if (pK->debug) {
DebugBegin (pK->debug, debugPacketErr);
DebugString (pK->debug, "Wrong packet check: received: ");
DebugInt (pK->debug, rxCheck);
DebugString (pK->debug, " computed: ");
DebugInt (pK->debug, myCheck);
DebugEnd (pK->debug);
}
return StsWarn (kBadPacket);
}
rxDataLength -= checkLength;
}
*pDataLength = rxDataLength;
if (pK->debug) {
DebugBegin (pK->debug, debugPacket);
DebugString (pK->debug, "Rcvd #");
DebugInt (pK->debug, *pSequence);
DebugString (pK->debug, " Type:");
DebugChar (pK->debug, *pType);
DebugString (pK->debug, " length:");
DebugUInt (pK->debug, *pDataLength);
DebugString (pK->debug, " ``");
if (*pDataLength < 40) {
DebugStringCount (pK->debug, (char *) pBuffer, *pDataLength);
DebugString (pK->debug, "''");
} else {
DebugStringCount (pK->debug, (char *) pBuffer, 37);
DebugString (pK->debug, "...");
}
DebugEnd (pK->debug);
}
if (pK->your.sopCharacter == 0xff)
pK->your.sopCharacter = header[0];
if (pK->my.sopCharacter == 0xff)
pK->my.sopCharacter = header[0];
return kOK;
}
STATIC void KDumpCache
(KERMIT_PRIVATE *pK) {
int i;
char c[2];
c[0] = 0;
c[1] = 0;
DebugBegin (pK->debug, debugCache);
DebugString (pK->debug, " minCache = ");
DebugInt (pK->debug, pK->minCache);
DebugString (pK->debug, " minUsed = ");
DebugInt (pK->debug, pK->minUsed);
DebugString (pK->debug, " sequence = ");
DebugInt (pK->debug, pK->sequence);
DebugString (pK->debug, " maxUsed = ");
DebugInt (pK->debug, pK->maxUsed);
DebugEnd (pK->debug);
DebugBegin (pK->debug, debugCache);
DebugString (pK->debug, " ");
for (i = 0; i < 64; i++) {
c[0] = (i % 10) + '0';
if ((pK->maxUsed & 63) == i)
DebugString (pK->debug, "M");
else if ((pK->minCache & 63) == i)
DebugString (pK->debug, "m");
else if ((pK->minUsed & 63) == i)
DebugString (pK->debug, "U");
else if ((pK->sequence & 63) == i)
DebugString (pK->debug, "S");
else if ((i % 5) == 0)
DebugString (pK->debug, c);
else
DebugString (pK->debug, ".");
}
DebugEnd (pK->debug);
DebugBegin (pK->debug, debugCache);
DebugString (pK->debug, "Tx:");
for (i = 0; i < 64; i++) {
c[0] = ' ';
if (pK->exchange[i].myPacketType != 0) {
c[0] = pK->exchange[i].myPacketType;
if (pK->exchange[i].myPacket == NULL)
c[0] = c[0] + 32;
} else if (pK->exchange[i].myPacket)
c[0] = '.';
DebugString (pK->debug, c);
}
DebugString (pK->debug, ":");
DebugEnd (pK->debug);
DebugBegin (pK->debug, debugCache);
DebugString (pK->debug, "Rx:");
for (i = 0; i < 64; i++) {
c[0] = ' ';
if (pK->exchange[i].yourPacketType != 0) {
c[0] = pK->exchange[i].yourPacketType;
if (pK->exchange[i].yourPacket == NULL)
c[0] = c[0] + 32;
} else if (pK->exchange[i].yourPacket)
c[0] = '.';
DebugString (pK->debug, c);
}
DebugString (pK->debug, ":");
DebugEnd (pK->debug);
{
BYTE *pThisBuffer = pK->rawBuffer;
int buffersRemaining = pK->rawBufferLength / pK->rawBufferPacketSize;
while (buffersRemaining > 0) {
int j, count = 0;
if (pThisBuffer == pK->spareExchange.myPacket)
count++;
if (pThisBuffer == pK->spareExchange.yourPacket)
count++;
for (j = 0; j < 64; j++) {
if (pThisBuffer == pK->exchange[j].myPacket)
count++;
if (pThisBuffer == pK->exchange[j].yourPacket)
count++;
}
if (count == 0) {
DebugBegin (pK->debug, debugWarn);
DebugString (pK->debug, "Cache buffer lost: address ");
DebugPtr (pK->debug, pThisBuffer);
if (pThisBuffer == pK->rxPacket)
DebugString (pK->debug, " == pK->rxPacket");
DebugEnd (pK->debug);
} else if (count > 1) {
DebugBegin (pK->debug, debugWarn);
DebugString (pK->debug, "Cache buffer duplicated: address ");
DebugPtr (pK->debug, pThisBuffer);
DebugEnd (pK->debug);
}
pThisBuffer += pK->rawBufferPacketSize;
buffersRemaining--;
}
}
}
STATIC void KResetSequence
(KERMIT_PRIVATE *pK) {
int low = 0, high = 63;
if (pK->debug) {
KDumpCache (pK);
DebugBegin (pK->debug, debugCache);
DebugString (pK->debug, "Resetting cache...");
DebugEnd (pK->debug);
}
while (low < high) {
while (pK->exchange[low].myPacket != NULL)
low++;
while (pK->exchange[high].myPacket == NULL)
high--;
if (low < high)
SwapSlots (low, high);
}
pK->maxUsed = -1;
pK->minCache = 0;
pK->minUsed = 0;
pK->sequence = 0;
KDumpCache (pK);
}
STATIC int KInitWindow
(KERMIT_PRIVATE *pK) {
unsigned long packetLimit;
unsigned long packetCount = pK->windowSize + 1;
if (pK->rawBuffer) {
int i;
free (pK->rawBuffer);
pK->txPacket = NULL;
pK->rxPacket = NULL;
pK->spareExchange.myPacket = NULL;
pK->spareExchange.myPacketLength = 0;
pK->spareExchange.myPacketType = 0;
pK->spareExchange.yourPacket = NULL;
pK->spareExchange.yourPacketLength = 0;
pK->spareExchange.yourPacketType = 0;
for (i = 0; i < 64; i++)
pK->exchange[i] = pK->spareExchange;
}
packetLimit = pK->my.maxPacketSize;
if (packetLimit < pK->your.maxPacketSize)
packetLimit = pK->your.maxPacketSize;
packetLimit += 100;
if ((packetLimit * packetCount) < MALLOC_LIMIT)
pK->rawBuffer = malloc (packetLimit * packetCount);
while (pK->rawBuffer == NULL) {
if (packetLimit > 200)
packetLimit = (packetLimit * 2) / 3;
else if (packetCount > 2)
packetCount--;
else
return StsWarn (kFailed);
if ((packetLimit * packetCount) < MALLOC_LIMIT)
pK->rawBuffer = malloc (packetLimit * packetCount);
}
pK->rawBufferLength = packetLimit * packetCount;
pK->rawBufferPacketSize = packetLimit;
{
BYTE *pNextBuffer = pK->rawBuffer;
int i = 0;
int offset = (pK->sending ? packetLimit - 100 : 100);
pK->spareExchange.myPacket = pNextBuffer;
pK->spareExchange.yourPacket = pNextBuffer + offset;
for (i = 0; i < packetCount - 1; i++) {
pNextBuffer += packetLimit;
pK->exchange[i].myPacket = pNextBuffer;
pK->exchange[i].yourPacket = pNextBuffer + offset;
pK->exchange[i].sequence = i;
}
pK->maxUsed = pK->minUsed;
pK->txPacket = pK->spareExchange.myPacket;
pK->txPacketLength = 0;
}
packetLimit -= 100;
if (packetLimit < pK->my.maxPacketSize)
pK->my.maxPacketSize = packetLimit;
if (packetLimit < pK->your.maxPacketSize)
pK->your.maxPacketSize = packetLimit;
if ((pK->sending) && (pK->your.maxPacketSize > 95))
pK->your.maxPacketSize = 95;
if (!(pK->sending) && (pK->my.maxPacketSize > 95))
pK->my.maxPacketSize = 95;
if (packetCount <= pK->windowSize)
pK->windowSize = packetCount - 1;
if (pK->debug) {
DebugBegin (pK->debug, debugCache);
DebugString (pK->debug, "Allocated ");
DebugUInt (pK->debug, packetCount);
DebugString (pK->debug, " cache buffers of ");
DebugUInt (pK->debug, packetLimit);
DebugString (pK->debug, " bytes each.");
DebugEnd (pK->debug);
KDumpCache (pK);
}
return kOK;
}
STATIC int KReceivePacketCache
(KERMIT_PRIVATE *pK, int timeout) {
int slot;
int sequence;
StsRet (KReceivePacket (pK, timeout, pK->spareExchange.yourPacket,
&(pK->spareExchange.yourPacketLength),
&(pK->spareExchange.yourPacketType), &sequence));
slot = sequence & 63;
sequence = ((pK->maxUsed - 32) & (~63)) | slot;
if (sequence < pK->maxUsed - 32)
sequence += 64;
if (sequence > pK->minUsed + 32)
return StsWarn (kBadPacket);
pK->rxPacketSequence = pK->spareExchange.sequence = sequence;
pK->rxPacket = pK->spareExchange.yourPacket;
pK->rxPacketLength = pK->spareExchange.yourPacketLength;
pK->rxPacketType = pK->spareExchange.yourPacketType;
if ((pK->minCache < pK->minUsed)
&& (pK->exchange[slot].yourPacket == NULL)
&& (pK->spareExchange.sequence >= pK->minUsed)) {
if (pK->debug) {
DebugBegin (pK->debug, debugCache);
DebugString (pK->debug, "Adding new slot to window");
DebugEnd (pK->debug);
}
SwapSlots (pK->minCache, slot);
pK->minCache++;
if (pK->spareExchange.sequence > pK->maxUsed)
pK->maxUsed = pK->spareExchange.sequence;
}
if (pK->exchange[slot].yourPacket) {
BYTE *pTmp;
if (pK->debug) {
DebugBegin (pK->debug, debugCache);
DebugString (pK->debug, "Putting received packet into slot ");
DebugInt (pK->debug, slot);
DebugEnd (pK->debug);
}
if (pK->spareExchange.sequence > pK->maxUsed)
pK->maxUsed = pK->spareExchange.sequence;
pTmp = pK->spareExchange.yourPacket;
pK->spareExchange.yourPacket = pK->exchange[slot].yourPacket;
pK->exchange[slot].yourPacket = pTmp;
pK->exchange[slot].yourPacketLength = pK->spareExchange.yourPacketLength;
pK->exchange[slot].yourPacketType = pK->spareExchange.yourPacketType;
pK->exchange[slot].sequence = pK->spareExchange.sequence;
pK->exchange[slot].packetCheck = pK->packetCheck;
pK->spareExchange.yourPacketLength = 0;
pK->spareExchange.yourPacketType = 0;
} else {
if (pK->debug) {
DebugBegin (pK->debug, debugCache | debugWarn);
DebugString (pK->debug, "Received packet ");
DebugInt (pK->debug, pK->spareExchange.sequence);
DebugChar (pK->debug, pK->spareExchange.yourPacketType);
DebugString (pK->debug, " out of current window [");
DebugInt (pK->debug, pK->minUsed);
DebugString (pK->debug, ",");
DebugInt (pK->debug, pK->maxUsed);
DebugString (pK->debug, "]");
DebugEnd (pK->debug);
}
if (pK->spareExchange.sequence > pK->minUsed) {
return StsWarn (kBadPacket);
} else {
return StsWarn (kBadPacket);
}
}
if (pK->debug)
KDumpCache (pK);
return kOK;
}
STATIC int KSendPacketFromCache
(KERMIT_PRIVATE *pK, int slot) {
slot &= 63;
if ((pK->exchange[slot].myPacketType == 0) ||
(pK->exchange[slot].myPacket == NULL)) {
if (pK->debug) {
DebugBegin (pK->debug, debugWarn | debugCache);
DebugString (pK->debug, "Can't send packet from cache slot ");
DebugInt (pK->debug, slot);
DebugEnd (pK->debug);
}
return kOK;
}
if (pK->debug) {
DebugBegin (pK->debug, debugCache);
DebugString (pK->debug, "Sending packet from cache slot ");
DebugInt (pK->debug, slot);
DebugEnd (pK->debug);
}
return StsWarn (KSendPacket (pK, slot, pK->exchange[slot].myPacketType,
pK->exchange[slot].myPacket, pK->exchange[slot].myPacketLength));
}
STATIC int KSendPacketReliable
(KERMIT_PRIVATE *pK, BYTE type,
const BYTE *pSendData, unsigned long sendDataLength) {
int retries = pK->retries;
int blocked = FALSE;
int err;
int slot = pK->sequence & 63;
EXCHANGE *pThisExchange = &(pK->exchange[slot]);
if (pThisExchange->myPacket == NULL) {
if (pK->minCache < pK->minUsed) {
if (pK->debug) {
DebugBegin (pK->debug, debugCache);
DebugString (pK->debug, "Adding new slot to window for packet to send");
DebugEnd (pK->debug);
}
SwapSlots (pK->minCache, slot);
pK->minCache++;
pThisExchange->yourPacketType = 0;
} else {
if (pK->debug) {
DebugBegin (pK->debug, debugWarn | debugCache);
DebugString (pK->debug,
"Fatal Internal Error: window blocked before packet sent");
DebugEnd (pK->debug);
KDumpCache (pK);
}
return StsWarn (kFail);
}
}
if (pSendData == pK->spareExchange.myPacket) {
BYTE *pTmp = pThisExchange->myPacket;
pThisExchange->myPacket = pK->spareExchange.myPacket;
pK->spareExchange.myPacket = pTmp;
} else {
memcpy (pThisExchange->myPacket, pSendData, sendDataLength);
}
if (pK->sequence > pK->maxUsed)
pK->maxUsed = pK->sequence;
pThisExchange->sequence = pK->sequence;
pThisExchange->myPacketLength = sendDataLength;
pThisExchange->myPacketType = type;
pK->txPacket = pK->spareExchange.myPacket;
pK->txPacketLength = 0;
StsRet (KSendPacketFromCache (pK, slot));
KDumpCache (pK);
if (pK->minUsed <= pK->minCache)
blocked = 1;
if (pK->maxUsed - pK->minUsed + 1 >= pK->currentWindowSize)
blocked = (pK->maxUsed - pK->minUsed + 1) - pK->currentWindowSize + 1;
if (pK->debug) {
if (blocked) {
DebugBegin (pK->debug, debugCache);
DebugString (pK->debug, "Need ");
DebugInt (pK->debug, blocked);
DebugString (pK->debug, " packets to unblock window.");
DebugEnd (pK->debug);
}
}
do {
err = KReceivePacketCache (pK, blocked ? pK->my.timeout : 0);
switch (err) {
case kOK:
switch (pK->rxPacketType) {
case 'N':
retries--;
if (pK->exchange[pK->rxPacketSequence & 63].myPacketType != 0)
StsRet (KSendPacketFromCache (pK, pK->rxPacketSequence));
if (pK->currentWindowSize == 1)
pK->exchange[(pK->rxPacketSequence - 1) & 63].yourPacketType = 'Y';
case 'Y':
if (pK->exchange[pK->minUsed & 63].yourPacketType != 'Y') {
long gap = pK->rxPacketSequence - pK->minUsed;
if (gap == 1) {
StsRet (KSendPacketFromCache (pK, pK->minUsed));
}
if (gap == pK->currentWindowSize / 2) {
StsRet (KSendPacketFromCache (pK, pK->minUsed));
}
if (gap / 3 == pK->currentWindowSize / 4) {
StsRet (KSendPacketFromCache (pK, pK->minUsed));
}
}
while ((pK->exchange[pK->minUsed & 63].yourPacketType == 'Y')
&& (pK->exchange[pK->minUsed & 63].sequence == pK->minUsed)) {
pK->minUsed++;
if (blocked > 0) {
blocked--;
if (pK->debug) {
DebugBegin (pK->debug, debugCache);
DebugString (pK->debug, "Need ");
DebugInt (pK->debug, blocked);
DebugString (pK->debug, " acknowledgements to unblock window");
DebugEnd (pK->debug);
}
}
}
break;
case 'E':
if (pK->debug) {
DebugBegin (pK->debug, debugWarn);
DebugString (pK->debug, "Transfer terminated by remote: ``");
DebugStringCount (pK->debug, (const char *) pK->rxPacket,
pK->rxPacketLength);
DebugString (pK->debug, "''");
DebugEnd (pK->debug);
}
return StsWarn (kFailed);
default:
return StsWarn (kFail);
}
KDumpCache (pK);
break;
case kBadPacket:
case kTimeout:
if (blocked) {
StsRet (KSendPacketFromCache (pK, pK->minUsed));
retries--;
}
break;
default:
return StsWarn (err);
}
if (retries <= 0) {
if (pK->debug) {
DebugBegin (pK->debug, debugWarn);
DebugString (pK->debug, "Too many retries");
DebugEnd (pK->debug);
}
return StsWarn (kFail);
}
} while (blocked || (err == kOK));
if (pK->exchange[pK->sequence & 63].yourPacketType &&
(pK->exchange[pK->sequence & 63].sequence == pK->sequence)) {
pK->rxPacket = pK->exchange[pK->sequence & 63].yourPacket;
pK->rxPacketType = pK->exchange[pK->sequence & 63].yourPacketType;
pK->rxPacketLength = pK->exchange[pK->sequence & 63].yourPacketLength;
}
pK->sequence++;
if (err == kTimeout)
return kOK;
if (err == kBadPacket)
return kOK;
return StsWarn (err);
}
STATIC int KReceivePacketReliable
(KERMIT_PRIVATE *pK) {
int retries = pK->retries;
int err;
int thisSlot = pK->sequence & 63;
int timeout = (pK->my.timeout >= 0) ? pK->my.timeout : 10;
if ((pK->exchange[thisSlot].yourPacketType != 0)
&& (pK->exchange[thisSlot].sequence == pK->sequence)) {
pK->rxPacket = pK->exchange[thisSlot].yourPacket;
pK->rxPacketLength = pK->exchange[thisSlot].yourPacketLength;
pK->rxPacketType = pK->exchange[thisSlot].yourPacketType;
pK->rxPacketSequence = pK->exchange[thisSlot].sequence;
return kOK;
}
while (TRUE) {
err = KReceivePacketCache (pK, timeout);
switch (err) {
case kTimeout:
if (pK->my.timeout < 0)
break;
case kBadPacket:
StsRet (KSendPacket (pK, pK->sequence, 'N', NULL, 0));
break;
case kOK:
if (pK->rxPacketType == 'E') {
if (pK->debug) {
DebugBegin (pK->debug, debugWarn);
DebugString (pK->debug, "Transfer terminated by remote: ``");
DebugStringCount (pK->debug, (const char *) pK->rxPacket,
pK->rxPacketLength);
DebugString (pK->debug, "''");
DebugEnd (pK->debug);
}
return StsWarn (kFailed);
}
if ((pK->rxPacketSequence & 63) == (pK->sequence & 63)) {
return kOK;
} else {
int slot = pK->rxPacketSequence & 63;
if (pK->exchange[slot].myPacketType == 0) {
if (pK->rxPacketSequence < pK->minCache) {
if (pK->debug) {
DebugBegin (pK->debug, debugCache);
DebugString (pK->debug, "Synthesizing ACK "
"for old packet #");
DebugInt (pK->debug, pK->rxPacketSequence);
DebugEnd (pK->debug);
}
KSendPacket (pK, pK->rxPacketSequence, 'Y', NULL, 0);
}
} else
KSendPacketFromCache (pK, slot);
}
break;
default:
return StsWarn (err);
}
if (retries-- <= 0)
return StsWarn (kFail);
}
}
STATIC int KSendResponse
(KERMIT_PRIVATE *pK, BYTE type,
const BYTE *pResponse, unsigned long responseLength) {
int slot = pK->sequence & 63;
EXCHANGE *pThisExchange = &(pK->exchange[slot]);
if (pResponse == pK->spareExchange.myPacket) {
BYTE *pTmp = pThisExchange->myPacket;
pThisExchange->myPacket = pK->spareExchange.myPacket;
pK->spareExchange.myPacket = pTmp;
} else
memcpy (pThisExchange->myPacket, pResponse, responseLength);
pThisExchange->myPacketLength = responseLength;
pThisExchange->myPacketType = type;
StsRet (KSendPacketFromCache (pK, slot));
pK->sequence++;
pK->minUsed = pK->sequence;
pK->txPacket = pK->spareExchange.myPacket;
return kOK;
}
STATIC int KSendFile
(KERMIT_PRIVATE *pK) {
int err = kOK;
{
const char *fileName;
DiskFileName (pK->f, &fileName);
pK->filePosition = 0;
KProgress (pK, stsNewFile);
StsRet (KSendPacketReliable (pK, 'F', (const BYTE *) fileName, strlen (fileName)));
}
if (pK->your.capabilities[0] & 8) {
}
pK->currentWindowSize = pK->windowSize;
do {
unsigned long dataLength = pK->my.maxPacketSize;
unsigned long rawLength;
BYTE *txBuffer = pK->txPacket;
err = KFileReadEncode (pK, txBuffer, &dataLength, &rawLength);
if (err == kOK) {
StsRet (KSendPacketReliable (pK, 'D', txBuffer, dataLength));
pK->filePosition += rawLength;
KProgress (pK, stsSending);
if (pK->rxPacketLength > 0) {
if (pK->rxPacket[0] == 'X') {
pK->currentWindowSize = 1;
err = KSendPacketReliable (pK, 'Z', (const BYTE *) "D", 1);
KProgress (pK, stsSkipped);
return kOK;
} else if (pK->rxPacket[0] == 'Z')
err = kFail;
}
}
} while (err == kOK);
pK->currentWindowSize = 1;
if (err == kEOF) {
err = KSendPacketReliable (pK, 'Z', NULL, 0);
KProgress (pK, stsEOF);
} else {
err = KSendPacketReliable (pK, 'Z', (const BYTE *) "D", 1);
}
return StsWarn (err);
}
STATIC int KSendInitiate
(KERMIT_PRIVATE *);
STATIC int KSendFiles
(KERMIT_PRIVATE *pK) {
int err;
KResetSequence (pK);
err = StsWarn (KSendInitiate (pK));
while (err == kOK) {
pK->eofFlag = 0;
pK->repeatStart = 0;
pK->repeatEnd = 1;
pK->num8bit = 0;
pK->repeatByte[0] = 0;
pK->repeatCount[0] = 0;
pK->lockingFlip = 0;
pK->quoteNext = 0;
err = KFileReadOpenNext (pK, diskFileUnknown);
if (err == kOK) {
err = KSendFile (pK);
KFileReadClose (pK);
}
}
if (err == kEndOfSession) {
err = KSendPacketReliable (pK, 'B', NULL, 0);
}
if (err == kOK)
KProgress (pK, stsDone);
else if (pK->userCancel)
KProgress (pK, stsCancelled);
else
KProgress (pK, stsFailed);
return StsWarn (err);
}
STATIC int KReceiveFileData
(KERMIT_PRIVATE *pK) {
pK->filePosition = 0;
pK->fileSize = -1;
while (TRUE) {
StsRet (KReceivePacketReliable (pK));
switch (pK->rxPacketType) {
case 'A':
if (pK->debug) {
const BYTE *p = pK->rxPacket;
BYTE attribute;
int length = pK->rxPacketLength;
unsigned int attrLength;
const char *attrValue;
while (length > 0) {
attribute = *p++;
length--;
attrLength = *p++ - 32;
length--;
attrValue = (const char *) p;
p += attrLength;
length -= attrLength;
DebugBegin (pK->debug, debugAttr);
DebugString (pK->debug, "Attribute: ");
switch (attribute) {
case '!':
DebugString (pK->debug, "Length in K:");
break;
case '"':
DebugString (pK->debug, "Type:");
break;
case '#':
DebugString (pK->debug, "Creation date:");
break;
case '$':
DebugString (pK->debug, "Creator:");
break;
case '%':
DebugString (pK->debug, "Account to charge:");
break;
case '&':
DebugString (pK->debug, "Area to store file:");
break;
case '\'':
DebugString (pK->debug, "Password for file storage area:");
break;
case '(':
DebugString (pK->debug, "Block size:");
break;
case ')':
DebugString (pK->debug, "Access:");
break;
case '*':
DebugString (pK->debug, "Encoding:");
break;
case '+':
DebugString (pK->debug, "Disposition:");
break;
case ',':
DebugString (pK->debug, "Protection Code:");
break;
case '-':
DebugString (pK->debug, "Generic Protection Code:");
break;
case '.':
DebugString (pK->debug, "Originating system: ");
{
static struct {
char vendor;
char system;
const char *name;
} systems[] = {
{
'A', 0, "Apple computer"
},
{
'B', 0, "Sperry/Univac mainframe"
},
{
'C', 0, "CDC mainframe"
},
{
'D', 0, "DEC system"
},
{
'E', 0, "Honeywell mainframe"
},
{
'F', 0, "Data General"
},
{
'G', 0, "PR1ME"
},
{
'H', 0, "Hewlett-Packard"
},
{
'I', 0, "IBM mainframe"
},
{
'J', 0, "Tandy microcomputer, TRSDOS"
},
{
'K', 0, "Atari microcomputer"
},
{
'L', 0, "Commodore"
},
{
'M', 0, "Miscellaneous mainframe"
},
{
'N', 0, "Microcomputer or workstation"
},
{
'U', '1', "Unix"
},
{
'U', '8', "MS-DOS"
},
{
'U', 0, "Portable O/S"
},
{
0, 0, "Unrecognized system"
}
};
int i = 0;
while (TRUE) {
if (systems[i].vendor == 0)
break;
if (systems[i].vendor == attrValue[0]) {
if (systems[i].system == 0)
break;
if ((systems[i].system == attrValue[1]) && (attrLength > 1))
break;
}
i++;
}
DebugString (pK->debug, systems[i].name);
DebugString (pK->debug, ": code ");
}
break;
case '/':
DebugString (pK->debug, "Data format:");
break;
case '0':
DebugString (pK->debug, "System-specific parameters:");
break;
case '1':
DebugString (pK->debug, "Length in bytes:");
break;
default:
DebugString (pK->debug, "Attribute ");
DebugChar (pK->debug, attribute);
break;
}
if (attrValue)
DebugStringCount (pK->debug, attrValue, attrLength);
DebugEnd (pK->debug);
}
} {
const BYTE *p = pK->rxPacket;
BYTE attribute;
int length = pK->rxPacketLength;
int attrLength;
long fileSize = -1;
const char *attrValue;
while (length > 0) {
attribute = *p++;
length--;
attrLength = *p++ - 32;
length--;
attrValue = (const char *) p;
p += attrLength;
length -= attrLength;
switch (attribute) {
case '!':
{
long num = 0;
int i = 0;
for (; i < attrLength; i++)
num = num * 10 + attrValue[i] - '0';
if (fileSize == -1)
fileSize = num;
}
break;
case '1':
{
long num = 0;
int i = 0;
for (; i < attrLength; i++)
num = num * 10 + attrValue[i] - '0';
fileSize = num;
}
break;
default:
break;
}
}
pK->fileSize = fileSize;
}
StsRet (KSendResponse (pK, 'Y', NULL, 0));
break;
case 'D':
StsRet (KFileWriteDecode (pK, pK->rxPacket, pK->rxPacketLength));
KProgress (pK, stsReceiving);
if (pK->userCancel)
StsRet (KSendResponse (pK, 'Y', (const BYTE *) "Z", 1));
else
StsRet (KSendResponse (pK, 'Y', NULL, 0));
break;
case 'Z':
if ((pK->rxPacketLength > 0) && (pK->rxPacket[0] == 'D')) {
KProgress (pK, stsSkipped);
} else {
KProgress (pK, stsEOF);
}
StsRet (KSendResponse (pK, 'Y', NULL, 0));
return kOK;
}
}
}
STATIC int KReceiveFiles
(KERMIT_PRIVATE *pK) {
KProgress (pK, stsNegotiating);
while (TRUE) {
StsRet (KReceivePacketReliable (pK));
switch (pK->rxPacketType) {
case 'F':
pK->rxPacket[pK->rxPacketLength] = 0;
StsRet (KFileWriteOpen (pK, (const char *) pK->rxPacket, 0, diskFileUnknown));
KProgress (pK, stsNewFile);
pK->eofFlag = 0;
pK->repeatStart = 0;
pK->repeatEnd = 1;
pK->num8bit = 0;
pK->repeatByte[0] = 0;
pK->repeatCount[0] = 0;
pK->lockingFlip = 0;
pK->quoteNext = 0;
StsRet (KSendResponse (pK, 'Y', NULL, 0));
StsRet (KReceiveFileData (pK));
StsRet (KFileWriteClose (pK));
break;
case 'B':
StsRet (KSendResponse (pK, 'Y', NULL, 0));
KProgress (pK, stsDone);
return kOK;
}
}
}
STATIC void KDumpInitString
(KERMIT_PRIVATE *pK, BYTE *initString,
unsigned initLength) {
BYTE *pExtended = NULL;
int extendedLength = 0;
BYTE capabilities[5] =
{0, 0, 0, 0, 0};
if (initLength > 0) {
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, " 1:");
DebugChar (pK->debug, initString[0]);
DebugString (pK->debug, " Max Packet Length =");
DebugInt (pK->debug, initString[0] - 32);
DebugEnd (pK->debug);
}
if (initLength > 1) {
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, " 2:");
DebugChar (pK->debug, initString[1]);
DebugString (pK->debug, " Timeout =");
DebugInt (pK->debug, initString[1] - 32);
DebugEnd (pK->debug);
}
if (initLength > 2) {
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, " 3:");
DebugChar (pK->debug, initString[2]);
DebugString (pK->debug, " Number Pad Characters =");
DebugInt (pK->debug, initString[2] - 32);
DebugEnd (pK->debug);
}
if (initLength > 3) {
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, " 4:");
DebugChar (pK->debug, initString[3]);
DebugString (pK->debug, " Pad Character =");
DebugInt (pK->debug, initString[3] ^ 0x40);
DebugChar (pK->debug, initString[3] ^ 0x40);
DebugEnd (pK->debug);
}
if (initLength > 4) {
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, " 5:");
DebugChar (pK->debug, initString[4]);
DebugString (pK->debug, " Packet Terminator =");
DebugInt (pK->debug, initString[4] - 32);
DebugEnd (pK->debug);
}
if (initLength > 5) {
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, " 6:");
DebugChar (pK->debug, initString[5]);
DebugString (pK->debug, " Control prefix =");
DebugInt (pK->debug, initString[5]);
DebugChar (pK->debug, initString[5]);
DebugEnd (pK->debug);
}
if (initLength > 6) {
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, " 7:");
DebugChar (pK->debug, initString[6]);
DebugString (pK->debug, " eighth-bit prefix =");
DebugInt (pK->debug, initString[6]);
DebugChar (pK->debug, initString[6]);
DebugEnd (pK->debug);
}
if (initLength > 7) {
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, " 8:");
DebugChar (pK->debug, initString[7]);
DebugString (pK->debug, " Packet check type =");
DebugInt (pK->debug, initString[7] - '0');
DebugEnd (pK->debug);
}
if (initLength > 8) {
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, " 9:");
DebugChar (pK->debug, initString[8]);
DebugString (pK->debug, " Repeat prefix =");
DebugInt (pK->debug, initString[8]);
DebugChar (pK->debug, initString[8]);
DebugEnd (pK->debug);
}
if (initLength > 9) {
int i = 0;
BYTE thisCapability;
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, " Capabilities bitmap: ");
do {
thisCapability = initString[i + 9] - 32;
if (i < sizeof (capabilities))
capabilities[i] = thisCapability;
i++;
DebugIntHex (pK->debug, thisCapability);
DebugChar (pK->debug, thisCapability + 32);
DebugString (pK->debug, " ");
} while ((i < sizeof (capabilities)) && (thisCapability & 1));
pExtended = initString + i + 9;
extendedLength = initLength - 9 - i;
DebugEnd (pK->debug);
} {
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, " Capabilities enabled: ");
if (capabilities[0] & 32)
DebugString (pK->debug, " (Locking Shift)");
if (capabilities[0] & 16)
DebugString (pK->debug, " (Extra-Long Packets)");
if (capabilities[0] & 8)
DebugString (pK->debug, " (Attributes)");
if (capabilities[0] & 4)
DebugString (pK->debug, " (Windowing)");
if (capabilities[0] & 2)
DebugString (pK->debug, " (Long Packets)");
DebugEnd (pK->debug);
}
if (extendedLength > 0) {
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, " ");
DebugChar (pK->debug, pExtended[0]);
DebugString (pK->debug, " Window size =");
DebugInt (pK->debug, pExtended[0] - 32);
DebugEnd (pK->debug);
}
if (extendedLength > 2) {
long packetSize = (pExtended[1] - 32) * 95 + (pExtended[2] - 32);
if (capabilities[0] & 16)
packetSize *= 95;
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, " ");
DebugChar (pK->debug, pExtended[1]);
DebugChar (pK->debug, pExtended[2]);
DebugString (pK->debug, " Extended packet size =");
DebugInt (pK->debug, packetSize);
DebugEnd (pK->debug);
}
}
STATIC void KParseYourInit
(KERMIT_PRIVATE *pK, BYTE *yourInit,
unsigned yourInitLength) {
BYTE *pYourExtended;
int yourExtendedLength;
long maxPacketSize;
maxPacketSize = 89;
if (yourInitLength > 0) {
maxPacketSize = yourInit[0] - 32 - 5;
if (maxPacketSize < 20)
maxPacketSize = 20;
}
if (yourInitLength > 1)
pK->my.timeout = yourInit[1] - 32;
else
pK->my.timeout = 10;
if (yourInitLength > 2)
pK->my.padCount = yourInit[2] - 32;
else
pK->my.padCount = 0;
if (yourInitLength > 3)
pK->my.padByte = yourInit[3] ^ 0x40;
else
pK->my.padByte = 0;
if (yourInitLength > 4)
pK->my.packetTerminator = yourInit[4] - 32;
else
pK->my.packetTerminator = 0x0D;
if (yourInitLength > 5)
pK->your.prefixControl = yourInit[5];
else
pK->your.prefixControl = '#';
if (yourInitLength > 6) {
char mine = pK->my.prefix8bit;
char yours = yourInit[6];
if (mine == 'Y')
mine = yours;
if (yours == 'Y')
yours = mine;
if (mine == yours) {
if (mine < '!')
yours = 0;
if ((mine > '>') && (mine < '`'))
yours = 0;
if (mine > '&')
yours = 0;
} else
yours = 0;
pK->my.prefix8bit = yours;
pK->your.prefix8bit = yours;
} else {
pK->my.prefix8bit = 0;
pK->your.prefix8bit = 0;
}
if (yourInitLength > 7) {
switch (yourInit[7]) {
case '1':
default:
pK->your.preferredPacketCheck = '1';
break;
case '2':
case '3':
case 'B':
pK->your.preferredPacketCheck = yourInit[7];
break;
}
} else {
pK->your.preferredPacketCheck = '1';
pK->my.preferredPacketCheck = pK->your.preferredPacketCheck;
}
if (yourInitLength > 8) {
pK->your.prefixRepeat = yourInit[8];
} else
pK->your.prefixRepeat = 0;
if (pK->your.prefixRepeat == ' ')
pK->your.prefixRepeat = 0;
if (yourInitLength > 9) {
int i = 0;
BYTE thisCapability;
do {
thisCapability = yourInit[i + 9] - 32;
if (i < sizeof (pK->your.capabilities))
pK->your.capabilities[i] = thisCapability;
i++;
} while ((yourInitLength > i + 9) && (thisCapability & 1));
pYourExtended = yourInit + (i + 9);
yourExtendedLength = yourInitLength - (i + 9);
} else {
pYourExtended = NULL;
yourExtendedLength = 0;
}
if (yourExtendedLength > 0) {
int windowSize = 1;
if (pK->your.capabilities[0] & 4)
windowSize = pYourExtended[0] - 32;
if (windowSize < pK->windowSize) {
pK->windowSize = windowSize;
if (pK->debug) {
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, "Setting window size to ");
DebugInt (pK->debug, windowSize);
DebugEnd (pK->debug);
}
}
} else
pK->windowSize = 1;
if (pK->your.capabilities[0] & 2) {
if (yourExtendedLength > 2) {
int packetSize = (pYourExtended[1] - 32) * 95 + (pYourExtended[2] - 32);
if (pK->your.capabilities[0] & 16)
packetSize *= 95;
packetSize -= 3;
if (packetSize > maxPacketSize)
maxPacketSize = packetSize;
} else
maxPacketSize = 497;
}
if (maxPacketSize < pK->my.maxPacketSize) {
pK->my.maxPacketSize = maxPacketSize;
if (pK->debug) {
DebugBegin (pK->debug, debugInit);
DebugString (pK->debug, "Setting maximum packet size to ");
DebugInt (pK->debug, pK->my.maxPacketSize);
DebugEnd (pK->debug);
}
}
}
STATIC void KBuildMyInit
(KERMIT_PRIVATE *pK, BYTE *myInit,
unsigned *pMyInitLength) {
BYTE *pMyExtended;
int myInitLength = 0;
myInit[0] = ((pK->your.maxPacketSize > 94) ? 94 : pK->your.maxPacketSize) + 32;
myInit[1] = pK->your.timeout + 32;
myInit[2] = pK->your.padCount + 32;
myInit[3] = pK->your.padByte ^ 0x40;
myInit[4] = pK->your.packetTerminator + 32;
myInit[5] = pK->my.prefixControl;
if (pK->my.prefix8bit)
myInit[6] = pK->my.prefix8bit;
else
myInit[6] = 'N';
myInit[7] = pK->my.preferredPacketCheck;
if (pK->my.prefixRepeat)
myInit[8] = pK->my.prefixRepeat;
else
myInit[8] = ' ';
pK->my.capabilities[0] = 0;
pK->my.capabilities[0] |= 2;
pK->my.capabilities[0] |= 4;
pK->my.capabilities[0] |= 8;
if (pK->your.maxPacketSize > 9024)
pK->my.capabilities[0] |= 16;
pK->my.capabilities[0] |= 32;
{
BYTE *source = pK->my.capabilities;
pMyExtended = myInit + 9;
do {
*pMyExtended++ = *source + 32;
} while (*source++ & 1);
if (pK->my.capabilities[0] & 4)
*pMyExtended++ = pK->windowSize + 32;
else
*pMyExtended++ = 1 + 32;
{
long extendedPacketSize = pK->your.maxPacketSize;
if (pK->my.capabilities[0] & 16)
extendedPacketSize /= 95;
if (extendedPacketSize > 9024)
extendedPacketSize = 9024;
*pMyExtended++ = (extendedPacketSize / 95) + 32;
*pMyExtended++ = (extendedPacketSize % 95) + 32;
}
myInitLength = pMyExtended - myInit;
}
*pMyInitLength = myInitLength;
}
STATIC int KSendInitiate
(KERMIT_PRIVATE *pK) {
BYTE *myInit = pK->txPacket;
unsigned myInitLength;
KBuildMyInit (pK, myInit, &myInitLength);
if (pK->debug)
KDumpInitString (pK, myInit, myInitLength);
StsRet (KSendPacketReliable (pK, 'S', myInit, myInitLength));
if (pK->debug)
KDumpInitString (pK, pK->rxPacket, pK->rxPacketLength);
KParseYourInit (pK, pK->rxPacket, pK->rxPacketLength);
StsRet (KInitWindow (pK));
if (pK->my.preferredPacketCheck == pK->your.preferredPacketCheck)
pK->packetCheck = pK->my.preferredPacketCheck;
else
pK->packetCheck = '1';
pK->lockingShift = pK->your.capabilities[0] & pK->my.capabilities[0] & 32;
if (!pK->my.prefix8bit)
pK->lockingShift = 0;
KInitEncoding (pK);
return kOK;
}
STATIC int KReceiveNegotiate
(KERMIT_PRIVATE *pK, BYTE *yourInit,
unsigned yourInitLength) {
BYTE myInit[100];
unsigned myInitLength;
if (pK->debug)
KDumpInitString (pK, yourInit, yourInitLength);
KParseYourInit (pK, yourInit, yourInitLength);
StsRet (KInitWindow (pK));
if (pK->your.timeout < pK->my.timeout + 5)
pK->your.timeout = pK->my.timeout + 5;
if (pK->your.timeout < 2 * pK->my.timeout)
pK->your.timeout = 2 * pK->my.timeout;
pK->my.preferredPacketCheck = pK->your.preferredPacketCheck;
pK->my.prefixRepeat = pK->your.prefixRepeat;
{
int i = 0;
for (i = 0; i < sizeof (pK->my.capabilities); i++)
pK->my.capabilities[i] &= pK->your.capabilities[i];
}
KBuildMyInit (pK, myInit, &myInitLength);
if (pK->debug)
KDumpInitString (pK, myInit, myInitLength);
StsRet (KSendResponse (pK, 'Y', myInit, myInitLength));
pK->packetCheck = pK->my.preferredPacketCheck;
pK->lockingShift = pK->your.capabilities[0] & pK->my.capabilities[0] & 32;
if (!pK->my.prefix8bit)
pK->lockingShift = 0;
KInitEncoding (pK);
return kOK;
}
STATIC int KServer
(KERMIT_PRIVATE *pK) {
int defaultTimeout = pK->my.timeout;
KProgress (pK, stsNegotiating);
while (TRUE) {
KResetSequence (pK);
if (pK->serverMode) {
defaultTimeout = pK->my.timeout;
pK->my.timeout = -1;
}
StsRet (KReceivePacketReliable (pK));
pK->my.timeout = defaultTimeout;
switch (pK->rxPacketType) {
case 'S':
KReceiveNegotiate (pK, pK->rxPacket, pK->rxPacketLength);
StsRet (KReceiveFiles (pK));
if (!pK->serverMode)
return kOK;
break;
case 'G':
switch (pK->rxPacket[0]) {
case 'F':
StsRet (KSendResponse (pK, 'Y', NULL, 0));
return kOK;
case 'L':
StsRet (KSendResponse (pK, 'Y', NULL, 0));
return kOK;
default:
StsRet (KSendResponse (pK, 'E', (const BYTE *) "Command not implemented", 23));
}
pK->serverMode = TRUE;
break;
case 'R':
case 'I':
case 'X':
case 'C':
case 'K':
pK->serverMode = TRUE;
default:
StsRet (KSendResponse (pK, 'E', (const BYTE *) "Command not implemented", 23));
break;
}
}
}
int KermitInit
(KERMIT *pKPublic, SERIAL_PORT port) {
KERMIT_PRIVATE *pK;
pK = malloc (sizeof (*pK));
if (pK == NULL)
return 1;
memset (pK, 0, sizeof (*pK));
pK->sending = TRUE;
pK->userCancel = 0;
pK->debug = NULL;
KInitCrc ();
pK->port = NULL;
pK->f = NULL;
pK->fileSize = -1;
pK->filenames = NULL;
pK->currentFileName = 0;
pK->numFileNames = 0;
pK->fileType = diskFileUnknown;
pK->progress = NULL;
pK->filePosition = 0;
{
pK->pFileBuffer = (BYTE *) malloc (FILE_BUFFER_SIZE);
if (pK->pFileBuffer == NULL)
return 1;
pK->fileBufferSize = FILE_BUFFER_SIZE;
pK->pFileThisChar = pK->pFileBuffer;
pK->pFileBufferLimit = pK->pFileBuffer;
pK->eofFlag = 0;
}
pK->your.prefixControl = '#';
pK->my.prefixControl = '#';
pK->your.prefix8bit = 'N';
pK->my.prefix8bit = 'Y';
pK->your.prefixRepeat = '~';
pK->my.prefixRepeat = '~';
pK->lockingShift = FALSE;
pK->lockingFlip = 0;
pK->quoteNext = 0;
pK->your.preferredPacketCheck = '1';
pK->my.preferredPacketCheck = '1';
pK->packetCheck = '1';
pK->your.sopCharacter = 0xff;
pK->my.sopCharacter = 0xff;
pK->your.maxPacketSize = 90;
pK->my.maxPacketSize = 80;
pK->my.timeout = 30;
pK->your.timeout = 10;
pK->your.padCount = 0;
pK->my.padCount = 0;
pK->your.padByte = 0;
pK->my.padByte = 0;
pK->your.packetTerminator = 0x0d;
pK->my.packetTerminator = 0x0d;
{
int i;
pK->exchange[0].sequence = 0;
pK->exchange[0].packetCheck = '1';
pK->exchange[0].myPacket = NULL;
pK->exchange[0].myPacketLength = 0;
pK->exchange[0].myPacketType = 0;
pK->exchange[0].yourPacket = NULL;
pK->exchange[0].yourPacketLength = 0;
pK->exchange[0].yourPacketType = 0;
for (i = 1; i < 64; i++) {
pK->exchange[i] = pK->exchange[0];
pK->exchange[i].sequence = i;
}
}
pK->rxPacket = NULL;
pK->rxPacketLength = 0;
pK->rxPacketType = 0;
pK->txPacket = NULL;
pK->txPacketLength = 0;
pK->currentWindowSize = 1;
pK->windowSize = 1;
pK->minCache = 0;
pK->maxUsed = 0;
pK->minUsed = 0;
pK->sequence = 0;
pK->retries = 10;
pK->rawBufferLength = 0;
pK->rawBuffer = NULL;
pK->rawBufferPacketSize = 0;
pK->spareExchange = pK->exchange[0];
{
int i;
for (i = 0; i < 5; i++) {
pK->your.capabilities[i] = 0;
pK->my.capabilities[i] = 0;
}
}
pK->serverMode = FALSE;
KInitWindow (pK);
pK->port = port;
*pKPublic = pK;
return 0;
}
int KermitDestroy
(KERMIT kPublic) {
KERMIT_PRIVATE *pK = (KERMIT_PRIVATE *) kPublic;
free (pK->pFileBuffer);
return 0;
}
int KermitSetFast
(KERMIT kPublic, int speed) {
KERMIT_PRIVATE *pK = (KERMIT_PRIVATE *) kPublic;
int packetSize = 90, windowSize = 1;
if (speed <= 0)
return 1;
switch (speed) {
default:
case 8:
packetSize = 9024;
windowSize = 32;
break;
case 7:
packetSize = 4096;
windowSize = 31;
break;
case 6:
packetSize = 2048;
windowSize = 31;
break;
case 5:
packetSize = 1024;
windowSize = 16;
break;
case 4:
packetSize = 256;
windowSize = 16;
break;
case 3:
packetSize = 128;
windowSize = 16;
break;
case 2:
packetSize = 90;
windowSize = 11;
break;
case 1:
packetSize = 1024;
windowSize = 1;
break;
}
pK->your.maxPacketSize = packetSize;
pK->my.maxPacketSize = packetSize;
pK->windowSize = windowSize;
if (packetSize > 500)
pK->my.preferredPacketCheck = '3';
else if (packetSize > 90)
pK->my.preferredPacketCheck = '2';
return 0;
}
int KermitSetDebug
(KERMIT kPublic, DEBUG debug) {
KERMIT_PRIVATE *pK = (KERMIT_PRIVATE *) kPublic;
pK->debug = debug;
return 0;
}
int KermitSetProgress
(KERMIT kPublic, PROGRESS progress) {
KERMIT_PRIVATE *pK = (KERMIT_PRIVATE *) kPublic;
pK->progress = progress;
return 0;
}
int KermitSetFileType
(KERMIT kPublic, int fileType) {
KERMIT_PRIVATE *pK = (KERMIT_PRIVATE *) kPublic;
pK->fileType = fileType;
return 0;
}
int KermitSend
(KERMIT kPublic, const char *filenames[], int numFiles) {
KERMIT_PRIVATE *pK = (KERMIT_PRIVATE *) kPublic;
int returnVal = 0;
pK->filenames = filenames;
pK->numFileNames = numFiles;
pK->currentFileName = 0;
pK->sending = TRUE;
ProgressSending (pK->progress);
if (KSendFiles (pK) != kOK)
returnVal = 1;
return returnVal;
}
int KermitReceive
(KERMIT kPublic) {
KERMIT_PRIVATE *pK = (KERMIT_PRIVATE *) kPublic;
int returnVal = 0;
pK->sending = FALSE;
ProgressReceiving (pK->progress);
if (KServer (pK) != kOK)
returnVal = 1;
return returnVal;
}
int KermitServer
(KERMIT kPublic) {
KERMIT_PRIVATE *pK = (KERMIT_PRIVATE *) kPublic;
int returnVal = 0;
pK->serverMode = TRUE;
pK->sending = FALSE;
ProgressReceiving (pK->progress);
if (KServer (pK) != kOK)
returnVal = 1;
return returnVal;
}
int KermitCancel
(KERMIT kPublic) {
KERMIT_PRIVATE *pK = (KERMIT_PRIVATE *) kPublic;
pK->userCancel = TRUE;
return 0;
}