[net.micro.apple] WANTED! Macput & Macget - David Spec

spector@acf4.UUCP (11/08/84)

Hi there, Yes, I do read net.micro.apple!  Sorry you couldn't get to me..
There were a few people who sent mail to me via UUCPnet and to whom
I couldn't reply because I couldn't get mail to'em, but heres the Macput/MacGet
sources with the manual pages...

...Sorry they're not in Archive form..., I'll see about putting in archive form
and then re-posting them to net.sources...

			-Enjoy
			  Dave.



This file contains the Following:

	Manual page for MACGET
	Manual page for MACPUT
	Corrected MacGet  program (in C)
	Corrected MacPut  program (in C)

All of the above were skillfully crafted by Dave Johnson at Brown, and
have been maintained (at New York University) by David Spector.

------------------------------------------------------------------------------
.TH MACGET local "31 July 1984"
.UC 4
.SH NAME
macget \- receive file from macintosh via Xmodem/macterminal
.SH SYNOPSIS
.B macget
[ 
.B \-rdu
] [file]
.SH DESCRIPTION
.I 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
versions of MacTerminal, but includes a compatibility option for the
older -0.15X Almost-Alpha version.
.PP
To use this program, log into the unix system using MacTerminal,
start macget with the desired options, select "Send File..." from
the "File" menu, and open the file you wish to send.
If MacTerminal is properly configured, it will put up an indicator
showing how much of the file has been transfered.
Several Control-X's may be used to force macget
to give up if the transfer fails.
.PP
The optional
.I file
parameter specifies the name to use when creating the unix files,
otherwise the Mac file name is used
(with spaces converted to underscores).
.PP
If none of the
.B \-rdu
flags are specified,
.I macget
receives three files from the Mac:
.IB file .info ,
.IB file .data ,
and
.IB file .rsrc .
This mode is useful for storing Mac files so they can
be restored later using
.IR macput .
.PP
The
.B \-r
flag specifies
.I resource
mode.
Only
.IB file .rsrc
will be created, from the Mac file's resource fork.
.PP
The
.B \-d
flag specifies
.I data
mode.
Only
.IB file .data
will be created, containing the data fork of the Mac file.
.PP
The
.B \-u
flag requests
.I unix
mode, in which carriage returns are converted into
unix newline characters, and the unix file
.IB file .text
is created.
A file saved from Mac applications as "text only" can be transfered
using this option to convert it to a normal unix text file.
.PP
The
.B \-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.
.SH SEE ALSO
macput(local)
.SH BUGS
Doesn't work over flow controlled communication lines,
or when using rlogin.
.SH AUTHOR
Dave Johnson, Brown 7/31/84

-----------------Manual Page for MacPut Follows-------------------------------
.TH MACPUT local "31 July 1984"
.UC 4
.SH NAME
macput \- send file to macintosh via xmodem/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.
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.
.PP
To use this program, log into the unix system using MacTerminal,
and run macput specifying the desired options and one file to be sent.
If MacTerminal is properly configured, it will recognize that a file
is arriving on the serial line and put up an indicator showing how
much of the file has been sent.
Several Control-X's may be used to force macput
to give up if the transfer fails.
.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.
Human-readable unix text files sent to the Mac using this option
will be compatible with applications which expect "text only" files.
.PP
The
.B \-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.
.PP
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
.I resource
mode are "APPL" and "CCOM".
.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.
.SH FEATURES
Properly initializes the Creation Date.
.SH AUTHOR
Dave Johnson, Brown 7/31/84

---------------------MacGet Program Follows----------------------------------
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <sgtty.h>

#ifdef NO_RENAME
#define rename(old, new)	link(old, new); unlink(old)
#endif

/* 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 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;
int pre_beta;	/* -o flag; for compatibility with MacTerminal Version -0.15X */

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;
char buf[DATABYTES];

/*
 * 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
 *
 * 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()
 */
char usage[] = "usage: \"macget [-o] [-rdu] [filename]\"\n";

main(ac, av)
char **av;
{ 
	char *name;

	mode = FULL;
	name = "";
	ac--; av++;
	while (ac) {
		if (av[0][0] == '-') {
			switch (av[0][1]) {
			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();
} 

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.text", 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);
	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 */
} 

purge(timeout)
int timeout;
{ 
	int c;

	do {
		c = tgetc(timeout);
	} while (c != TMO);
} 

static int ttyfd;
static FILE *ttyf;
jmp_buf timobuf;

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;

	cksum = 0;
	bp = buf;
	for (i = 0; i < count; bp++, i++) {
		cksum += *bp;
		if (txtmode && *bp == '\r')
			*bp = '\n';
	}
	return cksum;
} 

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;
} 

tputc(c)
char c;
{ 
	write(ttyfd, &c, 1);
} 

timedout()
{ 
	signal(SIGALRM, timedout);	/* for pre-4.2 systems */
	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()
{ 
	sleep(2);	/* should wait for output to drain */
	ioctl(ttyfd, TIOCSETP, &otty);
} 

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;
} 

----------------------Macput Program Follows----------------------------------

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <sgtty.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/timeb.h>

/* 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 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;
int pre_beta;	/* -o flag; for compatibility with MacTerminal Version -0.15X */

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;
char buf[DATABYTES];

/*
 * macput -- send file to macintosh using xmodem protocol
 * Dave Johnson, Brown University Computer Science
 *
 * (c) 1984 Brown University
 * 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
 */
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;
	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;
			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;
		sleep(1);
		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 tm *tp;
	struct timeb tbuf;
	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 (!pre_beta) {
		ftime(&tbuf);
		tp = localtime(&tbuf.time);
		tdiff = TIMEDIFF - tbuf.timezone * 60;
		if (tp->tm_isdst)
			tdiff += 60 * 60;
		mh.m_createtime = stbuf.st_mtime + tdiff;
		mh.m_modifytime = stbuf.st_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(stderr, "starting handshake timeout\r\n");
	}
	fprintf(stderr, "giving up\r\n");
	return CAN;
} 

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);
	}
	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);
} 

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);
} 

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;
long value;
{ 
	register int i, c;

	for (i = 0; i < 4; i++) {
		c = (value >> 24) & BYTEMASK;
		value <<= 8;
		*bp++ = c;
	}
}