davet@oakhill.UUCP (David Trissel) (10/21/88)
[Sorry for posting here but our mailer refuses to send to comp.sources.mac]
What follows is my version of the MacBinary XMODEM upload utility which was
originally posted as "Mbget". I call it "Macbinget". This version has #ifdefs
for System V compilation and the latest Sun 4.2 BSD (which for some reason
won't compile the original "Mbget.c" any more.)
An important new feature is a -g option for self-correcting modems (or any
error free communications link) which speeds uploading by about 40%. [Your
milage may vary.] Users without error-correcting modems can use the -g
option if they want to gamble speed for upload failure. (Any garbled data
causes the entire file transfer to abort.)
A companion posting is being done for my version of "Macbinput".
-- Dave Trissel Motorola Semiconductor Austin, Texas
ut-sally!cs.utexas.edu!oakhill!davet
**************** Cut Here *************************
/*
* (originally Macget) -- receive file from macintosh using xmodem protocol
* Dave Johnson, Brown University Computer Science
*
* (c) 1984 Brown University
* may be used but not sold without permission
*
*/
/* To compile:
cc -O -o macbinget macbinget.c
(For Sun BSD 4.2) cc -O -DSUNBSD42 -o macbinget macbinget.c
(For System V) cc -O -DSYSV -o macbinget macbinget.c
Latest modification 10/21/88 by Trissel -
1. Fast transfer (-g) option for error-correcting modems or any
error-free uplink. This option assumes all packets will be
received valid and pre-ACKs them which amounts to about a 42% speedup.
Option can be used by anyone but the entire transfer will fail if any
packets are garbled. (In other words: you're gambling that there will
be no communication errors for speed improvement.)
2. General cleanup by removal of unused definitions and headers.
3. Removed ancient Macterminal Beta 0.5X code.
4. Changed name to less cryptic "macbinget" from "mbget".
5. Added #ifdefs to support System V and BSD 4.2 Sun compilation.
Dave Trissel
Motorola Inc.
ut-sally!cs.utexas.edu!oakhill!davet
This code is fundamentally from two earlier programmers:
Jon Hueras
Symantec/THINK Technologies
singer@endor.harvard.edu
who added 2-Byte CRC capability to code from:
Dave Johnson, Brown University Computer Science
ddj%brown@csnet-relay.arpa
Brown University Computer Science
*/
/* if you have System V then define the following: */
/* #define SYSV */
/* if you have Sun BSD 4.2 then define the following: */
/* #define SUNBSD42 */
#ifdef SUNBSD42
/* RAW/ANYP no longer being found on latest Sun systems? (10/88) */
#define RAW 0x20
#define ANYP 0
#endif
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#ifdef SYSV
#include <termio.h>
#else
#include <sgtty.h>
#endif
#ifdef SYSV
#define NO_RENAME
#endif
#ifdef NO_RENAME
#define rename(old, new) link(old, new); unlink(old)
#endif
#define RECORDBYTES 132
#define DATABYTES 128
#define NAMEBYTES 63
#define RETRIES 10
#define SOHTIMO 10
#define LINTIMO 20
#define CHRTIMO 2
#define MAXRECNO 0xff
#define BYTEMASK 0xff
#define TMO -1
#define DUP '\000'
#define SOH '\001'
#define EOT '\004'
#define ACK '\006'
#define NAK '\025'
#define CAN '\030'
#define EEF '\032'
#define ESC '\033'
#define H_NLENOFF 1
#define H_NAMEOFF 2
/* 65 <-> 80 is the FInfo structure */
#define H_TYPEOFF 65
#define H_AUTHOFF 69
#define H_LOCKOFF 81
#define H_DLENOFF 83
#define H_RLENOFF 87
#define H_CTIMOFF 91
#define H_MTIMOFF 95
#define H_OLD_DLENOFF 81
#define H_OLD_RLENOFF 85
#define TEXT 0
#define DATA 1
#define RSRC 2
#define FULL 3
int mode, txtmode, preackopt;
struct macheader {
char m_name[NAMEBYTES+1];
char m_type[4];
char m_author[4];
long m_datalen;
long m_rsrclen;
long m_createtime;
long m_modifytime;
} mh;
struct filenames {
char f_info[256];
char f_data[256];
char f_rsrc[256];
} files;
char tmpname[16];
int lastack, crc;
char buf[DATABYTES];
char usage[] = "usage: \"macbinget [-rdug] [filename]\"\n";
main(ac, av)
char **av;
{
char *name;
mode = FULL;
name = "";
preackopt = 0;
ac--; av++;
while (ac) {
if (av[0][0] == '-') {
switch (av[0][1]) {
case 'g':
preackopt++;
break;
case 'r':
mode = RSRC;
break;
case 'd':
mode = DATA;
break;
case 'u':
mode = TEXT;
break;
default:
fprintf(stderr, usage);
exit(1);
}
}
else {
name = av[0];
}
ac--; av++;
}
setup_tty();
if (get_sync()) {
lastack = 0;
txtmode = 0;
recv_hdr(name);
if (mode == TEXT) txtmode++;
recv_file(files.f_data, mh.m_datalen, 1);
txtmode = 0;
recv_file(files.f_rsrc, mh.m_rsrclen, 0);
}
reset_tty();
}
recv_hdr(name)
char *name;
{
long get4();
int n;
FILE *fp;
char *np;
strcpy(tmpname, "#machdrXXXXXX");
mktemp(tmpname);
recv_file(tmpname, (long)DATABYTES, 1);
fp = fopen(tmpname, "r");
if (fp == NULL) {
perror("temp file");
cleanup(-1);
}
fread(buf, 1, DATABYTES, fp);
fclose(fp);
if (name && *name) {
n = strlen(name);
if (n > NAMEBYTES) n = NAMEBYTES;
strncpy(mh.m_name, name, n);
mh.m_name[n] = '\0';
}
else {
n = buf[H_NLENOFF] & BYTEMASK;
if (n > NAMEBYTES) n = NAMEBYTES;
strncpy(mh.m_name, buf + H_NAMEOFF, n);
mh.m_name[n] = '\0';
}
for (np = mh.m_name; *np; np++)
if (*np == ' ') *np = '_';
if (mode == FULL) {
sprintf(files.f_info, "%s.info", mh.m_name);
rename(tmpname, files.f_info);
tmpname[0] = '\0';
sprintf(files.f_data, "%s.data", mh.m_name);
sprintf(files.f_rsrc, "%s.rsrc", mh.m_name);
}
else {
unlink(tmpname);
tmpname[0] = '\0';
switch (mode) {
case RSRC:
sprintf(files.f_data, "/dev/null");
sprintf(files.f_rsrc, "%s.rsrc", mh.m_name);
break;
case DATA:
sprintf(files.f_data, "%s.data", mh.m_name);
sprintf(files.f_rsrc, "/dev/null");
break;
case TEXT:
sprintf(files.f_data, "%s", mh.m_name);
sprintf(files.f_rsrc, "/dev/null");
break;
}
}
strncpy(mh.m_type, buf + H_TYPEOFF, 4);
strncpy(mh.m_author, buf + H_AUTHOFF, 4);
mh.m_datalen = get4(buf + H_DLENOFF);
mh.m_rsrclen = get4(buf + H_RLENOFF);
mh.m_createtime = get4(buf + H_CTIMOFF);
mh.m_modifytime = get4(buf + H_MTIMOFF);
}
recv_file(fname, bytes, more)
char *fname;
long bytes;
int more;
{
register int status, n;
FILE *outf;
int naks = 0;
outf = fopen(fname, "w");
if (outf == NULL) {
perror(fname);
cleanup(-1);
}
for (;;) {
if (!bytes) {
if (!more)
tputc(ACK);
fclose(outf);
return;
}
if (preackopt)
tputc(ACK);
status = rec_read(buf, DATABYTES);
switch (status) {
case EOT:
if (!preackopt)
tputc(ACK);
fclose(outf);
if (more) {
purge(SOHTIMO);
cleanup(-1);
}
return;
case ACK:
if (!preackopt)
tputc(ACK);
naks = 0;
n = (bytes > DATABYTES) ? DATABYTES : bytes;
bytes -= n;
fwrite(buf, n, 1, outf);
break;
case DUP:
if (!preackopt)
tputc(ACK);
naks = 0;
break;
case NAK:
purge(CHRTIMO);
if (!preackopt)
if (naks++ < RETRIES) {
tputc(NAK);
break;
}
/* fall through */
case CAN:
tputc(CAN);
fclose(outf);
/* unlink fname? */
cleanup(-1);
/* NOTREACHED */
}
}
}
get_sync()
{
int c, i;
for (;;) {
if ((c = tgetc(15)) == TMO)
break;
if (c != ESC)
continue;
if ((c = tgetc(1)) == TMO)
continue;
if (c == 'b')
break;
}
for (i = 0; i < 3; i++) {
tputc('C');
if ((c = tgetc(SOHTIMO)) != SOH)
continue;
tungetc(c);
crc++;
return 1;
}
for (i = 0; i < 3; i++) {
tputc(NAK);
if ((c = tgetc(SOHTIMO)) != SOH)
continue;
tungetc(c);
return 1;
}
tputc(CAN);
return 0;
}
rec_read(buf, recsize)
char buf[];
int recsize;
{
int c, rec, rec_bar, cksum;
long tgetrec();
c = tgetc(SOHTIMO);
switch (c) {
case TMO:
default:
return NAK;
case EOT:
return EOT;
case CAN:
return CAN;
case SOH:
/* read header */
rec = tgetc(CHRTIMO);
if (rec == TMO)
return NAK;
rec_bar = tgetc(CHRTIMO);
if (rec_bar == TMO)
return NAK;
/* check header */
if (rec != MAXRECNO - rec_bar) return NAK;
/* fill buffer */
if ((cksum = tgetrec(buf, recsize, LINTIMO)) == TMO)
return NAK;
/* get checksum */
c = tgetc(CHRTIMO);
if (c == TMO)
return NAK;
if (crc) {
if (c != ((cksum >> 8) & BYTEMASK))
return NAK;
c = tgetc(CHRTIMO);
if (c == TMO)
return NAK;
}
if (c != (cksum & BYTEMASK))
return NAK;
/* check record number */
if (rec == lastack)
return DUP;
if (rec != ((lastack + 1) & MAXRECNO))
return CAN;
else {
lastack = rec;
return ACK;
}
}
/* NOTREACHED */
}
purge(timeout)
int timeout;
{
int c;
do {
c = tgetc(timeout);
} while (c != TMO);
}
static int ttyfd;
static FILE *ttyf;
jmp_buf timobuf;
long tgetrec(buf, count, timeout)
char *buf;
int count, timeout;
{
char *bp;
int i, cksum;
if (setjmp(timobuf))
return TMO;
alarm(timeout);
i = fread(buf, 1, count, ttyf);
alarm(0);
if (i != count)
return TMO;
if (crc)
cksum = calcrc(buf, count);
if (!crc)
cksum = 0;
bp = buf;
for (i = 0; i < count; bp++, i++) {
if (!crc)
cksum += *bp;
if (txtmode && *bp == '\r')
*bp = '\n';
}
return cksum & 0xFFFF;
}
tgetc(timeout)
int timeout;
{
int c;
if (setjmp(timobuf))
return TMO;
alarm(timeout);
c = getc(ttyf);
alarm(0);
if (c == -1) /* probably hung up or logged off */
return EOT;
else
return c & BYTEMASK;
}
tungetc(c)
char c;
{
ungetc(c, ttyf);
}
tputc(c)
char c;
{
write(ttyfd, &c, 1);
}
timedout()
{
signal(SIGALRM, timedout); /* for pre-4.2 systems */
longjmp(timobuf, 1);
}
#ifdef SYSV
static struct termio otty, ntty;
#else
static struct sgttyb otty, ntty;
#endif
/* should turn messages off */
setup_tty()
{
int cleanup();
int timedout();
ttyf = stdin;
ttyfd = fileno(stdout);
#ifdef SYSV
ioctl(ttyfd, TCGETA, &otty); /* get termio info */
#else
ioctl(ttyfd, TIOCGETP, &otty);
#endif
signal(SIGHUP, cleanup);
signal(SIGINT, cleanup);
signal(SIGQUIT, cleanup);
signal(SIGTERM, cleanup);
signal(SIGALRM, timedout);
#ifdef SYSV
ntty.c_iflag = BRKINT; /* only interrupt on break */
ntty.c_oflag = 0; /* no output processing */
ntty.c_cflag |= CS8; /* 8 bit characters */
ntty.c_lflag = 0; /* no echoing */
ntty.c_cc[VEOF] = 1; /* "MIN" minimum chars before input */
ntty.c_cc[VEOL] = 1; /* "TIME" maximum .1 secs before feed */
ioctl(ttyfd, TCSETAF, &ntty); /* set mode and flush input */
#else
ntty = otty;
ntty.sg_flags = RAW|ANYP;
ioctl(ttyfd, TIOCSETP, &ntty);
#endif
}
reset_tty()
{
#ifdef SYSV
ioctl(ttyfd, TCSETAF, &otty); /* reset after output drains */
#else
sleep(5); /* should wait for output to drain */
ioctl(ttyfd, TIOCSETP, &otty);
#endif
}
cleanup(sig)
int sig;
{
if (tmpname[0] != '\0')
unlink(tmpname);
reset_tty();
exit(sig);
}
long
get4(bp)
char *bp;
{
register int i;
long value = 0;
for (i = 0; i < 4; i++) {
value <<= 8;
value |= (*bp & BYTEMASK);
bp++;
}
return value;
}
int calcrc(ptr, count)
char *ptr;
int count;
{
int crc, i;
crc = 0;
while (--count >= 0) {
crc ^= ((int) *ptr++) << 8;
for (i = 0; i < 8; ++i)
if (crc & 0x8000)
crc = crc << 1 ^ 0x1021;
else
crc <<= 1;
}
return (crc & 0xFFFF);
}