ftp.nice.ch/pub/next/unix/communication/zm_tip_modified.NI.bs.tar.gz#/zm_tip/xfer/tiprz.c

This is tiprz.c in view mode; [Download] [Up]

char *numeric_revision = "tiprz 1.00";
/*+-------------------------------------------------------------------------
	tiprz.c - X/Y/ZMODEM receive program
  Derived from public domain source by Chuck Forsberg, Omen Technologies
  wht%n4hgf@emory.mathcs.emory.edu

  Defined functions:
	SIGALRM_handler()
	arg_token(parsestr,termchars)
	bye_bye(sig)
	cancel_transaction(sig)
	close_and_report()
	flushline()
	fname_split(cmd,arg,arg_max_quan,narg_rtn)
	fname_too_long(fname)
	fname_truncated()
	getfree()
	isanylc(str)
	main(argc,argv,envp)
	make_dirs(pathname)
	mkdir(dpath,dmode)
	procheader(name)
	purgeline()
	readline(timeout)
	rzfile()
	rzfiles()
	send_ZFIN_and_exit()
	send_cancel()
	sendline(c)
	substr(str,token)
	sys2(shellcmd)
	tryz()
	uncaps(str)
	usage(fail_reason)
	wcgetsec(rxbuf,maxtime)
	wcreceive(argc,argp)
	wcrx()
	wcrxpn(rpn)
	write_sec_to_disk(buf,n)
	xsendline(c)

      Usage:    tiprz -Z [-abeuy]    (ZMODEM)
                tiprz -Y [-abuy]     (YMODEM)
                tiprz -X [-abc] file (XMODEM or XMODEM-1k)

          -a ASCII transfer (strip CR)
          -b Binary transfer for all files
          -c Use 16 bit CRC (XMODEM)
          -e Escape control characters  (ZMODEM)
          -p protect local files (ZMODEM)
          -t <tenths> rx timeout seconds
          -+ force append
          -u convert uppercase filenames to lower case
          -y Yes, clobber existing file if any
          -. line fd to use
          -, log protocol packets

--------------------------------------------------------------------------*/
/*+:EDITS:*/
/*:05-21-1990-16:00-wht@tridom-adapt ecu xfer protocols for tipwht */

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#ifdef NeXT
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <time.h>
#include <sys/types.h>
#endif /* NeXT */
#include "zmodem.h"
#include "zlint.h"

#if defined(__STDC__) || defined(__cplusplus)
# define P_A(s) s
#else
# define P_A(s) ()
#endif


int write(int fd, char *buf, int nbytes);
int read(int fd, char *buf, int nbytes);
off_t lseek(int fd, off_t offset, int whence);
unsigned int alarm();
int open(const char *path, int flags, ...);
int getpid(void);
int utime();
int chmod();
int unlink(const char *path);


char *arg_token P_A((char *parsestr, char *termchars));
void fname_split P_A((char *cmd, char **arg, int arg_max_quan, int *narg_rtn));
int fname_too_long P_A((char *fname));
char *fname_truncated P_A((void));
char *substr P_A((char *str, char *token));
long getfree P_A((void));
void usage P_A((char *fail_reason));
void SIGALRM_handler ();
void bye_bye P_A((int sig));
void cancel_transaction P_A((int sig));
void sendline P_A((char c));
void xsendline P_A((char c));
void flushline P_A((void));
void purgeline P_A((void));
int wcreceive P_A((int argc, char **argp));
unsigned int wcgetsec P_A((char *rxbuf, int maxtime));
int wcrxpn P_A((char *rpn));
int write_sec_to_disk P_A((char *buf, int n));
int wcrx P_A((void));
int readline P_A((int timeout));
int mkdir P_A((char *dpath, int dmode));
int make_dirs P_A((char *pathname));
void uncaps P_A((char *str));
int isanylc P_A((char *str));
int procheader P_A((char *name));
void send_cancel P_A((void));
int tryz P_A((void));
int rzfile P_A((void));
int rzfiles P_A((void));
int close_and_report P_A((void));
void send_ZFIN_and_exit P_A((void));
int sys2 P_A((char *shellcmd));
int main P_A((int argc, char **argv, char **envp));
#undef P_A

char *strrchr();

#if defined(M_UNIX)
char *fname_truncated();
#endif

extern unsigned short crctab[];
extern unsigned long total_data_chars_xfered; /* zcurses.c */
extern int errno;
extern char *sys_errlist[];
extern char Attn[];		/* Attention string rx sends to tx on err */
extern int Crc32r;		/* Display flag indicating 32 bit CRC being received */
extern int Rxcount;		/* Count of data bytes received */
extern char Rxhdr[];	/* Received header */
extern char Txhdr[];	/* Transmitted header */
extern int Rxtimeout;	/* Tenths of seconds to wait for something */
extern char s256[];

/* Max value for VMIN_COUNT is 255.  A larger value reduces system
overhead but may evoke kernel bugs.  133 corresponds to an XMODEM/CRC
sector */
#if !defined(VMIN_COUNT)
#define VMIN_COUNT 133
#endif
unsigned char vmin_count = VMIN_COUNT;
int Readnum = VMIN_COUNT;	/* num bytes to ask for in read() from modem */

#define DEFBYTL 2000000000L	/* default rx file size */
#define RETRYMAX 5

