From: Frank D. Cringle <fdc@cliwe.ping.de>
Date: Tue, 30 Aug 94 09:40:15 +0200
Message-Id: <9408300740.AA08233@cliwe.ping.de>
To: jeff@vela.acs.oakland.edu
Subject: Submission for CP/M archives

Here is a unix equivalent to the epslink.azm program in the epson area of the
archives.  It may be useful for folks who want to transfer files between unix
machines and epson PX-8's without needing to install xmodem or kermit or
whatever on their PX-8.  Other than that I reverse-engineered it from
filink.com on my PX-8, its all my own work and I hereby donate it to the
public domain.

I am submitting this as a shell archive, because it is aimed at unix users.
Feel free to convert it if you want to conform to some other convention.

Submitted-by: fdc@cliwe.ping.de
Archive-name: filink-0.01/part01

---- Cut Here and feed the following to sh ----
#!/bin/sh
# This is filink-0.01, a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 08/30/1994 07:37 UTC by fdc@cliwe.ping.de
# Source directory /tmp/filink-0.01
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   1892 -rw-rw-r-- README
#    112 -rw-rw-r-- Makefile
#   7054 -rw-rw-r-- filink.c
#
# ============= README ==============
if test -f 'README' -a X"$1" != X"-c"; then
	echo 'x - skipping README (File already exists)'
else
echo 'x - extracting README (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'README' &&
Transfer files between a unix machine and an Epson PX-8 (Geneva)
================================================================
X
filink.c implements the file transfer protocol which is built into
the Epson PX-8 (a portable CP/M machine of early '80s vintage).
X
It is designed to be called as a file-transfer subprocess by some
other program that is talking to the PX-8.  This might be cu or tip
or, best of all, pcomm.
X
The user interface of filink is, well, spartan.  If filink is executed
without parameters it expects to receive one or more files from the
PX-8.  If you give it some command-line arguments (which may have been
wildcard-expanded by a shell), it will interpret them as filenames and
try to send them to the PX-8.
X
Using filing from tip:
~Cfilink			# receive files
~Cfilink list of files		# send files
X
Using filink from cu (only works with gnu (Taylor) cu):
~+filink			# receive files
~+filink list of files		# send files
X
Using filink from pcomm:
First install filink as an external protocol -
type ctrl-A S 6, then enter filink for upload and download as shown:
X 
X                                    UPLOAD
X 
X        Name         Command line                     Requires file list?
X     1) zmodem       zmtx                                      Y
X     2) kermit       kermit -ivs                               Y
X     3) filink       filink                                    Y
X 
X                                   DOWNLOAD
X 
X        Name         Command line                     Requires file list?
X     4) zmodem       zmrx -o                                   N
X     5) kermit       kermit -ivr                               Y
X     6) filink       filink                                    N
X 
You can now transfer files by typing ctrl-A <up-arrow> to upload to
the PX-8 or ctrl-A <down-arrow> to download files to the unix machine.
X
X
Frank Cringle
fdc@cliwe.ping.de
SHAR_EOF
chmod 0664 README ||
echo 'restore of README failed'
Wc_c="`wc -c < 'README'`"
test 1892 -eq "$Wc_c" ||
	echo 'README: original size 1892, current size' "$Wc_c"
fi
# ============= Makefile ==============
if test -f 'Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping Makefile (File already exists)'
else
echo 'x - extracting Makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
CC            =	gcc
CFLAGS        =	-O2 -g -Wall
X
filink:		filink.o
X
install:	filink
X		mv filink /usr/local/bin
SHAR_EOF
chmod 0664 Makefile ||
echo 'restore of Makefile failed'
Wc_c="`wc -c < 'Makefile'`"
test 112 -eq "$Wc_c" ||
	echo 'Makefile: original size 112, current size' "$Wc_c"
fi
# ============= filink.c ==============
if test -f 'filink.c' -a X"$1" != X"-c"; then
	echo 'x - skipping filink.c (File already exists)'
