info-vax@ucbvax.ARPA (04/02/85)
From: J.R.COWIE%edinburgh@ucl-cs.arpa I would be grateful for information on the following:- 1/ Where can I obtain a copy of a program which will handle the XMODEM file transfer protocol to run on UNIX 4.2? 2/ Is there a set of integrated tools available to handle micro-development work under UNIX? I am thinking of cross assemblers and compilers for several micro-processors, a common linker, library management, debugger and down- loading facilities. This could be either a commercial package or public domain. Thanks in advance. Jim Cowie, University of Strathclyde, Glasgow, Scotland.
info-vax@ucbvax.ARPA (04/03/85)
From: ihnp4!houxm!whuxl!whuxlm!jph@BERKELEY Here is a version of UMODEM for BSD 4.1 ===============cut here================= /* * UMODEM -- 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. * * NOTE: This program is a modified version of the * the UMODEM31.c program which was obtained from * a RCP/M. The original program was written by * R. Conn and ran under Version 7 unix. This * version was modified by J. Antrosiglio (BTL-HO) * to run under 4.0 UNIX and future releases. * The changes made were to upgrade the code to * use the termio structure rather than the old * sgttyb structure, and use the new ioctl calling * parameters. * * VERSION 4.0 - first release * changed on 09/82 : gettype() * this function called error() to close * the logfile which is not open while checking * input parameters. * * VERSION 4.1 * changed on 01/83 : argument parser * the default case called error() to close * the logfile which is not open at this time. * * * ANYONE INTERESTED IN THE VERSION 7 PROGRAM * MAY OBTAIN A COPY FROM J. C. ANTROSIGLIO * HO 3G-321 Ext. 2374 */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <termio.h> #include <signal.h> #define VERSION 41 /* Version Number */ #define TRUE 1 #define FALSE 0 /* 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!) */ /* UMODEM 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! */ #define CREATMODE 0644 /* mode for created files */ /* 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 */ struct termio ttys, ttysnew, ttystemp; /* for stty terminal mode calls */ struct stat statbuf; /* for terminal message on/off control */ FILE *LOGFP, *fopen(); char buff[BBUFSIZ]; int pagelen; char *ttyname(); /* forward declaration for C */ char *tty; char XMITTYPE; int ARPA, RECVFLAG, SENDFLAG, FTP1, PMSG, DELFLAG, LOGFLAG, MUNGMODE; int STATDISP, BIT7, BITMASK; int delay; long tloc; /* holds the current time */ alarmfunc(); main(argc, argv) int argc; char **argv; { char *logfile; int index; char flag; logfile ="umodem.log"; /* name of log file */ printf("\nUMODEM Version %d.%d", VERSION/10, VERSION%10); printf(" -- UNIX-Based Remote File Transfer Facility\n"); if (argc < 3 || *argv[1] != '-') { printf("\nUsage: \n\tumodem "); printf("-[options]"); printf(" filename\n"); printf("\n\tOption available:\n"); printf("\n"); printf("\n\trb <-- Receive Binary"); printf("\n\trt <-- Receive Text"); printf("\n\tsb <-- Send Binary"); printf("\n\tst <-- Send Text"); printf("\n\tp <-- Turn ON Parameter Display"); printf("\n\tl <-- (ell) Turn OFF LOG File Entries"); printf("\n\t1 <-- (one) Employ TERM II FTP 1"); printf("\n\ta <-- Turn ON ARPA Net Flag"); printf("\n\tm <-- Allow file overwiting on receive"); printf("\n\td <-- Delete umodem.log File before starting"); printf("\n\ty <-- Display file status (size) information only"); printf("\n\t7 <-- Enable 7-bit transfer mask"); printf("\n\t4 <-- Enable TERM II FTP 4"); printf("\n"); exit(-1); } 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 */ XMITTYPE = 't'; /* assume text */ DELFLAG = FALSE; /* do NOT delete log file before starting */ LOGFLAG = TRUE; /* assume log messages */ ARPA = FALSE; /* assume not on ARPA Net */ MUNGMODE = FALSE; /* protect files from overwriting */ STATDISP = FALSE; /* assume not a status display */ BIT7 = FALSE; /* assume 8-bit communication */ while ((flag = argv[1][index++]) != '\0') switch (flag) { case 'a' : ARPA = TRUE; /* set ARPA Net */ break; case 'p' : PMSG = TRUE; /* print all messages */ break; case '1' : FTP1 = TRUE; /* select FTP 1 */ delay = 5; /* FTP 1 delay constant */ printf("\nUMODEM: TERM II FTP 1 Selected\n"); break; case 'd' : /*DELFLAG = TRUE; delete log file first */ break; case 'l' : /*LOGFLAG = FALSE; turn off log report */ break; case 'r' : RECVFLAG = TRUE; /* receive file */ XMITTYPE = gettype(argv[1][index++]); /* get t/b */ break; case 's' : SENDFLAG = TRUE; /* send file */ XMITTYPE = gettype(argv[1][index++]); break; case 'm' : MUNGMODE = TRUE; /* allow overwriting of files */ break; case 'y' : STATDISP = TRUE; /* display file status */ 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("\nUMODEM: TERM II FTP 4 Selected\n"); break; default : printf("Invalid Flag\n"); exit(-1); } if (BIT7 && (XMITTYPE == 'b')) { printf("\nUMODEM: Fatal Error -- Both 7-Bit Transfer and "); printf("Binary Transfer Selected"); exit(-1); /* error exit to UNIX */ } 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 (!DELFLAG) LOGFP = fopen(logfile, "a"); /* append to LOG file */ else LOGFP = fopen(logfile, "w"); /* new LOG file */ fprintf(LOGFP,"\n\n++++++++\n"); fprintf(LOGFP,"\nUMODEM Version %d.%d\n", VERSION/10, VERSION%10); time(&tloc); /* get current time */ fprintf(LOGFP,"\n%sLogged user is %s \n",ctime(&tloc),getlogin()); printf("\nUMODEM: LOG File %s is Open\n",logfile); } if (STATDISP) yfile(argv[2]); /* status of a file */ if (RECVFLAG && SENDFLAG) error("Both Send and Receive Functions Specified", FALSE); if (!RECVFLAG && !SENDFLAG) error("Neither Send nor Receive Functions Specified", FALSE); if (RECVFLAG) { if(open(argv[2], 0) != -1) /* possible abort if file exists */ { printf("\nUMODEM: Warning -- Target File Exists\n"); if( MUNGMODE == FALSE ) error("Fatal - Can't overwrite file\n",FALSE); printf("UMODEM: Overwriting Target File\n"); } rfile(argv[2]); /* receive file */ } else sfile(argv[2]); /* send file */ } gettype(ichar) char ichar; { if (ichar == 't') return(ichar); if (ichar == 'b') return(ichar); printf("UMODEM: Invalid Send/Receive Parameter - missing r or t\n"); exit(-1); } /* set tty modes for UMODEM transfers */ setmodes() { char count; if (ioctl(0,TCGETA,&ttys)<0) /* get present tty modes and save them */ /* ttys structure */ error("Can't get TTY Parameters", TRUE); tty = ttyname(0); /* identify current tty */ /* init ttysnew structure */ ttysnew.c_iflag = 0; /* clear all input modes */ ttysnew.c_oflag = 0; /* clear all output modes */ ttysnew.c_cflag = (ttys.c_cflag&CBAUD)|CREAD; /* save baud rate */ ttysnew.c_lflag = 0; /* clear line discipline modes */ ttysnew.c_line = ttys.c_line; /* line discipline */ for(count=0;count < NCC;count++) ttysnew.c_cc[count] = ttys.c_cc[count]; /* set raw mode */ ttysnew.c_cc[VMIN] = 1; ttysnew.c_cc[VTIME] = 1; ttysnew.c_cflag |= CS8; /* set new paramters */ if (ioctl(0,TCSETAW,&ttysnew) < 0) error("Can't set new TTY Parameters", TRUE); if (stat(tty, &statbuf) < 0) /* get tty status */ error("Can't get your TTY Status", TRUE); if (PMSG) { printf("\r\nUMODEM: TTY Device Parameters Altered"); ttyparams(); /* print tty params */ } if (ARPA) /* set 8-bit on ARPA Net */ setarpa(); return; } /* 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 */ sleep(3); /* wait for TIP to configure */ return; } /* restore normal tty modes */ restoremodes(errcall) int errcall; { if (ARPA) /* if ARPA Net, reconfigure */ resetarpa(); if (ioctl(0,TCSETAW,&ttys) < 0) { if (!errcall) error("RESET - Can't restore normal TTY Params", FALSE); else { printf("UMODEM: "); printf("RESET - Can't restore normal TTY Params\n"); } } if (PMSG) { printf("UMODEM: TTY Device Parameters Restored\n"); ttyparams(); /* print tty params */ } 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 */ 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("UMODEM: %s\n", msg); if (LOGFLAG) { fprintf(LOGFP, "UMODEM Fatal Error: %s\n", msg); fclose(LOGFP); } exit(-1); } /** print status (size) of a file **/ yfile(name) char *name; { printf("UMODEM File Status Display for %s\n", name); if (LOGFLAG) fprintf(LOGFP,"UMODEM File Status Display for %s\n", name); if (open(name,0) < 0) { printf("File %s does not exist\n", name); if (LOGFLAG) fprintf(LOGFP,"File %s does not exist\n", name); exit(-1); /* error exit to UNIX */ } prfilestat(name); /* print status */ printf("\n"); if (LOGFLAG) { fprintf(LOGFP,"\n"); fclose(LOGFP); } exit(0); /* exit to UNIX -- no error */ } /** receive a file **/ rfile(name) char *name; { char mode; int fd, 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 ((fd = creat(name, CREATMODE)) < 0) error("Can't create file for receive", FALSE); setmodes(); /* setup tty modes for xfer */ printf("\r\nUMODEM: File Name: %s", name); if (LOGFLAG) { fprintf(LOGFP, "\n----\nUMODEM 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\nUMODEM: "); if (BIT7) printf("7-Bit"); else printf("8-Bit"); printf(" Transmission Enabled"); printf("\r\nUMODEM: 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 (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 */ if (write(fd, buff, bufctr) < 0) error("File Write Error", TRUE); else { if (FTP1) sendbyte(ESC); /* FTP 1 requires <ESC> */ 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 <ESC> */ 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 <ESC> */ 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); close(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 ((fd = open(name, 0)) < 0) { 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\nUMODEM: File Name: %s", name); if (LOGFLAG) { fprintf(LOGFP, "\n----\nUMODEM Send Function\n"); fprintf(LOGFP, "File Name: %s\n", name); } prfilestat(name); /* print file size statistics */ 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\nUMODEM: "); if (BIT7) printf("7-Bit"); else printf("8-Bit"); printf(" Transmission Enabled"); printf("\r\nUMODEM: 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 = read(fd, &c, 1)) < 0) error("File Read Error", TRUE); if (charval == 0) /* 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; } 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, "%x Non-ACK Received on Sector %d\n" ,sendresp,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); } close(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); } /* print file size status information */ prfilestat(name) char *name; { struct stat filestatbuf; /* file status info */ stat(name, &filestatbuf); /* get file status bytes */ printf("\r\nUMODEM: Estimated File Size %ldK, %ld Records, %ld Bytes", (filestatbuf.st_size/1024)+1, (filestatbuf.st_size/128)+1, filestatbuf.st_size); if (LOGFLAG) fprintf(LOGFP,"Estimated File Size %ldK, %ld Records, %ld Bytes\n", (filestatbuf.st_size/1024)+1, (filestatbuf.st_size/128)+1, filestatbuf.st_size); return; } /* get a byte from data stream -- timeout if "seconds" elapses */ readbyte(seconds) unsigned seconds; { int c; signal(SIGALRM,alarmfunc); /* catch alarms */ alarm(seconds); /* set the alarm clock */ if (read(0, &c, 1) < 0) /* get a char; error means we timed out */ { return(TIMEOUT); } alarm(0); /* turn off the alarm */ return((c&BITMASK)); /* return the char */ } /* send a byte to data stream */ sendbyte(data) char data; { char dataout; dataout = (data&BITMASK); /* maskyr 7 or 8 bits */ write(1, &dataout, 1); /* write the byte */ return; } /* function for alarm clock timeouts */ alarmfunc() { return; /* this is basically a dummy function to force error */ /* status return on the "read" call in "readbyte" */ } /* print data on TTY setting */ ttyparams() { ioctl(0,TCGETA,&ttystemp); tty = ttyname(0); stat(tty, &statbuf); printf("\r\n\nTTY Device Parameter Display"); printf("\r\n\tTTY Device Name is %s\r\n\n", tty); printf("\tEcho Enabled "); pryn(ECHO,ttystemp.c_lflag); printf("\tAny Parity Allowed "); pryn(PARENB,ttystemp.c_cflag); printf("\tEven Parity Allowed"); pryn(PARODD,ttystemp.c_cflag); printf("\tLower Case Map "); pryn(OLCUC,ttystemp.c_oflag); printf("\tTabs Expanded "); pryn(TAB3,ttystemp.c_oflag); printf("\tCR to NL(input) "); pryn(ICRNL,ttystemp.c_iflag); printf("\tNL to CR/NL(output)"); pryn(ONLCR,ttystemp.c_oflag); printf("\tRaw Input Disabled "); pryn(ISIG,ttystemp.c_lflag); printf("\tRaw Output Disabled"); pryn(OPOST,ttystemp.c_oflag); printf("\tTTY Input Rate : "); prbaud(ttystemp.c_cflag&CBAUD); printf("\tTTY Output Rate : "); prbaud(ttystemp.c_cflag&CBAUD); } pryn(iflag,pointer) int iflag; int pointer; { if (pointer&iflag) printf(": Yes\r\n"); else printf(": No\r\n"); } prbaud(speed) char speed; { switch (speed) { case B50 : printf("50"); break; case B75 : printf("75"); break; case B110 : printf("110"); break; case B134 : printf("134.5"); break; case B150 : printf("150"); break; case B200 : printf("200"); break; case B300 : printf("300"); break; case B600 : printf("600"); break; case B1200 : printf("1200"); break; case B1800 : printf("1800"); break; case B2400 : printf("2400"); break; case B4800 : printf("4800"); break; case B9600 : printf("9600"); break; case EXTA : printf("External A"); break; case EXTB : printf("External B"); break; default : printf("Error"); break; } printf(" Baud\r\n"); }
info-vax@ucbvax.ARPA (04/05/85)
From: dual!mordor!seismo!harvard!gcc-bill!brad@BERKELEY (Brad Parker) I have some very simple code, which does uploads only from unix. After doing this, I discovered macput.c does xmodem with some xtra stuff. I would grab a copy of macput.c (unix program for uploading files to a mac) and hack it a bit. If you want my upload code, I'd be glad to mail it to you. If I were you, I'd find a copy of Kermit - all the work should be done, and there are versions available for most popular micro's. J Bradford Parker uucp: seismo!harvard!gcc-bill!brad "I've seen this happen in other people's lives... and now it's happening in mine." -The Smiths
info-vax@ucbvax.ARPA (04/05/85)
From: dual!mordor!seismo!harvard!bu-cs!root@BERKELEY (BostonU SysMgr) Hope this helps... --------------------modem.c-------------------- /* * a version of Ward Christensen's MODEM program for * UNIX v7, 4.1bsd * * by Andrew Scott Beals * (SJOBRG.ANDY%MIT-OZ@MIT-MC) * * features added by BZS (root%bostonu@csnet-relay) * * sub options * a ascii (cr/lf->\n etc.) * b binary (good luck) * q send to our laser printer * changes by BZS * removed requirement of '-' for first arg * so essentially same as TOPS-20 version * (especially from our dandelion) */ #include <stdio.h> #include <ctype.h> #include <signal.h> #include <sgtty.h> #define uchar unsigned char #define SLEEP 30 #define ACKTIMO 30 /* longest we will wait for an ACK */ /* Protocol characters used */ #define SOH 1 /* Start Of Header */ #define EOT 4 /* End Of Transmission */ #define ACK 6 /* ACKnowlege */ #define NAK 0x15 /* Negative AcKnowlege */ short ttyhold; struct sgttyb ttymode; char *prog ; /* BZS argv[0] */ int rflag ; int sflag ; int aflag = 1 ; /* default is ASCII */ int qflag ; /* send to Qms printer Queue with -q flag */ int pflag ; /* third arg is a command to pipe to */ int Sflag ; /* login server */ char *tmpf = "/tmp/MODXXXXXX" ; #define SERVER "-MODEM" #define PRCMD "qpr -q %s" #undef tolower #define tolower(x) (isupper(x) ? (x) - ' ' : x) /* * collect all error messages here for later logging etc. */ eprint(fmt,a,b,c,d) char *fmt, *a, *b, *c, *d ; { fprintf(stderr,fmt,a,b,c,d) ; } usage() { eprint("Usage: %s [-]<option><suboption> filename\n",prog) ; eprint("\tWhere option can be either r for receive\n") ; eprint("\tor s for send and suboptions are:\n") ; eprint("\t\ta\tASCII (text) file\n") ; eprint("\t\tb\tBINARY file\n") ; eprint("\t\tq\tLASER PRINT FILE (implies ASCII)\n") ; eprint("\t\t\t(no file name for print files)\n") ; eprint("\t\tp\tpipe received data, 2nd arg is UNIX command\n") ; exit(1) ; } main(argc,argv) int argc; char **argv; { register uchar checksum,index,blocknumber,errorcount, character; uchar sector[128]; int foo,timeout(); FILE *fout ; char *ap ; char *fname, *pcmd ; static char buf[BUFSIZ+1] ; prog = *argv ; if(strcmp(prog,SERVER) == 0) { ++qflag ; ++Sflag ; ++rflag ; goto serv1 ; } if(argc < 2) usage() ; /* * skip but don't require initial dash in option list */ ap = argv[1] ; if(*ap == '-') ++ap ; /* * options * accept either case, twenex people expect this */ if(tolower(*ap) == 'r') ++rflag ; else if(tolower(*ap) == 's') ++sflag ; else usage() ; ++ap ; /* * suboptions */ switch(tolower(*ap)) { case '\0' : break ; case 'a' : break ; /* DEFAULT */ case 'p' : ++pflag ; break ; case 'q' : ++qflag ; break ; case 'b' : aflag = 0 ; break ; case 'h' : /* FALL THRU */ default : usage() ; } /* * check for other absurdities */ if(*++ap) usage() ; if(sflag && (qflag || pflag)) { eprint("%s: only use 'a' or 'b' with 's'\n",prog) ; exit(1) ; } /* * The file name */ serv1: if(qflag || pflag) { mktemp(tmpf) ; fname = tmpf ; } else if(argc != 3) usage() ; else fname = argv[2] ; if(pflag) pcmd = argv[2] ; gtty(0,&ttymode); ttyhold=ttymode.sg_flags; ttymode.sg_flags |= RAW ; ttymode.sg_flags& = ~ECHO; if(rflag) goto rec ; /* (else) */ /* send a file to the remote */ stty(0,&ttymode); if((foo=open(fname,0))==-1) { fprintf(stderr,"can't open %s for send!\7\n",fname); die(1); } fprintf(stderr,"file open, ready to send\r\n"); fflush(stderr); fflush(stdout); errorcount=0; blocknumber=1; signal(SIGALRM,timeout); alarm(80); while((getchar()!=NAK)&&(errorcount<10)) ++errorcount; alarm(0); #ifdef DEBUG fprintf(stderr,"transmission beginning\r\n"); fflush(stderr); #endif if(errorcount==10) { error: fprintf(stderr,"too many errors...aborting\7\7\7\r\n"); die(1); } while(read(foo,sector,sizeof sector) > 0) { errorcount=0; while(errorcount<10) { #ifdef DEBUG fprintf(stderr,"{%d} ",blocknumber); fflush(stderr); #endif putchar(SOH); /* here is our header */ putchar(blocknumber); /* the block number */ putchar(~blocknumber); /* & its complement */ checksum=0; for(index=0;index<128;index++) { putchar(sector[index]); checksum+=sector[index]; } putchar(checksum); /* tell our checksum */ fflush(stdout); alarm(ACKTIMO) ; if(getchar() != ACK) ++errorcount; else break; alarm(0) ; } alarm(0) ; if(errorcount==10)goto error; ++blocknumber; } index=1; while(index) { putchar(EOT); fflush(stdout); index=getchar()==ACK; } fprintf(stderr,"Transmission complete.\r\n"); fflush(stderr); die(0); rec: /* receive a file */ if(access(fname,0) == 0) { fprintf(stderr, "%s exists; you have 10 seconds to abort (^C)\r\n", fname); fflush(stderr); sleep(10); fprintf(stderr,"Too late!\r\n"); fflush(stderr); } stty(0,&ttymode); if((fout = fopen(fname,"w")) == NULL) { perror(fname); die(1); } printf("you have 30 seconds to start transmission..."); fflush(stdout); sleep(SLEEP); /* wait for the user to get his act together */ #ifdef DEBUG fprintf(stderr,"Starting...\r\n"); fflush(stderr); #endif putchar(NAK); fflush(stdout); errorcount=0; blocknumber=1; while((character=getchar())!=EOT) { register uchar not_ch; if(character!=SOH) { #ifdef DEBUG fprintf(stderr,"Not SOH\r\n"); fflush(stderr); #endif if(++errorcount<10)goto nakit; else goto error; } character=getchar(); not_ch=~getchar(); #ifdef DEBUG fprintf(stderr,"[%d] ",character); fflush(stderr); #endif if(character!=not_ch) { #ifdef DEBUG fprintf(stderr,"Blockcounts not ~\r\n"); fflush(stderr); #endif ++errorcount; goto nakit; } if(character!=blocknumber) { #ifdef DEBUG fprintf(stderr,"Wrong blocknumber\r\n"); fflush(stderr); #endif ++errorcount; goto nakit; } checksum=0; for(index=0;index<128;index++) { sector[index]=getchar(); checksum+=sector[index]; } if(checksum!=getchar()) { #ifdef DEBUG fprintf(stderr,"Bad checksum\r\n"); fflush(stderr); #endif errorcount++; goto nakit; } putchar(ACK); fflush(stdout); blocknumber++; if(aflag || qflag) decrlf(fout,sector) ; else fwrite(sector,sizeof sector[0],sizeof sector,fout); if(!errorcount) continue; nakit: putchar(NAK); fflush(stdout); } fclose(fout); putchar(ACK); /* tell the modem on the other end we accepted his EOT */ putchar(ACK); putchar(ACK); fflush(stdout); /* * special hack for stuff going directly to * our laser printer but an interesting idea * in general, no? (eg. this can be a shell) * BZS */ if(qflag) { sprintf(buf,PRCMD,fname) ; system(buf) ; unlink(fname) ; } else if(pflag) { sprintf(buf,"cat %s | %s",fname,pcmd) ; system(buf) ; unlink(fname) ; } fprintf(stderr,"Completed.\r\n"); fflush(stderr); die(0); } timeout() { fprintf(stderr, "Timed out waiting for NAK from remote system\7\7\7\r\n"); die(1); } die(how) register int how; { ttymode.sg_flags=ttyhold; stty(0,&ttymode); exit(how); } /* * typical nuisance is CR/LF pairs in UNIX files * and CR only * this is really the only need for 'ASCII' mode */ decrlf(fp,cp) FILE *fp ; register char *cp ; { register int i ; for(i = 128 ; i > 0 ; i--,cp++) { switch(*cp) { case '\r' : putc('\n',fp) ; ++cp ; if(i && (*cp == '\n')) --i ; break ; /* * Any ctrl chars that should pass thru go here */ case '\t' : /* TAB */ case 014 : /* FF */ case 033 : /* ESC */ case '\b' : /* BS */ case '\n' : /* NL */ putc(*cp,fp) ; break ; default : if(*cp >= ' ') putc(*cp,fp) ; break ; } } } --------------------modem.proto-------------------- MODEM/XMODEM Protocol Explained by Kelly Smith, CP/M-Net "SYSOP" January 8,1980 Edited April 28, 1981 - FJW I thought that it may be of some interest to those of you who use the MODEM/XMODEM file transfer capability to get a little insight as to the communications protocol (i.e. "handshaking method") used by the system. Herein lie the details of a very good (but not perfect) data communications protocol that has become the "de facto" standard for various remote CP/M systems (RCPM's) which are accessible across the country. (Refer to RCPMLSTx.DOC on all RCPM's for access numbers and note that the "x" in that list changes as new system are listed). I also wish to give credit to Ward Christensen (the "original" CBBS) for writing MODEM.ASM (CPMUG Volume 25.11) and Keith Petersen, Bruce Ratoff, Dave Hardy, Rod Hart, Tom "C" (we know who you are Tom!), and others, for enhancements to Ward's original program which we now call XMODEM (external modem). Data is sent in 128-byte sequentially numbered blocks, with a single checksum byte appended to the end of each block. As the receiving computer acquires the incoming data, it performs its own checksum and upon each completion of a block, it compares its checksum result with that of the sending computers. If the receiving computer matches the checksum of the sending computer, it transmits an ACK (ASCII code protocol character for ACKNOWLEDGE (04 Hex, Control-F)) back to the sending computer. The ACK therefore means "all's well on this end, send some more...". Notice in the following example, that the sending computer will transmit an "initial NAK" (ASCII protocol character for NEGATIVE ACKNOWLEDGE (15 Hex, Control-U))...or, "that wasn't quite right, please send again". Due to the asynchronous nature of the initial "hook-up" between the two computers, the receiving computer will "time-out" looking for data, and send the NAK as the "cue" for the sending computer to begin transmission. The sending computer knows that the receiving computer will "time-out", and uses this fact to "get in sync"... The sending computer responds to the "initial NAK" with a SOH (ASCII code protocol character for START OF HEADING (01 Hex, Control-A)), sends the first block number, sends the 1's complement of the block number (VERY important, I will discuss this later...), sends 128 bytes of 8 bit data (that's why we can transfer ".COM" files), and finally a checksum, where the checksum is calculated by summing the SOH, the block number, the block number 1's complement, and the 128 bytes of data. Receiving Computer: ----/NAK/------------------------/ACK/---------------------- 15H 06H Sending Computer: --------/SOH/BLK#/BLK#/DATA/CSUM/---/SOH/BLK#/BLK#/DATA/etc. 01H 001H 0FEH 8bit 8bit 01H 002H 0FDH 8bit .... This process continues, with the next 128 bytes, IF the block was ACK'ed by the receiving computer, and then the next sequential block number and its 1's complement, etc. But what happens if the block is NAK'ed?... Easy, the sending computer just re-sends the previous block. Now the hard part... What if the sending computer transmits a block, the receiving computer gets it and sends an ACK, but the sender does not see it?... The sending computer thinks that it has failed and after 10 seconds re-transmits the block... ARGH!... The receiving computer has "stashed" the data in memory or on disk (data is written to disk after receiving 16 blocks), the receiving computer is now 1 block AHEAD of the transmiting computer! Here comes the operation of the block numbers... The receiver detects that this is the last block (all over again), and transmits back an ACK, throws away the block, and (effectively) "catches up"... clever! What's more, the integrity of the block number is verified by the receiving computer, because it "sums" the SOH (01 Hex) with the block number plus the 1's complement of the block number), and the result MUST BE zero for a proper transfer (e.g. 01+01+FE hex = 00, on the first block). The sequence of events then, looks like this: Receiving Computer: ----/ACK/-----------------------/NAK/----------------------- 06H 15H Sending Computer: CSUM/---/SOH/BLK#/BLK#/DATA/CSUM/---/SOH/BLK#/BLK#/DATA/etc. 8bit 01H 003H 0FCH 8bit 8bit 01H 003H 0FCH 8bit .... Normal completion of data transfers will then conclude with an EOT (ASCII code protocol END OF TRANSMISSION, 04 Hex, Control- D) from the sending computer, and a final ACK from the receiving computer. Unfortunately, if the receiving computer misses the EOT, it will continue to wait for the next block (sending a NAK every 10 seconds, up to 10 times) and eventually "time-out". This is rarely the case however, and although not "bullet-proof", it is a very workable protocol. Receiving Computer: ----/ACK/---/ACK/"Transfer Complete"/A>(or B>) 06H 06H ............................. Sending Computer: CSUM/---/EOT/---/A>(or B>) 8bit 04H ............. In some cases, where the telephone transmission is repeatedly "trashed" (weak signals, multiple noise "hits", etc.), the receiving computer (and operator) will be provided the option to quit. Here, the operator enters "R" or "Q" in response to "Retry or Quit?" (after 10 retries). Receiving Computer: ----/NAK/...NAK's ten times.../"Retry or Quit?"(Q)/A>... 15H Sending Computer: CSUM/---/...Garbled Data....../-----------------------/A>... 8bit A final consideration when using the MODEM program, is a timing related problems when transfer status messages and/or textual data is directed to the screen of a slow (4800 Baud or less) terminal or to a hard copy printer. This problem is readily apparent (multiple NAK's) when using MODEM for the first time, and can usually be "cured" by NOT SPECIFYING the "V" (VIEW) sub-option when sending or receiving files. Users of Lifeboat Associates BSTAM encounter the same problem, but this is easily fixed with the files TQPATCH.ASM and RQPATCH.ASM (transfer quiet/receive quiet) that Keith Petersen (Royal Oak CP/M, "call- back" remote system, (313)-588-7054) wrote to solve the problem of low speed terminal I/O. For users of CBBS's that do not have MODEM.ASM (but DO HAVE a CP/M disk system...ESSENTIAL!), let me suggest that you "data capture" the file MBOOTx.ASM from one of the RCPM's (it's a small 8 kilo-byte file that "fits" in most systems' memory) to get the larger MODEM.ASM (40 kilo-bytes). Check it very carefully for errors using the "data capture" (read ERROR PRONE method here). Then edit and assemble for your modem configuration. If you are tired of buying software where the advertisment is written better than the program, then the RCPM's are just what you have been looking for...and FREE!