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

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


int Debug = 0 ;
int Timeout = 0 ;
char Takedefault = FALSE ;

char Sequence, Lastseq, Checktype, Czeof, Filexists ;
int Textfile, Size, Wordsize, Checksize ;


FILE *Log ;


/* readpkt
*
* DESCRIPTION Try to read a valid buffer sent from VTERM. A
* valid buffer is defined as starting with the SOH
* (start of header) character and ending with the
* TERMINATOR character. All characters until start
* of header (SOH) are ignored.
*
* RETURNS One of: PKT_ERROR, PKT_TIMEOUT, PKT_VALID. The
* length of the received packet is returned in
* "lenptr".
*
*/


int readpkt( buf, lenptr )
char *buf ; /* buffer to read into - must be MAXPKT bytes */
int *lenptr ; /* pointer to integer to return char count */
{
char c ;
int count = 0 ;

if ( Debug )
fprintf(Log,"entering readpkt() loop\n") ;

/* return only on good packet, timeout or buffer overflow */
while ( ! Timeout && count < MAXPKT ) {

switch( read( 0, &c, 1 ) ) {

case 0: /* end-of-file signaled on tty */
if ( Debug )
fprintf(Log,"EOF on tty\n") ;
return( PKT_ERROR ) ;

case -1: /* read error - assume a timeout occurred */
if ( Debug )
fprintf(Log,"read error in readpkt()\n") ;
return( PKT_TIMEOUT ) ;

case 1: /* a character was received - check special chars */
switch( c ) {

case TERMINATOR:
if ( count ) {
buf[ count++ ] = TERMINATOR ;
*lenptr = count ;
if ( Debug )
fprintf(Log,"readpkt() - got %d chars\n", count ) ;
return ( PKT_VALID ) ;
}
break ;

case SOH: /* reset buffer */
buf[ 0 ] = c ;
count = 1 ;
break ;

default: /* ignore chars until SOH */
if ( count )
buf[ count++ ] = c ;
else if ( Debug )
fprintf(Log,"got garbage %c = %d\n",
c, c ) ;
break ;
}
break ;
}
}

/* treat buffer overflow as timeout condition */
return( PKT_TIMEOUT ) ;
}


/* 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 ] ;
int type, len ;

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

/* If we didn't get a valid packet, indicate the host
* is aborting by returning PKT_TIMEOUT. If a valid
* packet was received, parse it and return the status.
*/
if ( readpkt( buf, &len ) != PKT_VALID ) {
if ( Debug )
fprintf(Log,"getresp() - invalid packet\n") ;
return( PKT_TIMEOUT ) ;
}

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

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

case PKT_ABORT:
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 NAK\n");
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 ;
{
char *data, rxbuf[ MAXPKT ] ;
int rxlen, ret, datalen ;

/* 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");

/* If we didn't get a valid packet, reset the timer and
* return PKT_TIMEOUT. If a properly enclosed packet was
* received, parse it and return the status.
*/
if ( readpkt( rxbuf, &rxlen ) != PKT_VALID ) {
clock( 0 ) ;
if ( Debug )
fprintf(Log,"getdata returning TIMEOUT\n") ;
return( PKT_TIMEOUT ) ;
}

/* What did we get? */
switch ( ret = parse( rxbuf, rxlen ) ) {

case PKT_EOF:
send( PKT_ACK ) ;
break ;

case PKT_ABORT:
break ;

case PKT_DATA:
/* de-nibbleize and return just the data */
data = rxbuf + HEADERSIZE ;
datalen = rxlen - HEADERSIZE - Checksize - 1 ;

if ( Debug )
fprintf(Log,"got data : length %d\n", datalen) ;

/* see if denibble() finds an error */
if ( ! (*lenptr = denibble( data, datalen, buf ) ) ) {
if ( Debug )
fprintf(Log,"de-nibble error - NAK\n");
send( PKT_NAK ) ;
continue ;
}

/* we don't send an ACK here so that we can
* write to disk first in readfile(), thus
* providing added pacing control. The ACK
* and sequence number are handled in readfile().
*/
break ;

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

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

clock( 0 ) ;
if ( Debug )
fprintf(Log,"getdata returning type %c\n", ret ) ;
return( ret ) ;
}
}


/* 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, REPEAT 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 buf[ HEADERSIZE + 1 ], seq ;

switch ( type ) {
case PKT_NAK:
/* must flush before sending, since output
* will be flushed also on some systems.
*/
flushin() ;
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, type, TERMINATOR ) ;
sendstr( buf ) ;
}

