wim@ecn.UUCP (Wim Rijnsburger) (03/06/90)
Last week I tried to post our source of 'psrun', a 'psh' alike utility for loose coupling of applications to NeWS user interfaces. Because problably something went wrong with this posting, here is another try. This is a corrected version, so the patch for the manual page, posted yesterday is not necessary anymore. The psrun command downloads NeWS code into the server, catches errors and handles client-server communications appropriate. We use loose coupling of applications to the NeWS server, by redirecting stdout of the application to stdin of psrun and vice versa. Below you will find a shar archive of the complete source of psrun, including a manual page. After unpacking of this archive, edit the Makefile to set the BINDIR, MANDIR and LIBDIR appropriate. Then type 'make' to build everything. Test it out by something like: psrun -h to get help on available options and arguments. To start an interactive debugging session with the server, use: psrun -d -P "executive" All commands typed at stdin are now given to the server. Try to type some garbage to test the error catching. To download NeWS code files from some directory, use: psrun -d -L /home/.../mydir file1 file2 ... If everything looks fine, do a 'make install'. The manual page gives more explanations on usage. Wim. ---------- Netherlands Energy Research Foundation, ECN -------------- Wim Rijnsburger email: RIJNSBURGER@ECN.NL P.O. Box 1, 1755 ZG Petten, Holland phone: +31 2246 4673 ----------- Remove everything below and including this line ----------- : To unbundle, sh this file echo main.c cat >main.c <<'@@@ Fin de main.c' #include "def.h" /* * psrun: a general client-server coupling program * * The main program tries to connect with the server and load the * application into the server. * A child is forked to read stdin and pass it to the server * Then a loop is started to read tagged commands from the server. * * If the -d (debug) flag was given as an option, the psrun * reports the server's messages on stderr, in case * the interpreter failed for some reason. * * Immediate to execute PostScript commands can be passed to the * server via the option -P. For instance to get into executive * mode, where the server do not stop on errors: * * psrun -d -P "executive" * * Server side code files, passed as arguments to psrun are downloaded * by psrun into the server. The server searches files in a library * directory as given with the -L <dir> option. The default lib dir * is determined by a variable in the Makefile. * * (c) 1989 Copyright Wim Rijnsburger, ECN Petten, Holland * * ---------- Netherlands Energy Research Foundation, ECN -------------- * Wim Rijnsburger UUCP : wim@ecn.uucp * P.O. Box 1, 1755 ZG Petten(NH) ARPA : ecn!wim@nluug.nl * Holland phone: +31 2246 4336 ecn!wim@uunet.uu.net * */ main(argc, argv) int argc; char **argv; { #define STRBUFLEN 1024 char Buf1[STRBUFLEN], Buf2[STRBUFLEN], *LibDir = LIBDIR, *PsString = "", *ErrStr; int status = OK, debug = 0, pid, i, c, cid; FILE *InPipe; /* * Parse the options */ extern char *optarg; extern int optind, opterr; opterr = 0; while ((c = getopt(argc, argv, "dhL:P:")) != -1) switch (c) { case 'd': debug = 1; break; case 'L': LibDir = optarg; break; case 'P': PsString = optarg; break; case 'h': status = ARG_ERROR; break; case '?': status = ARG_ERROR; break; } /* * Connect to the server */ if (ps_open_PostScript() == NULL) status = SERVER_ERROR; if (status == OK) { fprintf(stderr, "%s\n", HEADER); if (argc == 1) { fprintf(stderr, "Use 'psrun -h' to get help\n"); } /* * Init the cps code */ ps_Init(); /* * Pass the client parameters and command arg's to the ps environment * Let the server load the application files */ fprintf(PostScript, "/ClientPwd (%s) def\n", getwd(Buf1)); fprintf(PostScript, "/LibDir (%s) def\n", LibDir); fprintf(PostScript, "/NewsLibDir (XNEWSHOME) getenv (/etc/NeWS) append def\n"); fprintf(PostScript, "/Debug? %d 1 eq def\n", debug); fprintf(PostScript, "%s\n", PsString); fprintf(PostScript, "/ClientArgv [\n"); for (; optind < argc; optind++) fprintf(PostScript, "(%s)\n", argv[optind]); fprintf(PostScript, "] def\n"); fprintf(PostScript, "ClientPwd ClientArgv loadFiles\n"); ps_flush_PostScript(); /* * Fork a child to read PS commands from stdin */ pid = fork(); if (pid < 0) status = FORK_ERROR; else if (pid == 0) { while (status == OK) { if (gets(Buf1) == NULL) { status = QUIT; strcpy(Buf1, "quit"); } fprintf(PostScript, "%s\n", Buf1); ps_flush_PostScript(); } exit (0); } else { /* * Read and handle tagged commands from the server-side */ while (status == OK) { if (psio_error(PostScriptInput)) status = PS_ERROR; else if (ps_tag_Quit()) status = QUIT; else if (ps_tag_Print(Buf1)) { printf("%s", Buf1); fflush(stdout); } else if (ps_tag_Error(Buf1)) { fprintf(stderr, "%s", Buf1); fflush(stderr); } else if (ps_tag_System(&cid, Buf1)) { system(Buf1); if (cid) ps_CidExec(cid, "exit"); } else if (ps_tag_Popen(&cid, Buf1)) { InPipe = (FILE *) popen(Buf1, "r"); if (InPipe == NULL) status = POPEN_ERROR; else { strcpy(Buf1, "["); while (fgets(Buf2, STRBUFLEN, InPipe) != NULL) { strcat(Buf1, "("); strncat(Buf1, Buf2, strlen(Buf2) - 1); strcat(Buf1, ")"); } strcat(Buf1, "] exit"); ps_CidExec(cid, Buf1); pclose(InPipe); } } else if (debug) { if (fgets(Buf1, STRBUFLEN, PostScriptInput) > 0) fprintf(stderr, Buf1); else status = PS_ERROR; } else status = PS_ERROR; } /* * Clean up and quit */ ps_close_PostScript(); } } /* * Exit */ switch (status) { case ARG_ERROR: ErrStr = (char *) sprintf(Buf2, "usage: %s [-dh] [-L LibDir] [-P PsString] [ PsFiles ]\n", argv[0]); break; case IO_ERROR: ErrStr = "IO error"; break; case SERVER_ERROR: ErrStr = "Cannot contact NeWS server"; break; case PS_ERROR: ErrStr = "PS error"; break; case FORK_ERROR: ErrStr = "Fork failed"; break; case CHILD_ERROR: ErrStr = "Child error"; break; default: ErrStr = (char *) sprintf(Buf2, "abnormal termination, status = %d", status); break; } if (status != QUIT) fprintf(stderr, "%s: %s\n", argv[0], ErrStr); exit(status); } @@@ Fin de main.c echo client.cps cat >client.cps <<'@@@ Fin de client.cps' % % Define the tag's % #define QUITTAG 100 #define PRINTTAG 101 #define ERRORTAG 102 #define SYSTEMTAG 103 #define POPENTAG 104 % % Tag routines to poll the server % cdef ps_tag_Quit() => QUITTAG cdef ps_tag_Print(string s) => PRINTTAG(s) cdef ps_tag_Error(string s) => ERRORTAG(s) cdef ps_tag_System(int i, string s) => SYSTEMTAG(i, s) cdef ps_tag_Popen(int i, string s) => POPENTAG(i, s) % % Routine to return a Cid result and exit % cdef ps_CidExec(int id, string s) id {s cvx exec} sendcidevent % % Initialization % cdef ps_Init() % % Tagged commands interface % /c_Quit { % - => - QUITTAG tagprint } def /c_Error { % string => - ERRORTAG tagprint typedprint } def /c_Debug { % string => - Debug? { c_Error } { pop } ifelse } def /c_Print { % string => - PRINTTAG tagprint typedprint } def /c_System { % string => - SYSTEMTAG tagprint 0 typedprint typedprint } def /c_SystemAndWait { % string => - % send args: tag id string /MyCID uniquecid def SYSTEMTAG tagprint MyCID typedprint typedprint % wait until completed [ MyCID cidinterest ] forkeventmgr waitprocess pop } def /c_PopenAndWait { % string => string % send args: tag id string /MyCID uniquecid def POPENTAG tagprint MyCID typedprint typedprint % wait until completed [ MyCID cidinterest ] forkeventmgr waitprocess } def % % Safe executor % /safeExec { % string|key|proc => boolean cvx stopped { ExecutiveErrorHandler true } { false } ifelse } def % % Load files % /loadFiles { % dirname [filenames .. ] => - { 1 index exch safeRun {exit} if } forall pop } def /safeRun { % dirname filename => boolean { (r) openFile dup null eq { pop } { cvx exec } ifelse } safeExec } def % % Open a file in a directory % /openFile { % dirname filename mode => file|null 1 index length 0 eq { pop pop pop null } { 3 -1 roll % drop the dirname if filename is already complete 2 index 0 get (/) 0 get eq { pop () } if % Complete the dirname with a trailing slash dup length 0 gt { dup dup length 1 sub get (/) 0 get ne { (/) append } if } if % Complete the path and insert the client PWD, if not complete 3 -1 roll append dup 0 get (/) 0 get ne { ClientPwd dup dup length 1 sub get (/) 0 get ne { (/) append } if exch append } if 2 copy [ 3 1 roll ] (openFile: (%) %\n) exch sprintf c_Debug exch % Open the file { 2 copy file } stopped { pop pop (openFile: %\n) [ $error /errorname get ] sprintf c_Error null } if % stack: string string file|null % Skip over a #! line if opened for read dup null ne 2 index 0 get (r) 0 get eq and { dup 255 string readline pop (#!) anchorsearch { pop pop } { pop closefile 2 copy file } ifelse } if % Return the file object 3 1 roll pop pop } ifelse } def @@@ Fin de client.cps echo Makefile cat >Makefile <<'@@@ Fin de Makefile' TARGET =psrun VERSION =1.5 AUTHOR =Wim Rijnsburger INSTITUTION =ECN Petten Holland YEAR =1989 INSTDIR =$(OPENWINHOME) BINDIR =$(INSTDIR)/bin LIBDIR =$(INSTDIR)/lib/VisualObjects MANEXT =l MANDIR =$(INSTDIR)/share/man/man$(MANEXT) CC =cc CFLAGS = C_SCR =main.c CPS_SRC =client.cps INCLUDE =def.h MANPAGE =man.txt MISC =Makefile $(MANPAGE) LIBS =-I$(OPENWINHOME)/include -L$(OPENWINHOME)/lib -lcps ALL =$(C_SCR) $(CPS_SRC) $(MISC) $(INCLUDE) C_OBJ =$(C_SCR:.c=.o) CPS_H =$(CPS_SRC:.cps=.h) HEADER =$(TARGET) $(VERSION) (c) $(YEAR) $(AUTHOR), $(INSTITUTION) .c.o: cc -O -c -DHEADER=\""$(HEADER)"\" -DLIBDIR=\""$(LIBDIR)"\" $(LIBS) $< $(TARGET): $(C_OBJ) $(CC) $(CFLAGS) $(C_OBJ) -o $(TARGET) $(LIBS) $(CPS_H): $(CPS_SRC) cps $(CPS_SRC) $(C_OBJ): $(CPS_H) $(INCLUDE) new: rm -f $(TARGET) $(C_OBJ) $(CPS_H) core *% *.BAK clean: rm -f core *% *.BAK list: $(ALL) enscript -G -b"$(HEADER)" $? touch list backup: rm -f bak/* cp $(ALL) bak install: install -d $(BINDIR) install -s $(TARGET) $(BINDIR) install -d $(MANDIR) install -c -m 444 $(MANPAGE) $(MANDIR)/$(TARGET).$(MANEXT) update: $(TARGET) make install touch update shar: $(TARGET).shar $(TARGET).shar: $(ALL) \rm -f $(TARGET).shar shar $(ALL) > $(TARGET).shar @@@ Fin de Makefile echo man.txt cat >man.txt <<'@@@ Fin de man.txt' .TH PSRUN 1 "1 MARCH 1990" "ECN, Petten (Holland)" .SH NAME psrun \- a general client-server coupling program for NeWS applications .SH SYNOPSIS .B psrun [-dh] [-L \fILibDir\fP] [-P \fIPsString\fP] [ \fIPsFiles\fP ] .SH DESCRIPTION .LP \fIpsrun\fP is a utility for loosely coupling of client side code with the window server and to down load server side code into the server. The main program tries to connect with the server and load the application into the server. A child is forked to read stdin and pass it to the server. Then a loop is started to read tagged commands from the server. If the \fB-d\fP (debug) flag was given as an option, the psrun reports the server's messages on stderr, in case the interpreter failed for some reason. Commands entered at stdin are interpreted by the server and responses appear at stdout of \fBpsrun\fP. Server side code files, passed as arguments to psrun are downloaded by psrun into the server. The server searches files in a library directory as given with the \fB-L\fP \fILibDir\fP option. The default \fILibDir\fP is determined by a variable in the Makefile. The server side code can communicate with the client side via \fBc_Print\fP to send a string to stdout. Via \fBc_Error\fP and \fBc_Debug\fP messages are send to stderr. With \fBc_Quit\fP the server side code quits the session. Immediate to execute PostScript commands can be passed to the server via the option \fB-P\fP. For instance to get into executive mode, where the server do not stop on errors: .LP .RS .nf .IP "\fBpsrun -d -P 'executive (Type 'c_Quit' to stop\n) c_Error'\fP" .RE .fi .LP Loose coupling with the client side of an application can be done with redirection of stdin and stdout. For instance: .LP .RS .nf .IP "\fB/etc/mknod p1 p2\fP" .IP "\fBpsrun -d -P 'executive' -L . \fImyapplPSfiles\fP < p1 > p2 &\fP" .IP "\fB\fImyappl\fP < p2 > p1\fP" .RE .fi .LP .SH FILES .TP 2.2i /usr/openwin/bin/psrun the command is usually installed here .TP /usr/openwin/lib/VisualObjects default directory to search library files .SH "SEE ALSO" psh(1) .SH DIAGNOSTICS The status messages given by the command should be self explanatory. .SH AUTHOR Wim Rijnsburger, ECN, PO box 1, 1755 ZG Petten (NH), Holland. .SH BUGS This is a preliminary release. Please contact me about bugs and wishes. .SH NOTES The development of \fBpsrun\fP is part of the \fBVisualObjects\fP research project of the \fINetherlands Energy Research Foundation (ECN), Petten (NH), Holland\fP. @@@ Fin de man.txt echo def.h cat >def.h <<'@@@ Fin de def.h' #include "client.h" #define NOT ! #define AND && #define OR || #define ARG_ERROR 2 #define QUIT 1 #define OK 0 #define IO_ERROR -1 #define SERVER_ERROR -2 #define PS_ERROR -3 #define FORK_ERROR -4 #define CHILD_ERROR -5 #define POPEN_ERROR -6 @@@ Fin de def.h exit 0