#ifndef lint
static const char rcsid[] = "$Id: zmutil.c,v 1.1.1.1 2001/03/08 00:01:48 efalk Exp $" ;
#endif

/*
 * Copyright (c) 1995 by Edward A. Falk
 */


/**********
 *
 *
 *	@@@@@  @   @  @   @  @@@@@   @@@   @      
 *	   @   @@ @@  @   @    @      @    @      
 *	  @    @ @ @  @   @    @      @    @      
 *	 @     @ @ @  @   @    @      @    @      
 *	@@@@@  @ @ @   @@@     @     @@@   @@@@@  
 *
 *	ZMUTIL - utilties used by zmodem protocol.
 *
 *	Routines provided here:
 *
 *
 *	int ZXmitHdrHex(type, data, info)
 *		int	type ;
 *		u_char	data[4] ;
 *		ZModem	*info ;
 *
 *	transmit zmodem header in hex.
 *
 *
 *	int ZXmitHdrBin(type, data, info)
 *		int	type ;
 *		u_char	data[4] ;
 *		ZModem	*info ;
 *
 *		transmit zmodem header in binary.
 *
 *
 *	int ZXmitHdrBin32(type, data, info)
 *		int	type ;
 *		u_char	data[4] ;
 *		ZModem	*info ;
 *
 *		transmit zmodem header in binary with 32-bit crc.
 *
 *
 *	int ZXmitHdr(type, format, data, info)
 *		int	type, format ;
 *		u_char	data[4] ;
 *		ZModem	*info ;
 *
 *		transmit zmodem header
 *
 *
 *	int ZXmitData(format, data, len, term, info)
 *		int	format, len ;
 *		u_char	term ;
 *		u_char	*data ;
 *		ZModem	*info ;
 *
 *		transmit buffer of data.
 *
 *
 *	u_long FileCrc(name)
 *		char	*name ;
 *
 *		compute 32-bit crc for a file, returns 0 on not found
 *
 *
 *	u_char	*ZEnc4(n)
 *		u_long	n ;
 *
 *		convert u_long to 4 bytes.
 *
 *	u_long ZDec4(buf)
 *		u_char	buf[4] ;
 *
 *		convert 4 bytes to u_long
 *
 *
 *
 *	Edward A. Falk
 *
 *	January, 1995
 *
 *
 *
 **********/





#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>

#include "zmodem.h"
#include "crctab.h"


static	char	hexChars[] = "0123456789abcdef" ;

extern	char	*hdrnames[] ;

	FILE	*zmodemlogfile = NULL ;


	/* put a number as two hex digits */

static	u_char *
putHex( u_char *ptr, u_char c )
{
	*ptr++ = hexChars[(c>>4)&0xf] ;
	*ptr++ = hexChars[c&0xf] ;
	return ptr ;
}


	/* put a number with ZDLE escape if needed */

u_char *
putZdle( register u_char *ptr, register u_char c, register ZModem *info )
{
register u_char	c2 = c & 0177 ;

	if( c == ZDLE || c2 == 020 || c2 == 021 || c2 == 023 ||
	    c2 == 0177  ||  (c2 == 015 && info->atSign)  ||
#ifdef	COMMENT
	    c2 == 035  ||  (c2 == '~' && info->lastCR)  ||
#endif	/* COMMENT */
	    c2 == 035  ||
	    (c2 < 040 && info->escCtrl) )
	{
	  *ptr++ = ZDLE ;
	  if( c == 0177 )
	    *ptr = ZRUB0 ;
	  else if( c == 0377 )
	    *ptr = ZRUB1 ;
	  else
	    *ptr = c^0100 ;
	}
	else
	  *ptr = c ;

	info->atSign = c2 == '@' ;
	info->lastCR = c2 == '\r' ;

	return ++ptr ;
}


int
ZXmitHdrHex( int type, u_char data[4], ZModem *info )
{
	u_char	buffer[128] ;
register u_char	*ptr = buffer ;
register u_int	crc ;
	int	i ;

	zmodemlog("sending  %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
	    hdrnames[type], data[0], data[1], data[2], data[3],
	    ZDec4(data)) ;

	*ptr++ = ZPAD ;
	*ptr++ = ZPAD ;
	*ptr++ = ZDLE ;
	*ptr++ = ZHEX ;

	ptr = putHex(ptr, type) ; crc = updcrc(type, 0) ;
	for( i=4; --i >= 0; ++data ) {
	  ptr = putHex(ptr, *data) ;
	  crc = updcrc(*data, crc) ;
	}
	crc = updcrc(0,crc) ; crc = updcrc(0,crc) ;
	ptr = putHex(ptr, (crc>>8)&0xff) ;
	ptr = putHex(ptr, crc&0xff) ;
	*ptr++ = '\r' ;
	*ptr++ = '\n' ;
	if( type != ZACK  &&  type != ZFIN )
	  *ptr++ = XON ;

	return ZXmitStr(buffer, ptr-buffer, info) ;
}


int
ZXmitHdrBin( int type, u_char data[4], register ZModem *info )
{
	u_char	buffer[128] ;
register u_char	*ptr = buffer ;
register u_int	crc ;
	int	len ;

	zmodemlog("sending  %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
	    hdrnames[type], data[0], data[1], data[2], data[3],
	    ZDec4(data)) ;

	*ptr++ = ZPAD ;
	*ptr++ = ZDLE ;
	*ptr++ = ZBIN ;

	ptr = putZdle(ptr, type, info) ; crc = updcrc(type, 0) ;
	for( len=4; --len >= 0; ++data ) {
	  ptr = putZdle(ptr, *data, info) ;
	  crc = updcrc(*data, crc) ;
	}
	crc = updcrc(0,crc) ; crc = updcrc(0,crc) ;
	ptr = putZdle(ptr, (crc>>8)&0xff, info) ;
	ptr = putZdle(ptr, crc&0xff, info) ;

	len = ptr-buffer ;
	return ZXmitStr(buffer, len, info) ;
}