/* macros to access the individual bytes of a 16 bit word */
#define lowbyte(i) ((i) & 0xff)
#define highbyte(i) lowbyte( (i) >> 8 )

/* macro to maintain 16 bit quantities on > 16 bit machines */
#define low16(i) ((i) & 0xffff)


/* 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 ) ;

/* 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 ;
{
char nibs[ LEN_CRC + 1 ] ;
int i ;

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

/* calculate CRC, nibbleize it and store result in "nibs" */
crcnibble( crc( buf, len ), nibs ) ;

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

if ( Debug ) {
fprintf( Log, "testcrc: tranmitted:" ) ;
for ( i = 0 ; i < LEN_CRC ; i++ )
fputc( buf[i], Log ) ;
fprintf( Log, " computed: %s\n", nibs ) ;
}

/* compare with transmitted CRC */
for ( i = 0 ; i < LEN_CRC ; i++ ) {
if ( nibs[i] != *buf++ ) {
if ( Debug )
fprintf( Log, "testcrc: error\n" ) ;
return( 0 ) ;
}
}

if ( Debug )
fprintf( Log, "testcrc: good crc\n" ) ;

return( 1 ) ;
}


/* crcnibble
*
* DESCRIPTION Convert the value given into hex nibbles
* and store them low byte first in the buffer.
*
* RETURNS None.
*/

crcnibble( value, buf )
unsigned int value ;
char *buf ;
{
/* CRC transmitted low byte first */
sprintf( buf, "%02x", lowbyte ( value ) ) ;
sprintf( buf + 2, "%02x", highbyte( value ) ) ;
upcase( buf ) ;
}


/* testsum
*
* DESCRIPTION Check that the computed checksum 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 testsum( buf, len )
char *buf ;
int len ;
{
char nibs[ LEN_CHK + 1 ], upper() ;
int i ;

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

sprintf( nibs, "%02x", lowbyte( chksum( buf, len ) ) ) ;
upcase( nibs ) ;

/* advance pointer to transmitted checksum */
buf += len ;

if ( Debug ) {
fprintf( Log, "testsum: buf: `%c%c'\n", buf[0], buf[1] );
fprintf( Log, " computed: `%s'\n", nibs ) ;
}

/* compare with transmitted checksum */
for ( i = 0; i < LEN_CHK; i++ ) {
if ( nibs[i] != buf[i] ) {
if ( Debug )
fprintf( Log, "testsum: error\n" ) ;
return( 0 ) ;
}
}

if ( Debug )
fprintf( Log, "testsum: good chksum\n" ) ;

return( 1 ) ;
}


/* 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 ;
{
char tmp[ LEN_CRC + 1 ] ;

crcnibble( crc( buf, len ), tmp ) ;
buf += len ;
strcpy( buf, tmp ) ;
return( buf + LEN_CRC ) ;
}


/* 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 ;
{
char tmp[ LEN_CHK + 1 ] ;

sprintf( tmp, "%02x", lowbyte( chksum( buf, len ) ) ) ;
upcase( tmp ) ;
buf += len ;
strcpy( buf, tmp ) ;
return( buf + 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 ) ;

while ( len-- )
result = (result + *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.
* (the "- 2" is for the Start of header and
* TERMINATOR characters.)
*/
if ( Checktype == CRC )
return( testcrc( buf + 1, len - LEN_CRC - 2 ) ) ;
else
return( testsum( buf + 1, len - LEN_CHK - 2 ) ) ;
}

/* 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 ) ;

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

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

/* 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%c\r", VTRANS7, Size, Checktype,
LF_TERM, Czeof, Filexists, direction ) ;

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

sendstr( buf ) ;

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 ) ;
}


/* getstr
*
* DESCRIPTION Read a CR or LF terminated string.
*
* RETURNS Read status is returned - if the read was good,
* 1 is returned. On error or timeout, returns 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 ) ;
}

#ifdef TYPE2
#include
#include
#include

static struct termio Term, Save ;
#endif

#ifdef TYPE1
#include

static struct sgttyb Term, Save ;
static struct tchars Tchars, Savechars ;
#endif


/* 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()
{
#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, strip characters
* to 7 bits, and use XON/XOFF pacing.
*/
Term.c_iflag = BRKINT | ISTRIP | IXON | IXOFF ;

/* 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: no echo, automatic flow control,
* and return each character as available. Setting CBREAK
* turns off erase and kill processing. Also, don't perform
* any translation on newlines or returns.
*/
Term.sg_flags |= CBREAK | TANDEM ;
Term.sg_flags &= ~ECHO & ~CRMOD ;

