[net.sources] reposting of xmodem.c - umodem for 4.2BSD

brian@sdccsu3.UUCP (03/04/84)

This is being reposted because a number of sites have notified me that
it was arriving truncated.  My apologies if you've already seen in.
--------------------- cut here --------------------
/*
 *  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);
	}
-- 
	-Brian Kantor, UC San Diego 
	Kantor@Nosc
	ihnp4 \
	decvax \
	dcdwest  -----  sdcsvax  ----- brian
	ittvax /
	ucbvax/