[net.sources] Remote troff package

steve@tove.UUCP (Steve D. Miller) (03/29/85)

   I've gotten quite a bit of mail about my remote troff stuff;
while I've replied to most of it already, the level of interest
indicates that the world might actually be interested in such a
beast.  Anyway, here it is (it's pretty short, so I'm posting
this both here and in net.sources...).  For those of you who don't
want to read the code (and those of you who don't want to
read someone's first C program...), here's what I'm including,
how it works, and (roughly) how to install it.

   This set of programs includes:

	1)   A set of manual entries for rtroff (remote troff),
		load (a program written by Fred Blonder here
		at UMCP to return load status to the shell
		in various interesting forms), and ltroff (a
		shell script that uses load, rtroff, and rcatdvi
		to wrap all this stuff into a form that the average
		user can use to get stuff to our imagen without
		having to know what all this stuff does).

	2)  The source for ltroff (shell script), load, rtroff,
		and rcatdvi (remote Imagen catdvi/dviimp).

	3)  A makefile to make, install, and set the permissions
		stuff for everything.

   Rtroff operates via the rcmd() call in 4.2 BSD; it takes normal
troff arguments, figures out what refers to files and what doesn't
do so, then adds the "-t" and "-" options to the command line,
snips out all the filenames, and starts a troff up on a remote
machine.  Rtroff then sends all the files down the socket, doing
junk like ".so" elimination on the fly, and reads the socket (stdout/
stderr for the troff).  The results go to rtroff's stdout.  Rtroff
also looks at its invocation, and is perfectly capable of acting
as rnroff if a link is made between the two.

   Rcatdvi is like rtroff, but it doesn't need to do macro manipulation
or argument parsing, so it's a *lot* simpler.

   Ltroff is a csh script (yes, I've seen the recent postings about
csh scripts, but I didn't write it to start; I think Pete Cottrell
did most of it, with my mods for load-sharing and my documentation;
I avoid shell scripts whenever I can and I doubt that I could have
written it from scratch) that acts like troff, but handles load checking,
offloading (calling rtroff vs troff and rcatdvi vs catdvi), and
queueing (we use MDQS here; the script shouldn't be hard to change for
something else) for a user community that doesn't want to think about
such things.  The load level cutoff is specified in the "load"
variable in the shell script, and there is also extra stuff
in there that checks to see if the user is authorized to use our
Imagen.

   Load is a program that opens kmem and looks at the load average,
and returns status to the shell depending on what it finds there;
check the man entry for details.

   Rtroff handles going from host to host by reading a list of
