[net.sources.mac] MacGet and MacPut for VMS

stew@harvard.ARPA (Stew Rubenstein) (03/01/85)

Here is my version of macget and macput for VMS.  To unpack this, just
cut at the dotted line and execute the remainder of the file as a DCL
command procedure.  This works under Version 3.* of VMS.  It should work
under Version 4 as well, but some improvements will be possible;  I will
post again if there is enough interest.

Stew Rubenstein
Harvard University Chemical Labs
12 Oxford St.
Cambridge, MA  02138
rubenstein@harvard.arpa
{ihnp4,seismo,ut-sally}!harvard!rubenstein

------------------------------- Cut Here -----------------------------
$ ! This is a DCL command procedure.  Unpack with DCL, not sh!
$ COPY SYS$INPUT: 00README.TXT
$ DECK/DOLLAR=">> End <<"
Here is the VMS version of macget and macput.  This program was originally
written by Dave Johnson at Brown University, and was 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.

This file should be unpacked by executing it as a command procedure.
It will create the files 00README.TXT (this file), MACGET.C, MACGET.HLP,
MACGETPUT.H, MACPUT.C, MACPUT.HLP, MAKE.COM, RDT.C and TTYIO.MAR.
The command procedure MAKE.C will compile and link both MACGET and
MACPUT.

Please send comments, bug reports, etc to:
   Stew Rubenstein
   rubenstein@harvard.arpa
   {ihnp4, seismo, ut-sally}!harvard!rubenstein
>> 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];

/*
 * 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 = "";
	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();
}

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 {
		unlink(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 */
}

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) return TMO;
		cksum += c;
		if (txtmode && c == '\r')
			c = '\n';
		*(bp++) = c;
	}
	return cksum;
}

tgetc(timeout)
int timeout;
{
	int c;

	timed_in(&c, &timeout);
	if (c == -1)	/* probably hung up or logged off */
		return EOT;
	else
		return c & BYTEMASK;
}

tputc(c)
char c;
{
	out(&c);
	purge();
}

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')
		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;
}
>> 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_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)
#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];

/*
 * 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;
	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;
		send_file(files.f_info, 1);

/*  ***  Needs to be converted.
		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, 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(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);
}

tgetc(timeout)
int timeout;
{
	int c;

	timed_in(&c, &timeout);
	if (c == -1)	/* probably hung up or logged off */
		return EOT;
	else
		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();
}

tputc(c)
char c;
{
	out(&c);
	flush();
}

setup_tty()
{
	int (*cleanup)();

	raw_mode();
	signal(SIGHUP, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGQUIT, cleanup);
	signal(SIGTERM, cleanup);
}

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;
	}
}
>> 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
$ macro ttyio
$ link macput, rdt, ttyio
$ link macget, ttyio
>> 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.

.MACRO	MOVDSC	SOURCE, DEST
	MOVAL	SOURCE, R0
	MOVZBL	(R0)+, R1
	PUSHL	R0
	PUSHL	R1
	MOVL	SP, DEST
.ENDM

	.PSECT	$LOCAL, PIC, WRT, NOEXE, CON, LONG, NOSHR

OUTPTR:	.ADDRESS OUTBUF
TTIOSB:	.BLKL	2
TTYCHN:	.BLKW	1
TTMODE: .BLKQ	1
BUFF:	.BLKB	10
OUTBUF:	.BLKB	256
ENDBUF:

DEVNAM:	.ASCIC	/TT/

	.PSECT	$CODE NOWRT, EXE, SHR, CON, PIC

.ENTRY	TIMED_IN, 0
;     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.
;--
	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)
	CMPW	TTIOSB, #SS$_TIMEOUT
	BNEQ	1$
	MCOML	#0, @4(AP)
1$:	RET

.ENTRY	PURGE, 0
;     SUBROUTINE PURGE
;++
;	PURGE clears the input buffer of the graphics terminal.
;--
	TSTW	TTYCHN
	BNEQ	1$
	BSBW	GETTRM
1$:	$QIOW_S	CHAN=TTYCHN, -
		FUNC=#IO$_READVBLK!IO$M_NOECHO!IO$M_TIMED!IO$M_PURGE, -
		P1=BUFF, -
		P2=#1, -
		P3=#0
	RET

.ENTRY	IN,0
;     SUBROUTINE IN(CHAR)
;++
;	IN reads a single character from the terminal and returns it in CHAR.
;--
	CALLS	#0, FLUSH
	$QIOW_S	CHAN=TTYCHN, -
		FUNC=#IO$_READVBLK!IO$M_NOECHO!IO$M_NOFILTR, -
		P1=@4(AP), -
		P2=#1
;	CMPB	@4(AP), #3	; Is it a Ctrl-C?
;	BEQL	1$
	RET
;1$:	$Exit_S	#1


.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	ENDBUF, R0
	CMPL	OUTPTR, R0
	BNEQ	1$
	CALLS	#0, FLUSH
1$:	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$:	RET

GETTRM:
	MOVDSC	DEVNAM, R2
	$ASSIGN_S DEVNAM=(R2), CHAN=TTYCHN
	BLBS	R0, 1$
	$EXIT_S	R0
1$:	ADDL	#8, SP
	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 <<
$ WRITE SYS$OUTPUT "That's all!"
$ EXIT
-- 
-----------------------
Stew Rubenstein     UUCP: ihnp4!harvard!stew
Harvard Chemistry   ARPA: stew@harvard