Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!purdue!ames!apple!netcom!ward From: ward@netcom.UUCP (Mike Ward) Newsgroups: comp.os.minix Subject: XCOMM 2.2 for MINIX v1.3d (part 2) Keywords: terminal emulation, xmodem, cisb, phonelist, script Message-ID: <1274@netcom.UUCP> Date: 23 May 89 13:29:39 GMT Organization: NetCom Services - Public Access Unix System (408) 997-9175 guest Lines: 2015 Greetings ... I was looking around for a communications program to use under MINIX V1.3 and found XCOMM 2.2 by Larry Gensch on the COMPUSERVE UNIX forum. It took a little hacking to get it to run properly under MINIX, and I haven't tested everything thouroughly, but the things I have tried seem to work, so I though it would be safe to post. (Please send me info on any problems you find). MINIX XCOMM 2.2 supports the XMODEM file transfer protocol with CRC or CHECKSUM error detection, CompuServe Information Service protocol B, text capturing, and a phone directory. It also comes with documentation (which I've updated to reflect the changes I made), and should be fairly compatable with XCOMM 2.2 which Larry will support. I'm posting the package in three shell archives. Enjoy ... cut below the dotted line and place it in a file (xc22pX.sh) and then run through the MINIX v1.3 shell: sh xc22pX.sh ------------------------------------------------------------------------------- echo x - .phonelist sed '/^X/s///' > .phonelist << '/' X1-301-459-3865 Ada IC BITS=7 BAUD=2400 X1-512-832-9224 Austinet (TIPC) BITS=7 BAUD=2400 X 988-5366 CompuServe 2400 BITS=7 BAUD=2400 SCRIPT=cis.cmd X1-301-277-9408 Mars Hotel (MINIX) BITS=7 BAUD=2400 X 997-9175 Netcom (UNIX) BITS=7 BAUD=2400 X1-301-765-5555 PIWG BITS=7 BAUD=2400 X1-317-742-5533 Progrs Room (EMACS) BITS=7 BAUD=2400 / echo x - cis.cmd sed '/^X/s///' > cis.cmd << '/' X# Ensure we are in 7-bit mode (high bit ignored), since CIS defaults to X# seven-bit words (this ensures the waitfor commands will find what they X# are looking for). X X set 7bit on X X# The following IF condition tests to see if we are linked from a .phonelist X# entry. If not, we manually dial the local COMPUSERVE node: X X if !linked X dial 687-0374 # Note anything past a full command is ignored X endif X X# Wait for a CONNECT message from the modem. If we time out (20 seconds), X# redial the number (it is probably busy). X Xconnect: X waitfor "CONNECT" 20 X if !waitfor X redial X goto connect X endif X X# Pause a bit to let CIS catch up with us X X pause 2 X X# Transmit a Control-C (^C) to CompuServe, and then wait for the X# prompt "User ID:". If this is not seen in 10 seconds, we try again. X Xcis: X transmit "^C" X waitfor "User ID:" 10 X if !waitfor X goto cis X endif X X# Transmit the user id string (remember the ^M at the end for ENTER)... X X transmit "72236,3516^M" X X# Wait for CompuServe to ask for a password... X X waitfor "Password:" X X# Transmit our password X# (You don't think for a moment that this is my REAL password, do you?????) X X transmit "YELLOW*BANANA^M" X X# Wait for CompuServe header, and then transmit "G UNIXFORUM^M^O" X# (The ^M is an ENTER, the ^O tells CompuServe to shut up until we X# get there.) X X waitfor "Compuserve" X X transmit "G UNIXFORUM^M^O" / echo x - install.doc sed '/^X/s///' > install.doc << '/' XXCOMM Installation X XXCOMM 2.2 required some minor modifications to work under minix. Most of Xthese had to do with using MINIX signals (MINIX has no SIGUSR1, SIGCLD, Xand SIGALRM does not work as expected) and with using sgtty.h (sys 7, BSD) Xrather than termio.h (sys III, V). One modification of MINIX software was Xrequired to get this to work, namely, add mode w+ to the c library routine Xfopen. My new routine looks like: X X------------------------------------------------------------------------------- X#include X#include X#include "lib.h" X X#define PMODE 0666 X X XFILE *fopen(name,mode) Xchar *name , *mode; X{ X register int i; X FILE *fp; X char *malloc(); X int fd,omode, X flags = 0; X X for (i = 0; _io_table[i] != 0 ; i++) X if ( i >= NFILES ) X return(NULL); X X switch(*mode){ X X case 'w': X flags |= WRITEMODE; X if (( fd = creat (name,PMODE)) < 0) X return(NULL); X if (*(mode+1) == '+') { X flags |= READMODE; X omode = 2; X if (close(fd) < 0 || (fd = open(name,omode)) < 0) X return(NULL); X } X break; X X case 'a': X flags |= WRITEMODE; X omode = 1; X if (*(mode+1) == '+') { X flags |= READMODE; X omode = 2; X } X if (( fd = open(name,1)) < 0 ) X if(errno != ENOENT || (fd = creat(name, PMODE)) < 0) X return(NULL); X lseek(fd,0L,2); X break; X X case 'r': X flags |= READMODE; X omode = 0; X if (*(mode+1) == '+') { X flags |= WRITEMODE; X omode = 2; X } X if (( fd = open (name,omode)) < 0 ) X return(NULL); X break; X X default: X return(NULL); X } X X X if (( fp = (FILE *) malloc (sizeof( FILE))) == NULL ) X return(NULL); X X X fp->_count = 0; X fp->_fd = fd; X fp->_flags = flags; X fp->_buf = malloc( BUFSIZ ); X if ( fp->_buf == NULL ) X fp->_flags |= UNBUFF; X else X fp->_flags |= IOMYBUF; X X fp->_ptr = fp->_buf; X _io_table[i] = fp; X return(fp); X} X------------------------------------------------------------------------------- X X XTo compile: X X1. Read xcomm.h and, if necessary, make any necessary changes. The following X #define's affect compilation: X XCAPTFILE "capture.log" X X This is the default name given to the capture buffer in terminal mode X (and during script processing). X X XPHFILE ".phonelist" X X This is the default name given to the phone directory in terminal mode. X X XSTARTUP ".xcomm" X X This is the name of the startup script for XCOMM. If this file is found X in your current or home directory, it will be executed immediately on X entrance into the XCOMM program. X X XDRIBBLE 2 X X This is the default number of seconds to wait after transmitting a X newline when sending a file (using the F command) in terminal X mode. X X XCIS_INIT 1 X X Default CIS mode on startup. Basically, this is no longer necessary, X as the startup script can override this on entry to the program. This X value should be 1 if CIS Transfer mode is desired; 0 if it is not X desired. X X Xindex strchr Xrindex strrchr X X In the string(3) library on AT&T's Unix system, the functions "strchr" X and "strrchr" are used to find a character within a string. On Berkley X and some Xenix systems, "index" and "rindex" are used (these are defined X this way in K & R, also). The original XCOMM code used the index/rindex X functions, and this define helped make the program compile. X X Note: If your system does not support strchr and strrchr, define these X functions oppositely: X X #define strchr index X #define strrchr rindex X X Since I tend to use the latter functions. "The wonderful thing X about standards is that there are SO many to choose from!" X X X2. Read the makefile. You may wish to edit the INSTLIB reference to wherever X your system stores user-created commands. On some systems, /usr/bin is X used, other systems have other standards. On my system, I use /usr/comm. X X X3. Type "make" to compile xcomm. X X X4. Type "make install" to install as a permanent command. X X X XTo execute: X X xcomm -l /dev/ttyN X XWhere "/dev/ttyN" is the device name that is used to access your modem. X XThe "-l /dev..." option may be defaulted by setting the environment variable XMODEM. This may be set by your login profile to the name of the device that Xis used to access the modem. In /bin/sh, use the following to set your Xdevice to /dev/tty1: X X set MODEM="/dev/tty1"; export MODEM X XIn "csh", this would be done as follows: X X setenv MODEM /dev/tty1 X Xshould be used. (My appologies to "ksh" users, but I do not have that Xshell.) X X XImplementation: X XOn MY system, I install xcomm into its own directory (/usr/comm), and perform Xall uploads and downloads into this directory. This helps keep production Xfiles from getting crashed. I execute XCOMM from a shell script that changes Xthe current directory to /usr/comm and executes the xcomm program there. XThis keeps all uploads and downloads in that directory, keeping production Xfiles from getting overwritten inadvertantly. The file xcomm.sh included in Xthe source distribution is the script that I use. X X XNotes: X XXCOMM does not honor or set the LCK..tty files used by cu and uucp, so Xthere may be some contention for the use of the modem. If anybody would be Xso kind as to write a routine that performs the lock, I will consider Xplacing it into a future release. X X XBUG REPORTS, FIXES, IDEAS, etc: X XI am very receptive to reasonable requests, suggestions, and bug reports. XMy "electronic" addresses are listed below: X X CompuServe [72236,3516] X Delphi LARRYG X Bix LAR3RY X Andover CNode larry gensch X X(The Andove CNode is a FIDO bulletin board that supports C programming Xand can be reached at 617/470-2548 - 300/1200/2400 baud) X X Xlarry gensch / echo x - makefile sed '/^X/s///' > makefile << '/' XCFLAGS = -F -T. -i XINSTLIB = /usr/comm X XOBJS = xcomm.s xcterm.s xcxmdm.s xcsubs.s xcport.s xccisb.s \ X xcscrpt.s X Xxcomm: $(OBJS) X $(CC) $(CFLAGS) $(OBJS) -o xcomm X chmem =8192 xcomm X X$(OBJS): xcomm.h X Xclean:; rm -f $(OBJS) X Xinstall: xcomm X chmod u=rx,g=x,o=x xcomm X rm -f $(INSTLIB)/xcomm X ln xcomm $(INSTLIB)/xcomm / echo x - readme sed '/^X/s///' > readme << '/' XXCOMM 2.2 MINIX: X XNew in this release... X X* Modified XCOMM command line interface X* CIS "Quick B" Protocol support for faster up/downloads X* Brand new script processor with integration into the phonebook X* Some code cleanup X* Better "vanilla" Unix support (thanks to bug reports) X XRefer to "historical.doc" for a list of ALL new features. X XXCOMM is a PUBLIC DOMAIN (not shareware) communication program that works on XUnix System V.3 (and MINIX). It probably works on other Unix systems with no Xmodification, since no machine specific coding was done. X XMINIX XCOMM is distributed in three bundles: X XXCM22P1.SH All documentation for XCOMM XXCM22P2.SH source code, header files, and makefile. XXCM22P3.SH source code X XThis README manifest file is included in both packages. X XContents of XCM22P1.SH: X Xreadme This manifest file Xscript.doc Quick reference to the XCOMM script language Xxcomm.doc Latest documentation for the XCOMM program Xhistorical.doc Historical information about versions 1.0 and 1.1 Xphonelist.doc Documentation on using XCOMM's phonelist X X XContents of XCM22SRC.SH: X Xreadme This manifest file Xinstall.doc Quick installation notes Xmakefile Make file for compiling XCOMM Xcis.cmd A sample XCOMM script file for logging onto CompuServe. X.phonelist A sample Phone List file Xxcomm.h XCOMM's header file Xxcxmdm.c XCOMM XMODEM protocol module Xxccisb.c XCOMM CIS-B/QuickB protocol module X X XContents of XCM22SRC.SH: X Xreadme This manifest file Xxcomm.c XCOMM main program Xxcport.c XCOMM Terminal I/O module Xxcscrpt.c XCOMM Script processor Xxcsubs.c XCOMM's subroutines Xxcterm.c XCOMM Terminal mode module / echo x - xccisb.c sed '/^X/s///' > xccisb.c << '/' X/* X xccisb.c CIS "B" Routines for XCOMM X X The following code was modified from "BP.C", a "generic" B-protocol X handler available from CompuServe. X X*/ X/** X * Copyright (c) 1985 by Steve Wilhite, Worthington, Ohio X * X * Permission is granted to use or distribute this software without any X * restrictions as long as this entire copyright notice is included intact. X * You may include it in any software product that you sell for profit. X * X * This software is distributed as is, and is not guaranteed to work on any X * given hardware/software configuration. Furthermore, no liability is X * granted with this software. X * X * ABSTRACT: X * X * The function, Transfer_File, implements error-free file transfer using X * CompuServe's "B" protocol. X * X * It has been assumed that the start-of-packet sequence, DLE "B", has X * been detected and the next byte not received yet is the packet X * sequence number (an ASCII digit). X * X * AUTHOR: Steve Wilhite, CREATION DATE: 21-Jul-85 X * X * REVISION HISTORY: X * X * 4 Dec 1987 -- larry gensch -- Supports QUICK-B Protocol X **/ X X#include X#include X#include X#include X#include X#include X#include X X#include "xcomm.h" X Xextern FILE *tfp; /* Generic tty pointer */ X Xstatic int Abort_Flag = 0; /* Equals 1 if user wants to abort */ Xstatic int Text_Mode = 0; /* 1 = Convert /r/n to /n and back */ Xstatic int ACK_Count = 0; /* Number of successful packets */ Xstatic int XMIT_Count = 0; /* Number of successful packets */ Xstatic int NAK_Count = 0; /* Number of consecutive NAKS */ Xstatic long io_count = 0L; /* Number of bytes transferred */ Xstatic jmp_buf err_buf; /* setjmp() buffer */ Xstatic int Last_Chr = 0; /* Last char read from file */ Xstatic long start_time = 0L; /* Start time of transfer */ Xstatic long bytes_sec = 0L; /* Number of bytes transferred/sec */ X Xstatic int Failure_Sent = 0; /* CIS knows we aborted? */ X X#define Wants_To_Abort() (Abort_Flag) X#define MAX_WINDOW 2 X#define min(a,b) ((a)<(b) ? (a) : (b)) X X/* X The following define allows a hex transcript of the file transfer X to be generated. Uncomment this define if you want the file XCOMM.LOG X generated. (This really increases overhead, but handy for debugging!) X*/ X X/* #define CIS_B_DEBUG /* Uncomment if you want XCOMM.LOG generated */ X X#define ETX 0x03 X#define ENQ 0x05 X#define DLE 0x10 X#define XON 0x11 X#define XOFF 0x13 X#define NAK 0x15 X X#ifndef TRUE X#define TRUE 1 X#define FALSE 0 X#endif X X#define Success -1 X#define Failure 0 X X#define MAX_PACKET 1024 X#define Max_Errors 10 X#define Max_Time 10 X#define WACK ';' /* wait acknowledge */ X X/* Sender actions */ X X#define S_Send_Packet 0 X#define S_Get_DLE 1 X#define S_Get_Num 2 X#define S_Get_Seq 3 X#define S_Get_Data 4 X#define S_Get_Checksum 5 X#define S_Timed_Out 6 X#define S_Send_NAK 7 X#define S_Got_NAK 8 X#define S_Got_ACK 9 X X/* Receiver actions */ X X#define R_Get_DLE 0 X#define R_Get_B 1 X#define R_Get_Seq 2 X#define R_Get_Data 3 X#define R_Get_Checksum 4 X#define R_Send_NAK 5 X#define R_Send_ACK 6 X#define R_Send_CTL 6 X Xstatic int XCh, XChecksum, XSeq_Num, XR_Size, /* Size of receiver buffer */ XXOFF_Flag, XSeen_ETX; X Xstatic char S_Buffer[MAX_PACKET+1]; /* Sender buffer */ Xstatic char R_Buffer[MAX_PACKET+1]; /* Receiver buffer */ X Xstatic int QBP_ws; Xstatic int QBP_wr; Xstatic int QBP_bs; Xstatic int QBP_cm; Xstatic int QBP_dq; X Xstatic int QBP_Active; /* Signifies windowing in effect */ Xstatic unsigned QBP_Packet; /* QB Packet Length */ Xstatic int QB_Out = 0; /* Count of outstanding packets */ Xstatic int QB_Old = 0; /* Oldest packet */ Xstatic int QB_Current = 0; /* Current packet */ X Xstatic struct QB_Hist { /* Packet Send-ahead buffer */ X int block; /* Block number */ X int size; /* Size of the packet */ X int chk; /* Checksum of packet */ X char seq; /* Sequence number of packet */ X char buf[MAX_PACKET]; /* History buffer */ X} QB_Buf[MAX_WINDOW]; X X#ifdef CIS_B_DEBUG Xstatic void xclog(dir, val) X char dir; X int val; X{ X static int cnt = 0; X static FILE *fp = NULL; X static int lastdir = 0; X X if (fp == NULL) { X fp = fopen("xcomm.log", "w"); X cnt = 0; X lastdir = dir; X } X X if (dir == 0) { X fprintf(fp, "\n"); X fclose(fp); X return; X } X X if (++cnt > 20 || lastdir != dir) { X fprintf(fp, "\n"); X cnt = 1; X } X X if (lastdir != dir) X fprintf(fp, "\n"); X X fprintf(fp, "%c%1x%1x ", dir, val/16, val % 16); X lastdir = dir; X} X#else X#define xclog(dir,val) /* */ X#endif X Xstatic long filelength(fp) X FILE *fp; X{ X struct stat statbuf; X X fstat(fileno(fp), &statbuf); X X return statbuf.st_size; X} X Xstatic Put_Msg(Text) Xchar *Text; X{ X fprintf(tfp,"\r\n%s\r\n", Text); X} X Xstatic char *status_msg = "%-7.7s Blk %5d %7ld bytes %-30.30s\r"; X Xstatic Show_ACK() X{ X ACK_Count++; X NAK_Count = 0; X fprintf(tfp,status_msg, "ACK", ACK_Count, io_count, " "); X fflush(tfp); X} X Xstatic Show_XMIT(msg) X char *msg; X{ X fprintf(tfp,status_msg, msg, QB_Buf[QB_Current].block, io_count, " "); X fflush(tfp); X} X Xstatic Show_W_ACK() X{ X fprintf(tfp,status_msg, "ACK", QB_Buf[QB_Old].block, io_count, " "); X fflush(tfp); X} X Xstatic Show_NAK(msg) X{ X char NAK_msg[8]; X X sprintf(NAK_msg, "NAK(%d)", NAK_Count++); X fprintf(tfp,status_msg, NAK_msg, ACK_Count, io_count, msg); X fflush(tfp); X} X Xstatic Send_Byte(Ch) X{ X Ch &= 0xff; X X sendbyte(Ch); X xclog('>', Ch); X} X Xstatic Send_Masked_Byte(Ch) X{ X /* Mask any protocol or flow characters */ X X Ch &= 0xff; X X if (Ch == ETX || Ch == ENQ || Ch == DLE || X Ch == NAK || Ch == XON || Ch == XOFF) X { X Send_Byte(DLE); X Send_Byte(Ch + '@'); X } X else Send_Byte(Ch); X} X Xstatic Send_ACK() X{ X Send_Byte(DLE); X Send_Byte(Seq_Num + '0'); X} X Xstatic Read_Byte() X{ X if ((Ch = readbyte(Max_Time)) < 0 || Wants_To_Abort()) X return Failure; X else { X xclog('<', Ch); X return Success; X } X} X Xstatic Read_Masked_Byte() X{ X Seen_ETX = FALSE; X if (Read_Byte() == Failure) return Failure; X X if (Ch == DLE) { X if (Read_Byte() == Failure) X return Failure; X Ch -= '@'; X } else if (Ch == ETX) X Seen_ETX = TRUE; X X return Success; X} X Xstatic unsigned short crctab[256] = { X 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, X 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, X 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, X 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, X 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, X 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, X 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, X 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, X 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, X 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, X 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, X 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, X 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, X 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, X 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, X 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, X 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, X 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, X 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, X 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, X 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, X 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, X 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, X 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, X 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, X 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, X 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, X 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, X 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, X 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, X 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, X 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 X}; X X#define updcrc(ch, crc) (crctab[((crc >> 8) ^ ch) & 0xff] ^ (crc << 8)) X Xstatic Do_Checksum(Ch) X unsigned Ch; X{ X if (QBP_cm == 0) { /* Normal B Checksum */ X Checksum <<= 1; X if (Checksum > 0xff) X Checksum = (Checksum & 0xff) + 1; X X Checksum += (Ch & 0xff); X if (Checksum > 0xff) X Checksum = (Checksum & 0xff) + 1; X } else /* XMODEM CRC-16 */ X Checksum = (updcrc(Ch & 0xff, Checksum) & 0xffff); X /* The 0xffff is due to the fact that my X machine uses four-byte integers! */ X} X Xstatic int Read_Packet(Action) Xint Action; X{ X int Next_Seq; X int Errors = 0; X char *msg = "Timeout"; X int his_chk; X char mesag[30]; X X while (Errors < Max_Errors && !Wants_To_Abort()) X switch (Action) { X case R_Get_DLE: X if (Read_Byte() == Failure) X Action = R_Send_NAK; X else if (Ch == DLE) X Action = R_Get_B; X else if (Ch == ENQ) X Action = R_Send_ACK; X break; X X case R_Get_B: X if (Read_Byte() == Failure) X Action = R_Send_NAK; X else if (Ch == 'B') X Action = R_Get_Seq; X else X Action = R_Get_DLE; X break; X X case R_Get_Seq: X if (Read_Byte() == Failure) X Action = R_Send_NAK; X else { X Checksum = QBP_cm ? -1 : 0; X Next_Seq = Ch - '0'; X Do_Checksum(Ch); X R_Size = 0; X Action = R_Get_Data; X } X X case R_Get_Data: X if (Read_Masked_Byte() == Failure) X Action = R_Send_NAK; X else if (Seen_ETX) X Action = R_Get_Checksum; X else if (R_Size > QBP_Packet) { X msg = "Overrun"; X Action = R_Send_NAK; X } else { X R_Buffer[R_Size++] = Ch; X Do_Checksum(Ch); X } X X break; X X case R_Get_Checksum: X Do_Checksum(ETX); X X if (Read_Masked_Byte() == Failure) { X Action = R_Send_NAK; X break; X } X X if (QBP_cm) { X his_chk = Ch<<8; X if (Read_Masked_Byte() == Failure) { X Action = R_Send_NAK; X break; X } X his_chk |= Ch; X } else X his_chk = Ch; X X if (Checksum != his_chk) { X sprintf(mesag, "Checksum %4x s/b %4x", his_chk, Checksum); X msg = mesag; X Action = R_Send_NAK; X } else if (Next_Seq == Seq_Num) X Action = R_Send_ACK; X else if (Next_Seq != (Seq_Num + 1) % 10) { X msg = "Bad Block"; X Action = R_Send_NAK; X } else { X Seq_Num = Next_Seq; X return Success; X } X X break; X X case R_Send_NAK: X Errors++; X Send_Byte(NAK); X Show_NAK(msg); X Action = R_Get_DLE; X break; X X case R_Send_ACK: X Send_ACK(); X Action = R_Get_DLE; X break; X } X X return Failure; X} X X#define Next_W(ind) (ind++, ind %=QBP_ws) X Xstatic int Send_Window(Size) X int Size; /* size of packet to send */ X{ X int Action; X int I; X char *ptr; X struct QB_Hist *QB_Ptr; X X if (QB_Out == QBP_ws) { X if (get_ACK() == Failure) X return Failure; X } X X QB_Ptr = &QB_Buf[QB_Current]; X X Seq_Num = (Seq_Num + 1) % 10; X X Checksum = QBP_cm ? -1 : 0; X QB_Ptr->block = ++XMIT_Count; X QB_Ptr->seq = Seq_Num + '0'; X Do_Checksum(Seq_Num + '0'); X X QB_Ptr->size = Size; X for (ptr = QB_Ptr->buf, I = 0; I < Size; I++) { X Do_Checksum(S_Buffer[I] + 0); X *ptr++ = S_Buffer[I]; X } X Do_Checksum(ETX); X QB_Ptr->chk = Checksum; X X Send_Buf(QB_Current); X QB_Out++; X Next_W(QB_Current); X X return Success; X} X XSend_Buf(Ind) X{ X char *ptr; X int i; X X struct QB_Hist *QB_Ptr = &QB_Buf[Ind]; X X Show_XMIT(XMIT_Count == QB_Ptr->block?"Trans":"Retrans"); X X Send_Byte(DLE); X Send_Byte('B'); X Send_Byte(QB_Ptr->seq); X for (ptr = QB_Ptr->buf, i = QB_Ptr->size; i > 0; i--) X Send_Masked_Byte(*ptr++); X X Send_Byte(ETX); X if (QBP_cm) X Send_Masked_Byte(QB_Ptr->chk >> 8); X X Send_Masked_Byte(QB_Ptr->chk & 0xff); X X} X Xget_ACK() X{ X int Action = S_Get_DLE; X int Errors = 0; X int hold, cnt; X X Errors = 0; X X while (QB_Out >= QBP_ws) { X if (Errors > Max_Errors || Wants_To_Abort()) X return Failure; X X switch (Action) { X case S_Get_DLE: X if (Read_Byte() == Failure) { X Action = S_Timed_Out; X break; X } X X if (Ch == DLE) { X Action = S_Get_Num; X break; X } X X if (Ch == NAK) { X Action = S_Got_NAK; X break; X } X X break; X X case S_Timed_Out: X Errors++; X Action = S_Get_DLE; X break; X X case S_Get_Num: X if (Read_Byte() == Failure) { X Action = S_Timed_Out; X break; X } X X if (Ch < '0' || Ch > '9') { X Errors++; X Action = S_Get_DLE; X break; X } X /* Figure out which block was ACK'd */ X while (QB_Buf[QB_Old].seq != Ch && QB_Out > 0) { X QB_Out--; X Next_W(QB_Old); X } X X if (QB_Buf[QB_Old].seq != Ch) X return Failure; /* total loss of sync! */ X X Show_W_ACK(); X Action = S_Get_DLE; X break; X X case S_Got_NAK: X Show_NAK(); X X QB_Current = QB_Old; /* Reset currency point */ X for (cnt = 0; cnt < QB_Out; cnt++, Next_W(QB_Current)) { X if (Wants_To_Abort()) X return Failure; X Send_Buf(QB_Current); X } X X Action = S_Get_DLE; X break; X } X } X X return Success; X} X Xstatic int Send_Packet(Size) Xint Size; /* size of packet to send */ X{ X int Action, Next_Seq, RCV_Num, I, Errors, his_chk; X char *msg = "Timeout"; X char mesag[30]; X X Next_Seq = (Seq_Num + 1) % 10; X Errors = 0; X Action = S_Send_Packet; X X while (Errors < Max_Errors && !Wants_To_Abort()) X switch (Action) { X case S_Send_Packet: X Checksum = 0; X Send_Byte(DLE); X Send_Byte('B'); X Send_Byte(Next_Seq + '0'); X Do_Checksum(Next_Seq + '0'); X X for (I = 0; I < Size; I++) { X Send_Masked_Byte(S_Buffer[I]); X Do_Checksum(S_Buffer[I] + 0); X } X X Send_Byte(ETX); X Do_Checksum(ETX); X Send_Masked_Byte(Checksum & 0xff); X Action = S_Get_DLE; X break; X X case S_Get_DLE: X if (Read_Byte() == Failure) X Action = S_Timed_Out; X else if (Ch == DLE) X Action = S_Get_Num; X else if (Ch == NAK) { X Errors++; X Action = S_Send_Packet; X Show_NAK("Unknown"); X } X X break; X X case S_Get_Num: X if (Read_Byte() == Failure) Action = S_Timed_Out; X else if (Ch >= '0' && Ch <= '9') X { X if (Ch == Seq_Num + '0') X Action = S_Get_DLE; X else if (Ch == Next_Seq + '0') X { X Seq_Num = Next_Seq; X return Success; X } X else if (Errors == 0) Action = S_Send_Packet; X else Action = S_Get_DLE; X } X else if (Ch == WACK) X { X sleep(5); /* Sleep for 5 seconds */ X Action = S_Get_DLE; X } X else if (Ch == 'B') Action = S_Get_Seq; X else Action = S_Get_DLE; X break; X X case S_Get_Seq: X /** X * Start of a "B" protocol packet. The only packet that makes X * any sense here is a failure packet. X **/ X X if (Read_Byte() == Failure) X Action = S_Send_NAK; X else X { X Checksum = 0; X RCV_Num = Ch - '0'; X Do_Checksum(Ch); X I = 0; X Action = S_Get_Data; X } X X break; X X case S_Get_Data: X if (Read_Masked_Byte() == Failure) X Action = S_Send_NAK; X else if (Seen_ETX) X Action = S_Get_Checksum; X else if (I > QBP_Packet) { X msg = "Overrun"; X Action = S_Send_NAK; X } else { X R_Buffer[I++] = Ch; X Do_Checksum(Ch); X } X X break; X X case S_Get_Checksum: X Do_Checksum(ETX); X X if (Read_Masked_Byte() == Failure) X Action = S_Send_NAK; X X if (QBP_cm) { X his_chk = Ch<<8; X if (Read_Masked_Byte() == Failure) { X Action = R_Send_NAK; X break; X } X his_chk |= Ch; X } else X his_chk = Ch; X X if (Checksum != his_chk) { X sprintf(mesag, "Checksum %4x s/b %4x", his_chk, Checksum); X msg = mesag; X Action = S_Send_NAK; X } X else if (RCV_Num != (Next_Seq + 1) % 10) X { X msg = "Sequence"; X Action = S_Send_NAK; X } X else X { X /** X * Assume the packet is failure packet. It makes no X * difference since any other type of packet would be X * invalid anyway. Return failure to caller. X **/ X X Errors = Max_Errors; X } X X break; X X case S_Timed_Out: X Errors++; X Action = S_Get_DLE; X break; X X case S_Send_NAK: X Errors++; X Send_Byte(NAK); X Action = S_Get_DLE; X Show_NAK(msg); X break; X } X X return Failure; X} X Xstatic Send_Failure() X{ X register i; X X if (Failure_Sent++) X return; X X Send_Byte(DLE); /* Panic abort (usually works) */ X Send_Byte(DLE); X Send_Byte(DLE); X Send_Byte(DLE); X Send_Byte(DLE); X} X Xstatic int Receive_File(Name) Xchar *Name; X{ X FILE *Data_File; X int exists = (access(Name, 0) == 0); X X if (!mungmode && exists) { X Put_Msg("Transfer aborted due to file pre-existance."); X Send_Failure(); X return Failure; X } X X if ((Data_File = fopen(Name, "w")) == NULL) { X Put_Msg("Cannot create file"); X Send_Failure(); X return Failure; X } X X if ((Data_File = fopen(Name, "w")) == NULL) { X Put_Msg("Cannot create file"); X Send_Failure(); X return Failure; X } X X fprintf(tfp,"Receiving %s file %s\r\n", Text_Mode ? "Ascii":"Binary", Name); X Send_ACK(); X X for (;;) X if (Read_Packet(R_Get_DLE) == Success) X switch (R_Buffer[0]) { X case 'N': /* Data packet */ X if (Write(Data_File, &R_Buffer[1], R_Size - 1)) { X Put_Msg("Disk write error"); X Send_Failure(); X fclose(Data_File); X return Failure; X } X X if (Wants_To_Abort()) { X Send_Failure(); X fclose(Data_File); X return Failure; X } X X Send_ACK(); X Show_ACK(); X break; X X case 'T': /* Transfer packet */ X if (R_Buffer[1] == 'C') { X Send_ACK(); X fclose(Data_File); X return Success; X } else { X Put_Msg("Unexpected packet type"); X Send_Failure(); X fclose(Data_File); X return Failure; X } X X case 'F': /* Failure packet */ X Send_ACK(); X fclose(Data_File); X return Failure; X } X else { X fclose(Data_File); X return Failure; X } X} X Xstatic int Send_File(Name) Xchar *Name; X{ X FILE *Data_File; X int N; X long size; X X if ((Data_File = fopen(Name, "r")) == NULL) X { X Put_Msg("Cannot access that file"); X Send_Failure(); X return Failure; X } X X size = filelength(Data_File); X X fprintf(tfp,"Transmitting %s file %s (%ld bytes -- %ldK)\r\n", X Text_Mode ? "Ascii":"Binary", Name, size, size / 1024); X X do { X S_Buffer[0] = 'N'; X N = Read(Data_File, &S_Buffer[1], QBP_Packet); X X if (N > 0) { X if (Send_Window(N + 1) == Failure) { X fclose(Data_File); X return Failure; X } X X if (Wants_To_Abort()) { X Send_Failure(); X fclose(Data_File); X return Failure; X } X } X } while (N > 0); X X if (N == 0) { X fclose(Data_File); X S_Buffer[0] = 'T'; X S_Buffer[1] = 'C'; X if (Send_Window(2) == Failure) X return Failure; X return get_ACK(); X } else { X Put_Msg("Disk read error"); X Send_Failure(); X return Failure; X } X} X Xstatic int Transfer_File() X{ X XOFF_Flag = FALSE; X Seq_Num = 0; X X for (;;) { X if (Read_Packet(R_Get_DLE) == Success) X switch (R_Buffer[0]) { X case '+': /* Get/Set QB Parameters */ X Get_QB_Parms(); X break; X X case 'T': /* Transfer packet */ X return do_transfer(); X break; X X default: X Send_Failure(); /* Undefined packet */ X return Failure; X } X else X return Failure; X } X} X Xstatic int Get_QB_Parms() X{ X S_Buffer[0] = '+'; X S_Buffer[1] = QBP_wr = min(1, R_Buffer[1]); X S_Buffer[2] = QBP_ws = min(MAX_WINDOW - 1, R_Buffer[2]); X S_Buffer[3] = QBP_bs = min(MAX_PACKET / 128, R_Buffer[3]); X S_Buffer[4] = min(1, R_Buffer[4]); X S_Buffer[5] = QBP_dq = 1; /* Normal B Protocol Quoting */ X X QBP_ws++; /* Actual window size */ X X if (Send_Packet(6) != Success) X return Failure; X X QBP_cm = S_Buffer[4]; X QBP_Active = TRUE; X QBP_Packet = QBP_bs * 128; X} X Xdo_transfer() X{ X int I, N; X char *Name; X X /* Check data direction */ X X if (R_Buffer[1] != 'D' && R_Buffer[1] != 'U') X { X Send_Failure(); /* not implemented */ X return Failure; X } X X /* Check the file type */ X X if (R_Buffer[2] != 'A' && R_Buffer[2] != 'B') X { X Send_Failure(); X return Failure; X } X X /* Let CIS tell us if it is Ascii or not */ X X Text_Mode = (R_Buffer[2] == 'A'); X X /* Collect the file name */ X X Name = &R_Buffer[3]; X Name[R_Size - 3] = '\0'; X X /* Do the transfer */ X X start_time = time(NULL); X X if (R_Buffer[1] == 'U') X return Send_File(Name); X else X return Receive_File(Name); X} X Xstatic Write(fp, buf, cnt) X FILE *fp; X char *buf; X int cnt; X{ X for (; cnt-- > 0; buf++) { X io_count++; X X if (Text_Mode && (*buf == '\r' && *(buf+1) == '\n')) X continue; X X if (putc(*buf, fp) == EOF) X return -1; X } X X return 0; X} X Xstatic Read(fp, buf, max) X FILE *fp; X char *buf; X int max; X{ X int c, cnt = 0; X X while (max--) X switch (c = getc(fp)) { X case EOF: X return cnt; X X case '\n': X if (Text_Mode && Last_Chr != '\r') { X ungetc(c, fp); X c = '\r'; X } X X default: X Last_Chr = *buf++ = c; X cnt++; io_count++; X break; X } X X return cnt; X} X Xint B_Transfer(mode) X{ X int rc; X void newsigint(); X long elapsed; X X QBP_Active = FALSE; /* Start in normal B Mode */ X QBP_ws = 0; X QBP_wr = 0; X QBP_bs = 0; X QBP_cm = 0; X QBP_dq = 0; X QBP_Packet = 512; /* Normal packet size maximum */ X X fprintf(tfp,"CIS QuickB Protocol Transfer\r\n"); X X Failure_Sent = Abort_Flag = Last_Chr = 0; X XMIT_Count = ACK_Count = NAK_Count = 0; X io_count = 0L; X X signal(SIGINT, newsigint); X intdel(1); X X if (setjmp(err_buf) == 0) { X xc_setxon(0); /* turn off flow control */ X X Send_Byte(NAK); X X if (startup() == Failure) X Abort_Flag = 1; X else X rc = Transfer_File(); X } X X if (Abort_Flag) { X Send_Failure(); X purge(); X } X X if (Abort_Flag || rc == Failure) X fprintf(tfp,"\r\nFile Transfer aborted\r\n"); X else X fprintf(tfp,"\r\nFile Transfer successful\r\n"); X X if (io_count) { X elapsed = time(NULL) - start_time; X if (elapsed > 0) { X bytes_sec = io_count / start_time; X fprintf(tfp,"Average %ld bytes/sec\r\n", bytes_sec); X } X } X X xclog(0,0); X X intdel(0); X signal(SIGINT, SIG_IGN); X X return rc; X} X Xstatic startup() X{ X int ch; X X do { X Read_Byte(); X if (Ch != ENQ) X Send_Byte(NAK); X if (Wants_To_Abort()) X return Failure; X } while (Ch != ENQ); X X Send_Byte(DLE); Send_Byte('+'); /* Quick B Indicator */ X Send_Byte(DLE); Send_Byte('0'); X X return Success; X} X Xstatic void newsigint() X{ X Abort_Flag = 1; X X signal(SIGINT, SIG_IGN); X X fprintf(tfp,"\r\nUser Abort...\r\n"); X} / echo x - xcomm.h sed '/^X/s///' > xcomm.h << '/' X/* X * XCOMM Global Definitions X */ X X#define ENQ 0x05 /* CIS ENQ */ X X#define ESCAPE_CHR 0x1b X X#define HELPCHAR 0xBF /* ALT-? Teminal Mode Help */ X#define ENDCHAR 0xF8 /* ALT-x Exit terminal mode */ X#define TOGCHAR 0xE3 /* ALT-c Toggle capture buffer */ X#define DIVCHAR 0xF3 /* ALT-s Send file through modem */ X#define PSELECT 0xE4 /* ALT-d Dial from phonelist */ X#define HUPCHAR 0xE8 /* ALT-h Hang up modem */ X#define SCRPCHR 0xE7 /* ALT-g Execute script file */ X X#define CAPTFILE "capture.log" /* Default capture file */ X#define PHFILE ".phonelist" /* Default phonelist file */ X#define STARTUP ".xcomm" /* XCOMM Startup Script */ X#define DRIBBLE 2 /* Text "dribble" delay */ X#define CIS_INIT 1 /* cismode default 1=on/0=off */ X X#define index strchr /* for SYSV */ X#define rindex strrchr /* for SYSV */ X X/* X Other defines X*/ X X#ifndef TRUE X#define TRUE 1 X#define FALSE 0 X#endif X X#define K 1024 X#define NMSIZE 256 /* file name buffer size */ X#define WBSIZE 256 /* input and working buffer size */ X X/* globals */ X X#include X Xextern jmp_buf erret; Xextern int verbose, mungmode, bitmask, cbsiz, crcheck, badline, flowflag; Xextern int cismode, linkflag, quote_flag, eof_flag, nlmode, scriptflag; Xextern char captfile[NMSIZE], phonefile[NMSIZE], *mport(); Xextern char word[], *wptr, line[]; X Xstruct kw { /* Used by command parsing routines */ X char *keyword; X void (*rtn)(); X}; / echo x - xcxmdm.c sed '/^X/s///' > xcxmdm.c << '/' X/* X * Module: xcxmdm.c XMODEM Protocol Module X * X * This source code is purely public domain X */ X X#include X#include X#include X#include X#include "xcomm.h" X X#define SOH 001 /* ^A */ X#define EOT 004 /* ^D */ X#define ACK 006 /* ^F */ X#define NAK 025 /* ^U */ X#define CAN 030 /* ^X */ X#define CPMEOF 032 /* ^Z */ X#define WANTCRC 'C' X#define OK 0 X#define TIMEOUT -1 /* -1 is returned by readbyte() upon timeout */ X#define ERROR -2 X#define WCEOT -3 X#define RETRYMAX 10 X#define SECSIZ 128 X X/* globals */ Xint badline = FALSE; /* bad telephone line? */ Xint crcheck = TRUE; /* CRC check enabled? */ Xextern FILE *tfp; /* TTY File pointer */ X X/* locals */ Xstatic FILE *xfp; /* buffered file pointer */ Xstatic int firstsec; /* first sector of file or not? */ Xstatic char wcbuff[SECSIZ]; /* Ward Christensen sector buffer */ Xstatic unsigned short updcrc(); X Xstatic void newsigint(); /* Our SIGINT handler */ Xstatic int save_crc; /* Saved crcheck value */ Xstatic jmp_buf our_env; X Xxreceive() X{ X if (word[0] == '\0') { X fprintf(tfp,"No file name specified for XMODEM receive\r\n"); X return; X } X X xc_setxon(0); /* turn off XON/XOFF */ X save_crc = crcheck; X signal(SIGINT, newsigint); /* Install our handler */ X X if (!setmode()) { X if (setjmp (our_env) == 0) { X crcheck = 0xff; X do_xreceive(word); X } X } X X signal(SIGINT, SIG_IGN); /* Reinstall old handler */ X crcheck = save_crc; X restmode(); X} X Xxsend() X{ X if (word[0] == '\0') { X fprintf(tfp,"No file name specified for XMODEM send\r\n"); X return; X } X X xc_setxon(0); /* turn off XON/XOFF */ X save_crc = crcheck; X signal(SIGINT, newsigint); /* Install our handler */ X X if (!setmode()) { X if (setjmp (our_env) == 0) { X crcheck = 0xff; X do_xsend(word); X } X } X X signal(SIGINT, SIG_IGN); /* Reinstall old handler */ X crcheck = save_crc; X restmode(); X} X Xstatic void newsigint() X{ X fprintf(tfp,"\r\nUser abort\r\n"); X signal(SIGINT, SIG_IGN); /* Ignore subsequent DEL's */ X canit(); /* Abort the transmission */ X longjmp(our_env, 0); X} X Xstatic restmode() X{ X intdel(0); X xc_setflow(); /* restore XON/XOFF */ X} X Xstatic do_xreceive(p) Xchar *p; X{ X long bytes, ftime; X short fmode; X char *ctime(); X X fprintf(tfp,"XCOMM: Ready to receive single file %s\r\n", p); X if (wcrx(p) == ERROR) X canit(); X return; X} X Xdo_xsend(p) Xchar *p; X{ X if (wctx(p) == ERROR) { X fprintf(tfp,"Error transmitting file %s\r\n", p); X return; X } X} X X/* X * Receive a file using XMODEM protocol X */ Xstatic wcrx(name) Xchar *name; X{ X register int sectnum, sectcurr, sendchar, eofseen; X X if (!mungmode && !access(name, 0)) { X canit(); X fprintf(tfp,"Receive of %s aborted due to pre-exsistence.\r\n"); X return(ERROR); X } X X if ((xfp = fopen(name, "w")) == NULL) { X canit(); X fprintf(tfp,"Receive of %s aborted due to inabilty to open.\r\n"); X return(ERROR); X } X firstsec = TRUE; X eofseen = FALSE; X sectnum = 0; X sendchar = crcheck ? WANTCRC : NAK; X fprintf(tfp,"Sync...\r\n"); X X while(TRUE) { X if (badline) X purge(); X sendbyte(sendchar); X sectcurr = wcgetsec(6); X if (sectcurr == ((sectnum + 1) & 0xff)) { X sectnum++; X putsec(); X fprintf(tfp,"Received sector #%d \r", sectnum); X fflush(tfp); X X sendchar = ACK; X continue; X } X X if (sectcurr == (sectnum & 0xff)) { X fprintf(tfp,"Received duplicate sector #%d \r", sectnum); X fflush(tfp); X sendchar = ACK; X continue; X } X X fprintf(tfp, "\r\n"); X fclose(xfp); X X if (sectcurr == WCEOT) { X fprintf(tfp,"File recived OK\r\n"); X sendbyte(ACK); X return(OK); X } X X if (sectcurr == ERROR) X return(ERROR); X X fprintf(tfp,"Sync error ... expected %d(%d), got %d\r\n", X (sectnum + 1) & 0xff, sectnum, sectcurr); X return(ERROR); X } X} X X/* X * Transmit a file using XMODEM protocol X */ Xstatic wctx(name) Xchar *name; X{ X register int sectnum, eoflg, c, attempts; X X if ((xfp = fopen(name, "r")) == NULL) { X fprintf(tfp,"Can't open file %s for reading.\r\n", name); X return(ERROR); X } X firstsec = TRUE; X attempts = 0; X fprintf(tfp,"Sync...\r\n"); X X while((c = readbyte(30)) != NAK && c != WANTCRC && c != CAN) X if (c == TIMEOUT && ++attempts > RETRYMAX) { X fprintf(tfp,"Receiver not responding.\r\n"); X fclose(xfp); X return(ERROR); X } X if (c == CAN) { X fprintf(tfp,"Receiver CANcelled.\r\n"); X fclose(xfp); X return(ERROR); X } X crcheck = (c == WANTCRC); X fprintf(tfp,"%s error checking requested.\r\n", X crcheck ? "CRC check" : "Checksum"); X sectnum = 1; X X do { X eoflg = getsec(); X fprintf(tfp,"Transmitting sector #%d\r", sectnum); X fflush(tfp); X X if (wcputsec(sectnum) == ERROR) { X fclose(xfp); X return(ERROR); X } X sectnum++; X } while(eoflg); X X fprintf(tfp, "\r\n"); X fclose(xfp); X attempts = 0; X sendbyte(EOT); X while(readbyte(5) != ACK && attempts++ < RETRYMAX) X sendbyte(EOT); X if (attempts >= RETRYMAX) { X fprintf(tfp,"Receiver not responding to completion.\r\n"); X return(ERROR); X } X X fprintf(tfp,"Transmission complete.\r\n"); X return(OK); X} X X/* wcgetsec() inputs an XMODEM "sector". X * This routine returns the sector number encountered, or ERROR if a valid X * sector is not received or CAN received; or WCEOT if EOT sector. X * X * Maxtime is the timeout for the first character, set to 6 seconds for X * retries. No ACK is sent if the sector is received ok. This must be X * done by the caller when it is ready to receive the next sector. X */ Xstatic wcgetsec(maxtime) Xint maxtime; X{ X register checksum, j, c; X register unsigned short oldcrc; X int sectcurr, sectcomp, attempts; X X for(attempts = 0; attempts < RETRYMAX; attempts++) { X do { X c = readbyte(maxtime); X } while(c != SOH && c != EOT && c != CAN && c != TIMEOUT); X X switch(c) { X case SOH: X sectcurr = readbyte(3); X sectcomp = readbyte(3); X if ((sectcurr + sectcomp) == 0xff) { X oldcrc = checksum = 0; X for(j = 0; j < SECSIZ; j++) { X if ((c = readbyte(3)) < 0) X goto timeout; X wcbuff[j] = c; X if (crcheck) X oldcrc = updcrc(c, oldcrc); X else X checksum += c; X } X if ((c = readbyte(3)) < 0) X goto timeout; X if (crcheck) { X oldcrc = updcrc(c, oldcrc); X if ((c = readbyte(3)) < 0) X goto timeout; X if (updcrc(c, oldcrc)) { X fprintf(tfp,"\r\nCRC error\r\n"); X break; X } X } X else if (((checksum - c) & 0xff) != 0) { X fprintf(tfp,"\r\nChecksum error\r\n"); X break; X } X firstsec = FALSE; X return(sectcurr); X } X else X fprintf(tfp,"\r\nSector number garbled 0%03o 0%03o\r\n", X sectcurr, sectcomp); X break; X case EOT: X if (readbyte(3) == TIMEOUT) X return(WCEOT); X break; X case CAN: X fprintf(tfp,"\r\nSender CANcelled.\r\n"); X return(ERROR); X case TIMEOUT: X if (firstsec) X break; Xtimeout: X fprintf(tfp,"\r\nTimeout\r\n"); X break; X } X fprintf(tfp,"\r\nTrying again on ths sector.\r\n"); X purge(); X if (firstsec) X sendbyte(crcheck ? WANTCRC : NAK); X else { X maxtime = 6; X sendbyte(NAK); X } X } X fprintf(tfp,"\r\nRetry count exceeded.\r\n"); X canit(); X return(ERROR); X} X X/* wcputsec outputs a Ward Christensen type sector. X * it returns OK or ERROR X */ Xstatic wcputsec(sectnum) Xint sectnum; X{ X register checksum, j, c, attempts; X register unsigned short oldcrc; X X oldcrc = checksum = 0; X for(j = 0; j < SECSIZ; j++) { X c = wcbuff[j]; X oldcrc = updcrc(c, oldcrc); X checksum += c; X } X oldcrc = updcrc(0, updcrc(0, oldcrc)); X X for(attempts = 0; attempts < RETRYMAX; attempts++) { X sendbyte(SOH); X sendbyte(sectnum); X sendbyte(-sectnum - 1); X for(j = 0; j < SECSIZ; j++) X sendbyte(wcbuff[j]); X if (badline) X purge(); X if (crcheck) { X sendbyte((int) (oldcrc >> 8)); X sendbyte((int) oldcrc); X } X else X sendbyte(checksum); X switch(c = readbyte(10)) { X case CAN: X fprintf(tfp,"\r\nReceiver CANcelled.\r\n"); X return(ERROR); X case ACK: X firstsec = FALSE; X return(OK); X case NAK: X fprintf(tfp,"\r\nGot a NAK on sector acknowledge.\r\n"); X break; X case TIMEOUT: X fprintf(tfp,"\r\nTimeout on sector acknowledge.\r\n"); X break; X default: X fprintf(tfp,"\r\nGot 0%03o for sector acknowledge.\r\n", c); X do { X if ((c = readbyte(3)) == CAN) { X fprintf(tfp,"\r\nReceiver CANcelled.\r\n"); X return(ERROR); X } X } while(c != TIMEOUT); X if (firstsec) X crcheck = (c == WANTCRC); X break; X } X } X fprintf(tfp,"\r\nRetry count exceeded.\r\n"); X return(ERROR); X} X X/* update the cyclic redundantcy check value X */ Xstatic unsigned short updcrc(c, crc) Xregister c; Xregister unsigned crc; X{ X register int i; X X for(i = 0; i < 8; i++) { X if (crc & 0x8000) { X crc <<= 1; X crc += (((c <<= 1) & 0400) != 0); X crc ^= 0x1021; X } X else { X crc <<= 1; X crc += (((c <<= 1) & 0400) != 0); X } X } X return(crc); X} X Xstatic zerobuff() X{ X register int i; X X for(i = 0; i < SECSIZ; i++) X wcbuff[i] = '\0'; X} X X/* send 10 CAN's to try to get the other end to shut up */ Xstatic canit() X{ X register int i; X X for(i = 0; i < 20; i++) X sendbyte(CAN); X} X Xstatic setmode() X{ X intdel(1); X return(0); X} X X/* fill the CP/M sector buffer from the UNIX file X * return 1 if more sectors are to be read, or 0 if this is the last X */ Xstatic getsec() X{ X register int i, c; X X i = 0; X while(i < SECSIZ && (c = getc(xfp)) != EOF) { X wcbuff[i++] = c; X } X /* make sure that an extra blank sector is not sent */ X if (c != EOF && (c = getc(xfp)) != EOF) { X ungetc(c, xfp); X return(1); X } X /* fill up the last sector with 0's if binary mode */ X while(i < SECSIZ) X wcbuff[i++] = '\0'; X return(0); X} X X/* Put received WC sector into a UNIX file X * using text translations in nessesary. X */ Xstatic putsec() X{ X register int i, c; X X for(i = 0; i < SECSIZ; i++) { X c = wcbuff[i]; X putc(c, xfp); X } X return(0); X} /