home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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.
- */
-
- #include <stdio.h>
- #include <curses.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include "dial_dir.h"
- #include "status.h"
- #include "misc.h"
- #include "xmodem.h"
-
- int tot_err, err_method;
-
- int
- send_xmodem(win, list, type, fast)
- WINDOW *win;
- char *list;
- int type, fast;
- {
- FILE *fp;
- int i, block_size, file_count, secs, mins, hours, size, xmit_size;
- int big_blocks, small_blocks, err_count, got_it, num, block, sent;
- int crc, is_batch, code, max_block, default_err;
- char *file, *strtok(), *name, *strrchr();
- unsigned char buf[1029], blk, calc_sum();
- unsigned int packet, sleep();
- float performance, percent;
- struct stat sbuf;
-
- /*
- * What type of xmodem? The default_err means: 0=checksum only,
- * 1=checksum or CRC, 2=CRC only, and 3=none.
- */
- switch(type) {
- case XMODEM:
- mvwaddstr(win, 2, 24, "xmodem");
- is_batch = 0;
- default_err = 1;
- max_block = 128;
- performance = 1.36;
- break;
- case XMODEM_1k:
- mvwaddstr(win, 2, 24, "xmodem-1k");
- is_batch = 0;
- default_err = 1;
- max_block = 1024;
- performance = 1.09;
- break;
- case MODEM7:
- mvwaddstr(win, 2, 24, "modem7");
- is_batch = 1;
- default_err = 0;
- max_block = 128;
- performance = 1.36;
- break;
- case YMODEM:
- mvwaddstr(win, 2, 24, "ymodem");
- is_batch = 1;
- default_err = 2;
- max_block = 1024;
- performance = 1.09;
- break;
- case YMODEM_G:
- mvwaddstr(win, 2, 24, "ymodem-g");
- is_batch = 1;
- default_err = 3;
- max_block = 1024;
- performance = 1.02;
- break;
- }
-
- tot_err = 0;
- file_count = 0;
- mvwaddstr(win, 11, 24, "0 ");
-
- /* each one in the list */
- file = strtok(list, " ");
- do {
- /* is it a batch type ? */
- file_count++;
- if (file_count > 1 && !is_batch)
- break;
- /* display the name */
- clear_line(win, 3, 24, 1);
- if ((name = strrchr(file, '/')))
- name++;
- else
- name = file;
- waddstr(win, name);
- wrefresh(win);
- /* get the file size */
- if (stat(file, &sbuf) < 0) {
- beep();
- clear_line(win, 12, 24, 1);
- wattrstr(win, A_BOLD, "CAN'T FIND FILE");
- wrefresh(win);
- sleep(3);
- continue;
- }
-
- size = sbuf.st_size;
- mvwprintw(win, 4, 24, "%-10d", size);
- clear_line(win, 5, 24, 1);
-
- if (access(file, 4)) {
- beep();
- clear_line(win, 12, 24, 1);
- wattrstr(win, A_BOLD, "PERMISSION DENIED");
- wrefresh(win);
- sleep(3);
- continue;
- }
- if (!(fp = fopen(file, "r"))) {
- beep();
- clear_line(win, 12, 24, 1);
- wattrstr(win, A_BOLD, "CAN'T OPEN FILE");
- 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 = (big_blocks * 1024) + (small_blocks * 128);
- /* add block 0 to the size */
- if (type == YMODEM || type == YMODEM_G)
- xmit_size += 128;
-
- 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, 1);
- 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 -= 128;
- 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... */
- sent = 0;
- block = 1;
- blk = 1;
- while (num = fread((char *) &buf[3], sizeof(buf[0]), 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, "%-5d", block);
- if (fast) {
- percent = sent * 100.0 / xmit_size;
- mvwprintw(win, 8, 24, "%0.1f%%", percent);
- mvwprintw(win, 9, 24, "%-10d", 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 0: /* checksum */
- buf[block_size+3] = calc_sum(&buf[3], block_size);
- packet = block_size +4;
- break;
- case 1: /* CRC */
- crc = calc_crc(&buf[3], block_size);
- buf[block_size+3] = crc >> 8;
- buf[block_size+4] = crc;
- packet = block_size +5;
- break;
- case 2: /* 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 += block_size;
-
- /* change block size ? */
- if (xmit_size - sent < 1024)
- block_size = 128;
- }
- mvwaddstr(win, 8, 24, "100% ");
- mvwprintw(win, 9, 24, "%-10d", 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, 1);
- 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, " "));
-
- /*
- * 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, NULL, 0))
- 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.
- */
-
- int
- rcv_first(win, default_err)
- WINDOW *win;
- int default_err;
- {
- int err_count;
- unsigned int sleep();
-
- err_count = 0;
- while (err_count < MAX_ERRORS) {
-
- /* scan the keyboard for abort */
- if (wgetch(win) == 27) {
- beep();
- clear_line(win, 12, 24, 1);
- waddstr(win, "ABORTED");
- wrefresh(win);
- sleep(3);
- return(ABORT);
- }
-
- /* scan the tty line */
- switch(getc_line(10)) {
- case NAK: /* checksum marker */
- if (default_err < 2) {
- mvwaddstr(win, 5, 24, "CHECKSUM");
- err_method = 0;
- return(0);
- }
- err_count++;
- break;
- case 'C': /* CRC marker */
- if (default_err == 1 || default_err == 2) {
- mvwaddstr(win, 5, 24, "CRC");
- err_method = 1;
- return(0);
- }
- err_count++;
- break;
- case 'G': /* ymodem-g marker */
- if (default_err == 3) {
- mvwaddstr(win, 5, 24, "NONE");
- err_method = 2;
- 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, 1);
- wattrstr(win, A_BOLD, "REMOTE ABORTED");
- wrefresh(win);
- return(CANCEL);
- }
- /* fall thru... */
- default:
- err_count++;
- break;
- }
- clear_line(win, 12, 24, 1);
- waddstr(win, "BAD HEADER");
- mvwprintw(win, 10, 24, "%-2d", err_count);
- wrefresh(win);
- }
- /* failed to get it right ? */
- beep();
- clear_line(win, 12, 24, 1);
- 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;
- {
- int err_count;
-
- err_count = 0;
- while (err_count < MAX_ERRORS) {
- /* write the block */
- write(status->fd, (char *) blk, packet);
- /* scan the keyboard */
- if (wgetch(win) == 27) {
- beep();
- clear_line(win, 12, 24, 1);
- waddstr(win, "ABORTED");
- wrefresh(win);
- sleep(3);
- return(ABORT);
- }
- /* ymodem-g doesn't need ACKs */
- if (err_method == 2)
- return(0);
- /* wait for acknowledge */
- switch(getc_line(10)) {
- case ACK: /* Hooray!! we got it */
- return(0);
- case NAK: /* show our disappointment... */
- clear_line(win, 12, 24, 1);
- if (err_method)
- 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, 1);
- wattrstr(win, A_BOLD, "REMOTE ABORTED");
- wrefresh(win);
- return(CANCEL);
- }
- /* fall thru... */
- default:
- clear_line(win, 12, 24, 1);
- waddstr(win, "RESENDING");
- err_count++;
- tot_err++;
- break;
- }
- 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, 1);
- wattrstr(win, A_BOLD, "TOO MANY ERRORS");
- wrefresh(win);
- return(ERROR);
- }
-