FILE *fout;
char Lzmanag;		/* Local file management request */
char Lzconv;		/* Local ZMODEM file conversion request */
extern int Usevhdrs;
char Pathname[PATHLEN];
char curr_dir[256];
unsigned char linbuf[VMIN_COUNT];
char s128[128];
char secbuf[1025];
char zconv;				/* ZMODEM file conversion request */
char zmanag;			/* ZMODEM file management request */
char ztrans;			/* ZMODEM file transport request */
int Batch=0;
int Blklen;				/* record length of received packets */
int Crcflg;
int Eofseen;			/* indicates cpm eof (^Z) has been received */
int Filcnt=0;			/* count of number of files opened */
int Filemode;			/* Unix style mode for incoming file */
int Firstsec;
int Lastrx;
int Lleft=0;			/* number of characters in linbuf */
int MakeLCPathname=1;	/* make received pathname lower case */
int Nozmodem = 0;		/* If invoked as "rb" */
int Rxascii=0;			/* receive files in ascii (translate) mode */
int Rxbinary=0;			/* receive all files in bin mode */
int Rxclob=0;			/* Clobber existing file */
int Thisbinary;			/* current file is to be received in bin mode */
int Twostop = 0;		/* use two stop bits */
int Zctlesc;			/* Encode control characters */
int Zmodem=0;			/* ZMODEM protocol requested */
int Zrwindow = 1400;	/* RX window size (controls garbage count) */
int tipsz_flag = 0;
int skip_count = 0;		/* skipped files */
int errors;
int iofd = 0;
int log_packets = 0;
int npats = 0;
int oldBlklen = -1;		/* last block length */
int this_file_errors = 0;
int tryzhdrtype=ZRINIT;	/* Header type to send corresponding to Last rx close */
jmp_buf tohere;			/* For the interrupt on RX timeout */
long Bytesleft;			/* number of bytes of incoming file left */
long Modtime;			/* Unix style mod time for incoming file */
long TotalLeft;
long Filesleft;
long TotalToReceive = 0L;
long rx_char_count = 0L;
long tx_char_count = 0L;
struct stat fout_stat;
time_t timep[2];
unsigned Baudrate;
unsigned long this_file_length;
int required_type = 0;
char *bottom_label = (char *)0;

/*+-----------------------------------------------------------------------
	arg_token(parsestr,termchars)

Get next token from string parsestr ((char *)0 on 2nd, 3rd, etc.
calls), where tokens are nonempty strings separated by runs of chars
from termchars.  Writes nulls into parsestr to end tokens.
termchars need not remain constant from call to call.

Treats multiple occurrences of a termchar as one delimiter (does not
allow null fields).
------------------------------------------------------------------------*/
#if defined(M_UNIX)
static char *arg_token_static = (char *)0;
char *arg_token(parsestr,termchars)
char *parsestr;
char *termchars;
{
register int first = 1;
register char *termptr;
register char *parseptr;
char *token;

	if(parsestr == (char *)0 && arg_token_static == (char *)0)
		return((char *)0);

	if(parsestr)
		parseptr = parsestr;
	else
       parseptr = arg_token_static;

	while(*parseptr)
	{
		if(!strchr(termchars,*parseptr))
			break;
		parseptr++;
	}

	if(!*parseptr)
	{
		arg_token_static = (char *)0;
		return((char *)0);
	}

	token = parseptr;
	if(*token == '\'')
	{
		token++;
		parseptr++;
		while(*parseptr)
		{
			if(*parseptr == '\'')
			{
				arg_token_static = parseptr + 1;
				*parseptr = 0;
				return(token);
			}
			parseptr++;
		}
		arg_token_static = (char *)0;
		return(token);
	}
	while(*parseptr)
	{
		if(strchr(termchars,*parseptr))
		{
			*parseptr = 0;
			arg_token_static = parseptr + 1;
			while(*arg_token_static)
			{
				if(!strchr(termchars,*arg_token_static))
					break;
				arg_token_static++;
			}
			return(token);
		}
		parseptr++;
	}
	arg_token_static = (char *)0;
	return(token);
}	/* end of arg_token */
#endif

/*+-------------------------------------------------------------------------
	fname_split(cmd,arg,arg_max_quan,&narg)
--------------------------------------------------------------------------*/
#if defined(M_UNIX)
void
fname_split(cmd,arg,arg_max_quan,narg_rtn)
char *cmd;
char **arg;
int arg_max_quan;
int *narg_rtn;
{
register itmp;
register narg;

	for(itmp = 0; itmp < arg_max_quan; itmp++)
		arg[itmp] = (char *)0;
	arg[0] = arg_token(cmd,"/");

	for(narg = 1; narg < arg_max_quan; ++narg)
	{
		if((arg[narg] = arg_token((char *)0,"/")) == (char *)0) 
			break;
	}

	*narg_rtn = narg;

}	/* end of fname_split */
#endif

#if defined(M_UNIX)
#define MAX_COMPONENT_LEN	14
#define MAX_PATH_COMPONENTS	16
static char trunc_fname[257];
static char *trunc_components[MAX_PATH_COMPONENTS];
static int trunc_components_quan;
static int trunc_absolute_path;
#endif

/*+-------------------------------------------------------------------------
	fname_too_long(fname) - check for any pathname component too long
--------------------------------------------------------------------------*/
#if defined(M_UNIX)
int
fname_too_long(fname)
register char *fname;
{
register int itmp;
register char **cpptr;

	if(trunc_absolute_path = (*fname == '/'))
		fname++;
	strncpy(trunc_fname,fname,sizeof(trunc_fname) - 1);
	fname_split(trunc_fname,trunc_components,
		MAX_PATH_COMPONENTS,&trunc_components_quan);
	itmp = trunc_components_quan;
	cpptr = trunc_components;
	while(itmp--)
	{
		if(strlen(*cpptr) > MAX_COMPONENT_LEN)
			return(1);
		cpptr++;
	}
	return(0);
}	/* end of fname_too_long */
#endif

/*+-------------------------------------------------------------------------
	fname_truncated() - build truncated path last checked by fname_too_long
--------------------------------------------------------------------------*/
#if defined(M_UNIX)
char *
fname_truncated()
{
register int icomp;
char new_fname[257];
register char *cptr = new_fname;

	if(trunc_absolute_path)
	{
		*cptr = '/';
		*(cptr + 1) = 0;
	}
	else
		*cptr = 0;
	for(icomp = 0; icomp < trunc_components_quan; icomp++)
	{
		if(strlen(trunc_components[icomp]) > MAX_COMPONENT_LEN)
			*(trunc_components[icomp] + MAX_COMPONENT_LEN) = 0;
		strcat(cptr,trunc_components[icomp]);
		if(icomp < trunc_components_quan - 1)
			strcat(cptr,"/");
	}
	strcpy(trunc_fname,cptr);
	return(trunc_fname);

}	/* end of fname_truncated */
#endif

/*+-------------------------------------------------------------------------
	substr(str,token)

  searches for token in string str returns pointer to token within
  string if found,NULL otherwise
--------------------------------------------------------------------------*/
char *
substr(str,token)
register char *str,*token;
{
register char *ss,*tt;

	/* search for first char of token */
	for(ss=str; *str; str++)
		if(*str == *token)
			/* compare token with substring */
			for(ss=str,tt=token; ;)
			{
				if(*tt == 0)
					return(str);
				if(*ss++ != *tt++)
					break;
			}
	return(NULL);
}	/* end of substr */

