[net.followup] PC<==>UNIX SUMMARY

perl@rdin2.UUCP (Robert Perlberg) (05/16/84)

<>

Thanks to the people who sent me information on tranfering
files between UNIX and PC-DOS.

The package most recommended was KERMIT.  Here are some of
the comments I received on it:

It is a public-domain terminal emulator and file
transfer package that runs on *MANY* different mainframes, minis, and micros,
with many maintainers and central coordination.  Cost ~$100.
There is a C version for Unix, and an assembler version for the PC.
The UNIX version is still a little buggy, but they are
working on fixing it.  Mail to CC.FDC@Columbia-20.ARPA for more information.
His name is Frank DeCruise (sp?).  Contact

Daphne Tzoar
Columbia University Computer Center
612 W. 115th St.
New York City, NY 10025

for more information, or you can get the files via anonymous ftp via the 
arpanet.  Connect to COLUMBIA-20, and look at directory <KERMIT>, or they
can mail you a magtape with all the files in the <KERMIT> directory.

There is a special interest group on KERMIT available on the ARPA
net as:
	INFO-KERMIT@COLUMBIA-20

of course requests would be routed to

	INFO-KERMIT-REQUEST@COLUMBIA-20


Here are the rest of the responses:

---
there are versions of the CP/M modem program for both MS-DOS and Unix
(also tops-20, Xerox lisp machines and others).  See SIMTEL-20
---
General Micro Systems has a "PC-100" vt100 emulator which is available
under PC-DOS or QNX (which I use).  It uploads and downloads with
4.2bsd quite successfully.

GMS
7525 Mitchell Rd.
Suite 101
Eden Prairie, MN  55344
---
IF you have the PC/XT, you should look into Venix/86 offered by
Unisource Software Corp. (617-491-1264) for $800.  Venix is an
implementation of UNIX System III for PCs (Venix/86 is specifically
for the IBM), and includes cu and uucp.  If you don't have a hard
disk, I think Unisource offers a package (10 Meg hard disk with
Venix loaded) - can't recall their price.
---
There is a product called PC Works which is supposed to set up a pretty nice
network between a UNIX system and one or more PC's. One of our customers
(Molecular Compuer) ported this to one of our systems in fairly short order ;
it may well already be available for the Masscomp. Our marketing department
picked up on this in a hurry and want to sell it (I have to approve it
first, and I haven't seen a copy yet, so I can't tell you anything more
useful). As our marketing people want to price it, it runs $495 for the
basic product, plus $195 for each additional PC (beyond the first one) that
you want to hook up. I don't know if this is in line with what you want,
but if it sounds interesting I will dredge up the name/address of the company
that produces it, and mail it to you, and you can contact them yourself.

	    Mats Wichmann
	    Dual Systems Corp.
	    ...{ucbvax,amd70,ihnp4,cbosgd,decwrl,fortune}!dual!mats
---
I have found the terminal emulator written by Jim Holtman and his son
to be quite good - also its free, and the source comes with it. What more
could a person ask?