/* disable interrupt and quit processing */
ioctl( 0, TIOCGETC, &Savechars ) ;
ioctl( 0, TIOCGETC, &Tchars ) ;
Tchars.t_intrc = -1 ;
Tchars.t_quitc = -1 ;
#endif
}


/* set up terminal for transparent data transfer */

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

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

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, TIOCSETC, &Savechars ) ;
ioctl( 0, TIOCNXCL, 0 ) ; /* restore non-exclusivity */
#endif
}


/* flush the input queue - should be called on sending a NAK */

flushin()
{
#ifdef TYPE2
ioctl( 0, TCFLSH, 0 ) ;
#endif

#ifdef TYPE1
fflush( stdin ) ;
#endif
}

main( argc, argv )
int argc ;
char **argv ;
{
FILE *fp ;

/* print startup banner */
printf( "\n**VTRANS 1.6 -- HOST SOFTWARE FOR " ) ;
printf( "7-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 ) ;
}

/* nibble
*
* DESCRIPTION Break the sequence of "len" bytes starting at "buf"
* into nibbles and store the result starting at "ptr".
*
* RETURNS Return a pointer to the next available location
* in "buf".
*/

char *nibble( buf, len, ptr )
char *buf, *ptr ;
int len ;
{
char tmp[ 3 ] ;

while ( len-- > 0 ) {
/* don't allow sign extension */
sprintf( tmp, "%02x", *buf++ & 0xff ) ;
upcase( tmp ) ;
strcpy( ptr, tmp ) ;
ptr += 2 ;
}

return( ptr ) ;
}


/* denibble
*
* DESCRIPTION Convert "len" nibbleized bytes starting at "buf"
* into single bytes stored starting at "ptr".
*
* RETURNS Return character count if successful conversion,or
* 0 if any non-hex character was found. The count
* reflects the number of bytes after denibbling.
*/

int denibble( buf, len, ptr )
char *buf, *ptr ;
int len ;
{
int hi, lo ;
int save ;

save = len / 2 ;

while ( len > 0 ) {

/* check for valid hex digits */
if ( ( hi = hextoint( *buf++ ) ) < 0 ||
( lo = hextoint( *buf++ ) ) < 0 ) {
return( 0 ) ;
}
*ptr++ = hi * 16 + lo ;
len -= 2 ;
}

return( save ) ;
}


/* hextoint
*
* DESCRIPTION Convert an ascii character representing a
* hexadecimal digit into that digit.
*
* RETURNS The converted digit, if valid, or -1 if not.
*/

static int hextoint( c )
char c ;
{
if ( c >= '0' && c <= '9' )
return( c - '0' ) ;

if ( c >= 'A' && c <= 'F' )
return( c - 'A' + 10 ) ;

if ( c >= 'a' && c <= 'f' )
return( c - 'a' + 10 ) ;

return( -1 ) ; /* not a valid hex digit */
}


/* parse
*
* DESCRIPTION Examine the contents of the buffer "buf" to
* determine the type of message, and whether
* it is valid.
*
* RETURNS PKT_ERROR if an error is found, PKT_NAK, PKT_ABORT,
* PKT_EOF, PKT_DATA, PKT_ACK if valid, or PKT_REPEAT
* if a repeat sequence number is received.
*/

int parse( buf, len )
char *buf ;
int len ;
{
unsigned int crc() ;
char seq, type ;
int count ;

if ( Debug )
dump( buf, len ) ;

/* extract seqence number and buffer type */
seq = buf[ SEQ_POS ] ;
type = buf[ TYPE_POS ] ;

/* check that seqence number is one of the possible values */
if ( seq < SEQINIT || seq > SEQMAX ) {
if ( Debug )
fprintf(Log,"impossible seq: %c\n", buf[1] ) ;
return( PKT_ERROR ) ;
}

/* NAK and ABORT only require a legitimate seq #, not the real one */
if ( type == PKT_NAK || type == PKT_ABORT ) {
if ( Debug )
fprintf(Log,"parse returning type %c\n", type ) ;
return( type ) ;
}

if ( Debug )
fprintf( Log,"Sequence: %c tx'd: %c\n", Sequence, seq ) ;

/* Everything else requires good sequence number */
if ( seq != Sequence ) {

/* Special case: DATA packet with repeat sequence
* number - return PKT_REPEAT so it is ACKED.
*/
if ( seq == Lastseq && type == PKT_DATA )
return( PKT_REPEAT ) ;

if ( Debug )
fprintf(Log,"bad sequence number\n");

return( PKT_ERROR ) ;
}

/* that's enough checking for an ACK */
if ( type == PKT_ACK ) {
if ( Debug )
fprintf(Log,"parse returning ACK\n") ;
return( PKT_ACK ) ;
}

/* For a DATA packet, make sure that the transmitted length
* is numeric, and it corresponds to received character count.
*/
if ( type == PKT_DATA ) {

count = makeint( buf + LEN_POS, LEN_CHARS ) ;

if ( Debug )
fprintf( Log,"count from buffer is %d\n", count ) ;

if ( count < 0 || count != len - HEADERSIZE - Checksize - 2 ) {
if ( Debug )
fprintf(Log,"parse - bad length\n") ;
return( PKT_ERROR ) ;
}
}

/* test the computed check value */
if ( ! testcheck( buf, len ) ) {
if ( Debug )
fprintf(Log,"parse - bad check - returning ERROR\n") ;
return( PKT_ERROR ) ;
}

/* return type of packet. All that's left to do is unload data */
if ( type == PKT_EOF || type == PKT_DATA ) {
if ( Debug )
fprintf(Log,"parse returning type %c\n", type ) ;
return( type ) ;
}

/* catch all garbage */
if ( Debug )
fprintf(Log,"parse - got garbage - returning ERROR\n" ) ;
return( PKT_ERROR ) ;
}

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 ;

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 && c != LF ) {
if ( Debug )
fprintf( Log, "found CR with no LF\n" ) ;
fputc( LF, fp ) ;
}

