info-mac@uw-beaver (04/06/85)
From: stew%lhasa@harvard.ARPA $ COPY SYS$INPUT: 00README.TXT $ DECK/DOLLAR=">> End <<" Here is a new VMS version of macget, macput, xbin and other things. These programs were originally written by Dave Johnson at Brown University, and were modified for VMS and the Vax-11 C compiler by Stewart Rubenstein at Harvard University. It may be used and distributed but not sold for profit. The two programs which I have added are DUMPINF and FIXINF. DUMPINF will list the contents of a .INF file stored on your vax. FIXINF creates a new .INF file from an existing one by zeroing the location, flags, lock and all undefined fields. I have found that this occasionally helps in downloading a recalcitrant file. These may both be used with any number of arguments, any of which may include wildcards. The fgen() function in FGEN.C is a general purpose C interface for this purpose. The low-level i/o routines have been sped up considerably. MacGet, in particular, should work much better and faster now. This file should be unpacked by executing it as a command procedure. It will create the files 00README.TXT (this file), DUMPINF.C, FGEN.C, FIXINF.C, GETPUT.C, MACGET.C, MACGET.HLP, MACGETPUT.H, MACPUT.C, MACPUT.HLP, MAKE.COM, RDT.C and TTYIO.MAR. The command procedure MAKE.COM will compile and link everything. Please send comments, bug reports, etc to: Stew Rubenstein rubenstein@harvard.arpa {ihnp4, seismo, ut-sally}!harvard!rubenstein >> End << $ COPY SYS$INPUT: DUMPINF.C $ DECK/DOLLAR=">> End <<" #include stdio #include "macgetput.h" /* MAXFILE is the maximum number of files which can be matched by a wildcard filename. */ #define MAXFILE 200 main(argc, argv) int argc; char **argv; { char buf[DATABYTES]; FILE *input; char *mtchs[MAXFILE]; int i, fcount; while (--argc > 0) { fcount = fgen(*++argv, mtchs, MAXFILE, "*.inf"); for (i = 0; i < fcount; i++) { input = fopen(mtchs[i], "r"); if (input == NULL) perror(mtchs[i]); else { printf("\n%s\n", mtchs[i]); dump_inf(input); } free(mtchs[i]); } } } dump_inf(input) FILE *input; { char buf[DATABYTES]; if (fread(buf, 1, DATABYTES, input) == 0) perror("fread"); if (fclose(input) != 0) perror("fclose"); printf("Name: %.*s\nType: %.4s Creator: %.4s Finder flags: %04.4X\n", (int)(buf[H_NLENOFF]), buf+H_NAMEOFF, buf+H_TYPEOFF, buf+H_AUTHOFF, get2(buf+H_FFLGOFF)); printf("Location: %d, %d Folder: %d Lock: %d\nData len: %d Rsrc len: %d\n", get2(buf+H_LOCOFF), get2(buf+H_LOCOFF+2), get2(buf+H_FOLDOFF), get2(buf+H_LOCKOFF), get4(buf+H_DLENOFF), get4(buf+H_RLENOFF)); } int get2(bp) char *bp; { return ((bp[0] << 8) & BYTEMASK) | (bp[1] & BYTEMASK); } 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; } >> End << $ COPY SYS$INPUT: FGEN.C $ DECK/DOLLAR=">> End <<" /* fgen.c -- wildcard expansion for vms Stew Rubenstein, Harvard Chemical Labs, March, 1985 */ /* This routine takes a pattern and a default string and returns an array of pointers to the filenames which match. The strings are allocated with malloc(). The value of the function is the number of matches found, or -1 if any error is encountered. */ #include <descrip.h> #include <rmsdef.h> int fgen(pat, resarry, maxfiles, dfltname) char *pat, *resarry[]; int maxfiles; char *dfltname; { struct dsc$descriptor_s file_spec, result, deflt; long context; int count, slen, status; char result_string[256], *strchr(); file_spec.dsc$w_length = strlen(pat); file_spec.dsc$b_dtype = DSC$K_DTYPE_T; file_spec.dsc$b_class = DSC$K_CLASS_S; file_spec.dsc$a_pointer = pat; result.dsc$w_length = sizeof result_string; result.dsc$b_dtype = DSC$K_DTYPE_T; result.dsc$b_class = DSC$K_CLASS_S; result.dsc$a_pointer = result_string; deflt.dsc$w_length = strlen(dfltname); deflt.dsc$b_dtype = DSC$K_DTYPE_T; deflt.dsc$b_class = DSC$K_CLASS_S; deflt.dsc$a_pointer = dfltname; count = 0; context = 0; while (count < maxfiles && (status = LIB$FIND_FILE(&file_spec, &result, &context, &deflt)) == RMS$_NORMAL) { slen = strchr(result_string, ' ') - result_string; resarry[count] = malloc(slen + 1); strncpy(resarry[count], result_string, slen); resarry[count][slen] = '\0'; ++count; } #ifdef VMS_V4 lib$find_file_end(&context); /* Only on V4 systems */ #endif if (status == RMS$_FNF) return(0); if (status == RMS$_NMF) return(count); return(-1); } >> End << $ COPY SYS$INPUT: FIXINF.C $ DECK/DOLLAR=">> End <<" #include stdio #include "macgetput.h" /* MAXFILE is the maximum number of files which can be matched by a wildcard filename. */ #define MAXFILE 200 main(argc, argv) int argc; char **argv; { char buf[DATABYTES]; FILE *input, *output; char *mtchs[MAXFILE], *strchr(); int i, fcount; while (--argc > 0) { fcount = fgen(*++argv, mtchs, MAXFILE, "*.inf"); for (i = 0; i < fcount; i++) { input = fopen(mtchs[i], "r"); if (input == NULL) perror(mtchs[i]); else { printf("\n%s\n", mtchs[i]); *strchr(mtchs[i], ';') = '\0'; output = fopen(mtchs[i], "w"); if (output == NULL) perror(mtchs[i]); else dump_inf(input, output); } free(mtchs[i]); } } } dump_inf(input, output) FILE *input, *output; { int i; char buf[DATABYTES], outbuf[DATABYTES]; if (fread(buf, 1, DATABYTES, input) == 0) perror("fread"); printf("Name: %.*s\nType: %.4s Creator: %.4s Finder flags: %04.4X\n", (int)(buf[H_NLENOFF]), buf+H_NAMEOFF, buf+H_TYPEOFF, buf+H_AUTHOFF, get2(buf+H_FFLGOFF)); printf("Location: %d, %d Folder: %d Lock: %d\nData len: %d Rsrc len: %d\n", get2(buf+H_LOCOFF), get2(buf+H_LOCOFF+2), get2(buf+H_FOLDOFF), get2(buf+H_LOCKOFF), get4(buf+H_DLENOFF), get4(buf+H_RLENOFF)); for (i = 0; i < DATABYTES; i++) outbuf[i] = 0; outbuf[H_NLENOFF] = buf[H_NLENOFF]; strncpy(outbuf + H_NAMEOFF, buf + H_NAMEOFF, (int) buf[H_NLENOFF]); * (int *) (outbuf + H_TYPEOFF) = * (int *) (buf + H_TYPEOFF); * (int *) (outbuf + H_AUTHOFF) = * (int *) (buf + H_AUTHOFF); * (int *) (outbuf + H_DLENOFF) = * (int *) (buf + H_DLENOFF); * (int *) (outbuf + H_RLENOFF) = * (int *) (buf + H_RLENOFF); * (int *) (outbuf + H_CTIMOFF) = * (int *) (buf + H_CTIMOFF); * (int *) (outbuf + H_MTIMOFF) = * (int *) (buf + H_MTIMOFF); fwrite(outbuf, 1, DATABYTES, output); if (fclose(input) != 0) perror("fclose(input)"); if (fclose(output) != 0) perror("fclose(output)"); } int get2(bp) char *bp; { return ((bp[0] << 8) & BYTEMASK) | (bp[1] & BYTEMASK); } 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; } >> End << $ COPY SYS$INPUT: GETPUT.C $ DECK/DOLLAR=">> End <<" #include stdio #include signal #include "macgetput.h" extern FILE *dbg_file; extern int txtmode; extern char tmpname[]; tgetrec(buf, count, timeout) char *buf; int count, timeout; { char *bp; int c, i, cksum; cksum = 0; bp = buf; for (i = 0; i < count; i++) { timed_in(&c, &timeout); if (c < 0) { if (dbg_file != NULL) dbg_buf(buf, i, i, "tgetrec: timeout after %d chars rec=%s\n"); return TMO; } cksum += c; if (txtmode && c == '\r') c = '\n'; *(bp++) = c; } if (dbg_file != NULL) dbg_buf(buf, count, cksum, "tgetrec: cksum=%d rec=%s\n"); return cksum; } dbg_buf(buf, count, num, label) char *buf; int count, num; char *label; { char outbuf[512], *outptr; int i, c; outptr = outbuf; for (i = 0; i < count; i++) { c = buf[i] & BYTEMASK; if (c > 127) { *outptr++ = '&'; c &= 127; } if (c < 32 || c == 127) { *outptr++ = '#'; c ^= 64; } if (c == '&' || c == '#') *outptr++ = '#'; *outptr++ = c; } *outptr++ = '\0'; fprintf(dbg_file, label, num, outbuf); } setup_tty() { int cleanup(); raw_mode(); signal(SIGHUP, cleanup); signal(SIGINT, cleanup); signal(SIGQUIT, cleanup); signal(SIGTERM, cleanup); } cleanup(sig) int sig; { if (tmpname[0] != '\0') delete(tmpname); reset_tty(); exit(sig); } tgetc(timeout) int timeout; { int c; timed_in(&c, &timeout); if (c == -1) /* probably hung up or logged off */ { if (dbg_file != NULL) fprintf(dbg_file, "tgetc: timeout\n"); return EOT; } else { if (dbg_file != NULL) fprintf(dbg_file, "tgetc: %d\n", c); return c & BYTEMASK; } } tputrec(buf, count) char *buf; int count; { int i, c; for (i = 0; i < count; i++) { c = buf[i]; out(&c); } flush(); if (dbg_file != NULL) dbg_buf(buf, count, count, "tputrec(%d): %s\n"); } tputc(c) char c; { if (dbg_file != NULL) fprintf(dbg_file, "tputc: %d\n", (int) c); out(&c); flush(); } >> End << $ COPY SYS$INPUT: MACGET.C $ DECK/DOLLAR=">> End <<" #include stdio #include signal #include setjmp #include ctype #include "macgetput.h" struct macheader mh; struct filenames files; int mode, txtmode; int pre_beta; /* -o flag; for compatibility with MacTerminal Version -0.15X */ char tmpname[16]; int lastack; char buf[DATABYTES]; FILE *dbg_file; /* * macget -- receive file from macintosh using xmodem protocol * Dave Johnson, Brown University Computer Science * Stew Rubenstein, Harvard University Chemical Labs * * (c) 1984 Brown University * (c) 1985 President and Fellows of Harvard College * may be used but not sold without permission * * created ddj 5/22/84 * revised ddj 6/29/84 -- added [-rdu] options * revised ddj 7/16/84 -- protocol changes for MacTerminal Beta Version 0.5X * revised ddj 7/31/84 -- pre-4.2 signal bugs fixed in timedout() * revised sdr 2/28/85 -- Converted to VMS Vax-11 C. */ char usage[] = "usage: \"macget [-o] [-rdu] [filename]\"\n"; main(ac, av) char **av; { char *name; mode = FULL; name = ""; dbg_file = NULL; ac--; av++; while (ac) { if (av[0][0] == '-') { switch (av[0][1]) { case 'x': dbg_file = fopen("debug.dat", "w"); break; case 'r': mode = RSRC; break; case 'd': mode = DATA; break; case 'u': mode = TEXT; break; case 'o': pre_beta++; break; default: fprintf(stderr, usage); exit(1); } } else { name = av[0]; } ac--; av++; } setup_tty(); if (send_sync() == ACK) { 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(); } make_name(dest, type, name) /* * Construct a valid VMS file name. */ char *dest, *type, *name; { int i; char *dp, *np; for (dp = dest, np = name; dp - dest < 9 && *np != '\0'; np++) if (isalpha(*np)) *dp++ = *np; *dp++ = '.'; for (np = type, i = 0; i < 3 && *np != '\0'; np++) if (isalpha(*np)) { *dp++ = *np; ++i; } *dp = '\0'; } recv_hdr(name) char *name; { long get4(); int n; FILE *fp; char *np; strcpy(tmpname, TMPNAME); 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) { make_name(files.f_info, "info", mh.m_name); rename(tmpname, files.f_info); tmpname[0] = '\0'; make_name(files.f_data, "data", mh.m_name); make_name(files.f_rsrc, "rsrc", mh.m_name); } else { delete(tmpname); tmpname[0] = '\0'; switch (mode) { case RSRC: strcpy(files.f_data, "_NL:"); make_name(files.f_rsrc, "rsrc", mh.m_name); break; case DATA: make_name(files.f_data, "data", mh.m_name); strcpy(files.f_rsrc, "_NL:"); break; case TEXT: make_name(files.f_data, "text", mh.m_name); strcpy(files.f_rsrc, "_NL:"); break; } } strncpy(mh.m_type, buf + H_TYPEOFF, 4); strncpy(mh.m_author, buf + H_AUTHOFF, 4); if (pre_beta) { mh.m_datalen = get4(buf + H_OLD_DLENOFF); mh.m_rsrclen = get4(buf + H_OLD_RLENOFF); } else { 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; lastack = 0; outf = fopen(fname, "w"); if (outf == NULL) { perror(fname); cleanup(-1); } for (;;) { status = rec_read(buf, DATABYTES); switch (status) { case EOT: if (!pre_beta) tputc(ACK); if (more) tputc(NAK); fclose(outf); return; case ACK: tputc(ACK); naks = 0; n = (bytes > DATABYTES) ? DATABYTES : bytes; bytes -= n; fwrite(buf, n, 1, outf); break; case DUP: tputc(ACK); naks = 0; break; case NAK: purge(CHRTIMO); if (naks++ < RETRIES) { tputc(NAK); break; } /* fall through */ case CAN: tputc(CAN); fclose(outf); /* unlink fname? */ cleanup(-1); /* NOTREACHED */ } } } send_sync() { int c; for (;;) { c = tgetc(60); switch (c) { case ESC: break; case CAN: case EOT: case TMO: return c; default: continue; } c = tgetc(1); if (c != 'a') continue; tputc(ACK); return ACK; } } rec_read(buf, recsize) char buf[]; int recsize; { int c, rec, rec_bar, cksum; 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 */ cksum = tgetrec(buf, recsize, LINTIMO); if (cksum == TMO) return NAK; /* get checksum */ 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 */ } 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; } >> End << $ COPY SYS$INPUT: MACGET.HLP $ DECK/DOLLAR=">> End <<" macget - receive file from macintosh via xmodem/macterminal SYNOPSIS macget [ -rdu ] [ -o ] [file] DESCRIPTION Macget receives a file from a Macintosh running MacTerminal. The File Transfer settings should specify the "XModem" transfer method and a "MacTerminal" remote system. This program is designed for use with the 0.5 Beta and newer ver- sions of MacTerminal, but includes a compatibility option for the older -0.15X Almost-Alpha version. The optional file parameter specifies the name to use when creating the unix files, otherwise the Mac file name is used (with spaces converted to underscores). If none of the -rdu flags are specified, macget receives three files from the Mac: file.inf, file.dat, and file.rsr. This mode is useful for storing Mac files so they can be restored later using macput. The -r flag specifies resource mode. Only file.rsr will be created, from the Mac file's resource fork. The -d flag specifies data mode. Only file.dat will be created, containing the data fork of the Mac file. The -u flag requests unix mode, in which carriage returns are converted into unix newline characters, and the unix file file.text is created. Note -- this mode should also be used for VMS text files. The -o flag specifies "old" (version -0.15X) MacTerminal compatibility mode. You must manually disable XON/XOFF flow control in this version to perform file transfer; this is done automatically in the newer versions. AUTHOR Dave Johnson, Brown 7/25/84 >> End << $ COPY SYS$INPUT: MACGETPUT.H $ DECK/DOLLAR=">> End <<" #define TMPNAME "tmp:macXXXXXX" /* Mac time of 00:00:00 GMT, Jan 1, 1970 */ #define TIMEDIFF 0x7c25b080 #define RECORDBYTES 132 #define DATABYTES 128 #define NAMEBYTES 63 #define RETRIES 10 #define SOHTIMO 10 #define LINTIMO 20 #define CHRTIMO 1 #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_FFLGOFF 73 #define H_LOCOFF 75 #define H_FOLDOFF 79 #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 /* In VMS version 4, we will be able to do something better. */ #ifdef vax11c # define rename(old, new) # define unlink(file) delete(file) #else # ifdef NO_RENAME # define rename(old, new) link(old, new); unlink(old) # endif #endif 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; }; struct filenames { char f_info[256]; char f_data[256]; char f_rsrc[256]; }; >> End << $ COPY SYS$INPUT: MACPUT.C $ DECK/DOLLAR=">> End <<" #include stdio #include signal #include setjmp #include types #include time #include timeb #include "macgetput.h" int mode, txtmode; int pre_beta; /* -o flag; for compatibility with MacTerminal Version -0.15X */ struct macheader mh; struct filenames files; int recno; char buf[DATABYTES]; FILE *dbg_file; /* * macput -- send file to macintosh using xmodem protocol * Dave Johnson, Brown University Computer Science * Stew Rubenstein, Harvard University Chemical Labs * * (c) 1984 Brown University * (c) 1985 President and Fellows of Harvard College * may be used but not sold without permission * * created ddj 6/17/84 * revised ddj 7/16/84 -- protocol changes for MacTerminal Beta Version 0.5X * revised ddj 7/31/84 -- pre-4.2 signal bugs fixed in timedout() * revised ddj 7/31/84 -- fixed timeout problem in initial handshake * revised sdr 2/28/85 -- Converted to VMS Vax-11 C */ char usage[] = "usage: \"macput [-o] [-rdu] [-t type] [-a author] [-n name] filename\"\n"; main(ac, av) char **av; { int n; char *filename; if (ac == 1) { fprintf(stderr, usage); exit(1); } mode = FULL; dbg_file = NULL; ac--; av++; while (ac) { if (av[0][0] == '-') { switch (av[0][1]) { case 'x': dbg_file = fopen("debug.dat", "w"); break; case 'r': mode = RSRC; strncpy(mh.m_type, "APPL", 4); strncpy(mh.m_author, "CCOM", 4); break; case 'u': mode = TEXT; strncpy(mh.m_type, "TEXT", 4); strncpy(mh.m_author, "MACA", 4); break; case 'd': mode = DATA; strncpy(mh.m_type, "TEXT", 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 'a': if (ac > 1) { ac--; av++; strncpy(mh.m_author, av[0], 4); break; } else goto bad_usage; case 'o': pre_beta++; break; 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() == ACK) { txtmode = 0; send_file(files.f_info, 1); if (mode != FULL) delete(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, fd, status; struct tm *tp; struct timeb tbuf; time_t mtime; sprintf(files.f_data, "%s.dat", filename); sprintf(files.f_rsrc, "%s.rsr", filename); if (mode == FULL) { sprintf(files.f_info, "%s.inf", filename); if ((fd = open(files.f_info, 0)) < 0) { perror(files.f_info); cleanup(-1); } close(fd); return; } else { strcpy(files.f_info, TMPNAME); mktemp(files.f_info); } if (mode == RSRC) { strcpy(files.f_data, "_NL:"); if ((fd = open(files.f_rsrc, 0)) < 0) { strcpy(files.f_rsrc, filename); if ((fd = open(files.f_rsrc, 0)) < 0) { perror(files.f_rsrc); cleanup(-1); } } mh.m_datalen = 0; mh.m_rsrclen = lseek(fd, 0, 2); close(fd); status = revdate(files.f_rsrc, (char *) 0, &mtime); } else { strcpy(files.f_rsrc, "_NL:"); if ((fd = open(files.f_data, 0)) < 0) { sprintf(files.f_data, "%s.txt", filename); if ((fd = open(files.f_data, 0)) < 0) { strcpy(files.f_data, filename); if ((fd = open(files.f_data, 0)) < 0) { perror(files.f_data); cleanup(-1); } } } mh.m_datalen = lseek(fd, 0, 2); mh.m_rsrclen = 0; close(fd); status = revdate(files.f_data, (char *) 0, &mtime); } if (!pre_beta && status) { ftime(&tbuf); tp = localtime(&tbuf.time); tdiff = TIMEDIFF - tbuf.timezone * 60; if (tp->tm_isdst) tdiff += 60 * 60; mh.m_createtime = mtime + tdiff; mh.m_modifytime = mtime + tdiff; } 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); if (pre_beta) { put4(buf + H_OLD_DLENOFF, mh.m_datalen); put4(buf + H_OLD_RLENOFF, mh.m_rsrclen); } else { 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, i; for (i = 0; i < 3; i++) { tputc(ESC); tputc('a'); while ((c = tgetc(ACKTIMO)) != TMO) { switch (c) { case CAN: case EOT: case ACK: return c; default: continue; } } fprintf(dbg_file == NULL ? stderr : dbg_file, "starting handshake timeout\r\n"); } fprintf(dbg_file == NULL ? stderr : dbg_file, "giving up\r\n"); return CAN; } send_file(fname, more) char *fname; int more; { register int status, i, n; FILE *inf; if (dbg_file != NULL) fprintf(dbg_file, "Sending file %s\n", fname); inf = fopen(fname, "r"); if (inf == NULL) { perror(fname); cleanup(-1); } recno = 1; for (;;) { n = fread(buf, 1, DATABYTES, inf); if (n > 0) { for (i = 0; i < RETRIES; i++) { send_rec(buf, DATABYTES); status = tgetc(ACKTIMO); if (status != NAK) break; } if (status == NAK || status == CAN) { fclose(inf); cleanup(-1); /* NOTREACHED */ } } if (n < DATABYTES) { tputc(EOT); if (!pre_beta) { status = tgetc(ACKTIMO); } if (more) { status = tgetc(ACKTIMO); } return; } recno++; recno &= MAXRECNO; } } send_rec(buf, recsize) char buf[]; int recsize; { int i, cksum; char *bp; cksum = 0; bp = buf; for (i = 0; i < recsize; i++, bp++) { if (txtmode && *bp == '\n') *bp = '\r'; cksum += *bp; } tputc(SOH); tputc((char)recno); tputc((char)(MAXRECNO - recno)); tputrec(buf, recsize); tputc((char)cksum); } 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; } } >> End << $ COPY SYS$INPUT: MACPUT.HLP $ DECK/DOLLAR=">> End <<" macput - send file to macintosh via modem7/macterminal SYNOPSIS macput file macput [ -rdu ] file [ -t type ] [ -a author ] [ -n name ] DESCRIPTION Macput sends a file to a Macintosh running MacTerminal. The File Transfer settings should specify the "XModem" transfer method and a "MacTerminal" remote system. This program is designed for use with the 0.5 Beta and newer versions of MacTerminal, but includes a compatibility option for the older -0.15X Almost-Alpha version. If none of the -rdu flags are specified, macput sends three files to the mac: file.inf, file.dat, and file.rsr. This is useful for returning files to the mac which were stored using macget. The -r flag specifies resource mode. Either file.rsr or file will be sent to the Mac, along with a forged .inf file and an empty .dat file. The file sent becomes the resource fork of the Mac file. The -d flag specifies data mode. Either file.dat, file.text or file will be sent to the Mac, along with a forged .inf file and an empty .rsr file. The file sent becomes the data fork of the Mac file. The -u flag requests unix mode, which is the same as data mode mode except unix newline characters are converted into carriage returns. This mode should also be used for transferring VMS text files. The -o flag specifies "old" (version -0.15X) MacTerminal compatibility mode. You must manually disable XON/XOFF flow control in this version to perform file transfer; this is done automatically in the newer versions. The remaining options serve to override the default file type, author, and file name to be used on the Mac. The default type and author for resource mode are "APPL" and "CCOM". data mode defaults are "TEXT", "????", and unix mode defaults are "TEXT" and "MACA". BUGS Doesn't work over flow controlled communication lines, or when using rlogin. Doesn't set the bundle bit on resource files, to incorporate any icons into the Desk Top. Use setfile to set the bundle bit. FEATURES Properly initializes the Creation Date. AUTHOR Dave Johnson, Brown 7/25/84 >> End << $ COPY SYS$INPUT: MAKE.COM $ DECK/DOLLAR=">> End <<" $ cc macput, macget, rdt, getput $ macro ttyio $ link macput, rdt, ttyio, getput $ link macget, ttyio, getput >> End << $ COPY SYS$INPUT: RDT.C $ DECK/DOLLAR=">> End <<" #module revdate /* REVDATE(FILE, DFILE, RDT) returns the revision time and date for the specified vms file. FILE is the vms file specification string, DFILE is the default filename string (may be (char *) 0) and RDT is returned with the time in unix format (seconds since 00:00:00, January 1, 1970). The value of the function is 1 if successful, 0 if any errors encountered. (c) Stew Rubenstein, Harvard Chemical Labs, February, 1985 May be used but not sold for profit without written permission from the author. */ #include rms #include types typedef struct { unsigned a : 32; unsigned b : 32; } quad; int revdate(file, dfile, rdt) char *file, *dfile; time_t *rdt; { struct FAB fab; struct RAB rab; struct XABDAT xab; int status; long int hsecs, hnano; fab = cc$rms_fab; rab = cc$rms_rab; xab = cc$rms_xabdat; rab.rab$l_fab = &fab; fab.fab$l_xab = &xab; fab.fab$l_fna = file; fab.fab$b_fns = strlen(file); if (dfile != 0) { fab.fab$l_dna = dfile; fab.fab$b_dns = strlen(dfile); } status = sys$open(&fab); /* vms_to_unix_time(&xab.xab$q_rdt, rdt); */ lib$ediv(&1000000000, &xab.xab$q_rdt, &hsecs, &hnano); hsecs = hsecs - 35067168; /* Number of hundreds of secs from */ /* Nov 17, 1858 to Jan 1, 1970 */ *rdt = hsecs * 100 + hnano / 10000000; if (status == RMS$_NORMAL) status = sys$close(&fab); return status == RMS$_NORMAL; } >> End << $ COPY SYS$INPUT: TTYIO.MAR $ DECK/DOLLAR=">> End <<" .TITLE TTYIO -- Subroutines for simple terminal I/O ; This file supplies seven subroutines for terminal I/O. They are: ; ; IN(character.wl.r) ; Read one character. ; OUT(character.rl.r) ; Write one character (buffered). ; TIMED_IN(character.wl.r, seconds.rl.r) ; Read one character with timeout. If none are available within ; SECONDS seconds, then the value returned is -1. ; PURGE() ; Clean out any characters waiting in the input buffer. ; FLUSH() ; Actually write characters which have been buffered with OUT. ; RAW_MODE() ; Set the terminal to PASSALL mode. ; RESET_TTY() ; Reset the terminal to what it was before calling RAW_MODE. .PSECT $LOCAL, PIC, WRT, NOEXE, CON, LONG, NOSHR DEBUGGING = 1 INBUF_SIZE = 128 OUTBUF_SIZE = 256 TTMODE: .BLKQ 1 OUTPTR: .BLKL 1 INPTR: .BLKL 1 TTIOSB: .BLKL 2 TRMMSK: .BLKL 2 INCNT: .BLKW 1 TTYCHN: .BLKW 1 BUFF: .BLKB 10 INBUF: .BLKB INBUF_SIZE OUTBUF: .BLKB OUTBUF_SIZE .IF NE, DEBUGGING msg1: .ASCIZ /IN: CHAR = %d/<10> MSG2: .ASCIZ /TEST_INPUT: INCNT = %d, CHAR = %d/<10> MSG3: .ASCIZ /TEST_INPUT: SYSBUF = %d, CHAR = %d/<10> MSG4: .ASCIZ /TIMED_IN: CHAR = %d/<10> MSG5: .ASCIZ /OUT: CHAR = %d/<10> MSG6: .ASCIZ /PURGE/<10> MSG7: .ASCIZ /FLUSH/<10> .PSECT DBG_FILE, LONG, WRT, NOEXE, OVR, PIC, GBL, SHR DBG_FILE: .BLKL 1 .ENDC .PSECT $CODE NOWRT, EXE, SHR, CON, PIC .ENTRY IN, ^M<R2> ; SUBROUTINE IN(CHAR) ;++ ; IN reads a single character from the terminal and returns it in CHAR. ;-- BSBB TEST_INPUT TSTL R0 BLSS 1$ MOVL R0, @4(AP) RET 1$: CALLS #0, FLUSH $QIOW_S CHAN=TTYCHN, - FUNC=#IO$_READVBLK!IO$M_NOECHO!IO$M_NOFILTR, - P1=@4(AP), - P2=#1, - P4=#TRMMSK .IF NE,DEBUGGING TSTL DBG_FILE BEQL 80$ MOVZBL @4(AP), -(SP) PUSHAB MSG1 PUSHL DBG_FILE CALLS #3, FPRINTF 80$: .ENDC RET ; The local subroutine, TEST_INPUT, checks to see if any input has ; already arrived, either in the local input buffer, or in the system ; typeahead buffer. It returns the resulting character in R0. If there ; is no character ready, it returns -1. TEST_INPUT: TSTW INCNT ; Anything already read in? BEQL 1$ ; If not, go check system typeahead buffer .IF NE,DEBUGGING TSTL DBG_FILE BEQL 80$ MOVZBL @INPTR, -(SP) CVTWL INCNT, -(SP) PUSHAB MSG2 PUSHL DBG_FILE CALLS #4, FPRINTF 80$: .ENDC MOVL INPTR, R0 ; Get address of this character INCL INPTR ; Point to next DECW INCNT ; Update count of remaining chars MOVZBL (R0)+, R0 ; Return this character RSB 1$: TSTW TTYCHN ; Have we initialized? BNEQ 2$ ; If so, skip GETTRM call BSBW GETTRM ; Get the tty channel, etc. 2$: ; Snarf up anything in the system input buffer $QIOW_S CHAN=TTYCHN, - IOSB=TTIOSB, - FUNC=#IO$_READVBLK!IO$M_NOECHO!IO$M_TIMED, - P1=INBUF, - P2=#INBUF_SIZE, - P3=#0, - P4=#TRMMSK TSTW TTIOSB+2 ; Were there any? BEQL 3$ ; If not, return with flags set MOVAL INBUF+1, INPTR ; Yes, reset pointer to next character SUBW3 #1, TTIOSB+2, INCNT ; Save the count of characters remaining .IF NE,DEBUGGING TSTL DBG_FILE BEQL 81$ MOVZBL INBUF, -(SP) CVTWL TTIOSB+2, -(SP) PUSHAB MSG3 PUSHL DBG_FILE CALLS #4, FPRINTF 81$: .ENDC MOVZBL INBUF, R0 ; Return the first character RSB 3$: MCOML #0, R0 RSB .ENTRY TIMED_IN, ^M<R2> ; SUBROUTINE TIMED_IN(CHAR, TIME) ;++ ; TIMED_IN reads a single character from the grpahics terminal. ; If no such character is available within TIME seconds, -1 is ; returned. ;-- BSBW TEST_INPUT TSTL R0 BLSS 2$ MOVL R0, @4(AP) RET 2$: TSTL @8(AP) BEQL 3$ CALLS #0, FLUSH CLRL @4(AP) $QIOW_S CHAN=TTYCHN, - IOSB=TTIOSB, - FUNC=#IO$_READVBLK!IO$M_NOECHO!IO$M_TIMED, - P1=@4(AP), - P2=#1, - P3=@8(AP), - P4=#TRMMSK CMPW TTIOSB, #SS$_TIMEOUT BNEQ 1$ 3$: MCOML #0, @4(AP) 1$: .IF NE,DEBUGGING TSTL DBG_FILE BEQL 80$ PUSHL @4(AP) PUSHAB MSG4 PUSHL DBG_FILE CALLS #3, FPRINTF 80$: .ENDC RET .ENTRY PURGE, ^M<R2> ; SUBROUTINE PURGE ;++ ; PURGE clears the input buffer of the graphics terminal. ;-- TSTW TTYCHN BNEQ 1$ BSBW GETTRM 1$: CLRW INCNT $QIOW_S CHAN=TTYCHN, - FUNC=#IO$_READVBLK!IO$M_NOECHO!IO$M_TIMED!IO$M_PURGE, - P1=BUFF, - P2=#1, - P3=#0 .IF NE,DEBUGGING TSTL DBG_FILE BEQL 80$ PUSHAB MSG6 PUSHL DBG_FILE CALLS #2, FPRINTF 80$: .ENDC RET .ENTRY OUT,0 ; SUBROUTINE OUT(CHAR) ;++ ; OUT writes a single character, CHAR, to the terminal. ; Actually, the characters are buffered locally, and then sent ; out by a call to FLUSH, IN or GETLN. OUT is used for character ; output in graphics mode. ;-- MOVB @4(AP), @OUTPTR INCL OUTPTR MOVAL OUTBUF+OUTBUF_SIZE, R0 CMPL OUTPTR, R0 BNEQ 1$ CALLS #0, FLUSH 1$: .IF NE,DEBUGGING TSTL DBG_FILE BEQL 80$ MOVZBL @4(AP), -(SP) PUSHAB MSG5 PUSHL DBG_FILE CALLS #3, FPRINTF 80$: .ENDC RET .ENTRY FLUSH,^M<R2> ; SUBROUTINE FLUSH ;++ ; FLUSH writes out the characters accumulated with OUT to the terminal. ;-- MOVAL OUTBUF, R0 SUBL3 R0, OUTPTR, R2 BEQL 1$ TSTW TTYCHN BNEQ 2$ BSBW GETTRM 2$: $QIOW_S CHAN=TTYCHN, - FUNC=#IO$_WRITEVBLK!IO$M_NOFORMAT, - P1=OUTBUF, - P2=R2 MOVAL OUTBUF, OUTPTR 1$: .IF NE,DEBUGGING TSTL DBG_FILE BEQL 80$ PUSHAB MSG7 PUSHL DBG_FILE CALLS #2, FPRINTF 80$: .ENDC RET GETTRM: PUSHL #^A/TT/ PUSHL SP PUSHL #2 MOVL SP, R2 $ASSIGN_S DEVNAM=(R2), CHAN=TTYCHN BLBS R0, 1$ $EXIT_S R0 1$: ADDL #12, SP MOVAL OUTBUF, OUTPTR CALLS #0, PURGE RSB .ENTRY RAW_MODE, ^M<R2> ; SUBROUTINE RAW_MODE ;++ ; Puts the terminal in passall mode. ;-- TSTW TTYCHN BNEQ 1$ BSBW GETTRM 1$: $QIOW_S CHAN=TTYCHN, - FUNC=#IO$_SENSEMODE, - P1=TTMODE BISL3 #TT$M_EIGHTBIT!TT$M_PASSALL, TTMODE+4, -(SP) MOVL TTMODE, -(SP) MOVL SP, R2 $QIOW_S CHAN=TTYCHN, - FUNC=#IO$_SETMODE, - P1=(R2) RET .ENTRY RESET_TTY, 0 ; SUBROUTINE RESET_TTY ;++ ; Resets the terminal to the modes in effect before the call to RAW_MODE. ;-- $QIOW_S CHAN=TTYCHN, - FUNC=#IO$_SETMODE, - P1=TTMODE RET .END >> End << $ COPY SYS$INPUT: XBIN.C $ DECK/DOLLAR=">> End <<" #ifndef lint static char version[] = "xbin.c Version 2.1 03/26/85"; #endif lint #include <stdio.h> #ifdef vax11c #define FNAMELEN 256 #define search_last strrchr #else #include <sys/types.h> #include <sys/stat.h> #include <sys/dir.h> #ifdef MAXNAMLEN /* 4.2 BSD */ #define FNAMELEN MAXNAMLEN #else #define FNAMELEN DIRSIZ #endif #ifdef BSD #include <sys/time.h> #include <sys/timeb.h> #define search_last rindex #else #include <time.h> extern long timezone; #define search_last strrchr #endif #endif extern char *search_last(); /* Mac time of 00:00:00 GMT, Jan 1, 1970 */ #define TIMEDIFF 0x7c25b080 #define DATABYTES 128 #define BYTEMASK 0xff #define BYTEBIT 0x100 #define WORDMASK 0xffff #define WORDBIT 0x10000 #define NAMEBYTES 63 #define H_NLENOFF 1 #define H_NAMEOFF 2 /* 65 <-> 80 is the FInfo structure */ #define H_TYPEOFF 65 #define H_AUTHOFF 69 #define H_FLAGOFF 73 #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 F_BUNDLE 0x2000 #define F_LOCKED 0x8000 struct macheader { char m_name[NAMEBYTES+1]; char m_type[4]; char m_author[4]; short m_flags; 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 pre_beta; /* options */ int listmode; int verbose; int compressed; /* state variables */ int qformat; FILE *ifp; /* * xbin -- unpack BinHex format file into suitable * format for downloading with macput * Dave Johnson, Brown University Computer Science * * VMS support by Stew Rubenstein, Harvard University Chemical Labs * * checksum code by Darin Adler, TMQ Software * * (c) 1984 Brown University * may be used but not sold without permission * * created ddj 12/16/84 * revised ddj 03/10/85 -- version 4.0 compatibility, other minor mods * revised ddj 03/11/85 -- strip LOCKED bit from m_flags * revised ahm 03/12/85 -- System V compatibility * revised dba 03/16/85 -- 4.0 EOF fixed, 4.0 checksum added * revised ddj 03/17/85 -- extend new features to older formats: -l, stdin * revised ddj 03/24/85 -- check for filename truncation, allow multiple files * revised ddj 03/26/85 -- fixed USG botches, many problems w/multiple files * revised sdr 03/29/85 -- vms version under #ifdef vax11c switches */ char usage[] = "usage: \"xbin [-v] [-l] [-o] [-n name] [-] filename\"\n"; main(ac, av) char **av; { char *filename, *macname; filename = ""; macname = ""; ac--; av++; while (ac) { if (av[0][0] == '-') { switch (av[0][1]) { case '\0': filename = "-"; break; case 'v': verbose++; break; case 'l': listmode++; break; case 'o': pre_beta++; break; case 'n': if (ac > 1) { ac--; av++; macname = av[0]; filename = ""; break; } else goto bad_usage; default: goto bad_usage; } } else filename = av[0]; if (filename[0] != '\0') { setup_files(filename, macname); if (listmode) { print_header(); } else { process_forks(); /* now that we know the size of the forks */ forge_info(); } if (ifp != stdin) fclose(ifp); macname = ""; ifp = NULL; /* reset state */ qformat = 0; compressed = 0; } ac--; av++; } if (*filename == '\0') { bad_usage: fprintf(stderr, usage); exit(1); } } static char *extensions[] = { ".hqx", ".hcx", ".hex", "", NULL }; setup_files(filename, macname) char *filename; /* input file name -- extension optional */ char *macname; /* name to use on the mac side of things */ { char namebuf[256], *np; char **ep; int n; #ifndef vax11c struct stat stbuf; #endif long curtime; if (filename[0] == '-') { ifp = stdin; filename = "stdin"; } else { /* find input file and open it */ for (ep = extensions; *ep != NULL; ep++) { sprintf(namebuf, "%s%s", filename, *ep); #ifdef vax11c if ((n = open(namebuf, 0)) >= 0) { close(n); break; } #else if (stat(namebuf, &stbuf) == 0) break; #endif } if (*ep == NULL) { perror(namebuf); exit(-1); } ifp = fopen(namebuf, "r"); if (ifp == NULL) { perror(namebuf); exit(-1); } } if (ifp == stdin) { curtime = time(0); mh.m_createtime = curtime; } else { #ifdef vax11c revdate(namebuf, "", &mh.m_createtime); #else mh.m_createtime = stbuf.st_mtime; #endif } mh.m_modifytime = mh.m_createtime; if (listmode || verbose) { fprintf(stderr, "%s %s%s", listmode ? "\nListing" : "Converting", namebuf, listmode ? ":\n" : " "); } qformat = find_header(); /* eat mailer header &cetera, intuit format */ if (qformat) do_q_header(macname); else do_o_header(macname, filename); /* make sure host file name doesn't get truncated beyond recognition */ n = strlen(mh.m_name); if (n > FNAMELEN - 2) n = FNAMELEN - 2; strncpy(namebuf, mh.m_name, n); namebuf[n] = '\0'; /* get rid of troublesome characters */ for (np = namebuf; *np; np++) if (*np == ' ' || *np == '/') *np = '_'; #ifdef vax11c sprintf(files.f_data, "%s.dat", namebuf); sprintf(files.f_rsrc, "%s.rsr", namebuf); sprintf(files.f_info, "%s.inf", namebuf); if (verbose) fprintf(stderr, "==> %s.{inf,dat,rsr}\n", namebuf); #else sprintf(files.f_data, "%s.data", namebuf); sprintf(files.f_rsrc, "%s.rsrc", namebuf); sprintf(files.f_info, "%s.info", namebuf); if (verbose) fprintf(stderr, "==> %s.{info,data,rsrc}\n", namebuf); #endif } /* print out header information in human-readable format */ print_header() { char *ctime(); printf("macname: %s\n", mh.m_name); printf("filetype: %.4s, ", mh.m_type); printf("author: %.4s, ", mh.m_author); printf("flags: 0x%x\n", mh.m_flags); if (qformat) { printf("data length: %d, ", mh.m_datalen); printf("rsrc length: %d\n", mh.m_rsrclen); } if (!pre_beta) { printf("create time: %s", ctime(&mh.m_createtime)); } } process_forks() { if (qformat) { /* read data and resource forks of .hqx file */ do_q_fork(files.f_data, mh.m_datalen); do_q_fork(files.f_rsrc, mh.m_rsrclen); } else do_o_forks(); } /* write out .info file from information in the mh structure */ forge_info() { static char buf[DATABYTES]; char *np; FILE *fp; int n, tdiff; #ifndef vax11c struct tm *tp; #ifdef BSD struct timeb tbuf; #else long bs; #endif #endif 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); put2(buf + H_FLAGOFF, mh.m_flags & ~F_LOCKED); if (pre_beta) { put4(buf + H_OLD_DLENOFF, mh.m_datalen); put4(buf + H_OLD_RLENOFF, mh.m_rsrclen); } else { put4(buf + H_DLENOFF, mh.m_datalen); put4(buf + H_RLENOFF, mh.m_rsrclen); /* convert unix file time to mac time format */ #ifdef vax11c tdiff = TIMEDIFF; #else #ifdef BSD ftime(&tbuf); tp = localtime(&tbuf.time); tdiff = TIMEDIFF - tbuf.timezone * 60; if (tp->tm_isdst) tdiff += 60 * 60; #else /* I hope this is right! -andy */ time(&bs); tp = localtime(&bs); tdiff = TIMEDIFF - timezone; if (tp->tm_isdst) tdiff += 60 * 60; #endif #endif put4(buf + H_CTIMOFF, mh.m_createtime + tdiff); put4(buf + H_MTIMOFF, mh.m_modifytime + tdiff); } fp = fopen(files.f_info, "w"); if (fp == NULL) { perror("info file"); exit(-1); } fwrite(buf, 1, DATABYTES, fp); fclose(fp); } /* eat characters until header detected, return which format */ find_header() { int c, n, at_bol; char ibuf[BUFSIZ]; /* look for "(This file ...)" line */ while (fgets(ibuf, BUFSIZ, ifp) != NULL) { if (strncmp(ibuf, "(This file", 10) == 0) break; } at_bol = 1; while ((c = getc(ifp)) != EOF) { switch (c) { case '\n': case '\r': at_bol = 1; break; case ':': if (at_bol) /* q format */ return 1; break; case '#': if (at_bol) { /* old format */ ungetc(c, ifp); return 0; } break; default: at_bol = 0; break; } } fprintf(stderr, "unexpected EOF\n"); exit(2); } static unsigned int crc; short get2q(); long get4q(); /* read header of .hqx file */ do_q_header(macname) char *macname; { char namebuf[256]; /* big enough for both att & bsd */ int n; int calc_crc, file_crc; crc = 0; /* compute a crc for the header */ q_init(); /* reset static variables */ n = getq(); /* namelength */ n++; /* must read trailing null also */ getqbuf(namebuf, n); /* read name */ if (macname[0] == '\0') macname = namebuf; n = strlen(macname); if (n > NAMEBYTES) n = NAMEBYTES; strncpy(mh.m_name, macname, n); mh.m_name[n] = '\0'; getqbuf(mh.m_type, 4); getqbuf(mh.m_author, 4); mh.m_flags = get2q(); mh.m_datalen = get4q(); mh.m_rsrclen = get4q(); comp_q_crc(0); comp_q_crc(0); calc_crc = crc; file_crc = get2q(); verify_crc(calc_crc, file_crc); } do_q_fork(fname, len) char *fname; register int len; { FILE *outf; register int c, i; int calc_crc, file_crc; outf = fopen(fname, "w"); if (outf == NULL) { perror(fname); exit(-1); } crc = 0; /* compute a crc for a fork */ if (len) for (i = 0; i < len; i++) { if ((c = getq()) == EOF) { fprintf(stderr, "unexpected EOF\n"); exit(2); } putc(c, outf); } comp_q_crc(0); comp_q_crc(0); calc_crc = crc; file_crc = get2q(); verify_crc(calc_crc, file_crc); fclose(outf); } /* verify_crc(); -- check if crc's check out */ verify_crc(calc_crc, file_crc) unsigned int calc_crc, file_crc; { calc_crc &= WORDMASK; file_crc &= WORDMASK; if (calc_crc != file_crc) { fprintf(stderr, "CRC error\n---------\n"); fprintf(stderr, "CRC in file:\t0x%x\n", file_crc); fprintf(stderr, "calculated CRC:\t0x%x\n", calc_crc); exit(3); } } static int eof; static char obuf[3]; static char *op, *oend; /* initialize static variables for q format input */ q_init() { eof = 0; op = obuf; oend = obuf + sizeof obuf; } /* get2q(); q format -- read 2 bytes from input, return short */ short get2q() { register int c; short value = 0; c = getq(); value = (c & BYTEMASK) << 8; c = getq(); value |= (c & BYTEMASK); return value; } /* get4q(); q format -- read 4 bytes from input, return long */ long get4q() { register int c, i; long value = 0; for (i = 0; i < 4; i++) { c = getq(); value <<= 8; value |= (c & BYTEMASK); } return value; } /* getqbuf(); q format -- read n characters from input into buf */ /* All or nothing -- no partial buffer allowed */ getqbuf(buf, n) register char *buf; register int n; { register int c, i; for (i = 0; i < n; i++) { if ((c = getq()) == EOF) return EOF; *buf++ = c; } return 0; } #define RUNCHAR 0x90 /* q format -- return one byte per call, keeping track of run codes */ getq() { register int c; if ((c = getq_nocrc()) == EOF) return EOF; comp_q_crc(c); return c; } getq_nocrc() { static int rep, lastc; int c; if (rep) { rep--; return lastc; } if ((c = getq_raw()) == EOF) { return EOF; } if (c == RUNCHAR) { if ((rep = getq_raw()) == EOF) return EOF; if (rep == 0) { return RUNCHAR; } else { /* already returned one, about to return another */ rep -= 2; return lastc; } } else { lastc = c; return c; } } /* q format -- return next 8 bits from file without interpreting run codes */ getq_raw() { char ibuf[4]; register char *ip = ibuf, *iend = ibuf + sizeof ibuf; int c; if (op == obuf) { for (ip = ibuf; ip < iend; ip++) { if ((c = get6bits()) == EOF) if (ip <= &ibuf[1]) return EOF; else if (ip == &ibuf[2]) eof = 1; else eof = 2; *ip = c; } obuf[0] = (ibuf[0] << 2 | ibuf[1] >> 4); obuf[1] = (ibuf[1] << 4 | ibuf[2] >> 2); obuf[2] = (ibuf[2] << 6 | ibuf[3]); } if ((eof) & (op >= &obuf[eof])) return EOF; c = *op++; if (op >= oend) op = obuf; return (c & BYTEMASK); } char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr"; /* q format -- decode one byte into 6 bit binary */ get6bits() { register int c; register char *where; while (1) { c = getc(ifp); switch (c) { case '\n': case '\r': continue; case ':': case EOF: return EOF; default: where = tr; while (*where != '\0' && *where != c) where++; if (*where == c) return (where - tr); else { fprintf(stderr, "bad char\n"); return EOF; } } } } #define CRCCONSTANT 0x1021 comp_q_crc(c) register unsigned int c; { register int i; register unsigned int temp = crc; for (i=0; i<8; i++) { c <<= 1; if ((temp <<= 1) & WORDBIT) temp = (temp & WORDMASK) ^ CRCCONSTANT; temp ^= (c >> 8); c &= BYTEMASK; } crc = temp; } /* old format -- process .hex and .hcx files */ do_o_header(macname, filename) char *macname, *filename; { char namebuf[256]; /* big enough for both att & bsd */ char ibuf[BUFSIZ]; int n; /* set up name for output files */ if (macname[0] == '\0') { strcpy(namebuf, filename); /* strip directories */ macname = search_last(namebuf, '/'); if (macname == NULL) macname = namebuf; else macname++; /* strip extension */ n = strlen(macname); if (n > 4) { n -= 4; if (macname[n] == '.' && macname[n+1] == 'h' && macname[n+3] == 'x') macname[n] = '\0'; } } n = strlen(macname); if (n > NAMEBYTES) n = NAMEBYTES; strncpy(mh.m_name, macname, n); mh.m_name[n] = '\0'; /* read "#TYPEAUTH$flag" line */ if (fgets(ibuf, BUFSIZ, ifp) == NULL) { fprintf(stderr, "unexpected EOF\n"); exit(2); } n = strlen(ibuf); if (n >= 7 && ibuf[0] == '#' && ibuf[n-6] == '$') { if (n >= 11) strncpy(mh.m_type, &ibuf[1], 4); if (n >= 15) strncpy(mh.m_author, &ibuf[5], 4); sscanf(&ibuf[n-5], "%4hx", &mh.m_flags); } } do_o_forks() { char ibuf[BUFSIZ]; int forks = 0, found_crc = 0; int calc_crc, file_crc; int n; crc = 0; /* calculate a crc for both forks */ /* create empty files ahead of time */ close(creat(files.f_data, 0666)); close(creat(files.f_rsrc, 0666)); while (!found_crc && fgets(ibuf, BUFSIZ, ifp) != NULL) { if (forks == 0 && strncmp(ibuf, "***COMPRESSED", 13) == 0) { compressed++; continue; } if (strncmp(ibuf, "***DATA", 7) == 0) { mh.m_datalen = make_file(files.f_data, compressed); forks++; continue; } if (strncmp(ibuf, "***RESOURCE", 11) == 0) { mh.m_rsrclen = make_file(files.f_rsrc, compressed); forks++; continue; } if (compressed && strncmp(ibuf, "***CRC:", 7) == 0) { found_crc++; calc_crc = crc; sscanf(&ibuf[7], "%x", &file_crc); break; } if (!compressed && strncmp(ibuf, "***CHECKSUM:", 12) == 0) { found_crc++; calc_crc = crc & BYTEMASK; sscanf(&ibuf[12], "%x", &file_crc); file_crc &= BYTEMASK; break; } } if (found_crc) verify_crc(calc_crc, file_crc); else { fprintf(stderr, "missing CRC\n"); exit(3); } } make_file(fname, compressed) char *fname; int compressed; { register int n; char ibuf[BUFSIZ]; FILE *outf; int nbytes = 0; outf = fopen(fname, "w"); if (outf == NULL) { perror(fname); exit(-1); } while (fgets(ibuf, BUFSIZ, ifp) != NULL) { if (strncmp(ibuf, "***END", 6) == 0) break; if (compressed) nbytes += comp_to_bin(ibuf, outf); else nbytes += hex_to_bin(ibuf, outf); } fclose(outf); return nbytes; } comp_c_crc(c) unsigned char c; { crc = (crc + c) & WORDMASK; crc = ((crc << 3) & WORDMASK) | (crc >> 13); } comp_e_crc(c) unsigned char c; { crc += c; } #define SIXB(c) (((c)-0x20) & 0x3f) comp_to_bin(ibuf, outf) char ibuf[]; FILE *outf; { char obuf[BUFSIZ]; register char *ip = ibuf; register char *op = obuf; register int n, outcount; int numread, incount; numread = strlen(ibuf); ip[numread-1] = ' '; /* zap out the newline */ outcount = (SIXB(ip[0]) << 2) | (SIXB(ip[1]) >> 4); incount = ((outcount / 3) + 1) * 4; for (n = numread; n < incount; n++) /* restore lost spaces */ ibuf[n] = ' '; n = 0; while (n <= outcount) { *op++ = SIXB(ip[0]) << 2 | SIXB(ip[1]) >> 4; *op++ = SIXB(ip[1]) << 4 | SIXB(ip[2]) >> 2; *op++ = SIXB(ip[2]) << 6 | SIXB(ip[3]); ip += 4; n += 3; } for (n=1; n <= outcount; n++) comp_c_crc(obuf[n]); fwrite(obuf+1, 1, outcount, outf); return outcount; } hex_to_bin(ibuf, outf) char ibuf[]; FILE *outf; { register char *ip = ibuf; register int n, outcount; int c; n = strlen(ibuf) - 1; outcount = n / 2; for (n = 0; n < outcount; n++) { c = hexit(*ip++); comp_e_crc(c = (c << 4) | hexit(*ip++)); fputc(c, outf); } return outcount; } hexit(c) int c; { if ('0' <= c && c <= '9') return c - '0'; if ('A' <= c && c <= 'F') return c - 'A' + 10; fprintf(stderr, "illegal hex digit: %c", c); exit(4); } put2(bp, value) char *bp; short value; { *bp++ = (value >> 8) & BYTEMASK; *bp++ = value & BYTEMASK; } 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; } } >> End << $ COPY SYS$INPUT: XBIN.HLP $ DECK/DOLLAR=">> End <<" NAME xbin - convert mailable format BinHex file into binary before downloading to MacTerminal SYNOPSIS xbin [ -o ] [ -v ] [ -l ] [[ -n name ] file] ... DESCRIPTION Xbin converts a file created by BinHex (usually named with one of the extensions ".hex", ".hcx", or ".hqx") into three host-system files suitable for downloading to a Macintosh via macput. This program is designed for use with the 1.1 Release version of MacTerminal, but includes a compatibility option for the old -0.15X Almost-Alpha version. The -l (list) option reads the header information and prints out all the useful information there, without creating any converted output files. The -v (verbose) option prints a line for each file to be converted, indicating the input and output file names. The -n name option allows the user to specify the name to use when creating the host files and the eventual name to use on the mac. This option must precede the input file name it is to affect. If this option is not used, the names will be derived from either the input file name (.hex or .hcx files), or the name encoded in the header information (.hqx files). Spaces and slashes will be converted to underscores, and the .h?x extension will be deleted, if one is included in the input file name. A file name of "-" indicates that the input should be taken from SYS$INPUT. If no mac file name is specified, the default name (for .hex or .hcx files) is "stdin". Mail or news headers and signatures need not be manually stripped -- xbin will ignore pretty much anything it doesn't need. xbin creates three host-system files from each input file: name.inf, name.dat, and name.rsr. The -o flag specifies "old" (version -0.15X) MacTerminal compatibility mode. BUGS The "LOCKED" bit in the flags cannot be set by xbin. This is due to a bug in MacTerminal, which sets the flags when the file is created, rather than after it has been transfered, resulting in it not being able to write the file. Input files must contain a line starting with "(This file" to detect the beginning of the BinHex information. SEE ALSO macput, macget AUTHOR Dave Johnson, Brown 12/16/84; CRC handling code by Darin Adler, TMQ Software 3/16/85 VMS support by Stew Rubenstein, Harvard University 3/29/85 >> End << $ COPY SYS$INPUT: XBIN.TXT $ DECK/DOLLAR=">> End <<" This version of "xbin" can handle all three BinHex formats (so far). Thanks to Darin Adler at TMQ Software for providing the code to compute and check the CRC values for all three formats. Other new features include "list" and "verbose" modes, the ability to convert several binhex files at one time, the ability to read standard input, somewhat better error handling, and a manual page. Any extraneous mail or news headers are ignored, but xbin relies on finding a line which starts with "(This file" to know when the header ends and the good stuff begins. You can add one of these by hand if it's been lost. To compile it on USG systems, type: cc -o xbin xbin.c or on Berkeley systems: cc -o xbin xbin.c -DBSD As usual, please report any problems, suggestions, or improvements to me. Dave Johnson Brown University Computer Science ddj%brown@csnet-relay.ARPA {ihnp4,decvax,allegra,ulysses,linus}!brunix!ddj >> End <<