This is x_send.c in view mode; [Download] [Up]
/*
* Send a list of files using a version of Ward Christensen's file
* transfer protocol. A non-zero return code means an error must be
* acknowledged by the user (a user generated abort returns a 0).
*/
#include <stdio.h>
#include <curses.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "config.h"
#include "dial_dir.h"
#include "misc.h"
#include "xmodem.h"
static int tot_err, err_method;
int
send_xmodem(win, list, type, fast)
WINDOW *win;
char *list;
int type, fast;
{
extern char *protocol[];
FILE *fp, *my_fopen();
int i, block_size, file_count, secs, mins, hours, big_blocks;
int small_blocks, err_count, got_it, num, is_batch, code;
int max_block, default_err;
static int rcv_first();
long size, block, sent, xmit_size;
char *file, *strtok(), *name, *strrchr();
unsigned short crc, calc_crc();
unsigned char buf[1029], blk, calc_sum();
unsigned int packet, sleep();
float performance, percent;
struct stat stbuf;
/* which protocol? */
switch (type) {
case XMODEM:
is_batch = 0;
default_err = CRC_CHECKSUM;
max_block = 128;
performance = 1.36;
break;
case XMODEM_1k:
is_batch = 0;
default_err = CRC_CHECKSUM;
max_block = 1024;
performance = 1.09;
break;
case MODEM7:
is_batch = 1;
default_err = CHECKSUM;
max_block = 128;
performance = 1.36;
break;
case YMODEM:
is_batch = 1;
default_err = CRC;
max_block = 1024;
performance = 1.09;
break;
case YMODEM_G:
is_batch = 1;
default_err = NONE;
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 ");
/* each one in the list */
file = strtok(list, " \t");
do {
/* is it a batch type? */
file_count++;
if (file_count > 1 && !is_batch)
break;
/* display the name */
clear_line(win, 3, 24, TRUE);
if ((name = strrchr(file, '/')))
name++;
else
name = file;
waddstr(win, name);
wrefresh(win);
/* get the file size */
if (stat(file, &stbuf) < 0) {
beep();
clear_line(win, 12, 24, TRUE);
wattrstr(win, A_BOLD, "CAN'T FIND FILE");
wrefresh(win);
sleep(3);
continue;
}
/* sanity checking */
if ((stbuf.st_mode & S_IFREG) != S_IFREG) {
beep();
clear_line(win, 12, 24, TRUE);
wattrstr(win, A_BOLD, "NOT REGULAR FILE");
wrefresh(win);
sleep(3);
continue;
}
size = stbuf.st_size;
mvwprintw(win, 4, 24, "%-10ld", size);
clear_line(win, 5, 24, TRUE);
if (!(fp = my_fopen(file, "r"))) {
beep();
clear_line(win, 12, 24, TRUE);
wattrstr(win, A_BOLD, "PERMISSION DENIED");
wrefresh(win);
sleep(3);
continue;
}
/* get the xmit size */
block_size = max_block;
big_blocks = 0;
small_blocks = 0;
if (block_size == 128) {
small_blocks = size / 128;
if (size % 128)
small_blocks++;
}
else {
big_blocks = size / 1024;
small_blocks = (size % 1024) / 128;
if (size % 128)
small_blocks++;
if (small_blocks == 8 && !big_blocks) {
big_blocks++;
small_blocks = 0;
}
/* if tiny file */
if (big_blocks == 0)
block_size = 128;
}
xmit_size = ((unsigned int) big_blocks * 1024L) + ((unsigned int) small_blocks * 128L);
/* add block 0 to the size */
if (type == YMODEM || type == YMODEM_G)
xmit_size += 128L;
secs = (xmit_size * 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, " ");
mvwaddstr(win, 8, 24, "0% ");
mvwaddstr(win, 9, 24, "0 ");
mvwaddstr(win, 10, 24, "0 ");
clear_line(win, 12, 24, TRUE);
waddstr(win, "NONE");
wrefresh(win);
/* send the batch stuff */
switch (type) {
case MODEM7:
if (code = rcv_first(win, default_err)) {
fclose(fp);
return(code +1);
}
if (send_modem7(win, name)) {
fclose(fp);
return(1);
}
break;
case YMODEM:
case YMODEM_G:
if (code = rcv_first(win, default_err)) {
fclose(fp);
return(code +1);
}
if (code = send_ymodem(win, name, size)) {
fclose(fp);
/*
* CANCEL now means that the other
* end can't open that file.
*/
if (code == CANCEL)
break;
return(code +1);
}
xmit_size -= 128L;
break;
default:
code = 0;
break;
}
/* remote can't receive that file? */
if (code == CANCEL)
break;
/* wait for first character */
if (code = rcv_first(win, default_err)) {
fclose(fp);
return(code +1);
}
/* here we go... */
clear_line(win, 12, 24, TRUE);
waddstr(win, "NONE");
wrefresh(win);
sent = 0L;
block = 1L;
blk = 1;
while (num = fread((char *) &buf[3], sizeof(char), block_size, fp)) {
/* fill short block */
if (num < block_size) {
for (i=num; i<block_size; i++)
buf[i+3] = CTRLZ;
}
/* show current stats */
mvwprintw(win, 7, 24, "%-5ld", block);
if (fast) {
percent = sent * 100.0 / xmit_size;
mvwprintw(win, 8, 24, "%0.1f%%", percent);
mvwprintw(win, 9, 24, "%-10ld", sent);
}
wrefresh(win);
/* build the header */
if (block_size == 128)
buf[0] = SOH;
else
buf[0] = STX;
buf[1] = blk;
buf[2] = ~blk;
/* build the error detection stuff */
switch (err_method) {
case CHECKSUM:
buf[block_size+3] = calc_sum(&buf[3], block_size);
#ifdef DEBUG
fprintf(stderr, "blk=%d, checksum=%d\n", blk, buf[block_size+3]);
#endif /* DEBUG */
packet = block_size +4;
break;
case CRC:
crc = calc_crc(&buf[3], block_size);
buf[block_size+3] = crc >> 8;
buf[block_size+4] = crc;
#ifdef DEBUG
fprintf(stderr, "blk=%d, crc1=%d, crc2=%d\n", blk, buf[block_size+3], buf[block_size+4]);
#endif /* DEBUG */
packet = block_size +5;
break;
case NONE:
buf[block_size+3] = 0;
buf[block_size+4] = 0;
packet = block_size +5;
break;
}
/* send the block */
if (code = send_block(win, buf, packet)) {
fclose(fp);
return(code +1);
}
block++;
blk++;
sent = sent + (unsigned int) block_size;
/* change block size? */
if (xmit_size - sent < 1024)
block_size = 128;
}
mvwaddstr(win, 8, 24, "100% ");
mvwprintw(win, 9, 24, "%-10ld", sent);
/* at the end of the file */
err_count = 0;
got_it = 0;
while (err_count < MAX_ERRORS) {
putc_line(EOT);
if (getc_line(10) == ACK) {
got_it++;
break;
}
err_count++;
}
clear_line(win, 12, 24, TRUE);
if (!got_it) {
/*
* So what??? We don't do anything if there is
* no acknowledge from the host!!
*/
waddstr(win, "NO ACKNOWLEDGE");
}
else
waddstr(win, "TRANSFER COMPLETE");
if (!is_batch)
beep();
wrefresh(win);
sleep(2);
/* prepare to start again */
fclose(fp);
} while (file = strtok((char *) NULL, " \t"));
/*
* The end of batch markers... For modem7 it's an ACK and EOT, for
* ymodem, it's an empty block 0.
*/
switch (type) {
case MODEM7:
if (code = rcv_first(win, default_err))
return(code +1);
putc_line(ACK);
putc_line(EOT);
beep();
wrefresh(win);
break;
case YMODEM:
case YMODEM_G:
if (code = rcv_first(win, default_err))
return(code +1);
if (code = send_ymodem(win, "", 0L))
return(code +1);
beep();
wrefresh(win);
break;
default:
break;
}
return(0);
}
/*
* Wait for the first character to start the transmission. This first
* character also sets the crc/checksum method. Returns the standard
* error codes, or 0 on success. The variable err_method is global.
*/
static int
rcv_first(win, default_err)
WINDOW *win;
int default_err;
{
int i, err_count;
unsigned int sleep();
void cancel_xfer();
err_count = 0;
while (err_count < MAX_ERRORS) {
/* scan the keyboard for abort */
if (wgetch(win) == ESC) {
beep();
clear_line(win, 12, 24, TRUE);
waddstr(win, "ABORTED");
wrefresh(win);
cancel_xfer(UP_LOAD);
sleep(3);
return(ABORT);
}
/* scan the TTY line */
i = getc_line(10);
#ifdef DEBUG
fprintf(stderr, "rcv_first: got \"%c\", %02x, %03o, %d\n", i, i, i, i);
#endif /* DEBUG */
switch (i) {
case -1: /* timed out */
clear_line(win, 12, 24, TRUE);
wattrstr(win, A_BOLD, "NO RESPONSE");
err_count++;
break;
case NAK: /* checksum marker */
if (default_err == CHECKSUM || default_err == CRC_CHECKSUM) {
mvwaddstr(win, 5, 24, "CHECKSUM");
err_method = CHECKSUM;
return(0);
}
err_count++;
break;
case 'C': /* CRC marker */
if (default_err == CRC_CHECKSUM || default_err == CRC) {
mvwaddstr(win, 5, 24, "CRC");
err_method = CRC;
return(0);
}
err_count++;
break;
case 'G': /* ymodem-g marker */
if (default_err == NONE) {
mvwaddstr(win, 5, 24, "NONE");
err_method = NONE;
return(0);
}
err_count++;
break;
case CAN: /* two CAN's and you're out! */
if (getc_line(2) == CAN) {
beep();
clear_line(win, 12, 24, TRUE);
wattrstr(win, A_BOLD, "REMOTE ABORTED");
wrefresh(win);
return(CANCEL);
}
err_count++;
break;
default:
clear_line(win, 12, 24, TRUE);
waddstr(win, "BAD HEADER");
err_count++;
break;
}
mvwprintw(win, 10, 24, "%-2d", err_count);
wrefresh(win);
}
/* failed to get it right? */
beep();
clear_line(win, 12, 24, TRUE);
wattrstr(win, A_BOLD, "TIMED OUT");
wrefresh(win);
return(ERROR);
}
/*
* Send a block of data, scan the keyboard for a user abort, and check
* the return codes from the host. Returns standard error codes or 0
* on success.
*/
int
send_block(win, blk, packet)
WINDOW *win;
unsigned char *blk;
unsigned int packet;
{
extern int fd;
int i, err_count;
void cancel_xfer();
unsigned int sleep();
err_count = 0;
mvwaddstr(win, 10, 24, "0 ");
while (err_count < MAX_ERRORS) {
/* write the block */
write(fd, (char *) blk, packet);
/* scan the keyboard */
if (wgetch(win) == ESC) {
beep();
clear_line(win, 12, 24, TRUE);
waddstr(win, "ABORTED");
wrefresh(win);
cancel_xfer(UP_LOAD);
sleep(3);
return(ABORT);
}
/* ymodem-g doesn't need ACKs */
if (err_method == NONE)
return(0);
/* wait for acknowledge */
i = getc_line(10);
#ifdef DEBUG
fprintf(stderr, "send_block: got \"%c\", %02x, %03o, %d\n", i, i, i, i);
#endif /* DEBUG */
switch (i) {
case -1: /* timed out */
clear_line(win, 12, 24, TRUE);
waddstr(win, "NO RESPONSE");
err_count++;
tot_err++;
break;
case ACK: /* Hooray!! we got it */
return(0);
case NAK: /* show our disappointment... */
clear_line(win, 12, 24, TRUE);
if (err_method == CRC)
waddstr(win, "CRC FAILED");
else
waddstr(win, "CHECKSUM FAILED");
err_count++;
tot_err++;
break;
case CAN: /* two CAN's and you're out! */
if (getc_line(2) == CAN) {
beep();
clear_line(win, 12, 24, TRUE);
wattrstr(win, A_BOLD, "REMOTE ABORTED");
wrefresh(win);
return(CANCEL);
}
/* fall thru... */
default:
clear_line(win, 12, 24, TRUE);
waddstr(win, "RESENDING");
err_count++;
tot_err++;
break;
}
/* flush any pending garbage */
tty_flush(fd, 0);
mvwprintw(win, 10, 24, "%-2d", err_count);
mvwprintw(win, 11, 24, "%-3d", tot_err);
wrefresh(win);
}
/* failed to get it right */
beep();
clear_line(win, 12, 24, TRUE);
wattrstr(win, A_BOLD, "TOO MANY ERRORS");
wrefresh(win);
cancel_xfer(UP_LOAD);
return(ERROR);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.