/* mdmx_snd.c -- Routines to handle xmodem sending for HA5G * * Copyright 1989 by Hilgraeve Inc. -- Monroe, MI * All rights reserved * * $Revision: 9 $ * $Date: 4/24/02 3:49p $ */ #include #include #pragma hdrstop // #include #define BYTE unsigned char #include #include #include #include #include #include #include #include #include #include "xfr_srvc.h" #include "xfr_todo.h" #include "xfr_dsp.h" #include "xfer_tsc.h" #include "foo.h" #include "cmprs.h" #include "xfer.h" #include "xfer.hh" #include "mdmx.h" #include "mdmx.hh" #if !defined(STATIC_FUNC) #define STATIC_FUNC #endif #if !defined(CMPRS_MINSIZE) #define CMPRS_MINSIZE 4000L #endif /* * * * * * * * * * * * * * * * * local function prototypes * * * * * * * * * * * * * * * * */ STATIC_FUNC int xsend_start(ST_MDMX *xc, BYTE *start_chars, int *start_char); STATIC_FUNC int getresponse(ST_MDMX *xc, int time); STATIC_FUNC void make_file_pckt(ST_MDMX *xc, struct s_mdmx_pckt *p, char *fname, long size); /* * * * * * * * * Functions * * * * * * * * */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * mdmx_snd * * DESCRIPTION: * Sends a file using XMODEM or YMODEM protocol. Support 1k packets, batch * transfers and 'G' option streaming. * * ARGUMENTS: * attended -- TRUE if user is probably in attendance. Controls the display * of some messages. * * RETURNS: * True if transfer completes successfully, FALSE otherwise. */ int mdmx_snd(HSESSION hSession, int attended, int method, unsigned nfiles, long nbytes) { ST_MDMX *xc; struct s_mdmx_pckt * this_pckt = NULL; struct s_mdmx_pckt * next_pckt = NULL; struct s_mdmx_pckt * tpckt; /* column values for display box */ TCHAR sfname[FNAME_LEN];// file name of file being sent TCHAR xname[FNAME_LEN]; // transmitted file name int still_trying; // controls exit from main transfer loop int got_file; // controls when to complete batch op int got_response; // controls loop to get valid response int tries = 0; // number of retries for each packet unsigned total_tries; // number of retries for entire transfer int response; // response char. received from other end BYTE start_chars[3]; // acceptable start chars. from receiver int xstatus = TSC_OK; // winds up with overall status of transfer int check_type; // type of error checking in use unsigned pcktn; // number of packet currently being sent int override = FALSE; // set TRUE if comm. details changed to unsigned int uiOldOptions; int batch; // TRUE if YMODEM batch transfers used int big_pckts; // TRUE if 1K packets are allowed int streaming; // TRUE if no packet responses expected if (xfer_set_comport(hSession, TRUE, &uiOldOptions) != TRUE) { goto done; } else { override = TRUE; } /* set up options based on method used */ big_pckts = (method != XF_XMODEM); batch = (method == XF_YMODEM || method == XF_YMODEM_G); streaming = FALSE; /* will be turned on if receiver starts with 'G' */ // assert(nfiles == 1 || batch); this_pckt = NULL; next_pckt = NULL; xc = malloc(sizeof(ST_MDMX)); if (xc == NULL) { goto done; } memset(xc, 0, sizeof(ST_MDMX)); xc->hSession = hSession; xc->hCom = sessQueryComHdl(hSession); // RemoteClear(hSession); ComRcvBufrClear(xc->hCom); this_pckt = malloc(sizeof(ST_MDMX) + (big_pckts ? LARGE_PACKET : SMALL_PACKET) + 2); if (this_pckt == NULL) { goto done; } memset(this_pckt, 0, sizeof(ST_MDMX) + (big_pckts ? LARGE_PACKET : SMALL_PACKET) + 2); next_pckt = malloc(sizeof(ST_MDMX) + (big_pckts ? LARGE_PACKET : SMALL_PACKET) + 2); if (next_pckt == NULL) { goto done; } memset(next_pckt, 0, sizeof(ST_MDMX) + (big_pckts ? LARGE_PACKET : SMALL_PACKET) + 2); mdmxXferInit(xc, method); /* Could be smaller but this is easier */ if (xc->p_crc_tbl == NULL) { xstatus = TSC_NO_MEM; goto done; } // hp_report_xtime(0); /* make invalid in case transfer bombs */ xc->file_bytes = 0L; xc->total_bytes = 0L; xc->fh = NULL; xc->xfertimer = -1L; xc->nfiles = nfiles; /* make these available to display routines */ xc->filen = 0; xc->filesize = -1L; xc->nbytes = nbytes; mdmxdspTotalsize(xc, nbytes); mdmxdspFilecnt(xc, nfiles); xc->mdmx_byte_cnt = 0; StrCharCopy(start_chars, (batch ? "CG" : "C\x15")); /* \x15 is NAK */ check_type = CRC; mdmxdspChecktype(xc, (check_type == CRC) ? 0 : 1); total_tries = 0; mdmxdspErrorcnt(xc, total_tries); tries = 0; mdmxdspPacketErrorcnt(xc, tries); got_file = TRUE; while (got_file) { if ((got_file = xfer_nextfile(hSession, sfname)) == TRUE) { xc->total_bytes += xc->file_bytes; xc->file_bytes = xc->mdmx_byte_cnt = 0L; mdmxdspNewfile(xc, xc->filen + 1, sfname, sfname); ++xc->filen; if (xfer_opensendfile(hSession, &xc->fh, sfname, &xc->filesize, xname, NULL) != 0) { xstatus = TSC_CANT_OPEN; goto done; } mdmxdspFilesize(xc, xc->filesize); } else { // strblank(xname); xname[0] = TEXT('\0'); } pcktn = 0; if (batch) { make_file_pckt(xc, this_pckt, xname, xc->filesize); } if ((xstatus = xsend_start(xc, start_chars, &response)) != TSC_OK) { break; } if (xc->filen <= 1) { xc->xfertimer = (long)startinterval(); /* start the clock */ if (response == NAK) { check_type = CHECKSUM; } mdmxdspChecktype(xc, (check_type == CRC) ? 0 : 1); if (response == 'G') { streaming = TRUE; mdmxdspChecktype(xc, 2); } /* once we've received the first start_char, * subsequent ones must match */ start_chars[0] = (BYTE)response; start_chars[1] = '\0'; } if (got_file) { xc->p_getc = xm_getc; tries = 0; if (!batch && !load_pckt(xc, this_pckt, pcktn = 1, big_pckts, check_type)) { xstatus = TSC_DISK_ERROR; goto done; } } /* get the first pckt on its way while we prepare the second pckt */ if ( ComSndBufrSend(xc->hCom, &this_pckt->start_char, (unsigned)this_pckt->pcktsize, SMALL_WAIT) == COM_PORT_NOT_OPEN ) { xstatus = TSC_LOST_CARRIER; still_trying = FALSE; goto done; } mdmxdspPacketnumber(xc, pcktn); /* load next pckt*/ if (got_file && !load_pckt(xc, next_pckt, ++pcktn, big_pckts, check_type)) { xstatus = TSC_DISK_ERROR; goto done; } still_trying = TRUE; while (still_trying) { if (streaming) { /* these things are done in getresponse() if not streaming */ if (xfer_carrier_lost(hSession)) { xstatus = TSC_LOST_CARRIER; break; } if (xfer_user_interrupt(hSession)) { xstatus = TSC_USER_CANNED; break; } } /* wait until last packet is out before watching for response */ ComSndBufrWait(xc->hCom, this_pckt->pcktsize >= LARGE_PACKET ? LARGE_WAIT : SMALL_WAIT); /* get response from receiver */ got_response = FALSE; while (!got_response) { response = (streaming && this_pckt->start_char != EOT) ? ACK : getresponse(xc, 60); got_response = TRUE; switch(response) { case ACK: if (this_pckt->start_char == EOT) { /* successful */ mdmx_progress(xc, FILE_DONE); xc->xfertime = (long)interval(xc->xfertimer); fio_close(xc->fh); xfer_log_xfer(hSession, TRUE, sfname, NULL, TSC_OK); xc->fh = NULL; xstatus = TSC_OK; still_trying = FALSE; } else { xc->file_bytes = this_pckt->byte_count; tpckt = this_pckt; this_pckt = next_pckt; next_pckt = tpckt; /* pcktn will only be <= 1 when batch is on and * we've just sent the filename packet (packet 0) */ if (pcktn <= 1 && (!got_file || (xstatus = xsend_start(xc, start_chars, &response)) != TSC_OK)) { still_trying = FALSE; break; } /* send packet */ if ( ComSndBufrSend(xc->hCom, &this_pckt->start_char, (unsigned)this_pckt->pcktsize, SMALL_WAIT) == COM_PORT_NOT_OPEN) { xstatus = TSC_LOST_CARRIER; still_trying = FALSE; break; } mdmxdspPacketnumber(xc, pcktn); mdmx_progress(xc, 0); if (tries != 0) { mdmxdspPacketErrorcnt(xc, 0); tries = 0; } if (this_pckt->start_char != EOT) { if (!load_pckt(xc, next_pckt, ++pcktn, big_pckts, check_type)) { xstatus = TSC_DISK_ERROR; still_trying = FALSE; } } } break; case NO_RESPONSE: mdmxdspLastError(xc, 12); still_trying = FALSE; break; case ABORTED: xstatus = TSC_USER_CANNED; still_trying = FALSE; break; case CARR_LOST: xstatus = TSC_LOST_CARRIER; still_trying = FALSE; break; case 'C': case 'G': /* these act as NAKs for packets first packets */ if (pcktn > 2) { got_response = FALSE; break; } /* else fall through */ case NAK: if (++tries >= xc->mdmx_tries) { xstatus = TSC_ERROR_LIMIT; goto done; } else /* send packet */ { if (ComSndBufrSend(xc->hCom, &this_pckt->start_char, (unsigned)this_pckt->pcktsize, SMALL_WAIT) == COM_PORT_NOT_OPEN) { xstatus = TSC_LOST_CARRIER; still_trying = FALSE; break; } } if (this_pckt->start_char == EOT && tries == 1) { break; /* don't print first retransmission on final EOT */ } mdmxdspPacketErrorcnt(xc, tries); mdmxdspErrorcnt(xc, ++total_tries); mdmxdspLastError(xc, (response == NAK) ? 13 : 14); break; case CAN: if (getresponse(xc, 1) == CAN) /* two consecutive CANs? */ { xstatus = TSC_RMT_CANNED; still_trying = FALSE; break; } /* fall through */ default: got_response = FALSE; break; } } /* end while (!got_response) */ } /* end while(still_trying) */ if (!batch || xstatus != TSC_OK) { break; } } /* end while(got_file) */ done: mdmx_progress(xc, TRANSFER_DONE); mdmxdspCloseDisplay(xc); // ComSendSetCharDelay(hld_send_cdelay, COMSEND_SETDELAY); if (override) { #if FALSE cnfg.bits_per_char = hld_bits_per_char; cnfg.parity_type = hld_parity_type; (void)(*ComResetPort)(); #endif xfer_restore_comport(hSession, uiOldOptions); } if (xc == NULL || this_pckt == NULL || next_pckt == NULL) { xstatus = TSC_NO_MEM; } if (xc != NULL) { // hp_report_xtime((unsigned)xc->xfertime); if (xc->fh) { fio_close(xc->fh); } if (xstatus != TSC_OK) { if (xstatus != TSC_RMT_CANNED && xstatus != TSC_NO_MEM) { for (tries = 5 + 1; --tries > 0; ) { ComSendChar(xc->hCom, &xc->stP, CAN); } ComSendPush(xc->hCom, &xc->stP); } xfer_log_xfer(hSession, TRUE, sfname, NULL, xstatus); } #if FALSE if (attended && xstatus != TSC_USER_CANNED && xstatus != TSC_NO_MEM) { menu_bottom_line (BL_ESC, 0L); DosBeep(beepfreq, beeplen); menu_replybox((int)xc->msgrow, ENTER_RESP, 0, (int)transfer_status_msg((unsigned short)xstatus)); } #endif if (xc->p_crc_tbl != NULL) { #if defined(DEADWOOD) resFreeDataBlock(xc->hSession, xc->p_crc_tbl); xc->p_crc_tbl = NULL; #else // defined(DEADWOOD // // We don't need to free xc->p_crc_tbl since it is pointing // to a static constant array. REV: 4/10/2002 // xc->p_crc_tbl = NULL; #endif // defined(DEADWOOD) } free(xc); xc = NULL; } if (this_pckt) { free(this_pckt); this_pckt = NULL; } if (next_pckt) { free(next_pckt); next_pckt = NULL; } return((unsigned)xstatus); } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * xsend_start * * DESCRIPTION: * Waits up to one minute for a start request from the receiver at the * other end of the line. * * ARGUMENTS: * chktype -- Pointer to a variable to be set to the requested error * correction checking method, CRC or Checksum * * RETURNS: * Status code indicating result. Can be one of * TSC_USER_CANNED if user interrupted by hitting the ESC key. * TSC_NO_RESPONSE if 60 seconds elapses without receiving a start char. * TSC_RMT_CANNED if remote sends a control-C * TSC_OK if remote sends proper start char. * */ STATIC_FUNC int xsend_start(ST_MDMX *xc, BYTE *start_chars, int *start_char) { for ( ; ; ) { switch(*start_char = getresponse(xc, 60)) { case ABORTED: return(TSC_USER_CANNED); case NO_RESPONSE: return(TSC_NO_RESPONSE); case CARR_LOST: return(TSC_LOST_CARRIER); case ESC: case '\003': /* control-C */ return(TSC_RMT_CANNED); default: if (strchr(start_chars, *start_char)) { return(TSC_OK); } /* ignore any other char. */ break; } } return TSC_OK; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * getresponse * * DESCRIPTION: * Waits a specified time for a response from the receiver. Can be forced * to terminate early if user intervention is detected. No effort is made * here to interpret the meaning of a response character. Any character * received within the time limit will be returned. * * ARGUMENTS: * time -- Number of seconds to wait for a response character. * * RETURNS: * The response character if one was received or ABORTED or NO_RESPONSE or * CARR_LOST. */ STATIC_FUNC int getresponse(ST_MDMX *xc, int time) { TCHAR rc = 0; long timer; DbgOutStr("getresponse ", 0,0,0,0,0); #if FALSE if (kbd_check_flagkey(xc->flagkey, TRUE) > 0) { kbd_flush(); return ABORTED; } #endif if (xfer_user_interrupt(xc->hSession)) { DbgOutStr("aborted\r\n", 0,0,0,0,0); return ABORTED; } // if ((rc = RemoteGet(xc->hSession)) != -1) if (mComRcvChar(xc->hCom, &rc) != 0) { DbgOutStr("returned %d\r\n", rc, 0,0,0,0); return(rc & 0x7F); } time *= 10; timer = (long)startinterval(); while ((long)interval(timer) < (long)time) { #if FALSE if (kbd_check_flagkey(xc->flagkey, TRUE) > 0) { kbd_flush(); return ABORTED; } #endif if (xfer_carrier_lost(xc->hSession)) { DbgOutStr(" lost\r\n", 0,0,0,0,0); return CARR_LOST; } if (xfer_user_interrupt(xc->hSession)) { DbgOutStr("aborted\r\n", 0,0,0,0,0); return ABORTED; } mdmx_progress(xc, 0); if (mComRcvChar(xc->hCom, &rc) != 0) { DbgOutStr("returned %d\r\n", rc, 0,0,0,0); return(rc & 0x7F); } xfer_idle(xc->hSession, XFER_IDLE_IO); } DbgOutStr(" none\r\n", 0,0,0,0,0); return(NO_RESPONSE); } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * make_file_pckt * * DESCRIPTION: sets up the initial filename pckt for Ymodem (only) * * ARGUMENTS: * p -- Pointer to the packet structure which receives the filename * packet. * fname -- The file name as it should be placed in the packet. * size -- The size of the file as it should be placed in the packet. * * RETURNS: * nothing */ STATIC_FUNC void make_file_pckt(ST_MDMX *xc, struct s_mdmx_pckt *p, char *fname, long size) { BYTE sizestr[20]; BYTE *ptr; BYTE *cp; unsigned int crc; p->start_char = SOH; /* set start char to SOH */ p->pcktnum = 0; /* set pcktnumber to 0 */ p->npcktnum = 0xff; /* set npcktnumber to 0xff */ ptr = p->bdata; /* initialize data area with zeros */ memset(ptr, 0, SMALL_PACKET + 2); if (*fname) { StrCharCopy(ptr, fname); /* copy filename into buffer*/ /* replace all back slashes with slashes */ //while (strreplace(ptr, FstrBslashBslash(), "/")) // ; for (cp = ptr; *cp != '\0'; cp += 1) if (*cp == '\\') *cp = '/'; // StrFmt(sizestr, "%ld", size); /* format the file size */ wsprintf(sizestr, "%ld", (LONG)size); StrCharCopy(&ptr[StrCharGetByteCount(ptr)+1], sizestr); /* add it to buffer */ } /* calculate CRC value */ ptr = &p->bdata[SMALL_PACKET]; /* set ptr to char after buffer */ /* calculate CRC */ crc = calc_crc(xc, (unsigned)0, p->bdata, SMALL_PACKET+2); /* set the CRC */ *ptr++ = (BYTE)(crc / 0x100); *ptr = (BYTE)(crc % 0x100); /* set the packetsize */ p->pcktsize = SMALL_PACKET + 5; p->byte_count = xc->mdmx_byte_cnt; } /*********************** end of mdmx_snd.c **************************/