else
echo 'x - extracting filink.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'filink.c' &&
/* filink.c - transfer files between unix and an Epson PX-8 */
X
/* This program is in the public domain.  If it doesn't work or causes */
/* you grief in any way, blame the public, not me! */
/* Frank Cringle, August 1994 */
X
/* The best way to compile this is with gcc on a sysv-like machine. */
/* If that is not what you have available, try to get as close as */
/* possible - use the sys5 universe or /usr/5bin/cc or whatever. */
X
/* Version 0.01 */
X
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <ctype.h>
X
struct termios  cookedtio, rawtio;
X
#define GOTTIO	1
#define ISATTY	2
#define ISRAW	4
int             ttyflags;
X
/* put the tty back to normal mode */
static void
ttycooked(void)
{
X	tcflush(0, TCIOFLUSH);
X	if (ttyflags & ISRAW) {
X		tcsetattr(fileno(stdin), TCSAFLUSH, &cookedtio);
X		putc('\n', stdout);
X		ttyflags &= ~ISRAW;
X	}
}
X
/* set up the tty (stdin/stdout) to transfer all 8-bit characters */
static void
ttyraw(void)
{
X	if (!(ttyflags & GOTTIO) && isatty(fileno(stdin))) {
X		ttyflags = ISATTY|GOTTIO;
X		tcflush(0, TCIOFLUSH);
X		if (tcgetattr(fileno(stdin), &cookedtio) != 0) {
X			perror("tcgetattr");
X			exit(1);
X		}
X		rawtio = cookedtio;
X		rawtio.c_iflag = 0;
X		rawtio.c_oflag &= ~(OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET);
X		rawtio.c_lflag = 0;
X		memset(rawtio.c_cc, 0, NCCS);
X		rawtio.c_cc[VMIN] = 0; /* read returns as soon as data */
X				       /* is available */
X		rawtio.c_cc[VTIME] = 1;	/* or after 1 tenth of a */
X					/* second at the latest*/
X	}
X	if ((ttyflags & (ISATTY | ISRAW)) == ISATTY) {
X		tcsetattr(fileno(stdin), TCSAFLUSH, &rawtio);
X		ttyflags |= ISRAW;
X	}
X
X	/* reset to normal before exit. If your C library does not */
X	/* have atexit(), you should install ttycooked as a handler */
X	/* for SIGINT, SIGHUP, etc. */
X	atexit(ttycooked);
}
X
/* print timeout message and quit */
static void
noanswer(int state, int who)
{
X	fprintf(stderr, "%s not responding in state %d\r\n",
X		who == 'R' ? "Receiver" : "Sender", state);
X	exit(1);
}
X
/* send a character with error checking */
static void
send(char c)
{
X	if (write(1, &c, 1) != 1) {
X		perror("stdout");
X		exit(1);
X	}
}
X
/* get a character
X   (give up and return -1 after trys*0.1 secs if trys is positive) */
static int
get(int trys)
{
X	char            c;
X
X	do {
X		if (trys <= 0)
X			trys = -1;
X		switch (read(0, &c, 1)) {
X		case 1: return c & 0xff;
X		case 0: break;
X		default:
X			perror("stdin");
X			exit(1);
X		}
X	} while (--trys);
X	return -1;
}
X
/* send named file */
static void
sendfile(char *name)
{
X	FILE           *f;
X	char            buf[128];
X	int             bufc = 0;
X	char            fn[12];
X	char           *p = name;
X	int             csum = 0, i, state = 1;
X
X	if ((f = fopen(name, "r")) == NULL) {
X		perror(name);
X		return;
X	}
X	/* strip path */
X	if ((p = strrchr(name, '/')) != NULL)
X		p++;
X	else
X		p = name;
X	/* convert name to upper case (max 8 chars) */
X	for (i = 0; i < 8 && *p && *p != '.'; i++) {
X		fn[i] = toupper(*p);
X		p++;
X	}
X	while (i < 8)
X		fn[i++] = ' ';
X	/* skip to extension (if there is one) */
X	while (*p && *p != '.')
X		p++;
X	if (*p == '.')
X		p++;
X	/* copy and convert extension */
X	for (i = 8; i < 11 && *p; i++) {
X		fn[i] = toupper(*p);
X		p++;
X	}
X	while (i < 11)
X		fn[i++] = ' ';
X	fn[11] = 0;
X	p = fn;
X
X	while (1)
X		switch (state) {
X		case 1: send('R');
X			switch (get(50)) {
X			case 'S':
X				state = 2;
X				break;
X			case -1:
X				fputs("Receiver not ready\r\n", stderr);
X			}
X			break;
X		case 2: send('G');
X			state = 3;
X			break;
X		case 3: send(4);
X			switch (get(20)) {
X			case 8: state = 4;
X				break;
X			case -1:
X				noanswer(state, 'R');
X			}
X			break;
X		case 4: if (*p == 0)
X				state = 5;
X			else {
X				send(*p);
X				if ((i = get(20)) == *p)
X					p++;
X				else if (i == -1)
X					noanswer(state, 'R');
X				else
X					state = 3;
X			}
X			break;
X		case 5: send(5);
X			switch (get(20)) {
X			case 9: state = 6;
X				break;
X			case -1:
X				noanswer(state, 'R');
X			default:
X				state = 3;
X			}
X			break;
X		case 6: if (bufc == 0 && feof(f)) {
X				send(3);
X				state = 9;
X			}
X			else {
X				send(2);
X				switch (get(20)) {
X				case -1:
X					noanswer(state, 'R');
X				case 'P':
X					state = 7;
X				}
X			}
X			break;
X		case 7: if (bufc == 0) {
X				memset(buf, 0x1a, 128);
X				fread(buf, 128, 1, f);
X				bufc = 128;
X				csum = 0;
X			}
X			csum ^= buf[128 - bufc];
X			send(buf[128 - bufc--]);
X			state = bufc ? 7 : 8;
X			break;
X		case 8: send(csum);
X			switch (get(20)) {
X			case 'B':
X				state = 6;
X				bufc = 128;
X				break;
X			case 'G':
X				state = 6;
X				bufc = 0;
X				break;
X			case -1:
X				noanswer(state, 'R');
X			default:
X				state = 8;
X				break;
X			}
X			break;
X		case 9: send(19);
X			return;
X		}
}
X
/* receive however many files the other guy wants to send */
static void
receivefile(void)
{
X	FILE           *f = NULL;
X	char            buf[128], name[13], *p = NULL;
X	int             bufc = 0;
X	int             c, csum = 0, i = 0, state = 1;
X
X	while (1)
X		switch (state) {
X		case 1: switch (get(50)) {
X			case 'R':
X				send('S');
X				state = 2;
X				break;
X			case -1:
X				fputs("Sender not ready\r\n", stderr);
X			}
X			break;
X		case 2: switch (get(20)) {
X			case 'G':
X				state = 3;
X				break;
X			case -1:
X				noanswer(state, 'S');
X			}
X			break;
X		case 3: switch (get(20)) {
X			case 4: send(8);
X				p = name;
X				i = 0;
X				state = 4;
X				break;
X			case 19:
X				exit(0);
X			case -1:
X				noanswer(state, 'S');
X			default:
X				send('X');
X			}
X			break;
X		case 4: if ((c = get(20)) == -1)
X				noanswer(state, 'S');
X			/* pick up filename and convert it to */
X			/* lower case */
X			if (c < ' ' || i >= 11) {
X				send('X');
X				state = 3;
X			}
X			else {
X				if (++i == 8)
X					*p++ = '.';
X				if (c != ' ')
X					*p++ = tolower(c & 0x7f);
X				send(c);
X			}
X			if (i == 11) {
X				*p = 0;
X				if (p > name && p[-1] == '.')
X					p[-1] = 0;
X				state = 5;
X			}
X			break;
X		case 5: switch (get(20)) {
X			case 5: if ((f = fopen(name, "w")) == NULL) {
X					perror(name);
X					send('X');
X					state = 3;
X					/* protocol weakness: there is */
X					/* no way to say "this ain't */
X					/* never gonna work" */
X				}
X				else {
X					send(9);
X					state = 6;
X				}
X				break;
X			case -1:
X				noanswer(state, 'S');
X			default:
X				send('X');
X				state = 3;
X			}
X			break;
X		case 6: switch (get(20)) {
X			case 2: send('P');
X				p = buf;
X				bufc = 0;
X				csum = 0;
X				state = 7;
X				break;
X			case 3: fclose(f);
X				state = 3;
X				break;
X			case -1:
X				noanswer(state, 'S');
X			default:
X				send('N');
X			}
X			break;
X		case 7: if ((c = get(20)) == -1)
X				noanswer(state, 'S');
X			*p++ = c;
X			csum ^= c;
X			if (++bufc == 128)
X				state = 8;
X			break;
X		case 8: if ((c = get(20)) == -1)
X				noanswer(state, 'S');
X			if (c != csum)
X				send('B');
X			else {
X				if (fwrite(buf, 128, 1, f) == 0) {
X					perror(name);
X					exit(1);
X				}
X				send('G');
X			}
X			state = 6;
X			break;
X		}
X	return;
}
X
int
main(int argc, char **argv)
{
X	int             i;
X
X	ttyraw();
X	if (argc == 1)
X		receivefile();
X	else
X		for (i = 1; i < argc; i++)
X			sendfile(argv[i]);
X	ttycooked();
X	exit(0);
}
SHAR_EOF
chmod 0664 filink.c ||
echo 'restore of filink.c failed'
Wc_c="`wc -c < 'filink.c'`"
test 7054 -eq "$Wc_c" ||
	echo 'filink.c: original size 7054, current size' "$Wc_c"
fi
exit 0

Frank Cringle			| fdc@cliwe.ping.de
Phone +49 2304 45565		|

