/************************************************************* * vt100 terminal emulator - XMODEM protocol support * * v2.7 870825 ACS - Make multi_xfer() non-recursive; on non-ESC in * readchar() re-do the main window's title. * v2.6 870227 DBW - bug fixes for all the stuff in v2.5 * v2.5 870214 DBW - more additions (see readme file) * v2.4 861214 DBW - lots of fixes/additions (see readme file) * v2.3 861101 DBW - minor bug fixes * v2.2 861012 DBW - more of the same * v2.1 860915 DBW - new features (see README) * 860901 ACS - Added Parity and Word Length and support code * 860823 DBW - Integrated and rewrote lots of code * 860815 Steve Drew: readchar inproved with real timeouts * v2.0 860809 DBW - Major rewrite * v1.1 860720 DBW - Switches, 80 cols, colors, bug fixes * v1.0 860712 DBW - First version released * *************************************************************/ #include "vt100.h" int enablexon = TRUE; extern struct IntuiText MyTitle; static unsigned long parity_settings[4] = { 0x96696996, 0x69969669, 0x69969669, 0x96696996 }; /************************************************************ * Send a string (using sendchar below) ************************************************************/ void sendstring(s) char *s; { char c; while ((c = *s++) != '\000') sendchar(c); } /**************************************************************/ /* send char and read char functions for the xmodem function */ /************************************************************/ void sendchar(ch) int ch; { int doxon,i,j,k; doxon = enablexon; if (doxon) No_XON(); switch (p_parity) { case 0: /* no parity */ rs_out[0] = ch & 0xFF; break; case 1: /* mark */ rs_out[0] = (ch & 0x7F) | 0x80; break; case 2: /* space */ rs_out[0] = ch & 0x7F; break; case 3: /* even */ case 4: /* odd */ i = (ch >> 5) & 0x3; j = ch & 0x1F; k = ((parity_settings[i] >> j) & 0x1) << 7; if (p_parity == 3) /* even parity */ rs_out[0] = (ch & 0x7F) | k; else /* odd parity */ rs_out[0] = (ch & 0x7F) | (k ^ 0x80); } do { DoIO(Write_Request); } while(Write_Request->IOSer.io_Error != 0); if (doxon) Do_XON(); } /* send a break to the host */ void sendbreak() { AbortIO(Read_Request); Wait(1L << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit); WaitIO(Read_Request); Read_Request->IOSer.io_Command = SDCMD_BREAK; DoIO(Read_Request); Read_Request->IOSer.io_Command = CMD_READ; SendIO(Read_Request); } int readchar() { int rd,ch; ULONG class, waitmask; USHORT code; Timer.tr_time.tv_secs = ttime; Timer.tr_time.tv_micro = 0; SendIO((char *) &Timer.tr_node); rd = FALSE; waitmask = ((1L << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit) | ( 1L << mywindow->UserPort->mp_SigBit) | ( 1L << Timer_Port->mp_SigBit)); if(reqwinup) waitmask |= (1L << reqwindow->UserPort->mp_SigBit); while (rd == FALSE) { Wait(waitmask); if (CheckIO(Read_Request)) { WaitIO(Read_Request); ch=rs_in[0]; rd = TRUE; SendIO(Read_Request); } if(reqwinup && (NewMessage=(struct IntuiMessage *)GetMsg(reqwindow->UserPort))) { class = NewMessage->Class; ReplyMsg(NewMessage); if(class == NEWSIZE) ReqNewSize(reqwindow->Height, reqwindow->Width); } if (NewMessage=(struct IntuiMessage *)GetMsg(mywindow->UserPort)) { class = NewMessage->Class; code = NewMessage->Code; ReplyMsg(NewMessage); if ((class == RAWKEY) && (code == 69)) { AbortIO((char *) &Timer); Wait (1L << Timer_Port->mp_SigBit); WaitIO((char *) &Timer.tr_node); InfoMsg1Line("ERROR: User aborted transfer"); timeout = USERABORT; return('\0'); } PrintIText(mywindow->RPort, &MyTitle, 0L, 0L); continue; } if (rd == FALSE && CheckIO(&Timer)) { InfoMsg1Line("ERROR: Timeout waiting for character"); timeout = TIMEOUT; return('\0'); } } /* end while */ AbortIO((char *) &Timer); Wait (1L << Timer_Port->mp_SigBit); WaitIO((char *) &Timer.tr_node); timeout = GOODREAD; return(ch & (p_parity == 0 ? 0xFF : 0x7F)); } void No_XON() { /* turn off XON/XOFF processing */ enablexon = FALSE; Write_Request->io_SerFlags |= SERF_XDISABLED; Write_Request->IOSer.io_Command = SDCMD_SETPARAMS; DoIO(Write_Request); Write_Request->IOSer.io_Command = CMD_WRITE; } void Do_XON() { /* turn on XON/XOFF processing */ enablexon = TRUE; Write_Request->io_SerFlags &= ~SERF_XDISABLED; Write_Request->IOSer.io_Command = SDCMD_SETPARAMS; DoIO(Write_Request); Write_Request->IOSer.io_Command = CMD_WRITE; } /**************************************/ /* xmodem send and recieve functions */ /************************************/ int XMODEM_Read_File(file) char *file; { int firstchar, sectnum, sectcurr, sectcomp, errors, errorflag; unsigned int checksum, j, bufptr; char scrstr2[40]; bytes_xferred = 0L; ttime = TTIME_SHORT; if ((fd = creat(file, 0)) < 0) { InfoMsg2Line("XMODEM Can't Open File:",file); return FALSE; } else InfoMsg1Line("XMODEM Receive, in VT100 window to abort"); sectnum = errors = bufptr = 0; sendchar(NAK); firstchar = 0; No_XON(); while (firstchar != EOT && errors != ERRORMAX) { errorflag = FALSE; do { /* get sync char */ firstchar = readchar(); if (timeout != GOODREAD) { if (timeout == USERABORT || errors++ == ERRORMAX) Do_XON(); return FALSE; } } while (firstchar != SOH && firstchar != EOT); if (firstchar == SOH) { sprintf(scrstr2,"Getting Block %4d...",sectnum); InfoMsgNoScroll(scrstr2); sectcurr = readchar(); if (timeout != GOODREAD) { Do_XON(); return FALSE; } sectcomp = readchar(); if (timeout != GOODREAD) { Do_XON(); return FALSE; } if ((sectcurr + sectcomp) == 255) { if (sectcurr == ((sectnum + 1) & 0xff)) { checksum = 0; for (j = bufptr; j < (bufptr + SECSIZ); j++) { bufr[j] = readchar(); if (timeout != GOODREAD) { Do_XON(); return FALSE; } checksum = (checksum + bufr[j]) & 0xff; } if (checksum == readchar() && timeout == GOODREAD) { errors = 0; sprintf(scrstr2,"Block %4d verified",sectnum); sectnum++; bufptr += SECSIZ; bytes_xferred += SECSIZ; InfoMsgNoScroll(scrstr2); if (bufptr == BufSize) { if (write(fd, bufr, BufSize-128) == EOF) { InfoMsg1Line("XMODEM: Error Writing File"); Do_XON(); return FALSE; } bufptr = 128; for (j = 0; j < 128; j++) bufr[j] = bufr[(BufSize-128)+j]; } sendchar(ACK); } else { errorflag = TRUE; if (timeout == USERABORT) { Do_XON(); return FALSE; } } } else { /* got a duplicate sector */ if (sectcurr == (sectnum & 0xff)) { /* wait until we time out for 5secs */ do { readchar(); } while (timeout == GOODREAD); if (timeout == USERABORT) { Do_XON(); return FALSE; } InfoMsg1Line("XMODEM: Received Duplicate Sector"); sendchar(ACK); } else errorflag = TRUE; } } else errorflag = TRUE; } if (errorflag == TRUE) { errors++; InfoMsg1Line("XMODEM: Error"); sendchar(NAK); } } /* end while */ if ((firstchar == EOT) && (errors < ERRORMAX)) { sendchar(ACK); #ifdef BUGFIXES /* use firstchar to remember the last char for chopping */ if (bufptr && ((firstchar = bufr[--bufptr]) == 0 || firstchar == 0x1A)) { while (bufptr && bufr[--bufptr] == firstchar) ; if (bufptr || bufr[0] != firstchar) /* check for null buffer */ write(fd, bufr, ++bufptr); } #else while (bufptr > 0 && (bufr[--bufptr] == 0x00 || bufr[bufptr] == 0x1A)) ; write(fd, bufr, ++bufptr); #endif close(fd); Do_XON(); ScrollInfoMsg(1); return TRUE; } Do_XON(); return FALSE; } int XMODEM_Send_File(file) char *file; { int sectnum, bytes_to_send, size, attempts, c; unsigned checksum, j, bufptr; char scrstr2[40]; bytes_xferred = 0; ttime = TTIME_LONG; if ((fd = open(file, 0)) < 0) { InfoMsg1Line("XMODEM: Cannot Open Send File"); return FALSE; } else InfoMsg1Line("XMODEM Send, from VT100 window to abort"); attempts = 0; sectnum = 1; No_XON(); /* wait for sync char */ j=1; while (((c = readchar()) != NAK) && (j++ < ERRORMAX)) if (timeout == USERABORT) { Do_XON(); return(FALSE); } if (j >= (ERRORMAX)) { InfoMsg1Line("XMODEM: Receiver not sending NAKs"); Do_XON(); return FALSE; } while ((bytes_to_send = read(fd, bufr, BufSize)) && attempts != RETRYMAX) { if (bytes_to_send == EOF) { InfoMsg1Line("XMODEM: Error Reading File"); Do_XON(); return FALSE; } bufptr = 0; while (bytes_to_send > 0 && attempts != RETRYMAX) { attempts = 0; sprintf(scrstr2,"Sending block %4d",sectnum); #ifdef BUGFIXES size = SECSIZ <= bytes_to_send ? SECSIZ : bytes_to_send; bytes_to_send -= size; #endif do { InfoMsgNoScroll(scrstr2); sendchar(SOH); sendchar(sectnum); sendchar(~sectnum); checksum = 0; #ifdef BUGFIXES for (j = bufptr; j < bufptr + size; j++) { sendchar(bufr[j]); /* send buffer data */ #else size = SECSIZ <= bytes_to_send ? SECSIZ : bytes_to_send; bytes_to_send -= size; for (j = bufptr; j < (bufptr + SECSIZ); j++) if (j < (bufptr + size)) { sendchar(bufr[j]); #endif checksum += bufr[j]; } #ifdef BUGFIXES if( size < SECSIZ ) /* check if we need to pad */ { c = bufr[j-1] ? 0 : 0x1A; /* choose correct padding */ j = SECSIZ - size; checksum += j * c; while ( j-- ) sendchar(c); /* send padding */ } #else else sendchar(0); #endif sendchar(checksum); attempts++; c = readchar(); if (timeout == USERABORT) { InfoMsg1Line("XMODEM: ABORTED"); Do_XON(); return FALSE; } } while ((c != ACK) && (attempts != RETRYMAX)); bufptr += size; bytes_xferred += size; sprintf(scrstr2,"Sent block %4d",sectnum); InfoMsgNoScroll(scrstr2); sectnum++; } } close(fd); if (attempts == RETRYMAX) { InfoMsg1Line("XMODEM: No Acknowledgment, ABORTING"); Do_XON(); return FALSE; } else { attempts = 0; do { sendchar(EOT); attempts++; } while ((readchar() != ACK) && (attempts != RETRYMAX) && (timeout != USERABORT)) ; if (attempts == RETRYMAX) InfoMsg1Line("XMODEM: No end of file"); } Do_XON(); ScrollInfoMsg(1); return TRUE; } /* allow for multi file xfers separated by commas under kermit and XMODEM */ void multi_xfer(name,mode,do_send) char *name; int (*mode)(); int do_send; { int done = 0; int status; char *p, *name_start; timeout = USERABORT - 1; for(p=name_start=name; !done && timeout != USERABORT; name_start=++p) { if (*(name_start) == '$' && *(name_start+1) == '\0') { saybye(); return; } while(*p == ' ') p++; while(*p && *p != ',' && *p != ' ') p++; if (*p == '\0') { done = TRUE; multi = 0; } else multi = 1; *p = '\0'; status = ((*mode)(name_start, multi)); if (status == FALSE) close(fd); } server = 0; multi = 0; }