/*--------------------------------------------------------------------------*/ /* */ /* */ /* ------------ Bit-Bucket Software, Co. */ /* \ 10001101 / Writers and Distributors of */ /* \ 011110 / Freely Available Software. */ /* \ 1011 / */ /* ------ */ /* */ /* (C) Copyright 1987-96, Bit Bucket Software Co. */ /* */ /* This module was written by Bob Hartman */ /* */ /* BinkleyTerm Xmodem Sender State Machine */ /* */ /* */ /* 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.260. */ /* */ /* 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:343/491 */ /* P.O. Box 460398 AlterNet 7:42/1491 */ /* Aurora, CO 80046 BBS-Net 86:2030/1 */ /* Internet f491.n343.z1.fidonet.org */ /* */ /* Please feel free to contact us at any time to share your comments about */ /* our software and/or licensing policies. */ /* */ /*--------------------------------------------------------------------------*/ /* Include this file before any other includes or defines! */ #include "includes.h" void Build_Header_Block (XMARGSP args, char type) { struct FILEINFO dta = {0}; SEADATAP ttmp; (void) dfind (&dta, args->filename, 0); args->save_header = type; ttmp = (SEADATAP) & (args->header); (void) memset (ttmp, 0, sizeof (XMDATA)); ttmp->header = type; ttmp->block_num = 0; ttmp->block_num_comp = 0xff; ttmp->filelength = args->filelen; (void) strncpy (ttmp->sendingprog, xfer_id, 14); if (type == SYN) { (void) memset (ttmp->filename, ' ', 16); ttmp->timedate = dta.time; /* This is the CRC bit in the TeLink header */ ttmp->Resync = 1; } else { #ifdef ANSI_TIME_T ttmp->timedate = args->save_filetime.oneword.timedate - ANSI_TIME_T_DELTA; #else ttmp->timedate = args->save_filetime.oneword.timedate; #endif ttmp->SLO = (unsigned char) (((cur_baud.rate_value >= 9600L) && !no_overdrive) ? 1 : 0); ttmp->Resync = (unsigned char) (no_resync ? 0 : 1); ttmp->MACFLOW = 1; } if (args->temp_name != NULL) (void) strncpy (ttmp->filename, args->temp_name, strlen (args->temp_name)); else (void) strncpy (ttmp->filename, (char *) (dta.name), strlen (dta.name)); (void) dfind (&dta, NULL, 2); } void XSSetVars (XMARGSP); int XSInit (XMARGSP); int XSEnd (XMARGSP); int XSXmtStart (XMARGSP); int XSXmTeStrt (XMARGSP); int XSCheckACK (XMARGSP); int XSSendBlk (XMARGSP); int XSWaitEnd (XMARGSP); typedef struct { char *state_name; int (*state_func) (XMARGSP); } XSTATES, *XSTATEP; XSTATES Xmodem_Sender[] = { {"XSInit", XSInit}, {"XSEnd", XSEnd}, {"XS0", XSXmtStart}, {"XS0T", XSXmTeStrt}, {"XS1", XSCheckACK}, {"XS2", XSSendBlk}, {"XS3", XSWaitEnd} }; int XSInit (XMARGSP args) { struct stat st; char junkbuff[100]; /* Get the file information */ if (stat (args->filename, &st)) { /* Print error message */ return (OPEN_ERR); } if ((args->file_pointer = share_fopen (args->filename, read_binary, DENY_WRITE)) == NULL) { /* Print error message */ return (OPEN_ERR); } /* Get important information out of it */ args->filelen = st.st_size; args->LastBlk = (st.st_size + 127) / 128; args->save_filetime.oneword.timedate = st.st_atime; args->prev_bytes = 0L; args->tot_errs = 0; (void) sprintf (junkbuff, MSG_TXT (M_SEND_MSG), args->LastBlk, args->filename, st.st_size); if (un_attended && fullscreen) { clear_filetransfer (); sb_move (filewin, 1, 2); sb_puts (filewin, junkbuff); elapse_time (); sb_show (); } else { status_line ("+%s", junkbuff); (void) printf ("\n"); } locate_y = wherey (); locate_x = wherex (); /* Start the throughput calculations */ throughput (0, 0L); return ((int) args->control); } int XSEnd (XMARGSP args) { args->result = (int) args->control; /* Close file */ (void) fclose (args->file_pointer); if (args->tot_errs > 3) status_line (MSG_TXT (M_CORRECTED_ERRORS), args->tot_errs, args->LastBlk); /* Log that we sent it */ if (args->result == SUCCESS) { long lTime, lSize; lSize = args->filelen - args->prev_bytes; lTime = throughput (1, (unsigned long) lSize); status_line ("%s: %s", MSG_TXT (M_FILE_SENT), args->filename); update_files (1, args->filename, lSize, lTime, 0); } return (args->result); } void XSSetVars (XMARGSP args) { if (no_sealink) { args->options.SLO = 0; args->options.Resync = 0; } else { args->options.SLO = ((cur_baud.rate_value >= 9600L) && !no_overdrive) ? 1 : 0; args->options.Resync = (~no_resync) & 1; } args->options.SEAlink = 0; args->SendBLK = 1; args->curr_byte = 0L; args->NextBLK = 1; args->ACKST = 0; args->ACKBLK = -1L; args->Window = 1; args->ACKsRcvd = 0; args->NumNAK = 0; args->T1 = timerset (3000); } int XSXmtStart (XMARGSP args) { XSSetVars (args); Build_Header_Block (args, SOH); return (XS1); } int XSXmTeStrt (XMARGSP args) { XSSetVars (args); Build_Header_Block (args, SYN); return (XS1); } int XSCheckACK (XMARGSP args) { Check_ACKNAK (args); return (XS2); } int XSSendBlk (XMARGSP args) { if (!CARRIER) return (CARRIER_ERR); if (got_ESC ()) { status_line (MSG_TXT (M_KBD_MSG)); return (KBD_ERR); } if ((args->NumNAK > 4) && (args->SendBLK == 0)) { if (args->save_header == SOH) return (XS0T); else { args->NumNAK = 0; ++(args->ACKBLK); ++(args->SendBLK); return (XS2); } } if (args->NumNAK > 10) { /* Too Many Errors */ return (SEND_RETRY_ERR); } if (timeup (args->T1)) { /* Fatal Timeout */ return (SEND_TIMEOUT); } if (args->SendBLK > (args->LastBlk + 1)) return (XS3); if (args->SendBLK > (args->ACKBLK + args->Window)) { time_release (); return (XS1); } if (args->SendBLK == (args->LastBlk + 1)) { SENDBYTE (EOT); ++(args->SendBLK); args->T1 = timerset (3000); show_sending_blocks (args); time_release (); return (XS1); } /* Increment the block count before sending because we read the next block immediately after sending this block. On error free connects we have a big net win because we never do a seek, and while we are sending one block, we read the next. If we do get errors, then we have to seek back to the previous block, and that will be a bother. With today's phone lines and modems, we'll assume error free is more often than not, and take our chances. */ if (args->options.SLO && args->options.SEAlink) { args->ACKBLK = args->SendBLK; } ++(args->SendBLK); args->curr_byte += 128L; Send_Block (args); args->T1 = timerset (6000); return (XS1); } int XSWaitEnd (XMARGSP args) { show_sending_blocks (args); if (args->ACKBLK < (args->LastBlk + 1)) { time_release (); return (XS1); } if (!CARRIER) return (CARRIER_ERR); return (SUCCESS); } int SEAlink_Send_File (char *filename, char *sendname) { XMARGS xm; xm.filename = filename; xm.temp_name = sendname; return (state_machine ((STATEP) Xmodem_Sender, &xm, XS0)); } int Xmodem_Send_File (char *filename, char *sendname) { return (SEAlink_Send_File (filename, sendname)); } int Telink_Send_File (char *filename, char *sendname) { XMARGS xm; xm.filename = filename; xm.temp_name = sendname; return (state_machine ((STATEP) Xmodem_Sender, &xm, XS0T)); } void Get_Block (XMARGSP args) { XMDATAP xtmp; if (args->SendBLK == 0) { Build_Header_Block (args, args->save_header); args->NextBLK = -1L; return; } xtmp = (XMDATAP) & (args->header); /* Set up buffer as all ^Zs for EOF */ (void) memset (xtmp, SUB, sizeof (XMDATA)); /* Now set up the header stuff */ xtmp->header = SOH; xtmp->block_num = (unsigned char) (args->SendBLK & 0xff); xtmp->block_num_comp = (unsigned char) ~xtmp->block_num; if (args->NextBLK != args->SendBLK) { (void) fseek (args->file_pointer, (args->SendBLK - 1) * 128, SEEK_SET); } args->NextBLK = args->SendBLK + 1; /* Can we read any data? */ if (fread ((char *) xtmp->data_bytes, 1, 128, args->file_pointer) <= 0) return; /* Looks good */ return; } void Send_Block (XMARGSP args) { if (args->header == SYN) { Data_Check ((XMDATAP) & (args->header), CHECKSUM); } else { Data_Check ((XMDATAP) & (args->header), args->options.do_CRC ? CRC : CHECKSUM); } if ((!(args->options.do_CRC)) || (args->header == SYN)) { SENDCHARS ((char *) &(args->header), sizeof (XMDATA) - 1, 1); } else { SENDCHARS ((char *) &(args->header), sizeof (XMDATA), 1); } UNBUFFER_BYTES (); show_sending_blocks (args); Get_Block (args); } char * show_num (XMARGSP args, long b) { char *Rtn; Rtn = "EOT"; if (b > args->LastBlk) { if (!(fullscreen && un_attended)) (void) cputs (Rtn); } else if (b >= 0L) { Rtn = ultoa (((unsigned long) b), e_input, 10); if (!(fullscreen && un_attended)) (void) cputs (Rtn); } return Rtn; } void show_sending_blocks (XMARGSP args) { char *TmpPtr = (char *) &happy_compiler; char j[100]; long k; k = args->filelen - args->curr_byte; if (k < 0L) k = 0L; (void) sprintf (j, "%3d min", ((k * 10L / cur_baud.rate_value * 100L / ((args->save_header == SOH) ? 94L : 70L) + 59L) / 60L)); if (args->options.SLO) { if ((!((args->SendBLK - 1) & 0x1f)) || ((args->SendBLK - 1) > args->LastBlk)) { if (fullscreen && un_attended) { elapse_time (); sb_move (filewin, 2, 2); TmpPtr = show_num (args, args->SendBLK - 1); sb_puts (filewin, TmpPtr); (void) sb_putc (filewin, ':'); TmpPtr = show_num (args, args->ACKBLK); sb_puts (filewin, TmpPtr); sb_puts (filewin, " *Overdrive* "); sb_move (filewin, 2, 69); sb_puts (filewin, j); sb_show (); } else { gotoxy (locate_x, locate_y); (void) show_num (args, args->SendBLK - 1); (void) cputs (":"); (void) show_num (args, args->ACKBLK); (void) cputs (" *Overdrive* "); } } } else { if (fullscreen && un_attended) { elapse_time (); sb_move (filewin, 2, 2); TmpPtr = show_num (args, args->SendBLK - 1); sb_puts (filewin, TmpPtr); (void) sb_putc (filewin, ':'); TmpPtr = show_num (args, args->ACKBLK); sb_puts (filewin, TmpPtr); sb_puts (filewin, " "); sb_move (filewin, 2, 69); sb_puts (filewin, j); sb_show (); } else { gotoxy (locate_x, locate_y); (void) show_num (args, args->SendBLK - 1); (void) cputs (":"); (void) show_num (args, args->ACKBLK); (void) cputs (" "); } } happy_compiler = *(int *) TmpPtr; /* Makes the compiler happy! */ } int ACInit (XMARGSP); int ACEnd (XMARGSP); int ACChkRcvd (XMARGSP); int ACSLCheck (XMARGSP); int ACSLVerify (XMARGSP); int ACSLACKNAK (XMARGSP); int ACXMCheck (XMARGSP); int ACSLOCheck (XMARGSP); int ACSL1Check (XMARGSP); int ACACKNAK (XMARGSP); int ACXMACK (XMARGSP); int ACXMNAK (XMARGSP); int ACRESYNC (XMARGSP); typedef struct { char *state_name; int (*state_func) (XMARGSP); } ASTATES, *ASTATEP; ASTATES ACKNAK_Check[] = { {"ACInit", ACInit}, {"ACEnd", ACEnd}, {"AC0", ACChkRcvd}, {"AC1", ACSLCheck}, {"AC2", ACSLVerify}, {"AC3", ACSLACKNAK}, {"AC4", ACXMCheck}, {"AC5", ACSLOCheck}, {"AC6", ACSL1Check}, {"AC7", ACACKNAK}, {"AC8", ACXMACK}, {"AC9", ACXMNAK}, {"AC10", ACRESYNC} }; int ACInit (XMARGSP args) { args->result = 0; return ((int) args->control); } int ACEnd (XMARGSP args) { args->result = (int) args->control; return (args->result); } int ACChkRcvd (XMARGSP args) { if (PEEKBYTE () >= 0) { args->CHR = TIMED_READ (0); return (AC1); } return (SUCCESS); } int ACSLCheck (XMARGSP args) { if (args->ACKST > 2) return (AC2); return (AC6); } int ACSLVerify (XMARGSP args) { if (args->ARBLK8 == (unsigned char) ((~args->CHR) & 0xff)) { args->ARBLK = args->SendBLK - ((args->SendBLK - args->ARBLK8) & 0xff); return (AC3); } args->options.SEAlink = 0; args->Window = 1; args->ACKST = 0; return (AC6); } int ACSLACKNAK (XMARGSP args) { if ((args->ARBLK < 0) || (args->ARBLK > args->SendBLK) || (args->ARBLK <= (args->SendBLK - 128))) { return (AC0); } if (args->ACKST == 3) { args->options.SEAlink = (~no_sealink) & 1; args->Window = calc_window (); args->ACKBLK = args->ARBLK; ++(args->ACKsRcvd); args->ACKST = 0; return (AC5); } args->SendBLK = args->ARBLK; args->curr_byte = (args->SendBLK - 1) * 128L; if (args->curr_byte < 0L) args->curr_byte = 0L; if (args->SendBLK > 0) ++(args->tot_errs); Get_Block (args); args->ACKST = 0; return (AC4); } int ACXMCheck (XMARGSP args) { if (args->NumNAK < 4) { args->options.SEAlink = (~no_sealink) & 1; args->Window = calc_window (); } else { args->options.SEAlink = 0; args->Window = 1; } return (SUCCESS); } int ACSLOCheck (XMARGSP args) { if ((args->options.SLO == 0) || (args->ACKsRcvd < 10)) return (SUCCESS); args->options.SLO = 0; return (SUCCESS); } int ACSL1Check (XMARGSP args) { if ((args->ACKST == 1) || (args->ACKST == 2)) { args->ARBLK8 = (unsigned char) args->CHR; args->ACKST += 2; return (AC6); } if ((args->options.SEAlink == 0) || (args->ACKST == 0)) return (AC7); return (AC0); } int ACACKNAK (XMARGSP args) { long mac_timer; switch (args->CHR) { case ACK: args->ACKST = 1; args->NumNAK = 0; return (AC8); case WANTCRC: args->options.do_CRC = 1; /* Fallthrough */ case NAK: args->ACKST = 2; ++(args->NumNAK); CLEAR_OUTBOUND (); timer (6); return (AC9); case SYN: CLEAR_OUTBOUND (); if (!no_resync) { args->result = Receive_Resync (&(args->resync_block)); args->ACKST = 0; return (AC10); } else { return (AC0); } case DC3: /* ^S */ if (args->options.SEAlink && (args->ACKST == 0)) { mac_timer = timerset (1000); while (CARRIER && !timeup (mac_timer)) { if (TIMED_READ (0) == DC1) break; time_release (); } return (AC0); } /* Otherwise, fallthrough */ default: break; } return (AC0); } int ACXMACK (XMARGSP args) { if (!args->options.SEAlink) ++(args->ACKBLK); return (AC0); } int ACXMNAK (XMARGSP args) { if (!args->options.SEAlink) { args->SendBLK = args->ACKBLK + 1; args->curr_byte = (args->SendBLK - 1) * 128L; if (args->curr_byte < 0L) args->curr_byte = 0L; if (args->SendBLK > 0) ++(args->tot_errs); Get_Block (args); } return (AC0); } int ACRESYNC (XMARGSP args) { CLEAR_OUTBOUND (); if (args->result != SUCCESS) { SENDBYTE (NAK); return (SUCCESS); } if (args->SendBLK == 1) { args->prev_bytes = (args->resync_block - 1) * 128; if (args->prev_bytes > args->filelen) args->prev_bytes = args->filelen; status_line (MSG_TXT (M_SYNCHRONIZING), args->prev_bytes); } else { ++(args->tot_errs); } args->options.SEAlink = 1; args->Window = calc_window (); args->SendBLK = args->resync_block; args->curr_byte = (args->SendBLK - 1) * 128L; if (args->curr_byte < 0L) args->curr_byte = 0L; Get_Block (args); args->ACKBLK = args->SendBLK - 1; SENDBYTE (ACK); return (SUCCESS); } void Check_ACKNAK (XMARGSP args) { (void) state_machine ((STATEP) ACKNAK_Check, args, AC0); } int Receive_Resync (long *resync_block) { unsigned char resyncit[30]; unsigned char *p; unsigned char a, b; unsigned short nak_crc, his_crc; p = resyncit; while ((*p = (unsigned char) TIMED_READ (1)) != ETX) { if ((*p < '0') || (*p > '9')) { status_line (">SEAlink Send: Resync bad byte '%02x'", *p); return (RESYNC_ERR); } ++p; } *p = '\0'; nak_crc = crc_block ((unsigned char *) resyncit, (int) strlen ((char *) resyncit)); a = (unsigned char) TIMED_READ (1); b = (unsigned char) TIMED_READ (1); his_crc = (b << 8) | a; if (nak_crc != his_crc) { status_line (">SEAlink Send: Resync bad crc %04hx/%04hx", nak_crc, his_crc); return (CRC_ERR); } *resync_block = atol ((char *) resyncit); status_line (">SEAlink Send: Resync to %ld", *resync_block); return (SUCCESS); } int calc_window () { long window; window = cur_baud.rate_value / 400L; if (window <= 0L) window = 2L; if (small_window) window = (window > 6L) ? 6L : window; else window = (window > 2000L) ? 2000L : window; return (int) window; }