Category : Communication (modem) tools and utilities
Archive   : VTRANS.ZIP
Filename : C8UNIX.C

 
Output of file : C8UNIX.C contained in archive : VTRANS.ZIP
#define TYPE2 TRUE
#include
#include
#include "c8unix.h"

#ifdef TYPE2
#include
#include
#include

static struct termio Term, Save ;
#endif

#ifdef TYPE1
#include

static struct sgttyb Term, Save ;
#endif

int Debug = 0 ;
int Timeout = 0 ;
char Takedefault ;
char Sequence, Lastseq, Checktype, Czeof, Filexists ;
int Textfile, Size, Wordsize, Checksize ;


FILE *Log ;

/* getresp
*
* DESCRIPTION Used in Host to PC transfers. Reads a packet
* from VTERM, expecting ACK, NAK or ABORT.
*
* RETURNS One of PKT_ACK, PKT_NAK, PKT_ABORT or PKT_TIMEOUT.
* Error conditions are treated as NAKs.
*/

int getresp()
{
char buf[ MAXPKT + 1 ] ;
int len, type ;

if ( Debug )
fprintf(Log,"getresp()\n");

/* What did we get? */
switch ( type = getpkt( buf, &len ) ) {

case PKT_ABORT:
case PKT_TIMEOUT:
case PKT_ACK:
if ( Debug )
fprintf(Log,"getresp() returning type %c\n", type ) ;
return( type ) ;

default: /* anything else is treated as a NAK */
if ( Debug )
fprintf(Log,"getresp() got %c - returning NAK\n", type);

return( PKT_NAK ) ;
}
}


/* getdata
*
* DESCRIPTION Used for PC to Host transfers. Reads a packet
* from VTERM. Expects one of PKT_DATA, PKT_EOF or
* PKT_ABORT. ACKs and NAKs, if any, are ignored.
* Abort procedures are called here. If an error
* occurs, a NAK is sent and the loop repeats.
*
* RETURNS The length of the received data is returned in the
* argument lenptr. The function returns one of:
* PKT_TIMEOUT, PKT_ABORT, PKT_EOF, or PKT_DATA.
* If the packet is PKT_DATA type, the data is copied
* into the buffer at "buf". The value returned in
* "lenptr" represents a count of bytes in restored PC
* file format.
*/

int getdata( buf, lenptr )
char *buf ;
int *lenptr ;
{
int type ;

/* set a 20 second timer */
clock( DATAWAIT ) ;

/* loop until we get a data packet or an abort */
for (;;) {

if ( Debug )
fprintf(Log,"top of getdata() loop\n");

/* What did we get? */
switch ( type = getpkt( buf, lenptr ) ) {

case PKT_EOF:
send( PKT_ACK ) ;
break ;

case PKT_REPEAT: /* repeat of last packet */
send( PKT_REPEAT ) ;
continue ;

case PKT_ABORT:
case PKT_TIMEOUT:
break ;

case PKT_DATA:
if ( Debug )
fprintf(Log,"got data : len %d\n", *lenptr) ;
break ;

default: /* parse error - try again */
if ( Debug )
fprintf(Log,"type %c - return NAK\n", type );
send( PKT_NAK ) ;
continue ;
}

if ( Debug )
fprintf( Log, "getdata returning type %c\n", type ) ;

clock( 0 ) ;
return( type ) ;
}
}


/* sendstr
*
* DESCRIPTION Send a null terminated string to VTERM.
*
* RETURNS None.
*/

sendstr( str )
char *str ;
{
if ( Debug ) {
fprintf( Log, "sendstr() sending:\n") ;
dump( str, strlen(str) ) ;
}

fputs( str, stdout ) ;
fflush( stdout ) ;
}


/* send
*
* DESCRIPTION Send a message of the given type. The type must
* be one of ACK, NAK, or ABORT. DATA and EOF packets
* are only sent by the functions in "writef.c". No
* retransmissions are attempted for these packets.
*
* RETURNS None.
*/

send( type )
char type ;
{
char seq, buf[ HEADERSIZE + 1 ] ;

switch ( type ) {
case PKT_NAK:
fflush( stdin ) ;
seq = Lastseq ;
break ;
case PKT_REPEAT:
seq = Lastseq ;
type = PKT_ACK ;
break ;
case PKT_ACK:
case PKT_ABORT:
seq = Sequence ;
break ;
default:
printf("internal error - illegal packet type %c\n", type ) ;
return ;
}

sprintf( buf, "%c%c%c%c", SOH, seq, seqchk(seq), type ) ;
write( 1, buf, 4 ) ;
}

/* crc
*
* DESCRIPTION Compute cyclic redundancy check (CRC) for a
* byte string. This implementation assumes
* 16 bit integers.
*
* RETURNS unsigned integer containing 16 bit CRC value.
*/

unsigned int crc( buf, len )
char *buf ; /* pointer to buffer to compute CRC on */
int len ; /* the number of bytes in the buffer */
{
unsigned int t1, t2, t3, result = 0 ;
int signed, bits ;

if ( Debug ) {
fprintf( Log, "computing CRC on %d bytes\n", len ) ;
dump( buf, len ) ;
}

/* loop through "len" bytes starting at "buf" */
while ( len-- ) {
t1 = t2 = lowbyte( *buf++ ^ result ) ;
t1 = t1 >> 4 ;
t1 = t3 = t1 ^ t2 ;
t1 = t1 >> 2 ;
t1 = t3 = t1 ^ t3 ;
t1 = t1 >> 1 ;
t1 = t1 ^ t3 ;
t1 = t1 & 1 ;
if ( t1 != 0 )
t1 = 0xC001 ;
t3 = t1 ;
t1 = highbyte( result ) ;
t3 = t1 ^ t3 ;

/* make sure we have sign extension for signed addition */
signed = t2 ;
if ( ( bits = Wordsize - 16 ) > 0 )
signed = ( signed << bits ) >> bits ;

t1 = low16(signed + signed) ;

t1 = t1 ^ t2 ;
t1 = low16((t1 << 6) | (t1 >> 10)) ; /* rotate left 6 bits */

result = t1 ^ t3 ;
}

if ( Debug )
fprintf( Log, "crc returning: %x\n", result ) ;

return( result ) ;
}


/* testcrc
*
* DESCRIPTION Check that the computed CRC value matches that
* which was sent. This function hides peculiarities
* such as storing integers with the bytes swapped.
*
* RETURNS Boolean value indicating result of comparison.
*/

