/************************************************************* * vt100 terminal emulator - XMODEM protocol support * :ts=8 * * v2.9 ACS - multi_xfer() no longer looks for $ -- kermit does, * readchar() now infers ttime of 100,000 micros if ttime == 0 * (for newkermit); readchar() doesn't output a TIMED OUT msg * (because of newkermit); speed up sendstring(). * 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 }; /* crctab calculated by Mark G. Mendel, Network Systems Corporation */ static unsigned short crctab[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; /* * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell. * NOTE: First srgument must be in range 0 to 255. * Second argument is referenced twice. * * Programmers may incorporate any or all code into their programs, * giving proper credit within the source. Publication of the * source routines is permitted so long as proper credit is given * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, * Omen Technology. */ #define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp) /************************************************************ * Send a string (using sendchar below) ************************************************************/ void sendstring(s) register char *s; { char data[20]; register char *cp = data; int i; LONG oldlength = Write_Request->IOSer.io_Length; APTR saveaddr = Write_Request->IOSer.io_Data; Write_Request->IOSer.io_Length = sizeof(data)-1; Write_Request->IOSer.io_Data = (APTR) &(data[0]); if (enablexon) No_XON(); while(i = *(s++)) { *(cp++) = addparity(i); if( (cp - data) == sizeof(data)-1) { *cp = '\0'; do DoIO((struct IORequest *)Write_Request); while(Write_Request->IOSer.io_Error != 0); cp = data; } } if(cp > data) { *(cp++) = '\0'; Write_Request->IOSer.io_Length = strlen(data); do DoIO((struct IORequest *)Write_Request); while(Write_Request->IOSer.io_Error != 0); } Write_Request->IOSer.io_Length = oldlength; Write_Request->IOSer.io_Data = saveaddr; if (enablexon) No_XON(); } /**************************************************************/ /* send char and read char functions for the xmodem function */ /************************************************************/ void sendchar(ch) int ch; { if (enablexon) No_XON(); rs_out[0] = addparity(ch); do { DoIO((struct IORequest *)Write_Request); } while(Write_Request->IOSer.io_Error != 0); if (enablexon) Do_XON(); } static int addparity(ch) register int ch; { int i, j, k; if(p_parity > 0) switch (p_parity) { case 1: /* mark */ ch = (ch & 0x7F) | 0x80; break; case 2: /* space */ 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 */ ch = (ch & 0x7F) | k; else /* odd parity */ ch = (ch & 0x7F) | (k ^ 0x80); } return(ch & 0xFF); } /* send a break to the host */ void sendbreak() { AbortIO((struct IORequest *)Read_Request); Wait(1L << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit); WaitIO((struct IORequest *)Read_Request); Read_Request->IOSer.io_Command = SDCMD_BREAK; DoIO((struct IORequest *)Read_Request); Read_Request->IOSer.io_Command = CMD_READ; SendIO((struct IORequest *)Read_Request); } int readchar() { int rd,ch; ULONG class, waitmask; USHORT code; if(ttime == 0) Timer.tr_time.tv_micro = 100000; else Timer.tr_time.tv_micro = 0; Timer.tr_time.tv_secs = ttime; SendIO((struct IORequest *)&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((struct IORequest *)Read_Request)) { WaitIO((struct IORequest *)Read_Request); ch=rs_in[0]; rd = TRUE; SendIO((struct IORequest *)Read_Request); } if(reqwinup && (NewMessage=(struct IntuiMessage *)GetMsg(reqwindow->UserPort))) { class = NewMessage->Class; ReplyMsg((struct Message *)NewMessage); if(class == NEWSIZE) ReqNewSize(reqwindow->Height, reqwindow->Width); } if (NewMessage=(struct IntuiMessage *)GetMsg(mywindow->UserPort)) { class = NewMessage->Class; code = NewMessage->Code; ReplyMsg((struct Message *)NewMessage); if ((class == RAWKEY) && (code == 69)) { if(!CheckIO((struct IORequest *)&Timer)) AbortIO((struct IORequest *)&Timer); Wait (1L << Timer_Port->mp_SigBit); WaitIO((struct IORequest *)&Timer.tr_node); InfoMsg1Line("ERROR: User aborted transfer"); timeout = USERABORT; return('\0'); } PrintIText(mywindow->RPort, &MyTitle, 0L, 0L); } if (rd == FALSE && CheckIO((struct IORequest *)&Timer)) { /* InfoMsg1Line("ERROR: Timeout waiting for character"); */ timeout = TIMEOUT; return('\0'); } } /* end while */ if(!CheckIO((struct IORequest *)&Timer)) AbortIO((struct IORequest *)&Timer); Wait (1L << Timer_Port->mp_SigBit); WaitIO((struct IORequest *)&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((struct IORequest *)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((struct IORequest *)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, c, good_sect, nak_char, retval = FALSE; unsigned int checksum, j, bufptr; unsigned short crc; char scrstr2[40]; bytes_xferred = 0L; ttime = TTIME_SHORT; if( (bufr = AllocMem((long)BufSize, MEMF_PUBLIC|MEMF_CLEAR)) == NULL) { InfoMsg1Line("XMODEM: Can't get a buffer."); return FALSE; } if ((fd = creat(file, 0)) < 0) { InfoMsg2Line("XMODEM Can't Open File:",file); goto exit; } else InfoMsg1Line("XMODEM Receive, in VT100 window to abort"); sectnum = errors = bufptr = firstchar = 0; if(p_xproto == 2) nak_char = 'C'; else nak_char = NAK; No_XON(); sendchar(nak_char); while (firstchar != EOT && errors != ERRORMAX) { errorflag = FALSE; while( (firstchar = readchar()) != SOH && firstchar != EOT) { if (timeout != GOODREAD) { if (timeout == USERABORT || errors++ == ERRORMAX) goto exit; } sendchar(nak_char); } if (firstchar == SOH) { sprintf(scrstr2,"%s: Block: %4d Bytes: %d", p_xproto==2?"XmodemCRC":"Xmodem", sectnum, sectnum*SECSIZ); InfoMsgNoScroll(scrstr2); sectcurr = readchar(); if (timeout != GOODREAD) goto exit; sectcomp = readchar(); if (timeout != GOODREAD) goto exit; if ((sectcurr + sectcomp) == 255) { if (sectcurr == ((sectnum + 1) & 0xff)) { checksum = 0; crc = 0; for (j = bufptr; j < (bufptr + SECSIZ); j++) { bufr[j] = readchar(); if (timeout != GOODREAD) goto exit; checksum = (checksum + bufr[j]) & 0xff; crc = updcrc(((unsigned int)bufr[j] & 0xff), crc); } c = readchar(); if(timeout != GOODREAD) { errorflag = TRUE; if(timeout == USERABORT) goto exit; } if(p_xproto == 2) { crc = updcrc(((unsigned int)c & 0xff), crc); c = readchar(); if(timeout != GOODREAD) { errorflag = TRUE; if(timeout == USERABORT) goto exit; } crc = updcrc(((unsigned int)c & 0xff), crc); good_sect = (crc == 0); } else good_sect = (checksum == c); if (!good_sect) { errorflag = TRUE; if(timeout == USERABORT) goto exit; } else { errors = 0; /* sprintf(scrstr2,"Block %4d verified",sectnum); */ sectnum++; bufptr += SECSIZ; bytes_xferred += SECSIZ; /* InfoMsgNoScroll(scrstr2); */ if (bufptr == BufSize) { if (write(fd, bufr, BufSize-SECSIZ) == EOF) { InfoMsg1Line("XMODEM: Error Writing File"); goto exit; } bufptr = SECSIZ; for (j = 0; j < SECSIZ; j++) bufr[j] = bufr[(BufSize-SECSIZ)+j]; } sendchar(ACK); } } else { /* got a duplicate sector */ if (sectcurr == (sectnum & 0xff)) { /* wait until we time out for 5secs */ do { readchar(); } while (timeout == GOODREAD); if (timeout == USERABORT) goto exit; InfoMsg1Line("XMODEM: Received Duplicate Sector"); sendchar(ACK); } else errorflag = TRUE; } } else errorflag = TRUE; } if (errorflag == TRUE) { errors++; InfoMsg1Line("XMODEM: Error"); sendchar(nak_char); } } /* end while */ if ((firstchar == EOT) && (errors < ERRORMAX)) { sendchar(ACK); if (bufptr) { if(p_autochop) { /* use firstchar to remember the last char for chopping */ if((firstchar = bufr[--bufptr]) == 0 || firstchar == 0x1A) while (bufptr && bufr[--bufptr] == firstchar) ; bufptr++; } write(fd, bufr, bufptr); } close(fd); ScrollInfoMsg(1); retval = TRUE; } exit: Do_XON(); FreeMem(bufr, (long)BufSize); bufr = NULL; return retval; } int XMODEM_Send_File(file) char *file; { int sectnum, bytes_to_send, size, attempts, c, use_crc = 0, retval = FALSE; unsigned checksum, j, bufptr; unsigned short crc; char scrstr2[40]; bytes_xferred = 0; ttime = TTIME_LONG; if( (bufr = AllocMem((long)BufSize, MEMF_PUBLIC|MEMF_CLEAR)) == NULL) { InfoMsg1Line("XMODEM: Can't get a buffer."); return FALSE; } if ((fd = open(file, 0)) < 0) { InfoMsg1Line("XMODEM: Cannot Open Send File"); FreeMem(bufr, (long)BufSize); bufr = NULL; 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) && (c != 'C') && (j++ < ERRORMAX)) if (timeout == USERABORT) goto bad_exit; if (j >= (ERRORMAX)) { InfoMsg1Line("XMODEM: Receiver not sending"); goto bad_exit; } if(c == 'C') use_crc = 1; while ((bytes_to_send = read(fd, bufr, BufSize)) && attempts != RETRYMAX) { if (bytes_to_send == EOF) { InfoMsg1Line("XMODEM: Error Reading File"); goto bad_exit; } bufptr = 0; while (bytes_to_send > 0 && attempts != RETRYMAX) { attempts = 0; sprintf(scrstr2,"%s: Sending Block: %4d Bytes: %d", use_crc?"XmodemCRC":"Xmodem", sectnum, sectnum*SECSIZ); size = SECSIZ <= bytes_to_send ? SECSIZ : bytes_to_send; bytes_to_send -= size; do { InfoMsgNoScroll(scrstr2); sendchar(SOH); sendchar(sectnum); sendchar(~sectnum); checksum = 0; crc = 0; for (j = bufptr; j < bufptr + size; j++) { sendchar(bufr[j]); /* send buffer data */ checksum += bufr[j]; crc = updcrc(((unsigned int)bufr[j] & 0xff), crc); } 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-- ) { if(use_crc) crc = updcrc(c, crc); sendchar(c); /* send padding */ } } if(use_crc) { crc = updcrc(0, updcrc(0, crc)); sendchar(crc >> 8); sendchar(crc & 0xff); } else sendchar(checksum); attempts++; c = readchar(); if (timeout == USERABORT) { InfoMsg1Line("XMODEM: ABORTED"); goto bad_exit; } } 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"); goto bad_exit; } else { attempts = 0; do { sendchar(EOT); attempts++; } while ((readchar() != ACK) && (attempts != RETRYMAX) && (timeout != USERABORT)) ; if (attempts == RETRYMAX) InfoMsg1Line("XMODEM: No end of file"); } ScrollInfoMsg(1); retval = TRUE; bad_exit: Do_XON(); FreeMem(bufr, (long)BufSize); bufr = NULL; return retval; } /* 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+1) == '\0') 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; if(p_xbeep) cmd_beep(0L); }