/*+-------------------------------------------------------------------------
	getfree()

  Routine to calculate the free bytes on the current file system ~0
  means many free bytes (unknown)
--------------------------------------------------------------------------*/
long
getfree()
{
	return(~0L);	/* many free bytes ... */
}	/* end of getfree */

/*+-------------------------------------------------------------------------
	usage(fail_reason)
--------------------------------------------------------------------------*/
void
usage(fail_reason)
char *fail_reason;
{
	fprintf(stderr,"%s\n",fail_reason);
	exit(255);
}	/* end of usage */

/*+-------------------------------------------------------------------------
	SIGALRM_handler()
--------------------------------------------------------------------------*/
void
SIGALRM_handler()
{
	report_tx_ind(0);
	report_rx_ind(0);
	longjmp(tohere,-1);
}	/* end of SIGALRM_handler */

/*+-------------------------------------------------------------------------
	bye_bye(sig)
--------------------------------------------------------------------------*/
void
bye_bye(sig)
int sig;
{
	exit(sig+128);
}	/* end of bye_bye */

/*+-------------------------------------------------------------------------
	cancel_transaction(sig)
called by signal interrupt or terminate to clean things up
--------------------------------------------------------------------------*/
void
cancel_transaction(int sig)
{
	if(Zmodem)
		zmputs(Attn);
	send_cancel();
	mode(0);
	if(sig >= 0)
	{
		sprintf(s128,"tiprz aborted (signal %d)",sig);
		report_str(s128,0);
	}
	report_tx_ind(0);
	report_rx_ind(0);
	report_uninit(0);
	bye_bye(sig);
}	/* end of cancel_transaction */

/*+-------------------------------------------------------------------------
	sendline(c) -  send a character to DCE
--------------------------------------------------------------------------*/
void sendline(c)
char c;
{
	write(iofd,&c,1);
	++tx_char_count;
}	/* end of sendline */

/*+-------------------------------------------------------------------------
	xsendline(c)
--------------------------------------------------------------------------*/
void xsendline(char c)
{
	sendline(c);
}	/* end of xsendline */

/*+-------------------------------------------------------------------------
	flushline()
--------------------------------------------------------------------------*/
void flushline(void)
{
}	/* end of flushline */

/*+-------------------------------------------------------------------------
	purgeline() - purge the modem input queue of all characters
--------------------------------------------------------------------------*/
void purgeline(void)
{
	Lleft = 0;
#if defined(M_XENIX) || defined(M_UNIX)
	ioctl(iofd,TCFLSH,0);
#else
	lseek(iofd,0L,2);
#endif
}	/* end of purgeline */

/*+-------------------------------------------------------------------------
	wcreceive(argc,argp)
--------------------------------------------------------------------------*/
int wcreceive(argc,argp)
int argc;
char **argp;
{
register c;

	if(Batch || argc==0)
	{
		Crcflg=1;
		c=tryz();
		if(Zmodem)
		{
			report_protocol_type("ZMODEM");
			report_protocol_crc_type((Crc32r) ? "/CRC32" : "/CRC16");
		}
		if(c)
		{
			if(c == ZCOMPL)
				return(OK);
			if(c == ERROR)
				goto fubar;
			c = rzfiles();
			if(c)
				goto fubar;
		} else 
		{
			report_protocol_type("YMODEM");
			report_protocol_crc_type((Crcflg) ? "/CRC" : "/CHK");
			for(;;)
			{
				if(wcrxpn(secbuf)== ERROR)
					goto fubar;
				if(secbuf[0]==0)
					return(OK);
				if(procheader(secbuf) == ERROR)
					goto fubar;
				report_str("Receiving data",0);
				if(wcrx()==ERROR)
					goto fubar;
			}
		}
	}
	else 
	{
		report_protocol_type("XMODEM");
		report_protocol_crc_type((Crcflg) ? "/CRC" : "/CHK");
		Bytesleft = DEFBYTL;
		Filemode = 0;
		Modtime = 0L;
		procheader("");
		strcpy(Pathname,*argp);
		if((fout=fopen(Pathname,"w")) == NULL)
		{
			sprintf(s128,"%-0.85s: %-0.40s",Pathname,sys_errlist[errno]);
			report_str(s128,1);
			goto fubar;
		}

		report_file_rcv_started( Pathname,0L,Modtime,Filemode);
		this_file_length = 0;
		report_rxpos(0L);
		report_str("Receiving data",0);
		if(wcrx()==ERROR)
			goto fubar;
	}
	return(OK);
fubar:
	send_cancel();
	if(fout)
	{
		fflush(fout);
		fstat(fileno(fout),&fout_stat);
		report_file_byte_io((long)fout_stat.st_size);
		report_file_close();
		fclose(fout);
	}
	return(ERROR);
}	/* end of wcreceive */