int testcrc( buf, len )
char *buf ;
int len ;
{
unsigned computed, sent ;

if ( Debug )
fprintf(Log,"testcrc: %d chars\n", len ) ;

/* compute CRC for this buffer */
computed = crc( buf, len ) ;

if ( Debug )
fprintf( Log, "computed crc: %04x\n", computed ) ;

/* advance pointer to transmitted CRC values */
buf += len ;

/* compose a 16 bit value from the 2 transmitted bytes */
sent = lowbyte( *buf++ ) ;
sent |= lowbyte( *buf++ ) << 8 ;

if ( Debug )
fprintf( Log, "tranmitted crc: %04x\n", sent ) ;

/* compare with transmitted CRC */
return( sent == computed ) ;
}


/* testsum
*
* DESCRIPTION Check that the computed checksum matches that
* which was sent.
*
* RETURNS Boolean value indicating result of comparison.
*/

int testsum( buf, len )
char *buf ;
int len ;
{
int sent, sum ;

if ( Debug )
fprintf(Log,"testsum: %d chars\n", len ) ;

/* compute checksum */
sum = chksum( buf, len ) ;

if ( Debug )
fprintf(Log,"computed checksum: %d\n", sum ) ;

sent = lowbyte( buf[ len ] ) ;

if ( Debug )
fprintf( Log, "transmitted checksum: %d\n", sent ) ;

return( sent == sum ) ;
}


/* addcrc
*
* DESCRIPTION Add the computed CRC value onto the buffer.
*
* RETURNS Pointer to next available location in "buf".
*/

char *addcrc( buf, len )
char *buf ;
int len ;
{
unsigned value ;

value = crc( buf, len ) ;
buf += len ;
*buf++ = lowbyte ( value ) ;
*buf++ = highbyte( value ) ;
return( buf ) ;
}


/* addsum
*
* DESCRIPTION Add the computed checksum value onto the buffer.
*
* RETURNS Pointer to next available location in "buf".
*/

char *addsum( buf, len )
char *buf ;
int len ;
{
buf[ len ] = (char) chksum( buf, len ) ;
return( buf + len + LEN_CHK ) ;
}


/* chksum
*
* DESCRIPTION Calculate a modulo-255 checksum on "len" bytes
* starting at "buf".
*
* RETURNS The calculated checksum is returned as an integer.
*/

int chksum( buf, len )
char *buf ;
int len ;
{
int result = 0 ;

if ( Debug ) {
fprintf( Log, "computing CHKSUM on %d chars\n", len ) ;
dump( buf, len ) ;
}

while ( len-- )
result = ( result + lowbyte( *buf++ ) ) % 256 ;

if ( Debug )
fprintf( Log, "chksum returning %d\n", result ) ;

return( result ) ;
}


/* testcheck
*
* DESCRIPTION Test the check characters appended to the data
* buffer. The type of checking used is stored in
* the global variable "Checktype".
*
* RETURNS 1 if the buffer checks out, 0 if not.
*/

int testcheck( buf, len )
char *buf ;
int len ;
{

/* Compute check value and compare result. */
if ( Checktype == CRC )
return( testcrc( buf, len - LEN_CRC ) ) ;
else
return( testsum( buf, len - LEN_CHK ) ) ;
}

/* possible protocol types */
#define VTRANS8 '0'
#define ASCII '1'
#define XMODEM '2'
#define VTRANS7 '3'

/* possible block terminators */
#define CR_TERM '0'
#define LF_TERM '1'
#define CRLF_TERM '2'
#define LFCR_TERM '3'

/* setpcfile
*
* DESCRIPTION Send the PC file name to VTERM II and request
* the name back as verification.
*
* RETURNS 1 if the PC confirms the filename, 0 if it
* fails to respond, or responds incorrectly.
*/

int setpcfile( pcname )
char *pcname ;
{
char buf[ MAXINPUT ] ;
int ret ;

if ( Debug )
fprintf( Log, "setting pc file name to `%s'\n", pcname ) ;

/* set up for escape sequences */
ttysetup() ;

/* send VTERM the "set filename" sequence */
sprintf( buf, "\033[>0f%s\r", pcname ) ;
sendstr( buf ) ;

/* ask VTERM II for the filename received */
sprintf( buf, "\033[>1f\r" ) ;

sendstr( buf ) ;

if ( Debug )
fprintf(Log, "Waiting for PC to confirm filename\n") ;

clock( DATAWAIT ) ; /* set 20 second timeout for the response */

/* get a string terminated by a CR */
ret = getstr( buf ) ;

clock( 0 ) ; /* clear the timer */
ttyreset() ; /* reset terminal */

/* see if we timed out on the response */
if ( ! ret )
return( 0 ) ;

if ( Debug )
fprintf(Log, "setpc: returned name is `%s'\n", buf ) ;

return( strcmp( buf, pcname ) == 0 ) ;
}


/* setproto
*
* DESCRIPTION Send the start-transfer sequence containing the
* file transfer protocol parameters and get the "Y"
* response within 20 seconds.
*
* RETURNS 1 if VTERM responds correctly, 0 otherwise.
*/

int setproto( direction )
char direction ; /* which way we're transferring */
{
char buf[ MAXINPUT ] ;
int ret ;

if ( Debug )
fprintf( Log, "setproto( %s, %d, %s )\n",
direction == PCSEND ? "PCSEND" : "PCRECV",
Size, Checktype == CRC ? "CRC" : "CHKSUM" ) ;

sprintf( buf, "\033[>%c;%d;%c;%c;%c%c\r", VTRANS8, Size, Checktype,
Czeof, Filexists, direction ) ;

sendstr( buf ) ;

clock( DATAWAIT ) ; /* set 20 second timeout for the response */

ret = getstr( buf ) ;

if ( Debug ) {
if ( ret )
fprintf( Log, "confirmation string: `%s'\n", buf ) ;
else
fprintf( Log, "confirmation not received\n" ) ;
}

clock( 0 ) ;
return( ret && strcmp( buf, "Y" ) == 0 ) ;
}


int getstr( buf )
char *buf ;
{
int i = 0 ;
char c ;

while ( !Timeout && i++ < MAXINPUT ) {

if ( read( 0, &c, 1 ) <= 0 )
return( 0 ) ;

if ( c == CR || c == LF ) {
*buf = 0 ;
break ;
} else
*buf++ = c ;
}

return( !Timeout ) ;
}


/* getpkt
*
* DESCRIPTION Try to read a valid buffer sent from VTERM. A
* valid buffer is defined as starting with the SOH
* (start of header) character, with sequence number
* and the one's complement of the sequence number
* immediately following. All characters until start
* of header (SOH) are ignored.
*
* RETURNS One of: PKT_ERROR, PKT_TIMEOUT, PKT_ACK, PKT_NAK
* PKT_REPEAT, PKT_ERROR or PKT_ABORT. For data
* packets, the length of the received packet is
* returned in "lenptr".
*
*/


