[net.sources] X-Modem

wpl@burdvax.UUCP (William P Loftus) (04/21/86)

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 */
        }
}

farren@well.UUCP (04/23/86)

In article <2456@burdvax.UUCP> wpl@burdvax.UUCP (William P Loftus) writes:
>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!.
> [...]
> *  XM - a UNIX-to-CP/M file transfer shell
> *	by Richard Conn
           ^^^^^^^ ^^^^

   Out of the necessity to keep these things straight, as well as to
ensure receiving the proper accolades (or tomatoes :-) ) - I must
admit to having written this - I forgot to change the above
line when I edited the original program.  If you have problems with
XM, please contact me as below, and NOT Richard Conn.  BTW, XM was
designed to work on 4.2BSD, but will probably do o.k. on other
systems.  If you'd like the man pages, email me.
 

-- 
           Mike Farren
           uucp: {your favorite backbone site}!hplabs!well!farren
           Fido: Sci-Fido, Fidonode 125/84, (415)655-0667