/*+-------------------------------------------------------------------------
	wcgetsec(rxbuf,maxtime)

  Wcgetsec fetches a Ward Christensen type sector.  Returns sector
  number encountered or ERROR if valid sector not received, or CAN CAN
  received or WCEOT if eot sector time is timeout for first char,set to
  4 seconds thereafter. NO ACK IS SENT IF SECTOR IS RECEIVED OK. Caller
  must do that when he is good and ready to get next sector.
--------------------------------------------------------------------------*/
unsigned int
wcgetsec(rxbuf,maxtime)
char *rxbuf;
int maxtime;
{
register unsigned int firstch;
register unsigned short oldcrc;
register unsigned char checksum;
register wcj;
register char *p;
int sectcurr;

	for(Lastrx=errors=0; errors<RETRYMAX; errors++)
	{

		firstch=readline(maxtime);
		if((firstch == STX) || (firstch == SOH))
		{
			oldBlklen = Blklen;
			if(firstch == STX)
				Blklen=1024;
			else
				Blklen=128;
			if(oldBlklen != Blklen)
				report_rxblklen(Blklen);

			sectcurr=readline(1);
			if((sectcurr + (oldcrc=readline(1))) == 0xFF)
			{
				oldcrc=checksum=0;
				for(p=rxbuf,wcj=Blklen; --wcj>=0; )
				{
					if((firstch=readline(1)) < 0)
						goto bilge;
					oldcrc=updcrc(firstch,oldcrc);
					checksum += (*p++ = firstch);
				}
				if((firstch=readline(1)) < 0)
					goto bilge;
				if(Crcflg)
				{
					oldcrc=updcrc(firstch,oldcrc);
					if((firstch=readline(1)) < 0)
						goto bilge;
					oldcrc=updcrc(firstch,oldcrc);
					if(oldcrc)
					{
						sprintf(s128,"CRC error = 0x%04x",oldcrc);
						report_str(s128,1);
					}
					else 
					{
						Firstsec=0;
						return(sectcurr);
					}
				}
				else if((checksum-firstch)==0)
				{
					Firstsec=0;
					return(sectcurr);
				}
				else
					report_str("checksum error",1);
			}
			else
			{
				report_last_txhdr("Noise",0);
				sprintf(s128,"Sector garbled 0x%x 0x%x",sectcurr,oldcrc);
				report_str(s128,1);
			}
		}
		/* make sure eot really is eot and not just mixmash */
#if defined(NFGVMIN)
		else if(firstch==EOT && readline(1)==TIMEOUT)
			return(WCEOT);
#else
		else if(firstch==EOT && Lleft==0)
			return(WCEOT);
#endif
		else if(firstch==EOT)
		{
			report_str("Noisy EOT",2);
		}
		else if(firstch==CAN)
		{
			if(Lastrx==CAN)
			{
				report_str("Sender CANcelled",1);
				report_last_rxhdr("CAN",1);
				return(ERROR);
			} else 
			{
				Lastrx=CAN;
				continue;
			}
		}
		else if(firstch==TIMEOUT)
		{
			if(Firstsec)
				goto humbug;
bilge:
			report_str("Timeout",1);
		}
		else
		{
			sprintf(s128,"Got 0x%02x sector header",firstch);
			report_str(s128,1);
		}

humbug:
		Lastrx=0;
		while(readline(1)!=TIMEOUT)
			;
		if(Firstsec)
		{
			sendline(Crcflg?WANTCRC:NAK);
			report_last_txhdr(Crcflg ? "WANTCRC" : "NAK",0);
			Lleft=0;	/* Do read next time ... */
		} else 
		{
			maxtime=40;
			sendline(NAK);
			report_last_txhdr("NAK",1);
			Lleft=0;	/* Do read next time ... */
		}
	}
	/* try to stop the bubble machine. */
	send_cancel();
	return(ERROR);
}	/* end of wcgetsec */

/*+-------------------------------------------------------------------------
	wcrxpn(rpn)

  Fetch a pathname from the other end.  Length is indeterminate as long
  as less than Blklen.  During YMODEM xfers, a null string represents no
  more files.
--------------------------------------------------------------------------*/
int wcrxpn(rpn)
char *rpn;	/* receive a pathname */
{
register c;

#if defined(NFGVMIN)
	readline(1);
#else
	purgeline();
#endif

et_tu:
	Firstsec=1;
	Eofseen=0;
	sendline(Crcflg?WANTCRC:NAK);
	report_last_txhdr(Crcflg ? "WANTCRC" : "NAK",0);
	Lleft=0;	/* Do read next time ... */
	while((c = wcgetsec(rpn,100)) != 0)
	{
		if(c == WCEOT)
		{
			sprintf(s128,"Pathname fetch returned %d",c);
			report_str(s128,1);
			sendline(ACK);
			report_last_txhdr("ACK",0);
			Lleft=0;	/* Do read next time ... */
			readline(1);
			goto et_tu;
		}
		return(ERROR);
	}
	sendline(ACK);
	report_last_txhdr("ACK",0);
	return(OK);
}	/* end of wcrxpn */

/*+-------------------------------------------------------------------------
	write_sec_to_disk(buf,n)

  Putsec writes the n characters of buf to receive file fout.  If not in
  binary mode, carriage returns, and all characters starting with CPMEOF
  are discarded.
--------------------------------------------------------------------------*/
int write_sec_to_disk(buf,n)
char *buf;
register n;
{
register char *p;

	if(n == 0)
		return(OK);
	if(Thisbinary)
	{
		for(p=buf; --n>=0; )
			putc( *p++,fout);
	}
	else 
	{
		if(Eofseen)
			return(OK);
		for(p=buf; --n>=0; ++p )
		{
			if( *p == '\r')
				continue;
			if(*p == CPMEOF)
			{
				Eofseen=1;
				fflush(fout);
				fstat(fileno(fout),&fout_stat);
				report_rxpos(fout_stat.st_size);
				return(OK);
			}
			putc(*p ,fout);
		}
	}
	fflush(fout);
	fstat(fileno(fout),&fout_stat);
	report_rxpos(fout_stat.st_size);
	if(this_file_length != 0)
	{
		sprintf(s128,"Receiving data (%u%% complete)",
		    (unsigned int)(
		    ((unsigned long)fout_stat.st_size * (unsigned long)100)
		    / this_file_length));
		report_str(s128,0);
	}
	return(OK);
}	/* end of write_sec_to_disk */

/*+-------------------------------------------------------------------------
	wcrx() - receive an X/YMODEM sector

  Adapted from CMODEM13.C,written by Jack M.  Wierda and Roderick W. Hart
--------------------------------------------------------------------------*/
int
wcrx()
{
register unsigned int sectnum,sectcurr;
register unsigned char sendchar;
/* register unsigned char *p; */
int cblklen;			/* bytes to dump this block */

	Firstsec=1;
	sectnum=0;
	Eofseen=0;
	sendchar=Crcflg ? WANTCRC : NAK;
	report_last_txhdr(Crcflg ? "WANTCRC" : "NAK",0);

	for(;;)
	{
		sendline(sendchar);	/* send it now,we're ready! */
		if(sendchar == ACK)
			report_last_txhdr("ACK",0);
		Lleft=0;	/* Do read next time ... */
		sectcurr=wcgetsec(secbuf,(sectnum&0177)?50:130);
		sprintf(s128,"Block %d received",sectnum);
		report_last_rxhdr(s128,0);
		fstat(fileno(fout),&fout_stat);
		report_rxpos(fout_stat.st_size);
		if(sectcurr == (sectnum+1 & 0xFF))
		{
			sectnum++;
			cblklen = Bytesleft>Blklen ? Blklen : Bytesleft;
			if(write_sec_to_disk(secbuf,cblklen) == ERROR)
				return(ERROR);
			if((Bytesleft-=cblklen) < 0)
				Bytesleft = 0;
			sendchar=ACK;
		}
		else if(sectcurr == sectnum)
		{
			report_str("Received duplicate Sector",-1);
			sendchar = ACK;
		}
		else if(sectcurr == WCEOT)
		{
			if(close_and_report())
				return(ERROR);
			sendline(ACK);
			report_last_txhdr("ACK",0);
			Lleft=0;	/* Do read next time ... */
			return(OK);
		}
		else if(sectcurr==ERROR)
			return(ERROR);
		else 
		{
			report_str( "Sync Error",1);
			return(ERROR);
		}
	}
}	/* end of wcrx */

