Pcomm v1.1 (7 of 8)
Emmet Gray
egray at killer.DALLAS.TX.US
Mon Sep 5 04:06:03 AEST 1988
This is part 7 (of 8) to the Pcomm v1.1 release package.
Emmet P. Gray US Army, HQ III Corps & Fort Hood
..!uunet!uiucuxc!fthood!egray Attn: AFZF-DE-ENV
DEH, Environmental Management Office
Fort Hood, TX 76544-5057
------------------------------------------------------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
# vcs.c
# x_ascii.c
# x_batch.c
# x_extrnl.c
# x_menu.c
# x_rcv.c
# This archive created: Sat Sep 3 15:35:07 1988
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'vcs.c'" '(10169 characters)'
if test -f 'vcs.c'
then
echo shar: "will not over-write existing file 'vcs.c'"
else
sed 's/^X//' << \SHAR_EOF > 'vcs.c'
X/*
X * Routines for VCS detection.
X */
X
X#include <stdio.h>
X#include <curses.h>
X#include "config.h"
X#ifndef OLDCURSES
X#include <term.h>
X#endif /* OLDCURSES */
X#include "vcs.h"
X
Xstatic int putc_cnt;
Xstatic char putc_buf[VCS_SIZE];
X
X/*
X * Test for possible VCS (video command sequence). A character return
X * code means no match. An return code greater than 255 means a VCS
X * was found.
X */
X
Xint
Xvcs_filter(c)
Xchar c;
X{
X extern int vcs_codes[NUM_VCS][VCS_SIZE], vcs_leadin[NUM_VCS];
X extern int num_leadin;
X static int buf[VCS_SIZE];
X static int ptr = 0;
X register int i;
X int maybe, possible;
X
X /* see if possible */
X possible = 0;
X if (ptr == 0) {
X /* lead-in less than a space */
X if (c >= ' ')
X return(c & 0xff);
X /* check the list */
X for (i=0; i<num_leadin; i++) {
X if (c == vcs_leadin[i]) {
X possible++;
X break;
X }
X }
X if (!possible)
X return(c & 0xff);
X }
X
X /* build the string */
X buf[ptr++] = c;
X buf[ptr] = -1;
X /* test for match */
X maybe = 0;
X for (i=0; i<NUM_VCS; i++) {
X switch (match_codes(buf, vcs_codes[i], i)) {
X case YES:
X ptr = 0;
X return(i+256);
X case NO:
X break;
X case MAYBE:
X maybe++;
X break;
X }
X }
X /* abandon what you've got */
X if (maybe && ptr == VCS_SIZE-1) {
X ptr = 0;
X return(c & 0xff);
X }
X /* hang on, wait and see */
X if (maybe)
X return(MAYBE);
X /* a clean miss */
X ptr = 0;
X return(c & 0xff);
X}
X
X/*
X * See if the two integer arrays "match". Character parameters are
X * designated by codes > 1000 and ASCII digit parameters are designated
X * by codes > 2000. Uses a simple linear search, so if NUM_VCS grows
X * this routine will have to mature a bit.
X */
X
Xstatic int
Xmatch_codes(test, code, k)
Xint test[], code[], k;
X{
X extern int vcs_param[NUM_VCS][5];
X register int i, j;
X int pos, done;
X /* doesn't exist */
X if (code[0] == -1)
X return(NO);
X
X i = 0;
X j = 0;
X while (i<VCS_SIZE && j<VCS_SIZE) {
X /* at the end (a match) */
X if (test[i] == -1 && code[j] == -1)
X return(YES);
X /* ran out of input */
X if (test[i] == -1)
X break;
X /*
X * The char parameter (code 1000) always matches the
X * character.
X */
X if (code[j] >= 1000 && code[j] < 2000) {
X pos = code[j] -1000;
X vcs_param[k][pos] = test[i];
X i++;
X j++;
X continue;
X }
X /*
X * The digit parameter (code 2000) tries to match as many
X * ASCII digits as it can.
X */
X if (code[j] >= 2000) {
X pos = code[j] -2000;
X /* done with this number? */
X if (vcs_param[k][pos])
X done = 1;
X else
X done = 0;
X /* only digits */
X while (test[i] >= 48 && test[i] <= 57) {
X if (!done)
X vcs_param[k][pos] = (vcs_param[k][pos] * 10) + test[i] -48;
X i++;
X }
X /* ended in a digit */
X if (test[i] == -1 && code[j+1] != -1) {
X vcs_param[k][pos] = 0;
X break;
X }
X j++;
X continue;
X }
X /* a clean miss */
X if (test[i] != code[j]) {
X for (j=0; j<5; j++)
X vcs_param[k][j] = 0;
X return(NO);
X }
X i++;
X j++;
X }
X /* a maybe */
X return(MAYBE);
X}
X
X/*
X * Build the table of VCS codes. Actually we cheat... We tell curses(3)
X * to build the strings to perform the function, and then we decipher
X * what it did.
X */
X
Xvoid
Xvcs_table()
X{
X extern int vcs_codes[NUM_VCS][VCS_SIZE], vcs_opt[NUM_VCS][10];
X extern int vcs_leadin[NUM_VCS], num_leadin, max_row, max_col;
X int i, j, k, match, temp[VCS_SIZE];
X char *p, *strcpy(), buf[VCS_SIZE], *getenv(), *tparm();
X void fake_it();
X
X#ifdef OLDCURSES
X char tcbuf[1024], tb[1024], *t, *cursor_home, *clr_eol, *clr_eos;
X char *clear_screen, *cursor_up, *cursor_down, *cursor_right;
X char *cursor_left, *cursor_address, *getenv(), *tgetstr(), *tgoto();
X
X tgetent(tb, getenv("TERM"));
X t = tcbuf;
X
X cursor_home = tgetstr("ho", &t);
X clr_eol = tgetstr("ce", &t);
X clr_eos = tgetstr("cd", &t);
X clear_screen = tgetstr("cl", &t);
X cursor_up = tgetstr("up", &t);
X cursor_down = tgetstr("do", &t);
X cursor_right = tgetstr("nd", &t);
X cursor_left = tgetstr("le", &t);
X cursor_address = tgetstr("cm", &t);
X max_row = tgetnum("li");
X max_col = tgetnum("co");
X#else /* OLDCURSES */
X setupterm(getenv("TERM"), 1, &i);
X max_row = lines;
X max_col = columns;
X#endif /* OLDCURSES */
X
X /*
X * Do the easy ones first. These don't take positional parameters,
X * so all we have to do is strip the padding info.
X */
X for (i=0; i<NUM_VCS; i++) {
X switch (i) {
X case HOME:
X p = cursor_home;
X break;
X case CLR_EOL:
X p = clr_eol;
X break;
X case CLR_EOS:
X p = clr_eos;
X break;
X case CLEAR:
X p = clear_screen;
X break;
X case MV_UP:
X p = cursor_up;
X break;
X case MV_DOWN:
X p = cursor_down;
X break;
X case MV_RIGHT:
X p = cursor_right;
X break;
X case MV_LEFT:
X p = cursor_left;
X break;
X default:
X p = "";
X break;
X }
X /*
X * Either the capability doesn't exist, or we're gonna
X * do this one by hand (i.e.: ones with positional parameters)
X */
X if (!p) {
X vcs_codes[i][0] = -1;
X continue;
X }
X /* fake an "output" */
X fake_it(p);
X /* copy what it did */
X j = 0;
X while (putc_buf[j]) {
X vcs_codes[i][j] = putc_buf[j];
X j++;
X if (j == VCS_SIZE-1)
X break;
X }
X vcs_codes[i][j] = -1;
X }
X
X /*
X * And now for the difficult ones. The way it's done is: load the
X * string with a few known parameters and then find where the
X * parameters end up. The vcs_opt[][] array is "free-flowing"
X * and means something only to the routine being used.
X */
X /* add one to the param */
X if (substr(cursor_address, "%i") > 0)
X vcs_opt[MV_DIRECT][0] = 1;
X /* decimal codes used */
X if (substr(cursor_address, "%d") > 0)
X vcs_opt[MV_DIRECT][1] = 1;
X /* character codes used */
X if (substr(cursor_address, "%c") > 0)
X vcs_opt[MV_DIRECT][2] = 1;
X /* add an offset */
X if (substr(cursor_address, "%+") > 0)
X vcs_opt[MV_DIRECT][3] = 1;
X /* subtract an offset */
X if (substr(cursor_address, "%-") > 0)
X vcs_opt[MV_DIRECT][4] = 1;
X /* load with parameters 12 & 34 */
X#ifdef OLDCURSES
X fake_it(tgoto(cursor_address, 12, 34));
X#else /* OLDCURSES */
X fake_it(tparm(cursor_address, 12, 34));
X#endif /* OLDCURSES */
X j = 0;
X while (putc_buf[j]) {
X temp[j] = putc_buf[j];
X j++;
X if (j == VCS_SIZE-1)
X break;
X }
X temp[j] = -1;
X /* if decimal parameters */
X if (vcs_opt[MV_DIRECT][1]) {
X /* if add one */
X if (vcs_opt[MV_DIRECT][0])
X sprintf(buf, "13");
X else
X sprintf(buf, "12");
X /* where is the 12 (or 13)? */
X if ((i = substr(putc_buf, buf)) > 0) {
X temp[i] = 2000;
X temp[i+1] = -2;
X }
X else
X temp[0] = -1;
X /* if add one */
X if (vcs_opt[MV_DIRECT][0])
X sprintf(buf, "35");
X else
X sprintf(buf, "34");
X /* where is the 34 (or 35)? */
X if ((i = substr(putc_buf, buf)) > 0) {
X temp[i] = 2001;
X temp[i+1] = -2;
X }
X else
X temp[0] = -1;
X }
X /* if character parameters */
X if (vcs_opt[MV_DIRECT][2]) {
X /* original with 12 and 34 */
X strcpy(buf, putc_buf);
X /* change 12 to 13 */
X#ifdef OLDCURSES
X fake_it(tgoto(cursor_address, 13, 34));
X#else /* OLDCURSES */
X fake_it(tparm(cursor_address, 13, 34));
X#endif /* OLDCURSES */
X /* where are they different */
X i = 0;
X while (buf[i] != NULL) {
X if (buf[i] != putc_buf[i])
X break;
X i++;
X }
X /* sanity checking */
X if (buf[i] == NULL)
X temp[0] = -1;
X /* if add, what is offset? */
X if (vcs_opt[MV_DIRECT][3])
X vcs_opt[MV_DIRECT][5] = temp[i] - 13;
X
X /* if subtract, what is offset? */
X if (vcs_opt[MV_DIRECT][4])
X vcs_opt[MV_DIRECT][5] = 13 - temp[i];
X
X temp[i] = 1000;
X /* change 34 to 35 */
X#ifdef OLDCURSES
X fake_it(tgoto(cursor_address, 12, 35));
X#else /* OLDCURSES */
X fake_it(tparm(cursor_address, 12, 35));
X#endif /* OLDCURSES */
X /* where are they different */
X i = 0;
X while (buf[i] != NULL) {
X if (buf[i] != putc_buf[i])
X break;
X i++;
X }
X temp[i] = 1001;
X if (buf[i] == NULL)
X temp[0] = -1;
X }
X /* strip the -2's out, if any */
X i = 0;
X j = 0;
X while (temp[i] != -1) {
X if (temp[i] != -2)
X vcs_codes[MV_DIRECT][j++] = temp[i];
X i++;
X }
X vcs_codes[MV_DIRECT][j] = -1;
X
X /*
X * Simplify the list. Some codes are already handled by the
X * virtual screen routines... no need to duplicate them.
X */
X if (vcs_codes[MV_DOWN][0] == '\n')
X vcs_codes[MV_DOWN][0] = -1;
X
X if (vcs_codes[MV_LEFT][0] == 8)
X vcs_codes[MV_LEFT][0] = -1;
X
X /*
X * Often the "clear screen" sequence will contain the "home"
X * sequence... if so, don't duplicate the "home" portion.
X */
X fake_it(cursor_home);
X strcpy(buf, putc_buf);
X
X fake_it(clear_screen);
X /* if "home" inside "clear screen" */
X if ((k = substr(putc_buf, buf)) >= 0) {
X /* if at the beginning */
X if (k == 0) {
X i = 0;
X for (j=strlen(buf); j<VCS_SIZE; j++)
X vcs_codes[CLEAR][i++] = putc_buf[j];
X vcs_codes[CLEAR][i] = -1;
X }
X /* if at the end */
X else if (strlen(buf)+k == strlen(putc_buf))
X vcs_codes[CLEAR][k] = -1;
X }
X /* is "clear screen" still unique */
X k = 0;
X for (i=0; i<NUM_VCS; i++) {
X if (vcs_codes[CLEAR][i] == -1 || vcs_codes[CLR_EOS][i] == -1)
X break;
X if (vcs_codes[CLEAR][i] != vcs_codes[CLR_EOS][i]) {
X k++;
X break;
X }
X }
X if (k == 0)
X vcs_codes[CLEAR][0] = -1;
X
X /*
X * Make a list of unique lead-in characters to be used as a
X * simple hash table.
X */
X num_leadin = 0;
X for (i=0; i<NUM_VCS; i++) {
X if (vcs_codes[i][0] == -1)
X continue;
X /* add any new lead-in character */
X match = 0;
X for (j=0; j<num_leadin; j++) {
X if (vcs_leadin[j] == vcs_codes[i][0])
X match++;
X }
X if (!match)
X vcs_leadin[num_leadin++] = vcs_codes[i][0];
X }
X return;
X}
X
X/*
X * The routines that fakes curses(3) into outputing the string info with
X * the padding removed.
X */
Xstatic void
Xfake_it(s)
Xchar *s;
X{
X int fake_putc();
X
X putc_cnt = 0;
X putc_buf[0] = NULL;
X tputs(s, 1, fake_putc);
X putc_buf[putc_cnt] = NULL;
X return;
X}
Xstatic int
Xfake_putc(c)
Xchar c;
X{
X putc_buf[putc_cnt++] = c;
X return(c);
X}
X
X/*
X * Is string2 contained in string1? If so, return the offset otherwise
X * return a -1.
X */
X
Xstatic int
Xsubstr(s1, s2)
Xchar *s1, *s2;
X{
X int i, len;
X
X len = strlen(s2);
X /* not possible */
X if (len > strlen(s1))
X return(-1);
X
X i = 0;
X while (*s1) {
X if (!strncmp(s1, s2, len))
X return(i);
X s1++;
X i++;
X }
X return(-1);
X}
SHAR_EOF
if test 10169 -ne "`wc -c < 'vcs.c'`"
then
echo shar: "error transmitting 'vcs.c'" '(should have been 10169 characters)'
fi
fi
echo shar: "extracting 'x_ascii.c'" '(7019 characters)'
if test -f 'x_ascii.c'
then
echo shar: "will not over-write existing file 'x_ascii.c'"
else
sed 's/^X//' << \SHAR_EOF > 'x_ascii.c'
X/*
X * Transfer a file using just XON/XOFF flow control. Currently limited to
X * 7 bit ASCII codes. (If this causes too much trouble, I'll change it).
X */
X
X#define CLIST 64
X
X#include <stdio.h>
X#include <fcntl.h>
X#include <curses.h>
X#include <signal.h>
X#include "config.h"
X#include "misc.h"
X#include "param.h"
X
Xvoid
Xxfer_ascii(list, up)
Xchar *list;
Xint up;
X{
X int cr_lf;
X char *file, *strtok();
X void send_ascii(), rcv_ascii(), line_set(), st_line(), suspend();
X void load_vs(), ascii_mode();
X unsigned int sleep();
X
X touchwin(stdscr);
X refresh();
X /* only one file from list */
X file = strtok(list, " ");
X
X cr_lf = !strcmp(param->cr_out, "CR/LF");
X ascii_mode(up);
X if (up) {
X /* un-suspend the input routine */
X suspend(0);
X
X send_ascii(file, cr_lf);
X /* re-suspend the input routine */
X suspend(1);
X }
X else
X rcv_ascii(file, cr_lf);
X
X /*
X * Restoring the TTY modes is easier than setting them... The
X * fixterm() and line_set() routines fix most of the damage.
X */
X fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NDELAY);
X line_set();
X fixterm();
X
X /*
X * On downloading, the contents of the virtual screen won't contain
X * the characters shown during the transfer. Too bad...
X */
X load_vs();
X beep();
X st_line("xfer complete");
X
X sleep(2);
X return;
X}
X
X/*
X * Put the TTY line in a mode suitable for the ASCII transfer. Puts the
X * terminal in the raw, non-blocking mode.
X */
X
Xstatic void
Xascii_mode(up)
Xint up;
X{
X extern int fd;
X struct termio tbuf;
X void input_off(), term_mode();
X
X ioctl(fd, TCGETA, &tbuf);
X tbuf.c_oflag = 0;
X /* flow control & 8th bit stripping */
X if (up) {
X tbuf.c_iflag = (ISTRIP|IXON);
X
X /* use NL delays if no CR */
X if (!strcmp(param->cr_up, "STRIP"))
X tbuf.c_oflag = (OPOST|ONLRET);
X
X /* CR delay times */
X switch (param->cr_delay) {
X case 0:
X tbuf.c_oflag = 0;
X break;
X case 100:
X tbuf.c_oflag |= (OPOST|CR2);
X break;
X case 150:
X tbuf.c_oflag |= (OPOST|CR3);
X break;
X }
X }
X /* if down loading */
X else {
X tbuf.c_iflag = (ISTRIP|IXOFF);
X /* kill the input routine */
X input_off();
X }
X
X ioctl(fd, TCSETA, &tbuf);
X ioctl(fd, TCFLSH, 2);
X /* out of curses mode */
X resetterm();
X term_mode();
X /* non-blocking mode */
X fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) | O_NDELAY);
X return;
X}
X
X/*
X * Send a file. The local echo option is independent of the duplex option,
X * and would very rarely be used since the characters are most likely
X * being echoed on the screen anyway.
X */
X
Xstatic void
Xsend_ascii(file, cr_lf)
Xchar *file;
Xint cr_lf;
X{
X extern int fd;
X FILE *fp;
X int i, j, strip_cr, strip_lf, add_cr, add_lf, expand, lecho, pace;
X unsigned char c, last;
X unsigned int sleep();
X /* permission already checked */
X if (!(fp = fopen(file, "r")))
X return;
X /* ASCII transfer options */
X strip_cr = !strcmp(param->cr_up, "STRIP");
X add_lf = !strcmp(param->cr_up, "ADD LF");
X strip_lf = !strcmp(param->lf_up, "STRIP");
X add_cr = !strcmp(param->lf_up, "ADD CR");
X expand = !strcmp(param->expand, "YES");
X lecho = !strcmp(param->lecho, "YES");
X pace = !strcmp(param->pace, "YES");
X
X last = 0;
X while ((i = fgetc(fp)) != EOF) {
X /* any keyboard activity? */
X switch (j = getchar()) {
X case -1: /* no key was pressed */
X break;
X case ESC: /* <ESC> key for abort */
X fclose(fp);
X sleep(2);
X ioctl(fd, TCSBRK, 1);
X return;
X default: /* send the char */
X c = j & 0xff;
X putc_line(c);
X if (c == '\r' && cr_lf)
X putc_line('\n');
X break;
X }
X c = i & 0xff;
X /* expand blank lines */
X if (expand && last == '\n' && c == '\n')
X putc_line(' ');
X last = c;
X
X /* CR translations */
X if (c == '\r' && strip_cr)
X continue;
X if (c == '\r' && add_lf) {
X putc_line(c);
X putc_line('\n');
X continue;
X }
X /* LF translations */
X if (c == '\n' && strip_lf)
X continue;
X if (c == '\n' && add_cr) {
X putc_line('\r');
X putc_line(c);
X continue;
X }
X putc_line(c);
X /*
X * There's really no mechanism for delaying characters
X * going to the output, so we fake it by waiting for
X * each character to clear the I/O buffer.
X */
X if (pace)
X ioctl(fd, TCSBRK, 1);
X if (lecho) {
X putchar((char) c);
X fflush(stdout);
X }
X }
X fclose(fp);
X sleep(2);
X ioctl(fd, TCSBRK, 1);
X return;
X}
X
X/*
X * Receive a file. The timer is used to end the transfer. This is not
X * that much different from the data logging option. The use of bgetc_line()
X * and non-blocking input makes it seem like full duplex, but it's not.
X * Be aware that while the timer is active the keyboard is deaf. Input is
X * NOT loaded into the virtual screen!!
X */
X
Xstatic void
Xrcv_ascii(file, cr_lf)
Xchar *file;
Xint cr_lf;
X{
X FILE *fp;
X int i, strip_cr, strip_lf, add_cr, add_lf, got_first;
X unsigned int delay;
X char c;
X /* permission already checked */
X if (!(fp = fopen(file, "w")))
X return;
X /* ASCII transfer options */
X strip_cr = !strcmp(param->cr_dn, "STRIP");
X add_lf = !strcmp(param->cr_dn, "ADD LF");
X strip_lf = !strcmp(param->lf_dn, "STRIP");
X add_cr = !strcmp(param->lf_dn, "ADD CR");
X
X got_first = 0;
X delay = 1;
X while (1) {
X /* keyboard activity */
X switch (i = getchar()) {
X case -1: /* no key was pressed */
X break;
X case ESC: /* <ESC> key */
X fclose(fp);
X return;
X default: /* send it */
X c = i & 0xff;
X putc_line((unsigned char) c);
X if (c == '\r' && cr_lf)
X putc_line('\n');
X break;
X }
X /* read a character */
X if ((i = bgetc_line(delay)) == -1) {
X /*
X * The transfer timeout is not activated until the
X * first character is received. Until then, it polls
X * the line for one second and loops backs for
X * keyboard input.
X */
X if (got_first) {
X fclose(fp);
X return;
X }
X continue;
X }
X got_first = 1;
X delay = param->timer;
X c = i & 0xff;
X /* display it on the screen */
X putchar(c);
X fflush(stdout);
X /* CR translations */
X if (c == '\r' && strip_cr)
X continue;
X if (c == '\r' && add_lf) {
X fputc(c, fp);
X fputc('\n', fp);
X continue;
X }
X /* LF translations */
X if (c == '\n' && strip_lf)
X continue;
X if (c == '\n' && add_cr) {
X fputc('\r', fp);
X fputc(c, fp);
X continue;
X }
X fputc(c, fp);
X }
X}
X
X/*
X * Get a character from the line (using buffered I/O) with a specified
X * time-out period in seconds. If the function times-out, it returns a -1.
X */
X
Xstatic int bl_flag;
X
Xstatic int
Xbgetc_line(sec)
Xunsigned int sec;
X{
X int bl_force();
X char c;
X unsigned int alarm();
X
X signal(SIGALRM, bl_force);
X bl_flag = 0;
X
X alarm(sec);
X if ((int) (c = buf_read()) < 0) {
X alarm(0);
X return(-1);
X }
X if (bl_flag)
X return(-1);
X alarm(0);
X return(c & 0xff);
X}
X/*ARGSUSED*/
Xstatic int
Xbl_force(dummy)
Xint dummy;
X{
X bl_flag = 1;
X}
X
X/*
X * Do a single character buffered read from the serial port.
X */
X
Xstatic int
Xbuf_read()
X{
X extern int fd;
X static char buf[CLIST];
X static char *bufp = buf;
X static int n = 0;
X
X if (n <= 0) {
X if ((n = read(fd, buf, CLIST)) <= 0)
X return(-1);
X bufp = buf;
X }
X while (--n >= 0)
X return(*bufp++ & 0xff);
X return(-1);
X}
SHAR_EOF
if test 7019 -ne "`wc -c < 'x_ascii.c'`"
then
echo shar: "error transmitting 'x_ascii.c'" '(should have been 7019 characters)'
fi
fi
echo shar: "extracting 'x_batch.c'" '(8478 characters)'
if test -f 'x_batch.c'
then
echo shar: "will not over-write existing file 'x_batch.c'"
else
sed 's/^X//' << \SHAR_EOF > 'x_batch.c'
X/*
X * Routines to support the batch protocols.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <curses.h>
X#include "config.h"
X#include "misc.h"
X#include "xmodem.h"
X
X/*
X * Send the file name for the modem7 batch. Only uses 11 characters
X * of the filename.
X */
X
Xint
Xsend_modem7(win, name)
XWINDOW *win;
Xchar *name;
X{
X char *new_name, *fix_name();
X unsigned char sum, calc_sum();
X
X /* convert to 11 character name */
X new_name = fix_name(name);
X sum = calc_sum((unsigned char *) new_name, 12);
X
X putc_line(ACK);
X /* for each character in the name */
X while (*new_name != CTRLZ) {
X putc_line((unsigned char) *new_name);
X
X switch (getc_line(3)) {
X case -1: /* timed out */
X clear_line(win, 12, 24, 1);
X waddstr(win, "NO RESPONSE");
X wrefresh(win);
X return(ERROR);
X case ACK: /* got it! */
X break;
X case CAN: /* cancel transmission */
X if (getc_line(2) == CAN) {
X beep();
X clear_line(win, 12, 24, 1);
X wattrstr(win, A_BOLD, "REMOTE ABORTED");
X wrefresh(win);
X return(CANCEL);
X }
X /* fall thru... */
X default:
X clear_line(win, 12, 24, 1);
X waddstr(win, "NAME FAILED");
X wrefresh(win);
X return(ERROR);
X }
X new_name++;
X }
X putc_line(CTRLZ);
X /* verify the checksum */
X if (getc_line(10) != sum) {
X putc_line('u');
X clear_line(win, 12, 24, 1);
X waddstr(win, "CHECKSUM FAILED");
X wrefresh(win);
X return(ERROR);
X }
X putc_line(ACK);
X return(0);
X}
X
X/*
X * Receive a modem7 file name. A return code of 1 means the end of the
X * batch transfers.
X */
X
Xint
Xrcv_modem7(win, default_err)
XWINDOW *win;
Xint default_err;
X{
X extern char file_name[15];
X int i, j, err_method, err_count, got_it;
X unsigned char sum, calc_sum();
X char temp_name[13];
X void change_name(), unfix_name();
X
X err_method = default_err;
X if (default_err == CRC_CHECKSUM)
X err_method = CRC;
X
X err_count = 0;
X got_it = 0;
X while (err_count < MAX_ERRORS) {
X /* switch to checksum? */
X if (default_err == CRC_CHECKSUM && err_count > MAX_ERRORS/2)
X err_method = CHECKSUM;
X
X if (err_method == CRC)
X putc_line('C');
X else
X putc_line(NAK);
X /* what'd we get? */
X switch (getc_line(10)) {
X case -1: /* timed out */
X clear_line(win, 12, 24, 1);
X wattrstr(win, A_BOLD, "NO RESPONSE");
X wrefresh(win);
X err_count++;
X case ACK: /* ready to go... */
X got_it++;
X break;
X default: /* huh? */
X clear_line(win, 12, 24, 1);
X wattrstr(win, A_BOLD, "BAD HEADER");
X wrefresh(win);
X err_count++;
X }
X }
X if (!got_it)
X return(ERROR);
X /* get the name */
X for (i=0; i<12; i++) {
X j = getc_line(3);
X
X switch (j) {
X case -1: /* timed out */
X clear_line(win, 12, 24, 1);
X wattrstr(win, A_BOLD, "NO RESPONSE");
X wrefresh(win);
X return(ERROR);
X case EOT: /* end of batch? */
X return(-1);
X case CAN: /* cancel transmission */
X if (getc_line(2) == CAN) {
X beep();
X clear_line(win, 12, 24, 1);
X wattrstr(win, A_BOLD, "REMOTE ABORTED");
X wrefresh(win);
X return(CANCEL);
X }
X /* fall thru... */
X case 'u': /* bad name character */
X beep();
X clear_line(win, 12, 24, 1);
X wattrstr(win, A_BOLD, "BAD NAME");
X wrefresh(win);
X return(ERROR);
X default: /* the name... */
X temp_name[i] = j & 0xff;
X if (j != CTRLZ)
X putc_line(ACK);
X break;
X }
X }
X temp_name[12] = NULL;
X /* send our checksum */
X sum = calc_sum((unsigned char *) temp_name, 12);
X putc_line(sum);
X /* do they agree? */
X if (getc_line(10) != ACK) {
X beep();
X clear_line(win, 12, 24, 1);
X wattrstr(win, A_BOLD, "BAD NAME");
X wrefresh(win);
X return(ERROR);
X }
X /* load the file_name array */
X unfix_name(temp_name);
X /* any name collisions? */
X change_name(win, file_name);
X return(0);
X}
X
X/*
X * Send the block 0 information for a ymodem batch transfer. Uses only
X * the name component of the path and the file size.
X */
X
Xint
Xsend_ymodem(win, file, size)
XWINDOW *win;
Xchar *file;
Xlong size;
X{
X register int i;
X unsigned short crc, calc_crc();
X char *strcpy();
X unsigned char buf[133];
X /* start with a clean block */
X for (i=0; i<132; i++)
X buf[i] = NULL;
X /* the header */
X buf[0] = SOH;
X buf[1] = 0;
X buf[2] = 255;
X
X /*
X * The block zero consists of the file name (no path component),
X * a NULL, and the file length (as a string). The end of batch
X * marker is an empty block.
X */
X if (*file != NULL) {
X strcpy((char *) &buf[3], file);
X sprintf((char *) &buf[strlen(file)+4], "%ld", size);
X }
X /* the crc */
X crc = calc_crc(&buf[3], 128);
X buf[131] = crc >> 8;
X buf[132] = crc;
X /* the block count */
X mvwaddstr(win, 7, 24, "0 ");
X
X return(send_block(win, buf, 133));
X}
X
X/*
X * Receive the block 0 information for a ymodem batch transfer. We
X * only use the file name and the size (if present). Currently doesn't
X * support full path names.
X */
X
Xint
Xrcv_ymodem(win)
XWINDOW *win;
X{
X extern unsigned char buf[1029];
X extern long file_length;
X extern char file_name[15];
X int code, length_is_at;
X long atol();
X
X file_length = 0L;
X file_name[0] = NULL;
X /* read the zero block */
X if (code = rcv_block(win, 1, 1024, 0))
X return(code);
X /* at end of batch */
X if (buf[3] == NULL)
X return(0);
X /* get the file name */
X change_name(win, (char *) &buf[3]);
X /* any trouble? */
X if (file_name[0] == NULL) {
X putc_line(CAN);
X return(0);
X }
X /*
X * The file length is placed after the NULL of the file name
X * and is terminated by another NULL. If the length is missing,
X * atol() will see a NULL and return 0.
X */
X length_is_at = strlen((char *) &buf[3]) + 4;
X file_length = atol((char *) &buf[length_is_at]);
X return(0);
X}
X
X/*
X * Handle file name collisions. Prepend an "X" to the name until you find
X * a name that doesn't already exist. Creates a NULL name on error.
X * Loads the global character array "file_name".
X */
X
Xvoid
Xchange_name(win, str)
XWINDOW *win;
Xchar *str;
X{
X extern char file_name[15];
X register int i;
X int modified;
X char temp[15], ans[15], *s, *strrchr(), *strcpy(), *strncat();
X unsigned int sleep();
X /* dissect the name component */
X if ((s = strrchr(str, '/')))
X strcpy(temp, s++);
X else
X strcpy(temp, str);
X
X strcpy(ans, temp);
X file_name[0] = NULL;
X /* write permission on directory? */
X if (access(".", 2)) {
X beep();
X clear_line(win, 12, 24, 1);
X wattrstr(win, A_BOLD, "NO WRITE ON DIRECTORY");
X wrefresh(win);
X return;
X }
X /* prepend up to 13 "X"s */
X modified = 0;
X for (i=1; i<14; i++) {
X if (access(ans, 0)) {
X if (modified) {
X beep();
X clear_line(win, 12, 24, 1);
X waddstr(win, "NAME COLLISION");
X wrefresh(win);
X sleep(1);
X }
X strcpy(file_name, ans);
X return;
X }
X
X modified++;
X strcpy(temp, "X");
X strncat(temp, ans, 13);
X temp[14] = NULL;
X strcpy(ans, temp);
X }
X beep();
X clear_line(win, 12, 24, 1);
X waddstr(win, "BAD NAME");
X wrefresh(win);
X return;
X}
X
X/*
X * Convert a perfectly good Unix file name to fit the CP/M file name
X * rules. Used for the modem7 batch file transfer. Returns a pointer
X * to the new name.
X */
X
Xchar *
Xfix_name(path)
Xchar *path;
X{
X int dot;
X char *s, *name, temp[15], *ext, *strcpy(), *strrchr();
X static char ans[13];
X /* ignore the path component */
X if (s = strrchr(path, '/'))
X strcpy(temp, s++);
X else
X strcpy(temp, path);
X name = temp;
X
X ext = NULL;
X dot = 0;
X for (s=name; *s; ++s) {
X if (*s == '.' && !dot) {
X dot++;
X *s = NULL;
X ext = s + 1;
X }
X if (islower(*s))
X *s = toupper(*s);
X }
X /* if null name component */
X if (*name == NULL)
X name = "X";
X /* if name too long */
X if (strlen(name) > 8)
X *(name+8) = NULL;
X /* if extension too long */
X if (strlen(ext) > 3)
X *(ext+3) = NULL;
X
X sprintf(ans, "%-8.8s%-3.3s%c", temp, ext, CTRLZ);
X return(ans);
X}
X
X/*
X * Convert a CP/M style filename into a legal Unix file name. Loads the
X * global character array "file_name".
X */
X
Xvoid
Xunfix_name(cpm_name)
Xchar *cpm_name;
X{
X extern char file_name[15];
X register int i, n;
X int dot;
X char temp[15];
X
X file_name[0] = NULL;
X if (!*cpm_name)
X return;
X
X strcpy(temp, cpm_name);
X /* 8 character of the name */
X n = 0;
X for (i=0; i<8; i++) {
X if (temp[i] != ' ') {
X if (isupper(temp[i]))
X file_name[n++] = tolower(temp[i]);
X else
X file_name[n++] = temp[i];
X }
X }
X /* 3 character extension */
X dot = 0;
X for (i=8; i<11; i++) {
X if (temp[i] != ' ') {
X if (!dot) {
X dot++;
X file_name[n++] = '.';
X }
X if (isupper(temp[i]))
X file_name[n++] = tolower(temp[i]);
X else
X file_name[n++] = temp[i];
X }
X }
X file_name[n] = NULL;
X return;
X}
SHAR_EOF
if test 8478 -ne "`wc -c < 'x_batch.c'`"
then
echo shar: "error transmitting 'x_batch.c'" '(should have been 8478 characters)'
fi
fi
echo shar: "extracting 'x_extrnl.c'" '(1569 characters)'
if test -f 'x_extrnl.c'
then
echo shar: "will not over-write existing file 'x_extrnl.c'"
else
sed 's/^X//' << \SHAR_EOF > 'x_extrnl.c'
X/*
X * Spawn a shell with the stdin and stdout swapped with the remote
X * system. An undocumented feature: The external protocol gateway
X * can be used to pipe the output of a normal Unix command to the
X * remote system.
X */
X
X#include <stdio.h>
X#include <signal.h>
X#include <curses.h>
X#include <fcntl.h>
X#include "config.h"
X
Xvoid
Xextrnl(cmd)
Xchar *cmd;
X{
X extern int fd;
X WINDOW *xt_win, *newwin();
X int (*istat)(), (*qstat)(), status, epid, w;
X char *shell, *shellpath, *getenv(), *strrchr(), buf[40], *ttyname();
X char *strcpy();
X unsigned int sleep();
X void _exit(), input_off();
X
X input_off();
X /* a full window */
X xt_win = newwin(LINES, COLS, 0, 0);
X touchwin(xt_win);
X wrefresh(xt_win);
X /* out of curses mode */
X resetterm();
X
X shellpath = getenv("SHELL");
X if (shellpath == NULL || *shellpath == NULL)
X shellpath = "/bin/sh";
X
X shell = strrchr(shellpath, '/') + 1;
X
X if (!(epid = fork())) {
X /* recreate the device name */
X strcpy(buf, ttyname(fd));
X close(fd);
X /* swap the stdin */
X close(0);
X open(buf, O_RDONLY);
X /* swap the stdout */
X close(1);
X open(buf, O_WRONLY);
X#ifdef SETUGID
X setgid(getgid());
X setuid(getuid());
X#endif /* SETUGID */
X execl(shellpath, shell, "-c", cmd, (char *) 0);
X _exit(1);
X }
X istat = signal(SIGINT, SIG_IGN);
X qstat = signal(SIGQUIT, SIG_IGN);
X
X while ((w = wait(&status)) != epid && w != -1)
X ;
X
X signal(SIGINT, istat);
X signal(SIGQUIT, qstat);
X /* back to curses mode */
X sleep(1);
X fixterm();
X
X clearok(curscr, TRUE);
X werase(xt_win);
X wrefresh(xt_win);
X delwin(xt_win);
X return;
X}
SHAR_EOF
if test 1569 -ne "`wc -c < 'x_extrnl.c'`"
then
echo shar: "error transmitting 'x_extrnl.c'" '(should have been 1569 characters)'
fi
fi
echo shar: "extracting 'x_menu.c'" '(5812 characters)'
if test -f 'x_menu.c'
then
echo shar: "will not over-write existing file 'x_menu.c'"
else
sed 's/^X//' << \SHAR_EOF > 'x_menu.c'
X/*
X * Open a window to display the choices of file transfer protocols and
X * prompt for the file name(s). A return code of 1 means turn the
X * input routine back on.
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <curses.h>
X#include "config.h"
X#include "misc.h"
X#include "xmodem.h"
X
Xint
Xxfer_menu(up)
Xint up;
X{
X extern int fd;
X extern char *null_ptr;
X WINDOW *xm_win, *newwin();
X char *list, *get_names(), *get_extrnl();
X int type, is_batch;
X void xfer_win(), xfer_ascii(), free_ptr(), extrnl(), error_win();
X
X xm_win = newwin(15, 20, 2, 45);
X
X mvwaddstr(xm_win, 2, 3, "1) xmodem");
X mvwaddstr(xm_win, 3, 3, "2) xmodem-1k");
X mvwaddstr(xm_win, 4, 3, "3) modem7");
X mvwaddstr(xm_win, 5, 3, "4) ymodem");
X mvwaddstr(xm_win, 6, 3, "5) ymodem-g");
X mvwaddstr(xm_win, 7, 3, "6) ASCII");
X mvwaddstr(xm_win, 8, 3, "7) (external)");
X mvwaddstr(xm_win, 11, 3, "<ESC> to Abort");
X mvwaddstr(xm_win, 13, 3, "Protocol:");
X box(xm_win, VERT, HORZ);
X if (up)
X mvwattrstr(xm_win, 0, 6, A_BOLD, " Upload ");
X else
X mvwattrstr(xm_win, 0, 5, A_BOLD, " Download ");
X
X wmove(xm_win, 13, 13);
X wrefresh(xm_win);
X /* get the protocol */
X while ((type = get_num(xm_win, 1)) != -1) {
X if (type >= 1 && type <= PROTOCOLS)
X break;
X beep();
X mvwaddch(xm_win, 13, 13, (chtype) ' ');
X wmove(xm_win, 13, 13);
X wrefresh(xm_win);
X }
X type--;
X werase(xm_win);
X wrefresh(xm_win);
X delwin(xm_win);
X /* chickened out */
X if (type < 0)
X return(0);
X
X if (fd == -1) {
X error_win(0, "Not currently connected to any host", "");
X return(0);
X }
X /* is the external protocol? */
X if (type == EXTRNL) {
X /* get the command line */
X if (!(list = get_extrnl(up)))
X return(0);
X extrnl(list);
X return(1);
X }
X /* is a batch protocol? */
X is_batch = 0;
X switch (type) {
X case MODEM7:
X case YMODEM:
X case YMODEM_G:
X is_batch++;
X break;
X default:
X break;
X }
X
X /*
X * When receiving files in one of the batch modes, there is no
X * need to prompt for a list of file names.
X */
X list = null_ptr;
X if (up || !is_batch) {
X if (!(list = get_names(up, type, is_batch)))
X return(0);
X }
X /* if ascii transfer */
X if (type == XASCII) {
X xfer_ascii(list, up);
X free_ptr(list);
X if (up)
X return(0);
X return(1);
X }
X xfer_win(list, up, type);
X free_ptr(list);
X return(1);
X}
X
Xchar *protocol[PROTOCOLS] = {"xmodem", "xmodem-1k", "modem7", "ymodem",
X "ymodem-g", "ASCII", "(external)"};
X
X/*
X * Prompt for a list of files for the transfer programs. A NULL return
X * code means you chickened out.
X */
X
Xstatic char *
Xget_names(up, type, is_batch)
Xint up, type, is_batch;
X{
X int can;
X WINDOW *gn_win, *newwin();
X char *list, *ans, *file, buf[40], *expand(), *get_str(), *strtok();
X void st_line();
X struct stat stbuf;
X
X touchwin(stdscr);
X refresh();
X st_line("");
X
X gn_win = newwin(7, 70, 5, 5);
X mvwaddstr(gn_win, 3, 4, "Enter filename: ");
X box(gn_win, VERT, HORZ);
X if (up)
X sprintf(buf, " Send %s ", protocol[type]);
X else
X sprintf(buf, " Receive %s ", protocol[type]);
X mvwattrstr(gn_win, 0, 3, A_BOLD, buf);
X
X while (1) {
X wmove(gn_win, 3, 20);
X wrefresh(gn_win);
X /* get the answers */
X if (is_batch)
X ans = get_str(gn_win, 60, "", "");
X else
X ans = get_str(gn_win, 60, "", " ");
X
X if (ans == NULL || *ans == NULL) {
X list = NULL;
X break;
X }
X list = expand(ans);
X
X if (is_batch)
X break;
X /*
X * Here we have the opportunity to determine the read and
X * write permissions before things get started. Much nicer
X * than finding out later when there's no way to fix it.
X */
X file = strtok(list, " ");
X /* sanity checking */
X if (!stat(file, &stbuf)) {
X if ((stbuf.st_mode & S_IFREG) != S_IFREG) {
X beep();
X clear_line(gn_win, 4, 15, 1);
X mvwattrstr(gn_win, 4, 15, A_BOLD, "Not a regular file");
X wrefresh(gn_win);
X wait_key(gn_win, 3);
X clear_line(gn_win, 4, 15, 1);
X clear_line(gn_win, 3, 20, 1);
X continue;
X }
X }
X /* check read permission */
X if (up) {
X if (access(file, 0)) {
X beep();
X mvwattrstr(gn_win, 4, 15, A_BOLD, "Can't find file");
X wrefresh(gn_win);
X wait_key(gn_win, 3);
X clear_line(gn_win, 4, 15, 1);
X clear_line(gn_win, 3, 20, 1);
X continue;
X }
X if (access(file, 4)) {
X beep();
X mvwattrstr(gn_win, 4, 15, A_BOLD, "No read permission");
X wrefresh(gn_win);
X wait_key(gn_win, 3);
X clear_line(gn_win, 4, 15, 1);
X clear_line(gn_win, 3, 20, 1);
X continue;
X }
X break;
X }
X /* check write permission */
X if (!(can = can_write(file))) {
X beep();
X clear_line(gn_win, 4, 15, 1);
X mvwattrstr(gn_win, 4, 15, A_BOLD, "No write permission");
X wrefresh(gn_win);
X wait_key(gn_win, 3);
X clear_line(gn_win, 4, 15, 1);
X clear_line(gn_win, 3, 20, 1);
X continue;
X }
X if (can == 2) {
X if (!yes_prompt(gn_win, 4, 15, A_BOLD, "File exists, overwrite")) {
X clear_line(gn_win, 4, 15, 1);
X clear_line(gn_win, 3, 20, 1);
X continue;
X }
X break;
X }
X break;
X }
X werase(gn_win);
X wrefresh(gn_win);
X delwin(gn_win);
X
X return(list);
X}
X
X/*
X * Prompt for the Unix command line to be used as an external file
X * transfer program. A return code of NULL means forget it.
X */
X
Xstatic char *
Xget_extrnl(up)
Xint up;
X{
X WINDOW *ge_win, *newwin();
X char *cmd, *ans, *get_str(), *expand();
X void st_line();
X
X touchwin(stdscr);
X refresh();
X st_line("");
X /* prompt for command line */
X ge_win = newwin(7, 70, 5, 5);
X mvwaddstr(ge_win, 3, 4, "Enter Unix command: ");
X box(ge_win, VERT, HORZ);
X if (up)
X mvwattrstr(ge_win, 0, 3, A_BOLD, " Send (external) ");
X else
X mvwattrstr(ge_win, 0, 3, A_BOLD, " Receive (external) ");
X wmove(ge_win, 3, 24);
X wrefresh(ge_win);
X /* get the line */
X ans = get_str(ge_win, 60, "", "");
X cmd = expand(ans);
X if (*cmd == NULL)
X cmd = NULL;
X
X werase(ge_win);
X wrefresh(ge_win);
X delwin(ge_win);
X return(cmd);
X}
SHAR_EOF
if test 5812 -ne "`wc -c < 'x_menu.c'`"
then
echo shar: "error transmitting 'x_menu.c'" '(should have been 5812 characters)'
fi
fi
echo shar: "extracting 'x_rcv.c'" '(11805 characters)'
if test -f 'x_rcv.c'
then
echo shar: "will not over-write existing file 'x_rcv.c'"
else
sed 's/^X//' << \SHAR_EOF > 'x_rcv.c'
X/*
X * Receive a list of files using a version of Ward Christensen's file
X * transfer protocol. A return code of 1 means the user must acknowledge
X * an error condition (a user generated abort returns a 0). Write errors
X * are considered fatal.
X */
X
X#include <stdio.h>
X#include <curses.h>
X#include "config.h"
X#include "dial_dir.h"
X#include "misc.h"
X#include "xmodem.h"
X
Xunsigned char buf[1029];
Xchar file_name[15];
Xlong file_length;
X
Xstatic int err_method, tot_err, block_size;
X
Xint
Xrcv_xmodem(win, list, type, fast)
XWINDOW *win;
Xchar *list;
Xint type, fast;
X{
X extern char *protocol[];
X FILE *fp, *my_fopen();
X int i, default_err, is_batch, max_block, code, file_count, got_hdr;
X int hours, mins, secs, len;
X long block, recv;
X float percent, performance;
X unsigned char blk;
X unsigned int sleep();
X char *file, *name, *strcpy(), *strrchr(), *strtok();
X void cancel_xfer();
X /* which protocol? */
X switch (type) {
X case XMODEM:
X default_err = CRC_CHECKSUM;
X is_batch = 0;
X max_block = 128;
X break;
X case XMODEM_1k:
X default_err = CRC_CHECKSUM;
X is_batch = 0;
X max_block = 1024;
X break;
X case MODEM7:
X default_err = CHECKSUM;
X is_batch = 1;
X max_block = 128;
X break;
X case YMODEM:
X default_err = CRC;
X is_batch = 1;
X max_block = 1024;
X performance = 1.09;
X break;
X case YMODEM_G:
X default_err = NONE;
X is_batch = 1;
X max_block = 1024;
X performance = 1.02;
X break;
X default:
X return(1);
X }
X
X tot_err = 0;
X file_count = 0;
X mvwaddstr(win, 2, 24, protocol[type]);
X mvwaddstr(win, 11, 24, "0 ");
X
X while (1) {
X file_count++;
X file_length = 0L;
X /* user supplied name */
X if (!is_batch) {
X if (file_count > 1)
X break;
X
X file = strtok(list, " ");
X /* dissect the file name */
X if ((name = strrchr(file, '/')))
X strcpy(file_name, name++);
X else
X strcpy(file_name, file);
X }
X /* get the modem7 file name */
X if (type == MODEM7) {
X if (code = rcv_modem7(win, default_err))
X return(code +1);
X
X file = file_name;
X }
X /* get the block 0 */
X if (type == YMODEM || type == YMODEM_G) {
X if (code = send_first(win, max_block, default_err))
X return(code +1);
X
X if (code = rcv_ymodem(win))
X return(code +1);
X
X /* at the end? */
X if (buf[3] == NULL) {
X beep();
X wrefresh(win);
X putc_line(ACK);
X sleep(1);
X return(0);
X }
X file = file_name;
X }
X /* any trouble? */
X if (file_name[0] == NULL)
X continue;
X
X clear_line(win, 3, 24, 1);
X waddstr(win, file_name);
X /* if file length is known */
X if (file_length != 0L) {
X mvwprintw(win, 4, 24, "%-10ld", file_length);
X
X secs = (file_length * 10.0 / dir->baud[dir->d_cur]) * performance;
X hours = secs / 3600;
X mins = (secs % 3600) / 60;
X secs = (secs % 3600) % 60;
X
X mvwprintw(win, 6, 24, "%d:%02d:%02d", hours, mins, secs);
X }
X /* some starting numbers */
X mvwaddstr(win, 7, 24, "0 ");
X if (file_length != 0L && fast)
X mvwaddstr(win, 8, 24, "0% ");
X if (fast)
X mvwaddstr(win, 9, 24, "0 ");
X mvwaddstr(win, 10, 24, "0 ");
X clear_line(win, 12, 24, 1);
X waddstr(win, "NONE");
X wrefresh(win);
X
X /*
X * If the user supplied the name, write permission is checked
X * by the get_names() routine in xfer_menu(). If modem7
X * or ymodem supplied name, the name is unique and the write
X * permission on the directory is checked by the change_name()
X * routines. So why is this here?
X */
X /* open the file */
X if (!(fp = my_fopen(file, "w"))) {
X beep();
X clear_line(win, 12, 24, 1);
X wattrstr(win, A_BOLD, "CAN'T OPEN FILE");
X wrefresh(win);
X cancel_xfer(1);
X return(1);
X }
X /* ACK the block 0 */
X if (type == YMODEM || type == YMODEM_G)
X putc_line(ACK);
X
X if (code = send_first(win, max_block, default_err)) {
X fclose(fp);
X return(code +1);
X }
X /* here we go... */
X clear_line(win, 12, 24, 1);
X waddstr(win, "NONE");
X wrefresh(win);
X blk = 1;
X block = 1L;
X recv = 0L;
X got_hdr = 1;
X while (1) {
X code = rcv_block(win, got_hdr, max_block, blk);
X
X if (code < 0) {
X fclose(fp);
X return(code +1);
X }
X got_hdr = 0;
X /* are we done? */
X if (buf[0] == EOT) {
X if (!is_batch) {
X beep();
X wrefresh(win);
X sleep(1);
X }
X break;
X }
X /* if not a duplicate block */
X if (!code) {
X if (file_length != 0L) {
X len = file_length - recv;
X if (len > block_size)
X len = block_size;
X }
X else
X len = block_size;
X
X if (fwrite((char *) &buf[3], sizeof(char), len, fp) != len) {
X beep();
X clear_line(win, 12, 24, 1);
X wattrstr(win, A_BOLD, "WRITE ERROR");
X wrefresh(win);
X cancel_xfer(1);
X fclose(fp);
X /* fatal */
X return(1);
X }
X mvwprintw(win, 7, 24, "%-5ld", block);
X recv = recv + (unsigned int) len;
X if (fast)
X mvwprintw(win, 9, 24, "%-10ld", recv);
X blk++;
X block++;
X }
X /*
X * If the length is known, give the same status
X * report as uploading
X */
X if (file_length != 0L && fast) {
X percent = recv * 100.0 / file_length;
X if (percent > 100.0)
X percent = 100.0;
X mvwprintw(win, 8, 24, "%0.1f%%", percent);
X }
X wrefresh(win);
X putc_line(ACK);
X }
X if (file_length != 0L && fast) {
X mvwaddstr(win, 8, 24, "100% ");
X wrefresh(win);
X }
X /*
X * If the file length is not known, search backwards from
X * the end of the file until you find a character that is
X * not the ^Z padding character.
X */
X if (file_length == 0L) {
X for (i=block_size+2; i>2; i--) {
X if (buf[i] != CTRLZ)
X break;
X }
X file_length = recv - (unsigned int) block_size + (unsigned int) i -2L;
X fclose(fp);
X if (fix_length(file_name, file_length)) {
X beep();
X clear_line(win, 12, 24, 1);
X wattrstr(win, A_BOLD, "TRUNCATE ERROR");
X wrefresh(win);
X /* fatal */
X return(1);
X }
X }
X else
X fclose(fp);
X /* ACK the EOT */
X putc_line(ACK);
X }
X return(0);
X}
X
X/*
X * Send the first character to start the transmission and set the error
X * checking method. Returns the standard error codes or 0 on success.
X * The variables err_method and block_size are global.
X */
X
Xstatic int
Xsend_first(win, max_block, default_err)
XWINDOW *win;
Xint max_block, default_err;
X{
X int i, err_count;
X unsigned int sleep();
X void cancel_xfer();
X /* default error method */
X err_method = default_err;
X if (default_err == CRC_CHECKSUM)
X err_method = CRC;
X /* send the first char */
X err_count = 0;
X while (err_count < MAX_ERRORS*2) {
X mvwprintw(win, 10, 24, "%-2d", err_count);
X
X /* check for keyboard abort */
X if (wgetch(win) == ESC) {
X beep();
X clear_line(win, 12, 24, 1);
X waddstr(win, "ABORTED");
X wrefresh(win);
X cancel_xfer(1);
X sleep(3);
X return(ABORT);
X }
X /* switch to checksum? */
X if (default_err == CRC_CHECKSUM && err_count > MAX_ERRORS/2)
X err_method = CHECKSUM;
X
X /* send error method code */
X clear_line(win, 5, 24, 1);
X switch (err_method) {
X case CHECKSUM:
X waddstr(win, "CHECKSUM");
X putc_line(NAK);
X break;
X case CRC:
X waddstr(win, "CRC");
X putc_line('C');
X break;
X case NONE:
X waddstr(win, "NONE");
X putc_line('G');
X break;
X }
X /*
X * We've cut the delay time in half, so we double
X * the allowable errors
X */
X if ((i = getc_line(5)) == -1) {
X err_count++;
X clear_line(win, 12, 24, 1);
X waddstr(win, "NO RESPONSE");
X wrefresh(win);
X continue;
X }
X buf[0] = i;
X#ifdef DEBUG
X fprintf(stderr, "send_first: got header %02x, %03o, %d\n", buf[0], buf[0], buf[0]);
X#endif /* DEBUG */
X
X switch (buf[0]) {
X case SOH: /* small block follows */
X block_size = 128;
X return(0);
X case STX: /* large block follows */
X if (max_block == 1024) {
X block_size = 1024;
X return(0);
X }
X /* fall thru */
X default:
X err_count++;
X clear_line(win, 12, 24, 1);
X waddstr(win, "BAD HEADER");
X wrefresh(win);
X /* read some garbage... */
X fread_line(buf, 1028, 3);
X putc_line(NAK);
X break;
X }
X }
X beep();
X clear_line(win, 12, 24, 1);
X wattrstr(win, A_BOLD, "TIMED OUT");
X wrefresh(win);
X return(ERROR);
X}
X
X/*
X * Receive a block of info from the host. Returns a 0 on success, a 1 on
X * a duplicate block or the standard error codes. The variables
X * err_method and block_size are global.
X */
X
Xint
Xrcv_block(win, got_hdr, max_block, blk)
XWINDOW *win;
Xint got_hdr, max_block;
Xunsigned char blk;
X{
X int i, err_count, bad_block, out_of_sync;
X unsigned short crc, calc_crc();
X unsigned int packet, sleep();
X unsigned char calc_sum(), crc_1, crc_2;
X void cancel_xfer();
X
X err_count = 0;
X while (err_count < MAX_ERRORS) {
X mvwprintw(win, 10, 24, "%-2d", err_count);
X mvwprintw(win, 11, 24, "%-3d", tot_err);
X
X /* scan the keyboard for abort */
X if (wgetch(win) == ESC) {
X beep();
X clear_line(win, 12, 24, 1);
X waddstr(win, "ABORTED");
X wrefresh(win);
X cancel_xfer(1);
X sleep(3);
X return(ABORT);
X }
X /* have we already got a hdr? */
X if (!got_hdr) {
X if ((i = getc_line(10)) == -1) {
X err_count++;
X tot_err++;
X clear_line(win, 12, 24, 1);
X waddstr(win, "NO RESPONSE");
X wrefresh(win);
X continue;
X }
X buf[0] = i;
X#ifdef DEBUG
X fprintf(stderr, "rcv_block: got header %02x, %03o, %d\n", buf[0], buf[0], buf[0]);
X#endif /* DEBUG */
X /* what'd we get? */
X switch (buf[0]) {
X case EOT: /* we're done! */
X clear_line(win, 12, 24, 1);
X waddstr(win, "TRANSFER COMPLETE");
X wrefresh(win);
X sleep(1);
X return(0);
X case SOH: /* small block follows */
X block_size = 128;
X break;
X case STX: /* large block follows */
X if (max_block == 1024) {
X block_size = 1024;
X break;
X }
X /* fall thru... */
X default:
X err_count++;
X tot_err++;
X clear_line(win, 12, 24, 1);
X waddstr(win, "BAD HEADER");
X wrefresh(win);
X
X /* flush a bad packet */
X fread_line(buf, 1028, 5);
X putc_line(NAK);
X continue;
X }
X }
X got_hdr = 0;
X /* read the rest of the packet */
X packet = block_size + 2 + (err_method == CHECKSUM ? 1 : 2);
X if (fread_line(&buf[1], packet, 10) == -1) {
X clear_line(win, 12, 24, 1);
X waddstr(win, "TIMED OUT");
X wrefresh(win);
X putc_line(NAK);
X err_count++;
X tot_err++;
X continue;
X }
X
X /*
X * Validation of the packet includes checking the
X * block number, its complement, and the crc/checksum.
X */
X out_of_sync = 0;
X if (buf[1] != blk || buf[2] != (unsigned char) ~blk)
X out_of_sync++;
X
X bad_block = 0;
X switch (err_method) {
X case CHECKSUM:
X#ifdef DEBUG
X fprintf(stderr, "blk=%d, checksum=%d\n", blk, calc_sum(&buf[3], block_size));
X#endif /* DEBUG */
X if (buf[block_size +3] != calc_sum(&buf[3], block_size))
X bad_block++;
X break;
X case CRC:
X crc = calc_crc(&buf[3], block_size);
X crc_1 = crc >> 8;
X crc_2 = crc;
X#ifdef DEBUG
X fprintf(stderr, "blk=%d, crc1=%d, crc2=%d\n", blk, crc_1, crc_2);
X#endif /* DEBUG */
X if (buf[block_size +3] != crc_1 || buf[block_size +4] != crc_2)
X bad_block++;
X break;
X case NONE:
X return(0);
X }
X /* handle errors */
X if (bad_block) {
X clear_line(win, 12, 24, 1);
X if (err_method == CRC)
X waddstr(win, "CRC FAILED");
X else
X waddstr(win, "CHECKSUM FAILED");
X wrefresh(win);
X putc_line(NAK);
X err_count++;
X tot_err++;
X continue;
X }
X /* not really an error */
X if (out_of_sync) {
X /*
X * If a perfect packet is off by 1 block number,
X * (a lost ACK could cause this) then treat it as
X * a good block but don't write it to disk.
X */
X if (buf[1] == (unsigned char) blk-1)
X return(1);
X
X clear_line(win, 12, 24, 1);
X waddstr(win, "OUT OF SYNC");
X wrefresh(win);
X putc_line(NAK);
X err_count++;
X tot_err++;
X continue;
X }
X return(0);
X }
X beep();
X clear_line(win, 12, 24, 1);
X waddstr(win, "TOO MANY ERRORS");
X wrefresh(win);
X cancel_xfer(1);
X return(ERROR);
X}
SHAR_EOF
if test 11805 -ne "`wc -c < 'x_rcv.c'`"
then
echo shar: "error transmitting 'x_rcv.c'" '(should have been 11805 characters)'
fi
fi
exit 0
# End of shell archive
More information about the Unix-pc.sources
mailing list