/* check for CR */
if ( Crfound = ( 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 ;
{
char *ptr, *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. Choices are Prompt, Overwrite, and Rename.
* Default it 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 */
}
}


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 ) ;
}


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++ ;
}
}


/* 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' ) ;
}


/* 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 ) ;
}


/* 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 ;

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 ) ;
}


/* makeint
*
* DESCRIPTION Convert "len" characters from the string pointed
* to by "str" to an positive integer greater than 0.
*
* RETURNS The converted integer is returned, unless an error
* is found in which case -1 is returned.
*/

int makeint( str, len )
char *str ;
int len ;
{
char c, *ptr, buf[ 10 ] ;

/* make sure our buffer is big enough */
if ( len > 9 ) {
if ( Debug )
fprintf(Log,"makeint: len = %d\n", len ) ;
return( -1 ) ;
}

ptr = buf ;

/* make sure they're all digits */
while ( len-- ) {
if ( ( c = *str++ ) < '0' || c > '9' ) {
if ( Debug )
fprintf(Log,"makeint: non-digit: %c\n", c ) ;
return( -1 ) ;
}

*ptr++ = c ;
}
*ptr = 0 ;

if ( Debug )
fprintf( Log, "makeint: converting `%s'\n", buf ) ;

return( atoi( buf ) ) ;
}


/* 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( "\nPC 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 ) ;

/* if interactive - 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() ;

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 ;
static int Linefeed = 0 ;
static int Readsize ;



/* 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 ;

/* set byte count to zero */
Hostcount = 0 ;

/* calculate the max. number of bytes to read from the file */
Readsize = Size / 2 ;

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 )
char *buf ;
int len ;
{
char txbuf[ MAXPKT + 1 ] ;
char *ptr, *addchk(), *header() ;

/* compose the packet */
ptr = header( txbuf, len ) ; /* build header */
nibble( buf, len, ptr ) ; /* nibbleized the data */

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

*ptr++ = TERMINATOR ; /* add newline */

/* 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: len = %d\n", len ) ;

while ( retry < MAXRETRY ) {

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

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: /* everything else is considered a NAK */
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 Readsize 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, Readsize ) ) > 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 < Readsize && (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
*
* RETURNS
*/

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

*buf++ = SOH ;
*buf++ = Sequence ;
*buf++ = PKT_DATA ;
sprintf( buf, "%03d", len * 2 - 1 ) ;

return( buf + 3 ) ;
}


/* 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 ;
{
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 ( write( 1, buf, len ) != len ) {
if ( Debug )
fprintf( Log, "writepkt: returning TIMEOUT\n");
return( PKT_TIMEOUT ) ;
}

/* 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 *ptr, buf[ MAXCHECK + 4 ], *addchk() ;

sprintf( buf, "%c%c%c", SOH, Sequence, PKT_EOF ) ;
ptr = addchk( buf + 1, 2 ) ;
*ptr = TERMINATOR ;

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


  3 Responses to “Category : Communication (modem) tools and utilities
Archive   : VTRANS.ZIP
Filename : C7UNIX.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/