/*+-------------------------------------------------------------------------
	readline(timeout)

  read one or more characters timeout is in tenths of seconds
--------------------------------------------------------------------------*/
int readline(timeout)
int timeout;
{
int n;
static unsigned char *cdq;	/* pointer for removing chars from linbuf */

	if(--Lleft >= 0)
		return(*cdq++);

	n = timeout/10;
	if(n < 2)
		n = 3;
	if(setjmp(tohere))
	{
		Lleft = 0;
		return(TIMEOUT);
	}
	signal(SIGALRM,SIGALRM_handler);
	alarm(n);
	Lleft = read(iofd,cdq = linbuf,Readnum);
	alarm(0);
	rx_char_count += Lleft;

	if(Lleft < 1)
		return(TIMEOUT);

	--Lleft;
	return(*cdq++);

}	/* end of readline */

/*+-------------------------------------------------------------------------
	mkdir(dpath,dmode)
 Directory-creating routines from Public Domain TAR by John Gilmore
 Make a directory.  Compatible with the mkdir() system call on 4.2BSD.
--------------------------------------------------------------------------*/
#if defined(MD)
#if (MD != 2)
#define	TERM_SIGNAL(status)		((status) & 0x7F)
#define TERM_COREDUMP(status)	(((status) & 0x80) != 0)
#define TERM_VALUE(status)		((status) >> 8)
mkdir(dpath,dmode)
char *dpath;
int dmode;
{
int cpid,status;
struct stat statbuf;

	if(stat(dpath,&statbuf) == 0)
	{
		errno = EEXIST;		/* Stat worked,so it already exists */
		return(-1);
	}

	/* If stat fails for a reason other than non-existence,return error */
	if(errno != ENOENT)
		return(-1);

	switch(cpid = fork())
	{

	case -1:			/* Error in fork() */
		return(-1);		/* Errno is set already */

	case 0:				/* Child process */
		/*
		 * Cheap hack to set mode of new directory.  Since this
		 * child process is going away anyway,we zap its umask.
		 * FIXME,this won't suffice to set SUID,SGID,etc. on this
		 * directory.  Does anybody care?
		 */
		status = umask(0);	/* Get current umask */
		status = umask(status | (0777 & ~dmode)); /* Set for mkdir */
		execl("/bin/mkdir","mkdir",dpath,(char *)0);
		_exit(-1);		/* Can't exec /bin/mkdir */

	default:			/* Parent process */
		while(cpid != wait(&status)) ;	/* Wait for kid to finish */
	}

	if(TERM_SIGNAL(status) != 0 || TERM_VALUE(status) != 0)
	{
		errno = EIO;		/* We don't know why,but */
		return(-1);		/* /bin/mkdir failed */
	}

	return(0);
}	/* end of mkdir */
#endif /* MD != 2 */
#endif /* if defined(MD) */

/*+-------------------------------------------------------------------------
	make_dirs(pathname)

  Directory-creating routines from Public Domain TAR by John Gilmore
  After a file/link/symlink/dir creation has failed, see if it's because
  some required directory was not present, and if so, create all
  required dirs.
--------------------------------------------------------------------------*/
#if defined(MD)
make_dirs(pathname)
register char *pathname;
{
	register char *p;			/* Points into path */
	int madeone = 0;			/* Did we do anything yet? */
	int save_errno = errno;		/* Remember caller's errno */

	if(errno != ENOENT)
		return(0);		/* Not our problem */

	for(p = strchr(pathname,'/'); p != NULL; p = strchr(p+1,'/'))
	{
		/* Avoid mkdir of empty string,if leading or double '/' */
		if(p == pathname || p[-1] == '/')
			continue;
		/* Avoid mkdir where last part of path is '.' */
		if(p[-1] == '.' && (p == pathname+1 || p[-2] == '/'))
			continue;
		*p = 0;				/* Truncate the path there */
		if( !mkdir(pathname,0777))	/* Try to create it as a dir */
		{
			sprintf(s128,"Made directory %s",pathname);
			report_str(s128,-1);
			madeone++;		/* Remember if we made one */
			*p = '/';
			continue;
		}
		*p = '/';
		if(errno == EEXIST)		/* Directory already exists */
			continue;
		/*
		 * Some other error in the mkdir.  We return to the caller.
		 */
		break;
	}
	errno = save_errno;		/* Restore caller's errno */
	return(madeone);			/* Tell them to retry if we made one */
}	/* end of make_dirs */
#endif /* MD */

/*+-------------------------------------------------------------------------
	uncaps(str) - make string str lower case
--------------------------------------------------------------------------*/
void
uncaps(str)
register char *str;
{
register int itmp;

	while(itmp = *str)
	{
		if(isupper(itmp))
			*str = tolower(itmp);
		str++;
	}
}	/* end of uncaps */

/*+-------------------------------------------------------------------------
	isanylc(str) - returns 1 if string str has any lower case letters
--------------------------------------------------------------------------*/
int
isanylc(str)
register char *str;
{
	while(*str)
	{
		if(islower(*str))
			return(1);
		str++;
	}
	return(0);
}	/* end of isanylc */

