#define VERSION "sb 2.09 9-23-83" #define PUBDIR "/usr/spool/uucppublic" /*% /bin/env - /bin/ncc -O -Dvoid=int -DUSG -n sb.c -o sb * sb.c By Chuck Forsberg * * A small program for Unix which can send 1 or more * files in Batch mode to computers running YAM. (Use "rb" in yam.) * Supports the CRC option or regular checksum. * Exit status is 0 for all transfers completed successfully, * 1 for 1 or more unreadable files or'd with 200 for incomplete file xfer. * * accepts -k option for 1kb record length. * * sb is derived from yam2.c * Uses buffered i/o to reduce the CPU time compared to UMODEM. * USG UNIX (3.0) ioctl conventions courtesy Jeff Martin * cc -O -DV7 sb.c -o sb vanilla Unix version 7 * cc -O -DUSG sb.c -o sb USG (3.0) Unix * Unix is a trademark of Western Electric Company */ #define LOGFILE "/tmp/rblog" #include #include #include #define OK 0 #define FALSE 0 #define TRUE 1 #define ERROR (-1) #define HOWMANY 3 #include "rbsb.c" /* most of the system dependent stuff here */ FILE *in; /* Ward Christensen / CP/M parameters - Don't change these! */ #define ENQ 005 #define CAN ('X'&037) #define XOFF ('s'&037) #define XON ('q'&037) #define SOH 1 #define STX 2 #define EOT 4 #define ACK 6 #define NAK 025 #define CPMEOF 032 #define WANTCRC 0103 /* send C not NAK to get crc not checksum */ #define TIMEOUT (-2) #define ERRORMAX 5 #define RETRYMAX 5 #define WCEOT (-10) #define SECSIZ 128 /* cp/m's Magic Number record size */ #define KSIZE 1024 char Lastrx; char Crcflg; int Wcsmask=0377; int Verbose=0; int Restricted=0; /* restricted; no /.. or ../ in filenames */ int Quiet=0; /* overrides logic that would otherwise set verbose */ int Fullname=0; /* transmit full pathname */ int firstsec; int errcnt=0; /* number of files unreadable */ int blklen=SECSIZ; /* length of transmitted records */ int Totsecs; /* total number of sectors this file */ char txbuf[KSIZE]; int Filcnt=0; /* count of number of files opened */ unsigned updcrc(); char *substr(), *getenv(); /* called by signal interrupt or terminate to clean things up */ bibi(n) { canit(); mode(0); fprintf(stderr, "sb: caught signal %d; exiting\r", n); exit(128+n); } #ifdef REGULUS sendline(c) { static char d[2]; d[0] = c&Wcsmask; write(1, d, 1); } #else #define sendline(c) putchar(c & Wcsmask) #endif main(argc, argv) char *argv[]; { register char *cp; register npats; int agcnt; char **agcv; char **patts; int exitcode; #ifndef REGULUS static char xXbuf[BUFSIZ]; #endif if ((cp=getenv("SHELL")) && substr(cp, "rsh")) Restricted=TRUE; npats=0; if (argc<2) goto usage; #ifndef REGULUS setbuf(stdout, xXbuf); #endif while (--argc) { cp = *++argv; if (*cp == '-') { while ( *++cp) { switch(*cp) { case '7': Wcsmask=0177; break; case 'f': Fullname=TRUE; break; case 'k': blklen=KSIZE; break; case 'q': Quiet=TRUE; Verbose=0; break; case 'v': ++Verbose; break; default: goto usage; } } } else if ( !npats && argc>0) { if (argv[0][0]) { npats=argc; patts=argv; } } } if (npats < 1) { usage: fprintf(stderr,"%s by Chuck Forsberg\n", VERSION); fprintf(stderr,"Usage: sb [-7fkqv] file ...\n"); exit(1); } if (Verbose) { if (freopen(LOGFILE, "a", stderr)==NULL) { printf("Can't open log file %s\n",LOGFILE); exit(0200); } setbuf(stderr, NULL); } if (fromcu() && !Quiet) { if (Verbose == 0) Verbose = 2; } if (Verbose != 1) { fprintf(stderr, "sb: %d file%s requested:\r\n", npats, npats>1?"s":""); for ( agcnt=npats, agcv=patts; --agcnt>=0; ) { fprintf(stderr, "%s ", *agcv++); } } mode(1); if (signal(SIGINT, bibi) == SIG_IGN) { signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN); } else { signal(SIGINT, bibi); signal(SIGKILL, bibi); } if (wcsend(npats, patts)==ERROR) { exitcode=0200; canit(); } fflush(stdout); mode(0); exit((errcnt != 0) | exitcode); } wcsend(argc, argp) char *argp[]; { register n; Crcflg=FALSE; firstsec=TRUE; for (n=0; n"); if ((firstch=readone(800))==TIMEOUT) { logent("Timeout on pathname\n"); return ERROR; } if (firstch==WANTCRC) Crcflg=TRUE; for (p=name, q=txbuf ; *p; ) if ((*q++ = *p++) == '/' && !Fullname) q = txbuf; *q++ = 0; p=q; while (q < (txbuf + KSIZE)) *q++ = 0; #ifndef REGULUS /* This doesn't seem to work on Regulus */ if (*name && fstat(fileno(in), &f)!= -1) sprintf(p, "%lu %lo %o", f.st_size, f.st_mtime, f.st_mode); #endif /* force 1k blocks if name won't fit in 128 byte block */ if (strlen(txbuf) > 127) blklen=KSIZE; if (wcputsec(txbuf, 0, SECSIZ)==ERROR) return ERROR; return OK; } wctx() { register int sectnum, attempts, firstch; firstsec=TRUE; while ((firstch=readone(400))!=NAK && firstch != WANTCRC && firstch!=TIMEOUT && firstch!=CAN) ; if (firstch==CAN) { logent("Receiver CANcelled\n"); return ERROR; } if (firstch==WANTCRC) Crcflg=TRUE; sectnum=1; while (filbuf(txbuf, blklen)) { if (wcputsec(txbuf, sectnum, blklen)==ERROR) { return ERROR; } else sectnum++; } if (Verbose>1) fprintf(stderr, " Closing "); fclose(in); attempts=0; do { sendline(EOT); purgeline(); attempts++; } while ((firstch=(readone(100)) != ACK) && attempts < RETRYMAX); if (attempts == RETRYMAX) { logent("No ACK on EOT\n"); return ERROR; } else return OK; } wcputsec(buf, sectnum, cseclen) char *buf; int sectnum; int cseclen; /* data length of this sector to send */ { register checksum, wcj; register char *cp; unsigned oldcrc; int firstch; int attempts; firstch=0; /* part of logic to detect CAN CAN */ if (Verbose>1) fprintf(stderr, "\rSector %3d %2dk ", Totsecs, Totsecs/8 ); for (attempts=0; attempts <= RETRYMAX; attempts++) { Lastrx= firstch; sendline(cseclen==KSIZE?STX:SOH); sendline(sectnum); sendline(-sectnum -1); oldcrc=checksum=0; for (wcj=cseclen,cp=buf; --wcj>=0; ) { sendline(*cp); oldcrc=updcrc(*cp, oldcrc); checksum += *cp++; } if (Crcflg) { oldcrc=updcrc(0,updcrc(0,oldcrc)); sendline((int)oldcrc>>8); sendline((int)oldcrc); } else sendline(checksum); purgeline(); firstch=readone(100); if (firstch==CAN && Lastrx==CAN) { logent("Receiver CANcelled\n"); return ERROR; } else if (firstch==ACK) { firstsec=FALSE; Totsecs += (cseclen>>7); return OK; } else if (firstch==TIMEOUT) logent("TIMEOUT on sector ACK\n"); else if (firstch==ERROR) logent("Got error burst for sector ACK\n"); else { logent("Got 0%o for Sector ACK\n", firstch); if (firstsec && firstch==WANTCRC) Crcflg=TRUE; for (;;) { Lastrx=firstch; if ((firstch=readone(4))==TIMEOUT) break; if (firstch==CAN && Lastrx==CAN) { if (Verbose) logent("Receiver CANcelled\n"); return ERROR; } } } } if (Verbose) logent("Retry Count Exceeded\n"); return ERROR; } /* fill buf with count chars padding with ^Z for CPM */ filbuf(buf, count) register char *buf; { register c, m; m=count; while ((c=getc(in))!=EOF) { *buf++ =c; if (--m == 0) break; } if (m==count) return 0; else while (--m>=0) *buf++ = CPMEOF; return count; } alrm() { /* does nothing; actual effect is to give an error return on read */ } /* * readone(timeout) reads character(s) from file descriptor 0 * it attempts to read 3 characters. If it gets more than one, * it is an error unless all are CAN * (otherwise, only normal response is ACK CAN or C) * * timeout is in tenths of seconds */ readone(timeout) { register int c; char byt[4]; fflush(stdout); signal(SIGALRM, alrm); c = timeout/10; if (c<2) c=2; if (Verbose>3) logent("Timeout=%d Calling alarm(%d)\n", timeout, c); alarm(c); #ifndef REGULUS if ((c=read(0, byt, 3))<1) return TIMEOUT; #else if ((c=read(0, byt, 1))<1) /* regulus raw read is unique */ return TIMEOUT; #endif alarm(0); if (c==1) return (byt[0]&0377); else while (c) if (byt[--c] != CAN) return ERROR; return CAN; } purgeline() { lseek(0, 0L, 2); } /* update CRC */ unsigned updcrc(c, crc) register c; register unsigned crc; { register count; for (count=8; --count>=0;) { if (crc & 0x8000) { crc <<= 1; crc += (((c<<=1) & 0400) != 0); crc ^= 0x1021; } else { crc <<= 1; crc += (((c<<=1) & 0400) != 0); } } return crc; } /* send 10 CAN's to try to get the other end to shut up */ canit() { register n; for (n=10; --n>=0; ) sendline(CAN); } #ifdef REGULUS /* * copies count bytes from s to d * (No structure assignment in Regulus C compiler) */ movmem(s, d, count) register char *s, *d; register count; { while (--count >= 0) *d++ = *s++; } #endif /*VARARGS1*/ logent(a, b, c) char *a, *b, *c; { if(Verbose) fprintf(stderr, a, b, c); } /* * return 1 iff stdout and stderr are different devices * indicating this program operating with a modem on a * different line */ fromcu() { struct stat a, b; fstat(1, &a); fstat(2, &b); return (a.st_rdev != b.st_rdev); } /* * substr(string, token) searches for token in string s * returns pointer to token within string if found, NULL otherwise */ char * substr(s, t) register char *s,*t; { register char *ss,*tt; /* search for first char of token */ for (ss=s; *s; s++) if (*s == *t) /* compare token with substring */ for (ss=s,tt=t; ;) { if (*tt == 0) return s; if (*ss++ != *tt++) break; } return NULL; }