It makes the IBM PC emulate either an "HP-like" terminal or an adm3a. It
does require the rs232int part of the IBM Asynchronous Comm Support. Its
written in IBM Pascal (I recompiled the whole thing, and it still worked;
something I can't say for all the software I've tried that on). It starts
up *much* faster than the IBM emulator; the adm3a is a good emulation
(works with vi and some other software that I've tried.  A ventel
autodialing modem is supported.

For file transfer, you can do it the hard way (invoke ed or something, then
dump the IBM file to the line, then finish up), an automatic up/down link
(just for IBM<->UNIX; it does the work), and XMODEM protocols (the UNIX
end source is provided too).

All in all, an excellent working package. Jim's addresses are:

	Jim Holtman                     201/361-3395
	35 Dogwood Trail                harpo!whuxlb!jph
	Randolph, NJ 07869

Thanks, Jim!
-- 
		Lyle McElhaney
		(hao,brl-bmd,nbires,csu-cs,scgvaxd)!denelcor!lmc
---
I posted the source of `xsend' and `xrecv' which allow (multiple)
file transfer between PC and UN*X based on XMODEM protocol.
What you need to run on your PC/XT is either a PERFECT LINK,
PC-TALK III (from Freeware) or MODEM7xx program (which is being
circulated around PC community for free also).  If you have any
further questions on that, mail me.

kha sin
---
The PC/InterComm terminal emulator from Mark of the Unicorn can handle
XMODEM on the PC side.  The UMODEM public domain XMODEM program for UNIX
is available on BBSs, etc.  Also, you may use the XMODEM spec published
in net.sources a week ago to write your own program.

	Gary Samad
---
Hi,
	This is in response to your message on Usenet, regarding your
search for a convenient file transfer system between a PC and a UNIX
system.  I think my company may have a pretty good answer for you.

	LINK/pc is a vt100/vt52/ibm 3101 emulator that has most of
the standard stuff you would expect from a terminal emulation/file
transfer package (including command scripts for auto-login, DC-Hayes
modem manipulation, etc.).  In addition to being able to do file transfer
in the conventional inconvenient way, however, LINK/pc also allows
file transfers to be remotely invoked - the UNIX system can
tell the PC that a file is about to be transferred up to it, or that it
should transfer a file down to the UNIX system.  This allows us to put 
control of the operation into one set of hands. In our case, however, 
we chose to put the control into the more powerful hands - the UNIX system.
Using this facility, we have put together a couple of simple tools -
utopccp and pctoucp that have a "cp" like syntax but are used for copying
files between a PC and a UNIX system.  For example, 
	utopccp *.c *.h a:
will transfer all files with .c and .h suffixes to drive a: on the pc.
Similarly,
	pctoucp c:xyz.dat a:abc.dat .
will transfer the indicated files to the current directory.  We have the 
xmodem protocol implemented if you wish to do the transfer using 
an error correcting protocol.  Naturally, you can include these commands
in any aliases, or shell scripts you might have - the only caveat being
that the commands do assume that they have exclusive use of the tty line
for the duration of their execution.

	The source for these commands and 5 (or 6?) simpler example programs
is included when you get a LINK/pc

	But wait!  There's more!  LINK/pc also allows you to remotely 
execute PC-DOS commands.  In a similar fashion to the file transfer, the
UNIX system can tell the PC (running DOS V2.0 with >192K of memory)
to execute a program.  Upon termination of the program you are returned to
LINK/pc.  We also provide a nice UNIX cover utility for this capablity,
that you can include in shell scripts, aliases, etc..  I have a couple
of aliases, "dir" and "type" that allow me to transparently get directory
listings and view the contents of files without ever leaving LINK/pc or
UNIX.  This facility is very good for programs like spreadsheets, 
that are better suited to PCs than UNIX systems.  

	If you are interested, I would be happy to send you a brochure.
If you are very interested, the program costs $195.00 for version 2.0 
(xmodem and remote program invokation).  There are steep discounts for volume 
purchases, etc., etc..

	I will be interested in seeing the results of your survey.

	Yours,

	Douglas Orr


P.S.
	Our addresses are:

	sb1!mb2c!uofm-cv!cosivax!dbo

	COSI
	313 N. First St.
	Ann Arbor, Mi.  48103
	(313) 665-8778
---
I believe umodem, written in C to perform with the PD modem protocols,
may be close to what you're looking for.  Try the simtel archives: that's
where I got my copy.
					-- sam hahn [samuel@score]
---
We have a product you might be interested in.  If you send me you phone
number I will sic a salesman on you.

Cheers,
Topher Eliot
Cyb Systems, Austin, TX
{seismo, allegra, ihnp4}!ut-sally!cyb-eng!topher
---

Before I include the final response (a source), here is a
list of the people I got responses from:

acf4!mta7438            Mark Anders
kpno!brown              Mike Brown
utah-cs!brownc		Eric C. Brown
sun!djc                 dave cardinal
sdcrdcf!darrelj
cwruecmp!diamant        John Diamant
cyb-eng!topher		Topher Eliot
SAMUEL@SU-SCORE.ARPA	sam hahn
LARSON@USC-ECLB.ARPA	Bob Larson
mike@LOGICON.ARPA	Mike Parker
alberta!jeff            C. J. Sampson
hou2b!sims              Jim Simester
auvax!khasin		kha sin
dual!mats               Mats Wichmann

Finally, thanks to Jim Holtman (whuxle!jph) for the offer he
posted.  Thanks also to the other people who posted
responses whose names I did not save.

Here, now, is the source I received.

Robert,

	I have used Crosstalk XVI for about six months now and have had 
great results. The latest version that I know of, ver 3.4 has the 
XMODEM protocol. I have, and am enclosing, a version of XMODEM to run
under BSD 4.2 Unix that was posted to the net a while back. I had no
trouble either downloading or uploading file between machines, and have
transfered dbaseII files between a CPM machine and MSDOS by using the
VAX as a temporary storage area. Though the XMODEM protocol is written
for BSD 4.2, and will not run directly on you system (at least that's
what I was told) it shouldn't be hard to modify it to run on yours.
Hopefully you'll be able to find an XMODEM package already written for
Sys III, and won't have to bother with this, but if not, you won't be
stuck. I should note that Crosstalk can emulate a vt100, and a few others,
so you can use vi (or play rogue), and that I had execellent results 
transfering files straight ascii, but it is of course much better to use
some form of error checking. Good luck.


				Mark Anders
				cmcl2!acf4!mta7438






/***********************************************************************/
/*
 *  XMODEM Version 1.0  - by Brian Kantor, UCSD
 *
 *  XMODEM -- Implements the "CP/M User's Group XMODEM" protocol, 
 *            for packetized file up/downloading.    
 *
 *	This version is designed for 4.2BSD ONLY!  It won't work
 *	ANYWHERE else - uses the 'select' system call to replace
 *	the old alarm handlers.
 *
 *   -- Based on UMODEM 3.5 by Lauren Weinstein, Richard Conn, and others.
 *
 */

#include <ctype.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sgtty.h>
#include <signal.h>

/* log default define */
#ifndef LOGDEFAULT
#define LOGDEFAULT	1
#endif

/* Delete logfile define.  Useful on small systems with limited
 * filesystem space and careless users.
 */
#ifndef DELDEFAULT
#define DELDEFAULT	1
#endif

#define	     VERSION	10	/* Version Number */
#define      FALSE      0
#define      TRUE       1


/*  ASCII Constants  */
#define      SOH  	001 
#define	     STX	002
#define	     ETX	003
#define      EOT	004
#define	     ENQ	005
#define      ACK  	006
#define	     LF		012   /* Unix LF/NL */
#define	     CR		015  
#define      NAK  	025
#define	     SYN	026
#define	     CAN	030
#define	     ESC	033
#define	     CTRLZ	032   /* CP/M EOF for text (usually!) */

/*  XMODEM Constants  */
#define      TIMEOUT  	-1
#define      ERRORMAX  	10    /* maximum errors tolerated */
#define      RETRYMAX  	10    /* maximum retries to be made */
#define	     BBUFSIZ	128   /* buffer size -- do not change! */

/*  Mode for Created Files  */
#define      CREATMODE	0644  /* mode for created files */

struct sgttyb  ttys, ttysnew, ttystemp;    /* for stty terminal mode calls */

struct stat statbuf;  	/* for terminal message on/off control */
char *strcat();
FILE *LOGFP, *fopen();
char buff[BBUFSIZ];
int nbchr;  /* number of chars read so far for buffered read */

int wason;

int pagelen;
char *ttyname();  /* forward declaration for C */
char *tty;
char XMITTYPE;
int CRCMODE, RECVFLAG, SENDFLAG, PMSG, DELFLAG, LOGFLAG, MUNGMODE;
int FILTER, DEBUG;
int STATDISP;
char filename[256];

main(argc, argv)
int argc;
char **argv;
{
	char *getenv();
	char *fname = filename;
	char *logfile;
	int index;
	char flag;

	logfile = "xmodem.log";  /* Name of LOG File */

	printf("\nXMODEM Version %d.%d", VERSION/10, VERSION%10);
	printf(" -- UNIX-CP/M Remote File Transfer Facility\n");

	if (argc < 3)
		{
		help(FALSE);
		exit(-1);
		}

	index = 0;  /* set index for loop */
	PMSG = FALSE;  /* turn off flags */
	DEBUG = FALSE;
	RECVFLAG = FALSE;  /* not receive */
	SENDFLAG = FALSE;  /* not send either */
	FILTER = FALSE;		/* assume literal mode */
	CRCMODE = FALSE;	/* use checksums for now */
	XMITTYPE = 't';  /* assume text */

	DELFLAG = DELDEFAULT;
	LOGFLAG = LOGDEFAULT;
	if (LOGFLAG) 
		LOGFLAG = TRUE;
	   else
		LOGFLAG = FALSE;  

	MUNGMODE = FALSE; /* protect files from overwriting */

	while ((flag = argv[1][index++]) != '\0')
	    switch (flag) {
		case '-' : break;
		case 'x' : DEBUG = TRUE;
			   break;
/* no crc mode yet
		case 'c' : CRCMODE = TRUE;
			   xmdebug("CRC mode selected");
			   break;
*/
		case 'd' : DELFLAG = !DELDEFAULT;  /* delete log file ? */
			   xmdebug("delete log toggled");
			   break;
		case 'l' : LOGFLAG = !LOGDEFAULT;  /* turn off log ? */
			   xmdebug("write log toggled");
			   break;
		case 'm' : MUNGMODE = TRUE; /* allow overwriting of files */
			   xmdebug("munge mode selected");
			   break;
		case 'r' : RECVFLAG = TRUE;  /* receive file */
			   XMITTYPE = gettype(argv[1][index++]);  /* get t/b */
			   xmdebug("receive mode selected");
			   break;
		case 's' : SENDFLAG = TRUE;  /* send file */
			   XMITTYPE = gettype(argv[1][index++]);
			   xmdebug("send mode selected");
			   break;
		case 'f' : FILTER = TRUE;
			   xmdebug("filter selected");
			   break;
		default  : error("Invalid Flag", FALSE);
		}

	if (LOGFLAG)
	   { 
	     if ((fname = getenv("HOME")) == 0)	/* Get HOME variable */
		error("Can't get Environment!", FALSE);
	     fname = strcat(fname, "/");
	     fname = strcat(fname, logfile);
	     if (!DELFLAG)
		LOGFP = fopen(fname, "a");  /* append to LOG file */
	     else
		LOGFP = fopen(fname, "w");  /* new LOG file */
	     if (!LOGFP)
		error("Can't Open Log File", FALSE);
	     fprintf(LOGFP,"\n\n++++++++\n");
	     fprintf(LOGFP,"\nXMODEM Version %d.%d\n", VERSION/10, VERSION%10);
	     printf("\nXMODEM:  LOG File '%s' is Open\n", fname);
	   }


	if (RECVFLAG && SENDFLAG)
		error("Both Send and Receive Functions Specified", FALSE);

	if (!RECVFLAG && !SENDFLAG)
		error("Either Send or Receive Function must be chosen!",FALSE);
	
	if (FILTER && (!RECVFLAG || XMITTYPE != 't'))
		error("Filter is only valid in text receive mode!",FALSE);

	if (RECVFLAG)
		{  
		if(open(argv[2], 0) != -1)  /* possible abort if file exists */
	   		{	
			printf("\nXMODEM:  Warning -- Target File Exists\n");
			if( MUNGMODE == FALSE )
				error("Fatal - Can't overwrite file\n",FALSE);
			printf("XMODEM:  Overwriting Target File\n");
	   		}
	   	rfile(argv[2]);  /* receive file */
		}

	if (SENDFLAG) 
		sfile(argv[2]);  /* send file */
	
	if (LOGFLAG) fclose(LOGFP);
	xmdebug("done");
	exit(0);
}

/*  Print Help Message  */
help()
	{
	xmdebug("help:");
	printf("\nUsage:  \n\txmodem ");
	printf("-[rb!rt!sb!st][options] filename\n");
	printf("\nMajor Commands --");
	printf("\n\trb <-- Receive Binary");
	printf("\n\trt <-- Receive Text");
	printf("\n\tsb <-- Send Binary");
	printf("\n\tst <-- Send Text");
	printf("\nOptions --");
#if DELDEFAULT == 1
	printf("\n\td  <-- Do not delete umodem.log file before starting");
#else
	printf("\n\td  <-- Delete umodem.log file before starting");
#endif

#if LOGDEFAULT == 1
	printf("\n\tl  <-- (ell) Turn OFF LOG File Entries");
#else
	printf("\n\tl  <-- (ell) Turn ON LOG File Entries");
#endif

/* no crc mode yet
	printf("\n\tc  <-- Select CRC mode on receive");
*/
	printf("\n\tf  <-- Filter 8-bit chars on receive - use with WordStar files");
	printf("\n");
	}

/* get type of transmission requested (text or binary) */
gettype(ichar)
char ichar;
	{
	xmdebug("gettype:");
	if (ichar == 't') return(ichar);
	if (ichar == 'b') return(ichar);
	error("Invalid Send/Receive Parameter - not t or b", FALSE);
	return;
	}

/* set tty modes for XMODEM transfers */
setmodes()
	{
	xmdebug("setmodes:");
	if (ioctl(0,TIOCGETP,&ttys)<0)  /* get tty params [V7] */
		error("Can't get TTY Parameters", TRUE);

	tty = ttyname(0);  /* identify current tty */
	
	/* transfer current modes to new structure */
	ttysnew.sg_ispeed = ttys.sg_ispeed;	/* copy input speed */
	ttysnew.sg_ospeed = ttys.sg_ospeed;	/* copy output speed */
	ttysnew.sg_erase  = ttys.sg_erase;	/* copy erase flags */
	ttysnew.sg_flags  = ttys.sg_flags;	/* copy flags */
 	ttysnew.sg_kill   = ttys.sg_kill;	/* copy std terminal flags */

	ttysnew.sg_flags |= RAW;    /* set for RAW Mode */
			/* This ORs in the RAW mode value, thereby
			   setting RAW mode and leaving the other
			   mode settings unchanged */

	ttysnew.sg_flags &= ~ECHO;  /* set for no echoing */
			/* This ANDs in the complement of the ECHO
			   setting (for NO echo), thereby leaving all
			   current parameters unchanged and turning
			   OFF ECHO only */

	ttysnew.sg_flags &= ~XTABS;  /* set for no tab expansion */
	ttysnew.sg_flags &= ~LCASE;  /* set for no upper-to-lower case xlate */
	ttysnew.sg_flags |= ANYP;  /* set for ANY Parity */
	ttysnew.sg_flags &= ~NL3;  /* turn off ALL 3s - new line */
	ttysnew.sg_flags &= ~TAB2; /* turn off tab 3s */
	ttysnew.sg_flags &= ~CR3;  /* turn off CR 3s */
	ttysnew.sg_flags &= ~FF1;  /* turn off FF 3s */
	ttysnew.sg_flags &= ~BS1;  /* turn off BS 3s */
	ttysnew.sg_flags &= ~TANDEM;  /* turn off flow control */

	/* set new paramters */
	if (ioctl(0,TIOCSETP,&ttysnew) < 0)
		error("Can't set new TTY Parameters", TRUE);

	if (stat(tty, &statbuf) < 0)  /* get tty status */ 
		error("Can't get your TTY Status", TRUE);

	if (statbuf.st_mode & 022)	/* Need to turn messages off */
		if (chmod(tty, statbuf.st_mode & ~022) < 0)
			error("Can't change  TTY mode", TRUE);
		else 
			wason = TRUE;
	else 
		wason = FALSE;
	xmdebug("tty modes set");
	}

/* restore normal tty modes */
restoremodes(errcall)
int errcall;
	{
	xmdebug("restoremodes:");
	if (wason)
		if (chmod(tty, statbuf.st_mode | 022) < 0)
			error("Can't change TTY mode", FALSE);
	if (ioctl(0,TIOCSETP,&ttys) < 0)
		{ if (!errcall)
		   error("RESET - Can't restore normal TTY Params", FALSE);
		else
		   { printf("XMODEM:  ");
		     printf("RESET - Can't restore normal TTY Params\n");
		   }
		}
	xmdebug("tty modes reset");
	return;
	}

/* print error message and exit; if mode == TRUE, restore normal tty modes */
error(msg, mode)
char *msg;
int mode;
	{
	xmdebug("error:");
	if (mode)
		restoremodes(TRUE);  /* put back normal tty modes */
	printf("\r\nXMODEM:  %s\n", msg);
	if ((LOGFLAG || DEBUG) & (int)LOGFP)
		{   
		fprintf(LOGFP, "XMODEM Fatal Error:  %s\n", msg);
	    	fclose(LOGFP);
		}
	exit(-1);
	}

/**  print status (size) of a file  **/
yfile(name)
char *name;
	{
	xmdebug("yfile:");
	printf("\nXMODEM File Status Display for %s\n", name);

	if (open(name,0) < 0) 
		{
		printf("File %s does not exist\n", name);
		return;
		}
	prfilestat(name);  /* print status */
	printf("\n");
	}

/*
 *
 *	Get a byte from the specified file.  Buffer the read so we don't
 *	have to use a system call for each character.
 *
 */
getbyte(fildes, ch)				/* Buffered disk read */
int fildes;
char *ch;

	{
	static char buf[BUFSIZ];	/* Remember buffer */
	static char *bufp = buf;	/* Remember where we are in buffer */
	
	xmdebug("getbyte:");
	if (nbchr == 0)			/* Buffer exausted; read some more */
		{
		if ((nbchr = read(fildes, buf, BUFSIZ)) < 0)
			error("File Read Error", TRUE);
		bufp = buf;		/* Set pointer to start of array */
		}
	if (--nbchr >= 0)
		{
		*ch = *bufp++;
		return(0);
		}
	else
		return(EOF);
	}

/**  receive a file  **/
rfile(name)
char *name;
	{
	register int bufctr, checksum;
	register int c;
	char mode;
	int fd, j, firstchar, sectnum, sectcurr, tmode;
	int sectcomp, errors, errorflag, recfin;
	int errorchar, fatalerror, startstx, inchecksum, endetx, endenq;
	long recvsectcnt;

	xmdebug("rfile:");
	mode = XMITTYPE;  /* set t/b mode */

	if ((fd = creat(name, CREATMODE)) < 0)
	  	error("Can't create file for receive", FALSE);

	printf("XMODEM:  Ready to RECEIVE File %s\n", name);
	puts("Control-X to cancel.\n");

	if (LOGFLAG)
		{    
		fprintf(LOGFP, "\n----\nXMODEM Receive Function\n");
	     	fprintf(LOGFP, "File Name: %s\n", name);
		}

	setmodes();  /* setup tty modes for xfer */

	recfin = FALSE;
	sectnum = errors = 0;
	fatalerror = FALSE;  /* NO fatal errors */
	recvsectcnt = 0;  /* number of received sectors */

	if (mode == 't')
		tmode = TRUE;
	else
		tmode = FALSE;

	if (CRCMODE)
		{
		xmdebug("crc mode request sent");
		sendbyte('C');	/* CRC request for first block */
		}
	else
		{
		xmdebug("NAK sent");
		sendbyte(NAK);  /* Start up the sender's first block */
		}

        do
        	{   
		errorflag = FALSE;
            	do 
			{
                  	firstchar = readbyte(6);
            		} 
			while ((firstchar != SOH) 
				&& (firstchar != EOT) 
				&& (firstchar != TIMEOUT) 
				&& ((firstchar & 0x7f) != CAN));

            	if (firstchar == TIMEOUT)
	    		{  
			xmdebug("first char was timeout");
			if (LOGFLAG)
				fprintf(LOGFP, "Timeout on Sector %d\n", sectnum);
               		errorflag = TRUE;
	    		}

            	if ((firstchar & 0x7f) == CAN)
	    		{  
			xmdebug("CAN received");
			if (LOGFLAG)
				fprintf(LOGFP, "Reception canceled at user's request.\n");
			error("Reception canceled at user's request",TRUE);
	    		}

            	if (firstchar == SOH)
	   		{
			xmdebug("SOH received");
               		sectcurr = readbyte(3);
               		sectcomp = readbyte(3);
               		if ((sectcurr + sectcomp) == 0xff)
               			{  
				if (sectcurr == ((sectnum+1) & 0xff))
		 			{  
					checksum = 0;
		     			for (j = bufctr = 0; j < BBUFSIZ; j++)
	      	     				{  
						buff[bufctr] = c = readbyte(3);
		        			checksum = ((checksum+c) & 0xff);
						if (!tmode)  /* binary mode */
							{  
							bufctr++;
		           				continue;
		        				}
						if (FILTER)	/* bit 8 */
							buff[bufctr] &= 0x7f;
						if (c == CR)
			   				continue;  /* skip CR's */
						if (c == CTRLZ)  /* CP/M EOF char */
							{  
							recfin = TRUE;  /* flag EOF */
		           				continue;
		        				}
		        			if (!recfin)
			   				bufctr++;
		     				}
		     			inchecksum = readbyte(3);  /* get checksum */
		    			if (checksum == inchecksum)  /* good checksum */
		     				{  
						xmdebug("checksum ok");
						errors = 0;
						recvsectcnt++;
		        			sectnum = sectcurr; 
						if (write(fd, buff, bufctr) < 0)
			   				error("File Write Error", TRUE);
		        			else
			   				sendbyte(ACK);
		     				}
		     			else
		     				{  
						xmdebug("checksum bad");
						if (LOGFLAG)
							fprintf(LOGFP, "Checksum Error on Sector %d\n",
								sectnum);
		        			errorflag = TRUE;
		     				}
                  			}
                  		else
                  			{ 
					if (sectcurr == sectnum)
                    				{  
						xmdebug("dup sector flushed");
						while(readbyte(3) != TIMEOUT)
							;
            	       				sendbyte(ACK);
                    				}
                    			else
		    				{  
						xmdebug("sector out of seq");
						if (LOGFLAG)
							{ 
							fprintf(LOGFP, "Phase Error - Received Sector is ");
			  				fprintf(LOGFP, "%d while Expected Sector is %d\n",
			   					sectcurr, ((sectnum+1) & 0xff));
							}
						errorflag = TRUE;
						fatalerror = TRUE;
						sendbyte(CAN);
		    				}
	          			}
           			}
			else
	   			{  
				if (DEBUG)
					fprintf(LOGFP,"DEBUG: bad sector# sectcurr=%02xH, sectcomp=%02xH\n",sectcurr,sectcomp);
				if (LOGFLAG)
					fprintf(LOGFP, "Header Sector Number Error on Sector %d\n",
		   				sectnum);
               			errorflag = TRUE;
	   			}
        		}
	
        	if (errorflag)
        		{  
			xmdebug("flushing bad sector");
			errors++;
	   		while (readbyte(3) != TIMEOUT)
				;
			sendbyte(NAK);
        		}
  		}
  		while ((firstchar != EOT) && (errors < ERRORMAX) && !fatalerror);

  	if ((firstchar == EOT) && (errors < ERRORMAX))
  		{
		xmdebug("EOT received");
     		close(fd);
		sendbyte(ACK);
     		restoremodes(FALSE);  /* restore normal tty modes */
     		sleep(5);  /* give other side time to return to terminal mode */
     		if (LOGFLAG)
     			{  
			fprintf(LOGFP, "\nReceive Complete\n");
			fprintf(LOGFP,"Number of Received CP/M Records is %ld\n", recvsectcnt);
     			}
     		printf("\n");
  		}
  	else
  		{ 
		sendbyte(CAN);
		xmdebug("error limit exceeded");
     		error("\r\nABORTED -- Too Many Errors", TRUE);
  		}
	}

/**  send a file  **/
sfile(name)
char *name;
	{
	register int bufctr, checksum, sectnum;
	char blockbuf[134];
	char mode;
	int fd, attempts;
	int nlflag, sendfin, tmode;
	int bbufcnt;
	int firstchar;
	char c;
	int sendresp;  /* response char to sent block */

	xmdebug("sfile:");
	nbchr = 0;  /* clear buffered read char count */
	mode = XMITTYPE;  /* set t/b mode */
		
	if ((fd = open(name, 0)) < 0)
		{  
		if (LOGFLAG) fprintf(LOGFP, "Can't Open File\n");
     	   	error("Can't open file for send", FALSE);
		}


	printf("XMODEM:  File %s Ready to SEND\n", name);
	prfilestat(name);  /* print file size statistics */
	puts("\nControl-X to cancel.\n");

	if (LOGFLAG)
		{   
		fprintf(LOGFP, "\n----\nXMODEM Send Function\n");
	    	fprintf(LOGFP, "File Name: %s\n", name);
		}

	if (mode == 't')
	   tmode = TRUE;
	else
	   tmode = FALSE;

        sendfin = nlflag = FALSE;
  	attempts = 0;

	setmodes();  /* setup tty modes for xfer */	

	while (((firstchar=readbyte(30)) != NAK)
/* no crc mode yet
	&& (firstchar != 'C') 
*/
	&& (firstchar != CAN))
		{
		if (++attempts > RETRYMAX)
			error("Remote System Not Responding", TRUE);
		}

	if ((firstchar & 0x7f) == CAN)
		{
		xmdebug("can received");
		error("\nSend cancelled at user's request.\n",TRUE);
		exit(-1);
		}

	sectnum = 1;  /* first sector number */
	attempts = 0;

        do 
		{   
		for (bufctr=0; bufctr < BBUFSIZ;)
	    		{
			if (nlflag)
	        		{  
				buff[bufctr++] = LF;  /* leftover newline */
	           		nlflag = FALSE;
	        		}
			if (getbyte(fd, &c) == EOF)
				{ 
				sendfin = TRUE;  /* this is the last sector */
		   		if (!bufctr)  /* if EOF on sector boundary */
		      			break;  /* avoid sending extra sector */
		   		if (tmode)
		      			buff[bufctr++] = CTRLZ;  /* Control-Z for CP/M EOF */
	           		else
		      			bufctr++;
		   		continue;
		      		}

			if (tmode && c == LF)  /* text mode & Unix newline? */
	    			{
				buff[bufctr++] = CR;  /* insert carriage return */
		     		if (bufctr < BBUFSIZ)
	                		buff[bufctr++] = LF;  /* insert LF */
	 	      		else
		        		nlflag = TRUE;  /* insert on next sector */
	   			}	
			else
				buff[bufctr++] = c;  /* copy the char without change */
	    		}

            	attempts = 0;
	
	    	if (!bufctr)  /* if EOF on sector boundary */
   	       		break;  /* avoid sending empty sector */

            	do
            		{
			bbufcnt = 0;		/* start building block to be sent */
			blockbuf[bbufcnt++] = SOH;	    /* start of packet char */
			blockbuf[bbufcnt++] = sectnum;	    /* current sector # */
			blockbuf[bbufcnt++] = -sectnum-1;   /* and its complement */

                	checksum = 0;  /* init checksum */
                	for (bufctr=0; bufctr < BBUFSIZ; bufctr++)
               			{
				blockbuf[bbufcnt++] = buff[bufctr];
                 		checksum = ((checksum+buff[bufctr]) & 0xff);
	         		}

			blockbuf[bbufcnt++] = checksum;
			write(1, blockbuf, 132);  /* write the block */
			ioctl(1,TIOCFLUSH,0);

                	attempts++;
			sendresp = readbyte(10);  /* get response */
			if ((sendresp != ACK) && LOGFLAG)
		   		{
		   		fprintf(LOGFP, "Non-ACK Received on Sector %d\n",sectnum);
		   		if (sendresp == TIMEOUT)
					fprintf(LOGFP, "This Non-ACK was a TIMEOUT\n");
		   		}
            		}
			while((sendresp != ACK) && (attempts < RETRYMAX));

       		sectnum++;  /* increment to next sector number */
    		}
		while (!sendfin && (attempts < RETRYMAX));

    	if (attempts >= RETRYMAX)
		error("Remote System Not Responding", TRUE);

    	attempts = 0;
    	sendbyte(EOT);  /* send 1st EOT */
	
    	while ((readbyte(15) != ACK) && (attempts++ < RETRYMAX))
	   	sendbyte(EOT);

    	if (attempts >= RETRYMAX)
	   	error("Remote System Not Responding on Completion", TRUE);

    	close(fd);
    	restoremodes(FALSE);  
    	sleep(15);  /* give other side time to return to terminal mode */

    	if (LOGFLAG)
    		fprintf(LOGFP, "\nSend Complete\n");
    	printf("\n");

	}

/*  print file size status information  */
prfilestat(name)
char *name;
	{
	struct stat filestatbuf; /* file status info */

	xmdebug("prfilestat:");
	stat(name, &filestatbuf);  /* get file status bytes */
	printf("  Estimated File Size %ldK, %ld Records, %ld Bytes",
	  	(filestatbuf.st_size/1024)+1, (filestatbuf.st_size/128)+1,
	  	filestatbuf.st_size);
	if (LOGFLAG)
	  	fprintf(LOGFP,"Estimated File Size %ldK, %ld Records, %ld Bytes\n",
	  	(filestatbuf.st_size/1024)+1, (filestatbuf.st_size/128)+1,
	  	filestatbuf.st_size);
		return;
	}

/* get a byte from data stream -- timeout if "seconds" elapses */
int readbyte(seconds)
int seconds;
	{
	int i, readfd;
	char c;
	struct timeval tmout;

	tmout.tv_sec = seconds;
	tmout.tv_usec = 0;

	readfd = 1;

	if ((i=select(1, &readfd, 0, 0, &tmout)) == 0)
		{
		xmdebug("readbyte timeout");
		return(TIMEOUT);
		}
	if (DEBUG)
		fprintf(LOGFP,"DEBUG: readbyte select returned %d\n",i);

	read(0, &c, 1);

	if (DEBUG)
		fprintf(LOGFP,"DEBUG: readbyte %02xh\n",c);

	return(c & 0xff);  /* return the char */
	}

/* send a byte to data stream */
sendbyte(data)
char data;
	{
	if (DEBUG)
		fprintf(LOGFP,"DEBUG: sendbyte %02xh\n",data);
	write(1, &data, 1);  	/* write the byte */
	ioctl(1,TIOCFLUSH,0);	/* flush so it really happens now! */
	return;
	}

/* type out debugging info */
xmdebug(str)
char *str;
	{
	if (DEBUG)
		fprintf(LOGFP,"DEBUG: '%s'\n",str);
	}

Robert Perlberg
Resource Dynamics Inc.
New York
philabs!rdin!rdin2!perl