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 Macbinput which is a MacBinary XMODEM download program. This is an alteration of "Mbput.c" which was posted on the net some time ago. The only difference is a name change to "Macbinput" and ifdefs for System V and Sun 4.2 BSD compilation (and some cleanup.) A companion posting is being done for my version of "Macbinget" which supports faster uploading as an option as well as compilation on System V machines. -- Dave Trissel Motorola Austin ut-sally!cs.utexas.edu!oakhill!davet ************** Cut Here ***************** /* * (originally macput) -- send file to Macintosh using MacBinary 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 macbinput macbinput.c (Sun 4.2 BSD) cc -O -DSUNBSD42 -o macbinput macbinput.c (System V) cc -O -DSYSV -o macbinput macbinput.c Latest modifications 10/20/88 by Trissel - 1. General cleanup by removal of unused definitions and headers. 2. Added #ifdefs to support System V and BSD 4.2 Sun compilation. 3. Removed ancient Macterminal Beta 0.5X code. 4. Changed name to less cryptic "macbinput" from "mbput". Dave Trissel Motorola Inc. ut-sally!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 ddj%brown@csnet-relay.arpa Brown University Computer Science who did the initial MacTerminal 1.1 transfer protocol. */ /* If you have System V define the following: */ /* #define SYSV */ /* Sun BSD 4.2 systems should define the following: */ /* #define SUNBSD42 */ #include <stdio.h> #include <signal.h> #include <setjmp.h> #ifdef SYSV #include <termio.h> #else #include <sgtty.h> #endif #include <sys/types.h> #include <sys/stat.h> #ifdef SUNBSD42 /* RAW is no longer being found on latest Sun system (??) (Trissel) */ #define RAW 0x20 #endif #define RECORDBYTES 132 #define DATABYTES 128 #define NAMEBYTES 63 #define RETRIES 10 #define ACKTIMO 10 #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; 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; int recno, crc; char buf[DATABYTES]; char usage[] = "usage: \"macbinput [-rdu] [-t type] [-c creator] [-n name] filename\"\n"; main(ac, av) char **av; { int n; char *filename; if (ac == 1) { fprintf(stderr, usage); exit(1); } mode = FULL; ac--; av++; while (ac) { if (av[0][0] == '-') { switch (av[0][1]) { case 'r': mode = RSRC; strncpy(mh.m_type, "????", 4); strncpy(mh.m_author, "????", 4); break; case 'u': mode = TEXT; strncpy(mh.m_type, "TEXT", 4); strncpy(mh.m_author, "EDIT", 4); break; case 'd': mode = DATA; strncpy(mh.m_type, "????", 4); strncpy(mh.m_author, "????", 4); break; case 'n': if (ac > 1) { ac--; av++; n = strlen(av[0]); if (n > NAMEBYTES) n = NAMEBYTES; strncpy(mh.m_name, av[0], n); mh.m_name[n] = '\0'; break; } else goto bad_usage; case 't': if (ac > 1) { ac--; av++; strncpy(mh.m_type, av[0], 4); break; } else goto bad_usage; case 'c': if (ac > 1) { ac--; av++; strncpy(mh.m_author, av[0], 4); break; } else goto bad_usage; default: bad_usage: fprintf(stderr, usage); exit(1); } } else { filename = av[0]; } ac--; av++; } setup_tty(); find_files(filename, mode); if (mode != FULL) forge_info(); if (send_sync()) { recno = 1; txtmode = 0; send_file(files.f_info, 1); if (mode != FULL) unlink(files.f_info); if (mode == TEXT) txtmode++; send_file(files.f_data, 1); txtmode = 0; send_file(files.f_rsrc, 0); } reset_tty(); } find_files(filename, mode) char *filename; { int n, tdiff; struct stat stbuf; sprintf(files.f_data, "%s.data", filename); sprintf(files.f_rsrc, "%s.rsrc", filename); if (mode == FULL) { sprintf(files.f_info, "%s.info", filename); if (stat(files.f_info, &stbuf) != 0) { perror(files.f_info); cleanup(-1); } return; } else { strcpy(files.f_info, "#machdrXXXXXX"); mktemp(files.f_info); } if (mode == RSRC) { strcpy(files.f_data, "/dev/null"); if (stat(files.f_rsrc, &stbuf) != 0) { strcpy(files.f_rsrc, filename); if (stat(files.f_rsrc, &stbuf) != 0) { perror(files.f_rsrc); cleanup(-1); } } mh.m_datalen = 0; mh.m_rsrclen = stbuf.st_size; } else { strcpy(files.f_rsrc, "/dev/null"); if (stat(files.f_data, &stbuf) != 0) { sprintf(files.f_data, "%s.text", filename); if (stat(files.f_data, &stbuf) != 0) { strcpy(files.f_data, filename); if (stat(files.f_data, &stbuf) != 0) { perror(files.f_data); cleanup(-1); } } } mh.m_datalen = stbuf.st_size; mh.m_rsrclen = 0; } if (mh.m_name[0] == '\0') { n = strlen(filename); if (n > NAMEBYTES) n = NAMEBYTES; strncpy(mh.m_name, filename, n); mh.m_name[n] = '\0'; } } forge_info() { int n; char *np; FILE *fp; for (np = mh.m_name; *np; np++) if (*np == '_') *np = ' '; buf[H_NLENOFF] = n = np - mh.m_name; strncpy(buf + H_NAMEOFF, mh.m_name, n); strncpy(buf + H_TYPEOFF, mh.m_type, 4); strncpy(buf + H_AUTHOFF, mh.m_author, 4); put4(buf + H_DLENOFF, mh.m_datalen); put4(buf + H_RLENOFF, mh.m_rsrclen); put4(buf + H_CTIMOFF, mh.m_createtime); put4(buf + H_MTIMOFF, mh.m_modifytime); fp = fopen(files.f_info, "w"); if (fp == NULL) { perror("temp file"); cleanup(-1); } fwrite(buf, 1, DATABYTES, fp); fclose(fp); } send_sync() { int c; tputc(ESC); tputc('b'); for (;;) { if ((c = tgetc(ACKTIMO)) == TMO) { return(0); } if (c == NAK) { return(1); } if (c == 'C') { crc++; return(1); } } } send_file(fname, more) char *fname; int more; { register int status, i, n; FILE *inf; inf = fopen(fname, "r"); if (inf == NULL) { perror(fname); cleanup(-1); } for (;;) { n = fread(buf, 1, DATABYTES, inf); if (n > 0) { for (i = 0; i < RETRIES; i++) { send_rec(buf, DATABYTES); while ((status = tgetc(ACKTIMO)) != ACK && status != NAK && status != CAN); if (status != NAK) break; } if (status != ACK) { if (status != CAN) while ((status = tgetc(ACKTIMO)) != CAN); fclose(inf); cleanup(-1); /* NOTREACHED */ } } if (n < DATABYTES) { if (!more) { tputc(EOT); tgetc(ACKTIMO); } return; } recno++; recno &= MAXRECNO; } } send_rec(buf, recsize) char buf[]; int recsize; { int i, cksum; char *bp; if (txtmode || !crc) { cksum = 0; bp = buf; for (i = 0; i < recsize; i++, bp++) { if (txtmode && *bp == '\n') *bp = '\r'; cksum += *bp; } } if (crc) cksum = calcrc(buf, recsize); tputc(SOH); tputc((char) recno); tputc((char) (MAXRECNO - recno)); tputrec(buf, recsize); if (crc) { tputc((char) (cksum >> 8)); tputc((char) cksum); } else tputc((char) cksum); } static int ttyfd; static FILE *ttyf; static jmp_buf timobuf; 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; } tputrec(buf, count) char *buf; int count; { write(ttyfd, buf, count); } 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); ntty = otty; #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.sg_flags = RAW; ioctl(ttyfd, TIOCSETP, &ntty); #endif } reset_tty() { if (ttyf != NULL) { #ifdef SYSV ioctl(ttyfd, TCSETAF, &otty); /* reset after output drains */ #else sleep (5); /* wait for output to drain */ ioctl(ttyfd, TIOCSETP, &otty); #endif } } cleanup(sig) int sig; { reset_tty(); exit(sig); } put4(bp, value) char *bp; long value; { register int i, c; for (i = 0; i < 4; i++) { c = (value >> 24) & BYTEMASK; value <<= 8; *bp++ = c; } } 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); }