/*+-------------------------------------------------------------------------
	procheader(name) - process incoming file information header
--------------------------------------------------------------------------*/
int
procheader(name)
char *name;
{
    register char *openmode,*p/* ,**pp */;
    static dummy;

	/* set default parameters and overrides */
	openmode = "w";
	Thisbinary = (!Rxascii) || Rxbinary;
	if (zconv == ZCBIN && Lzconv != ZCRESUM)
		Lzconv = zconv;			/* Remote Binary override */
	if (Lzconv)
		zconv = Lzconv;
	if(Lzmanag)
		zmanag = Lzmanag;

	/*
	 *  Process ZMODEM remote file management requests
	 */
	if(!Rxbinary && zconv == ZCNL)	/* Remote ASCII override */
		Thisbinary = 0;
	if(zconv == ZCBIN)	/* Remote Binary override */
		Thisbinary = 1;
	else if(zmanag == ZMAPND)
		openmode = "a";

	report_xfer_mode(Thisbinary ? "BINARY" : "ASCII");
	this_file_errors = 0;

	Bytesleft = DEFBYTL;
	Filemode = 0;
	Modtime = 0L;
	this_file_length = 0;

	if(strlen(name))
		p = name + 1 + strlen(name);
	else
		p = name;

	if(*p)
	{	/* file coming from Unix or DOS system */
	int sscanf_count;
		sscanf_count = sscanf(p, "%ld%lo%o%lo%d%ld%d%d",
		  &Bytesleft, &Modtime, &Filemode,
		  &dummy, &Filesleft, &TotalLeft, &dummy, &dummy);

		switch(sscanf_count)
		{
		case 6:	/* TotalLeft */
			if(!TotalToReceive)
				TotalToReceive = TotalLeft;
		case 5:	/* Filesleft */
			if(!npats)
				npats = Filesleft;
		default:
			break;
		}

		if((zmanag & ZMMASK) == ZMNEW)
		{
			if(stat(name,&fout_stat) == 0)			/* if file accessable ... */
			{
				if(Modtime <= fout_stat.st_mtime)	/* ... and not older */
				{
					sprintf(s128,"RECEIVE skipped: %s (same or later date)",
						name);
					report_str(s128 + 8,-1);
					skip_count++;
					report_error_count();
					return(ERROR);
				}
			}
		}
		/* Check for existing file */
		else if(!Rxclob && ((zmanag & ZMMASK) != ZMCLOB) &&
			(fout=fopen(name,"r")))
		{
			fclose(fout);
			sprintf(s128,"RECEIVE skipped: %s (already exists)",name);
			report_str(s128 + 8,-1);
			skip_count++;
			report_error_count();
			return(ERROR);
		}

		if(Filemode & UNIXFILE)
			++Thisbinary;
		++Filcnt;

		report_file_rcv_started( name,
			(Bytesleft != DEFBYTL) ? Bytesleft : 0,Modtime,Filemode);
		report_rxpos(0L);
		report_str("",0);	/* get rid of End of File */
		if(Bytesleft != DEFBYTL)
		{
			long min_100;
			this_file_length = Bytesleft;
			min_100 = 2L + (((Bytesleft * 11L)) * 10L) / (Baudrate * 6L);
			sprintf(s128,"Receive time this file ~= %2lu:%02lu",
			    min_100 / 100,((min_100 % 100) * 60L) / 100L);
			if(TotalToReceive)
			{
				min_100 = 2L +
				    (((TotalToReceive * 11L)) * 10L) / (Baudrate * 6L);
				if(Baudrate > 4800)
				{
					min_100 *= 13;
					min_100 /= 9;	/* yech ... empirical */
				}
				sprintf(&s128[strlen(s128)],", transaction ~= %2lu:%02lu",
				    min_100 / 100,((min_100 % 100) * 60L) / 100L);
			}
			report_transaction(s128);
			sprintf(s128,"Receiving data (%d%% complete)",(int)0);
			report_str(s128,0);
		}
	}
	else 
	{		/* File coming from CP/M system */
	long now;
		for(p=name; *p; ++p)		/* change / to _ */
		{
			if( *p == '/')
				*p = '_';
		}

		if( *--p == '.')		/* zap trailing period */
			*p = 0;
		time(&now);
		report_file_rcv_started( name,
			(Bytesleft != DEFBYTL) ? Bytesleft : 0,now,0);
	}

	if(!Zmodem && MakeLCPathname && !isanylc(name) && !(Filemode&UNIXFILE))
		uncaps(name);

	strcpy(Pathname,name);
	report_xfer_mode(Thisbinary?"BINARY":"ASCII");
	fout = fopen(name,openmode);
#if defined(MD)
	if( !fout)
		if(make_dirs(name))
			fout = fopen(name,openmode);
#endif
	if( !fout)
		return(ERROR);
	this_file_errors = 0;
	return(OK);
}	/* end of procheader */

/*+-------------------------------------------------------------------------
	send_cancel() - send cancel string
--------------------------------------------------------------------------*/
void send_cancel(void)
{
static char canistr[] = 
{
	24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
};
register char *cptr = canistr;

	report_str("",0);
	report_last_txhdr("^X CAN",1);
	while(*cptr)
		sendline(*cptr++);
	Lleft=0;
}	/* end of send_cancel */

