This is x_rcv.c in view mode; [Download] [Up]
/*
* Receive a list of files using a version of Ward Christensen's file
* transfer protocol. A non-zero return code means the user must acknowledge
* an error condition (a user generated abort returns a 0). Write errors
* are considered fatal.
*/
#include <stdio.h>
#include <curses.h>
#include "config.h"
#include "dial_dir.h"
#include "misc.h"
#include "xmodem.h"
unsigned char buf[1029];
char file_name[15];
long file_length;
static int err_method, tot_err, block_size;
int
rcv_xmodem(win, list, type, fast)
WINDOW *win;
char *list;
int type, fast;
{
extern char *protocol[];
FILE *fp, *my_fopen();
int i, default_err, is_batch, max_block, code, file_count, got_hdr;
int hours, mins, secs, len;
static int send_first();
long block, recv, partial;
float percent, performance;
unsigned char blk;
unsigned int sleep();
char *file, *name, *strcpy(), *strrchr(), *strtok();
void cancel_xfer();
/* which protocol? */
switch (type) {
case XMODEM:
default_err = CRC_CHECKSUM;
is_batch = 0;
max_block = 128;
break;
case XMODEM_1k:
default_err = CRC_CHECKSUM;
is_batch = 0;
max_block = 1024;
break;
case MODEM7:
default_err = CHECKSUM;
is_batch = 1;
max_block = 128;
break;
case YMODEM:
default_err = CRC;
is_batch = 1;
max_block = 1024;
performance = 1.09;
break;
case YMODEM_G:
default_err = NONE;
is_batch = 1;
max_block = 1024;
performance = 1.02;
break;
default:
return(1);
}
tot_err = 0;
file_count = 0;
mvwaddstr(win, 2, 24, protocol[type]);
mvwaddstr(win, 11, 24, "0 ");
while (1) {
file_count++;
file_length = 0L;
/* user supplied name */
if (!is_batch) {
if (file_count > 1)
break;
file = strtok(list, " \t");
/* dissect the file name */
if ((name = strrchr(file, '/')))
strcpy(file_name, ++name);
else
strcpy(file_name, file);
}
/* get the modem7 file name */
if (type == MODEM7) {
if (code = rcv_modem7(win, default_err))
return(code +1);
file = file_name;
}
/* get the block 0 */
if (type == YMODEM || type == YMODEM_G) {
if (code = send_first(win, max_block, default_err))
return(code +1);
if (code = rcv_ymodem(win))
return(code +1);
/* at the end? */
if (buf[3] == '\0') {
beep();
wrefresh(win);
putc_line(ACK);
sleep(1);
return(0);
}
file = file_name;
}
/* any trouble? */
if (file_name[0] == '\0')
continue;
clear_line(win, 3, 24, TRUE);
waddstr(win, file_name);
/* if file length is known */
if (file_length != 0L) {
mvwprintw(win, 4, 24, "%-10ld", file_length);
secs = (file_length * 10.0 / dir->baud[dir->d_cur]) * performance;
hours = secs / 3600;
mins = (secs % 3600) / 60;
secs = (secs % 3600) % 60;
mvwprintw(win, 6, 24, "%d:%02d:%02d", hours, mins, secs);
}
/* some starting numbers */
mvwaddstr(win, 7, 24, "0 ");
if (file_length != 0L && fast)
mvwaddstr(win, 8, 24, "0% ");
if (fast)
mvwaddstr(win, 9, 24, "0 ");
mvwaddstr(win, 10, 24, "0 ");
clear_line(win, 12, 24, TRUE);
waddstr(win, "NONE");
wrefresh(win);
/*
* If the user supplied the name, write permission is checked
* by the get_names() routine in xfer_menu(). If modem7
* or ymodem supplied name, the name is unique and the write
* permission on the directory is checked by the change_name()
* routines. However, this is required for systems with
* SETUID_BROKE set.
*/
/* open the file */
if (!(fp = my_fopen(file, "w"))) {
beep();
clear_line(win, 12, 24, TRUE);
wattrstr(win, A_BOLD, "CAN'T OPEN FILE");
wrefresh(win);
cancel_xfer(DOWN_LOAD);
return(1);
}
/* ACK the block 0 */
if (type == YMODEM || type == YMODEM_G)
putc_line(ACK);
if (code = send_first(win, max_block, default_err)) {
fclose(fp);
return(code +1);
}
/* here we go... */
clear_line(win, 12, 24, TRUE);
waddstr(win, "NONE");
wrefresh(win);
blk = 1;
block = 1L;
recv = 0L;
got_hdr = 1;
while (1) {
code = rcv_block(win, got_hdr, max_block, blk);
if (code < 0) {
fclose(fp);
return(code +1);
}
got_hdr = 0;
/* are we done? */
if (buf[0] == EOT) {
if (!is_batch) {
beep();
wrefresh(win);
sleep(1);
}
break;
}
/* if not a duplicate block */
if (!code) {
if (file_length != 0L) {
partial = file_length - recv;
if (partial > (long) block_size)
len = block_size;
else
len = partial;
}
else
len = block_size;
if (fwrite((char *) &buf[3], sizeof(char), len, fp) != len) {
beep();
clear_line(win, 12, 24, TRUE);
wattrstr(win, A_BOLD, "WRITE ERROR");
wrefresh(win);
cancel_xfer(DOWN_LOAD);
fclose(fp);
/* fatal */
return(1);
}
mvwprintw(win, 7, 24, "%-5ld", block);
recv = recv + (unsigned int) len;
if (fast)
mvwprintw(win, 9, 24, "%-10ld", recv);
blk++;
block++;
}
/*
* If the length is known, give the same status
* report as uploading
*/
if (file_length != 0L && fast) {
percent = recv * 100.0 / file_length;
if (percent > 100.0)
percent = 100.0;
mvwprintw(win, 8, 24, "%0.1f%%", percent);
}
wrefresh(win);
putc_line(ACK);
}
if (file_length != 0L && fast) {
mvwaddstr(win, 8, 24, "100% ");
wrefresh(win);
}
/*
* If the file length is not known, search backwards from
* the end of the file until you find a character that is
* not the ^Z padding character.
*/
if (file_length == 0L) {
for (i=block_size+2; i>2; i--) {
if (buf[i] != CTRLZ)
break;
}
file_length = recv - (unsigned int) block_size + (unsigned int) i -2L;
fclose(fp);
if (fix_length(file, file_length)) {
beep();
clear_line(win, 12, 24, TRUE);
wattrstr(win, A_BOLD, "TRUNCATE ERROR");
wrefresh(win);
sleep(1);
}
}
else
fclose(fp);
/* ACK the EOT */
putc_line(ACK);
}
return(0);
}
/*
* Send the first character to start the transmission and set the error
* checking method. Returns the standard error codes or 0 on success.
* The variables err_method and block_size are global.
*/
static int
send_first(win, max_block, default_err)
WINDOW *win;
int max_block, default_err;
{
int i, err_count;
unsigned int sleep();
void cancel_xfer();
/* default error method */
err_method = default_err;
if (default_err == CRC_CHECKSUM)
err_method = CRC;
/* send the first char */
err_count = 0;
while (err_count < MAX_ERRORS*2) {
mvwprintw(win, 10, 24, "%-2d", err_count);
/* check for keyboard abort */
if (wgetch(win) == ESC) {
beep();
clear_line(win, 12, 24, TRUE);
waddstr(win, "ABORTED");
wrefresh(win);
cancel_xfer(DOWN_LOAD);
sleep(3);
return(ABORT);
}
/* switch to checksum? */
if (default_err == CRC_CHECKSUM && err_count > MAX_ERRORS/2)
err_method = CHECKSUM;
/* send error method code */
clear_line(win, 5, 24, TRUE);
switch (err_method) {
case CHECKSUM:
waddstr(win, "CHECKSUM");
putc_line(NAK);
break;
case CRC:
waddstr(win, "CRC");
putc_line('C');
break;
case NONE:
waddstr(win, "NONE");
putc_line('G');
break;
}
/*
* We've cut the delay time in half, so we double
* the allowable errors
*/
if ((i = getc_line(5)) == -1) {
err_count++;
clear_line(win, 12, 24, TRUE);
waddstr(win, "NO RESPONSE");
wrefresh(win);
continue;
}
buf[0] = i;
#ifdef DEBUG
fprintf(stderr, "send_first: got header %02x, %03o, %d\n", buf[0], buf[0], buf[0]);
#endif /* DEBUG */
switch (buf[0]) {
case SOH: /* small block follows */
block_size = 128;
return(0);
case STX: /* large block follows */
if (max_block == 1024) {
block_size = 1024;
return(0);
}
/* fall thru */
default:
err_count++;
clear_line(win, 12, 24, TRUE);
waddstr(win, "BAD HEADER");
wrefresh(win);
/* read some garbage... */
while(fread_line(buf, 1028, 1) != -1)
;
putc_line(NAK);
break;
}
}
beep();
clear_line(win, 12, 24, TRUE);
wattrstr(win, A_BOLD, "TIMED OUT");
wrefresh(win);
return(ERROR);
}
/*
* Receive a block of info from the host. Returns a 0 on success, a 1 on
* a duplicate block or the standard error codes. The variables
* err_method and block_size are global.
*/
int
rcv_block(win, got_hdr, max_block, blk)
WINDOW *win;
int got_hdr, max_block;
unsigned char blk;
{
int i, err_count, bad_block, out_of_sync;
unsigned short crc, calc_crc();
unsigned int packet, sleep();
unsigned char blk_compliment, calc_sum(), crc_1, crc_2;
void cancel_xfer();
err_count = 0;
while (err_count < MAX_ERRORS) {
mvwprintw(win, 10, 24, "%-2d", err_count);
mvwprintw(win, 11, 24, "%-3d", tot_err);
/* scan the keyboard for abort */
if (wgetch(win) == ESC) {
beep();
clear_line(win, 12, 24, TRUE);
waddstr(win, "ABORTED");
wrefresh(win);
cancel_xfer(DOWN_LOAD);
sleep(3);
return(ABORT);
}
/* have we already got a hdr? */
if (!got_hdr) {
if ((i = getc_line(10)) == -1) {
err_count++;
tot_err++;
clear_line(win, 12, 24, TRUE);
waddstr(win, "NO RESPONSE");
wrefresh(win);
continue;
}
buf[0] = i;
#ifdef DEBUG
fprintf(stderr, "rcv_block: got header %02x, %03o, %d\n", buf[0], buf[0], buf[0]);
#endif /* DEBUG */
/* what'd we get? */
switch (buf[0]) {
case EOT: /* we're done! */
clear_line(win, 12, 24, TRUE);
waddstr(win, "TRANSFER COMPLETE");
wrefresh(win);
sleep(1);
return(0);
case SOH: /* small block follows */
block_size = 128;
break;
case STX: /* large block follows */
if (max_block == 1024) {
block_size = 1024;
break;
}
/* fall thru... */
default:
err_count++;
tot_err++;
clear_line(win, 12, 24, TRUE);
waddstr(win, "BAD HEADER");
wrefresh(win);
/* read some garbage... */
while(fread_line(buf, 1028, 1) != -1)
;
putc_line(NAK);
continue;
}
}
/* read the rest of the packet */
packet = block_size + 2 + (err_method == CHECKSUM ? 1 : 2);
if (fread_line(&buf[1], packet, 10) == -1) {
clear_line(win, 12, 24, TRUE);
waddstr(win, "TIMED OUT");
wrefresh(win);
putc_line(NAK);
err_count++;
tot_err++;
continue;
}
/*
* Validation of the packet includes checking the
* block number, its complement, and the crc/checksum.
*/
out_of_sync = 0;
blk_compliment = ~blk;
if (buf[1] != blk || buf[2] != blk_compliment)
out_of_sync++;
bad_block = 0;
switch (err_method) {
case CHECKSUM:
#ifdef DEBUG
fprintf(stderr, "blk=%d, checksum=%d\n", blk, calc_sum(&buf[3], block_size));
#endif /* DEBUG */
if (buf[block_size +3] != calc_sum(&buf[3], block_size))
bad_block++;
break;
case CRC:
crc = calc_crc(&buf[3], block_size);
crc_1 = crc >> 8;
crc_2 = crc;
#ifdef DEBUG
fprintf(stderr, "blk=%d, crc1=%d, crc2=%d\n", blk, crc_1, crc_2);
#endif /* DEBUG */
if (buf[block_size +3] != crc_1 || buf[block_size +4] != crc_2)
bad_block++;
break;
case NONE:
return(0);
}
/* handle errors */
if (bad_block) {
clear_line(win, 12, 24, TRUE);
if (err_method == CRC)
waddstr(win, "CRC FAILED");
else
waddstr(win, "CHECKSUM FAILED");
wrefresh(win);
putc_line(NAK);
err_count++;
tot_err++;
continue;
}
/* not really an error */
if (out_of_sync) {
/*
* If a perfect packet is off by 1 block number,
* (a lost ACK could cause this) then treat it as
* a good block but don't write it to disk.
*/
if (buf[1] == (unsigned char) blk-1)
return(1);
clear_line(win, 12, 24, TRUE);
waddstr(win, "OUT OF SYNC");
wrefresh(win);
putc_line(NAK);
err_count++;
tot_err++;
continue;
}
return(0);
}
beep();
clear_line(win, 12, 24, TRUE);
waddstr(win, "TOO MANY ERRORS");
wrefresh(win);
cancel_xfer(DOWN_LOAD);
return(ERROR);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.