This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
magicka/Xmodem/main.c

914 lines
18 KiB
C
Raw Normal View History

2016-04-02 23:26:17 +00:00
#ifndef lint
static const char rcsid[] = "$Id: main.c,v 1.1.1.1 2001/03/08 00:01:48 efalk Exp $" ;
#endif
/*
* Copyright (c) 1995 by Edward A. Falk
*/
/**********
*
*
* @ @ @@@ @@@ @ @
* @@ @@ @ @ @ @@ @
* @ @ @ @@@@@ @ @ @ @
* @ @ @ @ @ @ @ @@
* @ @ @ @ @ @@@ @ @
*
* MAIN - demonstration zmodem program
*
* This program implements the x,y,z-modem protocols, using the
* zmodem library.
*
* Edward A. Falk
*
* Jan, 1995
*
*
*
**********/
static char usage [] =
"usage: zmodem -r [options]\n\
zmodem -s [options] file [file ...]\n\
-r receive files\n\
-s send files\n\
- file allows you to transmit files with '-' in their names\n\
-v verbose, more v's increase messages.\n\
-l /dev/ttyX use specified serial port instead of /dev/tty\n\
-b baud set baud rate\n\
-x file use xmodem, specify filename\n\
-y use ymodem\n\
-k use 1k, packets (ymodem, xmodem)\n\
-win nn set sliding window to nn bytes (send)\n\
-buf nn set input buffer size to nn bytes (receive)\n\
-L nn set packet length\n\
-e escape control characters\n\
-a ascii text\n\
-i image (binary data)\n\
-resume continue an interrupted transfer\n\
-new transfer only if newer\n\
-newl transfer only if newer or longer\n\
-diff transfer only if dates or lengths differ\n\
-crc transfer only if length or crc different\n\
-apnd append to existing file\n\
-clob force overwrite of existing files\n\
-prot do not overwrite existing files\n\
-chng change filename if destination exists\n\
-noloc do not transfer unless destination exists\n\
-[hq] this list\n" ;
#include <stdio.h>
/****
*
* Constants, typedefs, externals, globals, statics, macros, block data
*
****/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/termios.h>
#include <sys/param.h>
#include "zmodem.h"
#include "seriallog.h"
extern int errno ;
extern char *getenv() ;
/* compile-time parameters */
#define ESCCTRL 0 /* control characters need to be escaped */
#if !defined(CRTSCTS) && defined(CNEW_RTSCTS)
#define CRTSCTS CNEW_RTSCTS
#endif
static int baud = 0 ; /* requested baud rate */
static char *line = NULL ; /* requested serial line */
static int verbose = 0 ;
static int send = 0 ; /* send files */
static int receive = 0 ; /* receive */
static int xmodem = 0 ; /* use xmodem */
static int ymodem = 0 ; /* use ymodem */
static int escCtrl = ESCCTRL ;
static int ascii = 0 ; /* translate line endings */
static int binary = 0 ; /* don't translate line endings */
static int resume = 0 ; /* resume interrupted transfers */
static int xferType = 0 ; /* new,newl,diff,crc, etc. */
static int noloc = 0 ;
static int doCancel = 0 ;
static int doLogging = 0 ;
static int Corrupt = 0 ; /* deliberately corrupt data */
static int fileErrs ; /* used to track errors per file */
static int fileSent ; /* track amount sent */
static ZModem info ;
static int begun = 0 ;
static struct termios old_settings, new_settings ;
static int FinishXmit(ZModem *info) ;
static int DoReceive(ZModem *info) ;
static int InitXmit(ZModem *info) ;
static int XmitFile(char *filename, int f0, int f1, ZModem *info) ;
static void SendFile() ;
static void RcvFiles() ;
static void RcvXmodem() ;
static int getBaud() ;
static void resetCom() ;
static char *basename(char *name) ;
#ifdef SVr4
static void sighandle(int) ;
#else
static void sighandle() ;
#endif
extern FILE *SerialLogFile ;
extern FILE *zmodemlogfile ;
int
main(int argc, char **argv)
{
char *progname ;
#ifdef COMMENT
printf("%d\n", getpid()) ;
#endif /* COMMENT */
info.ifd = info.ofd = -1 ;
info.zrinitflags = 0 ;
info.zsinitflags = 0 ;
info.attn = NULL ;
info.packetsize = 0 ;
info.windowsize = 0 ;
info.bufsize = 0 ;
progname = basename(argv[0]) ;
if( strcmp(progname,"rz") == 0 )
receive = 1 ;
else if( strcmp(progname, "sz") == 0 )
send = 1 ;
++argv, --argc ; /* skip program name */
/* parse options */
for(; argc > 0 && **argv == '-'; ++argv, --argc )
{
if( strcmp(*argv, "-r") == 0 )
receive = 1 ;
else if( strcmp(*argv, "-s") == 0 )
send = 1 ;
else if( strncmp(*argv, "-v", 2) == 0 )
verbose += strlen(*argv)+1 ;
else if( strcmp(*argv, "-l") == 0 && --argc > 0 )
line = *++argv ;
else if( strcmp(*argv, "-b") == 0 && --argc > 0 )
baud = getBaud(atoi(*++argv)) ;
else if( strcmp(*argv, "-x") == 0 )
xmodem = 1 ;
else if( strcmp(*argv, "-y") == 0 )
ymodem = 1 ;
else if( strcmp(*argv, "-win") == 0 && --argc > 0 )
info.windowsize = atoi(*++argv) ;
else if( strcmp(*argv, "-buf") == 0 && --argc > 0 )
info.bufsize = atoi(*++argv) ;
else if( strcmp(*argv, "-L") == 0 && --argc > 0 )
info.packetsize = atoi(*++argv) ;
else if( strcmp(*argv, "-k") == 0 )
info.packetsize = 1024 ;
else if( strcmp(*argv, "-e") == 0 )
escCtrl = ESCCTL ;
else if( strcmp(*argv, "-a") == 0 )
ascii = 1 ;
else if( strcmp(*argv, "-i") == 0 )
binary = 1 ;
else if( strcmp(*argv, "-resume") == 0 )
resume = 1 ;
else if( strcmp(*argv, "-new") == 0 )
xferType = ZMNEW ;
else if( strcmp(*argv, "-newl") == 0 )
xferType = ZMNEWL ;
else if( strcmp(*argv, "-diff") == 0 )
xferType = ZMDIFF ;
else if( strcmp(*argv, "-crc") == 0 )
xferType = ZMCRC ;
else if( strcmp(*argv, "-apnd") == 0 )
xferType = ZMAPND ;
else if( strcmp(*argv, "-clob") == 0 )
xferType = ZMCLOB ;
else if( strcmp(*argv, "-prot") == 0 )
xferType = ZMPROT ;
else if( strcmp(*argv, "-chng") == 0 )
xferType = ZMCHNG ;
else if( strcmp(*argv, "-noloc") == 0 )
noloc = ZMSKNOLOC ;
else if( strcmp(*argv, "-h") == 0 ||
strcmp(*argv, "-q") == 0 )
{
fprintf(stderr, usage) ;
exit(0) ;
}
else if( strcmp(*argv, "-corrupt") == 0 )
Corrupt = 1 ;
else if( strcmp(*argv, "-log") == 0 )
doLogging = 1 ;
else if( strcmp(*argv, "-") == 0 ) {
++argv, --argc ; break ;
}
else {
fprintf(stderr, "unknown argument '%s' or missing value\n%s",
*argv, usage) ;
exit(2) ;
}
}
if( doLogging ) {
if( (SerialLogFile = fopen("xfer.log", "w")) == NULL )
perror("xfer.log") ;
if( (zmodemlogfile = fopen("zmodem.log","w")) == NULL )
perror("zmodem.log") ;
}
if( send ) {
for(; argc > 0; ++argv, --argc ) /* process filenames */
SendFile(*argv) ;
FinishXmit(&info) ;
}
else if( receive )
{
if( !xmodem )
RcvFiles() ;
else
for(; argc > 0; ++argv, --argc ) /* process filenames */
RcvXmodem(*argv) ;
}
else {
fprintf(stderr,
"either -s (send) or -r (receive) must be specified\n%s", usage) ;
exit(2) ;
}
resetCom() ;
exit(0) ;
}
static void
sighandle(int arg)
{
doCancel = 1 ;
}
static void
openCom()
{
char *ptr ;
if( line == NULL )
line = getenv("RZSZLINE") ;
if( baud == 0 && (ptr = getenv("RZSZBAUD")) != NULL )
baud = getBaud(atoi(ptr)) ;
if( line == NULL ) {
info.ifd = 0 ;
info.ofd = 1 ;
}
else if( (info.ifd = info.ofd = open(line, O_RDWR)) == -1 ) {
fprintf(stderr,
"cannot open %s, %s, use \"zmodem -h\" for more info\n",
line, strerror(errno)) ;
exit(2) ;
}
/* assumption: setting attributes for one half of channel will
* change both halves. This fails if different physical
* devices are used.
*/
tcgetattr(info.ifd,&old_settings) ;
new_settings = old_settings ;
#ifdef IUCLC
new_settings.c_iflag &=
~(ISTRIP|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXOFF|IMAXBEL) ;
#else
new_settings.c_iflag &=
~(ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXOFF|IMAXBEL) ;
#endif
new_settings.c_oflag &= ~OPOST ;
new_settings.c_cflag &= ~(CSIZE|PARENB) ;
new_settings.c_cflag |= CS8 ;
if( baud != 0 ) {
cfsetospeed(&new_settings, baud) ;
cfsetispeed(&new_settings, baud) ;
}
else
baud = cfgetospeed(&old_settings) ;
new_settings.c_lflag = 0 ;
new_settings.c_cc[VMIN] = 32 ;
new_settings.c_cc[VTIME] = 1 ;
tcsetattr(info.ifd,TCSADRAIN, &new_settings) ;
info.zrinitflags = CANFDX|CANOVIO|CANBRK|CANFC32|escCtrl ;
info.zsinitflags = escCtrl ;
if( info.packetsize == 0 ) {
if( xmodem || ymodem )
info.packetsize = 128 ;
/* TODO: what about future high-speed interfaces? */
else if( baud < B2400 )
info.packetsize = 256 ;
else if( baud == B2400 )
info.packetsize = 512 ;
else
info.packetsize = 1024 ;
}
}
static void
resetCom()
{
tcsetattr(info.ifd,TCSADRAIN, &old_settings) ;
}
static void
SendFile(char *filename)
{
int f0, f1 ;
fileErrs = 0 ;
if( !begun ) /* establish connection */
{
openCom() ;
signal(SIGINT, sighandle) ;
signal(SIGTERM, sighandle) ;
signal(SIGHUP, sighandle) ;
if( InitXmit(&info) ) {
fprintf(stderr, "connect failed\n") ;
resetCom() ;
exit(1) ;
}
begun = 1 ;
}
if( ascii )
f0 = ZCNL ;
else if( binary )
f0 = ZCBIN ;
else if( resume )
f0 = ZCRESUM ;
else
f0 = 0 ;
f1 = xferType | noloc ;
if( XmitFile(filename, f0,f1, &info) ) {
fprintf(stderr, "connect failed\n") ;
resetCom() ;
exit(1) ;
}
}
static void
RcvFiles()
{
openCom() ;
signal(SIGINT, sighandle) ;
signal(SIGTERM, sighandle) ;
if( DoReceive(&info) ) {
fprintf(stderr, "connect failed\n") ;
resetCom() ;
exit(1) ;
}
}
static void
RcvXmodem(char *filename)
{
/* TODO: */
}
static int
getBaud(int b)
{
switch(b) {
case 50: return B50 ;
case 75: return B75 ;
case 110: return B110 ;
case 134: return B134 ;
case 150: return B150 ;
case 200: return B200 ;
case 300: return B300 ;
case 600: return B600 ;
case 1200: return B1200 ;
case 1800: return B1800 ;
case 2400: return B2400 ;
case 4800: return B4800 ;
case 9600: return B9600 ;
case 19200: return B19200 ;
case 38400: return B38400 ;
default: return 0 ;
}
}
/* TEST: randomly corrupt every 1000th byte */
static void
corrupt(u_char *buffer, int len)
{
extern double drand48() ;
while( --len >= 0 ) {
if( drand48() < 1./1000. )
*buffer ^= 0x81 ;
++buffer ;
}
}
static int
doIO(ZModem *info)
{
fd_set readfds ;
struct timeval timeout ;
int i ;
int len ;
u_char buffer[1024] ;
int done = 0 ;
while(!done)
{
FD_ZERO(&readfds) ;
FD_SET(info->ifd, &readfds) ;
timeout.tv_sec = info->timeout ;
timeout.tv_usec = 0 ;
i = select(info->ifd+1, &readfds,NULL,NULL, &timeout) ;
if( doCancel ) {
ZmodemAbort(info) ;
fprintf(stderr, "cancelled by user\n") ;
resetCom() ;
exit(3) ;
}
if( i<0 )
perror("select") ;
else if( i==0 )
done = ZmodemTimeout(info) ;
else {
len = read(info->ifd, buffer, sizeof(buffer)) ;
if( Corrupt )
corrupt(buffer, len) ;
if( SerialLogFile != NULL )
SerialLog(buffer, len, 1) ;
done = ZmodemRcv(buffer, len, info) ;
}
}
if( SerialLogFile != NULL )
SerialLogFlush() ;
return done ;
}
static int
InitXmit(ZModem *info)
{
int done ;
if( xmodem )
done = XmodemTInit(info) ;
else if( ymodem )
done = YmodemTInit(info) ;
else
done = ZmodemTInit(info) ;
if( !done )
done = doIO(info) ;
return done != ZmDone ;
}
static int
XmitFile(char *filename, int f0, int f1, ZModem *info)
{
int done ;
done = ZmodemTFile(filename,filename, f0,f1,0,0,
0,0, info) ;
switch( done ) {
case 0:
if( verbose )
fprintf(stderr, "Sending: \"%s\"\n", filename) ;
break ;
case ZmErrCantOpen:
fprintf(stderr, "cannot open file \"%s\": %s\n",
filename, strerror(errno)) ;
return 0 ;
case ZmFileTooLong:
fprintf(stderr, "filename \"%s\" too long, skipping...\n",
filename) ;
return 0 ;
case ZmDone:
return 0 ;
default:
return 1 ;
}
if( !done )
done = doIO(info) ;
return done != ZmDone ;
}
static int
FinishXmit(ZModem *info)
{
int done ;
done = ZmodemTFinish(info) ;
if( !done )
done = doIO(info) ;
return done != ZmDone ;
}
static int
DoReceive(ZModem *info)
{
int done ;
if( ymodem )
done = YmodemRInit(info) ;
else
done = ZmodemRInit(info) ;
if( !done )
done = doIO(info) ;
return done != ZmDone ;
}
int
ZXmitStr(u_char *buffer, int len, ZModem *info)
{
u_char b2[1024] ;
/* TEST: randomly corrupt every 1000th byte */
if( Corrupt )
{
bcopy(buffer, b2, len) ;
corrupt(b2, len) ;
buffer = b2 ;
}
if( SerialLogFile != NULL )
SerialLog(buffer, len, 0) ;
if( write(info->ofd, buffer, len) != len )
return ZmErrSys ;
return 0 ;
}
void
ZIFlush(ZModem *info)
{
if( SerialLogFile != NULL )
SerialLogFlush() ;
if( tcflush(info->ifd, TCIFLUSH) != 0 )
perror("TCIFLUSH") ;
}
void
ZOFlush(ZModem *info)
{
if( SerialLogFile != NULL )
SerialLogFlush() ;
if( tcflush(info->ifd, TCOFLUSH) != 0 )
perror("TCOFLUSH") ;
}
void
ZStatus(int type, int j, char *str)
{
switch( type ) {
case RcvByteCount:
if( verbose >= 2 )
fprintf(stderr, "received: %6d bytes\n", j) ;
break ;
case SndByteCount:
fileSent = j ;
if( verbose >= 2 )
fprintf(stderr, "sent: %6d bytes\n", j) ;
break ;
case RcvTimeout:
if( verbose )
fprintf(stderr, "receiver timeouts: %2d\n", j) ;
break ;
case SndTimeout:
if( verbose )
fprintf(stderr, "sender timeouts: %2d\n", j) ;
break ;
case RmtCancel:
fprintf(stderr, "transfer cancelled by remote\n") ;
break ;
case ProtocolErr:
if( verbose >= 3 )
fprintf(stderr, "protocol error: %2.2d\n", j) ;
break ;
case RemoteMessage:
fprintf(stderr, "remote message: %s\n",str) ;
break ;
case DataErr:
if( verbose >= 3 )
fprintf(stderr, "data errors: %2d\n", j) ;
#ifdef COMMENT
if( ++fileErrs > 20 )
{
/* something's wrong */
ZmodemAbort(&info) ;
}
#endif /* COMMENT */
break ;
case FileErr:
fprintf(stderr, "cannot write file: %s\n", strerror(errno)) ;
break ;
}
}
int
ZAttn(ZModem *info)
{
char *ptr ;
if( info->attn == NULL )
return 0 ;
for(ptr = info->attn; *ptr != '\0'; ++ptr) {
if( *ptr == ATTNBRK )
tcsendbreak(info->ifd, 0) ;
else if( *ptr == ATTNPSE )
sleep(1) ;
else
write(info->ifd, ptr, 1) ;
}
return 0 ;
}
FILE *
ZOpenFile(char *name, u_long crc, ZModem *info)
{
struct stat buf ;
int exists ; /* file already exists */
static int changeCount = 0 ;
char name2[MAXPATHLEN] ;
int apnd = 0 ;
int f0,f1 ;
FILE *rval ;
/* TODO: if absolute path, do we want to allow it?
* if relative path, do we want to prepend something?
*/
if( *name == '/' ) /* for now, disallow absolute paths */
return NULL ;
if( stat(name, &buf) == 0 )
exists = 1 ;
else if( errno == ENOENT )
exists = 0 ;
else
return NULL ;
/* if remote end has not specified transfer flags, we can
* accept the local definitions
*/
if( (f0=info->f0) == 0 ) {
if( ascii )
f0 = ZCNL ;
else if( binary )
f0 = ZCBIN ;
else if( resume )
f0 = ZCRESUM ;
else
f0 = 0 ;
}
if( (f1=info->f1) == 0 )
f1 = xferType | noloc ;
if( f0 == ZCRESUM ) { /* if exists, and we already have it, return */
if( exists && buf.st_size == info->len )
return NULL ;
apnd = 1 ;
}
/* reject if file not found and it most be there (ZMSKNOLOC) */
if( !exists && (f1 & ZMSKNOLOC) )
return NULL ;
switch( f1 & ZMMASK ) {
case 0: /* Implementation-dependent. In this case, we
* reject if file exists (and ZMSKNOLOC not set) */
if( exists && !(f1 & ZMSKNOLOC) ) {
fprintf(stderr, "%s already exists\n", name) ;
return NULL ;
}
break ;
case ZMNEWL: /* take if newer or longer than file on disk */
if( exists && info->date <= buf.st_mtime &&
info->len <= buf.st_size ) {
fprintf(stderr, "%s already exists\n", name) ;
return NULL ;
}
break ;
case ZMCRC: /* take if different CRC or length */
if( exists && info->len == buf.st_size && crc == FileCrc(name) )
{
fprintf(stderr, "%s already exists\n", name) ;
return NULL ;
}
break ;
case ZMAPND: /* append */
apnd = 1 ;
case ZMCLOB: /* unconditional replace */
break ;
case ZMNEW: /* take if newer than file on disk */
if( exists && info->date <= buf.st_mtime ) {
fprintf(stderr, "%s already exists and is newer\n", name) ;
return NULL ;
}
break ;
case ZMDIFF: /* take if different date or length */
if( exists && info->date == buf.st_mtime &&
info->len == buf.st_size )
{
fprintf(stderr, "%s already exists\n", name) ;
return NULL ;
}
break ;
case ZMPROT: /* only if dest does not exist */
if( exists )
{
fprintf(stderr, "%s already exists\n", name) ;
return NULL ;
}
break ;
case ZMCHNG: /* invent new filename if exists */
if( exists ) {
while( exists ) {
sprintf(name2, "%s_%d", name, changeCount++) ;
exists = stat(name2, &buf) == 0 || errno != ENOENT ;
}
name = name2 ;
}
break ;
}
/* here if we've decided to accept */
#ifdef COMMENT
if( exists && !apnd && unlink(name) != 0 )
return NULL ;
#endif /* COMMENT */
/* TODO: build directory path if needed */
if( verbose )
fprintf(stderr, "Receiving: \"%s\"\n", name) ;
rval = fopen(name, apnd ? "a" : "w") ;
if( rval == NULL )
perror(name) ;
return rval ;
}
int
ZWriteFile(u_char *buffer, int len, FILE *file, ZModem *info)
{
/* TODO: if ZCNL set in info->f0, convert
* newlines to local convention
*/
return fwrite(buffer, 1, len, file) == len ? 0 : ZmErrSys ;
}
int
ZCloseFile(ZModem *info)
{
struct timeval tvp[2] ;
fclose(info->file) ;
if( ymodem )
truncate(info->filename, info->len) ;
if( info->date != 0 ) {
tvp[0].tv_sec = tvp[1].tv_sec = info->date ;
tvp[0].tv_usec = tvp[1].tv_usec = 0 ;
utimes(info->filename, tvp) ;
}
if( info->mode & 01000000 )
chmod(info->filename, info->mode&0777) ;
return 0 ;
}
void
ZIdleStr(u_char *buffer, int len, ZModem *info)
{
#ifdef COMMENT
fwrite(buffer, 1,len, stdout) ;
#endif /* COMMENT */
}
void
ZFlowControl(int onoff, ZModem *info)
{
if( onoff ) {
new_settings.c_iflag |= IXON|IXANY|IXOFF ;
#ifdef COMMENT
new_settings.c_cflag |= CRTSCTS ;
#endif /* COMMENT */
}
else {
new_settings.c_iflag &= ~(IXON|IXANY|IXOFF) ;
new_settings.c_cflag &= ~CRTSCTS ;
}
tcsetattr(info->ifd,TCSADRAIN, &new_settings) ;
}
static char *
basename(char *name)
{
char *ptr ;
if( (ptr = strrchr(name, '/')) == NULL )
return name ;
else
return ++ptr ;
}