int getpkt( buf, lenptr )
char *buf ; /* buffer to read into - must be MAXPKT bytes */
int *lenptr ; /* pointer to integer to return char count */
{
char c, seq, chk, type, lenbyte ;
char pkt[ MAXPKT + 1 ] ;
int len ;
register char *ptr ;

/* loop until start of header is found */
for (;;) {

if ( !rdchar( &c, 1 ) ) {
if ( Debug )
fprintf( Log, "getpkt: timeout at top\n");
return( PKT_TIMEOUT ) ;
}

if ( c != SOH ) {
if ( Debug )
fprintf( Log, "garbage read: %c = %d\n", c, c);
continue ;
}

if ( Debug )
fprintf( Log, "Found SOH\n" ) ;

if ( !rdchar( &seq, 1 ) || !rdchar( &chk, 1 ) ) {
if ( Debug )
fprintf( Log, "timed out on seq or chk\n") ;
return( PKT_TIMEOUT ) ;
}

if ( Debug )
fprintf( Log, "seq = %x chk = %x\n",
lowbyte( seq ), lowbyte( chk ) ) ;

/* ignore impossible sequence numbers */
/*****
This may cause NAKs to be ignored since VTERM sends the first one with
sequence number 0. Any sequence error will wash out below...

if ( seq < SEQINIT || seq > SEQMAX ) {
if ( Debug )
fprintf( Log, "seq out of range\n" ) ;
continue ;
}
*****/

if ( lowbyte( seqchk( seq ) ) == lowbyte( chk ) ) {
if ( Debug )
fprintf( Log, "good seq check\n" ) ;
break ;
}
}

if ( !rdchar( &type, 1 ) ) {
if ( Debug )
fprintf( Log, "timed out reading type\n") ;
return( PKT_TIMEOUT ) ;
}

/* no more checking needed for ABORT or NAK packets */
if ( type == PKT_ABORT || type == PKT_NAK ) {
if ( Debug )
fprintf( Log, "getpkt: returning %s\n",
( type == PKT_NAK ? "NAK" : "ABORT" ) ) ;
return( type ) ;
}

/* make sure the packet contains the correct sequence number */
if ( seq != Sequence ) {
if ( seq == Lastseq && type == PKT_DATA )
return( PKT_REPEAT ) ;

if ( Debug )
fprintf( Log, "getpkt: bad seq - Sequence = %d\n",
Sequence ) ;

return( PKT_ERROR ) ;
}

/* that suffices for ACK packets */
if ( type == PKT_ACK ) {
if ( Debug )
fprintf( Log, "getpkt: returning ACK\n" ) ;
return( PKT_ACK ) ;
}

/* place in packet buffer for check value computation
* note that in the 7-bit version the returned buffer
* contains the Start of header character. Here we
* throw it away, since we're processing the buffer
* as we read it.
*/
ptr = pkt ;
*ptr++ = seq ;
*ptr++ = chk ;
*ptr++ = type ;

/* for DATA and EOF, the computed check value must also match */
if ( type == PKT_DATA ) {

/* get the byte count */
if ( !rdchar( &lenbyte, 1 ) ) {
if ( Debug )
fprintf( Log, "timed out reading len\n") ;
return( PKT_TIMEOUT ) ;
}

/* save in buffer for check value computation */
*ptr++ = len = lowbyte( lenbyte ) ;

/* fix for zero relativity and save for return to caller */
*lenptr = ++len ;

if ( Debug )
fprintf( Log, "buffer len = %d\n", len ) ;

/* read "len" data bytes into local buffer */
if ( !rdchar( ptr, len ) ) {
if ( Debug )
fprintf( Log, "timed out reading data\n" ) ;
return( PKT_TIMEOUT ) ;
}

if ( Debug ) {
fprintf( Log, "getpkt: received DATA packet\n" ) ;
dump( ptr, len ) ;
}

ptr += len ;

} else if ( type == PKT_EOF ) {

if ( Debug )
fprintf( Log, "getpkt: received EOF packet\n" ) ;
len = 0 ;

} else /* otherwise we have a garbage packet type */
return( PKT_ERROR ) ;

/* read transmitted check value into "chkbuf" */
if ( !rdchar( ptr, Checksize ) )
return( PKT_TIMEOUT ) ;

ptr += Checksize ;

/* Compare the check value for the buffer. */
if ( testcheck( pkt, (int) (ptr - pkt) ) ) {
if ( Debug )
fprintf( Log, "good check value - returning type %c\n",
type ) ;

/* Copy the file data into the caller's buffer,
* skipping the header. Note that we didn't save
* the SOH, thus the "- 1".
*
*/
for ( ptr = pkt + HEADERSIZE - 1; len--; )
*buf++ = *ptr++ ;

return( type ) ;
} else {
if ( Debug )
fprintf( Log, "bad check value - returning ERROR\n" ) ;
return( PKT_ERROR ) ;
}
}


int rdchar( buf, len )
register char *buf ;
register int len ;
{

if ( Debug )
fprintf( Log, "rdchar (before) : Timeout = %d, len = %d\n",
Timeout, len ) ;

while ( !Timeout && len-- > 0 ) {
if ( read( 0, buf++, 1 ) <= 0 ) {
if ( Debug )
fprintf( Log, "rdchar: error (len=%d)\n",len ) ;
return( 0 ) ;
}
}

if ( Debug )
fprintf( Log, "rdchar (after) : Timeout = %d\n", Timeout ) ;

return( !Timeout ) ;
}


/* catch
*
* DESCRIPTION Catch interrupts. If anything but SIGALRM is
* received, call quit() to restore old terminal
* settings and close files. If SIGALRM is caught,
* set the global flag "Timeout" to 1.
*
* RETURNS None.
*/

catch( sig )
int sig ;
{

/* if not a timer, make a clean exit */
if ( sig != SIGALRM )
quit(0) ;

/* reset alarm catcher */
signal( SIGALRM, catch ) ;

/*show that a timer went off */
Timeout = 1 ;

if ( Debug )
fprintf( Log, "catch: timeout\n" ) ;
}


/* catchsigs
*
* DESCRIPTION Initialization routine called from main() to
* set interrupt traps. All signals are trapped
* and set to call the function catch() above.
*
* RETURNS None.
*/

