X-Modem
William P Loftus
wpl at burdvax.UUCP
Tue Apr 22 03:40:46 AEST 1986
A while ago I asked for X-modem programs for Unix. I got about 15 programs
in response to my request. This seems to be the best of the lot. The
source says it for UNIX-to-CP/M, but I use it (slightly modified) with
AmigaDOS running Online!.
I'm posting it because I got about 100 request to send people
anything I received.
Also I changed this version to work with Online! and my Amiga. To see
my changes grep for 'wpl'. I basically changed the EOL from CR/LF to LF,
that's all.
Good luck,
wpl
-------------Rip here------------------------Rip Here-------------------------
/*
* XM - a UNIX-to-CP/M file transfer shell
* by Richard Conn
*
* XM is based on UC version 1.2, which in turn was based on UMODEM 3.5,
* originally written by Lauren Weinstein, and mutated by Richard Conn,
* Bennett Marks, Michael Rubenstein, Ben Goldfarb, David Hinnant, and
* Lauren Weinstein. XM differs from UC in that it offers only a basic
* UNIX-to-CP/M file transfer facility using the Christensen XMODEM protocol
* in checksum mode.
*/
#define versmaj 1 /* Major Version */
#define versmin 0 /* Minor Version */
/* Basics */
#define FALSE 0
#define TRUE ~FALSE
/* ASCII Characters */
#define SOH 001
#define STX 002
#define ETX 003
#define EOT 004
#define ENQ 005
#define ACK 006
#define LF 012
#define CR 015
#define NAK 025
#define SYN 026
#define CAN 030
#define CTRLZ 032
#define ESC 033
/* XM Constants */
#define TIMEOUT -1 /* Timeout Flag */
#define ERRMAX 10 /* Max errors tolerated */
#define BLOCKSZ 128 /* Size of transmission block */
#define CREATE 0644 /* Mode for New Files */
#define DELAY 5 /* Basic delay for transmission */
/* Library Utilities */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sgtty.h>
#include <signal.h>
#include <ctype.h>
#include <setjmp.h>
/* Setjmp Environment */
jmp_buf rbenv;
main (argc, argv)
int argc;
char *argv[];
{
int sendflg, recvflg, statflg; /* major functions */
char filetype; /* tranfer type */
FILE *fopen(); /* forward ref */
int index; /* index for arg parsing loop */
char opt; /* current option character */
char *getenv(); /* getenv function defn */
/* Print Banner */
printf("XM Version %d.%d - Christensen XMODEM Protocol\n"
,versmaj,versmin);
printf("File Transfer Tool\n");
/* Check for Help Request */
if (argc == 1) {
help();
exit(0);
}
/* Init for Option Parsing */
sendflg = FALSE;
recvflg = FALSE;
statflg = FALSE;
/* Process Options */
index = 0;
while ((opt = argv[1][index++]) != '\0')
switch (opt) {
case '-' : /* skip dash */
break;
case 'F' :
case 'f' :
statflg = TRUE; /* set file stat mode */
break;
case 'R' :
recvflg = TRUE; /* set file recv mode */
filetype = 'b';
break;
case 'r' :
recvflg = TRUE; /* set file recv mode */
filetype = 't';
break;
case 'S' :
sendflg = TRUE; /* set file send mode */
filetype = 'b';
break;
case 's' :
sendflg = TRUE; /* set file send mode */
filetype = 't';
break;
default :
printf("Invalid Option %c\n", opt);
break;
}
/* Select and Execute Major Mode */
if (statflg) { /* File Status Display */
if (argc < 3) {
printf("File Name NOT Given\n");
exit(0);
}
fxstat(filetype,argv[2]);
exit(0);
}
if (sendflg) { /* Send File */
if (argc < 3) {
printf("File Name NOT Given\n");
exit(0);
}
sendfile(filetype,argv[2]);
exit(0);
}
if (recvflg) { /* Receive File */
if (argc < 3) {
printf("File Name NOT Given\n");
exit(0);
}
recv(filetype,argv[2]);
exit(0);
}
printf("Major Mode NOT Selected\n");
help();
exit(0);
}
/* Print Help */
help()
{
printf("Usage: XM c filename\n");
printf("\n");
printf("where 'c' MUST be One of the Following Commands --\n");
printf("\tR -- Receive Binary File\n");
printf("\tr -- Receive Text File\n");
printf("\tS -- Send Binary File\n");
printf("\ts -- Send Text File\n");
printf("\tf or F -- Show File Status\n");
printf("\n");
printf("Examples:\n");
printf("\tXM S myfile <-- Send Binary File \"myfile\"\n");
printf("\tXM s mytext <-- Send Text File \"mytext\"\n");
}
/* Send File */
sendfile(filetype,filename)
char filetype;
char *filename;
{
FILE *fd, *fopen();
int blocknum; /* Current Block Number */
int nlflg; /* New Line for File Convert */
int sending; /* Xmit In-Progress Flag */
int tries; /* Attempt Count */
int bufctr; /* Counter for Buffer Build */
int c; /* Temp Char */
int rcode; /* Return Code */
char buf[BLOCKSZ]; /* Buffer for Transfer */
/* Print Banner */
printf("XM Sending %s File: %s\n",
(filetype == 't') ? "Text" : "Binary",
filename);
/* Open File for Input and Print Opening Messages */
if ((fd = fopen(filename, "r")) == 0) {
printf("Can`t Open File %s for Send\n", filename);
return;
}
fxstat(filetype,filename); /* Print File Status Info */
printf("Ready to Send File\n");
binary(TRUE,TRUE); /* Open Binary Communications */
/* Init Parameters */
blocknum = 1;
nlflg = FALSE;
sending = TRUE;
/* Synchronize */
tries = 0;
while (recvbyte(30) != NAK) {
if (++tries > ERRMAX) {
printf("Remote System Not Responding\n");
return;
}
}
/* Main Transmission Loop */
while(sending) {
/* Build Next Block into buf */
for (bufctr = 0; bufctr < BLOCKSZ;) {
if (nlflg) { /* New Line */
buf[bufctr++] = LF; /* Store LF */
nlflg = FALSE;
}
if (bufctr == BLOCKSZ) break; /* Leave for Loop */
c = getc(fd); /* Get Next Byte from File */
if (c == EOF) {
sending = FALSE; /* Done */
if (!bufctr) /* Avoid Extra Block */
break;
for(;bufctr < BLOCKSZ; bufctr++)
buf[bufctr]= (filetype == 't')
? CTRLZ : '\0' ;
continue; /* Exit for Loop */
}
if (c == LF && filetype == 't') { /* NL? */
/* buf[bufctr++] = CR; /* Insert CR */
/*
I commented out the above line, because the EOL on the Amiga is
LF not CR/LF. wpl
*/
nlflg = TRUE; /* New Line */
}
else buf[bufctr++] = c; /* Store Char */
}
/* Send Block */
tries = 0; /* Set Try Count */
if (bufctr) do {
putblock(filetype,buf,blocknum); /* Send Block */
rcode = recvbyte(10); /* Get Response */
}
while (rcode != ACK && ++tries < ERRMAX);
blocknum = (blocknum + 1) & 0xFF;
if (tries == ERRMAX) sending = FALSE; /* Error Abort */
}
/* Cleanup After Send */
fclose(fd); /* Close File */
tries = 0;
sendbyte(EOT);
while (recvbyte(15) != ACK && ++tries < ERRMAX)
sendbyte(EOT);
binary(FALSE,TRUE); /* Leave Binary Mode */
sleep(3);
printf("\n");
}
/* Send Buffer to Receiver */
putblock(filetype,buf,blocknum)
char filetype;
char *buf;
int blocknum;
{
int i, j, checksum;
sendbyte(SOH); /* Send Start of Header */
sendbyte(blocknum&0xff); /* Send Block Number */
sendbyte((-blocknum-1)&0xff); /* Send Block Complement */
checksum = 0;
for (i = 0; i < BLOCKSZ; i++) {
sendbyte(*buf&0xff); /* Send Byte */
checksum = (checksum + *buf++) & 0xff;
}
sendbyte(checksum&0xff); /* Checksum */
}
/* Receive File */
recv(filetype,filename)
char filetype;
char *filename;
{
int fd; /* file descriptor */
int blocknum; /* next block to receive */
int rbcnt; /* total number of received blocks */
int errorcnt; /* number of errors on current block */
int receiving; /* continuation flag */
int char1; /* first char received in block */
int rcode; /* received block code */
if (!access(filename,2)) {
printf("File %s Exists -- Delete it? ", filename);
if (!getyn()) {
printf("Aborting\n");
return;
}
}
unlink(filename); /* delete old file, if any */
if ((fd = creat(filename, CREATE)) == -1) { /* can't create */
printf("Can't Create %s\n", filename);
return;
}
/* We Have a GO */
printf("XM Receiving %s File: %s\n",
(filetype == 't') ? "Text" : "Binary",
filename);
printf("Ready to Receive\n");
/* Init Counters et al */
blocknum = 1;
rbcnt = 0;
errorcnt = 0;
receiving = TRUE;
/* Establish Binary Communications */
binary(TRUE,TRUE);
/* Synchronize with Sender */
sendbyte(NAK);
/* Receive Next Packet */
while (receiving) {
do {
char1 = recvbyte(6);
}
while ((char1 != SOH) && (char1 != EOT) && (char1 != TIMEOUT));
switch (char1) {
case TIMEOUT : /* Timeout */
if (++errorcnt == ERRMAX) {
close(fd); /* Close File */
sleep(3); /* Delay for Sender */
binary(FALSE,TRUE); /* Normal I/O */
receiving = FALSE;
}
sendbyte(NAK);
break;
case EOT : /* End of Transmission */
sendbyte(ACK);
while (recvbyte(3) != TIMEOUT);
close(fd); /* Close File */
sleep(3); /* Delay for Sender */
binary(FALSE,TRUE); /* Normal I/O */
printf("\n");
receiving = FALSE;
break;
case SOH : /* New or Old Block */
rcode = getblock(filetype,fd,blocknum);/* read block */
switch (rcode) {
case 0 : /* OK */
blocknum = ++blocknum & 0xff;
rbcnt++;
case 2 : /* OK, but Duplicate Block */
errorcnt = 0;
sendbyte(ACK);
break;
case 1 : /* Xmit Error, Non-Fatal */
if (++errorcnt < ERRMAX) {
sendbyte(NAK);
break;
}
default : /* Xmit Error, Fatal */
close(fd);
sendbyte(CAN);
binary(FALSE,TRUE);
while (recvbyte(3) != TIMEOUT);
receiving = FALSE;
break;
}
break;
}
}
}
/* Get Block from Sender */
getblock(filetype,fd,blocknum)
char filetype;
int fd, blocknum;
{
int curblock, cmpblock;
int recdone, checksum, inchecksum, byte, bufcnt, c;
int startstx, endetx, endenq;
int errflg, errchr;
char buff[BLOCKSZ];
int j;
curblock = recvbyte(DELAY);
if (curblock == TIMEOUT) return(1);
cmpblock = recvbyte(DELAY);
if (cmpblock == TIMEOUT) return(1);
if ((curblock + cmpblock) != 0xff) {
while (recvbyte(DELAY) != TIMEOUT); /* Flush */
return(1);
}
checksum = 0; /* Init Checksum */
byte = 0; /* Init Buff Ptr */
recdone = FALSE; /* File Receive NOT Done */
for (bufcnt=0; bufcnt<BLOCKSZ; bufcnt++) {
c = recvbyte(DELAY);
if (c == TIMEOUT) return(1);
buff[byte] = c;
checksum = (checksum + c) & 0xff;
if (filetype != 't') {
byte++; /* binary xfer, so advance */
continue;
}
if (c == CR) continue; /* skip CR */
if (c == CTRLZ) { /* done */
recdone = TRUE;
continue;
}
if (!recdone) byte++; /* continue */
}
inchecksum = recvbyte(DELAY);
if (inchecksum == TIMEOUT) return(1);
errflg = FALSE;
if (checksum != inchecksum) errflg = TRUE;
if (errflg) return(1);
if (curblock != blocknum) {
if (curblock == ((blocknum+1)&0xff)) {
return(99);
}
return(2);
}
if (write(fd,buff,byte) < 0) {
return(99);
}
return(0);
}
/* File Status Display */
fxstat(filetype,filename)
char filetype;
char *filename;
{
struct stat fsi; /* file status info */
if (stat (filename, &fsi) == -1) { /* get file status info */
printf("File %s Not Found\n", filename);
return;
}
printf("File Size of %s is %ldK, %ld Blocks\n",
filename,
fsi.st_size%1024 ? (fsi.st_size/1024)+1 : fsi.st_size/1024,
fsi.st_size%128 ? (fsi.st_size/128)+1 : fsi.st_size/128);
}
/* SUPPORT ROUTINES */
/* get yes or no response from user */
getyn()
{
int c;
c = charx(); /* get char */
if (c == 'y' || c == 'Y') {
printf("Yes\n");
return(TRUE);
}
else {
printf("No\n");
return(FALSE);
}
}
/* get single char input */
charx()
{
int binary();
int c;
binary(TRUE,FALSE);
c = getchar();
binary(FALSE,FALSE);
return (c);
}
/* send byte to receiver */
sendbyte(data)
char data;
{
write (1, &data, 1); /* write the byte */
}
/* receive a byte from sender */
recvbyte(seconds)
unsigned seconds;
{
char c;
int alarmfunc(); /* forward declaration */
signal(SIGALRM,alarmfunc); /* catch alarms */
if (setjmp(rbenv) < 0) /* setup jump for alarm */
return (TIMEOUT);
alarm(seconds); /* set clock */
read (0, &c, 1); /* get char or timeout */
alarm(0); /* clear clock */
return (c&0xff);
}
/* dummy alarm function */
alarmfunc()
{
longjmp(rbenv,TIMEOUT); /* jump to recvbyte */
return;
}
/* set and clear binary mode */
binary(setflg,scope)
int setflg, scope;
{
static struct sgttyb ttys, ttysold;
if (setflg) { /* set binary */
if (gtty (0, &ttys) < 0) return(FALSE); /* failed */
ttysold.sg_ispeed = ttys.sg_ispeed; /* save old values */
ttysold.sg_ospeed = ttys.sg_ospeed;
ttysold.sg_erase = ttys.sg_erase;
ttysold.sg_kill = ttys.sg_kill;
ttysold.sg_flags = ttys.sg_flags;
ttys.sg_flags |= RAW; /* set for RAW Mode */
ttys.sg_flags &= ~ECHO; /* set no ECHO */
if (scope) { /* cover all values? */
ttys.sg_flags &= ~XTABS; /* set no tab exp */
ttys.sg_flags &= ~LCASE; /* set no case xlate */
ttys.sg_flags |= ANYP; /* set any parity */
ttys.sg_flags &= ~NL3; /* no delays on nl */
ttys.sg_flags &= ~TAB0; /* no tab delays */
ttys.sg_flags &= ~TAB1;
ttys.sg_flags &= ~CR3; /* no CR delay */
ttys.sg_flags &= ~FF1; /* no FF delay */
ttys.sg_flags &= ~BS1; /* no BS delay */
}
if (stty (0, &ttys) < 0) return(FALSE); /* failed */
#ifdef MESG
if (scope) system("mesg n >/dev/null"); /* turn off messages */
#endif
return(TRUE);
}
else { /* clear binary */
if (stty (0, &ttysold) < 0) return (FALSE);
#ifdef MESG
if (scope) system("mesg y >/dev/null"); /* turn on messages */
#endif
return(TRUE); /* OK */
}
}
More information about the Comp.sources.unix
mailing list