Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site burdvax.UUCP Path: utzoo!watmath!clyde!cbosgd!ukma!psuvm.bitnet!psuvax1!burdvax!wpl From: wpl@burdvax.UUCP (William P Loftus) Newsgroups: net.sources Subject: X-Modem Message-ID: <2456@burdvax.UUCP> Date: Mon, 21-Apr-86 12:40:46 EST Article-I.D.: burdvax.2456 Posted: Mon Apr 21 12:40:46 1986 Date-Received: Wed, 23-Apr-86 21:22:01 EST Organization: System Development Corp., Paoli, PA Lines: 514 Keywords: Written in C for Unix 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 #include #include #include #include #include #include /* 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/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 */ } }