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