This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
magicka/Xmodem/xmodemr.c
2016-04-03 09:26:17 +10:00

418 lines
7.8 KiB
C

#ifndef lint
static const char rcsid[] = "$Id: xmodemr.c,v 1.1.1.1 2001/03/08 00:01:48 efalk Exp $" ;
#endif lint
/*
* Copyright (c) 1995 by Edward A. Falk
*/
/**********
*
*
* @ @ @ @ @@@ @@@@ @@@@@ @ @ @@@@
* @ @ @@ @@ @ @ @ @ @ @@ @@ @ @
* @ @ @ @ @ @ @ @ @@@ @ @ @ @@@@
* @ @ @ @ @ @ @ @ @ @ @ @ @ @ @
* @ @ @ @ @ @@@ @@@@ @@@@@ @ @ @ @ @
*
* XMODEMR - receiver side of xmodem/ymodem protocol
*
* Caller sets flags defined in xmodem.h as appropriate.
* (default is basic xmodem)
*
* This code is designed to be called from inside a larger
* program, so it is implemented as a state machine where
* practical.
*
*
* functions:
*
* XmodemRInit(char *filename, Protocol p)
* Initiate a receive
*
* XmodemRTimeout()
* called after timeout expired
*
* XmodemRRcv(char c)
* called after character received
*
* XmodemRAbort()
* abort transfer
*
* all functions return 0 on success, 1 on abort
*
*
*
* Edward A. Falk
*
* January, 1995
*
*
*
**********/
#include <stdio.h>
#include <sys/types.h>
#include "xmodem.h"
/* TODO: WXmodem */
bool xmodem1k = False ;
Protocol protocol = Xmodem ;
int xmTfd = -1 ;
int xmRfd = -1 ;
int xmTimeout = 0 ;
char xmDefPath[MAXPATHLEN] ;
char xmFilename[MAXPATHLEN] ;
typedef enum {
Start, /* waiting to begin */
Init, /* sent initial NAK, 'C' or 'W' */
Packet, /* receiving a packet */
Wait, /* wait for start of next packet */
} XmodemState ;
static bool ymodem ;
static XmodemState state = Start ;
static int errorCount = 0 ;
static int errorCount2 ;
static int ignoreCount ;
static int eotCount ; /* count EOT's, reject first one */
static int inCount ; /* characters received this packet */
static int pktLen ; /* length of this packet data */
static int pktHdrLen ; /* id, cmpl, checksum or crc */
static char packet[MAXPACKET+5], *optr ;
static int packetId ; /* id of last received packet */
static int packetCount ; /* # packets received */
static FILE *ofile ; /* output file fd */
static int fileLen, fileDate, fileMode ;
static int XmodemRStart() ;
static int processPacket() ;
static int rejectPacket() ;
static int acceptPacket() ;
int
XmodemRInit( char *file, Protocol prot )
{
int err ;
state = Start ;
protocol = prot ;
ymodem = prot == Ymodem || prot == YmodemG ;
if( ymodem )
strcpy(xmDefPath, file) ;
else
strcpy(xmFilename, file) ;
eotCount = errorCount = errorCount2 = 0 ;
if( err=XmodemRStart() )
return err ;
state = Init ;
packetId = ymodem ? 255 : 0 ;
packetCount = 0 ;
pktHdrLen = protocol == Xmodem ? 3 : 4 ;
return 0 ;
}
/* send startup character */
static int
XmodemRStart()
{
static char pchars[5] = {NAK,'C','W','C','C'} ;
static int timeouts[5] = {INITTO, INITTO2, INITTO2, INITTO, INITTO} ;
char c = pchars[(int)protocol] ;
int err ;
if( err=sendFlush(c) )
return err ;
xmTimeout = timeouts[(int)protocol] ;
return 0 ;
}
int
XmodemRRcv(char c)
{
errorCount = 0 ;
switch( state ) {
case Start: /* shouldn't happen, ignore */
if( c == CAN )
return XmErrCancel ;
break ;
case Init: /* waiting */
case Wait:
switch( c ) {
case SOH:
case STX:
pktLen = c == STX ? 1024 : 128 ;
inCount = 0 ;
optr = packet ;
state = Packet ;
xmTimeout = PKTTO ;
break ;
case EOT:
if( ++eotCount > 1 ) {
sendFlush(ACK) ;
if( ymodem )
return XmodemRInit() ; /* restart protocol */
else
return XmDone ;
}
else
return rejectPacket() ; /* make xmitter try again */
case CAN: return XmErrCancel ;
default: /* ignore all others */
if( ++ignoreCount > 1030 ) {
ignoreCount = 0 ;
return sendFlush(NAK) ;
}
break ;
}
break ;
case Packet: /* mid packet */
*optr++ = c ;
if( ++inCount >= pktLen + pktHdrLen )
ProcessPacket() ;
break ;
}
return 0 ;
}
int
XmodemRTimeout()
{
if( ++errorCount > MAXERROR )
return state == Init ? XmErrInitTo : XmErrRcvTo ;
switch( state ) {
case Start: return -1 ; /* shouldn't happen */
case Init:
if( ++errorCount2 >= 3 )
switch( protocol ) {
case WXmodem: protocol = XmodemCrc ; errorCount2 = 0 ; break ;
case XmodemCrc: protocol = Xmodem ; pktHdrLen = 3 ; break ;
}
return XmodemRStart() ;
case Wait: /* timeout while waiting */
case Packet: /* timeout in mid packet */
return rejectPacket() ;
}
}
int
XmodemRAbort()
{
return sendCancel() ;
}
static int
ProcessPacket()
{
int id = (u_char)packet[0] ;
int idc = (u_char)packet[1] ;
int i ;
if( idc != 255-id )
return rejectPacket() ;
if( id == packetId ) /* duplicate */
return acceptPacket() ;
if( id != (packetId+1)%256 ) { /* out of sequence */
(void) sendCancel() ;
return XmErrSequence ;
}
if( protocol == Xmodem )
{
/* compute checksum */
register int csum = calcChecksum(packet+2, pktLen) ;
if( csum != (u_char) packet[2+pktLen] )
return rejectPacket() ;
}
else
{
int crc0 = (u_char)packet[pktLen+2] << 8 | (u_char)packet[pktLen+3] ;
int crc1 = calcrc(packet+2, pktLen) ;
if( crc0 != crc1 )
return rejectPacket() ;
}
/* it's a good packet */
packetId = (packetId+1)%256 ;
/* is this the first packet? */
if( packetCount == 0 )
{
if( ymodem )
{
if( packet[2] == '\0' ) /* last file */
{
(void) acceptPacket() ;
return XmDone ;
}
if( packet[2] == '/' )
strcpy(xmFilename, packet+2) ;
else {
strcpy(xmFilename, xmDefPath) ;
strcat(xmFilename, packet+2) ;
}
fileLen = fileDate = fileMode = -1 ;
sscanf(packet+2+strlen(packet)+1, "%d %o %o",
&fileLen, &fileDate, &fileMode) ;
}
if( (ofile = fopen(xmFilename, "w")) == NULL ) {
sendCancel() ;
return XmErrCantOpen ;
}
if( ymodem ) {
packetCount = 1 ;
(void) acceptPacket() ;
return sendFlush('C') ;
}
else
state = Packet ;
}
++packetCount ;
/* TODO: ymodem: if this is last packet, truncate it */
if( (i=fwrite(packet+2, 1, pktLen, ofile)) != pktLen )
{
sendCancel() ;
return XmErrSys ;
}
else
return acceptPacket() ;
}
static int
rejectPacket()
{
state = Wait ;
xmTimeout = INITTO ;
return sendFlush(NAK) ;
}
static int
acceptPacket()
{
state = Wait ;
xmTimeout = INITTO ;
return sendFlush(ACK) ;
}
#ifdef COMMENT /* stand-alone testing */
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/termios.h>
main(argc,argv)
int argc ;
char **argv ;
{
struct termios old_settings, new_settings ;
fd_set readfds ;
struct timeval timeout ;
int i ;
int len ;
char buffer[1024] ;
bool done = False ;
if( argc < 2 )
exit(2) ;
xmTfd = xmRfd = open(argv[1], O_RDWR) ;
if( xmTfd == -1 )
exit(1) ;
tcgetattr(xmTfd,&old_settings) ;
new_settings = old_settings ;
new_settings.c_iflag &=
~(ISTRIP|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXOFF|IMAXBEL) ;
new_settings.c_oflag = 0 ;
new_settings.c_cflag = B300|CS8|CREAD|CLOCAL ;
new_settings.c_lflag = 0 ;
new_settings.c_cc[VMIN] = 32 ;
new_settings.c_cc[VTIME] = 1 ;
tcsetattr(xmTfd,TCSADRAIN, &new_settings) ;
xmodem1k = 0 ;
done = XmodemRInit("foo", XmodemCrc) != 0 ;
#ifdef COMMENT
xmodem1k = 1 ;
done = XmodemRInit("./", Ymodem) != 0 ;
#endif /* COMMENT */
while(!done)
{
FD_ZERO(&readfds) ;
FD_SET(xmTfd, &readfds) ;
timeout.tv_sec = xmTimeout ;
timeout.tv_usec = 0 ;
i = select(xmTfd+1, &readfds,NULL,NULL, &timeout) ;
if( i<0 )
perror("select") ;
else if( i==0 )
done = XmodemRTimeout() != 0 ;
else {
len = read(xmTfd, buffer, sizeof(buffer)) ;
for(i=0; !done && i<len; ++i)
done = XmodemRRcv(buffer[i]) != 0 ;
}
}
tcsetattr(xmTfd,TCSADRAIN, &old_settings) ;
exit(0) ;
}
#endif /* COMMENT */