frank@sagan.UUCP (Frank Whaley) (10/02/85)
The following shar archive contains rb.c and sb.c -- a pair I found on some bulletin board. No comments other than they seem to work for me from an IBMPC running Qmodem v1.09. Watch for signature. ----------cut here----------cut here----------cut here---------- #!/bin/sh # # This is a shell archive. Remove anything before the "!/bin/sh" # line above and run the rest of this file through the "sh" command # (as "sh yourfile"), or use the "unshar" program. # echo x - rb.c sed 's/^X//' >rb.c <<'*-*-END-of-rb.c-*-*' X#define VERSION "rb 0.05 7-19-82" X X/* X * rb.c By Chuck Forsberg X * X * A program for Unix which can receive X * files from computers running YAM or MODEM. X * If no filename is given, YAM batch mode is assumed. X * X * Supports the CRC option or regular checksum. X * Received pathnames containing no lowercase letters will be changed to lower X * case unless -u option is given. X * X * Unless the -b (binary) option is given, \r is discarded and X * ^Z (which is also discarded) acts as end of file. X * X * Any slashes in the pathname are changed to underscore. X * If the raw pathname ends in .MSG, .TXT or .DOC, any existing file will X * be appended to rather than replaced. Trailing periods are eliminated. X * X * If the raw pathname ends in X * .COM .CMD .DAT .O .REL .PAG .CRL .OBJ .SAV X * or .?Q* (squeezed file), or if the parity bit of the first or second bytes X * of the file is set, X * that file will be received in binary mode. X * X * X * a log of activities is appended to "rblog" X * X * rb is derived from yam2.c and sb.c X * rb should use Unix System III buffered input to reduce CPU time. X * USG UNIX (3.0) ioctl conventions courtesy Jeff Martin X * cc -O -DV7 rb.c -o rb vanilla Unix version 7 X * cc -O -DUSG rb.c -o rb USG (3.0) Unix X * Unix is a trademark of Western Electric Company X */ X X#include <stdio.h> X#include <signal.h> X#include <ctype.h> X#ifdef USG X#include <termio.h> X#include <sys/ioctl.h> X#else X#include <sgtty.h> X#endif X Xchar *substr(); XFILE *fout; XFILE *logfile; X X#define OK 0 X#define FALSE 0 X#define TRUE 1 X#define ERROR (-1) X X/* Ward Christensen / CP/M parameters - Don't change these! */ X#define ENQ 005 X#define CAN ('X'&037) X#define XOFF ('s'&037) X#define XON ('q'&037) X#define SOH 1 X#define STX 2 X#define EOT 4 X#define ACK 6 X#define NAK 025 X#define CPMEOF 032 X#define WANTCRC 0103 /* send C not NAK to get crc not checksum */ X#define TIMEOUT (-2) X#define ERRORMAX 5 X#define RETRYMAX 5 X#define WCEOT (-10) X#define SECSIZ 128 /* cp/m's Magic Number record size */ X#define KSIZE 1024 /* record size with k option */ X Xint Lastrx; Xint Crcflg; Xint Firstsec; Xint Eofseen; /* indicates cpm eof (^Z) has been received */ Xint totblocks; /* total number of blocks received */ Xint errors; X Xint Batch; Xint MakeLCPathname=TRUE; /* make received pathname lower case */ Xint Verbose=FALSE; Xint Rxbinary=FALSE; /* receive all files in bin mode */ Xint Thisbinary; /* current file is to be received in bin mode */ Xint blklen; /* record length of received packets */ Xchar secbuf[KSIZE]; Xchar linbuf[KSIZE]; Xint lleft=0; /* number of characters in linbuf */ Xint llorig; Xchar *cdq; /* pointer for removing chars from linbuf */ X X X#ifdef USG Xstruct termio oldtty, tty; X#else Xstruct sgttyb oldtty, tty; X#endif X Xunsigned updcrc(); X Xmain(argc, argv) Xchar *argv[]; X{ X register char *cp; X register npats=0; X char **patts; X int exitcode; X X while (--argc) { X cp = *++argv; X if(*cp == '-') { X while( *++cp) { X switch(*cp) { X case 'b': X Rxbinary=TRUE; break; X case 'k': X case 'c': X Crcflg=TRUE; break; X case 'u': X MakeLCPathname=FALSE; break; X case 'v': X Verbose=TRUE; break; X default: X usage(); X } X } X } X else if( !npats && argc>0) { X if(argv[0][0]) { X npats=argc; X patts=argv; X } X } X } X#ifdef USG X (void) ioctl(0, TCGETA, &oldtty); X tty = oldtty; X tty.c_lflag &= ~(ECHO | ICANON | ISIG); /* No echo, crlf mapping, INTR X or QUIT chars, no crlf delays, X no erase/kill processing */ X tty.c_iflag = IGNBRK; /* Ignore break, disable parity check, no X stripping, no crnl mapping or ^S^Q */ X tty.c_oflag = 0; /* Transparent output, no delays, no crlf X mapping */ X tty.c_cflag &= ~PARENB; /* Leave baud rate alone, disable parity X generation and checking */ X tty.c_cflag |= CS8; /* Set character size = 8 */ X tty.c_cc[VMIN] = 20; /* Satisfy reads when this many chars in */ X tty.c_cc[VTIME] = 1; /* ... or in this many tenths of seconds */ X (void) ioctl(0, TCSETA, &tty); X#else X ioctl(1, TIOCEXCL, 0); X ioctl(1, TIOCGETP, &oldtty); X tty = oldtty; X tty.sg_flags |= RAW; X tty.sg_flags &= ~ECHO; X ioctl(1, TIOCSETP, &tty); X#endif X logfile=fopen("rblog", "a"); X if(wcreceive(npats, patts)==ERROR) { X exitcode=0200; X sendline(CAN); X sendline(CAN); X sendline(CAN); X sendline(CAN); X } X fclose(logfile); X#ifdef USG X (void) ioctl(0, TCSBRK, 1); /* Wait for output to drain */ X (void) ioctl(0, TCFLSH, 1); /* Flush input queue */ X (void) ioctl(0, TCSETAW, &oldtty); /* Restore original modes */ X (void) ioctl(0, TCXONC,1); /* Restart output */ X#else X ioctl(1, TIOCSETP, &oldtty); X ioctl(1, TIOCNXCL, 0); X#endif X exit(exitcode); X} X Xusage() X{ X fprintf(stderr,"%s by Chuck Forsberg\n", VERSION); X fprintf(stderr,"Usage: rb [-bcuv]\n"); X exit(1); X} X Xwcreceive(argc, argp) Xchar **argp; X{ X if(Batch || argc==0) { X Crcflg=TRUE; fprintf(stderr, "Receiving in batch mode "); X for(;;) { X totblocks=0; X if(wcrxpn(secbuf)== ERROR) X goto fubar; X if(secbuf[0]==0) X return OK; X if(wcrx(secbuf)==ERROR) X goto fubar; X } X } X else X for(; --argc>=0;) { X totblocks=0; X if(wcrx(*argp++)==ERROR) X goto fubar; X } X return OK; Xfubar: X sendline(CAN);sendline(CAN);sendline(CAN); X if(fout) X fclose(fout); X return ERROR; X} X X X/* X * Fetch a pathname from the other end as a C ctyle ASCIZ string. X * Length is indeterminate as long as less than blklen X * a null string represents no more files X */ Xwcrxpn(rpn) Xchar *rpn; /* receive a pathname */ X{ X purgeline(); X Firstsec=TRUE; X sendline(Crcflg?WANTCRC:NAK); X if(wcgetsec(rpn, 100) != 0) { X log( "Pathname fetch failed\n"); X return ERROR; X } X sendline(ACK); X return OK; X} X X/* X * Adapted from CMODEM13.C, written by X * Jack M. Wierda and Roderick W. Hart X */ X Xwcrx(name) Xchar *name; X{ X register int sectnum, sectcurr; X register char sendchar; X register char *filemode, *p; X X for(p=name; *p; ++p) /* change / to _ */ X if( *p == '/') X *p = '_'; X if ( *--p == '.') /* zap trailing period */ X *p = 0; X filemode = "w"; X Thisbinary=Rxbinary; X if (substr(name, ".COM") || substr(name, ".CRL") X || substr(name, ".REL") || substr(name, ".PAG") X || substr(name, ".CMD") X || substr(name, ".SAV") X || substr(name, ".O") X || substr(name, ".DAT") X || substr(name, ".OBJ") X || ((p=substr(name, ".")) && p[2] == 'Q' ) X || ((p=substr(name, ".")) && p[2] == 'q' )) X Thisbinary = TRUE; X if(substr(name, ".TXT") X || substr(name, ".MSG") X || substr(name, ".DOC")) X filemode = "a"; X if(MakeLCPathname && !IsAnyLower(name)) X uncaps(name); X fprintf(logfile, "Receiving %s %s %s\n", X name, Thisbinary?"BIN":"", filemode); X if ((fout=fopen(name, filemode)) == NULL) X return ERROR; X Firstsec=TRUE;sectnum=0; Eofseen=FALSE; X sendchar=Crcflg?WANTCRC:NAK; X X for(;;) { X/* X purgeline(); X*/ X sendline(sendchar); /* send it now, we're ready! */ X sectcurr=wcgetsec(secbuf, (sectnum&0177)?50:130); X if(sectcurr==(sectnum+1 &0377)) { X X if(sectnum==0 && ((secbuf[0]&0200) | (secbuf[1]&0200))) X Thisbinary++; X sectnum++; X if(putsec(secbuf, blklen)==ERROR) X return ERROR; X sendchar=ACK; X } X else if(sectcurr==(sectnum&0377)) { X log( "Received dup Sector\n"); X sendchar=ACK; X } X else if(sectcurr==WCEOT) { X sendline(ACK); X /* don't pad the file any more than it already is */ X if(fclose(fout)==ERROR) X return ERROR; X return OK; X } X else if(sectcurr==ERROR) X return ERROR; X else { X log( "Sync Error\n"); X return ERROR; X } X } X} X X/* X * wcgetsec fetches a Ward Christensen type sector. X * Returns sector number encountered or ERROR if valid sector not received, X * or CAN CAN received X * or WCEOT if eot sector X * time is timeout for first char, set to 4 seconds thereafter X ***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK ************** X * (Caller must do that when he is good and ready to get next sector) X */ X Xwcgetsec(rxbuf, time) Xchar *rxbuf; Xint time; X{ X register checksum, wcj, firstch; X register unsigned oldcrc; X register char *p; X int sectcurr; X X for(Lastrx=errors=0; errors<RETRYMAX; errors++) { X X if ((firstch=readline(time))==STX) { X blklen=KSIZE; goto get2; X } X if (firstch==SOH) { X blklen=SECSIZ; Xget2: X sectcurr=readline(1); X if((sectcurr+readline(1))==255) { X oldcrc=checksum=0; X for(p=rxbuf,wcj=blklen; --wcj>=0; ) { X if((firstch=readline(1)) < 0) X goto bilge; X oldcrc=updcrc(firstch, oldcrc); X checksum += (*p++ = firstch); X } X if((firstch=readline(1)) < 0) X goto bilge; X if(Crcflg) { X oldcrc=updcrc(firstch, oldcrc); X if((firstch=readline(1)) < 0) X goto bilge; X oldcrc=updcrc(firstch, oldcrc); X if(oldcrc) X log("CRC=0%o\n", oldcrc); X else { X Firstsec=FALSE; X return sectcurr; X } X } X else if(((checksum-firstch)&0377)==0) { X Firstsec=FALSE; X return sectcurr; X } X else X log( "Checksum Error\n"); X } X else X log( "Sector number garbled\n"); X } X /* make sure eot really is eot and not just mixmash */ X else if(firstch==EOT && readline(1)==TIMEOUT) X return WCEOT; X else if(firstch==CAN) { X if(Lastrx==CAN) { X log( "Sender CANcelled\n"); X return ERROR; X } else { X Lastrx=CAN; X continue; X } X } X else if(firstch==TIMEOUT) { X if (Firstsec) X goto humbug; Xbilge: X log( "Timeout\n"); X } X else X log( "Got 0%o sector header\n", firstch); X Xhumbug: X Lastrx=0; X while(readline(1)!=TIMEOUT) X ; X if(Firstsec) X sendline(Crcflg?WANTCRC:NAK); X else { X time=40; sendline(NAK); X } X } X /* try to stop the bubble machine. */ X sendline(CAN); sendline(CAN); sendline(CAN); X return ERROR; X} X X/* X * This version of readline is not so well suited for X * reading many characters. X * X * timeout is in tenths of seconds X */ Xreadline(timeout) Xint timeout; X{ X if(--lleft>=0) X return (*cdq++ & 0377); X settimeout(timeout); X if((llorig=lleft=read(1, cdq=linbuf, KSIZE))<1) X return TIMEOUT; X alarm(0); X --lleft; X return (*cdq++ & 0377); X} X X Xalrm() X{ X/* does nothing; actual effect is to give an error return on read */ X} X X/* X * timeout is in tenths of seconds X */ Xsettimeout(timeout) X{ X register int c; X X signal(SIGALRM, alrm); X if((c = timeout/10)<2) X c=2; X alarm(c); X} Xpurgeline() X{ X lleft=0; X lseek(1, 0L, 2); X} X X/* update CRC */ Xunsigned updcrc(c, crc) Xregister c; Xregister unsigned crc; X{ X register count; X X for(count=8; --count>=0;) { X if(crc & 0x8000) { X crc <<= 1; X crc += (((c<<=1) & 0400) != 0); X crc ^= 0x1021; X } X else { X crc <<= 1; X crc += (((c<<=1) & 0400) != 0); X } X } X return crc; X} X X/* make string s lower case */ Xuncaps(s) Xregister char *s; X{ X for( ; *s; ++s) X if(isupper(*s)) X *s = tolower(*s); X} X X X/* X * IsAnyLower returns TRUE if string s has lower case letters. X */ XIsAnyLower(s) Xregister char *s; X{ X for( ; *s; ++s) X if (islower(*s)) X return TRUE; X return FALSE; X} X/* X * putsec writes the n characters of buf to receive file fout. X * If not in binary mode, carriage returns, and all characters X * starting with CPMEOF are discarded. X */ Xputsec(buf, n) Xchar *buf; Xregister n; X{ X register char *p; X X ++totblocks; X if(Thisbinary) X { X for (p=buf; --n>=0; ) X putc( *p++, fout); X } X else { X if(Eofseen) X return OK; X for (p=buf; --n>=0; ++p ) { X if( *p == '\r') X continue; X if (*p == CPMEOF) { X Eofseen=TRUE; return OK; X } X putc(*p ,fout); X } X } X return OK; X} Xsendline(c) X{ X putchar(c); X fflush(stdout); X} X X X/* X * substr(string, token) searches for token in string s X * returns pointer to token within string if found, NULL otherwise X */ Xchar *substr(s, t) Xregister char *s,*t; X{ X register char *ss,*tt; X /* search for first char of token */ X for(ss=s; *s; s++) X if(*s == *t) X /* compare token with substring */ X for(ss=s,tt=t; ;) { X if(*tt == 0) X return s; X if(*ss++ != *tt++) X break; X } X return NULL; X} Xlog(s,p) Xchar *s, *p; X{ X fprintf(logfile, "error %d: ", errors); X fprintf(logfile, s,p); X} *-*-END-of-rb.c-*-* echo x - sb.c sed 's/^X//' >sb.c <<'*-*-END-of-sb.c-*-*' X#define VERSION "sb 1.01 12-21-81" X X/* X * sb.c By Chuck Forsberg X * X * A small program for Unix which can send 1 or more X * files in Batch mode to computers running YAM. (Use "rb" in yam.) X * Supports the CRC option or regular checksum. X * There are no messages of any sort (except incorrect usage). X * sb is derived from yam2.c X * Uses buffered i/o to reduce the CPU time compared to UMODEM. X * USG UNIX (3.0) ioctl conventions courtesy Jeff Martin X * cc -O -DV7 sb.c -o sb vanilla Unix version 7 X * cc -O -DUSG sb.c -o sb USG (3.0) Unix X * Unix is a trademark of Western Electric Company X */ X X#include <stdio.h> X#include <signal.h> X#ifdef USG X#include <termio.h> X#include <sys/ioctl.h> X#else X#include <sgtty.h> X#endif X XFILE *in; X X#define OK 0 X#define FALSE 0 X#define TRUE 1 X#define ERROR (-1) X X/* Ward Christensen / CP/M parameters - Don't change these! */ X#define ENQ 005 X#define CAN ('X'&037) X#define XOFF ('s'&037) X#define XON ('q'&037) X#define SOH 1 X#define EOT 4 X#define ACK 6 X#define NAK 025 X#define CPMEOF 032 X#define WANTCRC 0103 /* send C not NAK to get crc not checksum */ X#define TIMEOUT (-2) X#define ERRORMAX 5 X#define RETRYMAX 5 X#define WCEOT (-10) X#define SECSIZ 128 /* cp/m's Magic Number record size */ X Xchar Lastrx; Xchar Crcflg; Xchar firstsec; X#ifdef USG Xstruct termio oldtty, tty; X#else Xstruct sgttyb oldtty, tty; X#endif X Xunsigned updcrc(); X Xmain(argc, argv) Xchar *argv[]; X{ X register char *cp; X register npats=0; X char **patts; X int exitcode; X char xXbuf[BUFSIZ]; X X if(argc<2) X goto usage; X setbuf(stdout, xXbuf); X while (--argc) { X cp = *++argv; X if(*cp == '-') { X while( *++cp) { X switch(*cp) { X default: X goto usage; X } X } X } X else if( !npats && argc>0) { X if(argv[0][0]) { X npats=argc; X patts=argv; X } X } X } X if(npats < 1) { Xusage: X fprintf(stderr,"%s by Chuck Forsberg\n", VERSION); X fprintf(stderr,"Usage: sb file ...\n"); X exit(1); X } X#ifdef USG X (void) ioctl(0, TCGETA, &oldtty); X tty = oldtty; X tty.c_lflag &= ~(ECHO | ICANON | ISIG); /* No echo, crlf mapping, INTR X or QUIT chars, no crlf delays, X no erase/kill processing */ X tty.c_iflag = IGNBRK; /* Ignore break, disable parity check, no X stripping, no crnl mapping or ^S^Q */ X tty.c_oflag = 0; /* Transparent output, no delays, no crlf X mapping */ X tty.c_cflag &= ~PARENB; /* Leave baud rate alone, disable parity X generation and checking */ X tty.c_cflag |= CS8; /* Set character size = 8 */ X tty.c_cc[VMIN] = 20; /* Satisfy reads when this many chars in */ X tty.c_cc[VTIME] = 1; /* ... or in this many tenths of seconds */ X (void) ioctl(0, TCSETA, &tty); X#else X ioctl(1, TIOCEXCL, 0); X ioctl(1, TIOCGETP, &oldtty); X tty = oldtty; X tty.sg_flags |= RAW; X tty.sg_flags &= ~ECHO; X ioctl(1, TIOCSETP, &tty); X#endif X if(wcsend(npats, patts)==ERROR) { X exitcode=128; X sendline(CAN); X sendline(CAN); X sendline(CAN); X sendline(CAN); X } X fflush(stdout); X#ifdef USG X (void) ioctl(0, TCSBRK, 1); /* Wait for output to drain */ X (void) ioctl(0, TCFLSH, 1); /* Flush input queue */ X (void) ioctl(0, TCSETAW, &oldtty); /* Restore original modes */ X (void) ioctl(0, TCXONC,1); /* Restart output */ X#else X ioctl(1, TIOCSETP, &oldtty); X ioctl(1, TIOCNXCL, 0); X#endif X exit(exitcode); X} X X X X Xwcsend(argc, argp) Xchar *argp[]; X{ X register n; X X Crcflg=FALSE; X firstsec=TRUE; X for(n=0; n<argc; ++n) X if(wcs(argp[n])==ERROR) X goto fubar; X if(wctxpn("")==ERROR) X goto fubar; X return OK; Xfubar: X fclose(in); X sendline(CAN);sendline(CAN);sendline(CAN); X return ERROR; X} X Xwcs(name) Xchar *name; X{ X if((in=fopen(name, "r"))==NULL) X return ERROR; X if(wctxpn(name)!= ERROR) X return wctx(); X else { X return ERROR; X } X} X X Xwctxpn(name) Xchar *name; X{ X register firstch; X register char *p, *q; X char lname[SECSIZ]; X X if((firstch=readline(400))==TIMEOUT) X return ERROR; X if(firstch==WANTCRC) X Crcflg=TRUE; X for(p=name, q=lname ; *p; ) X if((*q++ = *p++) == '/') X q = lname; X while(q < lname + SECSIZ) X *q++ = 0; X if(wcputsec(lname, 0)==ERROR) X return ERROR; X return OK; X} X Xwctx() X{ X int sectnum, attempts, firstch; X char txbuf[SECSIZ]; X X firstsec=TRUE; X X while((firstch=readline(400))!=NAK && firstch != WANTCRC X && firstch!=TIMEOUT && firstch!=CAN) X ; X if(firstch==CAN) X return ERROR; X if(firstch==WANTCRC) X Crcflg=TRUE; X sectnum=1; X while(filbuf(txbuf, SECSIZ)) { X if(wcputsec(txbuf, sectnum)==ERROR) { X return ERROR; X } else X sectnum++; X } X fclose(in); X attempts=0; X do { X sendline(EOT); X purgeline(); X attempts++; X } X while((firstch=(readline(100)) != ACK) && attempts < RETRYMAX); X if(attempts == RETRYMAX) X return ERROR; X else X return OK; X} X Xwcputsec(txbuf, sectnum) Xchar *txbuf; X{ X register checksum, wcj; X register char *cp; X unsigned oldcrc; X int firstch; X int attempts; X X firstch=0; /* part of logic to detect CAN CAN */ X X for(attempts=0; attempts <= RETRYMAX; attempts++) { X Lastrx= firstch; X sendline(SOH); X sendline(sectnum); X sendline(-sectnum-1); X oldcrc=checksum=0; X for(wcj=SECSIZ,cp=txbuf; --wcj>=0; ) { X sendline(*cp); X oldcrc=updcrc(*cp, oldcrc); X checksum += *cp++; X } X if(Crcflg) { X oldcrc=updcrc(0,updcrc(0,oldcrc)); X sendline(oldcrc>>8);sendline(oldcrc); X } X else X sendline(checksum); X purgeline(); X X firstch=readline(100); X if(firstch==CAN && Lastrx==CAN) X return ERROR; X X else if(firstch==ACK) { X firstsec=FALSE; X return OK; X } X else if(firstch==TIMEOUT) X ; X else { X if(firstsec && firstch==WANTCRC) X Crcflg=TRUE; X for(;;) { X Lastrx=firstch; X X if((firstch=readline(1))==TIMEOUT) X break; X if(firstch==CAN && Lastrx==CAN) X return ERROR; X } X } X } X return ERROR; X X} X X X/* fill buf with count chars padding with ^Z for CPM */ Xfilbuf(buf, count) Xchar *buf; X{ X register c, m; X m=count; X while((c=getc(in))!=EOF) { X *buf++ =c; X if(--m == 0) X break; X } X if(m==count) X return 0; X else X while(--m>=0) X *buf++ = CPMEOF; X return count; X} X Xsendline(c) X{ X putchar(c); X} X Xalrm() X{ X/* does nothing; actual effect is to give an error return on read */ X} X X X/* X * This version of readline is not so well suited for X * reading many characters. Fortunately, it doesn't X * have to. X * timeout is in tenths of seconds X */ Xreadline(timeout) X{ X char byt; X register int c; X fflush(stdout); X signal(SIGALRM, alrm); X c = timeout/10; X if(c==0) X ++c; X alarm(c); X if(read(1, &byt, 1)<1) X return TIMEOUT; X alarm(0); X X return byt&0377; X} X Xpurgeline() X{ X lseek(1, 0L, 2); X} X X/* update CRC */ Xunsigned updcrc(c, crc) Xregister c; Xregister unsigned crc; X{ X register count; X X for(count=8; --count>=0;) { X if(crc & 0x8000) { X crc <<= 1; X crc += (((c<<=1) & 0400) != 0); X crc ^= 0x1021; X } X else { X crc <<= 1; X crc += (((c<<=1) & 0400) != 0); X } X } X return crc; X} *-*-END-of-sb.c-*-* exit -- frank ... Frank Whaley, MicroPro Product Development {dual,hplabs,glacier,lll-crg}!well!micropro!sagan!frank "The heights by great men reached and kept, were not attained by sudden flight. But they, while their companions slept, were toiling upward in the night." -Longfellow