[net.sources.mac] macput: here it is

vishniac@wanginst.UUCP (Ephraim Vishniac) (07/23/85)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	macput.c
#	macput.1w
#	Makefile
# This archive created: Tue Jul 23 08:25:38 1985
export PATH; PATH=/bin:$PATH
echo shar: extracting "'macput.c'" '(8781 characters)'
if test -f 'macput.c'
then
	echo shar: will not over-write existing file "'macput.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'macput.c'
	X/*
	X
	XHere is the source for the current incarnation of macput . . . .
	XIt is compatible with the 1.1 Release version of MacTerminal,
	Xthough in case you still need to use the -0.15X version, there's
	Xthe "-o" option to provide compatibility.  Versions 0.5 and 0.9
	Xhave a bug in the record checksum calculation which will break
	Xfile transfers, so 1.1 is recommended.
	X
	XPlease pass any improvements/bug fixes on to me, and
	Xif you know of any good protocols for use on a flow-controlled
	Xline, let me know.
	X
	X	Dave Johnson
	X	ddj%brown@csnet-relay.arpa
	X	Brown University Computer Science
	X
	X*/
	X#include <stdio.h>
	X#include <signal.h>
	X#include <setjmp.h>
	X#include <sgtty.h>
	X#include <time.h>
	X#include <sys/types.h>
	X#include <sys/stat.h>
	X#include <sys/timeb.h>
	X
	X/* Mac time of 00:00:00 GMT, Jan 1, 1970 */
	X#define TIMEDIFF 0x7c25b080
	X
	X#define RECORDBYTES 132
	X#define DATABYTES 128
	X#define NAMEBYTES 63
	X
	X#define RETRIES 10
	X#define ACKTIMO 10
	X
	X#define MAXRECNO 0xff
	X#define BYTEMASK 0xff
	X
	X#define TMO -1
	X#define DUP '\000'
	X#define SOH '\001'
	X#define EOT '\004'
	X#define ACK '\006'
	X#define NAK '\025'
	X#define CAN '\030'
	X#define EEF '\032'
	X#define ESC '\033'
	X
	X#define H_NLENOFF 1
	X#define H_NAMEOFF 2
	X/* 65 <-> 80 is the FInfo structure */
	X#define H_TYPEOFF 65
	X#define H_AUTHOFF 69
	X
	X#define H_LOCKOFF 81
	X#define H_DLENOFF 83
	X#define H_RLENOFF 87
	X#define H_CTIMOFF 91
	X#define H_MTIMOFF 95
	X
	X#define H_OLD_DLENOFF 81
	X#define H_OLD_RLENOFF 85
	X
	X#define TEXT 0
	X#define DATA 1
	X#define RSRC 2
	X#define FULL 3
	X
	Xint mode, txtmode;
	Xint pre_beta;	/* -o flag; for compatibility with MacTerminal Version -0.15X */
	X
	Xstruct macheader {
	X	char m_name[NAMEBYTES+1];
	X	char m_type[4];
	X	char m_author[4];
	X	long m_datalen;
	X	long m_rsrclen;
	X	long m_createtime;
	X	long m_modifytime;
	X} mh;
	X
	Xstruct filenames {
	X	char f_info[256];
	X	char f_data[256];
	X	char f_rsrc[256];
	X} files;
	X
	Xint recno;
	Xchar buf[DATABYTES];
	X
	X/*
	X * macput -- send file to macintosh using xmodem protocol
	X * Dave Johnson, Brown University Computer Science
	X *
	X * (c) 1984 Brown University 
	X * may be used but not sold without permission
	X *
	X * created ddj 6/17/84 
	X * revised ddj 7/16/84 -- protocol changes for MacTerminal Beta Version 0.5X
	X * revised ddj 7/31/84 -- pre-4.2 signal bugs fixed in timedout()
	X * revised ddj 7/31/84 -- moved forge_info() call ahead of send_sync()
	X * revised ddj 11/6/84 -- added sleep(5) after send_sync to give mac time to
	X *	turn off xon mode, set up sio chip, and put up progress indicator
	X */
	Xchar usage[] =
	X    "usage: \"macput [-o] [-rdu] [-t type] [-a author] [-n name] filename\"\n";
	X
	Xmain(ac, av)
	Xchar **av;
	X{
	X	int n;
	X	char *filename;
	X
	X	if (ac == 1) {
	X		fprintf(stderr, usage);
	X		exit(1);
	X	}
	X
	X	mode = FULL;
	X	ac--; av++;
	X	while (ac) {
	X		if (av[0][0] == '-') {
	X			switch (av[0][1]) {
	X			case 'r':
	X				mode = RSRC;
	X				strncpy(mh.m_type, "APPL", 4);
	X				strncpy(mh.m_author, "CCOM", 4);
	X				break;
	X			case 'u':
	X				mode = TEXT;
	X				strncpy(mh.m_type, "TEXT", 4);
	X				strncpy(mh.m_author, "MACA", 4);
	X				break;
	X			case 'd':
	X				mode = DATA;
	X				strncpy(mh.m_type, "TEXT", 4);
	X				strncpy(mh.m_author, "????", 4);
	X				break;
	X			case 'n':
	X				if (ac > 1) {
	X					ac--; av++;
	X					n = strlen(av[0]);
	X					if (n > NAMEBYTES) n = NAMEBYTES;
	X					strncpy(mh.m_name, av[0], n);
	X					mh.m_name[n] = '\0';
	X					break;
	X				}
	X				else goto bad_usage;
	X			case 't':
	X				if (ac > 1) {
	X					ac--; av++;
	X					strncpy(mh.m_type, av[0], 4);
	X					break;
	X				}
	X				else goto bad_usage;
	X			case 'a':
	X				if (ac > 1) {
	X					ac--; av++;
	X					strncpy(mh.m_author, av[0], 4);
	X					break;
	X				}
	X				else goto bad_usage;
	X			case 'o':
	X				pre_beta++;
	X				break;
	X			default:
	Xbad_usage:
	X				fprintf(stderr, usage);
	X				exit(1);
	X			}
	X		}
	X		else {
	X			filename = av[0];
	X		}
	X		ac--; av++;
	X	}
	X
	X	setup_tty();
	X	find_files(filename, mode);
	X	if (mode != FULL)
	X		forge_info();
	X
	X	if (send_sync() == ACK) {
	X		txtmode = 0;
	X		sleep(5);
	X		send_file(files.f_info, 1);
	X
	X		if (mode != FULL)
	X			unlink(files.f_info);
	X
	X		if (mode == TEXT) txtmode++;
	X		send_file(files.f_data, 1);
	X
	X		txtmode = 0;
	X		send_file(files.f_rsrc, 0);
	X	}
	X	reset_tty();
	X}
	X
	Xfind_files(filename, mode)
	Xchar *filename;
	X{
	X	int n, tdiff;
	X	struct tm *tp;
	X	struct timeb tbuf;
	X	struct stat stbuf;
	X
	X	sprintf(files.f_data, "%s.data", filename);
	X	sprintf(files.f_rsrc, "%s.rsrc", filename);
	X
	X	if (mode == FULL) {
	X		sprintf(files.f_info, "%s.info", filename);
	X		if (stat(files.f_info, &stbuf) != 0) {
	X			perror(files.f_info);
	X			cleanup(-1);
	X		}
	X		return;
	X	}
	X	else {
	X		strcpy(files.f_info, "#machdrXXXXXX");
	X		mktemp(files.f_info);
	X	}
	X
	X	if (mode == RSRC) {
	X		strcpy(files.f_data, "/dev/null");
	X		if (stat(files.f_rsrc, &stbuf) != 0) {
	X			strcpy(files.f_rsrc, filename);
	X			if (stat(files.f_rsrc, &stbuf) != 0) {
	X				perror(files.f_rsrc);
	X				cleanup(-1);
	X			}
	X		}
	X		mh.m_datalen = 0;
	X		mh.m_rsrclen = stbuf.st_size;
	X	}
	X	else {
	X		strcpy(files.f_rsrc, "/dev/null");
	X		if (stat(files.f_data, &stbuf) != 0) {
	X			sprintf(files.f_data, "%s.text", filename);
	X			if (stat(files.f_data, &stbuf) != 0) {
	X				strcpy(files.f_data, filename);
	X				if (stat(files.f_data, &stbuf) != 0) {
	X					perror(files.f_data);
	X					cleanup(-1);
	X				}
	X			}
	X		}
	X		mh.m_datalen = stbuf.st_size;
	X		mh.m_rsrclen = 0;
	X	}
	X
	X	if (!pre_beta) {
	X		ftime(&tbuf);
	X		tp = localtime(&tbuf.time);
	X		tdiff = TIMEDIFF - tbuf.timezone * 60;
	X		if (tp->tm_isdst)
	X			tdiff += 60 * 60;
	X		mh.m_createtime = stbuf.st_mtime + tdiff;
	X		mh.m_modifytime = stbuf.st_mtime + tdiff;
	X	}
	X
	X	if (mh.m_name[0] == '\0') {
	X		n = strlen(filename);
	X		if (n > NAMEBYTES) n = NAMEBYTES;
	X		strncpy(mh.m_name, filename, n);
	X		mh.m_name[n] = '\0';
	X	}
	X}
	X
	Xforge_info()
	X{
	X	int n;
	X	char *np;
	X	FILE *fp;
	X
	X	for (np = mh.m_name; *np; np++)
	X		if (*np == '_') *np = ' ';
	X
	X	buf[H_NLENOFF] = n = np - mh.m_name;
	X	strncpy(buf + H_NAMEOFF, mh.m_name, n);
	X	strncpy(buf + H_TYPEOFF, mh.m_type, 4);
	X	strncpy(buf + H_AUTHOFF, mh.m_author, 4);
	X	if (pre_beta) {
	X		put4(buf + H_OLD_DLENOFF, mh.m_datalen);
	X		put4(buf + H_OLD_RLENOFF, mh.m_rsrclen);
	X	}
	X	else {
	X		put4(buf + H_DLENOFF, mh.m_datalen);
	X		put4(buf + H_RLENOFF, mh.m_rsrclen);
	X		put4(buf + H_CTIMOFF, mh.m_createtime);
	X		put4(buf + H_MTIMOFF, mh.m_modifytime);
	X	}
	X	fp = fopen(files.f_info, "w");
	X	if (fp == NULL) {
	X		perror("temp file");
	X		cleanup(-1);
	X	}
	X	fwrite(buf, 1, DATABYTES, fp);
	X	fclose(fp);
	X}
	X
	Xsend_sync()
	X{
	X	int c, i;
	X
	X	for (i = 0; i < 3; i++) {
	X		tputc(ESC);
	X		tputc('a');
	X		while ((c = tgetc(ACKTIMO)) != TMO) {
	X			switch (c) {
	X			case CAN:
	X			case EOT:
	X			case ACK:
	X				return c;
	X			default:
	X				continue;
	X			}
	X		}
	X		fprintf(stderr, "starting handshake timeout\r\n");
	X	}
	X	fprintf(stderr, "giving up\r\n");
	X	return CAN;
	X}
	X
	Xsend_file(fname, more)
	Xchar *fname;
	Xint more;
	X{
	X	register int status, i, n;
	X	FILE *inf;
	X
	X	inf = fopen(fname, "r");
	X	if (inf == NULL) {
	X		perror(fname);
	X		cleanup(-1);
	X	}
	X	recno = 1;
	X	for (;;) {
	X		n = fread(buf, 1, DATABYTES, inf);
	X		if (n > 0) {
	X			for (i = 0; i < RETRIES; i++) {
	X				send_rec(buf, DATABYTES);
	X				status = tgetc(ACKTIMO);
	X				if (status != NAK)
	X					break;
	X			} 
	X			if (status == NAK || status == CAN) {
	X				fclose(inf);
	X				cleanup(-1);
	X				/* NOTREACHED */
	X			}
	X		}
	X		if (n < DATABYTES) {
	X			tputc(EOT);
	X			if (!pre_beta) {
	X				status = tgetc(ACKTIMO);
	X			}
	X			if (more) {
	X				status = tgetc(ACKTIMO);
	X			}
	X			return;
	X		}
	X		recno++;
	X		recno &= MAXRECNO;
	X	}
	X}
	X
	Xsend_rec(buf, recsize)
	Xchar buf[];
	Xint recsize;
	X{
	X	int i, cksum;
	X	char *bp;
	X
	X	cksum = 0;
	X	bp = buf;
	X	for (i = 0; i < recsize; i++, bp++) {
	X		if (txtmode && *bp == '\n')
	X			*bp = '\r';
	X		cksum += *bp;
	X	}
	X
	X	tputc(SOH);
	X	tputc((char)recno);
	X	tputc((char)(MAXRECNO - recno));
	X	tputrec(buf, recsize);
	X	tputc((char)cksum);
	X}
	X
	Xstatic int ttyfd;
	Xstatic FILE *ttyf;
	Xstatic jmp_buf timobuf;
	X
	Xtgetc(timeout)
	Xint timeout;
	X{
	X	int c;
	X
	X	if (setjmp(timobuf))
	X		return TMO;
	X
	X	alarm(timeout);
	X	c = getc(ttyf);
	X	alarm(0);
	X
	X	if (c == -1)	/* probably hung up or logged off */
	X		return EOT;
	X	else
	X		return c & BYTEMASK;
	X}
	X
	Xtputrec(buf, count)
	Xchar *buf;
	Xint count;
	X{
	X	write(ttyfd, buf, count);
	X}
	X
	Xtputc(c)
	Xchar c;
	X{
	X	write(ttyfd, &c, 1);
	X}
	X
	Xtimedout()
	X{
	X	signal(SIGALRM, timedout);	/* for pre-4.2 systems */
	X	longjmp(timobuf, 1);
	X}
	X
	Xstatic struct sgttyb otty, ntty;
	X/* should turn messages off */
	X
	Xsetup_tty()
	X{
	X	int cleanup();
	X	int timedout();
	X
	X	ttyf = stdin;
	X	ttyfd = fileno(stdout);
	X	ioctl(ttyfd, TIOCGETP, &otty);
	X	signal(SIGHUP, cleanup);
	X	signal(SIGINT, cleanup);
	X	signal(SIGQUIT, cleanup);
	X	signal(SIGTERM, cleanup);
	X	signal(SIGALRM, timedout);
	X	ntty = otty;
	X	ntty.sg_flags = RAW|ANYP;
	X	ioctl(ttyfd, TIOCSETP, &ntty);
	X}
	X
	Xreset_tty()
	X{
	X	if (ttyf != NULL) {
	X		sleep(2);	/* should wait for output to drain */
	X		ioctl(ttyfd, TIOCSETP, &otty);
	X	}
	X}
	X
	Xcleanup(sig)
	Xint sig;
	X{
	X	reset_tty();
	X	exit(sig);
	X}
	X
	Xput4(bp, value)
	Xchar *bp;
	Xlong value;
	X{
	X	register int i, c;
	X
	X	for (i = 0; i < 4; i++) {
	X		c = (value >> 24) & BYTEMASK;
	X		value <<= 8;
	X		*bp++ = c;
	X	}
	X}
	X
