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