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