SHAR_EOF
if test 8781 -ne "`wc -c < 'macput.c'`"
then
	echo shar: error transmitting "'macput.c'" '(should have been 8781 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'macput.1w'" '(3396 characters)'
if test -f 'macput.1w'
then
	echo shar: will not over-write existing file "'macput.1w'"
else
sed 's/^	X//' << \SHAR_EOF > 'macput.1w'
	X.TH MACPUT.WI "8 Nov 1984"
	X.UC 4
	X.SH NAME
	Xmacput \- send file to macintosh via modem7/macterminal
	X.SH SYNOPSIS
	X.B macput
	Xfile
	X.br
	X.B macput
	X[
	X.B \-rdu
	X] file
	X[
	X.B \-t
	Xtype
	X]
	X[
	X.B \-a
	Xauthor
	X]
	X[
	X.B \-n
	Xname
	X]
	X.SH DESCRIPTION
	X.I Macput
	Xsends a file to a Macintosh running MacTerminal.
	XThe File Transfer settings should specify the "Modem7"
	Xtransfer method and a "MacTerminal" remote system.
	XThis program is designed for use with the 1.1 Release
	Xversion of MacTerminal, but includes a compatibility option for the
	Xold -0.15X Almost-Alpha version.
	X.PP
	XTo use this program, log into the unix system using MacTerminal,
	Xand run macput specifying the desired options and one file to be sent.
	XIf MacTerminal is properly configured, it will recognize that a file
	Xis arriving on the serial line and put up an indicator showing how
	Xmuch of the file has been sent.
	XSeveral Control-X's may be used to force macput
	Xto give up if the transfer fails.
	X.PP
	XIf none of the
	X.B \-rdu
	Xflags are specified, 
	X.I macput 
	Xsends three unix files to the Mac:
	X.IB file .info ,
	X.IB file .data ,
	Xand
	X.IB file .rsrc .
	XThese specify the three parts of one Mac file:  the .data file
	Xbecomes the data fork, the .rsrc file becomes the resource fork,
	Xand the .info file specifies the sizes of the two forks, as well
	Xas the file name, file type, creation date, and other information.
	XThis is useful for returning files to the Mac which were stored
	Xusing macget.
	X.PP
	XThe
	X.B \-r
	Xflag specifies 
	X.I resource
	Xmode.
	XEither
	X.IB file .rsrc
	Xor
	X.I file
	Xwill be sent to the Mac, along with a forged
	X.B .info
	Xfile and an empty 
	X.B .data
	Xfile.
	XThe file sent becomes the resource fork of the Mac file.
	X.PP
	XThe
	X.B \-d
	Xflag specifies 
	X.I data
	Xmode.
	XEither
	X.IB file .data
	X,
	X.IB file .text
	Xor
	X.I file
	Xwill be sent to the Mac, along with a forged
	X.B .info
	Xfile and an empty 
	X.B .rsrc
	Xfile.
	XThe file sent becomes the data fork of the Mac file.
	X.PP
	XThe 
	X.B \-u
	Xflag requests 
	X.I unix
	Xmode, which is the same as 
	X.I data
	Xmode except unix newline characters are converted
	Xinto carriage returns.
	XHuman-readable unix text files sent to the Mac using this option 
	Xwill be compatible with applications which expect "text only" files.
	X.PP
	XThe 
	X.B \-o
	Xflag specifies "old" (version -0.15X) MacTerminal compatibility mode.
	XYou must manually disable XON/XOFF flow control in this version to
	Xperform file transfer; this is done automatically in the newer versions.
	X.PP
	XThe remaining options serve to override the default
	Xfile type, author, and file name to be used on the Mac.
	XThe default type and author for 
	X.I resource
	Xmode are "APPL" and "CCOM".
	X.I data
	Xmode defaults are "TEXT", "????", and 
	X.I unix
	Xmode defaults are "TEXT" and "MACA".
	X.SH SEE ALSO
	Xmacget(local)
	X.SH BUGS
	XThe modem7 protocol will not work over flow controlled communication lines,
	Xsome terminal concentrators, or when using rlogin.
	X.PP
	XDoesn't set the bundle bit on resource files,
	Xto incorporate any icons into the Desk Top.
	XUse setfile to set the bundle bit.
	X.PP
	XMacTerminal Beta versions 0.5 and 0.9 incorrectly calculate record checksums
	Xunder certain circumstances, preventing some files from being successfully
	Xtransfered; this bug has been fixed in the released version 1.1.
	XThis program is otherwise compatible with all versions of MacTerminal 
	Xsince 0.5 (and -0.15X by using the -o option).
	X.SH FEATURES
	XProperly initializes the Creation Date.
	X.SH AUTHOR
	XDave Johnson, Brown 7/31/84
	X
SHAR_EOF
if test 3396 -ne "`wc -c < 'macput.1w'`"
then
	echo shar: error transmitting "'macput.1w'" '(should have been 3396 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'Makefile'" '(191 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
sed 's/^	X//' << \SHAR_EOF > 'Makefile'
	Xmacput:	macput.o
	X	cc  macput.o -o macput
	X
	Xmacput-install:	macput
	X	mv macput /usr/local/bin/macput
	X
	Xmacput-man-install:	macput.1w
	X	cp macput.1w /usr/man/man1/macput.1w
	X	man macput >/dev/null&
SHAR_EOF
if test 191 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 191 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0