/* Comm XMODEM receive file routines */ #define XMDMRECV 1 #include "globals.h" #include #define STARTUP 1 #define STARTBLK 2 #define BLKNUM 3 #define COMPBLK 4 #define DATA 5 #define CHKSUM 6 #define CHKSUM1 7 static int index, state; extern void emit_tx(), emit_rx(), emits_tx(), emits_rx(); /****************************/ /* xmodem recieve functions */ /****************************/ XMODEM_Read_File(file) UBYTE *file; { extern void sendchar(); extern int readchar(); void chop_file(); UBYTE currsect, compsect, response; USHORT dirty, syncerr, errors, blkerrok; ULONG bytes; unsigned int i, noresponse, errsect, naks; int ch; errors = errsect = naks = syncerr = noresponse = index = bytes = 0; blkerrok = abort = dirty = FALSE; crcflag = xfrmode; if ((fd = creat(file, O_CREAT | O_WRONLY)) == -1) { sprintf(sbuff,"\nCannot Open File %s\n",file); emits_rx(sbuff); send_proto(CAN); send_proto( CAN ); return FALSE; } else emits_rx("\nReady to Receive File -- ESC aborts transfer\n\n"); if(viewflg) emit_vw(12); Xconfig(TRUE); /* set serial port to 8/N/1 */ sector = 1; if(wxflag) if(sendWs() == TRUE) return(WXmodem_rec(file)); response = (crcflag) ? 'C' : NAK; state = STARTUP; emits_rx("Waiting for initial handshake\n"); send_proto(response); while( TRUE ) { Process_window_event(); ch = readchar(TTIME,FALSE); if(ch == TIMEOUT) noresponse++; if(errors < RETRYMAX) switch(state) { case STARTUP: if(abort) return FALSE; if( ch == TIMEOUT) { if(noresponse == RETRYMAX/2) { crcflag = (crcflag == FALSE); response = crcflag ? 'C' : NAK; sprintf(sbuff,"\rSender not responding. Switching to %s mode\n", crcflag ? "CRC" : "Checksum"); emits_rx(sbuff); } else if(noresponse == RETRYMAX) break; send_proto(response); continue; } /* fall thru to STARTBLK state */ case STARTBLK: if(abort) break; switch( ch ) { case SOH: emit_rx_protocol(ch); state = BLKNUM; noresponse = 0; response = NAK; if(dirty) { movmem(xbuffer,&diskbuff[index++ * SECSIZ],SECSIZ); dirty = FALSE; } break; case EOT: if(syncerr) continue; emit_rx_protocol(ch); chop_file(index); close( fd ); send_proto( ACK ); return TRUE; case TIMEOUT: send_proto( NAK ); naks++; blkerrok = TRUE; errsect = sector; break; default: syncerr = 1; break; } /* switch( ch ) */ break; case BLKNUM: if(ch == TIMEOUT) break; noresponse = 0; emit_rx_protocol(ch); currsect = ch; state = COMPBLK; if(syncerr) if((currsect != (UBYTE)sector) && (currsect != (UBYTE)(sector - 1))) { if(ch == SOH ) state = BLKNUM; else state = STARTBLK; } continue; case COMPBLK: if(ch == TIMEOUT) break; emit_rx_protocol(ch); compsect = ch; noresponse = i = checksum = crc = 0; state = DATA; if(syncerr) { if(currsect == ~compsect) syncerr = 0; else state = STARTBLK; } continue; case DATA: if(ch == TIMEOUT) break; noresponse = 0; xbuffer[ i++ ] = ch; do_crc( ch ); if(viewflg) emit_vw( ch ); if( i == SECSIZ) state = CHKSUM; continue; case CHKSUM: if(ch == TIMEOUT) break; noresponse = 0; if(crcflag) { state = CHKSUM1; emit_rx_protocol(ch); do_crc( ch ); continue; } /* fall thru to CHKSUM1 state */ case CHKSUM1: if(ch == TIMEOUT) break; noresponse = 0; emit_rx_protocol(ch); state = STARTBLK; response = NAK; if(abort) { abort_xfer(); return FALSE; } if(!verify_checksum( ch )) { emits_rx(" Checksum error\n"); send_proto( NAK ); errsect = sector; naks++; errors++; status(file,bytes,naks,errsect); continue; } if(currsect != ~compsect) { emits_rx(" Bad block number received\n"); send_proto( NAK ); errsect = sector; naks++; errors++; status(file,bytes,naks,errsect); continue; } if(currsect == (UBYTE)(sector -1)) { if(blkerrok) blkerrok = FALSE; else { sprintf(sbuff," Duplicate block %d received\n",sector - 1); emits_rx(sbuff); } status(file,bytes,naks,errsect); if(check_line() == FALSE) /* if an RX char is ready */ send_proto( ACK ); /* don't send ACK */ response = ACK; errors++; continue; } if(currsect != (UBYTE)sector ) { emits_rx(" Sector numbering error\n"); send_proto( NAK ); errsect = sector; naks++; errors++; status(file,bytes,naks,errsect); continue; } errors = 0; bytes += SECSIZ; dirty = TRUE; sprintf(sbuff,"\rReceived block %d",sector); emits_rx(sbuff); status(file,bytes,naks,errsect); sector++; if(index == numbufs) { if(write(fd,diskbuff,numbufs * SECSIZ) != numbufs * SECSIZ) { emits_rx("\nERROR writing to file\n"); send_proto( CAN ); send_proto( CAN ); return FALSE; } index = 0; } if(check_line() == FALSE) /* if an RX char is ready */ send_proto( ACK ); /* don't send ACK */ response = ACK; continue; default: sprintf(sbuff,"\nIllegal internal state %d\n",state); emits_rx(sbuff); state = STARTBLK; continue; } /* end switch */ if( noresponse == RETRYMAX || errors == RETRYMAX ) { emits_rx("Maximum errors exceeded. XMODEM receive aborted\n"); abort_xfer(); return FALSE; } if(abort) { abort_xfer(); return FALSE; } } /* end while(TRUE) */ } send_proto(ch) UBYTE ch; { sendchar(ch); emit_tx_protocol(ch); } abort_xfer() { int ch, errs = 0; chop_file(index); while((ch = readchar(1,FALSE)) != TIMEOUT) ; while(errs++ < 3) { send_proto(CAN); send_proto(CAN); if(readchar(3,FALSE) == ACK) break; } } status(file,bytes,naks,errsect) UBYTE *file; ULONG bytes; USHORT naks,errsect; { sprintf(sbuff,"File=%s bytes=%ld naks=%d [ %d ]\r", file,bytes,naks,errsect); status_line(0,sbuff); } verify_checksum( byte ) UBYTE byte; { if(crcflag) { do_crc( byte ); return (int)(crc == 0); } else return ( checksum == byte ); } /******************************* remove pad characters from the end of the last sector of the file. *******************************/ void chop_file(index) { int count,i; UBYTE c; /* strip control Z -- CP/M and Aterm 1.4 use this for PAD character. Don't remove NULLs. Some programs pad with NULLs. This practice should stop, because some icon.INFO files require a NULL as the last character in the file to terminate an ASCII string. */ if(chopflg) { for(i = SECSIZ-1; i >= 0; i--) { c = xbuffer[ i ]; if( c == PAD ) continue; break; } } else i = SECSIZ-1; /* not chopping, write last sector intact */ count = 0; if(index) count = write(fd,diskbuff,index * SECSIZ); count += write(fd,xbuffer,++i); if ( count != index * SECSIZ + i) emits_rx("\nError writing file\n"); }