/*+-------------------------------------------------------------------------
	tryz()

  Initialize for Zmodem receive attempt, try to activate Zmodem sender
  Handles ZSINIT frame
  Return ZFILE if Zmodem filename received,-1 on error,
         ZCOMPL if transaction finished, else 0
--------------------------------------------------------------------------*/
int
tryz()
{
register c,n;

	if(Nozmodem)		/* Check for "rb" program name */
		return(0);

	for(n=Zmodem?15:5; --n>=0; )
	{
		/* Set buffer length (0) and capability flags */
		stohdr(0L);
#if defined(CANBREAK)
		Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK;
#else
		Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO;
#endif
		if(Zctlesc)
			Txhdr[ZF0] |= TESCCTL;
		Txhdr[ZF0] |= CANRLE;
		Txhdr[ZF1] = CANVHDR;
		/* tryzhdrtype may == ZRINIT */
		zshhdr(4,tryzhdrtype,Txhdr);
		if(tryzhdrtype == ZSKIP)	/* Don't skip too far */
			tryzhdrtype = ZRINIT;	/* CAF 8-21-87 */
again:
		switch(zgethdr(Rxhdr,0))
		{
		case ZRQINIT:
			if (Rxhdr[ZF3] & 0x80)
				Usevhdrs = 1;	/* we can var header */
			continue;
		case ZEOF:
			continue;
		case TIMEOUT:
			continue;
		case ZFILE:
			zconv = Rxhdr[ZF0];
			zmanag = Rxhdr[ZF1];
 			ztrans = Rxhdr[ZF2];
			if (Rxhdr[ZF3] & ZCANVHDR)
				Usevhdrs = TRUE;

			strcpy(s128,"Transfer: ");
			switch(zmanag & ZMMASK)
			{
			case 0:
				strcat(s128,"if destination nonexistent");
				break;
			case ZMAPND:	/* Append contents to existing file (if any) */
				strcat(s128,"append to destination");
				break;
			case ZMCLOB:	/* Replace existing file */
				strcat(s128,"absolute (overwrite)");
				break;
			case ZMNEW:
				strcat(s128,"if source newer");
				break;
			default:
				sprintf(s128 + strlen(s128),
				    "absolute (%02x)",zmanag & ZMMASK);
				break;
			}
			report_str(s128,2);

			tryzhdrtype = ZRINIT;
			c = zrdata(secbuf,1024);
			mode(3);
			if(c == GOTCRCW)
				return(ZFILE);
			zshhdr(4,ZNAK,Txhdr);
			goto again;
		case ZSINIT:
			Zctlesc = TESCCTL & Rxhdr[ZF0];
			if(zrdata(Attn,ZATTNLEN) == GOTCRCW)
			{
				stohdr(1L);
				zshhdr(4,ZACK,Txhdr);
				goto again;
			}
			zshhdr(4,ZNAK,Txhdr);
			goto again;
		case ZFREECNT:
			stohdr(getfree());
			zshhdr(4,ZACK,Txhdr);
			goto again;
		case ZCOMMAND:
			if(zrdata(secbuf,1024) == GOTCRCW)
			{
				stohdr(-1L);
				purgeline();	/* dump impatient questions */
				while(errors < 20)
				{
					zshhdr(4,ZCOMPL,Txhdr);
					if(zgethdr(Rxhdr,1) == ZFIN)
						break;
				}
				send_ZFIN_and_exit();
				return(ZCOMPL);
			}
			zshhdr(4,ZNAK,Txhdr);
			goto again;
		case ZCOMPL:
			goto again;
		default:
			continue;
		case ZFIN:
			send_ZFIN_and_exit();
			return(ZCOMPL);
		case ZCAN:
			return(ERROR);
		}
	}
	return(0);
}	/* end of tryz */

/*+-------------------------------------------------------------------------
	rzfile() - receive a file with ZMODEM protocol

  assumes file name frame is in secbuf
--------------------------------------------------------------------------*/
int rzfile(void)
{
register c,n;
long rxbytes;

	Eofseen=0;
	if(procheader(secbuf) == ERROR)
	{
		return(tryzhdrtype = ZSKIP);
	}

	n = 20;
	rxbytes = 0l;

	for(;;)
	{
		stohdr(rxbytes);
		zshhdr(4,ZRPOS,Txhdr);
		if(rxbytes)
			report_str("Sending ZRPOS",1);
nxthdr:
		switch(c = zgethdr(Rxhdr,0))
		{
		default:
			sprintf(s128,"zgethdr returned %02x",c);
			report_str(s128,1);
			return(ERROR);
		case ZNAK:
		case TIMEOUT:
			if( --n < 0)
			{
				sprintf(s128,"zgethdr returned %02x",c);
				report_str(s128,1);
				return(ERROR);
			}
		case ZFILE:
			zrdata(secbuf,1024);
			continue;
		case ZEOF:
			if(rclhdr(Rxhdr) != rxbytes)
			{
				/*
				 * Ignore eof if it's at wrong place - force
				 *  a timeout because the eof might have gone
				 *  out before we sent our zrpos.
				 */
				errors = 0;
				goto nxthdr;
			}
			if(close_and_report())
			{
				tryzhdrtype = ZFERR;
				return(ERROR);
			}
			report_str("End of file",0);
			return(c);
		case ERROR:	/* Too much garbage in header search error */
			if( --n < 0)
			{
				sprintf(s128,"zgethdr returned %02x",c);
				report_str(s128,1);
				return(ERROR);
			}
			zmputs(Attn);
			continue;
		case ZSKIP:
			close_and_report();
			sprintf(s128,"rzfile: Sender SKIPPED file");
			report_str(s128,1);
			return(c);
		case ZDATA:
			if(rclhdr(Rxhdr) != rxbytes)
			{
				if( --n < 0)
				{
					return(ERROR);
				}
				zmputs(Attn);
				continue;
			}
moredata:
			switch(c = zrdata(secbuf,1024))
			{
			case ZCAN:
				sprintf(s128,"zgethdr returned %02x",c);
				report_str(s128,1);
				return(ERROR);
			case ERROR:	/* CRC error */
				if( --n < 0)
				{
					sprintf(s128,"zgethdr returned %02x",c);
					report_str(s128,1);
					return(ERROR);
				}
				zmputs(Attn);
				continue;
			case TIMEOUT:
				if( --n < 0)
				{
					sprintf(s128,"zgethdr returned %02x",c);
					report_str(s128,1);
					return(ERROR);
				}
				continue;
			case GOTCRCW:
				n = 20;
				write_sec_to_disk(secbuf,Rxcount);
				rxbytes += Rxcount;
				stohdr(rxbytes);
				zshhdr(4,ZACK,Txhdr);
				sendline(XON);
				goto nxthdr;
			case GOTCRCQ:
				n = 20;
				write_sec_to_disk(secbuf,Rxcount);
				rxbytes += Rxcount;
				stohdr(rxbytes);
				zshhdr(4,ZACK,Txhdr);
				goto moredata;
			case GOTCRCG:
				n = 20;
				write_sec_to_disk(secbuf,Rxcount);
				rxbytes += Rxcount;
				goto moredata;
			case GOTCRCE:
				n = 20;
				write_sec_to_disk(secbuf,Rxcount);
				rxbytes += Rxcount;
				goto nxthdr;
			}
		}
	}
}	/* end of rzfile */