hosts in a file (/etc/rtrhosts); it connects to the first in
the list, and if that times out, tries the next, until it hits
the end of the list and gives up.  Rcatdvi operates in the
same way.  This scheme means that the least loaded of your
machines should be at the top of the list, followed by your
other machines in a sensible order.  The remote execution runs
under the "rtroff" account, which can have an asterisk for
a password field entry (as long as /etc/hosts.equiv is set
up properly; I suppose that one could do this using ~rtroff/
.rhosts and listing names, but that's ugly...).

   To do the things it has to, rtroff, rcatdvi, and load all need
to be setuid root; the makefile will set permissions if you
are running as root.  To make, do "make all", then "make install".

   If you have problems or questions, please let me know.  And without
further ado...

Spoken: Steve Miller 	ARPA:	steve@maryland	Phone: +1-301-454-4251
CSNet:	steve@umcp-cs 	UUCP:	{seismo,allegra}!umcp-cs!steve
USPS: Computer Science Dept., University of Maryland, College Park, MD 20742

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting Makefile'
sed 's/^X//' <<'//go.sysin dd *' >Makefile
#  Makefile for the remote troff stuff
#  Steven D. Miller (steve@maryland.ARPA)

CC = cc -s -O

#
#  RTRBIN is the place you want all this stuff to live (usually
#  /usr/local/bin).
#

RTRBIN = /usr/local/bin

RTRSRC = load.c rtroff.c rcatdvi.c

#  MAN is where you want the manual entries; should be /usr/man/man1

MAN = /usr/man/man1

all: man load rtroff rcatdvi

load: load.o
	$(CC) -o load load.o

rtroff: rtroff.o
	$(CC) -o rtroff rtroff.o

rcatdvi: rcatdvi.o
	$(CC) -o rcatdvi rcatdvi.o

man: rtroff.1l load.1l ltroff.1
	cp rtroff.1l $(MAN)/rtroff.1l
	cp ltroff.1 $(MAN)/ltroff.1
	cp load.1l $(MAN)/load.1l

install: $(RTRBIN)/ltroff $(RTRBIN)/rtroff $(RTRBIN)/rnroff \
		$(RTRBIN)/load $(RTRBIN)/rcatdvi

$(RTRBIN)/ltroff: ltroff
	cp ltroff $(RTRBIN)/ltroff

$(RTRBIN)/rtroff: rtroff
	echo I hope you're running as root...
	cp rtroff $(RTRBIN)/rtroff
	chmod 4555 $(RTRBIN)/rtroff

$(RTRBIN)/rnroff : rtroff
	ln -s $(RTRBIN)/rnroff $(RTRBIN)/rtroff

$(RTRBIN)/load : load
	echo Need to be root for this, too...
	cp load $(RTRBIN)/load
	chmod 4555 $(RTRBIN)/load

$(RTRBIN)/rcatdvi : rcatdvi
	echo Need to be root to install rcatdvi
	cp rcatdvi $(RTRBIN)/rcatdvi
	chmod 4555 $(RTRBIN)/rcatdvi
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 Makefile
	/bin/echo -n '	'; /bin/ls -ld Makefile
fi
/bin/echo 'Extracting load.1l'
sed 's/^X//' <<'//go.sysin dd *' >load.1l
X.TH LOAD 1L 4-Oct-1984
X.SH NAME
load \- determines if system load is greater than input value,
or prints current system load
X.SH SYNOPSIS
X.B load 
[\-sv]
X.B [ maxload ]
X.SH DESCRIPTION
X.I Load
returns true if the system load is greater
than or equal to the value given by 
X.I
maxload.
By default, the program returns yes if the system
load is too high and no if it is not.
If the -v option is specified, a more detailed message 
is displayed.
The -s option will display no message.  
X.sp
If
X.I load
is invoked with no argument, it prints the current 15-minute load average.
X.SH FILES
X.SH "SEE ALSO"
uptime(1), w(1), vmpic(1l)
X.SH DIAGNOSTICS
X.SH AUTHOR 
Fred Blonder
X.SH BUGS
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 load.1l
	/bin/echo -n '	'; /bin/ls -ld load.1l
fi
/bin/echo 'Extracting load.c'
sed 's/^X//' <<'//go.sysin dd *' >load.c
#ifndef lint
static char *scssid = "@(#)load.c	(University of Maryland) Oct 7, 1981";
static char RCSid[] = "@(#)$Header: /usr/fred/src/progs/RCS/load.c,v 1.2 84/10/04 21:54:14 fred Exp $";
#endif lint

X/*
 * load.c - returns true if the system load is >= value given by argv[1]
 *
 * 	Cannibalized from rogue (main.c) by Fred Blonder, Oct 7, 1981
 *
 * $Log:	load.c,v $
 * Revision 1.2  84/10/04  21:54:14  fred
 * RCSified everything. Uses sysexits.h. Default action is to print
 * current load. 4-Oct-84 FLB.
 * 
 * To compile:
 %	cc -s -O load.c -o load
 */

#include <sysexits.h>
#include <nlist.h>

main(argc, argv)
int argc;
char *argv[];
{
double maxload = 0.0, avec[3], atof();
char *yes_msg = "Yes\n", *no_msg = "No\n";

while (argc > 2 && argv[1][0] == '-') {
	switch (argv[1][1]) {
		case 's':
			yes_msg = no_msg = "";
			break;

		case 'v':
			yes_msg = "System load too high\n";
			no_msg  = "System load not too high\n";
			break;

		default:
			printf("Load: option ``%s'' not recognized, ignored.\n", argv[1]);
		}
	argc--;
	argv++;
	}

loadav(avec);

if (argc < 2) {
	printf("%5.2f\n", avec[2]);
	exit(EX_OK);
	}

if ((maxload = atof(argv[1])) <= 0.0) {
	printf("Load: absurd load value: %f\n", maxload);
	exit(EX_DATAERR);
	}
	
if (avec[2] >= maxload) {
	printf(yes_msg);
	exit(EX_OK);
	}
else {
	printf(no_msg);
	exit(1);
	}
}

struct nlist avenrun =
{
    "_avenrun"
};

loadav(avg)
register double *avg;
{
register int kmem;

if ((kmem = open("/dev/kmem", 0)) < 0) {
	perror("Can't open /dev/kmem");
	exit(EX_OSFILE);
	}
nlist("/vmunix", &avenrun);
if (avenrun.n_type == 0) {
	avg[0] = avg[1] = avg[2] = 0.0;
	return;
	}

lseek(kmem, (long) avenrun.n_value, 0);
read(kmem, avg, 3 * sizeof (double));
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 load.c
	/bin/echo -n '	'; /bin/ls -ld load.c
fi
/bin/echo 'Extracting ltroff'
sed 's/^X//' <<'//go.sysin dd *' >ltroff
#! /bin/csh -f
# troff to the IMAGEN imPrint printer
# /etc/renice 4 $$ >& /dev/null
set flags=(-rv1) noglob fonts=() cflags=() host=() rshhost=()
set macp=(/usr/lib/tmac/tmac.vcat)
set facp=(-F/usr/lib/imagen/font/ftXX )
set PATH=(. /usr/local/bin /usr/bin /usr/ucb )
set path=($PATH)
set local load=(2)
unset t w dv i doremote
top:
	if ($#argv > 0) then
		switch ($argv[1])

		case -sd:
			set dv
			shift argv
			goto top

		case -si:
			set i
			shift argv
			goto top

		case -sc:
		case -t:
			set t
			shift argv
			goto top

		case -w:
			set w
			shift argv
			goto top

		case -x:
			set macp=()
			shift argv
			goto top

		case -F:
			shift argv
			if ($#argv > 0) then
				set flags = ($flags -F$argv[1]/ftXX)
				set cflags = ($cflags -C$argv[1]/catab)
				set facp = ()
				set w
				shift argv
			endif
			goto top

		case -c*:
			set cflags = ($cflags $argv[1])
			shift argv
			goto top
		case -h:
			set host = ($argv[1])
			set doremote
			shift argv
			set host= ($host $argv[1])
			set rshhost= ($argv[1])
			shift argv
			goto top

		case -l:
			unset local
			shift argv
			goto top

		case -r:
			shift argv
			set load=($argv[1])
			shift argv
			goto top

		case -*:
			set flags = ($flags $argv[1])
			shift argv
			goto top

		endsw
	endif
if ($#argv == 0) then
	set argv=(-)
	set banner=stdin
else
	set banner="$argv"
endif
X/usr/local/bin/load -s $load
if ($status == 0 && $?local) then
	set troffcmd=(rtroff)
	set catcmd=(/usr/local/bin/rcatdvi $host )
else
	set troffcmd=(troff)
	set catcmd=(/usr/local/bin/catdvi)
	set host=()
endif
if ($?doremote) then
	set troffcmd=(rtroff)
	set catcmd=(/usr/local/bin/rcatdvi $host )
endif
if ($?t) then
    /usr/bin/troff -x -t $flags $facp $macp $*
else
    /usr/bin/fgrep -s ,`/usr/ucb/whoami`, /etc/restrict/mdqs/ltroff_users
    if ($status) then
         echo Sorry, you\'re not authorized to send directly to the Imagen.
	else if ($?dv) then
	    $troffcmd $host -x -t $flags $facp $macp $* | $catcmd -b "$banner" $cflags
	else if ($?i) then
	    $troffcmd $host -x -t $flags $facp $macp $* | $catcmd -a -b "$banner" $cflags 
	else if ($?w) then
	    $troffcmd $host -x -t $flags $facp $macp $* | $catcmd -b "$banner" $cflags
	else 
	    $troffcmd $host -x -t $flags $facp $macp $* | $catcmd -a -b "$banner" $cflags | /usr/bin/qpr -q imagen-imp
#######else
#######    /usr/bin/troff -x -t $flags $facp $macp $* | /usr/ucb/lpr -Pimagen -t
	endif
endif
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 ltroff
	/bin/echo -n '	'; /bin/ls -ld ltroff
fi
/bin/echo 'Extracting ltroff.1'
sed 's/^X//' <<'//go.sysin dd *' >ltroff.1
X.TH LTROFF 1 "4 October 1984"
X.SH NAME
ltroff \- text formatting on Imagen printer
X.SH SYNOPSIS
X.B ltroff
[ macro option ] ...
[ option ] ...
[ file ] ...
X.SH DESCRIPTION
X.I Ltroff
formats text in the named
X.I files
for printing on an Imprint-10 laser printer.  If the local load is above
seven, then the processing needed is done remotely; otherwise, the
processing is done locally.
X.I Ltroff
accepts files that would be used as input for
X.I troff
transparently, as far as the user is concerned.  In addition to
standard
X.I troff
options, the options that are specific to
X.I ltroff
are:
X.TP
X.BR \-sd " or " \-w
Leave output on standard output in DVI form.
X.TP
X.BI \-si
Leave output on standard output in imPress form.
This output may be printed with "qpr -q imagen-imp".
X.TP
X.BR \-sc " or " \-t
Leave output on standard output in C/A/T form.
X.TP
X.B \-x
Turn off the use of the standard macro package /usr/lib/tmac/tmac.vcat.
X.TP
X.B \-h [hostname]
If the load is above the cutoff, do the processing on machine
X.I hostname.
X.TP
X.B \-l
Force processing to be done on local machine, regardless of load.
X.TP
X.B \-Ffontdir
Use the files in this directory as the font width tables for
X.I troff.
The default font spacing files are in /usr/lib/imagen/font/ftXX.
X.TP
X.BR \-c " arg"
Pass arg to
X.I catdvi.
X.SH BUGS
X.PP
The options to leave files in DVI form are not currently implemented
due to modifications made in catdvi and dviimp.  The output
produced is in imPress form.
X.PP
Remote processing may only be done on hosts running the rtroff daemon.
The default host is gymble.
X.SH FILES
X.ta \w'/usr/lib/tmac/tmac.vcat  'u
X.br
X/usr/lib/tmac/tmac.vcat		C/A/T macro file
X.SH "SEE ALSO"
X.br
troff(1), rtroff(1), catdvi(1), dviimp(1), qpr(1), istat(1)
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 ltroff.1
	/bin/echo -n '	'; /bin/ls -ld ltroff.1
fi
/bin/echo 'Extracting rcatdvi.c'
sed 's/^X//' <<'//go.sysin dd *' >rcatdvi.c
X/*
 *
 * rcatdvi -- send stdin to catdvi on remote
 * machine, get stdout and stderr back.  Invoked as
 * "rcatdvi [-h host] [catdvioptions], runs setuid to make
 * connection, then does a setuid(getuid) to go back.
 *
 */


#include <stdio.h>
#include <netdb.h>
#include <pwd.h>
#include <time.h>
#define LINESIZ 133

main(argc,argv)
int	argc;
char	*argv[];
{
	char	cmdline[LINESIZ];
	char	*host;
	struct	servent	*serv;
	struct	passwd	*rtroff;
	struct	hostent	*rhost;
	int	i;
	int	firstarg;
	int	errfd, iofd;
	int	uid;
	int	readfds, writefds, readyrd, readywt;

	if((serv=getservbyname("shell","tcp"))==NULL) {
		perror("rcatdvi:getservbyname");
		exit(-1);
	}
	

	if (strcmp("-h",argv[1])==0) {
		host=argv[2];
		firstarg=3;
	}
	else
		firstarg=1;
	strcat(cmdline,"/usr/local/bin/catdvi");
	if (argc>(firstarg-1)) {
		for (i=firstarg;i<=argc;i++) {
			strcat(cmdline," ");
			strcat(cmdline,argv[i]);
		}
	}
	if ((rhost=gethostbyname(host))==NULL) {
		/*
		 * since we can't find the requested host, if any,
		 * let's check the config file and connect around
		 * in there.
		 */
		FILE	*rtrhosts;
		char	*pos;
		char	thishost[LINESIZ];
		char	*index();

		if ((rtrhosts=fopen("/etc/rtrhosts","r"))==NULL) {
			perror("rcatdvi:can't open /etc/rtrhosts");
			exit(1);
		}
		while ((host=fgets(thishost,LINESIZ,rtrhosts))!=NULL) {
			if ((pos=index(thishost,'\n'))!=0)
				*pos='\0';
			if((iofd=rcmd(&host,serv->s_port,"rtroff",
				"rtroff",cmdline,&errfd))<0) {
				perror("rcatdvi:rcmd:");
				continue;
			} else break;
		}
		if(iofd<0) {
			perror("rcatdvi:can't get a host");
			exit(1);
		}
	} else if((iofd=rcmd(&host,serv->s_port,"rtroff",
		"rtroff",cmdline,&errfd))<0) {
			perror("rcatdvi:rcmd");
			exit(-1);
		}
	setuid(getuid());
	setgid(getgid());
	readfds=(1<<iofd | 1<<errfd);
	writefds=(1<< iofd);

	while (readfds || writefds) {
		readyrd=readfds;
		readywt=writefds;
		if (select(16,&readyrd,&readywt,0,0)<0) {
		perror("rcatdvi:select");
		exit(1);
		}
		if (readyrd & (1<<iofd)) {
			char buf[LINESIZ];
			int	nread;
			if ((nread=read(iofd,buf,sizeof(buf)))<=0)
				readfds &= ~(1<<iofd);
			else 
				write(1,buf,nread);
		}
		if (readyrd & (1<<errfd)) {
			char buf[LINESIZ];
			int	nread;
			if ((nread=read(errfd,buf,sizeof(buf)))<=0)
				readfds &=~ (1<<errfd);
			else
				write(2,buf,nread);
		}
		if (readywt & (1<<iofd)) {
			char buf[LINESIZ];
			int nread;
			if ((nread=read(0,buf,sizeof(buf)))<=0) {
				shutdown(iofd,1);
				writefds &= ~(1<<iofd);
			}
			else
				write(iofd,buf,nread);
		}
	}
	shutdown(errfd,1);
	fflush(stdout);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 rcatdvi.c
	/bin/echo -n '	'; /bin/ls -ld rcatdvi.c
fi
/bin/echo 'Extracting rtroff.1l'
sed 's/^X//' <<'//go.sysin dd *' >rtroff.1l
X.TH RTROFF 1 "16 September 1984"
X.SH NAME
rtroff, rnroff \- remote text formatting and typesetting
X.SH SYNOPSIS
X.B rtroff
[ \-h hostname ]
[ option ] ...
[ file ] ...
X.PP
X.B rnroff
[ \-h hostname ]
[ option ] ...
[ file ] ...
X.SH DESCRIPTION
X.I Rtroff
formats text in the named
X.I files
for printing on a Graphic Systems C/A/T phototypesetter;
X.I rnroff
is used for typewriter-like devices.
Both of these commands act like
X.I troff(1)
and
X.I nroff(1)
,except that the text processing is done on a remote machine.
The \-h option may be used to specify the remote host for processing;
if no host is specified, the file /etc/rtrhosts is searched and a
connection is attempted to the machines listed in that file in the order
in which they appear.  If a connection cannot be established to one machine,
the next in order is consulted.
All options and
files are specified as in
X.I
troff(1).
X.SH SEE ALSO
troff(1)
X.SH BUGS
The troff directives '.so', '.nx', and '.rd' will not be handled
correctly if part of a macro definition or conditional statement,
but will be handled correctly if used as a deferred (i.e. "'so")
directive.
X.PP
Use of the '.pi' directive may produce unpredictable results.
X.PP
Files that use the '\-man' macros may have unusual page titles; i.e.
files sent from a Vax to a Pyramid 90X will not say "Unix Programmers'
Manual" but will say "Pyramid OSX Users' Manual."
X.PP
If rtroff has to try more than one machine in /etc/rtrhosts,
extraneous scary error messages ("Connection
timed out") will be printed.  If there are more machines in the list
left to be tried, these errors can be ignored.
X.SH AUTHOR
Steven D. Miller
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 rtroff.1l
	/bin/echo -n '	'; /bin/ls -ld rtroff.1l
fi
/bin/echo 'Extracting rtroff.c'
sed 's/^X//' <<'//go.sysin dd *' >rtroff.c
#define BUFSIZE 1024
#define LINESIZ 133
#define ATPORT  562
#define MAXARGS 256
#define FALSE 0
#define TRUE 1
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/wait.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>

int skt,errskt;
int errno;
char *hostname;

main(argc,argv)
int argc;
char *argv[];
{
	struct	 sockaddr_in addr;
	int	needstdin=FALSE;
	char	tcmd[BUFSIZE];
	char 	*fargv[MAXARGS];
	struct 	passwd	*pwd;
	int	geterrs();
	struct	servent	*serv;

	signal(SIGPIPE,geterrs); /* if t/nroff dies before all files are
				    sent, then read error msgs */
	needstdin=parseargs(argc,argv,fargv,tcmd);
	if ((serv=getservbyname("shell","tcp"))==NULL) {
		perror("rtroff:getservbyname");
		exit(1);
	}
	if ((pwd=getpwuid(getuid()))==NULL) {
		perror("rtroff:getpwuid");
		exit(1);
	}
	if (hostname!=NULL) {
		if ((skt=rcmd(&hostname,serv->s_port,"rtroff",
		"rtroff",tcmd,&errskt))<0) {
			perror("rtroff:rcmd");
			exit(1);
		}
	}
	else  {
		/*
		 *  If the user has not specified a host name, then
		 *  we search the hosts in /etc/rtrhosts in the
		 *  order specified in the file for a place to get 
		 *  a connection to.
		 */
		FILE	*rtrhosts;
		char	*pos;
		char	thishost[LINESIZ];
		char	*index();

		if ((rtrhosts=fopen("/etc/rtrhosts","r"))==NULL) {
			perror("rtroff:can't open /etc/rtrhosts");
			exit(1);
		}
		while ((hostname=fgets(thishost,LINESIZ,rtrhosts))!=NULL) {
			if ((pos=index(thishost,'\n'))!=0)
				*pos='\0';
			if((skt=rcmd(&hostname,serv->s_port,"rtroff",
				"rtroff",tcmd,&errskt))<0) {
				perror("rtroff:rcmd:");
				continue;
			} else break;
		}
		if (skt<0) {
			perror("rtroff:can't get a host");
			exit(1);
		}
	}
	setuid(getuid());
	setgid(getgid());
	sendfiles(fargv,skt);
	if(needstdin)
		sendonefile(0,skt);
	getmsgs(skt,errskt);
	close(skt);
	exit(0);
}

char *
sindex(s1,s2)
char	*s1,
	*s2;
{
	char	*ch;
	char	*ch2=s2;
	char	*index();
	
	if ((ch=index(s1,*s2))==NULL)
		return(NULL);
	while(*ch2!=0) {
		if (*ch++!=*ch2++)
			return(NULL);
	}
	return(index(s1,*s2));
}
	
int
parseargs(argc,argv,fargv,tcmd)
int argc;
char *argv[];
char *fargv[];
char *tcmd;
{
	int cnt=1;
	int tostdout=FALSE;
	int needstdin=FALSE;
	int fcnt=0;
	int tcnt=1;
	char *index();

	if (sindex(argv[0],"troff")!=NULL)
		strcpy(tcmd,"/usr/bin/troff ");
	else
		strcpy(tcmd,"/usr/bin/nroff ");
	hostname=NULL;
	cnt=1;
	while (cnt<argc) {
		char *arg;

		if(*argv[cnt]=='-') {
			arg=argv[cnt];
			arg++;
			switch(*arg) {
			case '\0':
				needstdin=TRUE;
				break; /* don't copy this arg, since troff
					  doesn't handle options after '-'
					  properly.  It will be put on the
					  end of the argument list later. */
			case 's':
			case 'i':
				needstdin=TRUE;
				strcat(tcmd,argv[cnt]);
				strcat(tcmd," ");
				break;
			case 't':
			case 'a':
				tostdout=TRUE;
				strcat(tcmd,argv[cnt]);
				strcat(tcmd," ");
				break;
			case 'h':
				hostname=argv[cnt+1];
				cnt++;
				break;
			default:
				strcat(tcmd,argv[cnt]);
				strcat(tcmd," ");
			}
		cnt++;
		}
		else { /* must be a file name */
			fargv[fcnt++]=argv[cnt];
			cnt++;
		}
	}
	if ((tostdout==FALSE) && (sindex(tcmd,"nroff")==0)) { /* not nroff */
		strcat(tcmd,"-t"); /* force to stdout if not going there */
		strcat(tcmd," ");
	}
	strcat(tcmd,"-");  /* must act as if reading only from stdin */
	fargv[fcnt]=0; /* terminate lists */

	return(needstdin);
}


sendfiles(fargv,skt)
char *fargv[];
int skt;
{
	char *sendbuf[BUFSIZE];
	int cnt=0;
	int nread;

	if (fargv[0]==0) {
		sendonefile(0,skt); /* send stdin */
		return;
	}
	while (fargv[cnt]!=0) {
		int ftosend;
		
		if ((ftosend=open(fargv[cnt],0))==-1) {
			fprintf(stderr,"rtroff: could not open file %s\n",
				fargv[cnt]);
			exit(-1);
		}
		else {
			sendonefile(ftosend,skt);
			cnt++;
		}
	}
	return;
}

sendonefile(fd,sktout)
int fd; /* open descriptor for file to be sent */
int sktout; /* descriptor to which it is to go */
{
	char *buf[LINESIZ];
	int nread;
	int readfds, writefds, readyrd, readywt;
	struct timeval timeout;


	readfds=(1<<skt) | (1<<errskt);
	writefds=(1<<sktout);
	do {
		readyrd=readfds;
		readywt=writefds;
		if (select(16,&readyrd,&readywt,0,0)<0)
			break;
		if(readyrd & (1<<skt)) {
			nread=read(skt,buf,sizeof(buf));
			if (nread >0)
				write(1,buf,nread);
		}
		if (readyrd & (1<<errskt)) {
			nread=read(errskt,buf,sizeof(buf));
			if (nread >0)
				write(2,buf,nread);
		}
		if (readywt & (1<<sktout)) {
			nread=getline(buf,fd);
			if (nread<=0) {
				close(fd);
				return;
			}
			if ((strncmp(buf,".so",3)==0) || (strncmp(buf,"'so",3)==0))	 /* include .so file in stream */
				getso(buf,sktout);
			else if ((strncmp(buf,".nx",3)==0) || (strncmp(buf,"'nx",3)==0))	  /* send .nx file */
				getnx(buf,sktout,fd);
			else if((strncmp(buf,".rd",3)==0)  || (strncmp(buf,"'rd",3)==0))	/* handle .rd instruction */
				getrd(buf,sktout);
			else  if (write(sktout,buf,strlen(buf))<=0) {
				perror("sendfiles:write");
				exit(-1);
			}
		}
	} while (readfds);
}

getline(buf,fd)
char *buf;
int fd;
{
	int nread=0;
	char chr=0;

	while(read(fd,&chr,1)!=0) {
		*buf++=chr;
		nread++;
		if (chr=='\n')
			break;
	}
	*buf++='\0';
	if(chr='\n')
		return(nread);
	else
		return(0);
}

getfname(chrptr,buf) /* copies from chrptr to white space into buf */
char *chrptr;
char *buf;
{
	int index=0;
	while((*chrptr!=' ') && (*chrptr!='\t') && (*chrptr!='\n')) {
		*buf++ = *chrptr;
		chrptr++;
	}
	*buf='\0';
}

getso(buf,skt)
char *buf;
int skt;
{
			char *sobuf[LINESIZ];
			char *sofname;
			int sofd;

			if ((sofname=index(buf,' '))==0) {
				if ((sofname=index(buf,'\t'))==0)
					return;
			}
			sofname++;
			getfname(sofname,sobuf);
			if((sofd=open(sobuf,0))<0) {
				fprintf(stderr,"rtroff: could not open file %s\n",sobuf);
				exit(-1);
			}
			sendonefile(sofd,skt);
}

getnx(buf,skt,fd)
char *buf;
int skt;
int fd;
{
			char *nxbuf[LINESIZ];
			char *nxfname;
			int nxfd;

			if ((nxfname=index(buf,' '))==0) {
				if ((nxfname=index(buf,'\t'))==0)
					return;
			}
			nxfname++;
			getfname(nxfname,nxbuf);
			close(fd);
			if((nxfd=open(nxbuf,0))<0) {
				fprintf(stderr,"rtroff: could not open file %s\n",nxbuf);
				exit(-1);
			}
			sendonefile(nxfd,skt);
}

getrd(buf,skt)
char *buf;
int skt;
{
			char *prompt[LINESIZ];
			char chr;
			char *prstart;
			int newlines=0;
			int nread;

			if ((prstart=index(buf,' '))==0) 
				if ((prstart=index(buf,'\t'))==0)
					strcpy(prompt,"\7");
				else {
					prstart++;
					getfname(prstart,prompt);
				}
			fprintf(stderr,"%s",prompt);
			chr=getchar();
			while(chr !=EOF) {
				if (chr=='\n') {
					fprintf(stderr,"%s",prompt);
					chr=getchar();
					if (chr=='\n')
						chr=EOF;
					else
						write(skt,"\n",1);
				}
				write(skt,&chr,1);
				fprintf(stderr,"%s",prompt);
				chr=getchar();
			}
		/* should really pass the end of the line on here */
}

getmsgs(sfd,errsfd)
int sfd;
int errsfd;
{
	char *buf[BUFSIZE];
	char *retstat[BUFSIZE];
	char *pos;
	int nread;
	int selectfds,readyfds;
	struct timeval timeout;

	shutdown(sfd,1);
	shutdown(errsfd,1);
	selectfds=(1<<sfd) | (1<<errsfd);
	do {
		readyfds=selectfds;
		if(select(16,&readyfds,0,0,0)<0)
			break;
		if (readyfds & (1<<sfd)) {
			nread=read(sfd,buf,sizeof(buf));
			if (nread <=0) {
				shutdown(sfd,2);
				selectfds &= ~(1<<sfd);
			}
			else
				write(1,buf,nread);
		}
		if (readyfds & (1<<errsfd)) {
			nread=read(errsfd,buf,sizeof(buf));
			if (nread<=0) {
				shutdown(errsfd,2);
				selectfds &= ~(1<<errsfd);
			}
			else
				write(2,buf,nread);
		}
	} while (selectfds);
	close(sfd);
	close(errsfd);
	exit(0);
}

geterrs()
{
	char *buf[BUFSIZE];
	int nread;
	fcntl(1,F_SETFL,FNDELAY);
	if (write(1,"\0",1)<0) {
		if (errno==EPIPE) {
			fprintf(stderr,"Broken pipe\n");
			exit (-1);
		}
	}

	while((nread=read(errskt,buf,sizeof(buf)))!=0)
		write(2,buf,nread);
	exit(0);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 rtroff.c
	/bin/echo -n '	'; /bin/ls -ld rtroff.c
fi