General Machine-Machine Transfer
utzoo!decvax!watmath!rhbartels
utzoo!decvax!watmath!rhbartels
Fri Jun 4 16:25:48 AEST 1982
SYSTEM OUTLINE
by
J. C. Winterton
Machine to Machine Transmission Package
at
Mathematics Faculty Computing Facility
Copyright (c) 1982 by the University of Waterloo
Outline Machine to Machine Transmission March 15, 1982
TABLE OF CONTENTS
1. General............................................. 1
2. Requirements........................................ 1
2.1 Program address space......................... 1
2.2 Simplicity.................................... 1
2.3 Specification................................. 2
3. Capabilities........................................ 2
3.1 Shell......................................... 2
3.2 Multiple Protocols............................ 2
3.3 Unattended Operation.......................... 2
Draft - Subject to Change - ii - University of Waterloo
Outline Machine to Machine Transmission March 15, 1982
Current and projected numbers of computers and work stations in
the University and the existence of local area network systems
accessible to them raises the need for a straightforward program
designed to allow the various machines on campus to communicate with
each other. Communication over the local networks can be either under
control of a terminal attached to one of the machines in a conversa-
tion or of a system daemon running on at least one of the machines.
This document presents the specification for such a program in
outline.
This section discusses the requirements of the program as they
are known and understood at this date.
Since it is expected that the program will usually be run in
restricted address space in a work station or personal micro-computer
running something like CP/M, the program address space is quite
limited. It is doubtful that most machines actually running CP/M will
have more than, say, 64K bytes. Since 8K is taken up by the monitor
and I/O system, there is an upper limit of 56K Bytes on the program.
The program size target should be considerably smaller than this to
allow for imponderables. At this time, the target is 32K Bytes.
It is felt that this will also be a benefit it larger multi-user
machines, but considering the size of some libraries (e.g. TSS Blib)
it may not be possible to meet this target in these cases.
It is not expected that a program will be supplied for each
machine in existence. An implementation will be done for UNIX[1],
TSS, GCOS6 and, perhaps, THOTH. The source code and specification
will be supplied to anyone else wanting to implement this program.
Because of this stand, it is necessary that simplicity of algorithms
be of higher priority than efficiency so that relatively
unsophisticated users may successfully implement the program. Hope-
fully, like music by J.S.Bach, this program will be writeable and
compilable in whatever reasonable language is available on the target
machine. [2]
__________________
[1] UNIX is a Trademark of Bell Laboratories.
[2] Bach wrote non-specifically and most of his works can be
played on whatever instruments are available.
Draft - Subject to Change - 1 - University of Waterloo
Outline Machine to Machine Transmission March 15, 1982
Where data are to be encoded, they will be converted from 8 bit
representation to hexadecimal. Data compression is provided whenever
two implimentations of this program are in communication.
The specification of this program must be as complete as
possible. Pseudo code or some similar representation shall be used
where logic is complex. At least one of the implementations prepared
by us shall be wholly from this specification.
The following are the capabilities that are foreseen an necessary
in the program.
The term shell is used in the UNIX sense. The program acts as a
link between the user and the network session. It is able to accept
commands to be executed:
(a) By the host machine;
(b) By the network (if possible); and
(c) By the remote host machine.
The host/remote host commands in the repertoire of the program
include redirection commands to facilitate file transfers.
Multiple protocols in the style of the UNIX uucp system are not
envisaged. Where two copies of the program are able to co-operate,
8-bit data may be encoded and sent. The program should be built to
handle only a seven bit data path. Where only one copy of the program
is active in the circuit, data may very well be restricted to ascii
graphics.
Unattended operation of the program is desirable where it is
under the control of a daemon such as the UNIX cron. It should be
possible to set up a list of machines to be polled for work on a
"spooled" basis so that lengthy transmissions may be accomplished
without the need for an "operator" at a terminal controlling the
program. This is on all fours with the present UNIX uucp capability
which has served as a model for much of this design.
Draft - Subject to Change - 2 - University of Waterloo
---------------------------------------------------------------------------
Brief On-Line Documentation
---------------------------------------------------------------------------
MMX - machine to machine transfer program.
Syntax:
MMX [SLave] [l=linename] [s=baudrate]
Description:
This C program transfers files over a communication
line in a secure manner. Checksumming and other controls
are present as well as data compression. A 7-bit data path
is assumed, so that the use of "transparent" or "raw" modes
on communication lines is avoided.
The program operates in two modes: MASTER and SLAVE.
Operation in the MASTER mode implies that commands will be
given from the standard input device and that communication
with the network will be over a separate device. SLAVE mode
implies that the communications network is identical with
the standard input/output device. Unless and until the host
system has the ability to attach separate communications
lines to programs, the program must be operated in SLAVE
mode.
Currently, Honeywell TSS cannot attach separate
communication lines, and therefore, it is necessary to use
SLAVE mode on that machine. The Math UNIX machine, on the
other hand, is perfectly capable of operating in either
mode.
Options:
SLave
Causes the program to issue internal !SLave and !Open
command on start up.
l=linename
In machines that possess the capability, this
parameter sets a string to be used by the open
command for the separate communication line.
s=baudrate
In machines that possess the capability, this
parameter allows the program to set the baud rate of
the line.
Defaults:
Defaults are set in each machine for the options. In
the current version for Honeywell TSS, SLave is forced
during startup. In the math UNIX machine the defaults are:
MAster, l=/dev/sytek, s=2400.
Program Operation:
When called, the program operates as a minor "shell".
The initiating end of the conversation operates as the
master (in MASTER mode) and the responding end as SLAVE. It
accepts commands and acts upon them as described below.
Commands are prefixed by the command escape character
exclamation-point (!). When an exclamation-point is
recognized at the beginning of a line, the line is parsed
and validated commands obeyed. When an error occurs, a
descriptive error message appears. When the second
character of a command line is also a command escape, then
the line is sent to the other program stripped of the first
escape (!). There is no escape sequence to issue system
commands from within this program.
Commands:
Open
Condition the communication line as necessary. This
command must be issued to both copies of the program
before any transfer can take place. Note that
calling the program with the SLave option performs
this function automatically for the standard
input/output device.
Send localfile remotefile
Transmit file localfile from the master to remotefile
at the slave. Correct pathnames and formats for
pathnames are the responsibility of the user.
Get remotefile localfile
Transmit the file remotefile from the slave to
localfile at the master. Pathnames are the full
responsibility of the user.
MaxData
Sets the maximum number of data bytes from the
sending file that will be passed in any one packet
during file transmission.
It is necessary to limit this artificially due to the
fact that some systems are line oriented and have
limited line sizes (Honeywell TSS). MaxData
represents a count of bytes that will be taken from
the file being sent. Since a four bit encoding
techinique is used, each eight bit byte is
transmitted as two graphic characters. This expan-
sion is alleviated somewhat by also employing data
compression techinques where the number of identical
characters exceeds two. There are ten (10) overhead
characters on a data packet in addition to the
encoded bytes. Default is 60 bytes.
Quit
Stops the program. In the UNIX environment, a
control-D (EOT, D) may be sent as a command to effect
a wrapup of the program, but the command is
available.
MAster
If the program is in slave state, switch to master
state.
SLave
If the program is in master state, switch to slave
state.
Remote
Inform the program of the type of system that is at
the other end of the conversation. This feature has
not been useful to date, and may be removed.
Viewsetup
Display the controlling parameters of the program.
Continue
Because many systems do not prompt, end of transmis-
sion over the communication line is detected by a
time out heuristic. The command is provided to cause
the program to re-examine the communication line
buffers where it is believed that more data may be
present. This is useful when system are heavily
loaded and multiple line output may be sent with more
than four seconds between data bursts.
Zdebug
Some versions of this program (notably the UNIX ver-
sion) contain debugging code activated by the command
"!Zdebug on" and deactivated by "!Zdebug off". This
code may be removed when not required or impractical.
(Copyright (c) 1982, University of Waterloo)
------------------------------------------------------------
DATA STRUCTURES FOR FILE TRANSMISSION
by
J. C. Winterton
Machine to Machine Transmission Package
at
Mathematics Faculty Computing Facility
Copyright (c) 1982 by the University of Waterloo
Formats Machine to Machine Transmission April 29, 1982
TABLE OF CONTENTS
1. General..................................................1
2. Data Packet..............................................1
2.1 Data Compression Scheme............................1
2.2 Processing the Data packet.........................2
3. Beginning of File Packet.................................2
3.1 Beginning of File Packet Processing................2
4. End of File Packet.......................................3
4.1 End of File Packet Processing......................3
5. Hand Shake Packet........................................3
6. Warm Feelings............................................4
Draft - Subject to Change - ii - University of Waterloo
Formats Machine to Machine Transmission April 29, 1982
1. General.
This document contains the data structures necessary to operate
the Machine to Machine Transmission program. All of these structures
are effectively character arrays (strings). In this design it is
considered that only a 7-bit data path is available.
When files are being transmitted, all data is packetized in the
format shown below. Two other packet structures are necessary, namely
a beginning of file packet and an end of file packet. An additional
packet is defined for the activation/deactivation of data compression.
For convenience of description, PL/I is used as the description
language. Because it is believed that many of the operating systems
involved consider the ASCII control characters as proprietary to
themselves, use of these characters has been avoided.
2. Data Packet.
The data packet shown below is used to transfer
dcl 1 data_packet unaligned,
2 d_soh char init ('|'),
2 check_sum char(4), /* modulo 10000 */
2 data_length char(3), /* <=250 */
2 d_stx char init ('{'),
2 data_bytes(data_length) char, /* data characters */
2 data_terminator char;
/* '~' if incomplete "record" */
/* '}' if end of "record" */
2.1 Data Compression Scheme.
Data compression is a feature of this protocol. The technique
employed is:
Scan the uncompressed data for occurances of three or more
occurances of the same character. When such are found, the
packet encoding routine translates them into GS-count-hh (4 ASCII
characters).[1]
Count is the binary number of characters to be propagated
expressed as an ASCII graphic character by adding a space
('0100000'b). This technique limits the compression range to
'0000011'b through '0011010'b (3 through 26) in order to avoid
entering control characters and key signal characters improperly
in the text.
hh is the hexadecimal representation of the character being
compressed.
___________________________
[1] GS is the ASCII character '!'.
Draft - Subject to Change - 1 - University of Waterloo
Formats Machine to Machine Transmission April 29, 1982
This scheme will only be invoked when co-operating versions of
the program are in contact. If the identity handshake fails when
transmission begins, the data are transmitted uncompressed.
2.2 Processing the Data packet.
The data of Figure 1 are encoded in ASCII hexadecimal notation,
two characters per byte for transmission. On transmission, the final
byte pair is followed by either an ASCII '}' or '~' character [2] and,
if necessary, a new-line character (\n) as appropriate to the host
system. In most cases, the '}' will be used as a packet terminator.
However, when it is considered important to pass data in "blocks" or
"records", '~' is used at the end of each packet of a block until the
last, when '}' is used to indicate to the receiving program that an
output write may now be accomplished. [3] The receiving program
decodes the packet, and if found correct an ASCII 'y' character and a
newline are returned. On receipt of the 'y', the sending program
sends the next packet. If an error is detected, the receiving program
returns a sequence consisting of an ASCII 'n' followed by a newline
character. On receipt of the 'n', the sending program will retransmit
the packet. The sending program shall attempt to send each packet a
minimum of five (5) times. If receipt of the packet is not possible
after the retry limit, the programs should wrap up gracefully.
3. Beginning of File Packet.
When it is desired to transmit a file between two machines
running this program, the fact is announced by the sending machine
with the transmission of a Beginning of File Packet Figure 2.
3.1 Beginning of File Packet Processing.
This packet is not encoded, but sent as plain ASCII text followed
by a newline character. Pathnames are the resonsibility of the
sending program. Retry processing is the same for this packet as for
a data packet with the following exceptions:
(a) The sending program will open the file to be sent
(send_pathname) before sending this packet. If the file cannot
be opened, appropriate error action shall be taken.
(b) On receipt of this packet, in addition to checksumming, the
receiving program will attempt to open/create the receiving file.
An 'n' shall be sent if this fails.
___________________________
[2] This restricts the operation of Hazeltine 2000 series
equipment to having the escape character set to the ASCII
ESC ('0011011'b).
[3] A record is an arbitrary division of text which may be
considered to be sent as a unit. In some computer systems,
it may be a logical record.
Draft - Subject to Change - 2 - University of Waterloo
Formats Machine to Machine Transmission April 29, 1982
dcl 1 beginning_of_file_packet unaligned,
2 bof_indicator char(5) init ('|FILE'),
2 bof_direction char(1),
/* 1 = slave_to_master */
/* 2 = master_to_slave */
2 bof_checksum char(4), /* modulo 10000 */
2 bof_stx char init ('{'),
2 send_pathname_size char(3), /* length of send-pathname */
2 receive_pathname_size char(3),
/* length of receive_pathname */
2 send_pathname(send_pathname_size) char,
/* send_pathname */
2 receive_pathname(receive_pathname_size) char,
/* receive_pathname */
2 bof_terminator char init ('}');
Figure 2. Beginning of File Packet
4. End of File Packet.
When all the data of a file has been transferred, the sending
program sends an End of File Packet Figure 3.
dcl 1 end_of_file_packet unaligned,
2 eof_heading char(4) ('|EOF'),
2 eof_stx char init ('{'),
2 eof_length char (3), /* length of next field */
2 eof_sending_name(eof_length) char,
/* send_pathname from bof packet */
2 eof_terminator char init ('}');
Figure 3. End of File Packet
4.1 End of File Packet Processing.
This packet is sent in plain ASCII text followed by a newline
with the retry protocol of the data packet. Once the processing of
this packet is completed, the files are closed at both ends and the
program returns to manual or shell mode to accept new commands.
5. Hand Shake Packet.
This packet is sent by the sending program before the start of a
file transfer. If it is returned by the receiving program, the data
compression algorithm is activated. If the packet is acknowledged
improperly, data compression is disengaged for the transfer.
Draft - Subject to Change - 3 - University of Waterloo
Formats Machine to Machine Transmission April 29, 1982
dcl 1 hand_shake_packet unaligned,
2 hs_soh char init ('|'),
2 hs_dum char(12) init ('!$Compress$!'),
2 hs_stx char init ('{'),
2 hs_etx char init ('}');
Figure 4. Hand Shake Packet
6. Warm Feelings.
Since this program is probably operated under control of a
terminal attached to an operator, it is sensible to send out some kind
of reassuring message every five or six seconds. It is suggested that
this "warm feelings" message should be brief. It has been suggested
that the output could be the number of characters or packets sent. If
it is known that the operator terminal is a CRT with addressable
cursor, an update in place would be desireable.
Draft - Subject to Change - 4 - University of Waterloo
---------------------------------------------------------------------------
HERE COME DE CODE
---------------------------------------------------------------------------
/* This program was developed by John C. Winterton at the University of
Waterloo using UNIX 4.1BSD under an educational licence. The program
is made available without support or warranty of any kind implicit
or explicit.
The purpose of the program is to communicate over a seven bit data
path with a similarly working copy of itself for the purpose of securely
transfering files from machine to machine. This version contains specific
code for the version of the UNIX operating system on which it was
developed. It further contains some code aimed at communicating with
a Honeywell TSS machine.
The subroutine _abbrv is the work of Kevin P. Martin, and works well
indeed. The other stuff is the author's, who takes the blame but
but no responsibility. */
/* manifests for the mmx package */
#ifdef unix
#include <stdio.h>
#include <sgtty.h>
#include <ascii.h>
#define SPEEDEFAULT B2400 /* default baud rate */
#define LINEDEFAULT "/dev/sytek" /* default comm line file */
#define LINETTY "/dev/tty" /* name of login tty */
#endif
#define BUFFSIZE 1024 /* length of keyboard buffers */
#define MAXSTRING 1024 /* length of general strings */
#define PACKSIZE 1024 /* length of packet buffers */
#define DEF_LENGTH 60 /* default data length in pkts */
#define CL 017 /* four bit mask for lower char */
#define CU 0360 /* four bit mask for upper char */
#define CM 0177 /* 7 bit mask for input char */
#define HEXUPPER 0x30 /* hex encodeing upper bits */
#define HEXFIRST HEXUPPER /* '0' */
#define HEXLAST 0x3f /* '?' */
#define HEXERROR -2 /* error signal for conversion */
#define PROGESC '!' /* escape character for commands*/
#define DISCONNECTED 0 /* state of communication line */
#define CONNECTED 1
#define MASTERPROMPT "\n>" /* prompt for master state */
#define SLAVEPROMPT "?" /* prompt for slave state */
/* manifests for ourcmd's line parsing */
#define STRING 0
#define OPENLINE 1
#define SENDFILE 2
#define GETFILE 3
#define MAXLINE 4
#define FINISHED 5
#define MASTER 6
#define SLAVE 7
#define REMOTE 8
#define VIEWSETUP 9
#define CONTINUE 10
#define ZDEBUG 11
/* manifests for the promptable (system names) */
#define SYTEK 1
#define LOCALNET 2
#define UNIX 3
#define GCOS 4
#define MOD400 5
#define THOTH 6
/* manifests for packet transmission (special chars) */
#define X_SOH '|' /* pseudo start of header */
#define X_STX '{' /* pseudo start of text */
#define X_ETX '}' /* pseudo end of text */
#define X_ETB '~' /* pseudo end of text block */
#define X_GS '!' /* pseudo group separator (comprssion */
/* manifests for character offsets into decoded packet blocks */
/* data packet */
#define D_SOH 0
#define D_CKSUM 1
#define D_DLENGTH 5
#define D_STX 8
#define D_BYTES 9
/* begin file packet */
#define F_SOH 0
#define F_FILE 1
#define F_DIRECTION 5
#define F_CKSUM 6
#define F_STX 10
#define F_SENDLENGTH 11
#define F_RECVLENGTH 14
#define F_SENDPATH 17
/* end file packet */
#define E_SOH 0
#define E_EOF 1
#define E_STX 4
#define E_EOFLENGTH 5
#define E_SENDPATH 8
/* values for F_DIRECTION */
#define TO_MASTER '1'
#define TO_SLAVE '2'
/* for direction of motion in the data packet processor */
#define NONE 0
#define IN 1
#define OUT 2
#define ERR_LIMIT 5 /* transmission error limit */
#define COM_LIMIT 26 /* maximum number of chars for compression */
#define WARM_FEEL 10 /* number of packets to pass before output
of warm feelings indicator to MASTER */
/*
* _abbrv( pat, str )
*
* Perform match against string pattern.
* The pattern characters which are lowercase are optional.
* This routine recurs to a depth of n, where n is the number of optional
* characters provided in the match string.
*
*/
int _abbrv( pat, str )
char *str, *pat;
{
char c;
/*
* Neither reqd nor patc is used without setting after the recursion,
* so they can be static to save stack space.
*/
static int reqd;
static char patc;
for(;;) { /* each target character */
/*
* Pick up the next character to be matched.
* and uppercase it.
*/
c = *(str++);
if( c >= 'a' && c <= 'z' )
c &= ~040;
for(;;) { /* each pattern character */
patc = *(pat++);
/*
* Is it required (non-lowercase, including null) ?
*/
reqd = patc < 'a' || patc > 'z';
/*
* Compare uppercased pattern char with uppercased
* target char.
*/
if( (reqd ? patc : patc & ~040) == c ) {
/*
* We have a hit. If the character was required,
* continue along the target string.
*/
if( reqd )
break;
/*
* Otherwise, recursively look down the string.
* If the remainder of the string matches the
* remainder of the pattern, all is matched.
*/
if( _abbrv( pat, str ) )
return( 1 );
/*
* If the remainder of the string and the rest
* of the pattern did not match, try skipping
* this (optional) pattern character,
* and find the next.
*/
}
else {
/*
* Didn't match. If it was required, fail.
* else try next pattern character.
*/
if( reqd )
return( 0 );
}
/*
* Loop back to next pattern character (on same
* target character).
*/
}
/*
* We matched a required character... Was it a null?
* (end of pattern)
*/
if( c == '\0' )
return( 1 );
/*
* Loop back to next target string character and next pattern
* character.
*/
}
}
/* convert input 8-bit character to hexadecimal */
#include "manifs.h"
char *a_to_hex(cin, cout)
char cin, *cout;
{
cout[0] = ((cin >> 4) & CL) | HEXUPPER;
cout[1] = (cin & CL) | HEXUPPER;
return (cout);
}
/* output the appropriate ack */
#include "manifs.h"
int ack()
{
sendline("y\n",2);
return(1);
}
/* NON-PORTABLE - UCB UNIX 4.X */
/* this routine uses an ioctl to see if there are any characters in the
input stream.
It returns the number of chars if there are any. */
#include "manifs.h"
long ccount(stream)
FILE *stream;
{
extern char prtab[];
extern int prompt, data_present;
long nchars;
unsigned sleeptime;
ioctl(fileno(stream), FIONREAD, &nchars);
nchars += (long) stream->_cnt;
while (nchars == 0)
{
if (prtab[prompt] != DEL)
return(nchars);
for (sleeptime = 1; sleeptime <= 16; sleeptime *= 2)
{
ioctl(fileno(stream), FIONREAD, &nchars);
nchars += (long) stream->_cnt;
if (nchars != 0)
return(nchars);
if (sleeptime > 2 && data_present)
return(nchars);
if (sleeptime == 8)
message ("Wait");
if (sleeptime > 8)
message (".");
sleep(sleeptime);
}
break;
}
return(nchars);
}
/* checksum from a given X_STX to either a X_ETX or X_ETB exclusive */
#include "manifs.h"
unsigned checksum(p)
char *p;
{ /* p->X_STX */
int c, i;
unsigned sum;
if (p[0] != X_STX)
return (0); /* error */
sum = 0;
for (i = 1; ; ++i)
{
c = (p[i]) & CM;
switch(c)
{
default:
if (c < ' ' || c > '~')
return(0); /* non-packet char */
sum += c;
continue;
case X_ETX:
case X_ETB:
return(sum);
}
}
}
/* process incoming packet in indicated buffer */
#include "manifs.h"
int dopacket(ptr, len)
char ptr[];
int len;
{
extern char handshake[];
extern int errcount, compress;
char c;
int i;
c = ptr[1]; /* get first char of header */
switch(c) /* and identify packet */
{
case '!':
i = strncmp(ptr, handshake, strlen(handshake) - 1);
if (i == 0)
{
compress = 1;
return(ack());
}
message("Compression request garbled - quitting.\n");
nack();
return(2); /* transfer request fails */
case 'F':
if (filepacket(ptr, len))
return(ack());
return(nack());
case 'E':
if(eofpacket(ptr, len))
{
ack();
return(2);
}
return(nack());
default:
if (c >= '0' && c <= '9')
if(xferpacket( ptr, len))
{
errcount = 0;
return(ack());
}
if ( (++errcount) > ERR_LIMIT )
{
message ("Too many errors - quitting.\n");
nack();
return(2);
}
return(nack());
}
}
/* process incoming eof packet */
#include "manifs.h"
int eofpacket(ptr, len)
char ptr[];
int len;
{
extern FILE *xfer_file;
extern int filecmd;
filecmd = NONE;
fclose(xfer_file);
return(1);
}
/* NON PORTABLE - UCB UNIX 4.X */
/* externals for mmx program */
#include "manifs.h"
FILE *in_line = 0; /* comm line input unit */
FILE *out_line = 0; /* comm line output unit */
FILE *xfer_file; /* for current file transfer */
int filecmd = NONE; /* direction indicator for xfer */
struct sgttyb com_line; /* comm line parameters from gtty */
struct sgttyb com_save; /* copy of com_line for wrapup */
struct tchars com_1; /* from ioctl for signals */
struct tchars com_1s; /* copy of com_1 for wrapup */
struct ltchars com_2; /* from ioctl for other signals */
struct ltchars com_2s; /* copy of com_2 for wrapup */
unsigned zdebug = 0; /* debug control */
int on_line = 0; /* flag set by setup if /dev/tty */
/* really is a terminal */
char syntax[] = {
"Use:mmx [slave] [l=linename] [s=baudrate]\n"
};
char fileformat[] = /* constant used in inwards */
{"%03d%03d%s%s%c"};
char callname[20]; /* name for the message routine */
char handshake[] = {
"|!$Compress$!{}\n" /* data compression handshake */
};
char linebuf[BUFFSIZE]; /* keyboard input buffer */
char packet[PACKSIZE]; /* data packet */
int eofsw = 0; /* additional control for loadline */
char file_local[MAXSTRING]; /* name of local transfer file */
char file_remote[MAXSTRING]; /* name of remote tranfer file */
/* Table of _abbrv patterns for the main ourcmd processor */
/* note: The zeroth and last entry of all tables of this type
are set to null strings for the benefit of the identify()
routine */
char *optab[] = {
"", /* zero'th one is always null */
"Open", /* open the line */
"Send", /* send infile outfile */
"Get", /* get infile outfile */
"MaxData", /* maxdata nnn */
"Quit", /* quit */
"MAster", /* enter master state (default) */
"SLave", /* enter slave state */
"Remote", /* request new remote set up */
"Viewsetup", /* display remote set up */
"Continue", /* read the comm_line send nothing */
"Zdebug", /* zdebug on|off */
"" /* last one is always null */
};
/* This collection of variables contains the program state */
int progstate = MASTER; /* current state of things */
int linestate = DISCONNECTED; /* current state of comm line */
int speed; /* line speed */
int errcount = 0; /* error count for packet xmit */
int compress = 0; /* data compression enable switch */
int maxline = DEF_LENGTH; /* default maximum transmission */
int newline = SYTEK; /* settable newline for outgoing */
int prompt = SYTEK; /* termination style of input */
int remote = SYTEK; /* comm line device type */
int data_present = 0; /* for readline routine */
char line_name[MAXSTRING]; /* comm line name string */
char line_save[MAXSTRING]; /* copy of line_name (MASTER) */
char in_prompt[5] /* prompt string at terminal */
= {MASTERPROMPT}; /* default for starting up */
/* The entries in the following three tables bear a one-to-one
relation to each other as follows:
prtable is the table of known system names for _abbrv
processing by the !remote request;
prtab entries indicate the prompt character for the
corresponding system;
nltab entries contain the chracter that the relevant system
accepts as a <newline> or <end transmission> */
char *prtable[] = {
"",
"Sytek",
"Localnet",
"Unix",
"Gcos8",
"Mod400",
"Thoth",
""
};
/* note: in prtab an entry of DEL (\0177) indicates a system with
no particular known prompt. Serviced by timeout */
char prtab[] = {
NUL,
DEL, /* sytek */
DEL, /* localnet */
DEL, /* unix */
DEL, /* control-q for the bun */
DEL, /* Mod400 (GCOS6) */
DEL, /* thoth(?) */
NUL
};
char nltab[] = {
NUL,
CR, /* sytek */
CR, /* localnet */
CR, /* unix */
CR, /* gcos8 */
CR, /* Mod400 */
CR, /* thoth(?) */
NUL
};
/* routine to packetize and send an input file */
#include "manifs.h"
int fileout()
{
extern FILE *xfer_file;
extern char packet[], linebuf[], file_local[];
extern int compress, maxline, errcount, progstate;
int lx, c, pkt_cnt;
c = lx = pkt_cnt = 0;
while ( (c = getc(xfer_file)) != EOF)
{
linebuf[lx++] = c;
if (lx < maxline)
continue;
outdata(linebuf, lx);
if (errcount >= ERR_LIMIT)
return(0);
if ( (progstate == MASTER) &&
( (++pkt_cnt % WARM_FEEL) == 0) )
message(".");
lx = 0;
}
if (lx)
{
outdata(linebuf, lx);
if (errcount >= ERR_LIMIT)
return(0);
}
sprintf(packet, "|EOF{%03d%s}", strlen(file_local), file_local);
for (errcount = 0; errcount < ERR_LIMIT; errcount++)
if (sendpacket(packet))
break;
return(errcount < ERR_LIMIT);
}
/* process an incoming file header packet */
#include "manifs.h"
int filepacket(ptr, len)
char ptr[];
int len;
{
extern FILE *xfer_file;
extern char file_local[];
extern int progstate, filecmd;
extern unsigned checksum();
char filename[160];
int i, s_length, r_length, limit;
unsigned cksum, value;
char *pick;
value = checksum(&(ptr[F_STX]));
i = sscanf(ptr, "|FILE%*c%4d{%3d%3d", &cksum, &s_length, &r_length);
if (i < 3)
return(0); /* error on packet */
if (value != cksum)
return(0);
switch(progstate)
{
case MASTER:
switch (ptr[F_DIRECTION])
{
case TO_MASTER:
pick = &(ptr[F_SENDPATH + s_length]);
limit = r_length;
break;
case TO_SLAVE:
pick = &(ptr[F_SENDPATH]);
limit = s_length;
break;
default:
return(0);
}
break;
case SLAVE:
switch (ptr[F_DIRECTION])
{
case TO_SLAVE:
pick = &(ptr[F_SENDPATH + s_length]);
limit = r_length;
break;
case TO_MASTER:
pick = &(ptr[F_SENDPATH]);
limit = s_length;
break;
default:
return(0);
}
break;
default:
message("Program in an impossible state in filepacket\n");
message("Dumping");
kill(getpid(), 9);
}
for (i = 0; i < limit; ++i)
filename[i] = pick[i];
filename[i] = NUL;
filecmd = IN;
if (progstate == MASTER)
if (ptr[F_DIRECTION] == TO_MASTER)
{
filecmd = OUT;
xfer_file = fopen(filename, "w");
}
else
xfer_file = fopen(filename, "r");
else /* state is SLAVE */
if (ptr[F_DIRECTION] == TO_SLAVE)
{
filecmd = OUT;
xfer_file = fopen(filename, "w");
}
else
xfer_file = fopen(filename, "r");
if (xfer_file == NULL)
{
filecmd = NONE;
return(0);
}
strcpy (file_local, filename);
return(1);
}
/* convert a pair of hexadecimal coded input chars to an 8 bit char */
#include "manifs.h"
int hex_to_a(cin)
char *cin;
{
register int i, cout;
register char c;
cout = 0; /* insurance */
for (i = 0; i < 2; ++i)
{
c = cin[i];
if (HEXFIRST <= c && c <= HEXLAST)
cout = ( ( cout << 4 ) & CU ) | (c & CL);
else
return(HEXERROR);
}
return (cout);
}
/* This routine identifies the presented argument
againts the table of acceptable commands */
#include "manifs.h"
int identify(table,string)
char *table[];
char string[];
{
int i;
for (i = 1; table[i][0] != NUL; ++i)
if ((_abbrv(table[i], string) == 1))
return (i);
return (0);
}
/* This routine tests stdin and returns 1 if it is a tty
otherwise it returns 0 */
int ifonline ()
{
extern int on_line;
return (on_line);
}
/* manage an inbound file transfer /*
/* when called, localname has been opened on xfer_file and
compression handshaking is complete. This routine formats and
sends out the file header packet with verification then turns
control over to the inbound packet processor */
#include "manifs.h"
int inwards(remotename, localname)
char remotename[], localname[];
{
extern char packet[], *rawfgets(), fileformat[];
extern int progstate;
extern unsigned checksum();
int err_cnt, l_length, r_length, i, q;
unsigned cksum;
char work[BUFFSIZE], *p;
strcpy (packet, "|FILE");
packet[F_DIRECTION] = (progstate == MASTER ? TO_MASTER : TO_SLAVE);
packet[F_STX] = X_STX;
l_length = strlen(localname);
r_length = strlen(remotename);
if (progstate == MASTER)
sprintf(work, fileformat,
r_length, l_length, remotename, localname, X_ETX);
else
sprintf(work, fileformat,
l_length, r_length, localname, remotename, X_ETX);
p = &(packet[F_SENDLENGTH]);
q = strlen(work);
for (i = 0; i < q; ++i)
p[i] = work[i];
cksum = checksum(&(packet[F_STX]));
if (cksum == 0)
{
message ("Error calculating checksum in inwards routine\n");
kill (getpid(), 9); /* program bug if happens */
}
sprintf(work, "%04d", cksum % 10000);
p = &(packet[F_CKSUM]);
for (i = 0; i < 4; ++i)
p[i] = work[i];
for (err_cnt = 0; err_cnt < ERR_LIMIT; ++err_cnt)
if (sendpacket(packet))
break;
if (err_cnt >= ERR_LIMIT)
{
message ("Error limit reached on file packet\n");
return(0);
}
if (!procpack())
message("Get: File transfer failed\n");
else
message("Get: Completed\n");
return(1);
}
/* parse the command option l=string for the comm line */
int linearg(string)
char *string;
{
extern char line_name[], line_save[];
if (string[1] != '=')
{
message ("%s not understood\n", string);
return (1);
}
strcpy(line_name, &string[2]); /* used here */
strcpy(line_save, line_name); /* remember here */
return(0);
}
/* list options for a verb when they consist of another table */
#include "manifs.h"
int listopt(string, table)
char string[], *table[];
{
int i;
message ("Options for the %s command are:\n", string);
for (i = 1; table[i][0] != NUL; ++i)
message ("%s ", table[i]);
message ("\n");
return(1);
}
/* load a line into a string from the stdin file */
#include "manifs.h"
int loadline(string, limit)
char string[];
int limit;
{
extern int progstate, eofsw, progstate, linestate;
extern char in_prompt[];
extern char *rawfgets();
char *ret;
if (eofsw > 0)
return (EOF);
message ("%s", in_prompt); /* prompt */
if (progstate == SLAVE && linestate == CONNECTED)
ret = rawfgets(string, limit, stdin);
else
ret = fgets(string, limit, stdin);
if (ret == NULL)
{
eofsw = 1;
return (EOF);
}
return ( strlen(string) );
}
/* NON PORTABLE UCB UNIX 4.X */
/* Set up input and output units for the comm line */
#include "manifs.h"
int lopen()
{
extern int linestate;
extern int speed;
extern char line_name[];
extern FILE *in_line, *out_line;
extern struct sgttyb com_line;
extern struct sgttyb com_save;
extern struct tchars com_1;
extern struct tchars com_1s;
extern struct ltchars com_2;
extern struct ltchars com_2s;
int stat, stat_too;
int ldiscipline = NTTYDISC;
int not_the_tty;
not_the_tty = ( strcmp (line_name, LINETTY) ); /* zero if terminal */
if (linestate == CONNECTED)
{
message ("Line is already open\n");
return(1);
}
if (not_the_tty)
{
in_line = fopen (line_name, "r"); /* open for read */
out_line = fopen (line_name, "w"); /* open for write */
if (in_line == NULL || out_line == NULL )
{
message ("Could not open line %s\n", line_name);
return(0);
}
}
else
{
in_line = stdin;
out_line = stdout;
}
/* set new tty */
stat = ioctl(fileno(in_line), TIOCSETD, &ldiscipline);
if (stat < 0)
{
message ("Couldn't set new tty discipline on line %s\n",
line_name);
return(0);
}
/* read line parameters */
stat = ioctl(fileno(in_line), TIOCGETP, &com_line);
stat_too = ioctl(fileno(in_line), TIOCGETP, &com_save);
if (stat < 0 || stat_too < 0)
{
message ("Open routine failed reading line parameters\n");
return(0);
}
ioctl (fileno(in_line), TIOCGETC, &com_1);
ioctl (fileno(in_line), TIOCGETC, &com_1s);
ioctl (fileno(in_line), TIOCGLTC, &com_2);
ioctl (fileno(in_line), TIOCGLTC, &com_2s);
com_1.t_intrc = com_1.t_quitc = com_1.t_eofc = -1;
ioctl(fileno(in_line), TIOCSETC, &com_1);
com_2.t_suspc = com_2.t_dsuspc = com_2.t_rprntc =
com_2.t_flushc = com_2.t_werasc = com_2.t_lnextc = -1;
ioctl(fileno(in_line), TIOCSLTC, &com_2);
if (not_the_tty)
com_line.sg_ispeed = com_line.sg_ospeed = speed;
com_line.sg_erase = '\010'; /* erase char is backspace */
com_line.sg_kill = '\030'; /* kill char is ctrl-x */
/* set line controls for char by char */
com_line.sg_flags = CBREAK | TANDEM;
stat = ioctl(fileno(in_line), TIOCSETP, &com_line); /* set it up */
if (stat < 0)
{
message ("Failed to set up line properly\n");
return(0);
}
/* set exclusive use */
stat = ioctl(fileno(in_line), TIOCEXCL, NULL);
if (stat < 0)
{
message ("Failed to set exclusive use on line\n");
return(0);
}
/* set hangup on close */
stat = ioctl(fileno(in_line), TIOCHPCL, NULL);
if (stat < 0)
{
message("Failed to set hang up on close on line\n");
return(0);
}
linestate = CONNECTED;
message("Line connected\n");
return(1);
}
/* general machine to machine file transfer shell program */
#include "manifs.h"
int main (argc, argv)
int argc;
char *argv[];
{
extern char linebuf[], callname[];
extern int progstate;
int status, i;
char c;
status = setup (argc, argv);
if (status == 0)
{
message ("%s: Setup fails\n", callname);
exit(-1);
}
message ("%s: Setup complete\nType !v for program status\n",
callname);
for (status = 1; status != 0; )
switch (status) {
case 1:
status = process(loadline(linebuf, BUFFSIZE));
continue;
case 2:
/* note - anti-reflection code */
i = readline(linebuf, BUFFSIZE);
c = linebuf[0];
if ( ( (progstate == SLAVE) && (c == X_SOH) )
|| (c == PROGESC) )
status = process(i);
else
status = 1;
continue;
default:
message ("Illegal status in main %d\n",
status);
wrapup();
exit(-1);
}
exit(0);
/* note on anti-reflection:
The case 2 above must be left this way. If process is
called directly (e.g. status = process(readline ...))
then the last line input will be sent to the other device
ad nauseam. This code checks for things that CAN be
handled by process(). It is left to the maintainer to
fathom why it was done this way.
*/
}
/* This routine is not portable, and is used to hide printf */
/* do a printf and return the value 1 */
#include "manifs.h"
int message(format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
char *format;
int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;
{
fprintf (stdout, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
fflush(stdout);
return (1);
}
/* make a data packet obeying compression rules
input is in in_char for length in_len chars
output is ready to send packet in out_pack */
#include "manifs.h"
mkdatapacket(in_char, out_pack, in_len)
char in_char[], out_pack[];
int in_len;
{
extern int compress;
char *cp, pair[2], *a_to_hex();
int wx, px, ix, count;
unsigned cksum;
cksum = 0;
for (ix = 0; ix < in_len; ++ix)
cksum += in_char[ix];
cksum %= 10000;
sprintf(out_pack, "|%04d%03d{", cksum, in_len);
cp = &(out_pack[D_BYTES]);
px = ix = 0;
while (ix < in_len)
{
if (compress && ((in_len - ix) > 2))
{
wx = ix; /* in case of backout */
while ((in_char[wx] == in_char[++ix]) && ix < in_len)
;
count = ix - wx;
if (count > COM_LIMIT)
{
ix -= (count - COM_LIMIT);
count = COM_LIMIT;
}
if (count > 2)
{
cp[px++] = X_GS;
cp[px++] = (count | 0x40) & CM;
a_to_hex(in_char[wx], pair);
cp[px++] = pair[0];
cp[px++] = pair[1];
continue;
}
else
ix = wx; /* back off */
}
a_to_hex(in_char[ix++], pair);
cp[px++] = pair[0];
cp[px++] = pair[1];
}
cp[px] = X_ETX;
}
/* output the appropriate nack */
#include "manifs.h"
int nack()
{
sendline("n\n",2);
return(0);
}
/* process a command line from the stdin device */
#include "manifs.h"
int ourcmd(linebuf, inlength)
char linebuf[];
int inlength;
{
extern char *optab[], *prtable[];
extern int linestate, progstate;
extern char line_name[], line_save[], in_prompt[];
extern char file_local[], file_remote[];
extern unsigned zdebug;
int cmd, argcount;
char arglist[20][MAXSTRING];
if (linebuf[1] == PROGESC) /* two escapes - send it */
{
sendline (&linebuf[1], inlength - 1);
return(2); /* main will read line */
}
argcount = tokenize(&linebuf[1], inlength-1, arglist);
if (!argcount)
{
message ("%s - no valid arguments found", linebuf);
return(1);
}
cmd = identify(optab, arglist[0]);
switch (cmd)
{
case OPENLINE: /* open the communications line */
if (lopen() == 0) {
wrapup(); /* fatal error */
return(0);
}
return(1);
case SENDFILE: /* send a file from here to there */
if (progstate == SLAVE)
{
message("Send: MASTER state command ");
message("but program is in SLAVE\n");
}
if (argcount != 3)
{
message ("Usage: Send localfile remotefile\n");
return (1);
}
strcpy(file_local, arglist[1]);
strcpy(file_remote, arglist[2]);
return(send(file_local, file_remote));
case GETFILE: /* get a file from there to here */
if (progstate == SLAVE)
{
message ("Get: MASTER state command ");
message ("but program is in SLAVE\n");
return(1);
}
if (argcount != 3)
{
message ("Usage: Get remotefile localfile\n");
return(1);
}
strcpy(file_remote, arglist[1]);
strcpy(file_local, arglist[2]);
return(receive(file_remote, file_local));
case MAXLINE: /* set the maximum number of bytes
that can be sent at one time */
if (argcount != 2)
{
message ("Usage: %s nn\n", optab[cmd]);
return(1);
}
return(setmaxl(arglist[1]));
case FINISHED: /* close out program */
wrapup();
return(0);
case MASTER:
case SLAVE:
if (progstate == cmd)
message ("Already in %s state\n",
optab[cmd]);
else {
progstate = cmd;
if (cmd == MASTER)
{
strcpy (in_prompt, MASTERPROMPT);
strcpy (line_name, line_save);
}
else
{
strcpy (in_prompt, SLAVEPROMPT);
strcpy (line_name, LINETTY);
}
message ("%s state set\n", optab[cmd]);
}
return (1);
case REMOTE:
if (argcount != 2)
{
listopt (optab[cmd], prtable);
return (1);
}
setremote(arglist[1]);
return(1);
case VIEWSETUP:
showsetup();
return(1);
case CONTINUE:
return(2);
case ZDEBUG:
zdebug = ((arglist[1][1] | ' ') == 'n');
return(1);
default:
message ("%s not recognized as a command\n",
arglist[0]);
listopt("on-line", optab);
return (ifonline());
}
}
/* make and output a data packet */
#include "manifs.h"
outdata(p, l)
char p[];
int l;
{
extern char packet[];
extern int errcount;
mkdatapacket(p, packet, l);
for (errcount = 0; errcount < ERR_LIMIT; ++errcount)
if (sendpacket(packet))
break;
}
/* manage an outbound file transfer */
/* this routine is the converse of inwards and is called with similar
conditions (xfer_file is open, etc.). After formatting the file
header and having it accepted by the other program, fileout is
called to transfer the data. */
#include "manifs.h"
int outwards (file_local, file_remote)
char file_local[], file_remote[];
{
extern char packet[], *rawfgets(), fileformat[];
extern int progstate;
extern unsigned checksum();
int err_cnt, l_length, r_length, i, q;
unsigned cksum;
char work[BUFFSIZE], *p;
strcpy (packet, "|FILE");
packet[F_DIRECTION] = (progstate == MASTER ? TO_SLAVE : TO_MASTER);
packet[F_STX] = X_STX;
l_length = strlen(file_local);
r_length = strlen(file_remote);
if (progstate == MASTER)
sprintf( work, fileformat,
l_length, r_length, file_local, file_remote, X_ETX);
else
sprintf( work, fileformat,
r_length, l_length, file_remote, file_local, X_ETX);
p = &(packet[F_SENDLENGTH]);
q = strlen(work);
for (i = 0; i < q; ++i)
p[i] = work[i];
cksum = checksum(&(packet[F_STX]));
if (cksum == 0)
{
message("Error calculating checksum in outwards routine\n");
kill (getpid(), 9); /* program bug if happens */
}
sprintf(work, "%04d", cksum % 10000);
p = &(packet[F_CKSUM]);
for (i = 0; i < 4; ++i)
p[i] = work[i];
for (err_cnt = 0; err_cnt < ERR_LIMIT; ++err_cnt)
if (sendpacket(packet))
break;
if (err_cnt >= ERR_LIMIT)
{
message("Error limit reached on file packet\n");
return(0);
}
if (!fileout())
message("Send: File transfer failed\n");
else
message("Send: Completed\n");
return(1);
}
/* process the contents of linebuf and decide where to get the next line */
#include "manifs.h"
int process(i)
int i;
{
extern char *rawfgets(), linebuf[];
extern int progstate, linestate, filecmd;
extern FILE *in_line;
char c;
if (i == EOF) { /* wrap up, friends */
wrapup();
return (0);
}
if (i == 0)
return(1);
c = linebuf[0];
if (progstate == SLAVE && c == X_SOH)
if (linestate == DISCONNECTED)
{
message("Packet input, but line is not open\n");
return(1);
}
else
{
while ((dopacket(linebuf, i) < 2)
&& (filecmd != IN))
i = strlen(rawfgets (linebuf, BUFFSIZE, in_line));
if (filecmd == IN)
if (!fileout())
{
message ("Too many errors - fileout\n");
return(0);
}
return(2);
}
if (c == PROGESC) /* something for us */
return(ourcmd(linebuf,i)); /* returns 0, 1, 2 */
sendline(linebuf, i); /* something for remote */
return (2); /* main should read line*/
}
/* loop doing rawfgets and processing packets until an end of file
packet is found. This is the main inwards file transfer routine */
#include "manifs.h"
int procpack()
{
extern char linebuff[], *rawfgets(), packet[];
extern int progstate;
extern FILE *in_line;
int pkt_cnt = 0;
int err_cnt = 0;
char c;
while (err_cnt < ERR_LIMIT)
{
rawfgets(packet, PACKSIZE, in_line);
if (packet[0] != X_SOH)
{
nack();
++err_cnt;
continue;
}
switch ((c = packet[1] & CM))
{
case 'E':
eofpacket(packet, strlen(packet));
return(ack());
default:
if ( c >= '0' && c <= '9')
if (xferpacket (packet, strlen(packet)))
{
err_cnt = 0;
ack();
if ( (progstate == MASTER) &&
( (++pkt_cnt % WARM_FEEL) == 0) )
message(".");
continue;
}
else
{
++err_cnt;
nack(); /* bad packet */
continue;
}
else
{
++err_cnt;
nack(); /* data packet expected */
continue;
}
}
}
return(err_cnt < ERR_LIMIT);
}
/* NON PORTABLE - UCB UNIX 4.x */
/* do an fgets on a line that is set to raw or cbreak mode using the newline
table entry nltab[newline] as the end of input line character */
#include "manifs.h"
char *rawfgets(string, limit, unit)
char string[];
int limit;
FILE *unit;
{
extern char nltab[];
extern int newline, progstate;
extern unsigned zdebug;
int i = 0;
char c = NUL;
while (c != nltab[newline] && i < limit)
{
c = (fgetc(unit)) & CM; /* hangs on the read */
if (zdebug && (progstate == MASTER))
{
char dc[3];
dc[0] = ' ';
dc[1] = c;
dc[2] = NUL;
if (c <= US || c == DEL)
{
dc[0] = '^';
dc[1] = (c == DEL ? '?' : (c | '@'));
}
message ("Rawfgets: Debug: input char = %s\n", dc);
}
/* control character filter */
if (NUL <= c && c <= US)
if ((c = nltab[newline]) || (c = '\n'))
; /* do nothing */
else
continue; /* scrag all except newline */
if (c == DEL)
continue;
string[i++] = c;
}
string[i] = NUL;
if (zdebug && progstate == MASTER)
message ("Rawfgets: Debug: return string -->%s<--\n", string);
return(string);
}
/* read characters from the comm_line and put them on stdout
also accumulating each "line" in the string buffer until a
prompt sequence is seen. In cases where the entry in prtab[prompt]
is a del, the end of the input is determined by absence of further input
and is controlled by a non-portable routine (ccount) */
#include "manifs.h"
int readline(string, limit)
char string[];
int limit;
{
extern int data_present, linestate, prompt, newline;
extern FILE *in_line;
extern char prtab[], nltab[];
extern long ccount();
register int i, j;
int reset_line;
register char c;
long nchars;
if (linestate == CONNECTED)
{
data_present = i = reset_line = 0;
for (;;)
{
nchars = ccount(in_line);
if (!nchars)
break;
data_present = 1;
for (j = 0; j < nchars; ++j)
{
if (reset_line)
{
reset_line = 0;
i = 0;
}
c = (fgetc(in_line)) & CM;
/* control character filter */
if ((c == DEL) || (NUL <= c && c <= US))
{
if ( c == nltab[newline] || c == '\n')
reset_line = 1;
else if (prtab[prompt] != DEL)
{
if (c == prtab[prompt])
reset_line = 1;
}
else
continue;
}
if (c == nltab[newline])
c = NL;
string[i++] = c;
putchar(c);
if (i >= limit)
break;
}
fflush(stdout);
if (i >= limit)
break;
}
fflush(stdout);
}
else
{
message("Readline - line not connected\n");
return(0);
}
string[i] = NUL;
return(i);
}
/* routine controls file transfers inwards */
#include "manifs.h"
int receive(remotename, localname)
char remotename[], localname[];
{
extern int linestate, compress, filecmd;
extern FILE *xfer_file;
if (linestate == DISCONNECTED)
{
message ("Get: Can't run. Line disconnected\n");
return(1);
}
xfer_file = fopen(localname, "w");
if (xfer_file == NULL)
{
message ("Get: Can't open %s for write\n", localname);
return(1);
}
filecmd = OUT;
compress = setcomp();
return(inwards(remotename, localname));
}
/* routine controls outward file transfers */
#include "manifs.h"
int send (file_local, file_remote)
char file_local[], file_remote[];
{
extern int linestate, compress, filecmd;
extern FILE *xfer_file;
if (linestate == DISCONNECTED)
{
message("Send: Can't run. Line disconnected`n");
return(1);
}
xfer_file = fopen(file_local, "r");
if (xfer_file == NULL)
{
message ("Send: Can't open %s for read\n", file_local);
return(1);
}
filecmd = IN;
compress = setcomp();
return(outwards(file_local, file_remote));
}
/* send a line out on the comm line */
#include "manifs.h"
int sendline(buffer, nchars)
char buffer[];
int nchars;
{
extern int linestate, newline;
extern FILE *out_line;
extern char nltab[];
char c;
register int i;
if (linestate == CONNECTED)
{
for ( i = 0; i < nchars; ++i )
{
if (buffer[i] == NL)
c = nltab[newline];
else
c = buffer[i];
fputc(c, out_line);
}
fflush(out_line);
}
else
message ("Communications line not opened\n");
return(1);
}
/* send the packet found in the incoming buffer */
#include "manifs.h"
int sendpacket(p)
char p[];
{
extern FILE *in_line, *out_line;
extern int newline, progstate;
extern char nltab[], linebuf[], *rawfgets();
extern unsigned zdebug;
int i;
char c;
if (zdebug && (progstate == MASTER))
showpacket("sendpacket", p);
i = 0;
do
{
c = p[i++];
fputc(c, out_line);
}
while (c != X_ETB && c != X_ETX);
fputc(nltab[newline], out_line);
fflush(out_line);
rawfgets(linebuf, BUFFSIZE, in_line);
return (linebuf[0] == 'y' ? 1 : 0);
}
/* offer to enable compression if other program is willing */
#include "manifs.h"
int setcomp()
{
extern char handshake[], linebuf[];
extern FILE *in_line;
extern char *rawfgets();
sendline(handshake, strlen(handshake));
rawfgets(linebuf, BUFFSIZE, in_line);
return (linebuf[0] == 'y' ? 1 : 0);
}
/* set maximum transmission line length */
#include "manifs.h"
int setmaxl(string)
char *string;
{
extern maxline;
int state;
state = sscanf(string, "%d", &maxline);
if (state != 1)
message ("Unable to set maxline. Current value = %d\n",
maxline);
else
message ("Maximum line length set to %d\n", maxline);
return(1);
}
/* set remote system paramters */
#include "manifs.h"
int setremote(string)
char string[];
{
extern char *prtable[];
extern int newline, prompt, remote;
int i;
i = identify(prtable, string);
if (prtable[i][0] == NUL) {
message("Cannot set remote state to %s - not supported\n",
string);
return(1);
}
newline = prompt = remote = i;
return(1);
}
/* NON PORTABLE - UCB UNIX 4.X */
/* setup - This routine performs whatever setup is necessary */
#include "manifs.h"
int setup (argc, argv)
int argc;
char *argv[];
{
extern int on_line, speed;
extern char line_name[], line_save[];
extern char syntax[], *upperc(), callname[];
int i, p;
p = 0;
if (strlen(argv[0]) < 19)
strcpy (callname, argv[0]); /* copy call name for msgs */
else
strncpy (callname, argv[0], 19);
upperc(callname);
on_line = isatty(fileno(stdin)); /* control errors */
speed = SPEEDEFAULT; /* set defaults */
strcat(line_name, LINEDEFAULT);
for (i = 1; i < argc; ++i)
switch (argv[i][0]) {
case 'l':
case 'L':
p += linearg(argv[i]);
continue;
case 's':
case 'S':
if (argv[i][1] == 'l' || argv[i][1] == 'L')
{
ourcmd ("!slave", 6);
ourcmd ("!open", 5);
continue;
}
p += speedarg(argv[i]);
continue;
default:
fprintf(stderr, "%s not understood\n%s",
argv[i], syntax);
return(0);
}
if (p != 0)
return(0);
return (1);
}
/* debugging tool - can be dropped later */
#include "manifs.h"
void showpacket(who, packet)
char *who, packet[];
{
char work[MAXSTRING], c;
unsigned i;
i = 0;
do
{
c = work[i] = packet[i];
++i;
} while (( c != X_ETX) && (c != X_ETB));
work[i] = NUL;
message ("Debug: from %s: packet contents\n%s\n", who, work);
}
/* routine to display the current state of the world */
#include "manifs.h"
int showsetup()
{
extern int progstate, linestate, speed, compress, maxline;
extern char line_name[];
extern char *optab[], *prtable[];
extern char nltab[], prtab[], callname[], line_name[];
extern int remote, newline, prompt;
message ("%s: Program Status\n", callname);
message ("State is %s\n", optab[progstate]);
message ("Communications line %s is %s\n", line_name,
(linestate == CONNECTED ? "open" : "closed"));
message ("Remote device is %s\n", prtable[remote]);
showspeed(speed);
message ("Remote newline is 0%o\n", nltab[newline]);
message ("Remote prompt is 0%o\n", prtab[prompt]);
message ("Maximum characters to transmit per packet is %d\n", maxline);
message ("Compression is %s\n", compress ? "ON" : "OFF");
return(1);
}
/* NON PORTABLE - UCB UNIX 4.X */
/* print the interpreted value in speed */
#include "manifs.h"
int showspeed(speed)
{
message("External line speed is ");
switch(speed) {
case B0:
message("Null\n");
return;
case B50:
message("50");
break;
case B75:
message ("75");
break;
case B110:
message ("110");
break;
case B134:
message("134.5");
break;
case B150:
message ("150");
break;
case B200:
message("200");
break;
case B300:
message ("300");
break;
case B600:
message ("600");
break;
case B1200:
message ("1200");
break;
case B1800:
message ("1800");
break;
case B2400:
message("2400");
break;
case B4800:
message("4800");
break;
case B9600:
message("9600");
break;
case EXTA:
case EXTB:
message("External\n");
return;
default:
message("Erroneous\n");
return;
}
message (" bits per second\n");
return;
}
/* NON PORTABLE - UCB UNIX 4.X */
/* Parse s=value */
#include "manifs.h"
int speedarg(string)
char *string;
{
extern int speed;
int i;
int val;
i = sscanf (string, "s=%4d", &val);
if (i != 1) {
message ("%s not understood\n", string);
return(1);
}
switch (val) {
case 110:
speed = B110;
return(0);
case 300:
speed = B300;
return(0);
case 1200:
speed = B1200;
return(0);
case 1800:
speed = B1800;
return(0);
case 2400:
speed = B2400;
return(0);
case 4800:
speed = B4800;
return(0);
case 9600:
speed = B9600;
return(0);
default:
message ("Speed %d is not supported\n", val);
return(1);
}
}
/* this routine breaks the input string up into tokens in the
list of strings given. */
#include "manifs.h"
int tokenize(string, limit, list)
char string[];
int limit;
char list[][MAXSTRING];
{
int i, j, k;
char c;
j = k = 0;
for (i = 0; i < limit; ++i) {
c = string[i];
switch(c) {
case ' ':
case '\t':
if (k > 0) {
list[j][k++] = NUL;
++j;
k = 0;
}
continue;
case NUL:
case '\r':
case '\n':
if ( k > 0)
list[j][k++] = NUL;
return (++j);
default:
list[j][k++] = c;
}
}
list[j][k] = NUL;
return(++j);
}
/* uppercase an input string's alphabetic characters */
char *upperc(string)
char string[];
{
int i, j;
char c;
j = strlen(string);
for (i = 0; i <= j; ++i) {
c = string[i];
if ( c >= 'a' && c <= 'z')
string[i] ^= 040;
}
return(string);
}
/* routine to perform wrapup functions at end of run */
#include "manifs.h"
int wrapup()
{
extern char callname[];
extern int linestate;
extern FILE *in_line, *out_line;
extern struct sgttyb com_save;
extern struct tchars com_1s;
extern struct ltchars com_2s;
extern char line_name[];
if (linestate == CONNECTED)
{
ioctl(fileno(in_line), TIOCSETC, &com_1s);
ioctl(fileno(in_line), TIOCSLTC, &com_2s);
ioctl(fileno(in_line), TIOCSETP, &com_save); /* restore */
if (strcmp (line_name, LINETTY)) /* not the tty */
{
fclose(in_line);
fclose(out_line);
}
else
in_line = out_line = NULL;
linestate = DISCONNECTED;
message("%s: Communication line closed\n", callname);
}
return(1);
}
/* process incoming data packet */
#include "manifs.h"
int xferpacket(ptr, len)
char ptr[];
int len;
{
extern FILE *xfer_file;
extern int compress;
int ret_char, dx, px;
unsigned value, nchars, cksum, count;
char data[MAXSTRING], *p;
sscanf(ptr, "|%4d%3d{", &value, &count);
dx = px = 0;
p = &(ptr[D_BYTES]);
while ((p[px] != X_ETX) && (p[px] != X_ETB))
{
if (compress && p[px] == X_GS)
{ /* de-compression */
nchars = p[px + 1] ^ 0x40;
px += 2;
ret_char = hex_to_a(&(p[px]));
if (ret_char == HEXERROR)
return(0);
while (nchars--)
data[dx++] = ret_char;
px += 2;
}
else
{
ret_char = hex_to_a(&(p[px]));
if (ret_char == HEXERROR)
return(0);
data[dx++] = ret_char;
px += 2;
}
if (dx >= count)
break;
}
for (cksum = px = 0; px < dx; px++)
cksum += data[px];
cksum %= 10000;
if (cksum != value)
return(0);
for (px = 0; px < dx; px++)
putc(data[px], xfer_file);
fflush(xfer_file);
return(1);
}
More information about the Comp.sources.unix
mailing list