info-mac@utcsrgv.UUCP (info-mac) (06/27/84)
Date: 22 Jun 84 (Fri) 18:32:17 EDT From: Dave Johnson <uw-beaver!ddj%brown.csnet@csnet-relay.arpa> To: info-mac@sumex-aim.arpa Subject: downloading files using macterminal/modem7 This program emulates the Mac's modified modem7 protocol on a unix system so that files can be easily downloaded through MacTerminal. It can be used for both binary and mundane text files, and avoids the need to convert binaries to and from printable-hex format. The source and the manual page follow immediately, with everything shifted over one tab stop to avoid problems with mailers eating lines which start with '.'. Macput has been tested under 4.2 on both vaxes and suns, but since I didn't use any 4.2 specific features, it should work under 4.1 without any hassles. Let me know if you have any problems or improvements; I will post updates when/if MacTerminal changes out from under macput. Dave Johnson Brown University ddj%brown@csnet-relay --------- macput.c: --------- #include <stdio.h> #include <signal.h> #include <setjmp.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #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 #define H_TYPEOFF 65 #define H_AUTHOFF 69 #define H_DLENOFF 81 #define H_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]; int m_datalen; int m_rsrclen; } mh; struct filenames { char f_info[256]; char f_data[256]; char f_rsrc[256]; } files; int recno; char buf[DATABYTES]; /* * macput -- send file to macintosh using xmodem protocol * Dave Johnson, Brown University Computer Science 6/17/84 */ char usage[] = "usage: \"macput [-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; ac--; av++; while (ac) { if (av[0][0] == '-') { switch (av[0][1]) { 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; default: bad_usage: fprintf(stderr, usage); exit(1); } } else { filename = av[0]; } ac--; av++; } find_files(filename, mode); if (mode != FULL) forge_info(); setup_tty(); if (send_sync() == ACK) { txtmode = 0; send_file(files.f_info, 1); if (mode == TEXT) txtmode++; send_file(files.f_data, 1); txtmode = 0; send_file(files.f_rsrc, 0); } if (mode != FULL) unlink(files.f_info); reset_tty(); } find_files(filename, mode) { int n; 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 status, 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); 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(stderr, "starting handshake timeout\r\n"); } fprintf(stderr, "giving up\r\n"); } send_file(fname, more) char *fname; int more; { register int status, i, n; register char *bp; FILE *inf; int naks = 0; 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 (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); } 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() { longjmp(timobuf, 1); } static struct sgttyb otty, ntty; /* should turn messages off */ setup_tty() { int cleanup(); int timedout(); ttyf = stdin; ttyfd = fileno(stdin); ioctl(ttyfd, TIOCGETP, &otty); signal(SIGHUP, cleanup); signal(SIGINT, cleanup); signal(SIGQUIT, cleanup); signal(SIGTERM, cleanup); signal(SIGALRM, timedout); ntty = otty; ntty.sg_flags = RAW|ANYP; ioctl(ttyfd, TIOCSETP, &ntty); } reset_tty() { if (ttyf != NULL) { sleep(2); /* should wait for output to drain */ ioctl(ttyfd, TIOCSETP, &otty); } } cleanup(sig) int sig; { reset_tty(); exit(sig); } put4(bp, value) char *bp; int value; { register int i, c; for (i = 0; i < 4; i++) { c = (value >> 24) & BYTEMASK; value <<= 8; *bp++ = c; } } -------- macput.l -------- .TH MACPUT local "22 June 1984" .UC 4 .SH NAME macput \- send file to macintosh via modem7 / macterminal .SH SYNOPSIS .B macput file .br .B macput [ .B \-rdu ] file [ .B \-t type ] [ .B \-a author ] [ .B \-n name ] .SH DESCRIPTION .I Macput sends a file to a Macintosh running MacTerminal. MacTerminal should be set to use the modem7 protocol, and should have flow control disabled. This program has only been tested with version -0.15x of MacTerminal. .PP If none of the .B \-rdu flags are specified, .I macput sends three files to the mac: .IB file .info , .IB file .data , and .IB file .rsrc . This is useful for returning files to the mac which were stored using macget. .PP The .B \-r flag specifies .I resource mode. Either .IB file .rsrc or .I file will be sent to the Mac, along with a forged .B .info file and an empty .B .data file. The file sent becomes the resource fork of the Mac file. .PP The .B \-d flag specifies .I data mode. Either .IB file .data , .IB file .text or .I file will be sent to the Mac, along with a forged .B .info file and an empty .B .rsrc file. The file sent becomes the data fork of the Mac file. .PP The .B \-u flag requests .I unix mode, which is the same as .I data mode except unix newline characters are converted into carriage returns. .PP The remaining options serve to override the default file type, author, and file name to be used on the Mac. The defaults for .I resource mode are "APPL", "CCOM", and .IR file . .I data mode defaults are "TEXT", "????", and .I unix mode defaults are "TEXT" and "MACA". .SH SEE ALSO macget(local) .SH BUGS Doesn't work over flow controlled communication lines, or when using rlogin. .PP Doesn't set the bundle bit on resource files, to incorporate any icons into the Desk Top. Use setfile to set the bundle bit. ----------- end of file