/*+-------------------------------------------------------------------------
	rzfiles() - receive file(s) with ZMODEM protocol
--------------------------------------------------------------------------*/
int rzfiles(void)
{
register c;

	for(;;)
	{
		switch(c = rzfile())
		{
		case ZEOF:
		case ZSKIP:
			switch(tryz())
			{
			case ZCOMPL:
				return(OK);
			default:
				return(ERROR);
			case ZFILE:
				break;
			}
			continue;
		default:
			return(c);
		case ERROR:
			return(ERROR);
		}
	}
}	/* end of rzfiles */

/*+-------------------------------------------------------------------------
	close_and_report() - close the received file, set mod time and chmod
(specifically exclude set uid and gid from chmod)
--------------------------------------------------------------------------*/
int close_and_report(void)
{
	fflush(fout);
	fstat(fileno(fout),&fout_stat);
	report_file_byte_io((long)fout_stat.st_size);
	report_file_close();

	if(fclose(fout)==ERROR)
		return(ERROR);

	if(Modtime)
	{
		timep[0] = time(NULL);
		timep[1] = Modtime;
		utime(Pathname,timep);
	}

	if((Filemode & S_IFMT) == S_IFREG)
	{
    	Filemode &= ~(S_ISUID | S_ISGID);
		chmod(Pathname,(07777 & Filemode));
	}

	return(OK);

}	/* end of close_and_report */

/*+-------------------------------------------------------------------------
	send_ZFIN_and_exit() - send ZFIN packet and wait for "OO" ack
--------------------------------------------------------------------------*/
void send_ZFIN_and_exit(void)
{
register n;

	Readnum = 1;
	stohdr(0L);
	for(n = 0; n < 4; n++)
	{
		purgeline();
		zshhdr(4,ZFIN,Txhdr);
		switch(readline(100))
		{
		case 'O':
			readline(1);	/* Discard 2nd 'O' */
			return;
		case RCDO:
			return;
		case TIMEOUT:
		default:
			break;
		}
	}
}	/* end of send_ZFIN_and_exit */

/*+-------------------------------------------------------------------------
	sys2(shellcmd) - extipte shell command

 Strip leading ! if present
--------------------------------------------------------------------------*/
int sys2(shellcmd)
register char *shellcmd;
{
	if(*shellcmd == '!')
		++shellcmd;
	return(system(shellcmd));
} /* end of sys2 */

/*+-------------------------------------------------------------------------
	main(argc,argv,envp)
--------------------------------------------------------------------------*/
int main(argc,argv,envp)
int argc;
char **argv;
char **envp;
{
register char *cp;
char **patts;
char *getenv();
int exitcode = 0;
char **gargv = argv;
int gargc = argc;

	signal(SIGINT,bye_bye);
	signal(SIGTERM,bye_bye);

	get_curr_dir(curr_dir,sizeof(curr_dir));

	Rxtimeout = 100;

	npats = 0;
	while(--argc)
	{
		cp = *++argv;
		if(*cp == '-')
		{
			while( *++cp)
			{
				switch(*cp)
				{
				case 'X':
					required_type = 1;
					Batch = 0;
					break;
				case 'Y':
					required_type = 1;
					Nozmodem = 1;
					Batch = 1;
					break;
				case 'Z':
					required_type = 1;
					Nozmodem = 0;
					Batch = 1;
					break;
				case '+':
					Lzmanag = ZMAPND;
					break;
				case 'a':
					Rxascii=1;
					break;
				case 'b':
					Rxbinary=1;
					break;
				case 'c':
					Crcflg=1;
					break;
				case 'e':
					Zctlesc = 1;
					break;
				case 'p':
					Lzmanag = ZMPROT;
					break;
				case ',':
					log_packets = 1;
					break;
				case '.':
					if(--argc < 1)
					{
						usage("no iofd after -.");
					}
					iofd = atoi(*++argv);
					break;
#ifdef HAYES
				case ':':
					if(--argc < 1)
					    usage("no speed after -:");
					Baudrate = atoi(*++argv);
					break;
#endif
				case 't':
					if(--argc < 1)
					{
						usage("no rcvr timeout after -t");
					}
					Rxtimeout = atoi(*++argv);
					if(Rxtimeout<10 || Rxtimeout>1000)
						usage("illegal timeout: must be 10 <= t <= 1000");
					break;
				case 'w':
					if(--argc < 1)
					{
						usage("no Zrwindow after -w");
					}
					Zrwindow = atoi(*++argv);
					break;
				case 'C':
					if(--argc < 1)
						usage("no label after -C");
					bottom_label = *++argv;
					break;
				case 'u':
					MakeLCPathname=0;
					break;
				case 'y':
					Rxclob=1;
					break;
				default:
					sprintf(s128,"Unknown switch -%c",*cp);
					usage(s128);
				}
			}
		}
		else if( !npats && argc>0)
		{
			if(argv[0][0])
			{
				npats=argc;
				patts=argv;
			}
		}
	}

	if(!required_type || !iofd)
	{
		printf("can only be run by tip\n");
		exit(255);
	}

	if(log_packets)
	{
		char log_packets_name[64];
		FILE *ftmp;
		int iargv;
		sprintf(log_packets_name,"/tmp/rz%05d.plog",getpid());
		unlink(log_packets_name);
		ftmp = fopen(log_packets_name,"w");
		fclose(ftmp);
		log_packets = open(log_packets_name,O_WRONLY,0644);
		if(log_packets < 0)
			log_packets = 0;
		else
		{
			write(log_packets,"exec: ",6);
			for(iargv = 0; iargv < gargc; iargv++)
			{
				write(log_packets,gargv[iargv],strlen(gargv[iargv]));
				write(log_packets," ",1);
			}
			write(log_packets,"\n",1);
		}
	}


	if(Batch && npats)
		usage("Cannot specify batch receive and filename");
	if(npats > 1)
		usage("only one filename allowed");
	sprintf(s128,"%s",numeric_revision);
	report_init(s128);
	mode(1);
	signal(SIGINT,cancel_transaction);
	signal(SIGTERM,cancel_transaction);
	signal(SIGQUIT,cancel_transaction);
	if(wcreceive(npats,patts)==ERROR)
	{
		exitcode=0200;
		send_cancel();
	}
	mode(0);
	if(exitcode && !Zmodem)	/* bellow again with all thy might. */
		send_cancel();
	report_uninit(0);
	exit(exitcode);
	return(0);
}

/* vi: set tabstop=4 shiftwidth=4: */
/* end of tiprz.c */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.