home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
listings
/
v_08_09
/
8n09104a
< prev
next >
Wrap
Text File
|
1990-06-24
|
13KB
|
480 lines
/*------------------------------------------------
XMODEMS.C
Author Date Description
-------------------------------------------
Jon Ward 22 Apr 90 Initial Revision.
Jon Ward 23 Apr 90 Cleanup and modify for
XMODEM-1K and XMODEM-CRC.
Jon Ward 26 Apr 90 Corrected implementation of
XMODEM-CRC.
------------------------------------------------*/
#define XMODEM_LIB 1
#include <stdio.h>
#include "xmodem.h"
#define STATIC static /* undef for debugging */
/*------------------------------------------------
Local Function Prototypes
------------------------------------------------*/
STATIC int xm_send_file (
FILE *f, /* file pointer */
xblock *xb, /* pointer to block data */
xfunc *xmf); /* xmodem external functions */
STATIC int xm_send_block (
xblock *xb, /* pointer to block data */
xfunc *xmf); /* xmodem external functions */
/*------------------------------------------------
This function sends a file via XMODEM, XMODEM-1K
or XMODEM/CRC. The f argument represents the
file to send that has been opened for reading.
------------------------------------------------*/
int xmodem_send (
int block_size, /* maximum block size */
FILE *f, /* file to write to */
int (*transmit) (char), /* xmit function */
int (*receive) (long, unsigned int *), /* recv function */
void (*dispstat) (long, long, const char *), /* display function */
int (*check_abort) (void)) /* manual abort function */
{
xblock xb; /* block data */
xfunc xmfuncs; /* xmodem external functions */
register int error_count; /* counter for errors */
register int can_count; /* cancel counter */
int error; /* gen error var */
unsigned int rerr; /* received char error */
/*------------------------------------------------
Initialize the function pointer structure.
------------------------------------------------*/
if ((xmfuncs.dispstat = dispstat) == NULL)
xmfuncs.dispstat = xm_no_disp_func;
if ((xmfuncs.check_abort = check_abort) == NULL)
xmfuncs.check_abort = xm_no_abort_func;
if ((xmfuncs.transmit = transmit) == NULL)
return (xm_perror (XERR_XMIT_FUNC, &xmfuncs));
if ((xmfuncs.receive = receive) == NULL)
return (xm_perror (XERR_RCVR_FUNC, &xmfuncs));
/*------------------------------------------------
------------------------------------------------*/
xb.total_block_count = 0L;
xb.total_byte_count = 0L;
(*xmfuncs.dispstat) (0L, 0L, "");
PURGE_RECEIVER(receive);
/*------------------------------------------------
Purge all data from the receive buffer. Then we
wait for 1 full minute. If we receive no
characters during that time, then we return
indicating the file was not transferred. If we
receive a NAK, then we begin transmission. If
we receive an excessive number of garbage
characters, then we return indicating no file
transfer.
------------------------------------------------*/
for (error_count = 0, can_count = 0; 1; )
{
int rcvd_char;
if (error_count >= START_XMIT_RETRY_COUNT)
return (xm_perror (XERR_NAK_TIMEOUT, &xmfuncs));
/* try for 1 minute */
rcvd_char = (*xmfuncs.receive) (START_XMIT_TIMEOUT, &rerr);
if (rerr != 0)
{
xm_send_cancel (xmfuncs.transmit);
return (xm_perror (XERR_CHAR_ERROR, &xmfuncs));
}
switch (rcvd_char)
{
case RECV_TIMEOUT:
xm_send_cancel (xmfuncs.transmit);
return (xm_perror (XERR_NAK_TIMEOUT, &xmfuncs));
case 'C':
xb.crc_used = 1;
break;
case NAK:
xb.crc_used = 0;
break;
case CAN:
if (++can_count >= CAN_COUNT_ABORT)
{
xm_send_cancel (xmfuncs.transmit);
return (xm_perror (XERR_RCVR_CANCEL, &xmfuncs));
}
continue;
default:
can_count = 0;
error_count++;
continue;
}
break;
}
/*------------------------------------------------
Setup the block size and the start of block
character and send the file.
------------------------------------------------*/
if (block_size == XMODEM_1K_BLOCK_SIZE)
{
xb.buflen = XMODEM_1K_BLOCK_SIZE;
xb.start_char = STX;
}
else
{
xb.buflen = XMODEM_BLOCK_SIZE;
xb.start_char = SOH;
}
if ((error = xm_send_file (f, &xb, &xmfuncs)) != XERR_OK)
return (error);
/*------------------------------------------------
Now, we send an EOT to the receiver and we
expect to get an ACK within 1 minute. If we
don't then we timeout and report an error. If
we get any character other than an ACK, we
retransmit the EOT. If we get an ACK, then we
exit with hopeful success.
------------------------------------------------*/
while (1)
{
if ((*xmfuncs.transmit) (EOT) != XMIT_OK)
return (xm_perror (XERR_OFFLINE, &xmfuncs));
switch ((*receive) (START_XMIT_TIMEOUT, &rerr))
{
case RECV_TIMEOUT:
return (xm_perror (XERR_LAST_ACK_TIMEOUT, &xmfuncs));
case ACK:
if (rerr != 0)
continue;
break;
default:
continue;
}
break;
}
return (xm_perror (XERR_OK, &xmfuncs));
}
/*------------------------------------------------
This function transmits the file associated with
the file pointer f according to the requirements
of the XMODEM transfer protocol.
------------------------------------------------*/
STATIC int xm_send_file (
FILE *f, /* file pointer */
register xblock *xb, /* pointer to block data */
register xfunc *xmf) /* xmodem external functions */
{
int done;
/*------------------------------------------------
Repeat until we have sent the whole file.
------------------------------------------------*/
for (done = 0, xb->block_num = 1; !done; xb->block_num++)
{
size_t bytes_read;
int error;
/*------------------------------------------------
Read a block of data. If it was not a full
block, then check for a file error. If it was a
file error, cancel the transfer and return an
error. Otherwise, pad the remainder of the
block with CPM EOFs.
------------------------------------------------*/
bytes_read = fread (xb->buffer, 1, xb->buflen, f);
if (bytes_read != xb->buflen)
{
register int i;
if (ferror (f))
{
xm_send_cancel (xmf->transmit);
return (xm_perror (XERR_FILE_READ, xmf));
}
for (i = bytes_read; i < xb->buflen; i++)
xb->buffer [i] = CPMEOF;
done = -1;
}
/*------------------------------------------------
Calculate the one's complement block number and
send the block.
------------------------------------------------*/
xb->not_block_num = (unsigned char) (255 - xb->block_num);
if ((error = xm_send_block (xb, xmf)) != XERR_OK)
return (error);
}
return (XERR_OK);
}
/*------------------------------------------------
This function transmits the block described by the
structure pointed to by the xb argument. The
transmit and receive arguments point to functions
that transmit and receive data to and from the
modem. A value of 0 is returned to indicate that
the block was successfully sent. A non-zero
return value indicates an offline condition.
------------------------------------------------*/
STATIC int xm_send_block (
register xblock *xb, /* pointer to block data */
register xfunc *xmf) /* xmodem external functions */
{
int error_count;
error_count = 0;
while (1)
{
int i;
int can_count; /* number of CANS received */
int block_resp; /* response to block */
unsigned rerr; /* received char error */
/*------------------------------------------------
If there were too many errors, then exit with
error.
------------------------------------------------*/
if (error_count > BLOCK_RETRY_COUNT)
{
xm_send_cancel (xmf->transmit);
return (xm_perror (XERR_ACK_TIMEOUT, xmf));
}
/*------------------------------------------------
Check for user abort and process it if
applicable.
------------------------------------------------*/
if ((*xmf->check_abort) () != 0)
{
xm_send_cancel (xmf->transmit);
return (xm_perror (XERR_USER_CANCEL, xmf));
}
/*------------------------------------------------
Transmit the block start character (it's
different depending on whether we're sending
1024-BYTE or 128-BYTE packets.
Send the block number.
Send the one's complement of the block number.
------------------------------------------------*/
if ((*xmf->transmit) (xb->start_char) != XMIT_OK)
return (xm_perror (XERR_OFFLINE, xmf));
if ((*xmf->transmit) (xb->block_num) != XMIT_OK)
return (xm_perror (XERR_OFFLINE, xmf));
if ((*xmf->transmit) (xb->not_block_num) != XMIT_OK)
return (xm_perror (XERR_OFFLINE, xmf));
/*------------------------------------------------
Clear the CRC and checksum and send the data
block while building the CRC or checksum.
------------------------------------------------*/
xb->crc = 0;
xb->checksum = 0;
for (i = 0; i < xb->buflen; i++)
{
if ((*xmf->transmit) (xb->buffer [i]) != XMIT_OK)
return (xm_perror (XERR_OFFLINE, xmf));
if (xb->crc_used != 0)
xb->crc = xm_update_CRC (xb->crc, xb->buffer [i]);
else
xb->checksum += xb->buffer [i];
}
/*------------------------------------------------
Send the CRC or checksum. If we send the CRC,
we must send the High BYTE first.
------------------------------------------------*/
if (xb->crc_used == 0)
{
if ((*xmf->transmit) (xb->checksum) != XMIT_OK)
return (xm_perror (XERR_OFFLINE, xmf));
}
else
{
if ((*xmf->transmit) ((unsigned char) (xb->crc >> 8)) != XMIT_OK)
return (xm_perror (XERR_OFFLINE, xmf));
if ((*xmf->transmit) ((unsigned char) (xb->crc & 0xFF)) != XMIT_OK)
return (xm_perror (XERR_OFFLINE, xmf));
}
/*------------------------------------------------
Wait for the receiver to respond with an ACK or
a NAK. If we timeout waiting, then return an
error code. If we get a NAK, retransmit the
block. If we gat an ACK, then return with
status OK. If we receive 2 consecutive CANs,
then we abort the transmission.
------------------------------------------------*/
can_count = 0;
GET_BLOCK_RESPONSE:
block_resp = (*xmf->receive) (BLOCK_RESP_TIMEOUT, &rerr);
if (rerr != 0)
{
xm_perror (XERR_CHAR_ERROR, xmf);
goto GET_BLOCK_RESPONSE;
}
switch (block_resp)
{
case ACK:
error_count = 0;
xb->total_block_count++;
xb->total_byte_count += xb->buflen;
(*xmf->dispstat) (xb->total_block_count,
xb->total_byte_count,
NULL);
break;
case CAN:
if (++can_count >= CAN_COUNT_ABORT)
{
xm_send_cancel (xmf->transmit);
return (xm_perror (XERR_RCVR_CANCEL, xmf));
}
goto GET_BLOCK_RESPONSE;
/*------------------------------------------------
I have seen 2 ways of handling this problem. One
is to return an error indicating that the sender
never received a packet ACK. Another method
retransmits the packet just sent in hopes that
the receiver will get its ACK together. The
second one is what I did.
------------------------------------------------*/
case RECV_TIMEOUT:
error_count++;
#if 0
return (xm_perror (XERR_ACK_TIMEOUT, xmf));
#else
xm_perror (XERR_ACK_TIMEOUT, xmf);
continue;
#endif
case NAK:
xm_perror (XERR_BLOCK_NAK, xmf);
error_count++;
continue;
default:
goto GET_BLOCK_RESPONSE;
}
break;
}
return (XERR_OK);
}
/*------------------------------------------------
This function updates a CRC accumulator for xmodem
CCITT CRC.
------------------------------------------------*/
unsigned int xm_update_CRC (
register unsigned int crc, /* current CRC */
unsigned int c) /* character to add to CRC */
{
static unsigned int crc_polynomial = 0x1021;
register int i;
c <<= 8;
for (i = 0; i < 8; i++)
{
if ((c ^ crc) & 0x8000)
crc = (crc << 1) ^ crc_polynomial;
else
crc <<= 1;
c <<= 1;
}
return (crc);
}
/*------------------------------------------------
This function sends 8 CANs and 8 BSs as done by
YAM. This is used to cancel an X or Y Modem send
or receive.
------------------------------------------------*/
void xm_send_cancel (
int (*transmit) (char)) /* transmit function */
{
register int i;
for (i = 0; i < 8; i++)
(*transmit) (CAN);
for (i = 0; i < 8; i++)
(*transmit) (BACKSPACE);
}
/*------------------------------------------------
------------------------------------------------*/