Xmodem
Ronald Florence
ron at mlfarm.UUCP
Sun Aug 20 00:19:12 AEST 1989
There have been several inquiries recently about code to enable the
use of Xmodem with cu or other terminal programs. The enclosed code
(written in 1986) can be compiled into either standalone programs
which enable a remote terminal to upload/download with Xmodem onto a
Unix/Xenix system, or if you have a source license, can be compiled
with cu to enable the use of Xmodem protocol within cu.
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
# Readme
# Makefile
# xr.c
# xmodem.c
# This archive created: Sat Aug 19 10:09:45 1989
# By: Ronald Florence (Maple Lawn Farm, Stonington, CT)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'Readme'" '(2008 characters)'
if test -f 'Readme'
then
echo shar: "will not over-write existing file 'Readme'"
else
sed 's/^X//' << \SHAR_EOF > 'Readme'
XXmodem Under Xenix/Unix
Xcopyright 1986 Ronald Florence
X
XREMOTE XMODEM
X
XWhen compiled with xr.c, xmodem.c provides standalone programs, xr
Xand xt (actually a link) which enable a remote computer logged onto
Xthe host (unix) machine to transmit to or receive from the unix machine
Xusing Xmodem protocol.
X
XThe options are the same for both send and receive:
X
X -c force checksum (instead of auto-detect of crc/checksum)
X -d fn debug messages to fn
X -t text (CR-NL <-> NL conversions)
X
XTo transfer TO the unix host give the command
X
X xr [-ct] [-d errfile] filename
X
Xon the unix host, wait for xr to announce it is ready, then begin the
XXmodem send from the remote machine. To transfer FROM the unix host,
Xgive the command
X
X xt [-ct] [-d errfile] filename
X
Xon the unix host, wait for xt to announce it is ready, then begin the
XXmodem receive on the remote machine. xt will adjust to the sync
Xcharacter of the Xmodem receiver (match crc or checksum).
X
XBoth xt and xr ignore signals, and should be terminated by sending a
XCtrl-X <CAN>, which is the signal Xmodem programs normally send to
Xcancel a transfer. If the remote machine screws up, the Ctrl-X can be
Xsent manually.
X
XThe byte counts of text files transferred from ms-dos machines with the
X"-t" option will be shorter on the unix machine because the <CR>s have
Xbeen dropped.
X
X
XXMODEM WITH OTHER TERMINAL PROGRAMS
X
XIf you have a unix/xenix source license, xmodem.c can be compiled with
Xcu.c to provide a version of cu that can receive and transmit to
Xbulletin boards, Compuserve, etc., using Xmodem (Modem 7) protocol in
Xeither crc or checksum mode.
X
XThe xmodem.c module can also be compiled with other terminal programs,
Xsuch as kermit, to provide xmodem transfer capabilities. One easy way
Xto use the module with a program like cu is to kill the receive process
Xin cu, condition the line to
X
X ~(ISTRIP | IXON | IXOFF | PARENB) , CS8
X
Xthen fork to start xget() or xput(). When the child is finished,
Xdo a longjmp to restart the receive process.
SHAR_EOF
if test 2008 -ne "`wc -c < 'Readme'`"
then
echo shar: "error transmitting 'Readme'" '(should have been 2008 characters)'
fi
fi
echo shar: "extracting 'Makefile'" '(423 characters)'
if test -f 'Makefile'
then
echo shar: "will not over-write existing file 'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
X# cu, xr/xt
X
XCFLAGS= -O -s
X
XLDFLAGS= -s
X
Xwhat:
X @echo "make what?"
X
Xdebugcu: cu.c cuxmodem.o login.o
X cc -Dddt $(CFLAGS) -s login.o cu.c cuxmodem.o -o cu
X
Xcu: cu.o cuxmodem.o login.o
X cc login.o cu.o cuxmodem.o $(LDFLAGS) -o cu
X
Xxr: xr.o xmodem.o
X cc xr.o xmodem.o -o xr
X ln xr xt
X
Xcuxmodem.o: xmodem.c
X cc $(CFLAGS) -DCU -c xmodem.c
X mv xmodem.o cuxmodem.o
X
Xdial: dial24.c
X cc $(CFLAGS) -o dial dial24.c
X
Xclean:
X rm *.o
SHAR_EOF
if test 423 -ne "`wc -c < 'Makefile'`"
then
echo shar: "error transmitting 'Makefile'" '(should have been 423 characters)'
fi
fi
echo shar: "extracting 'xr.c'" '(2252 characters)'
if test -f 'xr.c'
then
echo shar: "will not over-write existing file 'xr.c'"
else
sed 's/^X//' << \SHAR_EOF > 'xr.c'
X/*
X * xr.c - remote xmodem functions for xenix/unix
X * copyright 1986 Ronald Florence
X *
X * usage: xr|xt [-tc] [-d errfile] file
X * -t text mode (CR-NL <-> NL)
X * -c force checksum
X *
X * To avoid overwriting existing files,
X * a received file with the same name
X * as an existing file is stored as fname~.
X */
X
X#include <signal.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include <sys/ioctl.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <termio.h>
X
X#define DEBUG 01
X#define LF 02
X#define CHECKSUM 04
X#define BSIZE 128
X#define errx(m,f) printf("%s: ", pname), \
X printf(m, f), \
X printf("\n"), \
X exit(1)
X
XFILE *errf;
X
Xstruct termio old,
X new;
X
Xhangup()
X{
X resetline();
X exit(1);
X}
X
X
Xmain(ac, av)
Xint ac;
Xchar **av;
X{
X char *pname,
X trans = (av[0][strlen(av[0]) -1] == 't'),
X *fname;
X int c, opts = 0;
X struct stat stbuf;
X extern int optind;
X extern char *optarg;
X FILE *fp;
X
X pname = *av;
X while ((c = getopt(ac, av, "ctd:?")) != EOF)
X switch (c) {
X case 't' :
X opts |= LF;
X break;
X case 'c' :
X opts |= CHECKSUM;
X break;
X case 'd' :
X opts |= DEBUG;
X if (!(errf = fopen(optarg, "w")))
X errx("can't open %s", optarg);
X setbuf(errf, NULL);
X break;
X case '?' :
X printf("usage: %s [-tc] [-d errfile] file\n", pname);
X exit(1);
X }
X if (ac == 1 || ac == optind)
X errx("need file name", NULL);
X fname = av[optind];
X if (trans && !(fp = fopen(fname, "r")))
X errx("can't open %s", fname);
X if (!trans) {
X if (!access(fname, 0))
X strcat(fname, "~");
X if (!(fp = fopen(fname, "w")))
X errx("can't write %s", fname);
X }
X printf("Ready to %s %s\n", (trans) ? "send" : "receive", fname);
X if (trans) {
X stat(fname, &stbuf);
X printf("%d blocks (128 bytes/block)\n",stbuf.st_size/BSIZE+1);
X }
X printf("Ctrl-X to abort transfer\n");
X
X signal(SIGINT, SIG_IGN);
X signal(SIGQUIT, SIG_IGN);
X signal(SIGHUP, hangup);
X
X ioctl(1, TCGETA, &old);
X ioctl(1, TCGETA, &new);
X fflush(stdin);
X new.c_iflag = IGNBRK|IGNPAR;
X new.c_oflag = 0;
X new.c_lflag = 0;
X new.c_cc[4] = 1;
X new.c_cflag &= ~PARENB;
X new.c_cflag |= CS8;
X ioctl(1, TCSETAW, &new);
X
X (trans) ? xput(fp, opts) : xget(fp, opts);
X}
X
X
Xresetline()
X{
X ioctl(1, TCSETA, &old);
X}
X
SHAR_EOF
if test 2252 -ne "`wc -c < 'xr.c'`"
then
echo shar: "error transmitting 'xr.c'" '(should have been 2252 characters)'
fi
fi
echo shar: "extracting 'xmodem.c'" '(6588 characters)'
if test -f 'xmodem.c'
then
echo shar: "will not over-write existing file 'xmodem.c'"
else
sed 's/^X//' << \SHAR_EOF > 'xmodem.c'
X/*
X * xmodem.c - xmodem functions for Xenix or Unix
X * copyright 1986, 1988 Ronald Florence
X *
X * modified 3/88 to do crc by packet instead of by byte
X * modified 5/89 to make auto-match of CRC/checksum the default
X * modified 5/89 to filter out trailing null bytes from packets sent
X * with -t option from brain-damaged xmodems.
X */
X
X#include <signal.h>
X#include <stdio.h>
X
X#ifdef CU
X
Xextern int rlfd; /* the open line in cu */
X#define WFD rlfd
X#define RFD rlfd
X#define errf stderr
X
X#else
X
Xextern FILE *errf; /* error file for remote */
X#define WFD 1 /* stdout */
X#define RFD 0 /* stdin */
X
X#endif
X
X#define BSIZE 128
X#define DEBUG 01
X#define LF 02
X#define CHECKSUM 04
X#define NOREAD(x, c) (rchar(x, &c) == -1)
X#define TX(c) write(WFD, &c, 1)
X#define CRC_TRIES 2
X#define ever (;;)
X
Xstatic char soh = 0x01,
X eot = 0x04,
X ack = 0x06,
X nak = 0x15,
X can = 0x18,
X crcinit = 'C';
X
Xstatic int debug,
X crc;
X
Xunsigned short do_crc();
X
Xint kleenex(),
X onalarm();
X
Xxget(fp, opts)
XFILE *fp;
Xint opts;
X{
X char buf[BSIZE];
X unsigned char inch, b= 1, crchi;
X int iput = BSIZE, i, tries = 0;
X
X debug = (opts & DEBUG);
X crc = (opts & CHECKSUM) ? 0 : 1;
X signal(SIGALRM, onalarm);
X#ifdef CU
X signal(SIGINT, kleenex);
X#else
X sleep(10);
X#endif
X (crc) ? TX(crcinit) : TX(nak);
X for ever {
X if NOREAD(6, inch) {
X err("Timeout during SOH");
X if (tries++ >= CRC_TRIES)
X crc = 0;
X cksend(crc ? crcinit : nak);
X continue;
X }
X if (inch == eot)
X break;
X if (inch == can) {
X err("CAN block %u", b);
X kleenex(-1);
X }
X if (inch != soh) {
X err("Bad SOH block %u: %#x", b, inch);
X cksend(nak);
X continue;
X }
X if NOREAD(2, inch) {
X err("Timeout block %u during blocknum", b);
X cksend(nak);
X continue;
X }
X if (inch != b) {
X err("Expected blocknum %u, got %u", b, inch);
X cksend(nak);
X continue;
X }
X if NOREAD(2, inch) {
X err("Timeout block %u during ~blocknum", b);
X cksend(nak);
X continue;
X }
X if (inch != ~b) {
X err("Expected ~blocknum %u, got %u", ~b, inch);
X cksend(nak);
X continue;
X }
X /* Read in the block of 128 bytes without
X * taking time for checksums or crc.
X */
X for (i = 0; i < BSIZE; i++)
X if NOREAD(2, buf[i])
X break;
X if (i < BSIZE) {
X err("Timeout data recv, char #%d", i);
X cksend(nak);
X continue;
X }
X if (crc && NOREAD(2, crchi)) {
X err("Timeout crc hibyte");
X cksend(nak);
X continue;
X }
X if NOREAD(2, inch) {
X err("Timeout %s", (crc) ? "crc lobyte" : "checksum");
X cksend(nak);
X continue;
X }
X /* Now, when we have the whole packet,
X * do the checksum or crc.
X */
X if (crc) {
X unsigned short crcsum;
X
X crcsum = do_crc(buf);
X if (inch + (crchi << 8) != crcsum) {
X err("Expected crc %u, got %u",
X crcsum, inch + (crchi << 8));
X cksend(nak);
X continue;
X }
X }
X else {
X unsigned char cksum;
X
X for (cksum = 0, i = 0; i < BSIZE; i++)
X cksum += buf[i];
X cksum %= 256;
X if (cksum != inch) {
X err("Expected checksum %u, got %u", cksum,inch);
X cksend(nak);
X continue;
X }
X }
X TX(ack);
X#ifdef CU
X putc('.', stderr);
X#endif
X if (opts & LF)
X for (i=0, iput=0; i < BSIZE; i++) {
X if (buf[i] == 0x1a) /* old ms-dos eof */
X break;
X if (buf[i] != '\r' && buf[i] != '\0')
X buf[iput++] = buf[i];
X }
X fwrite(buf, iput, 1, fp);
X b++;
X b %= 256;
X }
X TX(ack);
X kleenex(0);
X}
X
X
Xxput(fp, opts)
XFILE *fp;
Xint opts;
X{
X char buf[BSIZE];
X unsigned char b = 1, cb, inch;
X int cread, i;
X
X
X#ifdef CU
X signal(SIGINT, kleenex);
X#endif
X signal(SIGALRM, onalarm);
X debug = (opts & DEBUG);
X rchar(60, &cb);
X if (cb == crcinit)
X crc = 1;
X else if (cb == nak)
X crc = 0;
X else {
X err("No startup %s", (crc) ? "'C'" : "NAK");
X kleenex(-1);
X }
X cread = fillbuf(fp, buf, (opts & LF));
X while (cread) {
X for (i = cread; i < BSIZE; i++)
X buf[i] = 0;
X TX(soh);
X TX(b);
X cb = ~b;
X TX(cb);
X write(WFD, buf, BSIZE);
X if (crc) {
X unsigned short crcsum;
X unsigned char crclo, crchi;
X
X crcsum = do_crc(buf);
X crclo = (crcsum & 0xff);
X crchi = (crcsum >> 8);
X TX(crchi);
X TX(crclo);
X }
X else {
X unsigned char cksum;
X
X for (cksum = 0, i = 0; i < BSIZE; i++)
X cksum += buf[i];
X cksum %= 256;
X TX(cksum);
X }
X if NOREAD(15, inch) {
X err("Timeout after block %u", b);
X continue;
X }
X if (inch == can) {
X err("CAN after block %u", b);
X kleenex(-1);
X }
X if (inch != ack) {
X err("Non-ACK after block %u: %#x", b, inch);
X continue;
X }
X#ifdef CU
X putc('.', stderr);
X#else
X if (debug)
X fprintf(errf, "Validated block %u\n", b);
X#endif
X cread = fillbuf(fp, buf, (opts & LF));
X b++;
X b %= 256;
X }
X for ever {
X TX(eot);
X if NOREAD(15, inch) {
X err("Timeout during EOT");
X continue;
X }
X if (inch == can) {
X err("CAN during EOT");
X kleenex(-1);
X }
X if (inch != ack) {
X err("Non-ACK during EOT: %#x", inch);
X continue;
X }
X break;
X }
X kleenex(0);
X}
X
X
Xfillbuf(fp, buf, lf)
XFILE *fp;
Xchar *buf;
Xint lf;
X{
X int i = 0, c;
X static int cr_held;
X
X if (cr_held) {
X buf[i] = '\n';
X i++;
X cr_held--;
X }
X for (; i < BSIZE; i++) {
X if ((c = getc(fp)) == EOF)
X break;
X if (c == '\n' && lf) {
X buf[i] = '\r';
X if (i == 127) {
X cr_held++;
X return BSIZE;
X }
X buf[i+1] = '\n';
X i++;
X }
X else
X buf[i] = c;
X }
X return i;
X}
X
X
Xunsigned short do_crc(b)
Xchar *b;
X{
X unsigned short shift, flag, crcsum;
X char c;
X int i;
X
X for (i = 0, crcsum = 0; i < (BSIZE + 2); i++) {
X c = (i < BSIZE) ? b[i] : 0;
X for (shift = 0x80; shift; shift >>= 1) {
X flag = (crcsum & 0x8000);
X crcsum <<= 1;
X crcsum |= ((shift & c) ? 1 : 0);
X if (flag)
X crcsum ^= 0x1021;
X }
X }
X return (crcsum);
X}
X
X
X/* The timeout in rchar() works by deliberately
X * interrupting the read() system call. We know
X * errno=EINTR, so there is no reason for a
X * perror() autopsy.
X */
Xrchar(timeout, cp)
Xunsigned timeout;
Xchar *cp;
X{
X int c;
X
X alarm(timeout);
X if ((c = read(RFD, cp, 1)) == -1)
X return -1;
X alarm(0);
X return c;
X}
X
X
Xonalarm()
X{
X signal(SIGALRM, onalarm);
X}
X
X
Xkleenex(sig)
Xint sig;
X{
X#ifdef CU
X if (sig > 0)
X cksend(can);
X else
X fprintf(stderr, "\r\nFile transfer %s.",
X (sig) ? "cancelled" : "complete");
X fprintf(stderr, "\r\n");
X#else
X printf("File transfer %s.\r\n", (sig) ? "cancelled" : "complete");
X resetline();
X#endif
X exit(sig);
X}
X
X
Xcksend(ch)
Xchar ch;
X{
X int j;
X char cp;
X
X do {
X j = rchar(2, &cp);
X } while (j != -1);
X TX(ch);
X}
X
X
X/* VARARGS1 */
Xerr(s, i, j)
Xchar *s;
Xint i, j;
X{
X if (debug) {
X fprintf(errf, s, i, j);
X#ifndef CU
X fprintf(errf, "\n");
X }
X#else
X fprintf(errf, "\r\n");
X }
X else
X putc('%', stderr);
X#endif
X}
SHAR_EOF
if test 6588 -ne "`wc -c < 'xmodem.c'`"
then
echo shar: "error transmitting 'xmodem.c'" '(should have been 6588 characters)'
fi
fi
exit 0
# End of shell archive
--
Ronald Florence ...{hsi!aati,rayssd}!mlfarm!ron
More information about the Comp.unix.xenix
mailing list