int
ZXmitHdrBin32( int type, u_char data[4], ZModem *info )
{
	u_char	buffer[128] ;
register u_char	*ptr = buffer ;
register u_long	crc ;
	int	len ;

	zmodemlog("sending  %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
	    hdrnames[type], data[0], data[1], data[2], data[3],
	    ZDec4(data)) ;

	*ptr++ = ZPAD ;
	*ptr++ = ZDLE ;
	*ptr++ = ZBIN32 ;
	ptr = putZdle(ptr, type, info) ; crc = UPDC32(type, 0xffffffffL) ;
	for( len=4; --len >= 0; ++data ) {
	  ptr = putZdle(ptr, *data, info) ;
	  crc = UPDC32(*data, crc) ;
	}
	crc = ~crc ;
	for(len=4; --len >= 0; crc >>= 8)
	  ptr = putZdle(ptr, crc&0xff, info) ;

	len = ptr-buffer ;
	return ZXmitStr(buffer, len, info) ;
}


int
ZXmitHdr( int type, int format, u_char data[4], ZModem *info)
{
	if( format == ZBIN && info->crc32 )
	  format = ZBIN32 ;

	switch( format ) {
	  case ZHEX:
	    return ZXmitHdrHex(type, data, info) ;

	  case ZBIN:
	    return ZXmitHdrBin(type, data, info) ;

	  case ZBIN32:
	    return ZXmitHdrBin32(type, data, info) ;

	  default:
	    return 0 ;
	}
}




	/* TODO: if input is not a file, need to keep old data
	 * for possible retransmission */

int
ZXmitData( int format, int len, u_char term, u_char *data, ZModem *info)
{
register u_char	*ptr = info->buffer ;
register u_int	crc ;

	if( format == ZBIN && info->crc32 )
	  format = ZBIN32 ;

	zmodemlog("ZXmiteData: fmt=%c, len=%d, term=%c\n", format, len, term) ;

	crc = (format == ZBIN) ? 0 : 0xffffffff ;

	while( --len >= 0 ) {
	  if( format == ZBIN )
	    crc = updcrc(*data, crc) ;
	  else
	    crc = UPDC32(*data, crc) ;
	  ptr = putZdle(ptr, *data++, info) ;
	}

	*ptr++ = ZDLE ;
	if( format == ZBIN )
	  crc = updcrc(term, crc) ;
	else
	  crc = UPDC32(term, crc) ;
	*ptr++ = term ;
	if( format == ZBIN ) {
	  crc = updcrc(0,crc) ; crc = updcrc(0,crc) ;
	  ptr = putZdle(ptr, (crc>>8)&0xff, info) ;
	  ptr = putZdle(ptr, crc&0xff, info) ;
	}
	else {
	  crc = ~crc ;
	  for(len=4; --len >= 0; crc >>= 8)
	    ptr = putZdle(ptr, crc&0xff, info) ;
	}

	return ZXmitStr(info->buffer, ptr-info->buffer, info) ;
}


	/* compute 32-bit crc for a file, returns 0 on not found */

u_long
FileCrc( char *name )
{
	u_long	crc ;
	FILE	*ifile = fopen(name, "r") ;
	int	i ;

	if( ifile == NULL )	/* shouldn't happen, since we did access(2) */
	  return 0 ;

	crc = 0xffffffff ;

	while( (i=fgetc(ifile)) != EOF )
	  crc = UPDC32(i, crc) ;

	fclose(ifile) ;
	return ~crc ;
}


u_char	*
ZEnc4( u_long n )
{
static	u_char	buf[4] ;
	buf[0] = n&0xff ; n >>= 8 ;
	buf[1] = n&0xff ; n >>= 8 ;
	buf[2] = n&0xff ; n >>= 8 ;
	buf[3] = n&0xff ;
	return buf ;
}

u_long
ZDec4( u_char buf[4] )
{
	return buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24) ;
}


char *
sname2(ZMState state)
{
	static char *names[] = {
	  "RStart", "RSinitWait", "RFileName", "RCrc", "RFile", "RData",
	  "RDataErr", "RFinish", "TStart", "TInit", "FileWait", "CrcWait",
	  "Sending", "SendWait", "SendDone", "SendEof", "TFinish",
	  "CommandData", "CommandWait", "StderrData", "Done", "YTStart",
	  "YTFile", "YTDataWait", "YTData", "YTEOF", "YTFin", "YRStart",
	  "YRDataWait", "YRData", "YREOF"} ;

	return names[(int)state] ;

}


char *
sname(ZModem *info)
{
	return sname2(info->state) ;
}



#ifdef	DEBUG
void
zmodemlog(const char *fmt, ... )
{
	va_list ap;
	struct timeval tv ;
	struct tm *tm ;
static	int	do_ts = 1 ;

	if( zmodemlogfile == NULL )
	  return ;

	if( do_ts ) {
	  gettimeofday(&tv, NULL) ;
	  tm = localtime(&tv.tv_sec) ;
	  fprintf(zmodemlogfile, "%2.2d:%2.2d:%2.2d.%2.2ld: ",
	    tm->tm_hour, tm->tm_min, tm->tm_sec,
	    tv.tv_usec/10000) ;
	}
	do_ts = strchr(fmt, '\n') != NULL ;

	va_start(ap, fmt) ;
	vfprintf(zmodemlogfile, fmt, ap) ;
	va_end(ap) ;
}
#endif	/* DEBUG */