rsalz@uunet.uu.net (Rich Salz) (06/15/88)
Submitted-by: Steve Grandi <grandi@noao.arizona.edu> Posting-number: Volume 15, Issue 73 Archive-name: xmodem3.6/part04 : This is a shar archive. Extract with sh, not csh. echo x - batch.c sed -e 's/^X//' > batch.c << '!Funky!Stuff!' X/* X * Various routines for batch transfer X */ X X#include "xmodem.h" X X/* make sure filename sent or received in YMODEM batch is canonical. */ X X/* Incoming: Turn Unix '/' into CP/M ':' and translate to all lower case. X * Remove trailing dot. X */ X Xunixify (name) Xchar *name; X { X char *ptr; X X /* change '/' to ':' and convert to lower case */ X for (ptr=name; *ptr; ++ptr) X { X if (*ptr == '/') X *ptr = ':'; X if (isupper (*ptr)) X *ptr |= 040; X } X X /* remove trailing dot if present */ X ptr--; X if (*ptr == '.') X *ptr = '\0'; X } X X/* make sure filename sent or received in YMODEM batch is canonical. */ X X/* Outgoing: Turn ':' into '/' (for symmetry!) and turn into all lower case. X * Remove everything before last '/'. Use "filename" to hold final name. X */ X Xchar * Xcpmify (name) Xchar *name; X { X char *ptr, *slash; X char *strcpy(); X X /* find last '/' and copy rest of name */ X X slash = name; X for (ptr=name; *ptr; ++ptr) X if (*ptr == '/') X slash = ptr + 1; X strcpy (filename, slash); X X /* change ':' to '/' and covert to all lower case */ X X for (ptr=filename; *ptr; ++ptr) X { X if (*ptr == ':') X *ptr = '/'; X if (isupper (*ptr)) X *ptr |= 040; X } X return (filename); X } X X X/* convert a CP/M file name received in a MODEM7 batch transfer X * into a unix file name mapping '/' into ':', converting to all X * upper case and adding dot in proper place. X * Use "filename" to hold name. X * Code stolen from D. Thompson's (IRTF) xmodem.c X */ X Xchar * Xcpm_unix (string) Xunsigned char *string; X{ X register int i; X unsigned char *iptr, temp; X register char *optr; X X if (*string == '\0') X error("Null file name in MODEM7 batch receive", TRUE); X X for (iptr=string; (temp = *iptr) ; ) { X temp &= 0177; /* strips bit 7 */ X if (isupper(temp)) X temp |= 040; /* set bit 5 for lower case */ X if (temp == '/') X temp=':'; /* map / into : */ X *iptr++ = temp; X } X X /* put in main part of name */ X iptr=string; X optr=filename; X for (i=0; i<8; i++) { X if (*iptr != ' ') X *optr++ = *iptr++; X } X X /* add dot if necessary */ X if (string[8] != ' ' || string[9] != ' ' || string[10] != ' ') X *optr++ = '.'; X X /* put in extension */ X iptr = &string[8]; X for (i=0; i<3; i++) { X if (*iptr != ' ') X *optr++ = *iptr++; X } X X *optr++ = '\000'; X return (filename); X} X X/* Send 11 character CP/M filename for MODEM7 batch transmission X * Returns -1 for a protocol error; 0 if successful X * NOTE: we tromp a little on the argument string! X * code stolen from D. Thompson's (IRTF) xmodem.c X */ X Xsend_name(name) Xchar *name; X{ X register int cksum; X register char *ptr; X X xmdebug("send_name"); X X /* append cp/m EOF */ X name[NAMSIZ] = CTRLZ; X name[NAMSIZ+1] = '\000'; X X /* create checksum */ X ptr = name; X cksum = 0; X while (*ptr) X cksum += *ptr++; X cksum &= 0x00FF; X X /* send filename */ X X sendbyte(ACK); X ptr = name; X sendbyte(*ptr++); X X while (*ptr) { X X switch (readbyte(15)) { X X case ACK: break; X X case TIMEOUT: { X logit("Timeout while sending MODEM7 filename\n"); X sendbyte(BAD_NAME); X return (-1); X } X X default: { X logit("Error while sending MODEM7 filename\n"); X sendbyte(BAD_NAME); X return (-1); X } X } X X sendbyte (*ptr++); X } X X /* Check checksum returned by other side against my value */ X if (readbyte(16) != cksum) { X logit("Bad checksum while sending MODEM7 filename\n"); X sendbyte(BAD_NAME); X return (-1); X } X X sendbyte(ACK); X return (0); X} X X/* Convert Unix filename to 11 character CP/M file name (8 char name, X * 3 char extension, dot in between is not included). X * map ':' into '/'; Use filename to hold name. X * code stolen from D. Thompson's (IRTF) xmodem.c X */ X Xchar * Xunix_cpm(string) Xchar *string; X{ X register char *iptr, *optr, temp; X int i; X X char *rindex(); X char *strcpy(); X X /* blank 11 character name */ X (void) strcpy (filename," "); X X /* strip off any path name */ X if ((iptr = rindex(string,'/'))) X iptr++; X else X iptr=string; X X /* skip leading '.'s */ X while (*iptr == '.') X iptr++; X X /* copy main part of name */ X optr = filename; X i = 8; X while ((i--) && (*iptr) && (*iptr != '.')) X *optr++ = *iptr++; X X /* advance to unix extension, or end of unix name */ X while ((*iptr != '.') && (*iptr)) X iptr++; X X /* skip over the '.' */ X while (*iptr == '.') X iptr++; X X /* copy extension */ X optr = &filename[8]; X i=3; X while ((i--) && (*iptr) && (*iptr != '.')) X *optr++ = *iptr++; X X filename[NAMSIZ] = '\000'; X X /* Fuss with name */ X for (iptr=filename; (temp = *iptr) ;) { X temp &= 0177; /* strip bit 7 (parity bit) */ X if (islower(temp)) X temp &= ~040; /* make upper case */ X if (temp == ':') X temp ='/'; /* map ':' into '/' */ X *iptr++ = temp; X } X X if (DEBUG) X fprintf (LOGFP, "DEBUG: File %s sent as %s\n", string, filename); X X return(filename); X} !Funky!Stuff! echo x - send.c sed -e 's/^X//' > send.c << '!Funky!Stuff!' X/** send a file **/ X X/* X * Operation of this routine depends on on MDM7BAT and YMDMBAT flags. X * X * If "name" is NULL; close out the BATCH send. X */ X X#include "xmodem.h" X Xsfile(name) Xchar *name; X { X X char *sectdisp(); X time_t time(); X char *strcpy(); X char *unix_cpm(); X char *cpmify(); X long countnl(); X X extern unsigned short crctab[1<<B]; /* CRC-16 constant values, see getput.c */ X X register int bufctr, /* array index for data buffer */ X sectnum; /* packet number for packet header */ X X register unsigned short checksum; /* checksum/crc */ X X char blockbuf[BBUFSIZ+6]; /* holds packet as it is constructed */ X X struct stat filestatbuf; /* file status info */ X X int fd, /* file descriptor for file being transmitted */ X attempts, /* number of attempts made to transmit a packet */ X nlflag, /* flag that we have to send a LF in next packet */ X sendfin, /* flag that we are sending the last packet */ X closeout, /* flag that we are closing out batch send */ X startup, /* flag that we are starting batch send */ X tmode, /* TRUE for text mode */ X amode, /* TRUE for apple mode */ X filepack, /* TRUE when sending first packet */ X buf1024, /* TRUE when sending 1K packets */ X bbufcnt, /* array index for packet */ X firstchar, /* first character in protocol transaction */ X bufsize, /* packet size (128 or 1024) */ X sendresp; /* response char to sent block received from remote*/ X long sentsect; /* count of 128 byte sectors actually sent */ X long expsect; /* count of 128 byte sectors expected to be sent */ X time_t start; /* starting time of transfer */ X char c; X X nbchr = 0; /* clear buffered read char count */ X X CRCMODE = FALSE; /* Receiver determines use of crc or checksum */ X X buf1024 = LONGPACK; /* set packet size flag to command line switch */ X X closeout = FALSE; startup = TRUE; filepack = FALSE; /* indicate state of batch transfer */ X X tmode = (XMITTYPE == 't') ? TRUE : FALSE; /* set text mode */ X amode = (XMITTYPE == 'a') ? TRUE : FALSE; /* set apple mode */ X X /* Check on NULL file name */ X if (strcmp(name,"") == 0) X { X if (BATCH) X closeout = TRUE; X else X { X sendbyte(CAN); sendbyte(CAN); sendbyte(CAN); X error("NULL file name in send", TRUE); X } X } X X if (!closeout) /* Are we closing down batch? */ X { /* no; let's send a file */ X logit("----\nXMODEM Send Function\n"); X X if ((fd = open(name, 0)) < 0) X { X sendbyte(CAN); sendbyte(CAN); sendbyte(CAN); X error("Can't open file for send", TRUE); X } X X stat(name, &filestatbuf); /* get file status bytes */ X if (tmode) /* count up NLs */ X filestatbuf.st_size += countnl(fd); X expsect = (filestatbuf.st_size/128) + 1; X X if (LOGFLAG) X { X fprintf(LOGFP, "File Name: %s\n", name); X fprintf(LOGFP,"File Size %ldK, %ld Records, %ld Bytes\n", X (filestatbuf.st_size/1024)+1, expsect, filestatbuf.st_size); X projtime(expsect, LOGFP); X } X } X else X { X logit("----\nXMODEM Send Function\n"); X logit("Closing down Batch Transmission\n"); X } X X X bufsize = buf1024 ? 1024 : 128; /* set sector size */ X if (buf1024 && !closeout) X logit("1K packet mode chosen\n"); X X sendfin = nlflag = FALSE; X attempts = 0; X X /* wait for and read startup character */ Xrestart: X do X { X while (((firstchar=readbyte(1)) != NAK) && (firstchar != CRCCHR) && (firstchar != CAN)) X if (++attempts > NAKMAX) X { X if (MDM7BAT && startup) X { X sendbyte(ACK); sendbyte(EOT); X } X error("Remote System Not Responding", TRUE); X } X X if ((firstchar & 0x7f) == CAN) X if (readbyte(3) == CAN) X error("Send cancelled at user's request",TRUE); X X if (firstchar == CRCCHR) X { X CRCMODE = TRUE; X if (!closeout) X logit("CRC mode requested\n"); X if (readbyte(1) == KCHR) X { X buf1024 = TRUE; X logit("Receiver invoked 1K packet mode\n"); X } X } X } X while (firstchar != NAK && firstchar != CRCCHR); X X if (MDM7BAT && closeout) /* close out MODEM7 batch */ X { X sendbyte(ACK); sendbyte (EOT); X flushin(); readbyte(2); /* flush junk */ X return; X } X X if (MDM7BAT && startup) /* send MODEM7 file name */ X { X if (send_name(unix_cpm(name)) == -1) X { X attempts = 0; X goto restart; X } X startup = FALSE; X attempts = 0; X goto restart; X } X X sectnum = 1; X X if (YMDMBAT) /* Fudge for YMODEM transfer (to send name packet) */ X { X sectnum = 0; X bufsize = 128; X filepack = TRUE; X } X X attempts = sentsect = 0; X start = time((time_t *) 0); X X do /* outer packet building/sending loop; loop till whole file is sent */ X { X X if (closeout && YMDMBAT && sectnum == 1) /* close out YMODEM */ X return; X X if (YMDMBAT && sectnum == 1) /* get set to send YMODEM data packets */ X { X bufsize = buf1024 ? 1024 : 128; X X do /* establish handshaking again */ X { X while (((firstchar=readbyte(2)) != CRCCHR) && (firstchar != NAK) && (firstchar != CAN)) X if (++attempts > ERRORMAX) X error("YMODEM protocol botch, C expected", TRUE); X if ((firstchar&0x7f) == CAN) X if (readbyte(3) == CAN) X error("Send cancelled at User's request", TRUE); X } X while ((firstchar != CRCCHR) && (firstchar != NAK)); X X attempts = 0; X } X X if ((bufsize == 1024) && (attempts > KSWMAX)) X { X logit("Reducing packet size to 128 due to excessive errors\n"); X bufsize = 128; X } X X if ((bufsize == 1024) && ((expsect - sentsect) < 8)) X { X logit("Reducing packet size to 128 for tail end of file\n"); X bufsize = 128; X } X X if (sectnum > 0) /* data packet */ X { X for (bufctr=0; bufctr < bufsize;) X { X if (nlflag) X { X buff[bufctr++] = LF; /* leftover newline */ X nlflag = FALSE; X } X if (getbyte(fd, &c) == EOF) X { X sendfin = TRUE; /* this is the last sector */ X if (!bufctr) /* if EOF on sector boundary */ X break; /* avoid sending extra sector */ X buff[bufctr++] = CTRLZ; /* pad with Ctrl-Z for CP/M EOF (even do for binary files) */ X continue; X } X X if (tmode && c == LF) /* text mode & Unix newline? */ X { X buff[bufctr++] = CR; /* insert carriage return */ X if (bufctr < bufsize) X buff[bufctr++] = LF; /* insert LF */ X else X nlflag = TRUE; /* insert on next sector */ X } X else if (amode && c == LF) /* Apple mode & Unix newline? */ X buff[bufctr++] = CR; /* substitute CR */ X else X buff[bufctr++] = c; /* copy the char without change */ X } X X if (!bufctr) /* if EOF on sector boundary */ X break; /* avoid sending empty sector */ X } X X else /* YMODEM filename packet */ X { X for (bufctr=0; bufctr<bufsize; bufctr++) /* zero packet */ X buff[bufctr]=0; X if (!closeout) X { X strcpy((char *)buff, cpmify(name)); X X /* put in file name, length, mode */ X { X register char *p; X p = (char *)buff + strlen(name) + 1; X sprintf(p, "%lu %lo %o", filestatbuf.st_size, X filestatbuf.st_mtime, filestatbuf.st_mode); X if (DEBUG) X fprintf(LOGFP, "DEBUG: YMODEM header information: %s\n", p); X } X buff[bufsize-2] = (expsect & 0xff); /* put in KMD kludge information */ X buff[bufsize-1] = ((expsect >> 8) & 0xff); X } X } X X bbufcnt = 0; /* start building block to be sent */ X blockbuf[bbufcnt++] = (bufsize == 1024) ? STX : SOH; /* start of packet char */ X blockbuf[bbufcnt++] = sectnum; /* current sector # */ X blockbuf[bbufcnt++] = ~sectnum; /* and its complement */ X X checksum = 0; /* initialize checksum */ X for (bufctr=0; bufctr < bufsize; bufctr++) X { X blockbuf[bbufcnt++] = buff[bufctr]; X X if (CRCMODE) X checksum = (checksum<<B) ^ crctab[(checksum>>(W-B)) ^ buff[bufctr]]; X X else X checksum = ((checksum+buff[bufctr]) & 0xff); X } X X if (CRCMODE) /* put in CRC */ X { X checksum &= 0xffff; X blockbuf[bbufcnt++] = ((checksum >> 8) & 0xff); X blockbuf[bbufcnt++] = (checksum & 0xff); X } X else /* put in checksum */ X blockbuf[bbufcnt++] = checksum; X X attempts = 0; X X do /* inner packet loop */ X { X X writebuf(blockbuf, bbufcnt); /* write the block */ X flushin(); /* purge anything in input queue */ X X if (DEBUG) X fprintf (LOGFP, "DEBUG: %d byte Packet %02xh (%02xh) sent, checksum %02xh %02xh\n", X bbufcnt, blockbuf[1]&0xff, blockbuf[2]&0xff, blockbuf[bufsize+3]&0xff, blockbuf[bufsize+4]&0xff); X X attempts++; X sendresp = readbyte(10); /* get response from remote */ X X if (sendresp != ACK) X { X if (sendresp == TIMEOUT) X { X logitarg("Timeout on sector %s\n",sectdisp(sentsect,bufsize,1)); X } X else if (sendresp == NAK) X { X logitarg("NAK on sector %s\n",sectdisp(sentsect,bufsize,1)); X } X else X { X logitarg("Non-ACK on sector %s\n",sectdisp(sentsect,bufsize,1)); X } X } X } X while((sendresp != ACK) && (attempts < ERRORMAX)); /* close of inner loop */ X X sectnum++; /* increment to next sector number */ X if (!filepack) X sentsect += (bufsize == 128) ? 1 : 8; X filepack = FALSE; X } X while (!sendfin && ( attempts < ERRORMAX)); /* end of outer loop */ X X if (attempts >= ERRORMAX) X { X sendbyte(CAN); sendbyte(CAN); sendbyte(CAN); sendbyte(CAN); sendbyte(CAN); X error ("Too many errors in transmission", TRUE); X } X X attempts = 0; X sendbyte(EOT); /* send 1st EOT to close down transfer */ X X while ((readbyte(15) != ACK) && (attempts++ < EOTMAX)) /* wait for ACK of EOT */ X { X if (attempts > 1) X logitarg("EOT not ACKed, try %d\n", attempts); X sendbyte(EOT); X } X X if (attempts >= RETRYMAX) X error("Remote System Not Responding on Completion", TRUE); X X close(fd); X X logit("Send Complete\n"); X prtime(sentsect, time((time_t *) 0) - start); X } !Funky!Stuff! exit -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.