/* * XMODEM -- Implements the "CP/M User's Group XMODEM" protocol, * the TERM II File Transfer Protocol (FTP) Number 1, * and the TERM II File Transfer Protocol Number 4 for * packetized file up/downloading. This version of XMODEM * has successfully communicated with RSX11M, RSX11M+ running * this version of XMODEM, UNIX running UMODEM v3.6, and several * CP/M machines. * * Installation notes: * * XMODEM communicates with XM:. This pseudo device should be assigned * globally for 1 device or locally via command files if XMODEM is to * be used with several devices. Intsall XMODEM at a high enough * priority to aviod loosing characters during receives. Otherwise * re-transmission of blocks will add to transmission time during receives * and may result in XMODEM aborting the receive. XMODEM uses little * overhead when sending data and should be run at a high enough priority * to aviod being locked out for more than a second at a time. * * Build file: * * XCC -a XMODEM * TKB * XMODEM/CP/PR:0/-IP=XMODEM,[1,1]CX/LB,C/LB * / * UNITS=12 * TASK=...MDM * // */ #ifdef DOCUMENTATION title XMODEM XMODEM Filel Transmission Progam index XMODEM protocol synopsis mdm -options file description XMODEM implements the CP/M User's Group XMODEM protocol, the TERM II File Transfer Protocol (FTP) Number 1, and the TERM II File Transfer Protocol Number 4. Basically, XMODEM should be executed on the remote end to send/receive a file. Next, it should be started on the local end to receive/send that file. It is used to insure transmission of one file at a time. XMODEM generates a log file at each end which should be read to verify transmission was successful. Generally XMODEM will complete with proper transmission or exit with an error message. The example at the end of this document shows how to use XMODEM with SOFTWIRE. Note that SOFTWIRE is not necessary if another method of invoking XMODEM at the remote end is used. .s The following options may be used when invoking XMODEM: .lm +8 .s.i -8;rb Receive Binary .i -8;rt Receive Text .i -8;rv Receive Variable Length Binary (RSX only) .i -8;sb Send Binary .i -8;st Send Text .i -8;sv Send Variable Length Binary (RSX only) .i -8;p Turn ON Parameter Display .i -8;l (ell) Turn OFF LOG File Entries .i -8;1 (one) Employ TERM II FTP 1 .i -8;a Turn ON ARPA Net Flag .i -8;n Start a new xmodem.log file (else append) .i -8;7 Enable 7-bit transfer mask .i -8;4 Enable TERM II FTP 4 .s.lm -8 These options are usually selected because of the protocol desired and the type file is being sent or received. All options have the defaults discussed below. .s.lm +8 .s.i -8;-rb Receive Binary .s Any file being received with the -rb options will be created with 512 byte fixed length records and no character has special meaning. Use this option to receive task images and RSX libraries from remote RSX computers. Note that the recieived file may need to be made contiguous when received. .s example: MDM -RB TEST.TSK .s PIP TEST.TSK/NV/CO=TEST.TSK .s RUN TEST .s .s.i -8;-rt Receive Text .s Any file being received with the -rt options will be created with variable length records containing text. This opion is almost always used when receiving files from non-RSX systems. Note that special services are performed when receiving text such as removal of carriage return (CR) characters. .s example: MDM -RT PROG.FTN .s.i -8;-rv Receive Variable Length Binary Record .s The -rv option should be specified when receiving a binary file of variable records from another RSX system using the -sv option. This RSX specific addition to XMODEM facilitates the transmission of object files. Note that an image copy of the file is made and no character has special meaning. .s example: MDM -RV PROGRAM.OBJ .s TKB PROGRAM=PROGRAM .s RUN PROGRAM .s.i -8;-sb, -st, -sv Send File Options .s The -sb option is used to send a binary file to a system executing XMODEM with the -rb option. Likewise, -st is paired with -rt and -sv is paired with -rv. .s example: MDM -SB TESTLIB.OLB .s .s.i -8;-p Turn on Verbose Paramter Display .s The -p option will display more information than usual about execution paramters. .s example: MDM -RTP .s .s.i -8;-l Turn off LOG File Entries .s XMODEM normally logs information into a file named XMODEM.LOG. Specifying -l turns off this logging. Normally the remote and local LOG file should be read after a transmission to make sure the transmission was successful. .s.i -8;-n Start a New LOG File .s Normally XMODEM appends logged data to the end of any existing XMODEM.LOG file. Specifying the -n option causes a new LOG file to be created. .s.i -8;-7 Seven Bit Mask .s Normally XMODEM processes eight bit bytes. It is sometimes necessary to mask the eighth bit when conversing with a remote system. Specifying the -7 option causes the eighth bit to be stripped. .s.i -8;-1, -4, -a Protocol Select .s Specifying -1 selects the TERM II FTP 1 protocol. Specifying -4 selects the TERM II FTP 4 protocol. Specifying -a turns on the ARPA Net Flag. You now know as much as I do about these options. Be aware they have not been tested. .s.lm -8 XMODEM expects one and only one file name to be specified on the command line. This file must exist and is opened for sending when the -s option is selected. A new file is opened and written when the -r option is selected. .s The following is an example of how to transmit a file to a remote system executing RSX using SOFTWIRE and XMODEM. Refer to the SOFTWIRE documentation if necessary. Assume the local machine has a prompt of A> and the remote machine has a prompt of B>. Assume the pseudo devices SW: for SOFTWIRE and XM: for XMODEM have been globally assigned on both machines. Also assume the machines are wired together although an auto-dialing modem could be used. .nf .lm +28 .s.i -20 A> SOW Establish communication. .s.i -20 B> HEL ACCT Login, etc. .s.i -20 B> MDM -ST FILE.TYP Send file from remote .s.lm -20 .f (A few lines of messages from remote XMODEM will appear) .lm +20 .nf .s.i -20 ~! Issue local command .s.i -20 SOW> Enter Command: .s.i -20 MDM -RT NEWFILE.TYP Recievie file at local .s.lm -20 .f (Messages from local XMODEM and the transmission should go to completion) .lm +20 .nf .s.i -20 SOW> DONE Local command done .s.i -20 B> PIP TI:=XMODEM.LOG Check remote LOG .s.i -20 B> BYE Log off remote .s.i -20 ~. Disconnect SOW .s.i -20 A> PIP TI:=XMODEM.LOG Check local LOG .s.lm -28 .f This four step process becomes quite simple with a little practice. Figure that XMODEM will take 5 seconds to transmitt 1 disk block at 1200 baud. Don't try to run XMODEM above 2400 buad while your boss is trying to get some work done. Finally note that XMODEM is not intended to replace DECNET but fills a viod. hints Build command files around SOFTWIRE and XMODEM when trying to support multiple ports to do local assingments instad of assigning SW: and XM: globally. User RMSDSP to determine if binary files should be transmitted with the -sb or -sv options. Be careful not to point two RSX machines at one another without slaving one of the terminal ports. Those of you who have done this know that the two MCRs argue until one gets knocked out. diagnostics Diagnostic messages should be self-explanatory. (A truism). bugs Fixed length records are created with 512 byte records only. XMODEM sometimes dies on noisy telephone lines and the transmission has to be restarted. It is not easy to get rid of the rmote XMODEM until it times out if the XMODEM on the local end terminates. Be patient. XMODEM sets up the TTY port as desired. Aborting an executing XMODEM leaves the terminal port in a weird state. (Usu. California). #endif /* * If you are running RSX11M leave the following commented out. If you * are running RSX11M+ uncomment the following. */ /* #define RSX11M+ */ /* * If you are running RSX11M+ with kernel space uncomment the following. * If you are running RSX11M+ without kernel space or RSX11M leave * it commented out */ /* #define KSPACE */ #include #include #include #include #include #define TRUE 1 #define FALSE 0 /* log default define */ #define LOGDEFAULT TRUE /* * The following are included here because they were left out of qiottdrv.h * somehow. */ #define TC_TBS 64 #define TC_TBM 65 #define VERSION 32 /* Version Number */ /* ASCII Constants */ #define SOH 001 #define STX 002 #define ETX 003 #define EOT 004 #define ENQ 005 #define ACK 006 #define LF 012 /* Unix LF/NL */ #define CR 015 #define NAK 025 #define SYN 026 #define CAN 030 #define ESC 033 #define CTRLZ 032 /* CP/M EOF for text (usually!) */ #define ESCAPE '\\' /* Unix style escape */ /* XMODEM Constants */ #define TIMEOUT -1 #define ERRORMAX 10 /* maximum errors tolerated */ #define RETRYMAX 10 /* maximum retries to be made */ #define BBUFSIZ 128 /* buffer size -- do not change! */ /* RSX Constants */ #define QIOEF 4 /* Event flag for qiow */ #define WAITEF 5 /* Event flag for wtse */ #define RBUFSIZ (2*BBUFSIZ+20) /* Circular read buffer size */ #define SBUFSIZ (BBUFSIZ+10) /* Send buffer size */ #define VBUFSIZ 4097 /* Max length of variable length record */ /* ARPA Net Constants */ /* The following constants are used to communicate with the ARPA * Net SERVER TELNET and TIP programs. These constants are defined * as follows: * IAC <-- Is A Command; indicates that * a command follows * WILL/WONT <-- Command issued to SERVER TELNET * (Host); WILL issues command * and WONT issues negative of * the command * DO/DONT <-- Command issued to TIP; DO issues * command and DONT issues * negative of the command * TRBIN <-- Transmit Binary Command * Examples: * IAC WILL TRBIN <-- Host is configured to transmit Binary * IAC WONT TRBIN <-- Host is configured NOT to transmit binary * IAC DO TRBIN <-- TIP is configured to transmit Binary * IAC DONT TRBIN <-- TIP is configured NOT to transmit binary */ #define IAC 0377 /* Is A Command */ #define DO 0375 /* Command to TIP */ #define DONT 0376 /* Negative of Command to TIP */ #define WILL 0373 /* Command to SERVER TELNET (Host) */ #define WONT 0374 /* Negative of Command to SERVER TELNET */ #define TRBIN 0 /* Transmit Binary Command */ FILE *LOGFP; char buff[BBUFSIZ]; int pagelen; char *tty; char XMITTYPE; int ARPA, RECVFLAG, SENDFLAG, FTP1, PMSG, NEWFLAG, LOGFLAG, MUNGMODE; int BIT7, VFLAG, BITMASK; int delay; FILE *tfp; /* Port pointer */ int lun; /* Lun of terminal */ int p[6]; /* Parameter block for qio */ struct iosb { /* I/O status block */ char stat; char term; int count; } io; struct gets { char name; char val; }; /* * The following structure contains the operating characteristics of the * terminal. Refer to the rsx11m i/o dirvers reference manual for details. */ struct gets stty[] = { { TC_ACR, 0 } , { TC_BIN, 1 } , { TC_CTS, 0 } , { TC_FDX, 1 } , { TC_NBR, 1 } , { TC_NEC, 1 } , { TC_SLV, 1 } , { TC_SMR, 1 } , #ifdef RSX11M+ {TC_TBM, 0 } , #endif #ifdef KSPACE {TC_TBS, 255 } , #endif #ifndef RSX11M+ {TC_RAT, 1 } , #endif { TC_WID, 255 } }; /* * The following structure is used to save the characteristics of the * terminal so they may be restored on exit of this program */ struct gets save[sizeof(stty)/sizeof(stty[0])]; /* * RSX buffers and pointers */ char rbuff[RBUFSIZ]; /* Receive buffer and pointers */ char *rnext = rbuff; /* Pointer to next character */ char *rend = rbuff; /* Pointer past last character */ char sbuff[SBUFSIZ]; /* Send buffer and pointers */ char *send = sbuff; /* Pointer past last character */ /* * Buffer to facilitate transmitting record containing record length */ char vbuff[VBUFSIZ]; char *vnext = vbuff; /* Pointer to next character */ char *vend = vbuff; /* Pointer to next character */ int procesc = FALSE; /* Processing an escped character in variable len rec */ main(argc, argv) int argc; char **argv; { char *logfile; int index; char flag; logfile = "xmodem.log"; /* Name of LOG File */ printf("\nXMODEM Version %d.%d", VERSION/10, VERSION%10); printf(" -- RSX-Based Remote File Transfer Facility\n"); if (argc < 3 || *argv[1] != '-') use(); index = 1; /* set index for loop */ delay = 3; /* assume FTP 3 delay */ PMSG = FALSE; /* turn off flags */ FTP1 = FALSE; /* assume FTP 3 (CP/M UG XMODEM2) */ RECVFLAG = FALSE; /* not receive */ SENDFLAG = FALSE; /* not send either */ VFLAG = FALSE; /* Not variable length binary records */ XMITTYPE = 't'; /* assume text */ NEWFLAG = FALSE; /* Start a new log file (else append to old) */ LOGFLAG = LOGDEFAULT; ARPA = FALSE; /* assume not on ARPA Net */ MUNGMODE = FALSE; /* protect files from overwriting */ BIT7 = FALSE; /* assume 8-bit communication */ while ((flag = argv[1][index++]) != '\0') switch (flag) { case 'a' : case 'A' : ARPA = TRUE; /* set ARPA Net */ break; case 'p' : case 'P' : PMSG = TRUE; /* print all messages */ break; case '1' : FTP1 = TRUE; /* select FTP 1 */ delay = 5; /* FTP 1 delay constant */ printf("\nXMODEM> TERM II FTP 1 Selected\n"); break; case 'n' : case 'N' : NEWFLAG = TRUE; /* Start a new log file */ break; case 'l' : case 'L' : if(LOGFLAG) LOGFLAG = FALSE; else LOGFLAG = TRUE; break; case 'r' : case 'R' : RECVFLAG = TRUE; /* receive file */ XMITTYPE = gettype(argv[1][index++]); if(XMITTYPE != 't' && XMITTYPE != 'b') use(); break; case 's' : case 'S' : SENDFLAG = TRUE; /* send file */ XMITTYPE = gettype(argv[1][index++]); if(XMITTYPE != 't' && XMITTYPE != 'b') use(); break; case 'v' : case 'V' : VFLAG = TRUE; /* varialbe length records */ break; case '7' : BIT7 = TRUE; /* transfer only 7 bits */ break; case '4' : FTP1 = TRUE; /* select FTP 1 (varient) */ BIT7 = TRUE; /* transfer only 7 bits */ delay = 5; /* FTP 1 delay constant */ printf("\nXMODEM> TERM II FTP 4 Selected\n"); break; default : printf("\nXMODEM> Invalid Flag"); exit(-1); } if (BIT7 && (XMITTYPE == 'b')) { printf("\nXMODEM> Fatal Error -- Both 7-Bit Transfer and "); printf("Binary Transfer Selected"); exit(-1); /* error exit */ } if (BIT7) /* set MASK value */ BITMASK = 0177; /* 7 significant bits */ else BITMASK = 0377; /* 8 significant bits */ if (PMSG) { printf("\nSupported File Transfer Protocols:"); printf("\n\tTERM II FTP 1"); printf("\n\tCP/M UG XMODEM2 (TERM II FTP 3)"); printf("\n\tTERM II FTP 4"); printf("\n\n"); } if (LOGFLAG) { if (!NEWFLAG) LOGFP = fopen(logfile, "a"); /* append to LOG file */ else LOGFP = fopen(logfile, "w"); /* new LOG file */ if(LOGFP == NULL) { printf("\nXMODEM> Can't open log file"); exit(-1); } fprintf(LOGFP,"\n\n++++++++\n"); fprintf(LOGFP,"\nXMODEM Version %d.%d\n", VERSION/10, VERSION%10); printf("\nXMODEM> LOG File '%s' is Open\n", logfile); } if (RECVFLAG && SENDFLAG) printf("\nXMODEM> Both Send and Receive Functions Specified"); if (!RECVFLAG && !SENDFLAG) printf("\nXMODEM> Neither Send nor Receive Functions Specified"); if (RECVFLAG) rfile(argv[2]); /* receive file */ else sfile(argv[2]); /* send file */ sflush(); /* for good measure */ } gettype(ichar) char ichar; { if (ichar == 't') return(ichar); if (ichar == 'b') return(ichar); if(ichar == 'v') { VFLAG = TRUE; return('t'); /* V mode exploits T mode */ } printf("\nXMODEM> Invalid Send/Receive Parameter - not t, b, or v"); exit(-1); } /* set ARPA Net for 8-bit transfers */ setarpa() { sendbyte(IAC); /* Is A Command */ sendbyte(WILL); /* Command to SERVER TELNET (Host) */ sendbyte(TRBIN); /* Command is: Transmit Binary */ sendbyte(IAC); /* Is A Command */ sendbyte(DO); /* Command to TIP */ sendbyte(TRBIN); /* Command is: Transmit Binary */ sflush(); sleep(3); /* wait for TIP to configure */ return; } /* reset the ARPA Net */ resetarpa() { sendbyte(IAC); /* Is A Command */ sendbyte(WONT); /* Negative Command to SERVER TELNET (Host) */ sendbyte(TRBIN); /* Command is: Don't Transmit Binary */ sendbyte(IAC); /* Is A Command */ sendbyte(DONT); /* Negative Command to TIP */ sendbyte(TRBIN); /* Command is: Don't Transmit Binary */ sflush(); return; } /* print error message and exit; if mode == TRUE, restore normal tty modes */ error(msg, mode) char *msg; int mode; { if (mode) restoremodes(TRUE); /* put back normal tty modes */ printf("XMODEM> %s\n", msg); if (LOGFLAG) { fprintf(LOGFP, "XMODEM Fatal Error: %s\n", msg); fclose(LOGFP); } exit(-1); } /** receive a file **/ rfile(name) char *name; { char mode; int fd, i, j, firstchar, sectnum, sectcurr, tmode; int sectcomp, errors, errorflag, recfin; register int bufctr, checksum; register int c; int errorchar, fatalerror, startstx, inchecksum, endetx, endenq; long recvsectcnt; mode = XMITTYPE; /* set t/b mode */ if(VFLAG) /* variable length */ fd = fopen(name, "wun"); else if(mode == 't') fd = fopen(name, "w"); else /* fixed length */ fd = fopen(name, "wn"); if (fd == NULL) error("Can't create file for receive", FALSE); setmodes(); /* setup tty modes for xfer */ printf("\r\nXMODEM> File Name: %s", name); if (LOGFLAG) { fprintf(LOGFP, "\n----\nXMODEM Receive Function\n"); fprintf(LOGFP, "File Name: %s\n", name); if (FTP1) if (!BIT7) fprintf(LOGFP, "TERM II File Transfer Protocol 1 Selected\n"); else fprintf(LOGFP, "TERM II File Transfer Protocol 4 Selected\n"); else fprintf(LOGFP, "TERM II File Transfer Protocol 3 (CP/M UG) Selected\n"); if (BIT7) fprintf(LOGFP, "7-Bit Transmission Enabled\n"); else fprintf(LOGFP, "8-Bit Transmission Enabled\n"); } printf("\r\nXMODEM> "); if (BIT7) printf("7-Bit"); else printf("8-Bit"); printf(" Transmission Enabled"); printf("\r\nXMODEM> Ready to RECEIVE File\r\n"); recfin = FALSE; sectnum = errors = 0; fatalerror = FALSE; /* NO fatal errors */ recvsectcnt = 0; /* number of received sectors */ if (mode == 't') tmode = TRUE; else tmode = FALSE; if (FTP1) { while (readbyte(4) != SYN); sendbyte(ACK); /* FTP 1 Sync */ } else sendbyte(NAK); /* FTP 3 Sync */ do { errorflag = FALSE; do { firstchar = readbyte(6); } while ((firstchar != SOH) && (firstchar != EOT) && (firstchar != TIMEOUT)); if (firstchar == TIMEOUT) { if (LOGFLAG) fprintf(LOGFP, "Timeout on Sector %d\n", sectnum); errorflag = TRUE; } if (firstchar == SOH) { if (FTP1) readbyte(5); /* discard leading zero */ sectcurr = readbyte(delay); sectcomp = readbyte(delay); if (FTP1) startstx = readbyte(delay); /* get leading STX */ if ((sectcurr + sectcomp) == BITMASK) { if (sectcurr == ((sectnum+1)&BITMASK)) { checksum = 0; for (j = bufctr = 0; j < BBUFSIZ; j++) { buff[bufctr] = c = readbyte(delay); checksum = ((checksum+c)&BITMASK); if (!tmode) { /* binary mode */ bufctr++; continue; } if(procesc) procesc = FALSE; else if(VFLAG && c == ESCAPE) procesc = TRUE; else { if (c == CR) continue; /* skip CR's */ if (c == CTRLZ) { /* skip CP/M EOF char */ recfin = TRUE; /* flag EOF */ continue; } } if (!recfin) bufctr++; } if (FTP1) endetx = readbyte(delay); /* get ending ETX */ inchecksum = readbyte(delay); /* get checksum */ if (FTP1) endenq = readbyte(delay); /* get ENQ */ if (checksum == inchecksum) { /* good checksum */ errors = 0; recvsectcnt++; sectnum = sectcurr; /* update sector counter */ for(i=0; i */ sendbyte(ACK); } else { if (LOGFLAG) fprintf(LOGFP, "Checksum Error on Sector %d\n", sectnum); errorflag = TRUE; } } else { if (sectcurr == sectnum) { while(readbyte(3) != TIMEOUT); if (FTP1) sendbyte(ESC); /* FTP 1 requires */ sendbyte(ACK); } else { if (LOGFLAG) { fprintf(LOGFP, "Phase Error - Received Sector is "); fprintf(LOGFP, "%d while Expected Sector is %d\n", sectcurr, ((sectnum+1)&BITMASK)); } errorflag = TRUE; fatalerror = TRUE; if (FTP1) sendbyte(ESC); /* FTP 1 requires */ sendbyte(CAN); } } } else { if (LOGFLAG) fprintf(LOGFP, "Header Sector Number Error on Sector %d\n", sectnum); errorflag = TRUE; } } if (FTP1 && !errorflag) { if (startstx != STX) { errorflag = TRUE; /* FTP 1 STX missing */ errorchar = STX; } if (endetx != ETX) { errorflag = TRUE; /* FTP 1 ETX missing */ errorchar = ETX; } if (endenq != ENQ) { errorflag = TRUE; /* FTP 1 ENQ missing */ errorchar = ENQ; } if (errorflag && LOGFLAG) { fprintf(LOGFP, "Invalid Packet-Control Character: "); switch (errorchar) { case STX : fprintf(LOGFP, "STX"); break; case ETX : fprintf(LOGFP, "ETX"); break; case ENQ : fprintf(LOGFP, "ENQ"); break; default : fprintf(LOGFP, "Error"); break; } fprintf(LOGFP, "\n"); } } if (errorflag == TRUE) { errors++; while (readbyte(3) != TIMEOUT); sendbyte(NAK); } } while ((firstchar != EOT) && (errors != ERRORMAX) && !fatalerror); if ((firstchar == EOT) && (errors < ERRORMAX)) { if (!FTP1) { sendbyte(ACK); sflush(); } fclose(fd); restoremodes(FALSE); /* restore normal tty modes */ if (FTP1) while (readbyte(3) != TIMEOUT); /* flush EOT's */ sleep(3); /* give other side time to return to terminal mode */ if (LOGFLAG) { fprintf(LOGFP, "\nReceive Complete\n"); fprintf(LOGFP,"Number of Received CP/M Records is %ld\n", recvsectcnt); fclose(LOGFP); } printf("\n"); exit(0); } else { if (LOGFLAG && FTP1 && fatalerror) fprintf(LOGFP, "Synchronization Error"); error("TIMEOUT -- Too Many Errors", TRUE); } } /** send a file **/ sfile(name) char *name; { char mode; int fd, charval, attempts; int nlflag, sendfin, tmode; register int bufctr, checksum, sectnum; char c; int sendresp; /* response char to sent block */ mode = XMITTYPE; /* set t/b mode */ if(VFLAG) fd = fopen(name, "run"); else if(mode == 't') fd = fopen(name, "r"); else fd = fopen(name, "rn"); if (fd == NULL) { if (LOGFLAG) fprintf(LOGFP, "Can't Open File\n"); error("Can't open file for send", FALSE); } setmodes(); /* setup tty modes for xfer */ printf("\r\nXMODEM> File Name: %s", name); if (LOGFLAG) { fprintf(LOGFP, "\n----\nXMODEM Send Function\n"); fprintf(LOGFP, "File Name: %s\n", name); } if (LOGFLAG) { if (FTP1) if (!BIT7) fprintf(LOGFP, "TERM II File Transfer Protocol 1 Selected\n"); else fprintf(LOGFP, "TERM II File Transfer Protocol 4 Selected\n"); else fprintf(LOGFP, "TERM II File Transfer Protocol 3 (CP/M UG) Selected\n"); if (BIT7) fprintf(LOGFP, "7-Bit Transmission Enabled\n"); else fprintf(LOGFP, "8-Bit Transmission Enabled\n"); } printf("\r\nXMODEM> "); if (BIT7) printf("7-Bit"); else printf("8-Bit"); printf(" Transmission Enabled"); printf("\r\nXMODEM> Ready to SEND File\r\n"); if (mode == 't') tmode = TRUE; else tmode = FALSE; sendfin = nlflag = FALSE; attempts = 0; if (FTP1) { sendbyte(SYN); /* FTP 1 Synchronize with Receiver */ while (readbyte(5) != ACK) { if(++attempts > RETRYMAX*6) error("Remote System Not Responding", TRUE); sendbyte(SYN); } } else { while (readbyte(30) != NAK) /* FTP 3 Synchronize with Receiver */ if (++attempts > RETRYMAX) error("Remote System Not Responding", TRUE); } sectnum = 1; /* first sector number */ attempts = 0; do { for (bufctr=0; bufctr < BBUFSIZ;) { if (nlflag) { buff[bufctr++] = LF; /* leftover newline */ nlflag = FALSE; } if ((charval = getbyte(fd)) == EOF) { /* EOF for read */ sendfin = TRUE; /* this is the last sector */ if (!bufctr) /* if EOF on sector boundary */ break; /* avoid sending extra sector */ if (tmode) buff[bufctr++] = CTRLZ; /* Control-Z for CP/M EOF */ else bufctr++; continue; } c = charval; if(!VFLAG) { if (tmode && c == LF) { /* text mode & Unix newline? */ if (c == LF){ /* Unix newline? */ buff[bufctr++] = CR; /* insert carriage return */ if (bufctr < BBUFSIZ) buff[bufctr++] = LF; /* insert Unix newline */ else nlflag = TRUE; /* insert newline on next sector */ } continue; } } buff[bufctr++] = c; /* copy the char without change */ } attempts = 0; if (!bufctr) /* if EOF on sector boundary */ break; /* avoid sending empty sector */ do { sendbyte(SOH); /* send start of packet header */ if (FTP1) sendbyte(0); /* FTP 1 Type 0 Packet */ sendbyte(sectnum); /* send current sector number */ sendbyte(-sectnum-1); /* and its complement */ if (FTP1) sendbyte(STX); /* send STX */ checksum = 0; /* init checksum */ for (bufctr=0; bufctr < BBUFSIZ; bufctr++) { sendbyte(buff[bufctr]); /* send the byte */ if (ARPA && (buff[bufctr]==0xff)) /* ARPA Net FFH esc */ sendbyte(buff[bufctr]); /* send 2 FFH's for one */ checksum = ((checksum+buff[bufctr])&BITMASK); } /* while (readbyte(3) != TIMEOUT); flush chars from line */ if (FTP1) sendbyte(ETX); /* send ETX */ sendbyte(checksum); /* send the checksum */ if (FTP1) sendbyte(ENQ); /* send ENQ */ attempts++; if (FTP1) { sendresp = NAK; /* prepare for NAK */ if (readbyte(10) == ESC) sendresp = readbyte(10); } else sendresp = readbyte(10); /* get response */ if ((sendresp != ACK) && LOGFLAG) { fprintf(LOGFP, "Non-ACK Received on Sector %d\n", sectnum); if (sendresp == TIMEOUT) fprintf(LOGFP, "This Non-ACK was a TIMEOUT\n"); } } while((sendresp != ACK) && (attempts != RETRYMAX)); sectnum++; /* increment to next sector number */ } while (!sendfin && (attempts != RETRYMAX)); if (attempts == RETRYMAX) error("Remote System Not Responding", TRUE); attempts = 0; if (FTP1) while (attempts++ < 10) sendbyte(EOT); else { sendbyte(EOT); /* send 1st EOT */ while ((readbyte(15) != ACK) && (attempts++ < RETRYMAX)) sendbyte(EOT); if (attempts >= RETRYMAX) error("Remote System Not Responding on Completion", TRUE); } fclose(fd); restoremodes(FALSE); sleep(5); /* give other side time to return to terminal mode */ if (LOGFLAG) { fprintf(LOGFP, "\nSend Complete\n"); fclose(LOGFP); } printf("\n"); exit(0); } /* get a byte from data stream -- timeout if "seconds" elapses */ readbyte(seconds) unsigned seconds; { register c; /* * Flush any characters waiting to be sent */ if(send != sbuff) sflush(); /* * If there is a character waiting send else, else wait for * next character or timeout */ if(rnext == rend) { mrkt(WAITEF, seconds, 2, 0); /* Set up timer */ wtse(WAITEF); if(rnext == rend) { return(TIMEOUT); } cmkt(WAITEF, 0); /* Cancel timer */ } c = *rnext++; if(rnext == rbuff+RBUFSIZ) rnext = rbuff; return((c&BITMASK)); /* return the char */ } /* * Local ast to put input characters in Local cirular buff & notify waiter. */ ast() { register num; astset(); *rend++ = gtdp(0) ; if(rend == rbuff+RBUFSIZ) rend = rbuff; if(rnext == rend) error( "Receive buffer overflow", TRUE); setf(WAITEF); astx(1); /* Remove character, terminal number off stack */ } /* send a byte to data stream */ sendbyte(c) char c; { rnext = rbuff; /* Re-sync sends/recieves */ rend = rbuff; *send++ = c&BITMASK; if(send == sbuff+SBUFSIZ) return; } /* * Flush any characters waiting to be output */ sflush() { if(send != sbuff) { fput(sbuff, send-sbuff, tfp); send = sbuff; } } /* * Put a byte handling escape sequences */ int putesc = FALSE; /* True if processing unix style escape */ putbyte(c, fd) char c; FILE *fd; { if(VFLAG) { if(putesc) { *vnext++ = c; putesc = FALSE; } else switch(c) { case ESCAPE: putesc = TRUE; break; case LF: fput(vbuff, vnext-vbuff, fd); vnext = vbuff; break; default: *vnext++ = c; } } else putc(c, fd); } /* * Get a byte handling escape sequences */ getbyte(fd) FILE *fd; { register c, len; if(VFLAG) { if(vnext == vbuff) { len = fget(vbuff, VBUFSIZ, fd); if(len == VBUFSIZ) error("Variable length buffer overflow", TRUE); if(feof(fd) == 1) return(EOF); vend = vbuff+len; } if(vnext == vend) { vnext = vbuff; return('\n'); } else if(procesc) { procesc = FALSE; return(*vnext++ & BITMASK); } else switch(*vnext) { case CR: case LF: case CTRLZ: case ESCAPE: procesc = TRUE; return(ESCAPE); break; default: return(*vnext++ & BITMASK); } } else return(getc(fd)); } /* * Save current status of the terminal then set up */ setmodes() { register i; /* * Open a path to the remote terminal */ if((tfp = fopen("XM:", "wn")) == NULL) { error("Can't open path on XM:\n", FALSE); exit(); } /* * Get terminal characteristics */ copy(save, stty, sizeof(save)); lun = fileno(tfp); /* Lun for qio's */ ata(); p[0] = &save; p[1] = sizeof(save); i = qiow(SF_GMC, lun, QIOEF, &io, 0, p); if(i != IS_SUC || io.stat != IS_SUC) error("Can't get old TTY prarmters", FALSE); settty(); /* Set up tty */ if (ARPA) /* set 8-bit on ARPA Net */ setarpa(); return; } /* * Set proper characteristics of terminal */ settty() { register i; p[0] = &stty; p[1] = sizeof(stty); i = qiow(SF_SMC, lun, QIOEF, &io, 0, p); if(i != IS_SUC || io.stat != IS_SUC) error("Can't set TTY Parameters", TRUE); } /* * Restore terminal to original state */ restoremodes(errcall) int errcall; { register i; if (ARPA) /* if ARPA Net, reconfigure */ resetarpa(); /* * Restore terminal to proper config */ p[0] = &save; p[1] = sizeof(save); i = qiow(SF_SMC, lun, QIOEF, &io, 0, p); if(i != IS_SUC || io.stat != IS_SUC) if (!errcall) error("RESET - Can't restore normal TTY Params", FALSE); else { printf("XMODEM> "); printf("RESET - Can't restore normal TTY Params\n"); } det(); return; } /* * Attatch local terminal */ ata() { p[0] = ast; p[1] = 0; p[2] = 0; if(qiow(IO_ATA, lun, QIOEF, 0, 0, p) != IS_SUC) error("Can't attach terminal", FALSE); } /* * Detatch terminal */ det() { if(qiow(IO_DET, lun, QIOEF, 0, 0, 0) != IS_SUC) error("Can't detatch terminal", FALSE); } use() { printf("\nUsage: \n\txmodem "); printf("-[rb!rt!rv!sb!st!sv][p][l][1][a][n][7][4]"); printf(" filename\n"); printf("\n"); printf("\n\trb <-- Receive Binary"); printf("\n\trt <-- Receive Text"); printf("\n\trv <-- Receive Variable Length Binary (RSX)"); printf("\n\tsb <-- Send Binary"); printf("\n\tst <-- Send Text"); printf("\n\tsv <-- Send Variable Length Binary (RSX)"); printf("\n\tp <-- Turn ON Parameter Display"); #if LOGDEFAULT printf("\n\tl <-- (ell) Turn OFF LOG File Entries"); #else printf("\n\tl <-- (ell) Turn ON LOG File Entries"); #endif printf("\n\t1 <-- (one) Employ TERM II FTP 1"); printf("\n\ta <-- Turn ON ARPA Net Flag"); printf("\n\tn <-- Start a new xmodem.log file (else append)"); printf("\n\t7 <-- Enable 7-bit transfer mask"); printf("\n\t4 <-- Enable TERM II FTP 4"); printf("\n"); exit(-1); }