catchsigs()
{
int i ;

/* catch all signals - see "signal.h" */
for ( i=1; i signal( i, catch ) ;
}


/* ttyinit
*
* DESCRIPTION Initialize terminal control structures for use
* by ttysetup() and ttyreset() functions.
*
* RETURNS None.
*/

ttyinit()
{
#ifdef TYPE2
ioctl( 0, TCGETA, &Save ) ; /* save a copy to restore with */
ioctl( 0, TCGETA, &Term ) ; /* working copy of terminal mode */

/* On input: interrupt on break, ignore parity errors */
Term.c_iflag = BRKINT | IGNPAR ;

/* On output: no post processing at all */
Term.c_oflag = 0 ;

/* Line discipline: no echo, no signals, no erase or kill */
Term.c_lflag &= ~ISIG & ~ECHO & ~ICANON ;

/* we want exclusive use of this line during transmissions */
Term.c_lflag |= XCLUDE ;

/* satisfy read on receiving 1 char or a 0/10 second delay occurs */
Term.c_cc[ VEOF ] = 1 ;
Term.c_cc[ VEOL ] = 0 ;

#endif

#ifdef TYPE1
ioctl( 0, TIOCGETP, &Save ) ; /* save copies to restore with */
ioctl( 0, TIOCGETP, &Term ) ; /* working copy of terminal mode */

/* Set tty interface for for RAW mode - no processing.
* Full 8-bit interface.
*/
Term.sg_flags = RAW ;
#endif
}


/* ttysetup
*
* DESCRIPTION Set up terminal for transparent data transfer.
* Assumes that ttyinit() has been called to
* initialize the terminal control structures.
*
* RETURNS None.
*/

ttysetup()
{
#ifdef TYPE2
ioctl( 0, TCSETA, &Term ) ;
#endif

#ifdef TYPE1
ioctl( 0, TIOCSETP, &Term ) ;
ioctl( 0, TIOCEXCL, 0 ) ; /* get exclusive use of tty */
#endif
}


/* ttyreset
*
* DESCRIPTION Restore terminal to state before vtrans was run.
* Assumes that ttyinit() has been called to save
* the previous terminal state.
*
* RETURNS None.
*/

ttyreset()
{
#ifdef TYPE2
ioctl( 0, TCSETA, &Save ) ; /* restore tty with saved copy */
#endif

#ifdef TYPE1
ioctl( 0, TIOCSETP, &Save ) ; /* restore tty with saved copy */
ioctl( 0, TIOCNXCL, 0 ) ; /* restore non-exclusivity */
#endif
}


main( argc, argv )

int argc ;
char **argv ;
{
FILE *fp ;

/* print startup banner */
printf( "\n**VTRANS 1.6 -- HOST SOFTWARE FOR " ) ;
printf( "8-BIT VTRANS: UNIX SYSTEMS**\n\n" ) ;
printf( "VTERM's File Transfer Setup options " ) ;
printf( "will be set automatically by this dialogue.\n") ;
printf( "There is no need to use the file transfer setup screen.\n") ;

if ( Debug && ! ( Log = fopen("logfile", "w") ) ) {
puts("can't open log file") ;
exit(0) ;
}

Wordsize = wordlen() ;
ttyinit() ;
catchsigs() ;

if ( argc > 1 ) {
while ( --argc ) {
if ( ! ( fp = fopen( *++argv, "r" ) ) ) {
printf( "can't open command file \"%s\"\n",
*argv ) ;
continue ; /* try the next file */
}

/* transfer using the info in the command file */
vtrans( fp ) ;

fclose( fp ) ;
}
}

/* if no command files are specified, or when command
* files are exhausted, read commands from keyboard
*/
vtrans( stdin ) ;
}


cmderr( msg )
char *msg ;
{
printf( "command file error: %s\n", msg ) ;
}


FILE *hostfile( fp, name, direction )
FILE *fp ;
char *name ;
int direction ;
{
FILE *host ;
char *mode = "w" ;

/* see if file exists - try to open for reading */
host = fopen( name, "r" ) ;

/* if PC is sending, see if we'll clobber a file on the host.
* If we're running a command file, any existing files are
* silently overwritten. Perhaps that's a tad unfriendly?
* Easily enough changed!
*/
if ( direction == PCSEND ) {

/* enter this dialog only if interactive and file exists */
if ( host && interact( fp ) ) {

printf( "\nFILE \"%s\" ALREADY EXISTS ON HOST.",
name ) ;

fclose( host ) ;

if ( yes( fp, "\nDO YOU WANT TO OVERWRITE? " ) )
mode = "w" ;
else {
if ( yes( fp, "\nDO YOU WANT TO APPEND? " ) )
mode = "a" ;
else
return( 0 ) ;
}
}

host = fopen( name, mode ) ;
}

if ( !host )
printf( "\ncan't open host file \"%s\"\n", name ) ;

return( host ) ;
}


/* quit
*
* DESCRIPTION Tidy up any loose ends (reset the terminal) and
* exit the program with the given exit code.
*
* RETURNS None.
*/

quit( code )
int code ;
{
ttyreset() ;
if ( Debug )
fclose( Log ) ;
exit( code ) ;
}


static int Crfound = 0 ; /* remember to reset between files */


/* readfile
*
* DESCRIPTION Read packets from VTERM and write the data to
* the file pointed to by the open stream "fp".
*
* RETURNS PKT_EOF if the file transfer was completed
* successfully, PKT_ABORT if VTERM called an

* abort, or PKT_TIMEOUT if the host is aborting.
*/

int readfile( fp, pbytes )
FILE *fp ;
long *pbytes ;
{
char buf[ MAXPKT + 1 ] ;
int len, type ;

*pbytes = 0 ;

if ( Debug )
fprintf( Log, "readfile()\n") ;

for (;;) {
switch ( type = getdata( buf, &len ) ) {

case PKT_TIMEOUT:
case PKT_ABORT:
if ( Debug )
fprintf(Log, "readfile returning %c\n", type ) ;
return( type ) ;

case PKT_EOF:
if ( Debug )
fprintf( Log, "readfile got EOF\n") ;

flushbuf( fp ) ;
return( PKT_EOF ) ;

case PKT_DATA:
if ( Debug )
fprintf(Log,"readfile() - good packet\n") ;

if ( ! writebuf( fp, buf, len ) ) {
if ( Debug )
fprintf(Log,
"error writing to host file\n") ;
return( PKT_TIMEOUT ) ;
}

/* if good packet, send an ACK */
send( PKT_ACK ) ;

/* update sequence counter */
nextseq() ;

*pbytes += len ;
}
}
}


/* writebuf
*
* DESCRIPTION write a buffer in pc format to the host,
* in host format.
*
* RETURNS 1 if ok, 0 if write error occurred.
*/

int writebuf( fp, buf, len )
FILE *fp ;
char *buf ;
int len ;
{
char c ;

if ( Debug )
fprintf( Log, "writebuf: writing %d chars\n", len ) ;

/* if not a text format file, just write whatever is in the buffer */
if ( ! Textfile )
return( write( fileno(fp), buf, len ) == len ) ;

while ( len-- > 0 ) {

c = *buf++ ;

/* convert CR/LF pairs to single LF's but
* CR/x for x != LF, we write the saved CR
* as a linefeed instead.
*/
if ( Crfound && strip(c) != LF ) {
if ( Debug )
fprintf( Log, "found CR with no LF\n" ) ;
fputc( LF, fp ) ;
}

/* check for CR */
if ( Crfound = ( strip(c) == CR ) )
continue ;

/* otherwise, just write what we have */
fputc( c, fp ) ;
}

return( 1 ) ;
}


/* flushbuf
*
* DESCRIPTION Write any buffered CR's and flush the file buffer.
*
* RETURNS None.
*/

flushbuf( fp )
FILE *fp ;
{
if ( Crfound )
fputc( LF, fp ) ;

fflush( fp ) ;
Crfound = 0 ;
}

#define DEBUG

#define PKTMSG "\nPACKET SIZE (32, 64, 128, 256) <256>? "
#define TXMSG "\nOPTION: "
#define CHKMSG "\nERROR DETECTION (CH-ecksum or CR-c) ? "
#define TEXTMSG "\nWILL HOST FILE BE TEXT FORMAT ? "
#define PCMSG "\nWHAT IS THE NAME OF THE FILE ON THE PC <"
#define HSTMSG "\nWHAT IS THE NAME OF THE FILE ON THE HOST? "
#define CZMSG "\nIS CONTROL-Z END OF PC FILE <"
#define COLLMSG "\nIF PC FILE EXISTS (P-rompt, O-verwrite, R-ename) ? "

/* getmode
*
* DESCRIPTION Find out which way the file transfer is to go.
* This function always allows retries on errors. If
* we are reading a command file, this will probably
* result in skipping the entire transaction until and
* resuming with the next line containing a "1", "2"
* or "3". If interacting with user, we allow unlimited
* retries until a valid response is entered.
*
* RETURNS One of mode macros PCSEND, PCRECV, EXIT, or CMDERR
* On encountering end of file, CMDERR is returned.
*/

char getmode( fp )
register FILE *fp ;
{
register char *ptr ;
int ok ;

if ( interact( fp ) ) {
puts("\n") ;
puts("WHICH WAY IS TRANSFER?\n") ;
puts( " 1 = PC TO HOST" ) ;
puts( " 2 = HOST TO PC" ) ;
puts( " 3 = EXIT PROGRAM" ) ;
}

/* loop until a '1' '2' or '3' is entered, or EOF is found */
do {
if ( ! ( ptr = input( fp, TXMSG ) ) && feof( fp ) )
return( CMDERR ) ;

ok = ( ptr && strlen(ptr) == 1 && *ptr >= '1' && *ptr <= '3' ) ;

if ( ! ok && cmdfile( fp ) )
cmderr( "bad direction specifier" ) ;

} while ( ! ok ) ;

/* return the appropriate character value */
switch ( *ptr ) {
case '1':
return( PCSEND ) ;
case '2':
return( PCRECV ) ;
case '3':
return( EXIT ) ;
default:
return( CMDERR ) ;
}
}


/* chktype
*
* DESCRIPTION Determine the type of error checking desired.
* The default value is CRC if nothing is entered.
* If anything is entered, it must be CRC or CHK.
* Anything else is an error, and if we're reading
* a command file, it is fatal.
*
* RETURNS One of the macros CRC, CHKSUM or CMDERR.
*/

char chktype( fp )
FILE *fp ;
{
register char *ptr ;
char *endptr, *instrng( ) ;

/* forever - or until the user gets it right! */
for (;;) {

if ( Takedefault )
return( CRC ) ;

if ( ! ( ptr = input( fp, CHKMSG ) ) )
return( CRC ) ;

if ( strcmp( ptr, ";" ) == 0 ) {
Takedefault = TRUE ;
return( CRC ) ;
}

if ( endptr = instrng( ptr, ';' ) )
*endptr = '\0' ;

upcase( ptr ) ;

if ( strcmp( ptr, "CH" ) == 0 ) {
if ( endptr )
Takedefault = TRUE ;
return( CHKSUM ) ;
}

if ( strcmp( ptr, "CR" ) == 0 ) {
if ( endptr )
Takedefault = TRUE ;
return( CRC ) ;
}

if ( cmdfile( fp ) )
return( CMDERR ) ;

/* if none of the above is true, we ask again */
}
}


/* packetsize
*
* DESCRIPTION Ask the packet size to use. If no value is entered,
* 256 bytes/packet is assumed. If an illegal value is
* entered in interactive mode, the user gets another
* chance. In command file mode, errors abort.
*
* RETURNS Packet size, or 0 if error in command file mode.
*/

int packetsize( fp )
FILE *fp ;
{
char *ptr, *endptr, *instrng( ) ;
int size;

for (;;) {

if ( Takedefault )
return( 256 ) ;

/* if nothing is entered, return the default = 256 bytes */
if ( ! ( ptr = input( fp, PKTMSG ) ) )
return( 256 ) ;

if ( strcmp( ptr, ";" ) == 0 ) {
Takedefault = TRUE ;
return( 256 ) ;
}

if ( endptr = instrng( ptr, ';' ) )
*endptr = '\0' ;

if ( ( size = atoi( ptr ) ) && ( size == 32 || size == 64 ||
size == 128 || size == 256 ) ) {
if ( endptr )
Takedefault = TRUE ;
return( size ) ;
}

if ( cmdfile( fp ) )
return( CMDERR ) ;
}
}


/* cziseof
*
* DESCRIPTION Ask whether Ctrl-Z is the end of file on the pc file.
* The default is yes, if the direction of transfer is
* host to pc. If the direction of transfer is pc to host
* the default is no if the file is not text format;
* otherwise it is yes.
*
* RETURNS CZEOF, CZNOTEOF, or CMDERR if error in command file.
*/

char cziseof( fp, direction )
FILE *fp ;
char direction ;
{
char *ptr, *endptr, dfault, *instrng( ) ;
char pcprmpt[ MAXFNAME + 37 ] ;

if ( direction == PCRECV || Textfile )
dfault = CZEOF ;
else
dfault = CZNOTEOF ;

strcpy( pcprmpt, CZMSG ) ;
if ( dfault == CZEOF )
strcat( pcprmpt, "Y>? " ) ;
else
strcat( pcprmpt, "N>? " ) ;

for (;;) {

if ( Takedefault )
return( dfault ) ;

/* if nothing is entered, return the default */
if ( ! ( ptr = input( fp, pcprmpt ) ) )
return( dfault ) ;

if ( strcmp( ptr, ";" ) == 0 ) {
Takedefault = TRUE ;
return( dfault ) ;
}

if ( endptr = instrng( ptr, ';' ) )
*endptr = '\0' ;

upcase( ptr ) ;

if ( strcmp( ptr, "Y" ) == 0 )
return( CZEOF ) ;

if ( strcmp( ptr, "N" ) == 0 )
return( CZNOTEOF ) ;

if ( cmdfile( fp ) )
return( CMDERR ) ;

/* if none of the above is true, we ask again */
}
}

/* collision
*
* DESCRIPTION Ask what to do if the file named on the PC already
* exists on host to pc transfer. Choices are Prompt,
* Overwrite, and Rename. Default is Rename.
*
* RETURNS RENAME, OVERWRITE, PROMPT, or CMDERR if error in
* command file.
*/

char collision( fp, direction )
FILE *fp ;
char direction ;
{
char *ptr, *endptr, *instrng( ) ;

for (;;) {

if ( Takedefault || direction == PCSEND )
return( RENAME ) ;

/* if nothing is entered, return the default */
if ( ! ( ptr = input( fp, COLLMSG ) ) )
return( RENAME ) ;

if ( strcmp( ptr, ";" ) == 0 ) {
Takedefault = TRUE ;
return( RENAME ) ;
}

if ( endptr = instrng( ptr, ';' ) )
*endptr = '\0' ;

upcase( ptr ) ;

if ( strcmp( ptr, "P" ) == 0 )
return( PROMPT ) ;


if ( strcmp( ptr, "O" ) == 0 )
return( OVERWRITE ) ;

if ( strcmp( ptr, "R" ) == 0 )
return( RENAME ) ;

if ( cmdfile( fp ) )
return( CMDERR ) ;

/* if none of the above is true, we ask again */
}
}


/* getpcfile
*
* DESCRIPTION Print the prompt (if interactive mode) given and
* get a file name from the user or the command file.
* The default pc filename is passed from the caller.
* (Static function - called locally, not externally
* available.)
*
* RETURNS Pointer to the file name entered, or NULL if error.
*/

char *getpcfile( fp, name, hostname )
register FILE *fp ;
char *name, *hostname ;
{
char *ptr, *endptr, *instrng( ) ;
char pcprmpt[ MAXFNAME + 37 ] ;

strcpy( pcprmpt, PCMSG ) ;
strcat( pcprmpt, hostname ) ;
strcat( pcprmpt, ">? " ) ;

if ( Takedefault ) {
strcpy( name, hostname ) ;
return( name ) ;
}

if ( ! ( ptr = input( fp, pcprmpt ) ) ) {
strcpy( name, hostname ) ;
return( name );
}

if ( cmdfile( fp ) )
return( (char*) NULL ) ;

if ( strcmp( ptr, ";" ) == 0 ) {
strcpy( name, hostname ) ;
return( name ) ;
}

if ( endptr = instrng( ptr, ';' ) ) {
*endptr = '\0' ;
Takedefault = TRUE ;
}

if ( ptr )
strcpy( name, ptr ) ;
else
strcpy( name, hostname ) ;
return( name ) ;
}

/* getfile
*
* DESCRIPTION Print the prompt (if interactive mode) given and
* get a file name from the user or the command file.
* (Static function - called locally, not externally
* available.)
*
* RETURNS Pointer to the file name entered, or NULL if error.
*/


char *getfile( fp, name, prompt )
register FILE *fp ;
char *name, *prompt ;
{
char *ptr, *endptr, *instrng( ) ;

/* repeat until a non-blank line is entered */
while ( ! ( ptr = input( fp, prompt ) ) ) {
if ( cmdfile( fp ) )
return( (char*) NULL ) ;
}

if ( endptr = instrng( ptr, ';' ) ) {
*endptr = '\0' ;
Takedefault = TRUE ;
}

strcpy( name, ptr ) ;
return( name ) ;
}


/* istext
*
* DESCRIPTION Find out if the file to be transferred is a text
* format file, and thus requires line terminator
* convertsion.
*
* RETURNS 1 if it is text format, 0 if not.
*/

int istext( fp )
FILE *fp ;
{
char *ptr, *endptr, *instrng( ) ;

/* forever - or until the user gets it right! */
for (;;) {
if ( Takedefault )
return( 1 ) ;

if ( ! ( ptr = input( fp, TEXTMSG ) ) )
return( 1 ) ;

upcase( ptr ) ;

if ( strcmp( ptr, ";" ) == 0 ) {
Takedefault = 1 ;
return( 1 ) ;
}

if ( endptr = instrng( ptr, ';' ) ) {
*endptr = '\0' ;
Takedefault = TRUE ;
}

if ( strcmp( ptr, "Y" ) == 0 )
return( 1 ) ;

if ( strcmp( ptr, "N" ) == 0 )
return( 0 ) ;

if ( cmdfile( fp ) )
return( CMDERR ) ;

/* if none of the above is true, we ask again */
}
}


/* compress
*
* DESCRIPTION Remove blanks, tabs and newlines from a string.
*
* RETURNS String length of compressed string.

*/

int compress( buf )
register char *buf ; /* pointer to string to compress */
{
register char c ; /* just for optimization */
char *ptr ; /* pointer to next available address */
char *start ; /* pointer to the given buffer */

start = buf ; /* save a pointer to the buffer address */
ptr = buf ; /* we'll store back into same buffer */

/* copy the string, skipping all whitespace */
while ( c = *buf++ ) {
if ( c != BLANK && c != TAB && c != LF )
*ptr++ = c ;
}

*ptr = '\0' ; /* null terminate the string */

return( ptr - start ) ; /* return count of characters in buffer */
}


/* input
*
* DESCRIPTION If the file pointer is "stdin", indicating an
* interactive situation (not a command file) the
* print the prompt given. In either case, read a
* a line of characters from the given stream and
* return the compressed results.
*
* RETURNS Pointer to local static buffer containing the
* resulting string. Note: the buffer is re-used
* on successive calls. If a read error occurrs or
* no characters remain after compression, then NULL
* is returned.
*/

char *input( fp, prompt )
FILE *fp ;
char *prompt ;
{
static char buf[ MAXINPUT ] ;

if ( interact( fp ) )
printf( prompt ) ;

if ( fgets( buf, MAXINPUT, fp ) == 0 )
return( NULL ) ;

if ( compress( buf ) == 0 )
return( NULL ) ;

return( buf ) ;
}


/* upper
*
* DESCRIPTION Return the upper case version of a character, if
* it is an lower case letter, else return the same
* character.
*
* RETURNS See description.
*
*/

char upper( c )
char c ;
{
return ( c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c ) ;
}


/* upcase
*
* DESCRIPTION Force a string to upper case.
*
* RETURNS None.
*
*/

upcase( str )
register char *str ;
{
while ( *str ) {
*str = upper( *str ) ;
str++ ;
}
}


/* instrng
*
* DESCRIPTION Looks for the first occurance of a given character
* in a string.
*

* RETURNS Returns pointer to the character if
* found, otherwise returns NULL.
*/

char *instrng( strngptr, c )
char *strngptr, c ;
{
do {
if ( *strngptr == c )
return( strngptr ) ;

} while ( *(strngptr++) ) ;

return( NULL ) ;
}



/* yes
*
* DESCRIPTION Print a prompt and wait for a 'y' or 'n'.
*
* RETURNS 1 if user answered 'y', 0 if not.
*/

int yes( fp, prompt )
FILE *fp ;
char *prompt ;
{
char *ptr ;

do {
if ( ptr = input( fp, prompt ) )
upcase( ptr ) ;

} while ( !ptr || strlen( ptr ) != 1 || (*ptr != 'Y' && *ptr != 'N') );

return( *ptr == 'Y' ) ;
}


/* clock
*
* DESCRIPTION Set or reset the alarm clock.
*
* RETURNS None.
*/

clock( secs )
int secs ;
{

/* if we're setting a new timer, reset the Timeout flag */
if ( secs )
Timeout = 0 ;

if ( Debug )
fprintf( Log, "clock(%d)\n", secs ) ;

alarm( (unsigned) secs ) ;
}


/* nextseq
*
* DESCRIPTION Increment the sequence counter, and save the
* current value in "Lastseq". At all times,
* "lastseq" indicates the last good packet sent
* or received, and "Sequence" indicates the next
* value to be used.
*
* RETURNS None.
*/

nextseq()
{

Lastseq = Sequence ;

if ( Sequence == SEQMAX )
Sequence = SEQINIT ;
else
Sequence++ ;
}


/* dump
*
* DESCRIPTION Display the contents of a buffer. (Debugging only)
*
* RETURNS None.
*/

dump( buf, len )
char *buf ;
int len ;
{
char c ;
int col = 0 ;

fprintf( Log, "dump: %d chars\n", len ) ;

if ( ! len )
return ;

while ( len-- ) {
c = *buf++ ;
if ( c >= ' ' && c <= '~' )
fputc( c, Log ) ;
else
fprintf(Log, "<%d>", c ) ;

if ( ++col > 76 ) {
fputc( LF, Log ) ;
col = 0 ;
}
}

fputc( LF, Log ) ;
}



/* wordlen
*
* DESCRIPTION Determine the word size of this machine
*
* RETURNS
*/

int wordlen()
{
int i = 8 ; /* assume at least an 8 bit word ! */

while ( ~0 << i )
i++ ;

return( i ) ;
}




/* vtrans
*
* DESCRIPTION Get file transfer parameters from the given
* input stream (either an interactive user or
* a command file) and initiate the transfer.
*
* RETURNS None.
*/

vtrans( fp )
FILE *fp ;
{
char direction, chktype() ;
char pname[ MAXFNAME + 1 ], hname[ MAXFNAME + 1 ] ;
int ret ;
FILE *file, *hostfile() ;
long bytes ;

/* loop until end of file reached, or EXIT command entered */
while ( ! feof( fp ) ) {

if ( Debug )
fprintf(Log,"top of vtrans() loop\n");

switch ( direction = getmode( fp ) ) {
case EXIT:
quit( 0 ) ;

case PCSEND:
case PCRECV:
Takedefault = FALSE ;

if ( !getfile( fp, hname, HSTMSG) ) {
cmderr( "missing host file name" ) ;
continue ;
}

/* check if host file exists - get file pointer */
if ( ! ( file = hostfile( fp, hname, direction ) ) )
continue ;

Textfile = istext( fp ) ;

break ;

case CMDERR:
continue ;
}

/* if we encounter any error reading a command file
* return to top of loop and search for next good request
*/
if ( getpcfile( fp, pname, hname ) == CMDERR ) {
cmderr( "missing PC file name" ) ;
continue ;
}

/* if we can't set the PC file name, return to top menu */
if ( ! setpcfile( pname ) ) {
puts( "\n*PC FILE NOT SET*.\n" ) ;
continue ;
}

if ( ( Size = packetsize( fp ) ) == 0 ) {
cmderr( "bad packet size specification" ) ;
continue ;
}

if ( ( Checktype = chktype( fp ) ) == CMDERR ) {
cmderr( "bad error check specifier" ) ;
continue ;
}

if ( ( Czeof = cziseof( fp, direction ) ) == CMDERR ) {
cmderr( "bad ctrl-z is eof specifier" ) ;
continue ;
}

if ( ( Filexists = collision( fp, direction ) ) == CMDERR ) {
cmderr( "bad 'if pc file exists' specifier" ) ;
continue ;
}

Checksize = ( Checktype == CRC ? LEN_CRC : LEN_CHK ) ;

/* wait for user response */
if ( ! Takedefault )
input( fp, "\nPRESS TO BEGIN TRANSFER" ) ;

Sequence = SEQINIT ;
Lastseq = SEQMAX ;
bytes = 0 ;

puts( "\nTRANSFER IN PROGRESS ...\n" ) ;
ttysetup() ;

/* send escape sequence to set protocol parameters */
if ( ! setproto( direction ) ) {
ttyreset() ;
puts("\nCan't set protocol - Host aborting.\n") ;
continue ;
}

ret = ( direction == PCSEND ? readfile( file, &bytes ) :
writefile( file, &bytes ) ) ;

if ( Debug )
fprintf( Log, "vtrans: result type %c\n", ret );

ttyreset() ;

sleep( 1 ) ;

fclose( file ) ;

/* What condition did we terminate on? */
switch ( ret ) {
case PKT_ABORT:
puts( "\nPC ABORTING...\n" ) ;
break ;
case PKT_TIMEOUT:
puts( "\nHOST ABORTING.\n" ) ;
break ;
case PKT_EOF:
puts( "\nTRANSFER COMPLETE." ) ;
printf( "TOTAL BYTES TRANSFERRED = %ld\n\n", bytes ) ;
break ;
default:
printf( "vtrans error: got type %c\n", ret ) ;
}
}
}

static long Hostcount = 0 ;
static int Linefeed = 0 ;



/* writefile
*
* DESCRIPTION Send a file to the PC
*
* RETURNS One of PKT_EOF, PKT_TIMEOUT or PKT_ABORT indicating
* good transfer, host abort, or PC abort, respectively.
*/

int writefile( fp, pbytes )
FILE *fp ;
long *pbytes ;
{
char buf[ MAXPKT + 1 ] ;
int len, type ;

/* reset linefeed flag for this file */
Linefeed = 0 ;

/* reset counter of host-file bytes */
Hostcount = 0 ;

if ( Debug )
fprintf(Log,"writefile()\n") ;

for (;;) {
/* the returned length represents buffered bytes.
* the count of original host file bytes is in
* the static variable "Hostcount"
*/
switch ( len = readbuf( fp, buf ) ) {

case 0: /* end of file */
if ( Debug )
fprintf(Log,"writefile: found EOF\n");
*pbytes = Hostcount ;
return( sendeof() ? PKT_EOF : PKT_TIMEOUT ) ;

case -1: /* read error */
if ( Debug )
fprintf(Log,"writefile: read error\n");
send( PKT_ABORT ) ;
return( PKT_TIMEOUT ) ; /* tell vtrans() we quit */

default: /* good data */
if ( ( type = sendbuf( buf, len ) ) != PKT_ACK )
return( type ) ;

/* otherwise, update the sequence counter */
nextseq() ;
}
}
}


/* sendbuf
*
* DESCRIPTION compose and send a packet to the PC
*
* RETURNS PKT_ACK, PKT_ABORT or PKT_TIMEOUT.
*/

int sendbuf( buf, len )
register char *buf ;
int len ;
{
char txbuf[ MAXPKT + 1 ] ;
char *addchk(), *header() ;
int i ;
register char *ptr ;

/* build the header */
ptr = header( txbuf, len ) ;

/* copy the data into the packet */
for ( i=0; i *ptr++ = *buf++ ;

/* add checksum or CRC */
ptr = addchk( txbuf + 1, len + HEADERSIZE - 1 ) ;

/* send the buffer and get a response */
return( trybuf( txbuf, (int) (ptr - txbuf) ) ) ;
}


/* trybuf
*
* DESCRIPTION Send a packet and wait for a response.
* If NAK'd retry up to MAXRETRY times.
*
* RETURNS PKT_ACK, PKT_ABORT or PKT_TIMEOUT.
*/

int trybuf( buf, len )
char *buf ;
int len ;
{
int rxtype ;
int retry = 0 ;

if ( Debug ) {
fprintf(Log, "trybuf:\n" ) ;
dump( buf, len ) ;
}

while ( retry < MAXRETRY ) {

switch ( rxtype = writepkt( buf, len ) ) {
case PKT_ACK:
case PKT_ABORT:
if ( Debug )
fprintf( Log, "trybuf returning type %c\n",
rxtype ) ;
clock( 0 ) ;
return( rxtype ) ;
default: /* including NAK and TIMEOUT */
retry++ ;
if ( Debug )
fprintf(Log,"trybuf: retry = %d\n", retry ) ;
continue ;
}
}

if ( Debug )
fprintf( Log, "exiting trybuf - retry count: %d\n", retry ) ;

/* we tried the max number of times - give up */
clock( 0 ) ;
return( PKT_TIMEOUT ) ;
}


/* readbuf
*
* DESCRIPTION Read up to Size bytes from the host file
* into the buffer given. If we are reading
* a textfile, LF's are translated into CR/LF.
*
* RETURNS return # bytes placed in the buffer, or 0 on
* eof or -1 on read error.
*/

int readbuf( fp, buf )
FILE *fp ;
char *buf ;
{
int c, count = 0 ;

/* if it's not a text file, we do no conversion. Just read! */
if ( ! Textfile ) {
if ( ( count = read( fileno(fp), buf, Size ) ) > 0 )
Hostcount += count ;
return( count ) ;
}

/* see if we reached the end of file on the previous read */
if ( feof( fp ) ) {
if ( Debug )
fprintf(Log,"readbuf - returning EOF\n");
return( 0 ) ;
}

/* load the buffer from the file, converting LF's to CR/LF */
for ( count = 0; count < Size && (c = readc(fp)) != EOF; count++ )
*buf++ = c ;

return( count ) ;
}


/* readc
*
* DESCRIPTION Read a buffer from the host file and convert
* LF's into CR/LF pairs.
*
* RETURNS The character read, or EOF at end of file.
*/

int readc( fp )
FILE *fp ;
{
int c ;

/* if last char was a LF, we returned CR. Now return the LF */
if ( Linefeed ) {
Linefeed = 0 ;
Hostcount++ ; /* count host source bytes! */
return( LF ) ;
}

/* if we get a LF, return a CR/LF pair */
if ( ( c = fgetc( fp ) ) == LF ) {
Linefeed = 1 ;
Hostcount++ ;
return( CR ) ;
}

/* don't count end-of-file! */
if ( c != EOF )
Hostcount++ ;

return( c ) ;
}


/* addchk
*
* DESCRIPTION add the necessary checking bytes to the end of
* the buffer.
*
* RETURNS Pointer to the next available address in buf.
*/

char *addchk( buf, len )
char *buf ;
int len ;
{
char *addcrc(), *addsum() ;

return( Checktype == CRC ? addcrc( buf, len ) : addsum( buf, len ) ) ;
}


/* header
*
* DESCRIPTION Fill in the header for this packet.
*
* RETURNS Pointer to next available address in buffer.
*/

char *header( buf, len )
char *buf ;
int len ;
{

*buf++ = SOH ;
*buf++ = Sequence ;
*buf++ = seqchk( Sequence ) ;
*buf++ = PKT_DATA ;
*buf++ = --len ; /* make length zero-relative */

return( buf ) ;
}


/* writepkt
*
* DESCRIPTION Send a buffer and get a respone.
*
* RETURNS Return one of PKT_ACK, PKT_NAK, or PKT_ABORT.
*/


writepkt( buf, len )
char *buf ;
int len ;
{
int status ;

if ( Debug )
fprintf( Log,"writepkt() writing %d chars\n", len ) ;

/* if we can't write the buffer, return TIMEOUT
* to indicate that the host is quitting.
*/
if ( ( status = write( 1, buf, len ) ) != len ) {
if ( Debug )
fprintf( Log, "write error - status %d\n", status ) ;
return( PKT_TIMEOUT ) ;
}

if ( Debug )
fprintf( Log, "buffer written ok\n" ) ;

/* set 10 second timer for returning ACKs */
clock( ACKWAIT ) ;

/* get and parse a response - return the type */
return( getresp() ) ;
}


/* sendeof
*
* DESCRIPTION Send an EOF packet and wait for an ACK.
*
* RETURNS 1 if EOF packet was ACKed, 0 if not.
*/

sendeof()
{
char buf[ MAXCHECK + 5 ], *addchk() ;

sprintf( buf, "%c%c%c%c", SOH, Sequence, seqchk( Sequence ), PKT_EOF );
addchk( buf + 1, 3 ) ;

return( trybuf( buf, Checksize + 4 ) == PKT_ACK ) ;
}


  3 Responses to “Category : Communication (modem) tools and utilities
Archive   : VTRANS.ZIP
Filename : C8UNIX.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/