home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 2 BBS
/
02-BBS.zip
/
BTMTSRC3.ZIP
/
JANUS.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-10-17
|
62KB
|
1,893 lines
/*--------------------------------------------------------------------------*/
/* */
/* */
/* ------------ Bit-Bucket Software, Co. */
/* \ 10001101 / Writers and Distributors of */
/* \ 011110 / Freely Available<tm> Software. */
/* \ 1011 / */
/* ------ */
/* */
/* (C) Copyright 1987-90, Bit Bucket Software Co., a Delaware Corporation. */
/* */
/* */
/* BinkleyTerm Janus revision 0.31, 11- 2-89 */
/* Full-duplex WaZOO file transfer protocol */
/* */
/* */
/* For complete details of the licensing restrictions, please refer */
/* to the License agreement, which is published in its entirety in */
/* the MAKEFILE and BT.C, and also contained in the file LICENSE.240. */
/* */
/* USE OF THIS FILE IS SUBJECT TO THE RESTRICTIONS CONTAINED IN THE */
/* BINKLEYTERM LICENSING AGREEMENT. IF YOU DO NOT FIND THE TEXT OF */
/* THIS AGREEMENT IN ANY OF THE AFOREMENTIONED FILES, OR IF YOU DO */
/* NOT HAVE THESE FILES, YOU SHOULD IMMEDIATELY CONTACT BIT BUCKET */
/* SOFTWARE CO. AT ONE OF THE ADDRESSES LISTED BELOW. IN NO EVENT */
/* SHOULD YOU PROCEED TO USE THIS FILE WITHOUT HAVING ACCEPTED THE */
/* TERMS OF THE BINKLEYTERM LICENSING AGREEMENT, OR SUCH OTHER */
/* AGREEMENT AS YOU ARE ABLE TO REACH WITH BIT BUCKET SOFTWARE, CO. */
/* */
/* */
/* You can contact Bit Bucket Software Co. at any one of the following */
/* addresses: */
/* */
/* Bit Bucket Software Co. FidoNet 1:104/501, 1:132/491, 1:141/491 */
/* P.O. Box 460398 AlterNet 7:491/0 */
/* Aurora, CO 80046 BBS-Net 86:2030/1 */
/* Internet f491.n132.z1.fidonet.org */
/* */
/* Please feel free to contact us at any time to share your comments about */
/* our software and/or licensing policies. */
/* */
/*--------------------------------------------------------------------------*/
#define WAZOO_SECTION
#include <sys\types.h>
#include <sys\stat.h>
#include <stdio.h>
#include <stdarg.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <io.h>
#include <conio.h>
#include <stdlib.h>
#ifdef __TURBOC__
#include "tc_utime.h"
#include <alloc.h>
#else
#include <sys/utime.h>
#include <malloc.h>
#endif
#ifndef IBM_C
extern int _fmode;
#endif
#include "com.h"
#include "xfer.h"
#include "zmodem.h"
#include "keybd.h"
#include "sbuf.h"
#include "sched.h"
#include "externs.h"
#include "prototyp.h"
#include "defines.h"
#include "janus.h"
/* Private routines */
static void getfname(word);
static void sendpkt(byte *,int,int);
static void sendpkt32(byte *,int,int);
static void txbyte(byte);
static long procfname(void);
static byte rcvpkt(void);
static void rxclose(word);
static void endbatch(void);
static void j_message(word,char *,...);
static void j_status(char *,...);
static int j_error(byte *,byte *);
static void update_y(void);
static void long_set_timer(long *,word);
static int long_time_gone(long *);
static int rcvrawbyte(void);
static int rxbyte(void);
static void xfer_summary(char *,char *,long *,int);
static void update_status(long *,long *,long,int *,int);
static void through(long *,long *);
static int get_filereq(byte);
static int record_reqfile(char *);
static byte get_reqname(byte);
static void mark_done(char *);
/* Private data. I know, proper software design says you shouldn't make data */
/* global unless you really need to. In this case speed and code size make */
/* it more important to avoid constantly pushing & popping arguments. */
static byte *GenericError = "!%s";
static byte *ReqTmp = "JANUSREQ.TMP";
#ifdef OS_2
static byte Upload_path[PATHLEN]; /* Dest. path of file being received */
#endif
static char *Rxbuf; /* Address of packet reception buffer */
static char *Txfname; /* Full path of file we're sending */
static char *Rxfname; /* Full path of file we're receiving */
static byte *Rxbufptr; /* Current position within packet reception buffer */
static byte *Rxbufmax; /* Upper bound of packet reception buffer */
static byte Do_after; /* What to do with file being sent when we're done */
static byte WaitFlag; /* Tells rcvrawbyte() whether or not to wait */
static byte SharedCap; /* Capability bits both sides have in common */
static int Txfile; /* File handle of file we're sending */
static int Rxfile; /* File handle of file we're receiving */
static int ReqRecorded; /* Number of files obtained by this request */
static word TimeoutSecs; /* How long to wait for various things */
static word Rxblklen; /* Length of data in last data block packet recvd */
static word Next_y; /* Number of next available line on screen */
static word Tx_y; /* Line number of file transmission status display */
static word Rx_y; /* Line number of file reception status display */
static long Txlen; /* Total length of file we're sending */
static long Rxlen; /* Total length of file we're receiving */
static long Rxfiletime; /* Timestamp of file we're receiving */
static long Diskavail; /* Bytes available in upload directory */
static long TotalBytes; /* Total bytes xferred in this session */
static long Txsttime; /* Time at which we started sending current file */
static long Rxsttime; /* Time at which we started receiving current file */
/*****************************************************************************/
/* Super-duper neato-whizbang full-duplex streaming ACKless batch file */
/* transfer protocol for use in WaZOO mail sessions */
/*****************************************************************************/
void Janus(void)
{
byte xstate; /* Current file transmission state */
byte rstate; /* Current file reception state */
byte pkttype; /* Type of packet last received */
byte tx_inhibit; /* Flag to wait and send after done receiving */
byte *holdname; /* Name of hold area */
#ifdef OS_2
byte *p; /* Temporary pointer */
#endif
byte fsent; /* Did we manage to send anything this session? */
byte sending_req; /* Are we currently sending requested files? */
byte attempting_req; /* Are we waiting for the sender to start our req? */
byte req_started; /* Has the sender started servicing our request? */
int txoldeta; /* Last transmission ETA displayed */
int rxoldeta; /* Last reception ETA displayed */
word blklen; /* Length of last data block sent */
word txblklen; /* Size of data block to try to send this time */
word txblkmax; /* Max size of data block to send at this speed */
word goodneeded; /* # good bytes to send before upping txblklen */
word goodbytes; /* Number of good bytes sent at this block size */
word rpos_count; /* Number of RPOS packets sent at this position */
long xmit_retry; /* Time to retransmit lost FNAMEPKT or EOF packet */
long txpos; /* Current position within file we're sending */
long lasttx; /* Position within file of last data block we sent */
long starttime; /* Time at which we started this Janus session */
long txstpos; /* Initial data position of file we're sending */
long rxstpos; /* Initial data position of file we're receiving */
long txoldpos; /* Last transmission file position displayed */
long rxoldpos; /* Last reception file position displayed */
long rpos_retry; /* Time at which to retry RPOS packet */
long brain_dead; /* Time at which to give up on other computer */
long rpos_sttime; /* Time at which we started current RPOS sequence */
long last_rpostime; /* Timetag of last RPOS which we performed */
long last_blkpos; /* File position of last out-of-sequence BLKPKT */
FILE *reqfile; /* File handle for .REQ file */
set_prior(3); /* Time Critical */
XON_DISABLE();
if (un_attended && fullscreen)
{
clear_filetransfer();
sb_show();
}
else
{
set_xy(NULL);
Next_y = locate_y;
}
Tx_y = Rx_y = 0;
SharedCap = 0;
TotalBytes = 0;
time(&starttime);
/*------------------------------------------------------------------------*/
/* Allocate memory */
/*------------------------------------------------------------------------*/
Rxbuf = Txbuf + 4096 + 8;
Txfname = Rxfname = NULL;
if (!(Txfname = malloc(PATHLEN)) || !(Rxfname = malloc(PATHLEN)))
{
status_line(msgtxt[M_MEM_ERROR]);
mdm_hangup();
goto freemem;
}
Rxbufmax = Rxbuf + BUFMAX + 8;
/*------------------------------------------------------------------------*/
/* Initialize file transmission variables */
/*------------------------------------------------------------------------*/
tx_inhibit = FALSE;
last_rpostime = xmit_retry = 0L;
TimeoutSecs = (unsigned int)(40960 / cur_baud);
if (TimeoutSecs < 10)
TimeoutSecs = 10;
long_set_timer(&brain_dead,120);
txblkmax = cur_baud/300 * 128;
if (txblkmax > BUFMAX)
txblkmax = BUFMAX;
txblklen = txblkmax;
goodbytes = goodneeded = 0;
Txfile = -1;
sending_req = fsent = FALSE;
xstate = XSENDFNAME;
getfname(INITIAL_XFER);
/*------------------------------------------------------------------------*/
/* Initialize file reception variables */
/*------------------------------------------------------------------------*/
holdname = HoldAreaNameMunge(&called_addr);
sprintf(Abortlog_name,"%s%s.Z\0",holdname,Hex_Addr_Str(&called_addr));
/* Sorry, this don't cut in OS/2's zfree */
#ifdef OS_2
p = strchr(strcpy (Upload_path, CurrentNetFiles),'\0') - 1;
while (p >= Upload_path && *p != '\\')
--p;
*(++p) = '\0';
if ((Diskavail = zfree(Upload_path)) <= 0L)
Diskavail = 0x7FFFFFF;
#else
Diskavail = (CurrentNetFiles[1] == ':') ? zfree(CurrentNetFiles) : 0x7FFFFFFF;
#endif
Rxbufptr = NULL;
rpos_retry = rpos_count = 0;
attempting_req = req_started = FALSE;
rstate = RRCVFNAME;
/*------------------------------------------------------------------------*/
/* Send and/or receive stuff until we're done with both */
/*------------------------------------------------------------------------*/
do
{ /* while (xstate || rstate) */
/*---------------------------------------------------------------------*/
/* If nothing useful (i.e. sending or receiving good data block) has */
/* happened within the last 2 minutes, give up in disgust */
/*---------------------------------------------------------------------*/
if (long_time_gone(&brain_dead))
{
j_status(msgtxt[M_OTHER_DIED]); /* "He's dead, Jim." */
goto giveup;
}
/*---------------------------------------------------------------------*/
/* If we're tired of waiting for an ACK, try again */
/*---------------------------------------------------------------------*/
if (xmit_retry)
{
if (long_time_gone(&xmit_retry))
{
j_message(Tx_y,msgtxt[M_TIMEOUT]);
xmit_retry = 0L;
switch (xstate)
{
case XRCVFNACK:
xstate = XSENDFNAME;
break;
case XRCVFRNAKACK:
xstate = XSENDFREQNAK;
break;
case XRCVEOFACK:
errno = 0;
lseek(Txfile, txpos=lasttx, SEEK_SET);
if (j_error(msgtxt[M_SEEK_MSG],Txfname))
goto giveup;
xstate = XSENDBLK;
break;
}
}
}
/*---------------------------------------------------------------------*/
/* Transmit next part of file, if any */
/*---------------------------------------------------------------------*/
switch (xstate)
{
case XSENDBLK:
if (tx_inhibit)
break;
*((long *)Txbuf) = lasttx = txpos;
errno = 0;
blklen = read(Txfile, Txbuf+sizeof(txpos), txblklen);
if (j_error(msgtxt[M_READ_MSG],Txfname))
goto giveup;
txpos += blklen;
sendpkt(Txbuf, sizeof(txpos)+blklen, BLKPKT);
update_status(&txpos,&txoldpos,Txlen-txpos,&txoldeta,Tx_y);
fsent = TRUE;
if (txpos >= Txlen || blklen < txblklen)
{
long_set_timer(&xmit_retry,TimeoutSecs);
xstate = XRCVEOFACK;
}
else
long_set_timer(&brain_dead,120);
if (txblklen < txblkmax && (goodbytes+=txblklen) >= goodneeded)
{
txblklen <<= 1;
goodbytes = 0;
}
break;
case XSENDFNAME:
blklen = strchr( strchr(Txbuf,'\0')+1, '\0') - Txbuf + 1;
Txbuf[blklen++] = OURCAP;
sendpkt(Txbuf,blklen,FNAMEPKT);
txoldpos = txoldeta = -1;
long_set_timer(&xmit_retry,TimeoutSecs);
xstate = XRCVFNACK;
break;
case XSENDFREQNAK:
sendpkt(NULL,0,FREQNAKPKT);
long_set_timer(&xmit_retry,TimeoutSecs);
xstate = XRCVFRNAKACK;
break;
}
/*---------------------------------------------------------------------*/
/* Catch up on our reading; receive and handle all outstanding packets */
/*---------------------------------------------------------------------*/
while (pkttype = rcvpkt())
{
if (pkttype != BADPKT)
long_set_timer(&brain_dead,120);
switch (pkttype)
{
/*---------------------------------------------------------------*/
/* File data block or munged block */
/*---------------------------------------------------------------*/
case BADPKT:
case BLKPKT:
if (rstate == RRCVBLK)
{
if (pkttype == BADPKT || *((long *)Rxbuf) != Rxpos)
{
if (pkttype == BLKPKT)
{
if (*((long *)Rxbuf) < last_blkpos)
rpos_retry = rpos_count = 0;
last_blkpos = *((long *)Rxbuf);
}
if (long_time_gone(&rpos_retry))
{
/* If we're the called machine, and we're trying
to send stuff, and it seems to be screwing up
our ability to receive stuff, maybe this
connection just can't hack full-duplex. Try
waiting till the sending system finishes before
sending our stuff to it */
if (rpos_count > 4)
{
if (xstate && !isOriginator && !tx_inhibit)
{
tx_inhibit = TRUE;
j_status(msgtxt[M_GOING_ONE_WAY]);
}
rpos_count = 0;
}
if (++rpos_count == 1)
time(&rpos_sttime);
j_message(Rx_y,msgtxt[M_J_BAD_PACKET],Rxpos);
*((long *)Rxbuf) = Rxpos;
*((long *)(Rxbuf + sizeof(Rxpos))) = rpos_sttime;
sendpkt(Rxbuf, sizeof(Rxpos)+sizeof(rpos_sttime), RPOSPKT);
long_set_timer(&rpos_retry,TimeoutSecs/2);
}
}
else
{
last_blkpos = Rxpos;
rpos_retry = rpos_count = 0;
errno = 0;
write(Rxfile, Rxbuf+sizeof(Rxpos), Rxblklen -= sizeof(Rxpos));
if (j_error(msgtxt[M_WRITE_MSG],Rxfname))
goto giveup;
Diskavail -= Rxblklen;
Rxpos += Rxblklen;
update_status(&Rxpos,&rxoldpos,Rxlen-Rxpos,&rxoldeta,Rx_y);
if (Rxpos >= Rxlen)
{
rxclose(GOOD_XFER);
Rxlen -= rxstpos;
through(&Rxlen,&Rxsttime);
j_status("%s-J%s %s",msgtxt[M_FILE_RECEIVED], (SharedCap&CANCRC32)?"/32":" ",Rxfname);
update_files(0);
rstate = RRCVFNAME;
}
}
}
if (rstate == RRCVFNAME)
sendpkt(NULL,0,EOFACKPKT);
break;
/*---------------------------------------------------------------*/
/* Name and other data for next file to receive */
/*---------------------------------------------------------------*/
case FNAMEPKT:
if (rstate == RRCVFNAME)
Rxpos = rxstpos = procfname();
if (!Rxfname[0] && get_filereq(req_started))
{
sendpkt(Rxbuf,strlen(Rxbuf)+2,FREQPKT);
attempting_req = TRUE;
req_started = FALSE;
}
else
{
if (attempting_req)
{
attempting_req = FALSE;
req_started = TRUE;
}
*((long *)Rxbuf) = Rxpos;
Rxbuf[sizeof(Rxpos)] = SharedCap;
sendpkt(Rxbuf,sizeof(Rxpos)+1,FNACKPKT);
rxoldpos = rxoldeta = -1;
if (Rxpos > -1)
rstate = (byte)((Rxfname[0]) ? RRCVBLK : RDONE);
else
j_status(msgtxt[M_REFUSING],Rxfname);
if (!rstate)
tx_inhibit = FALSE;
if (!(xstate || rstate))
goto breakout;
}
break;
/*---------------------------------------------------------------*/
/* ACK to filename packet we just sent */
/*---------------------------------------------------------------*/
case FNACKPKT:
if (xstate == XRCVFNACK)
{
xmit_retry = 0L;
if (Txfname[0])
{
SharedCap = (Rxblklen > sizeof(long)) ? Rxbuf[sizeof(long)] : 0;
if ((txpos = *((long *)Rxbuf)) > -1L)
{
if (txpos)
status_line(msgtxt[M_SYNCHRONIZING],txpos);
errno = 0;
lseek(Txfile, txstpos = txpos, SEEK_SET);
if (j_error(msgtxt[M_SEEK_MSG],Txfname))
goto giveup;
xstate = XSENDBLK;
}
else
{
j_status(msgtxt[M_REMOTE_REFUSED],Txfname);
if (sending_req)
{
if (!(sending_req = get_reqname(FALSE)))
getfname(GOOD_XFER);
}
else
{
Do_after = NOTHING_AFTER;
getfname(GOOD_XFER);
}
xstate = XSENDFNAME;
}
}
else
{
sent_mail = 1;
xstate = XDONE;
}
}
if (!(xstate || rstate))
goto breakout;
break;
/*---------------------------------------------------------------*/
/* Request to send more stuff rather than end batch just yet */
/*---------------------------------------------------------------*/
case FREQPKT:
if (xstate == XRCVFNACK)
{
xmit_retry = 0L;
SharedCap = *(strchr(Rxbuf,'\0')+1);
if (CurrentReqLim > 0)
{
sprintf(Txbuf,request_template,CurrentNetFiles,
Hex_Addr_Str (&(alias[0])));
errno = 0;
reqfile = fopen(Txbuf,"wt");
j_error(msgtxt[M_OPEN_MSG],Txbuf);
fputs(Rxbuf,reqfile);
fputs("\n",reqfile);
fclose(reqfile);
unlink(ReqTmp);
ReqRecorded = 0; /* counted by record_reqfile */
respond_to_file_requests(0,record_reqfile);
CurrentReqLim -= ReqRecorded;
if (sending_req = get_reqname(TRUE))
xstate = XSENDFNAME;
else
xstate = XSENDFREQNAK;
}
else
xstate = XSENDFREQNAK;
}
break;
/*---------------------------------------------------------------*/
/* Our last file request didn't match anything; move on to next */
/*---------------------------------------------------------------*/
case FREQNAKPKT:
attempting_req = FALSE;
req_started = TRUE;
sendpkt(NULL,0,FRNAKACKPKT);
break;
/*---------------------------------------------------------------*/
/* ACK to no matching files for request error; try to end again */
/*---------------------------------------------------------------*/
case FRNAKACKPKT:
if (xstate == XRCVFRNAKACK)
{
xmit_retry = 0L;
getfname(GOOD_XFER);
xstate = XSENDFNAME;
}
break;
/*---------------------------------------------------------------*/
/* ACK to last data block in file */
/*---------------------------------------------------------------*/
case EOFACKPKT:
if (xstate == XRCVEOFACK || xstate == XRCVFNACK)
{
xmit_retry = 0L;
if (xstate == XRCVEOFACK)
{
Txlen -= txstpos;
through(&Txlen,&Txsttime);
j_status("%s-J%s %s",msgtxt[M_FILE_SENT],(SharedCap&CANCRC32)?"/32":" ",Txfname);
update_files(1);
if (sending_req)
{
if (!(sending_req = get_reqname(FALSE)))
getfname(GOOD_XFER);
}
else
getfname(GOOD_XFER);
}
xstate = XSENDFNAME;
}
break;
/*---------------------------------------------------------------*/
/* Receiver says "let's try that again." */
/*---------------------------------------------------------------*/
case RPOSPKT:
if (xstate == XSENDBLK || xstate == XRCVEOFACK)
{
if (*((long *)(Rxbuf+sizeof(txpos))) != last_rpostime)
{
last_rpostime = *((long *)(Rxbuf+sizeof(txpos)));
xmit_retry = 0L;
CLEAR_OUTBOUND();
errno = 0;
lseek(Txfile, txpos = lasttx = *((long *)Rxbuf), SEEK_SET);
if (j_error(msgtxt[M_SEEK_MSG],Txfname))
goto giveup;
j_message(Tx_y,msgtxt[M_SYNCHRONIZING],txpos);
txblklen >>= 2;
if (txblklen < 64)
txblklen = 64;
goodbytes = 0;
goodneeded += 1024;
if (goodneeded > 8192)
goodneeded = 8192;
xstate = XSENDBLK;
}
}
break;
/*---------------------------------------------------------------*/
/* Debris from end of previous Janus session; ignore it */
/*---------------------------------------------------------------*/
case HALTACKPKT:
break;
/*---------------------------------------------------------------*/
/* Abort the transfer and quit */
/*---------------------------------------------------------------*/
default:
j_status(msgtxt[M_UNKNOWN_PACKET],pkttype);
/* fallthrough */
case HALTPKT:
giveup:
j_status(msgtxt[M_SESSION_ABORT]);
if (Txfname[0])
getfname(ABORT_XFER);
if (rstate == RRCVBLK)
{
TotalBytes += (Rxpos-rxstpos);
rxclose(FAILED_XFER);
}
goto abortxfer;
} /* switch (pkttype) */
} /* while (pkttype) */
}
while (xstate || rstate);
/*------------------------------------------------------------------------*/
/* All done; make sure other end is also finished (one way or another) */
/*------------------------------------------------------------------------*/
breakout:
if (!fsent)
j_status(msgtxt[M_NOTHING_TO_SEND], Full_Addr_Str (&called_addr));
abortxfer:
through(&TotalBytes,&starttime);
endbatch();
/*------------------------------------------------------------------------*/
/* Release allocated memory */
/*------------------------------------------------------------------------*/
freemem:
if (Txfname)
free(Txfname);
if (Rxfname)
free(Rxfname);
flag_file (CLEAR_FLAG, &called_addr, 1);
set_prior(4); /* Always High */
}
/*****************************************************************************/
/* Get name and info for next file to be transmitted, if any, and build */
/* FNAMEPKT. Packet contents as per ZModem filename info packet, to allow */
/* use of same method of aborted-transfer recovery. If there are no more */
/* files to be sent, build FNAMEPKT with null filename. Also open file and */
/* set up for transmission. Set Txfname, Txfile, Txlen. Txbuf must not be */
/* modified until FNACKPKT is received. */
/*****************************************************************************/
static void getfname(word xfer_flag)
{
static byte floflag, bad_xfers, outboundname[PATHLEN];
static long floname_pos;
static FILE *flofile;
static int have_lock;
char *holdname;
register char *p;
int i;
long curr_pos;
struct stat f;
/*------------------------------------------------------------------------*/
/* Initialize static variables on first call of the batch */
/*------------------------------------------------------------------------*/
if (xfer_flag == INITIAL_XFER)
{
floflag = outboundname[0] = '\0';
flofile = NULL;
have_lock = flag_file (TEST_AND_SET, &called_addr, 1);
/*------------------------------------------------------------------------*/
/* If we were already sending a file, close it and clean up */
/*------------------------------------------------------------------------*/
}
else if (Txfile != -1)
{
errno = 0;
close(Txfile);
j_error(msgtxt[M_CLOSE_MSG],Txfname);
Txfile = -1;
/*---------------------------------------------------------------------*/
/* If xfer completed, do post-xfer cleanup */
/*---------------------------------------------------------------------*/
if (xfer_flag == GOOD_XFER)
{
/*------------------------------------------------------------------*/
/* Perform post-xfer file massaging if neccessary */
/*------------------------------------------------------------------*/
switch (Do_after)
{
case DELETE_AFTER:
case SHOW_DELETE_AFTER:
j_status(msgtxt[M_UNLINKING_MSG],Txfname);
unlink(Txfname);
j_error(msgtxt[M_UNLINK_MSG],Txfname);
break;
case TRUNC_AFTER:
j_status(msgtxt[M_TRUNC_MSG],Txfname);
Txfile = open(Txfname,O_TRUNC | O_RDWR,S_IREAD | S_IWRITE);
if (Txfile != -1)
errno = 0;
j_error(msgtxt[M_TRUNC_MSG],Txfname);
close(Txfile);
Txfile = -1;
}
/*------------------------------------------------------------------*/
/* If processing .?LO file, flag filename as sent (name[0] = '~') */
/*------------------------------------------------------------------*/
skipname:
if (floflag)
{
curr_pos = ftell(flofile);
j_error(msgtxt[M_SEEK_MSG],outboundname);
fseek(flofile,floname_pos,SEEK_SET);
j_error(msgtxt[M_SEEK_MSG],outboundname);
fputc(Txfname[0] = '~',flofile);
j_error(msgtxt[M_WRITE_MSG],outboundname);
fseek(flofile,curr_pos,SEEK_SET);
j_error(msgtxt[M_SEEK_MSG],outboundname);
}
}
else
{
abort:
++bad_xfers;
}
}
/*------------------------------------------------------------------------*/
/* Find next file to be sent and build FNAMEPKT. If reading .FLO-type */
/* file get next entry from it; otherwise check for next .OUT/.FLO file */
/*------------------------------------------------------------------------*/
if (have_lock)
goto end_send;
holdname = HoldAreaNameMunge(&called_addr);
if (!floflag)
{
/*---------------------------------------------------------------------*/
/* If first getfname() for this batch, init filename to .OUT */
/*---------------------------------------------------------------------*/
if (!outboundname[0])
{
sprintf(outboundname,"%s%s.OUT",holdname,Hex_Addr_Str(&called_addr));
*ext_flags = 'O';
/*---------------------------------------------------------------------*/
/* Increment outbound filename until match found or all checked */
/* .OUT->.DUT->.CUT->.HUT->.FLO->.DLO->.CLO->.HLO->null name */
/*---------------------------------------------------------------------*/
}
else
{
nxtout:
p = strchr(outboundname,'\0') - 3;
for (i = 0; i < NUM_FLAGS; ++i)
if (ext_flags[i] == *p)
break;
if (i < NUM_FLAGS - 1)
{
*p = ext_flags[i+1];
#ifndef JACK_DECKER
if (isOriginator && *p == 'H')
goto nxtout;
#endif
}
else
{
/*---------------------------------------------------------------*/
/* Finished ?,D,C,H sequence; wrap .OUT->.FLO, or .FLO->done */
/*---------------------------------------------------------------*/
if (!floflag)
{
*p++ = *ext_flags = 'F';
*p++ = 'L';
*p = 'O';
++floflag;
}
else
end_send:
outboundname[0] = Txfname[0] = Txbuf[0] = Txbuf[1] = floflag = '\0';
}
}
/*---------------------------------------------------------------------*/
/* Check potential outbound name; if file doesn't exist keep looking */
/*---------------------------------------------------------------------*/
if (outboundname[0])
{
if (!dexists(outboundname))
goto nxtout;
if (floflag)
goto rdflo;
strcpy(Txfname,outboundname);
/*------------------------------------------------------------------*/
/* Start FNAMEPKT using .PKT alias */
/*------------------------------------------------------------------*/
invent_pkt_name(Txbuf);
Do_after = DELETE_AFTER;
}
/*------------------------------------------------------------------------*/
/* Read and process next entry from .?LO-type file */
/*------------------------------------------------------------------------*/
}
else
{
rdflo:
/*---------------------------------------------------------------------*/
/* Open .?LO file for processing if neccessary */
/*---------------------------------------------------------------------*/
if (!flofile)
{
bad_xfers = 0;
errno = 0;
flofile = fopen(outboundname,"rb+");
if (j_error(msgtxt[M_OPEN_MSG],outboundname))
goto nxtout;
}
errno = 0;
floname_pos = ftell(flofile);
j_error(msgtxt[M_SEEK_MSG],outboundname);
if (fgets(p = Txfname, PATHLEN, flofile))
{
/*------------------------------------------------------------------*/
/* Got an attached file name; check for handling flags, fix up name */
/*------------------------------------------------------------------*/
while (*p > ' ')
++p;
*p = '\0';
switch (Txfname[0])
{
case '\0':
case '~':
case ';':
goto rdflo;
case TRUNC_AFTER:
case DELETE_AFTER:
case SHOW_DELETE_AFTER:
Do_after = Txfname[0];
strcpy(Txfname,Txfname+1);
break;
default:
Do_after = NOTHING_AFTER;
break;
}
/*------------------------------------------------------------------*/
/* Start FNAMEPKT with simple filename */
/*------------------------------------------------------------------*/
while (p >= Txfname && *p != '\\' && *p != ':')
--p;
strcpy(Txbuf,++p);
}
else
{
/*------------------------------------------------------------------*/
/* Finished reading this .?LO file; clean up and look for another */
/*------------------------------------------------------------------*/
errno = 0;
fclose(flofile);
j_error(msgtxt[M_CLOSE_MSG],outboundname);
flofile = NULL;
if (!bad_xfers)
{
unlink(outboundname);
j_error(msgtxt[M_UNLINK_MSG],outboundname);
}
goto nxtout;
}
}
/*------------------------------------------------------------------------*/
/* If we managed to find a valid file to transmit, open it, finish */
/* FNAMEPKT, and print nice message for the sysop. */
/*------------------------------------------------------------------------*/
if (Txfname[0])
{
if (xfer_flag == ABORT_XFER)
goto abort;
j_status(msgtxt[M_SENDING],Txfname);
errno = 0;
Txfile = open(Txfname,O_RDONLY | O_BINARY);
if (Txfile != -1)
errno = 0;
if (j_error(msgtxt[M_OPEN_MSG],Txfname))
goto skipname;
stat(Txfname,&f);
sprintf(strchr(Txbuf,'\0')+1,"%lu %lo %o",Txlen = f.st_size,f.st_mtime,f.st_mode);
p = strchr(Txfname,'\0');
while (p >= Txfname && *p != ':' && *p != '\\')
--p;
if (!un_attended || !fullscreen)
Tx_y = Next_y;
else
Tx_y = 1;
xfer_summary(msgtxt[M_SEND],++p,&Txlen,Tx_y);
time(&Txsttime);
}
}
/*****************************************************************************/
/* Build and send a packet of any type. */
/* Packet structure is: PKTSTRT,contents,packet_type,PKTEND,crc */
/* CRC is computed from contents and packet_type only; if PKTSTRT or PKTEND */
/* get munged we'll never even find the CRC. */
/*****************************************************************************/
static void sendpkt(register byte *buf,int len,int type)
{
register word crc;
if ((SharedCap & CANCRC32) && type != FNAMEPKT)
sendpkt32(buf,len,type);
else {
BUFFER_BYTE(DLE);
BUFFER_BYTE(PKTSTRTCHR ^ 0x40);
crc = 0;
while (--len >= 0) {
txbyte(*buf);
crc = xcrc(crc, ((word)(*buf++)) );
}
BUFFER_BYTE((byte)type);
crc = xcrc(crc,type);
BUFFER_BYTE(DLE);
BUFFER_BYTE(PKTENDCHR ^ 0x40);
txbyte((byte)(crc >> 8));
txbyte((byte)(crc & 0xFF));
UNBUFFER_BYTES();
}
}
/*****************************************************************************/
/* Build and send a packet using 32-bit CRC; same as sendpkt in other ways */
/*****************************************************************************/
static void sendpkt32(register byte *buf,register int len,int type) {
unsigned long crc32;
BUFFER_BYTE(DLE);
BUFFER_BYTE(PKTSTRTCHR32 ^ 0x40);
crc32 = 0xFFFFFFFF;
while (--len >= 0) {
txbyte(*buf);
crc32 = Z_32UpdateCRC(((word)*buf),crc32);
++buf;
}
BUFFER_BYTE((byte)type);
crc32 = Z_32UpdateCRC(type,crc32);
BUFFER_BYTE(DLE);
BUFFER_BYTE(PKTENDCHR ^ 0x40);
txbyte((byte)(crc32 >> 24));
txbyte((byte)((crc32 >> 16) & 0xFF));
txbyte((byte)((crc32 >> 8) & 0xFF));
txbyte((byte)(crc32 & 0xFF));
UNBUFFER_BYTES();
}
/*****************************************************************************/
/* Transmit cooked escaped byte(s) corresponding to raw input byte. Escape */
/* DLE, XON, and XOFF using DLE prefix byte and ^ 0x40. Also escape */
/* CR-after-'@' to avoid Telenet/PC-Pursuit problems. */
/*****************************************************************************/
static void txbyte(register byte c) {
static byte lastsent;
switch (c) {
case CR:
if (lastsent != '@')
goto sendit;
/* fallthrough */
case DLE:
case XON:
case XOFF:
BUFFER_BYTE(DLE);
c ^= 0x40;
/* fallthrough */
default:
sendit: BUFFER_BYTE(lastsent = c);
}
}
/*****************************************************************************/
/* Process FNAMEPKT of file to be received. Check for aborted-transfer */
/* recovery and solve filename collisions. Check for enough disk space. */
/* Return initial file data position to start receiving at, or -1 if error */
/* detected to abort file reception. Set Rxfname, Rxlen, Rxfile. */
/*****************************************************************************/
static long procfname(void) {
register byte *p;
char linebuf[128], *fileinfo, *badfname;
long filestart, bytes;
FILE *abortlog;
struct stat f;
int i;
/*------------------------------------------------------------------------*/
/* Initialize for file reception */
/*------------------------------------------------------------------------*/
Rxfname[0] = Resume_WaZOO = 0;
/*------------------------------------------------------------------------*/
/* Save info on WaZOO transfer in case of abort */
/*------------------------------------------------------------------------*/
strcpy(Resume_name,fancy_str(Rxbuf));
fileinfo = strchr(Rxbuf,'\0') + 1;
p = strchr(fileinfo,'\0') + 1;
SharedCap = (Rxblklen > p-Rxbuf) ? *p & OURCAP : 0;
/*------------------------------------------------------------------------*/
/* If this is a null FNAMEPKT, return OK immediately */
/*------------------------------------------------------------------------*/
if (!Rxbuf[0])
return 0L;
strcpy(linebuf,Rxbuf);
strlwr(linebuf);
p = check_netfile(linebuf);
j_status("#%s %s %s",msgtxt[M_RECEIVING],(p) ? p : " ",Rxbuf);
/*------------------------------------------------------------------------*/
/* Extract and validate filesize */
/*------------------------------------------------------------------------*/
Rxlen = -1;
Rxfiletime = 0;
if (sscanf(fileinfo,"%ld %lo",&Rxlen,&Rxfiletime) < 1 || Rxlen < 0) {
j_status(msgtxt[M_NO_LENGTH]);
return -1L;
}
sprintf(Resume_info,"%ld %lo",Rxlen,Rxfiletime);
/*------------------------------------------------------------------------*/
/* Check if this is a failed WaZOO transfer which should be resumed */
/*------------------------------------------------------------------------*/
if (dexists(Abortlog_name)) {
errno = 0;
abortlog = fopen(Abortlog_name,read_ascii);
if (!j_error(msgtxt[M_OPEN_MSG],Abortlog_name)) {
while (!feof(abortlog)) {
linebuf[0] = '\0';
if (!fgets(p = linebuf,sizeof(linebuf),abortlog))
break;
while (*p >= ' ')
++p;
*p = '\0';
p = strchr(linebuf,' ');
*p = '\0';
if (!stricmp(linebuf,Resume_name)) {
p = strchr( (badfname = ++p), ' ');
*p = '\0';
if (!stricmp(++p,Resume_info)) {
++Resume_WaZOO;
break;
}
}
}
errno = 0;
fclose(abortlog);
j_error(msgtxt[M_CLOSE_MSG],Abortlog_name);
}
}
/*------------------------------------------------------------------------*/
/* Open either the old or a new file, as appropriate */
/*------------------------------------------------------------------------*/
p = strchr(strcpy(Rxfname,CurrentNetFiles),'\0');
errno = 0;
if (Resume_WaZOO) {
strcpy(p,badfname);
Rxfile = open(Rxfname,O_CREAT|O_RDWR|O_BINARY,S_IREAD|S_IWRITE);
} else {
strcpy(p,Rxbuf);
/*---------------------------------------------------------------------*/
/* If the file already exists: */
/* 1) And the new file has the same time and size, skip it */
/* 2) And OVERWRITE is turned on, delete the old copy */
/* 3) Else create a unique file name in which to store new data */
/*---------------------------------------------------------------------*/
if (dexists(Rxfname)) {
stat(Rxfname,&f);
if (Rxlen == f.st_size && Rxfiletime == f.st_mtime) {
j_status(msgtxt[M_ALREADY_HAVE],Rxfname);
return -1L;
}
i = strlen(Rxfname) - 1;
if ((!overwrite) || (is_arcmail(Rxfname,i))) {
unique_name(Rxfname);
j_status(msgtxt[M_RENAME_MSG],Rxfname);
} else {
unlink(Rxfname);
j_error(msgtxt[M_UNLINK_MSG],Rxfname);
}
}
Rxfile = open(Rxfname,O_CREAT|O_EXCL|O_RDWR|O_BINARY,S_IREAD|S_IWRITE);
}
if (Rxfile != -1)
errno = 0;
if (j_error(msgtxt[M_OPEN_MSG],Rxfname))
return -1L;
/*------------------------------------------------------------------------*/
/* Determine initial file data position */
/*------------------------------------------------------------------------*/
if (Resume_WaZOO) {
stat(Rxfname,&f);
j_status(msgtxt[M_SYNCHRONIZING_OFFSET],filestart = f.st_size);
p = Rxbuf;
errno = 0;
lseek(Rxfile,filestart,SEEK_SET);
if (j_error(msgtxt[M_SEEK_MSG],Rxfname)) {
close(Rxfile);
return -1L;
}
} else
filestart = 0L;
/*------------------------------------------------------------------------*/
/* Check for enough disk space */
/*------------------------------------------------------------------------*/
bytes = Rxlen - filestart + 10240;
if (bytes > Diskavail) {
j_status(msgtxt[M_OUT_OF_DISK_SPACE]);
close(Rxfile);
return -1L;
}
/*------------------------------------------------------------------------*/
/* Print status message for the sysop */
/*------------------------------------------------------------------------*/
if (!un_attended || !fullscreen)
Rx_y = Next_y;
else
Rx_y = 2;
xfer_summary(msgtxt[M_RECV],p,&Rxlen,Rx_y);
time(&Rxsttime);
return filestart;
}
/*****************************************************************************/
/* Receive, validate, and extract a packet if available. If a complete */
/* packet hasn't been received yet, receive and store as much of the next */
/* packet as possible. Each call to rcvpkt() will continue accumulating a */
/* packet until a complete packet has been received or an error is detected. */
/* Rxbuf must not be modified between calls to rcvpkt() if NOPKT is returned.*/
/* Returns type of packet received, NOPKT, or BADPKT. Sets Rxblklen. */
/*****************************************************************************/
static byte rcvpkt() {
static byte rxcrc32;
static word crc;
static unsigned long crc32;
register byte *p;
register int c;
int i;
unsigned long pktcrc;
/*------------------------------------------------------------------------*/
/* Abort transfer if operator pressed ESC */
/*------------------------------------------------------------------------*/
if (got_ESC()) {
j_status(GenericError,msgtxt[M_KBD_MSG]);
return HALTPKT;
}
/*------------------------------------------------------------------------*/
/* If not accumulating packet yet, find start of next packet */
/*------------------------------------------------------------------------*/
WaitFlag = FALSE;
if (!(p = Rxbufptr)) {
do
c = rxbyte();
while (c >= 0 || c == PKTEND);
switch (c) {
case PKTSTRT:
rxcrc32 = FALSE;
p = Rxbuf;
crc = 0;
break;
case PKTSTRT32:
rxcrc32 = TRUE;
p = Rxbuf;
crc32 = 0xFFFFFFFF;
break;
case NOCARRIER:
j_status(GenericError,&(msgtxt[M_NO_CARRIER][1]));
return HALTPKT;
default:
return NOPKT;
}
}
/*------------------------------------------------------------------------*/
/* Accumulate packet data until we empty buffer or find packet delimiter */
/*------------------------------------------------------------------------*/
if (rxcrc32) {
while ((c = rxbyte()) >= 0 && p < Rxbufmax) {
*p++ = (byte)c;
crc32 = Z_32UpdateCRC(c,crc32);
}
} else {
while ((c = rxbyte()) >= 0 && p < Rxbufmax) {
*p++ = (byte)c;
crc = xcrc(crc,c);
}
}
/*------------------------------------------------------------------------*/
/* Handle whichever end-of-packet condition occurred */
/*------------------------------------------------------------------------*/
switch (c) {
/*---------------------------------------------------------------------*/
/* PKTEND found; verify valid CRC */
/*---------------------------------------------------------------------*/
case PKTEND:
WaitFlag = TRUE;
pktcrc = 0;
for (i = (rxcrc32) ? 4 : 2; i; --i) {
if ((c = rxbyte()) < 0)
break;
pktcrc = (pktcrc << 8) | c;
}
if (!i) {
if ((rxcrc32 && pktcrc == crc32) || pktcrc == crc) {
/*------------------------------------------------------------*/
/* Good packet verified; compute packet data length and */
/* return packet type */
/*------------------------------------------------------------*/
Rxbufptr = NULL;
Rxblklen = --p - Rxbuf;
return *p;
}
}
/* fallthrough */
/*---------------------------------------------------------------------*/
/* Bad CRC, carrier lost, or buffer overflow from munged PKTEND */
/*---------------------------------------------------------------------*/
default:
if (c == NOCARRIER) {
j_status(GenericError,&(msgtxt[M_NO_CARRIER][1]));
return HALTPKT;
} else {
Rxbufptr = NULL;
return BADPKT;
}
/*---------------------------------------------------------------------*/
/* Emptied buffer; save partial packet and let sender do something */
/*---------------------------------------------------------------------*/
case BUFEMPTY:
Rxbufptr = p;
return NOPKT;
/*---------------------------------------------------------------------*/
/* PKTEND was trashed; discard partial packet and prep for next one */
/*---------------------------------------------------------------------*/
case PKTSTRT:
rxcrc32 = FALSE;
Rxbufptr = Rxbuf;
crc = 0;
return BADPKT;
case PKTSTRT32:
rxcrc32 = TRUE;
Rxbufptr = Rxbuf;
crc32 = 0xFFFFFFFF;
return BADPKT;
}
}
/*****************************************************************************/
/* Close file being received and perform post-reception aborted-transfer */
/* recovery cleanup if neccessary. */
/*****************************************************************************/
static void rxclose(word xfer_flag) {
register byte *p;
byte namebuf[PATHLEN], linebuf[128], c;
FILE *abortlog, *newlog;
struct utimbuf utimes;
/*------------------------------------------------------------------------*/
/* Close file we've been receiving */
/*------------------------------------------------------------------------*/
errno = 0;
close(Rxfile);
j_error(msgtxt[M_CLOSE_MSG],Rxfname);
if (Rxfiletime) {
utimes.actime = Rxfiletime;
utimes.modtime = Rxfiletime;
utime(Rxfname,&utimes);
}
/*------------------------------------------------------------------------*/
/* If we completed a previously-aborted transfer, kill log entry & rename */
/*------------------------------------------------------------------------*/
if (xfer_flag == GOOD_XFER && Resume_WaZOO) {
abortlog = fopen(Abortlog_name,read_ascii);
if (!j_error(msgtxt[M_OPEN_MSG],Abortlog_name)) {
c = 0;
strcpy(strchr(strcpy(namebuf,Abortlog_name),'\0')-1,"TMP");
newlog = fopen(namebuf,write_ascii);
if (!j_error(msgtxt[M_OPEN_MSG],namebuf)) {
while (!feof(abortlog)) {
linebuf[0] = '\0';
if (!fgets(p = linebuf,sizeof(linebuf),abortlog))
break;
while (*p > ' ')
++p;
*p = '\0';
if (stricmp(linebuf,Resume_name)) {
*p = ' ';
fputs(linebuf,newlog);
if (j_error(msgtxt[M_WRITE_MSG],namebuf))
break;
++c;
}
}
errno = 0;
fclose(abortlog);
j_error(msgtxt[M_CLOSE_MSG],Abortlog_name);
fclose(newlog);
j_error(msgtxt[M_CLOSE_MSG],namebuf);
unlink(Abortlog_name);
j_error(msgtxt[M_UNLINK_MSG],Abortlog_name);
if (c) {
if (!rename(namebuf,Abortlog_name))
errno = 0;
j_error(msgtxt[M_RENAME_MSG],namebuf);
} else {
unlink(namebuf);
j_error(msgtxt[M_UNLINK_MSG],namebuf);
}
} else {
fclose(abortlog);
j_error(msgtxt[M_CLOSE_MSG],Abortlog_name);
}
}
j_status(msgtxt[M_FINISHED_PART],Resume_name);
unique_name(strcat(strcpy(namebuf,CurrentNetFiles),Resume_name));
if (!rename(Rxfname,namebuf)) {
errno = 0;
strcpy(Rxfname,namebuf);
} else
j_error(msgtxt[M_RENAME_MSG],Rxfname);
/*------------------------------------------------------------------------*/
/* If transfer failed and was not an attempted resumption, log for later */
/*------------------------------------------------------------------------*/
} else if (xfer_flag == FAILED_XFER && !Resume_WaZOO) {
j_status(msgtxt[M_SAVING_PART],Rxfname);
unique_name(strcat(strcpy(namebuf,CurrentNetFiles),"BadWaZOO.001"));
if (!rename(Rxfname,namebuf))
errno = 0;
j_error(msgtxt[M_RENAME_MSG],Rxfname);
abortlog = fopen(Abortlog_name,"at");
if (!j_error(msgtxt[M_OPEN_MSG],Abortlog_name)) {
fprintf(abortlog,"%s %s %s\n",Resume_name,namebuf+strlen(CurrentNetFiles),Resume_info);
j_error(msgtxt[M_WRITE_MSG],Abortlog_name);
fclose(abortlog);
j_error(msgtxt[M_CLOSE_MSG],Abortlog_name);
} else {
unlink(namebuf);
j_error(msgtxt[M_UNLINK_MSG],namebuf);
}
}
}
/*****************************************************************************/
/* Try REAL HARD to disengage batch session cleanly */
/*****************************************************************************/
static void endbatch(void) {
register int done, timeouts;
long timeval, brain_dead;
/*------------------------------------------------------------------------*/
/* Tell the other end to halt if it hasn't already */
/*------------------------------------------------------------------------*/
done = timeouts = 0;
long_set_timer(&brain_dead,120);
goto reject;
/*------------------------------------------------------------------------*/
/* Wait for the other end to acknowledge that it's halting */
/*------------------------------------------------------------------------*/
while (!done) {
if (long_time_gone(&brain_dead))
break;
switch (rcvpkt()) {
case NOPKT:
case BADPKT:
if (long_time_gone(&timeval)) {
if (++timeouts > 2)
++done;
else
goto reject;
}
break;
case HALTPKT:
case HALTACKPKT:
++done;
break;
default:
timeouts = 0;
reject: sendpkt(NULL,0,HALTPKT);
long_set_timer(&timeval,TimeoutSecs);
break;
}
}
/*------------------------------------------------------------------------*/
/* Announce quite insistently that we're done now */
/*------------------------------------------------------------------------*/
for (done = 0; done < 10; ++done)
sendpkt(NULL,0,HALTACKPKT);
wait_for_clear();
}
/*****************************************************************************/
/* Print a message in the message field of a transfer status line */
/*****************************************************************************/
static void j_message(word pos,char *va_alist,...)
{
va_list arg_ptr;
int y, l;
char buf[128];
y = pos;
va_start(arg_ptr, va_alist);
if (!un_attended || !fullscreen)
gotoxy(MSG_X,y);
else
sb_move(filewin,y,MSG_X);
(void) vsprintf(buf,va_alist,arg_ptr);
for (l = 25-strlen(buf); l > 0; --l)
strcat(buf," ");
if (!un_attended || !fullscreen) {
cputs(buf);
} else {
sb_puts(filewin,buf);
sb_show();
}
va_end(arg_ptr);
}
/*****************************************************************************/
/* Print & log status message without messing up display */
/*****************************************************************************/
static void j_status(char *va_alist,...)
{
va_list arg_ptr;
char buf[128];
va_start(arg_ptr, va_alist);
if (!un_attended || !fullscreen)
gotoxy(1,Next_y-1);
(void) vsprintf(buf,va_alist,arg_ptr);
status_line(buf);
if (!un_attended || !fullscreen)
update_y();
va_end(arg_ptr);
}
/*****************************************************************************/
/* Print & log error message without messing up display */
/*****************************************************************************/
static int j_error(byte *msg,byte *fname) {
register int e;
if (e = errno) {
if (!un_attended || !fullscreen)
gotoxy(1,Next_y-1);
got_error(msg,fname);
if (!un_attended || !fullscreen)
update_y();
}
return e;
}
/*****************************************************************************/
/* Update screen position variables after printing a message */
/*****************************************************************************/
static void update_y() {
set_xy(NULL); /* Bump cursor to next line after printing */
if (locate_y == Next_y) { /* If we didn't go anywhere, screen scrolled; */
if (Tx_y > 1) /* so decrement status line numbers */
--Tx_y;
if (Rx_y > 1)
--Rx_y;
} else Next_y = locate_y;
}
/*****************************************************************************/
/* Compute future timehack for later reference */
/*****************************************************************************/
static void long_set_timer(long *Buffer,word Duration) {
time(Buffer);
*Buffer += (long)Duration;
}
/*****************************************************************************/
/* Return TRUE if timehack has been passed, FALSE if not */
/*****************************************************************************/
static int long_time_gone(long *TimePtr) {
return (time(NULL) > *TimePtr);
}
/*****************************************************************************/
/* Receive cooked escaped byte translated to avoid various problems. */
/* Returns raw byte, BUFEMPTY, PKTSTRT, PKTEND, or NOCARRIER. */
/*****************************************************************************/
static int rxbyte(void) {
register int c, w;
if ((c = rcvrawbyte()) == DLE) {
w = WaitFlag++;
if ((c = rcvrawbyte()) >= 0) {
switch (c ^= 0x40) {
case PKTSTRTCHR:
c = PKTSTRT;
break;
case PKTSTRTCHR32:
c = PKTSTRT32;
break;
case PKTENDCHR:
c = PKTEND;
break;
}
}
WaitFlag = (byte)w;
}
return c;
}
/*****************************************************************************/
/* Receive raw non-escaped byte. Returns byte, BUFEMPTY, or NOCARRIER. */
/* If waitflag is true, will wait for a byte for Timeoutsecs; otherwise */
/* will return BUFEMPTY if a byte isn't ready and waiting in inbound buffer. */
/*****************************************************************************/
static int rcvrawbyte(void) {
register int c;
long timeval;
if ((c = PEEKBYTE()) >= 0)
return MODEM_IN();
if (!CARRIER)
return NOCARRIER;
if (!WaitFlag)
return BUFEMPTY;
timeval = time(NULL) + TimeoutSecs;
while ((c = PEEKBYTE()) < 0) {
if (!CARRIER)
return NOCARRIER;
if (time(NULL) > timeval)
return BUFEMPTY;
time_release();
}
return MODEM_IN();
}
/*****************************************************************************/
/* Display start-of-transfer summary info */
/*****************************************************************************/
static void xfer_summary(char *xfertype,char *fname,long *len,int y) {
char buf[128];
if (!un_attended || !fullscreen)
gotoxy(2,y);
else
sb_move(filewin,y,2);
sprintf(buf,"%s %12.12s; 0/%8ldb,%4d min. ",
xfertype, fname, *len, (int)((*len*10/cur_baud*100/JANUS_EFFICIENCY+59)/60));
if (!un_attended || !fullscreen) {
cputs(buf);
cputs(local_CEOL);
update_y();
} else {
sb_puts(filewin,buf);
sb_show();
}
}
/*****************************************************************************/
/* Update any status line values which have changed */
/*****************************************************************************/
static void update_status(long *pos,long *oldpos,long left,int *oldeta,int y) {
char buf[16];
register int eta;
elapse_time();
if (*pos != *oldpos) {
sprintf(buf,"%8ld",*oldpos = *pos);
if (!un_attended || !fullscreen) {
gotoxy(POS_X,y);
cputs(buf);
} else {
sb_move(filewin,y,POS_X);
sb_puts(filewin,buf);
}
}
eta = (int)((left*10/cur_baud*100/JANUS_EFFICIENCY+59)/60);
if (eta != *oldeta) {
sprintf(buf,"%4d",*oldeta = eta);
if (!un_attended || !fullscreen) {
gotoxy(ETA_X,y);
cputs(buf);
} else {
sb_move(filewin,y,ETA_X);
sb_puts(filewin,buf);
}
}
if (un_attended && fullscreen)
sb_show();
}
/*****************************************************************************/
/* Compute and print throughput */
/*****************************************************************************/
static void through(long *bytes,long *started)
{
static byte *scrn = "+CPS: %u (%lu bytes) Efficiency: %lu%%%%";
unsigned long elapsed;
register word cps;
elapsed = time(NULL) - *started;
cps = elapsed ? (word)(*bytes / elapsed) : 0;
if (lock_baud) /* CML */
j_status(scrn, cps, *bytes, cps * 1000L / (long)actual_baud);
else
j_status(scrn, cps, *bytes, cps * 1000L / (long)cur_baud);
TotalBytes += *bytes;
}
/*****************************************************************************/
/* Get next file to request, if any */
/*****************************************************************************/
static int get_filereq(byte req_started) {
byte reqname[PATHLEN], linebuf[128];
register byte *p;
int gotone = FALSE;
FILE *reqfile;
if (flag_file (TEST_AND_SET, &called_addr, 1))
return FALSE;
strcpy(reqname,Abortlog_name);
strcpy(strchr(reqname,'Z'),"REQ");
if (req_started)
mark_done(reqname);
if (dexists(reqname)) {
if (!(remote_capabilities & WZ_FREQ))
j_status(msgtxt[M_FREQ_DECLINED]);
else if (!(SharedCap & CANFREQ))
j_status(msgtxt[M_REMOTE_CANT_FREQ]);
else {
errno = 0;
reqfile = fopen(reqname,read_ascii);
if (!j_error(msgtxt[M_OPEN_MSG],reqname)) {
while (!feof(reqfile)) {
linebuf[0] = '\0';
if (!fgets(p = linebuf,sizeof(linebuf),reqfile))
break;
while (*p >= ' ')
++p;
*p = '\0';
if (linebuf[0] != ';') {
strcpy(Rxbuf,linebuf);
*(strchr(Rxbuf,'\0')+1) = SharedCap;
gotone = TRUE;
break;
}
}
fclose(reqfile);
j_error(msgtxt[M_CLOSE_MSG],reqname);
if (!gotone) {
unlink(reqname);
j_error(msgtxt[M_UNLINK_MSG],reqname);
}
}
}
}
return gotone;
}
/*****************************************************************************/
/* Record names of files to send in response to file request; callback */
/* routine for respond_to_file_requests() */
/*****************************************************************************/
static int record_reqfile(char *fname) {
FILE *tmpfile;
errno = 0;
tmpfile = fopen(ReqTmp,"at");
if (!j_error(msgtxt[M_OPEN_MSG],ReqTmp)) {
fputs(fname,tmpfile);
j_error(msgtxt[M_WRITE_MSG],ReqTmp);
fputs("\n",tmpfile);
j_error(msgtxt[M_WRITE_MSG],ReqTmp);
fclose(tmpfile);
j_error(msgtxt[M_CLOSE_MSG],ReqTmp);
++ReqRecorded;
return TRUE;
}
return FALSE;
}
/*****************************************************************************/
/* Get next file which was requested, if any */
/*****************************************************************************/
static byte get_reqname(byte first_req) {
register byte *p;
byte gotone = FALSE;
FILE *tmpfile;
struct stat f;
if (!first_req) {
errno = 0;
close(Txfile);
j_error(msgtxt[M_CLOSE_MSG],Txfname);
Txfile = -1;
mark_done(ReqTmp);
}
if (dexists(ReqTmp)) {
errno = 0;
tmpfile = fopen(ReqTmp,read_ascii);
if (!j_error(msgtxt[M_OPEN_MSG],ReqTmp)) {
while (!feof(tmpfile)) {
Txfname[0] = '\0';
if (!fgets(p = Txfname,PATHLEN,tmpfile))
break;
while (*p >= ' ')
++p;
*p = '\0';
if (Txfname[0] != ';') {
j_status(msgtxt[M_SENDING],Txfname);
errno = 0;
Txfile = open(Txfname,O_RDONLY|O_BINARY);
if (Txfile != -1)
errno = 0;
if (j_error(msgtxt[M_OPEN_MSG],Txfname))
continue;
while (p >= Txfname && *p != '\\' && *p != ':')
--p;
strcpy(Txbuf,++p);
stat(Txfname,&f);
sprintf(strchr(Txbuf,'\0')+1,"%lu %lo %o",Txlen = f.st_size,f.st_mtime,f.st_mode);
if (!un_attended || !fullscreen)
Tx_y = Next_y;
else
Tx_y = 1;
xfer_summary(msgtxt[M_SEND],p,&Txlen,Tx_y);
time(&Txsttime);
gotone = TRUE;
break;
}
}
fclose(tmpfile);
j_error(msgtxt[M_CLOSE_MSG],ReqTmp);
if (!gotone) {
unlink(ReqTmp);
j_error(msgtxt[M_UNLINK_MSG],ReqTmp);
}
}
}
return gotone;
}
/*****************************************************************************/
/* Mark first unmarked line of file as done (comment it out) */
/*****************************************************************************/
static void mark_done(char *fname) {
byte linebuf[128];
FILE *fh;
long pos;
if (dexists(fname)) {
errno = 0;
fh = fopen(fname,"rb+");
if (!j_error(msgtxt[M_OPEN_MSG],fname)) {
while (!feof(fh)) {
pos = ftell(fh);
j_error(msgtxt[M_SEEK_MSG],fname);
if (!fgets(linebuf,sizeof(linebuf),fh))
break;
if (linebuf[0] != ';') {
fseek(fh,pos,SEEK_SET);
j_error(msgtxt[M_SEEK_MSG],fname);
fputc(';',fh);
j_error(msgtxt[M_WRITE_MSG],fname);
break;
}
}
fclose(fh);
j_error(msgtxt[M_CLOSE_MSG],fname);
}
}
}