901 lines
18 KiB
C
901 lines
18 KiB
C
#ifndef lint
|
||
static const char rcsid[] = "$Id: zmodem.c,v 1.1.1.1 2001/03/08 00:01:48 efalk Exp $" ;
|
||
#endif
|
||
|
||
/*
|
||
* Copyright (c) 1995 by Edward A. Falk
|
||
*/
|
||
|
||
|
||
/**********
|
||
*
|
||
*
|
||
* @@@@@ @ @ @@@ @@@@ @@@@@ @ @
|
||
* @ @@ @@ @ @ @ @ @ @@ @@
|
||
* @ @ @ @ @ @ @ @ @@@ @ @ @
|
||
* @ @ @ @ @ @ @ @ @ @ @ @
|
||
* @@@@@ @ @ @ @@@ @@@@ @@@@@ @ @ @
|
||
*
|
||
* ZMODEM - main logic parser for zmodem library
|
||
*
|
||
*
|
||
* Routines provided here:
|
||
*
|
||
*
|
||
* name (args)
|
||
* Brief description.
|
||
*
|
||
* int ZmodemRcv(u_char *buffer, int len, ZModem *info)
|
||
* Call whenever characters are received. If this function
|
||
* returns ZmDone, previous function has completed successfully,
|
||
* either call ZmodemTFile() to start next file, or call
|
||
* ZmodemTFinish() to terminate the session.
|
||
*
|
||
*
|
||
* int
|
||
* ZmodemTimeout(ZModem *info)
|
||
* Call whenever the timeout period expires and no
|
||
* characters have been received.
|
||
*
|
||
* int
|
||
* ZmodemAttention(ZModem *info)
|
||
* Call whenever the attention sequence has been received
|
||
* from the remote end. It is safe to call this function
|
||
* from an interrupt handler.
|
||
*
|
||
* int
|
||
* ZmodemAbort(ZModem *info)
|
||
* Call to abort transfer. Physical connection remains
|
||
* open until you close it.
|
||
*
|
||
*
|
||
*
|
||
*
|
||
* Edward A. Falk
|
||
*
|
||
* January, 1995
|
||
*
|
||
*
|
||
*
|
||
**********/
|
||
|
||
#include <stdio.h>
|
||
|
||
/****
|
||
*
|
||
* Constants, typedefs, externals, globals, statics, macros, block data
|
||
*
|
||
****/
|
||
|
||
|
||
|
||
/* TODO: sample input before initial send */
|
||
/* TODO: more intelligent timeout dispatch */
|
||
/* TODO: read all pending input before sending next data packet out */
|
||
/* TODO: if received ZDATA while waiting for ZFILE/ZFIN, it's probably
|
||
leftovers */
|
||
/* TODO: enable flow control for zmodem, disable for X/YModem */
|
||
|
||
#include <ctype.h>
|
||
#include <errno.h>
|
||
#include <string.h>
|
||
|
||
#include "zmodem.h"
|
||
#include "crctab.h"
|
||
|
||
static u_char zeros[4] = {0,0,0,0} ;
|
||
|
||
|
||
extern int YrcvChar( char c, register ZModem *info ) ;
|
||
extern int YrcvTimeout( register ZModem *info ) ;
|
||
extern void ZIdleStr(u_char *buffer, int len, ZModem *info) ;
|
||
|
||
|
||
/* LEXICAL BOX: handle characters received from remote end.
|
||
* These may be header, data or noise.
|
||
*
|
||
* This section is a finite state machine for parsing headers
|
||
* and reading input data. The info->chrCount field is effectively
|
||
* the state variable.
|
||
*/
|
||
|
||
static int FinishChar( char c, register ZModem *info ) ;
|
||
static int DataChar( u_char c, register ZModem *info ) ;
|
||
static int HdrChar( u_char c, register ZModem *info ) ;
|
||
static int IdleChar(u_char c, register ZModem *info) ;
|
||
extern int YsendChar() ;
|
||
|
||
static int ZProtocol(), ZDataReceived() ;
|
||
|
||
|
||
int
|
||
ZmodemRcv( register u_char *str, int len, register ZModem *info )
|
||
{
|
||
register u_char c ;
|
||
int err ;
|
||
|
||
info->rcvlen = len ;
|
||
|
||
while( --info->rcvlen >= 0 )
|
||
{
|
||
c = *str++ ;
|
||
|
||
if( c == CAN ) {
|
||
if( ++info->canCount >= 5 ) {
|
||
ZStatus(RmtCancel, 0, NULL) ;
|
||
return ZmErrCancel ;
|
||
}
|
||
}
|
||
else
|
||
info->canCount = 0 ;
|
||
|
||
if( info->InputState == Ysend ) {
|
||
if( (err = YsendChar(c, info)) )
|
||
return err ;
|
||
}
|
||
else if( info->InputState == Yrcv ) {
|
||
if( (err = YrcvChar(c, info)) )
|
||
return err ;
|
||
}
|
||
|
||
else if( c != XON && c != XOFF )
|
||
{
|
||
/* now look at what we have */
|
||
|
||
switch( info->InputState )
|
||
{
|
||
case Idle:
|
||
if( (err = IdleChar(c, info)) )
|
||
return err ;
|
||
break ;
|
||
|
||
case Inhdr:
|
||
if( (err = HdrChar(c, info)) )
|
||
return err ;
|
||
break ;
|
||
|
||
case Indata:
|
||
if( (err = DataChar(c, info)) )
|
||
return err ;
|
||
break ;
|
||
|
||
case Finish:
|
||
if( (err = FinishChar(c, info)) )
|
||
return err ;
|
||
break ;
|
||
|
||
default:
|
||
break ;
|
||
}
|
||
}
|
||
}
|
||
|
||
return 0 ;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/* handle character input while idling
|
||
* looking for ZPAD-ZDLE sequence which introduces a header
|
||
*/
|
||
|
||
static int
|
||
IdleChar(u_char c, register ZModem *info)
|
||
{
|
||
if( info->chrCount == 0 )
|
||
{
|
||
if( c == ZPAD )
|
||
++info->chrCount ;
|
||
else if( info->state == Sending && ++info->noiseCount > MaxNoise )
|
||
info->waitflag = 1 ;
|
||
else if( info->state == TStart && (c == 'C' || c == 'G' || c == NAK) )
|
||
{
|
||
/* switch to ymodem */
|
||
info->state = YTStart ;
|
||
info->InputState = Ysend ;
|
||
info->Protocol = YMODEM ;
|
||
return YsendChar(c, info) ;
|
||
}
|
||
else
|
||
ZIdleStr(&c, 1, info) ;
|
||
}
|
||
|
||
else
|
||
{
|
||
switch( c ) {
|
||
case ZPAD:
|
||
++info->chrCount ;
|
||
break ;
|
||
case ZDLE:
|
||
info->InputState = Inhdr ;
|
||
info->chrCount=0 ;
|
||
break ;
|
||
default:
|
||
while( --info->chrCount >= 0 )
|
||
ZIdleStr((u_char *)"*", 1, info) ;
|
||
info->chrCount = 0 ;
|
||
break ;
|
||
}
|
||
}
|
||
return 0 ;
|
||
}
|
||
|
||
|
||
|
||
static u_int
|
||
rcvHex( u_int i, char c )
|
||
{
|
||
if( c <= '9' )
|
||
c -= '0' ;
|
||
else if( c <= 'F' )
|
||
c -= 'A'-10 ;
|
||
else
|
||
c -= 'a'-10 ;
|
||
return (i<<4)+c ;
|
||
}
|
||
|
||
/* handle character input in a header */
|
||
|
||
static int
|
||
HdrChar( u_char c, register ZModem *info )
|
||
{
|
||
int i ;
|
||
int crc=0 ;
|
||
|
||
if( c == ZDLE ) {
|
||
info->escape = 1 ;
|
||
return 0 ;
|
||
}
|
||
|
||
if( info->escape ) {
|
||
info->escape = 0 ;
|
||
switch( c ) {
|
||
case ZRUB0: c = 0177 ; break ;
|
||
case ZRUB1: c = 0377 ; break ;
|
||
default: c ^= 0100 ; break ;
|
||
}
|
||
}
|
||
|
||
if( info->chrCount == 0 ) { /* waiting for format */
|
||
switch( c ) {
|
||
case ZHEX:
|
||
case ZBIN:
|
||
case ZBIN32:
|
||
info->DataType = c ;
|
||
info->chrCount = 1 ;
|
||
info->crc = (info->DataType != ZBIN32) ? 0 : 0xffffffffL ;
|
||
memset(info->hdrData,0,sizeof(info->hdrData)) ;
|
||
break ;
|
||
default:
|
||
info->InputState = Idle ;
|
||
info->chrCount = 0 ;
|
||
return ZXmitHdrHex(ZNAK, zeros, info) ;
|
||
}
|
||
return 0 ;
|
||
}
|
||
|
||
|
||
switch( info->DataType ) {
|
||
/* hex header is 14 hex digits, cr, lf. Optional xon is ignored */
|
||
case ZHEX:
|
||
if( info->chrCount <= 14 && !isxdigit(c) ) {
|
||
info->InputState = Idle ;
|
||
info->chrCount = 0 ;
|
||
return ZXmitHdrHex(ZNAK, zeros, info) ;
|
||
}
|
||
|
||
if( info->chrCount <= 14 ) {
|
||
i = (info->chrCount-1)/2 ;
|
||
info->hdrData[i] = rcvHex(info->hdrData[i], c) ;
|
||
}
|
||
|
||
if( info->chrCount == 16 ) {
|
||
crc = 0 ;
|
||
for(i=0; i<7; ++i)
|
||
crc = updcrc(info->hdrData[i], crc) ;
|
||
info->InputState = Idle ;
|
||
info->chrCount = 0 ;
|
||
if( (crc&0xffff) != 0 )
|
||
return ZXmitHdrHex(ZNAK, zeros, info) ;
|
||
else
|
||
return ZProtocol(info) ;
|
||
}
|
||
else
|
||
++info->chrCount ;
|
||
break ;
|
||
|
||
|
||
case ZBIN:
|
||
/* binary header is type, 4 bytes data, 2 bytes CRC */
|
||
info->hdrData[info->chrCount-1] = c ;
|
||
info->crc = updcrc(c, info->crc) ;
|
||
if( ++info->chrCount > 7 ) {
|
||
info->InputState = Idle ;
|
||
info->chrCount = 0 ;
|
||
if( (crc&0xffff) != 0 )
|
||
return ZXmitHdrHex(ZNAK, zeros, info) ;
|
||
else
|
||
return ZProtocol(info) ;
|
||
}
|
||
break ;
|
||
|
||
|
||
case ZBIN32:
|
||
/* binary32 header is type, 4 bytes data, 4 bytes CRC */
|
||
info->hdrData[info->chrCount-1] = c ;
|
||
info->crc = UPDC32(c, info->crc) ;
|
||
if( ++info->chrCount > 9 ) {
|
||
info->InputState = Idle ;
|
||
info->chrCount = 0 ;
|
||
if( info->crc != 0xdebb20e3 ) /* see note below */
|
||
return ZXmitHdrHex(ZNAK, zeros, info) ;
|
||
else
|
||
return ZProtocol(info) ;
|
||
}
|
||
break ;
|
||
}
|
||
return 0 ;
|
||
}
|
||
|
||
/* handle character input in a data buffer */
|
||
|
||
static int
|
||
DataChar( u_char c, register ZModem *info )
|
||
{
|
||
if( c == ZDLE ) {
|
||
info->escape = 1 ;
|
||
return 0 ;
|
||
}
|
||
|
||
if( info->escape ) {
|
||
info->escape = 0 ;
|
||
switch( c ) {
|
||
case ZCRCE:
|
||
case ZCRCG:
|
||
case ZCRCQ:
|
||
case ZCRCW:
|
||
info->PacketType = c ;
|
||
info->crcCount = (info->DataType == ZBIN32) ? 4 : 2 ;
|
||
if( info->DataType == ZBIN )
|
||
info->crc = updcrc(c, info->crc) ;
|
||
else
|
||
info->crc = UPDC32(c, info->crc) ;
|
||
return 0 ;
|
||
case ZRUB0: c = 0177 ; break ;
|
||
case ZRUB1: c = 0377 ; break ;
|
||
default: c ^= 0100 ; break ;
|
||
}
|
||
}
|
||
|
||
|
||
switch( info->DataType ) {
|
||
/* TODO: are hex data packets ever used? */
|
||
|
||
case ZBIN:
|
||
info->crc = updcrc(c, info->crc) ;
|
||
if( info->crcCount == 0 )
|
||
info->buffer[info->chrCount++] = c ;
|
||
else if( --info->crcCount == 0 ) {
|
||
return ZDataReceived(info, (info->crc&0xffff) == 0) ;
|
||
}
|
||
break ;
|
||
|
||
|
||
case ZBIN32:
|
||
info->crc = UPDC32(c, info->crc) ;
|
||
if( info->crcCount == 0 )
|
||
info->buffer[info->chrCount++] = c ;
|
||
else if( --info->crcCount == 0 ) {
|
||
return ZDataReceived(info, info->crc == 0xdebb20e3) ;
|
||
}
|
||
break ;
|
||
}
|
||
return 0 ;
|
||
}
|
||
|
||
|
||
/* wait for "OO" */
|
||
|
||
static int
|
||
FinishChar( char c, register ZModem *info )
|
||
{
|
||
if( c == 'O' ) {
|
||
if( ++info->chrCount >= 2 )
|
||
return ZmDone ;
|
||
}
|
||
else
|
||
info->chrCount = 0 ;
|
||
return 0 ;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
int ZPF() ;
|
||
int Ignore() ;
|
||
int AnswerChallenge() ;
|
||
int GotAbort() ;
|
||
int GotCancel() ;
|
||
int GotCommand() ;
|
||
int GotStderr() ;
|
||
int RetDone() ;
|
||
static int GotCommandData() ;
|
||
static int GotStderrData() ;
|
||
|
||
/* PROTOCOL LOGIC: This section of code handles the actual
|
||
* protocol. This is also driven by a finite state machine
|
||
*
|
||
* State tables are sorted by approximate frequency order to
|
||
* reduce search time.
|
||
*/
|
||
|
||
/* Extra ZRINIT headers are the receiver trying to resync. */
|
||
|
||
|
||
|
||
/* If compiling for Send Only or Receive Only, convert table
|
||
* entries to no-ops so we don't have to link zmodem[rt].o
|
||
*/
|
||
|
||
#if SendOnly
|
||
#define RStartOps DoneOps
|
||
#define RSinitWaitOps DoneOps
|
||
#define RFileNameOps DoneOps
|
||
#define RCrcOps DoneOps
|
||
#define RFileOps DoneOps
|
||
#define RDataOps DoneOps
|
||
#define RFinishOps DoneOps
|
||
#define GotFileName Ignore
|
||
#define ResendCrcReq Ignore
|
||
#define GotSinitData Ignore
|
||
#define ResendRpos Ignore
|
||
#define GotFileData Ignore
|
||
#define SendRinit Ignore
|
||
#else
|
||
extern StateTable RStartOps[] ;
|
||
extern StateTable RSinitWaitOps[] ;
|
||
extern StateTable RFileNameOps[] ;
|
||
extern StateTable RCrcOps[] ;
|
||
extern StateTable RFileOps[] ;
|
||
extern StateTable RDataOps[] ;
|
||
extern StateTable RFinishOps[] ;
|
||
extern int GotFileName() ;
|
||
extern int ResendCrcReq() ;
|
||
extern int GotSinitData() ;
|
||
extern int ResendRpos() ;
|
||
extern int GotFileData() ;
|
||
extern int SendRinit() ;
|
||
#endif
|
||
|
||
#if RcvOnly
|
||
#define TStartOps DoneOps
|
||
#define TInitOps DoneOps
|
||
#define FileWaitOps DoneOps
|
||
#define CrcWaitOps DoneOps
|
||
#define SendingOps DoneOps
|
||
#define SendDoneOps DoneOps
|
||
#define SendWaitOps DoneOps
|
||
#define SendEofOps DoneOps
|
||
#define TFinishOps DoneOps
|
||
#define SendMoreFileData Ignore
|
||
#else
|
||
extern StateTable TStartOps[] ;
|
||
extern StateTable TInitOps[] ;
|
||
extern StateTable FileWaitOps[] ;
|
||
extern StateTable CrcWaitOps[] ;
|
||
extern StateTable SendingOps[] ;
|
||
extern StateTable SendDoneOps[] ;
|
||
extern StateTable SendWaitOps[] ;
|
||
extern StateTable SendEofOps[] ;
|
||
extern StateTable TFinishOps[] ;
|
||
extern int SendMoreFileData() ;
|
||
#endif
|
||
|
||
|
||
|
||
static StateTable CommandDataOps[] = {
|
||
#ifdef COMMENT
|
||
{ZRQINIT,f,1,1},
|
||
{ZRINIT,f,1,1},
|
||
{ZSINIT,f,1,1},
|
||
{ZACK,f,1,1},
|
||
{ZFILE,f,1,1},
|
||
{ZSKIP,f,1,1},
|
||
{ZNAK,f,1,1},
|
||
{ZABORT,f,1,1,TFinish},
|
||
{ZFIN,f,1,1},
|
||
{ZRPOS,f,1,1},
|
||
{ZDATA,f,1,1},
|
||
{ZEOF,f,1,1},
|
||
{ZFERR,f,1,1,TFinish},
|
||
{ZCRC,f,1,1},
|
||
{ZCHALLENGE,f,1,1},
|
||
{ZCOMPL,f,1,1},
|
||
{ZCAN,f,1,1},
|
||
{ZFREECNT,f,1,1},
|
||
{ZCOMMAND,f,1,1},
|
||
{ZSTDERR,f,1,1},
|
||
#endif /* COMMENT */
|
||
{99,ZPF,0,0,CommandData},
|
||
} ;
|
||
|
||
static StateTable CommandWaitOps[] = {
|
||
#ifdef COMMENT
|
||
{ZRQINIT,f,1,1},
|
||
{ZRINIT,f,1,1},
|
||
{ZSINIT,f,1,1},
|
||
{ZACK,f,1,1},
|
||
{ZFILE,f,1,1},
|
||
{ZSKIP,f,1,1},
|
||
{ZNAK,f,1,1},
|
||
{ZABORT,f,1,1,TFinish},
|
||
{ZFIN,f,1,1},
|
||
{ZRPOS,f,1,1},
|
||
{ZDATA,f,1,1},
|
||
{ZEOF,f,1,1},
|
||
{ZFERR,f,1,1,TFinish},
|
||
{ZCRC,f,1,1},
|
||
{ZCHALLENGE,f,1,1},
|
||
{ZCOMPL,f,1,1},
|
||
{ZCAN,f,1,1},
|
||
{ZFREECNT,f,1,1},
|
||
{ZCOMMAND,f,1,1},
|
||
{ZSTDERR,f,1,1},
|
||
#endif /* COMMENT */
|
||
{99,ZPF,0,0,CommandWait},
|
||
} ;
|
||
|
||
static StateTable StderrDataOps[] = {
|
||
#ifdef COMMENT
|
||
{ZRQINIT,f,1,1},
|
||
{ZRINIT,f,1,1},
|
||
{ZSINIT,f,1,1},
|
||
{ZACK,f,1,1},
|
||
{ZFILE,f,1,1},
|
||
{ZSKIP,f,1,1},
|
||
{ZNAK,f,1,1},
|
||
{ZABORT,f,1,1,TFinish},
|
||
{ZFIN,f,1,1},
|
||
{ZRPOS,f,1,1},
|
||
{ZDATA,f,1,1},
|
||
{ZEOF,f,1,1},
|
||
{ZFERR,f,1,1,TFinish},
|
||
{ZCRC,f,1,1},
|
||
{ZCHALLENGE,f,1,1},
|
||
{ZCOMPL,f,1,1},
|
||
{ZCAN,f,1,1},
|
||
{ZFREECNT,f,1,1},
|
||
{ZCOMMAND,f,1,1},
|
||
{ZSTDERR,f,1,1},
|
||
#endif /* COMMENT */
|
||
{99,ZPF,0,0,StderrData},
|
||
} ;
|
||
|
||
static StateTable DoneOps[] = {
|
||
{99,ZPF,0,0,Done},
|
||
} ;
|
||
|
||
|
||
static StateTable *tables[] = {
|
||
RStartOps,
|
||
RSinitWaitOps,
|
||
RFileNameOps,
|
||
RCrcOps,
|
||
RFileOps,
|
||
RDataOps,
|
||
RDataOps, /* RDataErr is the same as RData */
|
||
RFinishOps,
|
||
|
||
TStartOps,
|
||
TInitOps,
|
||
FileWaitOps,
|
||
CrcWaitOps,
|
||
SendingOps,
|
||
SendWaitOps,
|
||
SendDoneOps,
|
||
SendEofOps,
|
||
TFinishOps,
|
||
|
||
CommandDataOps,
|
||
CommandWaitOps,
|
||
StderrDataOps,
|
||
DoneOps,
|
||
} ;
|
||
|
||
|
||
char *hdrnames[] = {
|
||
"ZRQINIT",
|
||
"ZRINIT",
|
||
"ZSINIT",
|
||
"ZACK",
|
||
"ZFILE",
|
||
"ZSKIP",
|
||
"ZNAK",
|
||
"ZABORT",
|
||
"ZFIN",
|
||
"ZRPOS",
|
||
"ZDATA",
|
||
"ZEOF",
|
||
"ZFERR",
|
||
"ZCRC",
|
||
"ZCHALLENGE",
|
||
"ZCOMPL",
|
||
"ZCAN",
|
||
"ZFREECNT",
|
||
"ZCOMMAND",
|
||
"ZSTDERR",
|
||
} ;
|
||
|
||
|
||
|
||
/* This function is called (indirectly) by the ZmodemRcv()
|
||
* function when a full header has been received.
|
||
*/
|
||
|
||
static int
|
||
ZProtocol( register ZModem *info )
|
||
{
|
||
register StateTable *table ;
|
||
|
||
zmodemlog("received %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
|
||
hdrnames[info->hdrData[0]], info->hdrData[1],
|
||
info->hdrData[2], info->hdrData[3], info->hdrData[4],
|
||
ZDec4(info->hdrData+1)) ;
|
||
|
||
/* Flags are sent in F3 F2 F1 F0 order. Data is sent in P0 P1 P2 P3 */
|
||
|
||
info->timeoutCount = 0 ;
|
||
info->noiseCount = 0 ;
|
||
|
||
table = tables[(int)info->state] ;
|
||
while( table->type != 99 && table->type != info->hdrData[0] )
|
||
++table ;
|
||
|
||
zmodemlog(" state %s => %s, iflush=%d, oflush=%d, call %x\n",
|
||
sname(info), sname2(table->newstate), table->IFlush,
|
||
table->OFlush, table->func) ;
|
||
|
||
info->state = table->newstate ;
|
||
if( table->IFlush ) {info->rcvlen = 0 ; ZIFlush(info) ;}
|
||
if( table->OFlush ) ZOFlush(info) ;
|
||
return table->func(info) ;
|
||
}
|
||
|
||
|
||
static int
|
||
ZDataReceived( register ZModem *info, int crcGood )
|
||
{
|
||
switch( info->state ) {
|
||
case RSinitWait: return GotSinitData(info, crcGood) ;
|
||
case RFileName: return GotFileName(info, crcGood) ;
|
||
case RData: return GotFileData(info, crcGood) ;
|
||
case CommandData: return GotCommandData(info, crcGood) ;
|
||
case StderrData: return GotStderrData(info, crcGood) ;
|
||
default: return ZPF(info) ;
|
||
}
|
||
}
|
||
|
||
|
||
int
|
||
ZmodemTimeout( register ZModem *info )
|
||
{
|
||
/* timed out while waiting for input */
|
||
|
||
++info->timeoutCount ;
|
||
|
||
zmodemlog("timeout %d [%s]\n", info->timeoutCount, sname(info) ) ;
|
||
|
||
switch( info->state ) {
|
||
/* receive */
|
||
case RStart: /* waiting for INIT frame from other end */
|
||
if( info->timeoutCount > 4 )
|
||
return YmodemRInit(info) ;
|
||
|
||
case RSinitWait:
|
||
case RFileName:
|
||
if( info->timeout > 0 )
|
||
ZStatus(SndTimeout, info->timeoutCount, NULL) ;
|
||
if( info->timeoutCount > 4 )
|
||
return ZmErrRcvTo ;
|
||
info->state = RStart ;
|
||
return SendRinit(info) ;
|
||
|
||
case RCrc:
|
||
case RFile:
|
||
case RData:
|
||
ZStatus(SndTimeout, info->timeoutCount, NULL) ;
|
||
if( info->timeoutCount > 2 ) {
|
||
info->timeoutCount = 0 ;
|
||
info->state = RStart ;
|
||
return SendRinit(info) ;
|
||
}
|
||
return info->state == RCrc ? ResendCrcReq(info) : ResendRpos(info) ;
|
||
|
||
case RFinish:
|
||
ZStatus(SndTimeout, info->timeoutCount, NULL) ;
|
||
return ZmDone ;
|
||
|
||
case YRStart:
|
||
case YRDataWait:
|
||
case YRData:
|
||
case YREOF:
|
||
return YrcvTimeout(info) ;
|
||
|
||
/* transmit */
|
||
case TStart: /* waiting for INIT frame from other end */
|
||
case TInit: /* sent INIT, waiting for ZACK */
|
||
case FileWait: /* sent file header, waiting for ZRPOS */
|
||
case CrcWait: /* sent file crc, waiting for ZRPOS */
|
||
case SendWait: /* waiting for ZACK */
|
||
case SendEof: /* sent EOF, waiting for ZACK */
|
||
case TFinish: /* sent ZFIN, waiting for ZFIN */
|
||
case YTStart:
|
||
case YTFile:
|
||
case YTDataWait:
|
||
case YTData:
|
||
case YTEOF:
|
||
case YTFin:
|
||
ZStatus(RcvTimeout,0,NULL) ;
|
||
return ZmErrRcvTo ;
|
||
|
||
case Sending: /* sending data subpackets, ready for int */
|
||
return SendMoreFileData(info) ;
|
||
|
||
/* general */
|
||
case CommandData: /* waiting for command data */
|
||
case StderrData: /* waiting for stderr data */
|
||
return ZmErrSndTo ;
|
||
case CommandWait: /* waiting for command to execute */
|
||
return ZmErrCmdTo ;
|
||
case Done:
|
||
return ZmDone ;
|
||
default:
|
||
return 0 ;
|
||
}
|
||
}
|
||
|
||
int
|
||
ZmodemAttention( register ZModem *info )
|
||
{
|
||
/* attention received from remote end */
|
||
if( info->state == Sending ) {
|
||
ZOFlush(info) ;
|
||
info->interrupt = 1 ;
|
||
}
|
||
return 0 ;
|
||
}
|
||
|
||
|
||
int
|
||
ZmodemAbort( register ZModem *info )
|
||
{
|
||
static u_char canistr[] = {
|
||
CAN,CAN,CAN,CAN,CAN,CAN,CAN,CAN,8,8,8,8,8,8,8,8,8,8
|
||
} ;
|
||
info->state = Done ;
|
||
ZIFlush(info) ;
|
||
ZOFlush(info) ;
|
||
return ZXmitStr(canistr, sizeof(canistr), info) ;
|
||
}
|
||
|
||
|
||
|
||
/* used to completely ignore headers */
|
||
|
||
int
|
||
Ignore( ZModem *info )
|
||
{
|
||
return 0 ;
|
||
}
|
||
|
||
|
||
/* ignore header contents, return ZmDone */
|
||
|
||
int
|
||
RetDone( ZModem *info )
|
||
{
|
||
return ZmDone ;
|
||
}
|
||
|
||
|
||
/* ignore header contents, return ZmErrCancel */
|
||
|
||
int
|
||
GotCancel( ZModem *info )
|
||
{
|
||
return ZmErrCancel ;
|
||
}
|
||
|
||
|
||
/* utility: set up to receive a data packet */
|
||
|
||
int
|
||
dataSetup( register ZModem *info )
|
||
{
|
||
info->InputState = Indata ;
|
||
info->chrCount = 0 ;
|
||
info->crcCount = 0 ;
|
||
info->crc = (info->DataType != ZBIN32) ? 0 : 0xffffffffL ;
|
||
return 0 ;
|
||
}
|
||
|
||
|
||
/* called when a remote command received. For now, we
|
||
* refuse to execute commands. Send EPERM and ignore.
|
||
*/
|
||
int
|
||
GotCommand( ZModem *info )
|
||
{
|
||
u_char rbuf[4] ;
|
||
/* TODO: add command capability */
|
||
|
||
rbuf[0] = EPERM ;
|
||
rbuf[1] = rbuf[2] = rbuf[3] = 0 ;
|
||
return ZXmitHdrHex(ZCOMPL, rbuf, info) ;
|
||
}
|
||
|
||
static int
|
||
GotCommandData( register ZModem *info )
|
||
{
|
||
/* TODO */
|
||
return 0 ;
|
||
}
|
||
|
||
|
||
|
||
/* called when the remote system wants to put something to
|
||
* stderr
|
||
*/
|
||
int
|
||
GotStderr( register ZModem *info )
|
||
{
|
||
info->InputState = Indata ;
|
||
info->chrCount = 0 ;
|
||
return 0 ;
|
||
}
|
||
|
||
static int
|
||
GotStderrData( register ZModem *info )
|
||
{
|
||
info->buffer[info->chrCount] = '\0' ;
|
||
ZStatus(RemoteMessage, info->chrCount, (char *)info->buffer) ;
|
||
return 0 ;
|
||
}
|
||
|
||
|
||
|
||
|
||
/* Protocol failure: An unexpected packet arrived. This could
|
||
* be from many sources, such as old pipelined info finally arriving
|
||
* or a serial line with echo enabled. Report it and ignore it.
|
||
*/
|
||
|
||
int
|
||
ZPF( ZModem *info )
|
||
{
|
||
info->waitflag = 1 ; /* pause any in-progress transmission */
|
||
ZStatus(ProtocolErr, info->hdrData[0], NULL) ;
|
||
return 0 ;
|
||
}
|
||
|
||
|
||
int
|
||
AnswerChallenge( register ZModem *info )
|
||
{
|
||
return ZXmitHdrHex(ZACK, info->hdrData+1, info) ;
|
||
}
|
||
|
||
int
|
||
GotAbort( register ZModem *info )
|
||
{
|
||
ZStatus(RmtCancel, 0, NULL) ;
|
||
return ZXmitHdrHex(ZFIN, zeros, info) ;
|
||
}
|
||
|