home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
098.lha
/
postb.c
< prev
next >
Wrap
C/C++ Source or Header
|
1986-11-20
|
22KB
|
881 lines
/**
* Copyright (c) 1985 by Steve Wilhite, Worthington, Ohio
*
* Permission is granted to use or distribute this software without any
* restrictions as long as this entire copyright notice is included intact.
* You may include it in any software product that you sell for profit.
*
* This software is distributed as is, and is not guaranteed to work on any
* given hardware/software configuration. Furthermore, no liability is
* granted with this software.
*
* ABSTRACT:
*
* The function, Transfer_File, implements error-free file transfer using
* CompuServe's "B" protocol.
*
* It has been assumed that the start-of-packet sequence, DLE "B", has
* been detected and the next byte not received yet is the packet
* sequence number (an ASCII digit).
*
* ENVIRONMENT: Lattice "C", machine independent.
*
* AUTHOR: Steve Wilhite, CREATION DATE: 21-Jul-85
*
* REVISION HISTORY:
* Jan 1-3 1988 - Charles B. Blish - Formatted source properly. IMHO.
* added 'short' specifier to vars,
* removing 32 bit default of lattice.
*
*
**/
#include <lattice/stdio.h>
#include <lattice/fcntl.h>
/* External Functions */
extern Delay(); /* Sleep for "n" milliseconds */
extern Start_Timer(); /* Enable the timer for the specified number
seconds */
extern int Timer_Expired(); /* Returns "true" if the timer has expired,
"false" otherwise */
extern int Wants_To_Abort(); /* Returns "true" if the user wants to abort
the file transfer, "false" otherwise */
extern int Read_Modem(); /* Read a character from the comm port.
Returns -1 if no character available */
extern int Write_Modem(); /* Send a character to the comm port. Returns
"true" is successful, "false" otherwise */
extern int open(), creat(); /* standard I/O functions */
extern int read(), write();
extern int close();
#define ETX 0x03
#define ENQ 0x05
#define DLE 0x10
#define XON 0x11
#define XOFF 0x13
#define NAK 0x15
#define NUL 0x00
#define True 1
#define False 0
#define Success -1
#define Failure 0
#define Packet_Size 512
#define Max_Errors 7
#define Max_Time 15
#define WACK ';' /* wait acknowledge */
/* Sender actions */
#define S_Send_Packet 0
#define S_Get_DLE 1
#define S_Get_Num 2
#define S_Get_Seq 3
#define S_Get_Data 4
#define S_Get_Checksum 5
#define S_Timed_Out 6
#define S_Send_NAK 7
/* Receiver actions */
#define R_Get_DLE 0
#define R_Get_B 1
#define R_Get_Seq 2
#define R_Get_Data 3
#define R_Get_Checksum 4
#define R_Send_NAK 5
#define R_Send_ACK 6
/* variables used by external routine that shows transfer status
* in window - provided by user: CBB, Jan/88
*/
extern int datablocks,dataretrys,oursize;
/******************************************/
/* routines called for handling status window;
* provided by user: CBB Jan/88
*/
extern int updl(); /* update download status window numbers */
extern int svcdl(); /* check for wants to abort by windowclose */
/* abort is forced by pushing ^C into the */
/* users kbd input */
extern int upul(); /* update upload status report window #'s */
extern int svcup(); /* check for wants to abort by windowclose */
/* abort is forced by pushing ^C into the */
/* users kbd input */
extern int showdownload(); /* opens window for status, calls recieve */
extern int showupload(); /* opens window for status, calls transmit */
/******************************************/
static short Checksum;
static short
Ch,
Seq_Num,
R_Size, /* Size of receiver buffer */
XOFF_Flag,
Seen_ETX;
static unsigned char
S_Buffer[Packet_Size], /* Sender buffer */
R_Buffer[Packet_Size]; /* Receiver buffer */
static Put_Msg(Text)
char *Text;
{
while (*Text != 0) Put_Char(*Text++);
Put_Char('\015');
Put_Char('\012');
return(0);
}
static Send_Byte(Ch)
int Ch;
{
short TCh;
/* Listen for XOFF from the network */
do
{
while ((TCh = Read_Modem()) >= 0)
{
if (TCh == XON)
{
XOFF_Flag = False;
}
else if (TCh == XOFF)
{
XOFF_Flag = True;
}
}
} while (XOFF_Flag);
while (!Write_Modem(Ch));
return(0);
}
static Send_Masked_Byte(Ch)
short Ch;
{
/* Mask any protocol or flow characters */
/*PutChar(Ch);*/
if (Ch == ETX || Ch == NUL || Ch == ENQ || Ch == DLE || Ch == NAK || Ch == XON || Ch == XOFF)
{
Send_Byte(DLE);
Send_Byte(Ch + '@');
}
else
{
Send_Byte(Ch);
}
return(0);
}
static Send_ACK()
{
Send_Byte(DLE);
Send_Byte(Seq_Num + '0');
return(0);
}
static Read_Byte()
{
if ((Ch = Read_Modem()) < 0)
{
Start_Timer(Max_Time);
do
{
if (Timer_Expired()) return Failure;
} while ((Ch = Read_Modem()) < 0);
}
return Success;
}
static Read_Masked_Byte()
{
Seen_ETX = False;
if (Read_Byte() == Failure) return Failure;
/*PutChar(Ch);*/
if (Ch == DLE)
{
if (Read_Byte() == Failure) return Failure;
Ch &= 0x1F;
}
else if (Ch == ETX) Seen_ETX = True;
return Success;
}
static Do_Checksum(Ch)
short Ch;
{
Checksum <<= 1;
if (Checksum > 255)
{
Checksum = (Checksum & 0xFF) + 1;
}
Checksum += Ch;
if (Checksum > 255)
{
Checksum = (Checksum & 0xFF) + 1;
}
return(0);
}
static int Read_Packet(Action)
/**
* Function:
* Receive a packet from the host.
*
* Inputs:
* Action -- the starting action
*
* Outputs:
* R_Buffer -- contains the packet just received
* R_Size -- length of the packet
*
* Returns:
* success/failure
**/
int Action;
{
short Errors;
short Next_Seq;
Errors = 0;
updl(); /* update download window */
svcdl(); /* watch for user close... */
while (Errors < Max_Errors)
{
switch (Action)
{
case R_Get_DLE:
{
if (Read_Byte() == Failure)
{
Action = R_Send_NAK;
}
else if (Ch == DLE)
{
Action = R_Get_B;
}
else if (Ch == ENQ)
{
Action = R_Send_ACK;
}
break;
}
case R_Get_B:
{
if (Read_Byte() == Failure)
{
Action = R_Send_NAK;
}
else if (Ch == 'B')
{
Action = R_Get_Seq;
}
else
{
Action = R_Get_DLE;
}
break;
}
case R_Get_Seq:
{
if (Read_Byte() == Failure)
{
Action = R_Send_NAK;
}
else
{
Checksum = 0;
Next_Seq = Ch - '0';
Do_Checksum(Ch);
R_Size = 0;
Action = R_Get_Data;
}
break;
}
case R_Get_Data:
{
if (Read_Masked_Byte() == Failure)
{
Action = R_Send_NAK;
}
else if (Seen_ETX)
{
Action = R_Get_Checksum;
}
else if (R_Size == Packet_Size)
{
Action = R_Send_NAK;
}
else
{
R_Buffer[R_Size++] = Ch;
Do_Checksum(Ch);
}
break;
}
case R_Get_Checksum:
{
Do_Checksum(ETX);
if (Read_Masked_Byte() == Failure)
{
Action = R_Send_NAK;
}
else if (Checksum != Ch)
{
Action = R_Send_NAK;
}
else if (Next_Seq == Seq_Num)
{
Action = R_Send_ACK; /* Ignore duplicate packet */
}
else if (Next_Seq != (Seq_Num + 1) % 10)
{
Action = R_Send_NAK;
}
else
{
Seq_Num = Next_Seq;
datablocks++;
return Success;
}
break;
}
case R_Send_NAK:
{
Errors++;
dataretrys++;
Send_Byte(NAK);
Action = R_Get_DLE;
break;
case R_Send_ACK:
Send_ACK();
Action = R_Get_DLE;
break;
}
}
}
return Failure;
}
static int Send_Packet(Size)
/**
* Function:
* Send the specified packet to the host.
*
* Inputs:
* Size -- length of the packet
* S_Buffer -- the packet to send
*
* Outputs:
*
* Returns:
* success/failure
**/
short Size; /* size of packet to send */
{
short Action;
short Next_Seq;
short RCV_Num;
short I;
short Errors;
Next_Seq = (Seq_Num + 1) % 10;
Errors = 0;
Action = S_Send_Packet;
upul(); /* update download window */
svcdl(); /* watch for user close... */
while (Errors < Max_Errors)
{
switch (Action)
{
case S_Send_Packet:
{
Checksum = 0;
Send_Byte(DLE);
Send_Byte('B');
Send_Byte(Next_Seq + '0');
Do_Checksum(Next_Seq + '0');
for (I = 0; I < Size; I++)
{
Send_Masked_Byte(S_Buffer[I]);
Do_Checksum(S_Buffer[I]);
}
Send_Byte(ETX);
Do_Checksum(ETX);
Send_Masked_Byte(Checksum);
Action = S_Get_DLE;
break;
}
case S_Get_DLE:
{
if (Read_Byte() == Failure)
{
Action = S_Timed_Out;
}
else if (Ch == DLE)
{
Action = S_Get_Num;
}
else if (Ch == NAK)
{
Errors++;
dataretrys++;
Action = S_Send_Packet;
}
break;
}
case S_Get_Num:
{
if (Read_Byte() == Failure)
{
Action = S_Timed_Out;
}
else if (Ch >= '0' && Ch <= '9')
{
if (Ch == Seq_Num + '0')
{
Action = S_Get_DLE; /* Ignore duplicate ACK */
}
else if (Ch == Next_Seq + '0')
{
/* Correct sequence number */
Seq_Num = Next_Seq;
return Success;
}
else
{
if (Errors == 0)
{
Action = S_Send_Packet;
}
else
{
Action = S_Get_DLE;
}
}
}
else if (Ch == WACK)
{
/*Delay(50); Sleep for 5 seconds */
Action = S_Get_DLE;
}
else if (Ch == 'B')
{
Action = S_Get_Seq;
}
else
{
Action = S_Get_DLE;
}
break;
}
case S_Get_Seq:
{
/**
* Start of a "B" protocol packet. The only packet that makes
* any sense here is a failure packet.
**/
if (Read_Byte() == Failure)
{
Action = S_Send_NAK;
}
else
{
Checksum = 0;
RCV_Num = Ch - '0';
Do_Checksum(Ch);
I = 0;
Action = S_Get_Data;
}
break;
}
case S_Get_Data:
{
if (Read_Masked_Byte() == Failure)
{
Action = S_Send_NAK;
}
else if (Seen_ETX)
{
Action = S_Get_Checksum;
}
else if (I == Packet_Size)
{
Action = S_Send_NAK;
}
else
{
R_Buffer[I++] = Ch;
Do_Checksum(Ch);
}
break;
}
case S_Get_Checksum:
{
Do_Checksum(ETX);
if (Read_Masked_Byte() == Failure)
{
Action = S_Send_NAK;
}
else if (Checksum != Ch)
{
Action = S_Send_NAK;
}
else if (RCV_Num != (Next_Seq + 1) % 10)
{
Action = S_Send_NAK;
}
else
{
/**
* Assume the packet is failure packet. It makes no
* difference since any other type of packet would be
* invalid anyway. Return failure to caller.
**/
Errors = Max_Errors;
}
break;
}
case S_Timed_Out:
{
Errors++;
dataretrys++;
Action = S_Get_DLE;
break;
}
case S_Send_NAK:
{
Errors++;
dataretrys++;
Send_Byte(NAK);
Action = S_Get_DLE;
break;
}
}
}
toterm("");
toterm("Too many Errors!");
toterm("");
return Failure;
}
static Send_Failure(Code)
/**
* Function:
* Send a failure packet to the host.
*
* Inputs:
* Code -- failure code
*
* Outputs:
*
* Returns:
**/
char Code;
{
S_Buffer[0] = 'F';
S_Buffer[1] = Code;
Send_Packet(2);
return(0);
}
int Receive_File(Name)
/**
* Function:
* Download the specified file from the host.
*
* Inputs:
* Name -- ptr to the file name string
*
* Outputs:
*
* Returns:
* success/failure
**/
char *Name;
{
int Data_File;
if ((Data_File = creat(Name, O_RAW)) == -1)
{
Put_Msg("Cannot create file");
Send_Failure('E');
return Failure;
}
Send_ACK();
for (;;)
{
if (Read_Packet(R_Get_DLE) == Success)
{
switch (R_Buffer[0])
{
case 'N': /* Data packet */
{
Send_ACK();
if (write(Data_File, &R_Buffer[1], R_Size - 1) != R_Size - 1)
{
/* Disk write error */
Put_Msg("Disk write error");
Send_Failure('E');
close(Data_File);
return Failure;
}
if (Wants_To_Abort())
{
/* The user wants to kill the transfer */
Send_Failure('A');
close(Data_File);
return Failure;
}
break;
}
case 'T': /* Transfer packet */
{
if (R_Buffer[1] == 'C') /* Close file */
{
Send_ACK();
close(Data_File);
return Success;
}
else
{
/**
* Unexpected "T" packet. Something is rotten on the
* other end. Send a failure packet to kill the
* transfer cleanly.
**/
Put_Msg("Unexpected packet type");
Send_Failure('E');
close(Data_File);
return Failure;
}
}
case 'F': /* Failure packet */
{
Send_ACK();
close(Data_File);
return Failure;
}
default:
{
return Failure;
}
}
}
else
{
close(Data_File);
return Failure;
}
}
return Failure;
}
int Send_File(Name)
/**
* Function:
* Send the specified file to the host.
*
* Inputs:
* Name -- ptr to the file name string
*
* Outputs:
*
* Returns:
* success/failure
**/
char *Name;
{
int Data_File;
short N;
if ((Data_File = open(Name, O_RDONLY | O_RAW)) == -1)
{
Put_Msg("Cannot access that file");
Send_Failure('E');
return Failure;
}
do
{
S_Buffer[0] = 'N';
N = read(Data_File, &S_Buffer[1], Packet_Size - 1);
if (N > 0)
{
if (Send_Packet(N + 1) == Failure)
{
close(Data_File);
return Failure;
}
if (Wants_To_Abort())
{
Send_Failure('A');
close(Data_File);
return Failure;
}
datablocks++;
}
} while (N > 0);
if (N == 0) /* end of file */
{
close(Data_File);
S_Buffer[0] = 'T';
S_Buffer[1] = 'C';
return Send_Packet(2);
}
else
{
Put_Msg("Disk read error");
Send_Failure('E');
return Failure;
}
return Failure;
}
/*
B PROTOCOL SUMMARY
Block Format:
<DLE><B><blknum+48><command><contents><ETX><checksum>
Defined commands are:
Remote Memory Reference
B Load memory
G Go execute at an address
File Transfer Mode
TE<file name> Erase a file
TR<present><0><new> Rename a file
TU<type><file name> Transfer from Remote to Host
TD<type><file name> Transfer from Host to Remote
N Next block of data
TC Close file
<type> is:
A - ASCII
B - Binary
I - Image
Failure Blocks
FA Failure - Abort
FC Failure - capacity problem
FE Failure - error during processing of last block
FN Failure - function not implemented
*/
int Transfer_File()
/**
* Function:
* Transfer a file from/to the macro to/from the host.
*
* Inputs:
*
* Outputs:
*
* Returns:
* success/failure
**/
{
short I, N,temp;
char Name[64];
XOFF_Flag = False;
Seq_Num = 0;
nowindow();
if (Read_Packet(R_Get_Seq) == Success)
{
if (R_Buffer[0] == 'T') /* transfer packet */
{
if (R_Buffer[1] != 'D' && R_Buffer[1] != 'U') /*Check drctchun*/
{
Send_Failure('N'); /* not UPLOAD or DOWNLOAD */
return Failure;
}
if (R_Buffer[2] != 'A' && R_Buffer[2] != 'B')/*Check file type*/
{
Send_Failure('N'); /* not ASCII or BINARY */
return Failure;
}
if (R_Size - 3 > 63) /* Get the file name length */
{
N = 63;
}
else
{
N = R_Size - 3;
}
for (I = 0; I < N; I++) /* Now actually read the file name */
{
Name[I] = R_Buffer[I + 3];
}
Name[I] = 0; /* NULL terminate the string */
if (R_Buffer[1] == 'U') /* Do Upload transfer */
{
oursize = findsize(Name);
temp = showupload(Name);
}
else /* Do Download transfer */
{
temp = showdownload(Name);
}
nowindow();
return((int)temp);
}
else
{
Send_Failure('E'); /* wrong type of packet */
return Failure;
}
}
else /* didn't read packet successfully */
{
return Failure;
}
return Failure;
}